单例模式特点是只能创建一个对象,单例模式有四种类型,分别是饿汉式、懒汉式、静态内部类式、枚举式,下面将分别介绍。在介绍之前,先讲解一些辅助知识点铺垫。
#####1、理解静态成员、实例成员、局部变量什么时候被加载初始化
(1)静态成员:这里静态成员指的是类的静态变量和静态方法;当类加载的时候,静态成员也会被加载到内存中,直到类消失它才会从内存中消失。
(2)实例成员:实例成员是类的非静态变量和方法,只有类的对象被创建了,实例成员才会被加载到内存中,随着对象被回收,实例成员也会被回收。
(3)局部成员:通常指的是局部变量,局部变量是在方法内部,通常方法被调用,局部变量才会被加载,方法用完了,局部变量就会被回收了,生命周期很短。
#####2、饿汉式
(1)饿汉式的特点:线程安全,调用效率高,不能够延时加载
(2)代码展示:
/**
* 饿汉式
*
* @author limingxing
*
*/
class HungrySingleton {
// 类初始时,立即加载这个这个对象。
private static HungrySingleton instance = new HungrySingleton();
// 外部不能通过构造方法创建对象了
private HungrySingleton() {
}
// 只能通过getInstance方法获取此类对象
public static HungrySingleton getInstance() {
return instance;
}
}
public class Test{
public static void main(String[] args) {
HungrySingleton hs = HungrySingleton.getInstance();
HungrySingleton hs2 = HungrySingleton.getInstance();
System.out.println(hs);
System.out.println(hs2);
}
结果:
(3)代码解释:当这个类被用到时,就去会内存中寻找,要是内存中没有加载这个类,那么立即加载这个类,类初始化时,执行类的内部代码,静态成员就会被加载到内存中,这时instance对象就会被放到内存中,强调一点instance这时已经是一个HungrySingleton对象,方法getInstance也会被加载,因为是静态的。在主方法通过方法getInstance得到对象,getInstance方法返回的是instance,这时的instance在类初始后就是一个对象了,所有得到的就是一个对象。当第二次调用getInstance方法时,返回的是又是instance,因为内存中的instance还没有被回收,程序结束才会被回收,所有instance对象还是还是和上面一样,输出的地址值(哈希码)一样,保证了单例模式。之所以线程安全是因为线程还没启动时,类初始后instance对象就创建好了;高效率时,还是因为用的时候不要再加载,在用之前就加载到内存中了,直接用就好了;不能延迟加载也是因为类加载初始化instance对象就创建好了,还没来得及操作。
#####3、懒汉式
(1)懒汉特点是:线程安全,调用效率不高,可以延时加载,这里之所以说线程安全是因为用了synchronized关键字,要不然线程不安全,注意看清。
(2)代码
/**
* 懒汉式
* @author limingxing
*
*/
class LazySingleton {
//类初始化时,虽然有给instance分配内存,但是它还不是一个LazySingleton对象。
private static LazySingleton instance;
private LazySingleton() {
}
//有了synchronized关键字,线程就安全了。
public static synchronized LazySingleton getInstance() {
//如果是第一次通过方法得到对象,那么就会创建一个对象,后面就还是这个对象。
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
(3)代码解释:不再像前面那么详细了,自己学着分析。线程安全是因为有synchronized关键字,多个线程同时访问这个资源时,只能同步访问;效率不高因为后面还要创建对象,刚开始没有创建对象,创建好对象后,每次拿到这个对象,还要判定是否为空,最重要的是实现同步,意味着其它线程需要等待;延时加载因为你想什么创建,调用getInstance方法,达到控制延时,不像饿汉式类初始化就创建对象了。
#####4、静态内部类式
(1)静态内部类式特点:线程安全,调用效率高,可以延时加载。
(2)代码:
/**
* 静态内部类式
* @author limingxing
*
*/
class StaticInnerSingleton{
//静态内部类,只有静态内部类的静态属性被调用时,才会加载,可实现延时加载功能。
private static class Inner{
private static final StaticInnerSingleton instance = new StaticInnerSingleton();
}
private StaticInnerSingleton() {
}
//方法没有同步,调用效率高
public static StaticInnerSingleton getInstance() {
return Inner.instance;
}
}
(3)解释:线程安全是因为只要一个线程调用getInstance()方法,那么instance就是一个对象,而且用了static final修饰,保证了内存中只有这样一个实例存在,其它线程都是用instance对象。延迟加载因为静态内部类的静态内容只有被使用才会加载。高效是可以直接用,不需要等待,因为没有同步。
5、枚举式
(1)枚举式特点:线程安全,调用效率高,不能延时加载。
(2)代码:
public class Test4 {
public static void main(String[] args) {
EnumSingleton es = EnumSingleton.INSTANCE;
EnumSingleton es2 = EnumSingleton.INSTANCE;
System.out.println(es==es2);
}
}
enum EnumSingleton{
//类似public static final Enumsingleton instance = new EnumSingleton();
INSTANCE;
public void EnumSingleton() {
}
}
(3)解释和前面差不多,不再赘述。