第1章

如何最有效地使用Java程序设计语言机器基本类库:java.lang,java.util,java.util.concurrent和java.io。

Sun JDK1.6_05版本

第2章 创建和销毁对象

创建和销毁对象:何时以及如何创建对象,何时以及如何避免创建对象,如何确保它们能够适时地销毁,以及如何管理对象销毁之前必须进行的各种清理动作。

1. 考虑用静态工厂方法代替构造器

为了让客户端获取对象的一个实例,最常用的方法就是提供一个公共的构造器,还有另一种方法:类可以提供一个公共的静态工厂方法(static factory method),它只是一个返回类的实例的静态方法。

注意静态工厂方法与设计模式中的工程方法模式不同。

优点

  • 静态工厂方法与构造器不同的第一大优势在于,它们又名称:如果构造器的参数本身没有确切的描述正被返回的对象,那么具有适当名称的静态工厂会更容易使用,产生的客户端代码也更易于阅读。例如:BigInteger(int, int, Random)返回的BigInteger可能为素数,如果用名为BigInteger.probablePrime的静态工厂方法来表示,显得更为清楚。
  • 静态工厂方法与构造器不同的第二大优势在于,不必再每次调用它们的时候都创建一个新对象:不可变类可以使用预先创建好的实例,或则将创建好的实例缓存。
  • 静态工厂方法与构造器不同的第三大优势在于,它们可以返回原返回类型的任何子类型的对象:选择返回对象的类时就有更大的灵活性。例如Java Collections Framework的集合接口有32个便利实现,分别提供了不可修改的集合,同步集合等。几乎所有这些实现都通过静态工厂方法在不可实例化的类java.util.Collections中导出,所有返回对象的类都是非公有的。
  • 静态工厂方法的第四大优势在于,在创建参数化类型实例的时候,它们使代码变得更加简洁。

缺点

  • 类如果不含公有的或受保护的构造器,就不能被子类化。
  • 它们与其他的静态方法实际上没有任何区别。静态工厂方法的一些惯用名称:valueOf,of,getInstance,newInstance,getType,newType

总结:静态工厂更加合适,因此切忌第一反应就是提供公有的构造器,而不先考虑静态工厂。

2. 遇到多个构造器参数时要考虑用构造器

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

面对很多参数的类,如何创建类?

(1)重载构造函数

package org.github.effective.p2;

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 NutritionFacts(int servingSize, int servings) {
this(servingSize, servings, 0);
} public NutritionFacts(int servingSize, int servings, int calories) {
this(servingSize, servings, calories, 0);
} public NutritionFacts(int servingSize, int servings, int calories, int fat) {
this(servingSize, servings, calories, fat, 0);
} public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) {
this(servingSize, servings, calories, fat, sodium, 0);
} public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbonhydrate) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
this.carbohydrate = carbonhydrate;
}
}  

重载构造器模式可行,但是当有许多参数的时候,客户端代码会很难编写,并且仍然较难阅读。

(2)JavaBeans模式

调用一个无参构造器来创建对象,然后调用setter方法来设置每个必要的参数,以及每个相关的可选参数。

缺点:

因为构造过程被分到了几个调用中,在构造过程中JavaBean可能处于不一致的状态。

JavaBean模式阻止了把类做成不可变的可能。

(3)Builder模式

不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造前期得到一个builder对象,然后客户端在Builder对象上调用类似于setter的方法,来设置每个相关的可选参数,最后客户端调用无参的build方法来生成不可变的对象。这个Builder是它构建的类的静态成员类。

package org.github.effective.p2;

