" ); System.exit(1); } try { Socket sock = new Socket(args[0], 80); OutputStream out = sock.getOutputStream(); InputStream in = sock.getInputStream(); //GET-Kommando senden String s = "GET " + args[1] + " HTTP/1.0" + "\r\n\r\n"; out.write(s.getBytes()); //Ausgabe lesen und anzeigen int len; byte[] b = new byte[100]; while ((len = in.read(b)) != -1) { System.out.write(b, 0, len); } //Programm beenden
Teil VIII
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029
1137
Kapitel 46 Listing 46.4: Laden einer Seite von einem WebServer (Forts.)
Netzwerkprogrammierung 030 in.close(); 031 out.close(); 032 sock.close(); 033 } catch (IOException e) { 034 System.err.println(e.toString()); 035 System.exit(1); 036 } 037 } 038 }
Wird das Programm beispielsweise auf einem SUSE-Linux 5.2 mit frisch installiertem Apache-Server mit localhost und /index.html als Argument aufgerufen, so beginnt seine Ausgabe wie folgt: HTTP/1.1 200 OK Date: Sun, 08 Nov 1998 18:26:13 GMT Server: Apache/1.2.5 S.u.S.E./5.1 Last-Modified: Sun, 24 May 1998 00:46:46 GMT ETag: "e852-45c-35676df6" Content-Length: 1116 Accept-Ranges: bytes Connection: close Content-Type: text/html <TITLE>Apache HTTP Server - Beispielseite Der Apache WWW Server
Diese Seite soll nur als Beispiel dienen. Die Dokumentation zum Apache-Server finden Sie hier. ...
46.3 Server-Sockets 46.3.1 Die Klasse ServerSocket In den bisherigen Abschnitten hatten wir uns mit dem Entwurf von Netzwerk-Clients beschäftigt. Nun wollen wir uns das passende Gegenstück ansehen, uns also mit der Entwicklung von Servern beschäftigen. Glücklicherweise ist auch das in Java recht einfach. Der wesentliche Unterschied liegt in der Art des Verbindungsaufbaus, für den es eine
1138
Server-Sockets
Kapitel 46
spezielle Klasse ServerSocket gibt. Diese Klasse stellt Methoden zur Verfügung, um auf einen eingehenden Verbindungswunsch zu warten und nach erfolgtem Verbindungsaufbau einen Socket zur Kommunikation mit dem Client zurückzugeben. Bei der Klasse ServerSocket sind im Wesentlichen der Konstruktor und die Methode accept von Interesse: public ServerSocket(int port) throws IOException
java.net. ServerSocket
public Socket accept() throws IOException Der Konstruktor erzeugt einen ServerSocket für einen bestimmten Port, also einen bestimmten Typ von Server-Anwendung (siehe Abschnitt 46.1.4, Seite 1125). Anschließend wird die Methode accept aufgerufen, um auf einen eingehenden Verbindungswunsch zu warten. accept blockiert so lange, bis sich ein Client bei der Server-Anwendung anmeldet (also einen Verbindungsaufbau zu unserem Host unter der Portnummer, die im Konstruktor angegeben wurde, initiiert). Ist der Verbindungsaufbau erfolgreich, liefert accept ein Socket-Objekt, das wie bei einer Client-Anwendung zur Kommunikation mit der Gegenseite verwendet werden kann. Anschließend steht der ServerSocket für einen weiteren Verbindungsaufbau zur Verfügung oder kann mit close geschlossen werden. Wir wollen uns die Konstruktion von Servern an einem Beispiel ansehen. Dazu soll ein einfacher ECHO-Server geschrieben werden, der auf Port 7 auf Verbindungswünsche wartet. Alle eingehenden Daten sollen unverändert an den Client zurückgeschickt werden. Zur Kontrolle sollen sie ebenfalls auf die Konsole ausgegeben werden: /* SimpleEchoServer.java */ import java.net.*; import java.io.*;
Listing 46.5: Ein ECHO-Server für Port 7
public class SimpleEchoServer { public static void main(String[] args) { try { System.out.println("Warte auf Verbindung auf Port 7..."); ServerSocket echod = new ServerSocket(7); Socket socket = echod.accept(); System.out.println("Verbindung hergestellt"); InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); int c; while ((c = in.read()) != -1) { out.write((char)c);
Teil VIII
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019
1139
Kapitel 46
Netzwerkprogrammierung
Listing 46.5: Ein ECHO-Server für Port 7 (Forts.)
020 System.out.print((char)c); 021 } 022 System.out.println("Verbindung beenden"); 023 socket.close(); 024 echod.close(); 025 } catch (IOException e) { 026 System.err.println(e.toString()); 027 System.exit(1); 028 } 029 } 030 }
Wird der Server gestartet, kann via Telnet oder mit dem EchoClient aus Listing 46.3, Seite 1133 auf den Server zugegriffen werden: telnet localhost 7 Wenn der Server läuft, werden alle eingegebenen Zeichen direkt vom Server zurückgesendet und als Echo in Telnet angezeigt. Läuft er nicht, gibt es beim Verbindungsaufbau eine Fehlermeldung.
i
i
i
INFO Wird das Programm unter UNIX gestartet, kann es möglicherweise Probleme geben. Einerseits kann es sein, dass bereits ein ECHO-Server auf Port 7 läuft. Er könnte nötigenfalls per Eintrag in inetd.conf oder ähnlichen Konfigurationsdateien vorübergehend deaktiviert werden. Andererseits dürfen Server auf Ports kleiner 1024 nur mit Root-Berechtigung gestartet werden. Ein normaler Anwender darf dagegen nur Server-Ports größer 1023 verwenden.
46.3.2 Verbindungen zu mehreren Clients Wir wollen das im vorigen Abschnitt vorgestellte Programm nun in mehrfacher Hinsicht erweitern: " Der Server soll mehr als einen Client gleichzeitig bedienen können. Die Clients sollen zur besseren Unterscheidung durchnummeriert werden. " Beim Verbindungsaufbau soll der Client eine Begrüßungsmeldung erhalten. " Für jeden Client soll ein eigener Thread angelegt werden. Um diese Anforderungen zu erfüllen, verändern wir das obige Programm ein wenig. Im Hauptprogramm wird nun nur noch der ServerSocket erzeugt und in einer Schleife jeweils mit accept auf einen Verbindungswunsch gewartet. Nach dem Verbindungsaufbau erfolgt die weitere Bearbeitung nicht mehr im Hauptprogramm, sondern es wird ein neuer Thread mit dem Verbindungs-Socket als Argument erzeugt. Dann wird der Thread gestartet und erledigt die gesamte Kommunikation mit dem Client. Beendet der Client die Verbindung, wird auch der zugehörige Thread beendet. Das Hauptprogramm braucht
1140
Server-Sockets
Kapitel 46
sich nur noch um den Verbindungsaufbau zu kümmern und ist von der eigentlichen Client-Kommunikation vollständig befreit. /* EchoServer.java */ import java.net.*; import java.io.*;
Listing 46.6: Eine verbesserte Version des EchoServers
public class EchoServer { public static void main(String[] args) { int cnt = 0; try { System.out.println("Warte auf Verbindungen auf Port 7..."); ServerSocket echod = new ServerSocket(7); while (true) { Socket socket = echod.accept(); (new EchoClientThread(++cnt, socket)).start(); } } catch (IOException e) { System.err.println(e.toString()); System.exit(1); } } } class EchoClientThread extends Thread { private int name; private Socket socket; public EchoClientThread(int name, Socket socket) { this.name = name; this.socket = socket; } public void run() { String msg = "EchoServer: Verbindung " + name; System.out.println(msg + " hergestellt"); try { InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); out.write((msg + "\r\n").getBytes()); int c; while ((c = in.read()) != -1) {
Teil VIII
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046
1141
Kapitel 46 Listing 46.6: Eine verbesserte Version des EchoServers (Forts.)
Netzwerkprogrammierung 047 out.write((char)c); 048 System.out.print((char)c); 049 } 050 System.out.println("Verbindung " + name + " wird beendet"); 051 socket.close(); 052 } catch (IOException e) { 053 System.err.println(e.toString()); 054 } 055 } 056 }
Zur besseren Übersicht werden alle Client-Verbindungen durchnummeriert und als erstes Argument an den Thread übergeben. Unmittelbar nach dem Verbindungsaufbau wird diese Meldung auf der Server-Konsole ausgegeben und an den Client geschickt. Anschließend wird in einer Schleife jedes vom Client empfangene Zeichen an diesen zurückgeschickt, bis er von sich aus die Verbindung unterbricht. Man kann den Server leicht testen, indem man mehrere Telnet-Sessions zu ihm aufbaut. Jeder einzelne Client sollte eine Begrüßungsmeldung mit einer eindeutigen Nummer erhalten und autonom mit dem Server kommunizieren können. Der Server sendet alle Daten zusätzlich an die Konsole und gibt sowohl beim Starten als auch beim Beenden eine entsprechende Meldung auf der Konsole aus.
46.3.3 Entwicklung eines einfachen Web-Servers In Abschnitt 46.2.4, Seite 1136 war schon angeklungen, dass ein Web-Server in seinen Grundfunktionen so einfach aufgebaut ist, dass wir uns hier eine experimentelle Implementierung ansehen können. Diese ist nicht nur zu Übungszwecken nützlich, sondern wird uns in Kapitel 47, Seite 1153 bei der RMI-Programmierung behilflich sein, Bytecode »on demand« zwischen Client und Server zu übertragen. Die Kommunikation zwischen einem Browser und einem Web-Server entspricht etwa folgendem Schema: " Der Web-Browser baut eine Verbindung zum Server auf. " Er schickt eine Seitenanforderung und ein paar zusätzliche Informationen in Form eines Request gemäß HTTP-Spezifikation. " Der Server analysiert den Request und schickt die gewünschte Datei (bzw. eine Fehlermeldung) an den Browser. " Der Server beendet die Verbindung. Hat der Browser auf diese Weise eine HTML-Seite erhalten, interpretiert er den HTMLCode und zeigt die Seite formatiert auf dem Bildschirm an. Enthält die Datei IMG-, APPLET- oder ähnliche Elemente, werden diese in derselben Weise vom Server angefordert und in die Seite eingebaut. Die wichtigste Aufgabe des Servers besteht also darin,
1142
Server-Sockets
Kapitel 46
eine Datei an den Client zu übertragen. Wir wollen uns zunächst das Listing ansehen und dann auf Details der Implementierung eingehen: /* ExperimentalWebServer.java */ import java.io.*; import java.util.*; import java.net.*;
Listing 46.7: Ein experimenteller Web-Server
/** * Ein ganz einfacher Web-Server auf TCP und einem * beliebigen Port. Der Server ist in der Lage, * Seitenanforderungen lokal zu dem Verzeichnis, * aus dem er gestartet wurde, zu bearbeiten. Wurde * der Server z.B. im Verzeichnis c:\tmp gestartet, so * würde eine Seitenanforderung * http://localhost:80/test/index.html die Datei * c:\tmp\test\index.html laden. CGIs, SSIs, Servlets * oder Ähnliches wird nicht unterstützt. *
* Die Dateitypen .htm, .html, .gif, .jpg und .jpeg werden * erkannt und mit korrekten MIME-Headern übertragen, alle * anderen Dateien werden als "application/octet-stream" * übertragen. Jeder Request wird durch einen eigenen * Client-Thread bearbeitet, nach Übertragung der Antwort * schließt der Server den Socket. Antworten werden mit * HTTP/1.0-Header gesendet. */ public class ExperimentalWebServer { public static void main(String[] args) { if (args.length != 1) { System.err.println( "Usage: java ExperimentalWebServer <port>" ); System.exit(1); } try { int port = Integer.parseInt(args[0]); System.out.println("Listening to port " + port); int calls = 0; ServerSocket httpd = new ServerSocket(port); while (true) { Socket socket = httpd.accept(); (new BrowserClientThread(++calls, socket)).start(); } } catch (IOException e) { System.err.println(e.toString());
Teil VIII
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046
1143
Kapitel 46 Listing 46.7: Ein experimenteller Web-Server (Forts.)
1144
Netzwerkprogrammierung 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095
System.exit(1); } } } /** * Die Thread-Klasse für die Client-Verbindung. */ class BrowserClientThread extends Thread { static final String[][] mimetypes = { {"html", "text/html"}, {"htm", "text/html"}, {"txt", "text/plain"}, {"gif", "image/gif"}, {"jpg", "image/jpeg"}, {"jpeg", "image/jpeg"}, {"jnlp", "application/x-java-jnlp-file"} }; private private private private private private private
Socket int PrintStream InputStream String String String
socket; id; out; in; cmd; url; httpversion;
/** * Erzeugt einen neuen Client-Thread mit der angegebenen * id und dem angegebenen Socket. */ public BrowserClientThread(int id, Socket socket) { this.id = id; this.socket = socket; } /** * Hauptschleife für den Thread. */ public void run() { try { System.out.println(id + ": Incoming call..."); out = new PrintStream(socket.getOutputStream()); in = socket.getInputStream(); readRequest();
Server-Sockets createResponse(); socket.close(); System.out.println(id } catch (IOException e) System.out.println(id System.out.println(id }
+ ": Closed."); { + ": " + e.toString()); + ": Aborted.");
Listing 46.7: Ein experimenteller Web-Server (Forts.)
} /** * Liest den nächsten HTTP-Request vom Browser ein. */ private void readRequest() throws IOException { //Request-Zeilen lesen Vector request = new Vector(10); StringBuffer sb = new StringBuffer(100); int c; while ((c = in.read()) != -1) { if (c == '\r') { //ignore } else if (c == '\n') { //line terminator if (sb.length() <= 0) { break; } else { request.addElement(sb); sb = new StringBuffer(100); } } else { sb.append((char)c); } } //Request-Zeilen auf der Konsole ausgeben Enumeration e = request.elements(); while (e.hasMoreElements()) { sb = (StringBuffer)e.nextElement(); System.out.println("< " + sb.toString()); } //Kommando, URL und HTTP-Version extrahieren String s = ((StringBuffer)request.elementAt(0)).toString(); cmd = ""; url = ""; httpversion = ""; int pos = s.indexOf(' '); if (pos != -1) { cmd = s.substring(0, pos).toUpperCase(); s = s.substring(pos + 1); //URL
Teil VIII
096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
Kapitel 46
1145
Kapitel 46 Listing 46.7: Ein experimenteller Web-Server (Forts.)
1146
Netzwerkprogrammierung 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
pos = s.indexOf(' '); if (pos != -1) { url = s.substring(0, pos); s = s.substring(pos + 1); //HTTP-Version pos = s.indexOf('\r'); if (pos != -1) { httpversion = s.substring(0, pos); } else { httpversion = s; } } else { url = s; } } } /** * Request bearbeiten und Antwort erzeugen. */ private void createResponse() { if (cmd.equals("GET") || cmd.equals("HEAD")) { if (!url.startsWith("/")) { httpError(400, "Bad Request"); } else { //MIME-Typ aus Dateierweiterung bestimmen String mimestring = "application/octet-stream"; for (int i = 0; i < mimetypes.length; ++i) { if (url.endsWith(mimetypes[i][0])) { mimestring = mimetypes[i][1]; break; } } //URL in lokalen Dateinamen konvertieren String fsep = System.getProperty("file.separator", "/"); StringBuffer sb = new StringBuffer(url.length()); for (int i = 1; i < url.length(); ++i) { char c = url.charAt(i); if (c == '/') { sb.append(fsep); } else { sb.append(c); } } try { FileInputStream is = new FileInputStream(sb.toString()); //HTTP-Header senden out.print("HTTP/1.0 200 OK\r\n");
Server-Sockets System.out.println("> HTTP/1.0 200 OK"); out.print("Server: ExperimentalWebServer 0.5\r\n"); System.out.println( "> Server: ExperimentalWebServer 0.5" ); out.print("Content-type: " + mimestring + "\r\n\r\n"); System.out.println("> Content-type: " + mimestring); if (cmd.equals("GET")) { //Dateiinhalt senden byte[] buf = new byte[256]; int len; while ((len = is.read(buf)) != -1) { out.write(buf, 0, len); } } is.close(); } catch (FileNotFoundException e) { httpError(404, "Error Reading File"); } catch (IOException e) { httpError(404, "Not Found"); } catch (Exception e) { httpError(404, "Unknown exception"); }
Listing 46.7: Ein experimenteller Web-Server (Forts.)
} } else { httpError(501, "Not implemented"); } } /** * Eine Fehlerseite an den Browser senden. */ private void httpError(int code, String description) { System.out.println("> ***" + code + ": " + description + "***"); out.print("HTTP/1.0 " + code + " " + description + "\r\n"); out.print("Content-type: text/html\r\n\r\n"); out.println(""); out.println("
"); out.println("ExperimentalWebServer-Error"); out.println(""); out.println(""); out.println("HTTP/1.0 " + code + "
"); out.println("" + description + "
"); out.println(""); out.println(""); }
Teil VIII
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 }
Kapitel 46
1147
Kapitel 46
Netzwerkprogrammierung
Der Web-Server besteht aus den beiden Klassen ExperimentalWebServer und BrowserClientThread, die nach dem in Abschnitt 46.3.2, Seite 1140 vorgestellten Muster aufgebaut sind. Nachdem in ExperimentalWebServer eine Verbindung aufgebaut wurde, wird ein neuer Thread erzeugt und die weitere Bearbeitung des Request an ein Objekt der Klasse BrowserClientThread delegiert. Der in run liegende Code beschafft zunächst die Ein- und Ausgabestreams zur Kommunikation mit dem Socket und ruft dann die beiden Methoden readRequest und createResponse auf. Anschließend wird der Socket geschlossen und der Thread beendet. In readRequest wird der HTTP-Request des Browsers gelesen, der aus mehreren Zeilen besteht. In der ersten wird die eigentliche Dateianforderung angegeben, die übrigen liefern Zusatzinformationen wie den Typ des Browsers, akzeptierte Dateiformate und Ähnliches. Alle Zeilen werden mit CRLF abgeschlossen, nach der letzten Zeile des Request wird eine Leerzeile gesendet. Entsprechend der Empfehlung in RFC1945 ignoriert unser Parser die '\r'-Zeichen und erkennt das Zeilenende anhand eines '\n'. So arbeitet er auch dann noch korrekt, wenn ein Client die Headerzeilen versehentlich mit einem einfachen LF abschließt. Ein typischer Request könnte etwa so aussehen (in diesem Beispiel wurde er von Netscape 4.04 unter Windows 95 generiert): GET /ansisys.html HTTP/1.0 Connection: Keep-Alive User-Agent: Mozilla/4.04 [en] (Win95; I) Host: localhost:80 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */* Accept-Language: en Accept-Charset: iso-8859-1,*,utf-8 HTTP/1.0 200 OK Server: ExperimentalWebServer 0.5 Content-type: text/html Unser Web-Server liest den Request zeilenweise in den Vector request ein und gibt alle Zeilen zur Kontrolle auf der Konsole aus. Anschließend wird das erste Element extrahiert und in die Bestandteile Kommando, URL (Dateiname) und HTTP-Version zerlegt. Diese Informationen werden zur weiteren Verarbeitung in den Membervariablen cmd, url und httpversion gespeichert. Nachdem der Request gelesen wurde, wird in createResponse die Antwort erzeugt. Zunächst prüft die Methode, ob es sich um ein GET- oder HEAD-Kommando handelt (HTTP kennt noch mehr). Ist das nicht der Fall, wird durch Aufruf von httpError eine Fehlerseite an den Browser gesendet. Andernfalls fährt die Methode mit der Bestimmung des Dateityps fort. Der Dateityp wird mit Hilfe der Arraykonstante mimetypes anhand der Dateierweiterung bestimmt und in einen passenden MIME-Typ konvertiert, der im Antwort-
1148
Server-Sockets
Kapitel 46
header an den Browser übertragen wird. Der Browser entscheidet anhand dieser Information, was mit der nachfolgend übertragenen Datei zu tun ist (Anzeige als Text, Anzeige als Grafik, Speichern in einer Datei usw.). Wird eine Datei angefordert, deren Erweiterung nicht bekannt ist, sendet der Server sie als application/octet-stream an den Browser, damit dieser dem Anwender die Möglichkeit geben kann, die Datei auf der Festplatte zu speichern. INFO
i
i
i
Der Mime-Typ application/x-java-jnlp-file wird für den Betrieb von Java Web Start benötigt. Dieses seit dem JDK 1.4 verfügbare Werkzeug zum Laden, Aktualisieren und Starten von Java-Programmen über Internetverbindungen wird ausführlich in Abschnitt 13.5, Seite 322 erläutert.
Nun wandelt der Server den angegebenen Dateinamen gemäß den Konventionen seines eigenen Betriebssystems um. Dazu wird das erste »/« aus dem Dateinamen entfernt (alle Dateien werden lokal zu dem Verzeichnis geladen, aus dem der Server gestartet wurde) und alle »/« innerhalb des Pfadnamens werden in den lokalen Pfadseparator konvertiert (unter MS-DOS ist das beispielsweise der Backslash). Dann wird die Datei mit einem FileInputStream geöffnet und der HTTP-Header und der Dateiinhalt an den Client gesendet. Konnte die Datei nicht geöffnet werden, wird eine Ausnahme ausgelöst und der Server sendet eine Fehlerseite. Der vom Server gesendete Header ist ähnlich aufgebaut wie der Request-Header des Clients. Er enthält mehrere Zeilen, die durch CRLF-Sequenzen voneinander getrennt sind. Nach der letzten Headerzeile folgt eine Leerzeile, also zwei aufeinanderfolgende CRLFSequenzen. HTTP 1.0 und 1.1 spezifizieren eine ganze Reihe von (optionalen) Headerelementen, von denen wir lediglich die Versionskennung, unseren Server-Namen und den MIME-Bezeichner mit der Typkennung der gesendeten Datei an den Browser übertragen. Unmittelbar nach dem Ende des Headers wird der Dateiinhalt übertragen. Eine Umkodierung erfolgt dabei normalerweise nicht, alle Bytes werden unverändert übertragen. Unser Server kann sehr leicht getestet werden. Am einfachsten legt man ein neues Unterverzeichnis an und kopiert die übersetzten Klassendateien und einige HTML-Dateien in dieses Verzeichnis. Nun kann der Server wie jedes andere Java-Programm gestartet werden. Beim Aufruf ist zusätzlich die Port-Nummer als Argument anzugeben: java ExperimentalWebServer 80
Teil VIII
Nun kann ein normaler Web-Browser verwendet werden, um Dateien vom Server zu laden. Befindet sich beispielsweise eine Datei index.html im Server-Verzeichnis und läuft der Server auf derselben Maschine wie der Browser, kann die Datei über die Adresse http://localhost/index.html im Browser geladen werden. Auch über das lokale Netz des Unternehmens oder das Internet können leicht Dateien geladen werden. Hat der Host,
1149
Kapitel 46
Netzwerkprogrammierung
auf dem der Server läuft, keinen Nameserver-Eintrag, kann stattdessen auch direkt seine IP-Adresse im Browser angegeben werden.
!
!
!
ACHTUNG Auf einem UNIX-System darf ein Server die Port-Nummer 80 nur verwenden, wenn er Root-Berechtigung hat. Ist das nicht der Fall, kann der Server alternativ auf einem Port größer 1023 gestartet werden:
java ExperimentalWebServer 7777 Im Browser muss die Adresse dann ebenfalls um die Port-Nummer ergänzt werden: http://localhost:7777/index.html.
46.4 Daten mit Hilfe der Klasse URL lesen Die Klasse URL wurde bereits in Abschnitt 40.1.1, Seite 943 behandelt. Neben den dort beschriebenen Möglichkeiten besitzt sie Methoden, um Daten von der Quelle zu lesen, die durch den URL adressiert wird: java.net.URL
public final InputStream openStream() throws IOException public final Object getContent() throws IOException public URLConnection openConnection() throws IOException Mit openStream wird ein InputStream geliefert, der wie die Methode getInputStream der Klasse Socket zum Lesen der Quelldaten verwendet werden kann. getContent versucht darüber hinaus, die Daten zu interpretieren. Dazu können Content Handler Factories registriert werden, die beispielsweise Text-, Image- oder Archivdateien interpretieren und ein dazu passendes Java-Objekt liefern. Die Methode openConnection stellt eine Vorstufe von getContent dar. Sie liefert ein Objekt des Typs URLConnection, das eine Abstraktion einer protokollspezifischen Verbindung zwischen einem Java-Programm und einem URL darstellt. Als einfaches Beispiel wollen wir uns das folgende Programm SaveURL ansehen. Es wird mit einem URL und einer Datei als Argument aufgerufen. Mit Hilfe der Klasse URL stellt das Programm eine Verbindung zur angegebenen URL her und beschafft durch Aufruf von openStream einen InputStream. Mit seiner Hilfe wird die Quelle gelesen und das Ergebnis in die als zweites Argument angegebene Datei geschrieben:
1150
Daten mit Hilfe der Klasse URL lesen
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035
/* SaveURL.java */ import java.net.*; import java.io.*;
Kapitel 46
Listing 46.8: Daten von einem URL lesen
public class SaveURL { public static void main(String[] args) { if (args.length != 2) { System.err.println( "Usage: java SaveURL " ); System.exit(1); } try { URL url = new URL(args[0]); OutputStream out = new FileOutputStream(args[1]); InputStream in = url.openStream(); int len; byte[] b = new byte[100]; while ((len = in.read(b)) != -1) { out.write(b, 0, len); } out.close(); in.close(); } catch (MalformedURLException e) { System.err.println(e.toString()); System.exit(1); } catch (IOException e) { System.err.println(e.toString()); System.exit(1); } } }
Das Programm kann nun leicht verwendet werden, um den Inhalt beliebiger URLs auf der Festplatte abzuspeichern. Die folgenden beiden Aufrufe zeigen den Download der Hauptseite des Java-Servers von SUN und das Laden einer Testseite von unserem in Abschnitt 46.3.3, Seite 1142 vorgestellten Web-Server: java SaveURL http://java.sun.com x.html
Teil VIII
java SaveURL http://localhost/index.html y.html
1151
Kapitel 46
Netzwerkprogrammierung
Zusammenfassung In diesem Kapitel wurden folgende Themen behandelt: " Grundlagen und Anwendungen der Netzwerkprogrammierung " Das ISO/OSI-7-Schichten-Modell und das vereinfachte Vier-Ebenen-Modell " Grundlagen der Protokolle TCP und IP " IP-Adressen, Netztypen und Domain-Namen " Port-Nummern und Applikationen " Request For Comments " Das Konzept der Sockets " Die Klasse InetAddress zur Adressierung von Socket-Verbindungen " Die besonderen Adressen localhost und 127.0.0.1 " Aufbau einer Client-Socket-Verbindung mit Hilfe der Klasse Socket und dem streambasierten Zugriff auf die Daten " Kommunikation mit dem DayTime- und Echo-Server " Lesender Zugriff auf einen Web-Server " Konstruktion von Serverdiensten mit der Klasse ServerSocket " Konstruktion mehrbenutzerfähiger Server mit Threads " Entwurf eines experimentellen Web-Servers " Daten mit Hilfe der Klasse URL lesen
1152
47
Remote Method Invocation
47.1
Einleitung
47.1.1 Prinzipielle Arbeitsweise Im vorigen Kapitel wurde die Netzwerkprogrammierung mit Hilfe von Sockets und URL-Objekten erläutert. Dabei wurden im Wesentlichen Dienste verwendet, deren Aufgabe es war, Daten zwischen zwei Netzwerkknoten zu übertragen. Höhere Anwendungen, wie etwa das Kopieren von Dateien, die Manipulation von Verzeichnissen oder das Starten von Programmen auf dem Server, wurden mit zusätzlichen Anwendungsprotokollen wie FTP oder HTTP realisiert. Neben der reinen Übertragung von Daten besteht eine weitere wichtige Anwendung von Netzwerkstrukturen darin, Programmcode zu verteilen und von unterschiedlichen Arbeitsplätzen im Netz aufzurufen. Auf diese Weise können spezielle Aufgaben einer Applikation (wie etwa der Datenbankzugriff oder die Kommunikation mit externen Systemen) an geeignete Server delegiert und so die Applikationslast gleichmäßiger verteilt und die Skalierbarkeit des Systems erhöht werden.
" In einem Remote-Interface werden eine oder mehrere Methoden definiert, die als aufrufbare Dienste anderen Arbeitsplätzen zur Verfügung gestellt werden sollen.
Teil VIII
Mit RMI (Remote Method Invocation) stellt das JDK seit der Version 1.1 einen Mechanismus zur Verfügung, der es ermöglicht, Objekte auf einfache Weise im Netz zu verteilen und ihre Dienste anderen Arbeitsplätzen zur Verfügung zu stellen. Die prinzipielle Arbeitsweise von RMI lässt sich wie folgt skizzieren (siehe Abbildung 47.1, Seite 1154):
Kapitel 47
Remote Method Invocation
" Eine Serverklasse implementiert das Interface und erzeugt eine oder mehrere Instanzen, die als Remote-Objekte bezeichnet werden. " Die Remote-Objekte werden bei einem Namens-Service registriert, der von potenziellen Clients abgefragt werden kann. Mit der RMI-Registry ist ein einfacher Namens-Service bereits Bestandteil des RMI-Pakets. " Clients beschaffen mit Hilfe der RMI-Registry Referenzen auf die benötigten Objekte und rufen die gewünschten Methoden auf. Die beim Aufruf übergebenen Parameter werden an das Remote-Objekt übertragen und die passende Methode wird dort ausgeführt. Der Rückgabewert wird an den Client zurückübertragen. Die Referenzen auf die Remote-Objekte werden als Remote-Referenzen bezeichnet. Abbildung 47.1: Prinzipielle Arbeitsweise von RMI
RMI-Registry Suche
Client
Registrierung
Virtueller Aufruf
Server RemoteObjekte
RMI etabliert also eine Client-Server-Architektur zwischen lokalen Java-Objekten und den von ihnen aufgerufenen Remote-Objekten. Die eigentliche Kommunikation zwischen den Teilnehmern ist fast vollständig unsichtbar. Die Rollen von Client und Server sind dabei keineswegs statisch festgelegt. So kann ein Client durchaus Server-Funktionalitäten implementieren oder ein Server kann zur Ausführung eines Client-Call die Hilfe eines anderen Remote-Objekts in Anspruch nehmen. Eine interessante Eigenschaft von RMI besteht auch darin, fehlenden Code dynamisch nachzuladen. Benötigt beispielsweise ein Server zur Ausführung eines Auftrags eine bis dato unbekannte Klasse vom Client (die natürlich ein ihm zur Compilezeit bekanntes Interface implementiert), so kann er diese dynamisch vom Client laden und – dank der Plattformunabhängigkeit von Java – auf dem Server ausführen.
47.1.2 Einzelheiten der Kommunikation Bei der Kommunikation mit RMI hat der Client den Eindruck, Methoden von Objekten aufzurufen, die auf dem Server liegen. In Wirklichkeit liegen die Dinge natürlich etwas komplizierter, denn der Client hat von der RMI-Registry lediglich ein Stub-Objekt erhalten und das Remote-Objekt hat den Server nicht wirklich verlassen. Ein Stub ist eine Klasse, die – wie das implementierende Remote-Objekt – das Remote-Interface implementiert und daher für den Client als Platzhalter für den Zugriff auf das Remote-Objekt dient.
1154
Einleitung
Kapitel 47
Der Stub kommuniziert über eine TCP-Verbindung mit dem als Skeleton bezeichneten Gegenstück auf der Server-Seite. Das Skeleton kennt das tatsächliche Applikationsobjekt, leitet die Anfragen des Stubs an dieses weiter und gibt den Rückgabewert an ihn zurück. Stub und Skeleton werden während der Entwicklung mit Hilfe eines Tools generiert und verbergen die komplizierten Details der Kommunikation zwischen Server und Client. Client
Virtuelle Kommunikation
Server
Stub
Skeleton
Remote Reference Layer
Remote Reference Layer
Abbildung 47.2: Stubs und Skeletons
Netzwerkverbindung
INFO
i
i
i
RMI verfolgt mit der Verteilung von Objekten im Netz einen ähnlichen Ansatz wie CORBA (die Common Object Request Broker Architecture, siehe beispielsweise http://www.omg.org). Da auch CORBA von Java sehr gut unterstützt wird, muss man sich als Entwickler natürlich die Frage stellen, welche der beiden Architekturen in einem entsprechenden Projekt die bessere Wahl ist. Für CORBA spricht die Vielzahl der verfügbaren Implementierungen, die größere Verbreitung und die Tatsache, dass neben Java auch andere Programmiersprachen unterstützt werden (etwa C++). Es ist daher ideal für heterogene Projekte, deren Größe eine gewisse kritische Masse überschreiten. RMI ist dagegen einfacher zu erlernen, bietet dynamischen Code-Austausch, erfordert keine zusätzlichen Lizenzen und kommt mit insgesamt weniger Aufwand aus. Für reine Java-Projekte könnte es sich daher als geeignete Wahl erweisen. Seit der Version 1.3 unterstützt das JDK die RMI-Kommunikation auch auf der Basis des CORBA-Protokolls IIOP und erleichtert so die Integration von RMI- und CORBA-Anwendungen.
Bei der Kommunikation zwischen Client und Server (wenn also der Client eine Methode auf einem Remote-Objekt aufruft) sind drei Arten von Datentypen zu unterscheiden: " Werden beim Aufruf einer Methode primitive Datentypen übergeben oder zurückgegeben (int, char, boolean usw.), werden sie wie gewöhnlich per Wert übergeben (call by value). In diesem Fall besteht also überhaupt kein Unterschied zu lokalen Java-Programmen.
Teil VIII
" Lokale Objekte können dagegen nur dann als Parameter oder Rückgabewert verwendet werden, wenn sie serialisierbar sind. Sie werden bei der Übertragung kopiert und somit ebenfalls per Wert übergeben (siehe Kapitel 41, Seite 963). Viele Klassen des JDK, wie etwa String, Calendar, die numerischen Wrapper-Klassen oder die Collection-Klassen sind bereits serialisierbar und können direkt verwendet werden. Eigene Klassen müssen das Interface Serializable implementieren.
1155
Kapitel 47
Remote Method Invocation
" Verweise auf Remote-Objekte, wie sie beispielsweise vom Namens-Service zurückgegeben werden, haben dagegen Referenzcharakter und werden wie gewöhnliche Objektreferenzen behandelt.
!
!
!
ACHTUNG Objekte, die weder Remote-Referenzen noch serialisierbar sind, können per RMI nicht ausgetauscht werden. Beispiele dafür sind die Klassen Thread, System oder RandomAccessFile. Derartige Objekte haben allerdings auch meist nur lokale Bedeutung und die Übertragung an eine andere virtuelle Maschine macht wenig Sinn.
47.2 Aufbau eines einfachen Uhrzeit-Services 47.2.1 Vorgehensweise In diesem Abschnitt wollen wir eine einfache Client-Server-Anwendung entwickeln und dabei die wichtigsten Eigenschaften von RMI vorstellen. Dazu soll ein Remote-Interface TimeService geschrieben werden, das zwei Methoden getTime und storeTime definiert. Mit getTime kann ein Client die aktuelle Uhrzeit auf dem Server ermitteln und sie sich als String zurückgeben lassen. storeTime erledigt prinzipiell dieselbe Aufgabe, speichert die Uhrzeit aber in einem TimeStore-Objekt, an dem wir die Besonderheiten des Austauschs von Objekttypen zeigen wollen. Der Server, der auf einem Computer mit dem Namen »ph01« läuft, wird ein einziges Remote-Objekt instanzieren und bei der ebenfalls auf »ph01« laufenden RMI-Registry unter dem Namen »TimeService« registrieren. Ein einfaches Client-Programm auf dem Rechner »ph02« wird bei der RMI-Registry eine Remote-Referenz auf das TimeServiceObjekt beschaffen, mit seiner Hilfe die Uhrzeit des Servers abfragen und die Ergebnisse auf seiner eigenen Console ausgeben. Da insbesondere das Setup der Systeme einigen Aufwand erfordert, wollen wir die einzelnen Schritte genau erläutern: " Zunächst wird das Remote-Interface erläutert. " Anschließend stellen wir seine Implementierung vor und zeigen, wie Stub und Skeleton erzeugt werden. " Danach erläutern wir, wie die RMI-Registry und der Server gestartet werden und welche Vorbereitungen dazu erforderlich sind. " Schließlich implementieren wir eine Client-Klasse und zeigen, wie sie gestartet und zum Zugriff auf den Server verwendet wird.
1156
Aufbau eines einfachen Uhrzeit-Services
Kapitel 47
47.2.2 Das Remote-Interface Das Remote-Interface definiert die Schnittstelle zwischen Client und Server. Bei seiner Entwicklung müssen einige Regeln beachtet werden: " Das Remote-Interface muss aus dem Interface Remote des Pakets java.rmi abgeleitet und als public deklariert werden. " Jede Methode muss die Exception RemoteException (ebenfalls aus dem Paket java.rmi) deklarieren. Hiermit werden alle Arten von Netzwerkproblemen oder Verbindungsstörungen zwischen Client und Server angezeigt. Nur die im Remote-Interface definierten Methoden stehen später den Clients zur Verfügung. Werden später bei der Implementierung des Servers weitere Methoden hinzugefügt, so bleiben sie für den Client unsichtbar. Das Remote-Interface für unseren UhrzeitService sieht so aus: 001 002 003 004 005 006 007 008 009 010 011 012 013 014
/* TimeService.java */ import java.rmi.*; import java.util.*;
Listing 47.1: Das RemoteInterface für den Uhrzeit-Service
public interface TimeService extends Remote { public String getTime() throws RemoteException; public TimeStore storeTime(TimeStore store) throws RemoteException; }
Die Methode getTime liefert einen String mit der aktuellen Server-Uhrzeit im Format »h[h]:m[m]:s[s]«. Die Methode storeTime erwartet ein Objekt vom Typ TimeStore, um den Uhrzeitstring dort hineinzuschreiben. Da Objekte (wegen der zur Übertragung erforderlichen Serialisierung) per Wert übergeben werden, würde jede Änderung an ihnen auf Client-Seite unsichtbar bleiben. storeTime gibt daher das TimeStore-Objekt mit der eingefügten Uhrzeit als Rückgabewert an den Client zurück. TimeStore wird als Interface wie folgt definiert: import java.io.Serializable; public interface TimeStore extends Serializable { public void setTime(String time);
Listing 47.2: Das TimeStoreInterface
Teil VIII
001 002 003 004 005 006 007 008 009
public String getTime(); }
1157
Kapitel 47
Remote Method Invocation
Mit setTime wird ein als String übergebener Uhrzeitwert gespeichert, mit getTime kann er abgefragt werden.
i
i
i
INFO Der Grund dafür, TimeStore als Interface zu definieren, liegt darin, dass wir mit seiner Hilfe zeigen wollen, auf welche Weise Code dynamisch zwischen Client und Server übertragen werden kann. Auf der Client-Seite werden wir dazu später eine Implementierung MyTimeStore verwenden, deren Bytecode server-seitig zunächst nicht bekannt ist, sondern zur Laufzeit nachgeladen wird.
47.2.3 Implementierung des Remote-Interface Die Implementierungsklasse Nach der Definition des Remote-Interface muss dessen Implementierung (also die Klasse für die Remote-Objekte) realisiert werden. Dazu erstellen wir eine Klasse TimeServiceImpl, die aus UnicastRemoteObject abgeleitet ist und das Interface TimeService implementiert. UnicastRemoteObject stammt aus dem Paket java.rmi.server und ist für die Details der Kommunikation zwischen Client und Server verantwortlich. Zusätzlich überlagert sie die Methoden clone, equals, hashCode und toString der Klasse Object, um den Remote-Referenzen die Semantik von Referenzen zu verleihen.
i
i
i
INFO Der hier verwendete Suffix »Impl« ist lediglich eine Namenskonvention, die anzeigt, dass das Objekt eine Implementierung von »TimeService« ist. Wir hätten auch jeden anderen Namen wählen können. Die Klasse TimeServiceImpl wird später nur bei der Instanzierung der zu registrierenden Remote-Objekte benötigt, auf Client-Seite kommt sie überhaupt nicht vor.
Das folgende Listing zeigt die Implementierung: Listing 47.3: Implementierung des UhrzeitServices
1158
001 002 003 004 005 006 007 008 009 010 011 012 013 014
/* TimeServiceImpl.java */ import java.rmi.*; import java.rmi.server.*; import java.util.*; public class TimeServiceImpl extends UnicastRemoteObject implements TimeService { public TimeServiceImpl() throws RemoteException { }
Aufbau eines einfachen Uhrzeit-Services 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 }
public String getTime() throws RemoteException { GregorianCalendar cal = new GregorianCalendar(); StringBuffer sb = new StringBuffer(); sb.append(cal.get(Calendar.HOUR_OF_DAY)); sb.append(":" + cal.get(Calendar.MINUTE)); sb.append(":" + cal.get(Calendar.SECOND)); return sb.toString(); }
Kapitel 47 Listing 47.3: Implementierung des UhrzeitServices (Forts.)
public TimeStore storeTime(TimeStore store) throws RemoteException { store.setTime(getTime()); return store; }
INFO
i
i
i
Der parameterlose Konstruktor ist erforderlich, weil beim (ansonsten automatisch ausgeführten) Aufruf des Superklassenkonstruktors eine RemoteException ausgelöst werden könnte. Ebenso wie die zu implementierenden Methoden kann er also stets eine RemoteException auslösen.
In getTime wird ein GregorianCalendar-Objekt instanziert und mit der aktuellen Uhrzeit belegt. Aus den Stunden-, Minuten- und Sekundenwerten wird ein StringBuffer erzeugt und nach Konvertierung in einen String an den Aufrufer zurückgegeben. storeTime ist noch einfacher aufgebaut. Es erzeugt zunächst einen Uhrzeitstring, speichert diesen in dem als Parameter übergebenen TimeStore-Objekt und gibt es an den Aufrufer zurück.
Erzeugen von Stub und Skeleton Nachdem die Implementierungsklasse angelegt wurde, müssen Stub und Skeleton erzeugt werden. Diese Arbeit braucht glücklicherweise nicht per Hand erledigt zu werden, sondern kann mit Hilfe des Programms rmic aus dem JDK ausgeführt werden. rmic erwartet den Namen der Implementierungsklasse als Argument (falls erforderlich, mit der vollen Paketbezeichnung) und erzeugt daraus die beiden Klassendateien für Stub und Skeleton. Aus der Klasse TimeServiceImpl werden die Klassen TimeServiceImpl_Stub und TimeServiceImpl_Skel erzeugt und als .class-Dateien zur Verfügung gestellt.
Teil VIII
rmic ist ein Kommandozeilenprogramm, das ähnliche Optionen wie javac kennt. Im einfachsten Fall reicht es aus, den Namen der Implementierungsklasse anzugeben: rmic TimeServiceImpl
1159
Kapitel 47
*
Remote Method Invocation
*
*
TIPP rmic analysiert den Bytecode der übergebenen Klasse und erzeugt daraus die angegebenen Klassendateien. Falls die Implememtierungsklasse noch nicht übersetzt war, wird dies automatisch erledigt. Wer sich einmal den Quellcode von Stub und Skeleton ansehen möchte, kann rmic mit der Option »-keep« anweisen, die temporären .java-Dateien nach dem Erzeugen der Klassendateien nicht zu löschen.
47.2.4 Registrieren der Objekte Starten der RMI-Registry Um den TimeService verwenden zu können, muss wenigstens eine Instanz von TimeServiceImpl erzeugt und bei der RMI-Registry registriert werden. Diese wird im JDK durch das Kommandozeilenprogramm rmiregistry zur Verfügung gestellt. Es wird auf dem Server gestartet und muss so lange laufen, wie Remote-Objekte dieses Servers verwendet werden sollen. Der einzige Parameter von rmiregistry ist eine optionale TCP-Port-Nummer. Diese gibt an, auf welchem TCP-Port eingehende Anfragen empfangen werden sollen. Sie ist standardmäßig auf 1099 eingestellt, kann aber auch auf einen anderen Wert gesetzt werden. Unter UNIX kann man die RMI-Registry im Hintergrund starten: rmiregistry & Unter Windows kann sie direkt von der Kommandozeile oder mit Hilfe des start-Kommandos in einer eigenen DOS-Box gestartet werden: start rmiregistry
Das Programm zur Registrierung des Remote-Objekts Nachdem rmiregistry läuft, können die zur Verfügung stehenden Remote-Objekte registriert werden. Wir verwenden dazu eine eigene Klasse TimeServiceRegistration, in deren main-Methode die Registrierung vorgenommen wird: Listing 47.4: Registrierung von Remote-Objekten
1160
001 002 003 004 005 006 007 008 009 010 011 012
/* TimeServiceRegistration.java */ import java.rmi.*; import java.util.*; public class TimeServiceRegistration { public static void main(String[] args) { System.setSecurityManager(new RMISecurityManager()); try { System.out.println("Registering TimeService");
Aufbau eines einfachen Uhrzeit-Services 013 TimeServiceImpl tsi = new TimeServiceImpl(); 014 Naming.rebind("TimeService", tsi); 015 System.out.println(" Done."); 016 } catch (Exception e) { 017 System.err.println(e.toString()); 018 System.exit(1); 019 } 020 } 021 }
Kapitel 47 Listing 47.4: Registrierung von Remote-Objekten (Forts.)
Das Programm erzeugt eine neue Instanz von TimeServiceImpl und übergibt diese unter dem Namen »TimeService« an die RMI-Registry. Dazu wird die statische Methode rebind der Klasse Naming aufgerufen. Naming ist die Programmierschnittstelle zur RMI-Registry, sie stellt folgende Methoden zur Verfügung: public static void bind(String name, Remote obj) throws AlreadyBoundException, MalformedURLException, RemoteException
java.rmi.Naming
public static void rebind(String name, Remote obj) throws RemoteException, MalformedURLException public static void unbind(String name) throws RemoteException, MalformedURLException, NotBoundException public static Remote lookup(String name) throws NotBoundException, MalformedURLException, RemoteException public static String[] list(String name) throws RemoteException, MalformedURLException
Teil VIII
Mit bind wird ein Remote-Objekt unter einem vorgegebenen Namen registriert. Gab es bereits ein Objekt dieses Namens, wird eine Ausnahme ausgelöst. rebind erledigt dieselbe Aufgabe, ersetzt jedoch ein eventuell vorhandenes gleichnamiges Objekt. Mit unbind kann ein registriertes Objekt aus der RMI-Registry entfernt werden. Die Methode lookup dient dazu, zu einem gegebenen Namen eine Remote-Referenz zu erhalten. Sie wird uns beim Client wiederbegegnen. Mit list kann eine Liste der Namen von allen registrierten Remote-Referenzen beschafft werden.
1161
Kapitel 47
Remote Method Invocation
Die an Naming übergebenen Namen haben das Format von URLs (siehe Abschnitt 40.1.1, Seite 943). Die Dienstebezeichnung ist »rmi«, der Rest entspricht einer HTTP-URL. Eine gültige rmi-URL wäre also beispielsweise: rmi://ph01:1099/TimeService Der Server heißt hier »ph01« und wird auf Port 1099 angesprochen, der Name des Remote-Objekts ist »TimeService«. Server-Name und Port-Nummer sind optional. Fehlt der Server, wird »localhost« angenommen, fehlt die Port-Nummer, erfolgt die Kommunikation auf TCP-Port 1099. Aus diesem Grund haben wir bei der Registrierung des TimeServiceImpl-Objekts mit »TimeService« lediglich den Namen des Remote-Objekts angegeben.
i
i
i
INFO Der Name, unter dem ein Remote-Objekt bei der RMI-Registry registriert werden soll, ist frei wählbar. Tatsächlich hat der in unserem Fall verwendete Begriff »TimeService« nichts mit dem Namen des Interface TimeService zu tun. Er stellt lediglich eine Vereinbarung zwischen Server und Client dar und hätte ebenso gut »ts«, »TimeService1« oder »Blablabla« lauten können.
Ändern der Policy-Datei Die in Zeile 010 stehende Installation der Klasse RMISecurityManager ist erforderlich, weil der Server den Code für die auf dem Client erzeugte TimeStore-Implementierung dynamisch laden soll. Aus Sicherheitsgründen ist das Laden von externem Bytecode aber nur dann erlaubt, wenn ein SecurityManager installiert ist. Um diesen mit den erforderlichen Rechten auszustatten, muss (ab dem JDK 1.2) die Policy-Datei auf dem Server um folgenden Eintrag ergänzt werden: grant { permission java.net.SocketPermission "ph01:1099", "connect,resolve"; permission java.net.SocketPermission "ph02:80", "connect"; }; Der erste Eintrag ermöglicht die TCP-Kommunikation mit der RMI-Registry auf Port 1099. Der zweite ermöglicht es dem Server, auf TCP-Port 80 eine Verbindung zu dem Rechner mit dem Namen »ph02« herzustellen. Dort wird später der Web-Server laufen, mit dem der Client die Klassendatei mit der TimeStore-Implementierung zur Verfügung stellt. Am besten ist es, die entsprechenden Einträge in der benutzerspezifischen Policy-Datei vorzunehmen. Sie liegt im Home-Verzeichnis des aktuellen Benutzers und heißt .java.policy. Auf Windows 95/98-Einzelplatzsystemen liegt sie im Windows-Verzeichnis (meist c:\windows). Weitere Informationen zur Policy-Datei sind im Kapitel über Sicherheit und Kryptografie in Abschnitt 48.3.4, Seite 1196 zu finden.
1162
Aufbau eines einfachen Uhrzeit-Services
Kapitel 47
Registrierung des Remote-Objekts Nach dem Ändern der Policy-Datei kann das Programm zur Registrierung des RemoteObjekts gestartet werden. Damit der Server später die dynamischen Klassendateien findet, muss das System-Property »java.rmi.server.codebase« gesetzt werden. In unserem Fall handelt es sich um eine http-Verbindung in das WebServer-Root-Verzeichnis auf dem Rechner »ph02«. Der Aufruf des Programms sieht damit wie folgt aus: c:\—>java -Djava.rmi.server.codebase=http://ph02/ TimeServiceRegistration Registering TimeService Done. Er ist nur erfolgreich, wenn die RMI-Registry läuft und die entsprechenden Änderungen in der Policy-Datei vorgenommen wurden. Andernfalls wird eine Ausnahme ausgelöst und das Programm mit einer Fehlermeldung beendet. War die Registrierung erfolgreich, wird die main-Methode beendet, das Programm läuft aber trotzdem weiter. Das liegt daran, dass der Konstruktor von UnicastRemoteObject einen neuen Thread zur Kommunikation mit der RMI-Registry aufgebaut hat, in dem er unter anderem das soeben erzeugte Objekt vorhält. INFO
i
i
i
Die RMI-Kommunikation zwischen Client und Server könnte auch völlig ohne SecurityManager, Web-Server und Änderungen an den Policy-Dateien durchgeführt werden. In diesem Fall wäre es aber nicht möglich, zur Laufzeit Bytecode zwischen den beiden Maschinen zu übertragen. Alle benötigten Klassendateien müssten dann im lokalen Klassenpfad des Clients bzw. Servers liegen.
47.2.5 Zugriff auf den Uhrzeit-Service Nach der Implementierung des Servers wollen wir uns nun die Realisierung der ClientSeite ansehen. Dazu soll das folgende Programm verwendet werden: /* TimeServiceClient.java */ import java.rmi.*;
Listing 47.5: Der TimeServiceClient
public class TimeServiceClient { public static void main(String[] args) { try { String host = "ph01"; String port = "1099"; String srv = "TimeService";
Teil VIII
001 002 003 004 005 006 007 008 009 010 011 012
1163
Kapitel 47 Listing 47.5: Der TimeServiceClient (Forts.)
Remote Method Invocation 013 String url = "rmi://" + host + ":" + port + "/" + srv; 014 System.out.println("Looking-up TimeService " + url); 015 TimeService ts = (TimeService)Naming.lookup(url); 016 System.out.println(" Server time is " + ts.getTime()); 017 System.out.print(" MyTimeStore contains "); 018 TimeStore tsd = new MyTimeStore(); 019 tsd = ts.storeTime(tsd); 020 System.out.println(tsd.getTime()); 021 } catch (Exception e) { 022 System.err.println(e.toString()); 023 System.exit(1); 024 } 025 026 } 027 }
Das Programm erstellt zunächst den URL-String zur Suche in der RMI-Registry. Er lautet »rmi://ph01:1099/TimeService« und wird in Zeile 015 an die Methode lookup der Klasse Naming übergeben. Falls auf dem Rechner »ph01« eine RMI-Registry auf Port 1099 läuft und ein Objekt mit dem Namen »TimeService« vorhält, wird durch diesen Aufruf eine passende Remote-Referenz erzeugt und der Variablen ts zugewiesen. Deren Methode getTime wird in Zeile 016 aufgerufen und über die Stub-Skeleton-Verbindung an das TimeServiceImpl-Objekt des Servers weitergeleitet. Der dort erzeugte Rückgabewert wird in umgekehrter Richtung an den Client zurückgegeben (die Klasse String ist standardmäßig serialisierbar) und auf dessen Console ausgegeben. Damit das Programm funktioniert, muss zuvor allerdings die Stub-Klasse TimeServiceImpl_Stub.class in das Startverzeichnis der Client-Klasse kopiert werden. Obwohl auch das dynamische Übertragen von Stubs leicht möglich wäre, haben wir es hier aus Gründen der Übersichtlichkeit nicht realisiert. In Zeile 018 wird eine Instanz der Klasse MyTimeStore erzeugt und an die Methode storeTime des Remote-Objekts übergeben. Dort wird die aktuelle Uhrzeit des Servers eingetragen und das Objekt als Rückgabewert an den Aufrufer zurückgegeben. Vor der Rückübertragung wird es nun ebenfalls serialisiert und landet nach der Deserialisierung durch den Client in Zeile 019 in der Variablen tsd. Der darin enthaltene Uhrzeitstring wird dann ebenfalls auf der Console ausgegeben. Die im Client verwendete Klasse MyTimeStore ist sehr einfach aufgebaut: Listing 47.6: Die Klasse MyTimeStore
1164
001 002 003 004 005 006 007 008
/* MyTimeStore.java */ import java.io.*; public class MyTimeStore implements TimeStore, Serializable { String time;
Aufbau eines einfachen Uhrzeit-Services 009 010 011 012 013 014 015 016 017 018 019 }
public void setTime(String time) { this.time = time; }
Kapitel 47 Listing 47.6: Die Klasse MyTimeStore (Forts.)
public String getTime() { return this.time; }
Sie implementiert das Interface TimeStore, um zu Parameter und Rückgabewert der TimeService-Methode storeTime kompatibel zu sein. Das Interface Serializable implementiert sie dagegen, um vom RMI-Laufzeitsystem zwischen Client und Server übertragen werden zu können. Die Klasse MyTimeStore ist zunächst nur auf dem Client bekannt und wird dort übersetzt. Wie eingangs erwähnt, besitzt RMI die Fähigkeit, Bytecode dynamisch nachzuladen. Dazu wird allerdings kein eigenes, sondern das aus dem World Wide Web bekannte HTTP-Protokoll verwendet. Wie ein Web-Browser fragt also einer der beiden Teilnehmer per HTTP-GET-Transaktion (siehe Abschnitt 46.2.4, Seite 1136) bei seinem Partner nach der benötigten Klassendatei. Damit der Server den Bytecode für MyTimeStore laden kann, muss also auf dem Client ein Web-Server laufen, der den Bytecode auf Anfrage zur Verfügung stellt. Wir können dazu einfach den in Abschnitt 46.3.3, Seite 1142 entwickelten ExperimentalWebServer verwenden und vor dem Aufruf des Client-Programms in dem Verzeichnis mit der Datei MyTimeStore.class starten: c:\—>start java ExperimentalWebServer 80 Nun kann das Client-Programm gestartet werden: c:\—>java TimeServiceClient Vorausgesetzt, dass die Server-Programme wie zuvor beschrieben gestartet wurden, die Klassendateien MyTimeStore.class, TimeServiceClient.class und TimeServiceImpl_Stub.class auf dem Client vorhanden sind und der Web-Server läuft, erhalten wir nun die Verbindung zum Server. Die Ausgabe des Clients sieht etwa so aus:
Teil VIII
Looking-up TimeService rmi://ph01:1099/TimeService Server time is 21:37:47 MyTimeStore contains 21:37:48 Abbildung 47.3, Seite 1166 stellt die Zusammenhänge noch einmal bildlich dar:
1165
Kapitel 47 Abbildung 47.3: Kommunikation im RMI-Beispiel
Remote Method Invocation
PH02
PH01
Web-Server
RMI-Registry
MyTimeStore
TimeServiceClient
Stub
Client-VM
TimeServiceRegistration
Skel
TimeServiceImpl
Server-VM
47.2.6 Ausblick Mit dem vorliegenden Beispiel wurden die grundlegenden Mechanismen von RMI vorgestellt. In der Praxis wird man meist etwas mehr Aufwand betreiben müssen, um eine stabile und performante RMI-Applikation zu erstellen. Nachfolgend seien einige der Aspekte genannt, die dabei von Bedeutung sind: " Zugriffe auf die RMI-Registry können je nach Konfiguration der Systeme recht kostspielig sein. Es ist daher sinnvoll, nur eines oder wenige Bootstrap-Objekte über die Registry zu laden und an sie das Beschaffen weiterer Remote-Referenzen zu delegieren. " Die RMI-Registry bietet lediglich eine sehr einfache Abbildung von Namen auf Remote-Objekte. Hierarchische Namensräume, dynamische Objektsuche, Lastverteilung oder ähnliche Eigenschaften fehlen ihr dagegen. Möglicherweise ist es besser, einen anderen Namens-Service zu verwenden. Mit JNDI (Java Naming and Directory Interface) steht dabei seit dem JDK 1.3 bereits eine mögliche Alternative standardmäßig zur Verfügung. " Das dynamische Laden von Bytecode wurde hier nur angedeutet. In der Praxis sollen möglicherweise auch Stubs dynamisch geladen werden. Andererseits kann vielleicht nicht jeder Client einen eigenen Web-Server zur Verfügung stellen. Die Verteilung des Bytecodes ist also ebenso zu organisieren wie die Verteilung der Objekte. " Auch auf den konkurrierenden Zugriff mehrerer Clients auf ein und dasselbe RemoteObjekt sind wir nicht eingegangen. Tatsächlich ist es in der Regel so, dass das RMILaufzeitsystem für jeden Zugriff eines Clients einen eigenen Thread erzeugt und somit parallel auf das Remote-Objekt zugegriffen wird. Die RMI-Spezifikation schreibt konsequenterweise vor, die Remote-Objekte thread-sicher zu implementieren.
1166
Zusammenfassung
Kapitel 47
Zusammenfassung In diesem Kapitel wurden folgende Themen behandelt: " Die prinzipielle Arbeitsweise von RMI " Die Bedeutung der Begriffe Remote-Interface, Remote-Objekt, Remote-Referenz und RMI-Registry " Die Verwendung von Stubs und Skeletons zur Kommunikation zwischen Server und Client " Die drei unterschiedlichen Parameterarten bei der Kommunikation zwischen Client und Server " Entwurf des Remote-Interface TimeService und Bedeutung des Interface Remote " Erstellen der Implementierungsklasse TimeServiceImpl durch Ableiten aus UnicastRemoteObject " Erzeugen von Stub und Skeleton mit rmic " Starten der RMI-Registry durch Aufruf von rmiregistry " Erzeugen und Registrieren von Remote-Objekten und das Format der RMI-URLs " Änderungen an den Policy-Dateien zum dynamischen Laden von Bytecode " Entwurf eines Clients zum Zugriff auf den Uhrzeit-Service
Teil VIII
" Verwendung eines Web-Servers zum dynamischen Laden von Bytecode
1167
48
Sicherheit und Kryptografie
48.1
Kryptografische Grundlagen
48.1.1 Wichtige Begriffe Thema dieses Kapitels ist es, die in Java verfügbaren Sicherheitsmechanismen vorzustellen. Wir werden dabei zunächst auf allgemeine Konzepte aus dem Gebiet der Kryptografie und ihre Implementierung in Java eingehen. Anschließend werden die eingebauten Sicherheitsmechanismen von Java vorgestellt. Zum Abschluss zeigen wir, wie signierte Applets erstellt und verwendet werden und wie mit ihrer Hilfe eine fein differenzierte Sicherheitspolitik etabliert werden kann. Zunächst sollen allerdings wichtige Begriffe erläutert werden, die für das Verständnis der nachfolgenden Abschnitte von Bedeutung sind.
Teil VIII
Angenommen, ein Sender will eine Nachricht an einen Empfänger übermitteln. Soll das geschehen, ohne dass ein Dritter, dem die Nachricht in die Hände fallen könnte, diese entziffern kann, könnte sie verschlüsselt werden. Der ursprüngliche Nachrichtentext (der als Klartext bezeichnet wird) wird dabei mit Hilfe eines dem Sender bekannten Verfahrens unkenntlich gemacht. Das als Schlüsseltext bezeichnete Ergebnis wird an den Empfänger übermittelt und mit Hilfe eines ihm bekannten Verfahrens wieder in den Klartext zurückverwandelt (was als entschlüsseln bezeichnet wird).
Kapitel 48 Abbildung 48.1: Verschlüsseln einer Nachricht
Sicherheit und Kryptografie
Sender
Empfänger
Klartext
Klartext
Verschlüsselung
Schlüsseltext
Entschlüsselung
Unsicherer Übertragungsweg
Solange der Algorithmus zum Entschlüsseln geheim bleibt, ist die Nachricht sicher. Selbst wenn sie auf dem Übertragungsweg entdeckt wird, kann kein Dritter sie entschlüsseln. Wird das Entschlüsselungsverfahren dagegen entdeckt, kann die Nachricht (und mit ihr alle anderen Nachrichten, die mit demselben Verfahren verschlüsselt wurden) entziffert werden. Um den Schaden durch das Entdecken eines Verschlüsselungsverfahrens gering zu halten, werden die Verschlüsselungsalgorithmen in aller Regel parametrisiert. Dazu wird beim Verschlüsseln eine als Schlüssel bezeichnete Ziffern- oder Zeichenfolge angegeben, mit der die Nachricht verschlüsselt wird. Der Empfänger benötigt dann zusätzlich zur Kenntnis des Verfahrens noch den vom Sender verwendeten Schlüssel, um die Nachricht entziffern zu können. Die Wissenschaft, die sich mit dem Verschlüsseln und Entschlüsseln von Nachrichten und eng verwandten Themen beschäftigt, wird als Kryptografie bezeichnet. Liegt der Schwerpunkt mehr auf dem Entschlüsseln (insbesondere dem Entziffern geheimer Botschaften), wird dies als Kryptoanalyse bezeichnet. Die Kryptologie schließlich bezeichnet den Zweig der Mathematik, der sich mit den formal-mathematischen Aspekten der Kryptografie und Kryptoanalyse beschäftigt.
48.1.2 Einfache Verschlüsselungen Substitution Seit dem Altertum sind einfache Verschlüsselungsverfahren bekannt. Zu ihnen zählen beispielsweise die Substitutionsverschlüsselungen, bei denen einzelne Buchstaben systematisch durch andere ersetzt werden. Angenommen, Klartexte bestehen nur aus den Buchstaben A bis Z, so könnte man sie dadurch verschlüsseln, dass jeder Buchstabe des Klartextes durch den Buchstaben ersetzt wird, der im Alphabet um eine feste Anzahl Zeichen verschoben ist. Als Schlüssel k kann beispielsweise die Länge der Verschiebung verwendet werden. Ist k beispielsweise 3, so würde jedes A durch ein D, jedes B durch ein E, jedes W durch ein Z, jedes X durch ein A usw. ersetzt werden. Dieses einfache Verfahren wurde beispielweise bereits von Julius Cäsar verwendet, um seinen Generälen geheime Nachrichten zu übermitteln. Es wird daher auch als Cäsarische
1170
Kryptografische Grundlagen
Kapitel 48
Verschlüsselung bezeichnet. Das folgende Listing zeigt eine einfache Implementierung dieses Verfahrens, bei dem Schlüssel und Klartext als Argument übergeben werden müssen: 001 002 003 004 005 006 007 008 009 010 011 012 013 014
/* Listing4801.java */ public class Listing4801 { public static void main(String[] args) { int key = Integer.parseInt(args[0]); String msg = args[1]; for (int i = 0; i < msg.length(); ++i) { int c = (msg.charAt(i) - 'A' + key) % 26 + 'A'; System.out.print((char)c); } } }
Listing 48.1: Verschlüsselung durch Substitution
Um die Nachricht zu entschlüsseln, verwendet der Empfänger dasselbe Verfahren, allerdings mit dem Schlüssel 26 - k: —>java Test2 3 HALLO KDOOR —>java Test2 23 KDOOR HALLO
Exklusiv-ODER Ein ähnlich weitverbreitetes Verfahren besteht darin, jedes Zeichen des Klartexts mit Hilfe des Exklusiv-ODER-Operators mit dem Schlüssel zu verknüpfen. Durch dessen Anwendung werden alle Bits invertiert, die zu einem gesetztem Bit im Schlüssel korrespondieren, alle anderen bleiben unverändert. Das Entschlüsseln erfolgt durch erneute Anwendung des Verfahrens mit demselben Schlüssel. INFO
i
i
i
Ein Verfahren, bei dem Ver- und Entschlüsselung mit demselben Algorithmus und Schlüssel durchgeführt werden, wird als symmetrische Verschlüsselung bezeichnet.
001 /* Listing4802.java */ 002 003 public class Listing4802 004 { 005 public static void main(String[] args)
Listing 48.2: Verschlüsselung mit Exklusiv-ODER
1171
Teil VIII
Eine einfache Implementierung der Exklusiv-ODER-Verschlüsselung zeigt folgendes Listing:
Kapitel 48
Sicherheit und Kryptografie
Listing 48.2: Verschlüsselung mit Exklusiv-ODER (Forts.)
006 { 007 int key = Integer.parseInt(args[0]); 008 String msg = args[1]; 009 for (int i = 0; i < msg.length(); ++i) { 010 System.out.print((char)(msg.charAt(i) ^ key)); 011 } 012 } 013 }
Ein Anwendungsbeispiel könnte so aussehen: —>java Test2 65 hallo ) –. —>java Test2 65 ") –." hallo
!
!
!
ACHTUNG Dass die Rückkonvertierung über die Kommandozeile hier geklappt hat, liegt daran, dass die Verschlüsselung keine nichtdarstellbaren Sonderzeichen produziert hat (der Schlüssel 65 kippt lediglich 2 Bits in jedem Zeichen). Im Allgemeinen sollte der zu ver- oder entschlüsselnde Text aus einer Datei gelesen und das Resultat auch wieder in eine solche geschrieben werden. Dann können alle 256 möglichen Bitkombinationen je Byte zuverlässig gespeichert und übertragen werden.
Vorsicht! Derart einfache Verschlüsselungen wie die hier vorgestellten sind zwar weit verbreitet, denn sie sind einfach zu implementieren. Leider bieten sie aber nicht die geringste Sicherheit gegen ernsthafte Krypto-Attacken. Einige der in letzter Zeit bekannt gewordenen (und für die betroffenen Unternehmen meist peinlichen, wenn nicht gar kostspieligen) Fälle von Einbrüchen in Softwaressysteme waren auf die Verwendung von zu einfachen Sicherheitssystemen zurückzuführen. Wir wollen diesen einfachen Verfahren nun den Rücken zukehren, denn seit dem JDK 1.2 gibt es in Java Möglichkeiten, professionelle Sicherheitskonzepte zu verwenden. Es ist ein großer Vorteil der Sprache, dass auf verschiedenen Ebenen Sicherheitsmechanismen fest eingebaut wurden und eine missbräuchliche Anwendung der Sprache erschwert wird. In den folgenden Abschnitten werden wir die wichtigsten dieser Konzepte vorstellen.
i
i
i
INFO Allerdings sollte dieser Abschnitt nicht als umfassende Einführung in die Grundlagen der Kryptografie missverstanden werden. Das Thema ist ausgesprochen vielschichtig, mathematisch anspruchsvoll und es erfordert in seiner Detailfülle weitaus mehr Raum als hier zur Verfügung steht. Wir werden neue Begriffe nur so weit einführen, wie sie für das Verständnis der entsprechenden Abschnitte erforderlich sind. Für Details sei auf weiterführende
1172
Kryptografische Grundlagen
Kapitel 48
Literatur verwiesen. Ein sehr gelungenes Buch ist »Applied Cryptography« von Bruce Schneier. Es bietet einen umfassenden und dennoch verständlichen Einblick in die gesamte Materie und ist interessant zu lesen. Gleichermaßen unterhaltsam wie lehrreich ist auch die in »Geheime Botschaften« von Simon Singh dargestellte Geschichte der Kryptografie.
48.1.3 Message Digests Ein Message Digest ist eine Funktion, die zu einer gegebenen Nachricht eine Prüfziffer berechnet. Im Gegensatz zu ähnlichen Verfahren, die keine kryptografische Anwendung haben (siehe z.B. hashCode in Abschnitt 8.1.2, Seite 179), muss ein Message Digest zusätzlich folgende Eigenschaften besitzen: " Die Wahrscheinlichkeit, dass zwei unterschiedliche Nachrichten dieselbe Prüfsumme haben, muss vernachlässigbar gering sein. " Es muss praktisch unmöglich sein, aus der berechneten Prüfsumme auf die ursprüngliche Nachricht zurückzuschließen. Ein Message Digest wird daher auch als Einweg-Hashfunktion bezeichnet. Er ist meist 16 oder 20 Byte lang und kann als eine Art komplizierte mathematische Zusammenfassung der Nachricht angesehen werden. Message Digests haben Anwendungen im Bereich digitaler Unterschriften und bei der Authentifizierung. Allgemein gesprochen werden sie dazu verwendet, sicherzustellen, dass eine Nachricht nicht verändert wurde. Bevor wir auf diese Anwendungen in den nächsten Abschnitten zurückkommen, wollen wir uns ihre Implementierung im JDK 1.2 ansehen. Praktisch alle wichtigen Sicherheitsfunktionen sind im Paket java.security oder einem seiner Unterpakete untergebracht. Ein Message Digest wird durch die Klasse MessageDigest implementiert. Deren Objekte werden nicht direkt instanziert, sondern mit der Methode getInstance erstellt: public static MessageDigest getInstance(String algorithm) throws NoSuchAlgorithmException
java.security. MessageDigest
Als Argument wird dabei die Bezeichnung des gewünschten Algorithmus angegeben. Im JDK 1.2 sind beispielsweise folgende Angaben möglich: " »SHA«: der 160-Bit lange »Secure Hash Algorithm« des amerikanischen National Institute of Standards and Technology " »MD5«: der 128-Bit lange »Message Digest 5« von Ron L. Rivest
Teil VIII
Nachdem ein MessageDigest-Objekt erzeugt wurde, bekommt es die Daten, zu denen die Prüfziffer berechnet werden soll, in einzelnen Bytes oder Byte-Arrays durch fortgesetzten Aufruf der Methode update übergeben:
1173
Kapitel 48
Sicherheit und Kryptografie
public void update(byte input) public void update(byte[] input) public void update(byte[] input, int offset, int len)
java.security. MessageDigest
Wurden alle Daten übergeben, kann durch Aufruf von digest das Ergebnis ermittelt werden: java.security. MessageDigest
public byte[] digest() Zurückgegeben wird ein Array von 16 bzw. 20 Byte (im Falle anderer Algorithmen möglicherweise auch andere Längen), in dem der Message Digest untergebracht ist. Ein Aufruf führt zudem dazu, dass der Message Digest zurückgesetzt, also auf den Anfangszustand initialisiert, wird. Das folgende Listing zeigt, wie ein Message Digest zu einer beliebigen Datei erstellt wird. Sowohl Algorithmus als auch Dateiname werden als Kommandozeilenargumente übergeben:
Listing 48.3: Erstellen eines Message Digest
1174
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031
/* Listing4803.java */ import java.io.*; import java.security.*; public class Listing4803 { /** * Konvertiert ein Byte in einen Hex-String. */ public static String toHexString(byte b) { int value = (b & 0x7F) + (b < 0 ? 128 : 0); String ret = (value < 16 ? "0" : ""); ret += Integer.toHexString(value).toUpperCase(); return ret; } public static void main(String[] args) { if (args.length < 2) { System.out.println( "Usage: java Listing4803 md-algorithm filename" ); System.exit(0); } try { //MessageDigest erstellen MessageDigest md = MessageDigest.getInstance(args[0]); FileInputStream in = new FileInputStream(args[1]); int len;
Kryptografische Grundlagen 032 byte[] data = new byte[1024]; 033 while ((len = in.read(data)) > 0) { 034 //MessageDigest updaten 035 md.update(data, 0, len); 036 } 037 in.close(); 038 //MessageDigest berechnen und ausgeben 039 byte[] result = md.digest(); 040 for (int i = 0; i < result.length; ++i) { 041 System.out.print(toHexString(result[i]) + " "); 042 } 043 System.out.println(); 044 } catch (Exception e) { 045 System.err.println(e.toString()); 046 System.exit(1); 047 } 048 } 049 }
INFO
Kapitel 48 Listing 48.3: Erstellen eines Message Digest (Forts.)
i
i
i
Im Paket java.security gibt es zwei Klassen, die einen Message Digest mit einem Stream kombinieren. DigestInputStream ist ein Eingabe-Stream, der beim Lesen von Bytes parallel deren Message Digest berechnet; DigestOutputStream führt diese Funktion beim Schreiben aus. Beide übertragen die eigentlichen Bytes unverändert und können dazu verwendet werden, in einer Komposition von Streams »nebenbei« einen Message Digest zu berechnen.
Authentifizierung Ein wichtiges Anwendungsgebiet von Message Digests ist die Authentifizierung, d.h. die Überprüfung, ob die Person oder Maschine, mit der kommuniziert werden soll, tatsächlich »echt« ist (also die ist, die sie vorgibt zu sein). Eine Variante, bei der ein Anwender sich mit einem Benutzernamen und Passwort autorisiert, kann mit Hilfe eines Message Digest in folgender Weise realisiert werden: " Zunächst wird vom System ein Benutzername vergeben. " Beim ersten Anmelden gibt der Anwender den Benutzernamen und ein von ihm vergebenes Passwort ein. " Das System berechnet den Message Digest für das Passwort und speichert ihn zusammen mit dem Benutzernamen in der Benutzerdatenbank ab.
Teil VIII
" Bei jedem weiteren Anmeldeversuch berechnet das System den Message Digest zum eingegebenen Passwort und vergleicht ihn mit dem in der Benutzerdatenbank zu diesem Benutzernamen gespeicherten. Sind beide identisch, wird der Zugang gewährt, andernfalls wird er verweigert.
1175
Kapitel 48
Sicherheit und Kryptografie
Bemerkenswert daran ist, dass das System nicht die Passwörter selbst speichert, auch nicht in verschlüsselter Form. Ein Angriff auf die Benutzerdatenbank mit dem Versuch, gespeicherte Passwörter zu entschlüsseln, ist daher nicht möglich. Eine bekannte (und leider schon oft erfolgreich praktizierte) Methode des Angriffs besteht allerdings darin, Message Digests zu allen Einträgen in großen Wörterbüchern berechnen zu lassen, und sie mit den Einträgen der Benutzerdatenbank zu vergleichen. Das ist einer der Gründe dafür, weshalb als Passwörter niemals Allerweltsnamen oder einfache, in Wörterbüchern verzeichnete, Begriffe verwendet werden sollten.
»Unwissende« Beweise Eine weitere Anwendung von Message Digests besteht darin, die Existenz von Geheimnissen oder den Nachweis der Kenntnis bestimmter Sachverhalte nachzuweisen, ohne deren Inhalt preiszugeben – selbst nicht eigentlich vertrauenswürdigen Personen. Dies wird in Bruce Schneier's Buch als Zero-Knowledge Proof bezeichnet und funktioniert so: " A speichert das Geheimnis in elektronischer Form und berechnet den Message Digest dazu. " A deponiert das Geheimnis an einer Stelle, die für Dritte unerreichbar ist. " A übergibt den Message Digest zum Zeitpunkt X einem Notar, der Inhalt und Eingangsdatum bestätigt und urkundlich festhält. Alternativ kann A den Message Digest auch in der regionalen Tagespresse veröffentlichen und sich den Zeitungsausschnitt an die Wand hängen. Das Geheimnis ist nicht veröffentlicht, der Nachweis für seine Existenz zum Zeitpunkt X aber erbracht. Muß A Jahre später die Existenz dieser Informationen nachweisen, holt es die Diskette mit dem Geheimnis aus dem Tresor, berechnet den Message Digest erneut und zeigt dessen Übereinstimmung mit dem seinerzeit in der Zeitung veröffentlichten.
Fingerprints Eine weitere Anwendung von Message Digests besteht im Erstellen von Fingerprints (also digitalen Fingerabdrücken) zu öffentlichen Schlüsseln (was das genau ist, wird in Abschnitt 48.1.5, Seite 1179 erklärt). Um die Korrektheit eines öffentlichen Schlüssels nachzuweisen, wird daraus ein Message Digest berechnet und als digitaler Fingerabdruck an prominenter Stelle veröffentlicht (beispielsweise in den Signaturen der E-Mails des Schlüsselinhabers). Soll vor der Verwendung eines öffentlichen Schlüssel überprüft werden, ob dieser auch wirklich dem gewünschten Inhaber gehört, ist lediglich der (durch das Schlüsselverwaltungsprogramm adhoc berechnete) Fingerprint des öffentlichen Schlüssels mit dem in der E-Mail veröffentlichten zu vergleichen. Stimmen beide überein, erhöht sich das Vertrauen
1176
Kryptografische Grundlagen
Kapitel 48
in die Authentizität des öffentlichen Schlüssels und er kann verwendet werden. Stimmen sie nicht überein, sollte der Schlüssel auf keinen Fall verwendet werden. Wir werden in Abschnitt 48.1.7, Seite 1187 noch einmal auf diese Problematik zurückkommen.
48.1.4 Kryptographische Zufallszahlen Zufallszahlen wurden bereits in Abschnitt 16.1, Seite 385 vorgestellt. In kryptografischen Anwendungen werden allerdings bessere Zufallszahlengeneratoren benötigt, als in den meisten Programmiersprachen implementiert sind. Einerseits sollte die Verteilung der Zufallszahlen besser sein, andererseits wird eine größere Periodizität gefordert (das ist die Länge der Zahlensequenz, nach der sich eine Folge von Zufallszahlen frühestens wiederholt). Zudem muss die nächste Zahl der Folge praktisch unvorhersagbar sein – selbst wenn deren Vorgänger bekannt sind. INFO
i
i
i
Es ist bekannt, dass sich mit deterministischen Maschinen (wie Computerprogramme es beispielsweise sind) keine echten Zufallszahlen erzeugen lassen. Eigentlich müssten wir daher von Pseudo-Zufallszahlen sprechen, um darauf hinzuweisen, dass unsere Zufallszahlengeneratoren stets deterministische Zahlenfolgen erzeugen. Mit der zusätzlichen Forderung kryptografischer Zufallszahlen, praktisch unvorhersagbare Zahlenfolgen zu generieren, wird diese Unterscheidung an dieser Stelle unbedeutend. Tatsächlich besteht der wichtigste Unterschied zu »echten« Zufallsgeneratoren nur noch darin, dass deren Folgen nicht zuverlässig reproduziert werden können (was bei unseren Pseudo-Zufallszahlen sehr wohl der Fall ist). Wir werden im Folgenden daher den Begriff »Zufallszahl« auch dann verwenden, wenn eigentlich »Pseudo-Zufallszahl« gemeint ist.
Die Klasse SecureRandom des Pakets java.security implementiert einen Generator für kryptografische Zufallszahlen, der die oben genannten Eigenschaften besitzt. Er wird durch Aufruf der Methode getInstance ähnlich instanziert wie ein Message Digest: public static SecureRandom getInstance(String algorithm) throws NoSuchAlgorithmException
java.security. MessageDigest
Als Algorithmus ist beispielsweise »SHA1PRNG« im JDK 1.2 implementiert. Hierbei entstehen die Zufallszahlen aus der Berechnung eines Message Digest für eine Pseudonachricht, die aus einer Kombination aus Initialwert und fortlaufendem Zähler besteht. Die Klasse SecureRandom stellt weiterhin die Methoden setSeed und nextBytes zur Verfügung: java.security. MessageDigest
Teil VIII
public void setSeed(long seed) public void nextBytes(byte[] bytes) Mit setSeed wird der Zufallszahlengenerator initialisiert. Die Methode sollte nach der Konstruktion einmal aufgerufen werden, um den Initialwert festzulegen (andernfalls
1177
Kapitel 48
Sicherheit und Kryptografie
macht es der Generator selbst). Gleiche Initialwerte führen auch zu gleichen Folgen von Zufallszahlen. Mit nextBytes wird eine beliebig lange Folge von Zufallszahlen erzeugt und in dem als Argument übergebenen Byte-Array zurückgegeben. Das folgende Listing instanziert einen Zufallszahlengenerator und erzeugt zehn Folgen zu je acht Byte Zufallszahlen, die dann auf dem Bildschirm ausgegeben werden: Listing 48.4: Erzeugen kryptografischer Zufallszahlen
1178
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040
/* Listing4804.java */ import java.security.*; public class Listing4804 { /** * Konvertiert ein Byte in einen Hex-String. */ public static String toHexString(byte b) { int value = (b & 0x7F) + (b < 0 ? 128 : 0); String ret = (value < 16 ? "0" : ""); ret += Integer.toHexString(value).toUpperCase(); return ret; } public static void main(String[] args) { try { //Zufallszahlengenerator erstellen SecureRandom rand = SecureRandom.getInstance("SHA1PRNG"); byte[] data = new byte[8]; //Startwert initialisieren rand.setSeed(0x123456789ABCDEF0L); for (int i = 0; i < 10; ++i) { //Zufallszahlen berechnen rand.nextBytes(data); //Ausgeben for (int j = 0; j < 8; ++j) { System.out.print(toHexString(data[j]) + " "); } System.out.println(); } } catch (Exception e) { System.err.println(e.toString()); System.exit(1); } } }
Kryptografische Grundlagen
Kapitel 48
48.1.5 Public-Key-Verschlüsselung Eines der Hauptprobleme bei der Anwendung symmetrischer Verschlüsselungen ist das der Schlüsselübertragung. Eine verschlüsselte Nachricht kann nämlich nur dann sicher übertragen werden, wenn der Schlüssel auf einem sicheren Weg vom Sender zum Empfänger gelangt. Je nach räumlicher, technischer oder organisatorischer Distanz zwischen beiden Parteien kann das unter Umständen sehr schwierig sein. Mit der Erfindung der Public-Key-Kryptosysteme wurde dieses Problem Mitte der siebziger Jahre entscheidend entschärft. Bei einem solchen System wird nicht ein einzelner Schlüssel verwendet, sondern diese treten immer paarweise auf. Einer der Schlüssel ist öffentlich und dient dazu, Nachrichten zu verschlüsseln. Der anderen Schlüssel ist privat. Er dient dazu, mit dem öffentlichen Schlüssel verschlüsselte Nachrichten zu entschlüsseln. Das Schlüsselübertragungsproblem wird nun dadurch gelöst, dass ein potenzieller Empfänger verschlüsselter Nachrichten seinen öffentlichen Schlüssel an allgemein zugänglicher Stelle publiziert. Seinen privaten Schlüssel hält er dagegen geheim. Will ein Sender eine geheime Nachricht an den Empfänger übermitteln, verwendet er dessen allgemein bekannten öffentlichen Schlüssel und überträgt die verschlüsselte Nachricht an den Empfänger. Nur mit Hilfe seines privaten Schlüssels kann dieser nun die Nachricht entziffern. Das Verfahren funktioniert natürlich nur, wenn der öffentliche Schlüssel nicht dazu taugt, die mit ihm verschlüsselte Nachricht zu entschlüsseln. Auch darf es nicht möglich sein, mit vertretbarem Aufwand den privaten Schlüssel aus dem öffentlichen herzuleiten. Beide Probleme sind aber gelöst und es gibt sehr leistungsfähige und sichere Verschlüsselungsverfahren, die auf dem Prinzip der Public-Key-Kryptografie beruhen. Bekannte Beispiele für solche Systeme sind RSA (benannt nach ihren Erfindern Rivest, Shamir und Adleman) und DSA (Digital Signature Architecture). Asymmetrische Kryptosysteme haben meist den Nachteil, sehr viel langsamer zu arbeiten als symmetrische. In der Praxis kombiniert man daher beide Verfahren und kommt so zu hybriden Kryptosystemen. Um eine geheime Nachricht von A nach B zu übertragen, wird dabei in folgenden Schritten vorgegangen: " Mit Hilfe eines kryptografischen Zufallszahlengenerators erzeugt A einen Einmalschlüssel (der auch als Session Key bezeichnet wird). " A verschlüsselt die geheime Nachricht mit Hilfe des Session Keys und eines (schnellen) symmetrischen Verfahrens.
Teil VIII
" A verschlüsselt den Session-Key mit dem öffentlichen Schlüssel von B auf der Basis des vereinbarten Public-Key-Kryptoverfahrens und sendet ihn zusammen mit der verschlüsselten Nachricht an B. " B entschlüsselt den Session-Key mit seinem privaten Schlüssel. " B entschlüsselt die geheime Nachricht mit dem Session Key.
1179
Kapitel 48
Sicherheit und Kryptografie
Fast alle Public-Key-Kryptosysteme arbeiten in dieser Weise als Hybridsysteme. Andernfalls würde das Ver- und Entschlüsseln bei großen Nachrichten viel zu lange dauern. Ein bekanntes Beispiel für ein solches System ist PGP (Pretty Good Privacy) von Phil Zimmermann. Es wird vorwiegend beim Versand von E-Mails verwendet und gilt als sehr sicher. Freie Implementierungen stehen für viele Plattformen zur Verfügung.
i
i
i
INFO Das Ver- und Entschlüsseln von Daten mit Hilfe von asymmetrischen Verfahren war bis zur Version 1.3 nicht im JDK enthalten. Zwar gab es als Erweiterung zum JDK die JCE (JAVA Cryptography Extension), doch diese durfte nur in den USA und Kanada verwendet werden. Mit dem JDK 1.4 wurden die JCE sowie die Java Secure Socket Extension (JSSE) und der Java Authentication and Authorization Service (JAAS) fester Bestandteil des JDK. Dennoch gibt es nach wie vor einige Einschränkungen in der Leistungsfähigkeit der einzelnen Pakete, die auf US-Exportbeschränkungen zurückzuführen sind. Details können in der Dokumentation zum JDK 1.4 oder neueren Versionen nachgelesen werden.
48.1.6 Digitale Unterschriften Ein großer Vorteil der Public-Key-Kryptosysteme ist es, dass sie Möglichkeiten zum Erstellen und Verifizieren von digitalen Unterschriften bieten. Eine digitale Unterschrift besitzt folgende wichtige Eigenschaften: " Sie stellt sicher, dass eine Nachricht von einem ganz bestimmten und eindeutig identifizierbaren Absender stammt. " Sie stellt sicher, dass die Nachricht intakt ist und nicht während der Übertragung verändert wurde. Beide Eigenschaften sind für den elektronischen Datenverkehr so fundamental wie die Verschlüsselung selbst. Technisch basieren sie darauf, dass die Funktionsweise eines Public-Key-Kryptosystems sich umkehren lässt. Dass es also möglich ist, Nachrichten, die mit einem privaten Schlüssel verschlüsselt wurden, mit Hilfe des korrespondierenden öffentlichen Schlüssels zu entschlüsseln. Im Prinzip funktioniert eine digitale Unterschrift so: Will A eine Nachricht signieren, so verschlüsselt er sie mit seinem privaten Schlüssel. Jeder, der im Besitz des öffentlichen Schlüssel von A ist, kann sie entschlüsseln. Da nur A seinen eigenen privaten Schlüssel kennt, muss die Nachricht von ihm stammen. Da es keinem Dritten möglich ist, die entschlüsselte Nachricht zu modifizieren und sie erneut mit dem privaten Schlüssel von A zu verschlüsseln, ist auch die Integrität der Nachricht sichergestellt. Den Vorgang des Überprüfens der Integrität und Authentizität bezeichnet man als Verifizieren einer digitalen Unterschrift. In der Praxis sind die Dinge wieder einmal etwas komplizierter, denn die Langsamkeit der asymmetrischen Verfahren erfordert eine etwas aufwändigere Vorgehensweise. Statt
1180
Kryptografische Grundlagen
Kapitel 48
die komplette Nachricht zu verschlüsseln, berechnet A zunächst einen Message Digest der Nachricht. Diesen verschlüsselt A mit seinem privaten Schlüssel und versendet ihn als Anhang zusammen mit der Nachricht. Ein Empfänger wird die Nachricht lesen, ihren Message Digest bilden und diesen dann mit dem (mit Hilfe des öffentlichen Schlüssels von A entschlüsselten) Original-Message-Digest vergleichen. Stimmen beide überein, ist die Signatur gültig. Die Nachricht stammt dann sicher von A und wurde nicht verändert. Stimmen sie nicht überein, wurde sie ver- oder gefälscht. Das JDK stellt Klassen zum Erzeugen und Verifizieren digitaler Unterschriften zur Verfügung. Wir wollen uns beide Verfahren in den folgenden Abschnitten ansehen. Zuvor wird allerdings ein Schlüsselpaar benötigt, dessen Generierung im nächsten Abschnitt besprochen wird.
Erzeugen und Verwalten von Schlüsseln mit dem JDK Um digitale Unterschriften erzeugen und verifizieren zu können, müssen Schlüsselpaare erzeugt und verwaltet werden. Seit dem JDK 1.2 wird dazu eine Schlüsseldatenbank verwendet, auf die mit Hilfe des Hilfsprogramms keytool zugegriffen werden kann. keytool kann Schlüsselpaare erzeugen, in der Datenbank speichern und zur Bearbeitung wieder herausgeben. Zudem besitzt es die Fähigkeit, Zertifikate (siehe Abschnitt 48.1.7, Seite 1187) zu importieren und in der Datenbank zu verwalten. Die Datenbank hat standardmäßig den Namen ».keystore« und liegt im Home-Verzeichnis des angemeldeten Benutzers (bzw. im Verzeichnis \windows eines Windows-95/98-Einzelplatzsystems). keytool ist ein kommandozeilenbasiertes Hilfsprogramm, das eine große Anzahl an Funktionen bietet. Wir wollen hier nur die für den Umgang mit digitalen Unterschriften benötigten betrachten. Eine vollständige Beschreibung findet sich in der Tool-Dokumentation des JDK. JDK 1.1–6.0 Im JDK 1.1 gab es keytool noch nicht. Stattdessen wurde das Programm javakey zur Schlüsselverwaltung verwendet. Hier soll nur das Security-API des JDK 1.2 und darüber betrachtet werden. Wir wollen daher auf javakey und andere Eigenschaften der Prä-1.2-JDKs nicht eingehen.
» » »
Um ein neues Schlüsselpaar zu erzeugen, ist keytool mit dem Kommando -genkey aufzurufen. Zusätzlich müssen weitere Parameter angegeben werden:
Teil VIII
" Mit -alias wird der Aliasname des neu zu generierenden Eintrags angegeben. Er dient als eindeutiger Bezeichner und wird künftig immer dann gebraucht, wenn auf diesen Eintrag zugegriffen werden soll. " Mit -dname wird ein strukturierter Name für den Schlüsselbesitzer angegeben. Er besteht aus mehreren Teilen, die durch Kommata voneinander getrennt sind. Jeder einzelne Teil besteht aus einer mnemonischen Bezeichnung, gefolgt von einem Gleich-
1181
Kapitel 48
Sicherheit und Kryptografie
heitszeichen und dem zugehörigen Wert. Einzelne Bestandteile können ausgelassen werden. Der strukturierte Name kann folgende Teile enthalten : " CN: Üblicher Name (Common Name) " OU: Organisatorische Einheit (Organisational Unit) " O: Unternehmen/Organisation (Organisation) " L: Stadt (Location) " S: Bundesstaat (State) " C: Land (Countrycode) Die Optionen für den Schlüssel- und Signaturtyp (-keyalg und -sigalg) sowie die Schlüssellänge (-keysize) und die Gültigkeitsdauer (-validity) sollen unspezifiziert bleiben (und daher gemäß den eingebauten Voreinstellungen belegt werden). Zusätzlich besitzt jede Schlüsseldatenbank ein Zugriffspasswort, das mit der Option -storepass (oder alternativ in der Eingabezeile) angegeben wird. Schließlich besitzt jeder private Schlüssel ein Schlüsselpasswort, das mit der Option -keypass (oder über die Eingabezeile) angegeben wird. Wir wollen zunächst ein Schlüsselpaar mit dem Aliasnamen »hjp3« erzeugen und mit dem Passwort »hjp3key« vor unberechtigtem Zugriff schützen. Die Schlüsseldatenbank wird beim Anlegen des ersten Schlüssels automatisch erzeugt und bekommt das Passwort »hjp3ks« zugewiesen. Wir verwenden dazu folgendes Kommando (bitte haben Sie etwas Geduld, das Programm benötigt eine ganze Weile): c:\–>keytool -genkey -alias hjp3 -dname "CN=Guido Krueger,O=Computer Books,C=de" Enter keystore password: hjp3ks Enter key password for (RETURN if same as keystore password): hjp3key Nun wird ein DSA-Schlüsselpaar der Länge 1024 mit einer Gültigkeitsdauer von 90 Tagen erzeugt. Zur Überprüfung kann das Kommando -list (in Kombination mit -v) angegeben werden: C:\—>keytool -alias hjp3 -list -v Enter keystore password: hjp3ks Alias name: hjp3 Creation date: Sun Dec 26 17:11:36 GMT+01:00 1999 Entry type: keyEntry Certificate chain length: 1 Certificate[1]: Owner: CN=Guido Krueger, O=Computer Books, C=de Issuer: CN=Guido Krueger, O=Computer Books, C=de
1182
Kryptografische Grundlagen
Kapitel 48
Serial number: 38663e2d Valid from: Sun Dec 26 17:11:25 GMT+01:00 1999 until: Sat Mar 25 17:11:25 GMT+01:00 2000 Certificate fingerprints: MD5: D5:73:AB:06:25:16:7F:36:27:DF:CF:9D:C9:DE:AD:35 SHA1: E0:A4:39:65:60:06:48:61:82:5E:8C:47:8A:2B:04:A4:6D:43:56:05 Gleichzeitig wird ein Eigenzertifikat für den gerade generierten öffentlichen Schlüssel erstellt. Es kann dazu verwendet werden, digitale Unterschriften zu verifizieren. Jedes Zertifikat in der Schlüsseldatenbank (und damit jeder eingebettete öffentliche Schlüssel) gilt im JDK automatisch als vertrauenswürdig.
Erstellen einer digitalen Unterschrift Wie erwähnt, entsteht eine digitale Unterschrift zu einer Nachricht durch das Verschlüsseln des Message Digest der Nachricht mit dem privaten Schlüssel des Unterzeichnenden. Nachdem wir ein Schlüsselpaar erstellt haben, können wir es nun dazu verwenden, beliebige Dateien zu signieren. Dazu wird die Klasse Signature des Pakets java.security verwendet. Ihre Programmierschnittstelle ähnelt der der Klasse MessageDigest: Zunächst wird ein Objekt mit Hilfe einer Factory-Methode beschafft, dann wird es initialisiert und schließlich werden die Daten durch wiederholten Aufruf von update übergeben. Nachdem alle Daten angegeben wurden, berechnet ein letzter Methodenaufruf das Resultat. Ein Signature-Objekt kann wahlweise zum Signieren oder zum Verifizieren verwendet werden. Welche der beiden Funktionen aktiviert wird, ist nach der Instanzierung durch den Aufruf einer Initialisierungsmethode festzulegen. Ein Aufruf von initSign initialisiert das Objekt zum Signieren, ein Aufruf von initVerify zum Verifizieren. public static Signature getInstance(String algorithm) throws NoSuchAlgorithmException
java.security. Signature
public final void initSign(PrivateKey privateKey) throws InvalidKeyException public final void initVerify(PublicKey publicKey) throws InvalidKeyException INFO
i
i
i
Teil VIII
Als Argument von getInstance wird der gewünschte Signier-Algorithmus übergeben. Auch hier wird – wie an vielen Stellen im Security-API des JDK – eine Strategie verfolgt, nach der die verfügbaren Algorithmen konfigurier- und austauschbar sind. Dazu wurde ein ProviderKonzept entwickelt, mit dessen Hilfe dem API Klassenpakete zur Verfügung gestellt werden können, die Funktionalitäten des Security-Pakets teilweise oder ganz austauschen. Falls der
1183
Kapitel 48
Sicherheit und Kryptografie
Provider beim Aufruf von getInstance nicht angegeben wird, benutzt die Methode den Standard-Provider »SUN«, der zusammen mit dem JDK ausgeliefert wird. Der zu dem von uns generierten Schlüssel passende Algorithmus ist »SHA/DSA«.
Die zum Aufruf der init-Methoden benötigten Schlüssel können aus der Schlüsseldatenbank beschafft werden. Auf sie kann mit Hilfe der Klasse KeyStore des Pakets java.security zugegriffen werden. Dazu wird zunächst ein KeyStore-Objekt instanziert und durch Aufruf von load mit den Daten aus der Schlüsseldatenbank gefüllt. Mit getKey kann auf einen privaten Schlüssel zugegriffen werden, mit getCertificate auf einen öffentlichen: java.security. KeyStore
public static KeyStore getInstance(String type) throws KeyStoreException public final Key getKey(String alias, char[] password) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException public final Certificate getCertificate(String alias) throws KeyStoreException
!
!
!
ACHTUNG Das von getCertificate zurückgegebene Objekt vom Typ Certificate stammt nicht aus dem Paket java.security, sondern java.security.cert. Das in java.security vorhandene gleichnamige Interface wurde bis zum JDK 1.1 verwendet, ab 1.2 aber als deprecated markiert. Wenn nicht mit qualifizierten Klassennamen gearbeitet wird, muss daher die import-Anweisung für java.security.cert.Certificate im Quelltext vor der importAnweisung von java.security.Certificate stehen.
Die Klasse Certificate besitzt eine Methode getPublicKey, mit der auf den im Zertifikat enthaltenen öffentlichen Schlüssel zugegriffen werden kann: java.security.cert. Certificate
java.security. Signature
1184
public PublicKey getPublicKey() Ist das Signature-Objekt initialisiert, wird es durch Aufruf von update mit Daten versorgt. Nachdem alle Daten übergeben wurden, kann mit sign die Signatur abgefragt werden. Wurde das Objekt zum Verifizieren initialisiert, kann das Ergebnis durch Aufruf von verify abgefragt werden: public final byte[] sign() throws SignatureException
Kryptografische Grundlagen
Kapitel 48
public final boolean verify(byte[] signature) throws SignatureException Nach diesen Vorüberlegungen können wir uns nun das Programm zum Erstellen einer digitalen Unterschrift ansehen. Es erwartet zwei Kommandozeilenargumente: den Namen der zu signierenden Datei und den Namen der Datei, in den die digitale Unterschrift ausgegeben werden soll. /* DigitalSignature.java */ import java.io.*; import java.security.cert.Certificate; import java.security.*; public class DigitalSignature { static final String KEYSTORE static final char[] KSPASS static final String ALIAS static final char[] KEYPASS
= = = =
Listing 48.5: Erstellen einer digitalen Unterschrift
"c:\\windows\·keystore"; {'h','j','p','3','k','s'}; "hjp3"; {'h','j','p','3','k','e','y'};
public static void main(String[] args) { try { //Laden der Schlüsseldatenbank KeyStore ks = KeyStore.getInstance("JKS"); FileInputStream ksin = new FileInputStream(KEYSTORE); ks.load(ksin, KSPASS); ksin.close(); //Privaten Schlüssel "hjp3" lesen Key key = ks.getKey(ALIAS, KEYPASS); //Signatur-Objekt erstellen Signature signature = Signature.getInstance("SHA/DSA"); signature.initSign((PrivateKey)key); //Eingabedatei einlesen FileInputStream in = new FileInputStream(args[0]); int len; byte[] data = new byte[1024]; while ((len = in.read(data)) > 0) { //Signatur updaten signature.update(data, 0, len); } in.close(); //Signatur berechnen byte[] result = signature.sign(); //Signatur ausgeben FileOutputStream out = new FileOutputStream(args[1]); out.write(result, 0, result.length); out.close();
Teil VIII
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041
1185
Kapitel 48
Sicherheit und Kryptografie
Listing 48.5: Erstellen einer digitalen Unterschrift (Forts.)
042 } catch (Exception e) { 043 System.err.println(e.toString()); 044 System.exit(1); 045 } 046 } 047 }
Will beispielsweise der Benutzer, dessen privater Schlüssel unter dem Aliasnamen »hjp3« in der Schlüsseldatenbank gespeichert wurde, die Datei DigitalSignature.java signieren und das Ergebnis in der Datei ds1.sign abspeichern, so ist das Programm wie folgt aufzurufen: C:\—>java DigitalSignature DigitalSignature.java ds1.sign
i
i
i
INFO Die Laufzeit des Programms ist beträchtlich. Das Verschlüsseln des Message Digest kann auf durchschnittlichen Rechnern durchaus etwa 30 Sekunden dauern. Glücklicherweise ist die Laufzeit nicht nennenswert von der Dateilänge abhängig, denn das Berechnen des Message Digest erfolgt sehr schnell.
Verifizieren einer digitalen Unterschrift Das Programm zum Verifizieren arbeitet ähnlich wie das vorige. Statt mit initSign wird das Signature-Objekt nun mit initVerify initialisiert und das Ergebnis wird nicht durch Aufruf von sign, sondern durch Aufruf von verify ermittelt. Listing 48.6: Verifizieren einer digitalen Unterschrift
1186
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021
/* VerifySignature.java */ import java.io.*; import java.security.cert.Certificate; import java.security.*; public class VerifySignature { static final String KEYSTORE = "c:\\windows\·keystore"; static final char[] KSPASS = {'h','j','p','3','k','s'}; static final String ALIAS = "hjp3"; public static void main(String[] args) { try { //Laden der Schlüsseldatenbank KeyStore ks = KeyStore.getInstance("JKS"); FileInputStream ksin = new FileInputStream(KEYSTORE); ks.load(ksin, KSPASS); ksin.close(); //Zertifikat "hjp3" lesen
Kryptografische Grundlagen 022 Certificate cert = ks.getCertificate(ALIAS); 023 //Signature-Objekt erstellen 024 Signature signature = Signature.getInstance("SHA/DSA"); 025 signature.initVerify(cert.getPublicKey()); 026 //Eingabedatei lesen 027 FileInputStream in = new FileInputStream(args[0]); 028 int len; 029 byte[] data = new byte[1024]; 030 while ((len = in.read(data)) > 0) { 031 //Signatur updaten 032 signature.update(data, 0, len); 033 } 034 in.close(); 035 //Signaturdatei einlesen 036 in = new FileInputStream(args[1]); 037 len = in.read(data); 038 in.close(); 039 byte[] sign = new byte[len]; 040 System.arraycopy(data, 0, sign, 0, len); 041 //Signatur ausgeben 042 boolean result = signature.verify(sign); 043 System.out.println("verification result: " + result); 044 } catch (Exception e) { 045 System.err.println(e.toString()); 046 System.exit(1); 047 } 048 } 049 }
Kapitel 48 Listing 48.6: Verifizieren einer digitalen Unterschrift (Forts.)
Soll die Datei DigitalSignature.java mit der im vorigen Beispiel erstellten Signatur verifiziert werden, kann das durch folgendes Kommando geschehen: C:\—>java VerifySignature DigitalSignature.java ds1.sign verification result: true Wird nur ein einziges Byte in DigitalSignature.java verändert, ist die Verifikation negativ und das Programm gibt false aus. Durch eine erfolgreich verifizierte digitale Unterschrift können wir sicher sein, dass die Datei nicht verändert wurde. Zudem können wir sicher sein, dass sie mit dem privaten Schlüssel von »hjp3« signiert wurde, denn wir haben sie mit dessen öffentlichem Schlüssel verifiziert.
48.1.7 Zertifikate
Teil VIII
Ein großes Problem bei der Public-Key-Kryptografie besteht darin, die Authentizität von öffentlichen Schlüsseln sicherzustellen. Würde beispielsweise B einen öffentlichen Schlüssel publizieren, der glaubhaft vorgibt, A zu gehören, könnte dies zu verschiedenen Unannehmlichkeiten führen:
1187
Kapitel 48
Sicherheit und Kryptografie
" Angenommen, C will eine verschlüsselte Nachricht an A schicken und verwendet dazu versehentlich den öffentlichen Schlüssel von B (den es für den von A hält). B kann nun mit seinem eigenen privaten Schlüssel die vertrauliche Nachricht lesen. Um weiterhin unentdeckt zu bleiben, könnte B die Nachricht anschließend sogar mit dem echten öffentlichen Schlüssel von A verschlüsseln und an A weitersenden. " Noch folgenreicher ist die Umkehrung. B könnte (als A getarnt) Dokumente mit einer digitalen Unterschrift versenden, die für die von A gehalten wird und so im Namen von A beispielsweise geschäftliche oder rechtlich relevante Transaktionen auslösen. A wäre anschließend in der unangenehmen Sitution, nachweisen zu müssen, dass ihm der von B verwendete private Schlüssel nie gehörte und bekannt war. Einen Schutz gegen derartigen Missbrauch bringen Zertifikate. Ein Zertifikat ist eine Art Echtheitsbeweis für einen öffentlichen Schlüssel, das damit ähnliche Aufgaben erfüllt wie ein Personalausweis oder Reisepass. Ein Zertifikat besteht meist aus folgenden Teilen: " Dem öffentlichen Schlüssel des Zertifikatsinhabers " Identitätsmerkmalen des Inhabers (beispielsweise Name, Wohnort, Firma, einem Photo etc.) " Der Bezeichnung des Zertifikatsausstellers " Der digitalen Signatur des Ausstellers " Der digitalen Signatur des Inhabers Die Glaubwürdigkeit des Zertifikats hängt von der Glaubwürdigkeit des Ausstellers ab. Wird dieser als vertrauenswürdig angesehen, d.h., kann man seiner digitialen Unterschrift trauen, so wird man auch dem Zertifikat trauen und den darin enthaltenen öffentlichen Schlüssel akzeptieren. Dieses Vertrauen kann einerseits darauf basieren, dass der Aussteller eine anerkannte Zertifizierungsautorität ist (auch Certification Authority, kurz CA, genannt), deren öffentlicher Schlüssel bekannt und deren Seriösität institutionell manifestiert ist. Mit anderen Worten: dessen eigenes Zertifikat in der eigenen Schlüsselverwaltung bekannt und als vertrauenswürdig deklariert ist. Andererseits kann das Vertrauen in das Zertifikat daher stammen, dass der Aussteller persönlich bekannt ist, sein öffentlicher Schlüssel eindeutig nachgewiesen ist und seiner Unterschrift Glauben geschenkt wird. Der erste Ansatz wird beispielsweise bei X.509-Zertifikaten verfolgt. Institute, die derartige Zertifikate ausstellen, werden meist staatlich autorisiert und geprüft. Beispiele dafür sind VeriSign, Thawte oder das TA Trustcenter. Der zweite Ansatz liegt beispielsweise den Zertifikaten in PGP zugrunde. Hier ist es sogar möglich, öffentliche Schlüssel mit mehreren digitalen Unterschriften unterschiedlicher Personen zu signieren und so die Glaubwürdigkeit (bzw. ihre Reichweite) zu erhöhen.
1188
Sicherheitsmechanismen in Java
Kapitel 48
Zertifizierungsinstitute stellen meist auch Schlüsseldatenbanken zur Verfügung, aus denen Zertifikate abgerufen werden können. Diese dienen auch als Anlaufstelle, um ungültig gewordene oder unbrauchbare Zertifikate zu registrieren. Lokale Schlüsselverwaltungen können sich mit diesen Informationen synchronisieren, um ihren eigenen Schlüsselbestand up-to-date zu halten.
48.2 Sicherheitsmechanismen in Java 48.2.1 Sprachsicherheit Java wurde von Anfang an mit höheren Sicherheitsansprüchen entworfen, als dies üblicherweise bei Programmiersprachen der Fall ist. Einer der Hauptgründe dafür war der Wunsch, den Aufruf von Applets, die aus dem Internet geladen wurden, so sicher wie möglich zu machen. Selbst bösartige Applets sollten nicht in der Lage sein, ernsthafte Angriffe auf den Computer, das Betriebssystem oder die Ressourcen des Anwenders auszuführen. Sicherheit beginnt in Java schon bei der Implementierung der Sprache. Anders als in C oder C++ gibt es beispielsweise keine direkten Zugriffe auf den Hauptspeicher und keine Pointerarithmetik. Das Memory-Management arbeitet vollautomatisch. Sicherheitslücken, die durch (provozierte) Speicherüberläufe verursacht werden, sind damit nicht ohne Weiteres möglich. Alle Typkonvertierungen werden zur Laufzeit geprüft und unerlaubte Umwandlungen von vorne herein ausgeschlossen. Auch Zugriffe auf Array-Elemente und Strings werden grundsätzlich auf Einhaltung der Bereichsgrenzen geprüft. Zugriffe, die außerhalb des erlaubten Bereichs liegen, führen nicht zu undefiniertem Verhalten, sondern werden definiert abgebrochen und lösen eine Ausnahme aus. Beim Laden von Bytecode über das Netz wird dieser vor der Ausführung von einem Bytecode-Verifier untersucht. Auf diese Weise wird beispielsweise sichergestellt, dass nur gültige Opcodes verwendet werden, dass alle Sprunganweisungen auf den Anfang einer Anweisung zeigen, dass alle Methoden mit korrekten Signaturen versehen sind und dass Ausdrücke korrekt typisiert sind. Zudem implementiert der Klassenlader einen lokalen Namensraum, der verhindert, dass bestehende Klassen verändert oder ersetzt werden können.
48.2.2 Das Sandbox-Konzept
Teil VIII
Traditionell wurde in Java zwischen lokalem Code und solchem, der aus dem Netz geladen wird, bezüglich seiner Sicherheitsanforderungen rigoros unterschieden. Während lokalem Code (also Applikationen und von der Festplatte geladenen Applets) der Zugriff auf alle verfügbaren Ressourcen gestattet ist, dürfen Applets, die aus dem Netz geladen wurden, nur einen kleinen Teil davon verwenden. Sie halten sich gewissermaßen in einem
1189
Kapitel 48
Sicherheit und Kryptografie
Sandkasten auf, in dem sie nur ungefährliche Spielzeuge verwenden und keinen ernsthaften Schaden anrichten können (daher der Name »Sandbox«). Zu den »gefährlichen Spielzeugen«, die nicht verwendet werden dürfen, zählen: " der lesende und schreibende Zugriff auf Dateien des lokalen Computers, " das Öffnen von TCP/IP-Verbindungen zu einem anderen als dem Host, von dem das Applet geladen wurde, " das Akzeptieren von TCP/IP-Verbindungen auf privilegierten Port-Nummern, " das Lesen benutzerbezogener System-Properties wie »user.name«, »user.home«, »user.dir« oder »java.home«, " das Erzeugen eines Top-Level-Fensters ohne Warnhinweis, " das Ausführen externer Programme, " das Laden von System-Libraries, " das Beenden der virtuellen Maschine.
i
i
i
INFO Benötigte ein Applet Zugriff auf diese Ressourcen, gab es im JDK 1.0 die Möglichkeit, die zugehörigen Dateien auf der lokalen Festplatte abzulegen. Denn wenn das Applet zur Ausführung nicht aus dem Netz, sondern aus den lokalen Dateien gestartet wurde, galt es automatisch als vertrauenswürdig und hatte Zugriff auf alle ansonsten verbotenen Ressourcen.
48.2.3 Veränderungen im JDK 1.1 und 1.2 Mit dem JDK 1.1 wurde eine neue Möglichkeit eingeführt, Applets Zugriff auf geschützte Ressourcen zu ermöglichen. Dazu müssen alle zum Applet gehörenden Dateien in eine jar-Datei verpackt und mit einer digitalen Unterschrift versehen werden. Wird der Unterzeichner auf dem ausführenden Computer als vertrauenswürdig angesehen (indem sein öffentlicher Schlüssel an geeigneter Stelle bekanntgemacht wurde), kann das Applet die Sandbox verlassen und hat Zugriff auf alle geschützten Ressourcen. Mit dem JDK 1.2 wurde dieses Konzept weiter verfeinert. Während es im JDK 1.1 schwierig war, die Zugriffsbeschränkungen schrittweise zu lockern, ist das nun viel einfacher. Die Zugriffsbeschränkungen sind konfigurierbar und können mit Hilfe einer Policy-Datei auch ohne Programmänderungen angepasst werden. Sie können wahlweise davon abhängig gemacht werden, von wem die Signatur stammt, als auch davon, woher das Applet geladen wurde. Zudem wurde die prinzipielle Unterscheidung zwischen lokalem und netzwerkbasiertem Code aufgegeben. Obwohl die Sicherheitseinstellungen so konfiguriert werden könnten, dass lokalem Code voller Zugriff auf alle Ressourcen gewährt wird, ist das standardmäßig nicht mehr der Fall.
1190
Signierte Applets
Kapitel 48
48.3 Signierte Applets In diesem Abschnitt wird beschrieben, wie einem Applet der Zugriff auf geschützte Ressourcen gewährt werden kann. Wir gehen dazu in folgenden Schritten vor: 1. Zunächst wird ein Applet entwickelt, das auf einige beschränkte Ressourcen zugreift. Ohne weitere Vorkehrungen ist es nicht lauffähig, sondern wird mit einer SecurityException abgebrochen. 2. Anschließend zeigen wir, wie das Applet mit Hilfe des in Abschnitt 48.1.6, Seite 1180 erzeugten Schlüsselpaars signiert wird. 3. Danach demonstrieren wir die Weitergabe von Zertifikaten. 4. Schließlich zeigen wir, wie die Sicherheitseinstellungen so angepasst werden, dass das Applet Zugriff auf die gewünschten Ressourcen erhält.
48.3.1 Ein »unerlaubtes« Applet Wir wollen uns die Aufgabe stellen, ein Applet zu schreiben, das einige Sicherheitsverstöße begeht. Zunächst soll es eine Datei auf dem lokalen Computer erzeugen und einen Zeitstempel hineinschreiben. Zusätzlich soll es auf einige geschützte System-Properties zugreifen und deren Inhalt in die Datei schreiben. Das Applet hat folgenden Aufbau: /* TrustedApplet.java */ import import import import
java.awt.*; java.applet.*; java.util.*; java.io.*;
Listing 48.7: Ein »unerlaubtes« Applet
public class TrustedApplet extends Applet { static final String ALLOWED_DIR = "c:\\tmp\\applets\&; static final String FNAME = "TrustedApplet.log"; static final String LOGMSG = "Erzeugt von Applet: "; String msg; public void init() { msg = "Uninitialisiert"; FileWriter out = null; try { //Ausgabedatei erzeugen out = new FileWriter(ALLOWED_DIR + FNAME); //Logmessage schreiben out.write(LOGMSG); //Zeitstempel schreiben
Teil VIII
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025
1191
Kapitel 48 Listing 48.7: Ein »unerlaubtes« Applet (Forts.)
Sicherheit und Kryptografie 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 }
GregorianCalendar cal = new GregorianCalendar(); out.write(cal.get(Calendar.DATE) + "."); out.write((cal.get(Calendar.MONTH) + 1) + "."); out.write(cal.get(Calendar.YEAR) + " "); out.write(cal.get(Calendar.HOUR_OF_DAY) + ":"); out.write(cal.get(Calendar.MINUTE) + ":"); out.write(cal.get(Calendar.SECOND) + ""); out.write(System.getProperty("line.separator")); //System-Properties lesen und in Datei schreiben out.write(getProp("user.name")); out.write(getProp("user.home")); out.write(getProp("user.dir")); //Datei schließen msg = "Alle Sicherheitshuerden ueberwunden!"; } catch (Exception e) { msg = e.toString(); } finally { if (out != null) { try { out.close(); } catch (IOException e) { //silently ignore } } } } public void paint(Graphics g) { g.drawString(msg, 20, 20); } private String getProp(String prop) { return prop + "=" + System.getProperty(prop) + System.getProperty("line.separator"); }
Es versucht zunächst, eine Datei c:\tmp\applets\TrustedApplet.log zu erzeugen. Wenn dies gelingt, instanziert es ein aktuelles GregorianCalendar-Objekt und schreibt dessen Werte in die Datei. Schließlich versucht es, die System-Properties »user.name«, »user.home« und »user.dir« zu lesen und deren Werte ebenfalls in die Datei zu schreiben. Sind all diese Versuche erfolgreich, gibt das Applet die Meldung »Alle Sicherheitshuerden ueberwunden!« aus. Tritt eine Ausnahme auf, wird deren Text ausgegeben.
1192
Signierte Applets
INFO
Kapitel 48
i
i
i
Um das Schreiben der Datei zu ermöglichen, ist ein Verzeichnis c:\tmp\applets anzulegen. Andernfalls gibt es eine IOException – selbst wenn alle Sicherheitshürden genommen sind.
Das Applet kann mit folgender HTML-Datei gestartet werden: 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018
TrustedApplet Demo
Listing 48.8: Vorläufige HTMLDatei zum Aufruf des unerlaubten Applets
TrustedApplet Demo
Das Applet kann nun beispielsweise mit dem appletviewer gestartet werden: appletviewer TrustedApplet.html Wird es ohne weitere Vorkehrungen gestartet, scheitert es bereits am Erzeugen der Ausgabedatei und gibt eine SecurityException auf dem Bildschirm aus.
48.3.2 Signieren des Applets Zum Signieren ist es zunächst erforderlich, alle für den Betrieb des Applets nötigen Dateien in ein jar-File zu packen. Signiert wird also nicht eine einzelne .class-Datei, sondern alle Dateien innerhalb des jar-Archivs. Dazu verwenden wir folgendes Kommando: jar cvf trapp.jar TrustedApplet.class
Teil VIII
Jetzt wird ein jar-Archiv trapp.jar erstellt, das die Klassendatei TrustedApplet.class enthält. Dieses Archiv muss nun signiert werden. Dazu steht im JDK das Hilfsprogramm jarsigner zur Verfügung. Es arbeitet kommandozeilenbasiert und wird folgendermaßen aufgerufen: jarsigner -signedjar outjar injar alias
1193
Kapitel 48
Sicherheit und Kryptografie
Die einzelnen Elemente haben folgende Bedeutung: " injar ist der Name des zu signierenden jar-Archivs. " outjar ist der Name der Ausgabedatei, also des signierten Archivs. " alias ist der Aliasname des Eintrags in der Schlüsseldatenbank, dessen privater Schlüssel zum Signieren verwendet werden soll. Nachdem wir in Abschnitt 48.1.6, Seite 1180 bereits ein Schlüsselpaar mit dem Alias »hjp3« erzeugt und in der Schlüsseldatenbank abgelegt haben, muss der Aufruf von jarsigner so erfolgen: C:\—>jarsigner -signedjar strapp.jar trapp.jar hjp3 Enter Passphrase for keystore: hjp3ks Enter key password for hjp3: hjp3key Die beiden Passwörter zum Zugriff auf die Schlüsseldatenbank und den Schlüssel werden auf Nachfrage in der Kommandozeile angegeben. Nachdem das Programm einige Zeit gerechnet hat, erzeugt es das signierte Archiv mit dem Namen strapp.jar.
i
i
i
INFO jarsigner bietet neben den hier erwähnten noch weitere Optionen, auf die wir nicht weiter eingehen wollen. Das Programm wird zusammen mit den anderen Hilfsprogrammen in der Tool-Dokumentation des JDK ausführlich beschrieben.
Bisher wurde das Applet direkt aus der .class-Datei geladen. Um es aus einem jar-Archiv zu laden, muss das APPLET-Tag der HTML-Datei um das archive-Argument erweitert werden: Listing 48.9: Aufruf des signierten Applets
1194
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021
<– TrustedApplet.html –> TrustedApplet Demo TrustedApplet Demo
Signierte Applets
INFO
Kapitel 48
i
i
i
Wir könnten das Applet jetzt wie zuvor starten, würden aber immer noch dieselbe SecurityException erhalten. Es ist zwar signiert und das Zertifikat ist auf diesem Rechner bekannt (denn hier wurde es erstellt). Die Policy-Datei ist aber noch nicht angepasst und daher lehnt der SecurityManager des JDK die Ausführung der gefährlichen Operationen nach wie vor ab.
48.3.3 Ex- und Import von Zertifikaten Soll das signierte Applet auf anderen Arbeitsplätzen laufen, ist es erforderlich, das Zertifikat des Schlüssels, mit dem es signiert wurde, dort zu installieren. Soll es dagegen nur auf dem Arbeitsplatz laufen, auf dem auch der Schlüssel generiert wurde, ist das nicht erforderlich. Bei der Generierung des Schlüssels wurde ja auch ein (selbst signiertes) Zertifikat erzeugt. Um das Zertifikat weitergeben zu können, muss es zunächst unter Verwendung der Option -export von keytool aus der lokalen Schlüsseldatenbank exportiert werden: keytool -export -alias hjp3 -file hjp3.cert Es liegt nun in der Datei hjp3.cert und kann auf das Zielsystem kopiert werden. Mit der -import-Option von keytool kann es dort in die Schlüsseldatenbank aufgenommen werden: C:\—>keytool -import -alias hjp3 -file hjp3.cert Enter keystore password: hjp3ks Owner: CN=Guido Krueger, O=Computer Books, C=de Issuer: CN=Guido Krueger, O=Computer Books, C=de Serial number: 38663e2d Valid from: Sun Dec 26 17:11:25 GMT+01:00 1999 until: Sat Mar 25 17:11:25 GMT+01 :00 2000 Certificate fingerprints: MD5: D5:73:AB:06:25:16:7F:36:27:DF:CF:9D:C9:DE:AD:35 SHA1: E0:A4:39:65:60:06:48:61:82:5E:8C:47:8A:2B:04:A4:6D:43:56:05 Trust this certificate? [no]: y Certificate was added to keystore
Teil VIII
Nach dem Aufruf muss zunächst das Passwort der Schlüsseldatenbank angegeben werden. Dann zeigt das Programm die Eigenschaften des Zertifikats an und erwartet, dass die Informationen bestätigt werden. Anschließend wird das Zertifikat in die Schlüsseldatenbank aufgenommen.
1195
Kapitel 48
Sicherheit und Kryptografie
48.3.4 Anpassen der Policy-Datei Policy-Dateien Der letzte Schritt besteht darin, die Sicherheitseinstellungen auf dem Zielsystem anzupassen. Applets, die mit dem Zertifikat verifiziert werden können, das unter dem Alias »hjp3« in der Schlüsseldatenbank abgelegt wurde, sollen Dateien im Verzeichnis c:\tmp\applets lesen und schreiben und auf die System-Properties »user.name«, »user.home« und »user.dir« zugreifen können. Die Sicherheitseinstellungen des JDK werden mit Hilfe von Policy-Dateien definiert. Es gibt zwei Stellen im Dateisystem, von denen das JDK sie standardmäßig einliest: " Die System-Policies befinden sich in der Datei java.policy im Unterverzeichnis jre\lib\security des JDK-Installationsverzeichnisses. Diese Datei braucht normalerweise nicht verändert zu werden, sie enthält die globalen Sicherheitseinstellungen. " Die benutzerbezogenen Sicherheitseinstellungen können in der Datei .java.policy abgelegt werden. Sie liegt im Home-Verzeichnis des aktuellen Benutzers. Auf Windows-95/98-Einzelplatzsystemen liegt sie (wie die Schlüsseldatenbank) im Verzeichnis c:\windows. Diese Datei ist standardmäßig nicht vorhanden, kann aber leicht selbst angelegt werden.
*
*
*
TIPP Policy-Dateien können auch an beliebigen anderen Stellen im Dateisystem liegen. In diesem Fall muss beim Aufruf des Java-Interpreters das System-Property »java.security.policy« mit dem Namen der zu verwendenen Policy-Datei angegeben werden. Wäre beispielsweise hjp3policy die zu verwendende Policy-Datei, so müsste der Appletviewer mit der Option »-J-Djava.security.policy=hjp3policy« aufgerufen werden.
Das Format einer Policy-Datei Policy-Dateien sind zeilenorientierte Textdateien, die mit einem gewöhnlichen Texteditor bearbeitet werden können. Alternativ stellt das JDK ein einfaches GUI-basiertes Hilfsprogramm mit dem Namen policytool zur Verfügung, mit dem Policy-Dateien erstellt und bearbeitet werden können. Auf seine Verwendung wollen wir aber nicht weiter eingehen. Eine Policy-Datei enthält zwei Arten von Einträgen. Beide sind optional: " Einen »keystore«-Eintrag, der die Lage der Schlüsseldatenbank angibt " Beliebig viele »grant«-Einträge, mit denen Berechtigungen definiert werden. Alle Einträge werden mit einem Semikolon beendet. Kommentare können an beliebiger Stelle durch // oder /* ... */ angelegt werden.
1196
Signierte Applets
Kapitel 48
Der »keystore«-Eintrag erwartet als Argument einen URL, der auf die Schlüsseldatenbank verweist. Auf einem Windows-98-Einzelplatzsystem sieht er beispielsweise so aus: keystore "file:/c:/windows/.keystore"; Die »grant«-Einträge haben folgende Syntax: grant [SignedBy "signer"] [, CodeBase "URL"] { permission permission_class [ "target" ] [, "action"] [, SignedBy "signers"]; ... }; Eine Berechtigung kann wahlweise an einen Unterzeichner oder eine Ladeadresse (oder beide) vergeben werden. Die Option »SignedBy« führt eine Liste von Aliasnamen auf, deren Zertifikate vorhanden sein müssen, damit die Berechtigung gewährt wird. Die Option »CodeBase« spezifiziert die Adresse, von der ein Applet geladen werden darf, um die Berechtigung zu halten. Fehlt die CodeBase-Klausel, wird nur die Unterzeichnerliste verwendet; fehlt die SignedBy-Klausel, ist es nur die Ladeadresse. Nach einer öffnenden geschweiften Klammer folgen beliebig viele Berechtigungen, die jeweils durch das Schlüsselwort »permission« eingeleitet werden. Anschließend wird der Eintrag mit einer schließenden geschweiften Klammer abgeschlossen. Der zuvor spezifizierte Berechtigte erhält alle Berechtigungen, die zwischen den beiden Klammern angegeben sind.
Teil VIII
Jede Berechtigung muss als erstes Argument den Namen einer Berechtigungsklasse angeben. Daneben kann sie zwei weitere Argumente target und action haben, mit denen Details spezifiziert werden. Tabelle 48.1, Seite 1198 listet die gebräuchlichsten Berechtigungen und ihre Argumente auf. Details können (und müssen) in dem Dokument »Java Security Architecture« nachgelesen werden, das Bestandteil der JDK-Dokumentation ist.
1197
Kapitel 48
Tabelle 48.1: Wichtige Permission-Klassen
Sicherheit und Kryptografie
Klasse
Zugriff auf
Target
Action
java.io.
Dateien und
Datei- oder Verzeichnis- read, write, delete, exe-
FilePermission
Verzeichnisse
namen. Wird als letztes cute Zeichen ein »*« angegeben, so gilt die Berechtigung für das komplette Verzeichnis. Steht dort ein »-«, so gilt sie zusätzlich für alle Unterverzeichnisse. Wird »<>« angegeben, gilt die Berechtigung für alle Dateien in allen Verzeichnissen!
java.net.
TCP/IP-Verbindungen
Hostname oder IP-Adres- accept, connect, listen, se, gefolgt vom Port-
SocketPermission
resolve
Nummern-Bereich java.util.
System-Properties
Name des Proper
read, write
Die Klasse Runtime
exitVM, stopThread, lo- -
PropertyPermission java.lang.
adLibrary, queuePrintJob,
RuntimePermission
... java.awt.
Window-Ressourcen
accessClipboard, show- WindowWithoutWar-
AWTPermission
ningBanner, ... java.security.
Alle Ressourcen
-
-
AllPermission
Erstellen der Policy-Datei Nach diesen Vorbemerkungen können wir die Policy-Datei \windows\.java.policy erstellen. Sie hat folgenden Inhalt: keystore "file:/c:/windows/.keystore"; grant SignedBy "hjp3" { permission java.io.FilePermission "c:\\tmp\\applets\\*", "read,write";
1198
Signierte Applets
Kapitel 48
permission java.util.PropertyPermission "user.name", "read"; permission java.util.PropertyPermission "user.home", "read"; permission java.util.PropertyPermission "user.dir", "read"; }; Im ersten Eintrag geben wir die Position der Schlüsseldatenbank an, sie liegt im Verzeichnis c:\windows. Anschließend definieren wir die Berechtigungen für alle Applets, die mit dem Zertifikat »hjp3« signiert wurden. Zunächst erhalten sie Schreib- und Leseberechtigung im Verzeichnis c:\tmp\applets. Dort können sie ohne weitere Einschränkungen Dateien anlegen, überschreiben und lesen. Zusätzlich erlauben wir den so signierten Applets, die drei System-Properties »user.name«, »user.home« und »user.dir« zu lesen. Nun lässt sich unser signiertes Applet ohne SecurityException aufrufen und gibt die erlösende Meldung »Alle Sicherheitshuerden ueberwunden« aus. Im Verzeichnis c:\tmp\applets sollte sich anschließend eine Datei TrustedApplet.log befinden und etwa folgenden Inhalt haben: Erzeugt von Applet: 30.12.1999 20:50:40 user.name=Guido Krüger user.home=C:\WINDOWS user.dir=C:\arc\doku\hjp3\misc
48.3.5 Die Klasse SecurityManager Die Prüfung der Zugriffsberechtigungen wird mit Hilfe der Klasse SecurityManager aus dem Paket java.lang vorgenommen. Der SecurityManager ist ein Objekt, das entweder gar nicht oder genau einmal im laufenden Java-Programm vorhanden ist. Nach der ersten Instanzierung kann es nicht mehr geändert oder entfernt werden. Zugriffe auf den SecurityManager sind an den Stellen der Laufzeitbibliothek eingebaut, an denen auf gefährliche Ressourcen zugegriffen wird. Ein Beispiel aus der Klasse FileInputStream sieht etwa so aus: ... SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(name); } //Gefährlicher Code ...
Teil VIII
Zunächst wird geprüft, ob ein SecurityManager installiert wurde. Ist das nicht der Fall, fährt das Programm ohne Einschränkung fort. Gibt es dagegen einen SecurityManager, wird dessen checkRead-Methode aufgerufen. Sie löst eine SecurityException aus, wenn die gewünschte Berechtigung fehlt. Der Code hinter dem checkRead wird in diesem Fall
1199
Kapitel 48
Sicherheit und Kryptografie
nicht mehr erreicht. Ist die Berechtigung dagegen vorhanden, wird checkRead ohne weitere Aktionen beendet und der dahinterliegende Code ausgeführt. Applets besitzen grundsätzlich einen SecurityManager. Der AppletViewer bzw. WebBrowser sorgen während der Initialisierung für dessen Instanzierung. Applikationen dagegen haben normalerweise keinen SecurityManager (das ist der Grund, weshalb in Applikationen alle gefährlichen Operationen erlaubt sind). Soll eine Applikation einen SecurityManager erhalten, so kann sie entweder mit der Option »-Djava.security.manager« gestartet werden oder der SecurityManager kann im Programm selbst installiert werden: ... System.setSecurityManager(new SecurityManager()); ... Das folgende Beispielprogramm gibt den Inhalt des System-Proper »user.name« aus. Normalerweise kann es ohne Fehler ausgeführt werden: Listing 48.10: Ausgeben des System-Property »user.name«
001 002 003 004 005 006 007 008 009 010 011
/* SecuMgrTest.java */ public class SecuMgrTest { public static void main(String[] args) { System.out.println( "user.name is " + System.getProperty("user.name") ); } }
Läuft es dagegen unter Kontrolle eines SecurityManagers, so führt der Aufruf zu einer SecurityException: C:\—>java -Djava.security.manager SecuMgrTest Exception in thread "main" java.security.AccessControlException: access denied (java.util.PropertyPermission user.name read) at java.security.AccessControlContext.checkPermission( AccessControlContext.java:276) at java.security.AccessController.checkPermission( AccessController.java:403) at java.lang.SecurityManager.checkPermission( SecurityManager.java:549) at java.lang.SecurityManager.checkPropertyAccess( SecurityManager.java:1242) at java.lang.System.getProperty(System.java:555) at Test1.main(SecuMgrTest.java:7)
1200
Zusammenfassung
Kapitel 48
Bei Bedarf kann man Applikationen auf diese Weise mit denselben Sicherheitsmechanismen ausstatten wie Applets. jar-Dateien, aus denen Applikationen geladen werden, lassen sich ebenso signieren wie solche, aus denen Applets geladen werden. Die in der Policy-Datei definierten Rechte gelten dann für die daraus gestartete Applikation.
Zusammenfassung In diesem Kapitel wurden folgende Themen behandelt: " Einfache Verschlüsselungen wie Ziffernsubstitution oder Exklusiv-ODER " Message Digests und die Klasse MessageDigest " Kryptografische Zufallszahlen und die Klasse SecureRandom " Public-Key-Kryptosysteme und Hybrid-Kryptosysteme " Erzeugen und Verifizieren von digitalen Unterschriften mit der Klasse Signature " Verwalten von Schlüsseln, der Umgang mit dem Programm keytool und der Zugriff auf die Schlüsseldatenbank mit der Klasse KeyStore " Die Bedeutung von Zertifikaten in der Public-Key-Kryptografie " Die eingebauten Sicherheitsmechanismen der verschiedenen Java-Versionen und das Sandbox-Konzept " Das Signieren eines Applets mit jarsigner, der Ex- und Import von Zertifikaten und das Erstellen einer Policy-Datei
Teil VIII
" Die Klasse SecurityManager
1201
49
Sound
49.1
Grundlagen und Konzepte
Seit der Version 1.3 besitzt das JDK Sound-Fähigkeiten, die weit über die in Abschnitt 39.3, Seite 933 erläuterten Möglichkeiten hinausgehen. Mit Hilfe des Sound-API können Samples abgespielt oder aufgenommen werden. Es können Midi-Dateien erzeugt oder wiedergegeben werden und es ist möglich, direkt auf angeschlossene oder eingebaute Tonerzeuger zuzugreifen. Das API abstrahiert alle für das Erzeugen und Bearbeiten von Sounds wesentlichen Konzepte und unterstützt die Erkennung und Verwendung unterschiedlichster Hardware-Komponenten. Das Sound-API ist allerdings nicht ganz leicht zu bedienen und wird in der Literatur sehr stiefmütterlich behandelt. Die Schwierigkeiten haben mehrere Ursachen: " Einerseits handelt es sich um ein Low-Level-API, also eines, bei dem schon zur Erzielung relativ einfacher Effekte recht viel Programmieraufwand nötig ist.
" Zu guter Letzt erfordert der Umgang mit dem Sound-API ein grundlegendes Verständnis für viele der auf diesem Gebiet relevanten Konzepte. Das API
Teil VIII
" Andererseits kann das API wenig Annahmen über standardmäßig verfügbare Hardware machen. Soll beispielsweise eine Flötenmelodie erklingen, muss das Programm erst einmal herausfinden, ob ein Synthesizer eingebaut oder über einen der Midi-Ports erreichbar ist. Zudem muss er das entsprechende Instrument zur Verfügung stellen können. Erst dann kann dieser konfiguriert und mit den entsprechenden Tonerzeugungskommandos beschickt werden.
Kapitel 49
Sound
ist sehr eng an Begriffe angelehnt, die direkte Pendants in der elektronischen oder elektronikunterstützten Musik haben. Ohne ein Basiswissen in Themenbereichen wie Samples, Midi, Tracks, Timecodes, Sequencer, Synthesizer Soundbanks oder Mixer sind die korrespondierenden Klassen und Interfaces schwerlich korrekt zu verwenden. Wir wollen deshalb in diesem Kapitel einen sehr pragmatischen Ansatz wählen. Erforderliche Begriffe werden, wo nötig, lediglich kurz erklärt, denn wir gehen davon aus, dass beim Leser bereits ein Grundstock an einschlägigen Grundkenntnissen vorhanden ist. Oder wenigstens die Bereitschaft, sich diese während des Lesens und Experimentierens anzueignen. Auch werden wir die APIs nur ansatzweise erläutern, denn mehr ist aus Platzgründen nicht möglich. Die Beispielprogramme wurden so gewählt, dass sie einen unmittelbaren Praxisnutzen haben. Sie stellen leicht einzusetzende Routinen zum Abspielen von Samples sowie zum Erzeugen einfacher Midi-Sequenzen und zum Abspielen von Midi-Files zur Verfügung. Damit werden die wichtigsten Standardfälle beim Einsatz von Sound abgedeckt. Das Sound-API dient als Basis für alle Arten von Sound-Support in Java. Seine Anwendungsgebiete reichen von interaktiven Applikationen oder Spielen mit Sound-Unterstützung über Media-Player und Musik-Tools bis hin zu Telefonie- und Konferenzapplikationen. Des Weiteren ist das Sound-API Basis höherer Programmierschnittstellen, wie etwa des Java Media Framework, das eine Schnittstelle zum Abspielen und Erzeugen einer ganzen Reihe von Audio- und Videoformaten zur Verfügung stellt.
!
!
!
ACHTUNG Die Beispiele in diesem Buch funktionieren natürlich nur dann, wenn auf dem Computer, an dem sie nachvollzogen werden sollen, eine geeignete (und vom Java-Sound-API unterstützte) Sound-Hardware vorhanden ist. Dabei handelt es sich typischerweise um eine SoundKarte, es kann aber (wenigstens beim Midi-API) auch eine Midi-Schnittstelle mit angeschlossenem Synthesizer verwendet werden. Ist eine solche Hardware nicht verfügbar, erklingt beim Ausführen der Beispielprogramme entweder gar nichts (oder das Falsche) oder es wird eine Ausnahme ausgelöst.
49.2 Gesampelter Sound 49.2.1 Was ist Sampling? Das Sound-API macht eine sehr grundlegende Unterscheidung zwischen gesampeltem Sound und Midi-Sound. Beim Sampling, das wir in diesem Abschnitt behandeln wollen, wird ein Audio-Signal in viele kleine Stücke zerlegt, deren Amplitude in sehr kurzen Abständen mit Hilfe eines Analog-Digital-Konverters gemessen wird:
1204
Gesampelter Sound
Kapitel 49 Abbildung 49.1: Samplen eines Audio-Signals
Die Frequenz, mit der die Abtastung geschieht, bezeichnet man als Sampling Rate und sie sollte mindestens doppelt so hoch sein wie die größte aufzuzeichnende Frequenz. Bei Audio-CDs beträgt sie 44100 Hz und die Auflösung des A/D-Konverters beträgt 16 Bit. Speichert man die so entstehenden Amplitudenwerte fortlaufend ab, so kann man bei Kenntnis der Sampling-Rate daraus später das Originalsignal näherungsweise rekonstruieren. Sind Sampling-Rate und Auflösung hoch genug, kann das menschliche Ohr keinen Unterschied zwischen gesampeltem und Originalsignal feststellen: Abbildung 49.2: Ein gesampeltes Audio-Signal
49.2.2 Das Sampling-API Das Sound-API macht keine Annahmen über vorhandene Hardware oder angeschlossene Geräte. Stattdessen stellt es Methoden zur Verfügung, mit denen die vorhandene Hardware zur Laufzeit ermittelt und Objekte zum Zugriff darauf beschafft werden können. Im Falle von gesampeltem Sound dient dazu die Klasse AudioSystem aus dem Paket javax.sound.sampled. Sie besitzt eine Reihe von statischen Methoden, mit denen die grundlegenden Hilfsmittel für den Umgang mit gesampeltem Sound beschafft werden können: " Die Klasse AudioFormat beschreibt das Format von gesampeltem Sound. Sie enthält unter anderem Informationen über das Kodierungsverfahren, die Anzahl der Kanäle, die Sampling Rate oder die Auflösung der einzelnen Samples.
Teil VIII
" Die Klasse AudioFileFormat beschreibt das Format von Dateien, die gesampelten Sound enthalten. Im Wesentlichen wird dazu ein eingebettetes AudioFormat-Objekt verwendet, das mit einigen Zusatzinformationen versehen ist. Die wichtigsten Dateiformate, die seit dem JDK 1.3 standardmäßig unterstützt werden, sind wav, aiff und au. " Eine weitere wichtige Abstraktion ist die der Line. Sie repräsentiert die verschiedenen, miteinander verbundenen Elemente der Audio-Pipeline, mit denen die Sound-Daten
1205
Kapitel 49
Sound
erzeugt, transportiert oder modifiziert werden. Die abstrakten Eigenschaften einer Line sind, dass sie entweder geöffnet oder geschlossen ist und dass sie eine Reihe von Control-Objekten haben kann, um etwa die Lautstärke, das Stereo-Panorama oder den Hall zu verändern. Zudem sendet eine Line bei wichtigen Statusänderungen Events an registrierte Listener. " Wichtige Subinterfaces von Line sind Port und Mixer. Ein Port ist ein Endpunkt der Audio-Pipeline, also etwa ein Mikrofon oder CD-Laufwerk auf der Eingabeseite oder ein Kopfhörer- oder Verstärkerausgang auf der Ausgabeseite. Ein Mixer ist ein AudioElement mit mehreren Ein-/Ausgabeleitungen. Er dient typischerweise dazu, verschiedene Eingangssignale getrennt aufzubereiten und zu einem Summensignal zusammenzumischen. Ein Mixer kann auch Veränderungen an den Eingangssignalen vornehmen, etwa einen Halleffekt hinzufügen oder mit Hilfe eines Equalizers den Klang verändern. " Mit DataLine kommt das dritte Subinterface von Line ins Spiel. Eine DataLine ist das Vaterinterface für alle Lines, die explizit mit Datenströmen umgehen. Eine SourceDataLine versorgt einen Mixer mit Eingabedaten und eine TargetDataLine empfängt Daten von ihm. Ein Clip ist eine spezielle Datenquelle, die vor der Weitergabe alle Daten in einem Stück liest und fortan im Hauptspeicher hält. Ein Clip kann dadurch wahlfrei auf die Daten zugreifen und beispielsweise nur einen Teil des Signals wiedergeben oder Ausschnitte beliebig oft wiederholen. Allerdings kommt er nur für relativ kleine Datenmengen in Betracht.
Zugriff auf Audio-Dateien Die Klasse AudioSystem stellt einige Methoden zum Zugriff auf Dateien mit gesampeltem Sound zur Verfügung: javax.sound. sampled. AudioSystem
public static AudioFileFormat getAudioFileFormat(File file) throws UnsupportedAudioFileException, IOException public static AudioFileFormat getAudioFileFormat(InputStream stream) throws UnsupportedAudioFileException, IOException public static AudioFileFormat getAudioFileFormat(URL url) throws UnsupportedAudioFileException, IOException public static AudioInputStream getAudioInputStream(File file) throws UnsupportedAudioFileException, IOException public static AudioInputStream getAudioInputStream(InputStream stream) throws UnsupportedAudioFileException, IOException public static AudioInputStream getAudioInputStream(URL url) throws UnsupportedAudioFileException, IOException
1206
Gesampelter Sound
Kapitel 49
public static boolean isConversionSupported( AudioFormat targetFormat, AudioFormat sourceFormat ) public static AudioInputStream getAudioInputStream( AudioFormat targetFormat, AudioInputStream sourceStream ) Mit getAudioFileFormat kann das AudioFileFormat einer Sound-Datei ermittelt werden. Die Eingabedatei kann entweder als File, InputStream oder URL übergeben werden. Mit getAudioInputStream kann ein AudioInputStream beschafft werden, mit dem der Inhalt der Sound-Datei gelesen werden kann. Auch hier darf der Parameter wahlweise vom Typ File, InputStream oder URL sein. Die Klasse AudioSystem unterstützt sogar Konvertierungen zwischen verschiedenen Formaten. Ob eine bestimmte Konvertierung verfügbar ist, kann mit isConversionSupported abgefragt werden. Wird getAudioInputStream mit einem Zielformat und einem anderen AudioInputStream als Parameter aufgerufen, führt die Methode die Konvertierung durch und liefert einen AudioInputStream, der das gewünschte Zielformat hat.
Audio-Geräte beschaffen Die Klasse AudioSystem stellt auch Methoden zum Zugriff auf Audio-Geräte zur Verfügung: public static Mixer.Info[] getMixerInfo() public static Mixer getMixer(Mixer.Info info)
javax.sound. sampled. AudioSystem
public static Line getLine(Line.Info info) throws LineUnavailableException
Teil VIII
Um herauszufinden, welche Mixer verfügbar sind, muss zunächst getMixerInfo aufgerufen werden. Der Rückgabewert ist ein Array mit Mixer.Info-Objekten, das je vorhandenem Mixer ein Element enthält. Durch Übergabe eines Mixer.Info-Objekts an getMixer kann der zugehörige Mixer beschafft werden. Man kann allerdings auch ohne explizite Verwendung eines Mixers gesampelten Sound ausgegeben, wenn mit getLine direkt ein geeignetes Audio-Gerät beschafft wird. Dazu muss ein Line.Info-Objekt übergeben werden, das dessen Eigenschaften beschreibt. Wir werden in Listing 49.1, Seite 1209 zeigen, wie das Line.Info-Objekt konstruiert werden muss, um einen Clip zu erzeugen.
1207
Kapitel 49
Sound
Die Steuerelemente eines Audio-Geräts Wir hatten eingangs erwähnt, dass eine Line eine Reihe von Steuerelementen zur Verfügung stellt, mit denen Parameter wie Lautstärke, Stereo-Panorama oder Hall eingestellt werden können. Auf diese kann mit folgenden Methoden zugegriffen werden: javax.sound. sampled.Line
public Control[] getControls() public boolean isControlSupported(Control.Type control) public Control getControl(Control.Type control) getControls liefert ein Array aller verfügbaren Steuerelemente, die durch Instanzen der Klasse Control repräsentiert werden. Mit isControlSupported kann durch Übergabe eines Control.Type-Objekts festgestellt werden, ob ein bestimmter Typ vorhanden ist. Mit getControl kann dieser schließlich beschafft werden. Control ist die Basisklasse einer ganzen Gruppe von Kontrollelementen, die sich durch den Datentyp des zu verändernden Parameters unterscheiden. Es gibt die Unterklassen BooleanControl, EnumControl, FloatControl und CompoundControl. Ein FloatControl beispielsweise dient zur Veränderung eines Fließkommawerts (wie etwa der Lautstärke), während ein BooleanControl einen An-/Aus-Wert verändern kann (beispielsweise die Stummschaltung eines Elements). Die wichtigsten Methoden der Klasse FloatControl sind:
javax.sound. sampled. FloatControl
public float getMaximum() public float getMinimum() public float getValue() public void setValue(float newValue) Mit getMinimum und getMaximum können der kleinste und größte einstellbare Wert abgefragt werden, mit getValue der aktuelle. Mit setValue kann der Wert des Controls verändert werden. Die Type-Klassen der einzelnen Steuerelemente besitzen jeweils eine Reihe von vordefinierten Konstanten, die an getControl übergeben werden können. Für FloatControl.TYPE sind das beispielsweise MASTER_GAIN zur Einstellung der Lautstärke oder PAN zur Veränderung des Stereo-Panoramas. Ein Steuerelement des Typs MASTER_GAIN bestimmt die Gesamtverstärkung, die das Audio-Element dem Ursprungssignal hinzufügt. Ihr Wert wird in Dezibel (dB) angegeben, wobei positive Werte eine Verstärkung und negative eine Abschwächung des Eingangssignals anzeigen. Das Dezibel ist eine logarithmische Maßeinheit, die den Verstärkungsfaktor durch die Formel 10dB/20 ausdrückt. 20 dB entsprechen also einer zehnfachen
1208
Gesampelter Sound
Kapitel 49
Verstärkung, -40 dB einer 100-fachen Abschwächung. 0 dB bedeutet, dass die Stärke des Ausgangs- im Verhältnis zum Eingangssignal unverändert bleibt. Ein Steuerelement des Typs PAN bestimmt die Lage des Audio-Signals im Stereo-Panorama. Es kann Werte von -1.0 (ganz links) bis +1.0 (ganz rechts) annehmen. Ein Wert von 0 legt das Signal in die Mitte.
Der Clip Ein Clip ist eine besondere Form einer DataLine, der alle Audiodaten komplett im Hauptspeicher hält. Wie jede Line muss er vor der Verwendung durch Aufruf von open geöffnet werden und nach Gebrauch mit close geschlossen werden. Zum Abspielen stellt er folgende Methoden zur Verfügung: public void start() public void stop()
javax.sound. sampled.Clip
public boolean isRunning() Ein Aufruf von start startet das Abspielen des Clips, mit stop wird es angehalten. Mit isRunning kann überprüft werden, ob die Wiedergabe noch läuft oder bereits beendet wurde. INFO
i
i
i
Die Methode start ist nicht modal, d.h., sie wartet beim Aufruf nicht, bis der Clip vollständig abgespielt ist. Sie initiiert lediglich den Abspielvorgang und kehrt dann sofort zum Aufrufer zurück. Falls dieser auf das Ende warten will, muss er entweder durch wiederholten Aufruf von isRunning den Status des Clips abfragen oder sich bei diesem als Listener registrieren und auf das STOP-Signal warten.
49.2.3 Abspielen einer Sample-Datei Nach den Erklärungen der vorangegangenen Abschnitte wollen wir nun das Beispielprogramm zum Abspielen einer Sample-Datei vorstellen: /* Listing4901.java */ import java.io.*; import javax.sound.sampled.*;
Listing 49.1: Abspielen einer Sample-Datei
public class Listing4901 { private static void playSampleFile(String name, float pan, float gain) throws Exception { //AudioInputStream öffnen
Teil VIII
001 002 003 004 005 006 007 008 009 010 011
1209
Kapitel 49 Listing 49.1: Abspielen einer Sample-Datei (Forts.)
1210
Sound 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060
AudioInputStream ais = AudioSystem.getAudioInputStream( new File(name) ); AudioFormat format = ais.getFormat(); //ALAW/ULAW samples in PCM konvertieren if ((format.getEncoding() == AudioFormat.Encoding.ULAW) || (format.getEncoding() == AudioFormat.Encoding.ALAW)) { AudioFormat tmp = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, format.getSampleRate(), format.getSampleSizeInBits() * 2, format.getChannels(), format.getFrameSize() * 2, format.getFrameRate(), true ); ais = AudioSystem.getAudioInputStream(tmp, ais); format = tmp; } //Clip erzeugen und öffnen DataLine.Info info = new DataLine.Info( Clip.class, format, ((int) ais.getFrameLength() * format.getFrameSize()) ); Clip clip = (Clip)AudioSystem.getLine(info); clip.open(ais); //PAN einstellen FloatControl panControl = (FloatControl)clip.getControl( FloatControl.Type.PAN ); panControl.setValue(pan); //MASTER_GAIN einstellen FloatControl gainControl = (FloatControl)clip.getControl( FloatControl.Type.MASTER_GAIN ); gainControl.setValue(gain); //Clip abspielen clip.start(); while (true) { try { Thread.sleep(100); } catch (Exception e) { //nothing } if (!clip.isRunning()) { break; }
Midi 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 }
} clip.stop(); clip.close(); }
Kapitel 49 Listing 49.1: Abspielen einer Sample-Datei (Forts.)
public static void main(String[] args) { try { playSampleFile( args[0], Float.parseFloat(args[1]), Float.parseFloat(args[2]) ); } catch (Exception e) { e.printStackTrace(); System.exit(1); } System.exit(0); }
Die Methode playSampleFile öffnet zunächst einen AudioInputStream und bestimmt dessen AudioFormat. Ist dessen Kodierung ULAW oder ALAW, konvertiert es den Stream in das PCM-Format, denn die anderen beiden Formate werden standardmäßig nicht unterstützt. Anschließend wird ein DataLine.Info-Objekt instanziert und in Zeile 038 ein Clip damit erzeugt. Dieser wird geöffnet und MASTER_GAIN und PAN werden auf die als Methodenparameter übergebenen Werte eingestellt. In Zeile 051 wird der Clip gestartet und anschließend gewartet, bis er vollständig abgespielt ist. Das Programm wird mit dem Namen der Datei und den Werten für PAN und MASTER_GAIN als Kommandozeilenparameter aufgerufen. Es kann zum Abspielen von wav-, aiff- und au-Dateien verwendet werden.
49.3 Midi 49.3.1 Was ist Midi?
Teil VIII
In den siebziger Jahren standen Musiker, die sich mit elektronischer Musik beschäftigten, vor einigen Herausforderungen. Zwar gab es gut klingende Synthesizer, die unzählige Sounds und Erweiterungsmöglichkeiten boten. Doch schwierig wurde es, wenn zwei von ihnen miteinander verbunden werden sollten. Es gab nämlich keinen einheitlichen Standard zur Übertragung der Daten zwischen den Systemen. Mit den ersten digitialen Synthesizern der 80er Jahre wurde dieses Problem durch die Schaffung des Midi-Standards behoben. Midi steht für Musical Instrument Digital Interface und bezeichnet einen Standard, der die Übertragung von Daten zwischen zwei oder mehr elektronischen
1211
Kapitel 49
Sound
Musikinstrumenten beschreibt. Neben der Standardisierung der Hardware (Kabel und Stecker) wurde dabei insbesondere festgelegt, welche Daten übertragen und wie sie kodiert werden sollten. Midi war ursprünglich eine serielle Schnittstelle, auf der die Daten byteweise übertragen werden. Drückte der Musiker auf seinem Keyboard die Taste C, wurde diese Information in eine drei Byte lange Nachricht verpackt (Status-/Kanalinformation, Tonhöhe, Lautstärke) und in Echtzeit an die angeschlossenen Synthesizer verschickt. Auch beim Loslassen der Taste wurde eine entsprechende Nachricht verschickt. Die angeschlossenen Synthesizer wurden also über die Midi-Schnittstelle ferngesteuert. Neben Notendaten können dabei auch Statusinformationen und Einstellungen von Reglern (Lautstärke, Effekte, Pitch-Bend etc.) übertragen werden. Auch die Übertragung proprietärer Daten ist vorgesehen, um die Kommunikation nichtstandardisierter, gerätespezifischer Informationen in kontrollierter Weise zu ermöglichen.
i
i
i
INFO Es ist wichtig zu verstehen, dass beim Midi-Protokoll nicht die Audio-Signale an sich übertragen werden, sondern lediglich die Ereignisse, die zur ihrer Entstehung führen. Midi-Daten können also in einem gewissen Sinne als die Partitur eines Stücks angesehen werden. Was dann tatsächlich erklingt, wird durch die dadurch angesteuerten Synthesizer und ihre klanglichen Eigenschaften bestimmt.
Zunächst war Midi ein reines »Wire«-Protokoll, das die Übertragung von Echtzeitdaten über eine elektrische Verbindung beschrieb. Später wollte man Midi-Datenströme auch aufzeichnen und in Dateien speichern können und man entwickelte dazu die Midi-Dateiprotokolle. Darin werden die eigentlichen Midi-Nachrichten mit Zeitstempeln versehen, um sie später mit Hilfe eines Sequenzers in ihrer exakten zeitlichen Abfolge wiedergeben zu können. Im Sound-API werden Midi-Daten ohne Zeitstempel als Midi-Nachrichten (Midi-Messages) und solche mit Zeitstempel als Midi-Ereignisse (Midi-Events) bezeichnet. Der Inhalt einer Midi-Datei wird üblicherweise als Sequenz bezeichnet. Eine Sequenz enthält eine Reihe von Spuren, die ihrerseits die Midi-Events enthalten. Meist repräsentieren die Spuren die unterschiedlichen Instrumente eines Stücks, so dass etwa in Spur eins das Piano liegt, in Spur zwei der Bass usw. Die verschiedenen Spuren werden innerhalb der Midi-Events durch Kanäle repräsentiert, von denen es maximal 16 pro Midi-Schnittstelle gibt.
49.3.2 Grundlegende Klassen des Midi-API Ähnlich wie im Sampling-API gibt es eine Reihe von Klassen und Interfaces, mit denen die zuvor beschriebenen Konzepte innerhalb des Midi-API umgesetzt werden. Sie befinden sich im zweiten großen Bestandteil des Java Sound-API, dem Paket javax.sound. midi. Wir wollen die wichtigsten von ihnen kurz vorstellen:
1212
Midi
Kapitel 49
" Analog zur Klasse AudioSystem gibt es im Paket javax.sound.midi eine Klasse MidiSystem, mit der Midi-Geräte und Midi-Dateien beschafft und Informationen über sie abgefragt werden können. " Ein Midi-Gerät wird durch die Klasse MidiDevice repräsentiert. Ein MidiDevice kann entweder geöffnet oder geschlossen sein und besitzt eine Reihe von Ein- und Ausgabeelementen. Die Eingabeelemente werden durch das Interface Receiver, die Ausgabeelemente durch das Interface Transmitter dargestellt. Ein MidiDevice kennt die Anzahl seiner Receiver und Transmitter und stellt diese auf Anfrage zur Verfügung. " Die wichtigsten Unterklassen von MidiDevice sind Synthesizer und Sequencer. Gegenüber seiner Vaterklasse besitzt ein Synthesizer zusätzlich Informationen über die verfügbaren Instrumente und ihre Zuordnung auf die verschiedenen Kanäle. Ein Sequencer besitzt dagegen Methoden, um eine Sequenz abzuspielen oder anzuhalten und Parameter wie Tempo, Synchronisierung oder Positionierung zu beeinflussen. " Eine Midi-Nachricht wird durch die Klasse MidiMessage und deren Unterklassen ShortMessage, MetaMessage und SysexMessage repräsentiert. Ein Midi-Ereignis wird durch die Klasse MidiEvent dargestellt, die zusätzlich zur MidiMessage einen Zeitstempel enthält. Eine Sequenz wird durch die Klasse Sequence repräsentiert. Sie enthält neben einigen globalen Informationen zum Timing der Daten eine Reihe von Tracks, die ihrerseits die MidiEvents enthalten. Weitere Details zu den genannten Klassen werden in den folgenden Abschnitten vorgestellt.
49.3.3 Alle meine Entchen – erster Versuch In diesem Abschnitt wollen wir uns die Aufgabe stellen, das allseits bekannte »Alle meine Entchen« mit Hilfe des Midi-API wiederzugeben. Zuerst wollen wir einen sehr einfachen Ansatz wählen, bei dem die Midi-Nachrichten in Echtzeit an einen Synthesizer geschickt werden, wobei das Timing mit Hilfe von Thread.sleep-Aufrufen manuell gesteuert wird. Zunächst wird also ein Synthesizer benötigt, den wir von der Klasse MidiSystem beziehen können: public static Synthesizer getSynthesizer() throws MidiUnavailableException
javax.sound.midi. MidiSystem
Teil VIII
getSynthesizer liefert den Default-Synthesizer der installierten Sound-Hardware, typischerweise den auf der Soundkarte eingebauten. Ist mehr als ein Synthesizer vorhanden, muss die Liste aller verfügbaren Synthesizer durch Aufruf von getMidiDeviceInfo durchsucht und mit getMidiDevice der gewünschte ausgewählt werden. Wir wollen zunächst davon ausgehen, dass ein Default-Synthesizer vorhanden ist, der unseren Ansprüchen genügt.
1213
Kapitel 49
Sound
Nachdem der Synthesizer verfügbar ist, muss er geöffnet und zur Übergabe von MidiNachrichten ein Receiver beschafft werden: javax.sound.midi. Synthesizer
public void open() throws MidiUnavailableException public void close() public boolean isOpen() public int getMaxReceivers() public Receiver getReceiver() throws MidiUnavailableException Das Öffnen und Schließen eines Midi-Geräts wird mit open und close erledigt und mit isOpen kann sein aktueller Status herausgefunden werden. Ein Receiver kann durch Aufruf von getReceiver beschafft werden, die Gesamtzahl aller vorhandenen Receiver kann mit getMaxReceivers abgefragt werden. Um an ein Midi-Gerät Daten zu senden, werden diese einfach an einen seiner Receiver geschickt. Dazu besitzt dieser eine Methode send, an die beim Aufruf die gewünschte MidiMessage übergeben wird:
javax.sound.midi. Receiver
javax.sound.midi. MidiDevice
public void send(MidiMessage message, long timeStamp) Das zweite Argument timeStamp ist zur Feinsynchronisierung der Midi-Nachrichten vorgesehen. Damit soll ein Synthesizer in der Lage sein, leichte Timing-Schwankungen beim Anliefern der Daten auszugleichen. Ob ein Gerät dieses Feature unterstützt, kann durch Aufruf von getMicrosecondPosition bestimmt werden. Ist dessen Rückgabewert -1, werden derartige Timestamps nicht unterstützt: public long getMicrosecondPosition() Aber auch, wenn diese Timestamps unterstützt werden, sollte man keine Wunder von ihnen erwarten. Die Spezifikation weist ausdrücklich darauf hin, dass damit nur kleinere Timing-Schwankungen ausgeglichen werden können. Liegt ein Zeitstempel dagegen weit in der Zukunft (oder gar in der Vergangenheit), ist das Midi-Gerät nicht verpflichtet, diesen korrekt zu behandeln. Wird -1 an das timeStamp-Argument von send übergeben, ignoriert das entsprechende Gerät den Zeitstempel und bearbeitet die Midi-Nachricht, so schnell es kann. Um eine MidiMessage zu konstruieren, wird diese zunächst mit new erzeugt und durch Aufruf von setMessage mit Daten gefüllt. Da wir weder Meta- noch Sysex-Daten benötigen, wollen wir uns lediglich die Konstruktion einer ShortMessage mit Hilfe der folgenden setMessage-Methode ansehen:
1214
Midi
public void setMessage( int command, int channel, int data1, int data2 ) throws InvalidMidiDataException
Kapitel 49
javax.sound.midi. ShortMessage
Als erstes Argument muss das gewünschte Midi-Kommando übergeben werden. Die für uns relevanten Kommandos sind NOTE_ON (Taste wird gedrückt), NOTE_OFF (Taste wird losgelassen) und PROGRAM_CHANGE (Instrumentenwechsel). Sie werden als Konstanten in der Klasse ShortMessage definiert. Als zweites Argument wird der Kanal angegeben, auf den sich das Kommando auswirken soll. Anschließend folgen zwei Datenbytes, die kommandospezifisch sind. Das NOTE_ON-Kommando erwartet darin beispielsweise die Tonhöhe (die verfügbaren Noten sind als Ganzzahlen durchnummeriert) und die relative Lautstärke (Anschlagsdynamik) der Note. NOTE_OFF erwartet die Tonhöhe als erstes Datenbyte und ignoriert das zweite. PROGRAM_CHANGE erwartet die gewünschte Programmnummer und ignoriert das zweite Datenbyte. Nach diesen Vorbemerkungen wollen wir uns nun ein Beispielprogramm ansehen: /* Listing4902.java */ import javax.sound.midi.*;
Listing 49.2: Alle meine Entchen – erster Versuch
public class Listing4902 { private static void playAlleMeineEntchen() throws Exception { //Partitur {{Tonhoehe, DauerInViertelNoten, AnzahlWdh},...} final int DATA[][] = { {60, 1, 1}, //C {62, 1, 1}, //D {64, 1, 1}, //E {65, 1, 1}, //F {67, 2, 2}, //G,G {69, 1, 4}, //A,A,A,A {67, 4, 1}, //G {69, 1, 4}, //A,A,A,A {67, 4, 1}, //G {65, 1, 4}, //F,F,F,F {64, 2, 2}, //E,E {62, 1, 4}, //D,D,D,D {60, 4, 1} //C }; //Synthesizer öffnen und Receiver holen
Teil VIII
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026
1215
Kapitel 49 Listing 49.2: Alle meine Entchen – erster Versuch (Forts.)
Sound 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 }
Synthesizer synth = MidiSystem.getSynthesizer(); synth.open(); Receiver rcvr = synth.getReceiver(); //Melodie spielen ShortMessage msg = new ShortMessage(); for (int i = 0; i < DATA.length; ++i) { for (int j = 0; j < DATA[i][2]; ++j) { //Anzahl Wdh. je Note //Note an msg.setMessage(ShortMessage.NOTE_ON, 0, DATA[i][0], 64); rcvr.send(msg, -1); //Pause try { Thread.sleep(DATA[i][1] * 400); } catch (Exception e) { //nothing } //Note aus msg.setMessage(ShortMessage.NOTE_OFF, 0, DATA[i][0], 0); rcvr.send(msg, -1); } } //Synthesizer schließen synth.close(); } public static void main(String[] args) { try { playAlleMeineEntchen(); } catch (Exception e) { e.printStackTrace(); System.exit(1); } System.exit(0); }
Ab Zeile 027 wird ein Synthesizer aktiviert und zur Übergabe von Midi-Nachrichten auf einen seiner Receiver zugegriffen. Anschließend wird die Melodie durch wiederholten Aufruf seiner send-Methode abgespielt. Die Melodie ist in dem Array DATA in Zeile 011 versteckt. Für jede einzelne Note wird dort die Tonhöhe, die Tondauer in Viertelnoten und die Anzahl der Wiederholungen angegeben. Die jeweilige Noteninformation wird mit setMessage an die vorinstanzierte ShortMessage übergeben und als NOTE_ON-Nachricht an den Synthesizer geschickt. In Zeile 039 pausiert das Programm, um die Note entsprechend ihrer Länge erklingen zu lassen (in unserem Beispiel 400 ms je Viertelnote). Anschließend wird die NOTE_OFF-Nachricht geschickt, um den Ton zu beenden.
1216
Midi
ACHTUNG
Kapitel 49
!
!
!
Wenn wir das Programm mit dem Java-Interpreter starten und eine passende Sound-Hardware vorhanden ist, hören wir tatsächlich »Alle meine Entchen«. Bei unveränderter Standardinstrumentierung sollte die Melodie auf einem Klavier gespielt werden. Wenn wir genau hinhören, stellen wir allerdings ein Problem fest: Das Timing des Stücks ist nicht präzise, sondern schwankt während der Darbietung. Manche Noten werden etwas zu kurz, andere dagegen zu lang gespielt. Das liegt daran, dass die mit Thread.sleep erzeugten Notenlängen bei weitem nicht präzise genug sind. Die beim Aufruf erzeugten Schwankungen sind für das menschliche Ohr sehr gut hörbar und führen dazu, dass das Musikstück ungenießbar wird. Obwohl das Verfahren prinzipiell funktioniert, benötigen wir also ein präziseres Wiedergabewerkzeug. Und das ist das Thema des nächsten Abschnitts.
49.3.4 Alle meine Entchen mit dem Sequencer Neben dem Synthesizer ist der Sequencer das zweite wichtige MidiDevice. Er dient dazu, Midi-Sequenzen entsprechend der darin enthaltenen Timing-Information präzise wiederzugeben. Das hört sich zunächst einmal einfach an, ist es aber nicht. Einerseits benötigt ein Sequencer einen Zeitgeber, der genauer ist als die üblicherweise vom Betriebssystem zur Verfügung gestellten Timer. Zweitens muss dieser möglichst immun gegen Schwankungen der CPU-Last sein, d.h., der Sequencer sollte auch dann noch stabil arbeiten, wenn im Hintergrund CPU-intensive Operationen ablaufen. Drittens muss ein Sequencer nicht nur, wie in unserem Beispiel, eine einzige Spur mit verhältnismäßig langsamen Viertelnoten abspielen, sondern möglicherweise ein Dutzend von ihnen, mit Achteln, Sechzehnteln oder noch kürzeren Noten, wie sie beispielsweise bei Schlagzeugspuren auftreten. Zudem enthalten die Spuren oft immense Mengen an Controller-Daten (z.B. Pitch-Bend), die ebenfalls präzise wiedergegeben werden müssen. Zu guter Letzt besitzt ein Sequencer Zusatzfunktionen, wie das Ändern der Abspielgeschwindigkeit, das Ausblenden einzelner Spuren oder das Synchronisieren mit externen Taktgebern, und er besitzt in aller Regel einen Aufnahmemodus, mit dem Midi-Daten in Echtzeit aufgenommen werden können. Wir sehen also, dass die Implementierung eines guten Sequenzers gar nicht so einfach ist. Glücklicherweise stellt das MidiSystem einen eigenen Sequencer zur Verfügung, dessen Standardimplementierung durch einen Aufruf von getSequencer beschafft werden kann: javax.sound.midi. MidiSystem
Beim Abspielen einer Melodie mit dem Sequencer werden die Midi-Nachrichten nicht mehr direkt an den Synthesizer geschickt, sondern zunächst in eine Sequence verpackt. Diese kann wie folgt konstruiert werden: public Sequence(float divisionType, int resolution) throws InvalidMidiDataException
javax.sound.midi. Sequence
1217
Teil VIII
public static Sequencer getSequencer() throws MidiUnavailableException
Kapitel 49
Sound
Das erste Argument gibt die Art und Weise an, wie das Timing erzeugt werden soll. Hier gibt es im Wesentlichen die Möglichkeiten, einen internen Timer zu verwenden, der auf Bruchteilen von Viertelnoten basiert, oder mit externer Synchronisation zu arbeiten, bei der die Timing-Informationen im SMPTE-Format zur Verfügung gestellt werden. Wir wollen die erste Variante wählen und übergeben dazu die Konstante Sequence.PPQ als erstes Argument. Das zweite Argument resoultion bezieht sich auf das erste und gibt die Auflösung des Timers an. In unserem Fall würde es also die Anzahl der Zeitimpulse je Viertelnote angeben. Um eine Sequence mit Daten zu füllen, wird mindestens ein Track benötigt, der durch Aufruf von createTrack erzeugt werden kann: javax.sound.midi. Sequence
javax.sound.midi. Track
javax.sound.midi. Sequencer
public Track createTrack() Ein Track-Objekt ist zunächst leer und wird durch wiederholte Aufrufe von add mit Midi-Ereignissen versehen: public boolean add(MidiEvent event) Nachdem die Sequence fertiggestellt ist, kann sie durch Aufruf von setSequence an den Sequencer übergeben werden: public void setSequence(Sequence sequence) throws InvalidMidiDataException public void setTempoInBPM(float bpm) public void start() public void stop() public boolean isRunning() Mit setTempoInBPM kann das Abspieltempo in »Beats Per Minute« (kurz BPM), also in Viertelnoten pro Minute, angegeben werden. start startet das Abspielen der Sequenz, stop beendet es. Mit isRunning kann geprüft werden, ob der Sequencer gerade läuft. Bevor eine Sequence abgespielt werden kann, müssen Synthesizer und Sequencer miteinander verbunden werden. Dies geschieht, indem ein Transmitter des Sequenzers an einen Receiver des Synthesizers angeschlossen wird. Der Transmitter verfügt dazu über eine Methode setReceiver, an die der empfangende Receiver übergeben wird:
javax.sound.midi. Transmitter
1218
public void setReceiver(Receiver receiver) Nach diesen Vorbemerkungen können wir uns nun ansehen, wie »Alle meine Entchen« mit Hilfe eines Sequenzers wiedergegeben werden kann.
Midi
/* Listing4903.java */ import javax.sound.midi.*;
Listing 49.3: Alle meine Entchen mit dem Sequenzer
public class Listing4903 { private static void playAlleMeineEntchen() throws Exception { //Partitur {{Tonhoehe, DauerInViertelNoten, AnzahlWdh},...} final int DATA[][] = { {60, 1, 1}, //C {62, 1, 1}, //D {64, 1, 1}, //E {65, 1, 1}, //F {67, 2, 2}, //G,G {69, 1, 4}, //A,A,A,A {67, 4, 1}, //G {69, 1, 4}, //A,A,A,A {67, 4, 1}, //G {65, 1, 4}, //F,F,F,F {64, 2, 2}, //E,E {62, 1, 4}, //D,D,D,D {60, 4, 1} //C }; //Sequence bauen final int PPQS = 16; final int STAKKATO = 4; Sequence seq = new Sequence(Sequence.PPQ, PPQS); Track track = seq.createTrack(); long currentTick = 0; ShortMessage msg; //Kanal 0 auf "EnsembleStrings" umschalten msg = new ShortMessage(); msg.setMessage(ShortMessage.PROGRAM_CHANGE, 0, 48, 0); track.add(new MidiEvent(msg, currentTick)); //Partiturdaten hinzufügen for (int i = 0; i < DATA.length; ++i) { for (int j = 0; j < DATA[i][2]; ++j) { //Anzahl Wdh. je Note msg = new ShortMessage(); msg.setMessage(ShortMessage.NOTE_ON, 0, DATA[i][0], 64); track.add(new MidiEvent(msg, currentTick)); currentTick += PPQS * DATA[i][1] - STAKKATO; msg = new ShortMessage(); msg.setMessage(ShortMessage.NOTE_OFF, 0, DATA[i][0], 0); track.add(new MidiEvent(msg, currentTick)); currentTick += STAKKATO; } }
Teil VIII
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049
Kapitel 49
1219
Kapitel 49 Listing 49.3: Alle meine Entchen mit dem Sequenzer (Forts.)
Sound 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 }
//Sequencer und Synthesizer initialisieren Sequencer sequencer = MidiSystem.getSequencer(); Transmitter trans = sequencer.getTransmitter(); Synthesizer synth = MidiSystem.getSynthesizer(); Receiver rcvr = synth.getReceiver(); //Beide öffnen und verbinden sequencer.open(); synth.open(); trans.setReceiver(rcvr); //Sequence abspielen sequencer.setSequence(seq); sequencer.setTempoInBPM(145); sequencer.start(); while (true) { try { Thread.sleep(100); } catch (Exception e) { //nothing } if (!sequencer.isRunning()) { break; } } //Sequencer anhalten und Geräte schließen sequencer.stop(); sequencer.close(); synth.close(); } public static void main(String[] args) { try { playAlleMeineEntchen(); } catch (Exception e) { e.printStackTrace(); System.exit(1); } System.exit(0); }
Die Partiturdaten stimmen mit denen des vorigen Beispiels überein, werden allerdings anders verwendet. Zunächst wird ab Zeile 027 eine Sequence mit einem einzelnen Track angelegt, deren Auflösung 16 Ticks per Viertelnote beträgt. Ab Zeile 038 werden die Daten in ShortMessage-Objekte übertragen und dem Track hinzugefügt. Anders als im vorigen Beispiel lassen wir die Note allerdings nicht während der kompletten Notenlänge an, sondern beenden sie STAKKATO Ticks früher als vorgesehen. Diese Zeit zählen wir in Zeile 047 zu der anschließenden Pause hinzu. Durch diese Maßnahme gehen die Noten
1220
Midi
Kapitel 49
nicht direkt ineinander über, sondern werden mit einem hörbaren Abstand gespielt. In Zeile 034 wird eine ShortMessage zur Programmumschaltung generiert, um Kanal 0 auf Instrument 48 umzuschalten. Dadurch erklingt die Sequenz nicht mit einem Piano-, sondern mit einem Streichersound. Ab Zeile 051 werden Sequencer und Synthesizer initialisiert und miteinander verbunden. Anschließend wird die Sequence an den Sequenzer übergeben, die Abspielgeschwindigkeit eingestellt und das Stück gespielt. In der nachfolgenden Schleife wartet das Programm durch wiederholten Aufruf von isRunning, bis die Sequenz vollständig abgespielt ist. Anschließend stoppt es den Sequenzer und schließt alle Geräte.
49.3.5 Zugriff auf Midi-Dateien Lesen und Abspielen einer Midi-Datei Als letztes Beispiel zum Thema Midi wollen wir uns ansehen, wie eine Midi-Datei gelesen und abgespielt werden kann. Dazu benötigen wir nur noch eine zusätzliche Methode, nämlich getSequence aus der Klasse MidiSystem: public static Sequence getSequence(File file) throws InvalidMidiDataException, IOException
javax.sound.midi. MidiSystem
getSequence erwartet ein File-Objekt als Argument, mit dem die abzuspielende MidiDatei angegeben wird. Es liefert als Rückgabewert eine Sequence, die an den Sequenzer übergeben und von diesem abgespielt werden kann. Ein Beispielprogramm zur Widergabe einer Midi-Datei ist nun sehr einfach zu konstruieren. Mit Ausnahme der expliziten Konstruktion der Sequence entspricht es vollkommen dem Beispielprogramm des vorigen Abschnitts: /* Listing4904.java */ import java.io.*; import javax.sound.midi.*;
Listing 49.4: Abspielen einer Midi-Datei
public class Listing4904 { private static void playMidiFile(String name) throws Exception { //Sequencer und Synthesizer initialisieren Sequencer sequencer = MidiSystem.getSequencer(); Transmitter trans = sequencer.getTransmitter(); Synthesizer synth = MidiSystem.getSynthesizer(); Receiver rcvr = synth.getReceiver(); //Beide öffnen und verbinden sequencer.open(); synth.open(); trans.setReceiver(rcvr);
Teil VIII
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019
1221
Kapitel 49 Listing 49.4: Abspielen einer Midi-Datei (Forts.)
Sound 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 }
//Sequence lesen und abspielen Sequence seq = MidiSystem.getSequence(new File(name)); sequencer.setSequence(seq); sequencer.setTempoInBPM(145); sequencer.start(); while (true) { try { Thread.sleep(100); } catch (Exception e) { //nothing } if (!sequencer.isRunning()) { break; } } //Sequencer anhalten und Geräte schließen sequencer.stop(); sequencer.close(); synth.close(); } public static void main(String[] args) { try { playMidiFile(args[0]); } catch (Exception e) { e.printStackTrace(); System.exit(1); } System.exit(0); }
Das Programm erwartet in der Kommandozeile den Namen der abzuspielenden MidiDatei. Wird es mit der (ebenfalls im Verzeichnis der Beispieldateien befindlichen) Datei ame.mid als Argument aufgerufen, ertönt das altbekannte »Alle meine Entchen«.
Abspeichern einer Sequenz in einer Midi-Datei Das Abspeichern einer Sequence in einer Midi-Datei ist fast so einfach wie ihr Abspielen. Die Klasse MidiSystem besitzt dazu eine Methode write, die drei Parameter erwartet: javax.sound.midi. MidiSystem
public static int write(Sequence in, int type, File out) throws IOException Als erstes Argument wird die zu speichernde Sequence übergeben, als zweites Argument der Midi-Dateityp und als Letztes ein File-Objekt mit dem Namen der Ausgabedatei. Für einfache Experimente kann als Midi-Dateityp einfach 0 übergeben werden. Ein Array mit allen unterstützten Dateitypen lässt sich durch Aufruf von getMidiFileTypes beschaffen.
1222
Zusammenfassung
Kapitel 49
Zusammenfassung In diesem Kapitel wurden folgende Themen behandelt: " Grundlagen des Samplings " Die Klasse AudioSystem zur Beschaffung von Audio-Ressourcen " Sampling-Formate und die Klassen AudioFormat und AudioFileFormat " Zugriff auf Audio-Dateien mit der Klasse AudioInputStream " Die Audio-Geräte Mixer, Line und Clip " Die Steuerelemente eines Audio-Geräts sowie die Klasse Control und ihre Unterklassen " Verwendung der Klasse Clip zum Abspielen einer Sound-Datei " Grundlagen von Midi " Die Klasse MidiSystem zur Beschaffung von Midi-Ressourcen " Die Midi-Geräte-Klassen MidiDevice, Synthesizer und Sequencer sowie ihre Verbindungselemente Transmitter und Receiver " Direkter Zugriff auf einen Synthesizer zum Abspielen einer Folge von Midi-Nachrichten " Erstellen von Sequenzen und Abspielen derselben mit dem Sequenzer " Lesen und Abspielen von Midi-Dateien
Teil VIII
" Abspeichern einer Sequence in eine Midi-Datei
1223
Teil IX Verschiedenes 50 Performance-Tuning................................. 1227
Teil IX
51 Hilfsprogramme des JDK .......................... 1251
50
Performance-Tuning
50.1
Einleitung
Java gilt gemeinhin als Sprache, die mit PerformanceProblemen zu kämpfen hat. Nicht nur die Ablaufgeschwindigkeit des Compilers und anderer Entwicklungswerkzeuge, sondern vor allem die der eigenen Programme lässt oft zu wünschen übrig. Aufgrund der Plattformunabhängigkeit des vom Compiler generierten Bytecodes kann dieser normalerweise nicht direkt auf dem jeweiligen Betriebssystem ausgeführt werden. Er verwendet stattdessen einen eigenen Interpreter, die Virtuelle Maschine (kurz: VM), zur Ausführung der erstellten Programme. Interpretierter Code wird naturgemäß langsamer ausgeführt als kompilierter, selbst wenn er in Form von Bytecodes vorliegt. Zwar ist es prinzipiell möglich, auch Java-Programme in Native-Code zu übersetzen (es gibt sogar einige kommerzielle Tools, die das tun), aber dann ist es mit der Plattformunabhängigkeit vorbei und das fertige Programm läuft nur noch auf einem Betriebssystem. Während das für Applikationen in bestimmten Fällen akzeptabel sein mag, verbietet sich diese Vorgehensweise für Applets, die im Internet auf vielen verschiedenen Browsern und Betriebssystemen laufen müssen, von selbst. Zudem konterkarieren native-kompilierte Programme die Grundidee der plattformübergreifenden Binärkompatibilität, die eine der herausragenden Eigenschaften von Java ist.
Teil IX
Eine Alternativlösung bieten Just-In-Time-Compiler (kurz: JIT), deren Entwicklung in großen Schritten voranschreitet. Ein JIT ist ein Programm, das den Bytecode von Methoden während der Ausführung des Programms in Maschinencode der aktuellen Plattform über-
Kapitel 50
Performance-Tuning
setzt und so beim nächsten Aufruf wesentlich schneller ausführen kann. Vorteilhaft ist dabei, dass die Klassendateien mit dem Bytecode unverändert ausgeliefert werden können und das Programm seinen plattformübergreifenden Charakter erhält. Nur der Just-In-Time-Compiler ist plattformspezifisch und an ein bestimmtes Betriebssystem gebunden. Nahezu alle Hersteller kommerzieller Java-Produkte haben mittlerweile einen JIT als festen Bestandteil ihres Java-Entwicklungssystems eingebunden. Auch SUN liefert seit dem JDK 1.1.6 den Just-In-Time-Compiler von Symantec als festen Bestandteil des JDK aus. Leider ist auch ein Just-In-Time-Compiler kein Allheilmittel gegen Performanceprobleme. Zwar ist er in der Lage, bestimmte Codeteile so stark zu beschleunigen, dass ihre Ablaufgeschwindigkeit der von kompiliertem C-Code nahekommt. Andererseits gibt es nach wie vor genügend Möglichkeiten, Programme zu schreiben, die inhärent langsamen Code enthalten, der auch von einem Just-In-Time-Compiler nicht entscheidend verbessert werden kann. Zudem ensteht durch den Einsatz des JIT ein gewisser Overhead, der möglicherweise einen Netto-Performancegewinn verhindert, denn das Kompilieren des Bytecodes kostet Zeit und zusätzlichen Speicher. Des Weiteren ist zu bedenken, dass zur Laufzeit eine Vielzahl von Checks durchgeführt werden müssen, die die Ablaufgeschwindigkeit von Java-Programmen vermindert: " Array- und String-Zugriffe werden auf Bereichsüberschreitungen geprüft. " Zeiger werden vor der Dereferenzierung gegen null gecheckt. " Zuweisungen von Objektinstanzen werden auf korrekte Typisierung geprüft. " Es gibt Checks zu vielen arithmetischen Operationen (Überläufe, Teilen durch Null usw.). Am besten ist es daher, bereits während der Entwicklung der Programme auf die Ablaufgeschwindigkeit des erzeugten Codes zu achten. Wir wollen uns in diesem Kapitel einige typische Java-Konstrukte ansehen, die bei unachtsamer Verwendung zu PerformanceProblemen führen können. Gleichzeitig wollen wir Möglichkeiten aufzeigen, wie man mit alternativem Code den Engpass umgehen und die Ablaufgeschwindigkeit des Programms verbessern kann. Wenn man diese Regeln beachtet, ist es durchaus möglich, in Java größere Programme zu schreiben, deren Laufzeitverhalten auf aktuellen Rechnern absolut akzeptabel ist.
i
i
i
INFO Wir wollen uns in diesem Kapitel nicht mit grundlegenden Techniken der Codeoptimierung beschäftigen. Auch wenn sie zeitweilig kurz angedeutet werden, können diese Themen besser in Büchern über Programmiersprachen, Algorithmen oder Optimierungstechniken für Compiler nachgelesen werden. Auch Tipps & Tricks, die in aller Regel nur marginale
1228
Tuning-Tipps
Kapitel 50
Verbesserungen bringen, oder langsamer Code, für den keine einfach anzuwendenden Alternativen bekannt sind, sollen hier nicht behandelt werden. Stattdessen wollen wir uns auf einige große Themenkomplexe konzentrieren, die leicht umzusetzen sind und in der Praxis schnell zu Verbesserungen führen.
50.2 Tuning-Tipps 50.2.1 String und StringBuilder String-Verkettung In Java gibt es zwei unterschiedliche Klassen String und StringBuilder zur Verarbeitung von Zeichenketten, deren prinzipielle Eigenschaften in Kapitel 11, Seite 265 erläutert wurden. Java-Anfänger verwenden meist hauptsächlich die Klasse String, denn sie stellt die meisten Methoden zur Zeichenkettenextraktion und -verarbeitung zur Verfügung und bietet mit dem +-Operator eine bequeme Möglichkeit, Zeichenketten miteinander zu verketten. Dass diese Bequemlichkeit ihren Preis hat, zeigt folgender Programmausschnitt: 001 002 003 004 005
String s; s = ""; for (int i = 0; i < 20000; ++i) { s += "x"; }
Listing 50.1: Langsame StringVerkettung
Das Programmfragment hat die Aufgabe, einen String zu erstellen, der aus 20000 aneinandergereihten »x« besteht. Das ist zwar nicht sehr praxisnah, illustriert aber die häufig vorkommende Verwendung des +=-Operators auf Strings. Der obige Code ist sehr ineffizient, denn er läuft langsam und belastet das Laufzeitsystem durch 60000 temporäre Objekte, die alloziert und vom Garbage Collector wieder freigegeben werden müssen. Der Compiler übersetzt das Programmfragment etwa so: 001 002 003 004 005
String s; s = ""; for (int i = 0; i < 20000; ++i) { s = new StringBuilder(s).append("x").toString(); }
Listing 50.2: Wie der Java-Compiler String-Verkettungen übersetzt
1229
Teil IX
Dieser Code ist in mehrfacher Hinsicht unglücklich. Pro Schleifendurchlauf wird ein temporäres StringBuilder-Objekt alloziert und mit dem zuvor erzeugten String initialisiert. Der Konstruktor von StringBuilder erzeugt ein internes Array (also eine weitere Objektinstanz), um die Zeichenkette zu speichern. Immerhin ist dieses Array 16 Byte größer als eigentlich erforderlich, so dass der nachfolgende Aufruf von append das Array nicht neu allozieren und die Zeichen umkopieren muss. Schließlich wird durch den Aufruf von toString ein neues String-Objekt erzeugt und s zugewiesen. Auf diese Weise
Kapitel 50
Performance-Tuning
werden pro Schleifendurchlauf drei temporäre Objekte erzeugt und der Code ist durch das wiederholte Kopieren der Zeichen im Konstruktor von StringBuilder sehr ineffizient. Eine eminente Verbesserung ergibt sich, wenn die Klasse StringBuilder und ihre Methode append direkt verwendet werden: Listing 50.3: Performante String-Verkettungen mit StringBuilder.append
001 002 003 004 005 006
String s; StringBuilder sb = new StringBuilder(1000); for (int i = 0; i < 20000; ++i) { sb.append("x"); } s = sb.toString();
Hier wird zunächst ein StringBuilder erzeugt und mit einem 1000 Zeichen großen Puffer versehen. Da die StringBuilder-Klasse sich die Länge der gespeicherten Zeichenkette merkt, kann der Aufruf append("x") meist in konstanter Laufzeit erfolgen. Dabei ist ein Umkopieren nur dann erforderlich, wenn der interne Puffer nicht mehr genügend Platz bietet, um die an append übergebenen Daten zu übernehmen. In diesem Fall wird ein größeres Array alloziert und der Inhalt des bisherigen Puffers umkopiert. In der Summe ist die letzte Version etwa um den Faktor 10 schneller als die ersten beiden und erzeugt 60000 temporäre Objekte weniger. Interessant ist dabei der Umfang der Puffervergrößerung, den das StringBuilder-Objekt vornimmt, denn er bestimmt, wann bei fortgesetztem Aufruf von append das nächste Mal umkopiert werden muss. Anders als beispielsweise bei der Klasse Vector, die einen veränderbaren Ladefaktor besitzt, verdoppelt sich die Größe eines StringBuilder-Objekts bei jeder Kapazitätserweiterung. Dadurch wird zwar möglicherweise mehr Speicher als nötig alloziert, aber die Anzahl der Kopiervorgänge wächst höchstens logarithmisch mit der Gesamtmenge der eingefügten Daten. In unserem Beispiel kann der interne Puffer zunächst 1000 Zeichen aufnehmen, wird beim nächsten Überlauf auf etwa 2000 Zeichen vergrößert, dann auf 4000, 8000, 16000 und schließlich auf 32000 Zeichen. Hätten wir die initiale Größe auf 20000 Zeichen gesetzt, wäre sogar überhaupt kein Kopiervorgang erforderlich geworden und das Programm hätte 12000 Zeichen weniger alloziert.
!
!
!
ACHTUNG Bei der Verwendung der Operatoren + und += auf String-Objekten sollte man zusätzlich bedenken, dass deren Laufzeit nicht konstant ist (bzw. ausschließlich von der Länge des anzuhängenden Strings abhängt). Tatsächlich hängt sie auch stark von der Länge des Strings ab, an den angehängt werden soll, denn die Laufzeit eines Kopiervorgangs wächst nun einmal proportional zur Länge des zu kopierenden Objekts. Damit wächst das Laufzeitverhalten der Schleife in Listing 50.1, Seite 1229 nicht linear, sondern annähernd quadratisch. Es verschlechtert sich also mit zunehmender Länge der Schleife überproportional.
1230
Tuning-Tipps
Kapitel 50
Einfügen und Löschen in Strings Ein immer noch deutlicher, wenn auch nicht ganz so drastischer Vorteil bei der Verwendung von StringBuilder ergibt sich beim Einfügen von Zeichen am vorderen Ende des Strings: 001 002 003 004 005
String s; s = ""; for (int i = 0; i < 10000; ++i) { s = "x" + s; }
Listing 50.4: Langsames Einfügen in einen String
In diesem Beispiel wird wiederholt ein Zeichen vorne in den String eingefügt. Der Compiler wandelt das Programm auch hier in wiederholte Aufrufe von StringBuilderMethoden um, wobei unnötig viele Zwischenobjekte entstehen und unnötig oft kopiert werden muss. Eine bessere Lösung kann man auch hier durch direkte Verwendung eines StringBuilder-Objekts erzielen: 001 002 003 004 005 006
String s; StringBuilder sb = new StringBuilder(1000); for (int i = 0; i < 10000; ++i) { sb.insert(0, "x"); } s = sb.toString();
Listing 50.5: Schnelles Einfügen in einen String
Im Test war die Laufzeit dieser Variante etwa um den Faktor vier besser als die der ersten Version. Außerdem wird nicht ein einziges temporäres Objekt erzeugt, so dass zusätzlich das Memory-Subsystem und der Garbage Collector entlastet werden. JDK 1.1–6.0 Seit dem JDK 1.2 gibt es in der Klasse StringBuilder (beziehungsweise in der Klasse StringBuffer) eine Methode delete, mit der ein Teil der Zeichenkette gelöscht werden kann. Dadurch können beispielsweise Programmteile der folgenden Art beschleunigt werden:
» » »
String sub1 = s.substring(0, 1000) + s.substring(2000); Anstatt hier die ersten 1000 Zeichen mit allen Zeichen ab Position 2000 zu verbinden, kann unter Verwendung eines StringBuilder auch direkt das gewünschte Stück gelöscht werden:
String sub2 = sb.delete(1000, 2000).toString();
Die Methode toString der Klasse StringBuilder
1231
Teil IX
Den vorangegangenen Abschnitten lässt sich entnehmen, dass die Verwendung der Klasse StringBuilder meist dann sinnvoll ist, wenn die Zeichenkette zunächst aus vielen kleinen Teilen aufgebaut werden soll oder wenn sie sich häufig ändert. Ist der String dagegen fertig konstruiert oder muss auf einen vorhandenen String lesend zugegriffen werden, geht dies im Allgemeinen mit den vielseitigeren Methoden der Klasse String
Kapitel 50
Performance-Tuning
besser. Um einen StringBuilder in einen String zu konvertieren, wird die Methode toString aufgerufen, die durch einen kleinen Trick sehr effizient arbeitet. Anstatt beim Aufruf von toString einen Kopiervorgang zu starten, teilen sich String- und StringBuilder-Objekt nach dem Aufruf das interne Zeichenarray, d.h., beide Objekte verwenden einund denselben Puffer. Normalerweise wäre diese Vorgehensweise indiskutabel, denn nach der nächsten Änderung des StringBuilder-Objekts hätte sich dann auch der Inhalt des String-Objekts verändert (was per Definition nicht erlaubt ist). Um das zu verhindern, wird vom Konstruktor der String-Klasse während des Aufrufs von toString ein shared-Flag im StringBuilder-Objekt gesetzt. Dieses wird bei allen verändernden StringBuilder-Methoden abgefragt und führt dazu, dass – wenn es gesetzt ist – der Pufferinhalt vor der Veränderung kopiert und die Änderung auf der Kopie vorgenommen wird. Ein echter Kopiervorgang wird also so lange nicht erforderlich, wie auf den StringBuilder nicht schreibend zugegriffen wird.
Die Unveränderlichkeit von String-Objekten Da die Klasse String keine Möglichkeit bietet, die gespeicherte Zeichenkette nach der Instanzierung des Objekts zu verändern, können einige Operationen auf Zeichenketten sehr effizient implementiert werden. So erfordert beispielsweise die einfache Zuweisung zweier String-Objekte lediglich das Kopieren eines Zeigers, ohne dass durch Aliasing die Gefahr besteht, beim Ändern eines Strings versehentlich weitere Objekte zu ändern, die auf denselben Speicherbereich zeigen. Soll ein String physikalisch kopiert werden, kann das mit Hilfe eines speziellen Konstruktors erreicht werden: String s2 = new String(s1); Da der interne Puffer hierbei kopiert wird, ist der Aufruf natürlich ineffizienter als die einfache Zuweisung. Auch die Methode substring der Klasse String konnte sehr effizient implementiert werden. Sie erzeugt zwar ein neues String-Objekt, aber den internen Zeichenpuffer teilt es sich mit dem bisherigen Objekt. Lediglich die Membervariablen, in denen die Startposition und relevante Länge des Puffers festgehalten werden, müssen im neuen Objekt angepasst werden. Dadurch ist auch das Extrahieren von langen Teilzeichenketten recht performant. Dasselbe gilt für die Methode trim, die ebenfalls substring verwendet und daher keine Zeichen kopieren muss.
1232
Tuning-Tipps
Kapitel 50
Durchlaufen von Zeichenketten Soll ein String durchlaufen werden, so lässt sich mit der Methode length seine Länge ermitteln und durch wiederholten Aufruf von charAt können alle Zeichen nacheinander abgeholt werden. Alternativ könnte man auch zunächst ein Zeichenarray allozieren und durch Aufruf von getChars alle Zeichen hineinkopieren. Beim späteren Durchlaufen wäre dann kein Methodenaufruf mehr erforderlich, sondern die einzelnen Array-Elemente könnten direkt verwendet werden. Die Laufzeitunterschiede zwischen beiden Varianten sind allerdings minimal und werden in der Praxis kaum ins Gewicht fallen (da die Klasse String als final deklariert wurde und die Methode charAt nicht synchronized ist, kann sie sehr performant aufgerufen werden).
Das Interface CharSequence und die Methode toString Wenn Sie eine Zeichenkette aus Einzelstücken zusammensetzen, verwenden Sie am besten die Klasse StringBuilder. Doch wenn Sie diese Zeichenkette anschließend als Parameter oder Rückgabewert übergeben, wird dieser häufig über die Methode toString in einen äquivalenten String umgewandelt. Wird die Zeichenkette anschließend weiterbearbeitet, wird der übergebene String anschließend wieder in einen StringBuilder umgewandelt und so weiter. Sie können sich diese unnötigen Kopieroperationen allerdings auch sparen und Ihren Code gleichzeitig wesentlich lesbarer machen, indem Sie in diesen Fällen einfach in der Methodensignatur einen Parameter vom Typ StringBuilder definieren und das Objekt direkt übergeben. Können Sie die Signatur der Methode allerdings nicht ändern – etwa, weil die Methode auch mit gewöhnlichen Strings aufgerufen werden soll –, hält das JDK seit der Version 5 das Interface CharSequence für Sie bereit, welches bereits in Abschnitt 11.5, Seite 277 vorgestellt wurde. Dieses Interface wird sowohl von der Klasse String als auch von StringBuilder implementiert und gestattet es, Objekte beiden Typs zu übergeben.
50.2.2 Methodenaufrufe
1233
Teil IX
Eine der häufigsten Operationen in objektorientierten Programmiersprachen ist der Aufruf einer Methode an einer Klasse oder einem Objekt. Generell werden Methodenaufrufe in Java recht performant ausgeführt. Ihr Laufzeitverhalten ist jedoch stark von ihrer Signatur und ihren Attributen abhängig. Tabelle 50.1, Seite 1234 gibt einen Überblick über die Laufzeit (in msec.) von 10 Millionen Aufrufen einer trivialen Methode unter unterschiedlichen Bedingungen. Alle Messungen wurden mit dem JDK 1.2 Beta 4 auf einem PentiumII-266 unter Windows 95 vorgenommen.
Kapitel 50
Tabelle 50.1: Geschwindigkeit von Methodenaufrufen
Performance-Tuning
Signatur/Attribute
Ohne JIT
Mit JIT
public
5650
280
public, mit vier Parametern
7800
390
public static
5060
110
protected
5770
280
private
5820
50
public synchronized
9500
4660
public final
6260
50
Dabei fallen einige Dinge auf: " In jedem Fall bringt der JIT einen erheblichen Geschwindigkeitsvorteil. Er liegt (mit Ausnahme der synchronized-Methode) in diesem Beispiel durchweg bei über einer Zehnerpotenz. " Methoden des Typs final und private werden am schnellsten ausgeführt, insbesondere bei aktiviertem JIT. " Klassenmethoden werden schneller ausgeführt als Instanzmethoden. " Die Übergabe von Parametern erfordert zusätzliche Zeit. In unserem Beispiel wurden vier Argumente (int, String, double und boolean) übergeben. " Mit Abstand am langsamsten ist der Aufruf von Methoden, die das synchronizedAttribut verwenden, denn der Zugriff auf die Sperre zur Synchronisation in MultiThreading-Umgebungen kostet erhebliche Zeit. Auch der Just-In-Time-Compiler bringt hier keine nennenswerten Vorteile. Weiterhin ist zu beachten, dass der polymorphe Aufruf von Methoden Zeit kostet (was nicht aus dieser Tabelle abgelesen werden kann). Ist beispielsweise aus einer Klasse A eine weitere Klasse B abgeleitet, so ist der Aufruf von Methoden auf einem Objekt des Typs A kostspieliger als der auf einem Objekt des Typs B. Aus diesen Ergebnissen allgemeingültige Empfehlungen abzuleiten, ist schwierig. Zwar empfiehlt es sich offensichtlich, Methoden als private bzw. final zu deklarieren, wenn sicher ist, dass sie in abgeleiteten Klassen nicht aufgerufen bzw. überlagert werden sollen. Auch könnte man versuchen, verstärkt Klassenmethoden zu verwenden oder zur Vermeidung von polymorphen Aufrufen die Vererbungshierarchie zu beschränken oder mit Hilfe des Attributs final ganz abzuschneiden. All diese Entscheidungen hätten aber einen starken Einfluss auf das Klassendesign der Anwendung und könnten sich leicht an anderer Stelle als Sackgasse herausstellen. Der einzig wirklich allgemeingültige Rat besteht darin, Methoden nur dann als synchronized zu deklarieren, wenn es wirklich erforderlich ist. Eine Methode, die keine Membervariablen verwendet, die gleichzeitig von anderen Threads manipuliert werden, braucht
1234
Tuning-Tipps
Kapitel 50
auch nicht synchronisiert zu werden. Eine Anwendung, die nur einen einzigen Thread besitzt und deren Methoden nicht von Hintergrundthreads aufgerufen werden, braucht überhaupt keine synchronisierten Methoden in eigenen Klassen.
50.2.3 Vektoren und Listen Ein Vector ist ein bequemes Hilfsmittel, um Listen von Objekten zu speichern, auf die sowohl sequentiell als auch wahlfrei zugriffen werden kann. Aufgrund seiner einfachen Anwendung und seiner Flexibilität bezüglich der Art und Menge der zu speichernden Elemente wird er in vielen Programmen ausgiebig verwendet. Bei falschem Einsatz können Vektoren durchaus zum Performance-Killer werden, und wir wollen daher einige Hinweise zu ihrer Verwendung geben. Zunächst einmal ist der Datenpuffer eines Vektors als Array implementiert. Da die Größe von Arrays nach ihrer Initialisierung nicht mehr verändert werden kann, erfordert das Einfügen neuer Elemente möglicherweise das Allozieren eines neuen Puffers und das Umkopieren der vorhandenen Elemente. Ein Vector besitzt dazu die beiden Attribute Kapazität und Ladefaktor. Die Kapazität gibt an, wie viele Elemente insgesamt aufgenommen werden können, also wie groß der interne Puffer ist. Der Ladefaktor bestimmt, um wie viele Elemente der interne Puffer erweitert wird, wenn beim Einfügen eines neuen Elements nicht mehr ausreichend Platz vorhanden ist. Je kleiner die anfängliche Kapazität und der Ladefaktor sind, desto häufiger ist beim fortgesetzten Einfügen von Elementen ein zeitaufwändiges Umkopieren erforderlich. Wird ein Vector ohne Argumente instanziert, so hat sein Puffer eine anfängliche Kapazität von zehn Objekten und der Ladefaktor ist 0. Letzteres bedeutet, dass die Kapazität bei jeder Erweiterung verdoppelt wird (analog zur Klasse StringBuilder, s. Abschnitt 50.2.1, Seite 1229). Alternativ können die Kapazität oder auch beide Werte beim Instanzieren an den Konstruktor übergeben werden. Durch die folgende Deklaration wird ein Vector mit einer anfänglichen Kapazität von 100 Elementen und einem Ladefaktor von 50 angelegt: Vector v = new Vector(100, 50); Ein weiteres Problem der Klasse Vector ist, dass die meisten ihrer Methoden als synchronized deklariert wurden. Dadurch kann ein Vector zwar sehr einfach als gemeinsame Datenstruktur mehrerer Threads verwendet werden. Die Zugriffsmethoden sind aber leider auch ohne Multi-Threading-Betrieb entsprechend langsam.
Seit der Version 1.2 des JDK stehen mit den Klassen LinkedList und ArrayList auch alternative Listenimplementierungen zur Verfügung, die anstelle von Vector verwendet werden können. Hier ist jedoch Vorsicht geboten, soll das Programm nicht langsamer laufen als vorher. Die Klasse LinkedList implementiert die Datenstruktur in klassischer Form als doppelt verkettete Liste ihrer Elemente. Zwar entfallen dadurch die Kopiervorgänge, die beim Erweitern des Arrays erforderlich waren. Durch die Vielzahl der allozierten Objekte, in
» » »
1235
Teil IX
JDK 1.1–6.0
Kapitel 50
Performance-Tuning
denen die Listenelemente und die Zeiger gespeichert werden müssen, und die teilweise ineffiziente Implementierung einiger Grundoperationen (insbesondere add) hat sich LinkedList jedoch im Test als relativ ineffizient herausgestellt. Wesentlich bessere Ergebnisse gab es mit der Klasse ArrayList. Sie ist ähnlich wie Vector implementiert, verzichtet aber (wie die meisten 1.2er Collections) auf die synchronized-Attribute und ist daher – insbesondere bei aktiviertem JIT und Zugriff mit add und get sehr – performant.
Listing 50.6, Seite 1236 zeigt drei Methoden, die jeweils ein String-Array übergeben bekommen und daraus eine bestimmte Anzahl von Elementen zurückgeben. Die erste Version verwendet einen Vector, die zweite eine LinkedList und die dritte eine ArrayList zur Datenspeicherung. Im Test war die dritte Version eindeutig die schnellste. Bei aktiviertem JIT und Übergabe von 100000 Elementen, von denen jeweils die Hälfte zurückgegeben wurden, war das Verhältnis der Laufzeiten der drei Methoden etwa 3:18:1. Listing 50.6: Vergleich von Listen und Vektoren
1236
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032
public static String[] vtest1(String el[], int retsize) { //Verwendet Vector Vector v = new Vector(el.length + 10); for (int i = 0; i < el.length; ++i) { v.addElement(el[i]); } String[] ret = new String[retsize]; for (int i = 0; i < retsize; ++i) { ret[i] = (String)v.elementAt(i); } return ret; } public static String[] vtest2(String el[], int retsize) { //Verwendet LinkedList LinkedList l = new LinkedList(); for (int i = 0; i < el.length; ++i) { l.add(el[i]); } String[] ret = new String[retsize]; Iterator it = l.iterator(); for (int i = 0; i < retsize; ++i) { ret[i] = (String)it.next(); } return ret; } public static String[] vtest3(String el[], int retsize) { //Verwendet ArrayList
Tuning-Tipps 033 034 035 036 037 038 039 040 041 042 }
ArrayList l = new ArrayList(el.length + 10); for (int i = 0; i < el.length; ++i) { l.add(el[i]); } String[] ret = new String[retsize]; for (int i = 0; i < retsize; ++i) { ret[i] = (String)l.get(i); } return ret;
Kapitel 50 Listing 50.6: Vergleich von Listen und Vektoren (Forts.)
Ist es dagegen erforderlich, viele Einfügungen und Löschungen innerhalb der Liste vorzunehmen, sollte im Allgemeinen eine zeigerbasierte Implementierung der arraybasierten vorgezogen werden. Während es bei Letzterer stets erforderlich ist, einen Teil des Arrays umzukopieren, wenn ein Element eingefügt oder gelöscht wird, brauchen bei den verzeigerten Datenstrukturen lediglich ein paar Verweise aktualisiert zu werden.
50.2.4 Dateizugriffe Schreiben von Streams Seit dem JDK 1.1 gibt es die Writer-Klassen, mit denen Character-Streams verarbeitet werden können. Passend zur internen Darstellung des char-Typs in Java verwenden sie 16-Bit breite UNICODE-Zeichen zur Ein- und Ausgabe. Um eine Datei zu erzeugen, kann ein FileWriter-Objekt angelegt werden, und die Zeichen werden mit den write-Methoden geschrieben. Um die Performance zu erhöhen, kann der FileWriter in einen BufferedWriter gekapselt werden, der mit Hilfe eines internen Zeichenpuffers die Anzahl der Schreibzugriffe reduziert. Im Test ergab sich dadurch ein Geschwindigkeitszuwachs um den Faktor drei bis vier gegenüber dem ungepufferten Zugriff. Die von BufferedWriter verwendete Standard-Puffergröße von 8 Kbyte ist in aller Regel ausreichend, weitere Vergrößerungen bringen keine nennenswerten Beschleunigungen. Das Dilemma der Writer-Klassen besteht darin, dass die meisten externen Dateien mit 8-Bit-Zeichen arbeiten, statt mit 16-Bit-UNICODE-Zeichen. Ein FileWriter führt also vor der Ausgabe eine Konvertierung der UNICODE-Zeichen durch, um sie im korrekten Format abzuspeichern. Der Aufruf der dazu verwendeten Methoden der Klasse CharToByteConverter aus dem Paket sun.io kostet natürlich Zeit und vermindert die Performance der Writer-Klasse. Wesentlich schneller sind die (älteren) OutputStream-Klassen, die nicht mit Zeichen, sondern mit Bytes arbeiten. Sie führen keine aufwändige Konvertierung durch, sondern geben je Zeichen einfach dessen niederwertige 8 Bit aus. Das spart viel Zeit und führte im Test zu einer nochmals um den Faktor drei bis vier beschleunigten Ausgabe (wenn auch der FileOutputStream in einen BufferedOutputStream eingeschlossen wurde).
1237
Teil IX
Die OutputStream-Klassen sind also immer dann den Writer-Klassen vorzuziehen, wenn entweder sowieso Binärdaten ausgegeben werden sollen oder wenn sichergestellt ist, dass
Kapitel 50
Performance-Tuning
keine UNICODE-Zeichen verwendet werden, die durch das simple Abschneiden der oberen 8 Bit falsch ausgegeben würden. Da der UNICODE-Zeichensatz in den ersten 256 Zeichen zum ISO-8859-1-Zeichensatz kompatibel ist, sollten sich für die meisten europäischen und angelsächsischen Sprachen keine Probleme ergeben, wenn zur Ausgabe von Zeichen die OutputStream-Klassen verwendet werden. Listing 50.7, Seite 1238 zeigt das Erzeugen einer etwa 300 Kbyte großen Datei, bei der zunächst die Writer- und dann die OutputStream-Klassen verwendet wurden. Im Test lag die Ausführungsgeschwindigkeit der zweiten Variante um etwa eine Zehnerpotenz über der ersten. Listing 50.7: Performance von Writer und OutputStream
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028
public static void createfile1() throws IOException { Writer writer = new FileWriter(FILENAME); for (int i = 0; i < LINES; ++i) { for (int j = 0; j < 60; ++j) { writer.write('x'); } writer.write(NL); } writer.close(); } public static void createfile4() throws IOException { OutputStream os = new BufferedOutputStream( new FileOutputStream(FILENAME) ); for (int i = 0; i < LINES; ++i) { for (int j = 0; j < 60; ++j) { os.write('x'); } os.write('\r'); os.write('\n'); } os.close(); }
Lesen von Streams Die Performance des sequentiellen Lesens von Zeichen- oder Byte-Streams zeigt ein ähnliches Verhalten wie die des sequentiellen Schreibens. Am langsamsten war der ungepufferte Zugriff mit der Klasse FileReader. Die größten Geschwindigkeitsgewinne ergaben sich durch das Kapseln des FileReader in einen BufferedReader, die Performance lag um etwa eine Zehnerpotenz höher als im ungepufferten Fall. Der Umstieg auf das byte-orientierte Einlesen mit den Klassen FileInputStream und BufferedInputStream
1238
Tuning-Tipps
Kapitel 50
brachte dagegen nur noch geringe Vorteile. Möglicherweise muss der zur Eingabekonvertierung in den Reader-Klassen verwendete ByteToCharConverter weniger Aufwand treiben, als ausgabeseitig nötig war.
RandomAccess-Dateien Der wahlfreie Zugriff auf eine Datei zum Lesen oder Schreiben erfolgt in Java mit der Klasse RandomAccessFile. Da sie nicht Bestandteil der Reader- Writer-, InputStream- oder OutputStream-Hierarchien ist, besteht auch nicht die Möglichkeit, sie zum Zweck der Pufferung zu schachteln. Tatsächlich ist der ungepufferte byteweise Zugriff auf ein RandomAccessFile sehr langsam, er liegt etwa in der Größenordnung des ungepufferten Zugriffs auf Character-Streams. Wesentlich schneller kann mit Hilfe der read- und writeMethoden gearbeitet werden, wenn nicht nur ein einzelnes, sondern ein ganzes Array von Bytes verarbeitet wird. Je nach Puffergröße und Verarbeitungsaufwand werden dann Geschwindigkeiten wie bei gepufferten Bytestreams oder höher erzielt. Das folgende Beispiel zeigt, wie man mit einem 100 Byte großen Puffer eine Random-Access-Datei bereits sehr schnell lesen kann. 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016
public static void randomtest2() throws IOException { RandomAccessFile file = new RandomAccessFile(FILENAME, "rw"); int cnt = 0; byte[] buf = new byte[100]; while (true) { int num = file.read(buf); if (num <= 0) { break; } cnt += num; } System.out.println(cnt + " Bytes read"); file.close(); }
Listing 50.8: Gepufferter Zugriff auf RandomAccess-Dateien
1239
Teil IX
Das Programm liest die komplette Datei in Stücken von jeweils 100 Byte ein. Der Rückgabewert von read gibt die tatsächliche Zahl gelesener Bytes an. Sie entspricht normalerweise der Puffergröße, liegt aber beim letzten Datenpaket darunter, wenn die Dateigröße nicht zufällig ein Vielfaches der Puffergröße ist. Die Performance von randomtest2 ist sehr gut, sie lag auf dem Testrechner (Pentium II, 266 MHz, 128 MB, UW-SCSI) bei etwa 5 Mbyte pro Sekunde, was für ein Java-Programm sicherlich ein respektabler Wert ist. Ein wesentlicher Grund ist darin zu suchen, dass durch den programmeigenen Puffer ein Großteil der Methodenaufrufe zum Lesen einzelner Bytes vermieden werden (in diesem Fall sind es um den Faktor 100 weniger). Auf die gleiche Weise lassen sich auch die streamorientierten Dateizugriffe beschleunigen, wenn die Anwendung nicht unbedingt darauf angewiesen ist, zeichenweise zu lesen bzw. zu schreiben.
Kapitel 50
Performance-Tuning
50.2.5 Speicheroptimierung Neben den direkten Prozessoraktivitäten hat auch die Art und Weise, in der das Programm mit dem Hauptspeicher umgeht, einen erheblichen Einfluss auf dessen Performance. Einige der Aspekte, die dabei eine Rolle spielen, sind: " Jede Allozierung von Speicher kostet Rechenzeit. Der Speicher muss entweder vom Betriebssystem oder vom Laufzeitsystem der VM beschafft werden. Auch das (automatische) Initialisieren des Speichers kostet Zeit. Das Anlegen eines Arrays mit 1000 Elementen dauert wesentlich länger als das eines mit 10 Elementen. " Objekte mit aufwändigen Konstruktoren benötigen möglicherweise viel Zeit zur Initialisierung. Bei ihnen kann es sinnvoll sein, sie zu »recyceln«. Dazu werden sie nach Gebrauch in einer geeigneten Datenstruktur gesammelt und können dem nächsten Interessenten (alternativ zur Erzeugung eines neuen Objekts) zur Verfügung gestellt werden. Vor der Verwendung muss dieser das Objekt natürlich geeignet initialisieren. " Nicht mehr referenzierter Speicher belastet den Garbage Collector und erfordert CPU-Zeit, um dem Programm wieder zugeführt werden zu können. " In ungünstigen Fällen kann es sein, dass die VM den benötigten Speicher schrittweise in relativ kleinen Stücken beim Betriebssystem anfordert. Das kostet unter Umständen sehr viel Zeit. In diesem Fall kann es sinnvoll sein, mit Hilfe des Schalters -Xms den beim Start der VM anzufordernden Speicher auf einen höheren Wert einzustellen. " Große Mengen an temporären, kurzlebigen Objekten belasten die VM ebenfalls. Derartige Allokationsszenarien entstehen beispielsweise beim Modifizieren von Strings oder wenn primitive Typen mit Hilfe ihrer Wrapper-Klassen in Collections gespeichert werden sollen. In Abschnitt 50.3, Seite 1241 zeigen wir ein harmlos aussehendes Programm, das beim Anlegen von 10 Kbyte Nutzdaten 75 Mbyte Datenmüll erzeugt. " Werden Referenzen auf Objekte nicht gelöscht, bleibt der zugeordnete Speicher belegt und der Garbage Collector kann ihn nicht wieder freigeben. Das belastet nicht nur die VM, die zunehmend neuen Speicher beim Betriebssystem anfordern muss, sondern führt früher oder später zum Absturz des Programms mit einem OutOfMemoryError. " Derartige Speicherlecks entstehen, wenn eigentlich nicht mehr benötigte Objekte an »lebenden« Referenzen hängen (also an Variablen, die im Programm noch benötigt werden). Lebende Referenzen sind die lokalen Variablen auf den Stacks aller laufenden Threads plus alle statischen Variablen des Programms. Zudem natürlich alle Variablen, die indirekt daran hängen. Als Programmierer sollte man diesbezüglich den statischen Variablen (insbesondere wenn sie auf Collections verweisen) besonderes Augenmerk schenken. " Um dem Garbage Collector die Arbeit zu erleichtern, kann es sinnvoll sein, ihn in Programmpausen von Zeit zu Zeit durch Aufruf der Methode gc der Klasse System explizit aufzurufen. Das Programm kann ihm auch dadurch helfen, dass nicht mehr benötigten Objektvariablen explizit der Wert null zugewiesen wird.
1240
Einsatz eines Profilers
Kapitel 50
50.3 Einsatz eines Profilers 50.3.1 Grundlagen Die bisher vorgestellten Tipps und Tricks sind sicherlich eine Hilfe, um bereits während der Entwicklung eines Programms grundsätzliche Performanceprobleme zu vermeiden. Läuft das fertige Programm dann trotzdem nicht mit der gewünschten Geschwindigkeit (was in der Praxis häufig vorkommt), helfen pauschale Hinweise leider nicht weiter. Stattdessen gilt es herauszufinden, welche Teile des Programms für dessen schlechte Performance verantwortlich sind. Bei größeren Programmen, die aus vielen tausend Zeilen Quellcode bestehen, ist das eine komplizierte Aufgabe, die nur mit Hilfe eines guten Profilers bewältigt werden kann. Der Profiler ist ein Werkzeug, mit dessen Hilfe im laufenden Programm Performanceparameter, wie beispielsweise die verbrauchte CPU-Zeit, die Anzahl der allozierten Objekte oder die Anzahl der Aufrufe bestimmter Methoden, überwacht und gemessen werden können. Durch manuelle Inspektion der erzeugten Logdateien oder mit Hilfe eines Auswertungsprogramms kann dann festgestellt werden, welche Teile des Programms die größte Last erzeugen und daher verbesserungsbedürftig sind. JDK 1.1–6.0 Das Standard-JDK enthält bereits seit der Version 1.0 einen eingebauten Profiler, der Informationen über Laufzeit und Aufrufhäufigkeit von Methoden geben kann. Im JDK 1.2 wurde er erweitert und kann seither den Speicherverbrauch messen und Profiling-Informationen threadweise ausgeben. Mit dem JDK 1.3 wurde er erneut verbessert und mit einem offenen Profiler-API versehen (JVMPI, Java Virtual Machine Profiler Interface). Mit dessen Hilfe ist es möglich, eigene Profiling-Werkzeuge zu schreiben. Fester Bestandteil des JDK ist eine Beispielimplementierung hprof, die für einfache Profiling-Aufgaben verwendet werden kann.
» » »
Im Vergleich zu spezialisierten Produkten sind die Fähigkeiten des eingebauten Profilers etwas rudimentär. Insbesondere die vom Profiler erzeugte Ausgabedatei erfordert einigen Nachbearbeitungsaufwand. Zudem gibt es keine grafischen Auswertungen wie bei kommerziellen Profilern. Dennoch ist der JDK-Profiler ein brauchbares und hilfreiches Instrument, mit dem Performanceprobleme und Speicherengpässe analysiert werden können. Wir wollen uns in diesem Abschnitt mit den Grundlagen seiner Bedienung vertraut machen.
1241
Teil IX
Als Beispiel für die Anwendung des Profiler wollen wir ein Programm verwenden, dessen simple Aufgabe darin besteht, einen String mit 10000 Punkten zu erzeugen und auf dem Bildschirm auszugeben. Statt die Ratschläge aus dem vorigen Abschnitt zu beherzigen, verwendet das Programm den Operator +=, um den String zeichenweise in
Kapitel 50
Performance-Tuning
einer Schleife zusammenzusetzen. Auf langsameren Rechnern kann es durchaus einige Sekunden dauern, bis der String erzeugt und vollständig auf dem Bildschirm ausgegeben wurde: Listing 50.9: Ein Beispielprogramm zum Testen des Profilers
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021
/* ProfTest1A.java */ import java.util.*; public class ProfTest1A { public static String dots(int len) { String ret = ""; for (int i = 0; i < len; ++i) { ret += "."; } return ret; } public static void main(String[] args) { String s = dots(10000); System.out.println(s); } }
Nachfolgend wollen wir uns eine Beispielsitzung mit dem Profiler ansehen. Ähnlich wie bei einem Debugger besteht die typische Vorgehensweise darin, schrittweise Informationen über Rechenzeit- und Speicherverbrauch zu gewinnen und das Programm mit diesen Informationen nach und nach zu optimieren. Für gewöhnlich gibt es dabei kein Patentrezept, das direkt zum Erfolg führt. Stattdessen ähnelt der Umgang mit dem Profiler einer Detektivarbeit, bei der die einzelnen Teile der Lösung nach und nach gefunden werden.
50.3.2 Eine Beispielsitzung mit dem Profiler Erzeugen der Profiling-Informationen Zunächst muss das Programm wie gewöhnlich übersetzt werden: javac ProfTest1A.java Um das Programm unter Kontrolle des Profilers zu starten, ist die Option -Xrunhprof zu verwenden und nach einem Doppelpunkt mit den erforderlichen Parametrisierungen zu versehen. Die Parameter werden als kommaseparierte Liste von Argumenten der Form »Name=Wert« angegeben. Die wichtigsten Parameter von hprof sind:
1242
Einsatz eines Profilers
Name
Mögliche Werte
cpu
samples, times, old
heap
dump, sites, all
file
Name der Ausgabedatei
depth
Maximale Tiefe der Stacktraces
Kapitel 50
Tabelle 50.2: Parameter von hprof
Mit der Option cpu wird der CPU-Profiler aktiviert. Er kennt die Modi »samples«, »times« und »old«. Im Modus »samples« werden die Profiling-Informationen dadurch gewonnen, dass das laufende Programm mit Hilfe eines separaten Threads regelmäßig unterbrochen wird. Bei jeder Unterbrechung wird ein Stacktrace gezogen, der dem Profiler Auskunft darüber gibt, welche Methode gerade ausgeführt wird, in welcher Quelltextzeile das Programm steht und wie die Kette ihrer Aufrufer aussieht. Jeder derartige Schnappschuss wird als Sample bezeichnet. Die unterschiedlichen Stacktraces werden mit einem Aufrufzähler versehen, der immer dann um 1 erhöht wird, wenn bei einer Unterbrechung ein entsprechender Stacktrace gefunden wird. Aus dem Endwert der Zähler kann dann abgeleitet werden, wo das Programm die meiste Rechenzeit verbraucht hat. Denn je höher der Zähler war, desto öfter wurde das Programm an der zugehörigen Programmstelle »angetroffen« und desto wahrscheinlicher ist es, dass dort nennenswert Rechenzeit verbraucht wird. ACHTUNG
!
!
!
Natürlich ist diese Vorgehensweise nicht sehr präzise und es sind Fälle denkbar, bei denen sie ganz versagt. Aber sie ist einfach zu implementieren und beeinträchtigt die Laufzeit des Programms nur unwesentlich. In der Praxis sollten die Ergebnisse mit der nötigen Vorsicht betrachtet werden. Sie dürfen nicht als absolute Messwerte angesehen werden, sondern sind vorwiegend dazu geeignet, Tendenzen zu erkennen und Programmteile mit besonders hoher Rechenzeit ausfindig zu machen.
Der zweite Modus »times« arbeitet etwas anders. Statt lediglich die Anzahl der Stacktraces zu zählen, misst er tatsächlich die innerhalb der einzelnen Methoden verbrauchte Rechenzeit. Allerdings wird dadurch auch die Laufzeit des Programms stärker erhöht als im Modus »samples«. In der Praxis kann eine gemischte Vorgehensweise sinnvoll sein, bei der zunächst per »samples« die größten Performancefresser eliminiert werden und dann per »times« das Feintuning vorgenommen wird. Die Option »old« erstellt die Ausgabedatei in einem Format, wie sie von den Profilern der Prä-1.2-Versionen verwendet wurde. Wir wollen hier nicht weiter darauf eingehen.
1243
Teil IX
Bei der Verwendung des CPU-Profilers sind weiterhin die Optionen file und depth von Bedeutung. Mit file kann der Name der Ausgabedatei angegeben werden, er ist standardmäßig java.hprof.txt. Mit depth wird festgelegt, mit welcher maximalen Tiefe die Stacktraces aufgezeichnet werden (standardmäßig 4). Ist die Aufrufkette einer Methode
Kapitel 50
Performance-Tuning
länger als der angegebene Wert, wird der Stacktrace abgeschnitten. Bei der Analyse ist nicht mehr bis ins letzte Detail erkennbar, von welcher Stelle aus der Aufruf erfolgte. Wird depth auf 1 gesetzt, sind nur noch die Aufrufstellen sichtbar, die Aufrufer selbst bleiben unsichtbar. Wir wollen einen ersten Lauf mit dem CPU-Profiler im Modus »samples« und mit einer maximalen Stacktiefe von 10 machen und rufen das Programm daher wie folgt auf: java -Xint -Xrunhprof:cpu=samples,depth=10 ProfTest1A
i
i
i
INFO Die Option -Xint geben wir an, um das Programm im Interpreter-Modus laufen zu lassen und mögliche Verfälschungen durch den Hotspot-Compiler zu vermeiden.
Das CPU-Profile Das Programm erzeugt nun die Ausgabedatei java.hprof.txt mit den Profiling-Informationen. Sie besteht aus drei Teilen: " Im oberen Teil werden allgemeine Informationen zur Struktur der Datei und den darin verwendeten Einträgen gegeben. " Im mittleren Teil befinden sich die Stacktraces. " Im unteren Teil werden die Sampling-Ergebnisse ausgegeben. Die Analyse beginnt im unteren Teil. Er sieht bei unserer Beispielsitzung so aus (die Samples ab Position 11 wurden aus Gründen der Übersichtlichkeit entfernt): CPU SAMPLES rank self 1 53.66% 2 17.48% 3 17.07% 4 1.63% 5 1.22% 6 0.81% 7 0.81% 8 0.81% 9 0.41% 10 0.41% ... CPU SAMPLES
BEGIN (total = 246) Sun Jun 18 17:56:28 2000 accum count trace method 53.66% 132 9 java.lang.StringBuilder.expandCapacity 71.14% 43 13 java.lang.System.arraycopy 88.21% 42 11 java.lang.System.arraycopy 89.84% 4 10 java.lang.StringBuilder.toString 91.06% 3 17 java.lang.StringBuilder.append 91.87% 2 19 java.lang.StringBuilder.append 92.68% 2 24 sun.io.CharToByteSingleByte.convert 93.50% 2 12 java.lang.StringBuilder. 93.90% 1 20 java.lang.StringBuilder.append 94.31% 1 14 java.lang.String.getChars END
Die Ausgabe ist nach Aufrufhäufigkeit geordnet. Von den insgesamt 246 Samples, die während des Programmlaufs gezogen wurden, waren 132 in der Methode expandCapacity
1244
Einsatz eines Profilers
Kapitel 50
der Klasse StringBuilder und 43 und noch einmal 42 in der Methode arraycopy der Klasse System. Damit fielen insgesamt 88,21 Prozent aller Samples in diese Methoden. Da beide Methoden nicht selbst geschrieben sind und sich damit unseren Optimierungsversuchen entziehen, kann eine Performanceverbesserung lediglich dadurch erreicht werden, dass die Anzahl ihrer Aufrufe vermindert wird. Um die Aufrufer herauszufinden, müssen wir uns die in der fünften Spalten angegebenen Stacktraces ansehen. Der Stacktrace zu expandCapacity hat die Nummer 9 und sieht so aus: TRACE 9: java.lang.StringBuilder.expandCapacity(StringBuilder.java:202) java.lang.StringBuilder.append(StringBuilder.java:401) ProfTest1A.dots(ProfTest1A.java:11) ProfTest1A.main(ProfTest1A.java:18) Er besagt, dass der Sample in Zeile 202 der Methode expandCapacity der Klasse StringBuilder erzeugt wurde. Diese wurde von append aufgerufen, das von unserer eigenen Methode dots in Zeile 11 aufgerufen wurde. In Zeile 11 steht zwar kein Aufruf von append, dort befindet sich aber der +=-Operator zur Verkettung der Strings. Dieser wird vom Compiler in entsprechende Methoden- und Konstruktorenaufrufe der Klassen String und StringBuilder übersetzt. Als erste Erkenntnis stellen wir also fest, dass offensichtlich der +=-Operator zur StringVerkettung interne StringBuilder-Objekte erzeugt, die einen erheblichen Teil der CPUZeit benötigen, um während des Anfügens von Zeichen vergrößert zu werden. Die Stacktraces 11 und 13 der nächsten beiden Kandidaten verstärken den Eindruck, dass der +=-Operator in unserem Programm CPU-intensiven Code erzeugt: TRACE 11: java.lang.System.arraycopy(System.java:Native method) java.lang.String.getChars(String.java:553) java.lang.StringBuilder.append(StringBuilder.java:402) ProfTest1A.dots(ProfTest1A.java:11) ProfTest1A.main(ProfTest1A.java:18) TRACE 13: java.lang.System.arraycopy(System.java:Native method) java.lang.StringBuilder.expandCapacity(StringBuilder.java:203) java.lang.StringBuilder.append(StringBuilder.java:401) ProfTest1A.dots(ProfTest1A.java:11) ProfTest1A.main(ProfTest1A.java:18)
1245
Teil IX
Beide Arten von Samples gehen letztlich auf Zeile 11 unseres Programms zurück und zeigen Rechenzeitverbräuche, die durch die vom +=-Operator ausgelöste Verarbeitung temporärer Strings und StringBuilder verursacht werden. Obwohl die Samples in beiden Fällen in arraycopy gezogen wurden, gibt es zwei unterschiedliche Stacktraces, weil die
Kapitel 50
Performance-Tuning
Aufruferkette in beiden Fällen unterschiedlich ist. Einerseits wird arraycopy aus getChars aufgerufen (da Strings immutable sind, muss getChars bei jedem Aufruf eine Kopie des Zeichenarrays erstellen), andererseits wird arraycopy in expandCapacity benötigt, um das bei einer Verlängerung des StringBuilders erforderliche Umkopieren der Zeichen zu erledigen. Unsere erste Vermutung hat sich also bestätigt: Der harmlos aussehende Aufruf des +=-Operators in Zeile 11 unseres Programms erzeugt temporäre String- und StringBuilder-Objekte, wobei ein Großteil der Rechenzeit durch das Anhängen und Kopieren von Zeichen und das Erhöhen der Kapazität verbraucht wird.
Das Heap-Profile Einen noch deutlicheren Eindruck vermittelt ein Heap-Profile. Wir erstellen es, indem wir das Programm mit der Option heap=sites erneut unter Profiler-Kontrolle laufen lassen: java -Xint -Xrunhprof:heap=sites,depth=10 ProfTest1A Die Ausgabe besteht wie beim CPU-Profiling aus drei Teilen. Die ersten beiden entsprechen dem CPU-Profiler, der dritte enthält Informationen zur dynamischen HeapBelegung: SITES BEGIN (ordered by live bytes) Sun Jun 18 18:37:28 2000 percent live alloc'ed stack class rank self accum bytes objs bytes objs trace name 1 48.27% 48.27% 189974 25 75074238 19950 471 [C 2 38.00% 86.27% 149524 157 149524 157 1 [I 3 4.91% 91.17% 19304 741 19364 747 1 [C 4 2.44% 93.61% 9588 153 9588 153 1 [B 5 2.29% 95.90% 9010 1550 9022 1552 1 6 1.13% 97.03% 4460 199 4460 199 1 [S 7 1.06% 98.09% 4164 253 4220 260 1 [L; 8 0.06% 98.15% 238 3 238 3 403 [B 9 0.04% 98.20% 172 1 172 1 396 [B 10 0.04% 98.24% 170 10 170 10 390 [C ... SITES END Auch hier geben die erste Spalte die Rangordnung und die nächsten beiden die einzelnen und kumulierten Prozentanteile am Gesamtverbrauch an. Die Spalten 4 und 5 bieten einen Überblick über die aktiven Objekte, die nächsten beiden über die insgesamt allozierten Objekte (jeweils mit der Gesamtzahl allozierter Bytes und der Anzahl der Objekte). Die letzte Spalte stellt den Datentyp des Objekts dar. [C steht beispielsweise für ein ZeichenArray, [I für ein Integer-Array.
1246
Einsatz eines Profilers
Kapitel 50
Am auffälligsten ist die oberste Zeile und die darin ersichtliche Diskrepanz zwischen aktiven und allozierten Objekten. Dort steht, dass unser Programm 19950 Zeichen-Arrays mit insgesamt 75 Mbyte Speicher alloziert hat, davon aber nur noch 25 Objekte mit kaum 200 Kbyte Speicher aktiv sind. Hier wurden also in erheblichen Umfang kurzlebige Objekte erzeugt und anschließend wieder fallengelassen. Stacktrace 471 sieht so aus: TRACE 471: java.lang.StringBuilder.expandCapacity(StringBuilder.java:202) java.lang.StringBuilder.append(StringBuilder.java:401) ProfTest1A.dots(ProfTest1A.java:11) ProfTest1A.main(ProfTest1A.java:18) Wieder liegt der Verursacher in Zeile 11 unseres Programms und wir sehen, dass der +=-Operator nicht nur viel Rechenzeit verbraucht, sondern zudem eine große Anzahl temporärer Objekte erzeugt und damit das Laufzeitsystem und den Garbage Collector belastet.
Die optimierte Version des Programms Da wir auf die Interna der Klassen String und StringBuilder keinen Einfluss haben, kann die Optimierung nur darin bestehen, die Verwendung des +=-Operators einzuschränken oder eine besser geeignete Alternative zu wählen. Diese ist natürlich schon aus Abschnitt 50.2.1, Seite 1229 bekannt und besteht darin, direkt mit StringBuilder-Objekten zu arbeiten. Die verbesserte Version unseres Programms sieht dann so aus: /* ProfTest1B.java */ import java.util.*; public class ProfTest1B { public static String dots(int len) { StringBuilder sb = new StringBuilder(len + 10); for (int i = 0; i < len; ++i) { sb.append('.'); } return sb.toString(); }
Listing 50.10: Das verbesserte Programm nach der Profiler-Sitzung
public static void main(String[] args) { String s = dots(10000); System.out.println(s); } }
1247
Teil IX
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021
Kapitel 50
Performance-Tuning
Wird nun ein CPU-Profiling durchgeführt, ergibt sich ein gänzlich anderes Bild: CPU SAMPLES BEGIN (total = 15) Sun Jun 18 19:03:56 2000 rank self accum count trace method 1 26.67% 26.67% 4 9 sun.io.CharToByteSingleByte.convert 2 6.67% 33.33% 1 2 java.io.InputStreamReader. 3 6.67% 40.00% 1 12 sun.io.CharToByteSingleByte.getNative 4 6.67% 46.67% 1 11 sun.io.CharToByteSingleByte.convert 5 6.67% 53.33% 1 13 java.io.FileOutputStream.writeBytes 6 6.67% 60.00% 1 4 sun.security.provider.PolicyFile.getPermissions 7 6.67% 66.67% 1 5 sun.net.www.protocol.file.FileURLConnection.getPermission 8 6.67% 73.33% 1 10 java.io.FileOutputStream.writeBytes 9 6.67% 80.00% 1 1 sun.misc.URLClassPath$FileLoader.getResource 10 6.67% 86.67% 1 6 java.net.URLClassLoader.getPermissions 11 6.67% 93.33% 1 3 java.security.Security.loadProviders 12 6.67% 100.00% 1 8 java.lang.StringBuilder.append CPU SAMPLES END Statt 246 gab es nur noch 15 Samples (die Laufzeit des Programms hat sich also auf ein Sechzehntel reduziert) und nur noch ein einziger von ihnen wurde durch den Aufruf der Methode dots verursacht. Alle anderen sind auf den Aufruf von println in Zeile 019 zurückzuführen. Der Heap-Profiler liefert ein ähnliches Bild: Der gesamte Speicherverbrauch des Programms liegt nun in der Größenordnung von 150 Kbyte und es gibt keine nennenswerten temporären Objektallokationen mehr.
» » »
1248
JDK 1.1–6.0 In früheren JDKs wurde der Profiler nicht mit der Option -Xrunhprof, sondern mit -Xprof bzw. mit -prof aufgerufen. Zudem durfte nicht der normale Interpreter verwendet werden, sondern mit java_g musste dessen Debug-Version aufgerufen werden. Auch das Deaktivieren des Just-In-Time-Compilers änderte sich im Laufe der Versionen. Während es seit dem JDK 1.3 mit dem Schalter -Xint erledigt wird, musste zuvor die Umgebungsvariable JAVA_COMPILER auf den Wert NONE gesetzt werden. Soll der Profiler bei einem Applet verwendet werden, kann die Aufrufoption mit dem Schalter -J an den Appletviewer übergeben werden.
Einsatz eines Profilers
Kapitel 50
50.3.3 Ausblick Egal, ob mit dem eingebauten Profiler das Laufzeitverhalten oder der Speicherverbrauch der Anwendung untersucht werden soll, die prinzipielle Vorgehensweise ist stets gleich: " Zunächst wird das Programm mit der Option -Xrunhprof gestartet und eine Datei mit Profiling-Informationen erstellt. " Die größten Rechenzeit- und Speicherverbraucher werden ermittelt und über ihre Stacktraces wird bestimmt, woher sie aufgerufen werden und in welcher Weise sie zum Programmergebnis beitragen. " Stehen genügend Erkenntnisse zur Verfügung, kann das Programm verbessert und ein erneuter Profilerlauf durchgeführt werden. " Sind die Ergebnisse zufriedenstellend, kann der Profiler deaktiviert werden, andernfalls beginnt das Spiel von vorne. Ist der Anteil von lokalem Code am Rechenzeitverbrauch hoch, kann versucht werden, diesen zu verringern. Typische Ansatzpunkte dafür sind das Vermindern der Anzahl von Schleifendurchläufen (durch bessere Algorithmen), die Verwendung von Ganz- statt Fließkommazahlen, das Herausziehen von schleifeninvariantem Code, das Vermeiden der Doppelauswertung von gemeinsamen Teilausdrücken, das Wiederverwenden bekannter Teilergebnisse, die Verwendung alternativer Datenstrukturen, das Eliminieren von unbenutztem Code oder das Reduzieren der Stärke von Ausdrücken, indem sie durch algebraisch gleichwertige, aber schnellere Ausdrücke ersetzt werden. Wird ein großer Anteil der Rechenzeit dagegen in Aufrufen von Untermethoden verbraucht, kann versucht werden, deren Aufrufhäufigkeit zu vermindern, sie durch performantere Aufrufe zu ersetzen, oder – im Falle eigener Methoden – ihre Ablaufgeschwindigkeit zu erhöhen. Der zielgerichtete Einsatz dieser Techniken erfordert gute Werkzeuge, namentlich einen guten Profiler. Bei kleineren Problemen mag es ausreichend sein, die Ablaufgeschwindigkeit mit Ausgabeanweisungen und System.currentTimeMillis zu ermitteln, und auch der JDK-Profiler kann mit Erfolg eingesetzt werden. Daneben gibt es einige kommerzielle und experimentelle Produkte mit wesentlich erweiterten Fähigkeiten. Beispiele für solche Profiler sind JProbe (http://www.quest.com/jprobe/), der auch für Teile der Software zu diesem Buch verwendet wurde, oder JInsight (http://www.alphaWorks.ibm.com). Zu ihren Fähigkeiten zählen beispielsweise: " Die grafische Darstellung der Aufrufhierarchie mit Laufzeitinformationen zu Methoden und Aufrufen " Das Ermitteln von Profiling-Informationen bis auf die Ebene einzelner Quelltextzeilen hinab
1249
Teil IX
" Das Erstellen dynamischer Profile, um die Veränderung wichtiger Parameter bereits während des laufenden Programms beobachten zu können
Kapitel 50
Performance-Tuning
" Die Rückverfolgung von alloziertem Speicher sowohl zu den Programmstellen, die den Speicher belegt haben, als auch zu den Variablen, in denen die zugehörigen Objekte gehalten werden " Das Vergleichen von Profiling-Läufen, um die Auswirkungen von Optimierungsversuchen studieren zu können " Die Ausgabe von Reports und HTML-Dateien zu Vergleichs- und Dokumentationszwecken
Zusammenfassung In diesem Kapitel wurden folgende Themen behandelt: " Allgemeine Bemerkungen zur Geschwindigkeit von Java-Programmen " Interpretierter Code, kompilierter Code und Just-In-Time-Compiler " Tuning von String-Zugriffen und Anwendung der Klasse StringBuilder " Die Performance von Methodenaufrufen " Hinweise zur Verwendung von Vektoren und Listen " Tuning von Dateizugriffen " Aspekte der Speicherzuordnung in Java " Anwendung des Profilers und Auswerten der Profiling-Informationen
1250
51
Hilfsprogramme des JDK
51.1
javac – der Compiler
In diesem Kapitel wollen wir die wichtigsten Hilfsprogramme des Java Development Kit bzw. Java Runtime Environment vorstellen. Die meisten Programme arbeiten kommandozeilenorientiert und werden aus einer DOS-Box oder einer Shell heraus aufgerufen. Einige von ihnen sind interaktiv und haben eine einfache grafische Oberfläche. Wir wollen die Aufrufsyntax und wichtigsten Optionen der folgenden Programme vorstellen: " javac – der Compiler " java – der Interpreter " appletviewer – der Applet-Interpreter " jdb – der Debugger " javadoc – der Dokumentationsgenerator " jar – das Archivierungswerkzeug " javap – der Disassembler " serialver – Zugriff auf die serialVersionUID " keytool – Verwaltung von kryptografischen Schlüsseln " policytool – Bearbeiten von Policy-Dateien " jarsigner – Signieren von Archiven " rmic – Erzeugen von RMI-Stubs und -Skeletons
Teil IX
" rmiregistry – RMI-Namensservice
Kapitel 51
Hilfsprogramme des JDK
51.1.1
Aufruf
javac [ options ] [@filelist | { filename.java }]
51.1.2
Beschreibung
Der Compiler javac übersetzt Sourcecode in Bytecode. Zu jeder Klasse, die innerhalb einer .java-Datei definiert wurde, wird eine eigene .class-Datei erzeugt. Der Compiler wacht automatisch über die Abhängigkeiten zwischen den Quelldateien. Wird beispielsweise in einer Klasse X in der Datei X.java eine Klasse Y, die in Y.java liegt, verwendet, so wird Y.java automatisch mit übersetzt, wenn es erforderlich ist. Anstelle der Liste von Dateinamen kann nach einem @ auch eine Textdatei angegeben werden, in der die zu übersetzenden Dateien durch Whitespaces getrennt angegeben werden.
51.1.3 Tabelle 51.1: Optionen von javac
Optionen
Option
Bedeutung
-version
Ausgabe der Versionsnummer (seit der Version 5.0 verfügbar)
-classpath path
Gibt die Liste der Pfade zur Suche von Klassendateien an. Dieser Schalter übersteuert den Wert einer eventuell gesetzten Umgebungsvariable CLASSPATH. Bezüglich der Auswirkungen des Klassenpfads in den unterschiedlichen JDK-Versionen lesen Sie bitte Abschnitt 13.2.2, Seite 305.
-g
Aktiviert die Erzeugung von Debug-Informationen. Dieser Schalter sollte aktiviert werden, bevor ein Programm debuggt wird.
-nowarn
Deaktiviert die Ausgabe von Warnungen.
-O
Schaltet den Code-Optimierer an. Dessen Fähigkeiten waren allerdings nie besonders ausgeprägt und beschränkten sich bis zum JDK 1.2 vorwiegend auf das Expandieren von Methodenaufrufen. In der Dokumentation zum JDK 1.3 steht sogar die Anmerkung Note: the -O option does nothing in the current implementation of javac and oldjavac. Seit dem JDK 1.4 ist der Schalter gar nicht mehr dokumentiert.
-verbose
Aktiviert die Ausgabe von Meldungen über geladene Quell- und Klassendateien während der Übersetzung.
-depend
Normalerweise wird eine Quelldatei neu kompiliert, wenn das Datum der letzten Änderung nach dem Änderungsdatum der Klassendatei liegt oder wenn die Klassendatei ganz fehlt. Mit Hilfe dieses Schalters wird diese Entscheidung auf der Basis von Headerinformationen in der Klassendatei auf eine zuverlässigere Weise getroffen. Der Übersetzungsvorgang wird aber unter Umständen langsamer. Dieser Schalter war bis zum JDK 1.1 vorhanden, wird aber mittlerweile nicht mehr unterstützt. Die neueren Compiler erkennen die Abhängigkeiten weitgehend automatisch.
1252
java – der Interpreter
Option
Bedeutung
-deprecation
Sorgt dafür, dass bei jedem Aufruf einer als deprecated markierten Methode (@deprecated-Marke im Dokumentationskommentar) zusätzliche Informa-
Kapitel 51 Tabelle 51.1: Optionen von javac (Forts.)
tionen über mögliche Workarounds ausgegegeben werden. Alle Methoden aus älteren JDKs, die im aktuellen JDK nicht mehr verwendet werden sollen, werden mit diesem Flag markiert. -target version
Es wird Bytecode erstellt, der zu der angegebenen Version des Laufzeitsystems kompatibel ist.
-source version
Es wird Sourcecode akzeptiert, der zu der angegebenen Version kompatibel ist. Dieser Schalter ist ab JDK-Version 1.4 verfügbar, er wird in Abschnitt 6.4.1, Seite 144 erläutert. Auch für die sprachlichen Neuerungen der J2SE 5.0 wird diese Option benötigt (in der Form "-source 1.5"). Steht ab der J2SE 5.0 zur Verfügung und aktiviert eine Reihe von Warnungen,
-Xlint
die anzeigen, dass der Quelltext Konstrukte enthält, die zwar legal, aber unter Umständen problematisch sind. Als Beispiel wird in der Dokumentation eine Klasse genannt, die zwar das Interface Serializable implementiert, aber keine eigene serialVersionUID zur Verfügung stellt.
51.2
java – der Interpreter
51.2.1
Aufruf
java [ options ] classname [{ args }] javaw [ options ] classname [{ args }]
51.2.2 Beschreibung Der Interpreter java dient zur Ausführung kompilierter Java-Programme, die als Bytecode in .class-Dateien vorliegen. javaw erfüllt denselben Zweck, erzeugt aber kein Terminalfenster beim Start des Programms und erlaubt nicht die Verwendung der Standard-Streams System.in, System.out und System.err. Beim Aufruf beider Programme wird der Name einer Klassendatei erwartet (ohne die Erweiterung .class). Damit sie ausgeführt werden kann, muss sie eine Klassenmethode main mit folgender Signatur enthalten: public static void main(String[] args)
1253
Teil IX
Alle Argumente, die nach dem Namen der Klassendatei an java übergeben werden, stehen nach dem Aufruf von main in args zur Verfügung. Der Java-Interpreter wird nach
Kapitel 51
Hilfsprogramme des JDK
dem Rücksprung aus main beendet, wenn keine eigenen Threads erzeugt wurden. Falls weitere Threads erzeugt wurden, wird er verlassen, nachdem der letzte VordergrundThread beendet wurde. Da während der Ausführung eines Java-Programms meist weitere Klassendateien benötigt werden, muss der Interpreter wissen, wo diese zu finden sind. Standardmäßig sucht er dabei im systemspezifischen Installationsverzeichnis und im aktuellen Verzeichnis. Die Suchstrategie kann durch Setzen der Umgebungsvariable CLASSPATH oder mit Hilfe der Option -classpath verändert werden. Sollen nur die Standardbibliotheken des JDK verwendet werden, sind weder CLASSPATH noch -classpath erforderlich. Weitere Informationen zu CLASSPATH-Einstellungen finden Sie in Abschnitt 13.2.2, Seite 305.
» » »
JDK 1.1–6.0 In der Windows-Version des JDK 1.2 ist ein Just-In-Time-Compiler (kurz JIT) enthalten, der standardmäßig aktiviert ist. Der JIT übersetzt zur Laufzeit des Programms häufig benötigte Bytecodes in Maschinencode und beschleunigt so die weitere Ausführung des Programms. Soll der JIT deaktiviert werden, kann die Umgebungsvariable JAVA_COMPILER oder die Systemeigenschaft java.compiler auf den Wert NONE gesetzt werden. Seit dem JDK 1.3 ist der adaptive Just-In-Time-Compiler HotSpot fester Bestandteil der Auslieferung. Er kann mit der Option -Xint aus- und mit -Xmixed angeschaltet werden. Standardmäßig ist er aktiviert. Soll im JDK 1.3 anstelle von HotSpot der JIT des JDK 1.2 verwendet werden, kann dieser mit der Option -Xclassic aktiviert werden. Seit der Version 1.3.1 ist zusätzlich der bis dahin nur separat erhältliche Server-Hotspot im JDK enthalten. Er führt weitergehende (und kostspieligere) Optimierungen aus als der Client-Hotspot und ist vorwiegend für langlaufende Applikationen ohne direkte Benutzeroberfläche gedacht. Er kann mit der Option -server aktiviert werden, während der Client-Hotspot auf -client reagiert.
51.2.3 Optionen Tabelle 51.2: Optionen des JavaInterpreters
Option
Bedeutung
-classpath path
Gibt die Liste der Pfade zur Suche von Klassendateien an. Alternativ kann auch die Abkürzung -cp verwendet werden.
-prof
Aktiviert in Prä-1.2-Versionen des JDK den Profiler im Interpreter, der Informationen über das Laufzeitverhalten der Anwendung in die Datei java.prof schreibt. Ab dem JDK 1.2 wird der Profiler mit der Option -Xprof bzw. -Xrunhpof aktiviert. Genaue Informationen zur Verwendung des Profilers sind in Abschnitt 50.3, Seite 1241 zu finden.
1254
-version
Ausgabe der Versionsnummer
-help
Ausgabe eines kurzen Hilfetextes
appletviewer – der Appletviewer
Option
Bedeutung
-verbose
Gibt bei jedem Laden einer Klasse eine Meldung auf der Console aus. Seit dem JDK 1.3 können wahlweise die Schalter :class, :gc oder :jni angehängt
Kapitel 51 Tabelle 51.2: Optionen des JavaInterpreters (Forts.)
werden. :class entspricht dabei der Voreinstellung, :gc dokumentiert die Garbage-Collector-Aufrufe und :jni zeigt die Verwendung von Native Methods. -verbosegc
Veranlasst den Garbage Collector bei jedem Aufruf zur Ausgabe einer Nachricht.
-DpropName=value
Weist dem Property propName den Wert value zu.
-Xms n
Spezifiziert die Größe des beim Start allozierten Speichers. n ist dabei eine Ganzzahl mit einer der Erweiterungen »k« oder »m«. Die Buchstaben stehen für die Größenordnungen kilo und mega. Die Standardeinstellung ist versions- und betriebssystemabhängig, typische Werte sind 1m oder 2m. Spezifiziert die Größe des maximal allozierbaren Speichers. n ist dabei eine
-Xmx n
Ganzzahl mit einer der Erweiterungen »k« oder »m«. Die Standardeinstellung ist versions- und betriebssystemabhängig, typische Werte sind 16m oder 64m. -enableassertions
Schaltet Assertions an (Abkürzung -ea). Dieser Schalter wird ausführlich in Abschnitt 6.4.1, Seite 144 erklärt.
-disableassertions
Schaltet Assertions aus (Abkürzung -da). Dieser Schalter wird ausführlich in Abschnitt 6.4.1, Seite 144 erklärt.
JDK 1.1–6.0 In den 1.1er Versionen des JDK gab es ein Programm jre, das dazu diente, den Interpreter des Laufzeitsystems zu starten (Java Runtime Environment). Dieses Programm ist in der aktuellen JDK-Version nicht mehr vorhanden, sondern wird durch das Programm java ersetzt. Auch der in früheren Versionen vorhandene debugging-fähige Interpreter java_g existiert seit dem JDK 1.2 nicht mehr.
51.3
appletviewer – der Appletviewer
51.3.1
Aufruf
» » »
appletviewer [ options ] { url }
51.3.2 Beschreibung
1255
Teil IX
appletviewer ist ein Hilfsprogramm zum Ausführen von Applets. Er interpretiert dazu die als Argument übergebenen (und auf HTML-Dokumente zeigenden) URLs und öffnet
Kapitel 51
Hilfsprogramme des JDK
für jedes darin gefundene APPLET-Tag ein Fenster, in dem das zugehörige Applet gestartet wird. Alle übrigen HTML-Befehle werden ignoriert. Details zur Struktur der HTMLDateien und zur Ausführung von Applets finden sich in Kapitel 39, Seite 923.
51.3.3 Optionen Tabelle 51.3: Optionen von appletviewer
Option
Bedeutung
-debug
Startet den Appletviewer unter Kontrolle des Debuggers und erlaubt so die Fehlersuche in Applets.
51.4
jdb – der Debugger
51.4.1 Aufruf jdb [ classfile ]
51.4.2 Beschreibung jdb ist der Debugger des JDK. Er bietet die Möglichkeit, Programme kontrolliert ablaufen zu lassen und dabei Breakpoints zu setzen, im Einzelschrittmodus den nächsten Befehl ausführen zu lassen oder den Inhalt von Variablen oder Objekten zu inspizieren. Man sollte allerdings nicht zu große Erwartungen an jdb stellen, denn das Programm ist ein Kommandozeilendebugger in schönster UNIX-Tradition. Mit den grafischen Debuggern der integrierten Java-Entwicklungsumgebungen hat er nur wenig gemeinsam. Leider ist er nicht nur umständlicher zu bedienen, sondern lässt (insbesondere in Prä-1.2-JDKs) auch einige wichtige Features moderner Debugger vermissen.
» » »
1256
JDK 1.1–6.0 Seit der Version 1.3 besitzt das JDK eine neue Debugging-Architektur. Sie wird als JPDA (Java Platform Debugger Architecture) bezeichnet und erlaubt es, Debugger komplett in Java zu schreiben. Zwar wird nach wie vor kein ausgefeilter GUI-Debugger mit dem JDK ausgeliefert. Doch befindet sich im Unterverzeichnis jpda des Demo-Verzeichnisses des JDK ein Beispielprogramm javadt, dass als Prototyp eines GUI-Debuggers angesehen werden kann. Hinweise zu seiner Verwendung finden sich in der JDK-Dokumentation. Auch integrierte Entwicklungsumgebungen verwenden diese Schnittstelle, um ihre eigenen Debugger einzubinden.
jdb – der Debugger
Kapitel 51
51.4.3 Vorbereitungen Damit ein Programm debuggt werden kann, sollte es mit der Option -g übersetzt werden. Dadurch werden symbolische Informationen in die Klassendatei geschrieben, die der Debugger zur Interpretation von lokalen Variablen benötigt. Beim Aufruf des Debuggers kann die zu untersuchende Klassendatei als Argument angegeben werden: jdb classfile INFO
i
i
i
!
!
!
Damit jdb überhaupt startet, muss ein laufender TCP/IP-Stack vorhanden sein. Dies ist für Anwender unangenehm, die nur eine Wählverbindung zu ihrem Provider haben, denn bei jedem Starten des Debuggers die Verbindung aufzubauen, ist nicht nur umständlich, sondern auch kostspielig. Die übliche Empfehlung im Usenet lautet in diesem Fall, eine Datei hosts anzulegen und den folgenden Eintrag einzufügen:
127.0.0.1
localhost
Unter Windows 95 muss die Datei im Windows-Installationsverzeichnis (typischerweise c:\windows) angelegt werden, meist gibt es dort schon eine Kopiervorlage hosts.sam, die verwendet werden kann. In aktuellen JDKs kann auf das Anlegen der Datei unter Umständen verzichtet werden. Falls der Debugger nicht starten will, kann es sinnvoll sein, zusätzlich die DNS-Konfiguration zu deaktivieren. Dazu ist in der Systemsteuerung im Bereich »Netzwerk« das TCP/IP-Protokoll auszuwählen und nach Klick auf »Eigenschaften« im Registerblatt »DNS-Konfiguration« der Button »DNS deaktivieren« anzuklicken. ACHTUNG Bei dieser Anpassung ist allerdings Vorsicht geboten, denn Veränderungen an den Netzwerkeinstellungen können die Netzwerkanbindung des Rechners unbrauchbar machen. Es empfiehlt sich in jedem Fall, vor der Veränderung der Parameter die alte Konfiguration zu notieren, um sie nötigenfalls wieder restaurieren zu können. Veränderungen sollte sowieso nur derjenige vornehmen, der genau weiß, was er tut.
1257
Teil IX
Nachdem der Debugger gestartet wurde, meldet er sich mit seiner Kommandozeile und ist bereit, Befehle entgegenzunehmen. Die wichtigsten von ihnen werden in Tabelle 51.4, Seite 1258 vorgestellt und kurz erläutert.
Kapitel 51
Tabelle 51.4: Kommandos von jdb
Hilfsprogramme des JDK
Kommando
Bedeutung
?
Liefert eine Übersicht aller Kommandos. Alternativ kann auch das Kommando help verwendet werden.
!!
Wiederholt das letzte Kommando.
load classname
Lädt eine Klassendatei in den Debugger. Falls jdb bereits mit einer Klassendatei als Argument aufgerufen wurde, muss dieses Kommando nicht mehr aufgerufen werden.
run
Startet das geladene Programm. Falls die Klassendatei mit load geladen wurde, müssen zusätzlich der Klassenname und ggfs. weitere Parameter des Programms übergeben werden. Nach Ausführung dieses Programms läuft das Programm bis zum nächsten Breakpoint oder bis es mit dem suspend-Kommando angehalten wird.
quit
Beendet den Debugger. Alternativ kann auch das Kommando exit verwendet werden.
stop in
Setzt einen Breakpoint auf den Anfang einer Methode. Das Kommando erwartet den Namen der Klasse, einen Punkt und den Namen der Methode als Argument. Beispiel: stop in JDBBeispiel.actionPerformed
stop at
Setzt einen Breakpoint auf eine vorgegebene Zeile. Dazu müssen der Name der Klasse, ein Doppelpunkt und die Zeilennummer innerhalb der Quelldatei angegeben werden. Beispiel: stop at JDBBeispiel:48
clear
Löscht einen Breakpoint. Als Argument müssen der Klassenname, gefolgt von einem Doppelpunkt und der Zeilennummer, in der sich der Breakpoint befindet, angegeben werden. Es gibt keine Möglichkeit, eine Liste der aktuellen Breakpoints anzeigen zu lassen.
list
Zeigt den aktuellen Ausschnitt des Quelltextes an, an dem das Programm angehalten wurde. Die als Nächstes auszuführende Zeile wird durch einen Pfeil markiert. Alternativ kann auch der Name einer Methode oder eine Zeilennummer an das Kommando übergeben werden.
step
Nachdem ein Programm angehalten wurde, kann es mit diesem Kommando im Einzelschrittmodus fortgeführt werden. Befindet sich an der Aufrufstelle ein Methodenaufruf, springt das Kommando in die Methode hinein und bleibt bei der ersten ausführbaren Anweisung stehen.
next
Wie step, springt dabei aber nicht in einen Methodenaufruf hinein, sondern führt die Methode als Ganzes aus und bleibt beim nächsten Kommando nach dem Methodenaufruf stehen. Dieses Kommando steht erst ab dem JDK 1.2 zur Verfügung.
step up
Führt die aktuelle Methode bis zum Ende aus.
cont
Führt das Programm nach einem Breakpoint fort. Es läuft dann bis zum nächsten Breakpoint oder bis es mit dem suspend-Kommando angehalten wird.
1258
javadoc – der Dokumentationsgenerator
Kommando
Bedeutung
print
Zeigt den Inhalt der Variablen an, die als Argument übergeben wurde. print verwendet dazu die Methode toString, die in allen Objektvariablen implemen-
Kapitel 51 Tabelle 51.4: Kommandos von jdb (Forts.)
tiert ist. Damit eine Variable angezeigt werden kann, muss sie an der Aufrufstelle sichtbar sein. Es gibt leider keine Möglichkeit, den Inhalt einer Variablen aus dem Debugger heraus zu verändern. dump
Zeigt den Inhalt der Objektvariable, die als Argument übergeben wurde, inklusive aller Membervariablen an. Damit eine Variable angezeigt werden kann, muss sie an der Aufrufstelle sichtbar sein.
locals
Gibt eine Liste aller lokalen Variablen und ihrer aktuellen Inhalte aus.
where
Zeigt einen Stacktrace an.
51.5
javadoc – der Dokumentationsgenerator
51.5.1
Aufruf
javadoc [ options ] { package | sourcefile }
51.5.2 Beschreibung javadoc ist ein Programm, das aus Java-Quelltexten Dokumentationen im HTML-Format erstellt. Dazu verwendet es die öffentlichen Klassen-, Interface- und Methodendeklarationen und fügt zusätzliche Informationen aus eventuell vorhandenen Dokumentationskommentaren hinzu. Zu jeder Klassendatei xyz.java wird eine HTML-Seite xyz.html generiert, die über verschiedene Querverweise mit den anderen Seiten desselben Projekts in Verbindung steht. Zusätzlich generiert javadoc diverse Index- und Hilfsdateien, die das Navigieren in den Dokumentationsdateien erleichtern.
51.5.3 Dokumentationskommentare
1259
Teil IX
Bereits ohne zusätzliche Informationen erstellt javadoc aus dem Quelltext eine brauchbare Beschreibung aller Klassen und Interfaces. Durch das Einfügen von Dokumentationskommentaren kann die Ausgabe zusätzlich bereichert werden. Ein Dokumentationskommentar beginnt mit /** und endet mit */ und ähnelt damit einem gewöhnlichen Kommentar. Er muss im Quelltext immer unmittelbar vor dem zu dokumentierenden Item platziert werden (einer Klassendefinition, einer Methode oder einer Instanzvariable). Er kann aus mehreren Zeilen bestehen. Die erste Zeile des Kommentars wird später als Kurzbeschreibung verwendet.
Kapitel 51
i
Hilfsprogramme des JDK
i
i
INFO Zur Erhöhung der Übersichtlichkeit darf am Anfang jeder Zeile ein Sternchen stehen, es wird später ignoriert. Innerhalb der Dokumentationskommentare dürfen neben normalem Text auch HTML-Tags vorkommen. Sie werden unverändert in die Dokumentation übernommen und erlauben es damit, bereits im Quelltext die Formatierung der späteren Dokumentation vorzugeben. Die Tags und sollten möglichst nicht verwendet werden, da sie von javadoc selbst zur Strukturierung der Ausgabe verwendet werden.
javadoc erkennt des Weiteren markierte Absätze innerhalb von Dokumentationskommentaren. Die Markierung muss mit dem Zeichen @ beginnen und – abgesehen von Leerzeichen – am Anfang der Zeile stehen. Jede Markierung leitet einen eigenen Abschnitt innerhalb der Beschreibung ein, alle Markierungen eines Typs müssen hintereinanderstehen. Tabelle 51.5, Seite 1260 gibt eine Übersicht der wichtigsten Markierungen und beschreibt, wie sie verwendet werden. Tabelle 51.5: Markierungen in Dokumentationskommentaren
Markierung und Parameter
Dokumentation
Verwendung in
@author name
Erzeugt einen Autoreneintrag.
Klasse, Interface
@version version
Erzeugt einen Versionseintrag. Darf
Klasse, Interface
höchstens einmal je Klasse oder Interface verwendet werden. @since jdk-version
Beschreibt, seit wann das beschriebene
Klasse, Interface
Feature existiert. @see reference
Erzeugt einen Querverweis auf eine an-
Klasse, Interface,
dere Klasse, Methode oder einen beliebi-
Instanzvariable,
gen anderen Teil der Dokumentation.
Methode
Gültige Verweise sind: " @see java.util.Vector " @see Vector " @see Vector#addElement " @see Spez @param name description
Parameterbeschreibung einer Methode
Methode
@return description
Beschreibung des Rückgabewerts einer
Methode
Methode @exception classname
Beschreibung einer Ausnahme, die von
description
dieser Methode ausgelöst wird
@deprecated description
Markiert eine veraltete Methode, die zukünftig nicht mehr verwendet werden sollte.
1260
Methode Methode
javadoc – der Dokumentationsgenerator
Kapitel 51
51.5.4 Aufruf von javadoc Um javadoc aufzurufen, sollte zunächst in das Verzeichnis gewechselt werden, in dem sich die zu dokumentierenden Quelldateien befinden. Anschließend kann durch Eingabe des folgenden Kommandos die Erzeugung der Dokumentationen für alle Quelldateien gestartet werden: javadoc *.java Das Programm erzeugt eine Reihe von HMTL-Dateien, die die zu den jeweiligen Quellen korrespondierenden Dokumentationen enthalten. Zusätzlich werden eine Reihe von Hilfsdateien zur Darstellung und Indexierung der Dokumentationsdateien erstellt. Alternativ zum Aufruf mit einer Reihe von Quelldateien kann javadoc auch mit Paketnamen als Argument aufgerufen werden. Wenn der Klassenpfad korrekt gesetzt ist, spielt es dann keine Rolle mehr, aus welchem Verzeichnis das Programm gestartet wird, denn die Klassendateien werden automatisch korrekt gefunden. Wenn nicht per Schalter -d etwas anderes angegeben wurde, erzeugt javadoc die Dokumentationsdateien im aktuellen Verzeichnis. JDK 1.1–6.0 In den JDKs 1.0 und 1.1 erzeugt javadoc zwei unterschiedliche Arten von Standardverweisen. Bei der ersten Art werden Grafiken eingebunden, um Überschriften für die verschiedenen Dokumentationsabschnitte zu generieren. Damit diese im Browser korrekt angezeigt werden, muss ein Unterverzeichnis images angelegt und die erforderlichen Grafikdateien dorthin kopiert werden (beispielsweise aus \jdk1.1.2\docs\api\images). Ab dem JDK 1.2 werden dagegen keine Grafikdateien mehr benötigt.
» » »
Die zweite Art von Verweisen ist die auf Klassen oder Methoden der Java-Klassenbibliothek (z.B. java.lang.String). Im JDK 1.1 geht javadoc davon aus, dass sich die Dokumentationsdateien zu allen externen Klassen und Methoden im selben Verzeichnis wie die zu erstellenden Dokumentationsdateien befinden. Damit also externe Verweise funktionieren, müsste man die HTML-Files der Originaldokumentation des JDK in sein eigenes Dokumentationsverzeichnis kopieren, was sicherlich nicht immer praktikabel ist. JDK 1.1–6.0 Mit dem JDK 1.2 wurde die Option -link eingeführt, mit der ein Pfad auf die Dokumentation der Standardklassen angegeben werden kann. Der Pfad muss als URL angegeben werden und das Verzeichnis beschreiben, in dem die Datei package-list der Dokumentationsdateien liegt. In der Dokumentation zu javadoc gibt SUN folgendes Beispiel an:
» » »
javadoc -link http://java.sun.com/products/jdk/1.2/docs/api ...
1261
Teil IX
Dadurch wird bei Standardklassennamen auf die auf dem SUN-Server liegende Originaldokumentation verwiesen. Soll dagegen auf eine lokal installierte Dokumentation ver-
Kapitel 51
Hilfsprogramme des JDK
wiesen werden, kann auch ein file-URL angegeben werden. Liegt die Dokumentation des JDK beispielsweise im Verzeichnis c:\jdk1.6\docs (und somit die API-Dokumentation im Verzeichnis c:\jdk1.6\docs\api), kann javadoc wie folgt aufgerufen werden: javadoc -link file:///c:/jdk1.6/docs/api ...
51.5.5 Optionen Tabelle 51.6: Einige Optionen von javadoc
Option
Bedeutung
-classpath path
Gibt die Liste der Pfade zur Suche von Klassendateien an.
-public
Nur Elemente des Typs public werden dokumentiert.
-protected
Elemente des Typs public und protected werden dokumentiert (das ist die Voreinstellung).
-package
Elemente des Typs package, public und protected werden dokumentiert.
-private
Alle Elemente werden dokumentiert.
-version
Versionseintrag generieren
-author
Autoreneintrag generieren
-sourcepath path
Pfad mit den Quelldateien
-d directory
Verzeichnis, in dem die generierten Dokumentationsdateien abgelegt werden. Standardmäßig werden sie im aktuellen Verzeichnis angelegt.
-verbose
» » »
1262
Ausgabe zusätzlicher Meldungen während der Dokumentationserstellung
JDK 1.1–6.0 Neben den hier erwähnten Schaltern kennt javadoc (insbesondere seit dem JDK 1.2) eine ganze Reihe zusätzlicher Optionen, mit denen die Codeerzeugung beeinflusst werden kann. Bemerkenswert ist dabei auf jeden Fall das Konzept der Doclets, mit denen das Verhalten von javadoc und das Aussehen der generierten Dokumentationsdateien weitgehend verändert werden kann. Doclets sind Zusatzmodule für javadoc, deren Aufgabe es ist, auf der Basis des Doclet-APIs und der geparsten Quelltexte die Ausgabedateien zu erzeugen. Ob der generierte Code dabei im HTML-, RTF- oder einem anderen Format erzeugt wird, spielt keine Rolle. Das seit dem JDK 1.2 ausgelieferte Standard-Doclet erzeugt die Dokumentationsdateien im HTML-Format.
jar – das Archivierungswerkzeug
51.6
Kapitel 51
jar – das Archivierungswerkzeug
51.6.1 Aufruf jar [ commands ] archive { input-file }
51.6.2 Beschreibung jar ist ein Archivierungsprogramm, das Dateien und komplette Unterverzeichnisse komprimieren und in eine gemeinsame Archivdatei packen kann. Es verwendet ein Kompressionsformat, das den diversen zip-/unzip-Programmen ähnelt, und wird analog dem UNIX-Tool tar bedient. Ein Vorteil von jar ist seine Portabilität, die sowohl für das erzeugte Dateiformat als auch für das (in Java geschriebene) Programm selbst gilt. Wichtigster Einsatzzweck von jar ist es, alle zu einem Java-Programm gehörenden Dateien (.class-, Image-, Sound-Dateien usw.) in einer einzigen Datei zusammenzufassen. Neben den organisatorischen Vorteilen, die diese Möglichkeit zweifellos bietet, wurden jar-Dateien vor allem eingeführt, um das Laden von Applets aus dem Internet zu beschleunigen. Dadurch müssen Web-Browser nämlich nicht mehr für jede einzelne Datei, die in einem Applet benötigt wird, eine eigene GET-Transaktion absetzen, sondern können alle erforderlichen Files in einem Schritt laden. Die Ladezeit von Applets wird dadurch drastisch verringert, insbesondere, wenn viele kleine Dateien benötigt werden.
51.6.3 Kommandos Im Gegensatz zu den übrigen Programmen, die in diesem Kapitel vorgestellt wurden, kennt jar keine Optionsparameter, sondern erwartet Kommandos an ihrer Stelle. Ein Kommando besteht aus einem Buchstaben, der ohne das Präfix - angegeben wird. Sollen mehrere Kommandos kombiniert werden, so werden die zugehörigen Buchstaben ohne Lücken direkt hintereinander geschrieben. Diese abweichende Syntax stammt von dem Kommando tar, das auf UNIX-Rechnern zur Archivierung von Dateien eingesetzt wird. Tabelle 51.7, Seite 1263 gibt eine Übersicht der verfügbaren Kommandos. Kommando
Bedeutung
c
Erzeugt eine neue Archivdatei (create). Kann nicht zusammen mit t oder x
Tabelle 51.7: Kommandos von jar
verwendet werden. t
Gibt das Inhaltsverzeichnis der Archivdatei aus (table of contents). Kann nicht zusammen mit c oder x verwendet werden.
x file
Extrahiert eine oder mehrere Dateien aus dem Archiv (extract). Kann nicht zusammen mit c oder t verwendet werden.
u
Fügt die angegebenen Dateien in die bestehende Archivdatei ein.
f
Gibt an, dass der nächste Parameter der Name der Archivdatei ist. Wird das Kommando f nicht angegeben, verwendet jar stattdessen die Standardein- und
1263
Teil IX
-ausgabe.
Kapitel 51 Tabelle 51.7: Kommandos von jar (Forts.)
Hilfsprogramme des JDK
Kommando
Bedeutung
v
Gibt zusätzliche Informationen aus (verbose). Kann zusätzlich zu einem der anderen Kommandos verwendet werden. Die Dateien werden ohne Kompression gespeichert.
0
Sollen beispielsweise alle .java-Dateien des aktuellen Verzeichnisses in ein Archiv mit der Bezeichnung xyz.jar gepackt werden, so kann dazu folgendes Kommando verwendet werden: jar cf xyz.jar *.java Das Inhaltsverzeichnis des Archivs kann folgendermaßen abgerufen werden: jar tf xyz.jar Etwas ausführlicher geht es mit: jar tvf xyz.jar Um die Datei Test.java aus dem Archiv zu extrahieren, kann das folgende Kommando verwendet werden (das natürlich auch ohne den Zusatz v funktioniert): jar xvf xyz.jar Test.java
51.6.4 Verwendung von jar-Dateien in Applets Die Verwendung von jar-Dateien in Applets erfolgt mit Hilfe des ARCHIVE-Parameters des APPLET-Tags (siehe Kapitel 39, Seite 923). Soll beispielsweise das »Hello, World«-Programm HWApplet.java aus Listing 39.7, Seite 934 aus einem jar-Archiv hello.jar ausgeführt werden, so ist in den folgenden Schritten vorzugehen. Zunächst werden die Dateien HWApplet.class, hello.au und world.au in ein jar-Archiv gepackt: jar cvf hello.jar HWApplet.class hello.au world.au Anschließend wird die HTML-Datei HWApplet.html zum Aufruf des Applets erstellt: Listing 51.1: HTML mit ARCHIVE-Tag
1264
001 002 003 004 005 006 007 008 009 010 011
HWApplet HWApplet
Kapitel 51 Listing 51.1: HTML mit ARCHIVE-Tag (Forts.)
Nun kann das Applet wie bisher gestartet werden, benötigt aber zum Laden aller Dateien nur noch eine einzige HTTP-Transaktion.
i
INFO
i
i
Leider unterstützen noch nicht alle Browser das jar-Format, so dass seine Verwendung zum heutigen Zeitpunkt überlegt sein will. Für die nahe Zukunft ist es aber ein wichtiger Schritt zur Verbesserung der Ladezeiten von Applets.
51.7
javap – der Disassembler
51.7.1
Aufruf
javap [ options ] classname
51.7.2 Beschreibung Der Disassembler javap liest den übersetzten Code einer Klasse und gibt Informationen darüber auf der Standardausgabe aus. Dabei können entweder nur Informationen über Variablen und Methoden oder der komplette Bytecode der Klasse ausgegeben werden. javap ist nicht in der Lage, den Java-Quellcode einer Klassendatei wieder herzustellen. Beim Aufruf ist der Name der Klasse ohne die Erweiterung .class anzugeben, also beispielsweise: javap -c java.lang.String
51.7.3 Optionen Option
Bedeutung
-classpath path
Gibt die Liste der Pfade zur Suche von Klassendateien an.
-public
Nur die Klassenelemente des Typs public werden angezeigt.
-protected
Nur die Klassenelemente des Typs public und protected werden angezeigt.
-package
Die Klassenelemente des Typs public, protected und die Elemente mit
Tabelle 51.8: Optionen von javap
Alle Klassenelemente werden angezeigt.
-c
Disassemblieren des Codes
-s
Ausgabe der Methodensignaturen
-l
Ausgabe von Zeilennummern
1265
Teil IX
Paketsichtbarkeit werden angezeigt. Das ist die Voreinstellung. -private
Kapitel 51
Hilfsprogramme des JDK
51.8
serialver – Zugriff auf die serialVersionUID
51.8.1 Aufruf serialver -show | classname
51.8.2 Beschreibung Mit serialver kann auf die serialVersionUID einer Klasse zugegriffen werden (siehe Abschnitt 41.2.1, Seite 971). Wenn das Programm mit einem Klassennamen aufgerufen wird, gibt es die serialVersionUID auf der Console aus. Wird es mit der Option -show (und ohne Klassennamen) aufgerufen, erfolgt der Aufruf mit einer einfachen grafischen Oberfläche.
51.8.3 Optionen Tabelle 51.9: Optionen von serialver
Option
Bedeutung
-show
Aufruf mit grafischer Oberfläche
51.9
keytool – Verwaltung von kryptografischen Schlüsseln
51.9.1 Aufruf keytool [ commands ]
51.9.2 Beschreibung keytool ist ein Hilfsprogramm zum Erzeugen und Verwalten von Schlüsseln für PublicKey-Kryptosysteme. Das Programm bietet eine große Anzahl von Funktionen, von denen die wichtigsten bereits in Abschnitt 48.1.6, Seite 1180 erklärt wurden. Weitere Details können in der Tool-Dokumentation des JDK nachgelesen werden.
51.10 policytool – Bearbeiten von Policy-Dateien 51.10.1 Aufruf policytool
1266
jarsigner – Signieren von Archiven
Kapitel 51
51.10.2 Beschreibung policytool ist ein interaktives Programm zum Bearbeiten von Policy-Dateien (siehe Abschnitt 48.3.4, Seite 1196). Mit seiner Hilfe können neue Policy-Dateien erstellt oder vorhandene geändert werden. Es versucht zunächst, die benutzerbezogene Policy-Datei zu öffnen (unter Windows c:\windows\.java.policy, unter UNIX $HOME/.java.policy), kann aber auch für beliebige andere Policy-Dateien verwendet werden. In einer Listbox zeigt das Programm alle vorhandenen Einträge an. Sie können editiert oder gelöscht oder es können neue hinzugefügt werden. Wenn ein Policy-Eintrag bearbeitet wird, werden alle Berechtigungen auf dem Bildschirm angezeigt. Auch sie können bearbeitet, gelöscht oder hinzugefügt werden. Beim Hinzufügen können die möglichen Varianten – ohne die Gefahr von Tippfehlern, wie sie beim manuellen Editieren leicht entstehen können – bequem im Dialog ausgewählt werden. Nach dem Bearbeiten können die Änderungen in die ursprüngliche oder eine andere Policy-Datei geschrieben werden.
51.11
jarsigner – Signieren von Archiven
51.11.1 Aufruf jarsigner [ options ] jarfile alias
51.11.2 Beschreibung jarsigner dient zum Zugriff auf die Signatur von jar-Archiven. Dazu wird der Schlüssel verwendet, der unter dem als Argument angegebenen Alias in der Schlüsseldatenbank gespeichert ist. Seine wichtigste Anwendung, das Signieren von Archiven, wurde bereits in Abschnitt 48.3.2, Seite 1193 erläutert.
51.11.3 Optionen Option
Bedeutung
-signedjar file
Name des beim Signieren erzeugten Archivs
-keystore url
Name der Schlüsseldatenbank
-storetype type
Typ der Schlüsseldatenbank
-storepass pass
Passwort der Schlüsseldatenbank (falls nicht angegeben, wird es interaktiv
Tabelle 51.10: Optionen von jarsigner
abgefragt) -keypass pass
Passwort des Schlüssels (falls nicht angegeben, wird es interaktiv abgefragt)
-verify
Wird diese Option angegeben, so wird das Archiv nicht signiert, sondern
1267
Teil IX
verifiziert
Kapitel 51
Hilfsprogramme des JDK
51.12 rmic – Erzeugen von RMI-Stubs und -Skeletons 51.12.1 Aufruf rmic [ options ] classname
51.12.2 Beschreibung rmic dient dazu, aus der Implementierung eines Remote-Objekts die zugehörigen RMIStubs und -Skeletons zu erzeugen. Seine Anwendung wurde in Abschnitt 47.2.3, Seite 1158 erläutert.
51.12.3 Optionen Tabelle 51.11: Optionen von rmic
Option
Bedeutung
-classpath path
Klassenpfad zur Suche der angegebenen Klasse
-d directory
Wurzel des Zielverzeichnisses für die zu erzeugenden Klassen
-g
Erzeugen von Debug-Code
-keepgenerated
Die temporären .java-Dateien werden nicht gelöscht.
-vcompat
Erzeugt Stubs und Skeletons, die zu den Stub-Protokollen 1.1 und 1.2 kompatibel sind (Voreinstellung).
-v1.1
Erzeugt Stubs und Skeletons, die zum Stub-Protokoll 1.1 kompatibel sind.
-v1.2
Erzeugt Stubs und Skeletons, die zum Stub-Protokoll 1.2 kompatibel sind.
51.13 rmiregistry – der RMI-Namensservice 51.13.1 Aufruf rmiregistry [port]
51.13.2 Beschreibung rmiregistry ist der Standard-Namensservice für RMI. Er dient als Hintergrund dazu, RMI-Objekte zu registrieren und auf Anfrage Clients zur Verfügung zu stellen. rmiregistry wurde in Abschnitt 47.2.4, Seite 1160 erläutert. Seine einzige Option ist die TCP-PortNummer. Wird sie nicht angegeben, gilt 1099 als Voreinstellung.
1268
Hilfsprogramme des JDK
Zusammenfassung In diesem Kapitel wurden folgende Themen behandelt: " Aufruf des Java-Compilers javac zum Übersetzen von .java- in .class-Dateien " Aufruf des Java-Interpreters java zum Ausführen von Java-Applikationen " Die Bedeutung der CLASSPATH-Umgebungsvariable " Aufruf des Applet-Viewers appletviewer zum Ausführen von Java-Applets " Vorbereiten eines Programms zum Debuggen und Aufruf des Debuggers jdb " Die wichtigsten Kommandos des Debuggers " Erzeugen von Quelltextdokumentationen mit Hilfe von javadoc " Die Struktur von Dokumentationskommentaren und die Markierungen @author, @version, @since, @see, @param, @return, @exception und @deprecated " Die Bedienung des Archivierungsprogramms jar " Verwendung von jar-Dateien in Applets " Aufruf des Disassemblers javap " Zugriff auf die serialVersionUID mit serialver " Verwaltung von kryptografischen Schlüsseln mit keytool " Verwendung von policytool zum Bearbeiten von Policy-Dateien " Signieren von Archiven mit jarsigner " Erzeugen von RMI-Stubs und -Skeletons mit rmic
1269
Teil IX
" Der RMI-Namensservice rmiregistry
J2SE Software License
Sun Microsystems, Inc. Binary Code License Agreement for the JAVA VERSION 6
SE
DEVELOPMENT
KIT
(JDK),
SUN MICROSYSTEMS, INC. ("SUN") IS WILLING TO LICENSE THE SOFTWARE IDENTIFIED BELOW TO YOU ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS BINARY CODE LICENSE AGREEMENT AND SUPPLEMENTAL LICENSE TERMS (COLLECTIVELY "AGREEMENT"). PLEASE READ THE AGREEMENT CAREFULLY. BY DOWNLOADING OR INSTALLING THIS SOFTWARE, YOU ACCEPT THE TERMS OF THE AGREEMENT. INDICATE ACCEPTANCE BY SELECTING THE "ACCEPT" BUTTON AT THE BOTTOM OF THE AGREEMENT. IF YOU ARE NOT WILLING TO BE BOUND BY ALL THE TERMS, SELECT THE "DECLINE" BUTTON AT THE BOTTOM OF THE AGREEMENT AND THE DOWNLOAD OR INSTALL PROCESS WILL NOT CONTINUE. 1. DEFINITIONS. "Software" means the identified above in binary form, any other machine readable materials (including, but not limited to, libraries, source files, header files, and data files), any updates or error corrections provided by Sun, and any user manuals, programming guides and other documentation provided to you by Sun under this Agreement. "Programs" mean Java applets and applications intended to run on the Java Platform, Standard Edition (Java SE) on Java-enabled general purpose desktop computers and servers. 2. LICENSE TO USE. Subject to the terms and conditions of this Agreement, including, but not limited to the Java
J2SE Software License
Technology Restrictions of the Supplemental License Terms, Sun grants you a non-exclusive, non-transferable, limited license without license fees to reproduce and use internally Software complete and unmodified for the sole purpose of running Programs. Additional licenses for developers and/or publishers are granted in the Supplemental License Terms. 3. RESTRICTIONS. Software is confidential and copyrighted. Title to Software and all associated intellectual property rights is retained by Sun and/or its licensors. Unless enforcement is prohibited by applicable law, you may not modify, decompile, or reverse engineer Software. You acknowledge that Licensed Software is not designed or intended for use in the design, construction, operation or maintenance of any nuclear facility. Sun Microsystems, Inc. disclaims any express or implied warranty of fitness for such uses. No right, title or interest in or to any trademark, service mark, logo or trade name of Sun or its licensors is granted under this Agreement. Additional restrictions for developers and/or publishers licenses are set forth in the Supplemental License Terms. 4. LIMITED WARRANTY. Sun warrants to you that for a period of ninety (90) days from the date of purchase, as evidenced by a copy of the receipt, the media on which Software is furnished (if any) will be free of defects in materials and workmanship under normal use. Except for the foregoing, Software is provided "AS IS". Your exclusive remedy and Sun's entire liability under this limited warranty will be at Sun's option to replace Software media or refund the fee paid for Software. Any implied warranties on the Software are limited to 90 days. Some states do not allow limitations on duration of an implied warranty, so the above may not apply to you. This limited warranty gives you specific legal rights. You may have others, which vary from state to state. 5. DISCLAIMER OF WARRANTY. UNLESS SPECIFIED IN THIS AGREEMENT, ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT ARE DISCLAIMED, EXCEPT TO THE EXTENT THAT THESE DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. 6. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF OR RELATED TO THE USE OF OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. In no event will Sun's liability to you, whether in contract, tort (including negligence), or otherwise, exceed the amount paid by you for Software under this Agreement. The foregoing limitations will apply even if the above stated warranty fails of its essential
1272
J2SE Software License
purpose. Some states do not allow the exclusion of incidental or consequential damages, so some of the terms above may not be applicable to you. 7. TERMINATION. This Agreement is effective until terminated. You may terminate this Agreement at any time by destroying all copies of Software. This Agreement will terminate immediately without notice from Sun if you fail to comply with any provision of this Agreement. Either party may terminate this Agreement immediately should any Software become, or in either party's opinion be likely to become, the subject of a claim of infringement of any intellectual property right. Upon Termination, you must destroy all copies of Software. 8. EXPORT REGULATIONS. All Software and technical data delivered under this Agreement are subject to US export control laws and may be subject to export or import regulations in other countries. You agree to comply strictly with all such laws and regulations and acknowledge that you have the responsibility to obtain such licenses to export, re-export, or import as may be required after delivery to you. 9. TRADEMARKS AND LOGOS. You acknowledge and agree as between you and Sun that Sun owns the SUN, SOLARIS, JAVA, JINI, FORTE, and iPLANET trademarks and all SUN, SOLARIS, JAVA, JINI, FORTE, and iPLANET-related trademarks, service marks, logos and other brand designations ("Sun Marks"), and you agree to comply with the Sun Trademark and Logo Usage Requirements currently located at http://www.sun.com/ policies/trademarks. Any use you make of the Sun Marks inures to Sun's benefit. 10. U.S. GOVERNMENT RESTRICTED RIGHTS. If Software is being acquired by or on behalf of the U.S. Government or by a U.S. Government prime contractor or subcontractor (at any tier), then the Government's rights in Software and accompanying documentation will be only as set forth in this Agreement; this is in accordance with 48 CFR 227.7201 through 227.7202-4 (for Department of Defense (DOD) acquisitions) and with 48 CFR 2.101 and 12.212 (for non-DOD acquisitions). 11. GOVERNING LAW. Any action related to this Agreement will be governed by California law and controlling U.S. federal law. No choice of law rules of any jurisdiction will apply. 12. SEVERABILITY. If any provision of this Agreement is held to be unenforceable, this Agreement will remain in effect with the provision omitted, unless omission would frustrate the intent of the parties, in which case this Agreement will immediately terminate. 13. INTEGRATION. This Agreement is the entire agreement between you and Sun relating to its subject matter. It supersedes all prior or contemporaneous oral or written communications, proposals, representations and warranties and prevails over any conflicting or additional terms of any quote, order, acknowledgment, or other communication between the parties relating to its subject matter during the term of this Agreement.
1273
J2SE Software License
No modification of this Agreement will be binding, unless in writing and signed by an authorized representative of each party. SUPPLEMENTAL LICENSE TERMS These Supplemental License Terms add to or modify the terms of the Binary Code License Agreement. Capitalized terms not defined in these Supplemental Terms shall have the same meanings ascribed to them in the Binary Code License Agreement.
These Supplemental Terms shall supersede any inconsistent or conflicting terms in the Binary Code License Agreement, or in any license contained within the Software. A. Software Internal Use and Development License Grant. Subject to the terms and conditions of this Agreement and restrictions and exceptions set forth in the Software "README" file incorporated herein by reference, including, but not limited to the Java Technology Restrictions of these Supplemental Terms, Sun grants you a non-exclusive, non-transferable, limited license without fees to reproduce internally and use internally the Software complete and unmodified for the purpose of designing, developing, and testing your Programs. B. License to Distribute Software. Subject to the terms and conditions of this Agreement and restrictions and exceptions set forth in the Software README file, including, but not limited to the Java Technology Restrictions of these Supplemental Terms, Sun grants you a non-exclusive, non-transferable, limited license without fees to reproduce and distribute the Software, provided that (i) you distribute the Software complete and unmodified and only bundled as part of, and for the sole purpose of running, your Programs, (ii) the Programs add significant and primary functionality to the Software, (iii) you do not distribute additional software intended to replace any component(s) of the Software, (iv) you do not remove or alter any proprietary legends or notices contained in the Software, (v) you only distribute the Software subject to a license agreement that protects Sun's interests consistent with the terms contained in this Agreement, and (vi) you agree to defend and indemnify Sun and its licensors from and against any damages, costs, liabilities, settlement amounts and/or expenses (including attorneys' fees) incurred in connection with any claim, lawsuit or action by any third party that arises or results from the use or distribution of any and all Programs and/or Software. C. License to Distribute Redistributables. Subject to the terms and conditions of this Agreement and restrictions and exceptions set forth in the Software README file, including but not limited to the Java Technology Restrictions of these Supplemental Terms, Sun grants you a non-exclusive, non-transferable, limited license without fees to reproduce and distribute those files specifically identified as redistributable in the Software "README" file ("Redistributables") provided that: (i) you distribute the Redistribut-
1274
J2SE Software License
ables complete and unmodified, and only bundled as part of Programs, (ii) the Programs add significant and primary functionality to the Redistributables, (iii) you do not distribute additional software intended to supersede any component(s) of the Redistributables (unless otherwise specified in the applicable README file), (iv) you do not remove or alter any proprietary legends or notices contained in or on the Redistributables, (v) you only distribute the Redistributables pursuant to a license agreement that protects Sun's interests consistent with the terms contained in the Agreement, (vi) you agree to defend and indemnify Sun and its licensors from and against any damages, costs, liabilities, settlement amounts and/or expenses (including attorneys' fees) incurred in connection with any claim, lawsuit or action by any third party that arises or results from the use or distribution of any and all Programs and/or Software. D. Java Technology Restrictions. You may not create, modify, or change the behavior of, or authorize your licensees to create, modify, or change the behavior of, classes, interfaces, or subpackages that are in any way identified as "java", "javax", "sun" or similar convention as specified by Sun in any naming convention designation. E. Distribution by Publishers. This section pertains to your distribution of the Software with your printed book or magazine (as those terms are commonly used in the industry) relating to Java technology ("Publication"). Subject to and conditioned upon your compliance with the restrictions and obligations contained in the Agreement, in addition to the license granted in Paragraph 1 above, Sun hereby grants to you a non-exclusive, nontransferable limited right to reproduce complete and unmodified copies of the Software on electronic media (the "Media") for the sole purpose of inclusion and distribution with your Publication(s), subject to the following terms: (i) You may not distribute the Software on a stand-alone basis; it must be distributed with your Publication(s); (ii) You are responsible for downloading the Software from the applicable Sun web site; (iii) You must refer to the Software as JavaTM SE Development Kit 6; (iv) The Software must be reproduced in its entirety and without any modification whatsoever (including, without limitation, the Binary Code License and Supplemental License Terms accompanying the Software and proprietary rights notices contained in the Software); (v) The Media label shall include the following information: Copyright 2006, Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. Sun, Sun Microsystems, the Sun logo, Solaris, Java, the Java Coffee Cup logo, J2SE, and all trademarks and logos based on Java are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries. This information must be placed on the Media label in such a manner as to only apply to the Sun Software; (vi) You must clearly identify the Software as Sun's product on the Media holder or Media label, and you may not state or imply that Sun is responsible for any third-party software contained on the Media; (vii) You may not include any third party software on the Media which is intended to be a replacement or substitute for the Software; (viii) You shall indemnify Sun for all damages arising from your failure to comply with the requirements of this Agreement. In addition, you shall defend, at your expense, any and all claims brought against Sun by third parties, and shall pay all
1275
J2SE Software License
damages awarded by a court of competent jurisdiction, or such settlement amount negotiated by you, arising out of or in connection with your use, reproduction or distribution of the Software and/or the Publication. Your obligation to provide indemnification under this section shall arise provided that Sun: (a) provides you prompt notice of the claim; (b) gives you sole control of the defense and settlement of the claim; (c) provides you, at your expense, with all available information, assistance and authority to defend; and (d) has not compromised or settled such claim without your prior written consent; and (ix) You shall provide Sun with a written notice for each Publication; such notice shall include the following information: (1) title of Publication, (2) author(s), (3) date of Publication, and (4) ISBN or ISSN numbers. Such notice shall be sent to Sun Microsystems, Inc., 4150 Network Circle, M/S USCA12-110, Santa Clara, California 95054, U.S.A, Attention: Contracts Administration. F. Source Code. Software may contain source code that, unless expressly licensed for other purposes, is provided solely for reference purposes pursuant to the terms of this Agreement. Source code may not be redistributed unless expressly provided for in this Agreement. G. Third Party Code. Additional copyright notices and license terms applicable to portions of the Software are set forth in the THIRDPARTYLICENSEREADME.txt file. In addition to any terms and conditions of any third party opensource/freeware license identified in the THIRDPARTYLICENSEREADME.txt file, the disclaimer of warranty and limitation of liability provisions in paragraphs 5 and 6 of the Binary Code License Agreement shall apply to all Software in this distribution. H. Termination for Infringement. Either party may terminate this Agreement immediately should any Software become, or in either party's opinion be likely to become, the subject of a claim of infringement of any intellectual property right. I. Installation and Auto-Update. The Software's installation and auto-update processes transmit a limited amount of data to Sun (or its service provider) about those specific processes to help Sun understand and optimize them. Sun does not associate the data with personally identifiable information. You can find more information about the data Sun collects at http://java.com/data/. For inquiries please contact: Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, California 95054, U.S.A.
1276
Abbildungsverzeichnis
Abbildung 2.1 Abbildung 2.2 Abbildung 2.3 Abbildung 2.4 Abbildung 2.5 Abbildung 3.1 Abbildung 3.2 Abbildung 3.3 Abbildung 3.4 Abbildung 4.1 Abbildung 7.1 Abbildung 10.1 Abbildung 10.2 Abbildung 10.3 Abbildung 10.4 Abbildung 10.5 Abbildung 10.6 Abbildung 10.7 Abbildung 10.8 Abbildung 11.1 Abbildung 11.2 Abbildung 11.3
Ist das JDK korrekt installiert? . . . . 59 Hello.java im Windows-Notepad . 61 Übersetzen von Hello.java . . . . . . . . 62 Ausführen von Hello . . . . . . . . . . . . . . 63 Eine Verknüpfung auf dem Windows-Desktop . . . . . . . . . . . . . . . . . . . . . 71 Die API-Dokumentation des JDK . 83 Die HTML-Ausgabe des Buchs . . . 88 UML-Notation für Klassen und Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 UML-Notation für Beziehungen zwischen Klassen und Interfaces . 91 Konvertierungen auf primitiven Datentypen . . . . . . . . . . . . . . . . . . . . . . . . 110 Vererbungshierarchie für Transportmittel . . . . . . . . . . . . . . . . . . . 159 Klassendiagramm einer FactoryMethode . . . . . . . . . . . . . . . . . . . . . . . . . . . 237 Klassendiagramm einer FactoryKlasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239 Klassendiagramm einer abstrakten Factory . . . . . . . . . . . . . . . . 241 Klassendiagramm eines Iterators 245 Klassendiagramm eines Delegate 248 Klassendiagramm eines Composite . . . . . . . . . . . . . . . . . . . . . . . . . 251 Klassendiagramm eines Visitors . 254 Klassendiagramm eines Observer 258 Ein Aufruf von substring(begin, end) . . . . . . . . . . . . . 267 Der Aufruf von regionMatches . . 270 Das Interface CharSequence . . . . . 277
Abbildungsverzeichnis
Abbildung 13.1 Abbildung 13.2 Abbildung 13.3 Abbildung 13.4 Abbildung 23.1 Abbildung 23.2 Abbildung 23.3 Abbildung 23.4 Abbildung 23.5 Abbildung 23.6 Abbildung 23.7 Abbildung 23.8 Abbildung 23.9 Abbildung 23.10 Abbildung 23.11 Abbildung 24.1 Abbildung 24.2 Abbildung 24.3 Abbildung 24.4 Abbildung 24.5 Abbildung 24.6 Abbildung 24.7 Abbildung 25.1 Abbildung 25.2 Abbildung 26.1 Abbildung 26.2 Abbildung 26.3 Abbildung 27.1 Abbildung 27.2 Abbildung 27.3 Abbildung 27.4 Abbildung 28.1 Abbildung 28.2 Abbildung 28.3 Abbildung 29.1 Abbildung 29.2 Abbildung 29.3 Abbildung 29.4 Abbildung 29.5 Abbildung 30.1 Abbildung 30.2 Abbildung 30.3
1278
Der Entwicklungszyklus in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Plattformübergreifende Entwicklung in Java . . . . . . . . . . . . . . . . . . . . . . . . Die Startdatei der WebStart-Anwendung im Browser . . . . . . . . . . . . . . . Der WebStart-Applikationsmanager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Koordinatensystem von Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein einfaches Fenster . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausgabe von Linien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausgabe von Rechtecken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausgabe eines Polygons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausgabe von Kreisen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausgabe von Kreisbögen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausgabe von gefüllten Flächen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausgabe des Java-Logos als Liniengrafik . . . . . . . . . . . . . . . . . . . . . . . . . . . . Kopieren von Grafiken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung der Clipping-Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einfache Textausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Randelemente eines Fensters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausgabe verschiedener Fonts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Liste der Standardschriften . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Größenmaßzahlen für Fonts in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anzeige von Font-Metriken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zentrierte Textausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Farbenkreis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung von Systemfarben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Programm zur Druckausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Dialog zur Druckseitenkonfiguration unter Windows . . . . . . . . . . Der Dialog zur Druckjobkonfiguration unter Windows . . . . . . . . . . . . . Hierarchie der Fensterklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein einfacher Bildschirmschoner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Beispiel-Icon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Programm mit veränderten Fensterelementen . . . . . . . . . . . . . . . . . . Die Hierarchie der Ereignisklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Hierarchie der EventListener-Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . Das Programm für den Nachrichtentransfer . . . . . . . . . . . . . . . . . . . . . . . . . Das Fenster sieht sich selbst aus der Vogelperspektive . . . . . . . . . . . . . Die Ausgabe des Mausklickprogramms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Ausgabe des Mausbewegungsprogramms . . . . . . . . . . . . . . . . . . . . . . Programm nach Erhalt des Eingabefokus . . . . . . . . . . . . . . . . . . . . . . . . . . . . Darstellung von Tastaturereignissen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Erzeugen von Menüs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Geschachtelte Menüs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Programm, das auf Action-Events reagiert . . . . . . . . . . . . . . . . . . . . . .
313 313 327 328 535 538 539 541 542 543 544 546 546 548 550 552 553 555 557 559 561 562 567 569 577 581 581 595 601 603 606 611 613 620 638 642 646 649 654 663 668 672
Abbildungsverzeichnis
Abbildung 30.4 Abbildung 31.1 Abbildung 31.2 Abbildung 31.3 Abbildung 31.4 Abbildung 31.5 Abbildung 31.6 Abbildung 31.7 Abbildung 31.8 Abbildung 31.9 Abbildung 31.10 Abbildung 31.11 Abbildung 31.12 Abbildung 31.13 Abbildung 31.14 Abbildung 31.15 Abbildung 31.16 Abbildung 31.17 Abbildung 31.18 Abbildung 32.1 Abbildung 32.2 Abbildung 32.3 Abbildung 32.4 Abbildung 32.5 Abbildung 32.6 Abbildung 32.7 Abbildung 32.8 Abbildung 32.9 Abbildung 32.10 Abbildung 32.11 Abbildung 32.12 Abbildung 33.1 Abbildung 33.2 Abbildung 34.1 Abbildung 34.2 Abbildung 34.3 Abbildung 34.4 Abbildung 34.5 Abbildung 34.6 Abbildung 34.7 Abbildung 34.8 Abbildung 34.9
Aufruf eines Kontextmenüs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Dialog mit zwei Buttons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung der Klasse FlowLayout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung der Klasse GridLayout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das FlowLayout in einem größeren Fenster . . . . . . . . . . . . . . . . . . . . . . . . . Das GridLayout in einem größeren Fenster . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung der Klasse BorderLayout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein BorderLayout mit Lücken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Beispiel für GridBagLayout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zellenschema für GridBagLayout-Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . Das GridBagLayout-Beispiel nach dem Skalieren . . . . . . . . . . . . . . . . . . . Verwendung des Null-Layouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung eines geschachtelten Layouts . . . . . . . . . . . . . . . . . . . . . . . . . . Ein weiteres Beispiel für geschachtelte Layouts . . . . . . . . . . . . . . . . . . . . . Das Vaterfenster für den modalen Dialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein einfacher Ja-/Nein-Dialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Aufruf von OKDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Aufruf von YesNoDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Aufruf von YesNoCancelDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Beispielprogramm zum Aufruf der Beispieldialoge . . . . . . . . . . . . Der noch leere Beispieldialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Dialog mit Label-Komponenten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Dialog mit Checkbox-Komponenten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Dialog mit CheckboxGroup-Elementen . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Dialog mit beschrifteten Textfeldern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Dialog mit einem TextArea-Objekt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Dialog mit einer Choice-Komponente . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Dialog mit einer Listbox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Dialog mit zwei Schiebereglern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ViewPort und virtueller Ausgabebereich beim ScrollPane . . . . . . . . . . Verwendung von ScrollPane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Aufbau der 7-Segment-Anzeige . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Beispiel für die Anwendung der 7-Segment-Anzeige . . . . . . . . . . . Laden und Anzeigen einer Bitmap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung von BitmapComponent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein animierter Zähler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Ausgabe während des Ladevorgangs . . . . . . . . . . . . . . . . . . . . . . . . . . . . Animation eines Schriftzugs, Schnappschuss 1 . . . . . . . . . . . . . . . . . . . . . . Animation eines Schriftzugs, Schnappschuss 2 . . . . . . . . . . . . . . . . . . . . . . Animation eines Schriftzugs, Schnappschuss 3 . . . . . . . . . . . . . . . . . . . . . . Die animierte Schlange, Schnappschuss 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die animierte Schlange, Schnappschuss 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . .
675 684 686 688 688 689 691 691 694 695 697 698 700 701 705 706 709 709 709 712 712 715 718 720 723 725 728 730 733 735 740 743 751 755 759 761 764 766 766 766 771 771
1279
Abbildungsverzeichnis
Abbildung 34.10 Abbildung 34.11 Abbildung 34.12 Abbildung 35.1 Abbildung 35.2 Abbildung 35.3 Abbildung 36.1 Abbildung 36.2 Abbildung 36.3 Abbildung 36.4 Abbildung 36.5 Abbildung 36.6 Abbildung 36.7 Abbildung 36.8 Abbildung 36.9 Abbildung 36.10 Abbildung 36.11 Abbildung 36.12 Abbildung 37.1 Abbildung 37.2 Abbildung 37.3 Abbildung 37.4 Abbildung 37.5 Abbildung 37.6 Abbildung 37.7 Abbildung 37.8 Abbildung 37.9 Abbildung 37.10 Abbildung 37.11 Abbildung 37.12 Abbildung 38.1 Abbildung 38.2 Abbildung 38.3 Abbildung 38.4 Abbildung 38.5 Abbildung 38.6 Abbildung 38.7 Abbildung 38.8 Abbildung 38.9 Abbildung 38.10 Abbildung 39.1 Abbildung 39.2
1280
Die animierte Schlange, Schnappschuss 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Lauflicht-Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Animation mit Doppelpufferung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Beispielprogramm im Metal-Look-and-Feel . . . . . . . . . . . . . . . . . . . . Das Beispielprogramm im Motif-Look-and-Feel . . . . . . . . . . . . . . . . . . . . . Das Beispielprogramm im Windows-Look-and-Feel . . . . . . . . . . . . . . . . Ein einfaches JFrame-Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Struktur einer RootPane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Hauptfenster mit Dialogelementen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein einfacher SplashScreen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Standard-Icons bei JOptionPane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Button-Kombinationen bei showConfirmDialog . . . . . . . . . . . . . . . . . . . . . Die Methode showInputDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JInternalFrame . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Swing-Programm mit einem einfachen Menü . . . . . . . . . . . . . . . . . . . Ein Swing-Programm mit einem umfangreichen Menü . . . . . . . . . . . . . Die wichtigsten Umrandungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Programm mit einem transparenten Button . . . . . . . . . . . . . . . . . . . . . Die Klasse JLabel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JTextField . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JTextArea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JSpinner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JButton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JCheckBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JRadioButton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JComboBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JScrollBar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JSlider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JProgressBar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Anatomie einer JScrollPane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JScrollPane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JSplitPane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JTabbedPane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine einfache Tabelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine JTable mit einer Million Zellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine JTable mit eigenem Spaltenmodell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Tabelle mit einem eigenen Zellrenderer . . . . . . . . . . . . . . . . . . . . . . . . Ein einfacher JTree im Metal- und Windows-Look-and-Feel . . . . . . . Ein veränderbarer JTree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ableitungsbaum der Applet-Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Darstellung des Schranken-Applets im Netscape Navigator . . . . . . . .
771 774 781 790 791 791 800 801 803 805 807 809 811 816 821 826 830 834 843 846 849 851 854 856 858 863 865 868 872 874 879 881 885 888 890 900 902 906 911 918 924 932
Abbildungsverzeichnis
Abbildung 39.3 Abbildung 39.4 Abbildung 40.1 Abbildung 40.2 Abbildung 40.3 Abbildung 41.1 Abbildung 41.2 Abbildung 42.1 Abbildung 44.1 Abbildung 44.2 Abbildung 44.3 Abbildung 44.4 Abbildung 44.5 Abbildung 44.6 Abbildung 44.7 Abbildung 44.8 Abbildung 44.9 Abbildung 44.10 Abbildung 44.11 Abbildung 45.1 Abbildung 46.1 Abbildung 46.2 Abbildung 47.1 Abbildung 47.2 Abbildung 47.3 Abbildung 48.1 Abbildung 49.1 Abbildung 49.2
Das sprechende Hello, World-Programm . . . . . . . . . . . . . . . . . . . . . . . . . . . . 934 Das Wolkenkratzer-Beispielprogramm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 937 Darstellung von URLLaden im Netscape Navigator . . . . . . . . . . . . . . . . 949 Die drei kommunizierenden Applets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 952 Die Calculator-Applikation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 953 Das Programm serialver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 972 Eltern-Kind-Graph für Serialisierungsbeispiel . . . . . . . . . . . . . . . . . . . . . . . 975 E/R-Diagramm für DirDB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 997 Die Glühlampen-Bean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1066 Die Beanbox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1068 Die Glühlampe in der BeanBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1072 Die deserialisierte Glühlampen-Bean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1074 LightedPushButton und VetoSwitch in der Beanbox . . . . . . . . . . . . . . . 1084 Testprogramm für Eigenschaftenänderungen . . . . . . . . . . . . . . . . . . . . . . 1085 Das ButtonPanel in der BeanBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1090 LightBulbLightOnEditor1 in der Beanbox . . . . . . . . . . . . . . . . . . . . . . . . . . 1096 LightBulbLightOnEditor2 in der Beanbox . . . . . . . . . . . . . . . . . . . . . . . . . . 1097 LightBulbLightOnEditor3 in der Beanbox . . . . . . . . . . . . . . . . . . . . . . . . . . 1100 LightBulbLightOnEditor3 in der Eigenschaftenliste . . . . . . . . . . . . . . . . 1100 Datenbankzugriff mit JDBC und ORM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1104 Das ISO/OSI-7-Schichten- Modell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1122 Das vereinfachte 4-Ebenen-Modell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1123 Prinzipielle Arbeitsweise von RMI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1154 Stubs und Skeletons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1155 Kommunikation im RMI-Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1166 Verschlüsseln einer Nachricht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1170 Samplen eines Audio-Signals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1205 Ein gesampeltes Audio-Signal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1205
1281
Tabellenverzeichnis
Tabelle 3.1 Tabelle 3.2 Tabelle 4.1 Tabelle 4.2 Tabelle 4.3 Tabelle 5.1 Tabelle 5.2 Tabelle 5.3 Tabelle 5.4 Tabelle 5.5 Tabelle 5.6 Tabelle 10.1 Tabelle 13.1 Tabelle 13.2 Tabelle 16.1 Tabelle 16.2 Tabelle 17.1 Tabelle 17.2 Tabelle 18.1 Tabelle 18.2 Tabelle 19.1 Tabelle 24.1 Tabelle 25.1 Tabelle 25.2 Tabelle 27.1 Tabelle 28.1 Tabelle 28.2 Tabelle 28.3
Inhalt der JDK-Dokumentation . . . . . . . 82 Die comp.lang.java-Hierarchie im Usenet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 Primitive Datentypen . . . . . . . . . . . . . . . . . 98 Standard-Escape-Sequenzen . . . . . . . . . 99 Symbolische Fließkommaliterale . . . . 101 Arithmetische Operatoren . . . . . . . . . . 118 Relationale Operatoren . . . . . . . . . . . . . . 118 Logische Operatoren . . . . . . . . . . . . . . . . 119 Bitweise Operatoren . . . . . . . . . . . . . . . . . 120 Zuweisungsoperatoren . . . . . . . . . . . . . . 121 Operator-Vorrangregeln . . . . . . . . . . . . . 127 Die Wrapper- Klassen . . . . . . . . . . . . . . . 223 Wichtige Standardpakete des JDK . . 304 Wichtige Standarderweiterungen des JDK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305 Feldbezeichner der Klasse Calendar 389 Standard-Properties . . . . . . . . . . . . . . . . . 397 Häufige Elemente für reguläre Ausdrücke in Java . . . . . . . . . . . . . . . . . . . . 414 Formatzeichen für DecimalFormat . . 428 Aus Writer abgeleitete Klassen . . . . . 442 Aus Reader abgeleitete Klassen . . . . . 453 Die UTF-8-Kodierung . . . . . . . . . . . . . . . 466 Style-Parameter . . . . . . . . . . . . . . . . . . . . . . 555 Gebräuchliche Farbwerte . . . . . . . . . . . . 563 Liste der vordefinierten Systemfarben . . . . . . . . . . . . . . . . . . . . . . . . . 567 Konstanten zur Cursorauswahl . . . . . 604 Focus-Ereignisse . . . . . . . . . . . . . . . . . . . . . 614 Methoden für Focus-Ereignisse . . . . . 615 Key-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . 615
Tabellenverzeichnis
Tabelle 28.4 Tabelle 28.5 Tabelle 28.6 Tabelle 28.7 Tabelle 28.8 Tabelle 28.9 Tabelle 28.10 Tabelle 28.11 Tabelle 28.12 Tabelle 28.13 Tabelle 28.14 Tabelle 28.15 Tabelle 28.16 Tabelle 28.17 Tabelle 28.18 Tabelle 28.19 Tabelle 28.20 Tabelle 28.21 Tabelle 28.22 Tabelle 29.1 Tabelle 29.2 Tabelle 29.3 Tabelle 29.4 Tabelle 29.5 Tabelle 32.1 Tabelle 32.2 Tabelle 36.1 Tabelle 36.2 Tabelle 36.3 Tabelle 36.4 Tabelle 36.5 Tabelle 36.6 Tabelle 38.1 Tabelle 38.2 Tabelle 39.1 Tabelle 42.1 Tabelle 42.2 Tabelle 42.3 Tabelle 42.4 Tabelle 42.5 Tabelle 43.1 Tabelle 43.2
1284
Methoden für Key-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615 Mouse-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615 Methoden für Mouse-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615 MouseMotion-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616 Methoden für MouseMotion-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616 Komponenten-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616 Methoden für Komponenten-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616 Container-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616 Methoden für Container-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617 Window-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617 Methoden für Window-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617 Action-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617 Methoden für Action-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617 Adjustment-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 618 Methoden für Adjustment-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 618 Item-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 618 Methoden für Item-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 618 Text-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 618 Methoden für Text-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 618 Methoden von WindowListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 634 Methoden von ComponentListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636 Methoden von MouseListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 639 Virtuelle Key-codes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651 Rückgabecodes bei Tastaturereignissen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652 Konstanten für Schieberegler-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 732 Konstanten zur Anzeige der Schieberegler in ScrollPane . . . . . . . . . . . . . . 734 Rückgabewerte des showConfirmDialogs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 809 Konstanten für Umschalttasten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 819 Die Konstanten der Klasse SwingConstants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 822 Border-Implementierungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 829 DebugGraphics-Konstanten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 832 Bedingungen zur Registrierung von Tastaturkommandos . . . . . . . . . . . . . . 837 Anzeige der Schieberegler bei JScrollPane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 878 Parameter für setAutoResizeMode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 892 Optionale Parameter des APPLET-Tags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 929 get-Methoden von ResultSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 994 Die Struktur der dir-Tabelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 996 Die Struktur der file-Tabelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 997 SQL-Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1018 SQL-Aggregatfunktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1020 Klassenobjekte für die primitiven Typen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1029 Sicherbarkeitsattribute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1053
Tabellenverzeichnis
Tabelle 45.1 Tabelle 45.2 Tabelle 45.3 Tabelle 45.4 Tabelle 46.1 Tabelle 46.2 Tabelle 46.3 Tabelle 48.1 Tabelle 50.1 Tabelle 50.2 Tabelle 51.1 Tabelle 51.2 Tabelle 51.3 Tabelle 51.4 Tabelle 51.5 Tabelle 51.6 Tabelle 51.7 Tabelle 51.8 Tabelle 51.9 Tabelle 51.10 Tabelle 51.11
Die Struktur der dir-Tabelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Struktur der dir-Tabelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Attribute der Annotation @Column . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anpassen der Konfigurationsdatei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Klassen von IP-Adressen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Standard-Port-Nummern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Liste wichtiger RFCs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wichtige Permission-Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Geschwindigkeit von Methodenaufrufen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Parameter von hprof . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Optionen von javac . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Optionen des Java-Interpreters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Optionen von appletviewer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Kommandos von jdb . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Markierungen in Dokumentationskommentaren . . . . . . . . . . . . . . . . . . . . . . Einige Optionen von javadoc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Kommandos von jar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Optionen von javap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Optionen von serialver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Optionen von jarsigner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Optionen von rmic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1105 1110 1110 1112 1124 1126 1127 1198 1234 1243 1252 1254 1256 1258 1260 1262 1263 1265 1266 1267 1268
1285
Listingverzeichnis
Listing 2.1 Listing 2.2 Listing 2.3 Listing 2.4 Listing 2.5 Listing 2.6 Listing 2.7 Listing 4.1 Listing 4.2 Listing 4.3 Listing 4.4 Listing 4.5 Listing 4.6 Listing 4.7 Listing 4.8 Listing 4.9 Listing 4.10 Listing 4.11 Listing 4.12 Listing 4.13 Listing 5.1 Listing 5.2 Listing 5.3 Listing 5.4
Hello, world . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 Einfache Ausgaben . . . . . . . . . . . . . . . . . . . . 66 Einfache Eingaben . . . . . . . . . . . . . . . . . . . . . 67 Einrücken von Klassen und Methoden 68 Einrücken von Kontrollanweisungen 69 Einrücken fortgesetzter Anweisungen 69 Einrücken langer Methodenaufrufe . . 69 Verwendung eines Dokumentationskommentars im Java-API . . . . . . . . . . . . . 96 Einfache Variablen ausgeben . . . . . . . . 103 Initialisieren von Variablen . . . . . . . . . . 103 Deklaration von Arrays . . . . . . . . . . . . . . 104 Erzeugen von Arrays . . . . . . . . . . . . . . . . . 105 Deklaration und Initialisierung von Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 Initialisierung mit literalen Arrays . . 105 Deklaration und Zugriff auf Arrays . 105 Zugriff auf mehrdimensionale Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 Ein nichtrechteckiges Array . . . . . . . . . 107 Erzeugen eines Objekts mit dem new-Operator . . . . . . . . . . . . . . . . . . . . . . . . . 109 Umwandlung zwischen int, byte und char . . . . . . . . . . . . . . . . . . . . . . . . . 111 Anwendung der Klasse ByteKit . . . . . 113 Fehler beim Kompilieren durch unvollständige Initialisierung . . . . . . . 116 Fehler beim Kompilieren durch unvollständige Datenflussanalyse . . . 117 String-Verkettung . . . . . . . . . . . . . . . . . . . . 123 Vorsicht bei der String-Verkettung! . 124
Listingverzeichnis
Listing 5.5 Listing 5.6 Listing 5.7 Listing 5.8 Listing 5.9 Listing 6.1 Listing 6.2 Listing 6.3 Listing 6.4 Listing 6.5 Listing 6.6 Listing 6.7 Listing 6.8 Listing 6.9 Listing 6.10 Listing 6.11 Listing 7.1 Listing 7.2 Listing 7.3 Listing 7.4 Listing 7.5 Listing 7.6 Listing 7.7 Listing 7.8 Listing 7.9 Listing 7.10 Listing 7.11 Listing 7.12 Listing 7.13 Listing 7.14 Listing 7.15 Listing 7.16 Listing 7.17 Listing 7.18 Listing 7.19 Listing 8.1 Listing 8.2 Listing 8.3 Listing 8.4 Listing 8.5 Listing 8.6 Listing 8.7
1288
Korrekte String-Verkettung bei gemischten Ausdrücken . . . . . . . . . . . . . . . Vergleichen von Referenzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vergleichen von Strings mit equals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bindungsprobleme bei den bitweisen Operatoren . . . . . . . . . . . . . . . . . . . . . . Korrekte Klammerung von bitweisen Operatoren . . . . . . . . . . . . . . . . . . . . . . Verdecken von Klassen- oder Instanzvariablen . . . . . . . . . . . . . . . . . . . . . . . . . Dangling else . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dangling else, ausgeschaltet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bedingtes Kompilieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Duff's Device . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das gelabelte break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anwendung der erweiterten for-Schleife auf Collections . . . . . . . . . . . . . . . Anwendung der erweiterten for-Schleife auf Arrays . . . . . . . . . . . . . . . . . . . . Anwendung von Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung einer IllegalArgumentException . . . . . . . . . . . . . . . . . . . . . . . . . . Anwendung von Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine einfache Klassendefinition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Erzeugen eines Objekts mit new . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Kombinierte Deklaration und Initialisierung einer Objektvariablen . . . . Zuweisen von Werten an die Variablen eines Objekts . . . . . . . . . . . . . . . . . . Lesender Zugriff auf die Variablen eines Objekts . . . . . . . . . . . . . . . . . . . . . . . Eine einfache Methode zur Altersberechnung . . . . . . . . . . . . . . . . . . . . . . . . . . . Aufruf einer Methode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung von this . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Methode zur Ausgabe des Alters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wiederholter Aufruf der Methode zur Ausgabe des Alters . . . . . . . . . . . . Eine Methode mit einer variablen Parameterliste . . . . . . . . . . . . . . . . . . . . . . . Die Anwendung von Object... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Überladen einer Methode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Definition eines parametrisierten Konstruktors . . . . . . . . . . . . . . . . . . . . . . . . . Aufruf eines parametrisierten Konstruktors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Klasse mit mehreren Konstruktoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verkettung von Konstruktoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Initialisierungsreihenfolge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die finalize- Methode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein einfaches Beispiel für Vererbung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zugriff auf geerbte Membervariablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ableitung einer abgeleiteten Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zugriff auf mehrfach vererbte Membervariablen . . . . . . . . . . . . . . . . . . . . . . . . Überlagern einer Methode in einer abgeleiteten Klasse . . . . . . . . . . . . . . . . . Zugriff auf fremde private Membervariablen . . . . . . . . . . . . . . . . . . . . . . . . . . . Realisierung eines Instanzenzählers mit Klassenvariablen . . . . . . . . . . . . . .
124 125 125 130 130 134 136 136 137 138 141 143 143 145 147 148 163 163 164 164 164 165 165 166 166 166 167 168 170 171 171 172 172 173 174 178 178 178 178 180 184 187
Listingverzeichnis
Listing 8.8 Listing 8.9 Listing 8.10 Listing 8.11 Listing 8.12 Listing 8.13 Listing 9.1 Listing 9.2 Listing 9.3 Listing 9.4 Listing 9.5 Listing 9.6 Listing 9.7 Listing 9.8 Listing 9.9 Listing 9.10 Listing 9.11 Listing 9.12 Listing 9.13 Listing 9.14 Listing 9.15 Listing 9.16 Listing 9.17 Listing 9.18 Listing 10.1 Listing 10.2 Listing 10.3 Listing 10.4 Listing 10.5 Listing 10.6 Listing 10.7 Listing 10.8 Listing 10.9 Listing 10.10 Listing 10.11 Listing 10.12 Listing 10.13 Listing 10.14 Listing 10.15 Listing 10.16 Listing 10.17 Listing 10.18
Verwendung von Klassenvariablen zur Definition von Konstanten . . . Verwendung von Math.sqrt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Definition eines statischen Initialisierers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Abstrakte Klassen und Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einfügen einer neuen Mitarbeiterklasse in die Gehaltsberechnung . . . . Polymorphe Methodenaufrufe im Konstruktor . . . . . . . . . . . . . . . . . . . . . . . . . . Definition eines Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Implementierung eines Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse FussballPlatz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse PapierBlatt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung eines Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der instanceof-Operator auf Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Interface Comparable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Implementierung mehrerer Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Erben von Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ableiten von Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konstanten in Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . static import . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Implementierung einer tiefen Kopie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Interface DoubleMethod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionszeiger mit Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das SimpleTreeNode-Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Default-Implementierung des SimpleTreeNode-Interfaces . . . . . . . . . . . . . Implementierung des SimpleTreeNode-Interface durch Delegation . . . Eine nicht-statische lokale Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Definition einer lokalen Klasse in einer Methode . . . . . . . . . . . . . . . . . . . . . . . Anwendung anonymer Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anwendung statischer lokaler Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anwendung der Wrapper-Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Call by Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Autoboxing und Autounboxing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anwendung von Aufzählungstypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Erweiterung von Aufzählungstypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Implementierung eines Singleton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Implementierung eines Immutable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Klasse mit einer Factory- Methode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Factory-Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine abstrakte Factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Implementierung eines Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Delegate-/Delegator-Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Composite-Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Visitor-Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
188 189 190 192 194 195 197 198 199 199 201 202 203 204 205 206 207 208 209 211 211 213 213 214 218 220 221 222 225 227 228 230 232 234 236 238 239 241 245 248 251 254
1289
Listingverzeichnis
Listing 10.19 Listing 11.1 Listing 11.2 Listing 11.3 Listing 11.4 Listing 11.5 Listing 11.6 Listing 11.7 Listing 12.1 Listing 12.2 Listing 12.3 Listing 12.4 Listing 12.5 Listing 12.6 Listing 12.7 Listing 12.8 Listing 12.9 Listing 13.1 Listing 13.2 Listing 13.3 Listing 13.4 Listing 13.5 Listing 13.6 Listing 13.7 Listing 13.8 Listing 13.9 Listing 13.10 Listing 13.11 Listing 13.12 Listing 14.1 Listing 14.2 Listing 14.3 Listing 14.4 Listing 15.1 Listing 15.2 Listing 15.3 Listing 15.4 Listing 15.5 Listing 15.6 Listing 15.7 Listing 15.8 Listing 15.9
1290
Das Observer- Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . String-Verkettung und die Methode substring . . . . . . . . . . . . . . . . . . . . . . . . . . Die Methode regionMatches der Klasse String . . . . . . . . . . . . . . . . . . . . . . . . . . Zerlegen von Zeichenketten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Implementierung der String- Verkettung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausgabe von beliebigen Zeichenketten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Formatierte Zahlenausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Formatierte Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die try-catch-Anweisung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Programm mit einem Laufzeitfehler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Abfangen des Laufzeitfehlers mit einer try-catch-Anweisung . . . . . . . . . . Verwendung des Fehlerobjekts nach einem Laufzeitfehler . . . . . . . . . . . . . Fortfahren nach Laufzeitfehlern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mehr als eine catch-Klausel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung der finally-Klausel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung der throws-Klausel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Auslösen einer Ausnahme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zugriff auf verdeckte Membervariablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Versehentliches Verdecken einer Membervariable . . . . . . . . . . . . . . . . . . . . . . Die Klasse A des Pakets demo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse B des Pakets demo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse C des Pakets demo.tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung der Klassen aus selbst definierten Paketen . . . . . . . . . . . . . . . Einen InputStream zu einer Ressourcen-Datei beschaffen . . . . . . . . . . . . . . Laden einer Textressource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Laden einer Image-Ressource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine WebStart-Deskriptordatei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die HTML-Datei zum Aufruf der WebStart-Applikation . . . . . . . . . . . . . . . Ein Beispielprogramm für das jnlp-API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Methode elements der Klasse Vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anwendung eines Stacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anwendung der Klasse Hashtable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konstruktion von Primzahlen mit der Klasse BitSet . . . . . . . . . . . . . . . . . . . . Anlegen und Bearbeiten zweier Listen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zugriff auf eine Collection mit einem Iterator . . . . . . . . . . . . . . . . . . . . . . . . . . . Generierung eines Lottotipps mit HashSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anwendung der Klasse HashMap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse TreeSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Rückwärts sortieren mit einem Comparator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sortieren einer Liste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine untypisierte Sortiermethode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die typsichere Version der Sortiermethode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
259 268 269 271 274 277 279 281 286 286 287 288 289 291 292 293 295 298 299 310 310 310 311 320 320 321 324 327 332 338 340 343 346 354 356 359 362 366 367 369 372 372
Listingverzeichnis
Listing 15.10 Listing 15.11 Listing 15.12 Listing 15.13 Listing 15.14 Listing 15.15 Listing 15.16 Listing 15.17 Listing 15.18 Listing 16.1 Listing 16.2 Listing 16.3 Listing 16.4 Listing 16.5 Listing 16.6 Listing 16.7 Listing 16.8 Listing 16.9 Listing 16.10 Listing 17.1 Listing 17.2 Listing 17.3 Listing 17.4 Listing 17.5 Listing 17.6 Listing 17.7 Listing 17.8 Listing 17.9 Listing 17.10 Listing 17.11 Listing 17.12 Listing 17.13 Listing 17.14 Listing 17.15 Listing 18.1 Listing 18.2 Listing 18.3 Listing 18.4 Listing 18.5 Listing 18.6 Listing 18.7 Listing 18.8
Die vereinfachte Version der typsicheren Variante . . . . . . . . . . . . . . . . . . . . . . Ein untypisierter Wortzähler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die 5.0-Wortzählervariante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine eigene typisierte Listenklasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionierendes Zusammenspiel von Ober- und Unterklassen . . . . . . . Nicht funktionierendes Zusammenspiel von Ober- und Unterklassen . Nicht funktionierende Ausgabe der Zahlenliste . . . . . . . . . . . . . . . . . . . . . . . . . Funktionierende Ausgabe der Zahlenliste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verbesserte funktionierende Ausgabe der Zahlenliste . . . . . . . . . . . . . . . . . . Zufallszahlen zur Generierung eines Lottotipps . . . . . . . . . . . . . . . . . . . . . . . . Die Felder der Klasse Calendar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Datumsarithmetik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausgeben der System-Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Auflösung des System-Timers bestimmen . . . . . . . . . . . . . . . . . . . . . . . . . . Die Auflösung von Thread.sleep bestimmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung von System.arraycopy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Starten von notepad.exe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Starten externer Programme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sortieren eines Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Reguläre Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Kurzer Zeichenketten-Vergleich mit regulären Ausdrücken . . . . . . . . . . . Strings und reguläre Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zerlegen von Zeichenketten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anwendung der Klasse BigInteger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anwendung der Klasse BigDecimal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Darstellung einiger Locales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anwendung von DecimalFormat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anwendung von DateFormat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse SimpleTextResource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Basisvariante MyTextResource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die deutschsprachige Variante MyTextResource_de . . . . . . . . . . . . . . . . . . . . Die schweizerische Variante MyTextResource_de_CH . . . . . . . . . . . . . . . . . Test von MyTextResource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die englische Variante MyTextResource_en . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Erstellen einer Datei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Gepufferte Ausgabe in eine Datei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Schachteln von Writer-Konstruktoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse PrintWriter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konstruktion einer eigenen FilterWriter-Klasse . . . . . . . . . . . . . . . . . . . . . . . . . Anwendung der Klasse FileReader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung der Klasse StringReader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eingabepufferung beim Lesen aus Dateien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
373 374 374 375 379 380 381 381 382 386 390 394 398 400 402 404 405 407 410 414 415 416 416 420 423 426 428 431 433 434 434 435 435 437 443 446 446 448 449 454 455 457
1291
Listingverzeichnis
Listing 18.9 Listing 19.1 Listing 19.2 Listing 19.3 Listing 19.4 Listing 19.5 Listing 19.6 Listing 19.7 Listing 20.1 Listing 20.2 Listing 21.1 Listing 21.2 Listing 21.3 Listing 21.4 Listing 21.5 Listing 21.6 Listing 21.7 Listing 22.1 Listing 22.2 Listing 22.3 Listing 22.4 Listing 22.5 Listing 22.6 Listing 22.7 Listing 22.8 Listing 22.9 Listing 22.10 Listing 22.11 Listing 22.12 Listing 22.13 Listing 22.14 Listing 23.1 Listing 23.2 Listing 23.3 Listing 23.4 Listing 23.5 Listing 23.6 Listing 23.7 Listing 23.8 Listing 23.9 Listing 23.10 Listing 23.11
1292
Die Klasse LineNumberReader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung eines FileOutputStream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung der Klasse DataOutputStream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Erstellen eines ZIP-Archivs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Kopieren einer Datei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung der Klasse DataInputStream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Berechnung der Adler-32-Prüfsumme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Entpacken eines ZIP-Archivs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lesen einer .class-Datei mit der Klasse RandomAccessFile . . . . . . . . . . . . . Spiegeln einer Datei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Modifikationsdatum einer Datei ausgeben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Interface DirectoryVisitor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein DirectoryVisitor zum Ausdrucken eines Verzeichnisses . . . . . . . . . . . Die Klasse DirectorySizeVisitor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Rekursiver Durchlauf von Verzeichnissen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung der Klasse File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anlegen einer temporären Datei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein einfacher Thread mit einem Zähler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Beenden des Thread durch Aufruf von stop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anwendung der Methoden interrupt und isInterrupted . . . . . . . . . . . . . . . . Implementieren von Runnable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Klasse zur Primfaktorzerlegung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung der Klasse zur Primfaktorzerlegung . . . . . . . . . . . . . . . . . . . . . . Primfaktorzerlegung mit Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung der Klasse zur Primfaktorzerlegung mit Threads . . . . . . . . Zwei Zählerthreads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Synchronisation von Threads mit Klassenobjekten . . . . . . . . . . . . . . . . . . . . . . Eine unzureichend synchronisierte Zählerklasse . . . . . . . . . . . . . . . . . . . . . . . . Synchronisieren der Zählermethode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Producer-/Consumer-Beispiel mit wait und notify . . . . . . . . . . . . . . . . . Das Producer-/Consumer-Beispiel mit einer Pipe . . . . . . . . . . . . . . . . . . . . . . Ein einfaches Fenster erzeugen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse WindowClosingAdapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Schließen des Fensters ermöglichen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Rahmenprogramm für nachfolgende Beispiele . . . . . . . . . . . . . . . . . . . . . . . . . . Ausgabe von Linien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausgabe von Rechtecken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausgabe eines Polygons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausgabe von Kreisen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausgabe von Kreisbögen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausgabe von gefüllten Flächen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Kopieren von Flächen mit copyArea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
458 463 466 468 471 475 476 476 483 485 489 490 491 491 492 494 497 501 502 504 508 509 510 511 512 513 516 517 519 520 523 533 535 536 537 539 540 542 543 544 545 547
Listingverzeichnis
Listing 23.12 Listing 24.1 Listing 24.2 Listing 24.3 Listing 24.4 Listing 24.5 Listing 24.6 Listing 24.7 Listing 25.1 Listing 25.2 Listing 26.1 Listing 26.2 Listing 26.3 Listing 26.4 Listing 27.1 Listing 27.2 Listing 27.3 Listing 27.4 Listing 28.1 Listing 28.2 Listing 28.3 Listing 28.4 Listing 28.5 Listing 28.6 Listing 29.1 Listing 29.2 Listing 29.3 Listing 29.4 Listing 29.5 Listing 29.6 Listing 30.1 Listing 30.2 Listing 30.3 Listing 30.4 Listing 30.5 Listing 30.6 Listing 30.7 Listing 31.1 Listing 31.2 Listing 31.3 Listing 31.4 Listing 31.5
Verwendung der Clipping-Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einfache Textausgabe im Grafikfenster . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausgabe verschiedener Schriften . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Auflistung aller Standardschriften . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Auflistung der Standardschriften . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vergrößern der Schriftart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anzeige von Font-Metriken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zentrierte Textausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Darstellung des Farbenkreises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung von Systemfarben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausdruck einer Testseite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse FilePrintHelper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse SimpleFilePrinter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Druckausgabe an LPT1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anzeigen und Entfernen eines Frame . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein einfacher Bildschirmschoner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anzeigezustand eines Fensters umschalten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Programm mit veränderten Fensterelementen . . . . . . . . . . . . . . . . . . . . . . Basisprogramm für den Nachrichtentransfer . . . . . . . . . . . . . . . . . . . . . . . . . . . . Implementieren eines Listener-Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung lokaler Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung einer anonymen Klasse als Ereignishandler . . . . . . . . . . . . . . Trennung von GUI- und Anwendungslogik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Überlagern der Komponenten-Event-Handler . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse CloseableFrame . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das eigene Fenster aus der Vogelperspektive . . . . . . . . . . . . . . . . . . . . . . . . . . . Reaktion auf Mausklicks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zeichnen von Rechtecken durch Ziehen der Maus . . . . . . . . . . . . . . . . . . . . . . Behandlung von Fokus-Ereignissen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Reaktion auf Tastaturereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Erzeugen von Menüs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Erzeugen eines Menüeintrags mit Beschleuniger . . . . . . . . . . . . . . . . . . . . . . . . Menüleisten mit zwei Menüs und Beschleunigertasten . . . . . . . . . . . . . . . . . Geschachtelte Menüs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Reaktion auf Action-Events aus einem Menü . . . . . . . . . . . . . . . . . . . . . . . . . . . Einbinden eines Kontextmenüs im AWT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Kommunikation mit der Zwischenablage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Dialog mit zwei Buttons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse FlowLayout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse GridLayout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das BorderLayout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Umgang mit GridBagLayout und GridBagConstraints . . . . . . . . . . . . . . . . .
549 552 555 556 557 558 560 561 565 568 574 584 586 591 598 600 601 605 619 620 622 624 626 628 634 636 641 643 647 652 661 664 665 666 669 674 678 683 686 687 690 692
1293
Listingverzeichnis
Listing 31.6 Listing 31.7 Listing 31.8 Listing 31.9 Listing 31.10 Listing 31.11 Listing 32.1 Listing 32.2 Listing 32.3 Listing 32.4 Listing 32.5 Listing 32.6 Listing 32.7 Listing 32.8 Listing 32.9 Listing 32.10 Listing 32.11 Listing 32.12 Listing 32.13 Listing 32.14 Listing 32.15 Listing 32.16 Listing 33.1 Listing 33.2 Listing 34.1 Listing 34.2 Listing 34.3 Listing 34.4 Listing 34.5 Listing 34.6 Listing 34.7 Listing 34.8 Listing 34.9 Listing 34.10 Listing 34.11 Listing 34.12 Listing 34.13 Listing 34.14 Listing 34.15 Listing 35.1 Listing 36.1 Listing 36.2
1294
Beispiel für GridBagLayout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anordnen von Dialogelementen ohne Layoutmanager . . . . . . . . . . . . . . . . . Schachteln von Layoutmanagern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine weitere Anwendung für geschachtelte Layoutmanager . . . . . . . . . . . Konstruktion modaler Dialoge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Drei modale Standarddialoge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Rahmenprogramm für Dialogelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung von Label-Komponenten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung von Checkbox-Komponenten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Behandlung von Item-Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung einer CheckboxGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung von Textfeldern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Behandlung von Text-Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Textfelder mit Beschriftung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Behandlung von Text-Events bei der Komponente TextArea . . . . . . . . . . . Verwendung einer TextArea-Komponente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Behandlung der Ereignisse einer Choice-Komponente . . . . . . . . . . . . . . . . . . Verwendung einer Choice-Komponente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Behandlung der Ereignisse einer List-Komponente . . . . . . . . . . . . . . . . . . . . . Verwendung einer List-Komponente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung von Scrollbars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung der Klasse ScrollPane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine 7-Segment-Anzeige . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einbinden der 7-Segment-Anzeige . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Laden einer Bitmap-Datei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Laden und Anzeigen einer Bitmap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Programm zum Laden und Anzeigen einer Bitmap . . . . . . . . . . . . . . . . . . . . . Eine Komponente zum Anzeigen einer Bitmap . . . . . . . . . . . . . . . . . . . . . . . . . Verwenden der Bitmap-Komponente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein animierter Zähler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung von Threads zur Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Abspielen einer Folge von Bitmaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die animierte Schlange . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bildschirmflackern reduzieren bei stehenden Animationen . . . . . . . . . . . . Standard-Implementierung von update . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Modifizierte Version von update . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Modifizierte Schlangenanimation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . update-Methode mit Doppelpufferung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Animation mit Doppelpufferung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein einfaches Swing-Beispielprogramm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung der Klasse JFrame . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anordnen von Dialogelementen in einem Hauptfenster . . . . . . . . . . . . . . . .
695 698 699 700 703 706 712 715 717 718 719 722 722 723 724 725 727 727 729 730 732 736 745 749 754 754 756 757 758 760 762 764 767 772 774 775 775 778 778 791 799 802
Listingverzeichnis
Listing 36.3 Listing 36.4 Listing 36.5 Listing 36.6 Listing 36.7 Listing 36.8 Listing 36.9 Listing 36.10 Listing 36.11 Listing 36.12 Listing 36.13 Listing 37.1 Listing 37.2 Listing 37.3 Listing 37.4 Listing 37.5 Listing 37.6 Listing 37.7 Listing 37.8 Listing 37.9 Listing 37.10 Listing 37.11 Listing 37.12 Listing 37.13 Listing 37.14 Listing 38.1 Listing 38.2 Listing 38.3 Listing 38.4 Listing 38.5 Listing 38.6 Listing 38.7 Listing 38.8 Listing 38.9 Listing 38.10 Listing 38.11 Listing 38.12 Listing 38.13 Listing 39.1 Listing 39.2 Listing 39.3 Listing 39.4
Ein einfacher SplashScreen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anwendungsbeispiel für JOptionPane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anwendungsbeispiel für JApplet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HTML-Datei für JApplet-Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JInternalFrame . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Swing-Programm mit einem einfachen Menü . . . . . . . . . . . . . . . . . . . . . . . Weitere Möglichkeiten von Swing-Menüs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anzeigen eines Kontextmenüs in Swing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Debug-Grafik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein transparenter Button . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Registrieren von Tastaturkommandos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JLabel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JTextField . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JTextArea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JSpinner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse DefaultButton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse CancelButton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JButton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JCheckbox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JRadioButton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JComboBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JScrollBar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JSlider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JProgressBar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JScrollPane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JSplitPane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse JTabbedPane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine einfache Tabelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Interface TableData . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Modell für schwach besetzte Tabellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine JTable mit einer Million Zellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine JTable mit einem eigenen Spaltenmodell . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein eigener Zellrenderer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Tabelle mit einem eigenen Zellrenderer . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein einfacher JTree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein JTree mit TreeSelectionListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einfügen, Ändern und Löschen in einem Baum . . . . . . . . . . . . . . . . . . . . . . . . . Ein einfaches Applet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung von getParameterInfo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Methode getAppletInfo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das APPLET-Tag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
804 810 812 812 815 820 824 827 832 833 837 842 845 848 850 852 853 853 855 857 861 864 867 870 873 879 883 887 889 890 897 899 901 904 905 909 913 916 927 927 928 928
1295
Listingverzeichnis
Listing 39.5 Listing 39.6 Listing 39.7 Listing 39.8 Listing 39.9 Listing 39.10 Listing 40.1 Listing 40.2 Listing 40.3 Listing 40.4 Listing 40.5 Listing 40.6 Listing 40.7 Listing 40.8 Listing 40.9 Listing 41.1 Listing 41.2 Listing 41.3 Listing 41.4 Listing 41.5 Listing 41.6 Listing 41.7 Listing 41.8 Listing 41.9 Listing 41.10 Listing 41.11 Listing 42.1 Listing 42.2 Listing 42.3 Listing 42.4 Listing 42.5 Listing 42.6 Listing 42.7 Listing 42.8 Listing 42.9 Listing 42.10 Listing 42.11 Listing 42.12 Listing 43.1 Listing 43.2 Listing 43.3 Listing 43.4
1296
Ein parametrisiertes Applet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 930 Die HTML-Datei zum Schranken-Applet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 931 Das sprechende Hello, World . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 934 Soundausgabe aus einer Applikation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 935 Das Wolkenkratzer-Applet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 938 Die HTML-Datei zum Aufrufen des Wolkenkratzer-Applets . . . . . . . . . . . 942 Laden von Webseiten aus einem Applet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 946 Die HTML-Datei zum Laden der Webseiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . 948 Die Klasse ChgNextApplet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 950 Die HTML-Datei mit den drei kommunizierenden Applets . . . . . . . . . . . . 951 Die Calculator-Applikation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 952 Ein Applikations-Applet im Popup-Fenster . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 954 HTML-Datei zum Aufruf des Beispiel-Applets . . . . . . . . . . . . . . . . . . . . . . . . . . 954 Das gleichwertige Calculator-Applet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 956 HTML-Datei zum Aufruf des Beispiel-Applets . . . . . . . . . . . . . . . . . . . . . . . . . . 956 Eine serialisierbare Uhrzeitklasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 965 Serialisieren eines Time-Objekts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 966 Serialisieren mehrerer Objekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 967 Deserialisieren eines Time-Objekts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 968 Deserialisieren mehrerer Elemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 970 Die Uhrzeitklasse mit serialVersionUID . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 973 Die Klasse Person . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 975 Serialisieren von Objekten und Referenzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 975 Ein einfacher Objektspeicher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 979 Beispielanwendung für den einfachen Objektspeicher . . . . . . . . . . . . . . . . . . 980 Kopieren von Objekten durch Serialisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 983 Behandeln einer SQLException . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 996 Das Rahmenprogramm der DirDB-Datenbank . . . . . . . . . . . . . . . . . . . . . . . . . . 998 Öffnen und Schließen der DirDB-Datenbank . . . . . . . . . . . . . . . . . . . . . . . . . . . 1000 Anlegen der DirDB-Tabellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1002 Füllen der DirDB-Tabellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1004 Anzahl der Sätze in der DirDB-Datenbank . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1006 Suchen nach Dateien in der DirDB-Datenbank . . . . . . . . . . . . . . . . . . . . . . . . . 1007 Suchen nach Verzeichnissen in der DirDB-Datenbank . . . . . . . . . . . . . . . . . 1008 Sortieren der Ergebnismenge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1009 Cluster-Berechnung mit der DirDB-Datenbank . . . . . . . . . . . . . . . . . . . . . . . . 1010 Die Klasse CachedConnection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1014 Verwenden eines PreparedStatement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1017 Dynamisches Laden von Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1025 Testcode in der main-Methode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1027 Die Klasse Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1030 Die Klasse TestQueue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1032
Listingverzeichnis
Listing 43.5 Listing 43.6 Listing 43.7 Listing 43.8 Listing 43.9 Listing 43.10 Listing 43.11 Listing 43.12 Listing 43.13 Listing 43.14 Listing 43.15 Listing 43.16 Listing 43.17 Listing 43.18 Listing 43.19 Listing 44.1 Listing 44.2 Listing 44.3 Listing 44.4 Listing 44.5 Listing 44.6 Listing 44.7 Listing 44.8 Listing 44.9 Listing 44.10 Listing 44.11 Listing 44.12 Listing 44.13 Listing 45.1 Listing 45.2 Listing 45.3 Listing 45.4 Listing 45.5 Listing 45.6 Listing 45.7 Listing 45.8 Listing 46.1 Listing 46.2 Listing 46.3 Listing 46.4 Listing 46.5 Listing 46.6
Funktionszeiger mit Reflection nachbilden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Parametrisierte Konstruktoren mit Reflection aufrufen . . . . . . . . . . . . . . . . Die Klasse PrintableObject . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Erzeugen von Arrays per Reflection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zugriff auf Array-Elemente per Reflection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine einfache Annotation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung der Annotation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Annotation mit einer Variablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zuweisen von annotierten Werten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zuweisen von annotierten Werten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Komplexe Annotation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zuweisen von annotierten Werten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Komplexe Annotation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einschränken der Verwendbarkeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vollständige Annotation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Deklaration der Klasse LightBulb . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Laden einer Image-Ressource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Bean zur Anzeige einer Glühbirne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einbinden einer einfachen Bean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einbinden einer serialisierten Bean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein beleuchteter Taster . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Veto-Schalter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eigenschaftenänderungen von Beans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse ButtonPanel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse LightBulbBeanInfo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse LightBulbLightOnEditor1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse LightBulbLightOnEditor2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse LightBulbLightOnEditor3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Klasse für die dir-Tabelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Annotierte Klasse für die dir-Tabelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konfigurationsdatei für das Java Persistenz API . . . . . . . . . . . . . . . . . . . . . . . Zugriff auf den EntityManager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Transaktionen im EntityManager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anlegen eines Datensatzes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Laden eines Datensatzes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Laden eines Datensatzes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . IP-Adressenauflösung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Abfrage des DayTime-Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lesender und schreibender Zugriff auf einen Socket . . . . . . . . . . . . . . . . . . Laden einer Seite von einem Web-Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein ECHO-Server für Port 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine verbesserte Version des Echo-Servers . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1033 1037 1039 1042 1046 1048 1049 1049 1049 1050 1050 1051 1051 1052 1053 1059 1060 1062 1065 1073 1076 1081 1084 1086 1092 1095 1096 1098 1105 1108 1111 1113 1114 1115 1116 1117 1129 1132 1133 1137 1139 1141
1297
Listingverzeichnis
Listing 46.7 Listing 46.8 Listing 47.1 Listing 47.2 Listing 47.3 Listing 47.4 Listing 47.5 Listing 47.6 Listing 48.1 Listing 48.2 Listing 48.3 Listing 48.4 Listing 48.5 Listing 48.6 Listing 48.7 Listing 48.8 Listing 48.9 Listing 48.10 Listing 49.1 Listing 49.2 Listing 49.3 Listing 49.4 Listing 50.1 Listing 50.2 Listing 50.3 Listing 50.4 Listing 50.5 Listing 50.6 Listing 50.7 Listing 50.8 Listing 50.9 Listing 50.10 Listing 51.1
1298
Ein experimenteller Web-Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Daten von einem URL lesen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Remote-Interface für den Uhrzeit-Service . . . . . . . . . . . . . . . . . . . . . . . . . . Das TimeStore-Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Implementierung des Uhrzeit-Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Registrierung von Remote-Objekten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der TimeService-Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse MyTimeStore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verschlüsselung durch Substitution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verschlüsselung mit Exklusiv-ODER . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Erstellen eines Message Digest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Erzeugen kryptografischer Zufallszahlen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Erstellen einer digitalen Unterschrift . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verifizieren einer digitalen Unterschrift . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein unerlaubtes Applet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vorläufige HTML-Datei zum Aufruf des unerlaubten Applets . . . . . . . Aufruf des signierten Applets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausgeben des System-Property user.name . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Abspielen einer Sample-Datei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Alle meine Entchen – erster Versuch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Alle meine Entchen mit dem Sequenzer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Abspielen einer Midi-Datei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Langsame String-Verkettung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wie der Java-Compiler String-Verkettungen übersetzt . . . . . . . . . . . . . . . . Performante String-Verkettungen mit StringBuilder.append . . . . . . . . . . Langsames Einfügen in einen String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Schnelles Einfügen in einen String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vergleich von Listen und Vektoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Performance von Writer und OutputStream . . . . . . . . . . . . . . . . . . . . . . . . . . . . Gepufferter Zugriff auf Random-Access-Dateien . . . . . . . . . . . . . . . . . . . . . . Ein Beispielprogramm zum Testen des Profilers . . . . . . . . . . . . . . . . . . . . . . . Das verbesserte Programm nach der Profiler-Sitzung . . . . . . . . . . . . . . . . . . HTML mit ARCHIVE-Tag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1143 1151 1157 1157 1158 1160 1163 1164 1171 1171 1174 1178 1185 1186 1191 1193 1194 1200 1209 1215 1219 1221 1229 1229 1230 1231 1231 1236 1238 1239 1242 1247 1264
Stichwortverzeichnis
! <APPLET> 928 /* 96 /** 96, 1259 // 96 2D-API 553, 789 7-Segment-Anzeige 742 100 % Pure Java Initiative 38 ?-Wildcard 380 @author 96, 1260 @deprecated 1253, 1260 @exception 96, 1260 @param 96, 1260 @return 96, 1260 @see 96, 1260 @since 1260 @version 96, 1260
A Abgeleitete Klasse 158 Abhängigkeiten zwischen den Quelldateien 1252 Ableiten einer Klasse 177 Ableitungsbäume 159 abs 386, 418–419 absbottom 930 absmiddle 930 abstract 191, 200 Abstract Windowing Toolkit 46, 531 AbstractButton 796, 852 AbstractList 350, 353 AbstractMap 362 AbstractSet 359 AbstractSpinnerModel 850 AbstractTableModel 897 Abstrakte Factory 240 Abstrakte Klassen und Polymorphismus 191 Abstraktion 156 Accelerator-Taste 819
Stichwortverzeichnis accept 490, 1139 Accessibility-API 789 acos 417 action 610 Action-Ereignisse 617 ActionEvent 611, 617, 669, 716 ACTION_EVENT_MASK 628 Action-Events 668 ActionListener 617, 658, 669, 716 actionPerformed 617, 669, 716 activeCaption 567 activeCaptionBorder 567 activeCaptionText 567 activeCount 527 Adapterklasse 610, 614 add 352, 358, 394, 419, 658–659, 682, 685, 711, 725, 817–817, 823, 826, 909, 1218 addActionListener 617, 669, 716, 819 addAdjustmentListener 618, 732, 866 addAll 352 addChangeListener 870 addColumnSelectionInterval 894 addComponentListener 616, 636 addContainerListener 616 addElement 337 addFocusListener 614, 647 addImage 755 addItem 726, 864 addItemListener 618, 717, 726 Addition 117 addJMenuBar 817 addKeyListener 601, 615, 621, 649 addListSelectionListener 861 addMouseListener 615 addMouseMotionListener 616 addPoint 541 addPropertyChangeListener 1075 addRowSelectionInterval 894 addSelectionInterval 860 addSelectionPath 914 addSelectionPaths 914 addSeparator 659, 817, 826 addTab 885 addTableModelListener 896 addTextListener 618, 722 addTreeSelectionListener 912 addVetoableChangeListener 1080 addWindowListener 536, 538, 617, 633 Adjustable 731, 735
1300
Adjustment-Ereignisse 618, 732 AdjustmentEvent 611, 618, 732 ADJUSTMENT_EVENT_MASK 628 AdjustmentListener 618, 732 adjustmentValueChanged 618, 732 Adler32 475 after 394 Aggregation 160 aiff-Format 47, 933, 1205 ALAW 1211 Alexander, Christopher 233 Aliasing 1232 ALIGN 930 ALL 1021 AllPermission 1198 ALT 929 ALTER TABLE 1018 AM_PM 389 anchor 693 and 346, 1020 andNot 346 Andreessen, Marc 37 Anhalten eines Threads 505 Animation 759, 936 Anlegen eines Fensters 533 Annotation 1047, 1107, 1110 Anonyme Klassen 624 ant 315 Anweisungen 93, 133, 297 Anweisungsobjekt 992 Anwendungsprotokolle 1122 ANY 1021 Apache-Server 1138 API 82 append 276, 584, 724, 1230 Applet 302, 595, 923, 1256 AppletContext 329, 945 Applet-Kontext 944 Applets 45, 302, 921, 921, 923, 925–926, 943 AppletSecurityException 955 APPLET-Tag 45, 928, 1264 appletviewer 64, 302, 932, 944, 1255, 1255 Application Programming Interface 82 application/octet-stream 1149 application/x-java-jnlp-file 323, 326, 1149 Applikationen 301 Applikations-Server 989 applyPattern 427 ARCHIVE 929, 1194, 1264
Stichwortverzeichnis Archivierungswerkzeug 1263 Arial 554 ArithmeticException 294 Arithmetische Operatoren 117 Array 104, 106, 1042 arraycopy 403 ArrayIndexOutOfBoundsException 403 ArrayList 350, 353, 1235 Arrays 410–411 ArrayStoreException 403 ASC 1021 asin 417 assert 40, 44, 144 assert-Anweisung 144 AssertionError 144 Assoziationen 160 Assoziativitätsregeln 115 atan 417 ATM 1122 Attribute 157 au-Dateien 47 AudioClip 933 AudioFileFormat 1205 AudioFormat 1205 AudioInputStream 1207 AudioSystem 1205 au-Format 933, 1205 Aufruf einer Methode 165 Aufzählungstypen 229 Ausdrucksanweisungen 135 Ausdrücke 44 Ausführungszeitpunkt 1058 Ausgabe von Sound 933 – einfache 66 Ausgabeformatierung 278 Ausgabe-Streams 441 Auslösen einer Ausnahme 285, 294 Ausnahmen 263, 285 Auswertungsreihenfolge 116 -author 1262 Autoboxing 228 Auto-Commit-Modus 1012 Automatisches Speichermanagement 44 AUTO_RESIZE_ALL_COLUMNS 892 AUTO_RESIZE_LAST_COLUMN 892 AUTO_RESIZE_NEXT_COLUMN 892 AUTO_RESIZE_OFF 892 AUTO_RESIZE_SUBSEQUENT_COLUMNS 892 Autounboxing 228
available 470 AWT 46, 531 AWTEvent 611, 628 AWTPermission 1198
B Baratz, Alan 37 baseline 930 BasicService 329 Basisklasse 158 BDK 1066 Bean Development Kit 1066 Beanbox 1066 Bean-Customizer 1095 BeanInfo 1091 BeanInfo-Klasse 1091 Beans 1057 Bedingtes Kompilieren 137 before 394 Behandeln einer Ausnahme 285 Beschleunigertasten 663, 819 BETWEEN 1021 BevelBorder 829 Bezeichner 97 BigDecimal 418 BigInteger 418 Bildschirmflackern reduzieren 772 Binäre Suche 370 binarySearch 370, 410 bind 1161 Bindungsregeln 115 Bitmap laden und anzeigen 753 BitmapComponent 757 BitSet 345 Bitweise Operatoren 120 black 564 _blank 945 Blobs 987 Bloch, Joshua 229 Block 133, 298 BLOCK_DECREMENT 732 BLOCK_INCREMENT 732 blue 564 bmp-Format 753 BOLD 555 Book 583 boolean 98, 99, 110, 136, 224, 224 BooleanControl 1208 Boolean.TYPE 1029
1301
Stichwortverzeichnis booleanValue 225 Bootstrap Classes 306 Bootstrap Loader 1061 Border 795 BorderFactory 795, 805 BorderLayout 689 Borland 38 BOTH 693 BOTTOM 822, 930 Bound Properties 1074 Bounded Wildcards 381 BPM 1218 break 44, 138, 140, 292 Breite eines Zeichens 559 BufferedInputStream 473, 1238 BUFFERED_OPTION 832 BufferedOutputStream 464, 1237 BufferedReader 453, 456 BufferedWriter 442, 445, 1237 Bug Parade 52, 85 Button 715 ButtonGroup 823, 857 ButtonModel 857 byte 98, 100, 223, 223 ByteArrayInputStream 472, 983 ByteArrayOutputStream 463, 983 Bytecode 313 Bytecode-Verifier 1189 Byte-Streams 440 ByteToCharConverter 454, 1239 Byte.TYPE 1029
C [C 1246, 1265 CA 1188 Cäsarische Verschlüsselung 1171 Cafe 38 CAFEBABE 482 Calendar 387, 396 call by reference 167, 227 call by value 166 Callback-Methoden 302 Call-Level-Interface 988 cancel 578 CancelButton 852 canRead 331, 489 Canvas 736, 741, 757 canWrite 331, 489 capacity 277
1302
CardLayout 682 CaretListener 844 case 138 catch 286 catch-or-throw-Regel 293 ceil 418 CENTER 683, 686, 693, 714, 822 Certificate 1184 Certification Authority 1188 CGI-Script 944 ChangeEvent 870, 887 ChangeListener 870, 887 char 98–99, 99, 224, 265 Character 224 Character-Streams 440, 1237 Character.TYPE 1029 CharArrayReader 453 CharArrayWriter 442, 444, 444 charAt 267 CharSequence 277, 1233 CharToByteConverter 442, 1237 CHAR_UNDEFINED 651–652 charValue 225 charWidth 559 Checkbox 716 CheckboxGroup 718 CheckboxMenuItem 657, 660 CheckedInputStream 475 CheckedOutputStream 469 checkRead 1199 Checksum 475 Choice 725 class 163, 319, 1024 ClassCastException 677, 1024 .class-Dateien 311–312, 313, 482, 1252 classes.zip 306 Classloader 45, 1027, 1060 .class-Notation für Klassenobjekte 1034 ClassNotFoundException 1024 CLASSPATH 57, 73, 75, 305–308, 310, 317, 1252, 1254, 1262, 1265, 1268 clear 345, 361 clearRect 547 clearSelection 860, 894, 914 -client 1254 Client-Hotspot 1254 Client-Server-Beziehung 1125 Clip 1206 Clipboard 676
Stichwortverzeichnis ClipboardOwner 678 ClipboardService 329 Clipping-Region 548 clipRect 548 clone 109, 167, 179, 209, 983 Cloneable 983 CloneNotSupportedException 209 close 441, 453, 462, 480, 992, 1209, 1214 Cluster 1010 CODE 928 CODEBASE 929 Coding Conventions 70 collapsePath 915 Collection 350 Collection-API 39, 49, 263, 349 Collections 368 Color 564 columnAtPoint 907 Comboboxen 725 commit 1012 CommPort 591 CommPortIdentifier 590 Community Source Licence 39 Comparable 202–202, 364 Comparator 364, 411 compare 364 compareTo 202, 269, 364 Compiler 1251 comp.lang.java 84 Component 595 ComponentAdapter 614 componentAdded 617 Component-Ereignisse 616 ComponentEvent 611, 616, 635 COMPONENT_EVENT_MASK 628, 744 Component-Events 635 componentHidden 616, 636 ComponentListener 616, 635 componentMoved 616, 636 componentRemoved 617 componentResized 616, 636 componentShown 616, 636, 745 ComponentUI 831 Composite-Pattern 250 CompoundBorder 829 CompoundControl 1208 com.sun.image.codec.jpeg 305 connect 523 Connection 992
Constrained Properties 1079 Constraints-Objekt 683 Constructor 1036 Container 595, 682 ContainerAdapter 614 Container-Ereignisse 616 ContainerEvent 616 ContainerListener 616 contains 342, 351 containsAll 351 containsKey 342, 361 containsValue 361 Content Handler Factories 1150 ContentPane 794, 800 CONTIGUOUS_TREE_SELECTION 911 continue 44, 140, 292 control 568, 1206, 1208 controlDkShadow 568 controlHighlight 568 controlLtHighlight 568 controlShadow 568 controlText 568 Control.Type 1208 CoolEdit 933 copyArea 547 CORBA 49, 305, 1155 cos 417 Courier 554 Courier New 554 -cp 75, 317, 1254 CRC32 475 CREATE INDEX 1018 CREATE TABLE 1018 createBevelBorder 805 createCompoundBorder 805 createCustomCursor 604 createEmptyBorder 830 createEtchedBorder 805 createImage 321, 754, 777 createLineBorder 830 createNewFile 497 createStatement 992 createTempFile 496 createToolTip 830 createTrack 1218 CROSSHAIR_CURSOR 604 currentTimeMillis 400 Cursor 603 cyan 564
1303
Stichwortverzeichnis
D -d 318, 1255, 1262, 1268 -da 146 Dämon 501 Dangling else 136 darkGray 564 Data Definition Language 995 DatabaseMetaData 1001 DataFlavor 677 DataFlavors 676 DatagramPacket 1123 DatagramSocket 1123 DataInput 474 DataInputStream 465, 474 DataLine 1206 DataLine.Info 1211 DataOutput 465 DataOutputStream 465 Date 387, 396–396 DateFormat 430 Datei- und Verzeichnis-Handling 263, 487 Dateiformate 47–47, 60, 313, 753–753, 933–933, 1263 Datenbankzugriffe mit JDBC 961, 987 Datenflussanalyse 98, 116 Datentypen 93, 95, 97, 108 Datums-/Zeitarithmetik 394 Datumswerte 387 DAY_OF_MONTH 389 DAY_OF_WEEK 389–390 DAY_OF_WEEK_IN_MONTH 389 DAY_OF_YEAR 389 DayTime 1131 dB 1208 -debug 1256 Debugger 1256 Debug-Grafik 786, 831 DebugGraphics 831–831 DecimalFormat 427 deep copy 209, 983 default 138, 430 DefaultButton 852 DEFAULT_CURSOR 604 DefaultDesktopManager 813 Default-Implementierung 213 Default-Konstruktor 172, 182 DefaultListModel 861 DefaultMutableTreeNode 908 defaultPage 580 Default-Paket 311
1304
defaultReadObject 1065 DefaultTableCellRenderer 903 DefaultTableColumnModel 900 DefaultTreeModel 908, 915 DefaultTreeSelectionModel 911 Definite Assignment 98, 116 DeflaterOutputStream 468 Deklaration von Variablen 102, 134 Dekrement-Operator 102 Delegate 247 Delegate-Pattern 247, 370 Delegation 214, 247 Delegation Based Event Handling 261, 532, 609 Delegator 247 delete 276, 480, 494, 1231 DELETE FROM 995, 1019 deleteCharAt 276 deleteOnExit 497 deleteShortCut 664 delItem 729 Denial-of-Service-Attacken 330 -depend 1252 deprecated 66, 344, 465, 472, 503, 505, 554, 556, 726, 1184, 1253 -deprecation 1253 DESC 1021 deselect 728 Design-Patterns 233–235, 237–237, 244, 247, 250, 253, 257, 627 Designzeitpunkt 1058 desktop 567–568, 813 DesktopManager 813 destroy 406, 811, 926 Destruktoren 174 Device-Kontext 534 Dezibel 1208 Dialog 533, 595, 682, 702 Dialog, modaler 701 Dictionary 341 digest 1174 DigestInputStream 470 DigestOutputStream 469 Digital Signature Architecture 1179 Digitale Unterschrift 1180 Digitaler Fingerabdruck 1176 Dimension 743 -disableassertions 146, 1255 Disassembler 1265 DISCONTIGUOUS_TREE_SELECTION 911
Stichwortverzeichnis dispose 573, 598 DISPOSE_ON_CLOSE 815 divide 419 Division 117 DNS 1125 DNS-Konfiguration 1257 do 44, 139–140 Doclets 1262 doClick 852 Document 845 DocumentListener 845 Dokumentation des JDK 57, 82 Dokumentationsgenerator 1259 Dokumentationskommentare 96, 96, 1259 Domain Name System 1125 DO_NOTHING_ON_CLOSE 815 Doppelpufferung 776 double 98, 101, 101, 223, 223 Double.TYPE 1029, 1035 doubleValue 225 DownloadService 329 Dr. Dobb's Journal 87 Drag-and-Drop API 39, 789 drawArc 544 drawBytes 551 drawChars 551 drawImage 753–754, 777 drawLine 539 drawOval 542 drawPolygon 541 drawPolyline 541 drawRect 540 drawRoundRect 540 drawString 551 DriverManager 991 DROP INDEX 1018 DROP TABLE 1018 Drop-Down-Listboxen 725 Drucken 529, 571 DSA 1179 DST_OFFSET 390–390 Duff's Device 138 Dynamische Datenstrukturen 98, 300 Dynamische Methodensuche 180 Dynamisches Binden 180 Dynamisches Laden von Klassen 1024
E -ea 146
EAST 683, 693, 822 ECHO-Service 1133 editCellAt 895 Eiffel 148 Eigenzertifikat 1183 Einerkomplement 120 Einfache Vererbung 177 Eingaben, einfache 67 Eingabe-Streams 452 Einschränkende Konvertierungen 109 Einweg-Hashfunktion 1173 Einzeilige Kommentare 96 elementAt 337 elements 338, 342 else 69, 137 EMBED 957 Embedded SQL 988 empty 340 EmptyBorder 829 EmptyStackException 340 -enableassertions 146, 1255 enableEvents 628, 673, 744 end 573 Ende des Programms 501 endsWith 269 Entity Bean 1104 Entry 362 entrySet 362 Entschlüsseln 1169 Entwicklungszyklus 65, 312 Entwurfsmuster 233 enum 230 EnumControl 1208 enumerate 526 Enumeration 49, 338, 355 EnumMap 230 EnumSet 230 equals 109, 125, 179, 269, 341 equalsIgnoreCase 269 ERA 389–390 Ereignisempfänger 609, 612 Ereignisquellen 609, 613 Ereignistypen 611 Ergebnistyp eines Ausdrucks 117 err 399 Erreichbare Anweisungen 137 Error 289 ERROR_MESSAGE 807 Ersetzen von Zeichenketten 271–271
1305
Stichwortverzeichnis Erste Gehversuche 59 Erstellen eigener Pakete 309 Erweiternde Konvertierungen 109 Escape-Kommandos 1012 Escape-Sequenzen 99 ESQL 988 EtchedBorder 829 Ethernet 1122 Event 611 Event Sources 609 EventListener 609, 610, 612 EventObject 611–611, 1074 Exception 289 Exceptions 45, 263, 285 exec 404 executeQuery 992, 993 executeUpdate 992, 995 exists 489, 1021 exit 399 exitValue 406 EXKLUSIV-ODER-Operator 119 exp 418 expandCapacity 1244 expandPath 915 ExperimentalWebServer 1148 Exponentialfunktion 418 extends 177, 381 Extension Framework 39 Externalizable 973
F Factory 237–238 Factory-Pattern 237 false 97–98, 99 FDDI 1122 Fehlerobjekt 288 Fensterklassen 595 Fenstertitel 602 Field 1038 FieldPosition 431 File 444, 487 FileContents 331 FileDescriptor 444 FileDialog 595 FileInputStream 471, 969 FilenameFilter 490 FileNotFoundException 454, 471, 480 FileOpenService 329–330 FileOutputStream 462, 964, 1237
1306
FilePermission 1198 FileReader 453 FileSaveService 329 file.separator 397, 440 FileWriter 442, 442 fill 410, 693 fillArc 545 fillOval 545 fillPolygon 545 fillRect 545 fillRoundRect 545 FilterInputStream 473 FilterOutputStream 464 FilterReader 453, 459 FilterWriter 442, 445, 449 final 179, 181, 186, 188, 1234 finalize 174 finally 292 Fingerprint 1176 firePropertyChange 1076 Firewall 1128 first 365 First Person, Inc. 36 firstElement 337 FLASH_OPTION 832 Fließkommazahlen 101 float 98, 101, 101, 224, 224 FloatControl 1208 FloatControl.TYPE 1208 Float.TYPE 1029 floatValue 225 floor 418 FlowLayout 685 flush 441, 445, 462, 464 Focus Model Specification 836 FocusAdapter 614 Focus-Ereignisse 614 FocusEvent 614 FOCUS_EVENT_MASK 744 Focus-Events 646 focusGained 615, 647, 744 FocusListener 614 focusLost 615, 647, 744 Fokus-Subsystem 745 Font 553 Font-Informationen 558 FontMetrics 559 Font-Metriken 559 font.properties 555
Stichwortverzeichnis for 44, 140, 140 foreach 142 format 278, 428, 430 Formatierte Ausgabe 278 Formatierung 427, 430 Formatter 278 formatTo 281 forName 1024, 1034 for-Schleife 139, 142 Fragezeichenoperator 122 Frame 533, 558, 595, 682 friend 44 Füllmodus 545 FULL 430 Funktionstasten 650 Funktionszeiger 210, 1033
G -g 1252, 1257, 1268 Gamelan 38 Gamma, Erich 233 Garbage Collector 109, 400 Garbage-Collector 44 gc 400, 1240 gcd 419 Gebundene Eigenschaften 1074 Gebundene Wildcards 381 General Public License 41 Generics 371 Generische Klassen 371 Generische Methoden 383 Generizität 204 Gesampelter Sound 1204 Geschachtelte Schleifen 140 get 342, 345, 361, 389, 1039, 1044, 1136, 1148 getAbsolutePath 488 getActionCommand 668, 716, 721 getAddress 1128 getAdjustable 732 getAdjustmentType 732 getAnchorSelectionIndex 860 getAnnotation 1054 getAnnotations 1054 getApplet 945, 949 getAppletContext 945 getAppletInfo 928 getApplets 945, 949 getAscent 560 getAsText 1095
getAudioClip 933 getAudioFileFormat 1207 getAudioInputStream 1207 getAvailableFontFamilyNames 556 getBestCursorSize 604 getBlockIncrement 731, 866 getBlue 564 getBoolean 994, 1044 getBounds 549, 599 getBuffer 444 getBundle 432 getByName 1129 getByte 994, 1044 getBytes 994 getCaretPosition 721, 844 getCertificate 1184 getChar 1044 getChars 1233 getCheckboxGroup 719 getChecksum 469, 475 getChildAt 909 getChildCount 909 getClass 382, 516, 1024 getClickCount 640 getClip 549 getClipBounds 549 getCodeBase 933 getColor 565 getColumnClass 896 getColumnCount 894, 896, 1012 getColumnModel 906 getColumnName 896, 1012 getColumns 720 getColumnType 1012 getComponent 636, 683 getComponentAt 886 getComponentCount 683 getComponents 683 getConnection 991 getConstructor 1036 getConstructors 1036 getContent 1150 getContentPane 794, 801 getContents 678 getControl 1208 getControls 1208 getCopies 578 getCountry 426 getCurrent 719
1307
Stichwortverzeichnis getCustomEditor 1098 getDate 994 getDateInstance 430 getDateTimeInstance 430 getDeclaredAnnotations 1054 getDeclaredField 1039 getDeclaredFields 1039 getDeclaredMethods 1028 getDefault 393, 426–426 getDefaultRenderer 903 getDefaultToolkit 556 getDescent 560 getDocument 845 getDocumentBase 933 getDouble 994, 1039, 1044 getEchoCharacter 724 getEditingColumn 895 getEditingRow 895 getElements 857 getEnabled 819 getErrorCode 995 getErrorStream 406 getFamily 558 getField 1039 getFields 1039 getFilePointer 481 getFloat 994, 1044 getFont 554 getFontList 556 getFontMetrics 559 getGlassPane 801 getGraphics 572, 777 getGreen 564 getHAdjustable 735 getHeight 560, 579, 758 getHorizontalAlignment 842, 852 getHorizontalTextPosition 841, 852 getHostAddress 1128 getHostName 1128 getIcon 1091 getID 612, 633 getImage 753 getImageableHeight 579 getImageableWidth 579 getImageableX 579 getImageableY 579 getInputStream 331, 406, 591, 1131 getInsets 535, 552 getInstance 1177
1308
getInt 994, 1039, 1044 getItem 659, 726–727 getItemCount 659, 726 getItemSelectable 717, 727 getJobName 578 getKey 362 getKeyChar 612, 650, 652, 745 getKeyCode 620, 650, 652 getKeys 433 getKeyStroke 819 getLabel 660, 716–717 getLanguage 426 getLastPathComponent 912 getLayeredPane 801 getLeading 560 getLeadSelectionIndex 860 getLeadSelectionPath 911 getLeadSelectionRow 912 getLength 331, 1044 getLine 1207 getLineCount 847 getLineEndOffset 847 getLineNumber 458 getLineOfOffset 847 getLineStartOffset 847 getLineWrap 848 getLocalGraphicsEnvironment 556 getLocalHost 1129 getLocation 599, 705 getLong 994, 1044 getMajorTickSpacing 869 getMaximum 731, 866, 1208 getMaximumCursorColors 604 getMaximumSize 743, 835 getMaxReceivers 1214 getMenu 658 getMessage 288 getMetaData 1001, 1011 getMethod 1034 getMethodDescriptors 1092 getMethods 1028 getMicrosecondPosition 1214 getMidiDevice 1213 getMidiDeviceInfo 1213 getMidiFileTypes 1222 getMinimum 731, 866, 1208 getMinimumSize 742, 835 getMinorTickSpacing 869 getMixer 1207
Stichwortverzeichnis getMixerInfo 1207 getMnemonic 818, 852 getModel 861 getModifiers 612, 1028, 1036 getName 331, 488, 526, 590, 678, 1028, 1036 getNewLeadSelectionPath 912 getNewValue 1075 getNextException 995 getNumberInstance 427 getNumberOfPages 583 getObject 435 getOldLeadSelectionPath 912 getOldValue 1075 getOrientation 579 getOutputStream 331, 406, 591, 1131 getPageDimension 573 getPageFormat 583 getPageResolution 573 getPaper 579 getParameter 930 getParameterInfo 927 getParameterTypes 1029, 1036 getParent 488, 526, 909 getPath 488, 912 getPathToRoot 916 getPoint 612 getPortIdentifier 590 getPortIdentifiers 590 getPortType 590 getPosition 639 getPreferredSize 735, 742, 835 getPrintable 583 getPrinterJob 578 getPrintJob 572 getPriority 526 getProperties 398 getProperty 344, 398 getPropertyDescriptors 1092 getPropertyName 1075 getPublicKey 1184 getReceiver 1214 getRed 564 getResource 1060 getResourceAsStream 319 getRootPane 801 getRowCount 894, 896 getRuntime 404 getScreenResolution 573 getScreenSize 600
getScrollPosition 736 getSelectedColumn 893 getSelectedColumns 893 getSelectedComponent 886 getSelectedIndex 726, 860, 863, 886 getSelectedIndexes 728 getSelectedIndices 860 getSelectedItem 726, 863 getSelectedItems 728 getSelectedRow 893 getSelectedRows 893 getSelectedText 721, 844 getSelectedValue 860 getSelectedValues 860 getSelection 857 getSelectionEnd 721, 844 getSelectionMode 860 getSelectionModel 906–907, 911 getSelectionPath 911 getSelectionPaths 911 getSelectionRows 912 getSelectionStart 721, 844 getSequence 1221 getSequencer 1217 getShort 994, 1044 getSize 552, 558, 599 getSource 611, 633, 721 getSQLState 995 getState 601, 661, 717–717, 823 getString 435, 994 getStringArray 435 getStyle 558 getSuperClass 1041 getSynthesizer 1213 getSystemClipboard 677 getTabCount 886 getTableCellRendererComponent 905 getTableName 1012 getTabPlacement 885 getTabSize 848 getTags 1096 getter-Methoden 1061 getText 715, 720, 844 getThreadGroup 526 getTime 396, 994 getTimeInstance 430 getTimestamp 994 getTitle 602 getToolkit 754
1309
Stichwortverzeichnis getToolTipText 830 getTransactionIsolation 1013 getTransferData 676 getTransferDataFlavors 676 getType 1039 getTypeInfo 1013 getUnitIncrement 731, 866 getUserName 578 getUserObject 909 getVAdjustable 735 getValue 362, 731–732, 866, 872, 1095, 1208 getValueAt 894, 896 getValueIsAdjusting() 866, 870 getVariant 426 getVerticalAlignment 842, 852 getViewportSize 736 getVisibleAmount 731, 866 getWhen 640 getWidth 579, 758 getWindow 633 getWrapStyleWord 848 getX 639 getY 639 gif-Format 753 gk.util 308, 1000 GlassPane 800 Gleichheitsoperator 118 Globale Funktionen 164 GoF – Gang of Four 234 GoldWave 933 Gosling, James 36 Grafikkontext 534 Graphical User Interface 1057 Graphics 534 Graphics2D 582 GraphicsEnvironment 556 gray 564 green 564 Green-OS 36 Green-Projekt 36 GregorianCalendar 388 GridBagConstraints 692 GridBagLayout 692 gridheight 693 GridLayout 687 gridwidth 693 gridx 693 gridy 693 Größergleich-Operator 118
1310
Größer-Operator 118 GROUP BY 1021 Grundlinie 559 GUI-Designer 1058 GZIPInputStream 476 GZIPOutputStream 468
H handleEvent 609 handleGetObject 433 Hardware-Voraussetzungen 55 hasBeenExpanded 915 hashCode 179, 351, 1173 Hash-Funktion 341, 362 HashMap 49, 359, 362 HashSet 359 Hashtable 341, 362 hasMoreElements() 49, 338 hasNext 356 hasPrevious 358 HAVING 1021 HEAD 1148 headSet 365 Heavyweight Components 786 HEIGHT 928–928 Helm, Richard 233 -help 1254 Helvetica 554 HIDE_ON_CLOSE 815 Himmelsrichtungen 689 Hintergrundfarbe 602 Hintergrund-Thread 501 Höhe einer Zeile 559 HORIZONTAL 693, 731, 822 HORIZONTAL_SCROLLBAR_ALWAYS 878 HORIZONTAL_SCROLLBAR_AS_NEEDED 878 HORIZONTAL_SCROLLBAR_NEVER 878 HORIZONTAL_SPLIT 882 Host-ID 1123 Host-Namen 943 hosts 1257 Host-Variablen 988 HotJava 37 HotJava Views 38 HotSpot 39, 1254 HOUR 389 HOUR_OF_DAY 389 hprof 1241 HSPACE 930
Stichwortverzeichnis HSQLDB 997 HTML-Adresse 943 HTML-Ausgabe des Buchs 87 HTMLConverter 959 HTML-Dokument 928 http 944 Hybride Kryptosysteme 1179 Hypersonic SQL 1002
I [I 1246 I18N 424 IAB 1126 IBM 38 ICMP 1122 Icon 602, 821 ICON_COLOR_16x16 1091 ICON_COLOR_32x32 1091 ICONIFIED 601 ICON_MONO_16x16 1091 ICON_MONO_32x32 1091 if 44, 135, 136 if-Anweisung 135 if-else 135 IIOP 1155 IllegalArgumentException 147, 353 IllegalThreadStateException 406 Image 753 ImageIcon 822 ImageObserver 754 immutable 227, 359 Immutable-Pattern 235 Impedance Mismatch 1103 Implementierung eines Interface 198 implements 198 import 68, 302, 303 import static 208 Import von java.lang 303 in 399, 1021 inactiveCaption 567 inactiveCaptionBorder 567 inactiveCaptionText 567 .inc-Dateien 537 indexOf 270 IndexOutOfBoundsException 291 Indizierte Eigenschaft 1062 InetAddress 1128 inetd.conf 1140 InflaterInputStream 476
info 568 Informationen im Internet 84 INFORMATION_MESSAGE 807 infoText 568 init 811, 923, 925 Initialisieren 103, 105 initSign 1183 initVerify 1183 Inkrement-Operator 102 Inner Classes 217 Input Method Framework 789 InputEvent 612, 649 InputStream 461, 461, 470 InputStreamReader 453, 454 insert 276, 724, 909 INSERT INTO 995, 1005, 1019 insertElementAt 337 insertItemAt 864 insertNodeInto 916 insertSeparator 659 insertTab 885 insets 694 Installation 56 instanceof 126, 337, 383, 722 InstantDB 997 Instanzmerkmale 157 Instanzvariablen 102, 104, 157 int 98, 100, 223 Integer 223 Integer.TYPE 1029–1029 Integer einlesen 68 Integrale Typen 100 interface 197 Interfaces 177, 197, 237 Internationalisierung 424 Internet 84 Internet Activity Board 1126 Interpreter 1253 interrupt 503 interrupted 503 InterruptedException 505, 756 InterruptedIOException 1135 Introspection 1024, 1059 Introspector 1090 intValue 225 invalidate 835 InvalidClassException 972 Invalidierung 835 Invariante 144
1311
Stichwortverzeichnis invoke 1029, 1035 IOException 344, 443, 480 IP-Adresse 1123 ipadx 694 ipady 694 IP-Protokoll 1122 IPv6 1124 IS NOT NULL 1020 IS NULL 1020 isAbsolute 489 isAbstract 1028 isAlive 506, 506 isAltDown 650 isAnnotationPresent 1054 isCancelled 578 isCellEditable 896 isCollapsed 914 isControlDown 650 isControlSupported 1208 isConversionSupported 1207 isDataFlavorSupported 676 isDirectory 489 isDoubleBuffered 834 isEditable 721, 863 isEditing 895 isEmpty 336, 351, 361 isEnabled 599, 660, 852 isEnabledAt 886 isExpanded 914 isExplicit 1028 isFile 489 isFinal 1028 isFocusable 745 isFocusTraversable 744 isHidden 489 isIndeterminate 874 isInterface 1028 isInterrupted 503 isLeaf 909 isMetaDown 650 isModal 702 isNative 1028 ISO/OSI-7-Schichten-Modell 1122 ISO-639 425 ISO-3166 425 ISO-8859-1 95 isOpen 1214 isPaintable 1098 isPathSelected 912
1312
isPopupTrigger 673, 827 isPrivate 1028 isProtected 1028 isPublic 1028 isResizable 703 isRootVisible 908 isRunning 1209, 1218 isSelected 823, 855 isSelectedIndex 860 isSelectionEmpty 860, 912 isShiftDown 650 isStatic 1028 isStrict 1028 isSynchronized 1028 isTemporary 646 isTransient 1028 isVisible 915 isVolatile 1028 ITALIC 555 Item-Ereignisse 618, 726 ItemEvent 611, 618, 717, 726 ItemListener 618, 717, 726 itemStateChanged 618, 717, 726 Iterable 142 Iterator 49, 356, 356 Iterator-Pattern 244
J -J 1248 J++ 38 JAAS 1180 Jakarta 315 JApplet 794, 811 -jar 318, 1263 JAR-Archiv 929 JarOutputStream 468 jarsigner 1193, 1267 java 64, 1253 Java 2 Platform 39 Java 2 SDK 42 Java 2D API 39, 46 Java 5 Enterprise Edition 42 Java 6 Standard Edition 42–42 Java Authentication and Authorization Service 1180 Java Beans 38, 49, 304, 1074 Java Beans Homepage 1066 Java Card API 38 Java Card Edition 42 Java Communications API 571, 589
Stichwortverzeichnis Java Community Process 41 JAVA Cryptography Extension 1180 Java Data Objects 1104 Java Database Connectivity 49, 988 Java Developer's Connection 38, 85 Java Developer's Journal 87 Java Development Kit 37 Java Foundation Classes 39, 46, 785 Java Media Framework 1204 Java Micro Edition 42 Java Naming and Directory Interface 1166 Java Network Launching Protocol 323 Java Platform Debugger Architecture 1256 Java Runtime Environment 57, 1255 Java Secure Socket Extension 1180 Java Server Pages 53 Java Spektrum 87 Java Virtual Machine Profiler Interface 1241 Java Web Start 322, 1149 Java Workstations 322 Java World 87 java.applet 304 java.awt 304, 533 java.awt.datatransfer 676 java.awt.event 611 java.awt.image 47 java.awt.print 577 javac 61, 64, 1252 java.class.path 397 java.class.version 397 JAVA_COMPILER 1248, 1254–1254 .java-Dateien 60, 64, 311, 1252 javadoc 96, 1259 javadt 1256 java_g 1248, 1255 java.home 397 javaidl 49 java.io 304, 440 javakey 1181 java.lang 303–304 java.lang.Iterable 142 java.lang.Math 208 java.lang.ref 304 java.lang.reflect 304, 1032 JavaLobby 86 java.math 304, 418 java.net 304, 1128 java.nio 304 JavaOne 38
JavaOS 1.0 38 javap 1265 Java-Plugin 788, 957 java.policy 1196–1196 java.rmi 304, 1157–1157 java.rmi.server 1158 JavaScript 45 java.security 304 java.security.cert 1184 JavaSoft 37, 85 java.specification.name 397 java.specification.vendor 397 java.specification.version 397 java.sql 304, 991 JavaStation 38 java.text 278, 304, 427 Java-Usergruppen 38 java.util 304, 349, 611 java.util.Deque 340 java.util.Formattable 281 java.util.Formatter 278 java.util.jar 468 java.util.zip 468 java.vendor 397 java.vendor.url 397 java.version 397 java.vm.name 397 java.vm.specification.name 397 java.vm.specification.vendor 397 java.vm.specification.version 397 java.vm.vendor 397 java.vm.version 397 javaw 72, 1253 javax.accessibility 305 javax.comm 590 javax.crypto 305 javax.imageio 305 javax.jnlp 330 javax.naming 305 javax-Pakete 304 javax.print 305 javax.security.auth 305 javax.sound 305 javax.sound.midi 1212 javax.sound.sampled 1205 javax.swing 305, 793 javax.swing.event 861, 870 javax.swing.plaf 831 javax.swing.table 896
1313
Stichwortverzeichnis javax.swing.text 843 javax.swing.tree 908 javax.xml 305 JButton 796, 851 JCE 1180 JCheckBox 855 JCheckBoxMenuItem 823–823 JComboBox 863 JComponent 794, 829 JCP 41 jdb 1256 JDBC 38, 49, 961, 987, 988 JDBC-ODBC-Bridge 988 JDBC-Versionen 990 JDC 85, 85 JDesktopPane 813 JDialog 794, 805 JDK 37, 42 JEditorPane 847 JFC 46 JFormattedTextField 846 JFrame 247, 794, 799 JInsight 1249 JInternalFrame 813 JIT 1227, 1254 JLabel 795, 841 JLayeredPane 800–801, 839 JList 795, 859 JMenu 817 JMenuBar 800 JMenuItem 818 JNDI 1166 jnlp-Datei 324 Johnson, Ralph 233 join 505, 507 JOptionPane 806 Joy, Bill 36 JPA 961, 1103 JPanel 794, 839 JPasswordField 795, 847 JPDA 1256 jpeg-Format 753 JPopupMenu 826 JProbe 1249 JProgressBar 872 JRadioButton 857 JRadioButtonMenuItem 823 JRE 42, 57, 1255 JRootPane 800
1314
JScrollBar 865 JScrollPane 796, 848, 877 JSlider 868 JSpinner 849 JSplitPane 881 JSSE 1180 JTabbedPane 885 JTable 888 JTextArea 847 JTextComponent 843 JTextField 843 JTextPane 847 JToggleButton 855 JToolTip 830 JTree 907 Just-In-Time-Compiler 1227, 1254 JVMPI 1241 JWindow 794, 803
K Kapselung 157 -keepgenerated 1268 Kestrel 40 KeyAdapter 614, 622, 624 Key-Ereignisse 615 KeyEvent 612, 615, 620, 649 KEY_EVENT_MASK 744 Key-Events 649 KeyListener 615, 620, 622, 649 -keypass 1267 keyPressed 615, 620, 624, 650, 652, 745 keyReleased 615, 620, 650 keys 342 keySet 362 .keystore 1181, 1184, 1267 KeyStroke 819 keytool 1181, 1195, 1266 keyTyped 615, 620, 650, 652 Klammerung 116 Klartext 1169 Klassen 300 Klassendiagramme 90 Klassenmethoden 189, 300 Klassenobjekt 1024 Klassenvariablen 102, 104, 187 Kleinergleich-Operator 118 Kleiner-Operator 118 Kommentare 96 Komposition 160
Stichwortverzeichnis Konstanten 188, 207 Konstruktoren 171 Konstruktorenverkettung 172, 181 Kontextmenüs 673, 826 Kontext-URL 945 Konvertierungen auf primitiven Datentypen 110 Konvertierungsfunktionen 272 Koordinatensystem 534 Kopieren einer Datei 471 – von Flächen 546 – von Objekten 983 Kreisbögen zeichnen 544 Kreise zeichnen 542 Kritischer Bereich 516 Kryptoanalyse 1170 Kryptografie 961, 1169, 1170 Kryptologie 1170
L -l 1265 L10N 424 Label 714 Ladefaktor 1230 Länge der Zeichenkette 267 LANDSCAPE 579 last 365 lastElement 337 lastIndexOf 270 lastModified 489 Late Binding 161 Laufzeitfehler 285 LayeredPane 800 Layoutmanager 684 LEADING 822 Lebensdauer 103, 183 Leere Anweisung 133 LEFT 686, 714, 822, 930 length 106, 108, 268, 268, 277, 481 Lexikalische Elemente 95 LIFO-Prinzip 339 lightGray 564 Lightweight Components 786 LIKE 1020 Line 1205 Lineare Liste 336 LineBorder 829 Line.Info 1207 LineNumberReader 453, 458 line.separator 397, 440, 445
Linien- oder Füllmodus 545 Linien zeichnen 539 LinkedList 350, 353, 1235 List 349, 352, 490, 728, 1161 Listboxen 728 ListDataEvent 861 Listener 261 listFiles 493, 1003 ListIterator 357 ListModel 796, 859, 861 listRoots 490 ListSelectionEvent 861 ListSelectionListener 861 ListSelectionModel 859, 889, 893 Literale 99–101 load 344 loadImage 1091 Locale 425 localhost 1130 Lockdateien 497 Löschen von Flächen 546 log 418 Logarithmus 418 Logische Operatoren 119 Logischer Typ 99 LOG_OPTION 832 Lokale Klassen 622 Lokale Variable 102, 134 Lokalisierung 424 long 98, 100, 223, 223, 430 Long.TYPE 1029 longValue 225 Look-and-Feel 787, 796 lookup 330, 1161, 1164 loop 934 lostOwnership 678 Lotus 38 lowerCase 452 LOWER_LEFT_CORNER 878 LOWER_RIGHT_CORNER 878
M magenta 564 main 60, 190, 301, 923, 1253 Main-Class 318 make 315 makefile 315 makeVisible 915 MalformedURLException 944
1315
Stichwortverzeichnis Manifest-Datei 318–318, 1070 Map 349, 360 Map.Entry 362 mark 453, 471 markSupported 453, 471 MASTER_GAIN 1208 Matcher 414 Math 387, 417, 438 Mauscursor 602, 603 max 417 MAX_PRIORITY 526 MAX_VALUE 101, 226 McNealy, Scott 35 MD5 1173 MDI 812 Mediator-Pattern 627 MediaTracker 755 MEDIUM 430 Mehrdimensionale Arrays 106 Mehrfachselektion 728 Mehrfachvererbung 43, 159, 177, 197 Mehrfachverzweigung 138 Mehrstufige Client-Server-Architekturen 989 Mehrzeilige Kommentare 96 Membervariablen 157, 163 Member-Zugriff 126 menu 568, 657 MenuBar 657 Menü 602 Menüeinträge 660 Menüleiste 658 MenuItem 657, 660 MenuShortcut 663 menuText 568 Message Digest 469, 1173 Message Queue 464 MessageDigest 1173 meta-inf 318 Metainformationen 1047 Metal-Look-and-Feel 790 MetaMessage 1213 Meta-Ressourcen 85 Method 1028, 1092 MethodDescriptor 1092 Methoden 126, 157, 164, 179, 300 Metriken für Fonts 559 Meyer, Bertrand 148 Microsoft Internet Explorer 45 middle 930
1316
Middleware 989 Midi 1204, 1211 Midi-Dateien 47 MidiDevice 1213 Midi-Ereignisse 1212 MidiEvent 1213 Midi-Format 933 MidiMessage 1213 Midi-Nachrichten 1212 MidiSystem 1213 MILLISECOND 390 MIME-Spezifikation 676 MIME-Typ 1148 min 417 Minimum und Maximum 417 MINUTE 390 MIN_VALUE 101, 226 MissingResourceException 433 Mixer 1204, 1206 Mixer.Info 1207 mkdir 494 mkdirs 494 Modale Dialoge 701 Model-Delegate-Prinzip 787 Model-View-Controller-Prinzip 787 Modifier 164, 183, 1028 Modulo-Operator 117 Monitor 500 Monospaced 554 MONTH 389 MouseAdapter 614 mouseClicked 615 mouseDragged 616 mouseEntered 615 Mouse-Ereignisse 615 MouseEvent 612, 615–616, 638 MOUSE_EVENT_MASK 744 Mouse-Events 638 mouseExited 615 MouseListener 615, 638 MouseMotionAdapter 614 MouseMotion-Ereignisse 616 MouseMotion-Events 643 MouseMotionListener 616 mouseMoved 616 mousePressed 615, 744 mouseReleased 615 moveCaretPosition 844 MOVE_CURSOR 604
Stichwortverzeichnis Mozilla Firefox 45 MS-Access 997 multiple bounds 383 Multiple Document Interface 812 MULTIPLE_INTERVAL_SELECTION 859, 893 Multiplikation 117 multiply 419 Multi-Tier-Architekturen 989 Mustang 40 mutable 359 MutableTreeNode 909 MVC 627, 787
N NAME 929 Namenskonventionen 70 Namens-Service 1154 Name-Server 1125 Naming 1161 NaN 101, 101, 226 National Institute of Standards and Technology 1173 Natürliche Ordnung 364 Naughton, Patrick 35 NCSA Mosaic 36 Nebeneffekte 116, 135 Nebenläufigkeit 499 negate 419 NEGATIVE_INFINITY 101, 101, 226 Netscape 38, 45 Network Information Center 1123 Networking-API 49 Netzwerk-ID 1123 Netzwerkprogrammierung 961, 1121 Netzwerkschicht 1122 new 105, 109, 126, 163 New I/O Package 304 newAudioClip 935 newInstance 1024, 1036, 1042 newLine 445 Newsgroups 53, 84 next 356, 993 nextBytes 1177 nextDouble 386 nextElement() 49, 338 nextFloat 386 nextGaussian 387 nextIndex 358 nextInt 386 nextLong 386
NIC 1123 NICHT-Operator 119 NoClassDefFoundError 63, 64, 66 nodeChanged 916 NONE 693 NONE_OPTION 832 NORMAL 601 NORM_PRIORITY 526 NORTH 683, 693, 822 NORTHEAST 693, 822 NORTHWEST 694, 822 NoSuchElementException 356 NoSuchMethodException 1035 NO_SUCH_PAGE 582 NOT 1020 NOTE_OFF 1215 NOTE_ON 1215 notify 520 NotSerializableException 966, 978 -nowarn 1252 null 97, 108, 164, 994 Null-Layout 685, 697 null-Objekt 160 NullPointerException 1035 Number 379 NumberFormat 427–427 NumberFormatException 286
O -O 1252 Oak 36 Oberlänge 559 Object... 168, 179, 179, 378, 929 Object Relational\nMapping 1103 ObjectInputStream 472, 967 ObjectOutputStream 463, 963 Objektorientierte Persistenz 961, 1103 Objektorientierte Programmiersprache 44 Objektorientierte Programmierung 155 Objektvariable 161 Observer-Pattern 257 ODBC-Treiber 988 ODER-Operator 119 Offscreen-Image 776 OK_CANCEL_OPTION 808 Online-Dokumentationen 87 OOP 44, 155 open 591, 1209, 1214 openConnection 1150
1317
Stichwortverzeichnis openStream 1150 Operatoren 117–123 Operator-Vorrangregeln 127 or 346, 1020 Oracle 38 orange 564 ORDER BY 1021 org.omg 305 org.w3c 305 org.xml 305 ORM 1103 os.arch 397 os.name 397 os.version 397 out 48, 399 OutOfMemoryError 1240 OutputStream 461–461, 461, 1237 OutputStreamWriter 442, 442
P pack 683, 743 package 309, 1262, 1265 package scoped 183, 185 package-list 1261 Pageable 577, 583 pageDialog 580 PAGE_EXISTS 582 PageFormat 578 paint 534, 604 paintBorder 831 paintChildren 831 paintComponent 831 paintValue 1098 Pakete 301, 302, 309 PAN 1208 Panel 595, 699, 1086 Paper 578 ParallelPort 591 Parameter von Methoden 166–167 PARAM-Tag 929 _parent 945 parse 430 parseByte 226 parseDouble 226 parseFloat 226 parseInt 226 parseLong 226 PATH 73–73 path.separator 397
1318
Pattern 413 PCM 1211 peek 339 Performance-Tuning 1225, 1227 Persistence Descriptor 1111 PersistenceService 329 Persistenz 963 PGP 1180 PI 208 PicoJava 38 pink 564 Pipe 523 PipedInputStream 473, 523 PipedOutputStream 464, 523 PipedReader 442, 453–454, 525 PipedWriter 442–442, 453, 525 pkunzip 468 PLAIN 555 PLAIN_MESSAGE 807 plainTextFlavor 677 play 933–934 Pluggable Look-and-Feel 46, 787 Plug-In für HTML-Browser 957 Point 639 Pointer 43 Policy-Datei 1190, 1196 policytool 1196, 1267 Polygon 541 Polygone zeichnen 541 Polymorphismus 161, 192 pop 339 PopupMenu 673 Popup-Menüs 673, 826 Port 1206 Port-Nummer 944, 1125 PORT_PARALLEL 590 PORTRAIT 579 PORT_SERIAL 590 Positionierung des Dateizeigers 480 POSITIVE_INFINITY 101, 101, 226 Postconditions 148 Postdekrement 117 Postinkrement 117 pow 418–419 PPQ 1218 Prädekrement 117 Präinkrement 117 Präprozessor 97 Preconditions 148
Stichwortverzeichnis Prepared Statements 1015 PreparedStatement 1016 prepareStatement 1016 Pretty Good Privacy 1180 previous 358 previousIndex 358 Primitive Datentypen 97 Primitive Variable 161 print 447, 464, 574, 582 Printable 577, 582 printAll 574 printDialog 580–581 PrinterException 583 PrinterJob 577–578 printf 278 PrintGraphics 572 PrintJob 572 println 399, 447, 464 PrintService 329 printStackTrace 288–289 PrintStream 48, 399, 464 PrintWriter 442, 445, 447 private 44, 181, 183–184, 1234, 1262, 1265 Process 406 processActionEvent 627 processComponentEvent 745 processEvent 627 processFocusEvent 744 processKeyEvent 628, 745 processMouseEvent 627, 673, 744 Producer/Consumer-Beispiel 520 -prof 1248, 1254 Profiler 1241, 1254 PROGRAM_CHANGE 1215 Programmende 501 Projektverwaltung 313 Properties 343, 398 propertyChange 1075 PropertyChangeEvent 1074 PropertyChangeListener 1075 PropertyChangeSupport 1075 PropertyDescriptor 1092 PropertyEditorSupport 1094 propertyNames 344, 398 PropertyPermission 1198 PropertyResourceBundle 438 PropertyVetoException 1080 protected 44, 183, 185, 1262, 1265 Protokoll 1122
Provider für Security-API 1183 Proxy-Server 1128 Pseudo-Zufallszahlen 1177 public 44, 183, 185, 312, 314, 1262, 1265 Public-Key-Verschlüsselung 1179 push 339 PushbackInputStream 473 PushbackReader 453, 459 put 341, 361 putAll 361
Q QUESTION_MESSAGE 807
R Radiobuttons 718 Random 49, 385 RandomAccessFile 467, 479, 1239 Random-Access-I/O 263, 479 read 453, 470 readBoolean 482, 967 readByte 482, 967 readChar 482, 967 readDouble 482, 967 Reader 452 readFloat 482, 967 readFully 482 readInt 482, 967 readLine 456, 482 readLong 482, 967 read-Methoden 482 readObject 967 readShort 482, 967 readUnsignedByte 482 readUnsignedShort 475, 482 readUTF 482, 967 ready 453 rebind 1161 Receiver 1213 Rechtecke zeichnen 540 Rechtsschiebeoperator 120 Rectangle 766 red 564 Red-Black-Tree 365 Referenzgleichheit und -ungleichheit 124 Referenztypen 108 Reflection 49, 961, 965, 1023 regionMatches 269 registerKeyboardAction 836
1319
Stichwortverzeichnis Registry 57 Reguläre Ausdrücke 413 Relationale Operatoren 118 remainder 419 Remote 1157 Remote Method Invocation 49, 961, 1153 RemoteException 1157 Remote-Interface 1153 Remote-Objekte 1154 Remote-Referenzen 1154 remove 353, 356, 361, 658–659, 683, 729, 909 removeActionListener 819 removeAll 353, 886 removeAllChildren 909 removeAllItems 864 removeColumnSelectionInterval 894 removeItem 864 removeItemAt 864 removeNodeFromParent 916 removePropertyChangeListener 1075 removeRowSelectionInterval 894 removeSelectionInterval 860 removeTabAt 886 removeTableModelListener 896 removeVetoableChangeListener 1080 renameTo 494 Rendering von Tabellen 903 repaint 636, 744 repaint-Schleife 760 replace 271, 274 replaceAll 271 replaceItem 729 replaceRange 724 Request For Comments 1126 requestDefaultFocus 887 requestFocus 647, 744 RESERVED_ID_MAX 612 reset 445, 453, 471, 978 ResourceBundle 432 Ressourcen 432 Ressourcendatei 319, 657 Restwertoperator 117 ResultSet 993 ResultSetMetaData 1011 resume 505 retainAll 353 Retention 1053 return 126, 169, 292 revalidate 835
1320
REVERSE_LANDSCAPE 579 RFC 1126 RFC 1521 676 RFC 1522 676 RGB-Farbmodell 563 RIGHT 686, 714, 822, 930 Rivest, Ron 1173 rmf-Format 933 RMI 961, 1153 rmic 1159, 1268 RMI-Registry 1154, 1160, 1268 RMISecurityManager 1162 rollback 1012 RootPane 800 RootPaneContainer 801 round 418 ROUND_CEILING 422 ROUND_DOWN 422 ROUND_FLOOR 422 ROUND_HALF_DOWN 422 ROUND_HALF_EVEN 422 ROUND_HALF_UP 422 ROUND_UNNECESSARY 422 ROUND_UP 422 rowAtPoint 907 RSA 1179 rt.jar 307 Rückgabewert einer Methode 169 run 500, 506 Runden und Abschneiden 418 Runnable 500, 507, 509 Runtime 404 RuntimeException 294 RuntimePermission 1198
S -s 1265 Samples 1204 Sampling 1204 Sandbox-Konzept 1189 SansSerif 554 save 344 scale 422 Scanner 67 Schachteln 445, 456, 699 Schiebeoperationen 120 Schieberegler 731 Schleifen 139 Schleifeninvarianten 148
Stichwortverzeichnis Schließen eines Fensters 535 Schlüssel 1170 Schlüsseltext 1169 Schlüsseltransformation 341 Schlüsselwörter 97 Schneier, Bruce 1173 Schnittstellen (Interfaces) 197 Schriftarten 553 scrollbar 568, 731 SCROLLBARS_ALWAYS 734 SCROLLBARS_AS_NEEDED 734 SCROLLBARS_BOTH 724 SCROLLBARS_HORIZONTAL_ONLY 724 SCROLLBARS_NEVER 734 SCROLLBARS_NONE 724 SCROLLBARS_VERTICAL_ONLY 724 ScrollPane 569, 734 search 340 SECOND 390 SecureRandom 1177 SecurityException 1191 SecurityManager 1199 seek 481 Segment7 742 select 721, 726, 728, 1019 selectAll 721, 894 SELECT-Anweisung 993 _self 945 Semidynamische Arrays 43 send 1214 Separatoren 659 Sequence 1213 SequenceInputStream 472 Sequencer 1204, 1213 Sequenz 1212 Sequenzer 1212 Serialisierte Beans 1072 Serialisierung 49, 963 Serializable 965, 969 SerialPort 591 serialver 972, 1266 serialVersionUID 971 Serif 554 -server 1254 Server-Hotspot 1254 ServerSocket 1130, 1139 ServiceManager 330 Services 329 Servlet-API 53
set 73, 345, 349, 358, 358, 389, 1039, 1044 setAccelerator 819 setActionCommand 664, 669, 716 setAsText 1095 setAutoCommit 1012 setAutoResizeMode 892 setBackground 538, 604 setBlockIncrement 731, 866 setBoolean 1044 setBorder 795, 829 setBounds 599, 697 setByte 1044 setCaretPosition 721, 844 setCellSelectionEnabled 893 setChar 1044 setCharAt 276 setCheckboxGroup 719 setClip 548 setClosable 814 setColor 565 setColumnHeaderView 878 setColumns 720 setColumnSelectionAllowed 893 setColumnSelectionInterval 894 setComponentAt 886 setConstraints 692 setContentPane 805 setContents 678 setCopies 578 setCorner 878 setCurrent 719 setCursor 603 setDebugGraphicsOptions 831 setDecimalSeparatorAlwaysShown 429 setDefaultButton 852 setDefaultCloseOperation 814 setDefaultRenderer 903 setDouble 1039, 1044 setDoubleBuffered 834 setEchoChar 847 setEchoCharacter 721, 795 setEditable 721, 863 setEnabled 599, 660, 819, 852 setEnabledAt 886 setErr 399, 465 setFlashCount 832 setFlashTime 832 setFloat 1044 setFont 554, 604
1321
Stichwortverzeichnis setForeground 604 setGridColor 892 setGroupingSize 429 setHeaderValue 901 setHorizontalAlignment 842, 852 setHorizontalTextPosition 822, 841, 852 setIcon 822 setIconifiable 814 setIconImage 603 setIn 399 setIndeterminate 874 setInt 1039, 1044 setIntercellSpacing 891 setJMenuBar 820 setJobName 578 setLabel 660, 716–717 setLayout 682, 685 setLength 481 setLineNumber 458 setLineWrap 848 setLocation 599 setLong 1044 setLookAndFeel 796 setMajorTickSpacing 869 setMaximizable 814 setMaximum 866 setMaximumSize 835 setMenuBar 658 setMessage 1214 setMinimum 866 setMinimumSize 835, 883 setMinorTickSpacing 869 setMnemonic 818, 852 setModal 702 setName 525 setNextFocusableComponent 836 setOneTouchExpandable 883 setOpaque 833 setOut 399, 465 setPage 584 setPaintLabels 869 setPaintTicks 869 setPreferredSize 835 setPrintable 582 setPriority 526 setPropertyEditorClass 1092 setReceiver 1218 setResizable 703, 814 setRootPaneCheckingEnabled 803
1322
setRootVisible 908 setRowHeaderView 878 setRowHeight 891 setRowMargin 891 setRowSelectionAllowed 893 setRowSelectionInterval 894 setScale 422 setScrollPosition 736 setSeed 1177 setSelected 823, 855 setSelectedIndex 860, 863, 886 setSelectedIndices 860 setSelectedItem 863 setSelectionBackground 892 setSelectionEnd 844 setSelectionForeground 892 setSelectionInterval 860 setSelectionMode 859, 893, 911 setSelectionModel 911 setSelectionPath 914 setSelectionPaths 914 setSelectionStart 844 setSequence 1218 setShort 1044 setShortCut 664 setShowGrid 891 setShowHorizontalLines 891 setShowVerticalLines 891 setSize 599, 697, 735 setSnapToTicks 870 setSoTimeout 1135 setState 601, 661, 717, 823 setStringPainted 872 setTabPlacement 885 setTabSize 848 setTempoInBPM 1218 setter-Methoden 1061 setText 715, 720, 844 setTime 396, 489 setTimeZone 393 setTitle 602, 814 setToolTipText 795, 830 setTransactionIsolation 1013 setUnitIncrement 731, 866 setUserObject 909 setValue 731, 866, 872, 1095, 1208 setValueAt 894, 896 setVerticalAlignment 842, 852 setVisible 533, 597, 683
Stichwortverzeichnis setVisibleAmount 866 setWindowAdapter 955 setWrapStyleWord 848 SHA 1173 shallow copy 209, 983 Shape 549 Shell 72 Sheridan, Mike 36 short 98, 100, 223, 223, 430 Short-Circuit-Evaluation 119 ShortMessage 1213–1214 Short.TYPE 1029 show 673, 826, 1266 showConfirmDialog 807 showDocument 945 showInputDialog 809 showMessageDialog 806 showStatus 927 Sicherheit 45, 961, 1169 Sichtbarkeit 103, 183, 298 sign 1184 Signatur einer Methode 170 Signature 1183 signedjar 1267 Signierte Applets 924 signum 419 SimpleBeanInfo 1091 sin 208, 417 SINGLE_INTERVAL_SELECTION 859, 893 SINGLE_SELECTION 859, 893 Singleton-Pattern 234 SINGLE_TREE_SELECTION 911 size 336, 351, 361, 445 Skeleton 1155 skip 453, 470 skipBytes 481 sleep 402, 506 SMPTE-Format 1218 Socket 1130, 1130 SocketException 1136 SocketPermission 1198 Sonstige Operatoren 122 sort 368, 410 SortedMap 351, 368 SortedSet 351, 365 Sortierte Collections 364 Sound 961, 1203 Soundausgabe 47, 933 Soundbanks 1204
-source 1253 -source 1.4 62, 145 -source 1.5 62 SourceDataLine 1206 -sourcepath 1262 SOUTH 683, 694, 822 SOUTHEAST 693, 822 SOUTHWEST 694, 822 Speichermanagement 109, 109 SpinnerDateModel 850 SpinnerListModel 850 SpinnerModel 850 SpinnerNumberModel 850 SplashScreen 803 Sprachmerkmale 43 Sprunganweisungen 142 SQL-2 Entry-Level 990 SQLException 992, 995–995 SQLWarning 996 sqrt 208, 418 Stack 339 Standarderweiterungen 304 Standard-Font 602 Standardschriftarten 556 Standardsichtbarkeit 183 Standardwert 98 Star Seven 36 start 500, 811, 923, 925, 1209, 1218 Starten der Beispielprogramme 64 startsWith 269 stateChanged 871 Statement 992 static 181, 185, 187, 189–190 static import 208 Statische Initialisierer 190 Statische Methoden 189 Statische Variable 187 Statuszeile des HTML-Browsers 927 Stelligkeit eines Operators 115 stop 502, 811, 926, 934, 1209, 1218 store 344 Stored Procedures 987 -storepass 1267 -storetype 1267 Stream 439 String 48, 99, 108, 265, 278 StringBuffer 48, 108, 275 StringBufferInputStream 472 StringBuilder 275
1323
Stichwortverzeichnis stringFlavor 677 StringIndexOutOfBoundsException 267, 276 String.intern 125 String-Literale 273 StringReader 453, 455 Strings 263, 265 StringSelection 677 StringTokenizer 272, 706 String-Verkettung 123, 268, 1229 stringWidth 559 StringWriter 442, 444 Stub 1154 Subqueries 1021 subSet 365 substring 267, 267, 274, 1232 subtract 419 Subtraktion 117 Suchen in Zeichenketten 270 SUN HotJava 45 sun.boot.class.path 306 sun.io 442, 454 sun.jdbc.odbc.JdbcOdbcDriver 991 SunWorld '95 37 super 181, 383 super.clone 209 Superklassenkonstruktor 181 Superklassenmethoden 181 supportsCustomEditor 1098 supportsTransactionIsolationLevel 1013 suspend 505 Swing 39, 46, 532, 785 SwingConstants 822 SwingUtilities 796 switch 44, 135, 138 Symantec 38 Symboldarstellung 601 Symmetrische Verschlüsselung 1171 Synchronisieren 370, 513 synchronized 349, 503, 516, 1234 Synthesizer 1204, 1213 SysexMessage 1213 System 397 SystemColor 567 System.currentTimeMillis 1249 System.err 1253 Systemfarben 567 System.in 48, 1253 System.out 278, 1253 System.out.print 66
1324
System.out.println 66, 399 System-Properties 397
T TA Trustcenter 1188 TableCellRenderer 903 tableChanged 907 TableColumn 900 TableColumnModel 889, 900 TableModel 889, 896 TableModelListener 896 tailSet 365 tan 417 -target 1253 TargetDataLine 1206 TCP/IP 1122 TCP/UDP 1122 Temporäre Dateien 496 text 568–568 TextArea 723 Textausgaben 48 TextComponent 720 TEXT_CURSOR 604 TextEvent 611, 618, 722 TextField 720 textHighlight 568–568 textHighlightText 568–568 textInactiveText 568 TextListener 618, 722 textText 568–568 texttop 930 textValueChanged 618, 722 Thawte 1188 this 165, 172, 182, 298 Thread 500 ThreadGroup 526 Thread-Gruppen 526 Threads 499, 762, 874 Thread-Synchronisation 513 throw 295 Throwable 288, 289 throws 225, 294–295 Time 994 Timecodes 1204 Times New Roman 554 TimesRoman 554 Timestamp 994 TimeZone 425 Titelleiste 602
Stichwortverzeichnis TitledBorder 829 toArray 351 toCharArray 444 toLowerCase 271 Toolkit 556, 754 Toolkit-Pattern 240 Tooltips 786, 795, 830 TOP 822, 930, 945 toString 123, 179, 225, 277, 281, 288, 444–444, 1232 toUpperCase 271, 451 TRACK 732, 1213, 1218 Tracks 1204 TRAILING 822 Transaction Isolation Level 1013 TRANSACTION_NONE 1013 TRANSACTION_READ_COMMITTED 1013 TRANSACTION_READ_UNCOMMITTED 1013 TRANSACTION_REPEATABLE_READ 1013 TRANSACTION_SERIALIZABLE 1013 Transaktionen 1012 Transferable 676 transient 965, 974 Transmitter 1213 Transparente Komponente 833 Transportschicht 1122 TreeMap 365, 368 TreeModel 908 TreeModelEvent 915 TreeModelListener 915 TreeNode 908 TreePath 912 TreeSelectionEvent 912 TreeSelectionListener 912 TreeSelectionModel 911 TreeSet 365 Treibermanager 991 Trigger 987 trim 267, 1232 true 97–98, 99 try 286 try-catch-Anweisung 286 TYPE 1029 Type-Cast-Operator 122 .TYPE-Objekte 1029 Types 1012 Typisierte Klassen 371 Typkonvertierungen 109 Typsicherheit 314 Typüberprüfungen 102
U UDP 1123 Überladen von Methoden 170 Überlagern von Methoden 162, 179 Übersetzen des Quelltextes 61 UI Delegate 788 UIManager 796 ULAW 1211 UML 90 Umrandungen 795 UnavailableServiceException 330 unbind 1161 unchecked warning 383 UND-Operator 119 Ungarische Notation 70 Ungleichheitsoperator 118 UnicastRemoteObject 1158 Unicode 95, 99–100 Unified Modeling Language 90 Uniform Resource Locator 943 UNION 1021 UNIT_DECREMENT 732 UNIT_INCREMENT 732 UnknownHostException 1129 UNKNOWN_NUMBER_OF_PAGES 583 unread 459, 473 UnsupportedLookAndFeelException 796 UnsupportedOperationException 352 Unterlänge 559 Untermenüs 666 Unterstützung für XML 40 Unveränderliche Collections 371 unwrapping 1030 update 774, 995, 1019, 1173 updateComponentTreeUI 796 UPPER_LEFT_CORNER 878 UPPER_RIGHT_CORNER 878 URL 943, 944–945, 1150 URLConnection 1150 Usenet-Newsgroups 84 User Datagram Protocol 1123 user.dir 397 user.home 397 user.name 397 user.timezone 393 UTF-8-Codierung 466
1325
Stichwortverzeichnis
V -v1.1 1268 -v1.2 1268 validate 835 Validierung 835 valueChanged 861, 912 valueOf 272 values 230, 362 van Hoff, Arthur 37 Variable Parameterlisten 167 Variablen 102, 134 Vaterklasse 158 -vcompat 1268 Vector 48, 336, 353, 1235 Veränderbarkeit 183 Veränderliche Objekte 359 -verbose 1252, 1255, 1262 -verbosegc 1255 Verdecken von Variablen 298 Vererbung 158, 177 Vergleichen von Zeichenketten 268 verify 1184, 1267 VeriSign 1188 Verkettung 123, 172 Verknüpfung auf dem Desktop 71 Verschlüsseln 1169 -version 1252, 1254, 1262 Versionierung von Klassen 971 VERTICAL 693, 731, 822 VERTICAL_SCROLLBAR_ALWAYS 878 VERTICAL_SCROLLBAR_AS_NEEDED 878 VERTICAL_SCROLLBAR_NEVER 878 VERTICAL_SPLIT 882 Verwalten von Threads 525 Verweise auf andere Seiten 943 Verzeichniszugriffe 490 Verzweigungen 135 vetoableChange 1079 VetoableChangeListener 1079 VetoableChangeSupport 1080 View einer Collection 361 Viewport 735 Virtuelle Ausgabefläche 734 Virtuelle Maschine 42, 45, 1227 Visitor-Pattern 253 Visual Cafe 38 VK_0 651 VK_9 651 VK_A 650–651
1326
VK_BACK_SPACE 651 VK_DELETE 651 VK_DOWN 651 VK_END 651 VK_ENTER 651 VK_ESCAPE 620, 651 VK_F1 651 VK_F12 651 VK_HOME 651 VK_INSERT 651 VK_LEFT 651 VK_PAGE_DOWN 651 VK_PAGE_UP 651 VK_RIGHT 651 VK_SPACE 651 VK_TAB 651 VK_UNDEFINED 651–652 VK_UP 651 VK_Z 651 Vlissides, John 233 VM 45 void 126, 169, 224, 224 Void.TYPE 1029 volatile 187 Vordefinierte Pakete 304 Vordergrundfarbe 602 Vorrangregeln 127 VSPACE 930
W Wahlfreier Zugriff auf Dateien 479 wait 505, 520 WAIT_CURSOR 604 waitFor 406 waitForAll 756 WARNING_MESSAGE 807 Warteliste 520 wasNull 994 Wave-Dateien 47 wav-Format 47, 933, 1205 WebRunner 37 WebStart 322 WEEK_OF_MONTH 389 WEEK_OF_YEAR 389 weightx 694 weighty 694 Weitergabe einer Exception 294 WEST 683, 694, 822 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT 837
Stichwortverzeichnis WHEN_FOCUSED 837 WHEN_IN_FOCUSED_WINDOW 837 WHERE 1020 while 44, 139, 140 white 564 WIDTH 928 Wiederverwendung 157 Wildcards 380 Window 533, 567, 595 windowActivated 617, 634 WindowAdapter 614, 634 windowBorder 568 windowClosed 617, 634 windowClosing 536, 617, 634 WindowClosingAdapter 64, 535 windowDeactivated 617, 634 windowDeiconified 617, 634 Window-Ereignisse 617 WindowEvent 617, 633 Window-Events 633 windowIconified 617, 634 WindowListener 535, 617, 633 windowOpened 617, 634 windowText 568 Winkelfunktionen 417 winzip 468 Wrapper-Klassen 223 write 441, 462 writeBoolean 485, 964 writeByte 485, 964 writeBytes 485, 964 writeChar 485, 964 writeChars 485, 964 writeDouble 485, 964 writeFloat 485, 964 writeInt 485, 964, 967 writeLong 485, 964 write-Methoden 485 writeObject 964 Writer 441, 1237 writeShort 485, 964
writeTo 445 writeUTF 466, 485, 964
X X.509-Zertifikat 1188 -Xclassic 1254 -Xint 1244, 1248, 1254 -Xlint 1253 -Xlint:unchecked 383 -Xmixed 1254 XML-Dateien 305 -Xms 1240, 1255 -Xmx 1255 xor 346 -Xprof 1248 -Xrunhprof 1242, 1248
Y YEAR 389 yellow 564 YES_NO_CANCEL_OPTION 808 YES_NO_OPTION 808 yield 1135
Z ZapfDingbats 557 Zeichenextraktion 267 Zeichenketten 48, 265 Zeichentasten 650 Zeichentyp 99 Zeilenabstand 559 Zeitzonen 388 Zeitzonenangabe 393 Zero-Knowledge Proof 1176 Zertifikat 1188 ZipEntry 469 ZipInputStream 476 ZipOutputStream 468 ZONE_OFFSET 390–390 Zufallszahlen 49, 385 Zuweisung 102 Zuweisungsoperatoren 121 Zwischenablage 676
1327
Copyright Daten, Texte, Design und Grafiken dieses eBooks, sowie die eventuell angebotenen eBook-Zusatzdaten sind urheberrechtlich geschützt. Dieses eBook stellen wir lediglich als persönliche Einzelplatz-Lizenz zur Verfügung! Jede andere Verwendung dieses eBooks oder zugehöriger Materialien und Informationen, einschliesslich •
der Reproduktion,
•
der Weitergabe,
•
des Weitervertriebs,
•
der Platzierung im Internet, in Intranets, in Extranets,
•
der Veränderung,
•
des Weiterverkaufs
•
und der Veröffentlichung
bedarf der schriftlichen Genehmigung des Verlags. Insbesondere ist die Entfernung oder Änderung des vom Verlag vergebenen Passwortschutzes ausdrücklich untersagt! Bei Fragen zu diesem Thema wenden Sie sich bitte an: [email protected] Zusatzdaten Möglicherweise liegt dem gedruckten Buch eine CD-ROM mit Zusatzdaten bei. Die Zurverfügungstellung dieser Daten auf unseren Websites ist eine freiwillige Leistung des Verlags. Der Rechtsweg ist ausgeschlossen. Hinweis Dieses und viele weitere eBooks können Sie rund um die Uhr und legal auf unserer Website
http://www.informit.de herunterladen