1. 程式人生 > >字串的不可變性(String的不可變性)Integer的常量池

字串的不可變性(String的不可變性)Integer的常量池

JVM的記憶體分配圖:

PC暫存器:會記錄各個執行緒的執行位置

Java方法棧:面向java方法

本地方法棧:面向本地方法(用C++ 寫的native方法)

方法區:載入後的Java類會被存放在java方法區,程式碼實際執行的時候,虛擬機器會執行方法區內的程式碼。

 

為什麼字串是不可變的?

因為字串的底層使用的是陣列儲存,陣列的長度是不可變的。且使用final,private修飾,不能直接方法,String也沒有提供直接修改的方法。

由此我們知道:String一旦被建立,則不能被修改,所謂的修改也只是建立了新的物件。

字串常量在記憶體中的位置:

字串常量物件儲存在常量池中,jdk1.6及之前在方法區中,jdk1.7之後堆中特殊的區域中;jdk1.8,在元空間中(元空間是方法區的一種體現)

String str1 = new String(“hello”);
String str2 = “hello”;
System.out.println(str1 == str2);//false; 

第一句程式碼建立了幾個物件?

字串常量池中有一個物件,堆中有一個字串物件;
堆中的字串物件指向了常量池中常量物件的"hello";str1,str2在棧中;
new String("hello")在堆中;"hello"字串常量在常量池中

 為什麼是false?

str1引用在棧中,指向了堆中的物件,str2引用在棧中指向常量池(方法區),兩者的地址是不一樣的。 請注意:字串常量中的字串也是物件,堆中的字串物件都是指向了字串常量池中的物件的。

String str1 = "hellojava";
String str2 = "hello" +"java";
System.out.println(str1 == str2);//true

為什麼是true?

hellojava作為一個字串(常量)物件在字串常量池中存在,java編譯器發現hello和java是兩個常量,
是字串的拼接,拼接之後仍然是字串常量物件,依然儲存在常量池中,發現常量池中有這個常量物件,
直接將棧中的引用str2指向該存在的常量物件。這裡的注意:沒有涉及到變數的運算,相同的字串不會產生新的物件
String str1 = "hello";
String str2 = "java";
String str3 = "hellojava";
String str4 = str1 + "java";
String str5 = str1 + str2;

System.out.println(str3 == str4);//false
System.out.println(str3 == str5);//false

為什麼是這個結果?

詳解:str1指向常量池中的常量物件hello str2指向常量池中的常量物件java,str3指向常量池中的常量物件hellojava。str4 和 str5 都涉及到了String物件,實際會產生新的物件儲存在堆中,而str3 指向的是常量池(方法區中)
總結:變數參與運算(拼接),一定會產生新的物件。物件在堆中

關於String物件的圖解

(1)

(2)

任何的型別都有常量池的概念,比如說

比如下面的程式碼:

package hello_java;

public class Test {
    public static void main(String[] args) {
        Integer i1 = 127;
        Integer i2 = 127;

        Integer i3 = 128;
        Integer i4 = 128;
        System.out.println(i1 == i2);
        System.out.println(i3 == i4);
    }
}

true
false

 為甚呢?看下面的原始碼,重新建立一個Integer的物件的時候,首先看值的範圍,如果在該方位內的話,就使用常量池中的值。

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }