Java — Reference

Source: https://docs.oracle.com/en/java/javase/25/

Java

  • Created: 1995 by James Gosling at Sun Microsystems; now stewarded by Oracle and the OpenJDK community
  • Latest stable: JDK 26 (2026-03-17, non-LTS); JDK 25 (LTS) (2025-09-16) — recommended for production
  • Paradigms: object-oriented (class-based), imperative; functional features since 8 (lambdas, streams); pattern matching since 21; records and sealed classes since 16/17
  • Typing: static, nominal, mostly invariant generics with use-site variance via wildcards (? extends, ? super); type erasure at runtime
  • Memory: garbage collected. JDK 25 ships ZGC (generational, sub-millisecond pauses), G1 (default), Shenandoah, Parallel, Serial, Epsilon (no-op for benchmarking)
  • Compilation: AOT to bytecode (javac.class) → JIT-compiled at runtime by HotSpot’s C1/C2 (or GraalVM). AOT-to-native via GraalVM native-image and Project Leyden.
  • Primary domains: enterprise backends, Android (Kotlin/Java on ART), big data (Hadoop, Spark, Flink, Kafka), build tooling, financial systems, embedded (Java ME), IDEs (IntelliJ, Eclipse, NetBeans)
  • Official docs: https://docs.oracle.com/en/java/javase/25/

At a glance

  • JDK distributions: Oracle JDK, OpenJDK builds (Eclipse Temurin / Adoptium, Amazon Corretto, Azul Zulu, Microsoft Build of OpenJDK, BellSoft Liberica, SapMachine, GraalVM, IBM Semeru). All built from OpenJDK source.
  • Release cadence: 6-month feature releases (JEP-process driven), LTS every 2 years (8, 11, 17, 21, 25, …).
  • JVM is multi-language: Kotlin, Scala, Clojure, Groovy, JRuby, Jython, plus polyglot via GraalVM (JS, Python, Ruby, R, WASM).
  • Governance: OpenJDK Community + Java Community Process (JCP) for JSRs; JEPs (JDK Enhancement Proposals) drive features.

Getting started

Install:

  • Recommended: SDKMAN! (sdk install java 25.0.1-tem) — switches JDKs per shell.
  • Direct: Eclipse Temurin (https://adoptium.net), Microsoft Build of OpenJDK, Amazon Corretto, Oracle JDK.
  • Windows: winget install EclipseAdoptium.Temurin.25.JDK. macOS: brew install --cask temurin@25.

Hello world (single file, since JEP 330 / 477):

// Hello.java — runnable directly with `java Hello.java` (no compile step) since JDK 11+
void main() {     // implicit class & no-arg main since JDK 25 (JEP 512)
    IO.println("Hello, world!");
}

Classic form:

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, world!");
    }
}

Project layout (Maven / Gradle convention):

myapp/
  pom.xml                # or build.gradle(.kts)
  src/
    main/
      java/
        com/example/App.java
      resources/
    test/
      java/
        com/example/AppTest.java
  target/                # Maven; build/ for Gradle

Build tools:

  • Maven (XML, declarative, ubiquitous in enterprise) — mvn package.
  • Gradle (Groovy or Kotlin DSL, programmatic) — ./gradlew build. Default for Android.
  • Bazel (Google’s, mono-repo).
  • Newer: Mill, Bld.
  • JDK 25 added the JEP 512 launcher with implicit imports; you can ship single-file scripts.

REPL: jshell (since JDK 9). Online: https://dev.java/playground/

Basics

Primitives: byte (8), short (16), int (32), long (64), float (32), double (64), char (UTF-16 code unit), boolean. Boxed: Integer, Long, Double, etc. Value classes (Project Valhalla) are landing in preview.

Variables / scope: lexically scoped. var (JDK 10+) for local-variable type inference (NOT for fields, params, returns). final for immutable bindings.

var users = new ArrayList<String>();   // inferred ArrayList<String>
final int max = 10;

Control flow: if/else, for, enhanced for (T x : iterable), while, do/while, switch (statement and expression form), try/catch/finally, try-with-resources. Pattern matching for switch (JEP 441, JDK 21):

