[转]学习笔记-Java中为什么内部类可以访问外部类的成员

学习的文章

  • 深入理解Java中为什么内部类可以访问外部类的成员
    https://blog.csdn.net/zhangjg_blog/article/details/20000769

  • Java 内部类综述
    https://blog.csdn.net/justloveyou_/article/details/53245561
    pm disable com.sankuai.meituan com.sankuai.meituan/com.dianping.base.push.pushservice.PushWakeUpJob

研究对象 Out.java

源码

package InnerJava;

class Outer {
    String outValue = "out value";

    class Inner {
        String innerValue = "inner value";

        String getOutValue() {
            return outValue;
        }
    }
}

调用-内部类可以访问外部类示例

内部类可以访问外部类示例

package InnerJava

class RunTest {
    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            println("main")

            val outer = Outer()
            println("Outer outValue= ${outer.outValue}")

            val inner = outer.Inner()
            println("Inner innerValue = ${inner.innerValue}, getOutValue = ${inner.getOutValue()}")
        }
    }
}

运行结果

main
Outer outValue= out value
Inner innerValue = inner value, getOutValue = out value

Process finished with exit code 0

编译class文件和反编译class文件

cmd窗口使用如下2个命令

  • javac Outer.java
  • javap -classpath . -v Outer$Inner

反编译文件Out.Inner

重点看这份,其实我没有看得很懂。暂时回复他人结论如下:

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

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

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

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

3 在调用内部类的构造函数初始化内部类对象时, 会默认传入外部类的引用。
————————————————
版权声明:本文为CSDN博主「昨夜星辰_zhangjg」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhangjg_blog/article/details/20000769

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

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

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

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

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



警告: 二进制文件Outer$Inner包含InnerJava.Outer$Inner
Classfile /D:/AsProject/gitProject/ReciteInterviewQuestions/mylibrary/doc/Outer$Inner.class
  Last modified 2022-1-29; size 518 bytes
  MD5 checksum 49078343d2043225136cd3928d8f6bcd
  Compiled from "Outer.java"
class InnerJava.Outer$Inner
  minor version: 0
  major version: 52
  flags: ACC_SUPER
Constant pool:
   #1 = Fieldref           #6.#20         // InnerJava/Outer$Inner.this$0:LInnerJava/Outer;
   #2 = Methodref          #7.#21         // java/lang/Object."<init>":()V
   #3 = String             #22            // inner value
   #4 = Fieldref           #6.#23         // InnerJava/Outer$Inner.innerValue:Ljava/lang/String;
   #5 = Fieldref           #24.#25        // InnerJava/Outer.outValue:Ljava/lang/String;
   #6 = Class              #26            // InnerJava/Outer$Inner
   #7 = Class              #29            // java/lang/Object
   #8 = Utf8               innerValue
   #9 = Utf8               Ljava/lang/String;
  #10 = Utf8               this$0
  #11 = Utf8               LInnerJava/Outer;
  #12 = Utf8               <init>
  #13 = Utf8               (LInnerJava/Outer;)V
  #14 = Utf8               Code
  #15 = Utf8               LineNumberTable
  #16 = Utf8               getOutValue
  #17 = Utf8               ()Ljava/lang/String;
  #18 = Utf8               SourceFile
  #19 = Utf8               Outer.java
  #20 = NameAndType        #10:#11        // this$0:LInnerJava/Outer;
  #21 = NameAndType        #12:#30        // "<init>":()V
  #22 = Utf8               inner value
  #23 = NameAndType        #8:#9          // innerValue:Ljava/lang/String;
  #24 = Class              #31            // InnerJava/Outer
  #25 = NameAndType        #32:#9         // outValue:Ljava/lang/String;
  #26 = Utf8               InnerJava/Outer$Inner
  #27 = Utf8               Inner
  #28 = Utf8               InnerClasses
  #29 = Utf8               java/lang/Object
  #30 = Utf8               ()V
  #31 = Utf8               InnerJava/Outer
  #32 = Utf8               outValue
{
  java.lang.String innerValue;
    descriptor: Ljava/lang/String;
    flags:

  
  final InnerJava.Outer this$0;//  在内部类Outer$Inner中, 存在一个名字为this$0 , 类型为Outer的成员变量, 并且这个变量是final的。 其实这个就是所谓的“在内部类对象中存在的指向外部类对象的引用”。但是我们在定义这个内部类的时候, 并没有声明它, 所以这个成员变量是编译器加上的。
    descriptor: LInnerJava/Outer;
    flags: ACC_FINAL, ACC_SYNTHETIC

  InnerJava.Outer$Inner(InnerJava.Outer);
    descriptor: (LInnerJava/Outer;)V
    flags:
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0  // 将局部变量表中的第一个引用变量加载到操作数栈。 这里有几点需要说明。 局部变量表中的变量在方法执行前就已经初始化完成;局部变量表中的变量包括方法的参数;成员方法的局部变量表中的第一个变量永远是this;操作数栈就是执行当前代码的栈。所以这句话的意思是: 将this引用从局部变量表加载到操作数栈。
         1: aload_1 // 将局部变量表中的第二个引用变量加载到操作数栈。 这里加载的变量就是构造方法中的Outer类型的参数。
         2: putfield      #1                  // Field this$0:LInnerJava/Outer; // 使用操作数栈顶端的引用变量为指定的成员变量赋值。 这里的意思是将外面传入的Outer类型的参数赋给成员变量this$0 。 这一句putfield字节码就揭示了, 指向外部类对象的这个引用变量是如何赋值的。
         5: aload_0
         6: invokespecial #2                  // Method java/lang/Object."<init>":()V
         9: aload_0
        10: ldc           #3                  // String inner value
        12: putfield      #4                  // Field innerValue:Ljava/lang/String;
        15: return
      LineNumberTable:
        line 6: 0
        line 7: 9

  java.lang.String getOutValue();
    descriptor: ()Ljava/lang/String;
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #1                  // Field this$0:LInnerJava/Outer;
         4: getfield      #5                  // Field InnerJava/Outer.outValue:Ljava/lang/String;
         7: areturn
      LineNumberTable:
        line 10: 0
}
SourceFile: "Outer.java"
InnerClasses:
     #27= #6 of #24; //Inner=class InnerJava/Outer$Inner of class InnerJava/Outer

