Skip to content

Core Java

Java is a statically-typed, object-oriented language that runs on the JVM. Key features: platform independence (WORA), automatic garbage collection, strong type system. Primitives vs wrappers (autoboxing), String pool and immutability, checked vs unchecked exceptions, and the final keyword are commonly tested fundamentals.


Primitives vs Wrapper Classes

Java has 8 primitive types (int, double, boolean, etc.) stored on the stack. Each has a corresponding wrapper class (Integer, Double, Boolean) for use in generics/collections. Autoboxing converts primitives to wrappers automatically; unboxing does the reverse. Pitfall: unboxing null throws NullPointerException. Java caches Integer values from -128 to 127.

Deep Dive: Primitives Table
Primitive Wrapper Size Default Value
byte Byte 8-bit 0
short Short 16-bit 0
int Integer 32-bit 0
long Long 64-bit 0L
float Float 32-bit 0.0f
double Double 64-bit 0.0d
char Character 16-bit '\u0000'
boolean Boolean 1-bit false
Deep Dive: Autoboxing Pitfalls & Integer Cache
Integer a = 10;        // Autoboxing (int → Integer)
int b = a;             // Unboxing (Integer → int)

// NPE danger
Integer value = null;
int result = value;    // NullPointerException!

Integer cache (-128 to 127):

Integer a = 127;
Integer b = 127;
System.out.println(a == b);  // true (same cached object)

Integer x = 128;
Integer y = 128;
System.out.println(x == y);       // false (different objects!)
System.out.println(x.equals(y));  // true (value comparison)

Rule: Always use .equals() for wrapper comparison, never ==.


String Pool & Immutability

Strings are immutable — every modification creates a new String object. The String pool is a special memory region in the heap that stores unique String literals for memory efficiency. Use StringBuilder (not thread-safe, fast) or StringBuffer (thread-safe, slower) for frequent string concatenation.

Deep Dive: String Pool Behavior
String s1 = "hello";              // Created in String pool
String s2 = "hello";              // Reuses s1 from pool
String s3 = new String("hello");  // Creates new object on heap

System.out.println(s1 == s2);       // true (same pool reference)
System.out.println(s1 == s3);       // false (different objects)
System.out.println(s1.equals(s3));  // true (same content)

Why immutable?

  • Security: Strings used as keys, credentials, class names
  • Thread-safe: No synchronization needed
  • Caching: Hash code can be cached (used heavily in HashMap)
  • String pool: Enables memory optimization
Deep Dive: StringBuilder vs StringBuffer
// Bad — creates n String objects, O(n²)
String result = "";
for (int i = 0; i < 1000; i++) {
    result += i;  // Each += creates a new String
}

// Good — O(n)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i);
}
String result = sb.toString();
StringBuilder StringBuffer
Thread-safe No Yes (synchronized)
Performance Faster Slower
Use when Single-threaded (default choice) Multi-threaded

Exceptions

Java has two types: Checked exceptions extend Exception (must be handled or declared — IOException, SQLException). Unchecked exceptions extend RuntimeException (no requirement to handle — NullPointerException, IllegalArgumentException). Use checked for recoverable conditions, unchecked for programming errors.

Deep Dive: Exception Hierarchy & Examples
Throwable
├── Error (JVM issues — don't catch)
│   ├── OutOfMemoryError
│   └── StackOverflowError
└── Exception
    ├── IOException (checked)
    ├── SQLException (checked)
    └── RuntimeException (unchecked)
        ├── NullPointerException
        ├── IllegalArgumentException
        └── ArrayIndexOutOfBoundsException
// Checked — must handle or declare
public void readFile(String path) throws IOException {
    BufferedReader reader = new BufferedReader(new FileReader(path));
}

// Unchecked — no requirement (but can still catch)
public void divide(int a, int b) {
    return a / b;  // May throw ArithmeticException
}

When to use which:

  • Checked: Recoverable conditions (file not found, network error)
  • Unchecked: Programming errors (null pointer, illegal argument)

final, finally, finalize

final — makes variables unassignable, methods un-overridable, classes un-extendable. A final reference can't be reassigned, but the object's internals can still change. finally — block that always executes after try/catch (for cleanup). finalize() — deprecated GC callback (use try-with-resources instead).

Deep Dive: final Keyword Examples
final int x = 10;
x = 20;  // Compilation error

final List<String> list = new ArrayList<>();
list.add("hello");          // OK — object internals can change
list = new ArrayList<>();   // Error — cannot reassign reference

public final void display() { }  // Cannot override
public final class Utility { }   // Cannot extend
Deep Dive: finally Block
try {
    // code
} catch (Exception e) {
    // handle
} finally {
    // ALWAYS executes (even if return/exception in try/catch)
    // Used for cleanup: close files, release resources
}

Can finally be skipped? Yes — System.exit(), infinite loop in try, or JVM crash.


== vs equals()

== compares references (memory addresses). equals() compares content (must be overridden). When you override equals(), you must also override hashCode() — contract: if a.equals(b), then a.hashCode() == b.hashCode(). Records and Lombok's @EqualsAndHashCode generate both automatically.

Deep Dive: Custom equals() and hashCode()
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)

Proper implementation:

@Override
public boolean equals(Object o) {
    if (this == o) return true;                    // Same reference
    if (!(o instanceof Person person)) return false; // Type check
    return age == person.age && Objects.equals(name, person.name);
}

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

Or use a record (Java 16+):

public record Person(String name, int age) {}
// equals() and hashCode() generated automatically


Common Interview Questions

Common Interview Questions
  • What is the difference between JDK, JRE, and JVM?
  • Why are Strings immutable in Java?
  • Explain the String pool.
  • What is autoboxing? What are its pitfalls?
  • Difference between checked and unchecked exceptions?
  • What does the final keyword do when applied to variables, methods, and classes?
  • What is the difference between == and equals()?
  • Why must you override both equals() and hashCode() together?
  • What is the difference between StringBuilder and StringBuffer?
  • Can finally block be skipped?