Effective Java读书笔记(三)
创建和销毁对象
1.何时以及如何创建对象?
2.何时以及如何避免创建对象?
3.如何确保它们能够适时地销毁?
4.如何管理对象销毁之前必须进行的各种清理动作?
用私用构造器或者枚举类型强化Singleton属性
Singleton是指仅仅被实例化一次的类。
Singleton通常被用来代表一个无状态的对象,如函数,或者那些本质上唯一的系统组件。使类成文Singleton会使它的客户端测试变得十分困难,因为不可能给Singleton替换模拟实现,除非实现一个充当其类型的接口。
实现方法:
1.公有静态成员是一个final域:
//Singleton with public final field
public class Elvis{
public static final Elvis INSTANCE = new Elvis();
private Elvis(){.....}
public void leaveTheBuilding(){....}
}
//test class
public class Test {
public static void main(String[] args) {
Elvis instance = Elvis.INSTANCE;
System.out.println(instance);
Elvis instance1 = Elvis.INSTANCE;
System.out.println(instance1);
}
输出结果:
私有构造器仅被调用一次,用来实例化公有的静态finalElvis.INSTANCE。由于缺少公有的或者受保护的构造器,所以保证了Elvis的全局唯一性:一旦Elvis类被实例化,将只会存在一个Elvis实例,不多也不少。客户端的任何行为都不会改变这一点。但是要提醒一点:享有特权的客户端可以借助AccessibleObject.setAccessible方法,通过反射机制来调用私有构造器。如果需要抵御这种攻击,可以修改构造器,让它在被要求创建第二个实例的时候抛出异常。
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Elvis instance = Elvis.INSTANCE;
System.out.println(instance);
Elvis instance1 = Elvis.INSTANCE;
System.out.println(instance1);
//通过反射获取私有构造器并创建一个新的对象
Class<?> clazz = Class.forName("org.example.domain.Elvis");
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Elvis instance2 = (Elvis)declaredConstructor.newInstance();
System.out.println(instance2);
}
输出结果:
2.公有的成员是个静态工厂方法:
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis(){}
public static Elvis getInstance() {return INSTANCE;}
public void leaveTehBuilding(){}
}
public class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Elvis instance = Elvis.getInstance();
System.out.println(instance);
Elvis instance1 = Elvis.getInstance();
System.out.println(instance1);
Class<?> clazz = Class.forName("org.example.domain.Elvis");
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Elvis instance2 = (Elvis)declaredConstructor.newInstance();
System.out.println(instance2);
}
}
输出结果:
两个方法的优势:
公共域方法:
1.API很清楚地表明了这个类是一个Singleton,因为公有的静态域是final的,所以盖玉总是包含相同的对象化引用。
2.简单。
静态工厂方法:
1.灵活性:在不改变其API的前提下,我们可以改变该类是否应该为Singleton的想法。
2.泛型化:可以编写一个泛型Singleton工厂(在后面的文章回有详细解释)。
3.通过方法引用作为提供者:Elvis::instance就是一个Supplier。