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

简介: <h2>先看一个单例:</h2> <p></p> <pre code_snippet_id="555854" snippet_file_name="blog_20141218_1_3706" name="code" class="java">public class Singleton{ private final static Singleton INSTANCE = new S

先看一个单例:

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可以作为想用单例时的第一选择。




相关文章
|
4月前
|
安全 Java
Java单例---序列化破坏单例模式原理解析(二)
Java单例---序列化破坏单例模式原理解析
24 0
|
4月前
|
Java
Java单例---序列化破坏单例模式原理解析(一)
Java单例---序列化破坏单例模式原理解析
35 0
|
5月前
|
前端开发 fastjson
mvc配置fastjson序列化枚举
mvc配置fastjson序列化枚举
43 0
|
8月前
|
存储 安全 Java
Java 序列化与反射
Java 序列化与反射
83 0
|
10月前
|
JSON 数据格式
【解决方案 十七】序列化反序列化时枚举值如何显示为字符串
【解决方案 十七】序列化反序列化时枚举值如何显示为字符串
108 0
|
前端开发 Java
SpringBoot自定义枚举序列化方式
SpringBoot自定义枚举序列化方式
965 0
|
JSON 开发框架 算法
.Net 序列化枚举为字符串
我所做的项目是需要调用业务算法的,算法中有一个入参是油品的性质,这个性质有名称、编码、类型等属性,其中类型是固定质量性质、体积性质和其他性质这三种,所以我把其作为枚举类型。问题也由此产生,默认情况下,枚举是以其整数形式进行 JSON 序列化,这就需要同研发算法的同事约定好数值的含义。但是经过协商,算法同事要求我们传递成字符串。因此,我们希望它们在一些情况下以字符串的形式进行序列化。本文将讲解实现这一目标的各种方法。
|
SQL Java
防止Java序列化/反射破坏单例模式的解决方案
防止Java序列化/反射破坏单例模式的解决方案
240 0
防止Java序列化/反射破坏单例模式的解决方案
|
安全 Java 编译器
Java中枚举的线程安全性及序列化问题
--枚举是如何保证线程安全的 要想看源码,首先得有一个类吧,那么枚举类型到底是什么类呢?是enum吗?答案很明显不是,enum就和class一样,只是一个关键字,他并不是一个类,那么枚举是由什么类维护的呢,我们简单的...
1669 0

热门文章

最新文章