设计模式日常学习(七)

news2024/11/24 8:57:24

6.5 状态模式

6.5.1 概述

【例】通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态,停止状态,运行状态。每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,就不能进行开门操作,而如果电梯门是停止状态,就可以执行开门操作。

类图如下:

public interface ILift {
    //电梯的4个状态
    //开门状态
    public final static int OPENING_STATE = 1;
    //关门状态
    public final static int CLOSING_STATE = 2;
    //运行状态
    public final static int RUNNING_STATE = 3;
    //停止状态
    public final static int STOPPING_STATE = 4;
​
    //设置电梯的状态
    public void setState(int state);
​
    //电梯的动作
    public void open();
    public void close();
    public void run();
    public void stop();
}
​
public class Lift implements ILift {
    private int state;
​
    @Override
    public void setState(int state) {
        this.state = state;
    }
​
    //执行关门动作
    @Override
    public void close() {
        switch (this.state) {
            case OPENING_STATE:
                System.out.println("电梯关门了。。。");//只有开门状态可以关闭电梯门,可以对应电梯状态表来看
                this.setState(CLOSING_STATE);//关门之后电梯就是关闭状态了
                break;
            case CLOSING_STATE:
                //do nothing //已经是关门状态,不能关门
                break;
            case RUNNING_STATE:
                //do nothing //运行时电梯门是关着的,不能关门
                break;
            case STOPPING_STATE:
                //do nothing //停止时电梯也是关着的,不能关门
                break;
        }
    }
​
    //执行开门动作
    @Override
    public void open() {
        switch (this.state) {
            case OPENING_STATE://门已经开了,不能再开门了
                //do nothing
                break;
            case CLOSING_STATE://关门状态,门打开:
                System.out.println("电梯门打开了。。。");
                this.setState(OPENING_STATE);
                break;
            case RUNNING_STATE:
                //do nothing 运行时电梯不能开门
                break;
            case STOPPING_STATE:
                System.out.println("电梯门开了。。。");//电梯停了,可以开门了
                this.setState(OPENING_STATE);
                break;
        }
    }
​
    //执行运行动作
    @Override
    public void run() {
        switch (this.state) {
            case OPENING_STATE://电梯不能开着门就走
                //do nothing
                break;
            case CLOSING_STATE://门关了,可以运行了
                System.out.println("电梯开始运行了。。。");
                this.setState(RUNNING_STATE);//现在是运行状态
                break;
            case RUNNING_STATE:
                //do nothing 已经是运行状态了
                break;
            case STOPPING_STATE:
                System.out.println("电梯开始运行了。。。");
                this.setState(RUNNING_STATE);
                break;
        }
    }
​
    //执行停止动作
    @Override
    public void stop() {
        switch (this.state) {
            case OPENING_STATE: //开门的电梯已经是是停止的了(正常情况下)
                //do nothing
                break;
            case CLOSING_STATE://关门时才可以停止
                System.out.println("电梯停止了。。。");
                this.setState(STOPPING_STATE);
                break;
            case RUNNING_STATE://运行时当然可以停止了
                System.out.println("电梯停止了。。。");
                this.setState(STOPPING_STATE);
                break;
            case STOPPING_STATE:
                //do nothing
                break;
        }
    }
}
​
public class Client {
    public static void main(String[] args) {
        Lift lift = new Lift();
        lift.setState(ILift.STOPPING_STATE);//电梯是停止的
        lift.open();//开门
        lift.close();//关门
        lift.run();//运行
        lift.stop();//停止
    }
}

问题分析:

  • 使用了大量的switch…case这样的判断(if…else也是一样),使程序的可阅读性变差。
  • 扩展性很差。如果新加了断电的状态,我们需要修改上面判断逻辑

定义:

对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。

6.5.2 结构

状态模式包含以下主要角色。

  • 环境(Context)角色:也称为上下文,它定义了客户程序需要的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
  • 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为。
  • 具体状态(Concrete State)角色:实现抽象状态所对应的行为。

