JavaRush /Java Blog /Random-IT /Analisi di domande e risposte da interviste per sviluppat...

Analisi di domande e risposte da interviste per sviluppatori Java. Parte 11

Pubblicato nel gruppo Random-IT
Ciao! Anche la nave più veloce senza rotta andrà semplicemente alla deriva lungo le onde. Se stai leggendo il mio articolo adesso, hai sicuramente un obiettivo. La cosa principale è non smarrirsi, ma seguire la propria linea fino alla fine: diventare uno sviluppatore Java. Oggi voglio continuare la mia analisi di oltre 250 domande per gli sviluppatori Java, che ti aiuteranno a colmare alcune lacune della teoria. Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 11 - 1

97. Vengono imposte condizioni di ridefinizione dell'accordo quando si ridefinisce Equals?

Il metodo equals() sottoposto a override deve rispettare le seguenti condizioni (regole):
  • riflessività : per qualsiasi valore x, un'espressione come x.equals(x) dovrebbe sempre restituire true (quando x != null ).

  • simmetria : per qualsiasi valore di xey , un'espressione nella forma x.equals ( y) deve restituire vero solo se y.equals(x) restituisce vero .

  • transitività - per qualsiasi valore di x , y e z , se x.equals(y) restituisce true e anche y.equals(z) restituisce true , allora x.equals(z) deve restituire true .

  • consistenza - per qualsiasi valore di x e y, una chiamata ripetuta a x.equals(y) restituirà sempre il valore della chiamata precedente a questo metodo, a condizione che i campi utilizzati per confrontare i due oggetti non siano cambiati tra le chiamate .

  • confronto null - per qualsiasi valore x, la chiamata x.equals(null) restituirà false .

98. Cosa succede se non sovrascrivi Equals e HashCode?

In questo caso, hashCode() restituirà un numero generato in base alla posizione di memoria in cui è archiviato l'oggetto specificato. Cioè, due oggetti con esattamente gli stessi campi riceveranno valori diversi quando chiamano un hashCode() non sovrascritto (dopo tutto, sono archiviati in posizioni di memoria diverse). Il comando equals() non sovrascritto confronta i riferimenti per vedere se puntano allo stesso oggetto o meno. Cioè il confronto avviene tramite == , e nel caso di oggetti con gli stessi campi restituirà sempre false . Vero sarà solo quando si confronteranno i riferimenti allo stesso oggetto. A volte c'è una logica nel non ignorare questi metodi. Ad esempio, vuoi che tutti gli oggetti di una determinata classe siano unici e sovrascrivere questi metodi rovinerà solo la logica dell'unicità. La cosa principale è comprendere le sfumature dei metodi sovrascritti e non sovrascritti e utilizzare entrambi gli approcci a seconda della situazione.

99. Perché la simmetria è vera solo se x.equals(y) restituisce vero?

Una domanda un po' strana. Se l'oggetto A è uguale all'oggetto B, allora l'oggetto B è uguale all'oggetto A. Se B non è uguale all'oggetto A, allora come è possibile il contrario? Questa è una logica semplice. Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 11 - 2

100. Cos'è la collisione in HashCode? Come affrontarlo?

Una collisione hashCode è una situazione in cui due oggetti diversi hanno lo stesso valore hashCode . Com'è possibile? Il fatto è che l'hashcode è mappato sul tipo Integer , che a sua volta ha un intervallo compreso tra -2147483648 e 2147483647, ovvero circa 4 miliardi di numeri interi diversi. Questa gamma è enorme, tuttavia, non è infinita. Pertanto, sono possibili situazioni in cui due oggetti completamente diversi hanno lo stesso codice hash. Ciò è altamente improbabile, ma possibile. Una funzione hash mal implementata può anche aumentare la frequenza di codici hash identici, che, ad esempio, restituiranno numeri in un intervallo ristretto, il che aumenterà la possibilità di collisioni. Per combattere una collisione, è necessario disporre di una buona implementazione del metodo hashCode in modo che la diffusione dei valori sia massima e la possibilità di ripetere i valori sia minima.

101. Cosa succede se un elemento che partecipa ad un contratto HashCode cambia il suo valore?

Se viene modificato un elemento che partecipa al calcolo del codice hash, allora verrà modificato il codice hash dell'oggetto stesso (se la funzione hash è buona). Pertanto, in HashMap si consiglia di utilizzare oggetti immutabili (non modificabili) come chiave, poiché il loro stato interno (campi) non può essere modificato dopo la creazione. Di conseguenza, anche il loro codice hash non viene convertito dopo la creazione. Se usi un oggetto mutabile come chiave, quando modifichi i campi di questo oggetto, il suo codice hash cambierà e, di conseguenza, puoi perdere questa coppia in HashMap . Dopotutto, verrà archiviato nel bucket con il codice hash originale e, dopo averlo modificato, verrà cercato in un altro bucket. Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 11 - 3

