Berner Fachhochschule Hochschule für Technik und Informatik, HTI Fachbereich Elektro- und Kommunikationstechnik Labor für Technische Informatik Kurzeinführung in Java GUI-Programmierung & Event-Handling Applets Threads Ausnahmen Ein- und Ausgabe © 2008, HTI Biel E. FIROUZI Dateiname: Skript_Einf_Java_Teil_2 Erstellt am: 10. September 2009 Autor: E FIROUZI Version: 3.0 Inhaltsverzeichnis Inhaltsverzeichnis 4 GUI-Programmierung & Event-Handling 4.1 Struktur von GUI-Anwendungen 4.1.1 Hierarchie der Fenster: Fenster im Fenster 4.1.2 Klassenhierarchie von GUI-Bausteinen 4.1.3 Elementare Controls und ihre Einbindung 4.2.1 Anordnung der Komponenten 4.2 Layoutmanager 4.2.1 Anordnung der Komponenten 4.2.2 BorderLayout 4.2.3 FlowLayout 4.2.4 GridLayout 4.2.5 GridBagLayout 4.2.6 CardLayout 4.2.7 Schachtelung der Layouts 4.3 Ereignissteuerung 4.3.2 Typen von Ereignissen 4.3.3 Quellen von Ereignissen 4.3.4 Beobachter von Ereignisse 4.3.5 Adapter 4.4 Elementare Controls in Benutzeroberfläche 4.4.1 Zeichenflächen: Canvas 4.4.2 Schalter: Button 4.4.3 Auswahl 4.4.4 Checkbox und Radiobutton 4.4.5 Statischer Text 4.4.6 Textfelder 4.4.7 Rollbalken 4.4.8 Menüs in Java 5 Applets 5.1 Einführung 5.1.1 Die HTML-Seiten 5.1.2 Appletbeispiel 5.2 Funktionsweise von Applets 5.2.1 Speicherung von Applets 5.2.2 Sicherheit von Applets 5.3.1 Die PARAM- Funktion 5.3.1 Appletbeispiel für 5.4 Klänge und Bilder 5.4.1 Klänge 5.4.2 Bilder 6 Threads 0 Ausnahmen 7.1 Einführung 7.1.1 Programmbeispiel 7.2 Funktionsprinzip 7.2.1 Eine mögliche Implementierung der Ausnahmenbehandlung Seite 2 4 4 4 5 6 7 7 7 7 9 10 11 13 14 15 18 19 19 20 21 21 24 26 27 29 29 29 33 36 36 36 36 38 40 40 41 41 44 44 45 48 51 51 51 52 52 Inhaltsverzeichnis 7.3 Vorteile der Ausnahmebehandlung in Java 7.4 Klassen und Ausnahmen 7.4.1 Programmbeispiel 8 Ein- und Ausgabe 8.1 Eingabe in Java 8.1.1 Eingabe mit der byteorientierten Stream-Klassen 8.1.2 Eingabe mit der zeichenorientierten Reader-Klassen 8.1.3 Die Standard Eingabe 8.2 Ausgabe in Java 8.2.1 Ausgabe mit der byteorientierten Stream-Klassen 8.2.2 Ausgabe mit der zeichenorientierten Writer-Klassen 8.2.3 Die Standard-Ausgabe 8.3 Anwendungs-Beispiele 8.3.1 Byteweise Verarbeitung von Dateien 8.3.2 Blockweise Verarbeitung von Daten 8.3.3 Daten im Format für das Internet verarbeiten Seite 3 52 52 53 55 55 55 56 58 59 59 60 60 60 61 62 63 4 GUI-Programmierung & Event-Handling 4 GUI-Programmierung & Event-Handling Das JDK enthält seit der Version 1.0 als „Abstract Windows Toolkit“ in dem Package java.awt eine umfangreiche Sammlung von Klassen zur Programmierung von grafischen Anwendungen und Benutzeroberflächen (GUI = Graphical User Interface). Damit können sowohl für Applets in HTML-Seiten als auch für Anwendungen, die unter den jeweiligen Betriebssystemen laufen, Vektorgrafiken und die Interaktion von Anwendungen mit der GUI programmiert werden. Für grafische Benutzeroberflächen gibt es im JDK die üblichen Möglichkeiten: Menus, Maus, Steuerelemente und Dialoge. Bei Applets gibt es Einschränkungen, denn aus Sicherheitsgründen dürfen die in Web-Seiten eingebetteten Applikationen nicht auf lokale Daten zugreifen. Damit entfallen für Applets die Dialoge zum Anzeigen von Inhaltsverzeichnissen und zum Auswählen von Dateien. rIm Hinblick auf portable Anwendungen und die verschiedenen Grössen von Bildschirmen im Internet, wurden in Java mit den sog. Layout-Managern flexible Methoden zur variablen Gestaltung von Benutzeroberflächen bereitgestellt. 4.1 Struktur von GUI-Anwendungen Mit Java 2 kann man GUI-Anwendungen nicht nur auf das AWT aufsetzen, sondern auch das „SwingKlassensystem“ als Basis für Anwendungen benutzen. Beim Design von Swing wurde bei den elementaren Möglichkeiten auf weitgehende Kompatibilität zu Ideen und Struktur des AWT geachtet. In den folgenden Kapiteln werden nur Beispiele mit dem AWT-Klassensystem als Basis dargestellt. Die entsprechenden Swing Darstellungen befinden sich im Jobst-Buch. 4.1.1 Hierarchie der Fenster: Fenster im Fenster Die grafische Anwendung besteht aus einem Hauptfenster. Dies ist im Sinne von Java ein „Container“, der untergeordnete Fenster enthalten kann. Jeder dieser Container kann seinerseits wieder Container als Komponenten enthalten. Der einfachste Container in AWT ist ein „Panel“, der auch die Basis für „Applet“ bildet. Aber Panel können auch zur Montage von Controls oder anderer Komponenten in anderen Fenster als Komponenten eingetragen werden. Die Klasse „Window“ steht für selbstständige Fenster. Ein Window ist ein rechteckiges Stück auf dem Bildschirm ohne Rahmen und Titelzeile. Ein „Frame“ ist ein Window mit Rahmen und Titelzeile. Ein Frame kann eine Menüleiste enthalten und steht als Prototyp für GUI-Anwendungen zur Verfügung. Programmbeispiel import java.awt.*; public class MyFrame extends Frame { MyFrame (String Title) { super(Title); add(new Button("AWT Button")); setSize(300, 100); setVisible(true); } public static void main(String[] args){ new MyFrame("Java application with AWT"); } } Seite 4 4 GUI-Programmierung & Event-Handling Hinweis Im obigen Beispiel ist „MyFrame“ eine abgeleitete Klasse von Frame. Dies wird durch public class MyFrame extends Frame zum Ausdruck gemacht. Die Eigenschaften des Fensters werden im Konstruktor von MyFrame festgelegt. Das Fenster erhält durch den Aufruf vom Konstruktor der Mutterklasse Frame mit einem String einen Namen, welcher in seiner obersten Leiste erscheint. Die verschiedenen Komponenten der grafischen Benutzeroberflächen müssen systematisch erzeugt und mit der Methode public void add(Component oneComponent) zum Container hinzugefügt werden. Hierdurch wurde in diesem Beispiel der Schalter „AWT Button“ dem Hauptfenster zugeordnet. Die Grösse in Pixel und die Sichtbarkeit des Fensters können beziehungsweise mit den Methoden „setSize()“ und „setVisible()“ festgelegt werden. In der Methode „main()“ wird ein Objekt der Klasse „MyFrame“ erzeugt. Damit wird das Fenster auf dem Bildschirm sichtbar. Ausgabenmöglichkeit 4.1.2 Klassenhierarchie von GUI-Bausteinen Die verschiedenen Komponenten der grafischen Benutzeroberflächen werden durch eine Klassenhierarchie abgebildet, die ihre Wurzel in der abstrakten Klasse „java.awt.Component“ hat. Sie verfügen damit über alle Attribute und Methoden von „Component“, welche zur Beeinflussung der Darstellungsweise und zum Umgang mit Ereignissen zur Verfügung stehen: • Festlegung und Ermitteln von Position und Grösse: getSize, getX, getY, setSize, setLocation usw. • Festlegung und Ermitteln von grafischen Eigenschaften: getForeground, setBackground, setFont, getCursor usw. • Anzeigen und Neuzeichnen von Komponenten: setVisible, paint, rePaint usw. • Ereignisverarbeitung. Die abstrakte Klasse „Container“ ist die wichtigste Klasse von Component. Sie dient hauptsächlich zur Zusammenfügung der Steuerelemente in den Fenstern oder Dialogen, und stellt die folgenden Methoden zur Verfügung: • Hinzufügen einer Komponente zu einem Container: add() • Entfernen von Komponenten aus einem Container: remove, removeAll usw. • Festlegen und Ermitteln des aktuellen Layouts: setLayout, getLayout, doLayout usw. Die Klassenhierarchie der AWT-Controls ist in der nachfolgenden Abbildung dargestellt. Seite 5 4 GUI-Programmierung & Event-Handling java.awt.Component java.awt.Button java.awt.Canvas java.awt.Checkbox java.awt.Choice java.awt.Label java.awt.List java.awt.Scrollbar java.awt.TextComponent java.awt.TextArea java.awt.TextField jawa.awt.Container java.awt.ScrollPane java.awt.Panel java.awt.Applet java.awt.Window jawa.awt.Frame jawa.awt.Dialog jawa.awt.FileDialog 4.1.3 Elementare Controls und ihre Einbindung In Java haben sich bei Anwendungen für grafische Benutzeroberflächen eine Reihe von festen Steuerelementen durchgesetzt: Schalter, Rollbalken usw. Die folgende Tabelle enthält diese sog. Controls aus dem Package jawa.awt. Eine vollständige Beschreibung von diesen Controls befindet sich im Kapitel 4.4 Elementare Controls in Benutzeroberfläche. AWT Klassen Canvas Component Panel Choice Scrollbar Checkbox Button Label List TextComponent TextArea TextField Beschreibung Zeichenfläche für Programme Basis für Komponenten „Pinnwand“ als Container für Controls Auswahl aus einem Pop-Up- Fenster Rollbalken Checkbox und Radiobuttons Schalter Statischer Text Auswahl aus einer Liste von Möglichkeiten Oberbegriffe für Textfelder Textfeld mit mehreren Zeilen Textfeld mit einer Zeile Canvas ist eine Zeichenfläche, auf welcher Grafikausgaben mit den Elementen der Grafik-Programmierung java.awt.Graphics möglich sind. Damit kann entweder Text ausgeben, oder Linien und Figuren gezeichnet werden. Choice implementiert eine Pop-Up- Menü mit einer Liste von Strings, aus denen nur einer gleichzeitig ausgewählt werden kann. Mit der Klasse Scrollbar kann ein Rollbalken dargestellt werden, in welchem die Position des Schiebereglers einem int- Wert entspricht. Dieser Wert kann genauso eingestellt werden, wie die Abstufung der Bewegung des Schiebereglers. Seite 6 4 GUI-Programmierung & Event-Handling Die Klasse „Checkbox“ implementiert Checkboxen, welche aus einem Markierungsfeld, das selektiert oder nicht selektiert werden kann, und einem daneben angeordneten Text bestehen. Die Klasse „TextComponent“ ist die Basis für die Klassen „TextArea“ und „TextField“. Sie definiert die grundlegenden Methoden zum Umgang mit Textfeldern. Die Klasse „TextArea“ stellt ein Texteingabefenster bereit, das sich auch über mehrere Zeilen erstrecken kann. Es ist möglich, Textzeilen zu markieren oder zu löschen sowie einen neuen Text einzufügen. Die Klasse „TextField“ stellt ein einzeiliges Texteingabefeld zur Verfügung. AWT-Controls in einem Rahmen Choice Button CheckBox TextField CheckBox as radio buttons List ScrollBar 4.2 Layoutmanager GUI-Systeme mit starren Anordnungen ihrer Komponenten sind für Anwendungen im Internet nicht ausreichend: Es gibt verschiedene Betriebssysteme wie Macintosh, Microsoft-Windows oder X-Windows und alle Bildschirmgrössen sind denkbar. Java erlaubt einerseits feste Anordnungen von Komponenten, aber auch anderseits die Platzierung der Komponenten nach diversen Strategien. 4.2.1 Anordnung der Komponenten Fenster können vom Anwender verkleinert und vergrössert werden. Dies betrifft insbesondere auch alle Komponenten in den Fenstern. In Java wird durch die sog. Layout Manager Abhilfe geschafft. Jeder Container hat seinen eigenen Layoutmanager, welcher für die Anordnung der Komponenten zuständig ist. Layoutmanager können unterschiedliche Strategien hinsichtlich der Platzierung der Komponenten eines Containers verfolgen. Java bietet seit JDK 1.0 fünf verschiedene Möglichkeiten. Die folgende Tabelle enthält die Layoutmanager aus dem Package jawa.awt, welche für jeden Container frei zuordenbar sind. Layoutmanager BorderLayout CardLayout FlowLayout GridLayout GridBagLayout Strategie der Anordnung Anordnung der Komponenten: links, rechts, oben, Mitte und unten Überlagerung der Komponenten Komponenten nach Reihe anordnen Komponenten in Matrix-Raster anordnen Komponenten in einem flexiblen Matrix-Raster anordnen: Zellen in Abhängigkeit von anderen Zellen positionieren 4.2.2 BorderLayout Im JDK wird bei Frame, Applet und Dialog standardmässig ein sog. BorderLayout angenommen. Dieses Layout kennt für seine vier Ränder und die Mitte die Bezeichnungen, welche der Windrose entsprechen: Seite 7 4 GUI-Programmierung & Event-Handling North West Center East South Anwendung BorderLayout löst übliche Anordnungsprobleme für Zeichenfläche und Controls. Controls können am Rand in Form sog. Toolbars einmontiert werden. Eine Toolbar wird dann seinerseits ein Panel sein, auf dem Schalter oder Knöpfe erscheinen. Programmbeispiel import java.awt.*; class BorderDemo extends Frame { public BorderDemo (String Title) { super (Title); setSize (300, 150); add (new Button ("North"), BorderLayout.NORTH); add (new Button ("South"), BorderLayout.SOUTH); add (new Button ("West"), BorderLayout.WEST); add (new Button ("East"), BorderLayout.EAST); add (new Button ("Center"), BorderLayout.CENTER); setVisible(true); } public static void main (String[] args) { new BorderDemo("BorderLayout"); } } Hinweis Im Konstruktor werden die erstellten Schalter mit der Methode public void add(Component oneComponent, Object constraints) zu dem Fenster hinzugefügt. Hierbei werden sie beim Layoutmanager mit dem Objekt constraints registriert. BorderLayout nützt diese Parameter, um die Komponente einer bestimmten Stelle zuzuordnen. Die folgende Tabelle enthält die Attribute für die verschiedenen Ausrichtungen: Ausrichtungs-Parameter BorderLayout.NORTH BorderLayout.SOUTH BorderLayout.WEST BorderLayout.EAST BorderLayout.CENTER Beschreibung Anordnung der Komponente oben im Fenster Anordnung der Komponente unten im Fenster Anordnung der Komponente in der rechten Seite des Fensters Anordnung der Komponente in der linken Seite des Fensters Anordnung der Komponente in der Mitte des Fensters Seite 8 4 GUI-Programmierung & Event-Handling Ausgabenmöglichkeit 4.2.3 FlowLayout Im JDK wird bei Panel standardmässig ein sog. FlowLayout angenommen. Bei diesem Layout werden die Komponenten der Reihe nach von links nach rechts in der Folge ihres Eintrags in einem Container angeordnet. Wenn eine „Zeile“ voll ist, wird die nächste Komponente in die nächste Zeile platziert. Die folgende Tabelle enthält die Attribute für die verschiedenen Ausrichtungen: Ausrichtungs-Parameter FlowLayout.LEFT FlowLayout.CENTER FlowLayout.RIGHT FlowLayout.LEADING FlowLayout.TRAILING Beschreibung Linksbündige Anordnung jeder Zeile Zentrierte Anordnung jeder Zeile (Default) Rechtsbündige Anordnung jeder Zeile Für sprachabhängige Ausrichtung. Bei Links-Rechts Ausrichtung wie LEFT. Für sprachabhängige Ausrichtung. Bei Links-Rechts Ausrichtung wie RIGHT. Programmbeispiel import java.awt.*; public class FlowDemo extends Frame { FlowDemo (String Title) { super (Title); setLayout (new FlowLayout (FlowLayout.LEFT)); setSize(200, 130); for (int i = 1; i <= 7; i++) { String s = ""; for (int j = 0; j < i; j++) { s += "S"; } add(new Button (s)); } setVisible(true); } public static void main (String[] args) { new FlowDemo("FlowLayout"); } } Hinweis Da bei Frame standardmässig ein BorderLayout angenommen wird, muss zuerst der Layoutmanager des Containers auf BorderLayout gesetzt werden. Deshalb wird hier ein Objekt der Klasse FlowLayout mit der linksbündigen Ausrichtung erzeugt und mit der Methode public void setLayout(LayoutManager mgr) Seite 9 4 GUI-Programmierung & Event-Handling dem Container zugeordnet. Anschliessend werden die erstellten Schalter mit der add- Methode zu dem Fenster hinzugefügt. Ausgabenmöglichkeiten FlowLayout.LEFT FlowLayout.CENTER FlowLayout.RIGHT 4.2.4 GridLayout Mit GridLayout kann man Komponenten matrixartig in einem n x m- Raster aus n Zeilen und m Spalten anordnen. Programmbeispiel import java.awt.*; public class GridDemo extends Frame { GridDemo (String Title) { super (Title); setLayout (new GridLayout (2, 4)); setSize(300, 200); for (int i = 0; i <= 7; i++) { add (new Button ("Button " + i)); } setVisible (true); } public static void main (String[] args) { new GridDemo ("GridLayout"); } } Hinweis Da bei Frame standardmässig ein BorderLayout angenommen wird, muss hier ebenfalls die Methode setLayout den neuen Layoutmanager festlegen. Deshalb wird hier ein Objekt der Klasse GridLayout erzeugt und dem Container zugeordnet. Beim Erzeugen des GridLayout kann die Zeilenzahl (zuerst) und die Spaltenzahl angegeben werden. Mit zwei weiteren int- Werten können zusätzlich horizontale und vertikale Abstände in Pixel zwischen den „Feldern“ der „Tabelle“ festgelegt werden. Seite 10 4 GUI-Programmierung & Event-Handling Ausgabenmöglichkeit 4.2.5 GridBagLayout GridBagLayout erlaubt eine rasterartige Anordnung von Komponenten. Im Gegensatz zu GridLayout lässt sich die Anordnung der Komponenten noch beeinflussen. Diese Steuerung erfolgt mit diversen Attributen der Klasse GridBagConstraints. Steuerungs-Attributen gridx, gridy gridwidth, gridheight fill weightx, weighty Beschreibung Mit diesen Parametern gibt man die linke, obere Ecke der Komponente an. Die am weitesten links oben stehende Komponente hat die Adresse gridx = 0 und gridy = 0. Mit dem Default-Wert GridBagConstraints.RELATIVE für gridx (bzw. gridy) bestimmt man, dass die Komponente rechts (bzw. unter) der zuletzt zu dem Container hinzu gefügten Komponente angeordnet wird. Mit diesen Parametern mit den Default-Werten 1 legt man die Anzahl der Zeilen (gridwidth) bzw. die Anzahl der Spalten (gridheight) fest. Der Wert GridBagConstraints.REMAINDER bestimmt, dass die Komponente die Letzte in der Zeile (gridwidth) bzw. in der Spalte (gridheight) ist. GridBagConstraints.RELATIVE bewirkt, dass die Komponente die Vorletzte in der Zeile (gridwidth) bzw. der Spalte (gridheight) ist. Wenn eine Komponente kleiner als der ihr zur Verfügung stehende Bereich ist, wird sie gemäss diesem Parameter an die Grösse angepasst. GridBagConstraints.NONE Keine Anpassung (Default Wert) GridBagConstraints.HORIZONTAL Nur horizontale Anpassung GridBagConstraints.VERTICAL Nur vertikale Anpassung GridBagConstraints.BOTH Horizontale und vertikale Anpassung Dieser Parameter legt die Verteilung einer Komponente in x- und y-Richtung fest. Programmbeispiel import java.awt.*; public class GridBagDemo extends Frame { Button b[] = new Button[6]; GridBagDemo (String Title) { super(Title); GridBagLayout l = new GridBagLayout(); setLayout(l); setSize(300,200); Seite 11 4 GUI-Programmierung & Event-Handling for(int i = 0; i < b.length; i++){ b[i] = new Button("Button " + i); } GridBagConstraints c = new GridBagConstraints (); c.fill = GridBagConstraints.BOTH; /* Button 0 */ c.weightx = 1; c.weighty = 1; c.gridheight = 2; l.setConstraints(b[0], c); add(b[0]); /* Button 1 */ c.gridheight = 1; l.setConstraints(b[1], c); add(b[1]); /* Button 2 */ l.setConstraints(b[2], c); add(b[2]); /* Button 3 */ c.gridwidth = GridBagConstraints.REMAINDER; l.setConstraints(b[3], c); add(b[3]); /* Button 4 */ c.gridwidth = GridBagConstraints.RELATIVE; l.setConstraints(b[4], c); add(b[4]); /* Button 5 */ c.gridwidth = GridBagConstraints.REMAINDER; l.setConstraints(b[5], c); add(b[5]); setVisible(true); } public static void main (String[] args) { new GridBagDemo ("GridBagLayout"); } } Hinweis Da bei Frame standardmässig ein BorderLayout angenommen wird, muss wieder die Methode „setLayout()“ den neuen Layoutmanager festlegen. Deshalb wird hier ein Objekt der Klasse GridBagLayout erzeugt und dem Container zugeordnet. Zusätzlich wird ein Spezifikationsobjekt der Klasse GridBagConstraints erstellt, damit die Anordnung der Komponenten beeinflusst werden kann. Die Methode public void setConstraints (Component oneComponent, GridBagConstraints constraints) verknüpft systematisch die Komponenten mit den Spezifikationsobjekten. In obigem Beispiel wird immer mit demselben Spezifikationsobjekt c gearbeitet, denn setConstraints benutzt jeweils nur eine Kopie von diesem Objekt. Seite 12 4 GUI-Programmierung & Event-Handling Beim ersten Schalter (mit der Aufschrift „Schalter0“) wird beim Spezifikationsobjekt das Attribut gridheight auf 2 gesetzt. Damit umspannt diese Komponente zwei Zeilen. Danach wird dieses Attribut auf 1 zurückgesetzt. Die Steuerung der Zeilen übernimmt das Attribut gridwidth. Wenn dieses auf GridBagConstraints.REMAINDER gesetzt wird, füllt die Komponente den Rest der Zeile aus. Ausgabenmöglichkeit 4.2.6 CardLayout CardLayout sorgt dafür, dass nur jeweils eine der Komponenten eines Containers sichtbar ist (wie in einem Kartenstapel). Mit Anrufen wie first(Container), last(Container), next(Container) kann zwischen Karten hin- und hergeschaltet werden. Anwendung Mit CardLayout kann man zwischen verschiedenen Sätzen von Steuerelementen schalten. Damit kann man eine komplizierte Auswahl von den Parametern realisieren, wie man sie etwa mit tabulatorartigen Steuerelementen in modernen Betriebssystemen findet. Es muss ein Steuerelement zur Steuerung der Anzeige sichtbar bleiben. Programmbeispiel import java.awt.*; public class CardDemo extends Frame { CardDemo (String Title) { super (Title); setLayout (new CardLayout()); setSize(300,150); for(int i = 0; i <=7; i++) { add("Button " + i, new Button("Button " + i)); } setVisible(true); } public static void main (String[] args) { CardDemo cardDemo = new CardDemo ("CardLayout"); /* Definition of a reference for CardLayout, which is initialized with the layout manager of CardDemo */ CardLayout cl = (CardLayout) cardDemo.getLayout (); Seite 13 4 GUI-Programmierung & Event-Handling /* Show the first card */ cl.first (cardDemo); for(int i = 0; i < 5; i++) { /* The component of the container can be shown step by step, with the help of break points */ cl.next (cardDemo); } /* Show the last card */ cl.last (cardDemo); } } Hinweis Da bei Frame standardmässig ein BorderLayout angenommen wird, muss wieder die Methode „setLayout()“ den neuen Layoutmanager festlegen. Deshalb wird hier ein Objekt der Klasse CardLayout erzeugt und dem Container zugeordnet. Die Methode public void add (String Name, Component oneComponent) fügt dem Container die erzeugte Komponente unter der Bezeichnung Name hinzu. In der Hauptmethode werden ein Objekt der Klasse „CardDemo“ und eine Referenz „cl“ der Klasse CardLayout erzeugt. Die Referenz cl wird mit der Hilfe der Methode public LayoutManager getLayout() initialisiert, damit sie auf den Layout Manager des Containers zeigt. Hierdurch kann cl für das Schalten zwischen den verschiedenen Sätzen von Steuerelemente benutzt werden. Am Ende der Hauptmethode wird mit den Methoden public void first(Container parent) public void next(Container parent) public void last(Container parent) der Klasse CardLayout jeweils nur eine der Komponenten des Containers sichtbar gemacht. Ausgabenmöglichkeit ..... 4.2.7 Schachtelung der Layouts Jeder Container kann andere Container enthalten. Da jedem Container ein Layout-Manager zugeordnet ist, kann man innerhalb eines Layouts vollständig Layouts verschachteln. Damit lassen sich komplexe Anordnungen der Steuerelemente aufbauen. Das folgende Beispiel zeigt, wie in ein BorderLayout fünf Container integriert werden. Dabei hat jeder dieser Container ein anderes Objekt der Klasse GridLayout als Manager. Programmbeispiel import java.awt.*; Seite 14 4 GUI-Programmierung & Event-Handling class MyPanel extends Panel { public MyPanel (String Text, int Line, int Column){ setLayout(new GridLayout(Line, Column)); for(int i = 0; i < Line * Column; i++){ add(new Button(Text + " - " + (i+1))); } } } public class NestedDemo extends Frame { NestedDemo (String Title){ super(Title); setSize(500, 300); add(BorderLayout.NORTH, new MyPanel("N", add(BorderLayout.SOUTH, new MyPanel("S", add(BorderLayout.WEST, new MyPanel("W", add(BorderLayout.EAST, new MyPanel("E", add(BorderLayout.CENTER, new MyPanel("C", setVisible(true); } 1, 1, 5, 7, 4, 4)); 8)); 1)); 1)); 3)); public static void main (String[] args) { new NestedDemo ("Nested Layouts"); } } Hinweis Die Klasse „MyPanel“ wurde hier für die Schachtelung der Layouts definiert. MyPanel ist eine Tochterklasse von Panel, und enthält ein Objekt der Klasse „GridLayout“ als Layoutmanager. In Java sind Panels sog. Pinnwände, welche als Container für Controls verwendet werden können. Die Parameterliste des Konstruktors „MyPanel“ enthält den auszugebenden Text sowie die Anzahl Zeilen und Spalten für den Layoutmanager. Im Konstruktor von „NestedDemo“ werden Objekte der Klasse „MyPanel“ erzeugt und mit der addMethode ins Hauptfenster nach dem Windrosenmuster platziert. Ausgabenmöglichkeit 4.3 Ereignissteuerung Bei Anwendungen für grafische Benutzeroberflächen haben sich eine Reihe von festen Steuerelementen durchgesetzt: Schalter, Rollbalken usw. Diese sog. „Controls“ werden auch von Java unterstützt. Darüber hinaus gibt es bei grafischen Benutzeroberflächen eine komplexe Wechselwirkung zwischen dem Anwender und dem Seite 15 4 GUI-Programmierung & Event-Handling Programm. Zur Implementierung von ereignisgesteuerten Programmen gibt es die folgenden Strategien: a) Definition von Einsprungspunkten für diverse Aktionen: Mausbewegung, Tastatur, Ziehen von Rollbalken, Drücken von Schalter usw. b) Definition eines Einsprungpunktes und Verzweigen zur Behandlung auf Grund des Typs des speziellen Ereignisses. c) Konzept der Ereignisse mit Quellen von Ereignissen, sowie Beobachtern. Mausbewegung Maustaste gedruckt Taste gedrückt Programm Rollbalken betätigt Schalter gedruckt Textenigabe abgeschlossen Neuer Bidschirmaufbau erfordelich Die Strategien a) und b) haben als Nachteil eine implizite Verbindung der Ereignisse mit dem Programm zur Folge. Die resultierenden Ergebnisse sind die Laufzeit und die komplexen Konstruktionen bei der Anmeldung einzelner Programme zur Bearbeitung von Ereignissen. Deshalb wurde ab dem JDK 1.1 die Strategie c) eingeführt. In diesem Fall werden Ereignisse explizit zu Beobachtern zugeordnet. Jede Klasse kann Beobachterschnittstellen für bestimmte Ereignisse bzw. Gruppen von Ereignissen implementieren. Die Anmeldung von Beobachtern zur Benachrichtigung vom Eintreten bestimmter Ereignisse muss jedoch immer explizit erfolgen. 4.3.1 Das „Delegation Event Model“ in Java AWT 1.1 Im JDK 1.1 wurde die Bearbeitung von Ereignissen auf das sog. „Delegation Event Model“ nach dem Beobachter-Entwurfmuster umgestellt. In diesem Model kennt die Ereignissteuerung für jedes Ereignis eine Quelle „Source“ sowie die Beobachter „Listener“. Die Zuordnung der Quelle zum Bearbeiter ist frei programmierbar und muss explizit durchgeführt werden. Ereignis Quelle Beobachter Bei der Verbreitung von Ereignissen ist zwischen „single-cast“ und „multi-cast“ zu unterscheiden. Für singlecast- Ereignisse wird der Beobachter mit „setxxListerner“ festgelegt. Für multi-cast- Ereignisse wird der Beobachter mit „addxxListerner“ hinzugefügt. Die Bearbeiter müssen spezifisch die „xxListerner“- Schnittstellen implementieren. Die Ereignissteuerung ruft eine Methode dieser Schnittstelle auf. Als Parameter wird eine Unterklasse von EventObject mit der Beschreibung des Ereignisses übergeben. Die Quelle ist ein Objekt, die Ereignisse neu anlegt. Die Quelle definiert den Typ des Ereignisses, indem sie Methoden zur Registrierung der Beobachter anbietet. Die Quelle kann z.B. eine GUI-Komponente sein. Der Bearbeiter ist ein sog. „Adapter“, der eine oder mehrere xxListener- Schnittstellen implementiert. Seite 16 4 GUI-Programmierung & Event-Handling Hierarchie der Ereignisse Die verschiedenen Typen von Ereignissen werden durch eine Klassenhierarchie abgebildet, die ihre Wurzel in der Klasse java.util.EventObjekt hat. In dieser Klassenhierarchie werden die diversen Ereignisse durch eine Klasse modelliert. java.util.EventObject java.awt.AWTEvent java.awt.event.ActionEvent java.awt.event.AdjustmentEvent java.awt.event.HierarchyEvent java.awt.event.InputMethodEvent java.awt.event.InvocationEvent java.awt.event.ItemEvent java.awt.event.TextEvent java.awt.event.ComponentEvent java.awt.event.ContainerEvent java.awt.event.FocusEvent java.awt.event.PaintEvent java.awt.event.WindowEvent java.awt.event.InputEvent jawa.awt.event.KeyEvent jawa.awt.event.MouseEvent Programmbeispiel Ein Programm, das in einem Fenster abläuft, muss sich auf irgendeine Weise anhalten lassen. Eine weithin akzeptierte Möglichkeit besteht darin, dass der Anwender in einer oberen Ecke des Fensters auf einen Knopf zum Schliessen drückt. Das Drücken ist ein Ereignis und kann von einem der Listener im AWT-Paket Event entdeckt werden. Das folgende Beispiel erzeugt ein Fenster, welches mit einem Knopfdruck in der oberen Ecke geschlossen werden kann. import java.awt.*; import java.awt.event.*; class Frame1 extends Frame implements WindowListener { public Frame1(String Title){ super(Title); setSize(300, 100); setVisible(true); // Affectation of the (window) events to the listener addWindowListener(this); } Seite 17 4 GUI-Programmierung & Event-Handling // Tasks of the window listener ! public void windowClosed (WindowEvent public void windowDeiconified(WindowEvent public void windowIconified (WindowEvent public void windowActivated (WindowEvent public void windowDeactivated(WindowEvent public void windowOpened (WindowEvent public void windowClosing (WindowEvent System.exit(0); } event){} event){} event){} event){} event){} event){} event){ public static void main(String[] args){ new Frame1("1. Frame"); } } Hinweis In diesem Beispiel wurde Frame1 explizit als Quelle und Beobachter von Fensterereignissen (WindowEvent) definiert. Dies wird durch addWindowListener(this); zum Ausdruck gemacht. Frame1 muss als Beobachter alle Methoden der Schnittstelle „WindowListener“ implementieren, auch wenn diese, wie im obigen Beispiel, nicht benutzt werden. Die Methode „windowClosing“ von dieser Schnittstelle wird nach dem Drucken des Knopfes in der oberen Ecke des Fensters von der Ereignissteuerung aufgerufen. Deshalb enthält sie die Anweisung „System.exit(0)“, welche das Programm wie gewünscht beendet. Ausgabenmöglichkeit close 4.3.2 Typen von Ereignissen Das AWT 1.1 unterscheidet bei Ereignissen zwischen „Low-Level“ und „Semantic-Level“. Low-LevelEreignisse modellieren einfache Eingaben (Tasten, Maus) oder Fensterereignisse, welche in der folgenden Tabelle aufgelistet worden sind: Low-Level- Ereignisse ComponentEvent FocusEvent KeyEvent MouseEvent ContainerEvent WindowEvent Beschreibung Die Komponente wurde vergrössert, verkleinert, verschoben. Die Komponente hat den Fokus erhalten, verloren. Eine Taste wurde gedrückt, losgelassen ... Maustaste gedrückt, losgelassen ... Der Container hat eine Komponente erhalten, verloren ... Fenster schliessen ... Semantic-Level- Ereignisse werden von den logischen Komponenten der GUI erzeugt. Sie sind aber nicht spezifisch für bestimmte Komponenten. So können ein Schalter, ein Menüpunkt oder ein List-Objekt ein ActionEvent erzeugen. Die folgende Tabelle enthält die Semantic-Level- Ereignisse: Seite 18 4 GUI-Programmierung & Event-Handling Semantic-Level- Ereignisse ActionEvent AdjustmentEvent ItemEvent TextEvent Beschreibung Ein Kommando ausführen. Ein Wert wurde angepasst. Der Zustand eines Eintrags hat sich geändert. Ein Text hat sich geändert. 4.3.3 Quellen von Ereignissen Die von einem Objekt erzeugten Ereignisse werden durch die setxxListerner- bzw. addxxListenerMethoden bestimmt. Alle AWT-Ereignisse unterstützen das multi-cast- Modell. Es gibt keinerlei Informationen darüber, in welcher Reihenfolge die einer Quelle zugeordnete Beobachter über das Auftreten des Ereignisses benachrichtigt werden. Garantiert ist nur, dass jeder Bearbeiter eine Kopie des Original-Ereignisses erhält. Quellen für Low-Level- Ereignisse Ereignis-Quellen Component Container Dialog Frame Methoden für die Erzeugung der Ereignisse addComponentListener addFocusListener addKeyListener addMouseListener addMouseMotionListener addContainerListener addWindowListener addWindowListener Quellen für Semantic-Level- Ereignisse Ereignis-Quellen Button Choice Checkbox CheckboxMenuItem List MenuItem Scrollbar TextArea TextField Methoden für die Erzeugung der Ereignisse addActionListener addItemListener addItemListener addItemListener addActionListener addItemListener addActionListener addAdjustementListener addTextListener addActionListener addTextListener 4.3.4 Beobachter von Ereignissen Im Prinzip hat ein EventListener- Schnittstelle für jeden Ereignistyp eine spezielle Methode. Aus Gründen der Übersichtlichkeit sind manchmal verschiedene Methoden zu einer einzigen Methode zusammengefasst. Die Schnittstellhierarchie der Low-Level- und Semantic-Level- Beobachter wird in den nachfolgenden Abbildungen dargestellt: Seite 19 4 GUI-Programmierung & Event-Handling Schnittstellen der Low-Level- Beobachter java.util.EvenListener java.awt.event.ComponentListener java.awt.event.ContainerListener java.awt.event.FocusListener java.awt.event.KeyListener java.awt.event.MouseListener java.awt.event.MouseMotionListener java.awt.event.WindowListener Schnittstellen der Semantic-Level- Beobachter java.util.EvenListener java.awt.event.ActionListener java.awt.event.AdjustmentListener java.awt.event.ItemListener java.awt.event.TextListener 4.3.5 Adapter Die Schnittstellen verlangen von einer Klasse eine vollständige Implementierung, auch wenn man nicht alle Typen von Ereignissen bearbeiten will. Abhilfe schaffen hier die sog. Adapter-Klassen, die für jeden Typ eine vordefinierte Routine beinhalten. Damit meldet man zur Bearbeitung z.B. der Maus-Ereignisse Sub-Klasse von MouseAdapter an. Wenn man jetzt nur das MousePressed- Event bearbeiten will, überschreibt man nur die entsprechende Methode. Adapter Klassen im AWT Es gibt nur Adapter-Klassen für Low-Level- Ereignisse, da die Schnittstellen für Semantic-Level- Ereignisse jeweils nur eine Methode besitzen. Die komplette Aufstellung aller Adapter-Klassen und die Pflichten der xxListener- Schnittstellen befinden sich im Jobst-Buch. Die Adapter-Klassenhierarchie wurde in der nachfolgenden Abbildung dargestellt. java.util.EvenListener java.awt.event.ComponentAdapter java.awt.event.FocusAdapter java.awt.event.KeyAdapter java.awt.event.MouseAdapter java.awt.event.MouseMotionAdapter java.awt.event.WindowAdapter Praktische Anwendungen Bei der Programmierung muss man für jeden Bearbeiter eine Subklasse der Adapterklasse schreiben. Der eleganteste Weg hierzu sind die inneren Klassen. Wenn ein Frame etwa nur das Ereignis windowClosing bearbeiten will, muss nur die entsprechende Routine überschrieben werden. Bespiel: Window- Ereignisse bearbeiten import java.awt.*; import java.awt.event.*; Seite 20 4 GUI-Programmierung & Event-Handling public class Frame2 extends Frame { Frame2 (String Title) { super(Title); setSize(300, 200); setVisible(true); addWindowListener(new MyWindowAdapter()); } class MyWindowAdapter extends WindowAdapter { public void windowClosing(WindowEvent event) { System.exit(0); } } public static void main (String[] args) { new Frame2("1. Frame"); } } Hinweis Im obigen Beispiel wird die innere Klasse „MyWindowAdapter“ definiert, welche von der Adapterklasse WindowAdapter abgeleitet ist. In dieser Klasse wird die Methode windowClosing mit der Anweisung System.exit(0) überschrieben, damit das Fenster per Knopfdruck geschlossen werden kann. Zusätzlich werden Frame2 und ein Exemplar vom MyWindowAdapter als Quelle bzw. als Beobachter von Fensterereignissen definiert. Dies wird durch addWindowListener(new MyWindowAdapter()); zum Ausdruck gebracht. Hierdurch werden die Fensterereignisse im Frame2 erzeugt und für die Bearbeitung zum Exemplar der Klasse MyWindowAdapter gesendet. 4.4 Elementare Controls in Benutzeroberfläche Dieser Abschnitt soll die elementaren Aspekte der Controls für die Perspektive der Implementierung beschreiben. Diese Controls werden als Steuerelement in der grafischen Benutzeroberfläche benutzt und wurden im Kapitel 4.1.3 (Elementare Controls und ihre Einbindung) kurz eingeführt. In den folgenden Kapiteln werden die Grundfunktionalitäten der Controls besprochen. Dazu sind jeweils ein Programm sowie dessen Anzeige am Bildschirm angegeben. 4.4.1 Zeichenflächen: Canvas Canvas ist ein rechteckiges Fenster mit der Basisfunktionalität für Zeichenflächen. Wenn in ein CanvasObjekt gezeichnet werden soll, muss die Methode „paint()“ überschreiben werden. Diese Methode wird vom Laufzeitsystem (runtime) aufgerufen, sofern ein vollständiger Neuaufbau des Fensters erforderlich ist. Die Methode „repaint()“ hat dieselbe Wirkung, aber sie kann ausdrücklich von einem Programm aufgerufen werden. Das Laufzeitsystem versorgt die Methode paint mit einem Graphics- Objekt als Parameter. Dieses Objekt liefert den Bezug zum grafischen Kontext der Umgebung und kann für die Ausgabe von Formen und Text benutzt werden. Die Grafik bezieht sich auf den Bildschirm, genauer gesagt auf die Canvas zugeteilte Teilfläche des Bildschirms. Die Klasse Graphics ist recht umfangreich, aber eine Zusammenfassung ist im folgenden Schema gegeben: Seite 21 4 GUI-Programmierung & Event-Handling Einige Methoden der Klasse Graphics clearRect(int x, int y, int width, int height) copyArea (int x, int y, int width, int height, int dx, int dy) drawChars(char [] data, int offset, int length, int x, int y) drawLine (int x1, int y1, int x2, int y2) drawOval (int x, int y, int width, int height) drawRect (int x, int y, int width, int height) drawstring(String str, int x, int y) fillOval (int x, int y, int width, int height) fillRect (int x, int y, int width, int height) setColor (Color c) setFont (Font f) // und 38 weitere Die fünf Methoden zum Zeichen (draw...) sind die Gebräuchlichsten und ermöglichen die Anzeige von Formen und Text. Die Parameter x und y sind Pixel-Werte, die von der oberen linken Ecke des Bildschirms aus berechnet werden. Breite und Höhe werden ebenfalls in Pixel angegeben. clearRect löscht einen Teil des Bildschirms und copyArea kopiert einen Ausschnitt an eine andere festgelegte Stelle, die (dx, dy) entfernt ist. Die Methoden setColor und setFont können das Erscheinungsbild der Zeichnung anpassen. Color ist eine Klasse, die Konstanten für die 13 gebräuchlichsten Farben sowie Möglichkeiten zum erstellen neuer Farben enthält. Die folgenden Farben stehen zur Verfügung: Schwarz, Blau, Zyan, Dunkelgrau, Grau, Grün, Hellgrün, Magentarot, Orange, Rosa, Rot, Weiss und Gelb. Alle Angaben erfolgen in Pixel. Die x-Koordinate läuft von links nach rechts, die y-Koordinate von oben nach unten, wie es bei pixelorientierten Darstellungen üblich ist. (0, 0) x y Programmbeispiel import java.awt.*; import java.awt.event.*; class FlagCanvas extends Canvas { String country; Color bar1, bar2, bar3; FlagCanvas(String country, Color bar1, Color bar2, Color bar3) { this.country = country; this.bar1 = bar1; this.bar2 = bar2; this.bar3 = bar3; } Seite 22 4 GUI-Programmierung & Event-Handling public void paint (Graphics g) { // Draw the flag with the colored rectangles g.setColor (bar1); g.fillRect (40, 20, 200, 40); g.setColor (bar2); g.fillRect (40, 60, 200, 40); g.setColor (bar3); g.fillRect (40, 100, 200, 40); g.setColor (Color.black); g.drawString(country, 100, 160); } } public class CanvasDemo extends Frame { CanvasDemo () { setSize(300, 200); add (new FlagCanvas("Germany", Color.black, Color.red, Color.yellow)); addWindowListener(new MyWindowAdapter()); setVisible(true); } class MyWindowAdapter extends WindowAdapter { public void windowClosing(WindowEvent event) { System.exit(0); } } public static void main (String [] args) { new CanvasDemo(); } } Hinweis In diesem Beispiel wird die Klasse „FlagCanvas“ definiert, welche von der Klasse Canvas abgeleitet ist. Dies wird durch class FlagCanvas extends Canvas zum Ausdruck gemacht. Diese Klasse ermöglicht Flaggen mit demselben Muster wie die deutsche Flagge zu zeichnen, also drei Querbalken in verschiedene Farben. Die Farben der Querbalken können beim Erzeugen der Flagge an die Klasse übergeben werden. Die Methode paint bezieht sich auf diese Parameter um die Flagge auf dem Bildschirm auszugeben. Zusätzlich wird hier die Klasse „CanvasDemo” definiert, welche von der Klasse Frame abgeleitet ist. Im Konstruktor CanvasDemo wird eine Instanz der Klasse FlagCanvas erzeugt und zu dem Fenster hinzugefügt. Dies wird durch add (new FlagCanvas("Germany", Color.black, Color.red, Color.yellow)); zum Ausdruck gemacht. Anschliessend wird in der Methode main ein Objekt der Klasse CanvasDemo erzeugt. Damit wird das Fenster mit der deutschen Flagge auf dem Bildschirm sichtbar. Seite 23 4 GUI-Programmierung & Event-Handling Ausgabenmöglichkeit 4.4.2 Schalter: Button Ein Button ist eine Fensterfläche mit der Funktionalität eines Schalters mit Text. Wenn der Schalter gedrückt wird, sendet Java ein Exemplar von einem Ereignis des Typs ActionEvent an den Button. Alle mit addActionListener bei dem Button registrierten Beobachter erhalten eine Kopie dieses Ereignisses. Die Methode getActionCommand des Ereignisses liefert den Text des Schalters. Die Methode getSource der Basisklasse EventObject von ActionEvent liefert das Objekt, welches das Ereignis ausgelöst hat. Zur Reaktion auf das Ereignis „Schalter gedrückt“ kann es erforderlich sein, den gedrückten Schalter zu ermitteln. Hierzu kann man für jeden Schalter eine eigene Instanz eines ActionListeners registrieren. Wenn man nur einen Beobachter für alle Schalter registrieren will, muss dieser anhand des zugestellten Ereignisses den gedrückten Schalter lokalisieren. Wenn der Text des Schalters eindeutig ist, kann er in der Reaktion auf das durch das Drücken des Schalters ausgelöste Ereignis als Identifikation dienen. In diesem Fall erspart man sich die Probleme mit der Aufbewahrung der Instanzvariablen des Schalters. // Somewhere add (new Button ("OK")); // ActionEvent if ("OK ".equals(event.getActionCommand())) // OK has been pressed. Code here the reaction. } Wenn diese Eindeutigkeit nicht gegeben ist, kann im Java-Programm jede Instanz eines Schalters aufbewahren werden. In der Reaktion auf ein Ereignis fragt man dann die Instanzen der Schalter der Reihe nach durch, bis der gedrückte Schalter gefunden wurde. // In the class, which treats the events Button OKButton = new Button ("OK"); add (OKButton); // not for BorderLayout // ActionEvent if (OKButton == event.getSource()) { // OK has been pressed. Code here the reaction. } Programmbeispiel import java.awt.*; import java.awt.event.*; Seite 24 4 GUI-Programmierung & Event-Handling public class ButtonDemo extends Frame implements ActionListener { ButtonDemo (String Title) { super (Title); setLayout (new FlowLayout()); setSize (250, 160); for (int i = 1; i <= 5; i++) { Button b = null; add (b = new Button ("Button " + i)); b.addActionListener(this); } addWindowListener (new WindowAdapter () { public void windowClosing (WindowEvent event) { System.exit(0); }; }); setVisible(true); } public void actionPerformed(ActionEvent event) { System.out.println("Button \"" + event.getActionCommand() + "\" has been pressed!"); } public static void main (String [] args) { new ButtonDemo ("Button-Demo"); } } Hinweis In der for- Schleife des Konstruktors „ButtonDemo“ werden 5 Schalter mit verschiedenen Texten erzeugt und zu dem Fenster hinzugefügt. Zusätzlich werden die 5 Schalter als Quelle und ButtonDemo als Beobachter von Aktionsereignissen („ActionEvent“) definiert. Dies wird durch add(b = new Button ("Button " + i)); b.addActionListener(this); zum Ausdruck gebracht. Als Beobachter muss ButtonDemo die Methode „actionPerformed()“ der Schnittstelle „ActionListener“ implementieren. Mit dieser Methode wird hier der Text des gedruckten Schalters auf der Konsole ausgegeben. Als Im obigen Beispiel werden zusätzlich ButtonDemo und ein Exemplar vom WindowAdapter als Quelle bzw. als Beobachter von Fensterereignissen definiert. Dies wird durch addWindowListener (new WindowAdapter () { public void windowClosing (WindowEvent event) { System.exit(0); }; }); zum Ausdruck gebracht. Als Hier wird die Methode windowClosing der Adapterklasse WindowAdapter vor dem Erzeugen des Klassenexemplars überschrieben. Seite 25 4 GUI-Programmierung & Event-Handling GUI-Anzeige in AWT Textausgabe nach Drücken von Schalter 1, 2, 3, 4, 5 Button Button Button Button Button "Button "Button "Button "Button "Button 1" 2" 3" 4" 5" has has has has has been been been been been pressed! pressed! pressed! pressed! pressed! 4.4.3 Auswahl Die Choice- Klasse in AWT liefert eine Auswahlmöglichkeit aus Texten in einem Pop-Up- Menü. Die aktuelle Auswahl lässt sich programmieren und wird in der Titelzeile des Controls angezeigt. „addItem()“ dient zum Hinzufügen von Texten in der Auswahl, und „select()“ zur programmierten Auswahl. Wenn der Anwender einen Eintrag ausgewählt hat, wird ein Exemplar des Typs „ItemEvent“ gesendet. getItem liefert den auslösenden Eintrag. Programmbeispiel import java.awt.*; import java.awt.event.*; public class ChoiceDemo extends Frame implements ItemListener{ ChoiceDemo (String Title) { super (Title); Choice choice = new Choice(); for(int i = 1; i <= 5; i++){ choice.addItem("Choice " + i); } choice.select(1); choice.addItemListener(this); add(choice, BorderLayout.NORTH); addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent event){ System.exit(0); } }); setSize(400, 200); setVisible(true); } Seite 26 4 GUI-Programmierung & Event-Handling // Responsibility as Item-Listener public void itemStateChanged(ItemEvent event){ System.out.println("Choice " + event.getItem()); } public static void main (String[] args) { new ChoiceDemo("Choice-Demo"); } } Hinweis Im Konstruktor von „ChoiceDemo“ wird ein Exemplar von der Auswahl „Choice“ erzeugt. Anschliessend werden in der for- Schleife mit der Methode addItem die Texte in die Auswahl hinzugefügt. Mit der Methode select wird der Eintrag von Position 1 selektiert. Zusätzlich werden die Auswahl choice und die Klasse ChoiceDemo als Quelle bzw. als Beobachter von Auswahlereignissen („ItemEvent“) definiert. Dies wird durch choice.addItemListener(this); explizit zum Ausdruck gebracht. Als Beobachter muss ChoiceDemo die Methode „itemStateChanged()“ der Schnittstelle „ItemListener“ implementieren. Mit dieser Methode wird hier der auslösende Eintrag auf der Konsole ausgegeben. GUI-Anzeige in AWT Textausgabe nach den Auslösungen von den Einträgen 1, 2, 3, 4, 5 Choice: Choice: Choice: Choice: Choice: Choice Choice Choice Choice Choice 1 2 3 4 5 4.4.4 Checkbox und Radiobutton In AWT kann man mit der Klasse Checkbox sowohl Checkboxen als auch Radiobuttons implementieren. Eine Checkbox in AWT add (new Checkbox ("Label", null, true)); Seite 27 4 GUI-Programmierung & Event-Handling Eine Gruppe von Radiobuttons in AWT CheckboxGroup group = new CheckboxGroup(); add (new Checkbox ("red", group, true)); add (new Checkbox ("green", group, false)); add (new Checkbox ("blue", group, false)); Wenn der Anwender auf eine Checkbox bzw. einen Radiobutton in der Gruppe klickt, wird das Java-System die Markierung hierauf setzen bzw. lassen. In allen anderen Mitgliedern der Gruppe wird die Markierung zurückgesetzt. Alle beim Control mit addItemListener registrierte Beobachtern erhalten einen ItemEvent. Programmbeispiel import java.awt.*; import java.awt.event.*; public class CheckboxDemo extends Frame implements ItemListener { private final static int N = 5; CheckboxDemo (String Title){ super (Title); // GridLayout with 2 lines and 5 columns setLayout(new GridLayout(2,N)); setSize(560, 100); Checkbox c = null; // Independent Checkbox for(int i = 1; i <= N; i++){ add(c = new Checkbox("Check Box " + i)); c.addItemListener(this); } // Grouped Checkbox, which furnishes radio buttons CheckboxGroup group = new CheckboxGroup(); for(int i = 1; i <= N; i++){ add(c = new Checkbox("Radio Button " + i, group, (i==2))); c.addItemListener(this); } addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent event){ System.exit(0); } }); setVisible(true); } public void itemStateChanged(ItemEvent event){ System.out.println("Choice: " + event.getItem()); } public static void main (String[] args) { new CheckboxDemo("Checkbox-Demo"); } } Hinweis In der ersten for- Schleife des Konstruktors „CheckboxDemo“ werden 5 einzelne Checkboxen mit Seite 28 4 GUI-Programmierung & Event-Handling verschiedenen Texten erzeugt und zu dem Fenster hinzugefügt. Zusätzlich werden die 5 Checkboxen und CheckboxDemo als Quelle bzw. als Beobachter von Auswahlereignissen („ItemEvent“) definiert. Dies wird durch for (int i = 1; i <= N; i++) { add(c = new Checkbox("Check Box " + i)); c.addItemListener(this); } zum Ausdruck gebracht. In der zweiten for- Schleife des Konstruktor wird dasselbe für 5 in einer Gruppe zusammengefasste Checkboxen, welche die Radiobuttons darstellen, durchgeführt. Als Beobachter muss CheckboxDemo die Methode „itemStateChanged()“ der Schnittstelle „ItemListener“ implementieren. Mit dieser Methode wird hier die selektierte Checkbox bzw. der selektierte Radiobutton auf der Konsole ausgegeben. GUI-Anzeige in AWT Textausgabe nach den klicken auf die Checkboxen 2 und 4, sowie das Radio Button 4 Choice: Check Box 2 Choice: Check Box 4 Choice: Radio Button 4 4.4.5 Statischer Text Ein Label ist ein Control zur Anzeige von Text, welcher nur vom Programm verändert werden kann. Label quantity; add (quantity = new Label ("Quantity...")); quantity.setText("1234"); 4.4.6 Textfelder TextComponent ist die Basisklasse für alle Controls, die der Editierung von Text dienen. Es bietet unter anderem die Methoden „void setText(String text)“ zum Setzen und „String getText()“ zum Auslesen des aktuellen Textes aus einem Textfeld. Mit String getSelectedText() kann der selektierte Text ausgelesen werden und mit den Methoden „int getSelectionEnd()“ sowie „int getSelectionStart()“ können die Grenzen der Selektion ermittelt werden. TextField TextField dient der Editierung von einzeiligem Text. Textfelder können mit den Aufrufen: new TextField("blue"); new TextField("green", 20); angelegt werden. Die Angabe einer Spaltenbreite in Zeicheneinheiten ist also optional. Dieses Control sendet eine Instanz der Ereignisklasse „TextEvent“, wenn sich der Wert des Textes ändert. Der Beobachter muss hierzu die Routine „textValueChanged()“ der Schnittstelle „TextListener“ implementieren. Der Anwender kann Textbereiche selektieren, ohne dass das Java-Programm darüber in Form eines Ereignisses informiert wird. Seite 29 4 GUI-Programmierung & Event-Handling TextArea „TextArea“ dient dem Editieren von Textbereichen mit mehreren Zeilen. Ein solches Control erhält bezüglich der Zeilen und Spalten vom AWT Scrollbars, mit denen der Anwender auf die zu bearbeitenden Abschnitte sich positionieren kann. Programmbeispiel import java.awt.*; import java.awt.event.*; public class TextDemo extends Frame implements TextListener { TextField ta; // Text line TextArea tf; // Text field with several lines TextDemo (String Title) { super (Title); setSize (250, 300); add (new Label ("Please enter your text:"), BorderLayout.NORTH); add (tf = new TextField ("Text line......"), BorderLayout.SOUTH); add (ta = new TextArea ("1\n2\n3\n4\n5\n6\n7\n"), BorderLayout.CENTER); tf.addTextListener(this); ta.addTextListener(this); addWindowListener (new WindowAdapter () { public void windowClosing (WindowEvent event) { System.exit(0); } }); setVisible(true); } public void textValueChanged(TextEvent event) { // The event source is a every time a text component TextComponent t = (TextComponent)event.getSource(); System.out.println ("New content: " + t.getText()); } public static void main (String [] args){ new TextDemo ("Text-Demo"); } } Hinweise Im Konstruktor von „TextDemo“ werden ein Exemplar von TextField (tf) sowie ein Exemplar von TextArea (ta) erzeugt und zu dem Fenster hinzugefügt. Dies wird durch add (tf = new TextField ("Text line......"), BorderLayout.SOUTH); add (ta = new TextArea ("1\n2\n3\n4\n5\n6\n7\n"), BorderLayout.CENTER); zum Ausdruck gebracht. Zusätzlich werden diese Textkomponenten und TextDemo als Quelle bzw. als Beobachter von Textereignissen (TextEvent) definiert. Dies wird durch tf.addTextListener(this); ta.addTextListener(this); zum Ausdruck gebracht. Seite 30 4 GUI-Programmierung & Event-Handling Als Beobachter muss TextDemo die Methode „textValueChanged()“ der Schnittstelle „TextListener“ implementieren. Mit dieser Methode wird hier der neue Inhalt des geänderten Textes auf der Konsole ausgegeben. GUI-Anzeige in AWT 4.4.7 Rollbalken Ein Scrollbar-Control dient dem Anwender zur Auswahl eines Zahlenwertes aus einem vorgegebenen Bereich. Solche Rollbalken können vertikal bzw. horizontal angeordnet werden. Der Anwender kann einen Rollbalken durch Klicken auf die Markierung auf bzw. ab bewegen, er kann den „Schieber“ des Rollbalkens erfassen und bewegen, oder er kann auf die neben dem Schieber liegende Leiste des Rollbalkens klicken. Die mit „addAdjustmentListener()“ registrierte Beobachter erhalten ein Anpassungsereignis („AdjustmentEvent“). Programmbeispiel import java.awt.*; import java.awt.event.*; public class ScrollbarDemo extends Frame implements AdjustmentListener { private Scrollbar h = null, v = null; ScrollbarDemo (String Title){ super (Title); setSize(300, 300); setLayout(null); h = new Scrollbar(Scrollbar.HORIZONTAL, 50, 20, 0, 100); v = new Scrollbar(Scrollbar.VERTICAL, 50, 20, 0, 100); h.setBounds(50, 50, 150, 30); v.setBounds(50, 100, 30, 150); add(h); add(v); h.addAdjustmentListener(this); v.addAdjustmentListener(this); addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent event){ System.exit(0); } }); setVisible(true); } Seite 31 4 GUI-Programmierung & Event-Handling // Responsibilities as Item-Listener public void adjustmentValueChanged(AdjustmentEvent event){ switch(event.getAdjustmentType()){ case AdjustmentEvent.UNIT_INCREMENT: case AdjustmentEvent.UNIT_DECREMENT: case AdjustmentEvent.BLOCK_INCREMENT: case AdjustmentEvent.BLOCK_DECREMENT: case AdjustmentEvent.TRACK: if(event.getSource() == h){ System.out.println("h " + event.getValue()); }else if (event.getSource() == v){ System.out.println("v " + event.getValue()); } break; } } public static void main (String[] args) { new ScrollbarDemo("Scrollbar-Demo"); } } Hinweise Im Konstruktor von „ScrollbarDemo“ werden zwei Exemplare von Rollbalken erzeugt und zu dem Fenster hinzugefügt. Dies wird durch h = new Scrollbar(Scrollbar.HORIZONTAL, 50, 20, 0, 100); v = new Scrollbar(Scrollbar.VERTICAL, 50, 20, 0, 100); h.setBounds(50, 50, 150, 30); v.setBounds(50, 100, 30, 150); add(h); add(v); zum Ausdruck gebracht. Beim Erzeugen der Rollbalken kann die Orientierung, der Wert, der sichtbare Bereich sowie der Minimal- und Maximalwert festgelegt werden. Zusätzlich kann die Position relativ zum übergeordneten Container und die Grösse des Rollbalkens mit der Methode setBounds() gesetzt werden. In diesem Beispiel werden die Rollbalken und die Klasse ScrollbarDemo als Quelle bzw. als Beobachter von Anpassungsereignis (AdjustmentEvent) definiert. Dies wird durch h.addAdjustmentListener(this); v.addAdjustmentListener(this); zum Ausdruck gebracht. Als Beobachter muss ScrollbarDemo die Methode „adjustmentValueChanged()“ der Schnittstelle „AdjustmentListener“ implementieren. Mit dieser Methode wird hier der geänderte Wert auf der Konsole ausgegeben. Seite 32 4 GUI-Programmierung & Event-Handling GUI-Ausgabe für AWT TRACK UNIT_INCREMENT BLOCK_INCREMENT 4.4.8 Menüs in Java Frames können in Java mit einer Menüleiste der Klasse MenuBar versehen werden. Die Menüleiste erhält vom Frame alle sie betreffenden Ereignisse. Aus einem Menü kann vom Anwender ein Punkt ausgewählt werden. Das Programm wird hiervon mit einem ActionEvent benachrichtigt. Jedes Java-Menü kann wieder ein Menü enthalten. Dabei kann jeder Eintrag ein gewöhnlicher Menüpunkt, ein Menüpunkt mit einer Marke oder ein Submenü sein. Das folgende Bild zeigt alle Fälle: Ansicht eines Menüs Einbau eines Menüs in einem Frame Eine Menüleiste wird in einem Frame mit der Methode „setMenuBar()“ eingebaut. Diese Menüleiste kann eine beliebige Anzahl Menüs enthalten. Jedes von diesen Menüs kann wiederum entweder, Menüpunkte (MenuItem), Menüpunkte mit einer Marke (CheckboxMenuItem) oder Submenüs (Menu) enthalten. MenuBar mMenuBar = new MenuBar(); SetMenuBar (mMenuBar); Menu mMenu = new Menu ("File"); mMenuBar.add (mMenu); mMenu.add (m_Save = new MenuItem ("Save)); Seite 33 // Only possible with Frame 4 GUI-Programmierung & Event-Handling Reaktion auf die Nachrichten Bei der Reaktion auf Nachrichten muss die Quelle gefunden werden. Wenn der Text der Menüeinträge eindeutig ist, kann er benutzt werden. Wenn diese Eindeutigkeit der Texte a priori nicht gegeben ist, kann man die Instanzen der Menüeinträge im Frame aufbewahren. Trifft dann ein Ereignis ein, kann man die getSource()-Methode dazu benutzen, den Ursprung der Nachricht zu finden. Mann muss der Reihe nach alle Instanzen im Menü mit dem Ziel des Ereignisses vergleichen, bis man fertig ist oder die Quelle des Ereignisses gefunden hat. Diese Vorgehensweise ist im nachfolgenden Programm implementiert. Programmbeispiel import java.awt.*; import java.awt.event.*; public class FrameMenu extends Frame implements ActionListener, ItemListener { // References for the event treatments private MenuItem m_Save, m_Open; private MenuItem m_Red, m_Lightblue, m_Blue, m_Darkblue; private CheckboxMenuItem m_Pink; FrameMenu (String Title){ super (Title); initializeMenu (); setSize (300, 200); addWindowListener (new WindowAdapter () { public void windowClosing (WindowEvent event) { System.exit(0); } }); setVisible(true); } // Search the menu item, which has generated the event // All the possible sources are treated here public void actionPerformed (ActionEvent event) { if (event.getSource() == m_Save) { System.out.println ("Activation of m_Save"); } else if (event.getSource() == m_Open) { System.out.println ("Activation m_Open"); } else if (event.getSource() == m_Red) { System.out.println ("Activation m_Red"); } else if (event.getSource() == m_Lightblue) { System.out.println ("Activation m_Lightblue"); } else if (event.getSource() == m_Blue) { System.out.println ("Activation m_Blue"); } else if (event.getSource() == m_Darkblue) { System.out.println ("Activation m_Darkblue"); } } public void itemStateChanged (ItemEvent event) { String sel = (event.getStateChange() == ItemEvent.DESELECTED) ? "SELECTED" : "DESELECTED"; System.out.println("m_Pink " + sel); } Seite 34 4 GUI-Programmierung & Event-Handling public void initializeMenu () { MenuBar mMenuBar = new MenuBar (); setMenuBar (mMenuBar); Menu mMenu; // 1. Under menu : File-Menu mMenuBar.add (mMenu = new Menu ("File")); mMenu.add (m_Save = new MenuItem ("Save")); mMenu.add (m_Open = new MenuItem ("Open")); // 2. Under menu : Option-Menu mMenuBar.add (mMenu = new Menu ("Option")); mMenu.add (m_Red = new MenuItem ("Red")); mMenu.add (m_Pink = new CheckboxMenuItem ("Pink")); Menu mSubMenu = new Menu ("Blue tone"); mMenu.add(mSubMenu); mSubMenu.add (m_Lightblue = new MenuItem ("Light blue")); mSubMenu.add (m_Blue = new MenuItem ("Blue")); mSubMenu.add (m_Darkblue = new MenuItem ("Dark blue")); // Register this class for the treatments of all events m_Save.addActionListener (this); m_Open.addActionListener (this); m_Red.addActionListener (this); m_Lightblue.addActionListener (this); m_Blue.addActionListener (this); m_Darkblue.addActionListener (this); m_Pink.addActionListener (this); m_Pink.addItemListener (this); } public static void main (String [] args) { new FrameMenu ("Menu-Demo"); } } Seite 35 5 Applets 5 Applets 5.1 Einführung Ein Applet ist ein Java-Programm, das innerhalb eines Browsers arbeitet und folglich in einer Web-Seite auftreten kann. Das Applet läuft auf dem Computer des Anwenders, obwohl es eventuell von weither geladen wurde. Diese spezielle und nur in Java vorhandene Arbeitsweise ist durch die folgende Kombination von Faktoren möglich: • Java-Programme werden nicht vollständig übersetzt sondern interpretiert. Der Quellcode wird in einen Zwischencode übersetzt, welcher dann seinerseits interpretiert wird. • Die Java Virtual Machine, welche den Zwischencode interpretiert, läuft auf allen Webbrowsern. Wie schone der Name sagt, sind Applets normalerweise kleine Programme, von denen jedes eine einzige Aufgabe auf einer einzigen Web-Seite ausführt. Das Integrieren von Applets in eine Web-Seite hat folgende Vorteile: • Die Arbeit wird auf der Maschine ausgeführt, auf der die Ergebnisse benötigt werden, statt die Ergebnisse dorthin zu senden. Dies mindert die Netzbelastung. • Die Maschine des Anwenders kann sich wirklich dem Applet widmen und es viel schneller ausführen, als eine Partition auf dem Server, auf dem das Applet angesiedelt ist. • Anders als bei manchen speziell für das Internet entwickelten Sprachen mit eingeschränkter Rechenund Strukturierungsleistungen stehen alle Möglichkeiten der Programmiersprache Java zur Verfügung. 5.1.1 Die HTML-Seiten HTML steht für Hyper Text Markup Language. Die Ausführung von Applets erfordert nur elementare Grundkenntnisse darin. Eigentlich besteht das Schema zur Aktivierung eines Applets nur aus einem einzigen Tag: HTML-Tags für ein Applet <APPLET code = "Name" width = n height = m> </APPLET> HTML wird im Modul „Internet Technologien/Embedded Webserver“ ausführlich behandelt. 5.1.2 Appletbeispiel Eine Viruswarnprogramm soll auf dem Bildschirm ausgegeben werden. Programmbeispiel import java.applet.Applet; import java.awt.*; public class DemoForApplet extends Applet { static private final int xCoordinate[] = {50, 110, 170}; static private final int yCoordinate[] = {95, 15, 95}; static private final int line = 18; static private final int letter = 8; Seite 36 5 Applets public void paint(Graphics g) { g.setColor(Color.red); g.fillPolygon(xCoordinate ,yCoordinate ,3); g.setColor(Color.white); g.setFont (new Font("Helvetica", Font.BOLD, 60)); g.drawString("!", 102, 80); g.setFont (new Font("Helvetica", Font.BOLD, 15)); g.setColor(Color.black); g.drawString("W A R N I N G", 8*letter, 7*line); g.drawString("Possible virus detected", 3*letter, 8*line); g.drawString("Reboot and run virus", 4*letter, 9*line); g.drawString("remover software", 6*letter, 10*line); } } Hinweis Das Programm ist ein Applet. Diese wird durch die Schreibweise public class DemoForApplet extends Applet Zum Ausdruck gebracht. Applets können sich selbst in einer grafischen Umgebung darstellen. Hierzu muss die Methode „paint(Graphics g)“ angeben werden. Diese Methode wird von der Umgebung des Programms aufgerufen, wenn ein Neuzeichnen der zugewiesenen Fläche erforderlich ist, also insbesondere bei Beginn eines Programmlaufes oder bei einer Änderung der Grösse der Zeichenfläche. Der Parameter Graphics g liefert den Bezug zum grafischen Kontext der Umgebung. Die Grafik bezieht sich auf den Bildschirm, genauer gesagt auf dem Applet zugeteilte Teilfläche des Bildschirms. Diese Teilfläche hat eine Breite und eine Höhe. Das Applet kann die Ausdehnung seiner Zeichenfläche über die Methoden getWidth() bzw. getHeight() ermitteln. Die Klasse Graphics ist recht umfangreich, aber eine Zusammenfassung wurden im Kapitel 4.4.1 eingeführt. Einbetten des Applets in ein HTML-Dokument Der Name der Klasse muss wie folgt in dem HTML-Dokument angegeben werden. Das HTML-Dokument soll in diesem Beispiel etwa example.htlm heissen. <title>Demo for Applet </title> <hr> <applet code = "DemoForApplet.class" width = 200 height = 100> <hr> Programmablauf Das HTML-Dokument kann mit einem Browser betrachtet werden, der Java unterstützen muss. In Diesem Fall muss der Browser die Datei example.html laden. Zu Testzwecken kann auch das Programm Appletviewer aus dem JDK benutzt werden: Appletviewer DemoForApplet.class Seite 37 5 Applets Ausgabenmöglichkeit 5.2 Funktionsweise von Applets Wenn ein Applet kein Hauptprogramm hat, wie wird es dann gestartet oder angehalten? Gestartet wird es aus einem Browser (z. B. Netscape oder Explorer) oder dem Applet-Viewer durch das Laufzeitsystem von Java. Die Java Virtual Machine (JVM) ruft dann zu geeigneten Zeitpunkten eine der vier im folgenden Schema gezeigten Methoden auf. Alle oder einige dieser vier Methoden sollten von jeder Unterklasse von Applet implementiert werden. Applet Methoden Beschreibung init() destroy() start() init initialisiert das GUI, welches zum Applet gehört und startet alle Threads destroy vernichtet das GUI und beendet das Applet start stösst jedes Mal, wenn das Applet von der Web-Seite aus wieder gestartet wird, Aktionen wie Animationen an. stop unterbricht die Aktionen, die durch start angestossen worden sind. stop() Die folgende Abbildung zeigt den Zustand von einem Applet, welches vom Webbrowser ausgeführt wird. Wenn der Browser zum ersten Mal dem Tag für das Applet begegnet, wird die Methode init() anstelle des Konstruktors aufgerufen. init ist für die erste Einrichtung des Applets, seiner Benutzungsschnittstelle, seiner Knöpfe und Menüs und möglicher weiterer Steuerungs- Threads verantwortlich. Wenn Dagegen wird die Methode start() immer dann aufgerufen, wenn die Web-Seite, in der das Applet vorkommt, erneut erscheint. start könnte zum Beispiel eine angehaltene Animation wieder in Gang setzen. Constructor startApp() init() destroyApp() start() startApp() destroy() Garbage collection stopApp() stop() destroyApp() Seite 38 5 Applets Wenn Jedes Mal wenn ein Neuzeichnen der zugewiesenen Fläche des Applets erforderlich ist, wie zum Beispiel nach start, wird die Methode paint() vom Laufzeitsystem aufgerufen. Dann kehrt das Applet in einem passiven Zustand zurück und wartet, bis etwas passiert. Es gibt zwei Möglichkeiten: • Das Applet kann unsichtbar werden, indem der Anwender es aus der Seite im Browser hinausschiebt. • Ein normales GUI-Ereignis wie z. B. das Drücken einer Maustaste oder eines Knopfs finden statt. Wird ein Applet unsichtbar, wird seine stop- Methode aufgerufen, die dazu da ist, eventuelle Animationen etc. anzuhalten. Wird es wieder sichtbar, so wird start aufgerufen. Alle übrigen Ereignisse werden ganz normal über die im Originalprogramm neu definierten AWT-Methoden behandelt. Zum Schluss kann eine destroy- Methode bereitgestellt werden, damit das Applet seine Ressourcen freigibt, bevor der Viewer endet oder der Browser zu einer anderen Seite kommt. Programmbeispiel import java.applet.Applet; import java.awt.*; public class DemoForApplet extends Applet { static private final int xCoordinate[] = {50, 110, 170}; static private final int yCoordinate[] = {95, 15, 95}; static private final int line = 18; static private final int letter = 8; public void init() { System.out.println("init"); } public void start() { System.out.println("start"); } public void paint(Graphics g) { System.out.println("paint"); g.setColor(Color.red); g.fillPolygon(xCoordinate ,yCoordinate ,3); g.setColor(Color.white); g.setFont (new Font("Helvetica", Font.BOLD, 60)); g.drawString("!", 102, 80); g.setFont (new Font("Helvetica", Font.BOLD, 15)); g.setColor(Color.black); g.drawString("W A R N I N G", 8*letter, 7*line); g.drawString("Possible virus detected", 3*letter, 8*line); g.drawString("Reboot and run virus", 4*letter, 9*line); g.drawString("remover software", 6*letter, 10*line); } public void stop() { System.out.println("stop"); } public void destroy() { System.out.println("destroy"); } } Seite 39 5 Applets Textausgabe nach dem Starten des Applets: init start paint Textausgabe nach dem auf der Seite Verschieben des Applets: stop Textausgabe nach dem Reaktivieren des Applets: start paint Textausgabe nach dem Verlassen des Applets: stop destroy 5.2.1 Speicherung von Applets Wird ein Applet auf einer Server-Maschine gespeichert. Mann kann von jeder anderen mit dem Internet verbundenen Maschine über das WWW-Protokoll und seinen Namen oder sein Universal Resource Locator (URL) auf das Applet zugreifen. Die URL des example wäre zum Beispiel: https://prof.hti.bfh.ch/fue1/java/example.html Hier ist die HTML-Datei in einem über das Internet zugänglichen Verzeichnis gespeichert. Die Klassendatei, auf die sie sich bezieht, muss in demselben Verzeichnis sein, wenn sie den einfachen, im folgenden Tag angegeben Namen hat: <applet code = "DemoForApplet.class" width = 200 height = 100> Befindet sich das Applet in einem anderen Verzeichnis, so muss der Tag seine vollständige URL spezifizieren. 5.2.2 Sicherheit von Applets Bei der Benutzung von Applets im Internet können Bedenken auftreten, dass ein herunter geladenes Programm eventuell infiziert ist und das System beschädigen könnte. Java beugt dem mit allen Mitteln vor. Zuerst wird der auf dem Computer eintreffende Bytecode auf seine Gültigkeit geprüft. Wurde er unterwegs verfälscht, führt die „Java Virtual Machine“ (JVM) ihn nicht aus. Zweitens führt die JVM keinerlei Operationen aus, die das System beschädigen könnte. So kann z. B. ein von weither stammendes Applet weder Passworte herausfinden noch Dateien löschen. Die folgende Tabelle fasst das gesamte Regelwerk zusammen. Operation Zugriff auf lokale Dateien Löschen lokaler Dateien Anderes Programm ausführen Eigenen Namen finden Mit Host neu verbinden Mit anderem Host verbinden Java-Bibliothek laden exit aufrufen Pop-Up- Fenster erstellen JavaAnwendung √ √ Applet im Applet-Viewer √ Lokales Applet im Browser Entferntes Applet im Browser √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ Seite 40 √ √ 5 Applets Aus dieser Tabelle ergibt sich, dass eine Java-Anwendung ein vollständiges Programm ist und als solches alles machen kann, was der Computer verlangt. Ein Applet ist etwas ganz anderes: Es unterliegt einer Kontrolle und den oben genanten Regeln. In einem Applet-Viewer ausgeführte Applets können alles, ausser Dateien löschen. Laufen sie allerdings in einem Browser, werden ihre Aktivitäten etwas beschnitten. In einem Browser hält ein Applet nicht an: Es endet erst, wenn die Seite, in der es sich befindet, durch eine andere ersetzt wird. Also ruft es exit nicht auf und es gibt kein close- Kästchen. Applets sind, wie die Fallstudie zeigt, nahtlos in Web-Seiten integriert. Dass Applets nichts aus dem lokalen Dateisystem lesen oder in dieses schreiben können, kann von Nachteil sein. Diese Einschränkung kann der Anwender des Applets jedoch aufheben, indem er einen speziellen Sicherheitsmanager definiert. Zum Schluss zeigt die letzte Tabellenspalte, dass für entfernte geladene Applets zusätzliche Einschränkungen gelten, die alle zu ihrem Besten sind. 5.3 Die PARAM- Funktion Die PARAM- Funktionen erlauben eine Interaktion zwischen Web-Seiten und Applets. PARAM stellt Listen benannter Parameter im HTML-Dokument bereit, die das Applet mit einer Methode namens getParameter aus der Klasse Applet lesen kann. Die Parameter sind zwischen den Tag-Klammern <APPLET> und </APPLET> aufgelistet und jeder Parameter muss die folgende Form haben. HTML-Parameter <PARAM NAME = "formal" VALUE = "actual"> NAME weist darauf hin, dass die nachfolgende Zeichenreihe der Name des gesuchten Parameters ist. In demselben Paar von spitzen Klammern gibt es einen Wert, VALUE, der diesem Parameter zugewiesen wird. Bei dem Wert handelt es sich ebenfalls um eine Zeichenreihe, aber er kann natürlich auch als Zahl oder als ein beliebiger anderer geforderter Typ gelesen werden. Eine mögliche Anwendung von PARAM wäre die Produktpreise von einer Supermarkt auf die Web-Seite zu verlagern, indem man sie folgendermassen spezifiziert: <PARAM NAME = "garlic" VALUE = "12"> Für den Zugriff auf die Parameter kann die Zeichenreihemethode getParameter wie folgt verwendet werden: unitCosts[i] = Integer.parseInt(getParamter(items[i])); Hier ist items[i] eine Zeichenreihe wie etwa „garlic", und getParameter findet den Parameter dieses Namens und gibt den entsprechenden Wert zurück, hier also die Zeichenreihe "12". 5.3.1 Appletbeispiel für die Währungsumrechnung Es soll ein Applet entwickelt werden, welches einen Betrag in Schweizer-Franken in eine andere Währung umrechnet. Dieser Wechsel sollte mit den folgenden Währungen möglich sein: amerikanischer Dollar, australischer Dollar, kanadischer Dollar, europäisches Euro, japanischer Yen und russische Rubble. Lösung Folgende Abbildung zeigt das gewünschte Bildschirmlayout. Der Textfeld „Input amount (CHF)“ (ganz oben) dient zur Betragseingabe in Schweizer Franken. Mit dem Auswahlkästchen „Currency“ (zweite Zeile) kann die gewünschte Währung, in der umgewandelt werden muss, gewählt werden. Das Textfeld „Output amount“ (dritte Zeile) dient zur Ausgabe des umgewandelten Betrags. Der Schalter „Convert“ (Ganz unten) aktiviert die Umrechnung von Schweizer Franken in die gewünschte Währung, und gibt das Ergebnis im Textfeld „Output amount“ aus. Der Schalter „Reset“ löscht beide Textfelder. Seite 41 5 Applets Input amount (CH) 0 Currency American Dollar Output amount 0 Convert Reset Java-Quellcode Das Programm sieht folgendermassen aus: import java.applet.*; import java.awt.*; import java.awt.event.*; public class CurrencyConversion extends Applet implements ActionListener { // Attribute private String currency[] = {"American Dollars", "Australia Dollars", "Canadian Dollars", "European Euro", "Japanese Yen", "Russian Rubbles"}; private double conversionRate[] = new double [currency.length]; private TextField inputTextField; private TextField outputTextField; private Choice currencyChoice; public void init () { // Read the conversion rates with the PARAM-Functions for (int i = 0; i < currency.length; i++) { conversionRate[i] = Double.parseDouble(getParameter(currency[i])); } // Set the properties of the application window setLayout (new GridLayout (4,2)); add(new Label ("Input amount (CHF)")); inputTextField = new TextField("0"); add(inputTextField); add(new Label ("Conversion currency")); currencyChoice = new Choice(); for (int i = 0; i < currency.length; i++) { currencyChoice.addItem(currency[i]); } add(currencyChoice); add(new Label ("Output amount")); outputTextField = new TextField("0"); add(outputTextField); Button button = new Button("Convert"); button.addActionListener(this); add(button); Seite 42 5 Applets button = new Button("Reset"); button.addActionListener(this); add(button); } public void actionPerformed (ActionEvent event) { if ("Convert".equals (event.getActionCommand())) { // Read the amount and convert it in to a forain monney double amount = Double.parseDouble (inputTextField.getText()); double rate = conversioRate[currencyChoice.getSelectedIndex()]; double output = amount * rate; outputTextField.setText(Double.toString(output)); } else { // Clear all text fields inputTextField.setText("0"); outputTextField.setText("0"); } } } Die Eigenschaften des Applets werden in der Methode init festgelegt. In diesem Beispiel werden die verschieden Controls erzeugt und dem Applet „CurrencyConversion“ hinzugefügt. Zusätzlich werden die Schalter „Convert“ und „Reset“ als Quelle sowie MigrosApplet als Beobachter von Aktionsereignisse („ActionEvent“) definiert. Als Beobachter muss CurrencyConversion die Methode actionPerformed der Schnittstelle ActionListener implementieren. In dieser Methode wird nach dem Text des gedrückten Schalters ermittelt und anschliessend die entsprechenden Aufgabe durchgeführt. Für die Parameterübergabe zwischen der Web-Seite und dem Applet wurde die Zeichenreihemethode getParameter wie folgt verwendet: for (int i = 0; i < currency.length; i++) { conversionRate[i] = Double.parseDouble(getParameter(currency[i])); } Hier ist currency[i] eine Zeichenreihe wie etwa "American Dollars", und getParameter findet den Parameter dieses Namens und gibt den entsprechenden Wert zurück, hier also die Zeichenreihe "0.80414". Einbetten des CurrencyConversion - Applets in eiem HTML-Dokument Der Klassenname und die PARAM- Funktionen müssen wie folgt in dem HTML-Dokument angegeben werden: <title> Demo for Currency Conversion </title> <APPLET code="CurrencyConversion.class" width = 300 height = 150> <PARAM NAME="American Dollars" VALUE="0.80414"> <PARAM NAME="Australia Dollars" VALUE="1.15197"> <PARAM NAME="Canadian Dollars" VALUE="1.09099"> <PARAM NAME="European Euro" VALUE="0.66428"> <PARAM NAME="Japanese Yen" VALUE="87.8662"> <PARAM NAME="Russian Rubles" VALUE="23.3402"> </APPLET> Seite 43 5 Applets Ausgabe 5.4 Klänge und Bilder In Java können Klänge und Bilder einfach und wirkungsvoll in ein Programm integriert werden. 5.4.1 Klänge Das Paket applet enthält ein Interface namens AudioClip, das über drei Methoden verfügt: play, stop und loop. Die Applet-Methode getAudioClip gibt ein Objekt zurück, das dieses Interface implementiert, und das abgespielt werden kann. Das Schema ist: Audioclip: Deklaration und Ausführung AudioClip name; name = getAudioClip(getCodeBase(), filename); name.play(); Die Methode getCodeBase in Applet stellt fest, wo das Applet ausgeführt wird, damit der Klang dort abgespielt werden kann. Derzeit muss die entsprechende Datei noch eine .au- Datei sein und keine .wayDateien. Zum Beispiel die Datei „ouch.au“ kann wie folgt abgespielt werden: AudioClip ouch; ouch = getAudioClip(getCodeBase(), "ouch.au"); ouch.play(); // Or the short version with an anonymous audio clip play(getCodeBase(), "ouch.au"); Seite 44 5 Applets 5.4.2 Bilder Bilder sind aus Pixel bestehende Daten, die in einer Datei gespeichert, über ein Netzwerk weitergegeben oder in Echtzeit von einem Grafik-Rechner oder einer Videokamera erzeugt werden. Java hat für Bilder ebenso eine Klasse wie für Audioclips, und um eines zu bekommen, folgt man genau demselben Schema wie oben, wobei AudioClip durch Image ersetzt wird. Ein Bild könnte folgendermassen bekommen werden. Image me; me = Toolkit.getDefaultToolkit().getImage(getCodeBase(), "bischop.gif"); Die Klasse „Toolkit“ ist die abstrakte Oberklasse aller tatsächlichen Implementierungen des Abstract Window Toolkit. Toolkit oder auch Toolbox (englisch für „Werkzeugsatz“) ist ein Begriff aus der elektronischen Datenverarbeitung. Er bezeichnet allgemein eine Sammlung von Bibliotheken, Klassen und Schnittstellen, die das Erstellen von Computerprogrammen vereinfachen sollen. Die Methode „getDefaultToolkit()“ ruft den Standard-Toolkit und implementiert somit die abstrakte Klasse Toolkit. Die Methode „getImage“ liefert das Bild, welches im gif format gespeichert worden ist. Zum Darstellen des Bildes greifen wir in der AWT-Klassen Graphics auf die Methode drawImage zu. Ein Bild wird nach dem folgenden Schema angezeigt: Ein Bild anzeigen g.drawImage(Image imagename, int x, int y, ImageObserver observer); Die x- und y- Koordinaten geben die obere linke Ecke der Stelle an, an der das Bild auf dem Bildschirm gezeichnet werden soll. Meistens ist der Browser der Beobachter. In dem Fall wird this als vierten Parameter verwendet. Z. B.: g.drawImage(me, 0, 0, this); Programbeispiel import java.applet.Applet; import java.awt.*; import java.awt.event.*; public class ImageDemo extends Applet implements ItemListener { Image image, imageRohr, imageKaufmann, imageFirouzi; public void init() { // Layout setLayout(null); // Generate the Choice control element Choice myChoice; myChoice = new Choice(); // Java supports TIFF, GIF, JPEG, BMP and PNG formats myChoice.addItem("Rohr.jpg"); myChoice.addItem("Kaufmann.png"); myChoice.addItem("Firouzi.gif"); myChoice.addItemListener(this); myChoice.select(0); // Fix the size of Choice and add it into the container myChoice.setBounds(0, 0, 300, 20); add(myChoice); Seite 45 5 Applets /* Read the image files */ imageRohr = Toolkit.getDefaultToolkit().getImage("Rohr.jpg"); imageKaufmann = Toolkit.getDefaultToolkit().getImage("Kaufmann.png"); imageFirouzi = Toolkit.getDefaultToolkit().getImage("Firouzi.gif"); // Choose the default image image = imageRohr; setSize(300, 180); } public void itemStateChanged (ItemEvent event) { System.out.println(event.getItem().toString()); if (event.getItem().equals("Beerli.jpg")) { image = imageRohr; } else if (event.getItem().equals("Kaufmann.png")) { image = imageKaufmann; } else { image = imageFirouzi; } repaint(); } public void paint(Graphics g) { g.drawImage(image, 0, 20, this); } } Hinweis Die Eigenschaften des Applets werden in der Methode init festgelegt. In diesem Beispiel wird zuerst der default Layoutmanager deaktiviert. Anschliessend wird die Auswahl-Komponente „myChoice“ erzeugt und oben im Applet eingefügt. Zusätzlich werden myCoice und ImageDemo respektiv als Quelle und Beobachter von Auswahlereignesse definiert. Zuletzt werden die Bilderdateien in den verschieden Formaten mit den der Methode getImage eingelesen. Als Beobachter muss ImageDemo die Methode itemStateChanged der Schnittstelle ItemListener implementieren. In dieser Methode wird nach dem Text des gewählten Auswahlpunktes ermittelt und anschliessend das entsprechende Bild als default Bild gesetzt. Die Methode paint gibt die default Bild auf dem Bildschirm aus. Seite 46 5 Applets Ausgabe Seite 47 6 Threads 6 Threads Threads erlauben verschiedene Aktivitäten gleichzeitig oder quasi gleichzeitig durchzuführen. Ein gutes Beispiel dafür ist der Vorgang des Lesens der Daten über das Internet, welcher mit einem Threads definiert werden kann. Damit kann das Programm immer noch auf andere Ereignisse von der Benutzeroberfläche reagieren. Auch wenn der Prozess des Lesens der Daten wegen mangelnden Daten blockiert ist. Applets sind zwar keineswegs statische Gebilde, dennoch bringen erst Threads Bewegung in Applets. Threads können als aktive Elemente dafür sorgen, dass „die Bilder laufen lernen“. Im obigen Applet wird zum Beispiel eine Schrift zum tanzen gebracht. Dabei wird ein Text in periodischen Abständen ausgegeben. Jeder einzelne Buchstabe bewegt sich in der y-Richtung (Höhe) hin und her, und die Farben der Buchstaben wechseln sich ab. In Java können Threads auf zwei Arten definiert werden: • Als Unterklasse von Thread • Durch Implementierung der Schnittstelle Runnable Die Klassen, welche die Haupteigenschaften von einem Thread enthalten, können als Unterklasse von Thread definiert werden. Dagegen müssen GUI-Objekte, welche als Unterklasse irgendwelcher GUI-Klassen definiert worden sind, die Runnable- Schnittstelle implementieren. Dies ergibt sich aus der Tatsache, dass Java keine Mehrfachvererbung kennt. Die wichtigsten Thread- Methoden sind start, run und sleep. Nachdem ein Thread über seinen Konstruktor erzeugt wurde, muss seine start Methode aufgerufen werden. start veranlasst einen Aufruf von run, wodurch das Betriebssystem diesen Thread in seiner Liste von wartenden Threads hinzufügt. Nach einer Zeit erhält er beim Prozessor seine Chance und beginnt selbstständig mit seiner Ausführung. Die run- Methode ist die eigentliche Thread- Routine und muss überschrieben werden. Sie besteht in der Regel aus einer Schleife, welche solang läuft, bis irgendwelche Bedingungen erfüllt sind. sleep ist eine Klassenmethode, welche die angegebene Wartezeit in Millisekunden ausführt. Programmbeispiel import java.awt.*; import java.applet.*; public class DancingText extends Applet implements Runnable { private Thread thread; private boolean stopThread = false; private static final Color [] Palette = { Color.black, Color.blue, Color.cyan, Color.darkGray, Color.gray, Color.green, Color.magenta, Color.orange, Color.pink, Color.red, Color.white, Color.yellow }; public void start() { System.out.println("start"); if (thread == null) { setSize (360, 80); thread = new Thread (this); stopThread = false; thread.start(); } } Seite 48 6 Threads public void run () { while (thread != null){ if (stopThread == true) return; repaint(); try { Thread.sleep (200); } catch (InterruptedException e) { } } } public void paint(Graphics g) { System.out.println("paint"); char text[] = {'D','a','n','c','i','n','g',' ','T','e','x','t'}; int height = 40; int width = 20; g.setFont (new Font("Helvetica", Font.BOLD, 20)); for (int i = 0; i < text.length; i++) { int start = (int)Math.round (20 * Math.random ()); g.setColor (Palette[start % Palette.length]); g.drawChars (text, i, 1, i * width, height + start); } } public void stop() { System.out.println("stop"); stopThread = true; thread = null; } } Hinweise Das Programm ist ein Applet, welches eine Thread implementiert. Dies wird durch die Schreibweise public class DancingText extends Applet implements Runnable Zum Ausdruck gebracht. Im obigen Beispiel verhält sich DancingText als ein Thread, indem es die Runnable- Schnittstelle implementiert. Da DancingText kein Thread ist, benötigt es eine Instanz von Thread. Deshalb wird ein Thread in der start- Methode erzeugt, welches mit dem Applet durch den Aufruf des Konstruktohrs thread = new Thread (this); verbunden wird. Damit kann die run- Methode des Threads als eigener Thread laufen und, wie alle andere Methoden, auf alle Komponenten von DancingText zugreifen. Die Art der Anbindung der run- Methode in DancingText ist für jede abgeleitete Klasse erforderlich, da Java keine Mehrfachvererbung kennt. Diese Technik des „Durchreichens“ von Aufträgen an eine andere Klasse wird auch als „Delegation“ bezeichnet. Die run- Methode im obigen Beispiel enthält eine Schleife, in welcher die repaint- Methode nach einer gewissen Zeitspanne aufgerufen wird. Damit wird in periodischen Abständen das Fenster vollständig neu aufgebaut. Seite 49 6 Threads Skizze zum Ablauf Stoppe thread Starte thread TanzendeSchrift thread Zeitachse Die Threads DancingText und thread sind in der obigen Abbildung gestrichelt dargestellt worden. Am Anfang initialisiert die init- Methode das Applet und startet dessen Steuerungsthread. Jedes Mal, wenn das Applet von der Web-Seite aus wieder gestartet wird, generiert start eine neue Instanz von thread. Damit wird die Animation mit der tanzenden Schrift wieder im Gang gesetzt. Die Aktionen, welche von start angestossen worden sind, müssen von stop unterbrochen werden. Deshalb werden die erzeugten Threads von der stop- Methode entsorgt und deren Ressourcen wieder frei gegeben. Ausgabemöglichkeit Seite 50 7 Ausnahmen 7 Ausnahmen 7.1 Einführung Wenn Programme nicht auf Ausnahmesituation reagieren können, führt das zu den von dem Anwendern gefürchteten Abstürzen. Bei komplexen Programmsystemen und verteilten Anwendungen sind Programme ohne jede Reaktion auf Ausnahmensituation nicht akzeptabel. Die Behandlung von Ausnahmen mit den Sprachkonstrukten für den Normalfall hat sich nicht bewährt. So könnte man sich eine Behandlung von Ausnahmen im Rahmen von Abfragen mit if vorstellen. Jede Routine müsste dann entsprechende Fehlerschalter bzw. entsprechende Ergebnisse liefern. Bei dieser Vorgehensweise hat man eine völlig unübersichtliche Programmstruktur erhalten: Eine tiefe Schachtelung nach Aufrufen von Routinen, eine Verquickung von Programmcode für den Ablauf sowie für den Ausnahmenfall. Die Anfänge der Behandlung von Ausnahmen wurden mit dem setjmp / longjmp- Konzept von ANSI-C gelegt. Wegen der inhärenten Sicherheitsdefizite wurde diese Strategie in C++ zu einer strukturierten Fehlerbehandlung ausgebaut. In Java wurde von Anfang an die Behandlung von Ausnahmen integriert. Sie ermöglicht eine „weiche“ Landung nach Fehlern. So können Ausnahmen differenziert behandelt werden. Das Grundkonzept der Behandlung von Ausnahmen in Java folgt dem Schema: • Ausprobieren mit try • Auffangen mit catch • Auslösen mit throw 7.1.1 Programmbeispiel import java.lang.ArithmeticException; public class Test { public static void main (String[] args) { for (int i = -3; i < 3; i++) { try { System.out.println(1 / i); } catch (java.lang.ArithmeticException e) { System.err.println(e); } } } } Ausgabe 0 0 -1 java.lang.ArithmeticException: / by zero 1 0 Die Division durch 0 wurde mit dem try-catch- Mechanismus abgefangen. Der Programmlauf wird durch die Ausnahme nicht abgebrochen, sondern kann kontrolliert durch die Software fortgesetzt werden. Seite 51 7 Ausnahmen 7.2 Funktionsprinzip Der Mechanismus der Ausnahmenbehandlung besteht quasi aus einem Wideranlauf des Programms. Mit try wird eine Art Momentaufnahme des Zustands der Umgebung des Programms gemacht. Diese schliesst den Stack sowie die Register ein, aber in keinem Fall alle Daten, benutzten Dateien oder Verbindungen mit anderen Computersystemen. Dann wird der nach try in { ... } angegebene Block betreten. Tritt bei der Abarbeitung dieses Programmteils eine Programmausnahme auf, sei es explizit oder implizit vom Laufzeitsystem her, wird der Stack nach catch- Routinen durchsucht, bis eine Passende gefunden wird. Dies gilt auch in Unterprogrammen, sodass es, bildlich gesprochen, zu einem Zurückrollen des Stacks kommen kann. Dabei sind folgende Punkte zu beachten: • Es kann nur auf Stellen zurückgesprungen werden, die in noch nicht beendeten aufrufenden Methoden liegen, die auf dem Weg zu throw auch durchlaufen und noch nicht verlassen wurden. Den try/catch kann nicht mehr als den Stack zurückrollen und Inhalte von Register wiederherstellen. • Es werden keine Aktionen des Programms rückgängig gemacht, wie z.B. das löschen einer Datei, das Stornieren einer Bestellung oder das Abfeuern einer Rakete. 7.2.1 Eine mögliche Implementierung der Ausnahmenbehandlung Stack main Rücksprung nach Ausnahme Aufrufkette 7.3 Vorteile der Ausnahmebehandlung in Java Der Mechanismus der Behandlung von Programmausnahmen in Java erlaubt Programme zu definieren, ohne dass überall Abfragen auf mögliche Fehlerausgänge vorzusehen sind. Ausserdem gibt es eine klare Trennung zwischen funktionalem Code (if wird für Steuerung eingesetzt) und dem Code zur Fehlerbehandlung (trycatch bei Fehlerausgang). Ein weiteren Vorteil der Sprache Java bei der Behandlung von Ausnahmen liegt in der Sicherheit. Im Gegensatz zu C oder C++ werden alle angelegten Objekte im Rahmen der Garbage Collection automatisch vom Laufzeitsystem „entsorgt“. Javaprogramme müssen auf jede Situation gefasst sein, auch auf Ausnahmensituationen. Gerade bei einer Programmierung für das Internet sind Abstürze von Programmen nicht akzeptabel. Wenn ein Programm nur in einer „friendly atmosphere“ läuft, ist dies zu wenig. Deshalb muss jede sichere Javaprogrammierung die Behandlung von Fehlern einschliessen. Damit ist eine „weiche Landung“ nach Programmlauffehlern oder bei Anwenderfehlern immer möglich. 7.4 Klassen und Ausnahmen In Java werden die verschiedenen Typen von Programmausnahmen durch eine Klassenhierarchie abgebildet, welche ihre Wurzel in der Klasse java.lang hat. In dieser Klassenhierarchie werden die diversen Programmausnahmen durch Klassen modelliert. Seite 52 7 Ausnahmen Programme und Programmsysteme können sich der Klassifikation der Ausnahmen in Java anschliessen. Die Behandlung der Ausnahmen erfolgt dann wieder klassenweise. Im folgenden Beispiel wird eine Basisklasse Exception1 definiert. Als Spezialfall von Exception1 wird die Klasse Exception2 eingeführt. Die Methode Demo hat die Aufgabe, verschiedene Programmausnahmen auszulösen. 7.4.1 Programmbeispiel import java.lang.Exception; class Exception1 extends Exception { Exception1 (String text) { super(text); } } class Exception2 extends Exception1 { Exception2 (String text) { super(text); } } public class ExceptionDemo { void demo (int i) throws Exception1, Exception2, Exception { if (i == 1) { throw new Exception1 ("More information 1"); } else if (i == 2) { throw new Exception2 ("More information 2"); } else if (i == 3) { throw new Exception ("More information 3"); } } public static void main (String [] args){ ExceptionDemo e = new ExceptionDemo (); for(int i = 1; i <= 4; i++){ try { e.demo (i); } catch (Exception2 ex) { System.out.println (ex); } catch (Exception1 ex) { System.out.println (ex); } catch (Exception ex) { System.out.println (ex); } finally { System.out.println("Final treatment " + i); } } } } Ausgabe 1. 2. 3. 4. 5. 6. 7. Exception1: More information 1 Final treatment 1 Exception2: More information 2 Final treatment 2 java.lang.Exception: More information 3 Final treatment 3 Final treatment 4 Seite 53 7 Ausnahmen Hinweis Beachten Sie bitte, dass die for- Schleife im Programm trotz der Programmausnahmen dreimal durchlaufen wird. Dies ist nur möglich, da die Ausnahmen aufgefangen wurden. Zeile 1 zeigt, dass die Ausnahme Exception1 von der richtigen Stelle (b) im Programm aufgefangen wird. In Zeile 2 sieht man die Ausgabe der Abschlussbehandlung für die Programmausnahme mit finally. Mit dieser Anweisung kann man gemeinsamen Programmcode in verschiedenen catch- Zweigen der Ausnahmenbehandlungen schreiben, welcher aber auch dann durchlaufen wird, wenn keine Ausnahme vorliegt. Zeile 3 zeigt die Behandlung der zweiten Ausnahme analog zur ersten Ausnahme. Exception2 ist ein Spezialfall von Exception1. Wird eine Exception2 geworfen, wird sie als Execption1 behandelt. Zeile 4 enthält die Abschlussbehandlung. Wenn die Blöcke (a) und (b) im Quellprogramm vertauscht wären, würde sich folgende Situation ergeben: catch (Exception1 ex) System.out.println } catch (Exception2 ex) System.out.println } { (ex); (b) { (ex); (a) Das so geänderte Programm wird vom Java-Compiler nicht übersetzt, da die Behandlung von Exception1 die von Exception2 verdecken würde, da immer die erste passende Ausnahmebehandlung zum Auffangen gewählt wird. Zeile 5 der Ausgabe des Programms zeigt, dass finally keine Behandlung jeder Ausnahme darstellt, sondern lediglich der abschliessenden Ausnahmenbehandlung dient. Wenn eine pauschale Behandlung von Ausnahmen gewünscht wird, fängt man die Oberklasse der Ausnahme Exception auf. Dadurch würde man aber eine differenzierte Reaktion auf die Fehlersituation verzichten. Zeile 6 zeigt die zugehörige Abschlussbehandlung. Die Ausgabe der Zeile 7 erfolgt auch ohne Programmausnahme. Seite 54 8 Ein- und Ausgabe 8 Ein- und Ausgabe In dem Package „java.io“ bündelt das JDK die Ein-/Ausgabe in Java. Da Java eine Programmiersprache ist, welche speziell für Anwendungen im Internet entwickelt wurde, müssen Programme in Java mit diversen Quellen für die Ein-/Ausgabe rechnen: lokale Dateien, Tastatur, Dateien über das Internet. Zur Lösung dieses Problems benutzt man die Abstraktionen „java.io.InputStream“ zum lesen und „java.io.OutputStream“ zum schreiben von Daten. Diese Klassen enthalten die Definitionen aller Methoden zur grundlegenden Ein-/Ausgabe. Allerdings gibt es keine Exemplare von diesen abstrakten Klassen, sondern nur Ableitungen davon, wie z.B. die Klasse „java.io.FileInputStream“. Zur internen Darstellung von Zeichen benutzt Java den Unicode. Dateien oder Datenströme im Internet liegen dagegen häufig als ASCII-Code vor. Deswegen muss in Java eine Brücke zwischen diesen Welten geschlagen werden. Zur besseren Unterstützung der Internationalisierung durch den Unicode wurde ab Java 1.1 zusätzlich zu den Byte-orientierten Stream-Klassen für die Ein-/Ausgabe die Zeichen-orientierten Reader- bzw. WriterKlassen eingeführt. Die folgende Skizze zeigt die möglichen Richtungen für die Eingabe. Die Ausgabe entspricht diesem Schema mit umgekehrter Richtung der Verarbeitung: Bytes Stream: Bytes P r o g r a m m Reader: char InputStream Internet Daten DataInput: Daten Streams arbeiten Byte orientiert ohne Umformung. Reader bzw. Writer arbeiten mit Unicode-Zeichen. Ein InputStreamReader bzw. OutputStreamWriter schlägt eine Brücke zwischen den Byte- und den Unicode-orientierten Welten. DataInput bzw. DataOutput Darstellungen verarbeiten die Daten in binären Formaten für das Internet. So werden die Darstellungen von Zahlen im dualen System, in der im Internet festgelegte Reihenfolge für die einzelnen Bytes, abgelegt bzw. eingelesen. 8.1 Eingabe in Java 8.1.1 Eingabe mit der byteorientierten Stream-Klassen Die Klasse InputStream dient als Abstraktion für alle konkret möglichen Eingabeströme. In ihr werden nur einfachste Funktionen zum sequenziellen Lesezugriff definiert. Man kann einzelne Bytes oder Blöcke von Byte lesen. Es gibt keinerlei Methoden zur Umformung von Daten. Die wichtigsten Methoden der Klassen InputStream befinden sich in der nächsten Tabelle: void close(); int read(); int read( byte[] cbuf, int off, int len); Schliessen des Streams und Freigabe aller belegten Ressourcen. Lesen eines Bytes. Das Ergebnis wird als Zahl im Bereich von 0 bis 255 (hexadezimal 0x00 bis 0xff) zurückgeliefert. Der Wert –1 steht für das Ende der Datei. Aus dem Eingabestrom werden len Bytes eingelesen und in den Puffer cbuf ab dem Byte off abgelegt. Der Wert –1 codiert das Ende der Datei. Seite 55 8 Ein- und Ausgabe Die InputStream- Klassenhierarchie ist in der nachfolgenden Abbildung dargestellt. Zur Anwendung wird jeweils eine Instanz von diesen abgeleiteten Klassen angelegt, die irgendeinen konkreten Eingabestrom benutzt. java.io.InputStream java.io.ByteArrayInputStream java.io.FileInputStream java.io.ObjectInputStream java.io.PipedInputStream java.io.SequenceInputStream java.io.StringBufferInputStream javax.sound.sampled.AudioInputStream java.io.FilterInputStream java.io.BufferedInputStream java.io.DataInputStream java.io.LineNumberInputStream java.io.PuschbackInputStream java.security.DigestInputStream java.util.zip.ChekedInputStream javas.swing.ProgressMonitorInputStream java.util.zip.InflaterInputStream java.util.zip.GZIPInputStream java.util.zip.ZipInputStream java.util.zip.jarInputStream Die Konstruktoren und die Beschreibungen der wichtigsten Klassen, welche von InputStream abgeleitet sind, befinden sich in der folgenden Tabelle. ByteArrayInputStream(byte[] buf) FileInputStream(File file) PipedInputStream() StringBufferInputStream(String s) Mit der Klasse ByteArrayInputStream ist es möglich, auf ein Byte-Array genauso zuzugreifen, wie auf einen Eingabe-Stream. Diese Klasse stellt einen Stream zur Verfügung, mit welchem Dateien ausgelesen werden können Mit den Klassen PipedInputStream und PipedOutputStream können zwei Threads Daten austauschen, indem Exemplare dieser Klassen verkettet werden. Der StringBufferInputStream ermöglicht es, auf einen String genauso wie auf einen InputStream zuzugreifen. Die Parameterliste der obigen Konstruktoren zeigen welche Eingabeströme bei der Instanzierung verwendet werden können. Zum Beispiel bei der Anlage von einem Objekt der FileInputStream- Klasse kann der Dateiname als Eingabestrom angegeben werden: FileInputStream fis = new FileInputStream ("FileName"); 8.1.2 Eingabe mit der zeichenorientierten Reader-Klassen Zur Eingabe von Text in Programme ist im JDK der Weg über die Reader empfohlen, weil dabei die Umsetzung der Bytes des Eingabestroms in Zeichen gemäss den lokalen Einstellungen erfolgt. Zudem funktioniert die Eingabe am besten in gepufferter Form. Deshalb sind die wichtigsten Methoden der Klasse BufferedReader besonders interessant. Seite 56 8 Ein- und Ausgabe void close(); int read(); int read( char[] cbuf, int off, int len); String readLine(); boolean ready(); Schliessen des Streams. Lesen eines Zeichens. Das Ergebnis wird als Zahl im Bereich von 0 bis 65535 (hexadezimal 0x0000 bis 0xffff) zurückgeliefert. Der Wert –1 steht für das Ende der Datei. Aus dem Eingabestrom werden len Zeichen eingelesen und in den Puffer cbuf ab dem Zeichen mit dem Index off abgelegt. Die Methode gibt die Anzahl der gelesenen Zeichen zurück. Der Wert –1 codiert das Dateiende. Einlesen einer Zeile. Bei Dateiende wird null geliefert. Anzeige, ob der Eingabestrom bereit zum lesen ist. Dies ist dann der Fall, wenn der Puffer nicht leer oder der darunter liegende Zeichenstrom bereit ist. Die Reader-Klassenhierarchie ist in der nachfolgenden Abbildung dargestellt: java.io.Reader java.io.CharArrayReader java.io.PipedReader java.io.StringReader java.io.BufferedReader java.io.LineNumberReader java.io.FilterReader java.io.PushbackReader java.io.InputStreamReader java.io.FileReader Die Konstruktoren und die Beschreibung der wichtigsten Klassen, welche von Reader abgeleitet sind, befinden sich in der nächsten Tabelle: CharArrayReader(char[] buf) PipedReader() StringReader(String s) BufferedReader(Reader in) InputStreamReader(InputStream in) FileReader(String fileName) CharArrayReader bietet die Möglichkeit, aus einem char-Array genauso wie aus einem Stream zu lesen Diese Klasse ist das Reader-Äquivalent des PipedInputStream Die Klasse StringReader bietet die Möglichkeit, auf einen String wie auf einen Eingabe-Stream zuzugreifen. BufferedReader ist ein gepufferter Eingabe-Stream für Unicode-Zeichen. Dieser Stream arbeitet stets auf einem anderen Reader-Objekt, aus dem er die Daten bezieht. InputStreamReader bietet die Möglichkeit, bytebasierten InputStreamKlassen und die zeichenbasierten Reader- Klassen zu koppeln. Diese Klasse stellt einen Stream zur Verfügung, der eine Datei ausliest. Hierbei werden die gelesenen Bytes aus der Zeichencodierung der Plattform in Unicode-Zeichen umgesetzt. Codebeispiel Der folgende Programmausschnitt zeigt, wie man einen Reader für Datei erzeugt und die Datei zeilenweise liest. Dabei ist zu achten, dass dieser Programmschnitt ein java.io.IOException werfen kann. // Either in the standard way FileInputStream fis = new FileInputStream("name"); InputStreamReader isr = new InputStreamReader(fis); BufferedReader br = new BufferedReader(isr); Seite 57 8 Ein- und Ausgabe /* or in a nested way BufferedReader br = new BufferedReader( new InputStreamReader( new FileInputStream("name"))); */ /* or even in the short way with the FileReader class BufferedReader br = new BufferedReader ( new FileReader("name")); */ String s; while((s = br.readLine()) != null){ // Treat the line s } Hinweis Im obigen Beispiel werden die Instanzen fis, isr und br angelegt. fis ist ein Objekt der Klasse FileInputStream, welches die Datei „name“ als Eingabestrom verwendet. fis stellt einen Stream zur Verfügung, mit dem aus name ausgelesen werden kann. isr ist ein Objekt der Klasse InputStreamReader, welches wiederum fis als Eingabestrom verwendet. Damit können die eingelesenen Bytes in Unicodezeichen umgewandelt werden. br ist ein Objekt der Klasse BufferedReader, welches erneut isr als Eingabestrom verwendet. Damit können die Daten in einem Puffer zwischengespeichert werden. Die nachfolgenden Leseoperationen beziehen ihre Daten aus diesem Puffer, was Geschwindigkeitsvorteile bringen kann. Mit der Verschachtelung der obigen Instanzen wird ein Datenstrom erzeugt, dessen Quelle die Datei name ist. Dieser Strom verfügt über alle Attribute und Methoden der Klasse BufferedReader (wie z.B. readLine). Damit können die einzelne Zeile von der Datei gelesen werden. 8.1.3 Die Standard Eingabe UNIX und Windows sehen für Anwendungen eine Standard-Eingaberichtung vor. Diese wird oft auch mit stdin bezeichnet. Java Programme können über das static- Attribut System.in auf diesen InputStream zugreifen. Programmbeispiel Zum Einlesen von Zeichen von Tastatur wird einen InputStreamReader auf den Eingabestrom System.in angewandt. Mit einem Puffer wird die Eingabe effizienter. Wegen der Komplexität der Eingabe von Tastatur, empfiehlt sich die Kapselung des Zugriffs, wie im folgenden Beispiel gezeigt: import java.io.*; public class MyReadLine { static BufferedReader b = null; public static String readln() throws java.io.IOException { if(b == null){ b = new BufferedReader( new InputStreamReader(System.in)); } return b.readLine(); } Seite 58 8 Ein- und Ausgabe public static void main (String[] args) throws java.io.IOException { String s; while((s = MyReadLine.readln()) != null){ System.out.println (">" + s + "<"); } } } 8.2 Ausgabe in Java 8.2.1 Ausgabe mit der byteorientierten Stream-Klassen Die Basisklasse für Ausgabe ist java.io.OutputStream. Sie bittet aber nur Transferdienste für Daten ohne jede Umformung an. Die wichtigste angeleitete Klasse ist die PrintStream- Klasse. Diese Klasse enthält die print(...) bzw. println(...) Methoden für elementare Datentypen sowie für Objekte. Sie ist damit besonders nützlich, wenn es um die Ausgabe von Daten als Text geht, wie es z.B. bei Ausgabe auf die Konsole der Fall ist. Falls die Standartcodierung von Zeichen nicht ausreicht, sollte an Stelle eines PrintStreams ein PrintWriter eingesetzt werden. Beide Klassen enthalten folgende Methoden. void close(); void write(int c); int write ( char[] cbuf, int off, int len); void flush(); void print(xx d); void println(xx d); Schliessen des Streams und Freigabe aller belegten Ressourcen. Schreiben des Zeichens in den niederwertigen 16 Bits von c. Die 16 höherwertigen Bits werden ignoriert. Schreiben von len Zeichen aus dem Feld cbuf ab dem Zeichen mit dem Index off. flush soll das System veranlassen, vorher geschrieben Bytes tatsächlich in den Ausgabestrom zu schreiben. Falls in einer Datei nach Schreibvorgängen Daten fehlen, ist flush eine gute Wahl. xx ist einer der elementaren Datentypen, ein String oder ein Objekt. Bei elementaren Datentypen werden die übergebenen Daten in Text umgewandelt und ausgeben. Ein Objekt wird mit der Methode String.valueOf(Object) in Text umgewandelt und ausgegeben. Wie print(xx d). Aber es wird zusätzlich eine neue Zeile begonnen. Die OutputStream -Klassenhierarchie ist in der nachfolgenden Abbildung dargestellt: java.io.OutputStream java.io.ByteArrayOutputStream java.io.FileOutputStream java.io.ObjektOutputStream java.io.PipedOutputStream java.io.FilterOutputStream java.io.BufferedOutputStream java.io.DataOutputStream java.security.DigestOutputStream java.util.zip.CheckedOutputStream java.io.PrintStream java.rmi.server.LogStream java.util.zip.DeflaterOutputStream java.util.zip.GZIPOutputStream java.util.zip.ZipOutputStream java.util.jar.JarOutputStream Die Konstruktoren und die Beschreibung der wichtigsten Klassen, welche von OutputStream abgeleitet sind, befinden sich in der nächsten Tabelle: Seite 59 8 Ein- und Ausgabe ByteArrayOutputStream() FileOutputStream(File file) PipedOutputStream() Mit der Klasse ByteArrayOutputStream können Schreibzugriffe auf ein Byte-Array genauso wie auf einen AusgabeStream erfolgen Diese Klasse stellt einen Stream zur Verfügung, mit dem Ausgaben in eine Datei gemacht werden können. Mit den Klassen PipedInputStream und PipedOutputStream können zwei Threads Daten austauschen, indem Exemplare dieser Klassen verkettet werden. 8.2.2 Ausgabe mit der zeichenorientierten Writer-Klassen Die Writer- Klassenhierarchie ist in der nachfolgenden Abbildung dargestellt: java.io.Writer java.io.BufferedWriter java.io.CharArrayWriter java.io.FilterWriter java.io.PipedWriter java.io.PrintWriter java.io.StringWriter java.io.OutputStreamWriter java.io.FileWriter Die Konstruktoren und die Beschreibung der wichtigsten Klassen, welche von Writer abgeleitet sind, befinden sich in der nächsten Tabelle: BufferedWriter(Writer out) CharArrayWriter() PipedWriter() StringWriter() OutputStreamWriter(OutputStream out) FileWriter(String fileName) BufferedWriter ist ein gepufferter Ausgabe-Stream für Unicode-Zeichen. Er arbeitet stets auf einem anderen Writer- Objekt, in das er die Daten schreibt CharArrayWriter bietet die Möglichkeit, in ein charArray genauso wie in einen Stream zu schreiben. Die Klasse PipedWriter ist das Writer- Äquivalent des PipedOutputStream. Die Klasse StringWriter bietet die Möglichkeit, in einen String genauso wie in einen Stream zu schreiben. OutputStreamWriter ist die Basis für Writer-Klassen, die auf OutputStreams arbeiten. Diese Klasse stellt einen Stream zur Verfügung, mit dem Unicode-Zeichen in Dateien geschrieben werden können. 8.2.3 Die Standard-Ausgabe UNIX und Windows sehen für Anwendungen eine Standard-Ausgaberichtung vor. Diese wird oft auch mit stdout bezeichnet. Java-Programme können über das static- Attribut System.out auf diesen PrintStream zugreifen. Für Fehlermeldungen ist dagegen die Ausgangsnachricht stderr, die in Java über System.err ansprechbar ist, besser geeignet. Denn mit der Umlenkung java Program > FileName wird die Ausgabe des Programms nach stdout in die durch „Dateiname“ angegebene Datei umgelenkt, nicht aber die Ausgabe nach stderr. 8.3 Anwendungs-Beispiele In diesem Kapitel sollen einige typische Situationen aus der Anwendung der Dateiverarbeitung besprochen werden. Sie werden mit den Hilfsmitteln gelöst, welche die Bibliothek java.io zur Verfügung stellt. Seite 60 8 Ein- und Ausgabe 8.3.1 Byteweise Verarbeitung von Dateien Ein Dateiname soll vom System.in eingelesen werden. System.in ist ein InputStream. Die Datei mit dem eingegebenen Namen soll geöffnet werden. Danach soll die Datei byteweise gelesen und ausgegeben werden. Vorgehen Zunächst wird der Dateiname eingelesen. Dann wird versucht, eine Datei dieses Namens zu öffnen. Dabei muss die Programmausnahme java.io.FileNotFoundException behandelt werden, denn die Datei könnte nicht vorhanden sein. Danach wird die Eingabedatei in der klassischen Schleife bearbeitet: while (!End of the file(inputFile)) { Read the Byte as int; Write the Byte; } Jedes Byte muss als int gelesen werden, um die EOF-Kennung –1 (End Of File) verarbeiten zu können: Da ein Byte alle Werte von 0 bis 255 annehmen kann, könnte in den 8 Bit des Bytes die EOF-Kennung nicht verschlüsselt werden. Beim lesen der Datei sowie beim Schliessen müssen die möglichen java.io.IOException- Ausnahmen bearbeitet werden. Das Programm gibt alle Anfragen nach System.err aus. Dadurch kann man das Programm auch zum Kopieren von Dateien verwenden, wenn man es in folgender Form aufruft. Der Name der Ausgangsdatei wird angefordert, das Ergebnis wird in die Datei „DestinationFile“ gespeichert. java ReadFileBytes > DestinationFile Programmbeispiel import java.io.*; public class ReadFileBytes { public static void main (String args[]) { // Read the file name System.err.print("File name?: "); String Filename = null; try { Filename = MyReadLine.readln(); } catch (IOException e) { System.err.println(e); return; } System.err.println("File name " + Filename); // Open the file. // Be careful, the given file could not exist! FileInputStream f = null; try { f = new FileInputStream(Filename); } catch (FileNotFoundException e) { System.err.println("File " + Filename + " has not been found!"); return; } Seite 61 8 Ein- und Ausgabe // Read the file contains int ch; try { while ((ch = f.read()) != -1) { System.out.write(ch); } f.close(); } catch (IOException e) { System.err.println(e); return; } } } 8.3.2 Blockweise Verarbeitung von Daten Eine Datei soll binär kopiert werden. Sie darf nicht auf sich selbst kopiert werden, denn wenn sie vor dem ersten Lesen zum Schreiben geöffnet würde, wäre der Inhalt gelöscht. Vor Überschreiben einer vorhandenen Datei soll beim Anwender um eine ausdrückliche Bestätigung angefragt werden. Alle Programmausnahmen bei der Ein/Ausgabe sollen abgefangen werden. Vorgehen Es soll ein FileInputStream bzw. ein FileOutputStream benutzt werden. Damit wird eine binäre Kopie von Dateien möglich. Es ist effizienter, eine Datei blockweise zu bearbeiten. Die Namen der Quell- und der Zieldatei sollen in der Kommandozeile übergeben werden. Wenn eine Datei auf sich selbst kopiert wird, führt dies bei vielen Systemen zum löschen der Datei. Um dies zu vermeiden, muss auf Gleichheit der Namen der Quell- bzw. der Zieldatei abgefragt werden. Mansche Betriebssysteme ignorieren Unterschiede bei der Grossbzw. Kleinschreibung von Dateinamen. Java bietet einen Vergleich von Strings an, der dies ignoriert. Mit der File- Klasse kann man prüfen, ob die Zieldatei vorhanden ist. Wenn dies der Fall ist, wird beim Anwender angefragt, ob die Datei wirklich überschreiben werden soll. Wenn der Anwender des Programms im Aufruf die Argumente Quelle und Ziel verwechselt, würde mit dieser Abfrage die automatische Überschreibung einer Datei verhindert. Nach dieser umfangreichen Vorbereitung kann in einer read-write- Schleife kopiert werden. Programmbeispiel import java.io.*; public class TreatFileBlock { final public int BUFSIZE = 80; public void copy (String Source, String Destination) { FileInputStream Input = null; FileOutputStream Output = null; if (Source.equalsIgnoreCase(Destination)) { System.err.println("Error: fill will be copied into himself"); // Windows needs ignore case return; } Seite 62 8 Ein- und Ausgabe File file = new File (Destination); if(file.exists()) { System.out.print("File " + Destination + " exists. Over write? (Y/N): "); System.out.flush(); try { char ch = (char)System.in.read(); if(Character.toUpperCase(ch) != 'Y') { return; // Break in case of doubt } } catch(IOException e) { return; } } try { Input = new FileInputStream(Source); } catch(FileNotFoundException e) { System.err.println("Error : Source file " + Source + " could not be found"); } try { Output = new FileOutputStream(Destination); } catch (FileNotFoundException e) { try { Input.close(); } catch (IOException e1) { System.err.println("Error : can not close the file " + Destination); return; } System.err.println("Error : can not close the file " + Source); return; } int nbytes = -1; byte b[] = new byte[BUFSIZE]; try { while ((nbytes = Input.read(b, 0, BUFSIZE)) != -1) { Output.write(b, 0, BUFSIZE); } } catch(IOException e) { System.err.println("Error during the copy process"); return; } } public static void main(String[] args) { if(args.length != 2) { System.err.println("Call TreatFileBlock: Source and Destination"); System.exit(1); } TreatFileBlock tfb = new TreatFileBlock (); tfb.copy(args[0], args[1]); } } 8.3.3 Daten im Format für das Internet verarbeiten Das Format der Daten für das Internet soll besprochen werden. Wichtige Routinen zum Schreiben auf der einen Seite und zum Lesen auf der anderen Seite sollen behandeln werden. Das Beispiel kann nicht dazu dienen, Daten Seite 63 8 Ein- und Ausgabe in der Textdarstellung ein bzw. auszugeben. Vorgehen Die Klasse DataInputStream implementiert die DataInput- Schnittstelle zur Eingabe von Daten. Die Daten werden dabei umgeformt. In der Datei müssen z.B. Zahlen im Internet-Format vorliegen. Damit ist ein Austausch von Daten auch mit Programmen möglich, die in C oder anderen Programmiersprachen geschrieben wurden, sofern die Daten dort in das entsprechende Format umgesetzt werden. Die Daten liegen dann in einem Format vor, das nicht vom Rechner abhängt. Programmbeispiel import java.io.*; public class IODemo { // Write the binary data into the file Data.txt void testOutput() { try { DataOutputStream out = new DataOutputStream( new FileOutputStream("Data.txt")); out.writeBoolean (true); out.writeByte (1); out.writeChar ('a'); out.writeDouble (1.0); out.writeFloat (1.0f); out.writeInt (1000); out.writeLong (2000l); // not 20001, but 2000l out.writeShort (300); out.writeBytes ("writeBytes"); } catch (IOException e) { System.err.println(e); } } // Write the binary data with their type name into the file Data.txt void testOutputText() { try { DataOutputStream out = new DataOutputStream( new FileOutputStream("Data.txt")); out.writeBytes ("Boolean") ; out.writeBoolean (true); out.writeBytes ("Byte") ; out.writeByte (1); out.writeBytes ("Char") ; out.writeChar ('a'); out.writeBytes ("Double") ; out.writeDouble (1.0); out.writeBytes ("Float") ; out.writeFloat (1.0f); out.writeBytes ("Int") ; out.writeInt (0x12345678 ); out.writeBytes ("Long") ; out.writeLong (2000l); out.writeBytes ("Short") ; out.writeShort (300); } catch (IOException e) { System.err.println(e); } } Seite 64 8 Ein- und Ausgabe /* Read the binary data from the file Data.txt, and write its contents on the screen */ void testInput () { try { DataInputStream in = new DataInputStream( new FileInputStream("Data.txt")); System.out.println (in.readBoolean()); System.out.println (in.readByte()); System.out.println (in.readChar()); System.out.println (in.readDouble()); System.out.println (in.readFloat()); System.out.println (in.readInt()); System.out.println (in.readLong()); System.out.println (in.readShort()); byte [] Bytes = new byte [100]; int ibytes = in.read(Bytes, 0, Bytes.length -1); System.out.println(ibytes + ">" + new String (Bytes, 0, ibytes -1) + "<"); } catch (IOException e) { System.err.println(e); } } public static void main (String[] args) { if (args.length == 0) { System.err.println ("Call IODemo: (read|bin|text)"); System.exit(1); } IODemo iodemo = new IODemo (); if (args[0].equalsIgnoreCase ("bin")) iodemo.testOutput (); if (args[0].equalsIgnoreCase ("text")) iodemo.testOutputText (); if (args[0].equalsIgnoreCase ("read")) iodemo.testInput (); } } Seite 65 Index Index < byteorientierte Stream-Klasse ..........................................55 </APPLET> ........................42 <APPLET>...........................42 C A Abstract Windows Toolkit .....4 ActionEvent........24, 33, 45 ActionListener ......26, 45 actionPerformed ....26, 45 Adapter .............................17 Adapter-Klassenhierarchie ...21 add...............................5, 8, 14 addActionListener .....24 addAdjustmentListener ..........................................31 addItem .............................26 addItemListener ..........27 addxxListerner ............17 AdjustmentEvent ..........31 AdjustmentListener...33 adjustmentValueChange d .......................................33 Anordnung der Komponenten 7 Anpassungsereignis ..............31 Applet ...........................4, 37 Applets ...................................4 Appletviewer ........................38 ASCII ...................................55 AudioClip ........................46 Ausnahmen...........................51 Auswahl................................26 B Beobachter von Aktionsereignissen ...........25 Beobachter von Anpassungsereignis ..........32 Beobachter von Auswahlereignissen..........27 Beobachter von Ereignissen .20 Beobachter von Fensterereignissen ......18, 21 Beobachter von Textereignissen.................31 BorderLayout ...................8 BorderLayout.CENTER ..9 BorderLayout.EAST .......9 BorderLayout.NORTH.....9 BorderLayout.SOUTH.....9 BorderLayout.WEST .......9 Browsers...............................37 BufferedReader ......57, 58 BufferedWriter ............61 Button ...............................24 Byte orientiert.......................55 Canvas ...............................22 CardLayout ........................8 catch............................51, 52 Checkbox.......................7, 28 Checkboxen......................7, 28 CheckboxGroup...............28 CheckboxMenuItem........34 Choice ...........................6, 26 close..................................56 Color..................................22 Component ..........................5 Container ................4, 5, 14 Controls ............................6, 16 D DataInput ........................64 DataInputStream ..........64 Delegation Event Mode........17 destroy .............................40 drawImage ........................47 drawLine...........................22 drawOval...........................22 drawRect...........................22 drawString ......................22 E Ereignis.................................17 Ereignis Beobachter .............17 Ereignis Quelle .....................17 Ereignissteuerung ...........17, 18 EventObject....................17 Exeption...........................54 F FileInputStream ...56, 58, 62 FileOutputStream..60, 62 FileReader ......................57 FileWriter ......................61 fillOval...........................22 fillRect...........................22 finaly ...............................54 first............................14, 15 FlowLayout ....................8, 9 FlowLayout.CENTER .......9 FlowLayout.LEADING.....9 FlowLayout.LEFT ............9 FlowLayout.RIGHT..........9 FlowLayout.TRAILING...9 for.......................................54 Frame....................................4 Seite 66 G Garbage Collection...............52 getActionCommand........24 getAudioClip .................46 getCodeBase....................47 getHeight ........................38 getItem .............................26 getParameter .................42 getSize ...............................5 getSource ........................24 getText .............................29 getWidth...........................38 Graphical User Interface.........4 Graphics...............22, 38, 47 GridBagConstraints ...11 GridBagConstraints.RE MAINDER .........................13 GridBagLayout...........8, 11 gridheight ................12, 13 GridLayout ......................10 gridwidth ..................12, 13 GUI.........................................4 H Hierarchie der Ereignisse .....17 HTML...................................37 I if .........................................51 Image..................................47 init ..............................39, 45 InputStream....................55 InputStreamReader ....55, 57, 58 interpretiert ...........................37 ItemEvent ........................26 ItemListener ...........27, 29 itemStateChanged..27, 29 J Java Virtual Machine......37, 41 java.io .............................55 java.io.FileInputStre am .....................................55 java.io.FileNotFoundE xception.......................61 java.io.InputStream.55 java.io.OutputStream ..........................................55 JDK ........................................4 JVM......................................41 L Label..................................29 last ..............................14, 15 Laufzeitsystem......................22 Index Layout Manager ...........4, 7, 15 Listener.................................17 longjmp .............................51 loop ....................................46 Low-Level Ereignis..............19 Low-Level-Beobachter.........20 Quelle von Textereignissen ..31 Quellen von Ereignissen.......19 R main ......................................5 Menu ....................................34 MenuBar .............................33 MenuItem...........................34 Menüleiste ............................33 multi-cast..............................17 Radiobuttons.........................28 read ....................................56 Reader .........................55, 57 readLine.....................57, 58 remove .................................5 rePaint ...............................5 Rollbalken ....................6, 7, 31 run.......................................48 Runnable...........................48 runtime .................................22 N S next ..............................14, 15 Schachtelung der Layout ......15 Schalter.............................6, 24 Scrollbar ....................7, 31 sdtout ...............................61 select ...............................26 Semantic-Level Ereignis ......19 Semantic-Level-Beobachter .20 setColor...........................22 setConstraints ............13 setFont .............................22 setjmp ...............................51 setLayout ....................6, 10 setMenuBar ......................34 setSize ...............................5 setText .............................29 setVisible ........................5 setxxListerner ............17 single-cast.............................17 sleep..................................48 Source...................................17 start............................39, 48 stdin..................................58 Steuerelementen .....................6 stop ..............................40, 46 Streams .................................55 M O OutputStream .................59 OutputStreamWriter..55, 61 P paint.................. 5, 22, 38, 40 Panel....................................4 PARAM..................................42 play ....................................46 Pop-Up-Menü...................6, 26 print..................................59 println .............................59 PrintStream....................59 Q Quelle von Aktionsereignissen ..........................................25 Quelle von Anpassungsereignis ..........................................32 Quelle von Auswahlereignissen ..........................................27 Quelle von Fensterereignissen ....................................18, 21 Seite 67 Swing......................................4 System.err ......................61 System.exit(0) ......18, 21 System.in ........................58 System.out ......................61 T TextArea.......................7, 30 TextComponent...........7, 29 TextEvent ..................30, 31 TextField ....................7, 30 TextListener ...........30, 31 textValueChanged..30, 31 Thread ...............................48 throw............................51, 52 try.................................51, 52 Typen von Ereignissen .........19 U Unicode ................................55 Universal Resource Locator .41 URL......................................41 W Web-Seite .............................37 Window .................................4 WindowAdapter.........21, 26 windowClosing...18, 21, 26 WindowEvent....................18 WindowListener ............18 Writer ...............................55 WWW-Protokoll ..................41 X xxListerner....................17 Z zeichenorientierte ReaderKlasse ...............................57