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 16

Pubblicato nel gruppo Random-IT
Ciao amico! Quanto tempo ci vuole per diventare uno sviluppatore? Ho chiesto a molte persone diverse e ho sentito molte risposte diverse. Per alcune persone può bastare anche un mese, ma per altre anche un anno non basterà. Ma so per certo che diventare uno sviluppatore Java è un percorso lungo e spinoso, indipendentemente dalle tue capacità iniziali. Dopotutto, non è tanto l'abilità ad essere importante quanto la testardaggine e il duro lavoro. Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 16 - 1Pertanto, oggi continuiamo ad analizzare in modo mirato le domande più popolari dell'intervista per gli sviluppatori Java. Studiarli ti avvicinerà gradualmente al tuo caro obiettivo. Iniziamo!

17. Fornisci esempi di utilizzo riuscito e infruttuoso dell'Opzionale

Supponiamo di avere una certa serie di valori attraverso i quali attraversiamo il flusso e alla fine otteniamo alcuni Opzionali come risultato:
Optional<String> stringOptional = Stream.of("a", "ab", "abc", "abcd")
   .filter(str -> str.length() >= 3)
   .findAny();
Come previsto, dobbiamo ottenere il valore da questo Optional . Usare solo get() è un brutto modo:
String result = stringOptional.get();
Ma questo metodo dovrebbe ottenere il valore da Opzionale e restituircelo? Questo è, ovviamente, vero, ma se ha un significato. Ebbene, se i valori nel flusso fossero diversi e alla fine ricevessimo un Opzionale vuoto , quando proviamo a prenderne un valore utilizzando il metodo get() , verrà lanciato quanto segue: Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 16 - 2Il che non va bene. In questo caso è meglio utilizzare le seguenti costruzioni:
  1. String result = null;
    if (stringOptional.isPresent()) {
     stringOptional.get();
    }

    In questo caso, stiamo controllando se l'elemento è in Optional . In caso contrario, la stringa risultante avrà il vecchio valore.

  2. String result = stringOptional.orElse("default value");

    In questo caso, specifichiamo un valore predefinito, che verrà assegnato alla stringa risultante nel caso di un Optional vuoto .

  3. String result = stringOptional.orElseThrow(() -> new CustomException());

    In questo caso, lanciamo noi stessi un'eccezione quando Optional è vuoto .

Ciò può essere utile in un'applicazione quando, ad esempio, viene utilizzato il metodo Spring JPA - findById() , che restituisce valori opzionali . In questo caso, con questo metodo proviamo a prendere il valore e, se non è presente, lanciamo qualche eccezione Runtime , che viene elaborata a livello di controller utilizzando ExceptionHandler e convertita in una risposta HTTP con lo stato 404 - NOT FOUND . Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 16 - 3

18. È possibile dichiarare un metodo main come final?

Sì, certo, nulla ci impedisce di dichiarare il metodo main() come final . Il compilatore non produrrà errori. Ma vale la pena ricordare che qualsiasi metodo dopo averlo dichiarato definitivo diventerà l'ultimo metodo , non sovrascritto. Tuttavia, chi ridefinirà main ??? Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 16 - 4

19. È possibile importare due volte lo stesso pacchetto/classe? Quali potrebbero essere le conseguenze?

Si, puoi. Conseguenze? Avremo un paio di importazioni non necessarie che Intelijj IDEA visualizzerà in grigio, ad es. inutilizzato. Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 16 - 5Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 16 - 6

20. Cos'è il casting? Quando possiamo ottenere una ClassCastException?

Il casting, o casting del tipo , è il processo di conversione di un tipo di dati in un altro tipo di dati: manualmente (casting implicito) o automaticamente (casting del tipo esplicito). Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 16 - 7La conversione automatica viene eseguita dal compilatore e la conversione manuale viene eseguita dallo sviluppatore. Il casting dei tipi per primitive e classi è leggermente diverso, quindi li considereremo separatamente. Tipi primitivi Un esempio di cast automatico di tipi primitivi:
int value = 17;
double convertedValue = value;
Come puoi vedere, qui non sono necessarie ulteriori manipolazioni oltre al segno = . Esempio di casting manuale di tipi primitivi:
double value = 17.89;
int convertedValue = (int)value;
In questo caso, possiamo osservare un cast manuale, che viene implementato utilizzando (int) , per cui la parte dopo la virgola verrà scartata e convertValue avrà un valore pari a -17. Maggiori informazioni sul cast dei tipi primitivi in ​​questo articolo . Bene, ora passiamo agli oggetti. Tipi di riferimento Per i tipi di riferimento, è possibile il casting automatico per le classi discendenti nelle classi principali. Questo è anche chiamato polimorfismo . Diciamo che abbiamo una classe Lion che eredita da una classe Cat . In questo caso, la conversione automatica sarà simile alla seguente:
Cat cat = new Lion();
Ma con un cast esplicito tutto è un po' più complicato, perché non esiste alcuna funzionalità per eliminare gli eccessi, come con i primitivi. E semplicemente facendo una conversione esplicita del modulo:
Lion lion= (Lion)new Cat();
Riceverai un errore: Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 16 - 8infatti puoi aggiungere metodi alla classe discendente Lion che non erano originariamente nella classe Cat , e poi provare a chiamarli, perché il tuo tipo di oggetto diventerà Lion . Ebbene, non c'è logica in questo. Pertanto, la restrizione del tipo è possibile solo quando l'oggetto originale è di tipo Lion ma è stato successivamente convertito in una classe genitore:
Lion lion = new Lion();
Cat cat = lion;
Lion newLion = (Lion)cat;
Inoltre, per una maggiore affidabilità, si consiglia di restringere il cast per gli oggetti utilizzando il costrutto istanzaOf :
if (cat instanceof Lion) {
 newLion = (Lion)new Cat();
}
Maggiori informazioni sui cast dei tipi di riferimento in questo articolo .

