从字节码角度分析以下几点:

  1. 内部类可以直接访问外部类的private字段和方法;
  2. 非静态内部类持有外部类的引用;
  3. 外部类可以直接访问内部类的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;
}

官方文档

添加新评论