谈谈Java的字符串及其常见问题

​ 一直对Java的字符串概念很模糊,记得很早之前就看到一道经典面试题:“String s = new String(“a”)”产生了几个对象“,看了许多文章都不太明白,并且,由此产生的问题又有”String a = b + c空间上怎么分配“,”String c = “abc”产生了几个对象“等等。这里还是写篇博文将Java字符串有关的知识总结下把🤣

0 Java的字符串类

​ Java中有三个可以用于表示字符串的类:String StringBuilder StringBuffer

String:字符串常量,底层用了字符数组+final关键字构成,由于每次改变时,都会生成一个新的String对象,所以是线程安全的。

StringBuilder:继承了AbstractStringBuilder,可变类,每次修改不会产生新对象,但线程不安全

StringBuffer:同StringBuilder,但线程安全,支持并发操作

1 String s = new String(“a”)产生了几个对象

​ 这个问题,我更偏向于网上一种答案:产生了一个或两个对象。因为new会在堆中创建一个对象,如果常量池之前没有,则会再创建一个。

2 String::intern

String::intern是一个本地方法,作用是

  • 如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象的引用
  • 否则,会将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用

​ 由此产生的一段代码

1
2
3
4
String str2 = new String("hello ") + new String("world"); // 1
str2.intern(); // 2
String str1 = "hello world"; // 3
System.out.println(str2 == str1);

​ 这段代码在JDK6中输出为false,在jdk7输出为true

​ 这是因为在JDK6时,常量池是放在方法区永久代中的,而new生成的对象引用在堆中,返回的地址当然不一样

​ 而在JDK7时,将常量池移到了堆中,使用intern方法会先查询常量池中是否存在,如果存在,则放会常量池引用,但不存在,不会将字符串拷贝到常量池,而是在常量池中生成一个对字符串的引用。

​ 所以在示例中,执行第1行时常量池是没有hello world的,但执行了intern之后就会将其引用放到常量池中,然后执行第3行返回的就是该引用。结果就是true了。但如果将23对换位置就不一样了。

3 拼接的String是否相等

​ 考虑以下代码

1
2
3
4
String st1 = "a" + "b" + "c";
String st2 = "abc";
System.out.println(st1 == st2);
System.out.println(st1.equals(st2));

​ 答案是两个都是true“a””b””c”三个本来就是字符串常量,进行+符号拼接之后变成了“abc”“abc”本身就是字符串常量(Java中有常量优化机制),所以常量池立马会创建一个“abc”的字符串常量对象,在进行st2=”abc”,这个时候,常量池存在“abc”,所以不再创建。所以,不管比较内存地址还是比较字符串序列,都相等。

4 String类拼接数据是否相等

​ 考虑以下代码

1
2
3
4
5
String st1 = "ab";
String st2 = "abc";
String st3 = st1 + "c";
System.out.println(st2 == st3);
System.out.println(st2.equals(st3));

​ 答案是falsetrue。第二个为true很好理解,但第一个为什么为false呢?

​ 因为在Java任何数据和字符串进行(+)运算的原理是StringBuilder或者StringBuffer类和里面的append方法实现拼接,然后调用toString()把拼接的对象转换成字符串对象,最后把得到字符串对象的地址赋值给变量。

​ 大致流程为:

  1. 常量池创建“ab”对象,并赋值给st1,所以st1指向了“ab”
  2. 常量池创建“abc”对象,并赋值给st2,所以st2指向了“abc”
  3. 由于这里走的(+)的拼接方法,所以第三步是使用StringBuffer类的append方法,得到了“abc”,这个时候内存0x0011表示的是一个StringBuffer对象,注意不是String对象。
  4. 调用了ObjecttoString方法把StringBuffer对象装换成了String对象。
  5. String对象(0x0022)赋值给st3

总结

​ 不得不说,Java的八股知识点是真的多呀,一个字符串这么多规矩,不过解决了这些问题感觉也对Java的字符串认识更加深刻了。😁

参考

(125条消息) Java字符串篇-3-String类5个常见面试题的解答过程和原理


谈谈Java的字符串及其常见问题
https://2w1nd.github.io/2022/04/17/Java/String到底产生了几个对象/
作者
w1nd
发布于
2022年4月17日
许可协议