Em Java, usamos final
palavra-chave final
com variables para especificar que seus valores não devem ser alterados. Mas vejo que você pode alterar o valor no construtor / methods da class. Novamente, se a variável é static
então é um erro de compilation.
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 compilation. Como esta final
realmente funciona?
Você sempre tem permissão para inicializar uma variável final
. O compilador garante que você pode fazer isso apenas uma vez.
Note que chamar methods em um object armazenado em uma variável final
não tem nada a ver com a semântica do final
. Em outras palavras: final
é apenas sobre a própria referência, e não sobre o conteúdo do object referenciado.
Java não tem nenhum conceito de imutabilidade do object; isto é conseguido através do desenho cuidadoso do object, e é um esforço longe de ser trivial.
Esta é uma pergunta favorita da entrevista . Com essas perguntas, o entrevistador tenta descobrir o quão bem você entende o comportamento dos objects em relação aos construtores, methods, variables de class (variables estáticas) e variables de instância.
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 void setFoo(List foo) { //this.foo = foo; Results in compile time error. } }
No caso acima, nós definimos um construtor para ‘Test’ e fornecemos um método ‘setFoo’.
Sobre o construtor: O construtor pode ser chamado apenas uma vez por criação de object 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ê quiser (mesmo nunca) e o compilador sabe disso.
Cenário 1
private final List foo; // 1
foo
é uma variável de instância . Quando criamos Test
object da class Test
, a variável da instância foo
será copiada dentro do object da class Test
. Se nós atribuímos foo
dentro do construtor, então o compilador sabe que o construtor será invocado apenas uma vez, então não há nenhum problema em atribuí-lo dentro do construtor.
Se nós atribuirmos foo
dentro de um método, o compilador sabe 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 uma variável final
. Então 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
private static final List foo = new ArrayList();
foo
é agora uma variável estática . Quando criamos uma instância da class Test
, foo
não será copiado para o object porque foo
é estático. Agora foo
não é uma propriedade independente de cada object. Esta é uma propriedade da class Test
. Mas foo
pode ser visto por vários objects e se todo object que é criado usando a new
palavra-chave que acabará invocando o construtor de Test
que altera o valor no momento da criação de múltiplos objects (Lembre-se static foo
não é copiado em cada object, mas é compartilhado entre vários objects.)
Cenário 3
t.foo.add("bar"); // Modification-2
Acima de Modification-2
é da sua pergunta. No caso acima, você não está alterando o primeiro object referenciado, mas está adicionando conteúdo dentro do foo
que é permitido. O compilador reclama se você tentar atribuir um new ArrayList()
à variável de referência foo
.
Regra Se você tiver inicializado uma variável final
, não poderá alterá-la para se referir a um object diferente. (Neste caso ArrayList
)
classs finais não podem ser subclassificadas
os methods finais não podem ser substituídos. (Este método está na superclass)
methods finais podem replace. (Leia isto de maneira gramatical. Este método está em uma subclass)
A palavra-chave final tem uma maneira numerosa de usar:
Outro uso:
Uma variável de class estática existirá desde o início da JVM e deverá ser inicializada na class. A mensagem de erro não aparecerá se você fizer isso.
A palavra-chave final
pode ser interpretada de duas maneiras diferentes, dependendo do que é usado:
Tipos de valor: para int
s, double
s etc, ele irá garantir que o valor não pode mudar,
Tipos de referência: Para referências a objects, final
garante que a referência nunca será alterada, o que significa que sempre se referirá ao mesmo object. Não garante, em absoluto, que os valores dentro do object sejam referenciados para permanecerem iguais.
Como tal, final List
garante que foo
sempre se refira à mesma lista, mas o conteúdo da lista pode mudar com o tempo.
Se você fizer foo
estático, você deve inicializá-lo no construtor de class (ou inline onde você o define) como os exemplos a seguir.
Construtor de class (não instância):
private static final List foo; static { foo = new ArrayList(); }
Na linha:
private static final List foo = new ArrayList();
O problema aqui não é como o modificador final
funciona, mas sim como o modificador static
funciona.
O modificador final
impõe uma boot de sua referência no momento em que a chamada ao seu construtor é concluída (ou seja, você deve inicializá-lo no construtor).
Quando você inicializa um atributo in-line, ele é inicializado antes que o código definido para o construtor seja executado, portanto, você obtém os seguintes resultados:
foo
for static
, foo = new ArrayList()
será executado antes que o construtor static{}
definido para sua class seja executado foo
não for static
, foo = new ArrayList()
será executado antes que seu construtor seja executado Quando você não inicia um atributo in-line, o modificador final
impõe que você o inicialize e que você deve fazê-lo no construtor. Se você também tiver um modificador static
, o construtor no qual você terá que inicializar o atributo é o bloco de boot da class: static{}
.
O erro que você recebe no seu código é do fato de que o static{}
é executado quando a class é carregada, antes da hora em que você instancia um object dessa class. Portanto, você não inicializou foo
quando a class é criada.
Pense no bloco static{}
como um construtor para um object do tipo Class
. É aqui que você deve fazer a boot de seus atributos de class static final
(se não for feito inline).
Nota:
O modificador final
assegura a constância apenas para tipos primitivos e referências.
Quando você declara um object final
, o que obtém é uma referência final
a esse object, mas o object em si não é constante.
O que você está realmente conseguindo ao declarar um atributo final
é que, quando você declara um object para seu propósito específico (como a final List
que você declarou), esse e somente aquele object será usado para aquele propósito: você não será capaz para alterar List foo
para outra List
, mas você ainda pode alterar sua List
adicionando / removendo itens (a List
você está usando será a mesma, apenas com seu conteúdo alterado).
Essa é uma pergunta muito boa para a entrevista. Às vezes, eles podem até perguntar qual é a diferença entre um object final e um object imutável.
1) Quando alguém menciona um object final, isso significa que a referência não pode ser alterada, mas seu estado (variables de instância) pode ser alterado.
2) Um object imutável é aquele cujo estado não pode ser alterado, mas sua referência pode ser alterada. Ex:
String x = new String("abc"); x = "BCG";
ref variável x pode ser alterado para apontar uma string diferente, mas o valor de “abc” não pode ser alterado.
3) Variáveis de instância (campos não estáticos) são inicializadas quando um construtor é chamado. Então você pode inicializar valores para suas variables dentro de um construtor.
4) “Mas vejo que você pode alterar o valor no construtor / methods da class”. – Você não pode alterá-lo dentro de um método.
5) Uma variável estática é inicializada durante o carregamento da class. Então você não pode inicializar dentro de um construtor, tem que ser feito antes mesmo dele. Então você precisa atribuir valores a uma variável estática durante a própria declaração.
Vale mencionar algumas definições diretas:
Classes / Métodos
Você pode declarar alguns ou todos os methods de class como
final
, para indicar que o método não pode ser substituído por subclasss.
Variáveis
Uma vez que uma variável
final
tenha sido inicializada, ela sempre contém o mesmo valor.
final
basicamente evitar replace / sobrescrever por qualquer coisa (subclasss, variável “reatribuir”), dependendo do caso.
Suponha que você tenha dois moneyboxes, vermelho e branco. Você atribui a esses moneyboxes apenas dois filhos e eles não podem trocar suas checkboxs. Então você tem checkboxs de dinheiro vermelhas ou brancas (final) você não pode modificar a checkbox, mas você pode colocar dinheiro em sua checkbox. Ninguém se importa (Modificação-2).
final
é uma palavra-chave reservada em Java para restringir o usuário e pode ser aplicada a variables de membros, methods, classs e variables locais. As variables finais são frequentemente declaradas com a palavra-chave static
em Java e são tratadas como constantes. Por exemplo:
public static final String hello = "Hello";
Quando usamos a palavra-chave final
com uma declaração de variável, o valor armazenado dentro dessa variável não pode ser alterado posteriormente.
Por exemplo:
public class ClassDemo { private final int var1 = 3; public ClassDemo() { ... } }
Nota : Uma class declarada como final não pode ser estendida ou herdada (ou seja, não pode haver uma subclass da superclass). Também é bom notar que os methods declarados como final não podem ser substituídos por subclasss.
Benefícios de usar a palavra-chave final são abordados neste segmento .
A palavra-chave final
em java é usada para restringir o usuário. A palavra-chave java final
pode ser usada em muitos contextos. Final pode ser:
A palavra-chave final
pode ser aplicada com as variables, uma variável final
que não tem valor, é chamada de variável final
branco ou variável final
não inicializada. Pode ser inicializado apenas no construtor. A variável final
branco também pode ser static
que será inicializada apenas no bloco static
.
Variável final do Java:
Se você fizer qualquer variável como final
, não poderá alterar o valor da variável final
(será constante).
Exemplo de variável final
Existe 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, nunca pode ser alterada.
class Bike9{ final int speedlimit=90;//final variable void run(){ speedlimit=400; // this will make error } public static void main(String args[]){ Bike9 obj=new Bike9(); obj.run(); } }//end of class
Classe final de Java:
Se você fizer uma aula como final
, não poderá estendê- la.
Exemplo de aula final
final class Bike{} class Honda1 extends Bike{ //cannot inherit from final Bike,this will make error void run(){ System.out.println("running safely with 100kmph"); } public static void main(String args[]){ Honda1 honda= new Honda(); honda.run(); } }
Método final de Java:
Se você fizer qualquer método como final, você não poderá sobrescrevê- lo.
Exemplo de método final
(run () no Honda não pode replace run () em Bike)
class Bike{ final void run(){System.out.println("running");} } class Honda extends Bike{ void run(){System.out.println("running safely with 100kmph");} public static void main(String args[]){ Honda honda= new Honda(); honda.run(); } }
compartilhado de: http://www.javatpoint.com/final-keyword
Quando você faz a final estática, ela deve ser inicializada em um bloco de boot estática
private static final List foo; static { foo = new ArrayList(); } public Test() { // foo = new ArrayList(); foo.add("foo"); // Modification-1 }
A palavra final
indica que uma variável só pode ser inicializada uma vez. Em seu código, você está executando apenas uma boot final para que os termos sejam satisfeitos. Esta declaração executa a boot solitária de foo
. Note que final
! = Imutável, significa apenas que a referência não pode mudar.
foo = new ArrayList();
Quando você declara foo
como static final
a variável deve ser inicializada quando a class é carregada e não pode confiar na instanciação (também chamada de construtor) para inicializar foo
pois os campos estáticos devem estar disponíveis sem uma instância de uma class. 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 cenário static final
, a class Test
é carregada antes de instanciar t
neste momento, não há nenhuma instância de foo
o que significa que ele não foi inicializado, então foo
é definido como padrão para todos os objects, que é null
. Neste ponto, presumo que seu código lança um NullPointerException
quando você tenta adicionar um item à lista.
final
apenas liga a referência a um object específico. Você é livre para mudar o “estado” desse object, mas não o object em si. Leia todas as respostas.
Existe outro caso de usuário em que final
palavra-chave final
pode ser usada, isto é, em um argumento de método:
public void showCaseFinalArgumentVariable(final int someFinalInt){ someFinalInt = 9; // won't compile as the argument is final }
Pode ser usado para variables que não devem ser alteradas.
Pensei em escrever uma resposta atualizada e detalhada aqui.
final
palavra-chave final
pode ser usada em vários lugares.
Uma final class
significa que nenhuma outra class pode estender essa class final. Quando o Java Run Time ( JRE ) sabe que uma referência de object está no tipo de uma class final (digamos F), ele sabe que o valor dessa referência pode estar apenas no tipo de F.
Ex:
F myF; myF = new F(); //ok myF = someOther; //someOther cannot be in type of a child class of F. //because F cannot be extended.
Portanto, quando ele executa qualquer método desse object, esse método não precisa ser resolvido no tempo de execução usando uma tabela virtual . isto é, o polymorphism em tempo de execução não pode ser aplicado. Então o tempo de execução não se incomoda com isso. O que significa que economiza tempo de processamento, o que melhorará o desempenho.
Um final method
de qualquer class significa que qualquer class filha que estenda essa class não pode sobrescrever esse (s) método (s) final (is). Portanto, o comportamento do tempo de execução nesse cenário também é bem o mesmo com o comportamento anterior que mencionei para as classs.
Se alguém especificou qualquer tipo de acima como final
, isso significa que o valor já está finalizado, portanto, o valor não pode ser alterado .
Ex:
Para campos, parâmetros locais
final FinalClass fc = someFC; //need to assign straight away. otherwise compile error. final FinalClass fc; //compile error, need assignment (initialization inside a constructor Ok, constructor can be called only once) final FinalClass fc = new FinalClass(); //ok fc = someOtherFC; //compile error fc.someMethod(); //no problem someOtherFC.someMethod(); //no problem
Para parâmetros do método
void someMethod(final String s){ s = someOtherString; //compile error }
Isso significa simplesmente que o valor do valor de referência final
não pode ser alterado. ou seja, apenas uma boot é permitida. Nesse cenário, no 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 não precisa carregar de novo e de novo da memory principal . Caso contrário, ele é carregado para o cache L2 e faz o tempo para carregar a partir da memory principal. Por isso, também é uma melhoria de desempenho.
Portanto, em todos os três cenários acima, quando não tivermos especificado a palavra-chave final
em lugares que podemos usar, não precisamos nos preocupar, pois as otimizações do compilador farão isso para nós. Há também muitas outras coisas que as otimizações do compilador fazem por nós. 🙂
Acima de tudo estão corretas. Além disso, se você não quiser que outras pessoas criem subclasss de sua turma, declare sua turma como final. Então, torna-se o nível de folha da hierarquia da sua tree de classs, que ninguém pode estender ainda mais. É uma boa prática evitar uma enorme hierarquia de classs.
Primeiro de tudo, o lugar no seu código onde você está inicializando (ou seja, atribuindo pela primeira vez) foo está aqui:
foo = new ArrayList();
foo é um object (com tipo List), portanto, é um tipo de referência , não um tipo de valor (como int). Como tal, ele contém uma referência a um local de memory (por exemplo, 0xA7D2A834) onde seus elementos de Lista são armazenados. Linhas como esta
foo.add("foo"); // Modification-1
não altere o valor de foo (que, novamente, é apenas uma referência a um local de memory). Em vez disso, eles apenas adicionam elementos ao local de memory referenciado. Para violar a palavra-chave final , você teria que tentar reatribuir o foo da seguinte forma novamente:
foo = new ArrayList();
Isso lhe daria um erro de compilation.
Agora, com isso fora do caminho, pense no que acontece quando você adiciona a palavra-chave estática .
Quando você não tem a palavra-chave estática, cada object que instancia a class tem sua própria cópia do foo. Portanto, o construtor atribui um valor a uma cópia nova e vazia da variável foo, que é perfeitamente adequada.
No entanto, quando você tem a palavra-chave static, existe apenas um foo na memory que está associada à class. Se você fosse criar dois ou mais objects, o construtor estaria tentando reatribuir esse foo a cada vez, violando a palavra-chave final .
A seguir estão contextos diferentes onde final é usado.
Variáveis finais Uma variável final só pode ser atribuída uma vez. Se a variável é uma referência, isso significa que a variável não pode ser recontratada para fazer referência a outro object.
class Main { public static void main(String args[]){ final int i = 20; i = 30; //Compiler Error:cannot assign a value to final variable i twice } }
a variável final pode receber um valor posterior (não obrigatório para um valor atribuído quando declarado), mas apenas uma vez.
Classes finais Uma class final não pode ser estendida (herdada)
final class Base { } class Derived extends Base { } //Compiler Error:cannot inherit from final Base public class Main { public static void main(String args[]) { } }
Métodos finais Um método final não pode ser substituído por subclasss.
//Error in following program as we are trying to override a final method. class Base { public final void show() { System.out.println("Base::show() called"); } } class Derived extends Base { public void show() { //Compiler Error: show() in Derived cannot override System.out.println("Derived::show() called"); } } public class Main { public static void main(String[] args) { Base b = new Derived();; b.show(); } }