JavaRush /Blogue Java /Random-PT /Diferença entre classes abstratas e interfaces

Diferença entre classes abstratas e interfaces

Publicado no grupo Random-PT
Olá! Nesta palestra, falaremos sobre como as classes abstratas diferem das interfaces e veremos exemplos com classes abstratas comuns. Diferença entre classes abstratas e interfaces - 1Dedicamos uma palestra separada às diferenças entre uma classe abstrata e uma interface, já que o tema é muito importante. Você será questionado sobre a diferença entre esses conceitos em 90% das entrevistas futuras. Portanto, certifique-se de entender o que lê e, se não entender algo completamente, leia fontes adicionais. Portanto, sabemos o que é uma classe abstrata e o que é uma interface. Agora vamos examinar suas diferenças.
  1. Uma interface apenas descreve o comportamento. Ele não tem fortuna. Mas uma classe abstrata tem um estado: descreve ambos.

    Vamos pegar uma classe abstrata Birde uma interface como exemplo Flyable:

    public abstract class Bird {
       private String species;
       private int age;
    
       public abstract void fly();
    
       public String getSpecies() {
           return species;
       }
    
       public void setSpecies(String species) {
           this.species = species;
       }
    
       public int getAge() {
           return age;
       }
    
       public void setAge(int age) {
           this.age = age;
       }
    }

    Vamos criar uma classe bird Mockingjay(mockingjay) e herdar de Bird:

    public class Mockingjay extends Bird {
    
       @Override
       public void fly() {
           System.out.println("Fly, birdie!");
       }
    
       public static void main(String[] args) {
    
           Mockingjay someBird = new Mockingjay();
           someBird.setAge(19);
           System.out.println(someBird.getAge());
       }
    }

    Como você pode ver, podemos acessar facilmente o estado da classe abstrata - suas variáveis species​​(tipo) e age(idade).

    Mas se tentarmos fazer o mesmo com a interface, o quadro será diferente. Podemos tentar adicionar variáveis ​​a ele:

    public interface Flyable {
    
       String species = new String();
       int age = 10;
    
       public void fly();
    }
    
    public interface Flyable {
    
       private String species = new String(); // error
       private int age = 10; // also an error
    
       public void fly();
    }

    Não poderemos nem criar variáveis ​​privadas dentro da interface. Por que? Porque o modificador privado foi criado para ocultar a implementação do usuário. Mas não há implementação dentro da interface: não há nada a esconder ali.

    A interface descreve apenas o comportamento. Conseqüentemente, não seremos capazes de implementar getters e setters dentro da interface. Essa é a natureza de uma interface: ela foi criada para lidar com comportamento, não com estado.

    Java8 introduziu métodos de interface padrão que possuem uma implementação. Você já sabe sobre eles, então não vamos repeti-los.

  2. Uma classe abstrata vincula e combina classes que possuem um relacionamento muito próximo. Ao mesmo tempo, a mesma interface pode ser implementada por classes que não têm nada em comum.

    Voltemos ao nosso exemplo com pássaros.

    Nossa classe abstrata Birdé necessária para criar pássaros baseados nela. Apenas pássaros e mais ninguém! Claro que serão diferentes.

    Diferença entre classes abstratas e interfaces - 2

    Com a interface Flyabletudo é diferente. Descreve apenas o comportamento correspondente ao seu nome - "voar". A definição de “voar”, “capaz de voar” inclui muitos objetos que não estão relacionados entre si.

    Diferença entre classes abstratas e interfaces – 3

    Essas 4 entidades não estão relacionadas de forma alguma. O que posso dizer, nem todos são animados. No entanto, todos eles são Flyablecapazes de voar.

    Não seríamos capazes de descrevê-los usando uma classe abstrata. Eles não possuem um estado comum ou campos idênticos. Para caracterizar uma aeronave provavelmente precisaremos dos campos “modelo”, “ano de fabricação” e “número máximo de passageiros”. Para Carlson, há campos para todos os doces que ele comeu hoje e uma lista de brincadeiras que vai fazer com o Kid. Para um mosquito...uh-uh...nem sabemos... Talvez “nível de aborrecimento”? :)

    O principal é que não podemos descrevê-los usando uma classe abstrata. Eles são muito diferentes. Mas há um comportamento comum: eles podem voar. A interface é ideal para descrever tudo no mundo que pode voar, nadar, pular ou ter algum outro comportamento.

  3. As classes podem implementar quantas interfaces quiserem, mas só podem herdar de uma classe.

    Já falamos sobre isso mais de uma vez. Não existe herança múltipla em Java, mas existe implementação múltipla. Este ponto decorre parcialmente do anterior: uma interface conecta muitas classes diferentes que muitas vezes não têm nada em comum, e uma classe abstrata é criada para um grupo de classes muito próximas umas das outras. Portanto, é lógico que você possa herdar apenas uma dessas classes. Uma classe abstrata descreve o relacionamento “é um”.

