HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – Inhalt – TH/KR – 01 ------------------------------------------------------------------------------------------------------------------------------------------------------- Programmieren Grundlagen (4. Sem.) Teil 2: Einführung in die OOP mit Java Unterlagen erstellt durch Prof. Dr. Rainer Thomas Vorlesung Prof. Dr. Heribert Kristl 10. Grundlegende Eigenschaften von Java 10.1 10.2. 10.3. 10.4. 10.5. 10.6. Java und C Klassen und Kapselung Programmstruktur und Startklasse Erzeugung und Start von Java-Programmen Packages in Java Java-Standardbibliothek (Java API) 11. Datentypen in Java 11.1. 11.2. 11.3. 11.4. 11.5. 11.6. Einfache Datentypen Referenztypen und Objekterzeugung Wrapper-Klassen für die einfachen Datentypen Strings Arrays Die Klasse Object 12. Elementare Programmfunktionalitäten 12.1. 12.1. 12.3. 12.4. Zugriff zu Programmparametern Standard-Ein- und Ausgabe Exceptions Dateizugriff 13. Ergänzungen zu Klassen – Vererbung 13.1. 13.2. 13.3. 13.4. Umfassende Klassendefinition Ableitung und Polymorphie Interfaces und ihre Implementierung Programmbeispiel : Simpson-Integration 14. Grundkonzepte der Objektorientierten Programmierung 14.0. 14.1. 14.2. 14.3. 14.4. 14.5 Grundgedanke der OOP Objekte und Klassen Kapselung Vererbung Polymorphie Klassenbeziehungen HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 11/00/0 – TH – 01 ----------------------------------------------------------------------------------------------------------------------------------------------------------- Programmieren (4. Sem.) Kapitelüberblick 10. Grundlegende Eigenschaften von Java 10.1. Java und C 10.2. Klassen und Kapselung 10.3. Programmstruktur und Startklasse 10.4. Erzeugung und Start von Java-Programmen 10.5. Packages in Java 10.6. Java-Standardbibliothek (Java API) HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 11/01/1 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Java und C (1) • Java-Entwicklung ◇ Java wurde von der Fa. Sun entwickelt. Die Entwicklung begann im Jahr 1990. 1995 wurde Java erstmals der Öffentllichkeit vorgestellt (Sun World 95). Seit 1996 stellt die Fa. Sun (jetzt Oracle) "offizielle Java-Versionen" in Form von Java Development Kits kostenlos zum Download bereit. Die Java Development Kits enthalten alle für die Entwicklung von Java-Programmen benötigten Tools (Compiler, Interpreter (Java Virtual Machine, JVM), weitere Dienstprogramme und die Standardbibliothek) ▻ 1996 (Jan.) Freigabe des Java Development Kits 1.0 (JDK 1.0) ▻ 1997 JDK 1.1 ▻ 1998 JDK 1.2, kurze Zeit später umbenannt in Java 2 Platform ▻ 2000 JDK 1.3 (Offizieller Name : Java2 System Development Kit 1.3, J2SDK 1.3) ▻ 2002 JDK 1.4 (J2SDK 1.4) ▻ 2004 JDK 1.5 (JDK 5.0) ▻ 2006 JDK 1.6 (JDK 6.0) ▻ 2011 (Mitte) geplant : JDK 7.0 • Java2-Plattformen ► Java2 Standard Edition (J2SE) ► Java2 MicroEdition (J2ME), eingeschränkter Sprachstandard für den Einsatz in Geräten wie Mobiltelefonen, PDAs usw ► Java2 Enterprise Edition (J2EE), zusätzliche Komponenten zur Entwicklung unternehmensweiter verteilter Anwendungen (u.a. Enterprise Java Beans, EJB) • Java – ein C-Derivat ◇ Java kann als Derivat der Sprache C betrachtet werden. Die Syntax und großenteils die Semantik von Java entsprechen weitgehend der Syntax und Semantik von C. Anweisungen und Variablenvereinbarungen werdem im wesentlichen wie in C formuliert. ◇ Im Unterschied zu C++, einer ebenfalls von C abgeleiteten hybriden (prozedural und objektorientiert verwendbaren) Programmiersprache, ist Java eine rein objektorientierte Sprache. ◇ Außer von C ist Java auch stark von C++ und weiteren objektorientierten Programmiersprachen wie Oberon, Ada, Eiffel, Objective-C beeinflusst worden. ◇ Zahlreiche fehleranfällige oder redundante Bestandteile /Eigenschaften von C/C++ wurden nicht implementiert. Wo sinnvoll und erforderlich sind diese durch "bessere" Konzepte ersetzt. ◇ Ein weiterer elementarer Unterschied zwischen C und Java liegt in der Anwendbarkeit der erzeugten Programme : - Ein C-Compiler erzeugt Maschinen-Code, C-Programme laufen direkt, aber nur auf der jeweiligen Hardware, für die sie erzeugt wurden. - Ein Java-Compiler erzeugt einen maschinenunabhängigen Byte-Code. Dieser kann als Maschinencode einer virtuellen Maschine aufgefasst werden. Æ Java-Programme müssen mittels eines Interpreters (Java Virtual Machine, JVM) ausgeführt werden. Ein einmal erzeugtes Java-Programm läuft damit aber auf jeder Hardware, für die eine JVM existiert. Æ Motto von Sun : Write Once , Run Anywhere HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 11/01/2 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Java und C (2) • In Java nicht implementierte C-Eigenschaften Im wesentlichen fehlen in Java die folgenden C- Eigenschaften bzw Sprachelemente : ▻ Pointer und Pointerarithmetik ▻ Funktionspointer ▻ Speicherklassen (auto, register, extern) ▻ globale Variable ▻ freie Funktionen ▻ inline-Funktionen ▻ die strukturierten Typen struct und union ▻ Bitfelder ▻ Datentyp long double ▻ vorzeichenlose Datentypen (kein unsigned und damit auch kein signed) ▻ Äquivalenz zwischen logischen (bool) und ganzzahligen Werten (keine Konvertierung zwischen bool und int) ▻ Einführung neuer Typnamen mittels typedef ▻ Sprunganweisung goto ▻ sizeof-Operator ▻ Komma-Operator ▻ ->-Operator ▻ Preprozessoranweisungen (Textersatz (Makros), bedingte Kompilierung) ▻ Header-Dateien ▻ Extern- und Vorwärtsreferenzen ▻ Dynamische Allokation von Variablen einfacher Datentypen ▻ Datentypen long long, _Bool, sowie komplexe und imaginäre Datentypen (C99) ▻ Datentyp wchar_t (ein in C in Headerdateien mittels typedef definierter Typ) • In Java geänderte Eigenschaften von C (C90) ▻ Die Trennung eines Blockes in Vereinbarungsteil (am Anfang) und Anweisungsteil existiert in Java nicht Æ Vereinbarungen und Anweisungen können beliebig gemischt werden. ▻ Der Anfangsausdruck im Kopf einer for-Anweisung kann durch eine initialisierte Variablendefinition ersetzt werden Æ keine vorherige Definition einer "Laufvariablen" notwendig Beispiel : for (int i=1; i<=end; i++) // ... ▻ In der Parameterliste einer parameterlosen Funktion darf das Schlüsselwort void nicht angegeben werden. Die Parameterliste muß leer bleiben. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 11/01/3 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Vergleich der Schlüsselworte und Identifier von Java und C • Schlüselworte (key words) reine CSchlüsselworte ++) C-99 Schlüsselworte, die in C und Java enthalten sind *) in Java reserviert, aber nicht verwendet neue Schlüsselworte von Java auto extern inline register restrict signed sizeof struct typedef union unsigned _Bool _Complex _Imaginary break case char const *) continue default do double else enum float for goto if int long return short static switch void volatile while abstract assert boolean byte catch class extends false **) final finally implements import instanceof interface native new null **) package private protected ++) *) ++) ++) ++) public strictfp super synchronized this throw throws transient true **) try **) genau genommen handelt es sich hierbei in Java nicht um ein Schlüsselwort, sondern um eine Konstante (literal) • Identifier ◇ Identifier (Namen) in Java werden nach den gleichen Regeln wie in C gebildet : ▻ Sie bestehen aus Buchstaben, dem Underscore ('_') und Ziffern ▻ Sie müssen mit einem Buchstaben oder dem Underscore ('_') beginnen ▻ Groß- und Klein-Buchstaben sind unterschiedlich ▻ Sie dürfen nicht wie ein reserviertes Wort (einschliesslich false, true und null) lauten ◇ Zusätzlich dürfen Java-Identifier das Dollarzeichen ('$') enthalten. Dieses wird wie ein Buchstabe behandelt (Beginn mit '$' ist zulässig) ◇ Als Buchstaben sind alle Unicode-Buchstaben (also auch andere als im lateinischen Alphabet) zulässig ◇ Java-Identifier dürfen aus beliebig vielen Zeichen bestehen. ◇ Konventionen (nicht verpflichtend) bezüglich der Schreibweise : ▻ Variable : nur Kleinbuchstaben, z. Tl. aber auch Unterteilung in Folge-Abschnitte (Beginn mit Groß-Bu) ▻ Konstante : nur Großbuchstaben ▻ Funktionen : Beginn mit Klein-Bu, häufig Unterteilung in Abschnitte, die jeweils mit Groß-Bu beginnen ▻ Typen (Klassen) : Beginn mit Groß-Bu, Unterteilung in Abschnitte, die jeweils mit Groß-Bu beginnen HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 11/01/4 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Operatoren in Java • Operator-Hierarchie von Java Priorität Operator Operation 1 [] () . ++ (Postfix) -- (Postfix) Indizierung Funktionsaufruf Komponentenzugriff Increment Decrement lÆr 2 ++ (Prefix) -- (Prefix) + ! ~ Increment Decrement Identität Negation (arithmetisch) Negation (logisch) bitweises Komplement lÅr 3 new (type) Objekterzeugung Typkonvertierung (Cast) lÅr 4 * / % Multiplikation / Division / Modulus lÆr 5 + - Addition bzw Konkatenation (Strings) Subtraktion lÆr 6 << >> >>> Linkssschieben Rechtsschieben, Nachschieben des Vorzeichens Rechtsschieben, Nachschieben von 0 lÆr *) *) Assoziativität 7 < <= > >= instanceof *) Vergleich (kleiner / kleiner gleich) Vergleich (größer / größer gleich) Typprüfung lÆr 8 == != Vergleich (gleich / ungleich) lÆr 9 & bitweises UND lÆr 10 ^ bitweises EXOR lÆr 11 | bitweises ODER lÆr 12 && logisches UND lÆr 13 || logisches ODER lÆr 14 ?: bedingte Auswertung lÅr 15 = *= /= %= += -= <<= >>= >>>= *) &= ^= |= Zuweisung Zuweisung mit Verknüpfungsoperation lÅr *) neue Operatoren in Java HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 11/02/1 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Klassen und Kapselung (1) • Klassen – Grundbausteine eines Java-Programms ◇ Im vorigen Kapitel wurde ein OOP-Programm im wesentlichen als eine Ansammlung miteinander kommunizierender Objekte definiert. Objekte werden durch Klassen beschrieben. Eine Klasse definiert die jeweiligen Eigenschaften (den Aufbau) und das Verhalten (die Funktionalität) der von ihr instanzierbaren Objekte. ◇ Klassen sind die elementaren Bestandteile jedes Java-Programms. Sämtlicher Code eines Java-Programms befindet sich innerhalb von Klassen. Ein Programm kann aus beliebig vielen Klassen bestehen. • Aufbau von Klassen ◇ Eine Klasse ist aus Klassenkomponenten aufgebaut. Diese werden in einer Klassendefinition festgelegt. Klassenkomponenten können sein : - Datenkomponenten (Membervariable) Æ Variablendefinitionen Die Gesamtheit der Datenkomponenten beschreibt den Aufbau und – mit ihren jeweiligen konkreten Werten in einem Objekt – den Zustand der Objekte (Æ Objektvariable). - Funktionskomponenten (Memberfunktionen) Æ Funktionsdefinitionen Diese legen das Verhalten, die Fähigkeiten der Objekte fest (Æ Objektfunktionen). ◇ Eine Klasse kann auch Komponenten (sowohl Daten- als auch Funktionskomponenten) enthalten, die nicht objekt-spezifisch sind, sondern den Zustand und das Verhalten der Klasse selbst beschreiben. Sie sind durch den Modifizierer static gekennzeichnet. Æ statische Datenkomponenten (Klassenvariable) bzw statische Funktionskomponenten (Klassenfunktionen) ◇ Zusätzlich kann eine Klassendefinition - Konstruktoren enthalten Konstruktoren sind spezielle klassenspezifische Funktionen, die bei der Objekterzeugung aufgerufen werden. Sie tragen immer den Klassennamen und besitzen keinen Rückgabetyp (auch nicht void). Sie werden in Java nicht zu den Memberfunktionen und nicht zu den Klassenkomponenten gerechnet. Ihre primäre Aufgabe besteht in der Initialisierung der Datenkomponenten des erzeugten Objekts. Konstruktoren können Parameter besitzen (Æ Initialisierungswerte). Ein parameterloser Konstruktor wird auch als no-arg-Konstruktor bezeichnet.In einer Klasse können mehrere Konstruktoren – mit jeweils unterschiedlicher Parameterliste – definiert sein. Falls eine Klassendefinition keinen Konstruktor enthält, wird vom Compiler implizit ein Default-Konstruktor (parameterlos) bereitgestellt. ◇ Eine Klassendefinition kann noch weitere Bestandteile enthalten (s. später) • Kapselung ◇ Das Konzept der Kapselung wird in Java durch die Festlegung von Zugriffsberechtigungen zur Klasse selbst und zu den Datenkomponenten realisiert. ◇ Dies erfolgt durch die Angabe von Zugriffs-Modifizierern. Hierfür existieren folgende Möglichkeiten : - kein Zugriffs-Modifizierer : Zugriffsberechtigung "package". Ein Zugriff besteht nur von innerhalb des Packages (eine Art Namensraum), in dem die Klasse definiert ist. - Modifizierer public : Ein Zugriff ist von überall her möglich (öffentlicher Zugriff). - Modifizierer private (nur für Klassenkomponenten) : Zugriff nur von innerhalb der Klasse, in dem die jeweilige Komponente definiert ist. - Modifizierer protected (nur für Klassenkomponenten) : Zugriff beschränkt auf die Klasse selbst, auf abgeleitete Klassen und auf das Package HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 11/02/2 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Klassen und Kapselung (2) • Vereinfachte Syntax der Klassendefinition (vollständiges Syntax-Diagramm s. später) class Klassen-Name { Komponentendefinition public } Konstruktordefinition ; ◇ Komponentendefinition : Variablen- oder Funktionsdefinition Beide können beliebig gemischt werden ▻ Variablendefinition (Definition von Datenkomponenten) Typangabe Variablenname Feld-Modifizierer Initialisierer = ; , Datenkomponenten können bei der Definition initialisiert werden (mit einem typkonformen Ausdruck) Æ Feld-Initialisierer ▻ Funktionsdefinition (Definition von Memberfunktionen), vereinfacht Rückgabetyp MethodenModifizierer Methoden-Name ( Methoden-Kopf { Formal-Parameterliste ) throws-Klausel Anweisung } MethodenRumpf Die wichtigsten Feld- bzw Methoden-Modifizierer sind (es gibt noch weitere) : - die Zugriffs-Modifizierer public, private und protected - static : Die Komponente ist eine Klassenkomponente - final : Bei Datenkomponenten : Komponente ist eine Konstante Bei Memberfunktionen : Methode kann in abgeleiteten Klassen nicht überschrieben werden Wenn eine Methode keine Parameter besitzt, ist die Formal-Parameterliste leer (kein void !!!) Die throws-Klausel deklariert Exceptions, die von der Funktion geworfen werden (s. später) ◇ Konstruktordefinition : analog zur Funktionsdefinition, mit folgenden Unterschieden : ▻ als (Konstruktor-) Modifizierer sind nur die Zugriffs-Modifizierer zulässig ▻ kein Rückgabetyp ▻ Methodenname == Klassenname HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 11/02/3 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Klassen und Kapselung (3) • Beispiel einer einfachen Klassendefinition : class Uhr { private long acttime; public Uhr() { acttime = 0; } // Datenkomponente // Konstruktor void setTime(long time) { acttime = time; } long tick() { ++actTime; return acttime; } // Funktionskomponenten void displayClock() { /* ... */ }; } Anmerkung : Der Klassenname allein dient als Typ-Bezeichnung für Variablendefinitionen • Zugriff zu Klassenkomponenten ◇ Ein Zugriff von ausserhalb der Klasse ist nur entsprechend der durch den jeweiligen Zugriffs-Modifizierer festgelegten Berechtigung möglich. Zu private-Komponenten ist ein Zugriff von außen grundsätzlich nicht zulässig ◇ Klassenkomponenten (sowohl Daten- als auch Funktionskomponenten) werden mittels des – aus C für den Struktur-Komponenten-Zugriff bekannten – Komponentenzugriff-Operators . ausgewählt : ▻ objektspezifische Komponenten über eine Objekt-Referenz Beispiel : myClck sei eine Referenz auf ein Uhr-Objekt Æ myClck.displayClock(); // Methodenaufruf Interpretation : An das durch myClock referierte Objekt wird die Botschaft displayClock geschickt. Das Objekt reagiert daraufhin mit der Ausführung der zugeordneten Methode. ▻ klassenspezifische Komponenten (static !!!) über den Klassennamen. Beispiel : System ist eine Bibliotheksklasse, die u.a. die statische Methode getProperties() besitzt. Æ System.getProperties() // Methodenaufruf HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 11/03/1 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Struktur von Java-Programmen (1) • Modulkonzept ◇ Ein Java-Programm besteht nur aus Klassen. ◇ Jede Klasse muß in einer eigenen Quelldatei vollständig definiert und implementiert werden. Ausnahme von der eigenen Quelldatei : eingebettete Klassen (nested classes).(hier nicht betrachtet) Der Quelldatei-Hauptname muß wie der (Haupt-)Klassenname lauten. Die Quelldatei-Extension ist .java. Beispiel : Klasse Welcome Æ Quelldateiname : Welcome.java ◇ Eine Quelldatei ist die Übersetzungseinheit (Übersetzungs-Modul). Für jede enthaltene Klasse wird vom Compiler eine eigene Byte-Code-Datei erzeugt. Die Byte-Code-Datei für die (Haupt-)Klasse bekommt den Hauptnamen der Quellcode-Datei und die Extension .class Beispiel : Aus Welcome.java erzeugt der Compiler Welcome.class Nur bei eingebetteten Klassen : Die Hauptnamen der übrigen Byte-Code-Dateien sind aus den Namen der Haupt-Klasse und der eingebetteten Klassen zusammengesetzt. • Programmstruktur ◇ Ein Linken der getrennt übersetzten Module (Klassen-Dateien) zu einer – ausführbaren – Programm-Datei findet in Java nicht statt. ◇ Ein Java-Programm ist damit konzeptionell nicht in einer einzigen Datei zusammengefasst. Vielmehr besteht es – entsprechend den im Programm eingesetzten Klassen – aus einer oder mehreren Dateien. Anmerkung : Es gibt Möglichkeiten der Zusammenfassung zu einer Archiv-Datei (hier nicht betrachtet) ◇ Eine dieser Dateien muß die Start-Klasse des Programms enthalten. ◇ Ausgehend von der Start-Klasse werden die übrigen Klassen des Programms referiert. • Programmarten ◇ Es werden zwei Java-Programmarten unterschieden : ▻ Applikations-Programme ▻ Applets ◇ Applikationsprogramme "Normale" Java-Programme. Sie werden durch direkten Start der Virtuellen Maschine (JVM) ausgeführt. Hierfür ist dieser beim Start die Start-Klasse des Programms als Parameter zu übergeben ◇ Applets Diese Programme werden innerhalb eines Java-fähigen Web-Browsers (oder im Kontext eines anderen "applet viewers") ausgeführt. Ein derartiger Browser (bzw "applet viewer") enthält eine – gegebenenfalls über ein Plug-In eingebundene – JVM. Der Applet-Code befindet sich üblicherweise auf einem Server und wird durch ein <Applet>-Tag im HTML-Code einer Web-Seite referiert. Stößt der Browser beim Interpretieren des HTML-Codes auf ein derartiges <Applet>-Tag, startet er seine JVM. Diese lädt von der angegebenen URL den Byte-Code der Start-Klasse des Applets und führt ihn aus. Applets sind damit – i.a. kleine – Programme, die in einfacher Art und Weise über das Internet verteilt werden können. Da Applets prinzipiell unzuverlässigen Code enthalten können, unterliegt ihre Ausführung strengen Sicherheitsrestriktionen. Der Start und die Ausführung eines Applets unterscheidet sich aber erheblich von Start und Ausführung eines Applikations-Programms. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 11/03/2 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Struktur von Java-Programmen (2) • Start-Klasse eines Java-Applikations-Programms ◇ Die der JVM beim Start zu übergebene Klasse muß – neben möglicherweise beliebig vielen anderen Methoden – eine statische main()-Methode besitzen. Diese main()-Methode muß öffentlich zugänglich (public) und vom Typ void sein. Als formalen Parameter muß sie ein String-Array besitzen. Sie stellt den Startpunkt der Programmausführung dar. ◇ Beispiel für ein minimales Java-Programm : // Welcome.java public class Welcome { public static void main(String[] args) { System.out.println("Welcome to the world of Java"); } } ◇ I.a. ist ein Java-Programm nicht klassenorientiert sondern objektorientiert. D.h. im Programm werden Objekte erzeugt, die miteinander kommunizieren. Die Erzeugung des "ersten" Objekts erfolgt dann in der main()-Methode der Start-Klasse : ▻ Entweder durch Instantiierung einer anderen Klasse (Start-Klasse dient nur zum Programmstart) ▻ oder durch Instantiierung der eigenen Klasse (Start-Klasse enthält auch spezifische Programmfunktionalitäten) ◇ Ausgehend vom ersten Objekt werden dann die weiteren Objekte erzeugt. ◇ Beispiel für ein minimales Java-Programm mit Objekterzeugung : // Willkommen.java public class Willkommen { Willkommen() { // nur zur Demonstration } // Konstruktor void begruessung() // Memberfunktion { System.out.println("Willkommen bei Java"); } public static void main(String[] args) { Willkommen gruss = new Willkommen(); gruss.begruessung(); } // Erzeugung eines Objekts } ◇ Anmerkung : Häufig wird auch bei Klassen, die in der späteren Verwendung nicht als Start-Klassen dienen sollen, eine statische main()-Methode vorgesehen. Diese Methode enthält dann i.a. Code, der das – weitgehend isolierte – Testen der Klasse ermöglicht. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 11/04/1 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Erzeugung und Start von Java-Programmen • Vom Quellprogramm zur Programmausführung Quell-Datei (z.B.Welcome.java) Java-Compiler weitere Byte-Code-Dateien javac Byte-Code-Datei (z.B.Welcome.class) Java-Interpreter (Java Virtual Machine) java • Aufruf des Java-Compilers (Kommandozeilen-Tool javac des JDK) ◇ Der Compiler sucht die zu übersetzende Quelldatei ausgehend vom aktuellen Arbeitsdirectory. Befindet sich die Datei in einem anderen Directory, ist ein entsprechender Zugriffspfad anzugeben. ◇ Der Name der zu übersetzenden Quelldatei ist einschließlich der Extension .java anzugeben. ◇ Beispiel :Quelldatei Welcome.java im Directory E:\Java\Vorl E:\Java\Vorl>javac Welcome.java ◇ Der Compiler erzeugt – bei Fehlerfreiheit – eine Byte-Code-Datei (hier : Welcome.class) im Verzeichnis der Quelldatei. ◇ Fehler werden zusammen mit der Zeilennummer und dem Quelldateinamen gemeldet. ◇ Werden in einer Quelldatei weitere Klassen referiert und findet der Compiler keine entsprechenden .class-Dateien, versucht er diese durch zusätzliches Übersetzen der entsprechenden .java-Dateien zu erzeugen. (Ausnahme : Klassen der Standardbibliothek) • Aufruf des Java-Interpreters (Kommandozeilen-Tool java des JDK) ◇ Dem Java-Interpreter ist der Klassenname (nicht der Dateiname, d.h. keine Extension !) der Start-Klasse als Programm-(Kommandozeilen-)Parameter zu übergeben. ◇ Der Java-Interpreter sucht nach der Byte-Code-Datei der Start-Klasse im aktuellen Directory und – falls dort nicht vorhanden – in den Directories, die in der Environment-Variablen CLASSPATH enthalten sind. ◇ Beispiel : Byte-Code-Datei Welcome.class (mit Klasse Welcome) im Verzeichnis E:\Java\Vorl CLASSPATH-Variable ist nicht gesetzt. E:\Java\Vorl>java Welcome ◇ Werden von der Start-Klasse weitere Klassen benötigt, sucht der Interpreter die entsprechenden Byte-CodeDateien im aktuellen Directory bzw in den Directories der CLASSPATH-Variablen (Ausnahme : Klassen der Standardbibliothek) ◇ Wird eine benötigte Byte-Code-Datei nicht gefunden, wird die Exception NoClassDefFoundError erzeugt. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 11/05/1 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Packages in Java (1) • Packages als Namensräume ◇ Klassen lassen sich in Java in Pakete (Packages) gruppieren. Packages können als Namensräume (name spaces) aufgefasst werden, in denen irgendwie zusammengehörige Klassen zusammengefasst sind (z.B. zu einem bestimmten Projekt gehörend, von einem bestimmten Entwickler erzeugt, zu einer bestimmten Bibliothek gehörend usw). ◇ Packages haben einen Namen und können hierarchisch strukturiert (Package Æ Unter-Package) sein. Æ Package-Namen können aus mehreren Bestandteilen bestehen, die durch einen Punkt ('.') vonein ander getrennt sind. ◇ Damit lässt sich eine Klasse über einen vollständigen Klassennamen ansprechen. Dieser besteht aus dem eigentlichen Klassennamen, dem der durch einen Punkt (.) abgetrennte Package-Name vorangestellt ist. Æ voll-qualifizierter Klassenname Package-Name Beispiel : . Klassenname Klasse Date im Package java.util voll-qualifizierter Klassenname : java.util.Date ◇ Mittels Packages lassen sich Namenskonflikte, insbesondere zwischen Klassen unterschiedlicher Hersteller (Bibliotheken), weitgehend vermeiden. ◇ Empfohlene Konvention zur Vergabe von Package-Namen : Beginn des Package-Namens mit dem invertierten Internet-Domain-Namen des Herstellers. Beispiel : Fa. SESA, Internet-Domain-Name : sesa.com Package-Name : com.sesa.wmf Es ist auch üblich, aus Gründen der Übersichtlichkeit den Top-Level-Domain-Namen wegzulassen. ◇ Klassen, die explizit keinem Package zugeordnet sind, befinden sich im namenlosen Package (unnamed package). Für die Verwendung in Bibliotheken sind derartige Klassen nicht geeignet. • Packages als Directory-Struktur ◇ Ein strukturierter Package-Name wird in eine entsprechende Directory-Struktur abgebildet. ◇ Beispiel : Klassen-Dateien des Packages com.sesa.wmf sind im Verzeichnis com\sesa\wmf ◇ Das Start-Verzeichnis einer derartigen Directory-Struktur muss sich im aktuellen Verzeichnis oder in einem über die CLASSPATH-Variable festgelegten Verzeichnis befinden. ◇ Package-Namen ermöglichen damit eine strukturierte und dadurch übersichtliche Ablage von KlassenDateien. ◇ Klassen-Dateien des namenlosen Package müssen direkt im aktuellen Directory bzw in einem durch die CLASSPATH-Variable festgelegten Verzeichnis liegen. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 11/05/2 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Packages in Java (2) • Package-Zuordnung einer Klasse ◇ Hierzu dient die package-Vereinbarung package Beispiel : Package-Name ; package hm.ee.vorl; ◇ Die package-Vereinbarung muss am Beginn einer Quelldatei (erste Vereinbarung) stehen. Sie legt fest, dass die nachfolgend definierte Klasse zu dem angegebenen Package gehört. • Verwendung von Klassen des eigenen Packages ◇ Eine Klasse kann eine andere Klasse ihres eigenen Packages allein mit dem einfachen Klassennamen verwenden. ◇ Jede Klasse eines Packages hat Zugriff zu den anderen Klassen desselben Packages, die nicht private sind, auch wenn sie nicht explizit public deklariert sind. (Zugriffsberechtigung "package") • Verwendung von Klassen aus anderen Packages ◇ Eine Klasse kann zu Klassen aus einem anderen Package nur zugreifen, wenn diese explizit public deklariert sind. ◇ Für die Verwendung bestehen prinzipiell zwei unterschiedliche Möglichkeiten : ▻ Verwendung des voll-qualifizierten Namens. ▻ Importieren der Klassen und Verwendung der einfachen Klassennamen ◇ Verwendung des voll-qualifizierten Namens : Beispiel : Verwendung der Klasse Date aus dem Package java.util // Datum1.java public class Datum1 { public static void main(String[] args) { java.util.Date now = new java.util.Date(); System.out.println(now); } } Nachteil : Klassennamen können sehr lang und damit unhandlich werden. ◇ Importieren von Klassen aus anderen Packages : mittels der import-Deklaration Diese existiert in zwei Formen : import import voll-qualifizierter Klassenname Package-Name . Importieren einer einzelnen Klasse ; * ; Importieren aller Klassen eines Packages (type import on demand), aber nicht von dessen Unter-Packages HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 11/05/3 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Packages in Java (3) • Verwendung von Klassen aus anderen Packages, Forts. ◇ Beispiel zum Importieren einer Klasse : Verwendung der Klasse Date aus dem Package java.util // Datum2.java import java.util.Date; // oder : import java.util.*; public class Datum2 { public static void main(String[] args) { Date now = new Date(); System.out.println(now); } } ◇ Anmerkung : Importieren bedeutet kein Einbinden von Code anderer Klassen. • Die Zugriffsberechtigung "package" ◇ In Java haben alle Klassenkomponenten einer Klasse, die nicht explizit als public, protected oder private gekennzeichnet sind, die Zugriffsberechtigung "package". Æ Default-Zugriffsberechtigung ◇ Diese Zugriffsberechtigung erlaubt den Zugriff durch alle Klassen desselben Packages. ◇ Die Zugriffsberechtigung durch ein Package erstreckt sich nicht auf dessen eventuelle Unter-Packages. Klassen aus Unter-Packages haben keinen Zugriff zu "package"-Komponenten von Klassen ihres OberPackages. ◇ Die Zugriffsberechtigung protected umfasst in Java auch die Zugriffsberechtigung "package" Æ Zu Klassenkomponenten mit der Zugriffsberechtigung protected können neben den abgeleiteten Klassen auch alle Klassen desselben Packages zugreifen. ◇ Beispiel : package vorl.pack1; public class PackAccDemo { int ipa = 1; protected int ipo = 2; public int ipu = 3; // ... } // Zugriffsberechtigung package package vorl.pack1; class PackAccDemoUse1 { public static void main(String[] args) { PackAccDemo demo = new PackAccDemo(); System.out.println("PackAckDemo.ipa (package) : " + demo.ipa); System.out.println("PackAckDemo.ipu (public) : " + demo.ipu); System.out.println("PackAckDemo.ipo (protected) : " + demo.ipo); } } HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 11/06/1 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Die Java-Standardbibliothek (1) • Allgemeines ◇ Die Java-Plattform stellt auch eine Laufzeit-Bibliothek (API) zur Verfügung. ◇ Diese sehr umfangreiche Java Standard-Bibliothek enthält zahlreiche nützliche Klassen für diverse Anwendungsbereiche. Eine Reihe dieser Klassen implementieren wesentliche Spracheigenschaften und sind damit sehr eng mit der Sprache selbst verknüpft. ◇ Die Klassen der Standardbibibliothek sind auf verschiedene APIs aufgeteilt und in Paketen (Packages) zusammengefasst. Dabei ist die Zuordnung der einzelnen Klassen zu den verschiedenen Paketen nicht immer ganz stimmig, insbesondere stimmen die API-Grenzen nicht immer mit den Paketgrenzen überein. ◇ Zu vielen der Haupt-Paketen existieren Unter-Pakete. ◇ Die Pakete der im JDK enthaltenen Java Standard-Bibliothek lassen sich in drei Gruppen einteilen : ▻ Standard-Pakete. Sie gehören zu jeder Java-Plattform ▻ Standard-Erweiterungs-Pakete. Sie stellen erweiterte und ergänzende Funktionalitäten zur Verfügung und müssen nicht unbedingt auf jeder Java-Plattform zur Verfügung stehen ▻ Ergänzende Pakete von Dritt-Herstellern. Sie ergänzen und erweitern ebenfalls die Funktionalität der Bibliothek. ◇ Die Java Standard-Bibliothek ist ständig erweitert worden. (JDK 1.0 : 8 Pakete, JDK 1.4 über 130 Pakete, JDK 5.0 über 170 Pakete, JDK 6.0 203 Pakete) ◇ Sun stellt in der das JDK begleitenden Dokumentation eine ausführliche Beschreibung der Klassen zur Verfügung (Java Platform API Specification) • Überblick über die Standard-Pakete ◇ Die Namen der Standard-Pakete beginnen mit java. ◇ Die folgende Tabelle gibt nur einen Überblick über die Haupt-Pakete dieser Gruppe java.applet Applets java.awt Abstract Windowing Toolkit : Erzeugung von GUIs, mehrere Unter-Pakete java.beans Java Beans (Komponenten-Architektur von Java), ein Unter-Paket java.io Ein-/Ausgabe mittels Streams, Dateien und Serialisierung java.lang Elementare Sprachunterstützung, fünf Unter-Pakete java.math Unterstützung von Ganzzahl- und Gleitpunkt-Arithmetik java.net Unterstützung von Netzwerkanwendungen java.nio New I/O Package, verbesserte I/O-Unterstützung, ab JDK 1.4, vier Unter-Pakete java.rmi Remote Method Invocation, Entfernte Objekt-Kommunikation, vier Unter-Pakete java.security Sicherheits-Framework, vier Unter-Pakete java.sql Datenbankzugriff java.text Erweiterte Textdarstellung und –bearbeitung, Internationalisierung, ein Unter-Paket java.util Utility-Klassen, Collection Framework, spezielle Datenstrukturen, neun Unter-Pakete HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 11/06/2 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Die Java-Standardbibliothek (2) • Überblick über die Standard-Erweiterungs-Pakete ◇ Die Namen der Standard-Erweiterungs-Pakete beginnt mit javax. ◇ Die folgende Tabelle gibt nur einen Überblick über die Haupt-Pakete dieser Gruppe (hier : Standard Edition) javax.accessibility Unterstützung spezieller I/O-Geräte (z.B. für Braille-Zeichen) javax.activation Unterstützung der Objekt-Aktivierung javax.activity spezielle Exception-Klassen im Zusammenhang mit der CORBA-Serialisierung javax.annotation Unterstützung von Annotations, ein Unter-Paket javax.crypto Unterstützung kryptographischer Operationen, zwei Unter-Pakete javax.imageio I/O von Bilddateien, mehrere Unter-Pakete javax.jws Unterstützung von Web Services, ein Unter-Paket javax.lang.model Unterstützung der Modellierung der Sprache Java, drei Unter-Pakete javax.management Java Management Erweiterungen, mehrere Unter-Pakete javax.naming Zugriff zu Namensdiensten, vier Unter-Pakete javax.net Ergänzung zur Netzwerkunterstützung, ein Unter-Paket javax.print Print Service API, Zugriff zu Print Services, drei Unter-Pakete javax.rmi Ergänzung zum RMI-API, zwei Unter-Pakete javax.script Scripting API (Definition von Java Scripting Engines) javax.security Ergänzung zum Sicherheits-Framework, besteht nur aus Unter-Paketen javax.sound Sound API, besteht nur aus Unter-Paketen javax.sql Ergänzung zum Datenbankzugriffs-API (serverseitig), drei Unter-Pakete javax.swing Swing Toolkit, Erweiterungen zur GUI-Erzeugung, zahlreiche Unter-Pakete javax.tools Interfaces und Klassen für Tools zum Starten aus Programmen heraus javax.transaction Unterstützung von Transaktionen, ein Unter-Paket javax.xml Zugriff zu XML-Dateien, zahlreiche Unter-Pakete • Ergänzende Pakete von Drittherstellern ◇ Hierzu gehören zahlreiche Pakete unterhalb der org.omg.-Hierarchie. Sie stellen im wesentlichen CORBA-Unterstützung zur Verfügung ◇ Weitere Pakete befinden sich unter den Hierachien org.w3c. und org.xml. Sie bieten Unterstützung für den Zugriff zu XML-Dateien. ◇ Das Paket org.ietf.jgss stellt ein Framework zur Nutzung von Sicherheitsdiensten (Authentifizierung, Daten-Sicherheit, Daten-Integrität usw) unterschiedlichster Mechanismen (wie z.B. Kerberos) zur Verfügung • Verwendung der Java Standard-Bibliothek ◇ Zur Verwendung von Komponenten der Standard-Bibliothek in eigenen Programmen werden entsprechende import-Vereinbarungen benötigt, wenn nicht voll-qualifizierte Namen verwendet werden sollen. Ausnahme : Das Paket java.lang ist so eng mit der Sprache verknüpft, dass es automatisch durch den Compiler eingebunden wird. Eine explizite import-Anweisung ist daher für Komponenten aus diesem Paket nicht erforderlich. ◇ Der Java-Compiler und der Java-Interpreter finden den Zugriffspfad zu den Bibliotheks-Dateien automatisch. Er muss daher nicht in die CLASSPATH-Variable aufgenommen werden (ab JDK 1.2). HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/00/0 – TH – 01 ----------------------------------------------------------------------------------------------------------------------------------------------------------- Programmieren (4. Sem.) Kapitelüberblick 11. Datentypen in Java 11.1. Einfache Datentypen 11.2. Referenztypen und Objekterzeugung 11.3. Wrapperklassen für die einfachen Datentypen 11.4. Strings 11.5. Arrays 11.6. Die Klasse Object HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/01/1 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Einfache Datentypen in Java (1) • Allgemeines zu Datentypen ◇ Java ist eine streng typisierte Sprache. Das bedeutet, dass jede Variable und jeder Ausdruck einen Typ hat, der zur Compilezeit bekannt sein muß. ◇ In Java muß jede Variable immer einen definierten Wert haben.: Membervariable werden automatisch mit einem Default-Wert initialisiert, sofern ihnen nicht explizit ein Wert zugewiesen wird. Bei lokalen Variablen verhindert der Compiler, dass sie ohne explizite Initialisierung bzw Wertzuweisung verwendet werden (Definite Assignment). ◇ In Java gibt es zwei Kategorien von Datentypen : ▻ einfache (primitive) Datentypen (primitive types) ▻ Referenz-Typen (reference types) • Überblick über die einfachen Datentypen ◇ Variable eines einfachen Datentyps enthalten einen Wert ihres jeweiligen Typs. ◇ Java kennt acht einfache Datentypen ◇ Für jeden Datentyp ist der Wertebereich und damit die Größe des belegten Speicherplatzes eindeutig – unabhängig von der jeweiligen Plattform – festgelegt. ◇ Zu jedem primitiven Datentyp ist in der Standard-Bibliothek eine Wrapper-Klasse definiert (s. später). ◇ Überblick : Typname Größe Art des Typs Wertebereich Default-Wert WrapperKlasse byte 1 Byte vorzeichenbeh. ganze Zahl -128 ... +127 0 Byte short 2 Bytes vorzeichenbeh. ganze Zahl -215 0 Short 31 ... +215-1 int 4 Bytes vorzeichenbeh. ganze Zahl -2 ... +2 -1 0 Integer long 8 Bytes vorzeichenbeh. ganze Zahl -263 ... +263-1 0 Long char 2 Bytes Unicode-Zeichen alle Unicode-Zeichen '\u0000' Character float 4 Bytes Gleitpunktzahl (reelle Zahl) +/-3.4028...*1038 0.0 Float 0.0 Double false Boolean double 8 Bytes 31 308 Gleitpunktzahl (reelle Zahl) +/-1.7976...*10 boolean 1 Bit *) logischer Wert false, true *) Die belegte Speichergröße ergibt sich durch die kleinste adressierbare Speichereinheit (meist 1 Byte) ◇ Der Datentyp float entspricht dem 32-Bit-IEEE-Format (single precision) Der Datentyp double entspricht dem 64-Bit-IEEE-Format (double precision) ◇ Der Datentyp char zählt (mit byte, short, int und long) zu den ganzzahligen Datentypen. ◇ Die ganzzahligen Datentypen bilden zusammen mit den Gleitpunkttypen (float und double) die numerischen Typen. ◇ Der logische Datentyp boolean muß überall dort verwendet werden, wo ein logischer Operand erforderlich ist (logische Operatoren, Steuerausdrücke in den Steueranweisungen, erster Ausdruck im bedingten Auswerte-Operator). Ein Ersatz durch ganzzahlige Typen (Werte 0 und !=0) ist nicht zulässig. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/01/2 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Einfache Datentypen in Java (2) • Darstellung der Werte der einfachen Datentypen (Konstante, literals) ◇ Die Wertedarstellung entspricht im wesentlichen der von C ◇ Datentypen byte, short, int und long (ganzzahlige Datentypen ohne char) : ▻ Darstellung als Dezimalzahl (Ziffern 0 bis 9, Beginn nicht mit 0) ▻ Darstellung als Oktalzahl : Beginn mit 0 (nur Ziffern 0 bis 7) ▻ Darstellung als Sedezimalzahl : Beginn mit 0x oder 0X (Ziffern 0 bis 9, a bis f, A bis F) ▻ Ziffernfolgen sind grundsätzlich (ohne Suffix) vom Typ int. Allerdings muss der dargestellte Wert innerhalb des für int zulässigen Bereichs liegen. ▻ Ziffernfolgen mit dem Suffix l oder L sind vom Typ long ▻ Jeder ganzzahlige Wert, kann mit dem Vorzeichen – oder + versehen werden. ◇ Datentypen float und double : ▻ Exponential- oder Dezimalbruchdarstellung ▻ ohne Suffix oder mit Suffix d oder D : Datentyp double ▻ mit Suffix f oder F : Datentyp float ▻ Zusätzlich kann eine Gleitpunktzahl mit dem Vorzeichen – oder + versehen werden. ◇ Datentyp char : ▻ Darstellung eines Einzelzeichens in einfachen Hochkommata (single quotes), z.B.: 'A', 'Π', ':' ▻ Darstellung durch eine C-kompatible Escape-Sequenz in einfachen Hochkommata: '\b' '\t' '\n' '\r' '\f' '\'' '\"' '\\' '\ooo' Backspace (BS) Horizontaler Tabulator (Horizontal Tab, HT) Zeilenwechsel (Newline, Linefeed, LF) Carriage Return (CR) Seitenwechsel (Form Feed, FF) Ersatzdarstellung für einfaches Hochkomma (single quote) Ersatzdarstellung für doppeltes Hochkomma (double quote) Ersatzdarstellung für Fluchtsysmbol \ Oktal-Escape-Sequenz, 1 bis 3 Oktalziffern, maximaler Wert : 0377 Anmerkung : Die C-Escape-Sequenzen '\v', '\a', '\?' und '\xhhh' sind in Java nicht implementiert. ▻ Darstellung durch eine Unicode-Escape-Sequenz in einfachen Hochkommata : '\uhhhh' (h Sedezimalziffer), z.B. : '\u0041' (== 'A'), '\u00dc' (== 'Ü'), '\uffff' ◇ Für einige Grenzwerte (z.B. größter und kleinster darstellbarer positiver Wert) der numerischen Datentypen sind in den entsprechenden Wrapper-Klassen (Package java.lang) symbolische Konstanten (als static public) definiert. Æ s. später ◇ Datentyp boolean ▻ Es existieren nur die beiden Konstanten true und false HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/01/3 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Einfache Datentypen in Java (3) • Typkonvertierungen bei einfachen Datentypen ◇ Typkonvertierungen (Casts) zwischen allen ganzzahligen Typen (einschließlich char) und den Gleitpunkt-Typen sind grundsätzlich zulässig. ◇ Implizite Typkonvertierungen bei typgemischten zweistelligen arithmetischen Operationen vor Ausführung der Operation (numeric promotion) : ▻ Nur ganzzahlige Operanden (Integer-Operation), kein Operand vom Typ long : Alle Nicht–int-Operanden werden in den Typ int umgewandelt. Die Operation wird als int-Operation ausgeführt und liefert ein int-Ergebnis ▻ Nur ganzzahlige Operanden (Integer-Operation), ein Operand vom Typ long : Der andere Operand wird in den Typ long umgewandelt. Die Operation wird als long-Operation ausgeführt und liefert ein long-Ergebnis ▻ Ein Operand ist vom Typ double. Der andere Operand wird in den Typ double umgewandelt. Die Operation wird als double-Operation ausgeführt und liefert ein double-Ergebnis ▻ Ein Operand ist vom Typ float und der andere nicht vom Typ double. Der andere Operand wird in den Typ float umgewandelt. Die Operation wird als float-Operation ausgeführt und liefert ein float-Ergebnis ◇ Implizite Typkonvertierungen zwischen numerischen Typen bei Wertzuweisungen (u. Initialisierungen) : Nur zulässig, wenn sichergestellt ist, dass keine Information verloren gehen kann : ▻ Ganzzahl-Werte an Ganzzahl-Variable eines größeren Typs. ▻ Ganzzahl-Werte an Gleitpunkt-Variable ▻ int-Konstante an byte-, short- oder char-Variable, wenn die int-Konstante sich auch als Wert des kleineren Datentyps darstellen lässt. ◇ Alle anderen Umwandlungen zwischen numerischen Typen, wenn also Information verloren gehen könnte, sind nur explizit möglich (Cast-Operator !). ◇ Zwischen dem Typ boolean und den numerischen Datentypen sind keinerlei Typkonvertierungen zulässig (auch nicht explizit). ◇ Werte eines numerischen Datentyps lassen sich mit einem String-Operanden (Bibliotheks-Klasse String) mittels des Konkatenations-Operators (+) vernüpfen. In einem derartigen Fall wird der numerische Wert in seine dezimale Text-Darstellung umgewandelt und mit dem String-Operanden zu einem neuen String zusammengefasst. Beispiel : double dv = 74.25; System.out.println("Wert von dv : " + dv); Æ Ausgabe : Wert von dv : 74.25 ◇ Analog lassen sich Werte des Typs boolean mit einem String-Operanden mittels des Konkatenations-Operators (+) verknüpfen. In einem derartigen Fall wird der logische Wert in seine Textdarstellung (entweder "true" oder "false") umgewandelt und mit dem String-Operanden zu einem neuen String zusammengefasst. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/02/1 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Referenz-Typen und Objekterzeugung in Java • Eigenschaften von Referenz-Typ-Variablen ◇ Variable eines Referenz-Typs referieren Objekte, d.h. Instanzen einer Klasse. Objekte können in Java nicht direkt als Werte verwendet werden (z.B. als Funktions-Parameter), sondern nur über eine Referenz. ◇ Variable eines Referenz-Typs (Referenz-Variable) werden zwar i.a. als "Objekt-Variable" betrachtet, tatsächlich sind sie aber Pointer-Variable, die auf ein Objekt verweisen. Sie belegen nur den Speicherplatz zur Aufnahme einer Objekt-Adresse. Ihr Wert ist also eine Speicheradresse. Bei ihrer Verwendung werden sie automatisch dereferenziert. (Anwendung des Punkt- (.) Operators zum Komponentenzugriff). ◇ Eine Referenz-Variable kann immer nur auf Objekte eines bestimmten Typs bzw bestimmter Typen (Polymorphie !) zeigen. Formal werden drei Arten von Referenz-Variablen unterschieden : ▻ Klassen-Typen (sie verweisen auf Objekte der entsprechenden Klasse oder auf Objekte davon abgeleiteter Klassen) ▻ Interface-Typen (sie verweisen auf Objekte, die das jeweilige Interface implementieren) ▻ Array-Typen (sie verweisen auf Arrays, Arrays sind in Java ebenfalls – spezielle – Objekte) ◇ Die für Referenz-Variable definierten Vergleichs-Operatoren == und != beziehen sich auf die Referenzen (Adressen) und nicht auf die referierten Objekte. Gleiche Referenzen bedeuten natürlich auch Gleichheit der referierten Objekte. Ungleiche Referenzen bedeuten aber nicht, dass auch die referierten Objekte ungleich sein müssen. ◇ Als spezielle Referenz-Konstante ist der Wert null definiert. Eine Referenz-Variable mit diesem Wert zeigt auf nichts (also auf kein Objekt). Einer lokalen Referenz-Variablen, die auf nichts zeigen soll, muss dieser Wert explizit zugewiesen werden. Beispiel : String sa = null; Für Referenz-Variable, die Member-Variable sind, ist null der ohne explizite Initialisierung zugewiesene Default-Wert. ◇ Mit dem auf Referenz-Variable anwendbaren Operator instanceof lässt sich überprüfen, ob das referierte Objekt von einem bestimmten Typ ist (s. später). • Erzeugung von Objekten ◇ Objekte werden grundsätzlich nur namenlos dynamisch auf dem Heap alloziert. ◇ Hierzu dient ein new-Ausdruck : new-Operator mit nachgestellten Konstruktor-Aufruf. ◇ Konstruktoren sind spezielle klassenspezifische Funktionen, die zur Erzeugung eines Objekts benötigt werden. Sie tragen i.a. den Namen der Klasse des zu erzeugenden Objekts (Ausnahme s. Arrays). Konstruktoren können Parameter besitzen. ◇ Der new-Ausdruck liefert die Adresse des erzeugten Objekts zurück, die dann einer Referenz-Variablen als Wert zugewiesen werden kann. Beispiel : String sv = new String("Hallo !"); ◇ In Java gibt eine in der JVM im Hintergrund laufende automatische Garbage Collection den allozierten Speicher für ein Objekt wieder frei, wenn keinerlei Referenz mehr auf das Objekt existiert Eine explizite Freigabe des dynamisch allozierten Speichers ist daher weder notwendig noch überhaupt möglich Æ In Java existiert daher kein Speicherfreigabe-Operator (wie z.B. delete in C++). HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/03/1 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Wrapper-Klassen für die einfachen Datentypen (1) • Verwendung von Werten einfacher Datentypen wie Objekte ◇ Werte einfacher Datentypen und Objekte werden unterschiedlich dargestellt und referiert. Dadurch lassen sich Werte einfacher Datentypen nicht direkt in Situationen, in denen Objekte benötigt werden, verwenden (z.B. als Argument in vielen Methoden, aktueller Typ bei generischen Datentypen) ◇ Um eine derartige Verwendung trotzdem zu ermöglichen, existieren Wrapper-Klassen für alle einfachen Datentypen. Ein Objekt einer Wrapper-Klasse kapselt einen Wert des korrespondierenden einfachen Datentyps. Dieser Wert wird bei der Objekterzeugung festgelegt (Konstruktor-Parameter) und kann später nicht mehr geändert werden. • Die Wrapper-Klassen im Überblick ◇ Die Wrapper-Klassen befinden sich im Package java.lang ◇ Alle Wrapper-Klassen – außer Character – verfügen über mindestens 2 Konstruktoren : ▻ Ein Konstruktor besitzt einen Parameter vom zu kapselnden einfachen Typ. Er dient zur Umwandlung : Wert Æ Objekt ▻ Der zweite Konstruktor besitzt einen String-Parameter. Der String muss die textuelle Repräsentation des zu kapselnden Wertes sein. ◇ Zur Ermittlung des von einem Wrapper-Klassen-Objekt gekapselten Wertes stellen die Wrapper-Klassen sogenannte "Getter"-Methoden zur Verfügung (Umwandlung Objekt Æ Wert): primitivtyp primitivtypValue() ◇ einfacher Datentyp Wrapper-Klasse "Getter"-Methode char Character public char charValue() byte Byte public byte byteValue() short Short public short shortValue() int Integer public int intValue() long Long public long longValue() float Float public float floatValue() double Double public double doubleValue() boolean Boolean public boolean booleanValue() ◇ Beispiele : Integer iobj Double dobj long lval1 Long lobj = = = = new Integer(27); new Double("3.14"); -34567L; new Long(lval1); int ival double dval long lval2 = iobj.intValue(); = dobj.doubleValue(); = lobj.LongValue(); HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/03/2 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Wrapper-Klassen für die einfachen Datentypen (2) • Umwandlung von Strings in dargestellte Werte ◇ Die Wrapper-Klassen stellen auch statische Methoden zur Verfügung, die einen String, der die textuelle Repräsentation eines Werts eines einfachen Datentyps ist, in den dargestellten Wert umwandeln. ("Parser"-Methoden) Falls der String nicht umwandelbar ist, wird von den Methoden eine NumberFormatException geworfen. ◇ Überblick über die "Parser"-Methoden : ▻ Klasse Byte : public static byte ▻ Klasse Short : public static short parseShort(String str) ▻ Klasse Integer : public static int parseInt(String str) ▻ Klasse Long : public static long parseLong(String str) ▻ Klasse Float : public static float parseFloat(String str) ▻ Klasse Double : public static double parseDouble(String str) parseByte(String str) ▻ Klasse Boolean : public static boolean parseBoolean(String str) Der String "true" (unabhängig von Groß-/Kleinschreibung) liefert true, alle anderen Strings liefern false (Methode existiert erst ab dem JDK 5.0) ◇ Beispiele : double dv String is int iv boolean bv = = = = Double.parseDouble("55.4321"); "98765"; Integer.parseInt(is); Boolean.parseBoolean("richtig"); • Grenzwerte der numerischen Datentypen ◇ Für alle numerischen Datentypen sind in den entsprechenden Wrapper-Klassen (Package java.lang) die folgenden symbolischen Konstanten (als static public) definiert : MAX_VALUE MIN_VALUE größter darstellbarer positiver Wert kleinster darstellbarer positiver Wert ◇ Für die Datentypen float und double sind in den Klassen Float und Double zusätzlich definiert : NaN NEGATIVE_INFINITY POSITIVE_INFINITY Not-a-Number (Repräsentation eines ungültigen Werts, z.B. 0/0) negativ unendlich positiv unendlich ◇ Verwendung dieser Konstanten immer nur zusammen mit dem jeweiligen Klassennamen (voll-qualifizierter Name) : z.B. : double dv = Double.MIN_VALUE; HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/03/3 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Wrapper-Klassen für die einfachen Datentypen (3) • Autoboxing/-unboxing (Implizite Umwandlung zwischen Werten und Objekten) ◇ Bis zum JDK 1.4 mußten Umwandlungen zwischen den Werten einfacher Datentypen und Wrapper-KlassenObjekten explizit mittels Konstruktoren und "Getter"-Methoden durchgeführt werden (s. oben). ◇ Mit dem JDK 5.0 wurde das Konzept des Autoboxing/-unboxing eingeführt. Dieses ermöglicht eine implizite (automatische) Umwandlung zwischen Werten und Objekten. Æ Werte einfacher Datentypen und Wrapper-Objekte können völlig kompatibel zueinander verwendet werden. ◇ Wert Æ Objekt (Autoboxing) : Beispiel 1 : Beispiel 2 : int iVal1 = 12; Integer iObj; iObj = iVal1; // implizite Erzeugung eines neuen Integer-Objekts Double dObj = 0.75; // implizite Erzeugung eines neuen Double-Objekts ◇ Objekt Æ Wert (Autounboxing) : Beispiel 1 : int iVal2 = iObj; Beispiel 2 : double dVal = dObj; // implizites De-Wrapping // implizites De-Wrapping ◇ Autoboxing/-unboxing findet überall statt, wo Werte einfacher Datentypen bzw Wrapper-Objekte benötigt werden. Damit können z.B. mit Wrapper-Objekten auch "direkt" arithmetische Operationen vorgenommen werden. Beispiele : Integer iObj1 = 10; Integer iObj2 = iObj1 + 5; ++iObj1; ◇ Autoboxing findet allerdings nur dann statt, wenn keine andere implizit mögliche Typkonvertierung anwendbar ist. Derartige Situationen können z.B. bei überladenen Funktionen auftreten. Beispiel : void func(double dv); void func(Integer io); . . . func(5) führt zum Aufruf der ersten Methode (func(5.0)) (int Æ double) HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/04/1 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Strings in Java - Allgemeines • Darstellung von Strings ◇ Strings in Java bestehen aus Unicode-Zeichen. Wie in C besteht eine String-Konstante (string literal) aus einer Folge von 0 oder mehr Zeichen, die in doppelte Hochkommata (double qoutes) eingeschlossen sein müssen. Die Zeichen können auch durch Escape-Sequenzen dargestellt werden. Beispiele : "\n\"Hallo\"" "\u00ea\u0256" ◇ Strings sind in Java Objekte. String-Konstante werden durch Objekte der Bibliotheks-Klasse String dargestellt. Der Inhalt derartiger Objekte ist unveränderlich. Zur Darstellung von veränderlichen Strings dienen die Bibliotheks-Klassen StringBuffer (threadsicher) und StringBuilder (ab JDK 5.0, nicht thread-sicher, aber schneller) ◇ Eine String-Variable ist – wie alle Objekt-Variablen in Java – eine Referenz-Variable. Sie enthält eine Referenz auf ein String-Objekt. Eine Zuweisung eines neuen Strings an eine String-Variable ("Variable vom Typ String") bedeutet nicht, dass das referierte String-Objekt geändert wird, sondern dass die String-Variable ein anderes String-Objekt referiert. Beispiel : String str = "Hausboot"; // ... str = "Segelyacht"; // str zeigt auf ein anderes Objekt ! • Direkte Sprachunterstützung für Strings ◇ In der Sprache Java selbst sind einige Mechanismen zur Erzeugung und Verwendung von Strings implementiert, die im Zusammenhang mit der Klasse String zur Anwendung kommen ◇ Beim Auftritt einer String-Konstanten im Quelltext erzeugt der Compiler ein String-Objekt, das mit der String-Konstanten initialisiert wird. Beispiele : System.out.println("Schoene Gruesse !"); Der Compiler erzeugt ein String-Objekt mit dem Inhalt "Schoene Gruesse !" und übergibt eine Referenz (Adresse) auf dieses Objekt der Methode println(...) String s1 = "Das alte Europa"; Der Compiler erzeugt ein String-Objekt mit dem Inhalt "Das alte Europa" und weist der Variablen s1 eine Referenz (Adresse) auf dieses Objekt zu ◇ Für String-Objekte ist der Operator + überladen als Konkatenations-Operator. Er erzeugt ein neues String-Objekt, das aus den beiden Operanden zusammengesetzt ist. Beispiel : s1 = s1 + " hatte Recht !"; ◇ Der + -Operator wirkt auch dann als Konkatenations-Operator, wenn nur ein Operand vom Typ String ist. In einem derartigen Fall wird der andere Operand implizit in ein String-Objekt umgewandelt und mit dem String-Operanden zu einem neuen String-Objekt konkateniert. Die implizite Konvertierung in ein String-Objekt (string conversion) findet für jeden einfachen Typ und für jeden Referenz-Typ (Klasse !) statt. Ist die in einer Objekt-Variablen gespeicherte Referenz gleich null, wird der String "null" erzeugt. Beispiel : System.out.println("Heute ist : " + new java.util.Date()); ◇ Eine String-Konkatenation kann auch mit dem += -Operator realisiert werden. Wenn hier der rechte Operand kein String-Objekt ist, wird er implizit in ein solches konvertiert. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/04/2 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Die Klasse String in Java (1) • Allgemeines zur Klasse String ◇ Standard-Klasse zur Darstellung von nicht veränderlichen Strings. ◇ Bestandteil des Package java.lang ◇ Von der Klasse String können keine anderen Klassen abgeleitet werden (Die Klasse ist final) ◇ Die Klasse String implementiert das Interface CharSequence ◇ Die Klasse String enthält zahlreicheKonstruktoren und Methoden zur Verwendung von Strings, u.a. - zur expliziten Erzeugung von String-Objekten (Konstruktoren), - zur Ermittlung der Stringlänge - zum lesenden Zugriff zu String-Elementen, - zum String-Vergleich - zum Suchen in Strings - zur Extraktion von Teilstrings, - zum Kopieren von Strings mit Umwandlung aller Buchstaben in Großbuchstaben bzw Kleinbuchstaben. • Konstruktoren der Klasse String (Auswahl) public String() Erzeugt ein String-Objekt, das den Leerstring enthält public String(String str) Erzeugt ein String-Objekt, das eine Kopie des Inhalts von str enthält (Copy-Konstruktor) public String(char[] acs) Erzeugt ein String-Objekt, das die Zeichenfolge aus acs enthält. Die Zeichen werden in das neu erzeugte Objekt kopiert. public String(byte[] abs) Erzeugt ein String-Objekt, das mit der Zeichenfolge, die durch Decodierung der byte-Werte von abs entsteht, initialisiert ist. Die Decodierung erfolgt für den Default-Zeichensatz der Implementierung public String(StringBuffer buf) Erzeugt ein String-Objekt, das mit der in buf bzw public String(StringBuilder bld) bld enthaltenen Zeichenfolge initialisiert ist. Die Zeichen werden in das neu erzeugte Objekt kopiert. ◇ Hinweis : Die explizite Erzeugung eines String-Objekts bei Initialisierung mit einer String-Konstanten ist ineffizient : String str = new String("Wer rastet, der rostet"); Dies führt zunächst zur impliziten Erzeugung eines String-Objektes das mit der String-Konstanten "Wer rastet, der rostet" initialisiert ist. Anschliessend wird zur expliziten Erzeugung eines weiteren String-Objekts der Copy-Konstruktor aufgerufen, dem eine Referenz auf das zuerst erzeugte Objekt übergeben wird. Eine Referenz auf das explizit erzeugte zweite Objekt wird dann der String-Variablen str zugewiesen. Vorzuziehen ist daher die direkte "Initialisierung" einer String-Variablen mit einer String-Konstanten. In diesem Fall wird nur ein String-Objekt – implizit – erzeugt String str = "Wer rastet, der rostet"; Entsprechendes gilt für die Wertzuweisung an String-Variable. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/04/3 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Die Klasse String in Java (2) • Memberfunktionen Klasse String (Auswahl Teil 1) public int length() ▻ Gibt die Länge eines String-Objekts (= die Anzahl der enthaltenen Unicode-Zeichen) als Funktionswert zurück public char charAt(int index) ▻ Liefert das Zeichen mit dem Index index als Funktionswert zurück. ▻ Der zulässige Indexbereich reicht von 0 (erstes Zeichen) bis length()-1 (letztes Zeichen). ▻ Erzeugung einer IndexOutOfBoundsException, wenn ein unzulässiger Index übergeben wird. public boolean contains(CharSequence s) // ab dem JDK 5.0 vorhanden ▻ Überprüfung, ob die Zeichenfolge s (z.B. Inhalt eines String-Objekts) im aktuellen Objekt enthalten ist ▻ Falls ja, wird true als Funktionswert zurückgegeben, andernfalls false public int indexOf(int ch) ▻ Rückgabe des Index des ersten Auftritts des – als int-Wert übergebenen – Zeichens ch als Funktionswert. ▻ Falls das Zeichen ch nicht enthalten ist, wird der Wert –1 zurückgegeben. ▻ Beispiel : "hamburger".indexOf('r') liefert 5 public int indexOf(int ch, int from) ▻ Funktionalität wie int indexOf(int ch), allerdings beginnt die Suche am Index from ▻ Beispiel : "hamburger".indexOf('r', 6) liefert 8 public int indexOf(String str) ▻ Rückgabe des Index des ersten Auftritts des Teilstrings str. ▻ Der zurückgegebene Funktionswert ist der Index des ersten Zeichens des gefundenen Teilstrings. ▻ Falls der Teilstring str nicht enthalten ist, wird der Wert –1 zurückgegeben. public int indexOf(String str, int from) ▻ Funktionalität wie int indexOf(String str), allerdings beginnt die Suche am Index from public public public public int int int int lastIndexOf(int ch) lastIndexOf(int ch, int from) lastIndexOf(String str) lastIndexOf(String str, int from) ▻ Ähnlich wie die Funktionen indexOf(...). ▻ Nur Ermittlung des Index des letzten Auftritts des Zeichens ch bzw des Teilsstrings str. ▻ Beispiel : "hamburger".lastIndexOf("rindfleisch") liefert -1 public boolean isEmpty() // ab dem JDK 6.0 vorhanden ▻ Gibt true zurück, wenn das aktuelle String-Objekt leer (d.h. seine Länge==0) ist, andernfalls false HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/04/4 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Die Klasse String in Java (3) • Memberfunktionen Klasse String (Auswahl Teil 2) public boolean endsWith(String suffix) ▻ Überprüfung, ob das aktuelle Objekt mit dem String suffix endet. ▻ Falls ja, wird true als Funktionswert zurückgegeben, andernfalls false public boolean startsWith(String prefix) ▻ Überprüfung, ob das aktuelle Objekt mit dem String prefix beginnt. ▻ Falls ja, wird true als Funktionswert zurückgegeben, andernfalls false public boolean equals(Object obj) ▻ Überprüfung, ob das übergebene Objekt ein String-Objekt ist und gleiche Länge und gleichen Inhalt wie das aktuelle Objekt hat. ▻ Falls ja, wird true als Funktionswert zurückgegeben, andernfalls false public int compareTo(String str) ▻ Durchführung eines lexikographischen Vergleichs zwischen dem aktuellen String-Objekt und dem String-Objekt str ▻ Der Vergleich wird mit den Codewerten der einzelnen Zeichen bzw der Stringlänge ausgeführt ▻ Funktionswert : - 0, wenn beide String-Objekte gleich sind - <0, wenn das aktuelle Objekt "kleiner" als str ist - >0, wenn das aktuelle Objekt "größer" als str ist ▻ Beispiel : "haben".compareTo("hat") liefert –18 public String substring(int beg, int end) ▻ Rückgabe des Teilstrings, der am Index beg beginnt und am Index end-1 endet, als neues StringObjekt (genauer : als Referenz auf ein neu erzeugtes String-Objekt mit dem entsprechenden Inhalt des Teilstrings). ▻ Erzeugung einer IndexOutOfBoundsException, falls wenigstens einer der übergebenen Indizees unzulässig ist oder beg>end ist. ▻ Beispiel : "hamburger".substring(4,8) liefert "urge" public String toUpperCase() ▻ Rückgabe eines neuen String-Objekts, in dem alle Buchstaben in Großbuchstaben konvertiert sind public String replace(char alt, char neu) ▻ Rückgabe eines neuen String-Objekts, das aus dem aktuellen Objekt durch Ersatz aller Auftritte des Zeichens alt durch das Zeichen neu und der Übernahme alle übrigen Zeichen entsteht. ▻ Ist das Zeichen alt überhaupt nicht im aktuellen Objekt enthalten, wird kein neues Objekt erzeugt, sondern das aktuelle Objekt zurückgegeben. public String trim() ▻ Rückgabe einer neu erzeugten Kopie des aktuellen Objekts, bei der alle führenden und endenden Blanks sowie ASCII-Steuerzeichen (Zeichencode <= '\u0020') entfernt sind ▻ Falls sowohl das erste als auch das letzte Zeichen des aktuellen String-Objekts einen Zeichencode > '\u0020' besitzen, wird keine Kopie erzeugt, sondern das aktuelle Objekt zurückgegeben HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/04/5 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Die Klasse String in Java (4) • Explizite Umwandlung in eine String-Darstellung ◇ In der Klasse String existiert eine mehrfach überladene statische Memberfunktion zur Umwandlung von Werten der einfachen Datentypen sowie von Objekten beliebiger Klassen in eine String-Darstellung : Beispiel für die Umwandlung von double-Werten : public static String valueOf(double d) ◇ Für jede Klasse ist in Java die folgende Memberfunktion definiert, mit der ein Objekt der jeweiligen Klasse in ein String-Objekt (textuelle Repräsentation des Objekts !) umgewandelt wird : public String toString() • Demonstrationsbeispiel zur Klasse String // StringDemo.java public class StringDemo { public static void main(String[] args) { System.out.println("Heute ist : " + new java.util.Date()); String str = "Aller Anfang"; str = str + " ist"; str += " schwer"; System.out.println(str); int len = str.length(); System.out.println("Laenge des Strings : " + len); int idx = 6; System.out.println(idx+1 + ". Zeichen : " + str.charAt(idx)); System.out.println("Index von \"ist\" : " + str.indexOf("ist")); char ch = 'r'; System.out.println("letzter Index von " + ch + " : " + str.lastIndexOf(ch)); int ie = idx+6; System.out.print("Teilstring (" + idx + ',' + ie + ") : "); System.out.println(str.substring(idx, ie)); System.out.println(str.toUpperCase()); if (str.equals("Aller Anfang ist schwer")) System.out.println("Strings sind gleich !"); else System.out.println("Strings sind ungleich !"); int diff = "haben".compareTo("hat"); System.out.println("Vergleich von \"haben\" und \"hat\" liefert : "+diff); } } Ausgabe des Programms Heute ist : Thu Sep 04 10:38:41 CEST 2003 Aller Anfang ist schwer Laenge des Strings : 23 7. Zeichen : A Index von "ist" : 13 letzter Index von r : 22 Teilstring (6,12) : Anfang ALLER ANFANG IST SCHWER Strings sind gleich ! Vergleich von "haben" und "hat" liefert : -18 HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/05/1 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Arrays in Java (1) • Allgemeine Eigenschaften ◇ Arrays sind in Java Objekte. Æ Array-Typen sind – namenlose – Klassen. Von einer Array-Klasse können keine weiteren Klassen abgeleitet werden. ◇ Als Element-Typ ist jeder beliebige Typ (einfacher Datentyp oder Referenz-Typ) zulässig. ◇ Die Auswahl eines Array-Elements erfolgt wie in C : Ein Array-Element wird über einen Index, der vom Typ int sein muß, ausgewählt. Der Index des ersten Elements hat den Wert 0. ◇ Array-Variable sind Referenz-Variable. Sie verweisen auf Array-Objekte. Æ Die Definition einer Array-Variablen erzeugt noch kein Array-Objekt, d.h. sie alloziert keinen Speicherplatz für die Array-Elemente. ◇ Eine Array-Variable legt nur den Element-Typ , nicht aber die Länge eines Arrays (d.h. die Anzahl seiner Elemente) fest. Sie kann daher auf Arrays beliebiger Länge zeigen. ◇ Die Länge eines Arrays wird dynamisch bei seiner Erzeugung (z.B. mittels eines new-Ausdrucks) festgelegt. Anschließend ist sie nicht mehr veränderbar. ◇ Ein Array kann auch die Länge 0 besitzen. Ein derartiges Array ist ein echtes Array-Objekt. Sinnvoll können derartige Arrays beispielsweise als Rückgabewert von Funktionen (genauer : Referenz darauf) auftreten. ◇ Jedes Array besitzt die öffentliche Datenkomponente public final int length; Sie enthält die Länge des Arrays. ◇ Ein Array-Zugriff wird zur Laufzeit auf Zulässigkeit überprüft. Der Versuch des Zugriffs zu einer nicht existierenden Array-Komponente (ungültiger Index) führt zum Werfen der Exception ArrayIndexOutOfBoundsException. • Vereinbarung von Array-Variablen ◇ Angabe eines Array-Typs : Element-Typ [] ◇ Diese Typangabe ist bei der Vereinbarung von Array-Variablen zu verwenden. Beispiele : int[] ia; // ia kann auf Array-Objekte zeigen, deren Elemente int-Werte sind String[] names; // names kann auf Array-Objekte zeigen, deren Elemente // Referenzen auf String-Objekte sind ◇ Element-Typ eines Arrays kann wiederum ein Array sein Æ mehrdimensionale Arrays. Die Vereinbarung mehrdimensionaler Arrays erfolgt analog zu eindimensionalen Arrays. Beispiele : double[][] kmat; // kmat kann auf Array-Objekte zeigen, deren Elemente Referenzen // auf Array-Objekte sind, die double-Werte enthalten String[][] seite; // seite kann auf Array-Objekte zeigen, deren Elemente // Referenzen auf Array-Objekte sind, die Referenzen auf // String-Objekte enthalten. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/05/2 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Arrays in Java (2) • Definition von Array-Objekten mittels new-Ausdruck ◇ Andere Syntax als bei Nicht-Array-Objekten : kein Konstruktoraufruf ! Stattdessen ist der Elementtyp zusammem mit der Länge des Arrays anzugeben. Die einzelnen Elemente werden mit ihrem Defaultwert initialisiert. Beispiele : ia = new int[10]; // Speicherallokation für ein Array-Objekt, das 10 int// Elemente hat. // Alle Elemente sind mit 0 initialisiert. names = new String[3]; // // // // Speicherallokation für ein Array-Objekt, das 3 Elemente hat, die Referenzen auf String-Objekte sind. Alle Elemente sind mit null initialisiert. ◇ Die Definition eines Array-Objekts mittels eines new-Ausdrucks kann natürlich auch zusammen mit der Vereinbarung einer Array-Variablen erfolgen. Beispiel : float[] pol = new float[6]; // pol zeigt auf ein alloziertes float-Array mit // 6 Elementen (alle mit 0.0 initialisiert) ◇ Bei mehrdimensionalen Arrays kann im new-Ausdruck die Länge aller Dimensionen angegeben werden. Es reicht aber aus, nur die Länge der ersten (am weitesten links stehenden) Dimension festzulegen. Die Längen der übrigen Dimensionen müssen dann später festgelegt werden. Beispiel : kmat = new double[5][4]; // // // // // // Speicherallokation für ein Array-Objekt, das 5 Elemente hat, die auf gleichzeitig allozierte double-Arrays, die alle 4 Elemente haben, zeigen. Die Elemente der double-Arrays sind mit 0.0 initialisiert Das obige Beispiel ist eine abkürzende Schreibweise für die folgende ausführlichere Formulierung : kmat = new double[5][]; // // // // Speicherallokation für ein Array-Objekt, das 5 Elemente hat, die auf double-Arrays zeigen können, aber alle mit null initialisiert sind. for (int i=0; i<kmat.length; i++) kmat[i] = new double[4]; // Speicherallokation für 5 double-Arrays der // Länge 4. // Alle Elemente dieser Arrays sind mit 0.0 // initialisiert. ◇ Als Vorteil der Java-Implementierung von mehrdimensionalen Arrays ergibt sich, dass die Element-Arrays (Arrays in zweiter (und gegebenenfalls höherer) Dimension eine unterschiedliche Länge besitzen können. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/05/3 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Arrays in Java (3) • Definition von Array-Objekten mittels Array-Initialisierer (array initializer) ◇ Syntax : { Initialisierungs-Ausdruck } , ◇ Bei der Vereinbarung einer Array-Variablen kann ein Array-Initialisierer kann angegeben werden. Dieser bewirkt die Allokation von Speicherplatz für ein Array-Objekt und initialisiert die einzelnen Elemente mit den angegebenen Initialisierungs-Ausdrücken. Die Länge des Arrays wird nicht angegeben .Sie ergibt sich aus der Anzahl der Initialisierungswerte Beispiel : String[] farben = { "rot", "gruen", "blau", "gelb", "weiss" }; // Vereinbarung der Array-Variablen farben. Diese zeigt auf ein gleichzeitig alloziertes // Array-Objekt, dessen 5 Elemente mit den Referenzen auf die angegebenen Farb-Strings, // für die ebenfalls Speicher alloziert wird, initialisiert werden. ◇ Bei mehrdimensionalen Arrays können Array-Initialisierer geschachtelt werden (InitialisierungsAusdrücke sind wiederum Array-Initialisierer) ◇ Array-Initialisierer können auch zusammen mit einem new-Ausdruck verwendet werden. Sie sind dem new-Ausdruck nachzustellen, eine Array-Länge darf nicht angegeben werden. (sinnvoll zur Erzeugung anonymer Arrays) • Unterstützende Klassen der Standard-Bibliothek ◇ In der Java-Standardbibliothek sind zwei Klassen definiert, die statische Memberfunktionen zur Unterstützung der Verwendung von Arrays zur Verfügung stellen Beide Klassen können nicht instanziiert werden. ◇ Klasse java.lang.reflect.Array Sie stellt statische Methoden zur dynamischen Erzeugung von und zum Zugriff zu Arrays zur Verfügung. ◇ Klasse java.util.Arrays Sie stellt statische Methoden zur Bearbeitung von Arrays zur Verfügung. Im wesentlichen handelt es sich um Methoden - zum Suchen in Arrays - zum Sortieren von Array-Komponenten - zum Vergleich von Arrays - zur Zuweisung an Array-Bereiche ◇ Die Klasse java.lang.System definiert zum Kopieren von Arraybereichen die statische Methode public static void arraycopy(Object src, int sPos, Object dest, int dPos, int len) Quell-Objekt (src) und Ziel-Objekt (dest) müssen Arrays sein HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/05/4 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Demonstrationsprogramm zu Arrays in Java • Quellcode des Programms (Klasse ArrayDemo) public class ArrayDemo { public static void main(String[] args) { int[] ia; ia = new int[6]; System.out.println("int-Array : " + ia); for (int i=0; i<ia.length; i++) System.out.println(i + " : " + ia[i]); String[] names = /* new String[]*/ { "Walter", "Franz", "Ilka" }; System.out.println("String-Array :" + names); for (int i=0; i<names.length; i++) System.out.println(i + " : " + names[i]); double[][] kmat = new double[4][3]; System.out.println("2-dimensionales double-Array : " + kmat); for (int i=0; i<kmat.length; i++) System.out.println(i + " : " + kmat[i]); System.out.println("erstes Zeilen-Array :"); for (int i=0; i<kmat[0].length; i++) System.out.println(i + " : " + kmat[0][i]); System.out.println("weiteres 2-dimensionales double-Array :"); double[][] gmat = new double[4][]; for (int i=0; i<gmat.length; i++) gmat[i] = new double[i+1]; for (int i=0; i<gmat.length; i++) { System.out.print(i + " : "); for (int j=0; j<gmat[i].length; j++) System.out.print(gmat[i][j]+ " "); System.out.println(); } } } • Ausgabe des Programms int-Array : [I@108786b 0 : 0 1 : 0 2 : 0 3 : 0 4 : 0 5 : 0 String-Array :[Ljava.lang.String;@119c082 0 : Walter 1 : Franz 2 : Ilka 2-dimensionales double-Array : [[D@1add2dd 0 : [D@eee36c 1 : [D@194df86 2 : [D@defa1a 3 : [D@f5da06 erstes Zeilen-Array : 0 : 0.0 1 : 0.0 2 : 0.0 weiteres 2-dimensionales double-Array : 0 : 0.0 1 : 0.0 0.0 2 : 0.0 0.0 0.0 3 : 0.0 0.0 0.0 0.0 HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/05/5 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Die vereinfachte for-Anweisung in Java • Iteration über alle Elemente eines Arrays (und eines Containers) ◇ Zum sequentiellen Durchlaufen der Elemente eines Arrays wird üblicherweise die – auch aus C bekannte – (Standard-) for-Anweisung eingesetzt. Zum Zugriff zu den Elementen erfordert sie die explizite Definition und Verwendung einer "Laufvariablen". ◇ Beispiel : double[] da; // ... for (int i=0; i<da.length; i++) System.out.println(da[i]); ◇ Analoges gilt für das Durchlaufen eines Containers (in Java Collection genannt). Anstelle der Laufvariablen tritt hier ein Iterator-Objekt. • Vereinfachte for-Anweisung (Enhanced for Loop, For-Each-Schleife) ◇ Sie wurde mit dem JDK 5.0 zum sequentiellen Durchlaufen aller Elemente eines Arrays (bzw einer Collection) eingeführt ◇ Bei ihr wird auf die explizite Definition einer Laufvariablen (bzw eines Iterator-Objekts) verzichtet. ◇ Syntax : Array-Objekt for ( Element-Typ Var-Name : ) Anweisung Collection-Objekt ◇ Beispiel : double[] da; // ... for(double el : da) System.out.println(el); ◇ Wirkung : "Für jedes Element el in da " Der Element-Typ-Variablen el wird in jedem Schleifendurchlauf das jeweils nächste Element des Arrays da zugewiesen. ◇ Die Gültigkeit der Element-Typ-Variablen el ist auf den Schleifenrumpf begrenzt. ◇ Der vom Compiler erzeugte Code benötigt und verwaltet auch hier für den Elementzugriff eine Laufvariable (bzw ein Iterator-Objekt). Diese wird vom Compiler implizit erzeugt. • Anwendbarkeit der vereinfachten for-Anweisung ◇ Sie ist kein allgemeiner Ersatz für die (Standard-) for-Anweisung ◇ Sie kann nur zum sequentiellen Durchlaufen aller Elemente eines Arrays (bzw einer Collection) in Vorwärtsrichtung eingesetzt werden (vom ersten zum letzten Element, ohne Überspringen einzelner Elemente) ◇ Sie kann nicht eingesetzt werden, wenn ▻ ein Element aus dem Array (der Collection) entfernt oder modifiziert werden soll ▻ innerhalb einer Schleife mehrere Arrays (Collections) parallel bearbeitet werden sollen ◇ Ihr Einsatz in geschachtelten Schleifen ist jedoch möglich Beispiel : Durchlaufen eines zweidimensionalen Arrays double[][] gmat; // ... for (double[] row : gmat) for (double el : row) System.out.println(el); HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/05/6 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Demonstrationsprogramm zur vereinfachten for-Anweisung in Java • Quellcode des Programms (Klasse ForEachDemo) // ForEachDemo.java public class ForEachDemo { public static void main(String[] args) { double[] da; da = new double[6]; for (int i=0; i<da.length; i++) da[i] = i; System.out.println("\ndouble-Array (eindimensional) : "); for(double el : da) System.out.println(el); String[] names = { "Walter", "Franz", "Ilka" }; System.out.println("\nString-Array : "); for (String str : names) System.out.println(str); System.out.println("\ndouble-Array (zweidimensional) :"); double[][] gmat = new double[4][]; for (int i=0; i<gmat.length; i++) gmat[i] = new double[i+1]; for (double[] row : gmat) { for (double el : row) System.out.print(el + " "); System.out.println(); } } } • Ausgabe des Programms double-Array (eindimensional) : 0.0 1.0 2.0 3.0 4.0 5.0 String-Array : Walter Franz Ilka double-Array (zweidimensional) : 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/06/1 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Die Klasse Object in Java (1) • "Mutter" aller Klassen ◇ In Java existiert nur eine Klassenhierarchie. Wurzel dieser Hierarchie ist die Klasse Object. Æ Alle Klassen haben – direkt oder indirekt – diese Klasse als Basisklasse. Auch Klassen, die scheinbar nicht abgeleitet sind, d.h. die ohne Angabe einer Basisklasse definiert sind, sind tatsächlich implizit von Object abgeleitet. ◇ Da eine Variable eines Klassen-Typs auch auf Objekte abgeleiteter Klassen zeigen kann (Polymorphie !), kann eine Variable vom Typ Object Objekte eines beliebigen Klassen-Typs (einschliesslich ArrayTyps) referieren. ◇ Die Klasse Object ist im Package java.lang enthalten. • Memberfunktionen der Klasse Object, Allgemeines ◇ Die Klasse Object definiert insgesamt elf Memberfunktionen, die wegen der Vererbung in allen Klassen zur Verfügung stehen. ◇ Sechs dieser Memberfunktionen sind nicht überschreibbar. Die übrigen lassen sich in abgeleiteten Klassen überschreiben. In Abhängigkeit von der jeweiligen Klasse kann für eine sinnvolle Funktionalität ein Überschreiben notwendig sein. ◇ Die Memberfunktionen von Object zerfallen in zwei Kategorien : - Allgemeine Utility-Funktionen (6) - Methoden zur Thread-Unterstützung (5) Hier werden nur 3 der 6 Utilility-Funktionen vorgestellt. Æ siehe nächste Seite HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/06/2 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Die Klasse Object in Java (2) • Memberfunktionen der Klasse Object, Auswahl public boolean equals(Object obj) ▻ Die Funktion vergleicht das aktuelle Objekt mit dem durch obj referierten Objekt. Sie liefert true als Funktionswert, wenn Gleichheit vorliegt, andernfalls false. ▻ Grundsätzlich ist die Funktion für eine Überprüfung auf Wert-Gleichheit der Objekte vorgesehen. ▻ Die Default-Implementierung in der Klasse Object geht davon aus, dass ein Objekt nur zu sich selbst gleich sein kann, d.h. sie setzt Wert-Gleichheit mit Referenz-Gleichheit (Identität) gleich. Sie liefert genau dann true, wenn obj das aktuelle Objekt referiert (obj==this). ▻ Für die Implementierung einer echten Wert-Gleichheit, die sich auf den Inhalt (Zustand) der referierten Objekte bezieht, muss die Methode in abgeleiteten Klassen geeignet überschrieben werden. Dies ist für zahlreiche Klassen der Standard-Bibliothek, u.a. auch für die Klasse String, erfolgt. public String toString() ▻ Diese Funktion ermöglicht es, dass jedes Objekt jeder beliebigen Klasse in einen String "umgewandelt" werden kann (z.B. bei der Konkatenation eines Objekts mit einem String) Æ Sie liefert eine String-Repräsentation des aktuellen Objekts. ▻ Die Default-Implementierung in der Klasse Object erzeugt ein String-Objekt, dessen Inhalt aus dem Klassennamen des aktuellen Objekts, dem Zeichen '@' und der sedezimalen Darstellung eines dem Objekt zugeordneten Codes (Hash-Code) zusammengesetzt ist. Typischerweise ist dieser Code gleich der Speicheradresse des Objekts. ▻ Soll eine andere das Objekt kennzeichnende String-Darstellung erzeugt werden, muss die Methode toString() geeignet überschrieben werden ▻ Die Methode toString() wird immer dann implizit aufgerufen, wenn eine Objekt-Referenz in einem String-Konkatenations-Ausdruck auftritt. protected Object clone() throws CloneNotSupportedException ▻ Die Funktion liefert ein neues Objekt, dass ein Clone (eine Kopie) des aktuellen Objekts ist. ▻ Die tatsächliche Klasse des aktuellen Objekts muß das Interface Cloneable implementieren. ▻ Die Default-Implementierung in der Klasse Object prüft, ob für das aktuelle Objekt das Interface Cloneable implementiert ist. Ist das nicht der Fall, wird die Exception CloneNotSupportedException geworfen. Falls es implementiert ist, wird ein neues Objekt der Klasse erzeugt, dessen Datenkomponenten mit den Werten der entsprechenden Datenkomponenten des aktuellen Objekts initialisiert werden. ("flache" Kopie, shallow copy) ▻ Soll das neue Objekt als "tiefe" Kopie (deep copy) erzeugt werden, muss clone() geeignet überschrieben werden. ▻ Die Klasse Object selbst implementiert das Interface Cloneable nicht. Das bedeutet, dass der Aufruf von clone() für ein Objekt der Klasse Object zum Werfen der Exception CloneNotSupportedException führt. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 12/06/3 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Die Klasse Object in Java (3) • Demonstrationsprogramm zu Memberfunktionen der Klasse Object // ObjDemo.java public class ObjDemo implements Cloneable { private String name; public ObjDemo(String str) { name=str; } public String toString() { return super.toString()+" ("+name+")"; } public static void main(String[] args) { ObjDemo o1 = new ObjDemo("Test1"); System.out.println("o1.toString() : " + o1.toString()); Object o2 = new ObjDemo("Test2"); System.out.println("o2 : " + o2); System.out.println("o2.equals(o1) : " + o2.equals(o1)); o2=o1; System.out.println("\nnach o2=o1 :"); System.out.println("o2 : " + o2); System.out.println("o2.equals(o1) : " + o2.equals(o1)); o2=o1.clone(); System.out.println("\nnach o2=o1.clone() :"); System.out.println("o2 : " + o2); System.out.println("o2.equals(o1) : " + o2.equals(o1)); } } Ausgabe des Programms o1.toString() : ObjDemo@3e25a5 (Test1) o2 : ObjDemo@19821f (Test2) o2.equals(o1) : false nach o2=o1 : o2 : ObjDemo@3e25a5 (Test1) o2.equals(o1) : true nach o2=o1.clone() : o2 : ObjDemo@addbf1 (Test1) o2.equals(o1) : false HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/00/0 – TH – 01 ----------------------------------------------------------------------------------------------------------------------------------------------------------- Programmieren (4. Sem.) Kapitelüberblick 12. Elementare Programmfunktionalitäten 12.1. Zugriff zu Programmparametern 12.2. Standard-Ein-und-Ausgabe (Konsolen-E/A) 12.3. Exceptions 12.4. Dateizugriff HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/01/1 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Zugriff zu Programmparametern in Java • Programmparameter (Kommandozeilenparameter) ◇ Einem Programm können i.a. beim Aufruf Parameter übergeben werden. Æ Programmparameter ◇ Diese werden beim Aufruf aus der Kommandozeile durch Blanks getrennt an den eigentlichen Programmaufruf angehängt. Æ Kommandozeilenparameter Bei Java-Programmen werden sie nach dem Namen der Start-Klasse angegeben. ◇ Beispiel : java Echo Sie tanzte nur einen Sommer Kommandozeilenparameter sind : Sie tanzte nur einen Sommer • Zugriff im Programm ◇ Die Programmparameter (ohne Namen der Startklasse !) werden in einem String-Array zusammengefasst. Eine Referenz auf dieses Array wird der main()-Methode der Startklasse als Parameter übergeben. ◇ Die main()-Methode der Startklasse muß daher mit einem Parameter (Argument) vom Typ String[] definiert werden : public static void main(String[] args) { // ... } ◇ Innerhalb der main()-Methode stehen damit die Programmparameter als Komponenten des StringArrays args zur Verfügung. ◇ Die Anzahl der Programmparameter (= Länge des String-Arrays) ist ermittelbar mittels args.length. • Beispiel : // Echo.java class Echo { public static void main(String[] args) { for (int i=0; i<args.length; i++) System.out.println("Parameter "+ i + " : " + args[i]); } } Beispiel eines Programm-Aufrufs : E:\java\fhm\ee\vorl>java Echo Sie tanzte nur einen Sommer Parameter Parameter Parameter Parameter Parameter 0 1 2 3 4 : : : : : Sie tanzte nur einen Sommer E:\java\fhm\ee\vorl> HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/02/1 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Standard-Ein- und Ausgabe in Java (1) • Grundsätzliches zum I/O-Model von Java ◇ Eine Programm-Ein- und Ausgabe findet üblicherweise über Dateien und/oder Geräte statt. ◇ Java betrachtet sowohl Geräte als auch Dateien grundsätzlich als sequentielle Folge von Bytes (Byte-Stream). Da Java Zeichen und Strings im Unicode darstellt, bietet die Sprache auch die Möglichkeit Textdateien/ Text-Geräte als sequentielle Folge von Unicode-Zeichen (Unicode-Stream) zu interpretieren. ◇ Programmintern wird eine Datei bzw ein Gerät durch ein Objekt einer Stream-Klasse repräsentiert. ◇ Es gibt zahlreiche verschiedene Stream-Klassen, die jeweils unterschiedliche Eigenschaften modellieren. Alle Stream-Klassen sind im Package java.io enthalten. • Standard-Ein- und Ausgabe-Objekte ◇ In den Programmen der meisten Programmiersprachen (z.B. auch in C-Programmen) werden automatisch Zugriffsmöglichkeiten zu den Standard-Ein- und Ausgabegeräten (üblicherweise Tastatur und Bildschirm des Konsolengeräts) bereitgestellt.. Diese können sofort nach Programmstart ohne weitere Maßnahmen eingesetzt werden. ◇ Auch in Java ist dies der Fall : In jedem Java-Programm werden automatisch Stream-Objekte angelegt, die einen Zugriff zur StandardEingabe, Standard-Ausgabe und Standard-Fehlerausgabe ermöglichen. Die entsprechenden Streams sind implizit geöffnet. Aus historischen Gründen handelt es sich bei diesen Stream-Objekten um Byte-Stream-Objekte. ◇ Referenzen auf diese Stream-Objekte stehen als öffentliche statische Datenkomponenten der – nicht instanzierbaren – Klasse System (im Package java.lang) zur Verfügung . Es handelt sich um Referenz-Konstante. Sie müssen zusammen mit dem Klassennamen System verwendet werden. ▻ public static final InputStream in Æ Standard-Eingabe-Objekt Æ Referierung mittels System.in ▻ public static final PrintStream out Æ Standard-Ausgabe-Objekt Æ Referierung mittels System.out ▻ public static final PrintStream err Æ Standard-Fehlerausgabe-Objekt Æ Referierung mittels System.err ◇ Ab den JDK 6.0 existiert eine – allerdings nicht immer verfügbare – alternative Möglichkeit zum Zugriff zur Standard-Ein- und Ausgabe zur Verfügung : Wenn ein Java-Programm aus der Kommandozeile (ohne Ein-/Ausgabe-Umleitung) gestartet wird, wird implizit durch die JVM ein Objekt der Klasse Console erzeugt. (nicht jedoch beim Start aus einer IDE). Eine Referenz auf dieses Objekt kann mit einer statischen Methode der Klasse System ermittelt werden : public static Console console() Die Klasse Console (Package java.io) stellt u.a. Methoden zum Schreiben in das sowie Lesen aus dem Konsolengerät (== Standard-Eingabe + Standard-Ausgabe) zur Verfügung. Genaueres s. API-Dokumentation. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/02/2 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Standard-Ein- und Ausgabe in Java (2) • Schreiben in die Standard-Ausgabe und Standard-Fehlerausgabe ◇ Die durch system.out (Standard-Ausgabe) und system.err (Standard-Fehlerausgabe) referierten Stream-Objekte sind Instanzen der Klasse PrintStream. Diese Klasse stellt die folgenden Methoden zum Schreiben zur Verfügung : public void print(...) public void println(...) ▻ Beide Methoden sind mehrfach überladen und haben jeweils einen Parameter, der dem jeweiligen Typ des auszugebenden Werts bzw Objekts entspricht. Sie ermöglichen die – nicht explizit formatierbare – Ausgabe von - von boolean-, char-, double-, float-, int- und long-Werten - von char-Arrays - von Strings (Objekte der Klasse String) - sowie der String-Repräsentation von Objekten beliebiger Klassen (impliziter Aufruf der Objekt-Methode toString()) ▻ Die Anwendung der String-Konkatenation in Verbindung mit der automatischen Umwandlung von beliebigen Datenwerten und Objekten in eine String-Repräsentation bei ihrem Auftritt in KonkatenationsAusdrücken (Methode toString()) erlaubt die Ausgabe mehrerer Werte/Objekte mit einem Methodenaufruf. ▻ Die Methode println() ergänzt die Ausgabe um ein Zeilenendezeichen. Sie lässt sich auch ohne Parameter aufrufen. In diesem Fall bewirkt sie lediglich einen Übergang in eine neue Zeile. public PrintStream printf(String form, Object... args) (ab dem JDK 5.0) ▻ Diese Methode ermöglicht eine – C-ähnliche – formatierte Ausgabe (s. gesonderte Beschreibung) ▻ Ihr Rückgabewert ist eine Referenz auf das aktuelle PrintStream-Objekt, für das sie aufgerufen wird. ◇ Beispiel zur Anwendung von print(…) und println(…´) : class StdOutDemo1 { public static void main(String[] args) { boolean b = true; char c = 'Z'; int i = 399127; long l = 124L; float f = 2.25E-2f; double d = 0.0/0.0; String s = "Hallo !"; StdOutDemo1 sod = new StdOutDemo1(); System.out.print(b); System.out.println(); System.out.println("Zeichen : " +c); System.out.println(i); System.out.println(i + " " + l); System.out.println(f); System.out.println(d); System.out.println(s); System.out.println(sod); } } Ausgabe : true HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/02/3 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Standard-Ein- und Ausgabe in Java (3) • Formatierte Ausgabe (ab dem JDK 5.0) ◇ Mittels der PrintStream-Methode public PrintStream printf(String form, Object... args); ◇ Die Methode ermöglicht eine C-ähnliche formatierte Ausgabe ◇ Der erste Parameter form ist der Format-String. Er enthält – analog zum Format-String der C-Funktion printf() – die einzelnen Formatangaben (format specifier) für die auszugebenden Werte/Objekte. Durch die Formatangaben wird auch der Typ bzw die Darstellungsart der Ausgabe-Werte festgelegt Zusätzlich kann der Format-String weiteren Text enthalten, der direkt ausgegeben wird. ◇ Die zweite Parameter-Angabe Object... args bedeutet, dass eine beliebige Anzahl (auch keine) weiterer Parameter beliebigen Referenz-Typs folgen darf. Diese Parameter (Argumente) legen die Werte fest, die entsprechend des jeweils spezifizierten Formats auszugeben sind. Für jede Formatangabe muss ein passendes Argument übergeben werden. Die Typen dieser Argumente müssen zu den entsprechenden Angaben im Format-String passen. Überflüssige Argumente werden ignoriert Als Parameter können auch Werte einfacher Datentypen auftreten, da sie mittels Autoboxing automatisch in Objekte der zugehörigen Wrapper-Klassen umgewandelt werden. ◇ Syntax der Formatangaben (vereinfacht) : % [flags][width][.precision]conversion ▻ conversion (Konvertierungszeichen) legt die Formatierungsart und/oder den Typ der Ausgabe fest und schließt eine Formatangabe ab Die wichtigsten Konvertierungszeichen sind : b d o x c e f g oder B oder X oder C oder E oder G s oder S n logischer Wert ganzzahliger Wert in Dezimaldarstellung ganzzahliger Wert in Oktaldarstellung ganzzahliger Wert in Sedezimaldarstellung Unicode-Zeichen Gleitpunkt-Wert in Exponentialdarstellung Gleitpunkt-Wert in Dezimalbruchdarstellung Gleitpunkt-Wert in Dezimalbruchdarstellung oder Exponentialdarstellung (abhängig von Wert und Genauigkeit) String (der String, der durch die jeweilige Methode toString() erzeugt wird) Zeilenendezeichen Bei einem Großbuchstaben als Konvertierungszeichen : Ausgabe aller Buchstaben als Großbuchstaben ▻ flags (Steuerflags) modifizieren das Ausgabeformat, sie können gegebenenfalls miteinander kombiniert werden Die wichtigsten Steuerflags sind : + 0 ' ' (Blank) linksbündige Ausgabe auch positive Werte werden mit Vorzeichen ausgegeben (nur für Zahlen anwendbar) Ausgabe führender Nullen (nur für Zahlen anwendbar) Ausgabe positiver Werte mit führendem Blank (nur für Zahlen anwendbar) ▻ width (Ausgabefeldbreite) legt die minimale Ausgabefeldbreite fest ▻ precision (Genauigkeit) legt i.a. die maximale Ausgabefeldbreite fest, bei Gleitpunktzahlen jedoch die Anzahl der Nachpunktstellen (Ausnahme : beim Konvertierungszeichen g bzw G wird die Gesamtzahl der Ziffern festgelegt), bei Strings : Anzahl der auszugebenden Zeichen des Strings HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/02/4 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Standard-Ein- und Ausgabe in Java (4) • Formatierte Ausgabe (ab dem JDK 5.0), Forts. ◇ Nichtzulässige Zeichen in einer Formatangabe sowie fehlende oder zu einer Formatangabe nichtkompatible Argumente führen zum Werfen einer IllegalFormatException. • Demonstrationsprogramm zur formatierten Ausgabe : // FormOutpDemo.java // Demonstrationsprogramm zur formatierten Ausgabe public class FormOutpDemo { void show() { int anz = 10; double summe = 99.98765; float anteil = 0.00005432f; String type = "Airbus A-380"; System.out.println("0123456789012345678901234567890123456789"); System.out.printf("Anzahl : %5d Summe : %8.3e\n", anz, summe); System.out.printf("Anzahl : %-5d Summe : %8.3f\n", anz, summe); System.out.printf("Anzahl : %0+5d Summe : %08.3f\n", anz, summe); System.out.printf("Anteil : % .4g\n", anteil); System.out.printf("Anteil : %.4E\n", anteil); System.out.printf("Anteil : %.4f\n", anteil); System.out.printf("Typ : %s\n", type); System.out.printf("Typ : %15s\n", type); System.out.printf("Typ : %5s\n", type); System.out.printf("Typ : %5.6S\n", type); System.out.printf("Typ : %8.6s\n", type); System.out.printf("%B\n", true); } public static void main(String[] args) { FormOutpDemo demo = new FormOutpDemo(); demo.show(); } } Ausgabe des Programms : 0123456789012345678901234567890123456789 Anzahl : 10 Summe : 9.999e+01 Anzahl : 10 Summe : 99,988 Anzahl : +0010 Summe : 0099,988 Anteil : 5.432e-05 Anteil : 5.4320E-05 Anteil : 0,0001 Typ : Airbus A-380 Typ : Airbus A-380 Typ : Airbus A-380 Typ : AIRBUS Typ : Airbus TRUE • Ergänzende Anmerkung zur formatierten Ausgabe ◇ Zur Erzeugung von formatierten Ausgaben kann auch die Klasse Formatter eingesetzt werden (s. API-Dokumentation) HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/02/5 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Standard-Ein- und Ausgabe in Java (5) • Lesen aus der Standard-Eingabe ◇ Das durch system.in referierte Stream-Objekt für die Standard-Eingabe steht als Instanz der Klasse InputStream zur Verfügung. Diese Klasse besitzt zum Einlesen lediglich die mehrfach überladene Methode read(), mit der nur einzelne Bytes oder eine Gruppe von Bytes (Byte-Array) eingelesen werden können. public int read() throws IOEception ▻ Einlesen eines Bytes ▻ Rückgabewert : gelesenes Byte zu int ergänzt, bzw -1 falls Eingabeende erreicht ist public int read(byte[] b) throws IOEception ▻ Einlesen einer Bytefolge in das als Parameter übergebene Array b Es werden maximal b.length Bytes gelesen ▻ Rückgabewert : Anzahl der gelesenen Bytes, bzw -1 falls Eingabeende erreicht ist ◇ Sofern die über die Standard-Eingabe (Tastatur) eingegebenen Zeichen jeweils nur aus einem Byte bestehen – was bei dem bei uns üblichen Zeichensatz i.a. der Fall ist – können mit diesen Methoden einzelne Zeichen eingelesen werden. ◇ Üblicherweise soll von der Standard-Eingabe aber aus mehreren Zeichen bestehender Text eingelesen werden, der vom Programm entweder als Wert eines einfachen Datentyps oder direkt als String interpretiert und verwendet werden soll. D.h. also, es müssen Zeichenfolgen (Datentyp char) und nicht Bytefolgen (Datentyp Byte) eingelesen und in interne Wertdarstellungen umgewandelt werden. ◇ Um dies effektiv zu realisieren, muß das Objekt System.in mit Objekten anderer Klassen zusammenarbeiten. Hierfür existieren mehrere Möglichkeiten. Die einfachste – allerdings erst ab dem JDK 5.0 vorhandene – Möglichkeit besteht im Einsatz eines Objekts der Klasse Scanner (Package java.util). • Allgemeines zur Klasse Scanner ◇ Objekte der Klasse Scanner dienen zum Zergliedern und Interpretieren von Zeichenfolgen. ◇ Die von Scanner-Objekten bearbeitbaren Zeichenfolgen können aus unterschiedlichen Eingabe-Quellen – auch aus Bytestreams – stammen. (Dateien, sonstige Eingabe-Streams wie z.B. die Standard-Eingabe, Strings). Das jeweilige Quell-Objekt muss beim Erzeugen eines Scanner-Objekts festgelegt werden (Æ Konstruktor-Parameter). ◇ Scanner-Objekte zerlegen ihre Eingabe-Zeichenfolge in Teil-Abschnitte (token). Die Zeichen(-muster), die alsTrennzeichen zwischen den Abschnitten interpretiert werden (delimiter pattern), lassen sich konfigurieren. Defaultmässig werden Whitespace-Character verwendet. ◇ Die einzelnen Teil-Abschnitte stehen als Strings zur Verfügung und können als Werte eines einfachen Datentyps interpretiert und in die dem jeweiligen Typ entsprechende interne Darstellung umgewandelt werden. Damit lassen sich Scanner-Objekte sehr elegant zum Einlesen von Werten der einfachen Datentypen aus der Standard-Eingabe einsetzen, insbesondere auch dann, wenn in einer Eingabezeile mehrere einzulesende Werte enthalten sind. ◇ Scanner-Objekte ermöglichen auch das zeilenweise Lesen. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/02/6 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Standard-Ein- und Ausgabe in Java (6) • Konstruktoren der Klasse Scanner (Auswahl) public Scanner(InputStream source) Erzeugung eines Scanner-Objekts, dessen Eingabe-Quelle das durch source spezifizierte InputStream-Objekt ist public Scanner(File source) Erzeugung eines Scanner-Objekts, dessen Eingabe-Quelle die durch source spezifizierte Datei ist public Scanner(String source) Erzeugung eines Scanner-Objekts, dessen Eingabe-Quelle der durch source spezifizierte String ist. • Erzeugung eines Scanner-Objekts für die Standard-Eingabe Scanner stdinscan = new Scanner(System.in); • Memberfunktionen der Klasse Scanner (Auswahl) public String nextLine() Rückgabe des Rests der aktuellen Eingabe-Zeile Wenn die Bearbeitungsposition sich unmittelbar vor dem Anfang der nächsten Zeile befindet, wird die gesamte nächste Zeile zurückgegeben public boolean hasNextLine() Überprüfung, ob eine weitere Eingabe-Zeile vorhanden ist Rückgabe von true wenn ja, sonst von false public String next() Rückgabe des nächsten Teil-Abschnitts (token) der Eingabe-Zeichenfolge public boolean hasNext() Überprüfung ob ein weiterer Teil-Abschnitt vorhanden ist Rückgabe von true wenn ja, sonst von false public boolean hasNextByte() public boolean hasNextShort() public boolean hasNextInt() public boolean hasNextLong() Überprüfung, ob der nächste Teilabschnitt sich als Wert des jeweiligen Typs interpretieren lässt. (boolean-Werten können mit Groß- und/oder Kleinbuchstaben dargestellt werden) Rückgabe von true wenn ja, sonst von false Die Methoden geben auch false zurück, wenn kein weiterer Teil-Abschnitt mehr vorhanden ist (d.h. das Eingabe-Ende erreicht ist) public boolean hasNextFloat() public boolean hasNextDouble() Achtung : Der Dezimal-Punkt bei Float- und Double-Werten muß bei der Eingabe durch ein Dezimal-Komma ersetzt werden public boolean hasNextBoolean() public byte nextByte() public short nextShort() public int nextInt() public long nextLong() public float nextFloat() public double nextDouble() public boolean nextBoolean() Interpretation des nächsten Teil-Abschnitts der Eingabe-Zeichenfolge als Wert des jeweiligen Typs und Rückgabe der internen Darstellung dieses Wertes Jede der Funktionen wirft eine Exception vom Typ InputMismatchException, wenn der Teil-Abschnitt sich nicht als Wert des jeweiligen Typs interpretieren lässt (boolean-Werte können mit Groß- und/oder Kleinbuchstaben dargestellt werden) HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/02/7 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Standard-Ein- und Ausgabe in Java (7) • Demonstrationsbeispiele zur Klasse Scanner ◇ Programm EchoLinesScanDemo // EchoLinesScanDemo.java // Echo der von der Standard-Eingabe eingelesenen Zeilen in die Standard-Ausgabe // Verwendung der Klase Scanner import java.util.*; public class EchoLinesScanDemo { public static void main(String[] args) { Scanner stdinscan = new Scanner(System.in); while (stdinscan.hasNextLine()) System.out.println(stdinscan.nextLine()); } } ◇ Programm StdInpScanDemo // StdInpScanDemo.java // Demonstrationsprogramm zum Einlesen von der Standard-Eingabe // unter Verwendung der Klasse Scanner import java.util.*; public class StdInpScanDemo { public static void main(String[] args) { int a, b; Scanner stdinscan = new Scanner(System.in); System.out.print("Zwei Integer-Werte a und b ? "); a = stdinscan.nextInt(); b = stdinscan.nextInt(); System.out.println("a+b = " + (a + b)); System.out.print("Boolean-Wert ? "); boolean bw = stdinscan.nextBoolean(); System.out.println("Eingabe war : " + bw); System.out.print("Double-Wert ? "); if (stdinscan.hasNextDouble()) { double d = stdinscan.nextDouble(); System.out.println("Double-Wert : " + d); } else System.out.printf("keine oder falsche Eingabe"); } ◇ Beispiel für die Ein- und Ausgabe des Programms StdInpScanDemo Zwei Integer-Werte a und b ? 12 25 a+b = 37 Boolean-Wert ? FalsE Eingabe war : false Double-Wert ? 4,5 Double-Wert : 4.5 HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/03/1 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Exceptions in Java (1) • Prinzip des Exception Handlings ◇ Java stellt – wie auch z.B. C++ – einen speziellen Mechanismus zur Behandlung von Ausnahmesituationen (Fehlerfällen, Exceptions), die während der Programmausführung auftreten können, zur Verfügung ⇒ Exception Handling ◇ Ausnahmesituationen in diesem Sinne sind Fehler oder sonstige unerwünschte Sonderfälle (z.B. Dateizugriffsfehler, Verletzung von Array-Grenzen, Datenformatfehler usw), die im normalen Programmablauf nicht auftreten sollten, aber auftreten können. Exception Handling ist kein Mechanismus zur Behandlung von externen oder internen Interrupts. ◇ Exception Handling trennt den Code, der Fehler verursachen kann, von dem Code, der einen aufgetretenen Fehler behandelt. Dadurch kann der eigentliche den normalen Programmablauf steuernde (produktive) Code frei gehalten werden von einer ständigen Überprüfung auf den Auftritt von Fehlern und Sonderfällen sowie der Reaktion auf diese. Dies trägt erheblich zur Klarheit und Übersichtlichkeit des Codes bei. ◇ Code (z.B. eine Methode einer Bibliotheksklasse), der eine Ausnahmesituation entdeckt, wirft eine Exception, die dann von einem anderen Code-Teil, dem Exception-Handler gefangen wird. Im Exception-Handler findet die Reaktion auf die (Bearbeitung der) Ausnahmesituation statt. Diese kann der Fehlerart sowie der jeweiligen spezifischen Programmsituation angepasst werden. Z.B. kann eine Fehlermeldung ausgegeben werden und anschliessend das Programm fortgesetzt oder beendet werden. Oder es kann versucht werden, die Fehlerursache zu beseitigen und das Programm dann fortzusetzen. ◇ Exceptions werden als Objekte behandelt. Sie enthalten Informationen über die Fehlerursache und stellen Methoden zum Zugriff zu diesen zur Verfügung. Werfen einer Exception (throwing) bedeutet das Erzeugen eines Objekts einer Exception-Klasse und die anschliessende Suche eines passenden Exception-Handlers. Wird ein derartiger Handler gefunden, wird ihm das Exception-Objekt (genauer eine Referenz darauf) übergeben (Æ Fangen der Exception, catching). Dem Handler stehen damit die im Exception-Objekt enthaltenen Informationen zur Auswertung zur Verfügung. ◇ Exception Handling wird in Java sehr umfangreich und strikt angewendet : ▻ In der Standard-Bibliothek sind zahlreiche Exception-Klassen definiert. Sie bilden eine über mehrere Pakete verteilte Klassenhierarchie, die die Klasse Throwable (von Object abgeleitet, Paket java.lang) als Wurzelklasse besitzt. Im Konstruktor einer derartigen Klasse wird i.a. die Exception-Ursache bzw eine Kurzbeschreibung der Exception durch einen String (fest codiert oder als Parameter übergeben) festgelegt. ▻ Methoden vieler Bibliotheksklassen werfen Exceptions. ▻ Java erzwingt, dass geworfene Exceptions gefangen werden müssen. Existiert in einem Benutzerprogramm kein geeigneter Exception-Handler, so werden sie von einem in der JVM angesiedelten Standard-Handler behandelt, der Informationen über die Exception in die Standard-Ausgabe ausgibt und anschliessend das Programm (genauer den aktuellen Thread) beendet. ▻ Ein Benutzer kann jederzeit eigene Exception-Klassen definieren. Dies kann sinnvoll sein, wenn spezielle Fehlerfälle gesondert von anderen behandelt werden sollen (z.B. "Division durch Null" als Spezialfall einer ArithmeticException). Eine beutzerdefinierte Exception-Klasse muß von einer der Standard-Bibliotheks-Exception-Klassen abgeleitet sein. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/03/2 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Exceptions in Java (2) • Hierarchie der Bibliotheks-Exception-Klassen ◇ Alle Exception-Klassen sind – direkt oder indirekt – von der Klasse Throwable abgeleitet. ◇ Die Klasse Throwable (Paket java.lang) ist eine direkte Unterklasse von Object. ◇ Überblick über den Anfang der Hierarchie : Throwable Error VirtualMachineError weitere Error-Klassen ArithmeticException Exception RuntimeException IOException weitere RuntimeExceptionKlassen FileNotFoundException weitere ExceptionKlassen weitere IOExceptionKlassen ◇ Exceptions der Klasse Error sowie der davon abgeleiteten Klassen kennzeichnen ernsthafte Systemfehler, die normalerweise nicht auftreten sollten, aber prinzipiell jederzeit auftreten können. Ein normales Benutzerprogramm kann auf derartige Exceptions i.a. nicht sinnvoll reagieren und sollte sie deswegen auch nicht fangen. Vielmehr sollten sie immer vom Default-Exception-Handler der JVM behandelt werden. ◇ Exceptions der Klasse RuntimeException sowie der davon abgeleiteten Klassen können ebenfalls prinzipiell an beliebiger Stelle während des Programmablaufs auftreten. I.a. liegt ihre Ursache in einem logischen Programmierfehler (z.B. Division durch 0 oder unzulässiger Array-Index). Eine vernünftige Reaktion zur Laufzeit ist i.a. nicht möglich. Derartige Fehler sollten i.a. im Programm-Code korrigiert werden. Ein Benutzerprogramm kann diese Exceptions behandeln (d.h. geeignete Exception-Handler bereitstellen), muß es aber nicht. Im letzteren Fall erfolgt wiederum eine Behandlung durch den Default-Handler der JVM. ◇ Exceptions aller übrigen Klassen (z.B. IOException) repräsentieren Fehler, mit denen man prinzipiell rechnen muß (z.B. Datei ist nicht vorhanden) und die deswegen auch in irgendeiner Weise behandelt werden müssen. Für diese Exceptions schreibt Java eine "catch-or-declare"-Erfordernis vor. Sie müssen von der Funktion, in der sie geworfen werden, entweder gefangen oder von ihr als weiter-werfbar deklariert werden. Andererseits darf eine Funktion nur solche Exceptions dieser Klassen werfen, die sie auch deklariert hat. Das Einhalten dieser Erfordernis kann vom Compiler überprüft werden. Sie werden daher als geprüfte (checked) Exceptions bezeichnet. ◇ Exceptions der Klassen Error und RuntimeException sowie der davon abgeleiteten Klassen sind dagegen ungeprüfte (unchecked) Exceptions. Sie können jederzeit geworfen werden, ohne dass sie von der entsprechenden Funktion deklariert werden müssen. Für sie gilt die "catch-or-declare"-Erfordernis nicht, sie dürfen aber deklariert werden HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/03/3 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Exceptions in Java (3) • Die Klasse Throwable ◇ Die direkt von der Klasse Object abgeleitete Klasse Throwable (Paket java.lang) ist Basisklasse aller Exception-Klassen. ◇ Sie definiert ein allgemeines Interface für den Zugriff zu den Exception-Objekten aller Exception-Klassen Die vier wichtigsten Methoden dieses Interfaces sind : public String getMessage() ▻ Diese Methode gibt den bei der Exception-Objekt-Erzeugung gespeicherten Ursachen- bzw KurzbeschreibungsString als Funktionswert zurück, bzw null, falls kein derartiger String gespeichert wurde. public String toString() ▻ Diese Methode erzeugt einen String, der aus dem vollqualifizierten Namen der tatsächlichen Klasse des ExceptionObjekts besteht, gegebenenfalls (falls getMessage() einen Wert !=null zurückgibt) gefolgt von ": " und dem Rückgabewert von getMessage(). public void printStackTrace() ▻ Diese Methode gibt den Funktionswert von toString() gefolgt von dem Aufruf-Stack der Funktion, in der die Exception ursprünglich geworfen wurde, in die Standard-Fehler-Ausgabe (System.err) aus. public void printStackTrace(PrintStream s) ▻ Diese Methode gibt den Funktionswert von toString() gefolgt von dem Aufruf-Stack der Funktion, in der die Exception ursprünglich geworfen wurde, in den durch s referierten Print-Stream aus. • Deklaration werfbarer Exceptions (throws-Klausel) ◇ Die von einer Funktion werfbaren geprüften Exceptions müssen von dieser deklariert werden. Werfbare ungeprüfte Exceptions können, müssen aber nicht deklariert werden. ◇ Die Deklaration erfolgt durch eine im Funktionskopf nach der Parameterliste anzugebende throwsKlausel. Die throws-Klausel besteht aus dem Schlüsselwort throws, gefolgt von einer durch Kommata getrennten Auflistung von Exception-Klassen ◇ Syntax : throws Exception-Klasse , ◇ Beispiel : public String liesEtwas(String dname) throws FileNotFoundException, EOFException { // ... } • Werfen einer Exception – throw-Anweisung throw Exception-Objekt-Referenz ; ◇ Nach dem Schlüsselwort throw ist eine Referenz auf das zu werfende Exception-Objekt anzugeben. Diese Referenz kann ▻ der Wert eines new-Ausdrucks sein (neu erzeugtes Exception-Objekt) ▻ die einem Exception-Handler übergebene Objekt-Referenz sein ("Weiterwerfen" der Exception) ◇ Beispiel : throw new ArithmeticException("Nenner ist Null"); HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/03/4 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Exceptions in Java (4) • Die try-Anweisung ◇ Sie ermöglicht das Überwachen und das Fangen von Exceptions. ◇ Eine try-Anweisung besteht i.a. aus drei Teilen : ▻ dem try-Block ▻ einem oder mehreren catch-Blöcken ▻ einem finally-Block Entweder die catch-Blöcke oder der finally-Block dürfen auch fehlen, aber nicht beides. ◇ Syntax : try { catch ( finally Anweisung Exception-Klasse } Name { Anweisung } { Anweisung } ) ◇ Der try-Block enthält den eigentlichen produktiven Code, der auf den Auftritt von Exceptions überwacht wird. Er wird solange ausgeführt, bis entweder durch eine der Anweisungen eine Exception (direkt durch eine throw-Anweisung oder indirekt durch eine aufgerufene Funktion) geworfen wird oder alle Anweisungen erfolgreich abgearbeitet worden sind. Beim Auftritt einer Exception wird der try-Block sofort verlassen und nach einem Exception-Handler gesucht, der die Exception fangen kann. ◇ Die catch-Blöcke bilden die Exception-Handler. Jeder catch-Block ist zuständig für das Fangen von Exceptions einer bestimmten Klasse und deren Unterklassen. Die von einem catch-Block fangbare Exception-Klasse ist in seinem Kopf – analog zur Parameter-Deklaration von Funktionen – angegeben. Da Objekte (genauer Objekt-Referenzen) einer Klasse zuweisungskompatibel zu Variablen einer Basisklasse sind, können auch Exceptions aller Klassen, die von der deklarierten Klasse abgeleitet sind, gefangen werden. Die Suche nach einem "passenden" Exception-Handler erfolgt in der Reihenfolge seiner Angabe im Quellcode. Wird ein Exception-Handler gefunden, der die geworfene Exception fangen kann, wird das Programm mit dessen Abarbeitung fortgesetzt. Dabei wird die Exception wie ein formaler Funktionsparameter an den Handler übergeben. Wird kein "passender" Exception-Handler gefunden, wird die Suche in der jeweils nächsten umfassenden try-Anweisung (gegebenenfalls in der aufrufenden Funktion) fortgesetzt. Bleibt auch das erfolglos, wird der Default-Exception-Handler der JVM ausgeführt und das Programm beendet. ◇ Der finally-Block enthält Code, der immer ausgeführt wird, unabhängig davon, wie die try-Anweisung verlassen wird, ob durch reguläre Beendigung des try-Blocks oder durch Werfen einer gefangenen oder nicht gefangenen Exception. Die Ausführung des finally-Blocks erfolgt unmittelbar vor Verlassen des try-Blocks bzw – beim Fangen einer Exception – vor Verlassen des entsprechenden catch-Blocks. Die Ausführung erfolgt auch dann, wenn try- bzw catch-Block mit einer return-Anweisung verlassen werden. Der finally-Block wird insbesondere verwendet, um Aufräumarbeiten durchzuführen und sicherzustellen, dass belegte System-Resourcen (z.B. geöffnete Dateien) freigegeben werden HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/03/5 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Exceptions in Java (5) • Demonstrationsprogramm zu Exceptions // ExceptDemo.java import java.io.*; class ExceptDemo { public void func1() { try { System.out.println("in func1() : Aufruf von func2()"); func2(); System.out.println("normales Ende von try in func1()!"); return; } finally { System.out.println("func1() verlassen !"); } } public void func2() { try { System.out.println("in func2() : Aufruf von func3()"); func3(); System.out.println("normales Ende von try in func2()!"); return; } catch (RuntimeException e) { System.out.print("Exception-Handler in func2() : "); System.out.println(e.getMessage()); } finally { System.out.println("func2() verlassen !"); } } public void func3() { try { System.out.println("in func3() : Exception wird geworfen"); throw new RuntimeException("Demo-Exception !"); } catch (ArithmeticException e) { System.out.print("Exception-Handler in func3() : "); System.out.println(e.getMessage()); } finally { System.out.println("func3() verlassen !"); } } public static void main(String[] args) { ExceptDemo ed = new ExceptDemo(); ed.func1(); } } in func1() : Aufruf von func2() in func2() : Aufruf von func3() in func3() : Exception wird geworfen func3() verlassen ! Exception-Handler in func2() : Demo-Exception ! func2() verlassen ! normales Ende von try in func1()! func1() verlassen ! HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/04/1 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Dateizugriff in Java (1) • Überblick ◇ Java behandelt Dateien als sequentielle Folge von Bytes (Byte-Streams). ◇ In der IO-Bibliothek (Package java.io) sind zahlreiche Klassen definiert, die das Arbeiten mit Dateien unterstützen. Die wichtigsten dieser Klassen sind : ▻ die File-Stream-Klassen zum sequentiellen Zugriff • FileInputStream (byteweises Lesen) • FileOutputStream (byteweises Schreiben) • FileReader (zeichenweises Lesen) • FileWriter (zeichenweises Schreiben) ▻ eine Klasse zum wahlfreien Zugriff (Lesen und Schreiben) • RandomAccessFile ▻ Klassen für den Zugriff zum Dateisystem • FileDescriptor (Handle zur Datei-Verwaltungsstruktur des Betriebssystems, hier nicht betrachtet) • File ▻ Zahlreiche weitere Stream-Klassen (meist Filter-Klassen) ermöglichen einen spezialisierten Dateizugriff ◇ Auszugsweises Klassendiagramm : Anmerkungen : ▻ Das Diagramm beschränkt sich auf die Datei-Eingabe-Stream-Klassen (FileInputStream, FileReader). ▻ Für die Datei-Ausgabe-Stream-Klassen (FileOutputStream, FileWriter) bestehen analoge Ableitungs-und Nutzungsbeziehungen. Deren – direkte bzw indirekte – Basisklassen sind OutputStream bzw Writer. ▻ Die IO-Bibliothek enthält eine Reihe weiterer – im Diagramm nicht aufgeführter – Stream-Klassen, die zumeist von InputStream oder Reader (bzw OutputStream oder Writer) direkt oder indirekt abgeleitet sind. InputStream Reader FilterInputStream BufferedInputStream DataInputStream InputStreamReader FileInputStream FileReader FileDescriptor File BufferedReader RandomAccessFile HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/04/2 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Dateizugriff in Java (2) • Die Klasse File ◇ Objekte dieser Klasse repräsentieren Zugriffspfade (zu Dateien bzw Directories) im Dateisystem. ◇ Wesentliche Funktionalitäten : ▻ Speicherung eines Datei- bzw Directory-Zugriffspfades in einer abstrakten system-unabhängigen Darstellung, sowie Bereitstellung von Informationen über denselben ▻ Ermittlung diverser Eigenschaften einer repräsentierten Datei (bzw eines repräsentierten Directories) ▻ Manipulation von Dateisystem-Einträgen (Erzeugen, Löschen, Änderung bestimmter Eigenschaften) ◇ Der durch ein File-Objekt repräsentierte Zugriffspfad muß nicht einen tatsächlich existierenden Dateisystem-Eintrag referieren. ◇ Konstruktoren der Klasse File (Auswahl) : public File(String path) ▻ Erzeugung eines neuen File-Objekts, das den Zugriffspfad path repräsentiert. ▻ Falls path==null ist, wird eine NullPointerException geworfen. public File(String dirName, String name) ▻ Erzeugung eines neuen File-Objekts, das den aus dirName und name gebildeten Zugriffspfad repräsentiert. (Eintrag name im Directory dirName). ▻ Äquivalent zu : File(dirName + File.separator + name) ▻ Falls name==null ist, wird eine NullPointerException geworfen. public File(File fileDir, String name) ▻ Erzeugung eines neuen File-Objekts, das den Zugriffspfad repräsentiert, der aus dem Eintrag name in dem durch fileDir repräsentierten Directory gebildet wird, ▻ Äquivalent zu : File(fileDir.getPath(), name) ▻ Falls name==null ist, wird eine NullPointerException geworfen. ◇ Methoden zum Erzeugen neuer Dateisystem-Einträge (Auswahl) public boolean createNewFile() throws IOException ▻ Diese Methode erzeugt eine neue leere Datei mit dem vom aktuellen File-Objekt repräsentierten Zugriffspfad, falls noch keine Datei mit diesem Zugriffspfad existiert. ▻ Funktionswert : true, falls die Datei erzeugt werden konnte false,falls eine Datei mit dem Zugriffspfad bereits existiert hat. ▻ Falls ein I/O-Fehler auftritt, wird eine IOException geworfen. public boolean mkdir() ▻ Diese Methode erzeugt ein neues Directory mit dem vom aktuellen File-Objekt repräsentierten Zugriffspfad ▻ Funktionswert : true, falls das Directory erzeugt werden konnte, andernfalls false HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/04/3 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Dateizugriff in Java (3) • Die Klasse File, Forts ◇ Methoden zur Veränderung von Dateisystem-Einträgen public boolean renameTo(File newPath) ▻ Umbenennen / Verschieben des aktuell repräsentierten Dateisystem-Eintrags. ▻ Der Dateisystem-Eintrag wird anschliessend durch das File-Objekt newPath repräsentiert. Der vom aktuellen Objekt bisher repräsentierte Eintrag existiert anschliessend nicht mehr. ▻ Funktionswert : true, falls Umbenennen / Verschieben erfolgreich war, andernfalls false ▻ Falls newPath==null ist, wird eine NullPointerException geworfen. public boolean delete() ▻ Löschen des aktuell repräsentierten Dateisystem-Eintrags. Ein zu löschendes Directory muss leer sein. ▻ Funktionswert : true, falls erfolgreich gelöscht werden konnte, andernfalls false ◇ Methoden zur Ermittlung von Informationen über Dateisystem-Einträge (Auswahl) public boolean exists() ▻ Überprüfung, ob durch das File-Objekt ein tatsächlich existierender Eintrag repräsentiert wird ▻ Funktionswert : true, wenn der Eintrag existiert, sonst false public boolean isDirectory() ▻ Funktionswert : true, wenn der Eintrag existiert und ein Directory ist, sonst false public boolean isFile() ▻ Funktionswert : true, wenn der Eintrag existiert und eine normale Datei ist, sonst false public boolean canWrite() ▻ Funktionswert : true, wenn Eintrag existiert und geändert werden kann, sonst false public boolean canRead() ▻ Funktionswert : true, wenn Eintrag existiert und gelesen werden kann, sonst false public String[] list() ▻ Funktionswert : Wenn Eintrag ein Directory ist : Rückgabe einer Auflistung aller in ihm enthaltenen Einträge als String-Array (ausser "." und "..") Wenn Eintrag kein Directory ist : null public long length() ▻ Funktionswert : Dateilänge, wenn Eintrag Datei ist, bzw. undefiniert, wenn Eintrag Directory ist public String getPath() public String getAbsolutePath() ▻ Funktionswert : Zugriffspfad des repräsentierten Dateisystem-Eintrags (wie im Konstruktor angegeben) bzw absoluter Zugriffspfad (gegebenenfalls mittels aktuellem Arbeitsdir. ermittelt) HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/04/4 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Dateizugriff in Java (4) • Demoprogramm zur Klasse File // ListDir.java import java.io.*; public class ListDir { public static void main(String[] args) { try { if (args.length<=0) throw new IOException("Programmparameter (Directory-Pfad) fehlt !"); File dir = new File(args[0]); if (!dir.exists()) throw new IOException("\"" + args[0] + "\" existiert nicht !"); if (!dir.isDirectory()) throw new IOException("\"" + dir.getPath() + "\" ist kein Directory !"); String[] entries = dir.list(); System.out.println("Inhalt des Directories \"" + dir.getAbsolutePath() + "\" :"); for (int i=0; i<entries.length; i++) System.out.println(entries[i]); } catch (IOException ex) { System.out.println(ex.getMessage()); } } } Beispiele für Programmaufruf und -ausgabe E:\Java\fhm\ee\vorl\fileacc>java ListDir Programmparameter (Directory-Pfad) fehlt ! E:\Java\fhm\ee\vorl\fileacc>java ListDir meindir "meindir" existiert nicht ! E:\Java\fhm\ee\vorl\fileacc>java ListDir ftestdir "ftestdir" ist kein Directory ! Inhalt des Directories "E:\Java\Vorl_Prog\elemprogfunc\." : FormOutpDemo.java Echo.java StdOutDemo1.java EchoLinesScanDemo.java StdInpScanDemo.java package cache StdOutDemo1.class FormOutpDemo.class Echo.class formoutpdemo.txt EchoLinesScanDemo.class StdInpScanDemo.class ExceptDemo.java ExceptDemo.class StdInpCharDemo.class ListDir.java ftestdir ListDir.class HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/04/5 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Dateizugriff in Java (5) • Sequentieller Dateizugriff mittels der File-Stream-Klassen ◇ Klassen für den byteweisen sequentiellen Dateizugriff : ▻ FileInputStream (byteweises Lesen) ▻ FileOutputStream (byteweises Schreiben) ◇ Klassen für den zeichenweisen sequentiellen Dateizugriff : ▻ FileReader (zeichenweises Lesen) ▻ FileWriter (zeichenweises Schreiben) ◇ Überblick über die Konstruktoren (Auswahl) : 1.Parameter 2. Parameter String path Dateizugriffspfad ------File fil repräsentiert Dateizugriffspfad String path Dateizugriffspfad File fil repräsentiert Dateizugriffspfad boolean app wenn true : Anhängen an Datei (append) wenn false : Schreiben am Dateianfang existiert für Klasse FileInputStream FileOutputStream FileReader FileWriter FileOutputStream FileWriter Beispiele : public FileInputStream(File fil) throws FileNotFoundException public FileWriter(String path, boolean app) throws IOException ● Bei der Erzeugung eines File-Stream-Objekts wird die referierte Datei – falls sie vorhanden und die jeweilige Zugriffsart zulässig ist – geöffnet ● Die Konstruktoren der Datei-Ausgabe-Stream-Klassen versuchen die referierte Datei neu anzulegen, falls sie nicht existiert. ● Kann eine Datei nicht geöffnet werden (z.B. weil der Zugriffspfad ein Directory referiert), wird eine FileNotFoundException (bzw bei der Klasse FileWriter eine IOException) geworfen. ◇ Schliessen eines File-Streams mittels der für alle File-Stream-Klassen existierenden Methode : public void close() throws IOException ▻ Die Methode schliesst den File-Stream und damit die referierte Datei und gibt alle sonstigen mit dem Stream assoziierten System-Resourcen frei. ▻ Beim Auftritt eines I/O-Fehlers wird eine IOException geworfen. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/04/6 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Dateizugriff in Java (6) • Sequentieller Dateizugriff mittels der File-Stream-Klassen, Forts. ◇ Methoden zum Dateizugriff (Auswahl) ● Alle folgenden Methoden werfen eine IOException falls ein I/O-Fehler auftritt ● Schreiben in Dateien (Klassen FileOutputStream / FileWriter) public void write(int bc) throws IOException ▻ Schreiben des Bytes bc (FileOutputStream) bzw des Zeichens bc (FileWriter) public void write(byte[] buff, int pos, int len) throws IOException public void write(char[] buff, int pos, int len) throws IOException ▻ Schreiben von len Bytes (FileOutputStream) bzw von len Zeichen (FileWriter) aus buff ab Position pos public void write(String str) throws IOException // nur FileWriter ▻ Schreiben des Strings str public void flush() throws IOException ▻ Herausschreiben aller Schreib-Buffer (Übergabe an das Betriebssystem) ● Lesen aus Dateien (Klassen FileInputStream / FileReader) public int read() throws IOException ▻ Lesen eines Bytes (FileInputStream) bzw eines Zeichens (FileReader) ▻ Funktionswert : gelesenes Byte bzw gelesenes Zeichen bzw -1 falls das Dateiende erreicht ist public int read(byte[] buff) throws IOException public int read(char[] buff) throws IOException ▻ Lesen einer Byte-Folge (FileInputStream) bzw einer Zeichen-Folge (FileReader) ▻ Ablage der gelesenen Byte- bzw Zeichen-Folge im Buffer buff ▻ Funktionswert : Anzahl gelesener Bytes bzw Anzahl gelesener Zeichen bzw -1 falls das Dateiende erreicht ist public long skip(long n) throws IOException ▻ Überlesen der nächsten n Bytes (FileInputStream) bzw der nächsten n Zeichen (FileReader) ● Es existieren noch weitere Methoden, mit denen auch Teile eines Byte- bzw Zeichen-Arrays gelesen bzw Teile eines Strings geschrieben werden können. ◇ Durch die Zusammenarbeit mit geeigneten Filterklassen lassen sich spezialisiertere Formen der DateiEin-/Ausgabe realisieren. Beispiel : Die Zusammenarbeit von FileInputStream mit DataInputStream sowie FileOutputStream mit DataOutputStream ermöglicht das Lesen und Schreiben von Daten in Binärdarstellung. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/04/7 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Dateizugriff in Java (7) • Demoprogramm zum sequentiellen Dateizugriff (Kopieren einer Datei) // FileCopy.java import java.io.*; public class FileCopy { public static void main(String[] args) { if (args.length <2) System.out.println("Aufruf : \"java FileCopy quelldatei zieldatei\""); else { try { File ifile = new File(args[0]); FileInputStream in = new FileInputStream(ifile); FileOutputStream out = new FileOutputStream(args[1]); long fsize = ifile.length(); System.out.print("Kopiert wird \"" + args[0]+ "\" (Laenge : "); System.out.println(fsize + " Bytes) nach \"" + args[1]+ "\""); byte[] buff = new byte[512]; int len; while ((len=in.read(buff))>=0) { out.write(buff, 0, len); } out.close(); in.close(); System.out.println("Kopieren der Dateien erfolgreich"); } catch(IOException ex) { System.out.println("Exception : " + ex.getMessage()); } } } } Beispiel für Programmaufruf und -ausgabe E:\Java\fhm\ee\vorl\fileacc>java FileCopy ListDir.class ListDir.bin Kopiert wird "ListDir.class" (Laenge : 1100 Bytes) nach "ListDir.bin" Kopieren der Dateien erfolgreich HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/04/8 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Dateizugriff in Java (8) • Wahlfreier Zugriff zu Dateien – Klasse RandomAccessFile ◇ Den wahlfreien Zugriff zu Dateien ermöglicht die Klasse RandomAccessFile. Diese Klasse ist von keiner der File-Stream-Klassen sondern direkt von Object abgeleitet. ◇ Durch Objekte dieser Klasse können Dateien für einen wahlfreien Zugriff nur zum Lesen oder zum Lesen und Schreiben geöffnet werden. Der wahlfreie Zugriff wird durch die Möglichkeit zur expliziten Veränderung der aktuellen Bearbeitungsposition (File-Pointer, nicht zu verwechseln mit dem File-Pointer in C, der referiert eine Datei) realisiert. ◇ Die Klasse stellt zahlreiche Methoden bereit, mit denen auch spezialisierte Dateizugriffe möglich sind : ▻ Lesen und Schreiben von Bytes und Bytefolgen (Byte-Arrays) ▻ Schreiben von Strings als Byte-Folge, Zeichenfolge und in UTF-8-Codierung ▻ zeilenweises Lesen (Textdateien !) ▻ Lesen von Strings in modifizierter UTF-8-Codierung ▻ Lesen und Schreiben von Werten der einfachen Datentypen in interner Binärdarstellung (Binärdateien !) ◇ Anmerkung zur modifizierten UTF-8-Codierung : ▻ UTF = Universal Transfer Format : effiziente Codierung von Unicode-Zeichen durch 1, 2 und 3 Bytes ▻ Zeichen zwischen '\u0001' und '\u007F' durch ein Byte : 01 ... 7F Zeichen zwischen '\u0080' und '\u07FF' durch zwei Bytes : C2 80 ... DF BF Zeichen zwischen '\u0800' und '\uFFFF' durch drei Bytes : E0 A0 80 ... EF BF BF der NUL-Character (das Zeichen '\u0000') durch zwei Bytes : C0 80 ▻ Vor der eigentlichen den String codierenden Bytefolge wird deren Länge als short-Wert (2 Bytes) in den Stream geschrieben. ◇ Konstruktoren der Klasse RandomAccessFile public RandomAccessFile(String path, String mode) throws FileNotFoundException public RandomAccessFile(File fil, String mode) throws FileNotFoundException ▻ Der erste Parameter referiert die zu bearbeitende Datei (über den Zugriffspfad bzw ein FileObjekt, das diesen repräsentiert) ▻ Der zweite Parameter legt die Zugriffsart fest : - "r" nur Lesen, - "rw" Lesen und Schreiben. ("rws" und "rwd" sind auch zulässig) ▻ Die Konstruktoren versuchen die referierte Datei für die angegebene Zugriffsart zu öffnen. ▻ Falls die referierte Datei nicht existiert wird bei der Zugriffsart "rw" versucht, sie zu erzeugen. ▻ Die Konstruktoren können die folgenden Exceptions werfen : - FileNotFoundException, falls die referierte Datei nicht existiert (Zugriffsart "r") oder ein Directory ist oder nicht erzeugt werden kann oder aus einem anderen Grund nicht geöffnet werden kann. - SecurityException, falls für die vorgesehene Zugriffsart keine Berechtigung besteht - IllegalArgumentException, falls ein nicht zulässiger String für die Zugriffsart angegeben wird. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/04/9 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Dateizugriff in Java (9) • Wahlfreier Zugriff zu Dateien – Klasse RandomAccessFile, Forts ◇ Methoden der Klasse RandomAccessFile zum Lesen u. Schreiben (Auswahl) : Schreiben void write(int b) void write(byte[] ba) Lesen int read() int read(byte[] ba) Datentyp byte byte-Array Rückgabewert : Anzahl gelesener Bytes void write(byte[] ba, int off, int len) int read(byte[] ba, int off, int len byte-Array (Länge len ab Offset off) Rückgabewert : Anzahl gelesener Bytes void writeBytes(String str) void writeChars(String str) void writeUTF(String str) ----String readUTF() String als Byte-Folge String als Char-Folge String (UTF-8-Cod.) String readLine() String (Zeile) Lesen der nächsten Zeile (Rückgabewert) Bytes werden zu Zeichen ergänzt (höherwertiges Byte == NUL-Byte void void void void void void void void writeBoolean(boolean b) writeByte(int b) writeChar(int c) writeShort(int s) writeInt(int i) writeLong(long l) writeFloat(float f) writeDouble(double d) boolean readBoolean() byte readByte() char readChar() short readShort() int readInt() long readLong() float readFloat() double readDouble() boolean byte char short int long float double Alle Methoden zum Lesen/Schreiben werfen eine IOException, wenn ein I/O-Fehler auftritt. Die meisten Lese-Methoden werfen beim vorzeitigen Erreichen des Stream-Endes eine EOFException Æ alle Methoden sind mit der throws-Klausel throws IOException definiert. ◇ Weitere Methoden der Klasse RandomAccessFile (Auswahl) void seek(long pos) throws IOException ▻ Setzen der Bearbeitungsposition auf pos Bytes nach dem Dateianfang long getFilePointer() throws IOException ▻ Rückgabe der aktuellen Bearbeitungsposition (in Bytes bezogen auf den Dateianfang) int skipBytes(int cnt) throws IOException ▻ Versuch, die Bearbeitungsposition um cnt Bytes weiterzusetzen ▻ Rückgabe der Anzahl Bytes, um die tatsächlich weitergesetzt wurde long length() throws IOException ▻ Rückgabe der Dateilänge void close()throws IOException ▻ Schliessen der Datei und Freigabe aller belegten Resourcen HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/04/10 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Dateizugriff in Java (10) • Demoprogramm zum wahlfreien Dateizugriff // RandAccDemo.java // Zufallsgesteuerte Auswahl eines Spruches aus einer Textdatei mit Spruechen. // Die einzelnen Sprueche sind jeweils durch eine Zeile, // die nur das Zeichen '*' enthaelt, getrennt. import java.io.*; public class RandAccDemo { public static void main(String[] args) { try { RandomAccessFile raf = new RandomAccessFile("sprueche.txt", "r"); long pos = (long)(raf.length()*Math.random()); raf.seek(pos); while((pos!=0) && ((char)raf.read()!='*')) raf.seek(--pos); int ch; if (pos==0) System.out.println(); while (((ch=raf.read())!=-1) && ((char)ch!='*')) { System.out.print((char)ch); } System.out.println(); raf.close(); } catch (IOException ex) { System.out.println(ex); } } } Beispiele für Programmaufruf und -ausgabe E:\Java\fhm\ee\vorl\fileacc>java RandAccDemo Gut gehaengt ist besser als schlecht verheiratet. E:\Java\fhm\ee\vorl\fileacc>java RandAccDemo Es ist nicht genug, dass man redet, man muss auch richtig reden. E:\Java\fhm\ee\vorl\fileacc>java RandAccDemo Ein Tor nur schliesst aus aeusserem Gehaben getrost auf eines Menschen innere Gaben. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 13/04/11 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Dateizugriff in Java (11) • Demoprogramm zum Schreiben von Daten in Binärdarstellung (mittels RandomAccessFile) // BinDataDemo.java // Schreiben von Werten einfacher Datentypen und Strings in interner Binärdarstellung // Lesen als Bytefolgen import java.io.*; public class BinDataDemo { public static String byteToHex(byte b) { String str = Integer.toHexString(b & 0x00FF); if (str.length() == 1) str = '0' + str; return str; } public static void main(String[] args) { if (args.length <1) System.out.println("Aufruf : \"java DataStreamDemo dateipfad\""); else { try { File fil = new File(args[0]); RandomAccessFile raf = new RandomAccessFile(fil, "rw"); raf.writeBoolean(true); raf.writeInt(124); raf.writeDouble(3.14); raf.writeUTF("Hallo Java-Freunde"); raf.writeUTF("\u007F\u0080\u07FF\u0800\uFFFF\u0000"); raf.seek(0); System.out.println("Inhalt der Datei \"" + args[0] + "\" :"); int b; int cnt = 0; while((b=raf.read())!=-1) { cnt++; String hex = byteToHex((byte)b); System.out.print(hex + ' '); if (cnt%25 == 0) System.out.println(); } System.out.println(); raf.close(); } catch(IOException ex) { System.out.println("Exception : " + ex.getMessage()); } } } } Programmaufruf und -ausgabe E:\Java\fhm\ee\vorl\fileacc>java DataStreamDemo demo.dat Inhalt der Datei "demo.dat" : 01 00 00 00 7c 40 09 1e b8 51 eb 85 1f 00 12 48 61 6c 6c 6f 20 4a 61 76 61 2d 46 72 65 75 6e 64 65 00 0d 7f c2 80 df bf e0 a0 80 ef bf bf c0 80 HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/00/0 – TH – 01 ----------------------------------------------------------------------------------------------------------------------------------------------------------- Programmieren (4. Sem.) Kapitelüberblick 13. Ergänzungen zu Klassen - Vererbung 13.1. Umfassende Klassendefinition 13.2. Ableitung und Polymorphie 13.3. Interfaces und ihre Implementierung 13.4. Programmbeispiel : Simpson-Integration HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/01/1 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Umfassende Klassendefinition (1) • Wiederholung ◇ Klassen sind die grundlegenden Programmiereinheiten in Java. Jedes Java-Programm besteht aus einer oder mehreren Klassen. Es existiert kein Code ausserhalb einer Klasse. ◇ I.a. ist jede Klasse in einer eigenen Quelldatei definiert (Ausnahme : eingebettete Klassen). Diese Klassen-Quelldateien bilden die Übersetzungseinheiten (Übersetzungs-Module). Pro Klasse (auch für jede eingebettete Klasse) wird vom Compiler eine eigene Byte-Code-Datei erzeugt. ◇ Klassen definieren den Aufbau und die Funktionalität (das Verhalten) von Objekten. Objekte sind Instanzen von Klassen. Sie bestehen aus Datenkomponenten (Felder, fields), die ihren Zustand beschreiben und besitzen Funktionen (Memberfunktionen, Methoden, methods), die mit diesen Datenkomponenten arbeiten und ihr Verhalten festlegen. Die Datenkomponenten und die (Member-)Funktionen werden in einer Klassendefinition festgelegt und als Klassenkomponenten bezeichnet. ◇ Neben den instanzspezifischen Datenkomponenten (Instanz-Variable) und Memberfunktionen (InstanzMethoden) kann eine Klasse auch klassenspezifische Datenkomponenten (Klassen-Variable) und Memberfunktionen (Klassen-Methoden) besitzen. Derartige statische Klassenkomponenten beschreiben den Zustand und das Verhalten der Klasse. • Klassenkomponenten ◇ Klassenkomponenten (class members) können sein : ▻ Datenkomponenten (Membervariable, Felder, fields) ▻ Funktionskomponenten (Memberfunktionen, Methoden, methods) ▻ Eingebettete Klassen und Interfaces (nested classes and interfaces) (hier nicht weiter betrachtet) ◇ Zusätzlich kann eine Klassendefinition enthalten : ▻ Konstruktoren (Sie werden in Java nicht zu den Memberfunktionen gerechnet) ▻ Initialisierungsblöcke (Code zur Initialisierung von Datenkomponenten) (hier nicht weiter betrachtet) • Syntax der Klassendefinition (vollständig) : class Klassen-Name KlassenModifizierer Typ-ParamDeklaration { KlassenAbleitung Komponentendefinition Konstruktordefinition Initialisierungsblock ; InterfaceImplementierung } HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/01/2 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Umfassende Klassendefinition (2) • Klassen-Modifizierer ◇ Sie legen bestimmte Eigenschaften der Klasse fest ◇ Die wichtigsten Modifizierer sind : ▻ public Die Klasse ist öffentlich zugänglich – sie kann überall verwendet werden. Ohne diesen Modifizierer ist sie nur innerhalb des Packages, in dem sie enthalten ist, verwendbar. ▻ abstract Die Klasse ist unvollständig definiert oder sie wird als unvollständig definiert betrachtet Æ abstrakte Klasse. Eine abstrakte Klasse ist nicht instanzierbar. I.a. besitzt sie eine oder mehrere abstrakte Methoden. Eine abstrakte Methode besitzt keine vollständige Definition. Häufig ist sie lediglich deklariert. Ihre – vollständige – Definition bleibt einer abgeleiteten Klasse überlassen. ▻ final Von der Klasse können keine weiteren Klassen abgeleitet werden. Damit können ihre Methoden niemals überschrieben werden. ◇ Die gleichzeitige Verwendung der Modifizierer public und abstract bzw public und final ist zulässig. Die Modifizierer abstract und final schließen sich gegenseitig aus. • Typ-Param-Deklaration Deklaration von formalen Typ-Parametern (Typ-Variable). Falls vorhanden, wird dadurch eine generische Klasse definiert. Generische Klassen werden hier nicht weiter betrachtet. • Klassen-Ableitung Festlegung der Basisklasse, falls es sich um eine abgeleitete Klasse handelt. Eine direkte Ableitung von der Klasse Object wird nicht explizit angegeben. • Interface-Implementierung Festlegung der von der Klasse direkt implementierten Interfaces • Eingebettete Klassen und Interfaces Als Klassenkomponenten lassen sich auch Klassen und Interfaces definieren. Æ eingebettete Typen. Dies wird gegebenenfalls angewendet, um den logischen Zusammenhang eng zusammenarbeitender Klassen auszudrücken. Es gibt verschiedene Arten eingebetteter Typen Sie werden hier nicht weiter betrachtet • Initialisierungsblöcke Hierbei handelt es sich um Codeblöcke, die ausserhalb jeder Funktion definiert sind. Dieser Code dient – als Alternative und/oder Ergänzung zu Konstruktoren und Feld-Initialisierern – zur Initialisierung von Datenkomponenten. Damit lassen sich – insbesonder auch für statische Datenkomponenten – komplexere Initialisierungsalgorithmen formulieren. Auf Initialisierungsblöcke wird hier nicht weiter eingegangen. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/01/3 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Umfassende Klassendefinition (3) • Ergänzung zur Definition von Datenkomponenten : Konstante in Java ◇ Konstante werden in Java i.a. als final-Klassen-Variable (Datenkomponenten mit den Modifizierern static und final) definiert. Es ist üblich – aber nicht verpflichtend – , Konstanten-Namen (Æ symbolische Konstante ) mit Großbuchstaben zu bilden. ◇ Beispiele aus der Java-Standard-Bibliothek public final class Integer { public static final int MAX_VALUE; public static final int MIN_VALUE; // ... } public final class System { public static final InputStream in; ..// ... } ◇ Außerhalb ihrer definierenden Klasse müssen symbolische Konstante mit ihrem vollqualifizierten Namen verwendet werden : klassenname.KONSTANTENNAME Beispiel : int iVar = Integer.MAX_VALUE; ◇ Anmerkung : Mit dem Schlüsselwort final lassen sich auch lokale Konstante in Funktionen definieren : final int ANZ = 10; Sofern diese nicht bei ihrer Definition initialisiert werden, kann ihnen später einmal ein Wert zugewiesen werden. Jeder Versuch einer weiteren Wertzuweisung führt zu einem Compilerfehler • Ergänzungen zur Definition von Memberfunktionen : ◇ Methoden-Modifizierer : Neben den schon bekannten Modifizierern public, private, protected, static und final existieren noch weitere Methoden-Modifizierer. U.a. sind dies : ▻ abstract Es handelt sich um eine abstrakte Methode. Für eine derartige Methode wird nur die Signatur (Name und Parameter), der Rückgabetyp und gegebenenfalls die throws-Klausel nicht jedoch die Implementierung festgelegt. (Methode wird nur deklariert) Der Methoden-Rumpf wird durch ein ; ersetzt. Abstrakte Methoden sind nur in abstrakten Klassen zulässig. Jede von einer abstrakten Klasse abgeleitete Klasse, die selbst nicht wieder abstrakt ist, muß eine Definition der abstrakten Methoden bereitstellen. ▻ native Dieser Modifizierer erlaubt das Einbinden von Methoden, die in nativen Machinencode vorliegen. Üblicherweise werden derartige Methoden in einer anderen Programmiersprache (z.B. C, C++, FORTRAN, Assembler) formuliert und dann in Maschinencode übersetzt. Eine native-Methode wird in der Klassendefinition nur deklariert (statt Methoden-Rumpf nur ; ). ◇ Überladene Funktionen : In einer Klasse können zwei oder mehr Funktionen mit gleichem Namen, aber unterschiedlicher Parameterliste (Æ unerschiedliche Signatur) definiert werden. Derartige Funktionen werden als überladen bezeichnet. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/02/1 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Ableitung und Polymorphie (1) • Prinzip und Eigenschaften der Klassenableitung ◇ Eine Klasse kann durch Vererbung erweitert (extended, subclassed) werden Æ Definition einer abgeleiteten Klasse. Die abgeleitete Klasse (subclass, extended class) erbt alle Datenkomponenten und Memberfunktionen ihrer Basisklasse (superclass) Sie kann neue Datenkomponenten und Methoden hinzufügen. Æ Ergänzung / Änderung des Verhaltens der Klasse ◇ Die in der abgeleiteten Klasse neu definierten Komponenten können den gleichen Namen wie Komponenten der Basisklasse tragen. In einem derartigen Fall werden die gleichnamigen Komponenten der Basisklasse entweder überschrieben oder überdeckt oder überladen. ◇ Von einer als final vereinbarten Klasse können keine weiteren Klassen abgeleitet werden. ◇ In Java hat jede Klasse, die nicht explizit von einer anderen Klasse abgeleitet ist, die Klasse Object als direkte Basisklasse. ◇ Da in Java nur einfache Vererbung möglich ist, besitzt jede Klasse – außer der Klasse Object – genau eine Basisklasse. ◇ Ein Objekt einer abgeleiteten Klasse kann überall verwendet werden, wo ein Objekt der Basisklasse benötigt wird. Æ Referenzvariable einer Basisklasse können auch auf Objekte abgeleiteter Klassen zeigen. Æ Polymorphie. Æ Abgeleitete Klassen sind zuweisungs-kompatibel zu ihren Basisklassen. ◇ In jedem Objekt einer abgeleiteten Klasse steht mit dem Schlüsselwort super eine Referenz auf das in ihm enthaltene Basisklassen-Teilobjekt zur Verfügung. Mittels der super-Referenz können nur Komponenten der Basisklasse angesprochen sowie BasisklassenKonstruktoren aufgerufen werden. Sie kann nicht – wie this – allein benutzt werden, um das Basisklassen-Teilobjekt insgesamt anzusprechen. • Syntax zur Angabe der Basisklasse in der Klassendefinition (extends-Deklaration) Klassen-Ableitung : extends Basisklassenname • Konstruktoren von abgeleiteten Klassen ◇ Ein Konstruktor kann nur die Datenkomponenten der eigenen Klasse initialisieren. Die Initialisierung der geerbten Datenkomponenten der Basisklasse muß durch einen Konstruktor der Basisklasse erfolgen. ◇ Jeder Aufruf eines Konstruktors muß daher als erstes den Aufruf eines Konstruktors der Basisklasse bewirken. Dies kann u.a. erfolgen durch : ▻ den expliziten Aufruf eines Basisklassen-Konstruktors mittels super(...). Dieser Aufruf muß die erste Anweisung im Konstruktor der abgeleiteten Klasse sein ▻ den impliziten Aufruf des no-arg-Konstruktors der Basisklasse, wenn die erste Anweisung im Konstruktor der abgeleiteten Klasse weder super(...) noch this(...) ist. ◇ Anmerkung : In den Parameter-Ausdrücken eines expliziten Konstruktoraufrufs (mittels super(...)) darf keine Komponente des aktuellen Objekts referiert werden. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/02/2 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Ableitung und Polymorphie (2) • Überschreiben von Methoden (Overriding) Æ Polymorphie ◇ Eine in der abgeleiteten Klasse definierte Methode mit gleichem Namen und gleicher Parameterliste ( == gleicher Signatur) sowie gleichem Rückgabetyp wie in der Basisklasse, überschreibt die Methode der Basisklasse. Hierdurch wird die Basisklassen-Implementierung der Methode für die abgeleitete Klasse durch eine neue Implementierung ersetzt. ◇ Handelt es sich bei der überschriebenen Funktion um eine abstrakte Methode (Deklaration als abstract), so wird sie durch die überschreibende Funktion erst implementiert. ◇ Die Definition einer Methode in der abgeleiteten Klasse, die sich nur im Rückgabetyp von einer Methode gleichen Namens in der Basisklasse unterscheidet, ist ein Fehler. ◇ Es können nur Methoden überschrieben werden, die weder als static noch als private noch als final vereinbart sind. Methoden, die überschrieben werden können, werden als virtuell bezeichnet. Æ In Java ist jede nichtstatische Methode der Basisklasse, die weder private noch final ist, grundsätzlich virtuell. ◇ Beim Aufruf einer derartigen Methode über eine Basisklassen-Referenz wird die in der tatsächlichen Klasse des aktuell referierten Objekts definierte Methode ausgeführt (Æ late binding, Laufzeit-Polymorphie !) ◇ Eine in der abgeleiteten Klasse definierte überschreibende Funktion darf die in der Basisklasse festgelegte Zugriffsberechtigung nicht einschränken. Eine Erweiterung der Zugriffsberechtigung ist zulässig. Beispiel : Wenn in der Basisklasse für eine Methode protected festgelegt ist, darf eine überschreibende Methode in der abgeleiteten Klasse protected oder public sein, jedoch nicht private oder package. ◇ Bezüglich der Exception-Deklaration (throws-Klausel) gelten folgende Regeln : ▻ Die Exception-Deklarationen von überschreibender und überschriebener Methode können unterschiedlich sein. ▻ Eine überschreibende Methode darf in ihrer Exception-Deklaration nur Exception-Typen enthalten, die zu einem in der Exception-Deklaration der überschriebenen Methode festgelegten Typen polymorph kompatibel sind (d.h. von der gleichen Klasse oder einer von ihr abgeleiteten Klasse sind). ▻ Eine überschreibende Methode muss nicht alle von der überschriebenen Methode deklarierten ExceptionTypen ebenfalls deklarieren. Auch wenn die überschriebene Methode eine Exception-Deklaration hat, darf diese bei der überschreibenden Methode fehlen. ▻ Eine überschreibende Methode darf nichtgeprüfte Exceptions werfen, die in der überschriebenen Methode nicht auftreten können. Diese dürfen aber nicht in die Exception-Deklaration aufgenommen werden. ◇ Zu der überschriebenen Methode kann – innerhalb der überschreibenden Methode oder in einer anderen Instanz-Methode der gleichen Klasse – über die Basisklassen-Teilobjekt-Referenz super zugegriffen werden (nicht jedoch über ihren vollqualifizierten Namen). super.method(...) ruft immer die Implementierung der Methode method(...) auf, die in der direkten Basisklasse verwendet wird. Es kann sich dabei durchaus um eine Methode handeln, die in der Ableitungshierarchie "weiter oben" definiert und in der direkten Basisklasse selbst nicht neu definiert worden ist. ◇ Ein Cast einer Referenz auf ein Objekt der abgeleiteten Klasse in die Basisklassen-Referenz ändert nicht den tatsächlichen Typ des referierten Objekts. Über einen derartigen Cast kann daher nicht zur überschriebenen sondern nur zur überschreibenden Methode zugegriffen werden. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/02/3 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Ableitung und Polymorphie (3) • Überladen von Methoden (Overloading) ◇ Eine in der abgeleiteten Klasse definierte Methode mit gleichem Namen aber anderer Parameterliste, überlädt die Methode der Basisklasse. Der Rückgabetyp bleibt unberücksichtigt, er kann gleich oder unterschiedlich sein. Das gleiche gilt für die Exception-Deklaration (throws-Klausel). ◇ In der abgeleiteten Klasse kann sowohl zu der geerbten Methode der Basisklasse als auch zu der neu definierten Methode allein über ihren Namen zugegriffen werden. Æ in Java ist ein Überladen von Methoden auch über Klassengrenzen hinweg möglich. ◇ Auch statische Methoden können im obigen Sinn überladen werden. ◇ Ferner können statische Methoden nicht-statische Methoden und umgekehrt überladen. • Überdecken von Klassenkomponenten (Hiding) ◇ Statische Methoden können überdeckt statt überschrieben werden. Eine in der abgeleiteten Klasse definierte statische Methode mit gleicher Signatur sowie gleichen Rückgabetyps wie eine statische Methode der Basisklasse, überdeckt die Methode der Basisklasse. ◇ Eine statische Methode darf keine Instanz-Methode überdecken (Æ Compiler-Fehler) ◇ Überdecken von Datenkomponenten ist zulässig ▻ zwischen Instanz-Variablen (nicht-statischen Datenkomponenten) ▻ zwischen Klassen-Variablen (statischen Datenkomponenten) ▻ gemischt zwischen Instanz- und Klassen-Variablen. ◇ Das Überdecken zwischen Datenkomponenten unterschiedlichen Typs ist zulässig. ◇ Überdeckt werden genaugenommen Namen. Überdecken eines Namens bedeutet, dass in der abgeleiteten Klasse der Name allein nur in der in dieser Klasse definierten Bedeutung verwendet werden kann. Um den Namen in der Bedeutung, die in der Basisklasse definiert ist, zu verwenden, muss der entsprechende vollqualifizierte Name oder der Name zusammen mit einer anderen Basisklassen-Referenz (z.B. super.kompo, innerhalb einer Instanz-Methode) verwendet werden. Die Verwendung des vollqualifizierten Namens ist nur für statische Komponenten zulässig. ◇ Beim Zugriff zu überdeckten Komponenten wird die tatsächlich ausgewählte Komponente durch die deklarierte Klasse der verwendeten Referenz (und nicht durch die Klasse des referierten Objekts) festgelegt. (Festlegung zur Compile-Zeit, early binding, keine Laufzeit-Polymorphie !) ◇ Zwischen einer Variablen und einer Funktion gleichen Namens findet kein Überdecken statt. Beide können gleichzeitig allein mit ihrem Namen verwendet werden. Das gilt auch, wenn beide in derselben Klasse definiert sind. ◇ Eine als private deklarierte Komponente kann weder überschrieben noch überdeckt werden (Sie ist ja in der abgeleiteten Klasse überhaupt nicht zugänglich). Ihr Name kann deshalb in der abgeleiteten Klasse für beliebige Definitionen verwendet werden. Beispielsweise ist es bei einer Memberfunktion zulässig, in der abgeleiteten Klasse eine Methode mit gleicher Signatur aber unterschiedlichem Rückgabetyp zu definieren. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/02/4 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Ableitung und Polymorphie (4) • Demonstrationsbeispiel zum Überschreiben und Überladen von Methoden ◇ Definition einer Basisklasse Punkt public class Punkt { private double x = 0.0; private double y = 0.0; public Punkt() { } public Punkt(double wx, double wy) { x = wx; y = wy; } public Punkt move(double dx, double dy) { x += dx; y += dy; return this; } // Die this-Referenz referiert das aktuelle Objekt // innerhalb einer Memberfunktion public Punkt change(Punkt p) { x = p.x; y = p.y; return this;} public String toString() { String rs = "(" + x + " , " + y + ")"; return rs; } } ◇ Definition einer abgeleiteten Klasse Farbpunkt public class Farbpunkt extends Punkt { private String col; public Farbpunkt() { col = "black"; } // impliziter Aufruf von super() public Farbpunkt(String wcol) { col = wcol; } // impliziter Aufruf von super() public Farbpunkt(double wx, double wy, String wcol) { super(wx, wy); col = wcol; } public Farbpunkt change(String ncol) { col = ncol; return this; } // ueberlaedt Basisklassenmethode public Farbpunkt change(Farbpunkt fp) { change((Punkt)fp); change(fp.col); return this; } // ueberlaedt Basisklassenmethode public String toString() // ueberschreibt Basisklassenmethode { String ts = super.toString() + " & " + col; return ts; } } HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/02/5 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Ableitung und Polymorphie (5) • Demonstrationsbeispiel zum Überschreiben und Überladen von Methoden, Forts ◇ Definition einer verwendenden Test-Klasse PunktTest // PunktTest.java public class PunktTest { public static void main(String[] args) { Punkt p1 = new Punkt(4.2, 6.3); Punkt p2 = new Farbpunkt(); Punkt p4 = new Farbpunkt("magenta"); Farbpunkt p5 = new Farbpunkt(2.5, 3.7, "blue"); System.out.println("p1 : " + p1); System.out.println("p2 : " + p2); System.out.println("p4 : " + p4); System.out.println("p5 : " + p5); System.out.println("nach einigen Aenderungen : "); p5.move(1.0, 1.0); System.out.println("p5 : " + p5); p5.change("yellow"); System.out.println("p5 : " + p5); p5.change(p1); System.out.println("p5 : " + p5); p4.change(p5); System.out.println("p4 : " + p4); } } ◇ Ausgabe des Demonstrations-Programms p1 : p2 : p4 : p5 : nach p5 : p5 : p5 : p4 : (4.2 , 6.3) (0.0 , 0.0) & black (0.0 , 0.0) & magenta (2.5 , 3.7) & blue einigen Aenderungen : (3.5 , 4.7) & blue (3.5 , 4.7) & yellow (4.2 , 6.3) & yellow (4.2 , 6.3) & magenta HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/02/6 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Ableitung und Polymorphie (6) • Typkompatibilität und -umwandlung ◇ Eine abgeleitete Klasse ist zuweisungs-kompatibel zu ihrer Basisklasse. Æ Einer Basisklassen-Variablen kann jederzeit eine Referenz auf ein Objekt einer von der Basisklasse abgeleiteten Klasse zugewiesen werden. Æ Voraussetzung für Laufzeit-Polymorphie ◇ Das durch eine Basisklassen-Variable referierte Objekt einer abgeleiteten Klasse wird dadurch als Objekt der Basisklasse behandelbar. Æ Implizite Typumwandlung. Da von einem spezialisierteren Typ in einen allgemeineren Typ umgewandelt wird, spricht man auch von einer erweiternden Typumwandlung (widening conversion). Weil diese Umwandlung in der KlassenHierarchie aufwärts erfolgt nennt man sie auch Aufwärts-Cast (upcast). ◇ Die Zuweisung einer Basisklassen-Referenz, die aber tatsächlich auf ein Objekt einer abgeleiteten Klasse zeigt, an eine Variable des tatsächlich referierten Typs ist implizit nicht möglich. Sie erfordert eine explizite Typumwandlung mittels des Cast-Operators. Hierbei findet eine Umwandlung von einem allgemeineren Typ in einen spezialisierteren Typ statt. Man nennt dies eine einengende Typumwandlung (narrowing conversion) oder einen Abwärts-Cast (down cast). ◇ Ein Abwärts-Cast ist prinzipiell unsicher. Er darf nur dann durchgeführt werden, wenn durch die Basisklassen-Referenz tatsächlich ein Objekt des angegebenen Zieltyps referiert wird. Der Versuch einer Typumwandlung, bei der dies nicht erfüllt ist, führt zum Werfen einer Exception vom Typ ClassCastException. • Der Operator instanceof ◇ Dieser binäre Operator ermöglicht die Überprüfung, ob ein Ausdruck zuweisungskompatibel zu einem bestimmten Typ ist. ◇ Syntax : Ausdruck instanceof Typangabe ◇ Wert eines instanceof-Ausdrucks : ▻ true, wenn der Ausdruck der linken Seite zuweisungs-kompatibel zum Typ der rechten Seite ist ▻ false, wenn der Ausdruck der linken Seite nicht zuweisungs-kompatibel zum Typ der rechten Seite ist ◇ Der instanceof-Operator kann eingesetzt werden, um einen sicheren Abwärts-Cast zu ermöglichen. ◇ Beispiel : class That { // ... } class More extends That { // ... } { // ... That sref = new More(); More mref; // ... if (sref instanceof More) mref=(More)sref; // ... } HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/03/1 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Interfaces und ihre Implementierung (1) • Prinzip des Interfaces ◇ Interfaces definieren – ähnlich wie Klassen – Typen. Allerdings erfolgt die Definition in einer abstrakten Form, die keinerlei Implementierung bereitstellt. Damit lassen sich Interfaces nicht instanziieren. Sie legen – wie der Name bereits ausdrückt – lediglich eine Schnittstelle (Kontrakt) fest, die dann von Klassen implementiert werden kann. ◇ Ein Interface besteht – ähnlich wie eine Klasse – aus Komponenten. Diese Komponenten können aber lediglich sein : ▻ abstrakte Methoden ▻ Konstante ▻ eingebettete Klassen und Interfaces (hier nicht betrachtet) ◇ Im Prinzip ähnelt ein Interface einer abstrakten Klasse. Im Unterschied zu einem Interface kann eine abstrakte Klasse aber eine (Teil-) Implementierung enthalten. (Instanz- und Klassen-Variable, Klassen-Methoden, definierte Instanz-Methoden). Ein Interface kann als abstrakte Klasse, die nur Konstante und abstrakte Methoden (und gegebenenfalls eingebettete Typen) vereinbart, aufgefasst werden ("rein abstrakte" Klasse). ◇ Interfaces müssen wie Klassen in jeweils einer eigenen Quelldatei (Ausnahme eingebettete Interfaces) definiert werden, deren Hauptname gleich dem Interface-Namen sein muß. Interfaces werden vom Compiler auch getrennt übersetzt. • Interface-Definition ◇ Syntax : interface Interface-Name InterfaceModifizierer Typ-ParamDeklaration { Konstantendefinition InterfaceAbleitung } Abstrakte-Methoden-Deklaration Klassen-/Interface-Definition ◇ Beispiel : public interface Fahrbar { int MOTOR_UND_REIFEN = 3; // int MOTOR_UND_SCHRAUBE = 5; // int MUSKEL_UND_REIFEN = 2; int MUSKEL_UND_SCHRAUBE = 4; int LEHRLAUF = 0; void vorwFahren(double dist); void rueckwFahren(double dist); int getAntrieb(); } ; implizit geltende Modifizierer static und final (Konstante) sowie public können weggelassen werden // implizit geltende Modifizierer abstract und public // können weggelassen werden HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/03/2 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Interfaces und ihre Implementierung (2) • Interface-Modifizierer ◇ Zulässig sind u.a. : ▻ public Ohne diesen Zugriffs-Spezifizierer besitzt jedes Interface die Zugrifssberechtigung package. ▻ abstract Jedes Interface ist implizit abstract. Eine explizite Angabe dieses Modifizierers ist daher überflüssig. • Definition von Memberkonstanten (Interface-Konstante) ◇ Ein Interface darf als Datenkomponenten nur Konstante enthalten. ◇ Jede Datenkomponente ist daher implizit static und final. Zusätzlich ist sie implizit public. Eine explizite Angabe dieser Feld-Modifizierer ist zwar zulässig, aber überflüssig. ◇ Jede Datenkomponente muß durch einen Initialisierer initialisiert werden. Æ blank finals sind nicht zulässig. Der Initialisierer muß ein konstanter Ausdruck sein. • Deklaration abstrakter Memberfunktionen (Interface-Methoden) ◇ Jede in einer Interface-Definition enthaltene Memberfunktion ist implizit public und abstract. Ihre Vereinbarung muß an Stelle eines Funktionsrumpfes ein ; enthalten. Nichtöffentliche Memberfunktionen machen keinen Sinn. ◇ Eine explizite Verwendung der Modifizierer abstract und public ist zwar zulässig, aber überflüssig ◇ Andere Modifizierer sind unzulässig. • Abgeleitete Interfaces ◇ Interfaces können auch voneinander abgeleitet werden ◇ Es gibt aber kein allgemeines Wurzel-Basis-Interface (etwa wie die Wurzel-Basisklasse Object) ◇ Im Unterschied zu Klassen ist bei Interfaces Mehrfachvererbung zulässig. Æ Ein Interface kann von mehreren anderen Interfaces abgeleitet sein. ◇ Syntax : Interface-Ableitung : extends Basis-Interface-Name , ◇ Beispiel : Bibliotheks-Interfaces Serializable (Package java.io) Runnable (Package java.lang) public Interface SerializeAndRunnable extends java.io.Serializable, Runnable { // ... } Das Interface SerializeAndRunnable enthält – durch Vererbung – alle Komponenten der Interfaces Serializable und Runnable. Zusätzlich kann es weitere Komponenten definieren. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/03/3 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Interfaces und ihre Implementierung (3) • Implementierung von Interfaces ◇ Der Sinn von Interfaces liegt in der Definition von Schnittstellen, die zur Realisierung einer Funktionalität durch Klassen implementiert werden müssen. ◇ Eine Klasse, die ein Interface implementiert, muß sämtliche Methoden des Interfaces (einschliesslich der von Basis-Interfaces geerbten) definieren (d.h. für sie eine Implementierung bereitstellen) oder selbst als abstract deklariert sein. Im letzteren Fall können – müssen aber nicht – die nicht implementierten Methoden des Interfaces als abstrakt deklariert werden Prinzipiell entspricht die Implementierung eines Interfaces damit der Ableitung von einer (rein) abstrakten Klasse. ◇ Eine Klasse kann – gegebenenfalls zusätzlich zur Ableitung von einer Klasse – mehrere Interfaces implementieren. Æ Realisierung einer Art eingeschränkter Mehrfachableitung in Java. ◇ Syntax : Interface-Implementierung : implements Interface-Name , Die implements-Klausel muss nach einer – gegebenenfalls vorhandenen – extends-Klausel aufgeführt werden. ◇ Beispiel : public class Auto implements Fahrbar { private double kmZaehler; private double kraftstoff; private double verbrauch; public Auto(double verbr, double km) { kmZaehler = km; verbrauch = verbr; } public int getAntrieb() { return MOTOR_UND_REIFEN; } public void vorwFahren(double dist) { // Funktionsrumpf } // Definition der im Interface // deklarierten Methoden public void rueckwFahren(double dist) { // Funktionsrumpf } // weitere Memberfunktionen } ◇ Die Implementierung eines Interfaces wird auch an abgeleitete Klassen vererbt. In einem derartigen Fall ist die Angabe einer implements-Klausel bei der abgeleiteten Klasse nicht explizit notwendig, aber zulässig. Æ Dem Kopf einer Klassendefinition können nicht in jedem Fall alle implementierten Interfaces entnommen werden. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/03/4 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Interfaces und ihre Implementierung (4) • Demonstationsbeispiel zur Implementierung eines Interfaces ▻ Definition der das Interface Fahrbar implementierenden Klasse Auto // Auto.java // Definition der Klasse Auto // Klasse implementiert das Interface Fahrbar import java.util.*; public class Auto implements Fahrbar { public static String TANKLEER = "Tanken notwendig !"; private double kmZaehler; private double kraftstoff; private double verbrauch; public Auto(double verbr, double km) { kmZaehler = km; verbrauch = verbr; } public int getAntrieb() { return MOTOR_UND_REIFEN; } public void vorwFahren(double dist) { double maxEntf = kraftstoff/verbrauch*100; if (dist>maxEntf) dist=maxEntf; kmZaehler+=dist; kraftstoff-=dist/100*verbrauch; if (dist==maxEntf) throw new RuntimeException(TANKLEER); } public void rueckwFahren(double dist) { } public void tanken(double lit) { kraftstoff += lit; } public double getKmStand() { return kmZaehler; } public double getKraftstoff() { return kraftstoff; } } HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/03/5 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Interfaces und ihre Implementierung (5) • Demonstationsbeispiel zur Implementierung eines Interfaces, Forts. ▻ Definition einer Start- und Testklasse AutoTest // AutoTest.java // Klasse AutoTest zum Testen der Klasse Auto import java.util.Scanner; public class AutoTest { public static void main(String[] args) { Auto myCar = new Auto(6.0, 500); Scanner stdinp = new Scanner(System.in); double entf; double liter; double altkm = myCar.getKmStand(); double neukm; do { try { System.out.print("Fahrstrecke ? "); entf=stdinp.nextDouble(); myCar.vorwFahren(entf); } catch (RuntimeException ex) { if (ex.getMessage().equals(Auto.TANKLEER)); { System.out.print("Tanken notwendig, wieviel Liter ? "); liter = stdinp.nextDouble(); myCar.tanken(liter); } } finally { neukm = myCar.getKmStand(); System.out.println("gefahrene km : " + (neukm-altkm)); System.out.println("km-Stand : " + neukm); altkm = neukm; } } while (myCar.getKraftstoff() > 0); } } ▻ Beispiel für einen Programmdialog HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/03/6 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Interfaces und ihre Implementierung (6) • Interfaces als Objekt-Referenzen ◇ Objekte einer Klasse, die ein Interface implementiert, lassen sich auch als Instanzen des implementierten Interfaces behandeln. Æ Explizite oder implizite Typkonvertierung (cast) eines Objekts in den Typ eines implementierten Interfaces. ◇ Dies ermöglicht es auch, mit dem instanceof-Operator zu überprüfen, ob die Klasse eines Objekts ein bestimmtes Interface implementiert. Beispiel : Auto car = new Auto(7.0, 10000); boolean isfb = car instanceof Fahrbar; // Æ true ◇ Man kann Variable eines Interface-Typs definieren und ihnen Referenzen auf Objekte einer implementierenden Klasse zuweisen. Natürlich lassen sich über eine Interface-Variable auch nur die Komponenten eines Objekts ansprechen, die in dem implementierten Interface definiert sind. Beispiel : // ... Fahrbar fb = new Auto(10, 150000); // ... fb.getAntrieb(); // ok fb.tanken(50); // unzulässig ! // ... ◇ Jede Objekt-Referenz eines Interface-Typs lässt sich allerdings als Referenz auf die Klasse Object verwenden. Damit lassen sich über eine Interface-Variable auch die in der Klasse Object definierten Methoden aufrufen. Dies ist deswegen möglich, da ja das referierte Objekt von irgendeiner Klasse sein muß und jede Klasse von Object abgeleitet ist. Beispiel : // ... Fahrbar fb = new Auto(5, 1500); // ... String str = fb.toString(); // keine Methode von Auto, // ... // aber von Object • Marker Interfaces ◇ Es kann auch sinnvoll sein, ein Interface leer, d.h. ohne jegliche Komponenten, zu definieren. ◇ Ein derartige Interface dient dazu, anzuzeigen, dass eine implementierende Klasse über eine bestimmte Eigenschaft verfügt. Es markiert die Klasse hinsichtlich des Besitzes dieser Eigenschaft Æ Marker Interface ◇ In der Java-Standardbibliothek sind mehrere derartige Marker Interfaces enthalten. Beispiel : ▻ Cloneable (Package java.lang) Es legt fest, dass Objekte einer implementierenden Klasse mit der clone()-Methode geclont werden können. Die clone()-Methode ist selbst nicht Komponente des Interfaces, sondern der Klasse Object. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/04/1 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Programmbeispiel : Simpson-Integration(1) ● Aufgabenstellung ◇ Im C-Teil der Vorlesung ist als Beispiel für die Verwendung von Funktions-Pointern ein Programm zur Anwendung der Simpson-Integration auf verschiedene zu integrierende Funktionen y=f(x) vorgestellt worden. In dem Beispiel wird eine allgemeine Simpson-Integrations-Funktion formuliert, der die jeweils zu integrierende Funktion mittels eines Funktions-Pointers als Parameter übergeben werden muss. ◇ Dieses Beispiel soll nun sinnvoll nach Java portiert werden. Die Implementierung soll dabei so erfolgen, dass auch eine einfache Erweiterung auf andere numerische Integrationsverfahren ermöglicht wird. ● Lösung : ◇ Definition einer abstrakten Basisklasse Integrator. Sie ist Basisklasse für konkrete Klassen, die jeweils ein spezielles numerisches Integrationsverfahren implementieren. Für die Simpson-Integration soll das die Klasse SimpsonIntegrator sein. Die Klasse Integrator definiert eine Instanzvariable zur Speicherung der gewünschten Genauigkeit (die ihr im Konstruktor zu übergeben ist) und deklariert eine abstrakte Methode integrate( ). Diese ist von den abgeleiteten Klassen zur Realisierung des jeweiligen Integrations-Algorithmus geeignet zu implementieren. ◇ Jede zu integrierende Funktion wird durch eine Klasse beschrieben, die das Interface FunctionYvonX implementieren muss (Æ Funktionsklassen). Dieses Interface deklariert die von den Funktionsklassen zu definierenden Methode funcVal(double x), die die jeweilige zu integrierende Funktion realisiert. Beispielhaft werden hier die Funktionsklassen Halbkreis, Dreieck und Polynom2 (Polynom 2. Ord.) definiert. ◇ Jedes Objekt einer Funktionsklasse lässt sich als Instanz des Interfaces FunctionYvonX behandeln. Als solche wird es (mittels einer entsprechenden Referenz) der Integrator-Funktion integrate(...) als Parameter übergeben. Innerhalb dieser Funktion ruft das Integrator-Objekt die Methode funcVal(...) für dieses Objekt auf. (Æ "Das Integrator-Objekt schickt die Botschaft funcVal an das FunctionYvonX-Objekt") ◇ Als Startklasse dient eine Klasse SimpIntDemo. In ihrer Methode main() werden ein SimpsonIntegrator-Objekt sowie die verschiedenen Funktionsklassen-Objekte erzeugt. ● Klassendiagramm der Lösung (ohne Startklasse) Integrator FunctionYvonX eps integrate() funcVal() SimpsonIntegrator Halbkreis Dreieck Polynom2 integrate() funcVal() funcVal() funcVal() HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/04/2 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Programmbeispiel : Simpson-Integration(2) ● Definition der abstrakten Klasse Integrator // Integrator.java // abstrakte Basisklasse fuer Integrator-Klassen package integration; public abstract class Integrator { protected double eps; public Integrator(double gen) { eps = gen; } public abstract double integrate(FunctionYvonX func, double x1, double x2); } ● Definition der Klasse SimpsonIntegrator // SimpsonIntegrator.java // Integrator-Klasse, implementiert die Simpson-Integration package integration; public class SimpsonIntegrator extends Integrator { public SimpsonIntegrator(double gen) { super(gen); } public double integrate(FunctionYvonX func, double dXu, double dXo) { long i; double dFl = 0.; /* letzter Integralwert */ double dFa = 0.; /* aktueller Integralwert */ double dH; /* Schrittweite */ double dX; /* X-Wert */ double dY; /* Funktionswert */ double dg; /* Gewicht */ double dErr; /* Fehler */ long n = 2; /* Intervallanzahl */ do /* Schleife ueber jeweils verdoppelte Intervallanzahl */ { dH = (dXo - dXu)/n; /* Schrittweite bestimmen */ dFa = func.funcVal(dXu) + func.funcVal(dXo); for (i=1; i < n; i++) /* Flaeche der akt. Intervallanzahl */ { dX = dXu + dH*i; dY = func.funcVal(dX); if (i%2 == 1) dg = 4.; else dg = 2.; dFa += dg*dY; } dFa *= dH/3; dErr = Math.abs((dFa -dFl)/dFa); n*=2; /* Verdoppeln der Intervallanzahl */ dFl = dFa; } while ( dErr > eps); return dFa; } } HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/04/3 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Programmbeispiel : Simpson-Integration(3) ● Definition des Interfaces FunctionYvonX // FunctionYvonX.java // Interface fuer Funktions-Klassen, Funktionen der Form y = f(x) package integration; public interface FunctionYvonX { double funcVal(double x); } ● Definition der Klasse Halbkreis // Halbkreis.java // Funktions-Klasse, die einen Halbkreis implementiert // Der Radius wird dem Konstruktor uebergeben package integration; public class Halbkreis implements FunctionYvonX { private double radius; public Halbkreis(double r) { radius = r; } public double funcVal(double dX) { double dY = 0; if (dX <= 1. && dX >= -1.) dY = Math.sqrt (radius*radius - dX*dX); return dY; } } ● Definition der Klasse Polynom2 // // // // Polynom2.java Funktions-Klasse, die ein Polynom 2.Grades implementiert y = a*x*x + b*x +c Die Polynom-Koeffizienten werden dem Konstruktor uebergeben package integration; public class Polynom2 implements FunctionYvonX { private double a, b, c; public Polynom2(double ak, double bk, double ck) { a = ak; b = bk; c = ck; } public double funcVal(double dX) { return a*dX*dX + b*dX + c; } } HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/04/4 – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Programmbeispiel : Simpson-Integration(4) ● Definition der Klasse Dreieck // // // // // Dreieck.java Funktions-Klasse, die ein Dreieck implementiert Koordinaten der Eckpunkte : (xl,0), (xr,0), (0,yh), mit xl < 0 und xr > 0 xl, xr und yh werden dem Konstruktor uebergeben Die Flaeche des Dreiecks betraegt (xr-xl)*yh/2 package integration; public class Dreieck implements FunctionYvonX { private double xl; private double xr; private double h; public Dreieck(double xlw, double xrw, double yhw) { xl=xlw; xr=xrw; h=yhw; } public double funcVal(double dX) { double dRet; if (dX > xr || dX < xl) dRet = 0.; else if(dX >= 0) dRet = (h-h/xr*dX); else dRet = (h-h/xl*dX); return dRet; } } ● Definition der Start-Klasse SimpIntDemo // SimpIntDemo.java // Startklasse fuer ein Demo-Programm zur Simpson-Integration package integration; import java.io.*; import java.util.*; public class SimpIntDemo { public static void main(String[] args) { double dXo = 1.; double dXu = -1.; double dF; FunctionYvonX funcArr[] ={ new Halbkreis(1.0), new Dreieck(-1, 1, 1), int i; boolean bBed; Scanner scan = new Scanner(System.in); Integrator sig = new SimpsonIntegrator(1.e-8); new Polynom2(1, -6, 1), new Dreieck(-0.5, 1, 3) }; do { System.out.printf(" Auswahl: "); i=scan.nextInt(); bBed = i>0 && i <= funcArr.length; /* Abbruchbedingung formulieren */ if(bBed) { dF = sig.integrate(funcArr[i-1],dXu,dXo); System.out.printf (" Xu: %8.4f, Xo: %8.4f, F: %12.8f \n", dXu,dXo,dF); } } while(bBed); } } HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/02/1a – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Ableitung und Polymorphie (1a) • Demonstrationsbeispiel zur Vererbung ◇ Definition einer Basisklasse Punkt public class Punkt { private double x = 0.0; private double y = 0.0; public Punkt() { } // no-arg Konstruktor public Punkt(double wx, double wy) { x = wx; y = wy; } //Konstruktor public Punkt move(double dx, double dy) { x += dx; y += dy; return this; // Die this-Referenz referiert das aktuelle Objekt } // innerhalb einer Memberfunktion public String showPunkt() { String rs = "(" + x + " , " + y + ")"; return rs; } } ◇ Definition einer abgeleiteten Klasse Farbpunkt public class Farbpunkt extends Punkt { private String col; // Vererbung // zusaetzliche Datenkomponente public Farbpunkt() { col = "black"; } // no-arg Konstruktor // impliziter Aufruf von super() public Farbpunkt(String wcol) { col = wcol; } // Konstruktor // impliziter Aufruf von super() public Farbpunkt(double wx, double wy, String wcol) { super(wx, wy); // expliziter Aufruf des Konstruktors col = wcol; // der Basisklasse } public String showFarbPunkt() // verwendet Basisklassenmethode { String ts = super.showPunkt() + " & " + col; return ts; } } HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 14/02/1b – TH – 01 -------------------------------------------------------------------------------------------------------------------------------------------------------- Ableitung und Polymorphie (1b) • Demonstrationsbeispiel zur Vererbung, Forts ◇ Definition einer Test-Klasse APunktTest public class APunktTest { public static void main(String[] args) { Punkt p1 = new Punkt(4.2, 6.3); Farbpunkt p2 = new Farbpunkt("rot"); Punkt p3 = new Farbpunkt(2.5, 3.7, "blau"); System.out.println("p1 : " + p1.showPunkt()); System.out.println("p2 : " + p2.showFarbPunkt()); System.out.println("p3 : " + p3.showPunkt()); // System.out.println("p3 : " + (p3.showFarbPunkt());// Fehler: p3 ist Punkt! System.out.println("p3 : " + ((Farbpunkt)p3).showFarbPunkt()); System.out.println("nach \"move(1,1)\": "); p1.move(1.0, 1.0); p2.move(1.0, 1.0); System.out.println("p1 : " + p1.showPunkt()); System.out.println("p2 : " + p2.showFarbPunkt()); } } ◇ Ausgabe des Demonstrations-Programms p1 : p2 : p3 : p3 : nach p1 : p2 : (4.2 , 6.3) (0.0 , 0.0) & rot (2.5 , 3.7) (2.5 , 3.7) & blau "move(1,1)": (5.2 , 7.3) (1.0 , 1.0) & rot HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 10/00/0 – TH – 01 ------------------------------------------------------------------------------------------------------------------------------------------------------------ Programmieren (4. Sem.) Kapitelüberblick 14. Grundkonzepte der Objektorientierten Programmierung (OOP) 14.0. Einführung : Grundgedanke der OOP 14.1. Objekte und Klassen 14.2. Kapselung 14.3. Vererbung 14.4. Polymorphie 14.5. Klassenbeziehungen HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 10/00/1 – TH – 01 ------------------------------------------------------------------------------------------------------------------------------------------------------------ Einführung • Grundgedanke der OOP: OOP ist eine Softwareentwicklungsmethodik, deren Grundidee aus der Simulationstechnik stammt : In dieser werden die Objekte der realen Welt sowie deren Beziehungen durch entsprechende Strukturen im Rechner abgebildet. In der OOP wird dieses Prinzip auf alle Arten von Informationen und Abläufen – auch auf solche abstrakter Natur – angewendet. Der Aufgabenbereich eines zu lösenden Problems wird in Objekte und die zwischen ihnen bestehenden Beziehungen zerlegt. Diese werden in einem das entsprechende Problem lösenden Programm nachgebildet. Æ Ein OOP-Programm besteht somit im wesentlichen aus einer Anzahl miteinander in Beziehung stehender Objekte. Diese Denk- und Vorgehensweise ermöglicht es, auch sehr umfangreiche und komplexe Aufgaben auf einem relativ hohem Abstraktionsniveau erfolgreich zu bearbeiten. Sie nutzt die intellektuellen Fähigkeiten aus, die der Mensch zur Bewältigung der ihn umgebenden Komplexität entwickelt hat. Dies sind im wesentlichen die Fähigkeiten des Abstrahierens, des Klassifizierens und des Generalisierens. Auf ihnen beruhen die Grundkonzepte der OOP : ◈ Bildung von Objekten ◈ Abstraktion der Objekte durch Klassen ◈ Kapselung ◈ Vererbung ◈ Polymorphie HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 10/01/1 – TH – 01 ------------------------------------------------------------------------------------------------------------------------------------------------------------ Grundkonzepte der OOP : Objekte und Klassen (1) • Konkrete Objekte: ◇ Objekte sind in einem bestimmten Sinn abgeschlossene Einheiten, die durch zwei Aspekte gekennzeichnet sind : ▻ Sie besitzen einen (inneren) Zustand ▻ und sie verfügen über Fähigkeiten, d.h. sie können bestimmte Operationen – aktiv oder passiv – ausführen Diese Fähigkeiten und damit das dadurch bestimmte Verhalten können von außen angefordert, aktiviert werden. Die Aktivierung der Fähigkeiten kann eine Zustandsänderung bewirken. ◇ Beispiel : Willies Uhr Zustand : aktuelle Zeit Fähigkeiten : Uhr stellen Zeit fortschalten (ticken) Zeit darstellen • Objekte in der OOP ◇ In der OOP stehen Objekte im Vordergrund. Sie bilden die grundlegenden Strukturierungseinheiten eines OOP-Programmms Dabei kann ein Objekt sehr konkret aber auch beliebig abstrakt sein, es kann ein statisches Gebilde (z.B. ein Auto), oder einen dynamischen Ablauf (Vorgang) beschreiben (z.B. ein Tennisspiel). Der (innere) Zustand des Objekts wird durch Datenstrukturen (Datenkomponenten), seine Fähigkeiten – die von ihm ausführbaren Operationen – werden durch Funktionen (Prozeduren) beschrieben. Die Datenkomponenten werden auch als Attribute, die Funktionen (Memberfunktionen) als Methoden bezeichnet. ◇ Ein Objekt verbindet also Daten und die zu ihrer Bearbeitung dienenden Funktionen (Code !) zu einer Einheit. Æ Die von einem Objekt ausführbaren Methoden (= Funktionen) sind Bestandteil des Objekts und nur als solche relevant. Dies steht im Gegensatz zur konventionellen (prozeduralen, imperativen) Programmierung, bei der Daten und Code getrennt sind, wobei der Code (Prozeduren, Funktionen) eigenständig ist und im Vordergrund steht : Code wird auf Daten angewendet. ◇ In der OOP wird eine Methode für ein Objekt aufgerufen, in dem an das Objekt – i.a. durch ein anderes Objekt – eine entsprechende Nachricht (Botschaft) geschickt wird : Das Objekt interpretiert die Nachricht und reagiert mit der Ausführung einer zugeordneten Operation (Methode). Zwischen den Objekten bestehen also Kommunikationsbeziehungen. Æ Ein OOP-Programm besteht im wesentlichen aus einer Ansammlung miteinander kommunizierender und dadurch interagierender Objekte. ◇ Der OOP-Ansatz erfordert eine andere Vorgehensweise bei der Problemlösung : Statt einer Top-Down-Zerlegung des Problems (Æ hierarchische Modularisierung) müssen die relevanten Objekte (Aufbau und Verhalten) des Problems und die zwischen ihnen bestehenden Beziehungen ermittelt werden ( Æ aufgaben- und kommunikationsorientierte Zerlegung) HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 10/01/2 – TH – 01 ------------------------------------------------------------------------------------------------------------------------------------------------------------ Grundkonzepte der OOP : Objekte und Klassen (2) • Klassen ◇ Der Aufbau, die Eigenschaften und Fähigkeiten (Verhaltensweisen) von Objekten werden durch Klassen beschrieben. ◇ Eine Klasse legt die Datenkomponenten (Datenstruktur) und die Methoden zur Bearbeitung der Daten (Memberfunktionen) für eine Menge gleichartiger Objekte – d.h. Objekte mit gemeinsamen Merkmalen und gleichen Fähigkeiten, die diese von anderen Objekten unterscheiden – fest. ◇ Ein spezielles Objekt der durch eine Klasse definierten Objektmenge wird auch Instanz genannt. Es unterscheidet sich von einem anderen Objekt (einer anderen Instanz) der gleichen Klasse nur durch seinen jeweiligen Zustand, d.h. den Werten seiner Datenkomponenten. ⇒ Die Klasse entspricht dem Datentyp prozeduraler Programmiersprachen, während eine Instanz (ein spezielles Objekt dieser Klasse) einer Variablen entspricht. ◇ Eine Klasse kann formal als Erweiterung einer Struktur (struct-Typ) in C aufgefasst werden. Gegenüber einer nur aus Datenkomponenten bestehenden Struktur besitzt eine Klasse zusätzlich Funktionskomponenten. ◇ Beispiel : Klasse Uhr Uhr Datenkomp : actTime Funktionskomp : setTime(...) tick() displayClock() ◇ Jedes Objekt (Instanz) einer Klasse hat einen eigenen (inneren) Zustand. Æ Die Datenkomponenten existieren für jedes Objekt (Unterschied zu Modulen der prozeduralen Programmierung). Sie werden erst geschaffen, wenn das Objekt generiert wird (Æ "Variablendefinition"). ◇ Die Methoden (Funktionen) existieren dagegen nur einmal pro Klasse. Sie werden durch die Definition der Klasse (Æ "Typdefinition") geschaffen. Æ Auch wenn es gar keine Objekte dieser Klasse gibt, existieren die Methoden. Jedes Objekt einer Klasse arbeitet mit demselben Code (Æ Code Sharing) Beim Aufruf einer Methode für ein spezielles Objekt, wird dieser eine Referenz auf das Objekt als verborgener Parameter übergeben. Dadurch kann die Methode zu sämtlichen Komponenten (also auch den Datenkomponenten) dieses Objekts zugreifen. Æ Durch den Aufruf der Methode wird diese einem speziellen Objekt zugeordnet. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 10/02/1 – TH – 01 ------------------------------------------------------------------------------------------------------------------------------------------------------------ Grundkonzepte der OOP : Kapselung • Kapselung (Encapsulation): ◇ Der Benutzer eines Objekts (allg. : Anwenderprogramm, speziell : anderes Objekt) braucht seinen genauen Aufbau nicht zu kennen. Ihm müssen lediglich die Methoden, die er für eine Interaktion mit dem Objekt benötigt, d.h. über die er die Fähigkeiten des Objekts aktivieren und dessen Zustand verändern kann, bekannt zu sein. Von der internen Darstellung der den jeweiligen Objektzustand festlegenden Daten braucht er dagegen keinerlei Kenntnis zu haben. ◇ Nur die Funktionen (= Methoden) eines Objekts, die zu seiner Verwendung tatsächlich benötigt werden, werden allgemein zugänglich, d.h. öffentlich (public), gemacht. Sie bilden das Interface (Protokoll), über das zu dem Objekt kontrolliert zugegriffen werden kann, d.h. sie bilden seine Schnittstelle zur "Außenwelt". Die Daten (u. gegebenenfalls reine Hilfs- und Verwaltungsfunktionen) sind nur Komponenten des Objekts selbst zugänglich, d.h. privat (private). Der "Außenwelt" gegenüber bleiben sie verborgen Æ Sie sind nach außen gekapselt. Hierdurch wird sichergestellt, dass zu einem Objekt nur über eine wohldefinierte Schnittstelle zugegriffen Werden kann.Æ Klassen-Schnittstelle Zugriffe zu Interna, die nur zur internen Realisierung und Verwaltung des Objekts dienen, sind nicht möglich Æ Vermeidung von Fehlern durch Verhinderung eines direkten und unkontrollierten Zugriffs ⇒ Trennung von Interface (Schnittstelle) und Implementierung. Methoden Daten Methoden ◇ Die Kapselung bewirkt außerdem eine Datenabstraktion (data abstraction) : Eine Datenstruktur ist - nach außen - nicht mehr an eine bestimmte Implementierung gebunden, sondern wird allein über die auf sie anwendbaren Operationen (Methoden, Funktionen) definiert. (Æ abstrakter Datentyp, ADT) Æ Eine Änderung der Implementierung - bei gleichbleibendem Interface – hat keinen Einfluß auf den Anwendungscode. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 10/03/1 – TH – 01 ------------------------------------------------------------------------------------------------------------------------------------------------------------ Grundkonzepte der OOP : Vererbung • Vererbung (Inheritance) : ◇ Weitergabe von Eigenschaften (Daten und Funktionen) eines Objekts an ein anderes Objekt. Zusätzlich zu den ererbten Eigenschaften kann ein Objekt neue spezifische Eigenschaften (Daten und Funktionen) besitzen bzw. bestimmte Eigenschaften modifizieren. Æ Schaffung einer neuen Art von Objekten durch Erweiterung einer bestehenden Art von Objekten. ◇ Die Vererbung führt zum Aufbau von Klassenhierarchien : Eine neue Klasse wird aus einer - oder mehreren - bereits definierten Klasse(n) abgeleitet. vorhandenene Klasse : Basisklasse, Elternklasse, Oberklasse neue Klasse : abgeleitete Klasse, Kindklasse, Unterklasse Die abgeleitete Klasse erbt die Daten und Methoden der Basisklasse(n). Dabei können geerbte Methoden abgeändert ("überschrieben") werden. Zusätzlich kann die abgeleitete Klasse neue Daten und Methoden besitzen. Æ Abänderung und Ergänzung im Sinne einer weiteren Spezialisierung ◇ Beispiel : Ableitung der Klasse UhrMitDatum von der Klasse Uhr neue Datenkomponenten : neue Funktionskomponenten : geänderte Funktionskomponenten : actDate setDate(), setClock() tick(), displayClock() Uhr Datenkomp : actTime Funktionskomp : setTime(...) tick() displayClock() UhrMitDatum neue Datenkomp : actDate neue Funkt.komp : setDate(...) setClock(...) modifiz. Funkt.komp : tick() displayClock() ◇ Ein Objekt einer abgeleiteteten Klasse kann immer auch als – spezielles - Objekt der Basisklasse(n) betrachtet werden : z.B. ist jede UhrMitDatum auch eine Uhr. ◇ Einfache Vererbung : Mehrfachvererbung : Ableitung einer Klasse von nur einer Basisklasse. Ableitung einer Klasse von mehreren Basisklassen (nicht von allen OOP-Sprachen unterstützt, u.a. auch nicht von Java) ◇ Durch Vererbung übertragene Methoden (Funktionen) existieren nur einmal (Æ Code Sharing). Dies erleichtert Änderungen und Erweiterungen an bestehenden Klassenhierarchien. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 10/04/1 – TH – 01 ------------------------------------------------------------------------------------------------------------------------------------------------------------ Grundkonzepte der OOP : Polymorphie • Polymorphie (Polymorphism) : ◇ Verwendung des gleichen Namens für unterschiedliche - aber miteinander verwandte - Dinge. (griech. : Vielgestaltigkeit) ◇ Polymorphie in der OOP ermöglicht, dass verschiedenartige Objekte (unterschiedlicher aber durch Vererbung miteinander verwandter Klassen) unter einem gemeinsamen Oberbegriff (Basisklasse) betrachtet und bearbeitet werden können.(Æ Generalisierung) Beispiel : Sowohl Objekte der Klasse Uhr als auch Objekte der Klasse UhrMitDatum lassen sich als Uhr-Objekte behandeln. . ◇ In der Basisklasse definierte Funktionen können in abgeleiteten Klassen mit dem gleichen Namen und der gleichen Parameterliste (d.h. gleichem Interface) erneut definiert werden. Æ Überschreiben von Funktionen (Function Overwriting), virtuelle Funktionen. Dadurch wird das durch den Aufruf einer deratigen Funktion bewirkte Verhalten eines Objekts in der abgeleiteten Klasse abgeändert. Der gleiche (Funktions-)Name kann somit zur Spezifizierung einer Gruppe ähnlicher - aber doch unterschiedlicher – Operationen (Methoden) verwendet werden. Æ Die gleiche durch den Funktions-Namen ausgedrückte Botschaft kann - an unterschiedliche Objekte gerichtet – zum Aufruf unterschiedlicher Methoden führen. Die in einem konkreten Fall ausgeführte Operation (Methode) hängt von der tatsächlichen Klasse des Objekts ab, an das die Botschaft gerichtet ist (Æ "Ein Interface - mehrere Methoden"). Also nicht die Botschaft, d.h. der Aufruf, bestimmt, welche Methode (Funktion) ausgeführt wird, sondern der Empfänger der Botschaft. Beispiel : Die Botschaft "displayClock()" an ein UhrMitDatum-Objekt gerichtet, bewirkt die Ausgabe der Uhrzeit und des Datums, während sie bei einem Uhr-Objekt nur zur Ausgabe der Uhrzeit führt. ⇒ Polymorphie erlaubt durch die Schaffung eines Standardinterfaces die einheitliche Verarbeitung unterschiedlicher Objekte, die über gemeinsame Grundfähigkeiten verfügen. Dadurch wird die Beherrschung einer größeren Komplexität ermöglicht. ◇ Beim Aufruf virtueller (überschreibbarer) Methoden wird die Zuordnung der tatsächlich aufgerufenen Methode zur Botschaft (Methodenname) erst zur Laufzeit vorgenommen. Dies bezeichnet man als "späte Bindung" ("late binding"). Bei einer Zuordnung bereits zur Compilezeit spricht man von "früher Bindung" ("early binding"). ◇ In einer erweiterten Betrachtungsweise ermöglicht Polymorphie auch das – nicht nur in der OOP eingesetzte – Überladen von Funktionen (Methoden) und Operatoren : ▻ Überladen von Funktionen (Function Overloading) : Mehrere Funktionen können den gleichen Namen besitzen, sofern ihre Parameterliste (Signatur) unterschiedlich ist. Durch den gleichen Namen wird auch hier eine Art Standard-Interface (das sich aber nicht auf die Parameter erstreckt) bereitgestellt, über das sich mehrere unterschiedliche aber meist miteinander verwandte Methoden aufrufen lassen. Die speziell angewandte Methode hängt hier von den beim Aufruf übergebenen Daten (Parametern) ab. ▻ Überladen von Operatoren (Operator Overloading) : Operatoren können zur Anwendung auf unterschiedliche Datentypen umdefiniert werden. Æ Definition spezieller Operatorfunktionen (in Java nicht möglich) HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 10/05/1 – TH – 01 ------------------------------------------------------------------------------------------------------------------------------------------------------------ Klassenbeziehungen (1) • Vererbungsbeziehung ◇ Beziehung zwischen Klassen, deren Komponenten sich teilweise überdecken ◇ Eine abgeleitete Klasse erbt die Eigenschaften und Fähigkeiten (Komponenten) der Basisklasse(n). "ist"-Beziehung Æ ein Objekt der abgeleiteten Klasse ist auch ein Objekt der Basisklasse(n) ◇ Ordnungsprinzip bei der Spezifikation von Klassen. Æ Generalisierung / Spezialisierung ◇ Ein Spezialfall der Vererbung ist die Implementierung (Basisklasse definiert nur ein Zugriffsinterface) • Nutzungsbeziehungen ◇ Unter einer (statischen) Nutzungsbeziehung versteht man eine in einem konkreten Anwendungsbereich geltende Beziehung zwischen Klassen, deren Instanzen voneinander Kenntnis haben und die dadurch miteinander kommunizieren können Æ Nutzungsbeziehungen sind notwendig für die Interaktion von Objekten ◇ Assoziation - Spezielle Beziehung zwischen Klassen bzw Objekten, bei der die Objekte unabhängig voneinander existieren und lose miteinander gekoppelt sind Beispiel : einem Objekt wird die Referenz auf ein anderes Objekt in einer Methode als Parameter übergeben und nur in dieser Methode verwendet - Navigationsrichtung : legt die Kommunikationsrichtung und die Richtung, in der ein Objekt der einen Klasse ein Objekt der anderen Klasse referieren kann, fest. bidirektional (Kommunikation in beiden Richtungen möglich) oder unidirektional - Kardinalität (multiplicity) : bezeichnet die mögliche Anzahl der an der Assoziation beteiligten Instanzen einer Klasse. ◇ Aggregation - Spezielle Beziehung zwischen Klassen bzw Objekten, bei der die Objekte der einen Klasse Bestandteile (Komponenten) eines oder mehrerer Objekte der anderen Klasse sind. Æ zwischen den Objekten besteht eine feste Kopplung - "hat"-Beziehung bzw "ist Teil von"-Beziehung - Das "umschließende" Objekt bildet einen Container für das bzw die enthaltene(n) Objekt(e) - Aggregation kann als Spezialfall der Assoziation aufgefaßt werden. - eine Aggregation ist i.a. eine unidirektionale Beziehung (Navigation vom umschließenden Objekt zu den Komponenten-Objekten) Je nach dem Grad der Kopplung unterscheidet man : ▻ einfache Aggregation Das umschließende Objekt und die Komponenten sind nicht existenzabhängig Eine Komponente kann zusätzlich noch weiteren umschließenden Objekten der gleichen oder einer anderen Klasse zugeordnet sein. Bei Löschung des umfassenden Objekts bleiben die Komponenten unabhängig davon erhalten. Beispiel : Objekt wird vom umfassenden Objekt erzeugt, Referenz darauf wird anderem Objekt übergeben ▻ echte Aggregation (Komposition, composite aggregation) Die Komponenten können nur einem umfassenden Objekt zugeordnet sein und nur innerhalb diesem existieren. Bei Löschung des Aggregats werden auch die Komponenten gelöscht. Beispiel : Objekt wird von umfassenden Objekt erzeugt und nur dort verwendet ◇ Anmerkung : In der Praxis kann es im Einzelfall sehr schwierig sein, zwischen Assoziation und Aggregation und den verschiedenen Formen der Aggregation zu unterscheiden. HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 10/05/2 – TH – 01 ------------------------------------------------------------------------------------------------------------------------------------------------------------ Klassenbeziehungen (2) • Klassendiagramm ◇ Die (statischen) Beziehungen (Vererbungsbeziehungen und Nutzungsbeziehungen) zwischen den in einem OOP-System zusammenwirkenden Klassen lassen sich durch Klassendiagramme beschreiben. ◇ Ein Klassendiagramm ist eines der in der UML (Unified Modelling Language) zusammengefassten graphischen Darstellungsmittel. Es ermöglicht die Erstellung eines detaillierten statisches Systemmodells ◇ Zur Erhöhung der Übersichtlichkeit kann die Gesamtheit der Klassen eines Systems auf mehrere Teildiagramme aufgeteilt sein • Anmerkung : ◇ Die dynamischen Beziehungen zwischen den Objekten eines OOP-Systems werden durch Sequenzdiagramme, einer anderen Diagrammart der UML, beschrieben. Sie werden hier nicht weiter betrachtet. • Elemente eines Klassendiagramms Klassenname KlasseA Datenkomponenten 1 * KlasseB Funktionskomponenten Klasse Assoziation (bidirektional) KlasseA KlasseA 1 n KlasseB Assoziation (unidirektional) KlasseB KlasseA Vererbung KlasseB 1 1 KlasseB (einfache) Aggregation KlasseA KlasseB KlasseB Komposition (echte Aggregation) Implementierung HOCHSCHULE MUENCHEN FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK FG TECHNISCHE INFORMATIK V – PR–JV – 10/05/3 – TH – 01 ------------------------------------------------------------------------------------------------------------------------------------------------------------ Klassenbeziehungen (3) • Beispiele für einfache Klassendiagramme Person 1..* Manager Projekt * Entwickler 2..n * * 1 Projektleiter 1 1 Firma 1 Auftraggeber TextFilterCtrl 1 TextFileWriter TextFileReader 1 1 1 CharFilter