Java Reference
Java Reference
Modern Java (17–21+): records, sealed classes, pattern matching, streams, optionals, generics, concurrency, and the build tool commands you run every day.
Java versions — what to know per release
| Version | LTS | Key features |
|---|---|---|
| Java 8 | Yes (EOL) | Lambdas, streams, Optional, default methods, java.time |
| Java 11 | Yes (EOL community) | var, String methods, HTTP Client API, removed JavaFX/Applets |
| Java 17 | Yes (active) | Records, sealed classes, text blocks, pattern matching instanceof |
| Java 21 | Yes (current LTS) | Virtual threads, sequenced collections, pattern matching switch, string templates (preview) |
| Java 25 | Yes (2025) | Upcoming LTS — value types, string templates stable |
Run LTS releases in production. Use Java 21 for new projects — virtual threads alone are worth the upgrade if you do any I/O-heavy work.
Modern language features — records, sealed, pattern matching
// Records (Java 16+) — immutable data carrier
// Generates constructor, getters, equals, hashCode, toString automatically
record Point(double x, double y) {
// Compact constructor for validation
Point {
if (Double.isNaN(x) || Double.isNaN(y)) throw new IllegalArgumentException("NaN");
}
// Additional methods are fine
double distance() { return Math.sqrt(x * x + y * y); }
}
var p = new Point(3.0, 4.0);
p.x(); // 3.0 (getter — not getX())
p.distance(); // 5.0
// Sealed classes (Java 17+) — restrict which classes can extend
sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
record Triangle(double base, double height) implements Shape {}
// Pattern matching — switch (Java 21+)
double area(Shape shape) {
return switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
case Triangle t -> 0.5 * t.base() * t.height();
}; // exhaustive — no default needed with sealed hierarchy
}
// Pattern matching — instanceof (Java 16+)
if (obj instanceof String s && s.length() > 3) {
System.out.println(s.toUpperCase()); // s is already cast
}
// Text blocks (Java 15+) — multi-line strings
String json = """
{
"name": "Alice",
"role": "admin"
}
"""; // trailing """ sets indentation — content is dedented
Streams — map, filter, collect
import java.util.stream.*;
import java.util.*;
List users = List.of(
new User("Alice", "admin", 95),
new User("Bob", "user", 72),
new User("Carol", "user", 88)
);
// Basic pipeline: source → intermediate ops → terminal op
List adminNames = users.stream()
.filter(u -> u.role().equals("admin"))
.map(User::name)
.sorted()
.collect(Collectors.toList());
// Collectors
Collectors.toList()
Collectors.toSet()
Collectors.toUnmodifiableList()
Collectors.joining(", ", "[", "]")
Collectors.counting()
Collectors.summingInt(User::score)
Collectors.averagingDouble(User::score)
// groupingBy — build a Map>
Map> byRole = users.stream()
.collect(Collectors.groupingBy(User::role));
// counting per group
Map countByRole = users.stream()
.collect(Collectors.groupingBy(User::role, Collectors.counting()));
// toMap
Map byId = users.stream()
.collect(Collectors.toMap(User::id, u -> u));
// reduce
int totalScore = users.stream()
.mapToInt(User::score)
.sum(); // or .average(), .max(), .min()
// flatMap — flatten nested collections
List allTags = users.stream()
.flatMap(u -> u.tags().stream())
.distinct()
.collect(Collectors.toList());
// Parallel streams — use for CPU-bound work on large collections
long count = users.parallelStream()
.filter(u -> expensiveCheck(u))
.count();
// Stream.of, Stream.iterate, Stream.generate
Stream.of(1, 2, 3)
Stream.iterate(0, n -> n + 2).limit(10) // 0, 2, 4, ..., 18
Stream.generate(Math::random).limit(5)
Optional — avoiding NullPointerException
import java.util.Optional;
// Create
Optional empty = Optional.empty();
Optional present = Optional.of("value"); // throws NPE if null
Optional nullable = Optional.ofNullable(maybeNull);
// Consume
opt.isPresent() // true if value present
opt.isEmpty() // true if empty (Java 11+)
opt.get() // get value — throws NoSuchElementException if empty
opt.orElse("default") // value or default
opt.orElseGet(() -> compute()) // lazy default — only evaluated if empty
opt.orElseThrow() // throw NoSuchElementException if empty
opt.orElseThrow(() -> new NotFoundException("not found"))
// Transform
opt.map(String::toUpperCase) // Optional
opt.flatMap(s -> parse(s)) // flatMap when f returns Optional
opt.filter(s -> s.length() > 3) // Optional or empty
// ifPresent / ifPresentOrElse
opt.ifPresent(System.out::println);
opt.ifPresentOrElse(
value -> process(value),
() -> handleEmpty()
);
// Stream integration (Java 9+)
opt.stream() // returns Stream of 0 or 1 elements — useful in stream pipelines
List values = optionals.stream()
.flatMap(Optional::stream) // filter out empties and unwrap
.collect(Collectors.toList());
// Common pattern: find in collection
Optional user = users.stream()
.filter(u -> u.id() == targetId)
.findFirst();
Don’t use Optional as a field or parameter type — it’s designed for return values. Don’t call .get() without checking .isPresent() — use orElse/orElseThrow instead.
Generics
// Generic class
class Box {
private T value;
Box(T value) { this.value = value; }
T get() { return value; }
Box map(Function f) { return new Box<>(f.apply(value)); }
}
// Generic method
> T max(T a, T b) {
return a.compareTo(b) >= 0 ? a : b;
}
// Bounded type parameters
double sum(List list) {
return list.stream().mapToDouble(Number::doubleValue).sum();
}
// Wildcards
// ? extends T — read (covariant / producer) — PECS: Producer Extends
void printAll(List extends Animal> animals) {
animals.forEach(a -> System.out.println(a.name()));
}
// ? super T — write (contravariant / consumer) — PECS: Consumer Super
void addAll(List super Dog> list) {
list.add(new Dog("Rex"));
}
// PECS — Producer Extends, Consumer Super
// Copy from source (producer) to dest (consumer):
void copy(List extends T> src, List super T> dest) {
for (T item : src) dest.add(item);
}
// Type erasure — generics are compile-time only, erased at runtime
// Cannot do: new T(), instanceof List, T.class
// Can do: new ArrayList<>(), obj instanceof List>
Functional interfaces and lambdas
import java.util.function.*;
// Core functional interfaces
Function T -> R apply(T t)
Consumer T -> void accept(T t)
Supplier () -> T get()
Predicate T -> boolean test(T t)
BiFunction (T,U) -> R
UnaryOperator T -> T (extends Function)
BinaryOperator (T,T) -> T
// Use in methods
void process(List users, Predicate filter, Consumer action) {
users.stream().filter(filter).forEach(action);
}
process(users, u -> u.active(), System.out::println);
// Method references
String::toUpperCase // instance method of type
user::getName // instance method of specific object
Integer::parseInt // static method
ArrayList::new // constructor
// Compose functions
Function trim = String::trim;
Function upper = String::toUpperCase;
Function prep = trim.andThen(upper);
prep.apply(" hello "); // "HELLO"
Predicate notEmpty = s -> !s.isEmpty();
Predicate notNull = Objects::nonNull;
Predicate valid = notNull.and(notEmpty);
// Custom @FunctionalInterface
@FunctionalInterface
interface Transformer {
T transform(T input);
default Transformer andThen(Transformer after) {
return input -> after.transform(this.transform(input));
}
}
Collections — List, Map, Set patterns
// Immutable collections (Java 9+)
List list = List.of("a", "b", "c"); // null-hostile, fixed size
Map map = Map.of("a", 1, "b", 2);
Set set = Set.of("x", "y", "z");
Map big = Map.ofEntries( // for >10 entries
Map.entry("key1", 1), Map.entry("key2", 2)
);
// Mutable copies
List mutable = new ArrayList<>(List.of("a", "b"));
Map mutableMap = new HashMap<>(Map.of("a", 1));
// List operations
list.add("d");
list.addAll(otherList);
list.remove(0); // by index
list.remove("element"); // by value
list.set(0, "replacement");
list.subList(1, 3); // view, not copy
Collections.sort(list);
Collections.shuffle(list);
Collections.unmodifiableList(list);
// Map operations
map.put("key", value);
map.putIfAbsent("key", defaultValue);
map.getOrDefault("key", fallback);
map.computeIfAbsent("key", k -> new ArrayList<>()); // get or create
map.merge("count", 1, Integer::sum); // upsert with merge fn
map.forEach((k, v) -> System.out.println(k + "=" + v));
// Sequenced collections (Java 21) — getFirst/getLast/addFirst/addLast
list.getFirst(); // replaces list.get(0)
list.getLast(); // replaces list.get(list.size()-1)
// Choose the right implementation
ArrayList — fast random access, slow insert/delete middle
LinkedList — fast insert/delete at ends, slow random access
ArrayDeque — queue/stack operations (prefer over Stack/LinkedList)
HashMap — O(1) put/get, unordered
LinkedHashMap — insertion-order iteration
TreeMap — sorted by key, O(log n)
HashSet — O(1) contains, unordered
EnumMap/EnumSet — fast, use when keys are enums
Concurrency — virtual threads and modern patterns
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
// Virtual threads (Java 21) — lightweight, millions per JVM
// Platform thread: one OS thread, expensive
// Virtual thread: JVM-managed, cheap — perfect for I/O-bound work
// Create a virtual thread
Thread vt = Thread.ofVirtual().start(() -> handleRequest());
// ExecutorService with virtual threads (drop-in replacement for thread pools)
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (Request req : requests) {
executor.submit(() -> process(req));
}
} // try-with-resources: waits for all tasks then shuts down
// Traditional thread pool (still useful for CPU-bound work)
ExecutorService pool = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors()
);
// CompletableFuture — async composition
CompletableFuture future = CompletableFuture
.supplyAsync(() -> fetchUser(id)) // async task
.thenApplyAsync(user -> enrich(user)) // transform result
.thenCombine(fetchPermissions(id), (u, p) ->
new UserWithPerms(u, p)) // combine two futures
.exceptionally(err -> UserWithPerms.empty()); // handle errors
UserWithPerms result = future.get(5, TimeUnit.SECONDS);
// Structured concurrency (Java 21 preview / 22+)
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
var user = scope.fork(() -> fetchUser(id));
var prefs = scope.fork(() -> fetchPrefs(id));
scope.join().throwIfFailed();
return new Dashboard(user.get(), prefs.get());
}
// Synchronized (avoid when possible — use Lock or atomic types)
synchronized (this) { count++; }
// ReentrantLock — more control than synchronized
Lock lock = new ReentrantLock();
lock.lock();
try { /* critical section */ }
finally { lock.unlock(); } // always unlock in finally
// Atomic variables — lock-free counters
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet();
counter.compareAndSet(expected, newValue);
Maven and Gradle — essential commands
// ——————— Maven ———————
mvn compile // compile main sources
mvn test // compile + run tests
mvn package // compile + test + package JAR/WAR
mvn package -DskipTests // skip tests
mvn install // package + install to local ~/.m2 repo
mvn clean // delete target/
mvn clean package // clean build (most common)
mvn dependency:tree // show full dependency tree
mvn dependency:tree -Dincludes=log4j // filter tree
mvn versions:display-dependency-updates // check for newer dependency versions
mvn spring-boot:run // run Spring Boot app
mvn help:effective-pom // show resolved POM (after parent/imports)
// Maven wrapper (use instead of system mvn)
./mvnw clean package
// ——————— Gradle ———————
./gradlew build // compile + test + assemble
./gradlew build -x test // skip tests
./gradlew test // run tests
./gradlew clean // delete build/
./gradlew clean build
./gradlew dependencies // show dependency tree
./gradlew dependencies --configuration runtimeClasspath
./gradlew bootRun // run Spring Boot app
./gradlew tasks // list available tasks
./gradlew tasks --all // include non-default tasks
./gradlew :module:task // run task in specific submodule
// Common Gradle flags
--info // more logging
--debug // full debug
--scan // build scan (upload to scans.gradle.com)
-p path // specify project dir
// ——————— Java CLI ———————
java -version
javac -version
java -jar app.jar
java -cp "lib/*:." com.example.Main
java -Xmx512m -Xms256m -jar app.jar // heap settings
java -XX:+UseG1GC -jar app.jar // GC tuning
jps // list running JVM processes
jstack // thread dump
jmap -dump:format=b,file=heap.hprof // heap dump
Exception handling
// Checked vs unchecked exceptions
// Checked: extends Exception — must be declared or caught
// Unchecked: extends RuntimeException — no requirement to handle
// try-with-resources (Java 7+) — auto-close Autocloseable
try (
var conn = dataSource.getConnection();
var stmt = conn.prepareStatement(sql)
) {
// conn and stmt closed automatically in reverse order
stmt.setInt(1, userId);
return stmt.executeQuery();
} catch (SQLException e) {
throw new DataAccessException("Query failed", e);
}
// Multi-catch (Java 7+)
catch (IOException | ParseException e) {
logger.error("Parse or IO failure", e);
}
// Custom exceptions
class ServiceException extends RuntimeException {
private final String code;
ServiceException(String code, String message) {
super(message);
this.code = code;
}
ServiceException(String code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
String getCode() { return code; }
}
// Re-throw with context (preserve original cause)
try {
repository.save(entity);
} catch (DataAccessException e) {
throw new ServiceException("DB_SAVE_FAILED",
"Failed to save user " + entity.id(), e);
}
// Suppressed exceptions — added when both try and close fail
try (var res = openResource()) {
throw new RuntimeException("primary");
} catch (Exception e) {
// e.getSuppressed() contains the close() exception
for (Throwable suppressed : e.getSuppressed()) {
logger.warn("Suppressed: {}", suppressed.getMessage());
}
throw e;
}
☕ Audit your Java deps: Use the Maven Dependency Health Checker to find outdated Spring Boot, Hibernate, log4j, and other pom.xml dependencies against Maven Central.
🔍 Free tool: Maven pom.xml Batch Health Checker — paste your entire pom.xml and get health grades for all dependencies at once.
🔍 Free tool: Gradle Dependency Health Checker — paste your build.gradle and grade all Java dependencies at once via Maven Central.
Founded
2023 in London, UK
Contact
hello@releaserun.com