×

Advanced Coding & Software Engineering Program

Duration: 1 Year (12 Months)

Join our premium 1-year program to master cutting-edge technologies and become an industry-ready Software Engineer!

Course Coverage

  • Languages: C, C++, Java, JavaScript, Python
  • Web Technologies: HTML, CSS, Bootstrap 5, MERN Stack, Full Stack Development
  • Databases: MySQL, MongoDB
  • Data Science Libraries: Pandas, NumPy
  • Development Tools: Visual Studio Code, IntelliJ IDEA, PyCharm, Postman, Git, GitHub
  • Cloud Platforms: Vercel, MongoDB Atlas

Program Highlights

  • Live Classes: Interactive sessions with real-time doubt resolution
  • Hands-On Sessions: Practical coding exercises to build real-world skills
  • Industry Experts: Learn from professionals with years of experience
  • Live Project: Work on real-world projects to apply your skills
  • Get Certificate: Earn a professional certificate upon program completion

Course Fee: Only ₹1020 / month
Limited Period Offer!

Java OOPs Concepts Tutorial for Beginners



Last Updated on: 24th Oct 2025 11:47:40 AM

Welcome to this beginner-friendly tutorial on Object-Oriented Programming (OOP) Concepts in Java! OOP is a programming paradigm that organizes code into objects, making it easier to manage, reuse, and scale. This tutorial is designed for beginners, providing clear explanations and practical examples of the core OOP concepts: Encapsulation, Inheritance, Polymorphism, Abstraction, Composition, Aggregation, and the is-a and has-a relationships. You can run the examples in any Java environment (e.g., Eclipse, IntelliJ, or Repl.it).

By the end, you’ll understand how to apply these OOP principles to write modular and efficient Java programs. Let’s dive in!

 

What Is Object-Oriented Programming (OOP)?

OOP is a way of designing programs using objects and classes. An object is a real-world entity with properties (data) and behaviors (actions), while a class is a blueprint for creating objects.

Key OOP Concepts in Java:

  1. Encapsulation: Bundling data and methods, restricting access to protect data.

  2. Inheritance: Reusing code by allowing one class to inherit from another.

  3. Polymorphism: Allowing objects to take multiple forms (e.g., method overriding or overloading).

  4. Abstraction: Hiding complex details and showing only essential features.

  5. Composition: A strong "has-a" relationship where one class owns another.

  6. Aggregation: A weaker "has-a" relationship where one class contains another.

 

Relationships:

  • Is-a: Represents inheritance (a subclass is a type of superclass).

  • Has-a: Represents composition or aggregation (one class contains another).

 

Real-World Analogy: Think of a car as an object. Its class (blueprint) defines properties (color, speed) and behaviors (drive, brake). OOP organizes these ideas into code, with relationships like a car "having" an engine (has-a) or a car "being" a vehicle (is-a).

 

1. Encapsulation

Encapsulation is the process of bundling data (fields) and methods that operate on that data into a single unit (class), while restricting access to protect data integrity.

  • How It’s Done: Use private fields and public getter/setter methods.

  • Why Use It: Prevents unauthorized access and ensures data is modified safely.

 

Example: Encapsulation with a Student Class

public class Student {
    // Private fields (data hiding)
    private String name;
    private int age;

    // Public getter for name
    public String getName() {
        return name;
    }

    // Public setter for name
    public void setName(String name) {
        this.name = name; // 'this' refers to the current object
    }

    // Public getter for age
    public int getAge() {
        return age;
    }

    // Public setter for age with validation
    public void setAge(int age) {
        if (age >= 0) {
            this.age = age;
        } else {
            System.out.println("Age cannot be negative!");
        }
    }
}

class EncapsulationExample {
    public static void main(String[] args) {
        // Create a Student object
        Student student = new Student();

        // Set values using setters
        student.setName("Alice");
        student.setAge(20);

        // Get values using getters
        System.out.println("Name: " + student.getName()); // Alice
        System.out.println("Age: " + student.getAge()); // 20

        // Try invalid age
        student.setAge(-5); // Age cannot be negative!
    }
}

 

Output:

Name: Alice
Age: 20
Age cannot be negative!

 

Explanation:

  • name and age are private, so they can’t be accessed directly (e.g., student.name would cause an error).

  • Public getName and setName methods control access to name.

  • The setAge method validates input, ensuring age is non-negative.

 

Real-World Analogy: Like a bank account—you can’t directly change the balance, but you can deposit/withdraw through specific methods.

 

2. Inheritance

Inheritance allows a class (child/subclass) to inherit fields and methods from another class (parent/superclass). It promotes code reuse and establishes a “is-a” relationship.

  • How It’s Done: Use the extends keyword.

  • Why Use It: Avoids duplicating code and creates a hierarchy.

 

Example: Inheritance with Vehicle and Car

// Parent class
class Vehicle {
    String brand;

    void honk() {
        System.out.println("Beep beep!");
    }
}

// Child class inheriting from Vehicle
class Car extends Vehicle {
    String model;

    void display() {
        System.out.println("Brand: " + brand + ", Model: " + model);
    }
}

public class InheritanceExample {
    public static void main(String[] args) {
        // Create a Car object
        Car car = new Car();
        car.brand = "Toyota"; // Inherited from Vehicle
        car.model = "Camry"; // Defined in Car

        car.honk(); // Inherited method
        car.display(); // Car's method
    }
}

 

Output:

Beep beep!
Brand: Toyota, Model: Camry

 

Explanation:

  • Car inherits brand and honk from Vehicle using extends.

  • Is-a Relationship: A Car is a Vehicle.

  • Car adds its own field (model) and method (display).

 

Real-World Analogy: A car “is-a” vehicle, inheriting general vehicle traits (like honking) while adding specific features (like a model name).

 

Note: Java supports single inheritance (one parent class). Use interfaces for multiple inheritance.

 

3. Polymorphism

Polymorphism means “many forms” and allows objects to be treated as instances of their parent class while behaving in their specific way. There are two types:

  • Compile-Time Polymorphism: Achieved via method overloading (same method name, different parameters).

  • Run-Time Polymorphism: Achieved via method overriding (child class redefines a parent’s method).

 

Example: Method Overloading (Compile-Time)

public class Calculator {
    // Overloaded methods
    int add(int a, int b) {
        return a + b;
    }

    double add(double a, double b) {
        return a + b;
    }

    int add(int a, int b, int c) {
        return a + b + c;
    }

    public static void main(String[] args) {
        Calculator calc = new Calculator();
        System.out.println("Add two ints: " + calc.add(5, 3)); // 8
        System.out.println("Add two doubles: " + calc.add(2.5, 3.7)); // 6.2
        System.out.println("Add three ints: " + calc.add(1, 2, 3)); // 6
    }
}

 

Output:

Add two ints: 8
Add two doubles: 6.2
Add three ints: 6

 

Explanation:

  • The add method is overloaded with different parameter types/counts.

  • Java chooses the correct method at compile time based on arguments.

 

Example: Method Overriding (Run-Time)

// Parent class
class Animal {
    void makeSound() {
        System.out.println("Some generic animal sound");
    }
}

// Child class
class Dog extends Animal {
    @Override
    void makeSound() {
        System.out.println("Woof woof!");
    }
}

public class PolymorphismExample {
    public static void main(String[] args) {
        Animal myAnimal = new Animal();
        Animal myDog = new Dog(); // Polymorphic reference
        myAnimal.makeSound(); // Generic sound
        myDog.makeSound(); // Woof woof!
    }
}

 

Output:

Some generic animal sound
Woof woof!

 

Explanation:

  • Dog overrides makeSound to provide specific behavior.

  • Animal myDog = new Dog() uses a parent reference but calls the child’s method at runtime.

  • Real-World Analogy: A pet (Animal) can make a sound, but a dog (specific type) barks differently.

 

4. Abstraction

Abstraction hides complex implementation details and exposes only essential features. It’s achieved using:

  • Abstract Classes: Cannot be instantiated; may contain abstract methods.

  • Interfaces: Define methods that classes must implement (100% abstraction).

 

Example: Abstract Class

// Abstract class
abstract class Shape {
    abstract double calculateArea();
    void display() {
        System.out.println("This is a shape.");
    }
}

class Circle extends Shape {
    double radius;

    Circle(double radius) {
        this.radius = radius;
    }

    @Override
    double calculateArea() {
        return Math.PI * radius * radius;
    }
}

public class AbstractionExample {
    public static void main(String[] args) {
        Shape circle = new Circle(5.0);
        circle.display(); // From Shape
        System.out.println("Area: " + circle.calculateArea()); // From Circle
    }
}

 

Output:

This is a shape.
Area: 78.53981633974483

 

Explanation:

  • Shape is abstract and cannot be instantiated.

  • Circle implements the abstract calculateArea method.

 

Example: Interface

interface Printable {
    void print();
}

class Document implements Printable {
    String content;

    Document(String content) {
        this.content = content;
    }

