java内部类的文件名太长了

时间:2009-06-17 11:53:27

标签: java filesystems

我正在将某些语言的程序翻译成嵌套的java类。在某些时候,嵌套的程度变得非常深,我得到了:

编译Test.javaTest.java:5179:写错误:测试$ 2 ... $ 1.class(文件名太长)

其中......是一个长字符串。

我正在使用ext3文件系统,所以我限制为256个字符长的文件名。此外,我想暂时继续使用这种翻译方法(对内部类),因为我更感兴趣的是测试执行闭包转换的语言,这将解决问题。是否有一种快速的方法来绕过这个? (使用不同的文件系统或告诉javac生成不同的文件名?)

4 个答案:

答案 0 :(得分:3)

底线是,是的,它是可行的。可以更改内部类的名称,因此它比javac指定的原始名称短。

我正在搜索The Java Language SpecificationThe Java Virtual Machine Specification以查找有关使用$字符表示内部类的内容,并且无法找到对它的引用。原因是,它并不重要。

案例和观点:

class A {
    class B {
        class C {}
    }

    A() {
        new B().new C();
    }

    public static void main(String[] s){
        new A();
    }
}

在这里,我们有嵌套的内部类。编译时,我们得到以下文件:

A.class
A$B.class
A$B$C.class

这是一个快速实验:

  1. 打开A.class文件,将引用更改为A$B$C并将其更改为ABCDE
  2. A$B$C.class重命名为ABCDE.class
  3. 打开ABCDE.class,将引用更改为ABCDE
  4. 运行java A,查看它是否正常运行。
  5. 注意:A$B$C更改为ABCDE的原因是因为标识符长度的变化似乎会破坏class文件格式,并会导致错误。技术说明将在本文末尾。

    结果?它有效。

    原因在class文件中。这是原始A.class的反汇编,只有相关部分:

    Compiled from "A.java"
    class A extends java.lang.Object
      SourceFile: "A.java"
      InnerClass: 
       #10= #3 of #7; //B=class A$B of class A
       #22= #2 of #3; //C=class A$B$C of class A$B
    
    // ... snip ... //
    
    const #2 = class    #21;    //  A$B$C
    
    // ... snip ... //
    
    const #21 = Asciz   A$B$C;
    
    // ... snip ...//
    

    事实证明,内部类的名称只是常量池中的名称。

    如果A$B$C常量池中A.class类的名称更改为ABCDE,并且A$B$C类中的文件名和名称更改为class {1}}文件被更改,然后Java虚拟机将很乐意使用新命名的内部类执行。

    这是什么意思?

    一个人不需要使用MyClass$1$1$1 ... $1作为类名,但是其他任何东西都可以满足一个人的需要,因此,可以在更短的文件名中使用更多的组合。

    有人会这样做吗?我将把这作为练习留给读者。

    关于使用ABCDE作为新班级名称

    的说明

    在这篇文章中,嵌套内部类A$B$C的名称已更改为ABCDE,以保持类名的长度相同,以防止ClassFormatError被抛出。原因是常量池的CONSTANT_Utf8_info structure具有length属性,表示字符串的长度。我在文本编辑器中编辑class文件时无法更改长度。

    为了缩短常量池中的字符串,我假设必须改变length字段的值以反映字符串本身的长度。

    <强>更新

    是的,可以编辑class文件的常量池来缩短内部类的名称。

    我能够将ABCDE班级更改为Z班级。

    以下是A.class

    的反汇编的一部分
    Compiled from "A.java"
    class A extends java.lang.Object
      SourceFile: "A.java"
      InnerClass: 
       #10= #3 of #7; //B=class A$B of class A
       #22= #2 of #3; //C=class Z of class A$B
    
    // ... snip ...//
    
    const #2 = class    #21;    //  Z
    
    // ... snip ...//
    
    const #21 = Asciz   Z;
    
    // ... snip ...//
    

    可以看出,内部类现在由Z引用,而不是A$B$C

    通过在A$B$CA.class文件中查找字符串A$B$C.class并将其替换为Z并更改字符串之前的字符来执行更改值0x050x01,表示字符串的长度现在为1而不是5

    通过这些更改,以及将文件重命名为Z.class,程序就像没有发生任何事情一样运行。

    所以,是的,可能也可以缩短内部类的名称。

答案 1 :(得分:1)

一种可能的解决方案是在另一个操作系统上编译,然后使用Obfuscater,例如yGuard。默认情况下,obfuscater会将类名更改为最小名称(例如A,B,C ......),从而大大缩短了类名(以及文件名)。

根据你想要测试的确切内容,可能对你毫无用处。

答案 2 :(得分:1)

您可以从java中编译java,将输出发送到您自己实现的文件管理器。

使用javax.tools.JavaCompilerJavaFileManager为您提供编译后的输出,您可以将其直接写入jar?

答案 3 :(得分:0)

Comparison of File Systems,看起来ResierFS可能是唯一支持更长文件名的公司之一。我会对这种方法保持警惕,因为所有工具(javac,java,ant,ls,rm,cp等)都可能对文件名长度做出假设,因为大多数文件系统都是255,你将被绑定到一个FS(如果它消失了怎么办?)如果这是纯粹学术性的,那就重新格式化(或使用虚拟化)。

您可能只需要重新评估算法,以避免如此深入地嵌套类。你能用多个文件吗?我知道你不想要这样做,但这可能只是 选项