JavaRush /Blogue Java /Random-PT /Construtores em Java

Construtores em Java

Publicado no grupo Random-PT
Olá! Hoje veremos um tema muito importante que diz respeito aos nossos objetos. Aqui, sem exagero, podemos dizer que você usará esse conhecimento todos os dias no trabalho real! Falaremos sobre construtores. Você pode estar ouvindo esse termo pela primeira vez, mas na verdade você provavelmente já usou construtores, mas você mesmo não percebeu :) Veremos isso mais tarde.

O que é um construtor em Java e por que ele é necessário?

Vejamos dois exemplos.
public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

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

   }
}
Criamos nosso carro e definimos seu modelo e velocidade máxima. Porém, em um projeto real, o objeto Car terá claramente mais de 2 campos. E, por exemplo, 16 campos!
public class Car {

   String model;//model
   int maxSpeed;//max speed
   int wheels;// disk width
   double engineVolume;//engine capacity
   String color;//color
   int yearOfIssue;//year of issue
   String ownerFirstName;//Owner's name
   String ownerLastName;//owner's last name
   long price;//price
   boolean isNew;//new or not
   int placesInTheSalon;//number of seats in the cabin
   String salonMaterial;// interior material
   boolean insurance;//is it insured
   String manufacturerCountry;//manufacturer country
   int trunkVolume;// trunk volume
   int accelerationTo100km;//acceleration to 100 km/h in seconds


   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";

   }

}
Criamos um novo objeto Car . Um problema: temos 16 campos, mas inicializamos apenas 12 ! Experimente agora usar o código para encontrar aqueles que esquecemos! Não é tão fácil, certo? Em tal situação, o programador pode facilmente cometer um erro e pular a inicialização de algum campo. Como resultado, o comportamento do programa se tornará errôneo:
public class Car {

   String model;//model
   int maxSpeed;//max speed
   int wheels;// disk width
   double engineVolume;//engine capacity
   String color;//color
   int yearOfIssue;//year of issue
   String ownerFirstName;//Owner's name
   String ownerLastName;//owner's last name
   long price;//price
   boolean isNew;//new or not
   int placesInTheSalon;//number of seats in the cabin
   String salonMaterial;// interior material
   boolean insurance;//is it insured
   String manufacturerCountry;//manufacturer country
   int trunkVolume;// trunk volume
   int accelerationTo100km;//acceleration to 100 km/h in seconds


   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("Model Bugatti Veyron. Engine size - " + bugatti.engineVolume + ", trunk - " + bugatti.trunkVolume + ", salon is made of" + bugatti.salonMaterial +
       ", disc width - " + bugatti.wheels + ". Was acquired in 2018 by Mr. " + bugatti.ownerLastName);

   }

}
Saída do console:
Modelo Bugatti Veyron. Cilindrada do motor - 6,3, porta-malas - 0, interior em null, largura do aro - 0. Foi adquirido em 2018 pelo Sr.
Seu comprador, que pagou US$ 2 milhões por um carro, obviamente não gostará de ser chamado de “Sr. Nulo”! Mas, falando sério, no final, nosso programa acabou com um objeto criado incorretamente - um carro com largura de aro 0 (ou seja, sem aro algum), um porta-malas faltando, um interior feito de material desconhecido e até mesmo pertencente a alguém desconhecido . Só podemos imaginar como tal erro poderia ocorrer durante a execução do programa! Precisamos de alguma forma evitar tais situações. Precisamos que nosso programa tenha uma limitação: ao criar um novo objeto veículo, por exemplo, deve-se sempre especificar o modelo e a velocidade máxima para ele. Caso contrário, não permita a criação de objetos. As funções construtoras lidam facilmente com essa tarefa. Eles receberam esse nome por um motivo. O construtor cria uma espécie de “esqueleto” da classe, ao qual cada novo objeto da classe deve corresponder. Por conveniência, vamos voltar a uma versão mais simples da classe Car com dois campos. Dados os nossos requisitos, o construtor da classe Car ficará assim:
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
E a criação de um objeto agora fica assim:
public static void main(String[] args) {
   Car bugatti = new Car("Bugatti Veyron", 407);
}
Prestar atençãocomo o construtor é criado. É semelhante a um método regular, mas não possui um tipo de retorno. Neste caso, o nome da classe é indicado no construtor, também com letra maiúscula. No nosso caso - Carro . Além disso, o construtor usa a palavra-chave nova para você this . "this" em inglês significa "isto, isto". Esta palavra se refere a um objeto específico. Código no construtor:
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
pode ser traduzido quase literalmente: " modelo para esta máquina (que estamos criando agora) = o argumento do modelo , que é especificado no construtor. maxSpeed ​​​​para esta máquina (que estamos criando) = o argumento maxSpeed , que é especificado no construtor." Isso é o que aconteceu:
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);
   }

}
Saída do console:
Bugatti Veyron 407
O construtor atribuiu com êxito os valores necessários. Você deve ter notado que um construtor é muito semelhante a um método normal! É assim: um construtor é um método, só que um pouco específico :) Assim como em um método, passamos parâmetros para o nosso construtor. E assim como chamar um método, chamar um construtor não funcionará se você não os especificar:
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!
   }

}
Veja, o designer fez o que estávamos tentando alcançar. Agora você não pode criar um carro sem velocidade ou sem modelo! As semelhanças entre construtores e métodos não param por aí. Assim como os métodos, os construtores podem estar sobrecarregados. Imagine que você tem 2 gatos em casa. Você pegou um deles quando era gatinho e trouxe o segundo da rua para casa já adulto e não sabe exatamente quantos anos ele tem. Isso significa que nosso programa deve ser capaz de criar gatos de dois tipos - com nome e idade para o primeiro gato, e apenas com nome - para o segundo gato. Para fazer isso, sobrecarregaremos o construtor:
public class Cat {

   String name;
   int age;

   //for the first cat
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   //for the second cat
   public Cat(String name) {
       this.name = name;
   }

   public static void main(String[] args) {

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

}
Ao construtor original com os parâmetros “nome” e “idade” adicionamos outro, apenas com nome. Sobrecarregamos os métodos da mesma forma nas lições anteriores. Agora podemos criar com sucesso as duas versões de gatos :) Por que os construtores são necessários?  - 2Você lembra que no início da palestra falamos que você já havia usado construtores, mas simplesmente não percebeu? Isto é verdade. O fato é que toda classe em Java possui um chamado construtor padrão. Não possui argumentos, mas é acionado sempre que qualquer objeto de qualquer classe é criado.
public class Cat {

   public static void main(String[] args) {

       Cat barsik = new Cat(); //this is where the default constructor worked
   }
}
À primeira vista, isso não é perceptível. Bom, criamos um objeto e criamos, cadê o trabalho do designer? Para ver isso, vamos escrever um construtor vazio para a classe Cat com nossas próprias mãos e dentro dele imprimiremos alguma frase no console. Se for exibido, o construtor funcionou.
public class Cat {

   public Cat() {
       System.out.println("Created a cat!");
   }

   public static void main(String[] args) {

       Cat barsik = new Cat(); //this is where the default constructor worked
   }
}
Saída do console:
Eles criaram um gato!
Aqui está a confirmação! O construtor padrão está sempre presente de forma invisível em suas classes. Mas você precisa conhecer mais uma característica dele. O construtor padrão desaparece da classe quando você cria algum construtor com argumentos. A prova disso, aliás, já vimos acima. Aqui neste 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!
   }
}
Não poderíamos criar um gato sem nome e idade porque definimos um construtor para Cat : string + número. O construtor padrão desapareceu da classe imediatamente depois disso. Portanto, lembre-se: se você precisar de vários construtores em sua classe, inclusive um vazio, será necessário criá-lo separadamente. Por exemplo, estamos criando um programa para uma clínica veterinária. Nossa clínica quer fazer boas ações e ajudar gatos vadios, dos quais não sabemos nome nem idade. Então nosso código deverá ficar assim:
public class Cat {

   String name;
   int age;

   //for domestic cats
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   //for street cats
   public Cat() {
   }

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 5);
       Cat streetCat = new Cat();
   }
}
Agora que escrevemos explicitamente um construtor padrão, podemos criar gatos de ambos os tipos :) Para um construtor (como para qualquer método), a ordem dos argumentos é muito importante. Vamos trocar os argumentos name e age em nosso construtor.
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!
   }
}
Erro! O construtor afirma claramente que quando um objeto Cat é criado, deve ser passado um número e uma string, nessa ordem. É por isso que nosso código não funciona. Lembre-se disso e tenha isso em mente ao criar suas próprias classes:
public Cat(String name, int age) {
   this.name = name;
   this.age = age;
}

public Cat(int age, String name) {
   this.age = age;
   this.name = name;
}
São dois designers completamente diferentes! Se expressarmos a resposta à pergunta “Por que precisamos de um construtor?” em uma frase, podemos dizer: para que os objetos estejam sempre no estado correto. Ao usar construtores, todas as suas variáveis ​​serão inicializadas corretamente e não haverá carros com velocidade 0 ou outros objetos “incorretos” no programa. A sua utilização é muito benéfica, em primeiro lugar, para o próprio programador. Se você mesmo inicializar os campos, existe um grande risco de perder alguma coisa e cometer um erro. Mas isso não acontecerá com um construtor: se você não passou todos os argumentos necessários para ele ou misturou seus tipos, o compilador lançará imediatamente um erro. Vale ressaltar separadamente que você não deve colocar a lógica do seu programa dentro do construtor. Para fazer isso, você tem à sua disposição métodos nos quais pode descrever todas as funcionalidades necessárias. Vejamos por que a lógica do construtor é uma má ideia:
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("Our car factory is called" + this.name);
   System.out.println("She was founded" + this.age + " years ago" );
   System.out.println("During this time it was produced" + this.carsCount +  "cars");
   System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
}

   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Ford", 115 , 50000000);
   }
}
Temos uma classe CarFactory que descreve uma fábrica para produção de carros. Dentro do construtor inicializamos todos os campos e colocamos a lógica aqui: exibimos algumas informações sobre a fábrica para o console. Parece que não há nada de errado com isso, o programa funcionou perfeitamente. Saída do console:
Nossa fábrica de automóveis se chama Ford. Foi fundada há 115 anos. Durante esse período, produziu 50 milhões de carros. Em média, produz 434.782 carros por ano.
Mas, na verdade, plantámos uma bomba-relógio. E esse código pode facilmente levar a erros. Imaginemos que agora não estamos a falar da Ford, mas sim da nova fábrica “Amigo Motors”, que existe há menos de um ano e já produziu 1000 automóveis:
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("Our car factory is called" + this.name);
   System.out.println("She was founded" + this.age + " years ago" );
   System.out.println("During this time it was produced" + this.carsCount +  "cars");
   System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
}


   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Amigo Motors", 0 , 1000);
   }
}
Saída do console:
Nossa fábrica de automóveis se chama Amigo Motors Exception no thread "main" java.lang.ArithmeticException: / by zero Foi fundada há 0 anos. Durante esse período, produziu 1.000 carros na CarFactory.<init>(CarFactory.java:15) em CarFactory.main(CarFactory.java:23) Processo concluído com código de saída 1</init>
Chegamos! O programa terminou com algum erro estranho. Você tentará adivinhar qual é o motivo? O motivo é a lógica que colocamos no construtor. Especificamente, nesta linha:
System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
Aqui fazemos o cálculo e dividimos a quantidade de carros produzidos pela idade da fábrica. E como nossa fábrica é nova (ou seja, tem 0 anos), o resultado é a divisão por 0, o que é proibido em matemática. Como resultado, o programa termina com um erro. O que deveríamos ter feito? Mova toda a lógica para um método separado e chame-o, por exemplo, printFactoryInfo() . Você pode passar para ele um objeto CarFactory como parâmetro . Você também pode colocar toda a lógica aí, e ao mesmo tempo - processar possíveis erros, como o nosso com zero anos. Cada um na sua. Construtores são necessários para definir corretamente o estado de um objeto. Para lógica de negócios, temos métodos. Você não deve misturar um com o outro.
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION