java 当我们定义枚举时,原来JVM编译器为我们做了这么多

本文详细探讨了Java中枚举的实现原理,包括其线程安全性、序列化特性及反编译后的内部结构。通过实际代码示例展示了枚举如何简化编程,并分析了其在多线程环境下的表现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

//当我们写下下列代码时:
public enum DataSourceEnum {
    DATASOURCE;
}  

使用java自带的反编译工具: 

在命令行 中输入  javap -c DataSourceEnum.class 进行字节码文件查看,结果如下

Compiled from "DataSourceEnum.java"
public final class DataSourceEnum extends java.lang.Enum<DataSourceEnum> {
  public static final DataSourceEnum DATASOURCE;

  public static DataSourceEnum[] values();
    Code:
       0: getstatic     #1                  // Field $VALUES:[LDataSourceEnum;
       3: invokevirtual #2                  // Method "[LDataSourceEnum;".clone:()Ljava/lang/Object;
       6: checkcast     #3                  // class "[LDataSourceEnum;"
       9: areturn

  public static DataSourceEnum valueOf(java.lang.String);
    Code:
       0: ldc           #4                  // class DataSourceEnum
       2: aload_0
       3: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
       6: checkcast     #4                  // class DataSourceEnum
       9: areturn

  static {};
    Code:
       0: new           #4                  // class DataSourceEnum
       3: dup
       4: ldc           #7                  // String DATASOURCE
       6: iconst_0
       7: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
      10: putstatic     #9                  // Field DATASOURCE:LDataSourceEnum;
      13: iconst_1
      14: anewarray     #4                  // class DataSourceEnum
      17: dup
      18: iconst_0
      19: getstatic     #9                  // Field DATASOURCE:LDataSourceEnum;
      22: aastore
      23: putstatic     #1                  // Field $VALUES:[LDataSourceEnum;
      26: return
}

 使用反编译命令行工具jad 查看

输入jad -s java DataSourceEnum.class

得到提示:

Parsing DataSourceEnum.class... Generating DataSourceEnum.java

表示jad会将字节码文件,反向生成java文件,得到的java文件如下:

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   DataSourceEnum.java


public final class DataSourceEnum extends Enum
{

    public static DataSourceEnum[] values()
    {
        return (DataSourceEnum[])$VALUES.clone();
    }

    public static DataSourceEnum valueOf(String s)
    {
        return (DataSourceEnum)Enum.valueOf(DataSourceEnum, s);
    }

    private DataSourceEnum(String s, int i)
    {
        super(s, i);
    }

    public static final DataSourceEnum DATASOURCE;
    private static final DataSourceEnum $VALUES[];

    static 
    {
        DATASOURCE = new DataSourceEnum("DATASOURCE", 0);
        $VALUES = (new DataSourceEnum[] {
            DATASOURCE
        });
    }
}

由此可见java 中的枚举使用了语法糖,为我们省去的大量代码

所谓 语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家 Peter.J.Landin 发明的一个术语,指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是但是更方便程序员使用。只是在编译器上做了手脚,却没有提供对应的指令集来处理它。

由反编译后的代码可知,DATASOURCE 被声明为 static 的,根据类加载过程,可以知道虚拟机会保证一个类的初始化 方法在多线程环境中被正确的加锁、同步。所以,枚举实现是在实例化时是线程安全。

 

接下来看看序列化问题:

 

Java规范中规定,每一个枚举类型极其定义的枚举变量在JVM中都是唯一的,因此在枚举类型的序列化和反序列化上,Java做了特殊的规定。

在序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过 java.lang.Enum 的 valueOf() 方法来根据名字查找枚举对象。

也就是说,以下面枚举为例,序列化的时候只将 DATASOURCE 这个名称输出,反序列化的时候再通过这个名称,查找对于的枚举类型,因此反序列化后的实例也会和之前被序列化的对象实例相同。

 

当然在jdk1.8 中 如果对枚举类进行反射会跑出异常,也就是说对枚举类进行反射时连类装载的入口方法<init>()都没有了

java.lang.NoSuchMethodException: com.lin.Person$Singleton.<init>()
	at java.lang.Class.getConstructor0(Class.java:3074)
	at java.lang.Class.getDeclaredConstructor(Class.java:2170)
	at com.lin.Person.main(Person.java:30)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值