反编译文件Out

可以忽略不看

D:\AsProject\gitProject\ReciteInterviewQuestions\mylibrary\doc>javap -classpath . -v Outer
警告: 二进制文件Outer包含InnerJava.Outer
Classfile /D:/AsProject/gitProject/ReciteInterviewQuestions/mylibrary/doc/Outer.class
  Last modified 2022-1-29; size 335 bytes
  MD5 checksum 1bbf65753067dca0d0fc84860ba56378
  Compiled from "Outer.java"
class InnerJava.Outer
  minor version: 0
  major version: 52
  flags: ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#17         // java/lang/Object."<init>":()V
   #2 = String             #18            // out value
   #3 = Fieldref           #4.#19         // InnerJava/Outer.outValue:Ljava/lang/String;
   #4 = Class              #20            // InnerJava/Outer
   #5 = Class              #21            // java/lang/Object
   #6 = Class              #22            // InnerJava/Outer$Inner
   #7 = Utf8               Inner
   #8 = Utf8               InnerClasses
   #9 = Utf8               outValue
  #10 = Utf8               Ljava/lang/String;
  #11 = Utf8               <init>
  #12 = Utf8               ()V
  #13 = Utf8               Code
  #14 = Utf8               LineNumberTable
  #15 = Utf8               SourceFile
  #16 = Utf8               Outer.java
  #17 = NameAndType        #11:#12        // "<init>":()V
  #18 = Utf8               out value
  #19 = NameAndType        #9:#10         // outValue:Ljava/lang/String;
  #20 = Utf8               InnerJava/Outer
  #21 = Utf8               java/lang/Object
  #22 = Utf8               InnerJava/Outer$Inner
{
  java.lang.String outValue;
    descriptor: Ljava/lang/String;
    flags:

  InnerJava.Outer();
    descriptor: ()V
    flags:
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: ldc           #2                  // String out value
         7: putfield      #3                  // Field outValue:Ljava/lang/String;
        10: return
      LineNumberTable:
        line 3: 0
        line 4: 4
}
SourceFile: "Outer.java"
InnerClasses:
     #7= #6 of #4; //Inner=class InnerJava/Outer$Inner of class InnerJava/Outer

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值