Java 中冷门的 synthetic 关键字原理解读
看JAVA的反射时,看到有个synthetic ,还有一个方法isSynthetic() 很好奇,就了解了一下:
1.定义
Any constructs introduced by a Java compiler that do not have a corresponding construct in the source code must be marked as synthetic, except for default constructors, the class initialization method, and the values and valueOf methods of the Enum class.
大意为:由java编译器生成的(除了像默认构造函数这一类的)方法,或者类
2.实例
既然知道synthetic方法和synthetic类是由编译器生成的,那到底编译器会怎么生成这些东西,又在什么情况下会生成这些东西呢?
先看一段代码:
import static java.lang.System.out;
public final class DemonstrateSyntheticMethods
{
public static void main(final String[] arguments)
{
DemonstrateSyntheticMethods.NestedClass nested =
new DemonstrateSyntheticMethods.NestedClass();
out.println("String: " + nested.highlyConfidential);
}
private static final class NestedClass
{
private String highlyConfidential = "Don't tell anyone about me";
private int highlyConfidentialInt = 42;
private Calendar highlyConfidentialCalendar = Calendar.getInstance();
private boolean highlyConfidentialBoolean = true;
}
}
编译之后,可以看到三个文件:
其中,最下面的这个类文件很好解释,就是我们的主class,中间的文件,是我们的内部类,上面的文件,后面再讲,我们先看一下中间这个内部类
2.1 内部类的反编译结果
用javap 反编译DemonstrateSyntheticMethods$NestedClass.class,得到如下结果:
javap DemonstrateSyntheticMethods$NestedClass.class
Compiled from "DemonstrateSyntheticMethods.java"
final class DemonstrateSyntheticMethods$NestedClass {
DemonstrateSyntheticMethods$NestedClass(DemonstrateSyntheticMethods$1);
static java.lang.String access$100(DemonstrateSyntheticMethods$NestedClass);
}
先把构造函数放一边,我们来看这个标黑的方法access$100 这个是怎么回事呢?我们的源文件里找不到这个access方法啊?
2.2 synthetic方法
这个方法就是编译器生成的synthetic方法,读者不信的话,可以用method.isSynthetic() 去验证一下。
为何要生成这样一个方法呢?
可以看到,我们的NestedClass类中,highConfidential是一个私有属性,而我们在外部类DemonstrateSyntheticMethods中,直接引用了这个属性。作为一个内部类,NestedClass的属性被外部类引用,在语义上毫无问题,但是这却苦了编译器。
为了能让一个private的变量被引用到,编译器生成了一个package scope的access方法,这个方法就是一个get方法,在外部类使用highConfidential这个属性时,实际是使用了这个access方法。
在javap中可以看到直接的证据:
图中红框的位置,可以很清楚的看到main方法实际上调用了access$100这个方法。
所以,结论很清楚了,编译器为了方便内部类的私有成员被外部类引用,生成了一个get方法,这可以被理解为一个trick,绕开了private成员变量的限制。
2.3 synthetic类
定义已经提到,编译器不仅仅会生成方法,也会生成synthetic类。
我们回过头来看2.1提到的最后一个类DemonstrateSyntheticMethods$1.class
这个类是一个完全的空类,反编译后是这个样子:
// $FF: synthetic class
class DemonstrateSyntheticMethods$1 {
}
这个类只出场了一次,作为内部类NestedClass的package scope的构造函数,如图所示:
那么,这个类的作用呢?笔者查了很多资料,都没有明确的说明这个类的用途,只能根据代码做推测如下:
NestedClass作为一个private类,其默认构造函数也是private的。那么,事实上,作为外部类的DemonstrateSyntheticMethods类,没有办法new这个内部类的对象,而这和我们需要的语义相违背。
那么,为了实现语义,编译器又用了一个trick,悄悄的生成了一个构造函数NestedClass(DemonstrateSyntheticMethods$1 obj), 这个构造函数是包可见的。
那么,外部类则可以通过new NestedClass(null)的方式,得到内部类的对象。如果读者检查一下main方法的话,可以看到这个方法的调用如下图所示。
这就是这个synthetic类的作用。如果我们给我们的NestedClass 增加一个public级别的默认构造函数的话,则可以看到编译器不会再生成这个synthetic类。
3.结论
编译器通过生成一些在源代码中不存在的synthetic方法和类的方式,实现了对private级别的字段和类的访问,从而绕开了语言限制,这可以算是一种trick。
在实际生产和应用中,基本不存在程序员需要考虑synthetic的地方。
PS: 在此提一个的常见的存在synthetic的案例。
如果同时用到了Enum和switch,如先定义一个enum枚举,然后用switch遍历这个枚举,java编译器会偷偷生成一个synthetic的数组,数组内容是enum的实例。
对于这种情况,笔者找到了一个资料,可供参考:
完。
- 使用Visual Studio 调试断点不起作用的问题解决办法 调试Revit CAD 不能进入断点
- openFileDialog的Filter属性设置
- dedecms调用当前栏目的子栏目怎么操作
- dedecms如何增加自定义字段
- dedecms如何快速删除跳转的文章(记得清空内容回收站)
- Javascript构造函数
- 帝国cms调用栏目自定义字段(栏目简介)如何操作
- dedecms手机站图片错误的解决方法
- 可以搜索到DedeCms后台文章列表文档id吗?或者快速定位id编辑文章
- 常用工具整理
- jQuery 获取对象 根据属性、内容匹配, 还有表单元素匹配
- 如何修改dedecms专题目录默认名称special
- 普里姆(Prim)算法
- dedecms用keyword标签调用含有某一关键词的文章
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- 要一遍做对——LeetCode题目20:有效的括号
- 双指针算法练习(一)
- 一般是面试的热身题——LeetCode题目21:合并两个有序链表
- LeetCode题目22:括号生成
- OpenGL ES 3.0 | 着色器编译器
- LeetCode题目23:合并K个排序链表
- LeetCode题目24:两两交换链表中的节点
- 最难链表题——LeetCode题目25:K 个一组翻转链表
- LeetCode题目26:删除排序数组中的重复项
- PCA算法原理及实现
- LeetCode题目27:移出元素
- LeetCode题目28:实现strStr()
- LeetCode题目29:两数相除
- LeetCode题目30:串联所有单词的子串
- LeetCode题目31:下一个排列