โ† Back | Java & OOP Fundamentals
Week 1
Week 1 ยท Core Backend

Java & OOP Fundamentals

Object-Oriented Programming is the foundation of Spring Boot and every Java framework. Master these concepts and you'll understand why Spring works the way it does.

โ˜• Java 21 4 OOP Pillars Interview Essentials
๐Ÿง 
Concept
Why Java? The Big Picture
Analogy: Java is like English in the business world. It's not the newest language, but it runs on more systems than any other language, has the largest ecosystem of tools and libraries, and almost every company (banks, e-commerce, healthcare) uses it. Learning Java = maximum employability.
FeatureWhat it means for you
Platform IndependentWrite once, run on Windows/Mac/Linux via JVM
Strongly TypedErrors are caught at compile time, not at 3am in production
Object-OrientedReal-world concepts map to code (User, Order, Product)
Massive EcosystemSpring, Hibernate, Kafka, Maven โ€” everything exists
Enterprise AdoptionLinkedIn, Uber, Amazon, banks โ€” all use Java backends
๐Ÿ”ง
Foundation
Core Java Basics for Microservices
Variables ยท Conditions ยท Loops ยท Methods ยท Constructors ยท Exceptions ยท Strings
These are the building blocks you will write every single day in Spring Boot. Master these before touching annotations or Spring concepts.

1. Variables & Data Types

// โ”€โ”€ Primitive types โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ int age = 21; // whole numbers (-2B to 2B) long id = 100_000_000L; // large whole numbers (use for DB IDs) double price = 99.99; // decimal numbers boolean active = true; // true / false char grade = 'A'; // single character // โ”€โ”€ Reference types โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ String name = "Rahul"; // text (immutable object) Integer score = 95; // int wrapper โ€” needed in Lists/Maps // โ”€โ”€ var (Java 10+) โ€” type inferred by compiler โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ var city = "Mumbai"; // compiler knows it's String var count = 10; // compiler knows it's int // โ”€โ”€ final โ€” constant, cannot be reassigned โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ final int MAX_RETRIES = 3; // convention: ALL_CAPS for constants // โ”€โ”€ Null safety โ€” always check before using โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ String email = null; if (email != null && !email.isEmpty()) { System.out.println(email.toLowerCase()); }
In Spring Boot โ€” use Long (not int) for entity IDs to match database BIGINT. Use String for any text field. Use boolean for flags like isActive, isVerified.

2. Conditions

// โ”€โ”€ if / else if / else โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ int marks = 75; if (marks >= 90) { System.out.println("A grade"); } else if (marks >= 60) { System.out.println("B grade"); // โ† this runs } else { System.out.println("Fail"); } // โ”€โ”€ Ternary operator โ€” one-line if/else โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ String status = (marks >= 60) ? "Pass" : "Fail"; // "Pass" // โ”€โ”€ Switch expression (Java 14+) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ String day = "MON"; String type = switch (day) { case "MON", "TUE", "WED", "THU", "FRI" -> "Weekday"; case "SAT", "SUN" -> "Weekend"; default -> "Unknown"; }; // "Weekday" // โ”€โ”€ Logical operators โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ // && (AND) โ€” both must be true // || (OR) โ€” at least one must be true // ! (NOT) โ€” reverses boolean boolean canLogin = (age >= 18) && active && (email != null);

3. Loops

// โ”€โ”€ for loop โ€” when you know the count โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ for (int i = 0; i < 5; i++) { System.out.println("Step " + i); } // โ”€โ”€ for-each โ€” most used in Spring Boot (iterating lists) โ”€ List<String> names = List.of("Rahul", "Priya", "Amit"); for (String name : names) { System.out.println(name); // Rahul, Priya, Amit } // โ”€โ”€ while loop โ€” when condition drives the loop โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ int retries = 0; while (retries < 3) { System.out.println("Retrying... attempt " + (retries + 1)); retries++; } // โ”€โ”€ break & continue โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ for (int i = 0; i < 10; i++) { if (i == 3) continue; // skip 3 if (i == 7) break; // stop at 7 System.out.print(i + " "); // 0 1 2 4 5 6 }
In Spring Boot โ€” for-each and Streams (from Java 8) are the two ways you process lists. Use for-each when you need simple iteration; use Streams when you need filter/map/collect pipelines.

4. Methods (Functions)

