Declaração de Construtores em Java

Já faz algum tempo que vejo principalmente em fóruns, novos usuários na linguagem Java que não compreendem a funcionalidade de um construtor. Acabam por exemplo, criando um construtor com argumentos (talvez com a ajuda de alguma IDE) e tentam acessar a classe através de uma inst ncia utilizando o construtor default, e sem ao menos ele ter sido criado. Neste artigo irei explicar e exemplificar algumas formas de se usar os construtores e como fazer para chamá-los através da inst ncia de classe.

O método construtor é muito semelhante a um método comum, porém ele se difere dos demais métodos por alguns pontos bem específicos e importantes para a linguagem Java.

Em primeiro lugar o método construtor deve possuir o mesmo nome da classe, sendo assim, é o único método por padrão Java Bean que será nomeado com a primeira letra em maiúscula.

Outro ponto importante é que um construtor nunca terá um tipo de retorno, poderá ser do tipo private, public, protected ou default, mas nunca terá um tipo de retorno nem que ele seja do tipo void. Os construtores também não poderão ser marcados como static, final ou abstract. Todas as classes devem possuir pelo menos um construtor, inclusive as classes abstratas.

Outro fato importante é que todo construtor possui de forma implícita uma chamada a super classe a qual pertence, e caso se faça essa chamada de forma explicita, o super() deve ser sempre a primeira declaração do método construtor, como no exemplo da listagem 2.

Se qualquer uma destas regras forem infringidas a compilação resultará em um erro.

1. Classe sem construtor explicito

Vou criar uma classe na Listagem 1, onde não precisarei declarar explicitamente um construtor padrão.

Listagem 1. Classe sem construtor explicito
public class Animal {
    private double peso;
    private String grupo;
    //getters and setters
    public static void main(String[] args) {
        Animal animal = new Animal();
        animal.setPeso(5.5);
        animal.setGrupo("Mamiferos");
    }
}

O que aconteceu? Foi criada uma classe chamada de Animal e foram criadas duas variáveis de instancia, peso e grupo. No método main(), criei uma variável de referência animal, do tipo Animal e atribui a ela um objeto do tipo Animal. Quando eu fiz new Animal(), aqui eu “mandei” construir um objeto do tipo Animal e atribuí-lo a variável de referencia criada. Mesmo não tendo criado um construtor explicitamente esse código não gera nenhum erro de compilação ou de execução.

Agora, se eu quisesse declarar outro construtor, que possuísse como argumento o peso e o grupo, conforme listagem 2, eu precisaria então obrigatoriamente declarar o construtor padrão para utilizá-lo, senão ocorreria um erro de compilação referente a linha que possui new Animal(). Sempre que um construtor com argumentos for declarado, o compilador não ira criar o construtor padrão, então devemos criá-lo explicitamente caso precisemos utilizá-lo.

2. Classe com construtor explicito

Criando dois construtores na classe Animal, um padrão e outro com lista de argumentos.

Listagem 2. Classe com construtor explicito
public class Animal {
    private double peso;
    private String grupo;
    //here getters and setters

    public Animal() {
        //default
    }

    public Animal(double peso, String grupo) {
        super();
        this.peso = peso;
        this.grupo = grupo;
    }

    public static void main(String[] args) {
        Animal animal = new Animal();
        animal.setPeso(5.5);
        animal.setGrupo("Mamiferos");

        Animal b = new Animal(6.0, "Aves");
    }
}

Foram declarados dois construtores, o primeiro com o comentário “default”, vai possibilitar a criação do objeto animal e o segundo ira possibilitar a criação do objeto b. O segundo construtor, o com argumentos, nos possibilita criar um objeto e inserir neste objeto valores em tempo de criação, sem precisar utilizar o método set() para este propósito. A chamada a super() é um padrão implícito em qualquer construtor, não precisa ser feita, mas caso seja utilizada, ela deve ser a primeira declaração dentro do construtor.

Podemos criar quantos construtores quisermos em uma única classe, mas sempre respeitando as regras de sobrecarga (http://pt.wikipedia.org/wiki/Sobrecarga).

Podemos também, por exemplo, fazer uma chamada ao construtor padrão a partir do construtor sobrecarregado, como na listagem 3.

Listagem 3. Classe com construtor explicito
public class Animal {
    private double peso;
    private String grupo;
    //here getters and setters

    public Animal() {
         System.out.println("Construtor Padrão");
    }

    public Animal(double peso, String grupo) {
        this();
        this.peso = peso;
        this.grupo = grupo;
    }

    public static void main(String[] args) {
        Animal b = new Animal(6.0, "Aves");
    }
}

Agora a execução irá chamar o construtor com argumentos e executará o this(), assim, será feita uma chamada ao construtor padrão e será impressa a string “Construtor Padrão“, e então a execução voltara para o construtor com os argumentos e executara as demais linhas dele. Quando o this() for usado, não deve ser usado o super() no construtor que declaramos a chamada a this(), por que a chamada a super() será executada pelo ultimo construtor chamado.

Não é possivel ter no mesmo construtor uma chamada a super() e uma chamada a this(), se isso ocorrer será gerado um erro de compilação. O this() sempre fará uma chamada ao construtor da própria classe, enquanto o super() fará a chamada ao construtor da super classe.

3. Construtor com Herança

Quando trabalhamos com herança em Java usamos a palavra chave extends para referenciar qual classe será herdada. Como já foi dito anteriormente, sempre que uma classe é instanciada seu construtor é chamado e a primeira execução dentro do construtor é uma chamada a super(), que chama o construtor da sua super classe e assim consecutivamente.

Vejamos um exemplo na tabela 1:

4. public Object() { }
3. public Animal() { super(); }Chamapublic Object() { }
2. public Dog() { super(); }Chamapublic Animal() { super(); }
1. Dog dog = new Dog()Chamapublic Dog() { super(); }

Tabela 1 – Passos executados na Herança

Conforme a tabela 1, a classe Dog que é herança da classe Animal, assim, quando executamos uma instancia da classe Dog, teremos os seguintes passos:

  1. A execução da instancia faz uma chamada ao construtor de Dog;
  2. Quando o construtor de Dog é executado ele faz uma chamada a super(), que é o construtor da classe Animal;
  3. Então o construtor da classe Animal é executado e faz uma chamada a super(), neste exemplo, o super() referencia a classe Object. Object é a classe mãe de todas as classes em Java, todas classes herdam Object de forma implícita.
  4. O construtor da classe Object é executado e então os passos voltam ordenadamente do passo 4 para o passo 1.
Listagem 4. Utilizando Herança
public class Animal {
    private double peso;
    private String grupo;
    //here getters and setters

    public Animal() {
        super();
        System.out.println("Construtor de Animal");
    }

    public Animal(double peso, String grupo) {
        this.peso = peso;
        this.grupo = grupo;
    }
}

public class Dog extends Animal {
    private String raca;
    private String porte;
    //here getters and setters

    public Dog() {
        super();
        System.out.println("Construtor de Dog");
    }

    public static void main(String[] args) {
        Dog dog = new Dog();
    }
}

Conforme a listagem 4, a execução do código ira imprimir na tela a frase “Construtor de Animal” e na próxima linha a frase “Construtor de Dog”. Isso acontece por que quando a pilha de chamadas chega em Object, ela começa a regredir, desempilhar, e então, passa a executar as linhas seguintes a super() em cada um dos construtores. Esse é dos motivos por que precisamos sempre declarar um construtor padrão ou um construtor sem argumentos quando estamos trabalhando com herança e possuímos também na classe, construtores com argumentos.

Listagem 5. Utilizando Herança parte 2
public class Animal {
    private double peso;
    private String grupo;
    //here getters and setters

    public Animal() {
        super();
        System.out.println("Construtor de Animal");
    }

    public Animal(double peso, String grupo) {
        this();
        System.out.println("Construtor sobrecarregado de Animal");
    }
}

public class Dog extends Animal {
    private String raca;
    private String porte;
    //here getters and setters

    public Dog() {
        super(5.6, "Mamiferos");
        System.out.println("Construtor de Dog");
    }

     public Dog(String raca, String porte) {
         this();
         System.out.println("Construtor sobrecarregado de Dog");
     }

     public static void main(String[] args) {
          Dog dog = new Dog("Vira Lata", "Médio");
     }
}

Executando o código da listagem 5, vamos ter um resultado diferente, isso porque foi criado um construtor sobrecarregado na classe Dog e a inst ncia da classe Dog é feita por ele. A seguinte ordem será executada quando este código rodar:

  1. Uma chamada ao construtor sobrecarregado de Dog;
  2. O construtor sobrecarregado é executado e faz uma chamada ao construtor padrão de Dog através do this();
  3. O construtor padrão de Dog é executado e faz uma chamada ao construtor sobrecarregado de Animal através do super sobrecarregado, super(5.6, "Mamiferos");
  4. O construtor sobrecarregado de Animal é executado e então faz uma chamada a seu construtor padrão através do this();
  5. O construtor padrão de Animal é executado e faz uma chamada a Object através do super();
  6. O construtor de Object é executado.

Após o item 6 ser executado, a pilha de chamadas começa a ser desfeita até chegar ao método main() onde tudo começou.

  1. Escreve na tela a frase: Construtor de Animal
  2. Escreve na tela a frase: Construtor sobrecarregado de Animal
  3. Escreve na tela a frase: Construtor de Dog
  4. Escreve na tela a frase: Construtor sobrecarregado de Dog
  5. Termina a execução voltando ao método main()

Conclusão

Vimos algumas regras essenciais para criação de construtores na linguagem Java. Também vimos como utilizá-los através da instancia de classe e chamadas por this(), a construtores da própria classe, e super(), a construtores das super classes. Os construtores são a forma de acesso de uma classe a outra, por isso são tão importantes na linguagem Java.

Download em PDF

Ballem

Marcio Ballem é bacharel em Sistemas de Informação pelo Centro Universitário Franciscano em Santa Maria/RS. Tem experiência com desenvolvimento Delphi e Java em projetos para gestão pública e acadêmica. Possui certificação em Java, OCJP 6.

Você pode gostar...