What is Object-Oriented Programming in Java?
Object-Oriented Programming (OOP) is a way of designing software by organizing code around objects that combine state (data) and behavior (methods).
In Java, OOP helps control complexity by:
Encapsulating state
Protecting invariants
Reducing coupling
Improving maintainability and testability
OOP is not about syntax or keywords, but about designing systems that are easier to evolve safely.
What is Encapsulation?
Encapsulation means hiding internal state and exposing behavior through well-defined methods.
Its main goal is to:
Prevent invalid states
Centralize business rules
Reduce unintended side effects
Example:
public void withdraw(BigDecimal amount) {
if (amount.signum() <= 0) throw new IllegalArgumentException();
}Why is Encapsulation important?
Encapsulation is important because it:
Protects invariants
Makes code easier to reason about
Reduces ripple effects when changes occur
Improves reliability and safety
Without encapsulation, rules are scattered and bugs appear easily.
Inheritance vs Composition
Inheritance models an is-a relationship and shares behavior via a parent class.
Composition models a has-a relationship by delegating behavior to another object.
Composition is preferred because it:
Reduces coupling
Avoids fragile base class problems
Is easier to test and refactor
Inheritance should be used carefully and only when the relationship is stable.
What is Polymorphism?
Polymorphism allows different implementations to be treated as the same abstraction.
In Java, this is typically achieved through interfaces.
Example:
PaymentProcessor processor;
The concrete implementation can change without affecting the caller.
Polymorphism enables flexibility and extensibility.
What are Java Generics?
Java Generics provide compile-time type safety and eliminate the need for casts.
They:
Prevent runtime ClassCastException
Improve code readability
Make APIs self-documenting
Example:
List<String> names = new ArrayList<>();
Why are Java generics invariant?
In Java, List<String> is NOT a subtype of List<object>.</object></String>
This prevents unsafe operations such as:
objects.add(10);
Invariance protects type safety at compile time.
What is ? extends T?
? extends T means an unknown subtype of T.
Use it when:
The collection is a producer
You only need to read from it
Example:
List<? extends Number>
You can read safely, but you cannot add elements.
What is ? super T?
? super T means an unknown supertype of T.
Use it when:
The collection is a consumer
You need to write into it
Example:
List<? super Integer>
You can add Integer values safely.
What is PECS?
PECS is a mnemonic:
Producer → Extends
Consumer → Super
It helps decide when to use ? extends or ? super in generics.
Difference between == and .equals()
== compares references (memory identity).
.equals() compares logical equality, if implemented.
Example:
Integer a = 1000; Integer b = 1000; a == b // false a.equals(b) // true
Always use .equals() for value comparison.
equals() and hashCode() contract
If two objects are equal according to equals(), they must have the same hashCode().
Breaking this contract causes bugs in:
HashMap
HashSet
Caches
This can lead to silent, hard-to-diagnose production issues.
Why mutable keys break HashMap
If an object used as a key in a HashMap is mutable and changes after insertion:
Its hashCode changes
The entry becomes unreachable
Solution:
Make keys immutable
Or avoid mutable fields in equality
ArrayList vs LinkedList
ArrayList:
LinkedList:
LinkedList is almost never faster in real systems.
HashMap vs TreeMap vs LinkedHashMap
HashMap:
TreeMap:
LinkedHashMap:
What is immutability?
An immutable object cannot change after creation.
Benefits:
Immutability reduces bugs and complexity.
How to create an immutable class
Rules:
this.roles = List.copyOf(roles);
Checked vs Unchecked Exceptions
Checked exceptions:
Unchecked exceptions:
Both have valid use cases.
Best practices for exception handling
Validate early
What is a race condition?
A race condition occurs when multiple threads access shared mutable state and the result depends on timing.
Example:
count++;
This operation is not atomic.
What does synchronized do?
synchronized:
It prevents race conditions but can reduce performance if overused.
What does volatile do?
volatile guarantees visibility, not atomicity.
It ensures that changes made by one thread are visible to others.
It does NOT make compound operations thread-safe.
What are Atomic classes?
Atomic classes (e.g. AtomicInteger) provide thread-safe operations using CAS (Compare-And-Swap).
They avoid explicit locks and scale better in many cases.