Na minha experiência, há uma e apenas uma razão para substituir Object.finalize()
, mas é uma boa razão :
Para colocar o código de log de erro no finalize()
qual notifica você se você esquecer de invocar close()
.
A análise estática pode capturar apenas omissões em cenários de uso trivial, e os avisos do compilador mencionados em outra resposta têm uma visão tão simplista das coisas que você realmente precisa desativá-las para fazer qualquer coisa não trivial. (Tenho muito mais avisos ativados do que qualquer outro programador que eu conheço ou já ouvi falar, mas não tenho avisos estúpidos ativados.)
A finalização pode parecer um bom mecanismo para garantir que os recursos não fiquem indisponíveis, mas a maioria das pessoas vê isso de uma maneira completamente errada: eles pensam nisso como um mecanismo alternativo de fallback, uma "segunda chance" de salvaguarda que salvará automaticamente o dia, descartando os recursos que eles esqueceram. Isso está completamente errado . Deve haver apenas uma maneira de fazer qualquer coisa: você sempre fecha tudo ou a finalização sempre fecha tudo. Mas como a finalização não é confiável, a finalização não pode ser isso.
Portanto, existe esse esquema que chamo de Disposição Obrigatória , e estipula que o programador é responsável por sempre fechar explicitamente tudo o que implementa Closeable
ou AutoCloseable
. (A instrução try-with-resources ainda conta como fechamento explícito.) É claro que o programador pode esquecer, então é aí que a finalização entra em jogo, mas não como uma fada mágica que, magicamente, fará as coisas corretamente no final: se a finalização descobrir que close()
não foi invocado, nãotente invocá-lo, precisamente porque (com certeza matemática) haverá hordas de programadores do n00b que confiarão nele para fazer o trabalho que eles eram preguiçosos ou distraídos demais para fazer. Portanto, com o descarte obrigatório, quando a finalização descobre que close()
não foi invocada, ela registra uma mensagem de erro em vermelho brilhante, dizendo ao programador com letras maiúsculas grandes e grandes para consertar suas coisas.
Como um benefício adicional, dizem os boatos de que "a JVM ignorará um método trivial finalize () (por exemplo, um que apenas retorna sem fazer nada, como o definido na classe Object)"; portanto, com o descarte obrigatório, você pode evitar toda finalização sobrecarga em todo o sistema ( consulte a resposta da alip para obter informações sobre quão terrível é essa sobrecarga) codificando seu finalize()
método da seguinte maneira:
@Override
protected void finalize() throws Throwable
{
if( Global.DEBUG && !closed )
{
Log.Error( "FORGOT TO CLOSE THIS!" );
}
//super.finalize(); see alip's comment on why this should not be invoked.
}
A idéia por trás disso é que Global.DEBUG
é uma static final
variável cujo valor é conhecido no momento da compilação; portanto, se for false
, o compilador não emitirá nenhum código para toda a if
instrução, o que tornará este um finalizador trivial (vazio), que por sua vez significa que sua turma será tratada como se não tivesse um finalizador. (Em C #, isso seria feito com um bom #if DEBUG
bloco, mas o que podemos fazer, isso é java, onde pagamos aparente simplicidade no código com sobrecarga adicional no cérebro.)
Mais sobre o descarte obrigatório, com discussões adicionais sobre o descarte de recursos no dot Net, aqui: michael.gr: descarte obrigatório versus a abominação "Dispose-descarte"
finalize()
é um pouco complicada. Se você implementá-lo, verifique se ele é seguro para threads em relação a todos os outros métodos no mesmo objeto.