Kap.9: KLASSENHIERARCHISCHES PROGRAMMIEREN
Kapitel-Index
9.1 Klassen (class, ggf. generisch) und Objekte
9.1.1 Klassen als Referenztypen, Mitglieder
9.1.2 Konstruktoren (ggf. generisch), this
9.1.3 Superklasse, super(), einfache Erbschaft (extends)
9.1.4 Abstrakte Klassen
9.1.5 Rekursive Typen
9.1.6 Aufzählung (Enumeration)
9.2 Schnittstellen (interface, ggf. generisch)
9.2.1 Schnittstellen als Referenztypen, Mitglieder
9.2.2 Superschnittst. (extends), mehrf. Erbsch. (implements)
9.2.3 Anmerkungstyp (Annotation)
9.3 Testfragen
Man verwendet Klassen (9.1), um hierarchische Strukturen mitsamt
der darin verfügbaren Methoden zu beschreiben. Klassen können Mitglieder von (Super-) Klassen erben. In Java kann es zu einer Klasse höchstens eine Superklasse geben, d.h. es ist nur "einfache
Erbschaft" möglich. "Mehrfache Erbschaft" kann mit Hilfe von
Schnittstellen (9.2) simuliert werden.
9.1 Klassen (class, ggf. generisch) und Objekte
Das Klassen-Konzept stammt aus SIMULA (1966). Eine Klasse ist
ein Typ, der neben Datenfeldern, d.h. Klassen-Variablen oder
-Konstanten, auch Methoden und innere Klassen oder innere Schnittstellen als Mitglieder enthalten kann. Der Vorteil "klassenhierarchischen Programmierens" liegt in der übersichtlichen Darstellung der Zusammenhänge.
"Späte Bindung" von Klassen kann erreicht werden durch

abstrakte Klassen (9.1.4), d.h.
Klassen mit Modifizierer abstract bzw.
mit abstrakter Methoden-Vereinbarung, oder

