Java中为什么this可以调用当前实例
作者:程序员小潘 发布时间:2022-02-19 18:41:39
目录
1. 前言
2. 栈帧
3. 分析
4. 总结
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号参数。
来源:https://juejin.cn/post/6988322694525616165


猜你喜欢
- 本文实例讲述了Android编程实现ListView滚动提示等待框功能。分享给大家供大家参考,具体如下:其实原理很简单,只需要设置监听lis
- 1.PDF文件简介PDF是可移植文档格式,是一种电子文件格式,具有许多其他电子文档格式无法相比的优点。PDF文件格式可以将文字、字型、格式、
- 本文实例为大家分享了java排序算法之冒泡排序的具体代码,供大家参考,具体内容如下冒泡排序冒泡排序无疑是最为出名的排序算法之一,从序列的一端
- 前言提问:springboot项目,开发环境、测试环境和生产环境配置文件如何分开表示?答:多profile文件方式1、多环境配置(profi
- Java 8 最大的特性无异于更多地面向函数,比如引入了lambda等,可以更好地进行函数式编程。前段时间无意间发现了map.merge()
- 最近在开发项目的时候遇到一个问题,在myecilpes上使用googlede gson读取项目中的json文件成功,然后把项目发布到tomc
- 一、前言正常情况下classloader只能找到jar里面当前目录或者文件类里面的*.class文件。为了能够加载嵌套jar里面的资源之前都
- 网上对于这样的功能已经是泛滥成河了,但是最近遇到这样的一个需求,还是要值得我们学习一下,并将他记录下来。布局文件:<FrameLayo
- 在之前的章节中,我们都是假设程序中只有一条执行流,程序从main方法的第一条语句逐条执行直到结束。从本节开始,我们讨论并发,在程序中创建线程
- 本文实例讲述了winform中的ListBox和ComboBox绑定数据用法。分享给大家供大家参考。具体实现方法如下:本例实现将集合数据绑定
- 效果图激活引擎第一步配置APP_ID和SDK_KEY int activeCode = FaceEngine.activeOnline( C
- Java8Stream流操作List去重根据属性去重整体去重使用distinctArrayList<LabelInfoDTO>
- 简介API Gateway,时系统的唯一对外的入口,介于客户端和服务端之间的中间层,处理非业务功能,提供路由请求,鉴权,监控,缓存,限流等功
- 1. 首先设置Web视图webview.setWebViewClient(new MyWebViewClient());webview1.s
- 本文实例为大家分享了Flutter自定义底部导航栏的具体代码,供大家参考,具体内容如下文件结构:main.dartimport 'p
- 平时我们编写WinForm程序经常使用VS进行拖控件的方式,这样做虽然简单,但是无法深入了解WinForm程序的本质。其实,用记事本也可以编
- 前言我们之前学的单链表,默认只能从链表的头部遍历到链表的尾部,在实际中应用太少见,太局限;而双向链表,对于该链表中的任意节点,既可以通过该节
- C#异常处理总结及简单实例一、异常处理的理解?异常处理是指程序在运行过程中,发生错误会导致程序退出,这种错误,就叫做异常。因此处理这种错误,
- 最近学习了一个视频公开课,讲到了利用HorizontalScrollView仿ViewPager设计的一个简单相册,其实主要用了ViewPa
- 在系统开发中,需要对请求和响应分别拦截下来进行解密和加密处理,在springboot中提供了RequestBodyAdviceAdapter