CodeGym /Blog Java /Aleatoriu /Valori fixe în Java: finale, constante și imuabile
John Squirrels
Nivel
San Francisco

Valori fixe în Java: finale, constante și imuabile

Publicat în grup
Bună! Sunteți deja familiarizat cu cuvântul „modificator”. Cel puțin, ați întâlnit modificatori de acces (public, privat) și modificatorul static. Astăzi vom discuta despre un modificator special numit final . Ați putea spune că modificatorul final „cimentează” părți ale programului nostru în care sunt necesare comportamente constante, neechivoce și neschimbate. Există trei locuri în programele dvs. pe care le puteți utiliza: clase, metode și variabile. Valori fixe în Java: finale, constante și imuabile - 2 Să le parcurgem în ordine. Dacă modificatorul final este folosit într-o declarație de clasă, înseamnă că clasa nu poate fi moștenită. În lecțiile anterioare, am folosit un exemplu simplu de moștenire: am avut o Animalclasă părinte și două clase copii: CatșiDog

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
}
Totuși, dacă folosim modificatorul final al Animalclasei, clasele Catși Dognu îl pot moșteni.

public final class Animal {

}

public class Cat extends Animal {

   // Error! Cannot inherit from final Animal
}
Compilatorul generează imediat o eroare. În Java, multe clase finale sunt deja implementate. Dintre cele pe care le folosești frecvent, Stringeste cea mai cunoscută. În plus, dacă o clasă este declarată finală , toate metodele clasei devin și ele finale . Ce înseamnă asta? Dacă o metodă este declarată utilizând modificatorul final , nu puteți suprascrie acea metodă. De exemplu, aici avem o Animalclasă care declară o speak()metodă. Dar, cu siguranță, câinii și pisicile „vorbesc” în moduri diferite. Deci, vom declara metodele speak() atât în ​​clase, Catcât și în Dogclase, dar le vom implementa diferit.

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!");
   }
}
Am făcut ca clasele Catși Dogsă înlocuiască metoda declarată în clasa părinte. Acum, un animal va vorbi diferit, în funcție de ce tip de obiect este:

public class Main {

   public static void main(String[] args) {

       Cat cat = new Cat();
       Dog dog = new Dog();
      
       cat.speak();
       dog.speak();
   }
}
Ieșire: Miau! Bătătură! Cu toate acestea, dacă declarăm metoda Animalclasei speak()ca finală, atunci nu o putem suprascrie în alte clase:

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!");
   }
}
Și obiectele noastre vor fi forțate să folosească speak()metoda așa cum este definită în clasa părinte:

public static void main(String[] args) {

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

   cat.speak();
   dog.speak();
}
Ieșire: Bună! Buna ziua! Acum, referitor la variabilele finale . Ele sunt cunoscute și ca constante . În primul rând (și cel mai important), valoarea inițială atribuită unei valori constante nu poate fi modificată. Este atribuit o dată pentru totdeauna.

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!
   }
}
O constantă nu trebuie inițializată imediat. Asta se poate face mai târziu. Dar, valoarea atribuită inițial acestuia va rămâne aceeași pentru totdeauna.

public static void main(String[] args) {

   final int CONSTANT_EXAMPLE;

   CONSTANT_EXAMPLE = 999;// This is allowed
}
În al doilea rând, notați numele variabilei noastre. Java are o convenție de denumire diferită pentru constante. Nu este notația obișnuită camelCase . Dacă ar fi fost o variabilă obișnuită, am fi numit-o constantExample. Dar, numele constantelor sunt scrise cu majuscule, cu liniuțe de subliniere între cuvinte (dacă există mai mult de un cuvânt), de exemplu „CONSTANT_EXAMPLE”. De ce avem nevoie de constante? Sunt foarte utile dacă, de exemplu, există o valoare fixă ​​pe care o utilizați în mod regulat într-un program. De exemplu, ai decis să faci istorie și să scrii singur jocul „The Witcher 4”. Jocul va folosi, evident, în mod regulat numele protagonistului: „Geralt of Rivia”. Acest șir (și numele altor eroi) este cel mai bine declarat ca o constantă: valoarea sa va fi stocată într-un singur loc și cu siguranță nu veți face o greșeală de tipar când îl introduceți de un milion de ori.

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");
   }
}
Ieșire: The Witcher 4 Este deja al patrulea joc Witcher, dar Geralt din Rivia încă nu poate decide pe cine îi place mai mult: Yennefer din Wengerberg sau Triss Merigold Dar, dacă nu ai mai jucat The Witcher până acum, vom începe de la început. Numele protagonistului este Geralt din Rivia Geralt din Rivia este un vrăjitor, un vânător de monștri. Am declarat numele eroilor drept constante. Acum cu siguranță nu vom face o greșeală de tipar și nu este nevoie să le scriem de mână de fiecare dată. Un alt plus: dacă trebuie vreodată să schimbăm valoarea variabilei în întregul program, o puteți face într-un singur loc, în loc să o modificați manual în întreaga bază de cod. :)