public class NutritionFacts2 {
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 sodium = 0;
private int carbohydrate = 0; public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
} public Builder calories(int val) {
this.calories = val;
return this;
} public Builder fat(int val) {
this.fat = val;
return this;
} public Builder carbohydrate(int val) {
this.carbohydrate = val;
return this;
} public Builder sodium(int val) {
this.sodium = val;
return this;
} public NutritionFacts2 build() {
return new NutritionFacts2(this);
} } private NutritionFacts2(Builder builder) {
this.servingSize = builder.servingSize;
this.servings = builder.servings;
this.calories = builder.calories;
this.fat = builder.fat;
this.sodium = builder.sodium;
this.carbohydrate = builder.carbohydrate;
} public static void main(String[] args) {
NutritionFacts2 cocaCola = new NutritionFacts2.Builder(240, 8).calories(100).sodium(50).carbohydrate(27).build();
} }  

总结:如果累的构造器或静态工厂中具有多个参数,这几这种类时,Builder模式就是不错的选择,特别是当大多数参数都是可选的时候。

3. 用私有构造器或枚举类型强化Singleton属性

Singleton指仅仅被实例化一次的类,通常被用来代表那些本质上唯一的系统组件。

创建单例的几种方法:

(1)

public class Elvis {
public static final Elvis INSTANCE = new Elvis()
private Elvis(){}
public void m(){......}
}  

但是享有特权的客户端可以借助AccessibleObject.setAccessible方法,通过反射机制调用私有构造器。

(2)

public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis(){}
public static Elvis getInstance(){
return INSTANCE;
} public void m() {......} }  

为了利用其中一种方法实现的Singleton类变成是可序列号的。仅仅在声明上加上implements Serializble是不够的,为了维护并保证Singleton,必须声明所有实例域都是transient的,并提供一个readResolve方法。否则每次反序列化一个序列化的实例时,都会创建一个新的实例。

(3)包含单个元素的枚举类型

public enum Elvis {
INSTANCE; public void m(){.......} }  

这种方法在功能上与公有域方法相近,但是它更加简洁,无偿提供了序列化机制,防止多次实例化,即使是在面对复杂的序列化或反射攻击的时候。虽然这种方法还没有广泛采用,但是单元素的枚举类型已经成为实现Singleton的最佳方法。

4. 通过私有构造器强化不可实例化的能力

工具类不希望别实例化,实例对它没有任何意义。

public class Utility {

 private Utility() {
throw new AssertionError()
} }  

由于显式的构造器是私有的,所有不可以在该类的外部访问它,AssertionError不是必须的,但是它可以避免不小心在类的内部调用构造器。保证该类在任何情况下都不会被实例化。

5. 避免创建不必要的对象

一般来说,最好能重用对象而不是在每次需要的时候就创建一个相同功能的新对象。如果对象是不可变的(immutable),就始终可以被重用。

6. 消除过期的对象引用

清空对象引用应该是一种例外,而不是一种规范行为。消除过期引用最好的方法是让包含该引用的变量结束其生命周期。

7. 避免使用终结方法

finalizer方法通常是不可预测的,也是很危险的,一般情况下是不必要的。

x.参考文档

《Effective Java中文版 第2版》

