java单列模式的几种实现方式

单列模式(SIngleton pattern)是java中最基础的一种设计模式,他的作用是保证一个类仅有一个实例,并且提供一个访问它的全局访问点,避免重复创建对象,节省系统资源。以下通过五种方式来实现,可根据不同的场景选用。

一、饿汉式

public class Singleton {
    //类加载时就创建本类的对象
    private static Singleton instance = new Singleton();
    //构造器私有化,使外部不能通过new来创建对象
    private Singleton(){}
    //外部可通过该方法来获取唯一实例
    public static Singleton getInstance(){
        return instance;
    }
}

之所以称之为饿汉式,可以理解为:饿了,马上就要吃,在类加载的时候就创建对象,用时直接取就行。

缺点:在还不需要此类的实例的时候就已经创建好了,没有起到 lazy loading 的效果。

优点:简单、安全可靠

二、懒汉式

1、线程不安全
public class Singleton {
    //用静态变量来记录本类实例
    private static Singleton instance;
    //获取实例
    public static Singleton getInstance(){
        if(instance == null){
        	instance = new Singleton();
    	}
        return instance;
    }
    //构造器私有化
    private Singleton(){}
  
}

可以理解为:很懒、需要用时再创建,当一个对象使用频率不是很高、占用的内存特别大,饿汉式就明显不合适了,这时就需要懒汉式这种懒加载的思想,等到程序需要实例的时候再创建。上面这种实现方式是存在线不程安全问题,在并发获取实例的时候,可能会存在构建多个实例的情况,通过下面的改进可以实现线程安全:

2、线程安全
public class Singleton {
    private static volatile Singleton instance;
    //构造器私有化,禁止外部通过new创建实例
    private Singleton(){}
    
    public static Singleton getInstance(){
        if(instance == null){
            synchronized(Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        
        return instance;
    }
}

与上面的相比,这里使用了双重效验的方式,对懒汉式线程安全化,通过加锁,可以保证同时只有一个线程走到第二个判空代码中去,这样保证了只创建 一个实例。这里还用到了volatile关键字来修饰singleton,其最关键的作用是防止指令重排。

三、静态内部类式

public class Singleton {
    
    private static class SingletonHolder {
        private static Singleton instance = new Singleton();
    }
    
    private Singleton(){}
    
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
}

通过这种静态内部类的方式实现的单例模式是线程安全的,静态的内部类在Singleton加载的时候是不会加载的。在调用getInstance时才会创建Singleton的实例,起到了懒加载的作用。

以上方式实现单例模式看起来已经完美,但是还会存在一些安全问题,即反射攻击、反序列化攻击,下面就通过这两种方式来获取新的实例,并验证原来的实列和新的实例是否是一个实例。

反射获取新实例

public class Singleton {
    
    private static class SingletonHolder {
        private static Singleton instance = new Singleton();
    }
    
    private Singleton(){}
    
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }

    public static void main(String[] args) {

            Singleton singleton = Singleton.getInstance();
            Constructor<Singleton> constructor;
            try {
                constructor = Singleton.class.getDeclaredConstructor();
                constructor.setAccessible(true);
                Singleton newSingleton = constructor.newInstance();
                System.out.println(singleton == newSingleton);
            }catch (Exception e){

            }

        }
}

在这里插入图片描述
通过结果可以看出这两个实例不是同一个实例,这和我们的单例模式不符。

除了反射获取还可以通过反序列化的方式获取到新的实例:

反序列化方式获取新实例

public class Singleton implements Serializable {
    
    private static class SingletonHolder {
        private static Singleton instance = new Singleton();
    }
    
    private Singleton(){}
    
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
    
    public static void main(String[] args) {

        Singleton singleton = Singleton.getInstance();

        try {

            ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream(new File("./Singleton.txt")));
            //把对象写入磁盘
            oos.writeObject(singleton);
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("./Singleton.txt")));
            //从磁盘中读取对象
            Singleton newSingleton = (Singleton)ois.readObject();
			//比较
            System.out.println(singleton == newSingleton);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述
和反射获取的结果是一样的,下面通过枚举的方式实现解决上面这两个问题。

四、枚举方式

public enum EnumSingleton {
    
    INSTANCE;

    private EnumSingleton(){
        System.out.println("init");
    }

    public void sayHello(){
        System.out.println("hello");
    }

    public static void main(String[] args) {
        for (int i =0;i<5;i++){
            EnumSingleton.INSTANCE.sayHello();
        }

    }

}

在这里插入图片描述
结果可以看出实际只初始化了一次,这种方式也可以实现单例。利用枚举的特性,让JVM来帮我们保证线程安全和单一实例的问题。除此之外,写法还特别简单。

总结

以上通过了多中方式实现了单例模式,分析其的利弊,可根据场景选用。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Java中,单例模式可以通过以下步骤实现:1.声明一个私有的静态变量;2.提供一个公共的静态方法来获取该变量的值;3.在该方法中,如果变量为null,则创建一个实例,并将其赋值给该变量;4.返回该变量的值。 ### 回答2: 在Java中,实现单例模式方式有多种,下面介绍两种常用的方式。 1. 懒汉单例模式(Lazy Initialization) 懒汉是指在首次使用时才创建实例。具体实现如下: ```java public class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } ``` 在上述代码中,私有构造函数确保无法通过实例化来创建新的对象。通过静态方法`getInstance`获取实例,当第一次调用该方法时,会检查实例是否为空,如果为空,则创建新的实例并返回。 2. 饿汉单例模式(Eager Initialization) 饿汉是指在类加载时就创建实例。具体实现如下: ```java public class Singleton { private static Singleton instance = new Singleton(); private Singleton() {} public static Singleton getInstance() { return instance; } } ``` 在上述代码中,类加载时即创建并初始化了静态变量`instance`,因此在获取实例时无需再进行判断和创建,直接返回即可。 这两种方式各有优缺点,懒汉延迟加载但在多线程环境下可能存在线程安全问题,需要额外考虑同步机制;饿汉在类加载时就创建了实例,不会存在线程安全问题,但无法实现懒加载。开发者可以根据不同的需求选择适合的单例模式实现方式。 ### 回答3: Java单例模式可以通过以下几种方式实现: 1. 饿汉饿汉单例模式在类加载时就创建实例。具体实现方法是在类中直接创建一个私有的静态对象,并提供一个公有的静态方法来获取该对象。 代码示例: ``` public class Singleton { private static Singleton instance = new Singleton(); private Singleton() {} public static Singleton getInstance() { return instance; } } ``` 2. 懒汉懒汉单例模式在第一次使用时才会创建实例。具体实现方法是在类中提供一个私有的静态对象,并在获取对象的方法中进行判断,在需要时才进行初始化。 代码示例: ``` public class Singleton { private static Singleton instance; private Singleton() {} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } ``` 3. 双重检验锁(Double-Checked Locking): 双重检验锁单例模式是对懒汉单例模式进行改进,减少了获取单例对象时的锁竞争,提高了性能。 代码示例: ``` public class Singleton { private volatile static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } } ``` 以上是三种常用的单例模式实现方法,根据不同的需求选择合适的方式实现单例模式

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值