美文网首页
String整理

String整理

作者: lesline | 来源:发表于2019-03-15 09:33 被阅读0次

浅谈StringBuilder - 简书
理解Java常量池 - gegewx - 博客园
通过反编译深入理解Java String及intern - liuxiaopeng - 博客园

深入分析String.intern和String常量的实现原理 - 简书
深入解析String#intern -


总结:

原理

常量存储内容

  • java6常量池在永久代
  • java7+java8常量池在堆中常量池中可以存储堆中的引用
  • 方法名、java等字符串在代码执行前已在常量池中存在

intern定义:

  • 如果常量池中存在当前字符串,就会直接返回当前字符串.如果常量池中没有此字符串,会将此字符串放入常量池中后再返回,返回的是常量池中的引用
  • java7及以后 常量池中存储的是:堆中的引用
    如果堆中有,常量池中没有,如何确定堆中是有的?因为执行intern方法的对象已确定。

对象声明

  • newString(“china”)每次执行都会在堆中建一个新的对象,但常量区就一个,返回堆中的引用

对象数

String str1 = “a”;//一个对象 放在常量池中(不会指向堆中的对象)
String str1 = new String(“a”);//两个对象(无论是jdk1.6还是1.7) 一个堆中 一个常量区 返回堆中的
String s3 = new String(“1”) + new String(“1”);//四个对象,一个常量区的1 一个堆中的11 两个堆中的匿名对象1
String c = “a” + “b” + “c”;  //应该是一个,编译时已确定(三个对象,都在常量区中 操作会加到常量池中)

String test = “test”;

通过反编译出来的字节码可以看出字符串 “test” 在常量池中的定义方式:

#2 = String             #14            // test
#14 = Utf8              test

在main方法字节码指令中,0 ~ 2行对应代码 String test = “test”; 由两部分组成:ldc #2 和 astore_1。

 // main方法字节码指令
 public static void main(java.lang.String[]);
   Code:
      0: ldc           #2                  // String test
      2: astore_1
      3: return

1、Test类加载到虚拟机时,”test"字符串在Constant pool中使用符号引用symbol表示,当调用 ldc #2 指令时,
如果Constant pool中索引 #2 的symbol还未解析,则调用C++底层的 StringTable::intern 方法生成char数组,
并将引用保存在StringTable和常量池中,当下次调用 ldc #2 时,可以直接从Constant pool根据索引 #2 获取 "test" 字符串的引用,
避免再次到StringTable中查找。
2、astore_1指令将”test”字符串的引用保存在局部变量表中。

总结:String test = “test”; 只在常量池中操作,跟堆中没关系。

ldc:Push item from run-time constant pool,从常量池中加载指定项的引用到栈。
astore_<n>:Store reference into local variable,将引用赋值给第n个局部变量

ldc      #2:加载常量池中的第二项("baseStr")到栈中。
astore_1 将引用类型或returnAddress类型值存入局部变量1

指令格式: 
astore index: 将栈顶数值(objectref)存入当前frame的局部变量数组中指定下标(index)处的变量中,栈顶数值出栈。
astore_0  该指令的行为类似于astore指令index为0的情况。

String str1 = new String(“a”);

String str2 = new String("str");
System.out.println(str2 == "str");//false

javap -c

Constant pool:
   #1 = Methodref          #6.#22         // java/lang/Object."<init>":()V
   #2 = Class              #23            // java/lang/String
   #3 = String             #24            // str
   #4 = Methodref          #2.#25         // java/lang/String."<init>":(Ljava/lang/String;)V

    Code:
       0: new           #2                  // class java/lang/String
       3: dup
       4: ldc           #3                  // String str
       6: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
       9: astore_1
      10: return

可以看出生成了两个对象:一个是第4行在常量池中生成的对象,一个是第6行初始化的对象。

