概述
学习的文章
-
深入理解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:AsProjectgitProjectReciteInterviewQuestionsmylibrarydoc>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
最后
以上就是酷炫冬日为你收集整理的[转]学习笔记-Java中为什么内部类可以访问外部类的成员的全部内容,希望文章能够帮你解决[转]学习笔记-Java中为什么内部类可以访问外部类的成员所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复