// โ”€โ”€ Method anatomy โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ // [access] [static?] returnType methodName(params) { body } // void โ€” returns nothing public void printGreeting(String name) { System.out.println("Hello, " + name); } // returns a value public int add(int a, int b) { return a + b; } // boolean check โ€” common in service layer validation public boolean isValidEmail(String email) { return email != null && email.contains("@"); } // returns an object โ€” typical Spring service method public Student findById(Long id) { // query DB โ†’ return student return studentRepository.findById(id) .orElseThrow(() -> new RuntimeException("Student not found")); } // multiple return paths public String getGrade(int marks) { if (marks >= 90) return "A"; if (marks >= 75) return "B"; if (marks >= 60) return "C"; return "F"; }

5. Constructors

public class Order { private Long id; private String product; private int quantity; // 1. No-arg constructor โ€” required by JPA/Jackson/Hibernate public Order() {} // 2. Parameterized constructor โ€” useful in tests & service layer public Order(String product, int quantity) { this.product = product; // this.x = parameter x this.quantity = quantity; } // 3. Constructor chaining with this() public Order(String product) { this(product, 1); // calls constructor above with quantity=1 } } // Usage Order o1 = new Order("Laptop", 2); Order o2 = new Order("Mouse"); // quantity defaults to 1 Order o3 = new Order(); // empty order โ€” JPA needs this
Spring Boot rule: If you use @Entity (JPA) or return objects as JSON (Jackson), your class must have a no-arg constructor. Use @NoArgsConstructor from Lombok to generate it automatically.

6. Exception Handling โ€” Critical for REST APIs

// โ”€โ”€ try / catch / finally โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ try { String text = null; text.length(); // throws NullPointerException } catch (NullPointerException e) { System.out.println("Caught: " + e.getMessage()); } catch (Exception e) { // catch-all (put LAST) System.out.println("Unexpected: " + e.getMessage()); } finally { System.out.println("Always runs โ€” good for cleanup"); } // โ”€โ”€ Checked vs Unchecked โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ // Checked โ€” must handle or declare: IOException, SQLException // Unchecked โ€” RuntimeException subclasses: NPE, IndexOutOfBounds // โ”€โ”€ throws โ€” declare that a method may throw โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ public void readFile(String path) throws IOException { Files.readAllLines(Path.of(path)); // caller must handle } // โ”€โ”€ Custom exception โ€” used in every Spring Boot service โ”€โ”€ public class StudentNotFoundException extends RuntimeException { public StudentNotFoundException(Long id) { super("Student not found with id: " + id); } } // โ”€โ”€ throw โ€” manually raise an exception โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ public Student getStudent(Long id) { return repository.findById(id) .orElseThrow(() -> new StudentNotFoundException(id)); }
Spring Boot pattern: Create a custom exception per resource (OrderNotFoundException, UserNotFoundException), extend RuntimeException, and use @RestControllerAdvice to catch them globally and return proper HTTP 404 responses.

7. String Manipulation โ€” Used daily in REST APIs

String s = " Hello, Spring Boot! "; // โ”€โ”€ Common methods โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ s.trim(); // "Hello, Spring Boot!" (remove spaces) s.toLowerCase(); // " hello, spring boot! " s.toUpperCase(); // " HELLO, SPRING BOOT! " s.contains("Spring"); // true s.startsWith(" Hello"); // true s.endsWith("! "); // true s.replace("Spring", "Java"); // " Hello, Java Boot! " s.trim().split(","); // ["Hello", " Spring Boot!"] s.trim().length(); // 20 s.isEmpty(); // false s.trim().isBlank(); // false (isBlank checks after trim too) // โ”€โ”€ String.format โ€” building response messages โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ String msg = String.format("Student %s enrolled in %s", "Rahul", "Java"); // "Student Rahul enrolled in Java" // โ”€โ”€ String.valueOf โ€” convert number to String โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ String numStr = String.valueOf(42); // "42" int num = Integer.parseInt("42"); // 42 (String โ†’ int) // โ”€โ”€ StringBuilder โ€” when building strings in a loop โ”€โ”€โ”€โ”€โ”€โ”€ StringBuilder sb = new StringBuilder(); for (String tag : List.of("java", "spring", "cloud")) { sb.append(tag).append(", "); } String result = sb.toString(); // "java, spring, cloud, " // โ”€โ”€ equals โ€” NEVER use == for String comparison โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ String role = "ADMIN"; if (role.equals("ADMIN")) { /* grant access */ } if (role.equalsIgnoreCase("admin")) { /* also works */ }

8. Access Modifiers โ€” Who can see what

ModifierSame classSame packageSubclassEverywhereUse in Spring Boot
publicโœ…โœ…โœ…โœ…Controller methods, service interfaces, entity getters
privateโœ…โŒโŒโŒEntity fields, internal helper methods
protectedโœ…โœ…โœ…โŒBase class methods meant for subclasses only
(default)โœ…โœ…โŒโŒPackage-private utilities (rare in Spring)
Golden rule in Spring Boot: Entity fields โ†’ private. Service and controller methods โ†’ public. Helper methods that should not be called from outside โ†’ private.
๐Ÿ“ฆ
Core Concept
Classes and Objects
The building blocks of Java
A Class is a blueprint. An Object is the real thing built from that blueprint.
Class = blueprint of a house (walls, rooms, doors). Object = the actual house you live in.
You can create many houses (objects) from the same blueprint (class).
Java โ€” Class definition
// Class = blueprint for a Student public class Student { // Fields (attributes) โ€” what a student HAS String name; int age; String email; // Constructor โ€” called when creating a new object public Student(String name, int age, String email) { this.name = name; this.age = age; this.email = email; } // Method โ€” what a student DOES public void study() { System.out.println(name + " is studying."); } } // Creating objects (instances) Student s1 = new Student("Rahul", 21, "rahul@email.com"); Student s2 = new Student("Priya", 22, "priya@email.com"); s1.study(); // Output: Rahul is studying. s2.study(); // Output: Priya is studying.
this keyword โ€” refers to the current object. Used to distinguish between the field name and the constructor parameter name when they have the same name.
๐Ÿ›๏ธ
Core Concept
The 4 Pillars of OOP
The most asked interview topic in Java

๐Ÿ”’ Encapsulation

Hide internal data. Expose only what's needed. (private fields + public getters/setters)

๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ Inheritance

Child class inherits fields and methods from parent class. Reuse code. (extends)

๐ŸŽญ Polymorphism

One interface, many implementations. Same method name, different behaviour. (overriding)

๐ŸŽจ Abstraction

Hide complexity. Show only the essential interface. (abstract class, interface)

๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ
Pillar 1
Inheritance
Child class reuses parent class code
A Dog is an Animal. A Car is a Vehicle. In Java, when class B "is a" class A, use inheritance. Child gets all parent's non-private members, plus can add its own.
public class Animal { String name; public void breathe() { System.out.println(name + " is breathing."); } } // Dog extends (inherits from) Animal public class Dog extends Animal { // Dog has its own additional method public void bark() { System.out.println(name + " says: Woof!"); } } Dog d = new Dog(); d.name = "Bruno"; d.breathe(); // Inherited from Animal โ†’ "Bruno is breathing." d.bark(); // Dog's own method โ†’ "Bruno says: Woof!"
Java supports single inheritance only. A class can extend only ONE parent class. To achieve multiple inheritance behaviour, use Interfaces.
๐ŸŽญ
Pillar 2
Polymorphism
One name, many forms
A "payment" method works differently depending on whether you use a credit card, UPI, or cash. Same operation (pay), different behaviour based on the actual type. That's polymorphism.

Method Overriding (Runtime Polymorphism)

public class Animal { public void makeSound() { System.out.println("Some generic sound"); } } public class Dog extends Animal { // @Override tells the compiler we're intentionally overriding @Override public void makeSound() { System.out.println("Woof!"); } } public class Cat extends Animal { @Override public void makeSound() { System.out.println("Meow!"); } } // Parent type reference, child type object Animal a = new Dog(); // works! Dog IS an Animal a.makeSound(); // Output: Woof! (decided at runtime) Animal b = new Cat(); b.makeSound(); // Output: Meow!

Method Overloading (Compile-time Polymorphism)

// Same method name, different parameters public class Calculator { public int add(int a, int b) { return a + b; } public double add(double a, double b) { return a + b; } public int add(int a, int b, int c) { return a + b + c; } }
๐Ÿ”’
Pillar 3
Encapsulation
Hide the data, expose the interface
An ATM machine is a great example. You can't directly access the cash inside (private). You use the buttons and card slot (public methods) to interact with it. The internal mechanism is hidden โ€” encapsulated.
public class BankAccount { // private = no direct access from outside private double balance; // Public getter โ€” read-only access public double getBalance() { return balance; } // Public method โ€” controlled access with validation public void deposit(double amount) { if (amount > 0) { balance += amount; } else { System.out.println("Invalid deposit amount"); } } public void withdraw(double amount) { if (amount > 0 && amount <= balance) { balance -= amount; } else { System.out.println("Insufficient funds"); } } } BankAccount acc = new BankAccount(); acc.deposit(1000); acc.withdraw(500); System.out.println(acc.getBalance()); // 500.0 // acc.balance = -999; // ERROR! Cannot access private field
๐ŸŽจ
Pillar 4
Abstraction
Define what to do, not how to do it
When you drive a car, you use the steering wheel and pedals. You don't need to know how the engine combustion works. The car provides an abstract interface (drive, brake) and hides the complexity underneath.
// Abstract class โ€” cannot be instantiated directly public abstract class Shape { // Abstract method โ€” subclasses MUST implement this public abstract double area(); // Regular method โ€” shared behaviour public void display() { System.out.println("Area = " + area()); } } public class Circle extends Shape { private double radius; public Circle(double radius) { this.radius = radius; } @Override public double area() { return Math.PI * radius * radius; } } Shape s = new Circle(5); s.display(); // Area = 78.539...
๐Ÿ”Œ
Must Know
Abstract Class vs Interface
The most common interview question in Java
FeatureAbstract ClassInterface
Keywordabstract classinterface
MethodsCan have abstract + concrete methodsAll methods abstract by default (Java 8+: can have default methods)
FieldsCan have instance variablesOnly public static final constants
ConstructorYesNo
InheritanceSingle (extends one class)Multiple (implements many interfaces)
Use whenClasses share code (IS-A relationship)Classes share capability (CAN-DO relationship)
public interface Flyable { void fly(); // abstract by default } public interface Swimmable { void swim(); } // Duck can both fly AND swim โ€” multiple interfaces public class Duck extends Animal implements Flyable, Swimmable { public void fly() { System.out.println("Duck flying"); } public void swim() { System.out.println("Duck swimming"); } }
โšก
Extra Topics
Static, equals(), and Generics

static keyword

public class MathUtils { // static method โ€” call without creating an object public static int square(int n) { return n * n; } // static field โ€” shared by ALL instances public static int instanceCount = 0; } // Call static method directly on class โ€” no new MathUtils() int result = MathUtils.square(5); // 25

== vs .equals()

String a = new String("hello"); String b = new String("hello"); System.out.println(a == b); // false โ€” different memory addresses System.out.println(a.equals(b)); // true โ€” same content // Rule: Use == for primitives (int, boolean), .equals() for objects

Generics (brief introduction)

// Without generics โ€” can store anything, needs casting List list = new ArrayList(); list.add("hello"); String s = (String) list.get(0); // cast needed // With generics โ€” type-safe, no casting needed List<String> names = new ArrayList<>(); names.add("Rahul"); String name = names.get(0); // no cast needed // names.add(42); // ERROR at compile time โ€” only Strings allowed
๐Ÿ“ฆ
Must Know ยท Interview Favourite
Collection Framework
The right data structure is half the answer in any interview
Analogy: Collections are like different types of storage containers. Use a List when order matters and duplicates are fine (like a shopping cart). Use a Set when you need uniqueness (like a set of usernames). Use a Map when you need key-value lookup (like a phone book: name โ†’ number).

Collection Hierarchy (memorise this)

// java.util โ€” root interfaces and classes Iterable โ””โ”€โ”€ Collection โ”œโ”€โ”€ List โ† ordered, duplicates allowed, indexed โ”‚ โ”œโ”€โ”€ ArrayList โ”‚ โ”œโ”€โ”€ LinkedList โ”‚ โ””โ”€โ”€ Vector (legacy, avoid) โ”œโ”€โ”€ Set โ† no duplicates โ”‚ โ”œโ”€โ”€ HashSet โ”‚ โ”œโ”€โ”€ LinkedHashSet โ”‚ โ””โ”€โ”€ TreeSet โ† sorted โ””โ”€โ”€ Queue / Deque โ† FIFO / double-ended โ”œโ”€โ”€ LinkedList (also Queue) โ”œโ”€โ”€ PriorityQueue โ””โ”€โ”€ ArrayDeque Map โ† key-value pairs (NOT a Collection) โ”œโ”€โ”€ HashMap โ”œโ”€โ”€ LinkedHashMap โ”œโ”€โ”€ TreeMap โ† sorted by key โ””โ”€โ”€ Hashtable (legacy, avoid)

List โ€” When order and index matter

ClassBacked byget(i)add/remove (middle)Best use case
ArrayListDynamic arrayO(1) โœ…O(n)Read-heavy list, most common choice
LinkedListDoubly linked listO(n)O(1) โœ…Frequent insert/delete at head or tail
VectorSynchronized arrayO(1)O(n)Legacy โ€” use ArrayList in new code
ArrayList and LinkedList
List<String> list = new ArrayList<>(); list.add("Rahul"); list.add("Priya"); list.add("Amit"); list.add(1, "Kavya"); // insert at index 1 list.remove("Priya"); // remove by value String s = list.get(0); // "Rahul" System.out.println(list.size()); // 3 // Iterate โ€” preferred way for (String name : list) { System.out.println(name); }

Set โ€” When uniqueness matters

ClassOrderNullPerformanceUse case
HashSetNo order1 null allowedO(1) add/containsFast uniqueness check โ€” most common
LinkedHashSetInsertion order1 null allowedO(1) approxUnique + preserve insertion order
TreeSetSorted (natural or Comparator)No nullO(log n)Unique + sorted (e.g. leaderboard)
HashSet example
Set<String> skills = new HashSet<>(); skills.add("Java"); skills.add("Spring"); skills.add("Java"); // duplicate โ€” silently ignored System.out.println(skills.size()); // 2 (not 3) System.out.println(skills.contains("Spring")); // true โ€” O(1)

Map โ€” When you need key โ†’ value lookup

ClassOrderNull keyThread-safe?Use case
HashMapNo order1 null key โœ…NoGeneral purpose โ€” most common
LinkedHashMapInsertion order1 null key โœ…NoCache, LRU cache
TreeMapSorted by keyNo null keyNoRange queries, sorted output
HashtableNo orderNo nullYes (slow)Legacy โ€” use ConcurrentHashMap instead
HashMap example
Map<String, Integer> scores = new HashMap<>(); scores.put("Rahul", 85); scores.put("Priya", 92); scores.put("Amit", 78); int p = scores.get("Priya"); // 92 scores.getOrDefault("Raj", 0); // 0 โ€” safe, no NPE scores.containsKey("Amit"); // true // Iterate key-value pairs for (Map.Entry<String, Integer> entry : scores.entrySet()) { System.out.println(entry.getKey() + " โ†’ " + entry.getValue()); } // putIfAbsent, computeIfAbsent (common in caching) scores.putIfAbsent("Rahul", 100); // ignored โ€” key already exists

Collections utility class (java.util.Collections)

List<Integer> nums = new ArrayList<>(List.of(3, 1, 4, 1, 5, 9, 2, 6)); Collections.sort(nums); // [1, 1, 2, 3, 4, 5, 6, 9] Collections.reverse(nums); // [9, 6, 5, 4, 3, 2, 1, 1] Collections.shuffle(nums); // random order Collections.max(nums); // 9 Collections.frequency(nums, 1); // 2 (how many times 1 appears) Collections.unmodifiableList(nums); // returns read-only view
Interview tip: Always declare with the interface type on the left: List<String> list = new ArrayList<>() not ArrayList<String> list = .... This is called "programming to an interface" and is best practice in Java.

Collections Interview Q&A

QArrayList vs LinkedList โ€” when to use which?

Use ArrayList when: you access elements by index frequently (get(i) is O(1)), or you mostly add at the end. It's faster for iteration and random access.

Use LinkedList when: you frequently insert or delete at the beginning or middle of the list (O(1) for head/tail). However, in practice, ArrayList is almost always preferred because modern CPUs cache arrays very efficiently.

QHow does HashMap work internally?

HashMap uses an array of buckets (internally called a Node array). When you call put(key, value):

  1. Java calls key.hashCode() to get an integer hash
  2. The hash is mapped to a bucket index (using modulo on array size)
  3. The key-value pair is stored in that bucket
  4. If two keys hash to the same bucket (collision), they're stored as a linked list in the same bucket (Java 8+: becomes a balanced tree when >8 entries)

For get(key): compute hash โ†’ find bucket โ†’ compare keys using equals() โ†’ return value. Average O(1), worst case O(log n) with Java 8 tree bins.

Important: If you use a custom object as a HashMap key, you must override both hashCode() and equals().

QHashMap vs Hashtable vs ConcurrentHashMap?

HashMap โ€” Not thread-safe. Allows one null key. Fastest for single-threaded use.

Hashtable โ€” Thread-safe (synchronized on every method). No null key/value. Legacy โ€” avoid in new code, it's very slow due to full lock.

ConcurrentHashMap โ€” Thread-safe but uses segment-level locking (fine-grained), so much faster than Hashtable in concurrent environments. No null key/value. Use this in multi-threaded code.

QWhat is fail-fast vs fail-safe iterator?

Fail-fast โ€” Throws ConcurrentModificationException if the collection is modified during iteration. ArrayList, HashMap use fail-fast iterators. Prevents reading inconsistent data.

Fail-safe โ€” Iterates on a copy of the collection, so modifications during iteration don't throw an exception. CopyOnWriteArrayList, ConcurrentHashMap use fail-safe iterators. Safe for concurrent reads but uses more memory.

QWhat is the difference between Comparable and Comparator?

Comparable (java.lang) โ€” The class itself defines its natural ordering by implementing compareTo(). E.g., String, Integer already implement Comparable. Used by Collections.sort(list) with no extra argument.

Comparator (java.util) โ€” An external class that defines a custom ordering for any class. Used when you don't own the class or need multiple sort orders.

// Comparator โ€” sort students by age list.sort((s1, s2) -> s1.getAge() - s2.getAge()); // Or with method reference list.sort(Comparator.comparingInt(Student::getAge));
โ˜•
Must Know ยท Interview Favourite
Java 8 Features
Lambda, Streams, Optional โ€” asked in every Java interview
Why Java 8 matters: Java 8 (released 2014) is still the most referenced version in interviews. It brought functional programming to Java. If you can write clean Stream pipelines, you immediately stand out as a candidate.

1. Lambda Expressions โ€” Anonymous functions

A lambda is a short, unnamed function you can pass around like a value. Before Java 8, you had to create an entire anonymous class just to pass a simple function. Lambda cuts that to one line.
Before Java 8 vs Lambda
// Before Java 8 โ€” anonymous inner class (verbose) Runnable r1 = new Runnable() { @Override public void run() { System.out.println("Running..."); } }; // Java 8 Lambda โ€” same thing, one line Runnable r2 = () -> System.out.println("Running..."); // Lambda with parameters Comparator<String> byLength = (s1, s2) -> s1.length() - s2.length(); // Lambda with body block Comparator<Integer> desc = (a, b) -> { if (a > b) return -1; else if (a < b) return 1; else return 0; };

2. Functional Interfaces โ€” The contract for lambdas

A functional interface has exactly one abstract method. Lambdas implement them. Java 8 added 4 built-in ones in java.util.function:

InterfaceMethodTakesReturnsUse case
Predicate<T>test(T t)TbooleanFilter/check: is age > 18?
Function<T,R>apply(T t)TRTransform: String โ†’ Integer
Consumer<T>accept(T t)TvoidSide effect: print, save
Supplier<T>get()nothingTFactory/lazy value: () โ†’ new Obj()
Predicate<Integer> isAdult = age -> age >= 18; Function<String, Integer> strLen = s -> s.length(); Consumer<String> printer = msg -> System.out.println(msg); Supplier<List<String>> newList = () -> new ArrayList<>(); isAdult.test(20); // true strLen.apply("hi"); // 2 printer.accept("hello"); // prints: hello

3. Stream API โ€” The most interviewed Java 8 feature

A Stream is like a conveyor belt in a factory. You put raw materials (a List) on the belt, run them through a series of operations (filter โ†’ transform โ†’ collect), and get the finished product out the other end. The original list is never modified.
Stream pipeline: source โ†’ intermediate ops โ†’ terminal op
List<String> names = List.of("Rahul", "Priya", "Amit", "Ram", "Priyanka"); // filter โ€” keep elements matching condition List<String> longNames = names.stream() .filter(n -> n.length() > 4) .collect(Collectors.toList()); // [Rahul, Priya, Priyanka] // map โ€” transform each element List<String> upper = names.stream() .map(String::toUpperCase) .collect(Collectors.toList()); // [RAHUL, PRIYA, ...] // sorted, distinct, limit, skip names.stream() .filter(n -> n.startsWith("P")) .sorted() .forEach(System.out::println); // Priya, Priyanka // reduce โ€” combine all elements into one value int totalLen = names.stream() .mapToInt(String::length) .sum(); // sum of all name lengths // collect to Map Map<String, Integer> nameLengths = names.stream() .collect(Collectors.toMap(n -> n, String::length)); // {Rahul=5, Priya=5, Amit=4, ...} // anyMatch, allMatch, noneMatch boolean hasShort = names.stream().anyMatch(n -> n.length() < 4); // false // findFirst Optional<String> first = names.stream() .filter(n -> n.startsWith("P")) .findFirst(); // Optional[Priya]
OperationTypeWhat it does
filter(predicate)IntermediateKeep elements matching condition
map(function)IntermediateTransform each element to another type
flatMap(function)IntermediateFlatten nested collections into one stream
sorted()IntermediateSort elements (natural or Comparator)
distinct()IntermediateRemove duplicates
limit(n)IntermediateKeep only first n elements
collect()TerminalProduce List/Set/Map from stream
forEach()TerminalPerform action on each element
count()TerminalReturn number of elements
reduce()TerminalCombine elements into single value
anyMatch/allMatchTerminalReturn boolean
findFirst/findAnyTerminalReturn Optional

4. Optional โ€” Avoid NullPointerException

// Bad โ€” can throw NullPointerException String name = user.getAddress().getCity(); // what if address is null? // Good โ€” Optional wraps a value that might or might not be present Optional<String> city = Optional.ofNullable(user.getAddress()) .map(Address::getCity); city.isPresent(); // true if value exists city.get(); // get value (throws if empty โ€” use carefully) city.orElse("Unknown"); // return "Unknown" if empty city.orElseThrow(() -> new RuntimeException("No city")); city.ifPresent(c -> System.out.println(c)); // do something if present

5. Default Methods in Interfaces

// Before Java 8: adding a method to an interface BROKE all implementing classes // Java 8 fix: default methods โ€” have a body, implementing classes can override public interface Greeting { void hello(String name); // abstract โ€” must implement default void bye(String name) { // default โ€” optional to override System.out.println("Goodbye, " + name); } static Greeting formal() { // static factory method in interface (Java 8) return name -> System.out.println("Good day, " + name); } }

6. Method References (::)

A shorthand for a lambda that just calls an existing method.

// Lambda vs Method Reference โ€” same thing list.forEach(s -> System.out.println(s)); // lambda list.forEach(System.out::println); // method reference โ€” cleaner // Types of method references String::toUpperCase // instance method of any instance System.out::println // instance method of specific object String::new // constructor reference Integer::parseInt // static method reference // Practical example โ€” sort by name using method reference students.sort(Comparator.comparing(Student::getName)); // Map names to uppercase names.stream().map(String::toUpperCase).collect(Collectors.toList());

7. New Date/Time API (java.time)

// Old java.util.Date was broken โ€” not thread-safe, confusing months (0-indexed) // Java 8 fixed it with java.time โ€” immutable, thread-safe, clean API LocalDate today = LocalDate.now(); // 2025-11-14 (date only) LocalTime now = LocalTime.now(); // 14:30:22 (time only) LocalDateTime dt = LocalDateTime.now(); // date + time, no timezone LocalDate dob = LocalDate.of(2000, 5, 15); // May 15, 2000 long age = ChronoUnit.YEARS.between(dob, today); // age in years today.plusDays(30); // 30 days from now today.minusMonths(1); // 1 month ago today.getDayOfWeek(); // FRIDAY today.isAfter(dob); // true // Format and parse DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy"); String formatted = today.format(fmt); // "14/11/2025" LocalDate parsed = LocalDate.parse("14/11/2025", fmt);

Java 8 Interview Q&A

QWhat is a lambda expression? What problem does it solve?

A lambda is a concise way to represent an anonymous function โ€” a block of code you can pass as a parameter. It implements a functional interface.

Problem solved: Before Java 8, passing behaviour required verbose anonymous inner classes. Lambda reduces this to a one-liner: (params) -> expression. This enables functional programming patterns like passing filter/transform logic to Collections and Streams.

QWhat is the difference between map() and flatMap() in Stream?

map() transforms each element into exactly one other element. The output stream has the same number of elements.

flatMap() transforms each element into a stream of elements and then flattens all those streams into one. Use when each element maps to multiple elements.

List<List<Integer>> nested = List.of(List.of(1,2), List.of(3,4)); // map would give Stream<List<Integer>> // flatMap gives Stream<Integer> [1, 2, 3, 4] nested.stream().flatMap(Collection::stream).collect(Collectors.toList());
QWhat is Optional and when should you use it?

Optional<T> is a container that may or may not contain a non-null value. It forces the caller to handle the "no value" case explicitly instead of returning null and risking NullPointerException.

Use it: as a return type from methods that might not find a result (e.g., repository findById() returns Optional<User>).

Don't use it: as a field type, constructor parameter, or method parameter โ€” that's an anti-pattern. Optional is for return types only.

QWhat is the difference between intermediate and terminal operations in Stream?

Intermediate operations (filter, map, sorted, distinct, limit) return a new Stream. They are lazy โ€” they don't execute until a terminal operation is called. You can chain multiple intermediate ops.

Terminal operations (collect, forEach, count, reduce, findFirst, anyMatch) trigger the pipeline to execute and produce a result or side effect. Once called, the stream is consumed and cannot be reused.

QWrite a Stream pipeline: given a list of employees, find the names of employees in the "Engineering" department with salary > 50000, sorted by name.
employees.stream() .filter(e -> e.getDepartment().equals("Engineering")) .filter(e -> e.getSalary() > 50000) .map(Employee::getName) .sorted() .collect(Collectors.toList());

This is the most common type of coding question asked about Java 8. Practice writing these from scratch.

QWhat are the major features introduced in Java 8?
  • Lambda expressions โ€” anonymous functions, enables functional programming
  • Stream API โ€” functional-style processing of collections
  • Optional<T> โ€” explicit null handling
  • Default and static methods in interfaces โ€” add methods to interfaces without breaking implementations
  • Method references (::) โ€” shorthand for lambdas that call existing methods
  • java.time API โ€” new Date/Time API (LocalDate, LocalDateTime, etc.)
  • Functional interfaces โ€” Predicate, Function, Consumer, Supplier in java.util.function
๐ŸŽฏ
Interview Prep
Common Interview Questions
QWhat are the 4 pillars of OOP? Explain each briefly.

Encapsulation โ€” Wrapping data (fields) and methods into a single unit (class) and restricting direct access to data using access modifiers (private). Expose data through public getters/setters.

Inheritance โ€” A child class acquires properties and behaviour of a parent class using extends. Promotes code reuse. Java supports single class inheritance.

Polymorphism โ€” "One interface, many implementations." Method overriding (runtime) and method overloading (compile-time). Allows treating different objects uniformly through a common interface.

Abstraction โ€” Hiding implementation details and exposing only the necessary interface. Achieved through abstract classes and interfaces.

QWhat is the difference between method overloading and method overriding?

Overloading โ€” Same method name, different parameter list (number, type, or order). Happens in the same class. Resolved at compile time (static polymorphism).

Overriding โ€” Same method name and signature in a child class that re-implements the parent's method. Resolved at runtime (dynamic polymorphism). Requires @Override annotation.

QWhat is the difference between abstract class and interface?

Use an abstract class when classes share code and have a common identity (IS-A). A Dog and Cat both IS-A Animal and share breathe() logic.

Use an interface when unrelated classes share a capability (CAN-DO). A Duck and Airplane both CAN fly โ€” they're not related, but they share the Flyable interface.

Key difference: A class can extend only one abstract class but can implement multiple interfaces.

QWhat is the difference between == and .equals() in Java?

== checks reference equality โ€” whether two variables point to the same object in memory. For primitives (int, boolean), it compares values.

.equals() checks content equality โ€” whether two objects have the same value. The String class overrides equals() to compare character sequences.

Always use .equals() to compare String and other objects. Use == only for primitives or when checking if two references are literally the same object.

QWhat is a constructor? Can a constructor be private?

A constructor is a special method called when an object is created with new. It has the same name as the class and no return type. It initializes the object's fields.

Yes, a constructor can be private. This is used in the Singleton design pattern โ€” when you want exactly one instance of a class to exist. The class controls its own instantiation through a static factory method.

QWhat is the static keyword in Java?

The static keyword means the member belongs to the class itself, not to any specific instance. You don't need to create an object to use it.

  • Static field โ€” shared by all instances. E.g., a counter of how many objects were created.
  • Static method โ€” can be called on the class directly. E.g., Math.sqrt(), Arrays.sort().
  • Static block โ€” runs once when the class is first loaded.
QWhat is encapsulation and what problem does it solve?

Encapsulation hides internal state and allows controlled access through public methods. It solves the problem of invalid state.

Without encapsulation, anyone could set account.balance = -1000000 directly. With encapsulation, your withdraw() method validates the amount before modifying the balance โ€” the object always stays in a valid state.