-
Uma interface apenas descreve o comportamento. Não tem estado. Mas uma classe abstrata inclui estado: ela descreve ambos.
Por exemplo, pegue a
Bird
classe abstrata e aCanFly
interface: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
MockingJay
classe bird e torná-la herdadaBird
:public class MockingJay extends Bird { @Override public void fly() { System.out.println("Fly, bird!"); } 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
e .age
Mas se tentarmos fazer o mesmo com uma interface, a imagem é diferente. Podemos tentar adicionar variáveis a ele:
public interface CanFly { String species = new String(); int age = 10; public void fly(); } public interface CanFly { private String species = new String(); // Error private int age = 10; // Another error public void fly(); }
Não podemos nem declarar variáveis privadas dentro de uma interface. Por que? Porque o modificador privado foi criado para ocultar a implementação do usuário. E uma interface não tem implementação dentro dela: não há nada a esconder.
Uma interface apenas descreve o comportamento. Da mesma forma, não podemos implementar getters e setters dentro de uma interface. Essa é a natureza das interfaces: elas são necessárias para trabalhar com comportamento, não com estado.
O Java 8 introduziu métodos padrão para interfaces que possuem uma implementação. Você já sabe sobre eles, então não vamos nos repetir.
-
Uma classe abstrata conecta e une classes que estão intimamente relacionadas. Ao mesmo tempo, uma única interface pode ser implementada por classes que não têm absolutamente nada em comum.
Voltemos ao nosso exemplo com pássaros.
Nossa
Bird
classe abstrata é necessária para criar pássaros baseados nessa classe. Apenas pássaros e nada mais! Claro, haverá diferentes tipos de pássaros.Com a
CanFly
interface, cada um se dá à sua maneira. Descreve apenas o comportamento (voar) associado ao seu nome. Muitas coisas não relacionadas 'podem voar'.Estas 4 entidades não estão relacionadas entre si. Nem todos estão vivos. No entanto, todos eles
CanFly
.Não poderíamos descrevê-los usando uma classe abstrata. Eles não compartilham o mesmo estado ou campos idênticos. Para definir uma aeronave, provavelmente precisaríamos de campos para o modelo, ano de produção e número máximo de passageiros. Para Carlson, precisaríamos de campos para todos os doces que comeu hoje e uma lista das brincadeiras que fará com seu irmãozinho. Para um mosquito, ...uh... eu nem sei... Talvez, um 'nível de aborrecimento'? :)
A questão é que não podemos usar uma classe abstrata para descrevê-los. Eles são muito diferentes. Mas eles têm comportamento compartilhado: eles podem voar. Uma interface é perfeita para descrever tudo no mundo que pode voar, nadar, pular ou exibir algum outro comportamento.
-
As classes podem implementar quantas interfaces você quiser, mas só podem herdar uma classe.
Já mencionamos isso mais de uma vez. Java não possui herança múltipla de classes, mas suporta herança múltipla de interfaces. Este ponto decorre em parte do anterior: uma interface conecta muitas classes diferentes que geralmente não têm mais nada em comum, enquanto uma classe abstrata é criada para um grupo de classes muito próximas. Portanto, faz sentido que você só possa herdar uma dessas classes. Uma classe abstrata descreve um relacionamento 'é-um'.
Interfaces padrão: InputStream e OutputStream
Já examinamos várias classes responsáveis pelos fluxos de entrada e saída. Vamos considerarInputStream
e OutputStream
. Em geral, essas não são interfaces, mas sim classes abstratas totalmente genuínas. Agora você sabe o que isso significa, então será muito mais fácil trabalhar com eles :) InputStream
é uma classe abstrata responsável pela entrada de bytes. Java tem várias classes que herdam InputStream
. Cada um deles é projetado para receber dados de diferentes fontes. Por InputStream
ser o pai, ele fornece vários métodos que facilitam o trabalho com fluxos de dados. Cada descendente de InputStream
tem estes métodos:
int available()
retorna o número de bytes disponíveis para leitura;close()
fecha o fluxo de entrada;int read()
retorna uma representação inteira do próximo byte disponível no fluxo. Se o fim do fluxo for atingido, -1 será retornado;int read(byte[] buffer)
tenta ler bytes no buffer e retorna o número de bytes lidos. Quando chega ao final do arquivo, retorna -1;int read(byte[] buffer, int byteOffset, int byteCount)
escreve parte de um bloco de bytes. É usado quando a matriz de bytes pode não ter sido totalmente preenchida. Quando chega ao final do arquivo, retorna -1;long skip(long byteCount)
ignora byteCount bytes no fluxo de entrada e retorna o número de bytes ignorados.
FileInputStream
: o tipo mais comum deInputStream
. É usado para ler informações de um arquivo;StringBufferInputStream
: Outro tipo útil de arquivoInputStream
. Ele converte uma string em umInputStream
;BufferedInputStream
: Um fluxo de entrada armazenado em buffer. É usado com mais frequência para aumentar o desempenho.
BufferedReader
e dissemos que você não precisa usá-lo? Quando escrevemos:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
…você não precisa usar BufferedReader
: An InputStreamReader
pode fazer o trabalho. Mas BufferedReader
melhora o desempenho e também pode ler linhas inteiras de dados em vez de caracteres individuais. A mesma coisa se aplica a BufferedInputStream
! A classe acumula dados de entrada em um buffer especial sem acessar constantemente o dispositivo de entrada. Vamos considerar 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 read: " + c);
}
} catch(Exception e) {
e.printStackTrace();
} finally {
inputStream.close();
buffer.close();
}
}
}
Neste exemplo, lemos os dados de um arquivo localizado em um computador em ' D:/Users/UserName/someFile.txt '. Criamos 2 objetos - a FileInputStream
e a BufferedInputStream
que o 'envolve'. Em seguida, lemos os bytes do arquivo e os convertemos em caracteres. E fazemos isso até o final do arquivo. Como você pode ver, não há nada complicado aqui. Você pode copiar este código e executá-lo em um arquivo real em seu computador :) A OutputStream
classe é uma classe abstrata que representa um fluxo de saída de bytes. Como você já sabe, isso é o oposto de um arquivo InputStream
. Ele não é responsável por ler dados de algum lugar, mas sim por enviar dados para algum lugar . Assim como InputStream
, essa classe abstrata fornece a todos os seus descendentes um conjunto de métodos convenientes:
void 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 contagem de bytes de uma matriz, começando na posição de deslocamento.
OutputStream
classe:
-
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 de dados e strings Java primitivos. Você provavelmente entenderá o seguinte código mesmo sem uma 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()
,writeShort()
e assim por diante. FileOutputStream
. Essa classe implementa um mecanismo para enviar dados para um arquivo no disco. A propósito, já o usamos no último exemplo. Você notou? Passamos para DataOutputStream, que atuou como um 'wrapper'.BufferedOutputStream
. Um fluxo de saída em buffer. Também não há nada complicado aqui. Seu propósito é análogo aBufferedInputStream
(ouBufferedReader
). Em vez da leitura sequencial usual de dados, ele grava os dados usando um buffer especial 'cumulativo'. O buffer permite reduzir o número de vezes que o coletor de dados é acessado, aumentando assim o desempenho.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'll convert this string to a byte array and write it to a file byte[] buffer = text.getBytes(); bufferedStream.write(buffer, 0, buffer.length); } }
Novamente, você mesmo pode brincar com esse código e verificar se ele funcionará em arquivos reais em seu computador.
FileInputStream
, FileOutputStream
e BuffreredInputStream
, então isso é informação suficiente para um primeiro contato. É isso! Esperamos que você entenda as diferenças entre interfaces e classes abstratas e esteja pronto para responder a qualquer pergunta, até mesmo perguntas capciosas :)
GO TO FULL VERSION