目录
Java中为什么String是不可变性的。今天我们从多角度解析为什么Java把String做成不可变的。
常量池
- Java中我们创建String对象有两种基本方法。
String str1 = "zxhtom";
String str2 = new String("zxhtom");
- 上面两种方式我们创建了两个String变量 。 但是第一种通过双引号创建的zxhtom这个对象我们称之为常量 。 在JVM中是存储在一块叫【常量池】中的。而第二种str2是我们称之为普通变量。new一次就在JVM中开辟一块内存。
- 【常量池】的作用就是复用,当同样的内容再次被通过常量方式创建的时候Java会指向同一块地址。通过如下代码理解:
String z1 = "zxhtom";
String z2 = "zxhtom";
-
通过上图我们可以了解 z1 , z2两个变量其实引用的是同一内存地址 。 所以z1==z2 为true .
-
到这里引发出为什么String被设计为不可变 。 上列中z1 被修改成zxh . 如果String是可变得那么z2就会被莫名其妙修改成zxh .
便利
- 在Java中判断两个对象相等时通过地址判断。但是地址被抽象话为一段hash函数。在Java使用中hash是经常被使用的。将String设置为不可变性那么hash就可以一直使用下去。不需要重新计算体现了便捷性
安全
- 仍是上面的情况 , z2会被不知情的情况下被修改了。这在多线程中很常见。我们在使用的时候会被其他情况将数据更改。这样我们的数据将会失去了准确性。
引申问题
-
在上部中我们提到String的常量池。针对常量池引发思考 【String.intern()】
-
该方法的功能就是扩充【常量池】。z2.intern() 表示判断常量池中是否存在与该值相同的对象如果有则返回该对象的引用。 如果没有则将该值注册到内存中。注意这里并不是将z2对象注册过去。而是将z2的值注册进去。
String z1=new String("zxhtom");
String z2=z1.intern();
System.out.println( z1==z1.intern() );
System.out.println( z1.hashCode()+" "+z2.hashCode() );
System.out.println( z2==z1 );
System.out.println( z2==z1.intern() );
- 输出结构
false
-688175064 -688175064
false
true
- 分析一下输出结果不难发现,z1.intern()是常量池中没有zxhtom,会将zxhtom值创建到常量池中,z2就是引用常量池中的引用。这个时候z1==z2 为false说明注册到常量池中的并不是z1的地址,而是相当于z1的一个对象拷贝。
- string创建方式的确定简单归结:
- 通过双引号创建的 == 常量创建
- 通过常量拼接 == 常量创建
- 通过非常量与常量拼接 = 非常量创建
- 通过new 创建 == 非常量创建
String在Java中的【引用传递】
- 在Java中方法参数传递都是通过值传递的。但是为什么String给我们的感觉是引用传递的呢?
public static void main(String[] args) {
String x = new String("ab");
change(x);
System.out.println(x);
}
public static void change(String x) {
x = "cd";
}
- String不是基本对象所以String是引用传递。但是这里的引用传递知识传递String引用的地址 .当执行x=cd是原来ab的对象还在JVM中。外部x的引用地址没有变 。 变得知识change方法中x的指向。所以外部打印的还是ab