CodeGym /Blogue Java /Random-PT /Conceitos OOP em Java
John Squirrels
Nível 41
San Francisco

Conceitos OOP em Java

Publicado no grupo Random-PT
Um dos maiores pontos fortes de Java é a programação orientada a objetos (OOP). Essa é a razão pela qual essa linguagem se tornou tão popular e é adequada para projetos de qualquer tamanho. O que é programação orientada a objetos? Não é mágica, mas pode parecer mágica se você realmente entrar nela. OOP é sobre como construir seu software. É um conceito, ou melhor, um monte de conceitos oop em Java, que permitem criar algumas interações e relacionamentos específicos entre objetos Java para desenvolver e usar software de maneira eficaz. Conceitos OOP em Java - 1OOP clássico inclui 3 + 1 conceitos principais. Vamos começar com os clássicos.

O objeto

Objetos Java, assim como objetos do mundo real, possuem duas características: estado e comportamento.

Por exemplo, um objeto Humano tem estado (nome, gênero, dormindo ou não…) e comportamento (estuda Java, anda, fala…). Qualquer objeto Java armazena seu estado em campos e expõe seu comportamento por meio de métodos.

Encapsulamento

O encapsulamento de dados está ocultando dados internos do mundo externo e acessando-os apenas por meio de métodos expostos publicamente. O que isso significa? Quais dados? Escondendo de quem? Ocultar significa restringir o acesso direto aos membros de dados (campos) de uma classe.

Como funciona em Java:

  1. Os campos são privados
  2. Cada campo em uma classe recebe dois métodos especiais: um getter e um setter. Os métodos getter retornam o valor do campo. Os métodos setter permitem alterar o valor do campo de maneira indireta, mas permitida.

Exemplo de encapsulamento em código Java:


public class Student {
private int age;
private String name;

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

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

public class Test{
public static void main(String[] args) {
Student firstStudent = new Student();
firstStudent.setName("John");
// The name field is private, so you can no longer do this:  firstStudent.name = "John"; 
}
}

Por que você deve usar o encapsulamento?

O principal motivo é facilitar a alteração do seu código. Imagine que você tenha um aplicativo para uma escola de hóquei e haja uma classe HockeyStudent com dois campos que armazenam o nome e a idade do aluno quando ele se matriculou na escola. Algo assim:

public class HockeyStudent {
public String name;
public  int ageOfEnrollment;
}
O campo ageOfEnrollment é público, sem getters ou setters… Essa classe é usada por muitas outras classes, e estava tudo bem até que algum desenvolvedor decidiu que um único campo int não era suficiente. Alguns jogadores de hóquei em uma coorte são quase um ano mais velhos que seus pares, então seria mais conveniente dividi-los em dois grupos, dependendo do mês em que nasceram. Portanto, o campo ageOfEnrollment deve ser alterado para um array int (int[][]) : o primeiro número é para anos completos e o segundo é para meses. Agora você precisa refatorar todo o código que usa a classe Aluno ! Mas se o seu ageOfEnrollmentcampo é privado e você tem getters e setters, então tudo fica mais fácil. Se o requisito para definir a idade de um aluno mudar, basta atualizar a lógica no método setAgeOfEnrollment() e suas aulas podem continuar usando Aluno sem problemas! Este exemplo é um tanto artificial, mas espero que explique por que usar o encapsulamento é uma ótima ideia.

Herança

Este princípio é mais fácil de entender mesmo sem nenhuma experiência prática. Não se repita (DRY) poderia ser o mote para o conceito de herança. A herança permite criar uma classe filha que herda os campos e métodos da classe pai sem redefini-los. Claro, você pode sobrescrever os campos e métodos da classe pai na classe filha, mas não é uma necessidade. Além do mais, você pode adicionar novos estados e comportamentos na classe filha. As classes pai são às vezes chamadas de superclasses ou classes base, e as classes filhas são conhecidas como subclasses. A palavra-chave extends do Java é usada para implementar o princípio de herança no código.

Como funciona em Java:

