java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例、饿汉式单例、登记式单例三种。
单例模式有一下特点:
1、单例类只能有一个实例。
2、单例类必须自己自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
java主要使用的单例模式有:
1.饿汉式
public class Singleton {
/**
* 单例对象()
*/
private static fianl Singleton uniqueInstance=new Singleton();
/**
* 构造方法私有
*/
private Singleton() {
}
public static Singleton getUniqueInstance() {
return uniqueInstance;
}
}
优点:简单暴力。没有线程安全问题。
缺点:过于简单,面试官不买账,没有延迟加载,初期内存使用增加
2.懒汉式(普遍版)
public class Singleton {
/**
* 单例对象(volatile实现变量的可见性)
*/
private static volatile Singleton uniqueInstance;
/**
* 构造方法私有
*/
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
// new Singleton()可能会因为重排序而产生线程安全问题
uniqueInstance = new Singleton();
System.out.println(Thread.currentThread().getName() + " :get a new singleton");
}
}
}
return uniqueInstance;
}
}
优点:看起来高大上- -。 用了延迟加载+双重检验+加锁+volatile保证有序性,大部分面试希望看到的代码
缺点:比较麻烦,效率又不是最高的。由于是大多数的普遍会写的版本有些细节还不到位,下文会继续优化。
3. 懒汉式(可序列化版)
public class Singleton implements Serializable{
/**
* 单例对象(volatile实现变量的可见性)
*/
private static volatile Singleton uniqueInstance;
/**
* 构造方法私有
*/
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
//new Singleton()可能会因为重排序而产生线程安全问题
uniqueInstance = new Singleton();
System.out.println(Thread.currentThread().getName() + " :get a new singleton");
}
}
}
return uniqueInstance;
}
private Object readResolve() throws ObjectStreamException {
//反序列化调用该方法产生新的实例,反正产生多个实例。
return uniqueInstance;
}
}
加个方法反正反序列化对象时产生多个实例。保证单例模式。(面试官看到了可能会加分哦)
最后由于反射的存在,即使类的构造方法设成私有还是会可以通过反射进行构造出第二个实例。所以要在构造方法上加个判断
(一般不建议在构造方法中加逻辑哦)
public class Singleton implements Serializable {
/**
* 单例对象(volatile实现变量的可见性)
*/
private static volatile Singleton uniqueInstance;
/**
* 构造方法私有 (用判断防止反射创建实例)
*/
private Singleton() {
if (uniqueInstance != null) {
throw new RuntimeException("不能创建第二个实例");
}
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
//new Singleton()可能会因为重排序而产生线程安全问题
uniqueInstance = new Singleton();
System.out.println(Thread.currentThread().getName() + " :get a new singleton");
}
}
}
return uniqueInstance;
}
private Object readResolve() throws ObjectStreamException {
//反序列化调用该方法产生新的实例,反正产生多个实例。
return uniqueInstance;
}
}
当面试官考你单例模式的时候,写这个代码会让面试官眼前一亮,很有效果哦。
但是,这个却不是最好的单例办法。性能安全性都比较高的单例模式有以下两种实现:
1.枚举类实现 。由于比较简单,虽然安全和效率都很高,但是大多数人都不喜欢这个方式,也很少人用,个人不建议使用。
2.静态内部类实现。安全效率也得到保障,大多数人喜欢使用这种方法,效率比懒汉式高一些。
静态内部类实现
/**
* @author Liquid
* @类名: Singleton2
* @描述: 内部类单例模式,安全效率都有保障
* @date 2019/3/26
*/
public class Singleton2 {
/**
* 内部类创建实例(内部类只在线程第一次访问时初始化化(延迟加载)
*/
private static class SingletonClass {
private static Singleton2 uniqueInstance = new Singleton2();
}
/**
* 构造方法私有 (用判断防止反射创建实例)
*/
private Singleton2() {
if (SingletonClass.uniqueInstance != null) {
throw new RuntimeException("不能创建第二个实例");
}
}
/**
* 获取单例对象,jvm会自动保证内部类中实例的线程安全
*/
public static Singleton2 getUniqueInstance() {
return SingletonClass.uniqueInstance;
}
private Object readResolve() throws ObjectStreamException {
//反序列化调用该方法产生新的实例,反正产生多个实例。
return SingletonClass.uniqueInstance;
}
}
个人推荐这种方法哦,如果面试官问你单例模式,别忘了告诉他这个安全又高效的解决方案.
------Liquid