2013-08-01 7 views
5

Ich habe in der System Klasse gesehen, dass das Objekt out (vom Typ PrintStream) mit einem null Wert initialisiert wird. Wie können wir Methode wie System.out.prinln(""); nennen? In System Klassenvariable aus wie auf diese Weise initialisiert:PrintStream Object out wird mit null initialisiert, wie rufen wir Methode darauf auf?

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(); 
    } 
} 

Gemäß obigem Code out Variable von null initialisiert gezeigt und diese Variable ist endgültig, es kann weiter dann so nicht initialisiert, wie wir „out“ Variable verwenden können.

+0

kann Ihnen helfen http://stackoverflow.com/questions/9454866/out-in-system-out-println – Prabhaker

Antwort

4

JVM ruft die Methode private static void initializeSystemClass() auf, die es initialisiert.

Sehen Sie diese zwei Zeilen Code:

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

Dies sind die beiden nativen Methoden:

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

Es gibt eine nice article on it ist.

+1

Der Artikel erklärt nicht den 'currentTimeMillis' Trick. Warum wird es benutzt? Um eine Art von Optimierung zu vermeiden? – kan

+0

@kan: Es ist lustig (oder manchmal ziemlich beängstigend), solch einen esoterischen Code auch in grundlegenden Java-Klassen zu sehen. [Das 'NullInputStream()' Gegenstück] (http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/System.java#1063) hat a Kommentar, der über das Verbot von Inlining spricht, aber 'null' wird nie zur Kompilierzeit inline angezeigt und die Art und Weise, wie du zum' null' kommst, ist zur Laufzeit irrelevant. Folglich ist [die Java 7-Version] (http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/java/lang/System.java/#104) nicht Ich habe diese seltsame Methode nicht mehr. – Holger

6

Die Erklärung ist in den Kommentaren:

/** 
* 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(). 
*/ 

Und initializeSystemClass() native Methoden verwendet, um den Standard-Streams auf Nicht-Null-Werte zu initialisieren. Nativer Code kann Variablen initialisieren, die als final deklariert sind.

+0

ich denke, Sie versuchen, über den folgenden Code zu sagen private statische void initializeSystemClass() { \t setOut0 (neue PrintStream (neue BufferedOutputStream (fdOut, 128), true)); } private statische native void setOut0 (PrintStream out); aber eine Sache, die ich nicht verstehe, wie kann die letzte Variable zweimal initialisiert werden ?? eine von oben Methode und zweites Mal als mein Fragecode. – Hits

+0

Gut und gut, aber wird das nicht scheitern, sobald ich einen Computer habe, der so schnell ist, dass er diese Initialisierungsmethode in weniger als 1 Millisekunden erreicht? Oder ist currentTimeMillis() garantiert, einen Wert> 0 zurückzuholen? – Ingo

+1

Ja. currentTimeMillis() gibt die aktuelle Zeit zurück? 0 bedeutet 1. Januar 1970, und wir sind in 2013. Eine letzte Variable kann zweimal initialisiert werden, weil ihr in C geschriebener nativer Code die Variable initialisiert. Ein solcher nativer Code, der Teil der JVM selbst ist, kann natürlich die JVM-Cecks umgehen, die auf Java-Code angewendet werden. –

1

Es gibt eine getter und setter für out Objekt.

0

Wenn System-Klasse initialisiert erhalten, ruft seine initializeSystemClass() Methode, hier ist der Code:

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

In diesem Code setOut0() ist eine native Funktion in System.c implementiert:

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); 
} 

Dies ist ein Standard-JNI-Code, der System.out für das übergebene Argument festlegt. Diese Methode ruft die native Methode setOut0() auf, die die out-Variable auf den entsprechenden Wert setzt.

System.out ist endgültig, es bedeutet, dass es in initializeSystemClass() nicht auf etwas anderes gesetzt werden kann, aber mit nativem Code ist es möglich, eine endgültige Variable zu ändern.