一、核心作用
保证一个类只有一个实例,并且提供一个访问该实例,并且提供一个访问该实例的全局访问
二、常见的应用场景
三、常见的五种单例模式模式实现方式
-主要
饿汉式(线程安全,调用效率高,但是不能延时加载)
懒汉式(线程安全,调用效率不高,可以延时加载)
-其他
双重检测锁式(由于JVM底层内部模型的原因,偶尔会出问题,不建议使用)
静态内部式(线程安全,调用效率高。可以延时加载)
枚举单例(线程安全,调用效率高。不能延时加载)
四、如何选用
-单例对象 占用资源少,不需要延时加载
枚举式好于饿汉式
-单例对象 占用资源大,需要延时加载
静态内部式好于懒汉式
五、各种实现方式代码
package Singleton;
/***
* 测试单例模式中 饿汉式
* 1.线程安全
* 2.线率高
* 3.但是,不能延时加载
* @author zw
*
*/
public class SingletonDemo01 {
//类初始化时,立即加载这个对象
private static SingletonDemo01 instance = new SingletonDemo01();
private SingletonDemo01() {
}
public static SingletonDemo01 getInstance() {
return instance;
}
}
package Singleton;
/***
* 测试单例模式中 懒汉式
* 1.线程安全
* 2.调用效率不高
* 3.但是,keyi 延时加载
* @author zw
*
*/
public class SingletonDemo02 {
private static SingletonDemo02 instance ;
private SingletonDemo02() {
}
//如果不加synchronized,会导致线程不安全。在多线程中,会同时创建多个对象
public static synchronized SingletonDemo02 getInstance() {
if(instance == null) {
instance =new SingletonDemo02();
return instance;
}
return instance;
}
}
package Singleton;
/***
* 静态内部类实现单例模式
*
* 优点:线程安全,调用安全,并实现了延时加载
* @author zw
*
*/
public class SingletonDemo03 {
private static class singletonClass{
private static final SingletonDemo03 instance = new SingletonDemo03();
}
private SingletonDemo03(){
}
public SingletonDemo03 getInstance(){
return singletonClass.instance;
}
}
package Singleton;
/***
* 测试枚举实现单例模式
* 1.避免反射 反序列化 漏洞
* 2.唯一 是没有懒加载
*
* @author zw
*
*/
public enum SingletonDemo04 {
//这个枚举元素本身就是单例模式。
INSTANCE;
}
反射,可以通过setAccessible(true)可以访问私有属性。反序列化,可以构造多个对象
package Singleton;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/***
* 测试反射 反序列化访问漏洞
*
* @author zw
*
*/
public class App {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, Exception {
SingletonDemo2 s1 = SingletonDemo2.getInstance();
SingletonDemo2 s2 = SingletonDemo2.getInstance();
System.out.println(s1);
System.out.println(s2);
//通过反射的方式直接调用私有构造器
// Class clzz =Class.forName("Singleton.SingletonDemo2");
// Constructor c =clzz.getDeclaredConstructor(null);
// c.setAccessible(true);//跳过安全检查就可以访问私有属性
// SingletonDemo02 s1 =(SingletonDemo02) c.newInstance(null);
// SingletonDemo02 s2 =(SingletonDemo02) c.newInstance(null);
// System.out.println(s1);
// System.out.println(s2);
//通过反序列化的方式构造多个对象
FileOutputStream fos = new FileOutputStream("d:/test/a.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s1);
oos.flush();
oos.close();
fos.close();
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("d:/test/a.txt"));
SingletonDemo2 sdd =(SingletonDemo2) ois.readObject();
System.out.println(sdd);
}
}
通过以下方法可以防止这种漏洞
package Singleton;
import java.io.Serializable;
/***
* 测试单例模式中 懒汉式
* 1.线程安全
* 2.调用效率不高
* 3.但是,keyi 延时加载
* @author zw
*
*/
public class SingletonDemo2 implements Serializable{
private static SingletonDemo2 instance ;
private SingletonDemo2() {
if(instance!=null) {
throw new RuntimeException();
}
}
//如果不加synchronized,会导致线程不安全。在多线程中,会同时创建多个对象
public static synchronized SingletonDemo2 getInstance() {
if(instance == null) {
instance =new SingletonDemo2();
return instance;
}
return instance;
}
//反序列化如果定义了readResolve(),则直接返回此方法的指定对象。而不需要单独创建对象
private Object readResolve() {
return instance;
}
}