DEV Community

Cover image for Java Core Mastery Part 4: Advanced Topics & Interview Mastery 🎯
Rajat Parihar
Rajat Parihar

Posted on

Java Core Mastery Part 4: Advanced Topics & Interview Mastery 🎯

Master Java 8+, Generics, Design Patterns, Modern Java, and ace your technical interviews with real-world examples

Java Advanced


πŸ“‹ Table of Contents


🎯 Introduction

Welcome to the final part of the Java Core Mastery series! This guide covers everything you need to become a senior Java developer and ace technical interviews.

What you'll learn:

  • πŸš€ Advanced Java 8+ features (Streams, Optional, Lambdas)
  • πŸ”’ Type-safe code with Generics
  • 🎨 Essential Design Patterns
  • πŸ“± Modern Java features (Records, Sealed Classes, Pattern Matching)
  • πŸ’Ό Interview preparation with 50+ real questions
  • πŸ—οΈ System design basics
  • πŸ’‘ Career tips from industry experts

Prerequisites:

  • Parts 1, 2, and 3 of this series
  • Basic understanding of OOP and Collections

Let's dive in! πŸš€


🌊 Java 8+ Stream API Mastery

What are Streams?

Streams allow you to process collections in a functional style - more readable, less code, and often faster!

Traditional vs Stream:

// ❌ Traditional way (verbose)
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve");
List<String> result = new ArrayList<>();
for (String name : names) {
    if (name.length() > 3) {
        result.add(name.toUpperCase());
    }
}
Collections.sort(result);

// βœ… Stream way (concise)
List<String> result = names.stream()
    .filter(name -> name.length() > 3)
    .map(String::toUpperCase)
    .sorted()
    .collect(Collectors.toList());
Enter fullscreen mode Exit fullscreen mode

Real-World Example: E-Commerce Order Processing

import java.util.*;
import java.util.stream.*;

class Order {
    private String orderId;
    private String customerName;
    private double amount;
    private String status; // "PENDING", "COMPLETED", "CANCELLED"
    private String category; // "ELECTRONICS", "CLOTHING", "FOOD"

    public Order(String orderId, String customerName, double amount, 
                String status, String category) {
        this.orderId = orderId;
        this.customerName = customerName;
        this.amount = amount;
        this.status = status;
        this.category = category;
    }

    // Getters
    public String getOrderId() { return orderId; }
    public String getCustomerName() { return customerName; }
    public double getAmount() { return amount; }
    public String getStatus() { return status; }
    public String getCategory() { return category; }

    @Override
    public String toString() {
        return String.format("Order[%s, %s, $%.2f, %s, %s]", 
            orderId, customerName, amount, status, category);
    }
}

public class StreamAPIDemo {
    public static void main(String[] args) {
        List<Order> orders = Arrays.asList(
            new Order("O001", "Alice", 1200.00, "COMPLETED", "ELECTRONICS"),
            new Order("O002", "Bob", 450.00, "COMPLETED", "CLOTHING"),
            new Order("O003", "Charlie", 800.00, "PENDING", "ELECTRONICS"),
            new Order("O004", "Alice", 200.00, "CANCELLED", "FOOD"),
            new Order("O005", "David", 1500.00, "COMPLETED", "ELECTRONICS"),
            new Order("O006", "Eve", 300.00, "COMPLETED", "CLOTHING"),
            new Order("O007", "Bob", 950.00, "PENDING", "ELECTRONICS"),
            new Order("O008", "Charlie", 150.00, "COMPLETED", "FOOD")
        );

        System.out.println("=== STREAM API EXAMPLES ===\n");

        // 1. Filter: Get all completed orders
        System.out.println("1. Completed Orders:");
        orders.stream()
            .filter(order -> order.getStatus().equals("COMPLETED"))
            .forEach(System.out::println);

        // 2. Map: Get all customer names
        System.out.println("\n2. All Customers:");
        List<String> customers = orders.stream()
            .map(Order::getCustomerName)
            .distinct()
            .collect(Collectors.toList());
        System.out.println(customers);

        // 3. Filter + Map: Get order IDs of electronics orders > $1000
        System.out.println("\n3. High-Value Electronics Orders:");
        List<String> highValueElectronics = orders.stream()
            .filter(order -> order.getCategory().equals("ELECTRONICS"))
            .filter(order -> order.getAmount() > 1000)
            .map(Order::getOrderId)
            .collect(Collectors.toList());
        System.out.println(highValueElectronics);

        // 4. Reduce: Calculate total revenue from completed orders
        System.out.println("\n4. Total Revenue (Completed):");
        double totalRevenue = orders.stream()
            .filter(order -> order.getStatus().equals("COMPLETED"))
            .mapToDouble(Order::getAmount)
            .sum();
        System.out.printf("$%.2f%n", totalRevenue);

        // 5. Grouping: Group orders by category
        System.out.println("\n5. Orders by Category:");
        Map<String, List<Order>> ordersByCategory = orders.stream()
            .collect(Collectors.groupingBy(Order::getCategory));
        ordersByCategory.forEach((category, orderList) -> {
            System.out.println(category + ": " + orderList.size() + " orders");
        });

        // 6. Grouping + Summing: Revenue by category
        System.out.println("\n6. Revenue by Category:");
        Map<String, Double> revenueByCategory = orders.stream()
            .filter(order -> order.getStatus().equals("COMPLETED"))
            .collect(Collectors.groupingBy(
                Order::getCategory,
                Collectors.summingDouble(Order::getAmount)
            ));
        revenueByCategory.forEach((category, revenue) -> 
            System.out.printf("%s: $%.2f%n", category, revenue));

        // 7. Sorting: Top 3 orders by amount
        System.out.println("\n7. Top 3 Orders:");
        orders.stream()
            .sorted(Comparator.comparingDouble(Order::getAmount).reversed())
            .limit(3)
            .forEach(System.out::println);

        // 8. Any Match: Check if any order > $2000
        System.out.println("\n8. Any order > $2000?");
        boolean hasHighValue = orders.stream()
            .anyMatch(order -> order.getAmount() > 2000);
        System.out.println(hasHighValue ? "Yes" : "No");

        // 9. All Match: Check if all orders are completed
        System.out.println("\n9. All orders completed?");
        boolean allCompleted = orders.stream()
            .allMatch(order -> order.getStatus().equals("COMPLETED"));
        System.out.println(allCompleted ? "Yes" : "No");

        // 10. Find First: Get first pending order
        System.out.println("\n10. First Pending Order:");
        orders.stream()
            .filter(order -> order.getStatus().equals("PENDING"))
            .findFirst()
            .ifPresent(System.out::println);
    }
}
Enter fullscreen mode Exit fullscreen mode

Output:

=== STREAM API EXAMPLES ===

1. Completed Orders:
Order[O001, Alice, $1200.00, COMPLETED, ELECTRONICS]
Order[O002, Bob, $450.00, COMPLETED, CLOTHING]
Order[O005, David, $1500.00, COMPLETED, ELECTRONICS]
Order[O006, Eve, $300.00, COMPLETED, CLOTHING]
Order[O008, Charlie, $150.00, COMPLETED, FOOD]

2. All Customers:
[Alice, Bob, Charlie, David, Eve]

3. High-Value Electronics Orders:
[O001, O005]

4. Total Revenue (Completed):
$3600.00

5. Orders by Category:
ELECTRONICS: 4 orders
CLOTHING: 2 orders
FOOD: 2 orders

6. Revenue by Category:
ELECTRONICS: $2700.00
CLOTHING: $750.00
FOOD: $150.00

7. Top 3 Orders:
Order[O005, David, $1500.00, COMPLETED, ELECTRONICS]
Order[O001, Alice, $1200.00, COMPLETED, ELECTRONICS]
Order[O007, Bob, $950.00, PENDING, ELECTRONICS]

8. Any order > $2000?
No

9. All orders completed?
No

10. First Pending Order:
Order[O003, Charlie, $800.00, PENDING, ELECTRONICS]
Enter fullscreen mode Exit fullscreen mode

Stream API Cheat Sheet

Operation Type Description Example
filter() Intermediate Keep elements matching condition .filter(x -> x > 10)
map() Intermediate Transform elements .map(String::toUpperCase)
flatMap() Intermediate Flatten nested streams .flatMap(List::stream)
distinct() Intermediate Remove duplicates .distinct()
sorted() Intermediate Sort elements .sorted()
limit() Intermediate Take first N elements .limit(5)
skip() Intermediate Skip first N elements .skip(3)
forEach() Terminal Iterate through elements .forEach(System.out::println)
collect() Terminal Collect to collection .collect(Collectors.toList())
reduce() Terminal Reduce to single value .reduce(0, Integer::sum)
count() Terminal Count elements .count()
anyMatch() Terminal Check if any match .anyMatch(x -> x > 10)
allMatch() Terminal Check if all match .allMatch(x -> x > 0)
findFirst() Terminal Get first element .findFirst()
findAny() Terminal Get any element .findAny()

flatMap() Explained

public class FlatMapDemo {
    public static void main(String[] args) {
        // Problem: List of lists
        List<List<Integer>> numbers = Arrays.asList(
            Arrays.asList(1, 2, 3),
            Arrays.asList(4, 5),
            Arrays.asList(6, 7, 8, 9)
        );

        // ❌ map() gives Stream<Stream<Integer>>
        // numbers.stream().map(List::stream) // Wrong!

        // βœ… flatMap() flattens to Stream<Integer>
        List<Integer> flattened = numbers.stream()
            .flatMap(List::stream)
            .collect(Collectors.toList());

        System.out.println(flattened); // [1, 2, 3, 4, 5, 6, 7, 8, 9]

        // Real example: Get all words from sentences
        List<String> sentences = Arrays.asList(
            "Hello World",
            "Java Streams",
            "Are Powerful"
        );

        List<String> words = sentences.stream()
            .flatMap(sentence -> Arrays.stream(sentence.split(" ")))
            .collect(Collectors.toList());

        System.out.println(words); // [Hello, World, Java, Streams, Are, Powerful]
    }
}
Enter fullscreen mode Exit fullscreen mode

🎁 Optional: Avoiding NullPointerException

The Billion Dollar Mistake

Tony Hoare (inventor of null): "I call it my billion-dollar mistake"

// ❌ Traditional null handling (ugly)
public String getUserEmail(User user) {
    if (user != null) {
        Address address = user.getAddress();
        if (address != null) {
            String email = address.getEmail();
            if (email != null) {
                return email.toLowerCase();
            }
        }
    }
    return "unknown@example.com";
}

// βœ… Optional way (clean)
public String getUserEmail(User user) {
    return Optional.ofNullable(user)
        .map(User::getAddress)
        .map(Address::getEmail)
        .map(String::toLowerCase)
        .orElse("unknown@example.com");
}
Enter fullscreen mode Exit fullscreen mode

Real-World Example: User Profile Service

import java.util.Optional;

class Address {
    private String street;
    private String city;
    private String zipCode;

    public Address(String street, String city, String zipCode) {
        this.street = street;
        this.city = city;
        this.zipCode = zipCode;
    }

    public String getStreet() { return street; }
    public String getCity() { return city; }
    public String getZipCode() { return zipCode; }
}

class User {
    private String name;
    private String email;
    private Address address;
    private Integer age;

    public User(String name, String email, Address address, Integer age) {
        this.name = name;
        this.email = email;
        this.address = address;
        this.age = age;
    }

    public String getName() { return name; }
    public String getEmail() { return email; }
    public Address getAddress() { return address; }
    public Integer getAge() { return age; }
}

class UserService {
    private Map<String, User> users = new HashMap<>();

    public UserService() {
        // Initialize with some users
        users.put("user1", new User("Alice", "alice@example.com", 
            new Address("123 Main St", "NYC", "10001"), 25));
        users.put("user2", new User("Bob", null, null, null));
        users.put("user3", new User("Charlie", "charlie@example.com", 
            new Address("456 Oak Ave", "LA", "90001"), 30));
    }

    // 1. Finding a user
    public Optional<User> findUserById(String userId) {
        return Optional.ofNullable(users.get(userId));
    }

    // 2. Get user email (with default)
    public String getUserEmail(String userId) {
        return findUserById(userId)
            .map(User::getEmail)
            .orElse("no-email@example.com");
    }

    // 3. Get user city (nested optional)
    public String getUserCity(String userId) {
        return findUserById(userId)
            .map(User::getAddress)
            .map(Address::getCity)
            .orElse("Unknown City");
    }

    // 4. Check if user is adult
    public boolean isAdult(String userId) {
        return findUserById(userId)
            .map(User::getAge)
            .filter(age -> age >= 18)
            .isPresent();
    }

    // 5. Get user info or throw exception
    public User getUserOrThrow(String userId) {
        return findUserById(userId)
            .orElseThrow(() -> new IllegalArgumentException(
                "User not found: " + userId));
    }

    // 6. Execute action if user exists
    public void sendEmail(String userId, String message) {
        findUserById(userId)
            .map(User::getEmail)
            .filter(email -> email != null && !email.isEmpty())
            .ifPresent(email -> 
                System.out.println("Sending email to " + email + ": " + message));
    }

    // 7. Get user age or compute default
    public int getUserAge(String userId) {
        return findUserById(userId)
            .map(User::getAge)
            .orElseGet(() -> {
                System.out.println("Age not found, calculating default...");
                return 18; // Default age
            });
    }

    // 8. Chain multiple operations
    public String getFormattedAddress(String userId) {
        return findUserById(userId)
            .map(User::getAddress)
            .map(addr -> String.format("%s, %s %s", 
                addr.getStreet(), addr.getCity(), addr.getZipCode()))
            .orElse("No address available");
    }
}

