1. Proprietà: getter e setter

Quando un grande progetto viene sviluppato da dozzine di programmatori contemporaneamente, spesso sorgono problemi se gestiscono i dati memorizzati nei campi di classe in modo diverso.

Forse le persone non studiano in dettaglio la documentazione della classe, o forse non descrive ogni caso. Di conseguenza, sono frequenti le situazioni in cui i dati interni di un oggetto possono essere "corrotti", rendendo l'oggetto non valido.

Per evitare queste situazioni, è consuetudine rendere privati ​​tutti i campi di classe in Java . Solo i metodi della classe possono modificare le variabili della classe. Nessun metodo di altre classi può accedere direttamente alle variabili.

Se vuoi che altre classi siano in grado di ottenere o modificare i dati all'interno degli oggetti della tua classe, devi aggiungere due metodi alla tua classe: un metodo get e un metodo set. Esempio:

Codice Nota
class Person
{
   private String name;

   public Person(String name)
   {
      this.name = name;
   }

   public String getName()
   {
      return name;
   }

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


privatename field



Inizializzazione del campo tramite il costruttore


getName()— Questo metodo restituisce il valore del name field




setName()— Questo metodo modifica il valore del name field

Nessun'altra classe può modificare direttamente il valore del campo name. Se qualcuno ha bisogno di ottenere il valore del campo name, dovrà chiamare il getName() metodo su un Personoggetto. Se un codice desidera modificare il valore del campo name, dovrà chiamare il setName() metodo su un Personoggetto.

Il getName()metodo è anche chiamato " getter per il campo del nome" e il setName()metodo è chiamato " setter per il campo del nome".

Questo è un approccio molto comune. Nell'80-90% di tutto il codice Java, non vedrai mai variabili pubbliche in una classe. Verranno invece dichiarate private(o protected) e ogni variabile avrà getter e setter pubblici.

Questo approccio rende il codice più lungo, ma più affidabile.

Accedere direttamente a una variabile di classe è come far girare la tua auto attraverso doppie linee gialle : è più facile e veloce, ma se lo fanno tutti, allora le cose peggiorano per tutti.

Supponiamo che tu voglia creare una classe che descriva un punto ( x, y). Ecco come lo farebbe un programmatore alle prime armi:

class Point
{
   public int x;
   public int y;
}

Ecco come lo farebbe un programmatore Java esperto:

Codice
class Point {
   private int x;
   private int y;

   public Point(int x, int y) {
      this.x = x;
      this.y = y;
   }

   public int getX() {
      return x;
   }

   public void setX(int x) {
      this.x = x;
   }

   public int getY() {
      return y;
   }

   public void setY(int y) {
      this.y = y;
   }
}

Il codice è più lungo? Senza dubbio.

Ma puoi aggiungere la convalida dei parametri a getter e setter. Ad esempio, puoi assicurarti che xe ysiano sempre maggiori di zero (o non minori di zero). Esempio:

Codice Nota
class Point {
   private int x;
   private int y;

   public Point(int x, int y) {
      this.x = x < 0 ? 0 : x;
      this.y = y < 0 ? 0 : y;
   }

   public int getX() {
      return x;
   }

   public void setX(int x) {
      this.x = x < 0 ?  0 : x;
   }

   public int getY() {
      return y;
   }

   public void setY(int y) {
      this.y = y < 0 ? 0 : y;
   }
}


2. Durata dell'oggetto

Sai già che gli oggetti vengono creati utilizzando l' newoperatore, ma come vengono eliminati gli oggetti? Non esistono per sempre. Non c'è abbastanza memoria per questo.

In molti linguaggi di programmazione, come C++, esiste un deleteoperatore speciale per l'eliminazione di oggetti. Ma come funziona in Java?

In Java, tutto è organizzato in modo leggermente diverso. Java non ha un operatore di cancellazione. Questo significa che gli oggetti non vengono eliminati in Java? No, vengono cancellati, ovviamente. Altrimenti, le applicazioni Java esaurirebbero rapidamente la memoria e non si parlerebbe di programmi in esecuzione senza interruzioni per mesi.

In Java, la cancellazione degli oggetti è completamente automatizzata. La macchina Java stessa gestisce la cancellazione degli oggetti. Questo processo è chiamato Garbage Collection e il meccanismo che raccoglie i rifiuti è chiamato Garbage Collector ( GC ).

Quindi, come fa la macchina Java a sapere quando eliminare un oggetto?

Il Garbage Collector divide tutti gli oggetti in "raggiungibili" e "irraggiungibili". Se c'è almeno un riferimento a un oggetto, è considerato raggiungibile. Se non esiste una variabile che fa riferimento a un oggetto, l'oggetto viene considerato irraggiungibile e viene dichiarato spazzatura, il che significa che può essere eliminato.

In Java, non puoi creare un riferimento a un oggetto esistente: puoi solo assegnare riferimenti che hai già. Se cancelliamo tutti i riferimenti a un oggetto, allora è perso per sempre.

Riferimenti circolari

Questa logica suona bene fino a quando non ci imbattiamo in un semplice controesempio: supponiamo di avere due oggetti che fanno riferimento l'uno all'altro (memorizzano i riferimenti l'uno all'altro). Nessun altro oggetto memorizza riferimenti a questi oggetti.

Non è possibile accedere a questi oggetti dal codice, ma sono comunque referenziati.

Questo è il motivo per cui il Garbage Collector divide gli oggetti in raggiungibili e irraggiungibili piuttosto che "referenziati" e "non referenziati".

Oggetti raggiungibili

Innanzitutto, gli oggetti attivi al 100% vengono aggiunti all'elenco raggiungibile. Ad esempio, il thread corrente ( Thread.current()) o la console InputStream ( System.in).

Quindi l'elenco degli oggetti raggiungibili si espande per includere gli oggetti a cui fa riferimento l'insieme iniziale di oggetti raggiungibili. Quindi viene nuovamente espanso per includere gli oggetti a cui fa riferimento questo insieme ingrandito e così via.

Ciò significa che se ci sono alcuni oggetti che si riferiscono solo l'uno all'altro, ma non c'è modo di raggiungerli da oggetti raggiungibili, allora quegli oggetti saranno considerati spazzatura e verranno eliminati.


3. Raccolta dei rifiuti

Frammentazione della memoria

Un altro punto importante relativo alla cancellazione degli oggetti è la frammentazione della memoria. Se crei ed elimini costantemente oggetti, presto la memoria sarà fortemente frammentata: aree di memoria occupata saranno intervallate da aree di memoria non occupata.

Di conseguenza, possiamo facilmente trovarci in una situazione in cui non possiamo creare un oggetto di grandi dimensioni (ad esempio, un array con un milione di elementi), perché non c'è una grossa fetta di memoria libera. In altre parole, potrebbe esserci memoria libera, anche molta, ma potrebbe non esserci un grande blocco contiguo di memoria libera

Ottimizzazione della memoria (deframmentazione)

La macchina Java risolve questo problema in un modo specifico. Assomiglia a questo:

La memoria è divisa in due parti. Tutti gli oggetti vengono creati (ed eliminati) in una sola metà della memoria. Quando arriva il momento di ripulire i buchi nella memoria, tutti gli oggetti nella prima metà vengono copiati nella seconda metà. Ma vengono copiati uno accanto all'altro in modo che non ci siano buchi.

Il processo è più o meno così:

Passaggio 1: dopo aver creato gli oggetti

Raccolta dei rifiuti in Java

Passaggio 2: aspetto dei "buchi"

Raccolta dei rifiuti in Java 2

Passaggio 3: eliminazione dei "buchi"

La raccolta dei rifiuti in Java 3

Ed è per questo che non è necessario eliminare oggetti. La macchina Java copia semplicemente tutti gli oggetti raggiungibili in una nuova posizione e libera l'intera area di memoria in cui erano archiviati gli oggetti.