《一》考虑用静态工厂方法代替构造器

下面是Boolean类的一个简单示例:

  1. public final class Boolean implements java.io.Serializable,
  2. Comparable<Boolean> {
  3.  
  4. public static final Boolean TRUE = new Boolean(true);
  5. public static final Boolean FALSE = new Boolean(false);
  6.  
  7. public static Boolean valueOf(boolean b) {
  8. return (b ? TRUE : FALSE);
  9. }
  10. }

Why ?平时我们用共有的构造器不爽吗?

1.静态工厂方法与构造器不同的第一大优势在于,它们有名称!!!名称对于程序多重要,毋庸置疑了,如果你没这样感觉,那就什么都别说,多敲代码就知道了。

  1. public class BigInteger extends Number implements Comparable<BigInteger> {
  2. /**
  3. * Returns a positive BigInteger that is probably prime, with the
  4. * specified bitLength. The probability that a BigInteger returned
  5. * by this method is composite does not exceed 2<sup>-100</sup>.
  6. */
  7.  
  8. public static BigInteger probablePrime(int bitLength, Random rnd) {
  9. }
  10. }

2.静态工厂方法与构造器不同的第二大优势在于,不必在每次调用它们的时候创建一个新的对象。比如不可变类可以使用预先构建好的实例,或者将构建好的实例缓存起来,进行重复利用,从而达到提升性能的效果。

3.静态工厂方法与构造器不同的第三大优势在于,它们可以返回原返回类型的任何子类型的对象。这样在选择返回对象上就有了更大的灵活性。

  1. public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
  2. implements Cloneable, java.io.Serializable {
  3.  
  4. EnumSet(Class<E>elementType, Enum[] universe) {
  5. }
  6. //RegularEnumSet与JumboEnumSet均为EnumSet的子类
  7. public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
  8. if (universe.length <= 64)
  9. return new RegularEnumSet<>(elementType, universe);
  10. else
  11. return new JumboEnumSet<>(elementType, universe);
  12. }
  13. }

4.静态工厂方法的第四大优势在于,在创建参数化类型实例的时候,它们使代码变得更加简洁。

  1. //常规实例化方式
  2. Map<String, List<String>> m =
  3. new HashMap<String, List<String>>();
  4.  
  5. public static <K, V> HashMap<K, V> newInstance() {
  6. return new HashMap<K, V>();
  7. }
  8. //使用静态工厂方法实例化,简化的声明
  9. Map<String, List<String>> m = HashMap.newInstance();

Not? 利弊总是同时存在,没有尽善尽美的东西

1.静态工厂的方法的主要缺点在于,类如果不含公有的或者受保护的构造器,就不能子类化。

如果我们在类中将构造函数设为private,只提供静态工厂方法来构建对象,那么我们将不能通过继承扩展该类。 
但是这也会鼓励我们使用复合而不是继承来扩展类。

2.静态工厂方法的第二缺点在于,它们与其他静态方法实际没有任何区别。

在API文档中,构建对象的静态工厂方法并没有像构造器那样明确标识出来,不能和其他静态方法很方便地区分开来。 
可以通过遵循静态工厂方法的命名规范来弥补这一劣势:

  • valueOf - 不太严格的讲,该方法返回的实例与它的参数具有相同的值,一般作为类型转换使用
  • of - valueOf的更为简洁的替代。
  • getInstance - 返回的实例通过方法的参数来描述,但不能说与参数具有同样的值。对于Singleton来说,使用无参getInstance,返回唯一的实例。
  • newInstance - 像getInstance一样,但其能够确保每次都返回新的对象。
  • getType - 像getInstance一样,但此方法返回的对象是另一个不同的类。
  • newType - 像getType一样,但每次返回一个新对象。

总而言之,静态工厂方法和公有构造器具有各自的用处,但静态工厂方法通常更加合适,因此切记第一反应就是提供公有的构造器,而先不考虑静态工厂方法。

《二》遇到多个构造器参数时候要考虑构建器

遇到多个构造器参数,一般采用的是重叠构造器模式,或者JavaBeans模式(线程不安全)

这里要说的是第三种,既能像重叠构造器模式那样安全性,也能保证像JavaBeans模式那么好的可读性,这就是Builder模式,构建器模式。

eg:

  1. // Builder Pattern - Pages 14-15
  2.  
  3. public class NutritionFacts {
  4. private final int servingSize;
  5. private final int servings;
  6. private final int calories;
  7. private final int fat;
  8. private final int sodium;
  9. private final int carbohydrate;
  10.  
  11. public static class Builder {
  12. // Required parameters
  13. private final int servingSize;
  14. private final int servings;
  15.  
  16. // Optional parameters - initialized to default values
  17. private int calories = 0;
  18. private int fat = 0;
  19. private int carbohydrate = 0;
  20. private int sodium = 0;
  21.  
  22. public Builder(int servingSize, int servings) {
  23. this.servingSize = servingSize;
  24. this.servings = servings;
  25. }
  26.  
  27. public Builder calories(int val)
  28. { calories = val; return this; }
  29. public Builder fat(int val)
  30. { fat = val; return this; }
  31. public Builder carbohydrate(int val)
  32. { carbohydrate = val; return this; }
  33. public Builder sodium(int val)
  34. { sodium = val; return this; }
  35.  
  36. public NutritionFacts build() {
  37. return new NutritionFacts(this);
  38. }
  39. }
  40.  
  41. private NutritionFacts(Builder builder) {
  42. servingSize = builder.servingSize;
  43. servings = builder.servings;
  44. calories = builder.calories;
  45. fat = builder.fat;
  46. sodium = builder.sodium;
  47. carbohydrate = builder.carbohydrate;
  48. }
  49.  
  50. public static void main(String[] args) {
  51. NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).
  52. calories(100).sodium(35).carbohydrate(27).build();
  53. }
  54. }

Java传统抽象工厂实现是class对象,用newInstance方法充当build方法的一部分,这种用法隐含许多问题,因为newInstance总是企图调用类的无参构造,这个构造可能根本不存在,但是也不会收到编译时期错误,相反,必须在运行时处理,换句话说,Class.newInstance破坏了编译时期的异常检查。

Builder模式不足之处,为了创建对象必须先创建它的构造器,虽然创建构造器的开销,实际中可以忽略,注重性能的情况除外,同时他比其他模式更加冗余。

因此,只有在参数比较多的时候使用它才比较合理(一般参数4个+),切记,一开始使用重叠构造或者JavaBeans,中间再切换构造器,这样会导致代码不协调,很乱,最好计划一开始就用。简而言之,如果类的构造器或者静态工厂中具有多个参数,设计这种类时候,Builder是不错选择,特别当大多数参数都是可选的,这样会使代码更加容易阅读和编写,同时也比JavaBeans更安全。

Chapter 01:创建和销毁对象的更多相关文章

  1. Effective Java笔记一 创建和销毁对象

    Effective Java笔记一 创建和销毁对象 第1条 考虑用静态工厂方法代替构造器 第2条 遇到多个构造器参数时要考虑用构建器 第3条 用私有构造器或者枚举类型强化Singleton属性 第4条 ...

  2. 《Effective Java》—— 创建与销毁对象

    本篇主要总结的是<Effecticve Java>中关于创建和销毁对象的内容. 比如: 何时以及如何创建对象 何时以及如何避免创建对象 如何确保及时销毁 如何管理对象销毁前的清理动作 考虑 ...

  3. ch2 创建和销毁对象

    ch2 创建和销毁对象              

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

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

  5. 《Effect Java》学习笔记1———创建和销毁对象

    第二章 创建和销毁对象 1.考虑用静态工厂方法代替构造器 四大优势: i. 有名称 ii. 不必在每次调用它们的时候都创建一个新的对象:   iii. 可以返回原返回类型的任何子类型的对象: JDBC ...

  6. effective java读书小记(一)创建和销毁对象

    序言 <effective java>可谓是java学习者心中的一本绝对不能不拜读的好书,她对于目标读者(有一点编程基础和开发经验)的人来说,由浅入深,言简意赅.每一章节都分为若干的条目, ...

  7. 和我一起学Effective Java之创建和销毁对象

    前言 主要学习创建和销毁对象: 1.何时以及如何创建对象 2.何时以及如何避免创建对象 3.如何确保它们能够适时地销毁 4.如何管理对象销毁之前必须进行的清理动作 正文 一.用静态工厂方法代替构造器 ...

  8. Effective Java(1)-创建和销毁对象

    Effective Java(1)-创建和销毁对象

  9. 《Effective Java 2nd》第2章 创建和销毁对象

    目录 第1条:考虑使用静态工厂方法代替构造器 第2条:遇到多个构造器参数时考虑用构建器 第3条:用私有构造器或者枚举类型强化Singleton属性 第4条:通过私有构造器强化不可实例化的能力 第5条: ...

  10. 《Effective Java》读书笔记(一)之创建和销毁对象

    最近在研读<Effective Java>一书,读书不做点笔记,感觉很容易就忘掉,于是用本篇博客来记录阅读此书的笔记. 郑重声明: 由于是<Effective Java>一书的 ...

随机推荐

  1. 每天一点css3聚沙成塔(一):transition

    transition 语法: transition:[ transition-property ] || [ transition-duration ] || [ transition-timing- ...

  2. adb shell am pm

    adb shell am instrument [options] <COMPONENT> 作用:启动对instrument实例的监视. 参数[options]: -e <key&g ...

  3. PHP中__autoload()的不解之处,求高手指点

    一整段代码: 运行结果: 使用__autoload(),分为两页代码: 第一段代码: ACMEManager.php,代码如下: 运行结果:

  4. php get_ini 和 get_cfg_var 的区别

    get_ini 和 get_cfg_var 都是用来获取 php 配置信息的函数. 区别是 get_ini 是用来获取当前运行的配置信息,get_cfg_var 是用来获取配置文件(php.ini)的 ...

  5. C语言基础10

    栈区间:在函数内部声明的变量都存放在栈区间,比如int char 数组 结构体 指针,只管申请,系统会自动帮我们回收,收回的时间是作用域结束之后,遵循的原则是"先进后出". int ...

  6. Linux服务器配置WEB应用日志文件到指定目录

    在Linux服务器上配置WEB应用程序日志到指定文件   服务器环境是 RedHat Linux, 其上运行的是 Apache + Tomcat,容器中运行的是我们公司的壹个小型电子商务网站,原来项目 ...

  7. IE6 js修改img的src属性问题

    今天在做项目,有个点击按钮切换图片功能,即修改img的src属性,在IE6下测试,切换图片不显示,右键选择显示图片,可以显示出来,琢磨了很久,最终发现是因为该按钮是a标签导致的, 随后上网查了下,有些 ...

  8. ArcGIS API for Silverlight中专题地图的实现浅析

    原文http://www.gisall.com/html/32/7232-2418.html 专题地图是突出表现特定主题或者属性的地图.常见专题地图类型有唯一值渲染,分类渲染,柱状图,饼状图,点密度图 ...

  9. C#/vbscript/JS如何加密保护HTML/javascript源代码

    原文地址:http://www.coding123.net/article/20121008/encrypt-javascript-by-charp-vbscript.aspx 本文通过将源代码进行u ...

  10. Linux进程通信----匿名管道

    Linux进程通信中最为简单的方式是匿名管道 匿名管道的创建需要用到pipe函数,pipe函数参数为一个数组表示的文件描述字.这个数组有两个文件描 述字,第一个是用于读数据的文件描述符第二个是用于写数 ...