2010-05-09 7 views
7

ejemplo clásico de un servidor simple:¿Cuál es el efecto de la declaración de variable final en los métodos?

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(); 
     } 
    } 
} 

¿Por qué el Socket ser declarado como final? ¿Es porque el nuevo Thread que maneja la solicitud podría hacer referencia a la variable socket en el método y causar algún tipo de ConcurrentModificationException?

Respuesta

13

En este caso, la variable debe ser definitiva para ser utilizada dentro de la implementación anónima Runnable.

Esto se debe a que ese objeto existirá cuando la variable ya haya salido del alcance y haya desaparecido. El objeto obtiene una copia de la variable. Para ocultar esto, la variable debe ser definitiva, de modo que nadie pueda esperar que los cambios en una copia sean visibles para la otra.

+0

Muchas gracias. – Finbarr

2

Debe declarar que es definitiva, no solo debe. Sin eso, el compilador no puede usarlo en la implementación anónima de la clase Runnable.

0

Las variables locales no se comparten entre subprocesos. (Una variable local es parte del registro de activación, y cada hilo tiene su propio registro de activación).

Dado que connection es una variable local, no es posible compartirla entre subprocesos. Como no se comparte entre subprocesos, debe hacerlo final, por lo que no importa que sea una variable local (se puede ver más como un valor constante).

0

No está destinado a resolver ConcurrentModificationException. Cualquier variable local utilizada dentro de una clase anidada por método (como una clase interna anónima) debe declararse como final. Ver una discusión similar a partir de la última semana aquí:

method local innerclasses accessing the local variables of the method

En realidad, en el caso de hilos no es una contribución menor aquí para hilo de seguridad; no habrá problemas de visibilidad en la variable final entre los hilos. Sin embargo, esto no garantiza la seguridad del hilo en absoluto.

3

Considere este ejemplo:

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(); 

Si c era no final, cuando llegue a b.goo(), sería apuntar a la chatarra, ya que c sería recolección de basura - Una variable local después del final de una llamada a un método.

+0

¿Implica que las variables finales no se recogen en la basura? Eso es falso. – Pindatjuh

+0

El problema NO es la recolección de basura. La clase interna tiene una fuerte referencia a la variable de todos modos. Los diseñadores del lenguaje podrían haber permitido el uso de variables locales no finales dentro de las clases internas. Sin embargo, esto podría ser confuso para el desarrollador (ver la respuesta de Michaels), ya que no estaría claro si las asignaciones a la referencia son vistas por la clase interna o no. –

+0

@Pindatjuh - Estoy implicando que las variables finales no se recogen basura * cuando el método finaliza *. @Eyal - Tiene razón, el idioma podría haber sido mejor diseñado, etc. Sin embargo, el diseño actual es el que tenemos ... –

2

declarar una variable de método final significa que su valor no puede cambiar; que solo se puede establecer una vez ¿Cómo se aplica eso en este contexto?

he sabido de esta restricción con las clases anónimas durante algún tiempo, pero nunca entendí por qué. veo que nadie más realmente lo hace de las respuestas hasta ahora. un poco de google apareció el siguiente que creo que hace un buen trabajo al explicarlo.

Una clase local anónimo puede utilizar variables locales porque el compilador da automáticamente la clase de un campo de instancia privada para mantener una copia de cada variable local utiliza la clase. El compilador también agrega los parámetros ocultos a cada constructor para inicializar estos campos privados creados automáticamente. Por lo tanto, una clase local no tiene acceso a las variables locales , sino simplemente a sus propias copias privadas de . La única forma en que esto puede funcionar correctamente es si las variables locales se declaran definitivas, de modo que se garantiza que no cambien . Con esta garantía, la clase local está segura de que sus copias internas de las variables reflejan con exactitud las variables locales reales .

de crédito a: http://renaud.waldura.com/doc/java/final-keyword.shtml#vars

ciertamente no es obvio y es algo que creo que el compilador realmente debería estar escondido de los desarrolladores.

+0

Gran respuesta, poco técnica. Me gusta agregar ** why ** necesita esa garantía: porque la clase interna puede ser GCed y si la variable local cambia más adelante y el cambio debe reflejarse en la instancia de clase interna, que es GCed: un problema evitado si la variable es final. – Pindatjuh

+0

'declarar una variable de método final significa que su valor no puede cambiar; que solo se puede establecer una vez' Realmente no. Significa que la ** referencia ** al objeto no se puede cambiar. El valor aún se puede cambiar varias veces. (A menos que el objeto sea primitivo) – mercury0114

Cuestiones relacionadas