1. String 不可变
-
不可变对象
对象在创建完成之后,其状态不能再被改变,则该对象即为不可变对象
-
对象不可变具体内容
不能改变对象内的成员变量
基本数据类型的值不能改变
引用类型的变量不能指向其他对象
引用类型所指向的对象状态也不能改变
-
例如
String s = "ABC"; System.out.print(s); // ABC s = "123"; System.out.print(s); // 123
-
执行结果
"ABC" "123"
释义
对象:在内存中是一块内存区,成员变量越多,这块内存区占的空间越大。
引用:只是一个4字节的数据,里面存放了它所指向的对象的地址,通过这个地址可以访问对象。
- s只是一个引用,它指向了一个具体的对象,当s=“123”; 这句代码执行过之后,又创建了一个新的对象“123”, 而引用s重新指向了这个新的对象,原来的对象“ABC”还在内存中存在,并没有改变。
- 不能直接操作对象本身,所有的对象都由一个引用指向,必须通过这个引用才能访问对象本身,包括获取成员变量的值,改变对象的成员变量,调用对象的方法等
- 效率和安全
Java 将 String 设成不可变最大的原因是效率和安全
- 提高字符串常量池的效率和安全性
- 如果一个对象是不可变的 ,需要拷贝的对象的内容时就不用复制它本身,而只是复制它的地址,复制地址(通常一个指针的大小)需要很小的内存,效率也很好
- 对于引用同一个对象的其他变量也不会造成影响
- 对于多线程是安全的。多线程同时进行时,一个可变对象的值很可能被其他线程改变
2. String 源码
-
JDK1.7以后,String 类的成员变量只剩下两个:
- 释义
- String 类实际上是对字符数组的封装。
- 成员变量 hash,是该 String 对象的哈希值的缓存。
-
在 Java 中,数组也是对象,所以 value 也只是一个引用,它指向一个真正的数组对象。在执行第一句代码时,真正的内存排布如下图:
- String 对象不可变
- String 内部成员变量 value 和 hash 并未对外提供 setter 方法,且变量 value 是 final 的。这些都说明一旦 String 内部的 value 被初始化后,便不会再改变,故而 String 对象不会改变。
-
至于 String 内部其他方法:如 replace() 在执行后,结果发生的变化,也是同理。即:这些方法执行过程中,会生成新的 String 对象,该对象将会被赋值给引用,而原来的对象并未改变。如:
-
值得注意的是,通过反射可以修改对象的值。如:
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { String s = "Hello World"; System.out.println("s: " + s); // 获取String类中的value字段 Field valueFieldOfString= String.class.getDeclaredField("value"); // 设置访问权限 valueFieldOfString.setAccessible(true); // 获取 s 对象上的value属性的值 char[] value = (char[]) valueFieldOfString.get(s); // 改变value所引用的数组中的第 5 个字符 value[5] = '_'; System.out.println("s: " + s); // Hello_World }