Kompakte Strings nun in Java 9 (JEP 254)

Wenn ein String nur aus Latin-1 Buchstaben besteht, dann kann die Zeichenfolge aus einem byte- statt einem char-Feld bestehen. Diese Änderung wurde in Java 9 nun umgesetzt: http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/4f6e52f9dc79. Gerade bei IDs lässt sich so 1/2 vom Speicher sparen. Und da Strings immutable sind, muss man nicht konvertieren.

Teurer wird es beim „Mischen“ wie:

String /*intern char[]*/ s  =  "λφα";
String /*intern char[]*/ t  =  " " /*intern byte[]*/
                                +
                                s  /*intern char[]*/;

Intern wird hier eine inflate()-Methoden aufgerufen:

If the coder is "isLatin1", this inflates the internal 8-bit storage to 16-bit <hi=0, low> pair storage.

Inselraus: Swings JLayeredPane

Die JLayeredPane nimmt JComponent-Objekte auf und stellt sie in einer geschichteten Reihenfolge (auch Z-Order genannt) dar. Die Layered Pane besteht selbst wieder aus zwei Objekten, einer Menüzeile und der Inhaltsfläche Content-Pane. Container vom Typ JLayeredPane platzieren ihre Kinder in Ebenen (engl. layers). Jedem Kind wird eine Ebene zugewiesen, und beim Zeichnen werden die Kinder von unten nach oben gezeichnet. Damit werden die Komponenten, die unter anderen Komponenten liegen, unter Umständen verdeckt. Standardmäßig hat die JLayeredPane keinen zugewiesenen Layoutmanager, und Objekte müssen mit setBounds(…) positioniert werden.

Wird ein JLayeredPane-Container verwendet, ist die add(…)-Methode so implementiert, dass sie die Komponenten auf einer Standardebene (JLayeredPane.DEFAULT_LAYER) platziert. Um Komponenten auf eine eigene Ebene zu setzen, sodass sie vor oder hinter anderen Komponenten liegen, wird ihnen eine eigene Ebene zugewiesen, und zwar mit einem Wert relativ zu DEFAULT_LAYER. Kleinere Werte bedeuten, dass die Komponenten unten liegen, und hohe bedeuten, dass sie oben liegen. Ein Beispiel:

layeredPane.add( component, new Integer(5000) );

Für einige Ebenen sind Werte als Konstanten deklariert. Dazu zählen zum Beispiel JLayered-Pane.DEFAULT_LAYER (0), PALETTE_LAYER (100), MODAL_LAYER (200), POPUP_LAYER (300) und DRAG_LAYER (400).

Neben der Möglichkeit, die Ebenen festzulegen, lässt sich die Reihenfolge innerhalb der Ebene später durch die Methode moveToFront() oder moveToBack() verändern.

Inselraus: Swings JRootPane und JDesktopPane

Unter den JComponent-Objekten gibt es einige ausgezeichnete, die als Container für andere Kinder fungieren.

Wurzelkomponente der Top-Level-Komponenten (JRootPane)

Die Komponenten JFrame, JDialog, JWindow, JApplet und JInternalFrame enthalten als einziges Kind den leichtgewichtigen Container JRootPane. Die Methode getRootPane() liefert dieses JRootPane-Objekt. Die JRootPane verwaltet eine Layered Pane, die wiederum Content-Pane und Menü aufnimmt, und eine Glass-Pane, die wie eine Glasscheibe über allen anderen Komponenten liegt. Sie kann Ereignisse abfangen oder in einer paint(…)-Methode etwas über alle Komponenten zeichnen.

Beispiel: Weise der Glass-Pane einen wartenden Cursor zu:

Component c = getRootPane().getGlassPane();
 if( c != null )
   c.setCursor( Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR ) );

 JDesktopPane und die Kinder von JInternalFrame

Die JDesktopPane ist eine Unterklasse von JLayeredPane und als Container für interne Fenster – also Objekte vom Typ JInternalFrame – gedacht. Mit internen Fenstern (engl. internal frames) lassen sich MDI-Applikationen implementieren, also GUI-Anwendungen, bei denen nicht das grafische Betriebssystem die Fenster verwaltet, sondern die eigene Anwendung.

Bevor ein JInternalFrame sichtbar wird, muss der Container erzeugt und sichtbar gemacht werden:

JDesktopPane desktop = new JDesktopPane();
 container.add( desktop );

Jetzt können beliebig viele JInternalFrame-Objekte erzeugt und auf der JDesktopPane platziert werden. Der einfachste Konstruktor ist der Standard-Konstruktor, der einen nicht vergrößerbaren, nicht schließbaren, nicht maximierbaren und nicht zum Icon verkleinerbaren JInternal-Frame ohne Titel erzeugt. Der ausführlichste Konstruktor erlaubt eine genaue Parametrisierung:

JInternalFrame iframe = new JInternalFrame( title, resizable, closeable,
   maximizable, iconifiable );

Zwar gibt es nun ein Exemplar, doch wäre es nach dem Aufsetzen auf den Container noch nicht sichtbar:

iframe.setVisible( true );

Bis zur Vollständigkeit fehlen aber noch die Maße:

iframe.setSize( /* width = */ 200, /* height = */ 100 );

Nun kann der iframe dem Container hinzugefügt werden:

desktop.add( iframe );

In einem kompletten Programm kann das so aussehen:

package com.tutego.insel.ui.swing;
 
 import javax.swing.*;
 import static java.lang.Math.random;
 
 public class JInternalFrameDemo {
 
   static void addInternalToDesktop( JDesktopPane desktop )
   {
     JInternalFrame iframe;
     iframe = new JInternalFrame( "Ein internes Fenster",  // title
                                  true,                    // resizable
                                  true,                    // closeable
                                  true,                    // maximizable
                                  true );                  // iconifiable
 
     iframe.setBounds( (int)(random() * 100), (int)(random() * 100),
                       100 + (int)(random() * 400), 100 + (int)(random() * 300) );
     iframe.add( new JScrollPane(new JTextArea()) );
     iframe.setVisible( true );
 
     desktop.add( iframe );
   }
 
   public static void main( String[] args ) {
     JFrame f = new JFrame();
     f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
     JDesktopPane desktop = new JDesktopPane();
     f.add( desktop );
     f.setSize( 500, 400 );
     addInternalToDesktop( desktop );
     addInternalToDesktop( desktop );
     addInternalToDesktop( desktop );
     f.setVisible( true );
   }
 }

Hinweis: Die Schnittstelle von JInternalFrame erinnert an JFrame, doch ist die Ereignisbehandlung anders. So besitzt JInternalFrame eine Methode addInternalFrameListener(…) an Stelle von addWindowListener(…). Ein JInternalFrame empfängt keine WindowEvents, daher darf es addWindowListener(…) – wie es JFrame von java.awt.Window erbt – auch nicht besitzen.

Inselraus: Swings Farbauswahldialog JColorChooser

Mit einem JColorChooser lassen sich Farben über drei unterschiedliche Reiter auswählen. Der Benutzer hat die Auswahl zwischen vordefinierten Farben, HSB-Werten und RGB-Werten. Um den Farbauswahldialog auf den Bildschirm zu bekommen, genügt ein Aufruf von JColorChooser.showDialog(Component, String, Color) mit drei Argumenten: einem Component-Objekt (dem Vater des Dialogs), dem Titel und einer Anfangsfarbe. Beendet der Benutzer den Dialog, wird als Rückgabewert die ausgewählte Farbe geliefert. Wird der Dialog abgebrochen, so ist der Rückgabewert null:

JFrame f = new JFrame();
 f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
 JButton b = new JButton( "Farbe ändern" );
 f.add( b );
 b.addActionListener( new ActionListener() {
   @Override public void actionPerformed( ActionEvent e ) {
    Component comp = (Component) e.getSource();
    Color newColor = JColorChooser.showDialog( null,
                                               "Wähle neue Farbe",
                                               comp.getBackground() );
    comp.setBackground( newColor );
  }
 } );
 f.pack();
 f.setVisible( true );

Den Aufruf mit showDialog(…) einzuleiten, ist nicht der einzige Weg. Wir können auch den Konstruktor nutzen und dieses Exemplar später mit JColorChooser.createDialog(…) übergeben und anzeigen.

class javax.swing.JColorChooser
extends JComponent implements Accessible

  • JColorChooser()
    Erzeugt einen neuen Farbauswahldialog.
  • JColorChooser(Colorc)
    Erzeugt einen neuen Farbauswahldialog mit einer vordefinierten Farbe.
  • staticColorshowDialog(Componentc,Stringtitle,ColorinitialColor)
    Zeigt einen modalen Farbauswahldialog.
  • staticJDialogcreateDialog(Componentc,Stringtitle,booleanmodal,
    JColorChooser chooserPane, ActionListener okLis, ActionListener cancelLis)
    Erzeugt einen neuen Dialog aufgrund des JColorChooser-Objekts mit Standardschaltflächen zum Bestätigen und Abbrechen.

JColorChooser-Objekte als spezielle Komponenten

Neben der statischen Methode showDialog(…) lässt sich auch der Konstruktor nutzen, um ein JColorChooser als spezielles JComponent-Objekt aufzubauen. Das bringt den Vorteil mit sich, dass die Farbauswahl nicht zwingend in einem eigenständigen Dialog stattfinden muss, sondern dass im Fall einer Komponente diese zusammen mit anderen Komponenten auf einen Container gesetzt werden kann. Änderungen an der Auswahl registriert ein ChangeListener, der etwa so angewendet wird:

chooser.getSelectionModel().addChangeListener( new ChangeListener() {
   @Override public void stateChanged( ChangeEvent e ) {
     Color c = ((ColorSelectionModel) e.getSource()).getSelectedColor();
   }
 } );

Weitere Beispiele finden sich unter http://www.java2s.com/Code/Java/Swing-JFC/Color-Chooser.htm. Wie ein neuer Reiter mit über 50 Schattierungen in der Graustufenanzeige eingebracht wird, zeigt etwa http://www.java2s.com/Code/Java/Swing-JFC/JColorChooserdialogwiththecustomGrayScalePanelpickertab.htm.

Inselraus: Die Windows-Optik von Swing mit JGoodies Looks verbessern

Zwar bemüht sich das JDK bestmöglich das Windows-Look-and-Feel zu emulieren, doch das gelingt nicht an allen Stellen. Selbst bei dem aktuellen JDK ist das noch nicht perfekt, wobei die Implementierung unter Windows auf den nativen Windows-Renderer zurückgreift. Und kleine Verbesserungen lassen sich auch nicht so einfach publizieren, da die Release-Zyklen vom JDK lang sind. In diesem Fall hilft JGoodies Looks (http://www.jgoodies.com/freeware/libraries/looks/), ein freies Look-and-Feel unter der BSD-Lizenz. Das Ziel von Looks ist die perfekte Nachbildung des Aussehens für alle Windows-Versionen. Gegenüber dem Windows-Look-and-Feel vom JDK korrigiert es viele Feinheiten, wie passende Insets bei Eingabefeldern, Icons, Farben, Rahmen, den richtigen Font, Menüeigenschaften, Auflösungen von 96 und 120 dpi und vieles mehr. Gesetzt wird Looks wie jedes andere LAF:

try {
   String laf = Options.getSystemLookAndFeelClassName();
   UIManager.setLookAndFeel( laf );
 } catch ( Exception e ) {
   System.err.println( "L&F lässt sich nicht initialisieren: " + e );
 }

Inselraus: Undo unter Swing durchführen

Gute Benutzerschnittstellen zeichnen sich dadurch aus, dass dem Benutzer Fehler unterlaufen dürfen. Die Änderungen müssen jedoch wieder zurückgenommen werden können. Um dies in Java zu realisieren, gibt es Unterstützung durch ein Paket javax.swing.undo. Mit ihm lassen sich Undo- und Redo-Operationen mit relativ wenig Aufwand realisieren. Unser Beispiel soll ein Textfeld zeigen, dessen Änderungen auf Knopfdruck rückgängig gemacht werden. Zentrales Objekt ist dabei ein UndoManager. Dieser sammelt einzelne Aktionen, im Fall von Benutzereingaben jedes Zeichen. Die Anzahl der zu speichernden Aktionen ist beschränkt, lässt sich aber anpassen. Wir wollen in einem Beispiel ein JTextField mit einem Standardtext erzeugen. Anschließend wird ein UndoManager mit dem Document-Objekt des Textfeldes verbunden. Das Document informiert den UndoManager über Änderungen, der UndoManager speichert diese. Wenn eine Schaltfläche aktiviert ist, wird dem UndoManager befohlen, die Aktion rückgängig zu machen. Sichtbar wird dann wieder der Text, so wie er am Anfang in der Textbox stand:

package com.tutego.insel.ui.undo;
 
 import java.awt.BorderLayout;
 import java.awt.event.*;
 import javax.swing.*;
 import javax.swing.event.UndoableEditEvent;
 import javax.swing.undo.*;
 
 public class IComeUndone {
   public static void main( String[] args ) {
     JFrame f = new JFrame();
     f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
 
     final JTextArea textarea = new JTextArea( 20, 40 );
     textarea.setText( "Hier zurück" );
     f.add( new JScrollPane(textarea) );
 
     //    final UndoManager undomanager = new MyUndoManager();
     final UndoManager undomanager = new UndoManager();
     textarea.getDocument().addUndoableEditListener( undomanager );
     undomanager.setLimit( 1000 );
 
     JButton undoB = new JButton( "Undo" );
     undoB.addActionListener( new ActionListener() {
       @Override public void actionPerformed( ActionEvent e ) {
         undomanager.end();
 
         if ( undomanager.canUndo() )
           undomanager.undo();
         textarea.requestFocus();
       }
     } );
 
     f.add( undoB, BorderLayout.PAGE_END );
     f.pack();
     f.setVisible( true );
   }
 }

Um genauer zu sehen, was das UndoManager-Objekt rückgängig macht, schreiben wir eine Unterklasse von UndoManager und überschreiben die Methode undoableEditHappened(UndoableEdit-Event) (diese Methode implementiert UndoManager von der Schnittstelle UndoableEditListener). Geben wir in unserer Realisierung auf dem Bildschirm aus, was bei jeder Aktion in den UndoManager kommt:

class MyUndoManager extends UndoManager {
   @Override
   public void undoableEditHappened( UndoableEditEvent e ) {
     UndoableEdit ue = e.getEdit();
     System.out.println( ue );
     addEdit( ue );
   }
 }

Die Methode undoableEditHappened(UndoableEditEvent) bekommt ein Ereignisobjekt, in dem der Verweis auf eine zurücknehmbare Operation abgelegt ist. An diese kommen wir mit get-Edit(). Die Rückgabe ist ein UndoableEdit-Objekt, also genau eine Operation, die zu einem Undo und Redo fähig ist. Der UndoManager speichert diese mit dem Aufruf von addEdit() in einer Datenstruktur ab. Wenn wir das UndoableEdit-Objekt auf dem Bildschirm ausgeben, sehen wir unsere durchgeführte Operation, zum Beispiel bei einem Einfügen:

[javax.swing.text.GapContent$InsertUndo@497934 hasBeenDone: true alive: true]

Beim Löschen erkennen wir ein:

[javax.swing.text.GapContent$RemoveUndo@ca470 hasBeenDone: true alive: true]

Inselraus: Die Zusatzkomponentenbibliothek SwingX

Für ernsthafte grafische Oberflächen mit Swing sind die Standardkomponenten der Java SE etwas mau. Wichtige Komponenten wie ein Datumsauswahldialog fehlen, zum Teil sind die angebotenen Komponenten nicht leistungsfähig genug. So kann beispielsweise die Swing-Textkomponente zwar einfaches HTML darstellen, scheitert aber an komplexeren Dokumenten mit CSS. Da die Java Foundation Classes in den letzten Jahren sehr stabil geblieben sind – es gab keine großen Änderungen oder nennenswerte neue Komponenten –, bleibt Entwicklern nichts anderes übrig, als entweder

  • die benötigten Swing-Komponenten selbst zu entwickeln oder
  • sich im Open-Source-Umfeld umzuschauen bzw.
  • eine kommerzielle Swing-Komponentenbibliothek einzukaufen.

Wie üblich ist die Eigenentwicklung immer nur die letzte Option. Im Laufe der letzten Jahre haben sich einige sehr gute quelloffene Swing-Zusätze etabliert. Hervorzuheben ist SwingX, ein Projekt, von dem lange Zeit gedacht wurde, dass die Komponenten in das javax.swing-Paket aufgenommen würden. Das ist aber bisher nicht passiert, nur eine Komponente hat es – wen auch abgewandelt – in die Standardbibliothek geschafft.

Hinweis: Aktiv wird SwingX nicht mehr weiterentwickelt.

Im Angebot: Erweiterte und neue Swing-Komponenten

Die SwingX-Bibliothek erweitert Swing auf zwei Arten:

  • Einige SwingX-Komponenten erweitern die Standard-Swing-Komponenten und fügen Funktionalität hinzu. So ist JXFrame eine Unterklasse von JFrame und kann zum Beispiel eine Statuszeile haben. Oder JXTable ist eine Unterklasse von JTable mit zusätzlicher Suchfunktionalität und dem Vorteil, dass sich Spalten automatisch ein-/ausblenden lassen.
  • SwingX definiert ganz neue Swing-Komponenten, etwa eine Mischung aus Tabelle und Baum (JXTreeTable), ausfaltbare Container oder einen Login-Dialog.

Um einen ersten Überblick über SwingX zu bekommen, lohnt es sich, einen Blick auf das SwingX-Demo zu werfen. Auf der SwingX-Homepage http://swingx.java.net/ wird auf die Web-Startapplikation verlinkt (http://swinglabs-demos.java.net/demos/swingxset6/swingxset.jnlp).

Überblick über erweiterte Standard-Swing-Klassen

SwingX nimmt einige Klassen als Basisklassen und fügt Funktionalität hinzu. Die Standardkomponenten lassen sich problemlos gegen die neuen Klassen austauschen.

SwingX-Klasse Swing-Basisklasse Aufgabe
JXButton JButton Schaltfläche mit Unterstützung für Painter
JXLabel JLabel Beschriftung mit Unterstützung für Painter
JXTable JTable stark erweiterte Tabelle mit Suche, Sortierung, Filterung, …
JXList JList stark erweiterte Liste mit Suche, Sortierung, Filterung, …
JXTree JTree stark erweiterter Baum mit Suche, Filterung, …
JXFrame JFrame Fenster mit optionaler Statuszeile, Warte-zustand, …
JXDialog JDialog Vereinfacht den Aufbau von Dialogen.
JXPanel JPanel Panel mit Transparenz und Painter
JXEditorPane JEditorPane verbesserte Editor-Funktionalität, Undo/Redo und Suche
JXComboBox JComboBox Verbessert den Edit-Modus beim Einsatz in einer Tabelle.
JXFormattedTextField JFormattedTextField Unterstützt Prompts und Buddies in formatierten Textzeilen.
JXTextArea JTextArea Unterstützt Prompts und Buddies in Text-bereichen.
JXTextField JTextField Unterstützt Prompts und Buddies in Text-zeilen.
JXRootPanel JRootPanel Wurzelkomponente mit Symbolleiste und Statuszeile

Erweiterte Standard-Swing-Klassen

Die Klassen JXTable, JXList, JXTreeTable sind sehr leistungsfähig und daher mit einem kleinen Satz kaum zu beschreiben. Die API-Dokumentation gibt mehr Informationen. Wichtig sind dabei Klassen aus dem Paket org.jdesktop.swingx.decorator, die für JXTable, JXTreeTable, JXTree und JXList Sortierung, Filterung und Hervorhebung unterstützen.

In den Zellen tauchen zudem die Begriffe Painter, Prompt und Buddy auf:

  • Ein Painter ist ein Objekt, das an die SwingX-Komponente gesetzt wird. Zum Zeichnen greift die SwingX-Klasse auf das externe Zeichenobjekt zurück. Der Vorteil ist, dass so keine Unterklassen nötig sind, etwa von JButton oder JLabel, nur wenn in den Zeichenprozess eingegriffen werden soll.
  • Prompts und Buddies zeigen bei JXTextField, JXFormattedTextField und JXTextArea Eingabehinweise oder Fehler.

Neue Swing-Klassen

Zusätzlich zu den Erweiterungen der Standard-Swing-Komponenten bietet SwingX weitere Komponenten. Manche sind dringend nötig, etwa zur Auswahl eines Datums, einige sind sehr speziell, etwa zum Zeichnen eines 2D-Graphen.

SwingX-Klasse Swing-Basisklasse Aufgabe
JXDatePicker JComponent Datumseingabe im Textfeld mit Popup-Menü
JXHyperlink JButton Hyperlink
JXBusyLabel JLabel Animation für Ladeprozesse
JXTreeTable JXTable Verschmilzt Tabelle und Baum.
JXStatusBar JComponent Statuszeile, in der Regel unten am Fenster
JXErrorPane JComponent Darstellung von Fehlermeldungen
JXMultiThumbSlider JComponent Slider mit zwei Knöpfen
JXMonthView JComponent Zeigt Monatskalender und erlaubt Monats-auswahl.
JXSearchField JXTextField Textfeld mit kleinem Such-Icon
JXRadioGroup JPanel Vereinfacht die ButtonGroup.
JXMultiSplitPane JPanel geschachtelte Split-Panels
JXCollapsiblePane JXPanel animierte ausfaltbare Panels
JXTaskPane JXPanel Titel mit Icon zum Ausfalten der Container-elemente
JXTaskPaneContainer JXPanel Container für mehrere JXTaskPanes
JXTipOfTheDay JXPanel Gibt den „Tipp des Tages“ aus.
JXTitledPanel JXPanel Panel mit Überschrift
JXTitledSeparator JXPanel Trennlinie mit Text
JXImageView JXPanel Zeigt ein Bild, ein neues kommt mit Drag & Drop.
JXColorSelectionButton JButton Zeigt bei Aktivierung einen Farbauswahldialog.
JXGradientChooser JXPanel Panel zum Aufbau eines Farbverlaufs (-Gradient)
JXGraph JXPanel Zeichnet 2D-Graphen.

Neue Klassen, die SwingX bietet

Zur Suche gehören noch ein paar andere Komponenten, die nicht aufgeführt sind. Für die JXTable gibt es weiterhin JXTableHeader, was eine Spezialisierung von JTableHeader ist.

Weitere SwingX-Klassen

Des Weiteren gibt es ein paar neue Layoutmanager, etwa HorizontalLayout, VerticalLayout oder StackLayout (setzt alle Komponenten aufeinander). SwingX unterstützt ebenfalls die Tastaturvervollständigung bei Textfeldern und der Combo-Box. Beispiele finden sich im Paket org.jdesktop.swingx.autocomplete.

SwingX-Installation

Wer SwingX einsetzen möchte, der lädt von http://java.net/downloads/swingx/releases/ das Java-Archiv swingx-all-1.6.4.jar (etwa 1,4 MiB) herunter und bindet es in den Klassenpfad ein. Auf den Seiten sind auch die Quellen und die Java-API-Dokumentation verlinkt. Nach dem Einbinden stehen die neuen Klassen unter dem Paket org.jdesktop.swingx zur Verfügung.

Inselraus: Details zur OpenJDK-Geschichte

Obwohl OpenJDK unter der GPL stand, enthielt es doch Teile wie den Font-Renderer, Sound-Unterstützung, Farbmanagement oder SNMP-Code, die als binäre Pakete beigelegt wurden, weil etwa die Rechte zur Veröffentlichung fehlten. Sun nennt diese Teile, die etwa 4 % vom JDK 6 ausmachen, belasteten Code (engl. encumbered code)[1]. Das hinderte puristische Linux-Distributoren daran, OpenJDK auszuliefern. RedHat startete im Juni 2007 das Projekt IcedTea, um diese binären Teile auf der Basis des OpenJDK durch GPL-Software zu ersetzen. So basiert der Font-Renderer zum Beispiel auf FreeType[2] und das Farbmanagement auf little CMS[3]. Mit diesen Ersetzungen erfüllte das OpenJDK mit IcedTea im Juni 2008 die Anforderungen des Technology Compatibility Kit (TCK) von Sun und ist in der Öffentlichkeit seither unter dem Namen OpenJDK 6 bekannt. Daraufhin floss das OpenJDK 6 plus der Ersetzungen unter der GPLv2 in Linux-Distributionen wie Fedora und Debian ein.

Das OpenJDK bildet die Basis von Java 8, und jeder Entwickler kann sein eigenes Java zusammenstellen und beliebige Erweiterungen veröffentlichen. Damit ist der Schritt vollzogen, dass auch Java auf Linux-Distributionen Platz finden darf, die Java vorher aus Lizenzgründen nicht integrieren wollten.

Auch wenn es sich so anhört, als ob das Oracle JDK bzw. OpenJDK das Gleiche sei, ist das nicht ganz richtig: Zwar basieren Oracle JDK und OpenJDK auf den gleichen Quellen (bei der Version 7 etwa zu 95 %), doch sind beim Oracle JDK immer noch proprietäre Dinge enthalten, und nicht alles ist hundertprozentig quelloffen und GPL. Das gilt für die Version 7 wie für die Version 6. Das Oracle JDK steht unter der Binary Code License; genau die muss jeder abnicken, der das JDK von der Webseite laden möchte.

Bei der 6er-Reihe kommt noch eine Besonderheit dazu, wie es die Versionsnummern[4] ganz gut zeigen. Während das Oracle JDK zum Beispiel im Juni 2011 bei Versionsnummer 1.6.0_26-b03 steht, ist das OpenJDK bei Version 6 b22. Die Versionsnummern sind deshalb völlig unabhängig, weil beide Projekte auch unabhängig voneinander laufen. Das hat mit der Geschichte zu tun. Nach der Entwicklung des JDK 6, das nicht unter der GPL steht, ging es mit dem JDK 7 logisch weiter. Aus dem JDK 7 (Build 10) entstand dann OpenJDK, das heute mit der Versionsnummer OpenJDK 7 genannt wird. OpenJDK 7 und JDK 7 entwickeln sich Hand in Hand, und Code-Änderungen gehen mal in die eine Richtung und mal in die andere.

Jetzt kommt die Besonderheit: Das OpenJDK 6 entstand nicht, wie vermutet werden könnte, aus dem Oracle JDK 1.6, sondern aus dem OpenJDK 7 (Build 20). Es wurden nur Java 7-Eigenschaften entfernt. So läuft das auch bis heute: Die meisten Änderungen am OpenJDK 6 sind Backports von OpenJDK 7. Änderungen am OpenJDK 7 stammen überwiegend von Oracle, und häufig ist es die Firma RedHat, die diese Änderungen in OpenJDK 6 portiert. Zwischen dem OpenJDK 6 und dem JDK 1.6 gibt es einen Quellcodeaustausch bei Bug-Fixes, doch die Codebasis ist unterschiedlich. Oracle JDK 6 ist im Wartungsmodus, und großartige Veränderungen passieren bis auf Fehlerbereinigungen nicht.

Oracle JDK 8 ist die Version, die die Download-Seite von Oracle anbietet; das OpenJDK 8 liegt auf einem eigenen Server http://openjdk.java.net/projects/jdk8/. Das OpenJDK bildet die Referenzimplementierung für Java SE, nicht das Oracle JDK.

[1]  http://www.sun.com/software/opensource/java/faq.jsp#h

[2]  http://www.freetype.org/

[3]  http://www.littlecms.com/

[4]  http://gist.github.com/925323

Zusätzliche statische Initialisier im enum

Es sind Blöcke der Art static { … } im Rumpf eines Aufzählungstyps erlaubt. Lädt die Laufzeitumgebung einer Klasse, initialisiert sie der Reihe nach alle statischen Variablen bzw. führt die static-Blöcke aus. Die Aufzählungen sind statische Variablen und werden beim Laden initialisiert. Steht der statische Initialisierer hinter den Konstanten, so wird auch er später aufgerufen als die Konstuktoren, die vielleicht auf statische Variablen zurückgreifen wollen, die der static-Block initialisiert. Ein Beispiel:

public enum Country {
  GERMANY, UK, CHINA;
  {
    System.out.println( "Objektinitialisierer" );
  }
  static {
    System.out.println( "Klasseninitialisier" );
  }

  private Country() {
    System.out.println( "Konstruktor" );
  }

  public static void main( String[] args ) {
    System.out.println( GERMANY );
  }
}

Die Ausgabe ist:

Objektinitialisierer

Konstruktor

Objektinitialisierer

Konstruktor

Objektinitialisierer

Konstruktor

Klasseninitialisier

GERMANY

Die Ausführung und Ausgabe hängt von der Reihenfolge der Deklaration ab und jede Umsortierung führt zu einer Verhaltensänderung. Jetzt könnten Programmierer auf die Idee kommen, mögliche static-Blöcke an den Anfang zu setzen, vor die Konstanten. Meine Leser sollten das Ergebnis testen …

Thema der Woche: Properties und Bean-Bindings

  1. Lies https://docs.oracle.com/javase/8/javafx/properties-binding-tutorial/binding.htm.
  2. Simuliere in einem Beispiel den Unterschied zwischen einem Change- und Invalid-Event.
  3. Was ist ein Java SE Profil und gehört die Property-API immer zu jedem Profil?
  4. Verschaffe Überblick über https://docs.oracle.com/javase/8/javafx/api/javafx/beans/binding/Bindings.html.
  5. Baue ein Bindung für ein double mit folgender Logik: ist das double echt kleiner als 0, ist das Ergebnis -1, wenn es größer/gleich 0 ist, ist das Ergebnis +1.
  6. Schreibe einige neues Bindings:
  • von double -> boolean, wenn double == 0 ist,
  • von Point -> double für den Abstand zum Nullpunkt
  • von String -> Point, wobei der String „1,23“ dann zum new Point(1,23) wird
  • verknüpfe die drei letzten Properties, dass ein String mit einem Punkt auf boolean abgebildet wird