你真的了解String吗?一道面试题引发的思考 前言 不可变

这是我参与新手入门的第3篇文章

前言

首先,先来和大家看一道熟悉的面试题,判断以下输出的结果分别是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
java复制代码public class Test {
public static void main(String[] args) {
String str1 = "hello";
String str2 = "hello";
String str3 = new String("hello");
String str4 = new String("hello");

System.out.println(str1==str2);
System.out.println(str1==str3);
System.out.println(str3==str4);

System.out.println(str1.equals(str2));
System.out.println(str1.equals(str3));
System.out.println(str3.equals(str4));
}
}

相信聪明的小伙伴们一定都知道正确输出是什么,没错,结果就是你们想的那样

1
2
3
4
5
6
java复制代码true
false
false
true
true
true

至于原因,因为equals比较的是两个对象的内容是否相等,显而易见,上面4个的内容都是hello,所以结果都是true
而==比较的则是对象的地址是否一致,我们知道,第一种方式String str1 = "hello",此种创建方式下,编译器首先检查常量池中是否含有此字符串常量,没有则创建,然后JVM会开辟一块堆内存存放一个隐式对象指向该字符串常量,接着在栈空间里创建str1变量,str1变量指向该块堆内存首地址。
第二种方式String str3 = new String("hello"),此种创建方式下,直接在堆内存new一个对象,并在常量池中创建“hello”字符串,该对象指向常量池中的“hello”字符串,然后创建str3变量,并将str3变量指向该块堆内存首地址。所以其实它们在内存中的分布是这样的
image.png
相信大家看到这里对上面的面试题应该就能理解了,接下来我们再聊聊String这个类的一些特性。

不可变?

打开String类的源码,可以看到这样一句话
image.png
这里已经明确说明,String类一旦创建就不会改变,原因也很简单,因为类被final修饰了
image.png
再来看下面一段代码

1
2
3
4
5
6
7
8
java复制代码public class Test {
public static void main(String[] args) {
String a1 = new String("Hello");
String a2 = new String("World");
a1 += a2;
System.out.println(a1);
}
}

此时输出的结果是HelloWorld,等等,不是刚说了String类是final修饰的,是不可变的么?怎么刚说完就又能变了呢?其实此段代码执行时,首先在内存中分配一块空间,它的大小为a1和a2空间大小之和,然后将a1和a2的内容复制到该内存空间相应位置,最后将变量a1指向该内存空间,此时的a1其实已经是一个新的String对象了。我们在阅读String中的方法是可以看出,所有的改变字符串的方法其实都是返回了一个新的对象return new String

原因

那么问题来了,为什么String会被设计成不可变的呢?主要原因有如下几点:

  1. 在Java程序中String类型是使用最多的,这就牵扯到大量的增删改查,每次增删改差之前其实jvm需要检查一下这个String对象的安全性,就是通过hashcode,当设计成不可变对象时候,就保证了每次增删改查的hashcode的唯一性,也就可以放心的操作。
  2. 网络连接地址URL,文件路径path通常情况下都是以String类型保存, 假若String不是固定不变的,将会引起各种安全隐患。就好比我们的密码不能以String的类型保存,,如果你将密码以明文的形式保存成字符串,那么它将一直留在内存中,直到垃圾收集器把它清除。而由于字符串被放在字符串缓冲池中以方便重复使用,所以它就可能在内存中被保留很长时间,而这将导致安全隐患
  3. 字符串值是被保留在常量池中的,也就是说假若字符串对象允许改变,那么将会导致各种逻辑错误。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%