Java设计模式与算法(面试常考的)

2024-03-18 15:20

本文主要是介绍Java设计模式与算法(面试常考的),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

开闭原则

对拓展开放,对修改关闭,提高了代码的拓展性和维护性。

如:

public class Person {
private String name;
private int age;}
public class SubPerson extends Person{private boolean gender;    
}

里氏代换原则:-任何父类可以出现的地方,子类一定可以出现

--子类 is a 父类

--在以后的开发中读哦使用继承和多态的概念

package com.example.demo.principle;public class LSPtest {public static void main(String[] args) {Bird bird1 = new Swallow();Bird bird2 = new BrownKiwi();bird1.setSpeed(120);bird2.setSpeed(120);System.out.println("如果飞行300公里:");try {System.out.println("燕子将飞行" + bird1.getFlyTime(300) + "小时.");System.out.println("几维鸟将飞行" + bird2.getFlyTime(300) + "小时。");} catch (Exception err) {System.out.println("发生错误了!");}}
}//鸟类
class Bird {double flySpeed;public void setSpeed(double speed) {flySpeed = speed;}public double getFlyTime(double distance) {return (distance / flySpeed);}
}//燕子类
class Swallow extends Bird {
}//几维鸟类
class BrownKiwi extends Bird {public void setSpeed(double speed) {flySpeed = 0;}}------------------   运行结果   --------------------------如果飞行300公里:
燕子将飞行2.5小时.
几维鸟将飞行Infinity小时。Process finished with exit code 0

依赖倒转原则

多依赖与抽象类和接口,而不是具体实现类。

--在以后的开发中读哦使用抽象类和接口,对子类具有强制性和规范性。

