Java程序员面试笔试宝典(第2版)
上QQ阅读APP看书,第一时间看更新

1.13 不可变类

不可变类(Immutable Class)是指当创建了这个类的实例后,就不允许修改它的值,也就是说一个对象一旦被创建出来,在其整个生命周期中,它的成员变量就不能被修改。它有点类似于常量,只允许别的程序读,不允许别的程序进行修改。

在Java类库中,所有基本类型的包装类都是不可变类,例如Integer、Float等。此外,String也是不可变类。可能有人会有疑问,既然String是不可变类,为什么还可以写出如下代码来修改String类型的值呢?

程序运行结果为:

表面上看,好像是修改String类型对象s的值。其实不是,String s="Hello"语句声明了一个可以指向String类型对象的引用,这个引用的名字为s,它指向了一个字符串常量"Hello"。

s+="world"并没有改变s所指向的对象(由于"Hello"是String类型的对象,而String又是不可变量),这句代码运行后,s指向了另外一个String类型的对象,该对象的内容为"Hello world"。原来的那个字符串常量"Hello"还存在于内存中,并没有被改变。

在介绍完不可变类的基本概念后,下面主要介绍如何创建一个不可变类。通常来讲,要创建一个不可变类需要遵循下面四条基本原则:

1)类中所有的成员变量被private所修饰。

2)类中没有写或者修改成员变量的方法,例如setxxx。只提供构造方法,一次生成,永不改变。

3)确保类中所有的方法不会被子类覆盖,可以通过把类定义为final或者把类中的方法定义为final来达到这个目的。

4)如果一个类成员不是不可变量,那么在成员初始化或者使用get方法获取该成员变量时,需要通过clone方法来确保类的不可变性。

5)如果有必要,覆盖Object类的equals()方法和hashCode()方法。在equals()方法中,根据对象的属性值来比较两个对象是否相等,并且保证用equals()方法判断为相等的两个对象的hashCode()方法的返回值也相等,这可以保证这些对象能正确地放到HashMap或HashSet集合中。

除此之外,还有一些小的注意事项:由于类的不可变性,在创建对象的时候就需要初始化所有的成员变量,因此最好提供一个带参数的构造方法来初始化这些成员变量。

下面通过给出一个错误的实现方法与正确的实现方法来对比说明在实现这种类的时候需要特别注意的问题。首先给出一个错误的实现方法如下:

程序的输出结果为:

需要说明的是,由于Date的对象的状态是可以被改变的,而ImmutableClass保存了Date类型对象的引用,当被引用对象状态改变的时候会导致ImmutableClass对象状态的改变。

其实,正确的实现方法应该如下所示:

程序的输出结果为:

Java语言里面之所以设计有很多不可变类,主要是因为不可变类具有使用简单、线程安全、节省内存等优点,但凡事有利就有弊,不可变类自然也不例外,例如,不可变的对象会因为值的不同而产生新的对象,从而导致出现无法预料的问题,所以,切不可滥用这种模式。