Java Pocket Book — Uplatz
50 deep-dive cards • Wide layout • Readable code examples • 20+ Interview Q&A
1) What is Java?
Java is a statically typed, object-oriented programming language running on the JVM (Java Virtual Machine). “Write once, run anywhere” comes from compiling to bytecode that executes on any platform with a compatible JVM. Java emphasizes readability, backward compatibility, and robust libraries. It’s widely used for enterprise backends, Android, big data, trading systems, and more. Modern Java (17/21 LTS) adds features like records, sealed classes, pattern matching, and virtual threads.
// Hello, Java
public class Main {
public static void main(String[] args) {
System.out.println("Hello, Java!");
}
}
2) JDK vs JRE vs JVM
JVM executes bytecode. JRE (runtime) = JVM + libraries to run apps. JDK (development kit) = JRE + compiler (javac), tools (jlink, jdeps, jpackage). Use the JDK to build; deploy with a JRE or custom runtime image via jlink
. Choose a distribution (Temurin, Oracle, Amazon Corretto, Zulu) that fits your support needs.
javac Main.java
java Main
3) Java Release Cadence & LTS
Java releases every 6 months; Long-Term Support (LTS) versions (e.g., 11, 17, 21) receive extended support and are recommended for production. Non-LTS are great for exploring new features early. For enterprises, standardize on an LTS and upgrade on a predictable schedule to stay secure and supported.
// Check version
java -version
4) JVM Architecture Basics
The JVM loads bytecode via the class loader, verifies it, and executes using the interpreter and JIT (C1/C2) compilers. Memory areas include heap (objects), stacks (frames per thread), metaspace (class metadata), and code cache. Garbage collectors manage heap (G1, ZGC, Shenandoah). Understanding GC and JIT helps you tune performance.
java -XX:+PrintFlagsFinal -version // Inspect defaults
5) OOP: The Big Four
Encapsulation (hide data via access modifiers), Inheritance (reuse via extends
), Polymorphism (same interface, different behaviors), and Abstraction (interfaces/abstract classes). Favor composition over inheritance; keep classes cohesive and immutable where possible.
interface Shape { double area(); }
class Circle implements Shape { double r; Circle(double r){this.r=r;} public double area(){return Math.PI*r*r;} }
6) Generics & Type Safety
Generics provide compile-time type safety and remove casts. Use bounded type parameters (<T extends Number>
) and wildcards (? super T
, ? extends T
) to express variance. Prefer generics over raw types to avoid ClassCastException
.
List<String> names = List.of("A","B");
for (String s : names) { /* ... */ }
7) Exceptions & Best Practices
Checked exceptions force callers to handle or declare; unchecked (runtime) don’t. Use checked exceptions for recoverable conditions, unchecked for programming errors. Don’t swallow exceptions; add context and rethrow. Prefer specific exceptions over Exception
.
try { Files.readString(Path.of("x.txt")); }
catch (IOException e) { throw new UncheckedIOException("Reading x.txt failed", e); }
8) Immutability
Immutable objects are thread-safe and simpler to reason about. Make fields private final
, no setters, and defensive copies of mutable inputs. Use records for concise immutable carriers.
public record Money(String currency, long cents) { }
9) Records, Sealed Classes, Pattern Matching
Records are concise immutable data carriers. Sealed classes restrict subclasses. Pattern matching for instanceof
and switch
reduces boilerplate.
sealed interface Shape permits Circle, Rect {}
record Circle(double r) implements Shape {}
record Rect(double w,double h) implements Shape {}
static double area(Shape s) {
return switch(s) {
case Circle c -> Math.PI*c.r()*c.r();
case Rect r -> r.w()*r.h();
};
}
10) Q&A — “Why Java for enterprise backends?”
Answer: Mature ecosystem (Spring, Jakarta EE), strong performance via JIT & modern GCs, excellent tooling, portability, and long-term compatibility. Java scales from monoliths to microservices with predictable latency and observability support.
11) Strings, Builders, Charset
Strings are immutable; repeated concatenation builds garbage. Use StringBuilder
in loops. Always specify charset for I/O to avoid platform defaults.
var sb = new StringBuilder();
for(int i=0;i<1000;i++) sb.append(i);
var s = sb.toString();
12) Collections & Map/Set
Choose the right structure: ArrayList
for indexed access, LinkedList
for frequent head/tail ops, HashMap
/HashSet
for average O(1), TreeMap
/TreeSet
for sorted. Prefer interfaces (List
, Map
) in APIs.
Map<String,Integer> freq = new HashMap<>();
freq.merge("java",1,Integer::sum);
13) Optional
Optional
models absence/presence without null. Don’t store in fields; use at boundaries (returns). Use orElse
/orElseGet
/orElseThrow
.
Optional<User> u = repo.find(id);
User x = u.orElseThrow();
14) java.time API
Use Instant
, LocalDate
, ZonedDateTime
, and Duration
. Avoid legacy Date
/Calendar
. Always store in UTC and format with ISO-8601.
Instant now = Instant.now();
ZonedDateTime z = now.atZone(ZoneId.of("UTC"));
15) I/O & NIO.2
java.nio.file
offers non-blocking channels and powerful file operations. Use Files
utility methods for convenience.
Files.lines(Path.of("data.txt")).forEach(System.out::println);
16) Streams & Lambdas
Streams enable declarative data processing; use parallel streams carefully. Avoid side effects inside stream ops; prefer collectors.
var total = orders.stream().mapToDouble(Order::amount).sum();
17) Functional Interfaces
Use Predicate
, Function
, Supplier
, Consumer
, and method references to simplify code. Custom @FunctionalInterface
can be handy for domain callbacks.
orders.removeIf(o -> o.isCancelled());
18) Reflection & Annotations
Reflection lets you inspect types at runtime; annotations add metadata. Use sparingly (frameworks rely on it). Be mindful of performance and module access with JPMS.
var clazz = Class.forName("com.app.User");
Arrays.stream(clazz.getDeclaredMethods()).forEach(System.out::println);
19) Modules (JPMS)
The Java Platform Module System (since 9) offers strong encapsulation and reliable configuration. Useful for large codebases and smaller runtime images via jlink
.
// module-info.java
module com.app.core { exports com.app.api; }
20) Q&A — “When to use Optional vs null?”
Answer: Use Optional
for return types at boundaries to signal absence. Don’t use in fields/params. For hot paths, null checks may be faster; for APIs, Optional
is clearer.
21) Threading Basics
Each Java thread has a stack and executes bytecode on the JVM. Creating too many platform threads is expensive. Use executors to manage pools and avoid manual thread lifecycle.
ExecutorService pool = Executors.newFixedThreadPool(8);
pool.submit(() -> doWork());
22) Synchronization & Locks
Use synchronized
for intrinsic locks; ReentrantLock
for advanced control (tryLock, fairness). Guard shared mutable state to avoid data races. Favor immutability to reduce locking.
synchronized(this){ counter++; }
23) java.util.concurrent
ConcurrentHashMap
, BlockingQueue
, Semaphore
, CountDownLatch
, Atomic*
types help build safe concurrency. Prefer these over manual wait/notify.
var q = new ArrayBlockingQueue<String>(100);
q.put("task");
24) Futures & CompletableFuture
CompletableFuture
composes async workflows with thenApply
/thenCompose
, timeouts, and combinators. Handle exceptions with exceptionally
or handle
.
CompletableFuture<User> u = repo.getAsync(id).orTimeout(1, TimeUnit.SECONDS);
25) Virtual Threads (Project Loom)
Virtual threads (Java 21) are lightweight threads scheduled by the JVM, enabling massive concurrency with simple blocking code. They map onto carrier threads as needed, reducing the “thread per request” cost. Great for I/O-bound services.
try (var exec = Executors.newVirtualThreadPerTaskExecutor()) {
exec.submit(() -> fetch());
}
26) Structured Concurrency
Structured concurrency (incubator/APIs evolving) scopes concurrent tasks with lifetimes tied to a parent. It improves cancellation, error handling, and observability.
// Pseudocode; APIs evolve across releases
// try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { ... }
27) Executors & Pool Sizing
Right-size pools to workload. I/O-bound can use larger pools (or virtual threads). CPU-bound ~ numCores
. Monitor queue length and task latency to tune.
ExecutorService cpu = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
28) Deadlocks & Liveness
Avoid nested locks and circular waits. Prefer time-bounded lock attempts and clear lock ordering. Watch for livelock and starvation.
if(lock.tryLock(50, TimeUnit.MILLISECONDS)){ try{ /*...*/ } finally { lock.unlock(); } }
29) Reactive vs Blocking
Reactive stacks (Project Reactor, RxJava) excel under massive I/O with non-blocking backpressure. Virtual threads let you keep imperative style while scaling. Choose reactive when you need fine-grained non-blocking and streaming; otherwise virtual threads simplify code.
// Reactor example
Mono<User> u = userClient.get(id);
30) Q&A — “Virtual threads vs reactive?”
Answer: Virtual threads scale blocking code with minimal refactor; great for request/response and DB I/O. Reactive shines for streaming, backpressure, and where non-blocking end-to-end is required. Many teams start with virtual threads; adopt reactive for specialized workloads.
31) Maven Basics
Maven uses declarative pom.xml
with a standard lifecycle (compile
, test
, package
). Use dependency management, profiles, and plugins (surefire, failsafe, shade). Keep versions centralized; enforce reproducible builds.
mvn -q -DskipTests package
32) Gradle Basics
Gradle offers flexible Groovy/Kotlin DSLs, incremental builds, and great performance. Use versions catalogs and the Java toolchain for consistent JDKs across dev/CI.
./gradlew test
./gradlew bootRun
33) JUnit 5 & Mockito
JUnit 5 (Jupiter) for modular tests, Mockito for doubles, AssertJ for fluent assertions. Parameterized tests improve coverage with less code.
@Test void adds(){ assertEquals(4, 2+2); }
34) Spring Boot Essentials
Auto-configuration, starters, actuator, profiles. Convention-over-configuration speeds delivery. With Boot 3 (Jakarta EE 10), prefer Java 17+ and leverage native images (GraalVM) if startup/footprint matters.
@SpringBootApplication
class App { public static void main(String[] a){ SpringApplication.run(App.class,a); } }
35) Spring MVC & REST
Annotate controllers, validate inputs, and return DTOs. Use @ControllerAdvice
for global error handling. Document with OpenAPI/Swagger.
@RestController
class UserApi {
@GetMapping("/users/{id}") User get(@PathVariable String id){ /*...*/ return u; }
}
36) Spring WebFlux (Reactive)
Non-blocking reactive web stack using Project Reactor. Use for streaming and high-concurrency I/O. Requires reactive drivers end-to-end to reap benefits.
@GetMapping("/stream") Flux<Event> stream(){ return service.tail(); }
37) Spring Data JPA & Hibernate
Repositories remove boilerplate; JPA entities map to tables. Understand fetch types (LAZY/EAGER), cascades, and transactions. Avoid N+1 queries; use joins and projections.
@Entity class User { @Id String id; String email; }
38) Transactions
Use @Transactional
at service layer. Choose isolation and propagation wisely. Keep transactions short; avoid remote calls inside them.
@Transactional
public void purchase(){ /* load, update, save */ }
39) Validation & DTOs
Bean Validation (jakarta.validation
) with annotations and custom constraints. Validate at edges; map entities to DTOs to protect domain and limit exposure.
record CreateUser(@NotBlank String email) { }
40) Q&A — “Boot MVC vs WebFlux?”
Answer: MVC is blocking and simple; great for most apps. WebFlux is reactive/non-blocking; choose it for streaming or extreme concurrency where reactive drivers exist. With virtual threads, MVC scales further while keeping imperative style.
41) Packaging & Runtimes
Package as JAR (fat/uber) or build native images with GraalVM for fast startup and low RSS (mind reflection configs). With JPMS and jlink
, ship trimmed runtimes.
jlink --add-modules java.base,java.sql --output runtime
42) Dockerizing Java
Use slim base images, layered jars, and reasonable heap settings for containers. Prefer distroless or Alpine carefully (glibc vs musl differences). Expose health endpoints.
FROM eclipse-temurin:21-jre
COPY build/libs/app.jar /app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
43) JVM Tuning & GC
Pick GC for workload: G1 (balanced), ZGC/Shenandoah (low-latency). Size heap for live set + headroom. Measure GC pauses; tune with -Xms
/-Xmx
/-XX:MaxGCPauseMillis
.
java -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -Xms512m -Xmx512m -jar app.jar
44) Performance Tactics
Avoid premature optimization. Profile with JFR, async-profiler, or JMC. Fix allocations, boxing, and hot loops; cache expensive computations; batch DB calls; use connection pools wisely.
java -XX:StartFlightRecording=name=app,settings=profile,dumponexit=true -jar app.jar
45) Security Basics
Validate inputs, escape outputs, use parameterized queries, and store secrets outside code. Enable HTTPS, HSTS, and strong ciphers. Keep dependencies patched (OWASP dependency-check). Sign artifacts when required.
// Spring Security starter + method security recommended
46) Spring Security & JWT
Use the modern config (SecurityFilterChain). For APIs, JWT with short TTL and refresh rotation. Enforce RBAC/ABAC, CSRF where applicable, and method-level security.
@Bean SecurityFilterChain http(HttpSecurity h) throws Exception {
return h.csrf(csrf -> csrf.disable()).authorizeHttpRequests(a -> a.anyRequest().authenticated()).build();
}
47) Observability
Use Micrometer for metrics, OpenTelemetry for tracing, and structured logs with context (trace/span IDs). Expose health/readiness via Spring Actuator and set SLOs (p95/p99).
management.endpoints.web.exposure.include=health,info,prometheus
48) Cloud & Config
Externalize config (environment, Config Server, Secrets Manager). Use profiles per environment and immutable deployments. Treat logs/metrics as first-class artifacts.
spring.profiles.active=prod
49) Common Pitfalls
Ignoring N+1 in JPA, mis-sized connection/thread pools, blocking calls in reactive flows, mutable shared state without locks, poor equals/hashCode on entities, and swallowing exceptions. Guard with tests, reviews, and observability.
@Override public boolean equals(Object o){ /* include business key fields */ }
50) Interview Q&A — 20 Practical Questions (Expanded)
1) JDK vs JRE vs JVM? JVM runs bytecode; JRE = JVM + libs to run apps; JDK adds compiler and tools for building. Deploy with JRE or jlink image.
2) Why LTS? Long support window and stable ecosystem; best for prod. Track non-LTS in CI to prepare upgrades.
3) How GC works? Traces reachable objects; reclaims unreachable. G1 splits heap into regions; ZGC/Shenandoah aim for low pauses.
4) String immutability benefits? Thread-safety, caching, interning, secure use as map keys. Use builders to avoid excessive allocations.
5) Checked vs unchecked exceptions? Checked for recoverable, unchecked for programming errors. Don’t overuse checked; propagate context.
6) equals & hashCode? Consistent contract; equal objects must have equal hashes. Critical for HashMap/HashSet behavior.
7) Generics erasure? Types erased at runtime; use bounded wildcards for flexibility. Avoid raw types.
8) Streams vs loops? Streams are declarative and composable; loops can be clearer for simple cases. Avoid side effects in stream ops.
9) Thread safety strategies? Immutability, confinement, synchronization, lock-free atomics, and safe publication.
10) Virtual threads? Lightweight threads enabling massive concurrency with blocking style; great for I/O-bound services.
11) CompletableFuture vs Future? CF composes async pipelines with callbacks, timeouts, and combinators; Future is basic.
12) Reactor vs imperative? Reactor enables non-blocking backpressure; imperative (with virtual threads) is simpler for request/response.
13) Spring Boot autoconfig? Conditional beans based on classpath/properties; can be customized or excluded via properties and @Conditional.
14) Avoiding JPA N+1? Fetch joins, entity graphs, projections, and proper batch fetching; inspect SQL logs.
15) Transactions best practices? Keep short, set proper isolation, avoid remote calls inside, and handle retries idempotently.
16) Testing pyramid? Unit > integration > selective e2e; use Testcontainers for real deps and wire-mock for externals.
17) JVM tuning approach? Measure with JFR, identify GC/alloc hotspots, tune heap/GC, and verify improvements with load tests.
18) Security essentials? Input validation, parameterized SQL, strong authn/z, secrets management, HTTPS, and dependency scanning.
19) Packaging choices? Fat jar for simplicity; native image for fast startup; jlink for trimmed runtime. Trade off build effort vs ops needs.
20) Microservices or monolith? Start modular monolith for speed/clarity; split when scaling/org needs demand. Invest early in observability.