程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java 中的 String對象為何是弗成變的

Java 中的 String對象為何是弗成變的

編輯:關於JAVA

Java 中的 String對象為何是弗成變的。本站提示廣大學習愛好者:(Java 中的 String對象為何是弗成變的)文章只能為提供參考,不一定能成為您想要的結果。以下是Java 中的 String對象為何是弗成變的正文


甚麼是弗成變對象?

String對象是弗成變的,但這僅意味著你沒法經由過程挪用它的私有辦法來轉變它的值。

盡人皆知, 在Java中, String類是弗成變的。那末究竟甚麼是弗成變的對象呢? 可以如許以為:假如一個對象,在它創立完成以後,不克不及再轉變它的狀況,那末這個對象就是弗成變的。不克不及轉變狀況的意思是,不克不及轉變對象內的成員變量,包含根本數據類型的值不克不及轉變,援用類型的變量不克不及指向其他的對象,援用類型指向的對象的狀況也不克不及轉變。

辨別對象和對象的援用

關於Java初學者, 關於String是弗成變對象老是存有困惑。看上面代碼:

String s = "ABCabc";
System.out.println("s = " + s);
s = "123456";
System.out.println("s = " + s);

打印成果為:

s = ABCabc
 s = 123456

起首創立一個String對象s,然後讓s的值為“ABCabc”, 然後又讓s的值為“123456”。 從打印成果可以看出,s的值確切轉變了。那末怎樣還說String對象是弗成變的呢? 其實這裡存在一個誤區: s只是一個String對象的援用,其實不是對象自己。對象在內存中是一塊內存區,成員變量越多,這塊內存區占的空間越年夜。援用只是一個4字節的數據,外面寄存了它所指向的對象的地址,經由過程這個地址可以拜訪對象。

也就是說,s只是一個援用,它指向了一個詳細的對象,當s=“123456”; 這句代碼履行過以後,又創立了一個新的對象“123456”, 而援用s從新指向了這個心的對象,本來的對象“ABCabc”還在內存中存在,並沒有轉變。內存構造以下圖所示:

Java和C++的一個分歧點是, 在Java中弗成能直接操尴尬刁難象自己,一切的對象都由一個援用指向,必需經由過程這個援用能力拜訪對象自己,包含獲得成員變量的值,轉變對象的成員變量,挪用對象的辦法等。而在C++中存在援用,對象和指針三個器械,這三個器械都可以拜訪對象。其實,Java中的援用和C++中的指針在概念上是類似的,他們都是寄存的對象在內存中的地址值,只是在Java中,援用損失了部門靈巧性,好比Java中的援用不克不及像C++中的指針那樣停止加減運算。

為何String對象是弗成變的?

要懂得String的弗成變性,起首看一下String類中都有哪些成員變量。 在JDK1.6中,String的成員變量有以下幾個:

