要点
1.某一个类它只能用拥有一个实例
2.该类必须自己创建这个实例对象
3.这个实例需提供给整个系统
总结:单例模式的类只需要提供私有的构造函数,且类定义中含有一个此类的静态私有对象,并提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。
饿汉式
一种饿汉式最为简单,无论是否会用到该对象,在类被加载时立即创建对象实例。此方法简单但因为其占用内存的特性所以不推荐使用
public class Hungry {
//私有化构造函数
private Hungry(){
}
//创建唯一的对象实例,在类加载时立即创建
private static Hungry instance = new Hungry();
//获取唯一实例的方法
public static Hungry getInstance(){
return instance;
}
}
class SingleTonTest{
public static void main(String[] args) {
//创建实例对象
Hungry instance01 = Hungry.getInstance();
Hungry instance02 = Hungry.getInstance();
//判断两实例是否相等----ture
System.out.println(instance01 == instance02);
}
}
懒汉式
普通懒汉式
普通的懒汉式,在调用其获取实例对象的函数时再创建对象实例,避免了饿汉式中出现的对象不被调用却依然加载而占用内存的情况,实现了延时加载,但因使用了synchronized关键字,此方法执行效率会有所下降。
public class Lazy {
//私有化构造函数
private Lazy(){
}
//创建唯一的实例,但类加载时不立即创建对象,实现延时加载,降低了对内存的消耗
private static Lazy instance;
//获取实例对象
//synchronized保证多线程情况下的线程安全,但会一定程度降低效率
public static synchronized Lazy getInstance(){
//先判断有无创建过对象
if (instance == null){
instance = new Lazy();
}
//返回实例
return instance;
}
}
class SingleTonTest{
public static void main(String[] args) {
//创建实例对象
Lazy instance01 = Lazy.getInstance();
Lazy instance02 = Lazy.getInstance();
//判断两实例是否相等----ture
System.out.println(instance01 == instance02);
}
}
DCL懒汉式
DCL懒汉式通过改变synchronized锁的范围,相对于上一个模式效率会有所提升,同时volatile关键字也保障了实例对象的原子性
//DCL懒汉式 double clear lock双重检查锁 效率高于普通的懒加载模式
public class DCLLazy {
//私有化构造函数,使得其自能被自身调用
private DCLLazy(){
}
//创建唯一的实例,但类加载时不立即创建对象
// volatile用于保证instance的原子性,一个线程修改了对象后在另一个线程中立即生效,避免多线程操作下出现极端情况错误
private volatile static DCLLazy instance;
//获取实例对象
public static DCLLazy getInstance(){
//先判断有无创建过对象
if (instance == null){
//与普通懒加载相比,此处synchronized锁用于代码块而不是整个方法,效率会有所提升
synchronized (DCLLazy.class){
//再次判断有无创建对象
if (instance == null){
instance = new DCLLazy();
}
}
}
//返回实例
return instance;
}
}
class SingleTonTest01{
public static void main(String[] args) {
//创建实例对象
DCLLazy instance01 = DCLLazy.getInstance();
DCLLazy instance02 = DCLLazy.getInstance();
//判断两实例是否相等----ture
System.out.println(instance01 == instance02);
}
}
静态内部类实现单例
通过静态内部类的方式来实现单例模式,因未使用synchronized关键字,效率又有所提升,但java的反射机制可以通过破坏其私有性来破坏单例的属性,故而当下最推荐的是通过ENUM枚举类型来创建单例对象
public class InnerClass {
//私有构造函数
private InnerClass(){}
//通过静态内部类来创建单例对象
//该方式实现了延时加载的同时避免了synchronize带来的性能下降问题
private static class Inner{
//关键词final保证全局唯一
private static final InnerClass instance = new InnerClass();
}
//获取单例对象
public static InnerClass getInstance(){
return Inner.instance;
}
}
//虽然已经相对精密了 ,但java的反射机制可以破坏除枚举外的这些单例模式架构
class InnerClassDemo{
public static void main(String[] args) {
InnerClass instance01 = InnerClass.getInstance();
InnerClass instance02 = InnerClass.getInstance();
System.out.println(instance01 == instance02);
}
}
枚举实现单例模式
简单方便,且因为枚举自身的性质,在其被反射创建对象时会直接抛出异常,使得无法通过反射暴力破解的方式来破坏其单例的属性。
public enum BestSingleTon {
//直接创建实例,因为枚举的性质,它也不会被反射破坏其单例的限制
INSTANCE;
public static BestSingleTon getInstance(){
return INSTANCE;
}
}