102. Scrivi i metodi Equals e HashCode per la classe Student, che consiste nei campi String name e int age

public class Student {
int age;
String name;

 @Override
 public boolean equals(final Object o) {
   if (this == o) {
     return true;
   }
   if (o == null || this.getClass() != o.getClass()) {
     return false;
   }

   final Student student = (Student) o;

   if (this.age != student.age) {
     return false;
   }
   return this.name != null ? this.name.equals(student.name) : student.name == null;
 }

 @Override
 public int hashCode() {
   int result = this.age;
   result = 31 * result + (this.name != null ? this.name.hashCode() : 0);
   return result;
 }
}
Equivale:
  • Per prima cosa confrontiamo direttamente i collegamenti, perché se i collegamenti riguardano lo stesso oggetto, che senso ha continuare il controllo? Sarà tutto vero comunque .

  • Verifica della presenza di null e della corrispondenza dei tipi di classe, perché se un oggetto è un argomento di null o di un altro tipo, ciò significa che gli oggetti non sono uguali: false .

  • Casting dell'oggetto argomento su un tipo (nel caso in cui fosse un oggetto del tipo genitore).

  • Confronto di un campo di classe primitivo (dopo tutto, il confronto tramite =! è sufficiente per questo ), se il campo non è uguale - false .

  • Controllando un campo non primitivo per null ed equals (in String il metodo viene sovrascritto e verrà confrontato correttamente), se entrambi i campi sono null o equals , il controllo termina e il metodo restituisce true .

Codice hash:
  • Impostazione del valore del codice hash iniziale sulla primitiva dell'età dell'oggetto .

  • Moltiplicando il codice hash corrente per 31 (per una maggiore diffusione) e aggiungendovi il codice hash di un campo stringa non primitivo (se non è nullo).

  • Restituendo il risultato.

  • Come risultato di questa sovrascrittura del codice hash, gli oggetti con lo stesso nome e valori int restituiranno sempre lo stesso valore.

103. Qual è la differenza tra l'utilizzo di if (obj istanza di Student) e if (getClass() == obj.getClass())?

Diamo un'occhiata a cosa fa ciascun approccio:
  • exampleof controlla se un riferimento a un oggetto sul lato sinistro è un'istanza di un tipo sul lato destro o un suo sottotipo.

  • getClass() == ... controlla l'identità del tipo.

Cioè, se getClass() controlla l'identità completa di una classe, allora exampleof restituirà true anche se l'oggetto è solo un sottotipo, il che può darci maggiore flessibilità quando utilizziamo attivamente il polimorfismo. In realtà, entrambi gli approcci sono validi se si comprendono le caratteristiche del loro lavoro e le si applicano nei posti giusti.

104. Fornisci una breve descrizione del metodo clone().

Clone() è un metodo della classe Object , il cui scopo è creare e restituire un clone dell'oggetto corrente (una copia dell'oggetto corrente). Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 11 - 4Per usarlo, è necessario implementare l' interfaccia del marcatore clonabile :
Student implements Cloneable
E sovrascrivi il metodo clone() stesso :
@Override
protected Object clone() throws CloneNotSupportedException {
 return super.clone();
}
Dopotutto, nella classe Object è protetto, cioè sarà visibile solo nella classe Student stessa , ma non visibile alle classi dall'esterno.

105. Qual è la particolarità del metodo clone() che lavora con i campi di un oggetto di tipo riferimento?

Quando si clonano oggetti, vengono copiati solo i valori primitivi e il valore dei riferimenti agli oggetti. Ciò significa che se un oggetto ha un collegamento ad un altro oggetto nel suo campo interno, solo questo collegamento verrà clonato, ma quest'altro oggetto non verrà clonato. In effetti, questo è ciò che chiamano clonazione di superficie. E se avessi bisogno di una clonazione completa con la clonazione di tutti gli oggetti nidificati? Come assicurarsi che queste non siano copie di collegamenti, ma cloni a tutti gli effetti di oggetti con altre celle di memoria occupate nell'heap? In effetti, tutto è abbastanza semplice: per questo è necessario sovrascrivere anche il metodo clone() in ciascuna classe di questi oggetti interni e aggiungere un'interfaccia marcatore - Cloneable . Allora non saranno i riferimenti agli oggetti a essere copiati, ma gli oggetti stessi, perché ora hanno anche la capacità di copiare se stessi.

Eccezioni

106. Qual è la differenza tra errore ed eccezione?

Sia le eccezioni che gli errori sono sottoclassi della classe Throwable . Tuttavia, hanno le loro differenze. L'errore indica un problema che si verifica principalmente a causa di risorse di sistema insufficienti. E la nostra applicazione non dovrebbe rilevare questo tipo di problemi. Alcuni esempi di errori sono arresto anomalo del sistema ed errore di memoria insufficiente. Gli errori si verificano principalmente in fase di esecuzione poiché sono di tipo non controllato. Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 11 - 5Le eccezioni sono problemi che possono verificarsi in fase di esecuzione e in fase di compilazione. In genere ciò accade nel codice scritto dagli sviluppatori. Cioè, le eccezioni sono più prevedibili e dipendono maggiormente da noi come sviluppatori. Allo stesso tempo, gli errori sono più casuali e più indipendenti da noi, ma dipendono piuttosto da problemi con il sistema stesso in cui viene eseguita la nostra applicazione.

107. Qual è la differenza tra controllato e non controllato, eccezione, lancio, lancio.

Come ho detto prima, un'eccezione è un errore durante l'esecuzione del programma e durante la compilazione che si è verificato nel codice scritto dallo sviluppatore (a causa di qualche situazione anomala). Checked è un tipo di eccezione che deve essere sempre gestita utilizzando il meccanismo try-catch o generata nei metodi precedenti. Throws viene utilizzato nell'intestazione del metodo per indicare possibili eccezioni generate dal metodo. Cioè, questo è il meccanismo per “lanciare” eccezioni nei metodi di cui sopra. Unchecked è un tipo di eccezione che non necessita di essere gestita e in genere è meno prevedibile e meno probabile che si verifichi. Tuttavia, se lo si desidera, possono anche essere elaborati. Throw viene utilizzato quando si lancia manualmente un'eccezione, ad esempio:
throw new Exception();

108. Qual è la gerarchia delle eccezioni?

La gerarchia delle eccezioni è molto ampia ed estesa, addirittura troppo estesa per raccontarla qui. Considereremo quindi solo i suoi collegamenti chiave: Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 11 - 6qui al vertice della gerarchia vediamo la classe - Throwable - una classe generale, l'antenata della gerarchia delle eccezioni, che a sua volta è divisa in:
  • Errore : errori critici e non controllabili.
  • Eccezione : verifica le eccezioni.
L'eccezione è divisa in varie eccezioni di runtime non controllate e varie eccezioni controllate.

109. Cos'è l'eccezione controllata e non controllata?

Come ho detto prima:
  • Selezionato : eccezioni che devi in ​​qualche modo gestire, ovvero elaborarle in un blocco try - catch o "inoltrarle" al metodo sopra. Per fare ciò, nella firma del metodo, dopo aver elencato gli argomenti del metodo, è necessario utilizzare la parola chiave trows <exception type> , che indica agli utenti del metodo che il metodo può lanciare questa eccezione (qualcosa come un avviso) e trasferisce il file responsabilità di gestire l'eccezione per gli utenti di questo metodo.

  • Non controllato : eccezioni che non devono essere gestite, poiché non vengono controllate in fase di compilazione e, di norma, sono più imprevedibili. Cioè, la differenza principale con i controllati è che per loro questi meccanismi di cattura o lancio funzionano allo stesso modo, ma non sono obbligatori.

101. Scrivi un esempio di intercettazione e gestione di un'eccezione in un blocco try-catch di un metodo

try{                                                 // начало блока перехвата
 throw new Exception();                             // ручной бросок исключения
} catch (Exception e) {                              // данное исключение и его потомки будут перехватываться
 System.out.println("Упс, что-то пошло не так =("); // вывод некоторого исключения в консоль
}

102. Scrivi un esempio di come catturare e gestire un'eccezione utilizzando le tue eccezioni

Innanzitutto, scriviamo la nostra classe di eccezione, che eredita da Exception e sovrascrive il suo costruttore con un messaggio di errore:
public class CustomException extends Exception {

 public CustomException(final String message) {
   super(message);
 }
}
Bene, allora lo lanceremo manualmente e lo intercetteremo come nella domanda precedente:
try{
 throw new CustomException("Упс, что-то пошло не так =(");
} catch (CustomException e) {
 System.out.println(e.getMessage());
}
E ancora, quando lo esegui, otterrai il seguente output sulla console:
Ops, qualcosa è andato storto =(
Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 11 - 7Puoi trovare ulteriori informazioni sulle eccezioni qui . Bene, per oggi è tutto! Ci vediamo nella prossima parte!
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION