Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/349.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java枚举和其他类文件_Java_Enums - Fatal编程技术网

Java枚举和其他类文件

Java枚举和其他类文件,java,enums,Java,Enums,我注意到enum在编译后引入了许多额外的类文件(class$1),使总大小膨胀。它似乎附加到每一个甚至使用枚举的类,并且这些类通常是重复的 为什么会发生这种情况?有没有一种方法可以在不删除枚举的情况下防止这种情况发生 (问题的原因是空间对我来说是额外的) 编辑 在进一步研究这个问题时,Sun的JavaC1.6在每次使用枚举开关时都会创建一个额外的合成类。它使用某种开关映射。该站点提供了更多信息,并告诉您如何分析Javac正在做什么 每次在枚举上使用开关时,额外的物理文件似乎要付出高昂的代价 有趣

我注意到
enum
在编译后引入了许多额外的类文件(class$1),使总大小膨胀。它似乎附加到每一个甚至使用枚举的类,并且这些类通常是重复的

为什么会发生这种情况?有没有一种方法可以在不删除枚举的情况下防止这种情况发生

(问题的原因是空间对我来说是额外的)

编辑

在进一步研究这个问题时,Sun的JavaC1.6在每次使用枚举开关时都会创建一个额外的合成类。它使用某种开关映射。该站点提供了更多信息,并告诉您如何分析Javac正在做什么

每次在枚举上使用开关时,额外的物理文件似乎要付出高昂的代价


有趣的是,Eclipe的编译器不会生成这些附加文件。我想知道唯一的解决方案是切换编译器吗?

在Java中,枚举实际上只是一些添加了语法糖分的类

因此,无论何时定义新的枚举,Java编译器都会为您创建相应的类文件。(无论枚举多么简单)

除了不使用枚举之外,没有办法解决这个问题


如果空间很重要,您可以始终使用常量来代替。

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

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

    public abstract boolean foo();
}
上面将创建三个类文件,一个用于基本枚举类,另一个用于YEA和NAY,以保存foo()的不同实现

在字节码级别上,枚举只是类,为了让每个枚举实例以不同的方式实现方法,每个实例需要有不同的类

但是,这并没有考虑为枚举用户生成的其他类文件,我怀疑这些只是匿名类的结果,与枚举无关


因此,为了避免生成此类额外的类文件,不要使用每个实例方法实现。在上述方法返回常量的情况下,您可以使用构造函数中设置的公共final字段(如果愿意,也可以使用带有公共getter的私有字段)。如果您确实需要不同的枚举实例的不同逻辑的方法,那么您不能避免额外的类,但我认为它是一个相当异乎寻常和很少需要的特性。

到目前为止,我知道,给定一个枚举名为“代码>操作< /代码>,您将得到附加的类文件,不包括明显的<代码>操作。如果您使用的是像下面这样的
抽象方法
,则每个枚举值一个:

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);
}

我只是被这种行为咬了一口,这个问题在谷歌搜索时出现了。我想我会分享我发现的一些额外信息

每次在枚举上使用开关时,JavaC1.5和1.6都会创建一个额外的合成类。该类包含一个所谓的“开关映射”,它将枚举索引映射到开关表跳转数。重要的是,合成类是为发生切换的类而不是枚举类创建的

以下是生成内容的示例:

EnumClass.java EnumUser.java 综合枚举用户$1.0类 然后使用此开关映射为
lookupswitch
tableswitch
JVM指令生成索引。它将每个枚举值转换为从1到[切换案例数]的对应索引

枚举用户类
表开关
用于三种或三种以上的开关情况,因为它执行的恒定时间查找比
查找开关
的线性搜索更有效。从技术上讲,javac在使用
lookupswitch
时,可以省略合成开关映射的整个业务


推测:我手头上没有Eclipse的编译器可供测试,但我认为它不需要处理合成类,只需使用
lookupswitch
。或者,它可能需要比之前测试的原始asker更多的开关情况,即“ugprades”到
表开关
我相信这样做是为了防止在更改枚举顺序时开关中断,而不是使用开关重新编译类。考虑以下情况:

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()
将打印两个“一”,反之亦然。

考虑到Java的这种行为并非所有Java开发人员都知道,我制作了一些视频来解释Java中的Switch语句是如何工作的

  • 带枚举的开关-
  • 带字符串的开关-
  • 关于表开关和查找开关-
  • Java 13中的开关表达式-

  • 这可能无法直接回答这个问题。但是,它确实回答了Java中switch语句的工作原理。

    Class$n类文件是匿名的内部类。我已经大量使用了枚举,但还没有看到这一点。你能发布一个这样做的源文件吗?你担心什么样的“膨胀”?希望不是磁盘空间。:)如果是下载大小,我可以推荐
    .pack200.gz
    (前几天我天真地将CORBA打包作为一个实验——最著名的Java膨胀不到48K)。是的,这是小程序打包Jar文件的下载大小。我正试图采取措施尽可能地压缩它(不需要任何源代码级别的更改)。pack200对这种情况有效吗?您好,谢谢您的回答,但在本例中并非如此(请参见上面的编辑)。非常有趣-我想说,在这种情况下,有必要将您的发现作为一个单独的答案发布并接受。一则轶事:我今天遇到了这个问题(特别是Eclipse的[JDK 8]编译器没有这样做)结合此行为:--这意味着运行本地
    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;
        };
    }
    
    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
    
    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;
             }
         }
    }
    
     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;
             }
         }