1. final只是保证被修饰的变量的值不可变!
1) 注意!保证的是值不可变,什么是值?对于一个引用来说,就是指针咯,即指针的值不变,即其指向不变,但是我还是可以通过这个指针来调用可以改变该对象成员的方法啊!!
2) 即final如果修饰引用变量,只能保证该引用的指向不变,但不能保证该引用指向的对象不变,其指向的对象还是可以改变的!!
3) 例如:final Person p = new Person("Tom"); p.setName("Peter");是允许的!!
2. 不可变类:
1) 不可变类是指创建该类的对象后,这些对象的数据成员都不能改变;
2) 这种需求是普遍的,例如Java的8个包装器类型以及String类都是不可变类;
3) 那该如何设计自己的不可变类呢?三原则:
i. 数据成员都是private final的!这是显然的;
ii. 只提供getter,不能提供setter!这也是显然的;
iii. hashCode和equals应该根据数据成员计算得来,并且hashCode相等的情况下也要保证equals相等;
4) 有了以上三原则就基本能保证设计的类是不可变的了;
!!什么叫做基本?难道这样还不能保证是不可变类吗?
!!诚然,如果数据成员都是Integer、String这些本身就是不可变类,那当然没有问题,但往往业务上你想包含的数据成员都是其它公司开发的类库中的类,而很多类本身设计时都是可变的,但你往往有得不到这些类库的源码,因此你无法通过改动源码的方式把这些类改成不可变类;
5) 因此现在的问题是,如果你想把一个包含可变类对象的类设计成不可变类应该如何做呢?
i. 其实思路很简单!无非就是从两个方面出发:
a. 所有在类内部对该可变成员的访问一定要保证不能对其进行修改。这是显而易见的!根本不用说都应该能想到;
b. 另外可能发生修改隐患的地方就是返回这个可变成员的地方了:如果一个方法返回的刚好就是这个可变成员,那么返回后在类的外部就完全有可能对该成员进行修改了(即返回数据成员也是一种暴露类的内部情况的一种情形);
ii. 因此需要做文章的地方就在于b.了;
iii. 其实应对策略也很简单,你只要创建一个该成员的副本并返回就行了,这样的话外界拿到的仅仅是该成员的副本,对副本修改不会影响到原本;
6) 示例:
class Test {
private final StringBuilder name; // 故意用StringBuilder,因为它是一个可变类
public Test(StringBuilder name) {
this.name = name;
}
public StringBuilder getName() {
return new StringBuilder(name); // 返回一个临时创建的副本,外界对副本的改变不会影响原本
}
}