Skip to content
Dev Dump

๐ŸŽฏ Object-Oriented Programming (OOP) in Java

Java is a pure object-oriented language (except for primitive types). Most of the power and flexibility in Java comes from its support for OOP principles. This section is critical for interviews, both for concept-based and coding questions.

6bb630bb1c50b68fdeee9f033b382644_MD5

BenefitDescription
ModularitySeparation of concerns - each class has a single responsibility
ReusabilityComponents can be reused across projects
ExtensibilityEasy to extend functionality without modifying existing code
MaintainabilityOrganized and clean codebase
Lower Cost & Faster DevelopmentDue to reusable code and better organization

Java follows four main OOP principles:

  1. Inheritance - Code reuse through โ€œis-aโ€ relationships
  2. Polymorphism - Same interface, different implementations
  3. Encapsulation - Data hiding and controlled access
  4. Abstraction - Hiding complexity, showing only essentials

778c854ff2b2151db41b17d036327b36_MD5


Inheritance allows a class (child/subclass) to derive properties and methods from another class (parent/superclass).

  • Keyword: extends
  • Purpose: Promotes code reuse and establishes an โ€œis-aโ€ relationship
  • Relationship: Child class is-a type of parent class
class DerivedClass extends BaseClass {
// methods and fields
}
  • โœ… Code Reuse - Donโ€™t repeat code across similar classes
  • โœ… Establishes โ€œis-aโ€ Relationship - Clear hierarchy and relationships
  • โœ… Polymorphism - Can use child objects where parent types are expected

f972e8ac415c2ff1a5aea59d72af0863_MD5

485dd28b0c9fbbb22bc097192720332f_MD5

447126bd744fc64ccc6d8ca0033abde7_MD5

class Animal { }
class Dog extends Animal { } // Dog inherits from Animal

f2f5f98641aa2c076945bddd49b10073_MD5

class Animal { }
class Mammal extends Animal { }
class Dog extends Mammal { } // Dog inherits from Mammal, which inherits from Animal

bd27e65540d335a73afe835ff6a401fb_MD5

class Animal { }
class Dog extends Animal { }
class Cat extends Animal { } // Both Dog and Cat inherit from Animal

1ae76a75817f9b62cfad3c35dc0ddac4_MD5

interface Flyable { }
interface Swimmable { }
class Duck implements Flyable, Swimmable { } // Duck can fly and swim
  • โœ… Public and protected members (fields and methods)
  • โœ… Default (package-private) members within the same package
  • โŒ Private members - but can be accessed via getters/setters
  • โŒ Constructors - but super() can be used to call parent constructor
  • Inherit as-is - Use parentโ€™s implementation
  • Override methods - Provide new implementation
  • Hide static methods - Define new static method with same signature
  • Add new members - Extend functionality
RuleExplanation
Cannot OverrideConstructors are tied to the class, not inherited
Can OverloadMultiple constructors with different parameters
super() and this()Only one allowed, must be first line
Default ConstructorInjected only if no custom constructors exist

โŒ Critical Rule: Never Use super() and this() Together

Section titled โ€œโŒ Critical Rule: Never Use super() and this() Togetherโ€
public class MyClass {
MyClass() {
this(5); // โŒ this() must be first
super(); // โŒ super() also must be first โ€” this is illegal!
}
MyClass(int x) {
// constructor logic
}
}

Correct Usage:

public class MyClass {
MyClass() {
this(5); // โœ… this() is first
}
MyClass(int x) {
super(); // โœ… super() is first
// constructor logic
}
}

Polymorphism is the ability of an object to take on many forms. It allows you to perform a single action in different ways, depending on the object that is performing the action.

  • Method Overloading - Same method name, different parameters
  • Operator Overloading - Limited in Java (mainly + operator)
  • Method Overriding - Subclass provides specific implementation
  • Uses upcasting and dynamic dispatch