public final class String
  implements java.io.Serializable, Comparable<String>, CharSequence
{
  /** The value is used for character storage. */
  private final char value[];
  /** The offset is the first index of the storage that is used. */
  private final int offset;
  /** The count is the number of characters in the String. */
  private final int count;
  /** Cache the hash code for the string */
  private int hash; // Default to 0

在JDK1.7中,String類做了一些修改,重要是轉變了substring辦法履行時的行動,這和本文的主題不相干。JDK1.7中String類的重要成員變量就剩下了兩個:

public final class String 
  implements java.io.Serializable, Comparable<String>, CharSequence { 
  /** The value is used for character storage. */ 
  private final char value[]; 
  /** Cache the hash code for the string */ 
  private int hash; // Default to 0

由以上的代碼可以看出, 在Java中String類其實就是對字符數組的封裝。JDK6中, value是String封裝的數組,offset是String在這個value數組中的肇端地位,count是String所占的字符的個數。在JDK7中,只要一個value變量,也就是value中的一切字符都是屬於String這個對象的。這個轉變不影響本文的評論辯論。 除此以外還有一個hash成員變量,是該String對象的哈希值的緩存,這個成員變量也和本文的評論辯論有關。在Java中,數組也是對象(可以參考我之前的文章java中數組的特征)。 所以value也只是一個援用,它指向一個真實的數組對象。其實履行了String s = “ABCabc”; 這句代碼以後,真實的內存結構應當是如許的:

value,offset和count這三個變量都是private的,而且沒有供給setValue, setOffset和setCount等公共辦法來修正這些值,所以在String類的內部沒法修正String。也就是說一旦初始化就不克不及修正, 而且在String類的內部不克不及拜訪這三個成員。另外,value,offset和count這三個變量都是final的, 也就是說在String類外部,一旦這三個值初始化了, 也不克不及被轉變。所以可以以為String對象是弗成變的了。

那末在String中,明明存在一些辦法,挪用他們可以獲得轉變後的值。這些辦法包含substring, replace, replaceAll, toLowerCase等。例如以下代碼:

String a = "ABCabc"; 
System.out.println("a = " + a); 
a = a.replace('A', 'a'); 
System.out.println("a = " + a);

打印成果為:

a = ABCabc
a = aBCabc

那末a的值看似轉變了,其實也是異樣的誤區。再次解釋, a只是一個援用, 不是真實的字符串對象,在挪用a.replace(‘A', ‘a')時, 辦法外部創立了一個新的String對象,並把這個心的對象從新賦給了援用a。String中replace辦法的源碼可以解釋成績:

讀者可以本身檢查其他辦法,都是在辦法外部從新創立新的String對象,而且前往這個新的對象,本來的對象是不會被轉變的。這也是為何像replace, substring,toLowerCase等辦法都存在前往值的緣由。也是為何像上面如許挪用不會轉變對象的值:

String ss = "123456";
System.out.println("ss = " + ss);
ss.replace('1', '0');
System.out.println("ss = " + ss);

打印成果:

ss = 123456
 ss = 123456

String對象真的弗成變嗎?

從上文可知String的成員變量是private final 的,也就是初始化以後弗成轉變。那末在這幾個成員中, value比擬特別,由於他是一個援用變量,而不是真實的對象。value是final潤飾的,也就是說final不克不及再指向其他數組對象,那末我能轉變value指向的數組嗎? 好比將數組中的某個地位上的字符變成下劃線“_”。 至多在我們本身寫的通俗代碼中不克不及夠做到,由於我們基本不克不及夠拜訪到這個value援用,更不克不及經由過程這個援用去修正數組。

那末用甚麼方法可以拜訪公有成員呢? 沒錯,用反射, 可以反射出String對象中的value屬性, 進而轉變經由過程取得的value援用轉變數組的構造。上面是實例代碼:

 public static void testReflection() throws Exception {
 //創立字符串"Hello World", 並賦給援用s
 String s = "Hello World"; 
 System.out.println("s = " + s); //Hello World
 //獲得String類中的value字段
 Field valueFieldOfString = String.class.getDeclaredField("value");
 //轉變value屬性的拜訪權限
 valueFieldOfString.setAccessible(true);
 //獲得s對象上的value屬性的值
 char[] value = (char[]) valueFieldOfString.get(s);
 //轉變value所援用的數組中的第5個字符
 value[5] = '_';
 System.out.println("s = " + s); //Hello_World
 }

打印成果為:

s = Hello World
 s = Hello_World

在這個進程中,s一直援用的統一個String對象,然則再反射前後,這個String對象產生了變更, 也就是說,經由過程反射是可以修正所謂的“弗成變”對象的。然則普通我們不這麼做。這個反射的實例還可以解釋一個成績:假如一個對象,他組合的其他對象的狀況是可以轉變的,那末這個對象極可能不是弗成變對象。例如一個Car對象,它組合了一個Wheel對象,固然這個Wheel對象聲明成了private final 的,然則這個Wheel對象外部的狀況可以轉變, 那末就不克不及很好的包管Car對象弗成變。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved