2.单例模式
- 饿汉式,DCL懒汉式,深究。
- 枚举避免单例模式被破坏
- 序列化与反序列化也能破坏单例
2.1 核心作用
- 保证一个类只有一个实例,并提供一个访问实例的全局访问点
2.2 应用场景
- win任务管理器
- win回收站
- 项目中读取配置文件的类,一般只有一个对象,没必要每次去new对象读取
- 网站计数器一般也会采用单例模式,可以保证同步
- 数据库连接池的设计一般也是单例模式
- 在servlet编程中,每个servlet也是单例的
- 在spring中,每个bean默认是单例的。
2.3 饿汉式
/***
* 饿汉式单例模式,浪费内存
*/
public class Hungry {
//构造器私有无法new对象
private Hungry(){
}
//先new出来这个对象
private static final Hungry hungry = new Hungry();
public static Hungry getInstance(){
return hungry;
}
}
@Test
public void test(){
for (int i = 0; i <= 100; i++) {
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
2.4 DCL懒汉式
/***
* 懒汉式单例模式
* 对象用的时候在加载,
* 避免内存浪费,但线程不安全
*/
public class LazyMan {
private LazyMan(){
System.out.println(Thread.currentThread().getName());
}
//volatile禁止指令重排序
private static volatile LazyMan lazyMan ;
//DCL双重检测 锁模式的懒汉式单例
public static LazyMan getInstance(){
//1
if(lazyMan == null){
synchronized (LazyMan.class){
//2
if(lazyMan == null){
//非原子性操作
lazyMan = new LazyMan();
//1、类的加载,jvm方法区是否有类信息;
// 2、分配内存空间(堆);
// 3、初始化零值,将分配的空间初始化;
// 4、设置对象头(对象结构);
// 5、执行构造方法(程序员写的)
/*
* 分配内存空间
* 执行构造方法,初始化对象
* 对象指向内存空间
* 指令重拍导致,lazyMan没完成构造,空间虚无
* */
}
}
}
return lazyMan;
}
}
@Test
public void test(){
for (int i = 0; i <= 100; i++) {
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
2.5 静态内部类
//静态内部类
public class InnerStaticClass {
//单例模式,构造器私有
private InnerStaticClass(){
}
public InnerStaticClass getInnerStaticClass(){
return InnerClass.innerStaticClass;
}
public static class InnerClass{
private static final InnerStaticClass innerStaticClass = new InnerStaticClass();
}
}
以上几种方式还是不安全的,因为反射破坏单例
2.6 枚举类
- 枚举自带单例
- 枚举类实际是只有一个有参构造器,没有无参构造。
- 底层源码禁止通过反射来实例枚举类
//枚举本身是个类
public enum EnumSingle {
INSTANCE;
public static EnumSingle getInstance(){
return INSTANCE;
}
}
//试图反射破坏枚举
@Test
public void test03() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
EnumSingle instance1 = EnumSingle.getInstance();
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println(instance1.hashCode()+"||"+instance2.hashCode());
}
java.lang.IllegalArgumentException: Cannot reflectively create enum objects
2.7 反编译过程
-
当用idea工具反编译的class类不准确时。
-
可以在命令行用javap -p xxx.class反编译命令进行反编译,但此命令有些时候反编译的class类也不准确时。
-
用工具jad.exe,那就生成的是Test.java文件,而-s<后缀名>允许你更改输出文件的后缀,例如-sjava就是把后缀名称更改成了.java
jad.exe是一个反编译的工具,需要把
jad.exe
放在class文件的同级目录下,然后在地址栏输入cmd
之后回车,如下图:
本专栏下一篇:设计模式之工厂模式
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/123934.html