java枚举类是怎么初始化的,为什么说枚举类是线程安全的

今天写枚举类的时候发现了一个有趣的现象,在这里分享一下:

首先我们定义一个简单的枚举类:

/**
 * @author jinghuaixin
 * @date 2020/04/30
 */
public enum Week {

    Monday, Tuesday;
    // 静态代码块
    static {
        System.out.println("枚举中静态代码块执行了");
    }

    // 构造方法
    private Week() {
        System.out.println("枚举中构造方法执行了");
    }

    // 构造代码块
    {
        System.out.println("枚举中代码块执行了");
    }
}

然后定义一个简单的测试类:

public class TestEnum {

    static {
        System.out.println("普通类中静态代码块执行了");
    }
    {
        System.out.println("普通类中代码块执行了");
    }

    public TestEnum() {
        System.out.println("普通类的构造方法执行了");
    }

    public static void main(String[] args) {
        new TestEnum();
        System.out.println("+++++++++++++++++++++++++++");
        new TestEnum();
        System.out.println("=========================");
        Week week = Week.Monday;
        System.out.println("=========================");
        Week week2 = Week.Tuesday;
    }

}

运行结果:

普通类中静态代码块执行了
普通类中代码块执行了
普通类的构造方法执行了
+++++++++++++++++++++++++++
普通类中代码块执行了
普通类的构造方法执行了
=========================
枚举中代码块执行了
枚举中构造方法执行了
枚举中代码块执行了
枚举中构造方法执行了
枚举中静态代码块执行了
=========================

普通类中,静态代码块在类加载的时候执行,类只会加载一次,所以只会执行一次,并且这个动作在对象实例化之前,所以是最先输出的,紧接着每次实例化都会去调用构造代码块和构造方法,所以会2次输出。但是枚举类就比较有趣了,可以看到枚举类中第一次使用的时候,调用构造代码块和构造方法块执行次数和枚举元素相等,第二次并没有再调用构造代码块和构造方法,静态代码块虽然也只执行了一次,但是却放在最后了,这是为什么呢?

我们借助jad反编译一下枚举类的代码:

jad Week.class

反编译生成的代码:

// 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:   Week.java

package learnbymaven.single;

import java.io.PrintStream;

public final class Week extends Enum
{

    private Week(String s, int i)
    {
        super(s, i);
        System.out.println("枚举中代码块执行了");
        System.out.println("枚举中构造方法执行了");
    }

    public static Week[] values()
    {
        Week aweek[];
        int i;
        Week aweek1[];
        System.arraycopy(aweek = ENUM$VALUES, 0, aweek1 = new Week[i = aweek.length], 0, i);
        return aweek1;
    }

    public static Week valueOf(String s)
    {
        return (Week)Enum.valueOf(learnbymaven/single/Week, s);
    }

    public static final Week Monday;
    public static final Week Tuesday;
    private static final Week ENUM$VALUES[];
	
	// 静态代码块初始化变量
    static 
    {
        Monday = new Week("Monday", 0);
        Tuesday = new Week("Tuesday", 1);
        ENUM$VALUES = (new Week[] {
            Monday, Tuesday
        });
        System.out.println("枚举中静态代码块执行了");
    }
}

通过反编译代码可以看到,枚举底层其实还是class,枚举元素是被声明成public static final的成员变量(可以通过类名直接调用),并且在static静态代码块中一起初始化了,这就解释了为什么第一次调用枚举类的时候,构造代码块和构造方法执行次数会和枚举元素相等,因为第一次加载类的时候就全部初始化了。由于java类的加载和初始化过程都是线程安全的,所以创建一个enum类型是线程安全的

posted @ 2020-04-30 11:57  三分魔系  阅读(263)  评论(0编辑  收藏  举报