Effective Java-第二章的更多相关文章

  1. 如何创建和销毁对象(Effective Java 第二章)

    最近有在看Effective Java,特此记录下自己所体会到的东西,写篇博文会更加的加深印象,如有理解有误的地方,希望不吝赐教. 这章主题主要是介绍:何时以及如何创建对象,何时以及如何避免创建对象, ...

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

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

  3. 对于所有对象都通用方法的解读(Effective Java 第二章)

    这篇博文主要介绍覆盖Object中的方法要注意的事项以及Comparable.compareTo()方法. 一.谨慎覆盖equals()方法 其实平时很少要用到覆盖equals方法的情况,没有什么特殊 ...

  4. “全栈2019”Java第二章:安装JDK11(Windows)

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 文章原文链接 "全栈2019"Java第二章:安装JDK11(Windows) 下一 ...

  5. Effective Java 第二版 Enum

    /** * Effective Java 第二版 * 第30条:用enum代替int常量 */ import java.util.HashMap;import java.util.Map; publi ...

  6. Java 第二章 变量

    第二章 变量 变量称为:是计算机语言中能储存计算机结果或能表示值抽象概念 .变量可以通过变量名访问 int money ; //变量 money=1000; //赋值 int money=1000: ...

  7. Java第二章----对象和类

    从第一章到第二章整整隔了一个月的时间,这速度也是慢的无语了.因为这个月负责开发公司一个SaaS类型APP,忙的昏天暗地终于上线了,这才有时间写个博客.本章还是以概念为主,有点枯燥重在理解. 第一节:对 ...

  8. Java 第二章 变量、数据类型和运算符

    第二章      变量.数据类型和运算符 什么是变量: 变量代表一块内存区域,变量类型不一样,这一块内存的大小也不一样. #在编程语言里面,你可以通过定义变量,向内存里添加数据或者修改内存已有的数据. ...

  9. 《Effective Java 第二版》读书笔记

    想成为更优秀,更高效程序员,请阅读此书.总计78个条目,每个对应一个规则. 第二章 创建和销毁对象 一,考虑用静态工厂方法代替构造器 二, 遇到多个构造器参数时要考虑用builder模式 /** * ...

  10. Effective java第一章引言

    菜鸟一枚,开始读第一本书<Effective Java>(第二版)~ 看引言就有好多名词不懂(>_<) 导出的API由所有可在定义该API的包之外访问的API元素组成.一个包的 ...

随机推荐

  1. GCC,LLVM,Clang编译器对比

    http://www.cnblogs.com/qoakzmxncb/archive/2013/04/18/3029105.html   在XCode中,我们经常会看到这些编译选项(如下图),有些人可能 ...

  2. 那些年我们踩过的坑-NSTimer

    昨天下午工作的时候遇见一个这样的需求,网络请求失败后把请求数据保存到本地,并自动重发3次,时间间隔是10秒,如果3次后还失败的话,下一次启动这个接口的时候,把新数据和保存在本地的数据都要发送,刚开始以 ...

  3. appium+python自动化44-appium命令行模式

    前言 appium desktop有个客户端版本,每次运行脚本的时候都要去双击启动才能运行,很显然不太方便,影响效率.那么有没什么办法不启动桌面程序就能运行呢,比如cmd命令行执行? 环境: appi ...

  4. java 从网络Url中下载文件

    转自:http://blog.csdn.net/xb12369/article/details/40543649 /** * 从网络Url中下载文件 * @param urlStr * @param ...

  5. Oracle判断两个时间段是否相交

    SQL中常常要判断两个时间段是否相交,该如何判断呢?比如两个时间段(S1,E1)和(S2,E2).我最先想到的是下面的方法一.方法一:(S1 BETWEEN S2 AND E2) OR (S2 BET ...

  6. Razor语法(五)

    约定:客户端代码称C域,服务器端代码称S域 0. 基本原则Razor模板默认是C域(与php.aspx相同)任何C域都可以内嵌S域行内S域不可内嵌C域,多行S域可内嵌任何C域@符号是关键符号,使用@从 ...

  7. 手写Json转换

    在做项目的时候总是要手动将集合转换成json每次都很麻烦,于是就尝试着写了一个公用的方法,用于转换List to json: using System; using System.Collection ...

  8. 云计算之路-阿里云上:SLB故障引发的网站不能正常访问

    2013年8月22日23:50~23:58左右,由于阿里云SLB(负载均衡)故障造成网站不能正常访问,给大家带来了麻烦,望大家谅解! 8月19日我们收到阿里云的短信通知: 尊敬的阿里云用户:      ...

  9. Android异步载入全解析之大图处理

    Android异步载入全解析之大图处理 异步载入中很重要的一部分就是对图像的处理,这也是我们前面用异步载入图像做示例的原因. 一方面是由于图像处理不好的话会很占内存,并且easyOOM,还有一方面,图 ...

  10. Maven版本的ssm框架项目常见依赖pom.xml

    <properties> <junit.version>4.12</junit.version> <spring.version>4.3.1.RELEA ...