Java
This is a reference page for Java, designed to be kept open alongside the Java Tutorial. Use it to look up syntax, concepts, and comparisons while you work through the hands-on exercises.
New to Java? Start with the interactive tutorial first — it teaches these concepts through practice with immediate feedback. This page is a reference, not a teaching resource.
Basics
Entry Point and Syntax
Java forces everything into a class. There are no free functions. The entry point is a static method called main — the JVM looks for it by name:
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}
Every word in the signature has a purpose:
| Keyword | Why |
|---|---|
public |
The JVM must be able to call it from outside the class |
static |
No instance of the class is created before main runs |
void |
Returns nothing; use System.exit() for exit codes |
String[] args |
Command-line arguments, like C++’s argv |
Quick mapping from Python and C++:
| Feature | Python | C++ | Java |
|---|---|---|---|
| Entry point | if __name__ == "__main__": |
int main() (free function) |
public static void main(String[] args) (class method) |
| Typing | Dynamic (x = 42) |
Static (int x = 42;) |
Static (int x = 42;) |
| Memory | GC + reference counting | Manual (new/delete) or RAII |
GC (generational) |
| Free functions | Yes | Yes | No — everything lives in a class |
| Multiple inheritance | Yes (MRO) | Yes | No — single class inheritance + interfaces |
// Variables — declare type like C++
int count = 10;
double pi = 3.14159;
String name = "Alice"; // String is a class, not a primitive
boolean done = false; // not 'bool' (C++) or True/False (Python)
// Printing
System.out.println("Count: " + count);
// Arrays — fixed size, .length is a field (no parentheses)
int[] scores = {90, 85, 92};
System.out.println(scores.length); // 3 — NOT .length() or len()
// Enhanced for — like Python's "for x in list"
for (int s : scores) {
System.out.println(s);
}
Size inconsistency: Arrays use
.length(field). Strings use.length()(method). Collections use.size()(method). This is a well-known Java wart.
The Dual Type System: Primitives and Wrappers
Java has 8 primitive types that live on the stack (like C++ value types), and corresponding wrapper classes that live on the heap:
| Primitive | Size | Default | Wrapper |
|---|---|---|---|
byte |
8-bit | 0 | Byte |
short |
16-bit | 0 | Short |
int |
32-bit | 0 | Integer |
long |
64-bit | 0L | Long |
float |
32-bit | 0.0f | Float |
double |
64-bit | 0.0 | Double |
char |
16-bit | '\u0000' |
Character |
boolean |
1-bit | false | Boolean |
Why wrappers exist: Java generics only work with objects, not primitives. You cannot write ArrayList<int> — you must write ArrayList<Integer>.
Autoboxing is the automatic conversion between primitive and wrapper:
ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(42); // autoboxing: int → Integer
int first = numbers.get(0); // unboxing: Integer → int
Autoboxing Traps
Trap 1 — Null unboxing causes NullPointerException:
Integer count = null;
int n = count; // NullPointerException! Can't unbox null.
Trap 2 — Boxing in loops is slow:
// BAD — creates a new Integer object on every iteration
Integer sum = 0;
for (int i = 0; i < 1_000_000; i++) {
sum += i; // unbox sum, add i, box result — every iteration!
}
// GOOD — use primitive type for accumulation
int sum = 0;
for (int i = 0; i < 1_000_000; i++) {
sum += i; // pure arithmetic, no boxing
}
The Identity Trap: == vs .equals()
⚠ False Friend: In Python,
==compares values. In Java,==on objects compares identity (are these the exact same object in memory?), not value equality.
String c = new String("hello");
String d = new String("hello");
System.out.println(c == d); // false — different objects in memory
System.out.println(c.equals(d)); // true — same characters
String literals appear to work with == because Java interns them into a shared pool:
String a = "hello";
String b = "hello";
System.out.println(a == b); // true — but only because both point to the interned literal!
Do not rely on this. Always use .equals() for string comparison.
The Integer cache trap: Java caches Integer objects for values −128 to 127, making == accidentally work for small numbers:
Integer x = 127;
Integer y = 127;
System.out.println(x == y); // true (cached — same object)
Integer p = 128;
Integer q = 128;
System.out.println(p == q); // false (not cached — different objects)
System.out.println(p.equals(q)); // true (always use .equals())
The golden rule:
- Use
==for primitives (int,double,boolean,char) - Use
.equals()for everything else (objects, strings, wrapper types)
Object-Oriented Programming
Classes and Encapsulation
A Java class bundles private fields with public methods that control access. Unlike Python (where self.balance is always accessible) and C++ (where you control access at the class level), Java enforces encapsulation at compile time.
public class BankAccount {
private String owner; // private — only accessible within this class
private double balance;
public BankAccount(String owner, double initialBalance) {
this.owner = owner; // 'this' disambiguates field from parameter
this.balance = initialBalance;
}
public void deposit(double amount) {
if (amount > 0) { // validation — callers can't bypass this
balance += amount;
}
}
public boolean withdraw(double amount) {
if (amount > 0 && balance >= amount) {
balance -= amount;
return true;
}
return false; // returns false instead of allowing overdraft
}
public double getBalance() { return balance; }
public String getOwner() { return owner; }
// Called automatically by System.out.println(account) — like Python's __str__
public String toString() {
return "BankAccount[owner=" + owner + ", balance=" + balance + "]";
}
}
Access Modifiers
Java has four access levels. The default (no keyword) is different from C++:
| Modifier | Class | Package | Subclass | World |
|---|---|---|---|---|
private |
✓ | ✗ | ✗ | ✗ |
| (none) = package-private | ✓ | ✓ | ✗ | ✗ |
protected |
✓ | ✓ | ✓ | ✗ |
public |
✓ | ✓ | ✓ | ✓ |
⚠ False Friend from C++: In C++, the default access in a
classisprivate. In Java, the default is package-private — accessible to any class in the same package. Always be explicit.
In UML class diagrams: - means private, + means public, # means protected, ~ means package-private.
Information Hiding
Encapsulation (using private fields) is a mechanism. Information hiding is a design principle.
A module hides its secrets — design decisions that are likely to change. When a secret is properly hidden, changing that decision modifies exactly one class. When a secret leaks, a single change cascades across many classes.
| Secret to Hide | Example | Why |
|---|---|---|
| Data representation | int[] vs ArrayList vs database |
Storage format may change |
| Algorithm | Bubble sort vs quicksort | Optimization may change |
| Business rules | Grading thresholds, capacity limits | Policy may change |
| Output format | CSV vs JSON vs text | Reporting needs may change |
| External dependency | Which API or library to call | Vendor may change |
The Getter/Setter Fallacy
Fields can be private and yet still leak design decisions:
// Fully encapsulated — but leaking the "ISBN is an int" decision
class Book {
private int isbn;
public int getIsbn() { return isbn; }
public void setIsbn(int isbn) { this.isbn = isbn; }
}
When the spec changes to support international ISBNs with hyphens (String), every caller of getIsbn() breaks. The module is encapsulated but hides nothing.
Better design — expose behavior, not data:
// Hides the representation; callers depend on behavior only
class GradeReport {
private ArrayList<Integer> scores; // hidden
public String getLetterGrade(int score) { ... } // hides the grading policy
public double getAverage() { ... } // hides the data representation
public String formatReport() { ... } // hides the output format
}
Test for information hiding: For each design decision, ask: “If this changes, how many classes must I edit?” If the answer is more than one, the secret has leaked.
Interfaces: Design by Contract
An interface defines what a class can do, without specifying how. Java’s philosophy:
Program to an interface, not an implementation.
// Defining an interface — method signatures only
public interface Shape {
double getArea();
double getPerimeter();
String describe();
}
// Implementing an interface — must provide ALL methods
public class Circle implements Shape {
private double radius;
public Circle(double radius) { this.radius = radius; }
public double getArea() { return Math.PI * radius * radius; }
public double getPerimeter() { return 2 * Math.PI * radius; }
public String describe() { return "Circle(r=" + radius + ")"; }
}
Declare variables as the interface type so you can swap implementations without changing calling code:
Shape s = new Circle(5.0); // interface type on the left
Shape r = new Rectangle(3, 4);
// s and r can be used interchangeably anywhere Shape is expected
Compared to C++ and Python:
| Aspect | C++ | Python | Java |
|---|---|---|---|
| Mechanism | Pure virtual functions / abstract class | Duck typing (no enforcement) | interface keyword, compiler-enforced |
| Multiple inheritance | Yes (virtual base classes) |
Yes (MRO) | A class can implement multiple interfaces |
| Default methods | No | No | Java 8+: default methods can have implementations |
Inheritance and Polymorphism
Java supports single class inheritance with abstract classes for sharing both state and behavior:
// Abstract class — cannot be instantiated, may have concrete fields and methods
public abstract class Vehicle {
private String make;
private int year;
public Vehicle(String make, int year) { // abstract classes have constructors
this.make = make;
this.year = year;
}
public String getMake() { return make; }
public int getYear() { return year; }
// Subclasses MUST implement these
public abstract String describe();
public abstract String startEngine();
}
public class Car extends Vehicle {
private int numDoors;
public Car(String make, int year, int numDoors) {
super(make, year); // MUST call parent constructor first — like C++ initializer lists
this.numDoors = numDoors;
}
@Override // optional but recommended — compiler verifies you're actually overriding
public String describe() {
return getYear() + " " + getMake() + " Car (" + numDoors + " doors)";
}
@Override
public String startEngine() { return "Vroom!"; }
}
Polymorphism — a parent reference can point to any subclass:
Vehicle[] fleet = {
new Car("Toyota", 2024, 4),
new Motorcycle("Harley", 2023, true),
};
for (Vehicle v : fleet) {
System.out.println(v.describe()); // calls Car.describe() or Motorcycle.describe()
// based on the actual runtime type — dynamic dispatch
}
Key differences from C++:
- Java methods are virtual by default — no
virtualkeyword needed @Overrideannotation is optional but the compiler validates it catches typossuper(args)must be the first statement in a constructor (C++ uses initializer lists)
When to use interface vs abstract class:
| Aspect | Interface | Abstract Class |
|---|---|---|
| Methods | Abstract (+ default in Java 8+) |
Abstract AND concrete |
| Fields | Only static final constants |
Instance fields allowed |
| Constructor | No | Yes |
| Inheritance | implements (multiple OK) |
extends (single only) |
| Use when… | Unrelated classes share behavior | Related classes share state + behavior |
Generics
Generics: Not C++ Templates
Java generics look like C++ templates but work completely differently:
| Feature | C++ Templates | Java Generics |
|---|---|---|
| Mechanism | Code generation (monomorphization) | Type erasure (single shared implementation) |
| Runtime type info | Yes — vector<int> ≠ vector<string> |
No — List<String> = List<Integer> at runtime |
| Primitive types | Yes — vector<int> works |
No — must use List<Integer> |
new T() |
Yes | No — type is unknown at runtime |
// A generic class — T is a type parameter
public class Box<T> {
private T item;
public Box(T item) { this.item = item; }
public T getItem() { return item; }
}
// The compiler ensures type safety — no casts needed
Box<String> nameBox = new Box<>("Alice");
String name = nameBox.getItem(); // compiler knows it's String
Box<Integer> numBox = new Box<>(42);
int num = numBox.getItem(); // unboxing Integer → int
Generic methods declare their own type parameters:
// <X, Y> before the return type — method's own type parameters
public static <X, Y> Pair<Y, X> swap(Pair<X, Y> pair) {
return new Pair<>(pair.getSecond(), pair.getFirst());
}
Bounded type parameters — restrict what types are allowed:
// T must implement Comparable<T> — like C++20 concepts
public static <T extends Comparable<T>> T findMax(T a, T b) {
return a.compareTo(b) >= 0 ? a : b;
}
Type Erasure
When Java 5 added generics (2004), billions of lines of pre-generics code already existed. To maintain binary compatibility, generic types are erased after compilation:
// What you write:
List<String> names = new ArrayList<>();
String first = names.get(0);
// What the compiler generates (roughly):
List names = new ArrayList();
String first = (String) names.get(0); // cast inserted by compiler
Consequences:
ArrayList<int>is illegal — useArrayList<Integer>insteadnew T()is illegal — type is unknown at runtimeif (list instanceof List<String>)is illegal — generic type is erased
Collections Framework
Choosing the Right Collection
Java Collections are organized by interfaces. Declare variables as the interface type:
@startuml
interface Collection
interface List
interface Set
interface Map
class ArrayList <>
class LinkedList <>
class HashSet <<unordered, fast>>
class TreeSet <>
class HashMap <<unordered, fast>>
class TreeMap <>
List --|> Collection
Set --|> Collection
ArrayList ..|> List
LinkedList ..|> List
HashSet ..|> Set
TreeSet ..|> Set
HashMap ..|> Map
TreeMap ..|> Map
@enduml
</code></pre>
| Need | Interface | Implementation | Python Equivalent |
|------|-----------|---------------|-------------------|
| Ordered sequence, index access | `List` | `ArrayList` | `list` |
| Unique elements, fast lookup | `Set` | `HashSet` | `set` |
| Key-value pairs | `Map<K,V>` | `HashMap<K,V>` | `dict` |
| Sorted unique elements | `Set` | `TreeSet` | `sorted(set(...))` |
| Sorted key-value pairs | `Map<K,V>` | `TreeMap<K,V>` | sorted `dict` |
**C++ mapping:** `vector` → `ArrayList`, `unordered_map` → `HashMap`, `map` → `TreeMap`, `unordered_set` → `HashSet`.
## Common Operations
```java
// List — like Python list or C++ vector
List names = new ArrayList<>();
names.add("Alice"); // append
names.add(0, "Bob"); // insert at index
String first = names.get(0); // index access
names.size(); // NOT .length — that's arrays!
// Map — like Python dict or C++ unordered_map
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 95); // insert or update
scores.get("Alice"); // lookup — returns null if missing!
scores.containsKey("Alice"); // check existence — always do this before get()
scores.getOrDefault("Bob", 0); // safe lookup with a default
// Set — like Python set or C++ unordered_set
Set unique = new HashSet<>();
unique.add("Alice");
unique.add("Alice"); // silently ignored — already present
unique.contains("Alice"); // true
unique.size(); // 1
// Iterating a Map
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
```
> **⚠ NullPointerException trap:** `HashMap.get(key)` returns `null` for missing keys. If you assign the result directly to a primitive (`int val = map.get("missing")`), auto-unboxing `null` throws `NullPointerException`. Always use `containsKey()` first, or `getOrDefault()`.
**Declare as the interface type** — this lets you swap implementations without changing callers:
```java
// ✓ Interface type — can swap to TreeMap later with no other changes
Map<String, Integer> scores = new HashMap<>();
// ✗ Concrete type — callers break if you switch to TreeMap
HashMap<String, Integer> scores = new HashMap<>();
```
# Exception Handling
## Checked vs Unchecked Exceptions
Java is unique in dividing exceptions into two categories:
| Category | Extends | Compiler enforcement | Use for |
|----------|---------|---------------------|---------|
| **Checked** | `Exception` (but not `RuntimeException`) | Must catch or declare `throws` | Recoverable external failures (file not found, network error) |
| **Unchecked** | `RuntimeException` | No enforcement | Programming errors (null pointer, bad index, bad argument) |
| **Error** | `Error` | No enforcement | JVM failures — never catch these |
```java
// Checked: compiler forces handling
public String readFile(String path) throws IOException {
// ... might throw IOException
}
// Callers MUST handle it — the compiler won't let them ignore it
try {
String content = readFile("data.txt");
} catch (IOException e) {
System.err.println("File error: " + e.getMessage());
}
// Unchecked: no compiler enforcement (like Python/C++)
public int divide(int a, int b) {
return a / b; // might throw ArithmeticException — compiler doesn't require handling
}
```
**Compared to Python and C++:**
| Aspect | Python | C++ | Java |
|---|---|---|---|
| Philosophy | EAFP — catch freely | Exceptions are expensive; prefer error codes | Checked exceptions = compiler-enforced contract |
| Enforcement | None — errors discovered at runtime | `noexcept` exists but rarely enforced | Compiler rejects unhandled checked exceptions |
## Custom Exceptions
```java
// Checked custom exception — extends Exception
public class InsufficientFundsException extends Exception {
private double deficit;
public InsufficientFundsException(double deficit) {
super("Insufficient funds: need " + deficit + " more"); // like Python's super().__init__
this.deficit = deficit;
}
public double getDeficit() { return deficit; }
}
// Usage
public boolean withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) {
throw new InsufficientFundsException(amount - balance);
}
balance -= amount;
return true;
}
```
**Multiple catch blocks** — catch specific exceptions before general ones:
```java
try {
String content = readFile("data.txt");
int value = Integer.parseInt(content.trim());
} catch (FileNotFoundException e) {
System.err.println("File missing: " + e.getMessage());
} catch (IOException e) {
System.err.println("Read error: " + e.getMessage());
} catch (NumberFormatException e) {
System.err.println("Not a number: " + e.getMessage());
} finally {
// runs whether or not an exception was thrown — use for cleanup
closeResources();
}
```
# Design Principles
## Top 10 Java Best Practices
### 1. Always use `.equals()` for object comparison, never `==`
```java
// ✓ Always correct
if (name.equals("Alice")) { ... }
if (a.equals(b)) { ... }
// ✗ Compares identity — will fail with new String("Alice")
if (name == "Alice") { ... }
```
The same applies to all wrapper types (`Integer`, `Double`, etc.) and any object.
### 2. Make fields `private`; validate in setters and constructors
```java
// ✓ Encapsulation with validation — callers can't bypass the contract
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
// ✗ Public fields let callers bypass all validation
public double balance;
```
### 3. Use primitives for accumulation, wrappers only when required
```java
// ✓ Primitive — no boxing overhead
int sum = 0;
for (int score : scores) { sum += score; }
// ✗ Boxing every iteration — slower and allocates garbage
Integer sum = 0;
for (int score : scores) { sum += score; } // boxes sum on every iteration
```
Use wrapper types only when required: generics (`List`), nullable values, or calling methods (`.compareTo()`).
### 4. Declare variables as interface types, not concrete classes
```java
// ✓ Interface type — easy to swap implementation
List names = new ArrayList<>();
Map<String, Integer> scores = new HashMap<>();
// ✗ Concrete type — caller breaks if you switch to LinkedList or TreeMap
ArrayList names = new ArrayList<>();
```
### 5. Program to the interface, not the implementation
Design method parameters and return types as interfaces. This enables polymorphism and makes code easier to test:
```java
// ✓ Accepts any List — works with ArrayList, LinkedList, etc.
public double average(List scores) { ... }
// ✗ Unnecessarily restricts callers to ArrayList
public double average(ArrayList scores) { ... }
```
### 6. Use `@Override` when overriding methods
`@Override` is optional, but it tells the compiler to verify that you're actually overriding a parent method. Without it, a typo in the method name silently creates a new method instead of overriding:
```java
@Override
public String toString() { ... } // compiler error if toString is misspelled
```
### 7. Handle checked exceptions at the right level
Don't catch exceptions before you can actually handle them. If a method can't recover from a failure, let it propagate:
```java
// ✓ Handle it where you can do something useful
try {
loadConfig("config.txt");
} catch (IOException e) {
loadDefaults(); // meaningful recovery
}
// ✗ Swallowing exceptions hides bugs — never do this
try {
loadConfig("config.txt");
} catch (IOException e) {
// empty — the problem disappears silently
}
```
### 8. Use `getOrDefault()` instead of null checks on Maps
```java
// ✓ Safe and concise
int count = scores.getOrDefault("Alice", 0);
// ✗ Verbose null check
int count = 0;
if (scores.containsKey("Alice")) {
count = scores.get("Alice");
}
```
### 9. Hide design decisions behind stable interfaces (Parnas 1972)
Each class should hide a **secret** — a design decision likely to change. When something changes, exactly one class changes:
```java
// ✓ Secret (grading policy) is hidden — change thresholds by editing one method
public String getLetterGrade(int score) {
if (score >= 90) return "A";
if (score >= 80) return "B";
...
}
// ✗ Grading policy leaks into calling code — changes require editing many places
if (score >= 90) letter = "A"; // in main, not in GradeReport
```
### 10. Choose the right collection for the job
| If you need… | Use |
|---|---|
| Ordered sequence with index access | `ArrayList` |
| Fast membership testing | `HashSet` |
| Key-to-value mapping with fast lookup | `HashMap<K,V>` |
| Sorted elements | `TreeSet` or `TreeMap<K,V>` |
| Deduplication | `HashSet` — add freely, duplicates are ignored |
Java — What Does This Code Do?
You are shown Java code. Go beyond naming what it does — explain *why* it behaves that way, what design choice it reflects, or what would break if it changed.
Difficulty:
Intermediate
String a = new String("hello");
String b = new String("hello");
System.out.println(a == b); // Line A
System.out.println(a.equals(b)); // Line B
Predict each output. Then explain why Line A and Line B differ — what does each operator actually check?
Difficulty:
Intermediate
Integer x = 127;
Integer y = 127;
System.out.println(x == y); // true
Integer p = 128;
Integer q = 128;
System.out.println(p == q); // false
The only change is 127 → 128. What mechanism in the JVM causes this flip, and why is this dangerous in production code?
Difficulty:
Intermediate
Integer count = null;
int n = count; // what happens here?
Describe exactly what the JVM does on the second line and what error results.
Difficulty:
Advanced
// Version A
Integer sum = 0;
for (int i = 0; i < 1_000_000; i++) {
sum += i;
}
// Version B
int sum = 0;
for (int i = 0; i < 1_000_000; i++) {
sum += i;
}
Both produce the same final value. Analyze what the JVM does differently in Version A on every iteration. Which version should you use?
Difficulty:
Basic
public class BankAccount {
public String owner;
public double balance;
...
}
The fields are public. Explain what specific harm this causes compared to making them private with a withdraw() method that validates before mutating.
Difficulty:
Advanced
class GradeReport {
private ArrayList<Integer> scores;
public ArrayList<Integer> getScores() { return scores; }
}
The field is private. A colleague says “information hiding is achieved.” Are they right? What would break if you later switch scores to int[]?
Difficulty:
Intermediate
public interface Shape {
double getArea();
double getPerimeter();
}
public class Circle implements Shape {
private double radius;
public Circle(double r) { this.radius = r; }
@Override
public double getArea() { return Math.PI * radius * radius; }
@Override
public double getPerimeter() { return 2 * Math.PI * radius; }
}
Shape s = new Circle(5.0);
System.out.println(s.getArea());
Explain what @Override buys you here. Give an example of the specific bug it prevents.
Difficulty:
Advanced
abstract class Vehicle {
private String make;
public Vehicle(String make) { this.make = make; }
public String getMake() { return make; }
public abstract String describe();
}
class Car extends Vehicle {
public Car(String make) {
super(make); // ← this line
}
@Override
public String describe() { return getMake() + " Car"; }
}
Why must super(make) be the first statement in Car’s constructor? What would happen if it were moved after getMake()?
Difficulty:
Intermediate
Vehicle[] fleet = {
new Car("Toyota", 2024),
new Motorcycle("Harley", 2023),
};
for (Vehicle v : fleet) {
System.out.println(v.describe());
}
The reference type is Vehicle, but describe() is abstract. Describe precisely what happens at compile time and at runtime when v.describe() is called.
Difficulty:
Expert
public class Pair<A, B> {
private A first;
private B second;
public static <X, Y> Pair<Y, X> swap(Pair<X, Y> p) {
return new Pair<>(p.getSecond(), p.getFirst());
}
}
Why does swap declare its own type parameters <X, Y> instead of reusing the class’s <A, B>?
Difficulty:
Intermediate
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 95);
int grade = scores.get("Bob"); // Bob not in map
This compiles without warnings. Predict what happens at runtime and explain the chain of events.
Difficulty:
Intermediate
public class SafeCalculator {
public double divide(int a, int b) throws CalculatorException {
if (b == 0) throw new CalculatorException("Division by zero");
return (double) a / b;
}
}
class CalculatorException extends Exception {
public CalculatorException(String msg) { super(msg); }
}
CalculatorException extends Exception, not RuntimeException. What concrete difference does this choice produce for callers of divide()?
Difficulty:
Advanced
// Version A
public double average(ArrayList<Integer> scores) { ... }
// Version B
public double average(List<Integer> scores) { ... }
Both compile. Analyze the practical difference when other code calls average().
Difficulty:
Intermediate
Set<String> submitted = new HashSet<>();
List<String> roster = new ArrayList<>();
submitted.add("Alice");
submitted.add("Alice"); // duplicate
roster.add("Alice");
roster.add("Alice"); // duplicate
System.out.println(submitted.size()); // ?
System.out.println(roster.size()); // ?
Predict each output and explain what design principle drives the difference between HashSet and ArrayList.
Difficulty:
Advanced
public class Course implements Enrollable {
private ArrayList<Student> students = new ArrayList<>();
public boolean isEnrolled(String name) {
for (Student s : students) {
if (s.getName().equals(name)) return true;
}
return false;
}
}
This works correctly. Evaluate it for performance and explain what would change if you swapped ArrayList<Student> for HashMap<String, Student>.
Java — Write the Code
You are given a scenario or design problem. Write Java code that solves it. Questions target Apply, Evaluate, and Create levels — not just syntax recall.
Difficulty:
Basic
[Apply] Two String variables input and stored may or may not point to the same object. Write a boolean expression that checks whether they contain the same characters, guaranteed to be correct regardless of how they were created.
Difficulty:
Intermediate
[Evaluate + Apply] A HashMap lookup is crashing in production with a NullPointerException. The code is:
Map<String, Integer> grades = loadFromDB();
int g = grades.get(studentId);
Fix it in one line, defaulting to 0 for missing students.
Difficulty:
Intermediate
[Create] Design a BankAccount class that:
- Stores
owner (String) and balance (double) — neither directly accessible from outside
- Provides a constructor,
getOwner(), getBalance()
deposit(double amount) — only accepts positive amounts
withdraw(double amount) — returns false if insufficient funds; true on success
toString() returns "BankAccount[owner=Alice, balance=100.0]"
Difficulty:
Advanced
[Evaluate + Create] This class has a design problem. Identify it, then rewrite GradeReport so that changing the grading thresholds (A ≥ 90, B ≥ 80…) requires editing only one method:
class GradeReport {
private List<Integer> scores;
public List<Integer> getScores() { return scores; }
}
// In main:
for (int s : report.getScores()) {
if (s >= 90) System.out.println("A");
else if (s >= 80) System.out.println("B");
}
Difficulty:
Basic
[Apply] Define a Drawable interface with one method: String draw(). Then write a Square class that implements it — draw() returns "Square(side=5.0)".
Difficulty:
Intermediate
[Create] Design an abstract class Animal with:
- A
private String name and a constructor
- A concrete
getName() getter
- An abstract method
makeSound() that returns a String
Then write a Dog subclass that calls the parent constructor and returns "Woof!" from makeSound().
Difficulty:
Basic
[Apply] Write a generic class Box<T> that holds one item of any type. Include a constructor, a getItem() method, and a setItem() method.
Difficulty:
Expert
[Apply + Analyze] Write a generic static method findMax that takes two arguments of any type and returns the larger one. The type must be constrained to types that can be compared.
Difficulty:
Advanced
[Create] Write a WordCounter class that takes a String[] in its constructor and provides:
int getCount(String word) — returns 0 for unknown words, no NPE
int getUniqueCount() — number of distinct words
Use the most appropriate collection for each responsibility.
Difficulty:
Advanced
[Apply] Define a checked exception EnrollmentException and a Course.enroll(Student s) method that throws it when the course is full (capacity exceeded). Write both the class definition and the calling code that handles the exception.
Difficulty:
Intermediate
[Apply] Write a try-catch-finally block that: opens a file (throws IOException), reads its content, and prints an error if it fails. The finally block should always print "Done.".
Difficulty:
Advanced
[Evaluate + Apply] You need to store course enrollments. Two options:
List<Student> with a manual duplicate check in enroll()
LinkedHashSet<Student> that handles duplicates automatically
Implement enroll(Student s) using each approach, then state which is preferable and why.
Difficulty:
Intermediate
[Apply] Write a method printAll(List<String> items) that iterates the list with an enhanced for-loop, printing each item. Then call it with an ArrayList<String> and a LinkedList<String>. Explain why both calls compile.
Difficulty:
Advanced
[Create] You are building a course registration system. Design the method signature (interface method + throws) for an Enrollable interface that:
- Adds a student (can fail if course is full or duplicate)
- Removes a student by name (returns whether it succeeded)
- Checks enrollment by name
- Returns a list of enrolled student names
Difficulty:
Advanced
[Evaluate + Create] A teammate wrote this accumulator. Find the performance issue, explain the root cause, and write the corrected version.
Integer total = 0;
for (String word : words) {
if (word.length() > 5) total++;
}
Java Concepts Quiz
Test your deeper understanding of Java's type system, OOP model, and design idioms. Covers false friends with C++/Python, encapsulation vs information hiding, generics, collections, and exception handling. Includes Parsons problems, technique-selection questions, and spaced interleaving across all concepts.
Difficulty:
Basic
Predict the output of this code:
String a = new String("hello");
String b = new String("hello");
System.out.println(a == b);
System.out.println(a.equals(b));
Correct Answer:
Difficulty:
Intermediate
What does this code print?
Integer x = 200;
Integer y = 200;
System.out.println(x == y);
System.out.println(x.equals(y));
Correct Answer:
Difficulty:
Intermediate
What happens at runtime when this code executes?
Integer count = null;
int n = count;
Correct Answer:
Difficulty:
Advanced
A teammate writes this in a hot loop:
Integer sum = 0;
for (int i = 0; i < 1_000_000; i++) {
sum += i;
}
You suggest changing Integer sum to int sum. What is the precise reason?
Correct Answer:
Difficulty:
Basic
In Java, what is the default access level when no access modifier is specified on a field or method?
Correct Answer:
Difficulty:
Intermediate
A GradeReport class has private ArrayList<Integer> scores and exposes it like this:
public ArrayList<Integer> getScores() { return scores; }
All fields are private. Has information hiding (Parnas 1972) been achieved?
Correct Answer:
Difficulty:
Basic
Dog, Car, and Printer each need a serialize() method. They share no fields or common behavior. Which Java construct is the right fit?
Correct Answer:
Difficulty:
Advanced
Why is ArrayList<int> illegal in Java, while vector<int> is valid in C++?
Correct Answer:
Difficulty:
Expert
This code does not compile. Why?
public boolean isStringList(List<?> list) {
return list instanceof List<String>;
}
Correct Answer:
Difficulty:
Intermediate
[Technique Selection] Match each task to the best collection:
- A: Track which students have submitted homework (no duplicates, O(1) lookup by name)
- B: Map each student ID (int) to their final grade (double)
- C: Maintain an ordered history of grade submissions (newest at the end, access by index)
Correct Answer:
Difficulty:
Intermediate
What is the bug in this code?
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 95);
int grade = scores.get("Bob");
Correct Answer:
Difficulty:
Intermediate
Which exceptions does the Java compiler force you to explicitly catch or declare with throws?
Correct Answer:
Difficulty:
Advanced
In a Java constructor, where must super(args) appear, and what happens if you omit it?
Correct Answer:
Difficulty:
Intermediate
Given:
Vehicle v = new Car("Toyota", 2024, 4);
System.out.println(v.describe());
Vehicle is abstract with abstract describe(). Car overrides it. Which describe() runs?
Correct Answer:
Difficulty:
Expert
Arrange the lines to implement a generic Pair<A, B> class with a static swap method that returns a Pair<B, A>.
Drag lines into the solution area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
Correct order:
public class Pair<A, B> {
private A first;
private B second;
public Pair(A first, B second) { this.first = first; this.second = second; }
public A getFirst() { return first; }
public B getSecond() { return second; }
public static <X, Y> Pair<Y, X> swap(Pair<X, Y> p) {
return new Pair<>(p.getSecond(), p.getFirst());
}
}
Difficulty:
Intermediate
Arrange the lines to define a Shape interface and a Circle class that correctly implements it.
Drag lines into the solution area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
Correct order:
public interface Shape {
double getArea();
double getPerimeter();
}
public class Circle implements Shape {
private double radius;
public Circle(double radius) { this.radius = radius; }
@Override
public double getArea() { return Math.PI * radius * radius; }
@Override
public double getPerimeter() { return 2 * Math.PI * radius; }
}
Difficulty:
Advanced
Arrange the lines to define a checked exception, declare it in a method, and handle it in calling code.
Drag lines into the solution area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
Correct order:
class InsufficientFundsException extends Exception {
public InsufficientFundsException(String msg) { super(msg); }
}
public boolean withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) { throw new InsufficientFundsException("Insufficient funds"); }
balance -= amount;
return true;
}
try {
account.withdraw(1000.0);
} catch (InsufficientFundsException e) {
System.out.println("Error: " + e.getMessage());
}
Difficulty:
Expert
[Interleaving: Interfaces + Collections + OOP] You’re designing a Course class. It needs:
- A way for other classes to enroll/drop students without knowing the internal storage
- Fast O(1) lookup for
isEnrolled(String name)
- No duplicate enrollments
Which two decisions together best achieve these goals?
Correct Answer: