- What is an Abstract Class in Java?
- Syntax:
- Real-World Example 1: Animal Sound
- Explanation for Beginners:
- Real-World Example 2: Online Payment System
- Explanation (Line-by-Line):
- 1. Abstract Class Payment
- 2. Subclasses (CreditCardPayment and UpiPayment)
- 3. Main Method (PaymentDemo)
- Key Points:
- Why Use Abstract Classes?
- Rules of Abstract Classes and Methods
- 1️⃣ You cannot instantiate an abstract class.
- 2️⃣ Abstract class can have constructors.
- 3️⃣ Abstract class can have static methods and variables.
- 4️⃣ An abstract class can extend another abstract class.
- 5️⃣ An abstract method cannot be private, static, or final.
- 6️⃣ If a class has at least one abstract method, it must be declared abstract.
- 7️⃣ A subclass must either implement all abstract methods or be declared abstract itself.
- Constructor in Abstract Class
- Real-World Analogy
- Step-by-Step Explanation:
- Abstract Class vs Interface
- Abstract Class with Multiple Subclasses
- Step-by-Step Explanation:
- 1️⃣ Vehicle is an abstract class
- 2️⃣ Car and Bike are concrete subclasses
- 3️⃣ In the main() method:
- Real-World Analogy:
- Benefits of Using Abstract Classes
- Practice Questions
- Conclusion
In Java, abstraction is one of the four main pillars of Object-Oriented Programming (OOP), along with encapsulation, inheritance, and polymorphism. Abstraction helps you hide complex implementation details and show only the essential features of an object.
When you design large applications, not every class should be instantiated. Some classes serve only as blueprints or templates for other classes. That’s where abstract classes come into play.
This tutorial will help you understand abstract classes in Java from scratch with real-world analogies, simple examples, and advanced concepts.
What is an Abstract Class in Java?
An abstract class in Java is a class that cannot be instantiated, meaning you cannot create an object of it directly. It is meant to be extended by other classes, and it can have abstract methods (without a body) as well as concrete methods (with a body).
Syntax:
abstract class ClassName {
// Abstract method (does not have a body)
abstract void methodName();
// Concrete method (has a body)
void normalMethod() {
System.out.println("This is a normal method.");
}
}
Real-World Example 1: Animal Sound
// Abstract class
abstract class Animal {
// Abstract method (no body)
abstract void makeSound();
// Concrete method (has body)
void sleep() {
System.out.println("Sleeping...");
}
}
// Subclass Dog
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Dog says: Woof!");
}
}
// Subclass Cat
class Cat extends Animal {
@Override
void makeSound() {
System.out.println("Cat says: Meow!");
}
}
// Main class
public class Main {
public static void main(String[] args) {
Animal a1 = new Dog(); // Polymorphism
a1.makeSound();
a1.sleep();
System.out.println();
Animal a2 = new Cat();
a2.makeSound();
a2.sleep();
}
}
Output:
Dog says: Woof!
Sleeping...
Cat says: Meow!
Sleeping...
Explanation for Beginners:
Animalis an abstract class — a general idea of all animals.makeSound()is an abstract method, so each animal defines its own sound.sleep()is a normal method that works for all animals.
➡️ You can’t create an object of Animal, but you can use its reference to point to objects of its subclasses (like Dog, Cat).
Real-World Example 2: Online Payment System
Let’s say you are designing an online payment system that supports multiple payment methods like Credit Card, UPI, and Net Banking.
We’ll create an abstract class Payment that outlines what every payment method must do, but lets the specific payment method define how it works.
// Abstract class
abstract class Payment {
String transactionId;
// Constructor
Payment(String transactionId) {
this.transactionId = transactionId;
}
// Abstract method (must be implemented by subclasses)
abstract void pay(double amount);
// Concrete method (shared by all subclasses)
void printReceipt() {
System.out.println("Transaction ID: " + transactionId);
System.out.println("Thank you for your payment.");
}
}
// Subclass 1: Credit Card Payment
class CreditCardPayment extends Payment {
String cardNumber;
CreditCardPayment(String transactionId, String cardNumber) {
super(transactionId);
this.cardNumber = cardNumber;
}
@Override
void pay(double amount) {
System.out.println("Paying ₹" + amount + " using Credit Card: " + cardNumber);
}
}
// Subclass 2: UPI Payment
class UpiPayment extends Payment {
String upiId;
UpiPayment(String transactionId, String upiId) {
super(transactionId);
this.upiId = upiId;
}
@Override
void pay(double amount) {
System.out.println("Paying ₹" + amount + " using UPI ID: " + upiId);
}
}
// Main class
public class PaymentDemo {
public static void main(String[] args) {
Payment payment1 = new CreditCardPayment("TXN12345", "1234-XXXX-XXXX-5678");
payment1.pay(2500);
payment1.printReceipt();
System.out.println();
Payment payment2 = new UpiPayment("TXN67890", "user@upi");
payment2.pay(999.99);
payment2.printReceipt();
}
}
Output
Paying ₹2500.0 using Credit Card: 1234-XXXX-XXXX-5678
Transaction ID: TXN12345
Thank you for your payment.
Paying ₹999.99 using UPI ID: user@upi
Transaction ID: TXN67890
Thank you for your payment.
Explanation (Line-by-Line):
1. Abstract Class Payment
- It holds a common property
transactionId. - It has:
- An abstract method
pay(double amount)→ No body. - A concrete method
printReceipt()→ Has shared logic.
- An abstract method
2. Subclasses (CreditCardPayment and UpiPayment)
- Both extend the
Paymentclass. - Both implement the
pay()method differently based on their payment type.
3. Main Method (PaymentDemo)
- Objects are created using child classes, not the abstract class.
- We call
pay()andprintReceipt()to show polymorphism in action.
Key Points:
- Use the
abstractkeyword before the class name. - Abstract classes can have both abstract and non-abstract methods.
- Abstract classes can have constructors, fields, and methods.
- An abstract method must be overridden in the subclass unless the subclass is also abstract.
- You cannot instantiate an abstract class directly.
Why Use Abstract Classes?
- To define common behavior for a group of related classes.
- To enforce implementation of certain methods in subclasses.
- To create a template that other classes must follow.
- Useful in large applications with multiple subclasses sharing base logic.
Rules of Abstract Classes and Methods
1️⃣ You cannot instantiate an abstract class.
You cannot create an object of an abstract class directly using new. It is meant to be a base or blueprint class, and must be extended by a subclass to be useful.
2️⃣ Abstract class can have constructors.
Even though you can’t create objects of an abstract class, it can still have constructors. These are used to initialize values when a subclass object is created.
3️⃣ Abstract class can have static methods and variables.
Static methods and variables belong to the class itself, not to objects. Abstract classes can have them, and they can be accessed using the class name.
4️⃣ An abstract class can extend another abstract class.
Abstract classes can form inheritance chains. One abstract class can extend another, and the child class can implement or further pass on abstract methods.
5️⃣ An abstract method cannot be private, static, or final.
Since abstract methods must be overridden in subclasses:
- They can’t be private, as that would restrict access.
- They can’t be static, as static methods can’t be overridden.
- They can’t be final, because final methods cannot be modified.
6️⃣ If a class has at least one abstract method, it must be declared abstract.
A class that includes even a single abstract method must itself be declared as abstract. Otherwise, it will result in a compile-time error.
7️⃣ A subclass must either implement all abstract methods or be declared abstract itself.
When a subclass inherits an abstract class, it must provide concrete implementations for all inherited abstract methods. If it doesn’t, then the subclass must also be marked abstract.
Constructor in Abstract Class
In Java, abstract classes can have constructors, even though you can’t create objects directly from them. These constructors are called when a subclass object is created, and they help initialize any common properties or logic defined in the abstract class.
Real-World Analogy
Imagine Shape as a blueprint for all shapes. You can’t draw a generic “shape” on paper (just like you can’t create an object of an abstract class), but every shape you draw (like a circle) follows some basic setup rules, like using a pencil and paper.
These setup rules are part of the abstract class constructor, and they are automatically executed when you draw a specific shape (like a Circle object).
// Abstract class
abstract class Shape {
// Constructor of abstract class
Shape() {
System.out.println("Shape constructor called");
}
// Abstract method
abstract void draw();
}
// Subclass that extends Shape
class Circle extends Shape {
Circle() {
System.out.println("Circle constructor called");
}
void draw() {
System.out.println("Drawing a circle");
}
}
// Main class to run the program
public class Test {
public static void main(String[] args) {
Circle c = new Circle();
c.draw();
}
}
Output:
Shape constructor called
Circle constructor called
Drawing a circle
Step-by-Step Explanation:
Circle c = new Circle();
You are creating an object of theCircleclass.- Since
CircleextendsShape, the constructor of the abstract classShapeis called first, even though it’s abstract.
👉 Output:Shape constructor called - Then, the constructor of
Circleruns.
👉 Output:Circle constructor called - The
draw()method ofCircleis then called.
👉 Output:Drawing a circle
Abstract Class vs Interface
| Feature | Abstract Class | Interface |
|---|---|---|
| Methods | Can have both abstract and concrete methods | Only abstract methods (Java 7), default/static (Java 8+) |
| Fields | Can have variables | Only static final variables (constants) |
| Constructors | Yes | No |
| Inheritance | One abstract class can be extended | Multiple interfaces can be implemented |
| Access Modifiers | Can be private, protected, public | Methods are public by default |
Use abstract class when:
- You want to provide base functionality to subclasses.
- You have state (variables) to share among subclasses.
Use interface when:
- You want to define a contract for unrelated classes.
- You need multiple inheritance.
Abstract Class with Multiple Subclasses
This example demonstrates how an abstract class can act as a common blueprint for multiple subclasses, and how each subclass provides its own unique implementation of the abstract method.
abstract class Vehicle {
abstract void start();
}
class Car extends Vehicle {
void start() {
System.out.println("Car started with key.");
}
}
class Bike extends Vehicle {
void start() {
System.out.println("Bike started with self-start.");
}
}
public class Test {
public static void main(String[] args) {
Vehicle v1 = new Car();
Vehicle v2 = new Bike();
v1.start(); // Car started with key.
v2.start(); // Bike started with self-start.
}
}
Output
Car started with key.
Bike started with self-start.
Step-by-Step Explanation:
1️⃣ Vehicle is an abstract class
- It defines an abstract method
start()with no body. - This method must be overridden by any subclass that extends
Vehicle.
2️⃣ Car and Bike are concrete subclasses
- Both inherit from
Vehicle. - Each one provides its own version of the
start()method:Caruses a key to start.Bikeuses a self-start system.
3️⃣ In the main() method:
Vehicle v1 = new Car();
- A reference of type
Vehicleis pointing to aCarobject. - This is an example of runtime polymorphism.
- When
v1.start()is called, it executes the Car’s start() method.
👉 Output:Car started with key.
Vehicle v2 = new Bike();
- Similarly, a
Bikeobject is assigned to aVehiclereference. - When
v2.start()is called, it runs the Bike’s start() method.
👉 Output:Bike started with self-start.
Real-World Analogy:
Think of Vehicle as a generic idea of a vehicle — it doesn’t say how the vehicle starts.
- A
Carmight need a key. - A
Bikemight have a button for self-start.
Each subclass customizes the abstract behavior defined by the abstract class.
Benefits of Using Abstract Classes
- Code Reusability: Shared methods can be written once and used by all subclasses.
- Enforced Design: Subclasses must implement abstract methods, ensuring consistency.
- Polymorphism: Abstract classes allow for upcasting and dynamic method dispatch.
- Scalability: Easily scalable in large applications.
Practice Questions
- Can we create an object of an abstract class?
- What happens if a subclass does not override all abstract methods?
- Can an abstract class have a
main()method? - Can an abstract class implement an interface?
Try implementing a class Bank with abstract method getInterestRate() and create subclasses SBI and HDFC that return different interest rates.
Conclusion
Abstract classes in Java are a powerful tool when designing applications with common behavior across multiple classes. They allow you to define a blueprint that must be followed by all subclasses, thus promoting code consistency, scalability, and reusability.