一、单例设计模式
1.模式定义/应用场景
1.1 模式定义
保证一个类只有一个实例,并且提供一个全局访问点
1.2 场景
重量级的对象,不需要多个实例,例如线程池,数据库连接池
2.各种模式
2.1 懒汉模式
定义:延迟加载,只有在真正使用的时候才进行实例化
1)线程安全问题
2)double check 加锁优化
3)编译器,CPU有可能对指令进行重排序,导致使用到尚未初始化的实例,可以通过添加volatile关键字进行修饰,对于volatile修饰的字段,可以防止指令重排
代码部分
public class LazySingletonTest {
public static void main(String[] args) {
LazySingleton instance = LazySingleton.getInstance();
LazySingleton instance1 = LazySingleton.getInstance();
System.out.println(instance == instance1);// == 的时候比较的是地址,实际返回的为true
}
}
class LazySingleton {
private static LazySingleton instance;// 私有成员变量
private LazySingleton(){// 私有构造函数
}
public static LazySingleton getInstance() {// 静态方法
if(instance == null)
instance = new LazySingleton();
return instance;
}
}
package DM;
/**
* @program: Test
* @description: Lazysingleton
* @author: Howie
* @create: 2020-03-18 20:40
**/
public class LazySingletonTest {
public static void main(String[] args) {
new Thread(()->{// 多线程下进行操作出现并发问题
LazySingleton instance = LazySingleton.getInstance();
System.out.println(instance);
}).start();
new Thread(()->{
LazySingleton instance1 = LazySingleton.getInstance();
System.out.println(instance1);
}).start();
}
}
class LazySingleton {
private static LazySingleton instance;
private LazySingleton(){// 私有构造方法,不允许外部直接new对象
}
public static LazySingleton getInstance() {// 可以通过加上synchronized来进行解决 ,但是加锁之后会有很多的性能损耗,所以可以延迟加锁,double check方式
if(instance == null){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new LazySingleton();
}
return instance;
}
}
package DM;
/**
* @program: Test
* @description: Lazysingleton
* @author: Howie
* @create: 2020-03-18 20:40
**/
public class LazySingletonTest {
public static void main(String[] args) {
new Thread(()->{// 多线程下进行操作出现并发问题
LazySingleton instance = LazySingleton.getInstance();
System.out.println(instance);
}).start();
new Thread(()->{
LazySingleton instance1 = LazySingleton.getInstance();
System.out.println(instance1);
}).start();
}
}
class LazySingleton {
private static LazySingleton instance;
private LazySingleton(){
}
public static LazySingleton getInstance() {
if(instance == null) {// 确保第一次才初始化,其实这个不加的话也不会出现问题,但是会浪费更多的锁资源
synchronized (LazySingleton.class){
if(instance == null){// 如果两个线程获取了锁,会造成异常
instance == new LazySingleton();// 这个其实包含了三步操作:1.分配空间 2.初始化 3.引用赋值,而底层会有指令重排机制,可以加上volatile避免指令重排序
}
}
}
return instance;
}
}
2.2 饿汉式
定义:类加载的初始化阶段就完成了实例的初始化。实质上就是借助于JVM类加载机制,保证实例的唯一性
类加载过程:
- 加载二进制数据到内存,生成对应的class数据结构
- 连接:a.验证,b.准备(给类的静态成员变量赋默认值), c.解析
- 初始化:给类的静态变量赋初值
什么时候类加载?
- 只有在真正使用对应的类时,才会触发初始化 如 (当前类是启动类即main函数所在的类,直接进行new操作,访问静态属性,访问静态方法,用反射访问类,初始化一个类的子类等)
package DM;
/**
* @program: Test
* @description: Hungry
* @author: Howie
* @create: 2020-03-18 21:53
**/
class HungrySingletonTest {
public static void main(String[] args) {
HungrySingleton instance = HungrySingleton.getInstance();
HungrySingleton instance1 = HungrySingleton.getInstance();
System.out.println(instance == instance1);
}
}
class HungrySingleton {
// 静态成员变量是在类加载的时候进行初始化的,借助JVM的加载机制来保证单例的
private static HungrySingleton instance = new HungrySingleton();
// 私有构造函数
private HungrySingleton(){
}
// 直接返回
public static HungrySingleton getInstance() {
return instance;
}
}
2.3 静态内部类
1)本质上是利用类的加载机制来保证线程安全
2)只有在实际使用的时候,才会触发类的初始化,所以也是懒加载的一种形式
package DM;
/**
* @program: Test
* @description: InnerClass
* @author: Howie
* @create: 2020-03-18 22:00
**/
class InnerClassSingletonTest {
public static void main(String[] args) {
InnerClassSingleton instance = InnerClassSingleton.getInstance();
InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
System.out.println(instance == instance1);
}
}
class InnerClassSingleton {
private static class InnerClassHolder {// 内部类在外部调用的时候才进行加载
private static InnerClassSingleton instance = new InnerClassSingleton();
}
private InnerClassSingleton(){
}
public static InnerClassSingleton getInstance() {
return InnerClassHolder.instance;
}
}
万恶的反射
-
可以在构造函数中进行判断,如果触发了则报错
-
如果使用反序列化、序列化的时候不会读构造函数中的
Object readResolve() throws objectStreamException { return InnerClassHolder.instance; } static final long serialVersionUID = 42L;// 执行序列化的版本ID
-
2.4 枚举
package DM;
/**
* @program: Test
* @description: enum
* @author: Howie
* @create: 2020-03-18 22:52
**/
public enum Enum {// 枚举是纯天然单例的,如果有反射则立马抛出异常,枚举也是天然反序列化的
INSTANCE;
public void print() {
System.out.println(this.hashCode());
}
}
class EnumTest {
public static void main(String[] args) {
Enum instance = Enum.INSTANCE;
Enum instance1 = Enum.INSTANCE;
instance.print();
System.out.println(instance == instance1);
}
}
3.在Java中的应用
3.1 Runtime运行时的类
3.2 Currency货币--》享元模式+单例模式
3.3 Spring中有DefaultSingletonBeanRegistry
3.4 Spring中的ReactiveAdapterRegistry
二、工厂方法模式
1.模式定义
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method
使得一个类的实例化延迟到子类
2.作用
- 实现了创建者和调用者的分离
-
详细分类
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
3.OOP的七大原则
- 开闭原则:一个软件的实体应该对扩展开放,对修改关闭
- 依赖倒置原则:要针对接口编程,不要针对实现编程
- 迪米特法则:只和直接的朋友通信,避免与陌生人通信
4.工厂模式
-
核心本质
- 实例化对象不使用new,用工厂方法替代
- 将选择实现类,创建对象统一管理和控制。从而将调用者跟我们的实现类解耦
-
三种模式
-
简单工厂模式
- 用来生产同一个等级结构中的任意产品(对于增加新的产品,需要扩展已有的代码)
-
工厂方法模式
- 用来生产同一个等级结构中的固定产品(支持增加任意产品)
-
抽象工厂模式
- 围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂
-
5.代码部分
package DM.Factory;
/**
* @program: Test
* @description: 简单工厂模式--静态
* @author: Howie
* @create: 2020-03-19 07:59
**/
public interface Car {
void name();
}
class WuLing implements Car {
@Override
public void name() {
System.out.println("五菱宏光");
}
}
class Tesla implements Car {
@Override
public void name() {
System.out.println("特斯拉");
}
}
class Mobike implements Car {
@Override
public void name() {
System.out.println("摩拜单车");
}
}
interface CarFactory {
Car getCar();
}
class TeslaFactory implements CarFactory {
@Override
public Car getCar() {
return new Tesla();
}
}
class WuLingFactory implements CarFactory {
@Override
public Car getCar() {
return new WuLing();
}
}
class MobikeFactory implements CarFactory {
@Override
public Car getCar() {
return new Mobike();
}
}
class Consumer {
public static void main(String[] args) {
// 接口,所有的实现类
//Car car = new WuLing();
//Car car1 = new Tesla();
// 2.使用工厂创建
Car car = new WuLingFactory().getCar();
Car car1 = new TeslaFactory().getCar();
Car car2 = new MobikeFactory().getCar();
car.name();
car1.name();
car2.name();
}
}
三.抽象工厂模式
1.定义
- 抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类
2.适用场景
- 客户端(应用层)不依赖于产品类实例如何被创建,实现等细节
- 强调一系列相关的产品对象(属于同一个产品族)一起使用创建对象需要大量的重复代码
- 提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现
3.优点
- 具体产品在应用层的代码隔离,无需关心创建的细节
- 将一个系列的产品统一到一起进行创建
4.缺点
- 规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难
- 增加了系统的抽象性和理解难度
5.代码部分
package DM.Factory;
/**
* @program: Test
* @description: Abstract
* @author: Howie
* @create: 2020-03-19 10:08
**/
public interface IphoneProduct {
void start();
void shutdown();
void callup();
void sendSMS();
}
interface routerProduct {
void start();
void shutdown();
void openwifi();
void setting();
}
class XiaomiPhone implements IphoneProduct {
@Override
public void start() {
System.out.println("开启小米手机");
}
@Override
public void shutdown() {
System.out.println("关闭小米手机");
}
@Override
public void callup() {
System.out.println("小米打电话");
}
@Override
public void sendSMS() {
System.out.println("小米发短信");
}
}
class HuaweiPhone implements IphoneProduct {
@Override
public void start() {
System.out.println("开启华为手机");
}
@Override
public void shutdown() {
System.out.println("关闭华为手机");
}
@Override
public void callup() {
System.out.println("华为打电话");
}
@Override
public void sendSMS() {
System.out.println("华为发短信");
}
}
class XiaomiRouter implements routerProduct {
@Override
public void start() {
System.out.println("启动小米路由器");
}
@Override
public void shutdown() {
System.out.println("关闭小米路由器");
}
@Override
public void openwifi() {
System.out.println("打开小米wifi");
}
@Override
public void setting() {
System.out.println("小米设置");
}
}
class HuaweiRouter implements routerProduct {
@Override
public void start() {
System.out.println("启动华为路由器");
}
@Override
public void shutdown() {
System.out.println("关闭华为路由器");
}
@Override
public void openwifi() {
System.out.println("打开华为wifi");
}
@Override
public void setting() {
System.out.println("华为设置");
}
}
interface ProductFactory {
// 生产手机
IphoneProduct iphoneProduct();
// 生产路由器
routerProduct routerproduct();
}
class XiaomiFactory implements ProductFactory {
@Override
public IphoneProduct iphoneProduct() {
return new XiaomiPhone();
}
@Override
public routerProduct routerproduct() {
return new XiaomiRouter();
}
}
class HuaweiFactory implements ProductFactory {
@Override
public IphoneProduct iphoneProduct() {
return new HuaweiPhone();
}
@Override
public routerProduct routerproduct() {
return new HuaweiRouter();
}
}
class Client {
public static void main(String[] args) {
System.out.println("=========== 小米系列产品 ==========");
XiaomiFactory xiaomiFactory = new XiaomiFactory();
IphoneProduct iphoneProduct = xiaomiFactory.iphoneProduct();
iphoneProduct.callup();
iphoneProduct.sendSMS();
System.out.println("=========== 华为系列产品 ==========");
HuaweiFactory huaweiFactory = new HuaweiFactory();
IphoneProduct iphoneProduct1 = huaweiFactory.iphoneProduct();
iphoneProduct1.callup();
iphoneProduct1.sendSMS();
}
}
6.应用场景
- JDK中Calendar的getInstance方法
- JDBC中的Connection对象的获取
- Spring中IOC容器创建管理bean对象
- 反射中Class对象的newInstance方法
四.建造者模式
1.定义相关
- 将一个复杂对象的构建与它的表示进行分离,使得同样的构建过程可以创建不同的表示
- 建造者模式也属于创建性模式,它提供了一种创建对象的最佳方式
作用
- 在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象
- 用户只需要给出指定复杂对象的类型和内容,建造者模式复杂按照顺序创建复杂对象(把内部的建造过程和细节隐藏起来)
例子
- 工厂(建造者模式):负责制造汽车(组装过程和细节在工厂内)
- 汽车购买者(用户):你只需要说出你需要的型号(对象的类型和内容),然后直接购买就可以使用了(不需要知道汽车是怎么组装的(车轮,车门,发动机,方向盘等等))
2.代码部分
package DM.Factory;
import com.sun.corba.se.spi.orbutil.threadpool.Work;
/**
* @program: Test
* @description: Builder
* @author: Howie
* @create: 2020-03-19 11:19
**/
// 抽象的建造者:方法
abstract class Builder {
abstract void buildA();
abstract void buildB();
abstract void buildC();
abstract void buildD();
// 完工:得到产品
abstract Product getProduct();
}
// 产品:房子
class Product {
private String buildA;
private String buildB;
private String buildC;
private String buildD;
public String getBuildA() {
return buildA;
}
public void setBuildA(String buildA) {
this.buildA = buildA;
}
public String getBuildB() {
return buildB;
}
public void setBuildB(String buildB) {
this.buildB = buildB;
}
public String getBuildC() {
return buildC;
}
public void setBuildC(String buildC) {
this.buildC = buildC;
}
public String getBuildD() {
return buildD;
}
public void setBuildD(String buildD) {
this.buildD = buildD;
}
@Override
public String toString() {
return "Product{" +
"buildA='" + buildA + '\'' +
", buildB='" + buildB + '\'' +
", buildC='" + buildC + '\'' +
", buildD='" + buildD + '\'' +
'}';
}
}
// 具体的建造者:工人
class Worker extends Builder {
private Product product;
public Worker(){
product = new Product();
}
@Override
void buildA() {
product.setBuildA("地基");
System.out.println("地基");
}
@Override
void buildB() {
product.setBuildB("钢筋工程");
System.out.println("钢筋工程");
}
@Override
void buildC() {
product.setBuildC("铺电线");
System.out.println("铺电线");
}
@Override
void buildD() {
product.setBuildD("刷墙面");
System.out.println("刷墙面");
}
@Override
Product getProduct() {
return product;
}
}
// 指挥:核心。负责指挥构建一个工程,工程如何构建,由它决定
class Director {
// 指挥工人按照顺序建房子
public Product build(Builder builder) {
builder.buildA();
builder.buildB();
builder.buildC();
builder.buildD();
return builder.getProduct();
}
}
class Test {
public static void main(String[] args) {
// 指挥
Director director = new Director();
// 指挥 具体的工人完成 产品
Product build = director.build(new Worker());
System.out.println(build.toString());
}
}
3.优点和缺点
-
优点
- 产品的建造和表示分离,实现了解耦。使用建造者模式可以使客户端不必知道产品内部组成的细节
- 将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰
- 具体的建造者类之间是相互独立的,这有利于系统的扩展。增加新的具体建造者无需修改原有类库的代码,符合“开闭原则”
-
缺点
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分类似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大
4.场景&&比较
-
应用场景
- 需要生成的产品对象有复杂的内部结构,这些产品对象具备共性
- 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建出不同的产品
- 适合于一个具有较多的零件的产品的创建过程
-
建造者和抽象工厂模式的比较
- 建造者模式返回一个组装好的完整产品,而抽象工厂返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族
- 在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端通过指挥者类来直到如何生成对象,它侧重于一步步构造一个复杂对象,返回一个完整的对象
五.原型模式
1.方法
- 克隆
- Prototype
- Cloneable接口
- clone()方法
2.代码
package DM;
import javafx.scene.media.VideoTrack;
import java.util.Date;
/**
* @program: Test
* @description: prototype
* @author: Howie
* @create: 2020-03-19 12:51
**/
public class prototype implements Cloneable {
private String name;
private Date createTime;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public prototype(){
}
public prototype(String name,Date createTime) {
this.name = name;
this.createTime = createTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return "prototype{" +
"name='" + name + '\'' +
", createTime=" + createTime +
'}';
}
}
class Copyprototype {
public static void main(String[] args) throws CloneNotSupportedException {
// 原型对象
Date date = new Date();
final prototype v1 = new prototype("howie", date);
System.out.println("v1=>"+v1);
System.out.println("v1=>hash"+v1.hashCode());
Object v2 = v1.clone();
System.out.println("v2=>"+v2);
System.out.println("v2=>hash"+v2.hashCode());
}
}
3.两种方式
-
浅克隆
- 拷贝值和引用
-
深克隆
- 克隆之后和以前的值不同
-
方式
- 序列化/反序列化
- 改造clone方法
4.应用
- Spring Bean:单例模式,原型模式
- 原型模式+工厂模式==》new=》原型模式
六.适配器模式
1.结构型模式
-
作用
- 从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题
-
分类
- 适配器模式
- 代理模式
- 桥接模式
- 装饰模式
- 组合模式
- 外观模式
- 享元模式
2.代码部分
package DM.Adapter;
/**
* @program: Test
* @description: adapter
* @author: Howie
* @create: 2020-03-19 14:59
**/
// 要被适配的类
// 1.继承(类适配器:单继承)
// 2.组合(对象适配器:常用)
public class Adaptee {
public void request() {
System.out.println("连接网线上网");
}
}
// 客户端类
class Computer {
// 电脑需要连接上转接器才能上网
public void net(NetToUSB netToUSB) {
// 上网的具体实现,需要找一个转接头
netToUSB.handleRequest();
}
}
// 接口转换器的抽象实现
interface NetToUSB {
// 作用:处理请求,网线==》usb
public void handleRequest();
}
// 真正的适配器,需要连接USB,连接网线
class Adapter2 extends Adaptee implements NetToUSB {
private Adaptee adaptee;
public Adapter2(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void handleRequest() {
adaptee.request();// 可以上网了
}
}
class Test {
public static void main(String[] args) {
// 电脑,适配器,网线
Computer computer = new Computer();// 电脑
Adaptee adaptee = new Adaptee();// 网线
Adapter2 adapter2 = new Adapter2(adaptee);// 转换器
computer.net(adapter2);
}
}
3.角色分析
- 目标接口:客户所期待的接口,目标可以是具体的或者是抽象的类,也可以是接口
- 需要适配的类:需要适配的类或适配者类
- 适配器:通过包装一个需要适配的对象,将原接口转换为目标对象
4.优点&&缺点
-
优点
- 一个对象适配器可以把多个不同的适配者适配到同一个目标
- 可以适配一个适配者的子类,由于适配器和适配者质检室关联关系,根据“里氏替换原则”,适配者的子类也可以通过该适配器进行适配
-
缺点
- 对于Java,c#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者
- 在Java,C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性
5.使用场景
- 系统需要使用一些现有的类,而这些类的接口(例如方法名)不符合系统的需要,甚至没有这些类的源代码
- 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作
6.使用
- Java中的InputStream
七.代理模式
1.分类
- 静态代理
- 动态代理
2.静态代理部分
-
角色分析
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色之后,一般会有一些附属操作
- 客户:访问代理对象的人
代码部分
package DM;
import javafx.util.converter.ShortStringConverter;
/**
* @program: Test
* @description: proxy
* @author: Howie
* @create: 2020-03-19 15:43
**/
// 租房
public interface Rent {
public void rent();
}
// 房东
class Host {
public void rent() {
System.out.println("房东要出租房子");
}
}
class Client {
public static void main(String[] args) {
Host host = new Host();
// 代理
Proxy proxy = new Proxy(host);
proxy.rent();
}
}
class Proxy implements Rent {
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
seeHouse();
host.rent();
Hetong();
fare();
}
// 看房
public void seeHouse() {
System.out.println("中介带你看房");
}
// 收中介费
public void fare() {
System.out.println("收中介费");
}
// 签合同
public void Hetong() {
System.out.println("租赁签合同");
}
}
好处&&缺点
-
好处
- 可以使真实角色操作更加纯粹,不用去关注一些公共的业务
- 公共也就交给代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
-
缺点
- 一个真实角色就会产生一个代理角色,代码量会翻倍
3.动态代理
定义
- 动态代理和静态代理的角色一样
- 动态代理的代理类是动态生成的,不是我们直接写好的
-
动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口--》JDK动态代理
- 基于类--》cglib动态代理
- Java字节码实现:javasist
代码部分
// 利用反射实现
八.桥接模式
1.定义
- 桥接模式是将抽象部分与它的实现部分分离,使它们都可以独立的变化。它是一种对象结构型模式,又称为柄体模式或者是接口模式
- 遵循单一职责原则
2.代码部分
package DM.Bridge;
/**
* @program: Test
* @description: Bridge
* @author: Howie
* @create: 2020-03-19 16:20
**/
interface Brand {
void info();
}
class Lenovo implements Brand {
@Override
public void info() {
System.out.println("联想");
}
}
// 苹果品牌
class Apple implements Brand {
@Override
public void info() {
System.out.println("苹果");
}
}
// 抽象的电脑类
abstract class Computer {
// 组合,品牌
protected Brand brand;
public Computer(Brand brand) {
this.brand = brand;
}
public void info() {
brand.info();// 自带品牌
}
}
class Desktop extends Computer {
public Desktop(Brand brand) {
super(brand);
}
@Override
public void info() {
super.info();
System.out.println("台式机");
}
}
class Laptop extends Computer {
public Laptop(Brand brand) {
super(brand);
}
@Override
public void info() {
super.info();
System.out.println("笔记本电脑");
}
}
public class BridgeMode {
public static void main(String[] args) {
// 苹果笔记本
Computer computer = new Laptop(new Apple());
computer.info();
// 联想台式机
Computer computer1 = new Desktop(new Lenovo());
computer1.info();
}
}
3.优点&&缺点
-
优点
- 桥接模式是比多继承方案更好的解决方法。极大地减少了子类的个数,从而降低管理和维护的成本
- 桥接模式提高了系统的可扩充性,在两个变化维护中任意扩展一个维度,都不需要修改原有的系统
-
缺点
- 设计阶段较为复杂
- 适用范围有一定的局限性
4.应用场景
- Java语言中通过Java虚拟机实现了平台的无关性
- JDBC驱动程序
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。