解析《Effective Java》之多个构造器、Javabeans模式和Builder模式
最近看《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模式还比重叠构造器更加冗长,因此它只有在很多参数的时候才使用。
结语:
如果有看过此书的,或者有对文章有不同看法的欢迎在评论区评论。
解析《Effective Java》之多个构造器、Javabeans模式和Builder模式的更多相关文章
- Java设计模式(3)建造者模式(Builder模式)
Builder模式定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构 ...
- 《Effective Java》-——用私有构造器或者枚举类型强化Singleton属性
Singleton指仅仅被实例化一次的类.Singleton通常被用来代表那些本质上唯一的系统组件,比如窗口管理器或者文件系统.使类成为Singleton会使它的客户端测试变得十分困难,因为无法给Si ...
- Effective Java 之 --- 用私有构造器或者枚举类型强化Singleton属性
Singleton指仅仅被实例化一次的类,通常用来代表那些本质上唯一的系统组件,实现Singleton有三种方法: 1)公有静态成员是个final域,享有特权的用户可以调用AccessibleObje ...
- Java设计模式--------建造者模式(Builder模式)
Builder模式定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构 ...
- effective java 3th item2:考虑 builder 模式,当构造器参数过多的时候
yiaz 读书笔记,翻译于 effective java 3th 英文版,可能有些地方有错误.欢迎指正. 静态工厂方法和构造器都有一个限制:当有许多参数的时候,它们不能很好的扩展. 比如试想下如下场景 ...
- 《Effective Java第二版》总结
第1条:考虑用静态工厂方法代替构造器 通常我们会使用 构造方法 来实例化一个对象,例如: // 对象定义 public class Student{ // 姓名 private String name ...
- Builder模式在Java中的应用
在设计模式中对Builder模式的定义是用于构建复杂对象的一种模式,所构建的对象往往需要多步初始化或赋值才能完成.那么,在实际的开发过程中,我们哪些地方适合用到Builder模式呢?其中使用Build ...
- Java设计模式之builder模式
Java设计模式之builder模式 今天学mybatis的时候,知道了SQLSessionFactory使用的是builder模式来生成的.再次整理一下什么是builder模式以及应用场景. 1. ...
- Builder模式在Java中的应用(转)
在设计模式中对Builder模式的定义是用于构建复杂对象的一种模式,所构建的对象往往需要多步初始化或赋值才能完成.那么,在实际的开发过程中,我们哪些地方适合用到Builder模式呢?其中使用Build ...
随机推荐
- JavaScript(js)获取本周,本月,本季,本年,上月,上周,上季,去年,上二周,上二月的时间段的代码
function dateChange(name){ var beginTimeObject = document.getElementById("beginTime"); var ...
- Android studio中的6大布局
1.相对布局代码: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns: ...
- EM 12c: OMS Failed to start After restarting the Repository Database or reboot of the server
原文地址:http://blog.csdn.net/wanghui5767260/article/details/39398449 更全面,更专业 EM 12c: OMS Failed to star ...
- 利用tcpdump分析工具来验证tcp连接的建立和关闭过程
本文要求读者在阅读之前应该对TCP通过三次握手建立和关闭连接有一定的了解,本文并没有详细讲解三次握手,只是通过一个实例对三次握手进行了一下验证. tcp连接的建立和关闭想必大家都已经非常熟悉了!通过三 ...
- WPF模拟Office2010文件菜单的TabControl模板
原文:WPF模拟Office2010文件菜单的TabControl模板 这是Office2010中的文件菜单点开后的效果.本文我将以强大的WPF(www.itstrike.cn)来实现类似的效果.希望 ...
- 熵、交叉熵、相对熵(KL 散度)意义及其关系
熵:H(p)=−∑xp(x)logp(x) 交叉熵:H(p,q)=−∑xp(x)logq(x) 相对熵:KL(p∥q)=−∑xp(x)logq(x)p(x) 相对熵(relative entropy) ...
- OpenGL(六) gluLookAt和gluPerspective函数解析
在调用gluLookAt和gluPerspective函数之前一般要先调用一下glLoadIdentity函数,先说一下这个函数是做什么的. glLoadIdentity glLoadIdentity ...
- [转]TensorFlow如何进行时序预测
TensorFlow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库.节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组 ...
- Harden the Hacker Thinking (Updating)
录制自己的最新思考harden过程.通过记录,反射,加强管理,发现缺陷. 等一下design,等一下coding,三十分钟rethinking. 2015-02-26 : 不要在一件事上停留太久: 歇 ...
- JDK源码阅读——Vector实现
1 继承结构图 Vector同样继承自AbstractList,与ArrayList.LinedList一样,是List的一种实现 2 数据结构 // 与ArrayList一样,也是使用对象数组保存元 ...