声明:此笔记为B站up主“程序员大表哥”所授课程【23种java设计模式】个人学习笔记,仅供学习,不做他用。
链接:https://www.bilibili.com/video/av81810102
一.单例模式
1.懒汉模式
延迟加载,只有在真正使用的时候, 才开始实例化。
1).线程安全问题
2) .double check加锁优化
3).编译器(JIT),CPU 有可能对指令进行重排序,导致使用到尚未初始化的实例,可以通过添加volatile 关键字进行修饰,对于volatile修饰的字段,可以防止指令重排。
class lazySingleton {
//声明但是不初始化,懒汉
private volatile static lazySingleton instance;
private lazySingleton(){}
public static lazySingleton getInstance() {
if (instance == null) {
synchronized (lazySingleton.class){
if (instance==null){
/*
字节码层面
JIT,JVM
1.分配空间
ps:2,3可能会颠倒顺序,有可能会发生空指针错误。 volatile解决此类问题
2.引用赋值
3.初始化
*/
instance=new lazySingleton();
}
}
}
return instance;
}
}
2.饿汉模式
类加载的初始化阶段就完成了实例的初始化。本质上就是借助于JVM类加载机制,保证实例的唯一性。
类加载过程:
1,加载二进制数据到内存中,生成对应的Class数据结构,
2,连接: a.验证,b.准备(给类的静态成员变量赋 默认值),c.解析
3,初始化:给类的静态变量赋 初值
只有在真正使用对应的类时,才会触发初始化如( 当前类是启动类即main函数所在类,直接进行new操作,访问静态属性、访问静态方法,用反射访问类,初始化一个类的子类等.)
class hungrySingleton{
//声明且初始化,饿汉,通过JVM加载机制保证单例
private static hungrySingleton instance=new hungrySingleton();
private hungrySingleton(){
}
public static hungrySingleton getInstance() {
return instance;
}
}
3.静态内部类实现
1).本质上是利用类的加载机制来保证线程安全
2).只有在实际使用的时候,才会触发类的初始化,所以也是懒加载的一种形式。
class InnerClassSingleton{
private static class InnerClassSingletonHolder{
private static InnerClassSingleton instance=new InnerClassSingleton();
}
private InnerClassSingleton(){}
public static InnerClassSingleton getInstance() {
return InnerClassSingletonHolder.instance;
}
}
4.反射攻击
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 反射攻击
Constructor<InnerClassSingleton> declaredConstructor
=InnerClassSingleton.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
InnerClassSingleton instance=declaredConstructor.newInstance();
InnerClassSingleton instance2=InnerClassSingleton.getInstance();
System.out.println(instance);
System.out.println(instance2);
}
解决方法:饿汉模式和静态内部类方式可以在构造函数加判断,抛出异常!防止反射攻击!
private InnerClassSingleton(){
if (InnerClassSingletonHolder.instance!=null){
throw new RuntimeException("Muti-instances is not allowed!");
}
}
5.枚举类型实现
1)天然不支持反射创建对应的实例,且有自己的反序列化机制
2)利用类加载机制保证线程安全
public enum EnumSingleton {
INSTANCE;
public void print(){
System.out.println(this.hashCode());
}
}
//反射实例化测试:不可以!
class EnumTest{
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingleton enumSingleton = declaredConstructor.newInstance("INSTANCE",0);
}
}
//报错:Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
5.序列化
1)可以利用指定方法来替换从反序列化流中的数据如下:
1 //ANY-ACCESS -MODIFIER object readResolve() throws objectStreamException;
5.1内部类反序列化:
//不支持序列化,需要实现Serializable
class InnerClassSingleton implements Serializable{
//加上serialVersionUID防止兼容对文件的修改
static final long serialVersionUID = 42L;
private static class InnerClassSingletonHolder{
private static InnerClassSingleton instance=new InnerClassSingleton();
}
private InnerClassSingleton(){
//阻止反射实例化第二个instance
if (InnerClassSingletonHolder.instance!=null){
throw new RuntimeException("Muti-instances is not allowed!");
}
}
public static InnerClassSingleton getInstance() {
return InnerClassSingletonHolder.instance;
}
//解决序列化后instance不一致的问题
Object readResolve() throws ObjectStreamException{
return InnerClassSingletonHolder.instance;
}
}
测试序列化:
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, ClassNotFoundException {
InnerClassSingleton instance=InnerClassSingleton.getInstance();
/*序列化
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("InnerClassSingletonSerialization"));
oos.writeObject(instance);
oos.close();
*/
//反序列化
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("InnerClassSingletonSerialization"));
InnerClassSingleton instance1=(InnerClassSingleton)ois.readObject();
ois.close();
System.out.println(instance);
System.out.println(instance1);
}
5.2枚举类型反序列化
//天然支持序列化
class EnumTest{
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, ClassNotFoundException {
// Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class,int.class);
// declaredConstructor.setAccessible(true);
// EnumSingleton enumSingleton = declaredConstructor.newInstance("INSTANCE",0);
EnumSingleton instance=EnumSingleton.INSTANCE;
// ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("EnumSingletonSerialization"));
// oos.writeObject(instance);
// oos.close();
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("EnumSingletonSerialization"));
EnumSingleton instance1=(EnumSingleton) ois.readObject();
ois.close();
System.out.println(instance);
System.out.println(instance1);
}
}
6.单例模式在源码中的运用
源码中的应用
1//Spring&JDK
2 java. lang . Runtime
3 org. springframework. aop. framework. ProxyFactoryBean
4 org. springframework . beans . factory. support . DefaultSingletonBeanRegistry
5 org. springframework . core . ReactiveAdapterRegistry
6 // Tomcat
org. apache. catalina. webresources .TomcatURLStreamHandlerFactory
8 //反序列化指定数据源
9 java. util. Currency|
Runtime:
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() {}
……
}