public class OptionalDemo {
    public static void main(String[] args) {
        UserService service = new UserService();

        System.out.println("=== OPTIONAL EXAMPLES ===\n");

        // Example 1: Get email
        System.out.println("1. Get Emails:");
        System.out.println("User1: " + service.getUserEmail("user1"));
        System.out.println("User2: " + service.getUserEmail("user2"));
        System.out.println("User999: " + service.getUserEmail("user999"));

        // Example 2: Get city
        System.out.println("\n2. Get Cities:");
        System.out.println("User1: " + service.getUserCity("user1"));
        System.out.println("User2: " + service.getUserCity("user2"));

        // Example 3: Check adult
        System.out.println("\n3. Is Adult:");
        System.out.println("User1: " + service.isAdult("user1"));
        System.out.println("User2: " + service.isAdult("user2"));

        // Example 4: Send email
        System.out.println("\n4. Send Emails:");
        service.sendEmail("user1", "Welcome!");
        service.sendEmail("user2", "Welcome!");
        service.sendEmail("user999", "Welcome!");

        // Example 5: Get formatted address
        System.out.println("\n5. Formatted Addresses:");
        System.out.println("User1: " + service.getFormattedAddress("user1"));
        System.out.println("User2: " + service.getFormattedAddress("user2"));

        // Example 6: OrElseThrow
        System.out.println("\n6. Get User or Throw:");
        try {
            User user = service.getUserOrThrow("user1");
            System.out.println("Found: " + user.getName());
        } catch (Exception e) {
            System.out.println("Error: " + e.getMessage());
        }

        try {
            User user = service.getUserOrThrow("user999");
        } catch (Exception e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Output:

=== OPTIONAL EXAMPLES ===

1. Get Emails:
User1: alice@example.com
User2: no-email@example.com
User999: no-email@example.com

2. Get Cities:
User1: NYC
User2: Unknown City

3. Is Adult:
User1: true
User2: false

4. Send Emails:
Sending email to alice@example.com: Welcome!

5. Formatted Addresses:
User1: 123 Main St, NYC 10001
User2: No address available

6. Get User or Throw:
Found: Alice
Error: User not found: user999
Enter fullscreen mode Exit fullscreen mode

Optional Best Practices

// βœ… DO: Use Optional for return types
public Optional<User> findUser(String id) {
    return Optional.ofNullable(database.get(id));
}

// ❌ DON'T: Use Optional as method parameter
public void process(Optional<User> user) { } // Bad!

// βœ… DO: Use orElse for default values
String name = optional.orElse("Unknown");

// βœ… DO: Use orElseGet for expensive computations
String name = optional.orElseGet(() -> database.getDefault());

// βœ… DO: Use orElseThrow for mandatory values
User user = optional.orElseThrow(() -> new UserNotFoundException());

// ❌ DON'T: Use get() without checking
User user = optional.get(); // Can throw NoSuchElementException!

// βœ… DO: Use ifPresent for side effects
optional.ifPresent(user -> sendEmail(user));

// βœ… DO: Chain operations
optional
    .filter(user -> user.getAge() >= 18)
    .map(User::getName)
    .ifPresent(System.out::println);
Enter fullscreen mode Exit fullscreen mode

πŸ”§ Functional Programming in Java

Functional Interfaces

A functional interface has exactly one abstract method.

@FunctionalInterface
interface Calculator {
    int calculate(int a, int b);

    // Can have default methods
    default void printResult(int result) {
        System.out.println("Result: " + result);
    }

    // Can have static methods
    static void info() {
        System.out.println("This is a calculator");
    }
}

public class FunctionalInterfaceDemo {
    public static void main(String[] args) {
        // Before Java 8
        Calculator add = new Calculator() {
            @Override
            public int calculate(int a, int b) {
                return a + b;
            }
        };

        // Java 8: Lambda
        Calculator add2 = (a, b) -> a + b;
        Calculator multiply = (a, b) -> a * b;
        Calculator subtract = (a, b) -> a - b;

        System.out.println("Add: " + add2.calculate(5, 3));
        System.out.println("Multiply: " + multiply.calculate(5, 3));
        System.out.println("Subtract: " + subtract.calculate(5, 3));
    }
}
Enter fullscreen mode Exit fullscreen mode

Built-in Functional Interfaces

import java.util.function.*;

public class BuiltInFunctionalInterfaces {
    public static void main(String[] args) {

        // 1. Predicate<T>: T -> boolean (testing)
        Predicate<Integer> isEven = num -> num % 2 == 0;
        System.out.println("Is 4 even? " + isEven.test(4)); // true

        Predicate<String> isLongString = str -> str.length() > 5;
        System.out.println("Is 'Hello' long? " + isLongString.test("Hello")); // false

        // 2. Function<T, R>: T -> R (transformation)
        Function<String, Integer> stringLength = str -> str.length();
        System.out.println("Length of 'Hello': " + stringLength.apply("Hello")); // 5

        Function<Integer, String> intToString = num -> "Number: " + num;
        System.out.println(intToString.apply(42)); // "Number: 42"

        // 3. Consumer<T>: T -> void (action)
        Consumer<String> printer = str -> System.out.println("Printing: " + str);
        printer.accept("Hello World");

        // 4. Supplier<T>: () -> T (generation)
        Supplier<Double> randomSupplier = () -> Math.random();
        System.out.println("Random: " + randomSupplier.get());

        // 5. BiFunction<T, U, R>: (T, U) -> R
        BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
        System.out.println("5 + 3 = " + add.apply(5, 3));

        // 6. UnaryOperator<T>: T -> T (special Function)
        UnaryOperator<Integer> square = num -> num * num;
        System.out.println("Square of 5: " + square.apply(5));

        // 7. BinaryOperator<T>: (T, T) -> T (special BiFunction)
        BinaryOperator<Integer> multiply = (a, b) -> a * b;
        System.out.println("5 * 3 = " + multiply.apply(5, 3));
    }
}
Enter fullscreen mode Exit fullscreen mode

Real-World Example: Data Validation Pipeline

import java.util.function.*;

class ValidationResult {
    private boolean valid;
    private String errorMessage;

    public ValidationResult(boolean valid, String errorMessage) {
        this.valid = valid;
        this.errorMessage = errorMessage;
    }

    public boolean isValid() { return valid; }
    public String getErrorMessage() { return errorMessage; }

    public static ValidationResult success() {
        return new ValidationResult(true, null);
    }

    public static ValidationResult failure(String message) {
        return new ValidationResult(false, message);
    }
}

class UserValidator {
    // Predicate for validation
    private static Predicate<String> isValidEmail = 
        email -> email != null && email.matches("^[A-Za-z0-9+_.-]+@(.+)$");

    private static Predicate<String> isValidPassword = 
        password -> password != null && password.length() >= 8;

    private static Predicate<Integer> isValidAge = 
        age -> age != null && age >= 18 && age <= 120;

    // Function to validate email
    public static Function<String, ValidationResult> validateEmail = email -> {
        if (!isValidEmail.test(email)) {
            return ValidationResult.failure("Invalid email format");
        }
        return ValidationResult.success();
    };

    // Function to validate password
    public static Function<String, ValidationResult> validatePassword = password -> {
        if (!isValidPassword.test(password)) {
            return ValidationResult.failure("Password must be at least 8 characters");
        }
        return ValidationResult.success();
    };

    // Function to validate age
    public static Function<Integer, ValidationResult> validateAge = age -> {
        if (!isValidAge.test(age)) {
            return ValidationResult.failure("Age must be between 18 and 120");
        }
        return ValidationResult.success();
    };
}

class UserRegistration {
    private String email;
    private String password;
    private Integer age;

    public UserRegistration(String email, String password, Integer age) {
        this.email = email;
        this.password = password;
        this.age = age;
    }

    public String getEmail() { return email; }
    public String getPassword() { return password; }
    public Integer getAge() { return age; }

    // Validate all fields
    public List<String> validate() {
        List<String> errors = new ArrayList<>();

        ValidationResult emailResult = UserValidator.validateEmail.apply(email);
        if (!emailResult.isValid()) {
            errors.add(emailResult.getErrorMessage());
        }

        ValidationResult passwordResult = UserValidator.validatePassword.apply(password);
        if (!passwordResult.isValid()) {
            errors.add(passwordResult.getErrorMessage());
        }

        ValidationResult ageResult = UserValidator.validateAge.apply(age);
        if (!ageResult.isValid()) {
            errors.add(ageResult.getErrorMessage());
        }

        return errors;
    }
}

public class FunctionalValidationDemo {
    public static void main(String[] args) {
        System.out.println("=== USER REGISTRATION VALIDATION ===\n");

        // Test case 1: Valid user
        UserRegistration user1 = new UserRegistration(
            "alice@example.com", "password123", 25);
        List<String> errors1 = user1.validate();

        System.out.println("User 1: " + user1.getEmail());
        if (errors1.isEmpty()) {
            System.out.println("βœ… Valid!");
        } else {
            System.out.println("❌ Errors: " + errors1);
        }

        // Test case 2: Invalid email
        UserRegistration user2 = new UserRegistration(
            "invalid-email", "password123", 25);
        List<String> errors2 = user2.validate();

        System.out.println("\nUser 2: " + user2.getEmail());
        if (errors2.isEmpty()) {
            System.out.println("βœ… Valid!");
        } else {
            System.out.println("❌ Errors: " + errors2);
        }

        // Test case 3: Multiple errors
        UserRegistration user3 = new UserRegistration(
            "bad-email", "short", 15);
        List<String> errors3 = user3.validate();

        System.out.println("\nUser 3: " + user3.getEmail());
        if (errors3.isEmpty()) {
            System.out.println("βœ… Valid!");
        } else {
            System.out.println("❌ Errors:");
            errors3.forEach(error -> System.out.println("  - " + error));
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Output:

=== USER REGISTRATION VALIDATION ===

User 1: alice@example.com
βœ… Valid!

User 2: invalid-email
❌ Errors: [Invalid email format]

User 3: bad-email
❌ Errors:
  - Invalid email format
  - Password must be at least 8 characters
  - Age must be between 18 and 120
Enter fullscreen mode Exit fullscreen mode

Method References

public class MethodReferenceDemo {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

        // 1. Static method reference
        // Lambda: name -> System.out.println(name)
        names.forEach(System.out::println);

        // 2. Instance method reference
        String prefix = "Hello, ";
        // Lambda: name -> prefix.concat(name)
        names.stream()
            .map(prefix::concat)
            .forEach(System.out::println);

        // 3. Constructor reference
        // Lambda: () -> new ArrayList<>()
        Supplier<List<String>> listSupplier = ArrayList::new;
        List<String> newList = listSupplier.get();

        // 4. Arbitrary object method reference
        // Lambda: str -> str.toUpperCase()
        List<String> upperNames = names.stream()
            .map(String::toUpperCase)
            .collect(Collectors.toList());
        System.out.println(upperNames);
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ” Generics & Type Safety

Why Generics?

Before Generics (Java < 5):

// ❌ No type safety
List list = new ArrayList();
list.add("Hello");
list.add(123); // No compile error!
String str = (String) list.get(1); // ClassCastException at runtime!
Enter fullscreen mode Exit fullscreen mode

With Generics:

// βœ… Type safe
List<String> list = new ArrayList<>();
list.add("Hello");
// list.add(123); // Compile error!
String str = list.get(0); // No casting needed
Enter fullscreen mode Exit fullscreen mode

Generic Classes

// Generic Box class
class Box<T> {
    private T content;

    public void set(T content) {
        this.content = content;
    }

    public T get() {
        return content;
    }

    public boolean isEmpty() {
        return content == null;
    }
}

public class GenericClassDemo {
    public static void main(String[] args) {
        // String box
        Box<String> stringBox = new Box<>();
        stringBox.set("Hello");
        String str = stringBox.get(); // No casting!

        // Integer box
        Box<Integer> intBox = new Box<>();
        intBox.set(123);
        int num = intBox.get();

        System.out.println("String: " + str);
        System.out.println("Integer: " + num);
    }
}
Enter fullscreen mode Exit fullscreen mode

Real-World Example: Generic Repository Pattern

import java.util.*;

// Generic Repository Interface
interface Repository<T, ID> {
    void save(T entity);
    Optional<T> findById(ID id);
    List<T> findAll();
    void deleteById(ID id);
    boolean existsById(ID id);
}

// User entity
class User {
    private Long id;
    private String name;
    private String email;

    public User(Long id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    public Long getId() { return id; }
    public String getName() { return name; }
    public String getEmail() { return email; }

    @Override
    public String toString() {
        return "User{id=" + id + ", name='" + name + "', email='" + email + "'}";
    }
}

// Product entity
class Product {
    private Long id;
    private String name;
    private double price;

    public Product(Long id, String name, double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }

    public Long getId() { return id; }
    public String getName() { return name; }
    public double getPrice() { return price; }

    @Override
    public String toString() {
        return "Product{id=" + id + ", name='" + name + "', price=" + price + "}";
    }
}

// Generic Repository Implementation
class InMemoryRepository<T, ID> implements Repository<T, ID> {
    private final Map<ID, T> storage = new HashMap<>();
    private final Function<T, ID> idExtractor;

    public InMemoryRepository(Function<T, ID> idExtractor) {
        this.idExtractor = idExtractor;
    }

    @Override
    public void save(T entity) {
        ID id = idExtractor.apply(entity);
        storage.put(id, entity);
    }

    @Override
    public Optional<T> findById(ID id) {
        return Optional.ofNullable(storage.get(id));
    }

    @Override
    public List<T> findAll() {
        return new ArrayList<>(storage.values());
    }

    @Override
    public void deleteById(ID id) {
        storage.remove(id);
    }

    @Override
    public boolean existsById(ID id) {
        return storage.containsKey(id);
    }
}

public class GenericRepositoryDemo {
    public static void main(String[] args) {
        System.out.println("=== GENERIC REPOSITORY PATTERN ===\n");

        // User Repository
        Repository<User, Long> userRepo = new InMemoryRepository<>(User::getId);

        userRepo.save(new User(1L, "Alice", "alice@example.com"));
        userRepo.save(new User(2L, "Bob", "bob@example.com"));
        userRepo.save(new User(3L, "Charlie", "charlie@example.com"));

        System.out.println("All Users:");
        userRepo.findAll().forEach(System.out::println);

        System.out.println("\nFind User with ID 2:");
        userRepo.findById(2L).ifPresent(System.out::println);

        System.out.println("\nUser with ID 2 exists? " + userRepo.existsById(2L));

        // Product Repository
        Repository<Product, Long> productRepo = new InMemoryRepository<>(Product::getId);

        productRepo.save(new Product(1L, "Laptop", 999.99));
        productRepo.save(new Product(2L, "Mouse", 29.99));
        productRepo.save(new Product(3L, "Keyboard", 79.99));

        System.out.println("\nAll Products:");
        productRepo.findAll().forEach(System.out::println);

        System.out.println("\nDelete Product with ID 2");
        productRepo.deleteById(2L);

        System.out.println("\nAll Products after deletion:");
        productRepo.findAll().forEach(System.out::println);
    }
}
Enter fullscreen mode Exit fullscreen mode

Output:

=== GENERIC REPOSITORY PATTERN ===

All Users:
User{id=1, name='Alice', email='alice@example.com'}
User{id=2, name='Bob', email='bob@example.com'}
User{id=3, name='Charlie', email='charlie@example.com'}

Find User with ID 2:
User{id=2, name='Bob', email='bob@example.com'}

User with ID 2 exists? true

All Products:
Product{id=1, name='Laptop', price=999.99}
Product{id=2, name='Mouse', price=29.99}
Product{id=3, name='Keyboard', price=79.99}

Delete Product with ID 2

All Products after deletion:
Product{id=1, name='Laptop', price=999.99}
Product{id=3, name='Keyboard', price=79.99}
Enter fullscreen mode Exit fullscreen mode

Bounded Type Parameters

// Bound to Number and its subclasses
class MathUtils<T extends Number> {
    private T value;

    public MathUtils(T value) {
        this.value = value;
    }

    public double doubleValue() {
        return value.doubleValue();
    }

    public boolean isPositive() {
        return value.doubleValue() > 0;
    }
}

public class BoundedTypeDemo {
    public static void main(String[] args) {
        MathUtils<Integer> intUtils = new MathUtils<>(42);
        MathUtils<Double> doubleUtils = new MathUtils<>(3.14);

        // MathUtils<String> stringUtils = new MathUtils<>("test"); // Compile error!

        System.out.println("Integer value: " + intUtils.doubleValue());
        System.out.println("Double positive? " + doubleUtils.isPositive());
    }
}
Enter fullscreen mode Exit fullscreen mode

Wildcards

public class WildcardDemo {
    // 1. Unbounded wildcard (?)
    public static void printList(List<?> list) {
        for (Object obj : list) {
            System.out.println(obj);
        }
    }

    // 2. Upper bounded wildcard (? extends Type)
    public static double sumNumbers(List<? extends Number> numbers) {
        double sum = 0;
        for (Number num : numbers) {
            sum += num.doubleValue();
        }
        return sum;
    }

    // 3. Lower bounded wildcard (? super Type)
    public static void addIntegers(List<? super Integer> list) {
        list.add(1);
        list.add(2);
        list.add(3);
    }

    public static void main(String[] args) {
        // Unbounded
        List<String> strings = Arrays.asList("A", "B", "C");
        printList(strings);

        // Upper bounded
        List<Integer> integers = Arrays.asList(1, 2, 3);
        List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3);
        System.out.println("Sum of integers: " + sumNumbers(integers));
        System.out.println("Sum of doubles: " + sumNumbers(doubles));

        // Lower bounded
        List<Number> numbers = new ArrayList<>();
        addIntegers(numbers);
        System.out.println("Numbers: " + numbers);
    }
}
Enter fullscreen mode Exit fullscreen mode

(Continuing with Annotations, Design Patterns, Modern Java, and Interview Questions...)

πŸ“Œ Quick Note

Due to length, I'll continue this in the next section. Should I proceed with the remaining sections:

  • Annotations & Reflection
  • Design Patterns (Singleton, Factory, Builder, Observer, Strategy)
  • File I/O & Serialization
  • JDBC Fundamentals
  • Modern Java (11, 17, 21) with Records, Sealed Classes
  • Top 50 Interview Questions
  • System Design Basics
  • Interview Strategy

Let me know if you want me to complete all of this in one go! πŸš€

πŸ“ Annotations & Reflection

What are Annotations?

Annotations provide metadata about your code. They don't directly affect code execution but can be used by:

  • Compiler (e.g., @Override)
  • Build tools (e.g., @Deprecated)
  • Frameworks (e.g., Spring's @Autowired, JPA's @Entity)

Built-in Annotations

// 1. @Override - Ensures method overrides parent method
class Animal {
    public void makeSound() {
        System.out.println("Some sound");
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() {  // βœ… Compiler checks this
        System.out.println("Bark!");
    }

    // @Override
    // public void makeSond() {  // ❌ Compile error - typo caught!
    // }
}

// 2. @Deprecated - Marks obsolete code
class OldAPI {
    @Deprecated
    public void oldMethod() {
        System.out.println("This method is deprecated");
    }

    public void newMethod() {
        System.out.println("Use this instead");
    }
}

// 3. @SuppressWarnings - Suppresses compiler warnings
public class AnnotationDemo {
    @SuppressWarnings("unchecked")
    public void processLegacyCode() {
        List list = new ArrayList(); // Raw type - warning suppressed
        list.add("item");
    }
}

// 4. @FunctionalInterface - Ensures interface has exactly one abstract method
@FunctionalInterface
interface Calculator {
    int calculate(int a, int b);

    // int multiply(int a, int b); // ❌ Compile error! Only one abstract method allowed
}
Enter fullscreen mode Exit fullscreen mode

Custom Annotations

import java.lang.annotation.*;

// Define custom annotation
@Retention(RetentionPolicy.RUNTIME)  // Available at runtime
@Target(ElementType.METHOD)          // Can be applied to methods
@interface Test {
    String author() default "Unknown";
    int priority() default 1;
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Entity {
    String tableName();
}

// Using custom annotations
@Entity(tableName = "users")
class User {
    private Long id;
    private String name;

    public User(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    @Test(author = "Alice", priority = 1)
    public void testCreateUser() {
        System.out.println("Testing user creation");
    }

    @Test(author = "Bob", priority = 2)
    public void testUpdateUser() {
        System.out.println("Testing user update");
    }

    public Long getId() { return id; }
    public String getName() { return name; }
}
Enter fullscreen mode Exit fullscreen mode

Reflection API

import java.lang.reflect.*;

public class ReflectionDemo {
    public static void main(String[] args) {
        try {
            // 1. Get class information
            Class<?> userClass = User.class;

            System.out.println("=== CLASS INFORMATION ===");
            System.out.println("Class name: " + userClass.getName());
            System.out.println("Simple name: " + userClass.getSimpleName());

            // 2. Get annotations on class
            if (userClass.isAnnotationPresent(Entity.class)) {
                Entity entity = userClass.getAnnotation(Entity.class);
                System.out.println("Table name: " + entity.tableName());
            }

            // 3. Get all methods
            System.out.println("\n=== METHODS ===");
            Method[] methods = userClass.getDeclaredMethods();
            for (Method method : methods) {
                System.out.println("Method: " + method.getName());

                // Check for @Test annotation
                if (method.isAnnotationPresent(Test.class)) {
                    Test test = method.getAnnotation(Test.class);
                    System.out.println("  @Test - Author: " + test.author() + 
                                     ", Priority: " + test.priority());
                }
            }

            // 4. Get all fields
            System.out.println("\n=== FIELDS ===");
            Field[] fields = userClass.getDeclaredFields();
            for (Field field : fields) {
                System.out.println("Field: " + field.getName() + 
                                 " - Type: " + field.getType().getSimpleName());
            }

            // 5. Create instance using reflection
            System.out.println("\n=== CREATE INSTANCE ===");
            Constructor<?> constructor = userClass.getConstructor(Long.class, String.class);
            Object userInstance = constructor.newInstance(1L, "John Doe");
            System.out.println("Created: " + userInstance);

            // 6. Invoke method using reflection
            System.out.println("\n=== INVOKE METHOD ===");
            Method getNameMethod = userClass.getMethod("getName");
            String name = (String) getNameMethod.invoke(userInstance);
            System.out.println("Name from reflection: " + name);

            // 7. Run all @Test methods
            System.out.println("\n=== RUN TESTS ===");
            for (Method method : methods) {
                if (method.isAnnotationPresent(Test.class)) {
                    System.out.println("Running: " + method.getName());
                    method.invoke(userInstance);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Output:

=== CLASS INFORMATION ===
Class name: User
Simple name: User
Table name: users

=== METHODS ===
Method: testCreateUser
  @Test - Author: Alice, Priority: 1
Method: testUpdateUser
  @Test - Author: Bob, Priority: 2
Method: getId
Method: getName

=== FIELDS ===
Field: id - Type: Long
Field: name - Type: String

=== CREATE INSTANCE ===
Created: User{id=1, name='John Doe'}

=== INVOKE METHOD ===
Name from reflection: John Doe

=== RUN TESTS ===
Running: testCreateUser
Testing user creation
Running: testUpdateUser
Testing user update
Enter fullscreen mode Exit fullscreen mode

Real-World Use Case: Simple Testing Framework

import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.util.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Test {
    String name() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface BeforeEach {
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface AfterEach {
}

class TestRunner {
    public static void runTests(Class<?> testClass) {
        try {
            Object instance = testClass.getDeclaredConstructor().newInstance();
            Method[] methods = testClass.getDeclaredMethods();

            // Find setup and teardown methods
            Method setupMethod = null;
            Method teardownMethod = null;
            List<Method> testMethods = new ArrayList<>();

            for (Method method : methods) {
                if (method.isAnnotationPresent(BeforeEach.class)) {
                    setupMethod = method;
                } else if (method.isAnnotationPresent(AfterEach.class)) {
                    teardownMethod = method;
                } else if (method.isAnnotationPresent(Test.class)) {
                    testMethods.add(method);
                }
            }

            // Run tests
            int passed = 0;
            int failed = 0;

            for (Method testMethod : testMethods) {
                Test test = testMethod.getAnnotation(Test.class);
                String testName = test.name().isEmpty() ? testMethod.getName() : test.name();

                try {
                    // Setup
                    if (setupMethod != null) {
                        setupMethod.invoke(instance);
                    }

                    // Run test
                    testMethod.invoke(instance);
                    System.out.println("βœ… PASSED: " + testName);
                    passed++;

                    // Teardown
                    if (teardownMethod != null) {
                        teardownMethod.invoke(instance);
                    }
                } catch (Exception e) {
                    System.out.println("❌ FAILED: " + testName);
                    System.out.println("   Error: " + e.getCause().getMessage());
                    failed++;
                }
            }

            System.out.println("\n=== TEST SUMMARY ===");
            System.out.println("Passed: " + passed);
            System.out.println("Failed: " + failed);
            System.out.println("Total: " + (passed + failed));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

// Test class
class MathTests {
    @BeforeEach
    public void setup() {
        System.out.println("Setting up test...");
    }

    @AfterEach
    public void teardown() {
        System.out.println("Cleaning up...");
    }

    @Test(name = "Addition Test")
    public void testAddition() {
        int result = 2 + 2;
        if (result != 4) {
            throw new AssertionError("Expected 4, got " + result);
        }
    }

    @Test(name = "Division Test")
    public void testDivision() {
        int result = 10 / 2;
        if (result != 5) {
            throw new AssertionError("Expected 5, got " + result);
        }
    }

    @Test(name = "Failing Test")
    public void testFailing() {
        throw new AssertionError("This test is meant to fail");
    }
}

public class CustomTestingFramework {
    public static void main(String[] args) {
        System.out.println("=== RUNNING TESTS ===\n");
        TestRunner.runTests(MathTests.class);
    }
}
Enter fullscreen mode Exit fullscreen mode

🎨 Design Patterns

Design patterns are proven solutions to common software problems.

1. Singleton Pattern

Problem: Need exactly ONE instance of a class.

// ❌ NOT thread-safe
class BadSingleton {
    private static BadSingleton instance;

    private BadSingleton() {}

    public static BadSingleton getInstance() {
        if (instance == null) {
            instance = new BadSingleton(); // Race condition!
        }
        return instance;
    }
}

// βœ… Thread-safe (Eager initialization)
class EagerSingleton {
    private static final EagerSingleton instance = new EagerSingleton();

    private EagerSingleton() {}

    public static EagerSingleton getInstance() {
        return instance;
    }
}

// βœ… Thread-safe (Lazy initialization with double-check locking)
class LazySingleton {
    private static volatile LazySingleton instance;

    private LazySingleton() {}

    public static LazySingleton getInstance() {
        if (instance == null) {
            synchronized (LazySingleton.class) {
                if (instance == null) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

// βœ… Best: Enum Singleton (recommended by Joshua Bloch)
enum EnumSingleton {
    INSTANCE;

    public void doSomething() {
        System.out.println("Doing something");
    }
}
Enter fullscreen mode Exit fullscreen mode

Real-World Example: Database Connection Manager

class DatabaseConnectionManager {
    private static volatile DatabaseConnectionManager instance;
    private String connectionString;
    private int maxConnections;

    private DatabaseConnectionManager() {
        // Private constructor
        this.connectionString = "jdbc:mysql://localhost:3306/mydb";
        this.maxConnections = 10;
        System.out.println("Database Connection Manager initialized");
    }

    public static DatabaseConnectionManager getInstance() {
        if (instance == null) {
            synchronized (DatabaseConnectionManager.class) {
                if (instance == null) {
                    instance = new DatabaseConnectionManager();
                }
            }
        }
        return instance;
    }

    public void connect() {
        System.out.println("Connecting to: " + connectionString);
    }

    public void disconnect() {
        System.out.println("Disconnecting from database");
    }
}

public class SingletonDemo {
    public static void main(String[] args) {
        // Get same instance everywhere
        DatabaseConnectionManager db1 = DatabaseConnectionManager.getInstance();
        DatabaseConnectionManager db2 = DatabaseConnectionManager.getInstance();

        System.out.println("Same instance? " + (db1 == db2)); // true

        db1.connect();

        // Enum singleton
        EnumSingleton.INSTANCE.doSomething();
    }
}
Enter fullscreen mode Exit fullscreen mode

2. Factory Pattern

Problem: Need to create objects without specifying exact class.

// Product interface
interface Vehicle {
    void drive();
    int getSeatingCapacity();
}

// Concrete products
class Car implements Vehicle {
    @Override
    public void drive() {
        System.out.println("Driving a car");
    }

    @Override
    public int getSeatingCapacity() {
        return 5;
    }
}

class Truck implements Vehicle {
    @Override
    public void drive() {
        System.out.println("Driving a truck");
    }

    @Override
    public int getSeatingCapacity() {
        return 2;
    }
}

class Motorcycle implements Vehicle {
    @Override
    public void drive() {
        System.out.println("Riding a motorcycle");
    }

    @Override
    public int getSeatingCapacity() {
        return 2;
    }
}

// Factory
class VehicleFactory {
    public static Vehicle createVehicle(String type) {
        switch (type.toLowerCase()) {
            case "car":
                return new Car();
            case "truck":
                return new Truck();
            case "motorcycle":
                return new Motorcycle();
            default:
                throw new IllegalArgumentException("Unknown vehicle type: " + type);
        }
    }
}

public class FactoryPatternDemo {
    public static void main(String[] args) {
        // Client doesn't know concrete classes
        Vehicle car = VehicleFactory.createVehicle("car");
        Vehicle truck = VehicleFactory.createVehicle("truck");

        car.drive();
        System.out.println("Car seats: " + car.getSeatingCapacity());

        truck.drive();
        System.out.println("Truck seats: " + truck.getSeatingCapacity());
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Builder Pattern

Problem: Too many constructor parameters, want readable object creation.

// ❌ Too many constructor parameters
class PizzaBad {
    public PizzaBad(String size, boolean cheese, boolean pepperoni, 
                   boolean mushrooms, boolean olives, boolean tomatoes) {
        // Which boolean is which?
    }
}

// βœ… Builder pattern
class Pizza {
    // Required parameters
    private final String size;

    // Optional parameters
    private final boolean cheese;
    private final boolean pepperoni;
    private final boolean mushrooms;
    private final boolean olives;
    private final boolean bacon;

    // Private constructor
    private Pizza(Builder builder) {
        this.size = builder.size;
        this.cheese = builder.cheese;
        this.pepperoni = builder.pepperoni;
        this.mushrooms = builder.mushrooms;
        this.olives = builder.olives;
        this.bacon = builder.bacon;
    }

    // Builder class
    public static class Builder {
        // Required
        private final String size;

        // Optional (with defaults)
        private boolean cheese = false;
        private boolean pepperoni = false;
        private boolean mushrooms = false;
        private boolean olives = false;
        private boolean bacon = false;

        public Builder(String size) {
            this.size = size;
        }

        public Builder cheese(boolean value) {
            cheese = value;
            return this;
        }

        public Builder pepperoni(boolean value) {
            pepperoni = value;
            return this;
        }

        public Builder mushrooms(boolean value) {
            mushrooms = value;
            return this;
        }

        public Builder olives(boolean value) {
            olives = value;
            return this;
        }

        public Builder bacon(boolean value) {
            bacon = value;
            return this;
        }

        public Pizza build() {
            return new Pizza(this);
        }
    }

    @Override
    public String toString() {
        return "Pizza{" +
                "size='" + size + '\'' +
                ", cheese=" + cheese +
                ", pepperoni=" + pepperoni +
                ", mushrooms=" + mushrooms +
                ", olives=" + olives +
                ", bacon=" + bacon +
                '}';
    }
}

public class BuilderPatternDemo {
    public static void main(String[] args) {
        // βœ… Readable and flexible
        Pizza pizza1 = new Pizza.Builder("Large")
                .cheese(true)
                .pepperoni(true)
                .mushrooms(true)
                .build();

        Pizza pizza2 = new Pizza.Builder("Medium")
                .cheese(true)
                .bacon(true)
                .build();

        System.out.println(pizza1);
        System.out.println(pizza2);
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Observer Pattern (You've seen this with YouTube!)

// Already covered in detail in Multithreading section!
// Quick recap:
interface Observer {
    void update(String event);
}

interface Subject {
    void attach(Observer observer);
    void detach(Observer observer);
    void notifyObservers(String event);
}
Enter fullscreen mode Exit fullscreen mode

5. Strategy Pattern

Problem: Need different algorithms for same task, choose at runtime.

// Strategy interface
interface PaymentStrategy {
    void pay(int amount);
}

// Concrete strategies
class CreditCardPayment implements PaymentStrategy {
    private String cardNumber;

    public CreditCardPayment(String cardNumber) {
        this.cardNumber = cardNumber;
    }

    @Override
    public void pay(int amount) {
        System.out.println("Paid $" + amount + " using Credit Card: " + 
                         cardNumber.substring(0, 4) + "****");
    }
}

class PayPalPayment implements PaymentStrategy {
    private String email;

    public PayPalPayment(String email) {
        this.email = email;
    }

    @Override
    public void pay(int amount) {
        System.out.println("Paid $" + amount + " using PayPal: " + email);
    }
}

class CryptoPayment implements PaymentStrategy {
    private String walletAddress;

    public CryptoPayment(String walletAddress) {
        this.walletAddress = walletAddress;
    }

    @Override
    public void pay(int amount) {
        System.out.println("Paid $" + amount + " using Crypto: " + 
                         walletAddress.substring(0, 8) + "...");
    }
}

// Context
class ShoppingCart {
    private PaymentStrategy paymentStrategy;

    public void setPaymentStrategy(PaymentStrategy strategy) {
        this.paymentStrategy = strategy;
    }

    public void checkout(int amount) {
        if (paymentStrategy == null) {
            throw new IllegalStateException("Payment strategy not set");
        }
        paymentStrategy.pay(amount);
    }
}

public class StrategyPatternDemo {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();

        // Pay with credit card
        cart.setPaymentStrategy(new CreditCardPayment("1234-5678-9012-3456"));
        cart.checkout(100);

        // Pay with PayPal
        cart.setPaymentStrategy(new PayPalPayment("user@example.com"));
        cart.checkout(200);

        // Pay with crypto
        cart.setPaymentStrategy(new CryptoPayment("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"));
        cart.checkout(300);
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ“ File I/O & Serialization

Reading Files

import java.io.*;
import java.nio.file.*;
import java.util.*;

public class FileReadingDemo {
    public static void main(String[] args) {
        // Method 1: BufferedReader (old way, line by line)
        try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Method 2: Files.readAllLines (Java 7+, all at once)
        try {
            List<String> lines = Files.readAllLines(Paths.get("data.txt"));
            lines.forEach(System.out::println);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Method 3: Files.lines (Java 8+, stream)
        try {
            Files.lines(Paths.get("data.txt"))
                .filter(line -> !line.isEmpty())
                .forEach(System.out::println);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Writing Files

import java.io.*;
import java.nio.file.*;
import java.util.*;

public class FileWritingDemo {
    public static void main(String[] args) {
        // Method 1: BufferedWriter
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {
            writer.write("Line 1\n");
            writer.write("Line 2\n");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Method 2: Files.write (Java 7+)
        List<String> lines = Arrays.asList("Line 1", "Line 2", "Line 3");
        try {
            Files.write(Paths.get("output.txt"), lines);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Serialization

import java.io.*;

// Class must implement Serializable
class Employee implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;
    private double salary;
    private transient String password; // Won't be serialized

    public Employee(String name, int age, double salary, String password) {
        this.name = name;
        this.age = age;
        this.salary = salary;
        this.password = password;
    }

    @Override
    public String toString() {
        return "Employee{name='" + name + "', age=" + age + 
               ", salary=" + salary + ", password='" + password + "'}";
    }
}

public class SerializationDemo {
    public static void main(String[] args) {
        Employee emp = new Employee("Alice", 30, 75000, "secret123");

        // Serialize (write to file)
        try (ObjectOutputStream out = new ObjectOutputStream(
                new FileOutputStream("employee.ser"))) {
            out.writeObject(emp);
            System.out.println("Serialized: " + emp);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Deserialize (read from file)
        try (ObjectInputStream in = new ObjectInputStream(
                new FileInputStream("employee.ser"))) {
            Employee deserializedEmp = (Employee) in.readObject();
            System.out.println("Deserialized: " + deserializedEmp);
            // Note: password is null (transient)
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ—„οΈ JDBC Fundamentals

import java.sql.*;

public class JDBCDemo {
    private static final String URL = "jdbc:mysql://localhost:3306/mydb";
    private static final String USER = "root";
    private static final String PASSWORD = "password";

    public static void main(String[] args) {
        // 1. Load driver (automatic in Java 6+)
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        // 2. Create connection
        try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD)) {
            System.out.println("Connected to database!");

            // CREATE
            createUser(conn, "Alice", "alice@example.com");

            // READ
            readUsers(conn);

            // UPDATE
            updateUser(conn, 1, "alice.smith@example.com");

            // DELETE
            deleteUser(conn, 1);

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    // CREATE
    private static void createUser(Connection conn, String name, String email) 
            throws SQLException {
        String sql = "INSERT INTO users (name, email) VALUES (?, ?)";

        try (PreparedStatement stmt = conn.prepareStatement(sql)) {
            stmt.setString(1, name);
            stmt.setString(2, email);

            int rows = stmt.executeUpdate();
            System.out.println(rows + " row(s) inserted");
        }
    }

    // READ
    private static void readUsers(Connection conn) throws SQLException {
        String sql = "SELECT * FROM users";

        try (Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery(sql)) {

            while (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String email = rs.getString("email");

                System.out.println("User: " + id + ", " + name + ", " + email);
            }
        }
    }

    // UPDATE
    private static void updateUser(Connection conn, int id, String newEmail) 
            throws SQLException {
        String sql = "UPDATE users SET email = ? WHERE id = ?";

        try (PreparedStatement stmt = conn.prepareStatement(sql)) {
            stmt.setString(1, newEmail);
            stmt.setInt(2, id);

            int rows = stmt.executeUpdate();
            System.out.println(rows + " row(s) updated");
        }
    }

    // DELETE
    private static void deleteUser(Connection conn, int id) throws SQLException {
        String sql = "DELETE FROM users WHERE id = ?";

        try (PreparedStatement stmt = conn.prepareStatement(sql)) {
            stmt.setInt(1, id);

            int rows = stmt.executeUpdate();
            System.out.println(rows + " row(s) deleted");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸš€ Modern Java (11, 17, 21)

1. var Keyword (Java 10)

public class VarDemo {
    public static void main(String[] args) {
        // Type inferred by compiler
        var name = "Alice";  // String
        var age = 25;  // int
        var list = new ArrayList<String>();  // ArrayList<String>

        // ❌ Can't use with null
        // var x = null;  // Error!

        // ❌ Can't use without initialization
        // var y;  // Error!

        // βœ… Can use in loops
        for (var i = 0; i < 10; i++) {
            System.out.println(i);
        }

        var numbers = List.of(1, 2, 3, 4, 5);
        for (var num : numbers) {
            System.out.println(num);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

2. Switch Expressions (Java 14)

public class SwitchExpressionsDemo {
    public static void main(String[] args) {
        // Old way
        String day = "MONDAY";
        int numLetters = 0;
        switch (day) {
            case "MONDAY":
            case "FRIDAY":
            case "SUNDAY":
                numLetters = 6;
                break;
            case "TUESDAY":
                numLetters = 7;
                break;
            default:
                numLetters = 0;
        }

        // βœ… New way (Java 14+)
        int numLetters2 = switch (day) {
            case "MONDAY", "FRIDAY", "SUNDAY" -> 6;
            case "TUESDAY" -> 7;
            case "THURSDAY", "SATURDAY" -> 8;
            case "WEDNESDAY" -> 9;
            default -> 0;
        };

        // βœ… With yield
        int result = switch (day) {
            case "MONDAY", "FRIDAY" -> {
                System.out.println("Working day");
                yield 6;
            }
            case "SATURDAY", "SUNDAY" -> {
                System.out.println("Weekend!");
                yield 8;
            }
            default -> 0;
        };

        System.out.println("Letters: " + numLetters2);
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Text Blocks (Java 15)

public class TextBlocksDemo {
    public static void main(String[] args) {
        // Old way
        String html = "<html>\n" +
                     "  <body>\n" +
                     "    <h1>Hello</h1>\n" +
                     "  </body>\n" +
                     "</html>";

        // βœ… New way with text blocks
        String html2 = """
                <html>
                  <body>
                    <h1>Hello</h1>
                  </body>
                </html>
                """;

        String json = """
                {
                  "name": "Alice",
                  "age": 30,
                  "email": "alice@example.com"
                }
                """;

        System.out.println(json);
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Records (Java 16)

// Old way
class PersonOld {
    private final String name;
    private final int age;

    public PersonOld(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }

    @Override
    public boolean equals(Object o) {
        // ... lots of boilerplate
    }

    @Override
    public int hashCode() {
        // ... more boilerplate
    }

    @Override
    public String toString() {
        return "Person[name=" + name + ", age=" + age + "]";
    }
}

// βœ… New way with Records
record Person(String name, int age) {
    // Compiler generates:
    // - Constructor
    // - Getters (name(), age())
    // - equals(), hashCode(), toString()

    // Can add custom methods
    public boolean isAdult() {
        return age >= 18;
    }
}

public class RecordsDemo {
    public static void main(String[] args) {
        Person alice = new Person("Alice", 25);
        Person bob = new Person("Bob", 30);

        System.out.println(alice.name());  // Getter
        System.out.println(alice.age());   // Getter
        System.out.println(alice);         // toString()
        System.out.println(alice.isAdult()); // Custom method

        System.out.println(alice.equals(bob)); // false
    }
}
Enter fullscreen mode Exit fullscreen mode

5. Sealed Classes (Java 17)

// Only specific classes can extend
sealed class Shape permits Circle, Rectangle, Triangle {
    abstract double area();
}

final class Circle extends Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    double area() {
        return Math.PI * radius * radius;
    }
}

final class Rectangle extends Shape {
    private double length, width;

    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }

    @Override
    double area() {
        return length * width;
    }
}

final class Triangle extends Shape {
    private double base, height;

    public Triangle(double base, double height) {
        this.base = base;
        this.height = height;
    }

    @Override
    double area() {
        return 0.5 * base * height;
    }
}

// ❌ Can't extend Shape
// class Square extends Shape { } // Compile error!

public class SealedClassesDemo {
    public static void main(String[] args) {
        Shape circle = new Circle(5);
        Shape rectangle = new Rectangle(4, 6);

        System.out.println("Circle area: " + circle.area());
        System.out.println("Rectangle area: " + rectangle.area());
    }
}
Enter fullscreen mode Exit fullscreen mode

6. Pattern Matching for instanceof (Java 16)

public class PatternMatchingDemo {
    public static void main(String[] args) {
        Object obj = "Hello World";

        // Old way
        if (obj instanceof String) {
            String str = (String) obj;  // Cast needed
            System.out.println(str.length());
        }

        // βœ… New way (Java 16+)
        if (obj instanceof String str) {  // Pattern variable
            System.out.println(str.length());  // No cast needed!
        }

        // Works with else
        if (obj instanceof String str) {
            System.out.println("String: " + str.toUpperCase());
        } else if (obj instanceof Integer num) {
            System.out.println("Integer: " + num * 2);
        } else {
            System.out.println("Unknown type");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

(Due to length, continuing with Interview Questions section next...)

This is getting massive! Should I continue with the remaining sections:

  • Top 50 Interview Questions with solutions
  • System Design Basics
  • Interview Strategy

Let me know if you want the complete file or if this is good! πŸš€

πŸ’Ό Top 50 Interview Questions

Core Java Questions

1. What is the difference between JDK, JRE, and JVM?

  • JVM: Executes bytecode, platform-specific
  • JRE: JVM + libraries to run Java apps
  • JDK: JRE + development tools (compiler, debugger)

2. Why is String immutable in Java?

  • Security: Strings used in critical operations (passwords, DB connections)
  • Thread Safety: Can be safely shared
  • String Pool: Allows caching
  • Hashcode Caching: Calculated once

3. What is the difference between == and equals()?

  • ==: Compares references (memory addresses)
  • equals(): Compares content (can be overridden)
String s1 = new String("Hello");
String s2 = new String("Hello");
System.out.println(s1 == s2);       // false (different objects)
System.out.println(s1.equals(s2));  // true (same content)
Enter fullscreen mode Exit fullscreen mode

4. Explain HashMap internal working

HashMap uses array of buckets + linked list/tree for collisions.

Key β†’ hashCode() β†’ index = hash & (n-1) β†’ bucket[index]

Bucket structure:
[0] β†’ null
[1] β†’ Node(key1, val1) β†’ Node(key2, val2)  // Collision chain
[2] β†’ null
[3] β†’ Node(key3, val3)
Enter fullscreen mode Exit fullscreen mode

Process:

  1. Compute hashCode() of key
  2. Calculate index: (n - 1) & hash
  3. If bucket empty, add new node
  4. If bucket has nodes, traverse chain:
    • If key exists (using equals()), update value
    • Else, add new node
  5. If chain length > 8, convert to tree (Java 8+)

5. What is the contract between hashCode() and equals()?

// RULE: If a.equals(b) is true, then a.hashCode() == b.hashCode()

@Override
public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null || getClass() != obj.getClass()) return false;
    Person person = (Person) obj;
    return age == person.age && Objects.equals(name, person.name);
}

@Override
public int hashCode() {
    return Objects.hash(name, age);
}
Enter fullscreen mode Exit fullscreen mode

6. Difference between ArrayList and LinkedList?

Feature ArrayList LinkedList
Structure Dynamic array Doubly linked list
Access O(1) O(n)
Insert at end O(1) amortized O(1)
Insert at beginning O(n) O(1)
Memory Less per element More (prev, next pointers)

7. What is fail-fast and fail-safe?

Fail-fast: Throws ConcurrentModificationException if modified during iteration

List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
for (String s : list) {
    list.remove(s);  // ConcurrentModificationException!
}
Enter fullscreen mode Exit fullscreen mode

Fail-safe: Works on copy, no exception

List<String> list = new CopyOnWriteArrayList<>(Arrays.asList("A", "B", "C"));
for (String s : list) {
    list.remove(s);  // Works fine!
}
Enter fullscreen mode Exit fullscreen mode

8. Difference between throw and throws?

// throw: Actually throws exception
public void method1() {
    throw new IllegalArgumentException("Invalid!");
}

// throws: Declares method might throw exception
public void method2() throws IOException {
    // Method might throw IOException
}
Enter fullscreen mode Exit fullscreen mode

9. What is try-with-resources?

// Old way
BufferedReader br = null;
try {
    br = new BufferedReader(new FileReader("file.txt"));
    // Use br
} finally {
    if (br != null) br.close();
}

// βœ… Try-with-resources (Java 7+)
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
    // Use br - automatically closed
}
Enter fullscreen mode Exit fullscreen mode

10. What are functional interfaces?

Interface with exactly one abstract method. Can use lambda.

@FunctionalInterface
interface Calculator {
    int calculate(int a, int b);
}

Calculator add = (a, b) -> a + b;
Enter fullscreen mode Exit fullscreen mode

OOP Questions

11. Difference between abstract class and interface?

Feature Abstract Class Interface
Methods Abstract + concrete Abstract + default + static
Variables Any public static final only
Constructor Yes No
Extends/Implements One class Multiple interfaces

12. Can we override static methods?

No! Static methods are hidden, not overridden.

class Parent {
    static void method() { System.out.println("Parent"); }
}

class Child extends Parent {
    static void method() { System.out.println("Child"); }  // Hiding, not overriding
}

Parent p = new Child();
p.method();  // Prints "Parent" - uses reference type!
Enter fullscreen mode Exit fullscreen mode

13. Can we override private methods?

No! Private methods are not inherited.

14. What is covariant return type?

Overriding method can return subtype of parent's return type.

class Parent {
    Number getValue() { return 10; }
}

class Child extends Parent {
    @Override
    Integer getValue() { return 20; }  // βœ… Integer is subtype of Number
}
Enter fullscreen mode Exit fullscreen mode

15. Explain method overloading and overriding

Overloading: Same name, different parameters (compile-time polymorphism)
Overriding: Same signature, different implementation (runtime polymorphism)

Multithreading Questions

16. Difference between process and thread?

  • Process: Independent program, separate memory
  • Thread: Lightweight, shares process memory

17. How to create threads in Java?

Three ways:

  1. Extend Thread
  2. Implement Runnable (preferred)
  3. Lambda expression (Java 8+)

18. What is synchronized keyword?

Ensures only one thread executes code block at a time.

synchronized void method() {
    // Only one thread at a time
}

// OR

synchronized(this) {
    // Critical section
}
Enter fullscreen mode Exit fullscreen mode

19. Difference between wait() and sleep()?

Feature wait() sleep()
Class Object Thread
Lock Releases Doesn't release
Purpose Wait for notification Pause execution

20. What is deadlock?

Two threads waiting for each other's locks.

Prevention:

  • Lock ordering
  • Lock timeout
  • Avoid nested locks

Java 8+ Questions

21. What is Stream API?

Functional-style operations on collections.

list.stream()
    .filter(x -> x > 10)
    .map(x -> x * 2)
    .collect(Collectors.toList());
Enter fullscreen mode Exit fullscreen mode

22. Difference between map() and flatMap()?

  • map(): One-to-one transformation
  • flatMap(): One-to-many, flattens result
// map: [[1,2], [3,4]] β†’ [Stream<Integer>, Stream<Integer>]
// flatMap: [[1,2], [3,4]] β†’ [1, 2, 3, 4]
Enter fullscreen mode Exit fullscreen mode

23. What is Optional?

Container that may or may not contain value. Avoids NullPointerException.

Optional<String> optional = Optional.ofNullable(getName());
String name = optional.orElse("Unknown");
Enter fullscreen mode Exit fullscreen mode

24. What is lambda expression?

Anonymous function for implementing functional interfaces.

// Before
Runnable r = new Runnable() {
    public void run() { System.out.println("Hello"); }
};

// After
Runnable r = () -> System.out.println("Hello");
Enter fullscreen mode Exit fullscreen mode

25. What is method reference?

Shorthand for lambda calling single method.

// Lambda
list.forEach(s -> System.out.println(s));

// Method reference
list.forEach(System.out::println);
Enter fullscreen mode Exit fullscreen mode

Memory & Performance

26. Explain Java memory model

HEAP: Objects, instance variables
STACK: Local variables, method calls
METASPACE: Class metadata (Java 8+, replaced PermGen)
Enter fullscreen mode Exit fullscreen mode

27. What is garbage collection?

Automatic memory management - removes unreferenced objects.

GC Types:

  • Serial GC
  • Parallel GC
  • CMS (Concurrent Mark Sweep)
  • G1 GC (default Java 9+)
  • ZGC, Shenandoah (low latency)

28. How to avoid memory leaks?

  1. Close resources (use try-with-resources)
  2. Remove unused listeners
  3. Weak references for caches
  4. Clear collections
  5. Don't hold references unnecessarily

29. What is String pool?

Special memory area in heap where String literals are stored. Reuses identical strings.

String s1 = "Hello";  // Pool
String s2 = "Hello";  // Same reference
String s3 = new String("Hello");  // Heap (not pool)
String s4 = s3.intern();  // Moves to pool
Enter fullscreen mode Exit fullscreen mode

30. Difference between String, StringBuilder, StringBuffer?

Feature String StringBuilder StringBuffer
Mutability Immutable Mutable Mutable
Thread-safe Yes No Yes
Performance Slow (creates new) Fast Slower (synchronized)

Collections Questions

31. Difference between HashMap and Hashtable?

Feature HashMap Hashtable
Null keys 1 allowed Not allowed
Null values Allowed Not allowed
Thread-safe No Yes
Performance Faster Slower

32. Difference between HashSet and TreeSet?

Feature HashSet TreeSet
Order No order Sorted
Null 1 allowed Not allowed
Performance O(1) O(log n)

33. What is ConcurrentHashMap?

Thread-safe HashMap using segment locking. Better than Hashtable.

34. Difference between Iterator and ListIterator?

  • Iterator: Forward only, for all collections
  • ListIterator: Bidirectional, only for Lists

35. What is Comparable vs Comparator?

Comparable: Natural ordering (inside class)

class Student implements Comparable<Student> {
    public int compareTo(Student other) {
        return this.age - other.age;
    }
}
Enter fullscreen mode Exit fullscreen mode

Comparator: Custom ordering (outside class)

Comparator<Student> nameComparator = 
    (s1, s2) -> s1.name.compareTo(s2.name);
Enter fullscreen mode Exit fullscreen mode

Exception Handling

36. Difference between checked and unchecked exceptions?

Checked: Must handle (IOException, SQLException)
Unchecked: Optional (NullPointerException, ArrayIndexOutOfBoundsException)

37. Can we have try without catch?

Yes, with finally or try-with-resources.

try {
    // code
} finally {
    // cleanup
}
Enter fullscreen mode Exit fullscreen mode

38. Can catch block catch multiple exceptions?

try {
    // code
} catch (IOException | SQLException e) {
    // Handle both
}
Enter fullscreen mode Exit fullscreen mode

39. What is exception chaining?

Wrapping one exception inside another.

try {
    // code
} catch (IOException e) {
    throw new RuntimeException("Failed to process", e);  // Chaining
}
Enter fullscreen mode Exit fullscreen mode

40. Order of catch blocks?

Specific exceptions first, generic later.

try {
    // code
} catch (FileNotFoundException e) {  // Specific
    // handle
} catch (IOException e) {  // Generic
    // handle
}
Enter fullscreen mode Exit fullscreen mode

Design & Best Practices

41. SOLID Principles?

  • Single Responsibility
  • Open/Closed
  • Liskov Substitution
  • Interface Segregation
  • Dependency Inversion

42. What is Dependency Injection?

Providing dependencies from outside instead of creating inside.

// ❌ Bad
class Service {
    private Repository repo = new Repository();  // Tight coupling
}

// βœ… Good
class Service {
    private Repository repo;

    public Service(Repository repo) {  // Dependency injected
        this.repo = repo;
    }
}
Enter fullscreen mode Exit fullscreen mode

43. Singleton vs Static class?

  • Singleton: Single instance, can implement interface, lazy loading
  • Static: No instance, can't implement interface, immediate loading

44. When to use abstract class vs interface?

  • Abstract class: Common base implementation, "is-a" relationship
  • Interface: Capabilities, "can-do" relationship, multiple inheritance

45. What is immutability?

Object whose state cannot change after creation.

final class ImmutablePerson {
    private final String name;
    private final int age;

    // No setters!
    // Deep copy for mutable fields
}
Enter fullscreen mode Exit fullscreen mode

Miscellaneous

46. Difference between final, finally, finalize?

  • final: Variable/method/class cannot be changed/overridden/extended
  • finally: Block always executes (with try-catch)
  • finalize(): Called by GC before destroying object (deprecated Java 9+)

47. What is reflection?

Inspect and modify classes/methods/fields at runtime.

Class<?> clazz = MyClass.class;
Method method = clazz.getMethod("methodName");
method.invoke(instance, args);
Enter fullscreen mode Exit fullscreen mode

48. What is marker interface?

Interface with no methods. Examples: Serializable, Cloneable, Remote

49. Difference between Serializable and Externalizable?

  • Serializable: Automatic, JVM controls
  • Externalizable: Manual, you control what to serialize

50. What is transient keyword?

Prevents field from being serialized.

class User implements Serializable {
    private String username;
    private transient String password;  // Won't be serialized
}
Enter fullscreen mode Exit fullscreen mode

πŸ—οΈ System Design Basics

1. How would you design a URL Shortener?

Requirements:

  • Shorten long URLs
  • Redirect to original
  • Track clicks

Design:

Components:
1. Database: Store mapping (shortURL β†’ longURL)
2. API:
   - POST /shorten: Create short URL
   - GET /{shortCode}: Redirect

Algorithm:
1. Generate unique short code (Base62 encoding of auto-increment ID)
2. Store mapping in database
3. Return short URL

Scale:
- Cache frequently accessed URLs (Redis)
- Use CDN for redirects
- Database sharding by hash of short code
Enter fullscreen mode Exit fullscreen mode

2. Design a Rate Limiter

Approaches:

  1. Token Bucket: Add tokens at fixed rate, consume per request
  2. Sliding Window: Track requests in time window
  3. Leaky Bucket: Process requests at fixed rate

Implementation:

class RateLimiter {
    private final int maxRequests;
    private final long windowMs;
    private final Map<String, Queue<Long>> userRequests;

    public boolean allowRequest(String userId) {
        long now = System.currentTimeMillis();
        Queue<Long> requests = userRequests.computeIfAbsent(
            userId, k -> new LinkedList<>());

        // Remove old requests
        while (!requests.isEmpty() && 
               requests.peek() < now - windowMs) {
            requests.poll();
        }

        if (requests.size() < maxRequests) {
            requests.offer(now);
            return true;
        }
        return false;
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Design a Cache System (LRU Cache)

class LRUCache<K, V> {
    private final int capacity;
    private final Map<K, Node<K, V>> map;
    private final DoublyLinkedList<K, V> list;

    class Node<K, V> {
        K key;
        V value;
        Node<K, V> prev, next;
    }

    public V get(K key) {
        if (!map.containsKey(key)) return null;

        Node<K, V> node = map.get(key);
        list.moveToHead(node);  // Recently used
        return node.value;
    }

    public void put(K key, V value) {
        if (map.containsKey(key)) {
            Node<K, V> node = map.get(key);
            node.value = value;
            list.moveToHead(node);
        } else {
            if (map.size() >= capacity) {
                // Remove LRU
                Node<K, V> tail = list.removeTail();
                map.remove(tail.key);
            }

            Node<K, V> newNode = new Node<>();
            newNode.key = key;
            newNode.value = value;

            list.addToHead(newNode);
            map.put(key, newNode);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

🎯 Interview Strategy

1. Before the Interview

Technical Preparation (4-6 weeks):

  • Week 1-2: Core Java (OOP, Collections, Multithreading)
  • Week 3-4: DSA (Arrays, LinkedList, Trees, Graphs)
  • Week 5-6: System Design + Mock interviews

Resources:

  • LeetCode (150-200 problems)
  • HackerRank/CodeSignal
  • System Design Primer (GitHub)
  • Mock interviews (Pramp, interviewing.io)

Resume:

  • Projects with impact (metrics!)
  • Technologies used
  • Your specific contributions
  • Keep it 1-2 pages

2. During the Interview

STAR Method for Behavioral Questions:

  • Situation: Set context
  • Task: What needed to be done
  • Action: What YOU did
  • Result: Outcome (with metrics)

Example:
"Tell me about a challenging project"

Bad Answer:
"I worked on a website. It was hard."

Good Answer:
"Situation: Our e-commerce site had 5-second page load times, causing 20% cart abandonment.
Task: I was asked to optimize performance.
Action: I implemented lazy loading, optimized database queries (added indexes), and introduced Redis caching.
Result: Reduced load time to 1.5 seconds, cart abandonment dropped to 8%, and revenue increased 15%."

3. Coding Interview Framework

Step-by-step:

  1. Clarify: Ask questions (input size, edge cases, constraints)
  2. Example: Walk through example
  3. Approach: Explain your approach
  4. Code: Write clean code
  5. Test: Walk through test cases
  6. Optimize: Discuss time/space complexity

Example Problem:
"Find if array has duplicate"

// ❌ Don't just code immediately!

// βœ… Follow framework:
// 1. Clarify: "Can array be empty? Are numbers only integers?"
// 2. Example: [1,2,3,1] β†’ true, [1,2,3,4] β†’ false
// 3. Approach: "I'll use a HashSet for O(n) time, O(n) space"
// 4. Code:
public boolean hasDuplicate(int[] nums) {
    Set<Integer> seen = new HashSet<>();
    for (int num : nums) {
        if (seen.contains(num)) return true;
        seen.add(num);
    }
    return false;
}
// 5. Test: Walk through examples
// 6. Optimize: "Could sort first for O(1) space but O(n log n) time"
Enter fullscreen mode Exit fullscreen mode

4. System Design Interview

Framework:

  1. Requirements: Functional + Non-functional
  2. Estimation: Scale, storage, bandwidth
  3. API Design: Endpoints
  4. Database Schema: Tables, relationships
  5. High-Level Design: Components, flow
  6. Deep Dive: Specific component
  7. Bottlenecks: Identify and solve

Example: Design Twitter

  1. Requirements:

    • Post tweets
    • Follow users
    • View timeline
    • Scale: 300M users, 200M daily active
  2. API:

    • POST /tweet
    • POST /follow/{userId}
    • GET /timeline
  3. Database:

    • Users, Tweets, Follows tables
    • Use Cassandra for tweets (write-heavy)
    • Use Redis for timeline cache
  4. Design:

   Client β†’ Load Balancer β†’ App Servers β†’ 
   β”œβ†’ Tweet Service β†’ Cassandra
   β”œβ†’ Timeline Service β†’ Redis
   β””β†’ Follow Service β†’ MySQL
Enter fullscreen mode Exit fullscreen mode

5. Common Mistakes to Avoid

❌ Don't:

  • Start coding immediately
  • Stay silent (think out loud!)
  • Give up when stuck
  • Ignore edge cases
  • Write messy code
  • Argue with interviewer

βœ… Do:

  • Ask clarifying questions
  • Communicate your thought process
  • Start with brute force, then optimize
  • Write clean, readable code
  • Handle edge cases
  • Be humble and open to feedback

6. Questions to Ask Interviewer

Technical:

  • "What's the tech stack?"
  • "How do you handle deployments?"
  • "What's the code review process?"

Team:

  • "What's the team structure?"
  • "How do you onboard new engineers?"
  • "What's a typical day like?"

Growth:

  • "What learning opportunities exist?"
  • "How is performance evaluated?"
  • "What's the career path?"

7. Salary Negotiation

Research:

  • Glassdoor, Levels.fyi
  • Know your market value
  • Consider total compensation

Strategy:

  1. Let them make first offer
  2. Don't immediately accept
  3. Ask for time to consider
  4. Negotiate total package (not just base)
  5. Be willing to walk away

Example:
"Thank you for the offer. I'm excited about the role. Based on my research and experience, I was expecting the base to be in the [X-Y] range. Can we discuss this?"


πŸŽ“ Summary & Final Checklist

Concepts Mastered in Part 4:

βœ… Java 8+ Streams (filter, map, reduce, collect)
βœ… Optional for null-safety
βœ… Functional interfaces (Predicate, Function, Consumer, Supplier)
βœ… Generics & type safety
βœ… Annotations & Reflection
βœ… Design Patterns (Singleton, Factory, Builder, Observer, Strategy)
βœ… File I/O & Serialization
βœ… JDBC fundamentals
βœ… Modern Java (var, switch expressions, text blocks, records, sealed classes)
βœ… Top 50 interview questions
βœ… System design basics
βœ… Interview strategy

Complete Java Roadmap:

Part 1: Fundamentals

  • JVM, Data Types, Operators, Control Flow

Part 2: OOP & Core

  • OOP Principles, Collections, Exceptions, Strings

Part 3: Multithreading

  • Threads, Synchronization, wait/notify, Thread Pools

Part 4: Advanced & Interview (this guide!)

  • Java 8+, Generics, Design Patterns, Modern Java, Interview Prep

Next Steps:

  1. Practice: LeetCode (150-200 problems)
  2. Build: Personal projects showcasing skills
  3. Learn: Spring Boot, Microservices
  4. Network: LinkedIn, GitHub, Twitter
  5. Apply: Target companies, prepare for interviews
  6. Keep Learning: Java keeps evolving!

Resources:

Books:

  • Effective Java (Joshua Bloch) - Must read!
  • Java Concurrency in Practice
  • Head First Design Patterns

Online:

  • Oracle Java Documentation
  • Baeldung (excellent tutorials)
  • Java Brains (YouTube)

Practice:

  • LeetCode
  • HackerRank
  • CodeSignal

πŸ‘¨β€πŸ’» Author

Rajat


πŸŽ‰ Series Complete!

You've completed the Java Core Mastery series! You now have:

βœ… Part 1: Rock-solid fundamentals
βœ… Part 2: Advanced OOP & Collections mastery
βœ… Part 3: Multithreading expertise
βœ… Part 4: Advanced topics & interview readiness

You're now ready to:

  • Ace technical interviews at top companies
  • Build production-ready Java applications
  • Contribute to open-source projects
  • Mentor other developers

Thank you for learning with me! Keep coding, keep growing! πŸš€


Made with ❀️ for developers who want to master Java

If this helped you, please share it with others and give it a ⭐ reaction!


πŸ“š Quick Reference

Essential Commands to Remember:

// Streams
list.stream().filter().map().collect(Collectors.toList());

// Optional
Optional.ofNullable(value).orElse(default);

// Lambda
(parameters) -> expression

// Method Reference
ClassName::methodName

// Try-with-resources
try (Resource r = new Resource()) { }

// Records
record Point(int x, int y) { }

// Switch Expression
int result = switch (day) {
    case MONDAY, FRIDAY -> 6;
    default -> 0;
};
Enter fullscreen mode Exit fullscreen mode

Complexity Cheat Sheet:

Operation ArrayList LinkedList HashMap TreeMap
Access O(1) O(n) O(1) O(log n)
Insert O(n) O(1) O(1) O(log n)
Delete O(n) O(1) O(1) O(log n)
Search O(n) O(n) O(1) O(log n)

END OF JAVA CORE MASTERY SERIES πŸŽ“βœ¨

Top comments (0)