解析《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 ...
随机推荐
- js之美
http://fxck.it/post/72326363595 大部分脚本语言里字面量都要快一些,因为解析[]比解析new Array()步骤少的多 关于Object可以用constructor注入的 ...
- 利用反汇编手段解析C语言函数
1.问题的提出函数是 C语言中的重要概念.利用好函数能够充分利用系统库的功能写出模块独立.易于维护和修改的程序.函数并不是 C 语言独有的概念,其他语言中的方法.过程等本质上都是函数.可见函数在教学中 ...
- IOC DI SL的一些理论
本文来自圣杰的简书 圣杰 yubinfeng的 这篇文章也很不错 很简单
- 【翻译自mos文章】对于每个文件的 file.id and file.incarnation number,重命名文件别名
对于每个文件的 file.id and file.incarnation number,重命名文件别名 參考原文: Rename Alias of Datafile as Per file.id an ...
- Delphi读取文件属性
Read File Detailed Properties https://www.board4all.biz/threads/read-file-detailed-properties.655787 ...
- memmove、memccpy和memcpy
这三个函数都是内存拷贝,目的都是将N个字节的源内存地址的内容拷贝到目标内存地址中. void *memmove(void *dest,void*src,int count) v ...
- 本文摘录 - FlumeJava
本文节选不保证论文的完整性和理解的准确性 原始的MapReduce.分Map,Shuffle,Reduce. Map里包含shards. Shuffle理解为groupByKey的事情.Reduce ...
- python解决urllib2乱码问题
示例: #!/usr/bin/env python # -*- coding: utf-8 -*- import urllib import urllib2 def main(): url = &qu ...
- WPF 使用Propereties:Resources.resx里面的资源
<Window x:Class="Wpf180706.Window7" xmlns="http://schemas.microsoft.com/win ...
- C# TCP 通讯
//接收 using System; using System.Collections.Generic;using System.ComponentModel;using System.Data;us ...