Effective Java 阅读 (1-3)
真正开始阅读这本书之后,我才发现我对这门语言的认识是多么肤浅。
这是这个系列的第一篇博客,写于2016年8月28日21:31:25,今天我从快递小哥手上接过这本书,突然就有一种神秘的感觉,就好像一只迷途的羔羊找到了新的道路。
哈哈扯远了,希望能够用这一系列的博客记录一下我阅读EJ的心得和体会吧。
更新频率:1-3次/周(1-3周/次hhhh)
第一条:考虑使用静态工厂方法
你还在使用共有构造器么?
什么是静态工厂方法?EJ上给出了一个简单的示例:
public static Boolean valueOf(boolean b){
return b ? Boolean.TRUE : Boolean.FALSE ;
}
通过调用Boolean类的这个静态的方法,我们就可以根据输入值去新建一个Boolean对象,这比之我们平常的方法:
Boolean b = new Boolean(true);
有什么优越的地方呢?
这个方法是有名称的!
有名称为什么就很有意义呢?
在EJ上举了这样一个例子:
比如BigInteger类的初始化时,我们会调用BigInteger(int, int,random)
这个方法,但是有的时候这个构造方法会返回一个素数(你可以看做是返回了一个炸弹),可是我们怎么提醒调用者这个构造方法可能会有恙呢?如果通过调换参数位置去新建一个构造方法的话只会让类的构造方法更加混乱。
这个时候,我们提供这样一个静态方法BigInteger.MaybeBoom(int , int , random)
这样既能提醒使用者可能构造的对象的情况,又能区分不同参数,两全其美。
当然,你应该慎重地为静态工厂方法取名你不在需要每次调用这个方法时都去新建一个对象啦!
这种做法对于一个要频繁创建相同对象的程序来说,可以极大的提高性能。它使得一个类可以保证是一个singleton;他使非可变类可以保证“不会有两个相等的实例存在”。他们可以返回你想要的任何子类型对象给你!
使用静态工厂方法,可以通过调用方法时使用不同的参数创建不同类的实例,还可以创建非公有类的对象,这就封装了类的实现细节。
也就是API可以返回对象,同时又不会令对象的类变成共有的,这样隐藏的实现类会使得API变得非常简洁。遇到多个构造器参数时要考虑使用构建器
比如说:你有一个类Point
public class Point{
private double [] coor ;//必须的
private String poiid ; //有的点可没有这个信息
private String status ;//有的点可没有这个信息
private String checkinNum ;//有的点可没有这个信息
...
}
当你在写这个Point的构造函数的时候,若这个Point只有coor[]这个参数是必须的,那么你就可以用以下这几种解决方法。
-
- 重叠构造器
...
public Point(double coor[]){
Point(double[] coor,"no poiid");
}
public Point(double coor[] , String poiid){
Point(double[] coor , String poiid ,"no status info")
}
public Point(double coor[] , String poiid , String status){
Point(double[] coor , String poiid ,status , 0);
}
public Point(double coor[] , String poiid , String status , int checkinnum){
this.coor = coor ;
this.poiid = poiid ;
this.status = status ;
this.checkin = checkinnum ;
}
...
当你对于一个点只知道其某个必要讯息,但是你又需要去实例化这个点,就利用参数列表中相对应的构造器,但是构造器的调用过程中会包含所有的参数
但是如果仅仅是这几个参数,感觉程序并不臃肿,但是随着参数的增加,这种构造器的实现方法就会越来越复杂。
-
- JavaBean
public Point(){
public void setCoor(double[] val){ coor = val ;}
public void setPoiid(String val){ poiid = val ;}
public void setStatus(String val){ status = val ;}
public void setCheckin(int val){ checkin = val ;}
}
使用JavaBean能够弥补使用重叠构造器的不足,他可以很容易的创建一个对象。
但是问题也来了,由于JavaBean模式在使用中构造过程被分配到好几个不同的调用中,所以在调用过程中可能会处在一个不一致的情况下!类无法通过检验构造器的参数的有效性来保证一致性。
同时JavaBean阻止了把类做成不可变的可能,所以线程安全变成了很大的问题!
-
- 使用Builder模式
什么是Builder模式?就是把JavaBean再次深化,我们先去创建一个Builder,这个Builder中包含了所有我们想要对我们将要进行定义的类的值的定义。然后我们使用这个Builder去定义我们将要定义的类。这样就保证了我们想要定义的类的一致性。
public class Point{
private double [] coor ;//必须的
private String poiid ; //有的点可没有这个信息
private String status ;//有的点可没有这个信息
private String checkinNum ;//有的点可没有这个信息
public Point(){
coor = builder.coor ;
poiid = builder.poiid ;
status = builder.status ;
checkinnum = builder.checkinnum ;
}
public static class Builder{
private double [] coor ;//必须的就不初始化
private String poiid = "no poiid"; //有的点可没有这个信息
private String status = "no status";//有的点可没有这个信息
private String checkinNum = 0;//有的点可没有这个信息
public builder(double[] coor){//初始化必要的变量
this.coor = coor;
}
public Builder poiid(String val){
poiid = val ;
return this ;
}
public Builder status(String val){
status = val ;
return this ;
}
public Builder checkin(int val){
checkin = val ;
return this ;
}
}
}
比如新建一个Point类方法
Point p = new Point.Builder(coor).poiid(poiid).
status(status).checkin(check);
附上一个Builder的泛型
public interface Builder<T>{
public T build();
}
builder的缺陷:
* 为了创建对象,还需要创建其构建器。虽然说创建构建器的开销在实践中可能不明显,但是为了性能,还真是个问题~
* Builder模式比较冗长,故最好只在参数很多的时候使用
使用私有的构造器或者枚举类强化Singleton属性
Singleton就是只被实例化一次的类,也就是单例模式,比如说唯一的系统组件。
在Java 1.5之前的两种构造Singleton的方法
A:
public class single{
public static final single INSTANCE = new single();
private single(){...}
public void leaveTheBuilding(){...}
}
即私有的构造器,私有的构造器只能在类内部被调用一次,用来创建final域的single.INSTANCE,由于缺少public或者protected的构造器,所以能够保证single的全局唯一性,一旦single被实例化,只会产生一个single实例,任何行为都不会改变这一点。
不过享有特权的客户端可以借助AccessibleObject.setAccessible方法,通过反射机制调用私有构造器。如果要彻底地域这种攻击,可以修改构造器,让它在第二次创建的时候抛出异常(比如内定个final flag进行判断)。
B:
public class single{
private static final single INSTANCE = new single();
private single(){...}
public static single getInstance(){return INSTANCE;}
public void leaveTheBuilding(){...}
}
第二种方法,共有的成员变量是一个静态工厂方法。对于所有的getInstance方法,都会返回同一个对象引用,所以,永远都不会创建其他的single实例(同之前的提醒哦)
这个方法的好处在于,组成类的成员的声明可以很清楚地表明这个类是一个Singleton。
这个方法的优势在于,它提供了灵活性,即在不改变API的前提下,我们可以轻松改变这个类是否为Singleton。工厂方法返回该类的唯一实例,但是它可以很容易被修改,比如改成每个调用该方法的线程返回一个唯一的实例。
Java1.5之后还有第三种方法去实现Singleton,只需要一个包含单个元素的枚举类
public enum single{
INSTANCE ;
public void leaveTheBuilding(){...}
}
这个方法更加简洁,无偿提供了序列化机制(这个我还不是很理解,所以之前的还没有写这个序列化)。虽然这种方法还没有广泛采用,但是单元素的枚举类型已经成为实现Singleton的最佳方法!