Programmieren in Java: Einführung
Dieses Buch ist unter einer Creative Commons-Lizenz lizensiert.
Von Zugriffsattributen haben Sie bereits häufiger in vorherigen Kapiteln gelesen. Zugriffsattribute können im Zusammenhang mit Klassen, Eigenschaften und Methoden verwendet werden und können außerdem bei der Vererbung eine Rolle spielen.
Sehr viele objektorientierte Sprachen unterstützen Zugriffsattribute. Mit ihnen kann jeweils festgelegt werden, inwiefern eine andere Klasse auf die eigene Klasse, auf eine Eigenschaft oder Methode zugreifen darf - entweder sie darf oder eben nicht.
Die Beispiele in diesem Kapitel bestehen daher aus jeweils zwei Klassen. Die eine Klasse ist wie gewohnt MyApplet
, die vom Browser geladen und angezeigt wird. Die andere Klasse ist diejenige, die Zugriffsattribute intensiv einsetzt und die von MyApplet
verwendet wird, um zu testen, ob MyApplet
auf die verschiedenen Merkmale der anderen Klasse zugreifen darf oder nicht.
Damit die Beispiele im Browser funktionieren, müssen die class
-Dateien der beiden Klassen zum Teil im gleichen Verzeichnis liegen, zum Teil in ganz bestimmten Relationen zueinander stehenden Unterverzeichnissen. Beim Ausführen der Beispiele in diesem Kapitel wird wie bisher auch meistens die class
-Datei von MyApplet
geladen. Die zweite class
-Datei wird vom Browser automatisch geladen, wenn MyApplet
auf sie zugreift.
Java unterstützt folgende vier Zugriffsattribute: private
, package
, protected
und public
. Die Bedeutung eines Zugriffsattributs hängt davon ab, wer eigentlich zugreifen möchte. Das kann die eigene Klasse sein, eine Klasse aus demselben Paket, eine Elternklasse oder aber eine wildfremde Klasse - also eine aus einem anderen Paket.
Je nachdem, wer zugreift, und je nachdem, welches Zugriffsattribut gilt, ist der Zugriff gestattet oder nicht. Folgende Matrix gibt detailliert Auskunft, wer wann Erlaubnis hat zuzugreifen.
Eigene Klasse | Paket-Klasse | Elternklasse | Fremde Klasse | |
---|---|---|---|---|
private | Zugriff erlaubt | |||
package | Zugriff erlaubt | Zugriff erlaubt | ||
protected | Zugriff erlaubt | Zugriff erlaubt | Zugriff erlaubt | |
public | Zugriff erlaubt | Zugriff erlaubt | Zugriff erlaubt | Zugriff erlaubt |
Die Matrix kann ausgehend von Zugriffsattributen oder von Klassen gelesen werden. Beginnen wir mit den Zugriffsattributen.
Das Zugriffsattribut private
erlaubt ausschließlich der eigenen Klasse, auf die entsprechende Eigenschaft oder Methode zuzugreifen. Es handelt sich hierbei also um den stärksten Schutzmechanismus. Andere Klassen - egal, ob Elternklasse, aus dem eigenen Paket oder eine wildfremde Klasse - können nicht auf mit private
geschützte Merkmale zugreifen. Sie wissen gar nicht, dass es derartige Merkmale in der Klasse gibt.
Mit package
geschützte Merkmale können sowohl von der eigenen Klasse als auch von Klassen aus dem eigenen Paket verwendet werden. Eine Elternklasse kann normalerweise nicht auf derartige Merkmale zugreifen - außer sie gehört dem Paket an und ist somit auch eine Paket-Klasse.
Merkmale, die mit protected
geschützt sind, können von der eigenen Klasse und von Klassen aus dem Paket verwendet werden. Zusätzlich gilt, dass Elternklassen auf vererbte protected
-Merkmale in Kindklassen zugreifen können. Es ist hierbei nicht notwendig, dass die Kindklasse im gleichen Paket liegt.
Das Zugriffsattribut public
bedeutet, dass jede beliebige Klasse auf das Merkmal zugreifen darf - von eigener Klasse über Eltern- und Paket-Klassen bis hin zu wildfremden Klassen darf das Merkmal jeder verwenden.
Soweit die Bedeutung der Matrix aus Sicht der Zugriffsattribute. Wie sieht es mit den Klassen aus?
Eine Klasse kann grundsätzlich auf jedes beliebige Merkmal der eigenen Klasse zugreifen - völlig egal, ob das Zugriffsattribut private
, package
, protected
oder public
gesetzt ist.
Eine Paket-Klasse kann auf alle Merkmale einer anderen Klasse im Paket zugreifen, soweit diese nicht mit dem Zugriffsattribut private
geschützt sind.
Elternklassen haben Zugriff auf mit protected
und mit public
geschützte Merkmale ihrer Kindklassen. Auf Merkmale, die mit private
und package
geschützt sind, kann hingegen von der Elternklasse aus nicht zugegriffen werden (außer natürlich die Elternklasse liegt im gleichen Paket wie die Kindklasse und das Zugriffsattribut package
wird verwendet).
Fremde Klassen, die weder Elternklasse einer anderen Klasse sind noch im gleichen Paket liegen, haben nur Zugriff auf Merkmale, die mit public
deklariert sind.
Grundsätzlich gilt wie bereits bei den Gültigkeitsbereichen von Variablen gelernt: Zugriffe sollten so restriktiv wie nur möglich eingestellt werden. Damit werden Eigenschaften und Methoden einer Klasse vor versehentlicher Änderung bzw. vor versehentlichem Aufruf durch andere Klassen geschützt, und es wird Fehlern vorgebeugt.
Im folgenden Beispiel werden zwei Klassen definiert. Die Klasse MyApplet
versucht innerhalb der Methode init()
auf eine Eigenschaft und eine Methode der Klasse MyPrivate
zuzugreifen.
import java.applet.*; public class MyApplet extends Applet { public void init() { MyPrivate Private = new MyPrivate(); Private.Property = 10; Private.Method(); } }
Die Klasse MyPrivate
enthält eine Eigenschaft vom Typ int
und eine leere Methode, die weder Parameter erwartet noch ein Ergebnis zurückliefert. Beide Merkmale sind mit dem Zugriffsattribut private
geschützt.
public class MyPrivate { private int Property; private void Method() { } }
Wenn Sie die beiden Klassen kompilieren, wird der Compiler bei MyApplet
meckern. Die Klasse MyPrivate
kompiliert einwandfrei. Innerhalb von MyApplet
wird jedoch versucht, auf private Merkmale der Klasse MyPrivate
zuzugreifen: Einmal soll der privaten Eigenschaft Property der Wert 10 zugewiesen werden, das andere Mal soll die private Methode Method()
aufgerufen werden. Beides wird vom Compiler mit einer Fehlermeldung quittiert, die Klasse wird nicht kompiliert. Sie können daher obiges Beispiel auch nicht im Browser ausführen.
Das Code-Beispiel wird nun derart abgewandelt, dass die Klasse MyApplet
auf eine Klasse MyPackage
zugreift, in der eine Eigenschaft und eine Methode mit dem Zugriffsattribut package
deklariert ist.
package abc; import java.applet.*; public class MyApplet extends Applet { public void init() { MyPackage Package = new MyPackage(); Package.Property = 10; Package.Method(); } }
Im Folgenden sehen Sie die Definition von MyPackage
. Wenn Sie genau hinsehen, stellen Sie fest, dass vor der Eigenschaft und der Methode überhaupt kein Zugriffsattribut angegeben ist. Fehlt ein Zugriffsattribut, wird automatisch package
verwendet. Kommen Sie auch nicht auf die Idee, das Schlüsselwort package
vor die Variable und Methode zu setzen - der Compiler meldet dann einen Fehler.
public class MyPackage { int Property; void Method() { } }
Wenn Sie obige beide Klassen kompilieren, stellen Sie fest, dass der Compiler die gleiche Fehlermeldung ausspuckt wie in dem Beispiel mit dem Zugriffsattribut private
. Der Grund ist, dass es sich bei MyApplet
und MyPackage
derzeit noch um völlig fremde Klassen handelt. Es ist ja nirgendwo angegeben worden, dass diese beiden Klassen dem gleichen Paket angehören. Fremde Klassen haben jedoch kein Recht, auf mit package
geschützte Merkmale einer anderen Klasse zuzugreifen. Deswegen quittiert der Compiler dies mit einer Fehlermeldung.
Um die Klassen MyApplet
und MyPackage
nun in ein gemeinsames Paket zu legen, muss am Dateianfang package abc;
stehen. Wenn Sie sich die Datei oben ansehen, in der die Klasse MyApplet
definiert wurde, so steht dort bereits package abc;
in der ersten Zeile. Diese Angabe ist nun auch für MyPackage
notwendig.
package abc; public class MyPackage { int Property; void Method() { } }
Auf diese Weise erstellen Sie ein Paket mit dem Namen abc, dem die beiden Klassen MyApplet
und MyPackage
zugeordnet sind. Nun schlägt die Kompilierung nicht mehr fehl, und die Klasse MyApplet
kann auf die mit package
geschützten Merkmale der Klasse MyPackage
zugreifen.
Wenn Sie das Beispiel jedoch im Browser ausprobieren, stellen Sie fest, dass es nicht funktioniert. Der Browser meckert, dass er die Klasse MyApplet
nicht finden kann.
Das Problem ist, dass Sie die Klasse nun einem Paket zugeordnet haben. Sie müssen, damit der Browser die Klasse finden kann, nicht mehr nur den Namen der Klasse in der HTML-Datei angeben, sondern zusätzlich den kompletten Paketnamen. Dies erfolgt auf die bisher gewohnte Schreibweise: Paketname, dann der Punkt, dann der Klassenname. Die HTML-Seite muss also nun wie folgt aussehen.
<html> <head> <title>Programmieren in Java: Einführung</title> </head> <body> <applet code="abc.MyApplet.class" width="200" height="30"> </applet> </body> </html>
Laden Sie die nun modifizierte Webseite in den Browser, funktioniert das Beispiel immer noch nicht. Der Browser meldet sich nun mit dem Fehler, dass er die Klasse abc.MyApplet
nicht finden kann.
Der Grund für diese Fehlermeldung ist, dass Pakete in Verzeichnisnamen aufgelöst werden. Der Browser versucht, in ein Unterverzeichnis namens abc
zu gucken und von dort eine Datei MyApplet.class
zu laden. Sie müssen daher nun ein derartiges Unterverzeichnis anlegen und die Datei MyApplet.class
dort ablegen. Nicht nur MyApplet.class
, sondern auch MyPackage.class
müssen Sie dorthin verschieben, weil ja auch diese Klasse zum Paket abc gehört.
Laden Sie die Webseite nun im Browser - es funktioniert jetzt. Denken Sie daran, wenn Sie Ihre Klassen in Pakete legen, dass diese als Unterverzeichnisse im Dateisystem abgebildet werden müssen. Dies ist auch wichtig, wenn Sie beispielsweise auf Klassen und Pakete von Drittherstellern zugreifen. Sie müssen die Klassen in die richtigen Unterverzeichnisse legen, damit der Browser die Klassen finden und mit ihnen arbeiten kann. Dies gilt im Übrigen nicht nur für Applets, sondern auch für Applications - sonst findet die Java-VM die Klassen nicht.
Wenn Sie Ihre Pakete in eine Hierarchie eingliedern, sie also anderen Paketen unterordnen und ähnlich wie die offizielle Java-Hierarchie einen Baum erstellen, müssen Sie beim Erstellen von Unterverzeichnissen diese ebenfalls verschachteln. Eine Klasse, die beispielsweise in einem Paket abc.xyz abgelegt ist, müsste im Unterverzeichnis xyz
gespeichert werden, was wiederum ein Unterverzeichnis von abc
sein müsste. Letztendlich setzen Sie an allen Stellen innerhalb einer Paketangabe, an denen der Punkt vorkommt, das Trennzeichen für Verzeichnisse.
Das Zugriffsattribut protected
ermöglicht, dass eine Klasse auf derartige Merkmale einer anderen Klasse zugreifen kann, wenn der Zugriff auf Merkmale einer Kindklasse erfolgt. Die Kindklasse darf hierbei auch in einem anderen Paket liegen als die Elternklasse.
Um das Beispiel in Java abzubilden, wird nun eine Klasse MyProtected
von der Klasse MyApplet
abgeleitet. Die Klasse MyApplet
definiert eine Eigenschaft und eine Methode, die beide mit protected
geschützt sind und durch Weitervererbung auch in der Klasse MyProtected
zur Verfügung stehen. Innerhalb der Methode init()
wird versucht, Zugriff auf die mit protected
geschützten und weitervererbten Merkmale der Klasse MyProtected
zu erhalten.
package abc; import java.applet.*; import xyz.*; public class MyApplet extends Applet { protected int Property; protected void Method() { } public void init() { MyProtected Protected = new MyProtected(); Protected.Property = 10; Protected.Method(); } }
Wenn der Browser das Applet startet und die Methode init()
aufruft, wird ein Objekt der Klasse MyProtected
erzeugt. Über die Referenzvariable Protected wird auf die Eigenschaft Property und auf die Methode Method()
zugegriffen.
package xyz; import abc.*; public class MyProtected extends MyApplet { }
Wenn Sie obiges Beispiel kompilieren und im Browser ausführen, stellen Sie fest, dass alles einwandfrei funktioniert. Innerhalb der Klasse MyApplet
wird zwar auf mit protected
geschützte Merkmale wie Property und Method()
aus der Klasse MyProtected
zugegriffen. Nachdem es sich bei MyApplet
jedoch um eine Elternklasse handelt, ist dies ohne Probleme möglich.
Wenn Sie die Definiton der Klasse MyProtected
insofern ändern, als dass es sich hierbei nicht mehr um eine Kindklasse von MyApplet
handelt, und Sie die Merkmale Property und Method()
direkt mit protected
in der Klasse MyProtected
definieren, wird der Compiler meckern: Der Zugriff auf mit protected
geschützte Merkmale ist von fremden Klassen aus nicht erlaubt. Wie die Zugriffsmatrix zeigt müssen die Klassen bei protected
im gleichen Paket liegen oder derart verwandt sein, dass die zugreifende Klasse eine Elternklasse ist.
Auf mit public
definierte Merkmale kann von jedem und allen zugegriffen werden. Im folgenden Beispiel werden zwei Klassen definiert. Obwohl sie in unterschiedlichen Paketen liegen und in keinster Weise verwandt sind, kann die Klasse MyApplet
auf die Merkmale der Klasse MyPublic
zugreifen.
package abc; import java.applet.*; import xyz.*; public class MyApplet extends Applet { public void init() { MyPublic Public = new MyPublic(); Public.Property = 10; Public.Method(); } }
Die Klasse MyPublic
stellt eine Eigenschaft Property und eine Methode Method()
zur Verfügung.
package xyz; public class MyPublic { public int Property; public void Method() { } }
Mit public
definierte Merkmale erlauben also allen Klassen den Zugriff. Probieren Sie obiges Beispiel aus, indem Sie es kompilieren und im Browser ausführen - es funktioniert.
Das Setzen von Zugriffsattributen ist nicht nur entscheidend, um andere Klassen am Zugriff auf bestimmte Merkmale zu hindern. Zugriffsattribute entscheiden auch darüber, ob Merkmale an Kindklassen weitervererbt werden oder nicht.
package abc; import java.applet.*; public class MyApplet extends Applet { private int PrivateProperty; int PackageProperty; protected int ProtectedProperty; public int PublicProperty; private void PrivateMethod() { } void PackageMethod() { } protected void ProtectedMethod() { } public void PublicMethod() { } }
In der Klasse MyApplet
sind vier Eigenschaften und vier Methoden definiert. Jede Eigenschaft und jede Methode ist mit einem anderen Zugriffsattribut definiert.
package xyz; import abc.*; public class MyChild extends MyApplet { public void init() { ProtectedProperty = 10; PublicProperty = 10; ProtectedMethod(); PublicMethod(); } }
Von der Klasse MyApplet
wird die Klasse MyChild
abgeleitet. MyChild
liegt in einem anderen Paket als MyApplet
. Daher werden nur Eigenschaften und Methoden weitervererbt, die mit protected
oder mit public
in der Elternklasse definiert sind.
Um obiges Beispiel-Programm zu testen, kompilieren Sie die Klassen und binden Sie MyChild
in die HTML-Datei ein. Weder der Compiler noch der Browser meckern, wenn Sie in der abgeleiteten Klasse ausschließlich auf Merkmale zugreifen, die in der Elternklasse mit protected
oder mit public
definiert wurden.
package abc; public class MyChild extends MyApplet { public void init() { PackageProperty = 10; ProtectedProperty = 10; PublicProperty = 10; PackageMethod(); ProtectedMethod(); PublicMethod(); } }
Die Klasse MyChild
wird nun in das gleiche Paket verschoben wie die Klasse MyApplet
. Nachdem die beiden Klassen nun im gleichen Paket liegen, werden zusätzlich zu den protected
- und public
-Merkmalen Eigenschaften und Methoden vererbt, die mit package
in der Elternklasse definiert sind. Sie können daher in der Kindklasse auch auf mit package
geschützte Merkmale aus der Elternklasse zugreifen.
Mehr Regeln gilt es bei der Vererbung nicht zu beachten. Die Vererbung von Merkmalen hängt also hauptsächlich davon ab, ob die Klassen im gleichen Paket liegen oder nicht. Es werden dann entweder package
-Merkmale zusätzlich zu protected
- und public
-Merkmalen vererbt oder eben nicht.
Mit private
definierte Merkmale werden nie an andere Klassen weitervererbt.
Bis jetzt haben Sie in diesem Kapitel sehr viel über Zugriffsbeschränkungen zwischen Objekten verschiedener Klassen gelernt. Betrachten Sie abschließend folgendes Beispiel.
import java.applet.*; public class MyApplet extends Applet { private int PrivateProperty; int PackageProperty; protected int ProtectedProperty; public int PublicProperty; private void PrivateMethod() { } void PackageMethod() { } protected void ProtectedMethod() { } public void PublicMethod() { } public void init() { MyApplet MyBuddy = new MyApplet(); MyBuddy.PrivateProperty = 10; MyBuddy.PackageProperty = 10; MyBuddy.ProtectedProperty = 10; MyBuddy.PublicProperty = 10; MyBuddy.PrivateMethod(); MyBuddy.PackageMethod(); MyBuddy.ProtectedMethod(); MyBuddy.PublicMethod(); } }
Innerhalb der Methode init()
wird ein neues Objekt der Klasse MyApplet
erzeugt - also von genau der gleichen Klasse, in der sich die Programmausführung befindet. Nachdem das Objekt erzeugt und der Referenzvariablen MyBuddy zugewiesen wurde, wird versucht, auf alle Eigenschaften und Methoden von MyApplet
zuzugreifen.
Wenn Sie obiges Beispiel-Programm kompilieren, stellen Sie fest, es funktioniert einwandfrei. Auch der Browser macht bei der Ausführung keine Probleme. Objekte derselben Klasse haben also vollen Zugriff auf alle Eigenschaften und Methoden, völlig egal, mit welchen Zugriffsattributen diese definiert sind.
Vielleicht haben Sie bemerkt, dass vor dem Schlüsselwort class
häufig das Zugriffsattribut public
gesetzt wurde. Klassendefinitionen sahen daher in etwa in allen Beispielen wie folgt aus.
public class MyClass { }
Ist in einer Klassendefinition das Schlüsselwort public
angegeben, so handelt es sich um eine öffentliche Klasse. Jede beliebige Klasse kann auf diese Klasse zugreifen und beispielsweise Objekte der Klasse erstellen.
Ist public
in einer Klassendefinition nicht angegeben, so handelt es sich um eine nicht-öffentliche Klasse. Derartige Klassen können nur innerhalb eines Pakets verwendet werden. Klassen außerhalb des Pakets, in dem die nicht-öffentliche Klasse definiert ist, kennen diese Klasse nicht und können beispielsweise auch keine Objekte dieser Klasse erstellen. Ist public
nicht angegeben, wird demnach automatisch das Zugriffsattribut package
verwendet.
Eine Klassendefinition darf das Zugriffsattribut public
enthalten oder eben nicht. Es dürfen keine anderen Zugriffsattribute vor das Schlüsselwort class
gesetzt werden.
Sie können die Lösungen zu allen Aufgaben in diesem Buch als ZIP-Datei erwerben.
Entwickeln Sie eine öffentliche Klasse MyApplet
und leiten Sie sie von java.applet.Applet
ab. Definieren Sie zwei Eigenschaften A und B vom Typ java.lang.String
. Setzen Sie für die Eigenschaft A das Zugriffsattribut public
. Definieren Sie zusätzlich eine Methode test()
, die keinen Rückgabewert besitzt und auch keinen Parameter erwartet. Die Methodendefinition kann leer sein. Die Methode wird mit dem Zugriffsattribut protected
geschützt. Legen Sie die Klasse MyApplet
in ein Paket abc. Kompilieren Sie Ihren Code und erstellen Sie eine class
-Datei.
Entwickeln Sie eine öffentliche Klasse MyBuddy
, die Sie ebenfalls in das Paket abc legen und von der in Aufgabe 1 erstellten Klasse MyApplet
ableiten. Definieren Sie die aus Java-Applets bekannte Standard-Methode start()
und greifen Sie in dieser auf alle vorhanden Merkmale zu, die von der Elternklasse geerbt wurden. Setzen Sie Eigenschaften auf einen beliebigen Wert und rufen Sie Methoden auf, soweit entsprechende Zugriffe erlaubt sind. Kompilieren Sie Ihren Code und erstellen Sie eine class
-Datei.
Greifen Sie auf die in Aufgabe 1 und 2 entwickelten Klassen zurück und legen Sie die Klasse MyBuddy
in ein neues Paket xyz. Schränken Sie ein oder erweitern Sie gegebenenfalls den Zugriff auf geerbte Merkmale der Klasse MyApplet
. Kompilieren Sie Ihren Code und erstellen Sie class
-Dateien.
Erweitern Sie den Code aus Aufgabe 3 insofern, als dass Sie die Klasse MyBuddy
um die Standard-Methode aus Java-Applets zum Zeichnen auf Applet-Oberflächen paint()
erweitern und in dieser den Text "Hallo, Welt!" in das Applet ausgeben. Erstellen Sie dann eine HTML-Seite, die die Klasse MyBuddy
in den Browser lädt. Kompilieren Sie Ihren Code und laden Sie die Webseite in den Browser, um zu testen, ob der Text "Hallo, Welt!" angezeigt wird.
Copyright © 2001-2010 Boris Schäling