1. 程式人生 > >Java中String的無意識的遞迴現象

Java中String的無意識的遞迴現象

  Java中的每個類從根本上都是繼承Object,標準容器類自然也不。因此容器類都有toString()方法,並且覆寫了這個方法,使得它生成的String結果能夠表達容器自身,以及容器所包含的物件。例如ArrayList.toString(),它會遍歷ArrayList中包含的所有物件,呼叫每個元素上的toString()方法:
Generator介面:

package com.jxs.chapeter13;

/**
 * Created by jiangxs on 2018/4/8.
 */
public interface Generator<T> {

    T next();
}

Coffee.java

package com.jxs.chapeter13;

/**
 * Created by jiangxs on 2018/4/8.
 */
public class Coffee {

    private static long counter = 0;
    private final long id = counter++;

    @Override
    public String toString() {

        return getClass().getSimpleName() + " " + id;
    }
}

    class Latte extends Coffee {
    }

    class Mocha extends Coffee {
    }

    class Cappuccino extends Coffee {
    }

    class Americano extends Coffee {
    }

    class Breve extends Coffee {
    }

CoffeeGenerator.java

package com.jxs.chapeter13;

import java.util.Iterator;
import java.util.Random;

/**
 * Created by jiangxs on 2018/4/8.
 */
public class CoffeeGenerator implements Generator<Coffee>,Iterable<Coffee> {

    private Class[] types = {Latte.class, Mocha.class,
            Cappuccino.class, Americano.class, Breve.class};
    private
static Random random = new Random(47); public CoffeeGenerator() { } private int size; public CoffeeGenerator(int size) { this.size = size; } @Override public Coffee next() { try { return (Coffee) types[random.nextInt(types.length)].newInstance(); } catch (Exception e) { throw new RuntimeException(e); } } class CoffeeIterator implements Iterator<Coffee> { int count = size; @Override public boolean hasNext() { return count > 0; } @Override public Coffee next() { count--; return CoffeeGenerator.this.next(); } @Override public void remove() { throw new UnsupportedOperationException(); } } @Override public Iterator<Coffee> iterator() { return new CoffeeIterator(); } public static void main(String[] args) { CoffeeGenerator gen = new CoffeeGenerator(); for (int i = 0; i < 5; i++) { System.out.println(gen.next()); } for (Coffee c : new CoffeeGenerator(5)) { System.out.println(c); } } }

ArrayListDisplay.java

package com.jxs.chapeter13;

import java.util.ArrayList;

/**
 * Created by jiangxs on 2018/4/8.
 */
public class ArrayListDisplay {

    public static void main(String[] args) {

        ArrayList<Coffee> coffees = new ArrayList<>();
        for (Coffee coffee : new CoffeeGenerator(10)) {
            coffees.add(coffee);
        }
        System.out.println(coffees);
    }
}

輸出結果:

[Americano 0, Latte 1, Americano 2, Mocha 3, Mocha 4, Breve 5, Americano 6, Latte 7, Cappuccino 8, Cappuccino 9]

Process finished with exit code 0

  如果你需要toString()方法打印出物件的記憶體地址,也許你會考慮使用this這個關鍵字:
InfiniteRecursion.java

package com.jxs.chapeter13;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by jiangxs on 2018/4/8.
 */
public class InfiniteRecursion {

    @Override
    public String toString() {
        return " InfiniteRecursion address: " + this + "\n";
    }

    public static void main(String[] args) {

        List<InfiniteRecursion> v = new ArrayList<>();
        for (int i=0;i<10;i++) {
            v.add(new InfiniteRecursion());
        }
        System.out.println(v);
    }
}

  當你建立了InfiniteRecursion物件,並將其打印出來的時候,你會得到一串非常長的異常。如果你將該InfiniteRecursion物件存入一個ArrayList中,然後列印該ArrayList,你也會得到同樣的異常:

Exception in thread "main" java.lang.StackOverflowError
    at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:125)
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
    at java.lang.StringBuilder.append(StringBuilder.java:136)
    at com.jxs.chapeter13.InfiniteRecursion.toString(InfiniteRecursion.java:13)
    at java.lang.String.valueOf(String.java:2994)
···

其實,當代碼執行時:

"InfiniteRecursion address: " + this

  這裡發生了自動型別轉換,由InfiniteRecursion型別轉換成String型別。因為編譯器看到一個String物件後面跟著一個”+”,而後面的物件不是String,於是編譯器試著將this轉換成一個String。它怎麼轉換呢,正是通過呼叫this上的toString()方法,於是就發生了遞迴呼叫。
  如果你真的想要打印出物件的記憶體地址,應該呼叫Object.toString()方法,這才是負責此任務的方法。所以,你不該使用this,而是應該呼叫super.toString()方法。
如下InfiniteRecursionModify.java所示:

package com.jxs.chapeter13;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by jiangxs on 2018/4/8.
 */
public class InfiniteRecursionModify {

    @Override
    public String toString() {
        return " InfiniteRecursion address: " + super.toString() + "\n";
    }

    public static void main(String[] args) {

        List<InfiniteRecursionModify> v = new ArrayList<>();
        for (int i=0;i<10;i++) {
            v.add(new InfiniteRecursionModify());
        }
        System.out.println(v);
    }
}

輸出結果:

[ InfiniteRecursion address: [email protected]
,  InfiniteRecursion address: [email protected]
,  InfiniteRecursion address: [email protected]
,  InfiniteRecursion address: [email protected]
,  InfiniteRecursion address: [email protected]
,  InfiniteRecursion address: [email protected]
,  InfiniteRecursion address: [email protected]
,  InfiniteRecursion address: [email protected]
,  InfiniteRecursion address: [email protected]
,  InfiniteRecursion address: [email protected]
]

