CodeGym /Java Blog /Random-IT /Esplorazione di domande e risposte da un colloquio di lav...
John Squirrels
Livello 41
San Francisco

Esplorazione di domande e risposte da un colloquio di lavoro per una posizione di sviluppatore Java. Parte 7

Pubblicato nel gruppo Random-IT
Ciao a tutti! La programmazione è piena di insidie. E non c'è quasi un singolo argomento che non ti faccia inciampare e inciampare. Ciò è particolarmente vero per i principianti. L'unico modo per salvare le dita dei piedi è imparare. In particolare, è necessario approfondire gli argomenti più fondamentali. Oggi continueremo la revisione delle domande più frequenti durante le interviste per gli sviluppatori Java. Queste domande dell’intervista fanno un ottimo lavoro nel coprire argomenti di base. Tieni presente che l'elenco include anche alcune domande non così standard che ti consentono di affrontare i problemi comuni in modo diverso. Esplorazione di domande e risposte da un colloquio di lavoro per una posizione di sviluppatore Java.  Parte 7 - 1

62. Cos'è lo string pool e perché è necessario?

Parte della memoria messa a disposizione di un programma Java è chiamata heap (di cui parleremo più avanti), e parte dell'heap è chiamata String pool . Serve per memorizzare valori di stringa. In altre parole, quando crei una stringa, ad esempio, utilizzando virgolette doppie come questa:
String str = "Hello world";
La JVM controlla se il pool di stringhe ha già il valore specificato. In tal caso, alla variabile str viene assegnato un riferimento a quel valore nel pool. In caso contrario, viene creato un nuovo valore nel pool e un riferimento ad esso viene assegnato alla variabile str . Consideriamo un esempio:
String firstStr = "Hello world";
String secondStr = "Hello world";
System.out.println(firstStr == secondStr);
true verrà visualizzato sullo schermo. Ricorda che == confronta i riferimenti e queste due variabili puntano allo stesso valore nel pool di stringhe. Ciò aiuta a evitare di produrre molti oggetti String identici in memoria. Possiamo farlo perché, come ricorderete, String è una classe immutabile, quindi non c'è niente di sbagliato nell'avere più riferimenti allo stesso valore. Ora è impossibile avere una situazione in cui la modifica del valore in un punto comporta modifiche a diversi altri riferimenti. Tuttavia, se creiamo una stringa utilizzando new :
String str = new String("Hello world");
quindi viene creato un oggetto separato in memoria e memorizza il valore di stringa specificato (non importa se tale valore è già nel pool di stringhe). Per confermare questa affermazione, considera questo:
String firstStr = new String("Hello world");
String secondStr = "Hello world";
String thirdStr = new String("Hello world");
System.out.println(firstStr == secondStr);
System.out.println(firstStr == thirdStr);
Otterremo due righe che indicano false , il che significa che abbiamo tre stringhe separate. Fondamentalmente, questo è il motivo per cui dovresti creare stringhe semplicemente usando virgolette doppie. Detto questo, è possibile aggiungere (o ottenere un riferimento a) valori nel pool di stringhe anche quando si crea un oggetto utilizzando la parola chiave new . Per fare ciò, utilizziamo il metodo intern() della classe String. Questo metodo garantisce la creazione del valore nel pool di stringhe o l'ottenimento di un riferimento ad esso se è già presente. Ecco un esempio:
String firstStr = new String("Hello world").intern();
String secondStr = "Hello world";
String thirdStr = new String("Hello world").intern();
System.out.println(firstStr == secondStr);
System.out.println(firstStr == thirdStr);
System.out.println(secondStr == thirdStr);
Questo codice viene restituito tre volte alla console, il che ci dice che tutte e tre le variabili si riferiscono alla stessa stringa in memoria.

63. Quali modelli di progettazione GoF vengono utilizzati nello string pool?

Nello string pool, il modello di progettazione GoF è un modello mosca . Se hai notato un altro modello di progettazione qui, condividilo nei commenti. Qui parleremo del modello di progettazione del peso mosca. È un modello di progettazione strutturale in cui un oggetto che si rappresenta come un'istanza unica in diversi punti del programma in realtà non è unico. Il peso mosca consente di risparmiare memoria memorizzando lo stato condiviso degli oggetti invece di memorizzare dati identici in ciascun oggetto. Per comprendere il concetto, consideriamo questo esempio elementare. Supponiamo di avere un'interfaccia Dipendente :
public interface Employee {
   void work();
}
E ha alcune implementazioni, come una classe di avvocato :
public class Lawyer implements Employee {

   public Lawyer() {
       System.out.println("A lawyer was hired.");
   }

   @Override
   public void work() {
       System.out.println("Settling legal issues...");
   }
}
E un corso di contabilità :
public class Accountant implements Employee {

   public Accountant() {
       System.out.println("An accountant was hired.");
   }

   @Override
   public void work() {
       System.out.println("Keeping accounting records...");
   }
}
I metodi sono del tutto arbitrari: per questo esempio dobbiamo solo vedere che vengono eseguiti. Lo stesso vale per il costruttore. L'output della console ci dice quando vengono creati nuovi oggetti. Abbiamo anche un dipartimento delle risorse umane il cui compito è restituire il dipendente richiesto. Se quel dipendente non fa già parte dello staff, viene assunto e il dipartimento Risorse umane restituisce il nuovo dipendente:
public class HumanResourcesDepartment {
   private Map<String, Employee> currentEmployees = new HashMap<>();

   public Employee getEmployee(String type) throws Exception {
       Employee result;
       if (currentEmployees.containsKey(type)) {
           result = currentEmployees.get(type);
       } else {
           switch (type) {
               case "Accountant":
                   result = new Accountant();
                   currentEmployees.put(type, result);
                   break;
               case "Lawyer":
                   result = new Lawyer();
                   currentEmployees.put(type, result);
                   break;
               default:
                   throw new Exception("This employee is not on the staff!");
           }
       }
       return result;
   }
}
Quindi la logica è semplice: se l'oggetto desiderato esiste, restituitelo; in caso contrario, crealo, mettilo in archivio (qualcosa come una cache) e restituiscilo. Ora vediamo come funziona il tutto:
public static void main(String[] args) throws Exception {
   HumanResourcesDepartment humanResourcesDepartment = new HumanResourcesDepartment();
   Employee empl1 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl2 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl3 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl4 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl5 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl6 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl7 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl8 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl9 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl10 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
}
Ecco cosa vedremo nella console:
È stato assunto un avvocato. Risoluzione delle questioni legali... È stato assunto un contabile. Tenuta della contabilità... Gestione delle questioni legali... Gestione della contabilità... Gestione delle questioni legali... Gestione della contabilità... Gestione delle questioni legali... Gestione della contabilità... Gestione delle questioni legali... Gestione della contabilità registra…
Come puoi vedere, abbiamo creato solo due oggetti e li abbiamo riutilizzati molte volte. L'esempio è molto semplice, ma dimostra come questo modello di progettazione possa preservare le nostre risorse. Come avrete notato, la logica di questo modello è dolorosamente simile alla logica di un pool assicurativo. Esplorazione di domande e risposte da un colloquio di lavoro per una posizione di sviluppatore Java.  Parte 7 - 2

64. Come dividiamo una stringa in parti? Fornisci un esempio del codice pertinente

Ovviamente, questa domanda riguarda il metodo di divisione . La classe String ha due varianti di questo metodo:
String split(String regex);
E
String split(String regex);
Il parametro regex è il delimitatore: un'espressione regolare utilizzata per dividere la stringa in un array di stringhe, ad esempio:
String str = "Hello, world it's Amigo!";
String[] arr = str.split("\\s");
for (String s : arr) {
  System.out.println(s);
}
La console visualizzerà:
Ciao mondo, sono Amigo!
Quindi, la nostra stringa è stata divisa in un array di stringhe, utilizzando uno spazio come delimitatore (invece dell'espressione regolare "\\s" , avremmo potuto anche usare la normale espressione di stringa " " ). La seconda variante, sovraccaricata, ha un parametro limite aggiuntivo . limit è la dimensione massima consentita dell'array risultante. In altre parole, una volta che la stringa è stata divisa nel numero massimo consentito di sottostringhe, la suddivisione si interrompe e l'ultimo elemento conterrà gli eventuali "avanzi" della stringa eventualmente non divisa. Esempio:
String str = "Hello, world it's Amigo!";
String[] arr = str.split(" ", 2);
for (String s : arr) {
  System.out.println(s);
}
Uscita console:
Ciao mondo, sono Amigo!
Come possiamo vedere, se non fosse per limit = 2 , l'ultimo elemento dell'array potrebbe essere suddiviso in tre sottostringhe.

65. Perché un array di caratteri è migliore di una stringa per memorizzare una password?

Esistono diversi motivi per preferire un array a una stringa quando si memorizza una password:

1. Il pool di stringhe e l'immutabilità delle stringhe.

Quando si utilizza un array ( char[] ), possiamo cancellare esplicitamente i dati una volta terminato di lavorarci. Possiamo anche sovrascrivere l'array quanto vogliamo, eliminando la password dal sistema ancor prima della garbage collection (è sufficiente modificare un paio di celle con valori non validi). Al contrario, String è una classe immutabile. Ciò significa che se vogliamo modificare il valore di un oggetto String , ne otterremo uno nuovo, ma quello vecchio rimarrà nello string pool. Se vogliamo rimuovere la stringa contenente la password, dovremo affrontare un compito complicato poiché abbiamo bisogno che il garbage collector rimuova quel valore dallo string pool, ma quella stringa probabilmente rimarrà lì per molto tempo. Cioè, quando si tratta di archiviare in modo sicuro i dati, String è inferiore a un array di caratteri .

2. Se restituiamo il valore String alla console (o a un registro), otteniamo:

String password = "password";
System.out.println("Password - " + password);
Uscita console:
Parola d'ordine - parola d'ordine
E se ti capita di stampare l'array sulla console:
char[] arr = new char[]{'p','a','s','s','w','o','r','d'};
System.out.println("Password - " + arr);
la console mostrerà parole senza senso incomprensibili:
Password: [C@7f31245a
In realtà non è una sciocchezza. Ecco come dare un senso a ciò che vedi: [C è il nome della classe - array di caratteri , @ è un delimitatore e quindi 7f31245a è un codice hash esadecimale.

3. La Guida di riferimento ufficiale Java Cryptography Architecture (JCA) menziona esplicitamente la memorizzazione delle password in un char[] anziché in una String :

"Sembrerebbe logico raccogliere e archiviare la password in un oggetto di tipo java.lang.String . Tuttavia, ecco l'avvertenza: gli oggetti di tipo String sono immutabili, ovvero non sono definiti metodi che consentano di modificare (sovrascrivere) o azzerare il contenuto di una stringa dopo l'uso. Questa funzionalità rende gli oggetti String inadatti alla memorizzazione di informazioni sensibili alla sicurezza come le password degli utenti. Dovresti invece sempre raccogliere e archiviare le informazioni sensibili alla sicurezza in un array di caratteri." Esplorazione di domande e risposte da un colloquio di lavoro per una posizione di sviluppatore Java.  Parte 7 - 3

Enum

66. Fornisci una breve descrizione di Enum in Java

Enum è l'abbreviazione di enumerazione, che è un insieme di costanti di stringa unite da un tipo comune. Ne dichiariamo uno utilizzando la parola chiave enum . Ecco un esempio con enum : ruoli consentiti in alcuni campus scolastici:
public enum Role {
   STUDENT,
   TEACHER,
   DIRECTOR,
   SECURITY_GUARD
}
Le parole scritte in maiuscolo sono le costanti di enumerazione. Vengono dichiarati in modo semplificato, senza il nuovo operatore. L'uso delle enumerazioni rende la vita molto più semplice poiché aiuta a evitare errori e confusione nei nomi (poiché l'elenco definisce gli unici valori validi). Per me sono molto convenienti nella costruzione dell'interruttore .

67. Un Enum può implementare interfacce (usare la parola chiave implements)?

SÌ. Dopotutto, le enumerazioni dovrebbero rappresentare qualcosa di più che semplici insiemi passivi (come i ruoli in un campus scolastico). In Java, possono rappresentare oggetti più complessi, quindi potrebbe essere necessario aggiungere loro funzionalità aggiuntive. Ciò consente inoltre di sfruttare il polimorfismo sostituendo il valore enum nei punti in cui è necessario il tipo di interfaccia implementato.

68. Enum può estendere una classe (utilizzare la parola chiave extends)?

No, non può, perché un'enumerazione è una sottoclasse della classe predefinita Enum<T> , dove T è il tipo enum. Questa non è altro che una classe base comune per tutti i tipi enum nel linguaggio Java. La conversione da un'enumerazione a una classe viene eseguita dal compilatore Java in fase di compilazione. L'estensione non è esplicitamente indicata nel codice, ma è sempre implicita.

69. È possibile creare un Enum senza istanze di oggetti?

Questa domanda è un po' confusa e non sono sicuro di averla compresa appieno. Ho due interpretazioni: 1. Puoi avere un enum senza valori? Sì, certo, ma sarebbe come una classe vuota, inutile, ad es
public enum Role {
}
E se chiamiamo:
var s = Role.values();
System.out.println(s);
Otteniamo quanto segue nella console:
[Lflyweight.Ruolo;@9f70c54
(un array vuoto di valori Role ) 2. È possibile creare un'enumerazione senza l' operatore new ? Sì, naturalmente. Come ho detto sopra, non usi l' operatore new per i valori enum, poiché sono valori statici.

70. Possiamo sovrascrivere il metodo toString() di Enum?

Sì, ovviamente puoi sovrascrivere il metodo toString() per definire come visualizzare la tua enumerazione quando viene chiamato il metodo toString (quando converti un'enumerazione in una stringa ordinaria, ad esempio, per inviarla alla console o ai log).
public enum Role {
   STUDENT,
   TEACHER,
   DIRECTOR,
   SECURITY_GUARD;

   @Override
   public String toString() {
       return "Selected role - " + super.toString();
   }
}
Questo è tutto per me oggi. Fino alla prossima parte!
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION