Em Java, usamos final
palavras-chave com variáveis para especificar que seus valores não devem ser alterados. Mas vejo que você pode alterar o valor no construtor / métodos da classe. Novamente, se a variável for static
, será um erro de compilação.
Aqui está o código:
import java.util.ArrayList;
import java.util.List;
class Test {
private final List foo;
public Test()
{
foo = new ArrayList();
foo.add("foo"); // Modification-1
}
public static void main(String[] args)
{
Test t = new Test();
t.foo.add("bar"); // Modification-2
System.out.println("print - " + t.foo);
}
}
O código acima funciona bem e sem erros.
Agora mude a variável como static
:
private static final List foo;
Agora é um erro de compilação. Como isso final
realmente funciona?
Respostas:
Você sempre tem permissão para inicializar uma
final
variável. O compilador garante que você possa fazer isso apenas uma vez.Observe que chamar métodos em um objeto armazenado em uma
final
variável não tem nada a ver com a semântica definal
. Em outras palavras:final
é apenas sobre a própria referência, e não sobre o conteúdo do objeto referenciado.Java não tem conceito de imutabilidade de objetos; isso é conseguido com o design cuidadoso do objeto e é um empreendimento longe de trivial.
fonte
final
. Objetos mutáveis também podem ser alocados à pilha.final
campos podem ajudar na análise de escape, porém, mas essa é uma rota bastante indireta. Observe também que variáveis efetivamente finais têm tratamento idêntico ao marcadofinal
no código-fonte.final
está presente no arquivo de classe e tem consequências semânticas significativas para otimizar o tempo de execução. Também pode incorrer em custos, porque o JLS tem uma forte garantia sobre a consistência dosfinal
campos de um objeto. Por exemplo, o processador ARM deve usar uma instrução explícita de barreira à memória no final de cada construtor de uma classe que possuifinal
campos. Em outros processadores, isso não é necessário, no entanto.Esta é uma pergunta de entrevista favorita . Com essas perguntas, o entrevistador tenta descobrir até que ponto você entende o comportamento dos objetos em relação aos construtores, métodos, variáveis de classe (variáveis estáticas) e variáveis de instância.
No caso acima, definimos um construtor para 'Test' e atribuímos a ele um método 'setFoo'.
Sobre o construtor: O construtor pode ser chamado apenas uma vez por criação de objeto usando a
new
palavra - chave Você não pode chamar o construtor várias vezes, porque o construtor não foi projetado para fazer isso.Sobre o método: Um método pode ser chamado quantas vezes você desejar (Mesmo nunca) e o compilador sabe disso.
Cenário 1
foo
é uma variável de instância . Quando criamos oTest
objeto de classe, a variável de instânciafoo
será copiada dentro do objeto deTest
classe. Se atribuirmosfoo
dentro do construtor, o compilador saberá que o construtor será invocado apenas uma vez; portanto, não haverá problema em atribuí-lo dentro do construtor.Se atribuirmos
foo
dentro de um método, o compilador saberá que um método pode ser chamado várias vezes, o que significa que o valor terá que ser alterado várias vezes, o que não é permitido para umafinal
variável. Portanto, o compilador decide que o construtor é uma boa escolha! Você pode atribuir um valor a uma variável final apenas uma vez.Cenário 2
foo
agora é uma variável estática . Quando criamos uma instância daTest
classe,foo
não serão copiados para o objeto porquefoo
é estático. Agorafoo
não é uma propriedade independente de cada objeto. Esta é uma propriedade deTest
classe. Masfoo
pode ser visto por vários objetos e se todos os objetos criados com anew
palavra - chave invocarem oTest
construtor que altera o valor no momento da criação de vários objetos (Lembre-static foo
se de que não é copiado em todos os objetos, mas é compartilhado entre vários objetos). .)Cenário 3
Acima
Modification-2
é da sua pergunta. No caso acima, você não está alterando o primeiro objeto referenciado, mas está adicionando conteúdo dentro dofoo
qual é permitido. O compilador reclama se você tentar atribuir umnew ArrayList()
àfoo
variável de referência.Regra Se você inicializou uma
final
variável, não poderá alterá-la para se referir a um objeto diferente. (Neste casoArrayList
)classes finais não podem ser subclassificadas métodos
finais não podem ser substituídos. (Este método está na superclasse)
os métodos finais podem substituir. (Leia isso de maneira gramatical. Este método está em uma subclasse)
fonte
foo
seria definido várias vezes, apesar da designação final, sefoo
for definido na classe Test e várias instâncias do teste forem criadas?foo
pode haver vários objetos.) Isso significa que, se eu criar vários objetos de cada vez, qual objeto está inicializando a variável final depende da execução?final
ao endereço de memória referenciado pelofoo
qual é um ArrayList. Você não está atribuindofinal
ao endereço de memória referenciado pelo primeiro elemento defoo
(ou por qualquer elemento). Portanto, você não pode mudar,foo
mas pode mudarfoo[0]
.foo = new ArrayList();
-foo
refere-se à variável estática porque estamos dentro da mesma classe.final
em uma variável como a mesmaconst
palavra - chave em C ++?A palavra-chave final tem várias maneiras de usar:
Outro uso:
Uma variável de classe estática existirá desde o início da JVM e deve ser inicializada na classe. A mensagem de erro não aparecerá se você fizer isso.
fonte
static
campos (contanto que não sejamfinal
) quantas vezes quiser. Veja este wiki para a diferença entre atribuição e inicialização .A
final
palavra-chave pode ser interpretada de duas maneiras diferentes, dependendo do que é usada:Tipos de valor: para
int
s,double
s etc., garantirá que o valor não possa ser alterado,Tipos de referência: para referências a objetos,
final
garante que a referência nunca seja alterada, o que significa que sempre se referirá ao mesmo objeto. Não garante, de forma alguma, que os valores dentro do objeto sejam mantidos iguais.Como tal,
final List<Whatever> foo;
garante quefoo
sempre se refira à mesma lista, mas o conteúdo dessa lista pode mudar com o tempo.fonte
Se você tornar
foo
estático, deverá inicializá-lo no construtor da classe (ou na linha em que o define) como nos exemplos a seguir.Construtor de classe (não instância):
Na linha:
O problema aqui não é como o
final
modificador funciona, mas como ostatic
funciona.O
final
modificador impõe uma inicialização de sua referência no momento em que a chamada ao construtor é concluída (ou seja, você deve inicializá-la no construtor).Quando você inicializa um atributo em linha, ele é inicializado antes da execução do código que você definiu para o construtor, para que você obtenha os seguintes resultados:
foo
forstatic
,foo = new ArrayList()
será executado antes da execução dostatic{}
construtor que você definiu para sua classefoo
contráriostatic
,foo = new ArrayList()
será executado antes da execução do construtorQuando você não inicia um atributo em linha, o
final
modificador impõe que você o inicialize e que você deve fazê-lo no construtor. Se você também tem umstatic
modificador, o construtor no qual você terá que inicializar o atributo é o bloco de inicialização da classe:static{}
.O erro que você obtém no seu código é o fato de
static{}
ser executado quando a classe é carregada, antes da instanciação de um objeto dessa classe. Assim, você não inicializoufoo
quando a classe for criada.Pense no
static{}
bloco como um construtor para um objeto do tipoClass
. É aqui que você deve fazer a inicialização do seustatic final
atributos de classe (se não for feito em linha).Nota:
o
final
modificador assegura a consistência apenas para tipos e referências primitivas.Quando você declara um
final
objeto, o que você recebe é umafinal
referência a esse objeto, mas o objeto em si não é constante.O que você realmente está alcançando ao declarar um
final
atributo é que, depois de declarar um objeto para sua finalidade específica (como afinal List
que você declarou), esse e somente esse objeto serão usados para essa finalidade: você não poderá mudarList foo
para outroList
, mas você ainda pode alterá-loList
adicionando / removendo itens (o queList
você está usando será o mesmo, apenas com o conteúdo alterado).fonte
Esta é uma pergunta de entrevista muito boa. Às vezes, eles podem até perguntar qual é a diferença entre um objeto final e um objeto imutável.
1) Quando alguém menciona um objeto final, significa que a referência não pode ser alterada, mas seu estado (variáveis de instância) pode ser alterado.
2) Um objeto imutável é aquele cujo estado não pode ser alterado, mas sua referência pode ser alterada. Ex:
A variável ref x pode ser alterada para apontar uma sequência diferente, mas o valor "abc" não pode ser alterado.
3) Variáveis de instância (campos não estáticos) são inicializadas quando um construtor é chamado. Assim, você pode inicializar valores para suas variáveis dentro de um construtor.
4) "Mas vejo que você pode alterar o valor no construtor / métodos da classe". - Você não pode alterá-lo dentro de um método.
5) Uma variável estática é inicializada durante o carregamento da classe. Portanto, você não pode inicializar dentro de um construtor, isso deve ser feito antes mesmo dele. Portanto, você precisa atribuir valores a uma variável estática durante a própria declaração.
fonte
A
final
palavra-chave em java é usada para restringir o usuário. Afinal
palavra-chave java pode ser usada em muitos contextos. Final pode ser:A
final
palavra-chave pode ser aplicada com as variáveis, umafinal
variável que não tem valor, é chamada definal
variável em branco oufinal
variável não inicializada . Pode ser inicializado apenas no construtor. Afinal
variável em brancostatic
também pode ser qual será inicializada nostatic
bloco.Variável final Java:
Se você fizer qualquer variável como
final
, não poderá alterar o valor definal
variável (será constante).Exemplo de
final
variávelExiste uma variável final speedlimit, vamos alterar o valor dessa variável, mas ela não pode ser alterada porque a variável final, uma vez atribuída a um valor, nunca pode ser alterada.
Classe final Java:
Se você formar uma classe como
final
, não poderá estender la.Exemplo de aula final
Método final Java:
Se você fizer um método final, não poderá substituí- lo.
Exemplo de
final
método (run () na Honda não pode substituir run () em Bike)compartilhado em: http://www.javatpoint.com/final-keyword
fonte
Vale mencionar algumas definições diretas:
Classes / Métodos
Variáveis
final
basicamente evite substituir / substituir por qualquer coisa (subclasses, variável "reatribuir"), dependendo do caso.fonte
final
é uma palavra-chave reservada em Java para restringir o usuário e pode ser aplicada a variáveis de membro, métodos, classe e variáveis locais. Variáveis finais são frequentemente declaradas com astatic
palavra - chave em Java e são tratadas como constantes. Por exemplo:Quando usamos a
final
palavra-chave com uma declaração de variável, o valor armazenado nessa variável não pode ser alterado posteriormente.Por exemplo:
Nota : Uma classe declarada como final não pode ser estendida ou herdada (ou seja, não pode haver uma subclasse da superclasse). Também é bom observar que os métodos declarados como finais não podem ser substituídos pelas subclasses.
Os benefícios do uso da palavra-chave final são abordados neste tópico .
fonte
the value stored inside that variable cannot be changed latter
é parcialmente verdade. É verdade apenas para tipos de dados primitivos. No caso de qualquer objeto ser criado comofinal
, como arraylist, seu valor pode mudar, mas não a referência. Obrigado!Suponha que você tenha duas caixas de dinheiro, vermelho e branco. Você atribui a essas caixas apenas dois filhos e eles não têm permissão para trocar suas caixas. Portanto, você tem caixas de dinheiro vermelhas ou brancas (final), não é possível modificar a caixa, mas você pode colocar dinheiro na caixa.Ninguém se importa (Modificação-2).
fonte
Leia todas as respostas.
Há outro caso de usuário em que a
final
palavra-chave pode ser usada, ou seja, em um argumento de método:Pode ser usado para variáveis que não devem ser alteradas.
fonte
Quando você o torna estático final, ele deve ser inicializado em um bloco de inicialização estático
fonte
A
final
palavra-chave indica que uma variável pode ser inicializada apenas uma vez. No seu código, você está executando apenas uma inicialização do final para que os termos sejam satisfeitos. Esta declaração executa a inicialização solitária defoo
. Observe quefinal
! = Imutável, significa apenas que a referência não pode ser alterada.Quando você declara
foo
comostatic final
a variável deve ser inicializada quando a classe é carregada e não pode confiar na instanciação (também chamada de construtor) para inicializarfoo
pois os campos estáticos devem estar disponíveis sem uma instância de uma classe. Não há garantia de que o construtor tenha sido chamado antes de usar o campo estático.Quando você executa seu método no
static final
cenário, aTest
classe é carregada antes da instanciaçãot
no momento. Não há instanciação defoo
significado que ela não foi inicializada; portanto,foo
é definida como o padrão para todos os objetos que sãonull
. Neste ponto, suponho que seu código lança umNullPointerException
quando você tenta adicionar um item à lista.fonte
Primeiro de tudo, o lugar no seu código em que você está inicializando (ou seja, atribuindo pela primeira vez) foo está aqui:
foo é um objeto (com o tipo Lista), portanto, é um tipo de referência , não um tipo de valor (como int). Como tal, ele mantém uma referência a um local de memória (por exemplo, 0xA7D2A834) onde seus elementos da Lista estão armazenados. Linhas como esta
não altere o valor de foo (que, novamente, é apenas uma referência a um local de memória). Em vez disso, eles apenas adicionam elementos nesse local de memória referenciado. Para violar a palavra-chave final , você teria que tentar atribuir novamente o foo da seguinte forma:
Isso iria dar-lhe um erro de compilação.
Agora, com isso fora do caminho, pense no que acontece quando você adiciona a palavra-chave estática .
Quando você NÃO possui a palavra-chave estática, cada objeto que instancia a classe possui sua própria cópia de foo. Portanto, o construtor atribui um valor a uma cópia nova e em branco da variável foo, o que é perfeitamente aceitável.
No entanto, quando você tem a palavra-chave estática, existe apenas um foo na memória associado à classe. Se você fosse criar dois ou mais objetos, o construtor tentaria reatribuir esse nome de cada vez, violando a palavra-chave final .
fonte
final
apenas vincula a referência a um objeto específico. Você é livre para alterar o 'estado' desse objeto, mas não o próprio objeto.fonte
A seguir, são apresentados diferentes contextos em que final é usado.
Variáveis finais Uma variável final pode ser atribuída apenas uma vez. Se a variável for uma referência, isso significa que a variável não pode ser ligada novamente para fazer referência a outro objeto.
A variável final pode receber um valor posteriormente (não é obrigatório atribuir um valor quando declarado), mas apenas uma vez.
Classes finais Uma classe final não pode ser estendida (herdada)
Métodos finais Um método final não pode ser substituído por subclasses.
fonte
Pensei em escrever uma resposta atualizada e aprofundada aqui.
final
A palavra-chave pode ser usada em vários lugares.A
final class
significa que nenhuma outra classe pode estender essa classe final. Quando o Java Run Time ( JRE ) sabe que uma referência a objeto está no tipo de uma classe final (digamos F), ele sabe que o valor dessa referência pode estar apenas no tipo F.Ex:
Portanto, quando ele executa qualquer método desse objeto, esse método não precisa ser resolvido em tempo de execução usando uma tabela virtual . ou seja, o polimorfismo em tempo de execução não pode ser aplicado. Portanto, o tempo de execução não se preocupa com isso. O que significa que economiza tempo de processamento, o que melhora o desempenho.
A
final method
de qualquer classe significa que qualquer classe filha que estenda essa classe não poderá substituir o (s) método (s) final (is). Portanto, o comportamento em tempo de execução nesse cenário também é o mesmo com o comportamento anterior que mencionei para as classes.Se alguém especificou qualquer tipo acima
final
, significa que o valor já está finalizado, portanto, o valor não pode ser alterado .Ex:
Para campos, parâmetros locais
Para parâmetros de método
Isso significa simplesmente que o valor do valor de
final
referência não pode ser alterado. ou seja, apenas uma inicialização é permitida. Nesse cenário, em tempo de execução, como o JRE sabe que os valores não podem ser alterados, ele carrega todos esses valores finalizados (de referências finais) no cache L1 . Porque ele não precisa a carga de volta novamente e novamente de memória principal . Caso contrário, ele será carregado no cache L2 e será carregado periodicamente da memória principal. Portanto, é também uma melhoria de desempenho.Portanto, em todos os três cenários acima, quando não especificamos a
final
palavra - chave nos locais que podemos usar, não precisamos nos preocupar, as otimizações do compilador farão isso por nós. Também existem muitas outras coisas que as otimizações do compilador fazem por nós. :)fonte
Acima de tudo, estão corretos. Além disso, se você não quiser que outras pessoas criem subclasses a partir de sua classe, declare-a como final. Então, torna-se o nível da folha da hierarquia da árvore de classes que ninguém pode estender ainda mais. É uma boa prática evitar uma hierarquia enorme de classes.
fonte