Java几种生成synthetic方法的情况
Java编译器将Java代码编译成过程中,会由编译器引入一些类、方法、属性,这些由编译器引入一些类、方法、属性会被标上synthetic字段。本文主要介绍几种编译器会生成synthetic方法的情况
1、泛型
子类在具象化泛型接口、泛型父类的泛型时,由于方法签名不一致,运行时动态分派将会查找不到重写的方法,为解决此问题,编译器会生成一个synthetic bridge方法
public class Base<T> {public void f(T t) {System.out.println("Base");}
}public class Sub extends Base<String> {@Overridepublic void f(String s) {System.out.println("Sub");}
}public class Main {public static void main(String[] args) {Base<String> base = new Sub();base.f("123");}
}
// 输出Sub
Base类编译后,由于泛型擦除,f方法签名将变为f(Ljava/lang/Object;)V,即入参为Object类型。Sub类不存在泛型,所以编译后,f方法的签名将变为f(Ljava/lang/String;)V。由于这两个方法签名不同,Sub#f方法并未真正意义上重写Base#f方法。这就存在一个问题,main方法里调用Base#f时,由于泛型擦除和静态分派,调用方法的签名是f(Ljava/lang/Object;)V;运行时动态分派根据实际类型去查找f(Ljava/lang/Object;)V方法,而Sub里并不存在该方法,顺着类继承层次会从父类中查找到该方法,从而调用父类方法,子类重写的方法失效。
为了解决这个问题,编译器在子类Sub中生成一个签名为f(Ljava/lang/Object;)V的方法,这个方法调用重写的f(Ljava/lang/String;)V方法。
用jclasslib插件查看Base类f方法编译出来的字节码
用jclasslib插件查看Sub类f方法编译出来的字节码,存在两个f方法。重写的f方法
编译器生成的f方法
2、子类重写方法改变方法签名
如上是泛型导致的方法签名不一致,编译器生成一个synthetic bridge方法。子类在重写父类方法时,可以修改方法的返回值,也会导致啊方法签名不一致,也需要编译器生成一个synthetic bridge方法。
public class Base {public Number f() {return 1.11;}
}public class Sub extends Base {@Overridepublic Integer f() {return 1;}
}public class Main {public static void main(String[] args) {Base base = new Sub();System.out.println(base.f());}
}
// 输出1
跟上面类似
3、可见性问题
由于反射设计问题,反射的方法访问权限控制与JVM访问权限控制存在差异,导致能直接调用的方法通过反射调用却存在问题,为解决此种不一致,编译器引入synthetic方法。
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6342411
// package org.example.refl.a
class Base {public void f() {System.out.println("Base");}
}
public class Sub extends Base {}// package org.example.refl
public class Main {public static void main(String[] args) {Sub sub = new Sub();sub.f();}
}// package org.example.refl
public class Main {public static void main(String[] args) throws Exception {Method f = Sub.class.getMethod("f");f.invoke(new Sub());}
}
org.example.refl.a包下面定义包可见的Base类和public可见的Sub类。Base类的f方法对Main类不可见,但对Sub可见;Sub类对Main类可见。 通过方法调用和反射两种方式调用Sub#f方法。
直接方法调用时,main方法编译成如下
invokevirtual的方法访问权限控制涉及三个阶段:验证、解析、执行阶段,本节问题只涉及解析阶段中的方法解析。解析就是将符号引用转换成常量池中的具体值引用。 方法解析将reference method解析为resolved method。reference method即invokevirtual指令操作数指定的方法,resolved method即常量池中的某个方法。方法解析首先按照类继承层次根据符号引用查找方法,然后校验权限。由于符号引用指定的方法可能不存在(继承父类获得的方法),导致reference method解析为resolved method不是同一个方法,但是校验权限仍按照reference method校验,所以Main类中可以调用org/example/refl/a/Sub.f。
通过反射调用方法时,实际上需要reflect包去实现JVM同样的权限校验逻辑,此时存在了不一致。由于Sub类没有f方法,Sub.class.getMethod(“f”)实际上会获得到Base#f方法,此时校验权限则不通过,因为Main中不能访问Base#f方法。为解决此问题,编译器在Sub类中加入了synthetic方法,在这个方法中调用Base#f方法。此时Sub.class.getMethod(“f”)拿到的方法是这个synthetic方法,不存在访问权限问题。
Java几种生成synthetic方法的情况相关推荐
- java 几种生成海报的方式
生成方式的util已经在另一篇文章里说过啦,这篇文章要参考这以下的链接才能看得懂 java生成海报并保存-包括微信小程序带场景值的二维码 1.第一种是生成base64的格式这种也是比较常见的 @Api ...
- JAVA三种多线程实现方法和应用总结
最近在做代码优化时学习和研究了下JAVA多线程的使用,看了菜鸟们的见解后做了下总结. 1.JAVA多线程实现方式 JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用E ...
- JAVA三种取整方法
方法一:向上取整Math.ceil(); 举例:Math.ceil(11.4)=12; Math.ceil(-11.6)=-11; 方法二:向下取整Math.floor(); 举例:Math.floo ...
- Java案例:编译器生成桥方法
泛型类继承时会产生类型擦除现象,导致多态失败.为了解决类型擦除与多态的冲突,Java编译器通过生成桥方法来解决这个问题. package net.hw.poly;import java.time.Lo ...
- matlab 遍历每个像素点,Mat中两种像素遍历方法比较
小白,入门中,不足其指正.刚刚接触opencv,从一个Matlab风格的编程环境突然跳转到C++,实在有些不适.单就pixels scanning花了好长时间研究.opencv-tutorials给出 ...
- 【Java基础】 随机数的3种生成方法
[Java基础]之3种随机数的生成方法 目录 前言: 第一种:new Random(): 第二种:Math.random(); 第三种:currentTimeMillis(): 前言: 生成指定范围内 ...
- java常见的ide_在三个Java IDE中生成的三种常见方法
java常见的ide 在本文中,我研究了NetBeans 8.0.2 , IntelliJ IDEA 14.0.2和Eclipse Luna 4.4.1生成的三种"通用"方法[ e ...
- 在三个Java IDE中生成的三种常见方法
在本文中,我研究了NetBeans 8.0.2 , IntelliJ IDEA 14.0.2和Eclipse Luna 4.4.1生成的三种"通用"方法[ equals(Objec ...
- java 序列化 文件_一种恢复Java序列化文件数据的方法与流程
本发明涉及信息安全技术领域,特别涉及一种恢复Java序列化文件数据的方法. 背景技术: 在数据解析恢复领域经常会遇到序列化文件的解析.Java序列化是Java 自身提供的一种数据序列化方式,它允许开发 ...
最新文章
- nohup不输出日志信息的方法,及linux重定向学习
- boost::grid_graph用法的测试程序
- mysql 无法处理非法数据_MySQL 处理非法数据
- 深度学习这么调参训练_深度学习训练的小技巧,调参经验(转)
- PHP经典项目案例-(一)博客管理系统5
- SpringBoot 集成 layering-cache 实现两级缓存调研与实践
- oracle not in 数组,慎用Oracle的not in (轉)
- Ubuntu 磁盘自动挂载解决
- nginx config的多个config配置
- windows server 2008 搭建文件共享服务
- 组态王登录服务器为空,组态王客户端与服务器失去联系
- Appium desktop下载安装
- 十行python代码定时给微信好友发送晚安,自动应答--python云舔狗
- MybatisPlus中乐观锁的配置
- DNS和BIND总结
- HENGSHI SENSE 4.0 预置明道云连接器,实现更灵活的数据自动传输
- python爬虫代码示例分享
- cs231n-2022-assignment2#Q1:多层全连接神经网络
- Qt 窗口操作函数(置顶、全屏,最大化最小化按钮设置等)
- 智力大冲浪 【贪心】
热门文章
- php怎么取整数,js怎么取整数
- HTML+CSS+JS网页设计期末课程大作业——多用途的图文展示博客HTML模板(16页) 学生DW网页设计作业成品 web课程设计网页规划与设计 计算机毕设网页设计源码...
- 【react】配置React 的开发环境
- word免费转为pdf怎么转,分享这几个方法给大家!
- 西门子博途编程-模拟量批量处理一
- Eigen之矩阵与向量的乘积
- 基于Pytorch的深度学习激励函数总结
- 机器视觉-简单入门小例子
- codesys com库_WAGO CODESYS库文件保证系统顺畅运行
- C++11 中的 emplace