Java SE 8 Programmer II: java.io.*

Zur Dateneingabe wird die Klasse java.util.Scanner verwendet, die AutoClosable ist.

Zudem gibt es die Klasse java.io.Console, die je nach JVM und Art und Weise des Starts der JVM zur Laufzeit verfügbar ist. Wenn die JVM aus einer interaktiven Konsole gestartet wird, existiert auch eine java.io.Console. Wenn der Aufruf der JVM durch einen Job oder aus einer IDE heraus erfolgt ist, dann existiert keine java.io.Console.

Standard-Streams sind Byte-basiert, während die Console Character-basiert arbeitet.

System.out und System.err sind vom Typ PrintStream. System.in ist vom Typ InputStream.

Anders als die meisten Klassen aus java.io werfen Methoden von PrintWriter nie IOExceptions.
Bei PrintWriter lässt sich im Konstruktor AutoFlushing aktivieren. Dies gilt allerdings nur für die Methoden println(), printf() und format().

Java SE 8 Programmer II: Date and Time

Das Handling von Datum und Zeit wurden über die gut 20 Jahre, die es Java nun schon gibt, mehrfach komplett überarbeitet. Da all die alten Ansätze aus Kompatibilitätsgründen noch im JDK enthalten sind, lässt sich der Prozess gut nachvollziehen.

Datum und Zeit werden unter Java 8 nach ISO-8601 abgebildet. Die allermeisten Klassen im Date-Time API sind unveränderbar dank dem Einsatz von Factory-Methoden statt Konstruktoren.
Die Nameskonventionen für Methoden sind wie folgt:

Bildschirmfoto 2018-01-26 um 06.24.56.png

In den Enums java.time.temporal.ChronoField und java.time.temporal.ChronoUnit (Implementation von java.time.temporal.TemporalUnit) sind die Konstanten abgelegt, die im Date-Time API verwendet werden.
LocalDate und LocalTime lassen sich zu LocalDateTime kombinieren und wieder auseinandernehmen.

Zeitpunkte werden durch java.time.Instant abgebildet. Dabei ist zu beachten, dass Veränderungen von Zeitpunkten mittels with() nur für Sekunden, bzw. Bruchteile von Sekunden möglich sind. Bei anderen Zeiteinheiten wird eine DateTimeException ausgelöst.

Differenzen zwischen Daten und Uhrzeiten werden mittels java.time.Period bzw. java.time.Duration abgebildet.

Für die Konversion zwischen einem Zeitpunkt und einer LocalDateTime wird java.time.ZoneId verwendet. ZoneIds können entweder als "UTC-02:00" (alternativ "GMT..." oder "UT...") oder "Europe/Zurich" angegeben werden. Die Klasse ZoneOffset (Subklasse von ZoneId) bildet die Zeitdifferenz to UTC ab. Beim Initialisieren eines ZoneOffset muss das erste Zeichen des Strings '+' oder '-' sein. Wenn der ZoneOffset '0' ist, muss 'Z'. angegeben werden.

ZonedDateTime bildet die Kombination von LocalDateTime und einer ZoneId ab. Achtung: an Orten, wo die Zeitumstellung gilt, ändert die Uhrzeit dadurch zwei Mal pro Jahr. Dies ist bei der Zertifikatsprüfung zu beachten. Zu beachtendes Detail: wird zu einer ZonedDateTime eine Period von einem Tag addiert, ändert sich die Uhrzeit auch bei Umstellung auf Sommerzeit nicht. Wenn eine Duration von einem Tag addiert wird, ändert die Uhrzeit um die verlorene Stunde.

java.time.OffsetTime bildet eine Zeitdifferenz zu ITC in Nanosekunden ab. Bei der Klasse java.time.OffsetDateTime kommen noch Datumsinformationen hinzu.

Zum Parsing und zur Formatierung von Datum und Zeit wird die Klasse java.time.format.DateTimeFormatter verwendet.

Java SE 8 Programmer II: Lambdas

Um Lambdas zu verstehen, muss zuerst der Begriff des 'Funktionalen Interfaces' geklärt werden.
Ein funktionales Interface hat nur eine abstrakte Methode. Methoden, die die Signatur der Methoden aus java.lang.Object entsprechen, verhindern genau so wenig, wie eine beliebige Anzahl von 'default' oder 'static' Methoden, dass ein Interface funktional ist.

Mit der Annotation @FunctionalInterface kann ein Interface entsprechend deklariert werden. Das macht ein Interface nicht zu einem funktionalen Interface, aber der Compiler zeigt Verletzungen der Regeln an.

 

Java SE 8 Programmer II: Streams

Es gibt eine gute Zusammenfassung über Stream im Java Study Guide im Kapitel 12: Streams.
Ich möchte nicht die ganze Seite übersetzen, sondern nur die wichtigsten Punkte zusammenfassen.

Streams sind keine Collections. Sie sind unveränderbar, speichern die Elemente nicht und arbeiten lazy (wenn möglich). Streams können aus Collections, Files oder 'von Hand' erzeugt werden. Die Verarbeitung von Streams kann sequentiell oder parallel erfolgen. 

Es gibt grundsätzlich zwei Arten von Methoden, die auf Streams ausgeführt werden können: Zwischenschritte (intermediate) und abschliessende Schritte (terminal).
Während die Zwischenschritte jeweils einen Stream erzeugen, führen die abschliessenden Schritte dazu, dass sämtliche Zwischenschritte ausgeführt werden und ein Resultat erzeugt wird bzw. etwas mit dem Resultat des letzten Zwischenschritts geschieht.
Nachdem eine abschliessenden Methode auf einem Stream aufgerufen wurde, kann keine Methode auf diesem Stream mehr ausgeführt werden. Wenn dies doch versucht wird, wird eine RuntimeException ausgelöst.

Im Java Study Guide Kapitel 12 sind diese Operationen detailliert aufgelistet und erklärt.

Ein wichtiges Thema im Zusammenhang mit Streams sind Lambdas, die in einem separatem Post beschrieben werden. In Zusammenarbeit von Streams mit Lambdas beschreibt man was man tun will und nicht wie.

Wenn der Task dafür geeignet ist, kann mittels parallelStream() die Bearbeitung beschleunigt werden.
Mittels reduce(...) und collect(...) können Einträge in Streams zusammengefasst oder zusammenkopiert werden.
Der Methode reduce(...) können ein BinaryOperator zur Akkumulation und optional ein Returntyp und ein BinaryOperator als Combiner-Funktion übergeben werden.
Der Methode collect(...) können Supplier, ein BiConsumer als Akkumulator und ein BiConsumer als combiner mitgegeben werden. Diese drei Funktionalitäten können auch in einem Collector zusammengefasst werden.

  • Supplier: .get()
  • Predicate: .test(...) -> boolean, .negate(), .and(Predicate)
  • BiConsumer: .accept(T, U), .andThen(BiConsumer) -> BiConsumer
  • Comparator<T>: .compare(T, T), .comparing(Function)
  • Function<T, R>: .apply(T) -> R, andThen(Function <? super R, ? extends V> after)
  • BinaryOperator: .maxBy(Comparator), .minBy(Comparator)

Selenium

Es gibt verschiedene Test-Technologien, die auf unterschiedlichen Ebenen einer Applikation zum Einsatz kommen. Während JUnit, TestNG und Mockito üblicherweise nicht sichtbare und meistens relativ kleine Teile der Applikation testen, braucht es auch Testframeworks für umfassende Tests, wie Integrationtests und User Acceptance Tests (UATs). Hier bieten sich Arquillian und Selenium an. Während Arquillian ziemlich schwergewichtig unterwegs ist, ist Selenium das Tool der Wahl, wenn es um's automatisierte Testen von WebGUIs geht.

Selenium-Tests können auf zwei Arten erstellt werden:
Zum Einen gibt es die Möglichkeit mit einem der verfügbaren Firefox-Plugins (z.B. "Selenium Builder") Tests zu erstellen. Dabei startet man seine zu testende Web-Applikation und startet das Plugin, welches nun die Interaktionen mit der Web-Applikation im JSON-Format aufzeichnet. Ein so aufgezeichneter Test lässt sich über den Selenium Builder laden und starten. Für eine Integration in einen nightly build oder sonstige automatisierte Tests ist dies kein gangbarer Weg. Ausserdem wird es in komplexen Fällen schwierig das generierte JSON-File zu bewirtschaften.
Der andere Weg ist der programmatische Ansatz. Hierbei werden JUnit bzw TestNG-ähnliche Tests geschrieben, die sich problemlos in Jenkins bzw. Hudson im Rahmen eines nightly builds aufrufen lassen. Diese Variante ist generell vorzuziehen, da oft eine Automatisierung der Tests gefordert ist.
 

Java Zertifikatsvorbereitung

Während der tagtäglichen Arbeit geht es gerne vergessen, dass eine Zertifizierung ein wichtiger Aspekt ist. Die Standard-Klassenbibliothek ist so gross und mächtig, dass eine Vorbereitung auf eine Zertifikatsprüfung einem viele Funktionalitäten wieder in Erinnerung ruft.
Ausserdem ist so eine Zertifizierung ja auch ein Leistungsausweis, der einem hilft sich bei einer beruflichen Neuausrichtung zu positionieren.
Sehr gute Lernunterstützung findest du hier.

Die Zertifizierungen haben mit der Übernahme von SUN durch Oracle ein grösseres Gewicht bekommen. Auch wurden die Zertifizierungen und die Upgrade-Pfade anders gestaffelt.

Wer ältere SCJP-Zertifakte hat, muss zuerst ein Einsteiger-Zertifikat machen (1ZO-808), die ziemlich trivial ist. Dies wird benötigt um das SCJP Äquivalent ‚Java SE 8 Programmer II‘ (1Z0-809) in Angriff nehmen zu können. Viele Themen sind altbekannt. Dinge, wie Streams, Lambdas und das neue Date-/Time-Framework benötigen Vorbereitung. Auch wenn man diese Themen schon bei der täglichen Praxis eingesetzt hat, lohnt sich ein Blick in die Spez. Verschiedene Aspekte dieser Frameworks setzt man im täglichen Bedarf nur sehr selten ein.

Unter 'Review Exam Topics' findet man die Liste der für die Programmer II Zertifizierung relevanten Themen. In einzelnen Posts werde ich die, für mich interessanten, Themen dokumentieren.