What is Java?

The language, the platform, and why it still matters in 2026

← Back to Index

What is Java?

Java is a statically typed, object-oriented, compiled language that runs on the Java Virtual Machine (JVM). Its core promise β€” "Write Once, Run Anywhere" (WORA) β€” means bytecode compiled on any platform runs unchanged on any other platform with a JVM installed. No recompilation needed.

Released in 1995 by Sun Microsystems, Java became the dominant language for enterprise software and remains one of the top 2 languages by usage in 2026. It powers the backend of companies like LinkedIn, Netflix, Uber, and most of the global banking infrastructure.

Why Java has survived 30 years
  • Backward compatibility β€” code written in Java 5 compiles on Java 25. No other major language can claim this.
  • The JVM ecosystem β€” Kotlin, Scala and Groovy all run on the JVM and interoperate seamlessly with Java code.
  • Enterprise inertia + active innovation β€” massive existing codebases combined with a 6-month release cadence since Java 9.
  • Tooling maturity β€” IntelliJ, Maven/Gradle, Spring, JUnit, Mockito, Testcontainers. An ecosystem unmatched in productivity.

How Java Works: Compile Once, Run Anywhere

Unlike C or C++ which compile directly to platform-specific machine code, Java uses a two-stage process. This is what enables platform independence.

/*
 *  YOUR CODE          BYTECODE              NATIVE CODE
 *  (HelloWorld.java)  (HelloWorld.class)    (Platform-specific)
 *        β”‚                   β”‚                     β”‚
 *        β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
 *        └─▢│ javac       │──┴─▢│     JVM      │──▢│
 *           β”‚ (compiler)  β”‚     β”‚  (java cmd)  β”‚   β”‚
 *           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
 *                                      β”‚
 *                       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 *                  β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”
 *                  β”‚ Windows β”‚   β”‚  macOS   β”‚   β”‚   Linux   β”‚
 *                  β”‚   JVM   β”‚   β”‚   JVM    β”‚   β”‚    JVM    β”‚
 *                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
 *
 *  The SAME .class file runs on ANY platform with a JVM installed.
 */

Your first program

// File: HelloWorld.java  (filename must match public class name)
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}
# Compile β†’ produces HelloWorld.class (bytecode)
javac HelloWorld.java

# Run β†’ JVM executes the bytecode
java HelloWorld

# Java 11+ shortcut: compile and run in one step
java HelloWorld.java

# Java 25+: compact source files drop the class/main boilerplate entirely
# for scripts and small programs β€” see the "Which version" box below

JVM Architecture

The JVM is not a simple interpreter. It has three main components working together:

/*
 *  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 *  β”‚                   Java Virtual Machine                    β”‚
 *  β”‚                                                          β”‚
 *  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
 *  β”‚  β”‚  Class Loader                                     β”‚   β”‚
 *  β”‚  β”‚  β€’ Loads .class files into memory                 β”‚   β”‚
 *  β”‚  β”‚  β€’ Verifies bytecode integrity                    β”‚   β”‚
 *  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
 *  β”‚                          β”‚                               β”‚
 *  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
 *  β”‚  β”‚  Runtime Data Areas                               β”‚   β”‚
 *  β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚   β”‚
 *  β”‚  β”‚  β”‚  Method  β”‚   Heap   β”‚  Stack (per thread)    β”‚β”‚   β”‚
 *  β”‚  β”‚  β”‚  Area    β”‚ (Objects)β”‚  (Local vars, frames)  β”‚β”‚   β”‚
 *  β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚   β”‚
 *  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
 *  β”‚                          β”‚                               β”‚
 *  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
 *  β”‚  β”‚  Execution Engine                                 β”‚   β”‚
 *  β”‚  β”‚  β€’ Interpreter: line-by-line for cold code        β”‚   β”‚
 *  β”‚  β”‚  β€’ JIT Compiler: compiles hot paths to native     β”‚   β”‚
 *  β”‚  β”‚  β€’ GC: automatic heap memory management           β”‚   β”‚
 *  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
 *  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
 */
JIT: why Java is not "slow"

The JVM starts interpreting bytecode, then identifies "hot" code paths executed frequently. Those paths get compiled to native machine code and cached. Result: after warmup, Java performance is comparable to C++ for most workloads. The JVM can even outperform static compilers by applying runtime-only optimizations (e.g. inlining based on actual call patterns). Java 25 pushes this further with ahead-of-time method profiling β€” usage data from a prior run can prime the JIT before the application even sees traffic, cutting warmup time for short-lived services.

What Can You Build With Java?

Domain Examples Key Technologies
Enterprise backends LinkedIn, Netflix, Uber, most banks Spring Boot, Jakarta EE
Microservices REST APIs, event-driven systems Spring Boot, Quarkus, Micronaut
Android apps Millions of apps on Play Store Android SDK (Kotlin preferred, Java supported)
Big Data Data pipelines, stream processing Apache Kafka, Spark, Hadoop, Flink
Cloud / serverless AWS Lambda, GCP Functions Quarkus + GraalVM native image
Desktop / tools IntelliJ IDEA, Minecraft, Eclipse JavaFX, Swing

