Java String类型的特性 " 复用性 ", “ 值不可变性 "
Java String类型的特性
Java 中的 String 类型不同与其他引用类型,一般的引用类型的特性在 String 类型上你会发现并不适用。其实是 Java 对 String 类进行了包装,所以你会感觉他与其他引用类型有所不一样。这其中就要说到他的两个特性:复用性和值不可变性。
复用性
直接赋值
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);
上面程序运行的结果:true
这是一个令人疑惑的问题,为什么会是 true 呢?引用对象 ” == “ 比较的不是地址吗?难道它们的地址是一样的?
它们的地址是一样的,这个就是 String 的复用性。"abc"是存放在常量池中的,并且 s1 和 s2 都指向同一个地方。
当把字符串 “abc” 赋值给 s1 时,Java虚拟机会先查看常量池中是否有 “abc” 这个值,如果没有就开辟一个空间把 “abc” 存放进去然后把地址赋值给 s ,如果有就直接把 “abc” 对应的地址赋值给 s1。同理当把字符串 “abc” 赋值给 s2 时会进行相同的操作,所以 s1 与 s2 指向的地址会是同一个。这个与Java对String的封装有关,为了减少内存的浪费,赋予了 String 复用性。
直接赋值与new关键创建对象区别
在 Java 中对 String 类型直接赋值和用 new 关键字创建对象赋值是不一样的。
String s = "abc";
String s1 = new String ("abc");
从 Java 虚拟机出发解释上面两行代码:
- 第一行代码:String s = “abc”;
把字符串 “abc” 赋值给 s 时,Java虚拟机会先查看常量池中是否有 “abc” 这个值,如果没有就开辟一个空间把“abc”存放进去然后把地址赋值给 s ,如果有就直接把 “abc” 对应的地址赋值给 s。 - 第二行代码:String s1 = new String (“abc”);
new 一个对象时,Java 虚拟机会在堆中开辟一个空间,把 “abc” 存放在堆里面,再把对应的地址赋值给 s。
所以本质上这两行代码存放的空间并不一样,一个是存放在常量池中,一个是存放在堆里
值不可变性
字符串可以用两种方式赋值有一个非常重要的特征,即不可变性(immutable)。
不可变的意思是:一旦一个字符串被创建后,它的值就不能被修改。若想改变它的值,Java虚拟机会重新开辟一个空间来存放新的值,然后指向这个地址。
为了能够重用这些不变的字符串,Java使用了字符串常量池;
凡是用=直接赋值的方式得到的字符串,都存储在常量池中;
相同字符串内容,共用池里的一个具体字符串对象;
使用new创建的字符串不适用常量池,每次都会在堆分配新内存空间
值不可变性避免了当多个对象指向同一个地址时,会出现当一个变量改变了值所有的对象都一起改变这个问题。但是它同时带来了一个问题,就是当某个对象的值要经常发生改变时,会不断的在内存分配空间造成内存的浪费。为了解决这个问题又出现了StringBuilder和StringBuffer这两个类,来解决这个问题。