Respostas:
De acordo com o Effective Java de Joshua Bloch (Segunda Edição), há dois cenários em que finalize()
é útil:
Uma é agir como uma "rede de segurança", caso o proprietário de um objeto esqueça de chamar seu método explícito de terminação. Embora não haja garantia de que o finalizador seja chamado imediatamente, pode ser melhor liberar o recurso mais tarde do que nunca, naqueles casos (provavelmente raros) em que o cliente falha em chamar o método de encerramento explícito. Mas o finalizador deve registrar um aviso se descobrir que o recurso não foi finalizado
Um segundo uso legítimo de finalizadores diz respeito a objetos com pares nativos. Um par nativo é um objeto nativo ao qual um objeto normal delega por métodos nativos. Como um par nativo não é um objeto normal, o coletor de lixo não o conhece e não pode recuperá-lo quando seu par Java é recuperado. Um finalizador é um veículo apropriado para executar esta tarefa, assumindo que o par nativo não possua recursos críticos. Se o par nativo reter recursos que devem ser finalizados imediatamente, a classe deverá ter um método de finalização explícito, conforme descrito acima. O método de encerramento deve fazer o que for necessário para liberar o recurso crítico.
Para uma leitura mais detalhada, consulte o Item 7, página 27.
Os finalizadores são importantes para o gerenciamento de recursos nativos. Por exemplo, seu objeto pode precisar alocar um WidgetHandle do sistema operacional usando uma API não Java. Se você não liberar esse WidgetHandle quando seu objeto estiver no GC, estará vazando WidgetHandles.
O importante é que os casos "finalizador nunca são chamados" sejam divididos de maneira simples:
Nos três casos, você não possui um vazamento nativo (devido ao fato de seu programa não estar mais sendo executado) ou já possui um vazamento não nativo (se continuar alocando objetos gerenciados sem que eles sejam GC'd).
O aviso "não confie no finalizador que está sendo chamado" é realmente sobre não usar finalizadores para a lógica do programa. Por exemplo, você não deseja acompanhar quantos objetos existem em todas as instâncias do seu programa, incrementando um contador em um arquivo em algum lugar durante a construção e diminuindo-o em um finalizador - porque não há garantia de que seus objetos serão Ao finalizar, esse contador de arquivos provavelmente nunca voltará a 0. Esse é realmente um caso especial do princípio mais geral de que você não deve depender do término normal do programa (falhas de energia, etc.).
Para o gerenciamento de recursos nativos, os casos em que o finalizador não é executado correspondem aos casos em que você não se importa se não for executado.
close()
nunca é chamado antes que se torne inacessível)
O objetivo deste método é explicado na documentação da API da seguinte maneira:
é invocado se e quando a Java virtual machine determinou que não há mais nenhum meio pelo qual esse objeto possa ser acessado por qualquer encadeamento que ainda não tenha morrido, exceto como resultado de uma ação executada pela finalização de outro objeto ou classe que está pronta para ser finalizada ...
o objetivo usual de
finalize
... é executar ações de limpeza antes que o objeto seja descartado irrevogavelmente . Por exemplo, o método finalize para um objeto que representa uma conexão de entrada / saída pode executar transações de E / S explícitas para interromper a conexão antes que o objeto seja descartado permanentemente ...
Se você também estiver interessado em razões pelas quais os designers de linguagem optaram por "o objeto é irrevogavelmente descartado" ( coleta de lixo ) da maneira que está além do controle do programador de aplicativos ("nunca devemos confiar"), isso foi explicado em uma resposta a pergunta :
coleta automática de lixo ... elimina classes inteiras de erros de programação que atormentam os programadores de C e C ++. Você pode desenvolver código Java com confiança de que o sistema encontrará muitos erros rapidamente e que os principais problemas não permanecerão inativos até que o código de produção seja enviado.
A citação acima, por sua vez, foi tirada da documentação oficial sobre os objetivos do projeto Java , ou seja, pode ser considerada uma referência autorizada, explicando por que os designers da linguagem Java decidiram dessa maneira.
Para uma discussão mais detalhada e independente do idioma sobre essa preferência, consulte a seção 9.6 da OOSC . Gerenciamento automático de memória (na verdade, não apenas esta seção, mas todo o capítulo 9 vale muito a pena ler se você estiver interessado em coisas desse tipo). Esta seção é aberta com uma declaração inequívoca:
Um bom ambiente OO deve oferecer um mecanismo automático de gerenciamento de memória que detecte e recupere objetos inacessíveis, permitindo que os desenvolvedores de aplicativos se concentrem em seu trabalho - desenvolvimento de aplicativos.
A discussão anterior deve ser suficiente para mostrar o quanto é importante ter esse recurso disponível. Nas palavras de Michael Schweitzer e Lambert Strether:
Um programa orientado a objetos sem gerenciamento automático de memória é aproximadamente o mesmo que uma panela de pressão sem uma válvula de segurança: mais cedo ou mais tarde a coisa certamente explodirá!
Os finalizadores existem porque se espera que eles sejam um meio eficaz de garantir que as coisas sejam limpas (embora na prática não sejam) e porque quando foram inventados, melhores meios de garantir a limpeza (como referências fantasmas e tentativa de -resources) ainda não existia. Em retrospectiva, o Java provavelmente seria melhor se o esforço despendido na implementação de sua instalação "finalize" tivesse sido gasto em outros meios de limpeza, mas isso não ficou claro durante o período em que o Java estava sendo desenvolvido inicialmente.
CAVEAT: Posso estar desatualizado, mas esse é o meu entendimento a alguns anos atrás:
Em geral, não há garantia de quando um finalizador é executado - ou mesmo que seja executado, embora algumas JVMs permitam solicitar um GC e uma finalização completos antes da saída do programa (o que, é claro, significa que o programa leva mais tempo sair e que não é o modo padrão de operação).
E sabia-se que alguns GCs atrasavam explicitamente ou evitavam objetos de GC que tinham finalizadores, na esperança de que isso produzisse melhor desempenho nos benchmarks.
Infelizmente, esses comportamentos conflitam com os motivos originais pelos quais os finalizadores foram recomendados e, em vez disso, incentivaram o uso de métodos de desligamento chamados explicitamente.
Se você tem um objeto que realmente precisa ser limpo antes de ser descartado e se não pode confiar nos usuários, um finalizador ainda pode ser considerado. Mas, em geral, existem boas razões para que você não as veja com tanta frequência no código Java moderno como em alguns dos primeiros exemplos.
O Google Java Style Guide tem alguns sábios conselhos sobre o assunto:
É extremamente raro substituir
Object.finalize
.Dica : não faça isso. Se você precisar, leia e entenda primeiro o Item eficaz do Java 7, "Evite finalizadores", com muito cuidado e, em seguida , não faça isso.
PhantomReference
e em ReferenceQueue
vez disso.
PhantomReference
é uma solução melhor. Os finalizadores são uma verruga que sobrou dos primeiros dias de Java, e Object.clone()
tipos like e raw, são parte da linguagem mais esquecida.
A Especificação de Linguagem Java (Java SE 7) declara:
Os finalizadores oferecem a chance de liberar recursos que não podem ser liberados automaticamente por um gerenciador de armazenamento automático. Nessas situações, a simples recuperação da memória usada por um objeto não garantiria que os recursos contidos nela fossem recuperados.