CodeGym/Java Blog/Java Interfaces/The difference between abstract classes and interfaces
Author
Aditi Nawghare
Software Engineer at Siemens

The difference between abstract classes and interfaces

Published in the Java Interfaces group
members
Hi! In this lesson, we'll talk about how abstract classes differ from interfaces and consider some examples with common abstract classes. The difference between abstract classes and interfaces - 1We've devoted a separate lesson to the differences between an abstract class and an interface, because this topic is very important. You'll be asked about the difference between these concepts in 90% of future interviews. That means you should be sure to figure out what you're reading. And if you don't fully understand something, read additional sources. So, we know what an abstract class is and what an interface is. Now we'll go over their differences.
  1. An interface only describes behavior. It has no state. But an abstract class includes state: it describes both.

    For example, take the Bird abstract class and the CanFly 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;
       }
    }

    Let's create a MockingJay bird class and make it inherit 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());
       }
    }

    As you can see, we can easily access the abstract class's state — its species and age variables.

    But if we try to do the same with an interface, the picture is different. We can try to add variables to it:

    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 can't even declare private variables inside an interface. Why? Because the private modifier was created to hide the implementation from the user. And an interface has no implementation inside it: there isn't anything to hide.

    An interface only describes behavior. Accordingly, we can't implement getters and setters inside an interface. This is the nature of interfaces: they are needed to work with behavior, not state.

    Java 8 introduced default methods for interfaces that have an implementation. You already know about them, so we won't repeat ourselves.

  2. An abstract class connects and unites classes that are very closely related. In the same time, a single interface can be implemented by classes that have absolutely nothing in common.

    Let's return to our example with birds.

    Our Bird abstract class is needed for creating birds that are based on that class. Just birds and nothing else! Of course, there will be different kinds of birds.

    The difference between abstract classes and interfaces - 2

    With the CanFly interface, everybody gets on in their own way. It only describes the behavior (flying) associated with its name. Many unrelated things 'can fly'.

    The difference between abstract classes and interfaces - 3

    These 4 entities are not related to each other. They aren't even all living. However, they all CanFly.

    We couldn't describe them using an abstract class. They don't share the same state or identical fields. To define an aircraft, we would probably need fields for the model, production year, and maximum number of passengers. For Carlson, we would need fields for all the sweets he ate today, and a list of the games he'll play with his little brother. For a mosquito, ...uh... I don't even know... Maybe, an 'annoyance level'? :)

    The point is that we can't use an abstract class to describe them. They are too different. But they do have shared behavior: they can fly. An interface is perfect for describing everything in the world that can fly, swim, jump, or exhibit some other behavior.

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

    We've already mentioned this more than once. Java doesn't have multiple inheritance of classes, but it does support multiple inheritance of interfaces. This point follows in part from the previous one: an interface connects many different classes that often have nothing else in common, while an abstract class is created for a group of very closely related classes. Therefore, it makes sense that you can only inherit one such class. An abstract class describes an 'is-a' relationship.

Standard interfaces: InputStream and OutputStream

We've already gone over various classes responsible for input and output streams. Let's consider InputStream and OutputStream. In general, these aren't interfaces at all, but rather entirely genuine abstract classes. Now you know what that means, so it will be much easier to work with them :) InputStream is an abstract class responsible for byte input. Java has several classes that inherit InputStream. Each of them is designed to receive data from different sources. Because InputStream is the parent, it provides several methods that make it easy to work with data streams. Each descendant of InputStream has these methods:
  • int available() returns the number of bytes available for reading;
  • close() closes the input stream;
  • int read() returns an integer representation of the next available byte in the stream. If the end of the stream has been reached, -1 will be returned;
  • int read(byte[] buffer) tries to read bytes into buffer, and returns the number of bytes read. When it reaches the end of the file, it returns -1;
  • int read(byte[] buffer, int byteOffset, int byteCount) writes part of a block of bytes. It is used when the byte array may not have been filled entirely. When it reaches the end of the file, it returns -1;
  • long skip(long byteCount) skips byteCount bytes in the input stream, and returns the number of bytes ignored.
