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 14

Pubblicato nel gruppo Random-IT
Fuochi d'artificio! Il mondo è in costante movimento e noi siamo in costante movimento. Prima, per diventare uno sviluppatore Java, bastava conoscere un po' la sintassi Java e il resto veniva. Nel corso del tempo, il livello di conoscenza richiesto per diventare uno sviluppatore Java è cresciuto in modo significativo, così come la concorrenza, che continua a spingere verso l’alto il livello inferiore delle conoscenze richieste. Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 14 - 1Se vuoi davvero diventare uno sviluppatore, devi darlo per scontato e prepararti a fondo per distinguerti tra i principianti come te. Quello che faremo oggi, ovvero, continueremo ad analizzare le oltre 250 domande . Negli articoli precedenti abbiamo esaminato tutte le domande di livello junior, mentre oggi affronteremo le domande di livello medio. Anche se noto che queste non sono domande di livello medio al 100%, puoi incontrarne la maggior parte durante un colloquio di livello junior, perché è in tali colloqui che avviene un'indagine dettagliata della tua base teorica, mentre per uno studente di livello medio il le domande sono più mirate a sondare la sua esperienza. Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 14 - 2Ma, senza ulteriori indugi, cominciamo.

Mezzo

Sono comuni

1. Quali sono i vantaggi e gli svantaggi dell'OOP rispetto alla programmazione procedurale/funzionale?

C'era questa domanda nell'analisi delle domande per Juinior e di conseguenza ho già risposto. Cerca questa domanda e la sua risposta in questa parte dell'articolo, domande 16 e 17.

2. In cosa differisce l'aggregazione dalla composizione?

