单例基本概念
在当前Jvm中只会有一个该实例对象
单例应用场景
- 项目中定义的配置文件
- Servlet对象默认就是单例
- 线程池、数据库连接池
- Spring中Bean对象默认就是单例
- 实现网站计数器
- Jvm内置缓存框架(定义单例HashMap)
- 定义枚举常量信息
单例优缺点
优点:能够节约当前堆内存,不需要频繁New对象,能够快速访问。
缺点:当多个线程访问同一个单例对象的时候可能会存在线程安全问题。
单例是否可以被破解
在Jvm中只能存在一个实例
Jvm中可以通过哪些方式创建对象
- 反射机制
- 序列化
- 自己new
- 克隆技术
序列化概念
序列化:将我们对象转换成二进制的形式直接存放在硬盘
反序列化:从本地文件读取二进制文件,转换成对象
序列化可以创建对象 违背单例的设计原则
疑问:
- 序列化创建对象为什么没有走无参构造函数却是一个新对象
2.防御序列化创建一个新的对象违背单例原则
readResolve
可以在序列化类中加上 readResolve方法 保证序列化生成
单例对象
原理:
通过序列化的形式创建是否会走序列化类无参构造函数
不会
判断序列化类是否有实现Serializable接口,如果实现该接口的情况下,则调用该类的父类没有实现Serializable初始化,通过父类实例化该对象。
readResolve
序列化在创建对象的时候,判断序列化类中是否存在readResolve方法,如果存在的情况下,则直接走反射调用序列化类中readResolve返回单例对象。
序列化、反射破解我们单例能不能再根本底层上解决
枚举 最安全单例:
- 先天性 序列化生成的对象默认就是保证单例
- 反射无法初始化到我们枚举
单例的(10种)写法
懒汉式线程不安全
public class Singleton01 {private static Singleton01 singleton01 = null;/*** 私有构造函数*/private Singleton01() {}/*** 懒汉式 线程不安全** @return*/public static Singleton01 getInstance() {if (singleton01 == null) {singleton01 = new Singleton01();}return singleton01;}public static void main(String[] args) {Singleton01 instance1 = Singleton01.getInstance();Singleton01 instance2 = Singleton01.getInstance();System.out.println(instance1 == instance2);}}
|
懒汉式:当真正需要获取对象的时候,才去创建该单例对象,该写法存在线程问题
懒汉式线程安全
public class Singleton02 {private static Singleton02 singleton02 = null;/*** 懒汉式线程安全 已经创建对象,获取该单例对象的时候还需要上锁效率比较低** @return*/public static synchronized Singleton02 getInstance() {if (singleton02 == null) {singleton02 = new Singleton02();}return singleton02;}public static void main(String[] args) {Singleton02 instance1 = Singleton02.getInstance();Singleton02 instance2 = Singleton02.getInstance();System.out.println(instance2 == instance1);}}
|
该写法能够保证线程安全问题,获取该单例对象的时候效率非常低
懒汉式双重检验锁 (重点方法,加volatile)
public class Singleton03 {private static volatile Singleton03 singleton03;public static Singleton03 getInstance() {// 第一次检查if (singleton03 == null) {//第二次检查synchronized (Singleton03.class) {if (singleton03 == null) {singleton03 = new Singleton03();}}}return singleton03;}public static void main(String[] args) {Singleton03 instance1 = Singleton03.getInstance();Singleton03 instance2 = Singleton03.getInstance();System.out.println(instance1==instance2);}}
|
能够保证线程安全,只会创建该单例对象的时候上锁,获取该该单例对象不会上锁,效率比较高。
饿汉式(私有)
public class Singleton04 {/*** 提前创建单例对象,优点先天性 保证线程安全,比较占用内存*/public static final Singleton04 singleton04 = new Singleton04();private Singleton04() {}private static Singleton04 getInstance() {return singleton04;}public static void main(String[] args) {Singleton04 instance1 = Singleton04.getInstance();Singleton04 instance2 = Singleton04.getInstance();System.out.println(instance1 == instance2);}}
|
饿汉式(公有)
public class Singleton04 {/*** 提前创建单例对象,优点先天性 保证线程安全,比较占用内存*/public static final Singleton04 singleton04 = new Singleton04();private Singleton04() {}private static Singleton04 getInstance() {return singleton04;}public static void main(String[] args) {Singleton04 instance1 = Singleton04.singleton04;Singleton04 instance2 = Singleton04.singleton04;System.out.println(instance1 == instance2);}}
|
静态代码块
public class Singleton05 {private static Singleton05 singleton05;static {singleton05 = new Singleton05();}public static Singleton05 getInstance() {return singleton05;}public static void main(String[] args) {Singleton05 instance1 = Singleton05.getInstance();Singleton05 instance2 = Singleton05.getInstance();System.out.println(instance1 == instance2);}}
|
静态内部类
public class Singleton06 {private Singleton06() {System.out.println(">>>Singleton06");}private static class SingletonHolder {private static final Singleton06 singleton06 = new Singleton06();}public static final Singleton06 getInstance() {return SingletonHolder.singleton06;}public static void main(String[] args) {Singleton06 instance1 = Singleton06.getInstance();Singleton06 instance2 = Singleton06.getInstance();System.out.println(instance1==instance2);}}
|
枚举实现单例子
public enum Singleton07 {INSTANCE;public void getInstance() {System.out.println("<<<getInstance>>>");}}
|
枚举最安全,不可以被反射也不能被序列化 破解
多少种方式可以创建对象
- 直接new对象
- 采用克隆对象
- 使用反射创建对象
- 序列化与反序列化
如何单例被破解
如何防止被反射破解
private Singleton01() throws Exception {if (singleton01 != null) {throw new Exception("该对象已经创建");}System.out.println("无参构造函数");} |
Class<?> aClass = Class.forName("com.mayikt.Singleton01");Constructor<?> constructor = aClass.getDeclaredConstructor();constructor.setAccessible(true);Singleton01 instance02 = Singleton01.getInstance();Singleton01 singleton01 = (Singleton01) constructor.newInstance();System.out.println(singleton01==instance02);
|
如何防止序列化破解
序列化概念:将对象转换成二进制的形式直接存放在本地
反序列化概念:从硬盘读取二进制变为对象
// 1.将对象序列化存入到本地文件中FileOutputStream fos = new FileOutputStream("d:/code/a.txt");ObjectOutputStream oos = new ObjectOutputStream(fos);Singleton04 singleton1 = Singleton04.singleton04;oos.writeObject(singleton1);oos.close();fos.close();//2.从硬盘中反序列化对象到内存中ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/code/a.txt"));Singleton04 singleton2 = (Singleton04) ois.readObject();System.out.println(singleton1);System.out.println(singleton2);
|
public class Singleton04 implements Serializable {/*** 提前创建单例对象,优点先天性 保证线程安全,比较占用内存*/public static final Singleton04 singleton04 = new Singleton04();private Singleton04() {System.out.println("Singleton04");}private static Singleton04 getInstance() {return singleton04;}public static void main(String[] args) {Singleton04 instance1 = Singleton04.singleton04;Singleton04 instance2 = Singleton04.singleton04;System.out.println(instance1 == instance2);}}
|
重写该方法 指定返回的对象 防止序列化破解
private Object readResolve() throws ObjectStreamException {return singleton04;}
|
注意:如果该类是Serializable类型的 则调用它第一个非Serializable父类的无参构造函数初始化该对象该对象
如何反射是否可以破解单例
不可以被反射 也不可以被序列化破解
// 1.将对象序列化存入到本地文件中FileOutputStream fos = new FileOutputStream("d:/code/a.txt");ObjectOutputStream oos = new ObjectOutputStream(fos);Singleton07 singleton1 = Singleton07.INSTANCE;oos.writeObject(singleton1);oos.close();fos.close();//2.从硬盘中反序列化对象到内存中ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/code/a.txt"));Singleton07 singleton2 = (Singleton07) ois.readObject();System.out.println(singleton1==singleton2);
|