大家好,欢迎来到程序视点
!我是小二哥。今天我们来来聊聊String类型对象不可变的问题。
前言
String是Java中一个不可变的类,所以String对象一旦被实例化就无法被修改。我们知道Java中就是这样定义的。但是,为什么要这样设计呢?
String 类设计成不可变的原因及好处?
什么是不可变对象
从字面意思也能够理解,也就是我们的创建的对象不可改变。即,不可变类的实例一旦创建,其成员变量的值就不能被修改。为了实现创建的对象不可变,java语言要求我们需要遵守以下5条规则:
(1)类内部所有的字段都是final修饰的。 (2)类内部所有的字段都是私有的,也就是被private修饰。 (3)类不能够被集成和拓展。 (4)类不能够对外提供哪些能够修改内部状态的方法,setter方法也不行。 (5)类内部的字段如果是引用,也就是说可以指向可变对象,那我们程序员不能获取这个引用。
如果我们查看 String 类的源码,我们会发现它全部满足上面这5个规则。
代码语言:javascript复制/**(3)*/
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** (1)(2)*/
private final char value[];
/**(1)(2)*/
private int hash; // Default to 0
......
/**(4)(5) 没有setter和getter*/
}
原因及好处
String 设计成不可变,主要是从性能和安全两方面考虑。
性能优化,主要体现在字符串常量池的设计和使用上。
字符串常量池(String pool)是 Java 堆内存中一个特殊的存储区域,当创建一个 String 对象时,假如此字符串已经存在于常量池中,则不会创建新的对象,而是直接引用已经存在的对象
。这样做能够减少 JVM 的内存开销,提高效率。
String s1 = "abc";
String s2 = "abc";
比如引用 s1和 s2 都是指向常量池的同一个对象 "abc",如果 String 是可变类,引用 s1 对 String 对象的修改,会直接导致引用 s2 获取错误的值。
其次是允许String对象缓存HashCode
String字符串不可变,所以在它创建的时候 hashcode就固定了,且被缓存了起来,之后不需要重新计算。这就使得字符串很适合作为 HashMap 中的 key,效率大大提高。
代码语言:javascript复制private int hash;//this is used to cache hash code.
大家在 String 类的源码中能看到这个成员变量。把String实例设计为不可变的,那么该实例的成员变量hash也是不会变的。
再者就是安全性上的考虑我们常用 String 字符串在其他Java类中充当参数,比如网络连接地址URL
,文件路径path
等。假若String不是固定不变的,将会引起各种安全隐患,例如,你本想访问百度,结果输入“www.baidu.com”后变成了你银行卡的转账地址;就问你心里慌不慌