Java中的匿名内部类及内部类的二三事

Posted AmosH

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中的匿名内部类及内部类的二三事相关的知识,希望对你有一定的参考价值。

匿名内部类适合创建那些只需要使用一次的类,它的语法有些奇怪,创建匿名内部类会立即创建一个该类的实例,这个类定义立即消失,且不能重复使用。

定义匿名类的格式如下:

new 实现接口() |父类构造器(实参列表){
//匿名内部类的类体部分
}

从定义来看,匿名内部类必须继承一个父类,或者实现一个接口,但是最多只能继承一个父类或者实现一个接口。

关于匿名内部类,还有如下两条规则:

  1. 匿名内部类不能是抽象类,因为系统在创建匿名内部类时,会立即创建匿名内部类的对象。因此不允许将匿名内部类定义成抽象类。
  2. 匿名内部类不能定义构造器。因为匿名内部类没有类名,所以无法定义构造器。但是匿名内部类可以定义初始化块,通过实例初始化块来完成构造器需要完成的事情。

下面给出了一个常见的匿名内部类的使用示例:

public class Main {
    public static void main(String[] args){
        Main main = new Main();
        main.test(new Man(){
            //这里传入一个Man接口的匿名实现类的实例
            public void talk(){
                System.out.print("I‘m a man!");
                //output I‘m a man!
            }
        });
    }
    
    public void test(Man m){
        //调用该方法需要传入一个Man类型参数
        m.talk();
    }
}

interface Man{
    void talk();
}

定义一个匿名内部类无需使用class关键字,而是在定义匿名内部类时直接生成该匿名内部类的对象。

由于匿名内部类不能是抽象类,所以匿名内部类必须实现它的抽象父类或者接口里包含的抽象方法。

虽然上面的例子完全可以采用使用实现类对象的方法来完成相同的功能,但是明显使用匿名内部类更加简洁。

当通过实现接口来创建匿名内部类时,匿名内部类不能显式创建构造器,因此匿名内部类只有一个隐式的无参数构造器,故new接口名后的括号中不能传入参数。

但是如果通过继承父类来创建匿名内部类时,匿名内部类将拥有和父类相似的构造器,注意此处相似的构造器指的是拥有相同形参列表。

下面是通过继承父类来创建匿名内部类的示例:

public class Main {
    public static void main(String[] args){
        Main main = new Main();
        main.test(new Man("Amos H"){
            //这里传入一个Man接口的匿名实现类的实例
            public void talk(){
                System.out.println(this.getName());
                //output Amos H
            }
            public int getHight(){
                return 175;
            }
        });
    }
    
    public void test(Man m){
        //调用该方法需要传入一个Man类型参数
        m.talk();
    }
}

abstract class Man{
    private String name;
    public Man(String name){
        this.name = name;
    }
    public void talk(){
        System.out.println(name);
    };
    public String getName(){
        return name;
    }
    public abstract int getHight();
}

可以看到,创造匿名内部类可以使用和父类相同的参数列表,调用父类的构造器。

当创建匿名内部类时,必须实现接口或者抽象父类中的所有抽象方法。如果有必要的话,可以重写父类中的普通方法。

内部类的二三事:

非静态内部类对象和外部类对象的关系如何?

非静态内部类对象必须寄生在外部类对象中,而外部类对象则不一定有非静态内部类对象寄生其中。因此外部类对象不能访问非静态内部类对象,而非静态内部类对象可以访问外部类成员,因为存在内部类必然存在外部类。

非静态内部类对象是否可以有静态初始化块?

非静态内部类对象不可以有静态初始化块,但是可以有普通初始化块。非静态内部类普通初始化块的作用与外部类初始化块的作用完全相同。

为何静态内部类的实例方法也不能访问外部类的实例属性?

因为静态内部类是外部类的类相关的,而不是外部类的对象相关的。静态内部类对象不是寄生在外部类的实例中,而是寄生在外部类的类本身中。当静态内部类对象存在时,并不一定存在一个被它寄生的外部类对象,静态内部类对象只持有外部类的类引用,并没有持有外部类对象的引用。如果允许静态内部类的实例方法访问外部类的实例成员,但找不到被寄生的外部类对象,这将引起错误。

如何实例化一个非静态内部类

非静态内部类的子类不一定是内部类,它也可以是一个外部类。但是非静态内部类的子类实例一样要保留一个引用,该引用用于指向其父类所在外部类的对象。也就是说,如果一个内部类子类的对象存在,则一定存在与之对应的外部类对象。因为要实例化一个非静态内部类的语法通常是这样的:

Out.In in = new Out().new In();

可以看到,非静态内部类的构造器必须使用外部类对象来调用。

以上是关于Java中的匿名内部类及内部类的二三事的主要内容,如果未能解决你的问题,请参考以下文章

匿名内部类及与接口和抽象类的关系

匿名内部类及与接口和抽象类的关系

logify与theos的二三事

[Java面经] 关于面试的二三事.

多态与继承的二三事

[Java面经分享] 关于面试的二三事.