В источнике System.java стандартные потоки ввода, вывода и ошибок объявляются окончательными и инициализированными нулями?
public final static InputStream in = null;
public final static PrintStream out = null;
public final static PrintStream err = null;
Но, как мы очень хорошо знаем, эти потоки подключены к консоли по умолчанию и уже открыты. Существуют также методы в System class setIn(), setOut и setErr() для перенаправления потоков. Как это возможно, когда они были объявлены окончательными и установлены на значение инициализации null?
Я скомпилировал следующий код, установил точку останова при вызове println() и отладил с помощью netbeans. Моя цель состояла в том, чтобы точно определить, когда переменная System.in инициализируется стандартным выходом, вступая в исходный код. Но кажется, что выходной поток уже инициализирован к моменту вызова основного метода.
public static void main(String[] args) {
System.out.println("foo");
}
Ответы
Ответ 1
Они позже устанавливаются нативными методами SetIn0
, SetOut0
и SetErr0
private static native void setIn0(InputStream in);
private static native void setOut0(PrintStream out);
private static native void setErr0(PrintStream err);
вызванный методом initializeSystemClass
, который в соответствии с JavaDoc вызывается после инициализации потока.
FileInputStream fdIn = new FileInputStream(FileDescriptor.in);
FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);
setIn0(new BufferedInputStream(fdIn));
setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true));
setErr0(new PrintStream(new BufferedOutputStream(fdErr, 128), true));
Ответ 2
Это делается для предотвращения "взлома". Эти поля могут быть изменены только соответствующими сеттерами, которые вызывают методы native
private static native void setIn0(InputStream in);
private static native void setOut0(PrintStream out);
private static native void setErr0(PrintStream err);
Нативные методы могут делать все, включая изменение конечных полей.
Ответ 3
Поля final
не обязательно постоянны. Их все еще можно манипулировать, это просто то, что манипуляции предотвращаются только во время компиляции, в частности, путем предотвращения использования оператора присваивания (=
). См. этот вопрос и JLS §17.5.3, в частности:
final
поля могут быть изменены с помощью отражения и других зависимых от реализации средств.
Это необходимо для таких вещей, как десериализация. Это также может вызвать некоторые интересные предостережения, поскольку компиляторы могут оптимизировать поля final
во время компиляции и времени выполнения. Пример JLS, приведенный выше, имеет пример этого.