Phase 2 — OOP + Core Java
This phase is where Java starts to feel like real Java rather than basic programming.
We will move from simple classes and objects into inheritance, polymorphism, interfaces, composition, immutability, exceptions, collections, and generics.
Chapters in this Phase
Part A — Object-Oriented Java
Classes, Objects & Instance State
Constructors,
this& Constructor OverloadingEncapsulation & Access Modifiers
static,final& Instance vs Class MembersInheritance &
superMethod Overriding & Polymorphism
Abstraction & Abstract Classes
Interfaces
Composition vs Inheritance
Enums
Records & Immutable Data
The
ObjectClass —toString(),equals()&hashCode()Nested & Inner Classes
Part B — Exception Handling
Exceptions &
try/catchthrow,throws, Custom Exceptions & PropagationTry-with-Resources & Exception Design
Part C — Collections
Collections Fundamentals &
ArrayListSets —
HashSet,LinkedHashSet&TreeSetMaps —
HashMap,LinkedHashMap&TreeMapQueues, Deques & Priority Queues
Iterators, Sorting & Comparators
Part D — Generics
Generic Classes & Methods
Bounded Types & Wildcards
extends,super, PECS & Type Erasure
Part A — Object-Oriented Java
Chapter 1 — Classes, Objects & Instance State
Question
Given below is a code snippet that:
Defines a
BankAccountclass.Creates multiple objects from the same class.
Gives each object its own state.
Uses methods to change and display object state.
What should be the output of the following code?
// Define a class that describes what a BankAccount object contains and can do.
class BankAccount {
// Each object gets its own copy of this field.
String owner;
// Each object gets its own balance.
double balance;
// This method adds money to this particular account.
void deposit(double amount) {
balance = balance + amount;
}
// This method displays this particular account's information.
void showBalance() {
System.out.println(owner + ": " + balance);
}
}
public class Main {
public static void main(String[] args) {
// Create the first BankAccount object.
BankAccount account1 = new BankAccount();
// Give the first object its own state.
account1.owner = "Alice";
account1.balance = 1000;
// Create a second BankAccount object.
BankAccount account2 = new BankAccount();
// Give the second object different state.
account2.owner = "Bob";
account2.balance = 500;
// Change only Alice's account.
account1.deposit(250);
// Display both accounts.
account1.showBalance();
account2.showBalance();
}
}Answer
Alice: 1250.0
Bob: 500.0Step-by-step explanation
BankAccountis a class. Think of it as a blueprint describing what a bank account object should contain and do.account1refers to oneBankAccountobject.account2refers to anotherBankAccountobject.Each object has its own
ownerandbalance.account1.deposit(250)changes only Alice's balance.Alice's balance becomes
1250.Bob's balance remains
500.Therefore two different objects can be created from the same class while holding different data.
How to read the important code
BankAccount account1 = new BankAccount();Read it naturally:
"Create a BankAccount variable called account1 and assign it a new BankAccount object."
account1 is a reference variable. It refers to the object.
Beginner trap
The class itself is not the individual account.
Class = blueprint. Object = actual thing created from that blueprint.
Key takeaway
A class describes objects; each object can have its own instance state.
Chapter 2 — Constructors, this & Constructor Overloading
Question
Given below is a code snippet that:
Creates objects using constructors.
Uses
thisto refer to the current object.Uses multiple constructors.
Initializes object state when objects are created.
What should be the output of the following code?
class User {
// Store the user's name.
String name;
// Store the user's age.
int age;
// Constructor that accepts both values.
User(String name, int age) {
// 'this.name' means the field belonging to the current object.
this.name = name;
// Store the supplied age in the object's field.
this.age = age;
}
// Another constructor with a different parameter list.
User(String name) {
// Reuse the main constructor with a default age.
this(name, 18);
}
// Display the object's state.
void show() {
System.out.println(name + " - " + age);
}
}
public class Main {
public static void main(String[] args) {
// Call the constructor that accepts name and age.
User user1 = new User("Alice", 25);
// Call the constructor that accepts only name.
User user2 = new User("Bob");
// Display both objects.
user1.show();
user2.show();
}
}Answer
Alice - 25
Bob - 18Step-by-step explanation
new User("Alice", 25)matches the constructor with two parameters.this.name = namestores"Alice"in the object'snamefield.this.age = agestores25.new User("Bob")matches the second constructor.That constructor calls
this(name, 18).This means "call another constructor in the same class."
Bob therefore gets the default age
18.
How to read the important code
this.name = name;Read:
"Assign the parameter named name to this object's name field."
this means the current object.
Beginner trap
These are different:
this.name
namethis.name → object's field.
name → parameter.
Key takeaway
A constructor initializes an object when it is created, and
thisrefers to the current object.
Chapter 3 — Encapsulation & Access Modifiers
Question
Given below is a code snippet that:
Hides internal object data using
private.Controls access through methods.
Validates data before changing it.
Demonstrates encapsulation.
What should be the output of the following code?
class BankAccount {
// Keep the balance private so outside code cannot directly change it.
private double balance;
// Allow money to be deposited through controlled logic.
public void deposit(double amount) {
// Accept only positive deposits.
if (amount > 0) {
balance += amount;
}
}
// Provide controlled access to the balance.
public double getBalance() {
return balance;
}
}
public class Main {
public static void main(String[] args) {
// Create an account.
BankAccount account = new BankAccount();
// Deposit valid money.
account.deposit(1000);
// This negative amount is rejected.
account.deposit(-500);
// Read the balance through the public method.
System.out.println(account.getBalance());
}
}Answer
1000.0Step-by-step explanation
balanceisprivate.Code outside
BankAccountcannot directly manipulate it.deposit()ispublic, so outside code can request a deposit.The method checks whether the amount is positive.
1000is accepted.-500is rejected.The balance therefore remains
1000.
How to read the important code
"Keep balance private and expose controlled public methods."
This is encapsulation: keeping an object's internal data protected and controlling how other code interacts with it.
Beginner trap
Don't think encapsulation means simply "use getters and setters everywhere."
The important idea is:
The object controls its own valid state.
Key takeaway
Encapsulation protects object state and allows the class to control how that state changes.
Chapter 4 — static, final & Instance vs Class Members
Question
Given below is a code snippet that:
Demonstrates instance fields.
Demonstrates a shared
staticfield.Demonstrates a
staticmethod.Demonstrates a
finalconstant.Shows the difference between object state and class-level state.
What should be the output of the following code?
class Employee {
// This value belongs separately to each Employee object.
String name;
// This value is shared by all Employee objects.
static int employeeCount = 0;
// A final value cannot be reassigned after initialization.
static final String COMPANY = "TechCorp";
// Constructor runs whenever an Employee object is created.
Employee(String name) {
// Store the name in this particular object.
this.name = name;
// Increase the shared employee count.
employeeCount++;
}
// Instance method works with a particular Employee object.
void show() {
System.out.println(name + " works at " + COMPANY);
}
// Static method belongs to the class rather than one employee.
static void showCount() {
System.out.println("Employees: " + employeeCount);
}
}
public class Main {
public static void main(String[] args) {
// Create the first employee.
Employee e1 = new Employee("Alice");
// Create the second employee.
Employee e2 = new Employee("Bob");
// Show information for each object.
e1.show();
e2.show();
// Call a class-level method.
Employee.showCount();
}
}Answer
Alice works at TechCorp
Bob works at TechCorp
Employees: 2Step-by-step explanation
namebelongs separately to each object.employeeCountisstatic, so there is one shared copy for the class.Creating Alice increases it to
1.Creating Bob increases it to
2.COMPANYisstatic final, so it represents a class-level constant.show()is an instance method and is called through an object.showCount()is static, so it can be called usingEmployee.showCount().
How to read the important code
static int employeeCount;Read:
"A static integer called employeeCount that belongs to the class."
Beginner trap
A static field is not copied separately into every object.
Key takeaway
Instance members belong to objects; static members belong to the class.
Chapter 5 — Inheritance & super
Question
Given below is a code snippet that:
Creates a parent class.
Creates a child class using
extends.Reuses parent functionality.
Uses
superto call the parent constructor.Adds child-specific behavior.
What should be the output of the following code?
// Parent class containing common employee information.
class Employee {
String name;
// Parent constructor.
Employee(String name) {
this.name = name;
}
// Common behavior.
void work() {
System.out.println(name + " is working");
}
}
// Manager inherits from Employee.
class Manager extends Employee {
// Manager constructor.
Manager(String name) {
// Call the parent constructor.
super(name);
}
// Add behavior specific to Manager.
void manage() {
System.out.println(name + " is managing");
}
}
public class Main {
public static void main(String[] args) {
// Create a Manager object.
Manager manager = new Manager("Alice");
// Use inherited behavior.
manager.work();
// Use Manager-specific behavior.
manager.manage();
}
}Answer
Alice is working
Alice is managingStep-by-step explanation
Manager extends Employeemeans Manager inherits from Employee.new Manager("Alice")calls the Manager constructor.super(name)calls the parentEmployeeconstructor.The parent constructor stores
"Alice"inname.Manager therefore has access to the inherited
work()method.Manager also has its own
manage()method.
How to read the important code
class Manager extends EmployeeRead:
"Define Manager as a subclass of Employee."
Beginner trap
Inheritance does not mean the child object contains a completely separate parent object.
The child object is also an Employee in the type hierarchy.
Key takeaway
Inheritance allows a class to reuse and specialize behavior from another class.
Chapter 6 — Method Overriding & Polymorphism
Question
Given below is a code snippet that:
Uses inheritance.
Overrides a parent method.
Stores child objects in a parent-type variable.
Demonstrates runtime polymorphism.
What should be the output of the following code?
class Animal {
// Define behavior common to animals.
void speak() {
System.out.println("Animal sound");
}
}
class Dog extends Animal {
// Replace the inherited behavior with Dog-specific behavior.
@Override
void speak() {
System.out.println("Woof");
}
}
class Cat extends Animal {
// Replace the inherited behavior with Cat-specific behavior.
@Override
void speak() {
System.out.println("Meow");
}
}
public class Main {
public static void main(String[] args) {
// The variable type is Animal, but the actual object is Dog.
Animal first = new Dog();
// The variable type is Animal, but the actual object is Cat.
Animal second = new Cat();
// Java chooses the overridden method based on the actual object.
first.speak();
second.speak();
}
}Answer
Woof
MeowStep-by-step explanation
firstis declared asAnimal.But the actual object created is a
Dog.Dogoverridesspeak().Therefore
first.speak()runsDog's version.secondrefers to aCat.Therefore
second.speak()runsCat's version.
This is polymorphism.
The simple idea is:
One parent type can refer to different child objects, and the appropriate overridden behavior runs.
How to read the important code
Animal first = new Dog();Read:
"Create a Dog object and store its reference in an Animal variable."
Beginner trap
Don't decide which overridden method runs only by looking at the variable type.
For overridden instance methods, Java uses the actual object.
Key takeaway
Polymorphism lets the same method call behave differently depending on the actual object.
Chapter 7 — Abstraction & Abstract Classes
Question
Given below is a code snippet that:
Defines an abstract class.
Defines an abstract method.
Provides shared concrete behavior.
Forces child classes to implement specific behavior.
What should be the output of the following code?
// Abstract class provides a common structure for payments.
abstract class Payment {
// Every payment must implement this operation.
abstract void pay(double amount);
// All payment types can use this common method.
void receipt(double amount) {
System.out.println("Receipt generated: " + amount);
}
}
class CardPayment extends Payment {
// Provide the payment behavior required by Payment.
@Override
void pay(double amount) {
System.out.println("Paid by card: " + amount);
}
}
public class Main {
public static void main(String[] args) {
// Create a concrete payment object.
Payment payment = new CardPayment();
// Run the child implementation.
payment.pay(500);
// Use behavior supplied by the abstract parent.
payment.receipt(500);
}
}Answer
Paid by card: 500.0
Receipt generated: 500.0Step-by-step explanation
Paymentis abstract.An abstract class is a class intended to provide a common foundation rather than necessarily being created directly.
pay()is abstract, meaning it has no implementation here.CardPaymentmust providepay().receipt()already has an implementation in the parent.The
Paymentvariable can refer to theCardPaymentobject.Calling
pay()therefore runs the CardPayment implementation.
How to read the important code
abstract void pay(double amount);Read:
"An abstract method called pay that subclasses must implement."
Beginner trap
An abstract class can contain normal methods too. It isn't required to contain only abstract methods.
Key takeaway
Abstraction defines what something must do while allowing subclasses to decide how it does it.
Chapter 8 — Interfaces
Question
Given below is a code snippet that:
Defines an interface.
Implements an interface.
Uses multiple implementations of the same contract.
Demonstrates polymorphism through an interface.
What should be the output of the following code?
// Define a capability that payment methods must provide.
interface PaymentMethod {
// Any implementing class must provide this method.
void pay(double amount);
}
class Card implements PaymentMethod {
// Implement the interface method.
@Override
public void pay(double amount) {
System.out.println("Card payment: " + amount);
}
}
class UPI implements PaymentMethod {
// Implement the same contract differently.
@Override
public void pay(double amount) {
System.out.println("UPI payment: " + amount);
}
}
public class Main {
public static void main(String[] args) {
// Store a Card object through the interface type.
PaymentMethod payment1 = new Card();
// Store a UPI object through the same interface type.
PaymentMethod payment2 = new UPI();
// The same method call produces different behavior.
payment1.pay(1000);
payment2.pay(750);
}
}Answer
Card payment: 1000.0
UPI payment: 750.0Step-by-step explanation
PaymentMethoddefines a contract.Cardpromises to follow that contract by usingimplements.UPIalso follows the same contract.Both classes implement
pay().payment1refers to a Card.payment2refers to a UPI.The same interface method call produces different implementations.
How to read the important code
PaymentMethod payment1 = new Card();Read:
"Create a Card and treat it as a PaymentMethod."
Beginner trap
An interface is not the same thing as an object.
The interface defines the contract; Card and UPI provide implementations.
Key takeaway
Interfaces allow unrelated classes to follow the same contract and be used polymorphically.
Chapter 9 — Composition vs Inheritance
Question
Given below is a code snippet that:
Creates independent classes.
Uses composition.
Gives one object another object as a field.
Demonstrates "has-a" rather than "is-a".
What should be the output of the following code?
class Engine {
// Define behavior belonging to the engine.
void start() {
System.out.println("Engine started");
}
}
class Car {
// Car has an Engine.
private Engine engine;
// Receive the Engine when creating the Car.
Car(Engine engine) {
this.engine = engine;
}
// Car delegates engine work to its Engine object.
void start() {
engine.start();
System.out.println("Car started");
}
}
public class Main {
public static void main(String[] args) {
// Create an independent Engine object.
Engine engine = new Engine();
// Put that Engine inside a Car.
Car car = new Car(engine);
// Start the Car.
car.start();
}
}Answer
Engine started
Car startedStep-by-step explanation
Engineis a separate class.Carcontains anEnginereference.Therefore a Car has an Engine.
Car.start()asks its Engine to start.The Engine prints its message.
The Car then prints its own message.
This is composition.
How to read the important code
private Engine engine;Read:
"The Car has an Engine field."
Beginner trap
Don't use inheritance simply because one object is related to another.
Ask:
Is-a? → inheritance may fit.
Has-a? → composition may fit.
A Car has an Engine. A Car is not an Engine.
Key takeaway
Composition builds objects from other objects and is often more flexible than inheritance.
Chapter 10 — Enums
Question
Given below is a code snippet that:
Defines an enum.
Stores an enum value in an object.
Uses an enum in a
switch.Demonstrates why enums are safer than arbitrary strings.
What should be the output of the following code?
// Define a fixed set of valid order statuses.
enum OrderStatus {
NEW,
PAID,
SHIPPED
}
class Order {
// Store the current status.
OrderStatus status;
// Initialize the order status.
Order(OrderStatus status) {
this.status = status;
}
// Describe the current status.
void describe() {
// Select behavior based on the enum value.
switch (status) {
case NEW -> System.out.println("Order received");
case PAID -> System.out.println("Payment completed");
case SHIPPED -> System.out.println("Order shipped");
}
}
}
public class Main {
public static void main(String[] args) {
// Create an order with a specific enum value.
Order order = new Order(OrderStatus.PAID);
// Describe the order.
order.describe();
}
}Answer
Payment completedStep-by-step explanation
OrderStatusdefines three allowed values.The order receives
OrderStatus.PAID.switchchecks that value.The
PAIDcase matches.Java prints
"Payment completed".
How to read the important code
OrderStatus.PAIDRead:
"The PAID value from the OrderStatus enum."
Beginner trap
An enum is not just a collection of strings.
PAID is a specific enum constant of type OrderStatus.
Key takeaway
Use enums when a value should come from a fixed, known set of choices.
Chapter 11 — Records & Immutable Data
Question
Given below is a code snippet that:
Defines a Java record.
Creates a record object.
Reads record components.
Demonstrates the concise syntax used for immutable data carriers.
What should be the output of the following code?
// A record is a concise way to create a data-carrying class.
record Product(String name, double price) {
}
public class Main {
public static void main(String[] args) {
// Create a Product record.
Product product = new Product("Laptop", 75000);
// Access the record components using automatically created methods.
System.out.println(product.name());
System.out.println(product.price());
}
}Answer
Laptop
75000.0Step-by-step explanation
Productis a record with two components:nameandprice.new Product(...)creates a record object.Java automatically provides accessor methods named
name()andprice().The record is designed primarily for carrying data.
Record components cannot simply be reassigned after construction.
How to read the important code
record Product(String name, double price)Read:
"Define a Product record with a String name and a double price."
Beginner trap
Record accessors are:
product.name()not:
product.getName()Key takeaway
Records provide concise Java classes for immutable-style data carriers.
Chapter 12 — Object, toString(), equals() & hashCode()
Question
Given below is a code snippet that:
Overrides
toString().Overrides
equals().Overrides
hashCode().Compares objects by their data rather than identity.
Demonstrates the contract between
equals()andhashCode().
What should be the output of the following code?
class User {
// Store user information.
String name;
int age;
// Initialize the object.
User(String name, int age) {
this.name = name;
this.age = age;
}
// Provide a readable representation of the object.
@Override
public String toString() {
return name + " (" + age + ")";
}
// Compare Users based on their data.
@Override
public boolean equals(Object other) {
// Check whether both references point to the same object.
if (this == other) {
return true;
}
// Check whether the other object is a User.
if (!(other instanceof User user)) {
return false;
}
// Compare the meaningful fields.
return age == user.age && name.equals(user.name);
}
// Generate a hash code using the same fields used by equals().
@Override
public int hashCode() {
return java.util.Objects.hash(name, age);
}
}
public class Main {
public static void main(String[] args) {
// Create two separate User objects containing the same data.
User user1 = new User("Alice", 25);
User user2 = new User("Alice", 25);
// Print the readable representation.
System.out.println(user1);
// Compare the two objects using equals().
System.out.println(user1.equals(user2));
// Check whether equal objects have equal hash codes.
System.out.println(user1.hashCode() == user2.hashCode());
}
}Answer
Alice (25)
true
trueStep-by-step explanation
user1anduser2are two separate objects.toString()determines howuser1is represented when printed.equals()compares the name and age.Both objects contain
"Alice"and25.Therefore
equals()returnstrue.hashCode()uses the same meaningful fields.Therefore equal objects produce the same hash code.
How to read the important code
@Override
public boolean equals(Object other)Read:
"Override the equals method so Users can define what equality means."
Beginner trap
These are different concepts:
user1 == user2checks whether two references point to the same object.
user1.equals(user2)can check whether two objects should be considered equal in value.
Key takeaway
If you override
equals(), you normally need a matchinghashCode()implementation.
Chapter 13 — Nested & Inner Classes
Question
Given below is a code snippet that:
Defines a class inside another class.
Creates an inner-class object.
Allows the inner class to access the outer object's state.
Demonstrates the relationship between outer and inner objects.
What should be the output of the following code?
class Computer {
// Store information belonging to the outer Computer object.
String brand = "Dell";
// Define a non-static inner class.
class Processor {
// Access the outer object's field.
void show() {
System.out.println("Processor belongs to " + brand);
}
}
}
public class Main {
public static void main(String[] args) {
// Create the outer object.
Computer computer = new Computer();
// Create the inner object through the outer object.
Computer.Processor processor = computer.new Processor();
// Run the inner object's method.
processor.show();
}
}Answer
Processor belongs to DellStep-by-step explanation
Computeris the outer class.Processoris an inner class.The inner class is associated with a particular
Computerobject.processortherefore belongs to thecomputerinstance.It can access the outer object's
brandfield.brandcontains"Dell".
How to read the important code
Computer.Processor processor = computer.new Processor();Read:
"Create a Processor inner object associated with the computer object."
Beginner trap
Inner classes are useful, but don't use them simply because Java allows them. Modern Java code often prefers simpler class structures unless the nesting has a clear purpose.
Key takeaway
An inner class can be closely associated with an instance of its outer class.
Part B — Exception Handling
Chapter 14 — Exceptions & try/catch
Question
Given below is a code snippet that:
Demonstrates an exception.
Uses
try.Uses
catch.Shows that the program can recover from an exceptional situation.
Continues execution after handling the exception.
What should be the output of the following code?
public class Main {
public static void main(String[] args) {
// Start code that might produce an exception.
try {
// Convert valid text into an integer.
int number = Integer.parseInt("100");
// Print the valid number.
System.out.println(number);
// This text cannot be converted into an integer.
int invalid = Integer.parseInt("hello");
// This line is never reached.
System.out.println(invalid);
} catch (NumberFormatException e) {
// Handle the invalid number situation.
System.out.println("Invalid number");
}
// Execution continues after the catch block.
System.out.println("Program continues");
}
}Answer
100
Invalid number
Program continuesStep-by-step explanation
"100"can be converted into an integer.Java prints
100."hello"cannot be converted into an integer.Java throws a
NumberFormatException.Java immediately leaves the remaining code inside the
tryblock.The matching
catchblock runs."Invalid number"is printed.Execution continues after the exception handling.
How to read the important code
catch (NumberFormatException e)Read:
"Catch a NumberFormatException and store the exception object in e."
Beginner trap
Once an exception occurs, Java does not continue with the next statement inside that try block.
Key takeaway
Exceptions allow Java programs to handle unexpected situations without necessarily terminating the entire program.
Chapter 15 — throw, throws, Custom Exceptions & Propagation
Question
Given below is a code snippet that:
Defines a custom exception.
Uses
throw.Uses
throws.Lets an exception travel from one method to its caller.
Handles the exception at a higher level.
What should be the output of the following code?
// Define a custom exception for invalid ages.
class InvalidAgeException extends Exception {
// Pass the error message to the parent Exception class.
InvalidAgeException(String message) {
super(message);
}
}
class RegistrationService {
// This method may throw InvalidAgeException.
void register(int age) throws InvalidAgeException {
// Reject users younger than 18.
if (age < 18) {
// Create and throw the custom exception.
throw new InvalidAgeException("User must be 18 or older");
}
// Run when the age is valid.
System.out.println("Registration successful");
}
}
public class Main {
public static void main(String[] args) {
// Create the service.
RegistrationService service = new RegistrationService();
try {
// Ask the service to register a user.
service.register(16);
} catch (InvalidAgeException e) {
// Handle the exception here.
System.out.println(e.getMessage());
}
}
}Answer
User must be 18 or olderStep-by-step explanation
InvalidAgeExceptionis a custom exception.register()declaresthrows InvalidAgeException.The supplied age is
16.16 < 18is true.The method executes
throw new InvalidAgeException(...).The exception leaves
register().It travels back to the caller.
The
catchblock receives it.getMessage()returns the message supplied to the exception.
How to read the important code
throw new InvalidAgeException(...)Read:
"Create an InvalidAgeException and throw it."
throws InvalidAgeExceptionRead:
"This method may throw an InvalidAgeException."
Beginner trap
throw and throws are different.
throw→ actually throws an exception.throws→ declares that a method may throw an exception.
Key takeaway
Exceptions can travel up through method calls until suitable code handles them.
Chapter 16 — Try-with-Resources & Exception Design
Question
Given below is a code snippet that:
Opens a resource.
Uses try-with-resources.
Automatically closes the resource.
Demonstrates
AutoCloseable.Handles an exception safely.
What should be the output of the following code?
class DatabaseConnection implements AutoCloseable {
// Simulate opening a database connection.
DatabaseConnection() {
System.out.println("Connection opened");
}
// Simulate performing database work.
void query() {
System.out.println("Query executed");
}
// Java automatically calls close() when leaving try-with-resources.
@Override
public void close() {
System.out.println("Connection closed");
}
}
public class Main {
public static void main(String[] args) {
try (
// Create a resource that must eventually be closed.
DatabaseConnection connection = new DatabaseConnection()
) {
// Use the resource.
connection.query();
} catch (Exception e) {
// Handle an exception if one occurs.
System.out.println("Something went wrong");
}
}
}Answer
Connection opened
Query executed
Connection closedStep-by-step explanation
DatabaseConnectionimplementsAutoCloseable.Java therefore knows it has a
close()method.The connection is created inside the try-with-resources parentheses.
"Connection opened"is printed.query()executes.When the try block finishes, Java automatically calls
close()."Connection closed"is printed.
How to read the important code
try (DatabaseConnection connection = new DatabaseConnection())Read:
"Open a DatabaseConnection resource and automatically close it when the try block finishes."
Beginner trap
Don't manually close resources that are already managed by try-with-resources unless there is a specific reason.
Key takeaway
Try-with-resources automatically closes resources that implement
AutoCloseable.
Part C — Collections
Chapter 17 — Collections Fundamentals & ArrayList
Question
Given below is a code snippet that:
Creates an
ArrayList.Adds and removes elements.
Reads elements by index.
Checks whether an element exists.
Iterates over the list.
What should be the output of the following code?
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
// Create a mutable list of String values.
List<String> users = new ArrayList<>();
// Add users to the list.
users.add("Alice");
users.add("Bob");
users.add("Charlie");
// Remove Bob from the list.
users.remove("Bob");
// Check whether Alice exists.
System.out.println(users.contains("Alice"));
// Read the first element using index 0.
System.out.println(users.get(0));
// Visit every remaining user.
for (String user : users) {
System.out.println(user);
}
}
}Answer
true
Alice
Alice
CharlieStep-by-step explanation
The list starts empty.
Alice, Bob and Charlie are added.
Bob is removed.
The list now contains Alice and Charlie.
contains("Alice")returnstrue.Index
0contains Alice.The enhanced
forloop visits Alice and then Charlie.
How to read the important code
List<String> users = new ArrayList<>();Read:
"Create a list of strings called users using an ArrayList."
List describes the general type of collection.
ArrayList is the concrete implementation being created.
Beginner trap
Java collection indexes start at 0.
Key takeaway
ArrayListis the common general-purpose choice when you need an ordered, resizable list.
Chapter 18 — Sets: HashSet, LinkedHashSet & TreeSet
Question
Given below is a code snippet that:
Uses a
Set.Demonstrates duplicate removal.
Demonstrates insertion ordering with
LinkedHashSet.Demonstrates sorted ordering with
TreeSet.
What should be the output of the following code?
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;
public class Main {
public static void main(String[] args) {
// LinkedHashSet removes duplicates while preserving insertion order.
Set<String> cities = new LinkedHashSet<>();
// Add city names.
cities.add("Delhi");
cities.add("Mumbai");
cities.add("Delhi");
// Print the set.
System.out.println(cities);
// TreeSet removes duplicates and sorts the values.
Set<Integer> numbers = new TreeSet<>();
// Add numbers in an unsorted order.
numbers.add(50);
numbers.add(10);
numbers.add(30);
// Print the sorted set.
System.out.println(numbers);
}
}Answer
[Delhi, Mumbai]
[10, 30, 50]Step-by-step explanation
A Set does not allow duplicate elements.
Delhi is added twice.
The second Delhi does not create another element.
LinkedHashSetpreserves insertion order.Therefore the first set prints Delhi followed by Mumbai.
TreeSetautomatically sorts its elements.Therefore the numbers appear as
10, 30, 50.
How to read the important code
"Create a set of strings using LinkedHashSet."
Beginner trap
Don't assume every Set preserves insertion order.
HashSet→ no ordering guarantee.LinkedHashSet→ insertion order.TreeSet→ sorted order.
Key takeaway
Use Set when uniqueness matters; choose the implementation according to your ordering needs.
Chapter 19 — Maps: HashMap, LinkedHashMap & TreeMap
Question
Given below is a code snippet that:
Stores key-value pairs.
Retrieves values using keys.
Updates a value.
Checks whether a key exists.
Iterates through a map.
What should be the output of the following code?
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
// Create a map where String keys point to Integer values.
Map<String, Integer> scores = new HashMap<>();
// Add key-value pairs.
scores.put("Alice", 90);
scores.put("Bob", 75);
// Update Alice's existing value.
scores.put("Alice", 95);
// Read Alice's score.
System.out.println(scores.get("Alice"));
// Check whether Bob exists as a key.
System.out.println(scores.containsKey("Bob"));
// Visit every key-value pair.
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}Answer
The first two lines are deterministic:
95
trueThe final two map-entry lines are not guaranteed to have a particular order, because HashMap does not guarantee iteration order.
A possible output is:
95
true
Bob: 75
Alice: 95Step-by-step explanation
The map stores a key and a value together.
"Alice"initially receives90.Putting
"Alice"again replaces the old value with95.get("Alice")therefore returns95.containsKey("Bob")returnstrue.entrySet()gives access to each key-value pair.Because this is a
HashMap, you should not rely on the order in which entries are printed.
How to read the important code
Map<String, Integer> scoresRead:
"A map from String keys to Integer values."
Beginner trap
Never write logic that depends on the iteration order of a normal HashMap.
Key takeaway
A
Mapassociates keys with values;HashMapis optimized for general-purpose key-value lookup without an ordering guarantee.
Chapter 20 — Queues, Deques & Priority Queues
Question
Given below is a code snippet that:
Uses a queue.
Demonstrates FIFO behavior.
Uses
Deque.Uses a
PriorityQueue.Shows different ways elements can be retrieved.
What should be the output of the following code?
import java.util.ArrayDeque;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Deque;
public class Main {
public static void main(String[] args) {
// Create a normal queue.
Queue<String> queue = new ArrayDeque<>();
// Add elements in order.
queue.offer("Alice");
queue.offer("Bob");
queue.offer("Charlie");
// Remove the element that entered first.
System.out.println(queue.poll());
// Create a deque that can work from both ends.
Deque<String> deque = new ArrayDeque<>();
// Add elements to both ends.
deque.addFirst("Middle");
deque.addFirst("First");
deque.addLast("Last");
// Remove from the front.
System.out.println(deque.pollFirst());
// Create a priority queue.
Queue<Integer> priorities = new PriorityQueue<>();
// Add numbers in arbitrary order.
priorities.offer(30);
priorities.offer(10);
priorities.offer(20);
// The smallest value has the highest priority by natural ordering.
System.out.println(priorities.poll());
}
}Answer
Alice
First
10Step-by-step explanation
A normal queue generally follows FIFO: first in, first out.
Alice enters first, so
poll()removes Alice.A
Dequeallows insertion/removal from both ends.addFirst("First")places First at the front.pollFirst()therefore returns First.A
PriorityQueuedoes not simply behave like FIFO.With natural integer ordering, the smallest value has the highest priority.
Therefore
10is removed first.
How to read the important code
queue.offer("Alice");Read:
"Add Alice to the queue."
queue.poll();Read:
"Remove and return the front element."
Beginner trap
A PriorityQueue is not sorted like a normal list when you iterate over it. Its important guarantee concerns which element is returned by operations such as poll().
Key takeaway
Queue = process in order; Deque = both ends; PriorityQueue = process according to priority.
Chapter 21 — Iterators, Sorting & Comparators
Question
Given below is a code snippet that:
Uses an
Iterator.Removes an element safely during iteration.
Sorts objects.
Uses a
Comparator.Demonstrates custom sorting logic.
What should be the output of the following code?
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
class Product {
// Store product information.
String name;
int price;
// Initialize the product.
Product(String name, int price) {
this.name = name;
this.price = price;
}
// Provide a readable representation.
@Override
public String toString() {
return name + ":" + price;
}
}
public class Main {
public static void main(String[] args) {
// Create a list of products.
List<Product> products = new ArrayList<>();
// Add products.
products.add(new Product("Mouse", 500));
products.add(new Product("Laptop", 70000));
products.add(new Product("Keyboard", 1500));
// Create an iterator for the list.
Iterator<Product> iterator = products.iterator();
// Visit each product.
while (iterator.hasNext()) {
// Get the next product.
Product product = iterator.next();
// Remove the Mouse through the iterator.
if (product.name.equals("Mouse")) {
iterator.remove();
}
}
// Sort products from cheapest to most expensive.
products.sort(Comparator.comparingInt(product -> product.price));
// Print the sorted products.
System.out.println(products);
}
}Answer
[Keyboard:1500, Laptop:70000]Step-by-step explanation
Three products are added.
The iterator visits each product.
When it reaches Mouse,
iterator.remove()removes it safely.The remaining products are Laptop and Keyboard.
Comparator.comparingInt(...)tells Java to compare products using their prices.Keyboard costs
1500.Laptop costs
70000.Therefore Keyboard comes first.
How to read the important code
products.sort(Comparator.comparingInt(product -> product.price));Read:
"Sort the products by their price."
Beginner trap
Don't casually remove elements from a collection inside an enhanced for loop. For situations where removal during iteration is required, an Iterator provides the appropriate mechanism.
Key takeaway
Iterators control traversal, while comparators define how objects should be ordered.
Part D — Generics
Chapter 22 — Generic Classes & Methods
Question
Given below is a code snippet that:
Defines a generic class.
Creates objects with different types.
Defines a generic method.
Lets Java enforce type safety at compile time.
What should be the output of the following code?
// Define a generic container whose type is decided by the user.
class Box<T> {
// Store a value of the generic type.
private T value;
// Store a value in the box.
void set(T value) {
this.value = value;
}
// Return the stored value.
T get() {
return value;
}
}
public class Main {
// Define a generic method that can work with any type.
static <T> void printValue(T value) {
System.out.println(value);
}
public static void main(String[] args) {
// Create a Box that stores Strings.
Box<String> nameBox = new Box<>();
// Store a String.
nameBox.set("Alice");
// Create a Box that stores Integers.
Box<Integer> ageBox = new Box<>();
// Store an Integer.
ageBox.set(25);
// Print values from both boxes.
printValue(nameBox.get());
printValue(ageBox.get());
}
}Answer
Alice
25Step-by-step explanation
Tis a type parameter.Box<T>means the class can work with different types.Box<String>creates a box specifically for Strings.Box<Integer>creates a box specifically for Integers.nameBox.get()therefore returns a String.ageBox.get()returns an Integer.printValue()is also generic and can accept both types.
How to read the important code
Box<String> nameBoxRead:
"A Box of Strings called nameBox."
Box<Integer> ageBoxRead:
"A Box of Integers called ageBox."
Beginner trap
T isn't a variable containing a value.
It represents a type.
Key takeaway
Generics allow classes and methods to work with different types while maintaining compile-time type safety.
Chapter 23 — Bounded Types & Wildcards
Question
Given below is a code snippet that:
Uses a bounded generic type.
Uses
? extends.Accepts lists of different numeric types.
Demonstrates that wildcard types control what can safely be read.
What should be the output of the following code?
import java.util.Arrays;
import java.util.List;
public class Main {
// T must be a Number or a subclass of Number.
static <T extends Number> double doubleValue(T value) {
return value.doubleValue();
}
// '? extends Number' means the list contains some unknown Number subtype.
static double sum(List<? extends Number> numbers) {
// Start the total at zero.
double total = 0;
// Safely read each value as a Number.
for (Number number : numbers) {
total += number.doubleValue();
}
// Return the total.
return total;
}
public static void main(String[] args) {
// Create a list of Integers.
List<Integer> integers = Arrays.asList(10, 20, 30);
// Create a list of Doubles.
List<Double> doubles = Arrays.asList(1.5, 2.5);
// Use the bounded generic method.
System.out.println(doubleValue(10));
// Sum the integer list.
System.out.println(sum(integers));
// Sum the double list.
System.out.println(sum(doubles));
}
}Answer
10.0
60.0
4.0Step-by-step explanation
Numberis a parent type of types such asIntegerandDouble.<T extends Number>says thatTmust beNumberor a subtype.Therefore an Integer can be passed.
? extends Numbermeans the method can receive a list containing some unknown subtype of Number.That could be
List<Integer>.It could also be
List<Double>.Java safely lets us read the elements as
Number.The integer list sums to
60.The double list sums to
4.0.
How to read the important code
List<? extends Number>Read:
"A list of some type that extends Number."
Beginner trap
? extends Number does not mean "a list where I can freely add any Number."
It is primarily useful here for safely reading values as Number.
Key takeaway
Bounded generics restrict allowed types and let generic code work safely with related types.
Chapter 24 — extends, super, PECS & Type Erasure
Question
Given below is a code snippet that:
Uses
? extendsfor reading.Uses
? superfor adding.Demonstrates the PECS rule.
Shows why generic types provide compile-time safety.
What should be the output of the following code?
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main {
// Producer: this list provides Number values to us.
static double total(List<? extends Number> numbers) {
// Start the running total.
double sum = 0;
// Read values safely as Number.
for (Number number : numbers) {
sum += number.doubleValue();
}
// Return the total.
return sum;
}
// Consumer: this list accepts Integer values from us.
static void addScores(List<? super Integer> scores) {
// Add Integer values safely.
scores.add(100);
scores.add(200);
}
public static void main(String[] args) {
// Create a list of Integers.
List<Integer> numbers = Arrays.asList(10, 20, 30);
// Read values through an extends wildcard.
System.out.println(total(numbers));
// Create a list that can accept Integers.
List<Number> scores = new ArrayList<>();
// Add Integer values through a super wildcard.
addScores(scores);
// Print the resulting list.
System.out.println(scores);
}
}Answer
60.0
[100, 200]Step-by-step explanation
List<? extends Number>is used when the collection is a producer of values.The
total()method only needs to read numbers.It can therefore safely read each element as
Number.List<? super Integer>is used when the collection is a consumer of Integer values.addScores()can safely add Integers.The
scoreslist is actually aList<Number>.An Integer is a valid Number, so adding the Integers is safe.
The final list contains
100and200.
This leads to the famous rule:
PECS = Producer Extends, Consumer Super.
How to read the important code
List<? super Integer>Read:
"A list of some type that is Integer or a superclass of Integer."
Beginner trap
Generic inheritance doesn't work like normal class inheritance.
For example:
List<Integer>is not a subtype of:
List<Number>even though Integer is a subtype of Number.
Key takeaway
Use
extendswhen you mainly read from a generic source, andsuperwhen you mainly put values into a generic destination.
Phase 2 Complete — What You Should Now Understand
You have now covered the major OOP + Core Java foundations that we need before moving into modern/advanced Java.
Object-Oriented Java
You should now understand:
Classes
Objects
Fields
Methods
Constructors
Constructor overloading
thisEncapsulation
Access modifiers
privatepublicstaticfinalInstance vs class members
Inheritance
extendssuperMethod overriding
Polymorphism
Abstract classes
Abstract methods
Interfaces
implementsComposition
Enums
Records
Object identity
equals()hashCode()toString()Nested/inner classes
Exception Handling
Exceptions
trycatchException objects
Exception propagation
throwthrowsCustom exceptions
finallyconceptsTry-with-resources
AutoCloseableResource management
Collections
ListArrayListSetHashSetLinkedHashSetTreeSetMapHashMapLinkedHashMapTreeMapQueueDequePriorityQueueIteratorSorting
Comparator
Generics
Generic classes
Generic methods
Type parameters
Bounded types
Wildcards
? extends? superPECS
Generic type safety
Generic inheritance rules
The most important mental model from Phase 2
You should now start reading Java code like this:
List<User> users = new ArrayList<>();"Create a list of User objects called users using an ArrayList."
PaymentMethod payment = new Card();"Create a Card object and treat it as a PaymentMethod."
User user = new User("Alice", 25);"Create a User object initialized with Alice and 25."
List<? extends Number> numbers"A list containing some unknown Number subtype, which I can safely read as Number."
That style of thinking and reading code is more important than memorizing individual syntax rules.
Phase 3 can now build directly on this foundation: Modern & Advanced Core Java — functional interfaces, lambdas, Optional, Streams, Collectors, advanced collections, I/O/NIO, Date-Time, regex, annotations, reflection, modules, JVM memory and garbage collection.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.