Java2_SKM - DHBW Mosbach

Werbung
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)
Herunterladen