Process finished with exit code 0

參考:《Java程式設計思想》

相關推薦

Java 意識

之前在練習中碰到一個問題,程式中沒有使用遞迴卻出現了 Exception in thread “main” java.lang.StackOverflowError 異常,先把之前的程式碼貼上來: public class InfiniteRecursio

java出現的一個的問題

例子 我們知道Java中每一個類都是繼承自Object,容器類也不例外。Object有toString方法,那麼容器類也就擁有。先看下面一個例子。作用是:建立一個容器類,並通過toString方法打印出容器內部的子類。 package com.test; import

JavaString意識現象

  Java中的每個類從根本上都是繼承Object,標準容器類自然也不。因此容器類都有toString()方法,並且覆寫了這個方法,使得它生成的String結果能夠表達容器自身,以及容器所包含的物件。例如ArrayList.toString(),它會遍歷Arra

序遍歷--和非java版)

根據中序遍歷的順序,對於任一結點,優先訪問其左孩子,而左孩子結點又可以看做一根結點,然後繼續訪問其左孩子結點,直到遇到左孩子結點為空的結點才進行訪問,然後按相同的規則訪問其右子樹。因此其處理過程如下:   對於任一結點root,引入一個輔助節點p,其作用是:標記已經訪問過的節點, &nb

二叉樹的先序遍歷(和非)、序遍歷(和非)、後序遍歷(非)及層次遍歷java實現

二叉樹的先序遍歷,遞迴實現: public List<Integer> preorderTraversal(TreeNode root) { //用棧來實現 List<Integer> list = new ArrayList&l

Java實現二叉樹前序序後序遍歷(通俗易懂)

想要弄懂Java的一個知識點,沒有比親自手寫一遍更好的方法 仔細研究手寫一遍,一定會收穫滿滿,沒有你想想中那麼難  小編下面以這個二叉樹為例,測試程式碼 以下是完整的四個類程式碼,大家可先放在自己本地IDE上除錯檢視,更加清晰 我們需要先將二叉樹構建出來,然後

javaString類型轉換為yyyy-MM-dd的Date類型

col code edate birt mat led div get sys String birthday ="2017-02-22"; SimpleDateFormat sdf = new SimpleDateFormat(("yyyy-MM-dd")); jav

轉:JavaString與byte[]的轉換

輸出字符串 單個字符 linu 編輯 繁體 中國人 對象 本質 計算機基礎知識 String s = "fs123fdsa";//String變量 byte b[] = s.getBytes();//String轉換為byte[] String t = new Stri

javastring.trim()函數的作用

main ati cnblogs return style ret blog substr system trim /[tr?m] / 英文意思:整理,修理,修剪,整齊的 trim()的作用:去掉字符串首尾的空格。 public static void main(S

javaString類為什麽要設計成final?

tro cli lai 這一 引用 沒有 num 重新 static 大神鏈接:在java中String類為什麽要設計成final? - 程序員 - 知乎 我進行了重新排版,並且更換了其中的一個例子,讓我們更好理解。 String很多實用的特性,比如說“不可變性”,是工

javaString的21種使用方法

i++ als cas tolower star out clas oid index (構造函數必須new出來) * public String (char[] vaue) 將一個字符數組變成字符串(構造函數) *

JavaString連接性能的分析

order 幫助 執行 .get leg body near 應該 con  總結:如果String的數量小於4(不含4),使用String.concat()來連接String,否則首先計算最終結果的長度,再用該長度來創建一個StringBuilder,最後使用這個Stri

JavaString的設計

字節對齊 執行 詳細 做了 永久 字符串變量 分析 擴展 mem String應用簡介 前言 String字符串在Java應用中使用非常頻繁,只有理解了它在虛擬機中的實現機制,才能寫出健壯的應用,本文使用的JDK版本為1.8.0_111。 常量池 Java代碼被

javaString new和直接賦值的區別

字符串相同 main 基礎 還需 賦值 請問 常量 int 表達 Java中String new和直接賦值的區別 對於字符串:其對象的引用都是存儲在棧中的,如果是編譯期已經創建好(直接用雙引號定義的)的就存儲在常量池中,如果是運行期(new出來的)才能確定的

javaString類常用方法、屬性等

col clas equal ack length ++ ava eal rgs package Head18; public class java09 { public static void main(String[] args) { St

關於javastring的內存位置

基本數據類型 對象 指向 存在 nbsp 引用 什麽 string類型 類型 java運行時內存分五部分:   線程共享:堆內存、方法區(包括常量池)   線程私有:棧內存、本地方法棧、程序計數器 string不是基本數據類型,那麽一個string的內存位置是什

javaString的equals()和 ==

spa 相等 println div 說明 java pri equal new 1 String a=new String("java"); 2 String b=new String("java"); 3 Syst

JAVAstring.replace()和string.replaceAll()的區別及用法

mod btn dsm ont match cep 產生 生成 語法 乍一看,字面上理解好像replace只替換第一個出現的字符(受javascript的影響),replaceall替換所有的字符,其實大不然,只是替換的用途不一樣。 public Strin

JavaString、StringBuilder、StringBuffer常用源碼分析及比較(一):String源碼分析

array string類 都是 epo sys 匹配字符串 bound 地址 簡單 String: 一、成員變量: /** The value is used for character storage. */ private final char value[

JavaString、StringBuilder、StringBuffer常用源碼分析及比較(二):StringBuilder、StringBuffer源碼分析

string類型 character private 字符 代碼 less pri des over StringBuilder: 一、構造方法: /** * Constructs a string builder with no characters in i