Core Java
Data types, OOP pillars, interfaces vs abstract classes, generics, exception handling, enums
Core Java covers the foundational building blocks that everything else rests on. A deep understanding of how primitives and objects differ, how the JVM handles memory, and how OOP concepts apply in Java is expected in any Java engineering interview.
Key Points
- Primitives (byte, short, int, long, float, double, char, boolean) live on the stack; their wrapper classes (Integer, Long, etc.) are objects on the heap
- Autoboxing converts primitives to wrappers automatically — beware NullPointerException when unboxing null and performance overhead in hot loops
- String is immutable; String literals are interned in the String Pool — use StringBuilder for string concatenation in loops
- OOP pillars: Encapsulation (hide state), Inheritance (reuse via extends), Polymorphism (runtime dispatch via overriding), Abstraction (interfaces / abstract classes)
- Interface vs Abstract Class: interfaces support multiple inheritance, default methods since Java 8; abstract classes can have state and constructors
- Generics enforce compile-time type safety — wildcards: ? extends T (covariant/producer), ? super T (contravariant/consumer) — PECS mnemonic
- Exception hierarchy: Throwable → Error (JVM errors, don't catch) / Exception → RuntimeException (unchecked) / checked exceptions
- try-with-resources (Java 7) automatically closes AutoCloseable resources — preferred over finally blocks
- Enums are full classes — can have fields, methods, and implement interfaces
| Concept | Interface | Abstract Class |
|---|---|---|
| Multiple inheritance | Yes (implements many) | No (extends one) |
| State (fields) | Only public static final constants | Yes, any access modifier |
| Constructor | No | Yes |
| Default impl (Java 8+) | default methods | Regular methods |
| When to use | Define a contract / capability | Share implementation among related classes |
Core Java patterns: generics, enums with behaviour, try-with-resources, equals/hashCode
// Generics + bounded wildcards
public <T extends Comparable<T>> T max(List<T> list) {
return list.stream().max(Comparator.naturalOrder()).orElseThrow();
}
// Enums with behaviour
enum Planet {
MERCURY(3.303e+23, 2.4397e6),
EARTH (5.976e+24, 6.37814e6);
private final double mass, radius;
Planet(double mass, double radius) {
this.mass = mass; this.radius = radius;
}
double surfaceGravity() { return 6.67300E-11 * mass / (radius * radius); }
}
// try-with-resources
try (var reader = new BufferedReader(new FileReader("data.txt"))) {
String line;
while ((line = reader.readLine()) != null) process(line);
} // reader.close() called automatically
// equals + hashCode contract (always override together)
@Override public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Point p)) return false;
return x == p.x && y == p.y;
}
@Override public int hashCode() { return Objects.hash(x, y); }Real-World Example
The most common Java bugs in production: (1) Integer cache: Integer.valueOf(127) == Integer.valueOf(127) is true but Integer.valueOf(128) == Integer.valueOf(128) is false — always use .equals(). (2) String concatenation with + in a loop creates O(n) intermediate objects — use StringBuilder. (3) Collections.unmodifiableList() wraps but does not deep-copy — the caller can still mutate internal state.