Java LTS Versions: What Actually Matters

Java releases every 6 months but only LTS (Long-Term Support) versions get multi-year patches. Use LTS for production. Non-LTS releases β€” including 22, 23, 24 and 26 β€” are 6-month previews: fine for trying a language feature early, not for a production baseline.

Version Year Key Features Status
Java 8 2014 Lambdas, Streams, Optional, new Date/Time API Still widespread in legacy systems
Java 11 2018 var, HTTP Client, new String methods Legacy baseline; migrate off if you still can
Java 17 2021 Records, Sealed classes, Pattern matching, Text blocks Minimum viable baseline for new work β€” still fully supported
Java 21 2023 Virtual threads (Loom), Pattern matching in switch, Sequenced collections Previous LTS β€” solid, still widely deployed in production
Java 25 2025 Scoped Values, Compact source files, Flexible constructor bodies, AOT method profiling, Generational Shenandoah (stable) βœ… Current LTS β€” the target for new projects
Which version should you use?

New project: Java 25. It's been in production for close to a year and carries Oracle's stated 8-year support commitment β€” no reason to start lower.
Existing project on 8 or 11: Plan migration to 17 minimum, then 25 when the codebase allows it. The compiler flags deprecated APIs you depend on at each step.
Existing project on 17 or 21: Not urgent. Both are still fully supported LTS lines; move to 25 on your team's normal upgrade cadence, not as an emergency.
Learning: Java 21 or 25 so you learn modern syntax (records, var, text blocks, virtual threads) from day one. Don't learn on 8 unless you're maintaining legacy code that requires it.

A quick note on Java 26

Java 26 shipped in March 2026. It is not LTS β€” it's a routine 6-month feature release, same category as 22, 23 and 24 before it. It doesn't replace Java 25 as the production target; the next LTS is expected roughly two years out, following the established 8 β†’ 11 β†’ 17 β†’ 21 β†’ 25 cadence. Knowing this distinction β€” LTS vs. feature release β€” is itself a fair interview question; conflating "newest version" with "version to use in production" is a common junior mistake.

Java vs Modern Alternatives

The honest comparison β€” not "Java is best everywhere", but when each language genuinely wins:

Scenario Best Choice Why
Enterprise backend / microservices Java / Kotlin Spring Boot ecosystem, team scalability, tooling depth
ML / AI / Data Science Python PyTorch, pandas, scikit-learn. Java has no equivalent here.
Systems / low-level / no GC Rust / C++ Direct memory control, zero GC pauses
Cloud-native / fast startup Go / Java+GraalVM Go starts in ms natively; GraalVM native image and Java 25's AOT improvements close the gap for Java
Android development Kotlin Google's official preference since 2017. Java still works but Kotlin is idiomatic.
Web frontend TypeScript Java doesn't run in the browser (WASM experiments aside)
Java β‰  JavaScript

Same name prefix, completely different languages. Java is compiled, statically typed, runs on the JVM, used for backend and Android. JavaScript is dynamic, runs in the browser and Node.js, used for frontend and web. The naming similarity was a 1995 marketing decision by Netscape. Don't confuse them in an interview.

Java in Production: What Seniors Actually Use

The gap between "learning Java" and "working with Java professionally" is significant. This is the real stack you'll encounter in enterprise teams:

The Standard Enterprise Stack (2026)

  • Spring Boot 3.x β€” the de facto framework. Auto-configuration, embedded Tomcat/Netty, production-ready Actuator endpoints. Requires Java 17+; runs cleanly on 25.
  • Spring Data JPA + Hibernate β€” ORM layer. Know the N+1 problem, lazy loading pitfalls, and when to drop to native queries or JOOQ.
  • Spring Security + OAuth2/JWT β€” every production API needs it. Know the filter chain, not just the annotations.
  • Apache Kafka β€” async communication between microservices. If you haven't used it in production, it's the single biggest gap most Java developers have.
  • Docker + Kubernetes β€” Java apps run in containers. Knowing how to tune JVM memory for containers is not optional.
  • Gradle β€” winning over Maven for new projects. Faster incremental builds, Kotlin DSL for type-safe build scripts.

JVM Flags Every Senior Must Know

// Container-aware memory β€” CRITICAL in Kubernetes
// Without this, JVM reads host memory instead of container limits
-XX:MaxRAMPercentage=75.0

// GC selection
// G1GC (default since Java 9): best for most web services
// ZGC (production since Java 15): sub-millisecond pauses regardless of heap size
// Generational Shenandoah (stable since Java 25): same low-pause goal as ZGC,
// different algorithm β€” worth benchmarking both under your actual load
-XX:+UseZGC -Xmx4g

