Java garantisce di incorporare costanti di stringa se possono essere determinate al momento della compilazione

StackOverflow https://stackoverflow.com/questions/1406616

  •  05-07-2019
  •  | 
  •  

Domanda

Considera questo caso:

public Class1 {
   public static final String ONE = "ABC";
   public static final String TWO = "DEF";
}

public Class2 {

  public void someMethod() {
    System.out.println(Class1.ONE + Class1.TWO);
  }
}

In genere ci si aspetta che il compilatore includa le costanti ONE e TWO. Tuttavia, questo comportamento è garantito? È possibile distribuire in runtime Class2 senza Class1 nel percorso di classe e aspettarsi che funzioni indipendentemente dai compilatori o si tratta di un'ottimizzazione del compilatore opzionale?

EDIT: Perché mai farlo? Bene, ho una costante che sarebbe condivisa tra le due estremità di un'applicazione (client e server su RMI) e sarebbe molto conveniente in questo caso particolare mettere la costante su una classe che può essere solo su un lato di quella divisione ( poiché è logicamente quello che possiede quel valore costante) piuttosto che averlo in una classe di costanti arbitrarie solo perché deve essere condiviso da entrambi i lati del codice. Al momento della compilazione è tutto un set di file di origine, ma al momento della compilazione è diviso per pacchetto.

È stato utile?

Soluzione

È garantito che sia trattato come un'espressione costante e garantito da internato da sezione 15.28 del JLS :

  

Un'espressione costante durante la compilazione è   un'espressione che indica un valore di   tipo primitivo o una stringa che lo fa   non si completa bruscamente ed è composto   utilizzando solo quanto segue:

     
      
  • Letterali di tipo primitivo e letterali di tipo String (& # 167; 3.10.5)
  •   
  • Lancia a tipi primitivi e lancia per digitare String
  •   
  • Gli operatori unari +, -, ~ e! (ma non ++ o -)
  •   
  • Gli operatori moltiplicativi *, / e%
  •   
  • Gli operatori additivi + e -
  •   
  • ...
  •   

...

  

Costanti in fase di compilazione di tipo String   sono sempre "internati" in modo da condividere   istanze uniche, usando il metodo   String.Intern.

Ora, ciò non significa che sia garantito che sia inline. Tuttavia, la sezione 13.1 delle specifiche dice:

  

Riferimenti a campi che sono costanti   le variabili (& # 167; 4.12.4) vengono risolte in   compilare il tempo al valore costante   questo è indicato. Nessun riferimento a tale   un campo costante dovrebbe essere presente in   il codice in un file binario (tranne in   la classe o l'interfaccia contenente il file   campo costante, che avrà codice   per inizializzarlo) e tale costante   i campi devono sempre essere stati   inizializzato; il valore iniziale predefinito   per il tipo di tale campo deve   non essere mai osservato.

In altre parole, anche se l'espressione stessa non era una costante , non dovrebbe esserci alcun riferimento a Class1 . Quindi sì, stai bene. Ciò non necessariamente garantisce che il valore concatenato sia utilizzato nel bytecode, ma i bit a cui fa riferimento in precedenza garantiscono che il valore concatenato viene internato, quindi sarei enormemente sorpreso se non solo incorporasse il valore concatenato. Anche se non funziona, sei sicuro che funzionerà senza Class1 .

Altri suggerimenti

Compilando che con javac 1.6.0_14 si ottiene il seguente bytecode:

public void someMethod();
  Code:
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc     #3; //String ABCDEF
   5:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return

Quindi le stringhe vengono concatenate al momento della compilazione e il risultato è incluso nel pool costante di Class2.

Non sarà integrato dal compilatore ma dall'interprete in fase di esecuzione e, se possibile, convertito in codice assembly.

Non può essere garantito, perché non tutti gli interpreti (JVM) funzionano allo stesso modo. Ma le implementazioni più importanti lo faranno.

Purtroppo non ho un link per sostenere questo :(

Sospetto, ma non so per certo, che funzionerà, ma non sembra una buona idea.

Il "normale" modi per farlo sono:

  1. Metti le costanti in un pacchetto condiviso tra il client e il server. Presumibilmente, esiste un pacchetto del genere, perché è lì che vanno le interfacce.
  2. Se non esiste un pacchetto simile, creare 2 classi con le costanti condivise: una per il server e una per il client.

Vedi JLS 13.4.9 . Sebbene non richieda esplicitamente che le costanti siano incorporate dal compilatore, suggerisce che la compilazione condizionale e il supporto per le costanti nelle istruzioni switch inducono sempre il compilatore a incorporare costanti.

Sembra che tu stia codificando la tua versione della funzionalità integrata in enum , che public static final per te, denominazione corretta tramite name ( ) e toString () (oltre ad avere alcuni altri vantaggi, ma forse avere lo svantaggio di un footprint di memoria maggiore).

Stai usando una versione precedente di Java che non include ancora enum?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top