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 2

Pubblicato nel gruppo Random-IT
Ciao di nuovo a tutti! Continuiamo a cercare risposte a oltre 250 domande per sviluppatori Junior, Middle e Senior. Le domande sono piuttosto interessanti e io stesso mi piace analizzarle: in questi momenti puoi scoprire lacune nella conoscenza teorica e nei luoghi più inaspettati. Analisi delle domande e risposte dell'intervista.  Parte 2 - 1La parte precedente può essere trovata in questo articolo . Ma prima di iniziare, voglio ricordarti che:
  1. Tralascerò le domande che si intersecano con questa serie di articoli per non duplicare ancora una volta le informazioni. Consiglio di leggere questi materiali poiché contengono le domande più comuni (popolari) dell'intervista Java Core.
  2. Le domande sulla DOU sono presentate in ucraino, ma qui avrò tutto in russo.
  3. Le risposte potrebbero essere descritte in modo più dettagliato, ma non lo farò, dato che la risposta ad ogni domanda potrebbe richiedere un intero articolo. E non ti chiederanno dettagli così dettagliati in nessun colloquio.
Se necessario, lascerò link per approfondimenti. Voliamo!

11. Assegna un nome a tutti i metodi della classe Object

La classe Object ha 11 metodi:
  • Class<?> getClass() — ottiene la classe dell'oggetto corrente;
  • int hashCode() : ottiene il codice hash dell'oggetto corrente;
  • booleano uguale​(Oggetto obj) - confronto dell'oggetto corrente con un altro;
  • Object clone() - crea e restituisce una copia dell'oggetto corrente;
  • String toString() — ottenere una rappresentazione di stringa di un oggetto;
  • void notify() - risveglia un thread in attesa sul monitor di questo oggetto (la selezione del thread è casuale);
  • void notifyAll() - risveglia tutti i thread in attesa sul monitor di questo oggetto;
  • void wait() - mette il thread corrente in modalità standby (lo blocca) sul monitor corrente, funziona solo in un blocco sincronizzato finché qualche notifica o notifyAll non riattiva il thread;
  • void wait(long timeout) - congela anche il thread corrente sul monitor corrente (su quello attualmente sincronizzato), ma con un timer per uscire da questo stato (o ancora: finché notify o notifyAll non si sveglia);
  • void wait(long timeout, int nanos) - un metodo simile a quello sopra descritto, ma con timer più precisi per uscire dal congelamento;
  • void finalize() - prima di eliminare questo oggetto, il garbage collector chiama questo metodo (finalmente). Viene utilizzato per ripulire le risorse occupate.
Per utilizzare correttamente i metodi hashCode , equals​ , clone , toString e finalize , è necessario ridefinirli, tenendo conto dell'attività e delle circostanze attuali.

12. Qual è la differenza tra try-with-resources e try-catch-finally quando si ha a che fare con le risorse?

In genere, quando si utilizza try-catch-finally, il blocco finale veniva utilizzato per chiudere le risorse. Java 7 ha introdotto un nuovo tipo di operatore try-with-resources , un analogo di try-catch-finalmente per liberare risorse, ma più compatto e leggibile. Ricordiamo come appare try-catch-finally :
String text = "some text......";
BufferedWriter bufferedWriter = null;
try {
   bufferedWriter = new BufferedWriter(new FileWriter("someFileName"));
   bufferedWriter.write(text);
} catch (IOException e) {
   e.printStackTrace();
} finally {
   try {
       bufferedWriter.close();
   } catch (IOException e) {
       e.printStackTrace();
   }
}
Ora riscriviamo questo codice, ma utilizzando try-with-resources :
String text = "some text......";
try(BufferedWriter bufferedWriter =new BufferedWriter(new FileWriter("someFileName"))) {
   bufferedWriter.write(text);
} catch (IOException e) {
   e.printStackTrace();
}
In qualche modo è diventato più facile, non credi? Oltre alla semplificazione, ci sono un paio di punti:
  1. In try-with-resources , le risorse dichiarate tra parentesi (che verranno chiuse) devono implementare l'interfaccia AutoCloseable e il suo unico metodo, close() .

    Il metodo close viene eseguito in un implicito last block , altrimenti come farà il programma a capire esattamente come chiudere una determinata risorsa?

    Ma, molto probabilmente, raramente scriverai le tue implementazioni delle risorse e il loro metodo di chiusura.

  2. Sequenza di esecuzione del blocco:

    1. prova a bloccare .
    2. Implicito, infine .
    3. Un blocco catch che rileva le eccezioni nei passaggi precedenti.
    4. Finalmente esplicito .

    Di norma, le eccezioni che appaiono più in basso nell'elenco interrompono quelle che appaiono più in alto.

Immagina una situazione in cui quando usi try-catch-finally si verifica un'eccezione nel tuo try . Di conseguenza, inizia immediatamente l'esecuzione di un blocco catch specifico , in cui si scrive un'altra eccezione (ad esempio, con un messaggio che descrive l'errore in modo più dettagliato) e si desidera che il metodo lanci ulteriormente questa eccezione. Successivamente viene eseguita l'esecuzione del last block e viene generata anche un'eccezione. Ma questo è diverso. Quale di queste due eccezioni genererà alla fine questo metodo? Eccezione lanciata dal last block ! Ma c'è anche un punto con try-with-resources . Ora diamo un'occhiata al comportamento di try-with-resources nella stessa situazione. Otteniamo un'eccezione nel blocco try quando proviamo a chiudere le risorse nel metodo close() , cioè nell'implicito finalmente . Quale di queste eccezioni verrà catturata ? Quello lanciato dal blocco try ! Un'eccezione implicita infine (dal metodo close() ) verrà ignorata. Questo ignorare è anche chiamato soppressione delle eccezioni.

13. Cosa sono le operazioni bit a bit?

Le operazioni bit a bit sono operazioni su stringhe di bit che includono operazioni logiche e spostamenti bit a bit. Operazioni logiche:
  • AND bit a bit : confronta i valori dei bit e, nel processo, qualsiasi bit impostato su 0 (falso) imposta il bit corrispondente nel risultato come 0. Cioè, se in entrambi i valori confrontati il ​​bit era 1 (vero), il anche il risultato sarà 1.

    Indicato come - AND , &

    Esempio: 10111101 e 01100111 = 00100101

  • L'OR bit a bit è l'operazione inversa della precedente. Qualsiasi bit impostato su 1 imposta un bit simile nel risultato come 1. Di conseguenza, se il bit era 0 in entrambi i valori confrontati, anche il bit risultante sarà 0.

    Indicato come -OR , |

    Esempio: 10100101 | 01100011 = 11100111

  • NOT bit a bit : applicato a un valore, capovolge (inverte) i bit. Cioè, quei bit che erano 1 diventeranno 0; e quelli che erano 0 diventeranno 1.

    Indicato come - NOT , ~

    Esempio: ~10100101 = 01011010

  • OR esclusivo bit per bit : confronta i valori dei bit e se in entrambi i valori il bit è uguale a 1, il risultato sarà 0 e se in entrambi i valori il bit è 0, il risultato sarà 0. Cioè, affinché il risultato sia uguale a 1, solo uno dei bit deve essere uguale a 1 e il secondo è uguale a 0.

    Indicato come -XOR , ^

    Esempio: 10100101 ^ 01100011 = 11000110

Spostamenti bit a bit - >> o << spostano i bit di un valore nella direzione specificata, del numero specificato. Le posizioni vacanti vengono riempite con zeri. Per esempio:
  1. 01100011 >> 4 = 00000110
  2. 01100011 << 3 = 00011000
