Java Fortgeschrittenen Schulung – Kursbezug
Ziel dieses Kapitels: Sie modellieren Fehler sauber, loggen verständlich (ohne Spam) und bauen einen JUnit-Check, der Regressionen verhindert.
Worum geht’s?
- Exceptions: passende Typen, klare Messages, Ursachen (cause) weiterreichen.
- Logging: Level (INFO/WARN/SEVERE), Kontext in Messages, kein
printStackTrace()im Produktivcode. - Unit Tests (JUnit 5): Arrange–Act–Assert (AAA), Assertions, Parametrized Tests.
Lehr-/Lernziele
- (LZ1) Exceptions so entwerfen, dass sie verständlich und nützlich sind (Message + Cause + passende Typen).
- (LZ2) Logging so einsetzen, dass Fehler analysierbar sind (Level, Kontext) – ohne Log-Spam.
- (LZ3) JUnit-Tests schreiben, die Verhalten zuverlässig prüfen (AAA,
assertThrows, Parametrized Tests).
Exceptions: sauber modellieren
public class BuchungException extends RuntimeException {
public BuchungException(String message) { super(message); }
public BuchungException(String message, Throwable cause) { super(message, cause); }
}
public class BuchungService {
public void bucheTeilnahme(String teilnehmerId) {
if (teilnehmerId == null || teilnehmerId.isBlank()) {
throw new BuchungException("teilnehmerId fehlt");
}
// ... Buchungslogik
}
}Logging: Level + Kontext
import java.util.logging.*;
public class BuchungService {
private static final Logger LOG = Logger.getLogger(BuchungService.class.getName());
public void bucheTeilnahme(String teilnehmerId, String kursTitel) {
LOG.info(() -> "Start Buchung | teilnehmerId=" + teilnehmerId + " | kurs=" + kursTitel);
try {
if (teilnehmerId == null || teilnehmerId.isBlank()) {
throw new IllegalArgumentException("teilnehmerId fehlt");
}
if (kursTitel == null || kursTitel.isBlank()) {
throw new IllegalArgumentException("kursTitel fehlt");
}
// ... business logic (z.B. Platz reservieren)
LOG.info(() -> "Buchung ok | teilnehmerId=" + teilnehmerId + " | kurs=" + kursTitel);
} catch (RuntimeException ex) {
LOG.log(Level.SEVERE,
"Buchung fehlgeschlagen | teilnehmerId=" + teilnehmerId + " | kurs=" + kursTitel,
ex
);
throw ex;
}
}
}Unit Testing (JUnit 5): AAA + assertThrows
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
class BuchungServiceTest {
@Test
void bucheTeilnahme_wirftFehler_wennTeilnehmerIdFehlt() {
// Arrange
BuchungService svc = new BuchungService();
// Act + Assert
assertThrows(IllegalArgumentException.class,
() -> svc.bucheTeilnahme(" ", "Java Fortgeschritten")
);
}
}Parametrized Tests: mehrere Inputs, ein Test
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.*;
class ValidierungTest {
@ParameterizedTest
@ValueSource(strings = {"", " ", "\t"})
void teilnehmerId_ungueltig(String bad) {
BuchungService svc = new BuchungService();
assertThrows(IllegalArgumentException.class,
() -> svc.bucheTeilnahme(bad, "Java Fortgeschritten")
);
}
}Praxisaufgabe (Mini)
- Bauen Sie
PreisRechnermitdouble endpreis(double basispreis, double rabatt). - Wenn
basispreis < 0oderrabattaußerhalb0..1liegt:IllegalArgumentExceptionmit sinnvoller Message werfen. - Loggen Sie im Fehlerfall eine SEVERE-Zeile mit Kontext (
basispreis/rabatt) + Throwable. - Schreiben Sie 2 JUnit-Tests: 1x „Happy Path“, 1x
assertThrows.
Lösung anzeigen
import java.util.logging.*;
public class PreisRechner {
private static final Logger LOG = Logger.getLogger(PreisRechner.class.getName());
public double endpreis(double basispreis, double rabatt) {
try {
if (basispreis < 0) throw new IllegalArgumentException("basispreis muss >= 0 sein");
if (rabatt < 0 || rabatt > 1) throw new IllegalArgumentException("rabatt muss in 0..1 liegen");
return basispreis * (1 - rabatt);
} catch (RuntimeException ex) {
LOG.log(Level.SEVERE,
"endpreis fehlgeschlagen | basispreis=" + basispreis + " | rabatt=" + rabatt,
ex
);
throw ex;
}
}
}import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
class PreisRechnerTest {
@Test
void endpreis_ok() {
PreisRechner r = new PreisRechner();
assertEquals(90.0, r.endpreis(100.0, 0.10), 0.0001);
}
@Test
void endpreis_wirftFehler_wennRabattUngueltig() {
PreisRechner r = new PreisRechner();
assertThrows(IllegalArgumentException.class, () -> r.endpreis(100.0, 1.5));
}
}Optional: typische Stolperfallen
- Zu generische Exceptions: lieber konkret (IllegalArgumentException, Domain-Exception).
- printStackTrace() in Produktion: ersetzt durch Logging (mit Throwable).
- Tests prüfen Interna: lieber Ergebnis/Fehlerverhalten prüfen.
Kurz-Takeaways
- Exceptions: klarer Typ + klare Message + ggf. Cause.
- Logging: Level + Kontext + Throwable, ohne Spam.
- JUnit: AAA,
assertThrows, Parametrisierung für Varianten.