- @[toc]
- 设计模式概述
- 设计模式目的
- 七大原则
- 单一职责原则
- 接口隔离原则
- 依赖倒转原则(面向接口编程)
- 里氏替换原则
- 开闭原则
- 迪米特法则
- 合成复用原则
- UML类图
- 设计模式
- 基本概念
- 单例模式
- 饿汉式
- 懒汉式
- 双重检查
- 静态内部类
- 枚举类
- 单例模式注意
- 工厂模式
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
- 原型模式
- 建造者模式
- 适配器模式
- 对象适配器
- 接口适配器(缺省适配器模式)
- 桥接模式
- 装饰者模式
- 外观模式(过程模式)
- 享元模式
- 代理模式
- 静态代理
- 动态代理(JDK代理)
- 动态代理(CGLIB代理)
- 代理模式的变种
- 模板方法模式
- 基本模板方法模式
- 钩子方法
- 命令模式
- 访问者模式
- 双分派
- **迭代器模式**
- 观察者模式
- 中介者模式
- 备忘录模式
- 解释器模式
- 状态模式
- 策略模式
- 职责链模式
- 补充
- 增强耦合的方法
- volatile关键词
- **先补充一下概念:Java 内存模型中的可见性、原子性和有序性。**
- 当一个变量定义为 volatile 之后,将具备两种特性:
- volatile 性能:
- 浅拷贝和深拷贝
- 浅拷贝
- 深拷贝
- Iteraior接口
- JDK Observable源码分析
- Arrays.sort的lambda表达式用法
-
编写过程中,程序员面临着耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性等多方面的挑战,设计模式是为了让程序具有更好:
- 代码重用性
- 可读性
- 可靠性
- 是程序具有低耦合性,高内聚性
-
设计模式七大原则:
- 单一职责原则
- 接口隔离原则
- 依赖倒转原则(面向接口编程)
- 里氏替换原则
- 开闭原则
- 迪米特法则
- 合成复用原则
- 基本介绍:对类来说,一个单一类只负责一个职责。若一个类负责多个值则,如:类A拥有这则1和值则2,当值则1发生改变而影响类A,则可能导致值则2的错误,所以需要将类A颗粒化为类A1和类A2
- 单一职责注意事项:
- 降低类的复杂度,一个类只负责一个职责
- 提高类的可读性,可维护性
- 降低变更引起的风险
- 通常情况下需要遵守单一职责的原则,但在类的方法特别少的情况下可以不遵守单一职责
- Attention:
- 对于if…else语句需要谨慎使用,if…else使得代码的耦合程度太高,在必须使用if…else…时考虑使用构造方法或构造类的形式来代替if…else
-
基本介绍:
- 客户端不应该依赖不需要的接口,即一个类对另一个类的依赖需要建立在最小耦合的接口之上
-
解决方法:若发生了接口的冗余,则通过接口的分解方式,将一个接口分解为多个子接口
- 基本介绍:
- 高层模块不应该依赖于低层模块,二者都应该依赖于其抽象
- 抽象不应该依赖细节,细节应该依赖于抽象
- 依赖倒转即面向接口编程
- 注意事项:
- 底层模块尽量有抽象类或接口或两者都有,这样使得程序的稳定性更好
- 变量的声明类型尽量是抽象类或接口,这样使得变量引用和实际对象间有一层缓冲层,有利于程序的扩展和优化
- 继承时遵循里氏替换原则
-
基本介绍:
如果对每个类型为T1的对象o1,有每个类型为T2的对象o2,若将程序所有为o1的地方改为o2,则程序不发生改动,则T2时T1的子类。即引用基类的地方都能透明的使用子类
- 在使用继承时子类尽量不要重写父类方法
- 继承使两个类的耦合程度增强了,在适当的情况下可以通过聚合,组合,依赖来解决
-
对于继承的解耦合:
- 将原来的继承关系进行接触
- 创建一个公共的base类,最抽象的类,使原先具有继承关系的两个类都来继承这个类
- 在其中一个类里(一般为当初的子类),创建(当初父类)变量
- 使用该对象的方法
-
基本介绍:
- 对扩展(提供者)开放,对修改(使用者)封闭。用抽象扩展框架,用实现修改细节
- 当软件的需求发生变化时尽量通过软件的实体行为来发生变化,而不是通过修改已有的代码来实现
- 编程中遵循的其他原则,以及设计模式的目的就是遵循开闭原则
-
解决方法:
- 通过实现接口和抽象类来将实体进行抽离
- 在实现的接口的子类或实现抽象类的子类中进行细节化,从而降低了耦合性
-
基本介绍:
-
一个类应对其他类保持最小的了解
-
类与类之间的关系越密切,其耦合度越大
-
迪米特法则:
即最少知到原则,一个类对自己依赖的类知到越少越好。也就是说,不管被依赖的类有多复杂,都尽量封装自己类内部,对外只提供public方法接口
- 直接朋友:在类 的内部直接调用、声明、使用的类
- 简介朋友:在方法的内部声明的局部变量(此时违反了迪米特法则)
-
-
个人理解:
- 即设计需要按照一定的拓扑关系,不能越级调用,需要按照流程来执行想要的功能
-
注意事项:
- 迪米特法则只要求降低耦合而不是完全没有耦合
- 基本介绍:能够使用合成/聚合的方式就不要使用继承
- 注意事项:
- 找出可能变化之处,并将其独立出来
- 针对接口编程,而不是针对实现编程
- 为了交互对象而实现松耦合
-
类和类之间的关系:依赖,泛化,实现,关联,聚合,组合
-
依赖:
只要是在类中用到了对方就存在依赖关系(最基本的类之间的关系,如果没有这个类,连编译都通过不了)
-
泛化(继承):
依赖关系的特例
-
实现:
实现接口,实现关系也为依赖关系
-
关联:
- 表示类与类之间的特例也为依赖关系
- 关联具有导航性,即一对一或是一对多
一对一:
classDiagram class Person{ IDCard card; }
双向一对一:
-
聚合:
各部分成员之间可以分离,既可以独立存在
-
组合:
各部分成员之间不可以分离,即不可以独立存在,必须共生共灭
-
- 设计模式分为三类,共23种:
- 创建型模式:
- 单例模式
- 抽象工厂模式
- 原型模式
- 建造者模式
- 工厂模式
- 结构型模式:
- 适配器模式
- 桥接模式
- 装饰模式
- 组合模式
- 外观模式
- 享元模式
- 代理模式
- 行为型模式:
- 模板方法模式
- 命令模式
- 访问者模式
- 迭代器模式
- 观察者模式
- 中介者模式
- 备忘录模式
- 解释器模式
- 状态模式
- 策略模式
- 职责链模式
- 创建型模式:
- 概念:单例模式即在软件系统中,对某个类只能存在一个对象,并且该类只提供一个取个该实例的方法(静态方法)
-
静态常量写法
class Hungry1{ // 隐藏构造器,使得类外无法创建对象 private Hungry1() { } // 创建静态对象 static final private Hungry1 h1 = new Hungry1(); // 提供静态对象接口 static Hungry1 getHungrt() { return h1; } }
- 优点:在类装载的时候就完成了实例化,避免了线程同步问题
- 缺点:在类装载的过程中就完成了实例化,没有达到lazyloading的效果。如果从未使用过该实例,则会造成内存的浪费。这种基于ClassLoader的机制避免了多线程问题,但导致类加载的方式有很多,,因此不能确定其他方式不会造成类的加载,从而无法达到lazyloading
-
静态代码块写法
class Hungry1{ private Hungry1() { } static final private Hungry1 h1; static { h1 = new Hungry1(); } static Hungry1 getHungrt() { return h1; } }
- 优缺点同上
-
线程不安全方式
class Hungry2{ private Hungry2() { } static private Hungry2 h2; static Hungry2 getHungry() { if(h2 == null) { h2 = new Hungry2(); return h2; } else return h2; } }
-
优点:起到了lazyloading的效果
-
缺点:只能在单线程下使用,但是由于存在
if(h2 == null) { h2 = new Hungry2(); return h2; }
语句,所以存在线程安全问题,不可以在多线程并发的执行(除非专门控制并发)
-
注意事项:在开发过程中尽量不要使用该方式实现单例模式
-
2. 线程安全式(同步方法方式)
class Hungry2{ private Hungry2() { } static private Hungry2 h2; public static synchronized Hungry2 getHungry() { if(h2 == null) { h2 = new Hungry2(); return h2; } else return h2; } } // 添加syn关键字,保证线程安全
- 优点:保证了线程的同步,解决了线程不安全的问题
- 缺点:使程序的效率变低,需要互斥的执行该函数
-
线程安全式(同步代码块形式)
错误方式,不存在!!!!
class Hungry3{ private Hungry3() { } static private volatile Hungry3 h3; public static Hungry3 getInstance() { if(h3 == null) { synchronized (Hungry3.class) { if(h3 == null)h3 = new Hungry3(); } } return h3; } }
- 优点:Double-Check概念是多线程经常用到的单例方法,进行了两次的null值判断且使用了syn关键词进行同步,既保证了线程安全又保证了高效的处理和懒加载
public class StaticInnerClass { @Test public void test() { } } class Static{ private Static() { } private static class StaticInner{ private static Static instance = new Static(); } public static Static getInstance() { return StaticInner.instance; } }
- 由于静态内部类的特殊性,在外部类装载的时候内部类不会进行装载,因此保证了懒加载,又因为内部类只会装载一次,因此保证了线程的安全
package SingleInstance; import org.junit.jupiter.api.Test; public class EnumClass { @Test public void test() { Enum e1 = Enum.Instance; Enum e2 = Enum.Instance; System.out.println(e1 == e2); } } enum Enum{ Instance(); private Enum() { } }
- 枚举类的机制保证了线程的安全
-
单例模式保证了系统内存内只有一个对象,对于需要频繁创建和销毁的对象,使用单例模式可以提高系统的效率
-
想要实例化一个单例时,使用的是相应的获取方法而不是使用new
-
单例模式使用的场景:
-
需要频繁创建和销毁对象的场景
-
创建对象耗时或耗费资源过多但又经常用到的对象
若能确保单线程的情况下:饿汉式(静态常量、静态代码块)
多线程情况下:双重检查,枚举类,静态内部类
-
package Factory.SimpleFactory; public class Factory { public Pizza getPizza(String name) { Pizza pizza = null; if(name.equals("BJ") || name.equals("TJ")) pizza = new Pizza(name); return pizza; } }
package Factory.SimpleFactory; import org.junit.jupiter.api.Test; public class OrderPizza { private Factory factory = new Factory(); private Pizza pizza = null; @Test public void getPizza() { String name = "BJ"; pizza = factory.getPizza(name); if(pizza == null) System.out.println("no"); else System.out.println("yes"); } }
package Factory.SimpleFactory; public class Pizza { public Pizza(String name) { this.name = name; } String name; }
- 个人理解:
- 简单工厂模式为对于种类较少的多个同种基类的创建,使用一个类进行菜单的作用,利用菜单进行分发,使用工厂类进行创建
package Factory.FactoryMethods.Pizzas; public class Pizza { String name; public Pizza(String name) { this.name = name; } }
package Factory.FactoryMethods.Pizzas; public class BJPizza extends Pizza{ public BJPizza(String name) { // TODO Auto-generated constructor stub super(name); } }
package Factory.FactoryMethods.Orders; import Factory.FactoryMethods.Pizzas.Pizza; import Factory.FactoryMethods.Pizzas.TJPizza; public class TJOrder extends Order{ @Override Pizza getPizza(String name) { // TODO Auto-generated method stub Pizza pizza = null; if(name.equals("22")) pizza = new TJPizza(name); return pizza; } }
package Factory.FactoryMethods.Orders; import Factory.FactoryMethods.Pizzas.Pizza; public abstract class Order { public Order() { // TODO Auto-generated constructor stub } public void orderPizza(String name) { Pizza pizza = getPizza(name); } abstract Pizza getPizza(String name); }
package Factory.FactoryMethods.Orders; import Factory.FactoryMethods.Pizzas.BJPizza; import Factory.FactoryMethods.Pizzas.Pizza; public class BJorder extends Order{ @Override Pizza getPizza(String name) { // TODO Auto-generated method stub Pizza pizza = null; if(name.equals("ll")) pizza = new BJPizza(name); return pizza; } }
package Factory.FactoryMethods.Orders; import Factory.FactoryMethods.Pizzas.Pizza; import Factory.FactoryMethods.Pizzas.TJPizza; public class TJOrder extends Order{ @Override Pizza getPizza(String name) { // TODO Auto-generated method stub Pizza pizza = null; if(name.equals("22")) pizza = new TJPizza(name); return pizza; } }
- 理解:大工厂与小工厂,大工厂负责一个菜单的作用,并实现基本方法,小工厂进行细化,执行各个部分,适用于类树较深的形式
package Factory.Abstract; public class Order { AbsFactory factory; public void setFactory(AbsFactory factory,String name) { this.factory = factory; Pizza pizza = factory.getPizza(name); } }
package Factory.Abstract; public interface AbsFactory { public Pizza getPizza(String name); }
package Factory.Abstract; public class BJFactory implements AbsFactory{ @Override public Pizza getPizza(String name) { // TODO Auto-generated method stub Pizza pizza = null; if(name.equals("6")) pizza = new BJPizza(name); return pizza; } }
package Factory.Abstract; public class TJPizza extends Pizza{ TJPizza(String name){ super(name); } }
- 理解:进一步分离了对象的创建与对象的种类之间的关系,设立了Order作为中间商,中间商负责获取条件,且向工厂传输,细节化后的工厂负责特殊的对象创建,都实现了最抽象的抽象共产的接口
- 利用clone进行对象的创建,快速的创建多个同种对象,简化对象的创建过程,提高创建效率
- 不用重新初始化对象,动态的获得运行的状态
- 如果原始对象的属性发生变化,其他对象无需进行更改(浅拷贝)
- 缺点:需要为每一个类配置拷贝方法,对已经存在的类来说违反了oop原则
- 建造者模式的模式角色:
- Product:一个具体的产品对象
- Builder:抽象的建造者(接口或者抽象类)
- ConcreateBuilder:具体的建造者
- Director:指挥者,构建一个使用Builder接口的对象。它主要是用于创建一个很复杂的对象,拥有两个作用:
- 隔离客户和产品的生产过程
- 负责控制产品对象的生产过程
package Builder; public interface BuildeHouse { House house = new House(); public void buildWalls(); public void buildBasic(); public void roofed(); }
package Builder; public class CommonHouse implements BuildeHouse{ @Override public void buildBasic() { // TODO Auto-generated method stub System.out.println("basic"); } @Override public void buildWalls() { // TODO Auto-generated method stub System.out.println("walls"); } @Override public void roofed() { // TODO Auto-generated method stub System.out.println("roof"); } }
package Builder; public class HighHouse implements BuildeHouse{ @Override public void buildBasic() { // TODO Auto-generated method stub System.out.println("highB"); } @Override public void buildWalls() { // TODO Auto-generated method stub System.out.println("highW"); } @Override public void roofed() { // TODO Auto-generated method stub System.out.println("highR"); } }
package Builder; public class House { }
package Builder; public class Director { BuildeHouse builder; public Director(BuildeHouse builder) { this.builder = builder; } public void setBuilder(BuildeHouse builder) { this.builder = builder; } public void buildHouse() { builder.buildBasic(); // builder.buildBasic(); builder.buildWalls(); builder.roofed(); } }
package Builder; import org.junit.jupiter.api.Test; public class Client { @Test public void test() { Director director = new Director(new CommonHouse()); director.setBuilder(new HighHouse()); director.buildHouse(); } }
-
注意事项:
- 客户端不需要知到内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象
- 每一个具体的建造者都相对独立,而与其他的具体建造者无关,因此可以很方便的替代具体建造者或增加新的具体建造者,用户使用不同的具体建造者得到不同的产品对象
- 可以更加精细的控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程
- 增加具体的建造者无需修改原有的代码,满足了OOP原则
- 建造者模式需要具体的实现类步骤相差不大,若步骤相差很大则不适合使用建造者模式
- 若产品内部变化复杂,则需要定义许多具体的建造者来适应这种变化,会使得系统变得庞大,需要考虑是否继续使用建造者模式
-
抽象工厂模式和建造者模式的区别:
抽象工厂模式实现对产品家族的创建,即不需要关系产品生产的步骤,只需要将生产的产品交给指定的工程工厂进行生产即可,至于生产步骤需要具体的工厂自己指定,而建造者模式对具体产品的生产步骤已经确定,而各个具体的建造者需要按照相同的模块,但至于模块的细节需要各个具体建造者自己实现
-
注意事项:
- 三种适配方式是以src如何给到adapter来命名的
-
- 类适配器:以类给到,在Adapter里,就是将src当作类,继承(泛化)
- 对象适配器:以对象给到,在adapter里,将src作为一个对象属性,持有(聚合)
- 接口适配器:以实现接口的形式,使adapter实现src的方法(实现)
- Adapter模式最大的作用是将原本不兼容的接口融合在一起工作
-
介绍:Adapter类,通过继承src完成dst的接口,实现src -> dst
package Adapter; public class Voltage220V { public int outPut220V() { return 220; } }
package Adapter; public interface Voltage5V { public int outPut5V(); }
package Adapter; public class VoltageAdapter extends Voltage220V implements Voltage5V{ @Override public int outPut5V() { // TODO Auto-generated method stub int input = outPut220V(); return (input / 44); } }
-
注意事项:
- 由于java的单继承机制,所以要求dst只能为接口,有一定的局限性
- 由于继承了src,使得src的方法暴露出来,增加了adapter的成本
- 继承来src,便于更灵活的重写Src的方法
- 思路:
- 基本思路与类适配器相同,但是不是继承Src,而是通过聚合的方式持有src类,进而实现dst接口,从而完成src -> dst的转变
- 符合合成复用原则,用关联的方式替代继承的方式
- 介绍:
- 只使用一个接口中的个别函数,可以通过构建一个抽象类空实现接口的函数,再构建该抽象函数的子类,重写指定的函数,完成接口的适配
- 适用于一个接口不想使用其全部方法的场景
-
基本介绍:
-
将实现和抽象放在两个不同的层次,使两个层次可以独立的改变
-
是一种结构型设计模式
-
Bridge模式基于类的最小的设计原则
-
各成分:
-
Client:桥接模式的调用者
-
Abstraction:维护了实现层的接口,二者是聚合关系
-
RefinedAbstraction:接口的具体实现类
-
-
package Bridge; public interface Brand { void open(); void close(); }
package Bridge; public class FolderPhone extends Phone{ }
package Bridge; public abstract class Phone { Brand brand; }
package Bridge; public class Vivo implements Brand{ @Override public void close() { // TODO Auto-generated method stub } @Override public void open() { // TODO Auto-generated method stub } }
package Bridge; public class XiaoMi implements Brand{ @Override public void close() { // TODO Auto-generated method stub } @Override public void open() { // TODO Auto-generated method stub } }
- 注意事项:
- 实现了抽象和实现部分的分离。从而提高了系统的灵活性,让抽象部分和实现部分分离开,有助于系统的分层设计,从而产生更好的系统结构
- 对于系统的高层部分,只需要知道系统的实现部分和抽象部分的接口就可以,其他部分由业务来实现
- 桥接模式代替多层继承方案,可以减少子类的个数,降低系统的管理和维护成本
- 桥接模式的引入增加了系统的理解和设计难度,桥接模式要求将抽象部分和实现部分分离出,有一定的局限性
以io模块为例:
- IO模块:
- FilterInputStream:装饰器
- InputStream:抽象类
- FileInputStream,StringBufferStrean,ByteArratInputStream:具体被修饰类
- BufferInputStream,DataInputStream…:具体修饰类
package Decorate; public class BlackCoffee extends Coffee{ @Override int cost() { // TODO Auto-generated method stub return super.cost() + 10; } }
package Decorate; public class Coffee extends Drink{ @Override int cost() { // TODO Auto-generated method stub return 5; } @Override void setCost() { // TODO Auto-generated method stub } }
package Decorate; public class Decorater extends Drink{ Drink drink; @Override int cost() { // TODO Auto-generated method stub int cost_temp = 0; if(drink != null) cost_temp = drink.cost(); return cost_temp + cost; } void setDrink(Drink drink) { this.drink = drink; }@Override void setCost() { // TODO Auto-generated method stub } }
package Decorate; public abstract class Drink { int cost; abstract int cost(); abstract void setCost(); }
package Decorate; public class Sugar extends Decorater{ void getCost() { int temp_cost = 0; if(drink != null) temp_cost = drink.cost(); cost = temp_cost + 5; } }
-
个人理解:
在装饰者模式中,对于某类的修饰,将被修饰的部分和修饰部分进行抽象和分离,使修饰和被修饰的部分都继承一个公共的抽象类,从而便于在修饰类中添加被修饰的对象,使得可以多层嵌套进行修饰
- 介绍:
- 外观模式可以理解为转换一群接口,客户只要调用一个接口,而不调用多个接口才能达到目的
- 外观模式就是解决多个复杂接口带来的使用困难,起到简化用户 *** 作的作用
- 个人理解:就是封装!!!即将子系统进行封装,提供一个总的接口进行调用
-
基本介绍:
- 享元模式也叫蝇量模式,运用共享技术有效的支持大量细粒度的对象
- 常用于 系统底层的开发,解决系统性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来使用,避免重复创建,如果没有需要的则创建一个
- 享元模式能够解决重复对象的内存浪费问题,当系统中由大量的相似对象,需要缓冲池时不需要重复创建对象,可以从缓冲池中获得,这样可以降低系统内存,同时提高效率
- 享元模式的经典应用即池技术
-
模式的角色:
- FlyWeight:抽象的享元角色,产品的抽象类,定义出对象的外部状态和内部状态的接口或实现
- ConcreteFlyWeigft:具体的享元角色,具体的产品类,实现角色定义的相关业务
- UnsharedConcreteFlyWeight:不可共享的角色,不会出现在享元工厂中
- FlyWeightFactory:享元工厂,用于构建一个容器池(set),同时构建从容器中获取对象的方法
- 内部状态和外部状态:
- 内部状态:内部状态是对象共享出来的属性,不会随着环境的变化而变化
- 外部状态:外部状态随着环境的改变而改变,是不可以共享的状态
package FlyWeight; public abstract class WebSite { }
package FlyWeight; public class ConcreteWebSite extends WebSite{ String type; public ConcreteWebSite(String type) { // TODO Auto-generated constructor stub this.type = type; } }
package FlyWeight; import java.util.HashMap; public class WebSiteFactory { private HashMappool = new HashMap (); public WebSite getWebSite(String type) { if(!pool.containsKey(type)) pool.put(type, new ConcreteWebSite(type)); return (WebSite)pool.get(type); } }
- 注意事项:
- 享元模式,享即共享,元即对象
- 享元模式注意内部和外部的划分,共享内部,细化外部
- 使用享元模式需要一个工厂类进行控制
- 代理类和被代理类通过继承相同的父类或实现相同的接口来完成代理关系,并使代理类完成和被代理类的聚合关系,从而使得被代理类通过代理类完成相应的功能
package Proxy; public interface TeacherDao { void teach(); }
package Proxy; public class TeacherDaoImpl implements TeacherDao{ @Override public void teach() { // TODO Auto-generated method stub System.out.println("this is teacher"); } }
package Proxy; public class TeacherProxy implements TeacherDao{ TeacherDao teacher; public TeacherProxy(TeacherDao teacher) { // TODO Auto-generated constructor stub this.teacher = teacher; } @Override public void teach() { // TODO Auto-generated method stub System.out.println("this proxy saying"); teacher.teach(); } }
package Proxy; import org.junit.jupiter.api.Test; public class TestClass { @Test public void test() { TeacherDao teacher = new TeacherProxy(new TeacherDaoImpl()); teacher.teach(); } }动态代理(JDK代理)
- 利用JDK提供的接口方法创建代理对象,使得代理对象不需要手动实现接口,但是被代理对象依然需要实现接口
package Proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyFactory { Object target; public void setTarget(Object obj) { this.target = obj; } public ProxyFactory(Object obj) { target = obj; } public Object getProxyInstance() { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new InvocationHandler( ) { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("proxy"); // TODO Auto-generated method stub method.invoke(target, args); return null; } }); } }
参数说明:
动态代理(CGLIB代理)newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new InvocationHandler):
- 参数一:被代理对象的类的加载器
- 参数二:被代理对象类的接口集合
- 参数三:事件处理,执行目标对象的方法时,会触发事件处理器的方法,进而完成代理方法的加强
-
基本介绍:
- 无论是静态代理,或是动态的JDK代理,都需要类实现一个或多个接口,当被代理对象未实现接口时,则不可以使用以上的两种代理方式
- CGLIB代理也叫做子类代理,他是在内存中构建一个子类对象而实现对目标功能的拓展
- CGLIB底层通过ASM字节码处理框架来转化成字节码文件
-
CGLIB代理是实现的步骤:
- 引入相应的jar包
- 在内存中动态的构建子类,注意代理类不能未final类
- ,目标对象的方法若为 final / static则目标方法不会被拦截,即目标方法不会被增强
package Proxy.CGLIB; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class ProxyFactory implements MethodInterceptor{ Object target; public ProxyFactory(Object obj) { // TODO Auto-generated constructor stub this.target = obj; } public Object getProxyInstance() { // 创建工具类 Enhancer enhancer = new Enhancer(); //设置父类 enhancer.setSuperclass(target.getClass()); //设置回调函数 enhancer.setCallback(this); //创建子类对象,即代理对象 return enhancer.create(); } @Override public Object intercept(Object arg0, Method method, Object[] arg2, MethodProxy arg3) throws Throwable { // TODO Auto-generated method stub System.out.println("cglib代理方法"); method.invoke(target, arg2); return null; } }
package Proxy.CGLIB; public class TeacherDao { void teach() { System.out.println("被代理对象"); } }
package Proxy.CGLIB; import org.junit.jupiter.api.Test; public class Client { @Test public void test() { TeacherDao teacher = new TeacherDao(); TeacherDao proxy = (TeacherDao)new ProxyFactory(teacher).getProxyInstance(); System.out.println(proxy.getClass()); } }代理模式的变种
- 防火墙代理:内网通过代理穿过防火墙实现对公网的访问
- 缓存代理:请求图片等资源时先到缓存代理中去取,若缓存代理中无数据,再到数据库中去取
- 远程代理:远程对象的本地代表,通过它可以将远程对象当作本地对象来调用,远程代理通过网络和真正的远程对象沟通
- 同步代理:主要使用在多线程编程中,完成多线程的同步工作
-
基本介绍:
- 在一个抽象类中公开定义了执行它的方法的模板,它的子类可以按需要重写方法的实现,但调用将以抽象类中定义的方式执行
- 模板方法模式是一个 *** 作中算法的骨架,将一些步骤延迟到子类中实现,使得子类可以不改变一个算法的结构,就可以重写定义算法的某些特征
- 模板方法模式属于行为型模式
-
模式角色:
- AbstractClass:定义了抽象方法,和抽象方法的执行骨架
- ConcreteClass:具体实现抽象的方法但是需要按照骨架进行顺序执行
package TemplateMethods; public abstract class MakeDrink { // 注意添加final关键词,防止子类重写该方法 final void operate() { System.out.println("select:" + select() + " add: " + add()); } abstract String select(); abstract String add(); }
package TemplateMethods; public class RedDrink extends MakeDrink{ @Override String add() { // TODO Auto-generated method stub return "red!!"; } @Override String select() { // TODO Auto-generated method stub return "select red!"; } }
package TemplateMethods; import org.junit.jupiter.api.Test; public class Client { @Test public void test() { MakeDrink drink = new RedDrink(); drink.operate(); } }
- 个人理解:将抽象的逻辑顺序抽离出,定义逻辑,具体实现交由子类完成
- 在模板方法中,定义一个方法,默认不做任何事情,可以在子类中视情况选择实现,该方法称为钩子方法
package TemplateMethods; public abstract class MakeDrink { final void operate() { if(isPure()) { addEquip(); } } void addEquip() { System.out.println("select:" + select() + " add: " + add()); } abstract boolean isPure(); abstract String select(); abstract String add(); }
package TemplateMethods; public class PureDrink extends MakeDrink{ @Override String add() { // TODO Auto-generated method stub return null; } @Override boolean isPure() { // TODO Auto-generated method stub return true; } @Override String select() { // TODO Auto-generated method stub return null; } }命令模式
- 使得发起请求的对象和执行请求的对象进行解耦,发器请求的对象是调用者,调用者只需要调用命令对象的execute方法就可以让接收者进行工作,而不必知到接收者是谁,命令对象会负责让接受者执行请求的动作,请求发起者和请求执行者的解耦是通过命令对象实现的
- 设计一个命令队列。把命令放进队列可以多线程的执行命令
- 可以设计空命令对象,方便null值的判定
- 设置undo方法进行命令的撤销
- 基本介绍:
- 模式角色:
- Visitor:抽象的visitor类,为子类提供visit方法
- ConcreteVistior:visitor的具体实现类
- Visited:被访问者的抽象类,为子类提供访问者的accpet接口
- ConcreteVisited:被访问者的具体实现类,调用accept方法
package Visitors; public class ObjectStructure { Person persons[]; void setPerson() { } }
package Visitors; public abstract class Action { abstract void setAction(); }
package Visitors; public class Failed extends Action{ @Override void setAction() { // TODO Auto-generated method stub } }
package Visitors; public class Sucessfully extends Action{ @Override void setAction() { // TODO Auto-generated method stub } }
package Visitors; public abstract class Person { Action action; abstract void accpet(Action action); }
package Visitors; public class Man extends Person{ @Override void accpet(Action action) { // TODO Auto-generated method stub this.action = action; } }
package Visitors; public class Woman extends Person{ @Override void accpet(Action action) { // TODO Auto-generated method stub this.action = action; } }
package Visitors; public class ObjectStructure { Person persons[]; void setPerson() { } }
- 个人理解:将有交互的两个类抽象出来,在抽象的类中就实现交互和聚合,为子类的交互提供接口
- 首先将一个对象作为参数传入(第一次分派)
- 在被传入的方法中将this作为参数传入第一个对象的某个方法内
package Visitors.Doule; public class A { String name = "A"; public void getBMethods(B b) { b.method(this); } }
package Visitors.Doule; public class B { public void method(A a) { System.out.println(a.name); } }
package Visitors.Doule; import org.junit.jupiter.api.Test; public class TestClass { @Test public void test() { A a = new A(); a.getBMethods(new B()); } }
- 注意事项:
- 优点:
- 访问者模式具有单一性职责原则,具有更好的可扩展性,灵活性高
- 访问者模式可以对功能进行统一,可以做报表,UI,拦截器和过滤器,使用与结构稳定的系统
- 缺点:
- 具体元素需要对访问者公布细节,也就是访问者关注了类的其他细节
- 违反了依赖倒转原则,访问者依赖的是具体的元素而不是抽象的元素
- 优点:
- 基本介绍:
- 迭代器模式常用的设计模式,属于行为模式
- 如果集合元素是用不同方式实现的,,则客户端遍历这些元素时就需要使用不同的方式,可能还会暴露元素内部属性
- 迭代器模式提供遍历元素的统一接口用一致的方法遍历元素,不需要知到集合元素的底层表示,即不爆露内部结构
- 迭代器职责介绍:
- Iterator:迭代器接口,系统提供,含有hasNext,next,remove
- ConcreteIterator:具体的迭代类,管理迭代
- Aggregate:一个统一的聚合接口,将客户端与聚合解耦
- ConcreteAggregate:具体的聚合持有对象,并返回迭代器对象,遍历元素
- Client:客户端,通过Iterator和Aggregate依赖子类
package Iterator; import java.util.Iterator; import java.util.List; public interface College { public Iterator getIterator(); }
package Iterator; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class ComputerCollege implements College{ Listlist; String name = "computer"; public ComputerCollege() { // TODO Auto-generated constructor stub list = new ArrayList<>(); list.add(new Department("66")); list.add(new Department("77")); } @Override public Iterator getIterator() { // TODO Auto-generated method stub return new ComputerIterator(list); } public String toString() { return name; } }
package Iterator; import java.util.Iterator; import java.util.List; public class ComputerIterator implements Iterator{ Listlist; int index = 0; @Override public boolean hasNext() { // TODO Auto-generated method stub if(index < list.size())return true; return false; } @Override public Object next() { // TODO Auto-generated method stub return list.get(index++); // return null; } public ComputerIterator(List list) { // TODO Auto-generated constructor stub this.list = list; } }
package Iterator; public class Department { String name; public Department(String name) { // TODO Auto-generated constructor stub this.name = name; } public String toString() { return name; } }
package Iterator; import java.util.Iterator; import java.util.List; public class OutPutImpl { Listlist; public OutPutImpl(List list) { // TODO Auto-generated constructor stub this.list = list; } public void printCollege() { Iterator iterator = list.iterator(); while(iterator.hasNext()) { College college = iterator.next(); System.out.println(college); printDepartment(college.getIterator()); } } private void printDepartment(Iterator iterator) { while(iterator.hasNext()) { System.out.println(iterator.next()); } } }
package Iterator; import java.util.ArrayList; import org.junit.jupiter.api.Test; public class TestClass { // @Test // public void test() { // Iterator// // } // 7 @Test public void client() { ArrayList list = new ArrayList(); list.add(new ComputerCollege()); OutPutImpl impl = new OutPutImpl(list); impl.printCollege(); } }
- 注意事项:
- 优点:
- 提供了一个统一遍历容器的方法,不需要考虑聚合的类型,使用一种方法就可以遍历对象
- 隐藏了聚合的内部的细节,客户端遍历时只能取到迭代器,而不知道迭代的具体组成
- 遵循了单一职责原则,将对象的管理和对象的遍历进行分开,使用内部类的迭代进行对象的迭代使用外部类的方法进行对象的管理
- 缺点:每个聚合的对象都需要一组迭代器,多个类会有多个迭代器
- 优点:
package Observer; public interface Subject { public void registObserver(Observer o); public void removerObserver(Observer o); public void notifyObserver(); }
package Observer; import java.util.ArrayList; import java.util.List; public class WeatherData implements Subject{ Listlist; double value; public WeatherData() { // TODO Auto-generated constructor stub list = new ArrayList(); } @Override public void notifyObserver() { // TODO Auto-generated method stub for(Observer o : list) o.update(value); } @Override public void registObserver(Observer o) { // TODO Auto-generated method stub if(!list.contains(o)) list.add(o); } @Override public void removerObserver(Observer o) { // TODO Auto-generated method stub if(list.contains(o)) list.remove(o); } public void valueReset(double v) { value = v; notifyObserver(); } }
package Observer; public interface Observer { public void update(double value); public void show(double value); }
package Observer; public class CurrentCondition implements Observer{ @Override public void update(double value) { // TODO Auto-generated method stub show(value); } @Override public void show(double value) { // TODO Auto-generated method stub System.out.println("new value: " + value); } }
package Observer; public class BaiduObserver implements Observer{ @Override public void show(double value) { // TODO Auto-generated method stub System.out.println("baidu: " + value); } @Override public void update(double value) { // TODO Auto-generated method stub show(value); } }
package Observer; import org.junit.jupiter.api.Test; public class Client { public static void main(String[] args) { WeatherData data = new WeatherData(); CurrentCondition condition = new CurrentCondition(); data.registObserver(condition); data.registObserver(new BaiduObserver()); data.valueReset(2); //data.removerObserver(); } }中介者模式
-
基本介绍:
- 用一个中介对象来封装一系列的对象交互。中介者使其对象不需要显示的互相引用,从而使其耦合松散,而且可以独立的改变他们之间的交互
- 中介者者模式属于行为型模式,使代码易于维护
-
模式角色:
-
角色说明:
- Mediator:中介者的抽象对象,定义了同事对象到中介者对象的接口
- Colleague:抽象同事类,抽象子系统的父类
- ConcreteMediator:具体的中介者,实现抽象方法,他需要知到所有的具体的同事类,以集合形式管理并接受同事对象的消息完成抽象任务
- ConcreteColleague:具体的同事类,每个同事只知道自己的行为,不了解其他同事类的行为,但他们都依赖中介者对象
-
注意事项:
- 多个类相互耦合,会形成网状结构,使用中介者模式会使网状结构变为星型结构
- 减少类间依赖,降低了耦合,符合迪米特法则
- 中介者承担了较多的责任
package Mediator; public abstract class Mediator { public abstract void registerColleague(Colleague colleague); public abstract void removeColleague(Colleague colleague); // public abstract void adjust(); public abstract void getMessage(Colleague colleague,int index); }
package Mediator; import java.util.ArrayList; import java.util.List; public class ConcreteMediator extends Mediator{ Listlist; public ConcreteMediator() { // TODO Auto-generated constructor stub list = new ArrayList(); } @Override public void registerColleague(Colleague colleague) { // TODO Auto-generated method stub list.add(colleague); } @Override public void removeColleague(Colleague colleague) { // TODO Auto-generated method stub list.remove(colleague); } @Override public void getMessage(Colleague colleague,int index) { // TODO Auto-generated method stub // getMessage from colleague and do property action } }
package Mediator; public abstract class Colleague { public Colleague(String name,Mediator mediator) { // TODO Auto-generated constructor stub this.name = name; this.mediator = mediator; } String name; Mediator mediator; public abstract void sendMessage(); }
package Mediator; public class ConcreteColleague extends Colleague{ public ConcreteColleague(String name,Mediator mediator) { // TODO Auto-generated constructor stub super(name,mediator); } @Override public void sendMessage() { // TODO Auto-generated method stub this.mediator.getMessage(this,1); } }备忘录模式
-
基本介绍:
- 在不破坏封装性的前提下捕获对象的状态,并在对象之外保存该状态,这样以后就可以将该对象回复到原先保存的状态
- 备忘录模式属于行为模式
-
个人理解:
- 即对对象的信心进行保存
- 可以联合原型模式一起使用通过clone
- 也可以通过序列化和反序列化实现
-
基本介绍:
- 在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元通过语法分析器形成语法分析树,最后形成一棵抽象的语法分析树,这里的词法分析器和语法分析器都可以看作为解释器
- 解释器模式:指定一个表达式,定义它的文法的一种表示,并定义一个解释器,使用该解释器来解析语言中的句子
-
模式角色:
-
Context:环境角色,含有解释器之外的全局信息
-
Abstractexpression:抽象表达式,声明一个抽象的解释 *** 作,这个为抽象语法树中的所有结点共享
-
Terminalexpression:为终结符表达式,实现与文法中的终结相关的解释 *** 作
-
NoneTerminalexpression:为非终结符表达式
-
解释器模式使得程序变得更为复杂,需要谨慎使用
- 基本介绍:
- 主要用来解决对象在多种状态转换时,需要对外输出不同行为的问题。状态和行为是一一对应的,状态之间可以相互转化
- 当一个对象内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类
- 基本介绍:
- 策略模式定义算法族,分别封装起来,让他们相互替换,此模式让算法的变化独立于使用算法的用户
- 算法体现了几个设计原则:
- 把变化的代码从不变的代码中分离出来
- 针对接口编程而不是具体的类(定义了策略接口)
- 更多的使用了组合和聚合而不是继承(里氏替换原则)
package Strategy; public abstract class Duck { Fly fly; Quack quack; void duckFly() { this.fly.fly(); } void duckQuack() { this.quack.quack(); } }
package Strategy; public class ToyDuck extends Duck{ public ToyDuck() { // TODO Auto-generated constructor stub this.fly = new BadFly(); this.quack = new BadQuack(); } }
package Strategy; public interface Fly { void fly(); }
package Strategy; public class BadFly implements Fly{ @Override public void fly() { // TODO Auto-generated method stub System.out.println("bad fly"); } }
package Strategy; public interface Quack { void quack(); }
package Strategy; public class BadQuack implements Quack{ @Override public void quack() { // TODO Auto-generated method stub System.out.println("bad quack"); } }
- 个人理解:策略模式即将提供一个可以聚合的抽象类作为接口,使得子类各种子类拥有更低的耦合性,使得方法将进行分离,做到物尽其用,但是会造成类的冗余
-
基本介绍:
- 职责链模式,为请求创建一个接收者对象的链,这种模式对请求的发送者和接收者进行解耦
- 职责链模式通常每个接收者都包含对另一个接收者的引用,若该接收者无法处理,则转交给引用的接收者
- 避免了if…else…的使用,降低了程序的耦合度,体现了面向对象的思想
- 使多个对象都有机会处理请求,从而避免了请求的发送者和接收者的紧密的耦合
-
模式角色:
- Handler:抽象的处理者,进行请求的接收,同时含有handler的聚合
- ConcreteHandler:具体的处理者,进行具体请求的处理,可以访问他的后继处理者,若可以处理则处理后返回否则交由后继进行处理,形成职责链
- Request:含有多个属性表示一个请求
补充 增强耦合的方法
- 继承
- if…else…(当出现多个if…else…时)
摘自:https://www.cnblogs.com/zhengbin/p/5654805.html
先补充一下概念:Java 内存模型中的可见性、原子性和有序性。可见性:
可见性是一种复杂的属性,因为可见性中的错误总是会违背我们的直觉。通常,我们无法确保执行读 *** 作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情。为了确保多个线程之间对内存写入 *** 作的可见性,必须使用同步机制。
**可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。**也就是一个线程修改的结果。另一个线程马上就能看到。比如:用volatile修饰的变量,就会具有可见性。volatile修饰的变量不允许线程内部缓存和重排序,即直接修改内存。所以对其他线程是可见的。但是这里需要注意一个问题,volatile只能让被他修饰内容具有可见性,但不能保证它具有原子性。比如 volatile int a = 0;之后有一个 *** 作 a++;这个变量a具有可见性,但是a++ 依然是一个非原子 *** 作,也就是这个 *** 作同样存在线程安全问题。
在 Java 中 volatile、synchronized 和 final 实现可见性。
原子性:
**原子是世界上的最小单位,具有不可分割性。**比如 a=0;(a非long和double类型) 这个 *** 作是不可分割的,那么我们说这个 *** 作时原子 *** 作。再比如:a++; 这个 *** 作实际是a = a + 1;是可分割的,所以他不是一个原子 *** 作。非原子 *** 作都会存在线程安全问题,需要我们使用同步技术(sychronized)来让它变成一个原子 *** 作。一个 *** 作是原子 *** 作,那么我们称它具有原子性。java的concurrent包下提供了一些原子类,我们可以通过阅读API来了解这些原子类的用法。比如:AtomicInteger、AtomicLong、AtomicReference等。
在 Java 中 synchronized 和在 lock、unlock 中 *** 作保证原子性。
有序性:
Java 语言提供了 volatile 和 synchronized 两个关键字来保证线程之间 *** 作的有序性,volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized 是由“一个变量在同一个时刻只允许一条线程对其进行 lock *** 作”这条规则获得的,此规则决定了持有同一个对象锁的两个同步块只能串行执行。
Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新 *** 作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的 *** 作与其他内存 *** 作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。
在访问volatile变量时不会执行加锁 *** 作,因此也就不会使执行线程阻塞,因此volatile变量是一种比sychronized关键字更轻量级的同步机制。
浅拷贝和深拷贝 浅拷贝当对非 volatile 变量进行读写的时候,每个线程先从内存拷贝变量到CPU缓存中。如果计算机有多个CPU,每个线程可能在不同的CPU上被处理,这意味着每个线程可以拷贝到不同的 CPU cache 中。
而声明变量是 volatile 的,JVM 保证了每次读变量都从内存中读,跳过 CPU cache 这一步。
当一个变量定义为 volatile 之后,将具备两种特性:1.保证此变量对所有的线程的可见性,这里的“可见性”,如本文开头所述,当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。但普通变量做不到这点,普通变量的值在线程间传递均需要通过主内存(详见:Java内存模型)来完成。
2.禁止指令重排序优化。有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)” *** 作,这个 *** 作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置),只有一个CPU访问内存时,并不需要内存屏障;(什么是指令重排序:是指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理)。
volatile 性能:volatile 的读性能消耗与普通变量几乎相同,但是写 *** 作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。
- 实现方法:通过实现Clonable的接口来进行浅拷贝
- 介绍:
- 若拷贝的值为基本数据类型,则拷贝会拷贝一份数据值
- 若拷贝的值为对象类型,则浅拷贝只会拷贝一份引用,即指向的为相同的对象
-
实现方法:
-
通过重写clone函数进行深拷贝:
public class DeepClone implements Cloneable,Serializable{ public CopiedClass c = new CopiedClass(); @Override protected Object clone() throws CloneNotSupportedException { // TODO Auto-generated method stub DeepClone instance = null; instance = (DeepClone) super.clone(); instance.c = (CopiedClass) c.clone(); return instance; } }
-
通过序列化实现深拷贝(推荐)
public class DeepClone implements Cloneable,Serializable{ public CopiedClass c = new CopiedClass(); public DeepClone deepClone() { DeepClone instance = null; ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; ByteArrayInputStream bis = null; ObjectInputStream ois = null; try { // 序列化 bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(this); // 反序列化 bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis); instance = (DeepClone)ois.readObject(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { try { bos.close(); oos.close(); bis.close(); ois.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return instance; } } // 注意,待序列化中的所有引用属性都需要进行序列化 public class CopiedClass implements Cloneable,Serializable{ String name; public CopiedClass(String name) { // TODO Auto-generated constructor stub this.name = name; } public CopiedClass() { // TODO Auto-generated constructor stub } @Override protected Object clone() throws CloneNotSupportedException { // TODO Auto-generated method stub return super.clone(); } @Test public void test() throws CloneNotSupportedException { CopiedClass a = new CopiedClass("ych"); CopiedClass b = (CopiedClass) a.clone(); } }
-
-
接口方法:
- boolean hasNext()
- E next()
- void remove()
-
迭代器一般设置成内部类,设置index进行元素的判断,在调用外部类进行内部类的创建
-
```java
package Iterator.MyIterator;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class A {
List list = new ArrayList();public A() {
// TODO Auto-generated constructor stub
list.add(new B(“66”));
list.add(new B(“77”));
list.add(new B(“88”));}
public Iterator iterator() {
return new AIterator();
}
// 内部类
class AIterator implements Iterator{int index = 0; public AIterator() { // TODO Auto-generated constructor stub } @Override public boolean hasNext() { // TODO Auto-generated method stub if(index < list.size())return true; return false; } @Override public Object next() { // TODO Auto-generated method stub return list.get(index++); }
}
}
```java package Iterator.MyIterator; public class B { String name; public B(String name) { // TODO Auto-generated constructor stub this.name = name; } public String toString() { return name; } }
package Iterator.MyIterator; import java.util.Iterator; import org.junit.jupiter.api.Test; public class TestClass { @Test public void test() { A a = new A(); Iterator iterator = a.iterator(); while(iterator.hasNext()) { System.out.println(iterator.next()); } } }
- 采用了观察者模式:但是在JDK实现的该模式中,没有Subject的接口,Observable直接充当了Subject的实现类,完成了对Observer的基本管理(register,remove,notify)
- Observer即观察者,拥有update的方法
- Observable和Observer的使用方法和普通的观察者方法中的用法一样,只是Observable不是接口而是类,通过继承实现
@Test public void test() { Integer arr[] = {9,6,3,8,5,2,7,4,1}; Arrays.sort(arr,(var1,var2) -> { if(var1.compareTo(var2) > 0)return 1; else return -1; }); System.out.println(Arrays.toString(arr)); }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)