《Effective Java》阅读笔记之对象创建及销毁

来源:互联网 发布:帝国cms 图片系统模型 编辑:程序博客网 时间:2024/05/17 06:20

写在前面

本文及后面的几篇笔记都是读《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 finalarraylist newarraylist(int initialcapacity){

return new arraylist(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 = newperson.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


======================================================
在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/
原创粉丝点击