java的内部类和外部类关系,# 深入理解Java中为什么内部类可以访问外部类的成员#(2)原创分享...

# 深入理解Java中为什么内部类可以访问外部类的成员#(2)原创分享

------影子侠开发者社区Rong

下面我们把注意力转移到构造函数上。 下面这段输出是关于构造函数的信息。

```

Outer$Inner(Outer);

flags:

Code:

stack=2, locals=2, args_size=2

0: aload_0

1: aload_1

2: putfield      #10                 // Field this$0:LOuter;

5: aload_0

6: invokespecial #12                 // Method java/lang/Object."":()V

9: return

LineNumberTable:

line 5: 0

LocalVariableTable:

Start  Length  Slot  Name   Signature

0      10     0  this   LOuter$Inner;

```

我们知道, 如果在一个类中, 不声明构造方法的话, 编译器会默认添加一个无参数的构造方法。 但是这句话在这里就行不通了, 因为我们明明看到, 这个构造函数有一个构造方法, 并且类型为Outer。 所以说,编译器会为内部类的构造方法添加一个参数, 参数的类型就是外部类的类型。

下面我们看看在构造参数中如何使用这个默认添加的参数。 我们来分析一下构造方法的字节码。 下面是每行字节码的意义:

aload_0 :

将局部变量表中的第一个引用变量加载到操作数栈。 这里有几点需要说明。 局部变量表中的变量在方法执行前就已经初始化完成;局部变量表中的变量包括方法的参数;成员方法的局部变量表中的第一个变量永远是this;操作数栈就是执行当前代码的栈。所以这句话的意思是: 将this引用从局部变量表加载到操作数栈。

aload_1:

将局部变量表中的第二个引用变量加载到操作数栈。 这里加载的变量就是构造方法中的Outer类型的参数。

putfield      #10                 // Field this$0:LOuter;

使用操作数栈顶端的引用变量为指定的成员变量赋值。 这里的意思是将外面传入的Outer类型的参数赋给成员变量this$0 。

这一句putfield字节码就揭示了, 指向外部类对象的这个引用变量是如何赋值的。

下面几句字节码和本文讨论的话题无关, 只做简单的介绍。 下面几句字节码的含义是: 使用this引用调用父类(Object)的构造方法然后返回。

用我们比较熟悉的形式翻译过来, 这个内部类和它的构造函数有点像这样: (注意, 这里不符合Java的语法, 只是为了说明问题)

```

class Outer$Inner{

final Outer this$0;

public Outer$Inner(Outer outer){

this.this$0 = outer;

super();

}

}

```

说到这里, 可以推想到, 在调用内部类的构造器初始化内部类对象的时候, 编译器默认也传入外部类的引用。 调用形式有点像这样: (注意, 这里不符合java的语法, 只是为了说明问题)

```

public class Outer{

int outerField = 0;

//在外部类中创建内部类对象

void outerMethod(){

new Inner(this);  //传入外部类的引用

}

class Inner{

void InnerMethod(){

int i = outerField;

}

}

}

```

这也印证了上面所说的内部类和外部类逻辑关系的第一条: 内部类对象的创建依赖于外部类对象。

关于在内部类中如何使用指向外部类的引用访问外部类成员, 就不用多做解释了, 其实和普通的通过引用访问成员的方式是相同的。 在内部类的InnerMethod方法中, 访问了外部类的成员变量outerField, 下面的字节码揭示了访问是如何进行的:

```

void InnerMethod();

flags:

Code:

stack=1, locals=2, args_size=1

0: aload_0

1: getfield      #10                 // Field this$0:LOuter;

4: getfield      #20                 // Field Outer.outerField:I

7: istore_1

8: return

```

getfield      #10                    // Field this$0:LOuter;

将成员变量this$0加载到操作数栈上来

getfield      #20                 // Field Outer.outerField:I

使用上面加载的this$0引用, 将外部类的成员变量outerField加载到操作数栈

istore_1

将操作数栈顶端的int类型的值保存到局部变量表中的第二个变量上(注意,第一个局部变量被this占用,第二个局部变量是i)。操作数栈顶端的int型变量就是上一步加载的outerField变量。所以,这句字节码的含义就是:使用outerField为i赋值。

上面三步就是内部类中是如何通过指向外部类对象的引用,来访问外部类成员的。

##总结

文章写到这里,相信读者对整个原理就会有一个清晰的认识了。下面做一下总结:

本文通过反编译内部类的字节码,说明了内部类是如何访问外部类对象的成员的,除此之外,我们也对编译器的行为有了一些了解,编译器在编译时会自动加上一些逻辑,这正是我们感觉困惑的原因。

关于内部类如何访问外部类的成员, 分析之后其实也很简单, 主要是通过以下几步做到的:

1 编译器自动为内部类添加一个成员变量, 这个成员变量的类型和外部类的类型相同, 这个成员变量就是指向外部类对象的引用;

2 编译器自动为内部类的构造方法添加一个参数, 参数的类型是外部类的类型, 在构造方法内部使用这个参数为1中添加的成员变量赋值;

3 在调用内部类的构造函数初始化内部类对象时, 会默认传入外部类的引用。

此文章系本人原创,如需转载,请注明出处影子侠开发者社区www.yingzixia.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值