java基础之this、super关键字详解

java基础之this、super详解

一、this关键字

this关键字相信大家都很熟悉,在实际使用中主要有以下三个用途:
1.调用类中的成员变量;
2.调用类中的其他方法;
3.构造方法中调用类的其他构造方法(注意只能调用一个且必须位于首行)。
当我们平时开发中经常使用this之后,你有没有想过this是从哪里来的?this的数据类型是什么?为什么在static方法和static块中无法使用this?
接下来,我将对这些问题一个一个进行剖析,本节主要通过class来分析this的原理。
下面的代码用到了三种this的用途:

public class ThisStu {
    private String name;
    private Integer age;
    public ThisStu(String name) {
        this.name = name;
    }
    public ThisStu(String name, Integer age) {
        this(name);
        this.age = age;
    }
    public String getName() {
        this.getStaticNme("");
        return this.name;
    }
    public Integer getAge() {
        return age;
    }
    public static String getStaticNme(String name){
        return name;
    }
}

我们通过javap反编译ThisStu.class,可以看到第一个构造方法生成的如下代码:

 public com.xr.basic.ThisStu(java.lang.String); //第一个构造方法
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: aload_1
         6: putfield      #2                  // Field name:Ljava/lang/String;
         9: return
      LineNumberTable:
        line 6: 0
        line 7: 4
        line 8: 9
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  this   Lcom/xr/basic/ThisStu;
            0      10     1  name   Ljava/lang/String;
    MethodParameters:
      Name                           Flags
      name

其中可以看到LocalVariableTable属性,该属性是位于Code属性的属性表中的可选变长属性,调试器在执行方法的过程中,可以用它来确定某个局部变量的值。对比第一个构造方法,我们可以看到第一个构造方法中只有一个参数,而args_size=2,LocalVariableTable的第一个局部变量就是this,其类型为ThisStu!所以当我们调用该构造方法的时候,第一个(序号为0)局部变量是用来存储该类对象的引用(this),后续的其他参数将会传递至局部变量表中从1开始的连续位置上
同样地,代码中getName()编译出来的代码如下:

public java.lang.String getName();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #2                  // Field name:Ljava/lang/String;
         4: areturn
      LineNumberTable:
        line 14: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/xr/basic/ThisStu;

我们可以发现,构造方法、实例方法在LocalVariableTable的第一个参数都是this,这是由编译器自动添加的,其中aload_0指令将this引用推至栈顶,即压入栈。而且this的数据类型就是this所在方法的所属类
在反编译ThisStu.class的代码中,我们还可以看到如下代码:

 public static java.lang.String getStaticNme(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: areturn
      LineNumberTable:
        line 20: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       2     0  name   Ljava/lang/String;
    MethodParameters:
      Name                           Flags
      name

该代码是类中的静态方法反编译出来的,我们可以看到args_size=1和LocalVariableTable中只有一个局部变量(String name),所以我们可以得出结论:类的静态方法中,并没有保存this的局部变量,所以我们也就无法在类的静态方法中使用this关键字

二、super关键字

super 可以理解为是指向离自己最近的一个父(基)类对象的一个指针。
super也有三种用法:
1.直接引用父类的成员;
2.子类和父类中成员同名时候用来调用父类的成员;
3.引用父类的某个构造函数。
super的原理是在创建子类对象的时候,会生成一个指向父类的对象的引用super。以下面的代码为例:

class A{
    public void sayName(String name) {
        System.out.println(name);
    }
}
class B extends A{
}

通过javap反编译B.class可以得到如下代码:

Classfile /E:/java-projects/inspection-task/target/classes/com/xr/basic/B.class
  Last modified 2021-3-15; size 259 bytes
  MD5 checksum 9482d914c1f65de35fd45d02273b7366
  Compiled from "SuperStu.java"
class com.xr.basic.B extends com.xr.basic.A
  minor version: 0
  major version: 52
  flags: ACC_SUPER
Constant pool:
   #1 = Methodref          #3.#13         // com/xr/basic/A."<init>":()V
   #2 = Class              #14            // com/xr/basic/B
   #3 = Class              #15            // com/xr/basic/A
   #4 = Utf8               <init>
   #5 = Utf8               ()V
   #6 = Utf8               Code
   #7 = Utf8               LineNumberTable
   #8 = Utf8               LocalVariableTable
   #9 = Utf8               this
  #10 = Utf8               Lcom/xr/basic/B;
  #11 = Utf8               SourceFile
  #12 = Utf8               SuperStu.java
  #13 = NameAndType        #4:#5          // "<init>":()V
  #14 = Utf8               com/xr/basic/B
  #15 = Utf8               com/xr/basic/A
{
  com.xr.basic.B();
    descriptor: ()V
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method com/xr/basic/A."<init>":()V
         4: return
      LineNumberTable:
        line 12: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/xr/basic/B;
}
SourceFile: "SuperStu.java"

首先可以看到access flag记录了flags: ACC_SUPER。ACC_SUPER是用来表示如何调用父类的方法,在jdk1.1之前,jvm使用一种invokenonvirtual的指令,调用父类方法。这个方法就是现在的invokespecial 前身,他们的实现差距比较大。invokenonvirtual是不会进行虚函数查找的,也就是总是静态绑定。为了兼容性,通过设置ACC_SUPER标记表示使用新语义。
其次我们还可以看到:

1:invokespecial #1                  // Method com/xr/basic/A."<init>":()V

而当我们通过javap反编译A.class依然可以看到:

1: invokespecial #1                  // Method java/lang/Object."<init>":()V

在A和B类中,此处调用了父类的init方法,
注意上面的 invokespecial指令。super 关键字的底层原理就是靠 invokespecial指令。invokespecial是一个普通的调用指令,调用< init >方法、私有及父类方法,解析阶段确定唯一方法版本
invokespecial在使用上,只能在三种情况下使用:

  1. the instance initialization method, < init>
  2. a private method of this
  3. a method in a superclass of this
  • 11
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值