2013-03-22 16 views
6

Poniżej znajduje się kod z klasy java.lang.System (JDK wersja 1,6)dziwne 'out' zmienna, System.out.println()

public final static PrintStream out = nullPrintStream(); //out is set to 'null' 

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

gdy piszemy System.out.println("Something"); w naszym kodzie dlaczego don” t otrzymujemy NullPointerException nawet gdy „out” jest ustawiony na „null”

Byle out zostanie ustalona poprzez następujące metody w klasie setOut systemu

public static void setOut(PrintStream out) { 
    checkIO(); 
    setOut0(out); 
} 

one n dlaczego JLS potrzebuje metody nullPrintStream?

+0

'if (currentTimeMillis()> 0) {return null; } '=> To naprawdę dziwne. W JDK 7 jest to po prostu:' public final static PrintStream out = null; '. – assylias

+1

@assylias Wszystko po to, aby poskromić wcześniejsze wersje kompilatorów javac/JIT. Bez tego "jeśli" kompilator mógłby zrozumieć, że zawsze zwraca 'null' i kompiluje' out' jako stałą czasu kompilacji, z wszystkimi złymi konsekwencjami. –

+0

Oznacza to, że gdy minie wystarczająco dużo czasu, aby wartość 'currentTimeMillis()' przekroczyła maksymalną wartość dla 'long', wszystkie aplikacje działające z maszynami wirtualnymi starszymi niż Java 7, zawiedzie z błędem:' java.lang .ExceptionInInitializerError Powodowane przez java.lang.NullPointerException na java.lang.System.nullPrintStream (Unknown Source) 'lub podobne. – gparyani

Odpowiedz

8

Spójrz na private static void initializeSystemClass() - ta metoda jest wywoływana, aby rozpocząć rzeczy, wywołuje setOut0(), który jest metodą native. To wiąże Stream w miejscu, w którym powinno być.

Więc nawet jeśli pole może wyglądaćpublic static final to faktycznie nie jest, kod native zmienia go.

EDIT

OP prosi Więc dlaczego JLS musi metodę nullPrintStream?

Ma to związek z kompilatorem java - spowoduje wstawienie pól static final, jeśli są przypisane do czegoś stałego w czasie kompilacji, na przykład null. Kompilator faktycznie zastąpi każdą referencję do pola stałą.

Spowoduje to przerwanie inicjalizacji, ponieważ obiekty nie będą już zawierać odwołania do Stream, ale do null. Przypisanie strumienia do powrotu metody zapobiega inliningowi.

Niektórzy mogą nazwać to brudnym hackerem. Błędne cytowanie Bismarcka "JDK jest jak kiełbaski, najlepiej nie widzieć, jak powstają".

+0

Mimo że, jak wspomniano powyżej, w Javie 7 został naprawiony - więc musi zrobić ze starszymi kompilatorami wskazanymi przez Marko. – assylias

+0

Mogli zdefiniować 'public final static PrintStream out = null;'. Pomóż w zrozumieniu statycznego pola końcowego "lnline". – AmitG

+2

@AmitG Spójrz na [to] (http://stackoverflow.com/questions/5173372/java-static-final-values-replaced-in-code-when-compiling) i [to] (http: // docs.oracle.com/javase/specs/jls/se7/html/jls-13.html#jls-13.4.9). Jeśli zdefiniujesz coś jako stałą czasu kompilacji, kompilator (Java 6) użyje tej informacji do optymalizacji kodu - zastąpi on wszelkie odniesienia literałem. Tak więc wszystko, co powiedziałoby, że "System.out" zostanie zastąpione przez 'null' przez kompilator, ponieważ zakłada, że' System.out' nie może zmienić bycia, ponieważ jest ustawione na 'null' i jest' końcowe'. –

2

Tak właśnie została zainicjowana klasa System.out.

Istnieje również metoda:

private static native void setOut0(PrintStream out); 

który nazywa się w następujący sposób:

private static void initializeSystemClass() { 
2

System.in, out i err są zarządzane przez JVM z natywnego kodu. Cała ta magia z nullPrintStream() była po to, aby powstrzymać javac przed wpisaniem tych pól. Od wersji Java 7 wygląda on na:

public final static PrintStream out = null;