String describe(Object o) {
    return switch (o) {
        case Integer i when i > 0 -> "positive int " + i;
        case String s             -> "string: " + s;
        case null                 -> "null!";
        default                   -> "other";
    };
}

Functions: methods belong to classes. Lambdas (since 8) implement functional interfaces (one abstract method).

Function<Integer, Integer> square = x -> x * x;
List<String> upper = names.stream().map(String::toUpperCase).toList();

Method references: Class::method, instance::method, Class::new.

Strings: String is immutable, UTF-16 internally (compact strings since 9 use Latin-1 when possible). Text blocks """...""" (JDK 15). String templates were withdrawn (JEP 459 retracted) — no native interpolation as of JDK 25; use String.format or formatted.

Built-in collections: List, Set, Map, Queue, Deque interfaces with ArrayList, LinkedList, HashMap, LinkedHashMap, TreeMap, HashSet, TreeSet, ArrayDeque. Immutable factories: List.of(), Map.of(), Set.of(). Sequenced collections (JDK 21): SequencedCollection, SequencedSet, SequencedMap with getFirst/getLast/reversed.

Intermediate

Type system:

  • Generics with invariant type parameters; use-site variance via ? extends T (covariant) / ? super T (contravariant).
  • Erased at runtime (no T.class directly — pass Class<T> token).
  • Bounded type params: <T extends Comparable<T>>.
  • Records (JDK 16): public record Point(int x, int y) {} — immutable data carriers with auto equals/hashCode/toString.
  • Sealed classes (JDK 17): sealed interface Shape permits Circle, Square {} — exhaustive pattern matching.
  • Pattern matching for instanceof (JDK 16): if (o instanceof String s) { ... }.

Modules / packages: package = directory; module = JEP 261 (JDK 9) module-info.java declaring requires/exports/opens. Most apps still use the classpath, not the module path — module system is mostly used by the JDK itself and by some frameworks.

Errors: checked vs unchecked exceptions — checked must be declared with throws or caught. RuntimeException and Error are unchecked. Try-with-resources auto-closes AutoCloseable.

try (var f = Files.newBufferedReader(path)) { ... }  // auto-close

Concurrency primitives:

  • Thread, Runnable, Callable<V>, Future<V>, CompletableFuture<V>.
  • java.util.concurrent: ExecutorService, ForkJoinPool, Semaphore, CountDownLatch, CyclicBarrier, ConcurrentHashMap, BlockingQueue.
  • synchronized, volatile, Lock, ReentrantLock, StampedLock.
  • Virtual threads (JEP 444, JDK 21) — millions of cheap green threads on a small carrier-thread pool. Thread.startVirtualThread(...), Executors.newVirtualThreadPerTaskExecutor().
  • Structured concurrency (JEP 480, finalizing): StructuredTaskScope for parent-child task lifetimes.
  • Scoped values (JEP 446): immutable per-thread context, designed for virtual threads.

File I/O / networking: java.nio.file.Path/Files/FileChannel, java.net.http.HttpClient (HTTP/2 + WebSocket), Socket/ServerSocket, Selector for non-blocking I/O.

Stdlib highlights: java.time (JSR-310 immutable date/time), java.util.stream, java.util.function, java.lang.invoke (MethodHandles), java.util.concurrent, java.security, java.net.http, java.lang.foreign (Panama FFM, JDK 22).

Advanced

Memory model & GC:

  • JMM (JLS Ch. 17) defines happens-before, volatile semantics, final-field freezing.
  • Default GC: G1 (low-pause, regional). ZGC (generational since JDK 21, sub-ms pauses, multi-TB heaps). Shenandoah (Red Hat). Parallel (throughput-oriented). Serial (single-threaded). Epsilon (no-op).
  • Tune: -Xms/-Xmx (heap size), -XX:+UseZGC, -XX:MaxGCPauseMillis=N, -XX:NewRatio, -XX:SurvivorRatio. Inspect: -Xlog:gc*:file=gc.log.
  • Tools: JFR (Flight Recorder, free since 11), Mission Control (JMC), jstat, jmap, jhsdb, async-profiler, Eclipse MAT.