  1. Crie a classe pai.
  2. Crie a classe filha usando a palavra-chave extends .
  3. No construtor da classe Child, use o método super(parentField1, parentField2, ...) para definir os campos dos pais.

Um construtor é um método especial usado para inicializar um objeto recém-criado. Um construtor tem o mesmo nome que o nome de sua classe. Existem dois tipos de construtores: padrão (construtor sem argumento) e construtor parametrizado. Uma classe deve ter pelo menos um construtor (tem o construtor padrão se nenhum outro construtor tiver sido definido) e pode ter vários deles.

Toda vez que você cria um novo objeto, você chama seu construtor. No exemplo acima, você faz isso nesta linha:


Student firstStudent = new Student();

Você usa a palavra-chave new para chamar o construtor padrão da classe Student : tudent() .

Algumas regras:

  1. Uma classe pode ter apenas um pai.
  2. Uma classe pai pode ter muitas classes filhas.
  3. Uma classe filha pode ter suas próprias classes filhas.

Exemplo de herança em código Java

Vamos criar uma classe Telefone .

public class Phone {
    int price;
    double weight;

// Constructor
public Phone(int price, double weight) {
        this.price = price;
        this.weight = weight;
    }

    void orderPhone(){
        System.out.println("Ordering phone...");
    }
}
Claro, existem diferentes tipos de telefones, então vamos criar duas classes filhas: uma para telefones Android e uma segunda para iPhones. Em seguida, adicionaremos alguns campos e métodos que o pai não possui. E usaremos super() para chamar construtores para inicializar os campos que a classe pai possui.

Exemplo de herança em Java


public class Android extends Phone {

// Some new fields     
String androidVersion;
int screenSize;

    String secretDeviceCode;

// Constructor 
    public Android(int price, double weight, String androidVersion, int screenSize, String secretDeviceCode) {
        super(price, weight); // Android inherits Phone’s fields

        //this - reference to the current object
        //super - reference to the parent object

        this.androidVersion = androidVersion;
        this.screenSize = screenSize;
        this.secretDeviceCode = secretDeviceCode;
    }

	// New Android-specific method, does not exist in the Phone class 
    void installNewAndroidVersion() {
        System.out.println("installNewAndroidVersion invoked...");

    }

}

public class IPhone extends Phone {
   
    boolean fingerPrint;

    public IPhone(int price, double weight, boolean fingerPrint) {
        super(price, weight);
        System.out.println("IPhone constructor was invoked...");
        this.fingerPrint = fingerPrint;
    }

    void deleteIPhoneFromDb() {
        System.out.println("deleteIPhoneFromDb invoked...");
    }

@Override // This is about polymorphism, see below
void orderPhone(){
        System.out.println("Ordering my new iPhone and deleting the old one...");
    }
}
Portanto, repetindo: em Java, a herança permite estender uma classe com classes filhas que herdam os campos e métodos da classe pai. É uma excelente maneira de obter a reutilização de código.

Polimorfismo

O polimorfismo é a capacidade de um objeto se transformar, assumindo diferentes formas ou agindo de maneiras diferentes. Em Java, o polimorfismo geralmente ocorre quando uma referência de classe pai é usada para se referir a um objeto de classe filho.

O que isso significa e como funciona em Java:

O que é polimorfismo em Java? Em geral, isso significa que você pode usar o mesmo nome de método para finalidades diferentes. Existem dois tipos de polimorfismo em Java: substituição de método (polimorfismo dinâmico) e sobrecarga de método (polimorfismo estático).

Substituição de método

Você pode substituir o método de uma classe pai em uma classe filha, forçando-a a funcionar de maneira diferente. Vamos criar uma classe pai Musician com um método play() .

Exemplo de polimorfismo em código Java


   public class Musician {
    String name;
    int age;

    // Default constructor
    public Musician() {
    }

    // Parameterized constructor
    public Musician(String name, int age) {
        this.name = name;
        this.age = age;
    }

    void play() {
        System.out.println("I am playing my instrument...");
    }
}
Músicos diferentes usam instrumentos diferentes. Vamos criar duas classes filhas: Pianist e Violinist . Graças ao polimorfismo, cada um executará sua própria versão do método play() . Ao substituir, você pode usar a anotação @Override , mas não é necessário.

public class Pianist extends Musician {
    
    String favoritePianoType;

    public Pianist(String name, int age, String favoritePianoType) {
        super(name, age);
        this.favoritePianoType = favoritePianoType;
    }


    @Override
void play(){
        System.out.println("I am playing the piano...");
    }
}
O violinista pode ser solista ou membro de uma orquestra. Vamos levar isso em consideração ao substituir nosso método play() .

public class Violinist extends Musician { 
    boolean isSoloist; 

public Violinist(String name, int age, boolean isSoloist) {
            super(name, age);
            this.isSoloist = isSoloist;
        }


