Java基礎3——深入理解String類
深入理解String類
String類的不變性
java.lang.String類使用了final修飾,不能被繼承。Java程式中的所有字面值,即雙引號括起的字串,如"abc",都是作為String類的例項實現的。String是常量,其物件一旦構造就不能再被改變。換句話說,String物件是不可變的,每一個看起來會修改String值的方法,實際上都是創造了一個全新的String物件,以包含修改後的字串內容。而最初的String物件則絲毫未動。String物件具有隻讀特性,指向它的任何引用都不可能改變它的值,因此,也不會對其他的引用有什麼影響。但是字串引用可以重新賦值。java字串在記憶體中採用unicode編碼方式,任何一個字元對應兩個位元組的定長編碼,即任何一個字元(無論中文還是英文)都算一個字元長度,佔用兩個位元組
public class Immutable {
public static String upcase(String s) {
return s.toUpperCase();
}
public static void main(String[ ] args) {
String str1= "Hello World";
System.out.println(str1); //Hello World
String str2 = upcase(str1);
System. out.println(str2); //HELLO WORLD
System.out.println(str1); //Hello World
}
}
當把str1傳遞給upcase()方法時,實際傳遞的是引用的一個拷貝。其實,每當把String物件作為方法的引數時,都會複製一份引用,而該引用所指的物件其實一直待在單一的物理位置上,從未動過。
String常量池(上一章仔細講了)
常量池(constant pool)指的是在編譯期被確定,並被儲存在已編譯的.class檔案中的一些資料。它包括了關於類、方法、介面等中的常量,也包括字串常量。Java為了提高效能,靜態字串(字面量/常量/常量連線的結果)在常量池中建立,並儘量使用同一個物件,重用靜態字串。對於重複出現的字串直接量,JVM會首先在常量池中查詢,如果常量池中存在即返回該物件。
public class test1 {
public static void main(String[] args){
String str1 = "Hello";
//不會建立新的String物件,而是使用常量池中已有的"Hello",
String str2 = "Hello";
System.out.println(str1 == str2); //true
//使用new關鍵字會建立新的String物件
String str3 = new String("Hello");
System.out.println(str1 == str3); //false
}
}
String、StringBuffer和StringBuilder的區別
1.物件的可變與不可變(不可變的底層原因)
翻開JDK原始碼,java.lang.String類起手前三行,是這樣寫的:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
/** String本質是個char陣列. 而且用final關鍵字修飾.*/
private final char value[]; ... ...
}
---------------------
作者:How 2 Play Life
來源:CSDN
原文:https://blog.csdn.net/a724888/article/details/80048782
版權宣告:本文為博主原創文章,轉載請附上博文連結!
首先String類是用final關鍵字修飾,這說明String不可繼承。再看下面,String類的主力成員欄位value是個char[]陣列,而且是用final修飾的。final修飾的欄位建立以後就不可改變。 有的人以為故事就這樣完了,其實沒有。因為雖然value是不可變,也只是value這個引用地址不可變。擋不住Array陣列是可變的事實。
Array的資料結構看下圖:
也就是說Array變數只是stack上的一個引用,陣列的本體結構在heap堆。String類裡的value用final修飾,只是說stack裡的這個叫value的引用地址不可變。沒有說堆裡array本身資料不可變。
final int[] value={1,2,3} ;
int[] another={4,5,6};
value=another; //編譯器報錯,final不可變 value用final修飾,編譯器不允許我把value指向堆區另一個地址。
但如果我直接對陣列元素動手,分分鐘搞定。
final int[] value={1,2,3};
value[2]=100; //這時候數組裡已經是{1,2,100} 所以String是不可變,關鍵是因為SUN公司的工程師。在後面所有String的方法裡很小心的沒有去動Array裡的元素,沒有暴露內部成員欄位。
private final char value[]這一句裡,private的私有訪問許可權的作用都比final大。而且設計師還很小心地把整個String設成final禁止繼承,避免被其他人繼承後破壞。所以String是不可變的關鍵都在底層的實現,而不是一個final。考驗的是工程師構造資料型別,封裝資料的功力。
---------------------
作者:How 2 Play Life
來源:CSDN
原文:https://blog.csdn.net/a724888/article/details/80048782
版權宣告:本文為博主原創文章,轉載請附上博文連結!
重點理解:
private的私有訪問許可權的作用都比final大。而且設計師還很小心地把整個String設成final禁止繼承,避免被其他人繼承後破壞。所以String是不可變的關鍵都在底層的實現,而不是一個final。(String的不可變也是基於程式碼封裝和訪問控制的)
StringBuilder與StringBuffer都繼承自AbstractStringBuilder類,在AbstractStringBuilder中也是使用字元陣列儲存資料,這兩種物件都是可變的。如下:char[ ] value;
下面是jdk1.8中AbstractStringBuilder的部分原始碼
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
/**
* The count is the number of characters used.
*/
int count;
jdk1.8 StringBuilder部分原始碼
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
/** use serialVersionUID for interoperability */
static final long serialVersionUID = 4383685877147921099L;
/**
* Constructs a string builder with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuilder() {
super(16);
}
jdk1.8 StringBuffer部分原始碼
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
/**
* A cache of the last value returned by toString. Cleared
* whenever the StringBuffer is modified.
*/
private transient char[] toStringCache;
/** use serialVersionUID from JDK 1.0.2 for interoperability */
static final long serialVersionUID = 3388685877147921107L;
/**
* Constructs a string buffer with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuffer() {
super(16);
}
作者:lucus_guo
連結:https://www.jianshu.com/p/f87fd4dbdf23
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。在這裡插入程式碼片
2.執行緒是否安全
String中的物件是不可變的,也就可以理解為常量,所以是執行緒安全。
AbstractStringBuilder是StringBuilder與StringBuffer的公共父類,定義了一些字串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。
StringBuffer對方法加了同步鎖或者對呼叫的方法加了同步鎖,所以是執行緒安全的。
StringBuilder並沒有對方法進行加同步鎖,所以是非執行緒安全的。
如果程式不是多執行緒的,那麼使用StringBuilder效率高於StringBuffer。
建立了幾個物件的問題
1.String str1 = "abc";
2.String str2 = new String("abc");
對於1中的 String str1 = “abc”,首先會檢查字串常量池中是否含有字串abc,如果有則直接指向,如果沒有則在字串常量池中新增abc字串並指向它.所以這種方法最多建立一個物件,有可能不建立物件。
對於2中的String str2 = new String(“abc”),首先會在堆記憶體中申請一塊記憶體儲存字串abc,str2指向其記憶體塊物件。同時還會檢查字串常量池中是否含有abc字串,若沒有則新增abc到字串常量池中。所以 new String()可能會建立兩個物件。
1 String temp="apple";
2 for(int i=0;i<1000;i++) {
3 temp=temp+i;
4 }
答案: 1001個物件
1 String temp=new String("apple")
2 for(int i=0;i<1000;i++) {
3 temp=temp+i;
4 }
答案:1002個物件