Vererbung (Inheritance)

Werbung
(1)
Vererbung (Inheritance)
Reale Welt: Dinge kommen in verschiedenen Varianten vor, die sich hierarchisch klassifizieren lassen
Kennzeichen: Dinge die hierarchisch tiefer stehen, sind speziellere Varianten der
übergeordneten Dinge (Spezialisierungshierarchie)
⇒ besitzen deren Eigenschaften + noch weitere spezifische Eigenschaften
Vererbung: Klassen können Methoden + Attribute anderer Klassen übernehmen, aber auch noch
zusätzliche Methoden + Attribute enthalten
Eine Klasse erbt von einer übergeordneten Klasse + enthält weitere Methoden + Attribute
Tier
Reptil
Säugetier
……..
…….
© H.Neuendorf
…….
Nagetier
....
..... .... .....
.......
Huftier
Pferd ...... ........
Spezielisierung
Fisch
Verallgemeinerung
Vererbungshierarchie
Relation :
"Ist ein ..."
Ein Nagetier ist ein Säugetier
Ein Säugetier ist ein Tier
Wichtig : Nur wenn eine
durchgängige "Ist-Ein"Relation besteht, macht die
Vererbungshierarchie Sinn!
Vererbung
(2)
Kann sich über viele Ebenen erstrecken ⇒ Hierarchie
Angestellter
TarifAngestellter
Unterklassen
Arbeiter
Oberklassen
Mitarbeiter
AußertarifAngestellter
Darstellung im Klassendiagramm (UML) :
Pfeilrichtung → von der speziellen zur allgemeinen Klasse, von der
Geschäftsführer
Unterklasse zur Oberklasse
Java : Eine Unterklasse kann nur eine direkte Oberklasse haben =
Einfachvererbung (keine Mehrfachvererbung in Java !)
Syntax :
Schlüsselwort extends in Klassendeklaration
© H.Neuendorf
Semantisch sinnvoll nur als
Ist-Ein Beziehung!
Alles andere führt zur
Verwirrung !!
Vererbung : Java → Schlüsselwort extends
UML :
Klassenname
Methoden()
Attribute
(3)
Person
getName()
name
Franzose
getGruss()
getGruss()
Aufbau Hierarchie :
Bayer
getGruss()
setLieblingsbier()
lieblingsbier
Obere Klassen sollen Methoden + Attribute bereitstellen / enthalten,
die auch in unteren Klassen "Sinn machen", dort verwendbar sind
( "Jeder hat einen Namen ....." )
Untere Klassen können Inhalt der oberen Klassen spezifisch
erweitern durch zusätzliche eigene Methoden + Attribute, die nur für
sie sinnvoll sind / eine besondere Ausprägung annehmen müssen.
( "Der Gruß lautet in jeder Sprache anders ...." )
Unterklasse (subtype) ist spezielle Abart der Oberklasse (supertype) :
AttributeOber ⊆ AttributeUnter
MethodenOber ⊆ MethodenUnter
© H.Neuendorf
Spezielisierungshirarchie
Deutscher
class Deutscher extends Person {
..........................
}
Vererbung - Schlüsselwort extends :
In Unterklassen dadurch alle public Methoden +
Attribute der Oberklasse verfügbar / ansprechbar
⇓
Objekte der Klassen Franzose und Deutscher
haben geerbte public Methode getName( ) und
geerbtes public Attribut name
Oberklasse Person liefert public Elemente, die
auch in Unterklassen direkt verwendbar sind !
Erweiterung in Unterklassen :
Spezifische Methode zum Grüßen:
getGruss( ) ;
© H.Neuendorf
(4)
class Person {
public Person( ) {
name = "Anonym" ;
}
public String getName( ) {
return name ;
}
public String name ;
}
class Franzose extends Person {
public Franzose( String nn ) {
name = nn ;
}
public String getGruss( ) {
return "Bonjour " + getName() ;
}
}
class Deutscher extends Person {
public Deutscher( String nn ) {
name = nn ;
}
public String getGruss( ) {
return "Mahlzeit " + getName() ;
}
}
(5)
Vererbung
class Person {
public Person( ) {
name = "Anonym" ;
}
public String getName( ) {
return name ;
}
public String name;
public int alter ;
}
class Test {
public static void main( String[] args ) {
Vorteil :
Konzentration von
Code in einer
Oberklasse, kein
Duplizieren von
Codestrecken.
Zentrales Testen,
Fehlersuchen,
Warten ...
Franzose f = new Franzose( "Jean" ) ;
IO.writeln( f.getGruss( ) ) ;
IO.writeln( f.getName( ) ) ;
f.alter = 25 ;
class Franzose extends Person {
public Franzose( String nn ) {
name = nn ;
}
public String getGruss() {
String s = "Bonjour " + getName( ) ;
return s ;
}
}
IO.writeln( f.name + " : " + f.alter ) ;
}
}
Public Attribute + Methoden von "Person"
stehen in der Klasse "Franzose" und für
Objekten vom Typ "Franzose" zur Verfügung
⇒ direkt aufrufbar / ansprechbar !
Speziell: Auch statische public Attribute und Methoden werden vererbt und
© H.Neuendorf
stehen in der Unterklasse als statische Elemente zur Verfügung
Vererbung
In Unterklassen alle public Methoden +
Attribute der Oberklasse verfügbar / direkt
ansprechbar
Aber :
Auf private Attribute + Methoden der
Oberklasse hat auch Unterklasse keinen
Zugriff !
Ebensowenig wie alle anderen Klassen !
Kein Unterlaufen der Kapselung durch
Vererbung !!
Weitere Zugriffsspezifikation :
protected
© H.Neuendorf
(anders als in C++ !! )
class Person {
public Person( ) {
name = "Anonym" ;
}
public String getName( ) {
return name ;
}
public String name;
public int alter ;
private String ort;
}
class Franzose extends Person {
public Franzose( String nn ) {
name = nn ;
}
public String getGruss( ) {
return "Bonjour" + getName( ) ;
// Nicht erlaubt :
// return "Wohnhaft: " + ort ;
}
public int getAlter( ) {
alter = 25 ;
return alter ; // ok!
}
}
(6)
(7)
Vererbung
In Unterklassen kann durchaus ein
indirekter Zugriff auf private Elemente der
Oberklasse möglich sein :
Via öffentlicher Methoden der Oberklasse !
⇓
Mit den Unterklassenobjekten werden auch
entsprechende Oberklassenobjekte mit ihren
Daten im Speicher gehalten !
⇓
Werden automatisch zusammen mit
Unterklassenobjekten erzeugt
⇓
Objekterzeugung in Klassenhierachien muss
noch gesondert untersucht werden …..
Anm: Öffentliche statische Attribute der Oberklasse (zB counter)
sind durch alle Unterklassen + ihre Objekte manipulierbar – dabei
wird für alle ein einheitlicher Wert vorgehalten.
© H.Neuendorf
class Person {
public Person( ) {
// …………….
}
public String getName( ) {
return name ;
}
public void setName( String n ) {
name = n ;
}
private String name = "Anonym";
}
class Franzose extends Person {
public Franzose( String nn ) {
setName( "Müller" ) ;
}
public String getGruss( ) {
return "Bonjour" + getName( ) ;
}
// …………………..
}
(8)
Überschreiben von Methoden
Spezialisieren geerbter Methoden in Unterklasse :
Klasse Bayer erbt von Person + von Deutscher
Bayer hat eigene Methode getGruss(), obwohl von Deutscher solche
Methode geerbt wurde
⇒
Für Bayer-Objekte wird bei getGruss()-Aufruf die eigene getGruss()Methode aufgerufen - nicht geerbte getGruss()-Methode von Deutscher !!
Überschreiben (Overriding) :
Deklaration einer Methode in Unterklasse mit gleicher
Schnittstelle / Signatur (Name, Parameter,
Rückgabewerte) wie in Oberklasse, jedoch anderer
Implementierung
Regeln :
1. Kein Einschränken der Sichtbarkeit beim Überschreiben, dh
public nicht durch protected oder private überschreibbar !
2. Nicht-statische Methode nur durch nicht-statische Methode,
statische Methode nur durch statische Methode überschreibbar!
Überschreiben: ≈ "Verdecken" der geerbten Methoden
Überladen:
≈ "Nebeneinander" von gleichnamigen Methoden
mit unterschiedlichen Parameterlisten
© H.Neuendorf
class Deutscher extends Person {
public Deutscher( String nn ) {
name = nn ;
}
public String getGruss( ) {
return "Mahlzeit";
}
}
class Bayer extends Deutscher {
public Bayer( String nn ) {
name = nn ;
}
public String getGruss( ) {
return "Grüß Gott!" ;
}
// ............................
}
Sinn : Flexibles Anpassen geerbter Methoden
an spezifische Bedürfnisse der Unterklassen,
Einbau zusätzlicher Prüfungen, Verwenden
anderer interner Datenformate ……
(10)
Überschreiben von Methoden – Einhalten Regeln
class Deutscher extends Person {
Spezielle Anmerkung :
public Deutscher( String nn ) { name = nn ; }
Modifikatoren synchronized, native und strictfp
sind Teil der Implementierung - nicht des Vertrags.
public String getGruss( ) { return "Mahlzeit"; }
Ebenso Kennzeichnung von Methodenparametern
als final.
}
Folglich können sie alle beim Überschreiben
uneingeschränkt verändert werden …
class Bayer extends Deutscher {
public Bayer( String nn ) { name = nn ; }
@Override public String getGruss( ) { return "Servus!" ; }
// OK !
// ..................................
private String getGruss( ) { return "Servus" ; }
// Fehler – Einschränken Zugriffsrechte !
public int getGruss( ) { return 11111 ; }
// Fehler – Ändern return-Typ !
public String getGruss( int n ) { return "Servus!" + n ; }
// OK – aber kein Überschreiben sondern Überladen
// Geerbte Methode getGruss() wurde überladen
Geerbte throws-Exception-Klauseln dürfen
nicht durch zusätzliche Exceptions ergänzt
werden – nur durch speziellere Variante der
Oberklassen-Exception ersetzt werden
Sinn : Einhalten Vertrag der Oberklasse
// Klasse Bayer hat nun beide Varianten
//
⇓
// Kombination von Überladen und Geerbten :
public String getGruss( int n ) { return getGruss() + n ; }
}
© H.Neuendorf
Annotation @Override bewirkt CompilerPrüfung, ob wirklich überschrieben oder
(unabsichtlich) überladen wird !
@Override
public String getGruss( int n ) { /* … */ }
Gibt Compilerfehler !
(11)
Super-Aufrufe für nicht-statische Methoden
Schlüsselwort
super.Methodenname(....)
Aufruf einer geerbten Methode der direkten
Oberklasse in der Unterklasse
Anwendung :
Unterklasse überschreibt geerbte Methode der
Oberklasse. Dennoch soll auch explizit die geerbte
Methode der Oberklasse gerufen werden.
⇓
Auch nach Überschreiben geerbter Methoden
können diese geerbten Methoden der Oberklasse
weiterhin in Unterklasse genutzt werden
In statischen Methoden steht super. nicht zur
Verfügung ⇒ Überschriebene statische
Methoden + Attribute können in Unterklassen nur
aus nicht-statischen Methoden gerufen werden
© H.Neuendorf
class Person {
public Person( ) {
name = "Anonym" ;
}
public String getName( ) {
return name ;
}
public String name ;
}
class Franzose extends Person {
public Franzose( String nn ) {
name = nn ;
}
public String getName( ) {
String n = super.getName( ) ;
n = "Name ist: " + n ;
return n ;
}
...........................................
}
(12)
Begriffe beim Vererbungsmechanismus
Oberklasse
Klassenhierachie in Projekt
wird dargestellt durch
hier Basisklasse
Strg + T
Attribute
Methoden
Unterklasse
Unterklasse
Unterklasse
Unterklasse
Abgeleitete Klasse
Abgeleitete Klasse
Abgeleitete Klasse
Abgeleitete Klasse
Neue oder
überschriebene
Attribute und Methoden
Neue oder
überschriebene
Attribute und Methoden
Neue oder
überschriebene
Attribute und Methoden
Neue oder
überschriebene
Attribute und Methoden
Einfachvererbung
Mehrfachvererbung
Unterklasse
Unterklasse
Abgeleitete Klasse
Abgeleitete Klasse
Neue oder
überschriebene
Attribute und Methoden
Neue oder
überschriebene
Attribute und Methoden
© H.Neuendorf
(13)
Methodenauswahl durch JVM
class GrussAusgabe {
Finden der richtigen
Person
auszuführenden
Methode für Objekte in getName()
Vererbungshierarchie :
public static void main( String[] args ) {
Bayer sepp = new Bayer( "Sepp" ) ;
name
IO.writeln( sepp.getName( ) ) ;
IO.writeln( sepp.getGruss( ) ) ;
Deutscher
getGruss()
}
}
Bayer
getGruss()
getGruss()
setLieblingsbier()
getName()
lieblingsbier
Unterklassenobjekte führen Methoden
aus, die in verschiedenen Ebenen der
Vererbungshierarchie angesiedelt sind !
Such-Prinzip :
getName( ): aus Klasse Person
Methode in Klasse des beauftragten Objekts vorhanden ?
getGruss( ): aus Klasse des Obj.
Ja
⇒
Nein ⇒
© H.Neuendorf
dort deklarierte Methode ausführen !
Suche in nächsthöherer Klasse der Hierarchie fortsetzen !
Überschreiben von Attributen
(14)
(seltener)
Auch gleichnamige geerbte Attribute können
überschrieben werden :
Primitive Typen ebenso wie Objekttypen
Nicht-statische ebenso wie statische Attribute
Deklaration gleichnamiger Attribute in
Unterklasse "verdeckt" Deklaration der geerbten
Attribute der Oberklasse.
class Ober {
public Ober ( ) {
testvar = true ;
}
public boolean testvar ;
}
class Unter extends Ober {
⇓
public Unter ( long a ) {
testvar = a ;
super.testvar = false ;
}
Bei Wertzuweisung wird das eigene Attribut der
Unterklasse belegt.
Auf gleichnamige geerbte Attribute der Oberklasse
kann jedoch weiterhin mittels :
// Eigene Variable "testvar"
// verdeckt die von Oberklasse geerbte Variable
super.Variablenname
zugegriffen werden.
Beim Attribut-Überschreiben darf Sichtbarkeit
eingeschränkt werden ! Typ kann sich ändern !
© H.Neuendorf
private long testvar ;
}
(15)
Vorgang beim Erzeugen von Unterklassen-Objekten
Mit Unterklassen-Objekt werden auch automatisch alle zugehörigen Oberklassen-Objekte
im Speicher angelegt, um geerbte Methoden + Attribute zugreifbar zu machen
RAM
RAM
private-Anteile
public-Anteile
Klasse Person
Klasse Franzose
Objekterzeugung Unterklassenobjekt
Franzose f = new Franzose( "Jean" ) ;
Klasse Person
Klasse Franzose
Automatisch
erzeugtes
Personen Objekt
Objekt f
Franzosen-Objekt f hat Zugriff auf die öffentlichen
Attribute und Methoden der Klasse Person.
Kann mit diesen arbeiten, als hätte es sie selbst in
seiner Klasse implementiert ............
© H.Neuendorf
Kann geerbte public
Attribute direkt mit Werten
füllen – aber auch private
Attribute indirekt mittels
geerbter public set-/getMethoden manipulieren
Konstruktoren in Klassenhierarchien
Aufruf Oberklassenkonstruktor im Konstruktor der
Unterklasse möglich – oft sogar erforderlich :
Anweisung
super( Parameterliste ) ;
Regeln :
1. Bei Anlegen Unterklassen-Objekt wird immer auch
Konstruktor der Oberklasse aufgerufen, da auch
Oberklassen-Objekt erzeugt wird – nur Oberklasse weiß, wie
ihr Zustand korrekt initialisiert wird
2. Konstruktoren werden nicht vererbt ⇒ Unterklassen
benötigen eigene Konstruktoren
3. super( ) -Aufruf muss erste Anweisung im Konstruktor der
Unterklasse sein, wenn explizit verwendet !
4. Compiler setzt automatisch parameterlosen super()-Aufruf
ein, wenn kein expliziter super()-Aufruf im Coding der
Unterklasse enthalten ist ! ⇒ Aufruf parameterloser
Oberklassen-Konstruktor !
5. Exceptions aus Oberklassen-Konstruktor können in
Unterklassen-Konstruktor nicht abgefangen werden !
6. Oberklassen-Konstruktoren sollten keine überschreibbaren
Methoden aufrufen - andernfalls dokumentieren !
© H.Neuendorf
class Person {
public Person( String nn ) {
name = nn ;
}
public String getName( ) {
return name ;
}
private String name ;
}
(16)
class Franzose extends Person {
public Franzose( String nn ) {
super( nn ) ;
}
public String getGruss( ) {
return "Bonjour" ;
}
}
Analog :
this(...) ;
Auch this( ) muss erste Anweisung sein ⇒
kann nicht mit super( ) auftreten!
(17)
Konstruktoren in Klassenhierarchien
Wenn expliziter super(...)-Aufruf in Unterklasse
unterbleibt wird automatisch parameterloser
Oberklassen-Konstruktor gerufen.
Fälle :
a) Oberklasse hat gar keinen Konstruktor :
class Ober {
public Ober ( int val ) {
value = val ;
}
private int value ;
}
class Unter extends Ober {
Parameterloser Standardkonstruktor der Oberklasse ist da
+ wird gerufen – ok !
public Unter ( int a , int b ) {
super( a ) ; // !!!!!!
number = b ;
}
private int number ;
b) Oberklasse hat einen parametrisierten Konstruktor :
Es muss parametrisierter Oberklassen-Konstruktor mit
super(….) gerufen werden !!
}
Wenn parametrisierter Konstruktor vorhanden, dann gibt es
keinen parameterlosen Standardkonstruktor !
Anmerkung :
super(....)
= Aufruf Oberklassen-Konstruktor
super.Methode(...)
= Aufruf ursprünglicher Oberklassenmethode
© H.Neuendorf
super.
this.
super(…)
this(…)
Beispiel Überschreibmechanismus →
class Tank {
private int stand ;
private int kapazitaet ;
public Tank( int kap ) {
Spezialisierung
(19)
class SicherheitsTank extends Tank {
public SicherheitsTank( int kap ) {
super( kap ) ;
}
kapazitaet = kap ;
public int fuelle( int menge ) {
if( getStand() + menge > getKapazitaet() ) {
return -1 ; // Exception werfen !!
}
else {
return super.fuelle( menge ) ;
}
}
}
public int fuelle( int menge ) {
stand = stand + menge;
return stand ;
}
public int getStand( ) {
return stand ;
}
}
public int getKapazitaet( ) {
return kapazitaet ;
}
}
Überschreiben geerbter Methoden zur Einführung
zusätzlicher semantischer oder technischer
Checks, Konsistenzprüfungen in Unterklasse
Vererbung nicht nur zur Wiederverwendung
sondern auch zur Anpassung
© H.Neuendorf
Beispiel Überschreibmechanismus →
class Oben {
Inversion of Control
(20)
class Unten extends Oben {
Möglichst nicht bei Konstruktoren
verwenden !
public void teil1( ) {
public void teil1( ) {
IO.writeln( "oben 1" ) ;
IO.writeln( "unten 1" ) ;
}
}
public void teil2( ) {
public void teil2( ) {
IO.writeln( "oben 2" ) ;
IO.writeln( "unten 2" ) ;
}
}
public void tusOben( ) {
// ……….
teil1( );
teil2( ) ;
}
Klassen, die nicht als Oberklassen
geeignet sind, sollten keine Aufrufe
öffentlicher = überschreibbarer
Methoden in ihrem eigenen Coding
enthalten !
Statt dessen sollten in den
öffentlichen Methoden dann nur
private = nicht überschreibbare
eigene Methoden aufgerufen
werden ⇒
Keine Verhaltensänderungen in der
Klasse durch Überschreiben ihrer
öffentlicher Methoden
}
class Test {
public static void main( String[] args ) {
}
Unten myU = new Unten( ) ;
Grundsätzliche Abfolge der Operationen von
tusOben() wird beibehalten, aber Details der
Bearbeitung werden von Unterklasse
angepasst. Grundidee des Template-Patterns.
Dort jedoch sauberer umgesetzt (s.u.)
myU.tusOben( ) ;
}
Ausgabe :
unten 1
unten 2
}
Unterklasse überschreibt geerbte Methoden, die in anderer Methode der Oberklasse
verwendet werden. Wird diese geerbte Methode in Unterklasse aufgerufen, so werden
darin die überschriebenen Methoden-Varianten der Unterklasse verwendet !
© H.Neuendorf
(21)
Polymorphie
Person
Vielgestaltigkeit
getName()
name
Deutscher
getGruss()
Gleichnamige Methodenaufrufe für
Objekte verschiedener Klassen einer
Vererbungshierarchie liefern
verschiedenes Verhalten
Bsp :
Aufruf der Methode getGruss( ) liefert je nach
Objekttyp unterschiedliches Verhalten !
JVM stellt bei Methodenaufruf fest zu welchem
Objekttyp die Methode gehört und führt dessen
Coding aus.
Bayer
getGruss()
getGruss()
setLieblingsbier()
getName()
lieblingsbier
Fundamentale Typkompatbilität :
Semantisch : Ausnutzen der Ist-ein Beziehung in Vererbungshierarchie
Jedes Unterklassenobjekt ist auch ein Vertreter der Oberklasse ⇒ kann diese vertreten
Technisch : Objekt der Unterklasse ist typkompatibel mit Objekt der Oberklasse
Person p ;
p=b;
© H.Neuendorf
Bayer b = new Bayer( "Sepp" ) ;
// zulässige Zuweisung ! → Upcast
Nutzen Vererbung : Typkompatibilität zwischen Ober- und Unterklasse
a) Übersichtlichkeit + "Schreibersparnis" :
(22)
Wiederverwendung von geerbtem Code
b) Kompatibilität der Unterklasse zur Oberklasse :
Ist-Beziehung, Teilmengen-Relation
Jedes Programm, das in der Lage ist, mit Objekten der Oberklasse zu
arbeiten, kann automatisch auch mit Objekten aller Unterklassen arbeiten !
Konsequenz :
Programme "laufen" prinzipiell auch mit Objekten der spezialisierten Unterklassen, ohne
angepasst werden zu müssen !
Man kann mit allgemeiner gehaltenen Klassen beginnen - und diese später zur Abdeckung
spezieller Bedürfnisse via Vererbung verfeinern !!
Person
Deutscher
Bayer
"ist ein"-Beziehung :
Franzose
is_a
is_kind_of
Mengendiagramm
© H.Neuendorf
(23)
Probleme Vererbung :
1. Unterklassen an Oberklassen gekoppelt :
Bloße Erweiterung der Oberklasse ist
meist unproblematisch : Hinzufügen
weiterer Attribute + Methoden
Vorsicht beim Weiterentwickeln der Oberklasse !
Unterklasse erbt Schnittstelle + Implementierung der Oberklasse
⇒ Invalidierungsgefahr :
Jede Änderung von public + protected-Elementen der Oberklasse wirkt sich auf Unterklassen aus !
Einseitig : Nur Unterklasse kennt ihre Oberklassen, ist auf diese angewiesen, nicht umgekehrt
⇒ Oberklassen-Entwickler merkt nicht sofort, wenn invalidierende Änderung erfolgt !
Bsp : Oberklasse wird neue public-Methode hinzugefügt, die sich nur im return-Typ von Methode
unterscheidet, die bereits in Unterklasse existiert …….
2. Unterklasse erbt von Oberklasse auch deren Fehler + Schwächen :
Fehler + Schwächen der öffentlichen Schnittstelle der Oberklasse wandern auch in die
Unterklassen - und werden zu Fehlern + Schwächen der öffentlichen Schnittstelle der
Unterklassen !
Dagegen lässt sich bei Assoziation (Attribute vom Typ der Oberklasse) eine Schnittstelle entwerfen, hinter
der diese Fehler und Schwächen verborgen werden können …..
⇒ Eine Klasse als potentielle Oberklasse zu entwerfen zwingt zu sehr sauberem Design !
⇒ Wenn Klasse nicht dafür geeignet, dann Vererbung von ihr verhindern
© H.Neuendorf
= finale Klasse, s.u.
(24)
Typkompatibilität
class GrussAusgabe2 {
Typumwandlung :
Java achtet streng auf Typkompatibilität !
double x = 3.3 ;
int y = x ;
public static void main( String[] args) {
// Fehler!
Franzose f = new Franzose( "Jean" ) ;
Person p1= f ; // Upcast !
Zuweisungen erlaubt, wenn Daten den Anforderungen des neuen Typs genügen. Dann führt Java
automatische, implizite Typumwandlung aus :
int x = 15 ;
double y = x ;
Person p2= new Bayer( "Sepp" ) ;
// erlaubt!
Bayer b = (Bayer) p2 ;
// Downcast !
b.setLieblingsbier( "Paulaner" ) ;
Bei Objektreferenzen ebenso !
Automatische Typumwandlung bei
Zuweisung an Objektvariable aus höherer
Stufe der Klassenhierarchie = UPCAST :
Person p1 = new Franzose( "Jean" ) ;
Explizite Typumwandlung erforderlich bei
Zuweisung an Objektvariable tieferer Stufe
der Klassenhierarchie = DOWNCAST :
Bayer b = (Bayer) p2 ;
© H.Neuendorf
}
}
Upcast ist immer unproblematisch : Ein
Franzose ist immer auch eine Person !
Vom Compiler ohne expliziten Cast akzeptiert
Downcast problematisch, fehleranfällig : Eine
Person ist nicht immer ein Bayer !
Vom Compiler nur durch expliziten Cast
akzeptiert !
Typkompatibilität: Upcast (safe cast)
(25)
Bayer
Person
name
lieblingsbier
getName( )
setLieblingsbier( )
getGruss( )
Bayer b = new Bayer( "Sepp" ) ;
Person p = b ;
// Upcast
Compiler verlangt :
Person-Objekt
Compiler bekommt :
Bayer-Objekt
Korrekt ! 9
⇒ Compiler bekommt "mehr", als er "verlangt" hat - aber jedenfalls "nicht zuwenig" !
⇒ Compiler nimmt, was er benötigt + "ignoriert" / "verbirgt" das "Überflüssige"
?????
⇒ Macht aus Bayer-Objekt eine Person, "reduziert" Bayer-Objekt auf Person-Objekt
Genug vorhanden, um Person-Objekt korrekt zu instanziieren
Durch "Weglassen" kann man aus Bayern eine bloße Person machen .......
⇒ Zuweisung ist korrekt und passiert die Typprüfung !
© H.Neuendorf
(26)
Typkompatibilität: Downcast (unsafe cast)
Bayer
Ausdruck markieren
Person
Strg + 1
name
lieblingsbier
getName( )
setLieblingsbier( )
Korrekturvorschlag
getGruss( )
Person p = new Person( "Paula" ) ;
Bayer b = (Bayer) p ; // Downcast
Fehler !
≠
Upcast: unkritisch, direkte Zuweisung
Compiler verlangt:
Bayer-Objekt
Compiler bekommt:
Person-Objekt
Downcast: verbotene Operation
⇒ Compiler bekommt "weniger", als er "verlangt" hat – wird durch cast gezwungen !
⇒ Das zugewiesene Person-Objekt enthält "zu wenig", um Bayer-Objekt zu "füllen"
⇒ Nicht "genug" im Person-Objekt, um Bayer-Referenz korrekt zu instanziieren
Aus bloßer Person läßt sich kein Bayer machen - es "fehlt" etwas! ...
⇒ Zuweisung nicht korrekt - ClassCastException zur Laufzeit !
© H.Neuendorf
(27)
Typkompatibilität: Downcast
Bayer
Person
name
lieblingsbier
getName( )
setLieblingsbier( )
getGruss( )
Bayer b1 = new Bayer( "Sepp" ) ;
Person p = b1 ; // Upcast
Hier : Korrekt ! 9
Bayer b2 = (Bayer) p ; // Downcast
Compiler verlangt :
Bayer-Objekt
Compiler bekommt :
Personen-Referenz - die zuvor mit Bayer-Objekt entstand
⇒ Kann aus dieser speziellen Personen-Referenz wieder Bayer-Objekt "hervorholen"
⇒ Genug "Inhalt" hinter dieser speziellen Person-Referenz, um Bayer zu instanziieren
⇒ Zuweisung korrekt - Typkonform zur Laufzeit !
Upcast: unkritisch, direkte Zuweisung
Downcast: kritisch, explizite cast-Operation
© H.Neuendorf
(28)
Typumwandlungen Casts
UPCAST :
Person p2 = new Bayer( "Sepp" ) ;
class GrussAusgabe2 {
Auf Objektreferenz p2 dürfen nur Methoden +
Attribute angesprochen werden, die schon in
Klasse Person bekannt sind !
public static void main( String[] args) {
Werden Methoden in Klasse Bayer überschrieben,
dann wird deren Version gerufen !
Person p1 = new Franzose( "Jean" ) ;
Person p2 = new Bayer( "Sepp" ) ;
Denn : p2 ist Referenz vom Typ Person, nicht
Referenz vom Typ Bayer !
IO.writeln( p2.getName( ) ) ; // ok!
p2.setLieblingsbier( "Paulaner" ) ; // Fehler!!
Bayer-spezifische Methoden + Attribute sind nicht
zugänglich !
( (Bayer) p2 ).setLieblingsbier( "Paulaner" ) ;
Bayer b1 = (Bayer) p2 ; // Downcast!
b1.setLieblingsbier( "Erdinger" ) ; // ok!
}
DOWNCAST :
(Bayer) p2
oder .....
Bayer b = (Bayer) p2 ;
Cast von p2 zu Typ Bayer
⇒
Somit nun Methoden + Attribute zugreifbar, die
spezifisch für Klasse Bayer sind, dh in Oberklasse
Person nicht existieren.
© H.Neuendorf
}
(29)
Zuweisungen + Typumwandlungen
Upcast :
unkritisch, direkte Zuweisung
class Zuweisung {
Downcast : per expliziter cast-Operation
Bayer b = (Bayer) p2 ;
public static void main( String[] args) {
// korrekt ??
Franzose f1 = new Franzose( "Jean" ) ;
Bayer b1 = new Bayer( "Sepp" ) ;
Aber :
Wenn p2 nicht auf ein Bayer-Objekt zeigt,
dann wird Laufzeitfehler ausgelöst !
Person p1, p2 ;
Laufzeit-Typprüfung mit Operator :
p1 = f1 ;
p2 = b1 ;
p2 instanceof Bayer
⇓
if ( p2 instanceof Bayer ) {
// Downcast möglich - ok!
Bayer b = (Bayer) p2 ;
b.setLieblingsbier( "Paulaner" ) ;
}
else // …….
Prüfung, ob p2 zur Laufzeit ein Objekt vom
Typ Bayer referenziert
Anm: Ein Programm ist typsicher, wenn schon zur
Compilezeit feststeht, dass kein Aufruf an ein Objekt
erfolgt, für das das Objekt keine entsprechende Methode
besitzt. Eine Programmiersprache ist typsicher, wenn alle
erzeugbaren Programme typsicher sind – so dass keine
LZ-Fehler durch falsche Datentyp-Operationen möglich
sind – es sei denn, dies wird durch Casts erzwungen.
© H.Neuendorf
// Upcast - ok!
// Upcast - ok!
}
}
(30)
Zuweisungen + Typumwandlungen
class Zuweisung {
Generell möglich :
public static void main( String[] args) {
Verwendung von Unterklassen-Referenzen
anstelle von Oberklassen-Referenzen :
Bayer b1 = new Bayer( "Sepp" ) ;
ausgabe1( b1 ) ; // Korrekt !!!!
a) bei Zuweisungen (s.o.)
b) bei Parameterübergaben :
Person p1 = new Person( "N.N." ) ;
ausgabe2( p1 ) ; // Fehler!
Wenn Methode als Parameter eine Referenz vom
Typ Person erwartet, ..............
............ dann kann auch eine Referenz vom Typ
Deutscher, Franzose, Bayer, d.h. eine Referenz
auf Unterklassen-Objekte übergeben werden !
}
public static void ausgabe1( Person p ) {
IO.writeln( "Hallo" + p.getName( ) ) ;
}
Unterklassen-Objekte / -Referenzen sind
typkompatibel zu Oberklassen-Objekten /
-Referenzen !!
public static void ausgabe2( Bayer b ) {
IO.writeln( "Hallo" + b.getName( ) ) ;
}
}
© H.Neuendorf
(31)
Polymorphie
Verwendung :
Methoden schreiben, die mit Objekten
der Oberklasse arbeiten, zB :
void ausgabe( Person p ) {
IO.writeln( p.getGruss() ) ;
}
+
Methodenaufrufe mit Objektreferenzen
typkompatibler Unterklassen
Beispiel :
Klasse Person enthält nun Methode
getGruss() .
Diese wird in jeder der Unterklassen
spezifisch überschrieben.
© H.Neuendorf
class Person {
public Person( String nn) { name = nn ; }
public String getName( ) { return name ; }
public String getGruss( ) { return "Hallo" ; }
private String name ;
}
class Franzose extends Person {
public Franzose( String nn ) { super( nn ) ; }
public String getGruss( ) { return "Bonjour" ; }
}
class Deutscher extends Person {
public Deutscher( String nn ) { super( nn ) ; }
public String getGruss( ) { return "Mahlzeit" ; }
}
class Bayer extends Deutscher {
public Bayer( String nn ) { super( nn ) ; }
public String getGruss( ) { return "Grüß Gott" ; }
}
(32)
Polymorphie :
Klasse Person enthält Methode
getGruss( ) - in Unterklassen
überschrieben.
class GrussAusgabe2 {
public static void main( String[] args ) {
Dadurch kann polymorph auf Objekten
der Unterklassen Methode getGruss( )
aufgerufen werden :
Franzose f = new Franzose( "Jean" ) ;
Deutscher d = new Deutscher( "Hans" ) ;
Bayer b = new Bayer("Sepp" );
Methode ausgabe() ist für Objekte vom
Typ Person definiert. Da deren
Unterklassen typkompatibel sind, kann
diese Methode auch mit allen
Unterklassenreferenzen aufgerufen
werden !
}
JVM führt dabei Methode getGruss( )
der übergebenen Objekte aus ⇒
public static void ausgabe( Person p ) {
IO.writeln( p.getGruss( ) ) ;
}
Es wird die spezifische getGruss()Methode der übergebenen
Unterklassenobjekte ausgeführt !
© H.Neuendorf
// polymorphe Aufrufe:
ausgabe( f ) ;
ausgabe( d ) ;
ausgabe( b ) ;
}
Voraussetzung : Klasse Person muss auch
eine Methode getGruss( ) besitzen
(33)
Polymorphie
class GrussAusgabe2 {
Typkompatible Unterklassenobjekte
instanziiert
public static void main( String[] args) {
Franzose f = new Franzose( "Jean" ) ;
Deutscher d = new Deutscher( "Hans" ) ;
Bayer b = new Bayer( "Sepp" ) ;
Ausführung erfolgt auf
Unterklassenobjekten :
// polymorphe Aufrufe:
ausgabe( f ) ;
ausgabe( d ) ;
ausgabe( b ) ;
zur Laufzeit
Deren klassenspezifische
Methode getGruss( ) wird
ausgeführt !
Methodenaufruf ist für Objekte
vom Typ Person formuliert
Compilezeit
© H.Neuendorf
}
public static void ausgabe( Person p ) {
IO.writeln( p.getGruss( ) ) ;
}
}
(34)
Was bedeutet Polymorphie :
Man schreibt Methoden mit Oberklassenreferenzen als Parameter - und kann diese mit
Unterklassen-Objektreferenzen aufrufen.
class GrussAusgabe2 {
public static void main( String[] args) {
Resultat :
Franzose f = new Franzose( "Jean" ) ;
Deutscher d = new Deutscher( "Hans" ) ;
Bayer b = new Bayer( "Sepp" ) ;
Je nachdem, welche Art von speziellem
Unterklassenobjekt man beim Aufruf übergibt,
verhält sich ein und dieselbe Methode
anders dh: vielgestaltig = polymorph :
// Drei polymorphe Aufrufe
// derselben Methode ausgabe:
ausgabe( f ) ; // 1. liefert: Bonjour
ausgabe( d ) ; // 2. liefert: Mahlzeit
ausgabe( b ) ; // 3. liefert: Grüß Gott
Hier: Methode ausgabe( Person p )
Für Objekte vom Typ der Klasse Person
geschrieben
}
public static void ausgabe( Person p ) {
IO.writeln( p.getGruss( ) ) ;
}
Aufgerufen mit Objekten vom Typ der
Unterklassen von Person : ⇒
Ergebnis der Aufrufe 1, 2, 3:
Jedesmal ein anderes Verhalten !
Bonjour, Mahlzeit, Grüß Gott
© H.Neuendorf
}
Nutzen Polymorphie :
(35)
Generische Programmierung
Jedes "Programm" , das mit Objekten der Oberklasse arbeitet,
kann auch mit Objekten der Unterklasse arbeiten !
Deshalb auch keine
Einschränkung der
Sichtbarkeit beim
Überschreiben
Konsequenz :
Mit allgemeiner gehaltenen Klassen beginnen - diese zur Abdeckung spezieller Bedürfnisse
via Vererbung verfeinern + anstelle der Oberklassenobjekte verwenden.
Semantisches Prinzip für sinnvollen Einsatz von Polymorphie :
Liskov Substitution Principle :
Einhalten des "Kontrakts" der Oberklassen .....
"Subclasses must be usable through the superclass interface without the need for the
user to know the difference"
( Einhalten der semantischen Integrität ..... )
Unterklassen sollen Semantik = inhaltlichen Sinn bewahren! Unterklasse ist Spezialisierung ihrer
Oberklasse ⇒ soll ihre Oberklasse sinnvoll vertreten können !
Unterklassen sollen sich in überschriebenen Methoden semantisch verhalten wie ursprüngliche
Oberklassen-Methoden. Änderung der Implementierung soll Vertrag nicht verletzen !
Objekte vom Typ Deutscher, Bayer, Franzose verhalten sich prinzipiell wie Objekte vom Typ Person :
⇒ Methode getGruss() liefert auch bei Ihnen einen speziellen Gruß zurück ......
Pacta sunt servanda :
Das durch die Basisklasse zugesicherte Objektverhalten soll auch
in ihren Unterklassen-Spezialisierungen eingehalten werden !
© H.Neuendorf
Statischer + Dynamischer Typ
class Zuweisung {
Objektvariablen können Objekte verschiedenen Typs
referieren
(36)
public static void main( String[] args ) {
Person p1, p2 ;
Statischer Typ :
- Deklarierter Typ der Objektvariablen
p1 = new Franzose( "Jean" ) ;
p2 = new Bayer( "Sepp" ) ;
- bestimmt, welche Methoden + Attribute via Variable
überhaupt ansprechbar sind
Dynamischer Typ :
dynamische späte Bindung
p1.getGruss( ) ;
// ruft Methode getGruss( )
// der Klasse Franzose
- Typ des zur Laufzeit referierten Objekts
- durch Zuweisungen zur LZ jederzeit änderbar
- bestimmt, welche Methoden aufgerufen werden
p2.getGruss( ) ;
// ruft Methode getGruss( )
// der Klasse Bayer
p1 und p2 haben statischen Typ Person ⇒
Über p1, p2 nur Methoden + Attribute ansprechbar, die
in Klasse Person deklariert sind
}
}
p1 hat nach Zuweisung dynamischen Typ Franzose
p2 hat nach Zuweisung dynamischen Typ Bayer
⇒ p1.getGruss( ) → getGruss() von Franzose
p2.getGruss( ) → getGruss() von Bayer
© H.Neuendorf
Dynamische Bindung von Methodenaufrufen:
Aufruf obj.m() führt zum Aufruf der m()-Methode,
die zum dynamischen Typ von obj gehört !
(37)
Statischer Typ - Dynamischer Typ : Methoden
Bayer
Person p = new Bayer( "Sepp" ) ;
Von Person
geerbt oder
überschrieben
In Klasse Bayer hinzugefügte, nicht von
Person geerbte Attribute und Methoden
Statischer Typ :
name
lieblingsbier
getName( )
setLieblingsbier( )
getGruss( )
p
Über Objektvariable p vom statischen Typ
Person nicht zugreifbar .....
Statischer Typ von p ist Person
⇒
Nur schon in Klasse Person deklarierte Attribute und
Methoden sind ansprechbar !
Dynamischer Typ von p ist Bayer ⇒
Die an Klasse Bayer vererbten und dort evtl. überschriebenen Methoden werden über p angesprochen !
© H.Neuendorf
IO.writeln( p.getGruss( ) ) ;
Deklarierter Variablen-Typ bestimmt, welche Methoden +
Attribute ansprechbar sind
Dynamischer Typ :
Typ des Objekts, auf den Variable
zur Laufzeit zeigt - bestimmt,
welche Methoden aufgerufen
werden
Statischer Typ - Dynamischer Typ
Überschriebene Attribute :
* Aber : Wenn Unterklasse geerbte Attribute
überschreibt, entscheidet der deklarierte
statische Typ der Objektvariable, welches
Attribut angesprochen wird ….. ⇒
Entscheidung über Attribut-Zugriff fällt zur
Compile-Zeit mittels Referenz-Typ !!
Methode user() ist geschrieben für Objekte vom
Oberklassentyp Auto.
Verlässt sich in ihrer Implementierung auf ein
Attribut wert vom Typ int.
Ist aufrufbar auch mit Unterklassen-Objekten
vom Typ PKW.
Soll aber dabei nicht invalidiert werden durch in
Unterklasse PKW überschriebenes Attribut vom
inkompatiblen Typ boolean !!
(38)
class Auto {
public int wert = 10 ;
public void m( ) {
IO.writeln( "Auto: " ) ;
}
}
class PKW extends Auto {
public boolean wert = false ; // überschreibt !
public void m ( ) {
IO.writeln( "PKW " ) ;
}
}
class AttributTest {
public static void main( String[] args ) {
PKW p = new PKW( ) ; // Upcast
user( p ) ;
}
public static void user( Auto a ) {
a.m( ) ;
// Typ Objekt entscheidend !
int x = 100 + a.wert ; // Typ Referenz entscheidend !
}
⇓
Nur durch Regel * kann die Unterklasse PKW
dennoch den Kontrakt der Oberklasse Auto
erfüllen !!
© H.Neuendorf
}
(39)
Oberklasse
Zugriff Attribute + Methoden
Nicht-statische
Methode
Statische Methode
Unterklasse
Überschreiben ohne
Einschränkung Sichtbarkeit
super-Zugriff
Kein super-Zugriff
in statischen
Methoden *)
Typ des referenzierten Objekts
(= dynamischer Typ) entscheidet
über Methodenauswahl
Nicht-statisches
Attribut
Statisches Attribut
Überschreiben auch mit
Einschränkung Sichtbarkeit
super-Zugriff
Kein super-Zugriff
in statischen
Methoden *)
Typ der Referenz / Objektvariablen
(=statischer Typ) entscheidet über
Attributzugriff
*) Compiler :
Cannot use super
in a static context !
Weitere Anwendung :
Generischer
Datenbehälter
© H.Neuendorf
Person[] pArr = new Person[3] ;
pArr[0] = new Person( "Hans" ) ;
pArr[1] = new Franzose( "Jean" ) ;
pArr[2] = new Bayer( "Sepp" ) ;
Statischer Typ : Deklarierter Objekttyp der
Variablen p
Statischer + Dynamischer Typ
class Zuweisung {
Dynamischer Typ : Objekttyp, den Variable
zur Laufzeit referiert - zur LZ änderbar !
public static void main( String[] args ) {
⇓
Person p ; // statischer Typ Person
Erst zur LZ steht aktueller dynamischer Typ
der Objektvariable fest.
char c = IO.promptAndReadChar( "f/b?" ) ;
// Festlegung dynamischer Typ gemäß
// User-Eingabe erst zur Laufzeit !!
if( c == ' f ' ) {
p = new Franzose( "Jean" ) ;
}
else {
p = new Bayer( "Sepp" ) ;
}
Auswahl auszuführender Methode erst zur LZ
dynamisch anhand des aktuellen
dynamischen Typs der Objektvariable - und
nicht schon statisch zur Compilezeit statisch
anhand des Typs der Objektvariable
⇓
Auszuführendes Methoden-Coding nicht
schon zur Compilezeit festgelegt sondern
erst zur Laufzeit :
Dynamische, späte Bindung (late
binding) ist technische Voraussetzung
für Polymorphie
© H.Neuendorf
(40)
p.getGruss( ) ;
// welche Methode getGruss( ) gerufen wird
// steht erst zur Laufzeit fest !
}
}
(42)
Aufruf überladener Methoden
Überladen auch bei Methoden mit Objektparametern möglich
class GrussAusgabe {
public static void main( String[] args ) {
Person p = new Person( "Jemand" ) ;
Regeln analog zum Überladen von Methoden
mit primitiven Datentypen
Deutscher d = new Deutscher( "Hans" ) ;
⇓
Bayer b = new Bayer( "Sepp" ) ;
1. Genau passende Methode wird aufgerufen
begruesse( p ) ;
2. Wenn keine Methode genau passt, dann
möglichst spezifische Methodenauswahl
begruesse( d ) ;
begruesse( b ) ;
⇓
}
Aufruf mit Bayer-Objekt führt zum Aufruf der
Methode mit Parameter vom Typ Deutscher,
nicht zum Aufruf der allgemeineren Methode
mit Parameter vom Typ Person
public static void begruesse( Person p ) {
IO.writeln( p.getGruss( ) ) ;
}
public static void begruesse( Deutscher d ) {
Bei Methoden mit mehreren Objektparametern
kann es zu Zweideutigkeiten kommen, die der
Compiler moniert …..
IO.writeln( "Hier: " + d.getGruss( ) ) ;
}
}
© H.Neuendorf
(43)
Abstrakte Klassen
abstract Person
Schlüsselwort
abstract
getName()
abstract getGruss()
Abstrakte Klassen (können) enthalten Methoden,
die als abstrakt deklariert sind
Methoden mit Methodenkopf deklariert , aber
enthalten keine Implementierung
name
Von abstrakten Klassen können keine Objekte
instanziiert werden !
Deutscher
getGruss()
Franzose
Begriff Person ist abstrakt.
Nicht bei
Konstruktoren
getGruss()
Konkreter Gruß nur sinnvoll
speziell für Deutsche, Franzosen,
Bayern, ...... definierbar !
abstract class Person {
Bayer
public Person( String name) {
this.name = name ;
}
public String getName( ) {
return name ;
}
getGruss()
setLieblingsbier()
lieblingsbier
Abstrakte Klasse stellt Methoden zum Überschreiben in
Unterklassen zur Verfügung, deren konkrete Ausformulierung
erst dort Sinn macht weil nur für spezialisierte
Unterklassen semantisch klar ist, wie die Wirkung einer
solchen Methode aussehen sollte !
Implementierung wird quasi "aufgeschoben"
© H.Neuendorf
abstract public String getGruss( ) ;
private String name ;
}
Abstrakte Klassen
abstract Person
getName()
abstract getGruss()
name
Von abstrakten Klassen können keine
Objekte instanziiert werden !
(44)
... da nicht klar ist, wie sich Objekte bei Aufruf der
abstrakten Methode verhalten sollten ....
Können jedoch auch :
a) implementierte Methoden enthalten
b) statische impl. Methoden enthalten – sind auf abstrakter Klasse aufrufbar !
Deutscher
getGruss()
Bayer
getGruss()
setLieblingsbier()
lieblingsbier
Abstrakte Methoden
können nicht static
oder private sein !
© H.Neuendorf
Unterklassen müssen alle
geerbten abstrakten
Methoden überschreiben +
implementieren .....
Franzose
Bsp: IO könnte abstract sein. Abstracte
Klassen könnten nicht-abstrakte statische
Factory-Methoden enthalten
getGruss()
.... sonst sind sie auch
abstrakt (nicht instanziierbar)
und müssen als abstract
gekennzeichnet werden !
Polymorphie funktioniert
auch mit abstrakten
Oberklassen !
abstract class Person {
public Person( String name ) {
this.name = name ;
}
public String getName() {
return name ;
}
Unterklassen können konkrete Methoden
abstrakt überschreiben
In Unterklassen können zusätzliche abstrakte
Methoden auftreten ⇒
abstract public String getGruss( ) ;
Es darf weitere abstrakte Unterklassen in einer
Klassenhierarchie geben
Abstrakte Methoden der Oberklasse können nicht
mit super. aufgerufen werden
private String name ;
}
Abstrakte Klassen und Methoden
abstract class Figur {
abstract public flaeche( ) ;
}
class Kreis extends Figur {
private double radius ;
Kreis( double r ) { radius = r ; }
public double flaeche( ) {
return Math.PI * radius * radius ;
}
}
Typisches Beispiel für abstrakte Oberklasse als
gemeinsamer Typ - innerhalb nicht durchgängig
kompatibler Vererbungshierarchie :
Kreise und Rechtecke haben sonst nichts
Gemeinsames ….
Vorsicht : Wenn man abstrakter Oberklasse neue abstrakte
Methode hinzufügt invalidiert man alle bisherigen Verwender
Hinzufügen konkreter Methode mit Default-Implementierung
jedoch meist problemlos
© H.Neuendorf
(45)
Speziellere Unterklassen können durchaus
auch weniger Attribute als ihre allgemeinere
Oberklasse haben. Dafür hier zusätzliche
Bedingung beim Quadrat : breite = laenge !
class Rechteck extends Figur {
private double laenge ;
private double breite ;
Rechteck( double l, double b ) {
laenge = l ; breite = b ;
}
public double flaeche( ) {
return laenge * breite ;
}
}
class Quadrat extends Rechteck {
Quadrat( double kante ) {
super( kante, kante ) ;
}
// Flächenberechnung funktioniert mit
// geerbter Methode !
}
(46)
Finale Klassen und Methoden
Konstanten :
final class Muenchner extends Bayer {
final int MAX = 5 ;
Wert endgültig, nicht veränderbar !
Finale Methoden :
Nicht bei
Konstruktoren
public Muenchner( String name ) {
super( name ) ;
}
Sind "endgültig" :
Dürfen in Unterklassen nicht überschrieben werden !
final public String getGruss( ) {
return "Servus !!" ;
}
Dürfen nicht abstrakt sein - sonst blieben sie ohne Inhalt !
Finale Klassen :
Sind "endgültig": Ende der Vererbungshierarchie
Von ihnen kann nicht abgeleitet werden - es können keine
Unterklassen aus ihnen gebildet werden
Dürfen keine abstrakten Methoden enthalten !
}
Finale Methoden :
Dürfen in Unterklassen nicht
überschrieben werden !
Vorteile :
Geschwindigkeit - Compiler kann kompakteren Bytecode generieren,
wenn klar ist, dass es keine weiteren polymorphen Unterklassen gibt :
Keine dynamische Bindung bei finalen Methoden.
Sicherheit - Unterlaufen von Regeln durch Überschreiben von
Methoden in Unterklassen wird verhindert.
Vererbung verhindern durch Klassen, die dafür nicht gedacht oder
designed sind !
© H.Neuendorf
↑↓
Abstrakte Methoden :
Sollen in Unterklassen
überschrieben werden !
Wenn finale Methoden intern nicht-private Attribute oder
nicht-finale, nicht-private Methoden verwenden, so kann
sich ihr Verhalten doch indirekt in überschreibenden
Unterklassen ändern
→ Template-Pattern
(47)
Klasse Object - Wurzel der Java-Klassenhierarchie
Object
Jede Java-Klassenhierarchie hat
immer die Wurzel Object !
Methoden von Object :
clone(), equals(), wait(), toString() ....
..... interessieren uns hier noch nicht ....
abstract Person
getName()
abstract getGruss()
Jede Klasse in Java erbt automatisch von
Klasse Object = "Mutter aller Java-Klassen"
Wichtig für generisches Programmieren
mittels Upcast : (Datenabstraktion)
Klasse Object als gemeinsamer Ober-Typ für
Objekte aller Klassen !
name
Deutscher
getGruss()
Bayer
getGruss()
setLieblingsbier()
lieblingsbier
© H.Neuendorf
Franzose
getGruss()
class GenericArray { // generischer Datenbehälter - gut ??
public static void main( String[] args ) {
Object[] container = new Object[3] ;
container[0] = new Franzose( "Jean" ) ;
container[1] = new StringBuffer( "Ein Wort" ) ;
container[2] = new Integer( 156 ) ;
}
}
(48)
Design : Beziehungen zwischen Objekten
Zwei grundsätzlich verschiedene
semantische Beziehungen
1. Ist_Ein – Beziehung :
Ein Franzose ist eine Person
⇒ Darstellung durch Vererbung
Spezialisierung, Erweiterung
class Person {
public Person( String nn ) {
name = nn ;
}
public String getName( ) {
return name ;
}
private String name ;
}
Alles, was sich wesentlich in der
Oberklasse findet, ist auch in der
Unterklasse vorhanden.
Vererbung stellt diese Verhältnisse
class Franzose extends Person {
public Franzose( String name) {
super( name ) ;
}
public String getName( ) {
String n = super.getName( ) ;
n = "Name ist: " + n ;
return n ;
}
// ...........................................
semantisch korrekt dar!
Unterklassen-Objekt kann OberklassenObjekt voll und ganz vertreten!
2. Hat_Ein – Beziehung ........
© H.Neuendorf
}
(49)
Beziehungen zwischen Objekten
2. Hat_Ein-Beziehung :
Eine Linie hat Anfangs- und Endpunkt
⇒ Darstellung als Assoziation
class Point {
public Point( double xk, double yk ) {
x = xk ;
y = yk ;
}
(nicht durch Vererbung!)
private double x ;
private double y ;
Zusammenwirken gleichberechtigter Objekte
Häufig auch: Ganzes-Teil-Beziehung
}
⇒ Darstellung durch Vererbung wäre
semantisch falsch !
Eine Linie ist keine spezielle Punkt-Version !
Keine Spezialisierung, sondern Verwendung
class Linie {
public Linie( Point pA, Point pB ) {
pAnfang = pA ;
pEnde = pB ;
}
einer Objektreferenz als Attribut in einer
private Point pAnfang ;
private Point pEnde ;
..............................................
anderen Klasse
}
Anm: Die Welt der Vererbung + Polymorphie ist doch noch etwas komplizierter als hier dargestellt →
siehe Invarianz, Kovarianz, Kontravarianz in Vererbungshierarchien als Stichworte zur Vertiefung …..
© H.Neuendorf
(50)
Warum keine Mehrfachvererbung ?
Java :
Nur Einfachvererbung
= Jede Unterklasse hat nur eine direkte Oberklasse
Realität : Häufig Mehrfachvererbung = Unterklassen können von mehreren Oberklassen erben (C++)
Kind = Vater + Mutter
Klavier = Musikinstrument + Möbel , .....
Grund: a) Mehrfachvererbung in Praxis eher selten verwendet, macht Programme unübersichtlicher
b) Erhöhter Speicherbedarf + Verwaltungsaufwand für Halten der VMT ( virtual method table )
c) Rautenproblem :
Mehrfachvererbung bedeutet für Klasse Unter_2 :
Basis
Erbt meth1() und wert1 gleich zweimal :
Auf Weg über Klasse Unter_1a und auf Weg über
Klasse Unter_1b
meth1()
wert1
Welcher Weg soll der gültige sein ?
Unter_1a
Unter_1b
................
...............
Methode meth1() könnte in Unter_1a und Unter_1b
überschrieben werden !
Welche Methodenvariante soll dann beim Aufruf aus
Unter_2 heraus gerufen werden ..... ???
⇓
Lösung : Keine Mehrfachvererbung
Unter_2
...............
© H.Neuendorf
Ersatz :
Interface-Konzept
(51)
Interface : Abstrakte Methoden + Konstanten
Schnittstellenbeschreibung :
Legt Schnittstelle (Typ) fest, nicht Implementierung
Anforderungsbeschreibung von Eigenschaften /
Fähigkeiten / Funktionalitäten
Alle Methoden automatisch public abstract
Alle Attribute automatisch
⇒
public static final
Schlüsselwörter public, abstract und static final müssen
nicht extra angegeben werden
Konstanten - müssen initialisiert werden !
Interfaces können mehrere Interfaces erben
⇒
Mehrere Schnittstellenbeschreibungen können in einem
Interface zusammengefasst werden
Klassen können mehrere Interfaces implementieren :
Schlüsselwort
interface Bonus {
public static final double MIN = 1000.0 ;
public abstract void addBonus(double betrag) ;
}
class Konto implements Bonus {
public Konto( ) { .......... }
public double getSaldo( ) {
return saldo ;
}
public void addBonus( double betrag ) {
if ( saldo >= MIN ) {
saldo = saldo + Betrag ;
}
}
implements
Implementierende Klasse ist typkonform zu Interface !
Klasse, die nicht alle Interfacemethoden implementiert,
enthält abstrakte Methoden und ist abstract !
Implementierte Methoden müssen public sein !
private double saldo;
}
Keine Einsparung von "Tiparbeit"
Bessere Strukturierung von Software !
© H.Neuendorf
(52)
Interfaces : Strukturierung + Modellierung
Fahrzeugantrieb
Nachbildung Mehrfachvererbung mittels
Interfacehierarchie : Schlüsselwort extends
Interfaces erlauben Modellierung von komplexen
Zusammenhängen, die durch Einfachvererbung nicht
abgebildet werden können :
→ Anforderungs-Spezifikation
Durch Interface-Hierarchie wird Zusammenhang
abgebildet + charakteristisches Verhalten festgelegt
Keine Implementierung von Methoden, aber
Modellierung + Strukturierung des Sachverhalts
Klassen, die Interfaces implementieren, übernehmen
dadurch Strukturierung des Sachverhalts und die
Anforderungsbeschreibung
Müssen Methoden nur noch ausimplementieren :
Spezifikation + Modellierung ⇒ Interfaces
Abstrakte Vertragsdefinition
Implementierung
© H.Neuendorf
⇒ Klassen
Diesel
Elektro
Hybrid
interface Fahrzeugantrieb {
int getLeistung( ) ; int getGewicht( ) ;
}
interface Diesel extends Fahrzeugantrieb {
float getHubraum( ) ; float getVerbrauch( ) ;
}
interface Elektro extends Fahrzeugantrieb {
float getBatteriekapazität( ) ;
}
interface Hybrid extends Diesel , Elektro {
String getKopplungsart( ) ;
}
class HybridAntrieb implements Hybrid {
public int getLeistung( ) {............}
public float getBatteriekapazität( ) {.......}
// ..............................
}
(53)
Interfaces
Interfaces können nicht instanziiert werden, sondern müssen
durch implementierende Klasse konkretisiert werden
interface W {....... }
interface X extends W {.....}
interface Y extends W {.....}
Unterschied Interface - abstrakte Klasse :
1. Abstrakte Klassen können Implementierung enthalten
2. Klassen können nur von einer abstrakten Oberklasse
erben (Einfachvererbung) aber beliebig viele Interfaces
implementieren. IFs erlauben Mehrfachvererbung
class Z implements X , Y { /*…*/
}
W
X
⇓
Rautenproblem bei Methoden – Regeln :
1.
Gleicher Methodenname, gleicher oder verschiedener Rückgabetyp,
unterschiedliche Parameterlisten :
⇒ Klasse Z muss entsprechend viele gleichnamige überladene
Methoden implementieren
2.
Gleicher Methodenname, gleicher Rückgabetyp, gleiche
Parameterlisten :
⇒ Klasse Z muss nur diese eine Methode implementieren
3.
Gleicher Methodenname, gleiche Parameterlisten unterschiedlicher
Rückgabetyp : ⇒ Nicht erlaubt - Compilerfehler !
Identische Konstanten-Namen in Interfaces :
Konflikt durch Angabe des Interface-Namens aufgelöst
© H.Neuendorf
Y
Z
interface Skat { int kartenZahl = 32 ; }
interface Poker { int kartenZahl = 52 ; }
class Z implements Skat, Poker {
/* …*/ Skat.kartenZahl /*…*/
/*… */ Poker.kartenZahl /*…*/
}
// IF-Konstanten sind auch in nicht
// implementierenden Klassen verwendbar :
class A {
/* …*/ Skat.kartenZahl /*…*/
/*… */ Poker.kartenZahl /*…*/
}
Programmieren mit Interfaces
Interface = Strukturierter Typ
Auf diesen kann man sich (u.a.) bei
Methodenimplementierung beziehen :
Parameter, Rückgabewert, Variablen von
Methoden können von Interface-Typ sein !
Interface-Referenz wird Objekt einer
implementierenden Klasse zugewiesen *)
Darauf nur die im IF definierten Methoden +
Konstanten zugreifbar
Erhöhte Typsicherheit & Flexibilität bei
Verwendung, leichtere Austauschbarkeit der
Implementierung !
⇓
Programmierung via Interfaces - konkrete
Aufrufe aber stets mit Objekten der
implementierenden Klassen
Vorsicht :
Nachträgliche Änderungen an Interfaces
erzwingen Quellcodeanpassungen in
implementierenden Klassen !!
© H.Neuendorf
(54)
interface IFEuroSF {
public static final double kurs = 1.14 ;
public abstract double inSF( double euroBetrag ) ;
}
//////////////////////////////////////////////////////////////////////////
class CIFUser {
public static void rechne( IFEuroSF iE ) {
double e = IO.promptAndReadDouble( "Euro : " ) ;
double s = iE.inSF( e ) ;
IO.writeln( "In SF sind das : " + s ) ;
}
Kennt Implementierer nicht !
}
///////////////////////////////////////////////////////////////////////////
class CEurorechner implements IFEuroSF {
public double inSF( double euroBetrag ) {
double sf = euroBetrag * IFEuroSF.kurs ;
return sf ;
}
Kennt User nicht !
}
/////////////////////////////////////////////////////////////////////////////
class IFTest {
public static void main( String[] args ) {
IFEuroSF eR = new CEurorechner( ) ;
// *)
CIFUser u = new CIFUser( ) ;
u.rechne( eR ) ;
}
}
(55)
Programmieren mit Interfaces
Interfaces legen keine Implementierungsdetails fest :
⇒ Ihre Methoden können nicht mit native, synchronized oder strictfp modifiziert werden
Würde Implementierung vorschreiben – dies ist allein Sache der impl. Klasse
Interfaces dürfen keine Konstruktoren vorschreiben
Methoden können nicht final sein
(finale Methodeparameter möglich, aber Klasse nicht daran gebunden … )
⇒ Denn sie müssen erst noch in einer Klasse implementiert werden
Methoden können nicht static sein
⇒ Denn statische Methoden können nicht abstract sein
Methoden dürfen jedoch mit throws-Klausel Exception-Verhalten vorgeben
Interface-Konstanten können Objekte sein !
Extreme Form :
class A {
public void tuWas( ) {/* … */ } // …..
}
interface IFEuro {
public static final double kurs = 1.95583;
public static final A a = new A( ) ;
public abstract double inDM( double euros ) throws MyException ;
}
Marker-Interfaces ohne jeden Inhalt
© H.Neuendorf
Vertrag nur durch Dokumentation festgelegt
Es wird auf Code-Ebene kein Verhalten
definiert, dh keine Methoden + Konstanten.
Gesamter Vertrag befindet sich in der
Dokumentation, die die Erwartungen
beschreibt, die eine implememtierende
Klasse erfüllen muss
Bsp : Serializable, Clonable, …
Interfaces – Möglichkeiten
class A { public void tuWas( ) { /* ... */ } }
(56)
interface IFDummy { public void tuWas() ; }
class B implements IFDummy {
class C1 implements IFTest, IFTest.IFInner {
public void tuWas( ) { /* ... */ }
public void m1( A aObj ){
}
aObj.tuWas( ) ;
interface IFTest {
aFin.tuWas( ) ; // Zugriff auf Konstante
// Parameter Typ andere Klasse :
}
public void m1( A aObj ) ;
public void m2( IFDummy d ) {
// Parameter Typ Interface :
d.tuWas( ) ; // Aufruf Interface-Methode
public void m2( IFDummy d ) ;
}
// Rückgabe Typ Interface :
public IFDummy m3( ) {
public IFDummy m3( ) ;
return new B( ) ; // Rückgabe typkomp.Objekt
// Konstanten Typ Klasse oder Interface :
}
public static final A aFin = new A( ) ;
public void m4( int b ) {/* ... */ }
public static final IFDummy ifd = new B( ) ;
// Konstanten erst zur LZ festgelegt :
public static final int d = IO.promptAndReadInt("?") ;
// Inneres Interface :
public interface IFInner {
public void m4( int b ) ;
}
}
© H.Neuendorf
}
Nicht alles technisch mögliche ist auch sinnvoll :
Interfaces sollen keine Implementierungsdetails
vorschreiben. Nicht das Wie? sondern das Was? zu
beschreiben ist Aufgabe von Interfaces ⇒
Interfaces sollten nicht von Implementierungen
(existierenden Klassen) abhängen !
Zusammenspiel : Interface – Abstrakte Klasse – Implementierende Klasse
interface IF {
m1( )
m3( )
(58)
Formuliert Vertrag + Typ =
Schnittstelle
Framework :
m2( )
→
m4( )
Verlangt nach Typ IF
m5( ) …
}
abstract class AbstractIF implements IF {
abstract public void m1( ) ;
Abstrakte skeletal-implementation-class
abstract public void m2( ) ;
als Implementierungshilfe :
public void m3( ) { }
Implementiert Vertrag bereits teilweise –
public void m4( ) { }
eventuell mit leerer Implementierung
public void m5( ) { }
Grundlegende Methoden bleiben abstrakt,
…......
verwendende Methoden vorimplementiert
}
Instanzen entsprechen
dem vom Framework
geforderten Typ !
Bsp :
java.util Collection Framework
class MyVersionOfIF extends AbstractIF {
public void m1( ) { }
public void m2( ) { }
User : Muss nur noch Teil der Methoden
implementieren bzw. zur individuellen
public void m4( ) { }
Anpassung / Effizienzerhöhung etc.
……………………
überschreiben
}
Weniger mühsam, als direkt mit IF zu beginnen
© H.Neuendorf
IF
Collection
AK
AbstractCollection ,
AbstractList , …….
(59)
Interfaces nicht als Konstantendeponie missbrauchen !
interface PhysicalConstants {
public static final double NA = 6.022e23 ;
package science ;
// Bessere Lösung !
public class PhysicalConstants { // Utility-Klasse
public static final double KB = 1.38e-23 ;
private PhysicalConstants( ) { }
// no objects!
public static final double ME = 9.11e-31 ;
public static final double NA = 6.022e23 ;
public static final double KB = 1.38e-23 ;
}
public static final double ME = 9.11e-31 ;
Interne Verwendung von Konstanten sollte ein
Implementierungsdetail der verwendenden Klasse sein.
Bei Implementierung eines entsprechenden "KonstantenInterfaces" werden die Konstanten jedoch Teil der
öffentlichen Schnittstelle der implementierenden Klasse
("implementation details leak into the class's exported API").
Verwirrt Benutzer in der Regel, da er keinen Sinn in Werten
sieht, die nur innerhalb von Methoden-Implementierungen
verwendet werden.
Der Namensraum der Klasse und all ihrer Unterklassen wird
mit den IF-Konstantennamen verschmutzt.
Zudem Bürde : Auch wenn in späteren Versionen der
Klasse diese Konstanten intern nicht mehr benötigt werden,
muss die Klasse doch weiterhin das IF implementieren, um
typkompatibel zu Vorgängern bleiben.
}
import science.PhysicalConstants ;
Nichtinstanziierbare
Utility-Klasse ist viel
bessere Lösung !
class Test {
public double atoms( double mol ) {
return PhysicalConstants.NA * mol ;
}
// ………
}
Interfaces sollten nur verwendet werden,
um Typen + Schnittstellen zu definieren !
Sie sollten nicht dem bloßen Export von
Konstanten dienen !
© H.Neuendorf
(60)
Vorteile und Probleme von Interfaces
lt is, generally speaking, impossible to add a method to a public interface without breaking all existing
classes that implement the interface. Classes that previously implemented the interface will be missing the
new method and won't compile anymore.
You could limit the damage somewhat by adding the new method to the skeletal implementation at the same
time as you add it to the interface, but this really wouldn't solve the problem. Any implementation that didn't
inherit from the skeletal implementation would still be broken.
Public interfaces, therefore, must be designed carefully. Once an interface is released and widely
implemented, it is almost impossible to change. You really must get it right the first time. If an interface
contains a minor flaw, it will irritate you and its users forever. If an interface is severely deficient, it can doom
an API.
The best thing to do when releasing a new interface is to have as many programmers as possible implement
the interface in as many ways as possible before the interface is frozen. This will allow you to discover flaws
while you can still correct them.
To summarize, an interface is generally the best way to define a type that permits multiple implementations.
An exception to this rule is the case where ease of evolution is deemed more important than flexibility and
power. Under these circumstances, you should use an abstract cass to define the type, but only if you
understand and can accept the limitations.
If you export a nontrivial interface, you should strongly consider providing a skeletal implementation to go
with it. Finally, you should design all of your public interfaces with the utmost care and test them thoroughly
by writing multiple implementations.
Joshua Bloch, Effective Java
© H.Neuendorf
S.97
Vorteile von Interfaces – Denken + Entwickeln via Schnittstellen
1. Interfaces fördern den modularen Aufbau von Systemen
→ Prinzip Separation of Concerns wird unterstützt – Reduktion der Abhängigkeiten
→ Wartbarkeit wird gefördert durch klar definierte funktionale Einheiten
2. Interfaces fördern das Information Hiding schon beim Entwurf von Systemen
→ Kein "Drauflos-Implementieren" sondern thematische Strukturierung
→ "Rauschen der Implementierung" kommt erst später
→ Konzentration auf die Sematik einer Schnittstelle
→ Konzentration auf saubere + sinnvolle Signaturen und Sinn von Schnittstellen
Prinzip der Kohäsion: Vollständigkeit und funktionaler Zusammenhang
3. Interfaces verbessern die spätere Implementierung
→ Implementierung hat klare Basis und eindeutige semantische Zielrichtung
→ Lose Kopplung zwischen Schnittstelle und Implementierung
→ Leichtere Austauschbarkeit der Implementierung
(Testen, Warten, Upgrade)
→ Open-Closed-Prinzip: Software soll offen sein für Erweiterungen aber
abgeschlossen für Änderungen (Konstanz der Schnittstelle)
© H.Neuendorf
(61)
Grundsätze Objektorientierten Designs
Grundeinheit des OO-Programmierens ist die Klasse
Aber Grundeinheit des OO-Entwurfs ist der Typ !
Abstrahiere von der konkreten Implementierung – denn diese ist austauschbar
- Programmiere gegen Interfaces – nicht gegen konkrete Klassen
- Abhängigkeiten von Abstraktionen herstellen – nicht von Konkretisierungen
- Hohe Abstraktion statt vorschnelle Konkretisierung – alles Konkrete veraltet schnell
- Implementierungen müssen austauschbar sein + Erweiterungen leicht möglich sein
Denke in Verträgen + Service-Anforderungen – nicht in Details von Algorithmen
- Entkopple Konsumenten und Produzenten durch Interfaces (Service-Verträge)
- Garantiert werden keine Implementierungsdetails , sondern …
… die Einhaltung von Service-Verträgen, die in Interfaces festgeschrieben sind
Die Seele des Ganzen ist die sinnvolle Typisierung – nicht die konkrete Implementierung
Erfinde das Rad nicht zweimal – orientiere dich an Design-Patterns
Modellieren ist wichtiger als Implementieren → Model Driven Architecture (MDA)
© H.Neuendorf
(62)
(63)
Anwendung : Template - Pattern
abstract class KoffeinhaltigesGetraenk {
public final void zubereitungsRezept() {
interface
final
class abstract
extends ....
Was geht damit ??
kocheWasser();
aufschütten();
inTasseSchütten();
if ( mitZutaten() ) {
zutatenHinzu();
Zubereitung ist final ⇒
Am Rezept (Algorithmus / Reihenfolge der Schritte)
können Unterklassen nichts ändern
}
}
Der Algorithmus enthält abstrakte Methoden ⇒
public abstract void aufschütten();
Durch Überschreiben können Unterklassen doch
indirekt eine Anpassung vornehmen
public abstract void zutatenHinzu();
public void kocheWasser() {
IO.writeln( "Koche Wasser" );
}
Diese Methode ist ein " Hook " :=
public void inTasseSchütten() {
Eine Methode mit Default-Implementierung oder sogar
ohne Implementierung
⇒
IO.writeln( "Schütte in Tasse" );
}
public boolean mitZutaten() { return true; }
}
© H.Neuendorf
Die Unterklassen können sich durch Überschreiben der
Methode hier "einhaken" – müssen es aber nicht ...
(64)
Anwendung : Template - Pattern
public class Kaffee extends KoffeinhaltigesGetraenk {
public void aufschütten() { IO.writeln( "Kaffee durch Filter" ); }
public void zutatenHinzufügen() { IO.writeln( "Zucker + Milch" ); }
public boolean mitZutaten() {
char z = IO.promptAndReadChar( "Zutaten?" );
if ( z == 'j' ) { return true; }
else { return false; }
}
Die Unterklassen passen
grundlegendes Algorithmus (das
Template ) durch Überschreiben
an ihre Bedürfnisse an !
Viele weitere Getränke können
implementiert werden – ohne an
abstrakter Basisklasse und am
Algorithmus selbst Änderungen
vornehmen zu müssen
⇓
}
public class Tee extends KoffeinhaltigesGetraenk {
public void aufschütten() { IO.writeln( "Tee in Kanne" ); }
Gemeinsame + unveränderliche
Anteile in Basisklasse erfasst
Template für Unterklassen
public void zutatenHinzufügen() { IO.writeln( "Zitrone" ); }
public boolean mitZutaten() {
Design-Patterns !
char z = IO.promptAndReadChar( "Zutaten?" );
if ( z == 'j' ) { return true; }
else { return false; }
}
}
© H.Neuendorf
Joshua Bloch, Effective Java
S.234
Don't sacrifice sound architectural principles for
performance. Strive to write good programs rather than
fast ones. If a good program is not fast enough, its
architecture will allow it to be optimized.
Good programs embody the principle of information
hiding: where possible, they localize design decisions
within individual modules, so individual decisions can be
changed without affecting the remainder of the system.
(65)
@Annotations : Meta-Informationen zu Klassen und Methoden
Annotations do not directly affect program semantics, but they do affect the way programs are treated by tools and
libraries […]. Annotations can be read from source files, class files, or reflectively at run time. Typical application
programmers will never have to define an annotation type, but it is not hard to do so. […] As of release 5.0, the
platform has a general purpose annotation facility that permits you to define and use your own annotation types.
Marker-Auswertung durch Tools zur Erstellung von Ressourcen
Teilweise durch Compiler gecheckt
→
@Override
Javadoc Junit ….
@SuppressWarnings("unused")
Einkompiliert in Bytecode, zur LZ-Auswertung via Reflection-API
Liefert Frameworks und Containern Informationen
→
→
→
Bei EJBs
@Deprecated
java.lang.annotation
@Stateless
@Remote
……
Im Coding am gleichen Ort wie andere Modifizierer zulässig
Parametrisierbar
Können Meta-Annotationen besitzen
@interface Test{ }
Annotationen werden als spezielle Art von Interface definiert.
Diese können Elemente enthalten, die bei Verwendung mit
passenden Werten zu füllen sind. Elemente unterliegen
Einschränkungen:
@interface Copyright {
Nur primitive Typen, Strings, Arrays, …
String author( ) ;
int year() ;
Elemente haben keine Parameter und keine Exceptions
Default-Werte können hinterlegt werden …..
}
Anwendbarkeit durch Meta-Annotationen einschränkbar
@Copyright( author="SAP AG" , year = 2009 )
→ Gosling et al. The Java Programming Language, Ch15
public class SomethingNew {
@Test void tuWas( ) { /* nur ein Test */ }
}
© H.Neuendorf
Paket
java.lang.annotation
Enthält Meta-Annotationen, um LZReflection korrekt einzustellen
(67)
Pakete (Packages)
Ordnung in großen Projekten :
Strukturierung durch Klassen + Methoden
Jedes Paket sollte
genau definierte
Zuständigkeiten +
Aufgaben besitzen !
Allerdings zu feinkörnig ⇒ Gröberer Strukturierungsmechanismus :
Paket
: Zusammenfassung zu größerer Einheit
= Sammlung zusammengehöriger Klassen und Interfaces
JDK-Bibliothek besteht aus zahlreichen Paketen :
u.a.
java.lang : Standardpaket mit absolut erforderlichen Klassen ( String, StringBuffer, ...)
wird in jedes Programm automatisch importiert
java.io :
Klassen zum Lesen + Schreiben von Datenströmen ( Dateien, Tastatur, Screen )
java.awt:
Klassen für graphische Benutzeroberflächen ( Fenster, Buttons, Menues, ...)
java.util:
Diverse Datencontainer, Zufallszahlengenerator, ....
Paket
Arbeiten mit Paketen:
1. Anlegen eigener Pakete
Schlüsselwort :
package
2. Einbinden von Paketen
Schlüsselwort :
© H.Neuendorf
import
Klasse1
Klasse2
Methoden
Attribute
Methoden
Attribute
(68)
Anlegen von Paketen
Klassen können bei Deklaration eindeutig einem Paket zugeordnet werden :
Am Anfang der Quelldatei (1.Zeile der Datei ! ) : package packageName einfügen
⇒ Alle Klassen in dieser Datei gehören dann zu diesem Paket
Paket = logische Einheit - Klassen können physisch über verschiedene Dateien verteilt sein
package graphics;
package graphics;
class Circle {
class Rectangle {
.........
.........
}
}
Datei Rectangle.java
Datei Circle.java
Paket graphics
Circle
Rectangle
Relativ eindeutige Paketnamen mittels umgekehrter Domain :
package de.dhbw-mosbach.graphics ;
© H.Neuendorf
Wenn keine Angabe einer packageZugehörigkeit erfolgt, gehören alle
Klassen der Datei zum namenlosem
Standardpaket. Werden dann im
aktuellen Arbeitsverzeichnis
gesucht.
Jede Klasse gehört zu einem Paket,
zumindest Standardpaket
Paket = Sichtbarkeitsgrenze
(69)
Paketkonzept leistet :
Alles, was zum Paket gehört, ist außerhalb Paket
Deklaration öffentlicher Schnittstellen
defaultmäßig unsichtbar, nicht zugreifbar !
Durchsetzung Geheimnisprinzip
Dokumentation von Verwendungen im Code
Einschränkung von Verwendungen
Veröffentlichung nur durch expliziten Export !
⇒ Klassen haben nur Zugriff auf andere Klassen (Attribute + Methoden) des gleichen Pakets !
Code des Pakets kann kooperieren, ohne dass externes Coding darauf Zugriff hätte !
⇒ Klassen eines Pakete haben keinen Zugriff auf Klassen eines anderen Pakets !
Paket = Namensraum :
In verschiedenen Paketen gleiche Namen verwendbar
Nur innerhalb Paket müssen Klassennamen eindeutig sein
package yourPack ;
package myPack ;
class C1 { int x;
void m(){...} }
class C2 { int y;
void m() {...} }
class C1 { int x;
class C22 {
int y;
// Klassen C1 und C2 sind lokal zu Paket myPack
© H.Neuendorf
void m() {...}
C2 c = new C2( ) ; // Fehler !!
// können sich gegenseitig benutzen
// aber nicht von Außen (= aus anderen Paketen) ansprechbar !
void m(){...} }
// Klasse C2 hier unbekannt !!
}
Export von Paket-Inhalten :
(70)
Kennzeichnung als public
Zusammenarbeit zwischen Paketen → Zugriff auf Klassen, Methoden, Attribute, IFs anderer Pakete
Gewährung Zugriff → Durch Export von Paketbestandteilen
Export → Deklaration von Klassen, Methoden, Konstruktoren, Attributen als public :
"Was public sein soll / exportiert wird, bestimmt das Paket selbst ! "
Voraussetzung für Sichtbarkeit von public-Methoden + public-Attributen :
→ Zugehörige Klasse selbst auch public deklariert !
Nur Interface-Attribute und Methoden
sind defaultmäßig public, nicht aber
Interface selbst
Wenn Klasse nur nicht-public
Konstruktoren hat, dann kann
sie aus anderen Paketen nicht
instanziiert werden
Konstruktoren → nur public Konstruktoren aus Fremdpaketen aufrufbar !
package myPack ;
// Auch ein Paket hat eine Schnittstelle !!
public class C1 {
// C1 wird exportiert
public int x ;
// Attribut x exportiert
int y ;
public void m1( ){...} // Methode m1( ) exportiert
void m2( ) {...}
public C1( int a ) { ... } // dieser Konstruktor wird exportiert
C1( ) {...}
}
class C2 { ...... }
// C2 von Außen nicht sichtbar !
© H.Neuendorf
Aus Fremd-Paketen :
public-Elemente von C1
zugreifbar
Nicht-public-Elemente und ganze
Klasse C2 jedoch verborgen !
Erlaubt:
C1 obj = new C1( 4 ) ;
Verboten: C1 obj = new C1() ;
Implementierung von nichtpublic-Elementen des Pakets
grundsätzlich änderbar !
Verwendung von Paket-Inhalten :
(71)
Import in andere Pakete
Voraussetzung für Verwendung exportierter Klassen + ihrer Komponenten in anderen Paketen :
→ Ausdrücklicher Import der fremden Klassen in Paketen
" Was von Außen importiert wird, bestimmt das verwendende Paket selbst ! "
Sinn : Klare Festlegung, welche fremden Klassen in eigenem Paket / Programm verwendet werden !
→ Verständlichkeit / Lesbarkeit / Durchschaubarkeit der Programme
Mittels : a) Volle Paket-Qualifikation des Klassennamens ( Impliziter Import )
b) Import-Anweisung in Quellcode-Datei ( Expliziter Import )
package myPack ;
package newPack ;
public class C1 { ....}
class C2 { ...... }
package ourPack ;
public class C1 { ....}
class C2 { ...... }
© H.Neuendorf
// Impliziter Import :
class C10 {
myPack.C1 obj_1 = new myPack.C1( ) ;
ourPack.C1 obj_2 = new ourPack.C1( ) ;
..........................
}
Gleichnamige Klassen aus
verschiedenen Paketen
durch Qualifikation nebeneinander verwendbar
Eigene Klassen dürfen so
heißen, wie implzit
importierte Klassen
Expliziter Import
Nachteil : In Programmcode weniger deutlich,
woher die Klassen wirklich stammen !
Import-Zeile in Quellcode-Datei
(72)
Varianten :
a) Import einer einzelnen Klasse :
import paketname.klassenname ;
b) Import aller Klassen eines Pakets :
import paketname.* ;
c) Statischer Import = statischer Elemente einer bestimmten Paketklasse : import static java.lang.Math.* ;
Regeln :
Import-Anweisungen in Quellcode-Datei müssen direkt nach package-Anweisung stehen !
Import-Anweisungen gelten nur für jeweilige importierende Quell-Datei !
Alle in Import-Anweisungen angeführten Klassen müssen verschiedenen Namen haben !
Eigene Klassen dürfen nicht so heißen, wie explzit importierte Klassen !
→ Namenskonflikt ⇒ Übergang zum impliziten Import mit Paket-Qualifikation
Verwendung :
In Coding Klassennamen somit ohne Paket-Qualifikation verwendbar
package myPack ;
package newPack ;
import myPack.C1 ; // expliziter Import:
In Quellcodedateien quasi
schon enthalten :
import java.lang.* ;
public class C1 { …… }
class C2 { ...... }
© H.Neuendorf
class C10 {
C1 obj_1 = new C1( ) ;
..........................
}
Dadurch sind nötige
"Spracherweiterungen"
(String, Object, ....)
verfügbar !
(73)
Pakete und Verzeichnisse - Regeln
Abbildung :
Klassen → Dateien
Pakete → Verzeichnisse
+
1. Eine public Klasse C muss in Datei namens C.java implementiert werden !
2. Alle Klassendateien eines Pakets P müssen in einem Verzeichnis namens P liegen !
3. Eine Datei darf beliebig viele Klassen enthalten, aber nur eine davon darf public sein !
Name der public-Klasse bestimmt Dateinamen .java
Andere Datei-Klassen werden von public-Klasse intern verwendet und nicht exponiert
Innere Klassen können public sein und separat importiert werden
Bsp: Paket P mit public Klassen A, B und C
set CLASSPATH=
⇒
Verzeichnis P mit Dateien A.java B.java C.java
P
P
→ .jar .zip .class …..
A
B
C
A.java
Pakethierachie → Pakete aus Paketen = Schachtelung
Bsp: Paket P1 + Paket P2 → Paket P3
Verzeichnis P3 enthält Unterverzeichnisse P1 + P2
In Unterverzeichnissen liegen einzelne .java-Dateien
B.java
C.java
Benutzung → Angabe gesamter Paketpfad bis zur Klasse :
import P3.P2.C1;
import P3.P2.*
// Import einer bzw aller Klassen aus P2
import P3.*
// Import aller direkter Klassen aus P3
// nicht aber der Klassen aus P2, P1 !!
© H.Neuendorf
Keine automatische Sichtbarkeit
zwischen inneren und äußeren
Paketen !
(74)
Pakete und Verzeichnisse - Regeln bei inneren Klassen
Eine public top-level Klasse kann auch eine oder mehrere public innere Klassen haben.
Diese können wahlweise importiert werden.
Nicht statische innere Klassen jedoch nur, wenn auch äußere Klasse importiert wird.
Erzeugung von inneren Instanzen folgt jedoch besonderer Syntax
package myPack ;
public class Outer {
public int a;
package newPack ;
import myPack.Outer ;
import myPack.Outer.Inner1 ;
import myPack.Outer.Inner2 ;
public class Inner1 {
// ……..
}
public static class Inner2 {
class C10 {
Outer obj = new Outer( ) ;
Inner1 drin1 = obj.new Inner1( ) ;
// ……..
}
}
class C2 { /* … */ }
Inner2 drin2 = new Inner2( );
// ……………..
}
Abfrage von Pakteeigenschaften im Coding mittels java.lang.Package :
Package p = Package.getPackage( "Paketname" );
© H.Neuendorf
p.getName() ; // …..
(75)
Pakete und Verzeichnisse in Eclipse
Jede Klasse ist einem Package zugeordnet
Default ist das default package = namenloses Standardpaket
Neues Package anlegen :
Eclipse legt pro Package
Ordner mit allen Klassen an :
Einbinden von IO in Eclipse-Projekte ohne
physische Kopie :
1. IO.class als ZIP- oder JAR-File
Zentral ablegen – zB Workspace-Ordner
2. Menü Project → Properties → Java Buid Path
3. Tabreiter Libraries wählen
4. Button Add External JARs …
5. Auf ZIP- / JAR-File mit IO.class verweisen
6. OK
Referenz kann natürlich nachträglich wieder entfernt
oder geändert werden ….
© H.Neuendorf
Zugriffsrechte auf Klassen in Java :
public private protected
Zugriffsrechte :
public int x ;
public :
public void m( ) {...}
In allen Klassen des eigenen Pakets sichtbar + In allen anderen Paketen, die die Klasse importieren
protected int x ;
protected void m( ) {...}
protected :
In Unterklassen der Klasse desselben Pakets + in Unterklassen der Klasse in anderen Paketen +
in Fremdklassen desselben Pakets
sichtbar
( Anders als in C++ definiert !! )
int x ;
void m( ) {...} [ Keine Angabe ]
Zugriff "package"
(kein Schlüsselwort) :
In allen Klassen des Pakets sichtbar, nicht in allen anderen Paketen
private int x ; private void m( ) {...}
private :
Nur in eigener Klasse selbst sichtbar, nirgendwo sonst
Zugriff durch
auf Klasse mit Attribut:
eigene Klasse
Unterklasse fremde Klasse
FremdPackage
private
X
package
X
X
X (gleiches Paket)
protected
X
X
X (gl. Paket)
X (U-Kl.)
public
X
X
X
X
© H.Neuendorf
(76)
Zugriffsrecht protected in Java :
Paket P1
Anders als C++ da mit Java-Paketkonzept verbunden
(77)
Paket P2
Oberklasse O :
protected void m()
In C++ werden durch protected
die erbenden Unterklassen
gegenüber den nicht erbenden
Fremdklassen zugriffsmäßig
privilegiert …
Unterklasse U2
import P1.*;
Unterklasse 1
Fremdklasse 1
Java : Paket stellt KlassenKomponenten zum Überschreiben
in Unterklassen zur Verfügung
Fremdklasse 2
Klasse Oberklasse O aus P1 habe ein protected Element m()
Nur die nicht erbende Fremdklasse2 hat keinen Zugriff auf protected-Elemente der
Oberklasse O aus P1
Zugriff auf m() jedoch nur im Coding der Unterklasse 2 :
package P2 ;
import P1.O ;
Aber nicht auf deren Objekten :
class U2 extends O {
U2 u = new U2( ) ;
u.m( ) ;
© H.Neuendorf
// Fehler !!
Gilt auch für protected static
Attribute + Methoden :
Außerhalb Unterklasse auch auf
Klassennamen nicht sichtbar !
public void test( ) {
}
m( ) ; }
// ok!
(78)
Modifizierer von Klassen, Attributen, Methoden
Nicht alle Modifizierer auf alles anwendbar :
Modifizierer
public
Klasse
Methode
Konstruktor
X
X
X
protected
X
X
X
private
X
X
X
X
X
X
X
static
X (IF)
Attribut
(X innere)
final
X
abstract
X (IF)
IF-Methoden müssen stets
public + abstract sein
IF-Attribute müssen stets
public + static + final sein
X
strictfp
X
native
X
dh : Sind es automatisch, auch
wenn nicht explizit vermerkt …..
...........
Bei public- Paketklassen gilt noch viel schärfer als bei paket-internen nicht-public-Klassen das Prinzip :
Keine public-Attribute sondern nur public-Zugriffsmethoden (Setter / Getter)
⇒
Nur dadurch bewahrt man sich die Flexibilität, die innere Datenrepräsentation der Klasse später noch
verändern / anpassen zu können ….
© H.Neuendorf
(79)
Dokumentation von Schnittstellen
Interfaces, Klassen, Methoden, Attribute → Spezifikation von Funktionalität + Bedeutung
JDK-Unterstützung → Tool :
javadoc *.java
java.sun.com/javase/6/docs/technotes/tools/
javadoc [options] {filename | packageName}
Filenamen oder Paketname
Optionen : -public
-private
Nur Dok zu public members
Auch Dok zu private members
-package Dok zu package, public, protected members
-overview <file>
-d path
Überblicks-Doku zu Paket aus File einlesen
Zielpfad für erzeugte Dateien
...........
(u.v.a.m.)
⇒ HTML-Files mit Schnittstellendarstellung + Kommentaren aus Quellcode
HTML auch direkt in DK-Text verwendbar
In vorgebbaren Ordner angelegt
/**
...
*/
Regeln für Dokkommentare (DK) :
1. Jeder DK beschreibt Bezeichner dessen Deklaration unmittelbar folgt
2. Der erste Satz des DK ist Zusammenfassung für den Bezeichner = Text bis erster Punkt mit Leerschritt
3. *-Zeichen am Anfang von DK-Zeilen werden ignoriert
4. Nur DKs direkt vor Interfaces, Klassen, Methoden und Attributen werden verarbeitet
5. Wenn geerbte Methode (zB aus Interface !) keinen eigenen DK bekommt, erbt sie DK des Obertyps
6. Wenn geerbte Methode sowohl aus Oberklasse als auch aus Interface DKs erbt, wird IF-DK verwendet
© H.Neuendorf
(80)
Dokumentation von Schnittstellen : Annotationen @
/**
Java-Doc-Tags :
...
*/
(u.a.)
@param
Spezikation einzelner Parameter. Erstes Wort ist Parametername, Rest seine Beschreibung
@return
Rückgabewert der Methode
@author
Autorenangabe
@version
Beliebige Versionsangabe
java.sun.com/javase/6/docs/technotes/tools/
@throws Beschreibt Ausnahmebehandlung
@deprecated
Markiert als veraltet. Compiler markiert Feld als veraltet + erzeugt Warnung
@see Querverweis auf andere Javadoc-Docu oder andere Files in doc-files Verzeichnis
Interface / Klasse aus aktuellem Paket unqualifiziert angebbar, Typen anderer Paketen mit voll qualifizierten
Namen anzusprechen. Attribute und Methoden werden mit # vor ihrem Namen angesprochen.
Bsp: @see meineMethode(String, Object)
@see java.lang.String
@see java.lang.Math#PI
@see <a href="doc-files/spec.html#attr>Attribute Spezifikation</a>"
@see Made by Magic AG <img src="doc-files/magiclogo.gif">
{@link}
Analog @see aber für Verweise innerhalb DK-Text
Bsp:
Ändert Aufrufwert auf {@link #getValue} wenn nötig.
{@code} Erläuternde Code-Auszüge
Bsp:
© H.Neuendorf
@throws IndexOutOfBoundsException bzgl. Index. ( {@code index <0 || index>=size()} )
(81)
Java API - Dokumentation
Dokumentation JDK auf Sun-Homepage :
(einsehen oder downloaden)
http://java.sun.com/javase/6/docs/api/
http://java.sun.com Æ Popular Downloads Æ Java SE 6 Documentation Æ Download
In Eclipse :
Namen markieren
Kontextsensitiv - Positionieren auf ein Element + Navigate-Menü :
zeigt API-Doku an, sofern
JDK installiert
© H.Neuendorf
Strg + Leertaste
(82)
JavaDoc und JAR mit Eclipse
Alle Tools auch direkt mit
diversen Optionen auf
Kommandozeile ausführbar
JavaDoc
1. Ordner für die durch JavaDoc erstellten Files anlegen
2. Menü: Project → Properties → JavaDoc Location
mittels Browse auf angelegten Ordner verweisen
3. Aufruf von JavaDoc : Project → Generate JavaDoc
Alle JavaDoc-Optionen einstellbar
Alternativ über Kontextmenü : Export → Java → Javadoc
Kommandozeilenaufruf :
jar cvf Demo.jar *.class
JAR-Files
Für geöffnetes Projekt Kontextmenü :
Export → Java → JAR File
File Demo.jar wird erstellt.
Enthält alle .class-Files aus
aktuellem Verzeichnis
Im Wizzard alle Einstellungen bzgl. Ablageort und Inhalt (Manifest-Datei !) einstellbar
Erzeugtes .jar-File mit Zip-Programmen einsehbar
Ein .jar-File mit Manifest-Eintrag für main()-Klasse kann direkt ausgeführt werden
Aufruf Launcher java mit Option jar auf Kommandozeile :
Vorteil jar :
C:\> java –jar Test.jar ↵
Organisation & Kompression
Weitergabe eines ganzen Projekts als ein File statt in Vielzahl von Files
Remote-Aufruf: Alle .class-Files lokal verfügbar - nicht einzeln übers Netz zu laden
(Bsp → Applets)
© H.Neuendorf
Weitere Tools
jps
….
(83)
im bin-Ordner der JDK-Installation
(JVM Process Status Tool)
Aufruf auf Kommandozeile → Anzeige laufender Java-Instanzen mit ProcessID
jps
jmap
(JVM Memory Map)
Aufruf auf Kommandozeile → Anzahl + Speicherverbrauch aller Objekte zu bestimmter PID
jmap –histo 5460
jstack
(JVM Runtime Stack)
Aufruf auf Kommandozeile → Anzeige aller laufenden Threads und deren (Warte-)Zustand
(Full Thread Dump)
jstack 5460
jstat
(JVM Statistics Monitoring Tool)
Option –help zur ersten Orientierung
Ausführliche Dokumentation aller Tools und
ihrer Aufrufoptionen in JDK-Doku :
java.sun.com/javase/6/docs/technotes/tools/
Aufruf auf Kommandozeile → Abfrage von Performance-Statistiken
jstat –gcutil 4176
Kapselung von Java-Archiven in komprimiertes direkt ausführbares Programm für Windows, Linux, Mac, Solaris : launch4j
http://launch4j.sourceforge.net/
© H.Neuendorf
Weitere Tools
(bin-Ordner der JDK-Installation)
jconsole (JVM Management Konsole)
Aufruf auf Kommandozeile → Prozess-Management-Konsole für wählbaren Java-Prozess
© H.Neuendorf
(84)
(86)
Klassen + Interfaces des Pakets java.lang der Java SE
Alles was man grundsätzlich braucht, um JavaProgramme zu schreiben + ihre Fehler zu behandeln .....
java.lang
Systemklassen
System
Runtime
RuntimePermission
Process
ProcessBuilder
Thread
ThreadGroup
ThreadLocal<T>
InheritableThreadLocal<T>
Thread.State
ClassLoader
SecurityManager
Class<T>
Compiler
Package
StackTraceElement
Wrapper
Boolean
Byte
Character
Character.Subset
Character.UnicodeBlock
Double
Float
Integer
Long
Short
Number
Wichtigste Informationsquelle →
API-Dokumentation von Sun :
http://java.sun.com/javase/6/docs/api/
© H.Neuendorf
Ausnahmebehandlung
Throwable
Exception
NullPointerException
ArrayIndexOutOfBounds-Exception
ClassCastException
ClassNotFoundException
NumberFormatException
SecurityException
Error
InternalError
NoClassDefFoundException
Sonstiges
Object
Math
StrictMath
String
StringBuffer
StringBuilder
Void
Enum
... und viele weitere ...
Interfaces
Appendable
CharSequence
Cloneable
Comparable<T>
Iterable<T>
Readable
Runnable
Thread.UncaughtExceptionHandler
Wichtiges Package :
Wegen elementarer Bedeutung
automatisch importiert ⇒
Kein explizites:
import java.lang.*
zur Verwendung erforderlich !
(87)
Java SE - Fundamentale Pakete
Java Standard Edition
Basis
java.lang
java.lang.instrument
java.lang.reflect
java.util
java.util.concurrent
java.util.jar
java.util.zip
java.util.regex
java.math
javax.management
Text
java.text
javax.swing.text
javax.swing.text.html
javax.swing.text.rtf
Grafik
java.awt
java.awt.color
java.awt.dnd
java.awt.event
java.awt.image
java.awt.font
java.awt.geom
javax.swing
javax.swing.event
Komponenten
java.beans
java.lang.reflect
... und zahlreiche weitere spezielle Pakete ...
© H.Neuendorf
Ein-/ Ausgabe
java.io
jawa.awt.print
java.awt.im
javax.imageio
javax.print
javax.sound.midi
Daten / SQL
java.sql
javax.sql
javax.sql.rowset
java.awt.datatransfer
javax.crypto
Netzwerk/ Web
java.net
javax.net
java.nio
java.applet
java.rmi
javax.rmi
javax.rmi.ssl
java.rmi.server
java.security
java.security.cert
javax.naming
XML
javax.xml
javax.xml.parsers
javax.xml.transform
javax.xml.validation
javax.stax
javax.xml.xpath
org.w3c.dom
org.xml.sax
Java Enterprise Edition - Fundamentale Pakete
Java Enterprise Edition
Basiert aufJava SE !
Java Standard
= Java SE 6
Servlets / JSP
javax.servlet
javax.servlet.jsp
javax.servlet.http
Einsatz auf Applikationsservern :
Steuerung transaktionaler Client-ServerBusiness-Prozesse am Backend
Enterprise Java Beans
javax.ejb
Grundlegende Überarbeitung durch
Version EJB 3.0 ab Java EE 5
Aktuell : Java EE 6
Kein Produkt, sondern Spezifikation, die
Applikations-Server erfüllen muss.
Deutliche Vereinfachung der EJBImplementierung und Verteilung.
Hersteller von Java EE-Servern müssen
Schnittstellen implementieren, um von SUN
zertifiziert zu werden.
Mail
javax.mail
javax.mail.event
javax.activation
Message Queues
javax.jms
Sun © liefert freie Referenzimplementation :
→ java.sun.com/javaee/
OpenSource: GlassFish v3
OpenSource J2EE-Server JBoss :
→ www.jboss.com/downloads/index
© H.Neuendorf
(89)
Kommunikation
javax.ressource
javax.xml.rpc
javax.xml.rpc.handler
javax.xml.rpc.server
javax.xml.rpc.soap
javax.xml.soap
Datenzugriff
javax.transaction
javax.xml.namespace
javax.xml.parsers
javax.xml.rpc
javax.xml.registry
javax.xml.transform
javax.xml.transform.dom
javax.xml.sax
management
javax.management
... und weitere
spezielle Pakete ...
(90)
Java Micro Edition - Fundamentale Pakete
Einsatz der Java ME auf Geräten mit
geringer Hardwareausstattung :
Handys, Palmtops, Smartphones,
Embedded Systems, ........
Java Micro Edition
Basis
Ein-/ Ausgabe
java.lang
java.util
java.io
Spezial-APIs
javax.microedition.io
javax.microedition.lcdui
javax.microedition.rms
Erweiterungen im Bereich
GUI, Netz, ........
Basiert auf Miniversion der virtuellen
Maschine = KVM
Noch manches im Fluss ........
J2ME Wireless Toolkit
=
Umgebung KToolbar, Emulatoren, J2ME, Dokumentation
Quelle:
© H.Neuendorf
java.sun.com /javame
(91)
Nützliche Klassen aus Java SE-Paketen
Mathematische Funktionen und Konstanten
Klasse :
java.lang.Math
java.lang.StrictMath
Nur statische Konstanten und Methoden
Gleiche Methoden wie in Math – aber
als strictfp gekennzeichnet ⇒
Strenge Gleitkommaarithmetik
garantiert reproduzierbare Ergebnisse
Konstanten :
Math.E
Math.PI
Methoden :
teilweise überladene Fassungen für verschiedene primitive Datentypen
abs()
acos()
asin()
atan()
atan2()
cbrt()
ceil()
hypot()
log()
log10() log1p() max()
min()
pow() random() rint()
sinh()
sqrt()
tan()
tanh()
toDegrees()
cos()
cosh()
exp()
floor()
round() signum() sin()
toRadians() ...
Definitionen siehe JDK-Doku
// import java.lang.Math; nicht erforderlich, da aus Standardpaket java.lang
// import static java.lang.Math;
// evtl. hilfreich – neues Feature in 5.0
public class Mathe {
public static void main( String[] args) {
IO.writeln( "Pi = " + Math.PI + " e = " + Math.E );
double d = Math.max( Math.PI, Math.E );
IO.writeln( "Cosinus(Pi) = " + Math.cos( Math.PI) );
IO.writeln( "Kubikwurzel von 27 = " + Math.cbrt(27) );
}
}
© H.Neuendorf
expm1()
(92)
Nützliche Klassen: Zufallszahlen
Klasse:
import java.util.Random ;
java.util.Random
Erzeugung von Zufallszahlen
Konstruktoren
class Lotto {
Random()
Random ( long seed )
Ganzzahlige Zufallszahl im gesamten
Wertebereich von int oder long :
int nextInt()
public static void main( String[] args) {
int zahl ;
Random generator = new Random( ) ;
long nextLong()
Zufallszahl zwischen 0 (incl) und n (excl) :
zahl = generator.nextInt( 50 ) ;
// liefert Zahlen im Bereich 0 bis 49 !
int nextInt( n )
Boolean :
boolean nextBoolean()
}
Fließkomma-Zufallszahl im Wertebereich von }
0.0(f) bis excl. 1.0(f) :
float nextFloat()
Initialisieren des Generators:
double nextDouble()
Random( ) ;
Gaußverteilte Fließkomma-Zufallszahlen
zentriert um 0.0 mit SdAbw. 1.0 :
nextGaussian()
© H.Neuendorf
→ liefert stets andere Folge von Zahlen!
Random( long seed ) ;
Ändern des Startwerts des Generators
void setSeed( long seed )
→ intern mittels Systemzeit
……
→ explizit mittels Wert seed
→ liefert stets gleiche Folge von Zahlen!
(93)
Stoppuhr / Timer
// import java.lang.* ;
Klasse:
java.lang.System
Methode:
System.currentTimeMillis( ) ;
Anzahl Millisekunden seit 1.1.1970
als long-Wert
public class Timer {
public static void main( String[] args) {
long start ;
long end ;
start = System.currentTimeMillis( ) ;
Verstrichene Zeit durch Differenzbildung zweier Aufrufe
int test = 0 ;
for ( int i = 1; i<100000; i++ ) {
test = test + i ;
}
Anwendung
Testen von Algorithmen
Evaluieren in welchen Programmteilen die
meiste Zeit verbraucht wird
end = System.currentTimeMillis( ) ;
In J2SE 5.0 zusätzlich die Methode :
long dauer = end - start ;
System.nanoTime( ) ;
Liefert Zeitwert des genauesten
Systemzeitgebers in Nanosekunden.
Allerdings Nanosekundengenauigkeit
sicherlich nicht erreicht .....
© H.Neuendorf
IO.writeln( "Dauer [ms] = " + dauer ) ;
}
}
(94)
Darstellung von Dezimalzahlen
Klasse
java.text.DecimalFormat
0 Ziffer, führende Nullen werden angezeigt
.
Dezimalpunkt (landesspezifisch)
,
Tausendertrennung (landesspezifisch)
% Darstellung als Prozentzahl
E Trennt Platzhalter für Mantisse und Exponent
Alle anderen Zeichen werden direkt in den formatierten
String übernommen !
Methoden :
String format( double x )
String format( long x )
Gibt Inhalt von x als gemäß Pattern-Vorgabe
formatierten String zurück.
© H.Neuendorf
}
# Ziffer, führende Nullen werden nicht angezeigt
import java.text.DecimalFormat ;
class Format {
public static void main( String[] args) {
Erzeugt ein Objekt mit dem durch pattern
vorgegebenen Format. String pattern kann sich
zusammensetzen aus Platzhaltern für :
}
DecimalFormat( String pattern )
IO.writeln( s ) ;
// Ausgabe ist 24.522,46
Konstruktor :
DecimalFormat f = new DecimalFormat( "###,##0.00" ) ;
String s = f.format( 24522.4567 ) ;
Formatierung von Dezimalzahlen vor Ausgabe
Darstellung von Dezimalzahlen
java.text.DecimalFormat
import java.text.DecimalFormat ;
public 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 ) ;
s = f3.format( 55.123456 ) ;
IO.writeln( s ) ;
Ausgabe :
24.522,46
s = f4.format( 0.123456789 ) ;
IO.writeln( s ) ;
ausgabe( f4, 4568.56 ) ;
Wert: 000.098,76500 Euro
98,77
}
Prozente = 5512,35 %
// Generische Methode zur formatierten Ausgabe :
1,2E-001
public static void ausgabe( DecimalFormat f, double d ) {
IO.writeln( f.format( d ) ) ;
}
}
© H.Neuendorf
4,6E003
(95)
(96)
Zerlegen von Strings
Klasse
java.util.StringTokenizer
import java.util.StringTokenizer ;
Zerlegung von Zeichenketten in definierte Einheiten =
class Tokens {
Token
Definition der Tokens : Was soll als Trennzeichen
zwischen zwei Tokens betrachtet werden ?
public static void main( String[] args ) {
Konstruktoren :
String ein = "HalloÖihrÖBA-Öler" ;
String trenn = "Ö" ;
StringTokenizer( String eingabestring ) ;
⇒ Leerzeichen als Trennzeichen
StringTokenizer st =
new StringTokenizer( ein, trenn );
StringTokenizer( String eingabestring, String trennstring ) ;
⇒ trennstring als Trennzeichen
int n = st.countTokens( ) ;
IO.writeln( "Tokenanzahl = " + n ) ;
Zugriff auf Tokens : (u.a.)
Anzahl von Tokens, die noch im StringTokenizer-Objekt
vorhanden sind :
while( st.hasMoreTokens( ) ) {
IO.writeln( st.nextToken( ) );
}
int countTokens( ) ;
Abfrage, ob noch Tokens vorhanden sind :
boolean hasMoreTokens( ) ;
}
}
Abgreifen des nächsten Tokens :
String nextToken( ) ;
© H.Neuendorf
Wenn keine mehr vorhanden :
NoSuchElementException
Systemzugriff
(97)
java.lang.System
Kapselt Funktionen der JVM
Zugriff auf Standard-Ein- und -Ausgabe,
Umgebungsvariablen, ...
Von System können keine Objecte erzeugt
werden - nur statische Klassenmethoden
import java.util.Properties ;
public class SysInfo {
public static void main( String[] args )
throws Exception {
// alle auf einmal ausgeben:
Properties p = System.getProperties( ) ;
p.list( System.out ) ;
p.storeToXML( System.out, "Kommentar" );
void gc( )
Bittet JVM, Garbadge Collector zu starten
// Spezielle Property :
String key = "java.version" ;
String prop = System.getProperty( key ) ;
IO.writeln( prop ) ;
void exit( int status )
Beendigung der laufenden JVM (und damit des
Programms) mit dem Exit-Code status
Properties getProperties( )
String getProperty( String key )
Abfrage der System-Properties, falls Zugriff
darauf erlaubt
Key ist Schlüssel der Properties - auch
ausgegeben, wenn man über Property-Objekt
alle zugänglichen Properties ausgibt.
© H.Neuendorf
}
}
Bsp :
java.version
java.vm.version
java.class.version
java.io.tmpdir
os.name
file.separator
user.name
java.vendor
java.vm.vendor
java.class.path
java.compiler
os.arch
path.separator
user.home
java.home
java.vm.name
java.library.path
java.ext.dirs
os.version
line.separator
user.dir
Browser / Editor / E-mail-Client aufrufen :
Plattformunahhängige
Programmierung des Starts von
Applikationen / Viewern, die auf
System bestimmten
Dokumenten zugeordnet sind.
Methoden der Klasse
Desktop :
class DesktopTest {
public static void main( String[] args ) throws Exception
mail( URI mailto)
edit( File file )
………..
© H.Neuendorf
{
Desktop myDesktop = Desktop.getDesktop( ) ;
myDesktop.browse( new URI( "www.amazon.de" ) ) ;
// öffnet Standardbrowser mit Webseite
// ………
mail()
print( File file )
(98)
(seit Java 6)
import java.awt.Desktop;
import java.net.URI;
import java.io.File;
browse( URI uri )
open( File file )
java.awt.Desktop
}
}
Systemzugriff :
Start von Fremdprozessen +
Abfrage Speichergröße :
Runtime getRuntime( )
Liefert Runtime-Objekt für
laufende JVM
long totalMemory( )
Liefert Größe des Systemspeichers in Bytes
Process p = rt.exec( "C:\\Windows\\notepad.exe" ) ;
IO.promptAndReadString( "Drücke Taste!" ) ;
p.destroy( ) ;
Process exec( String cmd )
Vollständige Pfadangabe des
aufzurufenden Programms/
System-Kommandos incl
Parametern
Klasse Process repräsentiert
den gestarteten Prozess :
void destroy( )
Beenden des gest. Prozesses
© H.Neuendorf
(99)
class CTest {
// Alles in java.lang
public static void main( String[] args ) throws Exception
{
Runtime rt = Runtime.getRuntime( ) ;
IO.writeln("Systemspeicher = " + rt.totalMemory( ) ) ;
IO.writeln("Freier Speicher = " + rt.freeMemory( ) ) ;
long freeMemory( )
Liefert Anzahl nichtbelegter
Bytes
Voraussetzung: Ausreichende Rechte !!
java.lang.Runtime
String[] a = new String[2] ;
a[0] = "Hallo" ;
a[1] = "ihr da";
Prog.main(a) ;
// Prog.class reicht aus !
}
}
Aus einem Java-Programm können beliebig viele andere
Java-Programme aufgerufen und String-Array als
Parameter für main() mitgegeben werden :
<Programmname>.main(args);
Systemzugriff
(100)
java.lang.ProcessBuilder
Start von Fremdprozessen mittels ProcessBuilder :
ProcessBuilder( List<String> command )
ProcessBuilder( String ... command )
Erzeugen ProcessBuilder-Objekt mit Angabe Programmnamen und variabler Zahl von Aufrufargumenten
ProcessBuilder command( List<String> command )
ProcessBuilder command( String ... command )
Setzen des Programmnamens und variabler Zahl von Aufrufargumenten
List< String > command()
Liefert gesetzten Programmnamen und Argumente als String-List
ProcessBuilder directory( File directory)
Setzen des Arbeitsverzeichnisses
File directory( )
Liefert Arbeitsverzeichnis
Auch bei ProcessBuilder repräsentiert die
Klasse Process den gestarteten Prozess :
void destroy( )
Beenden des gestarteten Prozesses
Map< String, String > environment()
Liefert betriebssystemspezifische Umgebungwerte des ProcessBuilders
Process start()
Start des Prozesses
... und weitere Methoden, zur Festlegung der Ausgabe von Fehlermeldungen
...
© H.Neuendorf
Systemzugriff
(101)
java.lang.ProcessBuilder
Aufruf des notepad-Editors
unter Windows mit zwei
abgelegten Textfiles ....
import java.io.File;
public class Prozesse {
public static void main( String[] args) throws Exception {
ProcessBuilder pB = new ProcessBuilder( "notepad", "Demo1.txt" ) ;
pB.directory( new File( "C:/WINNT/" ) ) ;
IO.writeln( pB.command() ) ;
IO.writeln( pB.directory() ) ;
IO.writeln( "Umgebungsinfo: \n" + pB.environment().toString() ) ;
Process p1 = pB.start() ;
pB.command( "notepad", "Demo2.txt" ) ;
// Für ProcessBuilder-Objekte darf start() mehrfach aufgerufen werden:
Process p2 = pB.start() ;
IO.promptAndReadString( "Drücke Taste!" ) ;
p1.destroy() ;
p2.destroy() ;
}
}
Ausgabe:
(nur teilweise .....)
[notepad, Demo1.txt]
C:\WINNT
Umgebungsinfo:
{PROCESSOR_ARCHITECTURE=x86, LOGONSERVER=
// und noch vieles mehr ................
© H.Neuendorf
Herunterladen