Ciao ciao! Gli sviluppatori Java sono molto richiesti oggi. Naturalmente non posso offrirti un'opportunità di lavoro, ma cercherò di aiutarti ad acquisire nuove conoscenze e a colmare alcune lacune. Continuiamo quindi la nostra revisione delle domande del colloquio per sviluppatori Java. Puoi trovare i collegamenti alle parti precedenti della recensione alla fine dell'articolo. Esplorazione di domande e risposte da un colloquio di lavoro per una posizione di sviluppatore Java.  Parte 5 - 1

39. Quali sono i modificatori di accesso in Java? Nominali. A cosa servono?

In precedenza ho trattato i modificatori di accesso in una domanda sugli elementi di Java utilizzati per ottenere l'incapsulamento. Ma comunque te lo ricorderò. I modificatori di accesso in Java sono parole chiave che descrivono il livello di accesso concesso a un particolare componente Java. Esistono i seguenti modificatori di accesso:
  • public : un elemento contrassegnato con questo modificatore è pubblico. In altre parole, i campi, i metodi e le classi dichiarate con il modificatore public sono visibili ad altre classi sia nel proprio pacchetto che in pacchetti esterni;
  • protetto : un elemento contrassegnato con questo modificatore è accessibile da qualsiasi punto della sua stessa classe nel pacchetto corrente o nelle classi derivate, anche se si trovano in altri pacchetti;
  • default (o nessun modificatore) si applica implicitamente quando non è indicato alcun modificatore di accesso. È simile al precedente, tranne per il fatto che è visibile nelle classi derivate trovate in altri pacchetti;
  • privato : questo è il più privato di tutti i modificatori. Consente l'accesso a un elemento solo all'interno della classe corrente.

40. Nomina le caratteristiche principali dei metodi statici e non statici