C'è anche un'eccezione quando si sposta a destra un numero negativo. Come ricordi, il primo bit è responsabile del segno e se questo bit è uguale a 1, il numero è negativo. Se si sposta un numero negativo, le posizioni libere non verranno più riempite con zeri, ma con unità, poiché è necessario mantenere il bit di segno. Ad esempio: 10100010 >> 2 = 11101000 Allo stesso tempo, in Java esiste un ulteriore operatore di spostamento a destra senza segno >>> Questo operatore è un analogo di >>, durante lo spostamento, le posizioni libere vengono riempite con 0, indipendentemente dal fatto che il numero è negativo o positivo. Ad esempio: 10100010 >>> 2 = 00101000 Maggiori informazioni sulle operazioni bit a bit qui . Analisi delle domande e risposte dell'intervista.  Parte 2 - 2Come esempio dell'uso degli spostamenti bit a bit in Java, puoi citare il metodo hash() di una HashMap, che viene utilizzato per determinare uno speciale codice hash interno per una chiave: Analisi delle domande e risposte dell'intervista.  Parte 2 - 3questo metodo consente di distribuire uniformemente i dati in una HashMap per ridurre al minimo il numero di collisioni.

14. Quali classi immutabili standard sono oggetti in Java?

Immutabile è un oggetto che non consente la modifica dei suoi parametri originali. Potrebbe avere metodi che restituiscono nuovi oggetti di un determinato tipo, con parametri che desideri modificare. Alcuni oggetti immutabili standard:
  • L'oggetto immutabile di gran lunga più famoso in Java è String;
  • istanze di classi wrapper che racchiudono tipi standard: Boolean, Character, Byte, Short, Integer, Long, Double, Float;
  • oggetti che vengono solitamente utilizzati per numeri particolarmente GRANDI: BigInteger e BigDecimal;
  • un oggetto che è un'unità in stacktrace (ad esempio, in un'eccezione stacktrace) StackTraceElement;
  • un oggetto della classe File: può modificare i file, ma allo stesso tempo è immutabile;
  • UUID - che viene spesso utilizzato come ID univoco per gli elementi;
  • tutti gli oggetti classe del pacchetto java.time;
  • Locale: utilizzato per definire una regione geografica, politica o culturale.

15. Quali sono i vantaggi di un oggetto immutabile rispetto agli oggetti normali?

  1. Tali oggetti sono sicuri se utilizzati in un ambiente multi-thread . Utilizzandoli, non devi preoccuparti di perdere dati a causa delle condizioni di thread race. A differenza del lavoro con oggetti ordinari: in questo caso dovrai pensare con molta attenzione ed elaborare i meccanismi per utilizzare l'oggetto in un ambiente parallelo.
  2. Gli oggetti immutabili sono buone chiavi in ​​una mappa, perché se usi un oggetto mutabile e poi l'oggetto cambia il suo stato, può creare confusione quando usi una HashMap: l'oggetto sarà ancora presente e se usi contieneKey() potrebbe non farlo Essere trovato.
  3. Gli oggetti immutabili sono ottimi per archiviare dati immutabili (costanti) che non dovrebbero mai essere modificati mentre il programma è in esecuzione.
  4. "Atomicità al fallimento": se un oggetto immutabile genera un'eccezione, non rimarrà comunque in uno stato indesiderato (rotto).
  5. Queste classi sono facili da testare.
  6. Non sono necessari meccanismi aggiuntivi come un costruttore di copie e un'implementazione di clonazione.

Domande sull'OOP

Analisi delle domande e risposte dell'intervista.  Parte 2 - 4

16. Quali sono i vantaggi dell'OOP in generale e rispetto alla programmazione procedurale?

Quindi, i vantaggi dell'OOP:
  1. Le applicazioni complesse sono più facili da scrivere rispetto alla programmazione procedurale, poiché tutto è suddiviso in piccoli moduli - oggetti che interagiscono tra loro - e di conseguenza la programmazione si riduce alle relazioni tra gli oggetti.
  2. Le applicazioni scritte utilizzando la programmazione orientata agli oggetti sono molto più facili da modificare (a condizione che vengano seguiti i concetti di progettazione).
  3. Poiché i dati e le operazioni su di esso formano un'unica entità, non vengono sparsi in tutta l'applicazione (cosa che spesso accade con la programmazione procedurale).
  4. L'incapsulamento delle informazioni protegge i dati più critici dall'utente.
  5. È possibile riutilizzare lo stesso codice con dati diversi, perché le classi consentono di creare molti oggetti, ognuno dei quali ha i propri valori di attributo.
  6. L'ereditarietà e il polimorfismo consentono inoltre di riutilizzare ed estendere il codice esistente (invece di duplicare funzionalità simili).
  7. Estendibilità dell'applicazione più semplice rispetto a un approccio procedurale.
  8. L'approccio OOP consente di astrarre dai dettagli di implementazione.

17. Raccontaci quali carenze ci sono nell'OOP

Purtroppo sono presenti anche:
  1. L'OOP richiede molte conoscenze teoriche che devono essere padroneggiate prima di poter scrivere qualsiasi cosa.Analisi delle domande e risposte dell'intervista.  Parte 2 - 5
  2. Le idee dell’OOP non sono così facili da comprendere e applicare nella pratica (bisogna essere un po’ filosofi in fondo).
  3. Quando si utilizza OOP, le prestazioni del software sono leggermente ridotte a causa dell'organizzazione più complessa del sistema.
  4. L'approccio OOP richiede più memoria, poiché tutto è costituito da classi, interfacce, metodi, che occupano molta più memoria rispetto alle normali variabili.
  5. Il tempo necessario per l’analisi iniziale è maggiore rispetto a quello procedurale.

18. Cos'è il polimorfismo statico e dinamico

Il polimorfismo consente agli oggetti di comportarsi in modo diverso per la stessa classe o interfaccia. Esistono due tipi di polimorfismo, noti anche come legame precoce e tardivo . Polimorfismo statico o legame precedente:
  • si verifica in fase di compilazione (all'inizio del ciclo di vita del programma);
  • decide quale metodo eseguire in fase di compilazione;
  • Il sovraccarico del metodo è un esempio di polimorfismo statico;
  • l'associazione anticipata include metodi privati, statici e terminali;
  • l'ereditarietà non è coinvolta nell'associazione anticipata;
  • Il polimorfismo statico non coinvolge oggetti specifici, ma informazioni sulla classe, il cui tipo è rappresentato a sinistra del nome della variabile.
Polimorfismo dinamico o legame tardivo:
  • si verifica in fase di esecuzione (mentre il programma è in esecuzione);
  • il polimorfismo dinamico decide quale implementazione specifica avrà un metodo in fase di esecuzione;
  • l'override del metodo è un esempio di polimorfismo dinamico;
  • l'associazione tardiva è l'assegnazione di un oggetto specifico, un riferimento al suo tipo o alla sua superclasse;
  • l'ereditarietà è associata al polimorfismo dinamico.
Puoi leggere ulteriori informazioni sulle differenze tra l'associazione anticipata e tardiva in questo articolo .

19. Definire il principio di astrazione nell'OOP

L'astrazione in OOP è un modo per evidenziare un insieme di caratteristiche significative di un oggetto, escludendo dettagli non importanti. Cioè, quando si progetta un programma con un approccio OOP, ci si concentra sui modelli in generale, senza approfondire i dettagli della loro implementazione. In Java, le interfacce sono responsabili dell'astrazione . Ad esempio, hai una macchina e questa sarà l'interfaccia. E varie interazioni con esso, ad esempio l'avvio del motore, l'utilizzo del cambio, sono funzioni che utilizziamo senza entrare nei dettagli di implementazione. Dopotutto, nel momento in cui guidi un'auto, non pensi a come esattamente il cambio adempie al suo scopo, o a come la chiave avvia il motore, o esattamente a come il volante gira le ruote. E anche se l'implementazione di una di queste funzionalità viene sostituita (ad esempio il motore), potresti non notarlo. Questo non ti importa: non entri nei dettagli dell'implementazione. Per te è importante che l'azione venga eseguita. In realtà, questa è un'astrazione dai dettagli di implementazione. È qui che ci fermeremo oggi: continua!Analisi delle domande e risposte dell'intervista.  Parte 2 - 6
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION