饿汉式:类加载就会导致该单实例对象被创建
懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会被创建
饿汉式实现
1、 饿汉式方式1 静态成员变量方式
public class Singleton{
//1.私有构造方法,外界无法创建对象
private Singleton(){}
//2. 在本类中创建本类对象
private static Singleton instance = new Singleton();
//3. 提供一个公共的访问方式,让外界获取该对象。静态方法只能访问静态变量
public stataic Singleton getInstance(){
return instance;
}
}
2、饿汉式方式2 静态代码块方式
public class Singleton{
private Singleton(){}
//只是申明Singleton类型的变量,没有赋值创建变量,默认为null
private static Singleton instance;
//在静态代码块中进行赋值
static {
instance = new Singleton();
}
public static Singleton getInstance(){
return instance;
}
}
饿汉式缺点:类加载但不使用会造成内存浪费
懒汉式实现
1、线程不安全
public class Singleton{
private Singleton(){}
//申明Singleton类型的变量,没有赋值,默认为null
private static Singleton instance;
//3. 提供一个公共的访问方式,让外界获取该对象。静态方法只能访问静态变量
public static Singleton getInstance(){
// 判断instance是否为null,如果为null表示还没有创建
// 如果没有创建,就创建一个,否则就直接返回
if(instance == null){
// 多个线程可能都会进入判断
instance = new Singleton();
}
return instance;
}
}
2、线程安全
public class Singleton{
private Singleton(){}
//申明Singleton类型的变量,没有赋值,默认为null
private static Singleton instance;
//3. 提供一个公共的访问方式,让外界获取该对象。静态方法只能访问静态变量
public static synchronized Singleton getInstance(){
// 判断instance是否为null,如果为null表示还没有创建
// 如果没有创建,就创建一个,否则就直接返回
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
缺点:加锁性能低下
3、双重检查锁
public class Singleton{
private Singleton(){}
//申明Singleton类型的变量,没有赋值,默认为null
private static Singleton instance;
//3. 提供一个公共的访问方式,让外界获取该对象。静态方法只能访问静态变量
public static synchronized Singleton getInstance(){
// 第一次判断如果instance的值不为null,不需要抢占锁,直接返回
if(instance == null){
synchronized (Singleton.class){
// 第二次判断是否为null
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
缺点:在多线程情况下,可能出现空指针情况,出现原因是JVM优化和指令重排,解决方法是使用volatile关键字保证可见性和有序性
最终版本:
推荐使用
public class Singleton{
private Singleton(){}
//申明Singleton类型的变量,没有赋值,默认为null
private static volatile Singleton instance;
//3. 提供一个公共的访问方式,让外界获取该对象。静态方法只能访问静态变量
public static synchronized Singleton getInstance(){
// 第一次判断如果instance的值不为null,不需要抢占锁,直接返回
if(instance == null){
synchronized (Singleton.class){
// 第二次判断是否为null
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
4、静态内部类方式
推荐使用
JVM在加载外部类的过程中不会加载静态内部类,只有内部类的属性/方法被调用时才会被加载
public class Singleton{
private Singleton(){}
// 定义一个静态内部类
private static class SingletonHolder{
// 在内部类中申明并定义外部类的对象,防止外部修改对象加一个final
private static final Singleton INSTANCE = new Singleton();
}
// 公共访问方式
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
静态内部类单例模式是开源项目中常用的一种单例模式,不需要加锁也可以保证多线程下的安全
5、枚举方式
枚举方式是极力推荐的单例实现方式,因为枚举类型是线程安全的,并且只会加装一次,是所有单例实现中唯一一种不会被破坏的单例实现模式
推荐使用
public enum Singleton{
INSTANCE;
}
单例模式存在的问题
破坏单例模式:
单例类可以创建多个对象,枚举方式除外,分别是序列化和反射。
1、序列化反序列化 Singleton类实现Serializable接口
2、反射方式:
public class Client{
public static void main(String[] args) throws Exception{
// 获取Singleton的字节码对象
Class clazz = Singleton.class;
// 获取无参构造方法
Constructor cons = clazz.getDeclaredConstructor();
// 取消访问检查
cons.setAccessible(true);
Singleton s1 = (Singleton)cons.newInstance();
Singleton s2 = (Singleton)cons.newInstance();
System.out.println(s1 == s2); // false;
}
}
解决方法
1、序列化反序列化可以添加一个readResolve()方法
public class Singleton{
private Singleton(){}
// 定义一个静态内部类
private static class SingletonHolder{
// 在内部类中申明并定义外部类的对象,防止外部修改对象加一个final
private static final Singleton INSTANCE = new Singleton();
}
// 公共访问方式
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
// 当进行反序列化时,会自动调用该方法,将调用方法的返回值直接返回
public Object readResolve(){
return SingletonHolder.INSTANCE;
}
}
2、解决反射方式
public class Singleton{
private static boolean flag = false;
private Singleton(){
synchronized (Singleton.class){
// 判断flag的值是否是true,如果是true,说明非第一次访问,直接抛异常,如果是false说明第一次访问
if(flag) {
throw new RuntimeException("不能创建多个对象");
}
// 将flag的值设置为true
flag = true;
}
}
// 定义一个静态内部类
private static class SingletonHolder{
// 在内部类中申明并定义外部类的对象,防止外部修改对象加一个final
private static final Singleton INSTANCE = new Singleton();
}
// 公共访问方式
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
JDK 中Runtime类使用了饿汉式
工厂模式
1、简单工厂模式
简单工厂模式不是一种模式,更像是一种编程习惯
缺点:违背了开闭原则,工厂和产品耦合
2、工厂方法模式
抽象工厂 AbstractFactory:提供创建产品的接口,调用者通过访问它访问具体工厂的工厂方法来创建产品
具体工厂 ConcretteFactory:主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
抽象产品 Product:定义了产品的规范,描述了产品的主要特性和功能。
具体产品 ConcreteProduct:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂一一对应
缺点:每增加一个新的产品都需要增加一个具体的产品类和具体工厂类,增加系统复杂度,类太多会造成类爆炸
3、抽象工厂模式
抽象工厂 AbstractFactory:提供创建产品的接口,包含多个创建产品的方法,可以创建不同等级的产品。
具体工厂 ConcretteFactory:主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
抽象产品 Product:定义了产品的规范,描述了产品的主要特性和功能,有多个抽象的产品
具体产品 ConcreteProduct:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂多对一的关系
缺点:每增加一个新的产品时候,所有工厂类都需要修改
模式扩展
简单工厂+配置文件解除工厂对象和产品对象的耦合,spring框架原理
JDK源码中Collection.iterator方法用了工厂模式
原型模式
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象
抽象原型类:规定了具体原型对象必须实现的clone()方法
具体原型类:实现抽象原型类的clone()方法,可以被复制的对象
访问类:使用具体原型类中的clone()方法来复制新的对象
浅克隆:创建一个对象,新对象的属性和原来对象完全相同,对于非基本类型属性,任然指向原有属性所指向的对象的内存地址
深拷贝:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址
java中Object类clone()方法是浅拷贝,Cloneable接口就是抽象原型类
具体原型类
public class Realizetype implements Cloneable{
@Override
public Realizetype clone() throws CloneNotSupportedException{
return (Realizetype)super.clone();
}
}
访问类
public class client{
public static void main(String[] args){
Realizetype realizetype = new Realizetype();
Realizetype clone = realizetype.clone();
System.out.println(realizetype == clone); // false;
}
}
深拷贝
略



