Java String为什么是不可变的
一: 什么是不可变
如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的。
二: String内部构成
/** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the string */ private int hash; // Default to 0
实质上,String的底层是char[]数组,其存储关系为:字符串对象的引用 --> 字符串对象 --> char数组对象
调用图来自:https://blog.csdn.net/zhangjg_blog/article/details/18319521
三: String方法返回大部分是新的字符串,而不是改变了字符串的内容,从黄色部分可以看出。
public String replace(char oldChar, char newChar) { if (oldChar != newChar) { int len = value.length; int i = -1; char[] val = value; /* avoid getfield opcode */ while (++i < len) { if (val[i] == oldChar) { break; } } if (i < len) { char buf[] = new char[len]; for (int j = 0; j < i; j++) { buf[j] = val[j]; } while (i < len) { char c = val[i]; buf[i] = (c == oldChar) ? newChar : c; i++; } return new String(buf, true); } } return this; }
四: String为什么要设计成不变
1-为了安全,也是最重要的原因
举个HashSet的应用:
Set<String> sets = Sets.newHashSet(); String s1 = "aaa"; sets.add(s1); sets.add("bbb"); String s2 = s1; s2 = "bbb"; sets.add(s2);
很简单sets集合的包含两个元素,“aaa” 和 “bbb”,自然size==2;
想一下,如果String是不可变的,元素就变成了“bbb” 和“bbb”,size==2;这样的结果显然是不正确的。
当然高并发也是有问题的。
2- 节约内存空间
都知道String是存储到常量池中的,每个String对象都是不可变的,这样就不存在修改(写)对象的情况,也就不存在高并发读取有误的安全问题。每个String都可以同时被多个线程调用。自然就节省了内存空间;
试想,如果String是可变的,为了保证安全性,会创建很多相同内容的对象,也就造成了不必要的浪费;
3-减少重复性计算
仍然拿HashSet集合举例,其HashSet的key是存储的hashcode值。String对象不变,其hashcode值就不会改变,也就不用每次调用hashSet进行重新计算。
五: String真的是不可变的吗
String对象内容实质是char[]数组,char[]为引用变量,而不是真正的对象。即使被final修饰,也只能说明不能改变其引用。然而实际上char[]还是可以改变的。
private static final char[] chars = {'a','a','a','a','a','a'}; public static void main(String[] args){ chars[0] = 'b'; System.out.println(chars); }
输出结果:baaaaa
大家可能还要说,有private修饰呀。
这个可以用反射进行修改;所以说String不是实质上的不可变;
参考文献:https://www.zhihu.com/question/20618891