Java auf den Punkt gebracht (ebooks.kofler)

Transcrição

Java auf den Punkt gebracht (ebooks.kofler)
Java auf den
Punkt gebracht
Michael Kofler
Variablen Operatoren Klassen
Exceptions Multi-Threading
Generics und Collections
Lambda-Ausdrücke
Aktuell zu Java 8!
ebooks.kofler
Java auf den Punkt gebracht (aktuell zu Java 8)
Ursprünglicher Titel: Die Java-Syntax
Stichworte: Java 8, Variablen, Arrays und Enums, Elementare Datentypen, Strings, Datum
und Uhrzeit, Operatoren, Verzweigungen, Schleifen, Methoden, Klassen, Schnittstellen,
Vererbung, Exceptions, Pakete, Bibliotheken, Multi-Threading, Generics, Collections,
Lambda-Ausdrücke, Annotationen.
© Michael Kofler und ebooks.kofler 2013, 2014
Autor
Michael Kofler
Korrektorat
Markus Hinterreither
ISBN (PDF)
978-3-902643-09-4
ISBN (EPUB)
978-3-902643-15-5
Verlag
ebooks.kofler, Schönbrunngasse 54c, 8010 Graz, Austria
Die PDF- und EPUB-Ausgabe dieses Buchs können Sie hier kaufen:
http://kofler.info/ebooks/java/
Viele in diesem eBook genannten Hard- und Software-Bezeichnungen sind geschützte
Markennamen.
Dieses eBook wurde mit großer Sorgfalt verfasst. Dennoch sind Fehler nicht ganz auszuschließen. Für allfällige Fehler kann keine Verantwortung oder Haftung übernommen
werden. Verbesserungsvorschläge oder Korrekturen sind selbstverständlich willkommen
([email protected]). Vielen Dank dafür!
Dieses eBook ist durch das österreichische Urheberrecht geschützt. Sie dürfen das eBook
für den persönlichen Gebrauch kopieren und ausdrucken, aber nicht an andere Personen
weitergeben, weder in elektronischer noch in anderer Form.
Inhaltsverzeichnis
Vorwort
7
1 Hello World!
9
1.1 Der Programmcode
9
1.2 Der Java-Compiler javac
10
1.3 Der Java-Interpreter java
12
1.4 Elementare Syntaxregeln
12
2 Variablen, Arrays und Enums
16
2.1 Variablen
16
2.2 Arrays
27
2.3 Konstanten und Enums
33
3 Zeichenketten (char und String)
37
3.1 Der char-Datentyp
37
3.2 Die String-Klasse
39
3.3 Die StringBuilder-Klasse
46
3.4 Zeichensatzprobleme
47
4 Datum und Uhrzeit
52
4.1 Die Date-Klasse
53
4.2 Die SimpleDateFormat-Klasse
55
4.3 Die Calendar-Klasse
57
4.4 ThreeTen (Java 8)
60
Java auf den Punkt gebracht
ebooks.kofler
INHALTSVERZEICHNIS
4
5 Operatoren
64
6 Verzweigungen (if, switch)
72
6.1 if
72
6.2 if-Kurzschreibweise (Ternary Operator)
74
6.3 switch
74
7 Schleifen
77
7.1 for
77
7.2 for-each
79
7.3 while und do-while
81
7.4 break und continue
82
8 Methoden
84
8.1 Parameterliste
86
8.2 Rückgabewert und return
91
8.3 Modifizierer
91
8.4 get- und set-Methoden
93
9 Klassen
9.1 Top-Level-Klassen
95
95
9.2 Lokale Klassen
105
9.3 Anonyme Klassen
107
9.4 Statische geschachtelte Klassen
110
10 Vererbung und Schnittstellen
112
10.1 Vererbung
112
10.2 Schnittstellen
119
10.3 Die Object-Klasse
126
Java auf den Punkt gebracht
INHALTSVERZEICHNIS
ebooks.kofler
11 Lambda-Ausdrücke
5
130
11.1 Hello Lambda-World!
130
11.2 Syntax von Lambda-Ausdrücken
133
11.3 War das schon alles?
139
12 Exceptions
140
12.1 Exception-Klassen
140
12.2 try-catch
143
12.3 Fehleranfällige Methoden deklarieren (throws)
148
12.4 Selbst Exceptions werfen (throw)
149
13 Pakete und Bibliotheken
151
13.1 import
152
13.2 Pakete
154
13.3 Bibliotheken
156
14 Multi-Threading
159
14.1 Multi-Threading-Syntax
159
14.2 Die java.util.concurrent-Klassen
172
15 Generics
182
15.1 Einführung
182
15.2 Deklaration generischer Klassen und Schnittstellen
186
15.3 Deklaration generischer Methoden
188
15.4 Wildcards
189
16 Collections
196
16.1 Einführung
197
16.2 Die Iterable-Schnittstelle
203
16.3 Die Collection-Schnittstelle
205
16.4 Die Set-Schnittstelle
208
Java auf den Punkt gebracht
ebooks.kofler
INHALTSVERZEICHNIS
6
16.5 Die List-Schnittstelle
213
16.6 Die Stream-Schnittstelle (Java 8)
218
16.7 Die Map-Schnittstelle
220
17 Annotationen
227
17.1 Vordefinierte Annotationen
228
17.2 Eigene Annotationen definieren
229
Vorwort
Dieses eBook fasst die grundlegenden Elemente der Programmiersprache Java
zusammen. Neben einer kompakten Beschreibung der Syntax zeigt das eBook anhand
kleiner Beispiele auch die praktische Anwendung von Java. Das Buch geht auch auf
elementare Klassen aus der Java-Standardbibliothek ein, etwa zur Bearbeitung von
Zeichenketten, Daten, Zeiten, Arrays und Aufzählungen sowie zur Verwaltung eigener
Threads.
Das Buch ist in kleine Kapitel strukturiert, die jeweils unabhängig voneinander gelesen
werden können. Das obligatorische Hello World!-Kapitel gibt eine Schnelleinführung in
die Konzepte von Java. Anschließend folgen die prozeduralen Sprachelemente (Variablen,
Operatoren, Schleifen etc.), bevor ich im Detail auf die objektorientierten Sprachelemente
von Java eingehe. Die folgende Liste gibt einen ersten Überblick:
Hello World
Lambda-Ausdrücke
Variablen, Arrays, Enums
Exceptions
Strings, Datum und Uhrzeit
Pakete und Bibliotheken
Operatoren
Multi-Threading
Verzweigungen und Schleifen
Generics und Collections
Methoden
Annotationen
Klassen, Vererbung, Schnittstellen
Nicht Thema dieses eBooks sind die Installation von Java, eine Einführung in Eclipse
oder andere grafische Entwicklungsumgebungen sowie eine umfassende Beschreibung der Java-Standardbibliotheken. Ebenfalls unbehandelt bleiben alle spezifischen
Besonderheiten, die für die Entwicklung von Enterprise-Anwendungen (Java EE) bzw. von
Android-Apps gelten. Das eBook konzentriert sich somit auf die elementaren Sprachelemente von Java.
Java auf den Punkt gebracht
INHALTSVERZEICHNIS
ebooks.kofler
8
Java 8
Dieses eBook ist aktuell zu Java 8 und berücksichtigt alle wesentlichen Neuerungen.
Damit Ihnen dieses eBook auch dann weiterhilft, wenn Sie noch mit Java 7 arbeiten oder
alten Java-6-Code warten müssen, weise ich bei neuen Sprachelementen darauf hin, mit
welcher Java-Version diese eingeführt wurden.
Zielgruppe
Mein primäres Ziel beim Verfassen dieses eBooks war es, die wichtigsten bzw. am
häufigsten eingesetzten Sprachelemente von Java knapp und klar darzustellen. Insofern
kann und will dieses eBook nicht 1000-seitige Java-Bücher ersetzen, die auch auf unzählige Spezialfälle eingehen, möglichst viele Klassen der Java-Standardbibliothek vorstellen
oder eine Hilfestellung beim Programmieren-Lernen bieten.
Dieses eBook richtet sich somit an Programmierer/-innen, die mit den Grundideen objektorientierter Programmierung bereits vertraut sind – sei es in Java oder in einer anderen
objektorientierten Programmiersprache. Die Gliederung in überschaubare Kapitel
erleichtert ein rasches Nachschlagen im Sinne einer Java-Syntaxreferenz. Zugleich ist
dieses eBook eine ideale Hilfestellung, wenn Sie von einer anderen Programmiersprache
auf Java umsteigen oder häufig zwischen verschiedenen Programmiersprachen wechseln.
Michael Kofler im Januar 2014
http://kofler.info
PS: Zu besonderem Dank bin ich Christian Ullenboom verpflichtet. Er hat die erste
Fassung dieses eBooks noch unter dem damaligen Titel Die Java-Syntax durchgesehen
und mich auf diverse Ungenauigkeiten hingewiesen. (Für alle Fehler, die eventuell
weiterhin vorhanden sind, bin natürlich alleine ich verantwortlich!) Seine online und bei
Galileo erschienen Bücher Java ist auch eine Insel und Java – Mehr als eine Insel mit
einem Gesamtumfang von mehr als 2700 Seiten gelten als die deutschsprachige JavaReferenz.
1
Hello World!
Eine minimalistische Version von Hello World! für Java sieht so aus:
public class H e l l o W orld {
public static void main( String [] args ) {
System . out . println ( " Hello World ! " ) ;
}
}
Um dieses Programm zu kompilieren und auszuführen, müssen Sie die folgenden
Kommandos eingeben:
javac H e l l o W o rld. java
java H e l l o W o rld
Alternativ können Sie dieses Programm natürlich auch in einen Entwicklungsumgebung
wie Eclipse oder Java Beans entwickeln. IDEs (also Integrated Development Environments)
sind aber nicht Thema dieses eBooks.
1.1 Der Programmcode
Obwohl der Code nur drei Zeilen umfasst (wenn man einmal von den Klammern absieht),
gibt es dazu eine Menge zu sagen:
Jedes Java-Programm muss in Klassen organisiert werden. Hello World besteht aus
einer einzigen öffentlichen Klasse (public class HelloWorld).
Java auf den Punkt gebracht
ebooks.kofler
1 Hello World!
1.2 Der Java-Compiler javac
10
Java-Code wird in Textdateien mit der Endung *.java gespeichert. Dabei ist es
entscheidend, dass der Dateiname exakt mit dem Klassennamen übereinstimmt,
in diesem Beispiel also HelloWorld.java. Achten Sie auch auf die Groß- und Kleinschreibung! (Es ist üblich, dass die Namen von Klassen mit einem Großbuchstaben
beginnen.)
Der Startpunkt eines Java-Programms ist die Methode main. Diese Methode muss
sich in einer öffentlichen Klasse befinden, und sie muss exakt wie im obigen Beispiel
deklariert werden: Die Methode muss öffentlich sein (public). Sie muss statisch definiert werden (static), damit sie verwendet werden kann, ohne vorher ein Objekt
der Klasse zu erzeugen. Die Methode darf nichts zurückgeben (void). Und sie muss
die an das Java-Programm übergebenen Parameter in einem Zeichenketten-Array
entgegennehmen (String [] args). Die Auswertung von args ist optional; das HelloWorld-Beispiel verzichtet darauf.
Innerhalb von main wird mit println die Zeichenkette ‘Hello World!’ im Terminal
bzw. unter Windows im Eingabeaufforderungsfenster ausgegeben. println ist eine
Methode der Klasse PrintStream. Der Zugriff auf ein Objekt der PrintStream-Klasse
erfolgt mit System.out.
Dabei ist System eine Klasse des Pakets java.lang der Java-Standardbibliothek. Alle
Klassen dieses Pakets stehen Java-Programmen standardmäßig zur Verfügung. out
ist ein statisches Feld der System-Klasse. Es verweist auf ein PrintStream-Objekt zur
Ausgabe von Texten auf der Standardausgabe. Dieses Objekt wird beim Start des JavaProgramms automatisch erzeugt.
1.2 Der Java-Compiler javac
An den Java-Compiler wird im einfachsten Fall der Name der zu kompilierenden Codedatei übergeben. Sofern javac keine Syntaxfehler bemerkt, erzeugt es eine neue Datei
mit der Endung *.class. Diese Datei enthält den Java-Byte-Code. Dabei handelt es sich
um einen Zwischencode, der es dem Java-Interpreter ermöglicht, das Programm effizient
auszuführen.
Java auf den Punkt gebracht
ebooks.kofler
1 Hello World!
1.2 Der Java-Compiler javac
11
Die vom Java-Compiler produzierten Dateien sind somit nicht unmittelbar ausführbar. Die
Verwendung eines Zwischencodes hat den Vorteil, dass ein unter Windows kompiliertes
Java-Programm auch unter Linux oder OS X ausgeführt werden kann. Diese Plattformunabhängigkeit zählte ursprünglich zu den wichtigsten Faktoren für den Erfolg von Java.
Jede öffentliche Java-Klasse, -Schnittstelle oder -Enumeration muss in einer eigenen
Datei definiert werden. Der Quellcode eines Java-Programms besteht also zumeist aus
mehreren Dateien. Zum Kompilieren reicht es dennoch aus, nur die .java-Datei mit
main() zu übergeben. Der Kompiler berücksichtigt automatisch alle Abhängigkeiten zu
anderen .java-Dateien und kompiliert diese Dateien bei Bedarf ebenfalls neu.
Das Verhalten des Java-Compiler können Sie mit diverse Optionen steuern. Besonders
wichtig sind die folgenden beiden Optionen:
-encoding=utf8: Diese Option gibt an, in welchem Zeichensatz die Quelltextdateien
codiert sind. Es hängt von der Java-Version und dem Betriebssystem ab, welchen
Zeichensatz Java erwartet. Java 7 verarbeitet standardmäßig unter Linux und OS X
UTF8-Dateien, unter Windows hingegen cp1252. Dieser Windows-spezifische Zeichensatz hat Ähnlichkeiten zu ISO-8859-1, ist damit aber nicht ganz identisch. Wenn Ihre
Code-Dateien einen anderen Zeichensatz verwenden als Java erwartet, müssen
Sie den Zeichensatz mit der Option -encoding angeben. Ausführliche Informationen zum Umgang von Java mit unterschiedlichen Zeichensätzen bietet das Kapitel
Zeichenketten.
-cp: Wenn Ihr Java-Projekt auf zusätzliche Java-Dateien bzw. Bibliotheken (*.classund *.jar-Dateien) zurückgreift, geben Sie mit der Option -cp deren Ort an.
Die Abkürzung cp steht dabei für classpath, also den Verzeichnispfad für Klassendateien. Tipps zum Umgang mit externen Bibliotheken finden Sie in Kapitel
Pakete und Bibliotheken.
Java auf den Punkt gebracht
ebooks.kofler
1 Hello World!
1.4 Elementare Syntaxregeln
12
1.3 Der Java-Interpreter java
Für die eigentliche Ausführung eines Java-Programms ist der Java-Interpreter java
verantwortlich. An das Kommando wird der Name der Klasse übergeben, die die mainMethode enthält, also den Startpunkt des Programms.
Auch das Verhalten von java kann durch diverse Optionen beeinflusst werden. Besonders
wichtig ist dabei wiederum -classpath zur Angabe des Speicherorts externer Bibliotheken.
Wenn Sie wissen möchten, welche Java-Version auf Ihrem Rechner installiert ist, führen
Sie java mit der Option -version aus:
java - version
java version " 1.8.0 - ea "
Java ( TM ) SE Runtime E n v i r o n men t ( build 1.8.0 - ea - b123 )
Java HotSpot ( TM ) 64 - Bit Server VM ( build 25.0 - b65 , mixed mode )
1.4 Elementare Syntaxregeln
Die Strukturierung des Codes erfolgt durch geschwungene Klammern {}.
Jede Java-Anweisungen muss mit einem Strichpunkt abgeschlossen werden.
Java-Anweisungen dürfen über mehrere Zeilen reichen. Der Java-Compiler ist dabei
sehr flexibel, was den Umgang mit Leerzeichen und Zeilenumbrüche betrifft. So wird
das folgende Kommando anstandslos akzeptiert:
System . out .
println ( " Hello World " ) ;
Zeichenketten werden in doppelte Anführungszeichen gestellt ("abc"). Sie dürfen nicht
über mehrere Zeilen reichen, können aber mit dem Operator + verbunden werden.
String s = " eine lange " +
" Z e i c h e n ket te" ;
Java auf den Punkt gebracht
ebooks.kofler
1 Hello World!
1.4 Elementare Syntaxregeln
13
Code-Einrückungen sind optional und nicht Teil der Java-Syntax.
Zwischen Groß- und Kleinschreibung wird unterschieden, sowohl bei Schlüsselwörtern als auch bei Klassen-, Methoden- und Variablennamen.
Der Zugriff auf Objekt- bzw. Klassenvariablen (Felder) erfolgt in der Form objekt.feld
oder Klasse.statischesFeld.
Methoden werden in der Form objekt.methode() oder Klasse.statischeMethode()
aufgerufen. Dem Methodennamen müssen runde Klammern folgen, auch wenn keine
Parameter übergeben werden. In Java ist es üblich, dass Methodennamen mit Kleinbuchstaben beginnen.
Java kennt keine Funktionen. Es ist immer von Methoden die Rede, egal ob diese ein
Ergebnis zurückgeben oder nicht.
Java kennt keine Eigenschaften. Anstelle von Eigenschaften müssen Sie entweder
Felder oder get- und set-Methoden verwenden.
In der Java-Standardbibliothek sind in Paketen unzählige Klassen vordefiniert. Das
Paket java.lang steht in allen Java-Programmen standardmäßig zur Verfügung, d. h.,
die dort enthaltenen Klassen können ohne Angabe des Paketnamens genutzt werden.
Bei allen anderen Klassen müssen Sie entweder den Paketnamen voranstellen (z. B.
java.util.Random) oder zu Beginn Ihres Programms einen sogenannten Import
durchführen (import
java.util.Random oder import java.util.*). Vergessen Sie
darauf, beklagt sich der Java-Compiler darüber, dass er das Symbol (also den Klassennamen) nicht kennt. Das Kapitel Pakete und Importe geht auf die Details dieser
Mechanismen ein.
Java kümmert sich selbst um die Speicherverwaltung. Der Speicherplatz von nicht
mehr benötigten Objekten (d. h. von Objekten, auf die keine Variable mehr verweist),
wird automatisch durch einen Hintergrundprozess mit dem Garbage Collector wieder
freigegeben. Als Programmierer müssen Sie sich daher nicht um die Speicherverwaltung kümmern.
Java auf den Punkt gebracht
1 Hello World!
1.4 Elementare Syntaxregeln
ebooks.kofler
14
Achtung
Ein Strichpunkt für sich ist syntaktisch erlaubt und gilt als eine Art leeres Kommando.
Im folgenden Beispiel wird die for-Schleife zwar korrekt durchlaufen; mit jedem
Schleifendurchgang wird aber nur das leere Kommando ; ausgeführt! Die printlnMethode wird aber nur einmal nach dem Ende der Schleife ausgeführt.
// falsch ; println wird nur einmal ausgef ü hrt
// ( Ausgabe 10)
int i ;
for ( i =0; i <10; i ++) ;
System . out . println ( i ) ;
// richtig ; println wird 10 x ausgef ü hrt
// ( Ausgabe 0 bis 9)
for ( i =0; i <10; i ++)
System . out . println ( i ) ;
Java-Schlüsselwörter
Die folgenden Schlüsselwörter sind reserviert und dürfen nicht als Namen von Variablen,
Klassen etc. verwendet werden:
abstract
assert
boolean
break
byte
case
catch
char
class
const
continue
default
do
double
else
enum
extends
final
finally
float
for
if
goto
i m p l e m ents
import
i n s t a n ceof
int
interface
long
native
new
package
private
protected
public
return
short
static
strictfp
super
switch
s y n c h r o niz ed
this
throw
throws
transient
try
void
volatile
while
Java auf den Punkt gebracht
ebooks.kofler
1 Hello World!
1.4 Elementare Syntaxregeln
15
Kommentare im Java-Code
Es gibt drei Möglichkeiten, um Kommentare in den Quellcode zu integrieren:
Einzeilige Kommentare werden mit // eingeleitet und reichen bis zum Ende der Zeile.
Mehrzeilige Kommentare beginnen mit /* und enden mit */.
Javadoc-Kommentare werden mit /** eingeleitet und enden ebenfalls mit */.
Diese Kommentare können vom Kommando javadoc ausgewertet und zu einem
HTML-Dokument verarbeitet werden, das alle Klassen, Felder, Methoden, Parameter etc. eines Java-Projekts beschreibt. Innerhalb der Javadoc-Kommentare
können einzelne Textpassagen durch Schlüsselwörter wie @author name oder
@param parametername beschreibung gekennzeichnet werden.
2
Variablen, Arrays und
Enums
In Variablen speichern Sie alle elementaren Daten sowie Referenzen auf alle Objekte,
die Sie in Ihrem Programm nutzen. Arrays bieten Ihnen darüber hinaus die Möglichkeit,
unkompliziert mehrere gleichartige Daten bzw. Objekte zu verwalten. Zu den weiteren
Themen dieses Kapitels zählen die elementaren Datentypen von Java (int, double etc.),
deren Boxing-Varianten, die Typumwandlung durch Casting sowie der Umgang mit
Konstanten und Enums (also Konstantenaufzählungen).
2.1 Variablen
Variablen müssen vor ihrer Verwendung mit Typangabe deklariert werden. Zusammen
mit der Deklaration kann auch die Initialisierung erfolgen. Jede Variable muss initialisiert
werden, bevor sie das erste Mal ausgelesen wird.
int a ;
long b =3;
Variablen können nur innerhalb des Codeblocks verwendet werden, in dem sie definiert
sind, nicht aber außerhalb.
Java auf den Punkt gebracht
2 Variablen, Arrays und Enums
2.1 Variablen
ebooks.kofler
17
{
double x =3;
{
System . out . println ( x );
}
}
System . out . println (x ) ;
// ok
// Fehler , x ist hier unzug ä nglich
Variablen, die auf Klassenebene definiert sind, werden auch Felder (Fields) genannt.
Innerhalb der Methoden einer Klasse greifen Sie mit name auf derartige Variablen zu.
Wenn es innerhalb der Methode einen gleichnamigen Parameter oder eine lokale Variable
gibt, ermöglicht this.name den Zugriff auf das außerhalb deklarierte Feld. Anwender der
Klasse können nur auf Felder zugreifen, die mit public deklariert sind.
class M e i n e K l ass e {
public int var1 ;
private int var2 ;
// K o n s t r ukt or
public M e i n e K l asse( int var1 , int var2 ) {
this. var1 = var1;
this. var2 = var2;
}
// a l l g e m eine Methode
public int berechne () {
return var1 + var2 ;
}
}
Java auf den Punkt gebracht
2 Variablen, Arrays und Enums
ebooks.kofler
2.1 Variablen
18
Variablennamen
Für die Bildung von Variablennamen gelten die folgenden Regeln:
Der Name darf aus beliebig vielen Buchstaben, Ziffern sowie den Zeichen _ und $
zusammengesetzt werden. Der Einsatz des Zeichens $ ist nur für automatisch generierten Code empfohlen.
Der Name darf nicht mit einer Ziffer beginnen.
Der Name darf nicht mit Java-Schlüsselwörtern wie if oder break übereinstimmen.
Ebenfalls unzulässig sind die booleschen Literale true und false sowie das NullLiteral null.
Internationale Zeichen wie äöüß sind erlaubt, können aber zu Problemen führen,
wenn der Zeichensatz der Java-Codedatei nicht den Erwartungen des Java-Compilers
entspricht. Abhilfe: javac-Option -encoding. Besser ist es in der Regel, auf den
Einsatz internationaler Zeichen zu verzichten.
Es wird zwischen Groß- und Kleinschreibung unterschieden.
Die obigen Regeln gelten auch für die Namen von Klassen, Aufzählungen (Enums) und
anderen Java-Konstrukten. Es ist üblich (aber nicht zwingend vorgeschrieben), Variablenund Methodennamen mit Kleinbuchstaben zu beginnen lassen, Klassennamen dagegen
mit Großbuchstaben. Für die Namen von Konstanten werden normalerweise ausschließlich Großbuchstaben verwendet.
Elementare Datentypen
Java kennt die in der folgenden Tabelle aufgezählten elemantaren Datentypen (primitive
data types, siehe Tabelle 2.1). Beim Umgang mit den elementaren Java-Datentypen sind
einige Besonderheiten zu beachten:
Vorzeichen: Im Gegensatz zu vielen anderen Programmiersprachen gibt es in Java
keine vorzeichenlosen Integerzahlen. Es existiert allerdings der Operator >>>, mit dem
eine Integerzahl bitweise so nach rechts verschoben wird, als würde es sich um eine
Zahl ohne Vorzeichen handeln.
Java auf den Punkt gebracht
2 Variablen, Arrays und Enums
ebooks.kofler
2.1 Variablen
Datentyp
Beschreibung
byte
ganze Zahl, 1 Byte, Zahlenbereich -128 bis +127
short
ganze Zahl, 2 Byte, Zahlenbereich -32768 bis +32767
int
ganze Zahl, 4 Byte, Zahlenbereich 231 (ca. 2 109 )
long
ganze Zahl, 8 Byte, Zahlenbereich 263 (ca. 9 1018 )
boolean
true oder false
char
ein Unicode-Zeichen
double
Fließkommazahl, 8 Byte, 16 Stellen (max. ca. 2 10308 )
float
Fließkommazahl, 4 Byte, 8 Stellen (max. ca. 3 1038 )
19
Tabelle 2.1: Elementare Java-Datenypen
Überlaufkontrolle: Java führt bei Berechnungen keine Überlaufkontrolle durch. Wenn
der zulässige Zahlenbereich bei Integer-Berechnungen überschritten wird, kommt es
zu falschen Ergebnissen. Bei Fließkommazahlen lautet das Ergebnis Infinity bzw.
-Infinity. In beiden Fällen kommt es zu keiner Fehlermeldung oder Exception!
Division durch Null: Bei Integer-Berechnungen löst eine Division durch 0
eine ArithmeticException aus. Bei Fließkommazahlen tritt hingegen keine Exception
auf, das Ergebnis lautet Infinity bzw. -Infinity.
Rundungsfehler: Bei Berechnungen mit Fließkommazahlen kommt es zu Rundungsfehlern. Das ist kein Java-Problem, sondern hat mit der binären Darstellung von
Zahlen zu tun und ist unvermeidbar.
Erschwert wird die Situation allerdings dadurch, dass die Fließkommaarithmetik
in Java ein wenig von der anderer Programmiersprachen abweicht, die zumeist
konform zum IEEE-Standard 754/854 sind. Das betrifft insbesondere die oben schon
erwähnte fehlende Überlaufkontrolle. Leseempfehlungen: einerseits die mittlerweile
legendäre Kritik an Javas Fließkommaarithmetik des Berkeley-Professors William
Java auf den Punkt gebracht
2 Variablen, Arrays und Enums
2.1 Variablen
ebooks.kofler
20
Kahan (veröffentlicht 1998), andererseits eine Zusammenfassung seither realisierter
Verbesserungen (veröffentlicht 2008).
Wenn Sie Rundungsfehler ausschließen möchten, z. B. beim Rechnen mit Geld,
können Sie die BigDecimal-Klasse einsetzen. Deren Anwendung ist freilich sehr
unhandlich, weil Sie dann Operatoren wie + und - nicht verwenden können und stattdessen Methoden wie add und subtract gebrauchen müssen.
double x =10;
x = x /0.0; // x enthä lt Infinity
int i =100000;
i=i*i;
// i enthä lt 1 4 1 0 0 65 408
double y =1 E250;
y=y*y;
// y enthä lt Infinity
double a = 0.7;
double b = 0.9;
double c = a + 0.1;
double d = b - 0.1;
System . out . println (c == d) ;
System . out . println (c - d );
// liefert false
// liefert - 1 . 1 1 0 2 2 3 0 2 46 251 565E - 16
Hinweis
Besonderheiten beim Umgang mit Zeichen (char) und Zeichenketten (String) werden
im Kapitel Zeichenketten beschrieben.
Literale
Standardmäßig werden ganze Zahlen im Java-Code als int-Zahlen interpretiert,
Fließkommazahlen als double-Zahlen. Das folgende Listing fasst Syntaxvarianten für
Zahlen-Literale zusammen. char- und String-Literale finden Sie abermals im Kapitel
Zeichenketten.
Java auf den Punkt gebracht
ebooks.kofler
2 Variablen, Arrays und Enums
2.1 Variablen
21
int i1 = 123;
int i2 = 0 xABCD ;
// hexadezimal , e n t s p r icht 43981
int i3 = 0123;
// oktal , e n t s p r icht 83
int i4 = 0 b110;
// bin är , e n t s p r icht 6 ( ab Java 7)
int i5 = 1 _ 237 _ 343 ; // dezimal mit T a u s e n d e r t ren nun g ( ab Java 7)
long l1 = 1 2 3 4 5 6 7 8 90 123 45L ; // long
boolean b1 = true ;
boolean b2 = false ;
double d1 = 1.0;
double d2 = 1.7 E4 ; // E x p o n e ntia l- Schreibweise , = 17000
float f = 1.0 F;
// float
Rechnen mit double-Zahlen
Elementare mathematische Funktionen wie Sinus, Cosinus und die Quadratwurzel sind
als statische Methoden in der Math-Klasse enthalten. Dort ist auch die Konstante PI definiert. Das folgende Listing gibt einige Anwendungsbeispiele.
double x =2.4 , y =0.7;
double sinus
= Math . sin ( x ) ;
double cosinus = Math . cos (0.3 * Math. PI ) ;
double quadrat = Math . pow (x , 2) ;
// e n t s p ri cht x * x
double qwurzel = Math . sqrt ( x ) ;
double minimum = Math . min (x , y) ;
// Minimum , liefert 0.7
double absolut = Math . abs ( - 0.3) ;
// liefert 3
long n1
= Math . round (2.3) ;
// liefert 2
long n2
= Math . round (2.5) ;
// liefert 3
Hinweis
Die Schreibweise x^2 im Sinne von x*x ist in Java nicht zulässig. Der Operator ^ dient
vielmehr zur Verknüpfung logischer Ausdrücke oder binärer Zahlen durch exklusives
Oder. Zum Potenzieren verwenden Sie die Methode Math.pow(x, n).
Java auf den Punkt gebracht
2 Variablen, Arrays und Enums
2.1 Variablen
ebooks.kofler
22
Zufallszahlen
Zur Erzeugung von Pseudozufallszahlen verwenden Sie ein Objekt der Klasse
java.util.Random. Die folgenden Zeilen zeigen die Anwendung:
java. util. Random r = new java . util . Random () ;
double x = r . n e x t D o uble() ;
// zwischen 0.0 ( inkl .) und 1.0 ( exkl .)
int i
= r . nextInt (100) ;
// zwischen 0 und 99
long n
= r . nextLong () ;
// gesamter long- Z a h l e n b e reic h
double g = r . n e x t G a u ss ian() ; // n o r m a l v e rtei lt ( M i t t e l wert 0 ,
// S t a n d a r d a b wei chu ng 1)
Unterschiede zwischen elementaren Datentypen und Objekten (reference types)
Für elementaren Datentypen stehen keine Felder oder Methoden zur Verfügung. Ihre
Daten werden bei Zuweisungen kopiert. Der Inhalt der beiden Variablen a und b im
folgenden Beispiel ist daher vollkommen unabhängig voneinander:
int a =3;
int b = a ;
b =4;
System . out . println (a + " " + b) ;
// Ausgabe : 3 4
Im Gegensatz zu elementaren Datentypen müssen Objekte explizit erzeugt werden (in
der Regel mit new). Bei Zuweisungen wird nur eine Referenz kopiert. Beide Variablen
verweisen nun auf dieselben Daten. Änderungen wirken sich daher für beide Variablen
aus. Im folgenden Beispiel verweisen p1 und p2 auf dasselbe Point-Objekt:
import java . awt . Point ;
...
Point p1 = new Point (3 , 3) ;
Point p2 = p1 ;
p2 .x = 4;
System . out . println ( p1 . x + " " + p2 .x ) ;
// Ausgabe : 4 4
Java auf den Punkt gebracht
2 Variablen, Arrays und Enums
ebooks.kofler
2.1 Variablen
23
Wenn Sie eine unabhängige Kopie eines Objekts benötigen, müssen Sie dieses kopieren
bzw. duplizieren (klonen). Einige Klassen der Java-Standardbibliothek sehen hierfür die
clone-Methode vor und implementieren die Schnittstelle Cloneable. Beachten Sie aber,
dass clone nur das unmittelbare Objekt dupliziert (shallow clone). Wenn das Objekt auf
weitere Objekte verweist und auch diese Objekte dupliziert werden sollen, ist ein deep
clone erforderlich, was in Java z. B. durch Serialisierungsmethoden ermöglicht wird.
Generell haben weder die Methode clone noch die Schnittstelle Cloneable in JavaKreisen einen guten Ruf. Der Java-Chefentwickler Joshua Bloch rät in einem Interview
davon ab, Cloneable und clone in eigenen Klassen zu implementieren und empfiehlt
stattdessen die Entwicklung eines eigenen Copy-Konstruktors.
Für den Umgang mit simplen Point-Objekten reicht clone aber vollkommen aus, um p1
unabhängig von p2 zu machen:
Point p1 = new Point (3 , 3) ;
Point p2 = ( Point ) p1 . clone () ;
p2 .x = 4;
System . out . println ( p1 . x + " " + p2 .x ) ;
// Ausgabe : 3 4
Das unterschiedliche Verhalten hat auch Konsequenzen auf die Parameterübergabe:
Bei elementaren Typen werden die Daten kopiert. Änderungen in der Methode haben
deswegen keinen Einfluss auf die ursprünglichen Variablen. Bei Objekten werden
hingegen wie bei Zuweisungen Referenzen kopiert. Das ist nicht nur effizient, sondern
ermöglicht es zudem, die zugrunde liegenden Daten in der Methode zu verändern.
int a =3;
Point b = new Point (3 , 3) ;
m (a , b ) ;
System . out . println (a + " " + b .x ) ; // Ausgabe : 3 4
void m ( int i , Point p ) {
i
= 4;
p. x = 4;
}
Java auf den Punkt gebracht
2 Variablen, Arrays und Enums
ebooks.kofler
2.1 Variablen
24
Zeichenketten (Strings) sind Objekte. Dennoch verhalten sie sich in mehrerlei Hinsicht
wie elementare Datentypen: Zum einen können Zeichenketten wegen ihrer tiefen Verankerung in der Java-Sprachdefinition ohne new einfach durch Zuweisungen erzeugt
werden (String s = "abc"). Zum anderen sind Zeichenketten generell unveränderlich
(immutable). Bei jedem Versuch, eine Zeichenkette zu verändern, wird daher in Wirklichkeit eine neue Zeichenkette erzeugt. Daher ist es auch unmöglich, eine als Parameter
übergebene Zeichenkette in einer Methode zu verändern.
String s1 = " abc " ;
String s2 = s1 ;
s2 = " efg " ;
// s2 zeigt jetzt auf einen neuen String !
System . out . println ( s1 + " " + s2 ) ;
// Ausgabe : abc efg
Boxing-Varianten von elementaren Datentypen
Zu allen elementaren Java-Datentypen gibt es seit Version 5 Boxed-Varianten. Die
elementaren Datentypen als Objekte verpackt, verhalten sich also wie Objekte.
Datentyp
Boxing-Variante
byte
Byte
short
Short
int
Integer
long
Long
boolean
Boolean
char
Character
double
Double
float
Float
Tabelle 2.2: Boxing-Datentypen
Java auf den Punkt gebracht
2 Variablen, Arrays und Enums
2.1 Variablen
ebooks.kofler
25
Im Unterschied zu den elementaren Datentypen können ihre Boxing-Varianten auch den
Wert null annehmen. Das ist besonders dann praktisch, wenn zwischen dem Wert 0 und
dem Zustand nicht initialisiert unterschieden werden soll. Boxed-Datentypen bieten sich
auch für Datenbankanwendungen an, weil Sie den SQL-Wert NULL abbilden können.
Die Initialisierung von Boxed-Variablen erfordert keinen Konstruktor, erfolgt also ohne
new. Die Umwandlung zwischen elementaren Datentypen und ihren Boxing-Varianten
(also das boxing und das unboxing) erfolgt automatisch.
Integer i =3;
int j = i + 1;
i = j + 1;
Boxed-Datentypen können also weitgehend wie gewöhnliche Datentypen verwendet
werden. Beachten Sie jedoch die Sonderfälle, die sich dadurch ergeben können, dass der
Zustand null erlaubt ist.
Integer i= null;
int j = i + 1;
// l ö st N u l l P o i n t e r Ex cep ti on aus
Die Klassen der Boxing-Datentypen sind in der Java-Standardbibliothek java.lang definiert und stehen ohne import in allen Java-Programmen zur Verfügung. Sie enthalten
eine Menge nützlicher Umwandlungsmethoden, die auch dann eingesetzt werden können,
wenn Sie in Ihren Variablen gewöhnliche elementare Datentypen verwenden.
double d = Double . p a r s e D o uble(" 12.34 " ) ;
String s = Long . t o H e x S t ri ng (123) ;
// 7b
Typumwandlung (Casting)
Wenn bei Zuweisungen oder in Ausdrücken unterschiedliche Datentypen vorkommen,
führt Java nach Möglichkeit eine automatische Typumwandlung durch (implizites
Casting).
Java auf den Punkt gebracht
ebooks.kofler
2 Variablen, Arrays und Enums
2.1 Variablen
26
int i1 =3;
double d1 =1.7;
double d2 = d1 * i1 ;
// double - M u l t i p l i kat ion - - > 5.1
long l1 = i1 ;
// casting int - - > long
String s1 = " abc " ;
String s2 = s1 + i1 ; // String - Verkn ü pfung - - > " abc3"
Wenn die Gefahr besteht, dass es bei der Typumwandlung zu einem Genauigkeitsverlust
(z. B. double ! float) oder zu einem Überlauf kommt (z. B. long ! int), produziert javac
einen Fehler. Das Programm kann also nicht kompiliert werden.
long l
int i
double
float
=
=
d
f
10;
l;
// javac - Fehler : possible loss of precision
= 3.14;
= d ; // javac - Fehler : possible loss of precision
Abhilfe schafft in solchen Fällen eine explizite Typumwandlung. Dazu stellen Sie dem
betreffenden Ausdruck in runden Klammern den gewünschten Datentyp voran. Sollte sich
zur Laufzeit herausstellen, dass die Umwandlung unmöglich ist oder dass dabei ein Überlauf auftritt, kommt es zu einer Exception.
long l
int i
double
float
=
=
d
f
10;
( int ) l ;
= 3.14;
= ( float ) d;
Modifizierer für die Variablendeklaration (public, private, static etc.)
Der Deklaration einer Variablen können verschiedene Schlüsselwörter vorangestellt
werden, die die Eigenschaften bzw. die Erreichbarkeit der Variablen modifizieren. Die
meisten Modifizier sind nur für Felder (fields) relevant, also für Variablen, die auf Klassenebene deklariert werden, nicht als lokale Variable innerhalb einer Methode.
Java auf den Punkt gebracht
2 Variablen, Arrays und Enums
2.2 Arrays
ebooks.kofler
Schlüsselwort
Bedeutung
public
öffentliche Variable
private
private Variable
protected
interne Variable
static
statische Variable
final
Konstante (siehe unten)
transient
wird bei der Serialisierung nicht gespeichert
volatile
Variable für Multi-Threaded-Programmierung
27
Tabelle 2.3: Schlüsselwörter, um das Verhalten von Variablen zu modifizieren
Die Schlüsselwörter public, private, protected und static werden im Kapitel Klassen
genauer beschrieben. transient bewirkt, dass der Inhalt dieser Klassenvariablen bei
einer Serialisierung des Objekts nicht gespeichert wird.
volatile bewirkt, dass der Compiler darauf verzichtet, aus Effizienzgründen auf
zwischengespeicherte Werte dieser Variable zuzugreifen. Stattdessen wird die Variable
bei jedem Zugriff neu ausgelesen. In Multi-Threaded-Programmen stellt volatile sicher,
dass das Lesen oder Schreiben der Variable ein atomarer Prozess ist, der nicht durch
einen anderen Thread gestört werden kann.
2.2 Arrays
Arrays speichern mehrere Zahlen, Zeichenketten oder andere Objekte. Die Größe des
Arrays muss beim Erzeugen festgelegt werden und kann nachträglich nicht mehr verändert werden. Die übliche Syntax zur Deklaration von Arrays lautet datentyp[] var.
Unüblich, aber ebenfalls erlaubt ist datentyp var[].
Wenn von vornherein klar ist, welche Daten ein Array aufnehmen soll, können Sie die
Elemente in der Form {e1, e2, e3} einer Array-Variable direkt zuweisen. Der Java-
Java auf den Punkt gebracht
2 Variablen, Arrays und Enums
2.2 Arrays
ebooks.kofler
28
Compiler kümmert sich dann selbstständig darum, das Array in der richtigen Größe zu
erzeugen.
int [] x ;
x = new int [5];
double [] y = new double [5];
int [] z = {17 , 19 , 25 , 12};
//
//
//
//
x als int - Array d e k l a r ier en
Array erzeugen
y d e k l a r ieren und erzeugen
z d e k l a r ieren + i n i t i a l i sie ren
Der Zugriff auf die Array-Elemente erfolgt in der Form array[n], wobei n von 0 bis zur
Elementanzahl -1 reicht. Die Anzahl der Array-Elemente geht aus dem Array-Feld (field)
length hervor. Beachten Sie, dass length keine Methode ist! Sie dürfen daher nicht zwei
runde Klammern hintanstellen, wie dies z. B. bei der gleichnamigen String-Methode
erforderlich ist!
for ( int i =0; i < x . length ; i ++)
System . out . println ( x [ i ]) ;
Arrays können auch in for-each-Schleifen durchlaufen werden. Die Array-Elemente
können dann allerdings nur gelesen, nicht aber verändert werden:
for ( int itm : x)
System . out . println ( itm );
Mehrdimensionale Arrays
Die Java-Syntax unterstützt auch mehrdimensionale Arrays. Dabei werden einfach
mehrere eckige Klammernpaare aneinandergereiht, wie die folgenden Beispiele zeigen:
int [][] x;
x = new int [8][4];
x [2][3] = 27;
double [][] y = new double [ 1 0 0 ] [ 1 00];
Java auf den Punkt gebracht
2 Variablen, Arrays und Enums
2.2 Arrays
ebooks.kofler
int [][] z = { {1 , 2 , 3} ,
{4 , 5 , 6} };
System . out . println (z [1][2]) ;
29
// liefert 6
// d r e i d i m e n s ion ale s Array ( ca. 2 MByte P l a t z b e darf)
short [][][] s = new short [ 1 0 0 ] [ 1 0 0 ][ 100 ];
In Wirklichkeit verwaltet Java allerdings nicht echte mehrdimensionale Arrays, sondern
Arrays von Arrays (vor Arrays . . . ). Im obigen Beispiel verweist x[2] auf ein einfaches
Array aus vier Elementen. Auch das length-Feld bezieht sich nur auf die Elemente der
ersten Dimension. x.length liefert deswegen 8, nicht etwa 32.
Dieses Konzept ermöglicht die unkomplizierte Realisierung von nicht-rechteckigen
Arrays. Im folgenden Beispiel zeigt x[0] auf ein Array mit einem Element, x[1] auf ein
Array mit zwei Elementen etc.
int [][] x = new int [10][];
for ( int i =0; i <10; i ++) {
x[ i ] = new int [ i +1];
}
Abbildung 2.1: Ein nicht-rechteckiges Arrays
Java auf den Punkt gebracht
2 Variablen, Arrays und Enums
ebooks.kofler
2.2 Arrays
30
Das folgende Beispiel zeigt zwei Schleifenvarianten, um alle Elemente des obigen Arrays
zu durchlaufen. Beachten Sie, dass bei der zweiten Variante die Schleifenvariable subarr
den Typ int[] aufweist. Jedes Element, das diese Schleifenvariable durchläuft, ist also
selbst ein Array.
// S c h l e i f e n v ari ant e 1: Array i n i t i a l i si eren
for ( int i =0; i < x . length ; i ++) {
for ( int j =0; j <x [ i ]. length ; j ++) {
x[ i ][ j] = i + j ;
}
}
// S c h l e i f e n v ari ant e 2: Summe a u s r e c hnen
int sum =0;
for ( int [] subarr : x ) {
for ( int itm : subarr ) {
sum += itm ; }}
System . out . println ( sum ) ;
Array-Interna und -Methoden
Während lokale Variablen vor ihrer Verwendung initialisiert werden müssen, gilt diese
Regel nicht für die Elemente eines Arrays sowie für Klassenvariablen (fields). Diese
werden beim Erzeugen des Arrays bzw. des Objekts automatisch mit 0 bzw. null initialisiert.
Java-intern sind Arrays Objekte. Es existiert allerdings nicht einfach eine Array-Klasse,
deren Instanzen dann Array-Objekte sind. Vielmehr sind Arrays eine Sonderform von
Klassen, die tief in der Java-Sprachdefinition verankert sind. Ihr Klassenname ergibt sich
aus einer oder mehreren eckigen Klammern, je nachdem, ob es sich um ein ein- oder um
ein mehrdimensionales Array handelt. Den Klammern folgt bei elementaren Datentypen
ein Buchstabe, z. B. I für int oder D für double, oder der vollständige Klassenname plus
einem Semikolon bei Objekt-Arrays:
Java auf den Punkt gebracht
2 Variablen, Arrays und Enums
2.2 Arrays
ebooks.kofler
Datentyp
Defaultwert
Integerzahlen
0
Fließkommazahlen
0.0
boolean
false
char
\u0000 (also das Unicode-Zeichen 0)
Objekte inklusive String
null
31
Tabelle 2.4: Defaultwerte für Array-Elemente und Klassenvariablen
import java . awt . Point ;
int [] x = new int [5];
System . out . println (x . getClass () . getName () ) ;
double [] y = new double [5];
System . out . println (y . getClass () . getName () ) ;
Point [][] pts = new Point [7][2];
System . out . println ( pts . getClass () . getName () ) ;
// Ausgabe :
[I
[D
[[ Ljava . awt . Point ;
Die Array-Klassen sind nur von Object abgeleitet und bieten daher keine spezifischen
Methoden. Vielmehr stellt die Klasse java.util.Arrays (beachten Sie die Endung -s!)
statische Methoden zur Verfügung, mit denen Sie Arrays initialisieren, durchsuchen,
sortieren etc. können. Beachten Sie aber, dass sämtliche Arrays-Methoden für eindimensionale Arrays konzipiert sind.
Java auf den Punkt gebracht
2 Variablen, Arrays und Enums
2.2 Arrays
ebooks.kofler
32
Methode
Funktion
binarySearch
durchsucht ein sortiertes Array.
sort
sortiert ein Array.
copyOfRange
bildet ein neues Teil-Array.
equals
vergleicht den Inhalt zweier Arrays.
fill
initialisiert die Array-Elemente mit einem Wert.
toString
liefert eine lesbare Zeichenkette des Arrays (zum Debuggen und Testen).
Tabelle 2.5: Ausgewählte Methoden der Klasse java.util.Arrays
int [] x = {7 , 12 , 3 , 9};
Arrays . sort ( x );
System . out . println ( Arrays . toString (x ) ) ;
// Ausgabe : [3 , 7 , 9 , 12]
Die Methoden sort und binarySearch setzen voraus, dass es sich bei den ArrayElementen um elementare Datentypen oder um Objekte von Klassen handelt, die
die Schnittstelle Comparable implementieren. Ist dies nicht der Fall, müssen Sie ein
zusätzliches Comparator-Objekt angeben, das zwei Objekte vergleicht. Beispiele für die
Implementierung der Comparable-Schnittstelle bzw. für eine Comparator-Klasse finden
Sie in den Kapiteln Vererbung und Schnittstellen und Collections.
Wenn Sie ein Array kopieren (duplizieren) möchten, verwenden Sie dazu am besten die
Methode clone:
int [] x = {1 , 4 , 189 , 3};
int [] y = x . clone () ;
Beachten Sie aber, dass clone nur ein sogenanntes shallow copy durchführt und dabei nur
die erste Dimension des Feldes berücksichtigt. Bei Objekt-Arrays werden nur die Objektreferenzen geklont, nicht die Objekte selbst. Eine clone-Kopie eines mehrdimensionalen
Java auf den Punkt gebracht
ebooks.kofler
2 Variablen, Arrays und Enums
2.3 Konstanten und Enums
33
Arrays liefert ein neues Array, dessen Elemente in der ersten Dimension auf dieselben
Subarrays zeigen wie im ursprünglichen Array.
Wie das folgende Beispiel beweist, können Arrays wie Objekte als Parameter an
Methoden übergeben bzw. als Ergebnis von Methoden zurückgegeben werden:
import java . util . Arrays ;
int [] x = {7 , 12 , 3 , 9};
m1 (x ) ;
System . out . println ( Arrays . toString (x ) ) ; // Ausgabe : [14 , 24 , 6 , 18]
x = m2 (5) ;
System . out . println ( Arrays . toString (x ) ) ; // Ausgabe : [0 , 1 , 2 , 3 , 4]
public static void m1 ( int [] ar ) {
for ( int i =0; i < ar . length ; i ++)
ar [ i ] * =2;
}
public static int [] m2 ( int n ) {
int [] ar = new int [ n ];
for ( int i =0; i < n ; i ++)
ar [ i ] = i ;
return ar ;
}
2.3 Konstanten und Enums
Konstanten
Java sieht keine direkte Definition von Konstanten vor. Stattdessen kann der Deklaration von Variablen das Schlüsselwort final vorangestellt werden. Es bewirkt, dass die
Variable nach der erstmaligen Initialisierung nicht mehr verändert werden kann. Versuchen Sie es dennoch, liefert javac beim Kompilieren eine Fehlermeldung.
Java auf den Punkt gebracht
2 Variablen, Arrays und Enums
ebooks.kofler
2.3 Konstanten und Enums
34
final double PI = 3 . 1 4 1 5 92 7;
Wenn final für eine Objektvariable verwendet wird, kann die Objektreferenz nicht mehr
verändert werden. Aber aufgepasst: Die Daten des Objekts können aber sehr wohl modifiziert werden! Insofern verhält sich ein finales Objekt nicht wie eine Konstante.
import java . awt . Point ;
...
final Point p = new Point (3 , 3) ;
p = new Point (4 ,4) ;
// nicht zul ä ssig
p . x = 4;
// erlaubt !
p . y = 4;
// erlaubt !
Hinweis
final kann auch bei der Deklaration von Klassen verwendet werden, hat dort aber
eine andere Bedeutung: Finale Klassen können durch Vererbung nicht erweitert
werden.
Konstantenaufzählungen (Enums)
Seit Version 5 können Sie in Java mit dem Schlüsselwort enum eine Aufzählung von
Konstanten definieren. Das folgende Listing demonstriert die Anwendung einer solchen
Aufzählung:
// D e f i n i tion
public enum Color {
RED , GREEN , BLUE , YELLOW , BLACK , WHITE }
// Anwendung
Color c = Color . GREEN ;
if( c == Color . BLACK )
System . out . println ( " schwarz ") ;
Java auf den Punkt gebracht
ebooks.kofler
2 Variablen, Arrays und Enums
2.3 Konstanten und Enums
35
Enums sind ein Sonderfall einer Klasse. Java-intern wird eine Enumeration durch die
Vererbung von der Klasse java.lang.Enum gebildet. Der Compiler macht daher aus
enum Color eine Klasse, deren Definition mit class Color extends Enum beginnt. Das ist
auch der Grund, weswegen Enumerationen nicht wie Klassen durch extends erweitert
werden können. (Java kennt keine Mehrfachvererbung in der Art class C extends A, B.
Da jede Enumeration bereits extends Enum impliziert, würde enum F extends E zu
class F extends Enum, E führen, also zu einer nicht zulässigen Mehrfachvererbung.)
Enums gelten wie Klassen als Referenztypen. Allerdings sind einfache Enum-Objekte wie
im obigen Beispiel so wie Strings unveränderlich (immutable). Sie verhalten sich daher in
vielerlei Hinsicht wie primitive Datentypen.
Die Java-Syntax bietet bei der Definition von Enums viel mehr Freiheiten als andere
Programmiersprachen. Beispielsweise können Sie eine Farbaufzählung auch mit einem
eigenen Konstruktor und privaten und öffentlichen Methoden ausstatten:
public enum Color {
RED (1 ,0 ,0),
GREEN (0 ,1 ,0),
BLUE (0 ,0 ,1),
WHITE (1 ,1 ,1),
BLACK (0 ,0 ,0),
GREY (0.7 ,0.7 ,0.7);
private double r , g , b ;
public Color ( double r , double g , double b ) {
this. r = r;
this. g = g;
this. b = b;
}
public String hexcode () {
return hex ( this. r ) + hex ( this . g) + hex ( this. b ) ;
}
Java auf den Punkt gebracht
ebooks.kofler
2 Variablen, Arrays und Enums
2.3 Konstanten und Enums
private String hex ( double x ) {
if (x <=0) return " 00 " ;
if (x >=1) return " FF " ;
return String . format (" %02 X" , ( int ) ( x * 256) );
}
}
...
Color c = Color . GREY;
System . out . println (c . hexcode () ) ; // liefert B3B3B3
Hinweis
Syntaktisch ist es möglich, Enums zu definieren, die sich kaum von gewöhnlichen
Klassen unterscheiden. Das ist in selten Fällen zweckmäßig, etwa zur Implementierung des Singleton-Entwurfsmuster, wie dies in einem Stack-Overflow-Beitrag
beschrieben ist.
36
12
Exceptions
Exceptions (Ausnahmen) geben Auskunft über in Java-Programmen aufgetretene Fehler.
Mit try-catch besteht die Möglichkeit, auf Fehler zu reagieren und das Programm fortzusetzen oder zumindest geordnet zu beenden.
Eine Besonderheit des Exception-Konzepts von Java besteht darin, dass bei der Deklaration von Methoden angegeben werden kann, welche Arten von Exceptions bei deren
Ausführung auftreten können. Der Aufruf von derart deklarierten Methoden muss durch
try-catch abgesichert werden; alternativ besteht die Möglichkeit, die übergeordnete
Methode ebenfalls so zu deklarieren, dass darin eine Exception auftreten kann.
12.1 Exception-Klassen
Formal betrachtet werden die Informationen über einen Fehler in einem Objekt transportiert, dessen Klasse von Throwable abgeleitet ist. In der Java-Standardbibliothek sind
Dutzende verschiedene Error- und Exception-Klassen definiert. Die folgende Abbildung
gibt einen ersten Überblick:
Java unterscheidet zwischen Fehlern, die im Code abgesichert werden müssen (in der
obigen Abbildung blau gekennzeichnet), und solchen, bei denen die Absicherung optional
ist (gelb). Welche Klasse in welche Gruppe fällt, hängt von der Erbfolge ab: Fehler, deren
Klasse von Error bzw. von RuntimeException abgeleitet ist, müssen nicht abgesichert
werden, alle anderen schon.
Java auf den Punkt gebracht
ebooks.kofler
12 Exceptions
12.1 Exception-Klassen
141
Abbildung 12.1: Die wichtigsten Error- und Exception-Klassen
Die Throwable-Klasse
An der Spitze der Klassenhierarchie steht Throwable. Anders als der Name vermuten
lässt, handelt es sich dabei um eine gewöhnliche Klasse, keine Schnittstelle. Objekte der
Throwable-Klasse enthalten eine Beschreibung des Fehlers sowie Informationen darüber,
wo im Code der Fehler aufgetreten ist. Dabei ist der sogenannte Stack Trace eine Liste
aller Methoden, die aufgerufen wurden, bis es zum Fehler gekommen ist. Die folgende
Tabelle fasst die wichtigsten Methoden zum Auslesen der Daten aus der ThrowableKlasse zusammen:
Java auf den Punkt gebracht
12 Exceptions
12.1 Exception-Klassen
ebooks.kofler
142
Methode
Bedeutung
getCause
gibt die Ursache des Fehlers an (ein weiteres Throwable-Objekt).
getLocalizedMessage
liefert die Fehlermeldung in der Sprache des Systems.
getMessage
liefert die englische Fehlermeldung.
getStackTrace
verrät, welche Methoden aufgerufen wurden, als es zum Fehler kam.
printStackTrace
gibt die Stack-Trace-Daten in der Konsole aus.
toString
liefert eine kurze Fehlerbeschreibung.
Tabelle 12.1: Wichtige Throwable-Methoden
Die Error-Klassen
Die von der Error-Klasse abgeleiteten Klassen beschreiben schwerwiegende Probleme,
z. B. wenn der Java-Interpreter nicht genug Speicherplatz zur Ausführung des
Programms findet. In den meisten Java-Programmen ist es nicht zweckmäßig und
mitunter auch gar nicht möglich, auf solche Fehler in einer sinnvollen Art und Weise zu
reagieren. Deswegen ist die Absicherung gegen solche Fehler optional.
Die RuntimeException-Klassen
Eine RuntimeException deutet in der Regel auf Programmierfehler (Schlampigkeitsfehler) hin. Die Entwickler der Sprache Java sind davon ausgegangen, dass solche Fehler
bereits während der Programmentwicklung im Code behoben werden. Daher ist auch
für diese Fehlerklasse eine Absicherung nicht vorgeschrieben. Ein typischer Vertreter
dieser Exception-Gruppe ist die ArrayIndexOutOfBoundException: Diese tritt auf, wenn
Sie beim Zugriff auf die Elemente eines Arrays einen zu großen Index verwenden, also
z. B. wenn Sie die Schleife für den Index so for(i=0; i<=ar.length; i++) statt korrekt so
for(i=0; i<ar.length; i++) formulieren.
Java auf den Punkt gebracht
ebooks.kofler
12 Exceptions
12.2 try-catch
143
Hinweis
Die Bezeichnung RuntimeException ist denkbar unglücklich gewählt. Jede Exception
tritt zur Laufzeit auf!
Gewöhnliche Exceptions
Alle Exceptions, die nicht von Error oder RuntimeException abgeleitet sind, gelten als
gewöhnliche Exceptions. Methoden, die solche Exceptions auslösen können, müssen mit
throws entsprechend markiert werden. Der Aufruf solcher Methoden muss entweder
durch try-catch abgesichert werden, oder die übergeordnete Methode muss ebenfalls
mit throws XxxException markiert werden.
Eine Gruppe von Fehlern, die in der Praxis besonders häufig vorkommt, ist IOException.
Derartige Fehler treten auf, wenn Sie mit Dateien hantieren. Diese Art von Fehlern ist
über die Basisklasse IOException zu einer eigenen Gruppe zusammengefasst. Die
IOException weist gegenüber der normalen Exception keine weiteren Methoden auf; ihre
Existenzberechtigung besteht darin, dass Sie damit sämtliche Ein- und Ausgabefehler in
einer einzigen catch-Anweisung verarbeiten können.
12.2 try-catch
Das folgende Listing zeigt beispielhaft die try-catch-Syntax zur Fehlerabsicherung. Mit
try leiten Sie einen Block ein, der Java-Anweisungen enthält, die eventuell einen Fehler
auslösen können.
Eine Reihe von catch-Blöcken enthält Code, in dem Sie auf Fehler reagieren. Wenn
es mehrere catch-Blöcke gibt, zwingt der Java-Compiler zwingt Sie dazu, zuerst die
speziellen und erst danach allgemeine Exception-Klassen anzugeben; wenn Sie diese
Reihenfolge missachten, tritt ein Compile-Fehler auf.
Der finally-Block enthält schließlich Code, der immer ausgeführt wird, egal, ob vorher
ein Fehler aufgetreten ist oder nicht. In diesem Codeblock werden üblicherweise
Aufräumarbeiten durchgeführt; wenn Sie beispielsweise mit try das Lesen einer Text-
Java auf den Punkt gebracht
ebooks.kofler
12 Exceptions
12.2 try-catch
144
datei abgesichert haben, ist finally der richtige Ort, um die Datei mit close wieder zu
schließen.
Sowohl catch als auch finally sind für sich optional. Die gesamte try-Konstruktion
muss aber zumindest eine dieser beiden Ergänzungen aufweisen. Nur try ohne catch
und ohne finally ist nicht erlaubt.
try {
... // fehleranf ä lliger Java- Code ( z .\ ,B .\ f ü r I /O )
...
} catch ( F i l e N o t F o u n d Exc ep ti on e ) {
... // Datei konnte nicht gefunden werden
} catch ( I O E x c e p tion e ) {
... // anderer IO- Fehler
} catch ( Exception e) {
... // anderer Fehler
} finally {
... // diesen Code immer ausfü hren
}
Ab Java 7 können Sie in einem catch-Block mehrere Exceptions gemeinsam verarbeiten:
// mehrere E x c e p t ions in einem catch - Block
try {
...
} catch ( F i l e N o t F o u n d Exc ep ti on|
U n s u p p o r t e d E n c od in gE xc ep t io n e ) {
...
}
Ebenfalls seit Java 7 besteht die Möglichkeit, in einem Parameter von try mehrere durch
Kommata getrennte Ressourcen zu öffnen. Die dort definierten Variablen stehen in der
gesamten try-catch-Konstruktion zur Verfügung. Java garantiert, dass die Ressourcen
am Ende der try-catch-Konstruktion automatisch geschlossen werden – egal, ob bei der
Code-Ausführung ein Fehler auftritt oder nicht. Die einzige Voraussetzung besteht darin,
Java auf den Punkt gebracht
ebooks.kofler
12 Exceptions
12.2 try-catch
145
dass die Klassen der in try(...) geöffneten Ressourcen die Schnittstelle AutoCloseable
implementieren.
try ( IOKlasse1 x = new IOKlasse1 ( " d a t e i n a me1" ) ,
IOKlasse2 y = new IOKlasse2 ( " d a t e i n a me2" ) ) {
... // x und y benutzen
} catch ( I O E x c e p tion e ) {
... // F e h l e r b e h an dlu ng
}
Diese try-Variante darf ohne catch und finally formuliert werden, weil es ja einen impliziten finally-Code zum Schließen der geöffneten Ressourcen gibt.
Exception-Weitergabe
Gewöhnliche Exceptions müssen Sie entweder durch try-catch absichern, oder die
übergeordnete Methode mit throws XxxException kennzeichnen. Für die Gruppe der
RuntimeExceptions gilt dies aber nicht. Was passiert also, wenn eine RuntimeException
in einem nicht durch try-catch abgesicherten Codeabschnitt auftritt?
In solchen Fällen bricht Java die Ausführung der aktuellen Methode ab und springt an die
Stelle im Code zurück, an der die Methode aufgerufen wurde. Die Exception wird nun dort
ausgelöst. Gibt es auch hier keine Fehlerabsicherung, wiederholt sich dieses Spiel durch
die gesamte Liste der Methodenaufrufe (also durch den gesamten Call Stack). Sollte es
bis zu main keine Fehlerabsicherung geben, wird das Programm letztlich mit einem Laufzeitfehler abgebrochen.
Im folgenden Beispiel ruft main die Methode m1 auf, m1 dann m2. Dort tritt wegen einer
schlampig programmierten Schleife eine ArrayIndexOutOfBoundsException auf. Da weder
m2 noch m1 abgesichert sind, wird die Exception schrittweise bis zu main zurückgereicht.
Dort wird der Fehler abgefangen und eine Fehlermeldung ausgegeben.
Java auf den Punkt gebracht
12 Exceptions
12.2 try-catch
ebooks.kofler
146
public class Test {
public static void main( String [] args ) {
try {
m1 () ;
} catch ( Exception e ) {
e . p r i n t S t a c kTr ace() ;
}
}
public static void m1 () {
m2 () ;
}
public static void m2 () {
int [] ar = {1 , 2 , 3};
int sum = 0;
for ( int i =1; i <=3; i ++)
sum += ar [ i ];
}
// falsch !
}
// F e h l e r m e ldun g:
java. lang. A r r a y I n d e x O u t O fB ou nd sE x ce pt io n: 3
at testme . m2 ( testme . java :18)
at testme . m1 ( testme . java :11)
at testme . main ( testme . java :4)
Beispiele
Das folgende Beispiel zeigt, wie ein PrintStream-Objekt verwendet wird, um eine Textdatei zu schreiben. Beachten Sie, dass im finally-Block das Schließen der Datei
neuerlich abgesichert ist. Die close-Methode kann ebenfalls eine IOException auslösen,
z. B. wenn das Dateisystem voll ist und die Datei nicht vollständig auf dem Datenträger
synchronisiert werden kann.
Java auf den Punkt gebracht
ebooks.kofler
12 Exceptions
12.2 try-catch
import java . io . * ;
...
P r i n t S tre am ps = null;
try {
// fehleranf ä lliger Code
ps = new P r i n t S t re am(
new F i l e O u t p u tS tre am(" out . txt " ) , false , " UTF - 8 ") ;
ps . printf ( " Zwei Zahlen % d % d\ n " , 1 , 2) ;
} catch ( I O E x c e p tion e ) {
// F e h l e r m el dung
System . out . println ( " Fehler : " + e. g e t M e s sage() ) ;
} finally {
// Datei schlie ß en ( immer )
try {
if ( ps != null)
ps . close () ;
} catch ( I O E x c e pt ion ex ) {
System . out . println ( " Fehler bei close ... " );
}
}
Ab Java 7 lässt sich dieselbe Aufgabe wesentlich kompakter formulieren:
import java . io . * ;
...
try ( P r i n t S t ream ps = new P r i n t S t ream(
new F i l e O u t p u tSt rea m( " out . txt " ) , false , " UTF -8 " ) ) {
// fehleranf ä lliger Code
ps . printf ( " Zwei Zahlen % d % d\ n " , 1 , 2) ;
} catch ( I O E x c e p tion e ) {
// F e h l e r m el dung
System . out . println ( " Fehler : " + e. g e t M e s sage() ) ;
}
// das P r i n t S tream- Objekt ist hier auf jeden Fall g e s c h l ossen
147
Java auf den Punkt gebracht
ebooks.kofler
12 Exceptions
12.3 Fehleranfällige Methoden deklarieren (throws)
148
12.3 Fehleranfällige Methoden deklarieren (throws)
Methoden, die eine gewöhnliche Exception auslösen können (also keinen Error und keine
RuntimeException), müssen mit throws XxxException deklariert werden. Es dürfen auch
mehrere, durch Kommata getrennte Exceptions angegeben werden.
void m1 () throws F i l e N o t FoundEx ception , T i m e o u t E x ce pti on {
... // Code , der die genannten E x c e p t ions auslö sen kann
}
In der Java-Klassenbibliothek sind unzählige Methoden auf diese Weise deklariert –
besonders häufig in den java.io-Klassen. Einige Beispiele:
P r i n t S tre am
K o n s t r u ktor: throws F i l e N o t FoundE xception ,
U n s u p p o r t e d E n co di ng Ex ce pt io n
F i l e O u t p u tSt rea m
K o n s t r u ktor: throws F i l e N o t F o u nd Ex ce pt ion
write (...) throws I O E x c e p tion
close () throws I O E x c e p tio n
F i l e I n p u tS trea m:
read (...) throws I O E x c e p tion
Die Deklaration einer eigenen Methode mit throws Xxx hat zwei Konsequenzen:
Zum einen dürfen Sie nun auf die Absicherung der betreffenden Exceptions innerhalb
der Methode verzichten.
Zum anderen zwingen Sie den Nutzer der Methode, sich selbst um die Fehlerabsicherung zu kümmern.
Wenn Sie also beispielsweise eine Methode zum Schreiben einer Datei programmieren
und dabei auf diverse java.io-Klassen und deren Methoden zurückgreifen, stehen Sie
vor zwei Wahlmöglichkeiten: Sie können sich selbst um die Fehlerabsicherung kümmern
(also try-catch), oder Sie können Ihre Methode mit throws IOException deklarieren
Java auf den Punkt gebracht
ebooks.kofler
12 Exceptions
12.4 Selbst Exceptions werfen (throw)
149
und die Fehlerabsicherung an den Nutzer Ihrer Methode delegieren. In Entwicklungsumgebungen wie Eclipse können Sie sich per Mausklick für eine der beiden Varianten
entscheiden.
Welcher Weg ist nun der bessere? Wenn es eine Möglichkeit gibt, die Methode dennoch
mit einem vernünftigen Ergebnis abzuschließen, ist try-catch natürlich der richtige
Weg. Andernfalls ist es ratsamer, den Fehler weiterzuleiten und den Nutzer der Methode
entscheiden zu lassen, wie er mit dem Problem umgehen möchte. In der Praxis werden
Sie oft sogar beide Wege zugleich beschreiten: Einerseits verwenden Sie try-catch bzw.
try-finally, um offene Ressourcen zu schließen und sonstige Aufräumarbeiten durchzuführen; andererseits geben Sie aber den Fehler auch weiter an die übergeordnete
Methode, um diese so zu informieren, dass ein Problem aufgetreten ist.
Tipp
Vermeiden Sie eine Fehlerabsicherung im Stil try-catch(Exception e) mit einem
catch-Block, der sich auf die Ausgabe des Fehlertexts in der Konsole beschränkt.
Formal haben Sie damit Ihre Methode zwar gegen alle denkbaren Fehler abgesichert, tatsächlich kaschieren Sie die auftretenden Probleme aber nur. Wenn
Sie in Ihrem Programm keine korrekte Möglichkeit sehen, auf einen bestimmten
Fehler angemessen zu reagieren, ist es ehrlicher, die betroffene Methode mit
throws XxxException zu deklarieren!
12.4 Selbst Exceptions werfen (throw)
Mit throw können Sie selbst eine Exception auslösen (werfen):
if(n <0)
throw new I l l e g a l A r g u me nt Ex ce pt io n( " Parameter n " +
" darf nicht negativ sein ! ") ;
Bei nicht abgesichertem Code wird damit die aktuelle Methode verlassen und die Exception an die übergeordnete Methode weitergeleitet. Wenn Sie throw innerhalb einer
Java auf den Punkt gebracht
ebooks.kofler
12 Exceptions
12.4 Selbst Exceptions werfen (throw)
150
try-catch-Konstruktion auslösen, wird der Code im ersten passenden catch-Block fortgesetzt.
Nach Möglichkeit sollten Sie versuchen, eine der vielen vordefinierten Exceptions aus der
Java-Standardbibliothek zu verwenden. Bei Bedarf können Sie aber auch vollkommen
unkompliziert eine eigene Exception-Klasse definieren:
class M y E x c e p tio n extends Exception {
...
}