    @Override
    public void print() {
        System.out.println("Printing: " + content);
    }
}

public class InterfaceExample {
    public static void main(String[] args) {
        Printable doc = new Document("Hello, Java!");
        doc.print();
    }
}

 

Output:

Printing: Hello, Java!

 

Explanation:

  • Printable defines a contract that Document implements.

  • Real-World Analogy: Like a printer interface—any device implementing it must know how to print.

 

5. Composition

Composition is a strong has-a relationship where one class owns another class. The contained object’s lifecycle is tied to the container object—if the container is destroyed, so is the contained object.

  • How It’s Done: A class includes an instance of another class as a field.

  • Why Use It: Models strong ownership, ensuring tight coupling.

 

Example: Composition with Car and Engine

class Engine {
    String type;

    Engine(String type) {
        this.type = type;
    }

    void start() {
        System.out.println(type + " engine started.");
    }
}

class Car {
    private String model;
    private Engine engine; // Composition: Car has-a Engine

    Car(String model, String engineType) {
        this.model = model;
        this.engine = new Engine(engineType); // Engine is created with Car
    }

    void drive() {
        engine.start();
        System.out.println(model + " is driving.");
    }
}

public class CompositionExample {
    public static void main(String[] args) {
        Car car = new Car("Toyota Camry", "V6");
        car.drive();
    }
}

Output:

V6 engine started.
Toyota Camry is driving.

 

Explanation:

  • Has-a Relationship: A Car has an Engine.

  • The Engine is created when the Car is created and destroyed when the Car is destroyed.

  • Real-World Analogy: A car owns its engine; if the car is scrapped, the engine is too.

 

6. Aggregation

Aggregation is a weaker has-a relationship where one class contains another, but the contained object can exist independently. It represents a “whole-part” relationship with less coupling.

  • How It’s Done: A class holds a reference to another class, but the contained object is created externally.

  • Why Use It: Allows flexibility and reuse of objects.

 

Example: Aggregation with Department and Employee

class Employee {
    String name;

    Employee(String name) {
        this.name = name;
    }

    void work() {
        System.out.println(name + " is working.");
    }
}

class Department {
    private String deptName;
    private Employee employee; // Aggregation: Department has-a Employee

    Department(String deptName, Employee employee) {
        this.deptName = deptName;
        this.employee = employee; // Employee exists independently
    }

    void showDetails() {
        System.out.println("Department: " + deptName);
        employee.work();
    }
}

public class AggregationExample {
    public static void main(String[] args) {
        Employee emp = new Employee("Alice");
        Department dept = new Department("HR", emp);
        dept.showDetails();
    }
}

 Output:
Department: HR
Alice is working.

 

Explanation:

  • Has-a Relationship: A Department has an Employee.

  • The Employee exists independently and can be shared across departments.

  • If the Department is destroyed, the Employee can still exist.

 

Real-World Analogy: A department has employees, but employees can exist without the department (e.g., transfer to another).

 

7. Is-a vs. Has-a Relationships

  • Is-a Relationship (Inheritance):

    • Represents a parent-child relationship via extends or implements.

    • Example: A Car is a Vehicle (see Inheritance example).

    • Use when one class is a specialized version of another.

    • Key Question: Is the subclass a type of the superclass?

  • Has-a Relationship (Composition/Aggregation):

    • Represents ownership or association.

    • Example: A Car has an Engine (Composition) or a Department has an Employee (Aggregation).

    • Use when one class contains or uses another.

    • Key Question: Does the class contain or include another object?

 

Example: Combining Is-a and Has-a

// Parent class (is-a)
class Vehicle {
    void move() {
        System.out.println("Vehicle is moving.");
    }
}

// Engine class for composition
class Engine {
    String type;

    Engine(String type) {
        this.type = type;
    }

    void start() {
        System.out.println(type + " engine started.");
    }
}

// Car class (is-a Vehicle, has-a Engine)
class Car extends Vehicle {
    private String model;
    private Engine engine; // Composition (has-a)

    Car(String model, String engineType) {
        this.model = model;
        this.engine = new Engine(engineType);
    }

    void drive() {
        engine.start();
        move(); // Inherited from Vehicle
        System.out.println(model + " is driving.");
    }
}

public class IsAHasAExample {
    public static void main(String[] args) {
        Car car = new Car("Honda Civic", "Petrol");
        car.drive();
    }
}

 

Output:

Petrol engine started.
Vehicle is moving.
Honda Civic is driving.

 

Explanation:

  • Is-a: Car is a Vehicle (inherits move via extends).

  • Has-a: Car has an Engine (composition).

  • The Car uses both inherited behavior and its own components.

 

