大一菜鸡的个人笔记,欢迎指点和交流
23种设计模式
学习设计模式的意义
设计模式的本质是面向对象设计原则的实际应用,是对类的封装性、继承性、和多态性以及类的关联关系和组合关系的充分理解。
优点:
- 可以提高程序员的思维能力,编程能力和设计能力。
- 使程序设计更加标准化,代码编程更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
- 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。
OOP七大原则
开闭原则
对扩展开发 对修改关闭
里氏替换原则
继承必须确保超类所拥有的性质在子类中仍然成立
意思是不重写父类的方法 子类自己写新方法
因为重写的复用性不好
依赖倒置原则
要面向接口编程,不要面向实现编程。
抽象不依赖细节,而细节应该依赖抽象。
单一职责原则
控制类的粒度大小,将对象解耦、提高其内聚性。
接口隔离原则
要为各个类建立他们需要的专用接口
迪米特法则
只与你的直接朋友交谈,不和陌生人讲话
合成复用原则
尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来来实现。
继承和组合的区别
组合:在子类中写自己方法的独特实现,再把子类对象传到其他类中,使传入不同的对象实现不同的功能。
各模式的分类
创建型:省去new 更好地创建对象
结构型:实现松耦合
单例模式
单例模式的应用场景(作用)
单例模式:让一个类只能创建一个实例,实现对象的唯一性。当对象的创建比较消耗资源,且我们只需要一个这样的对象,就可以使用单例模式。
饿汉式单例
一上来直接加载(使用static) 可能会浪费资源
饿汉式是线程安全的
因为在加载类的时候就已经加载了对象 类在整个生命周期中只会被加载一次 因此只会创建一个实例对象
懒汉式单例
普通版(线程不安全)
不使用锁的问题:
使用锁:
虽然安全了 但同步方法本身导致很大的性能消耗,并且加锁只需要在第一次初始化的时候用到,之后的调用都没必要加锁。
错误版DCL懒汉(线程安全)
使用双重检测的意义是节省资源
先判断对象是否已经被初始化,再决定要不要加锁。
只用在创建时加锁 节省了资源
第一次非空检测是防止已经初始化后,获取锁,等待锁的资源浪费。避免不必要的阻塞
第二次非空检测是防止多个线程同时通过了第一次检测,此时还未初始化,第二个线程阻塞等待,获取锁之后如果对象不为空且创建则违反单例原则。
这个版本有隐患:指令重排会导致出错
创建新对象不是一个原子性操作(编译器会进行指令重排)
1.分配内存空间
2.执行构造方法,初始化对象
3.把这个对象指向这个空间
可能顺序为123 也可能 132(先占用空间,再把对象放进去)
对象指向空间后释放了锁?
导致高并发时第二个线程可能会访问到未被初始化的对象
正确版DCL懒汉
避免了指令重排
静态内部类
public class Singleton {
private static class SingletonHolder {
private static Singleton instance = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
反射破坏单例
输出的两个hashcode不相同 单例被破坏
以上的单例模式虽然可以做到线程安全 但是仍可能被反射破坏单例 我们需要用枚举来解决这个问题
枚举
尝试使用反射破解枚举时需要注意
如果把枚举中当作无参构造 直接填null IDEA会提示该类没有无参构造器(使用JAD反编译字节码可知)
工厂模式
简单工厂模式(静态工厂模式)
在原来的类中加代码,并且使用static。违反了闭包原则。
工厂方法模式
每种类型做一个新的工厂实现接口工厂 代码量大 麻烦
抽象工厂模式
创建工厂的工厂
使用场景:在需求及其稳定的时候抽象工厂模式非常稳定
优点:具体产品在应用层的代码隔离,无需关心创建的细节。
将一系列的产品统一到一起创建
缺点:规定了所有可能被创建的产品集合,扩展性不强。
增加了系统的抽象性和理解难度。
建造者模式
优点和使用场景
创建型模式的其中之一。
定义:将一个复杂对象的构建和他的表示分离,使得同样的构建过程可以创建不同的表示。
主要作用在用户不知道对象的建造过程和细节的情况下就可以创建复杂的对象。
指挥者实现
静态内部类实现
原型模式
场景
浅克隆
一个对象的创建非常麻烦 使用原型模式克隆一个相同的
是用的是浅克隆 一但date重新赋值 两个对象的date都会改变 这是不好的
深克隆
适配者模式
将一个类的接口转换成客户希望的另外一个接口,使得原本不兼容而不能一起工作的类一起工作。
桥接模式
桥接模式的优劣
原本:
使用桥接模式后:
静态代理模式
意义
使真实角色的任务更加纯粹。
代理角色实现了任务的分工
业务扩展的时候 方便集中管理
缺点
代码量多。
中介的意义
中介的意义就是实现附属功能,而不只是租房。
中介起到了:带看很多房子,协调两方沟通,签合同。。。
代理实现功能增强
符合闭包原则
动态代理模式
意义
使真实角色的任务更加纯粹。
代理角色实现了任务的分工
业务扩展的时候 方便集中管理
一个动态代理类可以代理多个类(方便添加功能)
简介
动态代理和静态代理角色一样
动态代理的代理类是动态生成的,不是我们直接写好的
动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口:JDK动态代理
- 基于类:cglib
需要了解两个类:Proxy、invokatio
/**
* @author:zmc
* @function:
* @date: 2020/7/13 15:37
*/
public interface Rent {
public void rent();
}
/**
* @author:zmc
* @function:
* @date: 2020/7/13 15:38
*/
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author:zmc
* @function:
* @date: 2020/7/13 15:40
*/
//用这个类生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
seeHouse();
//动态代理的本质,就是使用反射机制实现
Object result = method.invoke(rent, objects);
fare();
return null;
}
public void seeHouse(){
System.out.println("中介带看房子");
}
public void fare(){
System.out.println("收中介费");
}
}
/**
* @author:zmc
* @function:
* @date: 2020/7/13 16:06
*/
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们调用的接口对象
pih.setRent(host);
//这里的proxy就是动态生成的
Rent proxy = (Rent)pih.getProxy();
proxy.rent();
}
}
动态代理模式的优势就是可以利用反射对方法进行拓展,不需要像静态代理模式那样对以前的代码进行修改,动态代理模式符合闭包原则。