Sandini Bib
Java 2
Sandini Bib
Sandini Bib
Florian Hawlitzek
Java 2
An imprint of Pearson Education München • Boston • San Francisco • Harlow, England Don Mills, Ontario • Sydney • Mexico City Madrid • Amsterdam
Sandini Bib Die Deutsche Bibliothek – CIP-Einheitsaufnahme Ein Titeldatensatz für diese Publikation ist bei Der Deutschen Bibliothek erhältlich Die Informationen in diesem Produkt werden ohne Rücksicht auf einen eventuellen Patentschutz veröffentlicht. Warennamen werden ohne Gewährleistung der freien Verwendbarkeit benutzt. Bei der Zusammenstellung von Texten und Abbildungen wurde mit größter Sorgfalt vorgegangen. Trotzdem können Fehler nicht vollständig ausgeschlossen werden. Verlag, Herausgeber und Autoren können für fehlerhafte Angaben und deren Folgen weder eine juristische Verantwortung noch irgendeine Haftung übernehmen. Für Verbesserungsvorschläge und Hinweise auf Fehler sind Verlag und Herausgeber dankbar. Alle Rechte vorbehalten, auch die der fotomechanischen Wiedergabe und der Speicherung in elektronischen Medien. Die gewerbliche Nutzung der in diesem Produkt gezeigten Modelle und Arbeiten ist nicht zulässig. Fast alle Hardware- und Softwarebezeichnungen, die in diesem Buch erwähnt werden, sind gleichzeitig auch eingetragene Warenzeichen oder sollten als solche betrachtet werden. Umwelthinweis: Dieses Buch wurde auf chlorfrei gebleichtem Papier gedruckt. Die Einschrumpffolie – zum Schutz vor Verschmutzung – ist aus umweltverträglichem und recyclingfähigem PE-Material. 10 9 8 7 6 5 4 3 2 1 06 05 04 03 02
ISBN 3-8273-2028-3 © 2002 by Addison-Wesley Verlag, ein Imprint der Pearson Education Deutschland GmbH Martin-Kollar-Straße 10–12, D-81829 München/Germany Alle Rechte vorbehalten Einbandgestaltung: Lektorat: Korrektorat: Herstellung: Satz und Layout: Druck und Verarbeitung: Printed in Denmark
Vera Zimmermann, Mainz Christina Gibbs,
[email protected] Simone Meißner, Fürstenfeldbruck; Christiane Auf,
[email protected] Philipp Burkart,
[email protected] mediaService, Siegen (www.media-service.tv) Nørhaven, Viborg (DK)
Sandini Bib
Inhaltsverzeichnis Vorwort
9
Teil I – Start up! 1
Java-Grundkonzepte
13
1.1
Einsatzgebiete
13
1.2
Java als Sprache und Plattform
15
1.3
Laufzeitumgebung
19
1.4
Beispiel
23
2
Entwicklungsumgebungen
27
2.1
Java Development Kit
27
2.2 Integrierte Entwicklungsumgebungen
32
3
Objektorientierung – Grundbegriffe
37
3.1
Klassen und Objekte
37
3.2 Vererbung
43
Teil II – Take that! 4
Java-Syntax
53
4.1
Sprachelemente
53
4.2 Schlüsselwörter
53
4.3 Kommentare
54
4.4 Aufbau von Klassen
56
4.5 Modifier
69
4.6 Interfaces
81
4.7 Erzeugung und Nutzung von Objekten
84
4.8 Datentypen
91
Inhaltsverzeichnis
5
Sandini Bib
4.9 Wichtige Sprachkonstrukte
110
4.10 Organisation von Klassen – Packages und Java-Archive
119
5
Java-2-Bibliothek
129
5.1
Die wichtigsten Packages – eine Kurzübersicht
129
5.2 Editionen der Java 2-Plattform
130
6
Basis- und Systemklassen
137
6.1
Datentypen und Systemklassen – java.lang
137
6.2 Mathematische Klassen – java.math
158
7
Datenspeicherung und -kommunikation
159
7.1
Ein-/Ausgabe – java.io
159
7.2 TCP/IP-Programmierung – java.net
189
7.3 Remote Method Invocation – java.rmi
208
7.4 CORBA – org.omg.CORBA
208
7.5 Enterprise JavaBeans – javax.ejb
208
7.6 Datenbankzugriffe – java.sql
209
7.7 Servlets – javax.servlet
219
8
Oberflächenprogrammierung
221
8.1 8.2 8.3 8.4
Überblick Einfache Oberflächen – java.awt LayoutManager Ereignisbehandlung
222 225 252 266
8.5 Weitere wichtige AWT-Klassen 8.6 Swing
276 295
9
349
Applets – Java im Browser
9.1 Applets – java.applet 9.2 Grundlagen 9.3 Applets und Sicherheit
349 349 356
9.4 Einbindung in einer HTML-Seite
360
6
Inhaltsverzeichnis
Sandini Bib
10
Weitere wichtige Bibliotheken
363
10.1 Vermischtes – java.util
363
10.2 Referenzen – java.lang.ref
386
10.3 Reflection – java.lang.reflect
386
10.4 JavaBeans – java.beans
386
10.5 Verschlüsselung – java.security
388
Teil III – Go ahead! 11
Exception Handling
395
11.1 Einführung
395
11.2 Vergleich mit Returncodes
396
11.3 Realisierung in Java
396
11.4 Zusicherungen
405
12
Tuning
407
12.1 Einflussmöglichkeiten
407
12.2 Garbage Collection
409
12.3 Packaging
410
12.4 Laufzeitumgebung
412
13
415
Architekturen
13.1 Übersicht
415
13.2 K.O.-Kriterien
416
A
Anhang
419
Stichwortverzeichnis
421
Inhaltsverzeichnis
7
Sandini Bib
Sandini Bib
Vorwort Über das Buch Dieses Buch richtet sich an Leute, die einen kompakten Leitfaden für die Java-Programmierung suchen. Es umfasst eine komprimierte Einführung in die Sprache Java und deren Begriffe, eine Referenz der Klassenbibliotheken von Java und Tipps für den erfolgreichen Einsatz der Sprache. Getreu dem Motto „nitty-gritty“ (engl.: to get down to the nitty-gritty: zur Sache kommen) soll sich das Werk auf das Wesentliche konzentrieren und Hilfestellungen für den täglichen Einsatz bieten. Der Schwerpunkt liegt auf der Praxis, nicht auf dem theoretischen Hintergrund, daher enthält dieses Buch eine Vielzahl von Beispielen, die Sie auch auf der Homepage www.nitty-gritty.de finden. Das Buch gliedert sich in drei Teile: T
T
T
Die ersten drei Kapitel sollen Ihnen einen Überblick über die Grundkonzepte der Programmiersprache und Plattform Java, einiger Entwicklungsumgebungen und die Vorteile der objektorientierten Programmierung geben. Der zweite Teil (Kapitel 4-10) stellt die Syntax von Java und die Standardklassenbibliotheken vor. Er soll Ihnen als Referenz dienen, wenn Sie eine kurze Einführung der einzelnen Themen und eine Beschreibung der wichtigsten Klassen, Methoden und Schlüsselwörter suchen. Im abschließenden Teil (Kapitel 11-13) finden Sie Tipps, welche Probleme Sie mit welchen Technologien lösen können und wie Sie Ihre Anwendungen tunen können.
Vorgeschichte Der Autor beschäftigte sich schon seit vielen Jahren mit C++ und seit 1996 zunehmend mit Java. Er war federführend an der Entwicklung mehrerer Kurse und Style-Guides beteiligt und veröffentlichte im Herbst 1999 das Lehrbuch „Java-Programmierung mit IBM VisualAge“. Aufgrund der positiven Reaktionen der Leser und vieler Anfragen entstand das nun vorliegende Buch. Vorwort
9
Sandini Bib
Viele Abschnitte haben ihre Wurzeln im VisualAge-Buch, bei dem jedoch das schrittweise Erlernen der Sprache und der Entwicklungsumgebung im Vordergrund stehen. „Nitty-Gritty Java“ dient vielmehr als praktischer Leitfaden mit Tipps und einer Referenz zum Nachschlagen. Das Fachbuch begleitete den Autor durch eine turbulente Phase im Java- wie auch im beruflichen Bereich. Sun veröffentlichte die Version 1.3 von Java, die bereits Eingang in dieses Buch gefunden hat (ebenso wie Hinweise zur Micro Edition). Beruflich führte diese Zeit den Autor in die Selbstständigkeit, denn er vollzog den Schritt vom Leiter einer Anwendungsentwicklungsabteilung zum Gründer der Firma Hawlitzek IT-Consulting. Anfang 2002 wurde das Buch noch einmal überarbeitet um es an den aktuellen Stand der Technik anzupassen. Insbesondere fanden die Erweiterungen mit Java 1.4 Eingang in diese Auflage. Danksagung Ich möchte ganz herzlich allen meinen Dank ausdrücken, die zur Entstehung dieses Buches beigetragen haben: An erster Stelle möchte ich meiner Firmenmitgründerin Kirsten Literski-Hawlitzek für die wertvollen Anregungen und auch für die viele Geduld danken, die sie mir in den letzten Monaten nun schon zum zweiten Mal entgegengebracht hat. Wieder einmal habe ich den Aufwand unterschätzt, den das Schreiben eines Buches erfordert. Vielen Dank für deine Zuwendung und Liebe! Für die inhaltliche Zuarbeit, viele Verbesserungsvorschläge und sein großartiges Engagement möchte ich besonders Wernher Bien danken. Vielen Dank auch an Tilmann Ochs für gute Dokumente und Grafiken. Mein Dank gilt natürlich auch allen, die am VisualAge-Buch mitgeholfen haben. Ganz herzlichen Dank an den Verlag Addison-Wesley, speziell an meine Lektorinnen Christina Gibbs und Christiane Auf. Feedback Anregungen oder Kritik nimmt gerne der Verlag oder der Autor entgegen. Florian Hawlitzek www.hawlitzek.de EMail:
[email protected] 10
Vorwort
Teil I Sandini Bib
– Start up!
TEIL I
START UP!
Sandini Bib
Die ersten drei Kapitel dieses Buches sollen Ihnen einen Überblick über die Programmiersprache Java, die Nutzung von Entwicklungsumgebungen und einige wichtige Konzepte der Objektorientierung geben. Sie sollen dabei die wichtigsten Konzepte und Begriffe kennen lernen. Den Feinheiten der Syntax und den Standardbibliotheken wenden wir uns dann in Teil II zu.
1
Sandini Bib
Java-Grundkonzepte
Das erste Kapitel stellt die Architektur von Java und die Einsatzmöglichkeiten vor, die die Sprache bietet. Anhand eines kleinen Beispiels zeigen wir den Aufbau eines Java-Programms, die Umsetzung des Quellcodes in das plattformunabhängige Binärformat und die Ausführung im Laufzeitsystem. Auf die in den Büchern sonst übliche Historie möchte ich hier verzichten, denn getreu dem Motto »nitty-gritty« soll sich dieses Buch auf das Wesentliche konzentrieren – und das sind die Fragen:
T
Wie funktioniert es?
1.1
1
Einsatzgebiete
Java ist eine Universalsprache, das heißt, prinzipiell kann man sie für jede Art von Anwendung einsetzen. Dies unterscheidet sie von so genannten Skriptsprachen, die besonders effizient in einem Gebiet sind, zum Beispiel JavaScript zur Steuerung von Webbrowsern, Perl für Webserver oder Makrosprachen wie VBA für das Zusammenspiel von Microsoft-Anwendungen. Es gibt jedoch Einsatzbereiche, wo Java besonders gut geeignet ist, und andere, bei denen es weniger zu empfehlen ist. Eine der Stärken von Java ist der Internetbezug. Sowohl auf der Clientseite im Webbrowser als auch auf dem Webserver hat sich Java bestens bewährt. Dies hängt zum einen mit der Plattformunabhängigkeit von Javacode zusammen, zum anderen mit den Standardklassen, die bereits eine komfortable Unterstützung der Webprotokolle und -technologien bereitstellen. So sind zum Beispiel eine Vielzahl von E-Commerce- und Banking-Anwendungen in Java realisiert. Java ist flexibel einsetzbar, modular, leicht zu lernen und besitzt eine große Anzahl von Standardprogrammierbibliotheken und -schnittstellen. Dies führt dazu, dass Java gerne bei Projekten eingesetzt Java-Grundkonzepte
13
Nitty Gritty • Start up!
T
Was kann ich mit der Sprache machen? Wie mache ich es?
T
Sandini Bib
wird, die eine hohe Entwicklungseffizienz erfordern und bei denen eine große Änderungswahrscheinlichkeit besteht. Diese Eigenschaften sind mittlerweile nicht mehr nur im Hobbyprogrammierer- wie schulischen Bereich gefragt, sondern auch bei den Profis. Ein weiterer Grund für die Popularität von Java sind die hervorragenden Entwicklungsumgebungen, die man teilweise sogar kostenlos erhält. Java läuft überall. Es gibt Laufzeitumgebungen von Palmtop bis hin zum Großrechner. Deshalb setzt man die Sprache immer dann gerne ein, wenn man vorab noch nicht weiß, welche Plattform der spätere Anwender einsetzt, oder man sich die Möglichkeit offen halten will, bei einem unerwartet hohen Ansturm auf die Anwendung den Server auf eine leistungsfähigere Hardware oder ein besseres Betriebssystem umzustellen. Man nennt dies auch Skalierung.
Nitty Gritty • Start up!
1
Besonders beliebt ist Java in verteilten, objektorientierten Systemen. Überall, wo es darauf ankommt, die Rechenleistung verschiedener Computer gleichzeitig zu nutzen oder eine Aufgabenteilung zum Beispiel in Darstellung, Verarbeitung und Datenhaltung durchzuführen, braucht man eine Infrastruktur oder Middleware, die dafür sorgt, dass die Kommunikation funktioniert, die Zugriffe sich nicht gegenseitig ins Gehege kommen und zuverlässig gesichert sind. Java bietet hier selbst einige Mechanismen an und integriert etablierte Technologien wie CORBA (Common Object Request Broker Architecture, standardisierte Infrastruktur zur Objektkommunikation). Einsatzgebiete
Client:
Server:
Backend:
- Webbrowser - Applikationen
- Servlets - Enterprise JavaBeans
- Datenbank - Host
Middleware: RMI, Enterprise JavaBeans , CORBA, DB-Zugriff Transaktionen, Naming, Legacy-Integration
Bild 1.1: Einsatzgebiete 14
Einsatzgebiete
Sandini Bib
Aber es gibt auch Bereiche, in denen Java weniger gut geeignet ist: Java kann nicht direkt auf die Hardware zugreifen. Deshalb kann man keine Treiber in Java programmieren, aber auch keine Spezialgeräte wie Scanner oder Force Feedback Joysticks ansprechen. Auch alle Anwendungen, die aus Effizienzgründen sehr hardwarenah programmiert werden, wie zum Beispiel Action-Computerspiele, sind problematisch – es sei denn, man hat das Glück, dass man die Java-3D-API nutzen kann, die auf einigen Betriebssystemen Zugriff auf OpenGL oder DirectX erlaubt.
1.2
Java als Sprache und Plattform
Java hat eigentlich zwei Bedeutungen: Einerseits als Programmiersprache, andererseits als Umgebung, die alle möglichen Dienste und Schnittstellen bereitstellt. 1.2.1 Die Programmiersprache Java Java ist eine objektorientierte Sprache, die in der Syntax C++ ähnelt. Eine einfache Klasse programmiert man so: class Konto { int nummer; double stand; void einzahlen(double betrag) { stand = stand + betrag; } }
Java-Grundkonzepte
15
1 Nitty Gritty • Start up!
Harte Echtzeitanforderungen sind ebenfalls nicht machbar. »Hart« bedeutet in diesem Zusammenhang, dass ein Ablauf – etwa die Steuerung einer Maschine – eine Reaktionszeit garantieren muss, zum Beispiel 10 Millisekunden. In diesem Fall kann man kein Java einsetzen. Das liegt daran, dass die Laufzeitumgebung sehr komplex ist und hin und wieder eine Speicherbereinigung durchführt, die zu einem »zufälligen« Zeitpunkt erfolgen und nicht unterbrochen werden kann.
Sandini Bib
Die Klasse Konto beschreibt, welche Attribute ein Objekt dieser Klasse auszeichnen, nämlich eine Kontonummer und der Kontostand, und welche Funktionalität die Klasse anbietet, in diesem Fall nur die Einzahlung.
Nitty Gritty • Start up!
1
Die Sprache bietet all das, was man von jeder Programmiersprache wie BASIC, C etc. gewohnt ist, also Variablen, Schleifen, bedingte Anweisungen und so weiter. Java ist jedoch voll objektorientiert, so dass alles in Klassen und Objekte verpackt ist. Daten sind als Zustände von Objekten abgelegt, also als Wertebelegungen der Variablen. Zum Beispiel könnte ein Konto die Nummer »08154711« haben und den Stand »2.500,68« aufweisen. Die Funktionalität ist in Form von Methoden dieser Klassen verfügbar. Eine weitere Möglichkeit von objektorientierten Sprachen ist die Vererbung, also die Wiederverwendung gemeinsamen Codes und die Spezialisierung von Klassen. So könnte es zum Beispiel die spezielleren Arten »Girokonto« und »Sparkonto« geben, die beide alle Eigenschaften des Kontos erben, aber zusätzlich Eigenheiten besitzen. Java ist im Vergleich zu C++ etwas einfacher und damit leichter zu lernen und zu verstehen, denn der Programmierer muss sich um die Speicherverwaltung nicht mehr kümmern, außerdem wurden komplexe Sprachmittel weggelassen. So gibt es in Java beispielsweise keine Mehrfachvererbung, keine Templates oder das Überladen von Standardoperatoren. 1.2.2 Die Java Plattform Seit 1998 bezeichnet die Firma Sun Microsystems – die Erfinder der Sprache – Java auch als Plattform. Was ist darunter zu verstehen? Java bringt eine Fülle von bereits fertigen Klassenbibliotheken mit. Das Spektrum reicht von Klassen für die Erstellung grafischer Oberflächen über den Datenbankzugriff bis hin zur Verschlüsselung und Datenkompression. Ähnlich wie bei Betriebssystemen, z. B. Windows, bei denen es einen Satz von Programmierschnittstellen für alle möglichen Bereiche und Geräte gibt, bieten die Bibliotheken von Java die Funktionalität als API (Application Programmers Interface, Programmierschnittstelle) an. Allerdings ist es hier ein und dieselbe Schnittstelle für alle eingesetzten Betriebssysteme, die Aufrufe wer16
Java als Sprache und Plattform
Sandini Bib
den durch die Laufzeitumgebung auf die reale Plattform umgesetzt. Die dadurch abstrakt definierte Umgebung mit dem zugehörigen Emulator heißt virtuelle Maschine. Weil sich Java für den Programmierer wie eine eigenständige Plattform verhält, nennt man es mittlerweile auch so, um auszudrücken, dass Java mehr als nur eine Sprache ist. Java-Anwendung Klassenbibliotheken Virt. Machine/Emulator Betriebssystem
Nutzung der Button-Klasse Umsetzung in API-Aufruf Aufruf der WindowAPI-Routine
Hardware
Bild 1.2: Umsetzung eines API-Aufrufs
Editionen Es wäre aber nicht sinnvoll, alle Bibliotheken auf jeder Plattform bereitzustellen, denn wozu bräuchte man auf einem Palmtop eine Unterstützung für Application Server Komponenten? Das verbietet sich schon aus Platzgründen. Sun hat deshalb drei verschiedene Editionen mit unterschiedlicher Ausrichtung definiert: 1. Standard Edition für den Desktop Client oder PC 2. Enterprise Edition für Application Server 3. Micro Edition für Kleingeräte wie Palmtops und Embedded Systeme, z. B. Videorekorder In diesem Buch geht es vorwiegend um die Standard Edition, die zugleich am weitesten entwickelt ist. In Abbildung 1.3 sind die Bereiche aufgeführt, die diese Sammlung von Klassenbibliotheken berührt.
Java-Grundkonzepte
17
1 Nitty Gritty • Start up!
Abbildung 1.2 zeigt ein Beispiel: In einer grafischen Anwendung wird eine Schaltfläche (Klasse Button) benutzt. Jeder Aufruf wird in der virtuellen Maschine in einen Aufruf der Window-API des Wirtsbetriebssystems umgesetzt, zum Beispiel der von Windows oder der MotifBibliothek unter UNIX.
Sandini Bib
Standard-Bibliotheken
Basisklassen, Collections
Netzwerk/ Applets
Komponenten
Sicherheit
GUI/ Grafik
Text, Ein-/ Ausgabe
Middleware
Datenbanken
- Swing - AWT - Drucken - Bildverarb.
- Dateien - Internationalisierung - HTML
- CORBA - RMI
- SQL - JDBC
Bild 1.3: Java 2 Standard Edition
Nitty Gritty • Start up!
1
Die Enterprise Edition ist eine Erweiterung der Standard Edition um die Funktionalität für einen Application Server. Ein solcher Server bietet verschiedenartigen Nutzern Dienste an und integriert Backendsysteme. Ein Beispiel könnte eine E-Commerce-Anwendung sein, bei der ein Kunde per Webbrowser bestellen kann und die auf Großrechnerdatenbanken und ein Warenwirtschaftssystem zugreift. Zuverlässige Middleware spielt hier eine noch größere Rolle, beispielsweise Namens- und Verzeichnisdienste oder Transaktionen. Enterprise-Bibliotheken
StandardBibliotheken
Servlets/ JSP
Enterprise JavaBeans
Transaktionen
Messaging
Mailing
Naming
XML
Bild 1.4: Java 2 Enterprise Edition
Die Enterprise Edition ist – im Gegensatz zur Standard Edition – keine Laufzeitumgebung, sondern nur ein Satz von Programmierschnittstellen und Kompatibilitätsstandards, die ein Application Server erfüllen muss, um das entsprechende Zertifikat von Sun zu erhalten. Die Ursprünge der Enterprise Edition lag in verschiedenen optionalen Erweiterungen zur Standard Edition.
18
Java als Sprache und Plattform
Sandini Bib
Die Micro Edition ist eine abgespeckte Version der Standard Edition, die für Kleingeräte gedacht ist und auf niedrige Hardware-Anforderungen optimiert wurde.
1.3
Laufzeitumgebung
Die Java-Laufzeitumgebung besteht im Wesentlichen aus der bereits angesprochenen virtuellen Maschine, die an die Zielplattform angepasst ist. Was bedeutet dies konkret und was bringt das für Vorteile? 1.3.1 Plattformunabhängigkeit Java wurde so konzipiert, dass es auf verschiedenartigsten Systemen ablauffähig ist. Deshalb war es nicht möglich, Annahmen über die Zielhardware oder die Eigenschaften des Betriebssystems zu machen, auf dem die Anwendungen laufen sollen. Vielmehr setzt Java auf einem abstrakt definierten, im Zielsystem zu emulierenden System auf, der virtuellen Maschine.
1
Viele interpretierte Sprachen wie BASIC oder REXX ähneln diesem Konzept. Hier wird der Quellcode direkt von einer Laufzeitumgebung eingelesen und Anweisung für Anweisung in Maschinencode (bzw. Betriebssystem-API-Aufrufe) umgesetzt. Diese Vorgehensweise ist allerdings nicht besonders effizient. Virtuelle Maschine Javas virtuelle Maschine (Java VM) dagegen ist wirklich als Maschine definiert, also als Hardware, die bestimmte Opcodes (Maschinenanweisungen) interpretieren kann. Und tatsächlich werden auch schon Chips entwickelt, die diese Maschine in Hardware implementieren. Im Regelfall geschieht aber eine Emulation durch ein Laufzeitsystem auf der jeweiligen Zielplattform. Wozu aber der Aufwand, auf jeder Zielplattform ein Laufzeitsystem zu installieren; man könnte doch gleich den Code in die richtige Form bringen?
Java-Grundkonzepte
19
Nitty Gritty • Start up!
Interpretierte Sprachen
Sandini Bib
Kompilierte Sprachen Abbildung 1.5 zeigt den Ablauf im Vergleich: Kompilierte Programmiersprache MOV 2,34 XCH 34,245 INC EAB JGT EAX JMP 7455
C-Compiler für OS/2
YourBank
MOV 2,34 XCH 34,245 INC EAB JGT EAX JMP 7455
#Include “demo.h” int a; struct Thread runner;
Maschinencode (PC, OS/2)
char* toString(void* x) { return (struktur)x; }
Quellcode(s)
C-Compiler für Solaris
BIV 2,34 JZ 34445 INC R12 JGT EAX JMP 4335
PC mit OS/2
YourBank
BIV 2,34 JZ 34445 INC R12 JGT EAX JMP 4335
Maschinencode (Sun Ultra, Solaris)
Sun Ultra mit Solaris
Java YourBank
Class Test extends Applet { int a; Thread runner; String toString() { return super.toString(); }
JavaCompiler
}
Êþº¾ - o "Ljava/beans/PropertyChange Support; propertyChange v334 Ljava/awt/Color; fieldLineColor I fieldLineWidth
(Ljava/awt/Color;)V r33 34, 434, j
PC mit OS/2 YourBank
1
Java Quellcode
Bytecode (maschinenunabhängig)
Nitty Gritty • Start up!
Sun Ultra mit Solaris
Bild 1.5: Vergleich von kompilierten und interpretierten Programmiersprachen
In einer kompilierten Programmiersprache wie C muss nicht nur für jede Zielhardware, sondern auch für jedes Betriebssystem (oder gar jede Betriebssystem-Version!) ein eigenes Binary erzeugt werden. Dies setzt voraus, dass die Entwickler für all diese Konfigurationen Systeme, Entwicklungs- und Testwerkzeuge haben. Da diese meist untereinander inkompatibel sind, müssen fast immer auch verschiedene Versionen des Quellcodes gepflegt werden. In der Praxis führt dies meist dazu, dass nur noch ein bis zwei Plattformen abgedeckt werden, da der Aufwand für ein Nicht-Mainstream-System zu groß wäre. Java Im Gegensatz dazu erzeugt der Java-Entwickler nur auf einer Plattform eine universelle Quelldatei (Source), die er dort in Binärcode für die Java VM kompiliert: den so genannten Bytecode. Das Laufzeitsys20
Laufzeitumgebung
Sandini Bib
tem besteht aus dem Emulator und einer Reihe von Klassenbibliotheken und führt den Bytecode aus. Auf Englisch heißt es Java Runtime Environment, kurz JRE. Sun selbst bietet es für Windows und Solaris an, es gibt aber noch viele andere Hersteller, zum Beispiel IBM, HP oder Microsoft. Eigentlich sollten sich alle identisch verhalten, aber leider steckt der Teufel manchmal im Detail. 1.3.2 Java-Versionen Insbesondere die unterstützte Sprachversion von Java und den zugehörigen Bibliotheken weicht ab. Die Versionsbezeichnung von Sun besteht aus drei Zahlen, wobei die zweite die wichtigste ist. Die letzte Ziffer kennzeichnet meist nur Fixpacks (Sammlung von Bugfixes) oder minimale Funktionserweiterungen.
1.3.3 Varianten Es gibt vier Varianten der Laufzeitumgebung, zwei für die Client- und zwei für die Serverseite (siehe Abbildung 1.6): 1. Client JRE für Applikationen 2. Webbrowser mit Applets 3. Webserver mit Servlet Engine 4. Application Server als EJB-Container
Java-Grundkonzepte
21
1 Nitty Gritty • Start up!
Die erste Version 1.0.x ist völlig veraltet und wird kaum noch benutzt. Mit 1.1.x, also z. B. 1.1.8, sind Sie auf der sicheren Seite. Sie wird von praktisch allen Umgebungen und Browsern unterstützt. Mit Java 1.2.x machte die Sprache einen großen Sprung, insbesondere in den Bereichen Grafik, Sicherheit und Middleware. Deshalb spricht Sun seit dieser Version auch von »Java 2«. Leider haben viele Anwender noch keine entsprechende virtuelle Maschine installiert und auch bei der Browserunterstützung sieht es nicht so gut aus. Häufig rechtfertigen die neuen Möglichkeiten aber den Einsatz. Die neueren Versionen 1.3 und 1.4 brachten nur wenige neue Programmierschnittstellen, dafür aber eine komfortablere und schnellere Laufzeitumgebung und die Integration bisher extra erhältlicher Bibliotheken.
Sandini Bib Java-Client Java Applikation
Browser-Client Java Applet
Klassenbibliotheken
Klassenbibliotheken
Java virt. Maschine
Java virt. Maschine
Betriebssystem
Webbrowser
Hardware
Betriebssystem Hardware
Java-Webserver
Java-EJB-Server
Java Servlet
Enterprise JavaBean
Klassenbibliotheken Java virt. Maschine Servlet Engine
EJB-Container Bibliothek.
Dienste
Java virt. Maschine Application Server
1
Webserver Betriebssystem
Nitty Gritty • Start up!
Betriebssystem
Hardware
Hardware
Bild 1.6: Varianten der Java-Laufzeitumgebung
Alle haben einen ähnlichen Aufbau. Die ablaufenden Klassen nutzen die mitgelieferten Klassenbibliotheken und werden in der virtuellen Maschine ausgeführt. Unterschiede gibt es aber in der Art der Anwendungen und dem System, auf dem die VM aufsetzt. Auf dem Client laufen Java-Anwendungen entweder als Applikationen oder Applets. Applikationen entsprechen herkömmlichen Anwendungen. Sie stellen jedoch keine Executables dar, sondern werden gestartet, indem man dem Java-Interpreter eine Klasse als Parameter übergibt, welche eine main-Methode besitzt. > java Homebanking
Applets sind spezielle Klassen, die in einem Java-fähigen Webbrowser ablaufen. Dieser hat dann das Laufzeitsystem bereits integriert, bei Netscape Navigator/Communicator oder Microsoft Internet Ex-
22
Laufzeitumgebung
Sandini Bib
plorer kann man dieses allerdings per Java Plug-In von Sun auch durch eine aktuelle Version ersetzen. Applets werden mit einem speziellen HTML-Befehl in eine Webseite eingebunden und meist auf einem Webserver bereitgestellt. Applets haben besondere Möglichkeiten, mit dem Browser bzw. der Webseite zu kommunizieren, in der sie eingebettet sind. Sie unterliegen besonderen Sicherheitsrestriktionen, die verhindern sollen, dass von einem Webserver heruntergeladener, unbekannter Code das eigene System beschädigen oder ausspähen kann. Aber auch auf Webservern ist Java heimisch. Sehr verbreitet sind Servlets, das sind Java-Klassen, die dynamisch HTML-Seiten generieren und im Prinzip ähnlich wie CGI-Anwendungen funktionieren. Eine solche Laufzeitumgebung nennt man Servlet Engine. Sie ist typischerweise als Plug-In für gebräuchliche Webserver erhältlich.
1.4
Beispiel
In diesem Abschnitt soll eine einfache Applikation erstellt und zum Laufen gebracht werden. Wir benutzen hier das Standard Java Development Kit (JDK bzw. Java 2 SDK), das sowohl die Laufzeitumgebung (JRE) enthält als auch einen Compiler, um die Java Klasse in eine ausführbare Form zu bringen. Die Klasse soll die aktuelle Systemzeit lesen und auf der Kommandozeile ausgeben. Erstellung der Klasse Öffnen Sie dazu einen beliebigen Texteditor und geben Sie folgende Zeilen ein (ohne die erläuternden Kommentare): Die erste Zeile macht die Systemklasse für das Datum sichtbar: import java.util.Date;
Java-Grundkonzepte
23
1 Nitty Gritty • Start up!
Ein Java Enterprise Application Server bietet besonderen Java-Komponenten eine Laufzeitumgebung, den so genannten Enterprise JavaBeans. Diese nutzen nicht nur die normalen Java-Klassenbibliotheken, sondern auch eine Reihe von Diensten, die der Server bereitstellt.
Sandini Bib
Die nächste Zeile gibt der Klasse einen Namen und öffnet einen Block. class ZeigeDatum {
Die main-Methode wird automatisch beim Interpreteraufruf ausgeführt. Damit sie gefunden werden kann, muss sie exakt so eingeleitet werden: public static void main(String[] args) {
Nun erzeugen wir ein neues Datum-Objekt, das automatisch auf das heutige Datum eingestellt wird, wenn wir nichts anderes angeben: Date aktuellesDatum = new Date();
Nun wandeln wir das Datum in eine Zeichenkette (Klasse String) um: String ausgabe = aktuellesDatum.toString();
1
und geben dieses auf der Standardausgabe – der Konsole – aus: Nitty Gritty • Start up!
System.out.println(ausgabe);
Nun müssen wir noch die Blöcke für die Methode und die Klasse schließen: } }
Ein erfahrener Programmier hätte diese Schritte auch etwas kompakter schreiben können, was folgende Kurzschreibweise ergibt: import java.util.Date; class ZeigeDatum { public static void main(java.lang.String[] args) { System.out.println(new Date()); } }
Speichern Sie die Klasse unter dem Namen ZeigeDatum.java.
24
Beispiel
Sandini Bib
Die .java-Datei muss genau den gleichen Namen haben wie die Klasse, und zwar inklusive Groß- und Kleinschreibung, also nicht Zeigedatum.java! Kompilation der Klasse Wechseln Sie auf der Kommandozeile in das Verzeichnis, in das Sie die Klasse gespeichert haben. Sorgen Sie entweder dafür, dass der Java-Compiler javac im Pfad liegt, oder geben Sie den vollqualifizierten Pfad an: > javac ZeigeDatum.java
bzw. > %JDK_HOME%\bin\javac ZeigeDatum.java
Als Ergebnis erhalten Sie eine Datei mit dem Namen ZeigeDatum.class, die ausführbare Klasse.
Wenn das aktuelle Verzeichnis ».« in der Umgebungsvariable CLASSPATH enthalten ist, können Sie die Applikation nun wie folgt aufrufen: > java ZeigeDatum
1. Falls Sie eine Fehlermeldung erhalten, dass die Klasse oder main-Methode nicht gefunden werden kann, liegt das vermutlich daran, dass die Laufzeitumgebung nur im CLASSPATH sucht und das aktuelle Verzeichnis dort nicht enthalten ist. In diesem Falle erledigen Sie dies unter DOS/Windows mittels > set CLASSPATH=%CLASSPATH%;.
2. Wenn Sie einen NoClassDefFoundError mit dem Hinweis »wrong name« erhalten, haben Sie vermutlich die Groß-/Kleinschreibung nicht beachtet. Wenn Sie später Packages benutzen, müssen Sie nicht in das Verzeichnis mit der Klasse wechseln, sondern im Basisverzeichnis, ab dem die Packagenamen beginnen. Dort geben Sie dann den kompletten Klassennamen inklusive Package an, z. B. java de.hawlitzek.nittyGritty.ZeigeDatum
Java-Grundkonzepte
25
Nitty Gritty • Start up!
1
Ausführung der Klasse
Sandini Bib
Wenn Sie später Packages benutzen, müssen Sie nicht in das Verzeichnis mit der Klasse wechseln, sondern im Basisverzeichnis, ab dem die Packagenamen beginnen. Dort geben Sie dann den kompletten Klassennamen inklusive Package an, z. B. java de.hawlitzek.nittyGritty.ZeigeDatum Gerade für Windows-Benutzer ist es oft schwierig, diese Eingaben auf der Kommandozeile durchzuführen. Es hilft auch nichts, wenn Sie die Endung ».class« mit dem Java-Interpreter verbinden, da dann das Arbeitsverzeichnis nicht korrekt eingestellt ist. Es ist deshalb empfehlenswert, eine einfache ».bat«-Datei mit dem Aufruf des Java-Interpreters und dem korrekten Klassennamen anzulegen.
Nitty Gritty • Start up!
1
26
Beispiel
2
Sandini Bib
Entwicklungsumgebungen
Java ist nicht zuletzt deshalb so verbreitet, weil es für die Sprache hochmoderne Programmierumgebungen gibt, die dem Entwickler viel Arbeit abnehmen und die tägliche Arbeit erleichtern. Außerdem sind einige Werkzeuge sogar kostenlos erhältlich.
2.1
Java Development Kit
Eine einfache Entwicklungsumgebung haben wir bereits vorgestellt, das Java Development Kit von Sun Microsystems.
Das JDK ist für Windows und Solaris bei der Sun-Tochter JavaSoft (http://www.javasoft.com/)zu beziehen.
Inhalte Das JDK enthält neben einer HTML-Dokumentation und einer virtuellen Maschine eine Reihe einfacher Kommandozeilen-Tools für die Software-Entwicklung unter Java. Nicht zu verwechseln ist das JDK mit kompletten, integrierten Entwicklungsumgebungen, die wir in Abschnitt 2.2 vorstellen werden. 2.1.2
Werkzeuge
Ablauf Zunächst werden die Quelldateien mit einem Editor erstellt. Ein solcher ist nicht im JDK enthalten. Jede (öffentliche) Klasse muss in einer Datei mit dem exakten Klassennamen und der Endung .java abgespeichert werden.
Entwicklungsumgebungen
27
2 Nitty Gritty • Start up!
2.1.1 Bestandteile Das Java Development Kit von Sun, kurz JDK, ist Laufzeitsystem und Entwicklungsplattform zugleich. Die Laufzeitbestandteile sind auch separat als Java Runtime Environment (JRE) erhältlich. Seit JDK Version 1.2 wird es auch als Java 2 Software Development Kit (Java 2 SDK) bezeichnet.
Sandini Bib
Danach kompiliert man mit dem Compiler und kann Applikationen in der Laufzeitumgebung des JDK starten. Applets kann man entweder in einem Java-fähigen Webbrowser laufen lassen (nicht enthalten) oder dem eingeschränkten Appletviewer des JDK. Class Test extends Applet { int a; Thread runner; String toString() { return super.toString(); }
Êþº¾ - o "Ljava/beans/PropertyChange Support; propertyChange v334 Ljava/awt/Color; fieldLineColor I fieldLineWidth (Ljava/awt/Color;)V r33 34, 434, j
JavaCompiler
}
Java Quellcode ZeigeDatum.java
Auslieferung oder Download
Bytecode ZeigeDatum.class
> javac ZeigeDatum.java
JRE oder Webbrowser > java ZeigeDatum
Eingaben auf der Kommandozeile
Bild 2.1: Ablauf der Programmerstellung mit dem JDK
javac
Nitty Gritty • Start up!
2
Der Compiler heißt javac und wird in der Kommandozeile mit der Java-Datei als Parameter aufgerufen: javac ZeigeDatum.java
Wenn die Quelldateien keine Fehler enthalten, generiert der Compiler die Bytcodedatei ZeigeDatum.class. Geben Sie beim Kompilieren den Dateinamen an, also in der Regel den Klassennamen inklusive der Endung ».java«! Die Datei könnte auch anders heißen oder mehrere Klassen enthalten. Als Ergebnis erhalten Sie aber immer ».class«-Dateien mit dem korrekten Namen der Klasse(n). Jede Klasse steht dabei in einer eigenen Datei. Nach der Installation des JDK liegt der Java-Compiler (im Gegensatz zum Interpreter) nicht im Systempfad. Tragen Sie unter der Umgebungsvariable PATH das bin-Verzeichnis des JDK ein oder verwenden Sie bei jedem Aufruf folgende Schreibweise: > %JDK_HOME%\bin\javac ZeigeDatum.java
28
Java Development Kit
Sandini Bib
Wenn eine Klasse noch weitere Klassen referenziert, dann müssen diese im CLASSPATH entweder als Quell- oder als Binärcode vorliegen. Bei unserem Beispiel ist dies die Klasse java.util.Date. Da diese Bestandteil der Standardbibliotheken ist, wird sie automatisch gefunden. Hätten wir eine eigene Klasse benutzt, die bisher nur im Quellcode existiert, hätte der Compiler sie automatisch mitkompiliert. Mit dem Parameter -verbose erhält man detaillierte Statusmeldungen, mit -classpath kann man die normale Umgebungsvariable CLASSPATH überschreiben und festlegen, wo der Compiler nach referenzierten Klassen sucht. Folgende Tabelle stellt die wichtigsten Parameter vor: Bedeutung
-classpath
Pfad für Suche nach weiteren Klassen angeben
-d
anderes Zielverzeichnis angeben
-deprecation
Warnung bei Benutzung veralteter Klassen und Methoden
-g
Debuginformation in Klasse generieren
-O
optimiertes Binärformat erzeugen
-verbose
ausführliche Statusmeldungen anzeigen
-source 1.4
Benutzung von Zusicherungen (Assertions, ab JDK 1.4, siehe Kapitel 11)
Ein Linken (Binden) ist nicht notwendig, da das Java-Laufzeitsystem die benötigten Klassen dynamisch, das heißt nur bei Bedarf, lädt und zur Laufzeit bindet. java Die Laufzeitumgebung, also der Java-Interpreter, heißt java. Man übergibt diesem eine Java-Klasse (.class-Datei) zur Ausführung: java ZeigeDatum
Dabei ist (im Gegensatz zum Compiler) keine Endung anzugeben! Diese Datei muss eine main-Methode enthalten. Es kommt auf die exakte Groß-/ Kleinschreibung des Klassennamens an! Entwicklungsumgebungen
29
2 Nitty Gritty • Start up!
Parameter
Sandini Bib
Wenn sich die Klasse in einem Package befindet, müssen Sie den vollqualifizierten Namen der Klasse angeben (d. h. inklusive des Packages), also z. B. java de.hawlitzek.nitty-gritty.ZeigeDatum. Das Basisverzeichnis, in dem die Unterverzeichnisse für die Packages liegen, also in unserem Fall ªde« muss im CLASSPATH enthalten sein. Wenn dort nur das aktuelle Verzeichnis (».«) eingetragen ist, müssen Sie auf der Kommandozeile in dieses Basisverzeichnis wechseln, bevor Sie die Klasse starten. Beispiel: Oben genannte Klasse wurde im Verzeichnis c:\java\Demos kompiliert und enthält eine Packageanweisung für de.hawlitzek.nitty-gritty. Nach der Kompilation liegt die Klasse unter c:\java\Demos\de\hawlitzek\ nitty-gritty\ZeigeDatum.class. Der Aufruf aus dem Basisverzeichnis lautet: C:\java\Demos> java de.hawlitzek.nitty-gritty.ZeigeDatum
2
Die genaue Syntax lautet Nitty Gritty • Start up!
java [Optionen] classfile [Argumente]
wobei die wichtigste Option wiederum -classpath ist. Parameter
Bedeutung
-classpath
Pfad für Suche nach weiteren Klassen angeben
-D=<Wert>
Umgebungsvariable angeben
-version
Versionsnummer ausgeben
-verbose
ausführliche Statusmeldungen anzeigen
-Xdebug
Remote Debugging ermöglichen
-Xms
initiale Heapgröße (Speicher) setzen
-Xmx
maximale Heapgröße (Speicher) setzen
-ea -enableassertions
Überprüfung von Zusicherungen (Assertions, ab JDK 1.4, siehe Kapitel 11)
appletviewer Java-Applets werden in HTML-Dateien mit dem -Tag eingebettet. Mehr dazu später. 30
Java Development Kit
Sandini Bib
Mit dem Appletviewer können diese Applets gestartet werden: appletviewer file://C:/java/Demos/ZeigeDatumApplet.html
Der Aufruf hat eine HTML-Seite (URL) als Parameter, keinen Klassennamen. Ein Applet kann nur aus einer Webseite gestartet werden, da es aus dieser seine Parameter bezieht. Weitere Werkzeuge Daneben gibt es noch eine Reihe weiterer Tools, zum Beispiel einen sehr einfachen Debugger (jdb), einen C-Header-Generator (javah), ein Werkzeug zur Generierung von Java-Archiven (jar) sowie mehrere für die Sicherheitsfunktionen (jarsigner, keytool, policytool) und für verteilte Methodenaufrufe (rmic und rmiregistry).
Beispielsweise wird für folgenden Ausschnitt aus einer Quelldatei eine gegliederte Code-Dokumentation erzeugt, deren Methoden-Index in Abbildung 2.2 und Abbildung 2.3 dargestellt ist. Beispiel /** * method for converting Euro currencies * uses rounding and correct scaling * @return java.lang.String converted value [...] */ public String calc(String value, String sourceCurr, String destCurr) throws CurrencyTypeException, CurrencyFormatException { [...]
Entwicklungsumgebungen
31
2 Nitty Gritty • Start up!
Wichtig ist noch ein Hilfe-Generator (javadoc), mit dem eine HTMLDokumentation von Java-Klassen und -Methoden erstellt werden kann. Diese Minimalinformation (vor allem die Signaturen der Methoden) kann und sollte der Entwickler um eigene Kommentare anreichern (/** Kommentar für javadoc */.)
Sandini Bib
Bild 2.2: Ausschnitt aus der javadoc-Dokumentation des JDK 1.2
2 Nitty Gritty • Start up!
Bild 2.3: Ausschnitt aus der gleichen javadoc-Dokumentation des JDK 1.1.x
2.2
Integrierte Entwicklungsumgebungen
Sehr lästig bei der Arbeit mit dem JDK ist, dass man sich um die Organisation der Quell- und Binärdateien selbst kümmern muss. Es passiert leicht, dass man nicht mehr zuordnen kann, welche Klassen zu welchen Projekten gehören und welche referenziellen Abhängigkeiten bestehen. Auch das Einstellen der CLASSPATH-Variable auf die korrekten Klassen und Java-Archive ist schwierig, insbesondere wenn dieselben Klassen in unterschiedlichen Version auf dem System liegen. Ein weiterer Nachteil ist, dass das JDK keinen Editor beinhaltet. Moderne Editoren für Programmierer kennzeichnen Schlüsselwörter der Sprache farblich, erlauben eine Syntaxergänzung für erst partiell eingegebene Ausdrücke oder prüfen die korrekte Syntax schon während der Eingabe.
32
Integrierte Entwicklungsumgebungen
Sandini Bib
Integrierte Entwicklungsumgebungen (IDE, Integrated Development Environment) gehen noch einen Schritt weiter. Sie bieten neben einem Editor eine Testumgebung mit grafischem Debugger, verschiedene Browser und Organisationshilfsmittel für komplette Java-Projekte, einen grafischen Oberflächeneditor sowie Wizards, die Informationen Schritt für Schritt vom Entwickler abfragen und daraus Javacode erzeugen. Beispiele für solche IDEs sind IBM WebSphere Studio und VisualAge for Java, Borland JBuilder, WebGain Visual Café und Sun NetBeans bzw. Forté. Marktführer im professionellen Bereich sind IBM und Borland. 2.2.1
Eclipse und IBM WebSphere Studio Workbench
Dieses Produkt gibt es für die Entwicklungsplattformen Windows 98/ ME/2000/NT und Linux. Es löst das Vorläuferprodukt VisualAge for Java ab. Es gibt drei Ausbaustufen: Die Basis ist die kostenlose Eclipse Workbench, die IBM als Open Source freigegeben hat. Sie enthält einen guten Editor, verschiedene Browser, eine Projektverwaltung sowie Unterstützung für unterschiedliche Java-Compiler. Sie ist durch Plug-Ins verschiedener Hersteller erweiterbar.
T
Die kommerzielle Version WebSphere Studio Site Developer (WSSD) ist eine durch IBM-Plug-Ins erweiterte Ecplise-Version. Zusätzlich zur Java-Entwicklung stellt sie auch Werkzeuge zur Erstellung und Berarbeitung von HTML- und XML-Dokumenten bereit. Auch Servlets und JavaServer Pages können entwickelt und in der integrierten WebSphere-Testumgebung ausprobiert werden.
T
Der WebSphere Studio Application Developer (WSAD) enthält weiterhin auch Unterstützung für die Entwicklung von Datenbankanwendungen, Enterprise JavaBeans und WebServices. Auch für die Performanceanalyse gibt es Werkzeuge.
Ein Vorzug der Entwicklungsumgebung von IBM ist die Projektverwaltung mit verschiedenen Ansichten (Perspektiven), Browsern und Editoren. Da die Workbench alle enthaltenen Klassen kennt, kann man sehr schnell Abhängigkeiten zwischen Klassen erkennen und globale Suchen durchführen, wie zum Beispiel: Welche Klassen implementieren eine bestimmte Schnittstelle? Entwicklungsumgebungen
33
2 Nitty Gritty • Start up!
T
Sandini Bib
Bild 2.4: Java-Quellcodeeditor
Nitty Gritty • Start up!
2
In Abbildung 2.4 sehen Sie die Java-Perspektive. Links unten befindet sich die Outline-View, die eine strukturierte Ansicht der Elemente einer Klasse bietet, rechts der Editor. In der aufgeklappten Box kann man mit Hilfe eines Assistenten eine kontextsensitive Syntaxergänzung nutzen. So tippt man zum Beispiel die ersten Buchstaben einer Methode ein und wählt die gewünschte aus einer Liste. 2.2.2
Borland JBuilder
Dieses Produkt gibt es für die Entwicklungsplattformen Windows XP/ 2000/NT, Linux, Solaris und Mac OS X. Auch dieses gibt es in drei Ausbaustufen: T
Die Personal Edition ist kostenlos erhältlich und stellt die Einsteigerversion dar.
T
Die Professional Edition unterstützt zusätzlich die Entwicklung von Servlets sowie von Datenbankanwendungen.
T
Darüber hinaus erleichtert die Enterprise Edition die Erstellung von Enterprise JavaBeans und CORBA-Anwendungen.
34
Integrierte Entwicklungsumgebungen
Sandini Bib
Eine Besonderheit dieser IDE ist, dass sie selbst komplett in Java geschrieben ist. Der JBuilder ist nicht an eine JDK-Version gebunden, man kann die Sprachversion also bei Bedarf wechseln. Das Werkzeug für die Erstellung grafischer Oberflächen kann JavaQuellcode interpretieren und in eine GUI umsetzen, so dass auch manuelle Änderungen im Quelltext keine Probleme bereiten.
Nitty Gritty • Start up!
2
Bild 2.5: Debugging im JBuilder
Entwicklungsumgebungen
35
Sandini Bib
3
Sandini Bib
Objektorientierung – Grundbegriffe
Um Java programmieren zu können, ist es wichtig, einige objektorientierte Grundkenntnisse zu besitzen, denn die Sprache setzt voll auf dem OO-Paradigma auf.
3.1
Klassen und Objekte
Klassen
class Konto // Klasse Konto { int nummer; // Variable double stand; // Variable }
Variablen Den Inhalt jeder Klasse, also deren Eigenschaften (Member), bilden Daten und Funktionen. Die Daten jedes Objekts, also deren Attribute, sind in den Variablen oder Feldern der Klasse abgelegt. Da diese Variablen in jeder Instanz (= jedem Objekt) anders belegt sein können, nennt man sie meist Instanzvariablen.
Objektorientierung – Grundbegriffe
37
3 Nitty Gritty • Start up!
Klassen sind prinzipiell nichts weiter als benutzerdefinierte Datentypen, die ebenso verwendet werden wie die Basistypen int, double oder char bzw. Strukturen wie struct in C, record in Pascal oder type in Basic. Klassen sind Schablonen, also eine Art exakt definierter Baupläne, aus denen zur Laufzeit Instanzen erzeugt werden. Diese Instanzen bezeichnet man auch als Objekte. Eine Klassendefinition in Java beginnt mit dem Schlüsselwort class und dem Namen der Klasse. Im folgenden Block, symbolisiert durch geschweifte Klammern, werden dann die Bestandteile der Klasse definiert:
Sandini Bib
Methoden class Konto // Klasse Konto { int nummer; // Variable double stand; // Variable void einzahlen(double betrag) // Methode { stand = stand + betrag; } }
Nitty Gritty • Start up!
3
Das Verhalten der Klassen wird durch deren Funktionen beschrieben. In Java bezeichnet man die Funktionen einer Klasse als Methoden. Diese Methoden unterscheiden die Klassen von den Strukturen oder Records prozeduraler Sprachen wie C oder Pascal. In objektorientierten Sprachen kapselt ein Datentyp nicht nur den strukturierten Aufbau und den Zustand eines Objekts, sondern auch dessen Funktionalität. Die Methoden sind also nicht global einem bestimmten Programm zugeordnet, sondern gehören zu einer Klasse. Damit ist die Klasse auch nicht an eine bestimmte Anwendung gebunden, sondern steht allen Nutzern offen. Meist gibt es noch eine besondere Art von Methoden, die so genannten Konstruktoren. Diese dienen dazu, neu erzeugte Objekte zu initialisieren. class Konto { int nummer; double stand; Konto(int neueNummer, double neuerStand) // Konstruktor { nummer = neueNummer; stand = neuerStand; } }
38
Klassen und Objekte
Sandini Bib
Grafische Darstellung Bestandteile/Member der Klasse Konto
Attribute
Funktionalität
Instanzvariablen Felder
Methoden
UML-Notation: Konto nummer : int stand : double abheben(betrag : double) einzahlen(betrag: double) ueberweisen(zielkonto : Konto, betrag : double)
3 In Abbildung 3.1 sehen Sie die Bestandteile einer Klasse grafisch dargestellt. Wir wollen im weiteren Verlauf des Buches – soweit möglich – die Notation der Unified Modeling Language (UML) benutzen, die sich für die Modellierung in objektorientierten Sprachen durchgesetzt hat. In der UML werden Klassen durch einen Kasten repräsentiert, oben steht der Klassenname, im mittleren Block folgen die Variablen und im unteren die Methoden. Eine Übersicht über alle im Buch benutzten UML-Symbole finden Sie im Anhang A. Objekte und Objektreferenzen Aus dem Bauplan einer Klasse werden zur Laufzeit Objekte, also konkrete Ausprägungen, erzeugt. Dies macht man wie in C++ mit dem Schlüsselwort new. Ein Objekt bzw. eine Instanz gehört genau einer Klasse an und hat einen Zustand, nämlich die aktuelle Belegung der Felder. In der Regel gibt es von einer Klasse gleichzeitig mehrere Instanzen, deren Zustand voneinander unabhängig ist. Objektorientierung – Grundbegriffe
39
Nitty Gritty • Start up!
Bild 3.1: Bestandteile einer Klasse
Sandini Bib
Konto nummer : int stand : double abheben(betrag : double) einzahlen(betrag: double) ueberweisen(zielkonto : Konto, betrag : double)
<> :Konto nummer = 0436572800 = 2.564,84 stand
:Konto nummer = 0436577800 stand = 18.663,97
Bild 3.2: Zwei Instanzen der Klasse Konto
Nitty Gritty • Start up!
3
Abbildung 3.2 zeigt zwei verschiedene Konto-Objekte in der UMLDarstellung. Die gestrichelte Linie zeigt die Instanziierung an. Jede Instanz wird mit einem Doppelpunkt und durch Unterstreichung gekennzeichnet. Für die einzelnen Instanzvariablen wird der Wert angegeben. Objekte werden im Speicher abgelegt, die automatische Speicherverwaltung von Java kümmert sich darum, dass genügend Speicherplatz angefordert wird und dieser bei Löschung des Objektes wieder freigegeben wird. Es nützt aber nichts, dass das Objekt irgendwo im Speicher existiert, man muss darauf auch wieder zugreifen können. Dazu verwendet man Referenzen, die auf ein bestimmtes Objekt verweisen. Man kann sich Objektreferenzen quasi als Namen des Objekts vorstellen (solange man dieser Referenz kein anderes Objekt zuweist). Referenzen sind also eine Art intelligente Zeiger, die automatisch immer auf das Objekt verweisen, auch wenn es sich im Speicher verschiebt. Es handelt sich jedoch nicht um wirkliche Zeiger wie in C. Denn hinter den Referenzen stecken keine Speicheradressen und man kann auch keine Zeigerarithmetik betreiben!
40
Klassen und Objekte
Sandini Bib
Und so erzeugt man in Java ein Konto-Objekt und weist es einer Referenz zu: Konto kundenkonto = new Konto();
Das erste Konto ist der Typ der Referenz mit dem Namen kundenkonto, diese Referenz kann also nur auf Instanzen der Klasse Konto verweisen. Diesem wird ein neues Objekt der Klasse Konto zugewiesen (siehe Abbildung 3.3). Entwicklungszeit
Laufzeit Programmablauf
Klassendefinition
1. Erzeugung einer Instanz 2. Zuweisung an eine Referenz
Konto new
kundenkonto
nummer : int stand : double
3 :Konto
Nitty Gritty • Start up!
nummer = 0436577800 stand = 18.663,97
Speicher
Bild 3.3: Erzeugen einer Instanz der Klasse Konto
<>
:Konto
Bild 3.4: Instanziierung in UML
Objektorientierung – Grundbegriffe
41
Sandini Bib
Abbildung 3.4 zeigt den Vorgang in der UML-Darstellung (Sequenzdiagramm). Der linke Pfeil gibt die Zeitachse an, die senkrechten Balken die Lebensdauer von Objekten. Die Löschung des Konto-Objektes ist mit einem Kreuz gekennzeichnet, das Männchen symbolisiert den Anwender. Danach kann man auf das Objekt zugreifen: int kontonummer = kundenkonto.nummer; kundenkonto.einzahlen(100.0);
Bedeutung von Klassen in Java
Nitty Gritty • Start up!
3
Java ist als objektorientierte Sprache komplett klassenbasiert. Anders als in C++ gibt es keine globalen Funktionen, Variablen oder Konstanten. Insbesondere gibt es auch keine globale main-Funktion. Die Ausführung einer Applikation beginnt mit der main-Methode derjenigen Klasse, die als Parameter an den Interpreter übergeben wurde. Da jede Klasse ihre eigene main-Methode enthalten kann, könnte zum Beispiel eine Gruppe mehrerer Klassen verschiedene Applikationen darstellen, je nachdem, mit welcher Klasse gestartet wurde. Auch Applets basieren auf Klassen. Wie wir später sehen werden, ist jedes Applet eine Unterklasse der Klasse java.applet.Applet. Spezialitäten von Variablen und Methoden Neben Instanzvariablen gibt es manchmal noch Felder, die der Klasse als Gesamtheit zugeordnet sind, also von allen Instanzen gemeinsam genutzt werden. Diese heißen Klassenvariablen. Analog existieren auch Klassenmethoden, die unabhängig von Objekten ausgeführt werden können. Ein Beispiel ist die bereits erwähnte main-Methode. Überladen von Methoden Methodenüberladung (Overloading) heißt, dass mehrere Methoden denselben Namen haben können. Welche von den gleichnamigen Methoden aufgerufen wird, hängt dann von den Aufrufparametern zur Laufzeit ab. Durch Methodenüberladung können Gruppen von semantisch gleichwertigen Methoden zusammengefasst werden. Für den Programmierer ist es sicherlich übersichtlicher, wenn alle Methoden, die gleichwertige Aufgaben realisieren, auch den gleichen Namen haben. 42
Klassen und Objekte
Sandini Bib void ueberweisen(Konto zielkonto, double betrag) ... void ueberweisen(int zielkontoNummer, double betrag) ...
3.2
Vererbung
Neben der Kapselung, also der Zusammenfassung von Daten und Funktionen in Klassen, ist die Vererbung das wichtigste objektorientierte Prinzip. Objekte können Eigenschaften von anderen Objekten bzw. deren Klassen »erben«, d. h. sie übernehmen deren Eigenschaften, sofern sie nicht in der eigenen Definition anders definiert sind. Damit muss man identischen Code nur einmal programmieren, was auch aus Wartbarkeitsgründen vorteilhaft ist.
Beispiel Vertiefen wir unser Konto-Beispiel. Neben dem allgemeinen Konto gebe es zwei abgeleitete Kontenarten: T
ein Sparkonto, für das zusätzlich Zinsen anfallen
T
ein Girokonto, das überzogen werden darf
Superklasse Die Eigenschaften (Konto-)nummer und stand sind bekannterweise nicht nur Eigenschaften von Sparkonten, sondern von allen Kontoarten. Daher werden diese Eigenschaften der Superklasse Konto zugeordnet. Diese Klasse enthält sämtliche Eigenschaften, die für alle Konten gelten. Subklasse Die Eigenschaft kreditlimit ist eine spezielle Eigenschaft von Girokonten. Die Klasse Girokonto wird von der Superklasse Konto abgeleitet und erbt sämtliche Eigenschaften. In der Subklasse Girokonto werObjektorientierung – Grundbegriffe
43
3 Nitty Gritty • Start up!
3.2.1 Super- und Subklassen Ausgehend von einer Superklasse (häufig auch: Oberklasse oder Basisklasse) können weitere Klassen abgeleitet werden, welche das Verhalten der Superklasse erben. Diesem Verhalten werden in der Subklasse (oder Unterklasse) zusätzliche Variablen und Methoden zugefügt, die Subklasse wird spezialisiert.
Sandini Bib
den nur noch die Eigenschaften hinzugefügt, die diese Klasse weiter spezialisieren. Auf diese Weise müssen allgemeine Eigenschaften nicht für jeden Kontentyp (z. B. Sparkonto, Kreditkartenkonto) neu definiert werden. Superklasse
Konto nummer : int stand : double abheben(...) einzahlen(...) ueberweisen(...)
<<erbt von>> <<extends>> Subklasse
Nitty Gritty • Start up!
3
Girokonto
Sparkonto
nummer {geerbt} stand {geerbt} kreditlimit : double abheben(...) {geerbt} einzahlen(...) {geerbt} ueberweisen(...) {geerbt} limitPruefen(...)
nummer {geerbt} stand {geerbt} zinssatz : double abheben(...) {geerbt} einzahlen(...) {geerbt} ueberweisen(...) {geerbt} zinsenBerechnen(...)
Bild 3.5: Einfache Vererbung
In der UML wird Vererbung durch einen Pfeil mit nicht ausgefüllter Pfeilspitze dargestellt, der auf die Superklasse zeigt (siehe Abbildung 3.5). Vererbung in Java In Java wird obiges Beispiel folgendermaßen implementiert: Beispiel class Konto { int nummer; double stand; void abheben(double betrag) { if (stand > betrag) stand = stand - betrag;
44
Vererbung
Sandini Bib } void einzahlen(double betrag) { stand = stand + betrag; } void ueberweisen(Konto zielkonto, double betrag) { abheben(betrag); zielkonto.einzahlen(betrag); } } class Girokonto extends Konto { double kreditlimit; boolean limitPruefen(double betrag) { if (stand – betrag > -kreditlimit) return true; else return false; } }
3.2.2 Überschreiben von Methoden Die Methoden von Subklassen können Methoden ihrer Superklasse überschreiben. Sie deklarieren diese in der Subklasse einfach mit dem gleichen Namen und derselben Signatur. Häufig wird in einer überschreibenden Methode zunächst die verschattete Methode der Superklasse aufgerufen und dann eigene Funktionalität ergänzt. Beispiel Unser Girokonto hatte bisher noch ein Problem: Man konnte zwar abheben, aber nur so lange noch Geld vorhanden war, das Kreditlimit wurde ignoriert. Man könnte nun zwar eine eigene Methode abhebenMitLimit() schreiben, aber dank Überschreiben ist es noch einfacher:
Objektorientierung – Grundbegriffe
45
Nitty Gritty • Start up!
3
Sandini Bib class Girokonto extends Konto { [...] void abheben(double betrag) { if (limitPruefen(betrag)) stand = stand - betrag; } }
Wenn wir mit Konten arbeiten, erkennt die Java-Laufzeitumgebung automatisch, dass es sich um ein Girokonto handelt (also den spezielleren Typ), und ruft immer die korrekte Methode auf. Man spricht auch von virtuellen Methoden. In obigem Code-Beispiel habe ich denjenigen Teil weggelassen, der für den aktuell erläuterten Sachverhalt irrelevant ist. Eine Auslassung im Code ist mit [...] gekennzeichnet.
Nitty Gritty • Start up!
3 3.2.3
Mehrfachvererbung und Interfaces
Einfachvererbung Mehrfachvererbung bedeutet, dass eine Klasse von mehreren Superklassen abgeleitet ist. Dies kann jedoch zu Mehrdeutigkeiten führen, wenn in den Superklassen ein und desselben Objektes ein Attribut oder eine Methode gleichen Namens vorkommt. Außerdem kann man sich in einem solchen Geflecht von Vererbungsstrukturen leicht verirren. Deshalb unterstützt Java nur Einfachvererbung, das heiß jede Klasse (bis auf die Urklasse Object) besitzt genau eine Superklasse. Interfaces Trotzdem möchte man häufig Klassen definieren, die die Schnittstellen mehrerer Basisklassen erfüllen. Beispielsweise könnte man eine »Klasse« Verzinsbar geschrieben haben, die gemeinsame Schnittstellen für Wertpapiere und Sparkonten anbietet. Von einer solchen Klasse können wir aber in unserem Beispiel nicht mehr ableiten, da das Sparkonto bereits von Konto abgeleitet ist.
46
Vererbung
Sandini Bib
Konto
Verzinsbar
Konto
<> Verzinsbar
Sparkonto
Wertpapier
verboten! Sparkonto
Wertpapier
Bild 3.6: Interfaces
Zu diesem Zweck gibt es in Java Interfaces. Dies sind rein abstrakte Klassen ohne Instanzvariablen und Methodenimplementierungen. Das heißt, die Methoden werden an dieser Stelle nicht implementiert, sondern es wird nur die Signatur (der Methodenkopf mit der Parameterliste) angegeben. Damit ist eine Mehrdeutigkeit ausgeschlossen, man kann aber damit das Vorhandensein von Methoden garantieren. Wenn eine Klasse ein Interface erfüllt, spricht man vom Implementieren. Die Klasse muss dann all jene Methoden implementieren, deren Deklaration im Interface enthalten ist.
Nitty Gritty • Start up!
Beispiel interface Verzinsbar { double zinsenBerechnen(); } class Sparkonto extends Konto implements Verzinsbar { [...] double zinsenBerechnen() { [...] } }
3.2.4 Vorteile/Nachteile der Vererbung Der Einsatz von Vererbung bringt einige wichtige Vorteile:
Objektorientierung – Grundbegriffe
3
47
Sandini Bib
Wiederverwendbarkeit Das Verhalten einer Superklasse wird an Subklassen vererbt. Somit wird der Programmcode der Superklasse auch in den Subklassen verwendet und muss daher nicht erneut geschrieben werden. Dadurch entfällt auch die parallele Wartung (und Änderung) mehrerer ähnlicher Codesequenzen für alle Subklassen – eine unangenehme Konstellation, die eine riskante Fehlerquelle darstellt. Konsistenz von Schnittstellen Methoden und Variablen, die bereits in der Superklasse definiert wurden, sind auch für sämtliche Subklassen gleich. Damit sind einheitliche Schnittstellen wenigstens bezüglich des in der Superklasse definierten Verhaltens gewährleistet. Diese Schnittstellen sind so auch in allen Subklassen gleich. Rapid Prototyping
Nitty Gritty • Start up!
3
Durch Benutzung von wiederverwendbaren Komponenten, welche durch Klassenhierarchien definiert werden können, kann der Entwicklungsaufwand für neue Projekte auf das Erstellen der projektspezifischen neuen Komponenten beschränkt werden. Der Ansatz des »Rapid Prototyping« besteht darin, möglichst schnell einen Grundstock von Softwarekomponenten zum Laufen zu bringen und dann Schritt für Schritt weitere Komponenten hinzuzufügen. Information Hiding Der Anwender der Klassenhierarchien muss nur die öffentlichen Schnittstellen der Klassenhierarchien verstehen. Die konkrete Implementierung bleibt ihm dabei verborgen. Mit Hilfe verschiedener Zugriffsrechte kann die Sichtbarkeit der Klassenbestandteile innerhalb der Vererbungshierarchie fein gesteuert werden. In manchen Fällen kann sich der Einsatz von Vererbung auch als Nachteil herausstellen: Effizienz Allgemein gehaltene Methoden in Superklassen sind oft langsamer und weniger effizient als für den Einzelfall »maßgeschneiderte« Methoden. Hier hat eine elegantere Programmierung und bessere Les48
Vererbung
Sandini Bib
barkeit des Programmcodes Vorrang vor höchstoptimierten, jedoch nicht wiederverwendbaren Lösungen. Komplexität Das Vererbungskonzept kann jedoch im Extremfall auch zum genauen Gegenteil führen. Unendlich verzweigte und in die Tiefe gehende Klassen- und Interface-Hierarchien sind schwer zu durchschauen und machen den Programmcode wenig transparent. Hier muss an die Erfahrung und das Fingerspitzengefühl des Programmierers appelliert werden. Nicht selten geschieht es, gerade in den Anfängen der Java-Programmierung, dass die Begeisterung über ein elegantes Vererbungskonzept den Blick für einfache und durchschaubare Klassenhierarchien trübt. Vor jeder Definition einer Klassen- und Interface-Hierarchie sollten Sie sich daher genau überlegen, ob zur Lösung dieses Problems wirklich eine Klassenhierarchie nötig ist oder ob dieses Problem auch mit einfacheren Konstrukten zu lösen ist.
3
Nicht umsonst zitiert der Erfinder der Programmiersprache C++ (von dessen Vererbungskonzept das von Java abstammt), Bjarne Stroustrup, bezüglich des Designs von C++-Klassen den Mathematiker Albert Einstein: »Haltet die Dinge einfach: So einfach wie möglich, aber nicht einfacher.«
Objektorientierung – Grundbegriffe
49
Nitty Gritty • Start up!
Einstein-Zitat
Sandini Bib
Teil II – Take that!
Sandini Bib
TEIL II
TAKE THAT!
Sandini Bib
Der zweite Teil dieses Buches stellt die Syntax von Java und die Standardklassenbibliotheken vor. Er soll Ihnen als Referenz dienen, wenn Sie eine kurze Einführung der einzelnen Themen und eine Beschreibung der wichtigsten Klassen, Methoden und Schlüsselwörter suchen. Wenn Sie nach Tipps suchen, welche Möglichkeiten von Java wie einzusetzen sind, dann werden Sie eher im dritten Teil fündig.
4
Sandini Bib
Java-Syntax
In diesem Kapitel möchte ich Ihnen die Schlüsselwörter der Sprache Java erklären. Dabei werden uns die bereits bekannten Klassen und Objekte aus den ersten Kapiteln wieder begegnen. Es gibt aber noch Möglichkeiten, das Verhalten der Klassen und einzelnen Member zu beeinflussen, beispielweise durch die Angabe von Zugriffsrechten. Des Weiteren werden Sprachkonstrukte wie Schleifen, bedingte Anweisungen etc. behandelt. Ebenfalls wichtig sind die elementaren Datentypen wie int oder double.
4.1
Sprachelemente
In Java gibt es – wie in jeder Programmiersprache – eine Reihe von Schlüsselwörtern, die der Compiler als Sprachelemente erkennt. Daneben gibt es noch Kontrollstrukturen, Bezeichner (Namen oder Identifikatoren), konstante Werte (Literale), Operatoren (z. B. +, <, &, ...) und Kommentare.
Schlüsselwörter Nitty Gritty • Take that!
4.2
4
Java kennt folgende Schlüsselwörter: abstract
default
if
package
this
assert
do
implements
private
throw
boolean
double
import
protected
throws
break
else
instanceof
public
transient
byte
extends
int
return
true
case
false
interface
short
try
catch
final
long
static
volatile
char
finally
native
super
void
class
float
new
switch
while
continue
for
null
synchronized
Java-Syntax
53
Sandini Bib
Daneben gibt es noch die reservierten Wörter const und goto, die aber nicht benutzt werden. Sie dienen zur Zeit vor allem dazu, sprechende Compilermeldungen ausgeben zu können (»not supported«), wenn ein C++-Programmierer diese in C gebräuchlichen Schlüsselwörter benutzt.
Nitty Gritty • Take that!
4 Bild 4.1: Schlüsselwörter nach Einsatzbereich
4.3
Kommentare
Es gibt in Java drei Arten von Kommentaren: Einzeiliger Kommentar Kommentare, die für den Rest einer Zeile gelten, leitet man mit zwei Schrägstrichen ein. Beispiel // Kreditprüfung if (limitPruefen(b)) // wenn Limit nicht überschritten
54
Kommentare
Sandini Bib
Mehrzeiliger Kommentar Diese dürfen alle Zeichen beinhalten, bis auf das Schlusszeichen (*/), das heißt ein mehrzeiliger Kommentar darf auch einen einzeiligen beinhalten. Schachteln darf man sie allerdings nicht. Beispiel /* Abheben ist nur zulässig, solange das Kreditlimit nicht überschritten ist */ if (limitPruefen(b)) [...] /* Schachteln ist /* nicht */ erlaubt! */
javadoc-Kommentar Für die Generierung von Code-Dokumentation gibt es einen speziellen Kommentar, den der javadoc-Compiler in eine HTML-Form bringt: Beispiel
Bild 4.2: Ausschnitt aus der Javadoc-Dokumentation des JDK 1.2 Java-Syntax
55
4 Nitty Gritty • Take that!
/** * method for converting Euro currencies * uses rounding and correct scaling * @return java.lang.String converted value [...] */ public String calc(String value, String sourceCurr, String destCurr) throws CurrencyTypeException, CurrencyFormatException { [...]
Sandini Bib
Eine mittels dieses Tools generierte Dokumentation finden Sie in Form der Standard-API-Beschreibung für Java von Sun. In diesem Kommentar können spezielle Kontrollzeichen eingebettet werden, die in der generierten HTML-Dokumentation hervorgehoben oder als Links erscheinen: Tag
Bedeutung
Beispiel
Hyperlink auf referenzierte Klasse oder Methode
@see java.lang.String
Allgemein @see
Bei Methoden @return
Rückgabetyp und Beschreibung; wird zur Hervorhebung bei Methodendokumentation verwendet
@return java.lang.String converted value
@param
Name und Beschreibung eines Parameters, wird zur Hervorhebung bei Methodendokumentation verwendet
@param destCurr destination currency for conversion
@exception
Hinweis auf und Beschreibung von Exceptions, die in der Methode auftreten können
@exception CurrencyTypeException unknown currency for conversion
@version
Versionsangabe für Klassen
@version 1.1.8C
@author
Angabe des Autors einer Klasse
@author Florian Hawlitzek
Nitty Gritty • Take that!
4 Bei Klassen
4.4
Aufbau von Klassen
Wie bereits im vorigen Kapitel beschrieben, bestehen Klassen aus Members, die entweder Variablen (Felder) sind oder Methoden. In Java sieht eine Klassendeklaration so aus: <<Modifier> class Klassenname { // Memberdefinitionen }
56
Aufbau von Klassen
Sandini Bib <Modifier> steht für verschiedene Schlüsselwörter, die Hinweise für den Compiler sind und zum Beispiel einen Zugriffsschutz angeben. Wir werden sie in Abschnitt 4.5 behandeln.
Die Ableitung von einer anderen Klasse wird mit extends angegeben, die Implementierung eines Interfaces mit implements: <Modifier> class Klassenname extends Superklasse implements Interfacename { // Memberdefinitionen }
Laut Namenskonvention schreibt man Klassennamen mit einen großen Anfangsbuchstaben, also z. B. Konto, String, SecurityManager. 4.4.1
Member von Klassen
Instanzvariablen
<Modifier> variablenname;
Beispiel class Konto { int nummer; double stand; Kunde inhaber;
// Klasse Konto // // // //
Instanzvariable für Kontonummer Instanzvariable für Kontostand Instanzvariable für Kontoinhaber Referenz auf anderes Objekt
}
Die Instanzvariablen werden nach der Erzeugung der Instanz gültig, das heißt, sie existieren erst nach der Erzeugung eines konkreten Objekts. Jedes Objekt dieser Klasse besitzt eine eigene Belegung der Instanzvariablen.
Java-Syntax
57
4 Nitty Gritty • Take that!
In den Instanzvariablen (Feldern) werden die Attribute einer Instanz gespeichert. Für jede Variable muss ein Typ angegeben werden. Dies können beliebige Datentypen sein wie zum Beispiel primitive Datentypen int, double oder char, aber auch Referenzen auf Objekte beliebiger Klassen. Die allgemeine Syntax lautet
Sandini Bib
Klassenvariablen Daneben gibt es noch Klassenvariablen, die nur einmal für die Gesamtklasse existieren. Alle Instanzen greifen daher auf denselben Wert zu und man kann diesen sogar abfragen, wenn es überhaupt keine Instanzen gibt. Klassenvariablen werden wie Instanzvariablen deklariert, allerdings mit dem Modifier static (siehe Abschnitt 4.5.2) gekennzeichnet: <Modifier> static variablenname;
Konstanten Konstanten sind unveränderliche Werte, die bei der Deklaration zugewiesen werden und danach unveränderlich sind. Sie werden mit den Modifiern final und static (siehe Abschnitt 4.5.2 und 4.5.3) gekennzeichnet: <Modifier> final static konstantenname = <wert>; public static final double PI = 3.14159265897;
Nitty Gritty • Take that!
4
Methoden Die Methoden einer Klasse haben vollen Zugriff auf alle Variablen und Methoden derselben Klasse. Die Methoden sind Teil der öffentlichen Schnittstelle eines Objektes, sie definieren ihr Verhalten und erlauben den Zugriff auf die Instanz, für die sie aufgerufen werden. Sie bestehen aus einem Deklarationsteil, der den Namen und die Parameterliste enthält, und einem Block mit der Implementierung der Methode. Gegebenenfalls wird die Deklaration – wie wir noch sehen werden – noch um Hinweise für den Compiler ergänzt, zum Beispiel Zugriffsrechte der Methode oder mögliche Fehler. Anders als in anderen Sprachen wird in Java Deklaration und Implementierung nicht getrennt. <Modifier> methodenname(...);
Beispiel class Girokonto extends Konto // Klasse Girokonto { double kreditlimit; // Instanzvariable
58
Aufbau von Klassen
Sandini Bib void abheben(double betrag) // Methodendeklaration { // Implementierungsblock if (limitPruefen(betrag)) stand = stand - betrag; } boolean limitPruefen(double betrag) // Methodendekl. { // Implementierungsblock if (stand – betrag > -kreditlimit) return true; // Beendigung else return false; // Beendigung } }
Innerhalb des Methodenblocks können lokale Variablen deklariert werden. Hier noch einmal eine Variante der Methode abheben(): Beispiel void abheben(double betrag) { boolean kreditPruefungOK = limitPruefen(betrag); if (kreditPruefungOK) stand = stand - betrag; }
Die Reihenfolge der Methoden- und Variablendeklarationen innerhalb einer Klassendefinition spielt übrigens keine Rolle. So ist es etwa im vorigen Beispiel zulässig, dass in abheben() bereits limitPruefen() aufgerufen wird, obwohl die Deklaration erst später erfolgt. Es ist zwar üblich, Java-Syntax
59
4 Nitty Gritty • Take that!
Die Methode wird so lange abgearbeitet, bis das Ende des Implementierungsblocks erreicht wird oder die Anweisung return erfolgt. return beendet die Methode sofort und gibt denjenigen Wert als Methodenrückgabewert zurück, der nach der Anweisung steht (siehe Methode limitPruefen()). Dieser Wert muss von dem in der Deklaration angegebenen Typ sein. Falls eine Methode keinen Rückgabewert besitzt, gibt man void an, der Abbruch erfolgt dann nur mit return, das in diesem Fall am Blockende auch weggelassen werden kann (siehe Methode abheben() im obigen Beispiel).
Sandini Bib
dass zuerst die Variablen deklariert werden und dann die Methoden, vorgeschrieben ist es jedoch nicht. Der JavaCompiler arbeitet nämlich als Two-Pass-Compiler, der schon im ersten Lauf die Deklarationen liest. Laut Namenskonvention schreibt man Variablen und Methoden mit einem kleinen Anfangsbuchstaben, also z. B. nummer, abheben(), setSize(). Abweichungen gibt es bei Konstanten, die häufig komplett in Großbuchstaben geschrieben werden. Dies ist aber nicht einmal innerhalb der Standardklassen des JDK konsistent. 4.4.2
Zugriffsrechte und -methoden
Zugriffsrechte In Abschnitt 4.5.1 werden wir die Zugriffsmodifier detailliert vorstellen. Aufgrund ihrer elementaren Bedeutung bei der Modellierung von Klassen hier aber vorab eine kurze Erläuterung:
Nitty Gritty • Take that!
4
Zugriffsrechte gibt es sowohl für Klassen als auch für deren Member. Der niedrigste Zugriffsschutz ist public (überall sichtbar). Öffentliche Klassen, Variablen und Methoden sind aus jedem Kontext sichtbar. Dies ist häufig nicht erwünscht, deshalb deklariert man interne Hilfsmethoden und Variablen private (nur innerhalb der Klasse sichtbar). Dadurch kann man die Implementierung später leicht anpassen und verbessern ohne die Schnittstelle nach außen zu verändern. Die Zugriffsrechte protected und »default« (ohne Angabe eines Modifiers) sind Spezialfälle für private Klassen oder Member, die aber für bestimmte andere Klassen öffentlich zugreifbar sind, zum Beispiel deren Subklassen oder den Klassen im selben Package (siehe Abschnitt 4.10.4). Unser Konto könnte nun wie folgt aussehen: public class Konto { private int nummer; private double stand; public void abheben(double betrag) {
60
Aufbau von Klassen
Sandini Bib if (stand > betrag) stand = stand - betrag; } [...] }
Wir könnten nun die public-Elemente aus jeder Klasse heraus aufrufen und nutzen, egal in welcher Bibliothek (bzw. welchem Package, siehe Abschnitt 4.10) sie steht. Der direkte Zugriff auf die internen Daten hingegen wäre von außerhalb der Klasse Konto gesperrt. Um abgeleiteten Klassen wie dem Girokonto den Zugriff zu erlauben hätten wir sie auch protected deklarieren können. Zugriffsmethoden Die Nutzer der Klassen haben nun keinen direkten Zugriff auf den Kontostand mehr und können ihn nur noch indirekt über die Methoden abheben(), einzahlen() und ueberweisen() manipulieren. Dies mag in manchen Fällen sinnvoll sein, aber in der Regel möchte man auf die Inhalte der Felder einer Klasse gezielt zugreifen können. Zu diesem Zweck kann man Zugriffsmethoden einsetzen. Diese stellen öffentliche Lese- und Schreibfunktionalität bereit (sofern dies für die Variable erwünscht ist):
Nitty Gritty • Take that!
Beispiel
4
public class Konto { private int nummer; private double stand; public int getNummer() { return nummer; } public double getStand() { return stand; } public void setStand(double betrag)
Java-Syntax
61
Sandini Bib { if (betrag > 0) stand = betrag; else // Fehlermeldung throw new KontoLimitException( "Keine Überziehung erlaubt."); } [...] }
Zugriffsmethoden und public-Instanzvariablen Bei Analyse der Implementierung der Zugriffsmethoden (Selektoren) getNummer() und setStand() für die Instanzvariablen im obigen Beispiel stellt sich die Frage, ob dieser zusätzliche Programmieraufwand notwendig ist oder ob man nicht gleich die Klassenvariablen auf public setzen soll. Es gibt aber entscheidende Gründe, die dagegen sprechen:
Nitty Gritty • Take that!
4
T
Mit Hilfe der Zugriffsmethoden kann man garantieren, dass Objekte der Klasse immer gültige Werte enthalten (indem man darin etwa Bereichsüberprüfungen vornimmt). In der obigen Kontoimplementierung kann der Anwender der Klasse zum Beispiel nicht direkt schreibend auf die Variable stand zugreifen und sie so verändern, dass sie ungültig wären, da sie private deklariert wurde. Wenn der Zugriff über setStand() oder die anderen Methoden erfolgt, kann man – wie im obigen Beispiel – zusätzliche Validierungen vornehmen, die einen gültigen Objektzustand sicherstellen.
T
Man kann gezielt bestimmen, welcher Zugriff erlaubt ist. Zum Beispiel ist im Kontobeispiel eine nachträgliche Manipulation der Kontonummer verboten, die Variable kann nur gelesen werden (Read-Only). Anders verhält es sich beim Kontostand, der gelesen und geschrieben werden darf. Man könnte sich jedoch auch WriteOnly-Variablen vorstellen.
T
Ein weiterer Grund ist der der Datenkapselung bzw. Data Hiding. Es wird eine klare Schnittstelle zum Anwender der Klasse geschaffen, da dieser von der internen Implementierung der Klasse nicht
62
Aufbau von Klassen
Sandini Bib
T
T
mehr abhängig ist. Somit muss der Anwender nicht über Implementierungsdetails informiert sein und der Programmierer der Klasse kann jederzeit die interne Darstellung seiner Daten ändern, ohne dass viel Code bei den Verwendern geändert werden müsste. Jedes Verbergen von Implementierungsdetails dient auch dem Know-how-Schutz, da nach außen nur die öffentliche Schnittstelle sichtbar ist. Das Anbieten von Zugriffsmethoden ist im JavaBeans-Komponentenstandard festgelegt. Damit kann jedes Werkzeug automatisch die öffentlichen Attribute erkennen und »sauberen« Code generieren.
Namenskonvention
Wenn es sich um boolesche Werte handelt, also Variablen vom Typ boolean, die nur die Werte true oder false annehmen können, benutzt man statt get häufig auch das Präfix is. Variable
Get-Methoden
Set-Methode
Visible
isVisible(), getVisible()
setVisible()
private boolean visible;
public boolean isVisible() { return getVisible(); }
public void setVisible(boolean v)
public boolean getVisible()
{ visible = v; }
{ return visible; }
4.4.3
Konstruktoren
Uninitialisierte Variablen In C und anderen Programmiersprachen existiert das Problem des Zugriffs auf uninitialisierte Variablen. Als Beispiel betrachtet, sei folgendes, ohne Fehler übersetzbares, Programmstück in C gegeben: Java-Syntax
63
4 Nitty Gritty • Take that!
Wie wir in Kapitel 10.4 beim Komponentenstandard JavaBeans sehen werden, gibt es auch dort Zugriffsfunktionen, und sie werden zwingend mit den Präfixen get und set begonnen. Man nennt sie deshalb auch Getter und Setter. Um Bean-kompatibel zu sein und eine verständliche Benennung zu erhalten empfiehlt es sich, auch schon jetzt diese Namenskonvention einzuhalten, auch wenn dann bei deutschen Bezeichnern gemischtsprachige Namen entstehen: getTag().
Sandini Bib
Beispiel main() { int x, y; y = x + 2; }
Als Ergebnis steht nun in y ein Wert, der – je nach Compiler – einen um zwei erhöhten zufälligen Speichereintrag an der Speicheradresse der Variable x enthält oder einen durch den Compiler vorinitialisierten und dann inkrementierten Wert. Dieses merkwürdige Verhalten ist oft die Ursache unauffindbarer Programmierfehler. Konstruktor
Nitty Gritty • Take that!
4
Java (und auch C++) bietet zur Lösung dieses Problems eine spezielle Methode, den Konstruktor. Der Konstruktor ist eine eigene, vom Programmierer der Klasse geschriebene Methode, die automatisch für jede Instanz der Klasse aufgerufen wird, sobald diese Instanz kreiert wird. Er kann also dazu eingesetzt werden, ein Objekt direkt bei dessen Erschaffung zu initialisieren. Damit kann unter anderem mit dem Konstruktor dafür gesorgt werden, dass ein Objekt immer »korrekte« Werte enthält. Ein Unterschied zu C++ ist, dass in Java bei der Neuerschaffung eines Objekts (mit new) alle Membervariablen (Felder) automatisch initialisiert werden, bevor der Konstruktor aufgerufen wird. Referenzen werden mit null initialisiert, boolesche Variablen mit false, Zahlen mit 0 und Character mit »\0«. Damit befindet sich jedes Objekt nach der Erzeugung in einem definierten Zustand. Java-Compiler melden bei nicht explizit initialisierten lokalen Variablen einen Fehler: Variable x may not have been initialized.
Bei Instanz- und Klassenvariablen wird die Initialisierung wie gerade beschrieben durchgeführt.
64
Aufbau von Klassen
Sandini Bib public class Konto { private double stand; // Instanzvariable, automatisch // pro Instanz mit 0.0 vorbelegt [...] public void abheben(double betrag) { boolean kreditPruefung; // Fehler, da Variable // lokal für diesen Block boolean kreditPruefungOK = limitPruefen(betrag); // So ist es OK! if (kreditPruefungOK) stand = stand - betrag; } [...] }
Ein Konstruktor besitzt den Namen der Klasse und es wird kein Rückgabetyp angegeben. Konstruktoren gehören in der Regel zur öffentlichen Schnittstelle und sind damit public zu deklarieren. Die Kontoklasse könnte damit wie folgt erweitert werden: Beispiel public class Konto { private int nummer; private double stand; public Konto() { nummer = Bank.getNaechteFreieNummer(); } public Konto(double neuerStand) { nummer = Bank.getNaechteFreieNummer(); stand = neuerStand;
Java-Syntax
65
4 Nitty Gritty • Take that!
Eine Initialisierung der Felder durch den Programmierer ist somit nur zwingend erforderlich, falls er geeignetere Werte zuweisen will. Allerdings zeugt es von gutem Programmierstil, trotzdem alle Werte von Hand zu initialisieren, da dadurch der Code verständlicher wird.
Sandini Bib } public Konto(int neueNummer, double neuerStand) { nummer = neueNummer; // ggf. checken, ob frei... stand = neuerStand; } }
Damit wird jetzt bei jeder Definition eines Objektes der Klasse Konto deren Konstruktor aufgerufen und für eine gültige Anfangsbelegung der Instanzvariablen gesorgt. // Nutzung des einfachen Konstruktors: Konto meinKonto = new Konto(); // Konto() ruft einen // Konstruktor auf. // Nutzung eines Konstruktors mit Parametern: Konto einAnderes = new Konto(453476800, 3000);
Nitty Gritty • Take that!
4
Dies verdeutlicht auch die Schreibweise dieser Instanziierung. Der Teil vor der Zuweisung ist die Deklaration einer Referenz der Klasse Konto (als Typ), wohingegen die zweite Hälfte ein neues Objekt mit Hilfe des Konstruktors Konto() erzeugt. Der Konstruktoraufruf ist ein Methodenaufruf, daher auch die runden Klammern. Im Kontobeispiel sahen Sie eine Überladung von Methoden. Der Konstruktor existiert in mehreren Varianten, aber mit unterschiedlichen Parametern. Dies kann man in Java mit allen Methoden so machen. Rückgabewert Beachten Sie, dass der Konstruktor keinen Rückgabewert besitzt (nicht einmal void). Damit darf der Konstruktor auch keine return-Anweisung enthalten. Der Konstruktor liefert keinen Wert, sondern er initialisiert ein Objekt. Default-Konstruktor Schreibt man in einer Klassen überhaupt keinen Konstruktor (wie ich beim Kontobeispiel in den ersten Kapiteln), ergänzt der Compiler einen Konstruktor ohne Parameter und mit leerer Implementierung. Man nennt diesen Default-Konstruktor.
66
Aufbau von Klassen
Sandini Bib
Dies kann dann zu einem Problem werden, wenn Sie einer Klasse mit einem Default-Konstruktor nachträglich einen Konstruktor mit Parametern hinzufügen. Denn in diesem Fall entfernt der Compiler den Default-Konstruktor und alle Objektinstanziierungen der Art new Klassenname() werden ungültig, da ja nur noch der neue Konstruktor gültig ist! In diesem Fall sollten Sie einen Konstruktor ohne Parameter und mit leerem Implementierungsblock selbst schreiben. Damit läuft der Code, der sich bisher auf dem DefaultKonstruktor abgestützt hat, weiterhin einwandfrei. Besser ist es jedoch, sich vorab Gedanken zu machen, welche Arten von Konstruktoren Sie dem Nutzer Ihrer Klasse anbieten wollen. finalize-Methode
Objekte können aber nicht nur Speicher belegen. Zum Beispiel könnte eine Datei geöffnet oder ein Netzwerk-Socket belegt worden sein. Diese Ressourcen müssen freigegeben werden können. Zu diesem Zweck gibt es eine spezielle Methode finalize(), die vom Garbage Collector vor dem Löschen aufgerufen und abgearbeitet wird (siehe Abschnitt 13.2). Diese kann man in jeder Klasse hinzufügen, muss die Signatur aber genau einhalten (Methodenkopf genau wie unten): Beispiel protected void finalize() { offeneDatei.close(); // Beispiel für Ressourcenfreigabe }
Java-Syntax
67
4 Nitty Gritty • Take that!
Anders als in anderen Programmiersprachen braucht sich der JavaProgrammierer keine Gedanken um die Speicherbereinigung zu machen. Dies erledigt das Laufzeitsystem automatisch. Wenn ein Objekt nicht mehr referenziert wird, weil alle Referenzen ungültig sind (Blockende erreicht oder explizit auf null gesetzt), kann es zu einem beliebigen Zeitpunkt vom System entsorgt werden (siehe Kapitel 13.2).
Sandini Bib
4.4.4 Innere Klassen Es kommt häufig vor, dass für die Programmierung einer Klasse eine Hilfsklasse benötigt wird, die nur im Kontext dieser Klasse sinnvoll ist und die nach außen hin vollkommen verborgen sein soll. Solche Klassen werden in Java als eingebettete oder innere Klassen bezeichnet, da sie direkt im Rumpf der Hauptklasse deklariert werden. Beispiel
Nitty Gritty • Take that!
4
public class Konto { // innere Klasse class PruefregelnFuerUeberweisung { boolean pruefeKonto(Konto zielkonto) {...} boolean pruefeDeckung(double betrag) {...} } [...] void ueberweisen(Konto zielkonto, double betrag) { [...] // nutzt Hilfsklasse } }
Verwendung Innere Klassen könnte man durch packageweit sichtbare Klassen oder private Methoden der umgebenden Klasse ersetzen. Die Implementierung mit inneren Klassen ist aber meist eleganter, denn zum einen bringt sie die Zugehörigkeit zu genau einer Klasse zum Ausdruck und zum anderem kapselt eine innere Klasse Funktionalität besser als ein Satz von Methoden. Innere Klassen werden gerne zur Programmierung von grafischen Oberflächen benutzt. So gibt es zum Beispiel Hilfsklassen zur Bearbeitung bestimmter Ereignisse (Events) oder Darstellungsvarianten der Zellen einer Tabellenklasse, die nur im speziellen Kontext ihrer umgebenden Klasse nutzbar sind.
68
Aufbau von Klassen
Sandini Bib
Es ist sogar möglich, so genannte anonyme innere Klassen zu erstellen. Die haben keine Namen, sondern werden innerhalb eines Methodenaufrufs definiert. new { }
Leider führen anonyme Klasse zu ziemlich schlecht lesbaren Code, deshalb rate ich von ihrer Verwendung ab.
4.5
Modifier
Modifier sind Schlüsselwörter, die die Definition von Klassen, Methoden und Variablen beeinflussen. Die Modifier werden vor den Namen dieser Elemente geschrieben, zum Beispiel <Modifier> void abheben(double betrag).
In diesem Abschnitt werden die verschiedenen Modifier kurz vorgestellt. Die meisten Modifier wurden oder werden an anderer Stelle noch ausführlicher behandelt. In diesem Kapitel steht der Gesamtüberblick im Vordergrund. Zugriffsrechte
Am Beispiel der Klassen Konto (Abschnitt 4.4.2) haben Sie vielleicht schon gesehen, dass es vorteilhaft ist, den Zugriff auf Klassen, Variablen und Methoden limitieren zu können. Mit Hilfe der Schlüsselwörter public, private und protected können Sie die Sichtbarkeit und den Zugriffsschutz aus anderen Klassen heraus steuern. Für die Variablen und Methoden gilt: Schlüsselwort private Private Member sind die interne Schnittstelle der Klasse. Auf diese Komponenten der Klasse kann nur von den Methoden der Klasse selbst zugegriffen werden. Es handelt sich um Implementierungsdetails, die für andere Klassen nicht sichtbar oder manipulierbar sein sollen. In der UML werden private Komponenten durch ein »-« -Symbol vor dem Namen abgekürzt.
Java-Syntax
69
Nitty Gritty • Take that!
4.5.1
4
Sandini Bib
Schlüsselwort public Public Member sind die öffentliche Schnittstelle der Klasse. Es existiert kein Zugriffsschutz. Auf die Komponenten der Klasse kann von jeder beliebigen Klasse aus zugegriffen werden. UML-Kürzel ist »+«. Schlüsselwort protected Dieses Zugriffsrecht spielt bei der Vererbung eine Rolle und ist eine Mischung zwischen private und public. Es erlaubt den Zugriff für die Methoden der Klasse sowie Methoden aller abgeleiteten Klassen und beliebiger anderer Klassen innerhalb des Packages (siehe Abschnitt 4.10, Achtung: Unterschied zu C++). Zugriffe von anderen Programmstellen aus auf protected-Methoden und -Variablen sind verboten. Symbol in der UML ist das »#«-Zeichen. Default
Nitty Gritty • Take that!
4
Falls überhaupt kein Zugriffs-Modifier angegeben wurde, gilt das Default-Zugriffsrecht. Der Zugriff ist für alle Methoden von Klassen innerhalb desselben Packages möglich. Packages werden in Abschnitt 4.10 behandelt. Das in JDK 1.0.1 definierte Zugriffsrecht private protected (entsprach protected in C++) wird im aktuellen Sprachstandard nicht mehr unterstützt. Beispiel public class Konto // öffentlich sichtbar { // Instanzvariablen: private int nummer; // nur intern sichtbar // -> Zugriff über get-Methode protected double stand; // intern und in Subklassen // sichtbar // Konstruktoren: public Konto(){ [...] } // öffentlich sichtbar public Konto(double neuerStand) { [...] } public Konto(int neueNummer, double neuerStand) { [...] } // Zugriffsmethoden:
70
Modifier
Sandini Bib public int getNummer() // öffentlich sichtbar { [...] } public double getStand() // öffentlich sichtbar { [...] } public void setStand(double betrag) { [...] } // Weitere Methoden: public void abheben(double betrag) { [...] } public void einzahlen(double betrag) { [...] } public void ueberweisen(Konto zielkonto, double betrag) { [...] } private boolean pruefeUeberweisung(double betrag) { [...] } } Konto - nummer : int #stand : double <> +Konto() +Konto(stand : double) +Konto(nummer : int, stand : double)
Bild 4.3: UML-Notation der Klasse Konto
Das folgende Beispiel zeigt die Verwendung der Klasse Konto in einer beliebigen anderen Klasse mit main-Methode: Beispiel public static void main(String argv[]) { Konto einKonto = new Konto(); [...] einKonto.einzahlen(200); // erlaubt, weil einzahlen() // public ist
Java-Syntax
71
Nitty Gritty • Take that!
4
<> +getNummer() : int +getStand() : double +setStand(betrag : double) <<Weitere Methoden>> +abheben(betrag : double) +einzahlen(betrag : double) +ueberweisen(zielkonto : Konto, betrag : double) -pruefeUeberweisung(betrag : double) : boolean
Sandini Bib einKonto.stand = 3000;
// verboten, weil stand // privat ist!
}
Zugriffsrechte bei Klassen und Interfaces Die Zugriffsrechte können nicht nur bei Variablen und Methoden angewendet werden, sondern analog auch für Klassen und Interfaces. Um die Klassen auch verwenden zu können, sind jedoch nur public und Default-Protection sinnvoll und erlaubt. Zugriffsrechte und Sichtbarkeit sind zwei unterschiedliche Aspekte; jede Klasse auf die zugegriffen werden soll, muss sichtbar sein (z. B. durch Referenzierung des kompletten Namens oder import); umgekehrt trifft dies aber nicht zu. Das heißt, eine private deklarierte Methode ist in anderen Klassen des Packages zwar sichtbar, aber nicht zugreifbar.
Nitty Gritty • Take that!
4
4.5.2 static – Klassenvariable und -methoden Der Modifier static dient dazu, Methoden und Variablen der Klasse als Ganzes zuzuordnen anstatt den einzelnen Instanzen. Klassenvariablen Neben den bisherigen Instanzvariablen können damit so genannte Klassenvariablen erzeugt werden. Sie gehören nicht zu einem bestimmten Objekt, sondern zur gesamten Klasse. Für eine Klassenvariable wird genau ein einziges Mal Speicher bereitgehalten. Sie ist damit unabhängig von der Anzahl existierender Instanzen der Klasse. Sie kann zwar von jeder Instanz aus angesprochen werden, als ob es sich um eine Variable dieser Instanz handeln würde, eine Manipulation wirkt sich aber auf alle Instanzen aus. Klassenvariablen können damit zur Verwaltung klassenspezifischer Daten verwendet werden, wie zum Beispiel für einen Zähler der aktuell vorhandenen Instanzen dieser Klasse. Sie unterliegen den gleichen Regeln der Zugriffsrechte wie Instanzvariablen.
72
Modifier
Sandini Bib
Initialisierung Klassenvariablen haben einen globalen Gültigkeitsbereich. Sie sollten direkt bei der Deklaration initialisiert werden. Der Konstruktor kommt für die Initialisierung nicht in Frage, da er beliebig oft aufgerufen werden kann bzw. gar nicht, falls noch keine Instanz existiert. <Modifier> static variablenname = ;
Zugriff Der Zugriff auf die statischen Klassenvariablen erfolgt wie üblich mit Hilfe des ».«-Operators, jedoch mit dem Klassennamen statt einer Referenz. Alternativ ist es auch möglich, über eine aktuelle Instanz zuzugreifen, statt EineKlasse.klassenVariable kann man auch eineInstanz.klassenVariable schreiben. Dies könnte aber den Eindruck erwecken, es handle sich dabei um einen Zugriff auf einen objektspezifischen Wert. Deshalb sollten Sie die erste Variante bevorzugen. Sind von einer Klasse noch keine Instanzen angelegt worden, so ist der Zugriff natürlich ausschließlich über den Klassennamen möglich. Beispiel
4 Nitty Gritty • Take that!
Als Beispiel noch einmal die Klasse Konto mit einem Zähler der aktuell existierenden Objekte: public class Konto { private int nummer; public static int naechsteFreieKontonummer = 1; public Konto() // Konstruktor { // Zugewiesene Nummer gilt für dieses Konto-Objekt: nummer = naechsteFreieKontonummer; // Neuer Wert gilt für alle Konto-Objekte: naechsteFreieKontonummer++; } [...] }
Der Zugriff von außen erfolgt beispielsweise mit int anzahlKonten = Konto.naechsteFreieKontonummer - 1;
Java-Syntax
73
Sandini Bib
Klassenmethoden Analog gibt es auch Klassenmethoden. Insbesondere ist es natürlich sinnvoll, die Zugriffsmethoden auf (nun private) Klassenvariablen ebenfalls static zu definieren, da Sie möglicherweise schon auf diese Methoden zugreifen können wollen, wenn es noch keine Instanzen gibt: private static int naechsteFreieKontonummer = 1; public static int getNaechsteFreieKontonummer() { return naechsteFreieKontonummer; }
Der Aufruf erfolgt wiederum über den Klassennamen: int anzahlKonten = Konto.getNaechsteFreieKontonummer() - 1;
Klassenvariablen und -methoden werden in UML-Diagrammen unterstrichen dargestellt.
Nitty Gritty • Take that!
4
Konto - nummer : int - naechsteFreieKontonummer : int = 1 #stand : double <> +Konto() +Konto(stand : double) +Konto(nummer : int, stand : double) <> +getNummer() : int +getNaechsteFreieKontonummer(): int
Bild 4.4: UML-Darstellung von static-Membern
main-Methode Das wichtigste Beispiel für static-Methoden ist main(). Diese Methode wird bereits aufgerufen, bevor eine Anwendung irgendwelche Objekte instanziiert. Sie muss daher zwingend static definiert sein. In ihr kann dann eine Instanz der Klasse selbst angelegt werden. Daher die auf den ersten Blick merkwürdig erscheinende Konstruktion:
74
Modifier
Sandini Bib public class BeispielApplikation { public static void main(String args[]) { BeispielApplikation runner = new BeispielApplikation(); runner.starteBearbeitung(); // Aufruf bezieht sich // auf eine Instanz. } public void starteBearbeitung () { [...] } }
Da beim Aufruf einer statischen Methode wie main()möglicherweise noch keinerlei Instanzen der Klasse existieren, dürfen Sie in ihr keine nicht-statischen Methoden aufrufen oder auf Instanzvariablen zugreifen. Ansonsten meldet der Compiler den Fehler Can't make a static reference to nonstatic variable/method
Es ist ein typischer Anfängerfehler, das obige Beispiel fälschlicherweise so zu schreiben:
4.5.3 final – Verhinderung von Vererbung und Konstanten Der Modifier final drückt aus, dass die Implementierung eines CodeElements oder dessen Belegung zur Laufzeit nicht mehr verändert werden kann. Das bedeutet insbesondere, dass die entsprechende Klasse, Variable oder Methode bei einer Vererbung nicht überschrieben werden darf. Eine finale Methode ist damit für alle Subklassen festgelegt, eine finale Klasse darf nicht vererbt werden und eine finale Variable ist nach ihrer Initialisierung konstant.
Java-Syntax
75
Nitty Gritty • Take that!
4
public class BeispielApplikation { public static void main(String args[]) { starteBearbeitung(); // Aufruf einer nicht// statischen Methode verboten! } public void starteBearbeitung () { [...] } }
Sandini Bib
Beispiel
Nitty Gritty • Take that!
4
public final class System{...} // JDK-Systemklasse ist // nicht überschreibbar public class MySystem extends System{...} // verboten! public class Object // JDK-Systemklasse { /* Methode getClass ermittelt den aktuellen Typ, auch für abgeleitete Klassen. */ public final Class getClass(){[...]} /* Methode toString gibt eine Standardrepräsentation einer Instanz als String aus. */ public String toString(){[...]} [...] } public class Konto extends Object { public final Class getClass(){[...]} // Verboten! // Als Klasse muss "Konto" zurückgegeben werden! /* toString hingegen darf bzw. soll sogar überschrieben werden */ public String toString(){[...]} [...] }
Konstanten Besonders wichtig ist die Kombination von static und final bei Variablen. Eine konstante Klassenvariable wird direkt bei der Deklaration initialisiert und nie mehr verändert. Dies gibt dem Compiler gute Optimierungsmöglichkeiten. De facto ersetzt static final das in Java nicht vorhandene Schlüsselwort const. <Modifier> static final konstantenname = <Wert>;
Ein Beispiel ist die Zahl Pi, die unter Java mit Math.PI abrufbar und als public static final double PI = 3.14159265897
definiert ist. Konstanten dürfen auch in Interfaces deklariert werden.
76
Modifier
Sandini Bib
4.5.4 abstract – abstrakte Klassen Eine Klasse oder Methode, die abstract definiert ist, beschreibt deren Aussehen oder Programmierschnittstelle, implementiert sie aber gar nicht oder nicht vollständig. Abstrakte Klassen dienen damit als Basis für konkrete Klassen, die von ihnen abgeleitet werden sollen. Die Deklaration einer abstrakten Klasse beschreibt die Gemeinsamkeiten aller Subklassen und legt fest, was die Minimalanforderungen für die Subklassen sind. Von abstrakten Klassen können keine Instanzen erzeugt werden. Beispiel Ein Beispiel ist die Klasse java.lang.Number. Sie dient als Superklasse für die verschiedenen numerischen Klassen (z. B. Integer, Double). Sie stellt sicher, dass alle abgeleiteten Klassen Konvertierungsmethoden zu den elementaren Datentypen bereitstellen. Manche werden direkt implementiert (z. B. byteValue()); andere sind rein abstrakt definiert (z. B. doubleValue()), müssen also von jeder abgeleiteten Klasse auf jeden Fall überschrieben und implementiert werden.
Abstrakte Methoden beschreiben nur die Signatur, enthalten aber keine Implementierung. Die Deklaration einer abstrakten Methode wird mit einem Semikolon statt dem Implementierungsblock abgeschlossen. Enthält eine Klasse eine abstrakte Methode, muss die Klasse selbst abstract definiert werden. Beispiel public abstract class Number { public abstract float floatValue(); public abstract double doubleValue(); public abstract int intValue(); public byte byteValue() { // Hier käme die gemeinsame Implementierung für alle // Subklassen. Auch diese Methode könnte jedoch // überschrieben werden, da sie nicht final // deklariert wurde.
Java-Syntax
77
Nitty Gritty • Take that!
4
Abstrakte Methoden
Sandini Bib [...] } }
Interfaces Ein Alternativkonzept zu abstrakten Klassen stellen Interfaces dar (siehe Abschnitt 4.10). Sie sind rein abstrakte Schnittstellenbeschreibungen und enthalten keinerlei Code. Eine Mischung aus abstrakten und definierten Methoden in Interfaces ist nicht möglich. Dafür ist aber der Einsatz von Mehrfachvererbung erlaubt. Sie sollten sich genau überlegen, für welchen Zweck Sie Interfaces oder abstrakte Klassen einsetzen. In einem Online-Kapitel auf www.nitty-gritty.de werden die beiden Konzepte genauer erläutert und gegenübergestellt. 4.5.5
synchronized und volatile – Synchronisation von Threads
synchronized
Nitty Gritty • Take that!
4
In Java wird Multithreading direkt von der Sprache unterstützt (siehe Online-Kapitel). Das bedeutet, dass mehrere Vorgänge parallel ablaufen. Beim Einsatz mehrerer Threads (Mini-Prozesse) kann es natürlich vorkommen, dass verschiedene Threads dieselben Objekte manipulieren oder auf die gleiche Ressource zugreifen. Um Konflikte, undefinierte Zustände und unerwartete (»zufällige«) Resultate zu vermeiden, kann man eine Methode atomar ausführen lassen, indem man sie synchronisiert. Eine synchronisierte Methode (Modifier synchronized) wird komplett vom aktiven Thread ausgeführt, alle anderen Threads werden bis zur Beendigung der Methode angehalten. Beispiel public synchronized void kritischeMethode() { vonMehrerenThreadsBenutzteVariable += 5; } public synchronized void einzahlen(double betrag) { stand += betrag; }
78
Modifier
Sandini Bib
Man muss aber meist nicht alle Threads stoppen, bis die komplette Methode abgearbeitet ist, sondern braucht nur den Schutz von einer oder einigen wenigen Anweisungen. Deshalb kann man auch synchronisierte Blöcke verwenden, die dann nur die kritische Sektion umfassen (siehe Abschnitt 4.9.1). Mehr zu den Themen Multithreading und Synchronisation finden Sie im Online-Teil dieses Buches auf www.nitty-gritty.de. volatile Ein selten anzutreffender Modifier ist volatile. Damit kann man bei einem Feld, das heißt einer Instanz- oder Klassenvariable, angeben, dass dieses in synchronisierten Threads benutzt wird und daher von Compiler-Optimierungen auszuschließen ist. Dem Autor ist jedoch kein einziger Compiler bekannt, der fehlerhaften Code erzeugt, wenn man ein solches Feld nicht mit volatile kennzeichnet. native – Einbindung von Nicht-Java-Code
Der Modifier native dient der Einbindung von plattformspezifischem Binärcode. Native Methoden einer Klasse werden ähnlich wie bei abstrakten Methoden (Abschnitt 4.5.4) im Klassenrumpf nur deklariert, die Implementierung befindet sich aber in einer externen Bibliothek, zum Beispiel in einer DLL (Dynamic Link Library), die etwa in C implementiert sein könnte. Beispiel public class NativeTest { // Binärbibliothek laden static { System.loadLibrary("NativeTest"); } public static void main(String[] args) { NativeTest runner = new NativeTest(); // Aufruf der C-Methode float f = runner.nativeMethod(1, 2.0); } public native float nativeMethod(int i, double l); }
Java-Syntax
79
4 Nitty Gritty • Take that!
4.5.6
Sandini Bib
Anwendung Native Methoden eignen sich nicht für Applets, bei denen Sie vorab nicht wissen, wo sie später ablaufen werden, sie sind dort aus Sicherheitsgründen standardmäßig auch nicht zulässig. Aber bei einer Server-Applikation mit Performance-kritischen Routinen oder von Java nicht unterstützten Schnittstellen macht der Einsatz durchaus Sinn. 4.5.7
transient – nicht persistente Referenzen
Serialisierung
Nitty Gritty • Take that!
4
Die Umwandlung einer Objektdarstellung im Speicher in einen ByteStrom nennt sich Serialisierung. Diese benötigt man, wenn man Objekte in eine Datei speichern oder über ein Netzwerk transportieren möchte. Wenn man ein Objekt serialisiert, das ein anderes Objekt referenziert, dann muss dieses andere Objekt auch serialisiert werden, damit die Originalsituation im Speicher rekonstruiert werden kann. Wenn man das rekursiv weiter verfolgt, kann mit einem einzigen Serialisierungsaufruf ein ganzes Geflecht von sehr vielen Objekten erfasst werden. Dies ist auch ein Grund, warum Netzwerkprotokolle auf Objektebene, wie z. B. RMI, häufig so langsam werden. Die bei einer Serialisierung nicht benötigten Referenzvariablen sollte man daher mit dem Modifier transient kennzeichnen. Damit zeigt man für den Fall der Serialisierung an, dass das Feld kein Bestandteil des persistenten Zustands eines Objektes ist und somit nicht übertragen werden muss. 4.5.8 Zusammenfassung und Anmerkungen Reihenfolge Für die Anordnung der Modifier ist keine Reihenfolge vorgeschrieben. Im Allgemeinen verwendet man jedoch static abstract synchronized final native
wobei für private, protected oder public oder gar nichts (Default-Schutz) steht.
80
Modifier
Sandini Bib
Zusammenfassung Klasse
Variable
Methode
private
–
maximaler Zugriffsschutz
protected
–
Zugriffsschutz außerhalb des Packages
[default]
Zugriffsschutz außerhalb des Packages
public
kein Zugriffsschutz
static
–
Klassenvariable
Klassenmethode
final
keine Subklassen zulässig
Konstante auf Objektebene
kein Überschreiben zulässig
abstract
keine Instanziierung möglich
–
von konkreten Klassen zu implementieren
synchronized
–
–
kritische Methode
volatile
–
kritische Variable
–
native
–
–
aus Binärbibliothek lesen
transient
–
nicht persistent bei Serialisierung
–
4.6
Interfaces
Wie in Abschnitt 3.2.3 beschrieben, benutzt man Interfaces (Schnittstellen) zur abstrakten Deklaration von Methoden und als Ersatz für die Mehrfachvererbung. Ein Interface kann Methodendeklarationen und Konstanten enthalten. Jede konkrete Klasse, die ein Interface implementieren will, muss alle dort deklarierten Methoden definieren. Das Interface selbst enthält keinerlei Implementierung. Interfaces sind nicht von einem Basis-Interface (analog zu Object, Abschnitt 6.1.1) abgeleitet. Sie erlauben Mehrfachvererbung.
Java-Syntax
81
4 Nitty Gritty • Take that!
Modifier
Sandini Bib
Definition Ein Interface wird ähnlich einer Klasse definiert: [public] interface NameDesInterfaces [extends SuperInterface [, WeiteresSuperInterface]*]] { <Methodendeklarationen und Konstanten> }
Eine Klasse, die das Interface implementiert, wird mit dem Schlüsselwort implements gekennzeichnet: [public] class EineKlasse implements NameDesInterfaces [, WeiteresInterface]*]] {[...]}
Ähnlich der Ableitung einer konkreten Klassen von einer abstrakten müssen auch hier durch die implementierende Klasse alle abstrakten Methoden des Interfaces implementiert werden. Falls dies nicht der Fall ist, erhält man die Compiler-Meldung: class EineKlasse must be declared abstract. It does not define void methode() from interface NameDesInterfaces.
Nitty Gritty • Take that!
4
Beispiel Für die Verzinsung verschiedener Klassen wie zum Beispiel für Sparkonten oder festverzinsliche Wertpapiere soll es eine gemeinsame Schnittstelle Verzinsbar geben. <> Verzinsbar
Konto
+zinsenBerechnen()
<> Sparkonto
-zinssatz: double +zinsenBerechnen()
Bild 4.5: UML-Darstellung von Interfaces
82
Interfaces
Sandini Bib public interface Verzinsbar { public double zinsenBerechnen(); } public class Sparkonto extends Konto implements Verzinsbar { private double zinssatz; public double zinsenBerechnen() { [...] } }
Konstanten Außer Methoden können auch Klassenvariablen (Abschnitt 4.5.3) in einem Interface definiert werden: interface NameDesInterfaces { public static final int MINVALUE = 0; }
4 Nitty Gritty • Take that!
Der Zugriff erfolgt wie gewohnt über NameDesInterfaces.MINVALUE. Beispiel public interface Verzinsbar { public final static int festverzinslich = 0; public final static int variabel = 1; public final static int mitAusbezahlung = 2; public final static int mitZinseszins = 4; public double zinsenBerechnen() {[...]} } int zinsart = Verzinsbar.variabel + Verzinsbar.mitZinseszins;
Ein Interface allein mit Konstanten und ohne Methode kann ähnlich dem in Java nicht vorhandenen enum-Konstrukt von C++ verwendet werden.
Java-Syntax
83
Sandini Bib
4.7
Erzeugung und Nutzung von Objekten
In Abschnitt 4.5.3 wurde der statische Aufbau von Klassen beschrieben, nun geht es um die dynamische Komponente, die Instanziierung von Objekten und den Aufruf von Methoden. Aus einer Klasse werden zur Laufzeit mit Hilfe des Schlüsselwortes new Objekte, also konkrete Ausprägungen erzeugt. Diese Objekte werden typisierten Referenzen zugewiesen. Konto -nummer : int #stand : double +abheben(betrag : double) +einzahlen(betrag: double) +ueberweisen(zielkonto : Konto, betrag : double)
<> k1:Konto
4 Nitty Gritty • Take that!
nummer = 0436572800 = 2.564,84 stand
k2:Konto nummer = 0436577800 stand = 18.663,97
Bild 4.6: Zwei Instanzen der Klasse Konto
Abbildung 4.6 zeigt zwei verschiedene Konto-Objekte in der UMLDarstellung. Die gestrichelte Linie zeigt die Instanziierung an. Jede Instanz wird mit einem Doppelpunkt und durch Unterstreichung gekennzeichnet. Für die einzelnen Instanzvariablen wird der Wert angeben. 4.7.1
Objektlebenszyklus
Objekte werden im Speicher abgelegt, die automatische Speicherverwaltung von Java kümmert sich darum, dass genügend Speicherplatz angefordert wird und dieser bei Löschung des Objektes wieder freigegeben wird.
84
Erzeugung und Nutzung von Objekten
Sandini Bib Zustandsdiagramm: new
Speicher allokiert
1
Konstruktoraufruf
Initialisiert
2 3
gelöscht
Garbage Collection
5
nicht referenziert
Referenz wird null
4
Zuweisung
referenziert
Seuqenzdiagramm:
1 2
<>
k1:Konto
3
Konto()
limitPruefen(500) Nutzung des Obj. true Referenz erlischt 4
5
Bild 4.7: Objektlebenszyklus im UML-Diagramm
In Java sieht dies dank der Unterstützung der Laufzeitumgebung sehr einfach aus: public static void main(String args[]) { Konto k1 = new Konto(); // (1)-(3) k1.limitPruefen(500); } // (4)
Das Konto vor der Zuweisung ist der Typ der Referenz mit dem Namen k1, diese Referenz kann also nur auf Instanzen der Klasse Konto verweisen. Diesem wird ein neues Objekt der Klasse Konto zugewiesen (Konstruktoraufruf ). Am Blockende wird die Referenz ungültig. Der Garbage Collector der Laufzeitumgebung kann danach zu einem beliebigen Zeitpunkt den Speicher wieder freigeben (siehe Abschnitt 13.2). Will man die Referenz vorab freigeben, kann man dies folgendermaßen tun: k1 = null;
Java-Syntax
85
Nitty Gritty • Take that!
4
Sandini Bib
4.7.2
Referenzen Referenzen sind Verweise, die auf Objekte eines bestimmten Typs zeigen. Dieser Typ kann eine Klasse oder ein Interface sein. Referenzen treten als lokale, Instanz- oder Klassenvariablen in Erscheinung. Sie können während ihrer Gültigkeit auf verschiedene Objekte zeigen, Sie können ihnen also unterschiedliche Objekte zuweisen. Falls es sich bei den Referenzen um Instanz- oder Klassenvariablen handelt, gilt hierfür die Voraussetzung, dass sie nicht final deklariert sind (siehe Abschnitt 4.5.3); lokale Variablen hingegen sind nie final. Referenzen sind also eine Art intelligente Zeiger. Sie zeigen immer auf das korrekte Objekt, auch wenn dieses von der Laufzeitumgebung an eine andere Stelle im Speicher verschoben wurde. Sie stellen damit keine Speicheradressen dar und man kann auch keine Zeigerarithmetik betreiben. Konto eineLeereReferenz = null; Konto einKonto = new Konto();
Nitty Gritty • Take that!
4
In der ersten Zeile wird eine leere Referenz erzeugt. Dies erreicht man mit dem Schlüsselwort null. Dieses stimmt nicht wie unter C mit dem Integerwert 0 überein, sondern ist eine typisierte Null-Referenz. Die zweite Anweisung erzeugt ein neues Objekt der Klasse Konto und initialisiert damit die Referenz einKonto. Vor dem Bezeichner (Namen) einer Referenz gibt man deren Typ an, das bedeutet, der Referenz einKonto kann man ausschließlich Objekte vom Typ Konto zuweisen. Beispiel Konto k1 = null; // Null-Referenz auf ein Konto k1 = new Konto(2000.00); // k1 zeigt nun auf ein Objekt. Konto k2 = new Konto(); // Referenz k2 auf ein neues // Objekt k1 = k2; // k1 zeigt jetzt auf dasselbe Objekt // wie k1; das ursprüngliche Objekt // von k1 ist nicht mehr referenziert // und kann durch den Garbage Collector // entsorgt werden.
86
Erzeugung und Nutzung von Objekten
Sandini Bib
Referenzen vom Typ von Superklassen oder Interfaces Bislang haben wir Instanzen einer bestimmten Klasse an Referenzen vom selben Typ zugewiesen. Daneben ist es aber auch möglich, einer Referenz ein Objekt eines seiner Subklassen zuzuweisen: Konto einKonto = new Girokonto();
Ein Girokonto ist ja ein spezielles Konto, daher ist die Zuweisung gültig (umgekehrt wäre es aber nicht erlaubt, ein Konto-Objekt einer Girokonto-Referenz zuzuweisen!). Sie können dann aber nur diejenigen Aspekte des Objekts sehen, die in der Klasse Konto sichtbar sind; das Objekt wird aus der Sichtweise des Referenztyps gesehen. Genauso ist es bei Interfaces: Verzinsbar verzinsbaresObjekt = new Sparkonto();
Bei dieser Referenz ist nur die Methode zinsBerechnen() sichtbar, alle anderen Methoden der Klassen Sparkonto und Konto sind nicht sichtbar.
Referenzen als Parameter Bei Methodenaufrufen werden die Objekte by-reference übergeben, also wird in der Methode das gleiche Objekt nur unter einem anderen Namen verwendet. Es wird keine Kopie angelegt, sondern das Original manipuliert! Beispiel Konto quelle = new Konto(1,500.00); Konto ziel = new Konto(2,20.00); quelle.ueberweise(ziel, 100.00); // Aufruf
Wenn nun in der Methode ueberweise() das Konto ziel benutzt wird, dann wird damit (wie erwartet) das Originalobjekt manipuliert:
Java-Syntax
87
4 Nitty Gritty • Take that!
Dieses Verhalten ist nützlich, wenn man eine Menge von Objekten verwaltet, zum Beispiel bei Collection-Klassen. Bei Mengenoperationen (zum Beispiel beim Sortieren) spielt der konkrete Typ oft keine Rolle, sondern nur ein Aspekt, den alle Objekte der Menge gemeinsam haben.
Sandini Bib void ueberweisen(Konto zielkonto, double betrag) { abheben(betrag); zielkonto.einzahlen(betrag); }
Wenn jedoch keine Objektreferenzen benutzt werden, sondern elementare Datentypen (wie int, double usw., siehe Abschnitt 4.8.2 und 4.8.7), dann werden die Parameter kopiert, also per call-by-value übergeben! Die Referenzen this und super Die this-Referenz ist eine spezielle Referenz, die für die Methoden einer Instanz zugänglich ist. this verweist auf die aktuelle Instanz der Klasse und dient damit der Vermeidung von Verschattungen mit lokalen Variablen. Beispiel
Nitty Gritty • Take that!
4
public class Konto { protected double stand; // Instanzvariable stand [...] public Konto(double stand) // lokaler Parameter stand { nummer = getNaechteFreieKontonummer(); this.stand = stand; // Auslösung der Verschattung } }
super ist eine Referenz auf die aktuelle Instanz als Superklasse. Sie können damit wie bei jeder normalen Referenz mit dem ».«-Operator auf die Member der Superklasse zugreifen. Beispiel public class Konto { [...] public void ueberweisen(Konto ziel, double betrag) {[...]} }
88
Erzeugung und Nutzung von Objekten
Sandini Bib public class Sparkonto extends Konto { [...] // Überschreiben einer Methode: public void ueberweisen(Konto ziel, double betrag) { // Überweisung bei Sparbuch nur erlaubt, wenn // Ziel ein anderes Konto desselben Kunden ist if (inhaber == ziel.inhaber) super.ueberweisen(ziel, betrag); // Aufruf der verschatteten Methode // der Superklasse } }
Referenzen mit Zuständen
4.7.3 Zugriff auf Instanzen Der Zugriff auf Instanzen von Klassen erfolgt mittels Referenzen und dem ».«-Operator für deren Member. Konto quelle = new Konto(); // Konto ziel = new Konto(); // quelle.einzahlen(200.00); // quelle.ueberweisen(ziel, 100);// int nr = ziel.getNummer(); // quelle.stand = 1000.0; // //
Zugriff auf Zugriff auf Zugriff auf Zugriff auf Zugriff auf Zugriff auf variable
ein Objekt ein Objekt eine Methode eine Methode eine Methode eine Instanz-
Der letzte Aufruf ist wegen der Zugriffsrechte nur innerhalb einer Methode der Klasse Konto oder einer ihrer Subklassen erlaubt.
Java-Syntax
89
4 Nitty Gritty • Take that!
Seit Java 2 gibt eine kleine Bibliothek (java.lang.ref), die Sie nutzen können, wenn Sie genauer über den aktuellen Status eines nicht mehr referenzierten Objekts Bescheid wissen müssen. So genannte Soft-, Weak- und Phantomreferences geben Auskunft, inwieweit diese Objekte tatsächlich schon aus dem Speicher entfernt wurden. Dies kann nützlich sein, wenn Sie einen Caching-Mechanismus schreiben möchten.
Sandini Bib
Ein Methodenaufruf bezieht sich auf eine spezielle Instanz (Ausnahme: Klassenmethoden, Abschnitt 4.5.2). Man kann sich Methodenaufrufe als Nachrichten vorstellen, die an ein wartendes Objekt geschickt werden. Dieses wird dadurch aktiv, führt den Methodencode aus, liefert möglicherweise einen Rückgabewert und kehrt dann in den Ruhezustand zurück (siehe Abbildung 4.8).
<>
quelle:Konto
<>
ziel:Konto
einzahlen(200.0)
ueberweisen(ziel,100) abheben(100) einzahlen(200.0)
getNummer() 436577800
Nitty Gritty • Take that!
4
stand = 100
Bild 4.8: Methodenaufrufe in UML
Aktuelles Objekt Betrachtet man nun die Implementierung der Methode getNummer() public int getNummer() { return nummer; }
so fällt auf, dass innerhalb der Methode auf die Komponente nummer der Klasse nicht mit dem ».«-Operator zugegriffen wird. Auch beim Aufruf einer anderen Methode (z. B. abheben() innerhalb von ueberweisen()) gilt dies analog. Dies liegt daran, dass eine solche Methode einer Klasse immer für ein konkretes Objekt aufgerufen wird und somit klar ist, um wessen Instanzvariablen es sich handelt.
90
Erzeugung und Nutzung von Objekten
Sandini Bib
Wenn eine Verschattung einer Variablen durch eine gleichnamige auftritt, können Sie diese mit Hilfe der speziellen Referenzen this und super auflösen (siehe Abschnitt 4.7.2)
4.8 Datentypen 4.8.1
Arten Datentypen
Referenztypen
- byte - short - int - long - float - double - boolean - char
NullReferenz
Klassen/ Interfaces
Arrays (Felder)
null
typisierte Objektreferenzen
Menge von Objekten
4
Bild 4.9: Arten von Java-Datentypen
Elementare Datentypen Es gibt eine Reihe von elementaren oder primitiven Datentypen, auf denen die Klassen aufsetzen und die einen gewissen Sonderstatus genießen. Zu diesen gehören: T
Integertypen mit verschiedenem Wertebereich
T
Fließkommatypen verschiedener Genauigkeit der Typ boolean der Typ char
T T
Im Gegensatz zu anderen Sprachen ist der Wertebereich jedes Datentyps in Java genau festgelegt. Referenztypen Daneben gibt es noch einige Referenztypen, die definieren, auf welchen Objekttyp eine Variable verweisen kann, wenn das Objekt keinen elementaren Typ darstellt. Java-Syntax
91
Nitty Gritty • Take that!
elementare Datentypen
Sandini Bib
Hierzu gehören zunächst Referenzen auf Klassen und Interfaces. Diese haben ich im Abschnitt 4.7.2 bereits vorgestellt. Eine Sonderstellung nehmen die Strings ein, für die eigene Operatoren definiert sind. Die Nullreferenz besteht aus dem festen Wert null, der jedem Referenztyp zugewiesen werden kann und für den undefinierten Zustand steht. Arrays Arrays, also Felder mit mehreren Objekten, stellen ebenfalls Referenztypen dar, die jedoch durch das Laufzeitsystem überwacht werden und einige Zusatzfunktionalität anbieten. Nun zu den Datentypen im Einzelnen: 4.8.2
Elementare Datentypen
Die elementaren Datentypen nehmen eine Sonderrolle bei der Definition von Variablen dieses Typs ein. Anders als bei Klassen ist eine Speicherallokation mit new bei Objekten elementaren Datentyps nicht notwendig.
Nitty Gritty • Take that!
4
Beispiel int int byte double
i; j = 2; b = 1; d = 0.3;
aber: Konto einKonto = new Konto(); // Konto ist kein // elementarer Datentyp!
Elementare Datentypen werden bei der Übergabe als Parameter kopiert (Call-by-value, siehe Abschnitt 4.8.7). elementare Datentypen
Integertypen - byte - short - int - long
Fließkommatypen
boolesche Werte
- float - double
- boolean
Zeichen - char
Bild 4.10: Gliederung der elementaren Datentypen 92
Datentypen
Sandini Bib
Integertypen In Java gibt es vier fix definierte Arten von ganzen Zahlen. Alle Integertypen sind vorzeichenbehaftet. Die Spanne reicht vom 8 Bit langen Typ byte über short (16 Bit) und int (32 Bit) bis zum 64 Bit Typ long. Alle Integer-Variablen werden standardmäßig mit 0 vorbelegt, sofern keine explizite Initialisierung vorgenommen wird. Typ
Länge
Wertebereich
Initialisierung
byte
8 Bit
-128 ... 127
0
short
16 Bit
-32768 ... 32767
0
int
32 Bit
-2147483648 ... 2147483647
0
long
64 Bit
-9223372036854775808 ... 9223372036854775807
0L
Neben der dezimalen Darstellung von Werten (Literalen) gibt es auch die Möglichkeit oktale (Präfix 0) oder hexadezimale Literale (Präfix 0x) anzugeben.
Typkonvertierung Bei Berechnungen mit Operanden verschiedener Genauigkeit wird automatisch in den größeren Typ konvertiert. Umwandlungen von Zahlen eines größeren Typs in einen kleineren können einen Informationsverlust bedeuten, da die oberen Bits einfach abgeschnitten werden. Der Compiler meldet: Type mismatch: cannot convert from to .
Deshalb muss eine solche Konvertierung explizit durch den Programmierer vorgenommen werden. Dies nennt man Casting. Dabei wird der Zieltyp in Klammern vor einem Ausdruck angegeben. Beispiel long l = 134L; short s = l;
// verboten, verursacht // Compilerfehler: type mismatch
Java-Syntax
93
Nitty Gritty • Take that!
4
int i = 10; // dezimal i = 012; // oktal, entspricht 10 dezimal i = 0xA; // hexadezimal, entspricht 10 dezimal
Sandini Bib short s = (short) l; int i = (int) ( l + s ); // Genaue Berechnung erfolgt // automatisch in long, // danach Casting nach int.
Fließkommatypen Für Fließ- oder Gleitkommazahlen gibt es zwei Datentypen: float mit 32 Bit Genauigkeit und double mit 64 Bit. Die Darstellung entspricht dem IEEE-Standard 754. Für Literale vom Typ float sollte das Postfix F angehängt werden, für double ein D. Beispiel float f = -0.3F; double d = 2.4E-3D;
Ist weder D noch F angehängt, wird der Wert als double interpretiert. Für die Typkonvertierung gilt das Analoge zu den Integertypen.
Nitty Gritty • Take that!
4
Typ
Länge
Wertebereich
Initialisierung
float
32 Bit
-3.40282347E+38 ... 3.40282347E+38
0.0F
double
64 Bit
-1.79769313486231570E+308 ... 1.79769313486231570E+308
0.0D
Boolesche Werte Boolesche Daten, also logische Werte für »falsch« und »wahr«, werden durch den Datentyp boolean dargestellt. Die Literale sind false und true, wobei die automatische Initialisierung mit false erfolgt. Die beiden Werte entsprechen nicht (wie in C/ C++ üblich) den Integerwerten 0 und Nicht-0! Um Missbrauch zu verhindern wurde ein Casting explizit verboten. Beispiel boolean b = false; int i = 1; b = i; b = (boolean) i;
94
Datentypen
// verboten! // ebenfalls verboten!
Sandini Bib b = (i != 0); i = b?1:0;
// // // //
Vergleich ist erlaubt, da das Ergebnis boolesch ist! Umwandlung in int per bedingter Zuweisung (siehe Abschnitt 4.9.4)
Zeichen In Java werden Zeichen nicht in 8-Bit-ASCII, sondern in 16-Bit-Unicode gespeichert. Dafür gibt es den Datentyp char. Literale können in drei Varianten zwischen einfachen Anführungszeichen dargestellt werden: T
ASCII-Zeichen: ’a’, ’A’,... Unicode-Nummern: ’\u1234’
T
Escape-Character: ’\n’ (new line), ’\\’ (Backslash) ... Escape-Character
Bedeutung
\b
Backspace
\f
Vorschub (formfeed)
\n
Zeilenumbruch (newline)
4
\r
Wagenrücklauf (carriage return)
\t
Tabulator
\\
Backslash
\’
einfaches Anführungszeichen
\”
doppeltes Anführungszeichen
Nitty Gritty • Take that!
T
Beispiel char c1 = ‘F’; char newLine = ‘\n’;
Character sind (im Gegensatz zu den Integerwerten) nicht vorzeichenbehaftet, die Vorbelegung ist ’\u0000’. 4.8.3
Operatoren
Es gibt die in den meisten Programmiersprachen üblichen arithmetischen und logischen Operationen mit den üblichen Vorrangregeln.
Java-Syntax
95
Sandini Bib
Operatoren
mathematisch
logisch
- Addition: + - Negation: ! && - Subtraktion: - - und: || - Multiplikation: * - oder: - Division: / - Rest/Modulo: % - Vorzeichen: +,- Inkrement: ++ - Dekrement: --
bitweise
Vergleiche
- Negation: ~ - und: & - oder: | - xor: ^ - Shift: >>,<<, >>>
- Gleichheit: == - Ungleichh.: != - Größen: <,>, <=,>= - Referenzen: instanceof
Zuweisung - Zweisg.: =, +=, -=, *=, /=, ^=, %=, &=, |=, <<=, >>=, >>>=
Bild 4.11: Operatoren unter Java
Mathematische Operatoren Es gelten die in fast allen Programmiersprachen üblichen Zeichen und Regeln (»Punkt vor Strich« etc.). Daneben ist eine Klammerung mit runden Klammern möglich. Für die Restberechnung bei Ganzzahldivision (Modulo) gibt es den Operator »%«. Von C/C++ wurden die »++«- und »--«-Operatoren für Inkrement und Dekrement übernommen.
Nitty Gritty • Take that!
4
Beispiel i = 0; j = i++; j = ++i;
// j = 0, i = 1 // j = 2, i = 2
Bei »++« als Präfix wird die Variable vor der Zuweisung hochgezählt, als Postfix (erste Zeile) erst danach. Logische Operatoren Für boolesche Werte gibt es die Negation »!«, und- »&&« und oderOperation »||«: Operation !a
a = true
a = false
!a
false
true
Operation (a && b)
a = true
a = false
b = true
true
false
b = false
false
false
96
Datentypen
Sandini Bib
Operation (a || b)
a = true
a = false
b = true
false
true
b = false
true
true
Boolesche Ausdrücke werden in Java von links nach rechts ausgewertet, und zwar so, dass der zweite Wert nicht mehr herangezogen wird, wenn der erste das Ergebnis bereits eindeutig bestimmt. Damit sind Ausdrücke der Art if ( (file != null) && (file.exists() )
gültig und werden häufig eingesetzt. Falls der zweite Ausdruck ausgewertet würde, obwohl die Referenz null ist, erhielte man einen Fehler. Bitweise Operatoren
Operation (a | b)
a = 00
a = 01
a = 10
a = 11
b = 00
00
01
10
11
b = 01
01
01
11
11
b = 10
10
11
10
11
b = 11
11
11
11
11
Der Operator »^« bezeichnet die bitweise Operation XOR. Um zu potenzieren, benutzen Sie die Methode Math.pow(basis, exponent). Siehe Abschnitt 6.1.5! Die Shift-Operatoren verschieben die Bits nach links »<<« oder rechts »>>«. Operation (a << 1)
a = 00
a = 01
a = 10
a = 11
a << 1
00
10
00
10
Java-Syntax
97
4 Nitty Gritty • Take that!
Hier gilt ein ähnliches Verhalten wie bei den logischen Operationen, nur dass die bitweisen Operatoren immer komplett ausgeführt und in der Regel auf Bitfelder angewendet werden. Die Länge bestimmt sich aus dem Typ der Operators, z. B. boolean, short, int ...
Sandini Bib
Der Operator »>>« führt einen bitweisen Shift nach rechts durch und füllt die »neuen« Bits links mit dem Vorzeichenbit auf. Falls jedoch mit Nullen aufgefüllt werden soll, kann man in Java den Operator »>>>« verwenden. Beispiel Variablenbelegung
Operator aufruf
Ergebnis dezimal
Ergebnis binär
i=4
I
4
000000000000000000000000 00000100
i>>1
2
000000000000000000000000 00000010
i>>>1
2
000000000000000000000000 00000010
i<<1
8
000000000000000000000000 00001000
I
-4
11111111111111111111111111111100
i>>1
-2
11111111111111111111111111111110
i>>>1
2147483646
01111111111111111111111111111110
i<<1
-8
11111111111111111111111111111000
i = -4
Nitty Gritty • Take that!
4
In der Programmiersprache C ist dies kein Problem, da es unsigned-Variablen gibt. Vergleiche Die Gleichheit wird mit einem doppelten Gleichzeichen dargestellt »==«, die Ungleichheit mit »!=«. Daneben gibt es noch die üblichen Größenvergleiche: »<, >, <=, >=«. Referenzen kann man mittels instanceof auf einen bestimmten Typ prüfen: Beispiel Konto einKonto = new GiroKonto(); einKonto instanceof Girokonto liefert true, einKonto instanceof Konto liefert true, einKonto instanceof Sparkonto liefert false.
98
Datentypen
Sandini Bib
Zuweisungen Eine Zuweisung erfolgt mit einem einfachen Gleichzeichen »=«. Zusätzlich gibt es noch einige abkürzende Zuweisungen, falls der veränderte Wert direkt wieder derselben Variablen zugewiesen werden soll: Beispiel int a = 5; a += 2; a -= 2; a *= 2; a /= 2; a %= 2; a ^= 2; a &= 2; a |= 2; a <<= 2; a >>= 2; a >>>= 2;
// // // // // // // // // // // //
Zuweisung Kurzschreibweise Kurzschreibweise Kurzschreibweise Kurzschreibweise Kurzschreibweise Kurzschreibweise Kurzschreibweise Kurzschreibweise Kurzschreibweise Kurzschreibweise Kurzschreibweise
für für für für für für für für für für für
a a a a a a a a a a a
= = = = = = = = = = =
a+2; a-2; a*2; a/2; a%2; a^2; (XOR) a&2; (AND) a|2; (OR) a<<2; a>>2; a>>>2;
4
Folgende Tabelle stellt die Operatoren mit abnehmendem Vorrang (Präzedenz) dar. Das heißt zum Beispiel a + b / ++c hat die Wirkung a + ( b / (++c) ), da Inkrement stärker bindet als die Division, aber diese wiederum stärker als die Addition. Operatoren in derselben Tabellenzeile haben gleichen Rang und werden von links nach rechts ausgewertet. Operator
Bedeutung
Beispiel
()
Klammern
(a+b)/c
[] .
Feldindizierung
a[i]
Objektreferenz
a.setValue()
++, --
Inkrement, Dekrement
a++, --b
+, -
Vorzeichen
-a
!
logische Negation
!(a & b)
~
bitweise Negation
~a
Java-Syntax
99
Nitty Gritty • Take that!
Operatoren und Vorrang
Sandini Bib
4
Operator
Bedeutung
Beispiel
*, /, %
Multiplikation, Division, Modulo
a*b/c
+, -
Addition, Subtraktion
a+b
<<, >>, >>>
Shift
a>>1
<, <=, >, >=
Vergleiche (größer und kleiner)
a <= b
instanceof
Typvergleiche
if (meinDatum instanceof Datum) ...
==, !=
Gleichheit, Ungleichheit
if (a == b)
&
bitweises und / AND
a&b
^
bitweises XOR
a^b
|
bitweises oder / OR
a|b
&&
logisches und / AND
if (a && b)...
||
logisches oder / OR
if (a || b)...
=, +=, -=, *=,
Zuweisungen
a=b
/=, %=, ^=, &=, |=,
a /= 2, kurz für a = a/2
Nitty Gritty • Take that!
<<=, >>=, >>>=
4.8.4
Referenztypen Referenzen verweisen auf Objekte und sind typisiert. Man kann einer Referenz per Zuweisung jederzeit ein Objekt eines kompatiblen Typs zuordnen. Referenzen auf Klassen und Interfaces werden in Abschnitt 4.7.2 behandelt. Der Typ einer Referenz muss nicht mit der Klasse des Objektes übereinstimmen. Da ein Objekt einer Klasse immer auch ein Objekt seiner Superklasse ist, kann die Referenz vom Typ einer Superklasse sein. Ähnliches gilt für Interfaces, die von der Klasse eines Objektes implementiert werden.
100
Datentypen
Sandini Bib
Beispiel Konto einKonto = new Sparkonto();
// // Verzinsbar verzObj = new Sparkonto(); // // Verzinsbar verzObj1 = (Sparkonto)einKonto; //
Referenztyp ist Superklasse Referenztyp ist impl. Klasse Nur mit Casting!
Die Referenz gibt die Schnittstelle an, die der Nutzer aufrufen kann. Die letzte Zeile im Beispiel zeigt dies. Eine direkte Zuweisung des Objektes ist nicht möglich, da die Referenz einKonto vom Typ Konto ist. Die Klasse Konto selbst erfüllt jedoch das Interface Verzinsbar nicht, sondern nur der »wirkliche« Typ des Objekts (Sparkonto). Daher ist eine Typumwandlung (Casting) notwendig (siehe auch Abschnitt 4.8.2). Bei der Übergabe von Referenztypen als Parameter wird nur die Referenz, nicht jedoch das referenzierte Objekt kopiert (Call-by-reference, siehe Abschnitt 4.8.7). Damit werden alle Manipulationen am übergebenen Objekt in der Methode am Original durchgeführt.
4
Unabhängig von ihrem Typ kann jede Referenz mit null belegt sein. Diese Konstante zeigt an, dass die Referenz im Augenblick auf kein Objekt verweist. Instanz- und Klassenvariablen, die nicht explizit initialisiert wurden, werden ebenfalls auf null gesetzt. Sie können Referenzen explizit auf null setzen, um der Laufzeitumgebung anzuzeigen, dass Sie das Objekt hinter dieser Referenz nicht mehr benötigen. Falls dann keine andere Referenz mehr auf das Objekt zeigt, wird es automatisch entsorgt. Beispiel Konto k1 = null; // Nullreferenz k1 = new Konto(); // k1 zeigt auf ein Objekt. k1 = new Girokonto(); // k1 zeigt auf ein anderes Objekt, // das erste kann entsorgt werden. k1 = null; // Nullreferenz, auch das zweite // Objekt kann entsorgt werden.
Java-Syntax
101
Nitty Gritty • Take that!
Nullreferenz
Sandini Bib
Neben den normalen Referenztypen gibt es noch zwei Typen, die zusätzliche Funktionalität anbieten: T
Strings
T
Arrays (Felder)
4.8.5 Strings Alle Strings (Zeichenketten) werden in Java als Objekte von der Klasse java.lang.String abgeleitet (siehe 6.1.4). Sie haben allerdings einige Möglichkeiten, die es bei anderen Klassen nicht gibt, zum Beispiel das Aneinanderhängen von Strings oder eine vereinfachte Erzeugung. Alle Klassen, die mit java.lang beginnen, werden automatisch importiert. Deshalb kann man auch kurz String statt java.lang.String schreiben. Mehr dazu in Abschnitt 6.1. Vergleich mit C++
Nitty Gritty • Take that!
4
In C und C++ werden Strings durch null-terminierte Arrays von 8-BitCharacters abgebildet. Der String "hallo" wird also in den 6 aufeinander folgenden Bytes ’h’,’a’,’l’,’l’,’o’,’\0’ abgelegt. Ganz anders in Java: Hier sind Strings echte Objekte und nicht null-terminiert. Java verwendet 16-Bit-Unicode-Zeichen. String-Literale Literale, also feste Werte vom Typ String, schreibt man in doppelten Anführungszeichen: "Das ist ein String." Sie können diese Literale direkt einer Referenz zuweisen: String ref = "Das ist ein String";
statt String ref = new String("Das ist ein String");
Durch die Angabe eines String-Literals wird automatisch ein (konstantes) Objekt erzeugt. Man kann deshalb dieses Objekt direkt der Referenz zuweisen ohne explizit mit new eine Instanziierung vorzunehmen.
102
Datentypen
Sandini Bib
Operator »+« Strings erfahren eine weitere Sonderbehandlung, weil sie so häufig auftreten. Da in Java (anders als in C++) keine Überladung von Operatoren möglich ist, könnte man Strings eigentlich nur mit der concatMethode verketten. Es wurde aber ein besonderer Operator, nämlich »+« zur Stringverkettung eingeführt. Man kann also schreiben: Beispiel String ein = "ein"; String s = new String("Dies ist " + ein + " String."); System.out.println(s); System.out.println("Noch " + ein + " String.");
Vergleich von Strings
Im Normalfall interessiert aber nur die Gleichheit der Inhalte und nicht der Objekte, deshalb ist die Verwendung von equals() immer vorzuziehen. Beispiel String ein = "ein"; if (ein.equals("ein")) ... // if (ein == "ein") ... // // //
4.8.6
OK, Ergebnis ist true. Riskant, Ergebnis hängt von der Optimierung des Compilers ab.
Arrays
Die dritte Art von Referenztypen (Abschnitt 4.8.7) sind Arrays. Ein Array ist ein Feld mit einer bestimmten Anzahl von Elementen, auf die mittels eines Index zugegriffen wird. Das gesamte Feld stellt wiederum ein Objekt dar (und ist nicht nur eine Aneinanderreihung von Objekten im Speicher).
Java-Syntax
103
4 Nitty Gritty • Take that!
Leider wurde »==« nicht ebenso umdefiniert. Die Verwendung von »==« ist ein Wagnis. Java legt nämlich die String-Literale (und statische Konkatenationen) in einem String-Pool ab. Damit kann ein Vergleich von Referenzen durchaus true ergeben, obwohl die Strings an unterschiedlichen Stellen im Programm definiert wurden.
Sandini Bib
Der Index eines Arrays mit n Elementen beginnt mit 0 und endet bei n-1. Er wird in eckigen Klammern angegeben: feld[index]. Leider ist der deutsche Begriff Feld in Java mehrdeutig: als Array und als Membervariable einer Klasse (field). Ich versuche deshalb in diesem Buch möglichst die alternativen Begriffe zu benutzen. Inhalte Anders als in C ist ein Array kein fester Speicherblock, der bei der Definition des Arrays allokiert wird, sondern ein Feld von Objektreferenzen oder elementaren Datentypen. Der Typ des Arrays gibt den Typ der Objekte an, die im Array gespeichert werden können. Aber auch das Array selbst ist ein Objekt und hat einen Typ: »[]«. Beispiel
Nitty Gritty • Take that!
4
String[] namen = new String[10]; namen[0] = "Kirsten"; String sarah = new String("Sarah"); namen[1] = sarah; namen[9] = namen[1]; /// namen[2] bis namen[8] sind gleich null namen[10] = "falsch!"; // Fehler, da 0 <= Index <= 9 namen: String[] 0 1 2 3 4 5 6 7 8 9
Kirsten Sarah
sarah: String
falsch!
Bild 4.12: Ein Array mit String-Objekten
Der Aufruf new String[10] legt keine zehn Stringobjekte an, sondern nur ein Arrayobjekt mit zehn Plätzen (Nullreferenzen)!
104
Datentypen
Sandini Bib
Initialisierung Das Referenzenfeld wird automatisch mit null-Referenzen initialisiert, ein Feld aus elementaren Datentypen mit den entsprechenden Nullwerten. Man kann ein Array aber auch direkt bei der Definition mit Werten vorbelegen: String[] countries = { "Germany", "USA", "France", "Italy", "Switzerland", "Austria", [...]}; String[] monate = { "Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"}; double[] zinssaetze ={ 2.0, 2.75, 4.0, 5.5 };
Indexüberprüfung Wenn auf einen ungültigen Index zugegriffen wird, meldet die Laufzeitumgebung eine Exception (siehe Abschnitt 6.1.11) vom Typ ArrayIndexOutOfBoundsException. Um dies zu vermeiden, ist es sinnvoll zu prüfen, ob der Index im gültigen Bereich liegt. Man kann dazu die Instanzvariable length eines Arrays verwenden:
4
Mehrdimensionale Arrays In Java gibt es kein besonderes Sprachkonstrukt für mehrdimensionale Felder. Da ein Feld aber andere Felder enthalten kann, ist man beliebig flexibel. Beispiel Double matrix[][] = new Double[5][10];
Es handelt sich hier streng genommen um ein Array, das wiederum Arrayobjekte vom Typ Double[] einhalten kann. Man könnte damit zum Beispiel auch Dreiecksmatrizen erzeugen: int int int int int
pascalDreieck[][] = new int[4][]; // 4 Zeilen ersteZeile[] = { 1 }; zweiteZeile[] = { 1, 1 }; dritteZeile[] = { 1, 2, 1 }; vierteZeile[] = { 1, 3, 3, 1 };
Java-Syntax
105
Nitty Gritty • Take that!
if (0 <= index && index < feld.length) ...
Sandini Bib pascalDreieck[0] pascalDreieck[1] pascalDreieck[2] pascalDreieck[3]
= = = =
ersteZeile; zweiteZeile; dritteZeile; vierteZeile;
pascalDreieck: int[][] 0 1 2 3 0 1 2 3 ersteZeile: int[]
1
zweiteZeile: int[]
1 1
dritteZeile: int[]
1 2 1
vierteZeile: int[]
1 3 3 1
Bild 4.13: Mehrdimensionales Array
Kürzer wäre die Schreibweise: int[][] pascalDreieck = { {1}, {1,1}, {1,2,1}, {1,3,3,1} };
Nitty Gritty • Take that!
4
Parameterübergabe an Applikationen Ein prominentes Beispiel für ein String-Array ist die Parameterübergabe in der main-Methode. Die Argumente sind in argv[] gespeichert. Dabei beginnen die Werte schon bei argv[0], das erste Argument enthält nicht wie unter C den Programmnamen! Die Anzahl der Argumente erhält man wie üblich über argv.length. Beispiel class ArgTest { public static void main(String argv[]) { for (int i = 0; i < argv.length; i++) System.out.println("Argument "+i+": "+argv[i]); } }
Der Aufruf java ArgTest Ein Test mit "zusammengesetzten Argumenten" ergibt:
106
Datentypen
Sandini Bib Argument Argument Argument Argument
0: 1: 2: 3:
Ein Test mit zusammengesetzten Argumenten.
Wenn Sie Argumente von der Kommandozeile einlesen, wird jede Leerstelle als Trennzeichen interpretiert. Damit beginnt eine neues Argument vom Typ String. Wenn Sie Argumente mit Leerstellen übergeben wollen, müssen Sie sie wie im obigen Beispiel in doppelte Anführungszeichen setzen. 4.8.7
Verhaltensunterschiede zwischen elementaren und Referenztypen Es gibt einige sehr wichtige Unterschiede in der Behandlung von elementaren Datentypen und Referenztypen, die wir im Folgenden diskutieren werden: Definition von Variablen
Beispiel int i; // Vorbelegung = 0 long j = 123L;
Wird eine Referenz angelegt, so ist sie per Default mit der Nullreferenz null vorbelegt. Eine Initialisierung mit (neuen) Objekten setzt eine explizite Erzeugung mittels new voraus. Einzige Ausnahme sind Stringliterale, für die es eine Kurzschreibweise gibt: Beispiel Konto kto; // Vorbelegung = null Konto kto = new Konto(); String name = "Kirsten";
Java-Syntax
107
4 Nitty Gritty • Take that!
Definiert man eine Variable eines elementaren Datentyps, wird automatisch Speicher allokiert und die Variable initialisiert. Der Operator new wird nicht benutzt. Konstruktoren gibt es nicht, eine Initialisierung erfolgt mit dem Standardwert oder einer Zuweisung bei der Variablendefinition.
Sandini Bib
Ein weiterer Unterschied ist das Verhalten bei der Parameterübergabe: Call-by-value Wird ein elementarer Datentyp als Parameter einer Methode übergeben, erfolgt der Aufruf Call-by-value. Das bedeutet, dass eine Kopie des Wertes übergeben wird. Wird diese Kopie manipuliert, ändert sich nichts am Wert des Originals. Beispiel int x = 1; // x=1 manipuliereX(x); // Aufruf [...] // x=1 public void manipuliereX(int rx) { rx = 2; // verändert nur Kopie von x }
Call-by-reference
Nitty Gritty • Take that!
4
Bei Methodenaufrufen mit Referenztypen werden die Objekte by-reference übergeben, also wird in der Methode das gleiche Objekt nur unter einem anderen Namen verwendet. Es wird nur die Referenz, nicht jedoch das referenzierte Objekt kopiert. Damit werden alle Manipulationen am übergebenen Objekt in der Methode am Original durchgeführt. Beispiel Konto quelle = new Konto(3000.0); Konto ziel = new Konto(0.0); quelle.ueberweisen(ziel, 500.0); // Aufruf [...] // ziel.stand = 500 void ueberweisen(Konto zielkonto, double betrag) { abheben(betrag); // verändert Originalobjekt! zielkonto.einzahlen(betrag); }
108
Datentypen
Sandini Bib
Diese unterschiedliche Behandlung geschieht aus Effizienzgründen. Man erreicht auf der einen Seite bei den elementaren, kleinen und oft benutzten Typen eine einfache Handhabung, bei komplexen und großen Objekten vermeidet man hingegen häufiges und unnötiges Kopieren. Häufig wünscht man sich aber auch bei elementaren Datentypen das Verhalten der Referenztypen, insbesondere wenn man Collections von Objekten anlegen will (Kollektionen oder Mengen), also zum Beispiel einen Vektor von Zahlen. Diese Collections sind nur für »echte« Objekte definiert. Vergleiche
Häufig ist die Methode equals() jedoch so umdefiniert , dass ein inhaltlicher Vergleich der Attribute durchgeführt wird anstatt eines reinen Referenzvergleich. Ein Beispiel hierfür ist die Klasse String (Abschnitt 4.8.5). Hätten wir also die equals-Methode Klasse Konto ebenso umdefiniert, würde das in dem in Abbildung 4.14 gezeigten Verhalten resultieren. Mehr dazu im Kapitel 6.1.1! WUXH NWR NWRD NWRD NWR NWR NWR WUXH NWRHTXDOVNWRD NWRDHTXDOVNWR NWRHTXDOVNWR
NWR .RQWR
.RQWR
QXPPHU VWDQG
NWRD .RQWR
NWR .RQWR
.RQWR
QXPPHU VWDQG
NWR .RQWR
.RQWR
QXPPHU VWDQG
Bild 4.14: Vergleiche von Objekten Java-Syntax
109
4 Nitty Gritty • Take that!
Vergleicht man zwei Objektreferenzen mit dem »==«-Operator, erhält man nur dann true, falls beide Referenzen auf exakt dasselbe Objekt zeigen. Auch wenn bei zwei verschiedenen Objekten derselben Klasse alle Attribute übereinstimmen, ergibt der Vergleich false. Ebenso verhält es sich bei Verwendung der Methode equals(), die sich im Standardfall exakt wie »==« verhält.
Sandini Bib
Den Typ einer Klasse kann man mit dem Operator instanceof vergleichen, z. B. if (einKonto instanceof Konto)...
Wrapper-Klassen In Java gibt es deshalb spezielle Wrapper-Klassen (siehe Abschnitt 6.1.7), die die elementaren Datentypen in Klassen einpacken. Diese dienen dazu, elementare Datentypen bei Bedarf in (unveränderbare) Objekte einzupacken, zum Beispiel um sie in eine Collection aufzunehmen. Zusätzlich bieten Wrapperklassen Konversionsmethoden zur Umwandlung von anderen und in andere Datentypen an, z. B. von einem String in eine Zahl. Diese Klassen beginnen mit einem Großbuchstaben am Anfang: T T T
4
T T
Nitty Gritty • Take that!
T
Byte statt byte Short statt short Integer statt int (nicht Int!) Long statt long Character statt char (nicht Char!) Boolean statt boolean
Byte und Short gibt es erst seit Java 1.1. Beispiel int i = 1; Integer iAlsObjekt = new Integer(i); int j = iAlsObjekt.intValue();
Ein Casting der Objekte in die elementaren Typen ist nicht möglich. Dafür gibt es spezielle Umwandlungsmethoden, wie zum Beispiel intValue().
4.9 Wichtige Sprachkonstrukte Die Kontrollstrukturen unterscheiden sich praktisch nicht von denen in C/C++ und anderen Programmiersprachen. Deshalb erfolgt hier keine ausführliche Einführung, sondern nur ein kurzer Überblick:
110
Wichtige Sprachkonstrukte
Sandini Bib
4.9.1 Blöcke Blöcke sind mit geschweiften Klammern eingefasst »{}«. Im Inneren können Sie lokale Variablen deklarieren, die nur in diesem Block Gültigkeit haben. Daneben kann ein Block beliebige Anweisungen oder weitere Blöcke enthalten. Jede Anweisung wird durch ein Semikolon »;« abgeschlossen. Sowohl Klassen und Interfaces als auch (nicht abstrakte) Methoden enthalten einen Implementierungsblock. Beispiel public class Konto { // Implementierungsblock der Klasse private int nummer; protected double stand; public void abheben(double betrag) { // Implementierungsblock der Methode if (stand > betrag) { // Block für Serie von Anweisungen int neuerStand = stand – betrag //lokale Variable stand = neuerStand; } } [...] }
Sie sollten sich ein geeignetes Formatierungsschema für Blöcke überlegen. In diesem Buch steht jede öffnende und schließende Klammer in einer eigenen Zeile. Die darin enthaltenen Elemente sind um eine Ebene (Tabulator) eingerückt. Häufig sieht man aber auch folgendes Schema: public void abheben(double betrag) { if (stand > betrag) { int neuerStand = stand – betrag; stand = neuerStand; } }
Java-Syntax
111
Nitty Gritty • Take that!
4
Sandini Bib
Synchronisierte Blöcke synchronized ( ) { } Falls Sie mit mehreren Threads arbeiten (siehe Kapitel 6.1.10), also verschiedene Abläufe parallel rechnen lassen, kann es vorkommen, dass verschiedene Threads dieselben Objekte manipulieren oder auf die gleiche Ressource zugreifen. Um Konflikte, undefinierte Zustände und unerwartete (»zufällige«) Resultate zu vermeiden, können Sie entweder die ganze Methode atomar ausführen lassen (siehe Abschnitt 4.5.5) oder in einem synchronisierten Block nur bestimmte Objekte schützen. Solange sich ein Thread in einem synchronisierten Block befindet, werden nur diejenigen anderen Threads bis zur Beendigung der Blocks angehalten, die ebenfalls auf das geschützte Objekt zugreifen möchten. Beispiel
Nitty Gritty • Take that!
4
synchronized (konto) { konto.abheben(200.0); }
4.9.2 Schleifen In Java gibt es drei Arten von Schleifen – while, do-while und for – die sich in der Art der Steuerung des Schleifendurchlaufes unterscheiden. while-Schleife while ( )
Bei der while-Schleife wird zunächst ein boolescher Ausdruck (Bedingung) ausgewertet. Falls dieser true ergibt, wird der Rumpf (ein Block oder eine einzelne Anweisung) so oft durchlaufen, bis die Bedingung, die vor jedem Durchlauf erneut ausgewertet wird, false ergibt. Danach wird mit der Anweisung nach dem Block weiter gearbeitet.
112
Wichtige Sprachkonstrukte
Sandini Bib
Beispiel while ( true ) einObjekt.doNothing(); // Endlosschleife while ( enum.hasMoreElements() ) { obj = enum.nextElement(); [...] }
Es passiert leicht, dass man einige Anweisungen ausführen möchte, wenn die while-Bedingung erfüllt ist, aber vergessen hat, diese Anweisungen in Blockklammern zu setzen. Der Compiler richtet sich dabei nicht nach der Formatierung, sondern nur nach der Klammerung. Passen Sie hier also immer auf! while ( enum.hasMoreElements() ) kto = enum.nextElement(); // kto.abheben(100); // // //
ohne Blockklammern! irreführende Klammerung! wirkt sich nur auf das letzte Element aus!
do ... while-Schleife do while ( )
Beispiel do {einObjekt.doNothing();} while (true);// Endlosschleife
for-Schleife for (; ; )
Die for-Schleife ist flexibler in der Steuerung der Schleifendurchläufe. Im Kopf findet eine Initialisierung statt. Zum Beispiel kann hier ein Schleifenzähler definiert werden. Der Ausdruck gibt an, ob der nächste Schleifendurchlauf durchgeführt wird. Wenn ja, wird zunächst der Rumpf und dann die Anweisung ausgeführt. Üblicherweise wird dort der Schleifenzähler hochgezählt:
Java-Syntax
113
Nitty Gritty • Take that!
4
Eine Variante der while-Schleife beginnt zuerst mit dem Durchlauf des Rumpfs und wertet erst dann die Bedingung aus. Damit wird sie mindestens einmal durchlaufen.
Sandini Bib
Beispiel for (;;) { einObjekt.doNothing(); } // Endlosschleife // Folgende Schleife wird 100x durchlaufen: for ( int i=0; i<100; i++ ) { einObjekt.doSomething(); } // Durch die Argumente einer main-Methode iterieren: for ( int j=0; j<argv.length; j++ ) System.out.println(argv[j]);
Die einzelnen Ausdrücke und Anweisungen im Kopf können durchaus leer bleiben, wie das erste Beispiel zeigt. Schleifendurchläufe abbrechen: break break break
Bei all den Endlosschleifen stellt sich die Frage, ob man darin gefangen ist oder Schleifen auch ohne Bedingung verlassen werden können.
Nitty Gritty • Take that!
4
Dafür gibt es die zwei Anweisungen break und continue. Mit break verlässt man den aktuellen Block. Man kann auch ein Label angeben, dann werden alle Blöcke bis zu dem Block mit dem Label verlassen (als Ersatz für goto unter C): Beispiel einLabel: for ( i=0 ; ; i++ ) // Schleife mit Label markiert { while ( true ) { if (i>100) break einLabel; // (1) while und // for-Schleife // werden verlassen break; // (2) nur while-Schleife wird verlassen dummy(); // nicht erreichbarer Code } // hier geht es nach dem break (2) weiter } // hier geht es nach dem break (1) weiter
114
Wichtige Sprachkonstrukte
Sandini Bib
Neben break können Sie Schleifen in Methoden natürlich auch mittels return abbrechen (siehe Abschnitt 4.4.1). Damit beenden Sie die gesamte Methode und somit natürlich auch die Schleife, die gerade ausgeführt wird. Sofort neue Iteration beginnen: continue continue continue
Analog zu break wird mit continue der aktuelle Schleifendurchlauf abgebrochen und die nächste Schleifeniteration begonnen. Falls ein Label angegeben ist, wird dort die neue Iteration begonnen. Beispiel einLabel: for ( i=0 ; i<100 ; i++ ) // Schleife mit Label markiert, // hier geht es nach dem // continue (1) weiter { while ( true ) // hier geht es nach dem // continue (2) weiter { if (i>10) continue einLabel; // (1) for-Schleife // wird neu begonnen i += 2; continue; // (2) nur while-Schleife neu beginnen dummy(); // nicht erreichbarer Code } }
Nitty Gritty • Take that!
4
Zusammenfassung 1 while ( ) 2 do while ( ) 3 for (;
Ausdruck>;
)
Java-Syntax
115
Sandini Bib 4 : T
einzelne Anweisung
T
Block von Anweisungen { ; ;...}
kann Schleifenabbruchbefehl oder return-Anweisung enthalten: T break: Abbruch
der Schleife
Abbruch der markierten Schleife continue: Beginn der nächsten Iteration
T break : T
T continue :
Beginn der nächsten Iteration der markierten
Schleife T return : Beendigung der aktuellen Methode
4.9.3
Bedingte Anweisungen
Für einfache true/false-Bedingungen gibt es die if-Anweisung, für komplexere Fallunterscheidungen switch. if-Anweisung
Nitty Gritty • Take that!
4
if ( ) if ( ) else
Bei der einfachen if-Anweisung wird die Anweisung oder der Block nur ausgeführt, wenn die Auswertung des booleschen Ausdrucks in Klammern true ergibt: if ( stand>0 ) deckung = true; if ( (file!=null) && file.exists() ) file.delete();
Falls der Ausdruck hingegen false ergibt, wird die Anweisung (oder der Anweisungsblock) übersprungen und die nächste Anweisung hinter der bedingten Anweisung ausgeführt. Optional kann auch für den false-Fall eine Anweisung oder ein Block angegeben werden: Beispiel boolean limitPruefen(double betrag) { if (stand – betrag > -kreditlimit)
116
Wichtige Sprachkonstrukte
Sandini Bib return true; else return false; } oder if ( i<0 ) { // i ist kleiner null signum=-1; } else if (i==0) signum=0; else signum=1;
Im letzten Beispiel sieht man eine Verkettung mehrerer if-Statements. switch-Anweisung switch () { case <Wert>: ; ... default: }
Beispiel switch (signum) { case -1: System.out.println("Zahl ist negativ."); break; case 0: case 1: System.out.println("Zahl ist nicht-negativ."); break; default: System.out.println("Fehler!"); }
Java-Syntax
117
Nitty Gritty • Take that!
4
Eine solche Schachtelung von Bedingungen kommt relativ häufig vor. In vielen Fällen handelt es sich um eine Auswahl, die von einem Ausdruck abhängt, der eine Reihe von konstanten Werten annehmen kann. Für die elementaren Datentypen boolean, byte, short, int und long gibt es dafür die switch-Anweisung:
Sandini Bib
Ist eine Alternative nicht mit break abgeschlossen (wie im Beispiel der Fall 0), so wird trotzdem die nächste Anweisung ausgeführt, was oft durchaus sinnvoll ist (so genanntes »fall-through«). Trifft keine Alternative zu, wird die Anweisung nach default aufgerufen. Sie können den default-Teil aber auch einfach weglassen. Ein häufiger Programmierfehler ist das Vergessen einer break-Anweisung. Würde im obigen Beispiel im Falle signum = -1 break fehlen, würden wegen des Fall-ThroughMechanismus zuerst die Anweisungen für den richtigen Fall ausgeführt, danach aber zusätzlich auch noch die für den nächsten Fall. Dies kann zu merkwürdigem Programmverhalten und schwer lokalisierbaren Fehlern führen. Für andere Datentypen oder Klassen wie Strings ist die switch-Anweisung leider nicht verwendbar. Häufig benutzt man die switch-Anweisung in Verbindung mit Konstanten: switch ( tastendruck.getKeyCode() ) { case KeyEvent.VK_DELETE: file.delete(); break; case KeyEvent.VK_F2: file.createNew(); break; }
Nitty Gritty • Take that!
4
Zusammenfassung 1 if ( ) if ( ) else 2 switch () { case <Wert>: ; ... default: }
118
Wichtige Sprachkonstrukte
Sandini Bib
4.9.4
Bedingte Zuweisung
var = ( )? :
Wie in C kann man in Java anstatt if ( x >= 0 ) betrag = x; else betrag = -x;
kürzer schreiben betrag = ( x >= 0 ) ? x : -x;
Ein beliebtes Beispiel ist die Maximumberechnung: Beispiel max = (i>j) ? i : j;
Ich möchte Ihnen aber von der Benutzung dieser Zuweisung abraten, da der Code leicht unübersichtlich wird. Würden Sie sofort erkennen, was diese verschachtelte bedingte Zuweisung macht?
4
4.10 Organisation von Klassen – Packages und Java-Archive 4.10.1 Ziele Schon die Java-Klassen des Standard-JDK 1.1 bestehen aus ungefähr 400 Einzelklassen, bei Java 2 sind es bereits über 2.300. Dazu kommen noch die von weiteren Klassenbibliotheken sowie selbst entwickelte Klassen. Die meisten dieser Klassen sind in Class-Dateien und Java-Archiven gespeichert, die im CLASSPATH stehen (sollten), einer Umgebungsvariable für die Standardklassen eines Systems. Es ist klar, dass hier schnell der Überblick verloren gehen würde und eine Gliederung in zusammengehörige Gruppen von Klassen erforderlich ist.
Java-Syntax
119
Nitty Gritty • Take that!
var = (((i>j)? i:j)<j)? i:j;
Sandini Bib
Bild 4.15: Einstellen der CLASSPATH-Systemvariable unter Windows NT
Nitty Gritty • Take that!
4 Eine explizite Angabe aller in einer Klasse referenzierten Klassen wie durch das #include-Statement der Sprache C wäre aber aufgrund der Fülle von Klassen unpraktikabel. Feinere Steuerung des Zugriffsschutzes Ein weiteres Problem stellt der Zugriffsschutz dar. Wie wir schon gesehen haben, braucht man eine Möglichkeit für Subklassen, auf interne Member ihrer Superklassen zugreifen zu können. Um effizient entwickeln zu können, ist es notwendig, dass auch besonders eng miteinander verzahnte Klassen, die nicht voneinander abgeleitet sind, zusätzliche Zugriffsrechte aufeinander erhalten. Namensräume Bei all den Klassen kann es natürlich vorkommen, dass zwei Klassen (z. B. aus verschiedenen Klassenbibliotheken) den gleichen Namen haben. Es muss deshalb ein möglichst allgemein gültiges Namensschema geben, um Klassen eindeutig referenzieren und identifizieren zu können. 120
Organisation von Klassen – Packages und Java-Archive
Sandini Bib
Packages All diese Forderungen werden mit dem Konzept der Packages erfüllt. Ein Package ist eine Gruppe zusammengehöriger Klassen, die über den Packagenamen eindeutig angesprochen werden können.
Bild 4.16: Darstellung von Packages in VisualAge
4.10.2 Das Schlüsselwort package
package einPackageName; class EineKlasse { [...] }
Eine Klasse gehört genau einem Package an; ein Package kann dabei aus beliebig vielen Klassen bestehen. In einem Package darf es nicht mehrere Klassen desselben Namens geben, in unterschiedlichen Packages ist dies erlaubt. Aufbau von Package-Namen Die Namen von Packages sind prinzipiell beliebig wählbar. Es gibt jedoch ein vorgegebenes System: Die Namen werden in einer Art hierarchischen Schreibweise angegeben, die einzelnen Teile mit Punkten getrennt: land.firma.projekt.subprojekt
Java-Syntax
121
4 Nitty Gritty • Take that!
Ein Package wird mit dem Schlüsselwort package definiert, das in der ersten Zeile jeder Klassendatei stehen kann:
Sandini Bib
Der Vorschlag von Sun ist, als erste Hierarchiestufe den umgekehrten Internet-Namen der Firma zu nehmen. An den Domainnamen wird typischerweise der Projektname angefügt, danach gegebenenfalls eine Hierarchie von Subprojektnamen. Falls das Package keinem Projekt zugeordnet werden kann, sollte ein fachlich sinnvoller Bezeichner gewählt werden. Die einzelnen Namensteile werden üblicherweise in gemischter Groß-/Kleinschreibung mit kleinem Anfangsbuchstaben geschrieben. com.sun.server de.nittyGritty.java de.hawlitzek.aktien de.abcBank.internet.homebanking
Allerdings hält sich Sun selbst häufig nicht an diese Konvention, die Sun-Klassen heißen sun.util... Fast alle Klassen der Standard-JavaKlassenbibliothek beginnen mit java, Java-Erweiterungen mit javax.
Nitty Gritty • Take that!
4
Bild 4.17: Die Standard-Java-Packages aus JDK 1.1
Speicherung In den meisten Betriebssystemen werden die Klassen in Verzeichnissen gespeichert, die diese Hierarchie widerspiegeln. Unter OS/2 und Windows würde man die Dateien des Packages de.hawlitzek.aktien unter \de\hawlitzek\aktien finden, wobei für einen Pfad im CLASSPATH steht. Unter UNIX wäre es z. B. de/hawlitzek/ aktien.
122
Organisation von Klassen – Packages und Java-Archive
Sandini Bib
Default-Package Grundsätzlich ist jede Java-Klasse in einem Package enthalten. Alle Klassen, die keine explizite package-Anweisung enthalten, werden automatisch in ein so genanntes Default-Package gespeichert, das keinen Namen besitzt. Sie sollten für alle Ihre Klassen ein Package angeben, um später eine thematische Zuordnung zu erleichtern und eine verständliche Verzeichnisstruktur zu erhalten. Einzige Ausnahme sind Testklassen, die nur zum Ausprobieren erstellt und danach wieder gelöscht werden. Hier lohnt sich der Aufwand des Packagings nicht und es werden Default-Packages benutzt. 4.10.3 Schlüsselwort import Zusammengehörige Klassen werden allein durch Angabe des gleichen Packagenamens zu einem Package zusammengefasst, eine Package-Datei oder ein Inhaltsverzeichnis existiert nicht. In Abschnitt 4.10.5 werden Java-Archive vorgestellt, die eine solche Bündelung ermöglichen.
4
Packages stellen Namensräume dar. Das bedeutet, dass innerhalb eines Packages alle anderen Klassen desselben Packages bekannt sind (beziehungsweise vom Compiler oder der Laufzeitumgebung ermittelt werden können). Zugriffe innerhalb eines Packages Man kann auf Klassen im gleichen Package mit ihren einfachen Namen zugreifen. Sind also in unserem Konto-Beispiel die Klassen Girokonto und Konto in demselben Package angesiedelt, kann man beide wie bisher benutzen. Wären sie hingegen in unterschiedlichen Packages angesiedelt, zum Beispiel Konto im Package de.nittyGritty.java.banking, müsste man in der Deklaration und den Methoden von Girokonto jedes Mal den kompletten Namen de.nittyGritty.java.banking.Konto benutzen.
Java-Syntax
123
Nitty Gritty • Take that!
Wie greift man nun auf Klassen in einem Package zu?
Sandini Bib
Zugriffe außerhalb eines Packages Will man auf Klassen zugreifen, die nicht innerhalb des aktuellen Packages definiert sind, die also nicht in dem Namensraum liegen, kann man dies auf zweierlei Weise tun: T
Braucht man die Klasse relativ selten oder gibt es eine Mehrdeutigkeit aufgrund von gleichen Klassennamen, greift man mittels des Package- und des Klassennamens auf die Klasse zu: de.nittyGritty.java.banking.Konto java.io.File
T
Der Namensraum (also der Sichtbarkeitshorizont) kann mit Hilfe der import-Anweisung erweitert werden, da das Ausschreiben ziemlich unpraktisch ist, wenn die gleiche Klasse häufiger verwendet wird.
import-Anweisung
4
Es handelt sich aber nicht um eine textuelle Einbindung von Klassen wie bei C. In Java werden Klassen nur geladen, wenn sie auch wirklich verwendet werden.
Nitty Gritty • Take that!
Diese Anweisung verhält sich logisch ähnlich einem #include unter C. Die Namen einer Klasse des anderen Packages oder alle darin enthaltenen Klassen sind nun in der aktuellen Klassendatei sichtbar.
Mit dem Statement import java.awt.Graphics wird die Klasse Graphics aus dem Package java.awt in der aktuellen Klasse bekannt gemacht. Die Klasse kann nun ohne den Vorsatz java.awt verwendet werden. Häufig will man aber nicht nur einzelne Klassen, sondern ganze Packages importieren. Dies wird mit dem Symbol ”*” erreicht: import java.awt.*. Nun sind alle Klassen (und Interfaces) dieses Packages im aktuellen Namensraum. Beispiel import de.nittyGritty.java.banking.Konto; import java.awt.*;
Pseudo-Hierarchie Bei Anwendung dieser abkürzenden Schreibweise sollten Sie Vorsicht walten lassen, denn obwohl durch die Punkte im Packagenamen eine echte Hierarchie vorgespiegelt wird, werden durch »*« 124
Organisation von Klassen – Packages und Java-Archive
Sandini Bib
keine »Subpackages« eingeschlossen. Das heißt, will man in einer Klasse mit der import-Anweisung aus obigem Beispiel die Klasse java.awt.event.WindowEvent ansprechen, muss man dies weiterhin mit vollem Namen tun. Auch event.WindowEvent reicht nicht. Abkürzungen wie java.awt.A* sind ebenfalls nicht zulässig. Das »*«-Symbol ist also keine Wildcard im üblichen Sinne, sondern nur eine Abkürzung für alle Klassen eines Packages. Sonderfall java.lang.* Alle Klassen aus der Package java.lang werden automatisch importiert, da diese sehr häufig verwendet werden. Dazu zählen die Wrapper-Klassen für die elementaren Datentypen ebenso wie String, System, Thread, Object und Math. Man kann also in jeder Klasse String statt java.lang.String schreiben.
Default-Zugriff Wenn bei einer Klasse oder einem Member keinerlei Zugriffsrecht angegeben wurden, gilt Folgendes: Auf dieses Code-Element kann von jeder Klasse desselben Packages aus zugegriffen werden, Klassen aus anderen Packages haben keinen Zugriff, selbst wenn sie die Klasse importieren. Es besteht also eine Package-weite Sichtbarkeit. protected Für das Zugriffsrecht protected gilt (anders als in C++!) dieselbe Regelung. Zusätzlich können – wie wir in diesem Kapitel (Zugriffsrechte) bereits gesehen haben – auch Subklassen darauf zugreifen. Java-Syntax
125
4 Nitty Gritty • Take that!
4.10.4 Packages und Zugriffsrechte Packages spielen auch bei den Zugriffsrechten eine wichtige Rolle, denn sie beeinflussen die Sichtbarkeit von Klassen und Membern. Neben private und public wurden protected und das Default-Zugriffsrecht in Abschnitt 4.5.1 schon angesprochen. Welche Rolle spielt dabei die Tatsache, dass eine Klasse in einem Package enthalten ist? private und public definierte Code-Elemente (Klassen, Interfaces, Variablen oder Methoden) sind lokal beziehungsweise global sichtbar, dies ist unabhängig von der Package-Struktur.
Sandini Bib
4.10.5 Java-Archive Java-Archive haben mit der Programmierung nichts zu tun, sondern sind ein Mittel zur vereinfachten Auslieferung des Codes. In einem Java-Archiv, einer Datei mit der Endung ».jar«, werden Java-BytecodeDateien und Ressourcen komprimiert gespeichert. Die Verwendung von Java-Archiven statt einzelner Class-Dateien beschleunigt insbesondere die Übertragung über das Netz, da nur noch große statt vieler kleiner Dateien geladen werden und die Datei zudem komprimiert ist. Deshalb sind Java-Archive besonders bei Applets beliebt. Das Format stellt eine Variante des ZIP-Formats dar, Sie können den Inhalt also mit jedem herkömmlichen unzip-Programm (z. B. WinZip) ansehen. Neben den einzelnen Klassen und Ressourcen enthält das Archiv ein Manifest, das Meta-Informationen über die im Archiv gespeicherten Daten enthält, zum Beispiel die Prüfsummen der komprimierten Daten. Dieses Manifest liegt unter meta-inf\Manifest.mf und hat beispielweise folgenden Inhalt für ein Archiv mit nur einer Klasse:
Nitty Gritty • Take that!
4
Manifest-Version: 1.0 Name: de/nittyGritty/java/banking/Konto.class Digest-Algorithms: SHA MD5 SHA-Digest: A8wxEo9x9zXae/deYR3iRaaMR3Y= MD5-Digest: y9FGldOxNXYJR08uFwdqnw==
Die Digest-Informationen stellen die Prüfsummen dar. Falls das Archiv zusätzlich noch digital signiert wurde, gibt es noch ein oder mehrere Signaturdateien mit der Endung ».sf«. Erstellung Wenn Sie mit dem JDK arbeiten (siehe Kapitel 2.1), können Sie JavaArchive mit dem Utility jar erstellen. Die Syntax ähnelt dem Unix-Tool tar und lautet: jar -Aufgabe[Optionen] archivname dateiliste
126
Organisation von Klassen – Packages und Java-Archive
Sandini Bib
Aufgabe
Bedeutung
c
neues Archiv erzeugen
u
bestehendes Archiv updaten
t
Inhalt eines Archivs auflisten
x
Archiv extrahieren
Option
Bedeutung
f
Archivname angeben
v
ausführliche Meldungen anzeigen (verbose)
0
keine Kompression
M
kein Manifest erzeugen
m
Manifest aus externer Datei importieren
C
Dateien nicht aus dem aktuellen, sondern aus dem Verzeichnis lesen
Beispiel
4
jar –cvf Banking.jar *.class
Dieses Archiv mit einer neuen Version der Klasse Konto updaten: jar –uvf Banking.jar Konto.class
Andere Entwicklungsumgebungen bieten meist komfortablere Möglichkeiten, ein Java-Archiv zu erstellen. Zum Beispiel können Sie in VisualAge ein komplettes Projekt selektieren, alle daraus referenzierten Klassen suchen lassen und das Ergebnis beim Export in ein Java-Archiv speichern. Benutzung Wenn Sie die Klasse Konto nun nutzen wollen, kann die Laufzeitumgebung ja nicht wissen, dass diese im Archiv Banking.jar liegt. Sie können jedoch das Archiv in den CLASSPATH aufnehmen. Dies funk-
Java-Syntax
127
Nitty Gritty • Take that!
Erzeugen eine neuen Archivs namens Banking.jar mit allen Klassen im aktuellen Verzeichnis erstellen:
Sandini Bib
tioniert genau so wie das Eintragen von Verzeichnissen in den CLASSPATH (siehe Kapitel 1.4), z. B. set CLASSPATH=%CLASSPATH%;c:\Banking.jar java KontoApp
oder java –classpath %CLASSPATH%;c:\Banking.jar KontoApp
Bei Applets wird ein Java-Archive mittels eines besonderen Tags in der HTML-Seite angegeben (siehe Kapitel 9): <APPLET CODE="KontoApplet.class" ARCHIVE="Banking.jar" WIDTH=300 HEIGHT=100>
Nitty Gritty • Take that!
4
128
Organisation von Klassen – Packages und Java-Archive
5
Sandini Bib
Java-2-Bibliothek
In den folgenden Kapiteln (5 bis 10) möchte ich Ihnen die zentralen Packages der Java-Klassenbibliothek vorstellen. Ziel dieses Kapitels ist nicht, eine Referenz aller Klassen darzustellen. Dieser Buchteil gibt Ihnen einen Leitfaden, wo Sie bestimmte Funktionalitäten finden, und stellt Ihnen einige besonders wichtige Konzepte vor.
5.1
Die wichtigsten Packages – eine Kurzübersicht
5 Nitty Gritty • Take that!
Schon die Standard Edition des JDK enthält eine für den Einsteiger unüberschaubare Menge fertiger Klassen. Einige davon werden für den Programmierer schnell zu täglichen Begleitern, andere tun ihren Dienst eher im Hintergrund. Dem Sprachstandard gemäß findet man diese Klassen auf diverse Packages verteilt. Um Ihnen einen groben Überblick über die wichtigsten dieser Klassen zu geben, möchte ich Ihnen hier Zweck und Inhalt der einzelnen Packages des JDK vorstellen (siehe Abbildung 5.1, in Java 2 hinzugekommene sind mit (J2) gekennzeichnet) und auf die gebräuchlichsten Klassen detaillierter eingehen.
Bild 5.1: Packages der Java-Standardklassenbibliothek
Java-2-Bibliothek
129
Sandini Bib
In diesem kurzen Kapitel möchte ich Ihnen einen ungefähren Anhaltspunkt geben, welche Themenbereiche von welchen Versionen von Java adressiert werden, die Beschreibung der enthaltenen Klassen und Interfaces erfolgt dann in den folgenden Kapiteln. Leider ist es unmöglich, all die Schlagwörter auch nur zu erklären, das würde Hunderte von Seiten füllen. Java vereint fast alle Bereiche der Programmierung – ein Entwickler interessiert sich mehr für 3DSpiele, ein anderer mehr für transaktionssichere Hostsysteme – und für fast jeden ist in der schier unerschöpflichen Menge der Klassenbibliotheken etwas dabei. Erschrecken Sie also nicht, wenn Sie hier vieles nicht verstehen, der anschließende Referenzteil behandelt nur die für alle wichtigen Klassen, aber mit Erläuterung und Beispielen.
Nitty Gritty • Take that!
5
Seit
SE
ME
EE
1.0
x
x
x
Obige Tabelle zeigt bei den besprochenen Klassen an, seit welcher Java-Sprachversion sie enthalten sind und ob sie Bestandteil der Standard Edition (SE, Abschnitt 5.2.1), Micro Edition (ME, Abschnitt 5.2.2) oder Enterprise Edition (EE, Abschnitt 5.2.4) sind.
5.2
Editionen der Java 2-Plattform
Wie bereits im ersten Kapitel erwähnt, ist Java ist seit Version 2 je nach Anwendungsgebiet in verschiedenen Ausgaben erhältlich, deren Umfang ich im folgenden vorstellen möchte. 5.2.1
Java 2 Standard Edition Java-2-Bibliothek
Basis-/ Hilfsklassen - java.lang - java.lang.ref - java.util - java.math
Grafik - java.awt - java.awt.event - java.awt.image - java.awt.font - java.awt.image - javax.swing ...
Ein-/ Ausgabe - java.io - java.awt.print
DatenKompoText/Internazugriff nenten tionalisierung - java.sql - java.beans - java.text - javax.swing.text - java.lang.reflect - java.util.zip - java.util.jar
Netzwerk/ Internet - java.net - java.applet . java.rmi - java.security ...
CORBA - org.omg.CORBA - org.omg.CosNaming
Bild 5.2: Gliederung der Java 2 Standard Edition 130
Editionen der Java 2-Plattform
Sandini Bib
Essenzielle Klassen Auf jeder Plattform steht ein Satz fundamentaler Klassen für die Erstellung von Java-Applikationen zur Verfügung. Hierzu zählen Basisund Hilfsklassen (java.lang, java.util), Klassen für die Ein-/Ausgabe (java.io), für Netzwerkkommunikation und Bibliotheken für die Gestaltung von Bedienoberflächen. Weitere Standardbibliotheken von Java 1.1 Die Standard Edition, die für den Einsatz in Clientrechnern gedacht ist, enthält zusätzlich Klassenbibliotheken für den Zugriff auf Datenbanken (java.sql), für Applets und andere Internetanwendungen (java.applet, java.net). Für die Gestaltung von Bedienoberflächen gab es bis Java 1.1 nur das Abstract Windowing Toolkit (java.awt). Textverarbeitung und Internationalisierung werden durch das Package java.text adressiert.
Java 2 Plattform Mit Java 2 (JDK 1.2) ging eine erhebliche Erweiterung der APIs einher, die jedoch auf vielen Zielplattformen wie Browsern und Application Servern noch nicht vollständig unterstützt werden. Die wichtigste Erweiterung ist die Swing-Bibliothek (javax.swing mit zahlreichen Subpackages) für den Entwurf moderner grafischer Bedienoberflächen. Weitere Zusätze betreffen den Grafikbereich (Zusätze in java.awt sowie diverse Subpackages), Drag & Drop (java.awt.dnd), Drucken (java.awt.print), formatierte Texte in HTML oder RTF (javax.swing. text), Collection-Klassen (neue Klassen in java.util), CORBA-Support (org.omg.CORBA, org.omg.CosNaming) und ein erneuertes SecurityKonzept (java.security und Subpackages).
Java-2-Bibliothek
131
5 Nitty Gritty • Take that!
Zur komponenten- und werkzeugbasierten Entwicklung dienen java.beans und java.lang.reflect. Aber auch Klassen für die Entwicklung verteilter Anwendungen (java.rmi) und für Verschlüsselungen und digitale Unterschriften (java.security) zählen zur Standardausstattung.
Sandini Bib
Bild 5.3: Neuerungen in Java 2 Standard Edition Version 1.2
Java 2 Version 1.3
Nitty Gritty • Take that!
5
Die Version 1.3 bietet auf Seiten der Bibliotheken nicht viel Neues. Sie beinhaltet aber einige bislang extra erhältliche Bibliotheken, besonders im Bereich des Client-Zugriffs auf Application Server. Hierzu gehören neue CORBA- und RMI-Klassen ebenso wie Bibliotheken zum Ansprechen von Namensdiensten. Des Weiteren sind einige Packages enthalten, die als Plug-In-Schnittstellen (SPI = Service Provider Interface) für zukünftige Erweiterungen durch andere Firmen dienen sollen. Ebenfalls erweitert wurde die Sound-Unterstützung.
Bild 5.4: Neuerungen in Java 2 Standard Edition Version 1.3
132
Editionen der Java 2-Plattform
Sandini Bib
Java 2 Version 1.4 Anfang 2002 wurde das JDK noch einmal stark erweitert: Neues gibt es im Bereich XML, Verschlüsselung, Datenbankzugriff sowie Grafik und Druck. Mit weiteren APIs können Log- und Benutzeroptionsdateien geschrieben werden. Auch die Auswertung regulärer Ausdrücke wird nun unterstützt.
Bild 5.5: Neuerungen in Java 2 Standard Edition Version 1.4
5.2.2 Java 2 Micro Edition Die Micro Edition ist eine abgespeckte Version für Geräte mit geringer Hardwareausstattung wie Palmtops/PDAs, Handys, Bordcomputer von Autos, Settopboxen oder Kiosksysteme. Sie basiert auf einer Miniversion der virtuellen Maschine, der KVM. Java 2 Micro Edition
Basis-/ Hilfsklassen - java.lang - java.util
Ein-/ Ausgabe - java.io
Profile/ Spezial-API -gerätespezifische Erweiterungen, GUI, Netz ...
Bild 5.6: Bestandteile der Java 2 Micro Edition Java-2-Bibliothek
133
Nitty Gritty • Take that!
5
Sandini Bib
Zusätzlich zu den fundamentalen Klassenbibliotheken gibt es so genannte Profile für den Typ des Geräts, zum Beispiel für Palmtops mit kleinem grafischen Display (z.B. Palm oder PocketPC), für SmartCards oder Smart-Handy wie dem Nokia Communicator oder dem Motorola Accompli. Teilweise gibt es auch noch gerätespezifische Zusatz-APIs, z. B. für den Palm. Die Micro Edition steckt allerdings noch sehr in den Kinderschuhen und es ist hier noch viel im Fluss.
Nitty Gritty • Take that!
5
5.2.3 Java 2 Enterprise Edition Die Enterprise Edition ist eine Erweiterung der Standard Edition für den Einsatz in Application Servern. Diese Server sollen in modernen, verteilten Architekturen die Steuerung von Businessprozessen und die zentrale Verarbeitung von Daten übernehmen. Aus Backendsystemen wie Datenbanken, Großrechnern oder komplexen wirtschaftlichen Systemen à la SAP sollen sie bestehende Anwendungen und Daten integrieren können, zu den Clients unterschiedlichste Plattformen unterstützen, vom WAP-Handy über den Webbrowser bis hin zur komplexen Swing-Anwendung. Die Enterprise Edition ist kein Produkt, sondern nur eine Sammlung von Schnittstellen, die ein Application Server erfüllen soll. Hersteller von Application Servern, die alle Schnittstellen erfüllen, können die Kompatibilität gegen die Referenzimplementierung von Sun prüfen lassen und erhalten ein Logo. Dieser Prozess soll sicherstellen, dass Hersteller wie IBM, BEA oder Inprise zueinander kompatible Server produzieren und der Entwickler auch auf der Serverseite plattformund herstellerunabhängige Anwendungen und Komponenten schreiben kann.
134
Editionen der Java 2-Plattform
Sandini Bib
Bild 5.7: Zusätzliche Packages in der Java 2 Enterprise Edition
Bild 5.8: Gliederung der Java 2 Enterprise Edition
5.2.4 Weitere Java-Klassenbibliotheken Daneben gibt es weitere Java-Klassen, die nicht zu einer Komplettedition gehören, jedoch für spezielle Zwecke als Erweiterungen der JavaAPI zur Verfügung stehen. Sun bezeichnet diese optionalen Bibliotheken als Java Extensions. Als Extensions sind viele Einzelteile der Enterprise Edition verfügbar, zum Beispiel Enterprise JavaBeans, ein Transaktionsdienst (JTS = Java Transaction Service), eine Schnitt-
Java-2-Bibliothek
135
5 Nitty Gritty • Take that!
Die Bereiche, in die sich die Enterprise Edition gliedert, sind: Servlets und Java Server Pages (javax.servlet.*) für die Unterstützung von HTML-Clients, Enterprise JavaBeans (javax.ejs) als Komponentenmodell, das Dienste wie Naming (javax.naming), Transaktionssicherheit (javax.transaction) und Persistenz (java.sql, javax.sql) anbietet. Daneben kann man mittels javax.mail.* auf einen Mailservice zugreifen. Optional ist ein Messaging Service für die asynchrone Übertragung von Nachrichten (javax.jms), z. B. ein Broadcast einer Meldung an registrierte Benutzer.
Sandini Bib
stelle zu verschiedenen Namensdiensten (JNDI = Java Name and Directory Interface), Servlets, die Mail-API und vieles mehr. Aber nicht nur der Serverbereich wird hier adressiert. Für Clients gibt es eine 3D-API und Jini für die Verbindung verschiedenartiger Geräte, z. B. Hauselektronik, Drucker. Die Tabelle zeigt einige Beispiele: Extension
Beschreibung
Java 3D
API für dreidimensionale Grafiken
Java Advanced Imaging (*)
Bildverarbeitung
Java Media Framework
Videos (MPEG, AVI ...), Audio (MP3, Wav ...)
Java Speech
API für Spracherkennung
Jini
Vernetzungstechnik für Geräte
JavaHelp
Hilfesystem auf HTML-Basis
Java Cryptograph Extensions (*)
Verschlüsselungsklassen
Secure Socket Extensions (*)
SSL-Verschlüsselung für diverse Internet-Protokolle
5 Nitty Gritty • Take that!
(*) seit Version 1.4 Bestandteil der Java 2 Standard Edition Andere Hersteller Neben diesen Standard-Klassenbibliotheken und -programmierschnittstellen, die von der Firma Sun Microsystems gepflegt werden, gibt es noch viele weitere von anderen Herstellern. Teilweise sind dies Programmierschnittstellen, die Brücken zu vorhandenen Softwareprodukten schlagen, komplette Sammlungen von eigenständigen Softwaremodulen wie die Lotus eSuite oder die JClass-Produkte der Firma Sitraka. Kataloge und Bestell- bzw. Download-Möglichkeiten gibt es im Internet unter anderem bei JARS (www.jars.com), Gamelan (www.gamelan.com) oder Component Source (www.componentsource.com/ java).
136
Editionen der Java 2-Plattform
6
Sandini Bib
Basis- und Systemklassen
Im folgenden Abschnitt lernen Sie die grundlegenden Klassen das Packages java.lang kennen. Die meisten sind in jeder virtuellen Maschine vorhanden (egal welcher Version und selbst in der Micro Edition). Das Package java.math (Abschnitt 6.2) ergänzt die Bibliothek um mathematische Funktionalität.
6.1
Datentypen und Systemklassen – java.lang
Das Package java.lang ist das wichtigste aller Packages. Der Compiler importiert das Paket java.lang wegen seiner elementaren Bedeutung vor der Übersetzung des Quellcodes automatisch in jede Klasse. Die Zeile import java.lang.*; können Sie beim Schreiben einer Klasse also getrost weglassen. java.lang
6
Klassen
Interfaces - Cloneable - Runnable
- System - Runtime - Process - Thread - ThreadGroup - ClassLoader - SecurityManager - Class - Compiler
Ausnahmebehandlung
WrapperKlassen - Boolean - Byte - Character - Double - Float - Integer - Long - Short - Number
Sonstige
Nitty Gritty • Take that!
SystemKlassen
- Object - Throwable - Math - Exception - NullPointerException - String - StringBuffer - ArrayIndexOutOf- Void BoundsException - ClassCastException - ClassNotFoundExc. - NumberFormatExc. - SecurityException - Error - InternalError - NoClassDefFoundError
Bild 6.1: Klassen im Package java.lang
6.1.1
Die Klasse Object
seit
SE
ME
EE
1.0
x
x
x
Basis- und Systemklassen
137
Sandini Bib
Um eine Klassenhierarchie oder -bibliothek zu definieren, braucht man zunächst eine Basis oder Wurzel, die das grundlegende Verhalten aller Klassen definiert. In Java wird diese Wurzel in Form der Klasse Object bereitgestellt. Sämtliche Javaklassen bilden eine Hierarchie mit java.lang.Object als gemeinsamer Basisklasse. Wie verhält sich das aber bei Klassen, die nicht als Subklassen deklariert wurden? Falls eine Klasse in der Deklaration nicht das Schlüsselwort extends enthält, ergänzt der Compiler automatisch extends java.lang.Object, so dass diese Klasse tatsächlich als »Urklasse« anzusehen ist. Methoden Durch dieses Verhalten als Wurzelklasse kann man bei jedem Objekt einer beliebigen Klasse die Basisfunktionalität in Anspruch nehmen, die Object in ihren Methoden bereitstellt. Diese sind in Abbildung 6.2 dargestellt. Die schematische Darstellung zeigt die Klasse in UMLDarstellung. Das »#«-Zeichen steht für das protected Zugriffsrecht, »+« für public. Im Anhang A finden Sie eine ausführlichere Dokumentation.
Nitty Gritty • Take that!
6 Object
+ equals(obj : Object) # finalize() + toString() + getClass() # clone() + wait() + notify() ...
Bild 6.2: Die Klasse Object T boolean equals( Object obj ) für Vergleiche T Object clone() für das Duplizieren von Objekten T void finalize() für das Aufräumen vor der Objektlöschung T String toString() für die Ausgabe von Objektbeschreibungen T void wait() und notify() für den Benachrichtigungsmechanismus T Class getClass() für Laufzeittypinformationen
138
Datentypen und Systemklassen – java.lang
Sandini Bib
Vergleichen und Kopieren Bei den Strings (Kapitel 4.8.5) haben Sie vielleicht schon die Methode equals() kennen gelernt, die zwei Objekte miteinander vergleicht. Dadurch dass Object diese Methode besitzt, kann man Objekte jeglicher Art vergleichen. Standardmäßig ist diese Methode so definiert, dass sie wie der Operator »==« Referenzen vergleicht. Das bedeutet, dass sie true liefert, wenn beide Objektreferenzen auf exakt dasselbe Objekt verweisen. Selbst wenn zwei Objekte derselben Klasse völlig identisch belegte Attribute besitzen, sind es unterschiedliche Objekte. Manchmal macht es aber Sinn, diese Methode für Subklassen geeignet zu überschrieben. Ein Beispiel ist die Klasse String, bei der die Methode true liefert, wenn in beiden Objekten inhaltlich die gleiche Zeichenkette steht. Grundsätzlich sich zwei Varianten an: 1. flacher Vergleich der Attribute 2. rekursiver inhaltlicher Vergleich.
6 Nitty Gritty • Take that!
Im ersten Fall würde equals() bei unserer Klasse Konto beispielsweise so überschreiben: public boolean equals(Object obj) { // gleicher Typ? if ( !(obj instanceof Konto) ) return false; Konto vObj = (Konto)obj; // Typumwandlung return ((nummer == vObj.getNummer()) && (stand == vObj.getStand()) && (inhaber == vObj.getInhaber())); }
Basis- und Systemklassen
139
Sandini Bib
Beispiel .RQWR LQKDEHU 3HUVRQ QXPPHU LQW VWDQG GRXEOH LQVWDQFHRI!! NWR.RQWR LQKDEHU QXPPHU VWDQG
S3HUVRQ
S3HUVRQ
QDPH )+DZOLW]HN
QDPH )+DZOLW]HN
NWR.RQWR LQKDEHU QXPPHU VWDQG
NWR.RQWR LQKDEHU QXPPHU VWDQG
Bild 6.3: Vergleich von Instanzen
Nitty Gritty • Take that!
6
Dies ergibt einen Vergleich von Feldern auf Referenzbasis. In Abbildung 6.3 sehen Sie drei Instanzen der Klasse Konto mit identischen Daten. Ein Vergleich der Art kto1==kto2 oder kto2==kto3 würde false ergeben, da es sich um unterschiedliche Objekte handelt. Anders sieht es bei der Anwendung der equals-Methode aus. Der Vergleich kto1.equals(kto2) würde false als Resultat zurückliefern, obwohl beide Objekte der Klasse Person die gleiche Wertebelegung besitzen. kto2.equals(kto3) ergibt true, da hier die beiden Referenzen auf dieselbe Instanz der Klasse Person verweisen. Möchten Sie den Vergleich dagegen rekursiv durchführen, das heißt den inhaltlichen Vergleich auch auf die beiden Person-Objekte ausdehnen, müssten Sie die Methode equals() wie folgt überschreiben: public boolean equals(Object obj) { // gleicher Typ? if ( !(obj instanceof Konto) ) return false; Konto vObj = (Konto)obj; // Typumwandlung if (nummer != vObj.getNummer() || stand != vObj.getStand()) return false; return (inhaber.equals(vObj.getInhaber())); }
140
Datentypen und Systemklassen – java.lang
Sandini Bib
Die Klasse Person müsste hier aber ebenfalls einen inhaltlichen Vergleich anbieten, der wiederum auf der equals-Methode der Klasse String aufsetzt: class Person { private String name; public boolean equals(Object obj) { if ( !(obj instanceof Person) ) return false; return (name.equals(((Person)obj).getName())); } }
Beispiel Variante 1: Shallow Copy – Kopie verweist auf gleiches Inhaberobjekt public class Konto implements Cloneable { [...] // von java.lang.Object geerbte clone-Methode // -> shallow copy public Object clone() { super.clone(); } }
Basis- und Systemklassen
141
6 Nitty Gritty • Take that!
Ähnliches gilt für clone(). Standardmäßig kopiert diese Methode ein Objekt mit dessen Referenzen (shallow copy), nicht aber die referenzierten Objekte (deep copy). clone() und equals() sollten immer gemeinsam geändert werden, um ein konsistentes Verhalten zu gewährleisten. Um sicherzustellen, dass sich der Entwickler über das Verhalten von clone() Gedanken gemacht hat, bevor er die Methode benutzt, ist diese Methode in Object protected deklariert und erzeugt eine CloneNotSupportedException, so lange man das Interface Cloneable nicht implementiert (Abschnitt 6.1.2).
Sandini Bib
Variante 2: Deep Copy – Kopie verweist auf Kopie von Inhaberobjekt public class Konto implements Cloneable { [...] public Object clone() { // überschriebene clone-Methode Konto copy = null; try { copy = (Konto)super.clone(); // deep copy (Klasse Person sei Cloneable) copy.setInhaber((Person)this.inhaber.clone()); } catch (CloneNotSupportedException e) { [...] } return copy; }
Nitty Gritty • Take that!
6
}
In der Micro Edition fehlt die Methode clone() der Klasse Object und das Interface Cloneable. Objektlöschung Bereits in Kapitel 4.4.3 haben wir die Methode finalize() kennen gelernt, die vom Garbage Collector vor der Vernichtung von Objekten aufgerufen wird. In dieser Methode sollte man Aufräumarbeiten durchführen, wie zum Beispiel Instanzenzähler dekrementieren oder unkritische Ressourcen freigeben. Sie sollten sich jedoch nicht zu sehr auf den Aufruf dieser Methode verlassen, denn zum einen ist durch die unterschiedlichen Garbage Collectoren der Aufrufzeitpunkt ungewiss und zum anderen könnte die Methode durch einen ungeplanten Programmabbruch vielleicht gar nicht zur Ausführung kommen. Deshalb haben zeitkritische oder wichtige Freigaben wie die von Datenbankverbindungen oder das Herausschreiben eines Puffers in eine Datei (flush) in finalize() nichts zu suchen.
142
Datentypen und Systemklassen – java.lang
Sandini Bib
Beispiel protected void finalize() { offeneDatei.close(); // Beispiel für Ressourcenfreigabe }
Objektausgabe Mit Hilfe der Methode toString() kann jedes Objekt als Textstring ausgegeben werden. Bei den Standardklassen wird hiermit eine übersichtliche Darstellung der wichtigsten Daten eines Objekts ausgegeben, zum Beispiel java.awt.Color[20, 150, 255] für ein Objekt der Klasse Color mit den Farbanteilen 20 für Rot, 150 für Grün und 255 für Blau. Bei eigenen Klassen wird standardmäßig der Klassenname mit einem objektidentifizierenden Anhängsel ausgegeben, zum Beispiel de.ars.test.Datum@562e oder de.nittyGritty.java.banking.Konto@da9. Die Methode toString() sollten Sie deshalb geeignet überschreiben, so dass etwa de.nittyGritty.java.banking.Konto [0436577800: 18663.0, Florian Hawlitzek] ausgegeben wird: public String toString() { return getClass().getName() + "[" + nummer + ": " + stand + ", " + inhaber.getName() + "]"; }
Was es mit dem Aufruf von getClass() auf sich hat, lernen wir im Zusammenhang mit der Klasse Class kennen (Abschnitt 6.1.3). wait(), notify() und notifyAll() sind Methoden, die Sie nicht überschreiben können. Sie dienen vor allem der Javainternen Threadsynchronisation. Ein mittels wait() blockiertes Objekt kann durch einen Aufruf einer der notifyMethoden wiedererweckt werden (siehe Online-Kapitel).
6.1.2
Das Interface Cloneable
seit
SE
ME
EE
1.0
x
-
x Basis- und Systemklassen
143
Nitty Gritty • Take that!
6
Beispiel
Sandini Bib
Jede Klasse erbt von Object eine Methode clone(). Diese ist allerdings nur protected definiert, so dass sie als öffentliche Methode überschrieben werden sollte, die super.clone() aufruft. Das Duplizieren (Klonen) ist aber nur zugelassen, wenn eine Klasse explizit das (leere) Interface Cloneable implementiert. Damit wird sichergestellt, dass sich der Programmierer Gedanken über die Art des Klonens (deep/shallow copy) gemacht hat (siehe Abschnitt 6.1.1). Leere Interfaces, die nur zur Sicherstellung bestimmter Sachverhalte dienen, nennt man auch Markierungsinterfaces. 6.1.3
Nitty Gritty • Take that!
6
Die Klasse Class
seit
SE
ME
EE
1.0
x
x
x
Diese Klasse stellt eine Art Metaklasse dar. Das bedeutet, ein Objekt dieser Klasse beschreibt den Namen und Aufbau einer anderen Klasse. Dies erfolgt ganz analog zu SQL-Datenbanken, bei denen Systemtabellen derselben Bauart den Aufbau, zum Beispiel Anzahl und Datentypen der Spalten, einer Tabelle dokumentieren. Die Klasse Class ist besonders wichtig im Zusammenhang mit selbstdokumentierenden Komponenten, den so genannten JavaBeans, die wir im Kapitel 10.4 vorstellen werden. Class +getName() +toString() +getPackage() +getSuperclass() +forName(name: String) +newInstance() +getFields() +getMethods() +getConstructors() +isArray() +isInterface() ...
Bild 6.4: Die Klasse Class
144
Datentypen und Systemklassen – java.lang
Sandini Bib
Methoden Abbildung 6.4 zeigt die wichtigsten Methoden von Class in Form eines UML-Klassendiagramms. Klassenmethoden und Klassenvariablen – also static definierte Elemente – werden in UML unterstrichen dargestellt. Die wichtigsten Methoden sind: T String getName() und toString() zur Ausgabe des Klassennamens T Package getPackage() und Class getSuperclass() zur Einordnung der
Klasse T Class forName(String) zum dynamischen Laden von Klassen T Object newInstance(), Field[] getFields(), Method[] getMethods(),
Constructor[] getConstructors(), boolean isArray(), boolean isInterface() und weitere zur Analyse und generischen Nutzung unbe-
kannter Klassen. Vergleiche Wenn Sie feststellen möchten, ob zwei Objekte derselben Klasse angehören, können Sie den Vergleich mittels if ( (obj1.getClass()).equals(obj2.getClass()) )
Beispiel Object einKonto = new Girokonto(2000.0); // Das Objekt wird hier absichtlich einer Referenz eines // allgemeineren Typs zugewiesen. Häufig bekommt man // in der Praxis nur Object-Referenzen aus Collections // zurück und muss den tatsächlichen Typ erst // herausfinden. // ergibt false: boolean b1 = einKonto.getClass().equals( Class.forName("de.nittyGritty.java.banking.Konto") ); // ergibt true: boolean b2 = (einKonto instanceof Konto);
Basis- und Systemklassen
145
Nitty Gritty • Take that!
6
durchführen. Allerdings berücksichtigt dieser Vergleich keine Vererbung. Wenn es Ihnen also nicht um die tatsächliche Klassenzugehörigkeit geht, sollten Sie das Schlüsselwort instanceof benutzen, wie folgendes Beispiel zeigt. Auch ist die Anwendung im zweiten Fall einfacher und die Ausführung schneller.
Sandini Bib
Das Beispiel ist beim ersten Vergleich sogar noch vereinfacht dargestellt, denn man müsste den Fehlerfall behandeln, dass die Klasse de.nittyGritty.java.Konto nicht existiert. Da getClass() ein Objekt des Typs Class zurückliefert, können wir es nicht einfach mit dem Namen der Klasse vergleichen (denn dies wäre ein Objekt der Klasse String), sondern müssen mittels forName() ein Class-Objekt erzeugen. Ausgabe des Klassennamens Mit der Methode getName() können Sie den vollqualifizierten Namen der Klasse als String erhalten, das heißt inklusive des Package-Namens. Alternativ können Sie auch toString() aufrufen, dann wird zusätzlich noch der Typ der »Klasse« vorangestellt: Klasse oder Interface (Abschnitt 4.6). Einordnung in Klassenhierarchie getPackage() liefert analog das Package (Abschnitt 4.10) zurück, dem
Nitty Gritty • Take that!
6
die Klasse des übergebenen Objekts angehört (seit Java Version 1.2). Allerdings nicht als String, sondern als Objekt der Klasse Package. Wenn Sie nur der Name interessiert, können Sie obj.getPackage().toString() aufrufen. Die Superklasse erhalten Sie mit der Methode getSuperclass(), wiederum als Objekt der Klasse Class. Laden und Analysieren von Klassen Im obigen Vergleichsbeispiel wurde auch die Klassenmethode Class.forName(String klassenname) benutzt. In diesem Zusammenhang dient sie dazu, ein Class-Objekt für eine benannte Klasse zu erzeugen und zurückzuliefern. Mit newInstance() kann man dann ein Objekt dieser Klasse instanziieren. Die Methode forName() hat aber noch einen weiteren Effekt: Falls die angegebene Klasse bisher noch nicht benutzt wurde, also nicht in einer der .class-Dateien statisch referenziert wurde, wird die Klasse zur Laufzeit der Anwendung dynamisch im CLASSPATH oder auf dem Ursprungsserver des Applets gesucht und in die Laufzeitumgebung geladen.
146
Datentypen und Systemklassen – java.lang
Sandini Bib
Das dynamische Nachladen und Binden von Klassen »on demand« stellt eine Besonderheit von Java dar. Damit kann man Anwendungen schreiben, die beim Start nur die grundlegenden Klassen laden und damit sehr schnell starten und wenig Speicher belegen und bei Benutzung weiterer Funktionalität nur die dazu notwendigen Klassen nachzuladen und damit die Anwendung auf das Benutzerverhalten zu optimieren. Es ist sogar möglich, eine völlig neue Klasse zur Laufzeit zu erzeugen und sofort zu benutzen. Eine genauere Erläuterungen dieser Fähigkeiten würde aber den Rahmen dieser kurzen Java-Einführung sprengen.
In der Micro Edition fehlen fast alle Analysemethoden.
6.1.4
Die Klasse String
seit
SE
ME
EE
1.0
x
x
x
Die Stringklassen String (siehe 4.8.5) und StringBuffer sind weitere wichtige Datenstrukturen dieses Packages. Die Verkettung von Strings mit »+« und andere String-Operationen sind relativ langsam. Das liegt daran, dass Java jedes Mal ein neues String-Objekt für das Ergebnis (und für Zwischenergebnisse) anlegt. Objekte der Klasse String sind nach ihrer Erzeugung konstant, d. h. man kann auch keine Zeichen ändern oder ersetzen. Besser geeignet für die String-ManiBasis- und Systemklassen
147
6 Nitty Gritty • Take that!
Class hat noch eine ganze Reihe weiterer Methoden, die hauptsächlich zur Beschreibung der Schnittstelle einer Klasse und zur Einordnung in die Klassenhierarchie dienen. Diese abstrakte und dynamische Dokumentation von Klassen wird als Reflection bezeichnet (siehe Abschnitt 10.3). Class trägt dazu Methoden zur Identifikation der Superklasse (getSuperclass()), der Methoden (getMethods()), Konstruktoren (getConstructors()) und Variablen (getFields()) einer Klasse bei. Außerdem kann man abfragen, ob es sich um eine Klasse oder ein Interface handelt (isInterface()).
Sandini Bib
pulation ist die Klasse StringBuffer, die die meisten Operationen »am Platz« durchführen kann. Sie stellt auch Methoden zum Einfügen und Anhängen weiterer Zeichen bereit. String
StringBuffer
+length() +trim() +concat(s : String) +charAt(index : int) +compareTo(s : String) +equals(obj : Object) +getBytes() +indexOf(char : int) +indexOf(substring : String) +valueOf(n : ...) +substring(von : int, bis : int) +toUpperCase() +toLowerCase() +replace(alt : char, neu : char) ...
+length() +setLength(len : int) +toString() +append(o : ...) +insert(index : int, o : ...) +charAt(index : int) +setCharAt(index : int, ch : char) +capacity() +reverse() ...
Bild 6.5: Die Klassen String und StringBuffer
Erzeugung
6
Strings kann man auf mehrere Weisen erzeugen:
Nitty Gritty • Take that!
T T T T
durch Zuweisung eines Literals: s = "Das ist ein String" aus einem Stringbuffer: s = new String(buf) aus einem elementaren Datentyp: s = String.valueOf(zahl) aus einem Array von Charactern oder Bytes: s = new String(chars)
Falls man ein Bytefeld zu einem String konvertiert, kann man optional noch das so genannte Encoding angeben. Java verwendet intern Unicode, durch geeignete Konverter können auch andere Encodings wie ISO8859-1, UTF-8, EBCDIC usw. gelesen werden. StringBuffer-Objekte erzeugt man aus einem String oder leer mit Angabe der Länge der maximal enthaltenen Zeichen (Kapazität). Methoden length() liefert bei beiden Klassen die Länge des Strings (Anzahl der Zeichen). Mit charAt(index) können Sie ein Zeichen an einer Indexpo-
sition abfragen, bei StringBuffer-Objekten ein solches auch mit setCharAt(index, zeichen) neu besetzen. 148
Datentypen und Systemklassen – java.lang
Sandini Bib
String-Objekte können Sie mit dem »+«-Zeichen oder der Methode concat() verketten, bei Stringbuffer-Objekten gibt es noch appendund insert-Methoden für verschiedene Datentypen. Für Vergleiche können Sie außer equals() auch die Methode compareTo() benutzen, die einen alphabetischen Vergleich ausführt. Daneben gibt es verschiedene Index- und Suchfunktionen für Zeichen- oder Zeichenketten in einem String. Varianten eines String können Sie durch folgende Methoden erzeugen: liefert den String, aber ohne führende und abschließende Leerzeichen
T trim()
T
mit substring(von, bis) erhalten Sie einen Teil der Zeichenkette toUpperCase() bzw. toLowerCase() ergeben eine komplette Großoder Kleinschreibung.
6.1.5
Die Klasse Math
seit
SE
ME
EE
1.0
x
(x)
x
6
Die Klasse Math beinhaltet alle gebräuchlichen mathematischen Funktionen als Klassenmethoden sowie Konstanten wie die eulersche Zahl e (Math.E) und pi (Math.PI). In der Micro Edition ist sie allerdings auf die Betrags- und Minimum-/Maximumberechnung beschränkt. Math +PI : double +E : double +abs(...) +min/max(...) +round(...) +sin/cos/tan(winkel : double) +asin/acos/atan(x : double) +exp/log(x : double) +pow(basis : double, exp : double) +sqrt(x : double) +ceil/floor(x : double) +random() ...
Bild 6.6: Die Klasse Math Basis- und Systemklassen
149
Nitty Gritty • Take that!
T
Sandini Bib
Methoden Von Math werden keine Instanzen erzeugt, sondern Klassenmethoden aufgerufen, z. B. wurzel = Math.sqrt(5.0). Die Methoden sind jeweils für verschiedene elementare Zahlendatentypen definiert. abs() berechnet den (Absolut-)Betrag, min/max() das Minimum bzw. Maximum zweier Zahlen, round() rundet eine Zahl, ceil/floor() ermitteln für double-Werte die nächste Ganzzahl. pow(a, exp) berechnet aexp und sqrt() die Wurzel. Mit random() kann man eine Zufallszahl im Bereich 0.0 bis 1.0 erzeugen. Neben diesen mathematischen Funktionen auf Basis von Ganz- und Fließkommazahlen gibt es noch eine Reihe von Klassen für lange und Festkommazahlen im Package java.math (siehe Abschnitt 6.2). Diese sind besonders wichtig, wenn man Rundungsfehler vermeiden will oder exakte Vorgaben hat, auf wie viele Stellen hinter dem Komma gerechnet oder nach welchen Vorgaben gerundet werden soll.
Nitty Gritty • Take that!
6
6.1.6
Die Klasse StrictMath
seit
SE
ME
EE
1.3
x
-
-
Die in JDK 1.3 neu enthaltene Klasse StrictMath beinhaltet dieselbe Funktionalität wie Math, jedoch entsprechen die Ergebnisse von numerischen Methoden bis aufs Bit standardisierten Algorithmen. 6.1.7
Die Klasse Number und Wrapperklassen
seit
SE
ME
EE
1.0/1.1
x
(x)
x
Die abstrakte Klasse Number bildet ein Grundgerüst für die Kapselung der elementaren Datentypen byte, short, int, long, float und double. Sie fordert Konversionsmethoden der von Number abgeleiteten Klassen in alle numerischen elementaren Datentypen.
150
Datentypen und Systemklassen – java.lang
Sandini Bib
Number {abstract}
Boolean +booleanValue() +toString() +valueOf(s : String) +getBoolean(s : String) ...
Byte
+byteValue() +shortValue() +intValue() +longValue() +floatValue() +doubleValue()
Short
Float
Character +charValue() +toString() +isDigit(c : char) +isLetter(c : char) +isWhitespace(c : char) +getType(c : Char) +getNumericValue(c : char) ...
Integer
Long
Double
Bild 6.7: Wrapper-Klassen
Auf die so genannten Wrapper-Klassen (Boolean, Character, Byte, Short, Integer, Long, Float und Double) wird sehr oft bei Datenkonvertierungen zurückgegriffen. Sie können als in Objektform verpackte Zahlentypen dienen: double d = 354.42; Double dAlsObjekt = new Double(d);
Häufig erzeugt man aber keine Objekte, sondern nutzt nur die Klassenmethoden: Beispiel // Umwandlung String -> elem. Datentyp: int i = Integer.parseInt(eineZahlAlsString); // Umwandlung elem. Datentyp -> String: String s = Double.toString(d);
Wollen Sie einen String in einen numerischen elementaren Datentyp wandeln, benutzen Sie die Klassenmethode parse() des entsprechenden Zahltyps (z. B. parseInt()), wollen Sie hingegen ein Objekt der Wrapperklasse, benutzen Sie den jeweiligen Konstruktor oder die Methode valueOf(). Value() (z. B. intValue()) wandelt das Objekt in einen elementaren Datentyp zurück, toString() wie üblich in eine Zeichenkette. Basis- und Systemklassen
151
Nitty Gritty • Take that!
6
Beispiel
Sandini Bib
Die Klasse Character bietet einige nützliche Methoden zur Ermittlung des Typs eines Zeichens, zum Beispiel Buchstabe, Zahl oder Leer-/ Trennzeichen. Mit getNumericValue(zeichen) ermitteln Sie die Unicode-Nummer eines Zeichens. Zwar nicht im Package java.lang gelegen, sondern in java.math, aber dennoch Subklassen von Number sind die beiden Klassen BigInteger und BigDecimal für die Darstellung von großen Zahlen, die nicht in die vorgegebenen Wertebereiche der elementaren Datentypen und ihrer Wrapper passen. Objekte vom Typ BigDecimal sind Festkommazahlen, bei denen man eine beinahe beliebige Genauigkeit und das Rundungsschema bei Rechenoperationen vorgeben kann. Siehe Abschnitt 6.2. Die Micro Edition enthält lediglich eingeschränkte Varianten von Boolean, Byte, Short, Integer und Long. 6.1.8
Nitty Gritty • Take that!
6
Die Klasse System
seit
SE
ME
EE
1.0
x
x
x
Auch die Klasse System findet sich hier in diesem Package. Sie besteht nur aus Klassenvariablen und -methoden und kann nicht instanziiert werden. Sie stellt die Verbindung zum unterliegenden Betriebssystem dar und erlaubt beispielsweise den Zugriff auf die Standardein-/ausgabe. Weitere Aufgaben von System sind das Laden von Bibliotheken und die Ermittlung von Systeminformationen, wie zum Beispiel Systemzeit oder Namen und Version des Betriebssystems. System +in : InputStream +out : PrintStream +err : PrintStream +gc() +exit(status : int) +loadLibrary(name : String) +getProperty(prop : String) +getProperties() +setProperties(props : Properties) +setIn(in : InputStream) +setOut(out : PrintStream) +setErr(err : PrintStream) +currentTimeMillis() +get/setSecurityManager() ...
Bild 6.8: Die Klasse System 152
Datentypen und Systemklassen – java.lang
Sandini Bib
Die Klassenvariablen in, out und err stellen die Standardeingabe, Standardausgabe sowie Fehlerausgabe zur Verfügung. Sie haben diese Variablen zum Beispiel für Textausgaben auf der Konsole bereits verwendet (System.out.println(einString);). Beispiel System.gc(); // ruft den Garbage Collector manuell auf System.exit(0); // beendet die aktuell benutzte // virtuelle Maschine System.getProperty("java.version"); // fragt System// variablen ab System.getProperty("user.dir"); // fragt System// variablen ab System.loadLibrary("testlib"); // lädt eine DLL, // z. B. testlib.dll // unter Windows System.currentTimeMillis(); // liefert die // aktuelle Zeit
Property
Bedeutung
nicht in Applets
java.version
JRE Version
java.home
Verzeichnis des JRE
x
java.class.path
aktueller CLASSPATH
x
os.name
Name des Betriebssystems
os.version
Version des Betriebssystems
file.separator
Pfadtrennzeichen (z. B. »/« o. »\«)
line.separator
Zeilentrennzeichen (z. B. »\n«)
user.name
Benutzerkennung
x
user.home
Homeverzeichnis des Benutzers
x
user.dir
aktuelles Arbeitsverzeichnis
x
Basis- und Systemklassen
153
6 Nitty Gritty • Take that!
Hier einige Standardproperties, wobei aus Sicherheitsgründen in unsignierten Applets einige Properties nicht zugänglich sind:
Sandini Bib
6.1.9
Die Klassen Runtime und Process
seit
SE
ME
EE
1.0
x
-
x
Zum Aufrufen von Programmen bedient man sich der Klassen Runtime und Process. Process stellt eine Referenz auf ein externes Programm dar. Ausgeführt wird es durch den Aufruf der Methode exec() der Klasse Runtime. Process
Runtime
+destroy() +waitFor() +getInputStream() +getOutputStream() +getErrorStream() ...
+getRuntime() +exec(command : String) ...
Bild 6.9: Die Klassen Runtime und Process
6
Beispiel
Nitty Gritty • Take that!
Runtime.getRuntime().exec("notepad");
Als Ergebnis eines exec-Aufrufes erhalten Sie ein Process-Objekt zurück. Sie können damit Streams auf die Ein-/Ausgabe des Prozesses aufbauen, auf das Ende des Prozesses warten (waitFor()) oder ihn mittels destroy() beenden. 6.1.10 Thread-Klassen seit
SE
ME
EE
1.0
x
x
x
Threads stellen eine Art Mini-Prozesse dar, die quasi-parallel im gleichen Adressraum ablaufen. Derselbe Code kann also »zur selben Zeit« mehrmals ausgeführt werden oder einzelne Aufgaben einer Anwendung laufen »gleichzeitig« in eigenen Threads ab. Java unterstützt Multithreading mittels der Klasse Thread, ThreadGroup, dem Interface Runnable sowie seit 1.2 ThreadLocal.
154
Datentypen und Systemklassen – java.lang
Sandini Bib
«Interface» Runnable +run()
Thread +MIN_PRIORITY : int +MAX_PRIORITY : int +NORM_PRIORITY : int +Thread() +Thread(Runnable) +run() +start() +stop() {deprecated} +suspend() {deprecated} +resume() {deprecated} +join() +sleep(time : long) +yield() +get/setPriority() +get/setName() +currentThread() +getThreadGroup() +isAlive() +set/isDaemon() ...
ThreadGroup +getName() +activeCount() +list() +enumerate(Thread[]) +interrupt() +stop() {deprecated} +suspend() {deprecated} +resume() {deprecated} ...
ThreadLocal +get() +set(wert: Object) #initialValue()
Bild 6.10: Thread-Klassen
6 Erzeugung Sie können neue Threads entweder als Klassen von Thread ableiten oder – falls dies wegen einer anderen Vererbungshierarchie nicht geht – das Interface Runnable implementieren und dieses Objekt dem Thread-Konstruktor übergeben. Danach starten Sie den Thread mittels start(). Methoden Die Klasse Thread enthält viele Methoden zur Ablaufsteuerung, manche sind allerdings »deprecated« (sollen also nicht mehr benutzt werden), da sie zu Verklemmungszuständen des Programms oder zu Abbrüchen ohne Ressourcenfreigabe führen könnten (siehe Webseite). Haben Sie viele Threads erzeugt, die Sie gemeinsam steuern möchten, dann können Sie dazu die Klasse ThreadGroup benutzen (siehe Webseite). Basis- und Systemklassen
155
Nitty Gritty • Take that!
Auf der Nitty-Gritty-Webseite wird Multithreading ausführlich dargestellt, deshalb an dieser Stelle nur einige Hinweise:
Sandini Bib
Alle Threads laufen in einem gemeinsamen Adressraum. Deshalb kann es leicht zu Problemen führen, wenn mehrere Thread gleichzeitig versuchen, dieselben Ressourcen oder Variablen zu manipulieren. Mit Hilfe des Modifiers synchronized (Abschnitt 4.5.5) und synchronized-Blöcken (Abschnitt 4.9.1) kann man den exklusiven Zugriff eines Threads garantieren. Wenn Sie hingegen eine Variable pro Thread erzeugen möchten, können Sie dies seit JDK 1.2 mit der Klasse ThreadLocal erreichen (siehe Webseite). ThreadGroup und ThreadLocal fehlen in der Micro Edition.
6.1.11
Exceptions und Errors
seit
SE
ME
EE
1.0
x
x
x
Throwable
Nitty Gritty • Take that!
6 <> +Throwable() +Throwable(msg : String) <<Methoden>> +getMessage() +printStackTrace() +toString() ...
Error
InternalError
Exception
OutOfMemoryError
InterruptedException
NoClassDefFoundError
IllegalArgumentException
NumberFormatException
ClassNotFoundException RuntimeException
NullPointerException
SecurityException
ArrayIndexOutOfBoundsException
Bild 6.11: Exceptions und Errors in java.lang 156
Datentypen und Systemklassen – java.lang
Sandini Bib
Im Rahmen des Abschnitts zur Fehlerbehandlung (Kapitel 11) werden wir noch die Klasse Throwable als Basisklasse für alle Exceptions (Ausnahmen) und Errors (fatale Fehler) kennen lernen. Ihnen sind möglicherweise bereits die Exceptions NullPointerException, ArrayIndexOutOfBoundsException, SecurityException, IOException oder der Fehler NoClassDefFoundError begegnet. Methoden Alle Fehler und Ausnahmen stammen von der Klasse Throwable ab. Zur Ausgabe stellt diese drei Methoden bereit: T toString() liefert Name und Beschreibung der Ausnahme T getMessage() zeigt nur die Meldung (falls vorhanden)
gibt die Ausnahme mit deren Entstehungsgeschichte auf der Standardfehlerausgabe System.err aus.
T printStackTrace()
Abgeleitete Klassen
Errors sind fatale Fehler, die jederzeit auftreten können und die man nicht sinnvoll abfangen kann. Ausnahmen oder Exceptions sollte man in der Regel behandeln. Mit Ausnahme der von RuntimeException abgeleiteten Ausnahmen zwingt der Compiler den Entwickler sogar zur Fehlerbehandlung. 6.1.12 Weitere Klassen Die anderen Klassen des Packages java.lang sind für Interna der virtuellen Maschine wichtig, werden aber selten direkt vom Entwickler benutzt. Der ClassLoader ist für das Laden von Klassen in die Laufzeitumgebung zuständig, je nach Art bestimmt er auch die Zugriffsrechte auf Systemressourcen. Diese werden wiederum vom SecurityManager kontrolliert.
Basis- und Systemklassen
157
6 Nitty Gritty • Take that!
Fehler- und Ausnahmeklassen werden von den Standardklassen abgeleitet. Meist enthalten eigene Exceptionklassen nichts weiter als zwei Konstruktoren (mit und ohne Message-String), aber allein der Name und der intern gespeicherte Stacktrace sagt schon sehr viel über die Fehlerursache aus (siehe Abschnitt 11.3.3).
Sandini Bib
6.2
Mathematische Klassen – java.math
seit
SE
ME
EE
1.1
x
-
x
Die beiden Klassen BigInteger und BigDecimal sind Subklassen von java.lang.Number für die Darstellung von großen Zahlen, die nicht in die vorgegebenen Wertebereiche der elementaren Datentypen und ihrer Wrapper passen. Diese sind besonders wichtig, wenn man Rundungsfehler vermeiden will oder exakte Vorgaben hat, auf wie viele Stellen hinter dem Komma gerechnet oder nach welchen Vorgaben gerundet werden soll. Number {abstract}
BigInteger
Nitty Gritty • Take that!
6
+add(BigInteger) +subtract(BigInteger) +multiply(BigInteger) +divide(BigInteger) +compareTo(BigInteger) +intValue() +longValue() +doubleValue() +valueOf(long) +min/max(BigInteger) +pow(exp : int) ...
BigDecimal +ROUND_UP +ROUND_DOWN +ROUND_HALF_UP ...
+scale() +setScale(stellen : int, rundungsmodus : int) +add(BigDecimal) +subtract(BigDecimal) +multiply(BigDecimal) +divide(BigDecimal) +compareTo(BigDecimal) +floatValue() +doubleValue() +toBigInteger() +valueOf(long) +min/max(BigDecimal) +pow(exp : int) ...
Bild 6.12: Die Klassen BigInteger und BigDecimal
Objekte vom Typ BigInteger sind lange Ganzzahlen und BigDecimal Festkommazahlen. Die Klassen stellen die üblichen mathematischen Operationen bereit (siehe Abschnitt 6.1.5 bis 6.1.7). Zusätzlich können Sie bei BigDecimal-Objekten eine Genauigkeit (Stellen hinter dem Komma) und das Rundungsschema bei Rechenoperationen vorgeben. Dies ist besonders nützlich, wenn Sie mit Geldbeträgen rechnen, denn beispielsweise für die Umrechnung von Euro gibt es feste Vorgaben von sechs Stellen und dem Rundungsmodus, die sich aber von der Anzeige (zwei Stellen hinter dem Komma) unterscheiden. 158
Mathematische Klassen – java.math
7
Sandini Bib
Datenspeicherung und -kommunikation
In fast jeder Anwendung werden Daten ausgeben oder bearbeitet. Diese Daten müssen jedoch zunächst einmal aus irgendeiner Quelle geladen werden und sollen bei einer Veränderungen auch wieder gespeichert werden können. Java bietet in diesem Bereich eine breite Unterstützung von Dateien (Abschnitt 7.1), Datenbanken (Abschnitt 7.6), TCP/IP und Internet (Abschnitt 7.2) bis hin zur verteilten Objektkommunikation via RMI (Abschnitt 7.3), CORBA (Abschnitt 7.4) und Enterprise JavaBeans (Abschnitt 7.5).
Ein-/Ausgabe – java.io
Im Package java.io finden Sie Klassen für die Ein- und Ausgabe. Die wichtigsten davon sind File, DataInputStream (binäres Einlesen), FileInputStream (Einlesen aus einer Datei), analog DataOutputStream und FileOutputStream und schließlich PrintStream für zeichenbasierte Textausgaben. Diese Klassen ermöglichen es dem Programmierer, Daten für den Programmverlauf von Speichermedien einzulesen und während des Programms erarbeitete Daten zu speichern. Die Methoden print() und println() der Klasse PrintStream haben wir bei der Standardausgabe System.out bereits vielfach verwendet. Wie Sie in Abbildung 7.1 sehen, gibt es im Package eine große Menge weiterer Klassen, die wir im Folgenden aufgliedern.
Datenspeicherung und -kommunikation
159
7 Nitty Gritty • Take that!
7.1
Sandini Bib java.io Klassen
Interfaces - DataInput - ObjectInput - DataOutput - ObjectOutput - Serializable - Externalizable
Ausgabe
Eingabe - InputStream - FileInputStr. - ObjectInputStr. - FilterInputStr. - BufferedInputS. - DataInputStr. - Reader - BufferedReader - InputStreamReader - FileReader ...
Ausnahmebehandlung
Sonstige
- File - IOException - OutputStream - EOFException - Random- FileOutputStr. AccessFile - ObjectOutputS. - Interrupted- StringIOException - FilterOutputStr. - BufferedOutputS. - FileNotFound- Tokenizer Exception - DataOutputStr. - InvalidObject- PrintStream Exception - Writer - NotSerializable- BufferedWriter Exception - OutputStream... Writer - FileWriter - PrintWriter
Bild 7.1: Klassen im Package java.io
Mit J2SE 1.4 kam mit java.nio eine weitere Bibliothek zur Ein-/Ausgabe hinzu. Die Klassen darin dienen zum Direktzugriff auf einzelne Blöcke sehr großer Dateien auf einer Festplatte und sind eigentlich nur für die Programmierung von Datenbanken oder Ähnlichem interessant.
Nitty Gritty • Take that!
7
7.1.1 Streams Ein wichtiges Konzept für die Ein-/Ausgabe verkörpern die so genannten Streams – zu Deutsch: Ströme. Zum leichteren Verständnis kann man sich Streams ähnlich wie Röhren vorstellen, in denen eine Flüssigkeit – in diesem Falle die Daten – fließt. Nicht umsonst spricht man auch vom »Datenfluss«. Streams sind meist unidirektional, das heißt, die Daten können nur in eine Richtung fließen (siehe Abbildung 7.2). Datei Anwendung
OutputStream
Êþº¾ - o "Ljava/beans/PropertyChange Support; propertyChange v334 Ljava/awt/Color; fieldLineColor I fieldLineWidth (Ljava/awt/Color;)V r33 34, 434, j
Datei Êþº¾ - o "Ljava/beans/PropertyChange Support; propertyChange v334 Ljava/awt/Color; fieldLineColor I fieldLineWidth (Ljava/awt/Color;)V r33 34, 434, j
InputStream
Anwendung
Bild 7.2: Streams zwischen einer Anwendung und einer Datei 160
Ein-/Ausgabe – java.io
Sandini Bib
Datenquellen Streams in Java haben immer eine wohldefinierte Quelle. Schon beim Erzeugen eines Streams muss festgelegt werden, von wo die Daten bezogen werden sollen. Diese Datenquellen können sehr unterschiedlich sein. So kann man mit den passenden Streams zum Beispiel aus Dateien oder URLs (Uniform Resource Locator, z. B. http://www.nitty-gritty.de/java/streams.txt oder file:///c:/NGJavaBuch/streams.txt) lesen oder auch über Socket-Verbindungen Informationen mit anderen Rechnern austauschen. Außerdem können passende Streams auch verkettet werden. So lassen sich zum Beispiel Puffer oder Filter verwirklichen (siehe Abbildung 7.3). In der Grafik sind die Streams durch Pfeile dargestellt, die die Richtung des Datenflusses andeuten. Datei Êþº¾ - o "Ljava/beans/PropertyChange Support; propertyChange v334 Ljava/awt/Color; fieldLineColor I fieldLineWidth (Ljava/awt/Color;)V r33 34, 434, j
FileInputStream
Anwendung
Datei
7 FileInputStream
DataInputStream
Anwendung
BufferedInputStream
Anwendung
URL InputStream
Bild 7.3: Verkettung von Streams
Streamarten In den Abschnitten 7.1.3 bis 7.1.10 werden die einzelnen Typen von Streams erläutert. Welche Art für welchen Zweck am besten geeignet ist, sehen Sie in der Gegenüberstellung in Abschnitt 7.1.11.
Datenspeicherung und -kommunikation
161
Nitty Gritty • Take that!
Êþº¾ - o "Ljava/beans/PropertyChange Support; propertyChange v334 Ljava/awt/Color; fieldLineColor I fieldLineWidth (Ljava/awt/Color;)V r33 34, 434, j
Sandini Bib
7.1.2
Die Klasse File
seit
SE
ME
EE
1.0
x
-
x
Die Plattformunabhängigkeit von Java stößt natürlich schnell an ihre Grenzen, wenn es um das Dateisystem einer bestimmten Plattform geht. So wird in der Unix-Welt der Pfad einer Textdatei zum Beispiel wie folgt angegeben: /home/username/NGJavaBuch/streams.txt
während unter Windows die gleiche Datei vielleicht so lokalisiert würde: z:\NGJavaBuch\streams.txt File +separator : String
Nitty Gritty • Take that!
7
<> +File(pfad : String) +File(pfad : String, dateiname : String) +File(verz : File, dateiname : String) <<Methoden>> +exists() +isDirectory() / isFile() +getName() / getPath() +canRead() / canWrite() +delete() +length() +lastModified() +list() +mkDir() +getParent() +renameTo(File) ...
Bild 7.4: Die Klasse File
Methoden Um diese Plattformabhängigkeit zu umgehen, bedient man sich in Java der Klasse File. Sie stellt eine plattformunabhängige Schnittstelle zu betriebssystemspezifischen Funktionen dar. So bietet sie unter anderem Methoden wie exists() zur Feststellung der Existenz einer Datei und canRead(), canWrite(), length() und lastModified(), anhand derer das jeweilige Attribut ermittelt werden kann. Die Namen der Methoden renameTo() und delete() sprechen ebenfalls für sich. 162
Ein-/Ausgabe – java.io
Sandini Bib
Beispiel // Prüfung vor dem Lesen aus einer Datei if ( (datei != null) && (datei.exists()) && (datei.canRead()) ) [...] // sicheres Lesen aus der Datei
Verzeichnisse Ein File-Objekt kann sowohl eine Datei im herkömmlichen Sinne als auch ein Verzeichnis repräsentieren. Auskunft hierüber geben die Methoden isDirectory() bzw. isFile(). Handelt es sich bei dem Objekt um ein Verzeichnis, liefert die Methode list() ein String-Array mit den Namen aller enthaltenen Dateien. Aufrufe der Methode mkdir() erzeugen Unterverzeichnisse mit dem das Objekt repräsentierenden Verzeichnisnamen. So lässt sich zum Beispiel sehr schnell eine einfache Applikation schreiben, die ähnlich wie der dir-Befehl unter Windows den Inhalt des aktuellen Verzeichnisses ausgibt: Beispiel Ein kurzes Programm zur Ausgabe von Verzeichnisinhalten:
7
Datenspeicherung und -kommunikation
Nitty Gritty • Take that!
import java.io.*; public class Dir { public static void main (String[] args) { String filesList[]; // aktuelles Verzeichnis ermitteln: File directory = new File( System.getProperty("user.dir")); // enthaltene Dateien und Verzeichnisse: filesList = directory.list(); for (int i=0; i < filesList.length; i++) { File curFile = new File (directory, filesList[i]); // formatierter Zeitstempel, // hier als Quick and Dirty-Lösung: String timestamp = (new java.util.Date( curFile.lastModified())).toLocaleString();
163
Sandini Bib if (curFile.isDirectory()) System.out.println(timestamp + "\t \t\t" + filesList[i]); else System.out.println(timestamp + "\t\t" + curFile.length() + "\t" + filesList[i]); } } }
Das Programm erzeugt ein Objekt vom Typ File. System.getProperty("user.dir") liefert den Pfad des aktuellen Verzeichnisses. Zur optischen Unterscheidung von Verzeichnissen und »normalen« Dateien werden in der for-Schleife Verzeichnisse mit dem Zusatz gekennzeichnet. Die Ausgabe könnte lauten: Beispiel
Nitty Gritty • Take that!
7
d:\java> java Dir 24.04.2000 12:19:29 20.06.1999 14:29:54 20.08.1998 11:34:04 22.02.1999 09:57:18 26.04.1999 13:12:22 26.04.1999 13:07:04
NGJavaBuch VAJBuch 379781 vajtips.zip websphere 752 Zaehler1.class 424 Zaehler1.java
Die Formatierung des Zeitstempels ist aus zwei Gründen eine Quick-und-Dirty-Implementierung: 1. Es ist nicht garantiert, dass der Rückgabewert von lastModified() vom Typ long auf jeder Plattform das Format besitzt, das der Konstruktor der Date-Klasse verlangt. Unter Windows zum Beispiel ist dies jedoch der Fall. 2. Die Methode toLocaleString() der Klasse Date ist deprecated, das bedeutet, sie sollte nicht mehr benutzt werden, da es einen besseren Ersatz gibt. Der Ersatz wäre in diesem Fall die Klasse java.text.DateFormat. Die Formulierung dieses kleinen Beispiels wäre damit etwas unübersichtlicher geworden. 164
Ein-/Ausgabe – java.io
Sandini Bib
Pfade Wie schon eingangs erwähnt, werden Pfade bei verschiedenen Betriebssystemen unterschiedlich repräsentiert. Insbesondere gibt es unterschiedliche Trennzeichen. Um einen Pfad plattformunabhängig darzustellen, können Sie die Konstante File.separator benutzen, also z. B. pfad + File.separator + dateiname statt pfad + "/" + dateiname (UNIX) bzw. pfad + "\\" + dateiname (Windows). Die Methode getPath() liefert den kompletten Pfad zurück, das bedeutet bei Dateien unter Einschluss des Dateinamens. getParent() ergibt bei Dateien nur den Pfad, bei Verzeichnissen das übergeordnete Verzeichnis. Beispiel File datei = new File("z:\\NGJavaBuch\\streams.txt"); String pfad1 = datei.getPath(); // liefert "z:\\NGJavaBuch\\streams.txt" String pfad2 = datei.getParent(); // liefert "z:\\NGJavaBuch\\" File verz = new File("z:\\demo"); String pfad3 = verz.getParent(); // liefert "z:\\"
Lesen aus Dateien – FileInputStream
seit
SE
ME
EE
1.0
x
-
x
Die Klasse File bietet nicht die Möglichkeit, die referenzierte Datei auszulesen oder in sie zu schreiben. Aber auch diese Aufgabe stellt in Java kein großes Hindernis dar. Zum Einlesen von Dateien existiert die Klasse FileInputStream (Abbildung 7.5).
Datenspeicherung und -kommunikation
165
Nitty Gritty • Take that!
7.1.3
7
Sandini Bib
FileInputStream
<> +FileInputStream(datei: File) +FileInputStream(datei: String) <<Methoden>> +read() +read(buffer: byte[]) +read(buffer: byte[], offset: int, länge: int) +close() +available() +skip(anz: long) ...
Bild 7.5: Die Klasse FileInputStream
Erzeugung Eine Instanz von FileInputStream wird üblicherweise mit einem Objekt vom Typ File als Argument erzeugt. Ein Konstruktoraufruf hierzu lautet also zum Beispiel: Beispiel
Nitty Gritty • Take that!
7
File datei; ... // "datei" gültiges Objekt zuweisen FileInputStream fiStream = new FileInputStream(datei);
Wahlweise kann der Konstruktor von FileInputStream aber auch mit einem String-Objekt aufgerufen werden, der den Dateinamen enthält. Dieser Ausschnitt ist so nicht funktionsfähig, da der Konstruktor eine FileNotFoundException erzeugen könnte und eine Behandlung mittels throws erzwingt (siehe Abschnitt 11.3.4). Mit IOException und davon abgeleiteten Klassen werden wir uns in Abschnitt 7.1.13 beschäftigen. Die korrekte Syntax – diesmal für den anderen Konstruktor – lautet: try { FileInputStream fiStream = new FileInputStream("streams.txt"); }
166
Ein-/Ausgabe – java.io
Sandini Bib catch (FileNotFoundException e) { System.out.println("Die Datei \"streams.txt\" wurde" + + " im aktuellen Verzeichnis nicht" + + " gefunden!"); }
Bei Applets – oder in Java 2 möglicherweise auch bei Anwendungen – kann auch eine SecurityException auftreten, wenn nicht mindestens eine Leseberechtigung besteht. Methoden
FilterStreams Die Klasse FileInputStream bietet nur Zugriffe auf die einzelnen Bytes einer Datei, kann aber mit beliebigen FilterStreams kombiniert werden. Ein FilterStream setzt auf einem anderen Stream auf und fügt zusätzliche Funktionalität hinzu. FilterStreams werden zum Beispiel von den Klassen BufferedInputStream (mit Lesepuffer), DataInputStream (zeilenweises und datentyp-orientiertes Lesen) und LineNumberInputStream (merkt sich die Zeilennummern) repräsentiert. Ihre Konstruktoren werden mit dem InputStream aufgerufen, auf den sie aufsetzen. So wäre folgende Schachtelung von Streams denkbar, um eine Datei zeilenweise einzulesen und dabei die Zeilennummern anzuzeigen:
Datenspeicherung und -kommunikation
167
7 Nitty Gritty • Take that!
Mit der Methode read() kann man nun aus dieser Datei einzelne Bytes bzw. Gruppen von Bytes in ein Array einlesen. Der Rückgabewert -1 symbolisiert das Dateiende. Falls das Dateiende noch nicht erreicht ist, aber aus dem Stream momentan keine Bytes zur Verfügung stehen, blockiert die Methode, das heißt sie wartet, bis wieder weitergelesen werden kann – unter Umständen unendlich lang. Deshalb kann man mit available() die Anzahl der Bytes abfragen, die ohne Blockieren gelesen werden können. Mit skip(n) überspringt man n Bytes und close() schließt den Stream. Das Schließen sollte man niemals vergessen, sonst bleibt die Datei bis zur Beendigung der virtuellen Maschine oder dem Löschen des Streams durch den Garbage Collector geöffnet.
Sandini Bib
Datei Êþº¾ - o "Ljava/beans/PropertyChange Support; propertyChange v334 Ljava/awt/Color; fieldLineColor I fieldLineWidth (Ljava/awt/Color;)V r33 34, 434, j
FileInputStream
LineNumber- DataInputInputStream Stream
Anwendung
Bild 7.6: FilterStreams
Beispiel
Nitty Gritty • Take that!
7
FileInputStream fiStream; LineNumberInputStream lniStream; DataInputStream diStream; fiStream = new FileInputStream("streams.txt"); lniStream = new LineNumberInputStream(fiStream); diStream = new DataInputStream (lniStream); String zeile; while ( (zeile = diStream.readLine()) != null ) { int ln = lniStream.getLineNumber(); System.out.println(""+ ln + ": " + zeile); } fiStream.close(); lniStream.close(); diStream.close();
7.1.4
Die Klassen Reader und BufferedReader
seit
SE
ME
EE
1.1
x
(x)
x
Unicode-Zeichen Die bisherigen Streams lesen byteweise aus Dateien, beispielsweise hat man sehr viele Altdateien im ASCII-Format, das pro Zeichen ein Byte benutzt. Java und moderne Betriebssysteme verwenden zur (internationalen) Zeichendarstellung jedoch Unicode mit zwei Bytes pro Zeichen. Für viele der beschriebenen Klassen gibt es auch eine Unicode-Variante, die als Reader bezeichnet wird (z. B. FileReader, FilterReader, LineNumberReader, BufferedReader ...). Sollen etwa die aus einem InputStream bezogenen Daten als Unicode-Zeichen vom Typ 168
Ein-/Ausgabe – java.io
Sandini Bib
char interpretiert werden, so verwendet man ein Objekt vom Typ InputStreamReader. Der Konstruktoraufruf von InputStreamReader hat eine Referenz auf InputStream als Argument, aus dem die Daten bezogen werden: InputStreamReader diStream = new DataInputStream (lniStream); Reader {abstract} +read() +read(buffer : char[]) +read(buffer: byte[], offset: int, länge: int) +close() +ready() +skip(anz : long) +mark(int) +reset() ...
Bild 7.7: Die Klasse Reader
Lesepuffer
Reader {abstract}
BufferedReader
<> +BufferedReader(in : Reader) +BufferedReader(in : Reader, bufsize : int) <<Methoden>> +readLine() +read() {geerbt} +read(char[]) {geerbt} +read(byte[], int, int) {geerbt} +close() {geerbt} ...
Bild 7.8: Die Klasse BufferedReader
Datenspeicherung und -kommunikation
169
7 Nitty Gritty • Take that!
Soll der Datenfluss noch gepuffert werden, da die Daten aus einer langsamen oder unregelmäßig schnellen Datenquelle wie einer Datei oder einem Netzwerk stammen, empfiehlt sich zusätzlich der Einsatz eines BufferedReader. Dieser setzt auf einen Reader auf und muss deshalb mit einem solchen als Konstruktorargument erzeugt werden.
Sandini Bib
Das folgende Programm gibt – ähnlich dem type-Befehl unter DOS – den Inhalt einer Textdatei auf der Standardkonsole aus: Beispiel
Nitty Gritty • Take that!
7
import java.io.*; public class Type { public static void main(String[] args) { File datei; String zeile; FileInputStream fiStream; InputStreamReader isReader; BufferedReader bufReader; if (args.length > 0) datei = new File(args[0]); else { System.out.println("Usage: Type "); return; } try { fiStream = new FileInputStream(datei); isReader = new InputStreamReader(fiStream); bufReader = new BufferedReader(isReader); zeile = bufReader.readLine(); while (zeile != null) { System.out.println(zeile); zeile = bufReader.readLine(); } } catch (FileNotFoundException e) { System.out.println("Die Datei " + datei.toString() + " konnte " + "nicht gefunden werden!"); }
170
Ein-/Ausgabe – java.io
Sandini Bib catch (IOException e) { System.out.println("IOException!"); } } }
In der Micro Edition gibt es nur die Klasse Reader und den davon abgeleiteten InputStreamReader. 7.1.5
Serialisierung – ObjectInput-/OutputStream
Bisher haben wir Zeichen oder Zeichenketten eingelesen. Mit Java ist es aber auch möglich, Java-Objekte mit all ihren Attributen zu schreiben und zu lesen. Wird in einer Instanzvariablen ein anderes Objekt referenziert, wird auch dieses gespeichert. Voraussetzung für die Speicherung von Objekten (Serialisierung) ist die Implementierung des Serializable-Interfaces. Das Interface Serializable SE
ME
EE
1.1
x
-
x
«interface» Serializable
Bild 7.9: Das Interface Serializable
Serialisierbare Objekte können in Dateien gespeichert oder über Netzwerke übertragen werden. Dazu müssen sie in ein Byteformat umgewandelt werden können. Das Markierungsinterface Serializable kennzeichnet eine Klasse als serialisierbar. Ähnlich wie bei Cloneable (Abschnitt 6.1.2) sind dazu keine Methoden zu implementieren, es müssen aber auch alle referenzierten Klassen serialisierbar sein. Falls dies nicht der Fall ist, wird eine NotSerializableException erzeugt. Falls man nicht den Standard-Serialisierungsmechanismus benutzen möchte, kann man die Methoden writeObject() und readObject() überschreiben. Datenspeicherung und -kommunikation
171
Nitty Gritty • Take that!
7
seit
Sandini Bib
Die Klasse ObjectInputStream seit
SE
ME
EE
1.1
x
-
x
InputStream {abstract}
ObjectInputStream
<> +ObjectInputStream(in : InputStream) <<Methoden>> +readObject() +readLine() +readByte/Short/Int/Long() +readFloat/Double() +readChar() +readBoolean() +read() {geerbt} +read(char[]) {geerbt} +read(byte[], int, int) {geerbt} +close() {geerbt} ...
Nitty Gritty • Take that!
7
Bild 7.10: Die Klasse ObjectInputStream
ObjectInputStream hat als Konstruktorparameter einen InputStream. Ein serialisiertes Objekt wird mittels der Methode readObject() eingelesen und dann mittels Casting auf einen geeigneten Typ konvertiert. Daneben bietet die Klasse auch Methoden zum Lesen der primitiven Datentypen. Folgender Code-Ausschnitt liest zunächst die Anzahl und anschließend eine Reihe von Datumsobjekten der Klasse Date aus einer Datei. Beispiel FileInputStream fiStream = new FileInputStream(datei); ObjectInputStream oinStream = new ObjectInputStream(fiStream); if (oinStream != null) // Datei gefunden { // Anzahl der Objekte einlesen und Array anlegen: int anz = oinStream.readInt(); Date[] datum = new Date[anz];
172
Ein-/Ausgabe – java.io
Sandini Bib for (int i=0; i
Der Einfachheit halber wurde das Exception Handling weggelassen (siehe Kapitel 11). Beim Lesen von Objekten sind noch weitere Exceptions möglich, zum Beispiel wird eine ClassNotFoundException erzeugt, wenn ein Objekt von einem Typ eingelesen wird, dessen Klassendefinition nicht gefunden werden kann. Die Klasse ObjectOutputStream SE
ME
EE
1.1
x
-
x
Daten und Objekte, die eingelesen werden können, müssen erst einmal geschrieben werden. Dazu gibt es eine Reihe von OutputStreamKlassen, die zu den InputStreams korrespondieren. Zum Schreiben von Objekten benutzt man den ObjectOutputStream. OutputStream {abstract}
ObjectOutputStream
<> +ObjectOutputStream(out : OutputStream) <<Methoden>> +writeObject(Object) +writeBytes/Chars( String) +writeByte/Short/Int/Long() +writeFloat/Double() +writeChar(int) +writeBoolean(boolean) +write(data: int) {geerbt} +write(byte[]) {geerbt} +write(byte[], int, int) {geerbt} +close() {geerbt} {geerbt} +flush() ...
Bild 7.11: Die Klasse ObjectOutputStream
Datenspeicherung und -kommunikation
173
7 Nitty Gritty • Take that!
seit
Sandini Bib
Methoden Dieser Stream setzt je nach Ziel auf einem anderen OutputStream auf. Nach der Instanziierung können Sie mit den verschiedenen write-Methoden unterschiedliche (serialisierbare) Datentypen schreiben. Strings können Sie mit writeBytes() oder writeChars() schreiben, je nachdem, welche Darstellungsart Sie wünschen. Die Methode writeObject() nimmt ein beliebiges serialisierbares Objekt entgegen, wandelt es in eine Byte-Darstellung um und schreibt es in den OutputStream. Wichtig ist der Aufruf der Methode flush() nach dem Schreiben der Daten, um den Schreibpuffer zu leeren, und close(), wenn Sie alle Schreiboperationen abgeschlossen haben. Beispiel Hier das zentrale Code-Stück, um die im vorigen Beispiel eingelesenen Daten im selben Format zu speichern.
Nitty Gritty • Take that!
7
ObjectOutputStream ooutStream = new ObjectOutputStream(aFileOutStream); [...] ooutStream.writeInt(datum.length); for (int i = 0; i < datum.length; i++) { ooutStream.writeObject(datum[i]); } ooutStream.flush(); ooutStream.close();
1. ObjectIn-/OutputStreams setzen auf beliebigen In-/ OutputStreams auf. Serialisierte Objekt können damit auch über Netzwerke übertragen werden. In diesem Falle ersetzt man die FileIn-/OutputStreams durch SocketStreams (siehe Abschnitt 7.2.2). 2. Bei der Serialisierung von Objekten ist Vorsicht geboten. Zum einen ist darauf zu achten, dass der Objektbestand konsistent bleibt, das heißt, es sollten zu keinem kritischen Zeitpunkt mehrere Kopien des gleichen Objekts vorhanden sein. Zum anderen gilt es, sich
174
Ein-/Ausgabe – java.io
Sandini Bib
Gedanken darüber zu machen, was mit Objekten geschieht, die von einem zu serialisierenden Objekt referenziert werden. Im Normalfall werden diese Objekte ebenfalls serialisiert. 3. Attribute, die nicht mit einem Objekt serialisiert werden sollen, können mit dem Modifier transient versehen werden. 4. Soll das Verhalten eines Objekts bei der Serialisierung anders implementiert werden, so sind folgende zwei Methoden zu erzeugen: private void writeObject(ObjectOutputStream out) throws IOException private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
Schreiben in Dateien – FileOutputStream
seit
SE
ME
EE
1.0
x
-
x
7
Mit einer High-Level-Klasse ist zwar ein Schreiben komplexer Datentypen möglich, dieser muss jedoch auf einem Low-Level-Stream aufsetzen, der ein byteweises Schreiben in ein bestimmtes Ziel erlaubt, zum Beispiel in eine Datei oder über eine Pipe zu einem anderen Rechner. Zum Erzeugen von Dateien und um darauf schreibend zuzugreifen, bedient man sich der Klasse FileOutputStream. OutputStream {abstract}
FileOutputStream
<> +FileOutputStream(datei : File) +FileOutputStream(datei : String) +FileOutputStream(datei : String, append : boolean) <<Methoden>> +write(data: int) +write(buffer: byte[]) +write(buffer: byte[], offset: int, länge: int) +close() +flush() ...
Bild 7.12: Die Klasse FileOutputStream Datenspeicherung und -kommunikation
175
Nitty Gritty • Take that!
7.1.6
Sandini Bib
Dem Konstruktor gibt man die Zieldatei per Dateinamen oder als File-Objekt an. Existiert diese Datei noch nicht, so wird sie beim ersten Zugriff angelegt. Gibt es die Datei bereits, so wird ihr Inhalt beim ersten Zugriff gelöscht und neu geschrieben. Beim Arbeiten mit FileOutputStream ist deshalb äußerste Vorsicht angebracht! Es gibt zwei Möglichkeiten, ein Überschreiben zu verhindern:
7
T
Wenn Sie die neuen Daten am Ende einer bestehenden Datei anfügen möchten, können Sie einen Konstruktor benutzen, der den Anfügemodus (append) als boolesches Flag angibt.
T
Wenn Sie nur prüfen wollen, ob die Datei bereits existiert, erzeugen Sie zunächst ein Objekt der Klasse File (Abschnitt 7.1.2) und fragen Sie mit exists() nach der Datei. Danach können Sie den Konstruktor von FileOutputStream benutzen, der das File-Objekt als Parameter besitzt.
Nitty Gritty • Take that!
Beispiel Im vorhergehenden Beispiel benutzen wir einen ObjectOutputStream zum Schreiben. Dieser ist allein jedoch nicht funktionsfähig, da er nur auf einem anderen OutputStream aufsetzt. Wenn in eine Datei geschrieben werden soll, ist dies typischerweise ein FileOutputStream: File neueDatei = new File("daten.dat"); if (neueDatei.exists()) System.out.println("Die Datei existiert bereits."); else { FileOutputStream aFileOutStream = new FileOutputStream(neueDatei); ObjectOutputStream ooutStream = new ObjectOutputStream(aFileOutStream); ... // Schreiben der Daten wie im obigen Beispiel aFileOutStream.close(); }
176
Ein-/Ausgabe – java.io
Sandini Bib
Schließen von Streams Während FileInputStream-Objekte beim Verlassen des Gültigkeitsbereiches, in dem sie definiert wurden, ohne Probleme von der Garbage Collection gelöscht werden können, muss bei OutputStream-Objekten (vor allem bei gepufferten) darauf geachtet werden, dass diese gültig geschlossen werden, was eine flush-Anweisung zur Leerung des Puffers beinhaltet. Die Methode hierfür lautet close(); sie beinhaltet implizit den Aufruf von flush(). Eventuell noch in der Kette der Streams befindliche Daten könnten sonst verloren gehen, wenn das Objekt gelöscht wird. Die Klassen OutputStream und PrintStream
seit
SE
ME
EE
1.0
x
x
x
Mit FileOutputStream-Objekten arbeitet man nur auf der Byte-Ebene. Analog zu den InputStream-Objekten gibt es aber auch für die Ausgabe High-Level-Klassen wie zum Beispiel den bereits behandelten ObjectOutputStream (Abschnitt 7.1.5), den gepufferten BufferedOutputStream oder den DataOutputStream, der die elementaren Datentypen sowie Strings unterstützt (siehe Abbildung 7.13). Alle OutputStreams basieren auf der gemeinsamen abstrakten Basisklasse OutputStream, die Operationen zum Schreiben von einzelnen Bytes, zur Leerung des Puffers (flush()) und Schließen des Streams bereitstellt. OutputStream {abstract}
FileOutputStream
BufferedOutputStream
FilterOutputStream
ObjectOutputStream
DataOutputStream
PrintStream
Bild 7.13: OutputStream-Hierarchie Datenspeicherung und -kommunikation
177
7 Nitty Gritty • Take that!
7.1.7
Sandini Bib
Die Klasse PrintStream Die bisherigen Klassen bieten zwar schon die Möglichkeit, verschiedene Datentypen zu schreiben, aber vermutlich fehlt Ihnen noch eine polymorphe Klasse mit einer Methode, der Sie ein Objekt jeden beliebigen Datentyps übergeben können. OutputStream {abstract}
FilterOutputStream
PrintStream
<> +PrintStream(out : OutputStream) +PrintStream(out : OutputStream, autoflush: boolean) <<Methoden>> +print(...) +println(...) ...
7
Bild 7.14: Die Klasse PrintStream
Nitty Gritty • Take that!
Methoden Dieses leistet die Klasse PrintStream mit den Methoden print() und println(), die für die elementaren Datentypen ebenso definiert ist wie für Referenztypen. Wenn Sie ein beliebiges Objekt übergeben, ruft diese Methode die toString-Methode des Objekts auf (siehe 6.1.1) und gibt diese String-Repräsentation des Objekts aus. println() führt nach der Ausgabe noch einen Zeilenumbruch durch. Mit dem Parameter autoflush in einem der Konstruktoren können Sie ein automatisches Herausschreiben nach jedem Methodenaufruf erzwingen. System.out Sicherlich sind Ihnen die Methoden print() und println() bekannt vorgekommen, wir haben sie ja bereits häufig für das Schreiben auf die Standardausgabe benutzt. Tatsächlich ist die Klassenvariable out der Klasse System vom Typ PrintStream.
178
Ein-/Ausgabe – java.io
Sandini Bib
Die Klasse PrintStream sollte jedoch in eigenen Programmen nicht benutzt werden, denn seit JDK 1.1 hat Sun diese Klasse bzw. deren Konstruktoren als deprecated erklärt. Stattdessen sollte man die Klasse PrintWriter verwenden, die eine identische Funktionalität bietet. 7.1.8
Schreiben mit Writer-Klassen
seit
SE
ME
EE
1.1
x
x
x
Will man 16-bit-codierte Unicode-Character-Daten ausgeben, so bedient man sich einer Referenz vom Typ Writer oder einer von ihr abgeleiteten Klasse. Die Writer-Klasse funktioniert ebenso wie die ReaderKlasse, nur im umgekehrten Sinn: Ein Writer-Objekt setzt auf einem OutputStream-Objekt auf, das wie immer beim Erzeugen der Instanz angegeben werden muss. Writer {abstract}
Bild 7.15: Die Klasse Writer
Analog zu den OutputStream-Klassen gibt es auch hier wieder eine Reihe von High-Level-Klassen (siehe Abbildung 7.16), die von Writer abgeleitet sind. Writer {abstract}
BufferedWriter
OutputStreamWriter
PrintWriter
StringStream
FileWriter
Bild 7.16: Writer-Hierarchie Datenspeicherung und -kommunikation
179
Nitty Gritty • Take that!
7
+write(data : int) +write(buffer: char[]) +write(buffer: byte[], offset: int, länge: int) +write(buffer: String) +write(buffer: String, offset: int, länge: int) +close() +flush() ...
Sandini Bib
7.1.9
Die Interfaces DataInput und DataOutput
seit
SE
ME
EE
1.0
x
x
x
Das Interface DataInput fordert Methoden zum Lesen der elementaren Datentypen (Abschnitt 4.8.2), DataOutput analog zum Schreiben. Die einzelnen Methoden können Sie bei den Klassen ObjectInput- bzw. ObjectOutputStream nachlesen (Abschnitt 7.1.5), die diese Interfaces implementieren. Ein weiteres implementierendes Klassenpärchen ist DataIn-/OutputStream; diese beherrschen allerdings nur eine Teilmenge der Funktionen des ObjectStreams. Deren Einsatz ist deshalb nur dann sinnvoll, wenn es Letztere in der aktuellen Laufzeitumgebung nicht gibt (z. B. in der Micro Edition). «interface» DataInput
Nitty Gritty • Take that!
7
DataInputStream
ObjectInputStream
«interface» DataOutput
RandomAccessFile
DataOutputStream
ObjectOutputStream
Bild 7.17: Die Interface DataInput und DataOutput
Die Klasse RandomAccessFile kombiniert Lese-/Schreib und Dateioperationen in einem und wirkt daher etwas überladen. Sie wird in Abschnitt 7.1.12 näher beschrieben. 7.1.10
Die Klassen FileReader und FileWriter
seit
SE
ME
EE
1.1
x
x
x
FileReader und FileWriter sind die zu FileInputStream (Abschnitt 7.1.3) und FileOutputStream (Abschnitt 7.1.6) analogen Klassen, die jedoch wie alle Reader/Writer 16-bit-Unicode-Zeichen verarbeiten.
180
Ein-/Ausgabe – java.io
Sandini Bib
Die Klasse FileReader Die Klasse FileReader bringt keine zusätzliche Funktionalität gegenüber ihrem byte-orientierten Gegenstück FileInputStream. Da man aber im Gegensatz zu den anderen Reader-Klassen nicht auf einem anderen Reader aufsetzen muss, eignet sie sich besser als Low-LevelStream für Unicode-Ströme (siehe Abschnitt 7.1.11). Die Klasse FileWriter Analog trifft dies auf die FileWriter-Klasse zu, aber im Gegensatz zu den OutputStreams kann man damit direkt Strings (neben Zeichen vom Typ char) in eine Datei schreiben und spart sich dadurch eine Stream-Verschachtelung (siehe Abschnitt 7.1.11). 7.1.11
Schachtelung und Zusammenfassung
Low-Level und High-Level
Datenspeicherung und -kommunikation
181
7 Nitty Gritty • Take that!
Wir haben nun eine Vielzahl von Klassen kennen gelernt, die man zur Laufzeit üblicherweise auch noch ineinander verschachtelt. Um deren Zusammenwirken zu verstehen, kann man sich den Zugriff als zweistufigen Prozess vorstellen. Ein Low-Level-Stream wird für das byte- oder zeichenweise Lesen aus einer spezifischen Quelle oder das Schreiben in ein bestimmtes Ziel ausgewählt. Zum Beispiel kann dies eine Datei sein oder auch ein TCP/IP-Socket (siehe Abschnitt 7.2.2). Darauf setzt ein High-Level-Stream auf, der mit Java-Datentypen umgehen kann und zum Beispiel Zeilen als Strings einliest oder sogar komplexe serialisierte Java-Objekte. Will man nun ein Programm ändern und einen Zugriff über das Netz erlauben, statt auf die lokale Festplatte zuzugreifen, braucht man an der Anwendungslogik nichts zu ändern, sondern nur den Low-Level-Stream auszutauschen. Ist die Quelle oder das Ziel nicht zuverlässig oder schnell genug, kann man noch einen Puffer dazwischenschalten.
Sandini Bib
Zusammenfassung Aufgabe
Typ
Eingabe
Ausgabe
Zugriff auf Dateien
LowLevel
FileInputStream/ FileReader
FileOutputStream/ FileWriter
Zugriff auf Pipes/Threads
LowLevel
PipedInputStream/ Reader
PipedOutputStream/Writer
Daten elementaren Datentyps lesen/ schreiben
HighLevel
DataInputStream
DataOutputStream
Objekte lesen/ schreiben
HighLevel
ObjectInputStream
ObjectOutputStream
beliebige Typen auf Textausgabe schreiben
HighLevel
–
PrintStream
Streams puffern
Filter
BufferedInputStream/ BufferedReader
BufferedOutputStream/ BufferedWriter
Zeilennummern merken
Filter
LineNumberInputStream/ LineNumberReader
–
Read-ahead (Vorauslesen mit Zurückstellen)
Filter
PushbackInputStream/ PushbackReader
–
Nitty Gritty • Take that!
7
Ob Sie jeweils die Reader/Writer oder Input-/OutputStream-Version einsetzen sollen, hängt davon ab, ob Sie Unicode-Zeichen lesen oder schreiben möchten oder Bytes. Streamklassen in anderen Packages Neben diesen Klassen im Package java.io gibt es noch weitere Streamklassen, die thematisch in andere Packages sortiert sind:
182
Ein-/Ausgabe – java.io
Sandini Bib
Typ
Eingabe
Ausgabe
Zugriff auf TCP-Sockets
Low-Level
java.net.SocketInputStream
java.net. SocketOutputStream
Prüfsumme berechnen
Filter
java.util.zip.CheckedInputStream
java.util.zip. CheckedOutputStream
Datenkompression
Filter
java.util.zip.ZipInputStream
java.util.zip. ZipOutputStream
Java-Archive lesen/schreiben
Filter
java.util.jar.JarInputStream
java.util.jar.Jar OutputStream
HTML schreiben
High-Level
(via Parser)
javax.swing.te xt.html.HTMLWriter
Audiodaten lesen/schreiben
High-Level
javax.sound.sampled.spi.AudioFileReader, javax.sound.sampled.AudioInputStream, javax.sound.midi. spi.MidiFileReader,
javax.sound. sampled.spi. AudioFileWriter, javax.sound. midi.spi.MidiFileWriter,
7.1.12 Parsen mit RandomAccessFile und StreamTokenizer Mit folgenden Klassen lassen sich strukturierte Dateien lesen und schreiben. Mit einem Tokenizer kann man eine Datei in ihre syntaktischen Bestandteile zurückübersetzen. Solche Strukturen weisen zum Beispiel HTML- oder XML-Dateien oder auch C- und Java-Sourcen auf. Die Klasse RandomAccessFile seit
SE
ME
EE
1.0
x
-
x
C-Programmierer werden wahrscheinlich den direkten Zugriff auf bestimmte Positionen in Dateien vermissen. Die Lösung in Java hierfür Datenspeicherung und -kommunikation
183
7 Nitty Gritty • Take that!
Aufgabe
Sandini Bib
bietet die RandomAccessFile-Klasse. Diese Klasse implementiert sowohl das Interface DataInput als auch DataOutput (Abschnitt 7.1.9) mit eigenen Methoden. RandomAccessFile
7
<> +RandomAccessFile(datei: String, zugriffsmodus: String) +RandomAccessFile(datei: File, zugriffsmodus: String) <<Methoden>> +seek(pos: long) +getFilePointer() +length() +close() +readLine() +readByte/Short/Int/Long/Float/Double() +readChar/Boolean() +read() +read(buffer: byte[], offset: int, länge: int) +writeBytes/Chars(s: String) +writeByte/Short/Int/Long/Float/Double(...) +writeChar(v: int) +writeBoolean(b: boolean) +write(data: int) +write(buffer: byte[]) +write(buffer: byte[], offset: int, länge: int) ...
Nitty Gritty • Take that!
Bild 7.18: Die Klasse RandomAccessFile
Methoden Instanziiert wird RandomAccessFile entweder mit dem Konstruktor RandomAccessFile (String datei, String zugriffsmodus),
oder mit RandomAccessFile (File datei, String zugriffsmodus).
Der zweite Parameter bestimmt die Art des Dateizugriffs. Hat der zweite Parameter den Wert »r«, so wird die Datei nur zum Lesen geöffnet, mit »rw« zum Lesen und Schreiben. Die Methode seek(long) setzt den so genannten FilePointer, eine Art Lese-/Schreib-Offset, an die angegebene Position. Von hier an wird bei Aufruf einer der Methoden write() oder read() (in sämtlichen Varianten) gelesen oder geschrieben. getFilePointer() gibt die aktuelle Position des FilePointers zurück.
184
Ein-/Ausgabe – java.io
Sandini Bib
Die Klasse StreamTokenizer seit
SE
ME
EE
1.0
x
-
x
Ebenfalls für das Parsen von strukturierten Dateien eignet sich die Klasse StreamTokenizer. Ein StreamTokenizer-Objekt setzt auf einem InputStream-Objekt oder einem Reader-Objekt auf und bietet dem Programmierer die Möglichkeit, die gelesenen Daten in einzelnen Teilen, so genannten Tokens, zu lesen. Eine Menge von bestimmten char-Werten wird beim Lesen des Streams als Trennzeichen interpretiert. StreamTokenizer +sval : String +nval : double
<> +StreamTokenizer(in: InputStream) +StreamTokenizer(in: Reader) <<Methoden>> +nextToken() +toString() +lineno() +pushBack() +ordinaryChar(ch: int) +ordinaryChars(low: int, hi: int) +whitespaceChars(low: int, hi: int) +commentChar(ch: int) +quoteChar(ch: int) +wordChars(low: int, hi: int) +lowerCaseMode(b: boolean) ...
Bild 7.19: Die Klassen StreamTokenizer
Trennzeichen Die Methode whitespaceChars(int, int) ermöglicht die Definition von Trennzeichen. Alle Zeichen, die zwischen zwei Trennzeichen liegen, werden als ein Token angesehen. Token Die Methode nextToken() liefert einen int-Wert zurück. Dieser gibt unter anderem an, ob das gelesene Token als Zahlenwert interpretiert werden kann. Den Wert des gelesenen Tokens liefert ein Zugriff auf die als öffentlich deklarierten Felder sval oder nval, je nachdem, ob Datenspeicherung und -kommunikation
185
Nitty Gritty • Take that!
7
Sandini Bib
ein String oder ein Zahlenwert gelesen werden soll. Mit pushBack() kann man den gelesenen Token wieder zurück in den Stream stellen. Weitere Methoden Die Klasse StreamTokenizer bietet weitaus umfangreichere Funktionalität als hier beschrieben. Zum Beispiel lassen sich auch Kommentarzeichen, Trennzeichen für Strings oder zu ignorierende Zeichen festlegen. Weitere Klassen zum Parsen strukturierter Dateien sind in anderen Packages enthalten. java.text und javax.swing. text eignen sich für strukturierte und formatierte Texte, javax.swing.text.html für das HTML-Format (Hypertext Markup Language) und javax.swing.text.rtf für RTF (Rich Text Format).
Nitty Gritty • Take that!
7
7.1.13 IOExceptions Viele Operationen im Ein-/Ausgabe-Bereich können zu Fehlersituationen führen. Als Beispiel stelle man sich ein Programm vor, das schreibend auf ein Netzlaufwerk zugreift. Wird der Rechner, auf dem sich das Laufwerk physikalisch befindet, während der Operation vom Netz getrennt, so muss der Schreibvorgang mit einer Fehlermeldung abgebrochen werden. Die allgemeine Klasse für Ausnahmen in java.io heißt IOException und wird von einer Reihe speziellerer Klassen beerbt (siehe Abbildung 7.20). IOException
EOFException
FileNotFoundException
UTFDataFormatException
InterruptedIOException
UnsupportedEncodingException
ObjectStreamException
InvalidClassException
NotSerializableException
Bild 7.20: Exceptions im Package java.io 186
Ein-/Ausgabe – java.io
Sandini Bib
Häufig fängt man IOException global ab (siehe Kapitel 11) und unterscheidet nicht nach den einzelnen Fehlern. Es gibt aber Situationen, wo eine detailliertere Behandlung Sinn macht: Dateizugriff Liest man aus Dateien, kann es vorkommen, dass die angegebene Datei nicht gefunden wird, was in einer FileNotFoundException resultiert. Versucht man über das Dateiende hinwegzulesen, erhält man eine EOFException. Serialisierung
7.1.14 Ein-/Ausgabe und Sicherheit Zugriffe auf Dateien unterliegen verschiedenen Schutzmechanismen. Zum einen kann das unterliegende Betriebssystem für den Benutzer Rechte vergeben haben, die einen Schreib- oder Lesezugriff sperren. Daneben gibt es auch noch die Security-Policies von Java (siehe Abschnitt 10.5). Im Sandboxmodell von Java 1.0 und 1.1 sind alle Dateizugriffe aus unsignierten Applets (Abschnitt 9.3) verboten. Dies ist auch die Standardeinstellung seit Java 1.2. Unerlaubte Zugriffsversuche resultieren in SecurityExceptions. Sie können mit Hilfe der Methoden canRead() und canWrite() der Klasse File testen, ob ein Zugriff erlaubt ist.
Datenspeicherung und -kommunikation
187
7 Nitty Gritty • Take that!
Serialisierbare Objekte werden in Dateien gespeichert oder über Netzwerke übertragen. Dazu müssen sie in ein Byteformat umgewandelt werden können. Implementiert eine der referenzierten Klassen nicht das Interface Serializable, wird eine NotSerializableException geworfen. Versucht man ein Objekt eines bestimmten Typs einzulesen, obwohl der Stream ein Objekt eines anderen Typs enthält, resultiert dies in einer InvalidClassException oder ClassNotFoundException. Ein anderer Grund für eine InvalidClassException kann sein, dass die eingelesene Klassenversion nicht kompatibel mit derjenigen beim Schreiben ist.
Sandini Bib
Die Klasse FilePermission seit
SE
ME
EE
1.2
x
-
x
Seit Java 1.2 ist dieser Zugriffsschutz fein gegliedert und für jede Art von Ressourcen gibt es eine Permission, die in den Benutzereinstellungen gesetzt werden kann. Für Dateien ist dies die Klasse FilePermission. Die Klasse kennt folgende Berechtigungsarten (Actions):
T
read: Lesen write: Schreiben execute: Ausführen
T
delete: Löschen
T T
Diese Permissions sind standardmäßig in der Datei <JREPfad>\lib\security\java.policy definiert und können mit dem Werkzeug policytool bearbeitet werden.
Nitty Gritty • Take that!
7 7.1.15 Hinweise zur Ein-/Ausgabe Im Überblick über das Package java.io haben wir viele Aspekte der Ein- und Ausgabe kennen gelernt. Es gibt aber noch weitere Packages, die mit diesem Thema in Verbindung stehen. Netzwerk und RMI Die Netzwerkprogrammierung mit Sockets basiert auf Streams; RMI (Remote Method Invocation) benutzt zur Objektübertragung die Serialisierung. Wir werden uns diese Themen in den nächsten Abschnitten (Abschnitt 7.2 und 7.3) näher ansehen. Klasse FileDialog Im Package java.awt (siehe Kapitel 8.2), das vor allem grafische Kontrollelemente beinhaltet, gibt es eine Klasse FileDialog (siehe Abschnitt 8.2.11), die es erlaubt, Dateien und Pfade nach der Art der Laufzeitplattform darzustellen und auszuwählen. Diese Klasse implementiert übrigens das Interface java.io.FilenameFilter, mit dem be-
188
Ein-/Ausgabe – java.io
Sandini Bib
stimmte Namensrestriktionen definiert werden können, zum Beispiel Auswahl aller Dateien, die auf ».html« enden. New IO Die in Java 1.4 hinzugekommene neue IO-API (java.nio) ersetzt nicht die bisherige, sondern bietet eine Spezialerweiterung für den LowLevel-Zugriff auf große Dateien. Sie ist nicht stream-, sondern pufferbasiert.
7.2
TCP/IP-Programmierung – java.net
Das Package java.net enthält Klassen zur Programmierung von TCP/ IP-Netzwerkzugriffen. Dieses (Internet-)Protokoll stellt die Basis aller Java-Netzwerkzugriffe dar. Darauf aufbauend gibt es höhere Netzwerkprotokolle wie RMI für entfernte Methodenaufrufe und verteilte Anwendungen und Enterprise JavaBeans für verteilte Objekte.
Nitty Gritty • Take that!
7
Bild 7.21: Klassen im Package java.net
7.2.1 TCP/IP-Kommunikationsverbindungen Eine Kommunikation zwischen zwei Rechnern, die das TCP/IP-Netzwerkprotokoll benutzen, kann über eine Socket Connection betrieben werden. TCP/IP, das Standardprotokoll des Internets und vieler lokaler Netze (LANs), unterscheidet alle an das Netzwerk angeDatenspeicherung und -kommunikation
189
Sandini Bib
schlossenen Geräte nach Geräte-IDs, IP-Adressen genannt. Diese bestehen aus vier Bytes, welche in String-Darstellungen durch Punkte voneinander getrennt sind. Jeder Computer mit installiertem TCP/IP besitzt (mindestens) eine IP-Adresse, die z. B. so aussehen kann: 122.153.2.102
Verschiedene Geräte dürfen nicht die gleiche IP-Adresse besitzen. IPAdressen dienen ähnlich einer Postanschrift dem Auffinden des Nachrichtenempfängers. Häufig benutzt man auch symbolische Namen der Art webserver.nitty-gritty.de, die auf die IP-Adressen abgebildet werden können. Damit auf einem Computer mit einer IP-Adresse verschiedene Programme gleichzeitig Daten austauschen können (Webbrowser, Dateitransfer mittels ftp ...), benutzt jedes dieser Programme einen eigenen Kanal für die Kommunikation, genannt Port. Dieser Port ist eine nahezu beliebige Ganzzahl zwischen 1 und 65.535. Achten Sie bei der Benutzung von Ports darauf, dass alle Zahlen kleiner 1.000 entweder bestimmten Programmtypen zugeordnet oder reserviert sind, z. B. 80 für HTTP. Für die Anwendung macht es keinen Unterschied, wie hoch die Portnummer ist. Es dürfen aber nie mehrere Programme den gleichen Port benutzen.
Nitty Gritty • Take that!
7
Zwei Rechner können nun entweder eine stehende Verbindung besitzen (Prinzip Telefon) oder sich asynchron Nachrichten schicken (Prinzip Brief). Letztere werden im Internetprotokoll (IP) Datagramme genannt. 7.2.2
Die Klassen Socket und ServerSocket
seit
SE
ME
EE
1.0
x
-
x
Sockets Ein Socket ist der Kommunikationsendpunkt einer Verbindung an einem Rechner und identifiziert sich durch die IP-Adresse des Rechners und eine Portnummer. Ein Objekt der Klasse Socket ermöglicht es, Objekte, die als Input- bzw. OutputStream referenziert werden, über TCP oder darauf aufsetzende Protokolle zu übertragen. 190
TCP/IP-Programmierung – java.net
Sandini Bib
Client/Server-Kommunikation Soll eine Verbindung zwischen zwei Sockets (Socket Connection) erstellt werden, so muss ein Rechner dabei als Server dienen, was in diesem Zusammenhang als Dienstanbieter übersetzt werden kann. Auf diesem Rechner wird mindestens ein ServerSocket instanziiert. Das ServerSocket-Objekt bekommt in seinem Konstruktor eine Portnummer zugewiesen und wartet – wenn dazu aufgefordert – auf Requests an diesen Server. Requests, die zwar an den Rechner adressiert sind, aber eine andere Portnummer besitzen, werden ignoriert, da man davon ausgeht, dass diese an ein anderes Programm gerichtet sind. Der ServerSocket dient nur dem Zustandekommen einer Socket Connection. Der oder die Client-Rechner meldet/melden sich mit einem ihrerseits instanziierten Socket an, deren Portnummer mit der des Server-Programms übereinstimmen muss. Akzeptiert der Server die Verbindung, so wird auch auf seiner Seite eine Instanz von Socket erzeugt. ServerSocket
<> +Socket(host: String, port: int) +Socket(host: InetAdress, port: int)
<> +ServerSocket(port: int) <<Methoden>> +accept() +close() +getInetAdress() +getLocalPort() +setSoTimeout(timeout: int) +getSoTimeout() ...
<<Methoden>> +getInputStream() +getOutputStream() +close() +getInetAdress() +getPort() +getLocalAdress() +getLocalPort() +setSoTimeout(timeout: int) +getSoTimeout() ...
7 Nitty Gritty • Take that!
Socket
Bild 7.22: Die Klassen Socket und ServerSocket
Ablauf der Kommunikation Auf der Clientseite erzeugt man unter Angabe der Serveradresse und der vereinbarten Portnummer ein Objekt vom Typ Socket. Will man Daten vom Server empfangen, besorgt man sich durch den Aufruf von getInputStream() eine Referenz von Typ InputStream für eine lesende Verbindung zum Server; zum Senden verfährt man analog mittels geDatenspeicherung und -kommunikation
191
Sandini Bib tOutputStream(). Auf diese Streams können je nach Art der Daten weitere Streams aufsetzen, zum Beispiel ObjectIn-/OutputStreams (siehe Abschnitt 7.1.5) oder DataIn-/OutputStreams (siehe Abschnitt 7.1.11). Client
Server Socket auf Port 1087
Stream Stream
pc03.firma.de
Socket auf Port 5005 webserver.firma.de
Bild 7.23: Socket-Verbindung über Streams
Beispiel
Nitty Gritty • Take that!
7
Socket verbZumServer = new Socket(serverHostname, 5005); ObjectInputStream vomServer = new ObjectInputStream(verbZumServer.getInputStream()); DataOutputStream zumServer = new DataOutputStream(verbZumServer.getOutputStream()); // Objekt zum Server schicken: zumServer.writeChars("getDate\n"); // Objekt vom Server empfangen: Date d = (Date) vomServer.readObject(); vomServer.close(); zumServer.close(); verbZumServer.close();
Die Methode getInputStream() ist so deklariert, dass man eine Referenz vom abstrakt definierten Typ InputStream erhält. Der wirkliche Typ des Objekts bleibt verborgen. Dies ist typisch für die Java-Klassenbibliotheken, denn es ist für den Anwender irrelevant, welcher Low-Level-Streamklasse das Objekt angehört. Er kann je nach seinen Bedürfnissen mit beliebigen High-Level- oder FilterStreams darauf aufsetzen. Genauso verhält es sich auch mit dem OutputStream. In Wirklichkeit werden übrigens die Klassen SocketIn-/OutputStreams benutzt, die wiederum in BufferedIn-/OutputStreams verpackt sind.
192
TCP/IP-Programmierung – java.net
Sandini Bib
Server Der Server instanziiert zunächst einen ServerSocket mit derselben Portnummer, auf die der Client später zugreifen wird. Dem Konstruktor muss keine Serveradresse mitgeteilt werden, da ein ServerSocket-Objekt immer nur auf dem aktuellen Rechner erzeugt werden kann. Der Aufruf der Methode accept() versetzt den Server in den Wartezustand, bis ein Client auf diesen Server zugreift und damit eine Socket Connection etabliert. Der Verbindungsaufbau geschieht automatisch, sobald ein Client ein Socket-Objekt instanziiert. Der accept-Aufruf liefert in diesem Fall eine Referenz auf ein Objekt der Klasse Socket zurück, das zur Kommunikation zwischen Client und Server benutzt wird. Beispiel ServerSocket server = new ServerSocket(portnummer); Socket verbZumClient = server.accept(); // Verbindung zum Client hergestellt; BufferedReader vomClient = new BufferedReader( new InputStreamReader(verbZumClient.getInputStream())); ObjectOutputStream zumClient = new ObjectOutputStream(verbZumClient.getOutputStream()); // Client-Request lesen String req = vomClient.readLine(); if (req.equals("getDate")) { Date d = new Date(); // Objekt zum Client schicken: zumClient.writeObject(d); } vomClient.close(); zumClient.close(); verbZumClient.close();
Nitty Gritty • Take that!
7
Exceptions In diesem Beispiel wurde das Exception Handling weggelassen (siehe Kapitel 11). In einer realen Anwendung könnten folgende Exceptions auftreten, die man geeignet behandeln sollte:
Datenspeicherung und -kommunikation
193
Sandini Bib
1. UnknownHostException, falls unter dem Hostname kein Rechner erreichbar ist 2. SocketException ist die Superklasse verschiedener Ausnahmen (BindException, ConnectionException, NoRouteToHostException), die anzeigen, dass aufgrund von Netzproblemen zwischen den Ports beider Rechner keine Verbindung aufgebaut werden kann. Beispielsweise könnte der gewünschte Client-Port bereits durch eine andere Anwendung gesperrt sein, oder der Server antwortet nicht auf dem angegebenen Port (SocketException: Connection refused). 3. IOExceptions können ebenfalls auftreten, da die Netzwerkkommunikation auf den Klassen aus java.io aufbaut. Bei nicht-signierten Applets gilt die Einschränkung, dass Netzwerkverbindungen nur zu demjenigen Server aufgebaut werden dürfen, von dem das Applet heruntergeladen wurde. Das bedeutet, dass Sie mit diesem Server auf allen (von der Firewall) zugelassenen Ports kommunizieren dürfen, bei Verbindungsversuchen mit anderen Rechnern jedoch eine SecurityException erhalten.
7 Nitty Gritty • Take that!
Nachteile Socket Connections setzen sehr tief auf dem so genannten Protokollstack des TCP/IP-Netzwerkprotokolls auf und stellen daher eine primitive, aber auch schnelle Art der Kommunikation dar. Der Entwickler muss sich um viele Probleme der Netzwerkprogrammierung selbst kümmern: T
Verbindungsunterbrechung und Wiederaufsetzen
T
Verlust von Daten
T T
Datenformatprobleme und Serialisierung Umsetzung objektorientierter Aufrufe in Kanäle, die nur Bytes transportieren
T
Verschlüsselung einer Kommunikationsverbindung
Aufgrund dieser Nachteile benutzt man meist höhere Protokolle wie RMI (siehe Abschnitt 7.3), CORBA (Abschnitt 7.4) oder Enterprise JavaBeans(Abschnitt 7.5), die Lösungen für diese Probleme bieten und 194
TCP/IP-Programmierung – java.net
Sandini Bib
damit den Programmierer entlasten. Mit RMI beispielsweise könnten wir unser getDate direkt als Methodenaufruf an den Server schicken und bekämen als Ergebnis ein Date-Objekt zurück, ohne dass wir uns mit Streams auseinander setzen müssten (siehe Beispiel im Online-Kapitel). Wenn Sie eine Serveranwendung schreiben wollen, die mehrere Clients bedient, empfiehlt sich folgender Aufbau: 1. Der Server befindet sich in einer Endlosschleife, die im accept-Zustand eines ServerSockets mit einer allen Clients bekannten Portnummer verweilt, bis sich ein neuer Client anmeldet. 2. Ein neuer Client bekommt vom Server über diesen Kanal eine neue, exklusive Portnummer für seine Kommunikation mit dem Server mitgeteilt. 3. Der Server erzeugt für einen neuen Client einen eigenen Thread und wartet in diesem Thread mittels accept() eines neuen ServerSockets auf den Client.
7.2.3 Datagramme Neben den beschriebenen Sockets für eine synchrone, verbindungsorientierte Kommunikation (Socket Connection), gibt es in Java auch Unterstützung für den verbindungslosen Datagrammdienst. Datagramme kann man sich wie Briefe vorstellen, die an einen Adressaten geschickt werden. Der Sender schickt die Nachricht los, weiß aber nicht, wann und ob sie ankommt.
&OLHQW
6HUYHU
Bild 7.24: Datagramme
Datenspeicherung und -kommunikation
195
7 Nitty Gritty • Take that!
4. Der Client baut eine weitere – nun dedizierte – Socket Connection mit dem Server auf dem ihm zugeteilten Port auf.
Sandini Bib
Ähnlich wie das Protokoll TCP setzt auch das DatagrammProtokoll UDP auf dem Internet-Protokoll IP auf und nutzt dessen Dienste zur Adressierung und Datenübertragung. Die Klassen DatagramSocket und DatagramPacket seit
SE
ME
EE
1.0
x
-
x
Ein Objekt der Klasse DatagramSocket kann mittels send() Datenpakete, so genannte Datagramme, verschicken und mit receive() empfangen. Die Datagramme selbst sind Objekte vom Typ DatagramPacket, die Daten in Byte-Form enthalten.
Nitty Gritty • Take that!
7
DatagramSocket
DatagramPacket
<> +DatagramSocket() +DatagramSocket(port : int)
<> +DatagramPacket(buffer: byte[], länge: int, ziel: InetAddress, port: int) +DatagramPacket(buffer: byte[], länge: int)
<<Methoden>> +send(p: DatagramPacket) +receive(p: DatagramPacket) +close() +getLocalPort() +setSoTimeout(timeout: int) +getSoTimeout() ...
<<Methoden>> +setData(daten : byte[]) +getData() +get/setLength(...) +get/setAddress(...) +get/setPort(...)
Bild 7.25: Die Klassen DatagramSocket und DatagramPacket
Beispiel Server: // Datenpaket erzeugen: byte[] buffer = (new String("Testdaten")).getBytes(); DatagramPacket daten = new DatagramPacket(buffer, buffer.length, zieladresse, zielport); // Sender erzeugen: DatagramSocket sender = new DatagramSocket(); sender.send(daten); // Daten versenden sender.close();
196
TCP/IP-Programmierung – java.net
Sandini Bib
Client: // Leeres Datenpaket erzeugen: byte[] buffer = new byte[256]; DatagramPacket daten = new DatagramPacket(buffer, buffer.length); // Empfänger erzeugen: DatagramSocket empfaenger = new DatagramSocket(portnr); empfaenger.receive(daten); // auf ankommende Daten warten buffer = daten.getData(); // Daten extrahieren String gelesen = new String(buffer); empfaenger.close();
Im Beispiel wurde die Fehlerbehandlung weggelassen. Es können dieselben Fehler auftreten wie bei Sockets (siehe Abschnitt 7.2.2). Wenn Sie Netzwerkanwendungen lokal testen wollen, ist dies kein Problem. »localhost« dient als symbolischer Name für den aktuellen Rechner, die so genannte Loopback-Adresse für Pakete, die den Rechner nicht verlassen sollen, lautet 127.0.0.1.
7
seit
SE
ME
EE
1.1
x
-
x
Mit dem MulticastSocket können Datagramme auch an Gruppen von Rechnern verschickt werden. DatagramSocket
MulticastSocket
<> +MulticastSocket(port: int) <<Methoden>> +joinGroup(gruppe : InetAddress) +leaveGroup(grupe: InetAddress) +get/setTimeToLive(...) ...
Bild 7.26: Die Klasse MulticastSocket Datenspeicherung und -kommunikation
197
Nitty Gritty • Take that!
Die Klasse MulticastSocket
Sandini Bib
Methoden Die Klasse ist vom konventionellen DatagramSocket abgeleitet und schickt die Pakete an eine IP-Adresse im speziell reservierten Adressraum von 224.0.0.1 bis 239.255.255.255. Mit den Methoden joinGroup() bzw. leaveGroup() kann sich ein Rechner einer Gruppe anschließen bzw. sich von ihr abmelden, die durch eine solche Adresse gekennzeichnet ist. Schickt man nun ein Datagramm an die Gruppe, erhalten dieses alle Mitglieder. Die Klasse MulticastSocket ähnelt einem Publish-AndSubscribe-Ansatz. Angemeldete Benutzer erhalten die Nachricht, andere nicht, wobei sich der Sender nach dem Abschicken um nichts mehr kümmern muss. Allerdings ist dieser Dienst sehr primitiv und nicht vergleichbar mit einem professionellen Message Queueing System. Zum Beispiel kann man mit Datagrammen nur Bytes, keine Objekte verschicken, es gibt keine Sicherungsmechanismen oder andere Quality-Of-Service-Definitionen für die Verteilung. In der Java 2 Enterprise Edition gibt es jedoch einen optionalen Messaging Service, dessen Schnittstelle im Package javax.jms definiert ist.
Nitty Gritty • Take that!
7
Datagramme in der Micro Edition seit
SE
ME
EE
1.0
-
x
-
Die Java 2 Micro Edition enthält zwar nicht das Package java.net, jedoch eine vereinfachte Variante mit dem Namen javax.microedition.io. Das darin enthaltene Interface Datagram vereint die Klassen DatagramSocket und DatagramPacket. «Interface» Datagram
+setData(buffer: byte[], offset:int, länge: int) +getData() +get/setLength(...) +get/setAddress(...) ...
Bild 7.27: Das Interface Datagram 198
TCP/IP-Programmierung – java.net
Sandini Bib
Zur Adressierung werden hier allerdings keine InetAddress-Objekte benutzt, sondern einfache Strings. 7.2.4
Adressen, Ressourcen und URLs
Die Klasse InetAddress seit
SE
ME
EE
1.0
x
-
x
Zur Identifizierung von Rechnern haben wir bislang IP-Adressen oder die symbolischen Hostnamen benutzt. Während die Hostnamen in Java durch einfache String-Objekte repräsentiert werden, gibt man IP-Adressen durch Objekte der Klasse InetAddress an. Diese Maßnahme stellt sicher, dass nur syntaktisch zugelassene Adressobjekte erzeugt werden können. InetAddress
Bild 7.28: Die Klasse InetAddress
Methoden Die Klasse InetAddress besitzt keinen (öffentlichen) Konstruktor. Ein Objekt dieser Klasse erzeugt man vielmehr durch den Aufruf der statischen Methoden getLocalHost() oder getByName(hostname). Bei letzterer Methode wird versucht, den symbolischen Namen in die Adressdarstellung aufzulösen, z. B. vom logischen IP-Namen pc02.firma.de in 192.145.32.5. Mittels getHostAddress() bzw. getHostName() können Sie beide Darstellungsarten in String-Format erhalten. Seit Java 1.4 werden auch Adressen nach IPv6 unterstützt.
Datenspeicherung und -kommunikation
199
Nitty Gritty • Take that!
7 +getLocalHost() +getByName(host: String) +getHostAdress() +getHostName() +getAdress() +toString() +equals()
Sandini Bib
Die Klasse URL seit
SE
ME
EE
1.0
x
-
x
Im WWW werden Ressourcen über so genannte URLs (Universal Resource Locator) identifiziert. Eine URL besteht zunächst aus einem Protokollnamen, zum Beispiel http (HTML), file (lokale Dateien), ftp (Dateitransfer), rmi (Remote Method Invocation), iiop (Internet Inter ORB Protocol) oder jdbc (Java Database Connectivity). Dem Protokoll folgen ein Doppelpunkt, zwei Schrägstriche und optional ein Hostname bzw. eine Hostadresse und eine Portnummer. Falls die Angabe des Rechners fehlt, wird der aktuelle Rechner (localhost) benutzt, bei den Ports bekannte Standardnummern. Schließlich folgt eine Bezeichnung der Ressource, typischerweise unter Angabe eines Pfades. Beispiel
Nitty Gritty • Take that!
7
http://www.nitty-gritty.de/java/index.html file:///c:/jdk12/docs/index.html ftp://125.65.3.107/public/download/updates jdbc:db2://dbserver:8008/sampledb URL
<> +URL(spec: String) +URL(protokoll: String, host: String, pfad: String) +URL(protokoll: String, host : String, portnummer: int, pfad: String) <<Methoden>> +getContent() +openStream() +openConnection() +getProtocol() +getHost() +getPort() +getFile() +toExternalForm() +toString() ...
Bild 7.29: Die Klasse URL
200
TCP/IP-Programmierung – java.net
Sandini Bib
Methoden Da die URLs einer festen Struktur entsprechen müssen, gibt es auch hierfür eine Java-Klasse mit dem Namen URL. Der Konstruktor erlaubt die Erzeugung als String oder aus den Namensbestandteilen. Bei diesem Vorgang sollte man die Exception MalformedURLException behandeln, die ungültige URLs anzeigt und ebenfalls im Package java.net definiert ist. Die einzelnen Teile der URL können mittels getMethoden abgefragt werden (getProtocol(), getHost(), getPort(), getFile()). toString() schließlich ruft toExternalForm() auf und liefert die gesamte URL als Zeichenkette zurück. Auf den Inhalt der Ressource, die hinter der URL steht, kann – je nach Typ – durch die Methoden getContent(), openConnection() oder openStream() zugegriffen werden: getContent() ist eine komfortable Art, die Objekte hinter der URL zu laden. Anhand der Endung (».txt«, ».gif«...) unterscheidet der Ladeprozess den Typ und liefert ein Objekt einer Hilfsklasse zum Zugriff auf die Datei, z. B. PlainTextInputStream für eine Textdatei oder URLImageSource für ein Bild.
7
zum Laden eines Objekts, z. B. eine FileURLConnection für das Lesen aus lokalen Dateien oder HttpURLConnection für Zugriffe mittels HTTP-Protokoll. openStream() macht dasselbe, eröffnet in dieser neuen Verbindung sofort einen InputStream und gibt diesen zurück. URLs und Applets URLs werden auch in Webbrowsern zur Identifikation von Webseiten und Ressourcen genutzt. Ein Applet kann mit der Methode getDocumentBase() ein URL-Objekt der Seite beziehen, von der es geladen wurde. Mit Hilfe von URLs können Sie in einem Applet, das in einem Browser läuft, auch ein anderes Dokument referenzieren und im Browser anzeigen. Dazu brauchen Sie einen Verweis auf den Browser und können mit showDocument(URLderWebseite) die Seite im Browser anzeigen. Im folgenden Beispiel sehen Sie ein Applet, das eine URL als Parameter besitzt und beim Starten im Webbrowser sofort die mit dieser URL spezifizierte Seite im aktuellen Browserfenster anzeigt. Datenspeicherung und -kommunikation
201
Nitty Gritty • Take that!
openConnection() liefert je nach Protokoll eine geeignete Verbindung
Sandini Bib
Beispiel
Nitty Gritty • Take that!
7
public class RedirectorApplet extends java.applet.Applet { // Methode start, alle anderen Methoden können // unverändert bleiben public void start() { // im Parameter URL im Applet-Tag der HTML-Seite // stehe die Zieladresse String ziel = getParameter("URL"); try { URL zielURL = new URL(ziel); getAppletContext().showDocument(zielURL); } catch (java.net.MalformedURLException e) { System.out.println("URL " + ziel + " ist ungültig"); } } }
Es gibt noch eine flexiblere Variante der Methode showDocument(URL dokument, String frame). Mit dem zweiten Parameter kann man den Namen des Frames angeben, in dem das Dokument angezeigt werden soll. Die Konstante "_self" gibt den Frame des Applets an, "_top" den Hauptframe und mittels "_blank" erreicht man die Darstellung in einem neuen Browserfenster. Die Klasse URLConnection und abgeleitete Klassen seit
SE
ME
EE
1.0
x
-
x
Eine URLConnection erhält man durch den Aufruf der Methode openConnection() eines URL-Objekts. Mit diesem Objekt lassen sich die Inhalte der Ressource lesen sowie Informationen über die Art des Objekts er-
202
TCP/IP-Programmierung – java.net
Sandini Bib
mitteln. Je nach Art des Objekts und Protokolls wird die abstrakte URLConnection durch verschiedene konkrete Klassen implementiert, zum Beispiel HttpURLConnection, AppletResource-Connection, FileURLConnection, FtpURLConnection oder MailToURLConnection. URLConnection {abstract} +getContent() +getInput/OutputStream() +getContentType() +getContentEncoding() +getContentLength() +getHeaderField() +getDate() +getExpiration() +getLastModified() +connect() +getPermission() ...
+HTTP_ACCEPTED: int +HTTP_FORBIDDEN: int +HTTP_BAD_REQUEST: int +HTTP_NOT_FOUND: int +HTTP_INTERNAL_ERROR: int +HTTP_CLIENT_TIMEOUT: int +getResponseCode() +getResponseMessage() +get/setRequestMethod(...) +disconnect() +usingProxy() ...
JarURLConnection +getManifest() +getCertificates() +getJarFile() +getJarEntry() ...
7
Bild 7.30: Die Klasse URLConnection
Zugriff auf eine URLConnection bzw. ein Objekt eines ihrer Subklassen erhält man in der Regel über den Aufruf von openConnection() eines URL-Objekts. Wenn Sie nur den Inhalt als Objekt oder per Stream lesen möchten, reichen die Methoden getContent() bzw. openStream() von URL, die sich wiederum Methodenaufrufe in URLConnection durchreichen (siehe Abbildung 7.31).
Datenspeicherung und -kommunikation
203
Nitty Gritty • Take that!
HttpURLConnection
Sandini Bib
<>
quelle:URL
getContent() openConnection() :URLConnection getContent()
<>
quelle:URL
openStream() openConnection() :URLConnection getInputStream()
Bild 7.31: Indirekte Nutzung von URLConnection aus URL-Objekten
Nitty Gritty • Take that!
7
Daneben bietet URLConnection die Möglichkeit, Information über die Ressource wie Länge, Datum usw. abzufragen. Besonders wichtig ist der ContentType, der die Art der Ressource und deren Behandlung als Objekt kennzeichnet. Er wird als MIME-Typ angegeben (Standard für Internet-Mailerweiterung). Beispiele sind text/plain, text/html, image/gif oder application/zip. In neueren Java-Versionen gibt es einige speziellere Implementierungen für Connections zu Ressourcen. Die HttpURLConnection (seit Java 1.1, siehe Abbildung 7.30) fügt einige Methoden für das HTTPProtokoll dazu, zum Beispiel get/setRequestMethod(), um die Art des Requests zu definieren (PUT, GET...), oder getResponseCode/Message() für die Verarbeitung von Rückgabewerten. Die Zahl 404 steht zum Beispiel für eine nicht gefundene Seite. Für die Werte sind zur besseren Lesbarkeit auch Konstanten vordefiniert, z. B. HTTP_NOT_ FOUND. Seit Java 1.2 gibt es die JarURLConnection, mit der man den Inhalt eines Java-Archivs (Abschnitt 4.10.5) sowie Zusatzinformationen wie das Manifest oder enthaltene Zertifikate abfragen kann. 204
TCP/IP-Programmierung – java.net
Sandini Bib
ContentConnections in der Micro Edition seit
SE
ME
EE
1.0
-
x
-
Die Java 2 Micro Edition enthält zwar nicht das Package java.net, jedoch eine vereinfachte Variante mit dem Namen javax.microedition.io. Dies enthält eine Klasse Connector mit mehreren Klassenmethoden zur Erzeugung von Connections. Eine spezielle Connection ist die ContentConnection, die eine »Light-Version« von java.net.URLConnection darstellt. Mit ihren Methoden kann man Input/OutputStreams erzeugen und Länge, Typ und Encoding der Ressource abfragen. 7.2.5 Netzwerk-Exceptions Auch beim Netzwerkzugriff kann es zu zahlreichen Problemen kommen. Beispielweise kann ein Port bereits durch eine andere Anwendung belegt sein oder ein Rechner ist nicht erreichbar. Alle Ausnahmen im Package java.net sind von der IOException in java.io abgeleitet (siehe Abbildung 7.32).
7
MalformedURLException
ProtocolException
UnknownHostException
Nitty Gritty • Take that!
java.io.IOException
UnknownServiceException
SocketException
BindException
ConnectHostException
NoRouteToHostException
Bild 7.32: Exceptions im Package java.net
Häufig fängt man IOException global ab (siehe Kapitel 11) und unterscheidet nicht nach den einzelnen Fehlern. Es gibt aber Situationen, wo eine detailliertere Behandlung Sinn macht: Datenspeicherung und -kommunikation
205
Sandini Bib
Ressourcenzugriff Arbeitet Sie mit URLs, dann zwingt Sie der Compiler zur Behandlung der MalformedURLException, die auftritt, falls eine URL nicht korrekt aufgebaut ist, zum Beispiel ein Bestandteil fehlt. Eine UnknownHostException tritt auf, wenn der angegebene IP-Name nicht in eine IPAdresse aufgelöst werden kann. Kommunikation Probleme des unterliegenden TCP/IP-Protokolls werden mit Exceptions vom Typ ProtocolException oder SocketException gemeldet. Eine BindException tritt dabei typischerweise auf, wenn der lokale Port, auf den zugegriffen werden soll, bereits in Benutzung ist. Die anderen Subklassen der SocketException deutet eher darauf hin, dass im entfernten Zielsystem oder auf dem Weg dorthin etwas nicht in Ordnung ist.
Nitty Gritty • Take that!
7
7.2.6 Netzwerkzugriff und Sicherheit Zugriffe auf Netzwerkressourcen unterliegen verschiedenen Schutzmechanismen. Zum einen kann man eine Authentifizierung (z. B. Userid und Passwort) manuell verlangen, zum anderen kann man bestimmte IP-Adressen und Ports in den Security-Policies von Java festlegen (siehe Abschnitt 10.5). Ein weiterer Mechanismus sind Firewalls, die wiederum den Netzwerkverkehr beschränken. Im Sandboxmodell von Java 1.0 und 1.1 sind alle Netzwerkzugriffe aus unsignierten Applets (Abschnitt 9.3) nur dann erlaubt, wenn sie mit demjenigen Server stattfinden, von dem das Applet geladen wurde. Unerlaubte Zugriffsversuche resultieren in SecurityExceptions. Die Klasse SocketPermission seit
SE
ME
EE
1.2
x
-
x
Seit Java 1.2 ist der Zugriffsschutz fein gegliedert und für jede Art von Ressourcen gibt es eine Permission, die in den Benutzereinstellungen gesetzt werden kann. Für Sockets ist dies die Klasse SocketPermission.
206
TCP/IP-Programmierung – java.net
Sandini Bib
Die Klasse kennt folgende Berechtigungsarten (Actions): T T T T
connect: Verbindungsaufbau resolve: Adressauflösung accept: Annahme eingehender Verbindungen listen: Warten auf eingehende Verbindungen
Beispiel SocketPermission("localhost:1024-8000", "accept,connect,listen");
Die Klasse NetPermission und Authentifizierung seit
SE
ME
EE
1.2
x
-
x
Daneben gibt es noch eine allgemeinere Permission namens NetPermission. Sie hat keine eigenen Berechtigungsarten (Actions), aber eine Methode zum Setzen eine Authenticators.
Diese Permissions sind standardmäßig in der Datei <JREPfad>\lib\security\java.policy definiert und können mit dem Werkzeug policytool bearbeitet werden. Verschlüsselung seit
SE
ME
EE
1.4
x
-
x
Seit Java 1.4 ist SSL (Secure Socket Layer) Bestandteil der Java-Laufzeitumgebung. Mithilfe der Klassen aus javax.net.ssl können Daten über so genannte Secure Sockets verschlüsselt übertragen werden.
Datenspeicherung und -kommunikation
207
7 Nitty Gritty • Take that!
Die abstrakte Klasse Authenticator und ihre Hilfsklasse PasswordAuthenticator dienen zur Prüfung eines Zugriffsversuches auf eine Ressource. Meist erfolgt die Prüfung durch Eingabe einer Benutzerkennung und eines Passwortes. Der benutzte Mechanismus kann aber unterschiedlich sein und wird durch Ableitung von der Klasse Authenticator und Überschreiben der Methode getPasswordAuthentication() erreicht.
Sandini Bib
7.3
Remote Method Invocation – java.rmi
Remote Method Invocation (RMI) ist ein Java-Mechanismus – ähnlich CORBA – zur Erstellung verteilter Anwendungen. Es kommt bei der Entwicklung von Netzwerkanwendungen häufig vor, dass ein bestimmter Dienst in Form einer Methode auf einem Server ausgeführt werden soll, wie dies auch beim Beispiel unserer Datum-Clients (»getDate«, siehe Socket-Programmierung im Abschnitt 7.2.2) der Fall war. Dort haben wir diesen verdeckten Aufruf noch in Bytes umgewandelt und am Server bearbeitet. Viel praktischer und objektorientierter wäre es gewesen, wenn wir direkt eine Methode am Server aufgerufen hätten und als Rückgabewert das Datum-Objekt bekommen hätten. Genau dieses ermöglicht RMI. Eine Beschreibung finden Sie in den Online-Kapiteln auf www.nittygritty.de
7.4
Nitty Gritty • Take that!
7
CORBA – org.omg.CORBA
Die Common Object Request Broker Architecture (CORBA) ist – ähnlich RMI – eine Software-Infrastruktur zur Erstellung verteilter Anwendungen. Im Unterschied zu RMI (siehe Abschnitt 7.3) bietet CORBA jedoch noch wesentlich mehr. Kommerzielle Object Request Broker (ORBs) wie zum Beispiel Borland/Inprise Visibroker oder Iona OrbixWeb stellen weitaus mächtigere Dienste bereit als RMI oder der in Java 2 integrierte Mini-Java-ORB. Dort ist die Einbeziehung von Nicht-Java-Diensten möglich, und es gibt Persistenz-, Transaktions-, und Objekt-Lifecycle- sowie rund ein Dutzend weiterer Dienste. Die Programmierung gestaltet sich aber weitaus komplizierter als bei RMI. Eine Beschreibung finden Sie in den Online-Kapiteln auf www.nitty-gritty.de
7.5
Enterprise JavaBeans – javax.ejb
Eine dritte Alternative für die komfortable Entwicklung verteilter Anwendungen (neben RMI und CORBA) sind Enterprise JavaBeans (EJBs). Auch hier werden Server-Objekte auf einem Applikationsserver gelagert, die von Thin Clients (siehe Abschnitt 7.3) genutzt wer208
Remote Method Invocation – java.rmi
Sandini Bib
den können. Dabei steht bei EJBs die einfache Entwicklung von Komponenten im Vordergrund, RMI (siehe Abschnitt 7.3) und CORBA (siehe Abschnitt 7.4) werden dabei als Infrastruktur genutzt. Die EJB-Klassen sind nur in der Java 2 Enterprise Edition enthalten, die notwendigen Clientklassen sind jedoch in der Java-Version 1.3 bereits teilweise in den neuen CORBA-Packages inbegriffen. Bei älteren Java-Versionen sind diese noch Extensions, das heißt, sie müssen extra installiert werden. Ein Download wie bei Applets kann dabei durchaus problematisch sein, da die Klassen sich je nach Hersteller durchaus auf mehrere Megabyte summieren können.
Eine Beschreibung finden Sie in den Online-Kapiteln auf www.nittygritty.de
7.6
Datenbankzugriffe – java.sql
Das Package java.sql beinhaltet alle Klassen und Interfaces, die für den Zugriff auf relationale Datenbanken benötigt werden. Seit Java 2 wurden diese Klassen stark erweitert, viele Datenbanken unterstützen jedoch bislang nur die alten Interfaces. Für Serveranwendungen mit hohem Anfrageaufkommen und der Notwendigkeit der Verwaltung mehrerer Datenquellen ist in der Enterprise Edition und J2SE 1.4 zusätzlich das Package javax.sql enthalten.
Datenspeicherung und -kommunikation
209
7 Nitty Gritty • Take that!
Enterprise JavaBeans sind nicht-grafische Beans, die in verteilten MultiTier-Anwendungen verwendet werden. Eine Enterprise JavaBean (EJB) ist in einem Container enthalten, der die Eigenschaften der Bean definiert. Dazu zählen beispielsweise Transaktionssicherheit, Persistenz, Lokalisierung oder Resource Pooling. Der Entwickler einer EJB muss sich mit diesen Middleware-Komponenten nicht direkt beschäftigen oder sie gar selbst implementieren, sondern er gibt über Deskriptoren an, welche Anforderungen er an einen Container und damit an die Laufzeitumgebung stellt.
Sandini Bib
java.sql & javax.sql Klassen
Interfaces
Datentypen - Blob - Clob - SQLData - SQLInput - SQLOutput - Struct - Array - Ref
Treiber/ Verbindung - Driver - Statement - PreparedStatement - CallableStatement - ResultSet - ResultSetMetaData - DatabaseMetaData - DataSource - PooledConnection - XADataSource - XAConnection - RowSet - ConnectionEvent - RowSetEvent
Treiber/ Verbindung
Datentypen
- DriverManager - DriverPropertyInfo
- Types - Date - Time - Timestamp
Ausnahmebehandlung - SQLException - SQLWarning - DataTruncation - BatchUpdateException
Bild 7.33: Die Klassen im Package java.sql
Nitty Gritty • Take that!
7
7.6.1 JDBC JDBC (Java DataBase Connectivity) ist eine definierte Schnittstelle zu relationalen Datenbanken. Sie basiert auf der Datenbanksprache SQL (Structured Query Language). Die konkrete Realisierung der Datenbankzugriffe wird durch die Einführung einer Zwischenschicht vom Entwickler abgeschirmt. Das bedeutet, dem Entwickler ist es egal, ob als Datenbankserver IBMs Universal Database/DB2, Oracle, Sybase oder der Microsoft SQL Server eingesetzt wird, die Programmierschnittstelle ist immer dieselbe. Ein Datenbanktreiber übernimmt die Umsetzung von JDBC auf das jeweilige Native-Interface der Datenbank. Der Zugriff war zunächst (JDBC 1.0) auf die wichtigsten SQL-Datentypen beschränkt, die seit Java 2 enthaltene Version JDBC 2.0 unterstützt nun auch strukturierte Typen und bietet eine vereinfachte Verwaltung größerer Datenmengen. Nur sehr rudimentär in dieser Bibliothek enthalten sind Befehle für das Management des Datenbankzugriffs, also zum Beispiel den Verbindungsauf- und -abbau, Zugriffsprotokolle etc. java.sql besteht zu weiten Teilen aus abstrakten Interfaces, die das API beschreiben, implementiert diese aber nicht selbst. JDBC-Treiber Das Package java.sql enthält vorwiegend Interfaces, die den Zugriff auf die Datenbank beschreiben. Dies reicht für die Entwicklung einer 210
Datenbankzugriffe – java.sql
Sandini Bib
Datenbankanwendung aus und der Hauptvorteil von JDBC liegt in der Unabhängigkeit konkreter Datenbankimplementierungen. Die entwickelten Programme benötigen zur Laufzeit einen JDBC-Treiber, der die Schnittstellen für die benutzte Datenbank realisiert. Diese Treiber liegen in der Regel dem jeweiligen Datenbankserver- oder Entwicklerpaket bei oder sind über Dritthersteller zu erwerben. Mittlerweile gibt es für praktisch jede relationale Datenbank JDBCTreiber, allerdings erst wenige, die JDBC 2.0 unterstützen. Oft gibt es sogar unterschiedliche Treiber für dieselbe Datenbank, zum Beispiel ein Treiber für Thin-Clients, der keinen lokal installierten Datenbankclient voraussetzt, und eine schlankere Variante für Rechner mit Datenbankclient. JDBC-ODBC-Bridge
7.6.2
Ablauf eines Datenbankzugriffs
Anwendung
DriverMgr.
getConnection()
<>
:Connection
Ref. auf Conn. createStatement()
<>
Referenz auf Statement
:Statement
executeQuery("SELECT * FROM...") ResultSet close() close()
Bild 7.34: Datenbankzugriff mit java.sql
Datenspeicherung und -kommunikation
211
7 Nitty Gritty • Take that!
Eine zu JDBC ähnliche Datenbank-Schnittstelle namens ODBC (Open DataBase Connectivity) existiert auf der PC-Plattform. Falls es für Ihre Datenbank keine JDBC-Treiber gibt, können Sie auch mit einer JDBCODBC-Bridge arbeiten, die jeden JDBC-Befehl in einen ODBC-Befehl übersetzt, dann dem ODBC-Treiber übergibt, der diesen Befehl wiederum in einen Datenbankbefehl umwandelt. Diese vielen Umwandlungen kosten viel Zeit, so dass mit diesem Verfahren keine gute Performance erreichbar ist. Die Verwendung einer Datenbank mit JDBC-Treibern ist in jedem Fall vorzuziehen!
Sandini Bib
1. Zuerst wird über den DriverManager ein spezifischer Datenbanktreiber geladen, der das Interface Driver implementiert. 2. Damit kann eine Verbindung (Typ Connection) zu einer Datenbank aufgebaut werden. 3. Im nächsten Schritt wird ein SQL-Befehl in ein Objekt des Typs Statement verpackt und anschließend ausgeführt. 4. Das Ergebnis ist in der Regel ein Array von Datensätzen, die in einem ResultSet-Objekt enthalten sind und mit Iteratoren – so genannten Cursorn – durchlaufen werden können. 5. Schließlich müssen das Statement und die Connection geschlossen werden. Nachteile
Nitty Gritty • Take that!
7
Die Schwierigkeiten bestehen darin, dass relationale Datenbanken Daten nicht objekt-, sondern tabellenorientiert ablegen. Damit ist eine Konvertierung notwendig. Außerdem entsprechen die SQL-Datentypen nicht den Javatypen, so dass auch hier eine Umwandlung notwendig ist, die man als Mapping bezeichnet. Dafür gibt es eine Reihe kommerzieller Werkzeuge und Entwicklungsumgebungen, die die Datenbankprogrammierung deutlich vereinfachen. 7.6.3
Die Klasse DriverManager
seit
SE
ME
EE
1.0
x
-
x
Diese Klasse dient als Schnittstelle zwischen den abstrakten Programmier-Interfaces aus java.sql und den konkreten Datenbanktreibern. Zunächst müssen die auf einem Rechner verfügbaren Treiber bekannt gemacht werden. Die Treiberklasse können Sie entweder in der Umgebungsvariable jdbc.drivers angeben oder sie explizit mit Class.forName() laden: Beispiel jdbc:drivers=sun.jdbc.odbc.JdbcOdbcDriver
212
Datenbankzugriffe – java.sql
Sandini Bib
oder Class.forName("COM.ibm.db2.jdbc.app.DB2Driver"); DriverManager
+getConnection(url: String) +getConnection(url: String, userid: String, passwort: String) +getDriver(url: String) +getDrivers() +registerDriver(treiber: Driver) ...
Bild 7.35: Die Klasse DriverManager
Methoden Steht nun ein (oder mehrere) Treiber zur Verfügung, können Sie mittels getConnection() eine Verbindung aufbauen. Dabei müssen Sie die Zieldatenbank als URL angeben. Der Aufbau dieses String ist wie folgt: jdbc:<Subprotokoll>: jdbc:<Subprotokoll>://:<port>/
Das Subprotokoll bezieht sich auf den verwendeten Datenbanktreiber, danach folgt der Name einer lokal katalogisierten Datenbank oder Adresse und optional der Port einer Datenbank auf einem Server. Beispiel jdbc:odbc:KontoDatenbank jdbc:db2://dbserver:4567/KontoDatenbank
Die anderen Methoden dienen zur Verwaltung von Treibern. 7.6.4
Das Interface Connection
seit
SE
ME
EE
1.0
x
-
x
Dieses Interface ist die zentrale Schnittstelle für die Verwaltung einer Datenbankverbindung. Sie können damit Datenbankabfragen vorbereiten und deren Attribute definieren bzw. abfragen. Datenspeicherung und -kommunikation
213
Nitty Gritty • Take that!
7
Sandini Bib
«interface» Connection +TRANSACTION_NONE: int +TRANSACTION_READ_COMMITED: int +TRANSACTION_READ_UNCOMMITTED: int +TRANSACTION_REPEATABLE_READ: int +TRANSACTION_SERIALIZABLE: int +createStatement() +prepareStatement(sql: String) +prepareCall(sql: String) +close() +getWarnings() +commit() +rollback() +setAutoCommit(modus: boolean) +getTransactionIsolation() +setTransactionIsolation(stufe: int) +getMetaData() +getTypeMap() +setTypeMap(mapping: Map) +setReadOnly(modus: boolean) +isReadOnly() ...
Bild 7.36: Das Interface Connection
Methoden
Nitty Gritty • Take that!
7
Die wichtigsten Methoden sind die zur Erzeugung von Statements (für Abfragen, Änderungen usw., siehe Abschnitt 7.6.5). Wenn Sie die Datenbankverbindung nicht benötigen, vergessen Sie nicht, sie per close() zu schließen. Mit getWarnings() können Sie Warnmeldungen der Datenbank abfragen; Fehler können als SQLExceptions in den meisten Methoden auftreten und müssen abgefangen werden. Daneben gibt es eine ganze Reihe von Methoden und Konstanten zur Steuerung von Transaktionen, also zur abgesicherten Ausführung der Statements. Eine Erläuterung der Transaktionssteuerung würde aber den Rahmen dieses Buches sprengen. Beispiel Connection con = DriverManager.getConnection("jdbc:odbc:KontoDatenbank", userID, passwort); Statement stmt = con.createStatement();
214
Datenbankzugriffe – java.sql
Sandini Bib
7.6.5
Statement-Interfaces
seit
SE
ME
EE
1.0
x
-
x
Statements beinhalten SQL-Anweisungen für die Datenbank, zum Beispiel zu Abfrage, Änderung, Einfügung oder Löschung von Datensätzen. Öfter benötigte Statements können Sie als PreparedStatements ablegen und bei Bedarf nur noch die Parameter geeignet belegen. Mittels CallableStatements können Sie in der Datenbank gespeicherte Stored Procedures aufrufen. «interface» Statement +execute(sql: String) +executeQuery(sql: String) +executeUpdate(sql: String) +executeBatch() +addBatch(sql: String) +close() +getResultSet() +getMoreResults() +setFetchSize(saetze: int) +setQueryTimeout(sekunden: int) ...
7 «interface» CallableStatement
+execute() +executeQuery() +executeUpdate() +setNull(index: int, sqlTyp: int) +setBoolean(index: int, wert: boolean) +setByte(index: int, wert: byte) ... +setDate(index: int, datum: Date) +setBlob(index: int, wert: Blob) +setString(index: int, wert: String) +setTime(index: int, wert: Time) +setTimestamp(index: int, wert: Timestamp)
Nitty Gritty • Take that!
«interface» PreparedStatement
+wasNull() +getBoolean(index: int) +getByte(index: int) ... +getDate(index: int) +getBlob(index: int) +getString(index: int) +getTime(index: int) +getTimestamp(index: int)
Bild 7.37: Statement-Interfaces
Methoden Für lesende Zugriffe (Queries) benutzt man in der Regel die Methode executeQuery(sql), die ein ResultSet-Objekt zurückliefert (eine Reihe von Datensätzen). Bei schreibenden Zugriffen, also den SQL-Befehlen INSERT, UPDATE und DELETE, verwendet man executeUpdate(sql). Auch Statements müssen mit close() abgeschlossen werden. Datenspeicherung und -kommunikation
215
Sandini Bib
PreparedStatements eignen sich für immer wiederkehrende Abfragen, die sich nur in den Parametern der SQL-Abfrage unterscheiden. Diese gibt man als Fragezeichen (»?«) in der SQL-Anweisung an. Bevor die Abfrage ausgeführt wird, können Sie die Parameter mit verschiedenen set-Anweisungen belegen, zum Beispiel setByte(index, wert), wobei der Index die Nummer des zu belegenden Parameters darstellt. Ebenso funktioniert die Parameterbelegung bei CallableStatements. Diese liefern jedoch in der Regel kein ResultSet zurück, sondern besitzen eine Reihe von Ausgabeparametern, die man mit get-Anweisungen abfragen kann. wasNull() gibt an, ob der Parameter nicht belegt ist. Beispiel
Nitty Gritty • Take that!
7
Statement stmt = con.createStatement(); String query = "SELECT * FROM KONTO"; ResultSet rs = stmt.executeQuery(query); PreparedStatement pStmt = con.prepareStatement("SELECT * FROM KONTO WHERE NR = ?"); pStmt.setInt(1, 643425600); ResultSet pRs = pStmt.executeQuery();
7.6.6
ResultSet-Interfaces
seit
SE
ME
EE
1.0
x
-
x
ResultSets beinhalten die Ergebnisse einer Datenbankabfrage. In der Regel sind dies mehrere Datensätze, die man mittels eines Cursors durchlaufen kann. Das Interface wurde durch Erweiterungen in JDBC 2.0 stark aufgewertet (siehe unten).
216
Datenbankzugriffe – java.sql
Sandini Bib
«interface» ResultSet
«interface» ResultSetMetaData
+next() +getBoolean(index: int/String) +getByte(index: int/String) ... +getDate(index: int/String) +getBlob(index: int/String) +getString(index: int/String) +getTime(index: int/String) +getTimestamp(index: int/String) +wasNull() +getMetaData() +findColumn(name: String) +previous() +first() +last() +beforeFirst() +afterLast() +getType() +insertRow() +refreshRow() +deleteRow() +updateXXX(index: int/String, wert:...) ...
+getColumnCount() +getColumnType(index: int) +getColumnName(index: int) +getColumnLabel(index: int) +getTableName() +getSchemaName() +isNullable(index: int) +isReadOnly(index: int) +isWritable(index: int) +getPrecision(index: int) +getScale(index: int) ...
Bild 7.38: Das Interface ResultSet
Methoden
Mit JDBC 2.0 wurde ResultSet um navigierbare Cursor erweitert. Sie können sich nun mittels first(), last(), previous() und weiteren Methoden frei in der Ergebnismenge bewegen. Außerdem ist es nun möglich, einzelne Datensätze zu verändern und in die Datenbank zurückzuschreiben.
Datenspeicherung und -kommunikation
217
7 Nitty Gritty • Take that!
Zunächst haben Sie Zugriff auf den ersten Datensatz, mit next() können Sie den Cursor jeweils einen Satz weitersetzen – und zwar so lange, wie next() true zurückliefert. Die einzelnen Werte eines Datensatzes können Sie mit verschiedenen get-Anweisungen abfragen, zum Beispiel getByte(index), wobei der Index entweder die Nummer der Ergebnisspalte oder deren Name darstellt. wasNull() gibt an, ob der Wert in der zuletzt abgefragten Spalte des aktuellen Datensatzes nicht belegt ist. Wenn Sie weitere Informationen über eine Spalte benötigen, wie zum Beispiel deren Namen oder SQL-Typ (siehe Abschnitt 7.6.7), können Sie mit getMetaData() diese Informationen als ResultSetMetaData erhalten.
Sandini Bib
7.6.7 SQL-Datentypen SQL hat eine Reihe von Standarddatentypen, die denen von Java nicht 1:1 entsprechen. So gibt es zum Beispiel den Typ VARCHAR, der ein variables Zeichenfeld mit einer Maximalzahl an Zeichen bezeichnet oder CHAR ein Zeichenfeld fester Länge. Die Klasse Types beinhaltet Konstanten für die SQL-Typen. Daneben gibt es die Klassen Date, Time und Timestamp für Zeitwerte. In JDBC 2.0 gibt es weitere Klassen für strukturierte und generische Typen. Statement
Nitty Gritty • Take that!
7
+NULL: int +CHAR: int +VARCHAR: int +SMALLINT: int +INTEGER: int +BIGINT: int +DECIMAL: int +NUMERIC: int +FLOAT: int +DOUBLE: int +DATE: int +TIME: int +TIMESTAMP: int +BLOB: int +CLOB: int +STRUCT: int +ARRAY: int +JAVA_OBJECT: int
java.util.Date
java.sql. Date
java.sql. Time
java.sql. Timestamp
Bild 7.39: SQL-Typen unter JDBC 1.0
Die Datentypen können Sie mit der Methode getColumnType() eines Objekts vom Typ ResultSetMetaData als Zahl abfragen. Da zum Beispiel die Zahl »12« als Datentyp nicht besonders intuitiv zu verstehen ist, empfiehlt es sich, die Konstanten aus Types zu benutzen, in diesem Fall Types.VARCHAR (siehe Abbildung 7.39). Die Klassen für die SQL-Zeit- und Datumstypen sind kleine Wrapper um die Standardklasse java.util.Date (siehe Abschnitt 10.1.2).
218
Datenbankzugriffe – java.sql
Sandini Bib
«interface» Blob
«interface» Array
«interface» Struct
+length() +getBinaryStream() +getBytes(pos : long, laenge : int) +position(muster : Blob/byte[], start : long)
«interface» Ref
«interface» Clob
«interface» SQLData
+length() +getAsciiStream() +getCharacterStream() +getSubString(pos : long, laenge : int) +position(muster : Clob/String, start : long)
«interface» SQLInput
«interface» SQLOutput
Bild 7.40: Neue SQL-Typen unter JDBC 2.0
Seit JDBC 2.0 (Abbildung 7.40) gibt es Unterstützung für einige modernere SQL-Typen. BLOBs (Binary Large OBjects) und CLOBs (Character Large OBjects) sind für große Datenmengen gedacht. So könnte ein BLOB beispielsweise eine Grafik enthalten und ein CLOB ein HTML- oder XML-Dokument.
SQLData, SQLInput und SQLOutput sind generische Interfaces für die Umwandlung von in der Datenbank definierten »User-defined Types« in Java-Datentypen. Diese werden Sie normalerweise nicht selbst benutzen.
7.7
Servlets – javax.servlet
seit
SE
ME
EE
1.0
-
-
x
Servlets sind eine serverseitige Anwendungsarchitektur (siehe Vergleich in Kapitel 13), die eine einfache Kommunikation zwischen einfachen Clients (meist Webbrowser) und einem Web (Application) Server. Servlets werden in der Regel in Zusammenarbeit mit dem HTTP-Protokoll benutzt, um dynamisch HMTL-Seiten zu generieren. Ähnlich wie Datenspeicherung und -kommunikation
219
7 Nitty Gritty • Take that!
Array und Struct stehen für strukturierte Typen und Ref für persistente Referenzen darauf. Diese Typen finden man in der Praxis allerdings recht selten vor.
Sandini Bib
Enterprise JavaBeans benötigen Servlets eine spezielle Infrastruktur auf dem Webserver, auf dem sie aktiv sind, in diesem Fall ist es die Servlet Engine (siehe Abbildung 7.41). Java-Webserver kompilierte Java-Klasse JAR-Dateien JSP-Dateien
Java Servlet JSPCompiler
Klassenbib. Java VM
Servlet Engine HMTL-Seiten statische HMTL-Seiten
Webserver Betriebssystem Browser
Hardware
Bild 7.41: Aufbau eines Java-Webservers
Nitty Gritty • Take that!
7
Die für Servlets notwendigen Bibliotheken javax.servlet und javax. servlet.http sind in der Java 2 Enterprise Edition (siehe 5.2.3) enthalten, können aber auch als Extensions oder in Form von Servlet-Engine-Produkten im Web Application Server bereitgestellt werden. Eine Erweiterung stellen die sogenannten Java Server Pages (JSPs) dar (javax.servlet.jsp), die den Einsatz von Java als serverseitige Skriptsprache für Webserver ermöglichen. Die in HTML-Seiten eingebetteten Java-Befehlen werden zur Laufzeit in Servlets umgewandelt. Nachdem leider der Platz im diesem Buch zur Darstellung dieser Serverarchitektur nicht ausreicht, wurde die Beschreibung auf die Webseite http://www.nitty-gritty.de verlegt. Dort können Sie eine etwas ausführlichere Darstellung zum Thema Servlets und Java Server Pages sowie Beispiele finden. Wenn Sie sich ausführlicher mit dem Thema beschäftigen möchten, empfehle ich Ihnen die SunDokumentation (http://java.sun.com/products/servlets) und ein gutes Buch (z. B. das von Peter Roßbach »Java Server und Servlets«, ISBN 3-8273-1408-9).
220
Servlets – javax.servlet
8
Sandini Bib
Oberflächenprogrammierung
Für grafische Bedienoberflächen gibt es in Java zwei Alternativen. Das Abstract Windowing Toolkit (AWT) ist seit den Anfängen von Java als Sammlung von einfachen Kontrollelementen und primitiven Grafikfunktionen enthalten (siehe Abbildung 8.1). Seit Java 2 gibt es mit Swing eine größere Sammlung, die auch moderne Kontrollelemente wie Tabellen oder Baumansichten bereitstellt (siehe Abbildung 8.2).
8 Nitty Gritty • Take that!
Bild 8.1: Eine einfache AWT-Oberfläche
Bild 8.2: Eine Swing-Anwendung Oberflächenprogrammierung
221
Sandini Bib
8.1
Überblick
Die folgende Übersicht (Abbildung 8.3) zeigt die Aufgabengebiete der einzelnen Packages, um Ihnen einen Anhaltspunkt zu geben, wo Sie nach speziellen Klassen und Interfaces Ausschau halten müssen, denn es ist unmöglich, in diesem Buch alle Klassen in diesem Bereich vorzustellen (mehr als 1100). GUI-Bibliotheken
Kontrollelemente
Ereignisbehandlung
- java.awt - javax.swing - javax.swing.* - java.awt.im
- java.awt - java.awt.event - javax.swing. event
Farben
Zwischenablage
- java.awt - java.awt.color
Nitty Gritty • Take that!
8
- java.awt. datatransfer
2D-Grafik/ Schrift
Drucken
- java.awt - java.awt - java.awt.font - java.awt.print - java.awt.geom
Drag & Drop
Bilder
- java.awt.dnd - java.awt.image - java.awt.image. renderer
Bild 8.3: Überblick über die GUI-Packages
Packages Die Grafikklassen sind auf mehrere Packages verteilt: T
java.awt enthält die wichtigsten Klassen zur Erzeugung grafischer Bedienoberflächen wie Fenster, Farben, Schriften, 2D-Grafiken und zum Drucken.
T
java.awt.event beinhaltet Klassen zur Ereignisbehandlung. java.awt.font (seit Java 2) brauchen Sie nur, wenn Sie Schriften analysieren oder transformieren wollen.
T T
java.awt.geom (seit Java 2) erweitert die 2D-Grafikfunktionalität aus java.awt um Formen und Flächen.
T
java.awt.print (seit Java 2) vereinfacht die Einstellung von Druckoptionen für mehrseitige Dokumente (im Vergleich zu java.awt. PrintJob). Alternativ gibt es seit Java 2 v1.4 mit dem Package javax.print noch eine mächtigere Programmierschnittstelle für den Dokumentendruck.
222
Überblick
Sandini Bib
java.awt.color (seit Java 2) unterstützt zusätzliche Farbmodelle neben RGB und HSB in der Klasse java.awt.Color.
T
java.awt.datatransfer dient zur Nutzung der Zwischenablage.
T
java.awt.dnd (seit Java 2) hilft Ihnen bei der Erstellung von Oberflächen, die Drag & Drop unterstützen.
T
java.awt.image stellt Klassen zur Darstellung von Bitmaps bereit. java.awt.im (seit Java 2) dient zur Texteingabe in asiatischen Schriftzeichen.
T T
javax.imageio (seit Java 2 v1.4) dient zur Verwaltung von Bildern (Transformieren, Erzeugen von Thumbnails...)
T
javax.swing (seit Java 2) enthält moderne Kontrollelemente, zum Teil befinden sich die notwendigen Klassen in weiteren Subpackages (z. B. javax.swing.border für Umrandungen, javax.swing. text für formatierte Dokumente, javax.swing.table für Tabellen, javax.swing.tree für die Baumansicht usw.)
8.1.1 Vergleich AWT und Swing Zwischen AWT und Swing gibt es viele Gemeinsamkeiten. Wie stehen die beiden in Beziehung und für welchen Zweck ist welche Bibliothek besser geeignet? Swing basiert auf den Fenster- und Grafikklassen des AWT und nutzt dessen Ereignisbehandlung. Der Hauptunterschied ist, dass AWT bei der Darstellung von Kontrollelementen (Schaltflächen, Checkboxen, Listen) auf das Fenstersystem des unterliegenden Betriebsystems aufsetzt und dessen Grafikbibliotheken nutzt (Heavy-weight Komponenten). Swing hingegen zeichnet alle Kontrollelemente selbst (Light-weight Komponenten). Eine Folge hiervon ist, dass AWT nur den kleinsten gemeinsamen Nenner aller Betriebssystem-Kontrollelemente darstellen kann, Swing hingegen praktisch alle gebräuchlichen. AWT-Oberflächen sind schnell in der Darstellung, relativ schnell zu implementieren und werden in allen Browsern unterstützt. Je nach Betriebssystem gibt es jedoch kleine Darstellungsunterschiede, so dass ein Test einer Anwendung zumindest auf einem Windows- und einem UNIX-System zu empfehlen ist. Oberflächenprogrammierung
223
8 Nitty Gritty • Take that!
T
Sandini Bib
Da Swing erst seit Java 2 im Standard enthalten ist, wird diese Bibliothek in vielen Browsern noch nicht direkt unterstützt (siehe Hinweis unten!). Swing eignet sich besonders für komplexe Oberflächen, in denen zum Beispiel Baumansichten, Tabellen oder Notizbuchkontrollelemente für die strukturierte Darstellung strukturierter Daten benutzen werden. Durch eine saubere Trennung von Darstellung und Datenhaltung werden auch komplexe Bedienoberflächen handhabund wartbar. Da die konkrete Darstellungsform (z. B. Windows Look & Feel) auf allen Plattformen durch Swing selbst gezeichnet wird, sieht jede Oberfläche auf allen Betriebssystemen gleich aus. Allerdings haben Swing-Oberflächen einen hohen Speicherbedarf und sind gerade auf älterer Hardware recht langsam.
Vorteile
AWT
Swing
einfacher Entwurf
maximale Flexibilität
schnelle Ausgabe zur Laufzeit
mehrere Darstellungsvarianten für ein Datenmodell leichte Austauschbarkeit der Modelle, Views und Renderer
Nitty Gritty • Take that!
8
plattformunabhängiges »Look & Feel«, da kein Native-Code enthalten beliebige transparente Darstellung (runde Buttons) und komplexere Kontrollelemente (Tabellen, Baumdarstellung) Nachteile
224
eingeschränkte Darstellungsmöglichkeiten auf undurchsichtige, rechteckige Systemkomponenten
komplexe Programmierung
doppelte Verwaltung von Daten in Daten- und Oberflächenobjekten
langsame Ausgabe zur Laufzeit
Überblick
Sandini Bib
Als Daumenregel lässt sich sagen: Wenn Sie keine Swing-Funktionalität benötigen, setzen Sie aufgrund der schnelleren Darstellung AWT ein. Dies gilt insbesondere für Applets, da Swing in Browsern noch kaum unterstützt wird. In der Praxis stößt man jedoch schon bei relativ einfachen Anwendungen an die Grenzen von AWT. Browser wie Netscape Communicator 4.x oder Internet Explorer 4/5 beinhalten noch Java 1.1 und damit keine Swing-Unterstützung. Sie können zwar die benötigten Swingklassen auch auf dem Webserver bereitstellen, aber bereits in gepackter Form sind dies ca. 2 Megabyte, die über das Netz gezogen werden müssten.
Netscape Communicator 6 beinhaltet bereits eine aktuelle virtuelle Maschine und setzt auf Wunsch auch direkt auf einem installierten JRE auf, so dass für diesen Browser kein Plug-In notwendig ist.
8.2 Einfache Oberflächen – java.awt Nahezu alle Programme mit grafischer Benutzerführung importieren das AWT (Abstract Windowing Toolkit), das Klassen für Fenster und Kontrollelemente (siehe dieser Abschnitt), zur Ereignisbehandlung (siehe Abschnitt 8.4) sowie für Grafiken und zum Drucken (siehe Abschnitt 8.5) enthält.
Oberflächenprogrammierung
225
8 Nitty Gritty • Take that!
Deshalb empfiehlt es sich, das Java Plug-In einzusetzen (siehe Kapitel 1.3), das sich im Lieferumfang des JRE (seit Version 1.2) befindet. Dieses klickt sich bei speziell dafür markierten HTML-Seiten ein und sorgt dafür, das eingebettete Applets in der virtuellen Maschine des JRE statt in der des Browser ausgeführt werden. Damit kann das Applet auch Java-2-Packages wie Swing ohne Download einsetzen.
Sandini Bib java.awt, java.awt.event & java.awt.datatransfer Klassen
Interfaces - LayoutManager - Adjustable - ItemSelectable - PrintGraphics - Shape - Paint - Composite - Stroke - Transparency - Transferable - ActionListener - FocusListener - WindowListener - KeyListener - MouseListener - MouseMotionL. - TextListener - ItemListener - AdjustmentL. ...
- Event - AWTEvent - ActionEvent - FocusEvent/Ad. - WindowEvent/Ad. - KeyEvent/Adapter - MouseEvent/Ad. - TextEvent - ItemEvent - AdjustmentEvent - EventQueue ... 2D-Grafik - Graphics - Graphics2D - Image - Color - Font - FontMetrics - Dimension - Point - Rectangle - Polygon - BasisStroke - GradientPaint - TexturePaint
8 Nitty Gritty • Take that!
Ereignisbehandlung
Kontrollelemente
Fenster
- Component - Button - Label - TextField - TextArea - CheckBox - Choice - List - Scrollbar - Canvas
- Container - Frame - Dialog - Window - Panel - FileDialog - ScrollPane
LayoutManager
Bilder
Menüs - MenuBar - MenuItem - Menu - PopupMenu - MenuShortcut ...
Sonstiges
- Cursor - BorderLayout - Image - FlowLayout - MediaTracker - Toolkit - PrintJob - GridLayout - Clipboard - GridBagLayout - DataFlavor - CardLayout - AWTException - AWTError ...
Bild 8.4: Die Klassen der AWT-Packages
Das Package java.awt beinhaltet die für den Java-Neuling wichtigsten Klassen und Interfaces, zum Beispiel Fenster, grafische Kontrollelemente sowie Grafikprimitive, also das Zeichnen von Linien und Flächen (siehe Abbildung 8.4). Als Ergänzung benutzt man die java.awt.event-Klassen und Listener-Interfaces zur Behandlung von Ereignissen und java.awt.datatransfer für die Benutzung der Zwischenablage. Die Programmierung von grafischen Oberflächen ohne Hilfsmittel erfordert ein gutes Vorstellungsvermögen. Außerdem ist die Programmierung aufgrund des Volumens und der Routinetätigkeiten fehleranfällig und der produzierte Code schwer wartbar. Deshalb empfiehlt sich der Einsatz von GUI-Editoren, wie sie in vielen integrierten Entwicklungsumgebungen zu finden sind. 226
Einfache Oberflächen – java.awt
Sandini Bib
8.2.1
Die AWT-Basisklasse Component
seit
SE
ME
EE
1.0
x
-
x
Alle Kontrollelemente sind von einer gemeinsamen abstrakten Basisklasse abgeleitet, die die Funktionalität für das Aussehen, die Positionierung, die Ausgabe und das Laufzeitverhalten (Fokus, Mauszeiger, Sichtbarkeit) bereitstellt. Von dieser Klasse sind konkrete Klassen wie Frame (Abschnitt 8.2.10) oder Button (Abschnitt 8.2.3) abgeleitet. Component {abstract}
+get/setName(name: String) +getParent() +contains(x: int, y: int) +setVisible(sichtbarkeit: boolean) +isVisible() +setEnabled(aktiv: boolean) +isEnabled() +isShowing() +get/setForeground(c: Color) +get/setBackground(c: Color) +get/setFont(schriftart: Font) +get/setBounds(x: int, y: int, breite: int, höhe: int) +getWidth/Height() +getX/Y() +get/setSize(...) +get/setMinimum/MaximumSize(...) +get/setLocation(...) +get/setCursor(mauszeiger: Cursor) +paint(ziel: Graphics) +repaint() +paintAll(ziel: Graphics) +update(ziel: Graphics) +print(ziel: Graphics) +printAll(ziel: Graphics) +hasFocus() +requestFocus() +transferFocus() +addFocusListener(l: FocusListener) +addKeyListener(l: KeyListener) +addMouseListener(l: MouseListener) +get/setLocale(gebietsschema: Locale) +add(menü: PopupMenu) ...
Nitty Gritty • Take that!
8
Bild 8.5: Die Klasse Component Oberflächenprogrammierung
227
Sandini Bib
Methoden Alle Komponenten sollten mittels setName() benannt werden, um eine leichte Identifikation (z. B. beim Debuggen) zu ermöglichen. Komponenten sind meist ineinander verschachtelt. So könnte beispielsweise ein Fenster (Klasse Frame) eine Reihe von Layout-Regionen (Panel) enthalten, von denen eine wiederum einige Schaltflächen (Button) umfasst. Komponenten, die andere Kontrollelemente beinhalten, nennt man Container. Bei jedem Kontrollelement können Sie dessen eindeutigen umgebenden Container mit Hilfe der Methode getParent() ermitteln.
Nitty Gritty • Take that!
8
Mittels setVisible(true) machen Sie ein unsichtbares Kontrollelement sichtbar, mit setEnabled(true) schalten Sie dessen Bedienungsfunktionalität ein. Mit isVisible() bzw. isEnabled() können Sie den aktuellen Zustand erfragen. Die meisten Kontrollelemente sind standardmäßig sichtbar und aktiv, aber zum Beispiel Fenster sind zunächst unsichtbar und müssen durch den Programmierer angezeigt werden. Bei Menüs, Checkboxes und Buttons wird ein inaktiver Zustand optisch angezeigt. Weitere Funktionen dienen dazu, die Vorder- und Hintergrundfarbe oder die Schriftart einzustellen, wenn dies bei dieser Art von Kontrollelement Sinn macht und durch das Betriebssystem unterstützt wird. Unter Windows sind beispielsweise alle Schaltflächen grau (background) mit schwarzer Schrift (foreground). Zur Ermittlung und zum Setzen der Größe und Position gibt es eine ganze Reihe von Methoden, die selbsterklärend sind. Mit setCursor() können Sie die Art des Mauszeigers wählen, der angezeigt wird, wenn sich dieser über dem Kontrollelement befindet. Es gibt unter anderem die Zeigerform (Cursor.DEFAULT_CURSOR), einen Texteingabestrich (Cursor.TEXT_CURSOR), ein Wartesymbol (Cursor.WAIT_ CURSOR) und ein Fadenkreuz (Cursor.CROSSHAIR_CURSOR). Setzt man den Wert nicht, wird der Mauszeiger des Parent-Containers (siehe Abschnitt 8.2.9) benutzt. Die Klasse Cusor wird in Abschnitt 8.5.6 besprochen.
228
Einfache Oberflächen – java.awt
Sandini Bib paint() gibt das Kontrollelement am Bildschirm aus, print() auf anderen Geräten. Standardmäßig sind die beiden Varianten identisch. Die Methoden paintAll() und printAll() tun dies auch für alle Subkomponenten. Die paint-Methoden werden allerdings normalerweise nicht vom Entwickler aufgerufen, sondern vom Java-Laufzeitsystem. repaint() hingegen erzwingt ein Neuzeichnen aus dem Code heraus und update() löscht die Fläche und ruft seinerseits paint() auf.
Mit hasFocus() können Sie ermitteln, ob das aktuelle Kontrollelement den Eingabefokus hat. Wenn nicht, können Sie mit requestFocus() diesen erhalten. transferFocus() schalten ihn auf das nächste Kontrollelement weiter. Daneben gibt es noch eine Reihe von Listener-Funktionen, über die Sie die Ereignisbehandlung (siehe Abschnitt 8.4) für Fokus-, Tastaturoder Mausereignisse einschalten können. Schließlich könne Sie sogar pro Kontrollelement ein eigenes Gebietsschema bzw. eine Sprache wählen (setLocale()) oder ein PopupMenü hinzufügen (add()).
Die in Klammern angegebenen Zahlen beziehen sich auf Erläuterungen, die Sie im Anschluss an die Übersicht finden. 1. AWT-Beans Beschreibung
Spezielle Properties
Methoden
Events
Button Schaltfläche, Aktionsknopf
label action Command enabled
actionPerformed
Oberflächenprogrammierung
229
8 Nitty Gritty • Take that!
8.2.2 Übersicht über die AWT-Kontrollelemente Die folgende Tabelle stellt eine Übersicht über die wichtigsten Funktionen der AWT-Kontrollelemente dar, die zusammen mit der Standardfunktionalität aller Components (siehe Abschnitt 8.2.1) vermutlich 90% aller Anwendungsfälle abdecken.
Sandini Bib Beschreibung
Spezielle Properties
Methoden
Events
Checkbox Option
label state checkboxGroup (1)
CheckboxGroup zur Gruppierung von vorhandenen Checkboxen als Optionsschalter
this (1) selectedCheckbox
Label statischer Text
text alignment (LEFT/RIGHT/ CENTER)
setText(String)
text selectedText selectionStart/End columns echoChar (2) editable
setText(String) selectAll() select(int, in)
textValueChanged keyTyped keyPressed keyReleased
text selectedText selectionStart/End columns/rows editable
setText(String) selectAll() select(int, int) append(String) insert(String, int)
textValueChanged keyTyped keyPressed keyReleased
items itemCount selectedItem[s] selectedIndex[es] multipleMode (3) rows
add/addItem(String) add/addItem(String, int) getItem(int) remove(...) removeAll() select/isSelected(int)
itemStateChanged
TextField einzeilige Texteingabe
Nitty Gritty • Take that!
8 TextArea mehrzeilige Texteingabe
List Liste von Strings
230
Einfache Oberflächen – java.awt
Sandini Bib Beschreibung
Spezielle Properties
Methoden
Events
selectedItem selectedIndex
add/addItem(String) remove(...) removeAll() select(...)
itemStateChanged
value minimum/maximum block-/unitIncrement orientation (VERTICAL/ HORIZONTAL) visibleAmount
setValue(int) setValues (int, int, int, int)
adjustmentValueChanged
layout
add(Component) getComponent[At](...) getComponents() getComponentCount() remove(...) removeAll()
componentAdded componentRemoved
setVisible (boolean) dispose() repaint() add/remove(...) getFocusOwner()
windowOpened windowClosing windowActivated windowDeactivated
Choice Drop-down-Liste
Scrollbar Verschiebebalken (horizontal und vertikal)
Panel
ScrollPane Teilfenster mit Schiebeleisten
scrollBarDisplayPolicy scrollPosition
Frame Anwendungsfenster
title layout resizable size iconImage menuBar
8 Nitty Gritty • Take that!
Panel, Fensterbereich für Layout
Oberflächenprogrammierung
231
Sandini Bib Beschreibung
Spezielle Properties
Methoden
Events
title layout resizable size modal
setVisible (boolean) show() dispose() repaint() add/remove(...) getFocusOwner()
windowOpened windowClosing windowActivated windowDeactivated
directory file mode (LOAD/SAVE) fileNameFilter title modal
setVisible (boolean) dispose()
menuCount helpMenu
add/remove(...) shortcuts() getShortcutMenuItem(MenuShortcut)
label actionCommand shortcut itemCount
add/insert/ remove(...) getItem(int) show(Component, int, int) add/insertSeparator()
actionPerformed
label actionCommand shortcut itemCount
add/insert/ remove(...) getItem(int) add/insertSeparator()
actionPerformed
Dialog Dialogfenster, Meldefenster
FileDialog Dateiauswahl
MenuBar
Nitty Gritty • Take that!
8
Menüleiste
PopupMenu Kontextmenü
Menu Menü in einer MenuBar oder als Untermenü
232
Einfache Oberflächen – java.awt
Sandini Bib Beschreibung
Spezielle Properties
Methoden
Events
MenuItem Menüeintrag
label actionCommand shortcut
actionPerformed
label actionCommand shortcut state
actionPerformed itemStateChanged
Checkbox-MenuItem Menüeintrag
Anmerkungen
8.2.3
Die Klasse Button
seit
SE
ME
EE
1.0
x
-
x
Bild 8.6: Darstellung eines Buttons
Oberflächenprogrammierung
233
8 Nitty Gritty • Take that!
1. Um mehrere Checkboxes zu einer Gruppe zusammenzufassen, in der jeweils nur ein Optionskästchen gewählt werden kann (Radiobuttons), weist man dem checkboxGroup-Attribut jeder Checkbox eine Referenz vom Typ CheckboxGroup zu. Das Aussehen der Checkboxes ändert sich dann zur Laufzeit (runde Knöpfe statt quadratische). 2. Durch Setzen des Properties echoChar auf ein Zeichen (z. B. »*«) kann ein TextField für die Eingabe von Passwörtern benutzt werden. Anstatt der eingegebenen Zeichen wird der echoChar verwendet, das Passwort kann auch nicht in das Clipboard übernommen werden. Achtung: Die Definition eines echoChar kann nicht mehr rückgängig gemacht werden, das heißt, wenn das Feld doch nicht als Passwortfeld genutzt werden soll, muss es komplett ersetzt werden. 3. Durch Einschalten des multipleMode wird die Selektion von mehreren Listeneinträgen ermöglicht.
Sandini Bib
Buttons, häufig auch Schaltflächen oder Pushbuttons genannt, sind rechteckige Knöpfe, die eine Aktion auslösen, wenn der Benutzer sie anklickt. Component
Button label: String actionCommand: String <> +Button() +Button(aufschrift: String) <<Methoden>> +addActionListener(l: ActionListener) ...
Bild 8.7: Die Klasse Button
Methoden und Attribute Die wichtigsten Attribute sind das label, also die Aufschrift, und actionCommand, ein String, der den Knopf beim ActionEvent charakterisiert
Nitty Gritty • Take that!
8
(wird mit dem Ereignisobjekt mitgeschickt, siehe Abschnitt 8.4). Der Event mit der zugehörigen Methode actionPerformed() wird ausgelöst, wenn der Button gedrückt wird. Wird actionCommand nicht besetzt, so erhält er den Wert von label; bei mehrsprachigen Oberflächen kann es aber sinnvoll sein, dass sich die beiden Werte unterscheiden. Beide Attribute werden – wie bei allen AWT-Kontrollelementen – mittels get- bzw. set-Methoden gelesen oder verändert. Wie im Abschnitt über Ereignisbehandlung beschrieben (Abschnitt 8.4), müssen alle Klassen, die benachrichtigt werden sollen, wenn der Button gedrückt wurde, mit addActionListener() einen Listener registrieren. Daneben stehen natürlich auch alle allgemeinen Methoden zur Verfügung, die in der Superklasse java.awt.Component definiert sind (siehe Abschnitt 8.2.1). Beispiel Button okButton = new Button("OK"); okButton.setActionCommand("OK"); okButton.addActionListener(eventHandler);
234
Einfache Oberflächen – java.awt
Sandini Bib
8.2.4
Die Klasse Checkbox
seit
SE
ME
EE
1.0
x
-
x
Bild 8.8: Darstellung von Checkboxen
Die Checkbox repräsentiert eine Option, die durch Anklicken eines Kästchens an- oder abgewählt werden kann. Eine selektierte Checkbox wird durch ein kleines Häkchen symbolisiert. Component
Checkbox
CheckboxGroup
label: String state: boolean checkboxGroup: CheckboxGroup <> +Checkbox() +Checkbox(aufschrift: String) +Checkbox(aufschrift: String, zustand: boolean) +Checkbox(aufschrift: String, zustand: boolean, radiobuttonGruppe: checkboxGroup) <<Methoden>> +addItemListener(l: ItemListener) ...
selectedCheckbox: Checkbox
Bild 8.9: Die Klassen Checkbox und CheckboxGroup
Methoden und Attribute Die Checkbox besitzt wie der Button ein label-Property, darüber hinaus ist der Zustand state (also an oder aus) wichtig. Eine Änderung des Zustandes wird durch einen ItemEvent (itemStateChanged()) signalisiert. Häufig reagiert man jedoch nicht sofort auf eine Änderung, sondern fragt den aktuellen Zustand einer Option erst beim Starten einer Aktion ab.
Oberflächenprogrammierung
235
Nitty Gritty • Take that!
8
Sandini Bib
Die Klasse CheckboxGroup Checkboxen können auch als Radio-Buttons verwendet werden, also für eine Reihe von sich gegenseitig ausschließenden Optionen. Der Zuordnung von Checkboxen in eine solche Gruppe dient das Property checkboxGroup in jeder Checkbox. Zur Laufzeit ändert sich dann die Darstellung der Kontrollelemente (siehe Abbildung 8.8). Mit Hilfe der CheckboxGroup kann die momentan selektierte Option abgefragt werden. Beispiel CheckboxGroup optionen = new CheckboxGroup(); Checkbox option1 = new Checkbox("Bildschirmausgabe", true, optionen); Checkbox option2 = new Checkbox("Druckausgabe", false, optionen); [...] boolean druck = option2.getState(); Checkbox ausgewaehlt = optionen.getSelectedCheckbox();
8.2.5
Nitty Gritty • Take that!
8
Die Klasse Label
seit
SE
ME
EE
1.0
x
-
x
Bild 8.10: Darstellung von Labels
Ein Label ist ein statischer Text, der zur Beschriftung innerhalb von Fenstern verwendet wird. Component
Label text: String alignment: int +Label() +Label(aufschrift: String) +Label(aufschrift: String, ausrichtung:int)
Bild 8.11: Die Klasse Label
236
Einfache Oberflächen – java.awt
Sandini Bib
Methoden und Attribute Der dargestellte Text steht im Attribut text und ist mit der Methode setText() modifizierbar. Die Ausrichtung kann linksbündig (Voreinstellung, Label.LEFT), rechtbündig (Label.RIGHT) oder zentriert (Label.CENTER) gewählt werden. Beispiel Label beschriftung = new Label("Ein Label"); beschriftung.setAlignment(Label.CENTER);
Die Klassen TextField und TextArea
seit
SE
ME
EE
1.0
x
-
x
Bild 8.12: Darstellung eines Textfeldes und einer TextArea
8
Ein TextField ist ein einzeiliges, eine TextArea ein mehrzeiliges Eingabefeld. Beide sind von der gemeinsamen Basisklasse TextComponent abgeleitet, die Funktionalität wie Selektion oder Editieren bereitstellt. Ein TextField kann auch für maskierte Felder benutzt werden, etwa für die Eingabe von Passwörtern. Allerdings kann man keine Masken definieren (z. B. nur Ziffern) und es gibt keine Festlegung auf eine fixe Eingabelänge. Methoden und Attribute Wie beim Label gibt es ein Attribut text. Dieser Text ist bei Eingabefeldern in der Regel editierbar, diese Funktion können Sie aber per setEditable(false) abschalten. Mit verschiedenen select-Methoden können Sie einen Textbereich markieren bzw. die Markierung abfragen. Die caretPosition gibt die Position des Eingabecursors an. Wichtig ist beim TextComponents der TextEvent (textValueChanged()), mit dem eine Eingabeüberprüfung erfolgen kann, zum Beispiel, ob die eingegebenen Zeichen nummerisch sind oder ob eine vorgegebene Höchstlänge erreicht wurde.
Oberflächenprogrammierung
237
Nitty Gritty • Take that!
8.2.6
Sandini Bib
Component
TextComponent text: String caretPosition: int selectionStart: int selectionEnd: int +select(anfang: int, ende: int) +selectAll() +getSelectedText() +isEditable() +setEditable(editierbar: boolean) +addTextListener(l: TextListener)
TextField
Nitty Gritty • Take that!
8
columns: int echoChar: char <> +TextField() +TextField(vorgabetext: String) +TextField(darstellbareZeichen: int) <<Methoden>> +echoCharIsSet()
TextArea columns: int rows: int <> +TextArea() +TextArea(vorgabetext: String) +TextArea(zeilen : int, spalten : int) +TextArea(vorgabetext: String, zeilen: int, spalten: int, scrollbars: int) <<Methoden>> +append(text: String) +insert(text: String, position: int) +replaceRange(text: String, anfang: int, ende: int) +getScrollbarVisibility() ...
Bild 8.13: Die Klassen TextField und TextArea
Bei TextFields gibt es zudem das Attribut echoChar, mit dem festgelegt werden kann, ob der eingegebene Text sichtbar sein soll oder ob es sich beispielsweise um ein Passwort handelt und jedes eingegebene Zeichen durch ein Ersatzzeichen wie »*« ersetzt werden soll. columns bzw. rows gibt an, wie groß das Feld bei dynamischen Layouts
(siehe LayoutManager im Abschnitt 8.3) dimensioniert werden muss, damit die angegebene Zeichenzahl in der eingestellten Schriftart Platz findet. Beide Werte haben keinen Einfluss darauf, wie viele Zeichen tatsächlich eingegeben werden können.
238
Einfache Oberflächen – java.awt
Sandini Bib
Die mehrzeilige TextArea erlaubt es, den Text mittels append(), insert() und replaceRange() zu verändern. Außerdem werden bei Bedarf Scrollbalken eingeblendet, sobald der Text die festgelegte Größe horizontal oder vertikal überschreitet. Ob nicht benötigte Scrollbalken angezeigt werden, hängt von der Implementierung der virtuellen Maschine ab und kann – anders als bei Swing – durch den Programmierer beeinflusst werden. Die Scrollbalken können jedoch im Konstruktor auch teilweise oder komplett abgeschaltet werden (TextArea.SCROLLBARS_NONE, TextArea.SCROLL-BARS_ VERTICAL_ONLY, TextArea.SCROLLBARS_HORIZON-TAL_ONLY). Beispiel TextField passwortFeld = new TextField(); passwortFeld.setEchoChar("*"); TextArea editor = new TextArea(); editor.setText("Ein mehrzeiliges Textfenster."); editor.append("\nScrollbar werden bei \n" + "Bedarf eingeblendet"); editor.select(4, 29);
Die Klasse Scrollbar
seit
SE
ME
EE
1.0
x
-
x
Nitty Gritty • Take that!
8.2.7
8
Bild 8.14: Darstellung von Scrollbars
Die Scrollbar stellt einen Schieberegler (auch Scrollbalken) zur grafischen Auswahl von Zahlenwerten dar. Für alle Klassen, die einen Integer-Wert repräsentieren, der zwischen einem Minimum und einem Maximum liegen darf, gibt es das Interface Adjustable, das auch die Klasse Scrollbar implementiert.
Oberflächenprogrammierung
239
Sandini Bib
Component
Adjustable
Scrollbar +VERTICAL: int +HORIZONTAL: int orientation: int value: int minimum/maximum: int visibleAmount: int unitIncrement: int blockIncrement: int <> +Scrollbar() +Scrollbar(orientierung: int) +Scrollbar(orientierung: int, wert: int, sichtbareBreite: int, minimum: int, maximum: int) <<Methoden>> +setValues(wert: int, sichtbareBreite: int, minimum: int, maximum: int) +addAdjustmentListener(l: AdjustmentListener)
Nitty Gritty • Take that!
8
Bild 8.15: Die Klasse Scrollbar
Methoden und Attribute Scrollbars besitzen entweder eine horizontale (Scrollbar.HORIZONTAL) oder vertikale Orientierung (Scrollbar.VERTICAL). value ist der aktuell eingestellte Wert, visibleAmount der Bereich, den der graue Teil des Scrollbalkens verkörpert (siehe Abbildung 8.16). Wenn z. B. von einem Text 60% sichtbar sind, dann sollte dieser Wert auf (getMaximum() – getMinimum())*0.6 eingestellt werden. minimum und maximum können auch negative Werte annehmen. unitIncrement ist die Schrittweite, wenn auf die Pfeiltasten geklickt wird (Standard ist 1), blockIncrement diejenige für einen Mausklick auf den weißen Hintergrund des Scrollbalken (Standard ist 10).
240
Einfache Oberflächen – java.awt
Sandini Bib
Mit addAdjustmentListener() lässt sich der Listener für ein Ereignis registrieren, das beim Scrollen eintritt und auch die Information beinhaltet, auf welche Weise der Benutzer den eingestellten Wert verändert hat. minimum = 0
maximum = 100
value = 10
visibleAmount = 60
Bild 8.16: Attribute eines Scrollbalkens
Beispiel Scrollbar regler = new Scrollbar(Scrollbar.HORIZONTAL); regler.setValues(10, 60, 0, 100); regler.setBlockIncrement(5);
Die Klassen List und Choice
seit
SE
ME
EE
1.0
x
-
x
8
Bild 8.17: Darstellung von List und Choice
Zur Auswahl fester Elemente gibt es die beiden Kontrollelemente Choice und List. Die Klasse List ist eine Liste, die optional auch die Selektion mehrerer Einträge erlaubt, wohingegen bei der Choice genau ein Eintrag selektiert ist und die Auswahlliste nur auf Anforderung ausgeklappt wird (Drop-down). Es handelt sich aber nicht um eine Combobox, denn der Benutzer kann keine eigenen Werte eingeben. Beide Klassen implementieren das Interface ItemSelectable.
Oberflächenprogrammierung
241
Nitty Gritty • Take that!
8.2.8
Sandini Bib
Nitty Gritty • Take that!
8
Component
«interface» ItemSelectable
List
Choice
<> +List() +List(sichtbareZeilen: int) +List(sichtbareZeilen: int, mehrfachSelektion: boolean) <<Methoden>> +add(eintrag: String) +add(eintrag: String, index: int) +remove(i: String/int) +removeAll() +select(index: int) +deselect(index: int) +getItemCount() +getItem(index: int) +getItems() +getSelectedIndex(es)() +getSelectedItem(s)() +isIndexSelected(index: int) +setMultipleMode( mehrfachSelektion: boolean) +isMultipleMode() +addItemListener(l: ItemListener) ...
<> +Choice() <<Methoden>> +add(eintrag: String) +insert(eintrag: String, index: int) +remove(i: String/int) +removeAll() +select(index: int) +getItemCount() +getItem(index: int) +getSelectedIndex() +getSelectedItem() +addItemListener(l: ItemListener)
Bild 8.18: Die Klassen List und Choice
Methoden und Attribute In beiden Klassen gibt es die Methode add() zum Hinzufügen neuer Elemente sowie remove/removeAll() zum Löschen. Mit select() können Sie Elemente auswählen und mit getSelectedItem/Index() abfragen. Einige Methoden gibt es zweifach, einmal mit der Indexposition und einmal mit dem Eintrag als Parameter. getItemCount() liefert die Anzahl der Elemente und getItem(i) den i-te Eintrag.
242
Einfache Oberflächen – java.awt
Sandini Bib
Bei einem Objekt der Klasse List können Sie mit setMultipleMode() festlegen, ob nur ein Eintrag oder mehrere Werte gleichzeitig markiert werden können. In diesem Falle stehen Ihnen weitere Methoden für Mengen von Einträgen zur Verfügung, z. B. getSelectedItems/Indexes(). Falls bei der An- oder Abwahl eines Listenelements eine Reaktion erfolgen soll, kann der ItemEvent in der Methode itemStateChanged() eines registrierten Listeners verwendet werden (siehe Abschnitt 8.4). Beispiel List liste = new List(); liste.setMultipleMode(true); liste.add("Listeneintrag 1"); liste.add("Listeneintrag 2"); liste.add("Listeneintrag 3"); liste.select(0); liste.select(liste.getItemCount()-1);
Die Klasse Container
seit
SE
ME
EE
1.0
x
-
x
8
Container sind Fenster oder Fensterbereiche, in denen andere Kontrollelemente platziert werden können. Viele unterstützen LayoutManager, mit denen die plattformunabhängige Darstellung und Platzierung der enthaltenen Kontrollelemente gesteuert werden kann (siehe Abschnitt 8.3).
Oberflächenprogrammierung
243
Nitty Gritty • Take that!
8.2.9
Sandini Bib
Component
Container layout : LayoutManager +add(kontrollelement: Component) +add(kontrollelement: Component, layoutAttribute: Object) +remove(i: int/Component) +removeAll() +getComponentCount() +getComponents() +getComponent(index: int) +getComponentAt(x: int, y: int) +findComponentAt(x: int, y: int) +paint(ziel: Graphics) +paintComponents(ziel: Graphics) ...
Bild 8.19: Die Klasse Container
Nitty Gritty • Take that!
8
Methoden und Attribute Mit add() fügen Sie Kontrollelemente in einen Container ein, mit remove/ removeAll() können Sie sie wieder entfernen. getComponentCount() liefert die Anzahl der enthaltenen Kontrollelemente, getComponents() ein Array mit deren Referenzen. getComponentAt(x, y) gibt das enthaltene Kontrollelement an der angegebenen Position, findComponentAt(x, y) hingegen arbeitet rekursiv und liefert das oberste. 8.2.10 Die Klassen Frame und Dialog seit
SE
ME
EE
1.0
x
-
x
Bild 8.20: Darstellung von Frame und Dialog 244
Einfache Oberflächen – java.awt
Sandini Bib
Window ist eine Klasse, die ein rechteckiges Fenster darstellt. Die Klasse wird selten eigenständig genutzt, dient aber als gemeinsame Superklasse von Frame, einem Hauptfenster, und Dialog, einer abhängigen Dialogbox. Ein Frame-Objekt kann eine Menüleiste besitzen und ein Symbol, das erscheint, wenn das Fenster minimiert wird. Ein Dialog-Objekt besitzt diese Eigenschaften nicht, es kann aber modal in Bezug auf sein Hauptfenster sein, das bedeutet, es steht immer vor diesem Fenster, in das man erst zurückkehren kann, wenn man die Dialogbox geschlossen hat. Container
Window
+Window(parent: Window) +show() +isShowing() +dispose() +getFocusOwner() +getOwner() +getOwnedWindows() +getToolkit() +getLocale() +toBack() +toFront() +pack() +addWindowListener(l: WindowListener)
title: String menuBar: MenuBar iconImage: Image state: int +ICONIFIED: int +NORMAL: int <> +Frame() +Frame(titel: String) <<Methoden>> +setResizable(skalierbar: boolean) +isResizable()
Nitty Gritty • Take that!
Frame
8
Dialog title: String <> +Dialog(parent: Frame/Dialog) +Dialog(parent: Frame/Dialog, titel: String) +Dialog(parent: Frame/Dialog, titel: String, modal: boolean) <<Methoden>> +setResizable(skalierbar: boolean) +isResizable() +setModal(modal: boolean) +isModal()
Bild 8.21: Die Klassen Frame und Dialog Oberflächenprogrammierung
245
Sandini Bib
Methoden und Attribute Die wichtigste Methode ist die von Component geerbte Methode setVisible(true) (oder show()), da Fenster standardmäßig nicht sichtbar sind. dispose() macht nicht nur das Fenster unsichtbar, sondern gibt auch alle verbundenen Ressourcen frei. Dies ist wichtig, da der Lebensyzklus eines Fensters vom Betriebssystem gesteuert wird, das keine automatische Garbage Collection kennt. Häufig benutzt werden auch die WindowEvents und deren Methoden windowOpened() und windowClosing(), bei denen eine Initialisierung beziehungsweise eine Bereinigung durchgeführt werden kann. Wenn Sie möchten, dass sich eine Anwendung beim Schließen des Fensters (z. B. durch Klick auf den »X«-Knopf oder (Alt)+(F4)) beendet, müssen Sie bei der WindowEvent-Methode windowClosing() System.exit(0) aufrufen. Kurzschreibweise mit innerer Klasse (siehe Abschnitt »Ereignisbehandlung« 8.4): fenster.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) {System.exit(0);} });
Nitty Gritty • Take that!
8
Daneben stehen natürlich auch alle allgemeinen Methoden zur Verfügung, die in den Superklassen Component (siehe Abschnitt 8.2.1) und Container (siehe Abschnitt 8.2.9) definiert sind. Bei Container-Objekten wird häufig Gebrauch von LayoutManagern gemacht (Abschnitt 8.3). Frame Ein Frame ist ein vollwertiges Fenster, wie es meist für Applikationen verwendet wird. Es hat einen Rahmen mit Titelleiste (Attribut title) und kann eine MenuBar (Attribut menuBar) besitzen. resizable gibt an, ob der Benutzer die Größe des Fensters verändern kannn und iconImage dient zur Festlegung eines Symbols, falls das Fenster den Zustand (state) Frame.ICONIFIED annimmt.
246
Einfache Oberflächen – java.awt
Sandini Bib
Dialog Eine etwas eingeschränkte Variante eines Fensters ist Dialog. Jedes Dialog-Objekt hat einen eindeutigen Eigner (owner), der in der Regel das erzeugende Frame- oder Dialog-Objekt ist. Im Gegensatz zu Frame gibt es kein Icon, und es kann auch keine MenuBar zugewiesen werden. Ein zusätzliches Feature des Dialogs ist modal, womit angegeben wird, ob das Dialogfenster erst geschlossen werden muss, bevor der Benutzer wieder Zugriff auf das Parent-Window bekommt, oder ob beide Fenster gleichzeitig benutzt werden können. Eine Systemmodalität, wie sie unter Windows existiert, gibt es in Java nicht. Es kann also kein Java-Fenster geben, das sämtliche anderen Fenster auf dem Bildschirm blockiert. Beispiel import java.awt.*; import java.awt.event.*; public class WinDemo extends Frame { public static void main(String[] args) { WinDemo win = new WinDemo(); win.setTitle("Identifizierung"); // gleichmäßige Verteilung der Kontrollelemente: win.setLayout(new FlowLayout()); win.add(new Label("ID: ")); win.add(new TextField(6)); // Anwendungsende beim Schließen des Fensters: win.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) {System.exit(0);} }); // Fenster positionieren und anzeigen: win.setBounds(100, 100, 200, 80); win.setVisible(true); } }
Oberflächenprogrammierung
Nitty Gritty • Take that!
8
247
Sandini Bib
8.2.11 Die Klasse FileDialog
Nitty Gritty • Take that!
8
seit
SE
ME
EE
1.0
x
-
x
Bild 8.22: Darstellung des FileDialogs unter Windows
Ein vorgefertigtes Dialogfenster für den Zugriff auf Dateien stellt die von Dialog abgeleitete Klasse FileDialog bereit. Diese öffnet den Dateidialog des jeweiligen Wirtsbetriebssystems. Dialog
FileDialog directory: String file: String filenameFilter: FilenameFilter mode: int +LOAD: int +SAVE: int +FileDialog(parent: Frame) +FileDialog(parent: Frame, titel: String) +FileDialog(parent: Frame, titel: String, modus: int)
Bild 8.23: Die Klasse FileDialog 248
Einfache Oberflächen – java.awt
Sandini Bib
Methoden und Attribute directory ist die Voreinstellung des Pfades, mit dem der FileDialog
geöffnet wird, und liefert den Ergebnispfad nach der Auswahl durch den Benutzer. Das Gleiche gilt für das Attribut file für Dateinamen. Mit filenameFilter können Sie einen Filter für Dateien einrichten, der die Auswahl bestimmter Dateien erlaubt oder verbietet. Dazu muss eine Klasse implementiert werden, die das Interface java.io.FilenameFilter mit der Methode accept() erfüllt. mode ist der Modus des FileDialogs, FileDialog.LOAD für das Öffnen von Dateien, FileDialog.SAVE für das Speichern. Beispiel FileDialog dateiAuswahl = new FileDialog(win, "Dokument öffnen", FileDialog.LOAD); dateiAuswahl.setVisible(true); // FileDialog ist modal, daher warten auf das Ergebnis: String pfad = dateiAuswahl.getDirectory(); String filename = dateiAuswahl.getFile();
seit
SE
ME
EE
1.0
x
-
x
Bild 8.24: Darstellung eines Panels
Ein Panel ist ein Fensterbereich, in dem andere Kontrollelemente platziert werden können (Container). Hauptaufgabe ist die Gestaltung und Gliederung von Benutzeroberflächen in Window-Systemen.
Oberflächenprogrammierung
249
Nitty Gritty • Take that!
8
8.2.12 Die Klasse Panel
Sandini Bib
Container
Panel
+Panel() +Panel(layout : LayoutManager)
Bild 8.25: Die Klasse Panel
Über das geerbte Attribut layout kann ein LayoutManager zugewiesen werden. Häufig verschachtelt man mehrere Panel mit unterschiedlichen LayoutManagern ineinander (siehe Abschnitt 8.3). Die Klasse Applet (Kapitel 9) ist übrigens ebenfalls von Panel abgeleitet.
Nitty Gritty • Take that!
8
8.2.13 Menüklassen seit
SE
ME
EE
1.0
x
-
x
Menüs dienen zur Auswahl von Funktionen und Optionen in Anwendungen. Menüs können entweder in Form von MenuBars an der Oberseite von Fenstern oder als PopUp-Menüs an einer beliebigen Stelle auftreten. Oft sind Menüs geschachtelt. Menu
MenuBar
MenuItem
CheckboxMenuItem
Bild 8.26: Darstellung verschiedener Menü-Objekte
250
Einfache Oberflächen – java.awt
Sandini Bib
Ein Menü ist entweder in eine Menüleiste eines Frame-Objekts eingebettet (Klasse MenuBar) oder erscheint auf Abruf als Kontextmenü (Klasse PopupMenu). Darin sind entweder Untermenüs (Klasse Menu) oder Menüeinträge (Klasse MenuItem) enthalten. Abbildung 8.27 zeigt die Vererbungshierarchie in Java. Menüelemente, die wiederum andere Menükomponenten enthalten (wie Menu, MenuBar oder PopupMenu), implementieren das Interface MenuContainer. MenuComponent {abstract} font: Font name: String +getParent()
MenuItem
MenuBar
label: String shortcut: MenuShortcut actionCommand: String enabled: boolean +MenuItem() +MenuItem(aufschrift: String) +MenuItem(aufschrift: String, kürzel: MenuShortcut) +addActionListener(l: ActionListener)
helpMenu: Menu +MenuBar() +add(menü: Menu) +getMenuCount() +getMenu(index: int) +remove(menü: MenuComponent/int) +shortcuts()
8
CheckboxMenuItem state: boolean +CheckboxMenuItem() +CheckboxMenuItem(aufschrift: String) +CheckboxMenuItem(aufschrift: String, zustand: boolean) +addItemListener(l: ItemListener)
Nitty Gritty • Take that!
«interface» MenuContainer
Menu
+Menu() +Menu(aufschrift: String) +add(eintrag: MenuItem/String) +addSeparator() +getItemCount() +getItem(index: int) +insert(eintrag: MenuItem/String, index: int) +remove(eintrag: MenuComponent/int) +removeAll()
PopupMenu
+PopupMenu() +PopupMenu(aufschrift: String) +show(ursprung: Component, x: int, y: int)
Bild 8.27: Die AWT-Menüklassen Oberflächenprogrammierung
251
Sandini Bib
Methoden und Attribute Jedes Menu-Objekt besitzt ein label und ein actionCommand, ein String, der das Menü beim ActionEvent charakterisiert, der ausgelöst wird, wenn der Eintrag angeklickt wird. Für Menüs können Shortcuts, also Tastaturkürzel, angegeben werden. Ein MenuItem ist ein Eintrag in einem Menü, der sich bei Selektion einer Option ändert (CheckboxMenuItem) oder ein Kommando absetzt. Ein MenuItem-Objekt wird genauso mittels add-Methode in ein MenuObjekt eingefügt wie ein Menu in eine MenuBar. Letztere ordnen Sie mit der Methode setMenuBar() einem Frame-Objekt zu. Ein PopupMenu-Objekt wird relativ zur angegebenen Komponente positioniert. Es ist also nicht richtig, für x und y der show-Methode die Mauszeigerposition anzugeben. Beispiel
Nitty Gritty • Take that!
8
MenuBar menueLeiste = new MenuBar(); Menu dateiMenue = new Menu("Datei"); menueLeiste.add(dateiMenue); win.setMenuBar(menueLeiste); MenuItem neu = new MenuItem("Neu", new MenuShortcut(KeyEvent.VK_N)); neu.addActionListener(eventHandler); dateiMenue.add(neu); MenuItem oeffnen = new MenuItem("Öffnen", new MenuShortcut(KeyEvent.VK_O)); oeffnen.addActionListener(eventHandler); dateiMenue.add(oeffnen);
8.3
LayoutManager
Java ist in der Lage, Fenster und Kontrollelemente unabhängig von einer absoluten Größe zu definieren. Mittels so genannter LayoutManager können Sie eine Oberfläche logisch definieren, die Größen und Positionen der Kontrollelemente in einem Fenster werden dann zur Laufzeit in Abhängigkeit von der Fenstergröße berechnet. Es gibt 5
252
LayoutManager
Sandini Bib
Standard-LayoutManager im JDK 1.1 und zusätzlich das BoxLayout aus Java 2, die in Abbildung 8.28 dargestellt sind. Daneben können Sie auch eigene LayoutManager schreiben, indem Sie das Interface LayoutManager implementieren.
GridBagLayout CardLayout
GridLayout
BorderLayout
FlowLayout
BoxLayout
Bild 8.28: Arten von LayoutManagern
Größenänderung und variabel große Kontrollelemente Ändert sich die Größe eines Containers, also eines Fensters, Applets oder Panels, das Kontrollelemente enthält, so wird anhand bestimmter Randbedingungen (Constraints) eine automatische Größen- und Positionsanpassung durchgeführt. Diese Bedingungen können je nach Art des LayoutManagers sehr unterschiedlich ausfallen. LayoutManager berücksichtigen auch die Unterschiede in der Darstellung von Kontrollelementen auf verschiedenen Betriebssystemen und in verschiedenen Sprachen. So kann eine Schriftart anders definiert sein oder eine deutschsprachige Meldung länger sein als eine englische. Ohne Verwendung von LayoutManagern würden die Texte dann unter Umständen nur teilweise dargestellt. Oberflächenprogrammierung
253
Nitty Gritty • Take that!
8
Sandini Bib
8.3.1
Die Klasse BorderLayout
seit
SE
ME
EE
1.0
x
-
x
Bild 8.29: Darstellung einer BorderLayout-Anwendung
Das BorderLayout ordnet die eingefügten Komponenten an den Rändern eines Containers an. Diese werden mit den Himmelsrichtungen bzw. mit CENTER bezeichnet. Es können maximal fünf Kontrollelemente eingefügt werden.
Nitty Gritty • Take that!
8
«interface» LayoutManager
BorderLayout hgap: int vgap: int +CENTER: String +NORTH: String +SOUTH: String +WEST: String +EAST: String +BorderLayout() +BorderLayout(horizAbstand: int, vertAbstand: int)
Bild 8.30: Die Klasse BorderLayout
254
LayoutManager
Sandini Bib
Constraints
Beschreibung
des Containers
Einstellungen des LayoutManagers:
Mögliche Werte
hgap/vgap: Abstand zwischen den Kontrollelementen
>= 0
enthaltener Kontrollelemente
Position
CENTER, NORTH, SOUTH, WEST, EAST
Größenberechnung
anhand von preferredSize, minimum und maximum der enthaltenen Kontrollelemente
Einsatz Das BorderLayout kommt insbesondere bei der Verwendung von Toolbars und Statuszeilen zum Einsatz, die in den North-/West- beziehungsweise South-Bereich platziert werden. Beispiel
8
8.3.2
Die Klasse FlowLayout
seit
SE
ME
EE
1.0
x
-
x
Nitty Gritty • Take that!
Frame win = new Frame("BorderLayout"); win.setLayout(new BorderLayout()); win.add(new Button("Center"), BorderLayout.CENTER); win.add(new Button("North"), BorderLayout.NORTH); win.add(new Button("South"), BorderLayout.SOUTH); win.add(new Button("West"), BorderLayout.WEST); win.add(new Button("East"), BorderLayout.EAST); win.setBounds(100, 100, 200, 150); win.setVisible(true);
Bild 8.31: Darstellung eines Dialogs mit FlowLayout
Oberflächenprogrammierung
255
Sandini Bib
Das FlowLayout ordnet alle Komponenten nacheinander in einer Reihe an, bis der Rand des Containers erreicht ist. Weitere Komponenten werden in einer neuen Zeile eingefügt. Auf das endgültige Aussehen kann man Einfluss nehmen, indem man die Properties alignment, hgap und vgap bestimmt. «interface» LayoutManager
FlowLayout hgap: int vgap: int alignment: int +CENTER: int +LEFT: int +RIGHT: int +FlowLayout() +FlowLayout(ausrichtung int, horizAbstand: int, vertAbstand: int) +FlowLayout(ausrichtung: int)
Nitty Gritty • Take that!
8
Bild 8.32: Die Klasse FlowLayout
Constraints
Beschreibung
des Containers
Einstellungen des LayoutManagers: alignment: Orientierung der
Kontrollelemente
Mögliche Werte
CENTER, LEFT, RIGHT
hgap/vgap: Abstand zwischen den Kontrollelementen
>= 0
enthaltener Kontrollelemente
-
Größenberechnung
anhand von preferredSize, minimum und maximum der enthaltenen Kontrollelemente
256
LayoutManager
Sandini Bib
Einsatz Eine typische Anwendung findet das FlowLayout bei der Gruppierung gleichartiger Kontrollelemente, zum Beispiel einer Reihe von Knöpfen: »OK«, »Abbrechen«, »Hilfe«. Beispiel Dialog win = new Dialog(parentFrame); win.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5)); win.add(new Button("OK")); win.add(new Button("Abbrechen")); win.add(new Button("Hilfe")); win.setBounds(100, 100, 200, 100); win.setVisible(true);
8.3.3
Die Klasse BoxLayout
seit
SE
ME
EE
1.2
x
-
x
Bild 8.33: Darstellung eines Dialogs mit BoxLayout
Das erst im Swing-Package enthaltene javax.swing.BoxLayout ist eng mit dem FlowLayout und dem GridLayout verwandt. Es ordnet die Komponenten in einer Reihe oder Spalte an, bricht diese jedoch nicht um. «interface» LayoutManager
BoxLayout +X_AXIS : int +Y_AXIS : int +BoxLayout(ziel: Container, orientierung: int)
Bild 8.34: Die Klasse BoxLayout Oberflächenprogrammierung
257
Nitty Gritty • Take that!
8
Sandini Bib
Constraints
Beschreibung
des Containers
Einstellungen des LayoutManagers:
Mögliche Werte
axis: Orientierung der Kontrollelemente
X_AXIS, Y_AXIS
enthaltener Kontrollelemente
-
Größenberechnung
anhand von preferredSize, minimum und maximum der enthaltenen Kontrollelemente
Einsatz Das BoxLayout wird häufig bei einer Gruppe gleichartiger Kontrollelemente und bei einer Menge ineinander geschachtelter Container als Ersatz für das komplexe GridBagLayout eingesetzt. Beispiel
Nitty Gritty • Take that!
8
Dialog win = new Dialog(parentFrame); win.setLayout(new BoxLayout(win, BoxLayout.X_AXIS)); win.add(new Button("OK")); win.add(new Button("Abbrechen")); win.add(new Button("Hilfe")); win.setBounds(100, 100, 200, 100); win.setVisible(true);
8.3.4
Die Klasse GridLayout
seit
SE
ME
EE
1.0
x
-
x
Bild 8.35: Darstellung eines Dialogs mit GridLayout 258
LayoutManager
Sandini Bib
Das GridLayout unterteilt den Container in gleich proportionierte Spalten und Zeilen. Die Anzahl der Spalten oder Zeilen kann angegeben werden. Die einzelnen Komponenten werden der Reihe nach in das entstandene Raster gesetzt und füllen dieses komplett aus. Dazu werden alle Kontrollelemente auf die gleiche Größe gebracht. «interface» LayoutManager
GridLayout hgap: int vgap: int columns: int rows: int +GridLayout() +GridLayout(zeilen: int, spalten: int) +GridLayout(zeilen: int, spalten: int, horizAbstand : int, vertAbstand : int)
Bild 8.36: Die Klasse GridLayout Beschreibung
Mögliche Werte
des Containers
Einstellungen des LayoutManagers: rows: Anzahl der Zeilen columns: Anzahl der Spalten hgap/vgap: Abstand zwischen den Kontrollelementen
>= 0 (0 bedeutet beliebig viele) >= 0 (0 bedeutet beliebig viele) >= 0
enthaltener Kontrollelemente
-
Größenberechnung
anhand von preferredSize, minimum und maximum der enthaltenen Kontrollelemente
Von den Properties rows und columns muss eines größer als null eingestellt sein. Zum Beispiel bedeutet rows=1, columns=0 eine einzeilige Darstellung mit beliebig vielen Kontrollelementen. Oberflächenprogrammierung
259
8 Nitty Gritty • Take that!
Constraints
Sandini Bib
Einsatz Man verwendet diesen LayoutManager zum Beispiel für Toolbars. Er wird jedoch bei Swing-Anwendungen durch Einführung der JToolBar nur noch selten benötigt. Ein weiterer Einsatzbereich sind komplexe geschachtelte Layouts, wenn aus programmiertechnischen Gründen das GridBagLayout zu aufwändig erscheint. Beispiel Dialog win = new Dialog(parentFrame); win.setLayout(new GridLayout(2, 0)); win.add(new Label("links oben")); win.add(new Label("rechts oben")); win.add(new Label("links unten")); win.setBounds(100, 100, 150, 75); win.setVisible(true);
8.3.5
Nitty Gritty • Take that!
8
Die Klasse GridBagLayout
seit
SE
ME
EE
1.0
x
-
x
0 1 2 3 4 gridY 0
1
2
3... gridX
Bild 8.37: Darstellung eines Fensters mit GridBagLayout
Das GridBagLayout ist ein sehr mächtiger, leider aber auch komplexer LayoutManager. Er ordnet die Komponenten in einem Raster mit einer definierten Anzahl von Zeilen und Spalten an. Die Größenaufteilung richtet sich nach den Constraints der enthaltenen Kontrollelemente. Hier können fixe und variable Größen angegeben werden, um den Platz optimal zu nutzen. Kontrollelemente können sich über mehrere (zusammenhängende) Felder hinweg erstrecken.
260
LayoutManager
Sandini Bib
Die Einstellungen erfolgen bei diesem LayoutManager nicht in dessen Properties, sondern in einem Objekt der Klasse GridBagConstraints pro Kontrollelement, was bei einer manuellen Programmierung sehr aufwändig ist. «interface» LayoutManager
GridBagLayout
+GridBagLayout() +setConstraints(kontrollelement: Component, attribute: GridBagConstraints) +lookupConstraints(kontrollelement: Component) +getConstraints(kontrollelement: Component) +location(x: int, y: int)
GridBagConstraints +gridx/gridy: int +gridwidth/gridheight: int +fill: int +BOTH/NONE/HORIZONTAL/ VERTICAL: int +insets : Insets +anchor: int +CENTER/NORTH/WEST/ NORTHWEST...: int +weightx/weighty: int +GridBagConstraints() +clone()
Bild 8.38: Die Klasse GridBagLayout
Beschreibung
Mögliche Werte
des Containers
Einstellungen des LayoutManagers
keine
enthaltene Kontrollelemente
gridx/gridy: Position im Raster (linke obere Ecke)
>= 0
gridwidth/gridheight: Anzahl der bedeckten Felder
>= 1
8
fill: fixe oder variable Größe
NONE, BOTH, HORIZONTAL, VERTICAL
insets: Rand um das Kontrollelement
top/left/bottom/ right >= 0
anchor: Positionierung innerhalb des Rasters
CENTER, NORTH, WEST, NORTHWEST..
weightx/weighty: relatives Gewicht
>= 0.0
Oberflächenprogrammierung
261
Nitty Gritty • Take that!
Constraints
Sandini Bib Constraints
Beschreibung
Größenberechnung
anhand von obigen Constraints unter Berücksichtigung von preferredSize, minimum und maximum der enthaltenen Kontrollelemente
Mögliche Werte
Der GridBag stellt ein Gitter bereit, das so viele Zeilen und Spalten hat, wie zur Platzierung der enthaltenen Kontrollelemente notwendig ist. Bei jedem gibt man zumindest die relative Position in Rastereinheiten an und falls ein Element mehr als eine Zelle bedeckt die gewünschte Breite oder Höhe.
Nitty Gritty • Take that!
8
Ein Feld im Raster ist normalerweise nur so breit, wie es die Mindestanforderung der Komponente vorschreibt (z. B. Länge des Textes auf einem Button). Füllen alle Komponenten zusammen den Container nicht von Rand zu Rand aus, so werden sie zwar zueinander angeordnet, aber in dieser Anordnung im Container zentriert. Anhand der horizontalen und vertikalen Gewichtung wird die Aufteilung des übrigen Platzes berechnet. Dies ist insbesondere für Kontrollelemente wichtig, die eine variable Größe besitzen. Soll eines den maximal verfügbaren Raum einer Zelle ausnutzen, können Sie dies durch das Attribut fill erreichen. Die Properties weightX und weightY müssen bei mindestens einem Kontrollelement größer als null eingestellt werden, damit die Berechnung variabel großer Felder durchgeführt wird – auch wenn keine prozentuale Aufteilung gewünscht wird. In diesem Falle sollten Sie bei einem beliebigen Kontrollelement einen sehr kleinen Wert eintragen, zum Beispiel 0.000001. Einsatz Die Benutzung wird bei allen Oberflächen empfohlen, bei denen einem Anwender mit einer höheren Bildschirmauflösung mehr Informationen präsentiert werden können. Dies ist insbesondere bei Fenstern der Fall, die Listen, Tabellen oder Baumdarstellungen beinhalten.
262
LayoutManager
Sandini Bib
Beispiel Frame win = new Frame(); win.setLayout(new GridBagLayout()); // Basiselement mit gemeinsamen Attributen: GridBagConstraints basis = new GridBagConstraints(); basis.insets = new Insets(5, 10, 5, 10); basis.anchor = GridBagConstraints.WEST; GridBagConstraints labelAttr = (GridBagConstraints) basis.clone(); labelAttr.gridx = 0; labelAttr.gridy = 0; // links oben win.add(new Label("Suche nach:"), labelAttr); GridBagConstraints eingabeAttr = (GridBagConstraints) basis.clone(); eingabeAttr.gridx = 1; eingabeAttr.gridy = 0; // daneben win.add(new TextField(10), eingabeAttr); GridBagConstraints listAttr = (GridBagConstraints) basis.clone(); listAttr.gridx = 0; listAttr.gridy = 1; // darunter listAttr.gridwidth = 2; // zwei Zellen breit // verfügbaren restlichen Platz ausfüllen: listAttr.fill = GridBagConstraints.BOTH; listAttr.weightx = 0.1; listAttr.weighty = 0.1; win.add(new List(), listAttr); win.setBounds(100, 100, 300, 150); win.setVisible(true);
Wie Sie sehen, ist der zu programmierende Code lang und schlecht zu lesen. Schwierig wird es insbesondere, wenn Sie nachträglich zwischen die Zeilen ein neues Kontrollelement einfügen wollen, da die Zellennummerierung absolut erfolgt. Es empfiehlt sich daher, zuerst das gewünschte Ergebnis auf Papier zu zeichnen und die Rasterlinien einzutragen. Benutzen Sie nach Möglichkeit eine Entwicklungsumgebung, die die Nutzung des GridBagLayouts grafisch unterstützt (z. B. IBM VisualAge), oder verzichten Sie auf den Einsatz und schachteln Sie Panels mit dem vertikalen und horizontalen BoxLayouts ineinander. Oberflächenprogrammierung
263
Nitty Gritty • Take that!
8
Sandini Bib
8.3.6
Die Klasse CardLayout
seit
SE
ME
EE
1.0
x
-
x
Bild 8.39: Darstellung einer Seite des CardLayouts
Nitty Gritty • Take that!
8
Das CardLayout ordnet die verschiedenen Komponenten (Subcontainer, in der Regel Panels) wie Karten hintereinander an. Dabei vergrößert es jede Komponente auf die volle Größe des übergeordneten Containers. Der LayoutManager stellt Methoden zum Umschalten zwischen den verschiedenen »Seiten« eines »Notizbuches« bereit, jedoch keine grafischen Hilfsmittel à la »Karteireiter« oder Knöpfe zum Vor-/Zurückblättern. Es ist deshalb eher mit einem Stapel von Karten vergleichbar als mit einem Notizbuch. «interface» LayoutManager
CardLayout hgap: int vgap: int +CardLayout() +CardLayout(horizAbstand: int, vertAbstand: int) +show(parent: Container, name: String) +first(parent: Container) +last(parent: Container) +next(parent: Container) +previous(parent: Container)
Bild 8.40: Die Klasse CardLayout
264
LayoutManager
Sandini Bib
Einsatz Als Notizbuch-Ersatz ist das CardLayout durch die Einführung der Klasse JTabbedPane (siehe Abschnitt 8.6.20) in Swing-Anwendungen fast überflüssig geworden. Ein weiterer Anwendungszweck ist jedoch eine Folge von Fensterinhalten, die sequenziell abgearbeitet werden sollen und innerhalb des gleichen Hauptfensters weitergeschaltet werden, wie zum Beispiel die Einzelschritte in einem Wizard. Beispiel Panel cards = new Panel(); CardLayout cardLayout = new CardLayout(); cards.setLayout(cardLayout); Label seite1 = new Label("Seite 1"); cards.add(seite1, seite1.getText()); Label seite2 = new Label("Seite 2"); cards.add(seite2, seite2.getText()); [...] switch (steuerung) { case ZURUECK: cardLayout.previous(cards); break; case VOR: cardLayout.next(cards); break; }
Die Klasse OverlayLayout
Der mit Swing hinzugekommene LayoutManager javax.swing.OverlayLayout dient zur Darstellung überlappender und sich überlagernder Kontrollelemente. Dies ist interessant für transparente Kontrollelemente oder Hintergrundbilder, ansonsten wird dieser LayoutManager sehr selten eingesetzt. 8.3.8 Bemerkungen zur Anwendung von LayoutManagern Die Benutzung von LayoutManagern macht bei einfachen Dialogboxen keinen Sinn. Mit einfach ist gemeint, dass diese Fenster so klein sind, dass sie bei jeder Bildschirmauflösung komplett dargestellt
Oberflächenprogrammierung
265
Nitty Gritty • Take that!
8.3.7
8
Sandini Bib
werden können und alle Beschriftungen statisch gesetzt sind. Damit ist keine Größenänderung notwendig, und in der Regel sind diese Fenster durch den Benutzer auch nicht skalierbar. Bei komplexen Layouts ist eine Schachtelung mehrerer (J)Panels anzuraten. Diese können durchaus verschiedene LayoutManager benutzen.
Nitty Gritty • Take that!
8 Bild 8.41: Kombination mehrerer LayoutManager
8.4 Ereignisbehandlung seit
SE
ME
EE
1.1
x
-
x
Ereignisbehandlung oder Event Handling geschieht in Java mittels der Listener- und Adapter-Klassen, die in java.awt.event definiert sind. Ein Ereignis tritt durch eine Benutzeraktion oder durch die Veränderung eines Objektattributes auf. Dieses Ereignis kann an Interessenten weitergemeldet werden. Seit Java 1.1 registriert sich ein solcher Interessent bei der Quelle des Ereignisses (Delegations-Er266
Ereignisbehandlung
Sandini Bib
eignismodell). Dazu muss er ein Listener-Interface implementieren und bekommt, wenn das Ereignis eintritt, ein Event-Objekt. Damit gibt es pro Ereignisart immer ein Pärchen von Event-Klasse und Interface, zum Beispiel ActionEvent und ActionListener. Für manche Klassen gibt es zusätzlich eine abstrakte Adapterklasse, die die Implementierung der Listener-Interfaces vereinfachen soll. BeispielEvent
«interface» BeispielListener
EventErzeugerKlasse
BeispielAdapter
+addBeispielListener(l: BeispielListener) +removeBeispielListener(l: BeispielListener) ...
EventHandlerKlasse
Beispiel: «interface» ActionListener
+getActionCommand() +getModifiers() +getSource()
+actionPerformed(e : ActionEvent)
Button
+addActionListener(l : ActionListener) +removeActionListener(l : ActionListener)
8 Nitty Gritty • Take that!
ActionEvent
ActionEventHandler +actionPerformed(e : ActionEvent)
Bild 8.42: Beispiel für Event-Klassen
Abbildung 8.42 zeigt die beteiligten Klassen für zwei Beispiele. Zunächst gibt es die Ereignisklasse selbst (BeispielEvent bzw. ActionEvent) und das passende Listener-Interface mit den zugehörigen Methoden (BeispielListener bzw. ActionListener). Der Programmierer muss eine EventHandler-Klasse implementieren, die dieses Interface erfüllt und im konkreten Fall angibt, was passieren soll, wenn das Ereignis eintreten soll. Im Beispiel unseres ActionEventHandlers ist Oberflächenprogrammierung
267
Sandini Bib
dies die einzige Klasse, die Sie selbst schreiben müssen. Falls ein Listener sehr viele Methoden definiert und man nur einige davon implementieren möchte, kann man bei manchen Klassen auch auf einen optionalen Adapter aufsetzen (BeispielAdapter). Und schließlich muss es noch eine Klasse geben, in der das Ereignis eintritt (EventErzeugerKlasse bzw. Button) und bei deren Objekten sich am Ereignis interessierte EventHandler anmelden können. Es hat sich eingebürgert, dass die Methoden event-erzeugender Klassen folgender Namenskonvention genügen: T addBeispielListener(l: BeispielEvent)
– Registrieren
eines Listeners T removeBeispielListener(l: BeispielEvent)
– Löschen
eines Listeners T fireBeispiel(...)
– Auslösen des Ereignisses
8 ok:Button
:Applet
Nitty Gritty • Take that!
addActionListener(this) Klickt auf OK-Button fireAction(...)
<>
e: ActionEvent
actionPerformed(e)
Bild 8.43: Beispielablauf eines Action-Events
In folgender Tabelle finden Sie einige Beispiele für Event-Klassen und -Listener aus den Packages java.beans und java.awt. Es ist jedoch auch möglich, selbst neue Event- und Listener-Paare (Event Sets) zu erstellen.
268
Ereignisbehandlung
Sandini Bib
Ereignis
Event-Typ
Listener
Listener-Methode
PropertyÄnderung
PropertyChangeEvent
PropertyChange Listener
propertyChanged()
Button angeklickt oder Menüpunkt gewählt
ActionEvent
ActionListener
actionPerformed()
Fokus erhalten bzw. verloren
FocusEvent
FocusListener
focusGained() focusLost()
Listeneintrag (de)selektiert
ItemEvent
ItemListener
itemStateChanged()
Maus-Events
MouseEvent
MouseListener
MouseMotion Event
MouseMotionListener
mouseClicked() mouseEntered() mouseExited() mousePressed() mouseReleased() mouseMoved() mouseDragged()
Tastatureingaben
KeyEvent
KeyListener
keyTyped() keyPressed() keyReleased()
FensterEvents
WindowEvent
WindowListener
windowOpened() windowClosing() windowClosed() windowActivated() windowDeactivted() windowIconified() windowDeiconified()
Textänderung
TextEvent
TextListener
textValueChanged()
Regler verändert
AdjustmentEvent
AdjustmentListener
adjustmentValueChanged()
Darüber hinaus sind mit Swing noch weitere Event-Klassen und -Listener im Package javax.swing.event (bzw. com.sun.java.swing.event in Swing 1.0.x) hinzugekommen, welche in nachfolgender Tabelle aufgelistet sind. Oberflächenprogrammierung
269
Nitty Gritty • Take that!
8
Sandini Bib
Nitty Gritty • Take that!
8
Ereignis
Event-Typ
Listener
Listener-Methode
Texteinfügemarke verändert
CaretEvent
CaretListener
caretUpdate()
Dokument verändert
DocumentEvent
DocumentListener
insertUpdate() removeUpdate() changedUpdate()
Liste verändert
ListDataEvent
ListDataListener
intervalAdded() intervalRemoved() contentsChanged()
Listeintrag (de)selektiert
ListSelectionEvent
ListSelectionListener
valueChanged()
Datenmodell einer Tabelle verändert
TableModel Event
TableModelListener
tableChanged()
Tabellenspalte verändert
TableColumnModelEvent
TableColumn ModelListener
columnAdded() columnRemoved() columnMoved() columnMarginChanged() columnSelectionChanged()
Tabellenzelle verändert
ChangeEvent
CellEditorListener
editingStopped() editingCanceled()
Datenmodell eines Baumes verändert
TreeModelEvent
TreeModelListener
treeNodesChanged() treeNodesInserted() treeNodesRemoved() treeStructureChanged()
Baumeintrag auf- oder zugeklappt
TreeExpansionEvent
TreeExpansionListener
treeExpanded() treeCollapsed()
Baumeintrag (de)selektiert
TreeSelect ionEvent
TreeSelectionListener
valueChanged()
Zustand verändert
ChangeEvent
ChangeListener
stateChanged()
270
Ereignisbehandlung
Sandini Bib Ereignis
Event-Typ
Listener
Listener-Methode
Menüeintrag verändert
MenuEvent
MenuListener
menuSelected() menuDeselected() menuCanceled
Umkehrbare Operation ausgeführt
UndoableEditEvent
UndoableEditListener
undoableEditHappened()
Nachdem wir die theoretischen Grundlagen des Event Handlings kennen gelernt haben, wird nun genauer auf die praktische Realisierung eingegangen. Für die Behandlung eines Ereignisses muss eine Handler-Klasse implementiert werden, die das Listener-Interface implementiert. Die können Sie auf verschiedene Weisen erreichen: 1. Sie schreiben eine öffentliche Klasse, die den Listener implementiert. 2. Sie definieren die Handler-Klasse lokal als innere Klasse.
Seit Swing und besonders seit Java 1.3 gibt noch eine Erweiterung. Wenn Sie dieselbe Aktion über mehrere Wege auslösen können (z. B. per Menü, Toolbar oder Drag & Drop) wäre es umständlich, für alle Möglichkeiten Listener zu implementieren, die das gewünschte Verhalten implementieren und zugleich den Status aller betroffenen Komponenten zu beeinflussen (z. B. Zustandwechsel auf disabled). Mit Hilfe des Interfaces javax.swing.Action werden alle betroffenen Komponenten gleich behandelt. 8.4.1 Event Handling mit eigenen Klassen In diesem Ansatz wird das Listener-Interface (in diesem Fall FocusListener) mit einer eigenen Klasse implementiert. Beispiel Der folgender Listener reagiert darauf, das ein Textfeld verlassen wurde, und prüft, ob eine fünfstellige Postleitzahl eingegeben wurde.
Oberflächenprogrammierung
271
8 Nitty Gritty • Take that!
3. Sie implementieren die Behandlung direkt bei der Listener-Registrierung.
Sandini Bib
Nitty Gritty • Take that!
8
import java.awt.*; import java.awt.event.*; public class PLZFocusHandler implements FocusListener { // focusGained wird aufgerufen, wenn ein // Kontrollelement den Eingabefokus erhält: public void focusGained(FocusEvent ereignis) {} // focusLost wird aufgerufen, wenn ein // Kontrollelement den Eingabefokus verliert: public void focusLost(FocusEvent ereignis) { TextField tf = (TextField) ereignis.getSource(); if (!checkPLZ(tf.getText()) { // ungültige PLZ: rot markieren und Fokus setzen tf.setBackground(Color.red); tf.requestFocus(); } else tf.setBackground(Color.white); //Marker löschen } private boolean checkPLZ(String text) { // PLZ ist fünfstellig... if (text.length() != 5) return false; // ...und besteht aus Ziffern. for (int i=0; i<5; i++) if (!Character.isDigit(text.charAt(i))) return false; return true; } }
Der Übersichtlichkeit halber wurde hier nicht überprüft, ob das Kontrollelement, bei dem dieser Handler registriert wurde, auch wirklich vom Typ TextField ist. Nun muss sich der Handler bei der Ereignisquelle, die diese Ereignisart generiert, als Listener registrieren. Dies geschieht im Beispiel mit der entsprechenden Methode addFocusEvent().
272
Ereignisbehandlung
Sandini Bib
Beispiel import java.awt.*; public class AdressFenster extends Frame { private PLZFocusHandler handler; private TextField plz; [...] // weitere Kontrollelemente public AdressFenster() { // Neue Instanz der Listener-Klasse anlegen: handler = new PLZFocusHandler(); plz = new TextField(5); // Handler registrieren: plz.addFocusListener(handler); [...] // weitere Initialisierungen } [...] // sonstige Methoden }
Von diesem Ansatz gibt es zwei kleine Varianten. Wenn Sie die Handlerklasse nur in einer Klasse benötigen und Sie sie nicht wiederwenden können, dann brauchen Sie nicht (wie eben) zwei Klassen schreiben, sondern können die Listener-Methoden direkt in der Klasse implementieren, in der der Listener registriert ist. Beispiel public class AdressFenster1 extends Frame implements FocusListener { public void focusGained(FocusEvent ereignis) {} public void focusLost(FocusEvent ereignis) { [...] } // Impl. wie bei PLZFocusHandler public AdressFenster1() { plz = new TextField(5); // Handler registrieren: plz.addFocusListener(this); [...] // weitere Initialisierungen } [...] // sonstige Methoden }
Oberflächenprogrammierung
273
8 Nitty Gritty • Take that!
Variante 1: Listener direkt in Container implementieren
Sandini Bib
Im Unterschied zu oben sparen Sie sich eine Klasse. Sie müssen nun auch kein Extra-Objekt für den Handler registrieren, sondern können die Ereignisbehandlung per thisReferenz anmelden. Variante 2 : Adapter nutzen In vorigen Beispielen konnten Sie sehen, dass Sie die focusLost-Methode hinzufügen mussten, obwohl darin keine Logik implementiert ist. Hier war der Aufwand noch vertretbar, aber beispielsweise beim WindowListener können dies auch sechs ungenutzte Methoden werden.
Nitty Gritty • Take that!
8
Um das im letzten Abschnitt beschriebene Problem zu umgehen, gibt es zu jedem Interface, das mehr als eine Methode enthält, eine zugehörige Adapterklasse, welche das entsprechende Interface implementiert. Eine Klasse, die beispielsweise die Verarbeitung eines Fokus-Ereignisses implementiert, kann somit von der Klasse FocusAdapter abgeleitet werden, anstatt das FocusListener-Interface zu implementieren. Dann muss die von der Adapterklasse abgeleitete Klasse nur noch die jeweils benötigten Methoden implementieren. Alle übrigen werden von der Adapterklasse geerbt, welche die im Interface definierten Methoden bereits mit einem leeren Methodenrumpf implementiert. Dies erspart dem Entwickler insbesondere dann unnötige Programmierarbeit, wenn nicht alle im Interface definierten Methoden zur Behandlung der Ereignisse benötigt werden. Beispiel public class PLZFocusHandler1 extends FocusAdapter { // focusGained kann entfallen. public void focusLost(FocusEvent ereignis) { [...] } // Impl. wie bei PLZFocusHandler private boolean checkPLZ(String text) { [...] } // Impl. wie bei PLZFocusHandler }
Die Verwendung von Adapterklassen erhöht die Übersichtlichkeit bei Listener-Interfaces mit vielen Methoden. Allerdings ist die Erweiterung einer Adapterklasse nicht immer eine gute Lösung, da Java, wie bereits erwähnt, nur einfache Vererbung zulässt. Dies kann zum Problem werden, wenn Sie die beiden Varianten kombinieren wollen, 274
Ereignisbehandlung
Sandini Bib
denn das AdressFenster wird bereits von Frame abgeleitet, nicht von FocusAdapter. 8.4.2 Event Handling mit inneren Klassen Zur Abhilfe des Problems der Mehrfachvererbung können innere Klassen (siehe Kapitel 4.4.4) wie folgt verwendet werden. Beispiel public class AdressFenster2 extends Frame { class PLZFocusHandler2 extends FocusAdapter { [...] } // Methoden wie bei PLZFocusHandler1 private PLZFocusHandler2 handler; private TextField plz; [...] // weitere Kontrollelemente public AdressFenster2() { [...] } // Impl. wie bei AdressFenster [...] // sonstige Methoden }
8.4.3 Event Handling mit unmittelbarer Implementierung Zusätzlich zu den bisher erwähnten Möglichkeiten, eine Ereignisverarbeitung zu programmieren, gibt es eine Kurzversion. Für diese Variante ist weder eine Vererbung einer Klasse noch eine Implementierung einer Instanz notwendig. Der Methode addFocusListener() wird einfach ein neu instanziierter FocusListener (oder FocusAdapter) übergeben, in dessen Ausführungsblock die ereignisverarbeitenden Methoden unmittelbar implementiert werden. Allerdings leidet die Lesbarkeit des Programmcodes. public class AdressFenster3 extends Frame { public AdressFenster3() {
Oberflächenprogrammierung
275
8 Nitty Gritty • Take that!
Die innere Klasse PLZFocusListener2 ist somit nur innerhalb der sie unmittelbar umgebenden Klasse AdressFenster2 sichtbar. Sie hat aber Zugriff auf alle Variablen und Methoden der sie umgebenden Klasse und erreicht somit die gleiche Funktionalität wie in den anderen Konzepten für die Umsetzung des Event Handlings.
Sandini Bib plz = new TextField(5); // Handler registrieren: plz.addFocusListener(new FocusAdapter() { public void focusLost(FocusEvent ereignis) { [...] } // Impl. wie bei PLZFocusHandler private boolean checkPLZ(String text) { [...] } // Impl. wie bei PLZFocusHandler }); [...] // weitere Initialisierungen } [...]
// sonstige Methoden
}
Man spricht hier von anonymen inneren Klassen, denn es wird implizit eine Klasse erzeugt, die von der angegebenen Adapter-Klasse abgeleitet wird. Statt einer AdapterKlasse können Sie auch hier das Listener-Interface angeben, müssen dann aber – wie üblich – alle enthaltenen Methoden implementieren.
Nitty Gritty • Take that!
8
In Java 1.3 gibt es eine elegantere Lösung für Validierungen von Benutzereingaben. Die neue abstrakte Klasse javax.swing.InputVerifier ermöglicht die Überprüfung der Benutzereingabe in einer JComponent (Swing-Komponente, siehe Abschnitt 8.6.2) bevor dem Benutzer erlaubt wird, den Focus aus dem Kontrollelement herauszubewegen.
8.5
Weitere wichtige AWT-Klassen
Neben Klassen für Fenster, Kontrollelemente, LayoutManager und Ereignisbehandlung enthält das Abstract Windowing Toolkit noch eine Reihe weiterer Klassen, die mit der Bedienschnittstelle zu tun haben. Dazu zählt die Unterstützung von 2D-Grafiken, die Darstellung von Bildern und die Nutzung der Systemzwischenablage. Zunächst zum Thema Grafik:
276
Weitere wichtige AWT-Klassen
Sandini Bib
8.5.1
Die Klassen Graphics und Graphics2D
Die Klasse Graphics seit
SE
ME
EE
1.0
x
-
x
Die abstrakte Klasse java.awt.Graphics stellt die Grafikprimitive zur Verfügung. Man kann eine Schriftart und eine Zeichenfarbe definieren, Linien, Rechtecke, Kreise und andere grafische Formen ausgeben. Ein Graphics-Objekt erzeugt der Entwickler nicht selbst, sondern bekommt es aus dem Kontext gestellt, auf dem er zeichnen möchte.
Graphics { abstract } color: Color font: Font clip: Shape +getFontMetrics(schriftart: Font) +drawString(s: String, x: int, y: int) +drawLine(x1: int, y1: int, x2: int, y2: int) +drawPolyline(...) +draw/fillRect(x: int, y: int, breite: int, höhe: int) +draw/fillPolygon(form: Polygon) +draw/FillOval(...) +draw/fillArc(...) +drawImage(bild: Image,..., ladeKontrolle: ImageObserver) +translate(x: int, y: int) +clear/clipRect(x: int, y: int, breite: int, höhe: int) +copyArea(...) ...
Bild 8.44: Die Klasse Graphics
Oberflächenprogrammierung
8 Nitty Gritty • Take that!
Das bedeutet, die Klasse Graphics hat keinen (öffentlichen) Konstruktor, aber die Klassen Component (und damit auch Frame, Applet usw., siehe Abschnitt 8.2.1) sowie PrintJob (siehe Abschnitt 8.5.9) verfügen über eine Methode getGraphics(), die ein Graphics-Objekt zurückliefert. Dieses repräsentiert dann die Zeichenfläche des Applets, das Innere eines Fensters oder auch ein Blatt Papier im Drucker.
277
Sandini Bib
Methoden und Attribute Für die Ausgabe von Texten und grafischen Formen können Sie Attribute wie Schriftarten und Zeichenfarben (siehe Abschnitt 8.5.2) definieren. Mit drawString() geben Sie einen String an einer bestimmten Position aus, drawImage() stellt eine Bitmap-Grafik dar (siehe Abschnitt 8.5.8). Eine Linie zwischen zwei Punkten gibt drawLine() aus, einen Linienzug mit mehreren Bezugspunkten drawPolyline(). Einfache Formen wie Rechtecke, Ellipsen oder Kreisbögen gibt man mit drawRect(), drawOval() oder drawArc() aus. Wenn diese gefüllt werden sollen, benutzt man die entsprechende fill-Methode, also z. B. fillRect(), fillOval(). Die Zeichenfähigkeiten sind allerdings eingeschränkt: So gibt es nur eine Strichstärke; gestrichelte Linien oder Bézier-Kurven fehlen ganz. Abhilfe schafft hier erst Java 2 mit dem 2D-Grafik-Package java.awt.geom.
8
Häufig müssen aber nicht alle Bereiche gezeichnet werden, sondern nur ein Teilbereich, die so genannte Clipping Area, kurz Clip. Diese setzen Sie mit setClip(). Teilbereiche können Sie mittels copyArea() kopieren und mit clearRect() löschen. translate() verschiebt den Koordinatenursprung auf den angegebenen Punkt.
Nitty Gritty • Take that!
Die Klasse Graphics2D seit
SE
ME
EE
1.2
x
-
x
Die abstrakte Klasse java.awt.Graphics2D erweitert die Funktionalität von Graphics um Transformationen, Linienstärken und -stil.
278
Weitere wichtige AWT-Klassen
Sandini Bib
Graphics { abstract }
Graphics2D { abstract } stroke: Stroke paint: Paint transform: AffineTransform composite: Composite background: Color +draw(form: Shape) +drawImage(...) +drawString(...) +getDeviceConfiguration() +transform(typ: AffineTransform) +rotate(winkel: double) +scale/shear(x: double, y: double) +translate(x: double/int, y: double/int) ...
Bild 8.45: Die Klasse Graphics2D
Methoden und Attribute
paint erweitert das Farbmodell um die Möglichkeit, auch Farbverläufe oder Texturen für die Füllung von geometrischen Formen zu benutzen. transform erlaubt Veränderungen der Form. Für einige typische Transformationen wie Rotation, Verschiebung oder Skalierung gibt es auch direkte Methoden. composite gibt an, wie Objekte verschiedener Farben überlagert werden sollen, und erlaubt auch weiche Farbübergänge (Anti-Aliasing).
Auch die draw-Methoden wurden erweitert. So können mittels draw(Shape form) beliebige Formen gezeichnet werden, drawImage() unterstützt verschiedene Bildarten und drawString() erlaubt die Darstellung von Schriftzügen in beliebigen Stilen und Richtungen.
Oberflächenprogrammierung
279
8 Nitty Gritty • Take that!
Die Klasse eröffnet praktisch unbegrenzte Möglichkeiten zur Erstellung von Grafiken. Mit dem Attribut stroke lassen sich Linienstile definieren. Das bedeutet, Sie können nun auch gepunktete, gestrichelte oder herkömmliche Linien in beliebiger Dicke erstellen sowie das Aussehen von Linienenden und zusammenstoßenden Linien bestimmen.
Sandini Bib
Beispiel public void paint(Graphics g) // z. B. in einem Applet { Graphics2D g2 = (Graphics2D) g; // seit Java 2 möglich Paint gp = Color.blue; g2.setPaint(gp); g2.fillRect(60, 60, 100, 80); // blaues Rechteck g2.setComposite(AlphaComposite.getInstance( AlphaComposite.SRC_OVER, 0.5f)); // halb transparent g2.setColor(Color.green)); g2.fillOval(100, 45, 50, 50); // grüner Kreis }
Die 2D-Grafik von Java 2 ist sehr mächtig. Leider ist es an dieser Stelle nicht möglich, auf die Grafiktheorie und alle Klassen detaillierter einzugehen, Sie finden aber im Sun JDK unter <JDK-Pfad>\demo\jfc\Java2D\Java2Demo.html ein gutes Beispiel, wie die Klassen benutzt werden können. Außer der Klasse java.awt.Graphics2D umfasst die Java 2D API Klassen in den Packages java.awt.geom (geometrische Formen, Pfade und Transformationen), java.awt. font (Textsatz), java.awt.color (Farbräume), java.awt. image und java.awt.image.renderable (Bitmaps und Filter) sowie java.awt.print (Drucken).
Nitty Gritty • Take that!
8
8.5.2
Die Klasse Color
seit
SE
ME
EE
1.0/1.2
x
-
x
Ein Objekt der Klasse Color stellt eine Farbe im RGB-Farbschema dar (siehe Abbildung 7.48). Seit Java 2 werden noch weitere Farbschemata unterstützt. Color implementiert nun ebenso das neue Interface Paint (Zeichenfarbe) wie die zwei neuen Klassen GradientPaint (Farbverlauf) und TexturePaint (Bitmap als Füllmuster).
280
Weitere wichtige AWT-Klassen
Sandini Bib
«interface» Paint
Color
TexturePaint
GradientPaint
+white : Color +black : Color +gray/lightGray/darkGray : Color +red/pink/yellow/orange : Color +green/blue/magenta/cyan : Color <> +Color(rot : int, grün : int, blau : int) +Color(rot : float, grün : float, blau : float) +Color(rgb: int) <<Methoden>> +getRed() +getGreen() +getBlue() +getRGB() +darker/brighter() +HSBToRGB(...) +RGBToHSB(...)
Bild 8.46: Die Klasse Color
Ein Farbobjekt kann aus den Rot-, Grün- und Blau-Anteilen konstruiert werden (in Prozent als float zwischen 0.0 und 1.0 oder als Ganzzahlen zwischen 0 und 255) oder man benutzt eine der vordefinierten Farbkonstanten, zum Beispiel Color.red oder Color.darkGray. Die Klasse Color besitzt Methoden zum Abfragen der Farbanteile, zum Aufhellen oder Abdunkeln und für die Konvertierung zu anderen Farbmodellen. 8.5.3
Die Klassen Font und FontMetrics
seit
SE
ME
EE
1.0/1.2
x
-
x
Schriftarten werden in der Klasse Font repräsentiert. Laufzeitattribute wie die Buchstabenhöhe oder die Breite eines auszugebenden Strings in Pixeln in Abhängigkeit von der eingestellten Schriftart und -größe können mit Hilfe der Klasse FontMetrics ermittelt werden. Oberflächenprogrammierung
281
Nitty Gritty • Take that!
8
Methoden und Attribute
Sandini Bib
Seit Java 2 wurden die Klassen zum Schriftsatz erweitert. Neben neuen Methoden in den beiden Klassen gibt es nun ein Package java.awt.font mit Klassen zur Analyse, Manipulation und Ausgabe von Schriftenarten. FontMetrics {abstract}
Font +PLAIN: int +BOLD: int +ITALIC: int <> +Font(name: String, stil: int, größe: int) +Font(attribute: Map)
8
<<Methoden>> +getName() +getFamily() +getSize() +getStyle() +isPlain() +isBold() +isItalic() +getAttributes() +decode(schriftart: String) +getFont(systemSchriftart: String) +deriveFont(neuerStil: int) +canDisplay(zeichen: char) ...
+stringWidth(s: String) +charWidth(ch: char) +getFont() +getHeight() +getWidths() +get[Max]Ascent() +get[Max]Descent() +getMaxAdvance() +getLeading() +getStringBounds(str: String, kontext: Graphics) +getLineMetrics(str: String, kontext: Graphics) ...
Nitty Gritty • Take that!
Bild 8.47: Die Klassen Font und FontMetrics
Methoden und Attribute Eine Schriftart wird definiert durch den Fontnamen, zum Beispiel »SansSerif« oder »Dialog«, die Schriftgröße, zum Beispiel 12 Punkt, und einen Stil. Als Stilkonstanten sind Fettdruck (Font.BOLD) und Kursivschrift (Font.ITALIC) vordefiniert. Es gibt einige Standardschriftarten, die auf allen Java-Systemen vorhanden sind (Dialog, SansSerif, Serif, und Monospaced). Diese können Sie mit Hilfe des AWT-Toolkits ermitteln: Toolkit.getDefaultToolkit().getFontList();
Wenn Sie einen String als Grafik ausgeben möchten und für die Berechnung der Ausgabeposition die Größe in Pixel berechnen wollen, können Sie sich mittels des getFontMetrics-Aufrufs beim GraphicsObjekt eine Referenz auf ein FontMetrics-Objekt besorgen und mittels stringWidth(ausgabeString) und getHeight() die Größe berechnen: 282
Weitere wichtige AWT-Klassen
Sandini Bib
Beispiel String ausgabeString = "Demo"; Font f = new Font("Dialog", Font.PLAIN + Font.BOLD, 12); graph.setFont(f); // Dies sei das Graphics-Objekt // im aktuellen Kontext FontMetrics fm = graph.getFontMetrics(); int breiteInPixel = fm.stringWidth(ausgabeString); int hoeheInPixel = fm.getHeight();
Neue Methoden Seit Java 2 können Sie sämtliche installierte Schriftarten und deren -stile nutzen. Statt mit der Klasse Toolkit können Sie jetzt die lokal vorhandenen Fonts allerdings mittels der Klasse GraphicsEnvironment erfragen: ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); String[] fontNamen = ge.getAvailableFontFamilyNames();
8.5.4
Größenangaben
seit
SE
ME
EE
1.0
x
-
x
Für die meisten Größenangaben werden int-Werte benutzt. Allerdings gibt es häufig auch alternative Formulierungsmöglichkeiten mit den Klassen Dimension, Point und Rectangle.
Oberflächenprogrammierung
283
8 Nitty Gritty • Take that!
Mit getAllFonts() erhalten Sie auch die Varianten einer Schriftart, zum Beispiel eine echte (nicht berechnete) Kursivschrift oder eine Variante für den Engdruck. Für eine solchermaßen benannte Schriftart können Sie mit der Klassenmethode decode() der Klasse Font ein Font-Objekt instanziieren. Mit derive() können Sie eine Stilvariante einer angegebenen Schriftart erzeugen und mittels canDisplay() ermitteln, ob das angegebene Zeichen in der benutzten Schriftart anzeigbar ist. Dies muss nicht immer der Fall sein, denn im Unicode-Zeichensatz sind z. B. auch koreanische Schriftzeichen enthalten, die aber von den meisten Zeichensätzen nicht darstellbar sind.
Sandini Bib
«interface» Shape
Point +x: int +y: int
Rectangle +x,y: int +width: int +height: int <> +Rectangle() +Rectangle(breite: int, höhe: int) +Rectangle(x: int, y: int, breite: int, höhe: int) +Rectangle(p: Point, d: Dimension) +Rectangle(r: Rectangle) <<Methoden>> +contains(...) +get/setLocation(...) +get/setSize(...) +setBounds(...) +resize/move(...) : {deprecated} +grow(horiz: int, vert: int) +intersection/union(r: Rectangle) +intersects(r: Rectangle) +isEmpty()
Nitty Gritty • Take that!
8
<> +Point() +Point(x: int, y: int) +Point(p: Point) <<Methoden>> +getx/getY() +getLocation() +setLocation/move(x: int, y: int) +setLocation(p: Point) +translate(horiz: int, vert: int)
Dimension +width: int +height: int <> +Dimension() +Dimension(breite: int, höhe: int) +Dimension(d: Dimension) <<Methoden>> +getSize() +setSize(breite: int, höhe: int) +setSize(d: Dimension)
Bild 8.48: Die Klassen Dimension und Rectangle
Methoden und Attribute Alle getSize-Aufrufe bei Kontrollelementen liefern ein Objekt der Klasse Dimension zurück. In dieser Klasse sind die Breite (width) und Höhe (height) als öffentliche Instanzvariablen definiert. Fragt man ein Kontrollelement oder eine grafische Form nach dem sie umgebenden Rechteck (mittels getBounds() oder getBoundingBox()), so erhält man ein Objekt vom Typ Rectangle. Die Klasse enthält Methoden wie Enthaltensein, Schnittmenge, Vereinigung und verschiedene Manipulationsfunktionen. Beispiel Rectangle r1 = new Rectangle(100, 100, 200, 50); Point ecke = r1.getLocation(); Dimension groesse = r1.getSize(); ecke.y = 140; groesse.width = 110; Rectangle r2 = new Rectangle(ecke, groesse); Rectangle gesamt = r1.union(r2);
284
Weitere wichtige AWT-Klassen
Sandini Bib
8.5.5
Das Interface Shape und geometrische Formen
seit
SE
ME
EE
1.2
x
-
x
Das Interface Shape wird von den bisherigen Figuren (z. B. Rectangle, Polygon) implementiert und außerdem von den neuen geometrischen Formen im Package java.awt.geom. «interface» java.awt.Shape
java.awt. Polygon
java.awt.geom. Line2D
java.awt.geom. GeneralPath
java.awt.geom. Area
java.awt.geom. RectangularShape
8 java.awt.geom. Ellipse2D
java.awt.geom. Rectangle2D
Bild 8.49: Das Interface Shape und implementierende Klassen
So repräsentiert etwa die Klasse java.awt.geom.GeneralPath einen offenen oder geschlossenen Zug von Linien oder Kurven: «interface» java.awt.Shape
java.awt.geom.GeneralPath <> +GeneralPath() +GeneralPath(form: Shape) <<Methoden>> +moveTo(x: float, y: float) +lineTo(x: float, y: float) +quadTo(cx: float, cy: float, x: float, y: float) +curveTo(c1x: float, c1y: float, c2x: float, c2y: float, x: float, y: float) +closePath() ...
Bild 8.50: Die Klasse GeneralPath Oberflächenprogrammierung
285
Nitty Gritty • Take that!
java.awt.geom. Arc2D
Sandini Bib
Methoden und Attribute moveTo() bewegt den Cursor zu der angegebenen Koordinate. lineTo() zieht eine Linie, quadTo() eine quadratische Kurve (Parabel) und curveTo() eine Bézier-Kurve. closePath() zieht eine Linie zu den Koordinaten des letzten moveTo().
8.5.6
Die Klasse Cursor
seit
SE
ME
EE
1.1
x
-
x
Bei allen Fenstern und Kontrollelementen können Sie auch die Art des Mauszeigers angeben, die angezeigt wird, wenn sich dieser über dem Element befindet. In der Klasse Cursor sind verschiedene Konstanten vordefiniert (siehe Abbildung 8.51). Cursor
Nitty Gritty • Take that!
8
+DEFAULT_CURSOR: int +CROSSHAIR_CURSOR: int +TEXT_CURSOR: int +WAIT_CURSOR: int +HAND_CURSOR: int +MOVE_CURSOR: int +Cursor(type: int) +getType() +getDefaultCursor()
Bild 8.51: Die Klasse Cursor
Seit Java 2 können Sie auch eigene Symbole für den Mauszeiger benutzen. Hierzu dient die Methode createCustomCursor() der Klasse Toolkit (siehe Abschnitt 8.5.7). 8.5.7
Die Klasse Toolkit
seit
SE
ME
EE
1.0
x
-
x
286
Weitere wichtige AWT-Klassen
Sandini Bib
Die Klasse Toolkit erlaubt – ähnlich java.lang.System – den Zugriff auf Systemeigenschaften und -ressourcen, bezieht sich jedoch stärker auf das Grafiksubsystem. Toolkit {abstract} +getDefaultToolkit() +getScreenSize() +getScreenResolution() +getSystemClipboard() +getProperty(key: String, default: String) +getPrintJob(fenster: Frame, jobName: String, eigenschaften: Properties) +getFontList() : {deprecated} +getFontMetrics(schriftart: Font) : {deprecated} +beep() +getImage(datei: String) +getImage(url: URL) +createImage(erzeuger: ImageProducer) +prepareImage(bild: Image, breite: int, höhe: int, ladeKontrolle: ImageObserver) +checkImage(bild: Image, breite: int, höhe: int, ladeKontrolle: ImageObserver) +createCustomCursor(bild: Image, position: Point, name: String) ...
8 GraphicsEnvironment {abstract}
Bild 8.52: Die Klasse Toolkit
Methoden und Attribute Eine Referenz auf das Toolkit erhalten Sie über die Klassenmethode Toolkit.getDefaultToolkit(). Sie können mit dieser Klasse die Auflösung und Größe des Bildschirms ermitteln, was für die Zentrierung von Fenstern wichtig ist. getSystemClipboard() liefert eine Referenz auf die Zwischenablage und beep() erzeugt einen Piepton. Mit getPrintJob() erzeugen Sie – wie wir in Abschnitt 8.5.9 sehen werden – einen Druckauftrag. Mit getFontList() erhalten Sie die Standardschriftarten. Allerdings ist die Methode seit JDK 1.2 deprecated und wurde durch getAvailableFontFamilyNames() in der neuen Klasse GraphicsEnvironment ersetzt (siehe Abschnitt 8.5.3).
Oberflächenprogrammierung
287
Nitty Gritty • Take that!
GraphicsConfiguration {abstract}
Sandini Bib getProperty(<schlüssel>, <defaultwert>) fragt Laufzeitvariablen ab, die man dem Java-Interpreter mit der Option -D<schlüssel>=<wert>
mitgeben kann. Die Methode getImage() lädt ein Bild aus der angegebenen Quelle, Standardformate sind GIF und JPG. Dieses Bild kann aber oft noch nicht sofort dargestellt werden, da es noch geladen wird. Eine Kontrolle des Lade- und Rendervorgangs erfolgt deshalb mit Hilfe der Methoden prepareImage() und checkImage() sowie der Klasse ImageObserver (siehe Abschnitt 8.5.8) Die Methode createCustomCursor() ist erst seit Java 2 dabei und erlaubt es, eigene Mauszeiger zu definieren (siehe Abschnitt 8.5.6). Beispiel
Nitty Gritty • Take that!
8
Toolkit tk = Toolkit.getDefaultToolkit(); // Fenster in Bildschirmmitte positionieren: Dimension screenSize = tk.getScreenSize(); Dimension windowSize = win.getSize(); win.setLocation((screenSize.width - windowSize.width) / 2, (screenSize.height - windowSize.height) / 2); // Schriftart aus Umgebungsvariable lesen, // falls nicht angegeben, wird "Dialog" benutzt: Font = new Font(tk.getProperty("Schriftart", "Dialog")); // Piepston: tk.beep();
8.5.8
Image-Klassen
seit
SE
ME
EE
1.0/1.2
x
-
x
Die Klassen zur Bildverarbeitung finden Sie im Package java.awt.image. Mit Hilfe der Klasse java.awt.Toolkit können Sie ein Bild aus einer angegebenen Quelle laden (getImage(datei/URL)). Sie erhalten dabei ein Objekt vom Typ java.awt.Image zurück, das sofort zur Verfügung steht. Wenn Sie das Bild nun mit drawImage() ausgeben, kann es passieren, dass das Bild aber noch nicht vollständig im Speicher ist, insbesondere, wenn Sie es über das Internet laden. Deshalb können Sie den Ladezustand eines Objekts überwachen, deren
288
Weitere wichtige AWT-Klassen
Sandini Bib
Klasse das Interface java.awt.image.ImageObserver implementiert. Dies tun beispielweise alle AWT- und Swing-Kontrollelemente. Zur vereinfachten Benutzung beim Laden mehrerer Bilder gibt es auch noch die Klasse java.awt.MediaTracker. Die weiteren Klassen im Package java.awt.image ermöglichen Ihnen eine Skalierung, Rotation, Filterung und andere Veränderungen der Bilder. Als Bildformate werden GIF und JPG unterstützt. In Java 2 kamen etliche Klassen zur pixelorientierten Erzeugung und Bearbeitung von Bildern im Package java.awt.image und dem neuen java.awt.image.renderable hinzu. Die Klassen sind eng verbunden zu den neuen 2D-Grafiken (siehe Abschnitt 8.5.1 und 8.5.5) sowie zu den Farbmodellen (siehe Abschnitt 8.5.2).
... +getImage(datei: String) +getImage(url: URL) +createImage(erzeuger: ImageProducer) +prepareImage(bild: Image, breite: int, höhe: int, ladeKontrolle: ImageObserver) +checkImage(bild: Image, breite: int, höhe: int, ladeKontrolle: ImageObserver) ...
«interface» ImageObserver +ALLBITS : int +SOMEBITS : int +ABORT : int +ERROR : int +PROPERTIES : int +imageUpdate(bild: Image, infoFlags: int, x: int, y: int, breite: int, höhe: int)
Image {abstract} +getHeight/getWidth( ladeKontrolle:ImageObserver) +getSource() +getGraphics() +flush() +getProperty(attribut: String, ladeKontrolle: ImageObserver) +getScaledInstance(vert: int, horiz: int, modus: int)
8 Nitty Gritty • Take that!
Toolkit {abstract}
MediaTracker +COMPLETE: int +LOADING: int +ABORTED: int +ERRORED: int +MediaTracker( kontrollelement: Component) +addImage(bild: Image, id: int) +checkAll/waitForAll() +checkID/waitForID(id: int) +isErrorAny/getErrorsAny() +isErrorID/getErrorsID(id: int)
Bild 8.53: Klassen zur Darstellung von Bildern
Beispiel Toolkit tk = Toolkit.getDefaultToolkit(); MediaTracker tracker = new MediaTracker(); Image bild = tk.getImage("bild.gif"); tracker.addImage(bild, 0);
Oberflächenprogrammierung
289
Sandini Bib tracker.waitForID(0); if (tracker.checkAll() && !tracker.isErrorAny()) { ausgabePanel.getGraphics().drawImage( bild, 0, 0, ausgabePanel); }
In der Swingbibliothek gibt es mit dem Interface Icon und der Klasse IconImage noch weitere Unterstützung für einfache Bilder, die den Ladevorgang mittels MediaTracker intern erledigen.
Nitty Gritty • Take that!
8
8.5.9
Die Klasse PrintJob
seit
SE
ME
EE
1.0/1.3
x
-
x
Die Druckfunktionalität im JDK 1.1 ist nicht besonders komfortabel. Sie basiert auf einem Graphics-Objekt, dieses wird statt auf dem Bildschirm auf einem Blatt Papier ausgegeben. Damit können sämtliche AWT-Grafikelemente auch für den Ausdruck verwendet werden. Erweiterte Funktionen wie automatischer Seitenumbruch fehlen aber. PrintJob {abstract} +getGraphics() +end() +getPageDimension() +getPageResolution() +lastPageFirst()
Bild 8.54: Die Klasse PrintJob
Ablauf 1. Zunächst fordert man einen Druckauftrag mittels der Methode getPrintJob() der Klasse Toolkit an. 2. Daraufhin erscheint ein Fenster zur Druckerauswahl (siehe Abbildung 8.55). Durch Auswahl von Setup können die Eigenschaften des Druckauftrags auf diesem Drucker eingestellt werden, zum 290
Weitere wichtige AWT-Klassen
Sandini Bib
Beispiel Hoch-/Querformat, Auflösung, Papierformat etc. Wenn der Benutzer den Dialog bestätigt, wird ein Objekt der Klasse PrintJob zurückgeliefert, ansonsten null. 3. Vom PrintJob-Objekt erhält man eine Referenz auf das GraphicsObjekt der nächsten zu druckenden Seite und kann darauf zeichnen. 4. Mit der dispose-Methode des Graphics-Objekts schickt man die Seite zum Drucker. 5. Wenn man weitere Seiten drucken möchte, muss man vom PrintJob für jede Seite ein neues Graphics-Objekt beziehen. 6. end() schließt den Druckauftrag ab.
Nitty Gritty • Take that!
8
Bild 8.55: Drucken eines Fensters
Beispiel PrintJob pjob = getToolkit().getPrintJob(null, "Druck-Test", (Properties) null); if (pjob != null) // User hat Job bestätigt { // und Optionen gesetzt // nächste Seite beziehen: Graphics seite = pjob.getGraphics(); if (seite != null) {
Oberflächenprogrammierung
291
Sandini Bib printAll(seite); // Inhalte auf Graphics-Objekt // zeichnen seite.dispose(); // Seite abschließen } pjob.end(); // falls keine weitere Seite folgt }
Das Drucken aus nicht-signierten Applets ist nicht erlaubt und resultiert in einer SecurityException. Java-Version 1.2 enthält im Package java.awt.print Klassen (siehe Abschnitt 8.5.10), die es erlauben, mit verschiedenen Dokumenttypen zu arbeiten, Seitenlayouts, -formate und Druckaufträge zu verwalten. Verwechseln Sie nicht die Klasse PrinterJob mit dem bisherigen PrintJob! In Java-Version 1.3 enthält das Package java.awt die beiden neuen Klassen JobAttributes und PageAttributes, über die es möglich ist, die Attribute des Druckauftrags und des Druckers wie Auflösung, Druckqualität, Papiersorten oder Duplexdruck abzufragen bzw. einzustellen. Dazu wurde eine weitere Variante der getPrintJob-Methode in java.awt.Toolkit hinzugefügt:
Nitty Gritty • Take that!
8
public PrintJob getPrintJob(Frame fenster, String jobName, JobAttributes jobAttribute, PageAttributes seitenAttribute)
8.5.10 PrinterJob und weitere Druckklassen seit
SE
ME
EE
1.2
x
-
x
Die neue Druck-API finden Sie im Package java.awt.print. Diese arbeitet seitenorientiert. Sie definieren ein Dokument (von der Klasse Book oder einer anderen Implementierung von Printable) und definieren für jede Seite ein PageFormat. Diese werden dann dem PrinterJob-Objekt übergeben, das den Druckauftrag in der gewünschten Anzahl auf den gewählten Drucker schickt. Die Formate können optional auch durch den Endbenutzer gesetzt werden. 292
Weitere wichtige AWT-Klassen
Sandini Bib
PrinterJob {abstract} copies: int jobName: String +getPrinterJob() +printDialog() +defaultPage() +cancel/isCancelled() +pageDialog(format: PageFormat) +setPrintable(renderer: Printable [, format: PageFormat]) +setPageable(dokument: Pageable)
PageFormat +PORTRAIT : int +LANDSCAPE : int paper : Paper orientation : int +PageFormat() +get[Imageable]Width/Height() +getImageableX/Y() +getMatrix()
«interface» Printable +PAGE_EXISTS: int +NO_SUCH_PAGE: int +print(quelle: Graphics, format: PageFormat, index: int)
«interface» Pageable +getNumberOfPages() +getPageFormat(seite : int) +getPrintable(seite : int)
Book
Bild 8.56: Druckklassen in Java 2
8
getPrinterJob() liefert ein Objekt vom Typ PrinterJob. Der Benutzer kann im Auswahlfenster, das Sie mit printDialog() anzeigen, einen Drucker wählen und die Anzahl der Kopien angeben. Um einen Auftrag drucken zu können, brauchen Sie zunächst eine oder mehrere Seiten. Im einfachsten Fall können Sie sich Standardseiten mit der Methode defaultPage() erzeugen (DIN A4, Hochformat). Alternativ können Sie mit pageDialog() in einem Auswahlfenster auch den Anwender das Format wählen lassen. setPrintable() verbindet ein Printable-Objekt mit dem PrinterJob und setPageable() ein PageableObjekt. print() startet schließlich den Druck.
Das Interface PageFormat enthält die Orientierung (horizontal/vertikal), die Größe des Papiers sowie des druckbaren Bereichs auf der Seite.
Oberflächenprogrammierung
293
Nitty Gritty • Take that!
Methoden und Attribute
Sandini Bib
Das Interface Pageable hingegen stellt das Dokument mit einer Anzahl von Seiten dar. Jede Seite kann ein eigenes PageFormat besitzen. Die Standardimplementierung ist Book. Diese wird benutzt, wenn man Dokumente mit Seiten unterschiedlichen Formats hat. Das Printable-Interface besitzt nur die Methode print(), die eine Seite für den Druck aufbereitet (Rendering) und dient vor allem für Druckaufträge mit einem einheitlichen Format. Drucken aus einer Klasse mit einheitlichem Format: Das Interface Printable print(Graphics quelle, PageFormat format, int seitenIndex) wird aufgerufen durch den print-Befehl eines PrinterJob-Objekts, welches mit dem Printable-Objekt verbunden ist – und zwar nacheinander für alle Page-Indizes. Wenn die Methode PAGE_EXISTS zurückgibt, so wird auf den Drucker die Seite (im PageFormat format) ausgedruckt, die vorher auf das Graphics-Objekt quelle gezeichnet wurde. Wenn die Methode Printable.NO_SUCH_PAGE liefert, so wird nichts gedruckt.
Nitty Gritty • Take that!
8
Beispiel import java.awt.*; import java.awt.print.*; public class SeitenPrint implements Printable { public static void main(String[] args) { SeitenPrint seitenprint = new SeitenPrint(); PrinterJob pjob = PrinterJob.getPrinterJob(); if (pjob.printDialog()) // Druckerauswahl { PageFormat format = // Seitenformat pjob.pageDialog(pjob.defaultPage()); pjob.setPrintable(seitenprint, format); try { pjob.print(); } // Druck starten catch (PrinterException e) { } } } public int print(Graphics g, PageFormat format, int index) throws PrinterException {
294
Weitere wichtige AWT-Klassen
Sandini Bib if (index >3 ) return Printable.NO_SUCH_PAGE; g.setFont(new Font("Serif", Font.PLAIN, 48)); g.drawString("Seite: " + String.valueOf(index), 100, 200); return Printable.PAGE_EXISTS; } }
Drucken aus verschiedenen Klassen oder Formaten Wenn Sie Dokumente drucken, bei denen die Seiten unterschiedliche Formate besitzen, können Sie das Interface Pageable implementieren oder Sie benutzen ein Objekt der Klasse Book, welches die Printables mit ihrem PageFormat in einem Vector ablegt und über eine append-Methode verfügt.
Beispiel Book bk = new Book(); bk.append(Printable, job.defaultPage()); bk.append... pjob.setPageable(bk); try { pjob.print(); } catch (PrinterException e) { /* Exception behandeln */ }
8.6 Swing Mit Java 2 kam eine weitere Oberflächenbibliothek neben AWT hinzu – Swing. Diese im Package javax.swing und einigen Subpackages enthaltene Bibliothek enthält eine Vielzahl mächtiger Klassen, zum Beispiel Baumansichten, Tabellen und Notizbuch-Kontrollelemente. Sie ist der wesentliche Bestandteil der Java Foundation Classes, deshalb liest man statt Swing häufig auch die Abkürzung JFC. Einen Vergleich zu AWT finden Sie in Abschnitt 8.1.1.
Oberflächenprogrammierung
295
8 Nitty Gritty • Take that!
Book() erstellt ein neues Book ohne Einträge. append() fügt hinten an das Buch eine Printable-Seite mit zugehörigem PageFormat. Durch Zuweisen eines Book-Objekts an einem PrinterJob und Aufruf der print()-Methode werden jeweils die print-Methoden der einzelnen Printable-Objekte aufgerufen.
Sandini Bib
javax.swing, javax.swing.event & javax.swing.table/tree/text Klassen
Interfaces
Nitty Gritty • Take that!
8
- ButtonModel - ComboBoxModel EreignisKontroll- ListModel Fenster Menüs behandlung elemente - TreeModel - TableModel - ListDataEvent - JMenuBar - JFrame - JComponent - TableColumnM.l - ListSelectionEv. - JButton - JMenuItem - JDialog - BoundedRangeM. - TableModelEv. - JCheckBox- JPanel - JCheckBox - Renderer - TreeModelEvent - JRadioButton - JApplet MenuItem - ListCellRend. - TreeExpansionEv. - JLabel - JRadioBox- JFileChooser - TableCellRend. - TreeSelectionEv. - JTextField MenuItem - JColorChooser - TreeCellRend. - CaretEvent - JPasswordField - JDesktopPane - JMenu - TreeCellEditor - MenuEvent - JInternalFrame - JPopupMenu - JTextArea - TableCellEditor - PopupMenuEv. - JComboBox - JToolBar - JOptionPane - ComboBoxEditor ... - JSeparator - JScrollPane - JList - TreeNode - JTabbedPane - JScrollbar - MutableTreeNode - JSilder - Border - JProgressBar - Scrollable - JTree - MenuElement - JTable - Document - Caret - Style ModelSonstiges Border Bilder - StyledDocument implementierg. - DesktopManger - ImageIcon - AbstractListM. - BorderFactory - Abstract- Icon Document - ButtonGroup - TitledBorder - SwingConstants - DefaultListM. - PlainDoc. - KeyStroke - AbstractTableM. - LineBorder - *Listener - DefaultStyled- - Debug- DefaultTableM. - BevelBorder ... Document Graphics - DefaultTreeM. - SoftBevelB. - DefaultCaret - LookAndFeel - DefaultButtonM. - EmptyBorder - UIManager - DefaultCombo- - EtchedBorder - EditorKit - DefaultEditor- - SwingUtilites - CompoundB. BoxModel Kit - Timer - MatteBorder - DefaultListSelectionModel - AbstractBorder - HTMLEditorKit - RTFEditorKit - DefaultSingle- StyleContext SelectionModel - StyleConstants - DefaultMutableTreeNode - DefaultTreeSelectionModel - TreePath
Bild 8.57: Klassen und Interfaces der Swing-Bibliothek
Die Programmierung von grafischen Oberflächen ohne Hilfsmittel erfordert ein gutes Vorstellungsvermögen. Außerdem ist die Programmierung aufgrund von Volumen und Routinetätigkeiten fehleranfällig und der produzierte Code schwer wartbar. Deshalb empfiehlt sich der Einsatz von GUI-Editoren, wie sie in vielen integrierten Entwicklungsumgebungen zu finden sind.
296
Swing
Sandini Bib
8.6.1 Model-View-Controller-Entwurfsmuster AWT verwaltet alle Daten, die dargestellt werden sollen, auch in den Oberflächenkontrollelementen. So sind z. B. die Elemente einer Liste sowohl in der ursprünglichen Quelle als auch als Strings im List-Objekt enthalten. Diese doppelte Verwaltung ist natürlich ungünstig, vor allem wenn sich die Daten auf beiden Seiten ändern können und deshalb synchronisiert werden müssen. Swing hingegen benutzt den Model-View-Controller-Ansatz, das heißt die konsequente Trennung der Daten und der Darstellung. Beim Model-View-Controller-Entwurfsmuster (MVC) unterscheidet man drei Arten von Applikationselementen: Model (Datenmodell) – Im Datenmodell werden Zustand und Funktionalität gespeichert. Es kann Anfragen vom View über seinen Zustand beantworten und Zustandsänderungswünsche vom Controller oder anderen Objekten verarbeiten.
T
View (Darstellung, »Look«) – Die Ansicht weiß nichts oder nur sehr wenig über das Datenmodell und stellt lediglich die Daten grafisch dar.
T
Controller (Steuerung, »Feel«) – Die Steuerung stellt ein Modell der realen Welt dar und reagiert auf die Benutzereingaben von Maus, Tastatur oder anderen Eingabegeräten.
Erreicht wird dadurch eine strikte Trennung von Oberfläche und Programmlogik. Swing hält diese Trennung allerdings nicht überall durch. Zwar sind Daten- und Funktionsmodelle von der Darstellung (View) getrennt, aber die Darstellung beinhaltet auch die Benutzerinteraktion (Delegate). Man kann jedoch bei manchen Kontrollelementen die Controller-Komponenten aus der Darstellung (View) herauslösen und durch andere ersetzen (zum Beispiel die Selektion oder das Editieren von Daten). Daten
Model Applikation
Kontrollelement Delegate Swing/App.
Look & Feel
Inhalte
UIManager Swing/JRE
Inhalte
Grafikprimitive Betriebssystem
Bild 8.58: Schritte zur Darstellung in Swing Oberflächenprogrammierung
297
8 Nitty Gritty • Take that!
T
Sandini Bib
Die Darstellung läuft in folgenden Schritten ab: 1. Das Kontrollelement fragt vom Model nur diejenigen Werte ab, die momentan sichtbar sind. 2. Diese Daten werden an einen UIManager weitergegeben, der das tatsächliche Rendering durchführt. So kann eine Baumansicht zum Beispiel wie unter Windows oder UNIX/Motif gewohnt dargestellt werden. 3. Die implementierenden Klassen zeichnen mit Hilfe der Grafikprimitive die Oberfläche und geben sie auf dem Zielbetriebssystem aus. 8.6.2
Die Klasse JComponent
seit
SE
ME
EE
1.2
x
-
x
Swing-Elemente fallen in zwei Gruppen:
Nitty Gritty • Take that!
8
T
Fenster (siehe Abschnitt 8.2.10), die direkt vom Betriebssystem dargestellt werden, z. B. JFrame, JDialog oder JApplet. Diese sind von den jeweiligen AWT-Komponenten abgeleitet und werden Heavy-weight-Komponenten genannt.
T
Kontrollelemente, die von Java selbst gerendert werden (Lightweight-Komponenten). Diese sind Subklassen von JComponent.
javax.swing.JComponent ist von java.awt.Container und damit von java.awt.Component abgeleitet und enthält damit alle in Abschnitt 8.2.1 und 8.2.9 beschriebenen Methoden. Als Container können alle JComponents insbesondere auch andere JComponents enthalten.
298
Swing
Sandini Bib
Container
JComponent border: Border toolTipText: String alignmentX/Y: float opaque: boolean +addPropertyChangeListener(l: PropertyChangeListener) +firePropertyChange(attribut: String, alterWert: , neuerWert: ) +getActionForKeyStroke(tastenkombination: KeyStroke) +registerKeyboardAction(aktion: ActionListener, tastenkombination: KeyStroke, bedingung: int)
Bild 8.59: Die Klasse JComponent
Methoden und Attribute
Bild 8.60: Border-Varianten
Per Tooltips wird eine Kurzbeschreibung eingeblendet, wenn der Cursor länger über der Komponente verweilt. Die so genannten Mnemonics oder KeyboardActions definieren Tastaturkürzel und werden insbesondere bei Menüs häufig verwendet.
Oberflächenprogrammierung
299
8 Nitty Gritty • Take that!
Mit dem border-Property kann ein Rahmen um die Komponente gezogen werden. Hierfür gibt es verschiedene Borderklassen, die das Aussehen der Umrandung definieren. Beispiele sind Ränder mit 3DEffekt oder mit in der Umrandung integrierter Beschriftung (siehe Abbildung 8.60). Einige Klassen dazu befinden sich im Package javax.swing.border oder in den plattformspezifischen Look & FeelBibliotheken.
Sandini Bib
Man registriert eine Aktion und die zugehörige Tastenkombination mit registerKeyboardAction(). In den Sprachversion 1.3 gibt es für die Tastatursteuerung die beiden neuen Swing-Klassen InputMap und ActionMap. Dabei handelt es sich um Zuordnungstabellen, die Tastenkombinationen mit bestimmte Aktionen verbinden. Bei Swing-Komponenten kann ein UIManager angegeben werden, der das plattformspezifische Aussehen (Look & Feel) bestimmt.
8 Nitty Gritty • Take that!
Bild 8.61: Look & Feel-Varianten eines Dialogfensters
Beispiel try { // Standard sei Windows Look & Feel UIManager.setLookAndFeel( "com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); } catch (ClassNotFoundException e) { // Windows Look & Feel nicht gefunden -> Plattform L&F UIManager.setLookAndFeel( }
300
Swing
Sandini Bib
Zusammenfassung Beschreibung
Benutzung
Rahmen
Definition und Aussehen eines Rahmens eines Kontrollelements
setBorder() BorderFactory und BorderKlassen
Tooltips
Hilfe, wenn der Cursor länger über der Komponente verweilt
setToolTipText()
KeyboardNavigation
Definition von Tastaturkürzeln
registerKeyboardAction() setAccelerator/ setMnemonic() bei einigen Kontrollelementen
Darstellung
Definition von LayoutRestriktionen
setMinimumSize() setMaximumSize() setPreferredSize() setAlignmentX() setAlignmentY() UIManager.setLookAndFeel()
8.6.3 Übersicht über die Swing-Kontrollelemente Die folgende Tabelle stellt eine Übersicht über die wichtigsten Funktionen der Swing-Kontrollelemente dar, die zusammen mit der Standardfunktionalität von JComponent (siehe Abschnitt 8.6.2) vermutlich 90% aller Anwendungsfälle abdecken. Die in Klammern angegebenen Zahlen beziehen sich auf Erläuterungen, die Sie im Anschluss an die Übersicht finden.
Oberflächenprogrammierung
301
8 Nitty Gritty • Take that!
Funktionalität
Sandini Bib
Beschreibung
Spezielle Properties
Methoden
Events
text (nicht label!) actionCommand icon/pressedIcon enabled disabledIcon defaultButton
doClick(int)
actionPerformed
text (nicht label!) selected (nicht state!) icon pressedIcon disabledIcon
isSelected() setSelected (boolean) doClick(int)
itemStateChanged actionPerformed
text selected icon pressedIcon disabledIcon
isSelected() setSelected (boolean) doClick(int)
itemStateChanged actionPerformed
text icon horizontal/verticalAlignment
setText(String)
JButton Aktionsknopf, Pushbutton
JCheckBox Kontrollkästchen
JRadioButton
Nitty Gritty • Take that!
8
Optionsschalter (zusammen mit ButtonGroup mit den Methoden add und remove) JLabel Statischer Text
JTextField, JPasswordField Datenfeld, einzeilige Texteingabe
302
Swing
text selectedText document columns editable horizontalAlignment echoChar (nur bei JPasswordField)
setText(String) cut/copy/paste()
actionPerformed keyTyped caretUpdate
Sandini Bib Beschreibung
Spezielle Properties
Methoden
Events
text selectedText document columns rows lineWrap lineCount editable
setText(String) append (String) insert (String, int) cut/copy/paste() getCaretPosition() selectAll()
keyTyped caretUpdate
value minimum/maximum major/minorTabSpacing orientation paintTicks/paintLabels snapToTicks extent model
getValue() setValue (int)
stateChanged
value minimum/maximum block/unitIncrement orientation visibleAmount model
getValue() setValue (int)
adjustmentValueChanged
model (1) selection-Mode (2) selectedValue[s] selectedIndex first/lastVisibleIndex visibleRowCount selectionModel cellRenderer
ensureIndexIsVisible(...) clearSelection() addSelectionInterval(...) isSelectedIndex(int) isSelectionEmpty() setListData(...) (1)
valueChanged
JTextArea Datenfeld, mehrzeilige Texteingabe
JSlider
8
JScrollBar Verschiebebalken (horizontal und vertikal)
JList Listenfeld, Liste von Strings
Oberflächenprogrammierung
303
Nitty Gritty • Take that!
Schieberegler
Sandini Bib Beschreibung
Spezielle Properties
Methoden
Events
model editable selectedItem selectedIndex itemCount maximumRowCount cellRenderer
addItem(Object) insertItemAt(Object, int) remove [ItemAt](int) remove-All[Items]() getItem-At(int)
itemStateChanged actionPerformed
model (3) editable editing/editingPath cellEditor cellRenderer selectionModel selectionPath min/maxSelectionRow selectionCount/Row[s] visibleRowCount
getRowForPath() collapsePath Row(...) isCollapsed(...) expandPath/ Row(...) isExpanded(...) startEditingAtPath(...) stopEditing() addSelectionInterval(...) addSelectionRows(...) addSelectionPath[s](...) clearSelection()
treeCollapsed treeExpanded treeSelectionEvents
JComboBox Kombinationsliste, Drop-downListe
JTree Baumansicht, Browserdarstellung
Nitty Gritty • Take that!
8
304
Swing
Sandini Bib Beschreibung
Spezielle Properties
Methoden
model (4) selectionMode selectedRow[s] selectedColumn[s] column/rowSelectionAllowed autoCreateColumnsFromModel cellEditor defaultRenderer showHorizontalLines showVerticalLines column/rowCount gridColor intercellSpacing selectionModel
getValue-At(int, int) setValue-At(...) editCell-At(int, int) add/removeColumn(...) moveColumn(int, int) add/removeColumn/ RowSelectionInterval(...) selectAll() clearSelection() isCellSelected(int, int) isRowSelected(int) isColumnSelected(int) isCellEditable(int, int)
Events
JTable Tabelle
8
Tabellenspalte
headerValue identifier cellRenderer cellEditor resizable width
sizeWidthToFit() (5) getModelIndex()
floatable (6)
add/remove(...) addSeparator()
value minimum/maximum orientation visibleAmount model
getValue() setValue(int)
Nitty Gritty • Take that!
TableColumn
JToolBar Funktionsleiste JProgressBar Skala, Fortschrittsanzeige
stateChanged
Oberflächenprogrammierung
305
Sandini Bib Beschreibung
Spezielle Properties
Methoden
Events
layout components
add(Component) getComponent[At](...) getComponentCount() remove(...) removeAll()
componentAdded componentRemoved
viewport horizontalScrollBar[Policy] verticalScrollBar[Policy] column/rowHeaderView
siehe JPanel! createHorizontal/ VerticalScrollBar()
siehe JPanel!
orientation left/rightComponent bottom/topComponent [last]dividerLocation dividerSize
siehe JPanel! resetToPreferredSizes()
siehe JPanel!
tabPlacement tabCount selectedComponent selectedIndex model
siehe JPanel! add/removeTabAt(...) get/setTitleAt(...) get/setIconAt(...)
siehe JPanel! stateChanged
document/styledDocument, page text, contentType editable editorKit logicalStyle selectedText
cut/copy/paste() setPage(URL) getContentType() selectAll()
caretUpdate (7) hyperlinkUpdate
JPanel Panel, Fensterbereich für Layout
JScrollPane Teilfenster mit Schiebeleisten
JSplitPane
Nitty Gritty • Take that!
8
2 Panels mit Trennleiste
JTabbedPane Notizbuch
JTextPane (7) Dateneingabe mit Textbearbeitungsfunktionen
(JEditorPane Superklasse von JTextPane)
306
Swing
Sandini Bib Beschreibung
Spezielle Properties
Methoden
Events
messageType message icon optionType (9) value wantsInput inputValue initialValue initialSelectionValue
showConfirmDialog(...) showMessageDialog(...) showInputDialog(...) createDialog(...) getSelectionValues() selectInitialValue()
propertyChange
title resizable size/location iconImage defaultCloseOperation menuBar/JMenuBar contentPane
setVisible (boolean) show() dispose() repaint()
windowOpened windowClosing windowActivated windowDeactivated
title resizable closable/closed maximizable selected iconifiable icon/frameIcon size defaultCloseOperation menuBar contentPane
setVisible (boolean) show() setClosed (boolean) setMaximum(boolean) setSelected (boolean) setIcon (boolean) repaint()
JOptionPane (8)(9) Messagebox oder Inhalt für eigene Messageboxen
JFrame Anwendungsfenster
Inneres Nebenfenster (MDI)
Oberflächenprogrammierung
Nitty Gritty • Take that!
JInternal-Frame
8
307
Sandini Bib Beschreibung
Spezielle Properties
Methoden
Events
componentsInLayer componentsCountInLayer
getDesktopManager() setDesktopManager(...) getComponents() getComponentAt(...) getComponentInLayer() moveToBack/Front(...) get/setLayer(...)
componentAdded componentRemoved
title resizable size modal defaultCloseOperation contentPane
setVisible (boolean) show() dispose() repaint() toBack/Front() setLocationRelativeTo()
windowOpened windowClosing windowActivated windowDeactivated
appletContext codeBase documentBase appletInfo parameterInfo
siehe JPanel! start/stop() init/destroy() repaint() getParameter (String) getAudioClip(URL) play() getImage (URL)
siehe JPanel!
menuCount
add(JMenu) getMenu(int) remove(...)
JDesktopPane Fensterbereich, der interne Nebenfenster beinhaltet (MDI)
JDialog Dialogfenster, Meldefenster
8 Nitty Gritty • Take that!
JApplet Applet, Anwendung im Webbrowser
JMenuBar Menüleiste
308
Swing
Sandini Bib Beschreibung
Spezielle Properties
Methoden
Events
label popupSize
add(JMenuItem) addSeparator() show(...)
popupMenuWillBecomeVisible/Invisible popupMenuCanceled
text (nicht label!) actionCommand accelerator mnemonic icon itemCount menuComponentCount selected
add(JMenuItem) addSeparator() insert/remove(...) doClick(int) isSelected() isTopLevelMenu()
actionPerformed itemStateChanged menuSelected menuDeselected menuCanceled
JPopupMenu Kontextmenü
JMenu Menü in einer MenuBar oder als Untermenü
8
JMenuItem text (nicht label!) actionCommand accelerator mnemonic icon pressedIcon disabledIcon
doClick(int) isSelected()
itemStateChanged actionPerformed
Nitty Gritty • Take that!
Menüeintrag
JRadioButton-MenuItem, JCheckBox-MenuItem Menüeintrag
text (nicht label!) selected actionCommand accelerator mnemonic selected icon selectedIcon pressedIcon disabledIcon
doClick(int) isSelected()
itemStateChanged actionPerformed
Oberflächenprogrammierung
309
Sandini Bib
Anmerkungen 1. Das Modell für JList muss das Interface ListModel implementieren; als Ableitungsbasis für die konkrete (Daten-)Klasse kann AbstractListModel verwendet werden; die Klasse DefaultListModel enthält eine Implementierung als Vector. Wenn es sich um eine einfache statische Liste handelt, können Sie auch auf den Entwurf eines Modells verzichten und stattdessen die Daten in Form eines Vectors oder Arrays mit der Methode setListData() der JList zuweisen. 2. Selektionsmodi: – einfache Selektion (höchstens ein Eintrag) MULTI_INTERVAL_SELECTION – Mehrfachselektion (beliebig viele Einträge)
T SINGLE_SELECTION T
– Bereichsselektion (durchgehendes Intervall) Das Modell für JTree muss das Interface TreeModel implementieren; als Ableitungsbasis für die konkrete (Daten-)Klasse kann DefaultTreeModel verwendet werden. Die einzelnen Knoten müssen die Interfaces TreeNode oder MutableTreeNode implementieren; die Klasse DefaultMutableTreeNode enthält eine universelle Implementierung inklusive Navigationsmethoden für Baumstrukturen. Das Modell für JTable muss das Interface TableModel implementieren; als Ableitungsbasis für die konkrete (Daten-)Klasse kann AbstractTableModel verwendet werden; die Klasse DefaultTableModel enthält eine Implementierung aus ineinander verschachtelten Vector-Objekten. Die Breitenanpassung wird nicht automatisch durchgeführt, sondern muss nach Änderungen der dargestellten Daten angestoßen werden. Die Nutzung von frei schwebenden und verschiebbaren Toolbars setzt einige Anpassungsarbeit voraus. Insbesondere sollte im Parent-Fenster BorderLayout als LayoutManager gewählt und die Toolbar am Rand platziert werden. Das mittlere Feld (CENTER) enthält die Client-Fläche. Die anderen (Layout-) Felder dürfen nicht belegt sein, da man sonst die Toolbar ebenfalls in diese Felder ziehen könnte, die Folge wären überlappende Kontrollelemente. T SINGLE_INTERVAL_SELECTION
3.
Nitty Gritty • Take that!
8 4.
5.
6.
310
Swing
Sandini Bib
7. Das JTextPane ist (im Gegensatz zu JTextArea) ein komplexes Kontrollelement für die Eingabe und Darstellung von formatiertem Text, zum Beispiel in HTML- oder RTF-Format. Das Caret (Textcursor) dient zur Positionierung und Selektion. 8. JOptionPane ist als einfaches Meldungs- und Eingabefenster gedacht. Es verfügt über eine Reihe von Standardtypen, die durch verschiedene Symbole (Icons) gekennzeichnet sind. T T T T
ERROR_MESSAGE INFORMATION_MESSAGE WARNING_MESSAGE QUESTION_MESSAGE T PLAIN_MESSAGE (ohne Symbol) Icon Error Look & Feel
Information Question Warning
Metal
Motif
8
Windows
8.6.4
Nitty Gritty • Take that!
Bild 8.62: Symbole für JOptionPane
Die Klasse JButton
seit
SE
ME
EE
1.2
x
-
x
Bild 8.63: Darstellung von JButtons
Buttons, häufig auch Schaltflächen oder Pushbuttons genannt, sind rechteckige Knöpfe, die eine Aktion auslösen, wenn der Benutzer sie anklickt. Die Klasse JButton ist das Äquivalent für den AWT-Button
Oberflächenprogrammierung
311
Sandini Bib
(siehe Abschnitt 8.2.3). Die Mehrzahl der Methoden ist bereits in der Superklasse AbstractButton definiert. JComponent
AbstractButton text: String selected: boolean model: buttonModel actionCommand: String icon, disabledIcon: Icon pressedIcon, selectedIcon: Icon rolloverIcon: Icon rolloverEnabled: boolean mnemonic: int margin: Insets +doClick() +addActionListener(l: ActionListener) +addChangeListener(l: ChangeListener) ...
JButton
Nitty Gritty • Take that!
8
+JButton() +JButton(aufschrift: String) +JButton(symbol: Icon) +JButton(aufschrift: String, symbol: Icon)
Bild 8.64: Die Klasse JButton
Methoden und Attribute Von der Ereignisbehandlung her unterscheidet sich der JButton nicht von einem AWT-Button. Das Property actionCommand ist ein String, der den Knopf beim ActionEvent charakterisiert. Der Event mit der zugehörigen Methode actionPerformed() wird ausgelöst, wenn der Button gedrückt wird. Wird actionCommand nicht besetzt, so erhält er den Wert von text; bei mehrsprachigen Oberflächen kann es aber sinnvoll sein, dass sich die beiden Werte unterscheiden. Wie im Abschnitt über Ereignisbehandlung beschrieben (8.4), müssen alle Klassen, die benachrichtigt werden sollen, wenn der Button gedrückt wurde, mit addActionListener() einen Listener registrieren.
312
Swing
Sandini Bib
Über das Property mnemonic können Sie ein Tastaturkürzel definieren; das Property für die Aufschrift heißt nun bei allen Swing-Komponenten einheitlich text. Mit der Methode doClick() kann ein Anklicken des Knopfes simuliert werden. Bei der Gestaltung des Aussehens ist man wesentlich flexibler geworden: Man kann für die verschiedenen Zustände eines Knopfes Symbole angeben (icon, pressedIcon, disabledIcon, rolloverIcon) und die Definition eines Rahmens ist möglich. So könnte man zum Beispiel auch einen Knopf völlig rahmenlos und mit einem Bild hinterlegt in eine Grafik integrieren. Das Modell vom Typ ButtonModel kennzeichnet die Zustände eines JButtons, wie selected, pressed oder rollover. Daneben sind die EventListener-Methoden und die Tastatursteuerung gefordert. Die Standardimplementierung ist DefaultButtonModel. Beispiel JButton okButton = new JButton("OK", new ImageIcon("ok.gif")); okButton.setActionCommand("OK"); okButton.addActionListener(eventHandler);
Die Klassen JCheckbox und JRadioButton
seit
SE
ME
EE
1.2
x
-
x
Nitty Gritty • Take that!
8.6.5
8
Bild 8.65: Darstellung von JCheckbox und JRadioButton
In Swing werden runde und eckige Optionskästchen nicht mehr in einer Klasse zusammengefasst (vergleiche AWT 8.2.4), sondern durch die beiden Klassen JCheckBox und JRadioButton dargestellt. Die Zuordnung zu einer Gruppe sich gegenseitig ausschließender Optionen geschieht durch ein Objekt der Klasse ButtonGroup.
Oberflächenprogrammierung
313
Sandini Bib
ButtonGroup
AbstractButton
JToggleButton
+ButtonGroup() +add(button: AbstractButton) +getSelection() +setSelected(b: ButtonModel, zustand: boolean) +getElements() +remove(b: AbstractButton)
JCheckBox
JRadioButton
+JCheckBox() +JCheckBox(aufschrift: String) +JCheckBox(aufschrift: String, selektiert: boolean) +JCheckBox(symbol: Icon) +JCheckBox(symbol: Icon, selektiert: boolean) +JCheckBox(aufschrift: String, symbol: Icon) +JCheckBox(aufschrift: String, symbol: Icon, selektiert: boolean)
+JRadioButton() +JRadioButton(aufschrift: String) +JRadioButton(aufschrift: String, selektiert: boolean) +JRadioButton(symbol: Icon) +JRadioButton(symbol: Icon, selektiert: boolean) +JRadioButton(aufschrift: String, symbol: Icon) +JRadioButton(aufschrift: String, symbol: Icon, selektiert: boolean)
Bild 8.66: JCheckbox und JRadioButton
Nitty Gritty • Take that!
8
Methoden und Attribute JCheckBox und JRadioButton besitzen wie der JButton ein text-Property, darüber hinaus ist der Zustand selected wichtig (nicht wie in AWT state!). Eine Änderung des Zustandes wird durch einen ItemEvent (itemStateChanged()) signalisiert. Häufig reagiert man jedoch nicht sofort auf eine Änderung, sondern fragt den aktuellen Zustand einer Option erst beim Starten einer Aktion ab. Alle bedeutsamen Methoden sind in den Superklassen definiert, eine Beschreibung finden Sie im Abschnitt 8.6.4. Die Klasse ButtonGroup JRadioButtons werden mit Hilfe eines ButtonGroup-Objekts als Reihe sich gegenseitig ausschließender Optionen zusammengefasst. Mittels add() fügen Sie ein JRadioButton-Objekt der Gruppe hinzu und mit getSelection() fragen Sie die aktuell gewählte Option ab. Beispiel ButtonGroup optionen = new ButtonGroup(); JRadioButton option1 = new JRadioButton("Bildschirmausgabe",true);
314
Swing
Sandini Bib optionen.add(option1); JRadioButton option2 = new JRadioButton( "Druckausgabe",false); optionen.add(option2); [...] boolean druck = option2.isSelected(); ButtonModel ausgewaehlt = optionen.getSelection();
8.6.6
Die Klasse JLabel
seit
SE
ME
EE
1.2
x
-
x
Bild 8.67: Darstellung von JLabels
JComponent
JLabel text: String icon, disabledIcon: Icon horizontal/verticalAlignment: int horizontal/verticalTextPosition: int iconTextGap: int +JLabel() +JLabel(aufschrift: String) +JLabel(symbol: Icon) +JLabel(aufschrift: String, ausrichtung: int) +JLabel(aufschrift: String, symbol: Icon, ausrichtung: int)
Bild 8.68: Die Klasse JLabel
Oberflächenprogrammierung
315
8 Nitty Gritty • Take that!
Ein JLabel ist ein statischer Text, der zur Beschriftung innerhalb von Fenstern verwendet wird. Neben textuellen Beschriftungen sind auch Symbole möglich. Die Klasse ist das Pendant zur AWT-Klasse Label (siehe Abschnitt 8.2.5).
Sandini Bib
Methoden und Attribute Der dargestellte Text steht im Attribut text und ist mit der Methode setText() modifizierbar. Die Ausrichtung kann linksbündig (Voreinstellung, SwingConstants.LEFT), rechtsbündig (SwingConstants.RIGHT) oder zentriert (SwingConstants.CENTER) gewählt werden. Neben dem Text kann auch ein Symbol für den Normal- und den abgeschalteten Zustand angegeben werden (icon, disabledIcon). iconTextGap bestimmt den Abstand zwischen Symbol und Text, voreingestellt sind 4 Pixel. Beispiel Label beschriftung = new Label("Ein Label"); beschriftung.setAlignment(Label.CENTER); JLabel beschriftung = new Label("Ein Textlabel"); JLabel symbolLabel = new Label("Label mit Symbol", new ImageIcon("euro.gif"), SwingConstants.LEFT); symbolLabel.setIconTextGap(10);
8.6.7
Die Klassen JTextField und JPasswordField
Nitty Gritty • Take that!
8 seit
SE
ME
EE
1.2
x
-
x
Bild 8.69: Darstellung von JText- und JPasswordField
Ein JTextField ist ein einzeiliges Eingabefeld, ein JPasswordField das davon abgeleitete Feld für die maskierte Eingabe von Passwörtern. Beide sind von der gemeinsamen Basisklasse JTextComponent abgeleitet, die Funktionalität wie Selektion oder Editieren bereitstellt. Die Klassen sind das Pendant zu den AWT-Klassen TextField und TextComponent (siehe Abschnitt 8.2.6).
316
Swing
Sandini Bib
JComponent
JTextField JTextComponent text: String document: Document caret: Caret caretPosition: int selectionStart: int selectionEnd: int +select(anfang: int, ende: int) +selectAll() +getSelectedText() +isEditable() +setEditable(editierbar: boolean) +cut/copy/paste() +read(quelle: Reader, typ: String) +write(ziel, Writer) +addCaretListener(l: CaretListener)
columns: int +JTextField() +JTextField(vorgabetext: String) +JTextField(darstellbareZeichen: int) +JTextField(vorgabetext: String, darstellbareZeichen: int)
JPasswordField echoChar: char <> +JPasswordField() +JPasswordField(vorgabePasswort: String) +JPasswordField(darstellbareZeichen: int) +JPasswordField(vorgabePasswort: String, darstellbareZeichen: int) <<Methoden>> +echoCharIsSet() +getPassword()
8
Bild 8.70: Die Klassen JTextField und JPasswordField
Wie beim JLabel gibt es ein Attribut text. Dieser Text ist bei Eingabefeldern in der Regel editierbar, diese Funktion können Sie aber per setEditable(false) abschalten. Der Text kann mit Hilfe der read- und write-Methoden auch über Streams eingelesen oder geschrieben werden. Mit verschiedenen select-Methoden können Sie einen Textbereich markieren bzw. die Markierung abfragen. Die caretPosition gibt die Eingabeposition an, getCaret() den Eingabecursor. Statt den unter AWT üblichen TextEvents liefern JTextComponents Caret- und KeyEvents. cut(), copy() und paste() dienen zum Zugriff auf die Systemzwischenablage. columns gibt an, wie groß das Feld bei dynamischen Layouts (siehe LayoutManager im Abschnitt 8.3) dimensioniert werden muss, damit die angegebene Zeichenzahl in der eingestellten Schriftart Platz findet.
Oberflächenprogrammierung
317
Nitty Gritty • Take that!
Methoden und Attribute
Sandini Bib
Bei JPasswordFields gibt es zudem das Attribut echoChar, mit dem ein Ersatzzeichen festgelegt werden kann, der Standard ist »*«. Außerdem ist bei Objekten dieser Klasse cut() und copy()gesperrt. Beispiel JTextField userIDFeld = new JTextField(20); JPasswordField passwortFeld = new JPasswordField(20); [...] String userID = userIDFeld.getText(); char[] passwort = passwortFeld.getText();
8.6.8
Nitty Gritty • Take that!
8
Die Klassen JTextArea und JTextPane
seit
SE
ME
EE
1.2
x
-
x
Bild 8.71: Darstellung von JTextArea und JTextPane
Auch die komplexeren Textkomponenten sind von JTextComponent abgeleitet (siehe Abschnitt 8.6.7). JTextArea ist ein mehrzeiliges Eingabefeld, das jedoch nicht die Funktionalität von java.awt.TextArea (siehe Abschnitt 8.2.6) besitzt, dass bei Bedarf Scrollbalken erzeugt werden (siehe auch Abschnitt 8.6.19: JScrollPane). JTextPane ist ein komplexes Kontrollelement für die Eingabe und Darstellung von formatiertem Text. Das Kontrollelement greift auf die Dienste eines EditorKits zurück. Als Formate sind bisher Rich Text Format (RTFEditorKit) und HTML 3.2 (HMTLEditorKit) enthalten, jedoch noch nicht die volle Unterstützung von HTML 4.0. Hilfsklassen und -Interfaces wie Document, Style oder EditorKit finden sich im Package javax.swing.text.
318
Swing
Sandini Bib
JTextComponent
columns: int rows: int lineWrap: boolean wrapStyleWord: boolean tabSize: int <> +JTextArea() +JTextArea(vorgabetext: String) +JTextArea(modell: Document) +JTextArea(zeilen : int, spalten : int) +JTextArea(vorgabetext: String, zeilen: int, spalten: int) +JTextArea(modell: Document, vorgabetext: String, zeilen: int, spalten: int) <<Methoden>> +append(text: String) +insert(text: String, position: int) +replaceRange(text: String, anfang: int, ende: int)
+getLineCount() ...
JEditorPane contentType: String editorKit: EditorKit <> +JEditorPane() +JEditorPane(url: String/URL) +JEditorPane(mimeTyp: String, text: String) <<Methoden>> +getPage() +setPage(url: String/URL) +replaceSelection(text: String)
JTextPane styledDocument: StyledDocument logicalStyle: Style paragraph/characterAttributes: AttributSet <> +JTextPane() +JTextPane(dokument: StyledDocument) <<Methoden>> +addStyle(name: String, stil: Style)
8
Bild 8.72: Die Klassen JTextArea und JTextPane
Methoden und Attribute Neben dem Attribut text teilen sich alle Textelemente das Modell document, das einen strukturierten Text mit einzelnen Absätzen und Stilen beschreiben kann. Die Befehle cut(), copy() und paste() erlauben einen einfachen Zugriff auf die Zwischenablage. Unformatierter Text In einer JTextArea wird allerdings in der Regel nur unformatierter Text dargestellt. Mit der Option lineWrap kann ein automatischer Zeilenumbruch durchgeführt werden, tabSize definiert die Zeichenanzahl, für die ein Tabulator steht. columns bzw. rows gibt an, wie groß das Feld
Oberflächenprogrammierung
319
Nitty Gritty • Take that!
JTextArea
Sandini Bib
bei dynamischen Layouts (siehe LayoutManager im Abschnitt 8.3) dimensioniert werden muss, damit die angegebene Zeichenzahl in der eingestellten Schriftart Platz findet. Beide Werte haben keinen Einfluss darauf, wie viele Zeichen tatsächlich eingegeben werden können. Die mehrzeilige JTextArea erlaubt den Text mittels append(), insert() und replaceRange() zu verändern. Formatierter Text Die Klasse JEditorPane und deren Subklasse JTextPane erlauben verschiedene Typen von Text darzustellen. Diese werden als InternetMIME-Typen angegeben (contentType): text/html, text/rtf oder text/plain. Geladen werden die Dokumente entweder über HTTP (setPage(url)), einen Stream (read()) oder über die setText-Methode. Die Parsing- und Editorfunktionalität ist in einem EditorKit für den jeweiligen MIME-Typ enthalten.
Nitty Gritty • Take that!
8
Die Benutzung von Stilen auf der Basis von Absätzen und Zeichen ist besonders in der Klasse JTextPane ausgeprägt. Beispiel JTextArea textEingabe = new TextArea(); textEingabe.setLineWrap(true); // Zeilenumbruch aktivieren textEingabe.setTabSize(3); // Tabulator = 3 Zeichen textEingabe.setText("Ein mehrzeiliges Textfenster."); textEingabe.append("\nDie Klasse ist JTextArea."); textEingabe.select(4, 29); JScrollPane scroller = new JScrollPane(); // Scrollfläche JEditorPane editor = new JEditorPane(); editor.setContentType("text/html"); // Typ = HTML scroller.getViewport().add(editor); // für Scrolling try { editor.setPage(url); // HTML_Seite laden }
320
Swing
Sandini Bib
8.6.9
Die Klassen JSlider, JScrollBar und JProgressBar
seit
SE
ME
EE
1.2
x
-
x
Bild 8.73: Darstellung verschiedener Schieberegler und Anzeigen
In Swing gibt es drei Kontrollelemente zur Eingabe und Visualisierung von Zahlenwerten aus einem festgelegten Wertebereich: JScrollBar – ein einfacher Scrollbalken
T
JSlider – ein komplexer Schieberegler mit Skala JProgressBar – eine Fortschrittsanzeige
T
8
Alle drei unterstützen das BoundedRangeModel-Interface, das den Zugriff auf die Attribute value, minimum, maximum und die Implementierung des ChangeEvents sicherstellt. Methoden und Attribute Das allen drei Klassen gemeinsame BoundedRangeModel definiert den aktuellen Wert als Ganzzahl (value), Minimal- und Maximalwert, den durch den Regler symbolisierten Wertebereich (extent) und ein Flag, das anzeigt, ob der Wert gerade eingestellt wird (valueIsAdjusting). Alle Klassen beinhalten dennoch auch die im Modell definierten und für das Kontrollelement wichtigen Attribute und zudem entweder eine horizontale (HORIZONTAL) oder vertikale Orientierung (VERTICAL). Die Klasse JScrollBar besitzt zudem die Attribute unitIncrement für die Schrittweite, wenn auf die Pfeiltasten geklickt wird (Standard ist 1), und blockIncrement für einen Mausklick auf den weißen Hintergrund des Scrollbalken (Standard ist 10). Aus Kompatibilitätsgründen zur AWT-Scrollbar (siehe Abschnitt 8.2.7) implementiert die Klasse das Interface Adjustable und unterstützt den AdjustmentListener. Oberflächenprogrammierung
321
Nitty Gritty • Take that!
T
Sandini Bib
JComponent
JScrollBar
JProgressBar
model: BoundedRangeModel value: int orientation: int unit/blockIncrement: int maximum/maximum: int visibleAmount: int <> +JScrollBar() +JScrollBar(orientierung: int) +JScrollBar(orientierung: int, wert:int, sichtbar: int, min: int, max: int)
model: BoundedRangeModel value: int orientation: int String/borderPainted: boolean maximum/maximum: int string: String <> +JProgressBar() +JProgressBar(modell: BoundedRangeModel) +JProgressBar(orientierung: int) +JProgressBar(orientierung: int, min: int, max: int) <<Methoden>> +getPercentComplete() +addChangeListener(l: ChangeListener)
<<Methoden>> +getValueIsAdjusting() +addAdjustmentListener( l: AdjustmentListener)
Nitty Gritty • Take that!
8
«interface» BoundedRangeModel value: int maximum/maximum: int extent: int valueIsAdjusting: boolean +setRangeProperties(wert: int, bereich: int, min: int, max: int, inEinstellung: boolean) +addChangeListener(l:ChangeListener)
DefaultBoundedRangeModel
JSlider model: BoundedRangeModel value: int orientation: int extent: int snapToTicks: boolean paintTicks/Labels/Track: boolean maximum/maximum: int minor/majorTickSpacing: int <> +JSlider() +JSlider(modell: BoundedRangeModel) +JSlider(orientierung: int) +JSlider(orientierung: int, min: int, max: int, wert: int) <<Methoden>> +createStandardLabels() +setLabelTable(beschriftung: Dictionary) +addChangeListener(l: ChangeListener)
Bild 8.74: Die Klassen JScrollBar, JSlider und JProgressBar
Die Klasse JProgressBar ist nicht durch den Endanwender einstellbar, sondern zeigt einen im Programm berechneten Fortschritt eines Vorgangs an. Im Balken können Sie auch einen Text anzeigen (mit setString() und setStringPainted()). Mit getPercentComplete() erhalten Sie den Wert value in Prozent ausgedrückt.
322
Swing
Sandini Bib
Die Klasse JSlider ist ein Schieberegler mit einer optionalen Skala. Die Skalenstriche nennen sich Ticks und existieren in zwei Längen: majorTicks und minorTicks. Die Beschriftung kann man standardmäßig aus dem Wertebereich erzeugen (createStandardTicks()) oder selbst als Collection von JLabels (siehe Abschnitt 8.6.6) angeben. Beispiel JScrollbar regler = new Scrollbar(JScrollBar.VERTICAL); regler.setValue(15); regler.setUnitIncrement(2); regler.setBlockIncrement(5); regler.setVisibleAmount(30); JProgressBar anzeige = new JProgressBar ( JProgressBar.HORIZONTAL, 0, 100); anzeige.setValue(35); float prozentFertig = anzeige.getPercentComplete(); JSlider slider = new JSlider(JSlider.HORIZONTAL); slider.setPaintLabels(true); slider.setPaintTicks(true); slider.setMajorTickSpacing(20); slider.setMinorTickSpacing(5);
8
seit
SE
ME
EE
1.2
x
-
x
Nitty Gritty • Take that!
8.6.10 Die Klasse JList
Bild 8.75: Darstellung einer JList
Die Klasse JList ist eine Liste, die die Selektion eines oder mehrerer Einträge erlaubt. Im Gegensatz zum AWT-Pendant List (siehe Abschnitt 8.2.8) werden nun die Objekte in einem Modell statt in der Oberfläche verwaltet und es sind nun Objekte jeglicher Art erlaubt, solange sie darstellbar sind (also gerendert werden können).
Oberflächenprogrammierung
323
Sandini Bib
JComponent
JList
Nitty Gritty • Take that!
8
model: ListModel selectionModel: ListSelectionModel selectionMode: int selectedIndex: int selectedIndices: int[] fixedCellHeight/Width: int visibleRowCount: int cellRenderer: ListCellRenderer <> +JList() +JList(modell: ListModel) +JList(daten: Vector/Object[]) <<Methoden>> +setSelectedValue(eintrag: Object, autoScroll: boolean) +getSelectedValue(s)() +setSelectionInterval(anfang: int, ende:int) +clearSelection() +isSelectionEmpty() +isSelectedIndex(index: int) +getFirst/LastVisibleIndex() +ensureIndexIsVisible(index: int) +setListData(daten: Vector/Object[])
«interface» ListCellRenderer
DefaultListCellRenderer
«interface» ListModel
+getElementAt(index: int) +getSize() +add/removeListDataListener( l: ListDataListener)
AbstractListModel {abstract}
DefaultListModel
+getElementAt(index: int) +getSize() +add/removeListDataListener( l: ListDataListener) +addElement(eintrag: Object) +add/insertElementAt( eintrag: Object, index: int) +remove[ElementAt](index: int) +removeElement(eintrag: Object) +removeAllElements/clear() +contains(eintrag: Object) +indexOf(eintrag: Object) +copyInto(einträge: Object[]) +toArray()
«interface» ListSelectionModel
DefaultListSelectionModel
Bild 8.76: Die Klasse JList und deren Modelle
Methoden und Attribute Die JList ist ein typisches Beispiel für das Model-View-ControllerPrinzip (siehe Abschnitt 8.6.1). Dabei stehen für die View (JList) mehrere Modelle zur Verfügung. Am wichtigsten ist das Datenmodell vom 324
Swing
Sandini Bib
Typ ListModel, das Auswahlmodell (ListSelectionModel) und die Klasse für die Darstellung der Einträge (ListCellRenderer). Die Klasse JList umfasst neben der Modellschnittstelle Methoden zur Darstellung wie für die Zellenhöhe und -breite sowie zur Steuerung der Sichtbarkeit (getFirst/LastVisibleIndex(), ensureIndexIsVisible()). Die Funktionalität zur Datenverwaltung der JList ist bis auf die Methode setListData() komplett ins ListModel ausgelagert. Diese Schnittstelle zwischen Model und View beinhaltet nur Methoden, die zur Kommunikation zwischen beiden notwendig sind: getElementAt() für die Abfrage eines (sichtbaren) Elements, getSize() für die Anzahl der Elemente und zwei Methoden zur Registrierung eines EventListeners, um Veränderungen der Daten sofort darstellen zu können. Die Implementierung ist Swing-typisch. Es gibt ein AbstractListModel, das nur die Ereignisbehandlung beinhaltet, aber die Speicherung und Manipulation der Daten offen lässt, und ein DefaultListModel, das eine einfache Implementierung mit einem Vector beinhaltet.
Für die Selektion gibt es ebenfalls ein Model-Interface (ListSelectionModel) und eine Default-Implementierung (DefaultListSelectionModel). Viele Auswahlmethoden stehen allerdings auch direkt in der View zur Verfügung, so dass Sie in der Regel das Modell nicht benötigen. Es gibt drei Selektionstypen, die im Attribut selectionMode festgelegt werden: ListSelectionModel.SINGLE_SELECTION (nur ein Eintrag), ListSelectionModel.MULTIPLE_INTERVAL_SELECTION (beliebig) oder ListSelectionModel.SINGLE_INTERVAL_SELECTION (ein zusammenhängender Bereich). Schließlich gibt es noch einen ListCellRenderer, der festlegt, wie die einzelnen Einträge gezeichnet werden (in den Modi: normal, selektiert, im Fokus). Der DefaultListCellRenderer unterstützt beispielsweise Icons und Objekte mit toString-Methode.
Oberflächenprogrammierung
325
8 Nitty Gritty • Take that!
Somit bleibt dem Entwickler die Wahl, ob er letzteres Modell verwenden kann oder ein eigenes von AbstractListModel ableiten möchte. Dies ist z. B. dann empfehlenswert, wenn die Datenmenge sehr groß gegenüber dem dargestellten Ausschnitt ist oder nur bei Bedarf aus Dateien, Datenbanken oder übers Netz geladen wird.
Sandini Bib
Wenn Sie selbst einen ListCellRenderer implementieren, müssen Sie in der Methode getListCellRendererComponent() ein Component-Objekt zurückgeben (z. B. ein JLabel wie im DefaultListCellRenderer). Der Renderer nutzt nur die paint-Methode der darstellenden Komponente, erzeugt jedoch kein voll funktionsfähiges Kontrollelement. Das bedeutet, Sie können zwar eine JComboBox in jeden Listeneintrag setzen, sie wird aber nicht aufklappen, wenn der Benutzer darauf klickt. Beispiel DefaultListModel daten = new DefaultListModel(); daten.addElement(Listeneintrag")); daten.addElement(new ImageIcon("db.gif")); // Symbol JList liste = new JList(daten); liste.setSelectionMode( ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); liste.setSelectedValue(daten.getElementAt(1), true); liste.ensureIndexIsVisible(1);
8 Nitty Gritty • Take that!
8.6.11 Die Klasse JComboBox seit
SE
ME
EE
1.2
x
-
x
Bild 8.77: Darstellung einer JComboBox
Die Klasse JComboBox ist eine Klappliste, die die Selektion genau eines Eintrags erlaubt. Im Gegensatz zum AWT-Pendant Choice (siehe Abschnitt 8.2.8) können in einer JComboBox auch neue Werte eingegeben werden. Die Objekte werden in einem Modell statt in der Oberfläche verwaltet und es sind nun Objekte jeglicher Art erlaubt, solange sie darstellbar sind (also gerendert werden können).
326
Swing
Sandini Bib
JComboBox model: ComboBoxModel editor: ComboBoxEditor selectedIndex: int selectedItem: Object editable/popupVisible: boolean cellRenderer: ListCellRenderer <> +JComboBox() +JComboBox(modell: ComboBoxModel) +JComboBox(daten: Vector/Object[]) <<Methoden>> +add/removeItem(eintrag: Object) +insert/removeItemAt(eintrag: Object, index: int) +getItemAt(index: int) +getItemCount() +show/hidePopup() +getMaximumRowCount() +addActionListener(l: ActionListener) +addItemListener(l: ItemListener)
AbstractListModel {abstract}
«interface» ListCellRenderer
DefaultListCellRenderer
«interface» ListModel
+getElementAt(index: int) +getSize() +add/removeListDataListener( l: ListDataListener)
«interface» ComboBoxModel
+getSelectedItem() +setSelectedItem(eintrag: Object)
«interface» MutableComboBoxModel
8
+add/removeElement(obj: Object) +add/removeElementAt( obj: Object, index: int)
Nitty Gritty • Take that!
JComponent
DefaultComboBoxModel
«interface» ComboBoxEditor
BasicComboBoxEditor
Bild 8.78: Die Klasse JComboBox und deren Modelle
Oberflächenprogrammierung
327
Sandini Bib
Methoden und Attribute Die JComboBox ist eng verwandt mit der JList (siehe Abschnitt 8.6.10) und teilt dasselbe Darstellungsmodell (ListCellRenderer). Das Datenmodell ComboBoxModel ist vom ListModel abgeleitet und erweitert es um Methoden zum Hinzufügen und Löschen von Einträgen. In einer JComboBox kann immer nur ein Eintrag ausgewählt sein, daher entfällt das Selektionsmodell. Die Klasse JComboBox umfasst neben der Modellschnittstelle die Methoden zur Manipulation und Selektion der Einträge und zur Steuerung der Klappliste (show/hidePopup(), get/setPopupVisible()). Das Datenmodell und die Popup-Methoden beziehen sich auf die Klappliste der JComboBox. Falls die ComboBox editierbar ist (setEditable(true)), gibt es daneben noch ein Eingabefeld. Dieses besitzt mit dem ComboBoxEditor und der Implementierung DefaultComboBoxEditor ein eigenes Modell. Beispiel
Nitty Gritty • Take that!
8
JComboBox liste = new JComboBox(); liste.addItem("Listeneintrag 1"); liste.addItem("Listeneintrag 2"); liste.addItem("Listeneintrag 3"); liste.setEditable(true); liste.addActionListener(ComboAddHandler); [...] /* Eventhandler für das Hinzufügen neuer Listeneinträge; wird bei Betätigung der Eingabetaste nach der Werteingabe in der JComboBox ausgelöst */ public class ComboAddHandler implements ActionListener { public void actionPerformed(ActionEvent e) { JComboBox cb = (JComboBox)e.getSource(); // Quelle Object neu = cb.getEditor().getItem(); // Eintrag // falls neuer Eintrag -> in Liste aufnehmen: if (cb.getSelectedIndex() == -1) cb.addItem(neu); } }
328
Swing
Sandini Bib
8.6.12 Die Klasse JTable seit
SE
ME
EE
1.2
x
-
x
Bild 8.79: Darstellung einer JTable
8 Nitty Gritty • Take that!
Die JTable stellt eine komplexe Tabelle zur Darstellung von Datensätzen dar. Jeder Spalte vom Typ TableColumn kann eine Renderer-Klasse zur Darstellung des Zelleninhalts und eine Editor-Klasse zugewiesen werden. So ist es zum Beispiel möglich, für bestimmte Werte Symbole für die Darstellung und für die Änderungen der Werte Klapplisten zu benutzen (siehe Abschnitt 8.6.11). Das Datenmodell für JTable muss das Interface TableModel implementieren, vorgegebene Implementierungen sind AbstractTableModel und DefaultTableModel.
Oberflächenprogrammierung
329
Sandini Bib
«interface» TableModel
JComponent
JTable
Nitty Gritty • Take that!
8
model: TableModel columnModel: TableColumnModel selectionModel: ListSelectionModel autoCreateColumnsFromModel: boolean autoResizeMode: int rowHeight: int intercellSpacing: int cell/defaultEditor: TableCellEditor defaultRenderer: TableCellRenderer <> +JTable() +JTable(modell: TableModel) +JTable(modell: TableModel, spalten: ColumnModel) +JTable(zeilen: int, spalten: int) +JTable(daten: Object[][], spaltennamen: Object[]) +JTable(daten: Vector, spaltennamen: Vector) <<Methoden>> +getValueAt(zeile: int, spalte: int) +isCellEditable(zeile: int, spalte: int) +editCellAt(zeile: int, spalte: int) +sizeColumnsToFit() +getSelectedColumn(s)/Row(s)() +selectAll/clearSelection() +getColumn/RowCount() +getColumnClass/Name(spalte: int) +addColumn(spalte: TableColumn) +getColumn(name: Object) +showHorizontal/VerticalLines()
+getValueAt(zeile: int, spalte: int) +setValueAt(wert: Object, ...) +getColumn/RowCount() +getColumnName/Class(spalte: int) +isCellEditable(zeile: int, spalte: int) +add/removeTableModelListener( l:TableModelListener)
AbstractTableModel {abstract}
DefaultTableModel
<> +DefaultTableModel() +DefaultTableModel(daten: Object[][], spaltennamen: Object[]) +DefaultTableModel(spaltennamen: Vector/Object[], zeilen: int) +DefaultTableModel(daten: Vector, spaltennamen: Vector) <<Methoden>> +addRow(daten: Vector/Object[]) +insertRow(zeile: int daten: Vector/Object[]) +removeRow(zeile: int) +addColumn(name: Object) ...
«interface» TableCellRenderer
«interface» TableCellEditor
DefaultTableCellRenderer
DefaultCellEditor
Bild 8.80: Die Klasse JTable und deren Modelle
330
Swing
Sandini Bib
Methoden und Attribute Eine Tabelle kann auf zwei Weisen aufgebaut werden: 1. Sie erzeugen die JTable und fügen einzelne Spalten vom Typ TableColumn hinzu. Danach müssen Sie ein dazu passendes Datenmodell zuweisen. 2. Sie wählen autoCreateColumnsFromModel und definieren die Spaltenüberschriften im Datenmodell.
Somit bleibt dem Entwickler die Wahl, ob er letzteres Modell verwenden kann oder ein eigenes von AbstractTableModel ableiten möchte. Dies ist z. B. dann empfehlenswert, wenn die Datenmenge sehr groß gegenüber dem dargestellten Ausschnitt ist oder nur bei Bedarf aus Dateien, Datenbanken oder übers Netz geladen wird. Der autoResizeMode der JTable bestimmt, wie die verfügbare Breite auf die Spalten verteilt wird. Die Breitenanpassung wird jedoch nicht automatisch durchgeführt, sondern muss nach jeder Änderung der dargestellten Daten mittels sizeColumnsToFit() angestoßen werden. Renderer (siehe auch Abschnitt 8.6.10) und Editoren werden in der Regel auf Spaltenbasis definiert. Die Tabelle in Abbildung 8.79 zeigt in der letzten Spalte einen Renderer für Bilder sowie einen Editor mit eine JComboBox. Eine Zelle ist editierbar, wenn die Methode isCellEditable() aus dem TableModel true zurückliefert.
Oberflächenprogrammierung
331
8 Nitty Gritty • Take that!
Die Tabelle fragt nur diejenigen Werte (mittels getValueAt()) aus dem Modell ab, die momentan sichtbar sind. Die Funktionalität zur Datenverwaltung der JTable ist komplett ins TableModel ausgelagert. Diese Schnittstelle zwischen Model und View beinhaltet nur Methoden, die zur Kommunikation zwischen beiden notwendig sind: getValueAt() für die Abfrage eines Elements, getRow/ColumnCount() für die Anzahl der Zeilen bzw. Spalten sowie Methoden zur Registrierung eines EventListeners, damit Veränderungen der Daten sofort darstellen werden können. Die Implementierung ist Swing-typisch. Es gibt ein AbstractTableModel, das alle Methoden bis auf die drei gerade erwähnten enthält, aber die Speicherung und Manipulation der Daten offen lässt, und ein DefaultTableModel, das eine einfache Implementierung auf Basis von Vector-Objekten beinhaltet.
Sandini Bib
Beispiel
8
public class KontoDaten extends AbstractTableModel { private Vector daten; private String[] spalten = {"Nr", "Stand", "Inhaber"}; public TableData(Vector initDaten) { daten = initDaten; } public int getColumnCount() { return spalten.length; } public String[] getColumnName() { return spalten; } public String getColumnName(int index) { return spalten[i]; } public int getRowCount() { return daten.size(); } public Object getValueAt(int zeile, int spalte) { return ((Object[])daten.elementAt(zeile))[spalte];} } [...] JTable tabelle = new JTree(); tabelle.setAutoCreateColumnsFromModel(true); tabelle.setModel(new KontoDaten(kontoVector));
Nitty Gritty • Take that!
8.6.13 Die Klasse JTtree seit
SE
ME
EE
1.2
x
-
x
Bild 8.81: Darstellung von JTree in verschiedenem Look & Feel
Die Baumdarstellung basiert auf einem hierarchischen Datenmodell. Die Modellklasse für JTree muss das Interface TreeModel implementieren; in Swing enthalten ist die Implementierungsklasse DefaultTreeModel. Die einzelnen Knoten müssen die Interfaces TreeNode oder MutableTreeNode implementieren, die Klasse DefaultMutable332
Swing
Sandini Bib
TreeNode enthält eine universelle Implementierung inklusive Navigationsmethoden für Baumstrukturen. «interface» TreeModel
JComponent
model: TreeModel selectionModel: TreeSelectionModel editable: boolean rootVisible: boolean rowHeight: int cellEditor: TreeCellEditor cellRenderer: TreeCellRenderer <> +JTree() +JTree(modell: TreeModel) +JTree(daten: Vector/Object[]) +JTree(wurzel: TreeNode) <<Methoden>> +expand/collapsePath(pfad: TreePath) +expand/collapseRow(zeile: int) +isExpanded(pfad: TreePath) +isCollapsed(pfad: TreePath) +getRowCount() +getRowForPath(TreePath) +getEditingPath() +getSelectionRows/Paths() +getColumn/RowCount() +isPathSelected(pfad: TreePath) +isRowSelected(zeile: int) +addSelectionPath(pfad: TreePath) +clearSelection() +is/makeVisible(pfad: TreePath) ...
+getRoot() +getChild(vater: Object, index: int) +getChildCount(vater: Object) +isLeaf(knoten: Object) +getIndexOfChild(vater, kind: Object) +add/removeTreeModelListener( l:TreeModelListener)
DefaultTreeModel root: Object <> +DefaultTreeModel(wurzel: TreeNode) <<Methoden>> +insertNodeInto(kind, vater: MutableTreeNode, index: int) +reload() +removeNodeFromParent( knoten: MutableTreeNode) +getPathToRoot( knoten: MutableTreeNode) ...
8 Nitty Gritty • Take that!
JTree
«interface» TreeNode
«interface» MutableTreeNode
DefaultMutableTreeNode
«interface» TreeCellRenderer
DefaultTreeCellRenderer
«interface» TreeCellEditor
DefaultTreeCellEditor
Bild 8.82: Die Klasse JTree und deren Modelle Oberflächenprogrammierung
333
Sandini Bib
Methoden und Attribute Die Klasse JTree stellt eine Baumstruktur dar. Die Navigation im Baum erfolgt entweder über den Index im Kontrollelement (Zeile) oder den Pfad von Vater- zu Kindknoten. Es gibt Methoden zur Selektion und zum Auf- und Zuklappen der einzelnen Unterbäume. Das Modell implementiert das TreeModel-Interface, das eine Struktur aus Knoten und Blättern beschreibt. Diese können Sie mit einem Objekt vom Typ DefaultTreeModel erzeugen. root stellt die Wurzel dar, mit insertNodeInto() lassen sich weitere Knoten einfügen. Während das TreeModel noch beliebige Objekte als Knoten zulässt, ist das DefaultTreeModel auf den Typ MutableTreeNode zugeschnitten. Dieser stellt einen Knoten dar und enthält Methoden zur Abfrage und Manipulation der enthaltenen Daten (userObject) sowie aller benachbarten Knoten.
Nitty Gritty • Take that!
8
Die Renderer (siehe auch Abschnitt 8.6.10) für die Knoten sind veränderbar. Standardmäßig wird bei inneren Knoten ein Ordnersymbol angezeigt und bei Blättern ein Punkt bzw. ein Blatt Papier (siehe Abbildung 8.81). Falls Sie Knoten mit setEditable(true) veränderbar machen, können Sie einen TreeCellEditor nutzen. Beispiel public class TreeData extends DefaultTreeModel { public TreeData() { super(new DefaultMutableTreeNode("Mitglieder")) DefaultMutableTreeNode gruppe1 = new DefaultMutableTreeNode("Firmen"); insertNodeInto(gruppe1, (DefaultMutableTreeNode)getRoot(), 0); insertNodeInto(new DefaultMutableTreeNode( "Inprise"), gruppe1, 0); [...] } } [...] JTree baumansicht = new JTree(); baumansicht.setModel(new TreeData());
334
Swing
Sandini Bib
8.6.14 Die Klassen JFrame und JDialog seit
SE
ME
EE
1.2
x
-
x
Bild 8.83: Darstellung von JFrame und JDialog
Ein JFrame-Objekt besitzt ein Symbol, das erscheint, wenn das Fenster minimiert wird. Ein JDialog-Objekt kann modal in Bezug auf sein Hauptfenster sein, das bedeutet, es steht immer vor diesem Fenster, in das man erst zurückkehren kann, wenn man die Dialogbox geschlossen hat.
Oberflächenprogrammierung
335
8 Nitty Gritty • Take that!
Die Swing-Klasse für Hauptfenster (JFrame) und Dialogboxen (JDialog) sind direkt von ihren AWT-Entsprechungen Frame und Dialog abgeleitet (siehe Abschnitt 8.2.10) und erben daher all ihre Methoden. Der Hauptunterschied ist, dass nun keine Kontrollelemente mehr direkt auf das Fenster platziert werden, sondern auf einen Client-Container mit dem Namen contentPane. Nun können beide Klassen Menüleisten aufnehmen (Typ JMenuBar) und besitzen Vorgabewerte für das Verhalten beim Schließen des Fensters.
Sandini Bib
Container
Window
Nitty Gritty • Take that!
8
Frame
Dialog
JFrame
JDialog
contentPane: Container defaultCloseOperation: int <> +JFrame() +JFrame(titel: String) <<Methoden>> +setJMenuBar(menüleiste: JMenuBar) +getJMenuBar() ...
contentPane: Container defaultCloseOperation: int <> +JDialog(parent: Frame/Dialog) +JDialog(parent: Frame/Dialog, titel: String) +JDialog(parent: Frame/Dialog, titel: String, modal: boolean) <<Methoden>> +setJMenuBar(menüleiste: JMenuBar) +getJMenuBar() ...
Bild 8.84: Die Klassen JFrame und JDialog
Methoden und Attribute Die wichtigste Methode ist die von Component geerbte Methode setVisible(true) (oder show()), da Fenster standardmäßig nicht sichtbar sind. dispose() macht nicht nur das Fenster unsichtbar, sondern gibt auch alle verbundenen Ressourcen frei. Dies ist wichtig, da der Lebenszyklus eines Fensters vom Betriebssystem gesteuert wird, das keine automatische Garbage Collection kennt. Häufig benutzt werden auch die WindowEvents und deren Methoden windowOpened() und windowClosing(), bei denen eine Initialisierung beziehungsweise eine Bereinigung durchgeführt werden kann. Als Defaultwerte für setDefaultCloseOperation() sind zulässig: WindowConstants.DISPOSE_ON_CLOSE (Ressourcen freigeben), WindowConstants.HIDE_ON_CLOSE (unsichtbar machen) und WindowConstants.DO_NOTHING_ON_CLOSE (windowOpened() abarbeiten). 336
Swing
Sandini Bib
Im Gegensatz zu AWT darf die Methode add() nicht mehr zum Hinzufügen von Kontrollelementen benutzt werden. Stattdessen muss es heißen getContentPane.add(). Daneben stehen natürlich auch alle allgemeinen Methoden zur Verfügung, die in den Superklasse Frame und Dialog (siehe Abschnitt 8.2.10), Component (siehe Abschnitt 8.2.1) und Container (siehe Abschnitt 8.2.9) definiert sind. Bei Container-Objekten wird häufig Gebrauch von LayoutManagern gemacht (Abschnitt 8.3). JFrame Ein JFrame ist ein vollwertiges Fenster, wie es meist für Applikationen verwendet wird. Es hat einen Rahmen mit Titelleiste (Attribut title) und kann eine MenuBar (Attribut JMenuBar, Unterschied zu AWT!) besitzen. resizable gibt an, ob der Benutzer die Größe des Fensters verändern kann und iconImage dient zur Festlegung eines Symbols, falls das Fenster den Zustand (state) Frame.ICONIFIED annimmt.
Eine etwas eingeschränkte Variante eines Fensters ist JDialog. Jedes Dialog-Objekt hat einen eindeutigen Eigner (owner), der in der Regel das erzeugende (J)Frame- oder (J)Dialog-Objekt ist. Im Gegensatz zu JFrame gibt es kein Icon, im Gegensatz zu AWT kann aber nun auch eine JMenuBar zugewiesen werden. Ein zusätzliches Feature des Dialogs ist modal, womit angegeben wird, ob das Dialogfenster erst geschlossen werden muss, bevor der Benutzer wieder Zugriff auf das Parent-Window bekommt, oder ob beide Fenster gleichzeitig benutzt werden können. Eine Systemmodalität, wie sie unter Windows existiert, gibt es in Java nicht. Es kann also kein Java-Fenster geben, das sämtliche anderen Fenster auf dem Bildschirm blockiert.
Oberflächenprogrammierung
337
Nitty Gritty • Take that!
8
JDialog
Sandini Bib
Beispiel import javax.swing.*; public class SwingWinDemo extends JFrame { public static void main(String[] args) { SwingWinDemo win = new SwingWinDemo(); win.setTitle("Identifizierung"); // gleichmäßige Verteilung der Kontrollelemente: win.getContentPane().setLayout(new FlowLayout()); win.getContentPane().add(new Label("ID: ")); win.getContentPane().add(new TextField(6)); win.setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE); // Fenster positionieren und anzeigen: win.setBounds(100, 100, 200, 80); win.setVisible(true); } }
Nitty Gritty • Take that!
8
8.6.15 Die Klasse JPanel seit
SE
ME
EE
1.2
x
-
x
Bild 8.85: Darstellung eines JPanels
Ein JPanel ist ein Fensterbereich, in dem andere Kontrollelemente platziert werden können (Container). Hauptaufgabe ist die Gestaltung und Gliederung von Benutzeroberflächen in Window-Systemen. Das AWT-Pendant heißt Panel (siehe Abschnitt 8.2.12).
338
Swing
Sandini Bib
JComponent
JPanel
+JPanel() +JPanel(layout : LayoutManager) +JPanel(speicherabbild: boolean) +JPanel(layout : LayoutManager, speicherabbild: boolean)
Bild 8.86: Die Klasse JPanel
Über das geerbte Attribut layout kann ein LayoutManager zugewiesen werden. Häufig verschachtelt man mehrere Panel mit unterschiedlichen LayoutManagern ineinander (siehe Abschnitt 8.3).
8.6.16 Weitere Swing-Container Neben dem JPanel (Abschnitt 8.6.15), JFrame und JDialog (Abschnitt 8.6.14) sind noch etliche Layout-Elemente hinzugekommen, die andere Container oder Kontrollelemente beinhalten können, zum Beispiel JSplitPane, JScrollPane, JTabbedPane, JOptionPane (Abschnitt 8.6.18 ). Bei den Fenstern sind noch JDesktopPane und JInternalFrame zur Implementierung von MDI-Anwendungen hinzugekommen (Abschnitt 8.6.22). MDI (Multi Document Interface) ist eine unter Windows weit verbreitete Anwendungsart, bei der ein Hauptfenster mehrere daran gebundene Dokumentenfenster steuert. Beispiel sind Microsoft Word oder Corel Draw. Oberflächenprogrammierung
339
8 Nitty Gritty • Take that!
Der Parameter speicherabbild (isDoubleBuffered) zeigt an, ob für die die Komponente Double-Buffering verwendet werden soll. Dies bedeutet, dass das JPanel im Speicher gezeichnet werden soll und bei der Ausgabe nur noch der Speicherbereich in die Bildschirmausgabe kopiert werden muss. Dies kostet zwar mehr Speicher, sorgt aber für eine flackerfreie Darstellung.
Sandini Bib
8.6.17 Swing-Menüklassen seit
SE
ME
EE
1.2
x
-
x
Menüs dienen zur Auswahl von Funktionen und Optionen in Anwendungen. Menüs können entweder in Form von JMenuBars an der Oberseite von Fenstern oder als PopUp-Menüs an einer beliebigen Stelle auftreten. Oft sind Menüs geschachtelt (vergleiche AWT-Menüs in Abschnitt 8.2.13). JMenu JMenuItem
JMenuBar JToolBar
JCheckboxMenuItem
Nitty Gritty • Take that!
8
Bild 8.87: Darstellung verschiedener Menü-Objekte
Ein Menü ist entweder in eine Menüleiste eines JFrame-Objekts eingebettet (Klasse JMenuBar) oder erscheint auf Abruf als Kontextmenü (Klasse JPopupMenu). Darin sind entweder Untermenüs (Klasse JMenu) oder Menüeinträge (Klasse JMenuItem) enthalten. Abbildung 8.88 zeigt die Vererbungshierarchie in Java. Die Swing-Varianten der Menüklassen unterstützen neben den Tastaturkürzeln (nun Accelerator statt Shortcut genannt) auch so genannte Mnemonics. Dies sind ebenfalls Tastaturkombinationen, die aus einer Meta-Taste (unter Windows (ALT)) und einem Buchstaben gebildet und im Menü durch eine Unterstreichung symbolisiert werden. Außerdem ist die Einbindung von Grafiken in Menüs erlaubt (siehe Abbildung 8.87).
340
Swing
Sandini Bib
JComponent
JMenuItem accelerator: KeyStroke <> +JMenuItem() +JMenuItem(aufschrift: String) +JMenuItem(symbol: Icon) +JMenuItem(aufschrift: String, symbol: MenuShortcut) +JMenuItem(aufschrift: String, kürzel: int) <<Methoden>> +addMenuKeyListener( l: MenuKeyListener)
JMenu selected: boolean delay: int <> +JMenu() +JMenu(aufschrift: String) +JMenu(aufschrift: String, tearOff: boolean) <<Methoden>> +add(eintrag: JMenuItem/String) +insert(eintrag: JMenuItem/String, index: int) +remove(eintrag: JMenuItem/int) +removeAll() +addSeparator() +getItem/MenuComponent(index: int) +getMenuComponents/subElements() +getItem/MenuComponentCount() +doClick(dauer: int) +isTearOff() +isTopLevelMenu() +remove(eintrag: MenuComponent/int)
JMenuBar helpMenu: JMenu <> +JMenuBar() <<Methoden>> +add(eintrag: JMenu) +getMenu/ComponentAtIndex(i: int) +getComponentIndex(c: Component) +getMenuCount()
JPopupMenu
<> +JPopupMenu() +JPopupMenu(aufschrift: String) <<Methoden>> +add(eintrag: JMenuItem/String) +insert(eintrag: Component index: int) +remove(eintrag: Component) +addSeparator() +show(auslöser: Component, x: int, y: int) +getSubElements()) +getInvoker() +isVisible()
8 Nitty Gritty • Take that!
AbstractButton
JCheckBoxMenuItem
JRadioButtonMenuItem
Bild 8.88: Die Swing-Menüklassen
Oberflächenprogrammierung
341
Sandini Bib
Methoden und Attribute Alle Swing-Menüeinträge sind von AbstractButton abgeleitet (siehe Abschnitt 8.6.4) und besitzen damit einen text, ein optionales Symbol und generieren beim Anklicken einen ActionEvent. Für Menüs können mit setMnemonic() Tastaturkürzel angegeben werden. Zusätzlich können Sie in JMenuItem-Objekten einen accelerator setzen, also eine Tastenkombination, die den Aufruf eines Menüpunktes erlaubt, ohne dass der Anwender durch die Menüs navigieren muss. Als Varianten eines JMenuItems gibt es JCheckBoxMenuItem und JRadioButtonMenuItem für selektierbare Optionen. Alle JMenuItem-Objekte werden mittels add-Methode in ein JMenu-Objekt eingefügt. In diesen Menüs und Submenüs können Sie mittels Trennlinien (addSeparator()) eine Gliederung vornehmen. JMenus können Sie mittels add() in eine JMenuBar oder ein JPopupMenu aufnehmen. Ein JMenuBar-Objekt müssen Sie mittels setJMenuBar() in einen JFrame (oder JDialog) aufnehmen (siehe Abschnitt 8.6.14). JFrame hat zwar von Frame die Methode setMenuBar() geerbt, diese unterstützt jedoch nur AWT-Menüs!
8 Nitty Gritty • Take that!
Beispiel JMenuBar menueLeiste = new JMenuBar(); JMenu dateiMenue = new JMenu("Datei"); dateiMenue.setMnemonic('D'); // Alt+D öffnet Dateimenü menueLeiste.add(dateiMenue); win.setJMenuBar(menueLeiste); JMenuItem neu = new JMenuItem("Neu", new ImageIcon("newfile.gif")); neu.setMnemonic('N'); // Alt+N selektiert "Neu" // Ctrl+N aktiviert "Neu": neu.setAccelerator(KeyStroke.getKeyStroke( KeyEvent.VK_N, Event.CTRL_MASK)); neu.addActionListener(eventHandler); dateiMenue.add(neu); JMenuItem oeffnen = new JMenuItem("Öffnen", new ImageIcon("openfile.gif")); oeffnen.setMnemonic('f'); oeffnen.setAccelerator(KeyStroke.getKeyStroke(
342
Swing
Sandini Bib KeyEvent.VK_O, Event.CTRL_MASK)); oeffnen.addActionListener(eventHandler); dateiMenue.add(oeffnen);
Toolbars Mit der Klasse JToolBar (siehe Abbildung 8.89) können Sie Werkzeugleisten erstellen, die Buttons enthalten. Man setzt sie in der Regel zusammen mit dem LayoutManager BorderLayout ein (siehe Abschnitt 8.3.1), mit dem sie oben und am linken Rand eines Anwendungsfenster angebracht werden können. Durch das Setzen des Properties floatable können Sie auch schwebende Toolbars erzeugen, die nicht wie Menüleisten an einem Fenster befestigt sind, sondern ähnlich einem Popup-Menü in einem eigenen kleinen Fenster untergebracht sind. JComponent
8
JToolBar
Nitty Gritty • Take that!
orientation: int floatable: boolean <> +JToolBar() +JToolBar(orientierung: int) <<Methoden>> +add(eintrag: Component) +add(eintrag: Component, index: int) +addSeparator() +remove(eintrag: Component) +getComponentAtIndex(index: int) +getComponentIndex(C: component) +getComponentCount()
Bild 8.89: Die Klasse JToolBar
8.6.18 Die Klasse JSplitPane seit
SE
ME
EE
1.2
x
-
x
Oberflächenprogrammierung
343
Sandini Bib
Bild 8.90: JSplitPane
Dieses Kontrollelement teilt eine Fläche in zwei Teile, die durch einen horizontalen oder vertikalen Trenner separiert werden. Mit diesem Divider kann der Benutzer die verfügbare Fläche zwischen den beiden Zonen aufteilen. Beispiel splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); splitPane.add(linkeTextArea, "left"); splitPane.add(rechteTextArea, "right");
8.6.19 Die Klasse JScrollPane
Nitty Gritty • Take that!
8
seit
SE
ME
EE
1.2
x
-
x
Bild 8.91: JTable in JScrollPane
Der Container JScrollPane ermöglicht die Anzeige eines Ausschnitts einer Komponente. Zum Beispiel kann der Text in einem JTextArea-Objekt über den Rand hinausragen (siehe Abschnitt 8.6.8). Das JScrollPane-Objekt ermöglicht es dann mit Hilfe nach Bedarf eingeblendeter Scrollbars, den angezeigten Ausschnitt über das gesamte Bild zu bewegen. Der sichtbare Ausschnitt wird Viewport genannt und durch die Methoden setViewport() und setViewportView() eingestellt. Ob die Scrollbars immer, bei Bedarf oder nie angezeigt werden, kann durch Policies angegeben werden. 344
Swing
Sandini Bib
Durch Einfügen einer JTextArea (Abschnitt 8.6.8) in eine JScrollPane kann ein Verhalten ähnlich der AWT-TextArea (Abschnitt 8.2.6) erreicht werden. Beispiel JScrollPane scroller = new JScrollPane(); // Scrollfläche JTextArea editor = new JTextArea(); scroller.getViewport().add(editor); // für Scrolling
8.6.20 Die Klasse JTabbedPane seit
SE
ME
EE
1.2
x
-
x
Bild 8.92: JTabbedPane
Ein JTabbedPane-Objekt erlaubt die Darstellung mehrerer Container, die über Karteireiter (Tabs) zugänglich sind. Die augenblicklich angewählte Seitenzahl steht im Property selectedIndex, der auf dieser Seite befindliche Container in selectedComponent. Beispiel JTabbedPane notizbuch = new JTabbedPane(); notizbuch.insertTab("Eingabe", null, seite1, null, 0); notizbuch.insertTab("Überblick", null, seite2, null, 1); notizbuch.insertTab("Mitglieder", null, seite3, null, 2); notizbuch.setSelectedIndex(1);
8.6.21 Die Klasse JOptionPane seit
SE
ME
EE
1.2
x
-
x
Oberflächenprogrammierung
345
Nitty Gritty • Take that!
8
Sandini Bib
Bild 8.93: JOptionPane
Dieser Container wird in der Regel nicht zur Aufnahme von Komponenten benutzt, sondern bietet mit verschiedenen show-Methoden die Möglichkeit an, ein Meldefenster zu öffnen. Dabei werden je nach Art des Dialogs neben der message verschiedene Symbole, Knöpfe oder ein Eingabefeld angezeigt. Das Attribut optionType kann die Werte T DEFAULT_OPTION, T YES_NO_OPTION, T YES_NO_CANCEL_OPTION
8
annehmen. Sie können jedoch über setOptions() auch einen eigenen Satz an Optionen bereitstellen.
Nitty Gritty • Take that!
T OK_CANCEL_OPTION
Der messageType kennt folgende Standardtypen, die durch verschiedene Symbole (Icons) gekennzeichnet sind. T ERROR_MESSAGE T INFORMATION_MESSAGE T WARNING_MESSAGE T QUESTION_MESSAGE T PLAIN_MESSAGE (ohne Symbol) Icon Error Look & Feel
Information Question Warning
Metal
Motif Windows
Bild 8.94: Symbole je nach Look & Feel 346
Swing
Sandini Bib
Beispiel JOptionPane.showConfirmDialog(win, "Wollen Sie die Datei löschen?", "Warnung", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
8.6.22 Die Klassen JDesktopPane und JInternalFrame SE
ME
EE
1.2
x
-
x
8
Bild 8.95: JDesktopPane und JInternalFrame
Im Windows-Umfeld gibt es viele MDI-Anwendungen. Das programmzentrierte Multiple Document Interface (MDI) sieht vor, dass jede Datei bzw. jedes Dokument in einem eigenen Fenster innerhalb des Programmhauptfensters dargestellt wird. Die Menüs befinden sich beim Hauptfenster, die inneren Fenster können dieses nicht verlassen, jedoch darin minimiert und maximiert werden. Beispiele für solche Anwendungen sind Microsoft Word oder CorelDRAW. Mittels der Swing-Klasse JDesktopPane kann ein JFrame-Objekt für die Verwaltung von MDI-Clients erweitert werden. Die Clients müssen von der Klasse JInternalFrame abgeleitet werden, deren Funktionalität weitgehend der von JFrame entspricht. Beispiel JDesktopPane mdiParent = new JDesktopPane(); win.getContentPane().add(mdiParent, "Center"); // MDIClient sei eine Subklasse von JInternalFrame: JInternalFrame mdiClient = new MDIClient(); mdiParent.add(mdiClient);
Oberflächenprogrammierung
347
Nitty Gritty • Take that!
seit
Sandini Bib
8.6.23 Chooser-Klassen seit
SE
ME
EE
1.2
x
-
x
Analog zum FileDialog unter AWT (siehe Abschnitt 8.2.11) gibt es auch unter Swing zwei Dialoge, die zur Auswahl von Dateien (JFileChooser) bzw. Farben (JColorChooser) durch den Anwender gedacht sind.
Bild 8.96: Die Klasse JFileChooser
Nitty Gritty • Take that!
8
Bild 8.97: Die Klasse JColorChooser
348
Swing
9 9.1
Sandini Bib
Applets – Java im Browser
Applets – java.applet
Das Package java.applet ist ein kleines, aber wichtiges Package für Clients. Es enthält die Klasse Applet, die als einzige Klasse in Browsern dargestellt werden kann. java.applet Interfaces - AppletContext - AppletStub - AudioClip
Klasse - Applet
Bild 9.1: Das Package java.applet
Grundlagen
Alle Applets sind von der Superklasse Applet oder von einer Subklasse davon abgeleitet. Eine Subklasse von Applet ist javax.swing. JApplet, die wiederum als Basisklasse für eigene Applets dient, die Swing nutzen. Die Klasse Applet umfasst etliche Methoden, die der Programmierer verwenden oder überschreiben kann. Da die Klasse Applet nichts Sichtbares tut, müssen Sie davon eine eigene Subklasse ableiten, die dann das gewünschte Verhalten zeigt, zum Beispiel grafische Ausgaben macht. Die Deklaration einer neuen Klasse, die als Applet dienen soll, lautet zum Beispiel: public class DatumApplet extends java.applet.Applet
Ablauf Bei einem Applet wird nicht die Klasse als Parameter einem Java-Interpreter übergeben, sondern es wird in eine HTML-Datei eingebettet, die in einen Java-fähigen Webbrowser geladen wird. Dieser übernimmt dann das Laden und die Kontrolle über das Applet. Dabei teilt der Browser dem Applet Benutzeraktion ebenso mit wie das Sichtbarwerden oder Maximieren des Applets oder des Browsers. Applets – Java im Browser
349
9 Nitty Gritty • Take that!
9.2
Sandini Bib
Applikationen und Applets Sie können auch Anwendungen schreiben, die sowohl als Applikationen als auch als Applets dienen. Diese müssen ebenfalls von der Klasse Applet abgeleitet sein und enthalten (für den Einsatz als Applikation) zusätzlich eine Methode main(). 9.2.1
Die Klasse Applet
seit
SE
ME
EE
1.0
x
-
x
Die Programmierung von Applets besteht aus dem Hinzufügen neuer Methoden zur abgeleiteten Applet-Klasse und dem Überschreiben der geerbten Methoden, die zur Laufzeit vom Browser in bestimmten Situation automatisch aufgerufen werden. Panel
Nitty Gritty • Take that!
9
Applet
+init() +start() +paint(g: Graphics) {geerbt} +update(g: Graphics) {geerbt} {geerbt} +repaint() +stop() +destroy() +getParameter(name: String) +getParameterInfo/AppletInfo() +getAppletContext() +getCodeBase/DocumentBase() +getImage(quelle: URL) +getAudioClip/play(quelle: URL) +showStatus(meldung: String)
Bild 9.2: Wichtige Methoden der Klasse Applet
paint-Methode Die wichtigste Methode für die grafische Ausgabe in einem Webbrowser ist paint(), mit der die Fläche gezeichnet wird, die das Applet auf der Webseite einnimmt. Auf dieser Fläche kann mit der Grafikprimitive gezeichnet (siehe 8.5.1 »Klasse Graphics«), eine Sammlung 350
Grundlagen
Sandini Bib
von Kontrollelementen als Formular angezeigt, ein Bild dargestellt werden (siehe 8.5.8 »Image-Klassen«) und so weiter. Die Methode sieht folgendermaßen aus: public void paint (Graphics g)
Die Methode hat ein Graphics-Objekt (siehe 8.5.1) als Parameter und wird nicht vom Entwickler im Code, sondern vom Browser aufgerufen. Allein mit dem Überschreiben dieser Methode können Sie bereits ein Applet erzeugen, das zum Beispiel einen Text ausgibt oder eine einfache Grafik darstellt. Es gibt aber noch weitere Methoden, die bestimmten Aufgaben dienen und zu ganz bestimmten Zeitpunkten vom Browser automatisch aufgerufen werden. Diese Methoden können Sie überschreiben. Es lohnt sich zu wissen, wie der Ablauf eines Applets in einem Webbrowser aussieht.
öffnet Webseite
Browser <>
:Applet
9
init()
paint() verlässt Webseite
stop()
schließt Browser
destroy()
Bild 9.3: Lebenszyklus eines Applets
1. Laden Wenn der Benutzer eine HTML-Seite im Browser lädt, die ein JavaApplet enthält, wird zunächst die Klasse geladen, die im Tag angegeben ist. Diese Klasse muss eine Subklasse von Applet sein. 2. init() Beim ersten Laden des Applets wird vom Browser die Methode init() aufgerufen. Die Methode kann vom Programmierer überschrieben werden, um Initialisierungen vorzunehmen. Applets – Java im Browser
351
Nitty Gritty • Take that!
start()
Sandini Bib
3. start() Danach kommt die Methode start() an die Reihe. Diese Methode wird jedes Mal erneut aufgerufen, wenn der Benutzer das Applet wieder sichtbar macht, zum Beispiel wenn zuvor durch Scrollen der Webseite das Applet nicht mehr sichtbar war oder die Webseite verlassen wurde.
Nitty Gritty • Take that!
9
4. paint() Die paint()-Methode wird vom Browser automatisch aufgerufen, um das Applet zu zeichnen. Diese Methode wird praktisch immer vom Entwickler überschrieben. Sie wird nicht direkt aufgerufen. Falls sich aus der Anwendung selbst die Notwendigkeit des Neuzeichnens ergibt, sollten Sie stattdessen repaint() benutzen. 5. update() Falls es Änderungen gibt, die ein teilweises oder vollständiges Neuzeichnen erfordern, ruft der Browser die Methode update() auf. Standardmäßig löscht sie die Ausgabefläche (das Panel) und ruft paint() auf (siehe Abbildung 9.4). 6. repaint() Will der Programmierer ein Neuzeichnen erreichen, ruft er weder paint() noch update() direkt auf, sondern die Methode repaint(), die den Browser anweist, sobald wie möglich ein Update einzuleiten. 7. stop() Wird das Applet unsichtbar, wird (als Gegenstück zu start) die Methode stop() ausgeführt. 8. destroy() destroy() schließlich ist das Gegenstück zur init()-Methode bei
der Löschung der Klasse oder beim Schließen des Browsers.
352
Grundlagen
Sandini Bib
Browser Neuzeichnen durch Browser:
:Applet
update()
Neuzeichnen durch Applet:
paint()
repaint() update() paint()
Bild 9.4: Neuzeichnen eines Applets
Die meisten dieser Methoden werden nur selten überschrieben, einzig die paint()-Methode wird häufig vom Programmierer angepasst. Da das Applet von Panel – also einem AWT-Container – abgeleitet wird, können Sie aber ebenso beliebige Kontrollelemente hinzufügen.
Mit getParameter() fragen Sie den Wert eines in der HTML-Seite benannten Parameters ab. getCodeBase() liefert die URL des Applets und getDocumentBase() diejenige des HTML-Dokuments, in das das Applet eingebunden ist. showStatus() gibt aus der Statuszeile des Browsers eine Meldung aus; mit getImage() bzw. getAudioClip() können Sie Ressourcen vom Server laden. getAppletInfo() können Sie überschreiben, so dass in der Java-Konsole des Browsers eine Information über das Applet ausgegeben werden kann. Beispiel import java.awt.*; import java.awt.event.*; public class LogonApplet extends java.applet.Applet implements ActionListener { private Button logonButton; private LogonDialog logonDialog; // eigene Subklasse // von java.awt.Dialog public void init()
Applets – Java im Browser
353
9 Nitty Gritty • Take that!
Weitere Methoden
Sandini Bib { setLayout(new FlowLayout()); add(new Label("LogonDemo")); logonButton = new Button("Logonfenster"); logonButton.addActionListener(this); add(logonButton); } public void actionPerformed(ActionEvent e) { if (e.getSource() == logonButton) { if (logonDialog == null) logonDialog = new LogonDialog(); logonDialog.setVisible(true); } } }
9.2.2
Nitty Gritty • Take that!
9
Die Klasse JApplet
seit
SE
ME
EE
1.2
x
-
x
Diese Klasse JApplet liegt nicht im Package java.applet, sondern javax.swing (siehe Abschnitt 8.6), gehört aber logisch zu den Applets. Sie erweitert die Applets um typische Swing-Eigenheiten, wie im Abschnitt über JFrame angesprochen (siehe 8.6.14). java.awt.Panel
java.applet.Applet
javax.swing.JApplet contentPane: Container +setJMenuBar(menüleiste: JMenuBar) +getJMenuBar() ...
Bild 9.5: Die Klasse JApplet 354
Grundlagen
Sandini Bib
Im Unterschied zum Applet, wo Sie Kontrollelemente mit this.add() hinzufügen konnten, müssen Sie bei JApplet this.getContentPane().add() benutzen! 9.2.3
Das Interface AppletContext
seit
SE
ME
EE
1.0
x
-
x
Mit der Methode getAppletContext() jedes Applets erhalten Sie Zugriff auf den Kontext des Applets in der Webseite. Die Referenz vom Typ des Interfaces AppletContext dient vorwiegend zur Kommunikation zwischen dem Applet und dem Webbrowser beziehungsweise anderen Applets. «interface» AppletContext +showDocument(quelle: URL) +showDocument(quelle: URL, zielframe: String) +showStatus(meldung: String) +getApplet(name: String) +getApplets() +getImage(quelle: URL) +getAudioClip(quelle: URL)
Nitty Gritty • Take that!
9
Bild 9.6: Das Interface AppletContext
Methoden und Attribute Beispielsweise kann mit getAppletContext().getApplets()
oder getAppletcontext().getApplet("Appletname")
auf andere Applets zugegriffen werden, wobei bei letzterem Aufruf ein Applet durch den Tag <APPLET CODE=... NAME="Appletname" WIDTH=...>
mit einem Namen versehen werden muss. Mit der Referenz auf das Applet kann nun auf dessen Methoden zugegriffen werden.
Applets – Java im Browser
355
Sandini Bib
Die Methode showDocument() lädt eine Webseite in den Browser, in dem das Applet läuft. Diese Methode haben wir bereits bei der Behandlung der Klasse URL vorgestellt (siehe Abschnitt 7.2.4). Als optionaler Parameter kann auch der Frame im Browser angegeben werden, in den die Seite geladen wird, z. B. "_self" (aktueller Frame), "_parent" (umfassender Frame), "_top" (Top-Level-Frame), "_blank" (neuer Frame) oder ein explizit mit Namen versehener Frame. Mit showStatus() kann eine Meldung in der Statuszeile des Webbrowsers angezeigt werden. getImage() und getAudioClip() dienen zum Laden von Ressourcen. Als Bildformate werden GIF und JPG unterstützt (siehe 8.5.8), für weitere Bildverarbietungsfunktionen gibt es die Erweiterung Java Media Framework. Java Sound unterstützt folgende Audio-Dateiformate: AIFF, AU, WAV, MIDI, RMF bei einer Sample-Rate von 22kHz in 16-bit Stereo. Um Sound zu spielen sind zwei Schritte nötig: 1. Mit der Methode getAudioClip() erhalten Sie ein Objekt, das das Interface AudioClip implementiert.
Nitty Gritty • Take that!
9
2. Mittels play(), loop() oder stop() von AudioClip können Sie die Sounddatei abspielen.
9.3
Applets und Sicherheit
9.3.1 Restriktionen (Unsignierte) Applets unterliegen einigen Sicherheitseinschränkungen, die bei den Applikationen nicht bestehen. Man spricht vom so genannten Sandbox-Prinzip. Man geht hier von der Prämisse aus, dass jeglicher Java-Code, der auf dem lokalen Rechner installiert ist, als sicher zu betrachten ist und alles, was von außen kommt, als unsicher. Vor diesem potenziell gefährlichen Code müssen alle lokalen Daten und Ressourcen vor Manipulation und Informationsweitergabe geschützt werden. Dazu zählt insbesondere: T
Das Applet darf keine Dateien lesen oder schreiben, löschen, umbenennen oder Informationen über Dateien abfragen.
356
Applets und Sicherheit
Sandini Bib T
Es dürfen keine neuen Verzeichnisse angelegt sowie Informationen über Verzeichnisse oder deren Attribute ermittelt oder verändert werden.
T
Die Weitergabe von Informationen über das System und den Benutzer, zum Beispiel Rechner- oder Benutzernamen, sind verboten.
T
Das Applet darf außer der bestehenden Verbindung zum Webserver, von dem das Applet geladen wurde, keine neuen Netzwerkverbindungen aufbauen.
T
Es dürfen keine lokalen Programme gestartet (wie format.com...) und keine nativen Bibliotheken (DLLs) eingebunden werden.
T
Es dürfen keine »fremden« Threads beeinflusst werden. Das sind alle Threads, die nicht der Thread-Gruppe des Applets angehören.
T
Die Klassen der Java-Systemklassenbibliothek (java.*) dürfen nicht durch andere Klassen oder Versionen überschrieben werden. Ansonsten ließen sich obige Restriktionen durch Überschreiben der Security-Klassen leicht ausschalten.
T
Die Ausgabe auf einem Drucker ist ebenfalls nicht gestattet.
Diese Restriktionen werden durch verschiedene Mechanismen beim Kompilieren, beim Download von Klassen, dem Linken und während der Laufzeit überwacht. 9.3.2
Signierte Applets
Ein spezielles Problem von im Inter-/Intranet verteilten Applikationen ist, dass der Nutzer oft gar nicht damit rechnet, dass dort Gefahren durch bösartige oder fehlerhafte Anwendungen lauern könnten, da er ja nur eine Webseite anwählt. Im Gegensatz zu lokal installierten Programmen, die vor einer Freigabe zunächst in einer Testumgebung evaluiert werden können, starten Java-Applets sofort und automatisch. Auch eine Firewall hilft hier nichts, denn der heruntergeladene Code wird lokal ausgeführt. Man könnte nur alle Applets sperren und damit auch sämtlichen Nutzen verlieren. Zum Glück waren die Entwickler von Java sich aber dieses Problems bewusst und haben Mechanismen zum Schutz des Clients vor unbe-
Applets – Java im Browser
357
Nitty Gritty • Take that!
9
Überwachung der Restriktionen
Sandini Bib
kanntem oder gar unerwünschtem Code aus einer unsicheren, nicht vertrauenswürdigen (»untrusted«) Quelle eingeführt. Jedoch zeigte sich das Sandbox-Prinzip als zu restriktiv und wurde Schritt für Schritt weiter entwickelt: Signierte Applets Die Restriktionen der Sandbox waren sehr stark. Es war zum Beispiel nicht möglich, ein Java-Applet für das »sichere« Intranet zu schreiben, das Dateien lokal drucken oder speichern kann. In den neueren Versionen von Java, insbesondere seit Java 2, gibt es aber nun die Möglichkeit, Teile der Restriktionen für »sichere« Applets (signed oder trusted applets) aufzuheben. Neben dem sicheren lokalen Code und dem unsicheren Code aus dem Internet gibt es nun auch Code aus einer vertrauenswürdigen Quelle (»trusted code«). Das kann zum Beispiel ein Webserver im Intranet sein. Für diesen Code kann man nun Rechte einräumen, zum Beispiel »für alle Applets, die vom Intranetserver www.demo-domain.de stammen, ist Drucken erlaubt«.
Nitty Gritty • Take that!
9
Digitale Unterschriften Es muss sichergestellt sein, dass dieser Code mit zusätzlichen Berechtigungen auch wirklich aus einer vertrauenswürdigen Quelle stammt. Zu diesem Nachweis werden alle dazu notwendigen Klassen und Ressourcen (z. B. Bilder) in eine Java-Archiv-Datei (JAR) verpackt und diese wird digital signiert. Die digitale Unterschrift bezieht sich dabei auf die im Archiv enthaltenen Daten und es wird offensichtlich, wenn Klassen verändert oder bei der Übertragung beschädigt wurden. Um eine digitale Unterschrift leisten zu können, muss sich der Signierer bei einer autorisierten Zertifizierungsbehörde registrieren lassen, die seine Identität bestätigt. Wenn diese Applets von einem sicheren Host, der ein kryptologisches Zertifikat mitliefert, heruntergeladen werden und der Benutzer dieser Firma beziehungsweise dem Zertifikat traut, kann er zum Beispiel bestimmte Verzeichnisse zum Schreiben oder einen Drucker als Ressource freigeben. Er muss dies aber explizit tun, so dass ein unbemerktes Eindringen nicht möglich ist (sein sollte).
358
Applets und Sicherheit
Sandini Bib
Praxisprobleme Leider funktioniert dieses Sicherheitsmodell nur beschränkt, denn die Marktführer bei den Webbrowsern, Netscape und Microsoft, bieten hier eigene, proprietäre Sicherheitsmodelle an. Außerdem ist es relativ aufwändig, ein Zertifikat zu erwerben. Ein weiteres Problem des Sicherheitsmodells von Java 1.1 war eine Beschränkung nach dem Motto »Alles oder nichts«. Ein signiertes Applet hatte automatisch alle Berechtigungen. Java 2 Diese Probleme wurden in Java 2 aufgegriffen. Es ist nun möglich, detaillierte Zugriffsrechte via Policies für jeglichen Java-Code zu definieren, nicht nur für Applets aus dem Internet. Für einzelne Ressourcen-Zugriffe gibt es spezielle Zugriffsrechte, Permissions. Beispiele wären Lese-/Schreibzugriff auf eine bestimmte Datei auf dem lokalen Rechner oder Netzwerkverbindungen zu einem bestimmten Rechner (Host + Portnummer). Beispiel
Die wichtigste Erweiterung ist aber eine feingranulare Berechtigungsvergabe für Code aus beliebiger Quelle. Wenn der Code ausgeführt wird, wird eine abgesicherte Laufzeitumgebung erzeugt, die man als Protection Domain bezeichnet. Dieser Code kann Requests für den Zugriff auf bestimmte Ressourcen enthalten. Anhand der Quelle (URL) und den mitgeführten Zertifikaten wird durch Vergleich mit den lokalen Policies entschieden, ob ein Request erlaubt ist. Falls nicht, wird dem Programm dies durch eine Exception mitgeteilt.
Applets – Java im Browser
359
Nitty Gritty • Take that!
9
grant signed by "Addison-Wesley Verlag", codeBase "http://www.nittyGritty.de/*" { permission java.io.filePermission "c:\\temp\\*", "read"; permission java.io.SocketPermission "dbserver.nittyGritty.de:8000", "connect"; };
Sandini Bib
9.4
Einbindung in einer HTML-Seite
Applets können direkt mit dem Appletviewer des JDK und vor allem mit einem Java-fähigen Webbrowser angezeigt werden. In beiden Fällen muss das Applet in eine HTML-Seite (Hypertext Markup Language) eingebunden werden. -Tag Mittels des Tags wird die Klasse des Applets eingebunden. Ein sehr einfaches Beispiel lautet: Beispiel
Nitty Gritty • Take that!
9
<TITLE>Eine Java-Beispielseite Ein Applet zur Anzeige eines Datums <APPLET CODE=”DatumApplet.class” WIDTH=300 HEIGHT=100> Bitte benutzen Sie einen java-fähigen Browser!
Pfad Voraussetzung für den Start ist, dass die Klasse im selben Verzeichnis wie die HTML-Datei liegt. Falls Sie Java-Code in einem anderen Verzeichnis verwalten möchten, können Sie mit dem Parameter CODEBASE einen alternativen Pfad angeben: <APPLET CODE="DatumApplet.class" CODEBASE="../applets" WIDTH=300 HEIGHT=100>
Java-Archive Bei Java-Archiven (JAR-Dateien) kann mit der ARCHIVE-Direktive das Archiv angegeben werden, in der die Klasse unter CODE enthalten ist. Das Archiv wird dann als Ganzes geladen und die entsprechende Klasse gestartet. Daneben können Sie noch weitere Archive angeben, in denen zur Laufzeit nach vom Applet benutzten Klassen gesucht wird.
360
Einbindung in einer HTML-Seite
Sandini Bib <APPLET CODE="DatumApplet.class" ARCHIVE="datum.jar" WIDTH=300 HEIGHT=100>
Parameterübergabe Bei Applets gibt es einen anderen Mechanismus zu Übergabe von Parametern als das String-Array bei Applikationen. Sie werden mit PARAM-Tags in der HTML-Datei definiert und im Applet über die Methode getParameter(String name) abgefragt. Beispiel <APPLET CODE=”AppletDatum.class” WIDTH=300 HEIGHT=100> Bitte benutzen Sie einen java-fähigen Browser!
Abfrage im Applet: String imageFile img = getParameter("backgroundimage"); int delay = Integer.parseInt(getParameter("delay"));
Damit der Browser aber weiß, dass ein Applet nicht mit der Browser-VM, sondern dem Plug-In geladen werden soll, muss die HTML, in der es eingebunden ist, angepasst werden. Für diesen Vorgang gibt es ein Werkzeug namens HtmlConverter, das seit JDK 1.4 standardmäßig mitgeliefert wird. Dieses lässt man einfach über die fertige HTMLSeite laufen, bevor sie ausgeliefert wird. Wenn Sie noch ein ältere JDK-Version besitzen, können Sie den HtmlConverter auch allein unter http://java.sun.com/products/ plugin/1.3/converter.html herunterladen. Applets – Java im Browser
361
9 Nitty Gritty • Take that!
Da viele Browser (z.B. Internet Explorer Version 4-6, Netscape 4.x) eine installierte Java-Laufzeitumgebung nicht direkt ansprechen, sondern ihre eigene veraltete virtuelle Maschine mitliefern, muss man bei Verwendung neuerer Klassen, wie zum Beispiel bei einer Swing-Oberfläche, die benutzte Java-VM des Browsers umschalten. Dies geschieht durch Nutzung eines Java Plug-Ins. Das Plug-In ist seit Java 2 enthalten.
Sandini Bib
10 10
Sandini Bib
Weitere wichtige Bibliotheken
In diesem Kapitel wird das Komponentenmodell der JavaBeans, die Securityklassen sowie das Package java.security vorgestellt, das als Pool von verschiedenartigen Klassen dient, angefangen von Zeit und Datum bis hin zu Collections.
10.1 Vermischtes – java.util
Weitere wichtige Bibliotheken
363
10 Nitty Gritty • Take that!
java.util ist die Kategorie »Vermischtes« der Standard-Bibliotheken. Sie enthält Collection-Klassen, Klassen für Datum, Zeit und für mehrsprachige Anwendungen, einige Klassen zum Event Handling sowie weitere, die in keine andere Kategorie passen (siehe Abbildung 10.1). Zusätzlich gibt es noch die Subpackages java.util.zip und java.util. jar, die Klassen für den Komprimierungsalgorithmus zip enthält, der vor allem für Java-Archive (JAR-Dateien, siehe Abschnitt 4.10.5) wichtig ist. In Java 2 v1.4 kamen noch java.util.logging für die Ausgabe von Meldungen in Logdateien, java.util.prefs für die Speicherung von Anwendungs- und Benutzeroptionen sowie java. utils.prefs für die Auswertung von regulären Ausdrücken (Finden von Mustern in Strings) hinzu.
Sandini Bib
Nitty Gritty • Take that!
10 Bild 10.1: Die Klassen des Packages java.util
10.1.1 Datum und Zeit Die Klasse Date verwaltet Zeit- und Datumswerte intern als Ganzzahlen. Mit Hilfe der Klasse java.text.DateFormat kann sie landesspezifisch formatiert ausgegeben werden, die Klasse Calendar erlaubt den Zugriff auf die Teilkomponenten wie Tag, Monat oder Stunde. 10.1.2 Die Klasse Date seit
SE
ME
EE
1.0
x
x
x
Die Klasse Date beinhaltet Datums- und Uhrzeitfunktionen. Sie hat allerdings seit Java 1.1 viel von ihrer Funktionalität eingebüßt, da sie nicht für internationale Darstellungsunterschiede optimiert und für 364
Vermischtes – java.util
Sandini Bib
die Verwaltung von Jahren ab 2000 ungeeignet war; zum Beispiel musste das Jahr 2001 als 101 kodiert werden. Deshalb wurden die meisten Methoden als veraltet (deprecated) erklärt und in die Klasse Calendar übertragen. In der Klasse Date selbst blieb eine Datumsund Zeitfunktionalität, die auf einem Zeitmodell in Millisekunden seit dem 1. 1. 1970 basiert. Sie dient als Basisklasse der SQL-Klassen java.sql.Date, java.sql.Time und java.sql.Timestamp (siehe Abschnitt 7.6.7). Date <> +Date() +Date(date: long) <<Methoden>> +getTime() +setTime(zeit: long) +toString() +toLocaleString(): {deprecated} +before(zeit: Date) +after(zeit: Date) ...
Methoden und Attribute Die booleschen Methoden before() und after() dienen dem Vergleich von Date-Objekten. toString() gibt das Datum in einem Langformat aus, zum Beispiel Mon Apr 26 13:12:22 GMT+02:00 1999
Dies ist natürlich in der Regel nicht das gewünschte Format. Mit toLocaleString() kann man eine Ausgabe anhand der aktuellen Locale, das heißt der Ländereinstellungen für Sprache und Land (z. B. de_DE oder en_US) erreichen: 26.04.1999 13:12:22 toLocaleString() ist deprecated und sollte besser durch die Benutzung von java.text.DateFormat (siehe nächster Abschnitt) ersetzt werden, die eine genauere Steuerung der Ausgabe erlaubt.
Weitere wichtige Bibliotheken
365
Nitty Gritty • Take that!
10
Bild 10.2: Die Klasse Date
Sandini Bib
10.1.3
Die Klasse DateFormat
seit
SE
ME
EE
1.1
x
-
x
Die Klasse DateFormat ist nicht in java.util, sondern in java.text enthalten. Sie dient zur Formatierung von Zeit- und Datumswerten. DateFormat +SHORT: int +MEDIUM: int +LONG: int +FULL: int timeZone: TimeZone numberFormat: NumberFormat +getDate/TimeInstance() +getDate/TimeInstance(stil: int) +getDate/TimeInstance(stil: int, gebietsschema: Locale) +getDateTimeInstance() +getDateTimeInstance(datumStil: int, zeitStil: int) +getDateTimeInstance(datumStil: int, zeitStil: int, gebietsschema: Locale) +format(datumOderZeit: Date) +parse(datumOderZeit: String)
10 Nitty Gritty • Take that!
Bild 10.3: Die Klasse DateFormat
Methoden und Attribute Mit getDateInstance() erhalten Sie eine Instanz für die Formatierung von Datumswerten, getTimeInstance() für Zeitwerte und getDateTimeInstance() für eine kombinierte Ein-/Ausgabe. Optional können Sie einen Stil und ein Gebietsschema (siehe Abschnitt 10.1.5) angeben. Als Stile sind zugelassen: T SHORT: 14.06.00 bzw. 10:53 T MEDIUM: 14.06.2000 bzw. 10:53:35 T LONG: 14. Juni 2000 bzw. 10:53:35 GMT+02:00 T FULL: Mittwoch, 14. Juni 2000 bzw. 10.53 Uhr GMT+02:00
Die Methode format() gibt einen Date-Wert im eingestellten Format aus, die Methode parse() liest einen String in diesem Format und liefert ein Date-Objekt. 366
Vermischtes – java.util
Sandini Bib
Beispiel Wir haben dies in unserem Datumsbeispiel in 7.1.2 benutzt: String timestamp = (new Date(curFile.lastModified())).toLocaleString();
Eine saubere Lösung (ohne deprecated-Methoden) lässt sich mit der Klasse DateFormat erreichen: java.text.DateFormat df = java.text.DateFormat.getInstance(); String timestamp = df.format(new Date(curFile.lastModified()));
Das Ergebnis ist bis auf die fehlenden Sekunden identisch: 26.04.99 13:12.
10.1.4 Die Klasse Calendar SE
ME
EE
x
x
x
10
Die Klasse Calendar ersetzt seit Java 1.1 in weiten Teilen Date (Abschnitt 10.1.2) und stellt Funktionalität zum Setzen der Zeitzone und zum Abfragen einzelner Felder, zum Beispiel des Wochentags, bereit. Sie enthält außerdem eine Reihe Konstanten für Monate, Tage etc. Calendar {abstract} +YEAR: int +MONTH: int +WEEK_OF_YEAR: int +DAY_OF_MONTH: int +DAY_OF_WEEK: int +DAY_OF_YEAR: int +HOUR: int +MINUTE: int +SECOND: int +ZONE_OFFSET: int +MONDAY...SUNDAY: int +JANUARY...DECEMBER: int +getInstance() +getInstance(gebietsschema: Locale) +getInstance(zeitzone: TimeZone) +toString() +get(attribut: int) +set(attribut: int, wert: int) +set(jahr: int, monat: int, tag: int) +setTime(d: Date) +setTimeZone(zone: TimeZone)
Bild 10.4: Die Klasse Calendar Weitere wichtige Bibliotheken
367
Nitty Gritty • Take that!
seit 1.1
Sandini Bib
Beispiel So sieht beispielsweise der Zugriff auf das Jahr eines Calendar-Objekts aus: Calendar cal = Calendar.getInstance(); // Instanz mit // aktueller Zeitzone und Ländereinstellung cal.setTime(new Date()); // heute int jahr = cal.get(Calendar.YEAR);
Nitty Gritty • Take that!
10
10.1.5
Internationalisierung
seit
SE
ME
EE
1.1
x
-
x
Neben der Zeitzone, die in der Klasse TimeZone implementiert ist und auf den Zeitzonen relativ zur Greenwich Mean Time (GMT) basiert, und der Klasse Locale, die die Sprache (z. B. de für Deutsch oder en für Englisch) und das Land (z. B. DE für Deutschland, AT für Österreich und US für die USA) kodiert, ist hier das Modell der ResourceBundles wichtig. Diese erlauben, internationale Anwendungen zu schreiben, deren Strings dynamisch anhand der aktuellen Ländereinstellungen geladen und angezeigt werden. Anwendung Locale= de_DE
:ResourceBundle
xy_de_DE xy_en_US
getBundle("xy")
getString("Cancel")
key="Cancel" Êþº¾ - o "Ljava/beans/PropertyChange Support; propertyChange v334 Ljava/awt/Color; fieldLineColor I fieldLineWidth (Ljava/awt/Color;)V r33 34, 434, j
"Abbrechen"
Locale= en_US
setLocale(new Locale("en","US") getString("Cancel")
key="Cancel"
"Cancel"
Bild 10.5: String-Abruf aus ResourceBundles
368
Vermischtes – java.util
Êþº¾ - o "Ljava/beans/PropertyChange Support; propertyChange v334 Ljava/awt/Color; fieldLineColor I fieldLineWidth (Ljava/awt/Color;)V r33 34, 434, j
Sandini Bib
ResourceBundles Die Anwendung ist sehr einfach. Statt direkt eine String-Konstante im Code anzulegen, lädt man jeden String mit der Methode getString(schlüssel) aus einem ResourceBundle. Das Bundle kann dabei entweder eine Datei oder eine Klasse sein. Jetzt muss man nur noch beim Start der Anwendung oder in der Initialisierung des Applets die Quelle angeben: Beispiel ResourceBundle b = ResourceBundle.getBundle(bundlename); // bundlename ohne Landesendung String cancelButtonLabel = getString("Cancel");
Die Anwendung sucht nach folgendem Schema ein passendes Bundle:
T
bundlename__.class, z. B. xy_de_DE.class bundlename_.class, z. B. xy_de.class
T
bundlename_.class, z. B. xy.class
10
Falls keine der Klassen gefunden werden kann, erhält man eine MissingResourceException. Damit ist die Anwendung völlig unabhängig von der Sprache und muss auch nicht geändert werden, wenn eine neue Sprache hinzukommt. In diesem Fall muss nur eine neue ResourceBundle-Klasse hinzugefügt werden. Diese hat folgenden Aufbau: Beispiel public class xy_de_DE extends java.util.ListResourceBundle { static final Object[][] contents = { // Paare aus Schlüssel und lokalisiertem String {"Cancel", "Abbrechen"}, {"Help", "Hilfe"} } protected Object[][] getContents() { return contents; } }
Weitere wichtige Bibliotheken
369
Nitty Gritty • Take that!
T
Sandini Bib
10.1.6 Collection-Klassen Java 1.1 enthält einige wenige, aber sehr wichtige Klassen zur Verwaltung von Objekten – so genannte Collections (Abschnitt 10.1.7 bis 10.1.11). In Java 2 wurde die Auswahl stark erweitert. Es kamen zum Beispiel Mengen (Set), Sequenzen (List), Maps und sortierte Varianten hinzu (Abschnitt 10.1.12 bis 10.1.18). 10.1.7
Die Klasse Vector
seit
SE
ME
EE
1.0
x
x
x
Vector ist eine Klasse, die eine Art Array darstellt, mit dem Unterschied, dass ein Objekt vom Typ Vector in seiner Länge nicht von Anfang an bestimmt sein muss. Ein Vector-Objekt stellt eine lineare Liste von Objekten beliebiger Klassen (also Instanzen von java.lang. Object) dar, das heißt, jedes Element hat eine feste Position. Map
Nitty Gritty • Take that!
10
Vector <> +Vector() +Vector(startkapazität: int) <<Methoden>> +elementAt(index: int) +size() +first/lastElement() +addElement(obj: Object) +insertElementAt(obj: Object, index: int) +setElementAt(obj: Object, index: int) +copyInto(obj: Object[]) +contains(obj: Object) +indexOf/lastIndexOf(obj: Object) +removeElement(obj: Object) +removeElementAt(index: int) +removeAllElements() +elements() +capacity()
Bild 10.6: Die Klasse Vector
370
Vermischtes – java.util
Sandini Bib
Methoden und Attribute Die Methode addElement() fügt am Ende des Vectors eine Referenz auf ein neues Objekt an, insertElementAt() an einer beliebigen Stelle. Mit copyInto() können alle Elemente vom Vector-Objekt in das übergebene Array kopiert werden. Mit contains() kann man das Enthaltensein eines Objekts im Vector prüfen, mit elementAt() fragt man ein Element ab, indexOf() bzw. lastIndexOf() liefern die Position eines Objekts im Vector und size() dessen Größe. Verwechseln Sie size() nicht mit capacity()! Letzteres gibt an, wie viele Elemente im Vector-Objekt Platz haben, ohne dass eine (automatische) Größenanpassung erfolgt. Mit removeElement() beziehungsweise removeAllElements() werden Objekte aus dem Vector entfernt. Seit Java 2 sind einige Methoden zu Vector hinzugekommen, damit das Collection-Interface List implementiert wird (siehe Abschnitt 10.1.15).
10
Array
Vector
Erzeugung
Object[] feld = new Object[10]; String[] str = new String[100];
Vector vektor = new Vector();
Zugriff auf i-tes Element
Object o = feld[i];
Object o = vektor.elementAt(i); String os = (String) vektor.elementAt(i);
Objekt o anhängen
for(int i=0; i
String os = str[i];
Nitty Gritty • Take that!
Vergleich mit Arrays
vektor.addElement(o);
Weitere wichtige Bibliotheken
371
Sandini Bib Array
Vector
Position von o suchen
for(int i=0; i
pos = vektor.indexOf(o);
Anzahl der Elemente
feld.length
vektor.size()
Nachteile
aufwändige Programmierung, keine flexible Größenanpassung
keine strenge Typprüfung, Casting notwendig, nicht für elementare Datentypen
10.1.8 Das Interface Enumeration
Nitty Gritty • Take that!
10
seit
SE
ME
EE
1.0
x
x
x
Klassen wie Vector oder Hashtable, die eine Liste von Elementen enthalten, die man der Reihe nach durchlaufen kann, bieten die Möglichkeit mittels so genannter Iteratoren die Elemente zu durchlaufen. «interface» Enumeration +hasMoreElements() +nextElement()
Bild 10.7: Das Interface Enumeration
Methoden und Attribute Der Zugriff auf die von einem Iterator referenzierten Elemente geschieht mit Hilfe des Interface Enumeration mit den beiden Methoden hasMoreElements() und nextElement(). Die Klasse Vector stellt ein Enumeration-Objekt durch die Methode elements() zur Verfügung.
372
Vermischtes – java.util
Sandini Bib
Beispiel Enumeration liste = quelle.elements(); while (liste.hasMoreElements()) { Object elem = nextElement(); // elem casten und benutzen... }
10.1.9 Die Klassen Hashtable und Properties seit
SE
ME
EE
1.0/1.2
x
(x)
x
Ein Dictionary ist eine Menge von Elementen, die über einen Schlüsselwert identifiziert werden. In der Regel benutzt man die von der abstrakten Klasse Dictionary abgeleitete konkrete Klasse Hashtable, deren wichtigste Methoden put(), get() und contains() darstellen. Davon abgeleitet ist die Klasse Properties, ein Satz von Schlüssel-Wert-Paaren, die auch per Streams geladen und geschrieben werden können.
10
Dictionary {abstract}
Hashtable
Nitty Gritty • Take that!
+put(schlüssel: Object, wert: Object) +get(schlüssel: Object) +elements() +keys() +size() +isEmpty() +remove(schlüssel: Object)
Map
+contains/containsValue(wert: Object) +containsKey(schlüssel: Object) +clone() +clear() +values() {seit Java 1.2} +entrySet/keySet {seit Java 1.2} +putAll(daten: Map) {seit Java 1.2}
Properties
+getProperty(schlüssel: String) +getProperty(....) +getProperty(schlüssel: String, defaultWert: String) +load(inStream: InputStream) +load(inStream: InputStream) +save(out: OutputStream, titel: String) : {deprecated} +save(out: OutputStream, String) : {deprecated} +store(out: OutputStream, header: titel: String) +store(out: OutputStream, header: String) +list(debugAusgabe: PrintStream/PrintWriter)
Bild 10.8: Die Klassen Hashtable und Properties Weitere wichtige Bibliotheken
373
Sandini Bib
Seit Java 2 sind einige Methoden zu Hashtable hinzugekommen, damit das Collection-Interface Map implementiert wird (siehe Abschnitt 10.1.17). In der Java Micro Edition gibt es nur die Klasse Hashtable, die dort direkt von Object abgeleitet ist. 10.1.10 Die Klasse Stack seit
SE
ME
EE
1.0
x
x
x
Eine Subklasse von Vector ist die Klasse Stack, die einen Last-in-firstout-Stapel implementiert. Die wichtigsten Methoden sind push() und pop(). Vector
Stack
Nitty Gritty • Take that!
10 +push(obj: Object) +pop() +peek() +search(obj: Object) +empty()
Bild 10.9: Die Klasse Stack
10.1.11 Die Klasse BitSet seit
SE
ME
EE
1.0
x
-
x
BitSet ist eine Menge boolescher Flags (Bits), die mit Operationen wie and(), or() oder xor() verknüpft werden können. BitSet +and/or/xor(bs: BitSet) +clear/get/set(index: int) +size() +clone()
Bild 10.10: Die Klasse BitSet 374
Vermischtes – java.util
Sandini Bib
10.1.12 Java-2-Collections Die bisherigen Implementierungen sind zwar für die meisten Fälle ausreichend. Manchmal hat man aber verschiedene Anforderungen an eine Implementierung und möchte deshalb abstrakte CollectionTypen wie z. B. sortierte Liste von der konkreten Implementierung lösen. Dann kann man etwa für einen Anwendungsfall mit relativ statischen Daten, aber vielen Suchzugriffen einen anderen Algorithmus wählen als für Daten mit großer Änderungshäufigkeit. Das seit Java 1.2 enthaltene Collections Framework beinhaltet solche Collections: Set (eine Liste ohne doppelte Elemente) und SortedSet, List (ersetzt Vector), Map (ersetzt Hashtable) und SortedMap. Es ist möglich, zusätzlich zur natürlichen Ordnung eigene Comparator-Interfaces zu implementieren und nach ihnen zu sortieren. Die Namen der Zugriffsmethoden sind verkürzt worden (list.set(index, object) statt vector.setElementAt(object, index). Das Package java.util enthält u. a. folgende Interfaces: Beschreibung
Implementierungen
Collection List
Æ Set Æ SortedSet Map
Æ SortedMap
keine Implementierungen ähnlich Vector
ArrayList, LinkedList, Vector
Liste ohne doppelte Elemente
HashSet
sortiertes Set
TreeSet
Zugriff über Schlüssel
HashMap, Hashtable
Map, nach Schlüsseln sortiert
TreeMap
Entsprechend dem Collections Framework sollten in Methoden als Eingabewerte immer die (möglichst allgemeinen) Interfaces und nicht die Implementierungen übergeben werden.
Weitere wichtige Bibliotheken
375
10 Nitty Gritty • Take that!
Abstrakte Collection
Sandini Bib
10.1.13 Das Interface Collection seit
SE
ME
EE
1.2
x
-
x
Das Interface Collection bildet die Basis aller neuen Collection-Klassen und -Interfaces und enthält allgemeine Verwaltungsfunktionen. «Interface» Collection +clear() +add(obj: Object) +addAll(collection: Collection) +remove(obj: Object) +removeAll(collection: Collection) +contains(obj: Object) +containsAll(collection: Collection) +equals(collection: Object) +isEmpty() +size() +retainAll(collection: Collection) +iterator() +toArray()
10 Nitty Gritty • Take that!
Bild 10.11: Das Interface Collection
Methoden und Attribute Das Interface Collection enthält die (selbsterläuternden) Methoden isEmpty(), contains(), containsAll(). Die boolean Methoden add(), addAll(), remove(), removeAll(), retainAll() liefern true, wenn die Collection durch die Operation verändert wurde. Der Befehl c1.addAll(c2) fügt alle Elemente aus c2 zu c1 hinzu. c1.retainAll(c2) bedeutet, dass alle nicht in c2 enthaltenen Elemente aus c1 gelöscht werden. Beispiel Folgender Einzeiler entfernt alle Vorkommen des Objektes e aus einer Collection c (Collections.singleton(e) erzeugt ein unveränderbares Set mit nur einem Element) c.removeAll(Collection.singleton(e));
376
Vermischtes – java.util
Sandini Bib
10.1.14 Das Interface Iterator seit
SE
ME
EE
1.2
x
-
x
Der Iterator (ersetzt Enumeration, siehe Abschnitt 10.1.11) erlaubt u.a. das Löschen von Elementen aus seiner Collection. «interface» Iterator +hasNext() +next() +remove()
Bild 10.12: Das Interface Iterator
Beispiel Folgendes Beispiel entfernt alle Objekte aus einer Collection, die die Bedingung cond nicht erfüllen:
10.1.15 Das Interface List seit
SE
ME
EE
1.2
x
-
x
Eine List ist eine Collection mit Objekten in einer festen Reihenfolge. Auf jedes Element kann mit einem Index zugegriffen werden. An Implementierungen für Listen enthält java.util ArrayList (= ein Array mit veränderbarer Größe), LinkedList (eine doppelt verkettete Liste) und die angepasste Klasse Vector. ArrayList ist für die meisten Anwendungen die schnellste Implementierung: Es ist schneller als Vector, dafür aber nicht synchronisiert.
Weitere wichtige Bibliotheken
377
Nitty Gritty • Take that!
10
static void filter(Collection c) { for (Iterator i = c.iterator(); i.hasNext(); ) if (!cond(i.next())) i.remove(); }
Sandini Bib
«interface» Collection
«interface» List +get(index: int) +set(index: int, obj: Object) +add(index: int, obj: Object) +remove(index: int) +subList(anfang: int, ende: int) +listIterator() +indexOf/lastIndexOf(obj: Object)
Nitty Gritty • Take that!
10
LinkedList
ArrayList
<> +LinkedList() +LinkedList(collection: Collection) <<Methoden>> +addFirst/Last(obj: Object) +getFirst/Last() +removeFirst/Last()
<> +ArrayList() +ArrayList(collection: Collection) +ArrayList(anfangskapazität: int) <<Methoden>> +trimToSize() +ensureCapacity(kap: int)
Vector
Bild 10.13: Das Interface List und implementierende Klassen
Methoden und Attribute Das Interface List fügt zu der von Collection geerbten Funktionalität folgende Funktionalität hinzu: get() und set() erlauben den index-basierten Zugriff auf einen Eintrag. Mit indexOf() bzw. lastIndexOf() können Sie den Index eines Listenelements suchen. Die Range-View-Operation subList(anfang, ende) erzeugt eine Ansicht auf die Liste, die die Elemente vom Index anfang inklusive bis ende exklusive enthält. Alle List-Operationen kön-
nen auf die Subliste angewandt werden und beeinflussen ggf. die ursprüngliche Liste.
378
Vermischtes – java.util
Sandini Bib
Beispiel Folgendes Beispiel erzeugt eine neue Liste, in der list2 an list1 angehängt ist: List list3 = new ArrayList(list1); list3.addAll(list2);
Bei der Benutzung des ListIterators ist zu beachten, dass der Index immer zwischen zwei Elementen steht: Steht z.B. der Index auf 1, liefert next() das zweite und previous() das erste Element in der Liste. Beispiel Ersetzung alle Vorkommen des Objektes val in einer Liste durch newVal: public void replace(List l, Object val, Object newVal) { // l, val und newVal seien <> 0 for (ListIterator i=l.listIterator(); i.hasNext(); ) if (val.equals(i.next()) i.set(newVal); }
10.1.16 Das Interface Set seit
SE
ME
EE
1.2
x
-
x
Ein Set ist eine Menge, in der keine doppelten Einträge vorkommen können. Set hat die gleichen Methoden wie Collection. Standard-Implementierungen für Set sind das unsortierte HashSet (= ein Array mit veränderbarer Größe) und das sortierte TreeSet (= Binärbaum).
Weitere wichtige Bibliotheken
379
Nitty Gritty • Take that!
10
Sandini Bib
«interface» Collection
«interface» Set
HashSet
«interface» SortedSet
+HashSet() +HashSet(collection: Collection) +HashSet(anfangsskapazität: int) +HashSet(anfangsskapazität: int, ladeFaktor: int)
+first/last() +subSet(anfang: Object, ende: Object) +headSet/tailSet(grenze: Object) +comparator()
TreeSet
Nitty Gritty • Take that!
10 +TreeSet() +TreeSet(collection: Collection) +TreeSet(vergleich: Comparator) +TreeSet(collection: SortedSet)
Bild 10.14: Das Interface Set und implementierende Klassen
Methoden und Attribute equals() liefert true, wenn zwei Sets die gleichen Elemente enthalten
– egal, wie die Sets implementiert sind. In einem SortedSet sind die Einträge nach einem Vergleichsmuster (Comparator, siehe Abschnitt 10.1.18) vergleichbar und liegen sortiert vor. Beispiel Folgendes Beispiel benutzt ein Set, um Duplikate aus einer Collection zu entfernen: Collection noDups = new HashSet(c);
380
Vermischtes – java.util
Sandini Bib
10.1.17 Das Interface Map seit
SE
ME
EE
1.2
x
-
x
Eine Map ist eine Menge von Objekten, auf die mittels Schlüssel zugegriffen wird. Jedem Schlüssel (Key) ist genau ein Wert (Value) zugeordnet. Standard-Implementierungen sind HashMap und HashTable (= größenveränderliches Array, siehe Abschnitt 10.1.9) und TreeMap (= sortierter Binärbaum). «interface» Collection
«interface» Map +put(schlüssel: Object, wert: Object) +putAll(collection: collection) +get(schlüssel: Object) +containsKey(schlüssel: Object) +containsValue(wert: Object) +values() +keySet/entrySet() +remove(schlüssel: Object)
HashMap
«interface» SortedMap
+HashMap() +HashMap(collection: Collection) +HashMap(anfangsskapazität: int) +HashMap(anfangsskapazität: int ladeFaktor: int)
+first/lastKey() +subMap(anfang: Object, ende: Object) +headMap/tailMap(grenze: Object) +comparator()
HashTable
Nitty Gritty • Take that!
10
TreeMap
+TreeMap() +TreeMap(collection: Map) +TreeMap(vergleich: Comparator) +TreeMap(collection: SortedMap)
Bild 10.15: Das Interface Map und implementierende Klassen Weitere wichtige Bibliotheken
381
Sandini Bib
Methoden und Attribute Wie im bisherigen Hashtable (siehe Abschnitt 10.1.9) erzeugt put(schlüssel, wert) einen neuen Eintrag oder überschreibt den bisherigen, wenn der Schlüssel bereits existiert. get(schlüssel) liefert den Eintrag, der dem Schlüssel zugeordnet ist. contains(wert) wurde der besseren Lesbarkeit halber umbenannt in containsValue(wert). Beispiel In den folgenden Beispielen werden Schlüssel und Werte gemischt: Die Map manager ordnet jedem Angestellten seinen Vorgesetzten zu. Folgende Routine ermittelt alle Angestellten, die keine Vorgesetzten sind: Set einfachAngestellte = new HashSet(manager.keySet()); einfachAngestellte.removeAll(managers.values());
So entlässt man alle Angestellten, die einem Manager bill zugeordnet sind: manager.values().removeAll(Collections.singleton(bill));
Nitty Gritty • Take that!
10
Durch diese Operation könnte es geschehen, dass einige Mitarbeiter keinen Vorgesetzten mehr haben (wenn nämlich jemand von den eben Entlassenen selbst Manager ist). Der folgende (etwas komplizierte) Code liefert alle Angestellten, deren Manager nicht mehr für die Firma arbeitet: Map m = new HashMap(manager); m.values().removeAll(manager.keySet()); Set angestellteOhneManager = m.keySet();
10.1.18 Die Klasse Collections seit
SE
ME
EE
1.2
x
-
x
Die Klasse Collections ist eine Hilfsklasse mit Klassenmethoden für Operationen auf Mengen sowie zur Kompatibilität zu den alten Collection-Klassen von Java 1.2.
382
Vermischtes – java.util
Sandini Bib
Ebenso gibt es eine Hilfsklasse Arrays für Operationen mit Arrays (Feldern). Collections +sort(liste: List) +sort(liste: List, vergleichsOperation: Comparator) +reverse(liste: List) +fill(liste: List, obj: Object) +shuffle(liste: List) +copy(ziel: List, quelle: List) +binarySearch(sortierteListe: List, obj: Object) +enumeration(collection: Collection) +singleton(obj: Object) : Set +min/max(collection: Collection) +min/max(collection: Collection, vergleichsOperation: Comparator) +synchronizedList(liste: List) +synchronizedMap(map: Map) +synchronizedSet(set: Set)
«interface» Comparator +compare(obj : Object, obj2: Object) +equals(kap: int, obj Object)
Bild 10.16: Die Klasse Collections
Methoden und Attribute
10
die Sortierreihenfolge auch anders als alphabetisch gestalten können. shuffle() permutiert die Liste in zufälliger Weise, reverse() dreht sie um. fill() füllt alle Elemente mit dem angegebenen Object. copy() kopiert die zweite Liste in die erste. binarySearch() funktioniert nur in sortierten Listen und liefert den Index des Objektes. singleton() erzeugt ein unveränderbares Set, bestehend nur aus dem Objekt. Aus Kompatibilitätsgründen zum Interface Enumeration (siehe Abschnitt 10.1.9) können Sie mit der Methode enumeration() ein implementierendes Objekt erzeugen. Die synchronized-Methoden erzeugen jeweils eine thread-sichere Variante der angegebenen Collection. Beispiel Folgendes Programm liefert die beim Aufruf übergebenen Werte in zufälliger Reihenfolge:
Weitere wichtige Bibliotheken
383
Nitty Gritty • Take that!
sort() sortiert die Liste, wobei Sie mit Hilfe des Interface Comparator
Sandini Bib import java.util.*; public class Shuffle { public static void main(String args[]) { List l = Arrays.asList(args); // eine Hilfsklasse // für Arrays Collections.shuffle(l); System.out.println(l); } }
10.1.19 Logging
Nitty Gritty • Take that!
10
seit
SE
ME
EE
1.4
X
-
-
Mit den Klassen aus dem Package java.util.logging lassen sich einfach Einträge in Logdateien schreiben. Zentrale Klasse ist Logger. An diese Klasse meldet man die Logeinträge. Dies bedeutet aber noch nicht, dass sie auch im Logfile landen. Am Logger sind ein oder mehrere Handler registriert. Diese können verschiedene Ausgabearten darstellen, zum Beispiel Konsole oder eine Datei, und zusätzlich Filter anwenden, so dass beispielsweise nur Fehler, Warnungen oder auch Info-Meldungen ausgegeben werden. Schließlich bleibt die Frage, wie die Logmeldungen aussehen. Dazu gibt es verschiedene Formatter, zum Beispiel den SimpleFormatter oder den XMLFormatter. Sie können aber auch individuelle Log-Formatter schreiben. Beispiel import java.util.logging.*; public class LogDemo { private static Logger logger = Logger.getLogger("meinLogger"); public static void main(String args[]) {
384
Vermischtes – java.util
Sandini Bib logger.info("Anwendung gestartet"); try { [...] // etwas tun } catch (Exception e) { logger.severe("Fehler: " + e.getMessage()); } logger.info("Anwendung beendet"); } }
Angenommen, es tritt nun ein Fehler auf, ergibt sich folgendes Log auf der Konsole: 01.02.2002 07:32:36 LogDemo main INFO: Anwendung gestartet 01.02.2002 07:32:37 LogDemo main SCHWERWIEGEND: Fehler: Die Fehlerbeschreibung 01.02.2002 07:32:37 LogDemo main INFO: Anwendung beendet
Sie können mit logger.addHandler(new FileHandler("log.txt") die Ausgabe auch in eine Logdatei schreiben. Wenn Sie die Info-Meldungen abschalten wollen und nur Warnungen und Fehler anzeigen möchten, ändern Sie den Loglevel mit logger.setLevel(Level.WARNING). Haben Sie einen XMLFormatter eingestellt, könnte nun eine Meldung so aussehen: 2002-02-01T07:40:57 <millis>1012545657423 <sequence>0 meinLogger SEVERE LogDemo <method>main
Weitere wichtige Bibliotheken
385
Nitty Gritty • Take that!
10
Sandini Bib 10 <message>Fehler: Die Fehlerbeschreibung
10.2 Referenzen – java.lang.ref seit
SE
ME
EE
1.2
X
-
x
Seit JDK 1.2 gibt es neben den herkömmlichen Referenzen einige speziellere Typen, die den Zustand von Objekten innerhalb der automatischen Speicherbereinigung ausdrücken. So genannte Soft-, Weakund Phantomreferences geben Auskunft, inwieweit diese Objekte tatsächlich schon aus dem Speicher entfernt wurden. Dies kann nützlich sein, wenn Sie einen Caching-Mechanismus schreiben möchten.
10.3 Reflection – java.lang.reflect
Nitty Gritty • Take that!
10
seit
SE
ME
EE
1.1
x
-
x
Die Klassen dieses Packages sind normalerweise für den Entwickler nicht wichtig, es sei denn, Sie wollen eine Entwicklungsumgebung oder einen Codegenerator schreiben. Sie können damit Klassen analysieren. Als Basis dient dabei die Klasse java.lang.Class (siehe Abschnitt 6.1.3), deren Methoden Objekte vom Typ Constructor, Method, Field usw. zurückliefern, die im Package java.lang.reflect definiert sind.
10.4 JavaBeans – java.beans JavaBeans ist das Komponentenmodell von Java. Diese Komponenten haben eine exaktere und für Programmierwerkzeuge verständlichere Schnittstellendefinition als Java-Klassen. Die zur Beschreibung und Analyse benötigten Interfaces und Klassen stehen im Package java.beans. Sie arbeiten sehr eng mit den Klassen zur Laufzeitanalyse von Java-Klassen im Package java.lang.reflect (siehe Abschnitt 10.3) 386
Referenzen – java.lang.ref
Sandini Bib
zusammen. Die Schnittstelle von JavaBeans bezeichnet man als Features; sie besteht aus drei Teilen: JavaBeans
Properties - Attribute mit Gettern und Settern - Gebundene Properties
Methoden - öffentliche Methoden
Ereignisse - AWT/SwingEvents - PropertyChangeEvent
dokumentierte Schnittstelle (in BeanInfo)
Bild 10.17: Die JavaBean-Features
2. Die öffentlichen Methoden, also Funktionen, die eine Bean ausführen kann, nennt man Methods. 3. Eine Bean erzeugt Ereignisse. Dies geschieht hauptsächlich, wenn sich ihre Properties ändern. Diese drei Arten von Features sind im Interface BeanInfo dokumentiert, das Interface PropertyEditor dient dem Customizing von JavaBeans. «Interface» BeanInfo +getPropertyDescriptors() +getMethodDescriptors() +getEventSetDescriptors() +getBeanDescriptor() +getIcon(iconTyp: int)
Bild 10.18: Das Interface BeanInfo
Weitere wichtige Bibliotheken
387
10 Nitty Gritty • Take that!
1. Die öffentlichen Attribute oder Eigenschaften, die eine JavaBean auszeichnen, heißen Properties. Über diese Properties sind Beans an die Vorstellungen des Programmierers anpassbar, ohne dass er den Quellcode ändern oder darauf irgendeinen Zugriff haben muss. Diese Anpassung bezeichnet man als Customizing.
Sandini Bib
Zu jeder Komponente wird zusätzlich zu implementierenden Klasse eine BeanInfo-Klasse mitgeliefert, die die Features der Bean beschreibt, zum Beispiel zu AnimationList.class eine AnimationListBeanInfo.class. Die BeanInfo-Klasse ist explizit dafür vorgesehen, dass grafische Entwicklungsumgebungen diese analysieren, um einzelne Komponenten zu einer Anwendung zusammenzustellen. Die Werkzeuge bieten meist auch komfortable Möglichkeiten an, selbst JavaBeans zu erzeugen und die BeanInfo-Klasse mittels Wizards und Generatoren zu erzeugen. Persistenz JavaBean-Instanzen können zur dauerhaften Speicherung genau wie andere Objekte auch per ObjectOutputStream in Byteform gewandelt werden, wenn sie das Interface java.io.Serializable implementieren (siehe Kapitel 7.1.5). Zusätzlich gibt es seit Java 2 V1.4 auch die Möglichkeit, JavaBeans per XMLEncoder in XML-Textform zu sichern und per XMLDecoder wieder einzulesen.
Nitty Gritty • Take that!
10
10.5 Verschlüsselung – java.security Klassen und Interfaces für die Verschlüsselung von Daten, die Erzeugung und Überprüfung von Schlüsseln und Zertifikaten finden sich im Package java.security. Im Umfeld der Applets (Kapitel 9) haben wir bereits die Problematik von unerwünschtem und gefährlichem Code aus einer unbekannten Quelle und Javas Antwort in Form des Sandbox-Prinzips und signierter Applets behandelt. Für die Verschlüsselung und Signatur werden im Package java.security Klassen und Interfaces bereitgestellt (siehe Abbildung 10.19)
388
Verschlüsselung – java.security
Sandini Bib
10
Bild 10.19: Klassen im Package java.security
Verschlüsselung durch Security-Klassen
Die Verschlüsselung und Schlüsselgenerierung erfolgt mittels Public-Key-Verfahren nach der Java Cryptographic Architecture (JCA). Im Package java.security sind jedoch nur die Schnittstellen für die Benutzung der Verfahren festgelegt, nicht jedoch das Verfahren (die Implementierung) selbst. Es ist lediglich definiert, dass EinwegHash-Funktionen Verwendung finden. Diese produzieren einen Ausgabe-String fester Länge (Hash oder Digest). Man nennt diese Algorithmen deshalb auch Message-Digest-Algorithmen. Diejenigen Klassen, die die abstrakten Klassen MessageDigest, KeyPairGenerator und Signature implementieren, nennt man EngineKlassen. Sun liefert MD5 und SHA-1 als Message-Digest-Algorithmen und DSA als Signatur-Algorithmus.
Weitere wichtige Bibliotheken
389
Nitty Gritty • Take that!
10.5.1
Sandini Bib
Krypto-Implementierungen APIs für Ver- und Entschlüsselung sind seit Java 2 V1.4 zusammen mit einigen Implementierungen im Package javax.crypto unter dem Schlagwort JCE (Java Cryptography Extension) zu finden. Für die verschlüsselte Netzwerkkommunikation über HTTP oder TCP/IP kann auch SSL (Secure Socket Layer) benutzt werden, die notwendigen Klassen befinden sich in javax.net.ssl. 10.5.2 Java 2 – Protection Domains In Java 2 wurden das Sicherheitsmodell verfeinert und die SecurityPackages erweitert. So ist es jetzt beispielsweise möglich, die Zugriffsrechte via Policies (java.security.Policy) für jeglichen Java-Code zu definieren, nicht nur für Applets aus dem Internet. Diese Sicherheitsvorschriften sind in der Policy-Datenbank abgelegt, ihre Einhaltung wird von der Klasse AccessController überprüft, die den SecurityManager aus Java 1.1 ablöst. Policies und Permissions
Nitty Gritty • Take that!
10
Eine Policy besteht aus einer Reihe von Permission-Objekten, die Zugriffsrechte für Ressourcen repräsentieren. Beispiele wären Lese-/ Schreibzugriff auf eine bestimmte Datei auf dem lokalen Rechner (siehe Abschnitt 7.1.14) oder Netzwerkverbindungen zu einem bestimmten Rechner (Host + Portnummer, siehe Abschnitt 7.2.6). Man kann dies mit der Zugriffskontrolle in Netzwerkbetriebssystemen wie Windows NT/2000/XP oder Linux vergleichen, nur dass in Java die Rechte nicht einem Benutzer oder einer Benutzergruppe eingeräumt werden, sondern einem oder mehreren Websites (z. B. die »lokale Intranet-Zone« im Internet Explorer). Eine Policy gibt beispielsweise an, welches Maß an Zugriff Java-Code von einem Ursprung (z. B. Webserver) erhält. Zertifikate Um das Zertifizierungsproblem zu lösen, nämlich dass jeder, der eine digitale Unterschrift leisten will, eine Art anerkannten Ausweis besitzen muss, wird nun der X.509 V3 Standard unterstützt, mit dem über eine Hierarchie von Zertifizierungsbehörden (CAs) die Erstellung von digitalen Signaturen erleichtert wird. Die Klassen hierzu sind im Package java.security.cert enthalten. 390
Verschlüsselung – java.security
Sandini Bib
Protection Domain Die wichtigste Erweiterung ist eine feingranulare Berechtigungsvergabe für Code aus beliebiger Quelle. Sobald der Code ausgeführt wird, wird eine abgesicherte Laufzeitumgebung erzeugt, die man als Protection Domain bezeichnet. Dieser Code kann Requests für den Zugriff auf bestimmte Ressourcen enthalten. Anhand der Quelle (URL) und den möglicherweise mitgeführten Zertifikaten wird durch Vergleich mit den lokalen Policies entschieden, ob ein Request erlaubt ist. Falls nicht, wird dem Programm dies durch eine Exception mitgeteilt.
Nitty Gritty • Take that!
10
Weitere wichtige Bibliotheken
391
Sandini Bib
Teil III – Go ahead!
Sandini Bib
TEIL III
GO AHEAD!
Sandini Bib
Der dritte und letzte Teil dieses Buches gibt Ratschläge, welche Probleme Sie mit welchen Technologien lösen können, und Tipps zum praktischen Einsatz der Sprache und der Bibliotheken. In Kapitel 11 finden Sie einen Leitfaden zur Fehlerbehandlung in Java. Kapitel 12 ist dem Aspekt gewidmet, wie Sie Ihre Anwendungen beschleunigen können, und das 13. und letzte Kapitel fasst verschiedene Architekturen zusammen und gibt Ihnen Tipps, welche Technologie für welchen Zweck am besten geeignet ist. Auf www.nitty-gritty.de steht zusätzlich ein Kapitel zu den Themen Multithreading und Synchronisation bereit.
Sandini Bib
11
Exception Handling
11.1 Einführung Exception Handling (Ausnahmebehandlung) ist ein Sprachkonzept, welches aus C++ übernommen wurde. Die Ausnahmebehandlung ist ein sehr wirkungsvolles und einfaches Konzept zur Behandlung von Laufzeitfehlern. An der Fehlerstelle wird eine Exception »geworfen« (siehe Abbildung 11.1), die von einem dafür definierten Handler »aufgefangen« und weiterverarbeitet wird. Als »Werfen« bezeichnet man dabei die Übergabe eines speziell erzeugten Exception-Objektes mit Informationen über den Fehler an eine Fehlerbehandlungsroutine.
:KritischeKlasse
:ExceptionHandler
ruft Methode mit ungültigen Daten auf Fehler tritt auf; Exception wird erzeugt. <>
11
e: Exception
<>
Fehlerbehandlung
Bild 11.1: Fehlerbehandlung mittels Exceptions
Hintergrund dieses Mechanismus ist folgender: Ein Softwareentwickler, der z. B. eine Klassenbibliothek implementiert, weiß, wann Fehler auftreten. Dagegen weiß er nicht, wie der Anwender seiner Klassenbibliothek auf diesen Fehler reagieren will. Umgekehrt weiß der Benutzer der Bibliothek sehr wohl, wie er auf Fehler reagieren will, ihm fehlt jedoch der Überblick, an welchen Stellen welche Fehler auftreten können.
Exception Handling
395
Nitty Gritty • Go ahead!
Exception wird geworfen.
Sandini Bib
Die Lösung ist mit dem Mechanismus der Ausnahmebehandlung einfach: Der Modulentwickler erzeugt an den Fehlerstellen Exceptions, der Benutzer kann diese Exceptions durch eigene Handlerfunktionen weiterverarbeiten.
11.2 Vergleich mit Returncodes Bei traditionellen Programmiersprachen benutzt man in der Regel die Rückgabewerte von Funktionen als Returncodes. Wenn beispielsweise ein Funktionsaufruf einen Wert ungleich null zurückliefert, ist dort ein Fehler aufgetreten. Der Wert dient als Schlüssel. Leider hat sich in der Praxis gezeigt, dass viele Entwickler der Einfachheit halber entweder den Fehlercode direkt an den Endbenutzer weitermelden ("Error 4711 has occured") oder alle möglichen Returncodes eines Funktionsaufrufs zu einem einzigen zusammenfassen und ihrerseits weitermelden. Dadurch gehen aber viele Informationen über den wirklichen Grund des Fehlers verloren. Insbesondere hat derjenige, bei dem der Fehler letztendlich in Erscheinung tritt, meist keine Dokumentation, die die Fehlernummern in eine Beschreibung umsetzt.
Nitty Gritty • Go ahead!
11
11.3 Realisierung in Java Aus diesem Grund setzt man Exceptions ein, die schon anhand ihres Namens einen Anhaltspunkt für den Fehler liefern und daneben mit einer Detailbeschreibung und einem Call-Stack-Auszug Aufschluss über die Art und die Entstehungsgeschichte des Fehlers liefern. Exceptions sind Java-Objekte, die von ihren Superklassen (z.B. java.lang.Exception, siehe Abschnitt 6.1.1) diese Eigenschaften geerbt haben. 11.3.1 try-catch-Konstrukt Der Benutzer einer fehlerkritischen Methode bettet den zu überwachenden Code in ein try {...}-Statement ein. Alles, was in diesem Block eingeschlossen ist, wird regulär abgearbeitet, wenn kein Fehler eintritt. Falls jedoch bei einer Anweisung im try-Block eine Ausnahme (Exception) vom normalen Ablauf eintritt, wird der Block sofort verlassen und nach folgenden catch-Anweisungen für die eingetretene Exception gesucht. 396
Vergleich mit Returncodes
Sandini Bib
Beispiel Exception
KontoLimitException
Bild 11.2: Beispiel einer Exception
Nehmen wir an, in der Methode setStand() unserer Klasse Konto (vgl. Abschnitt 4.4.2) könnte eine KontoLimitException auftreten. Direkt nach dem try-Statement wird ein Exception Handler für diesen Bereich definiert, der die KontoLimitException behandelt. public void abheben(double betrag) { // bis hier nicht überwachter Bereich try { // Dieser Bereich setStand(stand - betrag); // soll überwacht } // werden. catch (KontoLimitException e) { // Exception Handler: // Anwender der Methode reagiert selbst. System.out.println("Auszahlung nicht möglich."+ "Abhebung übersteigt Kontolimit"); } // Fortsetzung des nicht überwachten Bereichs }
Möglicherweise sind bei Ihren Experimenten mit Java schon einige Exceptions aufgetreten, die vom Laufzeitsystem gemeldet wurden. Wenn Sie die fehlerhafte Anwendung gestartet haben, haben Sie auf der Kommandozeile »Uncaught exception« gesehen, das heißt eine Ausnahme, die nicht mit catch behandelt wurde. Dieser Ausgabe folgt der so genannte Stacktrace, der angibt welche Methoden aufgerufen wurden, bis es zum Fehler kam. Exception Handling
397
Nitty Gritty • Go ahead!
11
Sandini Bib
Versuchen wir nun das fehlende Exception Handling einzufügen: Beispiel Eine Mini-Anwendung, die auf eine nicht initialisierte Referenz zugreift: public class RefErrorApp { static Object o; // = null! public static void main(String[] args) { o.toString(); // Fehler -> kein Objekt vorhanden System.out.println("Erfolgreiches Anwendungsende."); } }
Der Compiler kann das Laufzeitproblem nicht erkennen und erzeugt eine Anwendung, die nach dem Aufruf eine nicht-behandelte NullPointerException erzeugt: F:\temp>java RefErrorApp Exception in thread "main" java.lang.NullPointerException at RefErrorApp.main(RefErrorApp.java:6)
Nitty Gritty • Go ahead!
11
Damit wird die Meldung »Erfolgreiches Anwendungsende« nie ausgegeben. Den Fehler können Sie per try-catch-Konstrukt behandeln: Beispiel public class RefErrorApp { static Object o; // = null! public static void main(String[] args) { try { o.toString(); // Fehler -> kein Objekt vorhanden } catch (NullPointerException e) { System.out.println("Es wurde versucht, auf " + "eine nicht initialisierte Referenz " + "zuzugreifen.");
398
Realisierung in Java
Sandini Bib } System.out.println("Erfolgreiches Anwendungsende."); } }
Mit dieser Modifikation läuft das Programm erfolgreich ab und gibt beide Meldungen aus. Obwohl das Exception Handling meist für die Fehlerbehandlung benutzt wird, muss eine aufgetretene Ausnahme nicht unbedingt einen Fehler bedeuten. Es handelt sich eigentlich nur um einen nicht-regulären Ablauf, also eine Ausnahme von der Regel. Ein Beispiel ist das zeilenweise Einlesen einer Datei. Der Normalfall wäre, dass das Lesen der nächsten Zeile erfolgreich abläuft. Falls dies nicht möglich ist, weil das Dateiende erreicht ist, ist das kein Fehler, sondern eine Ausnahme (EOFExcecption), die man einfach durch Beenden des Einlesens behandelt. Mehrere Exceptions Nach einem try-Block können auch mehrere catch-Blöcke folgen: Beispiel
catch (IOException e) { [...] } // Exception Handler 2 catch (Exception e) { [...] }
// Restliche Exceptions abfangen
Falls eine Ausnahme auftritt, wird das Exception-Objekt nach seinem Typ von oben nach unten abgeprüft. Falls es einer Klasse anhört (direkt oder durch Vererbung), wird der entsprechende Exception Handler aufgerufen. Danach gilt die Exception als behandelt, und es wird die nächste Anweisung nach dem letzten catch-Block ausgeführt.
Exception Handling
399
Nitty Gritty • Go ahead!
11
try { // Dieser Bereich [...] // soll überwacht } // werden catch (NullPointerException e) { [...] } // Exception Handler 1
Sandini Bib
Das bedeutet im Beispiel, dass der zweite catch-Block nur geprüft wird, falls eine Exception aufgetreten ist, und es sich nicht um eine NullPointerException handelt. Der dritte Fall ist sehr allgemein gehalten und behandelt alle sonstigen Exceptions (also alle Klassen, die irgendwie von Exception abstammen, außer den zwei spezieller behandelten darüber). Diese abgestufte Ausnahmebehandlung vom Speziellen zum Allgemeinen kommt sehr häufig vor. Mit dieser Art der Trennung der Fehlerbehandlung vom Entstehungsort kann man deutlich besser lesbaren Code schreiben. Innerhalb eines try-Blockes formuliert man den Normalfall; sämtliche Unregelmäßigkeiten werden an dieser Stelle ausgeklammert. Derjenige, der den Quellcode später warten muss, kann nun den Regelfall schneller verstehen, da die oft 80 – 90% des Sourcecodes umfassenden Fehlerbehandlungen ausgelagert sind.
Nitty Gritty • Go ahead!
11
11.3.2 Pflichtcode – finally Es gibt bestimmte Aktionen, die ausgeführt werden müssen, egal ob der try-Block erfolgreich war, oder ob die Ausführung durch eine Exception unterbrochen wurde (Pflichtcode). Zum Beispiel sollten offene Dateien auf jeden Fall geschlossen werden, ob das Lesen nun erfolgreich war oder nicht. Um den Pflicht-Code nicht an mehreren Stellen schreiben zu müssen, gibt es den optionalen finally-Block. Beispiel try { // Dieser Bereich [...] // soll überwacht } // werden. catch (Exception e) { [...] } // Exception Handler finally { // Dieser Block muß auf jeden Fall [...] // ausgeführt werden. }
Code im finally-Block wird auf jedem Fall ausgeführt, egal, ob der tryBlock erfolgreich war oder nicht.
400
Realisierung in Java
Sandini Bib
11.3.3 Eigene Exceptions Da Ausnahmen nichts weiter als Objekte von Exception-Klassen sind, kann man auch eigene Typen definieren. Dazu muss man von einer bestehenden Exception ableiten: Beispiel public class KontoLimitException extends Exception { public KontoLimitException() {;} public KontoLimitException(String fehlermeldung) { super(fehlermeldung); } }
Die neue Klasse enthält keine weiteren Methoden, deutet aber aufgrund des Namens bereits die Art des Fehlers an. Eine Detailbeschreibung kann man optional mit dem zweiten Konstruktor hinzufügen. 11.3.4 throws und throw
Beispiel public void setStand(double betrag) throws KontoLimitException { if (betrag > 0) stand = betrag; else // Fehlermeldung throw new KontoLimitException( "Keine Überziehung erlaubt."); }
Exception Handling
401
11 Nitty Gritty • Go ahead!
Eine Methode, in der eine solche Ausnahme auftreten kann, sollte dies bereits im Header durch throws <Exceptionname> deklarieren. Damit wird sichergestellt, dass eine mögliche Exception mittels eines try-catch-Konstrukts behandelt werden muss. Ein neu erzeugtes Objekt der Exception-Klasse wird mit throw »geworfen«.
Sandini Bib
Es gibt einige Exceptions, deren Auftreten nicht mit throws angekündigt werden muss. Dazu gehören alle Ausnahmen vom Typ RuntimeException oder Error und von diesen abgeleitete Klassen. Damit wird vom Compiler bei der Benutzung der Methoden, in denen sie auftreten können, aber auch keine Behandlung der Ausnahmen angemahnt. Der Grund dafür ist, dass manche Fehler in fast jeder Situation auftreten können, zum Beispiel ein OutOfMemoryError oder eine NullPointerException (Subklasse von RuntimeException), so dass sie praktisch in jeder Methode mit throws angekündigt bzw. bei der Verwendung behandelt werden müssten. Re-Throw
Nitty Gritty • Go ahead!
11
Die Ausnahmebehandlung von Java basiert darauf, dass Fehler an der Stelle behandelt werden sollen, an der genug Wissen zur Behebung des Problems vorliegt. Das heißt, wenn eine Ausnahme nicht bei ihrer Entstehung – also in der gerufenen Methode – behandelt werden kann, wird sie mit throw an die rufende Methode weitergereicht. Kann sie aber auch von dieser im Augenblick nicht behandelt werden, sondern nur von einem noch weiter außen liegenden trycatch-Konstrukt, kann die Exception mit einem erneuten throw weitergereicht werden (so genanntes Re-Throw). 11.3.5 Throwable und Error Neben der Klasse Exception gibt es auch noch die Klasse Error (siehe Abschnitt 6.1.11). Beide stammen von der Superklasse Throwable ab (dies ist eine Klasse und kein Interface, trotz des Namens). Bei Errors handelt es sich um fatale Fehler, die im Allgemeinen ein sinnvolles Weiterlaufen der Anwendung unmöglich machen. Errors sind systembezogen, im Gegensatz dazu sind Exceptions anwendungs- oder datenbezogen. Ein Beispiel für einen Error ist Speichermangel (OutOfMemoryError), für eine Exception ein falscher Dateiname (FileNotFoundException).
402
Realisierung in Java
Sandini Bib
Throwable
<> +Throwable() +Throwable(msg : String) <<Methoden>> +getMessage() +printStackTrace() +toString() ...
Error
OutOfMemoryError
InterruptedException
NoClassDefFoundError
IllegalArgumentException
ClassNotFoundException RuntimeException
NullPointerException
NumberFormatException
11
SecurityException
ArrayIndexOutOfBoundsException
Bild 11.3: Exceptions und Errors in java.lang
Die Methoden wurden bereits in Abschnitt 6.1.11 vorgestellt. Die Methode printStackTrace() gibt einen Call-Stack-Auszug auf der Standardausgabe (oder einen anderen Stream) aus. Dieser Auszug stellt eine Aufzählung der Methodenaufrufe dar, die zum Fehler führten, und gibt damit Aufschluss über die Entstehungsgeschichte der Exception.
Exception Handling
403
Nitty Gritty • Go ahead!
InternalError
Exception
Sandini Bib
Exceptions in java.lang Eine Reihe von Exceptions sind im Package java.lang definiert. Einen kleinen Ausschnitt zeigt Abbildung 11.3. Viele andere Exceptions sind aber nicht in diesem Package enthalten, sondern in den Packages, in deren Klassen sie typischerweise auftreten, beispielsweise stehen die EOFException (Dateiende) und die FileNotFoundException im Package java.io (siehe Abschnitt 7.1.13 »IO-Exceptions«). Die Klassen sind nicht final, Sie können deshalb Ihre eigenen Exception-Klassen davon ableiten. Dies sollten Sie auch wirklich tun, denn anwendungsspezifische Exceptions sind individueller und können damit eindeutig zugeordnet und behandelt werden. Insbesondere durch Angabe von throws können Sie sogar die Behandlung der Fehler erzwingen. Schlechter Stil ist das Abfangen sämtlicher Exceptions in der main-Methode. Mit folgendem Codestück können Sie sich zwar der Mühe der Fehlerbehandlung entziehen, aber Sie bekommen im Fehlerfall keinerlei Rückmeldung – das Programm beendet sich einfach:
11 Nitty Gritty • Go ahead!
public static void main(String[] args) { // so nicht!!! try // Try-Block um sämtlichen Code { [ ]... // Aufgabe der Anwendung } catch (Throwable e) // nur allgemeinsten Fehlerfall // auffangen { ; } // sämtliche Fehler ignorieren // oder Standardfehlermeldung // ausgeben }
404
Realisierung in Java
Sandini Bib
In anderen Methoden ist eine solche Fehlerbehandlung sogar noch gravierender, da die Anwendung oder das Applet dann ohne Rücksicht auf den Fehlerzustand fortgesetzt wird.
11.4 Zusicherungen Seit Java 2 V1.4 gibt es ein neues Schlüsselwort: assert. Damit können so genannte Zusicherungen programmiert werden. Dies sind zum Beispiel Vor- und Nachbedingungen einer Methode, etwa dass alle übergebenen Parameter mit gültigen Werten belegt sind oder der Rückgabewert nicht null ist. Dies kann man zwar auch mit Exceptions absichern, die Prüfungen kosten allerdings etwas Performance und werden wohl zur Laufzeit nie benötigt. Zur Entwicklungszeit hingegen kann es schon einmal passieren, dass die eine oder andere Bedingung aufgrund von Programmier- und Designfehlern verletzt ist. Beispiel public String leerzeichenLoeschen(String str) { assert str != null; String ergebnis = str.trim(); assert ergebnis != null; return ergebnis; }
Die Prüfungen sollen sicherstellen, dass beim Aufruf der trimMethode keine NullPointerException auftreten kann und kein leeres Ergebnis zurückgeliefert wird (ein Leerstring "" ist hingegen erlaubt). Um das neue Schlüsselwort beim Kompilieren zu berücksichtigen, müssen Sie den Java-Compiler mit der Option -source 1.4 starten. Damit die Prüfung der Zusicherung im späteren Betrieb keine Performance kostet, sind die Prüfungen in der Laufzeitumgebung standardmäßig abgeschaltet. Wollen Sie sie im Testbetrieb aktivieren, rufen Sie den Java-Interpreter mit der Option -enableassertions (oder kurz -ea) auf.
Exception Handling
405
Nitty Gritty • Go ahead!
11
Sandini Bib
12
Sandini Bib
Tuning
Wie kann eine Java-Anwendung beschleunigt werden? Es gibt hier einerseits natürlich gute Algorithmen, aber auch verschiedene virtuelle Maschinen oder Techniken, um das Laden von Klassen über Netz zu vermeiden bzw. zu optimieren.
12.1 Einflussmöglichkeiten Algorithmen Nichts ist natürlich wichtiger als effiziente Algorithmen und Datenstrukturen. Es wird aber zunehmend schwieriger für den Entwickler, dies selbst zu beeinflussen. Denn bei den heutigen Programmgrößen und -komplexitäten ist man auf allerlei Bibliotheken angewiesen, deren interne Implementierung oft unbekannt ist. Auch die Entwickler der Java-Klassenbibliotheken haben dazugelernt. Früher waren viele Klassen auf »Nummer sicher« programmiert und stark synchronisiert ( thread-safe). Diese Sicherheit benötigt aber oft eine große Performance und deshalb wurde dieser Schutz bei Java 1.2 und 1.3 oft wieder herausgenommen bzw. er funktioniert nur auf explizite Aufforderung (Beispiel: Swing, Collection-Klassen).
Æ
Da die Speicherverwaltung in Java weitgehend dem Laufzeitsystem überlassen ist, sind hier die Hersteller der virtuellen Maschinen gefragt. Sie können allenfalls Referenzen auf nicht mehr benötigte Objekte frühzeitig auf null setzen, um der Speicherverwaltung zu signalisieren, dass die Objekte entfernt werden können. Seit Java 1.2 können Sie auch die Referenzbibliothek java.lang.ref (siehe 10.2) nutzen, um Objektcaches zu erzeugen, die den Zugriff auf Objekte erlauben, die zwar schon zur Löschung freigegeben, aber noch nicht entfernt wurden.
Tuning
407
Nitty Gritty • Go ahead!
Speicherverwaltung
12
Sandini Bib
Kommunikationsverbindung Hier sollten Sie darauf achten, dass Sie die zu übertragenden Datenmengen klein halten. Bei der Verwendung von RMI und allgemein bei serialisierten Daten (siehe Kapitel 7.1.5) sollten Sie nicht-notwendige oder leicht zu rekonstruierende Klassenattribute (Felder) nicht übertragen, sondern mit dem Modifier transient kennzeichnen. Die tatsächlich zu übertragenen Objekte können Sie z. B. mit den Klassen java. util.zip.ZipOutputStream bzw. java.util.zip.ZipInputStream komprimieren bzw. entpacken. Klassen laden und speichern Gerade bei Applets, bei denen die Klassen erst über ein Netz geladen werden, ist es wichtig, diese effizient zu übertragen und erst dann zu laden, wenn sie benötigt werden. Statt für jede Klasse eine Verbindung zum Webserver aufbauen zu müssen, ist es besser, diese in ein (komprimiertes) Java-Archiv zu packen (Abschnitt 4.10.5) und auf einmal zu laden.
Nitty Gritty • Go ahead!
12
Allerdings kann dies auch »nach hinten losgehen«, wenn nämlich das Archiv sehr viele Klassen und Ressourcen enthält, die vom Applet eigentlich gar nicht benötigt werden. Hier ist gezieltes Packaging nötig. (siehe Abschnitt 12.3) Wenn einzelne Funktionen in einer Anwendung selten gebraucht werden, sollten Sie die dazugehörigen Klassen erst mit Class.forName() dynamisch laden (siehe Abschnitt 6.1.3). Dann werden die Klassen nicht schon beim Laden des Applets bereitgestellt, sondern erst, wenn Sie tatsächlich benötigt werden ( schnellere Startup-Zeit).
Æ
Regelmäßig benutzte Klassenbibliotheken sollten lokal vorhanden sein. Die Java-Standardbibliothek ist seit Java 1.0 stark angewachsen, viele frühere Erweiterungen sind nun standardmäßig in jeder Laufzeitumgebung vorhanden. Weitere Java-Extensions kann man im <jre>\lib\ext-Verzeichnis dauerhaft installieren. Neu bei der Version 1.3 ist, dass diese Extensions nun auch versioniert seien können. Ein Applet kann nun also erkennen, ob eine notwenige Extension bereits in einer geeigneten Version vorhanden ist oder nachgeladen werden muss. Die Benutzung dieser Funktionalität wird mit dem Tool WebStart von Sun vereinfacht. Hier kann z. B. ein Applet genau definie-
408
Einflussmöglichkeiten
Sandini Bib
ren, welche Erweiterungen in welcher Version benötigt werden und diese, falls nicht vorhanden, von einem angegebenen Server laden und für die Zukunft lokal cachen. Multithreading Der Einsatz mehrerer Threads (siehe Online-Kapitel auf www.nittygritty.de) beschleunigt Anwendungen oft deutlich, da Wartezeiten effizient genutzt werden können. Bei allen dialog-orientierten Anwendungen wie Applets, Servlets oder grafischen Oberflächen muss häufig auf Benutzereingaben gewartet werden, bei Serveranwendungen sind oft viele Sitzungen offen, aber nur wenige tatsächlich gleichzeitig aktiv. Datenbankzugriffe In diesem Bereich gibt es verschieden Möglichkeiten wie Caching oder Pre-Fetching (Vorabladen von vermutlichen Folgedaten eines Zugriffs) von Daten, die nicht direkt mit Java zu tun haben, deren Einsatz aber für eine gute Performance wichtig ist. Den zeitaufwendigen Auf- und Abbau von Verbindungen zu einem Datenbankserver kann man durch die Nutzung von Connection Pools vermeiden. Java Applikationsserver und Java-Persistenzframeworks bieten in diesem Bereich gute Unterstützung.
12
Schließlich ist auch die virtuelle Maschine ein wichtiger Faktor, der in Abschnitt 12.4 beschrieben ist.
12.2 Garbage Collection In Java sorgt der so genannte Garbage Collector für die Speicherbereinigung. Wenn die letzte Referenz auf ein Objekt verschwindet, kann er dieses selbsttätig löschen. Man kann Referenzen auch explizit auf null setzen, um eine vorzeitige Garbage Collection zu ermöglichen. Es stellen sich einige Fragen: T
Wann verschwindet eine Referenz?
Tuning
409
Nitty Gritty • Go ahead!
Laufzeitumgebung
Sandini Bib
T
T
Eine Referenz, die in einem Block ( {...} ) definiert ist, zum Beispiel eine lokale Variable, verliert beim Erreichen des Blockendes ihre Gültigkeit.
T
Eine Referenz als Instanzvariable wird ungültig, wenn das Objekt gelöscht wird.
Wann wird der Speicher freigegeben? T
T
Das entscheidet der Garbage Collector, der parallel zur Anwendung abläuft. Er wird meist dann aktiv, wenn das Programm gerade nichts zu tun hat (zum Beispiel, wenn es auf ein Ereignis wartet).
Was ist, wenn sich zwei oder mehrere Objekte gegenseitig referenzieren? T
Der Garbage Collector erkennt so genannte zirkuläre Referenzen. Wenn die letzte Referenz auf diese »Insel« verschwindet, kann der Garbage Collector alle Objekte löschen. In Abbildung 12.1 kann die Löschung genau dann stattfinden, wenn die Referenz liste gelöscht wird.
list:VerketteListe
12 Nitty Gritty • Go ahead!
o1:ListenElement naechstes = daten = ... voriges = null
o2:ListenElement naechstes = daten = ... voriges =
o3:ListenElement naechstes = null daten = ... voriges =
Bild 12.1: Zirkuläre Referenzen
12.3 Packaging Ziel ist es, eine Zusammenstellung von Klassen zu finden, die von der Anwendung mit höchster Wahrscheinlichkeit benötigt werden, und diese komprimiert zu übertragen. Bei lokal installierten Applikationen ist dies nicht so kritisch, da Speicherplatz auf der Festplatte billig und es deshalb egal ist, ob einige 410
Packaging
Sandini Bib
Kilo- oder Megabyte an Class-Dateien und Ressourcen gespeichert werden, die zur Laufzeit nicht benötigt werden. Bei Applets kommt es aber darauf an, nur wirklich diejenigen Daten über das Inter- oder Intranet zu übertragen, die tatsächlich notwendig sind. Java bietet hierfür sehr gute Möglichkeiten an, denn der ClassLoader lädt tatsächlich nur die benötigten Klassen. Sie können deshalb einfach alle möglicherweise benötigten Klassen (als Bytecode) auf dem Webserver zugänglich machen. Allerdings hat diese Vorgehensweise zwei Nachteile: T
Pro Klasse muss eine Netzwerkverbindung aufgebaut werden, was bei mehreren hundert bis tausend Klassen sehr viel Performance kostet, da viel Protokolloverhead erzeugt wird.
T
Sie können keine signierten Applets einsetzen (siehe Abschnitt 9.3.2), da einzelne Klassen nicht signiert werden können.
Deshalb sollten Sie die Klassen und Ressourcen in Form eine JavaArchivs (jar-Datei, siehe Abschnitt 4.10.5) als ein einziges, komprimiertes Paket bereitstellen. Dieses können Sie dann auch digital signieren.
Der optimale Mittelweg sieht so aus: T
Das Archiv (oder die Archive) enthalten die Klassen, die unbedingt und in jedem Anwendungsfall benötigt werden.
T
Alle anderen Klassen werden auf dem Server bereitgestellt und bei Bedarf per dynamischem Classloading geladen (siehe 6.1.3 und Abschnitt 12.1).
Die Anwendung sollte schon in Hinblick auf diese Architektur entworfen werden. Stellen Sie sich die Frage, welche Funktionen der Benutzer praktisch immer benutzt, und welche nur selten oder von speziellen Nutzergruppen benötigt werden.
Tuning
411
12 Nitty Gritty • Go ahead!
Allerdings besteht nun umgekehrt die Gefahr, dass dieses Archiv viele Klassen enthält, die gar nicht genutzt und somit überflüssigerweise übertragen werden.
Sandini Bib
In Abschnitt 4.10.5 wird die Erzeugung von Java-Archiven und das enthaltene Manifest näher erläutert. In diesem Manifest können seit Java 1.3 auch die Informationen für die benötigten Extensions stehen (siehe Abschnitt 12.1). Informationen über das Signieren von Archiven finden Sie in den Abschnitten 9.3.2 und 10.5 sowie auf der Java-Security-Homepage von Sun (http://java.sun.com/security/index.html). Zu ladende Java-Archive werden im Applet-Tag einer HTML-Seite angegeben (siehe Abschnitt 9.4): <APPLET CODE="HomeBanking.class" ARCHIVE="Banking.jar" WIDTH=300 HEIGHT=100>
(Lokal installierte) Applikationen können Klassen aus Java-Archiven nutzen, die in der CLASSPATH-Variable explizit angeben sind (nur das Verzeichnis, das das jar-File enthält, reicht nicht!). Seit JRE 1.2 können Sie beim Aufruf des Java-Interpreters java mit der Option -jar ein Archiv angeben.
Nitty Gritty • Go ahead!
12
12.4 Laufzeitumgebung Schließlich gibt es neben der Optimierung auf der Seite der Anwendungsentwicklung auch Ansätze, die Laufzeitumgebung zu beschleunigen. Effiziente virtuelle Maschinen Die Hersteller versuchen immer effizientere virtuelle Maschinen zu entwickeln. Im Windows-Bereich wetteifern gleich mehrere kostenlosen Java VMs um die Gunst des Kunden, zum Beispiel von Sun, IBM und Microsoft (um nur die Größten zu nennen). Kompilierung zur Laufzeit So genannte Just-In-Time-Compiler (JIT) wandeln den Bytecode nach dem Herunterladen des Applets oder dem Start einer Anwendung in Maschinencode um und führen diesen aus. JIT-Compiler sind heutzutage in fast allen Laufzeitumgebungen und Webbrowsern enthalten.
412
Laufzeitumgebung
Sandini Bib
HotSpot: Analyse und Kompilierung zur Laufzeit Die virtuelle Maschine HotSpot von Sun analysiert den Code zur Laufzeit und konzentriert die Kompilation und Optimierung auf besonders häufig benutzte Methoden. Zur Optimierung stehen zusätzliche Informationen aus dem realen Ablaufverhalten zur Verfügung. HotSpot steht seit JRE 1.2 in einer Servervariante zu Verfügung und ist seit JRE 1.3 als für Clientvariante die Standard-Virtuelle-Maschine von Sun. Kompilierung zur Entwicklungszeit Wie in anderen Programmiersprachen auch, gibt es für Java NativeCompiler, die plattformspezifischen Maschinencode herstellen. Diese sind in Entwicklungsumgebungen (z. B. IBM VisualAge for Java Enterprise Edition, WebGain Visual Café) erhältlich. Dies ist allerdings der letzte Ausweg und in der Regel auch nur für Serverumgebungen sinnvoll, da einer der wichtigsten Vorteile von Java, nämlich die Plattformunabhängigkeit, bei einer Native-Kompilation verloren geht.
Nitty Gritty • Go ahead!
12
Tuning
413
Sandini Bib
13
Sandini Bib
Architekturen
13.1 Übersicht Die erste grundsätzliche Frage, die Sie sich stellen sollten, ist, ob die Anwendung für sich allein abläuft (Stand-Alone) oder mit einem Server Daten austauscht. Stand-Alone-Anwendungen Hier gibt es zwei Varianten: Eine Java-Applikation setzt eine lokal installierte Java-Laufzeitumgebung voraus. Wenn Sie diese selbst unter Kontrolle haben (z. B. im Intranet eines Unternehmens), haben Sie freie Wahl bei der Auswahl der Java-Version und können sämtliche Funktionen uneingeschränkt nutzen. Allerdings muss die Anwendung zuerst installiert und gegebenenfalls konfiguriert werden (z. B. CLASSPATH setzen).
T
Ein Applet läuft im Browser ab (siehe Kapitel 9). Sie brauchen es nicht zu installieren oder sich um neue Versionen kümmern, da bei jedem Aufruf das Applet wieder neu vom Server geladen wird. Der Nachteil ist, dass zum einen die Browser oft eine veraltete virtuelle Maschine mitliefern (Microsoft Internet Explorer 4/5, Netscape Communicator 4), so dass man sich z. B. in der Gestaltung der Oberfläche stark einschränken muss oder lange Downloadzeiten riskiert. Zum anderen unterliegen Applets Sicherheitsbeschränkungen (siehe Abschnitt 9.3.1), die sich nur durch aufwendiges Signieren aufheben lassen.
Servlets Servlets sind Java-Anwendungen, die rein auf dem Webserver ablaufen und dynamisches HTML generieren (ähnlich CGI-Skripten). Die notwendigen Klassenbibliotheken sind Bestandteil der Java 2 Enterprise Edition (siehe 5.2.3). Diese vermeiden die Sicherheits- und Sprachversionsnachteile der Applets, können aber nur Dokumente (in der Regel HTML-Seiten) zum Client schicken. Das bedeutet, es besteht keine stehende Verbindung und jede Änderung resultiert in einer neu generierten Webseite. Architekturen
415
13 Nitty Gritty • Go ahead!
T
Sandini Bib
Kommunikation Eine verbindungsorientierte Kommunikation haben Sie, wenn zwei Anwendungen auf Client- und Serverseite über ein Netzwerk Daten austauschen. In Java stehen Sockets, Datagramme, RMI und CORBA (bzw. teilweise Enterprise JavaBeans) zur Wahl. Diese Technologien sind in Kapitel 7 bzw. auf den den Nitty-Gritty-Webseiten beschrieben. Integration von Enterprise-Systemen Auf der Serverseite besteht oft das Problem, dass sehr viele nicht objektorientierte Backendsysteme eingebunden werden müssen (z. B. relationale Datenbanken, oder Transaktionssysteme). Eine Kapselung dieser Systeme ist besonders gut mit Enterprise JavaBeans möglich (siehe Online-Kapitel). Kombination von Technologien Oft ist eine Kombination verschiedener Technologien die Lösung. Zum Beispiel können HTML-Clients über Servlets mit Enterprise JavaBeans kommunizieren.
13.2 K.O.-Kriterien
Nitty Gritty • Go ahead!
13
Breites Publikum Ihre Anwendung soll von jedermann im Internet genutzt werden können und HTML reicht für Steuerung nicht aus.
Æ Meiden Sie neue Sprachfunktionen (aus Java 1.2, 1.3 oder 1.4)!
Begründung: Microsoft Internet Explorer 4/5 und Netscape Communicator 4 unterstützen nur Java 1.1. Das Java Plug-In ist zwar eine Lösung, die aber oft auf administrative Problem stößt. Restriktive Firewalls Die Firewall lässt nur HTTP-Verkehr durch, alle anderen Ports sind gesperrt.
Æ Meiden Sie Sockets, RMI und CORBA!
Begründung: Diese Technologien setzen auf TCP/IP auf und benutzen andere Ports. Überlegen Sie, ob Servlets eine Lösung wären. 416
K.O.-Kriterien
Sandini Bib
Komplexe grafische Oberfläche Sie haben eine aufwendige Bedienerschnittstelle mit Baumansicht, Drag & Drop usw.
Æ Meiden Sie Servlets!
Begründung: HTML bietet diese Funktionalität nicht. Wenig Speicher Die Hardwareausstattung der Rechner ist schlecht.
Æ Meiden Sie Swing, CORBA, Enterprise JavaBeans! Begründung: Diese Technologien sind sehr speicherintensiv. Hohe Skalierbarkeit, Ausfallsicherheit Sie haben sehr viele Requests. Die Server laufen als Cluster oder Multiprozessormaschinen. Die Clients sollen auch beim Ausfall eines Servers ohne Umstellung weiter laufen.
Æ Meiden Sie Sockets und RMI!
13 Nitty Gritty • Go ahead!
Begründung: Diese Technologien sind nicht ausgelegt auf wechselnde Server. Alle Dienste zur Lastverteilung, Lokalisierung und Sicherung müssten selbst implementiert werden. Nutzen Sie CORBA oder Enterprise JavaBeans.
Architekturen
417
Sandini Bib
A
Sandini Bib
Anhang
Im Anhang finden Sie eine Übersicht über die im Buch benutzten UML-Symbole. UML-Symbole In den folgenden Abbildungen werden die Symbole erläutert, die im Buch zur Darstellung von Klassen, Objekten, Relationen und Interaktionen verwendet werden. Als Notation wurde die Unified Modeling Language (UML) von Booch, Jacobson und Rumbaugh benutzt. KlassenName
Klasse
KlassenName {abstract}
Abstrakte Klasse
«interface» KlassenName
Interface
A
Klasse mit Variablen und Methoden KlassenName
Nitty Gritty • Start up!
+ publicVariable: Typ # protectedVariable: Typ - privateVariable: Typ + klassenVariable: Typ + publicMethode(parameter: Typ) # protectedMethode() - privateMethode() + klassenMethode() Superklasse
Vererbung (extends)
Subklasse
«interface» InterfaceName
Interface-Implementierung (implements)
ImplKlasse objektName: KlassenName : KlassenName
Objekt (Instanz) Objekt ohne Angabe eines Namens Objekt mit Variablenbelegung
k1:Konto nummer = 64246712 stand = 23456.98
Bild A.1: UML-Notation für Klassendiagramme Anhang
419
Sandini Bib
objektName: KlassenName
Objekt (Instanz) Objekt aktiv (z.B. Durchlauf einer Methode) Objekt inaktiv (Wartezustand)
:KlassenName <>
:KlassenName
Objekterzeugung
Objektlöschung methode()
Methodenaufrufe
methode()
Die ist eine Erläuterung
Bild A.2: UML-Notation für Interaktionsdiagramme
Nitty Gritty • Start up!
A
Kommentar
420
Sandini Bib
Stichwortverzeichnis Nummerisch
2D-Grafik 222276277278289
A abstract 77 Abstract Windowing Toolkit 221225 AbstractListModel 310325 AbstractTableModel 310329331 AccessController 390 ActionEvent 234252267269
312342
ActionListener 267269 Adapter (Ereignisbehandlung) 266
274
Adjustable 239321 AdjustmentEvent 269 AdjustmentListener 269321 Anonyme innere Klassen 69 Anti-Aliasing siehe Graphics2D Applet 212242194201225
292349350354411415
AppletContext 355 AppletResourceConnection 203 Appletviewer 30 Applikation 350410415 Argument 107 Array 92103371 ArrayList 375377 AudioFileReader 183 AudioFileWriter 183 AudioInputStream 183 Ausnahmebehandlung 395 Authenticators 207 AWT 221223224225229 AWT-Beans 229
B BeanInfo 387 Bedienoberflächen 222 Bedingte Anweisungen 116
Bedingte Zuweisung 119 BigDecimal 152158 BigInteger 152158 Bild 223276278288 Bildverarbeitung 288 BindException 194206 Bitmap 223278288 BitSet 374 Block 111 Book 292294295 Boolean 151 Boolesche Werte 94 Border 299301 BorderLayout 254310343 BoundedRangeModel 321 BoxLayout 253257 break-Anweisung 114 BufferedInputStream 167182 BufferedOutputStream 177182 BufferedReader 168169182 BufferedWriter 182 Button 229234311312 ButtonGroup 313314 ButtonModel 313 Byte 151 Bytecode 20411
C
Calendar 365367368 CallableStatement 215216 CardLayout 264 CaretEvent 270 CaretListener 270 Casting 93 CellEditorListener 270 ChangeEvent 270321 ChangeListener 270 Character 151152 Checkbox 230235313 CheckboxGroup 230233236 CheckboxMenuItem 233252
Stichwortverzeichnis
421
Sandini Bib CheckedInputStream 183 CheckedOutputStream 183 Choice 231241326 Class 37144145146147 ClassLoader 157 ClassNotFoundException 173187 CLASSPATH 2932119127412
415
Client/Server 191 Cloneable 141142144171 CloneNotSupportedException 141 Collection-Klassen 363370375
376382384
Color 280 ComboBox 326 ComboBoxEditor 328 ComboBoxModel 328 Comparator 375380383 Component 227229298 Connection 212213 ConnectionException 194 Constraints 253 Container 228243249298335
338
ContentConnection 205 continue-Anweisung 115 Controller 297 CORBA 194208416417 Cursor 286288 Customizing (von JavaBeans) 387
D Datagram 198 Datagramm 195196197416 DatagramPacket 196 DatagramSocket 196198 DataInput 180 DataInputStream 159167182192 DataOutput 180 DataOutputStream 159177182
192 Date 364367 DateFormat 164366 Datei 162163165183 Dateiauswahldialog 248 422
Stichwortverzeichnis
Datenbank 416 Datum 364 DefaultComboBoxEditor 328 Default-Konstruktor 66 DefaultListModel 310325 DefaultListSelectionModel 325 DefaultMutableTreeNode 310333 DefaultTableModel 310329331 DefaultTreeModel 310332334 Default-Zugriffsrecht 70 Delegate 297 Dialog 232245247248335 Dialogbox 245346 Dictionary 373 Digitale Unterschrift 358 Dimension 283 do...while-Schleife 113 Document 318 DocumentEvent 270 DocumentListener 270 Double 151 Drag & Drop 223 DriverManager 212 Drop-down-Liste 241 Drucken 222287290 dynamic Classloading 147 dynamisches Nachladen von Klassen 146147408
E
EditorKit 318320 Ein-/Ausgabe 159 Eingabefokus 229 eingebettete Klasse 68 EJB 2123208209416417 Elementare Datentypen 9192107 Ellipse zeichnen siehe Graphics Enterprise Edition 134 Enterprise JavaBeans 23135194
208209416417
Entwicklungsumgebung 9 Entwurfsmuster 297 Enumeration 372383 EOFException 187206 equals 139140
Sandini Bib Ereignis 222226266268269
271275387
Ereignisbehandlung 222266271
274275
Error 157402 Event 222266268269271
275387
Event Handling 266271275 Exception 157395396401402 Exception Handling 395 Extension 408
FontMetrics 281 Formatierung von Zeit- und Datumswerten 366 for-Schleife 113 Fortschrittsanzeige 321 Frame 231245246247252
335
FtpURLConnection 203
G
Garbage Collection 6785153
409410
F
Farbe 223280 Farbverlauf 279280 Features 387 Fehlerbehandlung 157 Feld 375657103 Fenster 245298 File 159162163165 FileDialog 188232248249348 FileInputStream 159165167181
182
FilenameFilter 188 FileNotFoundException 166187 FileOutputStream 159175176
182
FilePermission 188 FileReader 168180181182 FileURLConnection 201203 FileWriter 180181182 FilterReader 168 FilterStreams 167 final 75 protected 76 finalize 67142 finally-Block 400 Fließkommatypen 94 Float 151 FlowLayout 256 FocusAdapter 274 FocusEvent 269 FocusListener 269274275 Fokus 229269 Font 281283
Garbage Collector 142 GeneralPath 285 GradientPaint 280 Graphics 277278290291 Graphics2D 278 GraphicsEnvironment 283 GridBagConstraints 261 GridBagLayout 260263 GridLayout 259 GUI-Editor 226
H
HashMap 375381 HashSet 375379 HashTable 381 Hashtable 373374375 Hauptfenster 245 Heavy-weight-Komponente 223298 High-Level-Stream 181 HTML 30 HTML-Seite 31219225360412
415
HttpURLConnection 201203204
I IconImage 290 IDE 33 if-Anweisung 116 Image 288 ImageObserver 288289 import 123 Aufbau von Packagenamen 124
Stichwortverzeichnis
423
Sandini Bib InetAddress 199 Initialisierung 65 Innere Klasse 68275 InputStream 167169192201 InputStreamReader 169 instanceof 145 Instanz 37848589 Instanziierung 84 Instanzvariable 37425772 Integer 151 Integertypen 93 Integrierte Entwicklungsumgebung 33226 Interface 467881 Internationalisierung 368 Internet Explorer 23225415416 InvalidClassException 187 IOException 166186194205 IP-Adresse 190199206 ItemEvent 235243269314 ItemListener 269 ItemSelectable 241 Iterator 372377
J
JApplet 308349354 jar 31126411 JarInputStream 183 JarOutputStream 183 jarsigner 31 JarURLConnection 204 java 29 Java 3D 136 Java Advanced Imaging 136 Java Cryptographic Architecture 389 Java Cryptography Extension 136
390
Java Development Kit 27 Java Enterprise Extensions 135 Java Foundation Classes 295 Java Media Framework 136 Java Messaging Service 135 Java Plug-In 23225416 Java Runtime Environment 2127 Java Server Pages 135220
424
Stichwortverzeichnis
Java Speech 136 Java VM siehe virtuelle Maschine java.applet 349 java.awt 222226 java.awt.color 223 java.awt.datatransfer 223226 java.awt.dnd 223 java.awt.event 222226266 java.awt.font 222282 java.awt.geom 222285 java.awt.im 223 java.awt.image 223288 java.awt.print 222292 java.beans 386 java.io 159 java.lang 137152404 java.lang.ref 89407 java.lang.reflect 386 java.math 152 java.net 189 java.security 388 java.sql 209210 java.text 186 java.util 363 java.util.zip 363 Java-Archiv 31126358360411 JavaBean 144386 javac 2528 javadoc 3155 javah 31 JavaHelp 136 javax.jms 135198 javax.mail 135 javax.servlet 135220 javax.servlet.http 220 javax.servlet.jsp 220 javax.sql 209 javax.swing 223295 javax.swing.border 223299 javax.swing.table 223 javax.swing.text 186223318 javax.swing.text.html 186 javax.swing.text.rtf 186 javax.swing.tree 223 JButton 302311312314
Sandini Bib JCheckBox 302313314 JCheckBoxMenuItem 309342 JColorChooser 348 JComboBox 304326328 JComponent 298 jdb 31 JDBC 209210211212 JDBC-ODBC-Bridge 211 JDesktopPane 308339347 JDialog 308335337 JDK 232732129 JEditorPane 306320 JFC 295 JFileChooser 348 JFrame 307335337347 Jini 136 JInternalFrame 307339347 JLabel 302315 JList 303310323324328 JMenu 309342 JMenuBar 308335340342 JMenuItem 309340342 JobAttributes 292 JOptionPane 307311339346 JPanel 306338 JPasswordField 302316318 JPopupMenu 309340342 JProgressBar 305321322 JRadioButton 302313314 JRadioButtonMenuItem 309342 JRE 212327225415 JScrollBar 303321 JScrollPane 306318339344 JSlider 303321323 JSP 220 JSplitPane 306339344 JTabbedPane 265306339345 JTable 305310329 JTextArea 303318319320344
345
JTextField 302316 JTextPane 306311318320 JToolBar 305343 JTree 304310332334 Just-In-Time-Compiler 412
K Kapselung 43 KeyboardAction 299 KeyEvent 269 KeyListener 269 keytool 31 Klappliste 326 Klasse 3756 Klasse siehe auch Referenz Klassendeklaration 56 Klassenmethode 4274 Klassenvariable 425872 Klonen 144 Kommentar 54 Komponente 386 Konstante 58 Konstruktor 386465 Kontrollelement 223227229298 Kontrollstrukturen 110 Kreisbogen zeichnen siehe Graphics
L
Label 230236315 LayoutManager 243250252
253254256257259260 264265266339343 Light-weight-Komponente 223298 LineNumberInputStream 167182 LineNumberReader 168182 Linienstile siehe Graphics2D LinkedList 375377 List (AWT) 230241243323 List (Collection) 370371375377 ListCellRenderer 326328 ListDataEvent 270 ListDataListener 270 Listener 226266269271273
275
ListModel 310325 ListSelectionEvent 270 ListSelectionListener 270 ListSelectionModel 325 Literal 53 Locale 365368
Stichwortverzeichnis
425
Sandini Bib Long 151 Look & Feel 224300 Low-Level-Stream 181
M Mailservice 135 MailToURLConnection 203 MalformedURLException 201206 Manifest 126412 Map 370375381 Math 149 Mauszeiger 228286288 MDI 339347 MediaTracker 289290 Mehrfachvererbung 46 Member 37 Menu 232251252 Menü 250340 MenuBar 232246250251 MenuContainer 251 MenuEvent 271 MenuItem 233251252 MenuListener 271 MessageDigest 389 Methode 385658 Methodenaufruf 879092 Methodenüberladung 42 Micro Edition 133 MidiFileReader 183 MidiFileWriter 183 MIME 204 Mnemonics 299342 Model 297 Model-View-Controller 297324 Modifier 5769 abstract 77 final 75 native 79 private 69 protected 70 public 70 static 72 synchronized 78 transient 80 volatile 79
426
Stichwortverzeichnis
MouseEvent 269 MouseListener 269 MouseMotionEvent 269 MouseMotionListener 269 Multi Document Interface 339 MulticastSocket 197198 Multiple Document Interface 347 Multithreading 78 MutableTreeNode 310332334 MVC 297
N Namenskonvention 63 Namensraum 120123 native 79 NetPermission 207 Netscape Communicator 22225
415416
Netzwerkzugriffe 189 NoRouteToHostException 194 Notizbuch-Darstellung 264345 NotSerializableException 171187 Nullreferenz 101 Number 150158
O
Object 4681138139141142
143
ObjectInputStream 172182192 ObjectOutputStream 173182192 Objekt 373940 Objektorientierung 937 Objektreferenz siehe Referenz Objektserialisierung 171 Operator 539599 ORB 208 OutputStream 174177192 OutputStreams 181 OverlayLayout 265
P
Package 121124 Aufbau von Packagenamen 121 Zugriffsrechte 125
Sandini Bib Packaging 408410 Pageable 293294 PageAttributes 292 PageFormat 292293 Paint 280 Panel 231249338 Parallele Ausführung 154 Parameter 8792101106361 Parameterübergabe 108361 an Applets 361 an Applikationen 106 an Methoden 87108 Passwort 233238318 Performance 407411 Permission 390 Pfad 165 PipedInputStream 182 PipedOutputStream 182 Point 283 Policy 206390 policytool 31 PopUp-Menü 250 PopupMenu 232251 Port 190 Portnummer 190200 PreparedStatement 215216 Printable 292293294 PrinterJob 292293295 PrintJob 291292 PrintStream 159178182 PrintWriter 179 private 69 Process 154 Properties 373387 PropertyChangeEvent 269 PropertyChangeListener 269 PropertyEditor 387 protected 70 Protection Domain 391 ProtocolException 206 public 70 PushbackInputStream 182 PushbackReader 182 Pushbutton 234311
R RandomAccessFile 184 Reader 168182 Rechteck zeichnen siehe Graphics Rectangle 283 Referenz 4185868789100
407409
Referenztypen 91100107 Reflection 147 Remote Method Invocation siehe RMI ResourceBundle 368369 ResultSet 212215216 Returncode 396 RMI 188194208408416417 rmic 31 Runnable 154155 Runtime 154
S Sandbox 356 Schaltfläche 234311 Schieberegler 321323 Schleifen 112 Schlüsselwörter 53 Schriftart 228281283287 Scrollbalken 239240321344 Scrollbar 231239240321344 ScrollPane 231 Secure Socket Extensions 136 Security 412 SecurityException 167187194
206292
SecurityManager 157390 Security-Policy 206 Selektor 62 Serialisierung 80171408 Serializable 171187 ServerSocket 191193195 Servlet 2123135219415 Servlet Engine 23220 Set 370375379 Shape 279285 Shift 98 Short 151
Stichwortverzeichnis
427
Sandini Bib Sicherheitseinschränkungen 356 Signature 389 Signierte Applets 194292358
388411
Socket 189190191195416417 SocketException 194206 SocketInputStream 183 SocketOutputStream 183 SocketPermission 206 SortedMap 375 SortedSet 375380 SQL 210 SQLException 214 Stack 374 Stand-Alone-Anwendung 415 Standard Edition 131 Standardausgabe 153 Standardeingabe 153 Statement 212214215 static 7276 Streams 160161191 StreamTokenizer 185186 StrictMath 150 String 102147148149 StringBuffer 147148 Style 318 Subklasse 43 Sun 27 super 8891 Superklasse 43 Swing 221223224269295
417
switch-Anweisung 117 synchronized 78112 System 152 System.out 178
T
TableColumn 305329 TableColumnModelEvent 270 TableColumnModelListener 270 TableModel 310329331 TableModelEvent 270 TableModelListener 270 Tastaturkürzel 299340342
428
Stichwortverzeichnis
TCP/IP 189 TextArea 230237239318345 TextEvent 269 TextField 230237316 TextListener 269 TexturePaint 280 this 8891 Thread 78154155 ThreadGroup 154155 ThreadLocal 154 Throwable 157402 throws und throw 401 TimeZone 368 Token 185 Tokenizer 183 Toolbar 343 Toolkit 283287288290 Tooltips 299301 transient 80408 TreeCellEditor 334 TreeExpansionEvent 270 TreeExpansionListener 270 TreeMap 375381 TreeModel 310332334 TreeModelEvent 270 TreeModelListener 270 TreeNode 310332 TreeSelectionEvent 270 TreeSelectionListener 270 TreeSet 375379 try-catch-Konstrukt 398 Tuning 407 Types (SQL) 218 Typkonvertierung 93
U Überschreiben von Methoden 45 UDP 196 UML 394469707484 UndoableEditEvent 271 UndoableEditListener 271 Unicode 95148168182 Unified Modelling Language siehe UML UnknownHostException 194206
Sandini Bib URL 31161200201 URLConnection 202203204205
V
Variable 5657 Vector 325370371372375
377
Vererbung 4347 Vergleic 109 Vergleich 98 Verzeichnis 163 View 297 virtuelle Maschine 171922407
412
volatile 79
W
Webbrowser 2122201349 WebStart 408 while-Schleife 112 Window 245 WindowEvent 246269 WindowListener 269 Wrapper-Klassen 110151 Writer 179182
Z Zeichen 95 Zeichnen 278279 Zeit 364 Zeitzone 367368 Zertifikat 390 ZipInputStream 183408 ZipOutputStream 183408 Zugriffmodifier 69 Zugriffsmethode 62 Zugriffsmodifier 6072 Zugriffsrecht 606972 Zuweisung 99 Zwischenablage 223226276287
Stichwortverzeichnis
429
Sandini Bib
Copyright Daten, Texte, Design und Grafiken dieses eBooks, sowie die eventuell angebotenen eBook-Zusatzdaten sind urheberrechtlich geschützt. Dieses eBook stellen wir lediglich als 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 anderen Websites, der Veränderung, des Weiterverkaufs und der Veröffentlichung bedarf der schriftlichen Genehmigung des Verlags. Bei Fragen zu diesem Thema wenden Sie sich bitte an: mailto:[email protected]
Zusatzdaten Möglicherweise liegt dem gedruckten Buch eine CD-ROM mit Zusatzdaten bei. Die Zurverfügungstellung dieser Daten auf der Website ist eine freiwillige Leistung des Verlags. Der Rechtsweg ist ausgeschlossen.
Hinweis Dieses und andere eBooks können Sie rund um die Uhr und legal auf unserer Website
(http://www.informit.de)
herunterladen