一、行为模式:关注系统中对象之间的相互交互,研究系统在运行时对象之间的相互通信和协作,进一步明确对象的职责,共11种模式。
模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。
1.责任链模式(职责链模)(Chain Of Resposibility)
1)定义:将能够处理同一类的对象连成一条链,所提交的请求沿着链传递,链上的对象逐个判断是否有能力处理该请求,如果能则处理,如果不能则传递给链上的下一个对象。
链表方式定义职责链:
/* * 抽象类,Leader * */ public abstract class Leader { protected String name; protected Leader nextLeader; //责任链上的后继对象 public Leader(String name) { this.name = name; } //设定责任链上的后继对象 public void setNextLeader(Leader nextLeader) { this.nextLeader = nextLeader; } /* * 处理请求的核心的业务方法 * */ public abstract void handleRequest(LeaveRequest request); }
/* * 主任类 * */ public class Director extends Leader { public Director(String name) { super(name); } @Override public void handleRequest(LeaveRequest request) { if (request.getLeaveDays() < 3) { System.out.println("员工:" + request.getEmpName() + "请假,天数:" + request.getLeaveDays() + ",理由:" + request.getReason()); System.out.println("主任:"+this.name+",审批通过"); } else { if (this.nextLeader != null) { this.nextLeader.handleRequest(request); } } } }
/* * 经理类 * */ public class Manager extends Leader { public Manager(String name) { super(name); } @Override public void handleRequest(LeaveRequest request) { if (request.getLeaveDays() < 10) { System.out.println("员工:" + request.getEmpName() + "请假,天数:" + request.getLeaveDays() + ",理由:" + request.getReason()); System.out.println("经理:"+this.name+",审批通过"); } else { if (this.nextLeader != null) { this.nextLeader.handleRequest(request); } } } }
/* * 副总经理类 * */ public class ViceGeneralManager extends Leader { public ViceGeneralManager(String name) { super(name); } @Override public void handleRequest(LeaveRequest request) { if (request.getLeaveDays() < 20) { System.out.println("员工:" + request.getEmpName() + "请假,天数:" + request.getLeaveDays() + ",理由:" + request.getReason()); System.out.println("副经理:"+this.name+",审批通过"); } else { if (this.nextLeader != null) { this.nextLeader.handleRequest(request); } } } }
/* * 总经理类 * */ public class GeneralManager extends Leader { public GeneralManager(String name) { super(name); } @Override public void handleRequest(LeaveRequest request) { if (request.getLeaveDays() < 30) { System.out.println("员工:" + request.getEmpName() + "请假,天数:" + request.getLeaveDays() + ",理由:" + request.getReason()); System.out.println("总经理:" + this.name + ",审批通过"); } else { System.out.println("莫非" + request.getEmpName() + "想辞职,居然请假" + request.getLeaveDays() + "天!"); } } }
/* * 封装请假的基本信息 * */ public class LeaveRequest { private String empName; private int leaveDays; private String reason; public LeaveRequest(String empName, int leaveDays, String reason) { this.empName = empName; this.leaveDays = leaveDays; this.reason = reason; } public String getEmpName() { return empName; } public void setEmpName(String empName) { this.empName = empName; } public int getLeaveDays() { return leaveDays; } public void setLeaveDays(int leaveDays) { this.leaveDays = leaveDays; } public String getReason() { return reason; } public void setReason(String reason) { this.reason = reason; } }
public class Client { public static void main(String[] args) { Leader a = new Director("张三"); Leader b = new Manager("李四"); Leader c = new ViceGeneralManager("王五"); Leader d = new GeneralManager("小刘"); //组织责任链对象的关系 a.setNextLeader(b); b.setNextLeader(c); c.setNextLeader(d); //开始请假操作 LeaveRequest request1 = new LeaveRequest("TOM", 10, "回家"); a.handleRequest(request1); } }
非链表方式定义职责链:
通过集合、数组生成职责链更加实用!实际上,很多项目中,每个具体的Handler并不是由开发团队定义的,而是项目上线后由外部单位追加的,所以使用链表的方式定义COR链就很困难。
开发中常见的场景:
-java,异常机制就是一种责任链模式。一个try可以对应多个catch,当第一个catch不匹配类型,则自动跳到第二个catch
-javascript语言中,事件的冒泡和捕获机制。java语言中,事件的处理采用观察者模式
-Servlet开发中,过滤器的链式处理
-Struts2中,拦截器的调用也是典型的责任链模式
2.迭代器模式(iterator)
场景:
1)提供一种可以遍历聚合对象的方式。又称为:游标cursor模式
2)聚合对象:存储数据
3)迭代器:遍历数据
/* *自定义的迭代器接口 * */ public interface MyIterator { void first();//将游标指向第一个元素 void next();//将游标指向下一个元素 boolean hasNext();//判断是否存在下一个元素 boolean isFirst(); boolean isLast(); Object getCurrentObj();//获取当前游标指向的对象 }
/* * 自定义聚合类 * */ public class ConcreateMyAggregate { private List<Object> list = new ArrayList<>(); public ConcreateMyAggregate() { } public void addObject(Object object) { this.list.add(object); } public void removeObject(Object object) { this.list.remove(object); } public List<Object> getList() { return list; } public void setList(List<Object> list) { this.list = list; } //获得迭代器 public MyIterator createIterator() { return new ConcreateIterator(); } //使用内部类定义迭代器,可以直接使用外部类的属性 private class ConcreateIterator implements MyIterator { private int cursor;//定义游标,用于记录遍历时的位置 @Override public void first() { cursor = 0; } @Override public void next() { if (cursor < list.size()) { cursor++; } } @Override public boolean hasNext() { if (cursor < list.size()) { return true; } return false; } @Override public boolean isFirst() { return cursor==0 ? true : false; } @Override public boolean isLast() { return cursor == list.size() - 1 ? true : false; } @Override public Object getCurrentObj() { return list.get(cursor); } } }
public class Client { public static void main(String[] args) { ConcreateMyAggregate cma = new ConcreateMyAggregate(); cma.addObject("aaa"); cma.addObject("bbb"); cma.addObject("ccc"); MyIterator iter = cma.createIterator(); while (iter.hasNext()) { System.out.println(iter.getCurrentObj()); iter.next(); } } }
3.中介者模式(mediator)
核心:
1)如果一个系统中对象之间的联系呈现为网状结构,对象之间存在大量多对多关系,将导致关系及其复杂性,这些对象称为"同事对象"
2)我们可以引入一个中介者对象,使各个同事对象只是跟中介者打交道,将复杂的网络结构化解为如下的星形结构
本质:解耦多个同事对象之间的交互关系.每个对象都持有中介者对象的引用,只是和中介者对象打交道.我们通过中介者对象统一管理这些交互关系.
/* * 同事类的接口 * */ public interface Department { void selfAction();//做本部门的事情 void outAction();//向总经理发出申请 }
/* * 研发部 * */ public class Development implements Department { private Mediator m; //持有中介者(总经理)的引用 public Development(Mediator m) { this.m = m; m.register("development", this); //向中介者注册 } @Override public void selfAction() { System.out.println("专心科研"); } @Override public void outAction() { System.out.println("汇报工作!没钱了,需要资金支持"); m.command("finacial"); //向其他同事类发出命令 } }
/* * 市场部 * */ public class Market implements Department { private Mediator m; //持有中介者(总经理)的引用 public Market(Mediator m) { this.m = m; m.register("market", this);//向中介者注册 } @Override public void selfAction() { System.out.println("跑去接项目"); } @Override public void outAction() { System.out.println("汇报工作!项目承接的进度,需要资金支持"); m.command("finacial"); //中介者向财务部发出命令 } }
/* * 财务部 * */ public class Finacial implements Department { private Mediator m; //持有中介者(总经理)的引用 public Finacial(Mediator m) { this.m = m; m.register("finacial", this); } @Override public void selfAction() { System.out.println("数钱"); } @Override public void outAction() { System.out.println("汇报工作!钱太多!"); } }
/* * 抽象中介者 * */ public interface Mediator { void register(String dname, Department d); void command(String dname); }
/* * 具体中介者 * */ public class President implements Mediator { private Map<String, Department> map = new HashMap<>(); //用来存储具体同事类 @Override public void register(String dname, Department d) { map.put(dname, d); } @Override public void command(String dname) { map.get(dname).selfAction(); //向某一同事类发出命令,然后那个同事类执行本部的任务 } }
public class Client { public static void main(String[] args) { Mediator m = new President(); Market market = new Market(m); Development devp = new Development(m); Finacial f = new Finacial(m); market.selfAction(); market.outAction(); } }
4.命令模式(command)
将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作.也称之为:动作Action模式,事务transaction模式
结构:
1)Command抽象命令类
2)ConcrateCommand具体命令类
3)Invoke调用者/请求者
请求的发送者,它通过命令对象来执行请求.一个调用者并不需要在设计时确定接收者,因此它只与抽象命令类之间存在关联.在程序运行时,将调用对象的execute(),间接调用接收者的相关操作.
4)Receiver接收者
接收者执行与请求相关操作,具体实现对请求的业务处理
未抽象前,实际执行操作内容的对象
5)Client客户类
在客户类中需要创建调用者对象,具体命令对象,在创建具体命令对象时应指定对应的接收者.发送者和接收者之间没有关系,都是通过命令对象间接调用
/* * 真正命令的执行者 * */ public class Receiver { public void action() { System.out.println("Receiver.action()"); } }
public interface Command { /* * 这个方法是一个返回结果为空的方法 * 实际项目中,可以根据需求设计多个不同的方法 * */ void execute(); } class ConcrateCommand implements Command { private Receiver receiver;//真正命令的执行者 public ConcrateCommand(Receiver receiver) { this.receiver = receiver; } @Override public void execute() { //真正命令前或后,执行相关的处理 receiver.action(); } }
/* * 命令的调用者/发起者 * */ public class Invoke { //也可以通过容器List<Command>容纳很多命令对象,进行批处理.数据库底层的事务管理就是类似的结构 private Command command; public Invoke(Command command) { this.command = command; } //业务方法,用于调用命令类的方法 public void call() { command.execute(); } }
public class Client { public static void main(String[] args) { Command c = new ConcrateCommand(new Receiver()); Invoke i = new Invoke(c); i.call(); } }
5.解析器模式(Interpreter)
1)是一种不常用的设计模式
2)用于描述如何构成一个简单的语言解析器,主要用于使用面向对象语言开发的编译器和解释器设计
3)当我们需要开发一种新的语言时,可以考虑使用解释器模式
4)尽量不要使用解释器模式,后期维护会有很大麻烦.在项目中,可以使用Jruby,Groovy,java的js引擎来替代解析器的作用,弥补java语言的不足
开发中常见的场景:
EL表达式的处理
正则表达式解析器
SQL语法的解析器
数学表达式解析器
6.访问者模式(visitor)
1)模式动机:对于存储在一个集合中的对象,它们可能具有不同的类型(即使有一个公共的接口),对于集合中的对象,可以接受一类称为访问者的对象来访问,不同的访问者其访问方式也有所不同.
2)定义:表示一个作用于某对象结构中的各个元素的操作,它使我们可以在不改变元素的类的前提下定义作用于这些元素的新操作
3)开发中的场景:
XML文档解析器设计
编译器的设计
复杂集合对象的处理
7.策略模式(strategy)
场景:
不使用策略模式
/* * 实现起来比较容易,符时合一般开发人员的思路 * 假如,类型特别多,算法比较复杂时,整个条件语句的代码就变得很长,难于维护 * 如果有新增类型,就需要频繁的修改此处的代码 * 不符合开闭原则 * */ public class TestStrategy { public double getPrice(String type, double price) { if (type.equals("普通客户小批量")) { System.out.println("不打折,原价"); return price; } else if (type.equals("普通客户大批量")) { System.out.println("打九折"); return 0.9 * price; } else if (type.equals("老客户小批量")) { System.out.println("打八五折"); return 0.85 * price; } else if (type.equals("老客户大批量")) { System.out.println("打八折"); return 0.8 * price; } return price; } }
1) 策略模式:
策略模式对应于解决某一个问题的一个算法族,允许用户从该算法族中任选一个算法解决某一问题,同时可以方便的更换算法或者增加新的算法.并且由客户端决定调用哪个算法.
2)本质: 分离算法,选择实现
public interface Strategy { public double getPrice(double standardPrice); }
public class NewCustomFewStrategy implements Strategy { @Override public double getPrice(double standardPrice) { System.out.println("不打折,原价"); return standardPrice; } }
public class NewCustomManyStrategy implements Strategy { @Override public double getPrice(double standardPrice) { System.out.println("不打折,原价"); return standardPrice*0.9; }
public class OldCustomFewStrategy implements Strategy { @Override public double getPrice(double standardPrice) { System.out.println("打八五折"); return standardPrice*0.85; } }
public class OldCustomManyStrategy implements Strategy { @Override public double getPrice(double standardPrice) { System.out.println("打八折"); return standardPrice * 0.8; } }
/* * 负责和具体的策略类交互 * 这样的话,具体的算法和直接的客户端调用分离了,使得算法可以独立于客户端独立的变化 * 如果使用spring的依赖注入功能,还可以通过配置文件,动态的注入不同策略对象,动态的切换不同的算法 * * */ public class Context { private Strategy strategy; //当前采用的算法 //可以通过构造器来注入 public Context(Strategy strategy) { this.strategy = strategy; } //可以通过set方法来注入 public void setStrategy(Strategy strategy) { this.strategy = strategy; } public void printPrice(double s) { System.out.println("您的报价:" + strategy.getPrice(s)); } }
public class Client { public static void main(String[] args) { Strategy s1 = new OldCustomManyStrategy(); Context ctx = new Context(s1); ctx.printPrice(998); } }
8.模板方法模式(template method)
1)模板方法模式介绍:
模板方法模式是编程中经常用得到的模板,它定义了一个操作的算法骨架,将某些步骤延迟到子类中实现.这样,新的子类可以在不改变一个算法结构的前提下重新定义该算法的某些特定步骤.
2)核心:
处理某个流程的代码已经都具备,但是其中某个节点的代码暂时不能确定.因此,我们采用模板方法模式,将这个节点的代码实现转移给子类完成.即:处理步骤父类中定义好,具体实现延迟到子类中定义.
public abstract class BankTemplateMethod { //具体方法 public void takeNumber() { System.out.println("取号排队"); } //办理具体的业务,钩子方法,方法回调 public abstract void transact(); public void evalute() { System.out.println("反馈评分"); } //模板方法 public final void process() { this.takeNumber(); this.transact(); this.evalute(); } }
public class Client { public static void main(String[] args) { BankTemplateMethod btm = new DrawMoney(); btm.process(); //采用匿名内部类 BankTemplateMethod btm2 = new BankTemplateMethod() { @Override public void transact() { System.out.println("我要存钱"); } }; btm2.process(); BankTemplateMethod btm3 =new BankTemplateMethod() { @Override public void transact() { System.out.println("我要理财,我这里有2000万韩币"); } }; btm3.process(); } } class DrawMoney extends BankTemplateMethod { @Override public void transact() { System.out.println("我要取款"); } }
3)方法回调(钩子方法)
好莱坞原则 "Don't call me,we'll call you back"
9.状态模式(state)
1)核心
用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题
2)结构
1>Context环境类
环境类中维护一个State对象,它是定义了当前的状态
2>State抽象状态类
3>ConcreateState具体状态类
每一个类封装了一个状态对应的行为
例子:
酒店系统中,房间的状态变化:
1)已预订
2)已入住
3)空闲
public interface State { void handle(); }
/* * 空闲状态 * */ public class FreeState implements State { @Override public void handle() { System.out.println("房间空闲,没人住"); } }
/* * 已预订状态 * */ public class BookedState implements State { @Override public void handle() { System.out.println("房间已预订,别人不能订"); } }
/* * 已入住状态 * */ public class CheckedState implements State { @Override public void handle() { System.out.println("房间已入住,请勿打扰"); } }
public class Context { private State state; public void setState(State state) { System.out.println("修改状态!"); this.state = state; state.handle(); } }
public class Client { public static void main(String[] args) { Context ctx = new Context(); ctx.setState(new FreeState()); ctx.setState(new BookedState()); } }
10.观察者模式(observer)
场景:
1)聊天室程序的创建,服务器创建好后, A,B,C三个客户端连上来公开聊天 . A向服务器发送数据,服务器端聊天数据改变. 我们希望将这些聊天数据分别发给其他在线的客户. 也就是说,每个客户需要更新服务器端得数据.
2)网站上,很多人订阅了"java主题"的新闻. 当有这个主题新闻时,就会将这些新闻发给所有订阅的人
3)大家一起玩CS游戏时,服务器需要将每个人的方位变化发给所有的客户
上面这些场景,我们都可以使用观察者模式来处理.我们可以把多个订阅者,客户称之为观察者;需要同步给多个订阅者的数据封装到对象中,称之为目标.
核心:
1)观察者模式主要用于1:N的通知.当一个对象(目标对象Subject或Observable)的态度变化时,它需要及时告知一系列对象(观察者对象,Observer),令他们做出响应.
2)通知观察者的方式:
推:每次都会把通知以广播方式发送给所有观察者,所有观察者只能被动接收
拉:观察者只要直到有情况即可. 至于什么时候获取内容,获取什么内容,都可以自主决定
public class Subject { protected List<Observer> list = new ArrayList<>(); public void registerObserver(Observer obs) { list.add(obs); } public void removeObserver(Observer obs) { list.remove(obs); } //通知所有的观察者更新状态 public void notifyAllObserver() { for (Observer obs : list) { obs.update(this); } } }
public class ConcreateSubject extends Subject { private int state; public int getState() { return state; } public void setState(int state) { this.state = state; //主题对象(目标对象)值发生了变化,请通知所有的观察者 this.notifyAllObserver(); } }
public interface Observer { void update(Subject subject); }
public class ObserverA implements Observer { private int myState; //myState需要跟目标对象的state值保持一致 @Override public void update(Subject subject) { myState = ((ConcreateSubject) subject).getState(); } public int getMyState() { return myState; } public void setMyState(int myState) { this.myState = myState; } }
public class Client { public static void main(String[] args) { //目标对象 ConcreateSubject subject = new ConcreateSubject(); //创建多个对象 Observer obs1 = new ObserverA(); Observer obs2 = new ObserverA(); Observer obs3 = new ObserverA(); //将三个观察者添加到subject对象的观察者队伍中 subject.registerObserver(obs1); subject.registerObserver(obs2); subject.registerObserver(obs3); //改变subject的状态 subject.setState(3000); //我们看看,观察者的状态是不是也发生了变化 System.out.println(((ObserverA) obs1).getMyState()); System.out.println(((ObserverA) obs2).getMyState()); System.out.println(((ObserverA) obs3).getMyState()); //改变subject的状态 subject.setState(30); //我们看看,观察者的状态是不是也发生了变化 System.out.println(((ObserverA) obs1).getMyState()); System.out.println(((ObserverA) obs2).getMyState()); System.out.println(((ObserverA) obs3).getMyState()); } }
JAVASE提供了java.util.Observable和java.util.Observer来实现观察者模式
package java.util; /** * A class can implement the <code>Observer</code> interface when it * wants to be informed of changes in observable objects. * * @author Chris Warth * @see java.util.Observable * @since JDK1.0 */ public interface Observer { /** * This method is called whenever the observed object is changed. An * application calls an <tt>Observable</tt> object's * <code>notifyObservers</code> method to have all the object's * observers notified of the change. * * @param o the observable object. * @param arg an argument passed to the <code>notifyObservers</code> * method. */ void update(Observable o, Object arg); }
package cn.hyy.test.pattern.observer2; import java.util.Observable; import java.util.Observer; public class ObserverA implements Observer { private int myState; @Override public void update(Observable o, Object arg) { myState = ((ConcreateSubject) o).getState(); } public int getMyState() { return myState; } public void setMyState(int myState) { this.myState = myState; } }
package java.util; public class Observable { private boolean changed = false; private Vector<Observer> obs; /** Construct an Observable with zero Observers. */ public Observable() { obs = new Vector<>(); } public synchronized void addObserver(Observer o) { if (o == null) throw new NullPointerException(); if (!obs.contains(o)) { obs.addElement(o); } } public synchronized void deleteObserver(Observer o) { obs.removeElement(o); } public void notifyObservers() { notifyObservers(null); } public void notifyObservers(Object arg) { Object[] arrLocal; synchronized (this) { if (!changed) return; arrLocal = obs.toArray(); clearChanged(); } for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); } public synchronized void deleteObservers() { obs.removeAllElements(); } protected synchronized void setChanged() { changed = true; } protected synchronized void clearChanged() { changed = false; } public synchronized boolean hasChanged() { return changed; } public synchronized int countObservers() { return obs.size(); } }
package cn.hyy.test.pattern.observer2; import java.util.Observable; //目标对象 public class ConcreateSubject extends Observable { private int state; public void setState(int state) { this.state = state; //目标对象的状态发生了改变 setChanged(); //表示目标对象已经做了更改 notifyObservers(state); //通知所有的观察者 } public int getState() { return state; } }
public class Client { public static void main(String[] args) { //创建目标对象 ConcreateSubject subject = new ConcreateSubject(); //创建观察者 ObserverA obs1 = new ObserverA(); ObserverA obs2 = new ObserverA(); ObserverA obs3 = new ObserverA(); //将上面三个观察者添加到目标对象subject的观察者容器中 subject.addObserver(obs1); subject.addObserver(obs2); subject.addObserver(obs3); //改变subject对象的状态 subject.setState(1000); //看看观察者状态发生了变化 System.out.println(obs1.getMyState()); System.out.println(obs2.getMyState()); System.out.println(obs3.getMyState()); } }
11.备忘录模式(memento)
1)场景:
录入大批人员资料. 正在录入当前人资料时,发现上一个人录错了,此时需要恢复上一个人的资料,再进行修改
Word文档编辑时,忽然电脑死机或断电,再打开时,可以看到word提示你恢复到以前的文档
管理系统中,公文撤回功能.公文发送出去后,想撤回来
2)核心
就是保存某个对象内部状态的拷贝,这样以后就可以将该对象恢复到原先的状态
3)结构
源发器类Originator : 负责创建一个备忘录memento,用以记录当前时刻它的内部状态,并可使用备忘录恢复到内部状态
备忘录类Memento :负责存储Originator对象的内部状态,并可防止Originator以外的其他对象访问备忘录Memento
负责人类CareTake : 负责保存好备忘录memento
/* * 源发器类 * */ public class Emp { private String ename; private int age; private double salary; //进行备忘操作,并返回备忘录对象 public EmpMemento memento() { return new EmpMemento(this); } //进行数据恢复,恢复成指定备忘录对象的值 public void recovery(EmpMemento emt) { this.ename = emt.getEname(); this.age = emt.getAge(); this.salary = emt.getSalary(); } public Emp(String ename, int age, double salary) { this.ename = ename; this.age = age; this.salary = salary; } public String getEname() { return ename; } public void setEname(String ename) { this.ename = ename; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } }
/* * 备忘录类 * */ public class EmpMemento { private String ename; private int age; private double salary; public EmpMemento(Emp emp) { this.ename = emp.getEname(); this.age = emp.getAge(); this.salary = emp.getSalary(); } public String getEname() { return ename; } public void setEname(String ename) { this.ename = ename; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } }
/* * 负责人类 * 负责管理备忘录对象 * */ public class CareTake { private EmpMemento memento; private List<EmpMemento> list = new ArrayList<>(); //存储很多个备忘点 public EmpMemento getMemento() { return memento; } public void setMemento(EmpMemento memento) { this.memento = memento; } }
public class Client { public static void main(String[] args) { CareTake taker = new CareTake(); Emp emp = new Emp("张三", 11, 4000); System.out.println("第一次打印对象:" + emp.getEname() + "---" + emp.getAge() + "---" + emp.getSalary()); taker.setMemento(emp.memento()); //做了一次备份 //进行修改 emp.setAge(38); emp.setEname("李四"); emp.setSalary(3000); System.out.println("第二次打印对象:" + emp.getEname() + "---" + emp.getAge() + "---" + emp.getSalary()); emp.recovery(taker.getMemento()); //恢复到备忘录对象保存的状态 System.out.println("第三次打印对象:" + emp.getEname() + "---" + emp.getAge() + "---" + emp.getSalary()); } }