Ansammlungen von Klassen und Objekten |
|
In echten Programmen kommen oft viele Klassen und Objekte zusammen. Dieses Kapitel zeigt den Aufbau komplexer Objekte aus einfachen Objekten.
Ein weiterer Aspekt ist die Verwaltung grösserer Mengen von Klassen in packages und die Frage der Dokumentierung.
Wenn wir zurückkommen auf unsere Klasse Mitarbeiter und diese ausstatten wollen mit einem Geburtsdatum, so hätten wir erstens folgende Möglichkeit:
public class Mitarbeiter {
private String nachname;
private double gehalt;
private int gebtag;
private int gebmonat,
private int gebjahr;
}
Im Sinne der OOP ist es aber besser, auf unsere bereits vorhandene Klasse Datum zurückzugreifen und ein Objekt mit Namen gebDatum zu erzeugen, das der Klasse Datum angehört, also eine Instanz dieser Klasse ist:
public class Mitarbeiter {
private String nachname;
private double gehalt;
private Datum gebDatum = new Datum();
}
Dieses Zusammensetzen von Objekten aus anderen Objekten nennt man Composition
Weil das gebDatum ein ganz normales Objekt aus der Klasse Datum ist, kann man es mit get- und set-Methoden verarbeiten:
public void setGebDatum( int t, int m, int j )
public Datum getGebDatum()Oder man geht an das
Mitarbeiter-Objekt mit Namen m1 heran und lässt sich von ihm das Unter-Objekt gebDatum geben, auf das dann dessen Methoden anwendbar sind:
m1.gebDatum.getTag() m1.gebDatum.getMonat() m1.gebDatum.getJahr()Im folgenden Programm sind beide Möglichkeiten realisiert:
public class Mitarbeiter {
private String nachname;
private double gehalt;
private Datum gebDatum = new Datum();
public Mitarbeiter(){}
public Mitarbeiter( String n )
{
nachname = n;
}
public String getNachname() {
return nachname;
}
public void setNachname(String n )
{
nachname = n;
}
public void setGebDatum( int t, int m, int j ){
gebDatum.setTag( t );
gebDatum.setMonat( m );
gebDatum.setJahr( j );
}
public Datum getGebDatum(){
return gebDatum;
}
public static void main(String args[])
{
Mitarbeiter m1 = new Mitarbeiter();
m1.setNachname("Franz");
m1.setGebDatum( 13, 8, 1956 );
System.out.println( m1.getNachname() + " ist geb. am " +
m1.gebDatum.getTag() +"." +
m1.gebDatum.getMonat() +"." +
m1.gebDatum.getJahr() +"." );
Mitarbeiter m2 = new Mitarbeiter( "Franziska" );
m2.gebDatum.setTag( 25 );
m2.gebDatum.setMonat( 11 );
m2.gebDatum.setJahr( 1958 );
System.out.println( m2.getNachname() + " ist geb. am " +
m2.getGebDatum().getTag() +"." +
m2.getGebDatum().getMonat() +"." +
m2.getGebDatum().getJahr() +"." );
}
}
Man beachte, dass hier, wie immer in der OOP, mehrere Objekte m1 und m2 samt ihrer Unter-Objekte ganz unabhängig voneinander funktionieren. Wenn der Konstruktor der Klasse Mitarbeiter aufgerufen wird, dann wird auch jedesmal der Konstruktor der Klasse Datum aufgerufen und ein ganz neues Datum-Objekt für das neue Mitarbeiter-Objekt erzeugt.
Übung:
Man hätte natürlich auch eine neue MitarbeiterMitGebTag-Klasse von der ersten Version ohne Datum ableiten können und ihr nur ein Datums-Objekt nebst passender Methoden zusätzlich geben können; manches andere hätte diese neue Klasse geerbt:
public class MitarbeiterMitGebTag extends Mitarbeiter {
private Datum gebDatum = new Datum();
}
Man vervollständige diesen Ansatz!
Übung:
Man gebe den Mitarbeitern ein Konto!
Manchmal ist es auch sinnvoll, eine Unter-Klasse direkt in der Datei der Haupt-Klasse zu definieren, nämlich dann, wenn die Unter-Klasse so speziell ist, dass sie nur in dieser Haupt-Klasse einsetzbar ist. Innere Klassen sind nie public
public class HauptKlasse{
InnereKlasse ik = new InnereKlasse();
class InnereKlasse{
private void innereMethode(){
System.out.println( "Das ist eine Methode der inneren Klasse" );
}
}
public static void main( String [] args ){
HauptKlasse hk = new HauptKlasse();
hk.ik.innereMethode();
}
}
Dieses Definieren innerer Klassen spielt bei der Programmierung grafischer Benutzer-Oberflächen eine grosse Rolle.
Übung:
Man gebe der ersten einfachen Version von Mitarbeiter ein Datum als innere Klasse!
Die Menge der Klassen macht auf die Dauer eine Einteilung der Klassen nach inhaltlichen Gesichtspunkten erforderlich. Das Handbuch zeigt die typische Einteilung der Klassen in packages:
java.lang, wo die Klasse System untergebracht ist, siehe System.out.println() oder auch die Klasse String
Für packages gilt folgendes:
Die Klassen-Datei muss in einem Ordner abgespeichert werden, der genauso heisst wie das package, package-Name immer mit Kleinbuchstaben. Die Klassen-Datei muss als erste Zeile die package-Anweisung enthalten:
Unsere 4 Fahrzeug-, Pkw-, Sportwagen- und Rennauto-Dateien schieben wir nun in ein Verzeichnis mit Namen fahr (Dieser Name ist frei erfunden). Als erste Zeile kommt nun folgende Anweisung in jede der Dateien:
package fahr;
Beim Kompilieren und Aufrufen der Programme in Packages ist nun folgendermassen vorzugehen:
C:\>\jdk1.3\bin\javac fahr/Fahrzeug.javaC:\>java fahr.Fahrzeug
Der folgende Screenshot zeigt die Situation:
Übung:
Man schiebe alle Dateien, die mit Fahrzeug in Zusammenhang stehen, in das Verzeichnis fahr Man ergänze in allen Dateien die erste Zeile mit der Anweisung
package fahr;
Man kompiliere neu und teste die Klassen.
package -Anweisung. Danach alles neu kompilieren und testen!
Alle Java-Klassen gehören also zu einem Package. Das ist für die Übersichtlichkeit unbedingt erforderlich und es ermöglicht das Dokumentieren mit dem javadoc-Werkzeug.
Die Dokumentation für unser fahr-package wurde automatisch erstellt mit dem JDK-Befehl
C:\>\jdk1.3\bin\javadoc -package fahr
Man beachte, dass in die javadoc-Doku nur public und protected-Elemente aufgenommen werden, weil nur diese für das Weiterverarbeiten der Klassen von Belang sind.
package -Anweisung. Die Doku ist dann neu zu erstellen mit allen vorhandenen packages:
C:\>\jdk1.3\bin\javadoc -package fahr bank allgemein usw usf
Wie wir oben gesehen haben, kann jede Klasse auf andere Klassen zugreifen, wenn diese im selben Verzeichnis stehen. Wenn die Klassen in fremden Verzeichnissen stehen, weil sie fremden packages angehören, dann müssen sie mit der import-Anweisung importiert werden:
import bank.Konto;
oder, falls alle Klassen aus dem package importiert werden sollen:
import bank.*;
Jede Datei beginnt also mit der package-Anweisung, dann kommen die import-Anweisungen, dann erst beginnt die Klassen-Definition, siehe Beispiel.
Eine Besonderheit betrifft das package java.lang: Dieses package wird automatisch in alle Java-Programme importiert und braucht deshalb keine import-Anweisung.