java
- IntroductionInstallation and SetupJava Syntax and StructureRunning Your First ProgramJava VariablesData Types in JavaOperators in JavaJava Input and OutputControl StatementsLoops in JavaMethods in Java (Functions)Arrays in JavaString Handling in JavaOOPS Concept in JavaClasses and ObjectsConstructor and Constructor Overloadingthis KeywordStatic MembersInheritanceAggregation in JavaMethod OverloadingMethod Overridingsuper KeywordFinal Keyword with Class, Method, and VariablesAccess Modifiers in javaEncapsulation in Java Polymorphism in JavaAbstraction in JavaAbstract ClassesInterfaces in JavaDifference between Abstract Class and Interface Nested and Inner Classes Exception HandlingJava PackagesWrapper Classes and AutoboxingJava Collections FrameworkFile Handling in JavaMultithreadingBasics of Java Memory Management and Garbage CollectionJava JDBC
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:
-
Encapsulation: Bundling data and methods, restricting access to protect data.
-
Inheritance: Reusing code by allowing one class to inherit from another.
-
Polymorphism: Allowing objects to take multiple forms (e.g., method overriding or overloading).
-
Abstraction: Hiding complex details and showing only essential features.
-
Composition: A strong "has-a" relationship where one class owns another.
-
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! ![]()
.png)