- A blueprint or template for creating objects.
- Defines properties (fields) and behaviors (methods).
- An instance of a class.
- Represents a specific entity with actual data.
Real-World Analogy
- Class: A blueprint of a house.
- Object: A specific house built using the blueprint.
Example
class Car {
String brand;
void drive() {
System.out.println("Driving...");
}
}
Car myCar = new Car();
myCar.brand = "Toyota";
myCar.drive(); // Output: Driving...
Polymorphism allows methods or objects to take multiple forms.
class Calculator {
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
}
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog barks");
}
}
Encapsulation is wrapping data and methods into a single unit, ensuring controlled access.
Example
class Account {
private double balance;
public double getBalance() {
return balance;
}
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
}
| Modifier | Visibility |
|---|---|
| public | Accessible from anywhere. |
| private | Accessible only within the defining class. |
| protected | Accessible within the same package and subclasses. |
| default | Accessible within the same package. |
Example
class Example {
public int publicField;
private int privateField;
protected int protectedField;
int defaultField; // Package-private
}
Abstraction hides implementation details and exposes only essential features.
Abstract Class
abstract class Vehicle {
abstract void start();
}
Interface
interface Driveable {
void drive();
}
Can a Class Implement Multiple Interfaces? Yes, a class can implement multiple interfaces to provide a contract for different behaviors.
| Feature | Abstract Class | Interface |
|---|---|---|
| Methods | Abstract and concrete methods | Abstract methods only (before Java 8). |
| Variables | Instance variables allowed | Only static final constants |
Scenario An abstract class is preferred when some methods have shared code to be inherited by subclasses.
- Models an "is-a" relationship.
- Tight coupling between parent and child classes.
- Models a "has-a" relationship.
- Promotes loose coupling.
Example
class Engine {}
class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
}
When a class inherits from more than one class.
- Not Supported to avoid ambiguity (Diamond Problem).
- Achieved using interfaces.
Example
interface A {
void methodA();
}
interface B {
void methodB();
}
class C implements A, B {
public void methodA() {
System.out.println("Method A");
}
public void methodB() {
System.out.println("Method B");
}
}
- Supports multiple inheritance using MRO (Method Resolution Order).
Refers to the current instance of a class.
Example
class Example {
private int value;
Example(int value) {
this.value = value; // Refers to instance variable
}
void display() {
System.out.println("Value: " + this.value);
}
}
A subclass provides a specific implementation for a method in the parent class.
- Same method name, return type, and parameters.
- Cannot have a stricter access modifier in the subclass.
- Must use
@Overrideannotation for clarity.
Example
class Parent {
void display() {
System.out.println("Parent Display");
}
}
class Child extends Parent {
@Override
void display() {
System.out.println("Child Display");
}
}
public class Main {
public static void main(String[] args) {
Parent obj = new Child(); // Upcasting
obj.display(); // Output: Child Display
}
}
- Belong to the class, not an instance.
- Can be called without creating an object.
- Shared among all instances of a class.
- Hold a single copy of data.
- Utility methods (e.g.,
Math.sqrt()). - Constants or shared data across objects.
- Cannot access non-static fields or methods directly.
- Static methods cannot be overridden (hidden instead).
Example
class Example {
static int count = 0; // Static variable
static void incrementCount() { // Static method
count++;
}
}
- A special method to initialize objects.
- Same name as the class, no return type.
- Default Constructor: No arguments.
- Copy Constructor: Creates a new object by copying another object.
Example
class Example {
int value;
// Default Constructor
Example() {
value = 0;
}
// Copy Constructor
Example(Example obj) {
this.value = obj.value;
}
}
- Copies references, not actual objects.
- Creates new instances for all objects.
Example
class Address {
String city;
public Address(String city) {
this.city = city;
}
}
class Person implements Cloneable {
String name;
Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // Shallow Copy
}
public Person deepClone() {
return new Person(this.name, new Address(this.address.city)); // Deep Copy
}
}
- Cannot be extended.
- Use for immutable classes or to prevent inheritance.
- Cannot be overridden.
- Ensures critical methods are not modified.
Example
final class Example {
final void display() {
System.out.println("Final Method");
}
}
==: Compares memory references..equals(): Compares content of objects.
Example
String str1 = "Hello";
String str2 = new String("Hello");
System.out.println(str1 == str2); // false
System.out.println(str1.equals(str2)); // true
- Reclaims memory for unreferenced objects.
- Reduces memory leaks.
- JVM runs garbage collection automatically using algorithms like Mark-and-Sweep.
System.gc();
- Resolves method calls at runtime based on the object type.
Example
class Parent {
void display() {
System.out.println("Parent Display");
}
}
class Child extends Parent {
@Override
void display() {
System.out.println("Child Display");
}
}
public class Main {
public static void main(String[] args) {
Parent obj = new Child(); // Upcasting
obj.display(); // Output: Child Display
}
}
- Access parent class constructors.
- Access parent class methods or fields.
Example
class Parent {
Parent() {
System.out.println("Parent Constructor");
}
}
class Child extends Parent {
Child() {
super(); // Calls Parent constructor
System.out.println("Child Constructor");
}
}
- A design pattern where dependencies are provided externally, not created within a class.
Example
interface Service {
void execute();
}
class MyService implements Service {
public void execute() {
System.out.println("Service Executed");
}
}
class Client {
private Service service;
Client(Service service) {
this.service = service; // Dependency Injected
}
void performTask() {
service.execute();
}
}
public class Main {
public static void main(String[] args) {
Service service = new MyService();
Client client = new Client(service);
client.performTask();
}
}
- A "has-a" relationship.
- Child objects can exist independently of the parent.
Example
class Engine {}
class Car {
private Engine engine;
}
- A "part-of" relationship.
- Child objects' lifecycle depends on the parent.
Example
class Engine {}
class Car {
private final Engine engine = new Engine();
}
| Feature | Aggregation | Composition |
|---|---|---|
| Lifespan | Independent of parent | Dependent on parent |
| Coupling | Loosely coupled | Strongly coupled |
- Vehicle: Base class with methods
start()andstop(). - Car, Bike, Truck: Inherit from
Vehicleand add specific behaviors.
Code Example
abstract class Vehicle {
void start() {
System.out.println("Vehicle started");
}
void stop() {
System.out.println("Vehicle stopped");
}
}
class Car extends Vehicle {
void playMusic() {
System.out.println("Playing music in the car");
}
}
class Bike extends Vehicle {
void doWheelie() {
System.out.println("Doing a wheelie");
}
}
class Truck extends Vehicle {
void loadCargo() {
System.out.println("Loading cargo");
}
}
- Account: Manages balance and transactions.
- Customer: Holds account details.
- Bank: Contains multiple customers and handles transactions.
- Transaction: Represents deposits and withdrawals.
Code Example
class Account {
private double balance;
public void deposit(double amount) {
balance += amount;
}
public void withdraw(double amount) {
if (balance >= amount) balance -= amount;
}
public double getBalance() {
return balance;
}
}
class Customer {
private String name;
private Account account;
public Customer(String name, Account account) {
this.name = name;
this.account = account;
}
public Account getAccount() {
return account;
}
}
class Transaction {
private Customer customer;
private String type;
private double amount;
public Transaction(Customer customer, String type, double amount) {
this.customer = customer;
this.type = type;
this.amount = amount;
}
public void process() {
if (type.equals("deposit")) customer.getAccount().deposit(amount);
else if (type.equals("withdraw")) customer.getAccount().withdraw(amount);
}
}
- Product: Represents items.
- User: Customer of the platform.
- Cart: Aggregates products.
- Order: Composed of cart and user details.
Code Example
class Product {
private String name;
private double price;
public Product(String name, double price) {
this.name = name;
this.price = price;
}
public double getPrice() {
return price;
}
}
class User {
private String username;
public User(String username) {
this.username = username;
}
}
class Cart {
private List products = new ArrayList<>();
public void addProduct(Product product) {
products.add(product);
}
public List getProducts() {
return products;
}
}
class Order {
private User user;
private Cart cart;
public Order(User user, Cart cart) {
this.user = user;
this.cart = cart;
}
}
Code Example
class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
Code Example
interface Shape {
void draw();
}
class Circle implements Shape {
public void draw() {
System.out.println("Drawing Circle");
}
}
class Square implements Shape {
public void draw() {
System.out.println("Drawing Square");
}
}
class ShapeFactory {
public static Shape createShape(String type) {
switch (type) {
case "Circle": return new Circle();
case "Square": return new Square();
default: throw new IllegalArgumentException("Unknown shape type");
}
}
}
Code Example
interface Subscriber {
void update(String news);
}
class NewsPublisher {
private List subscribers = new ArrayList<>();
void subscribe(Subscriber sub) {
subscribers.add(sub);
}
void notifySubscribers(String news) {
for (Subscriber sub : subscribers) {
sub.update(news);
}
}
}
class EmailSubscriber implements Subscriber {
public void update(String news) {
System.out.println("Email received: " + news);
}
}
Code Example
abstract class Animal {
abstract void makeSound();
}
class Mammal extends Animal {
void makeSound() {
System.out.println("Mammal sound");
}
}
class Bird extends Animal {
void makeSound() {
System.out.println("Bird chirp");
}
}
class ZooKeeper {
void feedAnimal(Animal animal) {
System.out.println("Feeding animal");
}
}
Code Example
class Car {
private String color;
private boolean hasSunroof;
public static class Builder {
private String color;
private boolean hasSunroof;
public Builder setColor(String color) {
this.color = color;
return this;
}
public Builder setSunroof(boolean hasSunroof) {
this.hasSunroof = hasSunroof;
return this;
}
public Car build() {
return new Car(this);
}
}
private Car(Builder builder) {
this.color = builder.color;
this.hasSunroof = builder.hasSunroof;
}
}
Code Example
class ParkingSpot {
private boolean isOccupied;
public void occupy() {
isOccupied = true;
}
public void vacate() {
isOccupied = false;
}
}
class Vehicle {
private String licensePlate;
}
class ParkingLot {
private List spots;
public ParkingLot(int capacity) {
spots = new ArrayList<>();
for (int i = 0; i < capacity; i++) {
spots.add(new ParkingSpot());
}
}
public ParkingSpot findSpot() {
for (ParkingSpot spot : spots) {
if (!spot.isOccupied()) return spot;
}
return null;
}
}
Code Example
abstract class Notification {
abstract void send(String message);
}
class EmailNotification extends Notification {
void send(String message) {
System.out.println("Email sent: " + message);
}
}
class SMSNotification extends Notification {
void send(String message) {
System.out.println("SMS sent: " + message);
}
}
class NotificationService {
public void notifyUser(Notification notification, String message) {
notification.send(message);
}
}