Java中为什么this可以调用当前实例

 更新时间:2021年07月25日 16:17:32   作者:程序员小潘  
本文主要介绍了为什么可以通过this关键字访问到当前对象呢,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

1. 前言

在刚开始学习Java的时候,大家肯定都接触过this关键字,尤其是在构造函数赋值的时候,如下示例:

public class Person {
 private String name;
 private int age;

 public Person(String name, int age) {
  // 必须加this关键字,否则无法完成成员变量的赋值
  this.name = name;
  this.age = age;
 }
}

在构造函数中,如果成员变量名称和参数名称相同时,必须加this关键字,否则你只是将参数name赋值给它本身,并没有赋值给Person.name,因为局部变量的优先级高于成员变量。一旦加上this关键字,由于this指向的是当前实例,就可以完成对象的成员变量赋值。

那为什么可以通过this关键字访问到当前对象呢?

2. 栈帧

要弄清这个问题,首先要了解JVM在执行方法时,方法栈的栈帧结构。

Java虚拟机以【方法】作为最基本的运行单元,【栈帧】就是用于支持JVM进行方法调用背后的数据结构,它也是JVM运行时数据区中【虚拟机栈】中的栈元素。

简单点说,方法的执行过程可看作是一个个栈帧从入栈到出栈的过程。

栈帧中存储了方法的:局部变量表、操作数栈、动态连接和方法返回地址等信息。

当使用javac程序将源码编译成字节码后,一个方法的栈帧需要多大的局部变量表,多深的操作数栈就已经被计算出来,并且写入到方法表的【Code】属性中了,这一切和程序运行时无关。

换言之,一个方法它有多少个局部变量,在编译时就已经确定,不会随着程序的运行而改变。今天我们要探究的【this】问题,就在方法栈的局部变量表中。

3. 分析

我们仍然用javap命令来分析编译后的Class文件,这是最有说服力的。

public class Person {

 public void say() {

 }

 public static void staticSay() {
  
 }
}

如上一段代码,分别有一个实例方法和静态方法,我们已经知道,在静态方法中,是无法访问【this】的,但是在实例方法中可以。

先javac Person.java再javap -verbose Person,得到的信息有点长,我只贴这两个方法的信息。

public void say();
descriptor: ()V
flags: ACC_PUBLIC
Code:
  stack=0, locals=1, args_size=1
  0: return
  LineNumberTable:
 line 8: 0

public static void staticSay();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC
Code:
  stack=0, locals=0, args_size=0
  0: return
  LineNumberTable:
 line 12: 0

重点关注【Code】那一栏,say()有一个局部变量,有一个参数。staticSay()没有局部变量,也没有参数。

是不是感到很疑惑?say()方法形参是空的,方法体也是空的,为什么编译后会显示它有一个局部变量和一个形参呢?

其实,对于实例方法而言,它至少有一个参数和一个局部变量,那就是当前对象。JVM在调用对象的实例方法时,会将对象本身的引用作为第0号参数传递过去,这样你就可以通过this关键字访问到对象本身了,其实也就是访问第0个参数而已。

如下,这两个方法其实是等价的。

public void say(Person this, String text) {
    System.out.println(text);
}

public void say(String text) {
    System.out.println(text);
}

4. 总结

Java虚拟机在执行方法时,会将方法打包成一个【栈帧】,栈帧中有【局部变量表】,参数也是局部变量表的一部分,一个方法的局部变量表有多大在编译时就已经确定,不会随着程序的运行而改变。
对于实例方法而言,在编译时会自动加上一个隐藏的0号参数,就是当前类。JVM在调用对象的实例方法时,会自动将对象引用作为第0号参数传递过去,访问【this】其实就是访问第0号参数。

到此这篇关于Java中为什么this可以调用当前实例的文章就介绍到这了,更多相关Java this调用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • java对象初始化顺序验证示例

    java对象初始化顺序验证示例

    以下这段小程序对调用对象构造函数时,父类构造函数、成员变量初始化函数,以及非静态初始化块调用顺序进行验证,不考虑静态成员及静态初始化块
    2014-02-02
  • 学习spring事务与消息队列

    学习spring事务与消息队列

    这篇文章主要为大家详细介绍了spring事务与消息队列,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • SpringMVC 限流的示例代码

    SpringMVC 限流的示例代码

    这篇文章主要介绍了SpringMVC 限流的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-12-12
  • 使用HTTPclient保持长连接

    使用HTTPclient保持长连接

    这篇文章主要介绍了使用HTTPclient保持长连接,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • springboot后台session的存储与取出方式

    springboot后台session的存储与取出方式

    这篇文章主要介绍了springboot后台session的存储与取出方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06
  • Java泛型通配符的使用详解

    Java泛型通配符的使用详解

    本文主要介绍了Java泛型通配符的使用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01
  • SpringMVC处理数据输出的实例代码

    SpringMVC处理数据输出的实例代码

    这篇文章主要给大家介绍了关于SpringMVC处理数据输出的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-05-05
  • 使用原生JDBC动态解析并获取表格列名和数据的方法

    使用原生JDBC动态解析并获取表格列名和数据的方法

    这篇文章主要介绍了使用原生JDBC动态解析并获取表格列名和数据,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-08-08
  • Java依赖-关联-聚合-组合之间区别_动力节点Java学院整理

    Java依赖-关联-聚合-组合之间区别_动力节点Java学院整理

    这篇文章主要介绍了Java依赖-关联-聚合-组合之间区别理解,依赖关系比较好区分,它是耦合度最弱的一种,下文给大家介绍的非常详细,感兴趣的朋友一起看看吧
    2017-08-08
  • JPA如何使用findBy方法自定义查询

    JPA如何使用findBy方法自定义查询

    这篇文章主要介绍了JPA如何使用findBy方法自定义查询,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11

最新评论