JavaRush /Blog Java /Random-ES /Constructores en Java

Constructores en Java

Publicado en el grupo Random-ES
¡Hola! Hoy veremos un tema muy importante que concierne a nuestros objetos. ¡Aquí, sin exagerar, podemos decir que utilizará este conocimiento todos los días en el trabajo real! Hablaremos de constructores. Es posible que estés escuchando este término por primera vez, pero en realidad probablemente hayas usado constructores, pero no te diste cuenta :) Veremos esto más adelante.

¿Qué es un constructor en Java y por qué es necesario?

Veamos dos ejemplos.
public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

       Car bugatti = new Car();
       bugatti.model = "Bugatti Veyron";
       bugatti.maxSpeed = 407;

   }
}
Creamos nuestro coche y establecimos su modelo y velocidad máxima. Sin embargo, en un proyecto real, el objeto Car claramente tendrá más de 2 campos. ¡Y, por ejemplo, 16 campos!
public class Car {

   String model;//modelo
   int maxSpeed;//máxima velocidad
   int wheels;// ancho del disco
   double engineVolume;//capacidad del motor
   String color;//color
   int yearOfIssue;//año de emisión
   String ownerFirstName;//Nombre del dueño
   String ownerLastName;//apellido del propietario
   long price;//precio
   boolean isNew;//nuevo o no
   int placesInTheSalon;//cantidad de asientos en la cabina
   String salonMaterial;// material interior
   boolean insurance;// esta asegurado
   String manufacturerCountry;//país fabricante
   int trunkVolume;// volumen del maletero
   int accelerationTo100km;//aceleración a 100 km/h en segundos


   public static void main(String[] args) {
       Car bugatti = new Car();

       bugatti.color = "blue";
       bugatti.accelerationTo100km = 3;
       bugatti.engineVolume = 6.3;
       bugatti.manufacturerCountry = "Italy";
       bugatti.ownerFirstName = "Amigo";
       bugatti.yearOfIssue = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.placesInTheSalon = 2;
       bugatti.maxSpeed = 407;
       bugatti.model = "Bugatti Veyron";

   }

}
Hemos creado un nuevo objeto Car . Un problema: tenemos 16 campos, ¡pero solo inicializamos 12 ! ¡Pruebe ahora usar el código para encontrar aquellos que olvidamos! No es tan fácil, ¿verdad? En tal situación, el programador puede fácilmente cometer un error y omitir la inicialización de algún campo. Como resultado, el comportamiento del programa será erróneo:
public class Car {

   String model;//modelo
   int maxSpeed;//máxima velocidad
   int wheels;// ancho del disco
   double engineVolume;//capacidad del motor
   String color;//color
   int yearOfIssue;//año de emisión
   String ownerFirstName;//Nombre del dueño
   String ownerLastName;//apellido del propietario
   long price;//precio
   boolean isNew;//nuevo o no
   int placesInTheSalon;//cantidad de asientos en la cabina
   String salonMaterial;// material interior
   boolean insurance;// esta asegurado
   String manufacturerCountry;//país fabricante
   int trunkVolume;// volumen del maletero
   int accelerationTo100km;//aceleración a 100 km/h en segundos


