| Component | Description |
|---|---|
| JVM (Java Virtual Machine) | Runtime engine that executes Java bytecode (.class files). Platform-dependent. Doesn't understand Java source, only bytecode. |
| JRE (Java Runtime Environment) | JVM + standard Java class libraries + runtime resources. Used to run Java applications. Doesn't include compiler/debugger. |
| JDK (Java Development Kit) | JRE + compilers (javac) + tools like javadoc, javap, etc. Full development kit to write, compile, and debug Java apps. |
✅ Use JDK for development, JRE to run, and JVM is the execution engine.
-
Encapsulation – Binding data and methods in one unit (class). Access is controlled via access modifiers.
-
Abstraction – Hiding implementation details and showing only functionality. Achieved via interfaces/abstract classes.
-
Inheritance – Acquiring properties and behavior from a parent class.
-
Polymorphism – Ability to take many forms.
- Compile-time (method overloading)
- Runtime (method overriding)
| Comparison | == |
.equals() |
|---|---|---|
| Type | Reference comparison | Content/logical comparison |
| Use | Checks if two references point to the same object | Checks actual values (overridden in most classes like String) |
String a = new String("test");
String b = new String("test");
a == b // false (different objects)
a.equals(b) // true (same content)- Security: Used in class loaders, file paths, network connections.
- Thread-safe: No synchronization needed as value can’t change.
- Caching: Interned strings (pooling) save memory.
- HashCode stability: Hash-based collections (like HashMap) depend on immutability.
Internally, String uses a final char[] and doesn't expose mutators.
| Class | Mutable? | Thread-safe? | Performance |
|---|---|---|---|
String |
❌ Immutable | ✅ (Safe because immutable) | Slowest (new object on each concat) |
StringBuilder |
✅ Mutable | ❌ Not thread-safe | Fastest for single-thread |
StringBuffer |
✅ Mutable | ✅ Thread-safe (synchronized) | Slower than StringBuilder |
Use StringBuilder in most cases unless thread-safety is needed.
-
Overloading (Compile-time polymorphism):
- Same method name, different parameters (type/number/order) in same class.
- Happens within a class.
void print(int a) {}
void print(String a) {}-
Overriding (Runtime polymorphism):
- Subclass provides a specific implementation of a superclass method.
- Requires same signature and @Override annotation.
class A { void show() {} }
class B extends A { @Override void show() {} }| Feature | Abstract Class | Interface (Java 8+) |
|---|---|---|
| Methods | Can have concrete + abstract | All are abstract by default; can have default, static |
| Variables | Instance variables allowed | Only constants (public static final) |
| Inheritance | Single inheritance | Multiple interfaces |
| Constructor | Yes | No |
Use interface to define contract, abstract class to provide base with shared logic.
| Modifier | Class | Package | Subclass | World |
|---|---|---|---|---|
private |
✅ | ❌ | ❌ | ❌ |
| Default (no modifier) | ✅ | ✅ | ❌ | ❌ |
protected |
✅ | ✅ | ✅ | ❌ |
public |
✅ | ✅ | ✅ | ✅ |
Helps encapsulate access at class, method, and variable levels.
| Method Type | Static | Non-static |
|---|---|---|
| Binding | Class-level | Object-level |
| Access | Can’t access instance members directly | Can access both static and instance |
| Use Case | Utility/helper methods (e.g., Math.max) |
Behavior that depends on object state |
You don’t need an object to call a static method.
- Calling one constructor from another constructor of the same class (using
this(...)) or superclass (usingsuper(...)). - Ensures single initialization logic path.
class A {
A() { this(10); } // calls A(int)
A(int x) { System.out.println("X: " + x); }
}- First line of constructor must be
this(...)orsuper(...).
| Feature | ArrayList |
LinkedList |
|---|---|---|
| Internal Structure | Dynamic array | Doubly-linked list |
| Access Time | Fast (O(1) random access) | Slow (O(n) traversal) |
| Insert/Delete (middle) | Slow (O(n) shift elements) | Fast (O(1) if node is known) |
| Memory | Less (only data) | More (data + node pointers) |
| Use Case | Read-heavy ops | Frequent insertions/deletions |
✅ Use
ArrayListwhen random access is frequent,LinkedListfor frequentadd/removeoperations.
| Feature | HashMap |
Hashtable |
|---|---|---|
| Thread-safe | ❌ Not synchronized | ✅ Synchronized |
| Null Keys/Values | Allows one null key, many null values | ❌ No null key/value |
| Performance | Faster (no locking) | Slower (synchronized) |
| Introduced In | Java 1.2 | Legacy (Java 1.0) |
✅ Use
ConcurrentHashMapfor thread-safe operations instead ofHashtable.
-
Hashing:
hashCode()is called on the key to get hash. -
Index Calculation:
index = hash & (n - 1)wherenis capacity. -
Bucket Storage: Entry is placed in a bucket array.
-
Collision Handling:
- Java 7: Linked list
- Java 8+: TreeNode (Red-Black Tree) if bucket size > 8
-
Retrieval: Uses
equals()to find key match in the bucket.
✅ Keys must implement proper
hashCode()andequals()for consistent behavior.
| Feature | HashSet |
TreeSet |
|---|---|---|
| Order | Unordered | Sorted (natural/custom order) |
| Performance | Faster (O(1) insert) | Slower (O(log n) insert/search) |
| Null Element | Allows 1 null | ❌ Null not allowed (throws NullPointerException) |
| Backed By | HashMap | TreeMap (Red-Black tree) |
✅ Use
TreeSetwhen sorted uniqueness is required.
A unified architecture for storing and manipulating groups of objects.
Key Interfaces:
Collection→List,Set,QueueMap
Core Implementations:
ArrayList,LinkedList,HashSet,TreeSet,HashMap,TreeMap, etc.
Utilities:
Collectionsclass – static methods likesort(),reverse(),synchronizedList()Arrays– for array operations
✅ Promotes code reuse, type safety, and interoperability.
| Feature | Iterator |
ListIterator |
|---|---|---|
| Direction | Forward only | Forward + backward |
| Supported Collections | Any Collection | Only List (ArrayList, LinkedList) |
| Methods | hasNext(), next(), remove() |
add(), set(), hasPrevious(), previous() etc. |
| Index Access | ❌ | ✅ (nextIndex(), previousIndex()) |
✅ Use
ListIteratorwhen you need bidirectional traversal or want to modify the list during iteration.
| Feature | Comparable<T> |
Comparator<T> |
|---|---|---|
| Package | java.lang |
java.util |
| Method | compareTo(T o) |
compare(T o1, T o2) |
| Use | Natural ordering (inside class) | Custom ordering (outside class) |
| One or Many | One compare logic | Multiple comparators possible |
class Employee implements Comparable<Employee> {
public int compareTo(Employee e) { return this.id - e.id; }
}
Comparator<Employee> bySalary = (e1, e2) -> e1.salary - e2.salary;-
HashSet does not allow duplicates.
-
When you add:
- It checks
hashCode()and thenequals()to ensure uniqueness.
- It checks
-
If the element is already present, the
add()method returnsfalse.
✅ Make sure custom classes used in a
HashSetoverride bothequals()andhashCode()correctly.
| Feature | List |
Set |
|---|---|---|
| Order | Maintains insertion order | Unordered (HashSet) / Sorted (TreeSet) |
| Duplicates | ✅ Allowed | ❌ Not allowed |
| Index Access | ✅ (get by index) | ❌ No index |
| Implementations | ArrayList, LinkedList |
HashSet, LinkedHashSet, TreeSet |
✅ Use
Listfor ordered duplicates,Setfor unique elements.
| Feature | Array |
ArrayList |
|---|---|---|
| Size | Fixed | Dynamic |
| Type | Can store primitives | Only objects (Generics) |
| Performance | Fast (no boxing/unboxing) | Slightly slower (boxing + resizing overhead) |
| Flexibility | Less flexible (manual resizing) | More flexible (auto-resizes) |
int[] arr = new int[5];
ArrayList<Integer> list = new ArrayList<>();✅ Use
Arrayfor performance-critical primitive handling,ArrayListfor flexible, resizable collections.
Exception handling ensures program stability when unexpected runtime errors occur.
Key constructs:
try– Block to monitor for exceptionscatch– Handles specific exceptionsfinally– Always executes (cleanup)throw– Manually throw exceptionthrows– Declares exceptions a method might throw
try {
int x = 5 / 0;
} catch (ArithmeticException e) {
// handle
} finally {
// cleanup
}✅ Prevents system crashes and helps in graceful error recovery.
| Type | Checked | Unchecked |
|---|---|---|
| Package | java.lang.Exception |
java.lang.RuntimeException |
| Compiler Check | ✅ Must handle/declare | ❌ No requirement |
| Examples | IOException, SQLException |
NullPointerException, ArrayIndexOutOfBoundsException |
✅ Use checked when recovery is possible, unchecked for programming bugs.
| Feature | throw |
throws |
|---|---|---|
| Purpose | Actually throws exception | Declares potential exception |
| Used In | Method body | Method signature |
| Syntax | throw new IOException("error") |
public void read() throws IOException |
✅
throwis for actual action,throwsis for method contract.
-
finallyis always executed aftertryandcatch, whether:- An exception is thrown or not.
- An exception is caught or not.
returnorSystem.exit()is used (except exit kills JVM).
Used for:
- Resource cleanup (
close DB/file/socket) - Logging
try {
// risky code
} finally {
// always runs
}✅ Yes.
- Used to handle different exception types independently.
- Order matters – place child exceptions first.
try {
// risky
} catch (IOException e) {
// handle IO
} catch (Exception e) {
// handle all others
}✅ Java 7+ supports multi-catch:
catch (IOException | SQLException e)
| Keyword | Purpose |
|---|---|
final |
Keyword to declare constants, prevent method overriding or class inheritance. |
finally |
Block that always executes after try-catch. Used for cleanup. |
finalize() |
Method called by GC before reclaiming memory (deprecated since Java 9). |
✅
final= cannot change,finally= cleanup,finalize= GC hook (avoid using it).
Multithreading enables concurrent execution of two or more threads (units of a process).
Benefits:
- Efficient CPU utilization
- Faster processing
- Non-blocking I/O
Java supports:
ThreadclassRunnableinterfaceExecutorService(preferred for production)
| Feature | Thread |
Runnable |
|---|---|---|
| Inheritance | Extends Thread (no multiple inheritance) |
Implements interface (flexible) |
| Reusability | Less (tight coupling) | More (decoupled) |
| Syntax | class MyThread extends Thread |
class MyRunnable implements Runnable |
new Thread(new MyRunnable()).start();✅ Prefer
RunnableorCallablewithExecutorServicein scalable apps.
Synchronization ensures that only one thread can access a critical section at a time.
Achieved via:
synchronizedblock/method- Locks (
ReentrantLock,ReadWriteLock)
Purpose:
- Prevent data inconsistency
- Avoid race conditions
synchronized(this) {
// thread-safe block
}✅ Use fine-grained locking to avoid performance bottlenecks.
| Method | wait() |
sleep() |
|---|---|---|
| Package | java.lang.Object |
java.lang.Thread |
| Monitor Lock | Releases lock | Doesn't release lock |
| Usage | Thread coordination (producer-consumer) | Pause thread execution |
| Needs synchronized block | ✅ Yes | ❌ No |
synchronized(obj) {
obj.wait(); // releases lock
obj.notify();
}
Thread.sleep(1000); // pauses for 1 second✅ Use
wait()/notify()for inter-thread communication,sleep()for timing.
Deadlock occurs when two or more threads wait indefinitely for each other’s locks.
Thread-1 locks A → waits for B
Thread-2 locks B → waits for A
→ both wait forever- Lock ordering – Always acquire locks in the same order.
- Try-lock – Use
tryLock()fromReentrantLockwith timeout. - Avoid nested locks – Keep locking granular and minimal.
✅ In production, monitor threads via
jstackor Java Flight Recorder for deadlock detection.
- Ensures visibility of changes to variables across threads.
- Prevents CPU caching / reordering by compiler or hardware.
- Doesn’t ensure atomicity.
volatile boolean flag = true;✅ Use for flags or state indicators, not for compound operations like
i++.
| Memory | Stack | Heap |
|---|---|---|
| Stores | Method calls, local variables | Objects, class instances |
| Size | Smaller | Larger |
| Access | LIFO | Random |
| Thread-safety | Each thread has own stack | Shared among threads |
| GC'd | No (popped on method return) | Yes (by JVM GC) |
✅ Memory leaks happen in heap,
StackOverflowErroroccurs in stack due to deep recursion.
- GC frees memory occupied by unreachable objects.
- Managed by JVM, runs automatically (can be triggered with
System.gc()but not recommended).
- Mark – Identify referenced objects
- Sweep – Delete unreferenced
- Compact – Rearranging memory (optional)
- Serial GC, Parallel GC, CMS, G1 (default now), ZGC, Shenandoah
✅ Always nullify heavy objects post-use, prefer weak references when necessary.
| Copy Type | Shallow Copy | Deep Copy |
|---|---|---|
| Field Copy | Copies references | Copies actual data |
| Nested Objects | Shared between original and copy | Independent copies |
| Tools | clone() default |
Custom copy logic / Serialization / Copy constructors |
Shallow: newObj.field = oldObj.field
Deep: newObj.field = new Field(oldObj.field)✅ For mutable object graphs, prefer deep copy to avoid side effects.
Java provides object equivalents for primitive types:
| Primitive | Wrapper |
|---|---|
int |
Integer |
char |
Character |
boolean |
Boolean |
double |
Double |
| ... | ... |
✅ Useful for working with Collections, generics, and nullability.
- Autoboxing: Auto conversion from primitive → wrapper.
- Unboxing: Wrapper → primitive
Integer i = 5; // autoboxing
int j = i; // unboxing✅ Introduced in Java 5 to simplify code working with generics and collections.
- Allow classes, interfaces, and methods to operate on typed objects.
- Ensures compile-time type safety, removes need for casting.
List<String> names = new ArrayList<>();
names.add("Raj");
String s = names.get(0); // No cast needed✅ Use bounded generics (
<T extends Number>) for constraints.
- Anonymous function syntax to simplify implementation of functional interfaces.
Runnable r = () -> System.out.println("Hello");
List<String> list = Arrays.asList("a", "b", "c");
list.forEach(s -> System.out.println(s));✅ Used heavily in Streams, Collections, and event-driven logic.
- An interface with exactly one abstract method.
- Can have
defaultandstaticmethods. - Annotated with
@FunctionalInterface(optional but recommended)
@FunctionalInterface
interface Converter<F, T> {
T convert(F from);
}Examples:
Runnable,Callable,Comparator,Function<T,R>
✅ Enables usage of lambdas and method references.
A Functional Interface:
- Has exactly one abstract method (SAM — Single Abstract Method).
- Enables use of lambda expressions and method references.
@FunctionalInterface
interface MyFunc {
void apply();
}✅ Java 8 provides many built-ins:
Runnable,Function<T, R>,Predicate<T>,Consumer<T>, etc.
- A container object to represent a nullable value.
- Avoids
NullPointerException.
Optional<String> name = Optional.ofNullable(getName());
name.ifPresent(n -> System.out.println(n));Key methods:
of(),ofNullable(),isPresent(),ifPresent(),orElse(),map()
✅ Use
Optionalas return type — never as method argument or field.
- Functional-style operations on collections or arrays.
- Lazy evaluation, parallelism, pipelining.
List<String> result = names.stream()
.filter(s -> s.startsWith("R"))
.map(String::toUpperCase)
.collect(Collectors.toList());Key stream operations:
filter(),map(),sorted(),collect(),reduce(),flatMap()
✅ Improves readability, declarative logic, and parallel processing (
.parallelStream())
- Shortcut syntax for lambda that calls an existing method.
list.forEach(System.out::println); // instead of s -> System.out.println(s)Types:
- Static:
Class::staticMethod - Instance:
obj::instanceMethod - Constructor:
Class::new
✅ Makes code more concise and readable when lambdas only delegate.
| Concept | Serialization | Deserialization |
|---|---|---|
| Purpose | Convert object → byte stream | Convert byte stream → object |
| Use Case | File, DB, network transfer | Reading back serialized data |
| API | ObjectOutputStream |
ObjectInputStream |
| Marker | implements Serializable |
Same class needed at read time |
// Serialize
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("data.ser"));
oos.writeObject(obj);
// Deserialize
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("data.ser"));
Object obj = ois.readObject();✅ Custom serialization via
writeObject/readObjectmethods.
- Marks fields to be excluded from serialization.
- Useful for sensitive data or non-serializable resources.
transient String password;✅ Transient fields are reset to default values during deserialization.
- Allows inspection and modification of classes, methods, fields at runtime.
Class<?> clazz = Class.forName("com.MyClass");
Method m = clazz.getMethod("doSomething");
m.invoke(obj);Uses:
- Frameworks (Spring, Hibernate)
- Dependency injection
- Annotation processing
⚠️ Avoid excessive use in production – slow, breaks encapsulation, impacts security.
| Aspect | Inheritance | Composition |
|---|---|---|
| Relation | "is-a" | "has-a" |
| Coupling | Tightly coupled | Loosely coupled |
| Flexibility | Less (rigid hierarchy) | More (runtime polymorphism) |
| Usage | Extending behavior | Reusing functionality by delegation |
// Composition
class Car {
private Engine engine; // has-a relationship
}✅ Prefer composition over inheritance for maintainability.
Encapsulation = Wrapping data (fields) and code (methods) into one unit — the class.
How:
- Declare fields
private - Provide
publicgetters/setters
class User {
private String name;
public String getName() { return name; }
public void setName(String n) { name = n; }
}✅ Improves security, maintainability, and loose coupling.
| Variable | Static | Instance |
|---|---|---|
| Memory | Class-level (one copy) | Object-level (per instance) |
| Accessed via | Class name | Object reference |
| Lifecycle | Lives as long as class is loaded | Lives as long as object |
| Use Case | Constants, counters, config | Per-user/object data |
class User {
static int userCount;
String name;
}✅ Use static for shared state/config, avoid it for user-specific data in multi-threaded systems.
| Feature | Static Variable | Instance Variable |
|---|---|---|
| Scope | Shared across all instances | Unique per object |
| Memory | Stored in Method Area / MetaSpace (JVM-wide) | Stored in Heap (object-specific) |
| Access | Access via class (ClassName.var) |
Access via object (obj.var) |
| Lifecycle | Exists till class is loaded | Exists till object is GC’d |
class BankAccount {
static String bankName = "SBI";
String accountHolder;
}- Changing
bankNameaffects all accounts — shared config. accountHolderis unique per object — state encapsulation.
✅ Use static for config, counters, utility constants — never for request-scoped or user data.
- Occurs when a static method in subclass has same signature as one in superclass.
class Parent {
static void show() { System.out.println("Parent"); }
}
class Child extends Parent {
static void show() { System.out.println("Child"); }
}- Called based on reference type, not object type.
Parent obj = new Child();
obj.show(); // prints "Parent"✅ It's not polymorphism. Runtime dispatch doesn't apply to static methods.
| Keyword | this |
super |
|---|---|---|
| Refers To | Current object | Immediate parent class |
| Use Cases | Resolve shadowing, constructor chaining | Access overridden methods, superclass constructors |
| Constructor Use | this(args) for internal constructor |
super(args) to call parent constructor |
this.name = name; // resolve field shadowing
super.toString(); // call parent's method✅ Use
superfor inheritance-based delegation,thisfor self-reference or chaining.
❌ No.
- Private methods are not visible to child classes.
- So they’re not inherited → can’t be overridden.
- If a child class defines a method with the same signature → it's a new method, not override.
class A {
private void show() {}
}
class B extends A {
private void show() {} // This is NOT an override
}✅ Use
@Overrideannotation — it will fail compilation if no actual override.
- Defining multiple constructors with different parameter lists.
class Employee {
Employee() {}
Employee(String name) {}
Employee(String name, int age) {}
}- Flexibility during object creation.
- Support for optional parameters without setters.
✅ Use constructor overloading when object creation depends on context/state. Avoid telescoping constructors — prefer Builder pattern for many fields.
-
The compiler provides a default no-arg constructor only if:
- No constructor is explicitly defined.
class A {
// Compiler adds: A() {}
}- If any constructor is defined (e.g., with params), compiler won’t add default one.
class B {
B(int x) {} // Now B() {} is NOT auto-generated
}✅ Always define explicit no-arg constructors if frameworks like Hibernate or Jackson need them for reflection.
| Access Level | default (package-private) |
protected |
|---|---|---|
| Same Class | ✅ | ✅ |
| Same Package | ✅ | ✅ |
| Subclass in Different Package | ❌ | ✅ |
| Other Classes | ❌ | ❌ |
class A {
void show() {} // default
protected void print() {} // protected
}✅ Use
protectedfor inheritance reuse across packages, anddefaultwhen encapsulation is limited to a module.
- Checks whether an object is an instance of a specific class or interface.
if (obj instanceof String) {
String s = (String) obj;
}if (obj instanceof String s) {
System.out.println(s.toLowerCase());
}✅ Avoid overuse. Prefer polymorphism (like
obj.doWork()) instead of type checks.
| Statement | break |
continue |
|---|---|---|
| Use | Exit loop or switch | Skip current iteration |
| Scope | Affects loop/switch as a whole | Only current loop cycle |
| Often Used In | switch-case, loop termination |
Filtering in loops |
for (int i = 0; i < 10; i++) {
if (i == 5) break; // exits loop
if (i % 2 == 0) continue; // skips even
}✅ For nested loops, use labeled break/continue to precisely control flow.
-
Controls flow based on value of a variable (
int,char,enum,String, etc.) -
Introduced in Java:
- Java 7: Support for
String - Java 14+: Enhanced
switchexpressions (yield)
- Java 7: Support for
switch (status) {
case "STARTED":
start();
break;
case "STOPPED":
stop();
break;
default:
handleUnknown();
}String result = switch (code) {
case 1 -> "ONE";
case 2 -> "TWO";
default -> "UNKNOWN";
};✅ Prefer enhanced switch for cleaner, more readable logic.
| Feature | Traditional for |
Enhanced for-each |
|---|---|---|
| Index Access | ✅ (via index i) |
❌ (no index) |
| Applicable To | Arrays, Lists, etc. | Arrays, Iterable |
| Modification | Supports modification/removal via index | No index or control; can't modify during iteration safely |
| Use Case | When index or position is needed | When just values are needed |
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
for (int value : arr) {
System.out.println(value);
}✅ Prefer
for-eachfor read-only iteration, use classicforfor indexed logic or modification.
| Loop Type | while |
do-while |
|---|---|---|
| Condition Check | Before loop starts | After first iteration |
| Execution Guarantee | 0 or more times | At least once |
| Syntax | while(condition) {} |
do { } while(condition); |
int i = 0;
while (i < 5) {
System.out.println(i++);
}
int j = 0;
do {
System.out.println(j++);
} while (j < 5);✅ Use
do-whilewhen one-time execution is mandatory, like menu loops.
Nested classes are classes defined inside another class. Four types:
-
Static nested class:
- Does not have access to outer instance.
- Use when it doesn’t need outer state.
-
Non-static inner class:
- Has access to outer class instance.
- Declared without
static.
-
Local class:
- Defined inside method/block.
- Limited to scope.
-
Anonymous class:
- Inline class without a name.
- Used for one-time implementation.
class Outer {
static class StaticNested {}
class Inner {}
}✅ Use nested classes to group related functionality and reduce namespace clutter.
| Feature | Static Nested | Inner Class |
|---|---|---|
| Belongs To | Outer class | Instance of outer class |
| Access to Outer Members | ❌ Only static | ✅ Can access all |
| Object Creation | Outer.StaticNested obj = new Outer.StaticNested(); |
Outer.Inner obj = new Outer().new Inner(); |
✅ Use static nested when you don’t need outer class state. Use inner when you want tight coupling with the outer instance.
- A class without a name, declared and instantiated in a single expression.
- Commonly used to implement interfaces or abstract classes inline.
Runnable r = new Runnable() {
public void run() {
System.out.println("Running");
}
};✅ Prior to lambdas, widely used in event handling (like in Swing, listeners). Now replaced by lambdas for functional interfaces.
| Feature | Local Class | Anonymous Class |
|---|---|---|
| Has Name | ✅ | ❌ |
| Can Extend Multiple Times | ✅ | ❌ One-off use |
| Readability | Easier to debug | Harder to debug |
| Use Case | Reusable inside method | One-time implementation |
void method() {
class LocalHelper {}
new Thread(new Runnable() {
public void run() {}
}).start(); // anonymous class
}✅ Prefer local class for multi-method use, anonymous for short-lived implementations.
A package is a namespace for organizing classes, interfaces, and sub-packages.
- Avoids name conflicts
- Controls accessibility
- Provides modular structure
package com.orchestrationengine.ums.service;✅ Follow domain-reversed naming (e.g.,
com.yourcompany.module.submodule) for clean architecture.
- Brings external classes/interfaces into scope from other packages.
import java.util.List;
import static java.lang.Math.PI; // static import- Single class:
import java.util.List; - Wildcard:
import java.util.*;(not preferred for clarity) - Static:
import static ...to call static members directly
✅ Explicit imports are preferred in production for readability and IDE support.
- The search path where the JVM and compiler look for
.classfiles and JARs.
-
Command-line:
java -cp myapp.jar com.example.Main
-
Environment variable:
CLASSPATH -
Build tools: Maven/Gradle auto-manage it via
target/classes,dependencies
✅ Incorrect classpath leads to
ClassNotFoundException/NoClassDefFoundError.
public static void main(String[] args) {
}| Keyword | Purpose |
|---|---|
public |
Accessible by JVM |
static |
JVM doesn't need to create object to call it |
void |
Doesn't return anything |
String[] args |
Accepts command-line arguments |
- Entry point should be callable without object instantiation.
- Allows JVM to bootstrap your app independently.
✅ Java 5+ also allows
public static void main(String... args)due to varargs support.
| Feature | StringTokenizer |
split() |
|---|---|---|
| Package | java.util |
java.lang.String |
| Return Type | Enumeration of tokens | String array |
| Regex Support | ❌ No regex (single-char delimiter) | ✅ Full regex support |
| Flexibility | Limited | More control (trimming, empty handling) |
| Performance | Slightly faster (low overhead) | Slightly heavier (regex engine) |
// split
String[] parts = "a,b,c".split(",");
// StringTokenizer
StringTokenizer st = new StringTokenizer("a,b,c", ",");
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}✅ Prefer
split()for modern use — more flexible and readable.
| Feature | Vector |
ArrayList |
|---|---|---|
| Thread-safe | ✅ Yes (synchronized) | ❌ No |
| Performance | Slower (due to locking) | Faster |
| Grow Factor | Doubles in size | Increases by 50% |
| Legacy | Yes (pre-Java 2) | Modern collection framework |
✅ Use
ArrayListin non-threaded scenarios. For concurrent use, preferCopyOnWriteArrayListor synchronized wrappers (Collections.synchronizedList).
| Structure | Stack |
Queue |
|---|---|---|
| Order | LIFO (Last In First Out) | FIFO (First In First Out) |
| Use Cases | Undo, backtracking | Scheduling, task queues |
| Java Class | java.util.Stack (legacy) |
java.util.Queue interface |
| Preferred Types | Deque, ArrayDeque |
LinkedList, PriorityQueue, ConcurrentLinkedQueue |
Deque<String> stack = new ArrayDeque<>();
Queue<String> queue = new LinkedList<>();✅ Avoid
Stackclass in production — useDeque(push,pop) for LIFO behavior.
- A HashMap variant that maintains insertion order.
- Implemented as a doubly-linked list of buckets.
Map<Integer, String> map = new LinkedHashMap<>();- Cache-like behavior
- Predictable iteration
- LRU cache with
accessOrder = true
new LinkedHashMap<>(16, 0.75f, true); // access-order✅ Use
LinkedHashMapwhen you need consistent order + fast access.
- A
Mapimplementation where keys are weak references. - If a key has no strong references, it can be GC’d.
Map<Object, String> map = new WeakHashMap<>();
Object key = new Object();
map.put(key, "value");
key = null;
System.gc(); // entry may be removed- Caches
- Metadata holders
- Avoid memory leaks (like with ClassLoader)
✅ Use when you want the map to auto-clean up unused keys.
| Behavior | Fail-Fast | Fail-Safe |
|---|---|---|
| On Modification | Throws ConcurrentModificationException |
No exception |
| Works On | Original collection | Cloned copy |
| Examples | ArrayList, HashSet |
CopyOnWriteArrayList, ConcurrentHashMap |
List<String> list = new ArrayList<>();
for (String s : list) {
list.add("new"); // Fail-Fast
}✅ For concurrent read/write, use fail-safe collections from
java.util.concurrent.
| Feature | Synchronized Method | Synchronized Block |
|---|---|---|
| Lock On | this or class object |
Custom object |
| Granularity | Coarse | Fine |
| Flexibility | Limited | High |
public synchronized void method() { ... } // locks `this`
public void method() {
synchronized(lockObj) {
// fine-grained control
}
}✅ Prefer block-level sync for higher concurrency and reduced contention.
- Provides thread-isolated variables.
- Each thread accessing the variable has its own independent copy.
ThreadLocal<SimpleDateFormat> formatter = ThreadLocal.withInitial(() ->
new SimpleDateFormat("yyyy-MM-dd")
);- Date formatting (non-thread-safe)
- DB connections
- Per-thread context (request, security)
✅ Use
ThreadLocalfor per-thread state in multithreaded services (e.g., REST request context).
| Feature | notify() |
notifyAll() |
|---|---|---|
| Wakes | A single waiting thread | All waiting threads |
| Fairness | Unpredictable | All get a chance |
| Use Case | Single consumer | Multiple consumers |
synchronized (lock) {
lock.notify(); // wakes 1
// lock.notifyAll(); // wakes all
}✅ Use
notifyAll()when you have multiple condition waits, e.g., in producer-consumer with many waiting consumers.
A thread pool manages a fixed number of threads for executing tasks.
- Reuses threads → avoids overhead of creating/destroying threads.
- Managed via
ExecutorService.
ExecutorService pool = Executors.newFixedThreadPool(10);
pool.submit(() -> {
// task logic
});- Improved performance
- Resource control
- Task queueing
✅ Use
ThreadPoolExecutorfor advanced tuning: core pool size, queue size, rejection policy.
Understanding how Java handles memory in multi-threaded applications is vital.
- Happens-before relationship: Ensures memory visibility.
- Volatile vs synchronized: Volatile gives visibility; synchronized gives visibility + atomicity + mutual exclusion.
- Know the thread lifecycle,
Thread.State, and memory leaks due to long-living threads.
✅ Critical for writing correct concurrent code.
Immutable objects are thread-safe, cache-friendly, and easier to reason about.
- Use
finalfor fields. - Avoid setters.
- For collections: return
Collections.unmodifiableList(...).
private final List<String> items = new ArrayList<>();
public List<String> getItems() {
return Collections.unmodifiableList(items); // defensive
}From Joshua Bloch's book (should be your go-to reference):
- Prefer composition over inheritance.
- Use
Enuminstead of constants. - Use static factory methods over constructors (
of,valueOf). - Always override
equals()andhashCode()together.
Master the following for real-world concurrency:
ReentrantLock,ReadWriteLockCountDownLatch,CyclicBarrierSemaphore,BlockingQueueCompletableFuture(Java 8+ async processing)
✅ Use
ForkJoinPoolorCompletableFuturefor parallel streams and async workflows.
- Single Responsibility Principle: One reason to change.
- Open/Closed Principle: Open for extension, closed for modification.
- Dependency Inversion: Rely on abstractions.
✅ Build loosely-coupled, testable code — align with Spring DI patterns.
Don't just stop at Java 8:
- Java 9: Modules
- Java 10:
varfor local variables - Java 14: Records (data classes)
- Java 16+: Pattern matching for
instanceof - Java 21+: Virtual Threads (Project Loom)
✅ Stay updated — leverage language evolution for concise and efficient code.
- Understand GC types (G1, ZGC, Shenandoah).
- Know
-Xms,-Xmx, and GC tuning flags. - Use tools like
jvisualvm,jconsole,Java Flight Recorder,YourKit.
✅ Always measure before optimizing. Use profilers and benchmarks, not guesswork.
-
Use custom annotations + reflection for:
- AOP
- Framework development
- Code generation (via Annotation Processors)
✅ Learn how Spring, Lombok, and JPA work under the hood.
-
Build resilient systems:
- Retry logic (
RetryTemplate,Resilience4j) - Circuit breakers
- Graceful fallbacks
- Retry logic (
✅ Production systems should degrade gracefully, not fail completely.
- REST: Use proper HTTP verbs, status codes, versioning (
/v1/,/v2/). - JSON serialization (Jackson/Gson): Customize field names, handle nulls, formats.
- API docs: Swagger/OpenAPI
- Security: OAuth2, JWT, Spring Security
✅ Think idempotency, rate limiting, and input validation always.
- Use structured logs (
logback,log4j2). - Correlate logs with request ID / trace ID.
- Integrate with ELK, Grafana, Prometheus.
✅ Logs are not just for debugging — they are your primary tool in prod.
- Unit testing: JUnit 5, Mockito, AssertJ
- Integration testing:
@SpringBootTest,TestContainers - Contract testing: Pact
- Load testing: JMeter, Gatling
✅ Aim for fast, isolated, repeatable tests — CI-friendly.
- Maven / Gradle mastery:
dependencyManagement, scopes, exclusions - BOM files, fat JARs, multi-module builds
- CI/CD tools: Jenkins, GitHub Actions
✅ Ensure reproducible builds. Keep transitive dependencies under control.
- Always validate input (even internal).
- Avoid reflection-based injection unless sandboxed.
- Use parameterized queries to prevent SQL injection.
- Encrypt sensitive data at rest and in transit.
✅ Security is a first-class concern, not a post-deployment patch.
- Spring Boot / MVC / Security
- Spring Data JPA / Redis
- Spring Cloud (Config, Eureka, Feign, Resilience)
- Spring AOP for cross-cutting concerns
✅ Your Java skills multiply with Spring mastery.