一、什么是单例模式
单例模式属于创建型模式,是Java中最简单,也是最常用的设计模式之一。其本质是一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
二、单例模式的特点
1.该类只能有一个实例。
2.该类必须自己创建自己的实例。
3.该类必须给所有其他对象提供这一实例。
三、实现单例的主要思路
1.构造方法私有化(关键所在,目的是不让其它的类是主动创建该类对象)。
2.该类内部提供一个静态方法,返回该类对象(全局唯一)。
四、常见应用场景
1.项目的日志记录。
2.项目中读取配置文件的对象。
3.网站的计数器。
4……
五、单利模式常见的几种实现方式
1.饿汉式(线程安全,调用效率高,立即加载)
/**
* @Description 单利模式:饿汉式
* @Auther: 笑笑
* @Date: 17:15 2019/4/17
*/
public class Singleton01 {
//静态变量,类加载的时候会立即创建该类对象,如果用不到,会造成资源浪费
private static Singleton01 instance = new Singleton01();
//私有化构造方法
private Singleton01(){
}
//对外提供获得该类唯一对象的方法
public static Singleton01 getInstance(){
return instance;
}
public static void main(String[] args){
Singleton01 s1 = Singleton01.getInstance();
Singleton01 s2 = Singleton01.getInstance();
System.out.println(s1);
System.out.println(s2);
}
}
2.懒汉式(线程安全,调用效率不高,懒加载)。
/**
* @Description 单例模式:懒汉式
* @Auther: 笑笑
* @Date: 17:23 2019/4/17
*/
public class Singleton02 {
private static Singleton02 instance;
private Singleton02(){
}
//第一次调用该方法才创建该类对象,提高资源利用效率
//每次调用该方法都要同步,调用效率低
public static synchronized Singleton02 getInstance(){
if(instance == null){
instance = new Singleton02();
}
return instance;
}
public static void main(String[] args){
Singleton02 s1 = Singleton02.getInstance();
Singleton02 s2 = Singleton02.getInstance();
System.out.println(s1);
System.out.println(s2);
}
}
3.双重检测锁
/**
* @Description 单例模式:双重检测锁
* @Auther: 笑笑
* @Date: 17:32 2019/4/17
*/
public class Singleton03 {
private static Singleton03 instance;
private Singleton03(){}
public static Singleton03 getInstance(){
if (instance == null){ //第一次检查
synchronized (Singleton03.class){
if (instance == null){ //第二次检查
instance = new Singleton03();
}
}
}
return instance;
}
public static void main(String[] args){
Singleton03 s1 = Singleton03.getInstance();
Singleton03 s2 = Singleton03.getInstance();
System.out.println(s1);
System.out.println(s2);
}
}
4.静态内部类(懒汉式)
/**
* @Description 单例模式:静态内部类
* @Auther: 笑笑
* @Date: 17:42 2019/4/17
*/
public class Singleton04 {
private Singleton04(){}
//加载外部类,不会立即加载内部类,所以可以懒加载对象
private static class InnerClass{
private static final Singleton04 instance = new Singleton04();
}
public static Singleton04 getInstance(){
return InnerClass.instance;
}
public static void main(String[] args){
Singleton04 s1 = Singleton04.getInstance();
Singleton04 s2 = Singleton04.getInstance();
System.out.println(s1);
System.out.println(s2);
}
}
5.枚举(饿汉式)
/**
* @Description 单例模式:枚举实现
* @Auther: 笑笑
* @Date: 17:49 2019/4/17
*/
public enum Singleton05 {
//定义枚举元素,这就是一个Singleton05的单例
INSTANCE;
public static Singleton05 getInstance(){
return INSTANCE;
}
public static void main(String[] args){
Singleton05 s1 = Singleton05.getInstance();
Singleton05 s2 = Singleton05.getInstance();
System.out.println(s1);
System.out.println(s2);
}
}
六、反射方式破解单例模式(除了枚举实现的单例之外)
package com.xiao.designpattern.singletonpattern;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @Description 反射破解单例模式
* @Auther: 笑笑
* @Date: 20:41 2019/4/21
*/
public class Demo_01 {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
System.out.println("*************正常的单例模式*********************");
Singleton01 s_1 = Singleton01.getInstance();
Singleton01 s_2 = Singleton01.getInstance();
System.out.println(s_1);
System.out.println(s_2);
System.out.println(s_1 == s_2);//true
System.out.println("*************反射破解单例*********************");
Class<Singleton01> clazz = Singleton01.class;
Constructor<Singleton01> singleton01Constructor = clazz.getDeclaredConstructor(null);//获取私有空参的构造方法
singleton01Constructor.setAccessible(true);//跳过权限检查,可以获取私有属性和方法
Singleton01 s1 = singleton01Constructor.newInstance();//反射,使用空参构造创建对象
Singleton01 s2 = singleton01Constructor.newInstance();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1 == s2);// false , 这样使用反射就破解了单例模式
}
}
运行结果如下
*************正常的单例模式*********************
com.xiao.designpattern.singletonpattern.Singleton01@1540e19d
com.xiao.designpattern.singletonpattern.Singleton01@1540e19d
true
*************反射破解单例*********************
com.xiao.designpattern.singletonpattern.Singleton01@677327b6
com.xiao.designpattern.singletonpattern.Singleton01@14ae5a5
false
防止反射破解单例模式,在构造方法中抛出RuntimeException
private Singleton01(){
if(instance != null){
throw new RuntimeException();
}
}
再次运行测试类,结果如下
*************正常的单例模式*********************
com.xiao.designpattern.singletonpattern.Singleton01@1540e19d
com.xiao.designpattern.singletonpattern.Singleton01@1540e19d
true
*************反射破解单例*********************
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.xiao.designpattern.singletonpattern.Demo_01.main(Demo_01.java:26)
Caused by: java.lang.RuntimeException
at com.xiao.designpattern.singletonpattern.Singleton01.<init>(Singleton01.java:18)
... 5 more
七、反序列化方式破解单例模式(除了枚举实现的单例之外)
首先,测试的单例类需要实现序列化接口Serializable,最终测试代码如下
package com.xiao.designpattern.singletonpattern;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @Description 使用反序列化破解单例模式
* @Auther: 笑笑
* @Date: 20:55 2019/4/21
*/
public class Demo_02 {
public static void main(String[] args) throws Exception {
System.out.println("*************正常的单例模式*********************");
Singleton01 s_1 = Singleton01.getInstance();
Singleton01 s_2 = Singleton01.getInstance();
System.out.println(s_1);
System.out.println(s_2);
System.out.println(s_1 == s_2);
System.out.println("*************反序列化破解单例*********************");
//将上面的s_1对象序列化到一个文件中
FileOutputStream outputStream = new FileOutputStream("G:\\1.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(s_1);
objectOutputStream.close();
outputStream.close();
//从文件中反序列化s_1对象
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("G:\\1.txt"));
Singleton01 ss_1 = (Singleton01)objectInputStream.readObject();
objectInputStream.close();
System.out.println(s_1);
System.out.println(ss_1);
System.out.println(s_1 == ss_1);
}
}
运行结果如下
*************正常的单例模式*********************
com.xiao.designpattern.singletonpattern.Singleton01@1540e19d
com.xiao.designpattern.singletonpattern.Singleton01@1540e19d
true
*************反序列化破解单例*********************
com.xiao.designpattern.singletonpattern.Singleton01@1540e19d
com.xiao.designpattern.singletonpattern.Singleton01@5fd0d5ae
false
防止反序列化破解单例模式,在单例类中,添加readResolve()方法
//反序列化时,会直接调用此方法,返回对象,不需要再创建新的对象
private Object readResolve(){
return instance;
}
再次运行测试类,结果如下
*************正常的单例模式*********************
com.xiao.designpattern.singletonpattern.Singleton01@1540e19d
com.xiao.designpattern.singletonpattern.Singleton01@1540e19d
true
*************反序列化破解单例*********************
com.xiao.designpattern.singletonpattern.Singleton01@1540e19d
com.xiao.designpattern.singletonpattern.Singleton01@1540e19d
true