最近看《Effective Java》这本被很多同行称为神作的书,但是这本书很多地方缺少了举例不好懂,下面是关于我对书上知识的理解。
一、《Effective Java》中文版2 ——第二章 第2条:遇到多个构造器参数时要考虑用构建器
原文语段:遗憾的是,JavaBeans模式自身有着很严重的缺点。因为构造过程被分到了几个调用中,在构造过程中JavaBean可能处于不一致的状态。类无法仅仅通过检验构造器参数的有效性来保证一致性。试图使用处于不一致状态的对象,将会导致失败,这种失败与包含错误的代码大相径庭,因此它调试起来十分困难。与此相关的另一点不足在于,JavaBeans模式阻止了把类做成不可变的可能(见第15条),这就需要程序员付出额外的努力来确保它的线程安全。
二、理解多个构造器、Javabeans模式
1、首先我们要知道构造器有个很大的局限性:它们都不能很好地扩展到大量的可选参数。举个简单的例子 人有:姓名、性别、年龄、国家、职业等属性。
public class Person { private String name; //姓名 private String age; //年龄 private String sex; //性别 private String country; //国家 private String occupation; //职业 public Person(String name) { this(name,null); } public Person(String name, String age) { this(name,age,null); } public Person(String name, String age, String sex) { this(name,age,sex,null); } public Person(String name, String age, String sex, String country) { this(name,age,sex,country,null); }
public Person(String name, String age, String sex, String country, String occupation) { this.name = name; this.age = age; this.sex = sex; this.country = country; this.occupation = occupation; } }
类似是这样的构造方式,我们(程序员)一般习惯采用重叠构造器(telescoping constructor)模式。
当你想要创建实例的时候,就利用参数列表最短的构造器,但该列表中包含了要设置的所有参数:
Person person = new Person("xiaobai", "12", "男", "中国");
获取到的实例属性已经固定了不可变了。但这种方式有着明显的缺点就是:它们都不能很好的扩展到大量的可选参数。而且代码的可读性变差。
2、理解什么是Javabean
JavaBean是一个遵循特定写法的Java类,它通常具有如下特点:(1)这个Java类必须具有一个无参的构造函数(2)属性必须私有化。(3)私有化的属性必须通过public类型的方法暴露给其它程序,并且方法的命名也必须遵守一定的命名规范。
public class Person { private String name; //姓名 private String age; //年龄 private String sex; //性别 private String country; //国家 private String occupation; //职业 public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public String getOccupation() { return occupation; } public void setOccupation(String occupation) { this.occupation = occupation; } }
JavaBeans模式阻止了把类不可做成可变,而且代码的可读性变的很强。但JavaBeans 模式自身有着严重的缺点: 那就是构造过程被分解到了几个调用中,在构造过程中JavaBean可能处于不一致的状态。
举个例子来说,线程A创建Person实例并只给name用set赋值,线程B创建Person实例只给age用set赋值,这样两个线程创建出来的两个实例我们就无法用构造方法上的参数来判断属性状态是否相同(PS:person类用的无参方法。)。所以这样就能解释了《Effective Java》原文说:“因为构造过程被分到了几个调用中,在构造过程中JavaBean可能处于不一致的状态。类无法仅仅通过检验构造器参数的有效性来保证一致性。”
三、Builder模式
Builder模式:不直接生产想要的对象,而是让客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个builder对象。然后客户端再builder对象上调用类似于setter方法,来设置每个相关的可选参数。最后,客户端调用无参的build方法来生产不可变对象。
public class Person { private String name; //姓名 private String age; //年龄 private String sex; //性别 private String country; //国家 private String occupation; //职业 public static class Builder { private String name; //姓名 private String age; //年龄 private String sex; //性别 private String country; //国家 private String occupation; //职业 public Builder(String name, String age) { this.name = name; this.age = age; } public Builder sex(String val) { sex = val; return this; } public Builder country(String val) { country = val; return this; } public Builder occupation(String val) { occupation = val; return this; } public Person build() { return new Person(this); } } private Person(Builder builder) { name = builder.name; age = builder.age; sex = builder.sex; country = builder.country; occupation = builder.occupation; } }
调用方式:
Person person = new Person.Builder("xiaobai","18").sex("男").country("中国").occupation("程序员").build();
这样编写的build编写的可读性与多个构造器相比更强。
builder像个构造器一样,可以对其参数强加约束条件。build
方法可以检验这些约束条件。将参数从builder拷贝到对象中之后,并在对象域而不是builder域中对它们进行检验,这一点很重要。
还有就是Java中传统的抽象工厂实现是Class
对象,用newInstance
方法充当build
方法的一部分。这种用法隐含着许多问题。newInstance
方法总是企图调用类的无参构造器,这个构造器甚至可能根本不存在。如果类没有可以访问的无参构造器,你也不会收到编译时错误。相反,客户端代码必须在运行时处理InstantiationException
或者IllegalAccessException
,这样既不雅观也不方便。newInstance
方法还会传播由无参构造器抛出的任何异常,即使newInstance
缺乏相应的throws
子句。换句话说,Class.newInstance
破坏了编译时的异常检查。上面讲过的Builder接口弥补了这些不足。
builder模式的不足在于:(1)为了创建西对象,必须先创建它的构建器。虽然创建构建器的开销在实践中可能不那么明显,但是在某些十分注重性能的情况下,可能就成问题了。
(2)Builder模式还比重叠构造器更加冗长,因此它只有在很多参数的时候才使用。
结语:
如果有看过此书的,或者有对文章有不同看法的欢迎在评论区评论。