String.intern方法詳解

記錄建立String的兩種方式,"" 和 new String()區別,String intern方法的使用和常量池。java

  • String的使用 (Jdk1.8)

    代碼

    package com.com.string;
    
    /** * @Auther: lantao * @Date: 2019-04-15 13:53 * @Company: 隨行付支付有限公司 * @maill: lan_tao@suixingpay.com * @Description: TODO */
    public class StringTest {
        public static void main(String[] args) {
            // 使用 "" 建立 會直接存儲在 常量池中
            String a = "lantao";
            // 使用 new String 建立, 會將zahngsan存儲到常量池中,而後在Heap中建立對象指向b
            String b = new String("zhangsan");
            // 使用 字符串鏈接符拼接 ,會直接存儲 'wangwuzhaoliu' 字符串在常量池中
            String c = "wangwu" + "zhaoliu";
            // 使用字符串 "引用" 拼接 ,不執行 intern 方法,不會存放到常量池中,可是會將 --- 存入到常量池中
            String d = a + "---";
            // 使用 new String 拼接 ,不執行 intern 方法,不會存放到常量池中,可是會將wang 和 jiu 兩個字符串存到常量池中
            String f = new String("wang") + "jiu";
            // 使用 new String 拼接 ,不執行 intern 方法,不會存放到常量池中, 可是會將 zhao 和 ba 兩個字符串存入到常量池中
            String g = new String("zhao") + new String("ba");
        }
    }
    複製代碼

    解釋

    • 變量a: "lantao" 是字符串常量,在編譯期就被肯定了,先檢查字符串常量池中是否含有"lantao"字符串,若沒有則添加"lantao"到字符串常量池中,而且直接指向它。因此a直接指向字符串常量池的」lantao」,也就是變量a指向的地址是常量池中的 lantao
    • 變量b:用new String() 建立的字符串不是常量,不能在編譯期就肯定,因此new String() 建立的字符串不放入常量池中,它們有本身的地址空間(Java Heap 中)變量b的引用的地址在Java Heap中。 可是"zhangsan"字符串常量在編譯期也會被加入到字符串常量池(若是常量池不存在的話)。
    • 變量c:"wangwu"和"zhaoliu"也都是字符串常量,當一個字符串多個字符串常量鏈接而成時,它本身確定也是字符串常量,在編譯器會被編譯器優化成"wangwuzhaoliu",因此c也一樣在編譯期就被解析爲一個字符串常量,而且c是常量池中」wangwuzhaoliu」的一個引用,因此變量c的引用地址在常量池中
    • 變量d: JVM對於字符串引用,因爲在字符串的」+」鏈接中,有字符串引用存在,而引用的值在程序編譯期是沒法肯定的,即`(a+"---")
    • 變量f: 變量f一樣不能在編譯期肯定,可是"wang"和"jiu"這兩個字符串常量會添加到字符串常量池中,而且在堆中建立String對象。(字符串常量池並不會存放"wangjiu"這個字符串,除非執行f.intern()方法)
    • 變量g: 同理變量f。

    圖解

    String 拼接

    • 字符串拼接git

      public class StringTest {
          public static void main(String[] args) {
              String a = "lan" + "tao";
          }
      }
      複製代碼

      lan 和 tao 都是字符串,都是在編譯器可知的,編譯器會將這行代碼優化,當一個字符串是由多個可知的字符串(非引用字符串)鏈接組成,將會優化爲以下。github

      public class StringTest {
          public static void main(String[] args) {
            String a = "lantao";  
          }
      }
      複製代碼

      JVM會將字符串"lantao"放入到String常量池中。app

    • 引用拼接:ide

      public class StringTest {
          public static void main(String[] args) {
              String a = "lan";
            	String b = a + "tao";
            	// 上下含義相同
            	String c = "zhang";
            	String d = "san";
            	String f = c + d;
          }
      }
      複製代碼

      當Java編譯器遇到字符串引用字符串引用和可知字符串拼接的時候,會建立一個StringBuilder對象,後面的append()。優化

      由於有字符串引用存在,而引用的值在程序編譯期是沒法肯定的。另外 "lan"、"tao" 都會編譯器添加到字符串常量池中(若是沒有的話),由於它們都是編譯期肯定的字符串常量,可是最後的"lantao"並不會添加到字符串常量池, 除非執行b.intern() 方法ui

    • final拼接spa

      public class StringTest {
          public static void main(String[] args) {
              final String a = "lan";
           		final String b = "tao";
            	String c = a + b + "2019";
          }
      }
      複製代碼

      final拼接和以上二者的區別就是在前邊增長了final修飾,用final修飾的字符串就是在編譯期可知的,編譯期就會將以上代碼優化爲.net

      public class StringTest {
          public static void main(String[] args) {
      				String str = "lantao2019";
          }
      }
      複製代碼

      這裏 final 拼接的效果是和字符串拼接是一致的。code

  • String#intern方法

    intern方法詳解

    String.intern()是一個Native(本地)方法,它的做用是若是字符串常量池已經包含一個等於此String對象的字符串,則返回字符串常量池中這個字符串的引用, 不然將當前String對象的引用地址(堆中)添加到字符串常量池中並返回

    使用

    注意:基本數據類型之間的 == 是比較值,引用數據類型 == 比較的是地址值

    • 常量池中存在字符串

      public class StringTest {
          public static void main(String[] args) {
            	// 基本數據類型之間的 == 是比較值,引用數據類型 == 比較的是地址值
            	// 1:在Java Heap中建立對象 2:在字符串常量池中添加 zhangsan
              String a = new String("zhangsan");
            	// 調用 intern 方法,因上一步中已經將zhangsan存入常量池中,這裏直接返回常量池 zhangsan 的引用地址
              String b = a.intern();
            	// a 的地址在Java Heap中 , b的地址在 常量池中 ,因此結果是flase
              System.out.println(a == b);
            	// 由於常量池中已經包含zhangsan,因此直接返回
              String c = "zhangsan";
              // b c 的地址一致,因此是true
              System.out.println(b == c);
          }
      }
      
      //結果
      false
      true
      複製代碼

      解釋:

      1:在Java Heap建立對象而後在字符串常量池中添加 zhangsan。

      2:調用 intern 方法,因上一步中已經將zhangsan存入常量池中,這裏直接返回常量池 zhangsan 的引用地址。

      3:因 a 的地址在Heap中,b的地址在字符串常量池中。

      4:由於常量池中已經包含zhangsan,因此直接返回

      5: b c 的地址一致,因此是true

      地址可使用System.identityHashCode(a)方法獲取

    • 常量池中不存在字符串

      public class StringTest {
          public static void main(String[] args) {
            	//1: 首先會在Heap中建立對象,而後在常量池中放入zhagnsan 和 wangwu ,可是並不會放入zhagnsanwangwu
              String a = new String("zhangsan") + "wangwu";
            	// 2:調用 intern ,由於字符串常量池中沒有」zhangsanwangwu」這種拼接後的字符串,因此將堆中String對象的引用地址添加到字符串常量池中。jdk1.7後常量池引入到了Heap中,因此能夠直接存儲引用
              String b = a.intern();
            	// 3:由於 a 的地址和 b的地址一致,鎖以是true
              System.out.println(a == b);
            
            	//4:因常量池中已經存在 zhangsanwangwu 了,因此直接返回引用就是 a 類型 a ==b 鎖 a==b==c
              String c = "zhangsanwangwu";
              System.out.println(a == c); // true
            	System.out.println(b == c); // true
            
            	// 5:首先會在Heap中建立對象,而後會在常量池中存儲 zhang 和 san
            	String d = new String("zhang") + "san";
            	// 6: 返回的是 常量池中的 地址,因在a變量時已經將 zhangsan 放入到了常量池中
            	String f = d.inter();
            	System.out.println(d = f); // false
          }
      }
      複製代碼

      解釋:

      1:首先會在Heap中建立對象a,而後在常量池中放入zhagnsan 和 wangwu** ,可是並不會放入zhagnsanwangwu。

      2:調用 intern ,由於字符串常量池中沒有」zhangsanwangwu」這種拼接後的字符串,因此將堆中String對象的引用地址添加到字符串常量池中。jdk1.7後常量池引入到了Heap中,因此能夠直接存儲引用。

      3:由於 a 的地址和 b的地址一致,因此是true。

      4:因常量池中已經存在 zhangsanwangwu 了,因此直接返回引用就是 a 類型, a ==b 因此 a==b==c。

      5:首先會在Heap中建立對象d,而後會在常量池中存儲 zhang 和 san

      6:因在建立對象a時,已經將 "zhangsan"放入到了常量池,因此返回的是常量池中的zhangsan地址,對象d的地址在Heap中,f的地址在常量池中,並非一個,因此false;

博客地址:lantaoblog.site

參考:blog.csdn.net/qian520ao/a…

相關文章
相關標籤/搜索