JavaRush /Java Blog /Random EN /Difference between abstract classes and interfaces

Difference between abstract classes and interfaces

Published in the Random EN group
Hello! In this lecture we will talk about how abstract classes differ from interfaces and look at examples with common abstract classes. Difference between abstract classes and interfaces - 1We devoted a separate lecture to the differences between an abstract class and an interface, since the topic is very important. You will be asked about the difference between these concepts in 90% of future interviews. Therefore, be sure to understand what you read, and if you do not fully understand something, read additional sources. So, we know what an abstract class is and what an interface is. Now let's go through their differences.
  1. An interface only describes behavior. He has no fortune. But an abstract class has a state: it describes both.

    Let's take an abstract class Birdand interface as an example 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;
       }
    }

    Let's create a bird class Mockingjay(mockingjay) and inherit from 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());
       }
    }

    As you can see, we can easily access the state of the abstract class - its variables species(type) and age(age).

    But if we try to do the same with the interface, the picture will be different. We can try adding variables to it:

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

    We won't even be able to create private variables inside the interface. Why? Because the private modifier was created to hide the implementation from the user. But there is no implementation inside the interface: there is nothing to hide there.

    The interface only describes the behavior. Accordingly, we will not be able to implement getters and setters inside the interface. That's the nature of an interface: it's meant to deal with behavior, not state.

    Java8 introduced default interface methods that have an implementation. You already know about them, so we won’t repeat them.

  2. An abstract class links and combines classes that have a very close relationship. At the same time, the same interface can be implemented by classes that have nothing in common at all.

    Let's return to our example with birds.

    Our abstract class Birdis needed to create birds based on it. Only birds and no one else! Of course they will be different.

    Difference between abstract classes and interfaces - 2

    With the interface Flyableeverything is different. It only describes the behavior corresponding to its name - "flying". The definition of “flying”, “capable of flying” includes many objects that are not related to each other.

    Difference between abstract classes and interfaces - 3

    These 4 entities are not related to each other in any way. What can I say, not all of them are even animate. However, they are all Flyablecapable of flight.

    We wouldn't be able to describe them using an abstract class. They do not have a common state or identical fields. To characterize an aircraft, we will probably need the fields “model”, “year of manufacture” and “maximum number of passengers”. For Carlson, there are fields for all the sweets that he ate today, and a list of games that he will play with the Kid. For a mosquito...uh-uh...we don’t even know... Maybe “annoyance level”? :)

    The main thing is that we cannot describe them using an abstract class. They are too different. But there is a common behavior: they can fly. The interface is ideal for describing everything in the world that can fly, swim, jump, or have some other behavior.

  3. Classes can implement as many interfaces as they want, but they can only inherit from one class.

    We have already talked about this more than once. There is no multiple inheritance in Java, but there is multiple implementation. This point partly follows from the previous one: an interface connects many different classes that often have nothing in common, and an abstract class is created for a group of classes that are very close to each other. Therefore, it is logical that you can inherit from only one such class. An abstract class describes the “is a” relationship.

Standard InputStream & OutputStream Interfaces

We have already gone through the various classes responsible for streaming input and output. Let's look at InputStreamand OutputStream. In general, these are not interfaces, but real abstract classes. Now you know what they are, so working with them will be much easier :) InputStream- this is an abstract class that is responsible for byte input. Java has a series of classes that inherit from InputStream. Each of them is configured to receive data from different sources. Because InputStreamit is a parent, it provides several methods for conveniently working with data streams. Each child has these methods InputStream:
  • int available()returns the number of bytes available for reading;
  • close()closes the input source;
  • int read()returns an integer representation of the next available byte in the stream. If the end of the stream is reached, the number -1 will be returned;
  • int read(byte[] buffer)attempts to read bytes into a buffer, returning the number of bytes read. When it reaches the end of the file, it returns -1;
  • int read(byte[] buffer, int byteOffset, int byteCount)reads part of a block of bytes. Used when there is a possibility that the data block was not completely filled. When it reaches the end of the file, returns -1;
  • long skip(long byteCount)skips byteCount, a byte of input, returning the number of bytes ignored.
I advise you to study the full list of methods . There are actually more than a dozen successor classes. Here are a few examples:
  1. FileInputStream: the most common type InputStream. Used to read information from a file;
  2. StringBufferInputStream: another useful type InputStream. It turns a string into an input data stream InputStream;
  3. BufferedInputStream: buffered input stream. It is most often used to improve efficiency.
Do you remember when we walked by BufferedReaderand said that we don’t have to use it? When we write:

BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
... BufferedReaderno need to use it: InputStreamReaderit will do the job. But BufferedReaderit does it more efficiently and, moreover, can read data in entire lines, rather than individual characters. Everything BufferedInputStreamis the same! The class accumulates input data in a special buffer without constantly accessing the input device. Let's look at an example:

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 this example, we are reading data from a file that is located on the computer at the address "D:/Users/UserName/someFile.txt" . We create 2 objects - FileInputStreamand BufferedInputStreamas its “wrapper”. After that, we read the bytes from the file and convert them into characters. And so on until the file ends. As you can see, there is nothing complicated here. You can copy this code and run it on some real file that is stored on your computer :) A class OutputStreamis an abstract class that defines a byte stream output. As you already understand, this is the antipode of InputStream'a. It is responsible not for where to read data from, but for where to send it . Like InputStream, this abstract class provides all descendants with a group of methods for convenient work:
  • int close()closes the output stream;
  • void flush()clears all output buffers;
  • abstract void write (int oneByte)writes 1 byte to the output stream;
  • void write (byte[] buffer)writes an array of bytes to the output stream;
  • void write (byte[] buffer, int offset, int count)writes a range of count bytes from the array, starting at position offset.
Here are some of the class's descendants OutputStream:
  1. DataOutputStream. An output stream that includes methods for writing standard Java data types.

    A very simple class for writing primitive Java types and strings. Surely you will understand the written code even without explanation:

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

    It has separate methods for each type - writeDouble(), writeLong(), writeShort()and so on.

  2. Class FileOutputStream . Implements a mechanism for sending data to a file on disk. By the way, we already used it in the previous example, did you notice? We passed it inside the DataOutputStream, which acted as a “wrapper”.

  3. BufferedOutputStream. Buffered output stream. Nothing complicated either, the essence is the same as in BufferedInputStream(or BufferedReader'a). Instead of the usual sequential data recording, recording through a special “storage” buffer is used. By using a buffer, you can reduce the number of round trips to the data destination and thereby improve efficiency.

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

    Again, you can “play” with this code yourself and check how it will work on real files on your computer.

You can also read about the heirs in the material “ InputStreamInput /Output System ”. Oh , and we will also have a separate lecture, so there is enough information about them for the first acquaintance. That's all! We hope you have a good understanding of the differences between interfaces and abstract classes and are ready to answer any question, even a tricky one :) OutputStreamFileInputStreamFileOutputStreamBufferedInputStream
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION