参考文章:
- 总体:https://www.cnblogs.com/adamjwh/p/9033545.html
- 单例模式:https://www.cnblogs.com/ygj0930/p/10845530.html
创建型模式分为以下几种。
单例(Singleton)模式:保证多线程同时对一个对象和方法访问时,只能操作一个实例
原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
工厂方法(FactoryMethod)模式:定义一个用于创建产品的工厂接口,由子类决定生产一个产品。
抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。
一,单例模式
-
定义:单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例
-
特点:
1):只能由一个实例
2):必须自己创建自己的唯一实例
3):必须给所有其他对象提供这一实例
4):除了构造方法,其他都是static
1. 懒汉式(在第一次调用的时候实例化自己)
public class Singleton{
//将构造函数设置为私有,避免类在外部实例化
private Singleton(){}
//设置为静态,这样就可以每一次调用都是同一个对象
private static Singleton single=null;
public static Singleton getInstance(){
//只有在没有实例的情况下才创建实例,保证只有一个实例
if(single==null){
single=new Singleton();
}
return single;
}
}
Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。
(事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。)
但是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全,如果你第一次接触单例模式,对线程安全不是很了解,可以先跳过下面这三小条,去看饿汉式单例,等看完后面再回头考虑线程安全的问题。
- 在getInstance方法上加同步
public static synchronized Singleton getInstance(){
if(single == null){
single=new Singleton();
}
return single;
}
- 双重检查锁定
一. 注意这里的两个判断null是有很大的意义的
- 第一个null是使他每次只有创建对象的时候才加锁
- 第二个判断null是为了防止当两个线程同时访问该方法,都进入了第一个判断空方法,一个进入,一个阻塞,当第一个线程创建了实例后释放锁,第二个也进入锁,也创建了实例的情况。
二. 在实例对象那边需要加上volatile关键字
- 这里如果不加volatile可能会出现一个错误,即当代码读取到第11行的判断语句时,如果instance不为null时,但instance引用的对象可能还没有完成初始化,线程将访问到一个还未初始化的对象。究其原因是因为代码第14行,创建了一个对象,此代码可分解为三行伪代码,即分配对象的内存空间、初始化对象、设置instance指向刚分配的内存地址,分别记为1、2、3,在2和3之间,可能会被重排序,重排序后初始化就变为了最后一步。因此,线程A的intra-thread semantics(所有线程在执行Java程序时必须遵守intra-thread semantics,它保证重排序不会改变单线程内的程序执行结果)没有改变,但A2和A3的重排序将导致线程B判断出instance不为空,线程B接下来将访问instance引用的对象,此时,线程B将会访问到一个还未初始化的对象。而使用volatile就可以实现线程安全的延迟初始化,本质时通过禁止2和3之间的重排序,来保证线程安全的延迟初始化。
public class Singleton {
private volatile static Singleton instance;
private static Object syncRoot = new Object();
private Singleton() {
}
public static Singleton getInstance() {
//双重锁定
if(instance == null) {
synchronized (syncRoot) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
- 静态内部类(既实现了线程安全,又避免了同步带来的性能影响。)
利用类加载来保证只创建一个instance实例
缺点:无法做到延迟创建对象,在类加载时进行创建会导致初始化时间变长
public class Singleton {
//静态内部类
private static class SingletonHolder {
public static Singleton instance = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
2.饿汉式单例(在类的初始化时,就自行实例化)
饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。
public class Singleton{
private Singleton(){}
//定义为final和static,无法修改
private static final Singleton single = new Singleton().;
public static Singleton getInstance(){
return single;
}
}
上述4种方法实现单例的缺点
1. 反序列化对象时会破坏单例
反序列化对象时不会调用getXX()方法,于是绕过了确保单例的逻辑,直接生成了一个新的对象,破环了单例。
解决办法:
- 重写类的反序列化方法,在反序列化方法中返回单例而不是创建一个新的对象。
public class Singleton implements Serializable{
//当前实例
public static Singleton INSTANCE = new Singleton();
//双重锁定部分代码
。。。
//重写反序列化,使其直接返回当前实例
private Object readResolve() {
return INSTANCE;
}
}
- 重写私有构造方法
//维护一个volatile的标志变量在第一次创建实例时置为false;重写构造函数,根据标志变量决定是否允许创建。
private static volatile boolean flag = true;
private Singleton(){
if(flag){
flag = false; //第一次创建时,改变标志
}else{
throw new RuntimeException("The instance already exists !");
}
最好用的方法-使用枚举模式实现单例
public enum singletonEnum{
INSTANCE;
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
}
原因
- 使用SingletonEnum.INSTANCE进行访问,无需再定义getInstance方法和调用该方法
- JVM对枚举实例的唯一性,避免了上面提到的反序列化和反射机制破坏单例的情况出现,每一个枚举类型和定义的枚举变量再JVM中都是唯一的。
( 枚举类型在序列化时仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象)- 注意:编译器需要禁止重写枚举类型的writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。)
原型模式
- 适用场景:
- 对象之间相同或者相似,只有个别的几个属性不同的时候
- 对象的创建麻烦,但是复制方便时
- 复制出来的对象不是原对象,属性值相同,但是对象的地址不同,可以创建出大量相似的对象。
- 代码实现(利用java实现Cloneable接口就行)
//具体原型类
class A implements Cloneable{
//写一个自定的构造方法,方便理解
A(){
System.out.println("具体原型创建成功");
}
public Object clone throws CloneNotSupportedException{
System.out.println(“具体原型复制成功”);
return (A)super.clone();
}
}
//原型模式的测试类
public class B{
public static void main(String[] args)throws CloneNotSupportedException
{
A obj1=new A();
A obj2=(A)obj1.clone();
System.out.println("obj1==obj2?"+(obj1==obj2));
}
}
- 运行结果如下
具体原型创建成功!
具体原型复制成功!
obj1==obj2?false
工厂模式
- 适用场景:
- 对方不清楚具体的产品名,只清楚产品的生产工厂名
- 产品对象的创建由某一个具体工厂生成,抽象工厂只负责提供创建对象的接口
- 客户只关心产品的品牌,不关心产品的细节
- 一般结构
- 抽象工厂:提供创建对象的方法接口
- 具体工厂:实现创建对象的方法,不同的工厂创建不同产品的对象
- 抽象产品:定义产品的规范,描述产品的主要特性
- 具体产品:实现抽象产品,由具体工厂创建,与具体工厂一一对应。
- 模式的实现
/**
*产品类
**/
//抽象产品
interface product{
public void show();
}
//具体产品1
class proA implements product{
@Override
public void show() {
System.out.println("A");
}
}
//具体产品2
class proB implements product{
@Override
public void show(){
System.out.println("B");
}
}
/**
*工厂类
**/
//抽象工厂
interface factory {
product getPro();
}
//具体工厂1
class facA implements factory{
@Override
public product getPro(){
return new proA();
}
}
//具体工厂2
class facB implements factory {
@Override
public product getPro() {
return new proB();
}
}
抽象工厂模式
- 特点:当具体工厂不只生产某一个特定产品时
- 实现(修改工厂类)
···
interface AbstractFactory
{
public Product1 newProduct1();
public Product2 newProduct2();
}
class ConcreteFactory1 implements AbstractFactory
{
public Product1 newProduct1()
{
System.out.println(“具体工厂 1 生成–>具体产品 11…”);
return new ConcreteProduct11();
}
public Product2 newProduct2()
{
System.out.println(“具体工厂 1 生成–>具体产品 21…”);
return new ConcreteProduct21();
}
}
···
建造者模式
- 适用场景
- 创建的对象较复杂,有多个部件构成,各部件会有复杂的变化,但是构件间的建造顺序时稳定的。
- 产品的构建过程和最终的表示时独立
-
个人理解: 就是产品的各部件实现和建造过程和建造实现顺序分割出来,当一件产品的建造过程改变时,只需要改变建造者,而当产品的某个部件有相对的变化时,就改变部件的具体实现,各自分开,并且将建造者和指挥者分开,当建造过程改变时,只需要改变指挥者,而建造的其中一个过程的细节发生改变时,只需要改建造者的具体建造方法即可
-
一般结构
- 产品角色:包含多个组成部件的复杂对象,也就是各个部件的实现
- 抽象建造者:包含创建产品的各个子部分的抽象方法
- 具体建造者:实现了抽象建造者的接口
- 指挥者:调用建造者中的方法完成复杂对象的建造
也就类似于建造大楼的过程,建造者是工人,指挥者是包工头,包工头通过调度各个个人完成大楼不同部分的建造,完成一栋大楼的建造
- 实现代码
public class Abc {
public static void main(String[] args) {
//创建一个建造者
ture_builder ture_builder=new ture_builder();
//创建一个指挥者,并且给他建造者
boss boss=new boss(ture_builder);
product product=boss.build();
System.out.println();
}
}
//产品角色
class product{
//部分1
String pat1;
String pat2;
public void show(){
System.out.println("aaa");
};
}
//抽象建造者
abstract class builder{
abstract void getpat1();
abstract void getpat2();
abstract product getres();
}
//具体建造者
class ture_builder extends builder{
product pro1=new product();
@Override
void getpat1() {
pro1.pat1="1";
System.out.println("建造pat1部分");
}
@Override
void getpat2() {
pro1.pat2="2";
System.out.println("建造pat2部分");
}
@Override
product getres() {
return pro1;
}
}
//指挥者
class boss{
private ture_builder ture_builder;
boss(ture_builder ture_builder){
this.ture_builder=ture_builder;
}
product build(){
ture_builder.getpat1();
ture_builder.getpat2();
return ture_builder.getres();
}
}