前言
- Java的设计模式在面试时也会经常问到,他总共有23种设计模式,设计模式就是解决问题的方案,也是一种编码思想。
- Java的设计模式主要分为三类:
- 创建型模式
共五种:工厂(简单工厂(工厂方法模式)、抽象工厂模式)、单例模式、建造者模式、原型模式。 - 结构型模式
共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 - 行为型模式
共十一种:策略模式、模板方法模式、观察者模式(监听器)、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
- 创建型模式
- 一般情况下工厂模式的以Factory结尾;工具类通常使用单例模式,以Util结尾;建造者模式通常以Builder结尾;适配器模式通常以Adapter结尾;装饰器模式通常以Decorator结尾。
- 常用到的设计模式其实也不多。
单例模式
- 单例模式就是一个类只有一个实例
- 单例模式分为饿汉模式和懒汉模式
- 枚举是饿汉式的单例模式
饿汉式
- 类加载时就创建实例。
- 满足饿汉的单例模式有以下几个条件
- 不能让调用者创建:构造方法私有化
- 给调用者提供对象并且调用者不能创建对象:声明方法返回对象并且方法是静态方法
- 返回的对象是同一个并且类加载时就创建:声明字段创建对象,并且使用static final 修饰,并且赋初始值(静态方法可以调用多次,返回的对象就不是同一个了,对象不能修改,所以用final修饰,静态方法中返回值得是静态的,所以用static修饰)
- 饿汉模式是不用考虑线程并发安全问题的,因为static修饰的是在类加载的时候加载,JVM在类加载时,只会加载一次,所以实例也只会创建一次,并且实例用final修饰,一旦赋值就不能改变,所以从始至终类的实例只会有一个。
c
public class Student {
public static final Student student = new Student();
private Student(){}
public static Student getInstance(){
return student;
}
}
//测试
Student instance = Student.getInstance();
Student instance1 = Student.getInstance();
//测试的结果是两次获取的对象都是同一个
System.out.println(instance1.hashCode());
System.out.println(instance.hashCode());
懒汉式
- 使用的时候才创建实例
- 懒汉式满足的条件:
- 不能让调用者创建:构造方法私有化
- 给调用者提供对象并且调用者不能创建对象:声明方法返回对象并且方法是静态方法
- 返回的对象是同一个:声明字段创建对象,使用static修饰
- 使用实例的时候才创建:声明字段创建对象时不能用final修饰,并且初始值赋为null(final修饰后一旦赋值就不可更改,使用实例时就不能赋值了)
- 使用实例的时候判断没有实例才创建:双重判断加同步代码块判断是否已经有实例
- 版本一:
- 如果多个线程同时进入判断,比如线程1来的时候employee为空,满足条件就给employee赋值,当线程1还没给employee赋值成功时,线程2来了,判读employee还是为空,线程2也给employee赋值,那么就有多个Employee的实例了。
c
public class Employee {
public static Employee employee = null;
private Employee(){}
public static Employee getInstance(){
if(employee == null){
employee = new Employee();
}
return employee;
}
}
- 版本二
- employee赋值时候使用同步代码块,并且同步锁要是全局的,所以用字节码对象
- 这样还是会有问题:
- 并发情况下,多个线程都满足employee为空(假设线程1 和线程2 都满足),然后尝试获取锁,线程1获取到了锁,然后给employee赋值并释放锁;线程2获取到了锁,又去给employee赋值,那么又会出现多个实例
c
public class Employee {
public static Employee employee = null;
private Employee(){}
public static Employee getInstance(){
if(employee == null){
synchronized (Employee.class){
employee = new Employee();
}
}
return employee;
}
}
- 版本三
- 在加锁后还要判断employee是否为空
c
public class Employee {
public static Employee employee = null;
private Employee(){}
public static Employee getInstance(){
if(employee == null){
synchronized (Employee.class){
if(employee == null){
employee = new Employee();
}
}
}
return employee;
}
}
枚举
- 枚举的构造器默认私有化的,为枚举类定义一个实例就是单例
c
enum Teacher {
INSTANCE;
}
//使用
Teacher.INSTANCE
装饰者模式
- 对原有功能进行增强
- 装饰者模式的实现步骤
- 增强类继承原始类
- 增强类提供无参构造方法
- 增强类提供原有类的属性和参数为原有类的构造方法
- 增强类重写原有类要增强的方法
c
//原有类
public class Target {
//只能判断字符串是否为null
public Boolean isEmpty(String str){
return str == null;
}
}
//增强类
public class TargetDecorate extends Target {
private Target target;
public TargetDecorate(){
}
public TargetDecorate(Target target){
this.target = target;
}
//不仅能判断字符串是否为null,还能判断是否为""
@Override
public Boolean isEmpty(String str) {
Boolean empty = target.isEmpty(str);
if(!empty){
return "".equals(str);
}
return empty;
}
}
//测试
@Test
public void test1(){
TargetDecorate targetDecorate = new TargetDecorate(new Target());
System.out.println(targetDecorate.isEmpty(""));
}
工厂模式
- 就是用来创建对象的,实现解耦合
简单工厂
- 一个工厂生产同类的不同品牌产品
- 缺点:扩展困难
c
public interface IPhone {
void call();
}
public class Huawei implements IPhone{
@Override
public void call(){
System.out.println("华为");
}
}
public class Xiaomi implements IPhone{
@Override
public void call(){
System.out.println("小米");
}
}
//这里的类型可以用ConcurrentHashMap来存
public class PhoneFactory {
public IPhone getPhone(String type){
if("huawei".equals(type)){
return new Huawei();
}else if("xiaomi".equals(type)){
return new Xiaomi();
}
return null;
}
}
//上面的类修改后
public class PhoneFactory {
public static ConcurrentHashMap<String,IPhone> hashMap = new ConcurrentHashMap();
static{
initHashMap();//静态代码块中的方法随着类加载而执行
}
private static void initHashMap(){
hashMap.put("huawei",new Huawei());
hashMap.put("xiaomi",new Xiaomi());
}
public IPhone getPhone(String type){
return hashMap.get(type);
}
}
@Test
public void test2(){
PhoneFactory phoneFactory = new PhoneFactory();
IPhone xiaomi = phoneFactory.getPhone("xiaomi");
IPhone huawei = phoneFactory.getPhone("huawei");
xiaomi.call();//小米
huawei.call();//华为
}
工厂方法
- 每个工厂实现抽象工厂,每个产品实现抽象产品,一个工厂创建一个品牌的产品。
c
public interface IPhone {
void call();
}
public interface IPhoneFactory {
IPhone getPhone();
}
public class Huawei implements IPhone {
@Override
public void call(){
System.out.println("华为");
}
}
public class Xiaomi implements IPhone {
@Override
public void call(){
System.out.println("小米");
}
}
public class HaweiFactory implements IPhoneFactory{
@Override
public Huawei getPhone(){
return new Huawei();
}
}
public class XiaomiFactory implements IPhoneFactory {
@Override
public Xiaomi getPhone() {
return new Xiaomi();
}
}
@Test
public void test3(){
Xiaomi xiaomi = new XiaomiFactory().getPhone();
xiaomi.call();//小米
Huawei huawei = new HaweiFactory().getPhone();
huawei.call();//华为
}
抽象工厂
- 抽象工厂中可以生产不同的产品,每个工厂创建同一品牌的不同产品,不能生产的直接返回null.
c
public interface IPad {
public void tv();
}
public interface IPhone {
void call();
}
public interface IFactory {
IPhone getPhone();
IPad getPad();
}
public class HuaweiPhone implements IPhone {
@Override
public void call() {
System.out.println("华为");
}
}
public class HuaweiPad implements IPad {
@Override
public void tv() {
System.out.println("华为的pad");
}
}
public class XiaomiPhone implements IPhone {
@Override
public void call(){
System.out.println("小米");
}
}
public class HaweiFactory implements IFactory {
@Override
public HuaweiPhone getPhone(){
return new HuaweiPhone();
}
@Override
public IPad getPad() {
return new HuaweiPad();
}
}
public class XiaomiFactory implements IFactory{
@Override
public IPhone getPhone() {
return new XiaomiPhone();
}
@Override
public IPad getPad() {
return null;
}
}
@Test
public void test4(){
IPad huaweiPad = new HaweiFactory().getPad();
huaweiPad.tv();//华为的pad
IPhone huaweiPhone = new HaweiFactory().getPhone();
huaweiPhone.call();//华为
IPhone xiaomiPhone = new XiaomiFactory().getPhone();
xiaomiPhone.call();//小米
IPad xiaomiPad = new XiaomiFactory().getPad();
xiaomiPad.tv();//空指针,因为小米没有pad
}
模版模式
- 在一个方法中定义了一个算法的骨架,允许子类在不改变算法结构的情况下重定义某些步骤的具体内容。模板方法模式通常用于实现一个算法的不变部分,而将可变的行为留给子类来实现。
- Spring的容器刷新就是用的模版模式
- 其实就是一个抽象类里面定义类模版方法,这个模版方法按照一定次序调用了很多这个类的抽象方法,使用这个模版的类就要继承这个模版类,必须实现它的抽象方法,也就是模版中的内容自己定义,模版只是定义了框架和顺序。
c
public abstract class FoodPreparer {
// 模板方法
public void prepareFood() {
prepareIngredients();
cook();
serve();
}
// 步骤1:准备食材,抽象方法由子类实现
public abstract void prepareIngredients();
// 步骤2:烹饪,抽象方法由子类实现
public abstract void cook();
// 步骤3:服务/上菜,抽象方法由子类实现
public abstract void serve();
}
public class PastaPreparer extends FoodPreparer {
@Override
public void prepareIngredients() {
System.out.println("Preparing pasta ingredients.");
}
@Override
public void cook() {
System.out.println("Cooking pasta.");
}
@Override
public void serve() {
System.out.println("Serving pasta.");
}
}
public class PizzaPreparer extends FoodPreparer {
@Override
public void prepareIngredients() {
System.out.println("Preparing pizza ingredients.");
}
@Override
public void cook() {
System.out.println("Baking pizza.");
}
@Override
public void serve() {
System.out.println("Serving pizza.");
}
}
@Test
public void test() throws Exception{
FoodPreparer pastaPreparer = new PastaPreparer();
pastaPreparer.prepareFood(); // 输出准备意大利面的整个过程
System.out.println("======================");
FoodPreparer pizzaPreparer = new PizzaPreparer();
pizzaPreparer.prepareFood(); // 输出准备披萨的整个过程
}
适配器模式
- 用于解决接口不兼容的问题,使得原本没有任何关系的接口可以协同工作
- 下面的例子是美国插头适配中国插座的例子
c
// 目标接口:中国插座
public interface ChineseSocket {
void plugIn();
}
// 适配者类:美国插头
public class AmericanPlug {
public void connect() {
System.out.println("美国插头已连接。");
}
}
// 类适配器:美国插头适配到中国插座
public class PowerAdaptor extends AmericanPlug implements ChineseSocket {
// 适配器类不需要重写任何方法,因为它直接继承了适配者的方法,并且已经实现了目标接口
// 客户端可以通过这个适配器类来调用目标接口的方法
public void plugIn() {
// 在调用适配者的方法之前或之后,可以添加一些额外的逻辑
System.out.println("美国插头正在适配到中国插座...");
// 调用适配者的方法
connect();
// 适配成功后的额外逻辑
System.out.println("美国插头已成功适配到中国插座。");
}
}
@Test
public void test(){
// 创建适配器对象
ChineseSocket chineseSocket = new PowerAdaptor();
// 通过适配器对象调用目标接口的方法
chineseSocket.plugIn();
}
代理模式
- 客户端并不直接调用实际的对象,而是通过调用代理对象,来间接的调用实际的对象,相当于经纪人。
- 代理分为静态代理和动态代理。
- 代理可以增加在原业务的基础上增加功能。
静态代理
- 每个类都要创建一个代理类
c
//接口
public interface ISongHua {
void songHua();
}
//被代理类
public class XiaoMing implements ISongHua{
@Override
public void songHua() {
System.out.println("小明送花给小兰");
}
}
//代理类
public class XiaoMingProxy implements ISongHua{
private XiaoMing xiaoMing;
public XiaoMingProxy() {
}
public XiaoMingProxy(XiaoMing xiaoMing) {
this.xiaoMing = xiaoMing;
}
@Override
public void songHua() {
System.out.println("送花之前的逻辑");
//送花的逻辑
xiaoMing.songHua();
System.out.println("送花之后的逻辑");
}
}
//测试
@Test
public void SongHuaTest(){
new XiaoMingProxy(new XiaoMing()).songHua();
}
//结果
送花之前的逻辑
小明送花给小兰
送花之后的逻辑
动态代理
- 动态代理分为jdk动态代理和cglib动态代理
- jdk动态代理只能代理实现了接口的类(如上述的XiaoMing),cglib动态代理能代理没有实现接口的类,默认是jdk动态代理
JDK动态代理
- jdk代理中主要有java.lang.reflect.Proxy 类和java.lang.reflect.InvocationHandler接口
- 代理类和被代理类会实现同一个接口,只是这里是隐式的,是在JVM 在运行时动态生成的。
- InvocationHandler中的invoke 方法用来处理被代理类方法,对被代理类的方法进行增强
- Proxy的静态方法newProxyInstance 用来动态生成代理类,代理类实现了目标对象所实现的接口,并将方法调用委托给 InvocationHandler
代理类
的名字通常是$Proxy后加上一个数字
Class<?> proxyClass = Proxy.getProxyClass(类加载器,类实现的接口);
System.out.println("Proxy class name: " + proxyClass.getName());
//结果:Proxy class name: 包名.$Proxy7
代理对象(实例)
的名字通常是:被代理类名@哈希码
被代理类实现的接口proxy = (被代理类实现的接口) Proxy.newProxyInstance(类加载器,类实现的接口,InvocationHandler对象);
//结果:Proxy class name: 包名.被代理类@47f37ef1
- jdk动态代理主要用于实现aop
c
//接口
interface Hello {
void sayHello();
}
//被代理类(必须实现接口)
public class SimpleHello implements Hello{
@Override
public void sayHello() {
System.out.println("Hello, world!");
}
}
//调用处理器
public class HelloInvocationHandler implements InvocationHandler {
//目标对象,也就是被代理的对象
private Object target;
public HelloInvocationHandler(Object target) {
this.target = target;
}
/**
* @param proxy 被代理的对象
* @param method 被代理对象的方法
* @param args 被代理对象方法中的参数
* @return
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法执行前的操作");
//调用被代理的方法,target就是被代理类的对象,args就是被代理类对象中方法的参数
Object result = method.invoke(target, args);
System.out.println("方法执行后的操作");
//原始方法的执行结果
return result;
}
}
//测试
@Test
public void JdkTest() {
// 创建目标对象
SimpleHello hello = new SimpleHello();
// 创建InvocationHandler对象,并传入目标对象
InvocationHandler handler = new HelloInvocationHandler(hello);
//创建类加载器
ClassLoader classLoader = hello.getClass().getClassLoader();
//找到类实现的接口
Class<?>[] interfaces = hello.getClass().getInterfaces();
// 创建代理对象
Hello proxy = (Hello) Proxy.newProxyInstance(classLoader, interfaces, handler);
// 调用代理对象的方法
proxy.sayHello();
}
CGLIB动态代理
- org.springframework.cglib.proxy.Enhancer 类,org.springframework.cglib.proxy.MethodInterceptor接口,
- org.springframework.cglib.proxy.MethodProxy类
- Enhancer类创建代理类
- MethodInterceptor拦截方法,做方法增强
- MethodProxy是Java反射中 Method 类的一个补充。它提供了一种更高效的方式来调用方法
c
//被代理类
public class HelloTarget {
public void sayHello(String name) {
System.out.println("Hello, " + name + "!");
}
}
//方法拦截器
public class SimpleInterceptor implements MethodInterceptor {
/**
*
* @param obj 被代理对象
* @param method 被代理对象的方法
* @param args 被代理对象方法的参数
* @param proxy cglib 代理后的方法,一般不用
* @return
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("目标方法执行前操作");
// 调用目标方法:cglib代理后的方法.invokeSuper(被代理对象,被代理对象方法的参数)
Object result = proxy.invokeSuper(obj, args);
System.out.println("目标方法执行后操作");
//被代理方法返回的结果
return result;
}
}
//测试
@Test
public void CglibTest() throws Exception{
// 创建目标对象
HelloTarget target = new HelloTarget();
// 创建 Enhancer 对象,并设置目标类
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloTarget.class);
// 设置回调接口
enhancer.setCallback(new SimpleInterceptor());
// 创建代理对象
HelloTarget proxy = (HelloTarget) enhancer.create();
// 调用代理对象的方法
proxy.sayHello("World");
}
策略模式
- 它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
- 策略模式一般情况下都是用来优化if-else语句的
- 简单工厂中用ConcurrentHashMap改良就是策略模式
c
//策略接口
public interface PaymentStrategy {
public double pay(double money);
}
//策略类1
public class CashPayment implements PaymentStrategy {
@Override
public double pay(double money) {
System.out.println("Cash Payment of " + money);
return money;
}
}
//策略类2
public class CardPayment implements PaymentStrategy {
@Override
public double pay(double money) {
System.out.println("Card Payment of " + money);
return money - money * 0.02; // 假设刷卡有2%的优惠
}
}
//策略上下文类
public class PaymentContext {
private PaymentStrategy strategy;
public PaymentContext(PaymentStrategy strategy){
this.strategy = strategy;
}
public double executePayment(double money){
return this.strategy.pay(money);
}
}
//测试
@Test
public void strategyTest(){
PaymentContext paymentContext = new PaymentContext(new CashPayment());
System.out.println("Cash Payment: " + paymentContext.executePayment(100));
paymentContext = new PaymentContext(new CardPayment());
System.out.println("Card Payment: " + paymentContext.executePayment(100));
}