回调的实现--java

我们知道,在java中线程实现有两种方法,第一是继承Thread,第二是实现Runnable接口。无论哪个,都要重写run()方法,由于是重写,而且run()的定义:public abstract void run(),显而易见,这个函数式、是没有返回值的。那么怎样获取线程执行后的结果?

让我们来看一个例子

package com.lu.util;

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;

/**
 * 网络通用类
 * 
 * @author lu
 * 
 */
public class NetWorkUtil {

	/**
	 * 从给定的url页面获取页面内容
	 * 
	 * @param urlAddr
	 * @return
	 * @throws Exception
	 */
	public String getPageContent(String urlAddr) throws Exception {

		HttpURLConnection httpURLConn = null;
		InputStream input = null;
		Scanner scanner = null;

		try {
			URL url = new URL(urlAddr);
			httpURLConn = (HttpURLConnection) url.openConnection();

			httpURLConn.setDoInput(true);
			httpURLConn.setRequestMethod("GET");

			httpURLConn.connect();
			input = httpURLConn.getInputStream();

			scanner = new Scanner(input);
			StringBuffer strBuf = new StringBuffer();

			while (scanner.hasNextLine()) {
				strBuf.append(scanner.nextLine());
			}

			return strBuf.toString();
		} finally {
			if (null != scanner) {
				scanner.close();
			}
			if (null != input) {
				input.close();
			}
			if (null != httpURLConn) {
				httpURLConn.disconnect();
			}

		}

	}
}

//如果不了解URL、URLConnection,大家可以跳过不看,只需知道,如果调用该方法,就是获取给定url的页面内容即可。

package com.lu.callback;

import com.lu.util.NetWorkUtil;

/**
 * 该线程负责获取给定url页面的内容
 * 
 * @author lu
 * 
 */
public class GetPageContentThread implements Runnable {

	NetWorkUtil util = new NetWorkUtil();

	public String pageContent;

	public String urlAddr;

	public GetPageContentThread(String urlAddr) {
		this.urlAddr = urlAddr;
	}

	@Override
	public void run() {
		try {
			//获取页面内容
			pageContent = util.getPageContent(urlAddr);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

package com.lu.callback;

public class Client {

	public static void main(String[] args) {
		String url = "http://www.baidu.com";
		GetPageContentThreadtr = new GetPageContentThread(url);
		new Thread(tr).start();
		System.out.println(tr.pageContent.length());
	}
}

客户端开启线程并启动,一次来获取百度首页的内容。但是运行一下会发现程序抛出java.lang.NullPointerException异常。为什么呢?大家不要忘了这是在多线程环境下。如果执行System.out.println(tr.pageContent.length());的时候,获取页面的线程还没有获得时间片去执行,那么pageContent肯定为null,那么抛出异常就是毫无疑问了。

如果把客户端代码改成这样,那么问题就解决了。

package com.lu.callback;

public class Client {

	public static void main(String[] args) {
		String url = "http://www.baidu.com";
		ThreadResult tr = new ThreadResult(url);
		new Thread(tr).start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(tr.pageContent.length());
	}
}

主线程开启另一个线程后,休眠1秒钟,让给其他线程去执行。等主线程获得时间片去执行时,页面内容已经获得。但是存在一个更大的问题,休眠的时间怎么确定,如果休眠1秒,获取页面的时间为800毫秒,就白白浪费了CPU的资源。所以肯定有更好的办法,那就是回调。下面直接看代码。

package com.lu.callback;

/**
 * 该接口定义了process方法,改方法为回调调用的方法。
 * @author lu
 *
 */
public interface Callback {

	public void process(Object... objects);

}

package com.lu.callback;

/**
 * 改类继承了Callback接口并实现了process()这个回调方法,用于回调并处理
 * @author lu
 *
 */

public class GetPageContentCallBack implements Callback {

	public String urlAddr = null;
	public String pageContent = null;
	
	public GetPageContentCallBack(String urlAddr) {
		this.urlAddr = urlAddr;
	}

	@Override
	public void process(Object... objects) {
		if (objects != null && objects.length >= 1) {
			pageContent = (String) objects[0];
		}
		System.out.println(pageContent.length());
	}

	/**
	 * 该方法创建了获取页面内容的线程,并把自身当做参数传给改线程。
	 */
	public void getPageContent() {
		GetPageContentThread tr = new GetPageContentThread(urlAddr);
		tr.setCallback(this);
		new Thread(tr).start();
	}

}

package com.lu.callback;

import com.lu.util.NetWorkUtil;

/**
 * 该线程负责获取给定url页面的内容
 * 
 * @author lu
 * 
 */
public class GetPageContentThread implements Runnable {

	private NetWorkUtil util = new NetWorkUtil();

	public String pageContent;

	public String urlAddr;

	private Callback callback = null;
	
	public GetPageContentThread(String urlAddr) {
		this.urlAddr = urlAddr;
	}

	@Override
	public void run() {
		try {
			// 获取页面内容
			pageContent = util.getPageContent(urlAddr);
		} catch (Exception e) {
			e.printStackTrace();
		}
		//这条语句是关键所在,当获取页面之后,调用处理方法。
		callback.process(pageContent);
	}

	public void setCallback(Callback callback) {
		this.callback = callback;
	}
}

package com.lu.callback;

public class Client {

	public static void main(String[] args) {
		String url = "http://www.baidu.com";
		GetPageContentCallBack getPageContentCallBack = new GetPageContentCallBack(url);
		//调用获取页面方法
		getPageContentCallBack.getPageContent();
	}
}

首先客户端调用getPageContent()方法,该方法创建获取页面线程并把自身传递给该线程,即将自身的引用给了改线程。获取页面的线程执行run()方法获取页面,等获取成功后,调用callback的process()方法,并把结果传递给他。就这样得到了线程执行的结果并做出处理。

现在如果有多个对象关注该线程执行后的结果,那改怎么去通知呢?将其他想要关注该线程结果的对象都添加到该线程对象中就可以了,观察者... 主题...  blalalala....    好像是某个设计模式。


参考文献:java网络编程

小生不才,学术不精,欢迎大家批评指正



转载于:https://my.oschina.net/u/1469592/blog/208543

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,回调函数是一种常见的编程模式,它允许一个对象将自己的方法传递给另一个对象,在特定的事件发生时被调用。回调函数通常用于异步编程、事件处理和观察者模式等场景。 在Java实现回调函数有多种方式,下面介绍两种常见的实现方式: 1. 接口回调: - 定义一个接口,其中包含回调方法的签名。 - 在需要触发回调的类中,定义一个接口类型的成员变量,并提供一个注册回调的方法。 - 在合适的时机,调用注册的回调方法。 示例代码如下: ```java // 定义回调接口 public interface Callback { void onCallback(); } // 触发回调的类 public class CallbackTrigger { private Callback callback; // 注册回调 public void registerCallback(Callback callback) { this.callback = callback; } // 触发回调 public void doSomething() { // 执行某些操作 // ... // 调用回调方法 if (callback != null) { callback.onCallback(); } } } // 使用回调的类 public class CallbackReceiver implements Callback { @Override public void onCallback() { // 处理回调事件 // ... } } // 示例代码的使用 CallbackTrigger trigger = new CallbackTrigger(); CallbackReceiver receiver = new CallbackReceiver(); trigger.registerCallback(receiver); trigger.doSomething(); ``` 2. 函数式接口回调Java 8+): - 使用Java 8引入的函数式接口(Functional Interface)作为回调函数的类型。 - 在需要触发回调的类中,定义一个函数式接口类型的成员变量,并提供一个注册回调的方法。 - 在合适的时机,调用注册的回调方法。 示例代码如下: ```java // 定义函数式接口 @FunctionalInterface public interface Callback { void onCallback(); } // 触发回调的类 public class CallbackTrigger { private Callback callback; // 注册回调 public void registerCallback(Callback callback) { this.callback = callback; } // 触发回调 public void doSomething() { // 执行某些操作 // ... // 调用回调方法 if (callback != null) { callback.onCallback(); } } } // 使用回调的类 public class CallbackReceiver { public void handleCallback() { // 处理回调事件 // ... } } // 示例代码的使用 CallbackTrigger trigger = new CallbackTrigger(); CallbackReceiver receiver = new CallbackReceiver(); trigger.registerCallback(receiver::handleCallback); trigger.doSomething(); ``` 这两种方式都可以实现回调函数,具体选择哪种方式取决于具体的需求和代码结构。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值