Effective Java通俗理解(持续更新)

  这篇博客是Java经典书籍《Effective Java(第二版)》的读书笔记,此书共有78条关于编写高质量Java代码的建议,我会试着逐一对其进行更为通俗易懂地讲解,故此篇博客的更新大约会持续1个月左右。

第1条:考虑用静态工厂方法代替构造器

  通常情况下我们会利用类的构造器对其进行实例化,这似乎毫无疑问。但“静态工厂方法”也需要引起我们的高度注意。
  什么是“静态工厂方法”?这不同于设计模式中的工厂方法,我们可以理解它为“在一个类中用一个静态方法来返回这个类的实例”,例如:

public static People getInstance() {
    return new People();
}

  它是一个“方法”,那么它不同于构造器,它可以随意修改方法名,这就带来第一个优点——有名称。有时一个类的构造器往往不止一个,而它们的名称都是相同的,不同的仅仅是参数,如果不同参数带来不同的含义这样对于调用方来说除了注释很难理解它们有什么不同的含义。例如BigInteger(int, int, Random)返回一个素数,但调用者很难理解API设计者所要想表达的意思,如果此时有BigInteger.probablePrime静态工厂方法,则能一目了然的清楚API设计者所要想表达的含义。举一个JDK的例子:Executors类,在这个类中有newFixedThread、newSingleThreadExecutor、newCachedThreadPool等静态方法,因为它们有“名字”,所有就较为清晰的明白API的含义。

  《Effective Java》中所提到的静态工厂方法第二个优点在于不用重复创建一个对象,实际上也就是勤加载或者称为饿汉式的单例模式。例如:

public class Instance() {
    private static Instance instance = new Instance();
    private Instance(){}
    public static Instance getInstance() {
    return instance;
    }
}

  静态工厂方法的第三个优点,可以返回原返回类型的任何子类型的。这句话初看不好理解,举个JDK中的例子:Collections类。 

List list = Collections.synchronizedList(new ArrayList()) 

  这个例子就说明了可以返回原返回类型的任何子类型的对象。
  关于静态工厂方法的第四个优点,在创建参数化类型实例的时候,它们使代码变得更加简洁,书中举了一个例子:

Map<String, List<String>> m = new HashMap<String, List<String>>();    //这会显得很繁琐

  给集合类提供静态工厂方法后:

public static <K, V> HashMap<K, V> newInstance() {
    return new HashMap<K, V>();
}

  但是实际上从JDK7(包括JDK7)之后的集合类可以用以下简洁的代码代替:

Map<String, List<String>> m = new HashMap<>();

  静态工厂方法也有两个缺点:一是公有的静态方法所返回的非公有类不能被实例化,也就是说Collections.synchronizedList返回的SynchronizedList不能被实例化;二是查找API比较麻烦,它们不像普通的类有构造器在API中标识出来,而是和其他普通静态方法一样,鉴于此,书中提到了几种惯用名称:

  valueOf
  of  
  getInstance
  newInstance
  getType
  newType

第2条:遇到多个构造器参数时要考虑用构建器

  你是否写过下面类似的代码:

public void Student()  {
    /*必填*/
    private String name;
    private int age;
    /*选填*/
    private String sex;
    private String grade;
    public Student(String name, String sex) {
        this(name, sex, 0);
    }
    public Student(String name, String sex, int age) {
    this(name, sex, age, “”);
    }
    public Student(String name, String sex, int age, String grade) {
    this.name = name;
    this.sex = sex;
    this.age = age;
    this.grade = grade;
    }
}

  当我想实例化一个名字叫“kevin”,性别男,但是不写年龄,只有年级“1年级”,这个时候代码就:不得不要为年龄这个参数传递值。如果新增一个只含年级的构造方法,那又将多出许多代码,更严重的是,如果没有一份详尽的文档或者注释,看到如此多的构造方法将无从下手,这就是非常常见的重叠构造器。

Student student = new Student(“Kevin”, “男”, “0”, “1年级”);

  当然还有另外一种方法,只有一个必填项的构造方法,而其他选填项利用setter方法传递。例如:

Student student = new Student(“kevin”, “男”);
student.setGrade(“1年级”);

  这实际上导致了在构造过程中JavaBean可能处于不一致的状态,也就是说实例化对象本该是一气呵成,但现在却分割成了两大步,这会导致它线程不安全,进一步引发不可预知的后果。
  书中提到较为“完美”的解决方案就是利用“Builder模式(建造者模式)”,有关此设计模式可以查看《建造者模式》。这种解决方案属建造者模式的一种形式,其核心就是不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个builder对象,再调用类似setter的方法设置相关可选参数。构建器模式如下所示:

/**
 * 构建器模式
 * Created by yulinfeng on 2017/8/3.
 */
public class Student {
    /*必填*/
    private String name;
    private int age;
    /*选填*/
    private String sex;
    private String grade;

    public static class Builder {
        private String name;
        private int age;
        private String sex = "";
        private String grade = "";

