java issynthetic_Java synthetic

2550ddd0bfca848c243b94f3bef6cf2c.png

点击上方「蓝字」关注我们

7f974defcc26def8cfddff2f99accdd8.png

读完这篇文章你将会收获到

synthetic fields

synthetic method

synthetic class

概述

上一篇 Java 枚举 提及到编译成 class 文件之后、编译器会在枚举类里帮我们生成一个 VALUES 的静态数组 , 这种编译器生成的都有一个 flag 叫做 synthetic

f7e8ce8b9355dbfe48f628f361930ec4.png

那么 synthetic 的定义是什么、什么情况下才会有这个 flag ?

由编译器生成的,在源代码中没有出现的,都会被标记为 synthetic。当然有一些例外的情况:默认的构造函数、类的初始化方法、以及枚举类中的 value 和 valueOf 方法

synthetic fields

非常常见的一个例子

public class Father{

class Son{

}

}

我们都知道在一个内部类中,可以直接访问外部类的属性和方法,因为在内部类中是存在一个外部类的一个引用变量,而这个引用变量即是编译器帮我们生成的、也就是一个 synthetic 的属性

80aaa9deb400989a88562b5749ddf522.png

我们再写一个测试来验证下

Class clazz = Father.Son.class;

Field[] declaredFields = clazz.getDeclaredFields();

for (Field declaredField : declaredFields) {

System.out.println(declaredField.getName() + ":" + declaredField.isSynthetic());

}

this$0:true

我们再来验证一下上一篇文章 Java 枚举 的枚举类

Class clazz = BehaviorEnum.class;

Field[] declaredFields = clazz.getDeclaredFields();

for (Field declaredField : declaredFields) {

System.out.println(declaredField.getName() + ":" + declaredField.isSynthetic());

}

FOLLOW:false

WOW:false

FORWARD_TO_FRIENDS:false

ADD_TO_FAVORITES:false

$VALUES:true

synthetic method

我们再来看看被 synthetic 修饰的方法吧

public class Father{

class Son{

private String name;

}

/**

* just for test synthetic

* @return

*/

public String getName(){

// just for test synthetic

return new Son().name;

}

}

b7fdaa8c0299d3e9b0dff53a4fa5a8d6.png

在 Son 类中突然多出了这么一个方法。因为其实 name 属性是一个私有方法、外部类 Father 中却能直接访问这个属性、对于我们写代码来说、这是非常合理的一个事情、但是这都是编译器默默的付出、为我们生成了一个静态的 package 范围的方法、参数就是 Son 的实例、返回值就是 String

Class sonClass = Father.Son.class;

Method[] declaredMethods = sonClass.getDeclaredMethods();

for (Method declaredMethod : declaredMethods) {

System.out.println(

Modifier.toString(declaredMethod.getModifiers()) +

":" + declaredMethod.getName() +

":" + declaredMethod.isSynthetic());

}

static:access$000:true

synthetic class

我们再来看看被 synthetic 修饰的 class

public class Father03{

public Son generateSon(){

return new Son();

}

private class Son{

}

}

然后我们编译为 class 文件

7761949e551ce368097719e93621dc05.png

发现多出来一个匿名类 Father03$1.class 这个是什么鬼鬼

b301cdf9c9b65368a6dd4fb101713505.png

这个类完全是一个空的类、父类直接是 Object、也没额外定义一些自己的方法

我们再看看 Father03$Son.class

d4a9319887c43c2947f679f527d1008a.png

发现它居然有两个构造方法,一个带参数的 package scope 的构造参数是编译器生成的。参数是 Father03 和 Father03$1

我们再看看 Father03 里面的 generateSon 方法

681342325d4e46d56d1f005cbb311372.png

发现它调用的是那个带参数的构造方法,并且参数 Father03$1 的值是为 null 的

根据上面的种种信息来看、我们可以这么认为、对于一个 private 的内部类(其构造函数默认也是 private ) , 外部类也是无法直接去创建它的实例的、其实换句话来说、对于类的定义来说、不管你是作为一个内部类定义在另一个类中、还是单独定义在一个 java 文件,java 的可见性都是起效的。至于为啥可以在外部内直接创建一个 private 的类的实例、无外乎就是 java 编译器帮我们做了一些额外的工作。

回到上面的例子中、因为 Father03$Son. 只有一个私有的构造函数、而为了能在 Father03 中去创建这么一个 Father03$Son 对象,编译器不得不为我们生成一个 package scope 的构造函数、而午餐的构造函数已经存在了、那编译器只能创建一个有参的构造函数啊、那么问题来了、这个参数的类型应该是啥、那就生成一个类呗、专门为这个参数用。而调用这个构造函数的时候、就直接传给 null 值给它

所以说 Father03$1 作用无外乎可能就是作为一个参数的类型被用到

相关文章

这次一定?

群聊

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值