魏 钢
(锦州师范高等专科学校,辽宁 锦州 121000)
深入研究Java内部类的实现原理
魏 钢
(锦州师范高等专科学校,辽宁 锦州 121000)
内部类按照使用方式的不同可以分为常规内部类、静态内部类、局部内部类和匿名内部类。通过对这四种内部类的定义方式、使用约束和引用方式的分析,应用反编译技术对内部类和外部类进行反编译,分析编译器生成的额外代码,阐述内部类的实现原理。
内部类;静态内部类;局部内部类;匿名内部类;外部类
通常情况下Java中类的定义包括属性和方法,特殊用途下,类中还可以定义类,被包含的类叫做内部类[1]。相对于内部类而言,包含内部类的类叫做外部类。根据内部类在外部类中使用方式不同,又可以分为常规内部类、静态内部类、局部内部类和匿名内部类四种。以下论述中外部类命名为Outer,内部类命名为Inner。
1.1 常规内部类的定义
在外部类中直接定义无static修饰的类,称为常规内部类。例如:
public class Outer {;private int i=10;;private class Inner{;public void print(){;System.out.println(i); }};public void fun(){;Inner in=new Inner();;in.print();}}。
1.2 常规内部类的使用约束
第一,定义时可以加访问控制符,public、protected、默认和private均可。第二,不能定义静态属性和静态方法。第三,可以访问外部类的属性和方法。第四,外部类可以访问内部类的所有属性和方法。
1.3 常规内部类的引用方式
私有的常规内部类只能在外部类内引用,格式为Inner in=new Inner()。对于非私有的常规内部类可以在外部类之外进行引用,格式为:Outer.Inner in=new Outer().new Inner()。
1.4 常规内部类的实现原理
Java编译器对内部类和外部类是分别进行编译的,编译后产生Outer.class和Outer$1Inner.class两个字节码文件,外部类能实例化私有常规内部类的根本原因在于编译器在编译私有常规内部类时生成了部分附加代码,通过反编译技术对Outer$1Inner.class进行反编译可以查到附加代码如下:
final Outer this$0;;private Outer$1Inner(){this$0=Outer.this;super();};Outer$1Inner(Outer$1Inner outer$1inner){this();}。
反编译Outer.class时发现实例化私有常规内部类时的代码被改为1Inner in=new 1Inner(null),实际上调用的是包可见构造器,包可见构造器内部又调用了私有构造器,因此外部类可以实例化私有常规内部类。因为私有常规内部类通过构造器初始化了外部类引用变量,所以私有常规内部类能够访问外部类的属性和方法,对于能够访问外部类私有属性的原因在于编译器对外部类私有属性生成了专门的静态方法static int access$0(Outer),代码隐藏不可见,通过在内部类中使用静态的access$0方法引用外部类中的私有属性[2]。
2.1 静态内部类的定义
在外部类中直接定义有static修饰的类,称为静态内部类,例如:
public class Outer {;private static int i=10;;private static class Inner{;public void print(){;System.out.println(i); }}};
2.2 静态内部类的使用约束
只能访问外部类的静态属性和静态方法,其它约束与常规内部类使用约束基本相同。
2.3 静态内部类的引用方式
静态内部类的引用方式同常规内部类的引用方式相同。
2.4 静态内部类的实现原理
反编译Outer$1Inner.class时发现编译器对静态内部类并不产生外部类引用变量,因此静态内部类无法得到外部类的引用,所以静态内部类只能访问外部类的静态属性和静态方法。
3.1 局部内部类的定义
在方法中定义的类,称为局部内部类,例如:
3.2 局部内部类的使用约束
第一,局部内部类定义时不能加访问控制符,只能在方法内部使用。第二,局部内部类能访问外部类的属性和方法。第三,局部内部类只能访问包含方法中fnal修饰的变量。
3.3 局部内部类的引用方式
局部内部类只能在方法内部进行引用,使用方式同基本类。
3.4 局部内部类的实现原理
反编译Outer$1Inner.class时发现编译器对局部内部类生成“fnal Outer this$0”和“private fnal int val$j”。局部内部类通过外部类引用变量可实现访问外部类的属性和方法,val$j是fnal修饰的形参j的一个备份,目的在于方法运行结束时,避免Java垃圾回收器对方法内的资源进行垃圾回收时导致局部内部类在访问内部资源时查找失败[3]。
4.1 匿名内部类的定义
只创建一个对象且不用命名的局部内部类,称为匿名内部类,例如:
4.2 匿名内部类的使用约束
匿名内部类由于没有类名,因此不能定义构造器,其它使用约束同局部内部类。
4.3 匿名内部类的引用方式
匿名内部类通常以方法的参数形式出现,分为继承式匿名内部类和接口式匿名内部类两种。引用方式为new 父类名(){//重写父类方法}或new 接口名(){//实现接口方法}。
4.4 匿名内部类的实现原理
编译产生匿名内部类名称为Outer$1,反编译后发现生成fnal Outer this$0,证明匿名内部类可以访问外部类的属性和方法。
通过反编译技术的使用,深入研究了Java内部类的实现原理,加深了对内部类定义方式、使用约束和引用方式的认识,提高了对Java技术的应用水平。
[1]张孝祥.Java就业培训教程[M].北京:清华大学出版社,2003.
[2]郭广军,陈代武,王剑波.Java内部类的研究及应用[J].湖南人文科技学院学报,2007(06):32-38.
[3]刘文杰,郑玉,刘志昊.Java 7实用教程[M].北京:清华大学出版社,2013.
魏钢(1978-),男,辽宁盘锦人,硕士,讲师,主要从事Java Web开发研究。