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

通常情况下,我们创建一个对象采取new的形式,但是还有一种方法也是经常使用到的,它的名称叫做静态工厂方法。

例如,java中基本类型boolean的包装类Boolean就采用了这种方式,源代码如下:

    public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}

当然,除了valueOf这种比较low的名字之外,我们常用的还有getInstance(最常见),newInstance,getType等。

静态工厂方法相对于构造器,好处多多。

①静态工厂方法有名称,而构造器是固定的。

例如,BigInteger的构造方法返回一个数字。如果想返回一个随机素数,用名为BigInteger.probablePrime的静态方法表示显然会更加清晰易读。

②不必每次调用的时候都创建一个新的对象。

这使得不可变类可以使用预先构建好的实例,或者将构建好的实例缓存起来重复利用,从而避免创建不必要的重复对象。例如上例中的Boolean.valueOf(boolean b)方法,调用它时,它从来没有创建对象。

③它可以返回原返回类型的任何子类型对象。

这里就不得不提及以下简单工厂模式,大家在平时开发中使用频率极高。

例如,有物理型英雄和法术型英雄,想要根据传入的类型获取对应的英雄。

public abstract class Hero {
} public class APHero extends Hero {
@Override
public String toString() {
return "法术型英雄";
}
} public class ADHero extends Hero {
@Override
public String toString() {
return "物理型英雄";
}
}

使用静态工厂方法就可以获取到指定的子类型,大大增加了可扩展性。

public class HeroFactory {
APHero ap = new APHero();
ADHero ad = new ADHero();
public Hero getHeroInstance(String type){
switch(type){
case "AP":return ap;
case "AD":return ad;
default: return null;
}
}
}

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

静态工厂和构造器有个共同的局限性:它们都不能很好地扩展到大量的可选参数。

有时候会碰到这样的情况,一个类里面属性很多,有些是必须的,有些是可选的。这样一来,普通的构造方法就得写一大堆(因为创建对象时可能性太多了)。

还有一种方法也是我们最常用的,就是构造方法就用默认的,每个属性都添加get set方法(JavaBean)。这样创建一个实例很容易,代码也清晰很多。但遗憾的是JavaBean自身有很严重的缺点,因为构造过程被分到了若个个调用中,可能出现不一致状态,导致多线程操作时不安全。

构建器方法既能保证安全性,又能保证可读性。下面大家来看一个例子(食品上的营养成分表):

public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate; public static class Builder {
// 必需参数
private final int servingSize;
private final int servings; // 不必需参数
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0; public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
} public Builder calories(int val) {
calories = val;
return this;
} public Builder fat(int val) {
fat = val;
return this;
} public Builder carbohydrate(int val) {
carbohydrate = val;
return this;
} public Builder sodium(int val) {
sodium = val;
return this;
}
//注意这里,跟Javabean很像,设置了一系列属性之后,Builder调用build方法构建真正的对象
public NutritionFacts build() {
return new NutritionFacts(this);
}
} private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
} }

在客户端我们就可以使用类似于jquery中连缀的这种语法来创建对象了

    public static void main(String[] args) {
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
.calories(100).sodium(35).carbohydrate(27).build();
}

实际上,就相当于将NutritionFacts类将创建对象的功能委托给了静态内部类Builder。而且,因为static类本身是线程安全的,也避免了javabean的缺陷。

Effective Java2读书笔记-创建和销毁对象(一)的更多相关文章

  1. Effective Java2读书笔记-创建和销毁对象(三)

    第5条:避免创建不必要的对象 本条主要讲的是一些反面教材,希望大家引以为鉴. ①无意中使用自动装箱导致多创建对象. public class Sum { public static void main ...

  2. Effective Java2读书笔记-创建和销毁对象(四)

    第7条:避免使用终结方法 这一条讲的简直是不知所云.先简单记下来其中说出的几条: ①显式终止方法的典型例子有InputStream.OutputStream和java.sql.Connection上的 ...

  3. Effective Java2读书笔记-创建和销毁对象(二)

    第3条:用私有构造器或者枚举类型强化Singleton属性 这一条,总体来说,就是讲了一个小技巧,将构造器声明为private,可以实现单例.具体有以下几种实现的方式. ①最传统的单例实现模式,可能有 ...

  4. Effective Java读书笔记--创建和销毁对象

    1.优先考虑用静态工厂方法代替构造器2.遇到多个构造器参数时要考虑使用构建器Builder解决参数过多,不可变类型.私有构造方法,静态类的构造方法提供必要参数,剩下可选.new xxx.build() ...

  5. Effective Java学习笔记--创建和销毁对象

    创建和销毁对象 一.静态工厂方法代替构造器 静态工厂方法的优缺点 优点: 1.可以自定义名称(可以将功能表述的更加清晰) 2.不必每次调用都创建新的对象(同一对象重复使用) 3.返回的类型可以是原返回 ...

  6. [Effective Java]第二章 创建和销毁对象

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  7. Effective Java2读书笔记-对于所有对象都通用的方法(二)

    第10条:始终要覆盖toString 这一条没什么好讲的,就是说默认的toString方法打印出来的是类名+@+十六进制哈希码的值.我们应该覆盖它,使它能够展示出一些更为详细清晰的信息,这个看实际情况 ...

  8. Effective Java2读书笔记-对于所有对象都通用的方法(一)

    第8条:覆盖equals时请遵守通用约定 ①约定的内容 自反性.对于任何非null的引用值x.x.equals(x)必须返回true. 对称性.对于任何非null的引用值x和y.当且仅当y.equal ...

  9. Effective Java2读书笔记-对于所有对象都通用的方法(三)

    第12条:考虑实现Comparable接口 这一条非常简单.就是说,如果类实现了Comparable接口,覆盖comparaTo方法. 就可以使用Arrays.sort(a)对数组a进行排序. 它与e ...

随机推荐

  1. [Codeforces Round #186 (Div. 2)] A. Ilya and Bank Account

    A. Ilya and Bank Account time limit per test 2 seconds memory limit per test 256 megabytes input sta ...

  2. Oracle trunc函数

    --Oracle trunc()函数的用法/**************日期********************/1.select trunc(sysdate) from dual  --2011 ...

  3. XJOI网上同步训练DAY1 T2

    思路:似曾相识?...见http://www.cnblogs.com/qzqzgfy/p/5266874.html 一看时限还是4s!,于是就开开心心地打了70%的分,就是用容斥原理,就可以n^3解决 ...

  4. 【剑指offer】面试题32:从1到n整数中1出现的次数

    题目: 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了.A ...

  5. /etc/fstab 文件详解 及 /etc/mtab

    /etc/fstab 文件解释 文件fstab包含了你的电脑上的存储设备及其文件系统的信息.它是决定一个硬盘(分区)被怎样使用或者说整合到整个系统中的唯一文件. 这个文件的全路径是/etc/fstab ...

  6. WifiDog系统

    WifiDog:A captive portal suite What is it composed of ? A: It is composed of 2 components: The clien ...

  7. setOpaque(true);设置控件不透明

    setOpaque(true);设置控件不透明setOpaque(false);设置控件透明

  8. Unity 屏幕适配小脚本

    屏幕适配是可以通过代码实现的,相信给你时间就一定能写出来. 我们公司貌似没有分辨率适配框架通常对应小屏幕的苹果4要额外设置下等等就完了! 屏幕适配框架实现思路:  通过代码获取当前的分辨率 –> ...

  9. JUnit基础及第一个单元测试实例(JUnit3.8)

    单元测试 单元测试(unit testing) ,是指对软件中的最小可测试单元进行检查和验证. 单元测试不是为了证明您是对的,而是为了证明您没有错误. 单元测试主要是用来判断程序的执行结果与自己期望的 ...

  10. (转)Apache+Tomcat集群配置

    本文Apache+Tomcat集群配置 基于最新的Apache和Tomcat,具体是2011年4月20日最新的Tomcat和Apache集群和负载均衡配置. 准备环境 Apache Apache是ht ...