Klassendeklarationen in Java
Bis jetzt hießen alle benannten Klassen in diesem Kurs »Main«. Klassen müssen aber nicht immer nur mit diesem Namen deklariert werden.
Klassen sollten möglichst so benannt werden, daß der Name einer Klasse die Bedeutung dieser Klasse erklärt ohne zu lang zu sein. Obwohl es als guter Stil gilt, für jede Klasse immer wieder einen neuen passenden Namen zu erfinden, wird die Hauptklasse von Programmen in diesem Kurs zur Vereinfachung weiterhin oft »Main« genannt. (Gibt es in einer Entwicklungsumgebung schon eine Datei »Main.java«, so kann der Quelltext dann nämlich einfach in diese Datei kopiert werden, ohne daß dann noch weitere Anpassungen nötig sind.)
Die—etwas vereinfachte—Produktionsregel für eine Klassendeklaration lautet folgendermaßen.
- 〈ClassDeclaration 〉 ::=
- [〈ClassModifiers 〉] "class" 〈Identifier 〉 〈ClassBody 〉.
Dabei legen die Klassenmodifizierer 〈ClassModifiers 〉 Eigenschaften der deklarierten Klasse fest, deren Name nach dem Schlüsselwort »class« als Bezeichner 〈Identifier 〉 festgelegt wird. Darauf folgt der Klassenrumpf 〈ClassBody 〉 mit den in geschweiften Klammern enthaltenen Elementen der Klasse.
Eine Klasse kann zusammengehörige Methoden zusammenfassen. Die Namen von Methoden einer Klasse brauchen nur innerhalb der Klasse eindeutig zu sein.
Main.java
/** Das Lied "Auf einem Baum ein Kuckuck sass" */
final class AufEinemBaumEinKuckuckSass
{ /** Den Refrain des Liedes ausgeben. */
public static void refrainAusgeben()
{ java.lang.System.out.println( "Sim sa la dim, bam ba," );
java.lang.System.out.println( "Sa la du, sa la dim -" ); }
/** Den Text des Liedes ausgeben. */
public static void ausgeben()
{ { java.lang.System.out.println( "Auf einem Baum ein Kuckuck, -" );
AufEinemBaumEinKuckuckSass.refrainAusgeben();
java.lang.System.out.println( "Auf einem Baum ein Kuckuck sass." ); }
{ java.lang.System.out.println( "Da kam ein junger Jaeger, -" );
AufEinemBaumEinKuckuckSass.refrainAusgeben();
java.lang.System.out.println( "Da kam ein junger Jaegersmann." ); }
{ java.lang.System.out.println( "Der schoss den armen Kuckuck, -" );
AufEinemBaumEinKuckuckSass.refrainAusgeben();
java.lang.System.out.println( "Der schoss den armen Kuckuck tot." ); }}}
/** Das Lied "Vogelhochzeit" */
final class Vogelhochzeit
{ /** Den Refrain des Liedes ausgeben. */
public static void refrainAusgeben()
{ java.lang.System.out.println( "Fi-di-ra-la-la, fi-di-ra-la-la, fi-di-ra-la-la-la-la!" ); }
/** Den Text des Liedes ausgeben. */
public static void ausgeben()
{ java.lang.System.out.println( "Ein Vogel wollte Hochzeit machen, in dem gruenen Walde." );
refrainAusgeben();
java.lang.System.out.println( "Die Drossel war der Braeutigam, die Amsel war die Braute." );
refrainAusgeben();
java.lang.System.out.println( "Die Lerche, die Lerche, die fuehrt die Braut zur Kerche." );
refrainAusgeben(); }}
/** Testklient fuer Liedklassen */
public final class Main
{ /** Testklient fuer Liedklassen */
public static void main( final java.lang.String[] args )
{ AufEinemBaumEinKuckuckSass.ausgeben();
Vogelhochzeit.ausgeben(); }}System.out
Auf einem Baum ein Kuckuck, -
Sim sa la dim, bam ba,
Sa la du, sa la dim -
Auf einem Baum ein Kuckuck sass.
Da kam ein junger Jaeger, -
Sim sa la dim, bam ba,
Sa la du, sa la dim -
Da kam ein junger Jaegersmann.
Der schoss den armen Kuckuck, -
Sim sa la dim, bam ba,
Sa la du, sa la dim -
Der schoss den armen Kuckuck tot.
Ein Vogel wollte Hochzeit machen, in dem gruenen Walde.
Fi-di-ra-la-la, fi-di-ra-la-la, fi-di-ra-la-la-la-la!
Die Drossel war der Braeutigam, die Amsel war die Braute.
Fi-di-ra-la-la, fi-di-ra-la-la, fi-di-ra-la-la-la-la!
Die Lerche, die Lerche, die fuehrt die Braut zur Kerche.
Fi-di-ra-la-la, fi-di-ra-la-la, fi-di-ra-la-la-la-la!
In der Methode »main« wird zunächst die Methode »AufEinemBaumEinKuckuckSass.ausgeben« aufgerufen. Man sagt auch, daß der Klasse »AufEinemBaumEinKuckuckSass« die Nachricht »ausgeben()« geschickt werde. Dieselbe Nachricht wird in der nächsten Anweisung an eine andere Klasse geschickt und hat dann auch eine andere Wirkung.
Beim Aufruf einer Methode aus der selben Klasse wie die Klasse, in der sich der Aufruf befindet, ist es nicht nötig, den Namen dieser Klasse vor den Namen der Methode zu schreiben.
Namen von Klassen sollten Hauptwörter (Nomen) oder kurze Nominalphrasen sein, die mit einem großen Buchstaben anfangen und in denen der Anfangsbuchstabe jedes weiteren Wortes groß geschrieben wird und sonst kleine Buchstaben verwendet werden.
In Java darf nur eine öffentliche Klasse pro Übersetzungseinheit (Quelldatei) deklariert werden. Die Klasse mit der Methode »main« muß öffentlich sein, da diese Methode von der Javamaschine, also von außen, aufgerufen werden kann. „Öffentlich“ bedeutet daß solch ein Aufruf einer Methode von einer Entität außerhalb der Übersetzungseinheit möglich ist: Dazu müssen die Klasse und die Methode beide öffentlich sein. Der Name der Datei mit der Übersetzungseinheit muß der Name der öffentlichen Klasse mit der Erweiterung ».java« sein.
Die ersten beiden Klassen sind hier nicht mit »public« gekennzeichnet worden, da nur eine Klasse pro Quelldatei mit »public« gekennzeichnet werden darf. Dies ändert aber nichts am Verhalten dieses Programms.
In der Übersetzungseinheit stehen Klassendeklarationen mit Methodendeklarationen und kleine Klassen oder Methoden, dennoch nennt man—nicht ganz genau—eine Klassendeklaration oder eine Methodendeklaration oft einfach nur eine „Klasse“ oder eine „Methode“, wenn keine Mißverständnisse zu befürchten sind.
Auch vor Klassendeklarationen sollte—wie vor Methodendeklarationen—jeweils ein Dokumentationskommentar stehen, der die deklarierte Entität beschreibt. Im Falle der hier vorgestellten Übersetzungseinheit wird unter normalen Umständen aber nur die öffentliche Methode »main« der öffentlichen Klasse »Liedtest« in die erzeugte Dokumentation aufgenommen, weil nur sie öffentlich ist und damit zur von außen verwendbaren Schnittstelle der Übersetzungseinheit gehört.
Liedtest [Dokumentation]
Class Liedtest: Testklient fuer Liedklassen
main
public static void main( java.lang.String[] args )
Testklient fuer Liedklassen
? Übungsfrage →
Welcher Fehler findet sich in dem folgenden Quelltext?
Main.txt
public final class Main
{public static void m(){ }
public static void main( final java.lang.String[] args )
{ }}}
Übungsaufgaben
- Liedklassen umordnen
- Vertauschen Sie die Reihenfolge der beiden Methodendeklarationen in jeder der beiden ersten Klassendeklarationen. Vertauschen Sie dann die Reihenfolge der beiden ersten Klassendeklarationen.
- Liedklassen erweitern
- Erweitern Sie die Klasse »AufEinemBaumEinKuckuckSass« und die Klasse »Vogelhochzeit« um jeweils eine Methode »nameAusgeben«, die den Namen des jeweiligen Liedes ausgibt. Die schon vorhandenen Methoden dieser beiden Klassen dürfen dabei nicht verändert werden. Rufen Sie diese Methoden in sinnvoller Weise in der Hauptmethode »main« auf.
- Grußklassen deklarieren
- Schreiben Sie eine Übersetzungseinheit mit einer Klasse »Englisch« und einer Klasse »Deutsch«, die jeweils eine Methode »printHello« enthalten. Die Methode »printHello« der Klasse »Englisch« soll eine Begrüßung auf Englisch (z.B., den Text »Hello!«) ausgeben, die Methode »printHello« der Klasse »Deutsch soll eine Begrüßung auf deutsch (z.B., den Text »Hallo!«) ausgeben. Schreiben Sie eine Hauptmethode in einer weiteren öffentlichen Klasse dazu, die den beiden zuvor deklarierten Klassen jeweils die Nachricht »printHello« schickt und so einen Gruß auf deutsch und einen Gruß auf Englisch ausgibt.
Namen von Standardtypen
Die Übersetzungseinheit »Math.java« deklariert einen Typ, dessen Name mit einem Standardtyp aus dem Paket »java.lang« übereinstimmt.
Damit steht der Name »Math« innerhalb dieser Übersetzungseinheit für den darin deklarierten Typ und nicht mehr für die Standardklasse »Math« (also die Klasse »java.lang.Math«).
Die Standardklasse »java.lang.Math« kann unter diesen Umständen dann aber weiterhin mit erreicht werden, wenn sie durch ihren maximal explizierten Bezeichner »java.lang.Math« bezeichnet wird.
Math.java
import java.lang.*;
public final class Math
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( java.lang.Math.random() ); }}
Fehlte die Einschränkung »java.lang« vor dem Methodennamen »Math.random«, so nähme der Java-Übersetzer an, daß eine Methode »random« der aktuellen Klasse "Math" (man beachte die zweite Zeile) aufgerufen werden solle. Es wird aber gar keine solche Methode definiert, so daß die Übersetzungseinheit nicht übersetzt werden kann.
Konsole
F:\r\k\java>javac Math.java
Math.java:4: cannot resolve symbol
symbol : method random ()
location: class Math
{ java.lang.System.out.println( Math.random() ); }}
^
1 error
Um solche Probleme zu vermeiden, sollten Klassennamen, die schon in der Standardbibliothek von Java verwendet werden, nicht umdefiniert werden—insbesondere die Klassennamen aus dem Paket »java.lang« sollten vermieden werden. Falls einem Autor diese Klassennamen nicht alle bekannt sind, sollte wenigstens versucht werden, die Verwendung der schon bekannten Klassennamen zu vermeiden. Dann kann bei eventuellen Problemen noch einmal nachgeforscht werden.
Eine Datei »Math.class« oder »Math.java« im Projektverzeichnis stört unter Umständen alle anderen Klassen im selben Verzeichnis, die unter dem Namen »Math« die Standardklasse »java.lang.Math« erwarten. Zur Beseitigung dadurch verursachter Störungen reicht es nicht, eine eventuell vorhandene Datei »Math.class« alleine zu beseitigen, sondern auch die Datei »Math.java« darf im Projektverzeichnis nicht vorhanden sein, weil diese sonst unter Umständen vom Java -Übersetzer wieder automatisch zu einer Datei »Math.class« übersetzt werden könnte.
Falls mehrere Pakete mit gleichen Typnamen importiert wurden oder ein Typname eines importierten Pakets in der Übersetzungseinheit erneut definiert wurde, ist der Typname allein nicht mehr eindeutig und der Paketname muß weiterhin verwendet werden, um sich auf einen Typ in einem bestimmten Paket zu beziehen.
Prioritäten bei der Namensauflösung
Wie im vorigen Abschnitt berichtet wurde, kann Java annehmen, daß sich der Name »Math« auf eine Klasse »Math« aus dem gerade übersetzten Programm bezieht. Die Namen dieses Programms werden als Namen des sogenannten „namenlosen Pakets“ aufgefaßt. Um zu erreichen, daß der Name »Math« für die Standardklasse »java.lang.Math« und nicht für die Klasse »Math« aus dem namenlosen Paket steht, gibt es zwei Möglichkeit: Entweder das Paket wird mit einem Punkt getrennt davor geschrieben, wie beim Bezeichner »java.lang.Math« oder es wird am Anfang eine sternenlose Importdeklaration, wie die Importdeklaration »import java.lang.Math;« verwendet.
Eine bestirnte Importdeklaration, wie »import java.lang.*;« erfüllt diesen Zweck hingegen nicht.
Wenn Java versucht, einen Klassennamen ohne Paketangabe, wie den Namen »Math« aufzulösen, so verwendet es dabei zuerst die ausdrückliche Angaben des Namens mit seinem Pakete, wie in der Angabe »java.lang.Math«. Dem gleichgestellt, ist die Angabe »Math« ohne Paket, wenn es eine vorangehende sternlose Importdeklaration »import java.lang.Math;« gibt.
Falls solch eine Paketangabe nicht vorliegt, dann wird der Name »Math« im namenlosen Paket gesucht, also beispielsweise in der aktuellen Quelldatei.
Erst wenn der Name auch dort nicht gefunden wird, werden zuletzt die Pakete aus bestirnten Importdeklarationen, wie der Importdeklaration »import java.lang.*;« nach dem Namen durchsucht. Da ein Klassenname aus einem Paket, das auf diese Weise importiert wurde, also durch denselben Namen einer Klasse aus dem namenlosen Paket verdeckt werden kann, empfiehlt es sich zum Bezug auf einen Typen entweder dessen Paket ausdrücklich voranzustellen oder eine sternlose Importdeklaration zu verwenden. Dies ist dann auch eine weitere Begründung für die schon oben genannte folgende Stilregel.
Stilregel für Importdeklarationen
Unter einer bibliothekssicheren Quelldatei verstehen wir eine Quelldatei, die so geschrieben ist, daß sie in größeren Programmen, die längerfristig gewartet werden können sollen, eingesetzt werden kann.
Unter einem ad-hoc Programm verstehen wir das Gegenstück: ein Programm, das aus irgendwelchen Gründen nicht höchsten Qualitätsansprüchen genügen braucht, beispielsweise, weil es nur kurz ist und bald wieder gelöscht werden soll.
ℛ Stilregel Importdeklarationen mit einem Sternchen sollten in bibliothekssicheren Quelldateien vermieden werden.
„Auskommentieren“ von Quelltext
Auch Quelltext kann in einem Kommentar enthalten sein. Der Quelltext wird dann vom Compiler ignoriert. Man nennt ihn auskommentiert.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( 65 ); }}/*
// fruehere Programmversion vom 18. Januar 2014 (hier auskommentiert):
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( 64 ); }}
*/- Was der Compiler davon sieht:
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( 65 ); }}
Jedoch ist es nicht möglich, mit einem traditionellen Kommentar Programme auszukommentieren, die selber traditionelle Kommentare enthalten, da traditionelle Kommentare nicht verschachtelt werden dürfen (weil das erste »*/« den Kommentar immer beendet). Hier muß dann auf Zeilenendkommentare ausgewichen werden:
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( 65 ); }}// /* fruehere Programmversion vom 4. Dezember 2013 */
// public final class Main
// { public static void main( final java.lang.String[] args )
// { java.lang.System.out.println
( 56 ); }}- Was der Compiler davon sieht:
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println
( 65 ); }}