2010-02-18 23 views
34

Per curiosità,Perché il metodo finalize() in java.lang.Object è "protetto"?

Perché modificatore di accesso il metodo del finalize() vengono prodotti come protected. Perché non può essere public? Qualcuno può spiegarmi qualche motivo specifico dietro a questo?

Inoltre, sono venuto a sapere che il metodo finalize() viene chiamato una sola volta. Se lo chiamo due volte internamente al mio programma, cosa sta succedendo? Il garbage collector lo chiamerà di nuovo?

private void dummyCall() { 
    try { 
     finalize(); 
     finalize(); 
    } catch (Throwable e) { 
     e.printStackTrace();//NOT REACHES EXCEPTION 
    } 
} 
+2

perché scriverebbe un metodo per richiamare finalize(). finalize() è invocato a distruzione di istanza da parte della JVM. non dovresti invocarlo. Puoi sovrascriverlo, nel caso in cui desideri un comportamento speciale al momento della distruzione ... – harschware

+0

Sì. Personalmente non lo farei mai. Ma l'ho chiesto per curiosità dato che ho letto GC non lo invocherò se è già stato chiamato su un oggetto. Chi tiene traccia del fatto che questo metodo finalize() sia chiamato o meno è il mio dubbio. Non ho una spiegazione convincente per questa seconda domanda però. – bragboy

+1

La vera domanda interessante è: perché * finalize() * è presente nella parte superiore della gerarchia di OO Java? Progetto LOC 200K qui e non abbiamo superato * finalize() * una sola volta. Alcuni potrebbero addirittura dire che è un odore di codice da sovrascrivere * finalize() * e alcuni eretici andrebbero oltre a dire che * finalize() * non esiste al livello OOA/OOD e che è presenza nella parte superiore di Java la gerarchia è una idiosincrasia (spezzata) di Java (e completamente non correlata al tuo spazio problematico).Alcune delle risposte in aumento qui considerate * finalize() * da spezzare/imperfezioni in un modo o nell'altro :) – SyntaxT3rr0r

risposta

23

rispondo alla tua domanda con un'altra domanda:

Perché finalize metodo non deve essere protetto?

In generale, si dovrebbe cercare di mantenere le cose il più privato possibile. Questo è l'incapsulamento. In caso contrario, è possibile effettuare tuttopublic. finalize non può essere private (poiché le classi derivate dovrebbero essere in grado di accedervi per poterlo sovrascrivere), quindi dovrebbe essere almeno protected ma perché dare più accesso quando non è desiderabile?


Dopo aver letto il tuo commento con più attenzione, immagino di vedere ora il tuo punto principale. Penso che il tuo punto sia dato che tutto deriva da java.lang.Object e che di conseguenza accede ai suoi membri protected, non farebbe alcuna differenza per questo (o qualsiasi metodo in java.lang.Object per quello) per essere public in contrasto con protected. Personalmente, lo considererei un difetto di progettazione in Java. Questo è in effetti risolto in C#. Il problema non è perché finalize è protetto. Va bene. Il vero problema è che non si dovrebbe essere in grado di chiamare metodi protetti nella classe base attraverso un riferimento a un oggetto del tipo di classe base. Eric Lippert ha un blog entry discutendo sul motivo per cui consentire tale tipo di accesso ai membri protetti è una cattiva idea che è further elaborated on Stack Overflow in this question.

+2

"Nascondere" quante più informazioni possibili è l'essenza dell'astrazione. – jldupont

+0

Perché no? Posso chiamare il metodo finalize() di un'istanza da un'altra istanza, giusto? In entrambi i casi, la mia intenzione è la stessa. Quindi, perché non posso farlo? – bragboy

+0

@Bragaadeesh: è uno di quei casi in cui la piattaforma può proteggere il programmatore da se stesso ;-) – jldupont

3

Check out this link che ne discute.

Fondamentalmente, sarebbe più sensato che fosse private, come dovrebbe essere chiamato solo dalla JVM (Garbage Collector). Ma per consentire a una sottoclasse di chiamare il metodo genitore finalize() come parte del suo finalize(), deve essere protected.

(Modifica - E solo un avvertimento generale - uso della finalizzazione() metodo è generalmente sconsigliato in quanto non c'è modo di garantire che sarà mai chiamato Anche se questo non significa che non dovrete mai. occasione per usarlo - è solo rari)

20

Perché è l'accesso il modificatore il metodo del Finalize() è fatto come protetta.. Perché lo non è pubblico?

Non è pubblico perché non dovrebbe essere invocato da nessuno al di fuori della JVM. Tuttavia, deve essere protetto in modo che possa essere sovrascritto dalle sottoclassi che devono definire il comportamento per esso.

Se lo chiamo due volte nel mio programma, internamente cosa sta succedendo?

È possibile chiamare tutto ciò che si desidera, è solo un metodo dopo tutto. Tuttavia, proprio come public static void main(String [] args), che ha un significato speciale per la JVM

Intende la garbage collector questo di nuovo?

+0

Ma lo chiamo ancora (ciò significa che posso semplicemente chiamare il metodo finalize() come e quando voglio all'interno dell'oggetto sebbene non abbia alcuna logica)? In tal caso, avrebbero dovuto progettarlo in tal modo, non avrei dovuto essere in grado di chiamarlo (dal punto di vista di un programmatore). – bragboy

+0

Il punto è che non dovresti chiamare finalizzare te stesso. Mai. È a disposizione del jvm per ripulire (se necessario) il tempo di raccolta dei dati inutili nel caso in cui siano presenti risorse esterne/native che devono essere rilasciate nel caso in cui l'applicazione non riuscisse a farlo. È protetto in modo che le sottoclassi possano fornirne un'implementazione se necessario. .finalize() non è un sostituto per .close/.dispose o altre convenzioni di rilascio di risorse comuni – nos

+0

@nos: Si dovrebbe chiamare 'super.finalize()' quando lo si sovrascrive. –

2

Non public (o di accesso predefinito), perché è destinata ad essere chiamata dalla JVM internamente quando l'oggetto viene garbage collection - è non pensato per essere chiamato da qualsiasi altra cosa. E non è private perché è destinato a essere sovrascritto e non è possibile sovrascrivere i metodi privati.

Se lo chiamo due volte nel mio programma, internamente cosa sta succedendo? Il garbage collector chiamerà questo di nuovo?

Probabilmente sì, ma è difficile immaginare uno scenario in cui questo avrebbe alcun tipo di senso - il punto di finalize() è quello di fare pulizia quando un oggetto è garbage collection. E non lo fa nemmeno bene, quindi è davvero qualcosa che dovresti evitare del tutto piuttosto che sperimentare.

1

finalize() è utilizzato solo da JVM per eliminare le risorse quando viene raccolto l'oggetto. È ragionevole che una classe definisca le azioni da intraprendere per la raccolta, per le quali potrebbe essere necessario accedere a super.finalize(). Non ha senso che un processo esterno invochi finalize(), poiché un processo esterno non ha il controllo su quando l'oggetto viene raccolto.

+0

Detto questo, quale sarebbe la risposta alla mia seconda domanda. Se chiamo finalize() due volte, cosa succederà? – bragboy

+0

Se il metodo finalize è implementato correttamente, non causerà alcun danno chiamarlo due volte. Object.finalize non esegue nulla, nada, zip per impostazione predefinita a meno che non l'abbia sovrascritto o derivi da una classe che lo sovrascrive. Se si chiama finalize su qualcosa che lo sovrascrive, è possibile rilasciare le sue risorse prima di quanto necessario, e le successive operazioni su quell'oggetto potrebbero fallire in vari modi. – nos

+0

'Se chiamo finalize() due volte, cosa succederà?' Perché dovresti farlo? Non dovresti chiamarlo neanche una volta. La domanda non sorge. – EJP

1

Inoltre, sono venuto a sapere che il metodo finalize() viene chiamato una sola volta. Se chiamo lo due volte nel mio programma, internamente cosa sta succedendo?

Probabilmente lo chiedi con l'impressione di distruttori C++ ~. Nel metodo java finalize() non fa alcuna magia (come cancellare la memoria). Dovrebbe essere chiamato da Garbage Collector. Ma non viceversa.

Vi consiglio di leggere il capitolo corrispondente in "Effective Java" di Joshua Bloch. Dice che l'uso dei finalizzatori è una cattiva pratica e può causare prestazioni e altri problemi, e ci sono solo diversi casi in cui dovrebbero essere utilizzati. Il capitolo inizia con le seguenti parole:

I finalizzatori sono imprevedibili, spesso pericolosi e generalmente non necessari.

12
  • finalizzare è destinato a essere chiamato dal gc solo e come tale non richiede l'accesso del pubblico
  • finalizzare è garantito per essere chiamato solo una volta dal gc, chiamando da soli si romperà questa garanzia, come il gc non lo saprà.
  • Ogni classe può rendere prioritario finalizzare pubblica, che credo sia un male per le ragioni di cui sopra
  • finalizzazione non dovrebbe contenere molto codice, come qualsiasi eccezione generata dalla finalizzazione può uccidere il thread finalizzatore del GC.

Rant contro finalizzare()

  • Gestione delle risorse autoctone o di qualsiasi risorsa che richiede smaltire() o close() da chiamare può causare difficile trovare bug, saranno rilasciati solo quando la JVM esaurisce la memoria, è necessario rilasciare le risorse manualmente. La finalizzazione deve essere utilizzata solo per il debug delle perdite di risorse o per i casi in cui la gestione manuale delle risorse è eccessiva.
  • finalize verrà chiamato in un thread aggiuntivo del gc e potrebbe causare problemi con il blocco delle risorse e così via.
  • le classi di riferimento come WeakReference e ReferenceQueue sono un modo alternativo (piuttosto complesso) per gestire la pulizia e possono avere gli stessi problemi di finalize() per le risorse native.

Attenzione di errori nelle dichiarazioni di cui sopra, sono un po 'stanco :-)

+1

Sospetto che i progettisti di Java fossero estremamente ottimisti sull'utilità di 'Finalize'; se non fossero stati, qualcosa di simile a AutoCloseable sarebbe stato parte di Java 1.0. Se la lingua fornisse un mezzo per distinguere tra riferimenti che incapsulano la proprietà delle risorse e quelli che identificano semplicemente le risorse possedute da qualcun altro, direi che il 99% delle risorse potrebbe essere automatizzato in modo deterministico, perché il 99% delle entità che richiedono la pulizia, in ogni momento della loro vita, hanno esattamente un solo proprietario. Non sempre lo stesso proprietario, ma mai zero e mai due. – supercat

+0

Anche se ci sono alcuni casi in cui è necessario che le entità richiedano la pulizia ma non abbiano un proprietario chiaro, la maggior parte degli oggetti che incapsulano lo stato mutabile dovrebbe avere una proprietà chiaramente definita (indipendentemente dal fatto che richieda la pulizia) e la maggior parte degli oggetti che richiedono la pulizia incapsulano stato mutabile. Sapere quando l'ultimo riferimento a un oggetto esce dall'ambito è difficile; sapere quando il solo ed unico proprietario di un oggetto è fatto con esso è facile. L'ultimo punto è quando l'oggetto dovrebbe essere ripulito, * indipendentemente da quali altri riferimenti potrebbero esistere *. – supercat

3

La parte su finalize() essere chiamato solo una volta si applica solo alle chiamate dal GC. Puoi immaginare che l'oggetto abbia una bandiera nascosta "finalize() è stato chiamato dal GC", e il GC che controlla quel flag per sapere cosa fare con l'oggetto. La bandiera non è influenzata in alcun modo dalle tue chiamate fatte a mano allo finalize().

Alla finalizzazione, leggere this article da Hans Boehm (che è noto per il suo lavoro sulla raccolta dei rifiuti). Questo apre gli occhi sulla finalizzazione; in particolare, Boehm spiega perché la finalizzazione sia necessariamente asincrona. Un corollario è che mentre la finalizzazione è uno strumento potente, è molto raramente lo strumento giusto per un dato lavoro.

1

Penso che il motivo per cui lo finalize sia protetto sarebbe che forse è sovrascritto da alcune classi nel JDK, e quei metodi sovrascritti sono chiamati da JVM.

Problemi correlati