2011-12-13 11 views
11

Per impostazione predefinita, Sun's JVM carica pigramente le classi e inizializza pigramente (ad esempio chiama i loro metodi <clinit>). Considerare la seguente classe, ClinitBomb, che genera un Exception durante un blocco static{}.Come si disabilita il caricamento/inizializzazione della classe lazy in JVM di Sun?

public class ClinitBomb { 
    static { 
     explode(); 
    } 
    private static void explode() { 
     throw new RuntimeException("boom!"); 
    }  
} 

Ora, considerare come attivare la bomba:

public class Main { 
    public static void main(String[] args) { 
     System.out.println("A"); 
     try { 
      Class.forName("ClinitBomb"); 
     } catch (Exception e) { 
      e.printStackTrace(System.out); 
     } 
     System.out.println("B"); 
     ClinitBomb o2 = new ClinitBomb(); 
     System.out.println("C"); 
    } 
} 

Siamo garantito l'esplosione avviene prima del punto B, dal momento che forName 's documentazione dice così; la questione è se accade prima del punto A (quando Main viene caricato.) In JVM di Sun, anche se main() contiene un riferimento statico per ClinitBomb, succede dopo A.

Voglio un modo per dire alla JVM di caricare e inizializza ClinitBomb non appena inizializza Main (quindi la bomba esplode prima del punto A.) ​​In generale, voglio un modo per dire "ogni volta che carico/inizializzo la classe X, lo faccio anche per qualsiasi classe Y che fa riferimento".

C'è un modo per farlo?

+0

Un blocco statico riferito a ClinitBomb in Main? –

+0

@ Thorbjørn Ravn Andersen che non risolverà il problema generale. Ma suppongo che un programma di caricamento classi personalizzato possa iniettare blocchi statici in ogni classe caricata. – emory

+0

A corto di aggiungere una lista enorme di 'Class.forName()' Non lo so. OTOH, perché è così importante? – Bill

risposta

8

Non c'è modo di farlo. Il JLS dice, in §12.4.1 When Initialization Occurs (sottolineatura mia):

inizializzazione di una classe consiste di eseguire i suoi inizializzatori statici e gli inizializzatori per i campi statici dichiarati nella classe. [...]

Una classe o interfaccia tipo T vengono inizializzate immediatamente prima del primo verificarsi di uno qualsiasi dei seguenti:

  • T è una classe e viene creata un'istanza di T.
  • T è una classe e viene invocato un metodo statico dichiarato da T.
  • Viene assegnato un campo statico dichiarato da T.
  • Viene utilizzato un campo statico dichiarato da T e il campo non è una variabile costante (§4.12.4).
  • T è una classe di livello superiore e un'istruzione di asserzione (§14.10) annidata lessicale in T viene eseguita.

L'invocazione di alcuni metodi riflettenti nella classe Class e nel pacchetto java.lang.reflect provoca anche l'inizializzazione della classe o dell'interfaccia. Una classe o un'interfaccia non verrà inizializzata in nessun'altra circostanza.

Un'implementazione Java che inizializzava le classi non appena venivano caricate avrebbe violato il JLS.

Anche se quello che si potrebbe fare sarebbe quella di utilizzare la JVM instrumentation API per scrivere un ClassFileTransformer che ha aggiunto un blocco statico per ogni classe che inizializzata esplicitamente le sue classi di riferimento (tramite Class.forName, probabilmente). Non appena una classe viene inizializzata, tutte le classi raggiungibili da essa verranno inizializzate.Questo potrebbe darti il ​​risultato che cerchi. Comunque è un bel po 'di lavoro!

+1

Grazie. (Inoltre, sarebbe stato così comodo per il debugging in questo caso.Ho ereditato un codebase in cui la maggior parte delle classi dichiara un campo 'Logger finale statico', che è ottimo, tranne che sono creati usando un'infrastruttura di logging che colpisce up RMI. Quindi, in pratica, in punti ragionevolmente arbitrari in esecuzione, improvvisamente hai chiamate remote in cui un errore ti dà un 'ExceptionInInitializerError'. Non suona divertente?) – jon

+0

Hmm .. curiosamente, la classe di ClinitBomb non è nemmeno * caricato * fino a forName(), che sembra davvero strano. (Ovvero: rimuovere ClinitBomb.class consente ancora di stampare la "A" principale prima di lanciare un NoClassDefFoundError.) Secondo JLS 12.1.2, dipende dall'implementazione; qualche idea su come modificare il comportamento predefinito in Sun JVM? – jon

+0

Non ne sono a conoscenza. Esegui 'java -XX: + UnlockDiagnosticVMOptions -XX: + PrintFlagsFinal' e vedi se qualcosa suggerisce se stesso - non ho potuto vedere nulla con il JRE che ho. –

1
Class.forName("...", true /*initialize*/, getClassLoader()); 

Eri a metà strada.

+0

Puoi usare il tuo Classgerer. –

+0

No. Il codice che ha scritto inizializzerà 'ClinitBomb' nella chiamata' forName'; la versione [single-argument di forName] (http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#forName%28java.lang.String%29) "è equivalente a : Class.forome (className, true, currentLoader) ". Il suo desiderio è far sì che 'ClinitBomb 'sia inizializzato anche prima. –

+0

@ Tom Anderson. Sono corretto. –

Problemi correlati