Classe final
Quando uma classe é definida como uma classe final, significa que a classe não pode ser herdada por outras classes, ou seja, ela não pode ser usada após estender. Caso contrário, você receberá um erro durante a compilação.
pacote com.iderzheng.FinalKeyword; Public Final Class Finalclass {} // Erro: Não é possível herdar da FinalClass PackAGECLASS estende FinalClass {} O Java suporta a definição da classe como final, que parece violar os princípios básicos da programação orientada a objetos. No entanto, por outro lado, a classe fechada também garante que todos os métodos da classe sejam fixos e não haverá substituições de subclasse a serem carregadas dinamicamente. Isso fornece mais possibilidades para o compilador otimizar. O melhor exemplo é a string, que é a classe final. O compilador Java pode transformar diretamente as constantes de string (aquelas contidas em cotações duplas) em objetos de string e, ao mesmo tempo, otimizar diretamente a operação do operador + em novas constantes, porque a modificação final garante que nenhuma subclasse retorne valores diferentes para operações de emenda.
Para todas as diferentes definições de classe - classes de nível superior (global ou pacote visível), classes aninhadas (classes aninhadas internas ou estáticas) podem ser modificadas com o final. No entanto, em geral, o final é usado principalmente para modificar as classes definidas como públicas, porque para classes não globais, os modificadores de acesso restringiram sua visibilidade e já é difícil herdar essas classes, portanto, não há necessidade de adicionar uma camada de restrições finais.
Também é mencionado que, embora as aulas anônimas não possam ser herdadas, elas não se limitam à final pelo compilador.
importar java.lang.reflect.modifier; classe pública main {public static void main (string [] args) {runnable anonymous = new runnable () {@Override public void run () {}}; System.out.println (modifier.isfinal (anonymous.getclass (). GetModifiers ())); }}Saída:
falso
Método final
Intimamente relacionado ao conceito de herança está o polimorfismo, que envolve a diferença entre os conceitos de substituição e ocultação (por conveniência, o seguinte é chamado coletivamente de "reescrita"). No entanto, diferentemente da definição do método em C ++, se a palavra-chave virtual é adicionada à subclasse, se o método de assinatura da subclasse é substituído ou oculto, em Java, as subclasses usam a mesma assinatura do método para substituir o método da classe pai, que formará um método de classe oculto (método estático), enquanto o método de objetos (método não ético) somente Como o Java permite o acesso direto aos métodos de classe através de objetos, o Java não permite que os métodos de classe e os métodos de objeto tenham a mesma assinatura na mesma classe.
A classe final define que toda a classe não pode ser herdada e também significa que todos os métodos da classe não podem ser cobertos e ocultos por subclasses. Quando a classe não é modificada por final, alguns métodos ainda podem ser modificados usando o final para impedir que esses métodos sejam reescritos por subclasses.
Da mesma forma, esse design destrói o polimorfismo orientado a objetos, mas o método final pode garantir o determinismo de sua execução, garantindo assim a estabilidade das chamadas de método. Em alguns projetos de estrutura, alguns métodos implementados de classes abstratos são frequentemente vistos como limitados a final, porque algum código do motorista na estrutura se baseia nesses métodos para atingir os objetivos estabelecidos, portanto não há subclasses que o cobrem.
O exemplo a seguir mostra o papel da modificação final em diferentes tipos de métodos:
pacote com.iderzheng.Other; public class FinalMethods { public static void publicStaticMethod() { } public final void publicFinalMethod() { } public static final void publicStaticFinalMethod() { } protected final void protectedFinalMethod() { } protected static final void protectedStaticFinalMethod() { } final void finalMethod() { } static final void staticfinalMethod () {} private estático final void privatestaticfinalMethod () {} private final void privateFinalMethod () {}} pacote com.iderzheng.FinalKeyword; importação com.iderzheng.other.FinalMethods; Os métodos públicos da classe estendem o FinalMethods {public static void publicstaticMethod () {} // Erro: não é possível substituir public Final Void PublicFinalMethod () {} // erro: não é possível substituir o final da estática estática, o final da estática publicstatic FinaltaticFinal MethModMod () {} // Error: não é possível exagerar o ponto final estático. void publicstaticFinalMethod () {} // Erro: não é possível substituir o vazio final protegido protegidofinalMethod () {} // erro: não é possível substituir o vazio final protegido protegido protegidostaticFinalMethod () privatefinMod (} finalMethod () {} estático final? {} private vazio privatefinalMethod () {}} Primeiro de tudo, observe que, no exemplo acima, os métodos e os métodos finais são definidos em diferentes pacotes. Para o primeiro método público de Publicstatic, a subclasse reescreve com sucesso o método estático da classe pai, mas por ser um método estático, o que acontece é realmente "oculto". Especificamente, os métodos de chamada. Ao chamar FinalMethods.publicstaticmethod (), a implementação não ocorrerá com o carregamento polimórfico da subclasse, mas usará diretamente a implementação dos métodos finais. Portanto, ao usar subclasses para acessar métodos, a visibilidade dos métodos assinados pela classe pai está oculta.
Para o método global PublicFinalMethod, conforme descrito no método de modificação final, a subclasse é proibida de substituí -lo, e uma exceção será lançada no momento da compilação. No entanto, o nome do método é o mesmo na subclasse, mas possui um parâmetro, como: PublicFinalMethod (String x) está ok, porque essa é a assinatura do método síncrono.
Em Intellij, o IDE mostra um aviso ao PublicStaticFinalMethod: Método 'estático' declarado 'final'. Parece redundante, mas pelo exemplo, pode -se ver que Final também proíbe as definições de subclasse dos métodos estáticos para ocultá -lo. No desenvolvimento real, o comportamento de definir os mesmos métodos estáticos de subclasses e classes de pais é extremamente desejável, porque os métodos ocultos exigem que os desenvolvedores prestem atenção ao uso de nomes diferentes de classe para definir efeitos diferentes, o que é fácil causar erros. Além disso, dentro da classe, você pode chamar métodos estáticos diretamente sem usar o nome da classe. Quando o desenvolvedor herda novamente, ele pode não notar a existência oculta. Por padrão, ao usar o método da classe pai, ele descobrirá que não é o resultado esperado. Portanto, os métodos estáticos devem ser finais por padrão e não devem ser ocultos, então o IDE acha que é uma modificação desnecessária.
Os métodos protegidos de modificação e modificação pública na classe pai são visíveis para a subclasse; portanto, a situação da modificação final dos métodos protegidos é a mesma dos métodos públicos. Deve -se mencionar que, no desenvolvimento real, os métodos estáticos protegidos geralmente são raramente definidos porque esses métodos são muito práticos.
Para o método do pacote de classe pai, as subclasses em diferentes pacotes são invisíveis. O método privado foi personalizado e apenas a classe pai pode acessá -lo. Portanto, o compilador permite que as subclasses definam o mesmo método. Mas isso não forma substituição ou ocultação, porque a classe pai escondeu esses métodos por meio de modificadores, não causados pela reescrita de subclasses. Obviamente, se a subclasse e a classe pai estiverem no mesmo pacote, a situação será a mesma que o público anterior e protegido.
Por que o método final é eficiente?
O método final usará o mecanismo incorporado para otimizar a embutida durante a compilação. Otimização em linha refere -se a: Substituição de código de função de chamada diretamente durante a compilação, em vez de chamar funções em tempo de execução. O inline precisa saber qual função usar no final ao compilar. Obviamente, não é possível usá -lo sem final. Métodos não finais podem ser reescritos em subclasses. Devido ao possível polimorfismo, o compilador não pode determinar o tipo verdadeiro do objeto para chamar o método no futuro durante o estágio de compilação e não pode determinar qual método chamar.
Variável final
Simplificando, a variável final em Java só pode e deve ser inicializada uma vez e, em seguida, a variável está ligada ao valor. No entanto, essa atribuição não precisa necessariamente ser inicializada imediatamente quando a variável é definida. O Java também suporta resultados diferentes para variáveis finais através de declarações condicionais, mas a variável só pode ser atribuída uma vez em qualquer caso.
No entanto, a variável final de Java não é uma constante absoluta, porque as variáveis de objeto de Java são apenas valores de referência, portanto, apenas significa que a referência não pode ser alterada e o conteúdo do objeto ainda pode ser modificado. Comparado aos ponteiros de C/C ++, é mais como variável do tipo * const do que o tipo const * variável.
As variáveis Java podem ser divididas em duas categorias: variáveis locais (variável local) e variáveis de membro da classe (campo de classe). A seguir, é apresentado um código para introduzir sua situação de inicialização separadamente.
Variável local
As variáveis locais se referem principalmente a variáveis definidas nos métodos. Eles desaparecerão e se tornarão inacessíveis após o método. Há um caso especial que pode ser dividido em: parâmetros de função. Para este caso, sua inicialização está ligada aos parâmetros passados quando a função é chamada.
Para outras variáveis locais, elas são definidas no método e seus valores podem ser inicializados condicionalmente:
Public String Method (final boolean finalParam) {// Erro: o parâmetro final FinalParam não pode ser atribuído // finalparam = true; Final FinAllocal = FinalParam? novo objeto (): nulo; final int finalvar; if (Finallocal! = NULL) {finalvar = 21; } else {finalvar = 7; } // Erro: Variable finalvar já pode ter sido atribuído // finalvar = 80; Final String finalret; Switch (Finalvar) {Caso 21: finalret = "me"; quebrar; caso 7: finalret = "ela"; quebrar; padrão: finalret = null; } retornar finalret;} A partir do exemplo acima, pode -se observar que os parâmetros de função modificados pelo final não podem ser atribuídos um novo valor, mas outras variáveis locais podem receber um valor em uma instrução condicional. Isso também fornece uma certa flexibilidade para o final.
Obviamente, todas as condições na declaração condicional devem conter atribuições para variáveis locais finais; caso contrário, você receberá um erro de que a variável não poderá ser inicializada.
Public String Method (Final Object finalParam) {final int finalvar; if (finalParam! = null) {finalvar = 21; } string final finalret; // ERRO: Variable Finalvar pode não ter sido inicializado (FinalVar) {Caso 21: finalret = "Me"; quebrar; caso 7: finalret = "ela"; quebrar; } // Erro: a variável finalret pode não ter sido inicializada retornar finalret;} Em teoria, as variáveis locais não são necessárias para serem definidas como final, e um método de design razoável deve poder manter bem as variáveis locais. É apenas que, ao usar funções anônimas para fazer fechamentos nos métodos Java, o Java exige que a variável local referenciada seja definida como final:
método public runnable (string string) {int integer = 12; retornar new runnable () {@Override public void run () {// Erro: precisa ser declarado final System.out.println (string); // Erro: precisa ser declarado final System.out.println (Integer); }};}Campo de classe
As variáveis dos membros da classe podem realmente ser divididas em dois tipos: estática e não estática. Para variáveis estáticas dos membros da classe, porque elas estão relacionadas a classes, além de serem diretamente inicializadas no horário da definição, elas também podem ser colocadas em um bloqueio estático e o uso deste último pode executar declarações mais complexas:
pacote com.iderzheng.FinalKeyword; importar java.util.hashset; importar java.util.LinkedHashSet; importar java.util.set; classe pública staticfinalfields {static final int static_final_init_inline = 7; Conjunto final estático <Integer> static_final_init_static_block; / ** Bloco 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); }}Também existem blocos não estáticos em Java que podem inicializar variáveis não estáticas de membros, mas para essas variáveis, elas são frequentemente colocadas no construtor para inicialização. Obviamente, é necessário garantir que cada variável final seja inicializada uma vez no construtor. Se outros construtores forem chamados com isso (), essas variáveis finais não poderão mais ser atribuídas no construtor.
pacote com.iderzheng.FinalKeyword; classe pública finalfields {final long final_init_inline = System.currenttimemillis (); Final Long Final_init_block; final long final_init_constructor; / ** Bloco inicial **/ {final_init_block = System.nanotime (); } Finalfields () {this (217); } Finalfields (bool bool) {final_init_constructor = 721; } Finalfields (init longo) {final_init_constructor = init; }}Quando o final é usado para modificar classes (classe) e métodos (método), afeta principalmente a herança orientada a objetos. Sem herança, não haverá dependência do código da subclasse na classe pai. Portanto, ao modificar o código durante a manutenção, ele não precisa considerar se a implementação da subclasse será destruída, o que a torna mais conveniente. Quando é usado em uma variável, o Java garante que o valor da variável não seja modificado. Se um design adicional garantir que os membros da classe não possam ser modificados, toda a variável pode ser transformada em uma constante, o que é muito benéfico para a programação com vários threads. Portanto, o final tem um efeito muito bom na manutenção do código.