Concurrency / parallelism deep dive:

  • HotSpot biased locking removed in 15. Lightweight + heavyweight (monitor) locks remain.
  • VarHandle (JDK 9) — typed, mode-aware (getVolatile, compareAndSet, getAcquire/setRelease).
  • Fork/Join uses work-stealing deques.
  • Virtual threads pin to carrier thread inside synchronized; prefer ReentrantLock in pinning-sensitive code.

FFI / interop:

  • Project Panama Foreign Function & Memory API (java.lang.foreign, finalized JDK 22) — modern replacement for JNI. Arena, MemorySegment, Linker, FunctionDescriptor. jextract generates bindings from C headers.
  • JNI still works but is verbose and unsafe.
  • GraalVM Polyglot API: call JS, Python, Ruby, WASM from Java.

Reflection: java.lang.reflect (Class, Method, Field, Constructor, Modifier). MethodHandles (java.lang.invoke) — faster, type-safe, supports invokedynamic. Records expose RecordComponent[].

Performance tuning:

  • JIT: HotSpot tiered compilation (Interpreter → C1 → C2). -XX:+PrintCompilation, -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly (needs hsdis).
  • JFR: -XX:StartFlightRecording=duration=60s,filename=app.jfr. Open in JMC.
  • Profilers: async-profiler (JFR-aware, low overhead), VisualVM, YourKit, JProfiler.
  • Microbenchmarks: JMH — the only correct way to benchmark JVM code.
  • AppCDS / CDS: pre-share class metadata (-XX:ArchiveClassesAtExit).
  • AOT: GraalVM native-image, Project Leyden’s --AOT cache.

God mode

Bytecode (javap, ASM, ByteBuddy):

javap -p -c -v Foo.class    # disassemble

Bytecode is stack-based, ~200 opcodes (iload, invokevirtual, invokedynamic). ASM is the low-level lib; ByteBuddy is the friendly wrapper used by Mockito, Hibernate, Datadog agent.

Unsafe (sun.misc.Unsafe / jdk.internal.misc.Unsafe): off-heap memory, low-level CAS, raw object construction. Encapsulated since JDK 9; access requires --add-opens. Replacements:

  • VarHandle for atomic ops.
  • MemorySegment (Panama) for off-heap memory.
  • Lookup.defineHiddenClass for runtime class definition.

Java agents & instrumentation:

  • java.lang.instrument.Instrumentation — bytecode rewriting at load or runtime.
  • Premain agent: -javaagent:agent.jar.
  • Dynamic attach: VirtualMachine.attach(pid).loadAgent(...).
  • Used by: Datadog APM, New Relic, Mockito, IntelliJ debugger, JaCoCo coverage.

MethodHandle / invokedynamic: the bytecode behind lambdas, string concat (StringConcatFactory), and pattern matching dispatch. Build dynamic call sites with LambdaMetafactory.

GraalVM native-image: AOT compile JVM bytecode to a native binary. Closed-world assumption — reflection / dynamic class loading needs config. Used by Quarkus, Micronaut, Spring Native, Helidon, Picocli for fast-startup CLIs.

Project Leyden: AOT class loading + linking + (eventually) AOT-compiled native code without GraalVM’s closed-world restriction.

Project Valhalla: value classes + primitive classes + universal generics — flatten layout, eliminate boxing. In preview.

Project Loom: virtual threads (delivered) + structured concurrency + scoped values.

Project Panama: FFM API (delivered) + Vector API (incubator: SIMD intrinsics).

Custom build phases / annotation processors:

  • javax.annotation.processing — generate code at compile time. Used by Lombok (legacy hack via internal APIs), AutoValue, Dagger, Immutables, MapStruct.
  • Maven plugins: maven-compiler-plugin configures processors. Gradle: annotationProcessor configuration.

Embedding the JVM: Invocation API (JNI_CreateJavaVM) or Panama-based approach. libjvm.so is the runtime; you load it from C/C++.

Idioms & style

  • Naming: PascalCase for classes/interfaces, camelCase for methods/vars, SCREAMING_SNAKE for constants. Packages lowercase.dotted.like.com.example.
  • Formatters: google-java-format (canonical), Spotless (build-tool wrapper), Eclipse formatter, IntelliJ built-in.
  • Linters: Checkstyle, SpotBugs (formerly FindBugs), Error Prone (Google, plugs into javac), PMD, SonarQube/SonarLint.
  • Style guides: Oracle Code Conventions (1999, dated), Google Java Style (https://google.github.io/styleguide/javaguide.html), team-specific.
  • Idiomatic patterns:
    • Records for data carriers; sealed interfaces + records for sum types.
    • Optional<T> for “may return null” returns (NOT for fields/params).
    • Immutability where possible — final fields, List.copyOf(...).
    • Streams for collection pipelines, but loops are fine when clearer.
    • Try-with-resources for any AutoCloseable.
    • Builder pattern (@Builder from Lombok or hand-rolled) for big constructors.
    • Dependency injection (Spring, Guice, Dagger, Quarkus CDI, Micronaut).
  • Reviewer tells: mutable static state, swallowing exceptions, raw types (List instead of List<String>), null returns instead of Optional/empty collection, equals/hashCode mismatch, missing @Override, abuse of inheritance over composition.

Ecosystem

DomainTools
Web frameworksSpring Boot, Quarkus, Micronaut, Helidon, Vert.x, Javalin, Play
ORM / PersistenceHibernate / JPA, jOOQ, MyBatis, Spring Data, EclipseLink, Ebean
TestingJUnit 5 (Jupiter), TestNG, Mockito, AssertJ, Hamcrest, Testcontainers, ArchUnit
BuildMaven, Gradle, Bazel, Mill, Bld
Big Data / StreamApache Spark, Flink, Kafka, Beam, Hadoop, Cassandra, Elasticsearch, Lucene
Web serversTomcat, Jetty, Undertow, Netty (async), Helidon Nima (virtual-thread native)
Microservice frameworksSpring Cloud, Quarkus, Micronaut, Helidon
ObservabilityMicrometer, OpenTelemetry Java agent, JFR, Datadog, New Relic
DocsJavadoc (built-in), AsciiDoctor (asciidoclet)
Native / cloudGraalVM Native Image, Spring Native, Quarkus, Buildpacks, Jib, Liquibase, Flyway
Notable usersevery bank ever, Netflix, LinkedIn, Twitter (originally), Amazon, Google (Android, internal), Alibaba (Dragonwell JDK), Uber, Airbnb

Gotchas

  • Type erasure: new T[10] doesn’t work; can’t overload on List<String> vs List<Integer>; instanceof List<String> is illegal.
  • Autoboxing: Long vs long in ==== on boxed types compares references. Always use .equals() or unbox.
  • Integer cache: Integer.valueOf(127) == Integer.valueOf(127) is true, 128 == 128 is false.
  • Checked exceptions in lambdas: Stream.map(x -> Files.readString(p)) won’t compile — wrap in Function that re-throws as unchecked.
  • Date is awful — mutable, broken timezone handling. Use java.time (Instant, LocalDate, ZonedDateTime).
  • equals / hashCode contract — override both or neither. Records do it for you.
  • HashMap thread safety — none. Use ConcurrentHashMap.
  • Collections.synchronizedList(...) doesn’t synchronize iteration — wrap in synchronized(list) { ... } block.
  • Static initialization order can deadlock between classes.
  • String.intern() can fill PermGen / metaspace.
  • finalize() is deprecated; use Cleaner or try-with-resources.
  • Modular access: since JDK 9, setAccessible(true) on JDK internals requires --add-opens. Lombok and similar tools paper over this.
  • Optional is not Serializable; never use as a field type.
  • Newcomers from Python: verbose, no top-level functions (until JEP 512 in 25), checked exceptions feel painful, generics are erased so reflection-based libs need TypeReference/TypeToken.
  • Newcomers from C#: Java has no properties (write getX/setX), no operator overloading, no out/ref, no LINQ syntax (use Streams), generics are erased not reified.

Citations