从字节码的角度看Java内部类与外部类的互相访问(一)

2014-11-24 00:38:42 · 作者: · 浏览: 1
 Java中non-static内部类为何可以访问外部类的变量?Java中外部类又为何可以访问内部类的private变量?这两个问题困扰过我一段时间,查了一些网上的答案,大多从“闭包”概念入手,理解起来很是费劲,能否从另外一个角度来解释这个问题呢?有句话叫做“真正了不起的程序员应该对每一个字节都了如指掌”,而弄明白Java程序的“每个字节”还是相对容易的,下面就通过一段Java代码的bytecode来分析:
复制代码
1 public class Test
2 {
3 public static void main(String[] args)
4 {
5 new Test().initData();
6 }
7
8 private void initData()
9 {
10 new A().privateVar = 0;
11 new B().privateVar = 0;
12 }
13
14 // non-static inner class A
15 private class A
16 {
17 private int privateVar;
18 int defaultVar;
19 protected int protectedVar;
20 public int publicVar;
21 }
22
23 // static inner class B
24 private static class B
25 {
26 private int privateVar;
27 int defaultVar;
28 protected int protectedVar;
29 public int publicVar;
30 }
31 }
复制代码
  由于Java内部类会编译成单独的.class文件,我们用javap命令反编译每个.class文件来一探究竟。
  non-static内部类A的bytecode如下:
复制代码
1 E:\workspace\testClass\bin>javap -c Test$A
2 Compiled from "Test.java"
3 class Test$A extends java.lang.Object{
4 int defaultVar;
5
6 protected int protectedVar;
7
8 public int publicVar;
9
10 final Test this$0;
11
12 Test$A(Test, Test$A);
13 Code:
14 0: aload_0
15 1: aload_1
16 2: invokespecial #25; //Method "":(LTest;)V
17 5: return
18
19 static void access$1(Test$A, int);
20 Code:
21 0: aload_0
22 1: iload_1
23 2: putfield #29; //Field privateVar:I
24 5: return
25
26 }
复制代码
  static内部类B的bytecode如下:
复制代码
1 E:\workspace\testClass\bin>javap -c Test$B
2 Compiled from "Test.java"
3 class Test$B extends java.lang.Object{
4 int defaultVar;
5
6 protected int protectedVar;
7
8 public int publicVar;
9
10 Test$B(Test$B);
11 Code:
12 0: aload_0
13 1: invokespecial #20; //Method "":()V
14 4: return
15
16 static void access$1(Test$B, int);
17 Code:
18 0: aload_0
19 1: iload_1
20 2: putfield #23; //Field privateVar:I
21 5: return
22
23 }
复制代码
  从bytecode可以很清晰地看出,non-static内部类A的默认构造函数实质上传入了两个参数,第一个是外部类Test对象的引用,并且在内部类A中用final对象来持有这一引用(另一个参数是返回值,A的引用,与本文阐述主题无关),而static内部类B的默认构造函数则没有传入外部类Test对象的引用。这样就回答了第一个问题,non-static内部类是通过隐含传入的外部类对象的引用来完成对外部类的访问的。
  再看A和B中均有针对内部类private变量提供了一个access静态方法(注:若没有针对private变量的访问,编译器会把access方法优化掉,所以必须存在外部类访问内部类private变量的代码才有此方法),那么这一方法是否就是外部类可以访问内部类private变量的原因呢?反编译外部类Test的.class文件可以得到:
复制代码
1 E:\workspace\testClass\bin>javap -verbose Test
2 Compiled from "Test.java"
3 public class Test extends java.lang.Object
4 SourceFile: "Test.java"
5 InnerClass:
6 #42= #22 of #1; //A=class Test$A of class Test
7 #43= #31 of #1; //B=class Test$B of class Test
8 minor version: 0
9 major version: 50
10 Constant pool:
11 const #1 = class #2; // Test
12 const #2 = Asciz Test;
13 const #3 = class #4; // java/lang/Object
14 const #4 = Asciz java/lang/Object;
15 const #5 = Asciz ;
16 const #6 = Asciz ()V;
17 const #7 = Asciz Code;
18 const #8 = Method #3.#9; // java/lang/Object."":()V
19 const #9 = NameAndType #5: