Java Master Cheat-Sheets - Master Edition
Nicht mehr nur Kurznotizen, sondern eine praxistaugliche Schnellreferenz für Core Java, I/O, Concurrency, Architektur, Spring, Jakarta, Security, Cloud, Algorithmen und Interviews.
A. Arbeitsweise & Package-Standard
Schnell entscheiden, wohin Code gehört, wie Beispiele aufgebaut werden und welche Namen im SEB4U-System gelten.
Master-Regeln
- Eigene Java-Beispiele verwenden com.seb4u.demo... als Root-Package.
- Spring-Code liegt unter com.seb4u.demo.spring..., Jakarta-Code unter com.seb4u.demo.jakarta....
- Domain-Code bleibt frameworkfrei; Spring/Jakarta sind Adapter, nicht Fachlogik.
- Tiny Snippets dürfen ohne package sein, wenn sie nur Syntax demonstrieren.
- Ein Cheat-Sheet ist eine Entscheidungs- und Erinnerungsstütze, kein Ersatz für das Handbuch.
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| Core Java / Algorithmen / I/O | com.seb4u.demo.<thema> | Keine Framework-Abhängigkeit. |
| Spring Boot | com.seb4u.demo.spring.<thema> | Controller, Configuration, Security, Spring Data. |
| Jakarta EE | com.seb4u.demo.jakarta.<thema> | Servlets, JPA, Jakarta Validation ohne Spring. |
| Tests | src/test/java mit gleichem Package | Testcode spiegelt Produktionspackage. |
Copy-Paste Snippets
package com.seb4u.demo.orders; // Core / Domain / Service ohne Framework package com.seb4u.demo.io; // I/O, NIO, Dateien package com.seb4u.demo.concurrent; // Virtual Threads, Locks, Queues package com.seb4u.demo.spring.orders; // Spring MVC / Spring Boot Adapter package com.seb4u.demo.jakarta.orders; // Jakarta Servlet / JPA Adapter
B. Java Syntax, Records, Enums & sealed Types
Moderne Java-Sprachelemente schnell richtig einsetzen: Value Objects, Zustände, geschlossene Hierarchien und Pattern Matching.
Master-Regeln
- Records sind ideal für unveränderliche Daten und Value Objects, nicht für mutable Entities.
- Invarianten gehören in den compact constructor eines Records.
- sealed interfaces modellieren geschlossene Domänenzustände oder Ereignistypen.
- Enums sind gut für stabile, kleine Fachlisten mit Verhalten; nicht für dynamische Datenbank-Listen.
- Pattern Matching soll Verzweigungen klarer machen, nicht komplexer.
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| record | Value Object, DTO, Command, Event | Unveränderlich, equals/hashCode automatisch. |
| class | Entity mit Identität und kontrollierter Änderung | Bei Lifecycle und Verhalten. |
| enum | Kleine feste Menge | Kann Methoden und Felder enthalten. |
| sealed interface | Geschlossene Variante | Compiler kennt alle Implementierungen. |
Typische Fallen
- Record-Felder sind nur flach immutable: eine List muss defensiv kopiert werden.
- Enum-Namen sind API-Vertrag, wenn sie JSON werden.
- sealed Types verlieren Wert, wenn überall non-sealed verwendet wird.
Copy-Paste Snippets
package com.seb4u.demo.core; import java.math.BigDecimal; import java.util.Currency; import java.util.Objects; public record Money(BigDecimal amount, Currency currency) { public Money { Objects.requireNonNull(amount, "amount"); Objects.requireNonNull(currency, "currency"); if (amount.scale() > currency.getDefaultFractionDigits()) { throw new IllegalArgumentException("Too many fraction digits for " + currency); } } public Money add(Money other) { if (!currency.equals(other.currency)) { throw new IllegalArgumentException("Currency mismatch"); } return new Money(amount.add(other.amount), currency); } }
package com.seb4u.demo.core; import java.time.Instant; import java.util.UUID; public sealed interface OrderEvent permits OrderPlaced, OrderPaid, OrderCancelled { UUID orderId(); Instant occurredAt(); } public record OrderPlaced(UUID orderId, Instant occurredAt, String customerId) implements OrderEvent {} public record OrderPaid(UUID orderId, Instant occurredAt, String paymentRef) implements OrderEvent {} public record OrderCancelled(UUID orderId, Instant occurredAt, String reason) implements OrderEvent {} final class EventText { static String describe(OrderEvent event) { return switch (event) { case OrderPlaced e -> "Order placed for " + e.customerId(); case OrderPaid e -> "Payment received: " + e.paymentRef(); case OrderCancelled e -> "Cancelled: " + e.reason(); }; } }
C. Collections, Generics & Typ-Sicherheit
Die passende Datenstruktur wählen, Generics lesbar halten und stabile APIs bauen.
Master-Regeln
- ArrayList ist der Default für geordnete Listen; LinkedList ist selten schneller.
- HashMap/HashSet verlangen stabile equals/hashCode-Werte während der Speicherung.
- Map.computeIfAbsent ist gut für Indizes, aber nicht für teure Nebenwirkungen.
- Generics sollen fachliche Typfehler verhindern, nicht Signaturen unlesbar machen.
- Eigene Typed IDs verhindern, dass CustomerId und OrderId vertauscht werden.
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| List | Reihenfolge, Duplikate | ArrayList als Default. |
| Set | Eindeutigkeit | HashSet für Lookup, TreeSet für Sortierung. |
| Map | Lookup nach Schlüssel | HashMap als Default, LinkedHashMap für stabile Reihenfolge. |
| Queue/Deque | Arbeitslisten | ArrayDeque für Stack/Queue ohne Concurrency. |
Typische Fallen
- Mutable Objekte als Map-Key sind gefährlich.
- Raw Types vermeiden.
- Wildcard-Signaturen nur dort einsetzen, wo sie echten Nutzen bringen.
Copy-Paste Snippets
package com.seb4u.demo.collections; import java.util.UUID; public record OrderId(UUID value) { public OrderId { if (value == null) throw new IllegalArgumentException("OrderId required"); } public static OrderId newId() { return new OrderId(UUID.randomUUID()); } } public record CustomerId(String value) { public CustomerId { if (value == null || value.isBlank()) throw new IllegalArgumentException("CustomerId required"); } } final class OrderService { void assign(OrderId orderId, CustomerId customerId) { // assign(customerId, orderId) kompiliert nicht mehr versehentlich. } }
package com.seb4u.demo.collections; import java.util.*; import java.util.stream.Collectors; public record Invoice(String customerId, String number, long cents) {} public final class InvoiceIndex { public static Map<String, List<Invoice>> byCustomer(List<Invoice> invoices) { return invoices.stream().collect(Collectors.groupingBy( Invoice::customerId, LinkedHashMap::new, Collectors.collectingAndThen(Collectors.toList(), List::copyOf) )); } }
D. Streams, Optional & funktionale Pipelines
Streams als lesbare Transformationen verwenden und Nebenwirkungen kontrollieren.
Master-Regeln
- Eine Stream-Pipeline sollte eine klare Frage beantworten: filtern, transformieren, gruppieren, reduzieren.
- peek ist kein Logging-Konzept; es dient höchstens zum Debuggen.
- Optional ist Rückgabewert, nicht Feldtyp für Entities und nicht Parameterdefault.
- parallelStream nur nach Messung und bei CPU-lastigen Operationen verwenden.
- Eigene Collector lohnen sich, wenn dieselbe Aggregation mehrfach gebraucht wird.
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| map | 1:1 Transformation | DTOs, Werte extrahieren. |
| flatMap | 1:n Transformation | Listen in Listen flachziehen. |
| reduce | Aggregation | Nur wenn Collector nicht besser passt. |
| collect | Struktur bauen | List, Map, Gruppierung, eigene Aggregation. |
Typische Fallen
- Komplexe Stream-Ketten schlechter als klare Schleifen.
- Stream nach Terminal-Operation nicht wiederverwenden.
- Nebenwirkungen in map/filter machen Code schwer testbar.
Copy-Paste Snippets
package com.seb4u.demo.streams; import java.util.*; import java.util.stream.Collectors; public record Sale(String region, String product, long cents) {} public record RegionRevenue(String region, long totalCents, int count) {} public final class RevenueReport { public static List<RegionRevenue> summarize(List<Sale> sales) { return sales.stream() .filter(s -> s.cents() > 0) .collect(Collectors.groupingBy(Sale::region, Collectors.toList())) .entrySet().stream() .map(e -> new RegionRevenue( e.getKey(), e.getValue().stream().mapToLong(Sale::cents).sum(), e.getValue().size())) .sorted(Comparator.comparingLong(RegionRevenue::totalCents).reversed()) .toList(); } }
package com.seb4u.demo.streams; import java.util.*; public record User(String id, String email, boolean active) {} public final class UserDirectory { private final Map<String, User> usersByEmail; public UserDirectory(List<User> users) { this.usersByEmail = users.stream().collect( java.util.stream.Collectors.toUnmodifiableMap(User::email, u -> u)); } public Optional<User> findActiveByEmail(String email) { return Optional.ofNullable(usersByEmail.get(email)) .filter(User::active); } }
E. Exceptions, Validation & Fehlerverträge
Fehler so modellieren, dass Aufrufer korrekt reagieren können und Logs nützlich bleiben.
Master-Regeln
- Exceptions nicht verschlucken; Kontext ergänzen und Ursache behalten.
- Fachliche Ablehnung ist oft Result/Validation, nicht zwangsläufig Exception.
- Technische Fehler an Adaptergrenzen übersetzen, aber nicht verstecken.
- API-Fehler brauchen stabile Codes, nicht nur freie Texte.
- try-with-resources für alles, was Closeable/AutoCloseable ist.
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| Validation Result | Eingaben erwartbar ungültig | Mehrere Fehler sammeln. |
| Checked Exception | Externer technischer Fehler, den Aufrufer behandeln soll | I/O, Parser, Adapter. |
| RuntimeException | Programmierfehler, Invariantenbruch | Fail fast. |
| Problem DTO | HTTP/API Fehlervertrag | Stabile machine-readable Codes. |
Typische Fallen
- catch (Exception e) ohne Handlung ist gefährlich.
- Exception-Texte sind kein maschinenlesbarer Vertrag.
- Stacktraces nicht an Clients geben.
Copy-Paste Snippets
package com.seb4u.demo.errors; import java.util.List; public sealed interface Result<T> permits Result.Ok, Result.Invalid, Result.Failed { record Ok<T>(T value) implements Result<T> {} record Invalid<T>(List<String> violations) implements Result<T> {} record Failed<T>(String code, Throwable cause) implements Result<T> {} static <T> Result<T> ok(T value) { return new Ok<>(value); } static <T> Result<T> invalid(List<String> violations) { return new Invalid<>(List.copyOf(violations)); } static <T> Result<T> failed(String code, Throwable cause) { return new Failed<>(code, cause); } }
package com.seb4u.demo.errors; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; public final class ConfigLoader { public String load(Path path) { try { return Files.readString(path); } catch (IOException ex) { throw new IllegalStateException("Cannot read config: " + path.toAbsolutePath(), ex); } } }
F. I/O, NIO.2, Text & Ressourcen
Dateien sicher, atomar, streaming-basiert und encoding-bewusst verarbeiten.
Master-Regeln
- Immer Path statt String-Pfade für Dateioperationen verwenden.
- User-Pfade normalisieren und gegen erlaubten Root prüfen.
- Große Dateien streamen, nicht komplett in den Heap laden.
- Atomar schreiben: temporäre Datei im Zielverzeichnis, fsync soweit sinnvoll, dann atomic move.
- Encoding explizit setzen, meistens UTF-8.
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| Files.readString | Kleine Textdateien | Nur wenn Größe kontrolliert ist. |
| Files.lines | Streaming Text | Stream schließen: try-with-resources. |
| FileChannel.transferTo | Große Binärkopien | Effizienter als manuelles Buffering. |
| WatchService | Dateisystemevents | Debounce und Re-Check einplanen. |
Typische Fallen
- Path.normalize allein schützt nicht; startsWith(root) prüfen.
- Files.lines offen lassen führt zu Ressourcenlecks.
- Atomic move funktioniert nicht über alle FileStores hinweg.
Copy-Paste Snippets
package com.seb4u.demo.io; import java.nio.file.Path; public final class SafePathResolver { private final Path root; public SafePathResolver(Path root) { this.root = root.toAbsolutePath().normalize(); } public Path resolveUserPath(String input) { Path candidate = root.resolve(input).normalize().toAbsolutePath(); if (!candidate.startsWith(root)) { throw new SecurityException("Path traversal blocked: " + input); } return candidate; } }
package com.seb4u.demo.io; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.*; import static java.nio.file.StandardCopyOption.*; public final class AtomicTextWriter { public void write(Path target, String content) throws IOException { Path dir = target.toAbsolutePath().getParent(); Files.createDirectories(dir); Path tmp = Files.createTempFile(dir, target.getFileName().toString(), ".tmp"); try { Files.writeString(tmp, content, StandardCharsets.UTF_8, StandardOpenOption.TRUNCATE_EXISTING); Files.move(tmp, target, ATOMIC_MOVE, REPLACE_EXISTING); } finally { Files.deleteIfExists(tmp); } } }
package com.seb4u.demo.io; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.util.concurrent.atomic.LongAdder; public final class CsvCounter { public long countValidRows(Path csv) throws IOException { LongAdder count = new LongAdder(); try (var lines = Files.lines(csv, StandardCharsets.UTF_8)) { lines.skip(1) .filter(line -> !line.isBlank()) .filter(line -> line.split(",", -1).length >= 4) .forEach(line -> count.increment()); } return count.sum(); } }
G. Datum, Zeit, Locale & Textformatierung
Zeit fachlich korrekt modellieren und typische Zeitzonen- und Locale-Fehler vermeiden.
Master-Regeln
- Instant für technische Zeitpunkte, LocalDate für fachliche Kalendertage.
- ZonedDateTime verwenden, wenn Zeitzone Teil der Fachregel ist.
- Clock injizieren, damit Tests deterministisch sind.
- Locale niemals implizit aus Default-System ableiten, wenn Ausgabe ein Vertrag ist.
- Dauer (Duration) und Zeitraum (Period) nicht verwechseln.
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| Instant | Audit, Events, Persistenz-Zeitpunkt | UTC-basiert. |
| LocalDate | Geburtsdatum, Rechnungsdatum | Ohne Uhrzeit/Zone. |
| ZonedDateTime | Termine in Region | DST-Regeln relevant. |
| Duration | Millis/Sekunden/Stunden | Technische Laufzeit. |
| Period | Tage/Monate/Jahre | Kalenderlogik. |
Typische Fallen
- LocalDateTime ohne Zone ist kein globaler Zeitpunkt.
- new Date() in Tests macht Tests flaky.
- Default Locale kann Produktion anders formatieren als Entwicklung.
Copy-Paste Snippets
package com.seb4u.demo.time; import java.time.Clock; import java.time.Instant; import java.time.ZoneOffset; public final class TokenExpiry { private final Clock clock; public TokenExpiry(Clock clock) { this.clock = clock; } public boolean expired(Instant expiresAt) { return !Instant.now(clock).isBefore(expiresAt); } public static TokenExpiry fixedForTest() { return new TokenExpiry(Clock.fixed(Instant.parse("2026-01-01T00:00:00Z"), ZoneOffset.UTC)); } }
package com.seb4u.demo.time; import java.text.NumberFormat; import java.util.Currency; import java.util.Locale; public final class MoneyFormatter { public String format(long cents, Currency currency, Locale locale) { NumberFormat format = NumberFormat.getCurrencyInstance(locale); format.setCurrency(currency); return format.format(cents / 100.0); } }
H. Concurrency, Virtual Threads & Synchronisation
Nebenläufigkeit kontrolliert einsetzen: klare Ownership, Timeouts, Bulkheads, Backpressure und Cancellation.
Master-Regeln
- Mutable Shared State ist der Hauptfeind. Daten lieber unveränderlich weiterreichen.
- Virtual Threads sind stark für blockierende I/O, nicht automatisch für CPU-bound Workloads.
- Jede asynchrone Operation braucht Timeout- und Cancellation-Strategie.
- Semaphore schützt externe Ressourcen als Bulkhead.
- BlockingQueue modelliert Producer/Consumer mit Backpressure.
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| Virtual Thread per Task | Viele blockierende I/O-Aufgaben | Einfaches Thread-Modell. |
| ExecutorService fixed pool | CPU-bound Arbeit | Größe an CPU orientieren. |
| Semaphore | Externe Kapazität begrenzen | Bulkhead. |
| AtomicReference | Immutable Snapshot austauschen | Konfigurationswechsel. |
| StampedLock/ReentrantReadWriteLock | Viel Lesen, wenig Schreiben | Nur bei gemessener Notwendigkeit. |
Typische Fallen
- synchronized um I/O blockiert unnötig.
- Unbounded Executor/Queue kann Speicher fressen.
- ThreadLocal in Virtual-Thread-Massen kritisch prüfen.
Copy-Paste Snippets
package com.seb4u.demo.concurrent; import java.util.List; import java.util.concurrent.*; import java.util.function.Function; public final class BulkheadRunner<T, R> implements AutoCloseable { private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); private final Semaphore permits; public BulkheadRunner(int maxConcurrent) { this.permits = new Semaphore(maxConcurrent); } public List<R> runAll(List<T> items, Function<T, R> task) throws InterruptedException { List<Future<R>> futures = items.stream() .map(item -> executor.submit(() -> { permits.acquire(); try { return task.apply(item); } finally { permits.release(); } })) .toList(); return futures.stream().map(f -> { try { return f.get(10, TimeUnit.SECONDS); } catch (Exception e) { throw new CompletionException(e); } }).toList(); } @Override public void close() { executor.close(); } }
package com.seb4u.demo.concurrent; import java.util.concurrent.*; public final class ImportQueue implements AutoCloseable { private final BlockingQueue<String> queue = new ArrayBlockingQueue<>(500); private final ExecutorService worker = Executors.newSingleThreadExecutor(); private volatile boolean running = true; public void start() { worker.submit(() -> { while (running || !queue.isEmpty()) { String item = queue.poll(250, TimeUnit.MILLISECONDS); if (item != null) process(item); } return null; }); } public boolean submit(String item) throws InterruptedException { return queue.offer(item, 2, TimeUnit.SECONDS); } private void process(String item) { /* persistieren, validieren, publizieren */ } @Override public void close() { running = false; worker.close(); } }
I. CompletableFuture, Async Workflows & Resilience
Asynchrone Ketten lesbar, testbar und begrenzt halten.
Master-Regeln
- Executor explizit übergeben, statt unbeabsichtigt commonPool zu nutzen.
- Timeouts gehören in jede externe Kette.
- exceptionally ist Recovery, handle ist Mapping von Erfolg und Fehler.
- Nicht wild joinen; an klaren Grenzen warten.
- Retries brauchen Max-Versuche, Backoff und idempotente Operationen.
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| thenApply | synchron transformieren | Kein neuer Async-Schritt. |
| thenCompose | Async Schritt flachketten | Future<Future<T>> vermeiden. |
| allOf | Parallel sammeln | Fehlerstrategie bewusst wählen. |
| orTimeout | hartes Timeout | Fehler weiterreichen. |
| completeOnTimeout | Fallback-Wert | Degradation möglich. |
Typische Fallen
- join versteckt checked Exceptions in CompletionException.
- Async-Ketten ohne Executor sind schwer kontrollierbar.
- Retry ohne Idempotenz kann doppelte Effekte erzeugen.
Copy-Paste Snippets
package com.seb4u.demo.async; import java.time.Duration; import java.util.concurrent.*; public final class PricingGateway { private final Executor executor; public PricingGateway(Executor executor) { this.executor = executor; } public CompletableFuture<Long> priceCents(String sku) { return CompletableFuture.supplyAsync(() -> callRemotePricing(sku), executor) .orTimeout(800, TimeUnit.MILLISECONDS) .exceptionally(ex -> fallbackPrice(sku)); } private long callRemotePricing(String sku) { return 1299L; } private long fallbackPrice(String sku) { return 999L; } }
package com.seb4u.demo.async; import java.util.List; import java.util.concurrent.*; import java.util.function.Supplier; public record Try<T>(T value, Throwable error) { static <T> Try<T> ok(T value) { return new Try<>(value, null); } static <T> Try<T> failed(Throwable error) { return new Try<>(null, error); } } public final class BatchAsync { public static <T> List<Try<T>> run(List<Supplier<T>> tasks, Executor executor) { var futures = tasks.stream() .map(task -> CompletableFuture.supplyAsync(task, executor) .<Try<T>>thenApply(Try::ok) .exceptionally(Try::failed)) .toList(); return futures.stream().map(CompletableFuture::join).toList(); } }
J. Maven, Module & Projektstruktur
Reproduzierbare Builds, klare Modulgrenzen und saubere Dependency-Richtung sichern.
Master-Regeln
- Domain-Module dürfen Spring/Jakarta nicht kennen.
- Parent-POM bündelt Versionen und Plugin-Konfiguration.
- Dependency Management zentralisieren, Dependencies pro Modul explizit halten.
- Tests spiegeln Package-Struktur.
- Build-Befehle dokumentieren: compile, test, package, verify.
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| Parent POM | Versionen/Plugins zentral | Keine Fachlogik. |
| Domain Modul | Fachmodell | Keine Frameworks. |
| Application Modul | Use Cases und Ports | Hängt von Domain ab. |
| Adapter Modul | I/O, HTTP, DB, Messaging | Hängt von Application ab. |
Schnellcheck / Befehle
Copy-Paste Snippets
<modules> <module>commerceflow-domain</module> <module>commerceflow-application</module> <module>commerceflow-adapter-file</module> <module>commerceflow-adapter-spring</module> <module>commerceflow-tests</module> </modules>
<properties> <maven.compiler.release>25</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <release>${maven.compiler.release}</release> </configuration> </plugin> </plugins> </pluginManagement> </build>
K. Testing, Testbarkeit & Qualität
Tests so schreiben, dass sie Verhalten schützen, schnell Feedback geben und Refactoring ermöglichen.
Master-Regeln
- Unit Tests testen Fachlogik ohne Infrastruktur.
- Integration Tests testen Adapter, Datenbank, HTTP oder Messaging.
- Tests brauchen klare Arrange-Act-Assert-Struktur.
- Clock, IDs und externe Ports injizieren, statt statisch zu erzeugen.
- Testdaten-Builder reduzieren Rauschen, dürfen aber Invarianten nicht umgehen.
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| Unit Test | Domain und Use Cases | Schnell, ohne Container. |
| Integration Test | DB/HTTP/Messaging Adapter | Langsamer, aber realitätsnah. |
| Contract Test | API-Vertrag | Consumer/Provider absichern. |
| Architekturtest | Dependency-Regeln | Regeln automatisieren. |
Typische Fallen
- Mocke nicht Value Objects.
- Tests, die Implementierungsdetails prüfen, brechen bei gutem Refactoring.
- Thread.sleep in Tests ist fast immer ein Geruch.
Copy-Paste Snippets
package com.seb4u.demo.testing; import org.junit.jupiter.api.Test; import java.time.*; import static org.junit.jupiter.api.Assertions.*; final class TokenExpiryTest { @Test void token_is_expired_after_expiry_instant() { Clock clock = Clock.fixed(Instant.parse("2026-01-01T10:00:00Z"), ZoneOffset.UTC); TokenExpiry expiry = new TokenExpiry(clock); assertTrue(expiry.expired(Instant.parse("2026-01-01T09:59:59Z"))); assertFalse(expiry.expired(Instant.parse("2026-01-01T10:00:01Z"))); } }
package com.seb4u.demo.testing; import java.util.UUID; public final class OrderBuilder { private UUID id = UUID.randomUUID(); private String customerId = "C-100"; public OrderBuilder customer(String customerId) { this.customerId = customerId; return this; } public TestOrder build() { return new TestOrder(id, customerId); } } record TestOrder(UUID id, String customerId) {}
L. Hexagonale Architektur, DDD & CommerceFlow
Fachlogik von Frameworks trennen und Use Cases über Ports testbar halten.
Master-Regeln
- Domain modelliert Sprache und Regeln des Fachbereichs.
- Application Layer orchestriert Use Cases und spricht Ports an.
- Adapter implementieren Ports für Datei, Datenbank, HTTP, Messaging.
- Controller/Servlets sollen nicht direkt Fachregeln enthalten.
- Events beschreiben Vergangenes, Commands beschreiben Absicht.
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| Entity | Identität und Lifecycle | Order, Customer. |
| Value Object | Wertgleichheit und Invariante | Money, Email, Address. |
| Command | Absicht aus Außenwelt | PlaceOrderCommand. |
| Event | Fakt, der passiert ist | OrderPlaced. |
| Port | Abhängigkeit nach außen | OrderRepository, EventPublisher. |
Typische Fallen
- JPA Entity ist nicht automatisch Domain Model.
- DTOs nicht quer durch alle Schichten ziehen.
- Ports nicht mit Framework-Typen verschmutzen.
Copy-Paste Snippets
package com.seb4u.demo.commerce.application; import com.seb4u.demo.commerce.domain.*; public final class PlaceOrderUseCase { private final OrderRepository orders; private final EventPublisher events; public PlaceOrderUseCase(OrderRepository orders, EventPublisher events) { this.orders = orders; this.events = events; } public OrderId handle(PlaceOrderCommand command) { Order order = Order.place(command.customerId(), command.items()); orders.save(order); events.publish(order.pullEvents()); return order.id(); } }
package com.seb4u.demo.commerce.application; import com.seb4u.demo.commerce.domain.*; import java.util.List; import java.util.Optional; public interface OrderRepository { void save(Order order); Optional<Order> findById(OrderId id); } public interface EventPublisher { void publish(List<OrderEvent> events); }
M. Spring Boot, REST & Web Layer
Spring Boot als Adapter sauber einsetzen: schlanke Controller, DTOs, Validation, Fehlervertrag.
Master-Regeln
- Controller übersetzen HTTP in Use-Case-Aufrufe, enthalten aber keine Fachlogik.
- DTOs sind API-Verträge und dürfen sich von Domain Objects unterscheiden.
- Validation an Eingangsgrenzen, Invarianten zusätzlich im Domain Model.
- ProblemDetail oder ein eigenes Problem DTO für Fehlerantworten verwenden.
- Configuration Properties statt verstreuter @Value Strings.
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| @RestController | HTTP Adapter | Nur Request/Response Mapping. |
| @Service | Application Service | Use-Case-Orchestrierung. |
| @Repository | Persistence Adapter | DB-Ausnahmen übersetzen. |
| @ConfigurationProperties | Konfiguration | Typisiert und testbar. |
Typische Fallen
- Entities direkt als JSON ausgeben.
- Businesslogik in Controller packen.
- Globale Exception Handler ohne stabile Fehlercodes.
Copy-Paste Snippets
package com.seb4u.demo.spring.orders; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/orders") public class OrderController { private final PlaceOrderService service; public OrderController(PlaceOrderService service) { this.service = service; } @PostMapping ResponseEntity<OrderResponse> place(@Valid @RequestBody PlaceOrderRequest request) { String id = service.place(request.customerId()); return ResponseEntity.accepted().body(new OrderResponse(id)); } public record PlaceOrderRequest(@NotBlank String customerId) {} public record OrderResponse(String orderId) {} }
package com.seb4u.demo.spring.errors; import org.springframework.http.*; import org.springframework.web.bind.annotation.*; @RestControllerAdvice public class ApiExceptionHandler { @ExceptionHandler(IllegalArgumentException.class) ProblemDetail badRequest(IllegalArgumentException ex) { ProblemDetail problem = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST); problem.setTitle("Invalid request"); problem.setDetail(ex.getMessage()); problem.setProperty("code", "REQUEST_INVALID"); return problem; } }
N. Jakarta EE, Servlet & Persistence Adapter
Jakarta als Web-/Persistence-Adapter verwenden, ohne Domain an Container-APIs zu koppeln.
Master-Regeln
- Servlets sind Adapter und sollten Use Cases aufrufen.
- JPA Entities sind Persistence-Modell; Domain kann getrennt bleiben.
- Transaktionen gehören an Use-Case-/Repository-Grenzen.
- Bean Validation prüft Eingaben, nicht alle Fachregeln.
- Jakarta-Packages bleiben com.seb4u.demo.jakarta....
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| Servlet | HTTP ohne Spring | Mapping und Statuscodes. |
| JPA Entity | DB Mapping | Nicht automatisch API DTO. |
| CDI Bean | Dependency Injection | Containerverwaltete Services. |
| Bean Validation | Eingabeformate | NotBlank, Email, Size etc. |
Typische Fallen
- EntityManager im Domain Model verwenden.
- Lazy Loading im JSON Serializer auslösen.
- Servlet mit SQL und Fachlogik überladen.
Copy-Paste Snippets
package com.seb4u.demo.jakarta.orders; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.*; import java.io.IOException; @WebServlet("/orders") public class OrderServlet extends HttpServlet { private final PlaceOrderUseCase useCase = new PlaceOrderUseCase(); @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { String customerId = req.getParameter("customerId"); String orderId = useCase.place(customerId); resp.setStatus(HttpServletResponse.SC_ACCEPTED); resp.setContentType("application/json"); resp.getWriter().write("{"orderId":"" + orderId + ""}"); } }
package com.seb4u.demo.jakarta.persistence; import jakarta.persistence.*; @Entity @Table(name = "orders") public class OrderEntity { @Id private String id; private String customerId; private String status; protected OrderEntity() {} public OrderEntity(String id, String customerId, String status) { this.id = id; this.customerId = customerId; this.status = status; } }
O. JDBC, Transaktionen & Persistence Patterns
Datenbankzugriffe klar kapseln, Transaktionen bewusst begrenzen und Persistenzfehler übersetzen.
Master-Regeln
- Connection-Lifecycle immer über try-with-resources oder Pooling steuern.
- PreparedStatement statt String-Konkatenation.
- Transaktionen sollten fachliche Konsistenzgrenzen abbilden.
- Outbox Pattern verhindert Eventverlust zwischen DB und Messaging.
- N+1-Probleme und Lazy Loading aktiv im Blick behalten.
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| JDBC | Volle Kontrolle, einfache Queries | Mehr Code, weniger Magie. |
| JPA | Objektmapping und Unit of Work | Lazy/N+1 beachten. |
| Flyway/Liquibase | Schema-Migration | Versioniert und reproduzierbar. |
| Outbox | DB + Event Publish | Eventual Consistency. |
Typische Fallen
- Autocommit unbewusst nutzen.
- SQL-Injection durch String-Verkettung.
- Transaktion über Netzwerkcalls offen halten.
Copy-Paste Snippets
package com.seb4u.demo.persistence; import javax.sql.DataSource; import java.sql.*; public final class JdbcOrderRepository { private final DataSource dataSource; public JdbcOrderRepository(DataSource dataSource) { this.dataSource = dataSource; } public void insert(String id, String customerId) throws SQLException { String sql = "insert into orders(id, customer_id, status) values (?, ?, ?)"; try (Connection con = dataSource.getConnection()) { boolean oldAutoCommit = con.getAutoCommit(); con.setAutoCommit(false); try (PreparedStatement ps = con.prepareStatement(sql)) { ps.setString(1, id); ps.setString(2, customerId); ps.setString(3, "PLACED"); ps.executeUpdate(); con.commit(); } catch (SQLException ex) { con.rollback(); throw ex; } finally { con.setAutoCommit(oldAutoCommit); } } } }
package com.seb4u.demo.persistence; import java.sql.*; import java.time.Instant; public final class OutboxWriter { public void append(Connection con, String aggregateId, String type, String payload) throws SQLException { try (PreparedStatement ps = con.prepareStatement(""" insert into outbox(id, aggregate_id, event_type, payload, created_at) values (?, ?, ?, ?, ?) """)) { ps.setString(1, java.util.UUID.randomUUID().toString()); ps.setString(2, aggregateId); ps.setString(3, type); ps.setString(4, payload); ps.setTimestamp(5, Timestamp.from(Instant.now())); ps.executeUpdate(); } } }
P. Security, Auth & sichere APIs
Sicherheitsentscheidungen als feste Regeln verankern: Validierung, Auth, Geheimnisse, Hashing, Fehlerantworten.
Master-Regeln
- Passwörter nie selbst verschlüsseln; starke Passwort-Hashing-Verfahren nutzen.
- Secrets gehören nicht ins Git-Repository.
- Input Validation und Output Encoding sind getrennte Aufgaben.
- Authorization immer serverseitig prüfen, nicht nur im Frontend.
- Fehlerantworten dürfen keine internen Details preisgeben.
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| Authentication | Wer bist du? | Login, Token, Session. |
| Authorization | Was darfst du? | Rollen/Rechte/Policies. |
| Validation | Ist Input formal gültig? | Grenzen prüfen. |
| Sanitization/Encoding | Sichere Ausgabe | Kontextabhängig: HTML, SQL, JSON. |
| Rate Limit | Missbrauch begrenzen | API-Gateway oder App-Schicht. |
Typische Fallen
- JWT ohne Ablaufzeit.
- Rollen im Client vertrauen.
- Stacktrace an HTTP-Client ausgeben.
- Passwörter loggen.
Copy-Paste Snippets
package com.seb4u.demo.security; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.HexFormat; public final class HmacVerifier { public boolean valid(String body, String expectedHex, byte[] secret) throws Exception { Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(secret, "HmacSHA256")); byte[] actual = mac.doFinal(body.getBytes(StandardCharsets.UTF_8)); byte[] expected = HexFormat.of().parseHex(expectedHex); return MessageDigest.isEqual(actual, expected); } }
package com.seb4u.demo.spring.security; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Service; @Service public class AdminOrderService { @PreAuthorize("hasAuthority('ORDER_CANCEL')") public void cancelOrder(String orderId, String reason) { // Use Case aufrufen, Audit Event schreiben } }
Q. Logging, Metrics, Performance & JVM
Produktionsreife durch messbare, strukturierte und ressourcenschonende Anwendungen erreichen.
Master-Regeln
- Logs sollen strukturierte Felder enthalten: correlationId, orderId, userId, status.
- Metriken brauchen Zähler, Timer, Gauges und sinnvolle Labels.
- Performance erst messen, dann optimieren.
- Memory Leaks entstehen oft durch statische Collections, Caches ohne Limits und Listener ohne Deregistrierung.
- GC-Tuning ist später Schritt; zuerst Allocation Rate, Datenstrukturen und I/O prüfen.
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| Counter | Ereignisse zählen | requests_total, orders_created. |
| Timer | Dauer messen | HTTP, DB, externe Calls. |
| Gauge | Momentwert | Queue length, active workers. |
| Structured Log | Debug und Audit | Keine Secrets loggen. |
| Profiler | Hotspots finden | Nicht raten. |
Typische Fallen
- Hohe Kardinalität bei Metric Labels.
- Sensible Daten in Logs.
- Mikrobenchmarks ohne Warmup.
Copy-Paste Snippets
package com.seb4u.demo.observability; import java.time.Instant; import java.util.Map; public final class StructuredLog { public static void info(String event, Map<String, ?> fields) { System.out.println(Map.of( "level", "INFO", "event", event, "time", Instant.now().toString(), "fields", fields )); } }
package com.seb4u.demo.performance; public final class TimerProbe implements AutoCloseable { private final String name; private final long start = System.nanoTime(); public TimerProbe(String name) { this.name = name; } @Override public void close() { long millis = (System.nanoTime() - start) / 1_000_000; System.out.println(name + " took " + millis + " ms"); } }
R. Fullstack Java, HTTP, JSON & UI-Flows
Backend-APIs und einfache UIs konsistent verbinden: Validierung, Fehler, Pagination, Uploads und Exporte.
Master-Regeln
- HTTP-Statuscode, Fehlercode und Fehlermeldung müssen zusammenpassen.
- Pagination nie ohne maximale Page Size anbieten.
- Filter/Sortierung als stabilen Vertrag dokumentieren.
- Datei-Uploads brauchen Größenlimit, Content-Type-Prüfung und sichere Pfade.
- Frontend validiert für UX, Backend validiert für Sicherheit.
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| GET | Lesen ohne Seiteneffekt | Cachebar, idempotent. |
| POST | Erzeugen/Aktion | Nicht idempotent oder mit Idempotency-Key. |
| PUT | Ersetzen | Idempotent. |
| PATCH | Teiländerung | Semantik sauber dokumentieren. |
| DELETE | Löschen/Stornieren | Oft fachlich als Cancel modellieren. |
Typische Fallen
- Unbegrenzte Exporte synchron ausführen.
- Dateinamen aus Upload direkt verwenden.
- HTTP 200 für Fachfehler ohne Fehlervertrag.
Copy-Paste Snippets
package com.seb4u.demo.spring.web; import java.util.List; public record PageResponse<T>( List<T> items, int page, int size, long totalItems, int totalPages ) { public PageResponse { items = List.copyOf(items); if (size <= 0 || size > 100) throw new IllegalArgumentException("Invalid page size"); } }
package com.seb4u.demo.spring.web; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/payments") public class PaymentController { @PostMapping String pay(@RequestHeader("Idempotency-Key") String idempotencyKey, @RequestBody PaymentRequest request) { return "accepted:" + idempotencyKey; } record PaymentRequest(String orderId, long amountCents) {} }
S. Microservices, Messaging & Cloud
Verteilte Systeme bewusst entwerfen: Idempotenz, Konsistenz, Messaging, Retries und Observability.
Master-Regeln
- Microservices sind organisatorische und technische Grenzen, nicht nur kleine REST-Apps.
- Netzwerk ist unzuverlässig: Timeouts, Retries, Circuit Breaker und Idempotenz einplanen.
- Events sind Verträge; Versionierung und Kompatibilität sind Pflicht.
- Verteilte Transaktionen vermeiden; Saga/Outbox/Inbox einsetzen.
- Jeder Request braucht Correlation ID über Servicegrenzen hinweg.
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| REST | Synchrone Abfrage/Command | Direktes Feedback, gekoppelte Verfügbarkeit. |
| Messaging | Asynchrone Ereignisse | Entkopplung, Eventual Consistency. |
| Outbox | DB und Event sicher koppeln | Producer-Seite. |
| Inbox | Deduplizierung eingehender Events | Consumer-Seite. |
| Saga | Langlaufender Workflow | Kompensationen statt 2PC. |
Typische Fallen
- Retry Storm ohne Backoff.
- Events mit internen DB-Strukturen publizieren.
- Keine Idempotency Keys bei Zahlungs-/Bestelloperationen.
Copy-Paste Snippets
package com.seb4u.demo.cloud; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public final class IdempotentConsumer { private final Set<String> processed = ConcurrentHashMap.newKeySet(); public void handle(String eventId, Runnable action) { if (!processed.add(eventId)) { return; // bereits verarbeitet } action.run(); } }
services: commerceflow-api: build: context: . dockerfile: Dockerfile.spring ports: - "8080:8080" environment: SPRING_PROFILES_ACTIVE: docker DB_HOST: postgres depends_on: - postgres postgres: image: postgres:16 environment: POSTGRES_DB: commerceflow POSTGRES_USER: app POSTGRES_PASSWORD: app
T. Algorithmen & Datenstrukturen in Java
Interview- und Praxisalgorithmen schnell erkennen, korrekt implementieren und Big-O sauber begründen.
Master-Regeln
- Erst Problemklasse erkennen: Lookup, Sortierung, Graph, Fenster, DP, Backtracking.
- Big-O immer für Zeit und Speicher angeben.
- HashMap ist häufig der Schritt von O(n²) zu O(n).
- PriorityQueue für Top-K und Scheduling verwenden.
- Bei Graphen visited-Set und Zyklusfälle früh klären.
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| Two Pointers | Sortiertes Array/Palindrome/Paare | O(n), wenig Speicher. |
| Sliding Window | Subarray mit Fensterbedingung | O(n). |
| BFS | Kürzeste Wege in ungewichteten Graphen | Queue. |
| DFS | Komponenten, Topologie, Backtracking | Stack/Rekursion. |
| Dijkstra | Nichtnegative gewichtete Kanten | PriorityQueue. |
| Union-Find | Dynamische Komponenten | Fast O(1) amortisiert. |
Typische Fallen
- Rekursion ohne Abbruchbedingung.
- Integer Overflow bei Comparator a-b.
- Visited zu spät setzen kann Queue aufblasen.
Copy-Paste Snippets
package com.seb4u.demo.algorithms; public final class LongestOnesAfterFlip { public int longest(int[] bits, int maxZeros) { int left = 0, zeros = 0, best = 0; for (int right = 0; right < bits.length; right++) { if (bits[right] == 0) zeros++; while (zeros > maxZeros) { if (bits[left++] == 0) zeros--; } best = Math.max(best, right - left + 1); } return best; } }
package com.seb4u.demo.algorithms; import java.util.*; public final class Dijkstra { record Edge(String to, int weight) {} record Node(String id, int distance) {} public Map<String, Integer> shortest(Map<String, List<Edge>> graph, String start) { Map<String, Integer> dist = new HashMap<>(); PriorityQueue<Node> pq = new PriorityQueue<>(Comparator.comparingInt(Node::distance)); dist.put(start, 0); pq.add(new Node(start, 0)); while (!pq.isEmpty()) { Node current = pq.poll(); if (current.distance() != dist.get(current.id())) continue; for (Edge edge : graph.getOrDefault(current.id(), List.of())) { int next = current.distance() + edge.weight(); if (next < dist.getOrDefault(edge.to(), Integer.MAX_VALUE)) { dist.put(edge.to(), next); pq.add(new Node(edge.to(), next)); } } } return dist; } }
U. Code Review, Interview & Produktions-Checklisten
Schnell erkennen, ob Code produktionsreif, wartbar und interviewfest ist.
Master-Regeln
- Jede öffentliche API braucht klare Namen, Grenzen und Fehlerverhalten.
- Jede externe Abhängigkeit braucht Timeout, Retry-Entscheidung und Logging-Kontext.
- Jede Collection-Wahl soll begründbar sein.
- Jede Nebenläufigkeit braucht Ownership-Regel und Shutdown-Pfad.
- Jede Security-relevante Operation braucht Negativtests.
Entscheidungsmatrix
| Option | Wann? | Merksatz |
|---|---|---|
| Code Review Core | Lesbarkeit, Invarianten, Datenstrukturen | Kleine Methoden, gute Namen. |
| Code Review Runtime | I/O, Threads, Ressourcen | Leaks, Timeouts, Shutdown. |
| Code Review API | DTOs, Statuscodes, Fehler | Stabile Verträge. |
| Code Review Production | Logs, Metrics, Config | Betreibbarkeit. |
Schnellcheck / Befehle
Copy-Paste Snippets
Code Review Checklist - [ ] Package Standard: com.seb4u.demo..., spring oder jakarta korrekt? - [ ] Fachlogik frei von Frameworks? - [ ] I/O: try-with-resources, sichere Paths, explizites Encoding? - [ ] Concurrency: Timeout, Cancellation, Shutdown, Backpressure? - [ ] API: DTOs, Validation, Fehlercodes, keine Stacktraces? - [ ] Tests: wichtigste Fachregel + Fehlerfall + Grenze? - [ ] Observability: Korrelation, strukturierte Logs, keine Secrets?
1. Problemgrenze klären: Input, Output, Constraints, Fehlerfälle. 2. Einfachste korrekte Lösung erklären. 3. Datenstruktur begründen und Big-O nennen. 4. Edge Cases nennen: null/leer/groß/duplikate/concurrency. 5. Testfälle skizzieren. 6. Produktionsaspekte ergänzen: Logging, Timeout, Security, Monitoring.