"Нет, это не может быть сделано"
Это обсуждалось много раз, и единственный ответ - "Нет, это не может быть сделано". Но, ребята, мы делаем код. Все теоретически возможно с течением времени.
Проблема
В основном я пытаюсь расширить последний класс, чтобы изменить одно поведение. Это невозможно сделать традиционным способом - во время компиляции с ключевым словом extend
. Я искал несколько других способов:
- создать прокси - который работает только для интерфейсов;
- отредактировать
Modifiers
класса, удалить модификатор final
и расширитькласс во время выполнения - это можно сделать для полей, но не для классов; - использовать пользовательский
ClassLoader
для загрузки новой версии класса - он не работает, потому что класс, который я хотел быРасширение является частью пакета java
. К счастью, ClassLoader
не может загрузить класс этого пакета из-за SecurityException
. В противном случае это испортит много ценных бумаг.
static class TestClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.equals("java.io.StringReader")) {
try {
InputStream is = Test.class.getClassLoader().getResourceAsStream("java/io/StringReader.class");
byte[] buf = new byte[10000];
int len = is.read(buf);
return defineClass(name, buf, 0, len); // This line throws the SecurityException :(
} catch (IOException e) {
throw new ClassNotFoundException("", e);
}
}
return getParent().loadClass(name);
}
}
Итак, мы здесь. Есть ли какой-нибудь метод, который я мог бы использовать, чтобы заменить или расширить мой последний класс?
PS: я использую Java 8, поскольку SecurityManager
более увлечен уродливыми вещами, чем в более новых версиях Java:)
Контекст
У меня есть задание - эээ, это никогда не принималось на SO: мне нужно написать модульные тесты для класса, который я не могу изменить. Эта часть действительно проста, за исключением того, что к блоку catch
нельзя получить доступ:
Properties props = new Properties();
try
{
props.load(new StringReader(rules));
} catch (IOException e)
{
e.printStackTrace();
throw e;
}
Отчет о покрытии кода, который прислал нам наш учитель, показывает, что он не тестировал этот блок, поэтому мы не имеемк. Но я не доверяю Java и был бы уверен, что этот блок работает должным образом. Тогда я должен проверить это!
[Примечание: поскольку это не требуется, это всего лишь средство для обнаружения новых взломов Java, и это, вероятно, даже не будет учитываться в итоговой оценке.]
Единственная переменная, к которой я могу получить доступ, это String
rules
. Затем я должен поработать над этим, чтобы load
вызвать вызов IOException. Единственная причина для вызова load
для выброса IOException - закрытие потока StringReader. Это проверяется методом ensureOpen
:
/** Check to make sure that the stream has not been closed */
private void ensureOpen() throws IOException {
if (str == null)
throw new IOException("Stream closed");
}
Следовательно, только нуль String
может заставить этот метод выбросить IOException
. Тогда было бы легко ввести ноль String
в rules
. Плохая новость - конструктор StringReader
:
public StringReader(String s) {
this.str = s;
this.length = s.length();
}
Он бросает NPE при прохождении null
String
. Erk.
Последней идеей, с которой я столкнулся, было изменение значения String
, чтобы сделать его внутреннее поле value
null
- простым при работе с Modifiers
этого поля. Это не сработало, потому что ссылка, а не само значение должно быть null
;это вызовет NPE только из-за вызова метода read
для StringReader
:
public int read() throws IOException {
synchronized (lock) {
ensureOpen();
if (next >= length)
return -1;
return str.charAt(next++);
}
}
У вас есть идеи? Основа, которая сделает это для меня? Какой-нибудь путь, который я мог бы найти, чтобы решить мою бесполезную проблему?
Этот вопрос не о тестировании кода . Речь идет о о том, как взломать Java и его последние классы.