Java 饿汉式,懒汉式的单例模式(只能获取一个类的一个对象)
前言
提示:本节内容主要讲解Java中对于一个类只能获取一个对象的方法:分别是单例模式中的饿汉式和懒汉式,基于各自实现的代码以及区别将会在下面一 一讲述。
提示:以下是本篇文章正文内容,下面案例可供参考
一、一个类只能获取一个对象?
这句话的含义是:对于同一个类,不管是子类亦或是其他类,在实例化对象的时候有且只有一个对象,但是可以获取多次,其实也是获取到的是同一个对象。
举个例子:Windows中我们打开任务管理器的时候每次只能打开一个,(快捷键:CTRL + SHIFT + ESC),不会出现多开的情况。
二、实现思路:
- 如果只让外部获取到一个当前类实例化的对象,那么就不能在外部实例化对象,否则对象的数量将无法控制。所以我们需要将构造方法进行私有化(private)
- 其次,如果不在外部实例化对象,那么就要在类内部实例化对象,那么就可以将这个对象当做成员变量来看待,那么如何设置成员变量的修饰符呢?应该设置为(private static)
- 为什么这样设计呢?:
- 首先对于private来说:首先符合我们一贯的编程思维(成员变量私有化,成员方法公有化),其次是减少外部的可见性以及可操作性,这样才能保证我们只实例化一个对象而且永久可用的状态,否则如果是public的,外部可以直接获取到甚至修改对象,就无法保证以上所说的状态了。在饿汉式中也可以添加关键字final,让它是一个永远固定的值,内部也无法再次修改。
- 其次对于static来说:搭配private后,必须要使用get,set方法进行操作当前成员变量,但是如果不写这些方法的情况下,直接提升为类层级,在外部想要获取当前对象的时候可以不用new对象,直接使用静态方法将对象返回出去,但是静态方法只能调用静态内容,所以将成员变量修改为(static)
- 最后,编写一个公有的静态成员方法,可以在不创建对象的情况下使用,然后返回当前对象
三,代码实现
3.1 饿汉式
/**
* @author 云梦归遥
* @date 2021/11/22 10:05
* @description 只实例化一个对象,使用饿汉式,单例模式
*/
public class Singleton1 {
// 1.类内创建一个私有的,静态的类类型的对象
// 目的:
// 首先防止外部通过使用 Singleton1.singleton1 = null; 也避免内部修改,直接将对象报废(private,final)
// 其次是为了在类内部实例化一个对象,提升为类层级,外部便于使用静态方法返回当前对象,因为静态方法只能调用静态内容(static)
// 饿汉式:
// 一上来就实例化一个对象,当外部调用 get 方法时直接将对象返回出去
private final static Singleton1 singleton1 = new Singleton1();
// 2.私有化构造方法
// 目的:
// 只允许在类内实例化对象,如果可以在类外实例化对象,那么对象的数量就无法保证了
private Singleton1() {
}
// 3.提供公有的 get 方法将实例化的对象返回出去
public static Singleton1 getInstance() {
return singleton1;
}
}
3.2 懒汉式
/**
* @author 云梦归遥
* @date 2021/11/22 10:15
* @description 只实例化一个对象,使用懒汉式,单例模式
*/
public class Singleton2 {
// 1.类内创建一个私有的,静态的类类型的对象
// 目的:
// 首先防止外部通过使用 Singleton1.singleton1 = null; 将对象报废(private)
// 其次是为了在类内部实例化一个对象,提升为类层级,外部便于使用静态方法返回当前对象,因为静态方法只能调用静态对象(static)
// 懒汉式:
// 一开始不需要实例化对象,直到外部调用 get 方法的时候才实例化对象并返回出去
private static Singleton2 singleton2 = null;
// 2.私有化构造方法
// 目的:
// 只允许在类内实例化对象,如果可以在类外实例化对象,那么对象的数量就无法保证了
private Singleton2() {
}
// 3.提供公有的 get 方法将实例化的对象返回出去,synchronized 实行同步代码块,线程安全
public static synchronized Singleton2 getInstance() {
if (null == singleton2) {
singleton2 = new Singleton2();
}
return singleton2;
}
}
3.3 外部代码测试:
/**
* @author 云梦归遥
* @date 2021/11/22 10:22
* @description
*/
public class SingletonTest {
public static void main(String[] args) {
// 1.首先测试 饿汉式 的单例模式 创建对象
Singleton1 singleton11 = Singleton1.getInstance();
Singleton1 singleton12 = Singleton1.getInstance();
// 当你使用 IDEA 编写这行代码的时候就会自动提示 这个表达式永远为 true
System.out.println(singleton11 == singleton12); // true
System.out.println("===========================");
// 2.然后测试 懒汉式 的单例模式 创建对象
Singleton2 singleton21 = Singleton2.getInstance();
Singleton2 singleton22 = Singleton2.getInstance();
// 当你编写这行代码的时候不会自动提示,因为在编译器看来
// getInstance 方法会根据条件 返回不同的值,不是永远返回一个相同的值
System.out.println(singleton21 == singleton22); // true
System.out.println("===========================");
// 不会有小伙伴好奇要比较 懒汉式 和 饿汉式 的对象是否相同吧(这个问题不该问)
// 3.最后测试 懒汉式 和 饿汉式 两个单例模式 创建的对象 是否相同
// Error: java: 不可比较的类型: com.lagou.task02.Singleton1和com.lagou.task02.Singleton2
//System.out.println(singleton11 == singleton21);
//System.out.println(singleton11 == singleton22);
//System.out.println(singleton12 == singleton21);
//System.out.println(singleton12 == singleton22);
}
}
四,懒汉式和饿汉式区别
既然懒汉式和饿汉式都可以实现我们的目的,那为什么我们要写两种方式,一种不就解决了吗?接下拉我们来分析一下:
饿汉式:
- 一上来就实例化一个对象,当外部调用 get 方法时直接将对象返回出去
- 线程安全,对象早已经实例化好了,因为无论如何都只是同一个对象。
懒汉式:
- 一开始不需要实例化对象,直到外部调用 get 方法的时候才实例化对象并返回出去
- 线程不安全,因为不同线程在调用方法时对象状态仍然为null,大家都会实例化对象并返回,从而造成数据不同步,可以加上synchronized关键字实现同步代码块。
总结
以上就是Java实现懒汉式与饿汉式的思路分析,以及代码实现,对于区别,谢谢大家。