CodeGym /Blog Java /Random-ES /Valores fijos en Java: final, constantes e inmutables
Autor
Aditi Nawghare
Software Engineer at Siemens

Valores fijos en Java: final, constantes e inmutables

Publicado en el grupo Random-ES
¡Hola! Ya estás familiarizado con la palabra "modificador". Como mínimo, ha encontrado modificadores de acceso (público, privado) y el modificador estático. Hoy hablaremos de un modificador especial llamado final . Se podría decir que el modificador final "consolida" partes de nuestro programa donde se necesitan comportamientos constantes, inequívocos e inmutables. Hay tres lugares en sus programas donde puede usarlo: clases, métodos y variables. Valores fijos en Java: final, constantes e inmutables - 2 Vamos a repasarlos en orden. Si el modificador final se usa en una declaración de clase, significa que la clase no se puede heredar. En lecciones anteriores, usamos un ejemplo de herencia simple: teníamos una Animalclase principal y dos clases secundarias: CatyDog

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
}
Sin embargo, si usamos el modificador final en la Animalclase, las clases Caty Dogno pueden heredarlo.

public final class Animal {

}

public class Cat extends Animal {

   // Error! Cannot inherit from final Animal
}
El compilador genera inmediatamente un error. En Java, muchas clases finales ya están implementadas. Entre los que usas con frecuencia, Stringes el más conocido. Además, si una clase se declara como final , todos los métodos de la clase también se vuelven finales . ¿Qué significa eso? Si un método se declara usando el modificador final , no puede anular ese método. Por ejemplo, aquí tenemos una Animalclase que declara un speak()método. Pero, los perros y los gatos definitivamente "hablan" de diferentes maneras. Entonces, declararemos los métodos speak() en las clases Caty Dog, pero los implementaremos de manera diferente.

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!");
   }
}
Hicimos que las clases Caty Doganularan el método declarado en la clase principal. Ahora, un animal hablará diferente, dependiendo de qué tipo de objeto sea:

public class Main {

   public static void main(String[] args) {

       Cat cat = new Cat();
       Dog dog = new Dog();
      
       cat.speak();
       dog.speak();
   }
}
Salida: ¡Miau! ¡Guau! Sin embargo, si declaramos el método Animalde la clase speak()como final, no podemos anularlo en otras clases:

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!");
   }
}
Y nuestros objetos se verán obligados a utilizar el speak()método definido en la clase principal:

public static void main(String[] args) {

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

   cat.speak();
   dog.speak();
}
Salida: ¡Hola! ¡Hola! Ahora, con respecto a las variables finales . También se conocen como constantes . Primero (y lo más importante), el valor inicial asignado a un valor constante no se puede cambiar. Se asigna de una vez por todas.

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!
   }
}
Una constante no necesita inicializarse inmediatamente. Eso se puede hacer más tarde. Pero, el valor que se le asignó inicialmente permanecerá igual para siempre.

public static void main(String[] args) {

   final int CONSTANT_EXAMPLE;

   CONSTANT_EXAMPLE = 999;// This is allowed
}
En segundo lugar, tenga en cuenta el nombre de nuestra variable. Java tiene una convención de nomenclatura diferente para las constantes. No es la notación camelCase habitual . Si hubiera sido una variable ordinaria, la habríamos llamado constanteEjemplo. Pero, los nombres de las constantes se escriben en mayúsculas, con guiones bajos entre las palabras (si hay más de una palabra), por ejemplo, "CONSTANTE_EJEMPLO". ¿Por qué necesitamos constantes? Son muy útiles si, por ejemplo, hay un valor fijo que usas regularmente en un programa. Por ejemplo, ha decidido hacer historia y escribir el juego "The Witcher 4" usted mismo. El juego obviamente usará regularmente el nombre del protagonista: "Geralt of Rivia". Esta cadena (y los nombres de otros héroes) se declara mejor como una constante: su valor se almacenará en un solo lugar, y definitivamente no cometerá un error tipográfico al ingresarlo un millón de veces.

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");
   }
}
Salida: The Witcher 4 Ya es el cuarto juego de Witcher, pero Geralt de Rivia todavía no puede decidir quién le gusta más: Yennefer de Wengerberg o Triss Merigold Pero, si nunca antes has jugado a The Witcher, comenzaremos desde el comienzo. El nombre del protagonista es Geralt de Rivia Geralt de Rivia es un brujo, un cazador de monstruos Hemos declarado los nombres de los héroes como constantes. Ahora definitivamente no cometeremos un error tipográfico, y no hay necesidad de escribirlos a mano cada vez. Otra ventaja: si alguna vez necesitamos cambiar el valor de la variable en todo el programa, puede hacerlo en un solo lugar, en lugar de modificarlo manualmente en todo el código base. :)

