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方法的情况相关推荐

  1. java 几种生成海报的方式

    生成方式的util已经在另一篇文章里说过啦,这篇文章要参考这以下的链接才能看得懂 java生成海报并保存-包括微信小程序带场景值的二维码 1.第一种是生成base64的格式这种也是比较常见的 @Api ...

  2. JAVA三种多线程实现方法和应用总结

    最近在做代码优化时学习和研究了下JAVA多线程的使用,看了菜鸟们的见解后做了下总结. 1.JAVA多线程实现方式 JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用E ...

  3. JAVA三种取整方法

    方法一:向上取整Math.ceil(); 举例:Math.ceil(11.4)=12; Math.ceil(-11.6)=-11; 方法二:向下取整Math.floor(); 举例:Math.floo ...

  4. Java案例:编译器生成桥方法

    泛型类继承时会产生类型擦除现象,导致多态失败.为了解决类型擦除与多态的冲突,Java编译器通过生成桥方法来解决这个问题. package net.hw.poly;import java.time.Lo ...

  5. matlab 遍历每个像素点,Mat中两种像素遍历方法比较

    小白,入门中,不足其指正.刚刚接触opencv,从一个Matlab风格的编程环境突然跳转到C++,实在有些不适.单就pixels scanning花了好长时间研究.opencv-tutorials给出 ...

  6. 【Java基础】 随机数的3种生成方法

    [Java基础]之3种随机数的生成方法 目录 前言: 第一种:new Random(): 第二种:Math.random(); 第三种:currentTimeMillis(): 前言: 生成指定范围内 ...

  7. java常见的ide_在三个Java IDE中生成的三种常见方法

    java常见的ide 在本文中,我研究了NetBeans 8.0.2 , IntelliJ IDEA 14.0.2和Eclipse Luna 4.4.1生成的三种"通用"方法[ e ...

  8. 在三个Java IDE中生成的三种常见方法

    在本文中,我研究了NetBeans 8.0.2 , IntelliJ IDEA 14.0.2和Eclipse Luna 4.4.1生成的三种"通用"方法[ equals(Objec ...

  9. java 序列化 文件_一种恢复Java序列化文件数据的方法与流程

    本发明涉及信息安全技术领域,特别涉及一种恢复Java序列化文件数据的方法. 背景技术: 在数据解析恢复领域经常会遇到序列化文件的解析.Java序列化是Java 自身提供的一种数据序列化方式,它允许开发 ...

最新文章

  1. nohup不输出日志信息的方法,及linux重定向学习
  2. boost::grid_graph用法的测试程序
  3. mysql 无法处理非法数据_MySQL 处理非法数据
  4. 深度学习这么调参训练_深度学习训练的小技巧,调参经验(转)
  5. PHP经典项目案例-(一)博客管理系统5
  6. SpringBoot 集成 layering-cache 实现两级缓存调研与实践
  7. oracle not in 数组,慎用Oracle的not in (轉)
  8. Ubuntu 磁盘自动挂载解决
  9. nginx config的多个config配置
  10. windows server 2008 搭建文件共享服务
  11. 组态王登录服务器为空,组态王客户端与服务器失去联系
  12. Appium desktop下载安装
  13. 十行python代码定时给微信好友发送晚安,自动应答--python云舔狗
  14. MybatisPlus中乐观锁的配置
  15. DNS和BIND总结
  16. HENGSHI SENSE 4.0 预置明道云连接器,实现更灵活的数据自动传输
  17. python爬虫代码示例分享
  18. cs231n-2022-assignment2#Q1:多层全连接神经网络
  19. Qt 窗口操作函数(置顶、全屏,最大化最小化按钮设置等)
  20. 智力大冲浪 【贪心】

热门文章

  1. php怎么取整数,js怎么取整数
  2. HTML+CSS+JS网页设计期末课程大作业——多用途的图文展示博客HTML模板(16页) 学生DW网页设计作业成品 web课程设计网页规划与设计 计算机毕设网页设计源码...
  3. 【react】配置React 的开发环境
  4. word免费转为pdf怎么转,分享这几个方法给大家!
  5. 西门子博途编程-模拟量批量处理一
  6. Eigen之矩阵与向量的乘积
  7. 基于Pytorch的深度学习激励函数总结
  8. 机器视觉-简单入门小例子
  9. codesys com库_WAGO CODESYS库文件保证系统顺畅运行
  10. C++11 中的 emplace