I recommend that you study the complete list of methods. There are actually more than ten child classes. For example, here are a few:
  1. FileInputStream: the most common type of InputStream. It is used to read information from a file;
  2. StringBufferInputStream: Another helpful type of InputStream. It converts a string into an InputStream;
  3. BufferedInputStream: A buffered input stream. It is used most often to increase performance.
Remember when we went over BufferedReader and said that you don't have to use it? When we write:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
…you don't have to use BufferedReader: An InputStreamReader can do the job. But BufferedReader improves the performance and can also read whole lines of data rather than individual characters. The same thing applies to BufferedInputStream! The class accumulates input data in a special buffer without constantly accessing the input device. Let's consider 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 read: " + c);
           }
       } catch(Exception e) {

           e.printStackTrace();

       } finally {

           inputStream.close();
           buffer.close();
       }
   }
}
In this example, we read data from a file located on a computer at 'D:/Users/UserName/someFile.txt'. We create 2 objects — a FileInputStream and a BufferedInputStream that 'wraps' it. Then we read bytes from the file and convert them into characters. And we do that until the file ends. As you can see, there's nothing complicated here. You can copy this code and run it on a real file on your computer :) The OutputStream class is an abstract class that represents an output stream of bytes. As you already know, this is the opposite of an InputStream. It isn't responsible for reading data from somewhere, but rather for sending data somewhere. Like InputStream, this abstract class gives all of its descendants a set of convenient methods:
  • void 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 a byte array to the output stream;
  • void write(byte[] buffer, int offset, int count) writes a range of count bytes from an array, starting at the offset position.
Here are some of the descendants of the OutputStream class:
  1. DataOutputStream. An output stream that includes methods for writing standard Java data types.

    A very simple class for writing primitive Java data types and strings. You'll probably understand the following code even without an 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. FileOutputStream. This class implements a mechanism for sending data to a file on disk. By the way, we already used it in the last example. Did you notice? We passed it to DataOutputStream, which acted as a 'wrapper'.

  3. BufferedOutputStream. A buffered output stream. There's also nothing complicated here. It's purpose is analogous to BufferedInputStream (or BufferedReader). Instead of the usual sequential reading of data, it writes data using a special 'cumulative' buffer. The buffer makes it possible to reduce the number of times the data sink is accessed, thereby increasing performance.

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

    Again, you can play around with this code yourself and verify that it will work on real files on your computer.

We'll have a separate lesson about FileInputStream, FileOutputStream and BuffreredInputStream, so this is enough information for a first acquaintance. That's it! We hope you understand the differences between interfaces and abstract classes and are ready to answer any question, even trick questions :)
Comments (45)
  • Popular
  • New
  • Old
