1. Inicializar variables

Como ya sabe, puede declarar varias variables en su clase, y no solo declararlas, sino también inicializarlas inmediatamente con sus valores iniciales.

Y estas mismas variables también se pueden inicializar en un constructor. Esto significa que, en teoría, a estas variables se les podrían asignar valores dos veces. Ejemplo

Código Nota
class Cat
{
   public String name;
   public int age = -1;

   public Cat(String name, int age)
   {
     this.name = name;
     this.age = age;
   }

   public Cat()
   {
     this.name = "Nameless";
   }
}



A la agevariable se le asigna un valor inicial.




El valor inicial se sobrescribe.


La variable edad almacena su valor inicial.
 Cat cat = new Cat("Whiskers", 2);
Esto está permitido: se llamará al primer constructor
 Cat cat = new Cat();
Esto está permitido: se llamará al segundo constructor

Esto es lo que sucede cuando Cat cat = new Cat("Whiskers", 2);se ejecuta:

  • CatSe crea un objeto
  • Todas las variables de instancia se inicializan con sus valores iniciales
  • Se llama al constructor y se ejecuta su código.

En otras palabras, las variables primero obtienen sus valores iniciales, y solo entonces se ejecuta el código del constructor.


2. Orden de inicialización de variables en una clase

Las variables no se inicializan simplemente antes de que se ejecute el constructor, sino que se inicializan en un orden bien definido: el orden en que se declaran en la clase.

Veamos un código interesante:

Código Nota
public class Solution
{
   public int a = b + c + 1;
   public int b = a + c + 2;
   public int c = a + b + 3;
}

Este código no se compilará, ya que en el momento en que ase crea la variable no hay variables b y c todavía. Pero puede escribir su código de la siguiente manera: este código se compilará y funcionará correctamente.

Código Nota
public class Solution
{
   public int a;
   public int b = a + 2;
   public int c = a + b + 3;
}


0
0+2
0+2+3

Pero recuerda que tu código debe ser transparente para otros desarrolladores. Es mejor no usar técnicas como esta, ya que perjudica la legibilidad del código.

Aquí debemos recordar que antes de que a las variables se les asigne un valor, tienen un valor predeterminado . Para el inttipo, esto es cero.

Cuando la JVM inicialice la avariable, simplemente asignará el valor predeterminado para el tipo int: 0.

Cuando llegue a b, la variable a ya será conocida y tendrá un valor, por lo que la JVM le asignará el valor 2.

Y cuando llegue a la cvariable, las variables ay bya estarán inicializadas, por lo que la JVM calculará fácilmente el valor inicial para c: 0+2+3.

Si crea una variable dentro de un método, no puede usarla a menos que le haya asignado un valor previamente. ¡Pero esto no es cierto para las variables de una clase! Si no se asigna un valor inicial a una variable de una clase, entonces se le asigna un valor por defecto.


3. Constantes

Mientras analizamos cómo se crean los objetos, vale la pena mencionar la inicialización de constantes, es decir, variables con el finalmodificador.

Si una variable tiene el finalmodificador, entonces se le debe asignar un valor inicial. Ya lo sabes, y no tiene nada de sorprendente.

Pero lo que no sabe es que no tiene que asignar el valor inicial de inmediato si lo asigna en el constructor. Esto funcionará bien para una variable final. El único requisito es que, si tiene varios constructores, se debe asignar un valor a una variable final en cada constructor.

Ejemplo:

public class Cat
{
   public final int maxAge = 25;
   public final int maxWeight;

   public Cat (int weight)
   {
     this.maxWeight = weight; // Assign an initial value to the constant
   }
}


4. Código en un constructor

Y algunas notas más importantes sobre los constructores. Más tarde, a medida que continúe aprendiendo Java, se encontrará con cosas como la herencia, la serialización, las excepciones, etc. Todas influyen en el trabajo de los constructores en diversos grados. No tiene sentido profundizar en estos temas ahora, pero estamos obligados a al menos tocarlos.

Por ejemplo, aquí hay un comentario importante sobre los constructores. En teoría, puede escribir código de cualquier complejidad en un constructor. Pero no hagas esto. Ejemplo:

class FilePrinter
{
   public String content;

   public FilePrinter(String filename) throws Exception
   {
      FileInputStream input = new FileInputStream(filename);
      byte[] buffer = input.readAllBytes();
      this.content = new String(buffer);
   }

   public void printFile()
   {
      System.out.println(content);
   }
}






Abrir un flujo de lectura de archivos
Leer el archivo en una matriz de bytes
Guardar la matriz de bytes como una cadena




Mostrar el contenido del archivo en la pantalla

En el constructor de la clase FilePrinter, abrimos inmediatamente un flujo de bytes en un archivo y leímos su contenido. Este es un comportamiento complejo y puede dar lugar a errores.

¿Y si no hubiera tal archivo? ¿Qué pasa si hubo problemas con la lectura del archivo? ¿Y si fuera demasiado grande?

La lógica compleja implica una alta probabilidad de errores y eso significa que el código debe manejar las excepciones correctamente.

Ejemplo 1 — Serialización

En un programa Java estándar, hay muchas situaciones en las que no eres tú quien crea los objetos de tu clase. Por ejemplo, suponga que decide enviar un objeto a través de la red: en este caso, la propia máquina Java convertirá su objeto en un conjunto de bytes, lo enviará y volverá a crear el objeto a partir del conjunto de bytes.

Pero suponga que su archivo no existe en la otra computadora. Habrá un error en el constructor y nadie lo manejará. Y eso es bastante capaz de hacer que el programa termine.

Ejemplo 2 — Inicializar campos de una clase

Si su constructor de clase puede lanzar excepciones verificadas, es decir, está marcado con la palabra clave throws, entonces debe capturar las excepciones indicadas en el método que crea su objeto.

Pero, ¿y si no existe tal método? Ejemplo:

Código  Nota
class Solution
{
   public FilePrinter reader = new FilePrinter("c:\\readme.txt");
}
Este código no se compilará.

El FilePrinterconstructor de la clase puede generar una excepción comprobada , lo que significa que no puede crear un FilePrinterobjeto sin envolverlo en un bloque try-catch. Y un bloque try-catch solo se puede escribir en un método



5. Constructor de clase base

En lecciones anteriores, discutimos un poco la herencia. Desafortunadamente, nuestra discusión completa sobre la herencia y la programación orientada a objetos está reservada para el nivel dedicado a la programación orientada a objetos, y la herencia de los constructores ya es relevante para nosotros.

Si su clase hereda otra clase, un objeto de la clase principal se incrustará dentro de un objeto de su clase. Además, la clase principal tiene sus propias variables y sus propios constructores.

Eso significa que es muy importante que sepa y comprenda cómo se inicializan las variables y se llama a los constructores cuando su clase tiene una clase principal y hereda sus variables y métodos.

Clases

¿Cómo sabemos el orden en que se inicializan las variables y se llama a los constructores? Comencemos escribiendo el código para dos clases. Uno heredará al otro:

Código Nota
class ParentClass
{
   public String a;
   public String b;

   public ParentClass()
   {
   }
}

class ChildClass extends ParentClass
{
   public String c;
   public String d;

   public ChildClass()
   {
   }
}










La ChildClass clase hereda la ParentClassclase.

Necesitamos determinar el orden en que se inicializan las variables y se llama a los constructores. El registro nos ayudará a hacer esto.

Inicio sesión

El registro es el proceso de registrar acciones realizadas por un programa mientras se ejecuta, escribiéndolas en la consola o en un archivo.

Es bastante simple determinar que se ha llamado al constructor: en el cuerpo del constructor, escriba un mensaje en la consola. Pero, ¿cómo puede saber si una variable ha sido inicializada?

En realidad, esto tampoco es muy difícil: escriba un método especial que devuelva el valor utilizado para inicializar la variable y registre la inicialización. Así es como podría verse el código:

Código definitivo

public class Main
{
   public static void main(String[] args)
   {
      ChildClass obj = new ChildClass();
   }

   public static String print(String text)
   {
      System.out.println(text);
      return text;
   }
}

class ParentClass
{
   public String a = Main.print("ParentClass.a");
   public String b = Main.print("ParentClass.b");

   public ParentClass()
   {
      Main.print("ParentClass.constructor");
   }
}

class ChildClass extends ParentClass
{
   public String c = Main.print("ChildClass.c");
   public String d = Main.print("ChildClass.d");

   public ChildClass()
   {
      Main.print("ChildClass.constructor");
   }
}




Crear un ChildClassobjeto


Este método escribe el texto pasado a la consola y también lo devuelve.





Declare la ParentClassclase

Display text y también inicialice las variables con ella.




Escriba un mensaje de que se ha llamado al constructor. Ignora el valor de retorno.


Declare la ChildClassclase

Display text y también inicialice las variables con ella.




Escriba un mensaje de que se ha llamado al constructor. Ignora el valor de retorno.

Si ejecuta este código, el texto se mostrará en la pantalla de la siguiente manera:

Salida de consola del métodoMain.print()
ParentClass.a
ParentClass.b
ParentClass.constructor
ChildClass.c
ChildClass.d
ChildClass.constructor

Por lo tanto, siempre puede asegurarse personalmente de que las variables de una clase se inicialicen antes de llamar al constructor. Una clase base se inicializa completamente antes de la inicialización de la clase heredada.