匿名内部类,顾名思义,就是没有名字的类,匿名内部类格式如下:
new name(parameter)
{
......
}
name:父类或是接口的名字。
parameter:若name为父类,则parameter为父类构造函数的参数。
匿名内部类具有一系列的限制,原因待会解释:
1、不能具有static成员域和成员函数和类。
2、不能具有static final 修饰的引用类型。
3、不能有自定义的构造函数。
4、不能具有静态代码块。
5、匿名内部类不能有类修饰符
使用匿名内部类的方式:
class A
{
int clsvalue;
A(int v)
{
clsvalue=v;
}
}
interface B
{
int invalue=0;
}
public class Try {
public static void main(String[] args) {
/*方法一:利用父类或接口的引用指向匿名内部类,此时引用只能访问父类或是接口中的成员域和成员函数,
例如下面的a和b只能访问clsvalue和invalue,无法访问x和y。但可以使用被覆盖的方法。
*/
A a= new A(1)
{
static final int x=0;
};
B b=new B()
{
static final int y=0;
};
/*方法二:直接通过下列代码访问,此时可以访问父类或接口、匿名内部类中定义的成员域或是成员方法。但只能使用一次(由于没有变量名)*/
new A(2){static final int y=0;};
}
}
匿名内部类仅仅是局部有效的,方法一仅在作用域内有效,方法二仅在当行代码中有效。
class A
{
int clsvalue;
A(int v)
{
clsvalue=v;
}
}
public class Try {
public static void main(String[] args) {
A a= new A(1){ };
}
}
首先,匿名内部类会有属于自己的class文件。反编译匿名内部类的class文件得:
Last modified 2018-2-6; size 274 bytes
MD5 checksum e1349e62178fb6ad8402c5ec6ffe0bbf
Compiled from "Try.java"
final class Try$1 extends A
minor version: 0
major version: 52
flags: ACC_FINAL, ACC_SUPER
Constant pool:
#1 = Methodref #3.#13 // A."<init>":(I)V
#2 = Class #14 // Try$1
#3 = Class #16 // A
#4 = Utf8 <init>
#5 = Utf8 (I)V
#6 = Utf8 Code
#7 = Utf8 LineNumberTable
#8 = Utf8 SourceFile
#9 = Utf8 Try.java
#10 = Utf8 EnclosingMethod
#11 = Class #17 // Try
#12 = NameAndType #18:#19 // main:([Ljava/lang/String;)V
#13 = NameAndType #4:#5 // "<init>":(I)V
#14 = Utf8 Try$1
#15 = Utf8 InnerClasses
#16 = Utf8 A
#17 = Utf8 Try
#18 = Utf8 main
#19 = Utf8 ([Ljava/lang/String;)V
{
Try$1(int);
descriptor: (I)V
flags:
Code:
stack=2, locals=2, args_size=2
0: aload_0 //将this指针从局部变量表压入操作数栈。
1: iload_1 //将局部变量表中的int型变量的值压入操作数栈
2: invokespecial #1 // Method A."<init>":(I)V,调用父类构造函数,首先传入栈顶的int型变量,接着将值写入this指针所指内存的对应位置。
5: return
LineNumberTable:
line 11: 0
}
SourceFile: "Try.java"
EnclosingMethod: #11.#12 // Try.main
InnerClasses:
static #2; //class Try$1
首先注意到匿名内部类的类修饰符为final,且编译器自动加上了extends A,若
name为接口,则为implements+接口名。
编译器会根据parameter创建一个匿名内部类的构造函数。
问题出现了,从类文件来看,匿名内部类和普通的类没什么不同,我们知道可以用父类引用指向子类对象,并且可以通过父类引用调用子类方法和成员域,但匿名内部类的父类引用不行(即用方法一使用匿名内部类),为什么要这么设计?下面谈谈我自己的理解:
从使用角度来看:由匿名内部类的生命周期,我们可以知道,使用匿名内部类是为了使用一种'短期"效果,若父类是抽象类(接口),我们可以随时定义一个抽象方法使用,若类不是抽象类,我们可以使用在子类中被覆盖的方法,该功能的方法我们只想使用一次(或在当前作用域使用),此时我们便不用再去另外定义一个类,代码也更加简洁,可读性也更强(不然可能还需要拼命滑鼠标去找类,一串代码找的很痛苦的!)。当然,若父类是抽象类或接口,我们要定义所有的抽象方法。
以上,匿名内部类的父类引用只能访问父类的(被覆盖的)成员方法和成员域,设计者这么做应该是让匿名内部类的使用目的更加明确。
接下来解释各种限制的原因,以下均为个人理解:
1、为什么不能具有静态成员域和成员函数、静态代码块、static final修饰的引用类型、静态内部类?
答:和实名内部类类似,“静态”意味着与实例对象无关就可以访问,从匿名内部类的使用代码(方法一、方法二)我们可以看到,其一定会实例化一个对象,此时使用“静态”便毫无意义。static final修饰引用类型的目的:1、不需要实例对象就可访问,2、不希望指针指向改变,匿名内部类的使用方式使1变得毫无意义,所以规定不能用static final修饰引用类型,由于匿名内部类没有名字,想要使用静态内部类必须通过一个匿名内部类的实例对象,与“静态”定义矛盾。
2、为什么不能有自定义构造函数?
答:从class文件中,我们可以看到匿名内部类其实含有编译器自动加上的构造函数,且JVM只调用该构造函数,自定义构造函数无效,另外,匿名内部类名字都没有怎么定义构造函数(滑稽.jpg)?
3、为什么匿名内部类不能有类修饰符?
emmmm,加类修饰符可能与有效域冲突,例如加public,但匿名内部类只在当前作用域或是当前行有效,且这些类修饰符本来就是规定访问范围的,但匿名内部类访问范围已经规定。
4、为什么可以有static final修饰的具有字面量的变量?
其实对于匿名内部类来说,static final与final修饰的具有字面量的变量效果一样,只是初始化时机不一样,保存位置不一样,对于性能没有太多影响,所以保留。
如有错误,欢迎各位指出,另附实名内部类详解:点击打开链接