La differenza principale è che i metodi statici appartengono a una classe. In effetti, non è necessario creare un'istanza di questa classe: i metodi statici possono essere chiamati solo dal tipo di classe. Ad esempio, supponiamo di avere un metodo statico per accarezzare un gatto:
public class CatService {
   public static void petTheCat(Cat cat) {
       System.out.println("Pet the cat: " + cat.getName());
   }
Non abbiamo bisogno di un'istanza della classe CatService per chiamarla:
Cat cat = new Cat(7, "Bobby");
CatService.petTheCat(cat);
Al contrario, i metodi ordinari sono legati (appartengono) a un oggetto. Per chiamarli, devi avere un'istanza (oggetto) su cui verrà chiamato il metodo. Ad esempio, supponiamo che il nostro gatto abbia un metodo non statico meow() :
class Cat {
   public void meow() {
       System.out.println("Meow! Meow! Meow!");
   }
Per chiamare questo metodo, abbiamo bisogno di un'istanza specifica di un gatto:
Cat cat = new Cat(7, "Bobby");
cat.meow();

41. Quali sono le principali restrizioni che si applicano ai metodi statici e non statici?

Come ho detto prima, il limite principale di un metodo ordinario (non statico) è che deve sempre esserci qualche istanza su cui viene chiamato il metodo. Ma un metodo statico non lo richiede. Inoltre, un metodo statico non può utilizzare questo riferimento agli elementi di un oggetto poiché ora esiste un oggetto corrente per il metodo.

42. Cosa significa la parola chiave statica? È possibile sovrascrivere o sovraccaricare un metodo statico?

Un elemento contrassegnato con la parola chiave static non appartiene ad un'istanza di una classe ma alla classe stessa. Viene caricato quando viene caricata la classe stessa. Gli elementi statici sono gli stessi per l'intero programma, mentre gli elementi non statici sono gli stessi solo per un oggetto specifico. I seguenti elementi possono essere statici:
  • campi di una classe;
  • blocco di inizializzazione di una classe;
  • un metodo di una classe;
  • classi nidificate di una classe (ovviamente anche questa è una tautologia).
Un metodo statico non può essere sovrascritto: appartiene alla classe e non è ereditato, ma allo stesso tempo può essere sovraccaricato.

43. Può un metodo essere statico e astratto allo stesso tempo?

A questo ho già risposto in un articolo precedente: un metodo non può essere astratto e statico allo stesso tempo. Se un metodo è astratto, ciò implica che deve essere sovrascritto in una classe figlia. Ma un metodo statico appartiene alla classe e non può essere sovrascritto. Ciò crea una contraddizione, di cui il compilatore noterà e di cui si arrabbierà. Se ti trovi in ​​questa situazione, dovresti pensare seriamente alla correttezza dell'architettura della tua applicazione (suggerimento: c'è chiaramente qualcosa che non va). Esplorazione di domande e risposte da un colloquio di lavoro per una posizione di sviluppatore Java.  Parte 5 - 2

44. È possibile utilizzare metodi statici in mezzo a metodi non statici? E viceversa? Perché?

Possiamo usare metodi statici in metodi non statici. Niente lo impedisce. Detto questo, non è possibile il contrario: un metodo statico non può utilizzare un metodo non statico senza fare riferimento a un'istanza specifica della classe. Ricorda, i membri statici di una classe non hanno accesso a un riferimento this : puoi avere quanti oggetti concreti della classe vuoi, e ognuno di essi conterrà un riferimento this , che è un autoreferenza. Quindi, come determinare quale riferimento utilizzare? Uh, non lo sai. Questo è il motivo per cui gli elementi statici non possono riferirsi a elementi non statici senza un riferimento a un oggetto specifico. Fondamentalmente, un metodo statico può utilizzare un metodo non statico solo se ha un riferimento a un oggetto specifico. Ad esempio, uno che è arrivato come argomento del metodo:
public static void petTheCat(Cat cat) {
   System.out.println("Pet the cat: " + cat.getName());
}
Qui vediamo che nel metodo statico petTheCat() chiama getName , un normale metodo non statico di un oggetto Cat .

45. Cos'è un'interfaccia? Può esserci un'interfaccia finale?

Ricorderemo che Java non ha ereditarietà multipla. Le interfacce sono una sorta di alternativa ad esso. Un'interfaccia è come una classe molto ridotta. Definiscono la funzionalità ma non un'implementazione concreta. Questo compito è lasciato alle classi che implementano queste interfacce. Esempio di interfaccia:
public interface Animal {
    void speak();
}
Esempio di implementazione dell'interfaccia da parte di una classe
class Cat implements Animal {

   @Override
   public void speak() {
       System.out.println("Meow! Meow! Meow!");
   }
}
Ecco le cose più importanti da sapere sull'utilizzo delle interfacce:
  1. I metodi di interfaccia devono contenere solo un'intestazione. Non devono avere un corpo del metodo specifico, cioè devono essere astratti (anche se non utilizzano la parola chiave abstract ). Esistono eccezioni: metodi statici e metodi predefiniti, che richiedono un corpo del metodo.

  2. Una classe può implementare molte interfacce (come ho detto, le interfacce sono un'alternativa all'ereditarietà multipla). I nomi delle interfacce sono separati da virgole nell'intestazione del metodo: la classe Lion implementa Animal, Wild .

  3. Le interfacce vengono create utilizzando la parola chiave Interface .

  4. Quando una classe implementa un'interfaccia, utilizziamo la parola chiave implements .

  5. Una classe che implementa una determinata interfaccia deve implementare tutti i suoi metodi astratti oppure deve dichiararsi astratta.

  6. Lo scopo principale dell'utilizzo delle interfacce è implementare il polimorfismo (dare a un oggetto la capacità di assumere molte forme).

  7. Di norma, i modificatori di accesso per i metodi non sono indicati nelle interfacce: sono public per impostazione predefinita e non è possibile specificare modificatori diversi da public . A partire da Java 9, puoi utilizzare modificatori privati ​​sui metodi.

