单例,枚举,反射,序列化--effectiveJava读书笔记

先看一个单例:

public class Singleton{
	private final static Singleton INSTANCE  = new Singleton();
	private Singleton(){};
	public static Singleton getInstance(){return INSTANCE;}
}

我们用序列化来打破单例

public class Singleton implements Serializable{
	private final static Singleton INSTANCE  = new Singleton();
	private Singleton(){};
	public static Singleton getInstance(){return INSTANCE;}

	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
		Singleton s1 = Singleton.getInstance();
		File objectF = new File("/object");
		ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(objectF));
		out.writeObject(s1);
		out.close();
		ObjectInputStream in = new ObjectInputStream(new FileInputStream(objectF));
		Singleton s2 = (Singleton) in.readObject();
		in.close();
		System.out.println("是单例么?" + (s1 == s2));
	}
}

将会打印:

是单例么?false。

可见我们可以这样破坏其单例属性。要保持应该怎么办呢?需要增加readResolve方法,Java反序列化的时候会用这个方法的返回值直接代替序列化得到的对象

public class Singleton implements Serializable{
	private final static Singleton INSTANCE  = new Singleton();
	private Singleton(){};
	public static Singleton getInstance(){return INSTANCE;}

	private Object readResolve() {
		return INSTANCE;
	}

	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
		Singleton s1 = Singleton.getInstance();
		File objectF = new File("/object");
		ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(objectF));
		out.writeObject(s1);
		out.close();
		ObjectInputStream in = new ObjectInputStream(new FileInputStream(objectF));
		Singleton s2 = (Singleton) in.readObject();
		in.close();
		System.out.println("是单例么?" + (s1 == s2));
	}
}

打印:

是单例么?true

我们再通过反射来打破其的单例性:

public class Singleton{
	private final static Singleton INSTANCE  = new Singleton();
	private Singleton(){};
	public static Singleton getInstance(){return INSTANCE;}

	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
		Singleton s1 = Singleton.getInstance();
		Constructor<Singleton> c = Singleton.class.getDeclaredConstructor();
		c.setAccessible(true);
		Singleton s2 = c.newInstance();
		System.out.println("是单例么?" + (s1 == s2));
	}
}

将会打印:

是单例么?false。

说明使用反射调用私有构造器也是可以破坏单例的,解决的办法是如下:

public class Singleton{
	private final static Singleton INSTANCE  = new Singleton();
	private Singleton(){
		if(++COUNT > 1){
			throw new RuntimeException("can not be construt more than once");
		}
	};
	private static int COUNT = 0;
	public static Singleton getInstance(){
		return INSTANCE;
	}
}

这样当使用反射调用的时候,就会抛出异常。

再用clone来破坏单例性

public class Singleton implements Cloneable{
	private final static Singleton INSTANCE  = new Singleton();
	public static Singleton getInstance(){
		return INSTANCE;
	}

	public static void main(String[] args) {
		Singleton s1 = Singleton.getInstance();
		Singleton s2 = (Singleton) s1.clone();
		System.out.println("是单例么?" + (s1 == s2));
	}
}

这样也会发现不是单例了,办法是重新clone方法。

public class Singleton implements Cloneable{
	private final static Singleton INSTANCE  = new Singleton();
	public static Singleton getInstance(){
		return INSTANCE;
	}

	@Override
	protected Object clone() throws CloneNotSupportedException {
		return INSTANCE;
	}

	public static void main(String[] args) {
		Singleton s1 = Singleton.getInstance();
		Singleton s2 = (Singleton) s1.clone();
		System.out.println("是单例么?" + (s1 == s2));
	}
}

如果想要比较简便的避免上诉的问题,最好的方式是使用枚举:

public enum SingleEnum {
	INSTANCE;

	public static SingleEnum getInstance(){
		return INSTANCE;
	}
}

其通过反射会抛出如下异常:

 java.lang.NoSuchMethodException: com.price.effective.create.SingleEnum.<init>()

通过反序列化其也会返回INSTANCE对象。

其没有clone方法

综上,Enum可以作为想用单例时的第一选择。

时间: 2024-10-26 20:18:46

