Comments 15
Данные инструкции разнесены для того, чтобы находиться в разных try-блоках. Почему сделано именно так, я не знаю. Вероятно, это был единственный способ обрабатывать исключения так, чтобы они полностью соответствовали спецификации языка.
Если посмотреть на реализацию MethodAccessorGenerator
, то будет видно, что generateMethod()
и generateConstructor()
— обёртки над методом generate()
и генерируемые классы отличаются лишь в деталях.
Вот, к примеру, как будет выглядеть класс для доступа к PrintStream::println(String)
:
package sun.reflect;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
public class GeneratedMethodAccessor1 extends MethodAccessorImpl {
public Object invoke(Object var1, Object[] var2) throws InvocationTargetException {
if (var1 == null) {
throw new NullPointerException();
} else {
PrintStream var10000;
String var10001;
try {
var10000 = (PrintStream)var1;
if (var2.length != 1) {
throw new IllegalArgumentException();
}
var10001 = (String)var2[0];
} catch (NullPointerException | ClassCastException var4) {
throw new IllegalArgumentException(var4.toString());
}
try {
var10000.println(var10001);
return null;
} catch (Throwable var3) {
throw new InvocationTargetException(var3);
}
}
}
}
Здесь видно, что в блоке catch (NullPointerException | ClassCastException)
делается проверка того, что метод вызывается на объекте подходящего типа, в метод передаётся ровно столько параметров, сколько нужно и они верных типов.
Если в этом блоке что-то пойдёт не так, то наружу будет проброшено IllegalArgumentException
.
В блоке catch (Throwable)
производится уже непосредственный вызов метода и если в процессе его работы будет сгенерирована исключительная ситуация, то она будет обёрнута в InvocationTargetException
и так же проброшена наружу.
Единственное отличие при вызове конструктора, а не метода ровно одно — в самом начале catch (NullPointerException | ClassCastException)
добавляется инструкция new
, а в качестве вызываемого метода выступит <init>
(конструктор).
Другими словами, метод invoke()
сгенерированного класса ведёт себя аналогично методам java.lang.reflect.Method::invoke()
и java.lang.reflect.Constructor::invoke()
.
Согласен с вами, лейаут хипа испорчен. Объект ClassLoader
, переданный в конструктор, записался в чью-то чужую память и стал бомбой замедленного действия.
Но даже если бы я выделил правильное количество байт, всё равно почти любые манипуляции с этим объектом приводили бы к краху. В JVM ведь по факту никакого нового класса нет, а методы в java.lang.Class
почти все нативные.
Понятное дело, что приведённая задача решения не имеет. Вся 6-я часть может восприниматься как шутка.
Как говорится, "Жесть, читать до конца". Цинизм прохождения первого уровня насмешил. А на пятом вспомнился анекдот про "За использование неопределённого поведения в особо крупных размерах приговаривается к 40 годам обратной совместимости", ещё подумалось: "А как же в исходниках JVM поковыряться?" — и тут уровень 6...
— Какое будет имя у класса, который инстанцируете (что вернет getName())?
— Какие у него будут методы и поля?
— Как он будет связан с класс-лоадером (просто установка приватного поля в самом инстансе класса очевидно недостаточно)?
Даже если бы у вас получилось, вы на выходе будете иметь заведомо нерабочую конструкцию — зачем??
Если вы это все делаете просто из желания «обмануть систему» — тогда Ok, как говорится, «каждый борется со скукой по-своему».
Но если вы хотели вручную создать экземпляр класса, еще не загруженного JVM, то для этого есть нормальный путь:
1. Загрузить байткод (т.е. файл my/package/MyClass.class) в массив байт
2. Вызвать ClassLoader.defineClass(...) в том класслоадере, в котором хотите иметь вновь загруженный класс.
Конструктор java.lang.Class
— это лишь предлог, для любого другого класса изложение закончилось бы уже в первой части.
Настоящая цель должна быть понятна из содержания — исследование способов динамического инстанцирования объектов. Откинув абсурдную шестую часть, остаются реально используемые варианты:
- JNI вызов;
MagicAccessorImpl
;Unsafe
.
Инстанцируем java.lang.Class