从字节码角度分析以下几点:
- 内部类可以直接访问外部类的private字段和方法;
- 非静态内部类持有外部类的引用;
- 外部类可以直接访问内部类的private字段和方法(不管是不是静态内部类);
搜索“解释”二字即可从文中得到答案
示例java
package com.fqxyi;
class Test {
public Test() {
//用于解释外部类可以直接访问内部类的private字段和方法(不管是不是静态内部类)
testB();
}
private int count;
private static int count_static;
public static void main(String[] args) {
A a = new A();
a.printA();
}
static class A {
private void printA() {
//用于解释静态内部类可以直接访问外部类的private字段和方法
System.out.println("print log --> a count_static = " + count_static);
}
}
private void testB() {
B b = new B();
b.printB();
}
//用于解释非静态内部类持有外部类的引用
class B {
private void printB() {
//用于解释内部类可以直接访问外部类的private字段和方法
System.out.println("print log --> b count = " + count);
}
}
}
反编译代码
用法: javac <options> <source files>
其中, 可能的选项包括:
-g 生成所有调试信息
用法: javap <options> <classes>
其中, 可能的选项包括:
-c 对代码进行反汇编
-l 输出行号和本地变量表
反编译Test.class
$ javap -c -l Test.class
Compiled from "Test.java"
class com.fqxyi.Test {
//默认的构造方法
public com.fqxyi.Test();
//冒号前的数字表示执行顺序,0表示是第一个执行;冒号后为执行指令,说明地址:https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5
Code:
//从本地变量表中加载引用,此处表示加载索引为0的this引用,并将该引用push到操作数栈(operand stack)中
0: aload_0
//调用实例方法,即初始化this引用的init方法
1: invokespecial #3 // Method java/lang/Object."<init>":()V
4: aload_0
//调用实例方法,即初始化this引用的testB方法
5: invokespecial #4 // Method testB:()V
//结束方法并返回void
8: return
LineNumberTable:
//反编译结果对应源码的行数
line 5: 0
line 6: 4
line 7: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this Lcom/fqxyi/Test;
public static void main(java.lang.String[]);
Code:
//创建类Test$A的引用
0: new #5 // class com/fqxyi/Test$A
//复制操作数栈顶的值,即类Test$A的引用,并push到操作数栈中
3: dup
4: invokespecial #6 // Method com/fqxyi/Test$A."<init>":()V
//pop操作数栈中索引为1的值,将其作为引用存储到本地变量表中,此处指引用a
7: astore_1
//从本地变量表中加载引用,此处表示加载索引为1的a引用,并将该引用push到操作数栈中
8: aload_1
//调用静态类Test$A的方法,此处指printA方法
9: invokestatic #7 // Method com/fqxyi/Test$A.access$000:(Lcom/fqxyi/Test$A;)V
12: return
LineNumberTable:
line 14: 0
line 15: 8
line 16: 12
LocalVariableTable:
Start Length Slot Name Signature
0 13 0 args [Ljava/lang/String;
8 5 1 a Lcom/fqxyi/Test$A;
//创建静态方法access$100来替代private字段,目的是为了不破坏private属性的作用和设计,同时也解释了第3点外部类为什么可以直接访问静态内部类的private字段和方法
static int access$100();
Code:
//从类中获取静态变量count_static的值,并将count_static的值push到操作数栈中
0: getstatic #2 // Field count_static:I
//结束方法并返回int
3: ireturn
LineNumberTable:
line 3: 0
//创建静态方法access$100来替代private字段,目的是为了不破坏private属性的作用和设计,同时也解释了第3点外部类为什么可以直接访问内部类的private字段和方法
static int access$300(com.fqxyi.Test);
Code:
0: aload_0
//pop操作数栈中的引用,获取变量count的值,并将count的值push到操作数栈中
1: getfield #1 // Field count:I
4: ireturn
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 x0 Lcom/fqxyi/Test;
}
反编译Test$A.class
$ javap -c -l Test\$A.class
Compiled from "Test.java"
class com.fqxyi.Test$A {
//编译器给静态内部类A添加了一个构造方法Test$A,解释了第1点静态内部类可以直接访问外部类的private字段和方法
com.fqxyi.Test$A();
Code:
0: aload_0
1: invokespecial #2 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 18: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/fqxyi/Test$A;
static void access$000(com.fqxyi.Test$A);
Code:
0: aload_0
1: invokespecial #1 // Method printA:()V
4: return
LineNumberTable:
line 18: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 x0 Lcom/fqxyi/Test$A;
}
反编译Test$B.class
$ javap -c -l Test\$B.class
Compiled from "Test.java"
class com.fqxyi.Test$B {
final com.fqxyi.Test this$0;
//编译器给非静态内部类B添加了一个构造方法Test$B,传入了外部类引用作为参数,而且定义了一个外部类引用字段this$0,解释了第1点内部类可以直接访问外部类的private字段和方法和第2点非静态内部类持有外部类的引用
com.fqxyi.Test$B(com.fqxyi.Test);
Code:
//从本地变量表中加载引用,此处表示加载索引为0的this引用,并将该引用push到操作数栈中
0: aload_0
//从本地变量表中加载引用,此处表示加载索引为1的this$0引用,并将该引用push到操作数栈中
1: aload_1
//pop操作数栈前面的两个值,此处指this和this$0,取出this$0,将其赋值给this
2: putfield #2 // Field this$0:Lcom/fqxyi/Test;
5: aload_0
6: invokespecial #3 // Method java/lang/Object."<init>":()V
9: return
LineNumberTable:
line 29: 0
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/fqxyi/Test$B;
0 10 1 this$0 Lcom/fqxyi/Test;
static void access$200(com.fqxyi.Test$B);
Code:
0: aload_0
1: invokespecial #1 // Method printB:()V
4: return
LineNumberTable:
line 29: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 x0 Lcom/fqxyi/Test$B;
}
官方文档