解析《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 ...
随机推荐
- cordova使用cordova-plugin-baidumaplocation插件获取定位
原文:cordova使用cordova-plugin-baidumaplocation插件获取定位 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/m ...
- 配置文件——App.config文件读取和修改
作为普通的xml文件读取的话,首先就要知道怎么寻找文件的路径.我们知道一般配置文件就在跟可执行exe文件在同一目录下,且仅仅在名称后面添加了一个.config 因此,可以用Application.Ex ...
- 抛砖引玉 【镜像控件】 WPF实现毛玻璃控件不要太简单
原文:抛砖引玉 [镜像控件] WPF实现毛玻璃控件不要太简单 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/Vblegend_2013/articl ...
- C#委托五(自定义事件)
事件: "在发生其他类或对象关注的事情时,类或对象可以通过事件通知他们.发送(或引发)事件的类称为"发行者",接受(或处理)事件的类称为"订户".&q ...
- 自学 Python
如何系统地自学 Python? 最近开始系统的学习Python,以及整理的一些资料.github记录着个人自学 Python 的过程,持续更新.欢迎大家一起来完善这个自学Python学习的项目,给 ...
- STL优先级队列
priority_queue 这是一个优先级队列的所有权值概念单向队列queue.在这个队列中.全部元素是按优先级排列的(也能够觉得queue是个按进入队列的先后做为优先级的优先级队列--先进入队列的 ...
- 【树转数组】poj1195
/* 二维的树状数组: 更新某个元素时: NO.1:c[n1],c[n2],c[n3],....,c[nm]; 当中n1 = i,n(i+1) = ni+lowbit(ni); nm+lowbit(n ...
- ASP.NET Core 用户注册 - ASP.NET Core 基础教程 - 简单教程,简单编程
原文:ASP.NET Core 用户注册 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 用户注册 上一章节我们终于迁移完了 Identity 的数据,也创建 ...
- Distinct去除重复项
之前在做权限模块时,因不同角色可能拥有相同的菜单,导致呈现在浏览器上时出现重复菜单项,所以需要在获取用户拥有菜单项时需要过滤重复项, 用到了Distinct,两个重载 public static IQ ...
- 阿凡达是脸,教你的脸在线(包括URL和使用)
官方网站:http://www.mcdonalds.at/avatar/ 英文版本号:lid=finland" target="_blank">http://www ...