单例,枚举,反射,序列化--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可以作为想用单例时的第一选择。




相关文章
|
5月前
|
Java
JAVA单例模式-双重检验锁(防止反射、序列化多个)
JAVA单例模式-双重检验锁(防止反射、序列化多个)
|
5月前
|
存储 安全 网络协议
【JAVA反序列化】序列化与反序列化&Java反射&URLDNS链
【JAVA反序列化】序列化与反序列化&Java反射&URLDNS链
|
6月前
|
JSON 编译器 Go
Golang深入浅出之-结构体标签(Tags):JSON序列化与反射应用
【4月更文挑战第22天】Go语言结构体标签用于添加元信息,常用于JSON序列化和ORM框架。本文聚焦JSON序列化和反射应用,讨论了如何使用`json`标签处理敏感字段、实现`omitempty`、自定义字段名和嵌套结构体。同时,通过反射访问标签信息,但应注意反射可能带来的性能问题。正确使用结构体标签能提升代码质量和安全性。
297 0
|
6月前
|
安全 Java
Java单例---序列化破坏单例模式原理解析(二)
Java单例---序列化破坏单例模式原理解析
51 0
|
6月前
|
Java
Java单例---序列化破坏单例模式原理解析(一)
Java单例---序列化破坏单例模式原理解析
77 0
|
前端开发 fastjson
mvc配置fastjson序列化枚举
mvc配置fastjson序列化枚举
120 0
|
前端开发 Java
SpringBoot自定义枚举序列化方式
SpringBoot自定义枚举序列化方式
1098 1
|
存储 安全 Java
Java 序列化与反射
Java 序列化与反射
134 0
|
JSON 数据格式
【解决方案 十七】序列化反序列化时枚举值如何显示为字符串
【解决方案 十七】序列化反序列化时枚举值如何显示为字符串
149 0
|
JSON 开发框架 算法
.Net 序列化枚举为字符串
我所做的项目是需要调用业务算法的,算法中有一个入参是油品的性质,这个性质有名称、编码、类型等属性,其中类型是固定质量性质、体积性质和其他性质这三种,所以我把其作为枚举类型。问题也由此产生,默认情况下,枚举是以其整数形式进行 JSON 序列化,这就需要同研发算法的同事约定好数值的含义。但是经过协商,算法同事要求我们传递成字符串。因此,我们希望它们在一些情况下以字符串的形式进行序列化。本文将讲解实现这一目标的各种方法。