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的实例。

对于这种情况,笔者找到了一个资料,可供参考:

Java动态修改Enum实例


完。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java 源码分析

数据结构Stack

​ 在很多应用中,我们需要维护多个对象的集合,这种操作非常简单。我们可能想要向集合中 加入某个元素,去掉某个元素,以及遍历 集合中的元素并对他们执行某种操...

34560
来自专栏信数据得永生

JavaScript 编程精解 中文第三版 五、高阶函数

319100
来自专栏iOSer成长记录

iOS-Strong/Copy修饰词

11930
来自专栏Jimoer

数据结构:数组、链表、栈、队列的理解

解释定义 数据结构: 数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。再简单描述一下:数据结构就是描述对象间逻辑关系的学科。 如果还是不太清楚下面会...

544100
来自专栏程序你好

C# 7.3新特性一览

11430
来自专栏张善友的专栏

C# 4.0 Optional Parameters 和Named Parameters

Optional Parameters 是C# 4.0的特色之一,可减少重载函数的数量,却可达到相同的效果,加快开发效率。在使用上就跟C++一样,只需用等号为函...

22070
来自专栏用户2442861的专栏

json格式

  1. “名称/值”对的集合(A collection of name/value pairs)。不同的语言中,它被理解为对象(object),记录(reco...

34620
来自专栏求索之路

Effective Java笔记(不含反序列化、并发、注解和枚举)

最近把Effective Java复习了一遍,其中有比较多的java最佳实践可以在平时编程中用到。反序列化、并发、注解和枚举这四章没看,并发这本书里讲的比较简...

381110
来自专栏冰霜之地

JavaScript 新手的踩坑日记

在1995年5月,Eich 大神在10天内就写出了第一个脚本语言的版本,JavaScript 的第一个代号是 Mocha,Marc Andreesen 起的这个...

10720
来自专栏黑泽君的专栏

【Java面试复习经典】传智播客Java就业班入学测试题及答案解析(2012年版)

  共50道题,每道题2分,总分100分,80分为合格。   注意,题目有多选,也有单选。请认真作答。

20530

扫码关注云+社区

领取腾讯云代金券