最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Java String中intern方法的原理與使用

 更新時間:2022年05月19日 11:31:38   作者:IT利刃出鞘  
這篇文章主要為大家介紹了Java String中intern方法的原理以及使用。文中通過圖片和示例代碼進行了詳細展示,感興趣的小伙伴可以跟隨小編一起學習一下

簡介

本文介紹Java的String的intern方法的原理。

常量池簡介

在 JAVA 語言中有8種基本類型和一種比較特殊的類型String。這些類型為了使他們在運行過程中速度更快,更節(jié)省內(nèi)存,都提供了一種常量池(在方法區(qū))的概念。常量池就類似一個JAVA系統(tǒng)級別提供的緩存。8種基本類型的常量池都是系統(tǒng)協(xié)調(diào)的,String類型的常量池比較特殊。

String的常量池的主要使用方法有兩種:

直接使用雙引號聲明出來的String對象會直接存儲在常量池中。

如果不是用雙引號聲明的String對象,可以使用String提供的intern方法將其放到常量池。

intern方法簡介(JDK7)

原型:public native String intern();

說明:

從字符串常量池中查詢當前字符串是否存在(通過equals判斷)。

  • 如果存在,返回常量池中的字符串引用。
  • 如果不存在,把這個String對象引用存到常量池,然后返回這個String對象的引用。

返回值:都是返回String變量對應(yīng)的字符串常量池的引用。

示例

package com.example;
 
public class Demo {
    public static void main(String argv[]) {
        String s = "test";
        System.out.println(s == s.intern());
    }
}

JDK6及之前:輸出false

JDK7及之后:輸出true

原理(JDK6與JDK7)

常量池里的字符串的由來

JDK6及以前調(diào)用String.intern()

  • 若常量池中有,則返回常量池中這個字符串的引用
  • 若常量池中沒有,則拷貝一份對象,放到常量池(永久代)中;返回值是常量池(永久代)中對應(yīng)字符串實例的引用。

JDK7及以后調(diào)用String.intern()

  • 若常量池中有,則返回常量池中這個字符串的引用
  • 若常量池中沒有,則拷貝一份引用,放到常量池(堆)中;(JDK1.7將String常量池從Perm區(qū)移動到了Java Heap區(qū))

例程測試

例程1: 

package org.example.a;
 
public class Demo {
    public static void main(String argv[]) {
        String s1 = new String("1");
        s1.intern();
        String s2 = "1";
        System.out.println(s1 == s2);
 
        String s3 = new String("1") + new String("1");
        s3.intern();
        String s4 = "11";
        System.out.println(s3 == s4);
    }
}

結(jié)果

jdk6:false false
jdk7:false true
jdk8:false true

例程2: 

package org.example.a;
 
public class Demo {
    public static void main(String argv[]) {
        String s1 = new String("1");
        s1.intern();
        String s2 = "1";
        System.out.println(s1 == s2);
 
        String s3 = new String("1") + new String("1");
        String s4 = "11";
        s3.intern();
        System.out.println(s3 == s4);
    }
}

上述代碼第二部分有個對調(diào)。

結(jié)果

jdk6:false false
jdk7:false false
jdk8:false false

例程分析

下邊圖中:綠色線條代表 String 對象的內(nèi)容指向。 紅色線條代表地址指向。

jdk1.6

例程1與例程2的分析

如上圖所示。首先說一下 jdk6中的情況,在 jdk6中上述的所有打印都是 false 的,因為 jdk6中的常量池是放在 Perm 區(qū)中的,Perm區(qū)和正常的 JAVA Heap 區(qū)域是完全分開的。上面說過如果是使用引號聲明的字符串都是會直接在字符串常量池中生成,而 new出來的 String 對象是放在 JAVA Heap 區(qū)域。所以拿一個 JAVA Heap 區(qū)域的對象地址和字符串常量池的對象地址進行比較肯定是不相同的,即使調(diào)用String.intern方法也是沒有任何關(guān)系的。

jdk1.7

在 Jdk6 以及以前的版本中,字符串的常量池是放在堆的Perm區(qū)的,Perm區(qū)是一個類靜態(tài)的區(qū)域,主要存儲一些加載類的信息,常量池,方法片段等內(nèi)容,默認大小只有4m,一旦常量池中大量使用 intern 會產(chǎn)生java.lang.OutOfMemoryError:PermGen space錯誤的。在 jdk7 的版本中,字符串常量池已經(jīng)從Perm區(qū)移到正常的Java Heap區(qū)域了。為什么要移動,Perm 區(qū)域太小是一個主要原因,當然據(jù)消息稱jdk8已經(jīng)直接取消了Perm區(qū)域,而新建立了一個元區(qū)域。應(yīng)該是jdk開發(fā)者認為Perm區(qū)域已經(jīng)不適合現(xiàn)在 JAVA的發(fā)展了。字符串常量池移動到JAVA Heap區(qū)域,現(xiàn)在解釋為什么會有上述的打印結(jié)果。

例程1的分析

1.String s1 = new String("1"); 

分析:這行代碼生成了2個對象(常量池中的“1” 和 JavaHeap 中的字符串對象)。s.intern(); 這一句是 s1 對象去常量池中尋找后發(fā)現(xiàn) “1” 已經(jīng)在常量池里了。

此時s1指向的是Java Heap中的字符串對象。

2.String s2 = "1"; 

分析:這行代碼生成一個 s2的引用指向常量池中的“1”對象。 結(jié)果就是 s1 和 s2 的引用地址不同。

3.String s3 = new String("1") + new String("1");

分析:這行代碼生成了2個對象(字符串常量池中的“1” 和 Java Heap中的 s3 引用指向的對象“11”(中間還有2個匿名的new String("1")我們不討論它)。
此時s3 是Java Heap中的字符串對象的引用,對象內(nèi)容是”11″,此時常量池中是沒有 “11”對象的。

4.s3.intern();

分析:這行代碼將 s3中的"11"字符串放入String 常量池中,因為此時常量池中不存在"11"字符串,因此常規(guī)做法是跟 jdk6 圖中表示的那樣,在常量池中生成一個"11"的對象,關(guān)鍵點是 jdk7 中常量池不在Perm區(qū)域,而是在堆中了。常量池中不需再存儲一份對象了,可以直接存儲堆中的引用。這份引用指向s3引用的對象。 也就是說引用地址是相同的。

此時,s3是Java Heap中的字符串對象的引用,對象內(nèi)容是”11″,此時常量池中是有 “11”對象,它保存的就是s3引用地址。

5.String s4 = "11"; 

這行代碼”11″是顯式聲明的,因此會直接去常量池中創(chuàng)建,創(chuàng)建時發(fā)現(xiàn)已經(jīng)有這個對象了。

此時:s4 == 常量池的“11”對象引用 == s3引用對象的引用

例程2的分析

String s1 = new String("1");

s1.intern();

String s2 = "1";

分析:s1.intern();,這一句往后放也不會有什么影響了,因為對象池中在執(zhí)行第一句代碼String s = new String("1");的時候已經(jīng)生成“1”對象了。下邊的s2聲明都是直接從常量池中取地址引用的。 s1 和 s2 的引用地址是不會相等的。

String s3 = new String("1") + new String("1");

分析:這行代碼生成了2個對象(字符串常量池中的“1” 和 Java Heap中的 s3 引用指向的對象“11”(中間還有2個匿名的new String("1")我們不討論它)。

此時s3 是Java Heap中的字符串對象的引用,對象內(nèi)容是”11″,此時常量池中是沒有 “11”對象的。

String s4 = "11";

分析:聲明 s4 的時候常量池中是不存在“11”對象的,執(zhí)行完后,s4是常量池里“11“對象的引用。

s3.intern(); 

分析:此時常量池中“11”對象已經(jīng)存在了,不會有任何操作,s3仍然是堆中String對象的引用。因此 s3 != s4

應(yīng)用實例

package org.example.a;
 
import java.util.Random;
 
public class Demo {
    static final  int MAX = 1000 * 10000;
    static final String[] arr = new String[MAX];
    public static void main(String argv[]) {
        Integer[] DB_DATA = new Integer[10];
        Random random = new Random(10 * 10000);
        for(int i = 0; i < DB_DATA.length; i++){
            DB_DATA[i] = random.nextInt();
        }
 
        long t = System.currentTimeMillis();
        for(int i = 0; i < MAX; i++){
            //arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length]));
            arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length])).intern();
        }
 
        System.out.println((System.currentTimeMillis() -t) + "ms");
        System.gc();
    }
}

上述代碼是一個演示代碼,其中有兩條語句不一樣,一條是使用 intern,一條是未使用 intern。

運行的參數(shù)是:-Xmx2g -Xms2g -Xmn1500M

不用intern

2160ms

使用intern

826ms

通過上述結(jié)果,我們發(fā)現(xiàn)不使用 intern 的代碼生成了1000w 個字符串,占用了大約640m 空間。 使用了 intern 的代碼生成了1345個字符串,占用總空間 133k 左右。其實通過觀察程序中只是用到了10個字符串,所以準確計算后應(yīng)該是正好相差100w 倍。雖然例子有些極端,但確實能準確反應(yīng)出 intern 使用后產(chǎn)生的巨大空間節(jié)省。

