Herança em Java: Guia Completo de Programação Orientada a Objetos
- #Java
- #Orientação a objetos, classes e métodos
- #POO
Herança em Java: Guia Completo de Programação Orientada a Objetos
Introdução à Herança em Java
A herança é um dos pilares fundamentais da programação orientada a objetos (POO) em Java. Este conceito permite que uma classe filha herde características (atributos e métodos) de uma classe pai, promovendo a reutilização de código e criando hierarquias lógicas entre classes relacionadas.
Neste guia completo, você aprenderá tudo sobre herança em Java, desde conceitos básicos até implementações avançadas, com exemplos práticos que você pode usar em seus projetos.
O que é Herança em Java?
A herança em Java é um mecanismo que permite criar uma nova classe baseada em uma classe existente. A classe que é herdada é chamada de superclasse (ou classe pai), enquanto a classe que herda é chamada de subclasse (ou classe filha).
Vantagens da Herança
- Reutilização de código: Evita duplicação de código comum
- Manutenibilidade: Facilita a manutenção e atualização do código
- Extensibilidade: Permite adicionar novas funcionalidades facilmente
- Polimorfismo: Habilita o uso de polimorfismo em Java
- Organização: Cria hierarquias lógicas e organizadas
Sintaxe Básica da Herança em Java
A palavra-chave extends
é utilizada para implementar herança em Java:
public class ClassePai {
// Atributos e métodos da classe pai
}
public class ClasseFilha extends ClassePai {
// Atributos e métodos específicos da classe filha
// Herda automaticamente tudo da classe pai
}
Exemplo Prático: Sistema de Veículos
Vamos criar um exemplo completo de herança com um sistema de veículos:
Classe Pai (Superclasse)
public class Veiculo {
protected String marca;
protected String modelo;
protected int ano;
protected double velocidadeAtual;
// Construtor
public Veiculo(String marca, String modelo, int ano) {
this.marca = marca;
this.modelo = modelo;
this.ano = ano;
this.velocidadeAtual = 0.0;
}
// Métodos comuns a todos os veículos
public void acelerar(double incremento) {
this.velocidadeAtual += incremento;
System.out.println("Acelerando... Velocidade atual: " + velocidadeAtual + " km/h");
}
public void frear(double decremento) {
this.velocidadeAtual = Math.max(0, velocidadeAtual - decremento);
System.out.println("Freando... Velocidade atual: " + velocidadeAtual + " km/h");
}
public void exibirInfo() {
System.out.println("Marca: " + marca + ", Modelo: " + modelo + ", Ano: " + ano);
}
// Getters e Setters
public String getMarca() { return marca; }
public String getModelo() { return modelo; }
public int getAno() { return ano; }
public double getVelocidadeAtual() { return velocidadeAtual; }
}
Classes Filhas (Subclasses)
public class Veiculo {
protected String marca;
protected String modelo;
protected int ano;
protected double velocidadeAtual;
// Construtor
public Veiculo(String marca, String modelo, int ano) {
this.marca = marca;
this.modelo = modelo;
this.ano = ano;
this.velocidadeAtual = 0.0;
}
// Métodos comuns a todos os veículos
public void acelerar(double incremento) {
this.velocidadeAtual += incremento;
System.out.println("Acelerando... Velocidade atual: " + velocidadeAtual + " km/h");
}
public void frear(double decremento) {
this.velocidadeAtual = Math.max(0, velocidadeAtual - decremento);
System.out.println("Freando... Velocidade atual: " + velocidadeAtual + " km/h");
}
public void exibirInfo() {
System.out.println("Marca: " + marca + ", Modelo: " + modelo + ", Ano: " + ano);
}
// Getters e Setters
public String getMarca() { return marca; }
public String getModelo() { return modelo; }
public int getAno() { return ano; }
public double getVelocidadeAtual() { return velocidadeAtual; }
}
Testando a Herança
public class TesteHeranca {
public static void main(String[] args) {
// Criando objetos das classes filhas
Carro meuCarro = new Carro("Toyota", "Corolla", 2023, 4, "Flex");
Moto minhaMoto = new Moto("Honda", "CB600F", 2022, 600, true);
System.out.println("=== INFORMAÇÕES DOS VEÍCULOS ===");
meuCarro.exibirInfo();
System.out.println();
minhaMoto.exibirInfo();
System.out.println("\n=== TESTANDO MÉTODOS HERDADOS ===");
meuCarro.acelerar(50);
minhaMoto.acelerar(30); // Comportamento sobrescrito
System.out.println("\n=== MÉTODOS ESPECÍFICOS ===");
meuCarro.ligarArCondicionado();
minhaMoto.empinar();
// Demonstrando polimorfismo
System.out.println("\n=== POLIMORFISMO ===");
Veiculo[] veiculos = {meuCarro, minhaMoto};
for (Veiculo veiculo : veiculos) {
veiculo.exibirInfo(); // Chama o método sobrescrito de cada classe
System.out.println("---");
}
}
}
Conceitos Avançados de Herança
1. Modificadores de Acesso
public class Exemplo {
public String publico; // Acessível de qualquer lugar
protected String protegido; // Acessível na mesma package e subclasses
private String privado; // Acessível apenas na própria classe
String pacote; // Acessível apenas na mesma package
}
2. Palavra-chave Super
A palavra-chave super
é fundamental na herança:
public class Funcionario {
protected String nome;
protected double salario;
public Funcionario(String nome, double salario) {
this.nome = nome;
this.salario = salario;
}
public double calcularBonus() {
return salario * 0.1; // 10% do salário
}
}
public class Gerente extends Funcionario {
private String departamento;
public Gerente(String nome, double salario, String departamento) {
super(nome, salario); // Chama construtor da superclasse
this.departamento = departamento;
}
@Override
public double calcularBonus() {
// Gerentes têm bônus maior
return super.calcularBonus() * 2; // Chama método da superclasse
}
}
3. Classes Abstratas e Herança
public abstract class Animal {
protected String nome;
public Animal(String nome) {
this.nome = nome;
}
// Método concreto
public void dormir() {
System.out.println(nome + " está dormindo");
}
// Método abstrato - deve ser implementado pelas subclasses
public abstract void emitirSom();
}
public class Cachorro extends Animal {
public Cachorro(String nome) {
super(nome);
}
@Override
public void emitirSom() {
System.out.println(nome + " faz: Au au!");
}
}
Boas Práticas na Herança Java
1. Princípio da Substituição de Liskov
As subclasses devem ser substituíveis por suas superclasses sem quebrar a funcionalidade:
public class ContaBancaria {
protected double saldo;
public void sacar(double valor) {
if (valor <= saldo) {
saldo -= valor;
}
}
public double getSaldo() {
return saldo;
}
}
public class ContaPoupanca extends ContaBancaria {
@Override
public void sacar(double valor) {
// Mantém o contrato da superclasse
if (valor <= saldo && valor <= 1000) { // Limite adicional
super.sacar(valor);
}
}
}
2. Evitar Herança Profunda
Prefira composição quando a hierarquia ficar muito complexa:
// Em vez de herança profunda, use composição
public class Veiculo {
private Motor motor; // Composição
private Rodas rodas; // Composição
public Veiculo(Motor motor, Rodas rodas) {
this.motor = motor;
this.rodas = rodas;
}
}
Herança vs Composição: Quando Usar?
Use Herança quando:
- Existe uma relação "é um" clara
- Você quer aproveitar polimorfismo
- A hierarquia é estável e bem definida
Use Composição quando:
- A relação é "tem um" ou "usa um"
- Você precisa de maior flexibilidade
- A hierarquia pode mudar frequentemente
Limitações da Herança em Java
1. Herança Simples
Java permite apenas herança simples de classes (uma classe só pode estender uma superclasse):
// Isto NÃO é permitido em Java
// public class MinhaClasse extends Classe1, Classe2 { }
// Use interfaces para múltipla herança de comportamento
public interface Voador {
void voar();
}
public interface Nadador {
void nadar();
}
public class Pato implements Voador, Nadador {
@Override
public void voar() {
System.out.println("Pato voando");
}
@Override
public void nadar() {
System.out.println("Pato nadando");
}
}
}
Exercícios Práticos
Exercício 1: Sistema de Funcionários
Crie uma hierarquia de classes para representar diferentes tipos de funcionários (Desenvolvedor, Designer, Gerente) que herdem de uma classe Funcionario base.
Exercício 2: Formas Geométricas
Implemente uma hierarquia de formas geométricas (Círculo, Retângulo, Triângulo) que herdem de uma classe abstrata Forma com métodos para calcular área e perímetro.
Conclusão
A herança é um conceito fundamental na programação orientada a objetos em Java que oferece poderosas capacidades de reutilização e organização de código. Compreender seus princípios, sintaxe e boas práticas é essencial para todo desenvolvedor Java.
Lembre-se de sempre avaliar se a herança é a melhor solução para seu problema específico, considerando alternativas como composição quando apropriado. A herança bem implementada resulta em código mais limpo, manutenível e extensível.
Palavras-chave: herança java, programação orientada objetos, POO java, extends java, super java, override java, polimorfismo java, classes abstratas java, subclasse java, superclasse java, reutilização código java, tutorial herança java