2010-05-09 29 views
7

classico esempio di un server semplice:Qual è l'effetto della dichiarazione della variabile finale nei metodi?

class ThreadPerTaskSocketServer { 
    public static void main(String[] args) throws IOException { 
     ServerSocket socket = new ServerSocket(80); 
     while (true) { 
      final Socket connection = socket.accept(); 
      Runnable task = new Runnable() { 
       public void run() { 
       handleRequest(connection); 
       } 
      }; 
      new Thread(task).start(); 
     } 
    } 
} 

Perché il Socket essere dichiarato come final? È perché il nuovo Thread che gestisce la richiesta potrebbe fare riferimento alla variabile socket nel metodo e causare una sorta di ConcurrentModificationException?

risposta

13

In questo caso, la variabile deve essere finale da utilizzare all'interno dell'impianto anonimo Runnable.

Questo perché l'oggetto esiste quando la variabile è già uscita dall'ambito e quindi è scomparsa. L'oggetto ottiene una copia della variabile. Per nascondere questo, la variabile deve essere definitiva in modo che nessuno possa aspettarsi che i cambiamenti di una copia siano visibili all'altro.

+0

Grazie mille. – Finbarr

2

Devi dichiararlo definitivo, non solo dovrebbe. Senza di ciò, il compilatore non può usarlo nell'esecuzione anonima della classe Runnable.

0

Le variabili locali non vengono condivise tra i thread. (Una variabile locale è una parte del record di attivazione e ogni thread ha il proprio record di attivazione).

Poiché connection è una variabile locale, non è possibile condividerlo tra thread. Poiché non è condiviso tra thread, è necessario renderlo final, quindi non importa che sia una variabile locale (può essere vista più come un valore costante).

0

Non ha lo scopo di risolvere ConcurrentModificationException. Qualsiasi variabile locale utilizzata all'interno di un metodo - classe nidificata (come una classe interna anonima) deve essere dichiarata come definitiva. Vedere una discussione simile dall'ultima settimana qui:

method local innerclasses accessing the local variables of the method

In realtà nel caso di fili c'è un contributo minore qui per filo di sicurezza; non ci saranno problemi di visibilità sulla variabile finale tra i thread. Tuttavia, questo non garantisce la sicurezza del filo.

3

Considerate questo esempio:

class A { 
    B foo() { 
    final C c; 
    return new B() { 
     void goo() { 
     // do something with c 
     } 
    } 
    } 
} 
// somewhere else in the code 
A a = new A(); 
B b = a.foo(); 
b.goo(); 

Se c era non finale, quando si raggiunge b.goo(), sarebbe puntare a spazzatura, dato che c sarebbe garbage collection - Una variabile locale dopo la fine della una chiamata al metodo.

+0

Sottintendi che le variabili finali non vengono raccolte? Questo è falso. – Pindatjuh

+0

Il problema NON è la garbage collection. La classe interiore ha comunque un forte riferimento alla variabile. I progettisti linguistici avrebbero potuto consentire l'utilizzo di variabili locali non finali all'interno delle classi interne. Tuttavia, ciò potrebbe creare confusione per lo sviluppatore (vedere la risposta di Michaels), poiché non sarebbe chiaro se gli incarichi di riferimento siano visti dalla classe interna o meno. –

+0

@Pindatjuh - Sto insinuando che le variabili finali non si riempiono di spazzatura * quando il metodo finisce *. @Eyal - Hai ragione, il linguaggio avrebbe potuto essere progettato meglio, ecc. Tuttavia, il design attuale è quello che abbiamo ... –

2

dichiarare una variabile di metodo finale significa che il suo valore non può cambiare; che può essere impostato solo una volta. come si applica in questo contesto?

conosco da tempo questa restrizione con classi anonime, ma non ho mai capito bene perché. vedo che nessun altro fa realmente le risposte finora. alcuni googling hanno alzato il sotto che penso faccia un buon lavoro di spiegarlo.

Una classe locale anonimo può utilizzare variabili locali perché il compilatore dà automaticamente la classe di un campo privato un'istanza di tenere una copia di ciascuna variabile locale la classe utilizza. Il compilatore aggiunge anche i parametri nascosti a ciascun costruttore su inizializza questi campi privati ​​creati automaticamente . Pertanto, una classe locale non accede effettivamente alle variabili locali ma solo alle sue copie private di . L'unico modo in cui questo può funzionare è possibile se le variabili locali sono dichiarate definitive, in modo che siano garantite per non cambiare. Con questa garanzia in vigore, la classe locale è sicura che le sue copie interne riflettono con precisione le attuali variabili locali .

di credito a: http://renaud.waldura.com/doc/java/final-keyword.shtml#vars

certamente non ovvio e qualcosa che penso che il compilatore in realtà dovrebbe essere nascosto da parte degli sviluppatori.

+0

Ottima risposta, un po 'tecnico. Mi piace aggiungere ** perché ** ha bisogno di tale garanzia: perché la classe interna potrebbe essere GCed e se la variabile locale cambia in seguito e la modifica deve essere riflessa sull'istanza della classe interna, che è GCed: un problema evitato se la variabile è definitiva. – Pindatjuh

+0

'dichiarare una variabile di metodo final significa che il suo valore non può cambiare; che può essere impostato solo una volta. Non proprio. Significa che il ** riferimento ** all'oggetto non può essere cambiato. Il valore può ancora essere modificato più volte. (A meno che l'oggetto non sia una primitiva) – mercury0114

Problemi correlati