CodeGym /Java Blog /Random-IT /La differenza tra classi astratte e interfacce
John Squirrels
Livello 41
San Francisco

La differenza tra classi astratte e interfacce

Pubblicato nel gruppo Random-IT
CIAO! In questa lezione parleremo di come le classi astratte differiscono dalle interfacce e prenderemo in considerazione alcuni esempi con classi astratte comuni. La differenza tra classi astratte e interfacce - 1Abbiamo dedicato una lezione a parte alle differenze tra una classe astratta e un'interfaccia, perché questo argomento è molto importante. Ti verrà chiesto della differenza tra questi concetti nel 90% delle interviste future. Ciò significa che dovresti essere sicuro di capire cosa stai leggendo. E se non capisci completamente qualcosa, leggi altre fonti. Quindi, sappiamo cos'è una classe astratta e cos'è un'interfaccia. Ora esamineremo le loro differenze.
  1. Un'interfaccia descrive solo il comportamento. Non ha stato. Ma una classe astratta include lo stato: li descrive entrambi.

    Ad esempio, prendi la Birdclasse astratta e l' CanFlyinterfaccia:

    
    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;
       }
    }
    

    Creiamo una MockingJayclasse bird e facciamola ereditare Bird:

    
    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());
       }
    }
    

    Come puoi vedere, possiamo facilmente accedere allo stato della classe astratta: le sue variabili speciese age.

    Ma se proviamo a fare lo stesso con un'interfaccia, il quadro è diverso. Possiamo provare ad aggiungere variabili ad esso:

    
    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();
    }
    

    Non possiamo nemmeno dichiarare variabili private all'interno di un'interfaccia. Perché? Perché il modificatore private è stato creato per nascondere l'implementazione all'utente. E un'interfaccia non ha alcuna implementazione al suo interno: non c'è niente da nascondere.

    Un'interfaccia descrive solo il comportamento. Di conseguenza, non possiamo implementare getter e setter all'interno di un'interfaccia. Questa è la natura delle interfacce: sono necessarie per lavorare con il comportamento, non con lo stato.

    Java 8 ha introdotto metodi predefiniti per le interfacce che hanno un'implementazione. Li conosci già, quindi non ci ripeteremo.

  2. Una classe astratta collega e unisce classi strettamente correlate. Allo stesso tempo, una singola interfaccia può essere implementata da classi che non hanno assolutamente nulla in comune.

    Torniamo al nostro esempio con gli uccelli.

    La nostra Birdclasse astratta è necessaria per creare uccelli basati su quella classe. Solo uccelli e nient'altro! Naturalmente, ci saranno diversi tipi di uccelli.

    La differenza tra classi astratte e interfacce - 2

    Con l' CanFlyinterfaccia, ognuno se la cava a modo suo. Descrive solo il comportamento (volo) associato al suo nome. Molte cose non correlate "possono volare".

    La differenza tra classi astratte e interfacce - 3

    Queste 4 entità non sono correlate tra loro. Non sono nemmeno tutti viventi. Tuttavia, tutti CanFly.

    Non potremmo descriverli usando una classe astratta. Non condividono lo stesso stato o campi identici. Per definire un aeromobile, probabilmente avremmo bisogno di campi per il modello, l'anno di produzione e il numero massimo di passeggeri. Per Carlson, avremmo bisogno di campi per tutti i dolci che ha mangiato oggi, e una lista dei giochi che farà con il suo fratellino. Per una zanzara, ...uh... non lo so nemmeno... Forse, un 'livello di fastidio'? :)

    Il punto è che non possiamo usare una classe astratta per descriverli. Sono troppo diversi. Ma hanno un comportamento condiviso: possono volare. Un'interfaccia è perfetta per descrivere tutto ciò che al mondo può volare, nuotare, saltare o esibire qualche altro comportamento.

  3. Le classi possono implementare tutte le interfacce che desideri, ma possono ereditare solo una classe.

    Ne abbiamo già parlato più di una volta. Java non ha l'ereditarietà multipla delle classi, ma supporta l'ereditarietà multipla delle interfacce. Questo punto segue in parte il precedente: un'interfaccia collega tante classi diverse che spesso non hanno nient'altro in comune, mentre una classe astratta viene creata per un gruppo di classi molto affini. Pertanto, ha senso che tu possa ereditare solo una di queste classi. Una classe astratta descrive una relazione 'is-a'.

Interfacce standard: InputStream e OutputStream