細心的同學會發(fā)現(xiàn)使用了 intern 方法后時間上有了一些增長。這是因為程序中每次都是用了 new String 后, 然后又進行 intern 操作的耗時時間,這一點如果在內(nèi)存空間充足的情況下確實是無法避免的,但我們平時使用時,內(nèi)存空間肯定不是無限大的,不使用 intern占用空間導(dǎo)致 jvm 垃圾回收的時間是要遠遠大于這點時間的。 畢竟這里使用了1000w次intern 才多出來1秒鐘多的時間。

到此這篇關(guān)于詳解Java String中intern方法的原理與使用的文章就介紹到這了,更多相關(guān)Java String intern方法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • mybatis模糊查詢、分頁和別名配置的方法

    mybatis模糊查詢、分頁和別名配置的方法

    這篇文章主要介紹了mybatis模糊查詢、分頁和別名配置,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • Spark學習筆記Spark Streaming的使用

    Spark學習筆記Spark Streaming的使用

    這篇文章主要介紹了Spark學習筆記Spark Streaming的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-06-06
  • Java如何實現(xiàn)生成報表并將其導(dǎo)出為Excel

    Java如何實現(xiàn)生成報表并將其導(dǎo)出為Excel

    在許多企業(yè)應(yīng)用中,生成報表并將其導(dǎo)出到Excel表格是一項常見需求,這篇文章主要為大家介紹了Java如何實現(xiàn)生成報表并將其導(dǎo)出為Excel,需要的可以參考下
    2025-02-02
  • java使用jacob.jar將word轉(zhuǎn)pdf

    java使用jacob.jar將word轉(zhuǎn)pdf

    這篇文章主要為大家詳細介紹了java利用jacob.jar將word轉(zhuǎn)pdf,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • 淺談一下Java中枚舉的用法

    淺談一下Java中枚舉的用法

    這篇文章主要介紹了淺談一下Java中枚舉的用法,枚舉是一個被命名的整型常數(shù)的集合,用于聲明一組帶標識符的常數(shù),當一個變量有幾種固定可能的取值時,就可以將它定義為枚舉類型,需要的朋友可以參考下
    2023-04-04
  • 初識MyBatis及基本配置和執(zhí)行

    初識MyBatis及基本配置和執(zhí)行

    這篇文章主要介紹了初識MyBatis的基本知識,文中給大家提到了mybatis基本配置和執(zhí)行過程,需要的朋友可以參考下
    2017-11-11
  • mybatis foreach批量插入數(shù)據(jù):Oracle與MySQL區(qū)別介紹

    mybatis foreach批量插入數(shù)據(jù):Oracle與MySQL區(qū)別介紹

    這篇文章主要介紹了,需要的朋友可以參考下
    2018-01-01
  • springboot項目中的bootstrap.yml配置不生效的原因及解決(沒有自動提示)

    springboot項目中的bootstrap.yml配置不生效的原因及解決(沒有自動提示)

    新創(chuàng)建一個 springboot項目,添加了 bootstrap.yml 文件,發(fā)現(xiàn)文件并沒有如預(yù)期變成綠色葉子,編寫的時候也沒有自動提示,啟動的時候,發(fā)現(xiàn)端口是8080,由此發(fā)現(xiàn)配置并沒有生效,所以本文給大家講解了springboot項目中的bootstrap.yml配置不生效的原因及解決
    2024-01-01
  • idea常用的18個設(shè)置(程序員必不可少)

    idea常用的18個設(shè)置(程序員必不可少)

    這篇文章主要給大家介紹了關(guān)于idea常用的18個設(shè)置,這些對程序員們來說必不可少,idea開發(fā)常用基本且非常實用的配置,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2023-08-08
  • 淺談Sharding-JDBC強制路由案例實戰(zhàn)

    淺談Sharding-JDBC強制路由案例實戰(zhàn)

    本文主要介紹了淺談Sharding-JDBC強制路由案例實戰(zhàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-07-07

最新評論

开远市| 开封市| 任丘市| 周至县| 海口市| 吉水县| 华容县| 长沙市| 博湖县| 德钦县| 英德市| 玉门市| 天津市| 华亭县| 新密市| 阳信县| 玉树县| 米泉市| 绵阳市| 梁山县| 绥棱县| 株洲县| 宜兴市| 永靖县| 通化县| 互助| 呼图壁县| 保德县| 虞城县| 永胜县| 抚顺县| 阜阳市| 舟曲县| 行唐县| 连江县| 黎川县| 临夏县| 巴林右旗| 仙居县| 小金县| 镇坪县|