匿名內(nèi)部類是什么?
引子
public abstract class Animal {
abstract void sayHello();
}
假如現(xiàn)在有一個(gè)Animal的抽象類,他有一個(gè)sayHello的抽象方法.在我們的程序中,有時(shí)候我們需要一只貓,并且讓它們sayHello,這時(shí)我們會(huì)新建一個(gè)Cat類,繼承Animal,然后實(shí)現(xiàn)sayHello,比如
public class Cat extends Animal {
@Override
void sayHello() {
System.out.println("hello, I am a cat");
}
}
但是這時(shí)候如果我們又需要一只狗,怎么辦?當(dāng)然是新建一個(gè)Dog類,繼承Animal,然后實(shí)現(xiàn)sayHello,比如
public class Dog extends Animal{
@Override
void sayHello() {
System.out.println("hello, I am a dog");
}
}
接著,我們又需要雞鴨鵝,我們又得新建雞鴨鵝類,繼承Animal,然后實(shí)現(xiàn)sayHello,但是我們新建的這些子類,很多都是只用到一次,然后就不會(huì)再用到了,這時(shí)我們會(huì)感覺(jué)新建類這種操作是比較麻煩的,而且時(shí)間久了,那些類會(huì)越來(lái)越多,那有沒(méi)有更簡(jiǎn)便的方法呢?有的,那就是匿名內(nèi)部類.我們來(lái)看看用匿名內(nèi)部類來(lái)實(shí)現(xiàn)一只貓是怎么樣的?
public class AnimalTest {
public static void main(String[] args) {
Animal cat = new Animal() {
@Override
void sayHello() {
System.out.println("hello, I am a cat");
}
};
}
}
對(duì)比新建類的方式,用匿名內(nèi)部類的方式顯然更加簡(jiǎn)單優(yōu)雅一點(diǎn),我們?nèi)粢獔?zhí)行sayHello的方法,只需要cat.sayHello()
即可.
匿名內(nèi)部類的實(shí)現(xiàn)方式
除了上面實(shí)現(xiàn)一個(gè)抽象類和抽象方法,匿名內(nèi)部類還可以是實(shí)現(xiàn)一個(gè)接口類和接口方法,比如
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("I am " + Thread.currentThread().getName());
}
};
Thread t = new Thread(r);
t.start();
你打開(kāi)Runnable一看,就知道原來(lái)Runnable是一個(gè)接口類
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
匿名
之所以叫做匿名,我感覺(jué)應(yīng)該是它不像我們的貓狗雞鴨鵝一樣,有自己的類名,匿名內(nèi)部類用的是抽象類的類名和接口名
內(nèi)部類
之所以說(shuō)是內(nèi)部類,是因?yàn)樗挥眯陆ㄒ粋€(gè)類,而是寫(xiě)在方法里面.
匿名內(nèi)部類的原理
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();
}
}
我們把該段代碼編譯以后,實(shí)際上是生成兩個(gè)文件
查看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();
}
}
變異后實(shí)際上偷偷生成一個(gè)animal的子類,在AnimalTest的主方法中,只是創(chuàng)建一個(gè)這個(gè)子類的對(duì)象.
為什么要用final
我們發(fā)現(xiàn)在匿名內(nèi)部類中使用內(nèi)名內(nèi)部類外面的局部變量,必須要把局部變量修飾成final,這是為什么呢?
我們看上面的代碼,會(huì)發(fā)現(xiàn)局部變量是通過(guò)構(gòu)造函數(shù)傳進(jìn)匿名內(nèi)部類中的,而且匿名內(nèi)部類中使用的并不是這個(gè)局部變量本身,而是它的一個(gè)副本.這樣子就會(huì)產(chǎn)生一個(gè)問(wèn)題,如果我們?cè)谀涿麅?nèi)部類中修改局部變量的值,實(shí)際上并不是修改匿名內(nèi)部類外面的局部變量的值,這時(shí)候就會(huì)產(chǎn)生數(shù)據(jù)不同步的情況.為了避免這種情況的發(fā)生,干脆把局部變量修飾成final,不可修改.
什么時(shí)候可以用到匿名內(nèi)部類?
同樣的某些接口或者抽象方法的具體實(shí)現(xiàn)在程序中只會(huì)用到一次,就可以用匿名內(nèi)部類.比如我們?cè)诔绦蛑心硞€(gè)地方需要新建一個(gè)線程去處理把某個(gè)特定的緩存清除掉,注意整個(gè)程序只有一個(gè)地方會(huì)用到這個(gè)清除某個(gè)特定緩存的操作,或者其它地方需要清除的是其他不同的緩存
Thread t = new Thread(){
@Override
public void run(){
CacheUtil.remove(CACHE_ONE);
}
};
t.start();
假如同樣的某些接口或者抽象方法的具體實(shí)現(xiàn)在程序中只會(huì)用到多次,就是說(shuō)上面的操作還有其他很多地方都會(huì)用到,這時(shí)候我們就可以新建一個(gè)Thread內(nèi),實(shí)現(xiàn)run方法.比如
public class CacheThread extends Thread {
@Override
public void run() {
CacheUtil.remove(CACHE_ONE);
}
}