        public Builder(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public Builder sex(String sex) {
            this.sex = sex;
            return this;
        }
        public Builder grade(String grade) {
            this.grade = grade;
            return this;
        }
        public Student build() {
            return new Student(this);
        }
    }
    private Student(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.sex = builder.sex;
        this.grade = builder.grade;
    }
}

  客户端代码:

Student student = new Student.Builder("kevin", 23).grade("1年级").build();

  这样的客户端代码很容易边写,并且便于阅读。对于不了解的可能来说利用构建器模式编写Student类不算易事,甚至代码比重叠构造器的代码更多。所以当可选参数在很多时,谨慎使用重叠构造器,而是使用构建器模式。

——170803

时间: 2024-12-21 08:35:29

Effective Java通俗理解(持续更新)的相关文章

【Java】Java异常整理 - 持续更新

1. 编辑项目Build Path时出错: 检查Eclipse的进度条(Process),看是不是还在Build和Validate项目(一般出现在刚刚Project Clean之后),在这期间编辑Build Path,OK时会报错. 使用Maven重建了项目依赖之后,记得Refresh之后再编辑Build Path,否则也会报错. 2. Spring StackOverflowError 2015-07-01 20:13:44.001:WARN::failed [email protected]

JAVA native 学习 [持续更新]

最近查看ZipFile的源码,发现了native声明,学习一下,下面是整理的一些资料. java native方法及JNI实例 http://blog.csdn.net/xw13106209/article/details/6989415 JNI学习笔记(七)——异常处理 http://blog.csdn.net/ljeagle/article/details/6728930

java基础必备&lt;持续更新优化&gt;

java的程序分有2类:     1.嵌入在网页中,通过浏览器运行的程序,被称为Applet,译为小应用程序. 2.除1之外Java程序,被称为Application,译为应用程序. 第一个java Applet程序: 文件名firstApplet.java import java.applet.*;import java.awt.*;  // import关键字表示要引入某个包 // extends Applet表示继承Applet类 // 程序没有main()方法,取而代之是paint()方

java并发编程[持续更新]

[toc] java并发编程 1.常用类介绍 Semaphore Semaphore 类是一个计数信号量,必须由获取它的线程释放, 通常用于限制可以访问某些资源(物理或逻辑的)线程数目. Semaphore包含三种操作 初始化 获取acquire() 释放 release() 当信号量大于0的时候semaphore会响应线程请求,释放资源,当信号量等于0时即阻塞线程. Semaphore有两种模式,公平模式和非公平模式 公平模式遵循FIFO,按照acquire的顺序来分配资源,非公平则为抢占式的

java开发中遇到的问题及解决方法(持续更新)

摘自 http://blog.csdn.net/pony12/article/details/38456261 java开发中遇到的问题及解决方法(持续更新) 工作中,以C/C++开发为主,难免与其他服务和Web进行交换,Java开发必不可少,又不想动用Eclipse大家伙,只能自己动手编写脚本进行Java代码的编译和运行,期间遇到的一些问题,记录下来供自己和大家参考.1)软件包不存在/软件包 javax.jms 不存在    这是由于javac编译时找不到javax.jms所在的软件包,因此将

java 编译异常及其解决办法(持续更新)

java 编译异常及其解决办法(持续更新) 1.解决办法:将jar版本降低适配 低版本的jdk或tomcat 1 严重: ContainerBase.addChild: start: 2 org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/EasyGo]] 3 at org.apach

C#、Java中的一些小功能点总结(持续更新......)

前言:在项目中,有时候一些小的功能点,总是容易让人忽略,但是这些功能加在项目中往往十分的有用,因此笔者在这里总结项目中遇到的一些实用的小功能点,以备用,并持续更新...... 1.禁用DataGridView表头的排序功能 1 /// <summary> 2 /// 禁止点击列表头进行排序 3 /// </summary> 4 /// <param name="dgv">当前DataGridView控件</param> 5 private

Java基础知识汇总(持续更新)

持续更新算是给自己立个flag,截图来自内部课程,内容很杂,后续整理归类... Mobile apple;apple = new Mobile("Apple","iPhone Xs Max",8499); 创建对象后,相当于在内存中保存了对象的地址,通过对象的地址找到对象的本身,从而找到相关的属性 成员变量默认初始值: 编码规范:包:全小写,名词.公司.团队组织名域名的反写:类名:名词或名词性短语,首字母大写,多个单词首字母均大写:接口:等同于类:方法:动词或动宾短

我的读书清单(持续更新)

我的读书清单(持续更新) 2017-05-31 <一千零一夜>2006(四五年级) <中华上下五千年>2008(初一) <鲁滨孙漂流记>2008(初二) <钢铁是怎样炼成的>2009(初二) <坏蛋是怎样炼成的>2010(初三) <平凡的世界>2011(高二) <悲惨世界>2012(高二) <穆斯林的葬礼>2012(高二) <红楼梦>2012(高二) <边城> <傲慢与偏见>