Clase final
Cuando una clase se define como una clase final, significa que la clase no puede ser heredada por otras clases, es decir, no se puede usar después de extensiones. De lo contrario, recibirá un error durante la compilación.
paquete com.iderzheng.finalKeyword; Public Final Clase FinalClass {} // Error: no se puede heredar de la Clase FinalClass extiende FinalClass {} Java apoya la definición de la clase como final, que parece violar los principios básicos de la programación orientada a objetos. Sin embargo, por otro lado, la clase cerrada también asegura que todos los métodos de la clase estén fijos y no habrá anulaciones de subclase que se cargarán dinámicamente. Esto proporciona más posibilidades para que el compilador optimice. El mejor ejemplo es String, que es la clase final. El compilador Java puede girar directamente las constantes de cadena (las contenidas en citas dobles) en objetos de cadena, y al mismo tiempo optimizar directamente el operador + operación en nuevas constantes, porque la modificación final garantiza que no hay subclases devuelvan diferentes valores para las operaciones de empalme.
Para todas las definiciones de clase diferentes: las clases de nivel superior (global o visible), las clases anidadas (clases anidadas internas o estáticas) pueden modificarse con final. Sin embargo, en general, Final se usa principalmente para modificar las clases definidas como públicas, porque para las clases no globales, los modificadores de acceso han restringido su visibilidad, y ya es difícil heredar estas clases, por lo que no hay necesidad de agregar una capa de restricciones finales.
También se menciona que aunque las clases anónimas no pueden ser heredadas, el compilador no las limita a las finales.
import java.lang.reflect.modifier; public class Main {public static void main (string [] args) {runnable anyonymous = new runnable () {@Override public void run () {}}; System.out.println (modificador.isfinal (anónimo.getclass (). GetModifiers ())); }}Producción:
FALSO
Método final
Estrechamente relacionado con el concepto de herencia está el polimorfismo, que implica la diferencia entre los conceptos de anulación y ocultación (por conveniencia, lo siguiente se llaman colectivamente "reescritura"). Sin embargo, a diferencia de la definición del método en C ++, si se agrega la palabra clave virtual a la subclase, si el método de firma de subclase está sobrescribida o oculta, en Java, las subclases usan la misma firma del método para sobrescribir el método de clase principal, que formará un método de clase oculto (método estático), mientras que los métodos de objetos (método no estático) solo sobrescribe. Dado que Java permite el acceso directo a los métodos de clase a través de objetos, Java no permite que los métodos de clase y los métodos de objetos tengan la misma firma en la misma clase.
La clase final define que toda la clase no puede ser heredada, y también significa que todos los métodos en la clase no pueden cubrirse y ocultar por subclases. Cuando la clase no se modifica por final, algunos métodos aún pueden modificarse utilizando final para evitar que estos métodos sean reescritos por subclases.
Del mismo modo, dicho diseño destruye el polimorfismo orientado a objetos, pero el método final puede garantizar el determinismo de su ejecución, asegurando así la estabilidad de las llamadas del método. En algunos diseños de marco, algunos métodos implementados de clases abstractas a menudo se limitan a la final, porque algún código del conductor en el marco dependerá de estos métodos para lograr los objetivos establecidos, por lo que no hay subclases que lo cubran.
El siguiente ejemplo muestra el papel de la modificación final en diferentes tipos de métodos:
paquete com.iderzheng.other; public class FinalMethods {public static void publicSstaticMethod () {} public final void publicFinalMethod () {} public static final void publicSstaticFinalMethod () {} protegido final void protegidofinalMethod () {} protegido Estatico final void protegido protegido void staticFinalMethod () {} private estático final void privateStaticFinalMethod () {} private final void privateFinalMethod () {}} paquete com.iderzheng.finalKeyword; import com.iderzheng.other.finalMethods; Los métodos de clase pública extienden FinalMethods {public static void publicStaticMethod () {} // Error: no se puede anular public Final Void publicFinalMethod () {} // Error: no puede superar el público estatico de publicidad estatica PublicStaticFinalMethod () {} // Error: no se puede anular el error de PublicStaticFinalMethodmethod de Finalmethin -} /} /}. void publicstaticFinalMethod () {} // Error: no se puede anular protegido final void protegedFinalMethod () {} // Error: no se puede anular el void final estático final protegido ProtectedStaticFinalMethod () {} final Void final FinalMethod () {{} Final voide estático estático videfinalmethod () {}}}}}}} {}}} {} privado Void estaticfal de void static. {} private final void privateFinalMethod () {}} En primer lugar, tenga en cuenta que en el ejemplo anterior, los modos finales y los métodos se definen en diferentes paquetes. Para el primer PublicStaticMethod, la subclase reescribe con éxito el método estático de la clase principal, pero debido a que es un método estático, lo que sucede está en realidad "oculto". Específicamente, llamar métodos.publicstaticMethod () ejecutará la implementación en la clase de métodos. Al llamar a FinalMethods.PublicStaticMethod (), la implementación no ocurrirá con la carga polimórfica de la subclase, pero utilizará directamente la implementación de FinalMethods. Por lo tanto, cuando se usa subclases para acceder a los métodos, la visibilidad de los métodos firmados por la clase principal está oculta.
Para el método global de PublicFinalMethod, como se describe en el método de modificación final, la subclase está prohibido sobrescribirlo, y se lanzará una excepción en el momento de la compilación. Sin embargo, el nombre del método es el mismo en la subclase, pero tiene un parámetro, como: PublicFinalMethod (String X) está bien, porque esta es la firma del método sincrónico.
En IntelliJ, el IDE muestra una advertencia al PublicStaticFinalMethod: Método 'estático' declarado 'final'. Parece redundante, pero según el ejemplo, se puede ver que Final también prohíbe las definiciones de subclase de los métodos estáticos para ocultarlo. En el desarrollo real, el comportamiento de definir los mismos métodos estáticos de subclases y clases de padres es extremadamente deseable, ya que los métodos ocultos requieren que los desarrolladores presten atención al uso de diferentes nombres de clases para definir diferentes efectos, lo que es fácil de causar errores. Además, dentro de la clase, puede llamar a métodos estáticos directamente sin usar el nombre de la clase. Cuando el desarrollador hereda nuevamente, es posible que no note la existencia oculta. Por defecto, cuando use el método de clase principal, encontrará que no es el resultado esperado. Por lo tanto, los métodos estáticos deben ser finales por defecto y no deben ocultarse, por lo que IDE cree que es una modificación innecesaria.
Los métodos de modificación y modificación pública protegidas en la clase principal son visibles para la subclase, por lo que la situación de modificación final de los métodos protegidos es la misma que la de los métodos públicos. Se debe mencionar que en el desarrollo real, los métodos estáticos protegidos generalmente rara vez se definen porque tales métodos son demasiado prácticos.
Para el método del paquete de clase principal, las subclases en diferentes paquetes son invisibles. El método privado ha sido personalizado y solo la clase principal puede acceder a él. Entonces, el compilador permite que las subclases definan el mismo método. Pero esto no forma anulación u esconderse, porque la clase principal ha ocultado estos métodos a través de modificadores, no causados por la reescritura de subclases. Por supuesto, si la subclase y la clase principal están en el mismo paquete, entonces la situación será la misma que el público anterior y protegido.
¿Por qué el método final es eficiente?
El método final utilizará el mecanismo integrado para optimizar en línea durante la compilación. La optimización en línea se refiere a: llamar al reemplazo del código de función directamente durante la compilación, en lugar de llamar a las funciones en tiempo de ejecución. En línea necesita saber qué función usar al final al compilar. Obviamente, no es posible usarlo sin final. Los métodos no financieros pueden reescribirse en subclases. Debido al posible polimorfismo, el compilador no puede determinar el tipo verdadero del objeto para llamar al método en el futuro durante la etapa de compilación, y no puede determinar qué método llamar.
Variable final
En pocas palabras, la variable final en Java solo puede y debe inicializarse una vez, y luego la variable está vinculada al valor. Sin embargo, esta asignación no necesariamente debe inicializarse inmediatamente cuando se define la variable. Java también admite diferentes resultados para las variables finales a través de declaraciones condicionales, pero la variable solo se puede asignar una vez en cualquier caso.
Sin embargo, la variable final de Java no es una constante absoluta, porque las variables de objeto de Java son solo valores de referencia, por lo que final solo significa que la referencia no se puede cambiar, y el contenido del objeto aún se puede modificar. En comparación con los punteros C/C ++, es más parecido a la variable de tipo * const que la variable const *.
Las variables Java se pueden dividir en dos categorías: variables locales (variable local) y variables de miembros de clase (campo de clase). El siguiente es un código para introducir su situación de inicialización por separado.
Variable local
Las variables locales se refieren principalmente a variables definidas en los métodos. Desaparecerán y se volverán inaccesibles después del método. Hay un caso especial que se puede dividir en: parámetros de función. Para este caso, su inicialización está vinculada a los parámetros pasados cuando se llama a la función.
Para otras variables locales, se definen en el método, y sus valores se pueden inicializar condicionalmente:
Método de cadena pública (final Boolean FinalParam) {// Error: el parámetro final final no se puede asignar // finalParam = true; Objeto final Finallocal = finalParam? nuevo objeto (): nulo; final int finalvar; if (finallocal! = null) {finalVar = 21; } else {finalVar = 7; } // Error: la variable FinalVar ya podría haber sido asignada // FINALVAR = 80; Cadena final final; Switch (FinalVar) {Caso 21: FinalRet = "ME"; romper; Caso 7: FinalRet = "Ella"; romper; Predeterminado: FinalRet = NULL; } return FinalRet;} Del ejemplo anterior, se puede ver que los parámetros de función modificados por final no se pueden asignar un nuevo valor, pero a otras variables locales finales se les puede asignar un valor en una declaración condicional. Esto también proporciona una cierta flexibilidad para final.
Por supuesto, todas las condiciones en la declaración condicional deben contener asignaciones a las variables locales finales, de lo contrario, obtendrá un error de que la variable no se puede inicializar.
Método de cadena pública (objeto final finalparam) {final int finalVar; if (finalParam! = NULL) {FINALVAR = 21; } Cadena final final; // Error: la variable FinalVar podría no haber sido inicializado Switch (FinalVar) {Case 21: FinalRet = "ME"; romper; Caso 7: FinalRet = "Ella"; romper; } // Error: la variable FinalRet podría no haberse inicializado return FinalRet;} En teoría, las variables locales no son necesarias para definirse como finales, y un método de diseño razonable debería poder mantener bien las variables locales. Es solo que cuando se usa funciones anónimas para hacer cierres en los métodos Java, Java requiere que la variable local referenciada se define como final:
Método public runnable (cadena de cadena) {int Integer = 12; return new runnable () {@Override public void run () {// Error: debe declararse final System.out.println (String); // Error: debe declararse final System.out.println (Integer); }};}Campo de clase
Las variables de miembros de la clase se pueden dividir en dos tipos: estática y no estática. Para las variables de miembros de la clase estática, debido a que están relacionadas con las clases, además de ser inicializadas directamente en el momento de la definición, también se pueden colocar en un bloque estático, y el uso de este último puede ejecutar declaraciones más complejas:
paquete com.iderzheng.finalKeyword; import java.util.hashset; import java.util.linkedhashset; import java.util.set; clase pública staticfinalfields {static final int static_final_init_inline = 7; Establecimiento final estático <integer> static_final_init_static_block; / ** bloque estático **/ static {if (System.CurrentTimemillis () % 2 == 0) {static_final_init_static_block = new Hashset <> (); } else {static_final_init_static_block = new LinkedHashset <> (); } Static_final_init_static_block.add (7); Static_final_init_static_block.add (21); }}También hay bloques no estáticos en Java que pueden inicializar las variables de miembros no estáticos, pero para estas variables, a menudo se colocan en el constructor para la inicialización. Por supuesto, es necesario asegurarse de que cada variable final se inicialice una vez en el constructor. Si se llama a otros constructores a través de este (), estas variables finales ya no se pueden asignar en el constructor.
paquete com.iderzheng.finalKeyword; clase pública finalfields {final long final_init_inline = system.currentTimemillis (); final largo final_init_block; final largo final_init_constructor; / ** bloque inicial **/ {final_init_block = system.nanotime (); } Finalfields () {this (217); } FinalFields (boolean bool) {final_init_constructor = 721; } FinalFields (Long Init) {Final_init_Constructor = init; }}Cuando se usa final para modificar las clases (clase) y los métodos (método), afecta principalmente a la herencia orientada a objetos. Sin herencia, no habrá dependencia del código de la subclase en la clase principal. Por lo tanto, al modificar el código durante el mantenimiento, no necesita considerar si la implementación de la subclase será destruida, lo que lo hace más conveniente. Cuando se usa en una variable, Java asegura que el valor variable no se modifique. Si un diseño adicional asegura que los miembros de la clase no se puedan modificar, entonces toda la variable puede convertirse en una constante, lo cual es muy beneficioso para la programación de múltiples subprocesos. Por lo tanto, Final tiene un muy buen efecto en el mantenimiento del código.