Class文件常量池
每个Class文件都会有一个单独的常量池,我们称为Class文件常量池,我们可以用javap
命令反汇编Class文件,可以查看java编译器为我们生成的字节码。
CLass文件常量池存下内容:
字面量:
- 文本字符串(代码中用双引号包裹的字符串部分的值)
- 被声明为finnal的常量
- 基本数据类型的值
- 其他
符号引用:
- 类符号引用:类的完全限定名
- 字段的名称和描述符
- 方法的名称和描述符
运行时常量池
Class文件中含有类的版本,字段,方法,接口等信息,当类被加载的时候这部分信息将会被存放到运行时常量池中,并把里面的符号地址转化为内存真实地址。运行时常量池就是将字节码文件中的信息放入方法区中,属于方法区的一部分。运行时常量池是类在加载阶段完后,将class常量池中的符号引用转存到运行时常量中;类在解析阶段后,会将符号引用转为直接引用,与字符串常量池中的引用保持一致。每个类都有一个运行时常量池,可以用来动态获取类信息。
String常量池
并且String常量池底层是用C++的HashMap实现的,key是字符串的字面量,value是字符串对象的引用(HOTSPOT)。
当String类直接赋值时,如果常量池内存在这个字符串,则s1直接指向常量池的地址,若没有,则先在常量池内创建这个字符串对象,s1直接指向常量池这个字符串的内存地址; 当String类使用new实例对象时,首先在堆里创建这个对象,若是常量池内没这个字符串,则也创建一个,然后堆里的对象的value指向常量池内的字符串。
public static void main(String[] args){
String s1="hello";
String s2="hello";
String s3=new String("world");
String s4=new String("world");
}
注:引用网上csdn博客图片
所以以下案例为false
String s1="a";
String s2=new String("a");
System.out.println(s1==s2);//false
以下看综合案例
String s1="a";
String s2=new String("b");
String s3="a"+"b";
String s4=s1+s2;
String s5="ab";
String s6=s4.intern();
System.out.println(s3==s4);//false
System.out.println(s3==s5);//true
System.out.println(s3==s6);//true
String x2=new String("c")+new String("d");
String x1="cd";
x2.intern();
System.out.println(x1==x2);//false
String s3=”a”+”b”:
编译器会自动优化,组合这两个字面量变成“ab”,之后这个字面量“str1str2”和在堆中创建的String对象的引用会被放到String常量池内。所以s3==s5为true。
String s4=s1+s2:
编译器内部对于String字符串变量拼接,会创建一个StringBuilder,对于每一个要拼接的内容,调用append进行添加,最后在使用toString()方法返回成字符串。StringBuilder的value是同对象一起存放在堆中,那么拼接后的字符ab字符串value也是在堆中,并没有被添加到常量池;s4是在堆中,而s3是在常量池中,所以s3==s4为false
String.intern()
jdk1.6 中,将这个字符串对象尝试放入串池。
• 如果字符串常量池中有,则并不会放入。返回已有的串池中的对象的地址。
• 如果没有,会把此对象复制一份,放入串池,并返回串池中的对象地址。
Jdk1.7起,将这个字符串对象尝试放入串池
• 如果字符串常量池中有,则并不会放入,返回已有的串池中的对象的地址。
• 如果没有,则会把对象的引用地址复制一份,放入串池,并返回串池中的引用地址。
s3==s6:
当s6=s4.intern()的时候常量池中有ab字符串,故不放入常量池,s4依旧是堆对象,而s6指向的是常量池中的对象,故为true。
注:
在JDK1.7以前,字符串常量池 和 运行时常量池都存放在方法区中,对方法区的实现成为永久代
在JDK1.7,字符串常量池 从方法区转移到了堆中,运行时 常量池还是在方法区中
在jdk1.8以后,取消了永久代,取而代之的是元空间。运行时常量池和静态常量池都存放在元空间中,而字符串常量池依然在堆空间中(只是和堆共享空间,但和堆互相隔离)
StringBuilder最后也用到了new String,但为什么没有入池 ?
通过new String(“ab”) 创建对象时,是通过双引号修饰的字面量来创建的,而 StringBuilder() 拼接的字符串,不会进入字符串常量池。可以理解为,在代码中写的字符串字面量才会进入字符串常量池
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/143050.html