So sagt es https://openjdk.java.net/projects/jdk/17/#Schedule.
Download wie bisher unter https://jdk.java.net/17/. Bei Adoptium müssen wir noch warten.
So sagt es https://openjdk.java.net/projects/jdk/17/#Schedule.
Download wie bisher unter https://jdk.java.net/17/. Bei Adoptium müssen wir noch warten.
Das AdoptOpenJDK ist zur Eclipse Foundation gewandert und hat jetzt die Webseite live geschaltet: https://adoptium.net/.
Auf kurz oder lang wird es die vorherrschende Distribution werden.
Das javax.xml.bind-Paket bietet eine Klasse DatatypeConverter, die eigentlich für die Abbildung von XML-Typen auf Java-Typen gedacht ist, doch auch so einige nützliche Methoden bereitstellt. Wir finden in der Klasse statische parse*(String)-Methoden und print*(…)-Methoden: Die ersten konvertieren einen String in diverse Datentypen – etwa short parseShort(String) –, und die zweiten formatieren einen bestimmten Datentyp in einen String – etwa String printShort(short). Für die meisten Methoden gibt es mit String.valueOf(…) und den parse*(…)/toString(…)-Methoden in den Wrapper-Klassen bessere Alternativen, und die Umwandlung von Datumswerten und Fließkommazahlen ist nicht lokalisiert, doch hervorzuheben sind folgende zwei Methoden:
final class javax.xml.bind.DatatypeConverter
Mit diesen statischen Methoden können leicht Byte-Arrays in die String-Repräsentationen hexadezimal konvertiert werden. Das ist nötig, wenn etwa Bytes in einer Text-Konfigurationsdatei abgelegt werden sollen. DatatypeConverter bietet auch Methoden für eine Base64-Kodierung, allerdings sind die geschachtelten Klassen in java.util.Base64 üblicher.
Beispiel:
Konvertiere ein Byte-Array in eine String-Repräsentation, einmal im klassischen Hex-Format, einmal in Base64-Kodierung:
byte[] bytes = { 1, 2, 3, (byte) 254, (byte) 255 }; String s1 = DatatypeConverter.printHexBinary( bytes ); String s2 = DatatypeConverter.printBase64Binary( bytes ); System.out.println( s1 ); // 010203FEFF System.out.println( s2 ); // AQID/v8= // Arrays.equals( bytes, DatatypeConverter.parseHexBinary( s1 ) ) == true // Arrays.equals( bytes, DatatypeConverter.parseBase64Binary( s2 ) ) == true
Zwischen Java 1.2 und Java 16 unterstützte der Java-Compiler das Schlüsselwort strictfp. Die Unterscheidung sollte es der Laufzeitumgebung erlauben, nicht strikt nach der IEEE-754-Spezifikation zu arbeiten und damit schneller auf der lokalen Hardware Java-Programme auszuführen. Doch seit 20 Jahren unterstützen alle Prozessoren IEEE-754, und die Unterscheidung ist irrelevant geworden – alle von der JVM ausgeführten mathematischen Operationen sind wie im IEEE-754-Standard beschrieben. Mit dem Abschied von strictfp wird auch die Klasse StrictMath irrelevant.
Details: https://openjdk.java.net/jeps/306
Die Konstanten einer Schnittstelle können einer anderen Schnittstelle vererbt werden. Dabei gibt es einige kleine Einschränkungen. Wir wollen an einem Beispiel sehen, wie sich die Vererbung auswirkt, wenn gleiche Bezeichner in den Unterschnittstellen erneut verwendet werden. Die Basis unseres Beispiels ist die Schnittstelle BaseColors mit ein paar Deklarationen von Farben. Zwei Unterschnittstellen erweitern BaseColors, und zwar CarColors und PlaneColors, die für Farbdeklarationen für Autos und Flugzeuge stehen. Eine besondere Schnittstelle FlyingCarColors erweitert die beiden Schnittstellen CarColors und PlaneColors, denn es gibt auch fliegende Autos, die eine Farbe haben können.
interface BaseColors { int WHITE = 0; int BLACK = 1; int GREY = 2; } interface CarColors extends BaseColors { int WHITE = 1; int BLACK = 0; } interface PlaneColors extends BaseColors { int WHITE = 0; int GREY = 2; } interface FlyingCarColors extends CarColors, PlaneColors { } public class Colors { public static void main( String[] args ) { System.out.println( BaseColors.GREY ); // 2 System.out.println( CarColors.GREY ); // 2 System.out.println( BaseColors.BLACK ); // 1 System.out.println( CarColors.BLACK ); // 0 System.out.println( PlaneColors.BLACK ); // 1 System.out.println( FlyingCarColors.WHITE ); // field FlyingCarColors.WHITE is ambiguous System.out.println( FlyingCarColors.GREY ); // field FlyingCarColors.GREY is ambiguous } }
Die erste wichtige Tatsache ist, dass unsere drei Schnittstellen ohne Fehler übersetzt werden können, aber nicht die Klasse Colors. Das Programm und der Compiler zeigen folgendes Verhalten:
Mein neues Java-Buch, voraussichtlich März 2021. Details unter https://www.rheinwerk-verlag.de/captain-ciaociao-erobert-java/.
Details siehe https://blog.jetbrains.com/idea/2020/12/intellij-idea-2020-3/.
Eine Vorschau über einige Features in dem Video:
Details siehe https://github.com/spring-projects/spring-boot/releases/tag/v2.4.1
Veränderungen in der Verfügbarkeit des Java Development Kit seit dem Release des JDK 11 im September 2018 erschweren den sicheren Betrieb von Java-Anwendungen für viele Endnutzer. Die Nutzung des Oracle JDK ist im kommerziellen IT Betrieb nun nur noch mit Lizenz möglich.
Azul Systems tritt an, mit der Zulu OpenJDK Laufzeitumgebung eine wirtschaftlich attraktive Java-Distribution mit zuverlässigen Updates und Bug Fixes anzubieten.
2006 hat Sun Microsystems, zu diesem Zeitpunkt der Inhaber der Marke Java, angekündigt das JDK in ein Open-Source Projekt zu überführen. Zu diesem Zweck wurde das OpenJDK Projekt in Leben gerufen, das ein Zuhause auf http://openjdk.java.net fand. Beginnend mit dem JVM Source Code wurden bis Anfang 2007 auch die Kernbibliotheken veröffentlicht. Einige Teile des OpenJDK blieben bis April 2009 proprietär, bis mit build 53 des OpenJDK 7 eine vollständig auf Open Source basierende Distribution verfügbar war. Seit Java 7 SE ist das OpenJDK die Referenzimplementierung des Java SE-Standards, der durch die entsprechenden Java Specification Requests (JSR) festgelegt wird. Ein Projekt mit dem Ziel einer Open Source Umsetzung von Java SE 6 wurde ebenfalls gestartet, geleitet von Andrew Brygin von Azul Systems. Das OpenJDK steht unter der GNU Public License (GPL) version 2 with classpath exception (CPE). Die Lizenzierung unter der CPE verhindert das die „copyleft“ Natur der GPL sich auf die Anwendungen auswirkt die das OpenJDK als Laufzeitumgebung nutzen. Im September 2007 hat Oracle zahlreiche Veränderungen bei der Art und Weise wie das JDK entwickelt, verbreitet und aktualisiert wird. Diese lassen sich wie folgt zusammenfassen:
Das OpenJDK ist ein reines Source Code Projekt. Um es als Java Laufzeitumgebung nutzen zu können ist es nötig, das Projekt in eine Binärdistribution zu kompilieren. Die von Azul erstellte Distribution trägt den Namen Zulu. Zulu wird, mit dem vom OpenJDK vorgegebenen Standard
Build Prozess erstellt und alle Binaries mit dem Technology Compatibility Kit (TCK) validiert, um Kompatibilität und Konformität mit der Java SE-Spezifikation sicherzustellen. Um die den stabilen und sicheren Betrieb bei den Kunden zu ermöglichen werden regelmäßige Updates für
das OpenJDK erstellt, die Bug Fixes und Security Patches enthalten. Bis zu Version 9 des JDK wurden diese Updates jeweils für die aktuelle Version und die vorhergehende Version bereitgestellt. Die Überlappung der Supportzeiträume seit JDK 6 betrug zwischen 13 und 37 Monate. Unter der neuen Release Policy sind jedoch bereits OpenJDK 6, 7, 8 und 11 aus dem öffentlichen und freien Support herausgefallen. Azul liefert auf weiterhin Updates für all diese JDK-Versionen. Dies erlaubt es den Kunden auch ältere Java-Versionen sicher im produktiven Betrieb zu nutzen.
Da das OpenJDK die Referenzimplementierung des Java SE-Standards darstellt, nutzen Oracle und andere es als Basis ihrer Binärdistributionen. Vor JDK 11 enthielt das Oracle JDK eine Reihe von
zusätzlichen Features, von denen einige nun Open Source sind, andere bleiben proprietär.
Anwender können bei der Planung des OpenJDK-Rollout folgenge Option in Betracht ziehen:
Das Zulu OpenJDK wird unter der GPLv2 with CPE Lizenz veröffentlicht. Zulu Binaries stehen für Windows, Linux, Solaris und MacOS X auf https://www.azul.com/downloads/zulu-community/ zum Download bereit. Auch Release Notes mit Installationsanleitung finden sich auf dieser Seite. Diese decken alle bereitgestellten Paketformate, Abhängigkeiten sowie Anleitungen für die Installation in gängigen Cloud Umgebungen wie Microsoft Azure und Amazon Web Services bereit. Enthalten sind auch Post-Installationsschritte und eine Anleitung wie die Standard Java Runtime auf einem Server/Desktop aus festgelegt werden kann.
Die proprietären Lucida Font aus dem Oracle JDK sind nicht Teil des Standard OpenJDK. Falls erforderlich stellt das Zulu Commercial Compatibility Kit (CCK)diese Fonts bereit, um in GUIs und PDF Generatoren ein identisches Layout und Schriftbild zu erhalten.
Zum Download des Zulu CCK müssen die Nutzungsbedingungen akzeptiert werden. Der Installer überprüft, ob eine passende Zulu-Version installiert ist. Beim Download ist auf die korrekte Version zu achten! Details und Download gibt es unter https://www.azul.com/products/zulu-and-zulu-enterprise/cck-downloads/. Dies ermöglicht in der Regel eine Migration ohne zusätzlich betriebliche Aufwände.
Zulu ist eine OpenJDK Distribution die vollumfänglich der Java SE-Spezifikation entspricht. Jede Anwendung, die auf Standard Java-Komponenten und APIs setzt, kann durch einen einfachen Austausch des JDK Pfads migriert werden. Ein engagiertes Entwicklerteam nimmt komplexe Anpassungen, wie Backporting und Integration von Updates in ältere OpenJDK-Versionen vor; daher können sich Azul-Kunden der aktuellsten Security-Patches und Bug-Fixes auch auf älteren Java-Versionen erfreuen.
Die Zulu OpenJDK-Distribution bietet eine kostengünstige Alternative, um Applikationen auf aktuelle Java Versionen zu migrieren und sicher zu betreiben.
Während Array.asList(...)
nur ein Dektorator ist, der ein Array so aussehen lässt, als wäre es eine List
, übertragen drei überladene Methoden toArray(...)
aus Collection
alle Elemente einer Sammlung auf eine Liste:
Object[] toArray()
<T> T[] toArray(T[] a)
default <T> T[] toArray(IntFunction<T[]> generator)
(Java 11)Was ist das Ergebnis der Ausgabe?
List<String> list = new ArrayList<>( Arrays.asList( "a", "b" ) );
System.out.println( list.toArray()[ 0 ] );
System.out.println( list.toArray( new String[ 0 ] )[ 0 ] );
System.out.println( list.toArray( new String[ 0 ] ).length );
System.out.println( list.toArray( new String[ 1 ] )[ 0 ] );
System.out.println( list.toArray( new String[ 1 ] ).length );
System.out.println( list.toArray( value -> new String[ 1 ] )[ 0 ] );
System.out.println( list.toArray( value -> new String[ 1 ] ).length );
System.out.println( list.toArray().getClass() );
System.out.println( list.toArray( new String[ 0 ] ).getClass() );
Lösung .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
List<String> list = new ArrayList<>( Arrays.asList( "a", "b" ) );
System.out.println( list.toArray()[ 0 ] ); // a
System.out.println( list.toArray( new String[ 0 ] )[ 0 ] ); // a
System.out.println( list.toArray( new String[ 0 ] ).length ); // 2
System.out.println( list.toArray( new String[ 1 ] )[ 0 ] ); // a
System.out.println( list.toArray( new String[ 1 ] ).length ); // 2
System.out.println( list.toArray( value -> new String[ 1 ] )[ 0 ] ); // a
System.out.println( list.toArray( value -> new String[ 1 ] ).length ); // 2
System.out.println( list.toArray().getClass() ); // class [Ljava.lang.Object;
System.out.println( list.toArray( new String[ 0 ] ).getClass() ); // class [Ljava.lang.String;
Es hilft beim Verständnis sich den Code aus dem OpenJDK anzuschauen:
Aus der OpenJDK-Implementierung von java.util.ArrayList
:
public Object[] toArray() {
return Arrays.copyOf( elementData, size );
}
Aus der OpenJDK-Implementierung von java.util.ArrayList
:
public T[] toArray( T[] a ) {
if ( a.length < size ) // Make a new array of a’s runtime type, but my contents: return (T[]) Arrays.copyOf( elementData, size, a.getClass() ); System.arraycopy( elementData, 0, a, 0, size ); if ( a.length > size )
a[ size ] = null;
return a;
}
Aus der OpenJDK-Implementierung von java.util.Collection
:
default T[] toArray( IntFunction generator) {
return toArray(generator.apply(0));
}
Details siehe https://spring.io/blog/2020/09/16/spring-tools-4-8-0-released. Kurz zusammengefasst:
Download der Spring Tools 4: https://spring.io/tools/
Strings mit einem Zeilenumbruch kommen in Programmen immer wieder vor, etwa bei Bildschirmausgaben, eingebettetem HTML, XML, JSON oder SQL. Ein Beispiel:
String joke =
"Lehrer: '76 % aller Schüler in dieser Klasse haben keine Ahnung von Prozentrechnung.'\n" +
"Schüler: 'Herr Lehrer, so viele sind wir doch gar nicht!'";
Im ersten String-Literal steht \n für den Zeilenumbruch. Das Pluszeichen setzt die beiden Strings zusammen. Der Java-Compiler stellt selbständig aus den beiden Konstanten ein größeres String Literal her.
In Java 15 wurden Textblöcke eingeführt (standardisiert im JEP 378: Text Blocks, https://openjdk.java.net/jeps/378). Damit lassen sich einfacher mehrzeilige Strings aufbauen. Drei doppelte Anführungszeichen leiten einen Textblock ein (opening delimiter genannt) und drei doppelte Anführungszeichen schließen einen Textblock wieder ab (closing delimiter genannt). Mithilfe von Textblöcken sieht unser Beispiel von oben so aus:
String joke = """
Lehrer: '76 % aller Schüler in dieser Klasse haben keine Ahnung von Prozentrechnung.'
Schüler: 'Herr Lehrer, so viele sind wir doch gar nicht!'""";
System.out.println( joke );
Ein Textblock wird immer mit drei doppelten Anführungszeichen eingeleitet, und ein Zeilenumbruch muss folgen. Das bedeutet automatisch, dass die Textblöcke immer mindestens zwei Quellcodezeilen umfassen, und niemals nur in eine Zeile geschrieben werden können.
Nach der Einleitung eines Textblocks hängt der Compiler an jede Zeile, die nicht mit drei Anführungszeichen abgeschlossen wird, einen Zeilenumbruch LINE-FEED, kurs LF (Unicode \u000A). In unserem Beispiel haben wir nur einen Zeilenumbruch. Hätten wir die drei Anführungszeichen in die nächste Zeile geschrieben, hätten wir zwei Zeilenumbrüche im Ergebnis:
String joke = """
Lehrer: '76 % aller Schüler in dieser Klasse haben keine Ahnung von Prozentrechnung.'
Schüler: 'Herr Lehrer, so viele sind wir doch gar nicht!'
""";
Es macht also einen Unterschied, ob die drei Anführungszeichen zum Schließen des Textblocks in einer eigenen Zeile stehen (der entstehende String endet mit einem LF) oder am Ende eines Strings (der entstehende String endet nicht mit einem LF). Beim Schreiben würde das sicherlich etwas symmetrischer und hübscher aussehen, wenn der Beginn eines Textblocks und das Ende eines Textblocks jeweils in einer einzelnen Zeile stehen würden, doch das können wir nicht immer machen, denn das ergibt immer am Ende einen Zeilenumbruch, der vielleicht nicht gewünscht ist.
Textblöcke sind eine alternative Schreibweise für Strings in doppelten Anführungszeichen. Im Bytecode ist später nicht mehr zu erkennen, auf welche Art und Weise der String entstanden ist. Textblöcke können genauso wie reguläre Strings an allen Stellen eingesetzt werden, an denen Strings gefordert werden, etwa als Argument:
System.out.println( """
Lehrer: '76 % aller Schüler in dieser Klasse haben keine Ahnung von Prozentrechnung.'
Schüler: 'Herr Lehrer, so viele sind wir doch gar nicht!'""" );
Ist im Quellcode innerhalb vom Textblock ein Zeilenumbruch sinnvoll, damit z. B. die Zeile nicht so lang wird, lässt sich mit einem Backslash der Zeilenumbruch im Ergebnisstring verhindern:
String joke2 = """
Warum haben Giraffen so einen langen Hals? \
Weil der Kopf so weit oben ist.""";
// Warum haben Giraffen so einen langen Hals? Weil der Kopf so weit oben ist.
System.out.println( joke2 );
Da Textblöcke Strings sind, lassen sie sich auch mit dem +-Operator konkatenieren:
String person1 = "Lehrer", person2 = "Schüler";
String joke = person1 +
"""
: '76 % aller Schüler in dieser Klasse haben keine Ahnung von Prozentrechnung.'
""" + person2 + """
: 'Herr Lehrer, so viele sind wir doch gar nicht!'""";
System.out.println( joke );
Es lässt sich ablesen, dass das Einsetzen von Variableninhalten nicht besonders elegant ist — das war allerdings nicht das Ziel der Textblöcke gewesen, so etwas wie String-Interpolation zu schaffen, was etwa JavaScript kann. Etwas zur Hilfe kommt die String-Methode formatted(…), die als Objektmethode hinzugekommen ist:
String person1 = "Lehrer", person2 = "Schüler";
String joke = """
%s: '76 %% aller Schüler in dieser Klasse haben keine Ahnung von Prozentrechnung.'
%s: 'Herr Lehrer, so viele sind wir doch gar nicht!'""".formatted( person1, person2 );
System.out.println( joke );
Die Einrückung der Zeilen in einem Textblock spielt eine elementare Rolle. Die Zeilen eines Textblocks müssen nicht ganz links am Anfang einer Zeile bei der Position 0 beginnen, sondern dürfen rechts eingerückt sein und den üblichen Konventionen in der Einrückung von Java Programmen folgen. Bei der Einrückung sollte beiläufiger Weißraum (engl. incidental white space) aus dem eigentlichen Textblock ausgenommen werden. Hier wendet der Java-Compiler einen kleinen Algorithmus an. Die Regel ist, dass die Zeile die am weitesten links steht (die Zeile mit dem drei abschließenden Anführungszeichen gehört dazu) den beiläufigen Weißraum bestimmt, der abgeschnitten wird. Es ist dabei egal, ob das Zeichen ein Tabulator oder Leerzeichen ist, auch wenn das auf dem Bildschirm anders aussieht! Bestehen bleibt rechts von dieser Stelle der wesentliche Weißraum (engl. essential white space).
Beispiel 1:
String joke2 = """
Warum haben Giraffen so einen langen Hals?
Weil der Kopf so weit oben ist.""";
Ausgabe 1:
Warum haben Giraffen so einen langen Hals?
Weil der Kopf so weit oben ist.
Beispiel 2:
String joke2 = """
Warum haben Giraffen so einen langen Hals?
Weil der Kopf so weit oben ist.""";
Ausgabe 2:
Warum haben Giraffen so einen langen Hals?
Weil der Kopf so weit oben ist.
Beispiel 3:
String joke2 = """
Warum haben Giraffen so einen langen Hals?
Weil der Kopf so weit oben ist.
""";
Ausgabe 3:
Warum haben Giraffen so einen langen Hals?
Weil der Kopf so weit oben ist.
Kommen wir abschließend noch zu ein paar Regeln. Im Quellcode könnte ein Zeilenende durch unterschiedliche Symbole im Text vorkommen: CR (\u000D), CR-LF (\u000D\u000A) oder LF (\u000A). Der Compiler führt eine sogenannte Normalisierung durch, dass am Ende nur ein LF (\u000A) im String steht.
Textblöcke sind keine sogenannten Raw-Strings, die „roh“ sind, sondern können alle Escape-Sequenzen, unter anderem \n, \t, \‘, \“ und \\, enthalten. So lässt sich am Ende immer noch mit \r eine CR-LF-Sequenz erschaffen:
String joke = """
Lehrer: '76 % aller Schüler in dieser Klasse haben keine Ahnung von Prozentrechnung.'\r
Schüler: 'Herr Lehrer, so viele sind wir doch gar nicht!'""";
Das Ergebnis wäre „Lehrer: ’76 % aller Schüler in dieser Klasse haben keine Ahnung von Prozentrechnung.’\u000D\u000ASchüler: ‚Herr Lehrer, so viele sind wir doch gar nicht!'“.
Kommen im String einfache oder doppelte Anführungszeichen vor, können diese ohne Ausmaskierung eingesetzt werden. Ein Sonderfall ist allerdings, wenn das doppelte Anführungszeichen direkt am Ende vor den drei schließenden doppelten Anführungszeichen steht, dann eine Maskierung notwendig:
String joke = """
Lehrer: "76 % aller Schüler in dieser Klasse haben keine Ahnung von Prozentrechnung."
Schüler: "Herr Lehrer, so viele sind wir doch gar nicht!\"""";
Sollen drei Anführungszeichen selbst in der Ausgabe vorkommen, muss eines der drei Anführungszeichen ausmaskiert werden, um nicht fälschlicherweise als Abschluss interpretiert zu werden. Für die Ausgabe
1 "
2 ""
3 """
4 """"
5 """""
6 """"""
müssen wir schreiben:
System.out.println( """
1 "
2 ""
3 ""\"
4 ""\""
5 ""\"""
6 ""\"""\"""" );
Solche Zeichenketten sind nicht mehr besonders gut lesbar …
Eine weitere Regel ist, dass Leerzeichen am Ende abgeschnitten werden. Aus dem Textblock
String s = """
1␣␣␣␣␣␣␣
2␣␣␣
""";
wird der String „1\u0002“ — der “Offene Kasten” ␣ stehen hier für Leerzeichen, die man sonst im Druck nicht sehen würde.
Sollen Leerzeichen am Ende zählen, so führt man die Escape-Sequenz \s ein. Dies wird übersetzt in ein reguläres Leerzeichen \u0020. Für das obere Beispiel bedeutet das:
String s = """
1 \s
2 \s
""";
Leser können mit der folgenden Aufgabe ihr Verständnis prüfen: Was für ein String entsteht?
String joke2 = """
Warum haben Giraffen so einen langen Hals? \
Weil der Kopf so weit oben ist.
""";
Es entsteht „Warum haben Giraffen so einen langen Hals? Weil der Kopf so weit oben ist.\n“.
Siehe auch http://www.tutego.de/java/JDK-15-oracle-java-15-openjdk-15.html. Wenig spannend für Entwickler. IntelliJ kann direkt mit den Neuerungen umgehen, bei Eclipse muss man erst ein Plugin installieren.
Einige einführende Beiträge:
https://www.infoworld.com/article/3534133/jdk-15-the-new-features-in-java-15.html
https://blog.jetbrains.com/idea/2020/09/java-15-and-intellij-idea/
Download wie immer unter https://www.eclipse.org/eclipseide/.
Was (mich) nervt, ist der zeitgleiche Release von neuen Java-Versionen und der Eclipse IDE. Daher kann die Eclipse-Version Java 15 nur über ein Plugin: https://marketplace.eclipse.org/content/java-15-support-eclipse-2020-09-417
Schon seit Java 1.0 gibt es den Quellcode der Standardbibliotheken (falls er beim JDK mitinstalliert wurde, befindet er sich im Wurzelverzeichnis unter dem Namen src.zip), und jeder Interessierte konnte einen Blick auf die Implementierung werfen. Zwar legte Sun damals also die Implementierungen offen, doch weder die Laufzeitumgebung noch der Compiler oder die Bibliotheken standen unter einer akzeptierten Open-Source-Lizenz. Zehn Jahre seit der ersten Freigabe von Java gab es Forderungen an Sun, die gesamte Java-Plattform unter eine bekanntere Lizenzform wie die GNU General Public License (GPL) oder die BSD-Lizenz zu stellen. Dabei deutete Jonathan Schwartz in San Francisco bei der JavaOne-Konferenz 2006 schon an: »It’s not a question of whether we’ll open source Java, now the question is how.« War die Frage also statt des »Ob« ein »Wie«, kündigte Rich Green bei der Eröffnungsrede der JavaOne-Konferenz im Mai 2007 die endgültige Freigabe von Java als OpenJDK (http://openjdk.java.net/) unter der Open-Source-Lizenz GPL 2 an. Dem war Ende 2006 die Freigabe des Compilers und der virtuellen Maschine vorausgegangen.
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). 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 und das Farbmanagement auf little CMS. 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 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: Die meisten Patches am OpenJDK 6 sind Backports von OpenJDK 7. Änderungen am OpenJDK 7 stammen überwiegend von Oracle, und häufig ist es die Firma Red Hat, 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.
Das freie und unter der GPL stehende OpenJDK (http://openjdk.java.net/) bildet nunmehr die Referenzimplementierung für Java SE. Alle Entwicklungen finden dort statt. Der Fortschritt ist live zu beobachten, regelmäßig fixen und erweitern Hunderte von Entwicklern die Codebasis. Die Quellen für das OpenJDK lassen sich im Mercurial-Repository unter http://hg.openjdk.java.net/jdk/jdk14 einsehen (ein Wechsel auf GitHub wird diskutiert). Viele Technologien, die Oracle vorher nur im Oracle JDK hatte, wurden in das OpenJDK übertragen, etwa Java Flight Recorder, Java Mission Control, Application Class-Data Sharing und ZGC (Zero-Garbage-Collector).
Oracle selbst compiliert das OpenJDK und bietet es an. Aktuelle Versionen sind über http://jdk.java.net/ verlinkt. Es gibt von Oracle OpenJDK-x64-Builds für Windows, Linux und macOS.
Das Oracle OpenJDK steht unter der GNU General Public License v2 mit der Classpath Exception (GPLv2+CPE). Oracle selbst hat angekündigt, bei neuen Versionen keine Updates mehr für die alten Versionen zu veröffentlichen.
AdoptOpenJDK (https://adoptopenjdk.net/) ist eine Stiftung und betreibt eine Serverfarm, die regelmäßig Builds vom OpenJDK baut und dazu weitere Software wie die JavaFX-Implementierung OpenJFX und alternative Laufzeitumgebungen wie Eclipse OpenJ9 einbindet. Es wird angekündigt, auch ältere Versionen mit Bugfixes zu versorgen. Zu den Unterstützern zählen Amazon, IBM/Red Hat, Microsoft, Pivotal und viele weitere.
Von der Webseite lässt sich für unterschiedliche Betriebssysteme eine Version herunterladen. Angeboten werden Builds u.a. für: Windows, Linux, macOS.
AdoptOpenJDK zählt aktuell zu den populärsten OpenJDK-Distributionen.
Das Unternehmen Azul bietet Builds unter dem Namen Zulu an, auch lässt sich ein Support-Vertrag abschließen: http://www.azul.com/downloads/zulu/. Neben den Plattformen Windows, Linux und macOS gibt es von Azul ebenfalls Docker-Images.
Red Hat bietet neben Linux auch eine Windows-Version vom OpenJDK an: http://developers.redhat.com/products/openjdk/overview/. Die Integration in Linux ist sehr gut, und Red Hat pflegt auch noch Sicherheitsupdates in Java 6 und Java 7 ein.
SAP bietet mit der SapMachine (http://sap.github.io/SapMachine/) Builds für diverse Betriebssysteme und auch ein Docker-Image.
Amazon Corretto (http://aws.amazon.com/de/corretto/) wird intern von Amazon in der Cloud eingesetzt und ist für die LTS-Versionen Java 8 und Java 11 kostenlos.
Alibaba Dragonwell (http://github.com/alibaba/dragonwell8) ist eine vom OpenJDK 8 abgeleitete Implementierung von Alibaba.
Von BellSoft gibt es das Liberica JDK (http://bell-sw.com/) für die Plattformen Windows x86_64 und auch noch Windows x86, macOS x86_64, Linux x86_64, Linux ARMv8, Linux ARMv7 HardFloat, Solaris SPARC und Solaris x86_64.
Apple pflegte lange Zeit eine komplett eigene JVM, bis Apple den Code an Oracle für das OpenJDK übergab. Auch Google setzt bei Android neuerdings auf das OpenJDK.
Oracle vermarktet auf der Basis des OpenJDK sein eigenes Projekt, Oracle JDK. Ab Java 11 sind das Oracle JDK und OpenJDK vom Code her (nahezu) identisch. Das Oracle JDK ist die »offizielle« Version, die die Java-Download-Seite von Oracle anbietet. Wenige kleine Unterschiede sind die Paketierung (das Oracle JDK hat einen Installer, das Oracle OpenJDK ist nur ein ZIP), die Versionskennung und ein paar weitere Module.[1]
Die halbjährlichen Java-Releases haben zur Folge, dass Versionen immer dann veraltet sind, wenn eine neue Version erscheint. In dem Moment, in dem Java 10 kam, war Java 9 veraltet; das Gleiche gilt bei Java 14 – es machte sofort Java 13 zur alten Version. Das allein wäre kein Problem, wenn die älteren Versionen mit Sicherheitsupdates versorgt würden. Aber Oracle investiert für die Allgemeinheit keine Zeit und Mühe mehr und pflegt die alten Versionen nicht.
Für Unternehmen ist das ein Problem, denn es erzeugt Stress, mit den Änderungen mitziehen zu müssen. Aus diesem Grund bietet Oracle alle drei Jahre eine Java-Version mit Long Term Support (LTS) und versorgt sie mit Updates und Sicherheitspatches. Die LTS-Versionen nach Java 8 sind Java 11 (September 2018) und dann nach 3 Jahren Java 17 (September 2021). Das ist für weniger agile Unternehmen gut. Oracle will seine Java SE 8-Implementierung noch viele Jahre pflegen.
Auf den ersten Blick sieht das gut aus: Es gibt regelmäßige Updates für agile Unternehmen, und die konservativen Unternehmen setzen auf eine LTS-Version. Das Problem ist allerdings, dass alle Oracle JDK-Versionen nicht kommerziell eingesetzt werden dürfen; der Hersteller erlaubt die Nutzung nur für »development, testing, prototyping or demonstrating purposes«.
Für Java 8 endete die Schonfrist im Januar 2019. Das dürfte vielen Entwicklern gar nicht bewusst sein, denn seit 20 Jahren sind Unternehmen daran gewöhnt, das Oracle JDK für alles einzusetzen. Und wir wissen alle, wie viele Menschen wirklich die Lizenzbedingungen lesen …
Wer also das Oracle JDK kommerziell einsetzen möchte und nicht nur in einer Entwicklungs- oder Testumgebung, muss eine Lizenz von Oracle erwerben. Es wird monatlich abgerechnet, die Vertragslaufzeit beträgt mindestens ein Jahr. Es stehen zwei Modelle zur Auswahl:
Java SE Subscription | Java SE Desktop Subscription |
Für Serveranwendungen | Für Client-Anwendungen |
Abrechnung pro Prozessor | Abrechnung pro Benutzer |
Bis 25 USD/Monat, für 1–99 Benutzer | Bis 2,50 USD/Monat für 1–999 Benutzer/Clients |
Oracle wendet bei der Java SE Subscription das gleiche Geschäftsmodell wie bei der Oracle-Datenbank an. Wie genau ein Rechner in der Cloud mit einer unbestimmten Anzahl der Prozessoren abgerechnet werden soll, ist noch unklar.[2] Interessenten sollten die »Oracle Java SE Subscription FAQ« unter http://www.oracle.com/technetwork/java/javaseproducts/overview/javasesubscriptionfaq-4891443.html studieren und Oracle-Berater hinzuziehen. Wer Client- und Serveranwendungen nutzt, muss zweimal bezahlen. Statt »write once, run anywhere« heißt es nun »write once, pay everywhere«.
Die Kosten können sich schnell summieren, doch bekommen Unternehmen damit Support und insbesondere für Java 8 immer noch für einige Jahre Unterstützung. Der Nachteil ist, dass es das Subscription-Modell nur für die LTS-Versionen gibt, Unternehmen also gezwungen werden, größere Versionssprünge zu machen. Nach Java 11 kommt erst im September 2021 die Version Java 17 mit dem nächsten LTS.
Transition ist im Gange, das Ganze wird später unter https://projects.eclipse.org/projects/adoptium liegen.
News-Beitrag: https://blog.adoptopenjdk.net/2020/06/adoptopenjdk-to-join-the-eclipse-foundation/
Siehe http://jdk.java.net/15/
2020/06/11 | Rampdown Phase One (fork from main line) | |
2020/07/16 | Rampdown Phase Two | |
2020/08/06 | Initial Release Candidate | |
2020/08/20 | Final Release Candidate | |
2020/09/15 | General Availability |
Sind Variablen final, heißt das lediglich, dass es eine einmalige Zuweisung geben darf. Ob die Werte allerdings zur Laufzeit berechnet werden oder nicht, hat erst einmal nichts mit final zu tun. In folgendem Beispiel ist die Variable eine zur Compilezeit bekannte Konstante:
public class Finance {
public static final int TAX = 19;
}
Greift eine andere Klasse auf die Variable TAX zu, ist das im Quellcode nicht als direkter Variablenzugriff Finance.TAX kodiert, sondern der Compiler hat das Literal 19 direkt an jeder Aufrufstelle eingesetzt. Dies ist eine Optimierung des Compilers, die er laut Java-Spezifikation vornehmen kann.
Wir sprechen in diesem Zusammenhang von einer compile-time constant expression wenn gilt:
Das Einsetzen der konstanten Werte ist praktisch, bringt aber ein Probleme mit sich, wenn das finale Attribut sich ändert. Dann muss nämlich auch jede Klasse übersetzt werden, die Bezug auf die Konstante hatte. Werden die abhängigen Klassen nicht neu übersetzt, ist in ihnen immer noch der alte Wert eincompiliert.
Die Lösung ist, die bezugnehmenden Klassen neu zu übersetzen und sich am besten anzugewöhnen, bei einer Änderung einer Konstanten gleich alles neu zu compilieren. Ein anderer Weg transformiert die finale Variable in eine später initialisierte Form:
public class Finance {
public static final int TAX = Integer.valueOf( 19 );
}
Die Initialisierung findet im statischen Initialisierer statt, und die Konstante mit dem Literal 19 ist zunächst einmal verschwunden. Der Compiler wird also beim Zugriff auf Finance.TAX keine Konstante 19 vorfinden und daher das Literal an den Aufrufstellen nicht einbauen können. In der Klassendatei wird der Bezug Finance.TAX vorhanden sein, und eine Änderung der Konstanten erzwingt keine neue Übersetzung der Klassen.