我正在将某些语言的程序翻译成嵌套的java类.在某些时候,嵌套的程度变得如此之深以至于我得到:
编译Test.javaTest.java:5179:写错误:测试$2 … $1.class(文件名太长)
其中……是一个长串.
我正在使用ext3文件系统,因此我限制为256个字符长的文件名.此外,我想暂时继续使用这种翻译方法(对内部类),因为我更感兴趣的是测试执行闭包转换的语言,这将解决问题.是否有一种快速的方法来绕过这个? (使用不同的文件系统或告诉javac生成不同的文件名?)
解决方法:
底线是,是的,它是可行的.可以更改内部类的名称,因此它比javac指定的原始名称短.
案例和观点:
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
这是一个快速实验:
>打开A.class文件,将引用更改为A $B $C并将其更改为ABCDE.
>将A $B $C.class重命名为ABCDE.class.
>打开ABCDE.class,并将引用更改为ABCDE.
>运行java A,查看它是否运行.
注意:A $B $C更改为ABCDE的原因是因为标识符长度的变化似乎会破坏类文件格式,并且会导致错误.技术说明将在本文末尾.
结果?有用.
原因是在类文件中.这是原始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.class的常量池中的A $B $C类的名称更改为ABCDE,并且更改了类文件中的A $B $C类的文件名和名称,那么Java虚拟机将很高兴执行新命名的内部类.
这是什么意思?
一个人不需要使用MyClass $1 $1 $1 … $1作为类名,但是其他任何东西都可以满足一个人的需要,因此,可以在更短的文件名中使用更多的组合.
怎么会有人去做呢?我将把这作为练习留给读者.
关于使用ABCDE作为新类名的说明
在这篇文章中,嵌套内部类的名称A $B $C被更改为ABCDE以保持类名的长度相同,以防止抛出ClassFormatError.其原因是常量池的CONSTANT_Utf8_info structure具有长度属性,该属性表示字符串的长度.我在文本编辑器中编辑类文件时无法更改长度.
为了缩短常量池中的字符串,我认为必须改变长度字段的值以反映字符串本身的长度.
更新
是的,可以编辑类文件的常量池以缩短内部类的名称.
我能够将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.class和A $B $C.class文件中查找字符串A $B $C并将其替换为Z,并将字符串之前的字符从值0x05更改为0x01来执行更改,表示字符串的长度现在是1而不是5.
通过这些更改,以及将文件重命名为Z.class,程序运行就好像什么也没发生过一样.
所以,是的,也可以缩短内部类的名称.
标签:java,filesystems