Tipuri imuabile

Pe măsură ce ați lucrat cu Java, probabil că v-ați obișnuit deja cu ideea că programatorii au control aproape complet asupra stării tuturor obiectelor. Dacă doriți să creați un Catobiect, puteți. Dacă vrei să-l redenumiți, puteți. Dacă vrei să-i schimbi vârsta sau altceva, poți. Dar Java are mai multe tipuri de date care au o proprietate specială. Sunt imuabile . Dacă o clasă este imuabilă, atunci starea obiectelor sale nu poate fi schimbată. Vrei niște exemple? S-ar putea să vă surprindă, dar cea mai cunoscută clasă imuabilă este String! Deci, chiar nu putem schimba valoarea unui șir? Ei bine, hai să încercăm:

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
}
Ieșire: Iubesc Java I love Java După ce am scris

str1 = "I love Python";
obiectul "I love Java"șir nu s-a schimbat și nu s-a dus nicăieri. Încă există din fericire și are exact același text ca înainte. Codul

str1 = "I love Python";
pur și simplu a creat un alt obiect, spre care str1 indică acum. Dar, se pare că nu putem avea niciun efect asupra obiectului șir „Iubesc Java”. Bine, hai să încercăm altceva! Clasa Stringeste plină de metode, iar unele dintre ele par să schimbe starea obiectului! De exemplu, există o replace()metodă. Să schimbăm cuvântul „Java” în „Python” din șirul nostru!

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);
}
Ieșire: Iubesc Java Iubesc Java Nu a funcționat din nou! Poate metoda de înlocuire nu funcționează? Să încercăm altceva. De exemplu, substring(). Returnează un subșir bazat pe indici de caractere trecuți ca argumente. Să tăiem primele 10 caractere ale șirului nostru:

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);
}
Ieșire: Iubesc Java Iubesc Java Valori fixe în Java: finale, constante și imuabile - 3 Nu s-a schimbat nimic. Și n-ar fi trebuit. După cum am spus mai devreme, șirurile sunt imuabile. Deci, ce e cu toate metodele din Stringclasă? La urma urmei, pot trunchia șiruri, pot schimba caractere și multe altele. Ce rost are dacă nu se întâmplă nimic? Ei chiar pot face aceste lucruri! Dar, ei returnează un String nou de fiecare dată. Nu are rost să scrii

str1.replace("Java", "Python");
pentru că nu poți schimba obiectul original. Dar, dacă scrieți rezultatul metodei într-o nouă variabilă de referință, veți vedea imediat diferența!

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);
}
Toate Stringmetodele funcționează astfel. Nu se poate face nimic obiectului "I love Java". Puteți doar să creați un obiect nou și să scrieți: „<obiect nou> = rezultatul manipulării "I love Java" object ". Ce alte tipuri sunt imuabile? Unele de care va trebui să vă amintiți imediat sunt toate clasele de wrapper pentru tipurile primitive. Integer, Byte, Character, Short, Boolean, Long, Double, Float: toate aceste clase creează immutableobiecte (vom vorbi despre ele în lecțiile următoare). Aceasta include clasele folosite pentru a crea numere mari, cum ar fi BigIntegerși BigDecimal. Am tratat recent excepții și am atins următorul stivei . Ei bine , ghici ce, java.lang.StackTraceElementobiectele sunt de asemenea imuabile. Acest lucru are sens: dacă cineva ar putea schimba datele stivei noastre, totul ar fi inutil. Imaginați-vă că cineva trece prin urmărirea stivei și schimbă un OutOfMemoryError într-o FileNotFoundException . Și apoi utilizați acea stivă pentru a găsi cauza erorii. Dar programul nici măcar nu folosește fișiere. :) Deci, au făcut aceste obiecte imuabile, pentru orice eventualitate. Bine, deci are mai mult sau mai puțin sens pentru StackTraceElement . Dar, de ce ar trebui cineva să facă Strings imuabile? De ce ar fi o problemă schimbarea valorilor lor? Probabil ar fi chiar mai convenabil. :/ Există mai multe motive pentru aceasta. În primul rând, economisește memorie. Șirurile imuabile pot fi plasate în pool-ul de șiruri, permițând reutilizarea șirurilor în loc de a crea altele noi. În al doilea rând, pentru securitate. De exemplu, numele de utilizator și parolele sunt șiruri în aproape fiecare program. A face posibilă modificarea acestora ar putea duce la probleme de autorizare. Există și alte motive, dar studiul nostru despre Java nu le-a acoperit încă, așa că vom reveni la ele mai târziu.
Comentarii
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION