Construtores são usados para inicializar as variáveis de instância. Todo construtor por padrão possui uma chamada implícita super(), a não ser que o próprio programador coloque de forma explícita a chamada a this(). Quando é usado herança é importante verificar os construtores das classes para que não haja erros de compilação.

Exemplos práticos:

Sobrecarga X Sobrescrita

As classes herdam as características e comportamentos das superclasses. Se a classe A possui um método X e a classe B herda da classe A. A classe B possui o método X, dessa forma, a classe B pode usar essa versão herdada ou a classe B pode fazer algumas alterações no método X, ou seja, a classe B pode fazer a sobrescrita de um método. Lembre-se para ser considerado sobrescrita de método é fundamental manter a assinatura do método como o original herdado. A assinatura de um método é a junção do nome do método e os parâmetros. Já na sobrecarga o nome do método é o mesmo, porém os parâmetros são diferentes, portanto, a assinatura é outra.

Por exemplo: observe os dois métodos pertencentes a classe Contador.

public double calcularSalarioMensal(){  //método sobrescrito, herdado da classe Pessoa
      return super.calcularSalarioMensal()+adicional;
  }
public double calcularSalarioMensal(double aumento){   //método sobrecarregado, não é o mesmo método herdado da classe Pessoa
    return (super.calcularSalarioMensal()*aumento/100)+super.calcularSalarioMensal();
}

Classes Abstratas

São classes cujo propósito é ser estendidas por outras classes. Não é possível criar objetos de classes abstratas. Dessa forma, se no seu projeto você observou que a classe servirá apenas para fornecer seus recursos (atributos e métodos), esta é uma boa candidata a ser uma classe abstrata.

De acordo com Sierra e Bates (2005, p. 148) "no caso da classe abstrata quem se encarregará do trabalho no tempo de execução serão instâncias de uma subclasse de sua classe abstrata".

  • Referência da parte entre aspas: SIERRA, K; BATES, B. Use a cabeça Java. Tradução da 2ª edição. Editora Alta Books, 2005.

Observe as palavras abaixo:

  • Esponja;

  • Baleia;

  • Vertebrado;

  • Galinha;

  • Animal;

  • Macaco;

  • Ema;

  • Réptil;

  • Lagarto;

  • Invertebrado;

  • Mamífero;

  • Porífero;

  • Cobra;

  • Ave

Quais seriam candidatas às classes abstratas? Quais seriam candidatas às classes concretas? E como ficariam a ordenação?

package um;

public class Animal {

protected String nome;
protected int anoNascimento;
public int obterIdade(int anoAtual){
return anoAtual - anoNascimento;
}

}

package um;

public class Cachorro extends Animal{

}

package um;

public class Principal {

public static void main(String[] args) {
Animal a = new Animal();
a.nome = "Fulano";
a.anoNascimento = 0;
Cachorro c = new Cachorro();
c.nome = "Totó";
c.anoNascimento = 2020;
System.out.println(c.obterIdade(2021));
}

}

Agora retorne a classe Animal e adicione o seguinte método public abstract void comunicar();

O que acontece??

Um erro de compilação!!!

Observe que public class Animal { é uma classe concreta e esta não pode ter uma método abstrato. Um método abstrato é um método que não possui corpo, ou seja, apenas há a definição da assinatura dele. Para resolver o problema torne a classe Animal abstrata.

Troque a linha public class Animal { para public abstract class Animal {

Mas o projeto ainda apresenta erro de compilação.

Observe que ao adicionar um método abstrato, é obrigatório a classe Cachorro implementar o método. Então é preciso reescrever a classe Cachorro, como segue abaixo:

package um;

public class Cachorro extends Animal{

@Override
public void comunicar() {
System.out.println("auauau");
}

}

Mais ainda tem-se outro erro de compilação, porém na classe Principal.

Qual seria o motivo disso ???

Lembrete: classes abstratas não podem ser instanciadas.

Portanto, é preciso retirar o objeto do tipo Animal criado. Sendo assim, a classe Principal ficará assim:

package um;

public class Principal {

public static void main(String[] args) {
Cachorro c = new Cachorro();
c.nome = "Totó";
c.anoNascimento = 2020;
System.out.println(c.obterIdade(2021));
}

}

A classe Cachorro herdar a classe Animal, portanto, estas tem um relacionamento é-um. Portanto, a classe Principal pode ser escrita assim:

package um;

public class Principal {

public static void main(String[] args) {
Animal c = new Cachorro();
c.nome = "Totó";
c.anoNascimento = 2020;
System.out.println(c.obterIdade(2021));
}

}

A classe Cachorro é um Animal, porém esta pode ter também seus próprios atributos e métodos. Na classe Cachorro adicione a variável de instância double peso.

package um;

public class Cachorro extends Animal{

double peso;
@Override
public void comunicar() {
System.out.println("auauau");
}

}

Faça uma alteração na classe Principal, como mostrado a seguir:

package um;

public class Principal {

public static void main(String[] args) {
Animal c = new Cachorro();
c.nome = "Totó";
c.anoNascimento = 2020;
c.peso = 50;  //erro de compilação
System.out.println(c.obterIdade(2021));
}

}

Veja que ocorre um erro de compilação, pois o objeto c é do Animal e tenta acessar uma variável que não existe na classe Animal, esta é específica do tipo Cachorro. Dessa forma, para resolver o problema, temos que fazer uma conversão (downcast). Veja o resultado:

package um;

public class Principal {

public static void main(String[] args) {
Animal c = new Cachorro();
c.nome = "Totó";
c.anoNascimento = 2020;
((Cachorro)c).peso = 50;  //downcast, converte Animal para Cachorro
System.out.println(c.obterIdade(2021));
}

}