Abstraction in Java
Learn to hide complexity and show only what's necessary
💡 Think of abstraction like driving a car! You know how to drive (use the steering wheel, pedals, and gear shift) without knowing how the engine works inside. The car hides the complex engine details and only shows you the simple controls you need. That's abstraction - hiding complexity and showing only what matters!
🎨 What is Abstraction?
Abstraction means hiding the implementation details and showing only the functionality to the user. It focuses on WHAT an object does rather than HOW it does it. Think of it as a contract - 'I promise this method exists, but each class decides how to implement it.'
// Abstract class - cannot create objects from it directlyabstract class Animal { // Concrete method (has body) - all animals inherit this public void sleep() { System.out.println("Zzz... sleeping"); } // Abstract method (no body) - each animal must implement its own public abstract void makeSound(); // Another abstract method public abstract void move();}// Concrete class - implements all abstract methodsclass Dog extends Animal { @Override public void makeSound() { System.out.println("Woof! Woof!"); } @Override public void move() { System.out.println("Dog is running"); }}class Bird extends Animal { @Override public void makeSound() { System.out.println("Tweet! Tweet!"); } @Override public void move() { System.out.println("Bird is flying"); }}class AbstractionDemo { public static void main(String[] args) { // Animal animal = new Animal(); // ❌ ERROR! Can't instantiate abstract class // ✓ Create concrete classes Animal myDog = new Dog(); Animal myBird = new Bird(); // Use them through abstract type myDog.makeSound(); // "Woof! Woof!" myDog.move(); // "Dog is running" myDog.sleep(); // "Zzz... sleeping" (inherited) myBird.makeSound(); // "Tweet! Tweet!" myBird.move(); // "Bird is flying" myBird.sleep(); // "Zzz... sleeping" (inherited) }}❓ Why Use Abstraction?
- ✓Hide Complexity: Users don't need to understand internal workings
- ✓Reduce Code Duplication: Common structure defined once
- ✓Increase Flexibility: Change implementation without affecting users
- ✓Enforce Structure: Force child classes to implement certain methods
📋 Abstract Classes
abstract class Vehicle { // Fields (can have state) protected String brand; protected int year; protected boolean isRunning; // Constructor (abstract classes can have constructors!) public Vehicle(String brand, int year) { this.brand = brand; this.year = year; this.isRunning = false; } // Abstract methods (no implementation) public abstract void start(); public abstract void stop(); public abstract int getMaxSpeed(); // Concrete method (has implementation) public void displayInfo() { System.out.println("Brand: " + brand); System.out.println("Year: " + year); System.out.println("Max Speed: " + getMaxSpeed() + " km/h"); } public boolean isRunning() { return isRunning; }}class Car extends Vehicle { private int numberOfDoors; public Car(String brand, int year, int doors) { super(brand, year); // Call abstract class constructor this.numberOfDoors = doors; } @Override public void start() { isRunning = true; System.out.println(brand + " car engine starting... Vroom!"); } @Override public void stop() { isRunning = false; System.out.println(brand + " car engine stopped."); } @Override public int getMaxSpeed() { return 200; // Cars go up to 200 km/h }}class Motorcycle extends Vehicle { private boolean hasSidecar; public Motorcycle(String brand, int year, boolean sidecar) { super(brand, year); this.hasSidecar = sidecar; } @Override public void start() { isRunning = true; System.out.println(brand + " motorcycle starting... Brrrm!"); } @Override public void stop() { isRunning = false; System.out.println(brand + " motorcycle stopped."); } @Override public int getMaxSpeed() { return 180; // Motorcycles go up to 180 km/h }}class VehicleDemo { public static void main(String[] args) { Vehicle car = new Car("Toyota", 2023, 4); Vehicle bike = new Motorcycle("Harley", 2022, false); car.start(); car.displayInfo(); System.out.println("Running: " + car.isRunning()); System.out.println(); bike.start(); bike.displayInfo(); System.out.println("Running: " + bike.isRunning()); }}🔌 Interfaces
// Interface - pure contract (100% abstraction)interface Drawable { void draw(); // public abstract by default void resize(int scale);}interface Colorable { void setColor(String color); String getColor();}// A class can implement multiple interfaces!class Circle implements Drawable, Colorable { private int radius; private String color; private int x, y; public Circle(int x, int y, int radius) { this.x = x; this.y = y; this.radius = radius; this.color = "Black"; } // Must implement all methods from both interfaces @Override public void draw() { System.out.println("Drawing circle at (" + x + "," + y + ")"); System.out.println("Radius: " + radius + ", Color: " + color); } @Override public void resize(int scale) { radius *= scale; System.out.println("Circle resized to radius: " + radius); } @Override public void setColor(String color) { this.color = color; System.out.println("Circle color changed to: " + color); } @Override public String getColor() { return color; }}class Rectangle implements Drawable, Colorable { private int width, height; private String color; private int x, y; public Rectangle(int x, int y, int width, int height) { this.x = x; this.y = y; this.width = width; this.height = height; this.color = "Black"; } @Override public void draw() { System.out.println("Drawing rectangle at (" + x + "," + y + ")"); System.out.println("Size: " + width + "x" + height + ", Color: " + color); } @Override public void resize(int scale) { width *= scale; height *= scale; System.out.println("Rectangle resized to: " + width + "x" + height); } @Override public void setColor(String color) { this.color = color; System.out.println("Rectangle color changed to: " + color); } @Override public String getColor() { return color; }}class InterfaceDemo { public static void main(String[] args) { // Treat objects through interface types Drawable shape1 = new Circle(100, 100, 50); Drawable shape2 = new Rectangle(200, 150, 80, 60); shape1.draw(); shape1.resize(2); shape1.draw(); System.out.println(); shape2.draw(); shape2.resize(3); shape2.draw(); // Can also use Colorable interface Colorable colorShape = new Circle(50, 50, 25); colorShape.setColor("Red"); System.out.println("Color: " + colorShape.getColor()); }}🛠️ Ways to Achieve Abstraction
1. Abstract Classes (0-100% abstraction)
Can have both abstract methods (without body) and concrete methods (with body)
abstract class Animal { abstract void sound(); void sleep() {...} }
2. Interfaces (100% abstraction)
All methods are abstract by default (until Java 8). Pure contract.
interface Drawable { void draw(); }
⚖️ Abstract Class vs Interface
// ABSTRACT CLASS - Can have both abstract and concrete methodsabstract class Employee { // Can have fields protected String name; protected double baseSalary; // Can have constructor public Employee(String name, double baseSalary) { this.name = name; this.baseSalary = baseSalary; } // Concrete method - shared by all employees public void clockIn() { System.out.println(name + " clocked in at work"); } // Abstract method - each employee calculates differently public abstract double calculateSalary(); public abstract void performDuties();}// INTERFACE - Pure contractinterface Promotable { void promote(String newTitle); boolean isEligibleForPromotion();}interface Trainable { void attendTraining(String course); int getTrainingHours();}// Class can extend ONE abstract class and implement MULTIPLE interfacesclass Developer extends Employee implements Promotable, Trainable { private String title; private int projectsCompleted; private int trainingHours; public Developer(String name, double baseSalary, String title) { super(name, baseSalary); this.title = title; this.projectsCompleted = 0; this.trainingHours = 0; } // Implement abstract methods from Employee @Override public double calculateSalary() { return baseSalary + (projectsCompleted * 1000); } @Override public void performDuties() { System.out.println(name + " is writing code as " + title); projectsCompleted++; } // Implement Promotable interface @Override public void promote(String newTitle) { this.title = newTitle; System.out.println(name + " promoted to: " + newTitle); } @Override public boolean isEligibleForPromotion() { return projectsCompleted >= 5 && trainingHours >= 40; } // Implement Trainable interface @Override public void attendTraining(String course) { System.out.println(name + " attending: " + course); trainingHours += 8; } @Override public int getTrainingHours() { return trainingHours; }}class ComparisonDemo { public static void main(String[] args) { Developer dev = new Developer("Alice", 80000, "Junior Developer"); // Use inherited concrete method dev.clockIn(); // Perform duties (abstract method implemented) dev.performDuties(); dev.performDuties(); dev.performDuties(); // Attend training (interface method) dev.attendTraining("Java Advanced"); dev.attendTraining("Spring Boot"); dev.attendTraining("Microservices"); dev.attendTraining("System Design"); dev.attendTraining("Leadership"); // Check promotion eligibility if (dev.isEligibleForPromotion()) { dev.promote("Senior Developer"); } System.out.println("\nFinal Salary: $" + dev.calculateSalary()); System.out.println("Training Hours: " + dev.getTrainingHours()); }}🌟 Real-World Example: Database Connection
// Abstract class defines common database operationsabstract class Database { protected String host; protected int port; protected String username; protected boolean connected; public Database(String host, int port, String username) { this.host = host; this.port = port; this.username = username; this.connected = false; } // Abstract methods - each database implements differently public abstract void connect(); public abstract void disconnect(); public abstract void executeQuery(String query); // Concrete method - shared by all databases public void showStatus() { System.out.println("Host: " + host + ":" + port); System.out.println("User: " + username); System.out.println("Connected: " + connected); }}class MySQLDatabase extends Database { public MySQLDatabase(String host, int port, String username) { super(host, port, username); } @Override public void connect() { System.out.println("Connecting to MySQL database..."); // MySQL-specific connection logic here connected = true; System.out.println("MySQL connected successfully!"); } @Override public void disconnect() { System.out.println("Disconnecting from MySQL..."); connected = false; System.out.println("MySQL disconnected."); } @Override public void executeQuery(String query) { if (!connected) { System.out.println("Error: Not connected to MySQL"); return; } System.out.println("Executing MySQL query: " + query); System.out.println("Query executed successfully!"); }}class PostgreSQLDatabase extends Database { public PostgreSQLDatabase(String host, int port, String username) { super(host, port, username); } @Override public void connect() { System.out.println("Establishing PostgreSQL connection..."); // PostgreSQL-specific connection logic here connected = true; System.out.println("PostgreSQL connected!"); } @Override public void disconnect() { System.out.println("Closing PostgreSQL connection..."); connected = false; System.out.println("PostgreSQL disconnected."); } @Override public void executeQuery(String query) { if (!connected) { System.out.println("Error: Not connected to PostgreSQL"); return; } System.out.println("Running PostgreSQL query: " + query); System.out.println("Query completed!"); }}class MongoDatabase extends Database { public MongoDatabase(String host, int port, String username) { super(host, port, username); } @Override public void connect() { System.out.println("Connecting to MongoDB..."); // MongoDB-specific connection logic here connected = true; System.out.println("MongoDB connected!"); } @Override public void disconnect() { System.out.println("Closing MongoDB connection..."); connected = false; System.out.println("MongoDB disconnected."); } @Override public void executeQuery(String query) { if (!connected) { System.out.println("Error: Not connected to MongoDB"); return; } System.out.println("Executing MongoDB query: " + query); System.out.println("Query executed!"); }}class DatabaseDemo { // This method works with ANY database type! public static void performDatabaseOperations(Database db) { db.showStatus(); db.connect(); db.executeQuery("SELECT * FROM users"); db.disconnect(); System.out.println(); } public static void main(String[] args) { Database mysql = new MySQLDatabase("localhost", 3306, "admin"); Database postgres = new PostgreSQLDatabase("192.168.1.100", 5432, "dev"); Database mongo = new MongoDatabase("cloud.server.com", 27017, "user"); // Same method works with all database types! performDatabaseOperations(mysql); performDatabaseOperations(postgres); performDatabaseOperations(mongo); // Abstraction hides the complexity! }}🔗 Multiple Interface Implementation
interface Flyable { void fly(); int getMaxAltitude();}interface Swimmable { void swim(); int getMaxDepth();}interface Walkable { void walk(); int getMaxSpeed();}// Duck can do all three!class Duck implements Flyable, Swimmable, Walkable { private String name; public Duck(String name) { this.name = name; } @Override public void fly() { System.out.println(name + " is flying through the air!"); } @Override public int getMaxAltitude() { return 500; // meters } @Override public void swim() { System.out.println(name + " is swimming in the water!"); } @Override public int getMaxDepth() { return 3; // meters } @Override public void walk() { System.out.println(name + " is waddling on land!"); } @Override public int getMaxSpeed() { return 5; // km/h }}// Fish can only swimclass Fish implements Swimmable { private String species; public Fish(String species) { this.species = species; } @Override public void swim() { System.out.println(species + " is swimming gracefully!"); } @Override public int getMaxDepth() { return 100; // meters }}// Bird can fly and walkclass Sparrow implements Flyable, Walkable { private String name; public Sparrow(String name) { this.name = name; } @Override public void fly() { System.out.println(name + " is soaring in the sky!"); } @Override public int getMaxAltitude() { return 1000; // meters } @Override public void walk() { System.out.println(name + " is hopping on the ground!"); } @Override public int getMaxSpeed() { return 3; // km/h }}class MultiInterfaceDemo { public static void main(String[] args) { Duck duck = new Duck("Donald"); Fish fish = new Fish("Nemo"); Sparrow sparrow = new Sparrow("Jack"); System.out.println("=== Duck (can do everything!) ==="); duck.fly(); duck.swim(); duck.walk(); System.out.println("\n=== Fish (only swims) ==="); fish.swim(); System.out.println("Max depth: " + fish.getMaxDepth() + "m"); System.out.println("\n=== Sparrow (flies and walks) ==="); sparrow.fly(); sparrow.walk(); System.out.println("Max altitude: " + sparrow.getMaxAltitude() + "m"); }}🔑 Key Concepts
Abstract Class
Cannot be instantiated, can have abstract and concrete methods
abstract class Shape { abstract double area(); }
Abstract Method
Method without body, must be implemented by child class
abstract void methodName();
Interface
Contract that classes must follow, all methods abstract (pre-Java 8)
interface Printable { void print(); }
implements Keyword
Used to implement an interface
class Document implements Printable { }
✨ Best Practices
- ✓Use abstract classes when classes share common code
- ✓Use interfaces when defining pure contracts/capabilities
- ✓Name interfaces with adjectives (Runnable, Drawable, Printable)
- ✓Keep interfaces focused - don't make them too large
- ✓Abstract classes can have constructors, interfaces cannot
- ✓A class can implement multiple interfaces but extend only one abstract class
💼 Interview Tips
- •Abstract class vs Interface: Abstract can have state and concrete methods
- •You cannot create instance of abstract class (new AbstractClass() ❌)
- •Abstract method has no body, ends with semicolon
- •Child must implement all abstract methods or be abstract itself
- •Interfaces support multiple inheritance, abstract classes don't
- •From Java 8: interfaces can have default and static methods