java 字符串常量池

简单的问题

先看一个简单的经典问题

String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");
System.out.println(s1==s2);//true
System.out.println(s1==s3);//true

首先说明“==”比较的是引用地址而不是值,比较值用equals方法。
这个原因比较简单,就是“Hello”已经被保存在字符串变量池中了,所以s1,s2的引用都是字符串池中的,字符串池只保存唯一的值(HashMap保存),所以s1==s2。
而使用new时直接在堆中创建新的对象,并不会操作字符串池,所以s1!=s3.

进阶的题

先说明下
intern方法会先在字符串池中寻找值相等的字符串,如果找到了,就返回找到的值的引用,如果没有找到,就把当前的引用存入到字符串池中,并返回当前引用。

String s1 = new StringBuilder().append("ja").append("va1").toString();
System.out.println(s1.intern() == s1);

String s2 = new StringBuilder().append("ja").append("va").toString();
System.out.println(s2.intern() == s2);

String s3 = new StringBuilder().append("java1").toString();
System.out.println(s3.intern() == s3);

这里如果使用jdk6,结果是 false,false,false
如果使用的是jdk7或以上版本,结果是 true,false,false

这就很奇怪了
为什么jdk6和jdk7不同?
为什么s1和s2的区别只是s1的后面多了个1,结果就不同了?
为什么s3直接append“java1”和s1的分开append结果也不同呢?

回答这些问题,首先要明白几个点

1. 字符串是什么时候进入到字符串池的?

  • 类加载的时候
    类加载的时候,所有的字面量(被括号包括的字符串,如“ja”,“va1”等),会被保存到字符串变量池中

不过需要注意的时,类加载加入到字符串池时并不会马上创建字符串对象,而是只有一个空引用,直到首次使用时,才会创建实际的字符串对象。也就是懒加载。

  • 使用intern()方法
    当调用intern()方法时,会先在字符串池中找是否存在值相等的字符串,如果不存在,则会把当前的字符串的引用保存到字符串池中,并返回该引用。

2.jdk7对字符串池做了什么改变,字符串池保存的是引用还是对象?

jdk7时字符串池被从方法区移动到了堆里面,在jdk6或之前的版本,字符串池是放在方法区里的,所以存入字符串池的时候,会把字符串的实际对象和引用都存放到字符串池里。而到了jdk7,由于移动到了堆里面,则分成两种情况

  • 如果是通过在类加载时加入的字符串,没有发生变化,都是在字符串池中存放字符串对象和引用。
  • 如果是通过intern方法加入到字符串,那么只会存放引用到字符串池中。

所以有的人会问,字符串池保存的到底是引用还是对象,实际上是都保存,但是jdk7及之后的版本使用intern方法保存的字符串,只会保存其引用。

解题

对以上两点的了解,就可以解释上面的三道题了

第一题

String s1 = new StringBuilder().append("ja").append("va1").toString();
System.out.println(s1.intern() == s1);

这里的s1生成的"java1"是运行时生成的,并不是字面量,所以在调用intern方法之前,在字符串池里是没有相应的值的。所以调用s1.intern()时,会往字符串池添加数据

  • 在jdk6会生成一个新的对象和引用保存到字符串变量中,所以s1.intern()返回的是指向新字符串对象的新的引用,于是s1.intern() != s1.
  • 在jdk7,并不会生成新的对象,而是直接把s1的引用保存到字符串池中,所以s1.intern() == s1;

第二题

String s2 = new StringBuilder().append("ja").append("va").toString();
System.out.println(s2.intern() == s2);

这道题比较坑,咋一看好像跟第一题一样,关键是“java”这个词实际上早已经被使用过了。看下图,可以看到在java的版本类里,已经使用了“java”这个字符串,所以字符串池中保存的是虚拟机启动时创建的引用,并不是s2创建的字符串对象的引用。
在这里插入图片描述

第三题

String s3 = new StringBuilder().append("java1").toString();
System.out.println(s3.intern() == s3);

这道题貌似跟第一题也很像,区别在于这里的“java1”是一个字面量,而第一题中的“java1”是运行时生成的。而字面量会在类加载的时候就被存到字符串变量池中了,因此,s3.intern()返回的是类加载的时候存入的字符串对象引用而不是s3创建的对象的引用。所以s3.intern() != s3

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值