深浅模式
单例模式(存在的问题以及解决方案)
一、问题演示
破坏单例模式:
使上面定义的单例类(Singleton)可以创建多个对象,枚举方式除外。有两种方式,分别是序列化和反射。
序列化反序列化 Singleton类:
必须要实现Serializable 一个对象序列化的接口,一个类只有实现了Serializable接口,它的对象才能被序列化。
java
package com.mangoubiubiu.designpattern.single;
import java.io.Serializable;
/**
* 饿汉式
* 静态变量创建类的对象
*/
public class SingleEH implements Serializable {
private SingleEH(){}
//在成员变量创建对象
private static SingleEH singleEH =new SingleEH();
//提供对外访问的静态方法
public static SingleEH getSingleEH(){
return singleEH;
}
}java
package com.mangoubiubiu.designpattern.single;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class DestorySingle {
public static void main(String[] args) throws Exception{
writeSingle();
readSingle();
}
//从文件中读数据
public static void readSingle() throws Exception{
SingleEH singleEH=SingleEH.getSingleEH();
ObjectInputStream inputStream =new ObjectInputStream(new FileInputStream("D:\\single.txt"));
SingleEH singleObject = (SingleEH)inputStream.readObject();
System.out.println(singleEH);
System.out.println(singleObject);
inputStream.close();
}
//向文件中写入数据
public static void writeSingle() throws Exception{
SingleEH singleEH=SingleEH.getSingleEH();
ObjectOutputStream outputStream =new ObjectOutputStream(new FileOutputStream("D:\\single.txt"));
outputStream.writeObject(singleEH);
outputStream.close();
}
}运行结果 不是同一对象,说明序列化和反序列化破坏了单列模式
反射 Singleton类:
java
package com.mangoubiubiu.designpattern.single;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
public class DestorySingle {
public static void main(String[] args) throws Exception{
//获取SingleEH的字节码对象
Class clazz=SingleEH.class;
//2,获取无参构造方法
Constructor constructor=clazz.getDeclaredConstructor();
//3,取消访问检查
constructor.setAccessible(true);
//创建对象
SingleEH clazzSingleEH=(SingleEH)constructor.newInstance();
SingleEH singleEH=SingleEH.getSingleEH();
System.out.println(singleEH);
System.out.println(clazzSingleEH);
}
}运行结果 不是同一对象,说明反射破坏了单列模式 
二、解决方案
序列化、反序列方式破坏单例模式的解决方法
在Singleton类中添加readResolve()方法,在反序列化时被反射调用,如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象。
java
package com.mangoubiubiu.designpattern.single;
import java.io.Serializable;
/**
* 饿汉式
* 静态变量创建类的对象
*/
public class SingleEH implements Serializable {
private SingleEH(){}
//在成员变量创建对象
private static SingleEH singleEH =new SingleEH();
//提供对外访问的静态方法
public static SingleEH getSingleEH(){
return singleEH;
}
/**
* 下面是为了解决序列化反序列化破解单例模式
*/
private Object readResolve() {
return singleEH;
}
}java
package com.mangoubiubiu.designpattern.single;
import java.io.*;
import java.lang.reflect.Constructor;
public class DestorySingle {
public static void main(String[] args) throws Exception {
// writeSingle();
readSingle();
}
public static void reflectSingle()throws Exception{
//获取SingleEH的字节码对象
Class clazz=SingleEH.class;
//2,获取无参构造方法
Constructor constructor=clazz.getDeclaredConstructor();
//3,取消访问检查
constructor.setAccessible(true);
//创建对象
SingleEH clazzSingleEH=(SingleEH)constructor.newInstance();
SingleEH singleEH=SingleEH.getSingleEH();
System.out.println(singleEH);
System.out.println(clazzSingleEH);
}
//从文件中读数据
public static void readSingle() throws Exception{
SingleEH singleEH=SingleEH.getSingleEH();
ObjectInputStream inputStream =new ObjectInputStream(new FileInputStream("D:\\single.txt"));
SingleEH singleObject = (SingleEH)inputStream.readObject();
System.out.println(singleEH);
System.out.println(singleObject);
inputStream.close();
}
//向文件中写入数据
public static void writeSingle() throws Exception{
File file =new File("D:\\single.txt");
if(file.exists()){
System.out.println("文件已存在---准备删除文件");
file.delete();
}
SingleEH singleEH=SingleEH.getSingleEH();
ObjectOutputStream outputStream =new ObjectOutputStream(new FileOutputStream("D:\\single.txt"));
outputStream.writeObject(singleEH);
outputStream.close();
}
}
发现为同一对象 源码解析: ObjectInputStream类
java
public final Object readObject() throws IOException, ClassNotFoundException{
...
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
try {
Object obj = readObject0(false);//重点查看readObject0方法
.....
}
private Object readObject0(boolean unshared) throws IOException {
...
try {
switch (tc) {
...
case TC_OBJECT:
return checkResolve(readOrdinaryObject(unshared));//重点查看readOrdinaryObject方法
...
}
} finally {
depth--;
bin.setBlockDataMode(oldMode);
}
}
private Object readOrdinaryObject(boolean unshared) throws IOException {
...
//isInstantiable 返回true,执行 desc.newInstance(),通过反射创建新的单例类,
obj = desc.isInstantiable() ? desc.newInstance() : null;
...
// 在Singleton类中添加 readResolve 方法后 desc.hasReadResolveMethod() 方法执行结果为true
if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) {
// 通过反射调用 Singleton 类中的 readResolve 方法,将返回值赋值给rep变量
// 这样多次调用ObjectInputStream类中的readObject方法,继而就会调用我们定义的readResolve方法,所以返回的是同一个对象。
Object rep = desc.invokeReadResolve(obj);
...
}
return obj;
}反射方式破解单例的解决方法
java
public class Singleton {
//私有构造方法
private Singleton() {
/*
反射破解单例模式需要添加的代码
*/
if(instance != null) {
throw new RuntimeException();
}
}
private static volatile Singleton instance;
//对外提供静态方法获取该对象
public static Singleton getInstance() {
if(instance != null) {
return instance;
}
synchronized (Singleton.class) {
if(instance != null) {
return instance;
}
instance = new Singleton();
return instance;
}
}
}说明: 这种方式比较好理解。当通过反射方式调用构造方法进行创建创建时,直接抛异常。不运行此中操作。
JDK源码解析-Runtime类
Runtime类就是使用的单例设计模式。
- 通过源代码查看使用的是哪儿种单例模式
java
public class Runtime {
private static Runtime currentRuntime = new Runtime();
/**
* Returns the runtime object associated with the current Java application.
* Most of the methods of class <code>Runtime</code> are instance
* methods and must be invoked with respect to the current runtime object.
*
* @return the <code>Runtime</code> object associated with the current
* Java application.
*/
public static Runtime getRuntime() {
return currentRuntime;
}
/** Don't let anyone else instantiate this class */
private Runtime() {}
...
}- 使用Runtime类中的方法
java
public class RuntimeDemo {
public static void main(String[] args) throws IOException {
//获取Runtime类对象
Runtime runtime = Runtime.getRuntime();
//返回 Java 虚拟机中的内存总量。
System.out.println(runtime.totalMemory());
//返回 Java 虚拟机试图使用的最大内存量。
System.out.println(runtime.maxMemory());
//创建一个新的进程执行指定的字符串命令,返回进程对象
Process process = runtime.exec("ipconfig");
//获取命令执行后的结果,通过输入流获取
InputStream inputStream = process.getInputStream();
byte[] arr = new byte[1024 * 1024* 100];
int b = inputStream.read(arr);
System.out.println(new String(arr,0,b,"gbk"));
}
}