    @Override
void play(){
if (isSoloist) 
        System.out.println("I am playing the violin solo...");
else 
System.out.println("I am playing the violin in an orchestra...");

    }
}
Vamos criar uma classe Demo , na qual criaremos três objetos, uma instância de cada uma das classes criadas anteriormente. Veremos os resultados que obtemos.

public class Demo {
  public static void main(String[] args) {
  Musician musician = new Musician();
  Violinist violinist = new Violinist("John", 32, true);
  Pianist pianist = new Pianist("Glen", 30, "Acoustic"); 

  System.out.println("Musician said:");
  musician.play();
  System.out.println("Violinist said:");
  violinist.play();
  System.out.println("Pianist said:");
  pianist.play();
    }
}
Aqui está o que obtemos:

Musician said:
I am playing my instrument...
Violinist said:
I am playing the violin solo…
Pianist said:
I am playing the piano...
Todo violinista e pianista é músico, mas nem todo músico é violista ou pianista. Isso significa que você pode usar o método de reprodução do músico se não precisar criar um novo. Ou você pode chamar o método do pai do filho usando a palavra-chave super . Vamos fazer isso no código do Pianist:

public class Pianist extends Musician {

    String favoritePianoType;
    
    @Override
    void play(){
        super.play();
        System.out.println("I am playing the piano...");
    }
}
Agora vamos chamar nosso método main() na classe Demo . Aqui está o resultado:

Musician said:
I am playing my instrument...
Violinist said:
I am playing the violin solo...
Pianist said:
I am playing my instrument...
I am playing the piano...

Sobrecarga de métodos

Sobrecarga de método significa usar vários métodos com o mesmo nome na mesma classe. Eles devem ser diferentes em termos de número, ordem ou tipos de seus parâmetros. Suponha que um pianista possa tocar um piano acústico e um piano elétrico. Para tocar uma elétrica, o músico precisa de eletricidade. Vamos criar dois métodos play() diferentes. O primeiro sem parâmetros, para um piano acústico, e o segundo com um parâmetro que indica se há eletricidade disponível.

public class Pianist extends Musician {

    String name;
    int age;
    String favoritePianoType;

