Java 中的匿名内部类 Anonymous Inner Class

yufei       6 年, 1 月 前       1238

匿名内部类也是 嵌套类 中的一种。又或者说,匿名内部类是一种特殊的内部类。

匿名内部类是一个没有名称的内部类,只创建了一个单独的对象。

匿名内部类是非常有用的,也经常看到。经常用于那些只使用一次,又需要创建具有某些 额外 功能的对象实例( 例如重载类或接口的方法 )的场景。有了匿名内部类,我们就不需要创建只使用一次的类的子类。

匿名内部类主要有两种方式创建:

  1. 从一个类( 可以是虚类,也可以是普通类)中创建
  2. 从一个接口中创建。

匿名内部类的语法

Java 中创建匿名内部类的语法格式如下

Test t = new Test() 
{
   // 成员声明
   public void test_method() 
   {
      ........
      ........
    }   
};

其中,Test 可以是一个接口,一个虚类或一个普通的类。

从匿名内部类的语法中可以看出,创建一个匿名类,就是创建一个类/接口的子类并创建该子类的实例。

  • Test t = new Test() 其实是创建一个实例的语法
  • {} 中的语句,则是创建一个子类的语法。

为了让你更好的理解匿名内部类的创建语法,我们写一个范例来演示下。

我们先来看看常规情况下如何实现一个接口

JavaTester.java

// 创建一个 Age 接口 
interface Age 
{ 
    int x = 21; 
    void getAge(); 
} 


// Myclass 实现了 Age 接口,这是普通的创建子类方式
class MyClass implements Age 
{ 
    @Override
    public void getAge()  
    {  
        System.out.println("Age is "+x); 
    } 
} 

public class JavaTester 
{ 
    public static void main(String[] args)  
    { 
        // Myclass 实现了 Age 接口 
        MyClass obj=new MyClass(); 

        // 调用 getAge() 方法 
        obj.getAge();      
    } 
}

编译运行上面的代码,输出结果如下

[yufei@www.twle.cn java]$ javac JavaTester.java && java JavaTester
Age is 21

在上面这段代码中,我们使用常规的 class MyClass implements Age 方法实现了接口 Age

如果这个 MyClass 类只使用一次,只是为了创建一个 obj 对象。我们可以不用这么复杂。我们只要使用匿名内部类来实现即可。就像下面代码中所展示的那样

Age oj1 = new Age() {
    @Override
    public void getAge() {
        System.out.print("Age is "+x);
    }
};

看起来是不是简单明了的多了。

比较下两段代码,我们并没有对类的实现做任何更改。而匿名内部类的方式只更改了几个小的地方。

  1. 创建一个要继承的父类或要实现的接口的变量。
  2. 新建一个要继承的弗雷或要实现的接口的对象。
  3. 把我们要在子类中完成的代码全部放到一个大括号里面。

我们把前面一个范例给改一下,看看使用匿名内部类的完整版。

// 创建一个 Age 接口 
interface Age 
{ 
    int x = 21; 
    void getAge(); 
} 

public class JavaTester 
{ 
    public static void main(String[] args)  
    {  
        Age obj=new Age() {

            @Override
            public void getAge()  
            {  
                System.out.println("Age is "+x); 
            } 
        };

        // 调用 getAge() 方法 
        obj.getAge();      
    } 
} 

上面这段代码是可以运行的,且运行的结果和前面的范例一模一样。

匿名内部类的类型

有了匿名内部类,我们就不需要关心到底要给子类取一个什么名字。但烦恼也不是没有,就是匿名内部类的类型。

匿名内部类的类型,取决于如何声明和具体的行为,总的来说,有三种情况:

  1. 匿名内部类扩展自一个普通类

    匿名内部类是可以扩展自一个普通类的。比如我们经常见到的匿名内部类扩展自 Thread 类。

    假设我们需要一个只使用一次的线程,但我们不想创建一个始终扩展 Thread 类的类。借助这种类型的匿名内部类,我们可以定义一个即时线程,如下所示

    public class JavaTester  
    { 
        public static void main(String[] args) 
        {
            Thread t = new Thread() 
            { 
                public void run() 
                { 
                    System.out.println("Child Thread"); 
                } 
            }; 
            t.start(); 
            System.out.println("Main Thread"); 
        } 
    }
    

    编译运行上面这段代码,输出结果如下

    [yufei@www.twle.cn java]$ javac JavaTester.java && java JavaTester
    Main Thread
    Child Thread
    
  2. 匿名内部类实现了一个接口

    我们还可以创建实现了某个接口的匿名内部类。比如经常见到的实现了 Runnable 接口的匿名内部类。

    public class JavaTester  
    { 
        public static void main(String[] args) 
        {
            Runnable r = new Runnable() 
            { 
                public void run() 
                { 
                    System.out.println("Child Thread"); 
                } 
            }; 
            new Thread(r).start();
            System.out.println("Main Thread");
        } 
    }
    

    编译运行上面这段代码,输出结果如下

    [yufei@www.twle.cn java]$ javac JavaTester.java && java JavaTester
    Child Thread
    Main Thread
    
  3. 作为方法或构造函数参数的匿名内部类

    还有一种匿名内部类,比较少见的。当一个方法或构造函数需要某个类的对象时。我们可以创建一个匿名内部类作为它们的参数。

    例如,因为 Thread 类的构造函数接受一个 Runnable 类型的参数,因此,我们还可以对上面的实例进行简化。

    public class JavaTester  
    { 
        public static void main(String[] args) 
        {
            new Thread(new Runnable() 
                { 
                    public void run() 
                    { 
                        System.out.println("Child Thread"); 
                    } 
                }
            ).start();
            System.out.println("Main Thread");
        } 
    }
    

    编译运行上面这段代码,输出结果如下

    [yufei@www.twle.cn java]$ javac JavaTester.java && java JavaTester
    Child Thread
    Main Thread
    

普通类和匿名内部类的区别?

  • 普通类可以实现任意数量的接口,但匿名内部类一次只能实现一个接口。

  • 常规类可以扩展自一个类并同时实现任意数量的接口。但匿名内部类只可以扩展自一个类,或只实现一个接口,但两者不可兼得。

  • 对于常规/普通类,我们可以编写任意数量的构造函数,但我们不能为匿名内部类编写任何构造函数,因为匿名类没有任何名称,而定义构造函数类名和构造函数名必须相同。

匿名内部类如何访问外部变量?

跟本地类一样,匿名类可以捕获变量: 它们对相同作用域范围的局部变量具有相同的访问权限。

  • 匿名类可以访问外部类的成员。
  • 匿名类无法访问封闭作用域范围中未声明为 final 或者 effectively final 的局部变量
  • 与嵌套类一样,匿名类中的类型(例如变量)的声明会影响封闭范围中具有相同名称的任何其他声明
目前尚无回复
简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.