You must be signed in to leave a comment
Stanislav Mayer
Level 15 , Czech Republic
19 February 2022, 04:23
How to confuse your students 101: Standard interfaces: InputStream and OutputStream. In general, these aren't interfaces at all, but rather entirely genuine abstract classes.
Gellert Varga
Level 23 , Szekesfehervar, Hungary
26 April 2022, 19:17
Do you number the hits and this is already the 101st?:)
ImDevin
Level 15 , Old Town, United States
1 June 2021, 02:54
wow, this is what you call - an information overloading!
Gellert Varga
Level 23 , Szekesfehervar, Hungary
12 February 2021, 16:56
1) In the bottommost example in this lesson, correctly this would be the name of the class:
public class BufferedOutputStreamExample { // instead of DataOutputExample
2) To define the path to access a file, both versions of the followings are correct and work well:
. . . new FileOutputStream("C:\\TEST\\out.txt");
. . . new FileOutputStream("C:/TEST/out.txt");
And one more note: If "C:/TEST" exists, but the out.txt doesn't yet, it's no problem, the constructor will create this file. 3) If You will try out the DataOutputExample, you will see only some scrawls in the output file, because these methods write bytes, not normal chars.
Sylwia
Level 16 , Deutschland , Germany
31 January 2021, 07:18
Don't forget to close outputStream and bufferedStream in last example. Otherwise it won't work.
Gellert Varga
Level 23 , Szekesfehervar, Hungary
12 February 2021, 16:59
Yes, they forgot it... and without this the streamed text won't appear in the output file. But the flush() method did this job (force the writing ), too.
Gellert Varga
Level 23 , Szekesfehervar, Hungary
18 January 2021, 17:29
I was not able to understand at all what does this sentence from the lesson mean: "An interface has no state. But an abstract class includes state". I didn't know what does the word "state" exactly mean in Java? And consequently the whole topic got very confused for me. So, i did search for this term, and now i share the outcomes, maybe it helps to somebody to understand this topic better. State of a class means this: all of instance variables of the class = all of nonstatic variables. Behavior of a class means this: all of instance methods of the class = all of nonstatic methods. But an interface can have also fields (= variables), not only methods! Then how is it possible that an interface can not have state?... Answer: Any declared variable inside the interface is static by default! Man doesn't have to write this keyword "static" in the code, but a variable in an interface always becomes static, because this is the default setting by Java. So, the fields in an interface never can be nonstatic (instance). In other words: an interface never can have state.
Sylwia
Level 16 , Deutschland , Germany
24 January 2021, 21:10
Thank you Gellert!
Stanislav Mayer
Level 15 , Czech Republic
17 February 2022, 10:55
Very good explanation, thank you Gellert!
幻梦
Level 16 , 无锡, 中国
25 April 2022, 13:23
Thank you Gellert!!!
micro
Level 16 , China, China
7 June 2023, 02:06
Thank you Gellert!
Ron R
Level 2 , Washington, United States
4 January 2021, 23:21
I was trying to get the last example to run, "3. BufferedOutputStream". For anyone reading this, and especially when you get to the tasks, you have to close the BufferedOutputStream or else it won't write the data to the file. The last example should include a, bufferedStream.close().
Oliver Heintz
Level 18 , Mustang, United States
3 December 2020, 15:06
Who the blazes is Carlson?
Lisa
Level 41
7 August 2021, 09:28
I think Hans refers to Karlsson-on-the-Roof
Onur Bal
Level 27 , Istanbul, Turkey
14 September 2020, 16:19
What happens when two interfaces clash, i.e. they both have a method called "run()", and they both have provided a default, and we do not override it in our class. What happens then?
Gellert Varga
Level 23 , Szekesfehervar, Hungary
26 February 2021, 10:30
It will give you an error, like this: "class Test inherits unrelated defaults for run() from types infce1 and infce2". In this case, to avoid the error, you can do the following for example:
interface infce1 {
        default void run() {
            System.out.println("Run-1!");
        }
}
interface infce2 {
        default void run() {
            System.out.println("Run-2!");
        }
}

class Test implements infce1, infce2 {
        @Override
        public void run() {
           infce2.super.run(); //e.g. for interface2
        }
}

public class MyClass {
    public static void main(String args[]) {
        Test tst = new Test();
        tst.run();
    }
}
Valeri Burlacu
Level 15 , Bamberg, Germany
18 April 2020, 12:24
Hello. I have see and don't understand... public class MockingJay extends Bird { ... public static void main(String[] args) { MockingJay someBird = new MockingJay(); someBird.setAge(19); // in wich class or interface exist the method seAge() System.out.println(someBird.getAge()); } } Or you forget to add setter and getter for the age ? Thanks!
Sela
Level 20 , Poland
15 July 2020, 13:38
setter and getter are omitted here. both must be declared and implemented either in parent or child class
Valeri Burlacu
Level 15 , Bamberg, Germany
18 September 2020, 09:56
Ok. Thank you.
Gellert Varga
Level 23 , Szekesfehervar, Hungary
18 January 2021, 17:19
If age is a private field in the parent class, the setAge() method can only be declared and implemented in the parent class. In the child class this is not possible anymore, because the age variable is private, and other class can not access it directly, not even the child class.
David
Level 29 , Paris, France
12 April 2020, 16:50
Very Good