单例设计模式实现方式及破解方案

本文详细介绍了Java中单例模式的实现方式,包括饿汉式单例、懒汉式单例及枚举实现单例。重点讲解了每种实现的特点、线程安全性以及可能存在的问题。
概念:
  java中单例模式是一种常见的设计模式,
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例,单例模式的写法有好几种,这里主要介绍三种 : 饿汉式单例、懒汉式单例、枚举实现单例。

单例模式有以下特点:
  1、单例类只能有一个实例。
  2、单例类必须自己创建自己的唯一实例。
  3、单例类必须给所有其他对象提供这一实例。


一、饿汉式单例
[Java]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
public class Singleton1 {
         
         private static Singleton1 s1 = new Singleton1();
         
         private Singleton1(){
                 
         }
         
         public static Singleton1 getInstance(){
                 return s1;
         }
}


Singleton1通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。

测试代码 : 
[Java]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
public class Singleton1Demo {
         
         public static void main(String[] args) throws Exception {
                 
                 Singleton1 s1 = Singleton1.getInstance();
                 Singleton1 s2 = Singleton1.getInstance();
                 Singleton1 s3 = Singleton1.getInstance();
                 
                 System.out.println(s1.hashCode());
                 System.out.println(s2.hashCode());
                 System.out.println(s3.hashCode());
                 
         }
         
}

测试结果

1567917652
1567917652
1567917652

破解方案 :
事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。

破解代码
[Java]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
public class Singleton1Demo {
         
         public static void main(String[] args) throws Exception {
                 
                 Class single1 = Class.forName( "cn.itcast.single.Singleton1" );
                 Constructor constructor = single1.getDeclaredConstructor();
                 constructor.setAccessible( true );
                 
                 Object object1 = constructor.newInstance();
                 Object object2 = constructor.newInstance();
                 Object object3 = constructor.newInstance();
                 
                 System.out.println(object1.hashCode());
                 System.out.println(object2.hashCode());
                 System.out.println(object3.hashCode());
                 
         }
         
}


测试结果
1320144062
2007692877
2031122075

饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。

二、懒汉式单例
[Java]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
public class Singleton2 {
         
         private static Singleton2 s2 = null ;
         
         private Singleton2(){
                 
         }
         
         public static Singleton2 getInstance(){
                 if (s2== null ){
                         s2 = new Singleton2();
                 }
                 return s2;
         }
         
}


以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下两种种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全 。

1).方法上加同步
[Java]  纯文本查看  复制代码
?
1
2
3
4
5
6
public static synchronized Singleton2 getInstance(){
         if (s2== null ){
                 s2 = new Singleton2();
         }
         return s2;
}


2). 使用同步代码块 , 双重检查锁定
[Java]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
public static Singleton2 getInstance(){
                 if (s2== null ){
                         synchronized (Singleton2. class ) {
                                 if (s2== null ){
                                         s2 = new Singleton2();
                                 }
                         }
                 }
                 return s2;
         }


测试代码:

[Java]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
public class Singleton2Demo {
         
         public static void main(String[] args) throws Exception {
                 
                 Singleton2 s1 = Singleton2.getInstance();
                 Singleton2 s2 = Singleton2.getInstance();
                 Singleton2 s3 = Singleton2.getInstance();
                 
                 System.out.println(s1.hashCode());
                 System.out.println(s2.hashCode());
                 System.out.println(s3.hashCode());
                 
         }
         
}


测试结果
1537587829
1537587829
1537587829

破解方案 :
事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。

破解代码

public class Singleton2Demo {
        
        public static void main(String[] args) throws Exception {
                
                Class single2 = Class.forName("cn.itcast.single.Singleton2");
                Constructor[] constructors = single2.getDeclaredConstructors();
                constructors[0].setAccessible(true);
                
                Object object1 = constructors[0].newInstance();
                Object object2 = constructors[0].newInstance();
                Object object3 = constructors[0].newInstance();
                
                System.out.println(object1.hashCode());
                System.out.println(object2.hashCode());
                System.out.println(object3.hashCode());
        }
        
}

测试结果
2007692877
2031122075
668661813

三、枚举实现单例

[Java]  纯文本查看  复制代码
?
1
2
3
4
5
public enum Singleton3 {
         
         SINGLE;
         
}


测试代码
[Java]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
public class Singleton3Demo {
         
         public static void main(String[] args) throws Exception {
                 
                 Singleton3 s1 = Singleton3.SINGLE;
                 Singleton3 s2 = Singleton3.SINGLE;
                 Singleton3 s3 = Singleton3.SINGLE;
                 
                 System.out.println(s1.hashCode());
                 System.out.println(s2.hashCode());
                 System.out.println(s3.hashCode());
                 
         }
         
}


测试结果

2031122075
2031122075
2031122075

简简单单的一点代码就实现了一个线程安全,与其说是写法鬼斧神工,不如说是恰如其分地应用了enum的性质。
enum有且仅有private的构造器,防止外部的额外构造,这恰好和单例模式吻合,也为保证单例性做了一个铺垫。这里展开说下这个private构造器,如果我们不去手写构造器,则会有一个默认的空参构造器。
而且枚举类型实现的单例, 是不可以通过反射进行破解的 , 测试代码如下:
[Java]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Singleton3Demo {
         
         public static void main(String[] args) throws Exception {
                 
                 Class single3 = Class.forName( "cn.itcast.single.Singleton3" );
                 Constructor[] constructors = single3.getDeclaredConstructors();
                 Constructor constructor = constructors[ 0 ];
                 
                 constructor.setAccessible( true );
                 
                 Object object1 = constructor.newInstance();
                 Object object2 = constructor.newInstance();
                 Object object3 = constructor.newInstance();
                 
                 System.out.println(object1);
                 System.out.println(object2);
                 System.out.println(object3);
                 
                 
         }
         
}

测试结果
[Java]  纯文本查看  复制代码
?
1
2
3
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
     at java.lang.reflect.Constructor.newInstance(Constructor.java: 521 )
     at cn.itcast.single.test.Singleton3Demo.main(Singleton3Demo.java: 17 )

所以 , 对于以上实现单例的三种方式 , 枚举这种方式所实现的单例, 是线程安全的 , 是最简单的 , 也是最安全的方式 。
乐播投屏是一款简单好用、功能强大的专业投屏软件,支持手机投屏电视、手机投电脑、电脑投电视等多种投屏方式。 多端兼容与跨网投屏:支持手机、平板、电脑等多种设备之间的自由组合投屏,且无需连接 WiFi,通过跨屏技术打破网络限制,扫一扫即可投屏。 广泛的应用支持:支持 10000+APP 投屏,包括综合视频、网盘与浏览器、美韩剧、斗鱼、虎牙等直播平台,还能将央视、湖南卫视等各大卫视的直播内容一键投屏。 高清流畅投屏体验:腾讯独家智能音画调校技术,支持 4K 高清画质、240Hz 超高帧率,低延迟不卡顿,能为用户提供更高清、流畅的视觉享受。 会议办公功能强大:拥有全球唯一的 “超级投屏空间”,扫码即投,无需安装。支持多人共享投屏、远程协作批注,PPT、Excel、视频等文件都能流畅展示,还具备企业级安全加密,保障会议资料不泄露。 多人互动功能:支持多人投屏,邀请好友加入投屏互动,远程也可加入。同时具备一屏多显、语音互动功能,支持多人连麦,实时语音交流。 文件支持全面:支持 PPT、PDF、Word、Excel 等办公文件,以及视频、图片等多种型文件的投屏,还支持网盘直投,无需下载和转格式。 特色功能丰富:投屏时可同步录制投屏画面,部分版本还支持通过触控屏或电视端外接鼠标反控电脑,以及在投屏过程中用画笔实时标注等功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值