课程名称:Java设计模式精讲 Debug方式+内存分析
课程章节:第8章 单例模式讲解+Coding+源码解析
主讲老师:Geely
课程内容:
饿汉模式实现单例?
/**
* 饿汉式 : 比较饿,类加载的时候就初始化。
* 懒加载:调用才创建对象。
* 饿汉式:类加载就创建对象。
*/
public class HungrySingleton {
private static HungrySingleton _hungrySingleton_ = new HungrySingleton();
public static HungrySingleton getInstance(){
return _hungrySingleton_;
}
private HungrySingleton(){}
}
问题1)饿汉式和懒加载的区别?
饿汉式就是很饿,加载类就创建对象,懒加载只有调用的时候才初始化。
饿汉式是类加载就初始化。(如果始终都没用到该对象,会浪费资源。)
懒加载是被引用的时候初始化。(延迟加载,调用之后初始化)
类加载初始化的内容。决定了饿汉式还是懒汉式。
类加载分为三个步骤:加载,连接,初始化(这个初始化,只会初始化代码块和成员变量)
问题2)避免反射和反序列化带来的破坏?
枚举就是通过常量访问变量:
使用枚举创建一个对象:
1是枚举内部类,2是枚举常量(这里是一个值,访问的时候,是通过枚举类名.常量)
3是枚举变量 4是枚举类的构造函数。5是给枚举变量赋值(这个值是通过枚举常量来访问。)
6是通过一个方法返回实例,7是该类的私有构造函数,防止外部创建该类的实例。
使用反射,看创建的对象是否一致?发现反射的对象和直接获取的对象是一致的。
**Class clazz = EnumStarvingSingleton.****class;**
**Constructor constructor = clazz.getDeclaredConstructor()****;**
**constructor.setAccessible(****true****)****;**
**EnumStarvingSingleton enumStarvingSingleton = (EnumStarvingSingleton)constructor.newInstance()****;**
**System.**_**out**_**.println(enumStarvingSingleton.**_**getInstance**_**())****;**
使用反射直接获取枚举的对象。从而获取对应的实例。
其实反序列化也会创建多个对象。通过枚举类型创建单例是如何避免反序列化创建多个对象呢?
首先将类进行序列化存储,之后进行反序列化,就可以创建多个对象。但是通过枚举,反序列化也会创建一个对象。是通过
反编译工具:jad
先通过javac编译生成对应class 文件。
再通过jad class文件全路径。就会生成jad文件。
问题3)枚举类型的定义和使用方式?
枚举分为:
-
常量部分
-
变量部分。
-
构造函数。
-
Get/set返回常量值。
例子1:
public enum MyDay {
**MONDAY(1,"星期一"),THUSDAY(2,"星期二");//这个后面必须有分号**
**private int code;**
**private String name;**
**private MyDay(int code,String name) {**
**this.code = code;**
**this.name = name();**
**}**
public int getCode() {
return code;
}
public String getName() {
return name;
}
public void setCode(int code) {
this.code = code;
}
public void setName(String name) {
this.name = name;
}
}
**例如2:**
**public** **enum** **Color {**
**RED,BLANK,YELLOW**
**}**
**使用**
**System.out.println(Color.BLANK);**
问题4)double check和volatile的作用?
进行第二次判空的原因是,有两个线程同时通过了第一次判断,第一个线程创建了对象,释放了锁,第二个线程获取锁,如果不判断,还会再创建一个对象。
使用volatile的原因是,new 的时候是分三步,第三步和第二步因为不存在依赖关系,所以可以发送排序,也就是先让instance指向了内存空间,该内存空间还未进行初始化,就会让其他线程获取到null的情况。所以使用volatile避免重排序。
注意:无论饿汉模式,还是懒汉模式。都可以通过反射获取到类的实例。也就是通过反射重新创建一个对象。
问题5)使用Java9,设计单例模式?
在cpu中的指令:A,B不能重排序,C,D之间也不可以重排序,也就是在当前volatile的作用域内,所有的指令是不允许做重排序。
使用setRelease也是为了防止重排序,它能够仅仅防止当前语句重排序,不会防止它前后的语句重排序。
例如:它可以让A,B 之间重排序,可以让C,D之间重排序,但是不允许A,B和C,D之间有重排序