8. Combining OOP Concepts

Let’s create a program combining encapsulation, inheritance, polymorphism, abstraction, composition, and aggregation.

// Interface (Abstraction)
interface AccountOperations {
    void deposit(double amount);
}

// Engine class (for composition)
class Engine {
    String type;

    Engine(String type) {
        this.type = type;
    }

    void start() {
        System.out.println(type + " engine started.");
    }
}

// Abstract class (Abstraction, is-a)
abstract class Account implements AccountOperations {
    private double balance; // Encapsulation
    private String accountHolder;

    Account(String accountHolder, double balance) {
        this.accountHolder = accountHolder;
        this.balance = balance;
    }

    // Getters and setters
    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        if (balance >= 0) {
            this.balance = balance;
        }
    }

    public String getAccountHolder() {
        return accountHolder;
    }
}

// Child class (Inheritance, Polymorphism)
class SavingsAccount extends Account {
    private Engine engine; // Composition (has-a)

    SavingsAccount(String accountHolder, double balance, String engineType) {
        super(accountHolder, balance);
        this.engine = new Engine(engineType);
    }

    @Override
    public void deposit(double amount) { // Polymorphism
        if (amount > 0) {
            setBalance(getBalance() + amount);
            System.out.println("Deposited: " + amount);
        } else {
            System.out.println("Invalid deposit amount!");
        }
    }

    void startCar() {
        engine.start();
    }
}

// Aggregation example
class Bank {
    private String bankName;
    private SavingsAccount account; // Aggregation (has-a)

    Bank(String bankName, SavingsAccount account) {
        this.bankName = bankName;
        this.account = account;
    }

    void showDetails() {
        System.out.println("Bank: " + bankName);
        System.out.println("Account Holder: " + account.getAccountHolder());
        System.out.println("Balance: " + account.getBalance());
    }
}

public class BankSystem {
    public static void main(String[] args) {
        SavingsAccount account = new SavingsAccount("Alice", 1000.0, "Electric");
        account.deposit(500.0);
        account.startCar();

        Bank bank = new Bank("MyBank", account); // Aggregation
        bank.showDetails();
    }
}

 Output:
Deposited: 500.0
Electric engine started.
Bank: MyBank
Account Holder: Alice
Balance: 1500.0

 

Explanation:

  • Encapsulation: Private balance with getters/setters.

  • Inheritance: SavingsAccount extends Account (is-a).

  • Polymorphism: deposit is overridden.

  • Abstraction: AccountOperations interface and Account abstract class.

  • Composition: SavingsAccount has an Engine.

  • Aggregation: Bank has a SavingsAccount.

  • Is-a: SavingsAccount is an Account.

  • Has-a: SavingsAccount has an Engine; Bank has a SavingsAccount.

 

9. Common Issues and Solutions

  • Problem: Accessing private fields directly.

    • Solution: Use public getters/setters.

  • Problem: Overriding errors (wrong method signature).

    • Solution: Use @Override to catch mistakes.

  • Problem: Instantiating abstract classes/interfaces.

    • Solution: Use concrete subclasses/implementations.

  • Problem: Confusion between composition and aggregation.

    • Solution: Check lifecycle: composition implies ownership (destroyed together); aggregation implies independence.

 

10. Practice Tips

  • Experiment: Create classes with both is-a (inheritance) and has-a (composition/aggregation) relationships.

  • Test Code: Use an online compiler to run examples.

  • Common Mistake: Don’t confuse == with equals for objects.

  • Quiz Yourself: In class B extends A {}, what relationship does B have with A? (Answer: Is-a)

 

Summary

  • Encapsulation: Protects data with private fields and public methods.

  • Inheritance: Reuses code via extends (is-a relationship).

  • Polymorphism: Allows flexibility with method overloading/overriding.

  • Abstraction: Hides complexity using abstract classes/interfaces.

  • Composition: Strong has-a relationship with ownership.

  • Aggregation: Weaker has-a relationship with independent objects.

  • Is-a: Inheritance-based relationship (subclass is a superclass).

  • Has-a: Composition or aggregation-based relationship (contains another object).

  • Next Steps: Combine OOP with arrays, strings, or input/output for complex programs.

 

You’re now ready to apply OOP concepts, including composition, aggregation, and is-a/has-a relationships, in Java! Keep practicing, and happy coding! yes


Online - Chat Now
Let’s Connect

Inquiry Sent!

Your message has been successfully sent. We'll get back to you soon!

iKeySkills Logo