CodeGym /Java Blog /Willekeurig /Het verschil tussen abstracte klassen en interfaces
John Squirrels
Niveau 41
San Francisco

Het verschil tussen abstracte klassen en interfaces

Gepubliceerd in de groep Willekeurig
Hoi! In deze les bespreken we hoe abstracte klassen verschillen van interfaces en bekijken we enkele voorbeelden met veelvoorkomende abstracte klassen. Het verschil tussen abstracte klassen en interfaces - 1We hebben een aparte les gewijd aan de verschillen tussen een abstracte klasse en een interface, omdat dit onderwerp erg belangrijk is. In 90% van de toekomstige interviews wordt u gevraagd naar het verschil tussen deze concepten. Dat betekent dat je er zeker van moet zijn dat je erachter komt wat je leest. En als je iets niet helemaal begrijpt, lees dan aanvullende bronnen. We weten dus wat een abstracte klasse is en wat een interface is. Nu zullen we hun verschillen bespreken.
  1. Een interface beschrijft alleen gedrag. Het heeft geen staat. Maar een abstracte klasse omvat staat: het beschrijft beide.

    Neem bijvoorbeeld de Birdabstracte klasse en de CanFlyinterface:

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

    Laten we een MockingJayvogelklasse maken en deze erven 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());
       }
    }
    

    Zoals je kunt zien, hebben we gemakkelijk toegang tot de status van de abstracte klasse - zijn speciesen agevariabelen.

    Maar als we hetzelfde proberen te doen met een interface, ziet het plaatje er anders uit. We kunnen proberen er variabelen aan toe te voegen:

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

    We kunnen zelfs geen privévariabelen in een interface declareren. Waarom? Omdat de privémodifier is gemaakt om de implementatie voor de gebruiker te verbergen. En een interface heeft geen implementatie erin: er is niets te verbergen.

    Een interface beschrijft alleen gedrag. Daarom kunnen we geen getters en setters in een interface implementeren. Dit is de aard van interfaces: ze zijn nodig om met gedrag te werken, niet met staten.

    Java 8 introduceerde standaardmethoden voor interfaces die een implementatie hebben. Je kent ze al, dus we zullen onszelf niet herhalen.

  2. Een abstracte klasse verbindt en verenigt klassen die zeer nauw verwant zijn. Tegelijkertijd kan een enkele interface worden geïmplementeerd door klassen die absoluut niets gemeen hebben.

    Laten we terugkeren naar ons voorbeeld met vogels.

    Onze Birdabstracte klasse is nodig om vogels te maken die op die klasse zijn gebaseerd. Alleen vogels en niets anders! Natuurlijk zullen er verschillende soorten vogels zijn.

    Het verschil tussen abstracte klassen en interfaces - 2

    Met de CanFlyinterface gaat iedereen op zijn eigen manier verder. Het beschrijft alleen het gedrag (vliegen) dat bij zijn naam hoort. Veel niet-gerelateerde dingen 'kunnen vliegen'.

    Het verschil tussen abstracte klassen en interfaces - 3

    Deze 4 entiteiten zijn niet aan elkaar gerelateerd. Ze leven niet eens allemaal. Ze zijn echter allemaal CanFly.

    We konden ze niet beschrijven met behulp van een abstracte klasse. Ze delen niet dezelfde status of identieke velden. Om een ​​vliegtuig te definiëren, hebben we waarschijnlijk velden nodig voor het model, het productiejaar en het maximum aantal passagiers. Voor Carlson zouden we velden nodig hebben voor alle snoepjes die hij vandaag at, en een lijst van de spelletjes die hij met zijn kleine broertje gaat spelen. Voor een mug, ...uh... ik weet het niet eens... Misschien een 'ergernisniveau'? :)

    Het punt is dat we geen abstracte klasse kunnen gebruiken om ze te beschrijven. Ze zijn te verschillend. Maar ze hebben wel een gedeeld gedrag: ze kunnen vliegen. Een interface is perfect om alles in de wereld te beschrijven dat kan vliegen, zwemmen, springen of ander gedrag vertonen.

  3. Klassen kunnen zoveel interfaces implementeren als u wilt, maar ze kunnen slechts één klasse erven.

    We hebben dit al meer dan eens genoemd. Java heeft geen meervoudige overerving van klassen, maar ondersteunt wel meervoudige overerving van interfaces. Dit punt volgt gedeeltelijk uit het vorige: een interface verbindt veel verschillende klassen die vaak verder niets gemeen hebben, terwijl een abstracte klasse wordt gemaakt voor een groep zeer nauw verwante klassen. Daarom is het logisch dat je maar één zo'n klasse kunt erven. Een abstracte klasse beschrijft een 'is-een'-relatie.

Standaardinterfaces: InputStream en OutputStream

We hebben al verschillende klassen besproken die verantwoordelijk zijn voor invoer- en uitvoerstromen. Laten we eens kijken naar InputStreamen OutputStream. Over het algemeen zijn dit helemaal geen interfaces, maar eerder volledig echte abstracte klassen. Nu weet je wat dat betekent, dus het zal veel gemakkelijker zijn om ermee te werken :) InputStreamis een abstracte klasse die verantwoordelijk is voor byte-invoer. Java heeft verschillende klassen die InputStream. Elk van hen is ontworpen om gegevens uit verschillende bronnen te ontvangen. Omdat InputStreamhet de ouder is, biedt het verschillende methoden die het gemakkelijk maken om met gegevensstromen te werken. Elke afstammeling van InputStreamheeft deze methoden:
  • int available()geeft het aantal bytes terug dat beschikbaar is om te lezen;
  • close()sluit de invoerstroom;
  • int read()retourneert een gehele representatie van de volgende beschikbare byte in de stream. Als het einde van de stream is bereikt, wordt -1 geretourneerd;
  • int read(byte[] buffer)probeert bytes in de buffer te lezen en retourneert het aantal gelezen bytes. Wanneer het het einde van het bestand bereikt, retourneert het -1;
  • int read(byte[] buffer, int byteOffset, int byteCount)schrijft een deel van een bytesblok. Het wordt gebruikt wanneer de byte-array mogelijk niet volledig is gevuld. Wanneer het het einde van het bestand bereikt, retourneert het -1;
  • long skip(long byteCount)slaat byteCount bytes over in de invoerstroom en retourneert het aantal genegeerde bytes.
Ik raad u aan de volledige lijst met methoden te bestuderen . Er zijn zelfs meer dan tien kinderklassen. Hier zijn er bijvoorbeeld een paar:
  1. FileInputStream: de meest voorkomende vorm van InputStream. Het wordt gebruikt om informatie uit een bestand te lezen;
  2. StringBufferInputStream: Nog een nuttig type InputStream. Het zet een string om in een InputStream;
  3. BufferedInputStream: Een gebufferde invoerstroom. Het wordt het vaakst gebruikt om de prestaties te verbeteren.
Weet je nog dat we erheen gingen BufferedReaderen zeiden dat je het niet hoeft te gebruiken? Als we schrijven:

BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
…u hoeft niet te gebruiken BufferedReader: An InputStreamReaderkan het werk doen. Maar BufferedReaderverbetert de prestaties en kan ook hele regels gegevens lezen in plaats van individuele tekens. Hetzelfde geldt voor BufferedInputStream! De klasse verzamelt invoergegevens in een speciale buffer zonder voortdurend toegang te krijgen tot het invoerapparaat. Laten we een voorbeeld bekijken:

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 dit voorbeeld lezen we gegevens uit een bestand dat zich op een computer bevindt op ' D:/Users/UserName/someFile.txt '. We maken 2 objecten - een FileInputStreamen een BufferedInputStreamdie het 'omhult'. Vervolgens lezen we bytes uit het bestand en zetten deze om in tekens. En dat doen we tot het dossier eindigt. Zoals je kunt zien, is hier niets ingewikkelds. U kunt deze code kopiëren en uitvoeren op een echt bestand op uw computer :) De OutputStreamklasse is een abstracte klasse die een uitvoerstroom van bytes vertegenwoordigt. Zoals je al weet, is dit het tegenovergestelde van een InputStream. Het is niet verantwoordelijk voor het ergens vandaan lezen van gegevens, maar eerder voor het ergens naartoe sturen van gegevens . Zoals InputStream, deze abstracte klasse geeft al zijn afstammelingen een reeks handige methoden:
  • void close()sluit de uitvoerstroom;
  • void flush()wist alle uitvoerbuffers;
  • abstract void write(int oneByte)schrijft 1 byte naar de uitvoerstroom;
  • void write(byte[] buffer)schrijft een byte-array naar de uitvoerstroom;
  • void write(byte[] buffer, int offset, int count)schrijft een reeks telbytes uit een array, beginnend bij de offsetpositie.
Hier zijn enkele van de afstammelingen van de OutputStreamklas:
  1. DataOutputStream. Een uitvoerstroom die methoden bevat voor het schrijven van standaard Java-gegevenstypen.

    Een zeer eenvoudige klasse voor het schrijven van primitieve Java-gegevenstypen en strings. U zult waarschijnlijk de volgende code begrijpen, zelfs zonder uitleg:

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

    Het heeft aparte methoden voor elk type — writeDouble(), writeLong(), writeShort(), enzovoort.


  2. FileOutputStream. Deze klasse implementeert een mechanisme voor het verzenden van gegevens naar een bestand op schijf. We gebruikten het trouwens al in het laatste voorbeeld. Is het je opgevallen? We gaven het door aan DataOutputStream, dat fungeerde als een 'wrapper'.

  3. BufferedOutputStream. Een gebufferde uitvoerstroom. Er is hier ook niets ingewikkelds. Het doel is analoog aan BufferedInputStream(of BufferedReader). In plaats van het gebruikelijke sequentiële lezen van gegevens, schrijft het gegevens met behulp van een speciale 'cumulatieve' buffer. De buffer maakt het mogelijk om het aantal keren dat de data-sink wordt geopend te verminderen, waardoor de prestaties toenemen.

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

    Nogmaals, u kunt zelf met deze code spelen en controleren of deze werkt op echte bestanden op uw computer.

We hebben een aparte les over FileInputStream, FileOutputStreamen BuffreredInputStream, dus dit is genoeg informatie voor een eerste kennismaking. Dat is het! We hopen dat je de verschillen tussen interfaces en abstracte klassen begrijpt en klaar bent om elke vraag te beantwoorden, zelfs strikvragen :)
Opmerkingen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION