1 创建和销毁对象

Item 1:考虑用静态工厂方法取代构造器

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

静态工厂方法的优点:

  1. 有名字,因此可以直接看出来它的用法,如 Boolean.valueOf(bool)
  2. 不要每次创建新对象
  3. 可以返回方法返回类型的子类
  4. 可以根据输入参数,返回不同的类型,比如 EnumSet.of(...) 方法,根据元素长度,返回 RegularEnumSet 或者 JumboEnumSet
  5. 方法返回对象的类,在写此方法时,可以不存在。典型的例子是,JDBC

静态工厂方法的缺点:

  1. 只提供静态工厂方法的类,如果没有 public 或 protected 的构造器,不能有子类
  2. 由于静态工厂方法在 API 文档中没有被特别标注(像构造器那样),程序员难以找到它们

Item 2:当构造参数很多时,考虑使用 builder

当构造时的参数不超过 3 个时,通常使用 Telescoping constructor pattern 或者 JavaBean Pattern

// Telescoping constructor pattern
public HashMap();
public HashMap(int initialCapacity)
public HashMap(int initialCapacity, float loadFactor)

Telescoping constructor pattern 的缺点是,当参数多于 3 个时,难以书写和阅读。

// JavaBean Pattern
public class User {private String name;private String password;public void setName(String name);public void setPassword(String password);
}

JavaBean Pattern 的缺点是无法实现类的 immutable,因为每个使用对象的客户都可以用 set 方法修改对象的属性。

当构造器或者静态工厂方法含有超过(或者将来可能超过) 3 个的参数时,推荐使用 buider。

// 支持类继承拓展的 builder pattern
public abstract class Pizza {public enum Topping {HAM, MUSHROOM, ONION, PEPPER, SAUSAGE}final Set<Topping> toppings;abstract static class Builder<T extends Builder<T>> {EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);public T addTopping(Topping topping) {topping.add(Objects.requireNonNull(topping));return self();}abstract Pizza build();// Subclasses must override this method to return "this"protected abstract T self();}Pizza(Builder<?> builder) {toppings = builder.toppings.clone();}
}

Item 3:使用私有构造器或者枚举类型来加强单例属性

// public final 字段的单例
public class Elvis {public static final Elvis INSTANCE = new Elvis();private Elvis() {...}public void leaveTheBuilding() {...}
}

public final 字段实现单例的优点是:

  1. 显然是个单例模式
  2. 简单
// 静态工厂的单例
public class Elvis {private static final Elvis INSTANCE = new Elvis();private Elvis() {...}public static Elvis getInstance() { return INSTANCE; }public void leaveTheBuilding() {...}
}

静态工厂实现单例的优点:

  1. 灵活性,比如每个线程一个单例
  2. 可以实现泛型单例工厂
  3. 方法的引用可以作为 supplier,如 Elvis::instance 是一个 Supplier<Elvis>
// 枚举实现的单例 -- 推荐方式
public enum Elvis {INSTANCE;public void leaveTheBuilding() {...}
}

枚举类型实现单例可以保证绝对的安全:

  1. 保证单例,即使是面对复杂的序列化或反射攻击(前两个方法做不到)
  2. 免费提供序列化机制

Item 4:使用私有构造器来加强不可实例化

抽象类不能保证不可实例化性(noninstantiability),其子类可以实例化,而且它会误导使用者认为此类被设计用来继承。

可行的方案是私有化构造器:

// Noninstantiable utility class
public class UtilityClass {// Suppress default constructor for noninstantiabilityprivate UtilityClass() {throw new AssertionErrot(); // 防止反射实例化}...
}

Item 5:使用依赖注入取代硬编码的依赖实例化

硬编码的依赖实例化是意译,原文是 hardwiring resources。

// 静态工具模式(不建议,不灵活且不可测试)
public class SpellChecker {private static final Lexicon dictionary = ...;private SpellChecker() {} // Noninstantiablepublic static boolean isValid(String word) {...}public static List<String> suggestions(String typo) {...}
}// 单例模式(不建议,不灵活且不可测试)
public class SpellChecker {private final Lexicon dictionary = ...;private SpellChecker() {}public static INSTANCE = new SpellChecker(...);public static boolean isValid(String word) {...}public static List<String> suggestions(String typo) {...}
}

在实现一个类时,如果它依赖了其他资源且该资源的类型会影响此类,那么不要使用单例模式或者静态工具模式,也不要让它硬编码创建依赖实例。推荐的做法是,将依赖对象或依赖对象的工厂作为参数,传递给构造器、静态工厂方法或 builder,这就是依赖注入,它大大增强了类的灵活性、复用性和可测试性

// 依赖注入,具备了灵活性和可测试性
public class SpellChecker {private final Lexicon dictionary;private SpellChecker(Lexicon dictionary) {this.dictionary = dictionary;}public static boolean isValid(String word) {...}public static List<String> suggestions(String typo) {...}
}

对于大项目,通常包含成千上万的依赖,使用依赖注入会使得项目复杂化,但是可以通过使用优秀的依赖注入框架来解决这个问题,比如 Dagger、Guice 或 Sping。

Item 6:避免创建不必要的对象

创建了不必要的对象的例子:

  • String s = new String("bikini"); 中创建了两次 String 对象。
  • String.matches(...) 方法中,新建了 Pattern 对象来匹配,如果多次调用此方法,会多次创建 Pattern 对象。
  • Long sum = 0L; sum += 1; 原始类型的自动装箱也会创建不必要的对象,因此尽量使用原始类型替代其包装类。

但是,有些时候应该创建重复对象(Item 50)。因为没有做好必要的防御性拷贝可能带来可怕的 bugs 和安全漏洞,而创建了不必要的对象仅仅会影响代码风格和性能。

Item 7:消除过时的对象引用

// ArrayList
public E remove(int index) {rangeCheck(index);modCount++;E oldValue = elementData(index);int numMoved = size - index - 1;if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index,numMoved);elementData[--size] = null; // clear to let GC do its workreturn oldValue;
}

内存泄漏的来源:

  1. 如 ArrayList 中用到的对象数组,对于这类自己管理内存的类,程序员要对内存泄漏保持警惕
  2. 缓存。常常使用 LinkedHashMap 在内存紧张时回收最近没有访问的对象
  3. 监听器和回调。可以使用 WeakHashMap 保存 callback 的弱引用

Item 8:不要使用 finalizer 和 cleaner

filnalizer 的行为不可预测,危险,通常都不需要。cleaner 不如 finalizer 那么危险,但是仍然不可预测,缓慢,通常也不需要。最好的建议就是不要使用它们。

Item 9:用 try-with-resources 取代 try-finally

try-with-resources 语句块简洁又周到,是 Java 7 以来最优的关闭资源的方式,能使用它的场合就使用它,否则再考虑 try-finally。

举个例子:

   BufferedReader br = new BufferdReader(new FileReader(path));try {return br.readLine();} finally {if (c != null) {c.close();}}

如果物理设备出现故障无法访问,readLine() 会抛出异常,在 finally 代码块中 close() 也会抛出异常, 第二个异常会泯灭第一个异常,使得用户看不到自己真正关注的异常。

try (BufferedReader br = new BufferdReader(new FileReader(path))) {return br.readLine();
}

使用 try-with-resources 代码块后,两个异常都会保留,且 close() (隐式自动关闭)的异常隐含在 readLine() 的异常中,因此保留了使用者真正关心的异常,同时被隐含的异常并没有被丢弃,可以在 stack trace print 中看到,还可以用 getSuppressed() 方法获取到 Throwable 对象。

Effective Java (3rd Editin) 读书笔记:1 创建和销毁对象相关推荐

  1. Effective Java (3rd Editin) 读书笔记:3 类和接口

    3 类和接口 Item 15:最小化类和成员的访问权限 一个设计优秀的类应该隐藏它的所有实现细节,将它的 API 和内部实现干净地分离开.这种软件设计的基本准则被称为"封装"(en ...

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

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

  3. Effective Java读书笔记---二、创建和销毁对象

    二.创建和销毁对象 何时以及如何创建对象, 何时以及如何避免创建对象, 如何确保它们能够适时地销毁, 如何管理对象销毁之前必须进行的各种清理动作 1.用静态工厂方法代替构造器 优势: 它们有名称 不必 ...

  4. 《Effective Java》学习笔记 第二章 创建和销毁对象

    第二章 创建和销毁对象 何时以及如何创建对象,何时以及如何避免创建对象,如何确保他们能够适时地销毁,以及如何管理对象销毁之前必须进行的各种清理动作. 1 考虑用静态工厂方法代替构造器 一般在某处获取一 ...

  5. Effective Java:创建和销毁对象

    前言: 读这本书第1条规则的时候就感觉到这是一本很好的书,可以把我们的Java功底提升一个档次,我还是比较推荐的.本博客是针对<Effective Java>这本书第2章所写的一篇读书笔记 ...

  6. JAVA编程思想读书笔记(三)--RTTI

    接上篇JAVA编程思想读书笔记(二) 第十一章 运行期类型判定 No1: 对于作为程序一部分的每个类,它们都有一个Class对象.换言之,每次写一个新类时,同时也会创建一个Class对象(更恰当的说, ...

  7. Java核心技术卷一读书笔记

    文章目录 Java核心技术卷一读书笔记 第一章 Java程序设计概述 1.1 关键特性 第二章 Java程序设计环境 2.1 使用命令行工具 第三章 Java的基本查询设计结构 3.1 数据类型 3. ...

  8. 《Head First Java》20201023读书笔记

    P413-P <Head First Java>20201023读书笔记 操作Swing组件 几个常用组件:text field.可滚动的text area.checkbox以及list. ...

  9. Spring读书笔记——bean创建(下)

    有关Spring加载bean系列,今天这是最后一篇了,主要接上篇对于从Spring容器中获取Bean的一些细节实现的补充. <Spring读书笔记--bean加载>--Spring如何加载 ...

最新文章

  1. zabbis监控mysql数据库
  2. Hive客户端secureCRT中文显示设置
  3. 脚本:获取CSDN文章的访问量
  4. linux下dup函数,Linux dup dup2函数理解
  5. 亚信安全协助绿谷制药确保“秘方”安全
  6. java 随机手机验证码_基于Java随机生成手机短信验证码的实例代码|chu
  7. 又见程序媛 | 从索引的创建角度分析热门“面试题”
  8. mySQL 分组查询,根据分组的字段,取最小值
  9. TensorFlow安装教程(Windows/Linux两种版本)
  10. MOSS 2010:Visual Studio 2010开发体验(32)——工作流开发最佳实践(四):可重用工作流...
  11. 变频器22b系列说明书_变频器接电位器正确接法
  12. 阿克曼函数实现(Java代码)
  13. 知到计算机应用基础见面课答案,知到计算机应用基础(湖南环境生物职业技术学院)见面课答案...
  14. jQWidgets的TreeGrid 心得:
  15. 金戒指用计算机怎么算,大姐拿来一个金戒指,先卖后当有蹊跷,鉴定后发现有问题,假的...
  16. Proftpd配置文件
  17. 服务器上显示存储脱机,服务器硬盘脱机状态
  18. 浏览器开发者工具菜鸡相谈
  19. 树莓派安装Linux操作系统
  20. 一图看懂《百年孤独》人物关系

热门文章

  1. app运行时签名校验
  2. java求两个数相加代码
  3. 成人计算机考试操作题视频教程,成人计算机考试操作题模拟.doc
  4. win10双击jdk8安装包没反应?看这里给你解决
  5. Ubuntu上,tftpd-hpa 启动失败的解决方法.
  6. 小学计算机兴趣班培训总结,小学开展电脑制作活动工作总结
  7. 查询每个部门工资最低的两个员工的编号,姓名,工资
  8. 电脑桌面计算机总是自动打开,电脑开机后自动打开很多软件怎么办
  9. 自媒体5大免费网站,帮助你高效运营,快收藏起来
  10. 最新520表白HTML源码+实现3D动态相册