String str2 = new String("str")+new String("01");

        String str2 = new String("str") + new String("01");
        System.out.println(str2 == "str01");//false
        System.out.println(str2.intern() == "str01");//true
    @Test
    public void test1() {
        String str2 = new String("str")+new String("01");
        str2.intern();
        String str1 = "str01";
        System.out.println(str2==str1);//true
    }
    @Test
    public void test2() {
        String str1 = "str01";
        String str2 = new String("str")+new String("01");
        str2.intern();
        System.out.println(str2 == str1);//false
    }

这个原因主要是从JDK 1.7后,HotSpot 将字符常量池从永久代移到了堆中,正因为如此,JDK 1.7 后的intern方法在实现上发生了比较大的改变,JDK 1.7后,intern方法还是会先去查询常量池中是否有已经存在,如果存在,则返回常量池中的引用,这一点与之前没有区别,区别在于,如果在常量池找不到对应的字符串,则不会再将字符串拷贝到常量池,而只是在常量池中生成一个对原字符串的引用。

所以:test1中,因为常量池中没有“str01”这个字符串,所以会在常量池中生成一个对堆中的“str01”的引用,而在进行字面量赋值的时候,常量池中已经存在,所以直接返回该引用即可,因此str1和str2都指向堆中的字符串,返回true。
调换位置以后,因为在进行字面量赋值(String str1 = “str01")的时候,常量池中不存在,所以str1指向的常量池中的位置,而str2指向的是堆中的对象,再进行intern方法时,对str1和str2已经没有影响了,所以返回false。

看以下示例:

        String s1 = new String("a") + new String("b");
        /**
         String s11 = "ab"; //存在这个前后值不一样
         是否存在 String s11 = "ab";
         不存在 存在
         true   true
         true   false
         true   false
         */
        System.out.println(s1.intern() == "ab");
        System.out.println(s1.intern() == s1);
        System.out.println(s1 == "ab");

        String s2 = new String("ja") + new String("va");
        System.out.println(s2.intern() == "java");//true
        System.out.println(s2.intern() == s2);//false
        System.out.println(s2 == "java");//false

原因: 不存在 String s11 = "ab”;时,s1.intern()会将在常量池中查找是否存在“ab”,查找不存在,会在常量池中生成对s1对象的引用,所以s1.intern() == "ab" ,s1 == "ab”;
存在 String s11 = "ab”;时,s11 = "ab"; 会常量直接操作常量池中生成常量,不会查找是否存在“ab”,所以生成的常量不会引用堆中(s1)对角,所以s1.intern() == "ab" ,s1 <> "ab”。


引用

intellij idea中使用External Tool实现javap等工具 - 简书

相关文章

  • String整理

    从网上复制的,看别人的比较全面,自己搬过来,方便以后查找。原链接:https://www.cnblogs.com/...

  • String整理

    浅谈StringBuilder - 简书理解Java常量池 - gegewx - 博客园通过反编译深入理解Java...

  • GO语言

    map相关操作整理 知识点整理: 创建 make(map[string]string) 获取元素 m[key] k...

  • String知识

    **以下内容整理自互联网,仅用于个人学习 ** String源码分析 一、String类 String是被fina...

  • String的用法整理

    在C++里熟练使用String的前提是:忘掉char[]。 字符串的初始化 string str(str1,int...

  • GO语言 map相关操作整理

    知识点整理: 创建 make(map[string]string) 获取元素 m[key] key不存在 会获取v...

  • mongo使用方法整理

    更新某个string字段整理后改为int类型字段 嵌套查询

  • LC吐血整理-String篇

    github-Leecode_summary 28.实现strStr() 知识点:数组的切片题外话:真是个呆子,我...

  • Java String 常用操作整理

    记录常用的String操作, 包括String和StringUtils下的方法 (1) stratEndWith(...

  • String属性和方法整理

    String全局对象是一个用于字符串或一个字符串序列的构造函数。 语法: 有特殊功能的字符可以通过转义字符的形式放...

网友评论

      本文标题:String整理

      本文链接:https://www.haomeiwen.com/subject/qwwkmqtx.html