单例,枚举,反射,序列化--effectiveJava读书笔记的相关文章

静态方法代替构造器-effectiveJava读书笔记

一般可能用到的地方: 1. Boolean.valueOf(true) 2. BigInteger.probablePrime(int length, Random) 3. 单例 通常方法为getInstance 这里还有些其他的习惯用法,也总结一下: valueOf()    通常用来做类型转换,或者封装 of    valueOf的简洁形式 getInstance 单例 newInstance 每次调用返回不同的实例 getType 与getInstance类似,但是是工厂方法的形式,要获取

多参构造使用构建器--effectiveJava读书笔记

对付多参的构建,特别是一些必选,一些可选的时候我们一般有如下的解决办法. 比如抽象一个装机的类,  cpu,内存,主板,硬盘是必选的,但是显卡,声卡,固态硬盘是可选的. 为了对付各种各样的装机需求.我们一般有如下办法: 1. 重载构造器 这样可能会需要多个构造器,可读性不好. 2. 用JavaBean模式的set方法. 这种可读性要好很多,但是其能够在运行期间随意的修改,不能够保持状态,不能够通过对构造参数的校验来避免问题,并且还会给线程同步带来麻烦. 3. 就是使用构建器了,看如下的代码: p

Java 实现单例的难点

有简单又高效的方法可以实现单例模式,但没有一种方式能在任何情况下都确保单例的完整性. 单例模式是指某个类只被实例化一次,用来表示全局或系统范围的组件.单例模式常用于日志记录.工厂.窗口管理器和平台组件管理等.我认为要尽量避免 使用单例模式,因为一旦实现就很难改变或重载,而且会造成编写测试用例困难.代码结构糟糕等问题.另外,下面文章中的单例模式是不安全的. 人们花大量的精力研究怎样更好地实现单例模式,但有一种简单高效的实现方法.然而,没有一种方法能在任何情况下都确保单例的完整性.阅读下文,看看你是

《Java核心技术 卷Ⅱ 高级特性(原书第10版)》一2.4.4 序列化单例和类型安全的枚举

2.4.4 序列化单例和类型安全的枚举 在序列化和反序列化时,如果目标对象是唯一的,那么你必须加倍当心,这通常会在实现单例和类型安全的枚举时发生. 如果你使用Java语言的enum结构,那么你就不必担心序列化,它能够正常工作.但是,假设你在维护遗留代码,其中包含下面这样的枚举类型: 这种风格在枚举被添加到Java语言中之前是很普遍的.注意,其构造器是私有的.因此,不可能创建出超出Orientation.HORIZONTAL和Orientation.VERTICAL之外的对象.特别是,你可以使用=

Effective java 第2版 - 笔记(01) 单例(Singleton)的枚举(enum)实现

直接上代码: 1 public enum Boss { 2 3 INSTANCE; 4 5 private String name; 6 7 public void doSomeThing() { 8 System.out.println(name + " is doing something now..."); 9 } 10 11 public String getName() { 12 return name; 13 } 14 15 public void setName(Stri

【转载】JAVA序列化/反序列化与单例

本文转载自http://shift-alt-ctrl.iteye.com/blog/1842040   单例设计类:   Java代码   package com.test.singleton;      import java.io.IOException;   import java.io.ObjectStreamException;   import java.io.Serializable;         public class SingleTon implements Serial

序列化对单例的破坏

                                                                                序列化对单例的破坏 本文将通过实例+阅读Java源码的方式介绍序列化是如何破坏单例模式的,以及如何避免序列化对单例的破坏. 单例模式,是设计模式中最简单的一种.通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源.如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案.关于

JAVA序列化/反序列化与单例

单例设计类:   Java代码   package com.test.singleton;      import java.io.IOException;   import java.io.ObjectStreamException;   import java.io.Serializable;         public class SingleTon implements Serializable{          /**       *        */       private

序列化与单例

当单例模式的类实现了系列化Serializable接口,也可以通过反序列化来使它不再单例.  我们的单例类:  ? 1 2 3 4 5 6 7 8 9 10 11 12 public final class Singleton implements Serializable{       private static final long serialVersionUID = 1735776740157142434L;           private static final Singlet