设计模式-创建者模式-单例模式(存在的问题以及解决方案)

一、 存在的问题

1. 问题演示

破坏单例模式:

使上面定义的单例类(Singleton)可以创建多个对象,枚举方式除外。有两种方式,分别是序列化和反射。

  • 序列化反序列化
    Singleton类:

必须要实现Serializable

一个对象序列化的接口,一个类只有实现了Serializable接口,它的对象才能被序列化

复制代码
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;
    }

}
复制代码
复制代码
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类:

复制代码
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出来的对象。

复制代码
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;
    }

}
复制代码
复制代码
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类

复制代码
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;
}
复制代码

反射方式破解单例的解决方法

复制代码
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类就是使用的单例设计模式。

  1. 通过源代码查看使用的是哪儿种单例模式
复制代码
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() {}
    ...
}
复制代码
  1. 使用Runtime类中的方法
复制代码
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"));
    }
}
复制代码

 

 

 

 

 

本文作者:KwFruit

本文链接:https://www.cnblogs.com/mangoubiubiu/p/15991600.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   KwFruit  阅读(45)  评论(0编辑  收藏  举报
历史上的今天:
2021-03-10 Nginx 入门(十) 完结 nginx 原理与优化参数配置
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起