题目:设计一个类,只能生成该类的一个实例。
分析:只能生成一个实例的类是实现了Singleton(单例)模式的类型,保证一个系统中的某个类只有一个实例而且该实例易于外界访问。
大体步骤:
1、 | 构造方法私有化(private) |
2、 | 定义一个私有的(private)静态(static)实例化对象 |
3、 | 对外提供一个公共的(public)静态(static)方法返回该实例 |
以下综合了收集到的各种方法进行了测试
package com.xlx.Offer;
/**
* @author 谢凌宣
*
* 《剑指Offer》面试题2:实现Singleton模式
* 要求:设计一个类,只能生成该类的一个实例,即实现单例模式
*
* 大体步骤:
* 1、构造方法私有化(private)
* 2、定义一个私有的(private)静态(static)实例化对象
* 3、对外提供一个公共的(public)静态(static)方法返回该实例
*/
public class Test02 {
/**
* 单例模式,饿汉式,线程安全
*
* 步骤:
* 1、定义一个私有的构造方法;
* 2、定义一个私有的实例化对象,并加上static和final修饰符;
* 3、然后通过公共的静态方法调用返回实例。
*
* 优点:简单,并且不会因为没有加synchronized关键字而造成线程不安全问题。
* 缺点:当该类被加载时,会初始化该实例和静态变量并被创建分配内存空间,会一直占用内存。
*/
public static class Singleton1 {
private Singleton1() {
//构造函数设为私有,以禁止他人创建实例,确保只创建一个实例
}
private final static Singleton1 INSTANCE = new Singleton1();
public static Singleton1 getInstance() {
return INSTANCE;
}
}
/**
* 单例模式,饱(懒)汉式,只用于单线程,多线程不安全
*
* 步骤:
* 1、定义一个私有的构造方法;
* 2、定义一个该类的静态私有变量;
* 3、然后定义一个公共的静态方法,接着对变量的值进行空值判定,为空则构建实例化对象,不为空则直接返回即可。
*
* 优点:简单,当第一次调用时才会被初始化,节省内存空间。
* 缺点:线程不安全,多个线程调用会出现多个实例。
*/
public static class Singleton2 {
private Singleton2() {
//构造函数设为私有,以禁止他人创建实例,确保只创建一个实例
}
private static Singleton2 instance = null;
public static Singleton2 getInstance() {
if(instance == null) {
instance = new Singleton2();
}
return instance;
}
}
/**
* 单例模式,饱(懒)汉式,线程安全
*
* 加synchronized关键字,保证线程安全
*/
public static class Singleton3 {
private Singleton3() {
//构造函数设为私有,以禁止他人创建实例,确保只创建一个实例
}
private static Singleton3 instance = null;
public static synchronized Singleton3 getInstance() {
if(instance == null) {
instance = new Singleton3();
}
return instance;
}
}
/**
* 单例模式,懒汉式变种,线程安全
*/
public static class Singleton4 {
private Singleton4() {}
public static Singleton4 instance = null;
static {
instance = new Singleton4();
}
public static Singleton4 getInstance() {
return instance;
}
}
/**
* 单例模式,使用静态内部类,线程安全(推荐)
*
* 步骤:
* 1、定义一个私有的构造方法;
* 2、定义一个该类私有的静态内部类,然后在内部类中定义该类的私有的静态内部变量;
* 3、通过公共的final修饰的静态方法调用并返回实例。
*
* 优点:因为内部类被定义为私有的,除了对外公布的公共静态方法getInstance(),其它是无法访问的。
* 又因为它是延迟加载的,所以读取实例的时候不会进行同步,几乎没有性能缺陷,还是线程安全的。
*/
public static class Singleton5 {
private Singleton5() {
//构造函数设为私有,以禁止他人创建实例,确保只创建一个实例
}
private final static class SingletonHolder {
private static final Singleton5 INSTANCE = new Singleton5();
}
public static final Singleton5 getInstance() {
return SingletonHolder.INSTANCE;
}
}
/**
* 使用枚举方式,线程安全(推荐)
*
* JDK1.5之后版本可用
*
* 优点:完美支持单例模式,并且线程安全,效率高,书写超级简单。
*/
public enum Singleton6 {
INSTANCE;
}
/**
* 使用静态内部类,使用双重校验锁,线程安全(推荐)
*
* 步骤:
* 1、定义一个私有的构造方法;
* 2、通过volatile定义一个静态私有变量,保证变量的可见性;
* 3、然后定义一个公共的静态方法,第一次对该对象实例时进行与否判断,不为空直接返回。
*
* 优点:内存占用低、效率高、线程安全、多线程操作原子性。
*/
public static class Singleton7 {
private Singleton7() {
//构造函数设为私有,以禁止他人创建实例,确保只创建一个实例
}
private volatile static Singleton7 instance = null;
public static Singleton7 getInstance() {
if(instance == null) {
synchronized(Singleton7.class) {
if(instance == null) {
instance = new Singleton7();
}
}
}
return instance;
}
}
public static void main(String[] args) {
System.out.println(Singleton1.getInstance() == Singleton1.getInstance());
System.out.println(Singleton2.getInstance() == Singleton2.getInstance());
System.out.println(Singleton3.getInstance() == Singleton3.getInstance());
System.out.println(Singleton4.getInstance() == Singleton4.getInstance());
System.out.println(Singleton5.getInstance() == Singleton5.getInstance());
System.out.println(Singleton6.INSTANCE == Singleton6.INSTANCE);
System.out.println(Singleton7.getInstance() == Singleton7.getInstance());
}
}