匿名内部类是什么?
引子
public abstract class Animal {
abstract void sayHello();
}
假如现在有一个Animal的抽象类,他有一个sayHello的抽象方法.在我们的程序中,有时候我们需要一只猫,并且让它们sayHello,这时我们会新建一个Cat类,继承Animal,然后实现sayHello,比如
public class Cat extends Animal {
@Override
void sayHello() {
System.out.println("hello, I am a cat");
}
}
但是这时候如果我们又需要一只狗,怎么办?当然是新建一个Dog类,继承Animal,然后实现sayHello,比如
public class Dog extends Animal{
@Override
void sayHello() {
System.out.println("hello, I am a dog");
}
}
接着,我们又需要鸡鸭鹅,我们又得新建鸡鸭鹅类,继承Animal,然后实现sayHello,但是我们新建的这些子类,很多都是只用到一次,然后就不会再用到了,这时我们会感觉新建类这种操作是比较麻烦的,而且时间久了,那些类会越来越多,那有没有更简便的方法呢?有的,那就是匿名内部类.我们来看看用匿名内部类来实现一只猫是怎么样的?
public class AnimalTest {
public static void main(String[] args) {
Animal cat = new Animal() {
@Override
void sayHello() {
System.out.println("hello, I am a cat");
}
};
}
}
对比新建类的方式,用匿名内部类的方式显然更加简单优雅一点,我们若要执行sayHello的方法,只需要cat.sayHello()
即可.
匿名内部类的实现方式
除了上面实现一个抽象类和抽象方法,匿名内部类还可以是实现一个接口类和接口方法,比如
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("I am " + Thread.currentThread().getName());
}
};
Thread t = new Thread(r);
t.start();
你打开Runnable一看,就知道原来Runnable是一个接口类
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
匿名
之所以叫做匿名,我感觉应该是它不像我们的猫狗鸡鸭鹅一样,有自己的类名,匿名内部类用的是抽象类的类名和接口名
内部类
之所以说是内部类,是因为他不用新建一个类,而是写在方法里面.
匿名内部类的原理
public class AnimalTest {
public static void main(String[] args) {
final Integer age = 3;
Animal cat = new Animal() {
@Override
void sayHello() {
System.out.println("I am a cat, age = " + age);
}
};
cat.sayHello();
}
}
我们把该段代码编译以后,实际上是生成两个文件
![](https://img.haomeiwen.com/i1605579/b1f86d71f73b8053.png)
查看AnimalTest$1.class
package anonymous;
final class AnimalTest$1 extends Animal {
AnimalTest$1(Integer var1) {
this.val$age = var1;
}
void sayHello() {
System.out.println("I am a cat, age = " + this.val$age);
}
}
再查看AnimalTest.class
package anonymous;
import anonymous.AnimalTest.1;
public class AnimalTest {
public AnimalTest() {
}
public static void main(String[] args) {
Integer age = Integer.valueOf(3);
Animal cat = new 1(age);
cat.sayHello();
}
}
变异后实际上偷偷生成一个animal的子类,在AnimalTest的主方法中,只是创建一个这个子类的对象.
为什么要用final
我们发现在匿名内部类中使用内名内部类外面的局部变量,必须要把局部变量修饰成final,这是为什么呢?
我们看上面的代码,会发现局部变量是通过构造函数传进匿名内部类中的,而且匿名内部类中使用的并不是这个局部变量本身,而是它的一个副本.这样子就会产生一个问题,如果我们在匿名内部类中修改局部变量的值,实际上并不是修改匿名内部类外面的局部变量的值,这时候就会产生数据不同步的情况.为了避免这种情况的发生,干脆把局部变量修饰成final,不可修改.
什么时候可以用到匿名内部类?
同样的某些接口或者抽象方法的具体实现在程序中只会用到一次,就可以用匿名内部类.比如我们在程序中某个地方需要新建一个线程去处理把某个特定的缓存清除掉,注意整个程序只有一个地方会用到这个清除某个特定缓存的操作,或者其它地方需要清除的是其他不同的缓存
Thread t = new Thread(){
@Override
public void run(){
CacheUtil.remove(CACHE_ONE);
}
};
t.start();
假如同样的某些接口或者抽象方法的具体实现在程序中只会用到多次,就是说上面的操作还有其他很多地方都会用到,这时候我们就可以新建一个Thread内,实现run方法.比如
public class CacheThread extends Thread {
@Override
public void run() {
CacheUtil.remove(CACHE_ONE);
}
}
网友评论