6.5.3 案例实现

对上述电梯的案例使用状态模式进行改进。类图如下:

//抽象状态类
public abstract class LiftState {
    //定义一个环境角色,也就是封装状态的变化引起的功能变化
    protected Context context;
​
    public void setContext(Context context) {
        this.context = context;
    }
​
    //电梯开门动作
    public abstract void open();
​
    //电梯关门动作
    public abstract void close();
​
    //电梯运行动作
    public abstract void run();
​
    //电梯停止动作
    public abstract void stop();
}
​
//开启状态
public class OpenningState extends LiftState {
​
    //开启当然可以关闭了,我就想测试一下电梯门开关功能
    @Override
    public void open() {
        System.out.println("电梯门开启...");
    }
​
    @Override
    public void close() {
        //状态修改
        super.context.setLiftState(Context.closeingState);
        //动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作
        super.context.getLiftState().close();
    }
​
    //电梯门不能开着就跑,这里什么也不做
    @Override
    public void run() {
        //do nothing
    }
​
    //开门状态已经是停止的了
    @Override
    public void stop() {
        //do nothing
    }
}
​
//运行状态
public class RunningState extends LiftState {
​
    //运行的时候开电梯门?你疯了!电梯不会给你开的
    @Override
    public void open() {
        //do nothing
    }
​
    //电梯门关闭?这是肯定了
    @Override
    public void close() {//虽然可以关门,但这个动作不归我执行
        //do nothing
    }
​
    //这是在运行状态下要实现的方法
    @Override
    public void run() {
        System.out.println("电梯正在运行...");
    }
​
    //这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了
    @Override
    public void stop() {
        super.context.setLiftState(Context.stoppingState);
        super.context.stop();
    }
}
​
//停止状态
public class StoppingState extends LiftState {
​
    //停止状态,开门,那是要的!
    @Override
    public void open() {
        //状态修改
        super.context.setLiftState(Context.openningState);
        //动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作
        super.context.getLiftState().open();
    }
​
    @Override
    public void close() {//虽然可以关门,但这个动作不归我执行
        //状态修改
        super.context.setLiftState(Context.closeingState);
        //动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作
        super.context.getLiftState().close();
    }
​
    //停止状态再跑起来,正常的很
    @Override
    public void run() {
        //状态修改
        super.context.setLiftState(Context.runningState);
        //动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作
        super.context.getLiftState().run();
    }
​
    //停止状态是怎么发生的呢?当然是停止方法执行了
    @Override
    public void stop() {
        System.out.println("电梯停止了...");
    }
}
​
//关闭状态
public class ClosingState extends LiftState {
​
    @Override
    //电梯门关闭,这是关闭状态要实现的动作
    public void close() {
        System.out.println("电梯门关闭...");
    }
​
    //电梯门关了再打开,逗你玩呢,那这个允许呀
    @Override
    public void open() {
        super.context.setLiftState(Context.openningState);
        super.context.open();
    }
​
​
    //电梯门关了就跑,这是再正常不过了
    @Override
    public void run() {
        super.context.setLiftState(Context.runningState);
        super.context.run();
    }
​
    //电梯门关着,我就不按楼层
    @Override
    public void stop() {
        super.context.setLiftState(Context.stoppingState);
        super.context.stop();
    }
}
​
//环境角色
public class Context {
    //定义出所有的电梯状态
    public final static OpenningState openningState = new OpenningState();//开门状态,这时候电梯只能关闭
    public final static ClosingState closeingState = new ClosingState();//关闭状态,这时候电梯可以运行、停止和开门
    public final static RunningState runningState = new RunningState();//运行状态,这时候电梯只能停止
    public final static StoppingState stoppingState = new StoppingState();//停止状态,这时候电梯可以开门、运行
​
​
    //定义一个当前电梯状态
    private LiftState liftState;
​
    public LiftState getLiftState() {
        return this.liftState;
    }
​
    public void setLiftState(LiftState liftState) {
        //当前环境改变
        this.liftState = liftState;
        //把当前的环境通知到各个实现类中
        this.liftState.setContext(this);
    }
​
    public void open() {
        this.liftState.open();
    }
​
    public void close() {
        this.liftState.close();
    }
​
    public void run() {
        this.liftState.run();
    }
​
    public void stop() {
        this.liftState.stop();
    }
}
​
//测试类
public class Client {
    public static void main(String[] args) {
        Context context = new Context();
        context.setLiftState(new ClosingState());
​
        context.open();
        context.close();
        context.run();
        context.stop();
    }
}

6.5.4 优缺点

1,优点:

  • 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
  • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。

2,缺点:

  • 状态模式的使用必然会增加系统类和对象的个数。
  • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
  • 状态模式对"开闭原则"的支持并不太好。

6.5.5 使用场景

  • 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
  • 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。

6.6 观察者模式

6.6.1 概述

定义:

又被称为发布-订阅(Publish/Subscribe)模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。

6.6.2 结构

在观察者模式中有如下角色:

  • Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
  • ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
  • Observer:抽象观察者,是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
  • ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

6.6.3 案例实现

【例】微信公众号

在使用微信公众号时,大家都会有这样的体验,当你关注的公众号中有新内容更新的话,它就会推送给关注公众号的微信用户端。我们使用观察者模式来模拟这样的场景,微信用户就是观察者,微信公众号是被观察者,有多个的微信用户关注了程序猿这个公众号。

类图如下:

定义抽象观察者类,里面定义一个更新的方法

public interface Observer {
    void update(String message);
}

定义具体观察者类,微信用户是观察者,里面实现了更新的方法

public class WeixinUser implements Observer {
    // 微信用户名
    private String name;
​
    public WeixinUser(String name) {
        this.name = name;
    }
    @Override
    public void update(String message) {
        System.out.println(name + "-" + message);
    }
}

定义抽象主题类,提供了attach、detach、notify三个方法

public interface Subject {
    //增加订阅者
    public void attach(Observer observer);
​
    //删除订阅者
    public void detach(Observer observer);
    
    //通知订阅者更新消息
    public void notify(String message);
}

微信公众号是具体主题(具体被观察者),里面存储了订阅该公众号的微信用户,并实现了抽象主题中的方法

public class SubscriptionSubject implements Subject {
    //储存订阅公众号的微信用户
    private List<Observer> weixinUserlist = new ArrayList<Observer>();
​
    @Override
    public void attach(Observer observer) {
        weixinUserlist.add(observer);
    }
​
    @Override
    public void detach(Observer observer) {
        weixinUserlist.remove(observer);
    }
​
    @Override
    public void notify(String message) {
        for (Observer observer : weixinUserlist) {
            observer.update(message);
        }
    }
}

客户端程序

public class Client {
    public static void main(String[] args) {
        SubscriptionSubject mSubscriptionSubject=new SubscriptionSubject();
        //创建微信用户
        WeixinUser user1=new WeixinUser("孙悟空");
        WeixinUser user2=new WeixinUser("猪悟能");
        WeixinUser user3=new WeixinUser("沙悟净");
        //订阅公众号
        mSubscriptionSubject.attach(user1);
        mSubscriptionSubject.attach(user2);
        mSubscriptionSubject.attach(user3);
        //公众号更新发出消息给订阅的微信用户
        mSubscriptionSubject.notify("传智黑马的专栏更新了");
    }
}

6.6.4 优缺点

1,优点:

  • 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
  • 被观察者发送通知,所有注册的观察者都会收到信息【可以实现广播机制】

2,缺点:

  • 如果观察者非常多的话,那么所有的观察者收到被观察者发送的通知会耗时
  • 如果被观察者有循环依赖的话,那么被观察者发送通知会使观察者循环调用,会导致系统崩溃

6.6.5 使用场景

  • 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
  • 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时。

6.6.6 JDK中提供的实现

在 Java 中,通过 java.util.Observable 类和 java.util.Observer 接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例。

1,Observable类

Observable 类是抽象目标类(被观察者),它有一个 Vector 集合成员变量,用于保存所有要通知的观察者对象,下面来介绍它最重要的 3 个方法。

  • void addObserver(Observer o) 方法:用于将新的观察者对象添加到集合中。
  • void notifyObservers(Object arg) 方法:调用集合中的所有观察者对象的 update方法,通知它们数据发生改变。通常越晚加入集合的观察者越先得到通知。
  • void setChange() 方法:用来设置一个 boolean 类型的内部标志,注明目标对象发生了变化。当它为true时,notifyObservers() 才会通知观察者。

2,Observer 接口

Observer 接口是抽象观察者,它监视目标对象的变化,当目标对象发生变化时,观察者得到通知,并调用 update 方法,进行相应的工作。

【例】警察抓小偷

警察抓小偷也可以使用观察者模式来实现,警察是观察者,小偷是被观察者。代码如下:

小偷是一个被观察者,所以需要继承Observable类

public class Thief extends Observable {
​
    private String name;
​
    public Thief(String name) {
        this.name = name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
​
    public String getName() {
        return name;
    }
​
    public void steal() {
        System.out.println("小偷:我偷东西了,有没有人来抓我!!!");
        super.setChanged(); //changed  = true
        super.notifyObservers();
    }
}

警察是一个观察者,所以需要让其实现Observer接口

public class Policemen implements Observer {
​
    private String name;
​
    public Policemen(String name) {
        this.name = name;
    }
    public void setName(String name) {
        this.name = name;
    }
​
    public String getName() {
        return name;
    }
​
    @Override
    public void update(Observable o, Object arg) {
        System.out.println("警察:" + ((Thief) o).getName() + ",我已经盯你很久了,你可以保持沉默,但你所说的将成为呈堂证供!!!");
    }
}
public class Policemen implements Observer {
​
    private String name;
​
    public Policemen(String name) {
        this.name = name;
    }
    public void setName(String name) {
        this.name = name;
    }
​
    public String getName() {
        return name;
    }
​
    @Override
    public void update(Observable o, Object arg) {
        System.out.println("警察:" + ((Thief) o).getName() + ",我已经盯你很久了,你可以保持沉默,但你所说的将成为呈堂证供!!!");
    }
}

6.7 中介者模式

6.7.1 概述

一般来说,同事类之间的关系是比较复杂的,多个同事类之间互相关联时,他们之间的关系会呈现为复杂的网状结构,这是一种过度耦合的架构,即不利于类的复用,也不稳定。例如在下左图中,有六个同事类对象,假如对象1发生变化,那么将会有4个对象受到影响。如果对象2发生变化,那么将会有5个对象受到影响。也就是说,同事类之间直接关联的设计是不好的。

如果引入中介者模式,那么同事类之间的关系将变为星型结构,从下右图中可以看到,任何一个类的变动,只会影响的类本身,以及中介者,这样就减小了系统的耦合。一个好的设计,必定不会把所有的对象关系处理逻辑封装在本类中,而是使用一个专门的类来管理那些不属于自己的行为。

定义:

又叫调停模式,定义一个中介角色来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。

6.7.2 结构

中介者模式包含以下主要角色:

  • 抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。
  • 具体中介者(ConcreteMediator)角色:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。
  • 抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。
  • 具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。

6.7.3 案例实现

【例】租房

现在租房基本都是通过房屋中介,房主将房屋托管给房屋中介,而租房者从房屋中介获取房屋信息。房屋中介充当租房者与房屋所有者之间的中介者。

类图如下:

//抽象中介者
public abstract class Mediator {
    //申明一个联络方法
    public abstract void constact(String message,Person person);
}
​
//抽象同事类
public abstract class Person {
    protected String name;
    protected Mediator mediator;
​
    public Person(String name,Mediator mediator){
        this.name = name;
        this.mediator = mediator;
    }
}
​
//具体同事类 房屋拥有者
public class HouseOwner extends Person {
​
    public HouseOwner(String name, Mediator mediator) {
        super(name, mediator);
    }
​
    //与中介者联系
    public void constact(String message){
        mediator.constact(message, this);
    }
​
    //获取信息
    public void getMessage(String message){
        System.out.println("房主" + name +"获取到的信息:" + message);
    }
}
​
//具体同事类 承租人
public class Tenant extends Person {
    public Tenant(String name, Mediator mediator) {
        super(name, mediator);
    }
​
    //与中介者联系
    public void constact(String message){
        mediator.constact(message, this);
    }
​
    //获取信息
    public void getMessage(String message){
        System.out.println("租房者" + name +"获取到的信息:" + message);
    }
}
​
//中介机构
public class MediatorStructure extends Mediator {
    //首先中介结构必须知道所有房主和租房者的信息
    private HouseOwner houseOwner;
    private Tenant tenant;
​
    public HouseOwner getHouseOwner() {
        return houseOwner;
    }
​
    public void setHouseOwner(HouseOwner houseOwner) {
        this.houseOwner = houseOwner;
    }
​
    public Tenant getTenant() {
        return tenant;
    }
​
    public void setTenant(Tenant tenant) {
        this.tenant = tenant;
    }
​
    public void constact(String message, Person person) {
        if (person == houseOwner) {          //如果是房主,则租房者获得信息
            tenant.getMessage(message);
        } else {       //反正则是房主获得信息
            houseOwner.getMessage(message);
        }
    }
}
​
//测试类
public class Client {
    public static void main(String[] args) {
        //一个房主、一个租房者、一个中介机构
        MediatorStructure mediator = new MediatorStructure();
​
        //房主和租房者只需要知道中介机构即可
        HouseOwner houseOwner = new HouseOwner("张三", mediator);
        Tenant tenant = new Tenant("李四", mediator);
​
        //中介结构要知道房主和租房者
        mediator.setHouseOwner(houseOwner);
        mediator.setTenant(tenant);
​
        tenant.constact("需要租三室的房子");
        houseOwner.constact("我这有三室的房子,你需要租吗?");
    }
}

6.7.4 优缺点

1,优点:

  • 松散耦合中介者模式通过把多个同事对象之间的交互封装到中介者对象里面,从而使得同事对象之间松散耦合,基本上可以做到互补依赖。这样一来,同事对象就可以独立地变化和复用,而不再像以前那样“牵一处而动全身”了。
  • 集中控制交互多个同事对象的交互,被封装在中介者对象里面集中管理,使得这些交互行为发生变化的时候,只需要修改中介者对象就可以了,当然如果是已经做好的系统,那么就扩展中介者对象,而各个同事类不需要做修改。
  • 一对多关联转变为一对一的关联没有使用中介者模式的时候,同事对象之间的关系通常是一对多的,引入中介者对象以后,中介者对象和同事对象的关系通常变成双向的一对一,这会让对象的关系更容易理解和实现。

2,缺点:

当同事类太多时,中介者的职责将很大,它会变得复杂而庞大,以至于系统难以维护。

6.7.5 使用场景

  • 系统中对象之间存在复杂的引用关系,系统结构混乱且难以理解。
  • 当想创建一个运行于多个类之间的对象,又不想生成新的子类时。

6.8 迭代器模式

6.8.1 概述

定义:

提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。

6.8.2 结构

迭代器模式主要包含以下角色:

  • 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合元素以及创建迭代器对象的接口。
  • 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
  • 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、next() 等方法。
  • 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。

6.8.3 案例实现

【例】定义一个可以存储学生对象的容器对象,将遍历该容器的功能交由迭代器实现,涉及到的类如下:

定义迭代器接口,声明hasNext、next方法

public interface StudentIterator {
    boolean hasNext();
    Student next();
}

定义具体的迭代器类,重写所有的抽象方法

public class StudentIteratorImpl implements StudentIterator {
    private List<Student> list;
    private int position = 0;
​
    public StudentIteratorImpl(List<Student> list) {
        this.list = list;
    }
​
    @Override
    public boolean hasNext() {
        return position < list.size();
    }
​
    @Override
    public Student next() {
        Student currentStudent = list.get(position);
        position ++;
        return currentStudent;
    }
}

定义抽象容器类,包含添加元素,删除元素,获取迭代器对象的方法

public interface StudentAggregate {
    void addStudent(Student student);
​
    void removeStudent(Student student);
​
    StudentIterator getStudentIterator();
}

定义具体的容器类,重写所有的方法

public class StudentAggregateImpl implements StudentAggregate {
​
    private List<Student> list = new ArrayList<Student>();  // 学生列表
​
    @Override
    public void addStudent(Student student) {
        this.list.add(student);
    }
​
    @Override
    public void removeStudent(Student student) {
        this.list.remove(student);
    }
​
    @Override
    public StudentIterator getStudentIterator() {
        return new StudentIteratorImpl(list);
    }
}

6.8.4 优缺点

1,优点:

  • 它支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式。在迭代器模式中只需要用一个不同的迭代器来替换原有迭代器即可改变遍历算法,我们也可以自己定义迭代器的子类以支持新的遍历方式。
  • 迭代器简化了聚合类。由于引入了迭代器,在原有的聚合对象中不需要再自行提供数据遍历等方法,这样可以简化聚合类的设计。
  • 在迭代器模式中,由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无须修改原有代码,满足 “开闭原则” 的要求。

2,缺点:

增加了类的个数,这在一定程度上增加了系统的复杂性。

6.8.5 使用场景

  • 当需要为聚合对象提供多种遍历方式时。
  • 当需要为遍历不同的聚合结构提供一个统一的接口时。
  • 当访问一个聚合对象的内容而无须暴露其内部细节的表示时。

6.8.6 JDK源码解析

迭代器模式在JAVA的很多集合类中被广泛应用,接下来看看JAVA源码中是如何使用迭代器模式的。

List<String> list = new ArrayList<>();
Iterator<String> iterator = list.iterator(); //list.iterator()方法返回的肯定是Iterator接口的子实现类对象
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

看完这段代码是不是很熟悉,与我们上面代码基本类似。单列集合都使用到了迭代器,我们以ArrayList举例来说明

  1. List:抽象聚合类
  2. ArrayList:具体的聚合类
  3. Iterator:抽象迭代器
  4. list.iterator():返回的是实现了 Iterator 接口的具体迭代器对象

具体的来看看 ArrayList的代码实现

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    
    public Iterator<E> iterator() {
        return new Itr();
    }
    
    private class Itr implements Iterator<E> {
        int cursor;       // 下一个要返回元素的索引
        int lastRet = -1; // 上一个返回元素的索引
        int expectedModCount = modCount;
​
        Itr() {}
        
        //判断是否还有元素
        public boolean hasNext() {
            return cursor != size;
        }
​
        //获取下一个元素
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }
        ...
}

这部分代码还是比较简单,大致就是在 iterator 方法中返回了一个实例化的 Iterator 对象。Itr是一个内部类,它实现了 Iterator 接口并重写了其中的抽象方法。

注意:

当我们在使用JAVA开发的时候,想使用迭代器模式的话,只要让我们自己定义的容器类实现java.util.Iterable并实现其中的iterator()方法使其返回一个 java.util.Iterator 的实现类就可以了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/43327.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么是副业思维,副业应该怎么做,用创业思维分析副业的可行性

副业其实也算是创业的一种&#xff0c;他考量的不仅仅是自身的知识储备&#xff0c;还有你对市场的看法&#xff0c;再加上一定的做副业的技巧&#xff0c;下面分享七个做好副业的技巧​。 1.循序渐进投入 不要大量投资。首先&#xff0c;使用相对较轻的方法来验证创业理念是否…

教程六 在Go中使用Energy创建跨平台GUI - 应用下载事件

教程-示例-文档 介绍 Energy应用下载文件时触发的下载事件和使用 我们在页面上下载文件时&#xff0c;可以对文件下载时的处理&#xff0c;例如&#xff1a;保存路径&#xff0c;下载取消&#xff0c;开始、暂停。 下面将用代码和注释&#xff0c;和简要的说明来演示 Go代码…

GUI编程--PyQt5--布局管理

文章目录布局管理布局步骤QHBoxLayout & QVBoxLayoutQFormLayoutQGridLayout布局管理 布局&#xff0c;按照一定规则&#xff0c;将子控件放入父控件 手动布局&#xff1b;绝对布局move & resize & resizeEvent布局管理器&#xff0c;实现快速布局&#xff0c;是…

08 SQL优化

上一篇文章记录了索引的创建、使用、设计&#xff0c;除了索引方面还需要注意平日对于SQL的使用&#xff0c;对SQL进行优化&#xff1b;SQL的优化是建立在索引使用的基础上 这篇笔记将从以下7个方面对SQL进行优化。 1. 插入数据 使用批量插入,避免循环单条插入 注意批量插入不…

贤鱼的刷题日常(数据结构栈学习)--P1175 表达式的转换--题目详解

&#x1f3c6;今日学习目标&#xff1a; &#x1f340;例题讲解P1175 表达式的转换 ✅创作者&#xff1a;贤鱼 ⏰预计时间&#xff1a;25分钟 &#x1f389;个人主页&#xff1a;贤鱼的个人主页 &#x1f525;专栏系列&#xff1a;c &#x1f341;贤鱼的个人社区&#xff0c;欢…

ServletConfig 和 ServletContext

1 ServletConfig 1.1 ServletConfig 介绍 ServletConfig 是 Servlet 的配置参数对象&#xff0c;在 Servlet 的规范中&#xff0c;允许为每一个 Servlet 都提供一些初始化的配置。所以&#xff0c;每个 Servlet 都有一个自己的 ServletConfig。作用&#xff1a;在 Servlet 的…

静息态fMRI中的非线性功能网络连接

在这项工作中&#xff0c;我们关注功能网络中的显式非线性关系。我们介绍了一种使用归一化互信息(NMI)计算不同大脑区域之间非线性关系的技术。我们使用模拟数据演示了我们提出的方法&#xff0c;然后将其应用到Damaraju等人先前研究过的数据集。静息状态fMRI数据包括151名精神…

玩转高并发,17年开发经验架构师,历时三年编写Java高并发三部曲

前言 5G&#xff0c;IO&#xff0c;多屏合一&#xff0c;方物互联时代来了&#xff01;太分n式、高并发、微服务架构己经成为Java后端应用的主流架构。但是对Java高并发&#xff0c;springcloudRPC底层原理、Nginx底层原理等核心知识&#xff0c;广大的Java开发同学们相对欠缺…

【踩坑汇总】CLion开启QT编程

一下全部内容全都是大佬lht的经验&#xff0c;我只是记录一下给大家。 问题&#xff1a;Qt5Config.cmake找不到 解决办法&#xff1a; set(CMAKE_PREFIX_PATH "E:/Qt/Qt5.12.11/5.12.11/mingw73_64/lib/cmake/Qt5") 找到Qt5Config.cmake路径&#xff0c;添加上面这…

东南亚LazadaShopee文具类目好做吗?一文带你了解各国热销及需求品类

在东南亚&#xff0c;消费者刚刚经历完双11独有的“速度与激情”——11月11日00&#xff1a;11&#xff0c;开售11分钟&#xff0c;Lazada平台的销售额相比日销暴涨124倍&#xff1b;早上8&#xff1a;17&#xff0c;第一单跨越重洋的中国跨境商品就已成功送达签收。 东南亚&a…

Listen,Attend,and Spell(LAS)——李宏毅人类语言处理学习笔记3

Listen Encoder目标&#xff1a; 去掉noises&#xff0c;提取出相关信息 encoder有很多做法&#xff1a; CNN见文章&#xff1a;CNN-卷积神经网络 self-attention见文章self-attention Pyramid RNN将两个结合&#xff0c;然后送到下一层。Pooling over time则是两个中取一…

代谢组学文献分享:地中海饮食、血浆代谢组和心血管疾病风险

​全球三分之一的死亡由心血管疾病造成&#xff0c;2015-2020年美国膳食指南建议&#xff0c;地中海饮食是预防心血管疾病的一项重要且具有成本效益的战略措施。代谢组学文献分享&#xff0c;发表在期刊European Heart Journ-al&#xff08;IF 22.637&#xff09;上题目为“Th…

网络协议

网络通信协议&#xff1a;计算机网络中实现通信必须有一些约定&#xff0c;即通信协议&#xff0c;对速率、传输代码、代 码结构、传输控制步骤、出错控制等制定标准。 问题&#xff1a;网络协议太复杂&#xff1a;计算机网络通信涉及内容很多&#xff0c;比如指定源地址和目标…

【多标签, 极限的多标签算法】评价指标梳理

具体研究多标签和极限多标签 (XML) 的时候, 合理使用评价指标是关键. 最近在研究极限多标签算法的时候发现了它和传统多标签算法的评价指标是有异的, 而且我曾经积累的传统多标签评价指标也没有一个系统的体系 (很混乱). 于是写下本文用于自我总结. 查询目录<想看什么直接通…

语音识别翻译怎么做?这些方法值得收藏

随着网络的不断发展&#xff0c;我们可以通过网络与世界各地的网友进行聊天。小伙伴们平时会和外国人交流吗&#xff1f;如果是文字聊天&#xff0c;我们看不懂的时候&#xff0c;还可以直接复制文字进行翻译。那如果外国网友发了段语音&#xff0c;结果我们大部分内容听不懂的…

电力行业人员定位管理解决方案之智能巡检

智能巡检引入大数据分析理念&#xff0c;通过数字化技术实现对生产现场各关键要素的全面感知和实时互联&#xff0c;形成项目现场“数据一个库、监管一张网、管理一条线“。 在信息技术高速发展的今天&#xff0c;传统人工巡视、手工纸介质记录的工作方式已经无法满足电力设备巡…

第7章 博客文章的前端渲染显示

说实话本人通过Vue页面实现前端对后端数据的渲染显示也是初学咋练&#xff0c;但后端实现本人却是老鸟&#xff0c;对于后端开发者来说如果&#xff0c;渲染显示的软件是浏览器&#xff0c;除非团队中有Vue方面的大拿&#xff0c;不管是PC浏览器还是移动PC浏览器&#xff0c;Ra…

元宇宙初体验

14天学习训练营导师课程&#xff1a; 张子良《 元宇宙体系结构、关键技术和实践探索》 前言 最近这段时间加入勤学会的学习中&#xff0c;我加入的是元宇宙相关的学习组&#xff0c;为什么我选择元宇宙&#xff0c;不仅因为元宇宙是应用场景和生活方式的未来&#xff0c;而且元…

启动 idea 弹出“Failed to load JVM DLL\bin\server\jvm.dll”错误的解决方法

打开idea报failed to load JVM DLL 原因1&#xff1a; 查看是否缺少Microsoft Visual C 2010 Redistributable Package x64&#xff0c;没有则安装。 地址&#xff1a; 32 bit: http://www.microsoft.com/download/en/details.aspx?id5555 64 bit: http://www.microsoft.com…

面试必问 创建10个a点击弹出下标

<script> // for (let i 1; i <11; i) { // var adocument.createElement("a"); // a.href"#"; // a.innerHTML"<br />a标签"i // document.body…