    @Override
    void play(){
        super.play();
        System.out.println("I am playing the piano...");
    }
    void play(boolean isElectricity){
        if (isElectricity) {
            System.out.println("The electricity is on.");
            System.out.println("I am playing the piano...");
        }
        else System.out.println("I can't play this without electricity.");
    }
}
A propósito, você pode usar o primeiro método play() dentro do segundo método play(boolean) desta maneira:

void play(boolean isElectricity){
        if (isElectricity) {
            System.out.println("The electricity is on.");
            play();
        }
        else System.out.println("I can't play this without electricity.");
    }
Vamos adicionar algumas linhas à nossa classe Demo para demonstrar nossa sobrecarga:

public class Demo {
    public static void main(String[] args) {

        Musician musician = new Musician();
        Violinist violinist = new Violinist("John", 23, true);
        Pianist pianist = new Pianist("Glen", 30, "Acoustic"); 

        System.out.println("Musician said:");
        musician.play();
        System.out.println("Violinist said:");
        violinist.play();
        System.out.println("Pianist said:");
        pianist.play();
        System.out.println("The pianist will now try the electric piano:");
        pianist.play(true);
        System.out.println("The electricity has been shut off. Now when trying the electric piano, the pianist says:");
        pianist.play(false);
    }
}
Aqui está o resultado:

Musician said:
I am playing my instrument...
Violinist said:
I am playing the violin solo...
Pianist said:
I am playing my instrument...
I am playing the piano...
The pianist will now try the electric piano:
The electricity is on.
I am playing my instrument...
I am playing the piano...
The electricity has been shut off. Now when trying the electric piano, the pianist says:
I can't play this without electricity.
Java sabe qual método deve ser usado com base em seus parâmetros e no tipo de objeto. Isso é polimorfismo.

Abstração

Quando definimos uma classe, estamos tentando construir um modelo de algo. Por exemplo, suponha que estamos escrevendo um videogame chamado MyRacer com diferentes carros de corrida. Um jogador pode escolher um deles e depois atualizá-lo ou comprar um diferente. Então… O que é um carro? Um carro é uma coisa bastante complicada, mas se estamos tentando criar um videogame de corrida (em oposição a um simulador de direção), não precisamos descrever todos os milhares de engrenagens e juntas que ele contém. Precisamos de seu modelo, velocidade máxima, características de manobrabilidade, preço, cor… E talvez seja o suficiente. Esse é o modelo de carro para o nosso jogo. Mais tarde, no MyRacer 2, suponha que decidamos adicionar pneus que afetam o manuseio na estrada. Aqui o modelo é diferente, pois adicionamos mais detalhes. Deixar' s definem abstração de dados como o processo de identificar apenas as características importantes (ou necessárias) de um objeto e ignorar quaisquer detalhes irrelevantes. Existem diferentes níveis de abstração. Por exemplo, se você é passageiro de um ônibus, precisa saber como é o seu ônibus e para onde está indo, mas não precisa saber como dirigi-lo. Se você é um motorista de ônibus, não precisa saber como criar um novo ônibus - você só precisa saber como dirigi-lo. Mas se você é um fabricante de ônibus, precisa ir para um nível mais baixo de abstração, porque os detalhes do projeto do ônibus são muito importantes para você. Espero que você entenda o que quero dizer. você precisa saber como é o seu ônibus e para onde está indo, mas não precisa saber como dirigi-lo. Se você é um motorista de ônibus, não precisa saber como criar um novo ônibus - você só precisa saber como dirigi-lo. Mas se você é um fabricante de ônibus, precisa ir para um nível mais baixo de abstração, porque os detalhes do projeto do ônibus são muito importantes para você. Espero que você entenda o que quero dizer. você precisa saber como é o seu ônibus e para onde está indo, mas não precisa saber como dirigi-lo. Se você é um motorista de ônibus, não precisa saber como criar um novo ônibus - você só precisa saber como dirigi-lo. Mas se você é um fabricante de ônibus, precisa ir para um nível mais baixo de abstração, porque os detalhes do projeto do ônibus são muito importantes para você. Espero que você entenda o que quero dizer.

Como funciona em Java:

Vamos construir quatro níveis de abstração em Java, ou melhor, em OOP — do mais baixo (o mais específico) ao mais alto (o mais abstrato).
  1. O nível mais baixo de abstração é um objeto específico. É uma entidade com um conjunto de características que pertencem a uma classe específica. Tem valores de campo específicos

  2. Um modelo para criar objetos é uma classe. É uma descrição de um conjunto de objetos com propriedades e estrutura interna semelhantes.

  3. Uma classe abstrata é uma descrição abstrata das características de um conjunto de classes (ela atua como um modelo para herança por outras classes). Possui alto nível de abstração, sendo impossível criar objetos diretamente de uma classe abstrata. Somente classes filhas de classes abstratas podem ser usadas para criar objetos. Uma classe abstrata pode incluir métodos com uma implementação, mas isso não é um requisito.

  4. Uma interface é uma construção da linguagem de programação Java que contém apenas métodos públicos abstratos e campos constantes estáticos (estática final). Em outras palavras, nem classes abstratas nem interfaces podem ser usadas para gerar objetos.

Aliás, no Java 8 ou posterior, as interfaces podem ter não apenas métodos abstratos e constantes, mas também métodos padrão e estáticos. Em Java, uma interface define um comportamento, enquanto uma classe abstrata é usada para criar uma hierarquia. Uma interface pode ser implementada por várias classes.

Exemplo de interface em código Java


interface Human {
	public void struggle();
	public void protect();
}

interface Vulcan {
	int angleOfPointyEars; 
	public void turnOffEmotions(boolean isOn);
	public void telepathy();
}
Você pode implementar mais de uma interface

The Spock class implements Human and Vulcan {
public void struggle() {
System.out.println("I am struggling...");
}
	public void protect() {
System.out.println("You are under my protection!”);
}
public void turnOffEmotions(boolean isOn){
If (isOn) {
System.out.println("I am turning off my emotions.");
isOn= !isOn;
}
}
	public void telepathy() {
System.out.println("Connecting to your brain...");
}

}
Para alunos iniciantes, abrange todos os principais conceitos de programação orientada a objetos em Java. Além dos 4 princípios OOP principais, Java também possui associação, agregação e composição. Você pode chamá-los de "princípios OOP adicionais". Eles merecem seu próprio artigo separado.
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION