单例模式
单例模式是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
ps:博客有多种实现,内容较长,对代码有兴趣的可以上git:git地址,客官开心的话可以点赞
要点
一是某个类只能有一个实例:构造器私有化
二是它必须自行创建这个实例:含有一个该类的静态变量来保存这个唯一的实例
三是它必须自行向整个系统提供这个实例:(1)直接暴露(2)用静态变量的get方法获取
常见形式
- 饿汉式:直接创建对象,不存在线程安全问题
1.直接实例化对象(简介直观)
2.枚举类(最简洁)
3.静态代码块(返回复制化实例化)
- 懒汉式:延迟创建对象,存在线程安全问题
1.线程不安全(适用单线程)
2.线程安全(适用多线程)
3.静态内部类形式创建(适用多线程)
代码加注释深入理解
饿汉式1实现
/**
* @author 小鱼
* @version 1.0
* @date 2021/8/14 11:21 上午
* 饿汉式:直接创建对象,不存在线程安全问题
* 方式1.直接实现实例化对象,最明确
* 实现步骤:
* 1.创建一个静态的常量的类对象
* 2.构造器私有化,不允许外部创建
*/
public class SingletonHungryOne {
public static final SingletonHungryOne INSTANCE=new SingletonHungryOne();
private SingletonHungryOne(){
}
}
验证
/**
* @author 小鱼
* @version 1.0
* @date 2021/8/14 11:31 上午
* 测试饿汉式1是否正确
*/
public class TestOne {
public static void main(String[] args) {
SingletonHungryOne s1=SingletonHungryOne.INSTANCE;
SingletonHungryOne s2=SingletonHungryOne.INSTANCE;
System.out.println(s1.equals(s2));
System.out.println(s1==s2);
//判断对象hashCode是否相等
System.out.println("s1对象hashCode: "+s1.hashCode());
System.out.println("s2对象hashCode: "+s2.hashCode());
}
}
饿汉式2实现
使用枚举的特性创建单例
/**
* @author 小鱼
* @version 1.0
* @date 2021/8/14 12:00 下午
* 饿汉式:
* 方式2:使用枚举类型创建:
* 因为枚举类型表示该类型的对象是有限的几个,我们可以限定一个,那么就成了单例了
* 让JVM来帮我们保证线程安全和单一实例的问题。
* 我们只需要在类里面第一行添加INSTANCE;就实现了单例了
*/
public enum SingletonHungryTwo {
INSTANCE;
public void doSomething() {
System.out.println("doSomething");
}
}
验证
/**
* @author 小鱼
* @version 1.0
* @date 2021/8/14 11:31 上午
* 测试饿汉式2是否正确
*/
public class TestTwo {
public static void main(String[] args) {
SingletonHungryTwo s1=SingletonHungryTwo.INSTANCE;
SingletonHungryTwo s2=SingletonHungryTwo.INSTANCE;
System.out.println(s1.equals(s2));
System.out.println(s1==s2);
//判断对象hashCode是否相等
System.out.println("s1对象hashCode: "+s1.hashCode());
System.out.println("s2对象hashCode: "+s2.hashCode());
s1.doSomething();
s2.doSomething();
}
}
懒汉式1实现
线程不安全
/**
* @author 小鱼
* @version 1.0
* @date 2021/8/14 12:13 下午
* 懒汉式:
* 方式1:线程不安全
* 1.构造器私有化
* 2.用一个静态变量保存这个唯一实例
* 3.提供一个静态方法获取这个唯一实例
*/
public class SingletonLazyOne {
private SingletonLazyOne(){
}
private static SingletonLazyOne singletonLazyOne;
public static SingletonLazyOne getInstance(){
if(singletonLazyOne==null){
return singletonLazyOne= new SingletonLazyOne();
}
return singletonLazyOne;
}
}
验证
/**
* @author 小鱼
* @version 1.0
* @date 2021/8/14 11:31 上午
* 测试懒汉式1是否正确
*/
public class TestLazyOne {
public static void main(String[] args) {
SingletonLazyOne s1=SingletonLazyOne.getInstance();
SingletonLazyOne s2=SingletonLazyOne.getInstance();
System.out.println(s1.equals(s2));
System.out.println(s1==s2);
//判断对象hashCode是否相等
System.out.println("s1对象hashCode: "+s1.hashCode());
System.out.println("s2对象hashCode: "+s2.hashCode());
}
}
懒汉式2实现
线程安全单例,但是可以通过反射和反序列化
进行内部破坏
/**
* @author 小鱼
* @version 1.0
* @date 2021/8/14 12:13 下午
* 懒汉式:
* 方式2:线程安全
* 1.构造器私有化
* 2.用一个静态变量保存这个唯一实例(为了防止指令重排,我们加入了volatile关键字)
* 3.提供一个对外创建对象的安全的静态方法
*
*/
public class SingletonLazyTwo {
private SingletonLazyTwo(){
}
private static volatile SingletonLazyTwo singletonLazyTwo;
public static SingletonLazyTwo getInstance(){
if(singletonLazyTwo==null){
synchronized (SingletonLazyTwo.class){
if(singletonLazyTwo==null){
return singletonLazyTwo= new SingletonLazyTwo();
}
}
}
return singletonLazyTwo;
}
}
验证
/**
* @author 小鱼
* @version 1.0
* @date 2021/8/14 11:31 上午
* 测试懒汉式2是否正确
*/
public class TestLazyTwo {
public static void main(String[] args) {
SingletonLazyTwo s1=SingletonLazyTwo.getInstance();
SingletonLazyTwo s2=SingletonLazyTwo.getInstance();
System.out.println(s1.equals(s2));
System.out.println(s1==s2);
//判断对象hashCode是否相等
System.out.println("s1对象hashCode: "+s1.hashCode());
System.out.println("s2对象hashCode: "+s2.hashCode());
}
}
懒汉式3
静态内部类的形式:线程安全
/**
* @author 小鱼
* @version 1.0
* @date 2021/8/14 12:13 下午
* 懒汉式:
* 方式3:
* 在内部类被加载和初始化时,才创建INSTANCE实例对象
* 静态内部类不会自动随着外部类的加载和初始化而初始化,它是要单独去加载和初始化的。
*
*/
public class SingletonLazyThree {
private SingletonLazyThree(){
}
public static SingletonLazyThree getInstance(){
return Instance.instance;
}
private static class Instance{
private static final SingletonLazyThree instance=new SingletonLazyThree();
}
}