In OOP, esistono diversi tipi di interazione tra oggetti, riuniti sotto il concetto generale di "Relazione Has-A". Questa relazione indica che un oggetto è un componente di un altro oggetto. Allo stesso tempo, ci sono due sottotipi di questa relazione: Composizione : un oggetto crea un altro oggetto e la vita di un altro oggetto dipende dalla vita del creatore. Aggregazione - un oggetto riceve un collegamento (puntatore) ad un altro oggetto durante il processo di costruzione (in questo caso, la vita dell'altro oggetto non dipende dalla vita del creatore). Per una migliore comprensione, diamo un'occhiata a un esempio specifico. Abbiamo una certa classe di auto - Car , che a sua volta ha campi interni del tipo - Engine e un elenco di passeggeri - List<Passenger> , ha anche un metodo per avviare il movimento - startMoving() :
public class Car {

 private Engine engine;
 private List<Passenger> passengers;

 public Car(final List<Passenger> passengers) {
   this.engine = new Engine();
   this.passengers = passengers;
 }

 public void addPassenger(Passenger passenger) {
   passengers.add(passenger);
 }

 public void removePassengerByIndex(Long index) {
   passengers.remove(index);
 }

 public void startMoving() {
   engine.start();
   System.out.println("Машина начала своё движение");
   for (Passenger passenger : passengers) {
     System.out.println("В машине есть пассажир - " + passenger.getName());
   }
 }
}
In questo caso la Composition è la connessione tra Car e Engine , poiché le prestazioni dell'auto dipendono direttamente dalla presenza dell'oggetto engine, perché se engine = null , allora riceveremo una NullPointerException . A sua volta, un motore non può esistere senza una macchina (perché abbiamo bisogno di un motore senza una macchina?) e non può appartenere a più macchine contemporaneamente. Ciò significa che se eliminiamo l' oggetto Car , non ci saranno più riferimenti all'oggetto Engine , e presto verrà eliminato dal Garbage Collector . Come puoi vedere, questa relazione è molto stretta (forte). L'aggregazione è il collegamento tra Auto e Passeggero , poiché le prestazioni dell'Auto non dipendono in alcun modo dagli oggetti di tipo Passeggero e dal loro numero. Possono lasciare l'auto -removePassengerByIndex(indice lungo) o inserirne di nuovi - addPassenger(passeggero passeggero) , nonostante ciò, l'auto continuerà a funzionare correttamente. A loro volta, gli oggetti Passenger possono esistere senza un oggetto Car . Come capisci, questa è una connessione molto più debole di quella che vediamo nella composizione. Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 14 - 3Ma non è tutto, un oggetto che è connesso per aggregazione ad un altro può avere anche una determinata connessione con altri oggetti nello stesso momento. Ad esempio, tu, come studente Java, sei iscritto contemporaneamente ai corsi di inglese, OOP e logaritmi, ma allo stesso tempo non ne sei una parte criticamente necessaria, senza la quale il normale funzionamento è impossibile (come ad esempio Un insegnante).

3. Quali modelli GoF hai utilizzato nella pratica? Dare esempi.

Ho già risposto a questa domanda in precedenza, quindi lascerò semplicemente un collegamento all'analisi , vedere la prima domanda. Ho anche trovato un meraviglioso articolo sui modelli di progettazione, che consiglio vivamente di tenere a portata di mano.

4. Cos'è un oggetto proxy? Dare esempi

Un proxy è un modello di progettazione strutturale che consente di sostituire oggetti sostitutivi speciali, o in altre parole, oggetti proxy, invece di oggetti reali. Questi oggetti proxy intercettano le chiamate all'oggetto originale, consentendo l'inserimento di una logica prima o dopo che la chiamata viene passata all'oggetto originale. Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 14 - 4Esempi di utilizzo di un oggetto proxy:
  • Come proxy remoto: utilizzato quando abbiamo bisogno di un oggetto remoto (un oggetto in uno spazio di indirizzi diverso) che deve essere rappresentato localmente. In questo caso, il proxy gestirà la creazione della connessione, la codifica, la decodifica, ecc., mentre il client lo utilizzerà come se fosse l'oggetto originale situato nello spazio locale.

  • Come proxy virtuale: utilizzato quando è necessario un oggetto ad uso intensivo di risorse. In questo caso, l'oggetto proxy funge da qualcosa di simile all'immagine di un oggetto reale che in realtà non esiste ancora. Quando una richiesta reale (chiamata al metodo) viene inviata a questo oggetto, solo allora l'oggetto originale viene caricato e il metodo viene eseguito. Questo approccio è anche chiamato inizializzazione ritardata; questo può essere molto comodo, perché in alcune situazioni l'oggetto originale potrebbe non essere utile, e quindi non ci sarà alcun costo per crearlo.

  • Come proxy di sicurezza: utilizzato quando è necessario controllare l'accesso ad alcuni oggetti in base ai diritti del client. Cioè, se un client con diritti di accesso mancanti tenta di accedere all'oggetto originale, il proxy lo intercetterà e non lo consentirà.

Diamo un'occhiata ad un esempio di proxy virtuale: abbiamo un'interfaccia di gestione:
public interface Processor {
 void process();
}
La cui implementazione utilizza troppe risorse, ma allo stesso tempo potrebbe non essere utilizzata ogni volta che si avvia l'applicazione:
public class HiperDifficultProcessor implements Processor {
 @Override
 public void process() {
   // некоторый сверхсложная обработка данных
 }
}
Classe proxy:
public class HiperDifficultProcessorProxy implements Processor {
private HiperDifficultProcessor processor;

 @Override
 public void process() {
   if (processor == null) {
     processor = new HiperDifficultProcessor();
   }
   processor.process();
 }
}
Eseguiamolo in main :
Processor processor = new HiperDifficultProcessorProxy();
// тут тяжеловсеного оригинального an object, ещё не сущетсвует
// но при этом есть an object, который его представляет и у которого можно вызывать его методы
processor.process(); // лишь теперь, an object оригинал был создан
Noto che molti framework utilizzano il proxy e per Spring questo è un modello chiave (Spring è cucito con esso dentro e fuori). Maggiori informazioni su questo modello qui . Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 14 - 5

5. Quali innovazioni sono state annunciate in Java 8?

Le novità portate da Java 8 sono le seguenti:
  • Sono state aggiunte interfacce funzionali, leggi che tipo di bestia è qui .

  • Per le espressioni lambda, strettamente correlate alle interfacce funzionali, leggi ulteriori informazioni sul loro utilizzo qui .

  • Aggiunta l'API Stream per una comoda elaborazione delle raccolte dati, maggiori informazioni qui .

  • Aggiunti collegamenti ai metodi .

  • Il metodo forEach() è stato aggiunto all'interfaccia Iterable .

  • Aggiunta una nuova API di data e ora nel pacchetto java.time , analisi dettagliata qui .

  • API simultanea migliorata .

  • Aggiungendo una classe wrapper opzionale , che viene utilizzata per gestire correttamente i valori null, puoi trovare un ottimo articolo su questo argomento qui .

  • Aggiungendo la possibilità per le interfacce di utilizzare metodi statici e predefiniti (che, in sostanza, avvicina Java all'ereditarietà multipla), maggiori dettagli qui .

  • Aggiunti nuovi metodi alla classe Collection(removeIf(), spliterator()) .

  • Piccoli miglioramenti a Java Core.

Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 14 - 6

6. Cosa sono l'alta coesione e il basso accoppiamento? Dare esempi.

Alta Coesione o Alta Coesione è il concetto quando una determinata classe contiene elementi strettamente correlati tra loro e combinati per il loro scopo. Ad esempio, tutti i metodi nella classe User dovrebbero rappresentare il comportamento dell'utente. Una classe ha una bassa coesione se contiene elementi non correlati. Ad esempio, la classe User contenente un metodo di convalida dell'indirizzo email:
public class User {
private String name;
private String email;

 public String getName() {
   return this.name;
 }

 public void setName(final String name) {
   this.name = name;
 }

 public String getEmail() {
   return this.email;
 }

 public void setEmail(final String email) {
   this.email = email;
 }

 public boolean isValidEmail() {
   // некоторая логика валидации емейла
 }
}
La classe utente può essere responsabile della memorizzazione dell'indirizzo email dell'utente, ma non della sua convalida o dell'invio dell'email. Pertanto, per ottenere un'elevata coerenza, spostiamo il metodo di validazione in una classe di utilità separata:
public class EmailUtil {
 public static boolean isValidEmail(String email) {
   // некоторая логика валидации емейла
 }
}
E lo usiamo secondo necessità (ad esempio, prima di salvare l'utente). Low Coupling o Low Coupling è un concetto che descrive la bassa interdipendenza tra i moduli software. Essenzialmente, l’interdipendenza è il modo in cui il cambiamento di uno richiede il cambiamento dell’altro. Due classi hanno un accoppiamento forte (o accoppiamento stretto) se sono strettamente correlate. Ad esempio, due classi concrete che archiviano riferimenti reciproci e chiamano i rispettivi metodi. Le classi liberamente accoppiate sono più facili da sviluppare e mantenere. Poiché sono indipendenti l'uno dall'altro, possono essere sviluppati e testati in parallelo. Inoltre, possono essere modificati e aggiornati senza influenzarsi a vicenda. Consideriamo un esempio di classi fortemente accoppiate. Abbiamo alcune classi di studenti:
public class Student {
 private Long id;
 private String name;
 private List<Lesson> lesson;
}
Che contiene un elenco di lezioni:
public class Lesson {
 private Long id;
 private String name;
 private List<Student> students;
}
Ogni lezione contiene un link agli studenti frequentanti. Presa incredibilmente forte, non credi? Come puoi ridurlo? Innanzitutto, assicuriamoci che gli studenti non abbiano un elenco di materie, ma un elenco dei loro identificatori:
public class Student {
 private Long id;
 private String name;
 private List<Long> lessonIds;
}
In secondo luogo, non è necessario che la classe della lezione conosca tutti gli studenti, quindi eliminiamo del tutto la loro lista:
public class Lesson {
 private Long id;
 private String name;
}
Quindi è diventato molto più semplice e la connessione è diventata molto più debole, non credi? Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 14 - 7

Ops

7. Come è possibile implementare l'ereditarietà multipla in Java?

L'ereditarietà multipla è una caratteristica del concetto orientato agli oggetti in cui una classe può ereditare proprietà da più di una classe genitore. Il problema sorge quando sono presenti metodi con la stessa firma sia nella superclasse che nella sottoclasse. Quando si chiama un metodo, il compilatore non può determinare quale metodo di classe deve essere chiamato e anche quando si chiama il metodo di classe che ha la precedenza. Pertanto, Java non supporta l'ereditarietà multipla! Ma esiste una sorta di scappatoia, di cui parleremo dopo. Come accennato in precedenza, con il rilascio di Java 8, alle interfacce è stata aggiunta la possibilità di avere metodi predefiniti . Se la classe che implementa l'interfaccia non sovrascrive questo metodo, verrà utilizzata questa implementazione predefinita (non è necessario sovrascrivere il metodo predefinito, come implementarne uno astratto). In questo caso, è possibile implementare diverse interfacce in una classe e utilizzare i relativi metodi predefiniti. Diamo un'occhiata a un esempio. Abbiamo un'interfaccia per volantini, con un metodo fly() predefinito :
public interface Flyer {
 default void fly() {
   System.out.println("Я лечу!!!");
 }
}
L'interfaccia del walker, con il metodo walk() predefinito :
public interface Walker {
 default void walk() {
   System.out.println("Я хожу!!!");
 }
}
L'interfaccia del nuotatore, con il metodo swim() :
public interface Swimmer {
 default void swim() {
   System.out.println("Я плыву!!!");
 }
}
Bene, ora implementiamo tutto questo in una classe duck:
public class Duck implements Flyer, Swimmer, Walker {
}
Ed eseguiamo tutti i metodi della nostra papera:
Duck donald = new Duck();
donald.walk();
donald.fly();
donald.swim();
Nella console riceveremo:
Io vado!!! Sto volando!!! Sto nuotando!!!
Ciò significa che abbiamo rappresentato correttamente l’ereditarietà multipla, anche se non è così. Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 14 - 8Noterò anche che se una classe implementa interfacce con metodi predefiniti che hanno gli stessi nomi di metodo e gli stessi argomenti in questi metodi, il compilatore inizierà a lamentarsi dell'incompatibilità, poiché non capisce quale metodo deve realmente essere utilizzato. Esistono diverse vie d'uscita:
  • Rinominare i metodi nelle interfacce in modo che differiscano l'uno dall'altro.
  • Sostituisci metodi così controversi nella classe di implementazione.
  • Eredita da una classe che implementa questi metodi controversi (quindi la tua classe utilizzerà esattamente la sua implementazione).

8. Qual è la differenza tra i metodi final,finalmente e finalize()?

final è una parola chiave utilizzata per inserire un vincolo su una classe, metodo o variabile, un vincolo che significa:
  • Per una variabile: dopo l'inizializzazione iniziale, la variabile non può essere ridefinita.
  • Per un metodo, il metodo non può essere sovrascritto in una sottoclasse (classe successore).
  • Per una classe: la classe non può essere ereditata.
infine è una parola chiave prima di un blocco di codice, utilizzata durante la gestione delle eccezioni, insieme a un blocco try e insieme (o in modo intercambiabile) con un blocco catch. Il codice in questo blocco viene eseguito in ogni caso, indipendentemente dal fatto che venga lanciata o meno un'eccezione. In questa parte dell'articolo, alla domanda 104, vengono discusse le situazioni eccezionali in cui questo blocco non verrà eseguito. finalize() è un metodo della classe Object , chiamato prima che ogni oggetto venga eliminato dal garbage collector, questo metodo verrà chiamato (last), e viene utilizzato per ripulire le risorse occupate. Per ulteriori informazioni sui metodi della classe Object che ogni oggetto eredita, vedere la domanda 11 in questa parte dell'articolo. Bene, è qui che finiremo oggi. Ci vediamo nella prossima parte! Analisi di domande e risposte da interviste per sviluppatori Java.  Parte 14 - 9
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION