Snippets pin to Java 21 LTS. Records (16+), sealed types (17+), pattern matching in switch (21), virtual
threads (21), and the file-mode launcher (java HelloWorld.java)
are all stable. The String.formatted + text blocks
forms are assumed. Where a row shows pre-Java-8 idioms it’s tagged Legacy.
JDK · build · runSetup
bash
# Pick a JDK (LTS preferred)
sdk install java 21.0.4-tem # Temurin via SDKMAN
brew install --cask temurin@21 # macOS Homebrew
java -version # confirm
# Build tools
brew install maven
brew install gradle
# New Maven project
mvn archetype:generate -DgroupId=com.example -DartifactId=app \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DinteractiveMode=false
cd app && mvn package && java -jar target/app-1.0-SNAPSHOT.jar
# New Gradle project
gradle init --type java-application --dsl kotlin
./gradlew run
# Run a single .java file (JEP 330, Java 11+)
java HelloWorld.java
Immutable. + compiles to StringBuilder in loops since 9.
var x = expr;
Local-variable type inference. Right-hand-side type wins.
final var x = …
Stops accidental reassignment of locals.
enum Status { DRAFT, PUBLISHED }
Class-based enum. Methods + constructors allowed.
record User(long id, String name) {}
Preferred Immutable data carrier.
sealed interface Shape permits Circle, Square {}
Closed hierarchy. Enables exhaustive switch.
"a".formatted("b") / String.format(…)
Format strings.
""" text block """
Multi-line strings (Java 15+).
lists, maps, setsCollections
List.of(1, 2, 3)
Immutable list literal.
Map.of("a", 1, "b", 2)
Immutable map literal. Max 10 entries; use Map.ofEntries beyond that.
Set.of("a", "b")
Immutable set literal.
new ArrayList<>()
Mutable resizable array.
new HashMap<>() / LinkedHashMap / TreeMap
Hash / insertion-order / sorted.
new ArrayDeque<>()
Preferred over Stack / LinkedList.
Collections.unmodifiableList(list)
Read-only view.
List.copyOf(coll)
Immutable snapshot copy.
map.computeIfAbsent(k, _ -> new ArrayList<>())
Compute + insert atomically (single-thread).
list.removeIf(x -> …)
Predicate-based filter in place.
list.sort(Comparator.comparing(X::field))
Compose comparators with thenComparing.
java.util.SequencedCollection (Java 21)
Common API across List, Deque, LinkedHashMap.
functional pipelinesStreams
coll.stream() / Arrays.stream(arr)
Source. Always finite.
.filter(p) / .map(f) / .flatMap(f)
Intermediate ops. Lazy.
.distinct() / .sorted() / .limit(n) / .skip(n)
Slicing + ordering.
.peek(c)
Debug only. Don’t rely on it for side effects.
.toList()
Preferred (Java 16+) Immutable list result.
.collect(Collectors.toList())
Legacy Mutable list.
.collect(Collectors.groupingBy(…))
Group + downstream collector.
.collect(Collectors.toMap(k, v, merge))
Always pass merge for duplicate keys.
.reduce(0, Integer::sum)
Fold. Need an associative function for parallel.
.findFirst() / .anyMatch(p) / .allMatch(p)
Short-circuit ops.
IntStream.range(0, n).sum()
Specialised primitive streams. No boxing.
.parallel()
Only for CPU-bound + thread-safe + large workloads.
java
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
record Order(long id, String status, int total) {}
List orders = List.of(
new Order(1, "shipped", 120),
new Order(2, "shipped", 80),
new Order(3, "pending", 1500)
);
// filter -> map -> collect
List shippedIds = orders.stream()
.filter(o -> "shipped".equals(o.status()))
.map(Order::id)
.toList(); // immutable list (Java 16+)
// group + sum
Map totalByStatus = orders.stream()
.collect(Collectors.groupingBy(
Order::status,
Collectors.summingInt(Order::total)));
// Parallel — only when the work is CPU-bound + safe
long bigSum = Stream.iterate(1L, n -> n + 1)
.limit(1_000_000)
.parallel()
.reduce(0L, Long::sum);
// Early exit
Optional firstBig = orders.stream()
.filter(o -> o.total() > 1000)
.findFirst();
parametric typesGenerics
class Box<T> { T value; }
Generic class.
<T extends Comparable<T>> T max(List<T> l)
Upper-bound + method type parameter.
List<? extends Number>
Producer (read). PECS: Producer Extends.
List<? super Integer>
Consumer (write). PECS: Consumer Super.
@SuppressWarnings("unchecked")
Last resort. Document why.
Class<T>
Carry a type at runtime. Used by reflection / DI.
Type erasure
Generics gone at runtime. No new T[].
List.of() / Stream.of() use varargs
No unchecked warning thanks to @SafeVarargs.
data + closed hierarchiesRecords & sealed types
record User(long id, String name) {}
Auto: equals, hashCode, toString, accessors.
public User { /* validation */ }
Compact constructor.
static factory: User.of(…)
Common pattern. Records can have static methods.
sealed interface X permits A, B {}
Closed hierarchy. Permits must be named.
non-sealed class B implements X
Opt back into open inheritance for this subtype.
final class A implements X
Leaf type. Most common.
Exhaustive switch on sealed
Compiler enforces; no default branch needed.
Record patterns: case Circle(double r) -> …
Destructure inside switch / instanceof.
java
// Record — immutable data carrier (Java 16+)
public record User(long id, String name, String email) {
// Compact constructor for validation
public User {
if (name == null || name.isBlank()) throw new IllegalArgumentException("name");
}
// Static factory
public static User of(long id, String name) {
return new User(id, name, name.toLowerCase() + "@example.com");
}
}
// Sealed interface — finite set of subtypes (Java 17+)
public sealed interface Shape permits Circle, Square, Triangle {}
public record Circle(double r) implements Shape {}
public record Square(double s) implements Shape {}
public record Triangle(double a, double b, double c) implements Shape {}
// Pattern matching for switch + records (Java 21)
public static double area(Shape s) {
return switch (s) {
case Circle(double r) -> Math.PI * r * r;
case Square(double side) -> side * side;
case Triangle(var a, var b, var c) -> {
double p = (a + b + c) / 2;
yield Math.sqrt(p * (p - a) * (p - b) * (p - c));
}
};
}
function-shaped valuesLambdas & functional
(a, b) -> a + b
Lambda. Inferred parameter types.
String::length / List::of
Method reference. Most compact form when applicable.
Stdlib HttpClient over a virtual-thread executor. Replaces the classic
“CompletableFuture chain + thread pool” pattern with one that reads like blocking code.
java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse.BodyHandlers;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.Executors;
// End-to-end: parallel GET against N URLs using virtual threads + HttpClient.
public final class FetchAll {
record Result(String url, int status, int bytes) {}
public static void main(String[] args) {
var urls = List.of(
"https://example.com",
"https://example.org",
"https://www.python.org"
);
try (var exec = Executors.newVirtualThreadPerTaskExecutor()) {
var client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.build();
var futures = urls.stream()
.map(url -> exec.submit(() -> fetch(client, url)))
.toList();
for (var f : futures) {
try { System.out.println(f.get()); }
catch (Exception e) { System.err.println(e.getMessage()); }
}
}
}
static Result fetch(HttpClient c, String url) throws Exception {
var req = HttpRequest.newBuilder(URI.create(url))
.timeout(Duration.ofSeconds(10)).GET().build();
var resp = c.send(req, BodyHandlers.ofString());
return new Result(url, resp.statusCode(), resp.body().length());
}
}
Best practiceGood to know
Use records for DTOs / value objects.
Zero boilerplate, correct equals / hashCode,
and they slot into pattern-matching switches. Cuts hundreds of lines from a typical service.
Reach for virtual threads for blocking I/O.
One platform thread can carry thousands of virtual threads waiting on JDBC / HTTP. No more sizing
thread pools by hand for I/O-bound services.
Make new collections immutable.List.of / Map.of / .toList()
return immutable views. Pass these around; mutate only behind a deliberate boundary.
Common trapsWatch out for
Autoboxing in hot loops.long sum = 0; for (Long n : list) sum += n; boxes / unboxes per iteration.
Use LongStream or a primitive accumulator.
Parallel streams aren’t free.
They share the common ForkJoinPool. Mix with blocking I/O and you starve
every other parallel stream in the JVM. Use a dedicated executor or stay sequential.
Java time zones default to system.LocalDateTime.now() drifts between dev (your laptop) and prod (UTC). Use
Instant.now() or ZonedDateTime.now(ZoneOffset.UTC).
Java is a general-purpose, statically typed language used for enterprise backends, Android development, big data processing, and cloud-native services. The JVM ecosystem includes frameworks like Spring Boot, Quarkus, and Micronaut for building scalable web and microservice applications.
What are Java records?
Records (introduced in Java 16) are immutable data classes that auto-generate a constructor, getters, equals, hashCode, and toString from their component list. They reduce boilerplate for plain data carriers: record User(long id, String name) is a complete, immutable class.
What is the Java Stream API?
The Stream API (Java 8+) provides a pipeline of lazy, functional operations on sequences — filter, map, flatMap, reduce, collect, and toList(). Streams do not mutate the source; each terminal operation (toList, forEach, count) triggers the pipeline and closes the stream.
What are virtual threads in Java?
Virtual threads (Java 21, JEP 444) are lightweight threads managed by the JVM instead of the OS, enabling millions of concurrent threads at low overhead. They use blocking code (JDBC, HTTP) without pinning a platform thread, eliminating the need for reactive programming for I/O-bound work.
Is Java still popular in 2026?
Yes. Java remains one of the top three most-used languages in enterprise, finance, and Android development. Java 21 LTS brought records, sealed types, pattern matching in switch, and virtual threads, making modern Java far more expressive than Java 8 era code.