class Animal {
Animal get() { return new Animal(); }
}
class Dog extends Animal {
Dog get() { return new Dog(); } // โœ… More specific return type
}
public class MultipleMain {
public static void main(String[] args) {
System.out.println("Main with String[] args");
}
public static void main(String arg) {
System.out.println("Main with String arg");
}
public static void main() {
System.out.println("Main with no args");
}
}
class Parent {
Number getValue() { return 0; }
}
class Child extends Parent {
Integer getValue() { return 1; } // โœ… Integer extends Number
}

9b6c17417a0b234ae8b0c21dca1c56a5_MD5

  • Change in number of arguments
  • Change in type of arguments
  • Return type alone is NOT sufficient for overloading
public class Calculator {
// Overloaded sum with different number of parameters
public int sum(int x, int y) {
return (x + y);
}
public int sum(int x, int y, int z) {
return (x + y + z);
}
// Overloaded sum with different argument types
public double sum(double x, double y) {
return (x + y);
}
public static void main(String args[]) {
Calculator calc = new Calculator();
System.out.println(calc.sum(10, 20)); // Output: 30
System.out.println(calc.sum(10, 20, 30)); // Output: 60
System.out.println(calc.sum(10.5, 20.5)); // Output: 31.0
}
}
class OperatorDemo {
void operator(String str1, String str2) {
String s = str1 + str2;
System.out.println("Concatenated String - " + s);
}
void operator(int a, int b) {
int c = a + b;
System.out.println("Sum = " + c);
}
}
class Main {
public static void main(String[] args) {
OperatorDemo obj = new OperatorDemo();
obj.operator(2, 3); // Output: Sum = 5
obj.operator("Hello", "World"); // Output: Concatenated String - HelloWorld
}
}
  • Same method signature (name, return type, parameters)
  • Same or covariant return type
  • Same or broader access modifier
  • Same or narrower exception specification
class Parent {
void show() {
System.out.println("Parent's show()");
}
}
class Child extends Parent {
// This method overrides show() of Parent
@Override
void show() {
System.out.println("Child's show()");
}
}
class Main {
public static void main(String[] args) {
Parent obj;
// Parent reference, Parent object
obj = new Parent();
obj.show(); // Output: Parent's show()
// Parent reference, Child object - RUNTIME POLYMORPHISM!
obj = new Child();
obj.show(); // Output: Child's show()
}
}
class Parent {
void m1() {
System.out.println("From parent m1()");
}
}
class Child extends Parent {
@Override
// โœ… No issue while throwing unchecked exception
void m1() throws ArithmeticException {
System.out.println("From child m1()");
}
@Override
// โŒ Compile-time error while throwing checked exception
void m2() throws Exception {
System.out.println("From child m2");
}
}
class Parent {
void m1() throws RuntimeException {
System.out.println("From parent m1()");
}
}
class Child1 extends Parent {
@Override
// โœ… No issue while throwing same exception
void m1() throws RuntimeException {
System.out.println("From child1 m1()");
}
}
class Child2 extends Parent {
@Override
// โœ… No issue while throwing subclass exception
void m1() throws ArithmeticException {
System.out.println("From child2 m1()");
}
}
class Child3 extends Parent {
@Override
// โœ… No issue while not throwing any exception
void m1() {
System.out.println("From child3 m1()");
}
}
class Child4 extends Parent {
@Override
// โŒ Compile-time error while throwing parent exception
void m1() throws Exception {
System.out.println("From child4 m1()");
}
}

800744b2a21006b2d6875421803e5973_MD5


Encapsulation = data hiding + controlled access using getters/setters.

  1. Make variables private - Hide data from outside world
  2. Provide public getters and setters - Control access to data

5489ea11db4964e77209b0e08c5fa30c_MD5

