这篇文章是 Java里面的String对象到底神奇在什么地方 的姊妹篇。
先来说说java对象不可变。
所谓的对象不可变,可以简单的理解为,对象一旦创建后,其状态不可修改。所谓不可修改就是对象里面所有的状态属性都不能修改。
举个例子:
public class Book { //书价 private final double price; //书名 private final String name; public Book(double price, String name) { this.price = price; this.name = name; } public double getPrice() { return price; } public String getName() { return name; } }
这个对象一旦被创建,就没有办法修改内部状态。因为没有对外提供任何方法修改属性值。而且 price
和 name
属性被声明为 final
在来看看String类是如何定义的。以下源码摘自JDK1.8
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the string */ private int hash; // Default to 0 /** use serialVersionUID from JDK 1.0.2 for interoperability */ private static final long serialVersionUID = -6849794470754667710L; /** * Class String is special cased within the Serialization Stream Protocol. * * A String instance is written into an ObjectOutputStream according to * <a href="{@docRoot}/../platform/serialization/spec/output.html"> * Object Serialization Specification, Section 6.2, "Stream Elements"</a> */ private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0]; /** * Initializes a newly created {@code String} object so that it represents * an empty character sequence. Note that use of this constructor is * unnecessary since Strings are immutable. */ public String() { this.value = "".value; } }
这段代码最关键的两个部分就是, String
类用 final
关键字进行了修饰,还有就是字符数组 value
用 final
修饰,并且访问权限是 private
的。如果不清楚Java访问权限的,可以看我这篇关于 Java访问权限
的介绍。
String
类用 final
关键字修饰,证明该类是不可继承的。
String
类是用字符数组表示字符串对象的。属性 value
被 final
修饰,证明 value
是不可变的。等等,这里有个小问题, value
是个字符数组,数组是引用类型, value
不可变只能说明字符数组的引用不可变,字符数组里面的内容是可变的。
举个例子 String str = “java”;
value
里面的值是这样的 [‘j’,’a’,’v’,’a’]
,我们虽然不能改变 value
到数组 ['j','a','v','a']
的引用,但是我们很容易改变数组内的值,比如 value[2]=’s’
;
那为什么我们还说 String
是不可变的呢。关键就在于 private
关键字。用 private
关键字修饰之后,在java的访问权限规则里面,除了在类内部可以修改 value
的值以外,没有任何地方可以修改它了。而我们的JDK工程师,又很小心的设计了该类,在 String
类内部也没有任何一处可以修改 value
的值,真是连自己人都不信任啊。所以 String
对象一旦被创建,就再也不能被修改了。
有人说貌似不是,比如你看下面的代码
String str = “java”; str = str.substring(1,2);
要想知道str有没有被改变我们可以看一眼substring的源码。
public String substring(int beginIndex, int endIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } if (endIndex > value.length) { throw new StringIndexOutOfBoundsException(endIndex); } int subLen = endIndex - beginIndex; if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } return ((beginIndex == 0) && (endIndex == value.length)) ? this : new String(value, beginIndex, subLen); }
关健的代码就是最后一行, new String(value, beginIndex, subLen)
看到了吗,不是在原来的字符上进行修改,而是创建了一个新的对象,然后把变量str指向新的对象。 原来的字符串就被回收了。
费了这么大力气,把 String
类搞成不可变的有什么好处吗?当然有,主要体现在两个方面。
安全:
性能: