Secondo la mia esperienza, esiste un motivo uno e un solo per ignorare Object.finalize()
, ma è un buon motivo :
To place error logging code in finalize()
which notifies you if you ever
forget to invoke close()
.
Le analisi statiche possono catturare omissioni solo in banali scenari di utilizzo, e gli avvertimenti del compilatore citati in un'altra risposta hanno una visione così semplicistica delle cose che è necessario disabilitarli per ottenere qualcosa di non banale. (Ho molti più avvisi abilitati di qualsiasi altro programmatore di cui conosco o abbia mai sentito parlare, ma non ho abilitato gli avvisi stupidi.)
La finalizzazione potrebbe sembrare un buon meccanismo per assicurarsi che le risorse non siano esposte, ma la maggior parte delle persone la vede in un modo completamente sbagliato: la pensano come un meccanismo alternativo di riserva, una "seconda possibilità" di salvaguardia che salva automaticamente la giornata eliminando le risorse che hanno dimenticato. È assolutamente sbagliato . Deve esserci un solo modo per fare una determinata cosa: o si chiude sempre tutto, o la finalizzazione chiude sempre tutto. Ma poiché la finalizzazione è inaffidabile, la finalizzazione non può essere quella.
Quindi, c'è questo schema che chiamo Smaltimento obbligatorio , e stabilisce che il programmatore è responsabile per chiusura sempre esplicita di tutto che implementa Closeable
o AutoCloseable
. (La dichiarazione try-with-resource conta ancora come chiusura esplicita.) Naturalmente, il programmatore può dimenticare, quindi è qui che entra in gioco la finalizzazione, ma non come una fata magica che magicamente farà le cose giuste alla fine: se la finalizzazione scopre che close()
non è stato invocato, fa non tenta di invocarlo, proprio perché ci saranno (con certezza matematica) orde di programmatori n00b che si affideranno ad esso fare il lavoro che erano troppo pigri o troppo distratti per fare. Quindi, con lo smaltimento obbligatorio, quando la finalizzazione scopre che close()
non è stato richiamato, registra un messaggio di errore rosso vivo, dicendo al programmatore con grandi lettere maiuscole e grosse per aggiustare il suo s, la sua roba.
Come ulteriore vantaggio, diciamo che che "la JVM ignorerà un banale metodo finalize () ( ad esempio uno che ritorna semplicemente senza fare nulla, come quello definito nella classe Object) ", quindi con lo smaltimento obbligatorio puoi evitare tutto il sovraccarico di finalizzazione nell'intero sistema ( vedi la risposta di alip per informazioni su quanto sia terribile questo overhead codificando il tuo metodo finalize()
in questo modo:
@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.
}
L'idea alla base di questo è che Global.DEBUG
è una variabile static final
il cui valore è noto al momento della compilazione, quindi se è false
il compilatore non emetterà alcun codice per l'intera istruzione if
, che renderà questo un banale finalizzatore (vuoto), che a sua volta significa che la classe verrà trattata come se non avesse un finalizzatore. (In C # questo sarebbe fatto con un bel blocco #if DEBUG
, ma cosa possiamo fare, questo è java, dove paghiamo apparente semplicità nel codice con sovraccarico aggiuntivo nel cervello.)
Ulteriori informazioni sullo Smaltimento obbligatorio, con ulteriori discussioni sullo smaltimento delle risorse in dot Net, qui: michael.gr: disposizione obbligatoria contro l'abominio" Dispose-disposing "