Java中的枚举类是我们平时写代码时经常会用到的一个类型,在我们创建枚举类之后,Java会默认在该类中为我们生成values、valueof 等方法。
但你知道吗,values方法可是个拷贝操作。
写个例子验证下:
$ 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方法是拷贝操作,但这只是我们的猜测,有什么证据能明确证明吗?
我们看下上面类对应的字节码:
$ 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代码块。
该代码块的大致逻辑是:
javac还为该枚举类生成了一个values方法,这个values方法就是本文要讲的方法,我们来具体看下其操作:
由此我们可以看到,values方法的确是拷贝操作。
上文我们说到,values等方法是javac动态生成的,是这样吗?
我们还是通过源码来确认下这个疑问。
// 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方法,但还没有方法体。
// 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的值,然后返回给我们。
希望能对大家有所帮助。
完。
更多原创文章,请关注我微信公众号: