1. 程式人生 > >字串常量池深入解析

字串常量池深入解析

字串常量池

概述

  在分析字串常量池之前,先來分析一下java的記憶體區域,然後再各種的情況分析一下各種情況下的情況;

mark

在《深入理解java虛擬機器》這本書上是這樣寫的:對於HotSpot虛擬機器,根據官方釋出的路線圖資訊,現在也有放棄永久代並逐步的改為採用Native Memory來實現方法區的規劃了,在目前已經發布的JDK1.7的HotSpot中,已經把原來存放在方法區中的字串常量池移出。根據查閱的資料顯示在JDK1.7以後的版本中字串常量池移到堆記憶體區域;同時在jdk1.8中移除整個永久代,取而代之的是一個叫元空間(Metaspace)的區域

  在 JAVA 語言中有8中基本型別和一種比較特殊的型別String

。這些型別為了使他們在執行過程中速度更快,更節省記憶體,都提供了一種常量池的概念。常量池就類似一個JAVA系統級別提供的快取。

  8種基本型別的常量池都是系統協調的,String型別的常量池比較特殊。它的主要使用方法有兩種:

  • 直接使用雙引號宣告出來的String物件會直接儲存在常量池中。
  • 如果不是用雙引號宣告的String物件,可以使用String提供的intern方法。intern 方法會從字串常量池中查詢當前字串是否存在,若不存在就會將當前字串放入常量池中

一、Java中兩種建立字串物件的方式的分析。

String s1 = "abc";
String s2 = "abc"
; System.out.println(s1==s2);

結果是 true;

採用字面值的方式建立一個字串時,JVM首先會去字串池中查詢是否存在”abc”這個物件,如果不存在,則在字串常量池中建立”abc”這個物件,然後將池中”abc”這個物件的引用地址返回給”abc”物件的引用s1,這樣s1會指向字串常量池中”abc”這個字串物件;如果存在,則不建立任何物件,直接將池中”abc”這個物件的地址返回,賦給引用s2。因為s1、s2都是指向同一個字串池中的”abc”物件,所以結果為true。

String s3 = new String("xyz");
String s4 = new String
("xyz"); System.out.println(s3==s4);

結果是 false

採用new關鍵字新建一個字串物件時,JVM首先在字串池中查詢有沒有”xyz”這個字串物件,如果有,則不在池中再去建立”xyz”這個物件了,直接在堆中建立一個”xyz”字串物件,然後將堆中的這個”xyz”物件的地址返回賦給引用s3,這樣,s3就指向了堆中建立的這個”xyz”字串物件;如果沒有,則首先在字串池中建立一個”xyz”字串物件,然後再在堆中建立一個”xyz”字串物件,然後將堆中這個”xyz”字串物件的地址返回賦給s3引用,這樣,s3指向了堆中建立的這個”xyz”字串物件。s4則指向了堆中建立的另一個”xyz”字串物件。s3 、s4是兩個指向不同物件的引用,結果當然是false。

二、Intern的實現原理(JDK1.8)

先看一下Intern的原始碼

/**
     * Returns a canonical representation for the string object.
     * <p>
     * A pool of strings, initially empty, is maintained privately by the
     * class {@code String}.
     * <p>
     * When the intern method is invoked, if the pool already contains a
     * string equal to this {@code String} object as determined by
     * the {@link #equals(Object)} method, then the string from the pool is
     * returned. Otherwise, this {@code String} object is added to the
     * pool and a reference to this {@code String} object is returned.
     * <p>
     * It follows that for any two strings {@code s} and {@code t},
     * {@code s.intern() == t.intern()} is {@code true}
     * if and only if {@code s.equals(t)} is {@code true}.
     * <p>
     * All literal strings and string-valued constant expressions are
     * interned. String literals are defined in section 3.10.5 of the
     * <cite>The Java&trade; Language Specification</cite>.
     *
     * @return  a string that has the same contents as this string, but is
     *          guaranteed to be from a pool of unique strings.
     */
    public native String intern();

String#intern方法中看到,這個方法是一個 native 的方法,但註釋寫的非常明瞭。“ 當呼叫 intern方法時,如果池已經包含一個等於此String物件的字串(用equals(oject)方法確定),則返回池中的字串。否則,將此String物件新增到池中,並返回此String物件的引用。

三、JDK1.7的Intern的執行

來看一段程式碼:

public static void main(String[] args) {
     String s1 = new String("1");
     String s3 = s1.intern();
     String s2 = "1";
     System.out.println(s1 == s2);
     System.out.println(s2 == s3);
     System.out.println(s1 == s3);
    //第二種情況
    String s3 = new String("1") + new String("1");
    String s5 = s3.intern();
    String s4 = "11";
    System.out.println(s3 == s4);
}

首先分析一下這個程式碼:

依據上面的說法,String s = new String("1");這行程式碼做的事情是;JVM首先在字串池中查詢有沒有”1”這個字串物件,可知在字串常量池中沒有“1”,則首先在字串池中建立一個”1”字串物件,然後再在堆中建立一個”1”字串物件,然後將堆中這個”1”字串物件的地址返回賦給s1引用,這樣,s1指向了堆中建立的這個”1”字串物件;String s3 = s1.intern();先來看一下JDk1.7及其之後的情況,首先會去字串常量池中檢查池是否包含一個等於“1”的字串,因為在字串常量池中已經有字串“1”物件,所以此時s3指向的是方法區中的物件;String s2 = "1";這句程式碼首先會去字串的常量池中查否包含一個等於“1”的字串,因為字串常量池中已經存在方法區中了,所以s2指向的是字串常量池中的物件。

答案就非常好理解了s1 == s2為false ,s2 == s3為true ,s1 == s3為false;

下面分析一下下面的第二種情況:

String s3 = new String("1") + new String("1");首先分析一下這句程式碼的執行,第一個new String("1")會在字串常量池和堆記憶體中建立兩個物件,第二個new String("1")此時因為在方法區中已經存在了字串“1”物件,所以不會再方法區中建立,但是仍然會在堆記憶體中建立字串“1”物件。但是new String("1") + new String("1")會在堆中建立一個物件(假設名稱為obj),物件的內容為”11”,s3指向新建立的obj物件;s3.intern();這行程式碼在JDK1.6及其以前的版本中的執行情況是,首先會去字串常量池中尋找是否已經包含一個等於此String物件(”11”)的字串(用equals(oject)方法確定),因為在常量池中沒有,將 s3中的“11”字串放入 String 常量池中 ,在常量池中生成一個 “11” 的物件 ;關鍵點是 jdk7常量池中不需要再儲存一份物件了,可以直接儲存堆中的引用。這份引用指向 s3 引用的物件。 也就是說引用地址是相同的  ;String s4 = "11";這句程式碼會在字串常量池中找”11”物件,因為前面intern()方法執行過了,所以此時返回的是s3引用物件的的引用,也就是”11”物件的地址;所以此時s3 == s4為true

關於這一段程式碼我這裡有一個疑問,如果有看到這篇文章的小夥伴希望你能幫我解答一下:

@Test
public void test4(){
    String s3 = new String("1") + new String("1");
    String s5 = s3.intern();
    String s4 = "11";
    System.out.println(s5 == s3);
    System.out.println(s5 == s4);
    System.out.println(s3 == s4);

    System.out.println("======================");

    String s6 = new String("go") +new String("od");
    String s7 = s6.intern();
    String s8 = "good";
    System.out.println(s6 == s7);
    System.out.println(s7 == s8);
    System.out.println(s6 == s8);
}

這段程式碼的執行結果是:

false
true
false