public abstract class Account{//  普通类,尽量别依赖它public double getLix(){}public abstract double getMaxLix(){}}
public class FixedAccount extends Account{
//  此时子类必须重写父类的方法@Overridepublic double getMaxLix(){}
}

接口隔离原则

尽量依赖于小接口,而不是大接口,为了避免污染,降低类之间的耦合度。

-- 耦合 :一个模块与其他模块之间的关联度。

下面这个就不符合接口隔离原则。

public interface Animal{public abstract void run();public abstract void fly();
}
public class Dog implements Animal{public void run(){}//  此时的狗不能飞,所以不能重写此方法//  public void fly(){}
}

public interface RunAnimal{javapublic abstract void run();
}
public interface FlyAnimal{javapublic abstract void fly();
}
//  继承跑这个接口,所以狗就可以跑
public class Dog implements RunAnimal{public void run(){}
}

迪米特法则(最少知道法则)

一个实体应当尽量少于其他实体之间发生相互作用。

低耦合,高内聚。

高内聚:就是将一个实体应当将该实体所拥有的功能尽量聚集在该实体内部。一个对象应该对其他对象保持最少的了解。
迪米特法则还有个更简单的定义:只与直接的朋友通信。
直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。

package com.szh.principle.demeter.improve;import java.util.ArrayList;
import java.util.List;/****/
//学校总部员工类
class Employee {private String id;public void setId(String id) {this.id = id;}public String getId() {return id;}
}//学院的员工类
class CollegeEmployee {private String id;public void setId(String id) {this.id = id;}public String getId() {return id;}
}//管理学院员工的管理类
class CollegeManager {//返回学院的所有员工public List<CollegeEmployee> getAllEmployee() {List<CollegeEmployee> list = new ArrayList<>();for (int i = 1; i <= 5; i++) { //这里我们增加了5个员工到 listCollegeEmployee emp = new CollegeEmployee();emp.setId("学院员工id= " + i);list.add(emp);}return list;}//输出学院员工的信息public void printEmployee() {//获取到学院员工List<CollegeEmployee> list1 = getAllEmployee();System.out.println("------------学院员工------------");list1.stream().map(CollegeEmployee::getId).forEach(System.out::println);}
}//学校管理类
//分析 SchoolManager 类的直接朋友类有哪些: Employee、CollegeManager
//CollegeEmployee 不是 直接朋友, 而是一个陌生类, 这样违背了 迪米特法则
class SchoolManager {//返回学校总部的员工public List<Employee> getAllEmployee() {List<Employee> list = new ArrayList<>();for (int i = 1; i <= 3; i++) { //这里我们增加了3个员工到 listEmployee emp = new Employee();emp.setId("学校总部员工id= " + i);list.add(emp);}return list;}//该方法完成输出学校总部和学院员工信息(id)void printAllEmployee(CollegeManager sub) {//分析问题//改进代码:将输出学院的员工方法,封装到CollegeManagersub.printEmployee();//获取到学校总部员工List<Employee> list2 = getAllEmployee();System.out.println("------------学校总部员工------------");list2.stream().map(Employee::getId).forEach(System.out::println);}
}public class Demeter {public static void main(String[] args) {System.out.println("~~~使用迪米特法则的改进~~~");//创建了一个 SchoolManager 对象SchoolManager schoolManager = new SchoolManager();//输出学院的员工id 和  学校总部的员工信息schoolManager.printAllEmployee(new CollegeManager());}
}

合成服用原则  

尽量读哦使用合成的方式,而不是继承的方式。

public class A{public void show(){}
public void show2(){}
}//  不推荐 public classB extends A{
public class B{
//  将A类作为B类的成员变量,这样就可以只使用A类的show()方法,而不是和继承一样,将两个方法都继下来private A a;a.show();
}

设计模式(最常用的)

是一种被反复使用,多数人认知的,经过多年编程经验的总结。

单例设计模式、模板设计模式、工厂方法模式、抽象工厂方法。

单例设计模式

单例设计模式确保每一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

特点:

1.单例类只能有一个实例

2.单例类必须创建自己创建的唯一实例

3.单例类必须给所有其他对象提供这一唯一的实例。

单例模式分为饿汉式、懒汉式、登记式三种。

1.饿汉式

饿汉式是指全局的单例模式在类装载时构建并被初始化,其优点是速度快,不调用时也创建。典型的一空间换时间。

EHanShi.java

public class EHanShi {private EHanShi() {}//  饿汉式 ,在一开始就将实例创建private final static EHanShi ehanshi = new EHanShi();//  调用方法,像整个系统提供这个实例public static EHanShi getInstance() {return ehanshi;}
}

EHanShiTest.java

public class EHanShiTest {public static void main(String[] args) {EHanShi s = EHanShi.getInstance();System.out.println("创建一个实例s的内存地址是:\t" + s);EHanShi s1 = EHanShi.getInstance();System.out.println("创建一个实例s的内存地址是:\t" + s1);if (s == s1) {System.out.println("创建的是同一个实例");} else if (s != s1) {System.out.println("创建的不是同一个实例");}else{System.out.println("application error");}}
}

2.懒汉式 (线程不安全)

LanHanShi.java

public class LanHanShi {private static LanHanShi instance = null;// 私有默认构造方法private LanHanShi() {}//  静态工厂方法  synchronized  防止多次实例化对象public static synchronized LanHanShi getInstance() {//  等到用的时候才创建if (instance == null) {instance = new LanHanShi();}return instance;}
}

懒汉式是指全局的单例模式等到用的时候才创建实例对象。典型的以时间换空间。

懒汉式防止被多次创建 指令重排。采用双重锁进行实现。、

public class LanHanShi1 {public LanHanShi1() {System.out.println(Thread.currentThread().getName() + "OK");}//  双重检测锁模式 懒汉式单例 DCL懒汉式//  第一重:volatile 防止指令重排private volatile static LanHanShi1 lanHanShi1l;public static LanHanShi1 getInstance() {if (lanHanShi1l == null) {synchronized (LanHanShi1.class) {if (lanHanShi1l == null) {lanHanShi1l = new LanHanShi1();// 第二重:  synchronized 确定一个原子操作,使用同步化,已处理多线程操作/** 1.分配内存空间* 2.执行构造放发,初始化对象* 3.把这个对象只进行这个空间* */}}}return lanHanShi1l;}
}

静态内部类

是不安全,存在问题的

当一个单例类的初始开销很大,且希望用户实际需要时创建单例类,就会使用懒汉式延迟初始化,以提高启动速度。在加载singleton时并不 加载内部类SingletonInner,而在调用getInstance()方法时调用SingletonInner才加载,从而使用singleton的构造函数,实例化singleton,在不需要同步的情况下,才达到延迟的效果。

//  静态内部类
public class Singleton {private Singleton() {}// 静态内部类static class SingletonInner {static Singleton instance = new Singleton();}public static Singleton getInstance() {return Singleton.getInstance();}
}

枚举类

已经成为实现单例的最佳方法。

//  enum本身也是一个Class类 ,默认为单例
public enum EnumSingle {INSTANCE;public void DanLIOperation() {int s = 3 * 2 + 9;System.out.println("\t3+2*9=" + s);}public static void main(String[] args) {EnumSingle le = EnumSingle.INSTANCE;le.DanLIOperation();}
}

工厂方法模式

又称虚拟构造子模式或多态性工厂模式。定义是:一个产品对象的工厂接口,将实际创建的工作推迟到子类中。核心工厂类不在负责所有的产品的创建,二十具体创建的工作交给子类来完成,从而抽象出一个抽象工厂角色,仅负责给出具体工厂必须实现的接口,而不接触一个产品类应当被实例化这个细节。

1.抽象工厂(Creator) 角色:是工厂方法模式的核心,它与应用程序无关。任何在模式中创建对象的工厂类必须实现这个接口。

2.具体工厂(ConcreteCreator)角色:担任这个角色的是实现了抽象工厂接口的具体类。具体工厂角色含有与应用密切相关的编辑,并且受到应用程序的调用,以创建产品对象。

3.抽象产品(Procuct)角色:工厂方法模式中所创建的对象,即产品对象的共同父类或共同拥有的接口。

4.具体产品(Concrete Product)角色:这个角色实现了抽象产品角色声明的接口,工厂方法模式所创建表的每一个对象都是某个具体产品的实例。

工厂方法模式适用场景 :

重复代码 : 创建对象 需要使用 大量重复的代码 ;
不关心创建过程 : 客户端 不依赖 产品类 , 不关心 实例 如何被创建 , 实现等细节 ;
创建对象 : 一个类 通过其 子类 来 指定 创建哪个对象 ;

客户端 不需要知道 具体 产品类 的 类名 , 只需要知道 所对应的工厂 即可 , 具体的产品对象 , 由对应的工厂创建 , 客户端只需要知道 产品 对应的 工厂 ;
工厂方法模式 利用了 面向对象 的 多态性 , 和 里式替换 原则 ;
子类对象 覆盖 父类对象 , 使 系统 更容易扩展 , 将 创建对象的过程 推迟到子类实现 , 创建对象的任务 , 委托给 多个 工厂子类 中的某一个 , 客户端不需要关心是哪个 工厂子类 创建的 产品对象 ;
工厂子类 一般都是 需要的时候 , 动态指定 ;

工厂方法模式优点 :

不关心创建细节 : 用户 只需要 关心 所需产品 对应的工厂 , 无需关心创建细节 ;
符合开闭原则 : 加入 新产品 , 符合开闭原则 , 提高可扩展性 ;
工厂方法模式 中 , 使用 工厂类创建 产品对象 , 同时 隐藏了 具体的 产品类 被 实例化 的细节 ;


工厂方法模式缺点 :

增加复杂性 : 类的个数容易过多 , 增加系统复杂度 ;
在 添加新产品 时 , 除了编写 新的产品类 之外 , 还要 编写该产品类对应的 工厂类 ;
增加难度 : 增加了系统 抽象性 和 理解难度 ;
工厂方法本身 利用了抽象 , 该模式中会 引入抽象层 , 如果要动态创建产品类 , 还要 引入反射技术 ;
设计模式 的 使用 , 要根据 实际的 业务场景 , 模型 综合平衡考量 , 不能过分遵守设计原则 和 设计模式 ;

女娲造人例子

设计三个对象:女娲、八卦炉、不同肤色的人种。八卦炉类似于一个具体工厂,负责制造生产产品(即人类);2中不同肤色的人都是同一个接口下的不同实现类。对于八卦炉来说都是它生产的产品。

Human.java

public interface Human {//  每个人种的皮肤都有相应的颜色public void getColor();//  人类会说话public void talk();
}

白色人种.java

public class 白色人种 implements Human{@Overridepublic void getColor() {System.out.println("白色人种皮肤颜色都是白色的");}@Overridepublic void talk() {System.out.println("白色人种会说话");}}

黑色人种.java

public class 黑色人种 implements Human{@Overridepublic void getColor() {System.out.println("黑色人种皮肤颜色都是黑色的");}@Overridepublic void talk() {System.out.println("黑色人种会说话");}}

AbstractHumanFactory.java

public interface AbstractHumanFactory {public abstract Human createHuman1();public abstract Human createHuman2();}

八卦炉.java

public class 八卦炉 implements AbstractHumanFactory {@Overridepublic Human createHuman1() {return new 白色人种();}@Overridepublic Human createHuman2() {return new 黑色人种();}}

 女娲娘娘.java

public class 女娲娘娘 {public static void main(String[] args) {//  声明八卦炉AbstractHumanFactory LuZi = new 八卦炉();System.out.println("造出的是白色人种");Human baiRen = LuZi.createHuman1();baiRen.getColor();baiRen.talk();System.out.println("造出的是黑色人种");Human heiRen = LuZi.createHuman2();heiRen.getColor();heiRen.talk();}}

抽象工厂模式

可以向客户端提供一个接口,使得客户端在不必指定产品具体类型的情况下,创建多个产品族中的产品对象。抽象工厂模式面对的问题就是,产品等级结构的系统设计。创建一组相关或者相互依赖的对象提供的一个接口,而且无需指定具体类。

产品族是位于不同产品等级结构中相关联的产品组成的家族。

抽象工厂模式的结构

1.抽象工厂(AbstractFactory)角色:担任这个角色的是工厂方法模式的核心,它与使用系统的商业逻辑无关。通常使用接口或者抽象类实现,而所有的具体工厂类都必须实现这个接口或者继承这个接口。

2.具体工厂(ConcereteFactory)类角色:这个角色直接在客户端的调用下创建产品的实例。这个角色含有选择合适的产品对象的逻辑。而这个逻辑是与应用系统的商业逻辑紧密相关的。通常使用具体类来实现这个角色。

3.抽象产品(AbstractProduct)角色:担任这个角色的类是抽象工厂方法模式所创建的对象的父类,或它们共同拥有的接口。通常使用接口或抽象类来实现这一角色。

4.具体产品角色:抽象工厂模式所创建的任何产品对象都是某一个具体产品类的实例。

女娲造人2.0

 抽象工厂为AbstracWomanHumanFactory,AbstracManHumanFactory,具体工厂由concetrateWomanFactory和concerateManFactory组成。抽象产品为HumanKind具体产品为BlackManHuman,BlackWomanHuman,WhiteManHuman,WhiteWomanHuman,YellowManHuman,YellowWomanHuman

AbsractHumanManFactory.java
public interface AbsractHumanManFactory {public WhiteManHuman conceteMenFactory1();public BlackManHuman conceteMenFactory2();public YellowManHuman conceteMenFactory3();}
AbsractHumanWomanFactory.java
public interface AbsractHumanWomanFactory {public WhiteWomanHuman conceteWomenFactory1();public BlackWomanHuman conceteWomenFactory2();public YellowWomanHuman conceteWomenFactory3();}
conceteWomenFactory.java
public class conceteWomenFactory implements AbsractHumanWomanFactory {@Overridepublic WhiteWomanHuman conceteWomenFactory1() {return new WhiteWomanHuman();}@Overridepublic BlackWomanHuman conceteWomenFactory2() {return new BlackWomanHuman();}@Overridepublic YellowWomanHuman conceteWomenFactory3() {return new YellowWomanHuman();}
}
conceteMenFactory.java
public class conceteMenFactory implements AbsractHumanManFactory {@Overridepublic WhiteManHuman conceteMenFactory1() {return new WhiteManHuman();}@Overridepublic BlackManHuman conceteMenFactory2() {return new BlackManHuman();}@Overridepublic YellowManHuman conceteMenFactory3() {return new YellowManHuman();}}
HumanKind.java

public interface HumanKind {void skin();void say();void Iam();
}
WhiteManHuman.java
public class WhiteManHuman implements HumanKind {@Overridepublic void skin() {System.out.println("我是白色人种");System.out.println("白色人种的皮肤颜色是白色的");}@Overridepublic void say() {System.out.println("白色人种会说话");}@Overridepublic void Iam() {System.out.println("我是男性白色人种");}
}
WhiteWomanHuman.java
public class WhiteWomanHuman implements HumanKind{@Overridepublic void skin() {System.out.println("我是白色人种");System.out.println("白色人种的皮肤颜色是白色的");}@Overridepublic void say() {System.out.println("白色人种会说话");}@Overridepublic void Iam() {System.out.println("我是女性白色人种");}
}
BlackWomanHuman.java

public class BlackWomanHuman implements HumanKind {@Overridepublic void skin() {System.out.println("我是黑色人种");System.out.println("黑色人种的皮肤颜色是白色的");}@Overridepublic void say() {System.out.println("黑色人种会说话");}@Overridepublic void Iam() {System.out.println("我是女性黑色人种");}
}
BlackManHuman.java
public class BlackManHuman implements HumanKind {@Overridepublic void skin() {System.out.println("我是黑色人种");System.out.println("黑色人种的皮肤颜色是白色的");}@Overridepublic void say() {System.out.println("黑色人种会说话");}@Overridepublic void Iam() {System.out.println("我是男性黑色人种");}
}
YellowWomanHuman.java
public class YellowWomanHuman implements HumanKind{@Overridepublic void skin() {System.out.println("我是黄色人种");System.out.println("黄色人种的皮肤颜色是白色的");}@Overridepublic void say() {System.out.println("黄色人种会说话");}@Overridepublic void Iam() {System.out.println("黄是女性白色人种");}
}
YellowManHuman.java
public class YellowManHuman implements HumanKind {@Overridepublic void skin() {System.out.println("我是黄色人种");System.out.println("黄色人种的皮肤颜色是白色的");}@Overridepublic void say() {System.out.println("黄色人种会说话");}@Overridepublic void Iam() {System.out.println("黄是男性白色人种");}
}
Test.java
public class Test {public static void main(String[] args) {WhiteManHuman whiteManHuman = new WhiteManHuman();whiteManHuman.skin();whiteManHuman.say();whiteManHuman.Iam();}
}

 抽象工厂模式的优点


分离接口和实现。客户端使用抽象工厂来创建需要的对象,而客户端根本不知道具体实现是谁,客户端只是面向产品的接口编程而已。客户端从具体的实现中解耦。

使切换产品族变得很容易,因为一个具体的工厂实现代表的是一个产品族。


抽象工厂模式的缺点

抽象工厂模式不太容易拓展新的产品。如果增给整个产品族添加一个新的产品,则需要修改抽象工厂,同时也将修改所有的工厂实现类。


 抽象工厂模式的适用场景


客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码
提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现.

静态代理模式

代理模式是对象的结构型模式。代理模式给某一个对象提供一个代理对象并由代理对象控制对原对象那个的引用。

代理模式的思想是为了提供额外的处理或者不同的操作,而在实际对象与调用者之间插入一个代理对象。这些额外操作需要与实际对象进行通信。

代理模式的结构     

抽象对象(AbstractObject)角色:声明了目标对象和代理对象共同的接口,这样以来,在任何可以使用目标对象的地方都可以使用代理对象。

目标对象(RealObject)角色: 定义了代理对象所代表的目标对象。

代理对象(ProxyObject)角色:代理对象的内部含有对目标对象的引用,从而可以在任何时候操纵目标对象;代理对象提供一个与目标对象相同的接口,以便在任何时候均可替代目标对象。代理对象通常在客户端调用并传递给目标对象之前或者之后,执行某个操作,而不是单纯的将调用传递给目标对象。     

                                                                                                                                      

XiangBaJie.java
public interface XiangBaJie {void XiangBaJie();
}

    GaoXiaoJie.java

       

public class GaoXiaoJie implements XiangBaJie {@Overridepublic void XiangBaJie() {System.out.println("高小姐降八戒");}
}

                 

SunWuKong.java
public class SunWuKong implements XiangBaJie {private XiangBaJie xiangBaJie;public SunWuKong(XiangBaJie xbj) {xiangBaJie = xbj;}@Overridepublic void XiangBaJie() {System.out.println("高小姐授予孙悟空降八戒的能力");
//        xiangBaJie.XiangBaJie();System.out.println("孙悟空降伏八戒");}
}

  Test.java     

public class Test {public static void main(String[] args) {//  定义高小姐GaoXiaoJie gaoXiaoJie = new GaoXiaoJie();//  定义孙悟空SunWuKong sunWuKong = new SunWuKong(gaoXiaoJie);//  孙悟空降伏八戒sunWuKong.XiangBaJie();}
}


代理模式适用场景 

  • 保护目标对象 : 客户端 只与 代理类 进行交互 , 不清楚 目标对象 的具体细节 ; 相当于 租客 只与 中介 进行交互 , 不知道房东的信息 ;
  • 增强目标对象 : 代理类 在 目标对象的基础上 , 对 目标对象的功能 进行增强 ;

代理模式优缺点

代理模式优点 :

分离目标对象 : 代理模式 能将 代理对象 与 真实被调用的 目标对象 分离 ;
降低耦合 : 在一定程度上 , 降低了系统耦合性 , 扩展性好 ;
保护目标对象 : 代理类 代理目标对象的业务逻辑 , 客户端 直接与 代理类 进行交互 , 客户端 与 实际的目标对象之间没有关联 ;
增强目标对象 : 代理类 可以 在 目标对象基础上 , 添加新的功能 ;

代理模式缺点 :

类个数增加 : 代理模式 会 造成 系统中 类的个数 增加 , 比不使用代理模式增加了代理类 , 系统的复杂度增加 ; ( 所有的设计模式都有这个缺点 )
性能降低 : 在 客户端 和 目标对象 之间 , 增加了一个代理对象 , 造成 请求处理速度变慢 ;               

动态代理模式

动态代理和静态代理角色一样。动态代理的代理类是动态生成的,不是我们直接写好的。动态代理分为两大类:基于接口的动态代理,基于类的动态代理。

基于接口---JDK动态代理

基于类:cglib

Java字节码实现:javasist

需要了解两个类:Proxy类, InvocationHandler

 Rent.java

public interface Rent {public void rent();
}
Host.java
public class Host implements Rent {@Overridepublic void rent() {System.out.println("房东要出租房子了!");}
}
ProxyInvocationHandler.java
//  InvocationHandler自动生成代理类
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 proxy, Method method, Object[] args) throws Throwable {//  invoke  执行rent接口的动态方法//  动态代理的本质。就是使用反射机制实现Object result = method.invoke(rent, args);return result;}
}

Client.java

public class Client {public static void main(String[] args) {//  真实角色Host host = new Host();//  代理角色: 现在没有ProxyInvocationHandler pih = new ProxyInvocationHandler();//  通过调用程序处理角色来处理我们要调用的接口对象pih.setRent(host);Rent proxy = (Rent) pih.getProxy();  //  这里的proxy就是动态生成的,我们没有写proxy.rent();}
}
ProxyInvocationHandler .java
//  InvocationHandler自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {// 被代理的接口private Object target;public void setoXyInvocationHandler(Object target) {this.target = target;}//  生成得到代理类public Object getProxy() {// 加载到那个类中,代理接口,所传输的参数return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);}@Override//  处理代理类,并返回结果public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//  invoke  执行rent接口的动态方法//  动态代理的本质。就是使用反射机制实现Object result = method.invoke(target, args);return result;}
}

Client.java

public class Client {public static void main(String[] args) {//  真实角色Host host = new Host();//  代理角色: 现在没有ProxyInvocationHandler pih = new ProxyInvocationHandler();//  通过调用程序处理角色来处理我们要调用的接口对象pih.setoXyInvocationHandler(host);Rent proxy = (Rent) pih.getProxy();  //  这里的proxy就是动态生成的,我们没有写proxy.rent();}
}

责任链模式

责任链模式是一种对象的性为型模式。在责任链模式里很多对象,由每一个对象对其下家的引用而连接起来形成一条链。请求就是在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下,动态的重新组织和分配责任。

1.抽象处理者(Handler)角色:此角色定义一个处理请求的接口。接口定义一个方法,以设定和返回下家的引用。这个角色通常由一个抽象类或接口类实现。其中Handler类的聚合关系给出了具体子类对下家的引用,抽象方法handleRequest()规范了子类处理请求的操作。

2.具体处理者(ConcreteHandler)角色:具体处理者接收到请求后,可以选择将请求处理掉或者传给下家。由于具体处理者持有对下家的引用。因此,如果需要,具体处理者可以访问下家。

在此例子中,Handler类是规定了对Leader和Boss的引用,交给Leader.java进行操作,超出范围了则交给Boss.java来进行操作。

Handler.java
public abstract class Handler {// 代表下一个处理类protected Handler nextHandler;//  设置下一个类public void setNextHandler(Handler nextHandler) {this.nextHandler = nextHandler;}//  创建一个当前类的方法public abstract void process(Integer info);}

Leader.java

public class Leader extends Handler {@Overridepublic void process(Integer info) {if (info > 0 && info <= 10) {System.out.println("Leader处理");} else {nextHandler.process(info);}}
}

Boss.java

public class Boss extends Handler{@Overridepublic void process(Integer info) {System.out.println("Boss处理");}
}

 Test.java

public class Test {public static void main(String[] args) {Handler level1 = new Leader();Handler level2 = new Boss();level1.setNextHandler(level2);level1.process(10);level1.process(11);}
}

优点

1.责任链模式将请求和处理分开,请求者不需要知道谁去处理,处理者也不需知道请求的全貌。

2.可以提高系统的灵活性。

缺点

降低系统的性能,从链头走向链尾,当链比较长的时候性能下降。

 责任链模式使用场景

1.多个对象可以处理同一个请求,但具体由哪个对象处理则在运行时动态决定。
2. 在请求处理者不明确的情况下向对个对象中的一个提交一个请求。
3. 需要动态处理一组对象处理请求。

装饰器模式

装饰器模式又被称为包装模式。装饰模式以客户端透明的方式拓展对象的功能模式继承关系的一个替代方案。它提供比继承更多的灵活性,动态的给一个对象增加功能,这些功能可以在动态的撤销,还可以增加一些由基本功能排列组合产生更多新功能。

1.抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。

2.具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类。

3.装饰(Decorator)角色:持有一个构件Conponent对象的实例,并定义一个与抽象构件接口一致的接口。

4.具体装饰(ConcreteDecorator)角色:负责给对象“贴上”附加的责任。

在此例子中,定义一个Robot接口,为抽象构建角色,FirstRobot为具体第一个构件,定义机器人要唱歌、跳舞,再次定义一个具体装饰角色,增加机器人的功能,其可以进行扫地、拖地操作。

Robot.java
public interface Robot {void doSomething();
}

FirstRobot.java

public class FirstRobot implements Robot {@Overridepublic void doSomething() {System.out.println("唱歌");System.out.println("跳舞");}
}

RobotDecorator.java

public class RobotDecorator implements Robot {//  给机器人设置新的功能,所以创建机器人对象private Robot robot;public RobotDecorator(Robot robot) {this.robot = robot;}@Overridepublic void doSomething() {//  保证原有的功能不变robot.doSomething();}public void doMoreSomething() {robot.doSomething();System.out.println("扫地、拖地");}
}

Test.java

public class Test {public static void main(String[] args) {
//        FirstRobot firstRobot = new FirstRobot();new RobotDecorator(new FirstRobot()).doMoreSomething();}
}

装饰模式的优缺点


优点

装饰模式可以动态的扩展一个类的功能。
在扩展功能这一层面,比继承更灵活。
装饰模式使得装饰器类可以更容易的复用。
缺点

装饰模式会产生很多细粒度的小类,如果过度使用,会使程序变得很复杂。


装饰模式的应用场景及案例


需要扩展一个类的功能,或给一个类增加附加功能。
需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
Java中I/O流就是装饰模式最典型的应用。

观察者模式

定义对象间的一种一对多的依赖关系,使得每当一个对象状态发生改变时,七仙官依赖对象皆是得到通知并被自动更新。

1.抽象主题吧(Subject)角色:抽象主题角色把所有对观察者对象的引用保存在一个聚集(例如集合)中。每个主题对象都可以由任意数量的观察者。抽象主题对象提供一个接口,可以增加和删除观察者对象。抽象主题角色又被称为被观察者角色。

2.具体主题(CincreteSubject)角色:将有关状态存入具体观察者对象,当具体主题的内部发生改变时,给所有吧登记过的观察者发出通知。具体主题角色又称为具体被观察者(ConcreteObservable)角色。

3.抽象观察者(Observer)角色:为所有具体观察者定义一个接口,在得到主题的通知时更新自己/这个接口叫做更新接口。

4.具体观察者(ConcreteObserver)角色:存储与主题状态自洽的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身状态与主题的状态相协调。如果需要,具体观察者角色可以保持一个子项具体主题对象的引用。

Debt.java为借款类,定义两个方法,borrow为借钱的方法,参数为添加所借钱的对象,noticyCredits()为通知被借钱方的方法。

Credit.java

public interface Credit {void takeMoney();
}

Lisi.java

public class LiSi implements Credit{@Overridepublic void takeMoney() {System.out.println("给李四还钱");}
}

 Wangwu.java

public class WangWu implements Credit{@Overridepublic void takeMoney() {System.out.println("给王五还钱");}
}

Debt.java

public interface Debt {void borrow(Credit credit);void notifyCredits();
}

ZhangSan.java

public class ZhangSan implements Debt {//  代表多个被借钱的对象private List<Credit> allCredits = new ArrayList<>();private Integer state = 0; //  1 表示有钱@Override//  借钱  第一次增加为李四,第二次为王五public void borrow(Credit credit) {allCredits.add(credit);}@Overridepublic void notifyCredits() {//  注意还钱 向李四、王五还钱,所以进行多次通知allCredits.forEach(credit -> credit.takeMoney());}
}

Test.java

public class Test {public static void main(String[] args) {ZhangSan zhangSan = new ZhangSan();zhangSan.borrow(new LiSi());zhangSan.borrow(new WangWu());zhangSan.notifyCredits();}
}

观察者模式适用场景

观察者模式适用场景 : 关联行为 场景 , 建立一套 触发机制 ;

如 : 用户关注某个商品的价格 , 降价时进行通知 , 这样 用户 和 商品 产生了关联 , 触发机制就是 商品降价 ,

观察者模式优缺点

观察者模式 优点 

抽象耦合 : 在 观察者 和 被观察者 之间 , 建立了一个 抽象的 耦合 ; 由于 耦合 是抽象的 , 可以很容易 扩展 观察者 和 被观察者 ;
广播通信 : 观察者模式 支持 广播通信 ,  类似于消息广播 , 如果需要接收消息 , 只需要注册一下即可。

观察者模式 缺点 :

依赖过多 : 观察者 之间 细节依赖 过多 , 会增加 时间消耗 和 程序的复杂程度 ;
这里的 细节依赖 指的是 触发机制 , 触发链条 ; 如果 观察者设置过多 , 每次触发都要花很长时间去处理通知 ;
循环调用 : 避免 循环调用 , 观察者 与 被观察者 之间 绝对不允许循环依赖 , 否则会触发 二者 之间的循环调用 , 导致系统崩溃 ;

这篇关于Java设计模式与算法(面试常考的)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/822763

相关文章

在Ubuntu上部署SpringBoot应用的操作步骤

《在Ubuntu上部署SpringBoot应用的操作步骤》随着云计算和容器化技术的普及,Linux服务器已成为部署Web应用程序的主流平台之一,Java作为一种跨平台的编程语言,具有广泛的应用场景,本... 目录一、部署准备二、安装 Java 环境1. 安装 JDK2. 验证 Java 安装三、安装 mys

Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单

《Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单》:本文主要介绍Springboot的ThreadPoolTaskScheduler线... 目录ThreadPoolTaskScheduler线程池实现15分钟不操作自动取消订单概要1,创建订单后

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

SpringCloud集成AlloyDB的示例代码

《SpringCloud集成AlloyDB的示例代码》AlloyDB是GoogleCloud提供的一种高度可扩展、强性能的关系型数据库服务,它兼容PostgreSQL,并提供了更快的查询性能... 目录1.AlloyDBjavascript是什么?AlloyDB 的工作原理2.搭建测试环境3.代码工程1.

Java调用Python代码的几种方法小结

《Java调用Python代码的几种方法小结》Python语言有丰富的系统管理、数据处理、统计类软件包,因此从java应用中调用Python代码的需求很常见、实用,本文介绍几种方法从java调用Pyt... 目录引言Java core使用ProcessBuilder使用Java脚本引擎总结引言python

SpringBoot操作spark处理hdfs文件的操作方法

《SpringBoot操作spark处理hdfs文件的操作方法》本文介绍了如何使用SpringBoot操作Spark处理HDFS文件,包括导入依赖、配置Spark信息、编写Controller和Ser... 目录SpringBoot操作spark处理hdfs文件1、导入依赖2、配置spark信息3、cont

springboot整合 xxl-job及使用步骤

《springboot整合xxl-job及使用步骤》XXL-JOB是一个分布式任务调度平台,用于解决分布式系统中的任务调度和管理问题,文章详细介绍了XXL-JOB的架构,包括调度中心、执行器和Web... 目录一、xxl-job是什么二、使用步骤1. 下载并运行管理端代码2. 访问管理页面,确认是否启动成功

Java中的密码加密方式

《Java中的密码加密方式》文章介绍了Java中使用MD5算法对密码进行加密的方法,以及如何通过加盐和多重加密来提高密码的安全性,MD5是一种不可逆的哈希算法,适合用于存储密码,因为其输出的摘要长度固... 目录Java的密码加密方式密码加密一般的应用方式是总结Java的密码加密方式密码加密【这里采用的

Java中ArrayList的8种浅拷贝方式示例代码

《Java中ArrayList的8种浅拷贝方式示例代码》:本文主要介绍Java中ArrayList的8种浅拷贝方式的相关资料,讲解了Java中ArrayList的浅拷贝概念,并详细分享了八种实现浅... 目录引言什么是浅拷贝?ArrayList 浅拷贝的重要性方法一:使用构造函数方法二:使用 addAll(

解决mybatis-plus-boot-starter与mybatis-spring-boot-starter的错误问题

《解决mybatis-plus-boot-starter与mybatis-spring-boot-starter的错误问题》本文主要讲述了在使用MyBatis和MyBatis-Plus时遇到的绑定异常... 目录myBATis-plus-boot-starpythonter与mybatis-spring-b