前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java枚举的values方法是拷贝操作,你知道吗?

Java枚举的values方法是拷贝操作,你知道吗?

作者头像
KINGYT
发布2019-08-16 12:53:49
1.8K0
发布2019-08-16 12:53:49
举报

Java中的枚举类是我们平时写代码时经常会用到的一个类型,在我们创建枚举类之后,Java会默认在该类中为我们生成values、valueof 等方法。

但你知道吗,values方法可是个拷贝操作。

写个例子验证下:

代码语言:javascript
复制
$ cat Type.java
public enum Type {
  T1,
  T2,
  ;
  public static void main(String[] args) {
    System.out.println(Type.values() == Type.values());
  }
}
$ java Type.java
false

如果values方法不是拷贝操作的话,那两次方法调用返回的对象应该是一样的,但结果却输出了false,可见该方法应该就是拷贝操作。

有同学可能会发现,我们没编译,直接执行的java Type.java,而且还成功了,java不是要先编译后才能执行吗?

有关这个问题,可以看我上一篇文章:Java也可以不用编译直接执行了?

继续回到本文的话题。

上文我们说到,values方法是拷贝操作,但这只是我们的猜测,有什么证据能明确证明吗?

我们看下上面类对应的字节码:

代码语言:javascript
复制
$ javac Type.java
$ javap -c -p Type
Compiled from "Type.java"
public final class Type extends java.lang.Enum<Type> {
  public static final Type T1;

  public static final Type T2;

  private static final Type[] $VALUES;

  public static Type[] values();
    Code:
       0: getstatic     #1                  // Field $VALUES:[LType;
       3: invokevirtual #2                  // Method "[LType;".clone:()Ljava/lang/Object;
       6: checkcast     #3                  // class "[LType;"
       9: areturn
  
  # 省略无关字节码 #
  
  static {};
    Code:
       0: new           #4                  // class Type
       3: dup
       4: ldc           #10                 // String T1
       6: iconst_0
       7: invokespecial #11                 // Method "<init>":(Ljava/lang/String;I)V
      10: putstatic     #12                 // Field T1:LType;
      13: new           #4                  // class Type
      16: dup
      17: ldc           #13                 // String T2
      19: iconst_1
      20: invokespecial #11                 // Method "<init>":(Ljava/lang/String;I)V
      23: putstatic     #14                 // Field T2:LType;
      26: iconst_2
      27: anewarray     #4                  // class Type
      30: dup
      31: iconst_0
      32: getstatic     #12                 // Field T1:LType;
      35: aastore
      36: dup
      37: iconst_1
      38: getstatic     #14                 // Field T2:LType;
      41: aastore
      42: putstatic     #1                  // Field $VALUES:[LType;
      45: return
}

由字节码可见,javac自动为我们生成了很多东西,其中就包括一个static代码块。

该代码块的大致逻辑是:

1. 创建类型为Type的实例,new Type("T1", 0),赋值给静态变量T1。

2. 创建类型为Type的实例,new Type("T2", 1),赋值给静态变量T2。

3. 创建类型为Type数组,并将静态变量T1、T2依次放到数组中,然后再将该数组赋值给静态变量 $VALUES。

javac还为该枚举类生成了一个values方法,这个values方法就是本文要讲的方法,我们来具体看下其操作:

1. 获取静态变量$VALUES。

2. 调用$VALUES的clone方法。

3. 将clone方法返回的对象强转成Type数组。

4. 返回该数组。

由此我们可以看到,values方法的确是拷贝操作。

上文我们说到,values等方法是javac动态生成的,是这样吗?

我们还是通过源码来确认下这个疑问。

代码语言:javascript
复制
// com.sun.tools.javac.comp.TypeEnter.MembersPhase
private void addEnumMembers(JCClassDecl tree, Env<AttrContext> env) {
    ...
    // public static T[] values() { return ???; }
    JCMethodDecl values = make.
        MethodDef(make.Modifiers(Flags.PUBLIC|Flags.STATIC),
                  names.values,
                  valuesType,
                  List.nil(),
                  List.nil(),
                  List.nil(), // thrown
                  null, //make.Block(0, Tree.emptyList.prepend(make.Return(make.Ident(names._null)))),
                  null);
    ...
}

该方法向Enum类里添加了values方法,但还没有方法体。

代码语言:javascript
复制
// com.sun.tools.javac.comp.Lower
private void visitEnumDef(JCClassDecl tree) {
    ...
    Symbol valuesSym = lookupMethod(tree.pos(), names.values,
                                    tree.type, List.nil());
    ...
    if (useClone()) {
        // return (T[]) $VALUES.clone();
        JCTypeCast valuesResult =
            make.TypeCast(valuesSym.type.getReturnType(),
                          make.App(make.Select(make.Ident(valuesVar),
                                               syms.arrayCloneMethod)));
        valuesBody = List.of(make.Return(valuesResult));
    } else {
        // template: T[] $result = new T[$values.length];
        ...
        // template: System.arraycopy($VALUES, 0, $result, 0, $VALUES.length);
        ...
        // template: return $result;
        ...
    }
    ...
}

该方法向Enum类的values方法里添加了方法体。

怎么样,现在一切都非常明朗了吧,values方法会拷贝数组$VALUES的值,然后返回给我们。

希望能对大家有所帮助。

完。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-08-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Linux内核及JVM底层相关技术研究 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档