前言
这是第一次学习写博客总结,很多的不足的地方,请客官多多指正,有关java的地方,多多交流
现在进入正题:
一、考虑用静态工厂方法代替构造器
这里说的工厂方法和设计模式里面的工厂方法是有区别的,
设计模式里的工厂模式是定义一个创建对象的接口,让子类选择去实现哪一个类,工厂模式让一个类的初始化延迟到其子类(设计模式之禅)。
effective java中的静态工厂只是为一个类提供一个静态方法,它本质是和当前类的其他静态方法没有区别,只是它是用来创建对象的。和构造器相比,提供的静态方法本身具有一些优势,在于易阅读,易使用,而且可以提供方法参数,并且返回的对象可以协变成子类,相当的具有灵活性。还有一个比较常见的好处是 在创建参数化类型实例的时候,和传统的构造器相比可以让代码更简洁,比较实例如下:
传统构造器:
Map<String,List<String>> map = new HashMap<String,List<String>>();
可以看到,传统的构造需要在后面再写一遍,但是,提供了静态工厂方法后,这种情况就不再出现了:
静态工厂方法:
public static <K,V> HashMap<K,V> newInstance(){
return new HashMap<K,V>();
}
静态方法构造:
Map <String,List<String>> map = HashMap.newInstance();
直接看出来,简便了很多。。。
静态方法的缺陷,如果类没有实现公有的或者受保护的构造器,那么不能实现子类化。。
二、当对象的字段过多的时候,考虑使用Builder模式
我们在创建一个对象的时候,有几种方式设置内部参数
1.直接使用构造器,一般来说,都会创建一个构造方法,将字段值放在构造器里一起创建,但是当字段过多的时候,很容易造成字段很难区分,比如:
public Demo(String field1,String field2,int field3,int field4,int field5){}
在实际开发中,字段的命名请规范且易理解,考虑加上doc注释,不要像实例的一样。可以看到当字段很多的时候,类型基本相同的时候,很难区分各个字段,所以很容易造成错误,就像把field3 field4 field5 的字段顺序写错了,这个时候最好的结果是程序抛出运行时异常,最坏的结果程序可能还在运行中,且没有抛出错误,这时候的逻辑已经出错,而且不易发现,很难查找,但是整个程序已经偏离了我们的期望。
2.使用JavaBeans方法,通过调用默认的无参构造器,构造一个对象,之后通过提供的setter() 方法一个个的给字段添加值。
Demo demo = new Demo();
demo.setField1(field);
demo.setField2(field);
这样的方法,可以很轻松的给每一个字段赋值,但是缺陷的地方在于给每个字段赋值都是分开的操作,可能造成状态不一致。类无法仅仅通过检验构造器参数的有效性来保证一致性。试图使用处于这样不一致状态的对象,将会导致失败,这种失败与包含错误的代码大相径庭,因此调试起来也十分困难。另一点不足在于,JavaBeans模式防止了把类做成不可变的可能(15条有讲),这就需要程序员付出格外努力来确保它的线程安全。
3.使用Builder模式构造对象,既可以实现像javaBeans的给字段一一赋值,也不会像直接使用构造器一样,容易造成字段混乱。比较常见的Builder模式实例 StringBuilder类,可以查看相关源码。这里就不写代码了。StringBuilder的缺陷在于,每一次创建对象的时候都需要先创建一个它的builder对象,然后才能创建对象。
三、使用私有构造器或者枚举来强化singleton属性
对于单例模式,要保证只存在只有一个对象实例,一直以来都有很多实现方式,大多脱不出这两种范围
1,私有构造器,和静态的单例对象字段
例如:
public class SingletonDemo{
public static SingletonDemo demo = new SingletonDemo();
private SingletonDemo(){}
}
这种方法实现的单例,保证了只存在一个实例,但是有种情况是,如果特权用户用AccessibleObject.setAccess方法通过反射,可以调用当前类的私有构造器,从而创建新对象,对于抵御这种情况,需要添加判断,在第二次创建对象的时候抛出异常就可以了。
2.静态方法,私有的字段,私有的构造器
public class SingletonDemo{
private static SingletonDemo demo = new SingletonDemo();
private SingletonDemo(){}
public static SingletonDemo getInstance(){ return demo; }
}
这种方法也保证了只有一个实例,但是不管这种方法还是前面一种方法,如果要实现Serializable接口,使得对象可序列化,就会出现问题,在反序列化的时候,readObject()方法不管显示的还是默认的,都会提供一个新的对象给我们,导致反序列化单例失败,这种情况需要提供readResolve()方法,当反序列化的时候,readResolve()方法调用时,返回当前对象的引用来替换新建对象的引用,如
public SingletonDemo readResolve(){ return demo;}
这样就可以防止对象重复创建了
3.单元素枚举----其实这是实现单例的最好方法了
在java引入枚举之后,枚举就成了单例实现的最优方式,它可以自动维护对象的个数,绝对防止重复。因为enmu的元素就是enmu的实例,当只有一个元素的时候,它就只存在一个实例,枚举的用处有很多,不懂得可以去看看基础书(java程序设计语言 阿诺德,java编程思想)
public enmu SingletonDemo{
demo;
public void doSomething(){}
}
调用的时候,直接写成SingletonDemo.demo.doSomething()就可以了
四,使用私有构造器强化不可实例化的类属性
这条其实很简单,在我们代码中,有很多类是不需要实例化的,比如以前的配置类(现在不推荐这么写,可以考虑使用枚举来写),工具类等,它只包含了静态方法,静态字段,我们不需要给它实例化,这个时候,可以使用私有构造器,防止有人不小心实例化这个类。
注意:在给予私有构造器的时候,请写doc注释,说明为什么这个类使用私有构造器,因为私有之后直接创建对象也不能子类化了
五、避免创建不必要的对象
在代码中,其实很多这样的规则我们已经在遵守了,但是这里还是写出来,警惕自己犯错
1.使用基本数据类型来替代包装器类,避免自动装箱,java引入自动装箱和拆箱操作之后,基本数据类型和包装器类之间其实很模糊了,但是优先使用基本数据类型可以有效避免创建多余对象。基本数据类型是直接将值存在堆栈中,来保证高效,但是包装器类对象是保存在堆中的
2.对于同时提供了静态工厂方法和构造器的不可变类,使用静态工厂方法,避免创建不必要对象--
3.重(chong)用已知的可变但不会修改的对象
4.维护对象池并不是好做法,除非真的是非常重量级的对象,比较常规的是数据库连接池和缓存连接池
六、消除过期引用
过期引用这点,其实牵扯到内存泄漏(指的是被无意识的保存的对象,导致内存变小),所以在类自己管理内存时,请警惕内存泄漏的问题,消除过期引用。
可以使用WeakHashMap来存储 缓存 和监听器,回调。这样在内存不够的时候gc会优先清理这部分,达到释放内存的效果。
七,避免使用finalizer方法
finalizer()一般来说很少用到,它的运行是不可预测的,gc并不保证你调用了这个方法之后,它就马上运行,而且调用这个方法会造成严重的性能损耗(java虚拟机)
结语
昨晚就花了我几个小时的时间整理,今天中午午睡时间才打完发的,第一次写很多地方都写不好,考虑不周全或者错误,不足的地方,请多多指教。来简书是因为觉得里面的博客都很简洁,不太喜欢csdn的博客样子,哈哈,可以看到后面几条我写的非常少,这些都是正常的注意下就好了,其实书上是有很多的内容,推荐去看本书,认真研读几遍帮助很大,谢谢努力的自己和努力的你~~~