关于“类.class”和“类.this”
今天在浏览知乎的时候,看到了这个问题,感觉很多人说的不清楚。问题链接:Java 类名.class与类名.this 的区别?
话说它有什么区别呢?从API层面上来说,"类.class"返回该类所对应的class对象,而"类.this"得到的是该类的对象,这两者的区别大着呢!前者是描述该类的Class对象,后者是该类的实例对象,两者没有可比性。API层面上有些说不清,还是从字节码层面上来说吧!话不多说,先上车。
类.class
看“类.class”
public class Demo62 {
public static void main(String[] args) {
Class clz = Demo62.class;
}
}
执行反编译:
"D:\Program Files\Java\jdk1.8.0_77\bin\javap.exe" -v -p com.bigdata.java.Demo62
Classfile /D:/Project/JvmDemo/target/classes/com/bigdata/java/Demo62.class
Last modified 2020-2-17; size 438 bytes
MD5 checksum 19a4a4e0310212eaabf8f8a5c18d82fc
Compiled from "Demo62.java"
public class com.bigdata.java.Demo62
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #3.#19 // java/lang/Object."<init>":()V
#2 = Class #20 // com/bigdata/java/Demo62
#3 = Class #21 // java/lang/Object
#4 = Utf8 <init>
#5 = Utf8 ()V
#6 = Utf8 Code
#7 = Utf8 LineNumberTable
#8 = Utf8 LocalVariableTable
#9 = Utf8 this
#10 = Utf8 Lcom/bigdata/java/Demo62;
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 args
#14 = Utf8 [Ljava/lang/String;
#15 = Utf8 clz
#16 = Utf8 Ljava/lang/Class;
#17 = Utf8 SourceFile
#18 = Utf8 Demo62.java
#19 = NameAndType #4:#5 // "<init>":()V
#20 = Utf8 com/bigdata/java/Demo62
#21 = Utf8 java/lang/Object
{
public com.bigdata.java.Demo62();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/bigdata/java/Demo62;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=2, args_size=1
0: ldc #2 // class com/bigdata/java/Demo62
2: astore_1
3: return
LineNumberTable:
line 5: 0
line 6: 3
LocalVariableTable:
Start Length Slot Name Signature
0 4 0 args [Ljava/lang/String;
3 1 1 clz Ljava/lang/Class;
}
SourceFile: "Demo62.java"
Process finished with exit code 0
这一小段代码编译后这么多,别被吓到,有用的没几条,着重看这些:
Code:
stack=1, locals=2, args_size=1
0: ldc #2 // class com/bigdata/java/Demo62
2: astore_1
3: return
“ 0: ldc #2 // class com/bigdata/java/Demo62”,表示将常量池中索引2位置上的元素,压入到操作数的栈顶。通过查阅常量池,我们可以看到它就是“com/bigdata/java/Demo62”,只是它是一个符号引用,在类的解析阶段被转换为了直接引用,因此这里压入到操作数栈顶的是解析后的直接引用(也即该类所对应的Class对象的地址或指针),
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.ldc
“astore_1”,弹出操作数栈顶的值,然后放到局部变量表的slot1中。
所以最终看到的就是局部变量表的slot1中有一个class对象
LocalVariableTable:
Start Length Slot Name Signature
0 4 0 args [Ljava/lang/String;
3 1 1 clz Ljava/lang/Class;
小结:所以“类.class”返回的实际上就是该类所对应的class对象。但是底层实际上是通过常量池找到对应的class对象的。
类.this
再看“类.this”
public class Demo62 {
public Demo62 method1(){
return Demo62.this;
}
}
执行反编译:
"D:\Program Files\Java\jdk1.8.0_77\bin\javap.exe" -v -p com.bigdata.java.Demo62
Classfile /D:/Project/JvmDemo/target/classes/com/bigdata/java/Demo62.class
Last modified 2020-2-17; size 375 bytes
MD5 checksum 0ca3c6df6b1bc2a64c91aa755eba16fe
Compiled from "Demo62.java"
public class com.bigdata.java.Demo62
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #3.#15 // java/lang/Object."<init>":()V
#2 = Class #16 // com/bigdata/java/Demo62
#3 = Class #17 // java/lang/Object
#4 = Utf8 <init>
#5 = Utf8 ()V
#6 = Utf8 Code
#7 = Utf8 LineNumberTable
#8 = Utf8 LocalVariableTable
#9 = Utf8 this
#10 = Utf8 Lcom/bigdata/java/Demo62;
#11 = Utf8 method1
#12 = Utf8 ()Lcom/bigdata/java/Demo62;
#13 = Utf8 SourceFile
#14 = Utf8 Demo62.java
#15 = NameAndType #4:#5 // "<init>":()V
#16 = Utf8 com/bigdata/java/Demo62
#17 = Utf8 java/lang/Object
{
public com.bigdata.java.Demo62();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/bigdata/java/Demo62;
public com.bigdata.java.Demo62 method1();
descriptor: ()Lcom/bigdata/java/Demo62;
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: areturn
LineNumberTable:
line 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 2 0 this Lcom/bigdata/java/Demo62;
}
SourceFile: "Demo62.java"
Process finished with exit code 0
重点关注这段代码
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: areturn
LocalVariableTable:
Start Length Slot Name Signature
0 2 0 this Lcom/bigdata/java/Demo62;
"aload_0",表示将加载局部变量表slot0中的元素到操作数栈顶,而这个slot0中存储的就是this。这里先说明一下,每个方法(非静态方法)都隐含有一个“this”参数,这也是为什么我们能够在方法中使用this的原因。
“areturn”,表示从方法返回引用。这里它将弹出操作数栈弹出的元素并返回。
小结:“类.this”得到的是当前类的实例。底层实际上就是得到方法的隐含参数this。
最后,
public class Demo62 {
public static void main(String[] args) {
Class clz = Demo62.class;
Demo62 instance1 = new Demo62();
Demo62 instance2 = instance1.method1();
System.out.println(instance1 == instance2);//true
System.out.println(instance1.getClass()==clz);//true
}
public Demo62 method1(){
return Demo62.this;
}
}
通过上面的分析,得到这样的执行结果是毫不意外的。
再看另外热议的问题
class Foo {
class Bar {
Foo getFoo() {
return Foo.this;
}
}
}
在Foo.Bar类中的getFoo()方法中,如果直接写“this”的话所指的是这个Foo.Bar类的实例,而如果要指定外围的Foo类的this实例的话,这里就得写成Foo.this。
特别的,如果在上例的getFoo()方法中写Bar.this的话,作用就跟直接写this一样,指定的是当前的Foo.Bar类实例。作者:RednaxelaFX
链接:https://www.zhihu.com/question/55565290/answer/145355951
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
下面我们尝试从字节码的角度来解读,话不多说,上码:
...
Constant pool:
#1 = Fieldref #3.#20 // com/bigdata/java/other/Foo$Bar.this$0:Lcom/bigdata/java/other/Foo;
#2 = Methodref #4.#21 // java/lang/Object."<init>":()V
#3 = Class #23 // com/bigdata/java/other/Foo$Bar
#4 = Class #24 // java/lang/Object
#5 = Utf8 this$0
#6 = Utf8 Lcom/bigdata/java/other/Foo;
#7 = Utf8 <init>
#8 = Utf8 (Lcom/bigdata/java/other/Foo;)V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Bar
#14 = Utf8 InnerClasses
#15 = Utf8 Lcom/bigdata/java/other/Foo$Bar;
#16 = Utf8 getFoo
#17 = Utf8 ()Lcom/bigdata/java/other/Foo;
#18 = Utf8 SourceFile
#19 = Utf8 Foo.java
#20 = NameAndType #5:#6 // this$0:Lcom/bigdata/java/other/Foo;
#21 = NameAndType #7:#25 // "<init>":()V
#22 = Class #26 // com/bigdata/java/other/Foo
#23 = Utf8 com/bigdata/java/other/Foo$Bar
#24 = Utf8 java/lang/Object
#25 = Utf8 ()V
#26 = Utf8 com/bigdata/java/other/Foo
...
{
final com.bigdata.java.other.Foo this$0;
descriptor: Lcom/bigdata/java/other/Foo;
flags: ACC_FINAL, ACC_SYNTHETIC
com.bigdata.java.other.Foo$Bar(com.bigdata.java.other.Foo);
descriptor: (Lcom/bigdata/java/other/Foo;)V
flags:
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:Lcom/bigdata/java/other/Foo;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
LineNumberTable:
line 4: 0
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/bigdata/java/other/Foo$Bar;
0 10 1 this$0 Lcom/bigdata/java/other/Foo;
com.bigdata.java.other.Foo getFoo();
descriptor: ()Lcom/bigdata/java/other/Foo;
flags:
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #1 // Field this$0:Lcom/bigdata/java/other/Foo;
4: areturn
LineNumberTable:
line 6: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/bigdata/java/other/Foo$Bar;
}
...
重点看“ getFoo()”方法
aload_0:表示将本地变量表中的slot0元素加载到操作数栈顶,这里也就是将this(com/bigdata/java/other/Foo$Bar)压入到操作数栈顶。
getfield:表示获取字段。弹出操作数栈顶的com/bigdata/java/other/Foo$Bar,然后获取该对象的#1索引所对应的常量池的值,也即“this$0”,它的类型是“Foo”,然后将它压入到操作数的栈顶
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.getfield
areturn:弹出操作数栈顶的值并返回,也即返回“this.$0”,它的类型是Foo。
小结:通过这个实例,印证了Foo.this返回的就是Foo。
下面我们在分别看Bar.this和this
脑洞大开,写成这种形式:
class Foo {
class Bar {
Foo getFoo() {
return this;//编译器报错,提示类型不兼容
}
}
}
这里的类型不兼容也不难理解,由于getFoo中的this,指的是“Foo$Bar”,而返回值是“Foo”。
修改为return this
下面将返回值修改为this
class Foo {
class Bar {
Bar getFoo() {
return this;
}
}
}
反编译:
...
com.bigdata.java.other.Foo$Bar getFoo();
descriptor: ()Lcom/bigdata/java/other/Foo$Bar;
flags:
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: areturn
LineNumberTable:
line 6: 0
LocalVariableTable:
Start Length Slot Name Signature
0 2 0 this Lcom/bigdata/java/other/Foo$Bar;
...
可以发现,它返回了本地变量表slot0的元素,也即"Foo$Bar"
修改为Bar.this
class Foo {
class Bar {
Bar getFoo() {
return Bar.this;
}
}
}
反编译:
...
com.bigdata.java.other.Foo$Bar getFoo();
descriptor: ()Lcom/bigdata/java/other/Foo$Bar;
flags:
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: areturn
LineNumberTable:
line 6: 0
LocalVariableTable:
Start Length Slot Name Signature
0 2 0 this Lcom/bigdata/java/other/Foo$Bar;
...
可以发现字节码和return this是一样的。返回值也是一样的。从而印证了“getFoo()方法中写Bar.this的话,作用就跟直接写this一样,指定的是当前的Foo.Bar类实例”的结论。
关于“类.class”和“类.this”的更多相关文章
- JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式
相关链接: JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式 JS面向对象(2) -- this的使用,对 ...
- 【转】UML类图与类的关系详解
UML类图与类的关系详解 2011-04-21 来源:网络 在画类图的时候,理清类和类之间的关系是重点.类的关系有泛化(Generalization).实现(Realization).依赖(D ...
- Java如何解决脆弱基类(基类被冻结)问题
概述 大多数好的设计者象躲避瘟疫一样来避免使用实现继承(extends 关系).实际上80%的代码应该完全用interfaces写,而不是通过extends.“JAVA设计模式”一书详细阐述了怎样用 ...
- 类A have-a 类B,类B访问类A public 成员
需求是类A中包含类B,而类B又需要访问类A的public属性的成员. 首先类B中要访问类A的属性,那么对于类B而言,我们必须要知道有类A这个类,所以在类B的具体实现之前我们需要前向声明类A. 对于类A ...
- UML(一) 类图及类间关系
原创文章,同步发自作者个人博客,http://www.jasongj.com/uml/class_diagram/ UML类图 UML类图介绍 在UML 2.*的13种图形中,类图是使用频率最高的UM ...
- JAVA正则表达式:Pattern类与Matcher类详解(转)
java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包.它包括两个类:Pattern和Matcher Pattern 一个Pattern是一个正则表达式经编译后的表 ...
- C# 根据类名称创建类示例
//获得类所在的程序集名称(此处我选择当前程序集) string bllName = System.IO.Path.GetFileNameWithoutExtension(System.Reflect ...
- 使用date类和format类对系统当前时间进行格式化显示
一:Date------------String 代码1:(代码二对显示出来的时间格式进行优化) package DateDemo; import java.text.SimpleDateFormat ...
- java中的 FileWriter类 和 FileReader类的一些基本用法
1,FileWriter类(字符输出流类) |--用来写入字符文件的便捷类.此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的.要自己指定这些值,可以先在 FileOutputStream ...
- 使用 Date 和 SimpleDateFormat 类表示时间、Calendar类和Math类
一. Date 和 SimpleDateFormat类表示时间 在程序开发中,经常需要处理日期和时间的相关数据,此时我们可以使用 java.util 包中的 Date 类.这个类最主要的作用就是获取当 ...
随机推荐
- .net core 自带分布式事务的微服务开源框架JMS
事务的统一性是微服务的一个重点问题,简洁有效的控制事务,更是程序员所需要的.JMS的诞生,就是为了更简单.更有效的控制事务. 先看一段调用微服务的代码: using (var ms = new JMS ...
- 【NeurlPS2019】Positional Normalization 位置归一化
作者提出,当前的BatchNorm, GroupNorm, InstanceNorm在空间层面归一化信息,同时丢弃了统计值.作者认为这些统计信息中包含重要的信息,如果有效利用,可以提高GAN和分类网络 ...
- ath10k MAC地址
使用openwrt下ath10k驱动QCA9880修改其MAC地址方式 1. 使用artGUI修改9880寄存器方法(该方法有严重后遗症,目前还没有找到修改回去的方法) 2. 使用preinit修改其 ...
- python学习之路------你想要的都在这里了
python学习之路------你想要的都在这里了 (根据自己的学习进度后期不断更新哟!!!) 一.python基础 1.python基础--python基本知识.七大数据类型等 2.python基础 ...
- Salt 系统初始化
目录 编辑states文件 1.DNS配置 dns.sls(在init目录下创建一个files文件,然后把resolv.conf放到文件下) [root@master init]# cat dns. ...
- 深入理解Spring AOP 1.0
本文相关代码(来自官方源码spring-test模块)请参见spring-demysify org.springframework.mylearntest包下. 统称能够实现AOP的语言为AOL,即( ...
- Python打开和关闭文件
Python打开和关闭文件: open(文件名,打开文件的模式[,寄存区的缓冲]): 文件名:字符串值 注:文件名带有后缀名 # 打开创建好的 test.txt 文件 f = open("t ...
- 11-19 hashlib模块
Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等. 什么是摘要算法呢? 摘要算法又称哈希算法.散列算法.它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进 ...
- PDOStatement::fetch
PDOStatement::fetch — 从结果集中获取下一行(PHP 5 >= 5.1.0, PECL pdo >= 0.1.0) 说明 语法 mixed PDOStatement:: ...
- 牛客练习赛64 如果我让你查回文你还爱我吗 线段树 树状数组 manacher 计数 区间本质不同回文串个数
LINK:如果我让你查回文你还爱我吗 了解到了这个模板题. 果然我不会写2333... 考试的时候想到了一个非常辣鸡的 线段树合并+莫队的做法 过不了不再赘述. 当然也想到了manacher不过不太会 ...