CodeGym/Java Blog/Random-IT/Valori fissi in Java: final, constants e immutable
John Squirrels
Livello 41
San Francisco

Valori fissi in Java: final, constants e immutable

Pubblicato nel gruppo Random-IT
membri
CIAO! Conosci già la parola "modificatore". Come minimo, hai incontrato i modificatori di accesso (pubblico, privato) e il modificatore statico. Oggi parleremo di un modificatore speciale chiamato final . Si potrebbe dire che il modificatore finale "cementa" le parti del nostro programma in cui sono necessari comportamenti costanti, non ambigui e immutabili. Ci sono tre posti nei tuoi programmi in cui puoi usarlo: classi, metodi e variabili. Valori fissi in Java: final, constants e immutable - 2 Esaminiamoli in ordine. Se il modificatore final viene utilizzato in una dichiarazione di classe, significa che la classe non può essere ereditata. Nelle lezioni precedenti, abbiamo usato un semplice esempio di ereditarietà: avevamo una Animalclasse genitore e due classi figlie: CateDog
public class Animal {
}



public class Cat extends Animal {
   // Fields and methods of the Cat class
}


public class Dog extends Animal {

   // Fields and methods of the Dog class
}
Tuttavia, se usiamo il modificatore final sulla Animalclasse, le classi Cate Dognon possono ereditarlo.
public final class Animal {

}

public class Cat extends Animal {

   // Error! Cannot inherit from final Animal
}
Il compilatore genera immediatamente un errore. In Java, molte classi finali sono già implementate. Tra quelli che usi frequentemente, Stringè il più conosciuto. Inoltre, se una classe viene dichiarata final , anche tutti i metodi della classe diventano final . Che cosa significa? Se un metodo viene dichiarato utilizzando il final modificatore, non è possibile eseguire l'override di tale metodo. Ad esempio, qui abbiamo una Animalclasse che dichiara un speak()metodo. Ma cani e gatti sicuramente "parlano" in modi diversi. Quindi, dichiareremo i metodi speak() in entrambe le classi Cate Dog, ma li implementeremo in modo diverso.
public class Animal {

   public void speak() {
       System.out.println("Hello!");
   }
}

public class Cat extends Animal {

   @Override
   public void speak() {
       System.out.println("Meow!");
   }
}

public class Dog extends Animal {

   @Override
   public void speak() {
       System.out.println("Woof!");
   }
}
Abbiamo fatto in modo che le classi Cate Dogsovrascrivessero il metodo dichiarato nella classe genitore. Ora, un animale parlerà in modo diverso, a seconda del tipo di oggetto che è:
public class Main {

   public static void main(String[] args) {

       Cat cat = new Cat();
       Dog dog = new Dog();

       cat.speak();
       dog.speak();
   }
}
Uscita: Miao! Trama! Tuttavia, se dichiariamo il metodo Animaldella classe speak()come final, non possiamo sovrascriverlo in altre classi:
public class Animal {

   public final void speak() {
       System.out.println("Hello!");
   }
}


public class Cat extends Animal {

   @Override
   public void speak() {// Error! A final method can't be overridden!
       System.out.println("Meow!");
   }
}
E i nostri oggetti saranno costretti a utilizzare il speak()metodo come definito nella classe genitore:
public static void main(String[] args) {

   Cat cat = new Cat();
   Dog dog = new Dog();

   cat.speak();
   dog.speak();
}
Uscita: Ciao! Ciao! Ora, per quanto riguarda le variabili finali . Sono anche conosciute come costanti . Innanzitutto (e soprattutto), il valore iniziale assegnato a un valore costante non può essere modificato. Viene assegnato una volta per tutte.
public class Main {

   private static final int CONSTANT_EXAMPLE = 333;

