Master Java 8+, Generics, Design Patterns, Modern Java, and ace your technical interviews with real-world examples
π Table of Contents
- Introduction
- Java 8+ Stream API Mastery
- Optional: Avoiding NullPointerException
- Functional Programming in Java
- Generics & Type Safety
- Annotations & Reflection
- Design Patterns
- File I/O & Serialization
- JDBC Fundamentals
- Modern Java (11, 17, 21)
- Top 50 Interview Questions
- System Design Basics
- Interview Strategy
π― 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());
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);
}
}
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]
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]
}
}
π 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");
}
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());
}
}
}
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
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);
π§ 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));
}
}
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));
}
}
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));
}
}
}
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
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);
}
}
π 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!
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
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);
}
}
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);
}
}
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}
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());
}
}
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);
}
}
(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
}
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; }
}
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();
}
}
}
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
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);
}
}
π¨ 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");
}
}
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();
}
}
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());
}
}
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);
}
}
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);
}
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);
}
}
π 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();
}
}
}
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();
}
}
}
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();
}
}
}
ποΈ 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");
}
}
}
π 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);
}
}
}
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);
}
}
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);
}
}
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
}
}
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());
}
}
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");
}
}
}
(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)
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)
Process:
- Compute
hashCode()of key - Calculate index:
(n - 1) & hash - If bucket empty, add new node
- If bucket has nodes, traverse chain:
- If key exists (using
equals()), update value - Else, add new node
- If key exists (using
- 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);
}
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!
}
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!
}
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
}
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
}
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;
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!
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
}
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:
- Extend
Thread - Implement
Runnable(preferred) - 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
}
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());
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]
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");
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");
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);
Memory & Performance
26. Explain Java memory model
HEAP: Objects, instance variables
STACK: Local variables, method calls
METASPACE: Class metadata (Java 8+, replaced PermGen)
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?
- Close resources (use try-with-resources)
- Remove unused listeners
- Weak references for caches
- Clear collections
- 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
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;
}
}
Comparator: Custom ordering (outside class)
Comparator<Student> nameComparator =
(s1, s2) -> s1.name.compareTo(s2.name);
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
}
38. Can catch block catch multiple exceptions?
try {
// code
} catch (IOException | SQLException e) {
// Handle both
}
39. What is exception chaining?
Wrapping one exception inside another.
try {
// code
} catch (IOException e) {
throw new RuntimeException("Failed to process", e); // Chaining
}
40. Order of catch blocks?
Specific exceptions first, generic later.
try {
// code
} catch (FileNotFoundException e) { // Specific
// handle
} catch (IOException e) { // Generic
// handle
}
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;
}
}
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
}
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);
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
}
ποΈ 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
2. Design a Rate Limiter
Approaches:
- Token Bucket: Add tokens at fixed rate, consume per request
- Sliding Window: Track requests in time window
- 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;
}
}
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);
}
}
}
π― 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:
- Clarify: Ask questions (input size, edge cases, constraints)
- Example: Walk through example
- Approach: Explain your approach
- Code: Write clean code
- Test: Walk through test cases
- 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"
4. System Design Interview
Framework:
- Requirements: Functional + Non-functional
- Estimation: Scale, storage, bandwidth
- API Design: Endpoints
- Database Schema: Tables, relationships
- High-Level Design: Components, flow
- Deep Dive: Specific component
- Bottlenecks: Identify and solve
Example: Design Twitter
-
Requirements:
- Post tweets
- Follow users
- View timeline
- Scale: 300M users, 200M daily active
-
API:
- POST /tweet
- POST /follow/{userId}
- GET /timeline
-
Database:
- Users, Tweets, Follows tables
- Use Cassandra for tweets (write-heavy)
- Use Redis for timeline cache
Design:
Client β Load Balancer β App Servers β
ββ Tweet Service β Cassandra
ββ Timeline Service β Redis
ββ Follow Service β MySQL
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:
- Let them make first offer
- Don't immediately accept
- Ask for time to consider
- Negotiate total package (not just base)
- 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:
- Practice: LeetCode (150-200 problems)
- Build: Personal projects showcasing skills
- Learn: Spring Boot, Microservices
- Network: LinkedIn, GitHub, Twitter
- Apply: Target companies, prepare for interviews
- 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
- GitHub: @rajat12826
π 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;
};
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)