2013-03-22 25 views
6

seguito riporta il codice dalla classe java.lang.System (JDK versione 1,6)strano 'out' variabile, 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(); 
} 

quando scriviamo System.out.println("Something"); nel nostro codice allora perché don' t otteniamo NullPointerException anche quando 'fuori' è impostato su 'nullo'

Comunque out sarà impostato via seguente setOut metodo nella classe System

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

Essi n perché JLS ha bisogno del metodo nullPrintStream?

+0

'if (currentTimeMillis()> 0) {return null; } '=> Questo è davvero strano .. In JDK 7, è semplicemente:' public final static PrintStream out = null; '. – assylias

+1

@assylias È tutto lì per placare le versioni precedenti dei compilatori javac/JIT. Senza che 'if' il compilatore potesse rendersi conto restituisce sempre' null' e compila 'out' come costante in fase di compilazione, con tutte le conseguenze negative. –

+0

Ciò significa che una volta trascorso abbastanza tempo per rendere il valore di 'currentTimeMillis()' overflow del valore massimo per un 'long', tutte le applicazioni in esecuzione con macchine virtuali precedenti a Java 7, non riusciranno con un errore:' java.lang .ExceptionInInitializerError Causato da java.lang.NullPointerException su java.lang.System.nullPrintStream (sorgente sconosciuta) 'o simile. – gparyani

risposta

8

Date un'occhiata al private static void initializeSystemClass() - questo metodo viene chiamato per avviare le cose, si chiama setOut0() che è un metodo native. Ciò lega il Stream al punto in cui dovrebbe essere.

Quindi, anche se il campo può guardarepublic static final in realtà non è, il codice native cambia.

EDIT

OP chiede Allora perché JLS necessita metodo nullPrintStream?

Ciò è a che fare con il compilatore Java - sarà "in linea" static final campi se sono assegnati a qualcosa di costante in fase di compilazione, come null. Il compilatore sostituirà effettivamente ogni riferimento al campo con la costante.

Ciò interromperà l'inizializzazione in quanto gli oggetti non terrebbero più un riferimento allo Stream ma a null. Assegnare il flusso al ritorno di un metodo impedisce l'inlining.

Alcuni potrebbero chiamarlo un hack sporco. Per menzionare in modo errato Bismarck "Il JDK è come le salsicce, è meglio non vederlo fatto".

+0

Anche se come commentato in precedenza, in Java 7 è stato corretto, quindi deve fare con compilatori più vecchi come indicato da Marko. – assylias

+0

Potrebbero aver definito 'public finale statico PrintStream out = null;'. Aiutare a capire il campo finale statico "lnline". – AmitG

+2

@AmitG dai un'occhiata a [this] (http://stackoverflow.com/questions/5173372/java-static-final-values-replaced-in-code-when-compiling) e [this] (http: // docs.oracle.com/javase/specs/jls/se7/html/jls-13.html#jls-13.4.9). Se si definisce qualcosa come costante di tempo di compilazione, il compilatore (Java 6) utilizzerà tali informazioni per ottimizzare il codice: sostituirà qualsiasi riferimento con il letterale. Quindi tutto ciò che diceva 'System.out' sarebbe stato sostituito da' null' dal compilatore in quanto si presuppone che 'System.out' non possa essere modificato poichè è impostato su' null' ed è 'final'. –

2

Questo è il modo in cui la classe System.out viene inizializzata.

C'è anche un metodo:

private static native void setOut0(PrintStream out); 

che è chiamato nel seguente metodo:

private static void initializeSystemClass() { 
2

System.in, out e err vengono gestiti dalla JVM dal codice nativo. Tutta questa magia con nullPrintStream() era lì per impedire a javac di allineare questi campi. Poiché java 7 sembra

public final static PrintStream out = null; 
Problemi correlati