我正在将某些语言的程序翻译成嵌套的java类。在某些时候,嵌套的程度变得非常深,我得到了:
编译Test.javaTest.java:5179:写错误:测试$ 2 ... $ 1.class(文件名太长)
其中......是一个长字符串。
我正在使用ext3文件系统,所以我限制为256个字符长的文件名。此外,我想暂时继续使用这种翻译方法(对内部类),因为我更感兴趣的是测试执行闭包转换的语言,这将解决问题。是否有一种快速的方法来绕过这个? (使用不同的文件系统或告诉javac生成不同的文件名?)
答案 0 :(得分:3)
底线是,是的,它是可行的。可以更改内部类的名称,因此它比javac
指定的原始名称短。
我正在搜索The Java Language Specification和The 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
这是一个快速实验:
A.class
文件,将引用更改为A$B$C
并将其更改为ABCDE
。A$B$C.class
重命名为ABCDE.class
。ABCDE.class
,将引用更改为ABCDE
。java A
,查看它是否正常运行。 注意: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$C
和A.class
文件中查找字符串A$B$C.class
并将其替换为Z
并更改字符串之前的字符来执行更改值0x05
到0x01
,表示字符串的长度现在为1
而不是5
。
通过这些更改,以及将文件重命名为Z.class
,程序就像没有发生任何事情一样运行。
所以,是的,可能也可以缩短内部类的名称。
答案 1 :(得分:1)
一种可能的解决方案是在另一个操作系统上编译,然后使用Obfuscater,例如yGuard。默认情况下,obfuscater会将类名更改为最小名称(例如A,B,C ......),从而大大缩短了类名(以及文件名)。
根据你想要测试的确切内容,可能对你毫无用处。
答案 2 :(得分:1)
您可以从java中编译java,将输出发送到您自己实现的文件管理器。
使用javax.tools.JavaCompiler,JavaFileManager为您提供编译后的输出,您可以将其直接写入jar?
答案 3 :(得分:0)
Comparison of File Systems,看起来ResierFS可能是唯一支持更长文件名的公司之一。我会对这种方法保持警惕,因为所有工具(javac,java,ant,ls,rm,cp等)都可能对文件名长度做出假设,因为大多数文件系统都是255,你将被绑定到一个FS(如果它消失了怎么办?)如果这是纯粹学术性的,那就重新格式化(或使用虚拟化)。
您可能只需要重新评估算法,以避免如此深入地嵌套类。你能用多个文件吗?我知道你不想要这样做,但这可能只是 选项