Java单例模式是一种常见的设计模式,旨在确保一个类只有一个实例,并提供一个全局访问点。这个设计模式在很多场景中非常有用,比如数据库连接池、日志记录类等。下面我们将详细讲解Java单例模式的创建、破坏和防破坏的攻略。
Java单例模式的创建
Java单例模式的创建有多种方式,以下是比较常见的两种:
静态变量
这种方式是单例模式创建的最简单方式,代码如下:
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
这里将instance变量定义为静态变量,并且在类加载的时候初始化。同时,将构造函数定义为私有,保证其他地方不能通过new操作符来创建实例,只能通过getInstance()方法来获取实例。
懒汉模式
这种方式是指当需要实例的时候才创建实例,代码如下:
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
这里将instance定义为null,并且在getInstance()方法中判断是否为null,如果为null则创建实例,否则直接返回实例。需要注意的是,为了保证线程安全,需要在getInstance()方法中加上synchronized关键字。
Java单例模式的破坏
Java单例模式是一种比较常见的设计模式,但是也容易被破坏,以下列举三种常见的破坏方式:
反射破坏
Java中的反射可以调用私有构造方法,创建多个对象。下面是示例代码:
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
Class cl = Singleton.class;
Constructor c = cl.getDeclaredConstructor();
c.setAccessible(true);
Singleton s1 = (Singleton)c.newInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2); // false
这里使用反射获取私有的构造方法,然后创建了两个实例,最终结果是s1和s2不相等。
序列化破坏
当一个单例类实现了Serializable接口并被序列化后,再次反序列化时会创建一个新的实例,相当于创建了两个实例。示例代码如下:
public class Singleton implements Serializable {
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
Singleton s1 = Singleton.getInstance();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("test.txt")));
oos.writeObject(s1);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("test.txt")));
Singleton s2 = (Singleton) ois.readObject();
System.out.println(s1 == s2); // false
多线程破坏
在多线程环境下,如果没有正确的实现线程安全,也可能会导致多个实例被创建。以下是示例代码:
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
public class ThreadTest extends Thread {
public void run(){
Singleton s = Singleton.getInstance();
System.out.println(s);
}
public static void main(String[] args) {
for(int i = 0; i < 10; i++){
new ThreadTest().start();
}
}
}
这里创建了10个线程,每个线程都调用Singleton.getInstance()方法获取实例。如果没有正确的实现线程安全,可能会导致多个实例被创建。
防止Java单例模式的破坏
为了防止Java单例模式的破坏,以下是三种常见的方式:
枚举
Java枚举是线程安全的,而且还能防止反射破坏和序列化破坏。示例代码如下:
public enum Singleton {
INSTANCE;
}
这里使用枚举来创建单例模式实例,枚举类型保证在任何情况下都只会创建一个实例。
饿汉模式
饿汉模式在类加载的时候就完成了初始化,因此线程安全。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
双重检验锁
这种方式在getInstance()方法中使用synchronized关键字来保证线程安全,同时使用volatile关键字来防止指令重排,保证实例的创建与初始化操作的有序性。
public class Singleton {
private static volatile Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
以上就是Java单例模式的创建、破坏和防破坏的完整攻略。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java单例模式的创建,破坏和防破坏详解 - Python技术站