Tipos inmutables

Como ha trabajado con Java, probablemente ya se haya acostumbrado a la idea de que los programadores tienen un control casi total sobre el estado de todos los objetos. Si desea crear un Catobjeto, puede hacerlo. Si quieres cambiarle el nombre, puedes hacerlo. Si desea cambiar su edad o algo más, puede hacerlo. Pero Java tiene varios tipos de datos que tienen una propiedad especial. Son inmutables . Si una clase es inmutable, entonces el estado de sus objetos no se puede cambiar. ¿Quieres algunos ejemplos? Puede que te sorprenda, ¡pero la clase inmutable más conocida es String! Entonces, ¿realmente no podemos cambiar el valor de una cadena? Bueno, intentémoslo:

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
}
Salida: Me encanta Java Me encanta Java Después de que escribimos

str1 = "I love Python";
el "I love Java"objeto de cadena no cambió ni fue a ninguna parte. Todavía existe felizmente y tiene exactamente el mismo texto que antes. El código

str1 = "I love Python";
simplemente creó otro objeto, al que ahora apunta str1 . Pero parece que no podemos tener ningún efecto en el objeto de cadena "Me encanta Java". ¡Bien, intentemos otra cosa! ¡ La Stringclase está llena de métodos, y algunos de ellos parecen cambiar el estado del objeto! Por ejemplo, hay un replace()método. ¡Cambiemos la palabra "Java" a "Python" en nuestra cadena!

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);
}
Salida: Me encanta Java Me encanta Java ¡No funcionó de nuevo! ¿Quizás el método de reemplazo no funciona? Probemos otra cosa. Por ejemplo, substring(). Devuelve una subcadena basada en índices de caracteres pasados ​​como argumentos. Cortemos los primeros 10 caracteres de nuestra cadena:

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);
}
Salida: Me encanta Java Me encanta Java Valores fijos en Java: final, constantes e inmutables - 3 Nada ha cambiado. Y no debería haberlo hecho. Como dijimos antes, las cadenas son inmutables. Entonces, ¿qué pasa con todos los métodos de la Stringclase? Después de todo, pueden truncar cadenas, cambiar caracteres y más. ¿Cuál es el punto si no pasa nada? ¡Realmente pueden hacer estas cosas! Pero, devuelven una nueva cadena cada vez. No tiene sentido escribir

str1.replace("Java", "Python");
porque no puedes cambiar el objeto original. Pero, si escribe el resultado del método en una nueva variable de referencia, verá la diferencia inmediatamente.

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);
}
Todos Stringlos métodos funcionan de esta manera. No se puede hacer nada con el "I love Java"objeto. Simplemente puede crear un nuevo objeto y escribir: "<nuevo objeto> = el resultado de manipular el "I love Java" object ". ¿Qué otros tipos son inmutables? Algunos que definitivamente necesitará recordar de inmediato son todas las clases contenedoras para los tipos primitivos. Integer, Byte, Character, Short, Boolean, Long, Double, Float: todas estas clases crean immutableobjetos (hablaremos de ellos en las próximas lecciones). Esto incluye las clases que se usan para crear números grandes, como BigIntegery BigDecimal. Recientemente cubrimos las excepciones y tocamos el seguimiento de la pila . Bueno , adivina qué, java.lang.StackTraceElementlos objetos también son inmutables. Esto tiene sentido: si alguien pudiera cambiar los datos de nuestra pila, todo sería inútil. Imagine que alguien revisa el seguimiento de la pila y cambia un OutOfMemoryError por una FileNotFoundException . Y luego usa esa pila para encontrar la causa del error. Pero el programa ni siquiera usa archivos. :) Entonces, hicieron estos objetos inmutables, por si acaso. De acuerdo, tiene más o menos sentido para StackTraceElement . Pero, ¿por qué alguien necesitaría hacer que Strings sea inmutable? ¿Por qué sería un problema cambiar sus valores? Probablemente incluso sería más conveniente. :/ Hay varias razones para esto. Primero, ahorra memoria. Las cadenas inmutables se pueden colocar en el grupo de cadenas, lo que permite reutilizar cadenas en lugar de crear otras nuevas. En segundo lugar, por seguridad. Por ejemplo, los nombres de usuario y las contraseñas son cadenas en casi todos los programas. Permitir cambiarlos podría dar lugar a problemas de autorización. Hay otras razones, pero nuestro estudio de Java aún no las ha cubierto, por lo que volveremos a ellas más adelante.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION