Em Java, vemos muitos lugares onde a final
palavra - chave pode ser usada, mas seu uso é incomum.
Por exemplo:
String str = "abc";
System.out.println(str);
No caso acima, str
pode ser, final
mas isso geralmente é deixado de lado.
Quando um método nunca será substituído, podemos usar a palavra-chave final. Da mesma forma, no caso de uma classe que não será herdada.
O uso da palavra-chave final em algum ou em todos esses casos realmente melhora o desempenho? Se sim, então como? Por favor explique. Se o uso adequado final
realmente importa para o desempenho, que hábitos um programador Java deve desenvolver para fazer o melhor uso da palavra-chave?
Respostas:
Geralmente não. Para métodos virtuais, o HotSpot controla se o método foi realmente substituído e é capaz de executar otimizações, como alinhar na suposição de que um método não foi substituído - até carregar uma classe que substitui o método, nesse momento pode desfazer (ou desfazer parcialmente) essas otimizações.
(Obviamente, isso supõe que você esteja usando o HotSpot - mas é de longe a JVM mais comum, então ...)
Na minha opinião, você deve usar com
final
base em design e legibilidade claros, e não por razões de desempenho. Se você quiser alterar alguma coisa por motivos de desempenho, execute medições apropriadas antes de alterar o código mais claro de forma - para que você possa decidir se qualquer desempenho extra alcançado vale a pena a legibilidade / design mais baixos. (Na minha experiência, quase nunca vale a pena; YMMV.)EDIT: Como os campos finais foram mencionados, vale ressaltar que eles geralmente são uma boa ideia, em termos de design claro. Eles também alteram o comportamento garantido em termos de visibilidade de encadeamento cruzado: depois que um construtor é concluído, todos os campos finais ficam visíveis em outros encadeamentos imediatamente. Esse é provavelmente o uso mais comum
final
na minha experiência, embora, como defensor da regra básica de Josh Bloch "design para herança ou proibição", eu provavelmente deva usarfinal
mais frequentemente para aulas ...fonte
final
geralmente é recomendado porque facilita a compreensão do código e ajuda a encontrar bugs (porque torna explícita a intenção dos programadores). O PMD provavelmente recomenda o usofinal
por causa desses problemas de estilo, não por motivos de desempenho.Immutable classes are easier to design, implement, and use than mutable classes. They are less prone to error and are more secure.
. Além dissoAn 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.
. Da minha experiência pessoal, o uso da palavrafinal
- chave deve destacar a intenção do desenvolvedor de se inclinar para a imutabilidade, não de "otimizar" o código. Encorajo-vos a ler este capítulo, fascinante!final
palavra-chave em variáveis pode reduzir a quantidade de bytecode, o que pode produzir um efeito no desempenho.Resposta curta: não se preocupe!
Resposta longa:
Ao falar sobre variáveis locais finais, lembre-se de que o uso da palavra-chave
final
ajudará o compilador a otimizar o código estaticamente , o que pode resultar em um código mais rápido. Por exemplo, as seqüências finaisa + b
no exemplo abaixo são concatenadas estaticamente (em tempo de compilação).O resultado?
Testado no Java Hotspot VM 1.7.0_45-b18.
Então, quanto é a melhoria real de desempenho? Não ouso dizer. Na maioria dos casos, provavelmente marginal (~ 270 nanossegundos neste teste sintético porque a concatenação de cadeias é totalmente evitada - um caso raro), mas no código de utilitário altamente otimizado, pode ser um fator. De qualquer forma, a resposta à pergunta original é sim, pode melhorar o desempenho, mas marginalmente na melhor das hipóteses .
Além dos benefícios em tempo de compilação, não pude encontrar nenhuma evidência de que o uso da palavra
final
- chave tenha algum efeito mensurável no desempenho.fonte
String
concatenação é uma operação muito mais cara do que adicionarIntegers
. Fazer isso estaticamente (se possível) é mais eficiente, é o que o teste mostra.Sim pode. Aqui está um exemplo em que final pode aumentar o desempenho:
Compilação condicional é uma técnica na qual linhas de código não são compiladas no arquivo de classe com base em uma condição específica. Isso pode ser usado para remover toneladas de código de depuração em uma construção de produção.
considere o seguinte:
Ao converter o atributo doSomething em um atributo final, você disse ao compilador que sempre que vir doSomething, ele deve ser substituído por false, conforme as regras de substituição em tempo de compilação. A primeira passagem do compilador altera o código para algo assim:
Uma vez feito isso, o compilador dá uma outra olhada e vê que há instruções inacessíveis no código. Como você está trabalhando com um compilador de alta qualidade, ele não gosta de todos esses códigos de bytes inacessíveis. Portanto, eles são removidos e você acaba com isso:
reduzindo assim códigos excessivos ou qualquer verificação condicional desnecessária.
Editar: Como exemplo, vamos dar o seguinte código:
Ao compilar esse código com Java 8 e descompilar com
javap -c Test.class
, obtemos:Podemos observar que o código compilado inclui apenas a variável não final
x
. Isso prova que as variáveis finais têm impacto no desempenho, pelo menos neste caso simples.fonte
De acordo com a IBM - não para classes ou métodos.
http://www.ibm.com/developerworks/java/library/j-jtp04223.html
fonte
Estou surpreso que ninguém tenha realmente postado algum código real que seja descompilado para provar que há pelo menos alguma diferença menor.
Para a referência este foi testado contra a
javac
versão8
,9
e10
.Suponha que este método:
Compilar esse código como ele é, produz exatamente o mesmo código de byte de quando
final
estaria presente (final Object left = new Object();
).Mas este:
Produz:
Deixar
final
de estar presente produz:O código é praticamente auto-explicativo, caso exista uma constante de tempo de compilação, ele será carregado diretamente na pilha de operandos (não será armazenado na matriz de variáveis locais, como o exemplo anterior faz via
bipush 12; istore_0; iload_0
) - o que meio que faz sentido já que ninguém pode mudar isso.Por outro lado, por que no segundo caso o compilador não produz
istore_0 ... iload_0
está além de mim, não é como se esse slot0
fosse usado de alguma forma (poderia encolher a matriz de variáveis dessa maneira, mas pode estar faltando alguns detalhes internos, não é possível diga com certeza)Fiquei surpreso ao ver essa otimização, considerando o quanto os pequenos
javac
fazem. Como devemos sempre usarfinal
? Eu nem vou escrever umJMH
teste (o que eu queria inicialmente), tenho certeza de que o diff está na ordem dens
(se possível, pode ser capturado). O único lugar em que isso pode ser um problema é quando um método não pode ser incorporado devido ao seu tamanho (e a declaraçãofinal
reduziria esse tamanho em alguns bytes).Há mais dois
final
s que precisam ser abordados. Primeiro, quando um método éfinal
(de umaJIT
perspectiva), esse método é monomórfico - e estes são os mais amados peloJVM
.Depois, existem
final
variáveis de instância (que devem ser definidas em todos os construtores); estes são importantes, pois garantirão uma referência publicada corretamente, conforme tocado um pouco aqui e também especificado exatamente peloJLS
.fonte
javac -g FinalTest.java
), descompilei o código usandojavap -c FinalTest.class
e nãofinal int left=12
obtive os mesmos resultados (com , obtivebipush 11; istore_0; bipush 12; istore_1; bipush 11; iload_1; iadd; ireturn
). Portanto, sim, o código de byte gerado depende de muitos fatores e é difícil dizer se terfinal
ou não produzir um efeito no desempenho. Mas como o bytecode é diferente, pode existir alguma diferença de desempenho.Você está realmente perguntando sobre dois (pelo menos) casos diferentes:
final
para variáveis locaisfinal
para métodos / classesJon Skeet já respondeu 2). Cerca de 1):
Eu não acho que isso faça diferença; para variáveis locais, o compilador pode deduzir se a variável é final ou não (simplesmente verificando se foi atribuída mais de uma vez). Portanto, se o compilador quiser otimizar variáveis atribuídas apenas uma vez, poderá fazê-lo, independentemente de a variável ser realmente declarada
final
ou não.final
poderia fazer a diferença para campos protegidos / de classe pública; é muito difícil para o compilador descobrir se o campo está sendo definido mais de uma vez, pois isso pode acontecer a partir de uma classe diferente (que pode até não ter sido carregada). Mas, mesmo assim, a JVM poderia usar a técnica que Jon descreve (otimizar de maneira otimista, reverter se houver uma classe carregada que altere o campo).Em resumo, não vejo nenhum motivo para ajudar o desempenho. Portanto, é improvável que esse tipo de micro-otimização ajude. Você pode tentar compará-lo para ter certeza, mas duvido que faça alguma diferença.
Editar:
Na verdade, de acordo com a resposta de Timo Westkämper,
final
pode melhorar o desempenho dos campos de classe em alguns casos. Eu estou corrigido.fonte
final
, para garantir que você não a atribua duas vezes. Tanto quanto posso ver, a mesma lógica de verificação pode ser (e provavelmente é) usada para verificar se uma variável poderia serfinal
.javac
a otimizar melhor. Mas isso é apenas especulação.final
variável é de um tipo ou tipo primitivoString
e é imediatamente atribuída com uma constante em tempo de compilação, como no exemplo da pergunta, o compilador deve incorporá-la, pois a variável é uma constante em tempo de compilação por especificação. Mas para a maioria dos casos de uso, o código pode parecer diferente, mas ainda não faz diferença se a constante é incorporada ou lida a partir de uma variável local em termos de desempenho.Nota: Não é um especialista em java
Se eu me lembro corretamente do java, haveria muito pouca maneira de melhorar o desempenho usando a palavra-chave final. Eu sempre soube que ele existe para "bom código" - design e legibilidade.
fonte
Não sou especialista, mas acho que você deve adicionar uma
final
palavra-chave à classe ou método, se não for substituída e deixar as variáveis em paz. Se houver alguma maneira de otimizar essas coisas, o compilador fará isso por você.fonte
Na verdade, ao testar algum código relacionado ao OpenGL, descobri que o uso do modificador final em um campo privado pode prejudicar o desempenho. Aqui está o início da classe que testei:
E este é o método que eu usei para testar o desempenho de várias alternativas, entre as quais a classe ShaderInput:
Após a terceira iteração, com a VM aquecida, obtive consistentemente esses números sem a palavra-chave final:
Com a palavra-chave final:
Observe as figuras para o teste ShaderInput.
Não importava se eu tornava os campos públicos ou privados.
Aliás, existem mais algumas coisas desconcertantes. A classe ShaderInput supera todas as outras variantes, mesmo com a palavra-chave final. Isso é notável porque basicamente é uma classe que envolve uma matriz de flutuação, enquanto os outros testam diretamente manipulam a matriz. Tem que descobrir isso. Pode ter algo a ver com a interface fluente do ShaderInput.
Também System.arrayCopy, na verdade, aparentemente é um pouco mais lento para pequenas matrizes do que simplesmente copiar elementos de uma matriz para outra em um loop for. E o uso do sun.misc.Unsafe (bem como do java.nio.FloatBuffer direto, não mostrado aqui) tem um desempenho abismal.
fonte
Final (pelo menos para variáveis e parâmetros membros) é mais para seres humanos do que para a máquina.
É uma boa prática tornar as variáveis finais sempre que possível. Eu gostaria que o Java tivesse feito "variáveis" finais por padrão e tivesse uma palavra-chave "Mutável" para permitir alterações. Classes imutáveis levam a um código encadeado muito melhor, e apenas olhar para uma classe com "final" na frente de cada membro mostrará rapidamente que é imutável.
Outro caso - eu tenho convertido muito código para usar anotações @ NonNull / @ Nullable (você pode dizer que um parâmetro de método não deve ser nulo; o IDE pode avisar você em todos os lugares em que passar uma variável que não esteja marcada @ NonNull - a coisa toda se espalha a um nível ridículo). É muito mais fácil provar que uma variável de membro ou parâmetro não pode ser nulo quando é marcado como final, pois você sabe que não está sendo redesignado em nenhum outro lugar.
Minha sugestão é adquirir o hábito de aplicar final para membros e parâmetros por padrão. São apenas alguns caracteres, mas o levarão a melhorar seu estilo de codificação, se nada mais.
Final para métodos ou classes é outro conceito, pois não permite uma forma de reutilização muito válida e não diz muito ao leitor. O melhor uso é provavelmente o modo como eles fizeram o String e os outros tipos intrínsecos finais, para que você pudesse confiar em um comportamento consistente em todos os lugares - Isso evitou muitos bugs (embora haja momentos em que eu adoraria estender o string ... possibilidades)
fonte
Como mencionado em outro lugar, 'final' para uma variável local e, em menor grau, uma variável membro, é mais uma questão de estilo.
'final' é uma declaração que você pretende que a variável não mude (ou seja, a variável não varia!). O compilador pode ajudá-lo, reclamando se você violar sua própria restrição.
Compartilho o sentimento de que Java teria sido uma linguagem melhor se os identificadores (desculpe, eu simplesmente não posso chamar uma coisa não variável de 'variável') foram finais por padrão, e exigi que você dissesse explicitamente que eram variáveis. Mas, tendo dito isso, geralmente não uso 'final' em variáveis locais que são inicializadas e nunca atribuídas; apenas parece muito barulhento.
(Eu uso final em variáveis de membro)
fonte
Os membros declarados com final estarão disponíveis durante todo o programa, porque, diferentemente dos não-finais, se esses membros não tiverem sido usados no programa, eles ainda não serão atendidos pelo Garbage Collector, portanto, poderão causar problemas de desempenho devido ao mau gerenciamento de memória.
fonte
final
A palavra-chave pode ser usada de cinco maneiras em Java.Uma classe é final: uma classe é final significa que não podemos ser estendidos ou herança significa que herança não é possível.
Da mesma forma - Um objeto é final: em algum momento não modificamos o estado interno do objeto, portanto, nesse caso, podemos especificar que o objeto é objeto final. Objeto final significa não variável e também final.
Depois que a variável de referência é finalizada, ela não pode ser reatribuída para outro objeto. Mas pode alterar o conteúdo do objeto, desde que seus campos não sejam finais
fonte
final
significa, mas se o usofinal
influencia o desempenho.