Ciao! La parola “modificatore” ti è già familiare. Come minimo, ti sei imbattuto nei modificatori di accesso (pubblico, privato) e nel modificatore statico. Oggi parleremo dello speciale modificatore finale . Si può dire che “cementa” quelle aree del nostro programma in cui abbiamo bisogno di un comportamento costante, inequivocabile e immutabile. Può essere utilizzato in tre aree del nostro programma: classi, metodi e variabili. Esaminiamoli uno per uno. Se una dichiarazione di classe contiene il modificatore finale , significa che non puoi ereditare da questa classe. Nelle lezioni precedenti abbiamo visto un semplice esempio di ereditarietà: avevamo una classe genitore
Animal
e due classi figlie - Cat
eDog
public class Animal {
}
public class Cat extends Animal {
//..поля и методы класса Cat
}
public class Dog extends Animal {
//..поля и методы класса Dog
}
Tuttavia, se specifichiamo Animal
un modificatore per una classe, neanche final
le classi potranno ereditare da essa . Cat
Dog
public final class Animal {
}
public class Cat extends Animal {
//ошибка! Cannot inherit from final Animal
}
Il compilatore produce immediatamente un errore. Esistono molte classi già implementate in Java final
. Il più famoso di quelli che usi costantemente è String
. Inoltre, se una classe viene dichiarata come final
, anche tutti i suoi metodi diventano final
. Cosa significa? Se viene specificato un modificatore per un metodo final
, questo metodo non può essere sovrascritto. Ad esempio, abbiamo una classe Animal
che definisce un metodo voice()
. Tuttavia, cani e gatti chiaramente “parlano” in modo diverso. Pertanto, in ciascuna delle classi - Cat
e Dog
- creeremo un metodo voice()
, ma lo implementeremo in modo diverso.
public class Animal {
public void voice() {
System.out.println("Voice!");
}
}
public class Cat extends Animal {
@Override
public void voice() {
System.out.println("Meow!");
}
}
public class Dog extends Animal {
@Override
public void voice() {
System.out.println("Woof!");
}
}
Nelle classi Cat
e Dog
abbiamo sovrascritto il metodo della classe genitore. Ora l'animale vocalizzerà a seconda dell'oggetto della classe:
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
Dog dog = new Dog();
cat.voice();
dog.voice();
}
}
Conclusione: Miao! Trama! Tuttavia, se Animal
dichiariamo un metodo in una classe voice()
come final
, non sarà possibile ridefinirlo in altre classi:
public class Animal {
public final void voice() {
System.out.println("Voice!");
}
}
public class Cat extends Animal {
@Override
public void voice() {//ошибка! final-метод не может быть переопределен!
System.out.println("Meow!");
}
}
Quindi i nostri oggetti saranno costretti a utilizzare il metodo voice()
così come è definito nella classe genitore:
public static void main(String[] args) {
Cat cat = new Cat();
Dog dog = new Dog();
cat.voice();
dog.voice();
}
Conclusione: voce! Voce! Ora parliamo final
delle -variabili. Altrimenti si chiamano costanti . Innanzitutto (e soprattutto), il primo valore assegnato a una costante non può essere modificato. Viene assegnato una volta e per sempre.
public class Main {
private static final int CONSTANT_EXAMPLE = 333;
public static void main(String[] args) {
CONSTANT_EXAMPLE = 999;//ошибка! Нельзя присвоить новое meaning final-переменной!
}
}
Non è necessario inizializzare immediatamente la costante. Questo può essere fatto più tardi. Ma il valore assegnato per primo resterà per sempre.
public static void main(String[] args) {
final int CONSTANT_EXAMPLE;
CONSTANT_EXAMPLE = 999;//так делать можно
}
In secondo luogo, presta attenzione al nome della nostra variabile. Le costanti Java hanno una convenzione di denominazione diversa. Questo non è il CamelCase a cui siamo abituati. Nel caso di una variabile regolare, la chiameremmo costanteEsempio, ma i nomi delle costanti sono scritti in maiuscolo e tra le parole (se ce ne sono molte) c'è un carattere di sottolineatura - "CONSTANT_EXAMPLE". Perché sono necessarie le costanti? Ad esempio, torneranno utili se utilizzi costantemente un valore costante in un programma. Diciamo che hai deciso di passare alla storia e scrivere il gioco "The Witcher 4" da solo. Ovviamente il gioco utilizzerà costantemente il nome del personaggio principale: "Geralt di Rivia". È meglio separare questa riga e i nomi degli altri eroi in una costante: il valore di cui hai bisogno verrà archiviato in un unico posto e sicuramente non commetterai errori quando lo digiterai per la milionesima volta.
public class TheWitcher4 {
private static final String GERALT_NAME = "Геральт из Ривии";
private static final String YENNEFER_NAME = "Йеннифэр из Венгерберга";
private static final String TRISS_NAME = "Трисс Меригольд";
public static void main(String[] args) {
System.out.println("Ведьмак 4");
System.out.println("Это уже четвертая часть Ведьмака, а " + GERALT_NAME + " ниHow не определится кто ему" +
" нравится больше: " + YENNEFER_NAME + " or " + TRISS_NAME);
System.out.println("Но если вы никогда не играли в Ведьмака - начнем сначала.");
System.out.println("Главного героя зовут " + GERALT_NAME);
System.out.println(GERALT_NAME + " - ведьмак, охотник на чудовищ");
}
}
Conclusione:
Ведьмак 4
Это уже четвертая часть Ведьмака, а Геральт из Ривии ниHow не определится, кто ему нравится больше: Йеннифэр из Венгерберга or Трисс Меригольд.
Но если вы никогда не играли в Ведьмака — начнем сначала.
Главного героя зовут Геральт из Ривии
Геральт из Ривии — ведьмак, охотник на чудовищ
Abbiamo separato i nomi dei personaggi in costanti, e ora sicuramente non li scriveremo in modo errato, e non ci sarà bisogno di scriverli a mano ogni volta. Un altro vantaggio: se alla fine abbiamo bisogno di modificare il valore di una variabile in tutto il programma, è sufficiente farlo in un unico posto, invece di rifarlo manualmente in tutto il codice :)
Tipi immutabili
Mentre lavori in Java, probabilmente sei già abituato al fatto che il programmatore controlla quasi completamente lo stato di tutti gli oggetti. Wanted: creato un oggettoCat
. Se volevo, lo ribattezzavo. Se voleva, cambiava età, o qualcos'altro. Ma in Java esistono diversi tipi di dati che hanno uno stato speciale. Sono immutabili o immutabili . Ciò significa che se una classe è immutabile, lo stato dei suoi oggetti non può essere modificato. Esempi? Potresti rimanere sorpreso, ma l'esempio più famoso della classe Immutable è String
! Sembrerebbe, non possiamo cambiare il valore di una stringa? Proviamo:
public static void main(String[] args) {
String str1 = "I love Java";
String str2 = str1;//обе переменные-ссылки указывают на одну строку.
System.out.println(str2);
str1 = "I love Python";//но поведение str1 ниHow не влияет на str2
System.out.println(str2);//str2 продолжает указывать на строку "I love Java", хотя str1 уже указывает на другой an object
}
Conclusione: amo Java, amo Java Dopo che abbiamo scritto:
str1 = "I love Python";
l'oggetto con la stringa "I love Java" non è cambiato e non è andato da nessuna parte. Esiste in modo sicuro e contiene esattamente lo stesso testo di prima. Codice:
str1 = "I love Python";
ho appena creato un altro oggetto e ora la variabile str1
punta ad esso. Ma non possiamo influenzare in alcun modo l' oggetto “I love Java” . Ok, proviamolo diversamente! La classe String
è piena di metodi e alcuni di essi sembrano cambiare lo stato della riga! Ad esempio, esiste un metodo replace()
. Cambiamo la parola "Java" con la parola "Python" nella nostra riga!
public static void main(String[] args) {
String str1 = "I love Java";
String str2 = str1;//обе переменные-ссылки указывают на одну строку.
System.out.println(str2);
str1.replace("Java", "Python");//попробуем изменить состояние str1, заменив слово "Java" на “Python”
System.out.println(str2);
}
Conclusione: adoro Java, adoro Java Non ha funzionato di nuovo! Forse il metodo della curva non funziona? Proviamone un altro. Per esempio, substring()
. Taglia una stringa in base ai numeri dei caratteri trasmessi. Riduciamo il nostro ai primi 10 caratteri:
public static void main(String[] args) {
String str1 = "I love Java";
String str2 = str1;//обе переменные-ссылки указывают на одну строку.
System.out.println(str2);
str1.substring(10);//обрезаем исходную строку
System.out.println(str2);
}
Conclusione: amo Java, amo Java Non è cambiato nulla. E non avrebbe dovuto. Come abbiamo detto, gli oggetti String
sono immutabili. Cosa sono allora tutti questi metodi di classe String
? Possono tagliare la linea, cambiare i caratteri al suo interno, ecc. Perché allora sono necessari se non succede nulla? Loro possono! Ma restituiscono ogni volta un nuovo oggetto stringa. E' inutile scrivere:
str1.replace("Java", "Python");
- non modificherai 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;//обе переменные-ссылки указывают на одну строку.
System.out.println(str2);
String str1AfterReplacement = str1.replace("Java", "Python");
System.out.println(str2);
System.out.println(str1AfterReplacement);
}
Questo è l'unico modo in cui funzionano tutti questi metodi String
. Non puoi fare nulla con l' oggetto "I love Java" . Basta creare un nuovo oggetto e scrivere: “Nuovo oggetto = il risultato di alcune manipolazioni con l' oggetto “I love Java” .” Quali altri tipi sono immutabili? Da quello che devi assolutamente ricordare ora: tutte le classi wrapper sui tipi primitivi sono immutabili. Integer
, Byte
, Character
, Short
, Boolean
, Long
, Double
, Float
- tutte queste classi creano oggetti immutabili . Ciò include anche le classi utilizzate per creare numeri di grandi dimensioni - BigInteger
e BigDecimal
. Recentemente abbiamo esaminato le eccezioni e abbiamo toccato il tema StackTrace
. Quindi: anche gli oggetti della classe java.lang.StackTraceElement sono immutabili. Questo è logico: se qualcuno potesse modificare i dati nel nostro stack, ciò potrebbe annullare tutto il lavoro con esso. Immagina che qualcuno entri in StackTrace e modifichi OutOfMemoryError in FileNotFoundException . E dovresti lavorare con questo stack e cercare la causa dell'errore. E il programma non utilizza affatto i file :) Pertanto, per sicurezza, questi oggetti sono stati resi immutabili. Bene, con StackTraceElement è più o meno chiaro. Perché qualcuno dovrebbe voler rendere le stringhe immutabili? Quale sarebbe il problema se fosse possibile modificare i loro valori? Probabilmente sarebbe ancora più conveniente :/ Ci sono diverse ragioni per questo. Innanzitutto, risparmiare memoria. È possibile inserire stringhe immutabili String Pool
e utilizzare ogni volta le stesse invece di crearne di nuove. In secondo luogo, la sicurezza. Ad esempio, la maggior parte degli accessi e delle password in qualsiasi programma sono stringhe. La possibilità di modificarli potrebbe comportare problemi con l'autorizzazione. Ci sono altri motivi, ma non li abbiamo ancora affrontati imparando Java: torneremo più tardi.
GO TO FULL VERSION