jvm的角度理解字符串(常量池与串池、字符串的拼接、intern方法)

一、常量池与串池

1、书写代码:

public class test1804 {
    public static void main(String[] args) {
        String string1="a";
        String string2="b";
        String string3="ab";
    }
}

2、查看字节码

 常量池中的信息,都会被加载到运行时常量池,这时a,b,ab都是常量池中的符号,还没有变为java字符串对象

执行到此处的时候变为字符串对象,被存放在串池中:

 先去串池(hashtable结构,不能扩容)中查找是否有a,如果已经有a就不会再去创建字符串对象,没有的话 #2会把符号a变为字符串对象a,b和ab一样

二、字符串变量的拼接

1、代码

public class test1804 {
    public static void main(String[] args) {
        String string1="a";
        String string2="b";
        String string3="ab";
        String string4=string1+string2;
    }
}

2、字节码

 3、拼接的过程

 实质上是执行的下面的一段代码:

new StringBuilder().append("a").append("b").toString();

4、拼接字符串的比较

(1)方式一:

public class test1804 {
    public static void main(String[] args) {
        String string1="a";
        String string2="b";
        String string3="ab";
        String string4=string1+string2;
        System.out.println(string3==string4);
    }
}
false

string3在串池中,string4在堆里面(https://www.cnblogs.com/zhai1997/p/12423092.html

方式二:

public class test1804 {
    public static void main(String[] args) {
        String string1="a";
        String string2="b";
        String string3="ab";
        String string4="a"+"b";
        System.out.println(string3==string4);
    }
}
true

字节码执行过程:

 直接找到拼接好的字符串“ab”,并且存入到地址为5的地方,实际上是javac在编译期的优化,结果已经在编译期确定为字符串“ab”,而上一个拼接是不能在编译期确定的

三、intern方法

1、JDK1.8

主动将串池中还没有的字符串放入串池,如果存在则返回这个字符串的引用,否则就将这个字符串添加到字符串池中,然会返回这个字符串的引用。

程序一:

public class test1804 {
    public static void main(String[] args) {
        String string=new String("a")+new String("b");
        String string1=string.intern();//将字符串对象尝试放入串池,有的话不会放入,没有的话则放入串池,会把池子的对象返回
        System.out.println(string=="ab");
        System.out.println(string1=="ab");
        System.out.println(string==string1);
    }
}
true
true
true

底层执行过程:

 实际上是用StringBulider创建两个字符串对象,然后进行字符串的拼接操作。拼接后的字符串在堆中(拼接的字符串ab在串池中是不存在的),因为已经将字符串放入到了串池中,因此返回true,又因为intern的返回值依旧是串池中的字符串ab,因此第二条输出语句依旧返回true。

public class test1804 {
    public static void main(String[] args) {
        String string=new String("a")+new String("b");
        System.out.println(string=="ab");
        String string1=string.intern();//将字符串对象尝试放入串池,有的话不会放入,没有的话则放入串池,会把池子的对象返回
        System.out.println(string=="ab");
        System.out.println(string1=="ab");
        System.out.println(string==string1);
    }
}
false
false
true
false

此程序在第一次的判断的时候,因为串池中没有“ab”字符串,要先创建一个字符串对象(在串池中,堆中没有),这就导致了将拼接后的字符串string放入到串池的时候失败了,也就是说string依旧在堆内存中。和下面的程序类似,都是在尝试将堆中的字符串对象放入到串池中的时候串池中已经有该字符串对象了

public class test1804 {
    public static void main(String[] args) {
        String str="ab";
        String string=new String("a")+new String("b");
        String string1=string.intern();//将字符串对象尝试放入串池,有的话不会放入,没有的话则放入串池,会把池子的对象返回
        System.out.println(string==str);
        System.out.println(string1==str);
    }
}
false
true

 2、JDK1.6

将一个字符串尝试放入到串池,如果有则不会放入,没有的话则会将此对象复制一份放入到串池并把串池中的对象返回

public class test1804 {
    public static void main(String[] args) {
        String string=new String("a")+new String("b");
        String string1=string.intern();
        String str="ab";
        System.out.println(string1==str);
        System.out.println(string==str);
    }
}
true
false

调用intern方法后返回的是串池中的字符串“ab”,而string是堆里面的字符串“ab”,而在JDK1.8的环境中两个都为true,因为在1.8的环境中,string被移动到了串池中,而1.6是拷贝

原文地址:https://www.cnblogs.com/zhai1997/p/12855383.html