generische Klassen (siehe Beispiel Generic 9.2.2 und
Beispiel Net 14.1.2 mit generischer Klasse Vector), d.h.
Klassen- oder Konstruktor- oder Methoden-Vereinbarung
mit Typ-Parameter-Klausel.
Mit Klassentypen können namenlose new-Objekte, siehe z.B.
SchulKlasse (9.1.1), oder Objekte mit Namen, siehe z.B. Ziffer
(9.1.2), erzeugt werden. Nur static Mitglieder von Klassen sind
ohne Objekt - Erzeugung zugreifbar, siehe z.B. EinkommenSteuer
(9.1.4) mit static-Import des Datenfelds static PrintStream out
der Klasse java.lang.System.
------------------
VVVVVVVVVVV
Objekt (Prinzip)
VVVVVVVVVV
VV
------------------
VV
VV Ein Objekt ist eine Instanz (Spei- VV
VV cher-Bereitstellung) einer Klasse. VV
VV VV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
Der Vorteil "objektorientierten Programmierens" liegt in der
Werbewirksamkeit des Schlagworts, unter dem man sich "benutzerorientiertes Programmieren" vorstellt, obwohl es sich um "speicheraufwendiges Programmieren" handelt, siehe z.B. TierLaute (9.1.3).
9.1.1 Klassen als Referenz-Typen, Mitglieder
Wie schon in Kap. 6 (Reihung, 6.1 ff) ausgeführt, verzichtet man
in Java auf explizite Zeiger ('explicite reference considered
harmful') und führt statt dessen implizit Zeiger ein, d.h. Referenz-Typen (ReferenceType Syntax.030, 2) für Reihungen (6), Klassen (9.1.1) und Schnittstellen (9.2.1). Ein Zeiger-Zugriff (Referenz) wird realisiert durch eine Konstruktion aus zwei Speichern,
wobei der erste die Adresse des zweiten Speichers zum Inhalt hat.
//********************** SchulKlasse.java ************************
// Klasse mit Lehrer Laempel und Schuelern Max und Moritz *
//****************************************************************
class Schueler // Klassen-Vereinbarung
{String name; // Mitglied: Datenfeld
int note; // Mitglied: Datenfeld
Schueler(String name,int note) // Konstruktor
{this.name=name;this.note=note;}
} // `--------------`------------- Datenfeld-Zugriff
class Klasse // Klassen-Vereinbarung
{String lehrer; // Mitglied: Datenfeld
Schueler[] reihe; // Mitglied: Datenfeld
void druck() // Mitglied: Methode
{ System.out.println(lehrer);
for(Schueler each:reihe)
{System.out.println(each.name+" Note "+each.note);}
}
Klasse(String lehrer,Schueler[] reihe) // Konstruktor
{this.lehrer=lehrer;this.reihe=reihe;druck();}
} // `------------------`--------- Datenfeld-Zugriff
class SchulKlasse
{public static void main(String[] args) // args ungenutzt
{Schueler Max =new Schueler("Max ",3), // Objekt
Moritz=new Schueler("Moritz",4); // Objekt
new Klasse("Laempel",new Schueler[]{Max,Moritz});
// anonymes Objekt anonymes Objekt
}
}
//****************************************************************
Output
-------------
Laempel
Max Note 3
Moritz Note 4
Am obigen Beispiel SchulKlasse wird die Vereinbarung von Klassen
mit Datenfeldern, Methoden, Konstruktoren und die Instantiierung
dieser Klassen durch Vereinbarung von Objekten vorgeführt.
Eine normale Klassen-Vereinbarung (NormalClassDeclaration) ist
nach Syntaxdiagramm Syntax.091 von der Form
091: NormalClassDeclaration NormaleKlassenVereinbarung
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
H
--------------------
H
H
---------------
H
H -
-
ClassModifier
-
H
H
---------------
H
H
---------
---------------------
H
H
-
class -
Identifer
-
-
TypeParameterClause
-
H
H
---------
---------------------
H
H
of generic class
H
H
-------------------------
--------------------------
H
H
Super-
-------- ,
------
H
H
---------
-------------
H
H
-
extends -
ClassType
-
-
implements -
-
InterfaceType
-
H
H
---------
-------------
H
H
------------
---------------------------------
H
H
C l a s s B o d y H
H
-------------------------------
H
H
C l a s s B o d y Declaration
H
H
------------------------
H
H
-
{ -
-
-
ClassMemberDeclaration
-
-
} -
H
H
------------------------
H
H
------------------------
H
H
-
ConstructorDeclaration
-
H
H
------------------------
H
H
-
static ---
Initializer
H
H
-------
H
H
------------
----
Block
-
H
H
instance
-------
H
H
---------------------------------
H
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
z.B. (siehe SchulKlasse oben)
class // NormalClassDeclaration
Klasse // Class-Identifier
{String lehrer; //ClassMemberDeclaration
Schueler[] reihe; //ClassMemberDeclaration
void druck() {...} //ClassMemberDeclaration
Klasse(String lehrer,Schueler[] reihe)//ConstructorDeclaration
{...}
} // ClassBody
In der Mathematik steht der Begriff Klasse synonym für Menge.
Dementsprechend wird in Java eine Klasse vereinbart (NormalClassDeclaration) als Menge von Mitgliedern (ClassMemberDeclaration).
Es werden die mathematischen Mengenbildungszeichen { } verwendet.
Ein Objekt, d.h. die Instanz (Speicherbereitstellung) einer
Klasse, wird vereinbart mit dem Klassentyp und initialisiert mit
einem Klasseninstanz-Erzeugungsausdruck, der mit einem Allokator
(Seicherbereitstellungsoperator) new beginnt und den Konstruktor
der Klasse aufruft.
Ein Klasseninstanz-Erzeugungsausdruck (ClassInstanceCreationExpression) ist nach Syntaxdiagramm 092 von der Form
092: ClassInstanceCreationExpres. KlassenInstanzErzeugungsAusdruck
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
H Allocator H
H -
---------------------
new -
H
H
H
H
--------------------------
H
H
--------------------------
H
H
-
NonWildTypeArgumentClause
-
accessible H
H
--------------------------
Non-Enum- H
H
of generic constructor
--------------------
H
H
------------------------------
-
ClassOrInterfaceType
-
H
H
---------
Allocator
--------------------
H
H
---
Primary
-
. -
new -
H
H
---------
H
H
--------------------------
H
H
--------------------------
H
H
-
NonWildTypeArgumentClause
-
H
H
--------------------------
Constructor-
H
H
of generic constructor
------------
H
H
------------------------------
-
Identifier
-
H
H
------------
H
H
-------------------------------------------------
H
H
--------------------------
H
H
-
NonWildTypeArgumentClause
-
H
H
--------------------------
H
H
of generic class
H
H
-------------------------------
-------------------------
H
H
----------------
-----------
H
H
-
ArgumentClause
-----------
-
ClassBody
--------
-
H
H
----------------
-----------
H
H of constructor
of anonymous class
H
H
H
H
-----------------------
H
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
z.B. (siehe SchulKlasse oben)
new // Allocator
Schueler // ClassOrInterfaceType
("Moritz",4) // ArgumentClause
9.1.2 Konstruktoren (ggf. generisch), this
Konstruktoren dienen nur zur Klasseninstanz-Erzeugung (9.1.1).
Ein Konstruktor ist kein Mitglied seiner Klasse.
-----------------------
VVVVVVVVVVVVVVVVVVVVV
Konstruktor (Prinzip)
VVVVVVVVVVVVVVVVVVVV
VV
-----------------------
VV
VV Ein Konstruktor einer Klasse ähnelt einer Methode, VV
VV auch hinsichtlich Signatur und Überladen (7.3), aber VV
VV VV
VV
ein Konstruktor hat den selben Bezeichner wie die Klasse, VV
VV
ein Konstruktor hat keinen Ergebnistyp (auch nicht void). VV
VV VV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
//************************* Ziffer.java **************************
// Deutung chinesischer Ziffer-Bilder, Zhou, 1.Jtv. *
// Deutung deutscher Zahl-Worte, IndoEuropa, 2.Jtv. *
//****************************************************************
class Zif
{final String deutung; // Datenfeld
void druck() {System.out.println(deutung);} // Methode
Zif(String deutung){this.deutung=deutung;druck();}// Konstruktor
}
class Ziffer
{public static void main(String[] args) // args ungenutzt
{Zif
z1=new Zif( " \r\n ---1 Horizt Yang\r\n EIN weSen "),
z2=new Zif( " --,\r\n / 2 Him+Erd Yin\r\n `-- ZWEIgwesen "),
z3=new Zif( " -, \r\n -+-3 Trieb links\r\n -' TREIbwesen "),
z4=new Zif( ",--,\r\n||||4 4Eck+2Teilg\r\n`||' FInger quER"),
z5=new Zif( "-, \r\n +-,5 Hand links \r\n-' | FINgerWesen"),
z6=new Zif(" /\\ \r\n/||\\6 3Eck+2Teilg\r\n || SaEGweSen "),
z7=new Zif( " -, \r\n -+-7 RegenDrache\r\n |_ SIEBwesEN "),
z8=new Zif( " \r\n || 8 2Teilung \r\n || AuCH Tswei "),
z9=new Zif( " ,-,\r\n,+,|9 3*3Peitsche\r\n³³³| NEU Neu ");
}
}
//****************************************************************
Output
Output (Fortsetzung)
Output (Fortsetzung)
-----------------
---------------------
---------------------
,--,
-,
---1 Horizt Yang
||||4 4Eck+2Teilg
-+-7 RegenDrache
EIN weSen
`||' FInger quER
|_ SIEBwesEN
--,
-,
/ 2 Him+Erd Yin
+-,5 Hand links
|| 8 2Teilung
`-- ZWEIgwesen
-' | FINgerWesen
|| AuCH Tswei
-,
/\
,-,
-+-3 Trieb links
/||\6 3Eck+2Teilg
,+,|9 3*3Peitsche
-' TREIbwesen
|| SaEGweSen
³³³| NEU Neu
Beim obigen Programm Ziffer verbindet der Autor die "Pflicht",
ein einfaches Konstruktor-Beispiel vorzuführen, mit der "Kür", die
alten chinesischen Ziffer-Bilder aus der Zhou-Dynastie und die
entsprechenden deutschen aus dem Indoeuropäischen stammenden ZahlWorte zu deuten, siehe
In der obigen Klasse Zif besetzt der Konstruktor Zif(String) das
Datenfeld deutung mit einem Wert und ruft dann die Methode druck()
auf. Es kann Klassen ohne Konstruktor geben, dafür existiert ein
Default-Konstruktor mit Argument-Klausel ohne Argumente, und es
können Datenfelder durch den Konstruktor nicht mit Werten besetzt
sein, dann werden die Datenfelder mit Vorgabe-Nullwerten (DefaultNullValue Syntax.038) besetzt. Es kann auch Klassen mit mehreren
hinsichtlich ihrer Signatur verschiedenen Konstruktoren geben.
Ein Datenfeld-Zugriff (FieldAccess) ist nach Syntaxdiagramm
075 von der Form
075: FieldAccess DatenfeldZugriff
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
H
---------
H
H -
-
Primary
-
. ---
H
H
---------
H
H
e.g. this
H
H
H
H
Class-
H
H
------
H
H
-
Name
----
. -
H
H
------
Field- H
H
-------------------
------------
H
H
-
super -----
. ---
-
Identifier
-
H
H
------------
H
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
z.B. (siehe Klasse Zif in Ziffer oben)
this.deutung
Der formale Parameter String deutung des Konstruktors hat den
gleichen Bezeichner wie das Datenfeld String deutung. Für die eindeutige Bestimmung der linken Seite der Zuweisung
wird der Datenfeld-Zugriff this.deutung verwendet. Damit erübrigt
sich eine Umbenennung des formalen Parameters und der direkte
Bezug zum Datenfeld bleibt erkennbar. Im obigen Beispiel Ziffer
wird bei den Vereinbarungen der Objekte z1,...,z9 vom Typ Zif
durch Aufruf des Konstruktors Zif(String deutung) das Datenfeld
String deutung mit dem jeweils als Konstruktor-Argument genannten
String-Literal besetzt.
Konstruktor-Vereinbarer (Constructor-Declarator Syntax.087) können auch eine Typ-Parameter-Klausel besitzen, d.h. einen generischen Konstruktor vereinbaren (siehe Beispiel Generic 9.2.2).
9.1.3 Superklasse, super(), einfache Erbschaft (extends)
Im folgenden Beispiel TierLaute werden eine Superklasse Tier und
zwei Subklassen Hahn, Hund vereinbart, d.h. eine Hierarchie:
Tier
--
--
-...-
Hahn Hund weitere
möglich
//*********************** TierLaute.java *************************
// Kinder-Worte fuer Tierlaute in 5 verschiedenen Sprachen. *
// Hierarchie aus 3 Klassen. Erzeugung von 30 Klassen-Instanzen. *
//****************************************************************
abstract class Tier // Superklasse
{public static String[] sprachen= // Datenfeld
{" frnz." ," dtsh." ," russ." ," pers." ," chin." };
protected Tier(int i) // Konstruktor
{System.out.print(sprachen[i]);}
protected void gibLaut(String art,String laut) // Methode
{System.out.print(art+": "+laut);}
}
class Hahn extends Tier // Subklasse
{private String[] laute= // Datenfeld
{"KOkRIKO ","KIKERIKI ","KUKAREKU ","kUkULIkU ","KOKELOcO "};
Hahn(int i) {super(i);gibLaut("Hahn",laute[i]);} // Konstruktor
} // `----------------- ExpliziterKonstruktorAufruf
class Hund extends Tier // Subklasse
{private String[] laute= // Datenfeld
{"OUAH OUAH","WAU WAU ","GAV GAV ","HAp HAp ","WAn WAn "};
Hund(int i) {super(i);gibLaut("Hund",laute[i]);} // Konstruktor
} // `----------------- ExpliziterKonstruktorAufruf
class TierLaute
{public static void main(String[] args) // args ungenutzt
{for(int i=0;i<Tier.sprachen.length;i++)
{new Hahn(i);new Hund(i);System.out.println();}
// `-----------`----------- KlassenInstanzErzeugungsAusdruck
}
}
//****************************************************************
Hierarchie (30 Instanzen)
Output Object (10)
-----------------------------------------
frnz.Hahn: KOkRIKO frnz.Hund: OUAH OUAH Tier (10)
dtsh.Hahn: KIKERIKI dtsh.Hund: WAU WAU
russ.Hahn: KUKAREKU russ.Hund: GAV GAV
----
----
pers.Hahn: kUkULIku pers.Hund: HAp HAp
chin.Hahn: KOKELOcO chin.Hund: WAn WAn Hahn (5) Hund (5)
Mit "Hahn extends Tier" und "Hund extends Tier" wird in den
Klassen-Vereinbarungen (NormalClassDeclaration Syntax.091, 9.1.1)
von Hahn und Huhn festgelegt, dass sie Subklassen der Superklasse
Tier sind. Da in einer Klassen-Vereinbarung höchstens ein extends
mit höchstens einem nachfolgenden (Super-) Klassen-Bezeichner vorkommen kann, ist nur "einfache Erbschaft" möglich.
Zugriffsrechte werden mit Zugriff-Modifizierern (AccessModifier,
Modifier Syntax.070) geregelt. Im obigen Beispiel TierLaute ist
das Datenfeld public static String[] sprachen qualifiziert zugreifbar in jedem importierenden Paket sowie direkt zugreifbar in
der aktuellen Klasse Tier und in den Subklassen Hahn und Hund. Die
Methode protected void gibLaut(...) ist qualifiziert zugreifbar im
aktuellen Paket sowie direkt zugreifbar in der aktuellen Klasse
Tier und in den Subklassen Hahn und Hund. Das Datenfeld private
String[] laute ist nur zugreifbar in der eigenen Klasse Hahn bzw.
Hund. Der Modifizierer abstract der Klasse Tier kann fortgelassen
werden,da die Klasse Tier keine abstrakte Methode besitzt (9.1.4).
Er zeigt nur an, dass keine Objekte dieser Klasse gebildet werden.
Eine Konstruktor-Vereinbarung (ConstructorDeclaration) ist nach
Syntaxdiagramm 089 von der Form
089: ConstructorDeclaration KonstruktorVereinbarung
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
H
--------------------------
H
H
---------------------
H
H -
-
ConstructorModifier
-
H
H
---------------------
H
H
-----------------------
--------
H
H
-
ConstructorDeclarator
-
-
Throws
-
H
H
-----------------------
--------
H
H
----------------------------
-------------
H
H
C o n s t r u c t o r B o d y H
H
--------------
-------------------
H
H
Explicite-
--------------
H
H
-
{ -
-
Constructor-
-
-
-
BlockStatement
-
---
} -
H
H
Invocation
--------------
H
H
--------------
--------------------
H
H
-------------------
H
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
z.B. (siehe TierLaute oben)
Hahn(int i) // ConstructorDeclaration
{ // ConstructorBody
super(i); // ExpliciteConstructorInvocation
gibLaut("Hahn",laute[i]); // BlockStatement
}
Mit dem expliziten Konstruktor-Aufruf (ExpliciteConstructorInvocation, Syntax.088) am Anfang der Konstruktor-Vereinbarungen
von Hahn und Hund
wird der Konstruktor der Superklasse Tier aufgerufen.
Dort, in der Tier-Hierarchie, darf "super(i);" nicht ersetzt
werden durch "new Tier(i);", auch nicht nach Weglassen des Modifizierers abstract der Klasse Tier.
Um weitere Subklassen, z.B. Pferd, der Superklasse Tier hinzuzufügen, braucht die Superklasse Tier nicht verändert zu werden.
Klassen-hierarchisches Programmieren unterstützt also das vom
strukturierten Programmieren her bekannte Konzept der "schrittweisen Verfeinerung".
Das obige Beispiel TierLaute zeigt, dass für die kleine Hierarchie aus 3 Klassen Hahn, Hund und Tier bereits 30 Instanzen
angelegt werden: Mit den Klassen Hahn und Hund werden jeweils
5 namenlose new-Objekte angelegt, insgesamt 10 Instanzen. Für jede
der 10 Instanzen von Hahn und Hund wird jeweils eine neue Instanz
ihrer Superklasse Tier angelegt und außerdem für jede der 10 Instanzen von Tier eine Instanz der Superklasse Object.
Die Klasse java.lang.Object ist direkt oder indirekt Superklasse
jeder Klasse. Sie enthält z.B. die Methode toString() für String
-Darstellung (2.6) eines Objekts sowie die Methoden wait() und
notifyAll() für synchronisiertes Multithreading (12.2).
Probleme mit einer größeren Anzahl von Objekten, z.B. Auftragsabwicklung mit Kunden und Artikeln, sollten nicht mit "objektorientiertem Programmieren" gelöst werden, sondern mit relationalen Datenbanken, z.B. MySQL, für die es Konnektoren zu Java gibt
(15).
Eine Klasse kann mit einem Modifizierer abstract vereinbart
werden, wenn kein Objekt dieser Klasse gebildet werden soll, vgl.
Tier in TierLaute (9.1.3). Eine Klasse muss mit einem Modifizierer
abstract vereinbart werden, wenn sie abstrakte Mitglieder (ClassMemberDeclaration, Syntax.090) enthält, vgl. Tarif im folgenden Bei-
spiel EinkommenSteuer.
Für Subklassen abstrakter Klasse oder - falls diese wieder abstrakt sind - für Subklassen dieser Subklassen können dann Objekte
gebildet werden.
Eine abstrakte (unvollständige) Methode, d.h. eine Methode mit
einem Modifizierer abstract, enthält in ihrer Vereinbarung (MethodDeclaration Syntax.083, 7.1) statt des Methoden-Rumpfs nur ein
Semikolon. Sie dient als Platzhalter für eine Signatur-gleiche
vollständig mit Methoden-Rumpf in einer Subklasse vereinbarten Methode.
Es folgt das Beispiel EinkommenSteuer mit abstrakter Klasse
Tarif und darin vereinbarter abstrakter Methode hebSteuer(int).
Zur Superklasse Tarif werden zwei Subklassen Grund und Split
vereinbart, d.h. die folgende Klassenhierarchie:
Die Steuererhebung mit Abfrage und Ausdruck ist in Tarif als
abstrakte Methode hebSteuer(int) vereinbart. Sie wird in Grund und
in Split überschrieben.
Die eigentliche Steuerberechnung ist in Tarif als nicht-abstrakte
Methode setzTarif(int) vereinbart. Sie gilt sowohl für Grund als
auch für Split.
//********************* EinkommenSteuer.java *********************
// Grund- und Splitting-Tarif 2010. *
// Command: java EinkommenSteuer Einkommen [split] *
//****************************************************************
abstract class Tarif
{protected int setzTarif(int x)
{if (x<=8004) {return 0;}
else if(x<=13469) {double y=0.0001*(x-8004);
return(int)((912.17*y+1400)*y);}
else if(x<=52881) {double z=0.0001*(x-13469);
return(int)((228.74*z+2397)*z+1038);}
else if(x<=250730){return(int)(0.42*x-8172);}
else {return(int)(0.45*x-15694);}
}
abstract int hebSteuer(int einkommen);
}
class Grund extends Tarif
{int hebSteuer(int einkommen)
{return setzTarif(einkommen );}
}
class Split extends Tarif
{int hebSteuer(int einkommen)
{return 2*setzTarif(einkommen/2);}
}
class EinkommenSteuer
{public static void main(String[] args) //args[0],args[1] genutzt
{int einkommen=(int)Double.parseDouble(args[0]);
if (args.length==1)
{System.out.printf("%7d",new Grund().hebSteuer(einkommen));}
else
{System.out.printf("%7d",new Split().hebSteuer(einkommen));}
}
}
//****************************************************************
Hierarchie
Command
Output Tarif
------------------------------------
-------
java EinkommenSteuer 100000.00
33828
--
--
java EinkommenSteuer 10000.00
315
java EinkommenSteuer 110000.00 split
29856 Grund Split
Verdient der eine Ehepartner pro Jahr 100000 EUR und der andere
10000 EUR, so ist es für sie günstiger, sich nach Splitting-Tarif
versteuern zu lassen, d.h. ihr gemeinsames Einkommen von 110000
EUR aufzuteilen (spliting) und zweimal 55000 EUR zu versteuern,
als sich einzeln nach Grund-Tarif versteuern zu lassen.
Ein rekursiver Typ ist ein Klassentyp, der in seiner Klassen
-Vereinbarung (9.1.1) Mitglieder vom selben Klassentyp vereinbart.
Unendliche Vereinbarungs-Ketten können dadurch vermieden, dass
nach endlich vielen Schritten die Mitglieder vom selben Klassentyp
mit dem NullZeiger null besetzt werden.
Das folgende Programm ListUpdate ist sowohl ein Beispiel für rekursive Typen,
z.B. class List
{final String item; //
----
List suc; //
item
-
suc Rekursion
... } //
----
als auch für rekursive Methoden (7.3),
z.B. void append(final String s) // String s an List anhaegen
{if(suc!=null) {suc.append(s);} // Rekursion
else {suc=new List(s);}
}
z.B. void removeNextEqual(String s) // naechsten String s
{if(suc!=null) // aus List entfernen
if(s.equals(suc.item)) {suc=suc.suc;}
else {suc.removeNextEqual(s);} // Rekursion
}
z.B. void traverse() // alle folgenden String
{out.print(item+"-
"); // item von List drucken
if(suc!=null) {suc.traverse();} // Rekursion
}
Die Mitglieder item und suc einer neu erzeugten List-Instanz
werden zunächst mit dem Vorgabe-Nullwert (DefaultNullValue Syntax
.038) null besetzt. Die Anweisung suc=new List(s); besetzt den
Nachfolger suc mit s. Die Anweisung suc=suc.suc; entfernt den
Nachfolger suc durch Umsetzen auf den Nachfolger suc.suc des Nachfolgers suc.
ConsoleInput ist die in 8.2 beschriebene Klasse mit der Methode
get() zur Keyboard-Eingabe eines String-Werts über die DOS-Console. ConsoleInput.java muss im aktuellen Verzeichnis stehen.
//*********************** ListUpdate.java ************************
// Listenverarbeitung *
// Anhaengen und Entfernen von Gliedern aus einer Liste. *
// ConsoleInput.java muss im aktuellen Verzeichnis stehen. *
// *
//****************************************************************
// java.lang. Object
import static java.lang.System.out; // `System
class List //
----
{final String item; //
item
-
suc
List suc; //
----
List ( String item) {this.item=item;}
void append (final String s)
{if (suc!=null) {suc.append(s);}
else {suc=new List(s);}
}
void removeNextEqual(String s)
{if (suc!=null)
if (s.equals(suc.item)) {suc=suc.suc;}
else {suc.removeNextEqual(s);}
}
void traverse()
{out.print (item+"-
");
if (suc!=null) {suc.traverse();}
}
}
class ListUpdate extends ConsoleInput
{public static void main(String[] args) // args ungenutzt
{List first=null;
String s;
for(;;)
{out.print("\r\nString(+app,-rem,#break): ");
s=get();
if (s.startsWith("#")) {break;}
if (s.startsWith("+"))
{ s=s.substring(1);
if (first==null) {first=new List(s);}
else {first.append(s);}
}
if(s.startsWith("-"))
{ s=s.substring(1);
if (first!=null)
if (s.equals(first.item)) {first=first.suc;}
else {first.removeNextEqual(s);}
}
if (first!=null) {first.traverse();}
}
}
}
//****************************************************************
Output
Input
----------------------------------
-------
String(+app,-rem,#break):
+Hansi
Hansi-
String(+app,-rem,#break):
+Lutz
Harry-
Lutz-
String(+app,-rem,#break):
+Rosi
Hansi-
Lutz-
Rosi-
String(+app,-rem,#break):
+Harry
Hansi-
Lutz-
Rosi-
Harry-
String(+app,-rem,#break):
-Hansi
Lutz-
Rosi-
Harry-
String(+app,-rem,#break):
#
9.1.6 Aufzählung (Enumeration)
Eine Aufzählungsvereinbarung (EnumDeclaration) ist nach Syntaxdiagramm 094 von der Form
094: EnumDeclaration AufzählungsVereinbarung
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
H
--------------------
H
H
---------------
H
H -
-
ClassModifier
-
H
H
---------------
H
H
H
H
Enum-
------- ,
------
H
H
-----------
-------------
H
H
-
enum -
Identifer
-
-
implements -

InterfaceType
-
H
H
-----------
-------------
H
H
H
H
--------------------------
--------------------------------
H
H
E n u m B o d y H
H
-----------
H
H
---- ,
----
-
, -
-
ClassBody-
-
H
H
--------
Declaration
H
H
Enum-
-----------
H
H
-
{ 



Constant
-

-----
-
;
---------------

-
} -
H
H
--------
H
H
H
H
----------------
-----------------------
H
H H
H A ClassModifier for an Enum- Ein KlassenModifizierer einer H
H Declaration is implicitely fi- AufzählungsVereinbarung ist H
H nal (do'nt write) and must not implizit final (nicht schrei- H
H be abstract. ben) und darf nicht abstract H
H sein. H
H An EnumType must not be expli- Ein AufzählungsTyp darf nicht H
H citely instantiated. explizit instantiiert werden. H
H H
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
z.B. (siehe Tage unten)
enum Tag{MO,DI,MI,DO,FR,SA,SO}
Im folgenden Programm Tage wird eine enum-Klasse enum Tag mit
num-Konstanten MO,DI,MI,DO,FR,SA,SO vereinbart.
/*************************** Tage.java ***************************
/ enum-Klasse Tag mit enum-Konstanten MO,DI,MI,DO,FR,SA,SO *
/*****************************************************************
/ java.lang. Object
/ java.lang. ||`Enum<E>
mport static java.util.EnumSet.range; // |`EnumSet<E>
mport static java.lang.System .out; // `System
num Tag {MO,DI,MI,DO,FR,SA,SO} // enum-Klasse
lass Tage
{public static void main(String[] args) // args ungenutzt
{out.print ("Wochentage ");
for(Tag each:Tag.values()) // Methode values()
{out.print (each+" ");
}
out.println ();
out.println ("blauer Tag "
+Tag.valueOf("MO")); // Methode valueOf(String)
out.print ("Arbeitstage ");
for(Tag each:range(Tag.MO,Tag.FR)) // Methode range(E,E)
{out.print (each+(each==Tag.MO?"=blau ":" ")); // Operator ==
}
out.println ();
out.print ("S=Feiertage ");
for(Tag each:Tag.values()) // Methode values()
{out.print ((each.name().charAt(0)=='S'?each+" ":"")); // Enum
}
out.println ();
}
}
//****************************************************************
Output
--------------------------------
Wochentage MO DI MI DO FR SA SO
blauer Tag MO
Arbeitstage MO=blau DI MI DO FR
S=Feiertage SA SO
Java unterteilt Klassen syntaktisch (Syntax.101) in normale Klassen (NormalClassDeclaration Syntax.091, 9.1.1) und enum-Klassen
(EnumDeclaration, siehe oben, Syntax.094).
Im Unterschied zu normalen Klassen werden enum-Klassen bei ihrer
Vereinbarung implizit zu enum-Objekten instantiiert und können
nicht explizit als benannte Objekte oder namenlose new-Objekte instantiiert werden.
Zu jeder enum-Klasse E sind u.a. definiert (siehe Beispiel Tage
oben):
der Operator ==
zur Prüfung der Gleichheit von enum-Konstanten,
die Methode public static E valueOf(String name) der Klasse E
zur Bestimmung der enum-Konstante von E mit Namen name,
die Methode public static E[] values() der Klasse E
zur Erzeugung der Reihung der enum-Konstanten von E,
die Superklasse Enum<E> der enum-Klasse E im Paket java.lang
(siehe Java Dokumentation), u.a. mit der Methode name()
zur Konvertierung einer enum-Konstanten in einen String und
die Superklasse EnumSet<E> der enum-Klasse E
im Paket java.util (siehe Java Dokumentation),
u.a. mit der Methode range(E from,E to)
zur Erzeugung einer enum-Klasse mit den
enum-Konstanten im angegebenen Bereich from ... to von E .
Das nachfolgende Programm Muenze zeigt, dass enum-Klassen-Vereinbarungen auch enum-Konstanten mit Argument-Klausel enthalten
können und nachfolgend Datenfelder, Methoden und einen Konstruktor, in den das Argument der enum-Konstante als aktueller Parameter eingesetzt wird.
//************************* Muenze.java **************************
// enum-Klasse Muenze *
// mit enum-Konstanten und zugehoerigen Argument-Klauseln, *
// mit Datenfeld, Methoden und Konstruktor *
//****************************************************************
enum Farbe {AUSSENWEISS_INNENGELB,AUSSENGELB_INNENWEISS,GELB,ROT}
enum Muenze
{ EURO_2(2.00),EURO_1(1.00),// Konstanten
CENT50(0.50),CENT20(0.20),CENT10(0.10),
CENT05(0.05),CENT02(0.02),CENT01(0.01);
private final double wert; // Datenfeld
Muenze(double wert) {this.wert=wert;} // Konstruktor
double wert() {return wert;} // Methode
Farbe farbe(Muenze m) // Methode
{switch(m)
{case EURO_2: return Farbe.AUSSENWEISS_INNENGELB;
case EURO_1: return Farbe.AUSSENGELB_INNENWEISS;
case CENT50:
case CENT20:
case CENT10: return Farbe.GELB;
default: return Farbe.ROT;
}
}
public static void main(String[] args) // Methode
{for(Muenze each:Muenze.values()) //args ungenutzt
{System.out.printf("%1$6s: %2$4.2f EUR %3$s\r\n",
each.name(),each.wert(),each.farbe(each));
} } }
//****************************************************************
Output
--------------------------------------
EURO_2: 2,00 EUR AUSSENWEISS_INNENGELB
EURO_1: 1,00 EUR AUSSENGELB_INNENWEISS
CENT50: 0,50 EUR GELB
CENT20: 0,20 EUR GELB
CENT10: 0,10 EUR GELB
CENT05: 0,05 EUR ROT
CENT02: 0,02 EUR ROT
CENT01: 0,01 EUR ROT
9.2 Schnittstellen (interface, ggf. generisch)
Das Schnittstellenkonzept (InterfaceType) von Java unterstützt
den Entwurf von größeren, in Teamarbeit zu bewältigenden Programmiervorhaben. Die Schnittstellen-Hierarchie verwirklicht wie die
Klassen-Hierarchie (siehe Tierlaute, 9.1.3) das vom strukturierten
Programmieren her bekannte Konzept der "schrittweisen Verfeinerung".
Schnittstellen-Vereinbarungen (9.2.2) können auch Typ-Parameter
enthalten, d.h. generische Schnittstellen vereinbaren, siehe Beispiel Generic 9.2.2.
9.2.1 Schnittstellen als Referenztypen, Mitglieder
Wie schon in Kap. 6 (Reihung, 6.1 ff) ausgeführt, verzichtet man
in Java auf explizite Zeiger ('explicite reference considered
harmful') und führt statt dessen implizit Zeiger, d.h. Referenz
-Typen (ReferenceType Syntax.030,2) ein für Reihungen (6), Klassen
(9.1.1) und Schnittstellen (9.2.1). Ein Zeiger-Zugriff (Referenz)
wird realisiert durch eine Konstruktion aus zwei Speichern, wobei
der erste die Adresse des zweiten Speichers zum Inhalt hat.
Ähnlich wie Klassen (ClassMemberDeclaration Syntax.090) können
auch Schnittstellen (InterfaceMemberDeclaration Syntax.095) Datenfelder, Methoden und innere Klasse oder innere Schnittstellen als
Mitglieder haben, aber im Unterschied zu Klassen gibt es bei
Schnittstellen nur implizit final vereinbarte Datenfelder und nur
implizit abstract vereinbarte Methoden.
9.2.2 Superschnittst. (extends), mehrf. Erbsch. (implements)
Eine normale Schnittstellen-Vereinbarung (NormalInterfaceDecla-
ration) ist nach Syntaxdiagramm 096 von der Form
096: NormalInterfaceDeclaration NormaleSchnittst.Vereinbarung
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
H H
H
------------------------
H
H
-------------------
H
H -
-
InterfaceModifier
-
H
H
-------------------
H
H
Interface- H
H
-----------
---------------------
H
H
-
interface -
Identifer
-
--
TypeParameterClause
-
H
H
-----------
---------------------
H
H
of generic interface
H
H
H
H
-----------------------------
--------------------------
H
H
--------- ,
-------
H
H
---------------
H
H
-
extends -
-
InterfaceType
-
H
H
---------------
H
H
H
H 
--------------------------------
H
H
I n t e r f a c e B o d y H
H
----------------------------------
H
H
----------------------------
H
H
-------
{ -
-
InterfaceMemberDeclaration
--
-
} -
H
H
----------------------------
H
H
------------------------------------
H
H H
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
z.B. (siehe RingForm unten)
interface Left extends Upper {String TEXT="Left"; }
Man überzeuge sich an Hand der Syntaxdiagramme für normale
Klassen-Vereinbarung (Syntax.091, 9.1.1) und normale Schnittstellen - Vereinbarung (Syntax.096, siehe oben) mit K = Klasse und
S=Schnittstelle, dass für
Klassen höchstens extends K implements S1,..,Sn ,
Schnittstellen höchstens extends S1,...,Sn
zulässig ist.
Dass bei Klassen auch die Reihenfolge "extends vor implements"
verbindlich vorgeschrieben wird, ist eine unnötige Überreglementierung.
Eine Klasse kann ihren Bestand erweitern (extends K), d.h. Mitglieder ihrer Superklasse, z.B. vollständig implementierte Methoden, annehmen, und ist in der Lage, Schnittstellen zu implementieren (implements S1,...,Sn),
Eine Schnittstelle kann keine Mitglieder von Klassen, z.B. vollständig implementierte Methoden, annehmen und kann nicht implementieren, sondern kann nur ihren Bestand erweitern (extends S1,...
Sn) durch Annahme von Mitgliedern ihrer Superschnittstellen, z.B.
unvollständig implementierten (abstract) Methoden.
Für Klassen ist nur "einfache Erbschaft" zugelassen (9.1.3),
d.h. zu einer Klasse kann es höchstens eine Superklasse geben.
Mit der Einführung von Schnittstellen wird auch "mehrfache Erbschaft" (multiple inheritance) ermöglicht, d.h. zu einer Schnittstelle kann es mehrere Superschnittstellen geben (extends S1,...,
Sn) und eine Klasse kann mehrere Schnittstellen implementieren
(implements S1,...,Sn).
Im nachfolgenden Programm hat die Klasse MehrfachErbe eine Superklasse (extends) Tante und eine Schnittstelle (implements) Onkel,
d.h. man könnte hier von "mehrfacher Erbschaft" sprechen. Von seiner Tante erbt der MehrfachErbe die vollständig implementierte
Methode nimmEUR(,), d.h. er bekommt 300000,00 EUR, ohne sich anzustrengen. Von seinem Onkel erbt der MehrfachErbe die unvollständig
implementierte Methode fassSelbstvertrauen(), die er selbst implementieren muss (implements), um Selbstvertrauen zu gewinnen.
//*********************** MehrfachErbe.java **********************
// Mehrfach-Erbe erbt (extends) von class Tante bares Geld *
// Mehrfach-Erbe erbt (implements) vom interface Onkel guten Rat *
//****************************************************************
// java.lang . O b j e c t
import java.awt .*; // Component'`Font`Graphics
import java.applet.Applet; // Applet'
class Tante extends Applet
{double EUR=300000.00;
void nimmEUR(Graphics g,int size)
{g.drawString
(String.format(" Tante:\"nimm %9.2f EUR!\"",EUR),0,size);
}
}
interface Onkel
{String Selbstvertrauen="Selbstvertrauen";
void fassSelbstvertrauen(Graphics g); // abstract
}
// Tante Onkel
public class MehrfachErbe extends Tante //
implements Onkel //
--
--
{ //
int size; // MehrfachErbe
public void fassSelbstvertrauen(Graphics g)
{g.drawString(" Onkel:\"fass "+Selbstvertrauen+"!\"",0,2*size);
}
public void paint(Graphics g)
{nimmEUR (g,size);
fassSelbstvertrauen(g);}
public void start() // overrides Applet
{ size=getHeight()/3;
setFont (new Font("SansSerif",Font.BOLD,size));
} // calls paint()
}
//****************************************************************
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" >
<!--***************** MehrfachErbe.html ***********************-->
<meta http-equiv="content-type"
content="text/html;charset=ISO-8859-15" >
<title> MehrfachErbe </title>
<applet code="MehrfachErbe.class" width="636"height="126">
MehrfachErbe
</applet>
<!--***************** width=Hochformat height=3*size **********-->
Fig. 9.2.2: MehrfachErbe Darstellung
Schnittstellen sind implizit abstract, ihre Datenfelder implizit
public static final, ihre Methoden implizit abstract. Die Einschränkung auf abstrakte Methoden unterscheidet Schnittstellen wesentlich von Klassen. Die Einschränkung auf konstante Datenfelder
(final) ist kein wesentlicher Unterschied zu Klassen. Eine
Schnittstellen könnte mit einem Datenfeld String Faust komplett
Goethes "Faust" erfassen. Daher können Schnittstellen auch als
"Datei-Ersatz" in Applets verwendet werden, wo Dateien aus Datenschutz-Gründen nicht zugelassen sind.
Es folgt das Beispiel RingForm mit einer Superschnittstelle
Upper, zwei Upper erweiternden Subschnittstellen Left, Right und
einer alle drei Schnittstellen implementierenden Klasse RingForm.
Daraus ergibt sich eine ringförmig geschlossene Hierarchie, die
entsprechend mit Plot-Graphik auf dem Output ausgegeben wird.
RingForm ist nicht nur ein Beispiel für "mehrfache Erbschaft",
d.h. RingForm erbt von Left, Right und Upper, sondern auch für
"mehrdeutige Erbschaft", d.h. der gleiche Bezeichner Text wird für
verschiedene Datenfelder in Left, Right und Upper verwendet,
und für "mehrpfadige Erbschaft", d.h. RingForm erbt über drei verschiedene Pfade von Upper.
//************************ RingForm.java *************************
// *
// Klasse mit Schnittstellen in ringfoermiger Hierarchie: *
// *
// "mehrfache und mehrdeutige Erbung", *
// d.h. RingForm erbt Text aus Left, Upper, Right, *
// *
// "mehrpfadige Erbung", *
// d.h. RingForm erbt ueber drei Pfade von Upper. *
// *
//****************************************************************
interface Upper {String Text="Upper";}
interface Left extends Upper {String Text="Left" ;}
interface Right extends Upper {String Text="Right";}
class RingForm implements Left,Upper,Right
{
public static void main(String[] args) // args ungenutzt
{System.out.print(" ,--" + Upper.Text+"---," +"\r\n"+
" | " +" | " + " |" +"\r\n"+
Left.Text+ " | " + Right.Text+"\r\n"+
" | " +" | " + " |" +"\r\n"+
" `-" +"RingForm" + "-'" +"\r\n");
}
}
//****************************************************************
Output
--------------
,--Upper---,
| | |
Left | Right
| | |
`-RingForm-'
Das folgende Programm Generic zum Werte-Tausch der Komponenten
eines Paars demonstriert "späte Bindung" nicht nur am Beispiel der
generischen Klasse (9.1) Typ mit Typ-Parameter T, sondern auch an
der generischen Schnittstelle Paar mit Typ-Parameter P, an dem generischen Konstruktor (9.1.2) Typ() mit Typ-Parameter K und der
generischen Methode (7.1) swap() mit Typ-Parameter S.
Typ-Parameter-Klauseln findet man in den Syntaxdiagrammen der
Vereinbarungen von normalen Klassen (Syntax.091), normalen
Schnittstellen (Syntax.096), Konstruktoren (Syntax.087/089) und
Methoden (Syntax.083).
Typ-Argument-Klauseln findet man in den Syntaxdiagrammen der
Erzeugung von Klassen (Syntax.032/092) und des Aufrufs von Konstruktoren (Syntax.088) und Methoden (Syntax.085).
//************************ Generic.java **************************
// *
// Werte-Tausch der Komponenten eines Paars *
// *
// generische Klasse Duo mit Typ-Parameter D *
// *
// generische Schnittstelle Put mit Typ-Parameter P *
// *
// generischer Konstruktor Duo() mit Typ-Parameter K *
// *
// generische Methode swap() mit Typ-Parameter S *
// *
//****************************************************************
interface Put<P> // Typ-Parameter P
{void put(P[] p);
}
class Duo<D> implements Put<D> // Typ-Parameter D
{
public void put(D[] d)
{System.out.print ("{"+d[0]+","+d[1]+"}");}
<S>void swap(S[] s) // Typ-Parameter S
{S memo=s[0];
s[0]=s[1];
s[1]=memo;
}
<K>Duo(K k,D[] d) // Typ-Parameter K
{put (d);
this.<D>swap (d);
System.out.print (" "+k+" ");
put (d);
System.out.println();
}
}
class Generic
{public static void main(String[] args) // args ungenutzt
{
// K D K D
new<String> Duo<Double>("swap",new Double[]{ 2.72 , 3.14 });
new<Character>Duo<String>('>' ,new String[]{"Dick","Doof"});
}
}
//****************************************************************
Output
----------------------------
{2.72,3.14} swap {3.14,2.72}
{Dick,Doof} > {Doof,Dick}
9.2.3 Anmerkungstyp (Annotation)
Eine Anmerkungstyp-Vereinbarung (AnnotationTypeDeclaration) ist
nach Syntaxdiagramm 098 (hier Erweiterung 098° durch Einsetzung
von AnnotationTypeElementDeclaration Syntax.097) von der Form
098°:AnnotationTypeDeclaration AnmerkungsTypVereinbarung
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
H
------------------------
H
H
-------------------
H
H -
-
InterfaceModifier
-
H
H
-------------------
H
H
Annotation- implicitely extends H
H
-----------
annotation.Annotation H
H
-
@ -
interface -
Identifer
------------------------
H
H
-----------
H
H
-----------------------------------------------------------
H
H
A n n o t a t i o n T y p e B o d y H
H
--------------------------------------------
H
H
A n notation T y p e E lement D eclaration
H
H
------------------------
H
H
-
{ -

---
ConstantFieldDeclaration
------------
-
} -
H
H
------------------------
H
H
-----------------------------
H
H
------------------------
H
H


-
AbstractMethodModifier
-
H
H
------------------------
H
H
--------
Method- empty Formal-
H
H
Annotati
------
Parameter-
H
H
onResult
Identi
Clause
H
H
-
-Type
-
-fier
-
( -
) -
H
H
--------
------
H
H
---------------------------------
H
H
--------------
H
H
-
default -
ElementValue
----
-
; -
H
H
--------------
H
H
inner
H
H
-----------------------------
H
H
---
ClassOrInterfaceDeclaration
-------
H
H
-----------------------------
H
H
----------------------------------------------
H
H H
H An AnnotationResultType is a Ein AnmerkungsErgebnisTyp ist H
H a PrimitiveType or String, ist ein GrundTyp oder Zeichen- H
H Class or Class Invocation, kette, Class bzw.Class Aufruf, H
H EnumType, AnnotationType, or AufzählungsTyp, AnmerkungsTyp H
H an array of one of these oder eine Reihung aus einem H
H types. dieser Typen. H
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
z.B. (siehe MyAnnotation unten)
public // InterfaceModifier
@interface // at-sign Keyword
MyCopyright // AnnotationIdentifier
{ // AnnotationTypeBody Begin
String // ResultType of AnnotationMethod
value // MethodIdentifier
() // empty FormalParameterClause
default "my"; // default ElementValue (Syntax.099)
} // AnnotationTypeBody End
Mehrere AnmerkungsTyp sind (z.T. Compiler-abhängig) vordefiniert:
annotation.Documented
annotation.Inherited
annotation.Retention
annotation.Target
Nach Konvention ist value der Methoden-Bezeichner in einem Einzel-Element.
Eine Anmerkung (Annotation) ist nach Syntaxdiagramm 100 von der
Form
100: Annotation Anmerkung
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
H AnnotationType- H
H
------
H
H -
@ -
Name
-
H
H
------
H
H
---------------
H
H
E l e m e n t V a l u e P a i r L i s t H
H
----------------- ,
------------------
H
H
E l e m e n t V a l u e P a i r
H
H
Element-
H
H
------------
--------------
H
H
-
( -
-


Identifier
-
= -
ElementValue
-

-
) -
H
H
------------
--------------
H
H
S i n g l e E l e m e n t
H
H
--------------
H
H
------------------------
ElementValue
---
H
H
--------------
H
H
E m p t y E l e m e n t
H
H
--------------------------------------------
H
H
M a r k e r A n n o t a t i o n
H
H
---------------------------------------------------------
-
H
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
z.B. (siehe MyAnnotation unten)
@MyCopyright // at-sign AnnotationTypeName
( // ElementClause Begin
"MyMethod" // ElementValue (Syntax.099) of SingleElement
) // ElementClause End
Im folgenden ersten Beispiel MyAnnotation werden eine Klasse,
ein Konstruktor, ein Datenfeld und eine Methode mit einer selbstdefinierten Anmerkung mit Element-Klausel @MyCopyright("...")
versehen.
//********************* MyAnnotation.java ************************
// Meine Klasse, Konstruktor, Datenfeld und Methode werden mit *
// selbstdefinierten Anmerkungen @MyCopyright("...") versehen. *
// MyCopyright.java muss im aktuellen Verzeichnis stehen. *
//****************************************************************
@MyCopyright(/*"my" default*/) class MyClass
{@MyCopyright("myConstructor") MyClass() {};
@MyCopyright("myField") String myField="alles mein!";
@MyCopyright("myMethod") void myMethod(String s)
{System.out.println(s);}
}
class MyAnnotation
{public static void main(String[] args) // args ungenutzt
{new MyClass().myMethod(new MyClass().myField);}
}
//****************************************************************
Output
-----------
alles mein!
Der benutzerdefinierte Anmerkungstyp public @interface
MyCopyright muss vom Benutzer in einer Datei mit Namen
MyCopyright.java im Paket bzw. im aktuellen Verzeichnis vereinbart
werden.
//********************* MyCopyright.java *************************
// Anmerkungstyp-Vereinbarung fuer MyAnnotation.java *
//****************************************************************
public @interface MyCopyright
{String value() default "my";
}
//****************************************************************
Im folgenden zweiten Beispiel PredefinedAnnotation werden eine
Klasse, ein Konstruktor, ein Datenfeld und eine Methode mit der
vordefinierten Anmerkung @Deprecated versehen.
//****************** PredefinedAnnotation.java *******************
// Nicht empfohlene Klasse, Konstruktor, Datenfeld und Methode *
// werden mit vordefinierter Anmerkung @Deprecated versehen. *
//****************************************************************
@Deprecated class DeprClass
{@Deprecated DeprClass() {};
@Deprecated String deprField="alles nicht empfohlen!";
@Deprecated void deprMethod(String s)
{System.out.println(s);}
}
class PredefinedAnnotation
{public static void main(String[] args) // args ungenutzt
{new DeprClass().deprMethod(new DeprClass().deprField);}
}
//****************************************************************
Command
Output
---------------------------------
---------------------------
javac PredefinedAnnotation
Recompile with
-Xlint:deprecation ..
---------------------------------
---------------------------
javac -Xlint:
..
deprecation PredefinedAnnotation
DeprClass .. deprecated ..
..Typ() ..deprecated..
DeprClass().. deprecated ..
..Typ() ..deprecated..
deprField .. deprecated ..
deprMethod .. deprecated ..
warnings ..
---------------------------------
---------------------------
java PredefinedAnnotation
alles nicht empfohlen!
Der vordefinierte Anmerkungstyp public @interface Deprecated
(siehe AnnotationTypeDeclaration Syntax.098, oben) steht im Paket
java.lang. @Deprecated ist eine MarkerAnnotation (siehe Annotation
oben, Syntax.100) d.h. ohne Element-Klausel.
Anmerkungstypen sind Schnittstellen. Jeder Anmerkungstyp ist
Subschnittstelle der Superschnittstelle Annotation aus dem Paket
java.lang.annotation.
Anmerkungstypen werden verwendet, um Vereinbarungen mit Anmerkungen zu versehen, z.B. Copyright (siehe oben Beispiel MyAnnotation)
in Form einer Anmerkung, die bei der Übersetzung des Programms mit
in die class-Datei aufgenomen wird. Im Gegensatz dazu würde
Copyright in Form von Kommentar bei der Übersetzung des Programms
nicht mit in die class-Datei aufgenommen werden. Anmerkungen bieten Vorteile für die Gewinung von Meta-Information über Java-Prome, dürfen aber die Semantik von Java-Programmen nicht beeinflussen.
zu Frage
abdeckbare Antwort
---------------------------------------------
--------------------
9.1.1 Kann ein Mitglied einer Klasse den
ja
gleichen Bezeichner haben wie ein Ob-
jekt dieser Klasse?
9.1.2 Welche der 4 Vereinbarungen in K ist
eine Methode, welche ein Konstruktor?
class K
{
void P(int i){System.out.print(1);}
Methode (Prozedur)
int F(int i){return 2 ;}
Methode (Funktion)
public K() {System.out.print(3);}
Konstruktor
K(int i){System.out.print(4);}
Konstruktor
}
9.1.3 Wo sind Klassenmitglieder direkt
unter ihrem Bezeichner zugreifbar ,
wenn sie modifiziert sind als
private
in aktueller Klasse
protected
in aktueller Klasse
public
und in Subklassen
9.1.3 Wo sind Klassenmitglieder indirekt
unter ihrem qualifizierten Namen zu-
greifbar,wenn sie modifiziert sind als
private
in aktueller Klasse
protected
im aktuellen Paket
public
im aktuellen und in
Observer-Paketen
9.1.4 Was ist eine abstrakte Klasse?
Klasse mit Modifi-
zierer abstract
bzw. mit abstrakter
Methoden-Vereinba-
rung
9.1.5 Ist das folgende Programm Test mit
javac Test.java übersetzbar und was
Der Übersetzer
liefert der Interpreter java Test ?
javac Test.java
meldet kein.Fehler
class Type
{Type field=new Type();
Der Interpreter
}
java Test meldet
class Test
StackOverflowError
{public static void main(String[]args)
wegen unendlicher
{Type object=new Type();}
new-Erzeugung bei
}
der Type-Rekursion
9.1.6 Die Aufzählung Windrose ist wie
folgt vereinbart
enum Windrose {NORD,OST,SUED,WEST}
Ist Windrose eine Klasse?
ja
Wird Windrose implizit instantiiert?
ja
Kann man zu Windrose Objekte bilden?
nein
Was ist das Resultat von
Windrose.OST==Windrose.valueOf("OST")
true
Windrose.OST==Windrose.values()[1]
true
Windrose.OST.name().equals("OST")
true
9.2.2 Welche der folgenden Vereinbarungen
sind korrekt (C1,C2 seien vereinbarte
Klassen, I1,I2 vereinbarte Schnitt-
stellen)?
class C extends C1 implements I1,I2 {}
ja
interface I extends I1 {}
ja
class C extends C1,C2 {}
nein:2mal extends
interface I implements I1 {}
nein:impl.inkorrekt
9.2.2 Kann man in RingForm.java entweder
----------------
bei "interface Left extends Upper"
oder bei "interface Right extends Upper"
verzichten auf "extends Upper"
ja
und bei "class RingForm implements Left,Upper,Right"
verzichten auf "Upper," ,
ja
----------------
ohne dass der Output sich ändert?
9.2.3 Die Anmerkung OpenSource ist wie
folgt vereinbart
public @interface OpenSource {}
Ist OpenSource eine Schnittstelle?
ja
Ist OpenSource eine MarkerAnnotation?
ja
Wo muss OpenSource vereinbart werden?
in Datei mit Namen
OpenSource.java
Verseh HelloWorld in HelloWorld.java
@OpenSource class
mit der Anmerkung OpenSource
HelloWorld {...}