Einführung
Da Klassen die wohl wichtigsten Bestandteile von Java sind, widme ich
mich in einem extra Workshop noch einmal ausführlich diesem Thema.
Klassen vereinfachen die Programmierung mit Java enorm, bzw. machen Sie
erst möglich.
Was
sind Klassen?
Klassen sind eine Sammlung von Objekten. Jedes dieser Objekte hat bestimmte
Eigenschaften und Fähigkeiten. Einzelne Objekte können wie erwähnt
zu Objektklassen zusammengefasst werden.
Dies möchte ich am Beispiel Biologie verdeutlichen: Es gibt die Oberklasse
der Lebewesen, diese haben z.B. alle einen Stoffwechsel, sind auf Fortpflanzung
aus und bewegen sich; man kann sie jedoch z.B. auch noch in Amphibien,
Säugetiere oder Spinnentiere einklassieren. Diese Objekte haben alle
die gleichen Eigenschaften wie die Oberklasse Lebewesen. Jedoch noch spezifische
Eigenschaften, so sind Säugetiere z.B. Plazentatiere (außer
Schnabeltier).
Genauso ist es auch in Java. Klassen sind also Schablonen, ein exakt definierter
Bauplan. Von diesem Bauplan werden Kopien erstellt. Diese Kopien haben
alle Eigenschaften und Fähigkeiten der Klasse und können noch
zusätzliche Eigenschaften und Fähigkeiten haben.
In Java bezeichnet man die Eigenschaften als Attribute und die Fähigkeiten
als Methoden. Methoden verändern den Zustand eines Objekts durch
Veränderung seiner Attribute. Also kann ein Tierobjekt folgende Attribute
haben: Größe, Farbe, Gewicht. Durch die Methode Nahrungsaufnahme()
kann das Attribut Gewicht verändert werden.
Wie
arbeitet man mit Klassen?
Grundsätzlich könnte man mehrere Klassen in einer Datei speichern,
jedoch darf nur eine davon mit public class gekennzeichnet werden.
Jedoch ist das kein guter Stil, da dadurch die Übersichtlichkeit
- gerade in größeren Projekten - verloren geht. Darum wird
für jede neue Klasse auch eine neue Java-Datei angelegt, diese werden
dann alle in dem gleichen Verzeichnis gespeichert.
Nun aber mal zum Aufbau:
public class Klassenname { //Attribute public Datentyp Name; }
Die Definition dieser öffentlichen (public) Klasse
beginnt mit public class gefolgt von dem Klassennamen. Wie bei
der Hauptklasse (die mit dem main-Block) wird der Rumpf durch { und } begrenzt. Klassennamen sollten immer mit einem
Großbuchstaben beginnen, der Klassenname darf keine Leerzeichen
enthalten und Umlaute sind auch nicht gestattet.
Die Attribute der Klasse werden in deren Rumpf vereinbart. Bei der Deklaration
der Attribute wird der Modifier public vorangestellt, der die
Zugriffsrechte auf die Attribute regelt. Man kann nämlich auch den
Zugriff auf diese Attribute von außerhalb unterbinden (wird Kapselung
genannt).
Will man nun Kopien der Klasse machen, so muss man eine Referenz erzeugen
und den Konstruktor ausrufen. Das hört sich kompliziert an, ist jedoch
nicht schwerer zu verstehen als die Deklaration von Variablen. Denn die
Referenzerzeugung ist nichts anderes als die Vereinbarung der Objektnamen
für die Kopie der Klasse. Mit dem Aufruf des Kontruktors wird dann
der benötigte Speicherplatz reserviert. Das sieht dann so aus:
Klassenname objektname; objektname = new Klassenname();
Danach kann auf die öffentlichen Attribute der Klasse über
den Punktoperator zugegriffen werden. Das sieht dann so aus:
Objektname.attributname = wert;
Methoden
Eine Methode ist ein Programmteil (ähnlich einer Funktion), der in
die Klassendefinition eingebettet ist. Er kann uneingeschränkt auf
alle Daten des Objekts zugreifen. Methoden verringern den Programmieraufwand in dem Programm, da man Methoden
nur einmal (eben in der Klassendefinition) programmieren muss und danach
ganz einfach durch Aufruf ihre Funktion einsetzen kann.
Allgemein sehen Methoden so aus:
[Modifier] Typ Name([Parameter]) { Anweisungen }
Der Modifier ist nicht erforderlich und beinhaltet Schlüsselwörter
wie public. Die Namen von Methoden müssen immer mit einem
Kleinbuchstaben beginnen.
Werden mit der Methode Attribute verändert, so sollte der Methodenname
mit set (z.B. setSeiteL) beginnen.
Sollen Methoden keinen Rückgabewert haben, d.h. hat die Berechnung
kein Ergebnis, dann ist das Schlüsselwort void einzusetzen. Somit
würde die erste Zeile folgendermaßen aussehen:
public void Name()
Beispielprojekt:
Klassen
Da das Thema nun nicht so einfach zu verstehen ist, möchte ich nun
an dieser Stelle mal schnell ein kleines Beispielprojekt einschieben,
an Hand dessen man sich die komplizierten Fakten etwas besser zu Gemüte
führen kann.
Ich möchte ein Programm schreiben, das die Flächen zweier rechteckigen
Grundstücke berechnet. Die Berechnung soll in der Klasse 'Grund'
gemacht werden. Diese Klasse soll so aussehen:
public class Grund { public double seiteL; public double seiteB;
public void showFlaeche() { System.out.println("Die Fläche des Grundstücks beträgt: "+(seiteL * seiteB)+" qm"); } }
Die Klasse 'Haupt' ist die Hauptklasse und soll so aussehen:
import java.io.*;
public class Haupt { public static void main(String argv[]) throws IOException { BufferedReader in = new BufferedReader (new InputStreamReader(System.in)); Grund g1,g2; g1 = new Grund(); g2 = new Grund(); System.out.println(" - Grundstücksvergleich -"); System.out.println(" -------------"); System.out.print("Bitte Länge von Grundstück1 eingeben:"); g1.seiteL = Integer.parseInt(in.readLine()); System.out.print("Bitte Breite von Grundstück1 eingeben:"); g1.seiteB = Integer.parseInt(in.readLine()); System.out.print("Bitte Länge von Grundstück2 eingeben:"); g2.seiteL = Integer.parseInt(in.readLine()); System.out.print("Bitte Breite von Grundstück2 eingeben:"); g2.seiteB = Integer.parseInt(in.readLine()); System.out.println("Grundstück1 " +g1.seiteL+"X"+g1.seiteB); g1.showFlaeche(); System.out.println("Grundstück1 " +g2.seiteL+"X"+g2.seiteB); g2.showFlaeche(); } }
In der Klasse Grund sieht man nun die Methode showFlaeche,
die die Fläche des Grundstücks ausrechnet und ausgibt. In der Haupt-Klasse werden zwei Klassenobjekte g1 und g2 erstellt. Den Attributen
dieser beiden Instanzen werden nun die Eingaben des Benutzers zugewiesen.
Mit dem Aufruf der Methoden werden nun die Fläche berechnet.
So sieht die Arbeit mit Klassen in Java im einfachsten Fall aus. Man könnte
die Anzahl der zu vergleichenden Grundstücke mit sechs Zeilen Code
beliebig erweitern!
Methoden
mit Parameter
Nach dem Beispiel sollte nun jeder den grundsätzlichen Gebrauch von
Klassen verstanden haben. Nun wollen wir diese Kenntnisse noch ausbauen.
Mit Parametern sind Methoden noch variabler einsetzbar. Bei Parametern
handelt es sich um Informationen die an die Methode übergeben werden.
Mit diesen Informationen arbeitet die Methode.
Diese Übergabe erfolgt über die Parameterliste. In der Klassendeklaration
habe ich die Parameterliste schon erwähnt. Hier noch mal zur Erinnerung:
[Modifier] Typ Name([Parameter])
Im einfachsten Fall war die Parameterliste leer (wie
in unserem Beispiel). Bei der Methode main in der Hauptklasse sieht man
jedoch schon einmal eine Parameterliste (diese ist zwingend vorgeschrieben!).
Da der Parametername oft gleichlautend mit dem Attributname gewählt
wird, wird zur besseren Übersichtlichkeit das Schlüsselwort this eingesetzt, das folgendermaßen eingesetzt wird:
this.Parametername = Wert;
Hier wird dem Parameter ein Wert zugewiesen und nicht
dem Originalattribut.
Müssen mehrere Parameter übergeben werden, so wird bei der Definition
der Parameterliste jeder Parameter einzeln definiert (Typ und Name) und
getrennt durch ein Komma aufgelistet. Außerdem ist darauf zu achten,
dass der übergebene Wert kompatibel zum Datentyp des Parameters ist,
sonst kommt es zu einem Fehler!
Methoden überladen
Überladen von Methoden bedeutet, dass es eine Methode mehrfach mit
gleichem Namen, aber mit unterschiedlicher Parameterliste gibt. Auf diese
Weise können benutzerfreundliche Klassen erstellt werden, die z.B.
sowohl Zahlen als auch Strings akzeptieren.
So könnte man folgende Methodenüberladung für unser Beispiel
vornehmen:
public void showFlaeche(double seiteL) { } public void showFlaeche(String seiteL) { this.seiteL(Double.parseDouble(seiteL)); }
Es findet also abhängig vom Datentyp eine automatische
Konvertierung des Datentyps statt. Damit lässt sich ebenfalls einiges
an Programmieraufwand einsparen. So könnte man z.B. die Umwandlung
der Eingaben vom InputStreamReader über diese Methode laufen lassen.
Kapselung
Bis jetzt war es immer möglich direkt auf alle Attribute der Klasse
zuzugreifen. Jedoch ist es nicht immer erwünscht, dass jeder von
außerhalb auf die Attribute der Klasse zugreifen darf. Mit dem Modifier
den ich schon ein paar Mal erwähnt habe, kann man den Zugriff beschränken.
Bis jetzt war der Modifier immer public (=öffentlich), will
man den Zugriff begrenzen, so setzt man den Modifier private (=nicht-öffentlich) ein. Ist als Modifier private gewählt,
dann kann nur noch innerhalb der Klasse auf das private Attribute zugegriffen
werden, von der Hauptklasse (oder jeder anderen Klasse) ist aber der Zugriff
auf dieses Attribut nun nicht mehr möglich.
Zur Erinnerung hier noch mal die Syntax der Attributdefinition:
[public/private] Datentyp Name;
Will man Zugriff von außerhalb, so muss man Methoden
aus der Klasse der mit den privaten Attributen verwenden. Mit diesen Methoden
kann man Fehleingaben abfangen (durch Methodenüberladung).
Das Verfahren, dass man nur noch über Methoden auf die privaten Attribute
zugreifen kann nennt man Kapselung.
Das ganze funktioniert natürlich auch bei Methoden. Normalerweise
sind aber Attribute einer Klasse private und Methoden public,
damit die Attribute geschützt sind und die einmal definierten Methoden
mehrfach verwendet werden können. Intern genutzte Methoden können
jedoch auch auf private gesetzt werden.
Methoden
mit Rückgabewert
Eingangs habe ich schon erwähnt, dass man bei Klassen die keinen
Rückgabewert haben sollen das Schlüsselwort void einsetzen muss.
Zur Erinnerung: public void setSeiteL(seiteL)
Im Umkehrschluss heißt dass, das alle Methoden für gewöhnlich
einen Rückgabewert haben. Rückgabewert heißt, dass das
Ergebnis, dass nach der Abarbeitung der Methode erzielt wird, anstelle
des Aufrufs steht. Also z.B.
summe = getSumme(int Sum1, Sum2)
Anstelle von getSumme steht dann da das Ergebnis. Hieran sieht man auch
schon, dass Methoden, die einen Rückgabewert zurückgeben sollen
mit get (getSumme) beginnen.
Die Syntax der Methodendefinition mit Rückgabetyp sieht so aus:
[Modifier] Typ Name([Parameter]) { Anweisungen return wert; }
Eine Methode mit Rückgabewert muss immer mit der
Anweisung return wert; enden. Nach dieser Anweisung darf keine
weitere Anweisung folgen, da return wert; zum sofortigen Ende
der Methode führt. Der Typ des Rückgabewerts muss zum Datentyp
der Methode passen!
Statische
Methoden und Variablen
Wird eine Instanz von einer Klasse erstellt, so wird die Schablone der
Klasse angewendet, d.h. alle Methoden und Attribute werden kopiert und
es werden individuelle Daten in die Methoden und Attribute der Instanz
eingefügt. Manchmal gibt es aber Attribute und Methoden, die eigentlich
nichts mit den Daten zu tun haben, sie werden nicht mit individuellen
Daten der Instanz gefüllt. Darum macht es eigentlich auch keinen
Sinn, diese in die einzelnen Instanzen zu kopieren, sie bekommen dort
ja keine individuellen Daten. Deshalb belässt man diese Methoden
und Attribute in der Ursprungsklasse. Diese Methoden und Attribute werden
statisch genannt.
Um dieses statische Verhalten umzusetzen, wird vor das Attribut oder die
Methode das Schlüsselwort static gesetzt. Das sieht so aus: public static int ausgabeFormat;
Um auf ein solches Attribut zuzugreifen, müssen wir einfach
den Klassennamen benutzen.
Weiter auf nächster Seite
|