1. 遇到多个构造器考虑建造器
多个构造器,只有参数列表不同,应用起来会很不方便,容易混淆,参数过多,调用的时候可能并不知道如此多的参数具体代表什么含义。比如我们可能会有以下的代码片段:
public class Test {
public Test(String a, int b, boolean c, long d){}
public Test(int a, int b, boolean c, long d){}
public Test(int a, String b, boolean c, long d){}
public Test(float a, double b, int c, long d){}
}
Test类拥有很多构造方法,但是由于构造方法过多导致含义模糊,我们在实例化Test的对象的时候,不知道具体应该调用哪一个构造方法,所以有的时候我们可能会在类中写一个无惨的构造方法,然后这样为Test中的属性赋值:
Test test = new Test() ;
test.setA(a) ;
test.setB(b) ;
test.setC(c) ;
test.setD(d) ;
这样的代码看以来没什么不妥,不过从EffectiveJava书中,这种方式会发生一个问题,JavaBean状态不一致问题,下述讲述。
总之,如果存在多个构造器的时候,需要考虑用Builder模式。构建builder模式,可以保证JavaBean一致性,用户提前设置属性到builder中,然后通过builder来构建Bean实例,由于静态内部类可以产生多个实例,所以多个线程访问会出现多个实例,不会影响当前builder实例的内容,builder对象不受影响,通过builder构造Test也就没有状态不一致的问题了。如下代码:
public class Test {
private String name ;
private int age ;
public Test(TestBuilder builder){
this.age = builder.age ;
this.name = builder.name ;
}
public static class TestBuilder{
private String name ;
private int age ;
public TestBuilder name(String name){
this.name = name ;
return this ;
}
public TestBuilder age(int age){
this.age = age ;
return this ;
}
public Test build(){
return new Test(this) ;
}
}
public static void main(String[] args) {
Test test = new TestBuilder().age(11).name("EffectiveJava").build();
}
}
2.JavaBean状态不一致
个人理解JavaBean状态一致:用户用指定的参数创建JavaBean,创建之后此JavaBean内包含的属性值与用户指定的参数值相同,上述代码中,如果用构造方法创建的对象,是可以保持一致性的,因为程序在执行构造方法之后,才生成新的实例,所以实例中的属性值和用户指定的必定相同,因为在调用构造方法之前根本没有对象产生,用户无法通过一个不存在的对象来操作对象的属性。用set来设置JavaBean的值,不能保持一致性,创建对象后,用户通过set来设置属性值,但是在设置之后,另外的用户可以通过其他线程来调用set方法来更改此属性,此时也就是JavaBean状态不一致。
3.攻击单例模式
单例模式大家都很熟悉,例子我不在举出,只讨论在什么情况下会产生多个实例:
1.反射机制,我们可以通过反射来调用单例类的私有构造方法来创造对象。解决方式:可以再类的私有构造方法中增加判断,第二次创建该对象时候抛出异常。
2.IO流反序列化,利用IO流对类进行反序列化时,会生成新的对象。解决方式:单例类实现readResolve方法。
4. 避免创建不必要的对象
如果变量不需要改变,则声明为常量,不要每次都new一个对象
计算应该用基本数据类型,而不是用封装类。如使用int、long,但要规避使用Integer、Long