Object-Oriented Programming (OOP) is one of the most widely used paradigms in programming. One of its key principles is abstraction in Java, which is all about hiding complex implementation details and exposing only the necessary parts to the user. In simple terms, abstraction helps us focus on what an object does, rather than how it does it.
In this blog, we’ll focus on understanding abstraction in Java. We’ll not only explore the concept through Java code examples but also use real-life analogies and diagrams to make it more engaging and easier to grasp.
So, let’s start…
Index
- What is Abstraction in Java?
- Ways of Achieving Abstraction in Java.
- Abstract Class in Java
- Understanding Abstract Class with the help of a code
- Abstract Method
- Difference between Abstract Method and Concrete Method
- Interface
- Important Points about Interface
- Nested Interface
- Difference between Abstract class and interface
- Similarity between Abstract Class and Interface
- When to use Abstract Class VS When to use interface
- Advantages of Abstraction in Java
- Disadvantages of Abstraction in Java
- Conclusion
- Abstraction in Java FAQ's
What is Abstraction in Java?
As told above, abstraction is one of the four pillars of OOP, which is used to hide complex details while displaying the ones that are easier to understand. Developers use it to hide all the functionality or the complex stuff which are irrelevant from the user’s point of view while showing only the essential features of an object to the outside world. Let’s understand this with the help of an example –
We all like watching TV while sitting on couches, sofas, etc, taking the remote and switching on the TV, changing channels to find our favorite shows, increasing or decreasing the volume or Binge watching and all these features are at our disposal, it is just a matter of clicking the buttons on the remote. But how?
Well, the answer is rather simple, the internal structure of a remote is not as simple as its exterior (as shown in the above image), a series of things happen when we press a button. Let’s understand them one by one, but only briefly.
Signal Transmission: The remote sends an infrared signal to turn the TV on.
Signal Reception: The television has a receiver that catches this signal.
Processing the Signal: The TV processes this signal and responds by powering it up.
This entire process involves complex electronic principles and programming inside both the remote and the TV, yet all we need to know is which buttons to press for which action. So, with this principle in mind, we have abstracted or are unaware of all the complex things that occur inside both the TV and the remote, as these details are not necessary for us to know.
Similarly, in programming, when we use a class or a method in Java, we don’t need to understand the details of how the process is implemented. Just like using the remote, we only need to know what functions are available and how to call them. This simplifies our role as programmers because we can use code written by others without needing to understand every detail about how it works—just what it does is enough.
Ways of Achieving Abstraction in Java.
There are two ways of achieving abstraction in Java: they are
- Abstract Classes
- Interfaces.
Let’s understand each of them
Abstract Class in Java
An abstract class is a class that is declared using the abstract keyword or has one or more abstract methods. This abstract keyword is the modifier and can be used with classes and methods, but not variables. It may or may not contain all the abstract methods (methods without a body), as some of them may be concrete methods (methods with a body).
The purpose of an abstract class is to serve as the blueprint for other classes. It allows us to define a common structure and behaviour that can be shared among multiple subclasses while enforcing certain rules and guidelines. Though it has a default constructor, it can also have a parameterized constructor.
Syntax
abstract class ClassName {
// Regular (concrete) method
returnType anotherMethod() {
// Method body
}
}
// Subclass that extends the abstract class
class SubClassName extends ClassName {
// Must provide implementation for all abstract methods
@Override
returnType methodName() {
// Method body
}
}
Key points to remember
- Abstract class needs the abstract keyword to get initiated
- It can have both abstract and concrete methods
- They cannot be instantiated, but they can be subclassed.
- When a class extends an abstract class, it must either implement all the abstract methods of the parent class or be declared as an abstract class.
- When an abstract class is subclassed, it usually implements all of its parent class’s abstract methods. But if it fails to do so must also be declared abstract.
- If a class contains an abstract method, it by default becomes an abstract class.
- It can have parameterized constructors, though the default constructors are always present.
- Abstract classes allow you to define common behaviour and properties that subclasses can inherit and specialise.
- Abstract Class Can Have Constructors, Variables, and Static Blocks
- They can extend only one class in either public, private or protected concrete methods.
- Abstract classes can implement multiple interfaces in Java.
- We can declare fields that are neither static nor final
- A class can be both extended and implemented at the same time
class ClassName extends SuperClassName implements InterfaceName {
// class body (fields, constructors, methods, etc.)
}
Understanding Abstract Class with the help of a code
Let’s understand abstract class with the help of a code.
// Abstract class
abstract class Animal {
// Abstract method (no body)
abstract void makeSound();
// Concrete method
void sleep() {
System.out.println("Zzz");
}
}
// Subclass
class Dog extends Animal {
// Implementation of abstract method
@Override
void makeSound() {
System.out.println("Woof");
}
}
// Main class
class Main {
public static void main(String[] args) {
// Animal a = new Animal() ❌ This will not work,
Dog dog = new Dog(); // ✅ This will work
dog.makeSound(); // Outputs "Woof"
dog.sleep(); // Outputs "Zzz"
}
}
Output
In the above code, Animal is an abstract class with one abstract method makeSound and one concrete method sleep. The Dog class is an extended class of Animal that implements the makeSound method.
Let’s break the above code into three parts and understand each of them –
- Abstract Class
- Subclass
- Main method
Abstract Class
abstract class Animal {
// Abstract method (no body)
abstract void makeSound();
// Concrete method
void sleep() {
System.out.println("Zzz");
}
}
The above code is just the abstract class part of the whole code, meaning that part of the code where we have initialized the abstract class using the abstract keyword, as without it, the abstract class cannot be initialized.
Let’s understand the above code step by step.
- abstract class Animal: declares an abstract class named Animal using the abstract keyword.
- abstract void make Sound();: This is an abstract method. It has nobody (no implementation). Any class that extends Animal must provide an implementation for this method. Abstract methods are like contracts that subclasses must fulfil. We will understand them in detail later in the blog.
- void sleep(): This is a concrete method with an implementation. It prints “Zzz” when called. Subclasses can use this method as it is or override it if they need different behaviour.
Subclass
class Dog extends Animal {
// Implementation of abstract method
@Override
void makeSound() {
System.out.println("Woof");
}
}
Similar to the Abstract Class part, we will also understand this section step by step
- class Dog extends Animal: This declares a subclass named Dog that extends the abstract class Animal. Since Animal is abstract and has an abstract method, Dog must implement the makeSound method.
- void makeSound(): This provides the implementation for the makeSound method declared in Animal. When makeSound is called on a Dog object, it prints “Woof”.
Main Method
class Main {
public static void main(String[] args) {
// Animal a = new Animal() ❌ This will not work,
Dog dog = new Dog(); // ✅ This will work
dog.makeSound(); // Outputs "Woof"
dog.sleep(); // Outputs "Zzz"
}
}
- class Main: This is a class containing the main method, which is the entry point of the program.
- public static void main(String[] args): This is the main method where the program execution starts.
- Dog dog = new Dog();: This creates a new instance of the Dog class. Even though Animal is abstract and cannot be instantiated, Dog is a concrete class and can be instantiated.
- dog.makeSound();: This calls the makeSound method on the dog object. Since Dog provides an implementation for makeSound, it prints “Woof”.
- dog.sleep();: This calls the sleep method on the dog object. The sleep method is inherited from the Animal class and prints “Zzz”.
Now that we have understood each part of the code separately, it is time to understand the code as a whole –
First things first, Abstraction helps developers in many ways. It not only reduces code complexity, but it also increases code re-usability, maintainability and flexibility, and we will see how with this example.
In the above code, we created an abstract class Animal
using the abstract
keyword. This class has two methods:
makeSound()
is an abstract method, which means it has no code inside it.sleep()
is a regular method, which has some code (it prints “Zzz”).
We cannot create an object of the Animal
class because it’s abstract, and abstract classes cannot be instantiated. We will get a compilation error saying that the Animal class is abstract.
Instead, we must create another class that extends it. That’s where the Dog
class comes in. It extends the Animal
class and gives its own version of the makeSound()
method. This is required because abstract methods must be completed by a subclass. The sleep()
method is already complete, so the Dog
class automatically inherits it and can use it as it is.
The main()
method is where the program starts. In this method, we create an object of the Dog
class and call its makeSound()
and sleep()
methods.
makeSound()
prints “Woof”, which shows the behaviour defined in theDog
class.sleep()
prints “Zzz”, which comes from theAnimal
class.
Which gives us our output. “This clarifies that an abstract class is just a template that defines the structure, while other classes fill in the missing details. It helps make the code more organized and reusable.
An Alternative Approach
This approach is rather creative; what we are doing is creating an object of the subclass and storing its reference in a variable of the abstract class type. This approach gives us the power of abstraction and polymorphism. This is a common and powerful feature used in runtime polymorphism.
So, in the above code, let’s replace
Dog a = new Dog();
with
Animal a = new Dog();
Let’s understand the above line
- Animal is the abstract class
- a is the reference variable
- new is the keyword used for creating an object
- Dog() is the class whose object will be created
And when they are combined, the line Animal a = new Dog(;
means that we are creating an object of the Dog
class and storing its reference in a variable a
that is declared as type Animal
. This allows us to use the features of runtime polymorphism, where the object behaves according to its actual type (Dog
), even though the reference is of the parent type (Animal
).
In simple words, at runtime, the method that gets executed depends on the object type, not the reference type. This approach helps in writing clean, flexible, and reusable code by focusing on what the object is expected to do (as defined in the abstract class Animal
) rather than how it does it (which is written in the Dog
class).
It also makes the code easier to change in the future, as we can switch to a different subclass, like Cat
or Lion
, with minimal modifications to our existing code.
Now the new code becomes
public class Test {
public static void main(String[] args) {
Animal a = new Dog();
a.sleep();
a.makeSound();
}
}
abstract class Animal {
// Abstract method (no body)
abstract void makeSound();
// Regular method
void sleep() {
System.out.println("Sleeping...");
}
}
// Subclass (must override abstract method)
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Bark!");
}
}
Output
Abstract Method
Methods that have been declared with the abstract keyword are called abstract methods. These methods –
- Do not have a body
- Can only exist inside an abstract class
- Cannot be instantiated directly.
- Act as a placeholder that must be completed by a subclass.
- Contain name, return type and parameters.
- Enforces a specific behaviour on all classes
- Must be overridden, thus cannot be static or final
Syntax
abstract class ClassName {
abstract returnType methodName(parameters);
}
See in the above example of the abstract class, we have defined and used an abstract method – makeSound(). And to print its value, it has to be overridden by a different value inside the subclass. Later, this value was printed by the object of the same subclass, as we cannot create an object of an abstract class.
In the above code, we have seen how we can use an abstract class that uses both abstract and concrete methods to extend its reach or what it can do. Like the Animal class extends itself to the Dog subclass, which makes the Bark sound when the makeSound is triggered and Zzzz when sleep is triggered.
Difference between Abstract Method and Concrete Method
As we’ve already discussed, an abstract class in Java can contain both abstract and concrete methods. Abstract methods are those that do not have a body, while concrete methods are fully implemented with a body. But is that the only difference between them? The answer is no. There are several other key differences between abstract and concrete methods, which we’ll explore next.
Abstract Methods | Concrete Methods |
Declared without a body | Declared with a body |
Must be overridden by a concrete subclass | Can be inherited by a subclass |
Used to enforce a contract that a subclass must follow | Provides a common behaviour that can be shared across all subclasses |
Must be implemented by any subclass of an abstract class | These can be inherited and used directly by sub classes. |
Abstract Methods defines What to do and Abstract Classes defines What to do and how to do it (optional), but the actual work is done by the subclass.
Interface
We already know that there are two ways by which we can implement abstraction in Java – Abstract classes and interfaces. We have already understood what Abstract classes are with the help of both theory and code, so let’s understand the concept of interface.
Interface is probably the most powerful tool to achieve abstraction in Java, as using an interface, we can achieve 100% abstraction in Java. Its basic use is to specify what methods a class should implement without dictating how they should be implemented. Simply said, tells us what to do, but leaves the how part to us.
An interface is a reference type, similar to a class, and all the methods declared in an interface are abstract by default and have no implementation. We will use the word interface just like we used to write the word abstract for abstract classes. Thus, it’s safe to say that the interface cannot also be instantiated.
Why do we need an Interface?
- To achieve complete abstraction
- To make code more flexible and maintainable.
- To promote Loose coupling
- To achieve multiple inheritance-like behavior
To better understand interfaces, we’ll use the same code example as we did for abstract classes and methods. This comparison will help clarify how interfaces are both similar to and different from abstract classes.
public interface Animal {
void makeSound(); // abstract method
void sleep(); // abstract method
}
Notice something different in the above code – we have defined methods without the abstract keyword, but they aren’t concrete methods, they are abstract methods as they don’t have a body. That is because every method defined in an interface is abstract by default; thus, it doesn’t require the keyword to be declared abstract.
Now, we also know that an abstract class can extend only once, but that’s not the case with the interface. In an interface, an abstract class can have multiple interfaces, allowing it to inherit abstract methods from all the interfaces it implements, hence achieving the benefits of multiple inheritance without the associated complexities.
Important Points about Interface
- A class can implement multiple interfaces, thus making multiple inheritance possible without complexities.
- The interface can only be declared as public or default modifiers.
- Every interface in Java is by default abstract
interface A { } === abstract interface A { }
- Every variable of an interface is either final, static or public, while every method is public or abstract.
- In an interface, declaration and initialisation must be done at the same time.
interface A {
// int id; // Compilation error
int id = 20;
}
- An interface can extend another interface, but cannot implement it, only classes can.
- If a class implements two interfaces that have the same method name and return type, then implementing them once is enough.
interface A {
int calculateArea();
}
interface B {
int calculateArea();
}
class MyClass implements A, B {
@Override
public int calculateArea() {
// Defining once is enough since both interfaces have the same method signature
int length = 5;
int breadth = 4;
return length * breadth;
}
public static void main(String[] args) {
MyClass obj = new MyClass();
System.out.println("Area: " + obj.calculateArea());
}
}
- But if even one of them is different, then it’s not possible, and they will be written separately.
interface A {
int calulateArea();
}
interface B {
void calulateArea();
}
class MyClass implements A, B {
// Not possible to define calculateArea() for both interfaces
}
- If we have an interface and a class that implements it, and we create an object using the interface type, we can only call the methods that are declared in the interface. Trying to call other methods will cause a compile-time error.
interface MyInterface {
void print();
}
class MyClass implements MyInterface {
public void print() {
System.out.println("Inside print method");
}
public void message() {
System.out.println("Inside message method");
}
public static void main(String args []) {
MyInterface obj = new MyClass(); // Assigning MyClass object into MyInterface type
// obj.message(); // Compilation error
obj.print(); // prints "Inside print method"
}
}
This behavior is intentional and is a fundamental concept of polymorphism and abstraction in object-oriented programming. By assigning an object to an interface type, we can effectively treat the object as an instance of the interface, which can only access the members (methods and properties) defined in the interface contract.
This approach promotes code modularity, flexibility, and maintainability. It allows us to write code that works with objects of different classes as long as they implement the same interface, without needing to know the specific implementation details of each class.
- Suppose we encounter a situation in which we have defined a variable with the same name in two different interfaces. In that case, we cannot implement it as it will give us a compilation error (the diamond problem). What we can do is use the interface name and the variable name in conjunction. See the code below
interface X {
int id = 10;
}
interface Y {
int id = 20;
}
class MyClass implements X, Y {
public static void main(String[] args) {
// System.out.println(id); // ❌ Compilation error: ambiguous reference
System.out.println(X.id); // ✅ Access X.id — prints 10
System.out.println(Y.id); // ✅ Access Y.id — prints 20
}
}
In the above code, we didn’t create any objects. That’s because interface variables are public static final by default, thus belong to the interface itself, not to any object or class that implements the interface.
- A class implementing an interface cannot change the value of a variable declared in the interface since they are declared as final by default.
interface A {
int id = 35; // public static final by default
void test(); // abstract method
}
class MyClass implements A {
public void test() {
id = 30; // ❌ Compilation error: cannot assign a value to final variable 'id'
}
}
The way around is to declare it as an instance variable.
class MyClass implements A {
int id = 35; // now it's a regular instance variable
public void test() {
id = 30; // ✅ OK now
}
}
Understanding Interface with the help of code
Let’s understand the interface through code. In this code, we have defined
- An interface called Vehicle with a few method declarations and a constant.
- A class called Car that implements the Vehicle interface and defines the behaviour of a car.
- A Main class with the object declaration and method calling.
Let’s start…
interface Vehicle {
// Constants
double MAX_SPEED = 200.0; // public, static, and final by default
// Abstract methods
void start();
void accelerate(double speed);
void brake();
double getCurrentSpeed();
}
class Car implements Vehicle {
private double currentSpeed;
@Override
public void start() {
System.out.println("Car started.");
}
@Override
public void accelerate(double speed) {
currentSpeed += speed;
System.out.println("Car accelerated to " + currentSpeed + " km/h.");
}
@Override
public void brake() {
currentSpeed = 0;
System.out.println("Car stopped.");
}
@Override
public double getCurrentSpeed() {
return currentSpeed;
}
}
public class Main {
public static void main(String[] args) {
Car myCar = new Car();
myCar.start();
myCar.accelerate(50.0);
System.out.println("Current speed: " + myCar.getCurrentSpeed() + " km/h");
myCar.brake();
}
}
Output
There are many things that are going on and can be overwhelming for beginners, thus we have decided to divide the explanation part into three subheadings – The Interface Declaration, The Implementation and The Main Method.
The Interface Declaration
In the above code, we have defined an interface named Vehicle, which includes a constant MAX_SPEED. This constant is final by default and represents the maximum speed of a vehicle, set at 200 km/h. The interface also includes several abstract methods, some with a void return type, meaning they don’t return any value, and one method with a double return type that will return the current speed of the vehicle.
The Implementation Part
The implementation part begins with the Car class, which implements our Vehicle interface. Inside this class, we define an instance variable called currentSpeed, marked as private and of type double, so it can store decimal values like 30.5. This variable keeps track of the car’s current speed.
The start() method simply prints a message to indicate the car has started. The accelerate(double speed) method increases the currentSpeed by the given speed, using the formula currentSpeed += speed. The speed parameter is a double to allow for decimal values. When we call this method, the new speed is calculated and printed.
The brake() method resets the currentSpeed to 0, simulating what happens when a vehicle comes to a complete stop. Finally, the getCurrentSpeed() method is used to retrieve the car’s current speed. Inside this method, we use the return keyword to send the value of currentSpeed back to the part of the program that called it — in this case, the main() method, where it’s printed to the screen.
The Main Method
In the main() method, we create an object called myCar using the Car class and call its methods one by one to test the functionality of the program. First, we call the start() method to simulate starting the car. Then, we call accelerate(50.0) to increase the speed by 50.0 km/h( here, 50.0 is the argument we pass to the method, which is received as a parameter inside the method definition). This updated speed is retrieved using getCurrentSpeed() and printed to the console. Finally, the brake() method is called to stop the car by resetting the speed to 0.
Nested Interface
A nested interface (or member interface) is an interface declared inside a class or another interface. This helps logically group interfaces that are only used in one place, promoting better encapsulation and reducing code clutter.
- When declared inside a class, it can have access modifiers:
public
,protected
,private
, or package-private (no modifier). - When declared inside an interface, the nested interface is implicitly
public
andstatic
.
Syntax
- Interface inside another interface
interface first{
interface second{
// body
}
}
- Interface inside a Class
class abc{
interface _interface_name{
// body
}
}
Difference between Abstract class and interface
Parameter | Abstract Class | Interface |
Declaration | Uses the abstract keyword | Uses the interface keyword |
Method | Both Abstract and Concrete Methods | Abstract, Default and Static Methods (after Java 8) |
Inheritance | Doesn’t support multiple inheritance | Supports multiple inheritance |
Implementation | Can provide for the interface | Can not provide for Abstract Class |
Variable types | Can have instance variables, static variables, and constants | Can only have static and final variables (constants) |
Access Modifiers | Have public, protected and private | All methods are public by default, variables are public static final by default |
Abstraction | Partial Abstraction | Complete Abstraction |
Can extend | Another Java Class | Java interface |
Similarity between Abstract Class and Interface
- Both Abstract Classes and Interfaces cannot be instantiated – you cannot create objects of them directly.
- Both can contain abstract methods that have no implementation
When to use Abstract Class VS When to use interface
According to Oracle documentation, we can use Abstract Class when –
- We want to share code among several closely related classes.
- We expect that classes that extend your abstract class have many common methods or fields, or require access modifiers other than public (such as protected and private).
- We want to declare non-static or non-final fields. This enables you to define methods that can access and modify the state of the object to which they belong.
And the interface when
- We expect that unrelated classes will implement our interface. For example, the interfaces Comparable and Cloneable are implemented by many unrelated classes.
- We want to specify the behaviour of a particular data type, but are not concerned about who implements its behaviour.
- We want to take advantage of multiple inheritance of types.
Advantages of Abstraction in Java
- It reduces code complexity and increases code readability and security.
- It also improves maintainability, modularity, reusability and flexibility
- Supports modularity, as complex systems can be divided into smaller and more manageable parts.
- Abstraction allows for flexibility in the implementation of a program, as changes to the underlying implementation details can be made without affecting the user-facing interface.
- Abstraction enables modularity and separation of concerns, making code more maintainable and easier to debug.
Disadvantages of Abstraction in Java
- It can increase the complexity of the code if not used properly.
- It can make things difficult to understand, particularly the workings of the system.
- This may limit the flexibility of the application.
- Overuse of abstraction can result in decreased performance due to the additional layers of code and indirection.
Conclusion
In conclusion, we would just say that abstraction is one of the four fundamental pillars of OOP in Java, which helps in hiding the complex part of the system from the user while showing only the essential things to the user that he needs. We can also say that Abstraction is a process that displays what something does while hiding how it’s done.
There are two ways of ways in which we can achieve abstraction – through Abstract classes and Interfaces. Neither of these methods can be instantiated on their own and needs separate keywords to do so. In the case of Abstract Classes, it’s abstract, while in the case of an interface, it’s interface. We have already discussed about them in detail in the blog, and hope you will like it.
Abstraction in Java FAQ’s
Q1. What is the difference between Encapsulation and Abstraction
Ans. Encapsulation is the practice of bundling data and methods within a single unit, like a class, and controlling their access, whereas abstraction is about hiding complex implementation details and exposing only the essential functionalities.
Q2. What is the benefit of using abstract classes?
Ans. It reduces code complexity and increases code readability and security.
Q3. Can we create an object of the Abstract class?
Ans. No, we cannot create an object of an abstract class directly. We can only create objects of subclasses that extend the abstract class and provide implementations for its abstract methods.
Q4. Can an abstract class have a constructor?
Ans. Yes, an abstract class can have a constructor. The constructor is called when an instance of a subclass is created. This is useful for initializing common properties that are shared among all subclasses.
Q5. Can an abstract class be final in Java?
Ans. No, an abstract class cannot be declared as final in Java because a final class cannot be subclassed, and an abstract class needs to be subclassed to provide implementations for its abstract methods.
Q6. Why use interfaces when we have abstract classes?
Ans. The reason is that abstract classes may contain non-final variables, whereas variables in the interface are final, public, and static.
you are in reality a good webmaster The website loading velocity is amazing It sort of feels that youre doing any distinctive trick Also The contents are masterwork you have done a fantastic job in this topic
Its like you read my mind You appear to know a lot about this like you wrote the book in it or something I think that you could do with some pics to drive the message home a little bit but instead of that this is fantastic blog An excellent read I will certainly be back
“Mind = blown! 🌟 This is exactly the comprehensive breakdown I needed. Your expertise shines through in every paragraph. Thanks for sharing such well-researched content.”