Java基础12-深入理解Java中回调机制(一):https://developer.aliyun.com/article/1535683
实例二:由浅入深
前几天公司面试有问道java回调的问题,因为这方面也没有太多研究,所以回答的含糊不清,这回特意来补习一下。看了看网上的回调解释和例子,都那么的绕口,得看半天才能绕回来,其实吧,回调是个很简单的机制。在这里我用简单的语言先来解释一下:假设有两个类,分别是A和B,在A中有一个方法a(),B中有一个方法b();在A里面调用B中的方法b(),而方法b()中调用了方法a(),这样子就同时实现了b()和a()两个方法的功能。
疑惑:为啥这么麻烦,我直接在类A中的B.b()方法下调用a()方法就行了呗。解答:回调更像是一个约定,就是如果我调用了b()方法,那么就必须要回调,而不需要显示调用一、Java的回调-浅我们用例子来解释:小明和小李相约一起去吃早饭,但是小李起的有点晚要先洗漱,等小李洗漱完成后,通知小明再一起去吃饭。小明就是类A,小李就是类B。一起去吃饭这个事件就是方法a(),小李去洗漱就是方法b()。
public class XiaoMing { //小明和小李一起吃饭 public void eatFood() { XiaoLi xl = new XiaoLi(); //A调用B的方法 xl.washFace(); } public void eat() { System.out.print("小明和小李一起去吃大龙虾"); } } 那么怎么让小李洗漱完后在通知小明一起去吃饭呢 public class XiaoMing { //小明和小李一起吃饭 public void eatFood() { XiaoLi xl = new XiaoLi(); //A调用B的方法 xl.washFace(); eat(); } public void eat() { System.out.print("小明和小李一起去吃大龙虾"); } }复制代码
不过上面已经说过了这个不是回调函数,所以不能这样子,正确的方式如下
public class XiaoLi{//小李 public void washFace() { System.out.print("小李要洗漱"); XiaoMing xm = new XiaoMing(); //B调用A的方法 xm.eat();//洗漱完后,一起去吃饭 } }复制代码
这样子就可以实现washFace()同时也能实现eat()。小李洗漱完后,再通知小明一起去吃饭,这就是回调。
二、Java的回调-中可是细心的伙伴可能会发现,小李的代码完全写死了,这样子的场合可能适用和小明一起去吃饭,可是假如小李洗漱完不吃饭了,想和小王上网去,这样子就不适用了。其实上面是伪代码,仅仅是帮助大家理解的,真正情况下是需要利用接口来设置回调的。现在我们继续用小明和小李去吃饭的例子来讲讲接口是如何使用的。
小明和小李相约一起去吃早饭,但是小李起的有点晚要先洗漱,等小李洗漱完成后,通知小明再一起去吃饭。小明就是类A,小李就是类B。不同的是我们新建一个吃饭的接口EatRice,接口中有个抽象方法eat()。在小明中调用这个接口,并实现eat();小李声明这个接口对象,并且调用这个接口的抽象方法。这里可能有点绕口,不过没关系,看看例子就很清楚了。
EatRice接口:
public interface EatRice { public void eat(String food); } 小明: public class XiaoMing implements EatRice{//小明 //小明和小李一起吃饭 public void eatFood() { XiaoLi xl = new XiaoLi(); //A调用B的方法 xl.washFace("大龙虾", this);//this指的是小明这个类实现的EatRice接口 } @Override public void eat(String food) { // TODO Auto-generated method stub System.out.println("小明和小李一起去吃" + food); } } 小李: public class XiaoLi{//小李 public void washFace(String food,EatRice er) { System.out.println("小李要洗漱"); //B调用了A的方法 er.eat(food); } } 测试Demo: public class demo { public static void main(String args[]) { XiaoMing xm = new XiaoMing(); xm.eatFood(); } }复制代码
测试结果:
这样子就通过接口的形式实现了软编码。通过接口的形式我可以实现小李洗漱完后,和小王一起去上网。代码如下
public class XiaoWang implements EatRice{//小王 //小王和小李一起去上网 public void eatFood() { XiaoLi xl = new XiaoLi(); //A调用B的方法 xl.washFace("轻舞飞扬上网", this); } @Override public void eat(String bar) { // TODO Auto-generated method stub System.out.println("小王和小李一起去" + bar); } }复制代码
实例三:Tom做题
数学老师让Tom做一道题,并且Tom做题期间数学老师不用盯着Tom,而是在玩手机,等Tom把题目做完后再把答案告诉老师。
1 数学老师需要Tom的一个引用,然后才能将题目发给Tom。
2 数学老师需要提供一个方法以便Tom做完题目以后能够将答案告诉他。
3 Tom需要数学老师的一个引用,以便Tom把答案给这位老师,而不是隔壁的体育老师。
回调接口,可以理解为老师接口
//回调指的是A调用B来做一件事,B做完以后将结果告诉给A,这期间A可以做别的事情。 //这个接口中有一个方法,意为B做完题目后告诉A时使用的方法。 //所以我们必须提供这个接口以便让B来回调。 //回调接口, public interface CallBack { void tellAnswer(int res); }复制代码
数学老师类
//老师类实例化回调接口,即学生写完题目之后通过老师的提供的方法进行回调。 //那么学生如何调用到老师的方法呢,只要在学生类的方法中传入老师的引用即可。 //而老师需要指定学生答题,所以也要传入学生的实例。 public class Teacher implements CallBack{ private Student student; Teacher(Student student) { this.student = student; } void askProblem (Student student, Teacher teacher) { //main方法是主线程运行,为了实现异步回调,这里开启一个线程来操作 new Thread(new Runnable() { @Override public void run() { student.resolveProblem(teacher); } }).start(); //老师让学生做题以后,等待学生回答的这段时间,可以做别的事,比如玩手机.\ //而不需要同步等待,这就是回调的好处。 //当然你可以说开启一个线程让学生做题就行了,但是这样无法让学生通知老师。 //需要另外的机制去实现通知过程。 // 当然,多线程中的future和callable也可以实现数据获取的功能。 for (int i = 1;i < 4;i ++) { System.out.println("等学生回答问题的时候老师玩了 " + i + "秒的手机"); } } @Override public void tellAnswer(int res) { System.out.println("the answer is " + res); } }复制代码
学生接口
//学生的接口,解决问题的方法中要传入老师的引用,否则无法完成对具体实例的回调。 //写为接口的好处就是,很多个学生都可以实现这个接口,并且老师在提问题时可以通过 //传入List<Student>来聚合学生,十分方便。 public interface Student { void resolveProblem (Teacher teacher); }复制代码
学生Tom
public class Tom implements Student{ @Override public void resolveProblem(Teacher teacher) { try { //学生思考了3秒后得到了答案,通过老师提供的回调方法告诉老师。 Thread.sleep(3000); System.out.println("work out"); teacher.tellAnswer(111); } catch (InterruptedException e) { e.printStackTrace(); } }复制代码
测试类
public class Test { public static void main(String[] args) { //测试 Student tom = new Tom(); Teacher lee = new Teacher(tom); lee.askProblem(tom, lee); //结果 // 等学生回答问题的时候老师玩了 1秒的手机 // 等学生回答问题的时候老师玩了 2秒的手机 // 等学生回答问题的时候老师玩了 3秒的手机 // work out // the answer is 111 } }