前言
为什么在java中String是不可变的?
为什么String要是不可变的?意义何在?
问题一:为什么在java中String是不可变的?
初学者会遇到一些疑问,
“都说String不可变,可是我明明可以改一个String啊”
对,就像下面一样
public class test {
public static void main(String[] args) {
String oldStr = "阳光大男孩!!!";
System.out.println(oldStr);
oldStr = oldStr.substring(3);
System.out.println(oldStr);
}
}
输出结果是
阳光大男孩!!!
男孩!!!
你看,这不是能变吗?博主辣鸡!
这其实是对于String不可变性理解的一个误区
这段代码其实是这样一个过程
- 新申请一个字符串,内容为“阳光大男孩!!!”
- 输出字符串的值
- 对字符串执行截取操作,新建了一个字符串对象!!!,再让oldStr指向了这个新建的对象。
- 打印oldStr,这时候oldStr指向的是新对象,所以值是“男孩!!!”
佐证一下
我们打开substring方法的源码看看
public String substring(int beginIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = value.length - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
最后一行进行了一个三则表达式,如果beginIndex是0的话,说明根本不需要截取,所以直接返回原来的对象,否则,新建一个对象。
所以,你表面上改变了字符串的值,其实没有,原来的字符串并没有改变,而是新new了一个对象。
问题二:为什么String要是不可变的?意义何在?
- 字符串的实现是数组,数组是顺序存储的,所以在创建时就开辟了一个连续地址
- 多线程有共享变量的需要,String的不可变性,让程序变的安全,因为不可变意味着不可写只能读,所以是安全的。
- 字符串常量池的设计可以提高代码效率,节省空间,而String的不可变性正是一个前提条件。
关于字符串常量池浅析
public class test {
public static void main(String[] args) {
String oldStr1 = "阳光大男孩!!!";
String oldStr2 = "阳光大男孩!!!";
System.out.println(oldStr1.hashCode());
System.out.println(oldStr2.hashCode());
}
}
输出一下
1402535102
1402535102
可以看得出来这两个对象引用的都是同一个地址,
- 在创建第一个对象的时候,创建完成后,同时会把它放到字符串常量池中。
- new 第二个对象的时候,会去字符串常量池中比较,如果有,则让实例直接指向常量池中的地址,否则new一个新对象。