位元組碼底層分析String

專注的阿熊發表於2020-05-25

String

特性

 

不可變性:不可變性也就是 如果建立了一個String物件,進行改變那麼就是再建立了一個物件,而不是在原有的改變。

jdk8之前內部儲存定義的是char型別陣列

jdk9以後是定義的byte型別陣列儲存,可以更好的節省了空間。

字串常量池在jdk1.8(含8)以後都是儲存在堆空間中的。

字串常量池

 

String pool 底層也就是HashTable

所以字串常量池是不可重複的

StringMT4

String s = “abc” s2 =“abc”. 在棧中其實引用的是同一個地址(編譯最佳化)

jdk1.6以前 HashTable 長度預設值1009 ,jdk7預設為 60013, jdk8以後設定長度最低要求1009

引數設定

 

-XX:StringTableSize=*

 

連結串列短的話會影響效率

 

實戰

 

可編譯看位元組碼,一下都是基於jdk1.8以後進行說明

 

宣告:1.字面量建立String 那麼會在字串常量池中新增一個空間,但是堆空間不會有地址;

 

宣告:2.new 方式建立String 那麼會在字串常量池中新增一個空間,並且在堆中開闢一個空間,並且引用堆空間的地址

 

宣告:3.兩個字面量拼接,那麼會編譯階段就會進行編譯最佳化如String c = "a"+"b";編譯期間會預設為String c = "ab",不會再字串常量池中分別開闢空間

 

宣告:4.在String c = new String("a")+new String("b")相加是不會在字串常量池中開闢新的空間的,c只是引用

 

public void test(){

        String s = "javaHeap";

        String s1 = "java";

        String s2 = "heap";

        String s3 = s1 + s2;

        /**

         * 因為編譯時 s1 s2 相當於在字串常量池中建立物件,不知道其中的值,需要引用地址

         * 當賦值時,那麼底層會使用 StringBuilder 的append 方法進行拼接,是引用了兩個地址,進行賦值,組成新的物件,並且字串常量池中是沒有"a、javaHeap"

         */

        boolean result = s == s3; //false

    }

 

public void test2(){

        String s = "javaHeap";

        final String s1 = "java";

        final String s2 = "heap";

        String s3 = s1 + s2;

        /**

         *  final在編譯期就已經確定下來了值,在進行字串拼接時會進行 編譯最佳化 String s3 = "javaHeap"

         */

        boolean result = s == s3; //true

    }

public static void test3(){

        String s = new String("javaHeap");

        /**

         * 在記憶體中建立了5個物件 細緻的說6個

         * 1.new 在堆中

         * 2.“字串”在字串常量池中

         * 3. + 變數拼接 建立了StringBuilder

         * 外加:StringBuilder 中的toString方法,底層也就是new String();但是不會在常量池中建立

         */

        String s2 = new String("java") + new String("Heap");

    }

public static void test4(){

        /**

         * 字面量和new 的區別

         * 1. 字面量會在字串常量池中 放入,new物件也會在常量池中放入"javaHeap"

         * 2. 字面量不會在堆中開闢空間,而new會在堆中開闢空間

         */

        String b = new String("javaHeap");//返回的是堆空間中的地址

        String a = "javaHeap";

        System.out.println(a==b);//false

    }

intern方法

簡介

Intern方法:檢視字串常量池中是否有 , 如果有,那麼返回字串常量池中的引用,如果沒有將會檢視堆中是否有資料,有將會引用物件地址,沒有那麼建立字串,放入常量池。

 

為什麼使用intern?

 

:降低記憶體的大小,提高的執行的速度,會自動釋放記憶體,會引用常量池中的資料

 

實戰詳解

 

public static void test(){

        /**

         *  記憶體結構詳解:

         *      new String 是在堆中建立了物件,並且在字串常量池中建立了字面量,

         *      第二行:(在建立字面量時 先檢視了堆中是否有物件的引用,如果有那麼就不會進行建立,之間在字串 常量池中引用了堆中的地址【節省空間】)

         *      String s = "javaHeap";

         *      字面量:只會在常量池中 建立常量。

         *

         *      所以在比較時 new 在常量池是物件引用地址,而字面量是值(或者說地址)所以false

         */

        String s2 = new String("javaHeap");

        s2.intern();

        String s = "javaHeap";

        System.out.println(s==s2);//false

    }

 

public static void test2(){

        /**

         *  記憶體結構詳解:

         *      這裡在進行第一行程式碼時 結構(建立了6個物件,最後在堆中生成了物件s ="javaHeap" 變數池中沒有生成)

         *        第二行:在變數池中生成

         *        第三行:直接引用變數池

         *

         */

        String s = new String("java") + new String("Heap");

        s.intern();

        String s2 = "javaHeap";

        System.out.println(s==s2);//true

    }

//-------------------------------------------------------------------------------------------------

public static void main(String[] args) {

        String str2 = new String("xin") + new String("cen");

        //String str2 = new String("a");

        String intern = str2.intern();

        String str = "xincen";

        System.out.println(intern==str);//true 比較串常量池中的地址

        System.out.println(str2==str);//false 比較了堆中的地址

    }


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69946337/viewspace-2694235/,如需轉載,請註明出處,否則將追究法律責任。

相關文章