Programmiersprache Java Objektorientierte Programmierung II Peter Becker FH Bonn-Rhein-Sieg Fachbereich Informatik [email protected] Vorlesung Sommersemester 2008 Vorbemerkungen Allgemeines zur Vorlesung • Homepage zur Vorlesung: http://www2.inf.fh-rhein-sieg.de/˜pbecke2m/progjava/ • Die Vorlesung wird folienbasiert gehalten. • Die Folien zur Vorlesung (Skript) stehen auf der Homepage vor der Vorlesung zur Verfügung. • Format: PDF, ein- und vierseitig • Empfehlung: Bringen Sie die ausgedruckten Folien mit in die Vorlesung und versehen Sie diese dort mit schriftlichen Bemerkungen. • Benutzen Sie zum Drucken bitte die vierseitige Version des Skriptes. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 1 Vorbemerkungen Organisation • • • • Vorlesung V2, Dienstag 11:30 – 13:00 Uhr Bitte beachten Sie die Gruppeneinteilung für die Übungen! Für die Übungen benutzen wir Linux! Die wichtigsten Dinge für diese Veranstaltung: 1. Üben! 2. Üben! 3. Üben! Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 2 Vorbemerkungen Voraussetzungen und Lernziele Voraussetzungen: • Beherrschung des Stoffes “Einführung in die Programmierung” bzw. “Objektorientierte Programmierung I” insbesondere: algorithmische Elemente, abstrakte Datentypen, Rekursion, Java Grundlagen (einfache Datentypen, Kontrollstrukturen, Arrays) Lernziele: Programmieren lernen Kennen der Konzepte der objektorientierten Programmierung Kennen der Sprachkonzepte von Java Selbständig Programme in Java zu anwendungsorientierten Problemstellungen entwicklen können • Beherrschung einer objektorientierten Programmiersprache • • • • Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 3 Vorbemerkungen Prüfung • Modulzuordnung: Computer Science: GPS, 2. Semester Business Information Systems: GINF, 2. Semester • Schriftliche Prüfung in Form einer zweistündigen Klausur • Art der Klausur: Wissensfragen und Programmierung (auf Papier) • Hilfsmittel: keine • 7 Credits • keine weiteren Zulassungsvoraussetzungen? • Bonuspunkte? Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 4 Vorbemerkungen Inhalt 1. Referenzdatentypen, Felder 2. Benutzung von Klassen 3. Konstruktion von Klassen 4. Vererbung 5. Abstrakte Klassen 6. Ausnahmebehandlung (Exceptions) 7. Schnittstellen 8. parametrisierbare Klassen (Generics) 9. Pakete 10. Java Klassenbibliotheken 11. Threads Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 5 Vorbemerkungen Literatur D. Ratz, J. Scheffler, D. Seese Grundkurs Programmieren in Java Band 1: Der Einstieg in Programmierung und Objektorientierung Hanser 2004 • Standardwerk für diese Veranstaltung Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 6 Vorbemerkungen J. Goll, C. Weiß, F. Müller Java als erste Programmiersprache Teuber 2001 • Früheres Standardwerk für diese Veranstaltung Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 7 Vorbemerkungen C. Ullenboom Java ist auch eine Insel Galileo Press 2005 • Sehr umfangreich • Behandelt weiterführende Themen (z.B. Datenbank- und Netzwerkprogrammierung, XML, Grafische Oberflächen). • Geht weit über die Inhalte dieser Veranstaltung hinaus. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 8 Vorbemerkungen Oliver Böhm Java Software Engineering unter Linux SuSE Press 2002 • Vermittelt keine Grundlagen, • enthält dafür alles, was man zur professionellen Softwareentwicklung mit Java unter Linux benötigt. • Ein Buch für das gesamte Studium. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 9 1. Felder und Strings Eigenschaften von Referenzdatentypen 1. Referenzdatentypen: Felder und Strings • Referenzdatentypen sind Konstrukte, mit deren Hilfe wir aus einfachen Datentypen neue “eigene” Typen erzeugen können. • In Java gibt es prinzipiell zwei Arten von Referenzdatentypen: Felder und Klassen. • In diesem Kapitel lernen wir den Umgang mit Feldern und die Benutzung der Klassen String und StringBuffer. • Die Nutzung von Klassen ist Thema des nächsten Kapitels. • Die Definition eigener Klassen ist Thema des übernächsten Kapitels. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 10 1. Felder und Strings Eigenschaften von Referenzdatentypen Referenz- vs. einfache Datentypen • einfacher Datentyp: Eine Speicherzelle, die mit einer Variablen assoziiert ist, enthält den Wert der Variablen (direkter Zugriff). • Referenztyp: Eine Speicherzelle, die mit einer Variablen assoziiert ist, enthält eine Referenz (Verweis, Zeiger) auf eine andere Speicherzelle, die den Wert enthält (indirekter Zugriff). symbolische Adresse b Adresse im Speicher ... Inhalt ... Typ 96 4711 int r ... 104 ... 124 ... ... 124 ... ... Referenz Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 11 1. Felder und Strings Eigenschaften von Referenzdatentypen • Im Unterschied zum einfachen Datentyp wird der Wert 124 der Variablen r nicht als numerischer Wert interpretiert, sondern als Adresse für eine andere Speicherzelle. Dort findet sich der “eigentliche” Wert bzw. unser “eigentliches” Objekt. • Mit der Literalkonstanten null repräsentieren wir eine Referenz, die auf nichts (in das Nirgendwo) verweist. Wenn die Variable r von einem Referenztyp ist, dann sind r = null; ... if ( r == null ) ... gültige Anweisungen. • Grafische Notation für Variablen: b 4711 r Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 12 1. Felder und Strings Einfache Datentypen Rückblick: Einfache Datentypen (1) • Ganzzahlige Datentypen long int short byte Repräsentation: Zweierkomplement • Gleitkommatypen double float Repräsentation: 32 bzw. 64 Bit im IEEE 754 Standard • char Repräsentation: als vorzeichenloser 16-Bit Integerwert • boolean Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 13 1. Felder und Strings Einfache Datentypen Rückblick: Einfache Datentypen (2) Zu einem einfachen Datentyp gehört • sein Wertebereich, • sein lexikalischer Bereich (Literale) und • die zur Verfügung stehenden Operationen. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 14 1. Felder und Strings Einfache Datentypen Einfache Datentypen: Implizite und explizite Typumwandlung • Wir wollen die Addition 92233720366854775000L + 807 durchführen. Der Plus-Operator ist aber nur für Werte des gleichen Typs definiert. Was tun? • Der Java-Compiler erkennt, daß der Datentyp zur linken Zahl einen Wertebereich hat, der den der rechten Zahl umfasst. So wird die 807 vom Compiler in eine automatisch long-Zahl umgewandelt. ☞ implizite Typkonvertierung (implicite typecast) • Implizite Typkonvertierungen treten immer dann auf, wenn ein kleinerer Zahlenbereich in einen größeren Zahlenbereich abgebildet wird. byte → short → int → long char → int float → double • Ganzzahlige Datentypen können auch implizit in Gleitkommatypen umgewandelt werden, obwohl dabei Rundungsfehler auftreten können. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 15 1. Felder und Strings Einfache Datentypen • Die folgende Anweisung dagegen liefert einen Fehler beim Compilieren: int i = 3.0; Grund: 3.0 ist eine double-Zahl. Bei einer Umwandlung nach int geht eventuell Information verloren. • Stattdessen könnten wir eine explizite Typkonvertierung (explicite Typecast) durchführen. Hierzu schreibt man den Zieldatentyp in Klammern vor die entsprechende Zahl oder Ausdruck. (int) 3.14 Hier würde der Compiler die Nachkommastellen abschneiden. • Eine Umwandlung von boolean in einen anderen Datentyp ist nicht möglich. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 16 1. Felder und Strings Eindimensionale Felder Felder • Felder (Arrays) gestatten es, mehrere Variablen durch einen gemeinsamen Namen anzusprechen und lediglich durch einen Index zu unterscheiden. • Alle diese indizierten Variablen haben dabei den gleichen Typ (Komponententyp, Basistyp). • Die Variablen selbst werden als Komponenten des Feldes bezeichnet. • Der Index zum Ansprechen der Komponenten ist vom Typ int. Hierbei sind nur nichtnegative Werte erlaubt. • Wir können uns vorstellen, daß die Komponenten eines Feldes aufeinanderfolgend im Speicher des Rechners abgelegt sind. • Der Index für eine Komponente ergibt sich dabei aus der Position innerhalb des Feldes, von Null aufwärts gezählt. • Beispiel: – Repräsentation eines Vektors des IRn: Feld mit double als Komponententyp Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 17 1. Felder und Strings Eindimensionale Felder Deklaration von Feldern Syntax: Komponententyp[] Variablenname; Beispiele: int[] zahlenfolge; double[] vektor; • Die eckigen Klammern machen deutlich, daß die Variable ein Feld referenziert, mit dem Typ links von [] als Komponententyp. • Als Komponententyp sind nicht nur einfache Datentypen, sondern auch Referenztypen erlaubt. String[] wortliste; • Insbesondere sind auch Feldtypen als Komponententyp erlaubt (hierzu später mehr). Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 18 1. Felder und Strings Eindimensionale Felder Erzeugung von Feldern ☞ Die Deklaration einer Feld-Variablen erzeugt kein Feld! • Nach der Deklaration existiert eine Variable, die eine Referenz auf ein Feld als Wert aufnehmen, kann. • Das eigentliche Feld existiert aber noch nicht. Syntax zur Erzeugung von Feldern: Variablenname = new Komponententyp [ Feldlänge ]; Beispiele: zahlenfolge = new int[20]; vektor = new double[3]; Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 19 1. Felder und Strings Eindimensionale Felder Deklaration und Erzeugung können auch zusammen erfolgen. Beispiele: int[] zahlenfolge = new int[20]; double[] vektor = new double[3]; Bitte beachten Sie: ☞ Die Größe eines Feldes ist nicht Bestandteil des Typs. Sie wird erst bei der Erzeugung des Feldes festgelegt. ☞ Die Feldlänge kann durch einen Ausdruck angegeben werden. ☞ Einer Feld-Variable können jederzeit andere Felder gleichen Typs zugewiesen werden. double[] vektor = new double[3]; double[] vektor2 = new double[7]; vektor = new double[3]; vektor = new double[5]; vektor = vektor2; Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 20 1. Felder und Strings Eindimensionale Felder Komponentenzugriff und -initialisierung double[] vektor = new double[3]; Welchen Wert haben die Feldkomponenten nach der Erzeugung? Dies ist für jeden möglichen Komponententyp festgelegt: • • • • • ganzzahlige Typen: 0 char: ’\0’ double bzw. float: 0.0 bzw. 0.0f boolean: false Referenztyp: null Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 21 1. Felder und Strings Eindimensionale Felder Syntax für die Wertzuweisung an eine Komponente: Variablenname[Index] = Wert; Beispiel: vektor[0] = 1.0; vektor[1] = 2.0; vektor[2] = -1.0; • Index muß ein Ausdruck sein, der einen int-Wert liefert. • Der Wert für Index muß zwischen 0 und Feldlänge-1 liegen. • Wert kann natürlich ein beliebiger Ausdruck passend zum Komponententyp sein. int[] quadratFolge = new int[100]; for (int i=0 ; i<100 ; i++ ) quadratFolge[i] = i*i; Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 22 1. Felder und Strings Eindimensionale Felder Syntax für den Zugriff auf den Wert einer Komponente (als Ausdruck): Variablenname[Index] Beispiel: double summe = 0.0; for (int i=0 ; i<3 ; i++ ) { System.out.println( "vektor[" + i + "] = " + vektor[i] ); summe = summe + vektor[i] * vektor[i]; } System.out.println("Laenge des Vektors: " + Math.sqrt( summe )); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 23 1. Felder und Strings Eindimensionale Felder • Statt Erzeugung mit new und anschließender Initialisierung durch Wertzuweisungen ist auch eine verkürzte Schreibweise möglich. • Beispiele: int[] lottozahlen = { 6, 11, 19, 21, 30, 40 }; double[] vektor = { 1.0, 2.0, -1.0 }; String[] wortliste = { "ganz", "viele", "Wörter" }; • Die geklammerten Ausdrücke heißen Feld-Initialisierer (array initializer). • Nur Literale oder Konstanten dürfen als Wert in einem Feld-Initialisierer auftreten. • Diese Schreibweise ist nur in Verbindung mit einer Deklaration erlaubt, nicht für eine gewöhnliche Zuweisung. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 24 1. Felder und Strings Eindimensionale Felder Ermittlung der Länge von Feldern • Bei der Erstellung eines Feldes wird dessen Länge in einem zusätzlichem Element vom Typ int abgespeichert. • Auf dieses Element kann mit Variablenname.length zugegriffen werden. • Beispiel: double[] vektor = { 1.0, 2.0, -1.0 }; for (int i=0 ; i<vektor.length ; i++ ) System.out.println( "vektor[" + i + "] = " + vektor[i] ); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 25 1. Felder und Strings Eindimensionale Felder Kopieren und Vergleichen von Referenzen double[] v = { 1.0, 3.0, 5.0, 10.0 }; double[] q = v; for ( int i=0 ; i<q.length ; i++ ) q[i] = q[i]*q[i]; for ( int i=0 ; i<q.length ; i++ ) System.out.println( "Das Quadrat von " + v[i] + " ist " + q[i] ); Was wird ausgegeben und warum? Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 26 1. Felder und Strings Eindimensionale Felder • Die Zuweisung q = v; führt zu einer sogenannten Referenzkopie. • Es wird keine Kopie des Feldes angelegt, sondern q erhält die Referenz, die in v hinterlegt ist. • Wirkung: q und v verweisen auf das gleiche Feld. v 1.0 3.0 5.0 10.0 q Für die Herstellung einer “echten Kopie” haben wir die folgenden Möglichkeiten: 1. Wir benutzen eine Schleife: double[] v2 = new double[ v.length ]; for ( int i=0 ; i<v.length ; i++ ) v2[i] = v[i]; Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 27 1. Felder und Strings Eindimensionale Felder 2. Nutzung der Methode System.arraycopy: System.arraycopy( Quelle, QuelleStartIndex, Ziel, ZielStartIndex, Anzahl ); Hierbei ist: • Quelle: Feld, von dem kopiert werden soll • QuelleStartIndex: Index, ab der Quelle übertragen werden soll • Ziel: Feld, in das kopiert werden soll • ZielStartIndex: Index, ab dem die Eintragungen erfolgen sollen • Anzahl: Anzahl der zu kopierenden Komponenten 3. Nutzung der vordefinierten Methode clone. Hierzu später mehr. ☞ Alle diese Möglichkeiten legen eine sogenannte flache Kopie an. ☞ D.h. für die Komponenten selbst wird die übliche Zuweisung durchgeführt. ☞ Dies kann problematisch sein, wenn der Komponententyp selbst eine Referenzdatentyp ist (Referenzkopie). Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 28 1. Felder und Strings Eindimensionale Felder • int[] feld1 = { 1, 2, 3 }; int[] feld2 = { 1, 2, 3 }; • Der Ausdruck feld1 == feld2 liefert false! • Begründung: Bei Referenzdatentypen bedeutet == nicht, daß die Inhalte der referenzierten Felder (oder Objekte) verglichen werden. • Stattdessen wird geprüft, ob die Referenzen die gleiche Speicherstelle adressieren. feld1 1 2 3 feld2 1 2 3 • Wie vergleicht man dann die Inhalte von Feldern? ☞ Ausprogrammieren • Achtung: Der Komponententyp kann wieder ein Referenztyp sein! Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 29 1. Felder und Strings Mehrdimensionale Felder Mehrdimensionale Felder Mehrdimensionale Felder sind vom Prinzip her leicht zu definieren: Der Komponententyp ist selbst ein Feld. Beispiele: double[][] matrix; int[][] folgeVonFolgen; String[][] listeVonStringListen; Sollen alle “inneren” Felder die gleiche Länge aufweisen, können wir wiederum den new-Operator in einfacher Weise verwenden: matrix = new double[3][4]; folgeVonFolgen = new int[3][50]; listeVonStringListen = new String[5][30]; Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 30 1. Felder und Strings Mehrdimensionale Felder Beispiele für “innere” und “äußere” Feldlängen: • matrix.length liefert 3. • matrix[0].length liefert 4. • matrix[2].length liefert ebenfalls 4. Die “inneren” Felder können eine unterschiedliche Länge aufweisen. int[][] folgeVonFolgen = new int[3][]; for ( int i=0 ; i<folgeVonFolgen.length ; i++ ) folgeVonFolgen[i] = new int[10*(i+1)]; • folgeVonFolgen.length liefert 3. • folgeVonFolgen[0].length liefert 10. • folgeVonFolgen[2].length liefert 30. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 31 1. Felder und Strings Datentypen für Strings Der Datentyp String Zeichenketten können in Java mit dem Referenzdatentyp String dargestellt werden. Zeichenketten vom Typ String haben die folgenden wesentlichen Eigenschaften: ☞ Die Länge eines Strings steht mit seiner Erzeugung fest und kann nachträglich nicht geändert werden. ☞ Der Inhalt des Strings kann nicht geändert werden. • Ein String ist eigentlich ein Objekt der Klasse java.lang.String. • Für die Erzeugung kann die Literaldarstellung oder der new-Operator verwendet werden. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 32 1. Felder und Strings Datentypen für Strings Varianten zur Erzeugung von String-Objekten: String s1 = "abc"; String s2 = new String( "abc" ); char[] data = { ’a’, ’b’, ’c’ }; String s3 = new String( data ); byte[] b = { 97, 98, 99 }; String s4 = new String( b ); String s5 = new String( s4 ); Was liefert s1 == s2 oder s5 == s4? Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 33 1. Felder und Strings Datentypen für Strings Die Klasse String beinhaltet Methoden zum • Zugriff auf einzelne Zeichen der Zeichenkette, charAt(int index) • Vergleich von Zeichenketten, compareTo(String vergleichsString) und equals(String vergleichsString) • Suchen von Teil-Zeichenketten, indexOf(String teilString) • Herausgreifen von Teil-Zeichenketten und substring(int von) und substring(int von, int bis) • Wandeln von Groß- in Kleinbuchstaben (und umgekehrt). toLowerCase() und toUpperCase() Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 34 1. Felder und Strings Datentypen für Strings String s1 = "Weihnachten"; String s2 = "Weihnachten"; String s3 = "WEIHNACHTEN"; System.out.println(s1.charAt(4) ); System.out.println(s1.compareTo(s2) ); System.out.println(s1.compareTo(s3) ); System.out.println(s1.equals(s2) ); System.out.println(s1.indexOf("ach")); System.out.println(s1.substring(3)); System.out.println(s1.substring(3,7)); System.out.println(s1.toUpperCase()); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 // // // // // // // // Ausgabe: n Ausgabe: 0 Ausgabe: 1 Ausgabe: true Ausgabe: 5 Ausgabe: hnachten Ausgabe: hnac Ausgabe:WEIHNACHTEN 35 1. Felder und Strings Datentypen für Strings Der Datentyp StringBuffer • Die Klasse StringBuffer erlaubt es uns, mit veränderbaren ZeichenkettenObjekten zu arbeiten. • Wir können beispielsweise zusätzliche Zeichen hinzufügen oder entfernen, ohne das jeweils ein neues Objekt erzeugt wird. • Während String str = ""; for (int i=1; i<=500 ; i++ ) str = str + "x"; insgesamt 500 neue String-Objekte erzeugt, wird in den Zeilen StringBuffer buf = new StringBuffer(); for (int i=1; i<=500 ; i++ ) buf.append("x"); nur ein einziges StringBuffer-Objekt erzeugt und verwendet. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 36 1. Felder und Strings Datentypen für Strings • Vorteil: effizienter • Nachteil: komplexere Programmierung • Konstruktoren von StringBuffer: public StringBuffer() public StringBuffer(String s) • Einige Methoden: – Aktuelle Länge: length() – Etwas an die aktuelle Zeichenkette anhängen: verschiedene Varianten von append, z.B. StringBuffer append(int i) – Einfügen an einer bestimmten Stelle: verschiedene Varianten von insert, z.B. StringBuffer insert(int offset, int i) – Teilstring löschen: StringBuffer delete(int von, int bis) • Genaueres erfahren Sie in der API-Dokumentation. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 37 2. Unterprogramme und Methoden Methoden 2. Unterprogramme und Methoden • Durch Methoden wird ausführbarer Code unter einem Namen zusammengefasst. • Dieser Code kann unter Verwendung von sogenannten Parametern formuliert sein, denen später beim Aufruf der Methode Werte übergeben werden. • Methoden sind ein Bestandteil von Klassen. • Wir befassen uns hier mit einem Spezialfall (Klassenmethoden)! Später noch: Methoden in Verbindung mit Objekten Beispiel: mehrfache Auswertung der Funktion f (x, n) = x2n + n2 − nx Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 38 2. Unterprogramme und Methoden Methoden Deklaration von (Klassen-)Methoden Syntax: public static Rückgabetyp Methodenname ( Parameterliste ) { Anweisungen } • Rückgabetyp Datentyp des Ergebnisses, das die Methode zurückliefern soll. Soll die Methode keinen Wert zurückliefern, verwenden wir hier void. • Methodenname Bezeichner als Name für die Methode. • Parameterliste Eine Kommaliste von Variablendeklarationen. Die darin aufgeführten Variablen heißen formale Parameter. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 39 2. Unterprogramme und Methoden Methoden Parameterübergabe und -rückgabe • Der Methodenkopf für die Berechnung von f (x, n) = x2n + n2 − nx könnte lauten: public static double f(double x, int n) • Für die Ergebnisrückgabe an die aufrufende Umgebung steht die returnAnweisung zur Verfügung. • Durch return ergebnis; wird die Ausführung der Methode beendet und der Wert des Ausdrucks ergebnis als Resultat zurückgegeben. • Hat eine Methode den Rückgabetyp void, so reicht return; Ein solches return als letze Anweisung kann auch entfallen. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 40 2. Unterprogramme und Methoden Methoden • Eine Methode darf mehrere return-Anweisungen enthalten. Für Methoden, die nicht den Rückgabetyp void haben, muß aber sichergestellt sein, daß vor Beendigung der Methode eine return-Anweisung ausgeführt wird. • Beispielmethode komplett: public static double f(double x, int n) { double produkt = 1.0; for (int i=0; i<2*n ; i++) produkt = produkt * x; return produkt + n*n - n*x; } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 41 2. Unterprogramme und Methoden Methoden Aufruf von Methoden Syntax: Methodenname( Parameterliste ) • Ein Methodenaufruf ist ein elementarer Ausdruck. • Parameterliste ist eine Kommaliste von Ausdrücken (aktuelle Parameter). Die Werte der Ausdrücke werden über die formalen Parameter aus der Methodendeklaration an die Methode übergeben. • Nach dem Aufruf wird jeweils der entsprechende Rückgabewert für den ursprünglichen Methodenaufruf eingesetzt und mit diesem Wert weitergearbeitet. double y = f(x,n); double z = f(y,3) + 3*x; • In Java erfolgt die Parameterübergabe stets als Wertparameter (call by value). Es wird stets eine Kopie des Wertes einer Variablen an die Methode übergeben, der Wert der Variablen bleibt unverändert. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 42 2. Unterprogramme und Methoden Methoden Wertparameter Wertparameter ermöglichen die Übergabe von Variablenwerten, aber nicht deren Änderung. Was wird ausgegeben? public static void methode(int a) { a++; } public static void main(String[] args) { int a = 1; methode(a); System.out.println(a); } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 43 2. Unterprogramme und Methoden Methoden Referenzparameter • Referenzparameter ermöglichen nicht nur die Übergabe von Variablenwerten, sondern auch deren Änderung. • Referenzparameter gibt es z.B. in den folgenden Programmiersprachen: Pascal, Modula-2, C++ • Durch die Benutzung von Zeigern (Referenzen) kann ein den Referenzparametern ähnliches Verhalten simulieren. • Aber Vorsicht: Auch die Referenzen sind nur Wertparameter in Java! • Konsequenz: Nur die referenzierten Inhalte können geändert werden, nicht die Referenz selber! Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 44 2. Unterprogramme und Methoden Methoden Referenzparameter (2) Was wird ausgegeben? public static void methode(int[] a) { a[0]++; } public static void main(String[] args) { int[] a = { 1 }; methode(a); System.out.println(a[0]); } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 45 2. Unterprogramme und Methoden Methoden Referenzparameter (3) Was wird ausgegeben? public static void methode(int[] a) { a = new int[] { 2 }; } public static void main(String[] args) { int[] a = { 1 }; methode(a); System.out.println(a[0]); } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 46 2. Unterprogramme und Methoden Methoden Überladen von Methoden In Java darf es auch innerhalb einer Klasse mehrere Methoden mit dem gleichen Namen geben, d.h. Methoden können überladen werden: public static int max(int x, int y) { return (x>y) ? x : y; } public static double max(double x, double y) { return (x>y) ? x : y; } Java unterscheidet Methoden gleichen Namens • anhand der Zahl der Parameter, • anhand des Typs Typs der Parameter und • anhand der Position der Parameter. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 47 2. Unterprogramme und Methoden Methoden Sichtbarkeit und Verdecken von Variablen • Manchmal möchte man eine Variable deklarieren, die in mehr als einer Methode (einer Klasse) bekannt ist. • Wir erreichen dies, indem wir die Variable ausserhalb aller Methoden, aber innerhalb einer Klasse definieren und static davor schreiben. public class MeineKlasse { static String s = "Klassenvariable"; public static void meineMethode1() { System.out.println(s); } public static void meineMethode2() { System.out.println(s); } } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 48 2. Unterprogramme und Methoden Methoden • Solch eine Variable heißt Klassenvariable. • Java erlaubt es aber auch, daß Klassenvariablen und lokale Variablen bzw. Klassenvariablen und formale Parameter den gleichen Namen tragen. • Wenn dies so ist, welche Variable wird dann genommen? • Grundregel für die Sichtbarkeit bzw. das Verdecken: Innerhalb von Methoden verdecken lokale Variablen und formale Parameter die Klassenvariablen gleichen Namens. • Klassenvariablen können trotzdem angesprochen werden. Hierzu stellt man den Klassennamen vor den Variablennamen: Klassenname.Variablenname Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 49 2. Unterprogramme und Methoden public class VerdeckenTest { static int a=1, b=2, c=3; static int m(int a) { int b=20; System.out.println("a = " + a); System.out.println("b = " + b); System.out.println("c = " + c); return 100; } public static void main(String[] args) { int a=1000; System.out.println("a = " + a); System.out.println("b = " + b); System.out.println("m(c) = " + m(c)); } } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 Methoden Ausgabe: a = 1000 b = 2 a = 3 b = 20 c = 3 m(c) = 100 50 2. Unterprogramme und Methoden Methoden Rekursive Methoden Rekursion ist in Java erlaubt, d.h. Methoden dürfen sich selbst aufrufen! Beispiel: Methode zur Berechnung der Fibonacci-Zahlen Fi : F0 = 0, F1 = 1, Fn = Fn−1 + Fn−2 für n ≥ 2 static int fibonacci(int n) { if ( n == 0 ) return 0; if ( n == 1 ) return 1; return fibonacci(n-1) + fibonacci(n-2); } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 51 2. Unterprogramme und Methoden Methoden Die Methode main • Die Hauptmethode main ist nach dem gleichen Schema wie jede andere Methode aufgebaut. public static void main(String[] args) • Der Inhalt von args ergibt sich durch die Argumente, die beim Aufruf des Programms angegeben werden. • Beim Aufruf von > java MeineKlasse Argument1 ... ArgumentN gilt für args in main von MeineKlasse: – args.length hat den Wert N – args[0] hat den Wert Argument1 als String – args[args.length-1] hat den Wert ArgumentN als String Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 52 3. Klassen Klassen als strukturierter Datentyp 3. Klassen Wir werden schrittweise unser Verständnis über Klassen erweitern: • • • • • Klassen als strukturierte Datentypen Klassen als Konzept zur Kapselung Instanzmethoden Konstruktoren statische Komponenten von Klassen Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 53 3. Klassen Klassen als strukturierter Datentyp Was sind Klassen? • selbst definierter Datentyp (Referenzdatentyp) • Modellierung neuer Strukturen • Sammlung von Variablen verschiedener Typen • Beispiel: Adresse • Eine Adresse soll als Ganzes repräsentiert werden können. • Teile des Ganzen haben unterschiedliche Datentypen. • Eine Klasse fasst die Komponentenvariablen zusammen. • Darstellung: UML-Klassendiagramm Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 Adresse name: strasse: hausnummer: postleitzahl: wohnort: mail: kommentar: String String int int String String String 54 3. Klassen Klassen als strukturierter Datentyp Erläuterungen und Terminologie: • Die Komponentenvariablen heißen hier Instanzvariablen. • Eine Klasse kann als Bauplan für Objekte verstanden werden. • Eine konkrete Realisierung einer Klasse K bezeichnen wir als Instanz der Klasse K bzw. als Objekt der Klasse K . • Jedes Objekt verfügt über eine separate Version der Instanzvariablen. • Komponentenvariablen, die in der Deklaration mit static versehen sind, sind dagegen Klassenvariablen (siehe voriges Kapitel). • Klassenvariablen beschreiben üblicherweise eine Eigenschaft der Klasse, nicht eines einzelnen Objektes (später mehr). Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 55 3. Klassen radius farbe Klassen als strukturierter Datentyp radius = 1.0 farbe = blau Kreis a radius = 0.5 farbe = grün Kreis b radius = 1.3 farbe = rot Kreis c class Kreis Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 56 3. Klassen Klassen als strukturierter Datentyp Deklaration von Klassen Syntax zur Deklaration von Klassen (vorläufig): public class Klassenname { Variablendeklaration ... Variablendeklaration } Beispiel: public class Adresse { String name; String strasse; int hausnummer; int postleitzahl; String wohnort; String mail; String kommentar; } 1. Wir schreiben die Deklaration der Klasse Adresse in die Datei Adresse.java. 2. Wir compilieren Adresse.java. Damit entsteht Adresse.class. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 57 3. Klassen Klassen als strukturierter Datentyp 3. Nun steht uns der neue Datentyp Adresse zur Verfügung. Eine Variablendeklaration der Form: Adresse a; ist nun möglich. ☞ Mit Klassen sind stets Referenzdatentypen verbunden. ☞ Also steht uns nach Adresse a; noch kein Objekt der Klasse Adresse zur Verfügung. a Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 58 3. Klassen Klassen als strukturierter Datentyp Instanziierung von Objekten Instanzen einer Klasse erzeugen wir mit dem new-Operator: Variablenname = new Klassenname(); Man bezeichnet dies als Instanziierung. Beispiel: Adresse a; a = new Adresse(); Natürlich ist es auch möglich, Variablendeklaration und Instanziierung in einer Anweisung zu formulieren: Adresse a = new Adresse(); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 59 3. Klassen Klassen als strukturierter Datentyp Initialisierung von Instanzvariablen a name ☞ Instanzvariablen werden (automatisch) initialisiert wie Komponenten von Feldern. Hier: • postleitzahl, hausnummer: 0 • und alle anderen Komponeten: null, da Referenzdatentypen. strasse hausnummer 0 postleitzahl 0 wohnort mail kommentar Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 60 3. Klassen Klassen als strukturierter Datentyp Zugriff auf Instanzvariablen Zugriff auf Komponenten eines Objekts: Variablenname.Komponentenname Beispiele: Adresse a = new Adresse(); a.wohnort = "Musterhausen"; a.postleitzahl = 47111; System.out.println( "PLZ: " + a.postleitzahl ); System.out.println( "Ort: " + a.wohnort ); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 61 3. Klassen Klassen als strukturierter Datentyp Objekte und Referenzen Adresse a; Adresse b; a = new Adresse(); b = a; a name b • Klassen definieren Referenzdatentypen. • Konsequenz: Eine Zuweisung sorgt nicht dafür, dass ein Objekt kopiert wird. • Analog zu Feldern: Nur die Referenz wird kopiert! Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 strasse hausnummer 0 postleitzahl 0 wohnort mail kommentar 62 3. Klassen Klassen als strukturierter Datentyp Felder von Objekten Natürlich können wir auch Felder deklarieren, die als Komponententyp eine Klasse haben. Adresse[] adressen = new Adresse[20]; • Der obige Aufruf erzeugt nur das Feld für die Referenzen auf Instanzen von Adresse, nicht die Objekte selbst. • Die Feldkomponenten sind mit null initialisiert (siehe rechts). • Erzeugung der Objekte: for (int i=0 ; i<20 ; i++) adressen[i] = new Adresse(); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 adressen adressen[0] adressen[1] adressen[2] adressen[17] adressen[18] adressen[19] 63 3. Klassen Prinzipien der objektorientierten Programmierung Prinzipien der objektorientierten Programmierung • Objekte waren bisher nichts weiter als schlichte Datenspeicher, Klassen die Baupläne dieser Datenspeicher. • In der objektorientierten Programmierung wird diese Sicht erweitert. • Objekte sind hier mehr als reine Datenspeicher, sie haben insbesondere: – einen Zustand und – weisen ein bestimmtes Verhalten auf. • Eine Klasse beschreibt die möglichen Zustände und das Verhalten der zugehörigen Instanzen. Sie ist eine abstrakte Beschreibung der Objekte (Instanzen). Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 64 3. Klassen Prinzipien der objektorientierten Programmierung Klassen als Modelle • In der Softwareentwicklung transferieren wir die Problemwelt auf den Computer, indem wir ein Modell der Problemwelt erstellen. • Klassen sind wesentliche Bestandteile dieses Modells. Jede Klasse ist ein Modell für eine Gruppe gleichartiger Objekte der Problemwelt. • Durch die Instanziierung von Klassen erhalten wir Objekte. Diese Objekte stellen auf dem Rechner das Äquivalent zu Gegenständen, Eigenschaften oder Personen unserer Problemwelt dar. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 65 3. Klassen Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 Prinzipien der objektorientierten Programmierung 66 3. Klassen Prinzipien der objektorientierten Programmierung Vorteile des objektorientierten Ansatzes • Ein Entwurf wird in unabhängige Komponenten (Klassen/Objekte) unterteilt, die zusammen das Gesamtsystem bilden. • Wie die Einzelteile eines Modellbaukastens werden diese Objekte zu einer Gesamtheit zusammengefügt. Vorteile: 1. Durch die Aufteilung in (kleine) unabhängige Komponenten wird die Komplexität verringert. 2. Die einzelnen Komponenten können unabhängig entwickelt und getestet werden. 3. Der Anbau weiterer Komponenten ist ohne besondere Probleme möglich. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 67 3. Klassen Prinzipien der objektorientierten Programmierung Grundpfeiler der objektorientierten Programmierung • • • • Generalisierung Vererbung Kapselung Polymorphismus Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 68 3. Klassen Prinzipien der objektorientierten Programmierung Kapselung Grundidee der Kapselung: Vgl. Hinweis auf der Rückseite eines Fernsehgeräts: Gerät steht unter Spannung und darf nur vom Fachmann geöffnet werden! 1. Das Öffnen des Geräts ist für Unbefugte nicht ungefährlich. Wollen Sie sich von einem Fernsehmechaniker am Blinddarm operieren lassen? 2. Normale Benutzer sollten nicht wissen müssen, wie der Fernseher intern funktioniert. Hierzu dienen die Bedienelemente als Schnittstelle zur Außenwelt. In der objektorientierten Programmierung: 1. Der interne Aufbau einer Klasse bleibt verborgen (Data Hiding). Zur Benutzung der Klasse reichen Kenntnisse über deren Schnittstelle. 2. Der interne Aufbau einer Klasse kann geändert werden, ohne dass dies Auswirkungen auf andere Teile der Software hat. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 69 3. Klassen Zugriffsrechte Vom Referenzdatentyp zur Objektorientierung Wir beginnen mit einer einfachen Klasse: /** Diese Klasse repräsentiert Studierende */ public class Student { Student name: nummer: /** Der Name des/der Studierenden */ String name; String int /** Matrikelnummer */ int nummer; } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 70 3. Klassen Zugriffsrechte Ist das Grundprinzip der Kapselung erfüllt? • Haben wir die interne Struktur der Klasse Student von der Schnittstelle nach außen getrennt? • Könnten wir die Instanzvariablen einfach verändern, ohne hiermit Probleme zu verursachen? Nein! Wie können wir dies ändern? • Anpassung der Zugriffsrechte für die Instanzvariablen. • Deklaration von Zugriffsmethoden. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 71 3. Klassen Zugriffsrechte Zugriffsrechte • Data Hiding: Wir wollen unsere Instanzvariablen vor der Außenwelt verstecken. Bisher kann auch von anderen Klassen aus auf die Instanzvariablen von Student zugegriffen werden. • Dies wollen wir verhindern! Hierzu schränken wir die Zugriffsrechte auf die Variablen ein. • Zugriffsrecht wird private, d.h. nur innerhalb von Student ist ein Zugriff auf die Instanzvariablen möglich. Student -name: -nummer: String int public class Student { private String name; private int nummer; } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 72 3. Klassen Zugriffsrechte Die Klasse AndereKlasse enthalte folgende Anweisungen: Student studi = new Student(); studi.name = "Karla Karlsson"; studi.nummer = 4711; Wenn wir versuchen, AndereKlasse zu übersetzen, erhalten wir einen Fehler der Form: Variable name in class Student not accessible from class AndereKlasse. Das heißt, die Zugriffe werden verweigert. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 73 3. Klassen Instanzmethoden Instanzmethoden Wie können wir Daten aus Objekten auslesen oder zuweisen, wenn wir hierzu nicht berechtigt sind? ☞ Wir erweitern die Klasse um Instanzmethoden. • Instanzmethoden sind beim Aufruf stets an ein Objekt gebunden, d.h. wir rufen eine Instanzmethode auf einem Objekt auf. • Innerhalb der Instanzmethode haben wir Zugriff auf alle Instanzvariablen. • Methoden haben ihre eigenen Zugriffsrechte. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 74 3. Klassen Instanzmethoden Syntax der Deklaration von öffentlichen Instanzmethoden: public Rückgabetyp Methodenname ( Parameterliste ) { Anweisungen } Beispiele: public String getName() { return name; } public int getNummer() { return nummer; } public void setName( String name ) { this.name = name; } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 75 3. Klassen Instanzmethoden public void setNummer( int n ) { nummer = n; } Darstellung als Klassendiagramm: Student -name: -nummer: +getName() +getNummer() +setName(String) +setNummer(int) Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 String int String int void void 76 3. Klassen Instanzmethoden Was ist this? • Das Schlüsselwort this liefert innerhalb einer Instanzmethode immer eine Referenz auf das Objekt selbst, d.h. auf das Objekt, auf dem die Methode aufgerufen wurde. • Das Schlüsselwort this wird insbesondere dann benötigt, wenn Instanzvariablen durch Namenskonflikte verdeckt werden. Beispiel: Die Methode setName. Der formale Parameter name verdeckt die Instanzvariable name. • Mittels this.name können wir die Instanzvariable ansprechen. • Der Compiler “denkt ein wenig mit”. Liegen keine Namenskonflikte vor, ist die Angabe von this nicht notwendig. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 "Karla Karlsson" name nummer 4711 this 77 3. Klassen Instanzmethoden Instanzmethoden als erweiterte Funktionalität Wir wollen die Menge der gültigen Matrikelnummern einschränken: Eine Matrikelnummer sei dann gültig, wenn sie fünf Stellen sowie keine führenden Nullen hat und ungerade ist. Hierfür definieren wir eine Methode validateNummer: public boolean validateNummer() { return (nummer >= 10000 && nummer <= 99999 && nummer % 2 != 0); } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 78 3. Klassen Instanzmethoden Wir können nun unsere Methode setNummer so ändern, dass nur noch gültige Matrikelnummern akzeptiert werden. public void setNummer(int n) { int alteNummer = nummer; nummer = n; if (!validateNummer()) nummer = alteNummer; } Bemerkungen: • höhere Funktionalität • ohne Datenkapselung nicht möglich • keine Änderung an der Schnittstelle der Klasse Student Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 79 3. Klassen Instanzmethoden Beispiel: Ausgabe von Objekten auf der Konsole: public String toString() { return name + " (" + nummer + ")"; } Nutzung: Student studi = new Student(); studi.setName("Karla Karlsson"); studi.setNummer(12345); System.out.println(studi.toString()); Prinzipiell reicht sogar die Anweisung: System.out.println(studi); Grund: Jedes Objekt verfügt automatisch über eine Methode toString, die für die Ausgabe genutzt wird. Wir haben hier toString überschrieben. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 80 3. Klassen Statische Komponenten einer Klasse Klassenvariablen • Wir wollen die Zahl der instantiierten Studentenobjekte zählen. • Dies ist jedoch keine Eigenschaft eines einzelnen Objektes. Vielmehr gehört die Eigenschaft zu der Gesamtheit aller Studentenobjekte. • Eigenschaften einer Klasse als Ganzes werden durch Klassenvariablen repräsentiert. Syntax für eine private Klassenvariable: private static Datentyp Variablenname = Initialwert; Beispiel: private static int studZaehler = 0; • Das Schlüsselwort static “macht” eine Variable zur Klassenvariable. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 81 3. Klassen Statische Komponenten einer Klasse • Wenn die Zugriffsrechte nicht dagegen sprechen, erfolgt der Zugriff auf eine Klassenvariable durch: Klassenname.Variablenname • Beispiel: Wäre studZaehler öffentlich, könnte man den Zählerwert wie folgt ausgeben: System.out.println(Student.studZaehler); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 82 3. Klassen Statische Komponenten einer Klasse Klassenmethoden Für das Auslesen des Studentenzählers definieren wir eine öffentliche Klassenmethode: public static int getZaehler() { return studZaehler; } Nutzung der Klassenmethode: System.out.println(Student.getZaehler()); • Eine Klassenmethode ist eine Methode, die der Klasse als Ganzes zugeordnet ist. • In Klassenmethoden ist kein Zugriff auf Instanzvariablen möglich. • Nutzung allgemein: Klassenname.Methodenname( aktuelle Parameter ) Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 83 3. Klassen Statische Komponenten einer Klasse Wie können wir dafür sorgen, dass bei der Erzeugung von Objekten der “Studentenzähler” korrekt erhöht wird? Eine (noch nicht optimale) Möglichkeit: Wir “verpacken” die Erzeugung in einer Klassenmethode: public static Student createStudent() { studZaehler = studzaehler + 1; return new Student(); } Nutzung: Student studi = Student.createStudent(); System.out.println(Student.getZaehler()); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 84 3. Klassen • Warum nicht optimal? Bei älteren Programmen, die new verwenden, wird der Zähler nicht erhöht. • Wie können wir dies abstellen? Verwendung eines sogenannten Konstruktors (siehe folgendes Thema). • Klassenmethoden und Klassenvariablen werden in Klassendiagrammen durch Unterstreichung gekennzeichnet. Statische Komponenten einer Klasse Student -name: -nummer: -studZaehler: +getName() +getNummer() +setName(String) +setNummer(int) +validateNummer() +toString() +getZaehler() +createStudent() Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 String int int String int void void boolean String int Student 85 3. Klassen Statische Komponenten einer Klasse Vereinbarung von Konstanten • Die Vereinbarung von Konstanten erfolgt innerhalb einer Klassendefinition. • Konstanten sind Klassenvariablen. • Durch das Schlüsselwort final wird die nachträgliche Änderung des Variablenwerts verboten. Syntax für die Deklaration öffentlicher Konstanten: public static final Datentyp Variablenname = Initialwert; Beispiel: Definition von Konstanten für Studienfächer: public static final int MATHEMATIK STUDIUM = 1; public static final int INFORMATIK STUDIUM = 2; public static final int BIOLOGIE STUDIUM = 3; ... Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 86 3. Klassen Statische Komponenten einer Klasse • Der Zugriff erfolgt wie bei Klassenvariablen: Klassenname.Variablenname • Es hat sich die Schreibweise durchgesetzt, dass Konstantennamen ausschließlich aus Großbuchstaben und Unterstrichen bestehen. Teilwörter werden durch einen Unterstrich getrennt (siehe Java Code Conventions). • Die Zuweisung an eine als Konstante vereinbarte Variable führt zu einem Fehler bei der Übersetzung. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 87 3. Klassen Konstruktor Konstruktor • Mit Konstruktoren können wir Einfluss auf die Erzeugung eines Objektes nehmen. • Konstruktoren erlauben es, den Anfangszustand von Objekten zu parametrisieren. • Der Aufruf des/der Konstruktoren erfolgt implizit bei Anwendung von new. Syntax für einen öffentlichen Konstruktor: public Klassenname( Parameterliste ) { Anweisungen } • Ein Konstruktor heißt immer so wie die Klasse. • In der Parameterliste werden Argumente vereinbart. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 88 3. Klassen Konstruktor Der einfachste Konstruktor für Student lautet: public Student() { } Allgemein ist dies der Standard-Konstruktor. Er wird automatisch angelegt, wenn kein Konstruktor definiert wurde (aber nur genau für diesen Fall). Wir sollten im Konstruktor dafür sorgen, dass automatisch der Zähler für die Studentenobjekte erhöht wird. public Student() { studZaehler = studZaehler + 1; } Damit ist die Klassenmethode createStudent überflüssig. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 89 3. Klassen Konstruktor Überladen von Konstruktoren • Wie Methoden können auch Konstruktoren überladen werden. • D.h., es darf mehrere Konstruktoren zu einer Klasse geben. • Diese müssen sich aber in ihrer Signatur unterscheiden. Weiterer möglicher Konstruktor für Student: public Student(String name, int nummer) { studZaehler = studZaehler + 1; this.name = name; this.nummer = nummer; } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 90 3. Klassen Konstruktor • Innerhalb eines Konstruktors darf als erste Anweisung ein anderer Konstruktor aufgerufen werden. • Dies erfolgt durch das Schlüsselwort this in Verbindung mit aktuellen Parametern. Beispiel: public Student(String name, int nummer) { this(); this.name = name; this.nummer = nummer; } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 91 3. Klassen Statische Komponenten einer Klasse Der statische Initialisierer • Ein Konstruktor ermöglicht uns eine objektbezogene Initialisierung. • Gibt es auch eine klassenbezogene Initialisierung? • Ja, mit Hilfe des statischen Initialisierers. Syntax: static { Anweisungen } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 92 3. Klassen Statische Komponenten einer Klasse • Ein statischer Initialisierer einer Klasse wird ausgeführt, wenn die Klasse geladen wird. • Eine Klasse wird vom Class Loader der virtuellen Maschinene geladen, wenn sie das erste mal benötigt wird. • Eine Klasse kann mehrere statische Initialisierer haben. Für die Anweisungen gelten die Einschränkungen eines statischen Kontextes: • Nur Zugriff auf statische Komponenten einer Klasse. • Zugriff auf alle (auch private) Teile einer Klasse. • Weitere Einschränkung: Statische Initialisierer haben nur Zugriff auf statische Komponenten, die im Programmcode vor ihnen definiert wurden. ☞ Beispiel zum statischen Initialisierer. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 93 3. Klassen Beispiele Nutzen der Kapselung Wenn wir Programmlogik in einem Objekt kapseln, dann können wir 1. diese Logik mehrmals zur gleichen Zeit einsetzen, Beispiel: gleichzeitig mehrere unabhängige Objekte 2. in unterschiedlichen Zusammenhängen nutzen und Beispiel: Programm zum Zählen von Zeilen, Wörtern und Zeichen 3. komplexere Objekte aus einfacheren Objekten zusammensetzen. Beispiel: Die Klassen TorZaehler und Spielstand ☞ Leichtere Wiederverwendbarkeit Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 94 3. Klassen Beispiele Weitere Beispiele: • Klausuraufgabe Skyline Entwerfen Sie eine Klasse zur Ermittlung der Skyline, die von einer Menge von Gebäuden erzeugt wird. 5 5 0 5 10 15 0 Gebäude Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 5 10 15 Skyline 95 3. Klassen Beispiele – Die Skyline reicht in der Breite von 0 bis B . – Jedes Gebäude wird beschreiben durch ein Tripel (l, h, r) mit 0 ≤ l < r ≤ B . Hierbei ist l der linke Rand, r der rechte Rand und h die Höhe des Gebäudes. – Die oben angegebene Skyline wird beispielsweise durch B = 15 und die Gebäude (3, 3, 10), (6, 6, 12), (11, 4, 14) erzeugt. Die Klasse soll die folgenden Methoden aufweisen: – Ermittlung der Breite B der Skyline. – Einfügen eines Gebäudes (l, h, r) in die Skyline. – Ermittlung der Höhe der Skyline an der Stelle i. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 96 3. Klassen Beispiele • Klausuraufgabe TextSpeicher Definieren Sie eine Klasse für die Speicherung von Texten. Ein Objekt Ihrer Klasse soll bis zu 15 Texte speichern können. Jedem Text ist dabei eine Position im Speicher zugeordnet. Für die Verwaltung der Texte sollen die folgenden Methoden zur Verfügung stehen: – Löschen eines Textes Der Text an Position i wird aus dem Speicher entfernt. Alle Texte mit einer Position größer als i rücken um eine Position nach vorne. – Speichern eines Textes Der Text wird an der ersten freien Position gespeichert. Ist der Textspeicher voll, so wird der an der ersten Position gespeicherte Text gelöscht und der neue Text wird als letzter Text im Speicher abgelegt. – Rückgabe des Textes an Position i Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 97 4. Vererbung Grundlagen der Vererbung 4. Vererbung Wir wollen ein Verwaltungsprogramm für CDs und Videos entwickeln. Wir stellen uns dazu folgende Klassen vor: CD titel: kuenstler: titelanzahl: spielzeit: habIch: kommentar: setzeKommentar(String) gibKommentar() setzeVorhanden(boolean) gibVorhanden() ausgeben() String String int int boolean String void String void boolean void Video titel: regisseur: spielzeit: habIch: kommentar: setzeKommentar(String) gibKommentar() setzeVorhanden(boolean) gibVorhanden() ausgeben() Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 String String int boolean String void String void boolean void 98 4. Vererbung Grundlagen der Vererbung Wenn wir diese beiden Klassen realisieren, stellen wir fest, dass der Quelltext der Klassen weitgehend identisch ist. ☞ siehe Beispiele Nachteile dieses Ansatzes: • erhöhter Aufwand der Erstellung • bei der Wartung von dupliziertem Code sind Änderungen an mehreren Stellen notwendig • Wartung ist fehleranfällig • erhöhter Testaufwand • erhöhter Aufwand bei der Nutzung der Klassen Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 99 4. Vererbung Grundlagen der Vererbung Idee der Vererbung • Statt die beiden Klassen CD und Video unabhängig voneinander zu definieren, definieren wir zuerst eine Klasse, die die Gemeinsamkeiten von CD und Video zusammenfasst. Wir wollen diese Klasse Medium nennen. • Wir definieren dann anschließend, dass eine CD ein Medium ist und ebenso, dass ein Video ein Medium ist. • Schließlich ergänzen wir die Klassen für CD und Video ausschließlich um ihre spezifischen Eigenschaften. ☞ Prinzip der Generalisierung Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 100 4. Vererbung Grundlagen der Vererbung Medium −titel: String −spielzeit: int −habIch: boolean −kommentar: String +void setzeKommentar(String) +String gibKommentar() +void setzeVorhanden(boolean) +boolean gibVorhanden() +void ausgeben() CD −kuenstler: String −titelanzahl: int Video −regisseur: String +String gibRegisseur() +String gibKuenstler() +int gibTitelanzahl() Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 101 4. Vererbung Grundlagen der Vererbung Terminologie • Eine Superklasse (Oberklasse) ist eine Klasse, die von anderen Klassen erweitert wird. • Eine Subklasse (Unterklasse) ist eine Klasse, die eine andere Klasse erweitert. Man sagt auch, dass die Subklasse von der Superklasse erbt. • Vererbung bedeutet, dass die Subklasse alle Datenfelder und Methoden von der Superklasse übernimmt. • Es werden keine Konstruktoren vererbt! • Klassen, die über eine Vererbungsbeziehung miteinander verknüpft sind, bilden eine Vererbungshierarchie. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 102 4. Vererbung Grundlagen der Vererbung Beispiel: Eine Vererbungshierarchie für graphische Elemente. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 103 4. Vererbung Vererbung in Java Vererbung in Java Syntax: public class Unterklasse extends Oberklasse { ... } • Mit Hilfe von extends wird die Oberklasse zu einer Klasse angegeben. • Java unterstützt ausschließlich einfache Vererbung, d.h. wir können höchstens eine Oberklasse angeben. • Ohne Verwendung von extends ist implizit die Klasse java.lang.Object die Oberklasse. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 104 4. Vererbung Vererbung in Java Definition der Oberklasse Medium: Definition der Unterklassen: public class Medium { private String titel; private int spielzeit; private boolean habIch; private String kommentar; ... } public class CD extends Medium { private String kuenstler; private int titelanzahl; ... } public class Video extends Medium { private String regisseur; ... } ☞ In den Unterklassen werden zusätzliche Datenfelder und Methoden deklariert. ☞ Darüberhinaus verfügt ein Objekt der Unterklasse über alle Datenfelder und Methoden der Oberklasse. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 105 4. Vererbung Vererbung in Java Vererbung und Zugriffsrechte • Von einer Unterklasse aus kann man nicht auf die private deklarierten Datenfelder und Methoden der Oberklasse zugreifen. • Daher gibt es zusätzlich den Modifikator (Zugriffsrecht) protected. • Das Zugriffsrecht protected – erlaubt den Zugriff von Unterklassen aus, – erlaubt den Zugriff für Klassen des gleichen Pakets und – verbietet den Zugriff für alle anderen Klassen. • Der Modifikator protected kann nur auf Datenfelder, Konstruktoren und Methoden angewendet werden, nicht auf Klassen. • Datenfelder werden üblicherweise nicht als protected deklariert, da dies die Kapselung schwächen würde. Stattdessen definiert man Zugriffsmethoden die protected oder public sind. • Typischer Einsatz von protected: Bei Hilfsmethoden, die nach außen verborgen werden sollen, für Unterklassen aber hilfreich sein können. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 106 4. Vererbung Vererbung in Java • Der Einsatz von protected ist nur über Paketgrenzen hinweg sinnvoll. • protected wird deutlich seltener als public und private eingesetzt. • Bevor Sie protected verwenden, sollten Sie kritisch prüfen, ob private nicht auch ausreicht. • Man beachte: protected ist weniger restriktiv als die Verwendung keines Modifikators. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 107 4. Vererbung Vererbung in Java Vererbung und Initialisierung • Üblicherweise sorgt ein Konstruktor dafür, dass die Datenfelder eines Objekts nach der Erzeugung in einem vernüftigem Zustand sind. • Wie ist das nun, wenn die Datenfelder durch Vererbung über verschiedene Klassen verteilt sind? • Unter- und Oberklasse bieten Konstruktoren an. • Die Unterklasse kümmert sich in ihrem Konstruktor nur um die Datenfelder, die in der Unterklasse definiert sind. • Damit auch die Datenfelder der Oberklasse korrekt initialisiert werden, rufen wir den Konstruktor der Oberklasse auf. • Der Aufruf des Konstruktors der Oberklasse erfolgt mit dem Schlüsselwort super. • Dieser Aufruf muss stets die erste Anweisung in einem Konstruktor der Unterklasse sein! Ansonsten fügt der Compiler den Aufruf eines parameterlosen Konstruktors für die Oberklasse ein. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 108 4. Vererbung Vererbung in Java public class Medium { private private private private String titel; int spielzeit; boolean habIch; String kommentar; public Medium(String titel, int spielzeit) { this.title = titel; this.spielzeit = spielzeit; this.habIch = false; this.kommentar = ""; } ... } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 109 4. Vererbung Vererbung in Java public class CD extends Medium { private String kuenstler; private int titelanzahl; public CD(String titel, String kuenstler, int stuecke, int spielzeit) { super(title,spielzeit); this.kuenstler = kuenstler; this.titelanahl = stuecke; } ... } ☞ Sie sollten den Konstruktor der Oberklasse auch dann explizit aufrufen, wenn der Compiler diesen Aufruf automatisch einfügen würde. Es ist guter Programmierstil. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 110 4. Vererbung Vererbung in Java Vorteile der Vererbung (bis hierher) • • • • Vermeidung von Quelltext-Duplizierung Wiederverwendung von Quelltext Einfachere Wartung Erweiterbarkeit Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 111 4. Vererbung Subtyping Subtyping und Ersetzbarkeit • Wir wissen: Klassen definieren Datentypen. • Wir haben gelernt: Klassen bilden eine Klassenhierarchie. • Analog zur Klassenhierarchie entsteht somit auch eine Typhierarchie mit Suptypen (Untertypen) und Supertypen (Obertypen). • Der Typ, der durch eine Subklasse definiert wird, ist ein Subtyp des Typs, der durch die Zugeordnete Superklasse definiert wird. Prinzip der Ersetzbarkeit: Objekte von Subtypen können an allen Stellen verwendet werden, an denen ein Supertyp erwartet wird. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 112 4. Vererbung Subtyping Beispiel: Die folgenden Zuweisungen sind zulässig: Medium medium = new Medium(...); Medium cd = new CD(...); Medium video = new Video(...); • Eine Variable kann Objekte halten, deren Typ entweder gleich dem deklarierten Typ der Variablen ist oder ein beliebiger Subtyp des deklarierten Typs ist. • Bei der Zuweisung wird eine implizite Typumwandlung (Cast) durchgeführt. Umgekehrt gilt dies nicht. Die folgenden Anweisungen sind nicht zulässig: CD cd = new Medium(...); Video video = new CD(...); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 113 4. Vererbung Subtyping Dieses Prinzip der Ersetzbarkeit gilt natürlich auch bei einer Parameterübergabe. public class MedienDatenbank { ... public void einfuegen(Medium medium) { ... } } Wir können diese Methode sowohl für Videos als auch für CDs verwenden. MedienDatenbank db = new MedienDatenbank(...); Video video = new Video(...); CD cd = new CD(...); db.einfuegen(video); db.einfuegen(cd); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 114 4. Vererbung Subtyping • Variablen für Objekte sind in Java polymorphe Variablen. • Der Ausdruck polymorph (vielgestaltig) bezieht sich auf die Tatsache, dass eine Variable Objekte verschiedener Typen referenzieren kann. • Polymorphie ist einer der Grundpfeiler der Objektorientierung. • Polymorphie tritt in objektorientierten Sprachen in unterschiedlichen Zusammenhängen auf. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 115 4. Vererbung Überschreiben von Methoden Überschreiben von Methoden Die Methode ausgeben() in der Klasse Medium können wir folgendermaßen definieren: public void ausgeben() { System.out.print(titel + " (" + spielzeit + "Min)"); if (habIch) System.out.println("*"); else System.out.println(); System.out.println(" " + kommentar); } Jetzt erzeugen wir eine Instanz der Klasse CD: CD cd = new CD("Beggar\’s Banquet", "Rolling Stones", 10, 41); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 116 4. Vererbung Überschreiben von Methoden Mit weiteren Anweisungen können wir einen Kommentar angeben und das wir im Besitz der CD ist. Die Anweisung cd.ausgeben(); erzeugt dann die Ausgabe Beggar’s Banquet (41 Min)* Die letzte Platte der Ur-Stones, ein absolutes Meisterwerk! Problem: • Die Ausgabe ist unzureichend. • Es werden nur die allgemeinen Informationen angezeigt, die in der Klasse Medium definiert sind. • Dedizierte CD-Informationen (Künstler, Titelanzahl) fehlen in der Ausgabe. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 117 4. Vererbung Überschreiben von Methoden • Um eine vernüftige Ausgabe für CDs zu erhalten, müssen wir die Methode ausgeben() überschreiben. • Eine Subklasse kann die Implementierung einer Methode überschreiben. • Dazu deklariert die Subklasse eine Methode mit der gleichen Signatur wie in der Superklasse, implementiert diese jedoch mit einem anderen Rumpf. • Bei Aufrufen an Objekte der Unterklasse wird die überschreibende Methode ausgeführt. Ein erster Versuch: public class CD extends Medium { ... public void ausgeben() { System.out.println( " " + kuenstler + ", " + titelanzahl + " Titel"); } } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 118 4. Vererbung Überschreiben von Methoden Die Anweisung cd.ausgeben(); liefert jetzt: Rolling Stones, 10 Titel • Jetzt geben wir nur die CD-spezifischen Information aus, es fehlen die allgemeinen Informationen. • Auf die Instanzvariablen von Medium haben wir aber innerhalb von CD keinen Zugriff. Sollte man diese protected deklarieren? • Nein, u.U. können wir dies auch gar nicht, da wir nicht den Quelltext von Medium besitzen. • Stattdessen nutzen wir super, um innerhalb der überschreibenden Methode, die Methode der Oberklasse aufzurufen. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 119 4. Vererbung Überschreiben von Methoden Ein zweiter Versuch: public class CD extends Medium { ... public void ausgeben() { System.out.print("CD: " + kuenstler + ": "); super.ausgeben(); System.out.println( " " + titelanzahl + " Titel"); } } Jetzt liefert die Anweisung cd.ausgeben();: CD: Rolling Stones: Beggar’s Banquet (41 Min)* Die letzte Platte der Ur-Stones, ein absolutes Meisterwerk! 10 Titel Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 120 4. Vererbung Überschreiben von Methoden • Ein super-Aufruf in einer Methode hat immer folgende Form: super.Methodenname( Parameterliste ); Der Methodenname muss also explizit genannt werden. • Im Gegensatz zu super-Aufrufen in Konstruktoren kann der super-Aufruf in einer Methode an jeder beliebigen Stelle der Methode erfolgen. • Es muss auch kein super-Aufruf erfolgen und wenn dieser nicht erfolgt, wird auch nicht automatisch ein solcher Aufruf generiert. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 121 4. Vererbung Überschreiben von Methoden Vererbung und Methodenausführung Medium medium = new CD("Beggar\’s Banquet", "Rolling Stones", 10, 41); ... medium.ausgeben(); • Welche Methode ausgeben() wird hier ausgeführt? Die der Klasse Medium, weil die Variable medium von diesem Typ ist? ☞ statischer Typ der Variablen medium Die der Klasse CD, weil das Objekt, auf dem ausgeben() aufgerufen wird, eine Instanz von CD ist? ☞ dynamischer Typ der Variablen medium ☞ Entscheidend für die Methodenauswahl ist der dynamische Typ einer Variablen. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 122 4. Vererbung Überschreiben von Methoden Methoden-Polymorphie: • Methodenaufrufe in Java sind polymorph. • Derselbe Methodenaufruf kann zu unterschiedlichen Zeitpunkten verschiedene Methoden aufrufen. • Welche Methode tatsächlich aufgerufen wird, ist abhängig vom dynamischen Typ der Variablen, mit der der Aufruf durchgeführt wird. Weiteres Beispiel: • Wir gehen davon aus, daß die Klassen CD und Video die Methode ausgeben() aus Medium überschreiben. • Dann wird für i==0 die Methode ausgeben() von CD aufgerufen und • für i==1 die Methode ausgeben() von Video. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 Medium m; Medium m1 = new CD(...); Medium m2 = new Video(...); for (int i=0 ; i<2 ; i++) { if (i==0) m = m1; else m = m2; m.ausgeben(); } 123 4. Vererbung Überschreiben von Methoden Wir wollen die Frage, welche Instanzmethode bei einem Methodenaufruf aufgerufen wird, wird noch etwas genauer betrachten. Methodensuche: • Beim Aufruf einer Instanzmethode findet eine sogenannte Methodensuche statt. • Ausgehend vom dynamischen Typ der Variablen wird in der Vererbungshierarchie nach einer Methode gesucht, die auf die Signatur des Aufrufs passt. • Wenn die Klasse des dynamischen Typs keine solche Methode aufweist, wird in der direkten Oberklasse gesucht, usw. • Die Suche endet spätestens in der Klasse Object. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 124 4. Vererbung Die Klasse Object Die Klasse Object • Alle Klassen ohne explizit deklarierte Superklasse haben die Klasse Object als Superklasse. • Object gehört zum Paket java.lang. • Object verfügt über einige vordefinierte Methoden. Diese stehen somit allen Objekten (auch Feldern) zur Verfügung. • Es kann sinnvoll sein, diese Methoden in Unterklassen zu überschreiben, d.h. mit einer anderen Implementierung zu versehen. • Beispiel: toString() • genaueres zu den Methoden: siehe API-Dokumentation Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 125 4. Vererbung Die Klasse Object Die Methode equals public boolean equals(Object vergleichsObjekt) • Äquivalenzrelation auf nicht null Objekten. • Implementierung in Object: return this == vergleichsObjekt; • Überschreiben in Unterklassen: inhaltlicher Vergleich Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 126 4. Vererbung Die Klasse Object Die Methode toString public String toString() • toString() wird implizit aufgerufen, wenn eine Objektreferenz als Parameter der Methode println() verwendet wird. MeineKlasse obj = new MeineKlasse(); System.out.println(obj); • Die Implementierung in Object liefert einen String der Art: Klassenname@Referenz • Hier bietet es sich an, in Unterklassen die Methode toString zu überschreiben. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 127 4. Vererbung Die Klasse Object Die Methode finalize protected void finalize() • • • • Wird vom Garbage Collector auf einem Objekt aufgerufen. Aufruf erfolgt, wenn nicht mehr auf das Objekt zugegriffen werden kann. finalize der Klasse Object macht nichts. Kann in Unterklassen überschrieben werden, um Aufräumarbeiten durchzuführen. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 128 4. Vererbung Die Klasse Object Die Methode hashCode public int hashCode() • • • • • Liefert einen Hash-Wert, d.h. eine nahezu eindeutige Kennung für ein Objekt. Unterstützung für Hashing (siehe 3. Semester, AlgoDat) Selbes Objekt liefert stets den gleichen Hashwert Objekte, für die equals den Wert true liefert, müssen gleichen Hash-Wert haben. Verschiedene Objekte müssen nicht verschiedene Hash-Werte haben. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 129 4. Vererbung Finale Klassen und finale Methoden Finale Klassen • • • • • Der Modifier final schützt Klassen vor Vererbung. public final class Klasse { ... } Wirkung: Von Klasse kann keine Unterklasse abgeleitet werden. Typischerweise sind Klassen, die nur static Definitionen enthalten, final. Beispiel: java.lang.Math Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 130 4. Vererbung Finale Klassen und finale Methoden Finale Methoden • final vor einer Methode schützt diese Methode vor dem Überschreiben. • public final void meineMethode() • Finale Methoden verwendet man, wenn man sicherstellen möchte, dass eine Methode nicht verändert wird, z.B. weil Sie kritisch für den Zustand eines Objektes ist. • class ChessAlgorithm { static final int WHITE = 0; static final int BLACK = 1; ... final int getFirstPlayer() { return ChessAlgorithm.WHITE; } ... } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 131 4. Vererbung Typumwandlungen Sichtbarkeit von Methoden Medium medium = new CD("Beggarś Banquet", "Rolling Stones", 10, 41); System.out.println(medium.gibKuenstler()); Problem: • Das erzeugte CD-Objekt hat zwar eine Methode gibKuenstler(), • diese ist aber über die Variable medium nicht sichtbar. ✎ Compiler liefert Fehler Ursache: Die Variable medium könnte auch Objekte referenzieren, die nicht aus der Klasse CD stammen. ✎ Entscheidend für die Sichtbarkeit ist der statische Typ einer Variablen Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 132 4. Vererbung Typumwandlungen Typumwandlungen Kann man aus dem Medium-Objekt wieder ein CD-Objekt machen? Ja, mit einer expliziten Typumwandlung. Medium medium = new CD("Beggarś Banquet", "Rolling Stones", 10, 41); ... CD cd = (CD) medium; System.out.println(cd.gibKuenstler()); oder kurz System.out.println( ((CD)medium).gibKuenstler() ); • Eine (typischerweise implizite) Typumwandlung von einem Untertyp zu einem Obertyp heißt Upcast. • Eine (nur explizit mögliche) Typumwandlung von einem Obertyp zu einem Untertyp heißt Downcast. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 133 4. Vererbung Typumwandlungen Typumwandlung (2) public class OK { ... } public class UK1 extends OK { ... } public class UK2 extends OK { ... } OK a = new UK1(); OK b = new UK2(); UK1 c = (UK1) a; UK2 d = (UK2) b; UK2 e = (UK2) c; UK2 f = (UK2) a; // // // // // // ok, Upcast ok, Upcast ok, Downcast ok, Downcast Fehler, erkennt Compiler Fehler, ClassCastException Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 134 4. Vererbung Typumwandlungen Typumandlung bei Feldern • Die Typhierarchie setzt sich auf Felder fort. • Wenn OK eine Oberklasse von UK ist, dann ist OK[] ein Obertyp von UK[]. Medium[] medienliste = new CD[20]; medienliste[0] = new CD(...); CD cd = (CD) medienliste[0]; medienliste[1] = new Video(...); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 // // // // ok, Upcast ok, Upcast ok, Downcast Fehler, ClassCastException 135 4. Vererbung Typumwandlungen Der instanceof-Operator • zweistelliger Operator, der einen boolschen Wert liefert • Syntax: Ausdruck instanceof Referenztyp • Liefert true, wenn ein Cast von Ausdruck zu Referenztyp möglich ist. • Hierdurch kann eine ClassCastException vermieden werden. • Auch als Test möglich, um zu prüfen, ob ein Objekt aus einer bestimmten Unterklasse stammt. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 136 4. Vererbung Typumwandlungen Der instanceof-Operator (2) Beispiel: MedienDatenbank db = new MedienDatenbank(...); Medium[] medienliste = db.gibAlleMedien(); CD cd; Video video; for (int i=0 ; i<medienliste.length ; i++) if ( medienliste[i] instanceof CD ) { cd = (CD) medienliste[i]; System.out.println(cd.gibKuenstler()); } else if ( medienliste[i] instanceof Video ) { video = (Video)medienliste[i]; System.out.println(video.gibRegisseur()); } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 137 5. Abstrakte Klassen Beispiel 5. Abstrakte Klassen Angenommen, wir wollen die folgende Klassenhierarchie implementieren: Vogel + singe() Amsel Drossel Fink + singe() + singe() + singe() Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 138 5. Abstrakte Klassen Beispiel Beispiel (2) Vorschlag zur Implementierung: public class Vogel { public void singe() { System.out.println( "Vogel singt: ..."); } } public class Amsel extends Vogel { public void singe() { System.out.println( "Amsel singt: uiuiii..."); } } public class Drossel extends Vogel { public void singe() { System.out.println( "Drossel singt: zwawaaa..."); } } public class Fink extends Vogel { public void singe() { System.out.println( "Fink singt: zrzrrr..."); } } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 139 5. Abstrakte Klassen Beispiel Beispiel (3) Probleme des Implementierungsvorschlags: (1) In der Realität gibt es keinen Vogel, der “nur” ein Vogel ist. Konsequenz: Nur für die Unterklassen sollte eine Instanziierung möglich sein. (2) Ein Programmierer erweitert die Klassenhierarchie um die Klasse Star und vergisst dabei, die Methode singe() zu überschreiben. Dann haben Stare keine Stimme. (3) Wenn (1) erfüllt ist und der Fehler von (2) nicht vorliegt, dann wird die Methode singe() in der Klasse Vogel nie aufgerufen. Wir können Sie aber auch nicht einfach weglassen. Warum nicht? Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 140 5. Abstrakte Klassen Abstrakte Klasse Abstrakte Klasse • Problem (1) können wir lösen, indem wir eine Klasse als abstrakt kennzeichnen. • Zugehöriges Schlüsselwort: abstract public abstract class Klassenname ... { ... } • Von abstrakten Klassen können keine Instanzen erzeugt werden. • Konsequenz: Um Objekte erzeugen zu können, muss mindestens eine Unterklasse zur abstrakten Klasse definiert werden, die nicht mehr abstrakt ist. Für unser Beispiel: • Wenn wir die Klasse Vogel als abstrakt kennzeichnen, können wir nur noch Amseln, Drosseln oder Finken instanziieren. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 141 5. Abstrakte Klassen Abstrakte Klasse Abstrakte Klasse (2) Bemerkungen: • Eine abstrakte Klasse kann Unterklasse einer nicht abstrakten Klasse sein. • Eine abstrakte Klasse kann weitere abstrakte Klassen als Unterklassen haben. • Auch für abstrakte Klassen sollten Konstruktoren definiert werden. Grund: Die Initialisierung von Objekten erfolgt entlang der Klassenhierarchie (siehe Beispiel zu geometrischen Objekten). • Eine Klasse kann nicht gleichzeitig final und abstrakt sein. Konsequenz: Der Compiler verbietet die Kombination von abstract und final im Klassenkopf. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 142 5. Abstrakte Klassen Abstrakte Klasse Abstrakte Klasse in UML Im UML-Klassendiagramm werden abstrakte Klassen durch einen kursiven Klassennamen gekennzeichnet. Vogel + singe() Amsel Drossel Fink + singe() + singe() + singe() Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 143 5. Abstrakte Klassen Abstrakte Methoden Abstrakte Methoden Zur Lösung der Probleme (2) und (3) erklären wir die Methode singe() in der Klasse Vogel zu einer abstrakten Methode. • Die Definition einer abstrakten Methode besteht aus einer Methodensignatur ohne einen Rumpf. • Statt eines Blocks als Rumpf folgt dem Methodenkopf ein Semikolon. • Solch eine Methode wird mit dem Schlüsselwort abstract markiert. • Damit haben wir Problem (3) gelöst: Wir brauchen keine Dummy-Implementierung mehr für die Methode singe(). Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 144 5. Abstrakte Klassen Abstrakte Methoden Abstrakte Methoden (2) Syntax: Modifikator abstract Datentyp Methodenname(Parameterliste); Beispiel: Abstrakte Klasse mit einer abstrakten Methode: public abstract class Vogel { public abstract void singe(); } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 145 5. Abstrakte Klassen Abstrakte Methoden Abstrakte Methoden (3) Die Definition einer abstrakten Methode m in einer Klasse K bedeutet: • Für die Klasse K und somit auch für alle Unterklassen U von K ist die Methode m bekannt. • K möchte/kann die Methode nicht implementieren. Dies muss in einer Unterklassen U von K erfolgen. • Sei U eine (nicht abstrakte) Unterklasse von K . Dann ist folgendes möglich. K k = new U(); k.m(); • Die Methodenauswahl basiert auf dem dynamischen Typ von k. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 146 5. Abstrakte Klassen Abstrakte Methoden Abstrakte Methoden in UML Im UML-Klassendiagramm werden abstrakte Methoden durch eine kursive Schreibweise gekennzeichnet. Vogel + singe() Amsel Drossel Fink + singe() + singe() + singe() Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 147 5. Abstrakte Klassen Abstrakte Methoden Zusammenspiel: Abstrakte Klassen und Methoden • Abstrakte Klassen dürfen abstrakte Methoden anbieten. • Jede Klasse, die mindestens eine (evtl. geerbte) abstrakte Methode hat, ist selbst abstrakt und muss entsprechend deklariert werden. • Damit eine Unterklasse einer abstrakten Klasse eine konkrete Klasse werden kann, muss sie Implementierungen für alle geerbten abstrakten Methoden anbieten. • Andernfalls ist die Unterklasse selbst abstrakt und muss als solche gekennzeichnet werden. Damit haben wir Problem (2) gelöst: • Wenn der Programmierer der Klasse Star vergessen sollte, die Methode singe() zu überschreiben, meldet der Compiler einen Fehler. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 148 5. Abstrakte Klassen Abstrakte Methoden Wann setzt man abstrakte Klassen ein? • • • • Zur Repräsentation einer Generalisierung verschiedener Klassen, die Eigenschaften der gleichen Art haben, die in den Unterklassen aber unterschiedlich berechnet werden müssen. Für die Berechnung der Eigenschaften sehen wir in der Generalisierung jeweils eine abstrakte Methode vor, um deutlich zu machen, dass Objekte dieser Klasse (und der Unterklasse) diese Eigenschaften haben. • Die jeweilige Art und Weise der Berechnung wird aber erst in den Unterklassen festgelegt. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 149 5. Abstrakte Klassen Weitere Beispiele Beispiel: Roboter mit spezialisiertem Verhalten • Übungsaufgabe der kommenden Woche. • Roboter mit Positionsbestimmung in ganzzahligem Koordinatensystem • Steuerungsmöglichkeiten für solche Roboter: Einen Schritt in aktuelle Richtung, Änderung der Richtung durch Drehung um 90 Grad nach links bzw. rechts. • In Unterklassen soll für die Roboterklassen ein spezifisches Verhalten festgelegt werden: Sie erhalten einen Auftrag. • Zugehörige Methode: bewegeDich() • Torkelnder Roboter: Ziellos, zufällige Schritte und Drehungen • Wächter: Geht ein vorgegebenes Rechteck ab. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 150 5. Abstrakte Klassen Weitere Beispiele Beispiel: Roboter mit spezialisiertem Verhalten (2) Roboter int gibX() int gibY() void macheSchritt() void dreheLinks() void dreheRechts() void bewegeDich() TorkelnderRoboter void bewegeDich() Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 WächterRoboter void bewegeDich() 151 5. Abstrakte Klassen Weitere Beispiele Beispiel: Geometrische Objekte und deren Eigenschaft Fläche GeoObjekt double x double y double gibX() double gibY() void verschiebe(double,double) double berechneFlaeche() Rechteck Kreis double breite double hoehe double radius double berechneFlaeche() double berechneFlaeche() Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 152 5. Abstrakte Klassen Weitere Beispiele Beispiel: Geometrische Objekte und deren Eigenschaft Fläche (2) public abstract class GeoObjekt { private double x; private double y; public GeoObjekt(double x, double y) { this.x = x; this.y = y; } ... public abstract double berechneFlaeche(); } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 153 5. Abstrakte Klassen Weitere Beispiele Beispiel: Geometrische Objekte und deren Eigenschaft Fläche (2) public class Rechteck extends GeoObjekt { private double hoehe; private double breite; public Rechteck(double x, double y, double hoehe, double breite) { super(x,y); this.hoehe = hoehe; this.breite = breite; } public double berechneFlaeche() { return this.hoehe*this.breite; } } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 154 5. Abstrakte Klassen Weitere Beispiele Beispiel: Geometrische Objekte und deren Eigenschaft Fläche (3) public class Kreis extends GeoObjekt { private double radius; public Kreis(double x, double y, double radius) { super(x,y); this.radius = radius; } public double berechneFlaeche() { return Math.PI * this.radius * this.radius; } } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 155 6. Exceptions Prinzipien von Exceptions 6. Exceptions Hintergrund: Programmieren auf der Basis von Verträgen Kundenklasse Lieferantenklasse Methodenaufruf Methodendefinition • Verpflichtung zur Einhaltung der Vorbedingung • Vorbedingung • Invarianten • Verpflichtung zur Einhaltung der Nachbedingung ☞ Ausnahme: Lieferantenklasse kann ihren Vertrag nicht erfüllen. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 156 6. Exceptions Prinzipien von Exceptions Beispiel: public static void main(String[] args) { int a = Integer.parseInt(args[0]); int b = Integer.parseInt(args[1]); System.out.println(a + "/" + b + "=" + (a/b)); } • java ExceptionTest ☞ ArrayIndexOutOfBoundsException • java ExceptionTest 4 0 ☞ ArithmeticException • java Exception 4 a ☞ NumberFormatException Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 157 6. Exceptions Prinzipien von Exceptions Exception • Eine Exception ist ein Objekt, das Informationen über einen Programmfehler enthält. • Eine Exception wird ausgelöst, um zu signalisieren, daß ein Fehler aufgetreten ist. Vorteile von Exceptions: • Für einen Klienten/Kunden ist es (fast) unmöglich, eine aufgetretene Exception zu ignorieren und einfach weiterzuarbeiten. • Wenn der Kunde die Exception nicht behandelt, dann wird die laufende Anwendung beendet. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 158 6. Exceptions Prinzipien von Exceptions Die Klasse java.lang.Exception • Exceptions sind Instanzen der Klasse java.lang.Exception • bzw. einer Unterklasse von java.lang.Exception. • java.lang.Exception definiert die folgenden Methoden: – getMessage(): Rückgabe der Fehlermeldung – printStackTrace(): Erzeugt die Ausgabe, die wir auf der Konsole sehen. Klassenname Fehlermeldung java.lang.ArithmeticException: / by zero at ExceptionTest.main(ExectionTest.java:8) Ort des Auftretens Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 159 6. Exceptions Prinzipien von Exceptions Exception-Klassen • Eine Exception ist immer eine Instanz aus einer speziellen Vererbungshierarchie. • Wir können selbst neue ExceptionTypen definieren, indem wir Subklassen dieser Hierarchie erzeugen. • Subklassen von java.lang.Error sind für Fehler des Laufzeitsystems vorgesehen. • Für neue Exceptions erzeugen wir Subklassen von java.lang.Exception. • Das Paket java.lang stellt bereits eine Reihe von vordefinierten Subklassen bereit. Throwable Error Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 Exception GeprüfteException RuntimeException UngeprüfteException 160 6. Exceptions Prinzipien von Exceptions Arten von Exceptions • Java unterscheidet zwischen geprüften und ungeprüften Exceptions. • RuntimeException und alle Subklassen der Klasse RuntimeException definieren ungeprüfte Exceptions. • Alle anderen Subklassen von Exception definieren geprüfte Exceptions Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 161 6. Exceptions Prinzipien von Exceptions Ungeprüfte Exceptions • Dies sind Exceptions, bei deren Verwendung der Compiler keine zusätzlichen Überprüfungen vornimmt. • Genauer: Der Compiler prüft nicht, ob sich der Klient um diese Exceptions kümmert. • Ungeprüfte Exceptions sind für Fälle gedacht, die normalerweise nicht auftreten sollten, z.B. ausgelöst durch Programmfehler. Beispiele: ArrayIndexOutOfBoundsException NullPointerException NumberFormatException Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 162 6. Exceptions Prinzipien von Exceptions Geprüfte Exceptions • Geprüfte Exceptions sind Exception-Typen, bei deren Verwendung der Compiler zusätzliche Überprüfungen durchführt und einfordert. • Ein Klient muß sich um geprüfte Exceptions kümmern. • Geprüfte Exceptions sind für die Fälle gedacht, bei denen ein Klient damit rechnen sollte, daß eine Operation fehlschlagen kann. Hier sollte der Klient gezwungen werden, sich um den Fehler zu kümmern. Beispiele: java.io.IOException, java.io.FileNotFoundException Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 163 6. Exceptions Prinzipien von Exceptions Faustregeln Verwende ungeprüfte Exceptions ... ☞ ... in Situationen, die zu einem Programmabbruch führen sollten. ☞ ... bei Fehlern, die vermeidbar gewesen wären. Verwende geprüfte Exceptions ... ☞ ... bei Fehlern, die vom Klienten behandelt werden können. ☞ ... für Fälle, die außerhalb des Einflusses des Programmierers liegen. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 164 6. Exceptions Behandlung von Exceptions Auswirkungen einer Exception • Wenn eine Exception ausgelöst wird, wird die Ausführung der auslösenden Methode sofort beendet. • In diesem Fall muß kein Wert von der Methode zurückgeliefert werden, auch wenn die Methode nicht als void deklariert wurde. • Die Anweisung, die die auslösende Methode aufgerufen hat, konnte nicht korrekt abgearbeitet werden. • Es besteht die Möglichkeit, solche Exceptions zu fangen. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 165 6. Exceptions Behandlung von Exceptions Kontrollfluß bei Exceptions Klasse obj = new Klasse(); try { . . . obj.method(); . . . } catch ( MeineException e ) { /* spez. F.-Behandlung */ } catch ( Exception e ) { /* allg. F.-Behandlung */ } finally { /* Aufräumarbeiten */ } public class Klasse { public void method() throws MeineException { . . . /* Fehlererkennung */ throw new MeineException("..."); . . . } } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 166 6. Exceptions Behandlung von Exceptions Die Konstrukte try, catch, finally, throw und throws try Definiert einen Block, innerhalb dessen Exceptions auftreten können. catch Definiert einen Block, der die Fehlerbehandlung für eine Ausnahme durchführt. finally Definiert einen Block, der Aufräumarbeiten durchführt. throw Erzeugt eine Ausnahme. throws Deklariert eine Ausnahme für eine Methode. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 167 6. Exceptions Behandlung von Exceptions Geprüfte Exceptions: throws • Der Compiler fordert, daß eine Methode, die eine geprüfte Exception auslösen (oder weiterreichen) kann, dies im Methodenkopf angibt. • Syntaxbeispiel: public void speichereInDatei(String dateiname) throws IOException • Die Angabe von ungeprüften Exception-Typen ist erlaubt aber nicht erforderlich. • Hinter throws können durch Komma getrennt auch mehrere Exception-Typen angegeben werden. public void kopiereDatei(String quelldatei, String zieldatei) throws FileNotFoundException, IOException Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 168 6. Exceptions Behandlung von Exceptions Exception-Handler • Ein Exception-Handler ist ein Programmabschnitt, der Anweisungen schützt, in denen eine Exception auftreten könnte. • Der Exception-Handler definiert Anweisungen zur Meldung oder zum Wiederaufsetzen nach einer aufgetretenen Exception. Syntax: try { geschützte Anweisungen } catch (ExceptionTyp e) { Anweisungen für die Behandlung } finally { Anweisungen für alle Fälle } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 169 6. Exceptions Behandlung von Exceptions Zwang zur Behandlung von Exceptions • Eine Anweisung innerhalb der Methode caller ruft eine Methode method auf, die eine Exception E auslösen kann. • Dann muss für caller folgendes gelten: Entweder in caller wird E durch einen Exception-Handler behandelt oder caller macht mittels throws deutlich, daß die Exception E weitergereicht wird. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 170 6. Exceptions Behandlung von Exceptions Propagierung von Exceptions • Eine ausgelöste Exception wird entlang der Aufrufhierarchie propagiert, bis ein geeigneter Exception-Handler gefunden wurde. • Ein Exception-Handler ist geeignet, wenn er eine catch-Klausel hat, deren Exception-Typ gleich E ist oder ein Obertyp davon. • Die catch-Klauseln werden daraufhin in der Reihenfolge geprüft, wie sie im Quelltext angegeben sind. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 171 6. Exceptions Behandlung von Exceptions Auswahl des catch-Blocks ☞ Die erste passende catch-Klausel wird genommen! falsch: try { ... } catch (Exception e) { ... } catch (SpezielleException se) { ... } richtig: try { ... } catch (SpezielleException se) { ... } catch (Exception e) { ... } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 172 6. Exceptions Behandlung von Exceptions Aufräumen mit finally • Nach einer oder mehreren catch-Klauseln kann optional eine finally-Klausel folgen. • Die Anweisungen im Block zu finally werden immer ausgeführt: – Wenn keine Exception ausgelöst wurde, – wenn eine Exception ausgelöst und durch eine catch-Klausel behandelt wird und auch – wenn eine Exception ausgelöst wird, für die keine passende catch-Klausel vorhanden ist. • Typische Anwendung: Freigabe von Ressourcen unabhängig vom Auftreten eines Fehlers. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 173 6. Exceptions Behandlung von Exceptions Auslösen von Exceptions: throw • • • • Exceptions werden mit Hilfe der throw-Klausel ausgelöst. Hinter throw gibt man ein Exception-Objekt an. Typischerweise erzeugt man hierzu ein neues Exception-Objekt mittels new. Als Konstruktorparameter ist eine Fehlermeldung zulässig. Beispiel: throw new Exception("Parameterwert ungueltig!"); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 174 6. Exceptions Exceptions und Vererbung Maßgeschneiderte Exceptions public class MyException extends Exception { ... public MyException() { super(); } public MyException(String msg) { super( msg ); } ... } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 175 6. Exceptions Exceptions und Vererbung Exceptions und Vererbung Klasse Exception public void method() throws IOException; Unterklasse public void method() IOException OtherException throws IOException, OtherException; Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 176 6. Exceptions Exceptions und Vererbung Exceptions und Vererbung (2) • Durch die throws-Klausel werden Exceptions Bestandteil des Vertrages zwischen Klient und Lieferant. • Sie regeln, was im Falle einer Vertragsverletzung passiert. ☞ Prinzip der Ersetzbarkeit: Spezialisierte Methoden dürfen keine neuen Exceptions definieren. • Konsequenz: Das Design der vorangegangenen Folie ist in Java nicht erlaubt! • Korrekte Lösung: method() der Unterklasse darf nur IOException auslösen. • Und wenn method() der Unterklasse andere Methoden benutzt, die andere Exceptions als IOException auslösen können? Dann muss method() der Unterklasse diese Exceptions fangen und auf IOException umsetzen. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 177 6. Exceptions Assertions Assertions • Seit Java 1.4 gibt es in Java das Konzept der Zusicherung (assertion). • Eine Zusicherung ist ein Ausdruck, mit dem Einschränkungen definiert werden, welche die erlaubten Zustände oder das Verhalten von Objekten betreffen. • Zusicherungen gehören eigentlich zur Spezifikation bzw. Modellierung. • Zusicherungen im Quelltext prüfen, ob die Spezifikation verletzt ist (Korrektheit). • Verletzte Zusicherungen lösen in Java ein Objekt vom Typ AssertionError aus (Untertyp von Error). • Eine verletzte Zusicherung heißt: Das Programm bzw. die Klasse erfüllt nicht die geforderte Spezifikation. • Man beachte: Error statt Exception! Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 178 6. Exceptions Assertions Zusicherungen im Quelltext • Die Überprüfung erfolgt mit dem Schlüsselwort assert: assert boolscher-Ausdruck; • Liefert der boolesche Ausdruck den Wert true, wird das Programm fortgesetzt. • Ansonsten wird ein AssertionError ausgelöst. • Optional kann für eine Assertion ein Fehlertext angegeben werden: assert boolscher-Ausdruck : String-Ausdruck; Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 179 6. Exceptions Assertions Zusicherungen compilieren und aktivieren • Sie benötigen eine Java-Version ≥ 1.4. • Damit Kompatibilität zu älteren Versionen gewahrt ist, müssen Assertions für den Compiler durch die Option -source aktiviert werden: javac -source 1.4 Klasse.java • Assertions sind auch in der virtuellen Maschine standardmäßig nicht aktiviert. Mit Hilfe der Option -ea werden die Assertions aktiviert. java -ea Klasse Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 180 7. Schnittstellen Grundlagen zu Schnittstellen 7. Schnittstellen Eine Schnittstelle (Interface) ist eine Spezifikation eines Typs in Form eines Typnamens und einer Menge von Methoden, die keine Implementierungen für die Methoden definiert. • Wir können Schnittstellen anschaulich zunächst als rein abstrakte Klassen auffassen, d.h. alle Methoden dieser Klasse sind abstrakt. Was bringt uns das? • Vollständige Trennung von Spezifikation und Implementierung • Verschiedene Implementierungen der gleichen Spezifikation • Austauschbarkeit Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 181 7. Schnittstellen Grundlagen zu Schnittstellen Beispiel CD-Spieler: Exemplare eines abstrakten Konzepts Spezifikation ☞ einheitliche Signatur ☞ vereinbartes Verhalten Datenabstraktion ☞ Trennung von Verhalten und Implementierung ☞ Verstecken der Implementierung Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 182 7. Schnittstellen Grundlagen zu Schnittstellen Schnittstelle: Syntax Definition einer öffentlichen Schnittstelle: public interface InterfaceName { Konstantendeklarationen Methodenköpfe } Beispiel: public interface BewegbaresDing { void bewegeDich(); } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 183 7. Schnittstellen Grundlagen zu Schnittstellen Weitere Beispiele public interface Funktion { boolean istImDefBereich(double x); double wert(double x); String gibBeschreibung(); } public interface Woerterbuch { void put(String key, String info); String get(String key); void remove(String key); } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 184 7. Schnittstellen Grundlagen zu Schnittstellen Eigenschaften von Schnittstellendefinitionen • Im Kopf wird statt class das Schlüsselwort interface verwendet. • Alle Methoden in einem Interface sind abstrakt: – es sind keine Methodenrümpfe zugelassen, – das Schlüsselwort abstract wird nicht angegeben. • Interfaces enthalten keine Konstruktoren. • Alle Methoden sind implizit public: – die Sichtbarkeit muss nicht angegeben werden. • Als Datenfelder können nur öffentliche Konstanten deklariert werden: – die Festlegung als Konstante durch public, static und final muss nicht erfolgen. Sichtbarkeit für Interfaces: nur public oder default ist möglich Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 185 7. Schnittstellen Grundlagen zu Schnittstellen Schnittstellen und Datentypen (1) • Wenn die Schnittstellendefinition in einer Datei mit der Endung .java liegt, können wir die Schnittstellendefinition wie eine Klasse übersetzen. • Analog zu einer Klasse entsteht durch die Kompilierung auch eine .class-Datei. • Ebenso wie bei Klassen ist mit jeder Schnittstelle auch ein Datentyp verbunden. • Da Schnittstellen aber abstrakt sind, können zu Schnittstellen nicht direkt Objekte erzeugt werden, d.h. new Schnittstelle(...); ist nicht möglich. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 186 7. Schnittstellen Grundlagen zu Schnittstellen Implementierung von Interfaces • Klassen können Interfaces implementieren. • Hierzu müssen unsere Klassen folgendes leisten: 1. Für jede Methode des Interfaces muss die Klasse eine Methode mit passender Signatur anbieten. 2. Im Kopf der Klasse müssen wir explizit angegeben, dass unsere Klasse das Interface implementiert. Hierzu nutzen wir das Schlüsselwort implements. Syntax: public class Klasse extends Oberklasse implements Schnittstelle { ... } Beispiele: ☞ Funktionen Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 187 7. Schnittstellen Grundlagen zu Schnittstellen Implementierung mehrerer Interfaces • Wenn wir extends zusammen mit implements nutzen, muss extends vor implements stehen. • Es ist möglich, dass eine Klasse mehr als eine Schnittstelle implementiert. • In diesem Fall geben wir alle implementierten Schnittstellen als Kommaliste hinter implements an. public class Klasse extends Oberklasse implements Schnittstelle1, Schnittstelle2, ... { ... } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 188 7. Schnittstellen Einsatz von Interfaces Schnittstellen und Datentypen (2) • Ein Interface definiert genau wie eine Klasse einen Datentyp. • Klassen, die ein Interface implementieren, definieren Subtypen des Interface-Typs. • Somit können Variablen von einem Interface-Typ deklariert werden, obwohl keine Objekte dieses Typs existieren können, sondern nur Objekte der Subtypen. • Wenn die Klasse Parabel die Schnittstelle Funktion implementiert: Funktion f = new Parabel( 1.0, 0.0, 0.0 ); • Auch möglich: Funktion[] f = new Funktion[10]; • Damit haben wir wieder Variablen- und Methodenpolymorphie. Beispiel: ☞ Funktionen Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 189 7. Schnittstellen Einsatz von Interfaces Schnittstellen und Vererbung • Schnittstellen besitzen analog zu Klassen die Möglichkeit, mit dem Schlüsselwort extends eine schon vorhandene Schnittstelle zu erweitern. • public interface NachrichtenQuelle { public int SPORT = 0; public int POLITIK = 1; public int KULTUR = 2; public int ANZEIGEN = 3; public int GESAMT = 4; public void anmelden(NachrichtenEmpfaenger empf, int typ); public void sendeNachricht(String nachricht); } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 190 7. Schnittstellen Einsatz von Interfaces • public interface Vermittler extends NachrichtenQuelle { public void empfangeNachricht(String nachricht); } • Wirkungsweise: Die Definition der Unterschnittstelle erbt die Definitionen der Oberschnittstelle. • Hierbei muss wieder das Prinzip der Ersetzbarkeit gewährleistet bleiben. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 191 7. Schnittstellen Einsatz von Interfaces Mehrfachvererbung bei Schnittstellen • Im Gegensatz zur Einfachvererbung von Klassen ist in Java bei Schnittstellen eine Mehrfachvererbung erlaubt. • Damit kann eine Schnittstelle gleichzeitig mehrere andere Schnittstellen erweitern. • public interface Schnittstelle extends Oberschnittstelle1, Oberschnittstelle2, ... { ... } • Die Mehrfachvererbung bei Schnittstellen hat allerdings keine große Bedeutung. Die Möglichkeit, dass eine Klasse mehrere Schnittstellen implementiert, ist von erheblich größerer Bedeutung. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 192 7. Schnittstellen Einsatz von Interfaces Mehrdeutigkeiten bei Mehrfachvererbung public interface Grundfarben { int ROT = 1; int GRUEN = 2; int BLAU = 3; } public interface Sockenfarben extends Grundfarben { int SCHWARZ = 10; int LILA = 11; } public interface Hosenfarben extends Grundfarben { int LILA = 11; int SCHWARZ = 20; int BRAUN = 21; } public interface Allefarben extends Sockenfarben, Hosenfarben { int BRAUN = 30; } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 193 7. Schnittstellen Einsatz von Interfaces • Konstanten werden vererbt: Allefarben.ROT • Konstanten dürfen überschrieben werden: Allefarben.BRAUN • Bei der Nutzung der Konstanten dürfen keine Mehrdeutigkeiten auftreten: Allefarben.SCHWARZ • Auch keine potentiellen Mehrdeutigkeiten: Allefarben.LILA Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 194 7. Schnittstellen Einsatz von Interfaces Typumwandlungen (Upcasting) Implizite Upcasts finden in folgenden Fällen statt: • Von Klasse U nach Klasse O , wenn U Unterklasse von O ist. • Von Klasse K nach Schnittstelle S, wenn die Klasse K die Schnittstelle S implementiert. • Von null zu jeder Klasse, Schnittstelle oder Feld. • Von Schnittstelle U nach Schnittstelle S, wenn U Unterschnittstelle von S ist. • Von Schnittstelle oder Feld zu Object. • Von Feld S[ ] mit Schnittstelle S als Komponententyp nach Feld T[ ] mit Typ T als Komponententyp, wenn es einen Upcast von S nach T gibt. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 195 7. Schnittstellen Einsatz von Interfaces Typumwandlungen (Downcasting) • • • • Die Verwendung von instanceof ist auch mit Schnittstellentypen möglich. Downcasting ist nur explizit möglich. Wird zur Laufzeit auf Korrektheit geprüft. ClassCastException falls Downcasting fehlerhaft Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 196 7. Schnittstellen Einsatz von Interfaces Schnittstellen und Exceptions Zur Schnittstellendefinition gehört auch die Deklaration von möglichen Exceptions. Folgende Konstruktion ist nicht erlaubt: interface Schnittstelle { void methode(); } public class Klasse implements Schnittstelle { public void methode() throws Exception { ... } } ☞ Widerspruch zum Prinzip der Ersetzbarkeit. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 197 7. Schnittstellen Einsatz von Interfaces Lösung: Die Exception muss in der Schnittstelle deklariert werden! interface Schnittstelle { void methode() throws Exception; } • Damit dürfte die Implementierung von methode() Exception oder eine beliebige Unterklasse von Exception auslösen. • Nicht gestattet: allgemeinere Exceptions oder weitere Exceptions Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 198 7. Schnittstellen Einsatz von Interfaces Beispiel: CharSequence Siehe: C. Ullenboom, Java ist auch eine Insel, Abschnitt 6.10.7 Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 199 7. Schnittstellen Einsatz von Interfaces Interfaces als Spezifikation • Interfaces erlauben es, die Funktionalität (Schnittstelle) vollständig von der Implementierung zu trennen. • Gute Beispiele hierfür findet man im Java SDK. • Im Paket java.util findet man beispielsweise das Interface List sowie die Klassen ArrayList und LinkedList. • Das Interface List definiert die volle Funktionalität einer Liste, ohne eine Implementierung festzulegen. • Die Klassen ArrayList und LinkedList stellen verschiedene Implementierungen dieser Schnittstelle dar. • Die beiden Implementierungen unterscheiden sich erheblich in der Effizienz einiger Methoden. • Welche Implementierung für eine gegebene Anwendung besser ist, ist im voraus oft schwer festzulegen. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 200 7. Schnittstellen Einsatz von Interfaces • Das Interface List macht aber solch eine Listenanwendung flexibel. • Wenn wir stets List für Datentypen bei den entsprechenden Variablen und Parametern verwenden, funktioniert die Anwendung unabhängig von der aktuell gewählten Implementierung für Listen. • Lediglich bei der Erzeugung einer Liste müssen wir eine Implementierung festlegen. Beispiel: private Liste meineListe = new ArrayList(); • So könnten wir in der Anwendung eine LinkedList verwenden, indem wir einfach an dieser einen Stelle ArrayList durch LinkedList ersetzen. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 201 7. Schnittstellen Einsatz von Interfaces Markierungsschnittstelle (Marker Interface) • Eine Markierungsschnittstelle enthält in Ihrer Definition keine Methoden. • Implementierende Klassen geben über eine Markierungsschnittstelle an, dass sie eine bestimmte Funktionalität unterstützen. • Beispiel: java.lang.Cloneable • Objekte aus Klassen, die Cloneable nicht implementieren, können nicht kopiert werden. ☞ CloneNotSupportedException Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 202 8. Parametrisierbare Klassen Grundlagen 8. Parametrisierbare Klassen Java now supports generics, the most significant change to the language since the addition of inner classes in Java 1.2 — some would say the most significant change to the language ever. M. Naftalin, P. Wadler, Java Generics, O’Reilly, 2006. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 203 8. Parametrisierbare Klassen Grundlagen Beispiel: Stapel (Stack) Wichtige abstrakte Datentypen bzw. Datenstrukturen der Informatik sind unabhängig von einem Basis- bzw. Komponententyp. Beispiel: Es sei T ein beliebiger Datentyp. Ein Stapel (Stack) unterstützt die folgenden Operationen: • void push(T element) Legt das Element e vom Typ T auf dem Stapel ab. • T pop() Entfernt das oberste Element vom Stapel und liefert es als Ergebnis zurück. ☞ Die Anweisungen zur Implementierung eines Stapels sind unabhängig vom Komponententyp T . Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 204 8. Parametrisierbare Klassen Grundlagen Generische Objektreferenz durch Object (1) Zunächst kein Problem, es gibt ja die Klasse Object. public class Stapel { ... void push(Object element) { ... } Object pop() { ... } ... } ☞ Vorgehensweise bis Java 1.4 Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 205 8. Parametrisierbare Klassen Java Generics Generische Objektreferenz durch Object (2) Probleme: • Downcasting notwendig nach pop() T t = (T) stack.pop(); • Keine Typüberprüfung zur Übersetzungszeit Stack stack = new Stack(); stack.push( new Integer(4711) ); // wird vom Compiler stack.push( "hallo!" ); // anstandslos akzeptiert • Evtl. Typfehler zur Laufzeit Integer i = (Integer) stack.pop(); // ClassCastException Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 206 8. Parametrisierbare Klassen Java Generics Generische Objektreferenz durch Object (3) Und wenn wir für jeden benötigten Komponententyp eine eigene Implementierung bereitstellen? • hoher Aufwand – Mehrfache Verwendung des fast gleichen Quelltextes – Mehrfaches Testen notwendig – entdeckte Fehler müssen mehrfach korrigiert werden • geringe Abstraktion Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 207 8. Parametrisierbare Klassen Java Generics Typvariablen • Seit Java 5 sind Typvariablen bei einer Klassendefinition möglich. • Die damit realisierte Klasse entspricht einem generischen Datentyp. • In der objektorientierten Programmierung bezeichnen wir dies auch als parametrische Polymorphie. • In Java bezeichnet man diese Möglichkeit als Generics. Beispiel: class Stapel<T> { ... void push(T element) { ... } T pop() { ... } ... } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 208 8. Parametrisierbare Klassen Java Generics Die Typvariable T ersetzt innerhalb der Klassendefinition einen konkreten Typ. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 209 8. Parametrisierbare Klassen Java Generics Nutzung generischer Typen (1) Für die Instanziierung müssen wir die Typvariable durch einen konkreten Typ ersetzen. Stapel<String> sstapel = new Stapel<String>(); Stapel<Double> dstapel = new Stapel<Double>(); Die Stapel sind typsicher: dstapel.push("text"); // hier meckert der Compiler Kein Downcasting notwendig: Double d = dstapel.pop(); // kein Problem Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 210 8. Parametrisierbare Klassen Java Generics Nutzung generischer Typen (2) Leider wäre die folgende Deklaration nicht erlaubt: Stapel<int> istapel = new Stapel<int>(); Grund: Typvariablen dürfen nur durch Referenztypen instanziiert werden. Wie können wir denn dann einen Integer-Stapel erzeugen? Verwendung von WrapperKlassen: Integer, Double, etc. Stapel<Integer> istapel = new Stapel<Integer>(); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 211 8. Parametrisierbare Klassen Java Generics Exkurs: Wrapper-Klassen (Hüllklassen) • Instanzen von Wrapper-Klassen (Hüllklassen) haben die Aufgabe, einen primitiven Wert als Objekt zu repräsentieren. • Es gibt für jeden einfachen Datentyp eine zugehörige Wrapper-Klasse, z.B. int/Integer, double/Double, char/Character. • Integer i = new Integer(4711); • Wie Strings sind Instanzen von Wrapper-Klassen grundsätzlich unveränderlich (immutable). Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 212 8. Parametrisierbare Klassen Java Generics Exkurs: Boxing und Unboxing • Die Repräsentation eines einfachen Wertes als Objekt mit Hilfe einer WrapperKlasse bezeichnen wir auch als Boxing. Integer io = new Integer(4711); // Boxing • Der Zugriff auf den einfachen Wert nennen wir Unboxing. int ival = io.intValue(); // Unboxing Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 213 8. Parametrisierbare Klassen Java Generics Exkurs: Autoboxing (1) • Die manuelle Ausführung von Boxing und Unboxing ist oft unhandlich. Stapel<Integer> istapel = new Stapel<Integer>(); istapel.push( new Integer(4711) ); int iv = istapel.pop().intValue(); • Die automatische Umwandlung von Werten einfacher Datentypen in Instanzen einer Wrapper-Klasse und umgekehrt wird als Autoboxing bezeichnet. • Java beherrscht Autoboxing seit Java 5. int i = 4711; Integer j = i; int k = j; // automatisches Boxing // automatisches Unboxing Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 214 8. Parametrisierbare Klassen Java Generics Exkurs: Autoboxing (2) Damit ist natürlich auch möglich: Stapel<Integer> istapel = new Stapel<Integer>(); istapel.push(4711); // Boxing int iv = istapel.pop(); // Unboxing Vorsicht bei Vergleichen mit == und Autoboxing! Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 215 8. Parametrisierbare Klassen Java Generics Typanpassungen Ein instanziierter generischer Typ lässt sich durch Typanpassung auf eine allgemeine Form bringen: Stapel<Integer> istapel = new Stapel<Integer>(); Stapel stapel = (Stapel) istapel; Jetzt findet für stapel keine Typprüfung mehr statt. Daher würde stapel.push("No Integer"); keinen Fehler zur Übersetzungszeit liefern. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 216 8. Parametrisierbare Klassen Java Generics Typvariablen in Methoden Typvariablen können auch auf Methodendeklarationen beschränkt sein: class Util { public static <T> T zufall(T o1, T o2) { return Math.random() > 0.5 ? o1 : o2; } } Die Angabe von <T> beim Klassennamen entfällt und verschiebt sich auf die Methodendefinition. ☞ Typinferenz Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 217 8. Parametrisierbare Klassen Realisierung von Generics Realisierung von Generics • heterogen Für jeden konkreten Typ wird individueller Code erzeugt. Diesen Ansatz verfolgt z.B. C++ (templates directory) • homogen kein individueller Code, Object statt der Typvariablen, Typanpassungen für einen konkreten Typ, Typüberprüfungen zur Übersetzungszeit Realisierung für Java Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 218 8. Parametrisierbare Klassen Realisierung von Generics Typlöschung Zur Laufzeit ist der konkrete Typ der Typvariablen nicht bekannt. Dies hat Konsequenzen für instanceof. Folgendes liefert einen Übersetzungsfehler: Stapel<String> stapel = new Stapel<String>(); if ( stapel instanceof Stapel<String> ) // ... Grund: Zur Laufzeit liegt nur die Typinformation Stapel vor. Stapel<String> stapel1 = new Stapel<String>(); Stapel<Integer> stapel2 = new Stapel<Integer>(); System.out.println(stapel1.getClass() == stapel2.getClass()); // true! Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 219 8. Parametrisierbare Klassen Realisierung von Generics Raw-Type Eine Instanziierung mit einem konkreten Typ muss nicht unbedingt stattfinden. Ohne Instanziierung gilt implizit Object als konkreter Typ. Stapel stapel = new Stapel<String>(); Stapel ostapel = new Stapel(); Solche Typen heissen Raw-Type. Raw-Typen können wir die parametrisierten Typen verwendet werden. Es findet aber keine Typüberprüfung statt. Eventuell gibt der Compiler Warnungen aus. ☞ Vorsicht vor Typroblemen zur Laufzeit. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 220 8. Parametrisierbare Klassen Typeinschränkungen bei Generics Typeinschränkungen bei Generics Die zugelassenen konkreten Datentypen können mit extends auf Untertypen eines Obertyp eingeschränkt werden. Beispiel: Ein typsicheres generisches max: public static <T extends Comparable> T max(T o1, T o2) { return o1.compareTo(o2) > 0 ? o1 : o2; } ☞ extends auch für Untertypen von Schnittstellen! ☞ Das ist besser als public static Comparable max(Comparable o1, Comparable o2) Warum? Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 221 8. Parametrisierbare Klassen Typeinschränkungen bei Generics Einschränkung mit super • Mit super statt extends kann eine Einschränkung auf Obertypen statt auf Untertypen erfolgen. • <T super O> • Damit dürfen für die Typvariable T O oder Obertypen von O eingesetzt werden. • Diese Einschränkung wird z.B genutzt, wenn eine mit T parametrisierte generische Datenstruktur manipuliert wird (Kontravarianz). Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 222 8. Parametrisierbare Klassen Typeinschränkungen bei Generics Kombinationen von Obertypen • Soll die Typvariable T zu mehreren Obertypen passen, lassen sich Kombinationen bilden. • Man beachte: Für T kann nur eine Oberklasse angegeben werden, da es in Java nur einfache Vererbung bei Klassen gibt. • Weitere Obertypen müssen Schnittstellen sein. • <T extends O & I1 & I2 & ...> Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 223 8. Parametrisierbare Klassen Typeinschränkungen bei Generics Generics und Vererbung (1) • Wenn Fussballer eine Unterklasse von Sportler ist, dann ist Fussballer[] ein Untertyp von Sportler[]. • Daher ist folgenden problemlos möglich: Fussballer[] fussballer = new Fussballer[11]; Sportler[] sportler = fussballer; • In der objektorientierten Programmierung bezeichnet man dies als Kovarianz. • Beispiel: public static void gebeSportlerAus(Sportler[] sportler) { for (int i=0 ; i<sportler.length ; i++) sportler[i].gibAus(); } Kovarianz stellt bei lesendem Zugriff kein Problem dar. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 224 8. Parametrisierbare Klassen Typeinschränkungen bei Generics Generics und Vererbung (2) • Generics sind untereinander nicht kovariant sondern invariant! • D.h. Stapel<Fussballer> ist kein Untertyp von Stapel<Sportler>. • Daher liefert der Compiler für folgende Anweisungen einen Fehler: Stapel<Sportler> sportler = new Stapel<Fussballer>(); • Begründung: Wenn erlaubt, dann wäre folgendes möglich: sportler.push(new Handballer()); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 225 8. Parametrisierbare Klassen Typeinschränkungen bei Generics Generics und Vererbung (3) • Kovarianz stellt bei schreibendem Zugriff ein Problem dar! • Dagegen wäre folgendes vom Prinzip her unproblematisch: Stapel<Sportler> sportler = new Stapel<Mensch>(); sportler.push(new Handballer()); sportler.push(new Sportler()); • Kontravarianz Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 226 8. Parametrisierbare Klassen Typeinschränkungen bei Generics Generische Variablen: Wildcards Folgendes funktioniert nicht: Stapel<Sportler> sportler; Stapel<Fussballer> fussballer = new Stapel<Fussballer>(); Stapel<Handballer> handballer = new Stapel<Handballer>(); sportler = fussballer; // Fehler! • Es ist aber möglich, statt konkreter Typangaben Wildcards anzugeben. • Diese können mit zusätzlichen Einschränkungen (extends, super) versehen werden. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 227 8. Parametrisierbare Klassen Typeinschränkungen bei Generics Generische Variablen: Wildcards (2) Beispiel: Stapel<? extends Sportler> sportler; Stapel<Fussballer> fussballer = new Stapel<Fussballer>(); Stapel<Handballer> handballer = new Stapel<Handballer>(); Stapel<Biertrinker> biertrinker = new Stapel<Biertrinker>(); sportler = fussballer; sportler = handballer; // OK! sportler = biertrinker; // Fehler! Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 228 8. Parametrisierbare Klassen Typeinschränkungen bei Generics Generische Variablen: Wildcards (3) Wildcard mit extends liefert uns Kovarianz: Für public static void gebeSportlerAus(Stapel<? extends Sportler> sportler) { for (Sportler s : sportler) s.gibAus(); } ist Stapel<Fussballer> fussballer = new Stapel<Fussballer>(); gebeSportlerAus(fussballer); kein Problem. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 229 9. Aufzählungen mit enum Konstanten und Aufzählungen 9. Aufzählungen mit enum Für die Definition von systemweiten Konstanten haben wir bisher öffentliche finale Klassenvariablen verwendet. public class MusicStyle { public static final int public static final int public static final int public static final int } ROCK POP COUNTRY TECHNO = = = = 0; 1; 2; 3; Probleme: • Kein Zwang zur Verwendung • Datentyp int statt eines eigenen Datentyps Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 230 9. Aufzählungen mit enum enum enum-Deklaration (1) Seit Java 5 sind “richtige Aufzählungen” mit dem Schlüsselwort enum möglich. Syntax: public enum Aufzählungstyp { Aufzählung } Beispiel: public enum Wochentag { MONTAG, DIENSTAG, ... , SONNTAG } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 231 9. Aufzählungen mit enum enum enum-Deklaration (2) • Eine enum-Deklaration verhält sich wie eine Klassendeklaration. • Insbesondere ist mit der Deklaration ein neuer Datentyp verbunden. Wochentag tag = Wochentag.SONNTAG; • • • • Ober- und Unterklassen sind nicht erlaubt. Variablen und Methoden sind möglich. Für den Aufzählungstyp wird eine class-Datei erstellt. Die Elemente der Aufzählung werden durch Objekte repräsentiert. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 232 9. Aufzählungen mit enum enum enum-Konstanten in switch enum-Konstanten können als case-Marke verwendet werden. Wochentag tag = Wochentag.SONNTAG; switch (tag) { case MONTAG : System.out.println("Ich mag keine Montage!"); break; case FREITAG: System.out.println("Gott sei Dank, es ist Freitag!"); break; } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 233 9. Aufzählungen mit enum enum enum und Objektorientierung • enum-Typen überschreiben sinnvoll die folgenden Methoden aus java.lang.Objects: – toString() – hashCode() – equals() • Sie implementieren die Schnittstellen Serializable und Comparable. • Die Ordnung der Elemente ergibt sich durch die Reihenfolge bei der Deklaration. • Aufzählungstypen sind Referenztypen! Daher ist Wochentag tag = null; möglich. • Wo notwendig auf == null prüfen und Exception auslösen. • import wie bei Klassen Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 234 9. Aufzählungen mit enum enum Praktische Methoden im Zusammenhang mit enum • Die statische Methode valueOf(String) bestimmt zu einem String den zugehörigen enum-Wert: Wochentag tag = Wochentag.valueOf("SONNTAG"); // liefert Wochentag.SONNTA • Die Umkehrung hierzu ist toString(): Wochentag tag = Wochentag.valueOf("SONNTAG"); System.out.println("Heute ist " + tag.toString()); • Die Methode ordinal() liefert die Ordinalzahl zu einer enum-Konstanten. • Die Klassenmethode values() liefert ein Feld mit allen Werten des Aufzählungstyps: for (Wochentag t : Wochentag.values()) System.out.println(t + " hat den Wert " + t.ordinal()); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 235 10. Pakete Grundlagen zu Paketen 10. Pakete Ein Paket (package) bündelt thematisch zusammengehörige Klassen und Schnittstellen zu einer Klassenbibliothek. Beispiele: • • • • java.lang: Standardklassen zur Sprache Java java.net: Klassen zur Netzwerkprogrammierung java.io: Klassen für die Ein- und Ausgabe von Daten java.awt: Klassen für graphische Oberflächen (Abstract Window Toolkit) Pakete haben einen Namen, den Paketnamen. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 236 10. Pakete Grundlagen zu Paketen Vorteile von Paketen • Pakete bilden eigene Bereiche für die Sichtbarkeit. Dadurch kann mit Paketen eine Datenkapselung erreicht werden. • Jedes Paket bildet einen eigenen Namensraum. Damit können Namenskonflikte vermieden werden und identische Namen für Klassen in verschiedenen Paketen vergeben werden. • Pakete sind gröbere Einheiten für die Strukturierung von objektorientierten Systemen als Klassen. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 237 10. Pakete Benutzung von Paketen Nutzung von Klassen aus Paketen (1) Für die Nutzung von Klassen aus Paketen gibt es zwei Möglichkeiten: • volle Qualifikation: Paket und Klassenname werden angegeben: java.awt.Point p = new java.awt.Point(); • Deklaration mit Hilfe von import: Es wird einmal angegeben, aus welchen Paketen Klassen importiert werden: import java.awt.Point; Point p = new Point(); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 238 10. Pakete Benutzung von Paketen Nutzung von Klassen aus Paketen (2) Um nicht alle Klassen eines Paketes einzeln angeben zu müssen, besteht die Möglichkeit, mit Hilfe des * alle Klassen zu importieren. Die Anweisung import java.awt.*; importiert alle Klassen aus dem Paket java.awt. ☞ import steht zwischen einer optionalen package-Deklaration und dem Rest des Quelltextes. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 239 10. Pakete Benutzung von Paketen Nutzung von Klassen aus Paketen (3) • Das Paket java.lang wird immer automatisch importiert. • Sollten Klassen mit gleichem Namen aus verschiedenen Paketen importiert werden, muss eine volle Qualifikation erfolgen. • Auch Pakete können hierarchisch strukturiert werden (Unterpakete). • import bezieht die Unterpakete nicht automatisch mit ein! import java.awt.*; import java.awt.geom.*; Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 240 10. Pakete Benutzung von Paketen Statischer Import Klassenmethoden bzw. Konstanten werden über den Methodennamen (bzw. Konstantennamen) in Verbindung mit dem Klassennamen angesprochen. double x = Math.sin( Math.PI/4 ); Seit Java 5 können Klassenmethoden und Konstanten statisch importiert werden: import static java.lang.Math.sin; import static java.lang.Math.PI; double x = sin( PI/4 ); Auch beim statischen Import ist der Einsatz von * möglich. Ein statischer Import ist auch für überladene Klassenmethoden möglich. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 241 10. Pakete Pakete selber bauen Die package-Deklaration • Mit Hilfe einer package-Deklaration können wir Quelltextteile Paketen zuordnen. package Paketbezeichner; • Die package-Deklaration muss vor allen anderen Anweisungen im Quelltext stehen. • Hinter package wird ein hierarchisch aufgebauter Paketbezeichner als Paketname angegeben. package de.fhbrs.inf.meinPaket; • Wirkung: Alle Klassen und Schnittstellendefinitionen der Übersetzungseinheit (.java-Datei), werden dem Paket de.fhbrs.inf.meinPaket zugeordnet. • Natürlich können auch andere .java-Dateien diesem Paket zugeordnet werden. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 242 10. Pakete Pakete selber bauen Paketnamen • • • • Für die weltweite Eindeutigkeit: häufig umgedrehte Domainnamen Für die hierarchische Strukturierung ist der Punkt erlaubt. Dagegen ist leider kein “–” in Paketnamen erlaubt. Die Paketnamen java, javax und sun sind reserviert. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 243 10. Pakete Pakete selber bauen Default-Package Was passiert, wenn wir keine package-Deklaration angeben? • Die Quelltextteile werden einem sogenannten Default-Package zugeordnet. • Zu diesem Default-Package gehören alle Übersetzungseinheiten des lokalen Verzeichnisses, die nicht einem Paket zugeordnet sind. • Daher müssen wir uns bei kleinen Anwendungen nicht um die Paketorganisation kümmern. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 244 10. Pakete Pakete selber bauen Verzeichnisstruktur für Pakete projekte • In der Regel setzen wir für die Entwicklung die Paketstruktur eins zu eins auf eine Dateistruktur um. • Verzeichnisse tragen den Paketnamen. • Die zum Paket gehörigen Klassen werden in den entsprechenden Verzeichnissen abgelegt. lib paket1 sub1 Klasse1 projekt1 projekt2 classes doc paket2 Klasse2 sub2 Klasse3 Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 245 10. Pakete Pakete selber bauen C LASSPATH • Wenn sich nicht alle class-Dateien im lokalen Verzeichnis befinden, muss die virtuelle Maschine wissen, wo sie nach class-Dateien suchen soll. • Hierzu dient die Umgebungsvariable CLASSPATH. • Genauer: Im C LASSPATH werden die Verzeichnisse aufgelistet, in denen nach Paketen gesucht wird. • Mit CLASSPATH=/projekte/projekt1/classes werden die Klassen paket1.klasse1, paket1.klasse2 und auch paket1.sub1.klasse3 gefunden. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 246 10. Pakete Pakete selber bauen jar-Dateien • Für die Auslieferung einer Klassenbiblothek ist es einfacher, wenn sich die gesamte Bibliothek in einer Datei befindet, statt in einer losen Sammlung von classDateien. • Dies ist mit jar-Dateien möglich. • Eine jar-Datei ist eine Zusammenstellung von Dateien in einer einzigen Datei. • Erzeugung von jar-Dateien: siehe Hinweise auf der Homepage. • jar-Dateien können im C LASSPATH verwendet werden. • $ cd /projekte/projekt1/classes $ jar cf ../lib/projekt1.jar paket1 paket2 $ CLASSPATH=/projekte/projekt1/lib/projekt1.jar Damit werden die Klassen aus den Paketen paket1, paket2 und paket1.sub1 gefunden. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 247 10. Pakete Zugriffsmodifikatoren und Zugriffsrechte Verwendung von Zugriffsmodifikatoren • Zur Regelung der Zugriffsrechte gibt es in Java die Modifikatoren public, protected und private. • Ohne Zugriffsmodifikator besteht das Zugriffsrecht default. • Beachten Sie: default ist kein Schlüsselwort. • Für Klassen sind die Zugriffsrechte public und default möglich. • Für Klassen- bzw. Instanzvariablen und -methoden sind möglich: default, public, protected und private. • Variablen und Methoden innerhalb von Schnittstellen sind implizit public. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 248 10. Pakete Zugriffsmodifikatoren und Zugriffsrechte Zugriffsrechte für Variablen und Methoden Klasse A Klasse B gleiches Paket Subklasse C gleiches Paket Klasse D anderes Paket Subklasse E anderes Paket private default protected public ja ja ja ja nein ja ja ja nein ja ja ja nein nein nein ja nein nein ja/nein ja Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 249 11. Java-Klassenbibliothek Pakete des JDK 11. Java-Klassenbibliothek Die Java-Bibliothek der Standard Edition umfasst: • • • • • • • mehr als 200 Pakete. Diese definieren 3777 Typen, davon 2457 Klassen, 972 Schnittstellen, 49 Aufzählungen, 473 Klassen für Exceptions und 32 Error-Klassen Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 250 11. Java-Klassenbibliothek Wrapper-Klassen Wrapper-Klassen Wrapper-Klassen erfüllen zwei Aufgaben: • Repräsentation eines Werts als Objekt (siehe Kapitel Generics) • Sie stellen Methoden für die Umwandlung von Werten bereit. Wrapper-Klasse Byte Short Integer Long Double Float Boolean Character Void Einfacher Datentyp byte short int long double float boolean char void Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 251 11. Java-Klassenbibliothek Wrapper-Klassen Erzeugung von Wrapper-Objekten • Die Objekte zu den einfachen Werten können durch einen Konstruktoraufruf erzeugt werden. • Die Konstruktoren akzeptieren üblicherweise die zugehörigen einfachen Werte oder Strings. Integer Double String Integer i d s i = = = = new Integer( 4711 ); new Double( 0.815 ); "4711"; new Integer( s ); • Bei nicht korrekt aufgebauten Strings: NumberFormatException • Weitere Methoden zur Erzeugung von Wrapper-Objekten: Klassenmethode valueOf Integer i = Integer.valueOf( 4711 ); String s = "4711"; Integer i = Integer.valueOf( s ); Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 252 11. Java-Klassenbibliothek Wrapper-Klassen Zugriff auf den Wert • Hängt ab von der Wrapper-Klasse, • z.B. intValue für Integer, booleanValue für Boolean • Zusätzlich wird Autoboxing unterstützt. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 253 11. Java-Klassenbibliothek Zahlen mit beliebiger Genauigkeit Zahlen mit beliebiger Genauigkeit Die Datentypen int und double haben eine beschränkten Wertevorrat. • Mit Hilfe der Klasse java.Math.BigInteger können wir beliebig große Zahlen repräsentieren, • mit Hilfe der Klasse java.Math.BigDecimal beliebiger vorgegebener Genauigkeit. • Möglich ist dies durch eine variabel lange Repräsentation. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 254 11. Java-Klassenbibliothek Zahlen mit beliebiger Genauigkeit Konstruktoren und Methoden für BigInteger • Am einfachsten erzeugt man ein BigInteger aus einem String. String s = "1"; for (int i=1 ; i<=100 ; i++ ) s = s + "0"; BigInteger i = new BigInteger( s ); • Konstanten: ZERO, ONE, TEN • Instanzmethoden für Grundrechenarten: add, divide, multiply, remainder, subtract. Beispielsignatur: BigInteger add(BigInteger val) Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 255 11. Java-Klassenbibliothek Formatierte Ausgabe Formatierte Ausgabe Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 256 12. Threads in Java Einführendes Beispiel 12. Threads in Java Ein Thread ist eine Folge von Anweisungen, die unabhängig von anderen Threads parallel zu diesen ausgeführt wird. • Jeder Thread hat seinen eigenen Stack um lokale Variablen anzulegen und Methoden aufzurufen. • Alle Threads eines Prozesses teilen sich den Adressbereich des Prozesses (keine Interprozesskommunikation notwendig). • Man nennt Threads auch leichtgewichtige Prozesse. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 257 12. Threads in Java Einführendes Beispiel Sequentielle Abarbeitung (1) Wir erzeugen zwei gleichartige Objekte der Klasse ABCPrinter, deren Methode start() die Großbuchstaben auf der Konsole ausgibt: public class MehrmalsP { public static void main(String[] args) { ABCPrinter p1 = new ABCPrinter(); ABCPrinter p2 = new ABCPrinter(); p1.start(); p2.start(); } } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 258 12. Threads in Java Einführendes Beispiel Sequentielle Abarbeitung (2) public class ABCPrinter { public void run() { for (char c=’A’ ; c<= ’Z’ ; c++) { System.out.print(c); Machmal.eineSekundeNix(); } } public void start() { run(); } } Liefert die Ausgabe: ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ ☞ Sequentielle Abarbeitung Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 259 12. Threads in Java Einführendes Beispiel Beispiel: Thread (1) Aus ABCPrinter machen wir ABCThread, indem wir von der Klasse java.lang.Thread ableiten. Die Klasse Thread hat auch schon eine Methode start(), die genau das macht, was wir benötigen, nämlich die Methode run() aufrufen. public class ABCThread extends Thread { public void run() { for (char c=’A’ ; c<= ’Z’ ; c++) { System.out.print(c); Machmal.eineSekundeNix(); } } } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 260 12. Threads in Java Einführendes Beispiel Beispiel: Thread (2) In unserem main-Methode benutzen wir nun ABCThread statt ABCPrinter. public class MehrmalsT { public static void main(String[] args) { ABCThread p1 = new ABCThread(); ABCThread p2 = new ABCThread(); p1.start(); p2.start(); } } Dies liefert die Ausgabe: AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ ☞ parallele Abarbeitung Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 261 12. Threads in Java Threads in Java Threads in Java (1) • Die parallel auszuführenden Anweisungen müssen in einer Methode run() enthalten sein • oder von run() aus aufgerufen werden. • Die Methode start() sorgt dafür, dass run() nebenläufig ausgeführt wird. • Ruft man run() dagegen direkt auf, entsteht keine Nebenläufigkeit. • Die Klasse Thread verfügt bereits über eine Methode start(). • und eine Methode run() (die allerdings nichts tut). Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 262 12. Threads in Java Threads in Java Threads in Java (2) • Entscheidend für die Implementierung eines Threads ist nicht die Klasse Thread sondern die Schnittstelle java.lang.Runnable. • Runnable definiert eine (abstrakte) Methode run(). • Nur Klassen, die Runnable implementieren, können einen Thread realisieren. Konsequenz: Zwei Möglichkeiten zur Implementierung von Threads: • Thread implementiert Runnable, also leiten wir von Thread ab und überschreiben die Methode run(). • Wir implementieren Runnable in einer eigenen Klasse und lassen Objekte dieser Klasse von Objekten der Klasse Thread kontrollieren. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 263 12. Threads in Java Threads in Java Beispiel: Runnable (1) Hier die zweite Möglichkeit: Wir nutzen implements statt extends. public class ABCRunnable implements Runnable { public void run() { for (char c=’A’ ; c<= ’Z’ ; c++) { System.out.print(c); Machmal.eineSekundeNix(); } } } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 264 12. Threads in Java Threads in Java Beispiel: Runnable (2) In unserem main-Methode erzeugen wir nun ABCRunnable und steuern diese Objekte mit Hilfe von Objekten der Klasse Thread. public class MehrmalsR { public static void main(String[] args) { Thread t1 = new Thread(new ABCRunnable()); Thread t2 = new Thread(new ABCRunnable()); t1.start(); t2.start(); } } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 265 12. Threads in Java Threads in Java Die Klasse Thread Konstruktor (u.a.): • public Thread(Runnable target) Bei Aufruf von start() wird die run-Methode von target nebenläufig ausgeführt. Instanzmethoden (u.a.): • public void start() • public void run() • public final boolean isAlive() liefert true, wenn der Thread gestartet aber noch nicht beendet ist. • public void interrupt() setzt das Abbruch-Flag des Threads. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 266 12. Threads in Java Threads in Java • public boolean isInterrupted() liefert true, wenn das Abbruch-Flag gesetzt ist. Klassenmethoden(u.a.): • public static Thread currentThread() liefert eine Referenz auf den Thread, der gerade ausgeführt wird. • public static void yield() lässt den Thread, der gerade ausgeführt wird, kurz pausieren, um andere Threads zum Zuge kommen zu lassen. • public static void sleep(long millis) der Thread, der gerade ausgeführt wird, pausiert für millis Millisekunden. InterruptedException möglich, dabei wird das Abbruch-Flag zurückgesetzt. • public static boolean interrupted() liefert true, wenn beim Thread, der gerade ausgeführt wird, das Abbruch-Flag gesetzt ist. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 267 12. Threads in Java Threads in Java Beispiel: sleep public class Machmal { public static void eineSekundeNix() { try { Thread.sleep(1000); } catch(InterruptedException e) { } } } Die Methode sleep() kann eine InterruptedException auslösen. Das Abbruch-Flag des Threads wird dabei zurückgesetzt. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 268 12. Threads in Java Threads in Java Threads vorzeitig beenden • Threads müssen nicht ihre run-Methode komplett abarbeiten. Vielleicht ist in der run-Methode auch mit Absicht eine Endlosschleife programmiert. • Idealerweise nutzen wir die Instanzmethoden interrupt() und isInterrupted(). • Man beachte: Wenn sleep unterbrochen wird, wird eine Exception ausgelöst und das Abbruch-Flag zurückgesetzt. • Wenn das Abbruch-Flag gesetzt sein soll, muss deshalb im Exception-Handler interrupt() aufgerufen werden. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 269 12. Threads in Java Threads in Java Lebenszyklus eines Threads • Nach new ist der Thread erzeugt. • Mit start() wird der Thread ausführbar. • Mit run() kann der Thread laufend werden. Dieser Zustand ist ein Spezialfall von ausführbar. • Der Thread läuft i.d.R. nicht permanent, sondern wird zwischendurch suspendiert, z.B. durch yield(). Dann ist er nur noch ausführbar, aber nicht laufend. • Mit der Terminierung von run() ist der Thread beendet. • Im Zustand ausführbar kann durch sleep() oder wait() der Thread in den Zustand nicht ausführbar überführt werden. • Mit Hilfe von notify() wird ein nicht ausführbarer Thread wieder ausführbar. • Mit Hilfe von isAlive kann ermittelt werden, ob ein Thread in den Zuständen ausführbar oder nicht ausführbar ist. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 270 12. Threads in Java Threads in Java Scheduling • Ein Scheduler verteilt die zur Verfügung stehende Prozessorzeit auf die einzelnen Threads. • Das Scheduling ist nicht spezifiziert, sondern hängt von der virtuellen Maschine ab. • Es ist aber sichergestellt, dass ein Thread von höherer Priorität durchschnittlich mehr Prozessorzeit erhält. • präemptives Scheduling: Die Threads werden unterbrochen. • Ein Thread erhält zunächst die Priorität des Threads, der ihn erzeugt. Der mainThread hat die Priorität 5 (NORM PRIORITY). • Methoden zur Steuerung der Priorität: getPriority und setPriority. • Höchste Priorität gleich 10 (MAX PRIORITY), niedrigste gleich 1 (MIN PRIORITY). Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 271 12. Threads in Java Synchronisation Synchronisation • Ein Thread schreibt Werte in ein Objekt, ein anderes liest Werte aus dem selben Objekt. • Wird der schreibende Thread unterbrochen, bevor er den Schreibvorgang komplett abgeschlossen hat, liest der andere Thread inkonsistente Werte. • Leser/Schreiber-Problem Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 272 12. Threads in Java Synchronisation Beispiel: Leser/Schreiber (1) public class Sequenz { private static int zaehler = 0; public static void nachster() { int zahl = zaehler; eine Anweisung die lange dauert ... System.out.print(" " + zahl); zaehler++; } } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 273 12. Threads in Java Synchronisation Beispiel: Leser/Schreiber (2) public class ZaehlThread extends Thread { public void run() { for (;;) Sequenz.naechster(); } } Wie sieht die Ausgabe u.U. aus, wenn zwei ZaehlThreads parallel laufen? 0 1 2 2 4 5 6 6 8 ... Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 274 12. Threads in Java Synchronisation Sperren, Monitor • Das Problem des letzten Beispiels können wir prinzipiell durch Sperren (Locks) lösen. • Immer wenn ein Thread die Methode naechster() betritt, wird diese Methode für alle anderen Threads gesperrt. • Threads, die eine gesperrte Methode betreten wollen, werden solange supendiert, bis die Sperre freigegeben wird. • Die Sperre wird freigegeben, sobald der ausführende Thread die Methode naechster() verlassen hat. • Nun kann ein anderer Thread in die Methode naechster() eintreten. Damit sperrt er diese Methode wiederum für alle anderen Threads. • Fazit: maximal ein Thread soll sich in der Methode naechster() befinden (kritischer Abschnitt). • In der Programmierung bezeichnet man dies als Monitor. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 275 12. Threads in Java Synchronisation Monitor mit synchronized • In Java ist es leicht, einen Monitor zu implementieren. • Mit dem Schlüsselwort synchronized erreichen wir, dass sich maximal ein Thread in der durch synchronized gekennzeichneten Methode befinden darf. synchronized public static void naechster() { ... } Damit wäre gewährleistet, dass unsere ZaehlThreads eine korrekte Sequenz ausgeben: 0 1 2 3 4 5 6 7 8 ... Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 276 12. Threads in Java Synchronisation Klassen- und Instanzmethoden mit synchronized • Eine mit synchronized gekennzeichnete Klassenmethode kann höchstens von einem Thread gleichzeitig ausgeführt werden. • Eine mit synchronized gekennzeichnete Instanzmethode kann prinzipiell von mehreren Threads gleichzeitig ausgeführt werden. • Aber für jedes Objekt kann es maximal einen Thread geben, der sich in einer mit synchronized gekennzeichneten Instanzmethode befindet. • Bei Instanzmethoden ist die Synchronisation also objektbezogen. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 277 12. Threads in Java Synchronisation Das Erzeuger/Verbraucher-Problem Wir betrachten einen (zunächst abstrakten) einfachen Puffer, der maximal einen Integer-Wert aufnehmen kann. abstract class Wert { protected int wert; abstract public int get(); abstract public void put(int w); } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 278 12. Threads in Java Synchronisation Solch ein Puffer wird von einem Erzeuger-Thread mit den Werten von 0 bis 4 gefüllt. class Erzeuger extends Thread { Wert w; public Erzeuger(Wert w) { this.w = w; } public void run() { for (int i=0 ; i<5 ; i++) { w.put(i); System.out.println("Erzeuger put: " + i); try { sleep((int) (Math.random() * 100)); } catch(InterruptedException e) { } } } } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 279 12. Threads in Java Synchronisation Ein Verbraucher-Thread holt die Werte aus dem Puffer. class Verbraucher extends Thread { Wert w; public Verbraucher(Wert w) { this.w = w; } public void run() { int v; for (int i=0 ; i<5 ; i++) { v = w.get(); System.out.println("Verbraucher get: " + v); try { sleep((int) (Math.random() * 100)); } catch(InterruptedException e) { } } } } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 280 12. Threads in Java Synchronisation Wir spezialisieren die Pufferklasse Wert zunächst auf einfache Weise: class SchlechterWert extends Wert { public synchronized int get() { return wert; } public synchronized void put(int w) { wert = w; } } Unser Testprogramm public class EVTest1 { public static void main(String[] args) { Wert w = new SchlechterWert(); Erzeuger e = new Erzeuger(w); Verbraucher w = new Verbraucher(w); e.start(); v.start(); } } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 281 12. Threads in Java Möglicher Ablauf: Erzeuger put: Verbraucher get: Erzeuger put: Verbraucher get: Verbraucher get: Verbraucher get: Erzeuger put: Erzeuger put: Erzeuger put: Verbraucher get: Synchronisation 0 0 1 1 1 1 2 3 4 4 • Der Erzeuger produziert zwar die Werte 0 bis 4, • der Verbraucher entnimmt dem Puffer aber einige Werte mehrfach bzw. einige überhaupt nicht. • Wir müssen dafür sorgen, dass der Verbraucher immer nur dann aktiv wird, wenn der Erzeuger wieder einen neuen Wert bereitgestellt hat. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 282 12. Threads in Java Synchronisation Object-Methoden zur Steuerung von Threads Die Klasse Object stellt zur Verfügung: • public final void wait() der Thread, der gerade ausgeführt wird, wird suspendiert, bis ein anderer Thread notify oder notifyAll für das aktuelle Objekt ausführt. • public final void notify() reaktiviert einen einzelnen Thread, der sich im Wartezustand bezüglich des aktuellen Objekts befindet. • public final void notifyAll() reaktiviert alle Thread, die sich im Wartezustand bezüglich des aktuellen Objekts befinden. Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 283 12. Threads in Java Synchronisation Eine bessere Spezialisierung des abstrakten Puffers: class GuterWert extends Wert { private boolean verfuegbar = false; public synchronized int get() { if (!verfuegbar) try { wait(); } catch (InterruptedException ie) { } verfuegbar = false; notify(); return wert; } Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 284 12. Threads in Java Synchronisation public synchronized int put(int w) { if (verfuegbar) try { wait(); } catch (InterruptedException ie) { } wert = w; verfuegbar = true; notify(); } } • Das Flag verfuegbar zeigt an, ob ein Wert bereitsteht. • Kein Wert verfügbar, dann wait() in get(). • put() weckt mit notify() den Verbraucher-Thread, wenn Wert zur Verfügung steht. • put()/get() analog Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 285 12. Threads in Java Synchronisation Im Testprogramm ändern wir die Zeile zur Erzeugung des Puffers: Wert w = new GuterWert(); Damit erhalten wir dann eine saubere Verarbeitung: Erzeuger Verbraucher Erzeuger Verbraucher Erzeuger Verbraucher Erzeuger Verbraucher Erzeuger Verbraucher put: get: put: get: put: get: put: get: put: get: 0 0 1 1 2 2 3 3 4 4 Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 286 12. Threads in Java Synchronisation Zusammenfassung • • • • Threads: parallele Kontrollflüsse in einem Programm Implementierung: implements Runnable oder extends Thread kritische Bereiche, Monitor: synchronized Gegenseitiges Warten: wait(), notify() Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08 287