学习的文章
-
深入理解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