關於為什麼會出現這樣的結果,現在我是沒辦法解釋的通的;隨後我修改了字串“1”,把字串改為字串“2”出現的結果是true、true、true;這就更加令我想不通是為什麼呢?如果有小夥伴看到,希望能告訴我一下,非常的感謝!([email protected]

四:幾種特殊的情況的程式碼

先來看一下這種情況的程式碼:

1:程式碼中含有StringBuffer的

public void test5(){
        // Create three strings in three different ways.
        String s1 = "Hello";
        String s2 = new StringBuffer("He").append("llo").toString();
        String s3 = s2.intern();
        // Determine which strings are equivalent using the ==
        // operator
        System.out.println("s1 == s2? " + (s1 == s2));
        System.out.println("s1 == s3? " + (s1 == s3));
        System.out.println();
    }

先分析一下程式碼:

String s1 = "Hello";和上面分析的一樣,在字串常量池中建立“Hello”物件,然後返回字串常量池中“Hello”物件的引用。String s2 = new StringBuffer("He").append("llo").toString();這句程式碼會在字串常量池中建立“He”物件和“llo”物件,(其實像“a”+”b”這樣的程式碼,在編譯階段就會自動的有優化為StringBuilder.append()方法)然後在堆記憶體中會建立”He”物件,和內容為”Hello”的
物件,然後把這個物件的引用賦給s2;String s3 = s2.intern();去字串常量池中找有沒有值為”Hello”的物件,因為此時字串常量池中是有的,所以會把字串常量池中“Hello”物件的引用賦給s3,所以此時的s1的值和s3的值是相等的;s1和s2的值是不相等的

2:new String(引用)的情形

public void test3(){
        String m = "hello,world";
        String n = "hello,world";
        String u = new String(m);
        String v = new String("hello,world");
        System.out.println(m == u);
        System.out.println(u == v);
    }

分析String m = "hello,world" String n = "hello,world";這兩句程式碼和前面的分析一樣,會在字串常量池中建立一個物件“hello,world”,m和n指向的是字串常量池中的同一個物件;String u = new String(m);會在堆中生成一個新的物件,但是內部的字元陣列引用這m內部的字元陣列;String v = new String("hello,world");同樣會生成一個新的字元陣列物件在堆裡面,此時因為在常量池中已經存在了 “hello,world”物件,v指向的是堆裡面的物件

3:“Java”字串

public void test7(){
        //發現原來是在JVM啟動的時候呼叫了一些方法,在常量池中已經生成了"java"字串常量,
         String s2 = new String("ja") + new String("va");
         String s3 =  s2.intern();
         String s4 = "java";
         System.out.println(s2 == s3);
         System.out.println(s3 == s4);
    }
結果是:
fasle
true

分析在String s2 = new String("ja") + new String("va");程式碼中做的事情就是在字串常量池中儲存了”ja”和”va”物件,在堆裡面儲存的是”java”物件,String s3 = s2.intern();在字串常量池中尋找有沒有“java”物件,由於JVM的 特殊性在JVM啟動的時候呼叫了一些方法,在常量池中已經生成了“java”字串常量,所以s3直接返回字串常量池中已經存在的“java”,s4也是同樣的道理,所以結果就非常的明顯了

參考的部分文章

相關推薦

字串常量深入解析

字串常量池 概述   在分析字串常量池之前,先來分析一下java的記憶體區域,然後再各種的情況分析一下各種情況下的情況; 在《深入理解java虛擬機器》這本書上是這樣寫的:對於HotSpot虛擬機器,根據官方釋出的路線圖資訊,現在也有放棄永久

深入瞭解Java字串常量

java中有幾種不同的常量池,以下的內容是對java中幾種常量池的介紹以及重點研究一下字串常量池。 class常量池 我們寫的每一個Java類被編譯後,就會形成一份class檔案;class檔案中除了包含類的版本、欄位、方法、介面等描述資訊外,還有一項資訊就是常量池(constant poo

深入探究JVM之記憶體結構及字串常量

# 前言 Java作為一種平臺無關性的語言,其主要依靠於Java虛擬機器——JVM,我們寫好的程式碼會被編譯成class檔案,再由JVM進行載入、解析、執行,而JVM有統一的規範,所以我們不需要像C++那樣需要程式設計師自己關注平臺,大大方便了我們的開發。另外,能夠執行在JVM上的並只有Java,只要能夠編譯

理解Java字串常量與intern()方法

閱讀目錄 Java記憶體區域  兩種建立方式在記憶體中的區別 解釋開頭的例子 intern()方法 參考資料   String s1 = "Hello"; String s2 = "Hello"; String s3 = "Hel"

Java字串常量

字串的一些方法: java public class Demo{ public void demo(){ String s = "[哎]HH[呀][!]-[媽]=[呀]"; StringTonkenizer stk = new StringTonkenizer

02.字串常量 ? class常量? 執行時常量?

java物件建立流程: 簡介: 這幾天在看Java虛擬機器方面的知識時,看到了有幾種不同常量池的說法,然後我就去CSDN、部落格園等上找資料,裡面說的內容真是百花齊放,各自爭豔,因此,我好好整理了一下,將我自認為對的理解寫下來與大家共同探討: 在Java的記憶體分配中,總共3種

java字串常量——字串==比較的一個誤區

轉自:https://blog.csdn.net/wxz980927155/article/details/81712342     起因 再一次js的json物件的比較中,發現相同

關於java中的String類的字串常量的詳解

字串常量池       我們知道字串的分配和其他的物件分配一樣,是需要消耗高昂的時間和空間的,而且字串我們使用得非常多,JVM為了提高效能和減少記憶體的開銷,在例項化字串的時候做了一些優化:使用字串常量池。 每當我們建立字串常量時,JVM會首先檢查字串常量池,如果該字串已

java中的字串常量,棧和堆的概念

問題:String str = new String(“abc”),“abc”在記憶體中是怎麼分配的?    答案是:堆,字串常量區。 題目考查的為Java中的字串常量池和JVM執行時資料區的相關概念。 "abc"為字面量物件,其儲存在堆記憶體中。而字串常量池則儲存的是字

java的String與Stringpool(字串常量)

字串常量值與簡單語法: String s1 = "sworddancing"; String s2 = "sworddancing"; //在字串常量池中檢視是否含有與“sworddancing”相同的字串物件 //需要明確的是在java中每一個字串就是一個字串物件

瞭解intern(),瞭解字串常量

intern()是String一個api,平時不常用,但是面試可能會考,掌握intern可避免被面試官問到時大眼瞪小眼的尷尬局面。 當一個String物件呼叫intern()方法時,會首先檢查字串常量

後端---Java中的常量(字串常量、class常量和執行時常量)

在Java的記憶體分配中,總共3種常量池: 在JDK1.7之前執行時常量池邏輯包含字串常量池存放在方法區, 此時hotspot虛擬機器對方法區的實現為永久代 在JDK1.7 字串常量池被從方法區拿到了堆中, 這裡沒有提到執行時常量池,也就是說字串常量池被單獨拿到堆,執行時常量池剩下

JAVA String物件和字串常量的關係解析

轉載自:http://blog.csdn.net/sureyonder/article/details/5569366 1 字串內部列表   JAVA中所有的物件都存放在堆裡面,包括String物件。字串常量儲存在JAVA的.class檔案的常量池中,在編譯期就確定好了。虛

Java中的String、StringPool(字串常量)

字串常量池與簡單語法 String s = "hahahah"; String s2 = "hahahah"; 1->在字串常量池中檢視是否存在內容與"hahahah"相同的字串物件; 2-&

Java中的字串常量詳細介紹

Java中字串物件建立有兩種形式,一種為字面量形式,如String str = "droid";,另一種就是使用new這種標準的構造物件的方法,如String str = new String("droid");,這兩種方式我們在程式碼編寫時都經常使用,尤其是字面量的方式。然而這兩種實現其實存在著一些效能

JDK1.8版本java字串常量裡存的是String物件還是引用?

採用new關鍵字新建一個字串物件時,JVM首先在字串池中查詢有沒有"xyz"這個字串物件,如果有,則不在池中再去建立"xyz"這個物件了,直接在堆中建立一個"xyz"字串物件,然後將堆中的這個"xyz"物件的地址返回賦給引用s3,這樣,s3就指向了堆中建立的這個"xyz"字串物件;如果沒有,則首先在字串池中建

執行緒深入解析筆記

概述 這篇筆記也是整理於寒假實習期間,由於美顏相機demo中的縮圖渲染功能涉及到多執行緒,使用了執行緒池減少資源開銷,所以整理一下執行緒池筆記,以便以後查詢(待完善)。 使用執行緒池的主要目的在於: 1. 降低資源消耗 2. 提高響應速度 3.

Java中的常量(字串常量、class常量和執行時常量)

簡介: 這幾天在看Java虛擬機器方面的知識時,看到了有幾種不同常量池的說法,然後我就去CSDN、部落格園等上找資料,裡面說的內容真是百花齊放,各自爭豔,因此,我好好整理了一下,將我自認為對的理解寫下來與大家共同探討: 在Java的記憶體分配中,總共3種常量

什麼是字串常量

在理解字串常量前,我們先熟悉一下如何建立一個字串,在Java中有兩種方法可以建立一個字串物件: 使用new運算子。例如:String str = new String("Hello");使用字串常量或者常量表達式。例如: String str="Hello"; //(字

死磕 java執行緒系列之執行緒深入解析——體系結構

(手機橫屏看原始碼更方便) 注:java原始碼分析部分如無特殊說明均基於 java8 版本。 簡介 Java的執行緒池是塊硬骨頭,對執行緒池的原始碼做深入研究不僅能提高對Java整個併發程式設計的理解,也能提高自己在面試中的表現,增加被錄取的可能性。 本系列將分成很多個章節,本章作為執行緒池的第一章將對