Einführung in die Programmierung mit Java 2. Semester SS 2017 Prof. Dr. Herbert Neuendorf DHBW Mosbach [email protected] Programmieren 2 - H.Neuendorf (1) Literatur zu Java + Programmierung Deck, Neuendorf : Java-Grundkurs für Wirtschaftsinformatiker (Vieweg) ⊗ H. Mössenböck : Sprechen Sie Java? (dpunkt) D.Ratz et al. : Grundkurs Programmieren in Java (Hanser) 2.Auflage Vorlesungsfolien + Übungen : www.dhbw-mosbach.de/studienangebote/wirtschaftsinformatik/kontakt dort : → Prof.Dr. Neuendorf → Aktuelle Lehrveranstaltungen Kein Skript ersetzt die Lektüre eines guten Buches !! Ressourcen : www.oracle.com/technetwork/java dort : Java-Page von Oracle Software Downloads → Java SE http://docs.oracle.com/javase/8/docs/api/ www.eclipse.org JavaSE Doku Eclipse-Page → Download Programmieren 2 - H.Neuendorf (2) Programmierung 2 – WI16 SS2016 Spezielle Klassen + Methoden Paketkonzept ( Reflection ) Generics Algorithmen & Collection Framework Datenstrukturen DAO-Pattern Threading Ereignis-Orientierung GUI-Programmierung Themenauswahl gemäß Prof. Dr. Deck Thematiken sind nicht-trivial und erfordern unbedingt die zeitnahe Nachbereitung anhand des Skripts und der Übungen !! Programmieren 2 - H.Neuendorf (3) Spezielle Klassen und Methoden (Infrastruktur) Erste Datenbehälter String, StringBuffer & StringBuilder Hüllklassen BigInteger & BigDecimal equals( ) / hashCode( ) Enums Clonen von Objekten clone( ) Wird durch weitere Methoden und InfrastrukturInterfaces noch vertieft … Programmieren 2 - H.Neuendorf (4) Spezielle Klassen und Methoden (Infrastruktur) Erste Datenbehälter - Zeichenketten Strings StringBuffer & StringBuilder toString() Stringkonvertierungen Stringformattierungen und Zerlegungen Programmieren 2 - H.Neuendorf (5) Zeichen (characters) und Zeichenketten (Strings) Zeichen-Typ char : 16 bit-Codierung = UC UC-Nummernbereiche char ist Ganzzahltypen quasi gleichgestellt ⇒ Berechnungen : \u0000 - \u007f ASCII-Z 'c' - 'a' = 2 = Abstand in UC-Tabelle Alle Vergleichsoperationen anwendbar \u0080 - \u024f Sonderz. ==, !=, <, >, <=, >= if ( 'a' < ch && ch < 'z' || 'A' < ch && ch < 'Z' ) { ..... } Zeichenkonstanten in einfachen Hochkommata \u0370 - \u03ff Griechisch \u0400 - \u04ff Kyrillisch \u0600 - \u06ff Arabisch … char ch = 'a' ; Codierung : Unicode: 1 Zeichen = 2 Byte ⇒ 65536 Zeichen darstellbar ! Unicode-Zeichen durch Nummerncode ausdrückbar : zB \u0040 ← Java ! für @ String s = "Hallo\unnnnDH"; nnnn = vierstelliger Hexadezimalcode des UC-Zeichens Spezielle Steuerzeichen durch Escape-Sequenzen ansprechbar - geschützt mit \ Escape-Sequenzen \n new line \r return \t Tabulatorsprung \\ Backslash … Programmieren 2 - H.Neuendorf (6) Zeichen in Java - Hüllklasse Charakter Methoden zur erweiterten Zeichenbearbeitung in Hüllklasse char ch = 'a' ; java.lang.Character Character c = new Character( 'a' ) ; if ( Character.isLetter( ch ) ) … // prüft, ob ch ein Unicode-Buchstabe ist if ( Character.isDigit( ch ) // prüft, ob ch eine Ziffer ist )… if ( Character.isLetterOrDigit( ch ) ) ... // prüft, ob ch UC-Buchstabe oder Ziffer ist ch = Character.toUpperCase( ch ) ; // wandelt ch in Großbuchstaben um ch = Character.toLowerCase( ch ) ; // wandelt ch in Kleinbuchstaben um if ( Character.isUpperCase( ch ) ) … if ( Character.isLowerCase( ch ) ) … if ( Character.isSpace( ch ) ) … char d = c.charValue( ) ; int n = Character.digit( 'A' , 16 ) ; ... // numerischer Wert in Basis – hier : n == 10 Falls Basis unzulässig oder Zeichen keine Ziffer in der Basis darstellt, liefert Methode -1 zurück Programmieren 2 - H.Neuendorf (7) Zeichenketten in Java → Objekte der Klasse java.lang.String ! Komplexer Datentyp String : Aufnahme von konstanten Zeichenketten Strings sind Objekte! Typ String liefert Methoden zur Stringverarbeitung Stringkonstanten : Zeichenfolgen in Anführungszeichen → String → "x" Zeichen → 'x' Escape-Sequenzen einbaubar Deklaration, Initialisierung, Verkettung : a = a + "World" + 3 + x ; // Operator + " this is a string " " links \t mitte \t rechts " String a = "Hallo" ; int x = 9 ; für Strings und primitive Datentypen mit automatischer Typkonversion Interne Darstellung → Strings sind unveränderliche Objekte : immutable ! Jede Veränderung erzeugt ein neues Objekt - aufwendig ! a Hello String a, b ; a = "Hello" ; b a Hello World b Hello String-Variable ist Referenz auf String-Objekt ! b=a; a = a + " World" ; // Referenz a zeigt auf neues String-Objekt Interne Verwaltung durch char[ ] // Referenz b zeigt noch auf altesString-Objekt Arrays statisch ⇒ Strings immutable Programmieren 2 - H.Neuendorf (8) String-Vergleich Äquivalenz versus Identität Strings sind Objekte ⇒ Stringvariablen sind Objektreferenzen Vergleichsoperator == bewirkt Referenzvergleich nicht Inhaltsvergleich ! String a = new String( "Hallo" ) ; // String-Objekt a mit Copy-Konstruktor aus String "Hallo" String b = new String( "Hallo" ) ; // Anderes String-Objekt! Gleicher Inhalt wie a // a und b referenzieren zwei unterschiedliche Objekte gleichen Inhalts // ⇒ Referenzwert von b ≠ Referenzwert von a if ( a == b ) { IO.writeln( "gleich" ) ; } // Referenzvergleich - liefert hier false ! String-Klasse enthält deshalb Methoden zum Vergleich auf Äquivalenz → boolean equals( Object o ) if ( a.equals( b ) ) führt case sensitiven Inhaltsvergleich durch : { IO.writeln( "gleich" ) ; } // false bei Vergleich mit anderem Typ if ( a.equals( "Hello" ) ) { IO.writeln( "gleich" ) ; } → boolean equalsIgnoreCase( String s ) Ignoriert Unterschiede in Groß-Kleinschreibung ! Programmieren 2 - H.Neuendorf (9) String-Operationen java.lang.String String a = "Hallo" ; Anzahl Zeichen des Strings : int i = a.length( ) ; // Methode, nicht Attribut wie bei Arrays ! Zeichen an Position : char c = a.charAt(3) ; // Indizierung beginnt bei 0 ! Position des ersten Vorkommens : int i = a.indexOf( "al" ) ; Auch Prüfung, ob Substring überhaupt in anderem String vorkommt Position des letzten Vorkommens : Liefert -1 wenn nicht .. int i = a.lastIndexOf( 'l' ) ; Teilstring ab Position : String s = a.substring(2) ; // restlicher String bis zum Ende StringIndexOutOfBoundsException, wenn ungültiger Bereich Teilstring von bis Position : String s = a.substring(2,5) ; // bis ausschließlich Position 5 Beginn/ Ende mit String : boolean w = a.startsWith( "Ha" ) ; boolean w = a.endsWith( "llo" ) ; Vergleich auf lexikografische Anordung : int n = a.compareTo( b ) ; n = a.compareToIgnoreCase( b ) ; // Wert <0 oder 0 oder >0 .... (u.v.a.m. !! ) Programmieren 2 - H.Neuendorf (10) Erzeugen von String-Objekten a) aus Stringkonstanten : String s = "einfacher String" ; oder als neues Objekt : String s = new String( "einfacher String" ) ; b) aus char-Array : char[ ] ch = { 'h', 'a', 'l', 'l', 'o' } ; String s = new String( ch ) ; Teilstring : String s = new String( ch, 1, 3 ) ; // liefert "all" c) noch weitere Konstruktoren – siehe JDK-Doku … "Nachteil" String-Objekte → Nicht editierbar = immutable objects Bei jeder Veränderung wird neues String-Objekt erzeugt ⇒ benötigt Ressourcen Einmal angelegte Instanz kann nicht verändert werden Macht sich bei intensiven Operationen bemerkbar – z.B. Gesamtinhalt eines Datenbehälters als String Alternative : Verwenden von StringBuffer- oder StringBuilder -Instanzen … Programmieren 2 - H.Neuendorf (11) java.lang.StringBuffer und StringBuilder Repräsentieren variable Zeichenketten – mutable objects ! Viele Methoden liefern this-Referenz auf StringBuffer-Objekt zurück Ermöglicht Aufruf-Verkettung (invocation chaining) StringBuffer-Objekte veränderbar - ohne dass neues Objekt erstellt + ursprüngliche Fassung verworfen wird Konstruktoren : Stringbuffer( ) StringBuffer b = new StringBuffer( "Hallo" ) ; // Konstruktor nötig ! b.append( x ) ; Stringbuffer( int capacity ) … // Typ: alle primitiven, String, StringBuffer, char[ ], Object … b.length( ) ; // Anzahl Zeichen b.insert( pos, x ) ; // Einfügen x an Position pos Verkettung mit + nicht möglich ! b.delete( from, to ) ; b.substring( from, to ) ; // … // Konversion String ↔ StringBuffer: String s = new String( b ) ; StringBuilder ist nicht synchronisierte, nicht thread-sichere aber effizientere Variante von StringBuffer Gleiche Methoden und Konstruktoren s = b.toString( ) ; b = new StringBuffer( s ) ; s = new StringBuffer().append("das").append(1).append(". Semester").toString( ) ; Vorsicht bei equals ! Liefert nur bei identischem Objekt true ! Denn : StringBuffer sind veränderlich Programmieren 2 - H.Neuendorf (12) Objekt als String - Repräsentation → class Mitarbeiter { private String nachname ; private String vorname ; public Mitarbeiter( String nn, String vn ) { nachname = nn ; vorname = vn ; toString( ) Jede Klasse kann Methode implementieren : public String toString( ) Genauer : Überschreibt von Object geerbte Variante, die Klassenname@Hashcode in hex ausgibt } public String toString( ) { String s = vorname + " " + nachname ; return s ; Aufgerufen, wenn Objekt an Ausgabemethode übergeben wird Überladene Variante von Methode println() in Klasse PrintStream ruft Methode toString( ) auf } } Objekt soll repräsentative textuelle Beschreibung seiner selbst liefern class Personalliste { Typischerweise Information über Zustand = aktuelle Attributwerte - z.B. um Objektzustand zu speichern + Objekt später rekonstruieren zu können public static void main( String[] args ) { Mitarbeiter m1 = new Mitarbeiter( "Mann", "Peter" ) ; IO.writeln( "Inhalt: " + m1 ) ; Inhalt frei modellierbar – aber String-Rückgabe System.out.println( m1 ) ; } Auch alle Hüllklassen besitzen adäquate toString() -Methoden } Programmieren 2 - H.Neuendorf (13) Typen ↔ String Stringkonvertierungen a) Datentyp → String : String s = String.valueOf( x ) ; // x ∈ char, short, int, long, float, double, boolean, char[ ], Object String s = " Wert = " + 12.5 ; // Operator + konvertiert Liefert "null" wenn obj == null - sonst obj.toString() b) String → char-Array : char[ ] ch = s.toCharArray() ; c) String → Zahl, boolean : // liefert char-Array mit Inhalt des Strings s Mittels Hüllklassen (außer Character) s.u. boolean bb = Boolean.parseBoolean( "true" ) ; byte b = Byte.parseByte( "12" ) ; short s = Short.parseShort( "123333" ) ; Wenn String keinen typ-zulässigen Wert darstellt, wird NumberFormatException geworfen. int i = Integer.parseInt( "1234.68" ) ; Boolean liefert jedoch false long l = Long.parseLong( "123" ) ; float f = Float.parseFloat( " 3.14f " ) ; double d = Double.parseDouble( "123.79" ) ; Byte.parseByte( String, int base ); Short.parseShort( String, int base ); Integer.parseInt( String, int base ); Long.parseLong( String, int base ); Vorgabe numerische Basis zwischen 2 und 32 Wenn nicht passend NumberFormatException Programmieren 2 - H.Neuendorf (14) Erzeugt Objekt mit durch pattern vorgegebenen Format. pattern kann sich zusammensetzen aus Platzhaltern für : # Ziffer, führende Nullen werden nicht angezeigt Nachkommastellen genaue Zahl der angezeigten Ziffern . Dezimalpunkt (landesspezifisch angezeigt) , Tausendertrennung (landesspezifisch angezeigt) % Darstellung als Prozentzahl E Trennt Platzhalter für Mantisse und Exponent Alle anderen Zeichen ohne vordefinierte Bedeutung werden direkt in formatierten String übernommen ! Methoden : String format( double x ) String format( long x ) Liefert Inhalt von x gemäß Pattern-Vorgabe als String. import java.text.DecimalFormat ; class Format { public static void main( String[] args) { 0 Ziffer, führende Nullen werden angezeigt. Bei } DecimalFormat( String pattern ) } Konstruktor : IO.writeln( s ) ; // Ausgabe: 24.522,46 Formatierung von Dezimalzahlen zur Ausgabe DecimalFormat f = new DecimalFormat( "###,##0.00" ) ; String s = f.format( 24522.4567 ) ; Darstellung von Zahlenstrings - java.text.DecimalFormat Programmieren 2 - H.Neuendorf (15) Darstellung von Zahlenstrings java.text.DecimalFormat import java.text.DecimalFormat ; class Format { public static void main( String[ ] args ) { DecimalFormat f1 = new DecimalFormat( "###,###.##" ) ; DecimalFormat f2 = new DecimalFormat( "Wert: 000,000.00000 Euro" ) ; DecimalFormat f3 = new DecimalFormat( "Prozente = ###.## %" ) ; DecimalFormat f4 = new DecimalFormat( "#.#E000" ) ; String s = f1.format( 24522.4567 ) ; IO.writeln( s ); s = f2.format( 98.765 ) ; IO.writeln( s ) ; s = f1.format( 98.765 ) ; IO.writeln( s ) ; Ausgaben : s = f3.format( 55.123456 ) ; IO.writeln( s ) ; 24.522,46 s = f4.format( 0.123456789 ) ; IO.writeln( s ) ; Wert: 000.098,76500 Euro ausgabe( f4, 4568.56 ) ; 98,77 Prozente = 5512,35 % } // Generische Methode zur formatierten Ausgabe : public static void ausgabe( DecimalFormat f, double d ) { 1,2E-001 4,6E003 IO.writeln( f.format( d ) ) ; } } Programmieren 2 - H.Neuendorf (16) Zerlegen von Strings java.util.StringTokenizer Zerlegung Zeichenkette in definierte Einheiten = Token Definition Token : via delimiter zwischen zwei Tokens import java.util.StringTokenizer ; class Tokens { Konstruktoren public static void main( String[] args ) { Trennung bei Standardtrennzeichen = Leerzeichen, Tab, Zeilenende : String ein = "Hallo+ihr+DH-+ler" ; StringTokenizer( String str ) String delim = "+" ; Verwendung delim als Trennzeichen – Trennzeichen sind keine Tokens : StringTokenizer st = StringTokenizer( String str, String delim ) Festlegung, ob Trennzeichen als Tokens behandelt : new StringTokenizer( ein, delim ); StringTokenizer( String str, String delim, boolean returnDelims ) int n = st.countTokens( ) ; while( st.hasMoreTokens( ) ) Zugriff auf Tokens IO.writeln( st.nextToken( ) ) ; Anzahl verbleibender Tokens im StringTokenizer-Objekt : } int countTokens( ) } Abfrage, ob noch Tokens vorhanden : boolean hasMoreTokens( ) { } Entnahme nächstes Token - oder NoSuchElementException : String nextToken( ) Festlegung delim als neues Trennzeichen für weitere Aufrufe : String nextToken( String delim ) Anwendung z.B. Parsen einer toString( ) Zustandsrepräsentation, Abgriff der einzelnen Attributwerte zur Objekt-Rekonstruktion Programmieren 2 - H.Neuendorf (17) Spezielle Klassen und Methoden (Infrastruktur) Erste Datenbehälter - Hüllklassen Character, Byte, Short, Integer, Long, Float, Double Autoboxing & -Unboxing BigInteger & BigDecimal Hüllklassen (Wrapper) umhüllen "primitivere" Dinge und stellen für diese durch ihre statischen und nicht-statischen Methoden erweiterte Services zur Verfügung. Bsp: Auch die Klasse String ist im Grunde eine Hüllklasse, die eine Menge von Zeichen in einem Objekt zusammenfasst und über zahlreiche Methoden zur Bearbeitung der Zeichenmenge verfügt. Programmieren 2 - H.Neuendorf (18) Hüllklassen Jeder primitive Datentyp hat korrespondierende Hüllklasse : Wrapper Konstruktoren Byte Byte( String s ) Byte( byte b ) Ohne Umwandlungs-Methoden. Short Short( String s ) Short( short s ) Nur bei Reflection von Belang. Integer Integer( String s ) Integer( int i ) Long Long( String s ) Long( long l ) Float Float( String s ) Float( float f ) Double Double( String s ) Double( double d ) Boolean Boolean( String s ) Boolean( boolean b ) Character Void Überschreiben deren abstrakte Methoden xxxValue( ) für die primitiven Typen xxx. Character( char c ) Methoden liefern internen primitiven Wert zurück : byte byteValue() short shortValue() int intValue() long longValue() float floatValue() double doubleValue() char charValue() boolean booleanValue() // Vergleiche : Numerische Hüllklassen erben von abstrakter Klasse Number equals( ) compareTo( ) Alle numerischen Hüllklassen stellen alle numerischen xxxValue() Methoden zum Cast z.Vfg. : Double d = new Double( 10.6 ) ; int i = d.intValue( ) ; // i == 10 Vergleich mit == liefert verwirrende Ergebnisse Boolean : nur booleanValue( ) Character : nur charValue( ) Programmieren 2 - H.Neuendorf (19) Hüllklassen 1. Darstellung primitiver Datentypen als Objekte Boxing ⇒ Übergabe an JSE-Datenbehälter (Collections) Einheitliche Behandlung primitiver Datentypen via Hüllklasse-Objekten Wrapper-Instanzen umhüllen Wert eines primitiven Typs 2. Verarbeitung Objekterzeugung : Double dObj1 = new Double( 23.6 ) ; Double dObj11 = 23.6 ; // Auto-Boxing Double dObj2 = new Double( "23.6" ) ; Wenn String nicht decodierbar : NumberFormatException Wertzugriff : double d = dObj1.doubleValue( ) ; Konstanten: MIN_VALUE MAX_VALUE d = dObj1 ; // Auto-Unboxing Ganzzahltypen: negativster / positivster Wert des Wertebereichs Fließkommatypen: kleinster / größter darstellbarer positiver Wert 3. Mathematische Fließkomma-Grenzwerte Spezielle Werte durch Double und Float prüfbar : NaN undefinierter Wert ← 0.0 / 0.0 INFINITY ← 1.0 / 0.0 Double d = 5.1 / 0 ; if( d.isInfinite( ) ) { /* …. */ } if( d.isNaN( ) ) { /* …. */ } Programmieren 2 - H.Neuendorf (20) Hüllklassen - typische Methoden Vergleich → true, falls gleicher Typ (!) und Wert : boolean b = ob1.equals( ob2 ) ; Vergleich → <0, =0, >0 nur anwendbar für gleichen Typ : Konstanten und Methoden in Float + Double : int n = ob1.compareTo( ob2 ) ; public static final float / double Wert als Zeichenkette : POSITIVE_INFINITY NEGATIVE_INFINITY String toString( ) NaN Zeichenkette in Darstellung gemäß Basis : // … Integer.toString( int val, int base ) Long.toString( long val, int base ) public boolean isNaN( ) Erzeugen Hüllklassen-Objekt durch statische Factory : valueOf( String s ) // oder mit primitivem Typ // bei Character nur mit char valueOf( String s, int base ) NumberFormatException wenn s nicht typgerecht - zB Integer.valueOf( 1.5 ) isInfinte( ) isNaN( double d ) isInfinite( double d ) isNaN( float f ) isInfinite( float f ) Programmieren 2 - H.Neuendorf (21) Hüllklassen - Beispiele // Erzeugen Zahl-Objekt : // Prüfung "nicht definierter" Werte : Integer intObj = new Integer( 15 ) ; Double d = 1.0 / 0.0 ; intObj = 12 ; // Autoboxing intObj = Integer.valueOf( "1050" ) ; if( d.isInfinite( ) ) { /* … */ } if( d.isNaN( ) ) { /* … */ } if( Double.isNaN( 0.0/0.0 ) ) { /* … */ } // Abfrage Wert : int x = intObj.intValue( ) ; double d = intObj.doubleValue( ) ; byte b = intObj.byteValue( ) ; // Cast ! Hüllklassen-Objekte sind immutable Bei Änderung des Inhalts wird (analog zu Strings) neues Objekt mit neuem Inhalt angelegt. // String aus Hüll-Objekt : String s = intObj.toString( ) ; s = Double.toString( 45.78 ) ; s = Integer.toString( 128 , 2 ) ; // Basis 2 // Konstanten : Wertebereich … IO.writeln( "long Min: " + Long.MIN_VALUE ) ; IO.writeln( "long Max: " + Long.MAX_VALUE ) ; Programmieren 2 - H.Neuendorf (22) Wrapper java.math.BigInteger + BigDecimal Hintergrund & Motivation int-Wertebereich von -231 bis 231-1 // Falsche Resultate - Bsp : ⇒ int i = 2000000000 ; Kritische Operationen +, *, -, / IO.writeln( i + i ) ; // Überlauf bei + Bei Überlauf wird in Java keine Ausnahme ausgelöst ! IO.writeln( i * 3 ) ; // Überlauf bei * Es wird einfach mit dem entstehenden Bitmuster gearbeitet i = Integer.MIN_VALUE ; IO.writeln( i ) ; Ausführung wird fortgesetzt ! IO.writeln( --i ) ; Entwickler-Pflicht : // Überlauf bei – double d = 1E36 ; Erkennen und Vermeiden von Überläufen IO.writeln( d + 0.2446 ) ; // wirkungslos Statt int stets long verwenden ist keine (gute) Lösung Analoge Handhabung : BigDecimal für Gleitkommazahlen Lösung : Verwenden von BigInteger -Objekten Genaues Rechnen mit beliebig großen / kleinen GKZ Erforderlich bei Verrechnen von GKZ stark unterschiedlicher Größenordnung - so dass 15 Ziffern nicht ausreichen Spezielle Konstruktoren : BigDecimal( String val ) BigDecimal( BigInteger val ) BigDecimal( BigInteger val, int scale ) // liefert: val / (10scale) U.a. Divisionsmethoden incl. zahlreicher Rundungsoptionen Programmieren 2 - H.Neuendorf (23) Wrapper java.math.BigInteger Hüllklasse für beliebig große ganze Zahlen als immutable Objects Mehrere Konstruktoren - u.a. für beliebig lange Zahlen-Strings Methoden für arithmetische Operationen – erzeugen keinen Überlauf und produzieren richtige Resultate als Rückgabe-Objekt ! BigInteger b1 = new BigInteger( "-222" ) ; BigInteger b2 = new BigInteger( "55555555555555555555555" ) ; BigInteger b3 ; b3 = b1.add( b2 ) ; // Addition b3 = b1.subtract( b2 ) ; // Subtraktion b3 = b1.multiply( b2 ) ; // Multiplikation b3 = b1.divide( b2 ) ; // Division b3 = b1.mod( b2 ) ; // Modulo int cmp = b1.compareTo( b2 ) ; Methoden für alle gängigen Vergleichsoperationen … … und weitere interessante Methoden (Primzahltest, gcd, pow, min, max, abs …) // cmp : <0 falls b1<b2 cmp = b1.signum( ) ; // Größenvergleich >0 falls b1>b2 boolean b = b1.equals( b2 ) ; … usw. ==0 falls b1=b2 // Vergleich // Vorzeichen : -1, 1 oder 0 Konversionen : String s = b1.toString( ) ; // Wert als String byteValue() shortValue() intValue() longValue() floatValue() doubleValue() int a = b1.intValue( ) ; // Wert als primitiver Typ double d = b1.doubleValue( ) ; Programmieren 2 - H.Neuendorf (24) Spezielle Klassen und Methoden (Infrastruktur) equals - hashCode equals( ) hashCode( ) equals-hashCode-Kontrakt Programmieren 2 - H.Neuendorf (25) Spezielle Methoden : equals( ) + hashCode( ) public boolean equals( Object o ) { /* … */ } // von Object geerbt – dort Identitäts-Check Drückt aus, ob Instanzen äquivalente Entität repräsentieren Überschreiben, wenn verschiedene Instanzen äquivalente Entity repräsentieren können Voraussetzung : Klärung der semantische Bedingungen - was sind die relevanten (unveränderlichen!) Attribute ? Asymmetrische Aufrufsyntax : boolean b = m1.equals( m2 ) ; Reflexivität a.equals(a) == true Symmetrie a.equals(b) == b.equals(a) Transitivität a.equals(b), b.equals(c) ⇒ a.equals(c) Implementierungsregeln : Konsistenz a.equals(b) liefert immer selben Wert Instanzen unterschiedlicher Klassen sind stets verschieden ! Bivalenz : Rückgabe stets true oder false - keine Exceptions ! Signatur einhalten – nicht überladen statt überschreiben ! class Mitarbeiter { } Nur nicht-veränderliche Attribute als Kriterium heranziehen ! class Mitarbeiter { // ... // ... public boolean equals( Object o ) { } public boolean equals( Mitarbeiter m ) { } } Verletzt Transitivität ! Programmieren 2 - H.Neuendorf (26) Spezielle Methoden : equals( ) - Implementierung class Mitarbeiter { private int persNr ; private String name ; Relevantes Attribut sei eindeutige + unveränderliche Personalnummer Mitarbeiter( int nr, String nn ) { /* ... */ } public boolean equals( Object o ) { // 1. korrekte Signatur wählen // 2. Identitätstest – leichter Performanzgewinn bei Identität - nicht unbedingt nötig : if( this== o ) return true ; // 3. NullpointerException / unterschiedliche Klassentypen / ClassCastException ausschließen : if( o==null || this.getClass( ) != o.getClass( ) ) return false ; getClass( ) wird von Object vererbt. Liefert identisches Class-Objekt bei identischem Klassentyp. // 4. Cast auf Typ der relevanten Klasse : Mitarbeiter m = (Mitarbeiter) o ; // 5. relevanter Attributvergleich - liefert true oder false : return this.persNr == m.persNr ; } Korrekte Signatur ! Keine Exceptions erzeugen ! Bivalenz ! } Programmieren 2 - H.Neuendorf (27) Spezielle Methoden : equals( ) - Mehrere Attribute import java.util.Date ; Schema : class Person { // relevant seien: name, gebOrt, gebDatum Teste jedes relevante Attribut private String name ; private String vorname ; private String gebOrt ; Stimmt nicht überein ⇒ false Am Ende ⇒ true private Date gebDatum ; public Person( … ) { /* ... */ } public boolean equals( Object o ) { if( this == o ) return true ; if( o == null || this.getClass( ) != o.getClass( ) ) return false ; Für jede Klasse identischer Code-Teil Person p = (Person) o ; if( ! this.gebName.equals( p.gebName ) ) return false ; if( ! this.gebOrt.equals( p.gebOrt ) ) return false ; if( ! this.gebDatum.equals( p.gebDatum ) ) return false ; ! equals( ) != bei Objekttypen bei primitiven Typen return true ; } } Programmieren 2 - H.Neuendorf (28) Spezielle Methoden : equals( ) - in Vererbungshierarchie class FarbPunkt extends Punkt { // x,y sind relevant private String farbe ; // zusätzliches relevantes Attribut public FarbPunkt( int x, int y, String farbe ) { super( x, y ) ; this.farbe = farbe ; Schema : Delegation der Prüfung von Superklasse-Attributen an die Superklasse } Voraussetzung : public boolean equals( Object o ) { if( this == o ) return true ; Auch diese hat equals() korrekt implementiert if( o == null || o.getClass( ) != getClass( ) ) return false ; if( ! super.equals( o ) ) return false ; // als Punkt gleich ? Delegation an Superklasse FarbPunkt that = (FarbPunkt) o ; Diese prüft auf Gleichheit der Koordinaten (x,y) if( ! farbe.equals( that.farbe ) ) return false ; return true ; Voraussetzung für jede weitere sinnvolle Prüfung } } Kontrakt : Wer equals( ) überscheibt, der muss auch hashcode( ) überschreiben … Programmieren 2 - H.Neuendorf (29) Spezielle Methoden : equals( )-hashCode( ) - Kontrakt public int hashCode( ) { /* … */ } // von Object geerbt - liefert identityHashcode Einhaltung Kontrakt : Wer equals( ) überschreibt, muss auch hashCode( ) überschreiben ! Sonst funktionieren Hash-basierte Container nicht ! Tabelle HashCode → HashBucket Hashcode-basierte Schlüsselindizierte Objekt-Verwaltung : HashBuckets 934 935 Hashfunktion liefert für jedes Objekt int-Wert = Hashcode 936 Objektreferenzen in HashBuckets abgelegt 937 Objekte mit gleichem HashCode im gleichen HashBucket 938 HashBuckets über Tabelle verwaltet 939 Zugriff auf HashBucket mit HashCode Größe der HashBuckets variiert dynamisch equal objects must have equal hash codes ! API-Dokumentation zu Object.equals( ) Programmieren 2 - H.Neuendorf (30) Spezielle Methoden : hashCode( ) - Implementierung class Mitarbeiter { int persNr ; // relevantes eindeutiges Attribut String name ; import java.util.HashSet ; class Container { public static void main( String[ ] args ) { Mitarbeiter( int nr, String nn ) { /* ... */ } Mitarbeiter m1 = new Mitarbeiter( 2, "Hans" ) ; Mitarbeiter m2 = new Mitarbeiter( 2, "Hans" ) ; HashSet h = new HashSet( ) ; public boolean equals( Object o ) { h.add( m2 ) ; if( this== o ) return true ; boolean test = h.contains( m1 ) ; if( o==null || this.getClass( ) != o.getClass() ) // liefert nur bei Überschreiben return false ; // von hashCode( ) true ! Mitarbeiter m = (Mitarbeiter) o ; return this.persNr == m.persNr ; public int hashCode( ) { return persNr ; // oder Funktion davon ... } } } } } // ansonsten nicht im Container gefunden ! Methoden equals( ) und hashCode( ) müssen aneinander angepasst sein ! Idealfall : Nur auf nicht-veränderliche Attribute beziehen ! Programmieren 2 - H.Neuendorf (31) Spezielle Methoden : hashCode( ) - bei Vererbung class Punkt { class FarbPunkt extends Punkt { private int x ; private int y ; private String farbe ; public Punkt( int x, int y ) { public FarbPunkt( int x, int y, String farbe ) { this.x = x ; this.y = y ; super( x, y ) ; } this.farbe = farbe ; } public boolean equals( Object o ){ /* ... */ } public boolean equals( Object o ) { /* ... */ } public int hashCode( ) { public int hashCode( ) { return 11 * (3 + x) + y ; return super.hashCode( ) + farbe.hashCode( ) ; // alternativ : return x + y ; } } } } Delegation des entsprechenden Teils der hashCode-Berechnung an die Oberklasse Anmerkung : Eclipse generiert equals( ) und hashCode( ) - Methoden Source → Generate hashCode() and equals() … Programmieren 2 - H.Neuendorf (32) Spezielle Klassen und Methoden (Infrastruktur) Aufzählungstypen - Enumerations Java 5 Bedeutung Aufzählungstyp Workarounds vor Java 5 Enum-Konzept + Minimalstruktur Verwendungsregeln Verschieden komplexe Ausbaustufen Deutlich mächtiger als in C++ … Programmieren 2 - H.Neuendorf (33) Aufzählungstypen - Enumerations Aufzählung = Zur Compilezeit bekannte, begrenzte Menge benamter Konstanten Konventionelle Hilfskonstruktion : Klassen mit fester Menge von Konstanten final class Mengen { // zu simpel public static final int Klein = 0 ; final class Karten { // angemessen public static final int Mittel = 1 ; public static final int Gross = 2 ; public static final Karten HERZ = new Karten( "Herz", 1 ) ; public static final Karten KARO = new Karten( "Karo", 2 ) ; private Mengen( ) { } } public static final Karten PIK = new Karten( "Pik", 3 ) ; public static final Karten KREUTZ = new Karten( "Kreutz", 4 ) ; private Karten( String s, int n ) { name = s ; rang = n ; } private final String name ; private final int rang ; public String toString( ) { return name ; } Realistische Nachbildung des enum-Konzepts durch Klassen mit festgelegter begrenzter Zahl konstanter, immutabler Objekte als statische Attribute public int getRang( ) { return rang ; } // … } Programmieren 2 - H.Neuendorf (34) Echte Enumerations Java 5 class Preise { private double stueckPreis; Schlüsselwort enum private enum Mengen { Klein, Mittel, Gross ; } Konzept im objektorientierten Kontext public Preise( double p ) { stueckPreis = p; } public double gesamtPreis( int zahl ) { Java-Enums Mengen m = Mengen.Klein ; Besondere finale Klassen → double preis = 0.0 ; if( zahl > 100 ) Keine Objekte davon mit new direkt instanziierbar oder Unterklassen ableitbar m = Mengen.Mittel ; if( zahl > 1000 ) m = Mengen.Gross ; switch( m ) { case Klein : preis = 1.0*zahl*stueckPreis ; break; case Mittel : preis = 0.9*zahl*stueckPreis ; break; Inhalt : case Gross : preis = 0.8*zahl*stueckPreis ; break; Vorgegebene Menge von Objekten } Selbst von java.lang.Enum abgeleitet return preis ; } public static void main( String[ ] args ) { In Klassen oder separat anlegbar … Preise tester = new Preise( 100.0 ) ; IO.writeln( "Preis = " + tester.gesamtPreis( 780 ) ) ; } } Programmieren 2 - H.Neuendorf (35) Enum-Verwendungsregeln Deklaration + Aufzählungs-Inizialisierung : enum Mengen { Klein, Mittel, Gross ; } Klasse Mengen angelegt - enthält als statische Elemente die immutablen Objekte Klein, Mittel und Gross vom Enum-Typ Mengen. Jede Enum ist eigener Namensraum ⇒ gleiche Namen in verschiedenen enums ok : enum Mengen { Klein, Mittel, Gross ; } enum Gebinde { Klein, Mittel, Gross ; } 1. Nicht zu anderen Typen cast-bar 2. Typsicher – nicht mit anderen Typen verwechselbar : Mengen m = Mengen.Klein ; // OK Mengen m = Gebinde.Klein ; // Typ-Fehler m=2; // Typ-Fehler 3. Typsicherheit bei Methodenaufrufen Methode : double preis( int zahl, Mengen mArt ) { … } Aufruf : preis( 100 , Mengen.Mittel ) ; // ok Aufruf : preis( Mengen.Mittel , 100 ) ; // Typ-Fehler Programmieren 2 - H.Neuendorf (36) Enum-Verwendungsregeln Enums in switch-case verwendbar + iterierbar : for( Mengen m : Mengen.values( ) ) { /* … */ } Vergleichsoperationen : == und != zulässig … nicht aber <, >, <=, >= Mengen m = Mengen.Klein ; if( m == Mengen.Gross ) IO.writeln( "Grosse Menge" ) ; toString( ) liefert Name des enum-Objekts : Mengen m = Mengen.Klein ; IO.writeln( "Inhalt: " + m ) ; // Inhalt: Klein IO.writeln( "Inhalt: " + Mengen.Mittel + " " + Mengen.Gross ) ; // Inhalt: Mittel Gross Intern Ordnungsnummer gespeichert : Mengen m = Mengen.Mittel ; IO.writeln( "Inhalt: " + m.ordinal( ) ) ; IO.writeln( "Inhalt: " + Mengen.Gross.ordinal( ) ) ; Collections-Framework JDK 5 : enum-Container // Inhalt: 1 // Inhalt: 2 java.util.EnumSet + EnumMap Programmieren 2 - H.Neuendorf (37) Komplexe Enums class Preise { enum stellt Klasse dar ⇒ Kann Methoden, Konstruktoren, Attribute besitzen private double stueckPreis; public Preise( double p ) { stueckPreis = p ; } private enum Mengen { // Reihenfolge !! // Konstruktoraufrufe : Klein( 0 ) , Mittel( 100 ) , Gross( 1000 ) ; Konstruktoren müssen private sein : // Konstruktor : enum-Objekte nicht von außen erzeugbar private Mengen( int w ) { grenzWert = w ; } Via Konstruktor können Attributwerte belegt werden - durch Methodenaufrufe abfragbar KonstruktorAufrufe müssen vor KonstruktorDeklaration stehen ! // Attribut : private final int grenzWert ; // Methode : Attribute sollten private final sein public int getWert() { return grenzWert ; } Öffentliche Attribute sind zugreifbar public String toString( ) { return "Grenze: " + grenzWert ; } Methoden dürfen public sein - sind auf enum-Objekten aufrufbar } public double gesamtPreis( int stueck ) { Enum-Klasse kann IFs implementieren Jede enum ist Comparable & Serializable Mengen m = Mengen.Klein ; if( stueck > Mengen.Mittel.getWert( ) ) m = Mengen.Mittel ; if( stueck > Mengen.Gross.getWert( ) ) m = Mengen.Gross ; // … Programmieren 2 - H.Neuendorf (38) Komplexe Enums enum = Klasse … kann für sich allein stehen ! Kann enum-Konstanten-spezifische Methoden-Implementierungen für abstrakte Methoden enthalten : Jede enum-Konstante muss abstrakte Methode in eigenem Block überschreiben Aufzählungskonstanten enthalten dadurch spezifische Funktionalität : double d = Operation.PLUS.apply( 3.2, 4.5 ) ; enum Operation { PLUS( "+" ) { double apply( double x, double y ) { return x + y ; } }, MINUS( "-" ) { double apply( double x, double y ) { return x – y ; } }, TIMES( "*" ) { double apply( double x, double y ) { return x * y ; } }, DIVIDE( "/" ) { double apply( double x, double y ) { return x / y ; } }; private final String symbol ; private Operation( String symbol ) { this.symbol = symbol ; } public String toString( ) { return symbol ; } abstract double apply( double x, double y ) ; Kann eigene main( ) enthalten und noch manches mehr … public static void main( String[ ] args ) { double x = … ; double y = … ; for( Operation op : Operation.values( ) ) IO.writeln( "Res: " + x + op + y + "=" + op.apply( x, y ) ) ; } F.Esser "Java 5 im Einsatz", S.154 ff J.Bloch "Effective Java", S.147 ff } Programmieren 2 - H.Neuendorf (39) Spezielle Klassen und Methoden (Infrastruktur) Clonen von Objekten Spezifikation Clone-Vorgang Grundsätzliche Verhaltensweisen einer Klasse Java-Clone-Mechanismus : IF java.lang.Cloneable (speziell: Konstruktor-Ignoranz) Einfache Fälle / komplexere Fälle (tiefes Clonen) Clonen in Klassenhierarchie / Verhalten Unterklassen Alternative Clone-Mechanismen Java-Klasseninfrastruktur Programmieren 2 - H.Neuendorf (40) Clonen von Objekten Grundsätzliche Frage : Soll Klasse das Clonen ihrer Objekte unterstützen ? Clone-Methode : Liefert neues Objekt im gleichen Anfangszustand wie Ursprungsobjekt Spätere Veränderungen der Objekte sollen sich nicht beeinflussen ⇒ Ziel ist tiefe Kopie ! Spezifikation : x.clone() != x x.clone().getClass() == x.getClass() x.clone().equals( x ) Immutable Objects müssen und sollten nicht geclont werden, da immer identisch mit Orginal … ist true ! Serialisieren von Objekt-Strukturen als Byte-Strom + anschließendes Deserialisieren zu äquivalenter Kopie ist kein guter Ersatz fürs Clonen ! Zombies Programmieren 2 - H.Neuendorf (41) Clonen von Objekten Grundsätzlich plausible Verhaltensweisen einer Klasse : 1. Unterstützung von clone( ) ⇒ Implementieren von IF java.lang.Cloneable + klassenspezifische Methode public clone( ) 2. Verbot von clone( ) ⇒ IF Cloneable + public clone() nicht implementiert. Besser als schlechte / falsche Kopie ! 3. Nur Unterklassen unterstützen clone( ) ⇒ Klasse selbst implementiert IF Cloneable nicht, implementiert aber protected clone( ) ⇒ Unterklassen können IF Cloneable implementieren und dazu intern super.clone( ) verwenden 4. Bedingte Unterstützung von clone( ) ⇒ public clone( ) throws CloneNotSupportedException Klasse implementiert IF Cloneable + Methode public clone( ) – gibt jedoch Clone-Exceptions ihrer inneren assoziierten Objekte nach außen, falls diese selbst nicht geclont werden können Programmieren 2 - H.Neuendorf (42) Clonen : Interface java.lang.Cloneable Geerbt von Object : protected Object clone( ) throws CloneNotSupported Exception ⇒ Ziel tiefe Kopien : nicht direkt auf Objekten aufrufbar ! Object clone( ) von Object erzeugt aber flache Kopien ! ⇓ Durch klassenspezifisches public clone( ) überschreiben + IF Cloneable implementieren Interface Cloneable enthält keine Methoden ⇒ Reiner Marker ! Klasse, die Cloneable implementiert, zeigt an, dass clone( ) korrekte Objekt-Kopien erzeugt Wird clone( ) für Objekte aufgerufen, die zwar public clone( ) intern mittels Object.clone() implementieren nicht aber Cloneable, so wird kontrollierte CloneNotSupportedException geworfen ! Exception tritt nicht auf, wenn in public clone( ) intern nicht Object.clone( ) gerufen wird ! Object → protected Object clone( ) : Prüft, ob aufrufendes Objekt IF Cloneable implementiert Clone als Typ Object in flacher Kopie erzeugt ⇒ Downcast erforderlich ! Bitweise Kopie durch native Methode - keine Konstruktoraufrufe ! *) Object.clone( ) ist nicht threadsafe ! *) Kritik an clone( ) : "Extralinguistische" Objekterzeugung J.Bloch, Effective Java Programmieren 2 - H.Neuendorf (43) Clonen von Objekten class Mitarbeiter implements Cloneable { // !! Einfachster Fall : private int id ; Flache Kopie ausreichend ⇒ public Mitarbeiter( int i, double g ) { id = i ; IF Cloneable implementieren private double gehalt ; gehalt = g ; } + // 1.Alternative - Verwender muss casten : In public clone( ) einfach super.clone( ) aus Object rufen public Object clone( ) throws CloneNotSupportedException { return super.clone( ) ; + } Casten des Resultats durch Verwender (1.Alternative) oder intern durch Methode selbst (2.Alternative) oder Exception intern behandeln // 2. Alternative – ab Java5 – interner Cast : public Mitarbeiter clone( ) throws CloneNotSupportedException { return (Mitarbeiter) super.clone( ) ; } } Ab Java5 dürfen überschreibende Methoden einen Subtyp des Rückgabewerts der überschriebenen Methode zurückliefern Kovariante Rückgabetypen erlaubt … Will man beim Überschreiben von clone( ) mittels super.clone( ) die von Object geerbte Version aufrufen, muss IF Cloneable implementiert werden - sonst Exception ! Programmieren 2 - H.Neuendorf (44) Tiefes Clonen Flache Kopie nicht ausreichend ⇒ class Stack implements Cloneable { // ohne Fehlerhandling private int[ ] buffer ; 2-Schritt-Strategie : public Stack( int size ) { 1. Aufruf super.clone() + private int top ; buffer = new int[ size ] ; top = 0 ; } public void push( int val ) { buffer[ top ++ ] = val ; } 2. Attribute des geclonten Objekts public int pop( ) { return buffer[ --top ] ; } clonen + zuweisen public Stack clone( ) { Beim Clonen mit super.clone( ) wird Konstruktor nicht durchlaufen ⇒ Stack s = null ; // die neue Kopie try { Nicht flache Attribute, die geclont werden müssen, dürfen nicht final sein !! s = ( Stack ) super.clone( ) ; // 1. Schritt s.buffer = buffer.clone( ) ; // 2.Schritt Nachträgliche Zuweisung in clone( ) wäre dann nicht möglich ! return s ; } catch( CloneNotSupportedException e ) { Dann müsste Kopie in clone( ) intern doch mittels Konstruktoraufruf statt mit super.clone( ) erzeugt werden // selbst behandeln … } return s ; Wäre buffer final, dann wäre die (für tiefe Kopie nötige) Neu-Zuweisung im 2.Schritt nicht erlaubt ! } Seit Java5 liefert clone( ) auf Array direkt Array vom Typ des Originals ! Hier kein Cast nötig ! } Programmieren 2 - H.Neuendorf (45) Tiefes Clonen Wenn alle Typen tiefes Clonen unterstützen, gelingt tiefe Kopie mit clone( ) sehr einfach … Arrays : Selbst tief kopiert – aber nicht ihr Inhalt, wenn Referenztyp ! Strings : Unproblematisch - da stets neues String-Objekt erzeugt wird bei Veränderung ! Andere Ref-Typen : Müssen clone( ) oder eigenen Kopiermechanismus enthalten ! ⇓ super.clone( ) auf allen Ebenen führt zu vollständiger tiefer Kopie class Adresse implements Cloneable { private String strasse ; private int nummer ; public Adresse( String s, int nr ) { strasse = s ; nummer = nr ; } public Adresse clone( ) throws CloneNotSupportedException { return (Adresse) super.clone( ) ; } } class Person implements Cloneable { private String name ; private Adresse adresse ; public Person( String n, Adresse a ){ name = n ; adresse = a ; } public Person clone( ) throws CloneNotSupportedException { Person p = (Person) super.clone( ) ; p.adresse = adresse.clone( ) ; return p ; } } class Firma implements Cloneable { private String produkt ; private Person inhaber ; public Firma( String prd, Person p ){ produkt = prd ; inhaber = p ; } public Firma clone( ) throws CloneNotSupportedException { Firma f = (Firma) super.clone( ) ; f.inhaber = inhaber.clone( ) ; return f ; } } Programmieren 2 - H.Neuendorf (46) Clonen in Unterklassen class Mitarbeiter implements Cloneable { private int id ; Unterklassen, die IF-CloneableImplementierung erben : private double gehalt ; public Mitarbeiter( int i, double g ) { id = i ; gehalt = g ; } Können sich gegen Clonen wehren : public Object clone( ) throws CloneNotSupportedException { Indem sie clone( ) so überschreiben, dass immer CloneNotSupportedException geworfen wird. Dokumentieren - im Verwender trotz IF-Cloneable über unmögliches Clonen zu informieren. return super.clone( ) ; } } class Chef extends Mitarbeiter { // erbt Cloneable-Eigenschaft ! public Chef( int i, double g ) { super( i ,g ) ; } // Will sich nicht clonen lassen – obgleich IF geerbt : Kann man zu effektiven clone( )Methoden zwingen : public Object clone( ) throws CloneNotSupportedException { throw new CloneNotSupportedException( ) ; Wenn Oberklasse public clone( ) besitzt, die keine CNS-Exception deklariert *) } Unterklassen können nun keine CNS-Exception werfen, da Unterklassen in überschriebenen Methoden keine Ausnahmen hinzufügen dürfen! } Könnten nur Ungecheckte Exception werfen *) Es muss keine throws CNS-Klausel in Oberklassen erscheinen, wenn diese in Oberklassen-clone()-Methode mit try-catch intern behandelt wird ! Programmieren 2 - H.Neuendorf (47) Clonen in Unterklassen Unterklassen : Durchgängige clone( )-Implementierung in allen Klassen der Hierarchie. Ermöglicht auch in Unterklasse tiefe Kopie mit zur Verfügung stehenden clone( ) -Methoden. super.clone( )- Mechanismus wird durchgängig verwendet. Nachteile : Unterklasse hängt von sinnvoller clone( )Implementierung ihrer Oberklassen ab. Wenn Oberklasse keine sinnvolle clone()-Methode hat, können Unterklassen das IF Cloneable nicht sinnvoll implementieren Unterklasse wird durch geerbte clone()-Methode zu eigenständigen clone()-Verhalten gezwungen ⇓ Alternative Clone-Mechanismen … class Dummy implements Cloneable { public Dummy clone( ) throws CloneNotSupportedException { return (Dummy) super.clone( ) ; } } class Mitarbeiter implements Cloneable { private int id ; private Dummy dd ; public Mitarbeiter( int i , Dummy d ) { id = i ; dd = d ; } public Mitarbeiter clone( ) throws CloneNotSupportedException { Mitarbeiter m = (Mitarbeiter) super.clone( ) ; m.dd = dd.clone( ) ; return m ; In Konstruktoren besser } defensive copies erstellen } class Chef extends Mitarbeiter { private int gehalt ; public Chef( int nr, int g , Dummy d ) { super( nr , d ) ; gehalt = g ; } public Chef clone( ) throws CloneNotSupportedException { Chef c = (Chef) super.clone( ) ; return c ; } } Programmieren 2 - H.Neuendorf (48) Alternative Clone-Mechanismen – doch via Konstruktoraufruf Copy-Konstruktor : class Mitarbeiter { private int id ; Verwendet Referenz auf bereits erzeugtes Objekt als Argument. Dies wird intern zur Objekterzeugung ausgewertet private double gehalt ; public Mitarbeiter( int id, double gehalt ) { this.id = id ; this.gehalt = gehalt ; Copy-Faktory : } Statische Methode mit Wirkung analog Copy-Konstruktor public Mitarbeiter( Mitarbeiter m ) { this.id = m.id ; this.gehalt = m.gehalt ; } Vorteile gegenüber clone( ) : public static Mitarbeiter newInstance( Mitarbeiter m ) { Keine Abhängigkeit von "extralinguistischer Objekt-Erzeugung" Mitarbeiter ma = new Mitarbeiter( ) ; ma.id = m.id ; Keine Probleme bei finalen Attributen durch Verwendung von Konstruktoren return ma ; Keine Notwendigkeit von Casts } Parameter vom Interface-Typ sind möglich ⇒ Möglichkeit von Konversions-Konstruktoren und Konversions-Factories : Aus Objekten von Klassen, die das IF implementieren, können Objekte anderen Typs hergestellt werden ma.gehalt = m.gehalt ; Bei Referenzattributen komplizierter aber machbar // … // Konversions-Konstruktor : public Mitarbeiter( IFMitarbeiter m ) { /* … */ } } Programmieren 2 - H.Neuendorf (49) Java Klassen-Infrastruktur wird noch erweitert … class Konto implements Comparable, Cloneable, java.io.Serializable, AutoCloseable { private static final long serialVersioUID = 123L ; private int kontoNr ; private String name ; private boolean closed = false ; public Konto( int knr, String n ) { /* … */ } public boolean equals( Object o ) { Überschreiben / Implementieren if( this== o ) return true ; definierter Schnittstellen zur if( o==null || this.getClass( ) != o.getClass() ) return false ; korrekten Behandlung der Objekte return this.kontoNr == ( (Konto)o ).kontoNr ; in entsprechenden Umgebungen } public int hashCode( ) { return kontoNr ; } public String toString( ) { return name + " " + kontoNr ; } public int compareTo( Object o ) { return this.kontoNr – ( (Konto)o ).kontoNr ; } public Mitarbeiter clone( ) throws CloneNotSupportedException { return (Konto) super.clone( ) ; } public void close( ) { kontoNr = 0 ; name = null ; closed = true ; } public void finalize( ) { if( !closed ) close( ) ; } java.lang.Iterable { } public abstract java.util.Iterator iterator( ) ; } class NameComp implements java.util.Comparator { java.util.Iterator { public abstract boolean hasNext( ) ; public int compare( Object p, Object q ) { /* … */ } public abstract Object next( ) ; } public abstract void remove( ) ; } Programmieren 2 - H.Neuendorf (50)