Java Handbook – Chapter 1: OOP Basics (Detailed)
Prepared in the style of an IIT professor + Google recruiter
1. What is Object-Oriented Programming (OOP)?
Object-Oriented Programming is a paradigm where software is designed as a collection of **objects**—
self-contained units that bundle **state (fields)** and **behavior (methods)**. Instead of writing functions
that operate on raw data, OOP models **real-world entities** so that code becomes more **modular,
reusable, and maintainable**.
Why OOP? (Benefits)
Benefit What it means in practice
Modularity Break systems into classes; each class has a single responsibility.
Reusability Reuse via inheritance and composition; avoid copy–paste.
Maintainability Encapsulation limits ripple effects; safer refactoring.
Extensibility Polymorphism lets you add new behavior with minimal changes.
Security Access modifiers hide internal state; validate through methods.
2. Objects & Classes
A **Class** is a blueprint or template. An **Object** is a runtime instance created from that blueprint.
Each object has its own copy of instance fields and can invoke the class's methods.
// Class (blueprint) + Object (instance)
class Student {
String name;
int age;
private double cgpa; // encapsulated field
Student(String name, int age, double cgpa) { // constructor
[Link] = name;
[Link] = age;
[Link] = cgpa;
}
void introduce() {
[Link]("Hi, I'm " + name + ", age " + age + ", CGPA " + cgpa);
}
// Getter demonstrates controlled access (encapsulation)
public double getCgpa() {
return cgpa;
}
}
public class Demo {
public static void main(String[] args) {
Student s1 = new Student("Riya", 21, 8.7);
Student s2 = new Student("Arjun", 22, 9.1);
[Link]();
[Link]();
[Link]("Riya's CGPA (via getter): " + [Link]());
}
}
Interview tip: Constructors initialize objects; they have no return type and their name equals the class
name.
3. The Four Pillars of OOP
3.1 Encapsulation
Encapsulation **hides internal representation** and exposes a clean public API. Mark fields **private**
and provide **getters/setters** with validation. This prevents invalid state and reduces coupling.
class BankAccount {
private double balance;
public void deposit(double amount) {
if (amount <= 0) throw new IllegalArgumentException("Amount must be positive");
balance += amount;
}
public boolean withdraw(double amount) {
if (amount <= 0 || amount > balance) return false;
balance -= amount;
return true;
}
public double getBalance() { return balance; }
}
Key point: Changing the private field type later **does not break** other classes that use the API.
3.2 Inheritance
Inheritance lets a subclass **reuse** and **extend** behavior of a superclass. Use it for **is-a**
relationships, not just for code reuse. Prefer **composition** over inheritance when the relationship is not
is-a.
class Animal {
void eat() { [Link]("Animal eats"); }
}
class Tiger extends Animal { // Tiger is-an Animal
void hunt() { [Link]("Tiger hunts"); }
}
public class TestInheritance {
public static void main(String[] args) {
Tiger t = new Tiger();
[Link](); // inherited
[Link](); // specific
}
}
Warning: Don't use inheritance when **has-a** fits better (e.g., Car has-an Engine).
3.3 Polymorphism
Polymorphism means **one name, many forms**. In Java, it appears as **method overloading
(compile-time)** and **method overriding (runtime dispatch via dynamic binding)**.
// Overriding (Runtime Polymorphism)
class Animal {
void sound() { [Link]("Some sound"); }
}
class Dog extends Animal {
@Override void sound() { [Link]("Bark"); }
}
class Cat extends Animal {
@Override void sound() { [Link]("Meow"); }
}
public class PolyDemo {
public static void main(String[] args) {
Animal a = new Dog(); // reference type Animal, object Dog
[Link](); // Bark (runtime dispatch)
a = new Cat();
[Link](); // Meow
}
}
// Overloading (Compile-time Polymorphism)
class MathUtil {
int sum(int a, int b) { return a + b; }
double sum(double a, double b) { return a + b; }
int sum(int a, int b, int c) { return a + b + c; }
}
Interview tip: Overriding depends on object type; overloading depends on method signature at
compile-time.
3.4 Abstraction
Abstraction focuses on **what** an object does, not **how** it does it. Model behavior using **abstract
classes** and **interfaces**. Users depend on the abstraction; implementations can vary without
breaking clients.
abstract class Animal {
abstract void eat();
abstract void sleep();
void breathe() { [Link]("Breathing..."); } // concrete method allowed
}
class Cow extends Animal {
@Override void eat() { [Link]("Cow eats grass"); }
@Override void sleep() { [Link]("Cow sleeps"); }
}
interface Runner {
void run();
}
class Tiger extends Animal implements Runner {
@Override void eat() { [Link]("Tiger eats both veg & non-veg"); }
@Override void sleep() { [Link]("Tiger sleeps"); }
@Override public void run() { [Link]("Tiger runs fast"); }
}
Key point: Prefer programming to interfaces to enable easy swapping of implementations.
4. `this`, `super`, and Variable Shadowing
`this` refers to the current object; `super` refers to the immediate superclass. If a local variable or field
**shadows** a field with the same name, `[Link]` accesses the current class's field, while `[Link]`
accesses the parent's field.
class M { int n = 900; }
class Q extends M { int n = 100; }
class T extends Q {
int n = 200;
void m1() {
int n = 300;
[Link]("n: " + n); // 300 (local)
[Link]("this.n: " + this.n); // 200 (T's field)
[Link]("super.n: " + super.n); // 100 (Q's field)
}
}
public class DemoShadow {
public static void main(String[] args) {
new T().m1();
}
}
Interview tip: Use `super(...)` to call the superclass constructor as the first line.
5. The `final` Keyword
`final` makes the reference or definition **immutable** in different contexts: - final variable → cannot be
reassigned (for objects, reference is constant; object state may still change). - final method → cannot be
overridden in subclasses. - final class → cannot be extended.
class Car {
final String brandName;
Car(String brandName) { [Link] = brandName; }
// brandName cannot be reassigned after construction.
}
final class Constants { public static final double PI = 3.1415926535; } // cannot extend
class A { final void m() { [Link]("Can't override me"); } }
class B extends A {
// void m() { } // Compilation error
}
6. Composition vs Inheritance
Prefer **composition** when modeling **has-a** relationships. Example: A Car *has an* Engine and *has
a* Driver. This yields better flexibility and testability than deep inheritance chains.
class Engine {
private final int hp;
private final int torque;
Engine(int hp, int torque) { [Link] = hp; [Link] = torque; }
@Override public String toString() { return "Engine{hp=" + hp + ", torque=" + torque + "}"; }
}
class Driver {
private final String name; private final int age;
Driver(String name, int age) { [Link] = name; [Link] = age; }
@Override public String toString() { return "Driver{name='" + name + "', age=" + age + "}"; }
}
class Car {
private String name, color;
private final Engine engine; // composition
private Driver driver; // aggregation
Car(String name, String color, Engine engine) {
[Link] = name; [Link] = color; [Link] = engine;
}
void setDriver(Driver d) { [Link] = d; }
@Override public String toString() { return "Car{name="+name+", color="+color+", engine="+engi
}
public class AssocDemo {
public static void main(String[] args) {
Driver ashok = new Driver("Ashok", 30);
Car thar = new Car("Thar Roxx", "Black", new Engine(1120, 440));
[Link](ashok);
[Link](thar);
}
}
7. Common Pitfalls & Interview Gotchas
Topic Gotcha / Clarification
Overloading vs Overriding Overloading picks by signature at compile-time; overriding binds by object type at runtime.
Access Modifiers Cannot reduce method visibility when overriding (e.g., public → private is illegal).
Constructors They don't have return types; call to super() must be the first statement if used.
Downcasting Requires explicit cast and is unsafe unless instanceof check is used.
equals vs == `==` checks reference equality for objects; `.equals()` can be overridden to check value eq
8. Practice – MCQs, Conceptual, Coding
MCQs (answer after each):
1) Which of the following enables runtime method dispatch?
• a) Method overloading
• b) Method overriding
• c) Constructor chaining
• d) Static binding
Answer: b
2) Which keyword prevents inheritance of a class?
• a) static
• b) final
• c) private
• d) abstract
Answer: b
3) Which access modifier allows visibility within package and subclasses (outside package)?
• a) private
• b) default
• c) protected
• d) public
Answer: c
Short Theory:
1) Explain encapsulation with a code snippet that validates state.
2) Differentiate abstraction vs encapsulation with real-world examples.
3) Why is composition often preferred over inheritance? Provide two reasons.
Coding Exercises:
A) Create an abstract class Vehicle with abstract methods start() and stop(), and a concrete metho
Implement classes Car and Bike. Demonstrate runtime polymorphism using a Vehicle reference.
B) Write a class MathUtil that overloads a method area() for:
- circle (double radius),
- rectangle (double w, double h),
- triangle (double base, double height).
C) Given classes Animal, Dog, Cat with overridden sound(), write a function printSound(Animal a)
that prints sound for any animal. Show dynamic dispatch.