Abbiamo già esaminato varie classi responsabili dei flussi di input e output. Consideriamo InputStreame OutputStream. In generale, queste non sono affatto interfacce, ma classi astratte del tutto autentiche. Ora sai cosa significa, quindi sarà molto più facile lavorare con loro :) InputStreamè una classe astratta responsabile dell'input dei byte. Java ha diverse classi che ereditano InputStream. Ciascuno di essi è progettato per ricevere dati da fonti diverse. Poiché InputStreamè il genitore, fornisce diversi metodi che semplificano il lavoro con i flussi di dati. Ogni discendente di InputStreamha questi metodi:
  • int available()restituisce il numero di byte disponibili per la lettura;
  • close()chiude il flusso di input;
  • int read()restituisce una rappresentazione intera del successivo byte disponibile nel flusso. Se è stata raggiunta la fine del flusso, verrà restituito -1;
  • int read(byte[] buffer)tenta di leggere i byte nel buffer e restituisce il numero di byte letti. Quando raggiunge la fine del file, restituisce -1;
  • int read(byte[] buffer, int byteOffset, int byteCount)scrive parte di un blocco di byte. Viene utilizzato quando l'array di byte potrebbe non essere stato riempito interamente. Quando raggiunge la fine del file, restituisce -1;
  • long skip(long byteCount)ignora byteCount byte nel flusso di input e restituisce il numero di byte ignorati.
Ti consiglio di studiare l'elenco completo dei metodi . In realtà ci sono più di dieci classi per bambini. Ad esempio, eccone alcuni:
  1. FileInputStream: il tipo più comune di InputStream. Viene utilizzato per leggere informazioni da un file;
  2. StringBufferInputStream: Un altro utile tipo di InputStream. Converte una stringa in un InputStream;
  3. BufferedInputStream: un flusso di input bufferizzato. Viene utilizzato più spesso per aumentare le prestazioni.
Ricordi quando siamo andati da noi BufferedReadere abbiamo detto che non dovevi usarlo? Quando scriviamo:

BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
…non devi usare BufferedReader: An InputStreamReaderpuò fare il lavoro. Ma BufferedReadermigliora le prestazioni e può anche leggere intere righe di dati piuttosto che singoli caratteri. La stessa cosa vale per BufferedInputStream! La classe accumula i dati di input in un buffer speciale senza accedere costantemente al dispositivo di input. Consideriamo un esempio:

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();
       }
   }
}
In questo esempio, leggiamo i dati da un file che si trova su un computer in ' D:/Users/UserName/someFile.txt '. Creiamo 2 oggetti: a FileInputStreame a BufferedInputStreamche lo "avvolgono". Quindi leggiamo i byte dal file e li convertiamo in caratteri. E lo facciamo finché il file non finisce. Come puoi vedere, non c'è niente di complicato qui. Puoi copiare questo codice ed eseguirlo su un file reale sul tuo computer :) La OutputStreamclasse è una classe astratta che rappresenta un flusso di output di byte. Come già sai, questo è l'opposto di un file InputStream. Non è responsabile della lettura dei dati da qualche parte, ma piuttosto dell'invio dei dati da qualche parte . Come InputStream, questa classe astratta fornisce a tutti i suoi discendenti una serie di metodi convenienti:
  • void close()chiude il flusso di output;
  • void flush()cancella tutti i buffer di output;
  • abstract void write(int oneByte)scrive 1 byte nel flusso di output;
  • void write(byte[] buffer)scrive un array di byte nel flusso di output;
  • void write(byte[] buffer, int offset, int count)scrive un intervallo di count byte da una matrice, a partire dalla posizione di offset.
Ecco alcuni dei discendenti della OutputStreamclasse:
  1. DataOutputStream. Un flusso di output che include metodi per la scrittura di tipi di dati Java standard.

    Una classe molto semplice per scrivere stringhe e tipi di dati Java primitivi. Probabilmente capirai il seguente codice anche senza una spiegazione:

    
    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);
    
       }
    }
    

    Ha metodi separati per ogni tipo — writeDouble(), writeLong(), writeShort()e così via.


  2. FileOutputStream. Questa classe implementa un meccanismo per l'invio di dati a un file su disco. A proposito, l'abbiamo già usato nell'ultimo esempio. Hai notato? Lo abbiamo passato a DataOutputStream, che ha agito da "wrapper".

  3. BufferedOutputStream. Un flusso di output bufferizzato. Anche qui non c'è niente di complicato. Il suo scopo è analogo a BufferedInputStream(o BufferedReader). Invece della solita lettura sequenziale dei dati, scrive i dati utilizzando uno speciale buffer "cumulativo". Il buffer consente di ridurre il numero di accessi al data sink, aumentando così le prestazioni.

    
    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);
         }
    }
    

    Ancora una volta, puoi giocare tu stesso con questo codice e verificare che funzioni su file reali sul tuo computer.

Avremo una lezione separata su FileInputStream, FileOutputStreame BuffreredInputStream, quindi queste sono informazioni sufficienti per una prima conoscenza. Questo è tutto! Ci auguriamo che tu capisca le differenze tra interfacce e classi astratte e che tu sia pronto a rispondere a qualsiasi domanda, anche alle domande trabocchetto :)
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION