今天在浏览知乎的时候,看到了这个问题,感觉很多人说的不清楚。问题链接:Java 类名.class与类名.this 的区别?

话说它有什么区别呢?从API层面上来说,"类.class"返回该类所对应的class对象,而"类.this"得到的是该类的对象,这两者的区别大着呢!前者是描述该类的Class对象,后者是该类的实例对象,两者没有可比性。API层面上有些说不清,还是从字节码层面上来说吧!话不多说,先上车。

类.class

看“类.class”

  1. public class Demo62 {
  2. public static void main(String[] args) {
  3. Class clz = Demo62.class;
  4. }
  5. }

执行反编译:

  1. "D:\Program Files\Java\jdk1.8.0_77\bin\javap.exe" -v -p com.bigdata.java.Demo62
  2. Classfile /D:/Project/JvmDemo/target/classes/com/bigdata/java/Demo62.class
  3. Last modified 2020-2-17; size 438 bytes
  4. MD5 checksum 19a4a4e0310212eaabf8f8a5c18d82fc
  5. Compiled from "Demo62.java"
  6. public class com.bigdata.java.Demo62
  7. minor version: 0
  8. major version: 52
  9. flags: ACC_PUBLIC, ACC_SUPER
  10. Constant pool:
  11. #1 = Methodref #3.#19 // java/lang/Object."<init>":()V
  12. #2 = Class #20 // com/bigdata/java/Demo62
  13. #3 = Class #21 // java/lang/Object
  14. #4 = Utf8 <init>
  15. #5 = Utf8 ()V
  16. #6 = Utf8 Code
  17. #7 = Utf8 LineNumberTable
  18. #8 = Utf8 LocalVariableTable
  19. #9 = Utf8 this
  20. #10 = Utf8 Lcom/bigdata/java/Demo62;
  21. #11 = Utf8 main
  22. #12 = Utf8 ([Ljava/lang/String;)V
  23. #13 = Utf8 args
  24. #14 = Utf8 [Ljava/lang/String;
  25. #15 = Utf8 clz
  26. #16 = Utf8 Ljava/lang/Class;
  27. #17 = Utf8 SourceFile
  28. #18 = Utf8 Demo62.java
  29. #19 = NameAndType #4:#5 // "<init>":()V
  30. #20 = Utf8 com/bigdata/java/Demo62
  31. #21 = Utf8 java/lang/Object
  32. {
  33. public com.bigdata.java.Demo62();
  34. descriptor: ()V
  35. flags: ACC_PUBLIC
  36. Code:
  37. stack=1, locals=1, args_size=1
  38. 0: aload_0
  39. 1: invokespecial #1 // Method java/lang/Object."<init>":()V
  40. 4: return
  41. LineNumberTable:
  42. line 3: 0
  43. LocalVariableTable:
  44. Start Length Slot Name Signature
  45. 0 5 0 this Lcom/bigdata/java/Demo62;
  46. public static void main(java.lang.String[]);
  47. descriptor: ([Ljava/lang/String;)V
  48. flags: ACC_PUBLIC, ACC_STATIC
  49. Code:
  50. stack=1, locals=2, args_size=1
  51. 0: ldc #2 // class com/bigdata/java/Demo62
  52. 2: astore_1
  53. 3: return
  54. LineNumberTable:
  55. line 5: 0
  56. line 6: 3
  57. LocalVariableTable:
  58. Start Length Slot Name Signature
  59. 0 4 0 args [Ljava/lang/String;
  60. 3 1 1 clz Ljava/lang/Class;
  61. }
  62. SourceFile: "Demo62.java"
  63. Process finished with exit code 0

这一小段代码编译后这么多,别被吓到,有用的没几条,着重看这些:

  1. Code:
  2. stack=1, locals=2, args_size=1
  3. 0: ldc #2 // class com/bigdata/java/Demo62
  4. 2: astore_1
  5. 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对象

  1. LocalVariableTable:
  2. Start Length Slot Name Signature
  3. 0 4 0 args [Ljava/lang/String;
  4. 3 1 1 clz Ljava/lang/Class;

小结:所以“类.class”返回的实际上就是该类所对应的class对象。但是底层实际上是通过常量池找到对应的class对象的。

类.this

再看“类.this”

  1. public class Demo62 {
  2. public Demo62 method1(){
  3. return Demo62.this;
  4. }
  5. }

执行反编译:

  1. "D:\Program Files\Java\jdk1.8.0_77\bin\javap.exe" -v -p com.bigdata.java.Demo62
  2. Classfile /D:/Project/JvmDemo/target/classes/com/bigdata/java/Demo62.class
  3. Last modified 2020-2-17; size 375 bytes
  4. MD5 checksum 0ca3c6df6b1bc2a64c91aa755eba16fe
  5. Compiled from "Demo62.java"
  6. public class com.bigdata.java.Demo62
  7. minor version: 0
  8. major version: 52
  9. flags: ACC_PUBLIC, ACC_SUPER
  10. Constant pool:
  11. #1 = Methodref #3.#15 // java/lang/Object."<init>":()V
  12. #2 = Class #16 // com/bigdata/java/Demo62
  13. #3 = Class #17 // java/lang/Object
  14. #4 = Utf8 <init>
  15. #5 = Utf8 ()V
  16. #6 = Utf8 Code
  17. #7 = Utf8 LineNumberTable
  18. #8 = Utf8 LocalVariableTable
  19. #9 = Utf8 this
  20. #10 = Utf8 Lcom/bigdata/java/Demo62;
  21. #11 = Utf8 method1
  22. #12 = Utf8 ()Lcom/bigdata/java/Demo62;
  23. #13 = Utf8 SourceFile
  24. #14 = Utf8 Demo62.java
  25. #15 = NameAndType #4:#5 // "<init>":()V
  26. #16 = Utf8 com/bigdata/java/Demo62
  27. #17 = Utf8 java/lang/Object
  28. {
  29. public com.bigdata.java.Demo62();
  30. descriptor: ()V
  31. flags: ACC_PUBLIC
  32. Code:
  33. stack=1, locals=1, args_size=1
  34. 0: aload_0
  35. 1: invokespecial #1 // Method java/lang/Object."<init>":()V
  36. 4: return
  37. LineNumberTable:
  38. line 3: 0
  39. LocalVariableTable:
  40. Start Length Slot Name Signature
  41. 0 5 0 this Lcom/bigdata/java/Demo62;
  42. public com.bigdata.java.Demo62 method1();
  43. descriptor: ()Lcom/bigdata/java/Demo62;
  44. flags: ACC_PUBLIC
  45. Code:
  46. stack=1, locals=1, args_size=1
  47. 0: aload_0
  48. 1: areturn
  49. LineNumberTable:
  50. line 5: 0
  51. LocalVariableTable:
  52. Start Length Slot Name Signature
  53. 0 2 0 this Lcom/bigdata/java/Demo62;
  54. }
  55. SourceFile: "Demo62.java"
  56. Process finished with exit code 0

重点关注这段代码

  1. Code:
  2. stack=1, locals=1, args_size=1
  3. 0: aload_0
  4. 1: areturn
  5. LocalVariableTable:
  6. Start Length Slot Name Signature
  7. 0 2 0 this Lcom/bigdata/java/Demo62;

"aload_0",表示将加载局部变量表slot0中的元素到操作数栈顶,而这个slot0中存储的就是this。这里先说明一下,每个方法(非静态方法)都隐含有一个“this”参数,这也是为什么我们能够在方法中使用this的原因。

“areturn”,表示从方法返回引用。这里它将弹出操作数栈弹出的元素并返回。

小结:“类.this”得到的是当前类的实例。底层实际上就是得到方法的隐含参数this。

最后,

  1. public class Demo62 {
  2. public static void main(String[] args) {
  3. Class clz = Demo62.class;
  4. Demo62 instance1 = new Demo62();
  5. Demo62 instance2 = instance1.method1();
  6. System.out.println(instance1 == instance2);//true
  7. System.out.println(instance1.getClass()==clz);//true
  8. }
  9. public Demo62 method1(){
  10. return Demo62.this;
  11. }
  12. }
  13. 通过上面的分析,得到这样的执行结果是毫不意外的。

再看另外热议的问题

  1. class Foo {
  2. class Bar {
  3. Foo getFoo() {
  4. return Foo.this;
  5. }
  6. }
  7. }

在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

来源:知乎

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

下面我们尝试从字节码的角度来解读,话不多说,上码:

  1. ...
  2. Constant pool:
  3. #1 = Fieldref #3.#20 // com/bigdata/java/other/Foo$Bar.this$0:Lcom/bigdata/java/other/Foo;
  4. #2 = Methodref #4.#21 // java/lang/Object."<init>":()V
  5. #3 = Class #23 // com/bigdata/java/other/Foo$Bar
  6. #4 = Class #24 // java/lang/Object
  7. #5 = Utf8 this$0
  8. #6 = Utf8 Lcom/bigdata/java/other/Foo;
  9. #7 = Utf8 <init>
  10. #8 = Utf8 (Lcom/bigdata/java/other/Foo;)V
  11. #9 = Utf8 Code
  12. #10 = Utf8 LineNumberTable
  13. #11 = Utf8 LocalVariableTable
  14. #12 = Utf8 this
  15. #13 = Utf8 Bar
  16. #14 = Utf8 InnerClasses
  17. #15 = Utf8 Lcom/bigdata/java/other/Foo$Bar;
  18. #16 = Utf8 getFoo
  19. #17 = Utf8 ()Lcom/bigdata/java/other/Foo;
  20. #18 = Utf8 SourceFile
  21. #19 = Utf8 Foo.java
  22. #20 = NameAndType #5:#6 // this$0:Lcom/bigdata/java/other/Foo;
  23. #21 = NameAndType #7:#25 // "<init>":()V
  24. #22 = Class #26 // com/bigdata/java/other/Foo
  25. #23 = Utf8 com/bigdata/java/other/Foo$Bar
  26. #24 = Utf8 java/lang/Object
  27. #25 = Utf8 ()V
  28. #26 = Utf8 com/bigdata/java/other/Foo
  29. ...
  30. {
  31. final com.bigdata.java.other.Foo this$0;
  32. descriptor: Lcom/bigdata/java/other/Foo;
  33. flags: ACC_FINAL, ACC_SYNTHETIC
  34. com.bigdata.java.other.Foo$Bar(com.bigdata.java.other.Foo);
  35. descriptor: (Lcom/bigdata/java/other/Foo;)V
  36. flags:
  37. Code:
  38. stack=2, locals=2, args_size=2
  39. 0: aload_0
  40. 1: aload_1
  41. 2: putfield #1 // Field this$0:Lcom/bigdata/java/other/Foo;
  42. 5: aload_0
  43. 6: invokespecial #2 // Method java/lang/Object."<init>":()V
  44. 9: return
  45. LineNumberTable:
  46. line 4: 0
  47. LocalVariableTable:
  48. Start Length Slot Name Signature
  49. 0 10 0 this Lcom/bigdata/java/other/Foo$Bar;
  50. 0 10 1 this$0 Lcom/bigdata/java/other/Foo;
  51. com.bigdata.java.other.Foo getFoo();
  52. descriptor: ()Lcom/bigdata/java/other/Foo;
  53. flags:
  54. Code:
  55. stack=1, locals=1, args_size=1
  56. 0: aload_0
  57. 1: getfield #1 // Field this$0:Lcom/bigdata/java/other/Foo;
  58. 4: areturn
  59. LineNumberTable:
  60. line 6: 0
  61. LocalVariableTable:
  62. Start Length Slot Name Signature
  63. 0 5 0 this Lcom/bigdata/java/other/Foo$Bar;
  64. }
  65. ...

重点看“ 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

脑洞大开,写成这种形式:

  1. class Foo {
  2. class Bar {
  3. Foo getFoo() {
  4. return this;//编译器报错,提示类型不兼容
  5. }
  6. }
  7. }

这里的类型不兼容也不难理解,由于getFoo中的this,指的是“Foo$Bar”,而返回值是“Foo”。

修改为return this

下面将返回值修改为this

  1. class Foo {
  2. class Bar {
  3. Bar getFoo() {
  4. return this;
  5. }
  6. }
  7. }

反编译:

  1. ...
  2. com.bigdata.java.other.Foo$Bar getFoo();
  3. descriptor: ()Lcom/bigdata/java/other/Foo$Bar;
  4. flags:
  5. Code:
  6. stack=1, locals=1, args_size=1
  7. 0: aload_0
  8. 1: areturn
  9. LineNumberTable:
  10. line 6: 0
  11. LocalVariableTable:
  12. Start Length Slot Name Signature
  13. 0 2 0 this Lcom/bigdata/java/other/Foo$Bar;
  14. ...

可以发现,它返回了本地变量表slot0的元素,也即"Foo$Bar"

修改为Bar.this

  1. class Foo {
  2. class Bar {
  3. Bar getFoo() {
  4. return Bar.this;
  5. }
  6. }
  7. }

反编译:

  1. ...
  2. com.bigdata.java.other.Foo$Bar getFoo();
  3. descriptor: ()Lcom/bigdata/java/other/Foo$Bar;
  4. flags:
  5. Code:
  6. stack=1, locals=1, args_size=1
  7. 0: aload_0
  8. 1: areturn
  9. LineNumberTable:
  10. line 6: 0
  11. LocalVariableTable:
  12. Start Length Slot Name Signature
  13. 0 2 0 this Lcom/bigdata/java/other/Foo$Bar;
  14. ...

可以发现字节码和return this是一样的。返回值也是一样的。从而印证了“getFoo()方法中写Bar.this的话,作用就跟直接写this一样,指定的是当前的Foo.Bar类实例”的结论。

关于“类.class”和“类.this”的更多相关文章

  1. JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式

    相关链接: JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式 JS面向对象(2) -- this的使用,对 ...

  2. 【转】UML类图与类的关系详解

    UML类图与类的关系详解   2011-04-21 来源:网络   在画类图的时候,理清类和类之间的关系是重点.类的关系有泛化(Generalization).实现(Realization).依赖(D ...

  3. Java如何解决脆弱基类(基类被冻结)问题

    概述  大多数好的设计者象躲避瘟疫一样来避免使用实现继承(extends 关系).实际上80%的代码应该完全用interfaces写,而不是通过extends.“JAVA设计模式”一书详细阐述了怎样用 ...

  4. 类A have-a 类B,类B访问类A public 成员

    需求是类A中包含类B,而类B又需要访问类A的public属性的成员. 首先类B中要访问类A的属性,那么对于类B而言,我们必须要知道有类A这个类,所以在类B的具体实现之前我们需要前向声明类A. 对于类A ...

  5. UML(一) 类图及类间关系

    原创文章,同步发自作者个人博客,http://www.jasongj.com/uml/class_diagram/ UML类图 UML类图介绍 在UML 2.*的13种图形中,类图是使用频率最高的UM ...

  6. JAVA正则表达式:Pattern类与Matcher类详解(转)

    java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包.它包括两个类:Pattern和Matcher Pattern 一个Pattern是一个正则表达式经编译后的表 ...

  7. C# 根据类名称创建类示例

    //获得类所在的程序集名称(此处我选择当前程序集) string bllName = System.IO.Path.GetFileNameWithoutExtension(System.Reflect ...

  8. 使用date类和format类对系统当前时间进行格式化显示

    一:Date------------String 代码1:(代码二对显示出来的时间格式进行优化) package DateDemo; import java.text.SimpleDateFormat ...

  9. java中的 FileWriter类 和 FileReader类的一些基本用法

    1,FileWriter类(字符输出流类) |--用来写入字符文件的便捷类.此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的.要自己指定这些值,可以先在 FileOutputStream ...

  10. 使用 Date 和 SimpleDateFormat 类表示时间、Calendar类和Math类

    一. Date 和 SimpleDateFormat类表示时间 在程序开发中,经常需要处理日期和时间的相关数据,此时我们可以使用 java.util 包中的 Date 类.这个类最主要的作用就是获取当 ...

随机推荐

  1. Linux切换用户时报错/.bash_profile: Permission denied,命令行(终端提示符)出现-bash-4.2$

    Linux切换用户时报错/.bash_profile: Permission denied,命令行(终端提示符)出现-bash-4.2$ 利用su - 切换用户时,发现有一个用户切时出现如下情况 [r ...

  2. 01 . RPC简介原理及用Go实现一个简单的RCP

    RPC简介 本地过程调用 // 正常情况下程序的执行和调用情况.例如有如下go语言代码: package main import "fmt" func main() { var a ...

  3. 解决智慧城市发展困扰:Web 3D 智慧环卫 GIS 系统

    前言 智慧环卫,依托物联网技术与移动互联网技术,对环卫管理所涉及到的人.车.物.事进行全过程实时管理,合理设计规划环卫管理模式,提升环卫作业质量,降低环卫运营成本,用数字评估和推动垃圾分类管理实效.智 ...

  4. stringsream用法

    stringstream: 头文件: #include <sstream> 简单整理一下这玩意的作用,主要有三个吧. 类型转化 字符串拼接 字符串整合(这一个用处特别大!!!!!!!) 先 ...

  5. PHP array_unshift() 函数

    实例 插入元素 "blue" 到数组中: <?php$a=array("a"=>"red","b"=> ...

  6. HTML 布局 - 使用<div> 元素

    网站布局 大多数网站会把内容安排到多个列中(就像杂志或报纸那样).高佣联盟 www.cgewang.com 大多数网站可以使用 <div> 或者 <table> 元素来创建多列 ...

  7. PHP log1p() 函数

    实例 返回不同数的 log(1+number): <?phpecho(log1p(2.7183) . "<br>");echo(log1p(2) . " ...

  8. 程序员面试:C/C++求职者必备 20 道面试题,一道试题一份信心!

    面试真是痛并快乐的一件事,痛在被虐的体无完肤,快乐在可以短时间内积累很多问题,加速学习. 在我们准备面试的时候,遇到的面试题有难有易,不能因为容易,我们就轻视,更不能因为难,我们就放弃.我们面对高薪就 ...

  9. Codeforces Round #649 (Div. 2) E. X-OR 交互 二进制 随机 期望

    LINK:X-OR 本来是应该昨天晚上发的 可是昨天晚上 做这道题 写了一个分治做法 一直wa 然后查错 查不出来 心态崩了 想写对拍 发现交互库自己写不出来. 一系列sb操作 == 我都醉了. 今天 ...

  10. Jvm相关文章

    深入理解JVM-内存模型(jmm)和GC https://www.jianshu.com/p/76959115d486