  8. Per impostazione predefinita, le variabili dell'interfaccia sono statiche final . In altre parole, sono costanti: devono sempre essere inizializzate direttamente nell'interfaccia.

  9. Non è possibile creare un'istanza di un'interfaccia.

La risposta alla domanda se le interfacce possano essere definitive è, ovviamente, no. In effetti, lo scopo principale delle interfacce è che siano implementate. E come tutti ricordiamo molto bene, il modificatore finale a livello di classe rende una classe non ereditabile e, nel caso di un'interfaccia, non implementabile. Perché avremmo bisogno di un'interfaccia che non possiamo implementare e utilizzare? Hai ragione, non lo faremmo! E il compilatore è d'accordo. :) Esplorazione di domande e risposte da un colloquio di lavoro per una posizione di sviluppatore Java.  Parte 5 - 3In realtà, con l'introduzione dei metodi di interfaccia statica a partire da Java 8, potrebbe esserci un punto, ma ciò non cambia il fatto che un'interfaccia non può essere definitiva. Ho parlato delle interfacce solo in modo molto superficiale, poiché si tratta di un argomento vasto. Per ulteriori informazioni, vedere gli articoli sulle interfacce in Java e sulla differenza tra classi astratte e interfacce .

46. ​​Dove possono essere inizializzati i campi statici?

I campi statici possono essere inizializzati:
  • immediatamente dopo la dichiarazione, utilizzando un segno di uguale ( = );
  • in un blocco di inizializzazione statico;
  • in un blocco di inizializzazione non statico (ma è necessario comprendere che ogni volta che viene creato un oggetto, il campo statico verrà sovrascritto quando viene eseguito questo blocco di inizializzazione;
  • in un costruttore di classi. Ogni volta che viene chiamato il costruttore (ovvero ogni volta che viene creato un oggetto utilizzando questo costruttore), il campo verrà sovrascritto;
  • nei metodi statici;
  • in metodi non statici;
  • in classi nidificate statiche e non statiche, locali e anonime.

47. Cosa sono le classi anonime?

Le classi anonime sono classi che non hanno un proprio tipo. Di cosa sto parlando? Quando abbiamo parlato di interfacce, ho detto che non puoi creare un'istanza di un oggetto: puoi solo creare un'istanza di una classe che implementa un'interfaccia. Cosa succede se non vuoi che una classe implementi un'interfaccia ma hai bisogno di un oggetto che implementi l'interfaccia? E probabilmente questo sarà l'unico utilizzo dell'oggetto. E non è necessario creare una classe di implementazione completa. Come lo faresti? Giusto! Utilizzando una classe anonima! Esplorazione di domande e risposte da un colloquio di lavoro per una posizione di sviluppatore Java.  Parte 5 - 4Supponiamo di avere un'interfaccia Animal :
public final interface Animal {
   public void speak();
}
Se vogliamo utilizzare una classe anonima per istanziare una determinata interfaccia:
Animal cat = new Animal() {
   @Override
   public void speak() {
       System.out.println("Meow! Meow! Meow!");
   }
};
E poi, puoi tranquillamente usare questo oggetto e il suo metodo speak() implementato . In altre parole, la classe anonima implementa l'interfaccia e tutti i suoi metodi astratti, proprio qui e ora. Altrimenti, non saremmo in grado di creare un oggetto interfaccia/classe astratta poiché ci sarebbero metodi non implementati/astratti. Come ho già detto, le classi anonime vengono utilizzate non solo per implementare i metodi astratti di un'interfaccia ma anche per implementare i metodi astratti di una classe astratta. Questo approccio è utile per le situazioni in cui un oggetto viene utilizzato una volta o quando l'implementazione di un determinato metodo è necessaria solo una volta. Non è necessario creare una classe separata che implementerà la classe/interfaccia astratta richiesta. Ma noto anche che le classi anonime vengono utilizzate raramente nel lavoro. Di norma, viene ancora data la preferenza alle classi ordinarie. Puoi leggere ulteriori informazioni sulle classi anonime qui in questo articolo .

48. Cosa sono le classi primitive?

Penso che questa sia una domanda fuorviante, forse una domanda trabocchetto, dal momento che Java non ha classi primitive. Esiste solo il concetto di tipi primitivi, che abbiamo considerato in precedenza. Ricordiamo che Java ha 8 tipi primitivi: byte , short , int , long , float , double , char , boolean .

49. Cos'è una classe wrapper?

Il problema principale con l'utilizzo dei tipi primitivi in ​​Java è che non sono classi e Java è un linguaggio OOP. Cioè, i programmi scritti in questo linguaggio equivalgono a interazioni tra oggetti. Ma i primitivi non sono oggetti. Non hanno metodi, nemmeno i metodi standard della classe Object . Ma cosa succede se dobbiamo usare una primitiva come chiave in una Map ? Quindi dobbiamo chiamare il suo metodo hashCode() . Puoi anche chiamare il suo metodo equals() lì. Cosa poi? Ci sono una gran quantità di momenti in cui hai bisogno di una classe, non di un primitivo. Ciò rende le primitive elementi inutilizzabili e indesiderabili in un programma perché violano l'idea stessa di OOP. Ma la situazione non è così grave come sembra. Dopotutto, Java ha il concetto di wrapper primitivi. In Java, ogni tipo primitivo ha un gemello: una classe wrapper.
  • byte -> Byte.class
  • breve -> Classe.breve
  • int -> Classe.intera
  • lungo -> Classe.lunga
  • float -> Float.class
  • doppio -> Doppio.class
  • char -> Classe.carattere
  • booleano -> Booleano.class
Questi tipi rappresentano i tipi semplici, ma in classi complete con una serie di metodi vari e utili. I concetti di autoboxing e unboxing sono stati introdotti per consentire un utilizzo conveniente di queste classi. L'autoboxing è la conversione automatica di un tipo primitivo nella sua classe analoga, se necessario (ad esempio, convertendo int in Integer ). L'unboxing è il processo opposto: conversione automatica di una classe wrapper primitiva in un tipo primitivo (ad esempio, conversione di Integer in int ). Grazie all'introduzione delle classi wrapper primitive e dei processi di autoboxing e unboxing , i tipi primitivi sono ora membri a pieno titolo di Java come linguaggio OOP. Esplorazione di domande e risposte da un colloquio di lavoro per una posizione di sviluppatore Java.  Parte 5 - 5Per una trattazione più approfondita di questo argomento consiglio vivamente la lettura di questo articolo .

50. Cos'è una classe nidificata? Dove viene utilizzato?

Una classe nidificata è una classe che è essa stessa membro di un'altra classe. Esistono 4 tipi di classi annidate in Java: 1. Classe interna Questo tipo di classe viene dichiarata direttamente nel corpo di un'altra classe. Una classe interna è una classe nidificata non statica e può accedere a qualsiasi campo privato o metodo di istanza della classe esterna. Ad esempio, creiamo uno zoo che contenga un animale, una zebra:
public class Zoo {
   class Zebra {
       public void eat(String food) {
           System.out.println("Zebra eats " + food);
       }
   }
}
Non complicato, vero? Diamo un'occhiata a un esempio di creazione di un'istanza della classe interna:
Zoo.Zebra zebra = new Zoo().new Zebra();
zebra.eat("apple");
Come hai già visto è necessario prima creare un oggetto della classe che lo racchiude. Quindi usi il riferimento all'oggetto per creare un'istanza della classe interna. Vorrei anche sottolineare che una classe interna (classe nidificata non statica) non può avere metodi statici o campi statici. Ciò avviene proprio perché la classe interna è implicitamente associata a un'istanza della sua classe esterna e quindi non può dichiarare alcun metodo statico al suo interno. 2. Classi nidificate statiche Queste classi sono simili alla categoria precedente, ma hanno il modificatore di accesso statico nella dichiarazione della classe. Poiché questo tipo di classe non ha accesso ai campi non statici della classe esterna, sembra più una parte statica della classe esterna che una classe interna. Ma questa classe ha accesso a tutti i membri statici della classe esterna, anche a quelli privati. Esempio di una classe nidificata statica:
public class Zoo {
   static class Zebra {
       public void eat(String food) {
           System.out.println("Zebra eats " + food);
       }
   }
}
Viene creato in modo leggermente diverso dal precedente:
Zoo.Zebra zebra = new Zoo.Zebra();
zebra.eat("apple");
Qui non abbiamo bisogno di un oggetto della classe esterna per creare un oggetto della classe nidificata statica. Dobbiamo solo conoscere il nome della classe nidificata per trovarla all'interno della classe esterna. 3. Classi locali Le classi locali sono classi dichiarate all'interno del corpo di un metodo. Gli oggetti di una classe locale possono essere creati e utilizzati solo all'interno del metodo di inclusione. Esempio:
public class Zoo {
   public void feed(String animal, String food) {
       switch(animal) {
           case "zebra":
               class Zebra {
                   public void eat(String food) {
                       System.out.println("Zebra eats " + food);
                   }
               }
               Zebra zebra = new Zebra();
               zebra.eat(food);
               ...
Ecco un esempio:
Zoo zoo = new Zoo();
zoo.feed("zebra", "apple");
Se non vedessi il codice per il metodo feed() , non sospetteresti nemmeno che esista una classe locale, vero? Una classe locale non può essere statica o transitoria , ma può essere contrassegnata come astratta o finale (l'una OPPURE l'altra, ma non entrambe, poiché l'utilizzo simultaneo di questi due modificatori crea un conflitto). 4. Classi anonime Abbiamo già parlato delle classi anonime sopra e, come ricorderete, possono essere create da due fonti: interfacce e classi. Motivi per usarle Vengono utilizzate classi nidificate statiche e non statiche perché a volte è meglio incorporare classi piccole in classi più generali e tenerle insieme in modo che abbiano una maggiore coesione e uno scopo comune. Fondamentalmente, le classi nidificate ti consentono di aumentare l'incapsulamento del tuo codice. Potresti scegliere di utilizzare una classe locale se la classe viene utilizzata esclusivamente all'interno di un singolo metodo. In questo caso è necessario diffondere il codice nell'applicazione? No. Detto questo, aggiungo che nella mia esperienza non ho mai visto nessuno usare le lezioni locali, perché se siano necessarie o meno è molto controverso. Potresti utilizzare classi anonime quando un'implementazione specifica di un'interfaccia o di una classe astratta è necessaria solo una volta. In tal caso, non è necessario creare una classe separata e completa con un'implementazione. Invece, abbiamo mantenuto le cose semplici e implementato i metodi di cui avevamo bisogno utilizzando una classe anonima, utilizzato l'oggetto e poi ce ne siamo dimenticati (ovviamente, il garbage collector non se ne è dimenticato). La tua comprensione delle classi nidificate sarà migliorata dall'articolo qui.

51. Quali modificatori di accesso può avere una classe?

Esistono diversi tipi di classi e ad esse si applicano diversi modificatori di accesso:
  • una classe esterna può avere il modificatore di accesso pubblico o nessun modificatore (il modificatore predefinito);
  • una classe interna (classe annidata non statica) può avere uno qualsiasi dei 4 modificatori di accesso;
  • una classe statica annidata può avere uno qualsiasi dei modificatori di accesso tranne protected perché questo modificatore implica ereditarietà, che contraddice qualsiasi membro statico della classe (i membri statici non vengono ereditati);
  • una classe locale può avere solo il modificatore predefinito (ovvero nessun modificatore);
  • una classe anonima non ha alcuna dichiarazione di classe, quindi non ha alcun modificatore di accesso.
Qui è dove finiremo per oggi. Arrivederci!Esplorazione di domande e risposte da un colloquio di lavoro per una posizione di sviluppatore Java.  Parte 5 - 6