15.2.3 使用Runnable接口
我们知道,Java中的类只能够是单继承,也就是说,如果一个类为了使用线程而继承了Thread类,它就不能再继承别的类了。这很可能给编程带来不便。本节中介绍的就是一种脱离继承来使用线程的方法。这个方法的核心就是Runnable接口。
Runnable接口的全限定名是java.lang.Runnable。它其中只有一个抽象方法void run()。为了了解如何在线程中使用Runnable接口,我们还需要看一下Thread类中的一个叫做target的属性和Thread类中的run()方法。Thread类中有一个类型为Runnable的属性,叫做target。而Thread类的run()方法用到了这个属性,run()方法的代码如下:
- public void run() { // Thread类的run()方法
- if (target != null) { // 检查target属性
是否为空,target属性是Runnable- // 类型的引用
- target.run(); // 如果不为空则执行run()方法
- } // 否则什么都不做
- } // run()方法结束
如何让target的值不为null呢?Thread类的另一个构造方法就是用来给target属性赋值的,这个构造方法是Thread(Runnable)。当调用这个构造方法时,传递过来的参数就会赋值给target属性。也就是说,如果直接使用Thread类也是可以的,步骤如下:
(1)实现Runnable接口,例如叫做MyRunnable,并在MyRunnable类的run()方法里编写想要让线程做的事情。
(2)创建一个MyRunnable的实例。
(3)通过构造方法Thread(Runnable)来创建Thread类的实例。
这时再调用start()方法启动这个线程,执行的就是MyRunnable中run()方法的代码了。下面我们来使用以下这种方法,首先是MyRunnable类。
- package com.javaeasy.usethread;
// 程序在的包- public class MyRunnable implements Runnable {
// 实现Runnable接口- public void run() {
// 实现run()方法- System.out.println("这是在另一个线程
中执行的代码。");
// 向控制台输出一行字- }
// run()方法结束- }
MyRunnable实现了Runnable接口,其run()方法就是线程会去执行的方法。然后是例程。
- package com.javaeasy.usethread;
- public class UseMyRunnable {
- public static void main(String[] args) {
// 例程的main()方法- // 创建一个MyRunnable类的实例,MyRunnable,
MyRunnable实现了Runnable- // 接口
- MyRunnable runnable = new MyRunnable();
- Thread thread = new Thread(runnable);
// 调用Thread相应的构造
// 方法,传入参数- thread.start();
// 启动线程- }
- }
在例程中,按照步骤分别创建MyRunnable类的实例,调用Thread相应的构造方法,最后启动线程。因为Runnable是个接口,为了简单一些,还可以使用前面学到过的匿名类来实现相同的功能。使用匿名类的例程如下:
- package com.javaeasy.usethread;
- public class UseRunnable {
- public static void main(String[] args) { // 测试类的main()方法
- // 创建一个线程,参数为一个实现了Runnable接口的匿名类的实例
- Thread thread = new Thread(new Runnable() {
- public void run() { // 实现抽象方法run()
- System.out.println("这是在另一个线程中执行的代码。");
- }
- });
- thread.start(); // 启动线程
- }
- }
例程UseRunnable其实和例程UseMyRunnable是一样的。当然,从本质上讲,无论是使用继承还是使用Runnable接口,其目的都是一样的。让线程执行我们写的一段代码。使用继承并覆盖run()方法也好;使用Runnable接口也好,都是为了指定线程执行的方法。本节不再给出程序执行的实例图,图15-6可以涵盖本节的程序流程。
下面我们回过头去看看图15-6:当一个新的线程启动以后,程序就相当于是有两个同时在执行的线程。没错,事情就是这样的。就好像演奏会上的两个演奏家一样,两个演奏家是一起演奏各自的乐谱。两个线程也是各自执行自己的代码,彼此之间互不影响。但是事情到这里就开始变得有意思了:一个程序内有两个线程。好,下面让我们进入15.2.4节,看看两个线程的故事。
使用Runnable接口来让线程执行自己编写的run()方法。
====================================================================================================================
匿名类
匿名类是不能有名称的类,所以没办法引用它们。必须在创建时,作为new语句的一部分来声明它们。这就要采用另一种形式的new语句,如下所示:
new <类或接口> <类的主体>
这种形式的new语句声明一个新的匿名类,它对一个给定的类进行扩展,或者实现一个给定的接口。它还创建那个类的一个新实例,并把它作为语句的结果而返回。要扩展的类和要实现的接口是new语句的操作数,后跟匿名类的主体。
如果匿名类对另一个类进行扩展,它的主体可以访问类的成员、覆盖它的方法等等,这和其他任何标准的类都是一样的。如果匿名类实现了一个接口,它的主体必须实现接口的方法。
注意匿名类的声明是在编译时进行的,实例化在运行时进行。这意味着for循环中的一个new语句会创建相同匿名类的几个实例,而不是创建几个不同匿名类的一个实例。
从技术上说,匿名类可被视为非静态的内部类,所以它们具有和方法内部声明的非静态内部类一样的权限和限制。
如果要执行的任务需要一个对象,但却不值得创建全新的对象(原因可能是所需的类过于简单,或者是由于它只在一个方法内部使用),匿名类就显得非常有用。匿名类尤其适合在Swing应用程序中快速创建事件处理程序。
转的小例子:
abstract class A
{
abstract public void fun1();
}
class Outer
{
public static void main(String [] args)
{
class Inner extends A
{
public void fun1()
{
System.out.println("implement for fun1");
}
}
new Outer().callInner(new Inner());
}
public void callInner(A a)
{
a.fun1();
}
}
用匿名类后的例子
abstract class A
{
abstract public void fun1();
}
class Outer
{
public static void main(String [] args)
{
new Outer().callInner(new A()
{
public void fun1()
{
System.out.println("implement for fun1");
}
});
}
public void callInner(A a)
{
a.fun1();
}
}
内部类和匿名类最大的好处,个人觉得是可以实现多继承,当需要继承多个类的时候,可以继承一个,其他的用内部类和匿名类的形式来实现。