Выходной объект PrintStream инициализируется нулем, как мы вызываем для него метод?

Я видел в классе System, что объект out (типа PrintStream) инициализируется значением null. Как мы можем вызвать такой метод, как System.out.prinln("");? В классе System переменная out инициализируется следующим образом:

package java.lang;

public final class System {
    public final static PrintStream out = nullPrintStream();

     private static PrintStream nullPrintStream() throws NullPointerException {
        if (currentTimeMillis() > 0) {
            return null;
        }
        throw new NullPointerException();
     }
}

Как показано выше, переменная кода out инициализируется нулем, и эта переменная является окончательной, поэтому ее нельзя инициализировать дальше, тогда как мы можем использовать переменную «out».


person Hits    schedule 01.08.2013    source источник
comment
может помочь вам stackoverflow.com/questions/9454866/out -in-system-out-println   -  person Prabhaker A    schedule 01.08.2013


Ответы (4)


JVM вызывает private static void initializeSystemClass(), который его инициализирует.

Посмотрите на эти две строки кода:

setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true));
setErr0(new PrintStream(new BufferedOutputStream(fdErr, 128), true));

Это два нативных метода:

private static native void setOut0(PrintStream out);
private static native void setErr0(PrintStream err);

Об этом есть хорошая статья.

person AllTooSir    schedule 01.08.2013
comment
Статья не объясняет трюк currentTimeMillis. Почему это используется? Чтобы избежать какой-то оптимизации? - person kan; 17.12.2015
comment
@kan: смешно (а иногда и пугающе) видеть такой эзотерический код даже в фундаментальных классах Java. Аналог nullInputStream() имеет комментарий, говорящий о запрете встраивания, но null никогда не встраивается во время компиляции, и способ, которым вы попадаете в null, не будет иметь значения во время выполнения. Следовательно, версия Java 7 больше не имеет этого странного метода. - person Holger; 22.09.2017

Объяснение в комментариях:

/**
 * The following two methods exist because in, out, and err must be
 * initialized to null.  The compiler, however, cannot be permitted to
 * inline access to them, since they are later set to more sensible values
 * by initializeSystemClass().
 */

И initializeSystemClass() использует собственные методы для инициализации стандартных потоков ненулевыми значениями. Собственный код может повторно инициализировать переменные, объявленные окончательными.

person JB Nizet    schedule 01.08.2013
comment
я думаю, вы пытаетесь сказать о приведенном ниже коде private static void initializeSystemClass() { setOut0 (new PrintStream (new BufferedOutputStream (fdOut, 128), true)); } private static native void setOut0(PrintStream out); но одно я не понимаю, как конечная переменная может быть инициализирована два раза ?? один из вышеуказанных методов и второй раз как мой код вопроса. - person Hits; 01.08.2013
comment
Ну и хорошо, но не выйдет ли это из строя, если у меня есть компьютер, который настолько быстр, что достигает этого метода инициализации менее чем за 1 миллисекунду? Или currentTimeMillis() гарантированно возвращает значение › 0? - person Ingo; 01.08.2013
comment
Да. currentTimeMillis() возвращает текущее время? 0 означает 1 января 1970 года, и мы живем в 2013 году. Переменная final может быть инициализирована дважды, потому что это собственный код, написанный на C, который инициализирует переменную. Такой собственный код, являющийся частью самой JVM, конечно, может обойти проверки JVM, применяемые к коду Java. - person JB Nizet; 01.08.2013

Есть объект getter и setter для out.

person Ruchira Gayan Ranaweera    schedule 01.08.2013

Когда класс System инициализируется, он вызывает свой метод initializeSystemClass(), вот код:

FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true));

В этом коде setOut0() является встроенной функцией, реализованной в System.c:

JNIEXPORT void JNICALL
Java_java_lang_System_setOut0(JNIEnv *env, jclass cla, jobject stream)
{
    jfieldID fid =
        (*env)->GetStaticFieldID(env,cla,"out","Ljava/io/PrintStream;");
    if (fid == 0)
        return;
    (*env)->SetStaticObjectField(env,cla,fid,stream);
}

Это стандартный код JNI, который устанавливает System.out в переданный ему аргумент, этот метод вызывает собственный метод setOut0(), который устанавливает выходную переменную в соответствующее значение.

System.out является окончательным, это означает, что он не может быть установлен на что-то другое в initializeSystemClass(), но с помощью собственного кода можно изменить конечную переменную.

person Mohit Tyagi    schedule 27.08.2017