public class Person {
// Private fields - data is hidden
private String name;
private int age;
private String email;
// Constructor
public Person(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
// Getter for name
public String getName() {
return name;
}
// Setter for name with validation
public void setName(String newName) {
if (newName != null && !newName.trim().isEmpty()) {
this.name = newName.trim();
}
}
// Getter for age
public int getAge() {
return age;
}
// Setter for age with validation
public void setAge(int newAge) {
if (newAge >= 0 && newAge <= 150) {
this.age = newAge;
}
}
// Getter for email
public String getEmail() {
return email;
}
// Setter for email with validation
public void setEmail(String newEmail) {
if (newEmail != null && newEmail.contains("@")) {
this.email = newEmail;
}
}
}
// Using the encapsulated class
class Main {
public static void main(String[] args) {
Person person = new Person("Alice", 30, "alice@email.com");
// Access data through controlled methods
System.out.println("Name: " + person.getName());
System.out.println("Age: " + person.getAge());
// Modify data through controlled methods
person.setAge(31);
person.setEmail("alice.new@email.com");
System.out.println("Updated Age: " + person.getAge());
System.out.println("Updated Email: " + person.getEmail());
}
}
  • Data Protection - External code cannot directly modify private data
  • Flexibility & Modularity - Can change internal implementation without affecting external code
  • Data Validation - Can add validation logic in setters
  • Maintainability - Easier to maintain and modify code

Abstraction = hiding implementation and exposing essential features.

Think of a car: You view it as a car with essential features (start, stop, accelerate, brake) rather than its individual components (engine, transmission, brakes).

  • Abstract methods - Methods without implementation
  • Concrete methods - Methods with implementation
  • Cannot be instantiated directly
abstract class Shape {
protected String color;
// Constructor
public Shape(String color) {
this.color = color;
}
// Abstract method - must be implemented by subclasses
public abstract double calculateArea();
// Concrete method - has implementation
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
class Circle extends Shape {
private double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
class Rectangle extends Shape {
private double length;
private double width;
public Rectangle(String color, double length, double width) {
super(color);
this.length = length;
this.width = width;
}
@Override
public double calculateArea() {
return length * width;
}
}

FeatureEncapsulationAbstraction
PurposeData hidingImplementation hiding
AccessVia getters/settersVia abstract classes/interfaces
FocusHow data is accessedWhat functionalities are exposed
LevelLow-level (data)High-level (design)

  • All methods are implicitly public and abstract
  • All fields are public static final
  • Used for multiple inheritance and 100% abstraction
  • A class can implement multiple interfaces
  • An interface can extend multiple interfaces

๐ŸŒŸ Why Use Interfaces When We Have Abstract Classes?

Section titled โ€œ๐ŸŒŸ Why Use Interfaces When We Have Abstract Classes?โ€
AspectAbstract ClassInterface
VariablesCan contain non-final variablesVariables are implicitly final, public, and static
InheritanceA class can extend only one abstract classA class can implement multiple interfaces
ConstructorCan have constructorsCannot have constructors
Method ImplementationCan have concrete methodsAll methods are abstract (before Java 8)
interface Flyable {
// public, static and final
final int MAX_ALTITUDE = 10000;
// public and abstract
void fly();
void land();
}
interface Swimmable {
void swim();
void dive();
}
// A class that implements multiple interfaces
class Duck implements Flyable, Swimmable {
@Override
public void fly() {
System.out.println("Duck is flying");
}
@Override
public void land() {
System.out.println("Duck is landing");
}
@Override
public void swim() {
System.out.println("Duck is swimming");
}
@Override
public void dive() {
System.out.println("Duck is diving");
}
public static void main(String[] args) {
Duck duck = new Duck();
duck.fly();
duck.swim();
System.out.println("Max altitude: " + MAX_ALTITUDE);
}
}

Output:

Duck is flying
Duck is swimming
Max altitude: 10000

375855c27120bd8cfaefa6b8f3b66941_MD5


ConceptSummary
InheritanceCode reuse via โ€œis-aโ€ relationship using extends
OverloadingSame method name, different parameters (compile-time)
OverridingSubclass redefines superclass method (runtime)
EncapsulationPrivate data + public accessors for controlled access
AbstractionHide implementation, show only functionality
Interface100% abstraction, enables multiple inheritance