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
Java中String的無意識的遞迴現象
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上除錯檢視,更加清晰 我們需要先將二叉樹構建出來,然後
java中String類型轉換為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
轉:Java中String與byte[]的轉換
輸出字符串 單個字符 linu 編輯 繁體 中國人 對象 本質 計算機基礎知識 String s = "fs123fdsa";//String變量 byte b[] = s.getBytes();//String轉換為byte[] String t = new Stri
java中string.trim()函數的作用
main ati cnblogs return style ret blog substr system trim /[tr?m] / 英文意思:整理,修理,修剪,整齊的 trim()的作用:去掉字符串首尾的空格。 public static void main(S
在java中String類為什麽要設計成final?
tro cli lai 這一 引用 沒有 num 重新 static 大神鏈接:在java中String類為什麽要設計成final? - 程序員 - 知乎 我進行了重新排版,並且更換了其中的一個例子,讓我們更好理解。 String很多實用的特性,比如說“不可變性”,是工
java中String的21種使用方法
i++ als cas tolower star out clas oid index (構造函數必須new出來) * public String (char[] vaue) 將一個字符數組變成字符串(構造函數) *
Java中String連接性能的分析
order 幫助 執行 .get leg body near 應該 con 總結:如果String的數量小於4(不含4),使用String.concat()來連接String,否則首先計算最終結果的長度,再用該長度來創建一個StringBuilder,最後使用這個Stri
Java中String的設計
字節對齊 執行 詳細 做了 永久 字符串變量 分析 擴展 mem String應用簡介 前言 String字符串在Java應用中使用非常頻繁,只有理解了它在虛擬機中的實現機制,才能寫出健壯的應用,本文使用的JDK版本為1.8.0_111。 常量池 Java代碼被
java中String new和直接賦值的區別
字符串相同 main 基礎 還需 賦值 請問 常量 int 表達 Java中String new和直接賦值的區別 對於字符串:其對象的引用都是存儲在棧中的,如果是編譯期已經創建好(直接用雙引號定義的)的就存儲在常量池中,如果是運行期(new出來的)才能確定的
java中String類常用方法、屬性等
col clas equal ack length ++ ava eal rgs package Head18; public class java09 { public static void main(String[] args) { St
關於java中string的內存位置
基本數據類型 對象 指向 存在 nbsp 引用 什麽 string類型 類型 java運行時內存分五部分: 線程共享:堆內存、方法區(包括常量池) 線程私有:棧內存、本地方法棧、程序計數器 string不是基本數據類型,那麽一個string的內存位置是什
java中String的equals()和 ==
spa 相等 println div 說明 java pri equal new 1 String a=new String("java"); 2 String b=new String("java"); 3 Syst
JAVA中string.replace()和string.replaceAll()的區別及用法
mod btn dsm ont match cep 產生 生成 語法 乍一看,字面上理解好像replace只替換第一個出現的字符(受javascript的影響),replaceall替換所有的字符,其實大不然,只是替換的用途不一樣。 public Strin
Java中String、StringBuilder、StringBuffer常用源碼分析及比較(一):String源碼分析
array string類 都是 epo sys 匹配字符串 bound 地址 簡單 String: 一、成員變量: /** The value is used for character storage. */ private final char value[
Java中String、StringBuilder、StringBuffer常用源碼分析及比較(二):StringBuilder、StringBuffer源碼分析
string類型 character private 字符 代碼 less pri des over StringBuilder: 一、構造方法: /** * Constructs a string builder with no characters in i