Programmieren in Java: Einführung


Kapitel 5: Zugriffsattribute


Inhaltsverzeichnis

Dieses Buch ist unter einer Creative Commons-Lizenz lizensiert.


5.1 Allgemeines

Schutz vor fremden Klassen

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.


5.2 Zugriffsmatrix

Verschiedene Zugriffsattribute in verschiedenen Welten

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.

Tabelle 5.1. Zugriffsmatrix
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.


5.3 Das Zugriffsattribut private

Zugreifen darf nur die eigene Klasse

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.


5.4 Das Zugriffsattribut package

Klassen im selben Paket gehören zum Freundeskreis

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.


5.5 Das Zugriffsattribut protected

Eltern dürfen alles

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.


5.6 Das Zugriffsattribut public

Jeder darf alles

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.


5.7 Zugriffsattribute und Vererbung

Elternklassen vererben nur teilweise

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.


5.8 Zugriffsattribute und Objekte derselben Klasse

Objekte der gleichen Klasse kennen keine Zugriffsbeschränkung

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.


5.9 Zugriffsattribute und Klassendefinitionen

Öffentliche und nicht-öffentliche Klassen

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.


5.10 Aufgaben

Übung macht den Meister

Sie können die Lösungen zu allen Aufgaben in diesem Buch als ZIP-Datei erwerben.

  1. 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.

  2. 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.

  3. 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.

  4. 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.