Construtor em Java

O construtor em Java é usado para criar a instância da classe. Os construtores são quase iguais aos métodos, exceto por duas coisas – seu nome é o mesmo que o nome da classe e ele não tem tipo de retorno. Às vezes, os construtores também são referidos como métodos especiais para inicializar um objeto.

Construtor em Java

Sempre que usamos a palavra-chave new para criar uma instância de uma classe, o construtor é invocado e o objeto da classe é retornado. Como o construtor só pode retornar o objeto para a classe, isso é feito implicitamente pelo tempo de execução do Java e não é necessário adicionar um tipo de retorno a ele. Se adicionarmos um tipo de retorno a um construtor, ele se tornará um método da classe. Esta é a maneira como o tempo de execução do Java distingue entre um método normal e um construtor. Vamos supor que temos o seguinte código na classe Employee.

public Employee() {
	System.out.println("Employee Constructor");
}


public Employee Employee() {
	System.out.println("Employee Method");
	return new Employee();
}

Aqui o primeiro é um construtor, observe que não há tipo de retorno e nenhuma declaração de retorno. O segundo é um método normal onde estamos novamente chamando o primeiro construtor para obter uma instância de Employee e retorná-lo. É recomendável não ter o nome do método igual ao nome da classe, pois isso cria confusão.

Tipos de Construtor em Java

Há três tipos de construtor em Java.

  1. Construtor Padrão
  2. Construtor Sem Argumentos
  3. Construtor Parametrizado

Vamos examinar todos esses tipos de construtores com programas de exemplo.

Construtor Padrão em Java

Não é necessário sempre fornecer uma implementação de construtor no código da classe. Se não fornecermos um construtor, então o java fornece uma implementação de construtor padrão para usarmos. Vamos ver um programa simples onde o construtor padrão está sendo usado, já que não vamos definir explicitamente um construtor.

package com.journaldev.constructor;

public class Data {

	public static void main(String[] args) {
		Data d = new Data();
	}
}
  1. O papel único do construtor padrão é inicializar o objeto e retorná-lo ao código que o chamou.
  2. O construtor padrão é sempre sem argumentos e fornecido pelo compilador java apenas quando não há nenhum construtor existente definido.
  3. Muitas vezes estamos bem apenas com o construtor padrão, já que outras propriedades podem ser acessadas e inicializadas através de métodos getter setter.

Construtor Sem Argumentos

Construtor sem nenhum argumento é chamado de construtor sem argumentos. É como substituir o construtor padrão e é usado para realizar algumas inicializações prévias, como verificar recursos, conexões de rede, log, etc. Vamos dar uma rápida olhada no construtor sem argumentos em Java.

package com.journaldev.constructor;

public class Data {
        //construtor sem argumentos
	public Data() {
		System.out.println("No-Args Constructor");
	}
	public static void main(String[] args) {
		Data d = new Data();
	}
}

Agora, quando chamarmos new Data(), então nosso construtor sem argumentos será chamado. A imagem abaixo ilustra esse comportamento, verifique a saída do console do programa.

Construtor Parametrizado

O construtor com argumentos é chamado de construtor parametrizado. Vamos ver o exemplo de construtor parametrizado em Java.

package com.journaldev.constructor;

public class Data {

	private String name;

	public Data(String n) {
		System.out.println("Parameterized Constructor");
		this.name = n;
	}

	public String getName() {
		return name;
	}

	public static void main(String[] args) {
		Data d = new Data("Java");
		System.out.println(d.getName());
	}

}

Sobrecarga de Construtor em Java

Quando temos mais de um construtor, então é uma sobrecarga de construtor em Java. Vamos ver um exemplo de sobrecarga de construtor em um programa Java.

package com.journaldev.constructor;

public class Data {

	private String name;
	private int id;

	//construtor sem argumentos
	public Data() {
		this.name = "Default Name";
	}
	//construtor de um parâmetro
	public Data(String n) {
		this.name = n;
	}
	//construtor de dois parâmetros
	public Data(String n, int i) {
		this.name = n;
		this.id = i;
	}

	public String getName() {
		return name;
	}

	public int getId() {
		return id;
	}

	@Override
	public String toString() {
		return "ID="+id+", Name="+name;
	}
	public static void main(String[] args) {
		Data d = new Data();
		System.out.println(d);
		
		d = new Data("Java");
		System.out.println(d);
		
		d = new Data("Pankaj", 25);
		System.out.println(d);
		
	}

}

Construtor Privado em Java

Observe que não podemos usar as palavras-chave abstract, final, static e synchronized com construtores. No entanto, podemos usar modificadores de acesso para controlar a instanciação do objeto da classe. O uso de acesso public e default ainda é permitido, mas qual é a utilidade de tornar um construtor privado? Nesse caso, nenhuma outra classe poderá criar uma instância da classe. Bem, um construtor é tornado privado no caso de querermos implementar o padrão de design singleton. Como o Java fornece automaticamente um construtor padrão, precisamos criar explicitamente um construtor e mantê-lo privado. As classes cliente são fornecidas com um método estático de utilidade para obter a instância da classe. Um exemplo de construtor privado para a classe Data é dado abaixo.

// construtor privado
private Data() {
	// construtor vazio para implementação do padrão singleton
	// pode conter código a ser usado dentro do método getInstance() da classe
}

Encadeamento de Construtores em Java

Quando um construtor chama outro construtor da mesma classe, é chamado de encadeamento de construtores. Devemos usar a palavra-chave this para chamar outro construtor da classe. Às vezes, é usado para definir alguns valores padrão das variáveis da classe. Note que outra chamada de construtor deve ser a primeira instrução no bloco de código. Além disso, não deve haver chamadas recursivas que criem um loop infinito. Vamos ver um exemplo de encadeamento de construtores em um programa Java.

package com.journaldev.constructor;

public class Employee {

	private int id;
	private String name;
	
	public Employee() {
		this("John Doe", 999);
		System.out.println("Default Employee Created");
	}
	
	public Employee(int i) {
		this("John Doe", i);
		System.out.println("Employee Created with Default Name");
	}
	public Employee(String s, int i) {
		this.id = i;
		this.name = s;
		System.out.println("Employee Created");
	}
	public static void main(String[] args) {

		Employee emp = new Employee();
		System.out.println(emp);
		Employee emp1 = new Employee(10);
		System.out.println(emp1);
		Employee emp2 = new Employee("Pankaj", 20);
		System.out.println(emp2);
	}

	@Override
	public String toString() {
		return "ID = "+id+", Name = "+name;
	}
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}

I have overridden the toString() method to print some useful information about Employee object. Below is the output produced by above program.

Employee Created
Default Employee Created
ID = 999, Name = John Doe
Employee Created
Employee Created with Default Name
ID = 10, Name = John Doe
Employee Created
ID = 20, Name = Pankaj

Observe como um construtor está sendo chamado de outro construtor, isso é chamado de processo de encadeamento de construtores.

Super Construtor em Java

Às vezes, uma classe é herdada de uma superclasse, nesse caso, se precisarmos chamar o construtor da superclasse, podemos fazer isso usando a palavra-chave super. Vamos dar uma olhada em um exemplo de uso do construtor da superclasse. Note que a chamada do construtor super deve ser a primeira instrução no construtor da classe filha. Além disso, ao instanciar o construtor da classe filha, o Java primeiro inicializa a superclasse e depois a classe filha. Portanto, se o construtor da superclasse não for explicitamente chamado, o construtor padrão ou sem argumentos é chamado pelo tempo de execução do Java. Vamos entender esses conceitos através de um programa de exemplo. Vamos supor que temos duas classes como abaixo.

package com.journaldev.constructor;

public class Person {

	private int age;

	public Person() {
		System.out.println("Person Created");
	}

	public Person(int i) {
		this.age = i;
		System.out.println("Person Created with Age = " + i);
	}

}
package com.journaldev.constructor;

public class Student extends Person {

	private String name;

	public Student() {
		System.out.println("Student Created");
	}

	public Student(int i, String n) {
		super(i); // super class constructor called
		this.name = n;
		System.out.println("Student Created with name = " + n);
	}

}

Agora, se criarmos um objeto Estudante como abaixo;

Student st = new Student();

Qual será a saída produzida? A saída do código acima será:

Person Created
Student Created

Portanto, a chamada foi para o construtor sem argumentos da classe Estudante, já que não houve chamada super na primeira instrução, o construtor sem argumentos ou padrão da classe Pessoa é chamado. Daí a saída. E se estivermos usando o construtor parametrizado da classe Estudante como Student st = new Student(34, "Pankaj");, a saída será:

Person Created with Age = 34
Student Created with name = Pankaj

Aqui a saída é clara porque estamos chamando explicitamente o construtor da superclasse, então o Java não precisa fazer nenhum trabalho extra do lado deles.

Construtor de Cópia Java

Java: O construtor de cópia em Java recebe um objeto da mesma classe como argumento e cria uma cópia dele. Às vezes, precisamos de uma cópia de outro objeto para realizar algum processamento. Podemos fazer isso de várias maneiras:

  1. implementar clonagem
  2. fornecer um método utilitário para cópia profunda do objeto.
  3. Ter um construtor de cópia

Agora vamos ver como escrever um construtor de cópia. Suponha que tenhamos uma classe Frutas como abaixo.

package com.journaldev.constructor;

import java.util.ArrayList;
import java.util.List;

public class Fruits {

	private List<String> fruitsList;

	public List<String> getFruitsList() {
		return fruitsList;
	}

	public void setFruitsList(List<String> fruitsList) {
		this.fruitsList = fruitsList;
	}

	public Fruits(List<String> fl) {
		this.fruitsList = fl;
	}
	
	public Fruits(Fruits fr) {
		List<String> fl = new ArrayList<>();
		for (String f : fr.getFruitsList()) {
			fl.add(f);
		}
		this.fruitsList = fl;
	}
}

Observe que Frutas(Frutas fr) realiza uma cópia profunda para retornar a cópia do objeto. Vamos analisar um programa de teste para entender por que é melhor ter um construtor de cópia para copiar um objeto.

package com.journaldev.constructor;

import java.util.ArrayList;
import java.util.List;

public class CopyConstructorTest {

	public static void main(String[] args) {
		List<String> fl = new ArrayList<>();
		fl.add("Mango");
		fl.add("Orange");

		Fruits fr = new Fruits(fl);

		System.out.println(fr.getFruitsList());

		Fruits frCopy = fr;
		frCopy.getFruitsList().add("Apple");

		System.out.println(fr.getFruitsList());

		frCopy = new Fruits(fr);
		frCopy.getFruitsList().add("Banana");
		System.out.println(fr.getFruitsList());
		System.out.println(frCopy.getFruitsList());

	}

}

A saída do programa acima é:

[Mango, Orange]
[Mango, Orange, Apple]
[Mango, Orange, Apple]
[Mango, Orange, Apple, Banana]

Observe que, ao usar o construtor de cópia, tanto o objeto original quanto sua cópia são independentes entre si e qualquer modificação em um deles não será refletida no outro. Isso é tudo para o construtor em Java.

Source:
https://www.digitalocean.com/community/tutorials/constructor-in-java