21. Perché i framework moderni utilizzano principalmente solo eccezioni non controllate?

Penso che questo sia tutto perché la gestione delle eccezioni controllate è ancora un codice spaghetti che si ripete ovunque, ma non è realmente necessario in tutti i casi. Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 16 - 9In questi casi, è più semplice eseguire l'elaborazione all'interno del framework, in modo da non scaricarla nuovamente sulle spalle degli sviluppatori. Sì, certo, può verificarsi una situazione di emergenza, ma queste stesse eccezioni non controllate possono essere gestite in modo più conveniente, senza preoccuparsi dell'elaborazione in try-catch e senza passarle ulteriormente attraverso i metodi. È sufficiente convertire l'eccezione in una risposta HTTP in eccezioneHandler .

22. Cos'è l'importazione statica?

Quando si utilizzano dati statici (metodi, variabili), non è possibile creare l'oggetto stesso, ma farlo tramite il nome della classe, ma anche in questo caso abbiamo bisogno di un riferimento alla classe. Tutto è semplice con esso: viene aggiunto utilizzando l'importazione regolare. Ma cosa succede se andiamo ad utilizzare un metodo statico senza scrivere il nome della classe, come se fosse un metodo statico della classe corrente? Questo è possibile con l'importazione statica! In questo caso, dobbiamo scrivere l'importazione statica e un collegamento a quel metodo. Come questo, ad esempio, un metodo statico della classe Math per il calcolo del valore del coseno:
import static java.lang.Math.cos;
Di conseguenza, possiamo utilizzare il metodo senza specificare il nome della classe:
double result = cos(60);
Possiamo anche semplicemente caricare tutti i metodi statici di una classe contemporaneamente utilizzando l'importazione statica:
import static java.lang.Math.*;
Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 16 - 10

23. Qual è la relazione tra i metodi hashCode() e equals()?

Secondo Oracle , la regola è: se due oggetti sono uguali (ovvero il metodo equals() restituisce true ), devono avere lo stesso codice hash. Allo stesso tempo, non dimenticare che due oggetti diversi possono avere lo stesso codice hash. Per capire perché equals() e hashCode() vengono sempre sovrascritti in coppia, considera i seguenti casi:
  1. Entrambi i metodi vengono sovrascritti.

    In questo caso, due oggetti diversi con gli stessi stati interni restituiranno equals() - true , mentre hashCode() restituiranno entrambi lo stesso numero.

    Si scopre che va tutto bene, perché la regola viene rispettata.

  2. Entrambi i metodi non vengono sovrascritti.

    In questo caso, due oggetti diversi con gli stessi stati interni restituiranno false quando equals() , poiché il confronto avviene per riferimento tramite l' operatore == .

    Anche il metodo hashCode() restituirà valori diversi (molto probabilmente) poiché produce il valore convertito dell'indirizzo della posizione di memoria. Ma per lo stesso oggetto questo valore sarà lo stesso, così come equals() in questo caso restituirà true solo quando i riferimenti puntano allo stesso oggetto.

    Si scopre che in questo caso va tutto bene e la regola è rispettata.

  3. Equals() sovrascritto , non hashCode() sovrascritto .

    In questo caso, per due oggetti diversi con gli stessi stati interni, equals() restituirà true e hashCode() restituirà (molto probabilmente) valori diversi.

    Questa è una violazione della regola, quindi non è consigliabile farlo.

  4. equals() non viene sovrascritto , hashCode() viene sovrascritto .

    In questo caso, per due oggetti diversi con gli stessi stati interni, equals() restituirà false e hashCode() restituirà gli stessi valori.

    C'è una violazione della regola, quindi l'approccio non è corretto.

Come puoi vedere, la regola può essere eseguita solo quando equals() e hashCode() vengono entrambi sovrascritti o entrambi non vengono sovrascritti affatto. Maggiori informazioni Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 16 - 11su equals() e hashCode() in questo articolo .

24. Quando vengono utilizzate le classi BufferedInputStream e BufferedOutputStream?

InputStream viene utilizzato per leggere i dati byte per byte da alcune risorse e OutputStream viene utilizzato per scrivere i dati byte per byte. Ma le operazioni byte-byte possono essere molto scomode e richiedere un'elaborazione aggiuntiva (per leggere/scrivere normalmente i testi). In realtà, per semplificare tali record di byte, è stato introdotto BufferedOutputStream e BufferedInputStream per la lettura . Queste classi non sono altro che buffer che accumulano dati, consentendo di lavorare con i dati non byte per byte, ma con interi pacchetti di dati (array). Una volta creato, BufferedInputStream accetta nel suo costruttore un'istanza del tipo InputStream , da cui vengono letti i dati:
BufferedInputStream bufferedInputStream = new BufferedInputStream(System.in);
byte[] arr = new byte[100];
bufferedInputStream.read(arr);
System.in è un oggetto InputStream che legge i dati dalla console. Cioè, utilizzando questo oggetto BufferedInputStream , possiamo leggere i dati da InputStream scrivendoli nell'array passato. Questo risulta essere una sorta di wrapper della classe InputStream . L'array arr di questo esempio è l'array che riceve i dati da BufferedInputStream . Questo, a sua volta, legge i dati dall'InputStream con un altro array, che per impostazione predefinita ha una dimensione di 2048 byte. Lo stesso vale per BufferedOutputStream : occorre passare al costruttore un'istanza del tipo OutputStream , nella quale scriveremo i dati in interi array:
byte[] arr = "Hello world!!!".getBytes();
BufferedOutputStream bufferedInputStream = new BufferedOutputStream(System.out);
bufferedInputStream.write(arr);
bufferedInputStream.flush();
System.out è un oggetto OutputStream che scrive dati sulla console. Il metodo flush() invia i dati da BufferedOutputStream a OutputStream , scaricando BufferedOutputStream nel processo . Senza questo metodo non verrà registrato nulla. E simile all'esempio precedente: arr è l'array da cui i dati vengono scritti in BufferedOutputStream . Da lì vengono scritti su OutputStream in un array diverso, che per impostazione predefinita ha una dimensione di 512 byte. Maggiori informazioni su queste due classi nell'articolo .

25. Qual è la differenza tra le classi java.util.Collection e java.util.Collections?

La raccolta è un'interfaccia che è a capo della gerarchia della raccolta. Presenta classi che consentono di creare, contenere e modificare interi gruppi di oggetti. Sono disponibili molti metodi a questo scopo, come add() , remove() , contiene() e altri. Principali interfacce della classe Collection :
  • Set è un'interfaccia che descrive un set che contiene elementi univoci (non ripetitivi) non ordinati.

  • List è un'interfaccia che descrive una struttura dati che memorizza una sequenza ordinata di oggetti. Questi oggetti ricevono il proprio indice (numero), utilizzando il quale è possibile interagire con essi: prendere, eliminare, modificare, sovrascrivere.

  • La coda è un'interfaccia che descrive una struttura dati con elementi di memorizzazione sotto forma di una coda che segue la regola FIFO - First In First Out .

Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 16 - 12Ulteriori informazioni sulla Collezione . Collections è una classe di utilità che fornisce molti metodi di utilità diversi. Per esempio:
  • addAll(Collection<? super T> collection, T...element) - aggiunge gli elementi passati di tipo T alla collection .

  • copy(List<? super T> dest, List<? extends T> src) - copia tutti gli elementi dalla lista src alla lista in dest .

  • listavuota() - restituisce una lista vuota.

  • max(Collection<? extends T> collection, Comparator<? super T> comp) - Restituisce l'elemento massimo di una determinata raccolta in base all'ordine specificato dal comparatore specificato.

  • unmodifyingList(List<? extends T> list) - restituisce una rappresentazione immodificabile dell'elenco passato.

E ci sono moltissimi metodi convenienti in Raccolte . Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 16 - 13Un elenco completo di questi metodi è disponibile sul sito Web di Oracle . Non per niente ho detto che sono comodi. Dopotutto, sono tutti statici. Cioè, non è necessario creare ogni volta un oggetto di questa classe per chiamare su di esso il metodo necessario. Devi solo inserire il nome della classe, chiamare su di essa il metodo desiderato e passare tutti gli argomenti richiesti. Per riassumere, Collection è l'interfaccia radice del framework delle raccolte. Collections è una classe helper per l'elaborazione più conveniente di oggetti appartenenti a un tipo dalla struttura delle raccolte. Bene, per oggi è tutto. Ti auguro il meglio!Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 16 - 14
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION