概念:
java中单例模式是一种常见的设计模式,单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例,单例模式的写法有好几种,这里主要介绍三种 : 饿汉式单例、懒汉式单例、枚举实现单例。
单例模式有以下特点:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
一、饿汉式单例
Singleton1通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。
测试代码 :
测试结果
1567917652
1567917652
1567917652
破解方案 :
事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。
破解代码
测试结果
1320144062
2007692877
2031122075
饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。
二、懒汉式单例
以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下两种种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全 。
1).方法上加同步
2). 使用同步代码块 , 双重检查锁定
测试代码:
测试结果
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
三、枚举实现单例
测试代码
测试结果
2031122075
2031122075
2031122075
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
)
|
所以 , 对于以上实现单例的三种方式 , 枚举这种方式所实现的单例, 是线程安全的 , 是最简单的 , 也是最安全的方式 。