// Virtual threads β€” the biggest concurrency change since Java 5 (Java 21+)
// In Spring Boot 3.2+ application.properties:
spring.threads.virtual.enabled=true
// That's it. Your app now handles dramatically more concurrent requests.
// Blocking I/O is fine again β€” virtual threads are cheap (< few KB each).

// Compact object headers (Java 25): shrinks per-object header from 12-16 to
// 8 bytes. Free heap-density win for object-heavy workloads β€” enable and measure.
-XX:+UseCompactObjectHeaders
Virtual Threads (Project Loom) β€” why this matters

Traditional platform threads cost ~1MB of stack memory each. With a 4GB heap you can run ~4,000 threads before OOM. A virtual thread costs a few KB β€” you can run millions concurrently. They unmount from the carrier thread while blocked on I/O, so blocking code is efficient again.

This eliminates the main reason to adopt reactive programming (WebFlux/Reactor) for most use cases. Imperative code is readable, testable, and now also scalable.

Important caveat: virtual threads do NOT help CPU-bound work. For heavy computation, you still need ForkJoinPool or explicit parallelism.

Java 25 companion feature: Scoped Values (JEP 506, finalized in 25) solve a problem virtual threads made worse β€” ThreadLocal doesn't scale cleanly to millions of threads. Scoped Values are immutable, shared safely across a thread's lifetime, and are the modern replacement for ThreadLocal in code written against virtual threads.

Interview Questions: Junior vs Senior

πŸŽ“ Junior level β€” know these cold

Q: What does "Write Once, Run Anywhere" mean?
Java source is compiled to platform-independent bytecode. Any device with a JVM can run that bytecode without recompilation. The JVM abstracts the platform differences.

Q: What's the difference between JDK, JRE and JVM?
JVM executes bytecode. JRE = JVM + standard libraries (enough to run Java programs). JDK = JRE + compiler (javac) + tools (enough to develop Java programs). In production you install JRE. On a dev machine you install JDK.

Q: Is Java pass-by-value or pass-by-reference?
Always pass-by-value. For objects, the value passed is the reference (pointer copy), not the object itself. Reassigning the parameter inside a method doesn't affect the caller, but mutating the object's state through the reference does.

Q: What is the difference between == and .equals()?
== compares references (memory address). .equals() compares content. Always use .equals() for Strings and objects. Exception: enums and primitives can use ==.

Q: What's the difference between an LTS and a non-LTS Java release?
LTS (Long-Term Support) versions β€” 8, 11, 17, 21, 25 β€” get years of patches from Oracle and other vendors. Non-LTS versions ship every 6 months in between (22, 23, 24, 26...) and receive updates only until the next release. Production systems target LTS; non-LTS is for trying new language features early.

πŸ”₯ Senior level β€” where you prove real experience

Q: Explain the Java memory model. Where do objects and primitives live?
Objects live on the heap, managed by GC. Primitives and object references live on the thread stack. Since Java 8, PermGen was replaced by Metaspace (native memory, no fixed limit by default) β€” it stores class metadata. A common production issue is Metaspace growing unbounded due to dynamic class generation (e.g. CGLIB proxies, Groovy scripts). Since Java 25, compact object headers also shrink per-instance overhead on the heap itself β€” relevant when discussing memory density for object-heavy workloads.

Q: What is the difference between G1GC, ZGC and Shenandoah?
G1GC (default since Java 9): generational, region-based, good general-purpose GC with configurable pause targets (-XX:MaxGCPauseMillis). ZGC (production since Java 15): concurrent, sub-millisecond pauses regardless of heap size, ideal for latency-sensitive systems. Shenandoah (Red Hat): same low-pause goal as ZGC via a different algorithm; its generational mode was promoted from experimental to a stable product feature in Java 25, closing most of the gap with ZGC's memory efficiency. For most REST APIs, G1GC is sufficient. For trading systems or real-time apps with strict SLAs, benchmark ZGC and Generational Shenandoah against your actual workload rather than assuming one is universally better.

Q: What problem do virtual threads solve, and what they don't?
Virtual threads solve the thread-per-request bottleneck: a platform thread blocked on I/O holds ~1MB of stack and a kernel thread. Virtual threads unmount from the carrier thread while blocked, making millions of concurrent blocking operations feasible. They do NOT help CPU-bound work β€” for parallel computation, ForkJoinPool or parallel streams are still the right tool. A related, more recent question: virtual threads exposed the weaknesses of ThreadLocal at scale, which is why Scoped Values (JEP 506) were finalized in Java 25 as the safer, immutable alternative for propagating context across threads.

Q: What is a memory leak in Java if GC manages memory?
GC only collects objects with no reachable references. A leak occurs when objects remain reachable but are never actually used: static collections that grow without eviction, unclosed resources (connections, streams β€” use try-with-resources), listeners never deregistered, or incorrect hashCode() on mutable keys breaking HashMap invariants. Diagnose with heap dumps via Eclipse MAT, VisualVM, or jmap.