《Effective Java》阅读笔记之对象创建及销毁
写在前面
本文及后面的几篇笔记都是读《Effective Java》的随笔。内容里面有书里面重点的部分,所以大家别见怪就好。如果各位发现啥问题,希望能不吝赐教,虽然是自己的笔记,也希望能得到大家的指教。
关于对象的创建,《Design patterns》里面有五种设计模式支撑,不能说设计模式就是最终导向,只能说是特定情况下的全局考量。那么在《Effective Java》(以下用EJ表示)里面的处理方式,可以跟这些设计模式相得益彰,因为GOF讲的是大局,而Bloch讲的是实践。必要时,两个方面都会谈谈。
一、静态工厂方法
优点:
1.可以有更能说明具体作用的方法名(与构造函数相比)
从维护代码的角度讲,这的确是个优点。至少可以提供更加简洁明了的api支持。但是,如果一个类已经能够说明清楚用途,大可不必因为这个原因来做此重构。
2.不必每次都创建一个新对象(与构造函数相比)
从接触java开始,我们就不停地在利用构造函数创建对象,new来new去,再加上jvm的垃圾回收机制,使得我们java团员们根本不会考虑一味的对象创建会对内存和效率产生多大影响。通过静态工厂方法,我们可以重复使用一些对象。这些对象的特点:常用属性不变(field或method)。
在java的api里面,就有:
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
这就可以重复使用public static final Boolean TRUE = new Boolean(true);或者public static final Boolean FALSE = new Boolean(false);
在这种情况下,我们的代码里面应该都是领域(用有业务含义的词来说明)的语言表达。结合第一点,我们可以让我们的代码变得更加简洁有效。
3.可以返回子类的对象(这点明显优于构造函数,但是否有这个必要,还需考虑)
一般情况下,我想可以不考虑这个实现。但是考虑到EJ提到的服务提供框架,这的确是非常高级的一种处理。
在此也大体说明一下这种service provider framework!在EJ中对这种框架的组成部分有比较详细的说明(不禁感叹学习的重要性了)。这种框架主要包括三个接口:服务接口(各种服务提供类必须实现的统一服务接口);服务注册接口(便于各个具体服务提供实现,注册到框架中);服务访问接口(这是服务使用端【或者称为客户端】需要的)。
例子的话,也就是JDBC里面的:Connection是服务接口,DriverManager.registerDriver是服务注册接口,DriverManager.getConnection是服务访问接口。
4.降低冗长的泛型定义(在定义一些集合类时,的确比较繁琐,尤其是我们的泛型比较复杂时)
对于这点,我也模仿EJ里面的方式,写了几个方法:
public static final <T> ArrayList<T> newArrayList(int initialCapacity){
return new ArrayList<T>(initialCapacity);
}
在使用的时候,真的少些很多type reference(就是泛型的类型定义)。
缺点:
1.如果提供了静态工厂方法,那么这个类就应该禁用构造函数创建,同时也就不能被继承。
2.无法区分静态工厂方法跟其他静态方法。
二、用构造器替换参数较多的构造函数
这种情况时候存在,比方说:
class Person {
public Person(int id,int age,int weight,int hight){
this.id = id;
this.age = age;
this.weight = weight;
this.hight = hight;
}
//nothing more ....
}
在这个类里面我们需要四个变量,这还是少的。即便少,有时也会眼迷一下,搞错位置,就出问题了:Person p = new Person(123344,23,76,178);哪个可怜鬼要是碰到了这个类,算是比较幸运的。再来四个变量,就疯了。一般在这种情况下,考虑通过setter的方式来避免同一类型的参数错误:
class Person {
public Person(){}
public void setId(int id){this.id = id;}
//其他几个变量都有对应的setter方法
}
创建对象:
Person p = new Person();
p.setId(123344);
p.setAge(23);
...
尽管这种方式比构造函数的参数的情况好很多,但还是要写大片的代码。所以,就在此引入builder。
接下来是EJ里面的实现方式:
class Person{
private int id;
private int age;
private int weight;
....
private Persion(Builder builder){
this.id = builder.id;
this.age = builder.age;
......
}
public static class Builder{
private int id;
private int age;
private int weight;
....
public Builder(int id){this.id = id;}
public Builder age(int age){this.age = age; return this;}//这个效果的确比较方便,能出现面条式的赋值方式
public Builder weight(int weight){this.weight = weight;return this;}
.......
public Person build(){
return new Person(this);
}
}
}
各位看到这里,可能也会跟我一样,会有中看到懒婆娘的裹脚啦。但是这种方式在创建对象时,效果还是相当可观的:Person p = new Person.Builder(123344).age(23).weight(76).build();比前面好了很多。当然我也在想,EJ的作者为啥非要在Person里面再创建一个builder,而不是直接在Person里面用
这种赋值方式呢?毕竟可以减少,变量的复制,因为在builder里面有跟Person一样的域列表。个人看法,EJ作者是想把责任明确,如果是Person里面就需要一个用于创建实例对象的方法,但是那样的话,责任就不是很明确了,就是对象自己创建自己,还不如使用构造函数算了。
上面的这种方式,的确很值得借鉴,个人觉得适用的情况要明确。
三、强制属性单例
首先,单例在某些情景中是违反设计模式的,比如比较重要的资源:IO,数据库链接等。凡事不可一概而论,单例也有很多优点,可以重用对象。我们该如何加入单例属性呢?
第一点要注意的就是构造函数应该是private的。当然,用单例属性时不一定非要使得宿主类是不可被构造的,这种情况仅仅是保证宿主类就要是单例的。这个问题在单例模式(GOF)中有详细说明。这个单例属性应该是静态的,而且最好是在定义时初始化,否则,就要考虑double-check问题。还是直接来一个类说明一下比较好:
class Person{
private static final Person INSTANCE = new Person();
public static Person getInstance(){ return INSTANCE;}//静态工厂方法
}
这种方式中,通过静态的final变量定义方式,可以保证其永远都是同一个对象。
在序列化的时候,因为static和transient变量不会被写到流中。所以,经过序列化之后,总是会使得静态变量再次创建。那么我们所谓的单例也就不能保证了。
但是通过enum可以避免在序列化(从参考资料中,查看enum的序列化处理)中像static那样的负面影响,可以比较保险的实现单例。
public enum PersonEnum{
INSTANCE;
public void doAction(){...}
}
四、如何实现非实例化的对象创建及避免多余的对象创建?
通过private构造函数就可以避免不能实例化的对象创建了,当然EJ中也分析了抽象类的一些缺点(引导用户去继承呀等等)。
而避免多余的对象创建,其例子是String s = new String("test");这里就创建了两个string对象。那么对于其他的一些情况,我们要再深入些:
1.java的autobox功能。尽量避免这种转换,以减少对象创建的频率。
2.仔细分析方法调用,区分变化和非变化部分。例如:一个方法调用中,总是会创建一些固定的对象,可以考虑让这些个对象的创建过程在实例或者类层次上完成。以避免在多次的方法调用过程中重复对象创建。
五、避免过时的对象引用及finalizer
一般的变量赋值可能不会出现过时对象引用问题。在用到引用数组或者缓存等数据管理时,要时刻关注这种引用,是否会造成对象引用过时。对于数组(集合也算在其中)情况,可以在使用完之后,赋值成null。而在缓存中,可以使用weakreference的方式来处理。
对于finalize,个人觉得就是记住:一定不要相信它来做任何资源处理。当然,如果你用了,而且说这东西很好,我只能说,那是个奇迹。
六、参考资料
java序列化的高级认识 http://www.ibm.com/developerworks/cn/java/j-lo-serial/
三种enum的序列化http://www.vineetmanohar.com/2010/01/3-ways-to-serialize-java-enums/
weakreference相关的 http://www.cnblogs.com/ericchen/archive/2011/05/27/2059060.html
posted on 2011-08-11 14:45 eric_chen 阅读(2211) 评论(6) 编辑 收藏 举报