`
RednaxelaFX
  • 浏览: 3037056 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

javac在编译创建内部类对象时生成的奇怪的getClass()调用是什么?

阅读更多
有人问下面这段代码里,main()方法里的outer.new Inner()部分为什么会生成了一个对outer.getClass()的调用:
public class Outer {
  public class Inner { }
  public static void main(String[] args) {
    Outer outer = new Outer();
    Outer.Inner inner = outer.new Inner();
  }
}

javac编译它生成的main方法的代码是:
public static void main(java.lang.String[]);
  Code:
   Stack=4, Locals=3, Args_size=1
   0:   new     #2; //class Outer
   3:   dup
   4:   invokespecial   #3; //Method "<init>":()V
   7:   astore_1
   8:   new     #4; //class Outer$Inner
   11:  dup
   12:  aload_1
   13:  dup
   14:  invokevirtual   #5; //Method java/lang/Object.getClass:()Ljava/lang/Class;
   17:  pop
   18:  invokespecial   #6; //Method Outer$Inner."<init>":(LOuter;)V
   21:  astore_2
   22:  return
  LineNumberTable:
   line 4: 0
   line 5: 8
   line 6: 22

其中,对应outer.new Inner()的部分是:
   8:   new     #4; //class Outer$Inner
   11:  dup
   12:  aload_1
   13:  dup
   14:  invokevirtual   #5; //Method java/lang/Object.getClass:()Ljava/lang/Class;
   17:  pop
   18:  invokespecial   #6; //Method Outer$Inner."<init>":(LOuter;)V

可以看到里面有一处对outer.getClass()的调用,然而得到的结果却马上被pop指令抛弃掉了。

这个问题通过调试javac很容易解决。调试javac的方法可以参考以前一帖

设置好调试环境后,在调试器里把上面的代码交给javac去编译。
简单猜测就可以知道,生成的对outer.getClass()方法的调用是在最终生成代码的时候才做的,而不是在更早阶段被解除的语法糖,所以我们要注意的目标就是com.sun.tools.javac.jvm.Gen类,其中的visitNewClass(JCNewClass tree)方法。

在这个方法设上断点,然后开始调试。
第一次碰到断点会是main()方法里的new Outer(),这个跳过。
然后第二次进来的时候,观察调试器的变量窗口,可以看到:

从javac的角度来看,源码里的
outer.new Inner()

被改写成了这种形式:
new Outer$Inner(outer<*nullchk*>)

(内部类被改名和改写为顶层类、隐式的外部类参数改写为显式参数)

接下来,有趣的点就是那个<*nullchk*>注释。
传给Inner()构造器的实际参数并不是原本的outer局部变量,而是outer局部变量外加一个空指针检查——要的值还是outer的值,不过如果outer为null的话,这里要抛出NullPointerException。
从javac的角度看,直接读outer局部变量可以用一个JCTree.JCIdent节点来表示,而这里则多包装了一个tag为JCTree.NULLCHK的JCTree.JCUnary节点。

正是在生成这个outer<*nullchk*>节点的代码时,会执行到Gen.visitUnary()的下述部分:
public void visitUnary(JCUnary tree) {
    // ...
        Item od = genExpr(tree.arg, operator.type.getParameterTypes().head);
        switch (tree.tag) {
        // ...
        case JCTree.NULLCHK:
            result = od.load();
            code.emitop0(dup);
            genNullCheck(tree.pos());
            break;
        }
}

其中的genNullCheck()是:
/** Generate a null check from the object value at stack top. */
private void genNullCheck(DiagnosticPosition pos) {
    callMethod(pos, syms.objectType, names.getClass,
               List.<Type>nil(), false);
    code.emitop0(pop);
}

也就是说那个对getClass()的调用只不过是借invokevirtual指令来帮忙做null检查而已。getClass()本身得到的值其实是没用到的。

这个行为在Java语言规范里有相应的规定。在Java语言规范第三版,
15.9.2 Determining Enclosing Instances
  该小节规定了应该使用什么对象作为外部类的实例
15.9.3 Choosing the Constructor and its Arguments
  该小节规定了外部类实例在参数列表中的位置
15.9.4 Run-time Evaluation of Class Instance Creation Expressions
  该小节规定了上面提到的空指针检查的行为:
Java Language Specification, 3rd Edition 写道
At run time, evaluation of a class instance creation expression is as follows.
First, if the class instance creation expression is a qualified class instance creation expression, the qualifying primary expression is evaluated. If the qualifying expression evaluates to null, a NullPointerException is raised, and the class instance creation expression completes abruptly. If the qualifying expression completes abruptly, the class instance creation expression completes abruptly for the same reason.


规范里明确了“要抛出NullPointerException”的行为,至于是如何实现null检查的则没规定,可以由实现自由发挥。用普通的if...else来做这个检查当然也可以,只不过生成的字节码就比调用getClass()的办法更长一些。

====================================================================

无独有偶,ECJ(Eclipse Compiler for Java)在编译这种代码的时候同样会生成对getClass()方法的调用:
public static void main(java.lang.String[]);
  Code:
   Stack=3, Locals=2, Args_size=1
   0:   new     #1; //class Outer
   3:   dup
   4:   invokespecial   #13; //Method "<init>":()V
   7:   astore_1
   8:   new     #14; //class Outer$Inner
   11:  aload_1
   12:  dup
   13:  invokevirtual   #16; //Method java/lang/Object.getClass:()Ljava/lang/Class;
   16:  pop
   17:  invokespeci8al   #20; //Method Outer$Inner."<init>":(LOuter;)V
   20:  return
  LineNumberTable:
   line 4: 0
   line 5: 8
   line 6: 20

这当然不是偶然,因为getClass()方法是在Object上声明的(因此所有对象上必然存在),而且是final的(保证了它有确定的行为),而且运行开销比较低。
同样是Object上声明的方法,toString()、hashCode()之类其实也可以用,但它们都不是final的,有潜在可能性会引发较大的运行开销;这么分析一圈下来,Object上最好用的就剩下getClass()了。
  • 大小: 41.9 KB
分享到:
评论

相关推荐

    无人机图像目标检测.zip

    目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行

    数据结构《数据结构》经典算法代码.zip

    【数据结构】《数据结构》经典算法代码.zip 栈 待更新 队列 括号匹配(搞定) 用栈实现递归式的非递归代码P90 树 的考察在于各种树的特点,以及树的遍历算法 先序 递归 非递归 后序 递归 非递归 中序 递归 非递归 层序 线索化 求二叉树的高度 递归 非递归 平衡二叉树 判断一棵树是否为平衡二叉树 (2019-9-6)

    Microsoft Remote Desktop for mac

    Microsoft Remote Desktop for mac

    Axelrod-1.10.0-py2.py3-none-any.whl.zip

    Axelrod-1.10.0-py2.py3-none-any.whl.zip

    支持易码支付的聚合支付最新破解去后门源码.zip

    最新源支付系统源码 ∨7版全开源 免授权 附详细撘建教程 站长亲测YPay是专为个人站长打造的聚合免签系统,拥有卓越的性能和丰富的功能。它采用全新轻量化的界面U!,让您能更方便快捷地解决知识付费和运营赞助的难题。同时,它基于高性能的thinkphp 6.1.4+layui2.9.3+PearAdmin架构,提供实时监控和管理功能,让您随时随地掌握系统运营情况。 运行环境 Nignx/Apache/llS PHP 8.1 Mysql 5.6 ~ 5.7 Redis Supervison

    基于JSP的图书管理系统.zip

    基于JSP的图书管理系统 项目简介 本项目是一个基于JSP的图书管理系统,旨在提供一个简单的图书管理解决方案。系统包含了图书信息管理、借阅信息管理、读者信息管理以及用户认证等核心功能。 功能模块 图书信息管理 book.java: 定义了一个图书对象的属性及其对应的getter和setter方法。包含图书编号、图书名称、作者、出版社、价格、类型编号和备注等属性。 借阅信息管理 borrow.java: 表示借款信息相关的Bean,包含借款编号、图书编号、借款日期、还款日期、实际还款日期、借款金额和借款状态等属性。 读者信息管理 Reader.java: 代表一个读者对象,包含读者编号、姓名、性别、部门、联系电话、身份证号码、读者类型、创建时间和状态等属性。 用户认证 USER.java: 简单的用户信息类,包含用户名和密码两个属性,并提供对应的getter和setter方法。 字符编码过滤 CharFilter.java: 一个Servlet过滤器,用于处理字符编码问题,确保请求中的字符数据以UTF8编码进行处理。 技术栈 编

    Axelrod-1.17.1-py2.py3-none-any.whl.zip

    Axelrod-1.17.1-py2.py3-none-any.whl.zip

    基于yolov8的西红柿缺陷检测系统python源码+onnx模型+评估指标曲线+精美GUI界面.zip

    【测试环境】 windows10 anaconda3+python3.8 torch==1.9.0+cu111 ultralytics==8.2.70 【模型可以检测出类别】 Bad Good Unripe 更多实现细节参考博文:https://blog.csdn.net/FL1623863129/article/details/141931442

    基于Spring Boot框架的OA管理系统.zip

    基于Spring Boot框架的OA管理系统 项目简介 本项目是一个基于Spring Boot框架开发的OA管理系统,专注于提供高效、稳定的企业办公自动化解决方案。系统集成了用户管理、安全控制、数据处理和报表生成等功能,旨在提升企业内部管理效率和数据处理能力。 主要功能 用户管理 用户信息管理通过UserInfo类管理用户的基本信息,包括账号、姓名、密码、电子邮件等。 用户状态管理支持用户账号的启用和禁用,以及用户信息的删除标记。 安全控制 登录认证通过SecurityController实现用户登录功能,包括账号验证、密码检查和状态判断。 数据访问控制通过SecurityMapper和SecurityService实现用户信息的数据库查询和业务逻辑处理。 数据处理 分页处理通过PageUtils工具类实现数据的分页显示,支持动态页码处理。 Excel报表生成通过ExcelUtils工具类生成Excel报表,支持自定义表头和内容。 错误处理 API响应处理通过BitResult类统一处理API调用的结果,包括操作成功与否、数据和错误信息

    基于Spring Boot框架的微信课程学习系统.zip

    基于Spring Boot框架的微信课程学习系统 项目概述 本项目是一个基于Spring Boot框架开发的微信课程学习系统,专注于提供丰富的课程资源和学习互动功能。系统集成了微信消息处理、课程管理、用户签到、试卷管理等多个模块,旨在为学生和教师提供一个高效、便捷的学习和教学环境。 功能模块 微信消息处理 基础事件处理处理微信事件的基本信息,如消息的发送方和接收方、消息创建时间、消息类型和事件类型等。 地理位置事件存储和处理用户的地理位置信息,用于签到和位置相关的功能。 菜单事件处理微信自定义菜单相关的事件,如点击菜单按钮后的响应。 二维码事件处理扫描二维码相关的事件,包括事件的标识和换取二维码所需的票据。 关注事件处理微信中的关注或取消关注事件。 课程管理 资源上传教师可以上传课程资源,如文档、视频等。 课程论坛学生和教师可以在课程论坛中进行讨论和互动。 申请入班学生可以申请加入班级,教师可以审核学生的申请。 用户签到 签到功能学生可以通过系统进行签到,记录签到时间和位置。 签到详情教师可以查看学生的签到详情,包括签到时间和位置。

    IMG_20240905_192349.jpg

    IMG_20240905_192349.jpg

    Avanza-0.0.11-py3-none-any.whl.zip

    Avanza-0.0.11-py3-none-any.whl.zip

    基于C++实现的Linux的 Shell 程序+项目开发文档,实现基本的命令执行功能,支持 I/O 重定向和管道操作

    本项目旨在开发一个用于 Linux 的 Shell 程序,利用 C++ 标准库和 Linux 系统调用来实现。该 Shell 程序提供了基本的命令执行功能,支持 I/O 重定向和管道操作,并实现了一些内置命令

    使用OpenCV部署yolov5-pose目标检测+人体姿态估计,包含C++和Python两个版本的程序

    。支持yolov5s,yolov5m,yolov5l.zip目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行

    byoc框架中代码生成部分文档

    byoc框架中代码生成部分文档,包括生成C代码和JSON格式

    2024-2030东南亚与中国废旧轮胎回收(热裂解部分)市场现状及未来发展趋势 Sample wyl.pdf

    QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。

    wireshark-4.2.4

    wireshark-4.2.4

    yolov4-tiny版本的目标检测及可视化界面设计.zip

    目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行

    资源数据 (2qssxcx

    qss

    Access client

    Access client

Global site tag (gtag.js) - Google Analytics