java中的回调CallBack,Thread以及与android的关联

本文探讨了Java中的回调机制,通过一个类A实现接口InA并调用类B的test方法,使得B能够回调InA的方法。此外,还讨论了继承Thread类和实现Runnable接口创建线程的两种方式,指出了继承Thread类的局限性和实现Runnable接口的优势,如资源共享和多代理访问。最后提到了静态代理模式中的真实角色和代理角色的概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • class A实现接口InA 
  • class A中包含一个class B的引用b
  • class B有一个参数为InA的方法test(InA a) 
  • A的对象a调用B的方法传入自己,test(a) ——这一步相当于you call me
  • 然后b就可以在test方法中调用InA的方法 ——这一步相当于i call you back
这是回调的基本含义,大家可以看一下的代码,首先先定义一个接口CallBack,唯一的方法是solve():

再来定义实现了CallBack接口的class A,定义为Boss类,我们模拟的场景是,Boss手下有一个员工Employee,Boss在某个时刻想要让员工去帮他买东西,当员工买完后将把结果告知给Boss,也就是我们所说的回调,通过回调函数的solve()方法将结果给Boss。

Boss类在创建的时候需要传入一个员工对象Employee,在需要的时候,会调用Employee的方法,让他来处理逻辑,Employee如下:

当想要调用Employee的buySomething方法时,需要传入Callback接口的实现类,逻辑处理完毕后则调用Callback接口的方法回传结果,编写测试类,测试结果如下:



其实在Java中,线程Thread的原理也是使用了回调机制,我们可以来看下面这个最简单的线程实例:
new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				
			}
		}).start();
我们先进入Runnable代码中去看,可以发现他是个接口:
@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

我们将Runnable的实现类当做参数传入给Thread类,Thread的start()方法会回调Runnable实体类的run方法来进行逻辑处理,但是我们却发现了一个奇怪的事情,那就是Thread类已经实现了Runnable接口,却还要传入Runnable的实现类,这是为什么?原来这是java中的代理模式!!!

继承Thread类方式和通过Runnable接口的方式

   继承Thread类方式的缺点:如果我们的类已经继承了其他类,那就无法再继承Thread类了

   实现Runnable接口的方式优点:避免了单继承,方便共享资源,同一份资源可以有多个代理访问

代理就比如是找房子可以找中介,结婚可以找婚庆公司。

   在静态代理模式中有两个角色

  1:真实角色

  2:代理角色

   两个角色通过实现相同的接口来实现关联

下面可以看一个例子:

//真实角色  
class You implements IMarry{  
  
    @Override  
    public void marry() {  
    }  
}  

代理角色
class WeddingCompany implements  IMarry{  
  
    @Override  
    public void marry() {  
  
    }  
}  

让代理模式持有真实角色的引用

class WeddingCompany implements IMarry {  
    private IMarry you;  
    public WeddingCompany(){}  
    public WeddingCompany(IMarry you){  
        this.you=you;  
    }  
    @Override  
    public void marry() {  
        you.marry();  
    }  
}  
使用实例代码
public static void main(String[] args) {  
     //创建真实角色  
     IMarry you=new You();  
     //创建代理角色加入真实角色的引用  
     WeddingCompany weddingCompany=new WeddingCompany(you);  
 }  


此时让我们进入Thread的构造函数来去看看:
public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

继续往下找,就能看到初始化Runnable对象的代码(中间省略):
  private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
...
 this.target = target;
        setPriority(priority);
...
    }
好了,我们可以看到Thread的构造函数将Runnable的实例对象初始化给了成员变量,我们都知道要启动一个线程需要调用start()方法,我们看下start()方法做了什么:
public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
具体代码不做研究,start()方法调用操作系统本地的线程启动方法,使得run()方法内部的代码执行在异步线程中,我们都知道线程启动有两种方法,start()和run(),start方法正常启动异步线程,而run方法虽然同样执行了内部的所有操作,但确实跟主线程同步执行的,无法进行异步操作,我们看下Thread的run方法就知道为什么了:
/**
     * If this thread was constructed using a separate
     * <code>Runnable</code> run object, then that
     * <code>Runnable</code> object's <code>run</code> method is called;
     * otherwise, this method does nothing and returns.
     * <p>
     * Subclasses of <code>Thread</code> should override this method.
     *
     * @see     #start()
     * @see     #stop()
     * @see     #Thread(ThreadGroup, Runnable, String)
     */
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

这样我们就能看出所以然了,Thread的run方法居然只是简单地调用了传入的Runnable对象的run方法,并没有开启异步线程。
关于Thread的几种常用方式可以看下网上的博客,一般都是两种方法:直接继承Thread重写run方法或者实现Runnable接口写run方法,两种区别就是,实现Runnable接口适用于多线程对于同一个数据进行操作,例如我们常见的卖票系统等。
本篇主要是说回调的使用,有些跑偏了,那么android中是否存在回调呢?
必须得啊,我们天天都在使用的OnClickListener,OnLongClickListener等一系列方法都是回调方法,首先我们看下回调接口:
/**
     * Interface definition for a callback to be invoked when a view is clicked.
     */
    public interface OnClickListener {
        /**
         * Called when a view has been clicked.
         *
         * @param v The view that was clicked.
         */
        void onClick(View v);
    }

对,没错,就是View类中的OnClickListener接口,这个接口唯一的方法:onClick也就是回调函数用于我们处理点击事件的逻辑,当一个View发生点击事件时,就会回调onClick方法,我们就可以在Activity进行逻辑处理。下面我们模拟一下这个场景,首先定义抽象接口和view如下:
//点击事件的接口
		public interface OnClickListener {
			public void onclick();
		}
	//view类,保存实现事件接口的实体对象,在适当的时候调用接口方法,回传点击事件
	public class View{
		private  OnClickListener listener;
		public void performClick(){
			listener.onclick();
		}
		public void SetOnClickListener(OnClickListener l){
			listener=l;
		}
	}
实际情况中OnClickListener是定义在View类的内部,但这里我们为了更好地看清楚回调函数的使用,将其分开来了。View中定义了两个方法,SetOnClickListener用来将onClickListener的实例对象与其绑定,也就是获得实例对象,当用户进行点击操作时performClick方法就会被调用,然后回调接口的onclick方法。
下面是Activity的定义:
//Activity实现点击事件的接口,用来接收view的点击事件
	public class Activity implements OnClickListener{
		private View view;
		public Activity(){
			view=new View();
			view.SetOnClickListener(this);
		}
		public void onclick(){
			System.out.println("view点击一次");
		}
		public void clickOnce(){
			view.performClick();
		}
	}

Activity实现了接口OnClickListener,并定义了一个view,调用view的SetOnClickListener方法将自己传入,然后我们模拟点击事件,clickOnce调用时,就会调用view的performClick方法,然后view会回调onclick(),如下:
/**
	 * @param args
	 */
	public static void main(String[] args) {
		Activity activity=new MyActivity().new Activity();
		activity.clickOnce();
	}



当然我们只是非常简单地模拟点击事件,不过原理就是我们上面所讲述的:回调!!!可见回调在编程中是非常重要的一个知识点。在android开发中要结合java的特性来思考,以前只会简单地调用view.setOnClickListener(),不明白其中的原理,现在一定要多想多思考。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值