   public static void main(String[] args) {
       Car bugatti = new Car();

       bugatti.color = "blue";
       bugatti.accelerationTo100km = 3;
       bugatti.engineVolume = 6.3;
       bugatti.manufacturerCountry = "Italy";
       bugatti.ownerFirstName = "Amigo";
       bugatti.yearOfIssue = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.placesInTheSalon = 2;
       bugatti.maxSpeed = 407;
       bugatti.model = "Bugatti Veyron";

       System.out.println("Modelo Bugatti Veyron. Tamaño del motor - " + bugatti.engineVolume + ", trompa - " + bugatti.trunkVolume + ", el salón está hecho de" + bugatti.salonMaterial +
       ", ancho del disco -" + bugatti.wheels + ". Fue adquirida en 2018 por el Sr. " + bugatti.ownerLastName);

   }

}
Salida de consola:
Modelo Bugatti Veyron. Cilindrada del motor: 6,3, maletero: 0, interior hecho de nulo, ancho de llanta: 0. Fue comprado en 2018 por el Sr. nulo.
¡A su comprador, que pagó dos millones de dólares por un automóvil, obviamente no le gustará que lo llamen “Señor Nulo”! Pero en serio, al final, nuestro programa terminó con un objeto creado incorrectamente: un automóvil con un ancho de llanta 0 (es decir, sin llantas), un baúl faltante, un interior hecho de material desconocido e incluso perteneciente a alguien desconocido. . ¡Sólo podemos imaginar cómo podría ocurrir tal error mientras el programa se está ejecutando! Necesitamos evitar de alguna manera tales situaciones. Necesitamos que nuestro programa tenga una limitación: al crear un nuevo objeto vehículo, por ejemplo, siempre se debe especificar el modelo y la velocidad máxima. De lo contrario, no permita la creación de objetos. Las funciones constructoras hacen frente fácilmente a esta tarea. Obtuvieron su nombre por una razón. El constructor crea una especie de “esqueleto” de la clase, al que debe corresponder cada nuevo objeto de la clase. Por conveniencia, volvamos a una versión más simple de la clase Car con dos campos. Dados nuestros requisitos, el constructor de la clase Car tendrá este aspecto:
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
Y crear un objeto ahora se ve así:
public static void main(String[] args) {
   Car bugatti = new Car("Bugatti Veyron", 407);
}
Prestar atencióncómo se crea el constructor. Es similar a un método normal, pero no tiene tipo de retorno. En este caso, el nombre de la clase se indica en el constructor, también con mayúscula. En nuestro caso - Coche . Además, el constructor utiliza la palabra clave nueva para usted this . "esto" en inglés significa "esto, esto". Esta palabra se refiere a un objeto específico. Código en el constructor:
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
se puede traducir casi literalmente: " modelo para esta máquina (que estamos creando ahora) = el argumento del modelo , que se especifica en el constructor. maxSpeed ​​​​para esta máquina (que estamos creando) = el argumento maxSpeed , que se especifica en el constructor." Esto es lo que pasó:
public class Car {

   String model;
   int maxSpeed;

   public Car(String model, int maxSpeed) {
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   public static void main(String[] args) {
       Car bugatti = new Car("Bugatti Veyron", 407);
       System.out.println(bugatti.model);
       System.out.println(bugatti.maxSpeed);
   }

}
Salida de consola:
Bugatti Veyron 407
El constructor asignó exitosamente los valores requeridos. ¡Te habrás dado cuenta de que un constructor es muy similar a un método normal! Así es: un constructor es un método, sólo que un poco específico :) Al igual que en un método, le pasamos parámetros a nuestro constructor. Y al igual que llamar a un método, llamar a un constructor no funcionará si no los especificas:
public class Car {

   String model;
   int maxSpeed;

   public Car(String model, int maxSpeed) {
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   public static void main(String[] args) {
       Car bugatti = new Car(); //¡error!
   }

}
Verá, el diseñador hizo lo que intentábamos lograr. ¡Ahora no puedes crear un coche sin velocidad o sin modelo! Las similitudes entre constructores y métodos no terminan ahí. Al igual que los métodos, los constructores pueden sobrecargarse. Imagina que tienes 2 gatos en casa. Te llevaste a uno de ellos cuando era gatito y al segundo te lo trajiste de la calle a casa cuando era adulto y no sabes exactamente cuántos años tiene. Esto significa que nuestro programa debería poder crear gatos de dos tipos: con un nombre y una edad para el primer gato, y sólo con un nombre, para el segundo gato. Para ello, sobrecargaremos el constructor:
public class Cat {

   String name;
   int age;

   //para el primer gato
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   //para el segundo gato
   public Cat(String name) {
       this.name = name;
   }

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 5);
       Cat streetCatNamedBob = new Cat("Bob");
   }

}
Al constructor original con los parámetros “nombre” y “edad” le agregamos otro, solo con un nombre. Sobrecargamos métodos de la misma manera en lecciones anteriores. Ahora podemos crear con éxito ambas versiones de gatos :) ¿ ¿Por qué se necesitan constructores?  - 2Recuerdas que al comienzo de la conferencia dijimos que ya habías usado constructores, pero simplemente no te diste cuenta? Esto es cierto. El hecho es que cada clase en Java tiene un constructor predeterminado. No tiene ningún argumento, pero se activa cada vez que se crea un objeto de cualquier clase.
public class Cat {

   public static void main(String[] args) {

       Cat barsik = new Cat(); //aquí es donde trabajaba el constructor por defecto
   }
}
A primera vista esto no se nota. Bueno, creamos un objeto y lo creamos, ¿dónde está el trabajo del diseñador? Para ver esto, escribamos un constructor vacío para la clase Cat con nuestras propias manos , y dentro de él imprimamos alguna frase en la consola. Si se muestra, entonces el constructor ha funcionado.
public class Cat {

   public Cat() {
       System.out.println("¡Creó un gato!");
   }

   public static void main(String[] args) {

       Cat barsik = new Cat(); //aquí es donde trabajaba el constructor por defecto
   }
}
Salida de consola:
¡Crearon un gato!
¡Aquí está la confirmación! El constructor predeterminado siempre está presente de forma invisible en tus clases. Pero necesitas conocer una característica más. El constructor predeterminado desaparece de la clase cuando creas algún constructor con argumentos. La prueba de esto, de hecho, ya la hemos visto anteriormente. Aquí en este código:
public class Cat {

   String name;
   int age;

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

   public static void main(String[] args) {

       Cat barsik = new Cat(); //¡error!
   }
}
No pudimos crear un gato sin nombre y edad porque definimos un constructor para Cat : cadena + número. El constructor predeterminado desapareció de la clase inmediatamente después de esto. Por lo tanto, asegúrese de recordar: si necesita varios constructores en su clase, incluido uno vacío, debe crearlo por separado. Por ejemplo, estamos creando un programa para una clínica veterinaria. Nuestra clínica quiere hacer buenas obras y ayudar a los gatos callejeros, de los que desconocemos su nombre y su edad. Entonces nuestro código debería verse así:
public class Cat {

   String name;
   int age;

   //para gatos domésticos
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   //para gatos callejeros
   public Cat() {
   }

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 5);
       Cat streetCat = new Cat();
   }
}
Ahora que hemos escrito explícitamente un constructor predeterminado, podemos crear gatos de ambos tipos :) Para un constructor (como para cualquier método), el orden de los argumentos es muy importante. Intercambiemos los argumentos de nombre y edad en nuestro constructor.
public class Cat {

   String name;
   int age;

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

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 10); //¡error!
   }
}
¡Error! El constructor establece claramente que cuando se crea un objeto Cat, se le debe pasar un número y una cadena, en ese orden. Por eso nuestro código no funciona. Asegúrese de recordar esto y tenerlo en cuenta al crear sus propias clases:
public Cat(String name, int age) {
   this.name = name;
   this.age = age;
}

public Cat(int age, String name) {
   this.age = age;
   this.name = name;
}
¡Estos son dos diseñadores completamente diferentes! Si expresamos la respuesta a la pregunta "¿Por qué necesitamos un constructor?" en una frase, podemos decir: para que los objetos estén siempre en el estado correcto. Cuando uses constructores, todas tus variables se inicializarán correctamente y no habrá autos con velocidad 0 u otros objetos "incorrectos" en el programa. Su uso resulta muy beneficioso, ante todo, para el propio programador. Si inicializa los campos usted mismo, existe un alto riesgo de perder algo y cometer un error. Pero esto no sucederá con un constructor: si no le pasó todos los argumentos requeridos o confundió sus tipos, el compilador arrojará inmediatamente un error. Vale la pena mencionar por separado que no debes poner la lógica de tu programa dentro del constructor. Para ello, tienes a tu disposición métodos en los que puedes describir todas las funcionalidades que necesitas. Veamos por qué la lógica del constructor es una mala idea:
public class CarFactory {

   String name;
   int age;
   int carsCount;

   public CarFactory(String name, int age, int carsCount) {
   this.name = name;
   this.age = age;
   this.carsCount = carsCount;

   System.out.println("Nuestra fábrica de automóviles se llama" + this.name);
   System.out.println("Ella fue fundada" + this.age + " hace años que" );
   System.out.println("Durante este tiempo se produjo" + this.carsCount +  "carros");
   System.out.println("En promedio ella produce" + (this.carsCount/this.age) + "coches por año");
}

   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Ford", 115 , 50000000);
   }
}
Tenemos una clase CarFactory que describe una fábrica para producir automóviles. Dentro del constructor, inicializamos todos los campos y colocamos la lógica aquí: mostramos información sobre la fábrica en la consola. Parecería que esto no tiene nada de malo, el programa funcionó perfectamente. Salida de consola:
Nuestra fábrica de automóviles se llama Ford, fue fundada hace 115 años y durante este tiempo ha producido 50.000.000 de automóviles, en promedio 434.782 automóviles por año.
Pero, en realidad, hemos colocado una bomba de tiempo. Y ese código puede provocar errores muy fácilmente. Imaginemos que ahora no estamos hablando de Ford, sino de la nueva fábrica "Amigo Motors", que existe desde hace menos de un año y ha producido 1000 coches:
public class CarFactory {

   String name;
   int age;
   int carsCount;

   public CarFactory(String name, int age, int carsCount) {
   this.name = name;
   this.age = age;
   this.carsCount = carsCount;

   System.out.println("Nuestra fábrica de automóviles se llama" + this.name);
   System.out.println("Ella fue fundada" + this.age + " hace años que" );
   System.out.println("Durante este tiempo se produjo" + this.carsCount +  "carros");
   System.out.println("En promedio ella produce" + (this.carsCount/this.age) + "coches por año");
}


   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Amigo Motors", 0 , 1000);
   }
}
Salida de consola:
Nuestra fábrica de automóviles se llama Amigo Motors Excepción en el hilo "principal" java.lang.ArithmeticException: / por cero Fue fundada hace 0 años Durante este tiempo, produjo 1000 automóviles en CarFactory.<init>(CarFactory.java:15) en CarFactory.main(CarFactory.java:23) Proceso finalizado con código de salida 1</init>
¡Hemos llegado! El programa terminó con algún error extraño. ¿Intentarás adivinar cuál es el motivo? La razón es la lógica que colocamos en el constructor. En concreto, en esta línea:
System.out.println("En promedio ella produce" + (this.carsCount/this.age) + "coches por año");
Aquí hacemos el cálculo y dividimos el número de coches producidos por la antigüedad de la fábrica. Y como nuestra fábrica es nueva (es decir, tiene 0 años), el resultado es la división por 0, lo cual está prohibido en matemáticas. Como resultado, el programa finaliza con un error. ¿Qué deberíamos haber hecho? Mueva toda la lógica a un método separado y llámelo, por ejemplo, printFactoryInfo() . Puedes pasarle un objeto CarFactory como parámetro . También puedes poner toda la lógica allí y, al mismo tiempo, procesar posibles errores, como el nuestro con cero años. A cada uno lo suyo. Se necesitan constructores para establecer correctamente el estado de un objeto. Para la lógica empresarial tenemos métodos. No debes mezclar uno con el otro.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION