JavaRush /Java Blog /Random-IT /Differenza tra classi astratte e interfacce

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 vedremo esempi con classi astratte comuni. Differenza tra classi astratte e interfacce - 1Abbiamo dedicato una lezione a parte alle differenze tra una classe astratta e un'interfaccia, poiché l'argomento è molto importante. Ti verrà chiesto della differenza tra questi concetti nel 90% delle interviste future. Pertanto, assicurati di comprendere ciò che leggi e, se non capisci completamente qualcosa, leggi fonti aggiuntive. Quindi sappiamo cos'è una classe astratta e cos'è un'interfaccia. Ora esaminiamo le loro differenze.
  1. Un'interfaccia descrive solo il comportamento. Non ha fortuna. Ma una classe astratta ha uno stato: li descrive entrambi.

    Prendiamo come esempio una classe astratta Birde un'interfaccia 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;
       }
    }

    Creiamo una classe uccello Mockingjay(mockingjay) ed ereditiamo da 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());
       }
    }

    Come puoi vedere, possiamo accedere facilmente allo stato della classe astratta: le sue variabili species(tipo) e age(età).

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

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

    Non potremo nemmeno creare variabili private all'interno dell'interfaccia. Perché? Perché il modificatore private è stato creato per nascondere l'implementazione all'utente. Ma non c'è alcuna implementazione all'interno dell'interfaccia: non c'è nulla da nascondere lì.

    L'interfaccia descrive solo il comportamento. Di conseguenza, non saremo in grado di implementare getter e setter all'interno dell'interfaccia. Questa è la natura di un'interfaccia: è pensata per gestire il comportamento, non lo stato.

    Java8 ha introdotto metodi di interfaccia predefiniti che hanno un'implementazione. Li conosci già, quindi non li ripeteremo.

  2. Una classe astratta collega e combina classi che hanno una relazione molto stretta. Allo stesso tempo, la stessa interfaccia può essere implementata da classi che non hanno nulla in comune.

    Torniamo al nostro esempio con gli uccelli.

    La nostra classe astratta Birdè necessaria per creare uccelli basati su di essa. Solo uccelli e nessun altro! Naturalmente saranno diversi.

    Differenza tra classi astratte e interfacce - 2

    Con l'interfaccia Flyabletutto è diverso. Descrive solo il comportamento corrispondente al suo nome: "volare". La definizione di “volante”, “capace di volare” comprende molti oggetti che non sono correlati tra loro.

    Differenza tra classi astratte e interfacce - 3

    Queste 4 entità non sono collegate tra loro in alcun modo. Cosa posso dire, non tutti sono nemmeno animati. Tuttavia, sono tutti Flyablein grado di volare.

    Мы бы не смогли описать их с помощью абстрактного класса. У них нет общего состояния, одинаковых полей. Whatбы дать характеристику самолету, нам, наверное, понадобятся поля «модель», «год выпуска» и «максимальное количество пассажиров». Для Карлсона — поля для всех сладостей, которые он сегодня съел, и список игр, в которые он будет играть с Малышом. Для комара...э-э-э...даже не знаем… Может, «уровень надоедливости»? :)

    Главное, что с помощью абстрактного класса описать их мы не можем. Они слишком разные. Но есть общее поведение: они могут летать. Интерфейс идеально подойдет для описания всего на свете, что умеет летать, плавать, прыгать or обладает Howим-то другим поведением.

  3. Классы могут реализовывать сколько угодно интерфейсов, но наследоваться можно только от одного класса.

    Об этом мы уже говорor не раз. Множественного наследования в Java нет, а множественная реализация есть. Отчасти этот пункт вытекает из предыдущего: интерфейс связывает между собой множество разных классов, у которых часто нет ничего общего, а абстрактный класс создается для группы очень близких друг другу классов. Поэтому логично, что наследоваться можно только от одного такого класса. Абстрактный класс описывает отношения «is a».

Стандартные интерфейсы InputStream & OutputStream

Мы уже проходor различные классы, отвечающие за потоковый ввод и вывод. Давай рассмотрим InputStream и OutputStream. Вообще, ниHowие это не интерфейсы, а самые настоящие абстрактные классы. Теперь ты знаешь, что это такое, поэтому работать с ними будет намного проще :) InputStream — это абстрактный класс, который отвечает за byteовый ввод. В Java есть серия классов, наследующих InputStream. Каждый из них настроен таким образом, чтобы получать данные из разных источников. Поскольку InputStream является родителем, он предоставляет несколько методов для удобной работы с потоками данных. Эти методы есть у каждого потомка InputStream:
  • int available() возвращает число byte, доступных для чтения;
  • close() закрывает источник ввода;
  • int read() возвращает целочисленное представление следующего доступного byteа в потоке. Если достигнут конец потока, будет возвращено число -1;
  • int read(byte[] buffer) пытается читать byteы в буфер, возвращая количество прочитанных byteов. Когда он достигает конца file, возвращает meaning -1;
  • int read(byte[] buffer, int byteOffset, int byteCount) считывает часть блока byte. Используется, когда есть вероятность, что блок данных был заполнен не целиком. Когда он достигает конца file, возвращает -1;
  • long skip(long byteCount) пропускает byteCount, byte ввода, возвращая количество проигнорированных byteов.
Советую изучить полный перечень методов. Классов-наследников на самом деле больше десятка. Для примера приведем несколько:
  1. FileInputStream: самый распространенный вид InputStream. Используется для чтения информации из file;
  2. StringBufferInputStream: еще один полезный вид InputStream. Он превращает строку во входной поток данных InputStream;
  3. BufferedInputStream: буферизированный входной поток. Чаще всего он используется для повышения эффективности.
Ricordi quando siamo passati BufferedReadere abbiamo detto che non dovevamo usarlo? Quando scriviamo:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
... BufferedReadernon c'è bisogno di usarlo: InputStreamReaderfarà il suo lavoro. Ma BufferedReaderlo fa in modo più efficiente e, inoltre, può leggere i dati in intere righe, anziché in singoli caratteri. Tutto BufferedInputStreamè lo stesso! La classe accumula i dati di input in un buffer speciale senza accedere costantemente al dispositivo di input. Diamo un'occhiata ad 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 was read" + c);
           }
       } catch(Exception e) {

           e.printStackTrace();

       } finally {

           inputStream.close();
           buffer.close();
       }
   }
}
In questo esempio, stiamo leggendo i dati da un file che si trova sul computer all'indirizzo "D:/Users/UserName/someFile.txt" . Creiamo 2 oggetti - FileInputStreame BufferedInputStreamcome il suo "involucro". Successivamente, leggiamo i byte dal file e li convertiamo in caratteri. E così via fino al termine del file. Come puoi vedere, qui non c'è nulla di complicato. Puoi copiare questo codice ed eseguirlo su un file reale memorizzato sul tuo computer :) Una classe OutputStreamè una classe astratta che definisce un output del flusso di byte. Come hai già capito, questo è gli antipodi di InputStream'a. Non è responsabile non da dove leggere i dati, ma da dove inviarli . Ad esempio InputStream, questa classe astratta fornisce a tutti i discendenti un gruppo di metodi per un lavoro conveniente:
  • int 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 dall'array, a partire dall'offset della posizione.
Ecco alcuni dei discendenti della classe OutputStream:
  1. DataOutputStream. Un flusso di output che include metodi per scrivere tipi di dati Java standard.

    Una classe molto semplice per scrivere tipi e stringhe Java primitivi. Sicuramente capirai il codice scritto anche senza spiegazioni:

    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 ciascun tipo: writeDouble(), writeLong()e writeShort()così via.

  2. Classe FileOutputStream . Implementa un meccanismo per l'invio di dati a un file su disco. A proposito, l'abbiamo già usato nell'esempio precedente, hai notato? Lo abbiamo passato all'interno del DataOutputStream, che fungeva da “wrapper”.

  3. BufferedOutputStream. Flusso di output bufferizzato. Niente di complicato, l'essenza è la stessa di BufferedInputStream(o BufferedReader'a). Invece della consueta registrazione sequenziale dei dati, viene utilizzata la registrazione tramite uno speciale buffer di "archiviazione". Utilizzando un buffer è possibile ridurre il numero di viaggi di andata e ritorno verso la destinazione dei dati e quindi migliorare l'efficienza.

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

    Ancora una volta, puoi "giocare" tu stesso con questo codice e verificare come funzionerà su file reali sul tuo computer.

Puoi anche leggere degli eredi nel materiale " Sistema InputStreamdi input / output ". Oh , e avremo anche una conferenza separata, quindi ci sono abbastanza informazioni su di loro per la prima conoscenza. È tutto! Ci auguriamo che tu abbia una buona comprensione delle differenze tra interfacce e classi astratte e che tu sia pronto a rispondere a qualsiasi domanda, anche complicata :) OutputStreamFileInputStreamFileOutputStreamBufferedInputStream
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION