java枚举和其他类文件

5gfr0r5j  于 2021-06-30  发布在  Java
关注(0)|答案(6)|浏览(338)

我注意到了 enums 在编译后引入许多额外的类文件(class$1),使总大小膨胀。它似乎附加到每一个甚至使用枚举的类上,而且这些类常常是重复的。
为什么会发生这种情况?有没有一种方法可以在不删除枚举的情况下防止这种情况发生。
(问题的原因是空间对我来说是有价值的)
编辑
在进一步研究这个问题时,sun的javac1.6会在每次使用枚举上的开关时创建一个额外的合成类。它使用某种开关图。这个站点有更多的信息,这里告诉您如何分析javac正在做什么。
每次使用枚举上的开关时,额外的物理文件似乎要付出很高的代价!
有趣的是,eclipe的编译器并不生成这些附加文件。我想知道是否唯一的解决办法是切换编译器?

1zmg4dgp

1zmg4dgp1#

据我所知,给定一个名为 Operation 您将获得额外的类文件,不包括明显的 Operation.class ,并且如果使用 abstract method 就像这个:

enum Operation {

   ADD {
      double op(double a, double b) { 
          return a + b;
      }
   },

   SUB {
      double op(double a, double b) { 
          return a - b;
      }
   };

   abstract double op(double a, double b);
}
ukxgm1gy

ukxgm1gy2#

我只是被这种行为咬了一口,这个问题在谷歌搜索时出现了。我想我会分享我发现的一些额外的信息。
javac1.5和1.6每次在enum上使用开关时都会创建一个额外的合成类。该类包含一个所谓的“开关Map”,它将枚举索引Map到开关表跳转数。重要的是,合成类是为发生切换的类而不是枚举类创建的。
下面是生成内容的示例:

枚举类.java

public enum EnumClass { VALUE1, VALUE2, VALUE3 }

枚举用户.java

public class EnumUser {
    public String getName(EnumClass value) {
        switch (value) {
            case VALUE1: return "value 1";
            // No VALUE2 case.
            case VALUE3: return "value 3";
            default:     return "other";
        }
    }
}

合成枚举用户$1.class

class EnumUser$1 {
    static final int[] $SwitchMap$EnumClass = new int[EnumClass.values().length];

    static {
        $SwitchMap$EnumClass[EnumClass.VALUE1.ordinal()] = 1;
        $SwitchMap$EnumClass[EnumClass.VALUE3.ordinal()] = 2;
    };
}

然后使用此开关Map为 lookupswitch 或者 tableswitch jvm指令。它将每个枚举值转换为从1到[number of switch cases]的相应索引。

枚举用户类

public java.lang.String getName(EnumClass);
  Code:
   0:   getstatic       #2; //Field EnumUser$1.$SwitchMap$EnumClass:[I
   3:   aload_1
   4:   invokevirtual   #3; //Method EnumClass.ordinal:()I
   7:   iaload
   8:   lookupswitch{ //2
                1: 36;
                2: 39;
                default: 42 }
   36:  ldc     #4; //String value 1
   38:  areturn
   39:  ldc     #5; //String value 3
   41:  areturn
   42:  ldc     #6; //String other
   44:  areturn
``` `tableswitch` 如果有三个或更多的开关情况,则使用,因为它执行更有效的常数时间查找。 `lookupswitch` 的线性搜索。从技术上讲,javac在使用syntheticswitchmap时可以省略整个业务 `lookupswitch` .
推测:我手头没有eclipse的编译器可供测试,但我认为它不需要使用合成类,只需使用 `lookupswitch` . 或者可能它需要更多的开关情况下,比原来的asker测试之前,它“ugprades”到 `tableswitch` .
qnzebej0

qnzebej03#

我相信这样做是为了防止在枚举顺序改变时开关中断,而不是用开关重新编译类。考虑以下情况:

enum A{
    ONE, //ordinal 0
    TWO; //ordinal 1
}
class B{
     void foo(A a){
         switch(a){
              case ONE:
                   System.out.println("One");
                   break;
              case TWO:
                   System.out.println("Two");
                   break;
         }
     }
}

如果没有开关图, foo() 大致可以转化为:

void foo(A a){
         switch(a.ordinal()){
              case 0: //ONE.ordinal()
                   System.out.println("One");
                   break;
              case 1: //TWO.ordinal()
                   System.out.println("Two");
                   break;
         }
     }

因为case语句必须是编译时常量(例如,不是方法调用)。在这种情况下,如果 A 被切换, foo() 打印“一”代表两个,反之亦然。

7nbnzgx9

7nbnzgx94#

在java中,枚举实际上只是带有一些语法糖的类。
因此,每当您定义新的枚举时,java编译器都会为您创建相应的类文件(无论枚举多么简单)。
除了不使用枚举之外,没办法绕过这个问题。
如果空间是一个溢价,你总是可以用常量代替。

rjee0c15

rjee0c155#

考虑到并非所有java开发人员都知道java的这种行为,我创建了一些视频来解释java中switch语句的工作原理。
使用枚举切换-https://www.youtube.com/watch?v=hlspheb_xz4
用字符串切换-https://www.youtube.com/watch?v=cg9o815fewy
关于表开关和查找开关-https://www.youtube.com/watch?v=ohwdczhbpcw
java 13中的开关表达式-https://www.youtube.com/watch?v=sufn87irpb4
这可能无法直接回答问题。但是,它确实回答了java中switch语句的工作方式。

ufj5ltwl

ufj5ltwl6#

当您使用java枚举的“每示例方法实现”功能时,会出现$1 etc.文件,如下所示:

public enum Foo{
    YEA{
        public void foo(){ return true };
    },
    NAY{
        public void foo(){ return false };
    };

    public abstract boolean foo();
}

上面将创建三个类文件,一个用于基本枚举类,另一个用于yea和nay,以保存foo()的不同实现。
在字节码级别,枚举只是类,为了让每个枚举示例实现不同的方法,每个示例需要有不同的类,
但是,这并没有考虑为enum用户生成的其他类文件,我怀疑这些只是匿名类的结果,与enum无关。
因此,为了避免生成此类额外的类文件,不要使用每示例方法实现。在上述方法返回常量的情况下,可以使用构造函数中设置的公共final字段(如果愿意,可以使用带有公共getter的私有字段)。如果您真的需要为不同的enum示例使用不同逻辑的方法,那么您就不能避免额外的类,但是我认为这是一个非常奇特而且很少需要的特性。

相关问题