领略内部类的“内部”

Java代码

1. //代码1:内部类实例
2. class Outer{
3. ........
4. class Inner{
5. ......
6. }
7. }

//代码1:内部类实例
class Outer{
........
class Inner{
......
}
}



所有的内部类代码都有这样两个特点:

(1) 在外部类的作用范围内可以任意创建内部类对象,即使内部类是私有的。(内部类对外部类可见)
Java代码

1. //代码2:内部类对外部类可见
2. class Outer{
3. //创建私有内部类对象
4. public Inner in=new Inner();
5. //私有内部类
6. private class Inner{
7. ...
8. }
9. }

//代码2:内部类对外部类可见
class Outer{
//创建私有内部类对象
public Inner in=new Inner();
//私有内部类
private class Inner{
...
}
}

(2) 在内部类中可以访问其外部类的所有成员。(外部类对内部类可见)
Java代码

1. //代码3:外部类对内部类可见
2. class Outer{
3. //外部类私有数据域
4. private int data=0;
5. //内部类
6. class Inner{
7. void print(){
8. //内部类访问外部私有数据域
9. System.out.println(data);
10. }
11. }
12. }

//代码3:外部类对内部类可见
class Outer{
//外部类私有数据域
private int data=0;
//内部类
class Inner{
void print(){
//内部类访问外部私有数据域
System.out.println(data);
}
}
}

这里我们好好探究一下Java内部类的"内部"

其实,内部类是Java编译器一手操办的。虚拟机并不知道内部类与常规类有什么不同。 编译器是如何瞒住虚拟机的呢?

对内部类进行编译后发现有两个class文件:Outer.class 和Outer$Inner.class 。这说明内部类Inner仍然被编译成一个独立的类(Outer$Inner.class),而不是Outer类的某一个域。 虚拟机运行的时候,也是把Inner作为一种常规类来处理的。
但问题来了,即然是两个常规类,为什么他们之间可以互相具有私有权限那(最开始提到的两个内部类特点)?这就要问问编译器到底把这两个类编译成什么东西了。

我们利用reflect反射机制来探查了一下内部类编译后的情况(关于探查类内部机制的代码提供在下面的附件里Reflect.java)。

1、编译代码2生成 Outer$Inner.class 文件后使用 ReflectUtil.reflect("Outer$Inner") 对内部类Inner进行反射。运行结果 发现了三个隐含的成分:
Html代码

1. class Outer$Inner
2. {
3. Outer$Inner(Outer,Outer$Inner); //包可见构造器
4. private Outer$Inner(Outer); //私有构造器将设置this$0域
5. final Outer this$0; //外部类实例域this$0
6. }

class Outer$Inner
{
Outer$Inner(Outer,Outer$Inner); //包可见构造器
private Outer$Inner(Outer); //私有构造器将设置this$0域
final Outer this$0; //外部类实例域this$0
}



好了,现在我们可以解释上面的第一个内部类特点了: 为什么外部类可以创建内部类的对象?

首先编译器将外、内部类编译后放在同一个包中。

然后在内部类中附加一个包可见构造器。

这样, 虚拟机运行Outer类中Inner in=new Inner(); 实际上调用的是包可见构造: new Outer$Inner(this,null)。

因此即使是private内部类,也会通过隐含的包可见构造器成功的获得私有内部类的构造权限。

2、编译代码3生成 Outer.class文件,然后使用 ReflectUtil.reflect("Outer") 对外部类Outer进行反射 。 运行结果 发现一个隐含成分如下:
Html代码

1. class Outer
2. {
3. static int access$0(Outer); //静态方法,返回值是外部类私有域 data 的值。
4. }

class Outer
{
static int access$0(Outer); //静态方法,返回值是外部类私有域 data 的值。
}

现在可以解释第二个特点了:为什么内部类可以引用外部类的私有域?

原因的关键就在编译器在外围类中添加了静态方法access$0。 它将返回值作为参数传递给他的对象域data。这样内部类Inner中的打印语句:

System.out.println(data);
实际上运行的时候调用的是:

S ystem.out.println(access$0(Outer));

总结一下编译器对内部类做的手脚吧:

(1) 在内部类中偷偷摸摸的创建了包可见构造器,从而使外部类获得了创建权限。

(2) 在外部类中偷偷摸摸的创建了访问私有变量的静态方法,从而 使 内部类获得了访问权限。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值