Java 单例设计模式
1、懒汉模式
class LazySingleton{
//volatile防止指令重排
private static volatile LazySingleton SINGLETON ;
//构造方法私有化
private LazySingleton(){}
/*
双重检查锁DCL(Double Check Lock)
检查两次是否为null,再加一把锁
*/
public static LazySingleton getInstance(){
if (SINGLETON == null){
synchronized (LazySingleton.class){
if (SINGLETON == null){
SINGLETON = new LazySingleton();
}
}
}
return SINGLETON;
}
}
为什么加锁?
保证线程安全,防止多线程情况下多个线程同时执行新建对象方法。
为什么不使用synchronized直接锁getInstance()方法?
同一时间只能有一个线程访问方法,效率低下。
为什么加volatile关键字?
防止执行new MySingleton()时发生指令重排。
2、饿汉模式
/**
* 饿汉式--单例模式
*/
class HungrySingleton{
//类加载的是否就初始化了,不用担心线程安全
private static HungrySingleton SINGLETON = new HungrySingleton();
//构造方法私有化
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return SINGLETON;
}
}
缺点:
实例在类初始化一开始就被创建了,哪怕后来根本没有使用它。
3、静态内部类
/**
* 静态内部类(懒汉式)--单例模式
*/
class InnerClassSingleton{
//静态内部类创建对象
private static class InnerClass{
private static InnerClassSingleton singleton = new InnerClassSingleton();
}
private InnerClassSingleton(){}
public static InnerClassSingleton getInstance(){
return InnerClass.singleton;
}
}
通过JVM类加载来解决线程安全。
缺点:可以通过反射创建对象
4、反射攻击
我们通过静态内部类创建单例对象。虽然我们的构造方法是private的,当时仍然可以通过狗仔方法创建对象。
public class TestDemo3 {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//单例创建的对象
System.out.println(InnerClassSingleton.getInstance());
//通过反射创建对象
Constructor<InnerClassSingleton> constructor = InnerClassSingleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
InnerClassSingleton innerClassSingleton = constructor.newInstance();
System.out.println(innerClassSingleton);
}
}
com.zjw.InnerClassSingleton@1540e19d
com.zjw.InnerClassSingleton@677327b6
发现两个对象不一致。。
解决办法:在构造方法处检查对象是否创建
/**
* 静态内部类(懒汉式)--单例模式
* 解决反射创建问题
*/
class InnerClassSingleton{
//静态内部类创建对象
private static class InnerClass{
private static InnerClassSingleton singleton = new InnerClassSingleton();
}
//在构造方法检查对象是否创建
private InnerClassSingleton(){
if (InnerClass.singleton!=null){
throw new RuntimeException("单例已经创建,不能再新建了");
}
}
public static InnerClassSingleton getInstance(){
return InnerClass.singleton;
}
}
5、枚举类型
/**
* 枚举类型--单例模式
*/
public enum EnumSingleton {
SINGLETON;
public void print(){
System.out.println(this.hashCode());
}
}
public class TestDemo4 {
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
EnumSingleton singleton1 = EnumSingleton.SINGLETON;
EnumSingleton singleton2 = EnumSingleton.SINGLETON;
System.out.println(singleton1);
System.out.println(singleton2);
singleton1.print();
singleton2.print();
Constructor<EnumSingleton> constructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
EnumSingleton singleton = constructor.newInstance("SINGLETON", 0);
}
}
结果:
SINGLETON
SINGLETON
356573597
356573597
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
at com.zjw.TestDemo4.main(TestDemo4.java:17)
发现通过枚举类型创建的单例是不能通过反射创建的。
6、序列化
/**
* 反序列化--单例模式
*/
class HungrySingleton implements Serializable {
private static final long serialVersionUID = 23423454L;
private static HungrySingleton SINGLETON = new HungrySingleton();
//构造方法私有化
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return SINGLETON;
}
Object readResolve() throws ObjectStreamException{
return SINGLETON;
}
}
public class TestDemo2
{
public static void main(String[] args) throws IOException, ClassNotFoundException {
HungrySingleton instance = HungrySingleton.getInstance();
//序列化
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("instance"));
outputStream.writeObject(instance);
outputStream.close();
//反序列化
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("instance"));
HungrySingleton readSingleton = (HungrySingleton)inputStream.readObject();
inputStream.close();
//比较是否为同一个对象
System.out.println(instance==readSingleton);
}
}
不太懂,之后再研究~~~
---------------
我每一次回头,都感觉自己不够努力,所以我不再回头。
---------------