JavaRush /Java Blog /Random-IT /Nucleo Java. Domande per un'intervista, parte 3
Vadim625
Livello 27

Nucleo Java. Domande per un'intervista, parte 3

Pubblicato nel gruppo Random-IT
Nei due articoli precedenti abbiamo discusso alcune domande importanti che ti vengono poste più spesso durante le interviste. È ora di andare avanti e guardare il resto delle domande.
Nucleo Java.  Domande dell'intervista, parte 3 - 1

Copia profonda e copia superficiale

Una copia esatta dell'originale è il suo clone. In Java ciò significa poter creare un oggetto con una struttura simile all'oggetto originale. Il metodo clone()fornisce questa funzionalità. La copia superficiale copia la minor quantità di informazioni possibile. Per impostazione predefinita, la clonazione in Java è superficiale, ad es. Object classnon conosce la struttura della classe che sta copiando. Durante la clonazione, la JVM esegue le seguenti operazioni:
  1. Se una classe ha solo membri di tipo primitivo, verrà creata una copia completamente nuova dell'oggetto e verrà restituito un riferimento a quell'oggetto.
  2. Se una classe contiene non solo membri di tipi primitivi, ma anche qualsiasi altro tipo di classe, vengono copiati i riferimenti agli oggetti di queste classi. Pertanto, entrambi gli oggetti avranno gli stessi riferimenti.
La copia profonda duplica tutto. La copia profonda consiste in due raccolte, una delle quali duplica tutti gli elementi della raccolta originale. Vogliamo fare una copia in modo tale che apportare modifiche a qualsiasi elemento della copia non influenzi la raccolta originale. La clonazione profonda richiede le seguenti regole:
  1. Non è necessario copiare separatamente i dati primitivi;
  2. Tutte le classi membro della classe originale devono supportare la clonazione. Per ogni membro della classe, deve essere chiamato super.clone()quando il metodo viene sovrascritto clone();
  3. Se qualsiasi membro di una classe non supporta la clonazione, nel metodo clone è necessario creare una nuova istanza di quella classe e copiare ciascuno dei suoi membri con tutti gli attributi in un nuovo oggetto di classe, uno alla volta.
Scopri di più sulla clonazione qui

Cos'è la sincronizzazione? Blocco a livello di oggetto e blocco a livello di classe?

La sincronizzazione si riferisce al multithreading. Un blocco di codice sincronizzato può essere eseguito solo da un thread alla volta. Java consente di elaborare più thread contemporaneamente. Ciò potrebbe comportare che due o più thread desiderino accedere allo stesso campo. La sincronizzazione aiuta a evitare errori di memoria che si verificano quando le risorse di memoria vengono utilizzate in modo errato. Quando un metodo viene dichiarato sincronizzato, il thread mantiene il suo monitor. Se un altro thread tenta di accedere a un metodo sincronizzato in questo momento, il thread viene bloccato e attende che il monitor si liberi. La sincronizzazione in Java viene eseguita con la speciale parola chiave sincronizzata . Puoi contrassegnare singoli blocchi o metodi nella tua classe in questo modo. La parola chiave sincronizzata non può essere utilizzata insieme a variabili o attributi di classe. Il blocco a livello di oggetto è un meccanismo quando si desidera sincronizzare un metodo non statico o un blocco di codice non statico in modo che solo un thread possa eseguire il blocco di codice su una determinata istanza della classe. Questo dovrebbe essere sempre fatto per rendere sicuro il thread dell'istanza della classe. Il blocco a livello di classe impedisce a più thread di accedere a un blocco sincronizzato per tutte le istanze disponibili della classe. Ad esempio, se sono presenti 100 istanze della classe DemoClass, solo 1 thread sarà in grado di eseguire demoMethod() utilizzando una delle variabili in un dato momento. Questo dovrebbe essere sempre fatto per garantire la sicurezza del thread statico. Scopri di più sulla sincronizzazione qui.

Qual è la differenza tra sleep() e wait()?

Sleep()è un metodo utilizzato per ritardare il processo di alcuni secondi. Nel caso di wait(), il thread è in uno stato di attesa finché non chiamiamo il metodo notify()o notifyAll(). La differenza principale è che wait()rilascia il blocco del monitor mentre sleep()non rilascia il blocco. Wait()utilizzato per applicazioni multi-thread, sleep()utilizzato semplicemente per mettere in pausa l'esecuzione del thread. Thread.sleep()mette il thread corrente nello stato "Non eseguibile" per un certo periodo di tempo. Il thread salva lo stato del monitor precedente alla chiamata di questo metodo. Se un altro thread chiama t.interrupt(), il thread che si è “addormentato” si sveglierà. Tieni presente che questo sleep()è un metodo statico, il che significa che influisce sempre sul thread corrente (quello che esegue il metodo sleep()). Un errore comune è chiamare t.sleep()dov'è tun altro thread; anche quando il thread corrente che ha chiamato il metodo sleep()non è tun thread. Object.wait()invia il thread corrente nello stato "Non eseguibile" per un po', proprio come sleep(), ma con qualche sfumatura. Wait()chiamato su un oggetto, non su un thread; chiamiamo questo oggetto “oggetto blocca”. Prima di chiamare lock.wait(), il thread corrente deve essere sincronizzato con l'“oggetto lock”; wait()successivamente rilascia questo blocco e aggiunge il thread alla "lista d'attesa" associata a questo blocco. Successivamente, un altro thread può sincronizzarsi con lo stesso oggetto lock e chiamare il metodo lock.notify(). Questo metodo "risveglierà" il thread originale, che è ancora in attesa. In linea di principio wait()/ notify()può essere paragonato a sleep()/ interrupt(), solo che il thread attivo non ha bisogno di un puntatore diretto al thread dormiente, ha solo bisogno di conoscere l'oggetto lock condiviso. Leggi la differenza dettagliata qui.

È possibile assegnare null a questa variabile di riferimento?

No, non puoi. In Java, la parte sinistra dell'operatore di assegnazione deve essere una variabile. "This" è una parola chiave speciale che fornisce sempre l'istanza corrente della classe. Non è una variabile qualsiasi. Allo stesso modo, non è possibile assegnare null a una variabile utilizzando la parola chiave “super” o qualsiasi altra parola chiave simile.

Qual è la differenza tra && e &?

&- bit per bit e &&- logicamente.
  1. &valuta entrambi gli aspetti dell'operazione;
  2. &&valuta il lato sinistro dell'operazione. Se è vero, continua a valutare il lato destro.
Guarda qui per una comprensione più profonda.

Come sovrascrivere i metodi equals() e hachCode()?

hashCode()e equals()i metodi sono definiti nella classe Object, che è la classe genitore per gli oggetti Java. Per questo motivo, tutti gli oggetti Java ereditano l'implementazione predefinita dei metodi. Il metodo hashCode()viene utilizzato per ottenere un numero intero univoco per un determinato oggetto. Questo numero intero viene utilizzato per determinare la posizione di un oggetto quando tale oggetto deve essere archiviato, ad esempio in HashTable. Per impostazione predefinita, hashCode()restituisce integeruna rappresentazione dell'indirizzo della posizione di memoria in cui è archiviato l'oggetto. Il metodo equls(), come suggerisce il nome, viene utilizzato semplicemente per verificare se due oggetti sono uguali. L'implementazione predefinita controlla i riferimenti agli oggetti per vedere se sono uguali. Di seguito sono riportate linee guida importanti per ricaricare questi metodi:
  1. Utilizza sempre gli stessi attributi dell'oggetto durante la generazione hashCode()e equals();
  2. Simmetria. Quelli. xse restituisce true per alcuni oggetti y x.equals(y), allora y.equals(x)dovrebbe restituire true;
  3. Riflessività. Per qualsiasi oggetto x x.equals(x)deve restituire true;
  4. Consistenza. Per qualsiasi oggetto xe y x.equals(y)restituisce la stessa cosa se le informazioni utilizzate nei confronti non cambiano;
  5. Transitività. Per qualsiasi oggetto x, ye z, se x.equals(y)restituisce true e y.equals(z)restituisce true, allora x.equals(z)dovrebbe restituire true;
  6. Ogni volta che un metodo viene chiamato sullo stesso oggetto durante l'esecuzione dell'applicazione, dovrebbe restituire lo stesso numero a meno che le informazioni utilizzate non cambino. hashCodepuò restituire valori diversi per oggetti identici in diverse istanze dell'applicazione;
  7. Se due oggetti sono uguali, secondo equals, allora hashCodedevono restituire gli stessi valori;
  8. Il requisito opposto è facoltativo. Due oggetti non uguali possono restituire lo stesso hashCode. Tuttavia, per migliorare le prestazioni, è preferibile che oggetti diversi restituiscano codici diversi.
Leggi fatti interessanti su questi metodi qui.

Parlaci dei modificatori di accesso

Le classi, i campi, i costruttori e i metodi Java possono avere uno dei quattro diversi modificatori di accesso: private Se un metodo o una variabile è contrassegnata come private , solo il codice all'interno della stessa classe può accedere alla variabile o chiamare il metodo. Il codice all'interno delle sottoclassi non può accedere a una variabile o a un metodo, né può accedervi da qualsiasi altra classe. Il modificatore di accesso privato viene spesso utilizzato per costruttori, metodi e variabili. default Il modificatore di accesso predefinito viene dichiarato se il modificatore non è specificato affatto. Questo modificatore significa che l'accesso ai campi, ai costruttori e ai metodi di una determinata classe può essere ottenuto tramite codice interno alla classe stessa, codice interno alle classi dello stesso pacchetto. Le sottoclassi non possono accedere ai metodi e alle variabili membro di una superclasse se sono dichiarate come default , a meno che la sottoclasse non sia nello stesso pacchetto della superclasse. protected Il modificatore protected funziona allo stesso modo di default , tranne per il fatto che le sottoclassi possono anche accedere ai metodi e alle variabili protette della superclasse. Questa affermazione è vera anche se la sottoclasse non è nello stesso pacchetto della superclasse. public Il modificatore di accesso pubblico significa che tutto il codice può accedere alla classe, alle sue variabili, ai costruttori o ai metodi, indipendentemente da dove si trova il codice. Nucleo Java.  Domande per un'intervista, parte 3 - 2

Cos'è un garbage collector? Possiamo chiamarlo?

La Garbage Collection è una funzionalità di gestione automatica della memoria in molti linguaggi di programmazione moderni, come Java e i linguaggi del NET.Framework. I linguaggi che utilizzano la garbage collection spesso interpretano la garbage collection in una macchina virtuale come la JVM. La Garbage Collection ha due scopi: la memoria inutilizzata deve essere liberata e la memoria non deve essere liberata se il programma la utilizza ancora. È possibile eseguire manualmente la raccolta dei rifiuti? No, System.gc()ti dà il maggior accesso possibile. L'opzione migliore è chiamare il metodo System.gc(), che suggerirà al Garbage Collector che deve essere eseguito. Non è possibile eseguirlo immediatamente poiché il Garbage Collector non è deterministico. Inoltre, secondo la documentazione, OutOfMemoryErrornon verrà inoltrato se la macchina virtuale non riesce a liberare memoria dopo una garbage collection completa. Scopri di più sul garbage collector qui.

Cosa significa la parola chiave nativa? Spiega in dettaglio

La parola chiave native viene utilizzata per indicare che il metodo è implementato in un linguaggio di programmazione diverso da un file Java. In passato sono stati utilizzati metodi nativi . Nelle attuali versioni di Java questo è necessario meno spesso. Attualmente, i metodi nativi sono necessari quando:
  1. È necessario richiamare una libreria da Java scritta in un'altra lingua.
  2. È necessario accedere a risorse di sistema o hardware a cui è possibile accedere solo utilizzando un altro linguaggio (solitamente C). Infatti, molte funzioni di sistema che interagiscono con il computer reale (come i dischi o i dati di rete) possono essere richiamate solo con il metodo nativo.
Anche gli svantaggi derivanti dall’utilizzo delle librerie di metodi nativi sono significativi:
  1. JNI/JNA può destabilizzare la JVM, soprattutto se provi a fare qualcosa di complesso. Se il tuo metodo nativo fa qualcosa di sbagliato, c'è la possibilità che la JVM si blocchi. Inoltre, possono accadere cose brutte se il tuo metodo nativo viene chiamato da più thread. E così via.
  2. È più difficile eseguire il debug di un programma con codice nativo .
  3. Il codice nativo richiede la costruzione separata di framework, il che può creare problemi con il porting su altre piattaforme.

Cos'è la serializzazione?

Nell'informatica, nel contesto dell'archiviazione e della trasmissione dei dati, la serializzazione è il processo di traduzione di una struttura di dati o dello stato di un oggetto in un formato che può essere archiviato e successivamente recuperato in un altro ambiente informatico. Dopo aver ricevuto una serie di bit, questi vengono ricalcolati secondo il formato di serializzazione e possono essere utilizzati per creare un clone semanticamente identico dell'oggetto originale. Java fornisce la serializzazione automatica, che richiede che l'oggetto implementi l'interfaccia java.io.Serializable. L'implementazione dell'interfaccia contrassegna la classe come "serializzabile". L'interfaccia java.io.Serializable non dispone di metodi di serializzazione, ma la classe serializable può facoltativamente definire metodi che verranno chiamati come parte del processo di serializzazione/diserializzazione. Quando si apportano modifiche alle classi, è necessario considerare quali saranno e quali non saranno compatibili con la serializzazione. Puoi leggere le istruzioni complete qui. Fornirò i punti più importanti: Modifiche incompatibili:
  1. Elimina un campo;
  2. Sposta una classe verso l'alto o verso il basso nella gerarchia;
  3. Modifica di un campo da non statico a statico o da non transitorio a transitorio;
  4. Modifica del tipo di dati primitivo dichiarato;
  5. Modificare il metodo WriteObjectin ReadObjectmodo che non scrivano o leggano più i campi per impostazione predefinita;
  6. Cambiare classe Serializablein Externalizableo viceversa;
  7. Modifica di una classe enum in una non enum o viceversa;
  8. Rimozione Serializableo Externalizable;
  9. Aggiunta writeReplacedi un readResolvemetodo a una classe.
Modifiche compatibili:
  1. Aggiunta di campi;
  2. Aggiunta/rimozione di classi;
  3. Aggiunta di metodi WriteObject/ReadObject[metodi defaultReadObjecto defaultWriteObjectdevono essere chiamati all'inizio];
  4. Metodi di rimozione WriteObject/ReadObject;
  5. Aggiunta java.io.Serializable;
  6. Modifica dell'accesso al campo;
  7. Modifica di un campo statico in non statico o da transitorio a non transitorio .
Collegamenti alle parti precedenti: Java Core. Domande dell'intervista, parte 1 Java Core. Domande dell'intervista, parte 2 Articolo originale Buono studio!
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION