设计模式-创建型
单例模式(注意这里是已经创建出的实例)
1、通俗理解: 无论在什么岛屿或国家,国王只有唯一一个
2、特点:
2.1、只有一个实例。
2.2、能够自我实例化。
2.3、提供全局访问点。
3、使用场景: 线程池、缓存、数据库连接
4、结构:
4.1、单例类:包含一个实例且能自行创建这个实例的类。
4.2、访问类:使用单例的类。
5、实现示例
5.1、饿汉式:一个王国成立的同时国王也就已经生成了。
java
public class King {
/**
* 全局属性,体现一下多线程下的不安全性。
*/
private String param;
/**
*直接创建实例
*基础知识:
* final常量,只被赋值一次。
* static静态变量,只要程序加载了类的字节码文件,不用创建任何实例象,静态变量就会被分配空
* 间,静态变量就可以被使用了
*/
private static final King king = new King();
/**
* 私有无参构造,不允许其他类通过King的构造器创建实例
*/
private King(){}
/**
* 对外开放一个方法让其获取内存中唯一的实例
*/
public King getKing(){
return king;
}
/**
* set方法
* @param param
*/
public void setParam(String param){
this.param = param;
}
/**
* get方法
* @return String
*/
public String getParam(){
return this.param;
}
}
5.2、懒汉式:大家一起打下了天下,这时还没有国王。各自过各自的生活。等有一天其他的部落来侵略,需要有人统领大家反抗时才会创建一个国王。
java
public class Lazy_Singleton {
/**
* 在单例对象占用资源大,需要延时加载的情况下优选。
* 1、使用时生成类
* 2.volatile关键字防止指令重排
* 字节码层:
* JIT、CPU在执行过程中经历下面不在
* (1)、分配空间
* (2)、初始化
* (3)、引用赋值
* (2)与(3)执行顺序有可能颠倒
*/
private volatile static Lazy_Singleton instance ;
/**
* 功能描述: 创建一个私有的构造,防止被其他类通过构造器实例化
*/
private Lazy_Singleton(){ }
/**
* 静态工厂方法,创建实例
*/
public static Lazy_Singleton getInstance() {
/*先判断需要实例化的对象是否空.如果不为空就返回,这样可以提供一些性能,无需再加锁等待。*/
if(null == instance){
//加锁,防止多线程同时访问,第一个线程还没有创建完,第二个有来创建的问题。(这里性能
//会有问题)
synchronized (Lazy_Singleton.class){
/*判断是否为空*/
if(null == instance){
instance = new Lazy_Singleton();
}
return instance;
}
}
return instance;
}
/**
* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致
*/
public Object readResolve() {
return getInstance();
}
}
5.3.静态内部类:与懒汉式一样使用时创建
public class Instonce_Singleton {
private static class InstanceHolder{
private static Instonce_Singleton instance = new Instonce_Singleton();
}
public Instonce_Singleton getInstance(){
return InstanceHolder.instance;
}
}
5.4、枚举类:
这里首先介绍一下枚举类
枚举类学习链接:
- 通过对枚举的了解,下面我们进一步了解什么枚举实现单例是一个理想的方式。
- 首先枚举类是不能通过获取无参构造器反射生成的。应为Constructor类的newInstance方法:有这样的判断
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor;
- 枚举类中的成员被编译后是加了static的。
- 反序列化的时候则是通过 java.lang.Enum 的 valueOf() 方法来根据名字查找枚举对象。也就是说,序列化的时候只将 成员的 名称输出,反序列化的时候再通过这个名称,查找对于的枚举类型,因此反序列化后的实例也会和之前被序列化的对象实例相同。
- 补充: 枚举类都继承自Enum,Enum中有一个只有一个参数为(String.class,int.class)构造器。可以看Enum源码发现,这两个参数是name和ordial两个属性。
- 下面看一下枚举实现单例,
java:
public enum EnumSingleton {
INSTANCE;
public EnumSingleton getInstance(){
return INSTANCE;
}
public void sayHello(){
System.out.print("sayHello!");
}
}
//调用
EnumSingleton singleton1=EnumSingleton.INSTANCE;
EnumSingleton singleton1=EnumSingleton.INSTANCE;
singleton1.sayHello();
单例基本也就这些了。