En Java vemos muchos lugares donde el final
se puede usar palabra clave pero su uso es poco común.
Por ejemplo:
String str = "abc";
System.out.println(str);
En el caso anterior, str
puede ser, final
pero esto comúnmente se deja fuera.
Cuando un método nunca se va a anular, podemos usar la palabra clave final. Del mismo modo, en el caso de una clase que no se va a heredar.
¿El uso de la palabra clave final en alguno o en todos estos casos realmente mejora el rendimiento? Si es así, ¿cómo? Por favor explique. Si el uso adecuado de final
realmente es importante para el rendimiento, ¿qué hábitos debe desarrollar un programador de Java para hacer un mejor uso de la palabra clave?
Respuestas:
Usualmente no. Para los métodos virtuales, HotSpot realiza un seguimiento de si el método realmente se ha anulado y puede realizar optimizaciones, como la inclusión en el supuesto de que un método no se ha anulado, hasta que carga una clase que anula el método, en ese momento Puede deshacer (o deshacer parcialmente) esas optimizaciones.
(Por supuesto, esto supone que está utilizando HotSpot, pero es, con mucho, la JVM más común, así que ...)
En mi opinión, debe usarlo con
final
base en un diseño claro y legibilidad en lugar de por razones de rendimiento. Si desea cambiar algo por razones de rendimiento, debe realizar las medidas adecuadas antes de deformar el código más claro, de esa manera puede decidir si un rendimiento adicional alcanzado vale la legibilidad / diseño más pobre. (En mi experiencia, casi nunca vale la pena; YMMV).EDITAR: Como se han mencionado los campos finales, vale la pena mencionar que a menudo son una buena idea de todos modos, en términos de diseño claro. También cambian el comportamiento garantizado en términos de visibilidad de hilos cruzados: después de que un constructor se ha completado, se garantiza que cualquier campo final sea visible en otros hilos inmediatamente. Este es probablemente el uso más común
final
en mi experiencia, aunque como partidario de la regla empírica "diseñar para heredar o prohibir" de Josh Bloch, probablemente debería usarlofinal
más a menudo para las clases ...fuente
final
generalmente se recomienda porque hace que el código sea más fácil de entender y ayuda a encontrar errores (porque hace explícita la intención de los programadores). PMD probablemente recomienda usarlofinal
debido a estos problemas de estilo, no por razones de rendimiento.Immutable classes are easier to design, implement, and use than mutable classes. They are less prone to error and are more secure.
. AdemásAn immutable object can be in exactly one state, the state in which it was created.
vsMutable objects, on the other hand, can have arbitrarily complex state spaces.
. Desde mi experiencia personal, el uso de la palabra clavefinal
debería resaltar la intención del desarrollador de inclinarse hacia la inmutabilidad, no para "optimizar" el código. ¡Te animo a leer este capítulo, fascinante!final
palabra clave en las variables podría reducir la cantidad de bytecode, lo que puede producir un efecto en el rendimiento.Respuesta corta: ¡no te preocupes por eso!
Respuesta larga:
Al hablar sobre las variables locales finales, tenga en cuenta que el uso de la palabra clave
final
ayudará al compilador a optimizar el código estáticamente , lo que al final puede resultar en un código más rápido. Por ejemplo, las cadenas finalesa + b
en el ejemplo a continuación se concatenan estáticamente (en tiempo de compilación).¿El resultado?
Probado en Java Hotspot VM 1.7.0_45-b18.
Entonces, ¿cuánto es la mejora del rendimiento real? No me atrevo a decir En la mayoría de los casos, probablemente marginal (~ 270 nanosegundos en esta prueba sintética porque la concatenación de cadenas se evita por completo, un caso raro), pero en un código de utilidad altamente optimizado podría ser un factor. En cualquier caso, la respuesta a la pregunta original es sí, podría mejorar el rendimiento, pero marginalmente en el mejor de los casos .
Dejando de lado los beneficios en tiempo de compilación, no pude encontrar ninguna evidencia de que el uso de la palabra clave
final
tenga un efecto medible en el rendimiento.fuente
String
la concatenación es una operación mucho más costosa que sumarIntegers
. Hacerlo estáticamente (si es posible) es más eficiente, eso es lo que muestra la prueba.Sí puede. Aquí hay una instancia donde final puede aumentar el rendimiento:
La compilación condicional es una técnica en la que las líneas de código no se compilan en el archivo de clase en función de una condición particular. Esto se puede usar para eliminar toneladas de código de depuración en una compilación de producción.
considera lo siguiente:
Al convertir el atributo doSomething en un atributo final, le ha dicho al compilador que cada vez que vea doSomething, debe reemplazarlo por falso según las reglas de sustitución en tiempo de compilación. El primer paso del compilador cambia el código a algo como esto:
Una vez hecho esto, el compilador vuelve a mirarlo y ve que hay declaraciones inalcanzables en el código. Como está trabajando con un compilador de alta calidad, no le gustan todos esos códigos de bytes inalcanzables. Entonces los elimina y terminas con esto:
reduciendo así los códigos excesivos o cualquier verificación condicional innecesaria.
Editar: como ejemplo, tomemos el siguiente código:
Al compilar este código con Java 8 y descompilarlo
javap -c Test.class
obtenemos:Podemos notar que el código compilado incluye solo la variable no final
x
. Esto prueba que las variables finales tienen un impacto en el rendimiento, al menos para este caso simple.fuente
Según IBM, no es para clases o métodos.
http://www.ibm.com/developerworks/java/library/j-jtp04223.html
fuente
Estoy sorprendido de que nadie haya publicado un código real que se descompila para demostrar que hay al menos alguna diferencia menor.
Para la referencia de este ha sido probado contra
javac
versión8
,9
y10
.Supongamos este método:
Compilar este código tal como es, produce exactamente el mismo código de byte que cuando
final
hubiera estado presente (final Object left = new Object();
).Pero este:
Produce:
Dejar
final
estar presente produce:El código se explica por sí solo, en caso de que haya una constante de tiempo de compilación, se cargará directamente en la pila de operandos (no se almacenará en la matriz de variables locales como lo hace el ejemplo anterior
bipush 12; istore_0; iload_0
), lo que tiene sentido ya que nadie puede cambiarlo.Por otro lado, por qué en el segundo caso el compilador no produce
istore_0 ... iload_0
está más allá de mí, no es que esa ranura0
se use de ninguna manera (podría reducir la matriz de variables de esta manera, pero es posible que me falten algunos detalles internos, no puedo decir con seguridad)Me sorprendió ver tal optimización, considerando lo poco que
javac
hacen los pequeños . ¿En cuanto a lo que debemos usar siemprefinal
? Ni siquiera voy a escribir unaJMH
prueba (que quería inicialmente), estoy seguro de que la diferencia está en el orden dens
(si es posible que se capture). El único lugar en el que esto podría ser un problema es cuando un método no se puede alinear debido a su tamaño (y declararfinal
reduciría ese tamaño en unos pocos bytes).Hay dos
final
s más que deben abordarse. Primero es cuando un método esfinal
(desde unaJIT
perspectiva), tal método es monomórfico , y estos son los más queridos por elJVM
.Luego hay
final
variables de instancia (que deben establecerse en cada constructor); estos son importantes ya que garantizarán una referencia publicada correctamente, como se ha tocado aquí un poco y también se especifica exactamente por elJLS
.fuente
javac -g FinalTest.java
), descompilé el código usandojavap -c FinalTest.class
y nofinal int left=12
obtuve los mismos resultados (con , obtuvebipush 11; istore_0; bipush 12; istore_1; bipush 11; iload_1; iadd; ireturn
). Entonces sí, el bytecode generado depende de muchos factores y es difícil decir si tenerfinal
o no producir un efecto en el rendimiento. Pero como el código de bytes es diferente, puede existir alguna diferencia de rendimiento.Realmente está preguntando acerca de dos (al menos) casos diferentes:
final
para variables localesfinal
para métodos / clasesJon Skeet ya ha respondido 2). Alrededor de 1):
No creo que haga la diferencia; Para las variables locales, el compilador puede deducir si la variable es final o no (simplemente comprobando si está asignada más de una vez). Entonces, si el compilador quería optimizar las variables que solo se asignan una vez, puede hacerlo sin importar si la variable se declara realmente
final
o no.final
podría hacer una diferencia para los campos de clase protegida / pública; allí es muy difícil para el compilador averiguar si el campo se está configurando más de una vez, ya que podría suceder desde una clase diferente (que puede que ni siquiera se haya cargado). Pero incluso entonces, la JVM podría usar la técnica que Jon describe (optimice de manera optimista, revierta si se carga una clase que cambia el campo).En resumen, no veo ninguna razón por la que debería ayudar al rendimiento. Por lo tanto, es poco probable que este tipo de microoptimización ayude. Podrías intentar compararlo para asegurarte, pero dudo que haga una diferencia.
Editar:
De hecho, según la respuesta de Timo Westkämper, en algunos casos
final
puede mejorar el rendimiento de los campos de clase. Estoy corregido.fuente
final
, para asegurarse de que no la asigna dos veces. Hasta donde puedo ver, la misma lógica de verificación se puede (y probablemente se usa) para verificar si una variable podría serfinal
.javac
a optimizar mejor. Pero esto es solo especulación.final
variable es de un tipo o tipo primitivoString
y se le asigna inmediatamente una constante de tiempo de compilación como en el ejemplo de la pregunta, el compilador debe alinearla, ya que la variable es una constante de tiempo de compilación por especificación. Pero para la mayoría de los casos de uso, el código puede tener un aspecto diferente, pero de todos modos no importa si la constante está en línea o se lee desde una variable local en términos de rendimiento.Nota: no es un experto en Java
Si recuerdo mi Java correctamente, habría muy poca forma de mejorar el rendimiento con la palabra clave final. Siempre he sabido que existe para el "buen código": diseño y legibilidad.
fuente
No soy un experto, pero supongo que debe agregar una
final
palabra clave a la clase o método si no se sobrescribirá y dejar las variables solas. Si habrá alguna forma de optimizar tales cosas, el compilador lo hará por usted.fuente
En realidad, mientras probaba un código relacionado con OpenGL, descubrí que usar el modificador final en un campo privado puede degradar el rendimiento. Aquí está el comienzo de la clase que probé:
Y este es el método que utilicé para probar el rendimiento de varias alternativas, entre las cuales la clase ShaderInput:
Después de la tercera iteración, con la VM calentada, obtuve constantemente estas cifras sin la palabra clave final:
Con la palabra clave final:
Tenga en cuenta las cifras para la prueba ShaderInput.
No importaba si hacía los campos públicos o privados.
Por cierto, hay algunas cosas más desconcertantes. La clase ShaderInput supera a todas las demás variantes, incluso con la palabra clave final. Esto es notable b / c, básicamente es una clase que envuelve una matriz flotante, mientras que las otras pruebas manipulan directamente la matriz. Tengo que resolver esto. Puede tener algo que ver con la interfaz fluida de ShaderInput.
Además, System.arrayCopy aparentemente es algo más lento para matrices pequeñas que simplemente copiar elementos de una matriz a otra en un bucle for. Y el uso de sun.misc.Unsafe (así como el java.nio.FloatBuffer directo, que no se muestra aquí) funciona abismalmente.
fuente
Final (al menos para las variables y parámetros miembros) es más para los humanos que para la máquina.
Es una buena práctica hacer que las variables sean finales siempre que sea posible. Desearía que Java hubiera hecho "variables" finales por defecto y tuviera una palabra clave "Mutable" para permitir cambios. Las clases inmutables conducen a un código de subprocesos mucho mejor, y solo mirar una clase con "final" delante de cada miembro mostrará rápidamente que es inmutable.
Otro caso: he estado convirtiendo una gran cantidad de código para usar anotaciones @ NonNull / @ Nullable (Puedes decir que un parámetro de método no debe ser nulo, entonces el IDE puede advertirte cada lugar que pasas una variable que no está etiquetada @ No nulo: todo se extiende hasta un grado ridículo). Es mucho más fácil demostrar que una variable miembro o parámetro no puede ser nulo cuando se etiqueta como final, ya que sabe que no se reasigna en ningún otro lugar.
Mi sugerencia es adquirir el hábito de aplicar el final para miembros y parámetros de forma predeterminada, solo son unos pocos caracteres, pero lo empujarán a mejorar su estilo de codificación si no es nada más.
Final para métodos o clases es otro concepto, ya que no permite una forma muy válida de reutilización y realmente no le dice mucho al lector. El mejor uso es probablemente la forma en que hicieron que String y los otros tipos intrínsecos sean definitivos para que pueda confiar en un comportamiento coherente en todas partes: eso evitó muchos errores (aunque a veces me ENCANTARÍA extender la cadena ... ¡oh! posibilidades)
fuente
Como se mencionó en otra parte, 'final' para una variable local y, en menor medida, una variable miembro, es más una cuestión de estilo.
'final' es una declaración de que tiene la intención de que la variable no cambie (es decir, ¡la variable no variará!). El compilador puede ayudarlo quejándose si viola su propia restricción.
Comparto el sentimiento de que Java habría sido un mejor lenguaje si los identificadores (lo siento, simplemente no puedo llamar a una cosa que no varía como 'variable') fueran finales por defecto, y requiriera que dijeras explícitamente que eran variables. Pero dicho esto, generalmente no uso 'final' en variables locales que se inicializan y nunca se asignan; Simplemente parece demasiado ruidoso.
(Yo uso final en las variables miembro)
fuente
Los miembros declarados con final estarán disponibles en todo el programa porque, a diferencia de los no finales, si estos miembros no se han utilizado en el programa, Garbage Collector no los tendrá en cuenta, por lo que puede causar problemas de rendimiento debido a una mala gestión de la memoria.
fuente
final
La palabra clave se puede utilizar de cinco maneras en Java.Una clase es final: una clase es final significa que no podemos extendernos o herencia significa que la herencia no es posible.
Del mismo modo: un objeto es final: en algún momento no modificamos el estado interno del objeto, por lo que en ese caso podemos especificar que el objeto es final object.object final significa no variable también final.
Una vez que la variable de referencia se convierte en definitiva, no se puede reasignar a otro objeto. Pero puede cambiar el contenido del objeto siempre que sus campos no sean finales
fuente
final
significa, pero si el usofinal
influye en el rendimiento.