匿名内部类
定义: 匿名内部类是指在使用某个类的时候,临时创建一个继承于该类的子类对象,并对该对象进行重写或扩展操作,但是不拥有具体的类名,而是直接使用匿名的方式进行定义和实例化。
语法:new 构造器(实参列表) {// 匿名内部类的类体部分}
匿名内部类通常用于一次性的需求,不需要单独创建一个新的类或者接口来实现某个功能。它主要用于简化代码的编写,减少类的定义和对象实例化的步骤。
隐式继承/实现
匿名内部类可以重写父类的方法、实现接口的方法。由此,通常会使用一种可拓展性更强的写法:
new 父类构造器(实参列表)/接口() { // 匿名内部类的类体部分 }
此处,类体部分实质上是外部父类/接口的子类/实现类,在内部直接实现某个功能。
例如,在创建对象时,直接重写父类的抽象方法:
public class Main {
public static void main(String[] args) {
// 创建一个匿名内部类对象,并继承自父类Animal
Animal animal = new Animal() {
@Override
public void makeSound() {
System.out.println("Meow"); // 实现了父类的抽象方法
}
};
// 调用匿名内部类对象的方法
animal.makeSound(); // 输出:Meow
}
}
// 父类Animal
abstract class Animal {
abstract void makeSound();
}
还有一种常见的方式,创建线程时,直接重写run
方法:
Thread thread = new Thread(new Runnable() {
public void run() {
// 线程执行的任务
}
});
此处,new Runnable(){}
作为实现Runnable
接口的匿名实现类充当new Thread()
的实参列表,在创建线程时定义好了run
方法。
tips:
- 通常情况下,匿名内部类是一次性使用的,因为它没有类名,无法复用;
- 匿名内部类访问外部变量时,需要外部变量声明为
final
(JDK8之后,要求使用过程不修改即可)
为何只能访问局部final变量?
首先,回顾以下final
的作用:
final的作用
- 修饰类:表示类不可被继承
- 修饰方法:表示方法不可被子类重写覆盖,但可以重载
- 修饰变量:表示变量一旦被赋值就不可更改
- 修饰成员变量:在声明的时候就需要赋值或者在代码块中赋值
- 修饰局部变量:系统不会为局部变量初始化,可以在用的时候赋值(使用之前一定要赋值且只能赋值一次)
- 修饰基本类型变量:其数值一旦初始化后不能更改
- 修饰引用类型变量:其初始化后不可在让其指向另一对象(地址不变),但引用值可变
重载和重写的区别:
- 重载:发生在同一个类中,方法名相同,参数列表不同(个数,顺序也算),和方法返回值没有关系
- 重写:子类重写父类方法,方法名相同,参数列表相同返回值范围小于等于父类,抛出异常范围小于等于父类,修饰符范围大于等于父类(如果父类方法为private,那么子类不可重写)
为什么局部内部类和匿名内部类只能访问局部final变量
局部内部类和匿名内部类编译之后会生产单独的class
文件,实际上是同一个级别。里面的class
文件不会随着外部方法执行结束而回收。
这里产生一个问题:外部类的方法结束时,局部变量就会被销毁,但是内部类对象可能还存在。为了解决这个问题,JVM将局部变量复制了一份作为内部类的成员变量。因此,就将局部变量设为final
,必须保证内部类和外部的变量是一样的。