   public static void main(String[] args) {

       CONSTANT_EXAMPLE = 999;// Error! You can't assign a new value to a final variable!
   }
}
Non è necessario inizializzare immediatamente una costante. Questo può essere fatto in seguito. Ma il valore inizialmente assegnato ad esso rimarrà lo stesso per sempre.
public static void main(String[] args) {

   final int CONSTANT_EXAMPLE;

   CONSTANT_EXAMPLE = 999;// This is allowed
}
In secondo luogo, annota il nome della nostra variabile. Java ha una diversa convenzione di denominazione per le costanti. Non è la solita notazione camelCase . Se fosse stata una variabile ordinaria, l'avremmo chiamata costanteEsempio. Ma i nomi delle costanti sono scritti in maiuscolo, con underscore tra le parole (se c'è più di una parola), ad esempio "CONSTANT_EXAMPLE". Perché abbiamo bisogno di costanti? Sono molto utili se, per esempio, c'è un valore fisso che usi regolarmente in un programma. Ad esempio, hai deciso di fare la storia e scrivere il gioco "The Witcher 4" da solo. Il gioco ovviamente utilizzerà regolarmente il nome del protagonista: "Geralt di Rivia". Questa stringa (e i nomi di altri eroi) è meglio dichiarata come una costante: il suo valore verrà memorizzato in un posto e sicuramente non farai un errore di battitura quando lo inserisci un milione di volte.
public class TheWitcher4 {

   private static final String GERALT_NAME = "Geralt of Rivia";
   private static final String YENNEFER_NAME = "Yennefer of Wengerberg";
   private static final String TRISS_NAME = "Triss Merigold";

   public static void main(String[] args) {

       System.out.println("The Witcher 4");
       System.out.println("It's already the fourth Witcher game, but " + GERALT_NAME + " still can't decide who" +
               " he likes more: " + YENNEFER_NAME + " or " + TRISS_NAME);

       System.out.println("But, if you've never played The Witcher before, we'll start from the beginning.");
       System.out.println("The protagonist's name is " + GERALT_NAME);
       System.out.println(GERALT_NAME + " is a witcher, a monster hunter");
   }
}
Uscita: The Witcher 4 È già il quarto gioco di Witcher, ma Geralt di Rivia non riesce ancora a decidere chi gli piace di più: Yennefer di Wengerberg o Triss Merigold Ma, se non hai mai giocato a The Witcher prima, inizieremo dal inizio. Il nome del protagonista è Geralt di Rivia Geralt di Rivia è un witcher, un cacciatore di mostri Abbiamo dichiarato i nomi degli eroi come costanti. Ora sicuramente non faremo errori di battitura e non è necessario scriverli a mano ogni volta. Un altro vantaggio: se mai avessimo bisogno di cambiare il valore della variabile nell'intero programma, puoi farlo in un posto, invece di modificarlo manualmente nell'intera base di codice. :)

Tipi immutabili

Dato che hai lavorato con Java, probabilmente ti sei già abituato all'idea che i programmatori abbiano il controllo quasi completo sullo stato di tutti gli oggetti. Se vuoi creare un Catoggetto, puoi farlo. Se vuoi rinominarlo, puoi farlo. Se vuoi cambiare la sua età o qualcos'altro, puoi farlo. Ma Java ha diversi tipi di dati che hanno una proprietà speciale. Sono immutabili . Se una classe è immutabile, lo stato dei suoi oggetti non può essere modificato. Vuoi qualche esempio? Potrebbe sorprenderti, ma la classe immutabile più conosciuta è String! Quindi, non possiamo davvero cambiare il valore di una stringa? Bene, proviamo:
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;// Both reference variables point to the same string.
   System.out.println(str2);

   str1 = "I love Python";// but changing str1 has no impact on str2
   System.out.println(str2);// str2 continues to point to the "I love Java" string, but str1 now points to a different object
}
Output: Amo Java Amo Java Dopo che abbiamo scritto
str1 = "I love Python";
l' "I love Java"oggetto stringa non è cambiato né è andato da nessuna parte. Esiste ancora felicemente e ha esattamente lo stesso testo di prima. Il codice
str1 = "I love Python";
ha semplicemente creato un altro oggetto, a cui ora punta str1 . Ma sembra che non possiamo avere alcun effetto sull'oggetto stringa "I love Java". Ok, proviamo qualcos'altro! La Stringclasse è piena di metodi e alcuni di essi sembrano modificare lo stato dell'oggetto! Ad esempio, c'è un replace()metodo. Cambiamo la parola "Java" in "Python" nella nostra stringa!
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;// Both reference variables point to the same string.
   System.out.println(str2);

   str1.replace("Java", "Python");// We try to change the state of str1 by swapping the word "Java" with "Python"
   System.out.println(str2);
}
Output: Amo Java Amo Java Non ha funzionato di nuovo! Forse il metodo di sostituzione non funziona? Proviamo qualcos'altro. Ad esempio, substring(). Restituisce una sottostringa basata su indici di caratteri passati come argomenti. Tagliamo i primi 10 caratteri della nostra stringa:
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;// Both reference variables point to the same string.
   System.out.println(str2);

   str1.substring(10);// Truncate the original String
   System.out.println(str2);
}
Output: Amo Java Amo Java Valori fissi in Java: final, constants e immutable - 3 Non è cambiato nulla. E non avrebbe dovuto. Come abbiamo detto prima, le stringhe sono immutabili. Allora, cosa sono tutti i metodi della Stringclasse? Dopotutto, possono troncare stringhe, cambiare caratteri e altro. Che senso ha se non succede niente? In realtà possono fare queste cose! Ma restituiscono ogni volta una nuova stringa. È inutile scrivere
str1.replace("Java", "Python");
perché non puoi cambiare l'oggetto originale. Ma se scrivi il risultato del metodo in una nuova variabile di riferimento, vedrai immediatamente la differenza!
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;// Both reference variables point to the same string.
   System.out.println(str2);

   String str1AfterReplacement =  str1.replace("Java", "Python");
   System.out.println(str2);

   System.out.println(str1AfterReplacement);
}
Tutti Stringi metodi funzionano in questo modo. Nulla può essere fatto all'oggetto "I love Java". Puoi semplicemente creare un nuovo oggetto e scrivere: "<nuovo oggetto> = il risultato della manipolazione di "I love Java" object ". Quali altri tipi sono immutabili? Alcuni che dovrai sicuramente ricordare subito sono tutte le classi wrapper per i tipi primitivi. Integer, Byte, Character, Short, Boolean, Long, Double, Float: tutte queste classi creano immutableoggetti (ne parleremo nelle prossime lezioni). Ciò include le classi utilizzate per creare numeri grandi, come BigIntegere BigDecimal. Di recente abbiamo trattato le eccezioni e toccato la traccia dello stack . Bene , indovina un po', java.lang.StackTraceElementanche gli oggetti sono immutabili. Questo ha senso: se qualcuno potesse cambiare i dati del nostro stack, renderebbe tutto inutile. Immagina che qualcuno esegua la traccia dello stack e modifichi un OutOfMemoryError in un FileNotFoundException . E poi usi quella pila per trovare la causa dell'errore. Ma il programma non usa nemmeno i file. :) Quindi, hanno reso questi oggetti immutabili, per ogni evenienza. Ok, quindi ha più o meno senso per StackTraceElement . Ma perché qualcuno dovrebbe rendere immutabili le stringhe? Perché cambiare i loro valori dovrebbe essere un problema? Probabilmente sarebbe anche più conveniente. :/ Ci sono diverse ragioni per questo. Innanzitutto, salva la memoria. Le stringhe non modificabili possono essere inserite nel pool di stringhe, consentendo il riutilizzo delle stringhe invece di crearne di nuove. In secondo luogo, per sicurezza. Ad esempio, nomi utente e password sono stringhe in quasi tutti i programmi. Rendere possibile la loro modifica potrebbe comportare problemi di autorizzazione. Ci sono altri motivi, ma il nostro studio di Java non li ha ancora coperti, quindi torneremo su di essi più tardi.
Commenti
  • Popolari
  • Nuovi
  • Vecchi
Devi avere effettuato l'accesso per lasciare un commento
Questa pagina non ha ancora commenti