美文网首页
关于内部类

关于内部类

作者: Wi1ls努力努力再努力 | 来源:发表于2019-04-30 15:44 被阅读0次

一直说每个方法都会持有外部的引用,每个内部类都会持有外部类的引用。
对于方法具有外部类的引用,在看 jvm 的时候发现编译器在每个非静态方法都会插入 solot 为 0 的一个入参,改入参即为调用方法的对象。
对于内部类持有外部类的引用,也是今天通过反编译时候发现了其实现。

定义的 OuterClass和 InnerClass

public class OuterClass {
  private String f1 = "en";
  private int f2 = 5;
  private Object f3 = new Object();
  public float f4;

  public class InnerClass {
    public void printOuterClassPrivateFields() {
      String f11 = f1;
      int f22 = f2;
      Object f33 = f3;
      float f44 = f4;
    }
  }

  public synchronized static void method_outer() {

  }
}

当我们进行 javap -v 时:

   ... 

  static int access$100(OuterClass);
    descriptor: (LOuterClass;)I
    flags: ACC_STATIC, ACC_SYNTHETIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #2                  // Field f2:I
         4: ireturn
      LineNumberTable:
        line 7: 0

  static java.lang.Object access$200(OuterClass);
    descriptor: (LOuterClass;)Ljava/lang/Object;
    flags: ACC_STATIC, ACC_SYNTHETIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #1                  // Field f3:Ljava/lang/Object;
         4: areturn
      LineNumberTable:
        line 7: 0
}
SourceFile: "OuterClass.java"
InnerClasses:
     public #9= #8 of #7; //InnerClass=class OuterClass$InnerClass of class OuterClass

可以看到莫名增加了几个在源文件没有定义的方法:
public synchronized static String access000(OuterClass); public synchronized static int access100(OuterClass);
public synchronized static Object access$200(OuterClass);

而从方法的字节码可以看出来,这三个方法正是返回 f1,f2,f3 的三个 static 方法,这三个方式是编译器自动生成的。而声明为 public的f4 却没有生成对应的方法。在看 InnerClass的printOuterClassPrivateFields()方法:

final OuterClass this$0;
    descriptor: LOuterClass;
    flags: ACC_FINAL, ACC_SYNTHETIC

  public OuterClass$InnerClass(OuterClass);
    descriptor: (LOuterClass;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: putfield      #1                  // Field this$0:LOuterClass;
         5: aload_0
         6: invokespecial #2                  // Method java/lang/Object."<init>":()V
         9: return
      LineNumberTable:
        line 13: 0

public void printOuterClassPrivateFields();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=5, args_size=1
         0: aload_0
         1: getfield      #1                  // Field this$0:LOuterClass;
         4: invokestatic  #3                  // Method OuterClass.access$000:(LOuterClass;)Ljava/lang/String;
         7: astore_1
         8: aload_0
         9: getfield      #1                  // Field this$0:LOuterClass;
        12: invokestatic  #4                  // Method OuterClass.access$100:(LOuterClass;)I
        15: istore_2
        16: aload_0
        17: getfield      #1                  // Field this$0:LOuterClass;
        20: invokestatic  #5                  // Method OuterClass.access$200:(LOuterClass;)Ljava/lang/Object;
        23: astore_3
        24: aload_0
        25: getfield      #1                  // Field this$0:LOuterClass;
        28: getfield      #6                  // Field OuterClass.f4:F
        31: fstore        4
        33: return
      LineNumberTable:
        line 15: 0
        line 16: 8
        line 17: 16
        line 18: 24
        line 19: 33

可以看到,在内部类的构造方法中,编译器帮我们帮外部类 OuterClass的引用自动放到了 InnerClass的构造方法的第一个参数(所有构造方法都会自动添加,就像所有的非静态方法的第一个 slot 都是 this 引用),通过 IDE 直接查看 OuterClass$InnerClass.class 可以看到

public class OuterClass$InnerClass {
  public OuterClass$InnerClass(OuterClass var1) {
    this.this$0 = var1;
  }

  public void printOuterClassPrivateFields() {
    String var1 = OuterClass.access$000(this.this$0);
    int var2 = OuterClass.access$100(this.this$0);
    Object var3 = OuterClass.access$200(this.this$0);
    float var4 = this.this$0.f4;
  }
}

构造函数中将外部类定义为内部类的一个 this0变量,而引用外部类正是通过编译器在 OuterClass 中自动添加的 static 方法进行获得 private 的实例。而对于 public 的 f4,在 InnerClass直接通过 this.this0.f4 获得。


再做比如删除内部类的实验等几个实验,可以获得以下结论。

  • 编译器在内部类的构造函数中,会自动添加外部类的引用作为构造函数的第一个参数,在内部中变量名为 this$0。
  • 当一个类不具有内部类,或者其内部类没有引用外部类的 private 字段时,或者没有引用外部类的 private 方法时,则不会生成对应的public synchronized xxx access$XXX(OuterClass)方法。
  • 如果一个类的内部类引用了外部类的 private 字段,则会在外部类生成对应的 static accessXXX(OuterClass)方法返回对应的 private 字段。如果引用了外部类的 private 方法,则会在外部类生成对应的 static accessXXX(OuterClass)方法,内部进行 invoke 调用对应的 private 方法。
  • 同理,外部类能引用内部类的 private 字段或者方法,也是通过编译器在内部类生成对应的 accessXXX(OuterClassInnerClass)的方法去实现的。并且当且仅当是外部类才可以引用。
  • 以上的一切都是编译器自动优化生成,并且只有引用了才会生成,不引用不会生成。可以视为惰性生成。

相关文章

  • 2019-02-01——内部类

    1.静态内部类和成员内部类的用法?2.内部类的修饰符有什么用? 一.关于内部类的分类内部类广义上可分为四种:成员内...

  • 内部类

    成员内部类 局部内部类(定义在方法内和定义在作用域内的类) 匿名内部类

  • Java 内部类

    内部类包括成员内部类、方法内部类、*静态内部类、匿名内部类*。 内部类的作用 由于内部类的实现和外部类没有关系,内...

  • Java学习——内部类

    内部类 一,成员内部类(包括静态内部类和非静态内部类) 非静态内部类可以直接访问外部类的成员,反之则不行 非静态内...

  • Java 内部类、静态内部类、方法内部类(未完待续)

    内部类 什么是内部类?内部类其实就是在一个类中创建的类。内部类有四种: 成员内部类 静态内部类 局部内部类 匿名内...

  • java 内部类

    一般有四种内部类的使用方式: 嵌套的内部类 方法内的内部类 静态内部类 匿名内部类 什么时候用内部类: 有时候明显...

  • 内部类

    内部类 1.可以访问访问其外部类所有属性和方法,无需创建外部类对象 2.必须创建内部类对象,否则无法从外部类访问内...

  • Java 中的方法内部类

    Java 中的方法内部类 方法内部类就是内部类定义在外部类的方法中,方法内部类只在该方法的内部可见,即只在该方法内...

  • java——内部类

    内部类 定义: 内部类是指在一个外部类的内部再定义一个类。内部类作为外部类的一个成员,并且依附于外部类而存在的。内...

  • java之面向对象5

    方法内部类: 简而言之就是定义在外部类的方法内的类。 方法内部类只在该方法的内部可见,即只在该方法内可以使用。 方...

网友评论

      本文标题:关于内部类

      本文链接:https://www.haomeiwen.com/subject/cxlvnqtx.html