Skript zum Kurs Programmieren in Java v1.1/SS98 ©’97,98, Stefan Berner, Stefan Joos Forschungsgruppe Requirements Engineering Institut für Informatik der Universität Zürich Programmieren in Java 02.Apr.98 1/120 … I know you believe you understand what you think I said, but I am not sure you realize that what you heard is not what I meant … Programmieren in Java 02.Apr.98 2/120 Vorwort Dies ist die erste überarbeitete Version des Skripts zum Kurs “Programmieren in Java“. Aufgrund einiger konkreter Anregungen im letzten Kurs, ist nun im Übungsteil ein zusammenhängendes Beispiel hinzugekommen. Schwerpunkt dieser Veranstaltung ist (und bleibt) die Programmiersprache Java und nicht die Virtuelle Maschine oder die Klassenbibliothek. Die Beispiele und Übungen sind im Wesentlichen abgestimmt auf Java 1.0.x. Sie in der Kürze der zur Verfügung stehenden Vorbereitungszeit auf Java 1.1.x umzustellen, war nicht möglich; ist aber auch nicht unbedingt notwendig, da der Schwerpunkt der Veranstaltung die Programmiersprache Java ist. Hier haben sich von Java Version 1.0.x auf Java Version 1.1.x nur kleinere Änderungen ergeben. Damit das Skript nicht allzu dick wird, ist der Code oft dichter gesetzt und spärlicher kommentiert, als es in der Praxis sinnvoll wäre. Das Skript ist zur Zeit weder vollständig noch fehlerfrei. Syntaktische und orthographische Mängel bitte ich daher zu entschuldigen. Für kritische oder konstruktive Hinweise bin ich wie immer sehr dankbar. Stefan Berner Programmieren in Java 02.Apr.98 3/120 Tips und Vorabbemerkungen zum Kurs • aktiv mitarbeiten • fragen • ausprobieren • üben Programmieren in Java 02.Apr.98 4/120 Inhaltsübersicht • Einleitung • Variablen, Bezeichner, Typsystem • Einfache Datentypen • Komplexe Datentypen • Variablendeklaration • Sichtbarkeit und Lebensdauer • Speicherverwaltung • Ausdrücke und Operatoren • Steueranweisungen • Methoden • Ausnahmebehandlung • Threads und Synchronisation Programmieren in Java 02.Apr.98 5/120 Einleitung 1 Einleitung 1.1 Die Java Plattform Die Java Plattform besteht aus drei Komponenten: • Programmiersprache ♦ eben eine Programmiersprache • Virtuelle Maschine, Ausführungsmodell ♦ ein durch Software simulierter Rechner ♦ eine virtuelle Maschine ist ein Programm, welches einen Rechner simuliert und auf dem eigentlichen Betriebssystem eines realen Rechners aufsetzt • Klassenbibliothek, Entwicklungsumgebung ♦ eine Klassenbibliothek ist eine Sammlung von Klassen, die bestimmte (häufig benötigte) Dienste zur Verfügung stellen ♦ vergleichbar den Diensten, welches ein Betriebssystem zur Verfügung stellt Schwerpunkt in diesem Kurs ist die Programmiersprache Java. Auf die Virtuelle Maschine und das Ausführungsmodell wird nur kurz eingegangen. Der Inhalt der Klassenbibliothek wird soweit angeschnitten, wie es zum Verständnis der Sprache (→ Paket java.lang) oder der Beispiele notwendig ist. Programmieren in Java 02.Apr.98 6/120 Einleitung 1.2 Die Programmiersprache Java 1.2.1 Entwurfsziele von Java Die Programmiersprache Java ist: • einfach, da → objektorientiert (siehe Abschnitt 1.3) → weniger komplex als C/C++ • keine Typedef-, Define- und Preprocessor Anweisungen • kein struct und union • keine Funktionen und Prozeduren, sondern nur Methoden • keine Mehrfachvererbung, sondern Interfaces • keine goto’s • kein Überladen von standard Operatoren • keine automatische bzw. implizite Typkonversion • “keine Zeiger” • Robust und sicher, da → strenge Typung → umfangreiche statische Prüfung (z.B. Typverträglichkeit) → effiziente dynamische Prüfung (z.B. Arrays) → keine “legale“ Zeigerarithmetik → Exeptions → automatische Speicherverwaltung, Garbage Collection • Portabel, da → Virtuelle Maschine → Basistypen genau definiert → standardisierte Klassenbibliothek • Multithreaded, da → Threads und Synchronisationsprimitiven Programmieren in Java 02.Apr.98 7/120 Einleitung 1.3 Objektorientierung ( … Wiederholung, Einschub … ) Java ist eine objektorientierte Sprache. Objektorientiert steht hierbei für: • Objekte • Klassen • Nachrichten und Methoden • Vererbung • Polymorphie/Redefinition 1.3.1 • Objekte Modell eines realen oder imaginären Gegenstandes ♦ dieses Modell besteht aus zwei Teilen • Operationsraum • Datenraum → ein Objekt realisiert eine Datenkapsel • ein objektorientiertes Softwaresystem besteht aus Objekten, welche mittels Nachrichten (s.u.) kommunizieren und so die Aus-/Durchführung von Operationen/Methoden (s.u.) anstossen 1.3.2 • Klassen eine Klasse realisiert einen abstrakten Datentyp, d.h. Klassen sind eine Art “Bauplan“ oder “Fabrik“ für Objekte → Von Klassen können beliebig viele Objekte erzeugt werden • Klassen definieren die Schnittstelle, die Implementierung der Methoden und den Aufbau des Datenraums von Objekten • ein Objekt ist immer Instanz genau einer Klasse und hat genau die durch die Klasse definierten Eigenschaften ♦ Eigenschaften einer Klasse sind entweder neu oder ererbt oder redefiniert Programmieren in Java 02.Apr.98 8/120 Einleitung 1.3.3 • Nachrichten und Methoden Nachrichten fordern ein Objekt zur Durchführung einer Methode/Operation auf Nachricht passende Methode nein ? ja Superklasse ja ? Weiterreichen an Superklasse nein Fehler(meldung) Nachricht an Superklasse Nachricht bearbeiten • Nachrichten an andere Objekte eine Nachricht besteht aus 3 Teilen ♦ Empfänger … ein Objekt ♦ Selektor … der Methoden- bzw. Operationsname ♦ Argumente … Objekte • eine Methode/Operation hat entweder lokale Effekte (manipuliert lokale Daten/ändert Objektzustand), z.B. aList.sort( “ascending“ ); aTrafficLight.switchToRed(); • und/oder globale Effekte (d.h. Empfängerobjekt schickt Nachrichten an andere Objekte). Nachrichten an andere Objekte können deren Objektzustand ändern, z.B. aFox.eat( aDuck ); • die Semantik einer Methode/Operation kann vom Zustand des Objekts abhängen, z.B. aList.insert( 3, 4, 5, 6 ); aList.at( 2 ); aList.deleteAll(); aList.at( 2 ); Programmieren in Java 02.Apr.98 9/120 Einleitung 1.3.4 Vererbung • Gemeinsame Eigenschaften (Datenattribute und Methoden) von Objekten sollen nur einmal definiert werden • Klassen werden in Beziehung gesetzt: Unter-/Oberklassen-Beziehung ergibt Klassenhierarchie • Klasse »erbt« Attribute und Methoden der Oberklasse → transitiv werden Eigenschaften von allen Vorfahren in der Klassenhierarchie übernommen • Für jede Klasse in der Hierarchie können zusätzliche Datenattribute oder Methoden definiert werden 1.3.5 Polymorphie/Redefiniton • Ererbte Methoden (oder Attribute) können in der Unterklasse anders als in der Oberklasse (re-)definiert werden • Ermöglicht Anpassung der geerbten Attribute und Methoden auf spezifische Invarianten der Unterklasse Beispiel: Geometrisches Element Kreis A B A ist Unterklasse von B Programmieren in Java Vieleck Dreieck 02.Apr.98 Viereck 10/120 Einleitung 1.4 Ein erstes Java-Beispiel class Circle { double radius; // Klassendeklaration; Klasse Circle // Anfang Klassenrumpf // Instanzvariablendeklaration Circle( double radius ) { this.radius = radius; } // Konstruktormethode // Anfang Methodenrumpf // Zuweisung an Instanzv. // Ende Methodenrumpf double area() // Instanzmethode; berechnet Flächeninhalt { return ( Math.pow( radius, 2 ) * Math.PI ); } } // Klassendeklaration; Klasse Application class Application { public static void main( String args[] ) { Circle aCircle = new Circle( 42d ); System.out.println( aCircle.area() ); } } Programmieren in Java 02.Apr.98 11/120 Einleitung • this referenziert das Objekt selbst → this.radius … zugewiesen wird der Instanzvariable radius und nicht dem Methodenparameter radius • Alle Klassen des Pakets java.lang werden automatisch importiert → Math wird automatisch importiert, da im Paket java.lang • Kommentare ♦ der Inhalt von Kommentaren wird vom Übersetzer ignoriert ♦ drei Varianten: // Einzeiliger Kommentar /* Mehrzeiliger Kommentar */ /** JavaDoc Kommentar */ ♦ zu den gebräuchlichsten und auch wichtigsten Dokumentationsmitteln für Code zählt der Kommentar. Mitunter tragen Kommentare entscheidend zum schnellen Verständnis von Code und damit zum einfachen Ändern, Erweitern, Lesen, Portieren, Warten etc. von Software bei. • Wird das Programm ausgeführt (java Application), dann wird die Methode public static void main( String args[] ) nach dem Laden der Klasse automatisch aufgerufen 1.4.1 Entwicklungszyklus und Ausführung des Beispiels 1. Java-Quellcodedatei mittels Texteditor (emacs, sniff, textedit etc.) erstellen bzw. ändern 2. Datei übersetzen (z.B. javac Application.java) 3. Nach erfolgreicher Übersetzung die VM starten und zu ladende Startklasse angeben (z.B. java Application) 4. … siehe Schritt 1 … Programmieren in Java 02.Apr.98 12/120 Einleitung 1.4.2 Programmstruktur package ein.paket; import import angabe.der.importe; … class EineKlasse { static EinTyp EineKlassenVariable; NochEinTyp eineInstanzVariable; void eineMethode( … ) { eineInstanzVariable.einMethodenAufruf( … ); … } } interface EinInterface { … } Programmieren in Java 02.Apr.98 13/120 Einleitung Pakete package ein.paket.etc. … .etc; • gliedern eng kooperierende Klassen • definieren einen Namensraum/Gültigkeitsbereich für Klassen, Methoden und Variablen(-bezeichner) • Paktetangabe dient auch als Pfadinformation für die Ablage übersetzter Klassen (sog. class files) • die Klassen des Pakets dürfen auf verschiedene Dateien verteilt sein Importe import ein.paket.etc. … .klasse; ♦ “.*” importiert alle Klassen des Pakets • geben die benutzten Klassen und Schnittstellen an • ähnlich IMPORT in Modula II, with in Ada oder uses in Object Pascal • Suchpfad für zu importierende Klassen Klassen class EineKlasse … • dienen als Schablonen für Objekte • definieren die Variablen und Operationen “ihrer“ Objekte Instanz- und Klassenvariablen NochEinTyp eineInstanzVariable; • repräsentieren die Daten, die das Objekt ausmachen Methoden void eineMethode( … ) • repräsentieren die Operationen, die das Objekt kennt Programmieren in Java 02.Apr.98 14/120 Einleitung 1.5 Ausführungsmodell Quellprogramm <datei>.java Klassenbibliothek (API) Übersetzer javac Class Loader Byte Code Verifyer Bytecode wird von Disk oder über das Netz geladen Quellprogramm <datei>.java Interpreter javac Runtime Code Generator Laufzeitsystem Betriebssystem, Hardware, … Programmieren in Java 02.Apr.98 15/120 Einleitung 1.5.1 Das Laden einer Klasse im Detail läuft in drei Phasen ab • laden (load) → Binärrepräsentation (class file) finden und zur Klassenbibliothek hinzufügen → Arbeitsspeicher (RAM) bereitstellen → Zyklenfreiheit der Vererbungshierarchie sicherstellen • binden (link) ♦ verifizieren (verification) → sicherstellen, dass die Binärrepräsentation der Klasse (Symboltabelle, Methodencode etc.) “korrekt“ ist ♦ vorbereiten (preparation) → Klassenvariablen und -Konstanten erzeugen (aber noch nicht initialisieren) ♦ auflösen von symbolischen Referenzen (resolution) → sicherstellen, dass die referenzierten Bezeichner sichtbar sind und existieren • initialisieren (initialize) → ausführen des Initialisierungsblocks (static initializer) → initialisieren der Klassenvariablen Programmieren in Java 02.Apr.98 16/120 Einleitung 1.6 • Die Virtuelle Maschine von Java Die Virtuelle Maschine (kurz VM) ist eine Stackmaschine mit exakt definiertem Instruktionssatz und Ausführungsmodell ♦ Stackmaschine → Parameter für Maschineninstruktionen werden über den Stack und nicht über Register übergeben ♦ Die VM hat zwar Register; diese sind aber nicht direkt über Bytecodes (also durch den Benutzer der VM) manipulierbar → Eine Stackmaschine ermöglicht u.a. die einfache Implementierung/Portierung auf Maschinen mit CPUs, die nur wenige oder operationsgebundene Register haben, wie z.B. Intel486 • Der Instruktionssatz der VM ist in Form von Bytecodes definiert. Diese ähneln (in etwa) den Befehlen eines Assemblers → nahe genug an Assembler, um sehr schnell nativen/echten Maschinen-Code während der Laufzeit zu generieren (→ runtime code generation, just-in-time-compiler (JIT)) • Die Java VM ist eine “high level“ Maschine; d.h. diese VM ist nicht nur ein in Software geschriebener Assembler, sie führt z.B. auch: ♦ div. Sicherheitsüberprüfungen durch, wie z.B. primitive Typprüfungen, byte code verification (siehe Kapitel 1.5.1) ♦ die Garbage Collection (siehe Kapitel 7) durch, ♦ die Task-Switches durch (→ verwaltet den Stackpointer eigenständig, kann nicht über Bytecode-Anweisungen gesetzt werden) Programmieren in Java 02.Apr.98 17/120 Einleitung 1.6.1 Beispiel, Byte-Code der VM Gegeben sei folgendes Java-Code-Fragment: public class Application { static int foo( int i, int j ) { // (2) int k = 1; return i + j + k; } public static void main( String args[] ) { // (1) System.out.println( foo( 17, 42 ) ); } } Das Beispiel besteht aus einer Klasse (Application) mit zwei Methoden (foo(…) und main(…)). In der Methode main wird die Methode foo aufgerufen und das Ergebnis ausgegeben. Die Methode foo addiert die beiden Parameter, zählt 1 dazu und gibt das Ergebnis zurück. Übersetzt in Java-Bytecode ergibt sich folgender Code: Magic=0xCAFEBABEVersion=45.3Access=public (0x0001) Class=(#1) "Application"(#36) SuperClass=(#2)"java/lang/Object"(#28) Constant Pool Entries=43 #1 Kind=CONSTANT_Class(7) Name="Application"(#36) #2 Kind=CONSTANT_Class(7) Name="java/lang/Object"(#28) #3 Kind=CONSTANT_Class(7) Name="java/io/PrintStream"(#19) #4 Kind=CONSTANT_Class(7) Name="java/lang/System"(#35) #5 Kind=CONSTANT_Methodref(10) Class=(#2) "java/lang/Object"(#28) NameAndType=(#12)"<init>"(#31) Programmieren in Java 02.Apr.98 18/120 Einleitung "()V"(#38) #6 Kind=CONSTANT_Methodref(10) Class=(#1) "Application"(#36) NameAndType=(#11)"foo"(#34) "(II)I"(#18) #7 Kind=CONSTANT_Fieldref(9) Class=(#4) "java/lang/System"(#35) NameAndType=(#9)"out"(#27) "Ljava/io/PrintStream;"(#32) #8 Kind=CONSTANT_Methodref(10) Class=(#3) "java/io/PrintStream"(#19) NameAndType=(#10)"println"(#14) "(I)V"(#15) #9 Kind=CONSTANT_NameAndType(12) Name="out"(#27) Signature="Ljava/io/PrintStream;"(#32) #10 Kind=CONSTANT_NameAndType(12) Name="println"(#14) Signature="(I)V"(#15) #11 Kind=CONSTANT_NameAndType(12) Name="foo"(#34) Signature="(II)I"(#18) #12 Kind=CONSTANT_NameAndType(12) Name="<init>"(#31) Signature="()V"(#38) #13 Kind=CONSTANT_Utf8(1)Length=4Value="this" 74 68 69 73 #14 Kind=CONSTANT_Utf8(1)Length=7Value="println" 70 72 69 6E 74 6C 6E #15 Kind=CONSTANT_Utf8(1)Length=4Value="(I)V" 28 49 29 56 #16 Kind=CONSTANT_Utf8(1)Length=13Value="ConstantValue" 43 6F 6E 73 74 61 6E 7456 61 6C 75 65 #17 Kind=CONSTANT_Utf8(1)Length=18Value="LocalVariableTable" 4C 6F 63 61 6C 56 61 7269 61 62 6C 65 54 61 62 6C 65 #18 Kind=CONSTANT_Utf8(1)Length=5Value="(II)I" 28 49 49 29 49 #19 Kind=CONSTANT_Utf8(1)Length=19Value="java/io/PrintStream" 6A 61 76 61 2F 69 6F 2F50 72 69 6E 74 53 74 72 65 61 6D Programmieren in Java 02.Apr.98 19/120 Einleitung #20 Kind=CONSTANT_Utf8(1)Length=10Value="Exceptions" 45 78 63 65 70 74 69 6F6E 73 #21 Kind=CONSTANT_Utf8(1)Length=15Value="LineNumberTable" 4C 69 6E 65 4E 75 6D 6265 72 54 61 62 6C 65 #22 Kind=CONSTANT_Utf8(1)Length=1Value="I" 49 #23 Kind=CONSTANT_Utf8(1)Length=10Value="SourceFile" 53 6F 75 72 63 65 46 696C 65 #24 Kind=CONSTANT_Utf8(1)Length=14Value="LocalVariables" 4C 6F 63 61 6C 56 61 7269 61 62 6C 65 73 #25 Kind=CONSTANT_Utf8(1)Length=4Value="Code" 43 6F 64 65 #26 Kind=CONSTANT_Utf8(1)Length=16Value="Application.java" 41 70 70 6C 69 63 61 7469 6F 6E 2E 6A 61 76 61 #27 Kind=CONSTANT_Utf8(1)Length=3Value="out" 6F 75 74 #28 Kind=CONSTANT_Utf8(1)Length=16Value="java/lang/Object" 6A 61 76 61 2F 6C 61 6E67 2F 4F 62 6A 65 63 74 #29 Kind=CONSTANT_Utf8(1)Length=4Value="main" 6D 61 69 6E #30 Kind=CONSTANT_Utf8(1)Length=22Value="([Ljava/lang/String;)V" 28 5B 4C 6A 61 76 61 2F6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56 #31 Kind=CONSTANT_Utf8(1)Length=6Value="<init>" 3C 69 6E 69 74 3E #32 Kind=CONSTANT_Utf8(1)Length=21Value="Ljava/io/PrintStream;" 4C 6A 61 76 61 2F 69 6F2F 50 72 69 6E 74 53 74 72 65 61 6D 3B #33 Kind=CONSTANT_Utf8(1)Length=13Value="LApplication;" 4C 41 70 70 6C 69 63 6174 69 6F 6E 3B #34 Kind=CONSTANT_Utf8(1)Length=3Value="foo" 66 6F 6F #35 Kind=CONSTANT_Utf8(1)Length=16Value="java/lang/System" 6A 61 76 61 2F 6C 61 6E67 2F 53 79 73 74 65 6D #36 Kind=CONSTANT_Utf8(1)Length=11Value="Application" 41 70 70 6C 69 63 61 7469 6F 6E #37 Kind=CONSTANT_Utf8(1)Length=1Value="k" 6B Programmieren in Java 02.Apr.98 20/120 Einleitung #38 Kind=CONSTANT_Utf8(1)Length=3Value="()V" 28 29 56 #39 Kind=CONSTANT_Utf8(1)Length=1Value="j" 6A #40 Kind=CONSTANT_Utf8(1)Length=1Value="i" 69 #41 Kind=CONSTANT_Utf8(1)Length=4Value="args" 61 72 67 73 #42 Kind=CONSTANT_Utf8(1)Length=19Value="[Ljava/lang/String;" 5B 4C 6A 61 76 61 2F 6C61 6E 67 2F 53 74 72 69 6E 67 3B Interface Table Entries=0 Field Table Entries=0 Method Table Entries=3 AL_CODE: Method 1 Method="Application.foo"(#34) Signature="(II)I"(#18) Signature="(II)I"(#18) Access=static (0x0008)Attribute Count=1 Attribute="Code"(#25)Length=74 Max Stack=2Max Locals=3Code Length=8 0x00000000 04iconst_1 0x00000001 3Distore_2 0x00000002 1Aiload_0 0x00000003 1Biload_1 0x00000004 60iadd 0x00000005 1Ciload_2 0x00000006 60iadd 0x00000007 ACireturn Exception Handler Entries=0 Attribute="LineNumberTable"(#21)Length=10Entry Count=2 Start=0x00000000Line Number=7 Start=0x00000002Line Number=8 Attribute="LocalVariableTable"(#17)Length=32Entry Count=3 Start=0x00000000Effective Length=8Slot=0 Name="i"(#40) Signature="I"(#22) Start=0x00000000Effective Length=8Slot=1 Name="j"(#39) Signature="I"(#22) Start=0x00000002Effective Length=6Slot=2 Name="k"(#37) Signature="I"(#22) AL_CODE: Method 2 Method="Application.main"(#29) Signature="([Ljava/lang/String;)V"(#30) Signature="([Ljava/lang/String;)V"(#30) Programmieren in Java 02.Apr.98 21/120 Einleitung Access=public,static (0x0009)Attribute Count=1 Attribute="Code"(#25)Length=56 Max Stack=3Max Locals=1Code Length=14 0x00000000 B20007getstatic (#7) java/lang/System.out 0x00000003 1011bipush 17 0x00000005 102Abipush 42 0x00000007 B80006invokestatic (#6) Application.foo 0x0000000A B60008invokevirtual (#8) java/io/PrintStream.println 0x0000000D B1return Exception Handler Entries=0 Attribute="LineNumberTable"(#21)Length=6Entry Count=1 Start=0x00000000Line Number=13 Attribute="LocalVariableTable"(#17)Length=12Entry Count=1 Start=0x00000000Effective Length=14Slot=0 Name="args"(#41) Signature="[Ljava/lang/String;"(#42) AL_CODE: Method 3 Method="Application.<init>"(#31) Signature="()V"(#38) Signature="()V"(#38) Access=public (0x0001)Attribute Count=1 Attribute="Code"(#25)Length=47 Max Stack=1Max Locals=1Code Length=5 0x00000000 2Aaload_0 0x00000001 B70005invokenonvirtual (#5) java/lang/Object.<init> 0x00000004 B1return Exception Handler Entries=0 Attribute="LineNumberTable"(#21)Length=6Entry Count=1 Start=0x00000000Line Number=1 Attribute="LocalVariableTable"(#17)Length=12Entry Count=1 Start=0x00000000Effective Length=5Slot=0 Name="this"(#13) Signature="LApplication;"(#33) Attribute Table Entries=1 Attribute="SourceFile"(#23)Length=2Source File="Application.java"(#26) Anmerkung: Dieser Code wird nicht im Detail behandelt, sondern nur der Code, welcher für zwei ausgewählte Methoden erzeugt wird (siehe nächste Seite). Programmieren in Java 02.Apr.98 22/120 Einleitung Für das zuvor gezeigte Java-Code-Fragment (siehe Seite 18) sieht der Bytecode für die VM nach der Übersetzung folgendermassen aus: … getstatic bipush bipush invokestatic invokevirtual … (#5) 17 42 (#6) (#7) // Empfänger java/lang/System.out auf Stack // 1. Parameter auf Stack // 2. Parameter auf Stack // Aufruf: Application.foo // Aufruf: java/io/PrintStream.println Aufruf der Methode foo von der Methode main aus (1) (siehe Seite 18). Die aktuellen Parameter werden auf den Stack gelegt und dann die Methode aufgerufen. … iconst_1 istore_2 iload_0 iload_1 iadd iload_2 iadd … // Konstante 1 auf Stack // Speichern in lokaler Variable k // i auf Stack // j auf Stack // Stack.push( add( Stack.pop(), Stack.pop() ) ) // k auf Stack // Stack.push( add( Stack.pop(), Stack.pop() ) ) Methode foo wird/wurde aufgerufen (2). Die aktuellen Parameter werden vom Stack geholt und in lokalen Variablen (i, j) zwischengespeichert. Die lokale Variable k wird initialisiert; dann wird die Addition durchgeführt und das Ergebnis zurückgegeben. Programmieren in Java 02.Apr.98 23/120 Einleitung 1.7 Übersicht über die Klassenbibilothek (API – application programming interface) Die Klassenbibliothek (API) von Java ist standardisiert und steht “identisch“ auf allen Java-Implementationen zur Verfügung. Gegliedert ist sie in verschiedene Pakete, welche die diensttragenden Klassen enthalten. Die wichtigsten Pakete der Java API sind: java.lang • enthält die essentiellen Basisklassen von Java wie beispielsweise Object, String, Thread • einziges Paket, welches automatisch importiert wird java.io • enthält Klassen zur Handhabung von Ein-/Ausgabe-Sequenzen (input/ output streams), um in Dateien, Strings etc. zu schreiben oder von dort zu lesen java.net • enthält Klassen, um über Netzwerke zu kommunizieren, URLs, TCPSockets, UDP-Sockets, IP-Adressen etc. java.util • viele nützliche Hilfsklassen, wie beispielsweise Hashtabellen, Zufallszahlen, Vektoren, Containerklassen etc. java.awt • enthält Klassen zur Realisierung von grafischen Benutzerschnittstellen, Fenster, Menüs, Knöpfe, Schalter etc. java.awt.image • enthält spezielle Klassen zur Anzeige und Manipulation von (Bitmap-) Bildern innerhalb von grafischen Benutzeroberflächen java.applet • spezielle Klassen und Interfaces, um Applets zu realisieren Programmieren in Java 02.Apr.98 24/120 Einleitung 1.8 Übungen (1) Aus welchen Teilen besteht die Java Plattform? (2) Tippen Sie das Circle-Beispiel aus Kapitel 1.4 ab, übersetzen Sie es und führen Sie das übersetzte Java-Programm aus. (3) Was ist speziell an der Methode public static void main( String[] args )? (4) Ändern Sie das Circle-Beispiel (siehe Kapitel 1.4) ab, so dass über die Kommandozeile der Radius des Kreises eingegeben werden kann, für welchen dann der Flächeninhalt berechnet wird. Wichtiger Hinweis: Parameter, welche über die Kommandozeile übergeben wurden, werden in das String-Array args[] der main-Methode geschrieben und sind dort abrufbar; mit args[0] erhält man beispielsweise den ersten Parameter, der über die Kommandozeile übergeben wurde. Zur Umwandlung von Zeichenketten in Fliesskommazahlen können sie die Klasse java.lang.Double (siehe API Dokumentation) bzw. die dort definierten Methoden verwenden. Übersetzen Sie das Java-Programm und führen Sie es aus. (5) Erweitern Sie die Klasse Circle um eine Methode zur Berechnung des Kreisumfangs und geben Sie (zusätzlich) in der Methode main in der Klasse Application den Kreisumfang aus. Übersetzen Sie das Java-Programm und führen Sie es aus. (6) Welche Vor- und Nachteile hat das Ausführungsmodell von Java? (7) Beschreiben Sie das Ausführungsmodell von Java! (8) Warum ist Java (vergleichsweise) plattformunabhängig? (9) Was ist ein “Byte-Code“? (10) Was ist eine Stackmaschine? (11) Warum kann u.a. der Stackpointer in der Java VM nicht über ByteCode-Befehle geändert werden? (12) Aus welchen (Haupt-)Paketen besteht die Klassenbibliothek von Java? Was ist die Aufgabe eines jeden Pakets? (13) Was ist speziell am Paket java.lang? Programmieren in Java 02.Apr.98 25/120 Einleitung 1.9 Literatur Arnold, K., J. Gosling (1996): The Java Programming Language; Java Series. N.Y: Addison Wesley, 1996. Bückner, M. C., J. Geidel, M. F. Lachmann (1993): Objektworks\Smalltalk für Anfänger. Berlin: Springer Verlag, 1993. Campione, M., K. Walrath (1996): The Java Tutorial: Object-Oriented Programming for the Internet; Java Series. N.Y: Addison Wesley Inc., 1996. Flanagan, D. (1996): Java in a Nutshell. Sebastopol CA: O’Reilly & Associates, 1996. (ISBN 1-56592-183-6) Gosling, J., H. McGilton (1995): The Java Language Environment – A White Paper. Technical report, Sun Microsystems, Mountain View, CA, 1995. Lindholm, T., F. Yellin (1996): The Java Virtual Machine Specification. Addison Wesley. (ISBN 0-201-63452-X) Meyer, B. (1988): Object-Oriented Software Construction. London: Prentice Hall International, 1988. Wegner, P. (1990): Concepts and paradigms of object-oriented programming. Expansion of Oct. 4 OOPSLA-89 Keynote Talk. OOPS Messenger, 1(1), ACM Press, 1990. Programmieren in Java 02.Apr.98 26/120 Variablen, Bezeichner, Typsystem 2 Variablen, Bezeichner, Typsystem 2.1 Variablen • Variablen sind Behälter für Programmdaten während der Laufzeit eines Programms • Der Inhalt von Variablen wird unterschiedlich interpretiert, z.B. (short) 0000 0000 0100 0001 → 65 (char) 0000 0000 0100 0001 → ‘A’ (Ganzzahl) (Buchstabe) ♦ Typen teilen dem Übersetzer/Laufzeitsystem mit, wie der Wert/ Inhalt einer Variablen zu interpretieren ist → Variablen haben unterschiedliche Typen, z.B. Variablen die Zahlen, Buchstaben, Zeichenketten, etc. enthalten dürfen/können • Der Typ bzw. die Klasse einer Variable “entscheidet“ über deren (sinnvolle) Interpretation, z.B. (short) … + (short) … ≠ (String) … + (String) … 1 + 1 ≠ “1“ + “1“ 0000 0000 0000 0010 ≠ 0000 0000 0011 0001 0000 0… 2.2 Bezeichner • Ein Bezeichner benennt (bzw. bezeichnet) eine Klasse, ein Interface, eine Methode, eine Variablen etc. • Bezeichner beginnen in Java mit einem: ♦ Buchstaben ♦ "_" ♦ "$" • Beispiele: class Circle … int area() … double radius; Double radius; Programmieren in Java // Klassenbezeichner // Methodenbezeichner // Variablenbezeichner 02.Apr.98 27/120 Variablen, Bezeichner, Typsystem 2.3 • Datentypen und Typsystem Java ist eine streng getypte Sprache ♦ statische Typprüfung/-bindung (aber dynamische Methodenbindung; vorsicht nicht verwechseln) ♦ keine “legale” Zeigerarithmetik (u.a. kein *ptr++ etc.) ♦ Variablen können nur typverträgliche Werte enthalten ♦ Zuweisungen sind nur zwischen typverträglichen Variablen und Ausdrücken möglich • zwei Typen sind gleich, wenn sie den gleichen (voll qualifizierten) Bezeichner haben (Namensäquivalenz vs. Strukturäquivalenz) • Java unterscheidet zwischen sogenannten einfachen Datentypen (primitive data types), wie z.B. int, boolean, char, etc. (siehe Kapitel 3) und komplexen Datentypen (reference data types), wie z.B. Array, Objekt, etc. (siehe Kapitel 4) • (fast) keine implizite Typkonvertierung • keine Typkonvertierung (siehe auch Kapitel 8.5.1) zwischen “unzusammenhängenden” bzw. typunverträglichen Typen ♦ eine Typkonvertierung ist u.a. nicht möglich: • von einem einfachen in einen komplexen Datentyp (Ausnahme: Konvertierung nach String, also in eine Zeichenkette) • von einem komplexen in einen einfachen Datentyp • vom Typ null in einen einfachen Datentyp • vom Typ boolean in irgend einen anderen Datentyp oder von irgend einem anderen Datentyp in den Typ boolean (Ausnahme: Konvertierung nach String) • von einem Klassentyp S in einen Klassentyp T, wenn nicht S Unterklasse von T ist • von einem Klassentyp S in einen Interfacetyp K, wenn S final attributiert ist und nicht K implementiert Programmieren in Java 02.Apr.98 28/120 Variablen, Bezeichner, Typsystem 2.4 Übungen (14) Wozu gibt es Typen? (15) Welche Bezeichner sind zulässig in Java? Int, $_ab_$, _$ab$_, a_b, a/b, a§b, a1, 1a, _1a (16) Implementieren sie eine Klasse LifeForm. • Für unsere Zwecke soll ein Objekt dieser Klasse eine Farbe (java.awt.Color) und einen Namen (String) haben. Legen sie hierfür Instanzvariablen (color und name) an. Denken sie daran, dass ggf. Klassen aus bestimmten Paketen importiert werden müssen, z.B. Color aus java.awt. • Legen sie entsprechende Zugriffsmethoden zum Setzen und Lesen der Instanzvariablen an (siehe unten). • Implementieren sie ferner in der static public void main(…)-Methode der Klasse einen Minitest. Übersetzen sie die Klasse und führen sie den Minitest aus. class LifeForm extends Object { … // Lesen von color void color() … Color color( Color color ) // Setzen von color … // Lesen von age String name() … // Setzen von age void name( String name) … } Bewahren sie den Quellcode auf, er wird für spätere Übungen noch benötigt. Hinweis: alle Übungen mit einer fettgedruckten Nummer bauen aufeinander auf und sollten bevorzugt bearbeitet werden. (17) Schreiben Sie ein kurzes Programm, welches feststellt, ob verschachtelte Kommentare in Java zulässig sind, d.h. das Programm soll entweder ausgeben ( System.out.println(…)), dass verschachtelte Kommentare zulässig sind oder dass sie es nicht sind. Das Programm soll in jedem Fall korrekter Java-Code sein. Programmieren in Java 02.Apr.98 29/120 Einfache Datentypen (primitive data types) 3 Einfache Datentypen (primitive data types) • Einfache Datentypen repräsentieren Werte ohne eigene Identität • Eine Variable eines einfachen Datentyps enthält immer direkt den Wert der entsprechenden Variable → immer Wertsemantik, d.h. Speicherstelle für die Variable enthält direkt den Wert der Variable • i 42 An vordefinierten einfachen Datentypen stehen zur Verfügung: ♦ vier Arten von Ganzzahlen (byte, short, int, long) ♦ zwei Arten von Fliesskommazahlen (double, float) ♦ Zeichen bzw. Buchstaben (char) und ♦ bool’sche Werte (boolean) • Es gibt in Java keine benutzerdefinierbaren, einfachen Datentypen (mit Wertsemantik), wie z.B. Aufzählungstypen (enumeration types) oder Wertbereichstypen (ranged types) in Ada, Modula2, Pascal, etc. Programmieren in Java 02.Apr.98 30/120 Einfache Datentypen (primitive data types) 3.1 • Ganzzahlen 4 Varianten ♦ byte → 8 bit → -( 27 ) bis ( 27 -1 ) ♦ short → 16 bit → -( 215 ) bis ( 215 -1 ) ♦ int → 32 bit → -( 231 ) bis ( 231 -1 ) ♦ long → 64 bit → -( 263 ) bis ( 263 -1 ) • vorzeichenbehaftet (kein “unsigned”) • Zweierkomplement ♦ z.B. (byte) -12810 = 1000 00002, (byte) -12710 = 1000 00012, (byte) 010 = 0000 00002, byte 12710 = 0111 1112 • Randbedingung: Ablaufgeschwindigkeit ♦ 1 Anweisung/Operation ≡ Drei-Address-Code ♦ keine automatische Grössenanpassung (Bignums) wie beispielsweise in Smalltalk ♦ keine Exception/Fehlermeldung/Programmabbruch bei Bereichsüberschreitung, aber bei Division durch 0 → Beispiel … bei der Konvertierung werden die entsprechenden Bits “abgeschnitten“ byte e1; int e2; byte a = 127; int b = 1; e1 = (byte) ( a + b ); System.out.println( e1 ); // -128 e2 = ( a + b ); System.out.println( e2 ); // 128 Programmieren in Java 02.Apr.98 31/120 Einfache Datentypen (primitive data types) 3.2 • Fliesskommazahlen 2 Varianten mit unterschiedlicher Genauigkeit/Auflösung (Fliesskomma-Arithmetik in Java entspricht dem IEEE754 Standard) ♦ 32 bit Fliesskomma → float ♦ 64 bit Fliesskomma → double • auch hier keine Bignums (aber ab Java v1.1 Klasse BigNum) • keine Exceptions ♦ da im Standard so definiert und auch sinnvoll → manchmal kann nur ein “übergeordnetes“ System entscheiden, ob eine Operation fehlerhaft war und der Betrieb tatsächlich abgebrochen werden soll, z.B. Komparator (-Software) für mehrfach redundant ausgelegten Höhenmesser und nicht der Höhenmesser selbst ♦ auch bei “Division durch 0” keine Exception ♦ problematische Operationen können nachträglich erkannt werden. Hierfür die folgenden Ergebniswerte vordefiniert: POSITIVE_INFINITY, NEGATIVE_INFINITY, “Negative Zero” und NaN (not a number) System.out.println( 1.0 / 0.0 ); System.out.println( -1.0 / 0.0 ); System.out.println( 0.0 / 0.0 ); // INF // -INF // NaN ♦ Anleihe von funktionalen Sprachen, wo jede Funktion ein Ergebnis liefert muss (siehe VAL oder SETL) Programmieren in Java 02.Apr.98 32/120 Einfache Datentypen (primitive data types) 3.3 • Zeichen und Buchstaben char ♦ 16 Bit Unicode → Sonderzeichen, Umlaute etc. sind zulässig in Bezeichnern ♦ Transparent implementiert, d.h. solange keine Unicode-Zeichen ( > ASCII(256) ) verwendet werden, ist der Umgang mit Zeichen und Buchstaben wie gewohnt • ‘a’, ‘b’, etc. 3.4 • Bool’sche Werte boolean ♦ true und false ♦ sind keine “Integers” o.ä., z.B. aBoolean = (boolean) 17 wird nicht übersetzt ♦ boolean ist in echter, eigenständiger Typ, welcher nicht mit den Ganzzahlen “verwandt“ ist. Programmieren in Java 02.Apr.98 33/120 Einfache Datentypen (primitive data types) 3.5 Übungen (18) Einige Methoden der Klasse LifeForm (siehe Übung (16), sie erinnern sich) sollen geändert werden, so dass sie immer sinnvolle Default-Werte liefern. • Ändern Sie die (Getter-)Methode name() und color() der Klasse LifeForm so ab, dass die Methode name() für den Fall, dass kein Name vergeben wurde (d.h. die entsprechende Instanzvariable ist gleich null) mit dem Namen der Klasse des Objekts ( this.getClass().getName()) antwortet. • Ändern Sie und die (Getter-)Methode color() der Klasse LifeForm so ab, dass für den Fall, dass keine Farbe zugewiesen wurde, mit der Farbe Schwarz (java.awt.Color.black) antwortet. Beispiel: LifeForm lifeForm = new LifeForm(); // antwortet nun mit “LifeForm“, lifeForm.name(); // bisher wurde mit null geantwortet lifeForm.color(); // antwortet nun mit Color.black, bisher null lifeForm.name( “Bobby“ ); // antwortet wie bisher mit dem gesetzen Namen lifeForm.name() … (19) Schreiben sie ein kleines Testprogramm (oder erweitern sie den Minitest der static public void main(…)-Methode), welches Objekte der Klasse LifeForm erzeugt und deren Funktionalität (Farbe(n) und Name(n) lesen und schreiben) testet. (20) Wie viele/Welche Testfälle sollten (nachdem die Änderung aus Übung (18) vollzogen ist) für den Minitest zweckmässigerweise unterschieden werden? Programmieren in Java 02.Apr.98 34/120 Komplexe Datentypen (reference data types) 4 Komplexe Datentypen (reference data types) • Komplexe Datentypen repräsentieren Werte mit eigener Identität. • Eine Variable eines komplexen Datentyps enthält immer eine Referenz (d.h. einen Zeiger) auf ein Objekt/Datum → immer Referenzsemantik, d.h. Speicherstelle für die Variable enthält “nur“ eine Referenz auf den eigentlichen Wert der Variable p • An komplexen Datentypen stehen zur Verfügung: Felder/Arrays sowie Klassen und Interfaces Point • Felder → int[][]… matrix = new int [3][3] … • Klassen → class EineKlasse … • Schnittstellen → interface EineSchnittstelle … • Java kennt keine Records, Structs, Unions etc. (wie beispielsweise die Programmiersprachen Ada, C, Modula II, Pascal etc.) 42 42 ♦ Alternative: (methodenlose) Klassen, z.B. class Point extends Object { double x; double y; } class Rectangle extends Object { Point upperLeft; Point lowerRight; } Programmieren in Java 02.Apr.98 35/120 Komplexe Datentypen (reference data types) 4.1 • Felder (arrays) ein Feld oder Array repräsentiert eine geordnete Menge von Variablen eines Typs ♦ über einen Index können die Elemente des Arrays adressiert werden → anIntegerArray[17] ♦ in Java ist der Index des ersten Elements ist immer 0 • je nach Basistyp des jeweiligen Arrays sind entweder Werte oder Referenzen abgelegt anIntegerArray 5 1 0 9 1 2 … anObjectArray • … Arrays in Java sind (eigentlich) spezielle Objekte ♦ Arrays haben ein Instanzvariable length • es können Arrays mit beliebigem Typ deklariert werden int[] anIntegerArray; int anotherIntergerArray[]; Object[] anObjectArray; • es können “Arrays von Arrays“, also Arrays mit beliebig vielen Dimensionen deklariert werden → mehrdimensionale Arrays int aMatrix[][] = new int [4][2]; ♦ aMatrix.length → 4 ♦ aMatrix[0].length → 2 Programmieren in Java 02.Apr.98 36/120 Komplexe Datentypen (reference data types) A propos • String ≠ char[] … Strings sind in Java keine Arrays aus Zeichenketten, sondern ein echter eigenständiger Typ, dessen Realisierung verborgen ist! Beispiele für Arrays • ein leeres Array: int intArray[] = new int [400]; • ein vorinitialisiertes Array: String colorNames[] = { “rot”, “grün”, “blau” }; • es ist in Java nicht möglich, die Grösse von Feldern statisch d.h. zur Übersetzungszeit festzulegen bzw. zu beschränken boolean word[32]; // wird nicht übersetzt Programmieren in Java 02.Apr.98 37/120 Komplexe Datentypen (reference data types) 4.2 Klassen [Attributierung] class Klassenbezeichner // Klassenkopf [extends Oberklassenbezeichner] [implements Interfacebezeichner {, Interfacebezeichner}] // Klassenrumpf { Anweisungssequenz } • Typ/Daten + zugehörige Implementierung Klassenkopf • Attributierung ♦ abstract - abstrakte Klasse • eine unvollständige Implementierung • nicht instanzierbar, d.h. von einer abstrakten Klasse können keine Objekte erzeugt werden ♦ final - es dürfen keine Unterklassen gebildet werden ♦ public - Klasse ist überall sichtbar (siehe Kapitel 6.2) • die Einhaltung der durch die Attributierung gegebenen Restriktionen wird vom Übersetzer überprüft Klassenrumpf • Rumpfblock enthält ♦ Klassen- und Instanzvariablen (siehe Kapitel 4.2.4 – 4.2.6) ♦ Klassen- und Instanzmethoden (siehe Kapitel 10.7 – 10.8) ♦ Konstruktoren (siehe Kapitel 10.9) ♦ lokale Klassen, sogenannte “Inner Classes“ (ab Java 1.1.x) Programmieren in Java 02.Apr.98 38/120 Komplexe Datentypen (reference data types) 4.2.1 Vererbung … extends Oberklassenbezeichner implements Schnittstellenbezeichner • wenn nicht redefiniert, dann verfügt eine Klasse über die gleichen Attribute (Klassen- und Instanzvariablen) und Operationen (Klassenund Instanzmethoden) wie ihre Oberklasse → Bezeichner werden ggf. von Oberklasse geerbt → private-Bezeichner → vorhanden aber nicht sichtbar → Redefinierte Bezeichner → sichtbar mit super.identifier … → Konstruktoren werden nicht vererbt • extends deklariert Oberklasse ♦ Wird extends weggelassen, dann wird Object als Oberklasse angenommen • implements deklariert einzuhaltende Schnittstellen • Ausschliesslich Einfachvererbung ... aber ... • Klasse kann mehrere Schnittstellen implementieren • der Typ des Rückgabewerts einer Methode kann nicht ausschliesslichredefiniert werden • Typ des Empfängerobjekts der Methoden wird variiert, d.h. implizite Variation des Empfängerobjekts, keine Variation der Parameter ♦ Typ anderer Parameter wird nicht variiert • Typ der Parameter redefinierter Methoden darf nicht allgemeiner sein als in der Oberklasse (siehe auch Beispiel in Kapitel 4.2.3) Programmieren in Java 02.Apr.98 39/120 Komplexe Datentypen (reference data types) 4.2.2 Einschub/Wiederholung Varianz des Typs formaler Parameter bei vererbten Methoden Grundsätzliche Frage für alle OOPSen, die Vererbung unterstützen: • Welche Signatur/Schnittstelle hat eine ererbte Methode? ♦ Welche Signatur hat eine ererbte Methode, wenn Objekte der selben Klasse übergeben werden? Beispiel class T // { T add( T b ) // } class ST extends T { … } in Parameternotation: -> add( a, b : T ) return T Welchen Typ haben die Parameter der ererbten add-Funktion? ST add( b : ST ); // T add( b : ST ); // T add( b : T ); // // -> add( a, b : ST ) return ST; Covarianz -> add( a, b, : ST ) return T; Contravarianz -> add( a : ST; b : T ) return T; impl. Variation des Empfängerobj.typs, keine Variation der Parameter Es gibt generell keine richtige oder falsche Art der Varianz; Beispiel • Was wenn: T = Integer und ST = Byte? • Was wenn: T = Numerischer Typ und ST = numerische Matrix? Programmieren in Java 02.Apr.98 40/120 Komplexe Datentypen (reference data types) 4.2.3 Beispiel Vererbung und Redefinition class Outside { } class Top { } class Middle extends Top { void foo( Middle anObject ) { } void fooToo( Middle anObject ) { } } class Bottom extends Middle { void foo( Outside anObject ) { … } /* neue Methode */ /* illegal - würde nicht übersetzt werden */ void fooToo( Top anObject) */ /* legal */ void fooToo( Bottom anObject ) { … } /* redefiniert fooToo */ } Programmieren in Java 02.Apr.98 41/120 Komplexe Datentypen (reference data types) 4.2.4 Klassenvariablen static Typ Bezeichner; • Klassenvariablen repräsentieren Daten, die für alle Objekte einer bestimmten Klasse gleich sind → der Inhalt einer Klassenvariable (kurz KV) ist identisch für alle Objekte der Klasse und alle Objekte ihrer Unterklassen, z.B. class MyClass { static int N = 42; } // Deklaration und Initialisierung einer KV class MySubClass extends MyClass {} class Application { static void dump( int a, int b, int c, int d ) {System.out.println( a+", "+b+", "+c+", "+d );} public static void main( String args[] ) { MyClass mc = new MyClass(); MySubClass msc = new MySubClass(); dump( MyClass.N, MySubClass.N, mc.N, msc.N ); // Änderung der KV MyClass.N = 17; dump( MyClass.N, MySubClass.N, mc.N, msc.N ); } } • Klassenvariablen sollten generell sparsam und umsichtig verwendet werden, da sie dazu führen können, dass Klassen zunehmend kontextabhängig werden und/oder Seiteneffekte verbergen • Klassenvariablen können die Änderbarkeit von Klassen erschweren, da ihr Typ in Unterklassen nicht mehr geändert werden kann/sollte/… Programmieren in Java 02.Apr.98 42/120 Komplexe Datentypen (reference data types) 4.2.5 Klasseninstanzvariablen … gibt es nicht (direkt) in Java … … gibts doch (auf Umwegen) … • Klasseninstanzvariablen repräsentieren Daten, die für alle Objekte genau eines Klassen-Typs gleich sind → der Inhalt einer Klasseninstanzvariable (kurz KIV) ist gleich für alle Objekte einer Klasse (oder besser eines “Klassen-Typs“), aber nicht zwingend für alle Objekte von Unterklassen dieser Klasse • Beispiel für KIVs ♦ eine Klasse Fahrzeug mit der KIV anzahlRaeder hat zwei Unterklassen. Die Klasse Zweirad und die Klasse Dreirad. ♦ die KIV anzahlRaeder kann nun unterschiedliche Werte für Zweirad (→ anzahlRaeder=2) und Dreirad (→ anzahlRaeder=3) haben; ist aber für alle Objekte der entsprechenden Unterklassen gleich ♦ ändert sich der Wert der KIV, so betrifft dies alle Objekte des “Klassen-Typs“ aber nicht alle Objekte der Klasse • Nachbildung von KIVs in Java ♦ mittels Instanzvariablen und entsprechend unterschiedlicher Initialisierung bei der Objekterzeugung ♦ oder Redefinition einer Klassenvariablen • Verwendung von KIV bringt generell ähnliche Probleme wie die Verwendung von Klassenvariablen • KIV können zusätzlich problematisch sein, da im Programmcode nicht ohne weiteres ersichtlich ist, wer/welche Objekte von der Änderung einer KIV eigentlich betroffen sind (siehe Beispiel) Programmieren in Java 02.Apr.98 43/120 Komplexe Datentypen (reference data types) • (schlechtes) Beispiel: KIV über Redefinition einer Klassenvariablen class Ke { static int W = -1; } class BiKe extends Ke { static int W = 2; } // Deklaration KV, W entspricht anzahlRaeder // Redefinition der KV der Oberklasse class TriKe extends Ke { // Redefinition der KV der Oberklasse static int W = 3; } class Application { public static void main( String args[] ) { Ke aKe = new Ke(); BiKe aBiKe = new BiKe(); BiKe anotherBiKe = new BiKe(); TriKe aTriKe = new TriKe(); System.out.println(aKe.W+", "+aBiKe.W +", "+anotherBiKe.W+", "+aTriKe.W); // Änderung der KIV aBiKe.W = 42; // hier ist nicht mehr ohne weiteres erkennbar, was sich eigentlich ändert // Es müssen ggf. ALLE(!) Klassen der Hierarchie betrachet werden System.out.println(aKe.W+", "+aBiKe.W +", "+anotherBiKe.W+", "+aTriKe.W); } } Programmieren in Java 02.Apr.98 44/120 Komplexe Datentypen (reference data types) 4.2.6 Instanzvariablen Typ Bezeichner; • Instanzvariablen repräsentieren Exemplardaten → der Inhalt einer Instanzvariable (kurz IV) kann für unterschiedliche Objekte unterschiedlich sein, z.B. class MyClass { static int N = 42; int n = 42; } // Deklaration und Initialisierung einer KV // Deklaration und Initialisierung einer IV class MySubClass extends MyClass { } class Application { static void dump( int a, int b, int c, int d ) { System.out.println( a+", "+b+", "+c+", "+d ); } public static void main( String args[] ) { MyClass mc = new MyClass(); MySubClassmsc = new MySubClass(); dump( MyClass.N, MySubClass.N, mc.n, msc.n ); // Änderung der IV mc.n = 17; dump( MyClass.N, MySubClass.N, mc.n, msc.n ); // Änderung der KV MyClass.N = 0; dump( MyClass.N, MySubClass.N, mc.n, msc.n ); } } Programmieren in Java 02.Apr.98 45/120 Komplexe Datentypen (reference data types) 4.3 Interfaces interface EinInterface [ extends EinAnderesInterface ] { /* Schnittstellenrumpf */ public void eineMethode( … ); public void nochEine(); … } • ein Interface definiert Signaturen für Methoden (Methodenköpfe ohne Methodenrümpfe, Methodenschnittstellen eben), die von denjenigen Klassen (und allen ihren Unterklassen) bereitgestellt werden müssen, welche dieses Interface implementieren, • wenn eine Klasse eine Schnittstelle implementiert, dann ♦ ist die Schnittstelle bindend auch für alle Unterklassen ♦ wird deren Einhaltung vom Übersetzer überprüft • Schnittstellendeklaration analog zur Klassendeklaration ♦ Schnittstellenmethoden dürfen nicht native, static, synchronized, final, private oder protected attributiert sein ♦ Schnittstellen können von anderen Interfaces erben • eine Klasse kann beliebig viele Interfaces implementieren • Interfaces erlauben von der Klassenhierarchie und jeweiligen Realisierung unabhängige Protokoll- oder Schnittstellenvereinbarungen Programmieren in Java 02.Apr.98 46/120 Komplexe Datentypen (reference data types) 4.4 Beispiel Klassen und Interfaces Das nachfolgende Beispiel zeigt die Anwendung des Interfaces Enumeration Klassen und Interfaces aus dem Paket java.util public interface Enumeration { boolean hasMoreElements(); Object nextElement(); } das Interface Enumeration (aus dem Paket java.util) dient dazu, sicher (Terminierung der Schleife kann einfach(er) bewiesen werden) und komfortabel über Containerklassen, wie Dictionary, Vector etc. zu iterieren. Eine Iteration erfolgt folgendermassen: … Enumeration e = v.elements(); // Enumerationsobj. erzeugen while (e.hasMoreElements()) // gibts noch weitere Objekte, { //über die zu iterieren wäre // Element ausgeben und zum weiterschalten System.out.println(e.nextElement()); } … Damit so iteriert werden kann, muss die Klasse (hier Vector) eine Methode (hier elements()) bereitstellen, welche ein Enumerationsobjekt liefert. Diese ist folgendermassen implementiert: … public final synchronized Enumeration elements() { return new VectorEnumerator(this); } … Programmieren in Java 02.Apr.98 47/120 Komplexe Datentypen (reference data types) Nun muss noch die Enumerationsklasse VectorEnumerator implementiert sein. Die Implementierung dieser Enumerations-Klasse ist folgendermassen realisiert: final class VectorEnumerator implements Enumeration { // Instanzvariablen Vector vector; int count; VectorEnumerator(Vector v) { vector = v; count = 0; } // Konstruktor public boolean hasMoreElements() { return count < vector.elementCount; } public Object nextElement() { synchronized (vector) { if (count < vector.elementCount) { return vector.elementData[count++]; } } … rest wurde ausgelassen … } } Programmieren in Java 02.Apr.98 48/120 Komplexe Datentypen (reference data types) 4.5 Übungen (21) Erstellen sie nun eine Klasse, welche eine spezielle Lebensform, nämlich einen Fisch repräsentiert. Implementieren sie hierfür die Klasse Fish, als eine Unterklasse von LifeForm. Ein Fisch soll standardmässig, d.h. solange nichts anderes definiert ist (vgl. Übung (18)), die Farbe Blau (-> java.awt.Color.blue) haben. (22) Realisieren Sie eine weitere spezielle Lebensform, nämlich eine Pflanze. Implementieren sie hierfür die Klasse Plant, ebenfalls als Unterklasse von LifeForm. Eine Pflanze soll standardmässig, d.h. solange nichts anderes definiert ist, die Farbe Grün (java.awt.Color.green) haben. Implementieren sie die entsprechende Funktionalität. Ferner soll eine Pflanze einen Standort (java.awt.Point) haben; d.h. es soll möglich sein, einer Pflanze einen Standort zuzuweisen und/oder den Standort einer Pflanze zu erfragen. Nachdem einer Pflanze ein Standort jedoch einmal zugeweisen ist, darf es nicht mehr möglich sein, diesen zu ändern. Legen sie hierfür geeignete Instanzvariablen (Point location) und Zugriffsmethoden (Point location() und void location(Point location)) an. (23) Erstellen Sie Klasse Animal, welche (was für eine Überraschung) wieder eine spezielle Lebensform – diesmal ein Tier – repräsentiert. • Welche Klasse wählen sie zweckmässigerweise als Oberklasse von Animal? • Welche Eigenschaften (Attribute und Methoden) erbt Animal? • Genau wie eine Pflanze soll auch ein Tier seinen eigenen Standort kennen, jedoch soll es im Gegensatz zur Pflanze (siehe Übung (22)) jederzeit möglich sein, den Standort eines Tiers wieder zu ändern. Fügen sie hierfür analog zur Klasse Plant die entsprechende Funktionalität zur Klasse Animal hinzu. (24) Überlegen sie nun, ob für die Klasse Fish nicht eine andere Oberklasse zweckmässig wäre. Bisher ist die Oberklasse LifeForm. Wenn sie eine passend(er)e Oberklasse gefunden haben, ändern sie die Klasse Fish entsprechend ab. Wie sieht die Klassenhierarchie nun aus? Welche Funktionalität erbt Fish? Programmieren in Java 02.Apr.98 49/120 Variablendeklaration 5 Variablendeklaration • Deklaration à la C • Java ist “case sensitive“, d.h. Gross- und Kleinschreibung von Bezeichnern wird unterschieden → A ≠ a etc. • Variablendeklaration kann prinzipiell überall erfolgen (vgl. Algol). ♦ (aber) der Ort und die Attributierung der Variablendeklaration entscheidet jedoch über die Art der Variable • Klassenrumpf und static attributiert → Klassenvariable • Klassenrumpf und kein static → Instanzvariable • Methodenkopf → Methodenparameter Methodenrumpf → lokale Variable • Variablen haben immer einen definierten Wert → 0, /u0000, null ♦ immer möglich (ausser bei Methodenparametern): explizite Initialisierung einer Variablen gleichzeitig (oder besser gleichzeilig) mit Variablendeklaration, z.B. int max = 17; • Syntax generell: Zuerst Attributierung, dann Typ, dann Bezeichner class Foo { static final int AClassConstant = 1; public static Object AClassVariable; private boolean anInstanceVariable; Object anotherInstanceVariable = new Object(…); … void static aMethod( float aParameter ) { char aLocalVariable; … } } Programmieren in Java 02.Apr.98 50/120 Sichtbarkeit und Lebensdauer 6 Sichtbarkeit und Lebensdauer 6.1 Lebensdauer • Ort und Attributierung der Deklaration bestimmen Art der Variable • Variablenart regelt Lebensdauer • Klassenvariablen ♦ Lebensdauer: solange die Klasse existiert • Instanzvariablen ♦ Lebensdauer: solange das Objekt existiert • Methodenparameter und lokale Variablen ♦ Lebensdauer: während die Methode betreten ist (Stapel) Programmieren in Java 02.Apr.98 51/120 Sichtbarkeit und Lebensdauer 6.2 Sichtbarkeit • Ort und Attributierung der Bezeichnerdeklaration regelt Sichtbarkeit bzw. den Gültigkeitsbereich des Bezeichners • namensgleiche Bezeichner verdecken Bezeichner der nächst höheren Sichtbarkeitsebene ♦ namensgleiche Bezeichner für lokale Parameter und Methodenparamenter verdecken Instanzvariablen ♦ eine in einer Unterklasse redefinierte Instanz- oder Klassenvariable verdeckt eine namensgleiche Variable der Oberklasse • Sichtbarkeit von Bezeichnern für KV und IV sowie von Methodenbezeichnern (und mit Einschränkung auch von Klassenbezeichnern) wird über eine entsprechende Attributierung der Bezeichnerdeklaration angegeben. → Verbergen von Bezeichnern unterstützt das Geheimnisprinzip (Information Hiding) Sichtbarkeit public protected standard (ohne At.) private selbes Paket, Unterklasse ✔ ✔ ✔ ✘ selbes Paket, andere Klasse (nicht Unterkl.) ✔ ✔ ✔ ✘ anderes Paket, Unterklasse ✔ ✔✘ ✘ ✘ anderes Paket, andere Klasse (nicht Unterkl.) ✔ ✘ ✘ ✘ Programmieren in Java 02.Apr.98 private protected ab Version 1.0 nicht mehr unterstützt 52/120 Sichtbarkeit und Lebensdauer 6.3 Beispiel: Sichtbarkeit von Variablen package foo; public class Foo { public int a; protected int b; int c; private int d; // muss public sein, sonst kein Export möglich void doSomethingInFoo( int d ) { Foo e = new Foo(); a = 1; b = 2; e.b = 3; c = 5; d = 6; this.d = 7; } } class Guu { void doSomethingInGuu() { Foo aFoo = new Foo(); aFoo.a aFoo.b aFoo.c aFoo.d } = = = = 1; 2; 5; 6; // erlaubt, da <a> public // erlaubt, da gleiches Paket // erlaubt, da gleiches Paket // offensichtlich nicht erlaubt } Programmieren in Java 02.Apr.98 53/120 Sichtbarkeit und Lebensdauer package guu; import foo.Foo; class SubFoo extends Foo { void doSomethingInSubFoo( int d ) { Foo e = new Foo(); SubFoo f = new SubFoo(); a = 1; b = 2; e.b = 3; f.b = 4; c = 5; d = 6; this.d = 7; } // offensichtlich erlaubt // erlaubt da protected // nicht erlaubt; protected, aber <Foo> in anderem Paket // erlaubt; protected und <SubFoo> im gleichen Paket // offensichtlich nicht erlaubt // erlaubt; d ist Methodenparamenter und verdeckt IV // nicht erlaubt; d ist private } class Guu { void doSomethingInGuu() { Foo aFoo = new Foo(); SubFoo aSubFoo = new SubFoo(); // offensichtlich erlaubt, da <a> public aFoo.a = 1; aFoo.b = 2; // nicht erlaubt, da <protected> aSubFoo.b = 4; // nicht erlaubt, da <Guu> nicht Unterkl. von <Foo> aFoo.c = 5; // offensichtlich nicht erlaubt aFoo.d = 6; // offensichtlich nicht erlaubt } } Programmieren in Java 02.Apr.98 54/120 Sichtbarkeit und Lebensdauer 6.4 Übungen (25) Was ist in Java ein einfacher Datentyp (primitive data type) und was ist ein komplexer Datentyp (complex/reference data type)? • Worin unterscheiden sich einfache und komplexe Datentypen? • Was versteht man unter Wertsemantik und was versteht man unter Referenzsemantik? (26) Welche vordefinierten Datentypen kennt Java? (27) Wie und wann können Typen in Java konvertiert werden? (28) Warum wird folgender Ausdruck nicht übersetzt? int i = (int) true; (29) Was sind Klassenvariablen und was sind Klasseninstanzvariablen? (30) Gibt es Klasseninstanzvariablen in Java? (31) Wann und weshalb kann die Verwendung von Klassenvariablen und/ oder Klasseninstanzvariablen problematisch sein? (32) Was sind Instanzvariablen? (33) Was ist der Unterschied zwischen this und super? (34) Betrachten Sie wieder die Klassen Animal (siehe Übung (23)) und Plant (siehe Übung (22)). • Welche gemeinsamen Eigenschaften besitzen diese Klassen (Stichwort location)? • Ist es möglich, gemeinsame Eigenschaften (Attribute, Methoden) in eine gemeinsame Oberklasse zu verlagern? Wenn ja, tun sie dies. Übersetzen und testen die neuen bzw. geänderten Klassen. • Welche Eigenschaften erben Animal, Fish und Plant nun ? (35) Ändern sie die Klasse LifeForm ab, so dass die Standardfarbe über eine Klassen- oder Instanzvariable (DefaultColor) vordefiniert werden kann. Die Standardfarbe ist die Farbe, mit welcher die Methode color() antwortet, wenn keine Farbe explizit gesetzt ist. Entscheiden sie selbst, welche Variablenart (KV, IV oder Kombination) sie verwenden wollen. Welche Auswirkung(en) hat diese Modifikation der Klasse LifeForm auf die Funktionalität der Methode color() in den vorhandenen Unterklassen von LifeForm? Müssen hier Anpassungen vorgenommen werden? Wenn ja, welche? Programmieren in Java 02.Apr.98 55/120 Speicherverwaltung 7 • Speicherverwaltung in Java kann belegter Hauptspeicher nicht explizit (aber implizit, siehe unten) vom Programmierer freigegeben werden ♦ es gibt kein DISPOSE(), dealloc() etc. wie in Pascal, C/C++ ♦ Speicherfreigabe wird automatisch durchgeführt • Idee: “Speicherabfall“ ist weniger gefährlich als Zeiger ins Leere bzw. hängende Referenzen → solange Abfall produzieren, bis neue Speicheranforderungen nicht mehr erfüllt werden können; dann, oder auch parallel zur Programmausführung, Abfall einsammeln → Garbage Collection (GC) ♦ VM führt Garbage Collection durch • Standardlösung für GC: ♦ Phase 1: Markieren aller erreichbaren Heap-Objekte ♦ Phase 2: Freigabe der nicht markierten Heap-Objekte und evtl. Zusammenlegung von fragmentierten Speicherbereichen → d.h. wenn ein Objekt nirgends mehr referenziert wird, dann wird der Speicher, welchen es belegt, bei der nächsten Garbage Collection eingesammelt Beispiel für implizite Speicherfreigabe String[] hugeArray = new String[100000]; /* tue irgend etwas mit <hugeArray>*/ ….doSomething(hugeArray); /* ab hier wird der Array (aus irgendwelchen Gründen) nicht mehr gebraucht, aber dafür nochmals viel Arbeitsspeicher, also sind wir nett und geben dem Garbage Collector die Möglichkeit den Arbeitsspeicher, welchen <hugeArray> belegt, freizugeben */ hugeArray = null; Object[] anotherHugeArray = new Object[100000]; Programmieren in Java 02.Apr.98 56/120 Speicherverwaltung Vorteile der GC • weniger Arbeit für den Programmierer (→ weniger Fehler) • keine Zeiger ins Leere; d.h. sichere Freigabe → Freispeicherliste kann (fast) nicht zerstört werden, siehe Bsp. p := NEW(…) q := p; DISPOSE(p); q^ := 10; Nachteile der GC • u.U. nichtdeterministische Unterbrechung oder Verzögerung der normalen Programmausführung → kann bei wirklich harten Echtzeitanforderungen zu erheblichen Problemen führen (Lsg. “Echtzeitsubset“ der Sprache und eine spezielle VM) Programmieren in Java 02.Apr.98 57/120 Speicherverwaltung 7.1 Übungen (36) Was ist besonders an der Speicherverwaltung in Java? (37) Was ist und wie funktioniert eine Garbage Collection? (38) Welche Vor- und Nachteile bringt eine CG? (39) Ändern sie (analog zu Übung (35)) die Methode location() der Klasse LifeForm so ab, dass diese immer einen sinnvollen DefaultWert liefert, und dieser über eine Variable vordefiniert werden kann. (40) Die künstliche Tier- und Pflanzenwelt soll nun auf dem Bildschirm visualisiert werden. • Zuerst ist etwas Vorarbeit notwendig. Ändern sie hierfür die Oberklasse von LifeForm in java.util.Observable (bisher war die Oberklasse Object) und übersetzen sie ihre Klassen neu. Hinweis: Diese Änderung hat momentan noch keine direkten Auswirkungen, wir benötigen jedoch die Observer/Observable-Funktionalität später noch, wenn Tiere sich selbständig bewegen sollen. • Wenn sie bis hierher alles “richtig“ implementiert haben, d.h. LifeForm ist Unterklasse von Observable und ein Objekt der Klasse LifeForm oder einer ihrer Unterklassen liefert auf die Nachrichten color(), location() und name() hin, sinnvolle Werte ungleich null, dann kann zur Visualisierung die (bereits vorimplementierte) Klasse Zoo verwendet werden. Objekte, die dieser Klasse mittels der Methode add(aLifeForm) hinzugefügt werden, werden dann automatisch angezeigt. • Schreiben sie ein Programm (bzw. eine eigenständige Java-Applikation) in welchem Sie einen Zoo anlegen (analog zum Beispiel unten) und fügen sie je ein Objekt der Klasse LifeForm, Plant, Animal und Fish diesem Zoo hinzu. Zoo zoo = new Zoo(); … Animal animal1 = new Animal(); … zoo.add( animal1 ); Programmieren in Java 02.Apr.98 58/120 Ausdrücke und Operatoren 8 Ausdrücke und Operatoren Ausdrücke Ein Ausdruck ist eine wohlgeformte, d.h. entsprechend der Syntax der Sprache aufgebaute Reihe von Variablen, Operatoren und Methodenaufrufen, die bei Ausführung des Programms zu einem Wert evaluieren. Mittels Ausdrücken werden • Werte berechnet • Zuweisungen durchgeführt und • der Kontrollfluss gesteuert Beispiele i++ a + b c = a + i System.in.read() != -1 Operatoren • Operatoren führen eine Operation mit ihren Operanden durch • Operatoren haben entweder ein oder zwei Operanden • in Java können vordefinierte Operatoren nicht redefiniert werden Programmieren in Java 02.Apr.98 59/120 Ausdrücke und Operatoren 8.1 • Arithmetische Operatoren arithmetische Operatoren können auf Operanden angewandt werden, deren Typ numerisch ist, also byte, short, int, long, float und double 8.1.1 Unäre Arithmetische Operatoren Operator Anwendung Beschreibung + + op … op * 1 – - op Negation von op (op*(-1)) ++ ++op Inkrementiert op um 1; Evaluation des Ausdrucks (d.h. Bestimmung des Wertes des Ausdrucks) erfolgt nach dem Inkrement -- --op Dekrementiert op um 1; Evaluation des Ausdrucks erfolgt nach dem Dekrement ++ op++ Inkrementiert op um 1; Evaluation des Ausdrucks erfolgt vor dem Inkrement -- op-- Dekrementiert op um 1; Evaluation des Ausdrucks erfolgt vor dem Inkrement 8.1.2 • Binäre Arithmetische Operatoren Operator Anwendung Beschreibung + op1 + op2 Summe von op1 plus op1 – op1 - op2 Differenz op1 minus op2 * op1 * op2 Produkt von op1 multipliziert mit op2 / op1 / op2 ganzzahliger Quotient von op1 dividiert durch op2 % op1 % op2 Divisionsrest der Division op1 geteilt durch op2 (op1 mod 0p2) Anmerkung: Der binäre Operator + ist überladen und führt nicht nur Additionen durch, sondern auch String-Konkatenationen, wenn die beiden Operatoren vom Typ String sind Programmieren in Java 02.Apr.98 60/120 Ausdrücke und Operatoren 8.2 • Vergleichsoperatoren (relational operators) Vergleichsoperatoren geben in Form eines Wahrheitswertes Auskunft darüber, ob eine bestimmte Beziehung zwischen den Operanden besteht oder nicht. Operator Anwendung Beschreibung > op1 > op2 Evaluiert zu true, wenn op1 grösser als op2 ist; sonst false >= op1 >= op2 true, wenn op1 grösser oder gleich op2 ist; sonst false < op1 < op2 true, wenn op1 kleiner als op2 ist; sonst false op1 <= op2 true, wenn op1 kleiner oder gleich op2 ist; sonst false == op1 == op2 true, wenn op1 gleich op2 ist, d.h. op1 und op2 enthalten den gleichen Wert oder referenzieren beide ein identisches Objekt; sonst false != op1 != op2 true, wenn op1 ungleich op2 ist; sonst false op1 instanceof op2 true, wenn op1 instanz der Klasse op2 (oder einer Oberkasse von op2 ist) <= instanceof Programmieren in Java 02.Apr.98 61/120 Ausdrücke und Operatoren 8.3 Logische Operatoren (conditional operators) • Logische Operatoren führen logische Operationen auf ihren Operanden durch. • Die Operanden werden nur bedingt ausgewertet und müssen zu einem Wert vom Typ boolean evaluieren. Operator Anwendung Beschreibung && op1 && op2 logisches UND || op1 || op2 logisches ODER ! 8.4 Negation ! op Operatoren zur Bitmanipulation • Bitmanipulationsoperatoren führen, wie logische Operatoren auch, logische Operationen auf ihren Operanden durch. Im Unterschied zu logischen Operatoren jedoch nicht mit den Wahrheitswerten, zu welchen die Operanden evaluieren, sonder direkt auf den Bits der Operanden. • Bitmanipulationsoperatoren werden unbedingt ausgewertet! Operator Anwendung Beschreibung >> op1 >> op2 vorzeichensensitive Verschiebung der Bits von op1 um op2-Stellen nach rechts << op1 << op2 vorzeichensensitive Verschiebung der Bits von op1 um op2-Stellen nach links >>> op1 >>> op2 nicht vorzeichensensitive Verschiebung der Bits von op1 um op2-Stellen nach rechts & op1 & op2 bitw. AND-Verknüpfung von op1 und op2 | op1 | op2 bitw. OR-Verknüpfung von op1 und op2 ^ op1 ^ op2 bitw. XOR-Verknüpfung von op1 und op2 ~ ~ op bitweises Invertieren von op Programmieren in Java 02.Apr.98 62/120 Ausdrücke und Operatoren 8.5 • Zuweisungsoperatoren (assignment operators) Zuweisungsoperatoren werden benutzt, um Variablen (und initial auch Konstanten) Werte zuzuweisen. Operator Anwendung Beschreibung = op1 = op2 Weist op1 den Inhalt (Wert oder Referenz) von op2 zu += op1 += op2 op1 = op1 + op2 -= op1 -= op2 op1 = op1 - op2 *= op1 *= op2 op1 = op1 * op2 /= op1 /= op2 op1 = op1 / op2 %= op1 %= op2 op1 = op1 % op2 &= op1 &= op2 op1 = op1 & op2 |= op1 |= op2 op1 = op1 | op2 ^= op1 ^= op2 op1 = op1 ^ op2 <<= op1 <<= op2 op1 = op1 << op2 >>= op1 >>= op2 op1 = op1 >> op2 >>>= op1 >>>= op2 op1 = op1 >>> op2 8.5.1 Zuweisbarkeit e = v … die Variablen e und v mit den Typen Te und Tv sind u.a. zuweisbar, wenn die zugehörigen Typen Te und Tv: • gleich sind • beide numerische Typen sind und der Wertebereich von Te den von Tv umfasst • beides Klassen sind und Te Oberklasse von Tv ist • Te eine Klasse ist, Tv ein Interface ist und Te Tv implementiert • Te die Klasse Objekt ist und Tv ein Feldtyp ist Programmieren in Java 02.Apr.98 63/120 Ausdrücke und Operatoren 8.6 Auswertung von Ausdrücken • Ausdrücke werden von links nach rechts ausgewertet • Operanden werden vor der eigentlichen Operation ausgewertet • Parameterlisten für Methoden werden ebenfalls von links nach rechts ausgewertet • Klammerung und Auswertungsreihenfolge für Operatoren wird grundsätzlich beachtet Auswertungsreihenfolge →→→ Postfix Unär Obj.-Erz., Konv. Multiplikative Additive ↓ [] . (params) expr++ expr-++expr --expr +expr -expr ~ ! new (type)expr * / % + - logischer Shift << >> >>> Relationale Op. < > <= >= instanceof ↓ Vergleichsop. == != ↓ bitweises UND & bitw. Exkl-ODER ^ bitw. Inkl.-ODER | logisches UND && log. ODER || bed. Op. ? : Zuweisung Programmieren in Java = += -= *= /= %= ^= &= |= <<= >>= >>>= 02.Apr.98 64/120 Ausdrücke und Operatoren 8.7 • Beispiele Ausdrücke und Operatoren Welchen Wert hat b? byte b = (byte) 64; b <<= 1; • Ist dies korrekt? boolean bool = false; bool++; • Wie lautet die Ausgabe? i = 0; j = 1; System.out.println( i+++j ); System.out.println( i+++i ); • Was geben die Schleifen aus? i = 0; while ( i++ <= 3 ) System.out.println( i ); i = 0; while ( ++i <= 3 ) System.out.println( i ); for ( i = 0; i <=3; i++ ) System.out.println( i ); for ( i = 0; i <=3; ++i ) System.out.println( i ); • Wo kann es hier Probleme geben? (beachten die den verwendeten Operator und die Auswertungsreihenfolge) if (( array == null ) | ( array.length == 0 )) System.out.println( "null or length == 0" ); else System.out.println( "valid" ); Programmieren in Java 02.Apr.98 65/120 Ausdrücke und Operatoren 8.8 Übungen (41) Welche Ausgabe macht das folgende Programm? Begründen sie Ihre Lösung? Ist das Ergebnis offensichtlich oder nicht? class Application { static int confusing( int j, int k ) { return j+k; } public static void main( String[] args ) { int[] a = { 3, 4, 5 }; int i = 1; a[i] = i++; System.out.println(a[0]+", "+a[1]+", "+a[2]); System.out .println(i+", "+confusing(i++,i)+", "+i); } } (42) Die Methode reverse hat zwei Arrays als Parameter und soll die Komponenten des ersten Arrays in umgekehrter Reihenfolge an den zweiten Parameter zuweisen. Funktioniert reverse unter allen Umständen korrekt? Korrigieren sie ggf. reverse! public static void reverse( char[] a, char[] b ) { for ( int i = 0; i < a.length; i=i+1 ) { b[i]=a[a.length-1-i]; } } Programmieren in Java 02.Apr.98 66/120 Ausdrücke und Operatoren (43) Vervollständigen Sie die Klasse LinkedList indem Sie die Methoden addLast und remove implementieren. Geben Sie jeweils eine rekursive und eine iterative Lösung an. class LinkedList { Object element; LinkedList next; void addLast( Object elementToAdd ) { … } LinkedList remove( Object elementToRemove ) { … } } (44) Schreiben Sie ein kleines Testprogramm bzw. einen Testtreiber, um die von Ihnen implementierte LinkedList zu testen. (45) Die Klasse Animal soll nun erweitert werden. Für jedes Tier soll ein Partner zugeweisen werden können. Legen Sie hierfür eine entsprechende Instanzvariable partner vom Typ Animal an. Implementieren sie ferner die Zugriffsmethoden Partner partner() und void partner(Partner partner). Erstere (der “Getter“) sollte mit dem aktuell gesetzten Partner antworten. Zweitere (der “Setter“) sollte einen Partner zuweisen. Beachten sie, dass das Zuweisen eines neuen Partners zur Folge hat, dass der zuzuweisende Partner ebenfalls einen Partner bekommt, nämlich genau das Objekt, welchem ein Partner zugewiesen werden soll (-> “hatPartner“ ist eine symmetrische Relation). Programmieren in Java 02.Apr.98 67/120 Steueranweisungen 9 Steueranweisungen 9.1 Bedingte Anweisungen • if-Anweisung wie in C ♦ b1, b2 müssen vom Typ boolean sein!!! if ( b1 ) { … } else if ( b2) { … } … else { … } • switch-Anweisung (leider) wie in C ♦ i darf vom Typ byte, char, short, int, oder long sein switch ( i ) { case 1: System.out.println(1); case 2: System.out.println(2); break; default:System.out.println(3); break; } ♦ was gibt die obenstehende switch-Anweisung mit i=0, i=2 und i=4 aus? Programmieren in Java 02.Apr.98 68/120 Steueranweisungen 9.2 Iterative Anweisungen • For-Schleife: for (int i; i < n; i++) { … } • While-Schleife: while( B ) { … } • Do-While-Schleife: do { … } while ( B ) ♦ wie Repeat-Until-Schleife in Pascal, Modula etc. Apropos • Java kennt keine Prozedurparameter, wie z.B. Modula II oder parametrisierbare Blöcke, wie z.B. Smalltalk → daher ist es umständlich, Iterator-Methoden (z.B. do: oder collect: in ST) zu erstellen. #( 1 $a ‘einWort’ #einSymbol ) do: [ :i | Transcript show: i printString] ♦ Als Alternative gibt es sogenannte “Enumerations” (siehe auch Kapitel 4.4) Enumeration anEnumeration = aCollection.elements(); while ( aEnumeration.hasMoreElements() ) { System.out.println( aEnumeration.nextElement() ); } Programmieren in Java 02.Apr.98 69/120 Steueranweisungen 9.2.1 Beispiel /** Klasse mit Methoden zur Berechnung der Fakultät */ class Faculty { static int recursive( int n ) { if ( n > 1) { return ( n * recursive( n-1 )); } else { return 1; }; } static int iterative( int n ) { int result = 1; for ( int i = 2; i <= n; i = i+1 ) { result = result * i; } return result; } } /** Hauptprogramm */ class Application { public static void main( String args[] ) { System.out.println( Faculty.recursive(5) ); System.out.println( Faculty.iterative(5) ); } } Programmieren in Java 02.Apr.98 70/120 Steueranweisungen 9.3 Übungen (46) Schreiben sie die Methode static int iterative( int n ) aus des Beispiels aus Kapitel 9.2.1 um, so dass die Methode immer noch semantisch das gleiche tut (nämlich die Fakultät berechnen), aber einmal (a) eine while-Schleife verwendet wird und das andere mal (b) eine do-while-Schleife verwendet wird. [Text aus Ludewig, Einführung in die Informatik] Leonardo Fibonacci hat in seinem 1202 erschienen “Liber abaci“ die Frage gestellt, wieviele Kaninchen-Pärchen es nach n Jahren gebe, wenn man im Jahr 1 mit einem Pärchen beginnt und jedes Pärchen vom zweiten Jahr an ein weiteres Pärchen Nachwuchs hat. Offenbar entsteht die folgende Zahlenreihe Jahr Zahl der Pärchen 1 1 2 1 3 2 4 3 5 5 6 8 7 8 … 13 21 … Diese Art von Wachstum gibt es auch in vielen anderen Bereichen, so dass die sog. Fibonacci-Zahlen allgemeine Bedeutung haben. Sie lassen sich wie folgt definieren Fibonacci(n) = 1 Fibonacci(n) = Fibonacci(n-1) + Fibonacci(n-2) für n = 1, 2 für n > 2 (47) Implementieren Sie (analog zur Klasse Faculty in Beispiel 9.2.1) eine Klasse Fibonacci mit zwei Methoden, von welchen die eine die Fibonacci-Zahlen rekursiv und die andere die Fibonacci-Zahlen iterativ berechnet. class Fibonacci { static int recursive( int n ) { … } static int iterative( int n ) { … } } Programmieren in Java 02.Apr.98 71/120 Methoden 10 Methoden public … Object aMethod( int aParameter, … ) throws anException, … { // Methodenrumpf } • Methoden repräsentieren die Operationen, welche ein Objekt kennt bzw. auf diesem ausführbar sind Methodenkopf • Im Methodenkopf wird deklariert: Die Attributierung, der Typ des Rückgabewert, der Methodenbezeichner, die Liste der formalen Parameter und die Exceptions welche in der Methode ausgelöst werden können • Bezeichner + Parameter zusammen müssen eindeutig sein • Überladen von Methoden ist zulässig, solange Methodenbezeichner und Parameterliste eindeutig sind Methodenrumpf • im Rumpfblock der Methode steht der eigentliche (Arbeits-)Code der Methode, also die Deklarationen der lokalen Variablen, Ausdrücke und Steueranweisungen etc. Programmieren in Java 02.Apr.98 72/120 Methoden 10.1 Attributierung public … Object aMethod( int aParameter, … ) throws anException, … die Attributierung der Methode deklariert: die Methodenart (Klassen- oder Instanzmethode), die Sichtbarkeit (des Methodenbezeichners), abstrakte Methoden, die Synchronität von Methodenaufrufen, native Methoden und nicht redefinierbare Methoden mögliche Attributierungen ♦ protected, public, private, static wie bei Variablen (siehe Kapitel 6.2), zusätzlich gibt es noch folgende Attributierungen ♦ abstract → abstrakte Methode, d.h es existiert keine Implementierung → abstrakte Klasse ♦ synchronized → für Threadsynchronisation. Erwirbt exkl. Lock auf Objekt ♦ native → Methode ist in einer anderen Sprache implementiert (üblicherweise C oder C++) und wird nicht von der VM interpretiert ♦ final → die Methode kann in einer Unterklasse nicht mehr redefiniert werden Programmieren in Java 02.Apr.98 73/120 Methoden 10.2 Rückgabeparameter public … Object aMethod( int aParameter, … ) throws anException, … • deklariert den Typ des Ergebnisses bzw. des Ergebniswertes, mit welchem die Methode antwortet • die Deklaration des Typs des Rückgabeparameters kann nicht weggelassen werden ♦ Schlüsselwort void wird benutzt, wenn die Methode kein Ergebnis zurückliefert ♦ eckige Klammern zeigen an, dass ein Array zurückgegeben wird, z.B. public int[] aMethod() {…} 10.3 Parameterliste, Formale Parameter public … int add1( int aParameter, … ) throws anException, … { aParameter = aParameter + 1; return aParameter; } • Die Objekte und/oder Methoden, welche an eine Methoden übergeben werden können, werden durch Angabe des Typs des Parameter und eines Bezeichners (formaler Parameter) deklariert • im Methodenrumpf sind formale Parameter wie Variablen ansprechbar • es ist ausschliesslich Call-By-Value möglich, d.h. der Wert einer Variablen wird vor dem Aufruf der Methode kopiert. Eine Änderung des Werts der Variablen betrifft also nur den Methodenrumpf Programmieren in Java 02.Apr.98 74/120 Methoden 10.4 Deklaration der möglichen Exceptions public … Object aMethod( int aParameter, … ) throws anException, … (siehe Kapitel 11) 10.5 Methodenaufruf anObject.aMethod( actualParameter, … ); es wird immer die Methode aufgerufen die • anwendbar und • “am speziellsten“ ist • anwendbar heisst ♦ gleicher Methodenbezeichner ♦ gleiche Anzahl von Parametern ♦ aktuelle Parameter (Aufruf) sind den formalen Parametern (Methodendeklaration) zuweisbar • “am speziellsten“ heisst ♦ die Methode aMethod1 ist spezieller als die Methode aMethod2, wenn die formalen Parameter der Methode aMethod1 als aktuelle Parameter der Methode aMethod2 dienen könnten, aber nicht umgekehrt Programmieren in Java 02.Apr.98 75/120 Methoden 10.6 Beispiel class Foo { int i = 1; void doSomething() { i = i+1; } void doSomething( int i ) { i = i+1; } void doSomething( Foo aFoo ) { aFoo.i = i+1; } } class Application { public static void main( String[] args ) { Foo aFoo = new Foo(); System.out.println( aFoo.i ); aFoo.doSomething(); System.out.println( aFoo.i ); aFoo.doSomething( 1 ); System.out.println( aFoo.i ); aFoo.doSomething( aFoo ); System.out.println( aFoo.i ); } } Programmieren in Java 02.Apr.98 76/120 Methoden 10.7 Instanzmethoden … Object aMethod( int aParameter, … ) throws anException, … • sind Exemplaroperationen • wird eine Instanzmethode aufgerufen, so wird implizit eine Referenz auf das aufrufende Objekt mit übergeben 10.8 Klassenmethoden static … Object aMethod( int aParameter, … ) throws anException, … • sind keine Exemplaroperationen, sondern beziehen sich auf alle Objekte der Klassen • Schlüsselwort static legt fest, dass es sich um eine Klassenmethode handelt • in einer Klassenmethode kann ♦ this. … nicht verwendet werden ♦ nicht auf Instanzvariablen zugegriffen werden ♦ keine Instanzmethode aufgerufen werden Programmieren in Java 02.Apr.98 77/120 Methoden 10.9 Konstruktoren (constructors) Erzeugen und Initialisieren von Objekten geschieht in Java über sogenannte Konstruktoren (constructor methods, constructors). • Konstruktoren sind spezielle Methoden und dienen ♦ der Objekterzeugung sowie ♦ der Objektinitialisierung • Konstruktoren sind immer genau so, wie die zugehörige Klasse bezeichnet, z.B. class Foo { Foo() { // Code zur Initialisierung des Objekts } • // Klassendeklaration // Konstruktor mehrere Konstruktoren sind erlaubt, wenn sie sich eindeutig durch die Signatur unterscheiden, z.B. Foo( int anInteger ) … // nicht legal, da gleiche Signatur wie Foo( int anInteger ) Foo( int withOtherIdentifier ) … // legal, da Typ <anObject> und <anInteger> unterschiedlich Foo( Object anObject ) … // eindeutig legal Foo( int anInteger, Object anObject ) … • Konstruktoren bzw. deren Bezeichner werden nicht vererbt ♦ super( … ) ruft Konstruktoren der Oberklasse auf ♦ this( … ) ruft andere Konstruktoren der Klasse auf Programmieren in Java 02.Apr.98 78/120 Methoden 10.9.1 • Verkettung von Konstruktoren (constructor chaining) bevor ein Konstruktor ausgeführt wird, werden immer erst die Konstruktoren der Oberklasse ausgeführt ♦ wenn nichts anderes angegeben wird, dann wird der Standardkonstruktor Klassenname() aufgerufen Beispiel class Foo { Foo() { System.out.println( "Foo()" ); } } class FooSub extends Foo { FooSub() { System.out.println( "FooSub()" ); } } → Ausgabe Foo() FooSub() • Ist super( arg1, arg2 … argn ) die erste Anweisung im Rumpfblock eines Konstruktors, dann wird der entsprechende n-wertige Konstruktor der Oberklasse aufgerufen, ohne dass zusätzlich der Standardkonstruktor aufgerufen wird Programmieren in Java 02.Apr.98 79/120 Methoden 10.10 Initialisierung von Klassen (class initializer/static initializer) class Foo { static int[] smallArray = { 1, 2, 3 }; static int[] hugeArray = new int[1000]; static { for ( int i = 0, i < hugeArray.length, i++ ) { hugeArray[i] = i; } } … } • ein class initializer oder static initializer ist ein spezieller Code-Block (oder eine spezielle Methode) zur initialisierung einer Klasse ♦ keine Name ♦ keine Parameter können übergeben werden (von wo auch?!) ♦ kein Rückgabewert • wenn eine Klasse zum ersten mal von der VM geladen wird (siehe Kapitel 1.5.1), dann werden sämtliche Initialsierungen (Variableninitialisierung und class initializer) in der Reihenfolge, in welcher sie im Code erscheinen, aufgerufen. Programmieren in Java 02.Apr.98 80/120 Methoden 10.11 Finalizer protected void finalize() … • ein “Finalizer“ ist eine spezielle Methode, wie main(…) auch, • Finalizer dienen dazu, “aufzuräumen“ • werden vor der Garbage Collection – d.h. bevor das Objekt zerstört und der belegte Speicher freigegeben wird – genau einmal aufgerufen • im Gegensatz zu Konstruktoren (siehe Kapitel 10.9) wird nicht automatisch auch der Finalizer der Oberklasse aufgerufen; dies muss ggf. mittels super.finalize() vom Programmierer selbst getan werden • der genaue Aufrufzeitpunkt ist nicht determiniert, d.h. der exakte Zeitpunkt des Finalizer-Aufrufs ist nicht garantiert • alle nicht behandelten Exceptions werden ignoriert Beispiel protected void finalize() { try { myStream.close(); } catch ( IOException e ) { System.err.println (“Stream konnte nicht geschlossen werden”); } } Programmieren in Java 02.Apr.98 81/120 Methoden 10.12 Übungen (48) Beschreiben sie den Parameterübergabemechanismus der Sprache Java. Welche Eigenheiten/Probleme gibt es dabei und wie kann man sie ggf. umgehen? (49) Wie würde die Ausgabe des Programms des Beispiels in Kapitel 10.6 lauten, wenn die Methode doSomething( int i ) folgendermassen geändert würde? Begründen Sie Ihr Ergebnis! void doSomething( int i ) { this.i = this.i+i; } (50) Würde die Ausgabe des Programms des Beispiels in Kapitel 10.6 anders lauten, wenn folgende Methode hinzugefügt würde? Begründen Sie Ihr Ergebnis! void doSomething( Object i ) { this.i = this.i+25; } (51) Was ist eine Klassenmethode und was ist eine Instanzmethode? Wie unterscheiden sich Klassen- und Instanzmethoden? (52) Welchen Restriktionen bestehen bezüglich der Verwendung von Variablen und Methoden bei Klassenmethoden? Warum bestehen diese Restriktionen? (53) Für die Klasse LinkedList (Objekte dieser Klasse repräsentieren eine verkettete Liste) soll eine Methode sortieren(…) implementiert werden. • Würden Sie diese Methode als Klassen- oder als Instanzmethode realisieren? Begründen Sie Ihre Entscheidung! (54) Für die Klasse Teilnehmer (Objekte dieser Klasse repräsentieren bestimmte Teilnehmerdaten – wie Name, Legi-Nr. etc. – für Teilnehmer des Java Kurses) soll eine Methode neuenTeilnehmerAnlegen(…) implementiert werden. • Würden Sie diese Methode als Klassen- oder als Instanzmethode realisieren? Begründen Sie Ihre Entscheidung! Programmieren in Java 02.Apr.98 82/120 Methoden (55) Erstellen sie selbst (d.h. ohne eine solche Klasse aus einem Java Paket zu importieren) eine Klasse Stack, welche einen Stack mit den Operationen push(…) und pop(…) realisiert. Hinweis: Verwenden, ändern und/oder erweitern sie hierzu, die in Aufgabe (43) erstellte LinkedList Übersetzen und testen Sie Ihr Programm. (56) Was ist ein “Finalizer“ und wozu dient er? (57) Welche Ausgabe macht das folgende Programm? Warum ist das so? Begründen Sie Ihre Lösung (bitte nicht nur abtippen, übersetzen und ausführen ohne weitere Herleitung) class Foo { Foo() { init(); } void init() { System.out.println( "Foo.init()" ); } } class FooSub extends Foo { FooSub() { init(); } void init() { System.out.println( "FooSub.init()" ); } } class Application { public static void main( String[] args ) { new FooSub(); } } Programmieren in Java 02.Apr.98 83/120 Methoden (58) Welche Ausgabe macht das folgende Programm? Warum ist das so? Begründen Sie Ihre Lösung (bitte nicht nur abtippen, übersetzen und ausführen ohne weitere Herleitung) class Foo { Foo() { this("called from Foo()"); } Foo( String aString ) { System.out.println("Foo(String)"+aString); } } class FooSub1 extends Foo { FooSub1( String aString ) { super("called from FooSub1(String)"); System.out.println("FooSub1(String)"+aString); } } class FooSub2 extends Foo { FooSub2( String aString ) { System.out.println("FooSub2(String)"+aString); } } class Application { public static void main( String[] args ) { new FooSub1( "called from main(String[])" ); new FooSub2( "called from main(String[])" ); } } Programmieren in Java 02.Apr.98 84/120 Methoden (59) Wieder soll die Klasse Animal erweitert werden. In Übung (45) wurde eine Funktionalität implementiert, welche es erlaubt jedem Tier einen Partner zuzuweisen und den aktuellen Partner eines Tieres abzufragen. In dieser Übung geht es darum, dass ein Tier einen eventuell vorhandenen Partner benachrichtigt, wenn es sich bewegt. Der Partner kann anhand dieser Benachrichtigung erkennen, dass sich sein Partner bewegt hat und ggf. nachziehen (wenn er das will). Um diese Funktionalität zu realisieren, sollen neue Methoden angelegt werden. Zum einen ist dies die Methode void moveTo(Point newLocation) und die Methode void partnerMoved(). Die Methode moveTo(…) soll ein Tier an den mittels newLocation angegebenen Ort bewegen (mittels this.location(…)) und anschliessend einen eventuell vorhandenen Partner benachrichtigen (mittels partner().partnerMoved()). Die Methode partnerMoved() wird vom Partner aufgerufen wenn er sich bewegt hat und dient – wie bereits erwähnt – dazu, zu signalisieren, dass sich der Partner bewegt hat. Als Reaktion auf die Bewegung des Partners soll ein Tier sich mit 75% Wahrscheinlichkeit an den Ort bewegen, welcher jeweils 15 Pixel rechts und unterhalb des Ortes liegt, an welchen sich der Partner hinbewegt hat; d.h. in drei von vier Fällen “trottet“ ein Tier hinterher, wenn sich sein Partner bewegt hat. Hinweis zur Realisierung: Ein Objekt der Klasse java.util.Random dient der Generierung von Zufallszahlen und kann verwendet werden, um die 75% “Hinterherzugswahrscheinlichkeit“ zu realisieren (random.nextFloat() < 0.75). class Animal extends LifeForm { … /** Bewegt das Tier und benachrichtigt ggf. den Partner. */ void moveTo( Point newLocation ) { … } /** Wird vom Partner aufgerufen, wenn er sich bewegt hat. Mit einer Wahrscheinlichkeit von 75% bewegt sich das Tier dann auch Programmieren in Java 02.Apr.98 85/120 Methoden ungefähr dort hin wo der Partner ist. */ void partnerMoved() { … } } (60) Als nächstes soll für die Klasse Animal eine Methode moveRandom() hinzugefügt werden. Wird diese Methode aufgerufen, dann soll sich das Tier auf eine zufällig ausgewählte Position (x,y) mit 0≤x≤190 und 0≤y≤310 bewegen. Auch soll ein ggf. vorhandener Partner benachrichtigt werden, wenn sich das Tier bewegt hat; sie können also auf die in Übung (59) implementierte Methode moveTo(…) zurückgreifen. Hinweis zur Realisierung: Um die neue, zufällige Position des Tiers zu berechnen, können sie wieder ein Objekt der Klasse Random verwenden. Eine mögliche x-Position berechnet sich dann folgendermassen: newX = (int) (random.nextFloat()*310). Noch ein Hinweis zur Realisierung: Vielleicht haben sie ja in Übung (59) bereits eine entsprechende Instanzvariable zur Generierung von Zufallszahlen angelegt, welche sie nun wieder verwenden können. Programmieren in Java 02.Apr.98 86/120 Ausnahmebehandlung (exceptions & errors) 11 • Ausnahmebehandlung (exceptions & errors) in Java ist eine speziell Ausnahme- bzw. Fehlerbehandlung eng in die Programmiersprache integriert, d.h. es müssen beispielsweise keine speziellen Rückgabewerte (z.B. -1) vereinbart werden, um Fehler zu erkennen Idee: Beim Eintritt einer bestimmten Ausnahmesituation (exception) wird automatisch zu einem geeigneten Programmteil (exception handler) verzweigt, welcher die Situation (hoffentlich) bereinigt. Danach wird das Programm von einem definierten Punkt an fortgesetzt • die Ausnahmebehandlung in Java hat “Recovery-Semantik“ (im Gegensatz zur “Resume-Semantik“ z.B. in PL/I), d.h. nachdem eine Exception behandelt wurde, fährt die Programmsteuerung am Ende des umschliessenden Konstrukts fort (und nicht am Ende derjenigen Anweisung, welche die Exception ausgelöst hat) • wird eine Exception nicht abgefangen und behandelt so wird sie entsprechend der lexikalischen/statischen Blockstruktur “nach oben“ propagiert, bis ein entsprechender Exception Handler gefunden wird • wird kein Exception Handler gefunden, d.h. wird die Exception nirgendwo abgefangen und behandelt, so bricht das Programm bzw. der Thread mit einem Laufzeitfehler ab Eine Exception in Java muss entweder ♦ behandelt, d.h. abgefangen werden oder ♦ es muss (zumindest) im Methodenkopf explizit deklariert werden, dass dies nicht geschieht → “catch or declare”-Regel Programmieren in Java 02.Apr.98 87/120 Ausnahmebehandlung (exceptions & errors) 11.1 Auslösen, Weiterleiten und Abfangen Auslösen von Exceptions (raising/throwing exceptions) • throw anException … löst eine Exception aus, z.B. throw new EOFException(); Propagieren, d.h. “nicht-abfangen“ und weiterleiten von Exceptions (exception propagation) • throws AnException, AnotherException, … deklariert die Exceptions, welche eine Methode auslösen kann und die nicht von der Methode selbst abgefangen und behandelt werden, z.B. public int read() throws IOException Abfangen und behandeln von Exceptions (exception handling) • mittels der try/catch/finally-Anweisungen werden Exceptions in Java abgefangen und behandelt try { // der Block, in welchem eine Exception auftreten kann } catch ( AnException e ) { // der Block, in welchem eine Exception vom Typ <AnException> behandelt wird } catch ( AnotherException e ) … finally { // der Code in diesem Block wird IMMER ausgeführt, egal wie der try-Block // verlassen wurde } Programmieren in Java 02.Apr.98 88/120 Ausnahmebehandlung (exceptions & errors) 11.2 Beispiel Exceptions Die Entscheidung, was bei einer Exception zu tun ist, kann nicht immer lokal entschieden werden, sondern ist problembereichsabhängig und muss von übergeordneten Systemkomponenten vorgenommen werden (z.B ein Sensor kann aufgrund von Erschütterungen/Turbulenzen keine korrekten Daten liefern. Nur der Sensor kann diesen Zustand erkennen und eine Exception “werfen“). … while (true) { try { data = sensor.getData(); airplane.control( Data ); } catch ( … ) { lass den Zyklus aus, aber “keep flying“ } } … vs. … try { while (true) { data = sensor.getData(); NuclearPowerPlant.control( Data ); } } catch ( … ) { shut down } Programmieren in Java 02.Apr.98 89/120 Ausnahmebehandlung (exceptions & errors) 11.3 Exceptions & Errors • eine Exception in Java zeigt eine Ausnahmesituation an, die im regulären Programmbetrieb nicht vorgesehen ist und besonderer Behandlung bedarf, damit das Programm weiterlaufen kann • ein Error in Java zeigt einen schwerwiegenden Fehler oder ein Systemversagen an, welches in den allermeisten Fällen dazu führt, dass eine Programmausführung abgebrochen werden muss → Errors sollten – auch wenn dies möglich ist – nicht abgefangen werden, da sich das System in einem undefinierten Zustand befinden kann 11.4 • Realisierung jede Exception und jeder Error wird in Java durch ein Objekt irgend einer Unterklasse der Klasse java.lang.Throwable repräsentiert ♦ standardmässig hat die Klasse Throwable zwei Unterklassen. Die Klasse Exception und die Klasse Error (siehe Kapitel 11.3) • wird eine Exception ausgelöst, so wird das in der throw-Anweisung referenzierte Objekt benutzt, um nach einer passenden catch-Anweisung zu suchen ♦ gesucht wird anhand der lexikalischen Blockstruktur von “unten nach oben“. Wird auf einer Ebene keine passende catch-Anweisung gefunden so wird die Exception “nach oben“ propagiert. ♦ eine catch-Anweisung ist passend, wenn der instanceof-Operator angewandt auf das Exception-Object und die Klasse des formalen Parameters einer catch-Anweisung true ergibt. • wird eine passende catch-Anweisung gefunden, so wird der entsprechende Rumpfblock ausgeführt. Anschliessend fährt die Programmsteuerung am Ende der entsprechenden try/catch/finalizeAnweisung fort • wird keine passende catch-Anweisung gefunden so bricht das Programm oder der betroffene Thread mit einem Laufzeitfehler ab Programmieren in Java 02.Apr.98 90/120 Ausnahmebehandlung (exceptions & errors) 11.5 • Anwendung von Exceptions mittels Exceptions und Exception Handlern soll(t)en unerwartete – nicht aber unvorhersehbare – Bedingungen/Fehler/Situationen/ Zustände, so abgefangen/behandelt werden, dass ein Programm möglichst nicht abbricht, sondern ab einem definierten Wiedereinstiegspunkt weiterlaufen kann. ♦ Code für Fehlerbehandlung wird separiert vom Code für regulären Programmablauf → Code wird besser verständlich • Exceptions sind nicht dazu da, Bedingungen/Situationen/Zustände zu behandeln, die bei jedem regulären Programmablauf zu erwarten sind ♦ Code für Fehlerbehandlung würde nun nicht separiert vom Code für regulären Programmablauf → in diesem Fall bieten Exceptions keine Vorteile gegenüber verschachtelten if-Anweisungen → Code wird zunehmend schwierig(er) zu verstehen, da der jeweilige Kontext schlechter zu erfassen ist → effiziente Optimierung des Übersetzers ist (fast) nicht mehr möglich, da nun zwei “reguläre“ Kontroll- und Datenflüsse bestehen würden schlechtes Beispiel: int[] anArray = { 3, 1, 2, 5, 4 }; int sum = 0; try { for( int i = 0; i<Integer.MAX_VALUE; i = i+1 ) { sum = sum + anArray[i]; } } catch( ArrayIndexOutOfBoundsException e ) {} Programmieren in Java 02.Apr.98 91/120 Ausnahmebehandlung (exceptions & errors) 11.6 Übungen (61) Was ist eine Exception in Java? (62) Was versteht man in Java unter einem Error? (63) Wie wird eine Exception in Java ausgelöst bzw. geworfen? (64) Wie kann man Exceptions in Java abfangen? (65) Was sagt die “catch or declare“-Regel aus? (66) Ändern sie die Methode reverse aus Übung (42) so ab, dass bei einer Eingabe, bei welcher die Methode nicht mehr korrekt funktionieren kann (z.B. identische Arrays werden übergeben) entsprechende Exceptions geworfen werden. (67) Warum ist folgende Verwendung des “Java-Exception-Handlings“ zumindest schlechter Stil? try { FileInputStream input; input = new FileInputStream("duesentrieb.txt"); while (true) { int aValue = input.read(); if ( aValue == -1 ) { System.out.println(); throw ( new EOFException() ); } else System.out.print( (char) aValue ); } } catch ( IOException e ) { } Programmieren in Java 02.Apr.98 92/120 Threads und Synchronisation 12 Threads und Synchronisation Threads • Threads sind eigenständig ablaufende (Sub-)Prozesse • Threads → haben einen separaten Kontrollfluss → haben eine eigene Lebendauer → konnen mit anderen Prozessen kommunizieren und Daten gemeinsam mit anderen Prozessen nutzen → werden “ge-schedult“ (preemptiv oder kooperativ) Synchronisation • Regelt den Zugriff mehrerer Prozesse auf gemeinsam genutzte (kritische) Datenbereiche • In Java kann ein kritischer Code-Bereich geschützt werden, so dass nur ein Thread diesen betreten kann • Ist eine Methode synchronized attributiert, dann ♦ wird automatisch vor dem Methodenaufruf der Monitor für das zugehörige Objekt erworben bzw. belegt ♦ wird nach dem Methodenaufruf der Monitor wieder freigegeben ♦ werden andere Threads ggf. “schlafen geschickt” bis Monitor wieder frei ist ♦ können mittels wait() und notify()/notifyAll() Threads “schlafen geschickt“ und “geweckt” werden Programmieren in Java 02.Apr.98 93/120 Threads und Synchronisation 12.1 Erzeugen von Threads 1. Schritt: Programmieren des Threads • Schreiben einer Klasse mit einer Methode run() • Die Klasse muss entweder Unterklasse der Klasse Thread sein oder das Interface Runnable implementieren • wenn einem Objekt dieser Klasse die Nachricht start() geschickt wird, dann wird die Methode run() als separater Kontrollfluss ausgeführt 2. Schritt: Starten des Threads • erzeugen eines Objekts ♦ mit new <Unterklasse>, wenn die Klasse Unterklasse der Klasse Thread ist ♦ mit new Thread( … ), wenn die Klasse das Interface Runnable implementiert • diesem Objekt die Nachricht start() schicken Programmieren in Java 02.Apr.98 94/120 Threads und Synchronisation Beispiel class Foo extends Thread { public void run() { while (true) { System.out.println( getName() ); yield(); } } } class Application { public static void { Thread t1 = new Thread t2 = new Thread t3 = new main( String args[] ) Foo(); Foo(); Foo(); System.out.println( "Starting Threads ..." ); t1.start(); t2.start(); t3.start(); System.out.println( "... Threads started!" ); try { t3.join(5); } catch ( InterruptedException e ) { } System.out.println( "Stopping Threads ..." ); t1.stop(); t2.stop(); t3.stop(); System.out.println( "... Threads stopped!" ); } } Programmieren in Java 02.Apr.98 95/120 Threads und Synchronisation 12.2 Synchronisation • am Beispiel Reader/Writer-Problem • Zeigt u.a. recht gut: Threads und Synchronisationsprimitiven Reader/Writer-Problem • Ein gemeinsam genutzter Speicher • Zwei Arten von Threads (aber beliebig viele Threads) • Eine Art produziert Werte (Writer) die andere “konsumiert” diese (Reader) • Produzierte Werte werden im gemeinsam genutzten Speicher abgelegt • Zu konsumierende Werte werden beim gemeinsam genutzten Speicher abgeholt • Es kann nur konsumiert werden wenn zuvor produziert wurde und umgekehrt … → Zwang zur Synchronisation von Reader und Writer Programmieren in Java 02.Apr.98 96/120 Threads und Synchronisation 12.2.1 Beispiel Synchronisation class SharedStorage { private Object content; private boolean isEmpty() { return ( content == null ); } public synchronized Object content() { Object temp; while ( isEmpty() ) { try { wait(); } catch ( InterruptedException anException ) {} } temp = content; content = null; notifyAll(); return temp; } public synchronized void content(Object aContent) { while ( ! isEmpty() ) { try { wait(); } catch ( InterruptedException anException ) {} } content = aContent; notifyAll(); } } Programmieren in Java 02.Apr.98 97/120 Threads und Synchronisation class Reader extends Thread { private SharedStorage toReadFrom; public Reader( SharedStorage toReadFrom ) { this.toReadFrom = toReadFrom; } public void run() { Object anObject; for ( int i = 0; i < 8; i++ ) { anObject = toReadFrom.content(); Transcript.show( "Reader[" + getName() + "] got: " + anObject.toString()); yield(); } Transcript.show( "*** Reader[" + getName() + "] terminates" ); } } Programmieren in Java 02.Apr.98 98/120 Threads und Synchronisation import java.util.Random; class Writer extends Thread { private SharedStorage toWriteTo; private Random rndGen = new Random(); public Writer( SharedStorage toWriteTo ) { this.toWriteTo = toWriteTo; } public void run() { Integer anInteger; for ( int i = 0; i < 12; i++ ) { anInteger = new Integer( rndGend.nextInt() ); toWriteTo.content( anInteger ); Transcript.show( "Writer[" + getName() + "] put: " + anInteger.toString() ); yield(); } Transcript.show( "*** Writer[" + getName() + "] terminates" ); } } Programmieren in Java 02.Apr.98 99/120 Threads und Synchronisation public class ReaderWriter { public static void main( String args[] ) { SharedStorage shared = new SharedStorage(); Reader firstReader = new Reader( shared ); Reader secondReader = new Reader( shared ); Reader thirdReader = new Reader( shared ); Writer firstWriter = new Writer( shared ); Writer secondWriter = new Writer( shared ); firstReader.start(); secondReader.start(); thirdReader.start(); firstWriter.start(); secondWriter.start(); } } Programmieren in Java 02.Apr.98 100/120 Threads und Synchronisation 12.3 • Monitore Monitore in Java sind reentrant, d.h. der Versuch des “Besitzers“ den Monitor mehrfach zu erwerben bzw. zu belegen, führt nicht zu einer Verklemmung class Reentrant { public synchronized void a() { System.out.println("bin in a(), rufe b() auf"); b(); System.out.println("wieder in a()"); } public synchronized void b() { System.out.println("jetzt bin ich in b()"); } } class Application { public static void main( String[] { new Reentrant().a(); } } Programmieren in Java 02.Apr.98 args ) 101/120 Threads und Synchronisation 12.4 Übungen (68) Welche besondere Eigenschaft haben Monitore in Java? (69) Ändern Sie die main-Methode des Beispiels in Kapitel 12.1, so dass diese mit folgender Implementierung der Klasse Foo sinngemäss gleich arbeitet wie o.g. Beispiel. Hinweis: Es geht darum, einen Thread nicht als Unterklasse von Thread zu implementieren (wie im Beispiel 12.1), sondern mittels des Interfaces Runnable. class Foo implements Runnable { public void run() { while (true) { System.out.println( this ); Thread.yield(); } } } class Application { public static void main( String args[] ) { // Diese Methode ist zu implementieren } } (70) Wir wollen nun den Tieren im Zoo etwas Leben “einhauchen“ und sie dazu bringen sich selbständig zu bewegen. Bei der Erzeugung eines Animal-Objekts soll ein eigener Thread gestartet werden, welcher das Tier von Zeit zu Zeit auf eine zufällig ausgewählte Position bewegt. • Implementieren sie hierfür in der Klasse Animal das Interface Runnable. Gestalten sie die run()-Methode so, dass sich das Tier auf eine zufällig ausgewählte Position bewegt (moveRandom()) und anschliessend ca. eine Sekunde wartet (Thread.sleep(…)). Programmieren in Java 02.Apr.98 102/120 Threads und Synchronisation • Implementieren sie ferner für Animal einen Konstruktor, welcher bei der Objekterzeugung- bzw. Initialisierung den entsprechenden Thread startet (new Thread( this )).start()). • Damit der Zoo die Bewegung des Tieres auch mitbekommt und die Anzeige aktualisieren kann, muss er entsprechend benachrichtigt werden. Dies erfolgt dadurch, dass in der Setter-Methode location(…) der Klasse LifeForm folgende Codezeilen ans Ende der Methode location(…) eingefügt werden: void location( … ) { … setChanged(); notifyObservers(); } (71) Als vorletzte Übung soll nun noch ein spezielles Tier implementiert werden. Diesmal ein Chamäleon. Ein Chamäleon bewegt sich wie ein Tier nur seltener und es soll weiterhin die Besonderheit haben, dass es selbständig von Zeit zu Zeit seine Farbe wechselt. Implementieren sie die entsprechende Funktionalität. (72) Bauen sie ihren Zoo mit Tieren und Planzen so aus, wie es Ihnen beliebt. Programmieren in Java 02.Apr.98 103/120 Threads und Synchronisation Das wars … Programmieren in Java 02.Apr.98 104/120 Schlüsselworte 13 Schlüsselworte Die folgenden Worte sind reservierte Schlüsselworte in Java und können daher nicht als Bezeichner verwendet werden. abstract default goto null synchronized boolean do if package this break double implements private throw byte else import protected throws case extends instanceof public transient catch false int return true char final interface short try class finally long static void const float native super volatile continue for new switch while Hinweise • strenggenommen sind true und false, wie auch null keine reservierten Schlüsselworte sondern Literale • Obwohl unbenutzt in Java, sind die Schlüsselworte const und goto reserviert. Dies ermöglicht es dem Übersetzer, bessere Fehlermeldungen zu produzieren, wenn diese C++ Schlüsselworte irrtümlicherweise in Java-Programmen auftauche Programmieren in Java 02.Apr.98 105/120 Java 1.0.2 EBNF Grammatik 14 Java 1.0.2 EBNF Grammatik 14.1 Bezeichner Identifier = IdentifierChars . IdentifierChars = JavaLetter | IdentifierChars JavaLetterOrDigit . JavaLetter = beliebiger Unicode-Buchstabe JavaLetterOrDigit = beliebiger Unicode-Buchstabe oder beliebiges Unicode-Zeichen 14.2 Literale 14.2.1 Integer Literale Literal = IntegerLiteral | FloatingPointLiteral | BooleanLiteral | CharacterLiteral | StringLiteral | NullLiteral . IntegerLiteral = DecimalIntegerLiteral | HexIntegerLiteral | OctalIntegerLiteral . DecimalIntegerLiteral = DecimalNumeral [ IntegerTypeSuffix ] . HexIntegerLiteral = HexNumeral [ IntegerTypeSuffix ] . OctalIntegerLiteral = OctalNumeral [ IntegerTypeSuffix ] . Programmieren in Java 02.Apr.98 106/120 Java 1.0.2 EBNF Grammatik IntegerTypeSuffix = l|L. DecimalNumeral = 0 | NonZeroDigits [ Digits ] . Digits = Digit | Digits Digit . Digit = 0 | NonZeroDigit . NonZeroDigit = 1|2|3|4|5|6|7|8|9. HexNumeral = 0 x HexDigit | 0 X HexDigit | HexNumeral HexDigit . HexDigit = 0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f|A|B|C|D|E|F. OctalNumeral = 0 x OctalDigit | 0 X OctalDigit | OctalNumeral OctalDigit . OctalDigit = 0|1|2|3|4|5|6|7|8. 14.2.2 Boolean Literale BooleanLiteral = true | false . 14.2.3 FloatingPoint Literale FloatingPointLiteral = Digits . [ Digits ] [ ExponentPart ] [ FloatTypeSuffix ] | . Digits [ ExponentPart ] [ FloatTypeSuffix ] | Digits ExponentPart [ FloatTypeSuffix ] | Digits [ ExponentPart ] FloatTypeSuffix . ExponentPart = ExponentIndicator SignedInteger . Programmieren in Java 02.Apr.98 107/120 Java 1.0.2 EBNF Grammatik ExponentIndicator = e|E. SignedInteger = [ Sign ] Digits . Sign = +|-. FloatTypeSuffix = f|F|d|D. 14.2.4 Character und String Literale CharacterLiteral = ‘ SingleCharacter ‘ | ‘ EscapeSequence ‘ . SingleCharacter = InputCharacter aber nicht ‘ oder \ StringLiteral = “ [ StringCharacters ] “ . StringCharacters = StringCharacter | StringCharacters StringCharacter . StringCharacter = InputCharacter aber nicht “ oder \ | EscapeSequence . EscapeSequence = \b|\t|\n|\f|\r|\“|\‘|\\| OctalEscape . OctalEscape = \ OctalDigit | \ OctalDigit OctalDigit | \ ZeroToThree OctalDigit OctalDigit . ZeroToThree = 0|1|2|3. Programmieren in Java 02.Apr.98 108/120 Java 1.0.2 EBNF Grammatik 14.2.5 Null Literal NullLiteral = null . 14.2.6 Separators Separators = (|)|{|}|[|]|;|,|.. 14.3 Typen Type = PrimitiveType | ReferenceType . PrimitiveType = NumericType | boolean . NumericType = IntegralType | FloatingPointType . IntegralType = byte | short | int | long | char . FloatingPointType = float | double . ReferenceType = ClassOrInterfaceType | ArrayType . ClassOrInterfaceType = Name . ClassType = ClassOrInterfaceType . InterfaceType = ClassOrInterfaceType . ArrayType = PrimitiveType [] | Name [] | ArrayType [] . 14.4 Namen Name = SimpleName | QualifiedName . Programmieren in Java 02.Apr.98 109/120 Java 1.0.2 EBNF Grammatik SimpleName = Identifier . QualifiedName = Name . Identifier . 14.5 Pakete CompilationUnit = [ PackageDeclaration ] [ ImportDeclarations ] [ TypeDeclarations ] . ImportDeclarations = ImportDeclaration | ImportDeclarations ImportDeclaration . TypeDeclarations = TypeDeclaration | TypeDeclarations TypeDeclaration . PackageDeclaration = package Name ; . ImportDeclaration = SingleTypeImportDeclaration | TypeImportOnDemandDeclaration . SingleTypeImportDeclaration = import Name ; . TypeImportOnDemandDeclaration = import Name . * ; . TypeDeclaration = ClassDeclaration | InterfaceDeclaration ; . 14.6 Attributierungen Modifiers = Modifier | Modifiers Modifier . Programmieren in Java 02.Apr.98 110/120 Java 1.0.2 EBNF Grammatik Modifier = public | protected | private | static | abstract | final | native | synchronized | transient | volatile . 14.7 Klassen 14.7.1 Klassen Deklaration ClassDeclaration = [ Modifiers ] class Identifier [ Super ] [ Interfaces ] ClassBody. Super = extends ClassType . Interfaces = implements InterfaceTypeList . InterfaceTypeList = InterfaceType | InterfaceTypeList , InterfaceType . ClassBody = { ClassBodyDeclarations } . ClassBodyDeclarations = ClassBodyDeclaration | ClassBodyDeclarations ClassBodyDeclaration . ClassBodyDeclaration = ClassMemberDeclaration StaticInitializer ConstructorDeclaration . ClassMemberDeclaration = FieldDeclaration MethodDeclaration . 14.8 Variablen FieldDeclaration = [ Modifiers ] Type VariableDeclarators ; . Programmieren in Java 02.Apr.98 111/120 Java 1.0.2 EBNF Grammatik VariableDeclarators = VariableDeclarator | VariableDeclarators , VariableDeclarator . VariableDeclarator = VariableDeclaratorId | VariableDeclaratorId = VariableInitializer . VariableDeclaratorId = Identifier | VariableDeclaratorId [] . VariableInitializer = Expression | ArrayInitializer . 14.9 Methoden MethodDeclaration = MethodHeader MethodBody . MethodHeader = [ Modifiers ] Type MethodDeclarator [ Throws ] | [ Modifiers ] void MethodDeclarator [ Throws ] . MethodDeclarator = Identifier ( [ FormalParameterList ] ) | MethodDeclarator [] . FormalParameterList = FormalParameter | FormalParameterList , FormalParameter . FormalParameter = Type VariableDeclaratorId . Throws = throws ClassTypeList . ClassTypeList = ClassType | ClassTypeList , ClassType . MethodBody = Block | ;. Programmieren in Java 02.Apr.98 112/120 Java 1.0.2 EBNF Grammatik 14.10 Static Initializers StaticInitializer = static Block . 14.11 Konstruktoren ConstructorDeclaration = [ Modifiers ] ConstructorDeclarator [ Throws ] ConstructorBody . ConstructorDeclarator = SimpleName ( [ FormalParameterList ] ) . ConstructorBody = { [ ExplicitConstructorInvocation ] [ BlockStatements ] } . ExplicitConstructorInvocation = this ( ArgumentListopt ) ; | super ( ArgumentListopt ) ; . 14.12 Interfaces 14.12.1 Interface Deklaration InterfaceDeclaration = [ Modifiers ] interface Identifier [ ExtendsInterfaces ] InterfaceBody . ExtendsInterfaces = extends InterfaceType | ExtendsInterfaces , InterfaceType . InterfaceBody = { [ InterfaceMemberDeclarations ] } . InterfaceMemberDeclarations = InterfaceMemberDeclaration | InterfaceMemberDeclarations InterfaceMemberDeclaration . InterfaceMemberDeclaration = ConstantDeclaration | AbstractMethodDeclaration . ConstantDeclaration = FieldDeclaration . Programmieren in Java 02.Apr.98 113/120 Java 1.0.2 EBNF Grammatik AbstractMethodDeclaration = MethodHeader ; . 14.13 Arrays ArrayInitializer = { [ VariableInitializers ] [ , ] } . VariableInitializers = VariableInitializer | VariableInitializers , VariableInitializer . 14.14 Blöcke und Anweisungen Block = { [ BlockStatements ] } . BlockStatements = BlockStatement | BlockStatements BlockStatement . BlockStatement = LocalVariableDeclarationStatement | Statement . LocalVariableDeclarationStatement = LocalVariableDeclaration ; . LocalVariableDeclaration = Type VariableDeclarators . Statement = StatementWithoutTrailingSubstatement | LabeledStatement | IfThenStatement | IfThenElseStatement | WhileStatement | ForStatement . StatementNoShortIf = StatementWithoutTrailingSubstatement | LabeledStatementNoShortIf | IfThenElseStatementNoShortIf | WhileStatementNoShortIf | ForStatementNoShortIf . Programmieren in Java 02.Apr.98 114/120 Java 1.0.2 EBNF Grammatik StatementWithoutTrailingSubstatement = Block | EmptyStatement | ExpressionStatement | SwitchStatement | DoStatement | BreakStatement | ContinueStatement | ReturnStatement | SynchronizedStatement | ThrowStatement | TryStatement . EmptyStatement = ; LabeledStatement = Identifier : Statement . LabeledStatementNoShortIf = Identifier : StatementNoShortIf . ExpressionStatement = StatementExpression ; . StatementExpression = Assignment | PreIncrementExpression | PreDecrementExpression | PostIncrementExpression | PostDecrementExpression | MethodInvocation | ClassInstanceCreationExpression . IfThenStatement = if ( Expression ) Statement . IfThenElseStatement = if ( Expression ) StatementNoShortIf else Statement . IfThenElseStatementNoShortIf = if ( Expression ) StatementNoShortIf else StatementNoShortIf . SwitchStatement = switch ( Expression ) SwitchBlock . Programmieren in Java 02.Apr.98 115/120 Java 1.0.2 EBNF Grammatik SwitchBlock = { [ SwitchBlockStatementGroups ] [ SwitchLabels ] } . SwitchBlockStatementGroups = SwitchBlockStatementGroup | SwitchBlockStatementGroups SwitchBlockStatementGroup . SwitchBlockStatementGroup = SwitchLabels BlockStatements . SwitchLabels = SwitchLabel | SwitchLabels SwitchLabel . SwitchLabel = case ConstantExpression : | default : . WhileStatement = while ( Expression ) Statement . WhileStatementNoShortIf = while ( Expression ) StatementNoShortIf . DoStatement = do Statement while ( Expression ) ; . ForStatement = for ( [ ForInit ] ; [ Expression ] ; [ ForUpdate ] ) Statement . ForStatementNoShortIf = for ( [ ForInit ] ; [ Expression ] ; [ ForUpdate ] ) StatementNoShortIf . ForInit = StatementExpressionList | LocalVariableDeclaration . ForUpdate = StatementExpressionList . StatementExpressionList = StatementExpression | StatementExpressionList , StatementExpression . Programmieren in Java 02.Apr.98 116/120 Java 1.0.2 EBNF Grammatik BreakStatement = break [ Identifier ] ; . ContinueStatement = continue [ Identifier ] ; . ReturnStatement = return [ Expression ] ; . ThrowStatement = throw Expression ; . SynchronizedStatement = synchronized ( Expression ) Block . TryStatement = try Block Catches | try [ Block Catches ] Finally . Catches = CatchClause | Catches CatchClause . CatchClause = catch ( FormalParameter ) Block . Finally = finally Block . 14.15 Ausdrücke Primary = PrimaryNoNewArray | ArrayCreationExpression . PrimaryNoNewArray = Literal | this | ( Expression ) | ClassInstanceCreationExpression | FieldAccess | MethodInvocation | ArrayAccess . ClassInstanceCreationExpression = new ClassType ( [ ArgumentList ] ) . Programmieren in Java 02.Apr.98 117/120 Java 1.0.2 EBNF Grammatik ArgumentList = Expression | ArgumentList , Expression . ArrayCreationExpression = new PrimitiveType DimExprs [ Dims ] | new ClassOrInterfaceType DimExprs [ Dims ] . DimExprs = DimExpr | DimExprs DimExpr . DimExpr = [ Expression ] . Dims = [] | Dims [] . FieldAccess = Primary . Identifier | super . Identifier . MethodInvocation = Name ( [ ArgumentList ] ) | Primary . Identifier ( [ ArgumentList ] ) | super . Identifier ( [ ArgumentList ] ) . ArrayAccess = Name [ Expression ] | PrimaryNoNewArray [ Expression ] . PostfixExpression = Primary | Name | PostIncrementExpression | PostDecrementExpression . PostIncrementExpression = PostfixExpression ++ . PostDecrementExpression = PostfixExpression -- . UnaryExpression = PreIncrementExpression | PreDecrementExpression | Programmieren in Java 02.Apr.98 118/120 Java 1.0.2 EBNF Grammatik + UnaryExpression | - UnaryExpression | UnaryExpressionNotPlusMinus . PreIncrementExpression = ++ UnaryExpression . PreDecrementExpression = -- UnaryExpression . UnaryExpressionNotPlusMinus = PostfixExpression | ~ UnaryExpression | ! UnaryExpression | CastExpression . CastExpression = ( PrimitiveType [ Dims ] ) UnaryExpression | ( Expression ) UnaryExpressionNotPlusMinus | ( Name Dims ) UnaryExpressionNotPlusMinus . MultiplicativeExpression = UnaryExpression | MultiplicativeExpression * UnaryExpression | MultiplicativeExpression / UnaryExpression | MultiplicativeExpression % UnaryExpression . AdditiveExpression = MultiplicativeExpression | AdditiveExpression + MultiplicativeExpression | AdditiveExpression - MultiplicativeExpression . ShiftExpression = AdditiveExpression | ShiftExpression << AdditiveExpression | ShiftExpression >> AdditiveExpression | ShiftExpression >>> AdditiveExpression . RelationalExpression = ShiftExpression | RelationalExpression < ShiftExpression | RelationalExpression > ShiftExpression | RelationalExpression <= ShiftExpression | RelationalExpression >= ShiftExpression | RelationalExpression instanceof ReferenceType . Programmieren in Java 02.Apr.98 119/120 Java 1.0.2 EBNF Grammatik EqualityExpression = RelationalExpression | EqualityExpression == RelationalExpression | EqualityExpression != RelationalExpression . AndExpression = EqualityExpression | AndExpression & EqualityExpression . ExclusiveOrExpression = AndExpression | ExclusiveOrExpression ^ AndExpression . InclusiveOrExpression = ExclusiveOrExpression | InclusiveOrExpression | ExclusiveOrExpression . ConditionalAndExpression = InclusiveOrExpression | ConditionalAndExpression && InclusiveOrExpression . ConditionalOrExpression = ConditionalAndExpression | ConditionalOrExpression || ConditionalAndExpression . ConditionalExpression = ConditionalOrExpression | ConditionalOrExpression ? Expression : ConditionalExpression . AssignmentExpression = ConditionalExpression | Assignment . Assignment = LeftHandSide AssignmentOperator AssignmentExpression . LeftHandSide = Name | FieldAccess | ArrayAccess . AssignmentOperator = = | *= | /= | %= | += | -= | <<= | >>= | >>>= | &= | ^= | |= . Expression = AssignmentExpression . ConstantExpression = Expression . Programmieren in Java 02.Apr.98 120/120