Итог, да, это выполнимо. Можно изменить имя внутреннего класса, поэтому оно короче исходного имени, присвоенного javac
.
Я искал Спецификации языка Java и Спецификации виртуальной машины Java , чтобы найти, где говорится об использовании символа $
для обозначения внутреннего класса, и не было смог найти ссылку на него. Причина в том, что это не имеет значения.
Дело и точка:
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 'в файле class
изменяется, то Виртуальная машина Java будет успешно выполняться с новым внутренним классом.
Что это значит?
Не нужно использовать MyClass$1$1$1 ... $1
для имени класса, но все остальное, что соответствует его потребностям, следовательно, было бы возможно иметь больше комбинаций в более коротком имени файла.
Как бы кто-то пошел и сделал это? Это я оставлю читателю в качестве упражнения.
Примечание об использовании ABCDE
в качестве имени нового класса
В этом посте имя вложенного внутреннего класса A$B$C
было изменено на ABCDE
, чтобы сохранить длину имени класса одинаковой, чтобы предотвратить выброс ClassFormatError
. Причина этого заключается в том, что структура CONSTANT_Utf8_info
пула констант имеет свойство 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
, программа работала так, как будто ничего не происходило.
Итак, да, возможно также сократить имя внутреннего класса.