最近有在看Effective Java,特此记录下自己所体会到的东西,写篇博文会更加的加深印象,如有理解有误的地方,希望不吝赐教。
这章主题主要是介绍:何时以及如何创建对象,何时以及如何避免创建对象,如何确保他们能够适时的销毁,以及如何管理对象销毁之前必须进行的清理动作。下面我会根据书中内容+例子总结:
一、考虑用静态工厂方法代替构造器(体现了如何创建对象、避免创建对象)
如何获取一个类的实例,最常用的方法就是提供一个共有的构造器, 但是还有一种方法就是使用静态工厂方法(与设计模式中的工厂方法不同)。静态工厂方法只是一个静态的,用来返回当前类型或子类型的实例的方法。
先来说说静态工厂方法与构造器不同的优势,然后我们在根据例子来体现:
优势:1、静态工厂方法可以有自己的名称。这样更容易理解当前方法所要表达的意思。
以随机生成数类为案例,看第二个构造方法(构造一个指定最小值到Integer.MAX_VALUE的区间)和第三个构造方法(构造一个Integer.MIN_VALUE到指定max的区间),两者表达的意思完全不同,且存在是有意义的, 但是如果这两者同时存在将会出现编译错误:Duplicate method RandomIntGenerate(int) in type RandomIntGenerate;问题原因是出现了两个签名相同的(返回值、名称、参数类型及个数均相同)方法。
1 /**
2 * @Description: 随机生成数 以构造器方法为例
3 * @author yuanfy
4 * @date 2017年7月7日 上午9:26:04
5 */
6 public class RandomIntGenerate {
7 //定义最小值
8 private int min;
9 //定义最大值
10 private int max;
11 /**
12 * 使用构造器生成指定区间的随机数
13 * @param min
14 * @param max
15 */
16 public RandomIntGenerate(int min, int max) {
17 this.min = min;
18 this.max = max;
19 }
20 /**
21 * 指定最小值到Integer.maxValue区间的构造器
22 * @param min
23 */
24 public RandomIntGenerate(int min) {
25 this.min = min;
26 this.max = Integer.MAX_VALUE;
27 }
28
29 /**
30 * 指定最大值到Integer.minValue区间的构造器 如果添加了这个方法,将会引起第二个和第三个构造方法的编译错误。
31 * @param min
32 */
33 public RandomIntGenerate(int max) {
34 this.min = Integer.MIN_VALUE;
35 this.max = max;
36 }
37 }
通过上面看出用构造方法时候会造成方法歧义,下面我们通过静态工厂方法来创建对象实例就会清楚很多。
/**
* @Description: 随机生成数 以静态工厂方法为例
* @author yuanfy
* @date 2017年7月7日 上午9:26:04
*/
public class RandomIntGenerate {
//定义最小值
private int min;
//定义最大值
private int max;
/**
* 使用构造器生成指定区间的随机数 (不对外开放)
* @param min
* @param max
*/
private RandomIntGenerate(int min, int max) {
this.min = min;
this.max = max;
}
/**
* @Description: 使用静态工厂方法 创建指定两端边界值的构造器
* @param min 最小值
* @param max 最大值
* @return RandomIntGenerate对象
* @author yuanfy
* @date 2017年7月7日 上午9:56:37
*/
public static RandomIntGenerate between(int min, int max) {
return new RandomIntGenerate(min, max);
}
/**
* @Description: 使用静态工厂方法 创建大于指定最小值边界的构造器
* @param min 最小值
* @return RandomIntGenerate对象
* @author yuanfy
* @date 2017年7月7日 上午9:56:37
*/
public static RandomIntGenerate biggerThanMin(int min) {
return new RandomIntGenerate(min, Integer.MAX_VALUE);
}
/**
* @Description: 使用静态工厂方法 创建小于最大值边界的构造器
* @param max 最大值
* @return RandomIntGenerate对象
* @author yuanfy
* @date 2017年7月7日 上午9:56:37
*/
public static RandomIntGenerate smallerThanMax(int max) {
return new RandomIntGenerate(Integer.MIN_VALUE, max);
}
}
使用场景:当一个类需要多个带有相同签名的构造器时,就可以用静态工厂方法代替构造器。
2、不必每次调用他们的时候都创建一个新对象。例如Boolean.valueOf()方法。下面是截取java.lang.Boolean类源码中的代码,这个静态方法返回均为不可变Boolean对象TRUE或者FALSE。
1 public final class Boolean implements java.io.Serializable,
2 Comparable<Boolean>
3 {
4 /**
5 * The {@code Boolean} object corresponding to the primitive
6 * value {@code true}.
7 */
8 public static final Boolean TRUE = new Boolean(true);
9
10 /**
11 * The {@code Boolean} object corresponding to the primitive
12 * value {@code false}.
13 */
14 public static final Boolean FALSE = new Boolean(false);
15
16 public static Boolean valueOf(boolean b) {
17 return (b ? TRUE : FALSE);
18 }
19 }
除了这个例子,还有我们常见的单例模式(饿汉式)。
1 public class SingletonModeDemo {
2
3 private static SingletonModeDemo demo = new SingletonModeDemo();
4
5 /**
6 * @Description: 单例模式(饿汉式)
7 * @author yuanfy
8 * @date 2017年7月7日 上午10:43:47
9 */
10 public static SingletonModeDemo newInstance(){
11 return demo;
12 }
13 }
3、可以返回原返回类型的任何子类型对象。其实这就跟多态有关了,下面以动物类为例。
//动物类
class Animal{
//动物都有名字
private String name;
public Animal(String name) {
this.name = name;
}
}
//鸟类
class Bird extends Animal {
public Bird(String name) {
super(name);
}
}
//大小类
class Elephant extends Animal {
public Elephant(String name) {
super(name);
}
}
//动物集合类
public class Animals {
private Animals(){}
public static Animal getDog(String name) {
return new Bird(name);//事实证明可以返回动物类的任何子类
}
public static Animal getPig(String name) {
return new Elephant(name);//事实证明可以返回动物类的任何子类
}
}
4、在创建参数化类型实例的时候,它们使代码变得更加简单。以书中举的HashMap为例。
创建HashMap实例时,不管参数有多少都必须指明,这就增加了代码量了。如下代码:
Map<String, List<String>> map = new HashMap<String, List<String>>();
如果使用静态工厂方法创建实例则会简化很多。如下代码:
1 public class HashMapUtil {
2
3 /**
4 * @Description: 使用静态工厂简化参数
5 * @author yuanfy
6 * @date 2017年7月7日 上午11:14:34
7 */
8 public static <K, V> HashMap<K, V> newInstance(){
9 return new HashMap<K, V>();
10 }
11
12 public static void main(String[] args) {
13 Map<String, List<String>> map = HashMapUtil.newInstance();
14 }
15 }
劣势:使用静态方法的类如果不含有共有的或受保护的构造器,就不能被子类化。
第二个就是与其他的静态方法没什么区别。所以在静态方法命名上要注意使用,通常用惯用名称。
- valueOf - 返回的实例与它的参数具有相同的值,一般作为类型转换使用,例如
Boolean.valueOf(boolean)
- of - valueOf的更为简洁的替代。
- getInstance - 返回的实例通过方法的参数来描述,但不能说与参数具有同样的值。对于Singleton来说,使用无参getInstance,返回唯一的实例。
- newInstance - 像getInstance一样,但其能够确保每次都返回新的对象。
- getType - 像getInstance一样,但是在工厂方法处于不同的类中的时候使用,返回对象类型
- newType - 像newInstance 一样,但是在工厂方法处于不同的类中的时候使用,返回对象类型
二、遇到多个构造器参数时要考虑用构建器(体现了如何创建对象)
书中NutritionFacts例子已经说明的很详细了,请参考书中例子。
三、用私有构造器或者枚举类型强化Singleton属性
第一种使用私有构造器来强化Singleton属性,就是单例模式中的饿汉式是典型的例子;
第二种用枚举类型来强化Singleton属性,确实实用。其中书中说道“单元素的枚举类型已经成为实现Singleton的最佳方法”
1 /**
2 * @Description: 用枚举来实现单例模式
3 * @author yuanfy
4 * @date 2017年7月7日 下午2:30:16
5 */
6 public enum EnumDemo {
7 INSTANCE;//调用这个就已经调用了EnumDemo()构造方法。
8 private Date instance;
9
10 private EnumDemo() {
11 instance = new Date();
12 }
13
14 public Date getInstance(){
15 return instance;
16 }
17
18 public static void main(String[] args) {
19 System.out.println(EnumDemo.INSTANCE.getInstance());
20 }
21 }
四、通过私有构造器强化不可实例化的能力。
从上面那个单例模式的例子来看是严谨的(在缺少显示构造器的情况下,编译器会自动提供一个共有的、无参的缺省构造器),因为没有私有化构造器,所以直接new SingletonModeDemo()就可以创建实例对象了。 强化后的代码:
1 public class SingletonModeDemo {
2
3 private static SingletonModeDemo demo = new SingletonModeDemo();
4
5 /**
6 * 私有化构造器,使其获取实例的入口只有newInstance()方法
7 */
8 private SingletonModeDemo(){}
9
10 /**
11 * @Description: 单例模式(饿汉式)
12 * @author yuanfy
13 * @date 2017年7月7日 上午10:43:47
14 */
15 public static SingletonModeDemo newInstance(){
16 return demo;
17 }
18 }
五、避免创建不必要的对象
主要思想是:最好能重用对象而不是在每次需要的时候就创建一个相同功能的新对象。如果对象是不可变的,它始终可以被重用。
前面几点也介绍了相关的案例,如使用静态工厂方法可以适当表面创建不必要的对象。单例模式也是一样。所以在编程的时候多多注意下就行,写出好的代码这方面也是一种体现。
六、消除过期的对象引用
七、避免使用终结方法
后面两点没有什么更深的理解,所以还得多看几遍书。