Interfaces padrão InputStream e OutputStream

Já passamos pelas diversas classes responsáveis ​​pelo streaming de entrada e saída. Vejamos InputStreame OutputStream. Em geral, não são interfaces, mas verdadeiras classes abstratas. Agora que você sabe o que são, será muito mais fácil trabalhar com eles :) InputStream- esta é uma classe abstrata responsável pela entrada de bytes. Java possui uma série de classes que herdam de InputStream. Cada um deles está configurado para receber dados de diferentes fontes. Por InputStreamser pai, ele fornece vários métodos para trabalhar convenientemente com fluxos de dados. Cada criança tem estes métodos InputStream:
  • int available()retorna o número de bytes disponíveis para leitura;
  • close()fecha a fonte de entrada;
  • int read()retorna uma representação inteira do próximo byte disponível no fluxo. Se o final do fluxo for atingido, o número -1 será retornado;
  • int read(byte[] buffer)tenta ler bytes em um buffer, retornando o número de bytes lidos. Quando chega ao final do arquivo, retorna -1;
  • int read(byte[] buffer, int byteOffset, int byteCount)lê parte de um bloco de bytes. Usado quando existe a possibilidade de o bloco de dados não ter sido completamente preenchido. Ao chegar ao final do arquivo, retorna -1;
  • long skip(long byteCount)skips byteCount, um byte de entrada, retornando o número de bytes ignorados.
Aconselho você a estudar a lista completa de métodos . Na verdade, existem mais de uma dúzia de classes sucessoras. Aqui estão alguns exemplos:
  1. FileInputStream: o tipo mais comum InputStream. Usado para ler informações de um arquivo;
  2. StringBufferInputStream: outro tipo útil InputStream. Transforma uma string em um fluxo de dados de entrada InputStream;
  3. BufferedInputStream: fluxo de entrada em buffer. É mais frequentemente usado para melhorar a eficiência.
Você se lembra quando passamos BufferedReadere dissemos que não precisamos usar? Quando escrevemos:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
... BufferedReadernão há necessidade de usá-lo: InputStreamReaderele fará o trabalho. Mas BufferedReaderfaz isso de forma mais eficiente e, além disso, pode ler dados em linhas inteiras, em vez de caracteres individuais. É tudo BufferedInputStreama mesma coisa! A classe acumula dados de entrada em um buffer especial sem acessar constantemente o dispositivo de entrada. Vejamos um exemplo:
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;

public class BufferedInputExample {

   public static void main(String[] args) throws Exception {
       InputStream inputStream = null;
       BufferedInputStream buffer = null;

       try {

           inputStream = new FileInputStream("D:/Users/UserName/someFile.txt");

           buffer = new BufferedInputStream(inputStream);

           while(buffer.available()>0) {

               char c = (char)buffer.read();

               System.out.println("Character was read" + c);
           }
       } catch(Exception e) {

           e.printStackTrace();

       } finally {

           inputStream.close();
           buffer.close();
       }
   }
}
Neste exemplo, estamos lendo dados de um arquivo localizado no computador no endereço "D:/Users/UserName/someFile.txt" . Criamos 2 objetos - FileInputStreame BufferedInputStreamcomo seu “invólucro”. Depois disso, lemos os bytes do arquivo e os convertemos em caracteres. E assim sucessivamente até o arquivo terminar. Como você pode ver, não há nada complicado aqui. Você pode copiar este código e executá-lo em algum arquivo real armazenado em seu computador :) Uma classe OutputStreamé uma classe abstrata que define uma saída de fluxo de bytes. Como você já entendeu, este é o antípoda de InputStream'a. Ele é responsável não por onde ler os dados, mas por onde enviá-los . Da mesma forma InputStream, esta classe abstrata fornece a todos os descendentes um grupo de métodos para um trabalho conveniente:
  • int close()fecha o fluxo de saída;
  • void flush()limpa todos os buffers de saída;
  • abstract void write (int oneByte)grava 1 byte no fluxo de saída;
  • void write (byte[] buffer)grava uma matriz de bytes no fluxo de saída;
  • void write (byte[] buffer, int offset, int count)grava um intervalo de bytes de contagem da matriz, começando no deslocamento da posição.
Aqui estão alguns dos descendentes da classe OutputStream:
  1. DataOutputStream. Um fluxo de saída que inclui métodos para gravar tipos de dados Java padrão.

    Uma classe muito simples para escrever tipos e strings Java primitivos. Certamente você entenderá o código escrito mesmo sem explicação:

    import java.io.*;
    
    public class DataOutputStreamExample {
    
       public static void main(String[] args) throws IOException {
    
           DataOutputStream dos = new DataOutputStream(new FileOutputStream("testFile.txt"));
    
           dos.writeUTF("SomeString");
           dos.writeInt(22);
           dos.writeDouble(1.21323);
           dos.writeBoolean(true);
    
       }
    }

    Possui métodos separados para cada tipo - writeDouble(), writeLong()e writeShort()assim por diante.

  2. Aula FileOutputStream . Implementa um mecanismo para enviar dados para um arquivo em disco. Aliás, já utilizamos no exemplo anterior, você percebeu? Passamos dentro do DataOutputStream, que funcionou como um “wrapper”.

  3. BufferedOutputStream. Fluxo de saída com buffer. Nada complicado também, a essência é a mesma de BufferedInputStream(ou BufferedReader'a). Em vez da gravação sequencial usual de dados, é usada a gravação através de um buffer especial de “armazenamento”. Ao usar um buffer, você pode reduzir o número de viagens de ida e volta até o destino dos dados e, assim, melhorar a eficiência.

    import java.io.*;
    
    public class DataOutputStreamExample {
    
       public static void main(String[] args) throws IOException {
    
           FileOutputStream outputStream = new FileOutputStream("D:/Users/Username/someFile.txt");
           BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream);
    
           String text = "I love Java!"; // we will convert this string into an array of bytes and write it to a file
    
           byte[] buffer = text.getBytes();
    
           bufferedStream.write(buffer, 0, buffer.length);
           bufferedStream.close();
       }
    }

    Novamente, você mesmo pode “brincar” com esse código e verificar como ele funcionará em arquivos reais do seu computador.

Você também pode ler sobre os herdeiros no material “ Sistema InputStreamde Entrada /Saída ”. Ah , e também teremos uma palestra separada, para que haja informações suficientes sobre eles para um primeiro contato. Isso é tudo! Esperamos que você tenha um bom entendimento das diferenças entre interfaces e classes abstratas e esteja pronto para responder a qualquer pergunta, mesmo que seja complicada :) OutputStreamFileInputStreamFileOutputStreamBufferedInputStream
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION