今天来记录一下在Effective中看到的内容,大意是:写代码要健壮。
首先来看一下场景:
我们要写一个不可变类代码如下:
package com.youyou.learn.effective.test39;
import java.util.Date;
/**
* 用来保存一个时间段的不可变类
*/
public final class Period {
private final Date start;
private final Date end;
public Period(Date start, Date end) {
if (start.compareTo(end) > 0) {
throw new IllegalArgumentException("开始时间不能晚于结束时间");
}
this.start = start;
this.end = end;
}
public Date getStart() {
return start;
}
public Date getEnd() {
return end;
}
}
我们想要的是一个不可变的类,用来保存时间段,这个类的对象在实例化之后应该是不可变。
乍一看挺好,但是这代码禁不起琢磨啊!
当我们在执行下面代码的时候就会出现问题:
public static void main(String[] args) {
Date start = new Date();
Date end = new Date();
Period period = new Period(start, end);
end.setTime(System.currentTimeMillis());
}
在调用end.setTime(System.currentTimeMillis()); 时,我们发现不可变类的,成员常量也会跟着改变了。
为了保护Period实例的内部信息避免受到这种攻击,对于每个构造器的每个可变参数进行保护性拷贝是必要的。
我们可以进行如下修改:
public Period(Date start, Date end) {
if (start.compareTo(end) > 0) {
throw new IllegalArgumentException("开始时间不能晚于结束时间");
}
this.start = new Date(start.getTime());
this.end = new Date(end.getTime());
}
经过以上修改后,我们的代码就又健壮了一些,解决了上述的问题。
乍一看挺好,但是这代码禁不起琢磨啊!
当我们执行一下代码是还是会有问题:
public static void main(String[] args) {
Date start = new Date();
Date end = new Date();
Period period = new Period(start, end);
period.getEnd().setTime(System.currentTimeMillis());
}
为了防御第二种攻击,我们只需要改这两个访问的方法,使它返回可变内部域的保护性拷贝。
我们可以这样修改:
public Date getStart() {
return new Date(start.getTime());
}
public Date getEnd() {
return new Date(end.getTime());
}
这么一来,Period真正的是不可变了。这些域被真正的封装在对象的内部了。