1.6. Modifier und Sichtbarkeit
Klassen, Felder und Methoden können mit Modifiern, z.B. mit
public
, deklariert werden. Ein Modifier legt dabei die
Sichtbarkeit (scope) der Klasse, des Feldes bzw. der Methode fest. Bei entsprechender
Deklaration kann eine Klasse z.B. nur innerhalb eines Pakets genutzt werden (sie
ist nur innerhalb des Pakets "sichtbar"). Die Sichtbarkeit von Methoden und
Feldern, ist im Sinne des Aufrufs einer Methode bzw. eines Feldzugriffs zu verstehen.
In den folgenden Unterabschnitten werden verschiedene Java-Quelltexte vorgestellt,
um die Thematik Sichtbarkeit zu erläutern. Die einzelnen Beispielprogramme
befinden sich dabei in unterschiedlichen Ordnern. Die im Unterordner
p1
stehenden Dateien sollen zu einem Paket p1
gehören. Die Datei Test.java
befindet sich in einem geeigneten
Verzeichnis und soll dem Default-Paket (unnamed package, der Quelltext enthält
keine package
-Deklaration) angehören.
Ausgangsverzeichnis: Test.java
Ausgangsverzeichnis/p1: ScopeExample.java
ScopeExample2.java
ScopeExample3.java
TestP1.java
Bei Klassen wird zwischen Top-Level-Klassen und
eingebetteten Klassen unterschieden. Dabei ist eine eingebettete Klasse eine
Klasse, die innerhalb eines Körpers einer anderen Klasse deklariert ist (siehe
dazu Abschnitt 1.10). Im Gegensatz zu Feldern und Methoden können
Top-Level-Klassen nur mit dem Modifier public
und nicht mit
protected
oder private
deklariert werden.
Top-Level-Klassen können auch ohne einen Modifier deklariert werden. Dies führt
dazu, dass eine Sichtbarkeit einer solchen Klasse nur innerhalb des Pakets besteht.
Die Angabe von public
bei der Deklaration erhöht die Sichtbarkeit
der Klasse. Sie ist dann auch von anderen Paketen aus nutzbar.
Das folgende Beispiel beginnt mit dem Schlüsselwort package
. Die
Klasse ScopeExample
soll dem Paket p1
angehören und
der zugehörige Quelltext steht im Unterverzeichnis p1
. Die
Klasse ist als public
deklariert.
Listing 1.22. ScopeExample.java
. Deklaration der Beispielklasse mit dem
Modifier public
.
package p1;
public class ScopeExample {
int a;
public int b;
protected int c;
private int d;
void method1() {
System.out.println("method1");
}
public void method2() {
System.out.println("method2");
}
protected void method3() {
System.out.println("method3");
}
private void method4() {
System.out.println("method4");
}
}
Der zweite Java-Quelltext zum Beispiel legt die Klasse
ScopeExample2
fest. Sie soll ebenfalls dem Paket p1
zugeordnet werden. Die Klassendeklaration enthält im Gegensatz zum vorhergehenden
Listing keinen Klassenmodifier. Dies führt zu einer eingeschränkten Sichtbarkeit
der Klasse, wie die noch folgenden kurzen Testprogramme zeigen werden.
Listing 1.23. ScopeExample2.java
. Klasse mit eingeschränkter Sichtbarkeit
(Paket-Sichtbarkeit).
package p1;
class ScopeExample2 {
int a;
void method1() {
System.out.println("method21");
}
}
Das kurze Testprogramm Test.java
befindet sich direkt
im gewählten Ausgangsverzeichnis und ist dem Default-Paket zuzuordnen. Innerhalb
der main
-Methode soll versucht werden, Objekte der Klassen
ScopeExample
und ScopeExample2
zu beschaffen. Die
beiden genannten Klassen befinden sich in einem anderen Paket als Test
.
Es zeigt sich, dass die Erzeugung eines Objekts der Klasse ScopeExample2
fehlschlägt. Der Compiler moniert, dass die Klasse ohne Klassenmodifier vom
Default-Paket aus nicht zugänglich ist.
/* Test.java */
public class Test {
public static void main(String[] args) {
p1.ScopeExample se = new p1.ScopeExample();
p1.ScopeExample2 se2 = new p1.ScopeExample2();
// Fehler (Compiler): p1.ScopeExample2 is not public in p1;
// cannot be accessed from outside package
}
}
Im Gegensatz zu Test.java
befindet sich das zweite Testprogramm
TestP1.java
im Unterordner p1
. Die Klassen TestP1
,
ScopeExample
und ScopeExample2
befinden sich also
im gleichen Paket. Die Erzeugung eines Objekts von ScopeExample2
gelingt in diesem Fall ohne Probleme.
/* TestP1.java */
package p1;
public class TestP1 {
public static void main(String[] args) {
ScopeExample se = new ScopeExample();
ScopeExample2 se2 = new ScopeExample2();
}
}
Ausgangsverzeichnis> javac p1/TestP1.java
Ausgangsverzeichnis> java p1.TestP1
1.6.2. Felder und Methoden
Die Programmiersprache Java stellt verschiedene Zugriffsebenen
zur Verfügung, zu denen Methoden und Felder zugeordnet werden können. Zugriffsebenen
können sinnvoll sein, um z.B. Aufrufe von bestimmten Methoden zu verhindern.
Die unterschiedlichen Ebenen werden durch Modifier (public
,
protected
bzw. private
) festgelegt, welche
bei der Deklaration von Feldern bzw. Methoden verwendet werden.
Die anschließenden vier Aussagen zu den Zugriffsebenen sollen anhand später
folgenden Beispielen erläutert werden.
-
Standardzugang.
Bei der Deklaration des Feldes bzw. der Methode wurde keiner der Modifier
public
, protected
bzw. private
verwendet. Der Zugriff auf das Feld bzw. der Aufruf der Methode ist eingeschränkt
(kein Zugang von außerhalb des Pakets: Paket-Sichtbarkeit,
der Zugang innerhalb der selben Klasse ist gewährleistet).
-
Zugang mit public
.
Erfolgt die Deklaration eines Feldes bzw. einer Methode mit public
ist der Zugang des Feldes bzw. der Methode auch von außerhalb des Pakets
möglich (der Zugang innerhalb der selben Klasse ist gewährleistet).
-
Zugang mit protected
.
Der Zugang zu Feldern bzw. Methoden, welcher mit dem Schlüsselwort
protected
eingeschränkt wurde, ist innerhalb des Pakets (Klasse)
gewährleistet und ist außerhalb des Pakets nur unter bestimmten Voraussetzungen
möglich: Die Klasse, innerhalb derer der Zugriff stattfinden soll, befindet sich
in einem anderen Paket und erweitert die Klasse (siehe Abschnitt 1.7.2),
welche das protected
-Feld bzw. die protected
-Methode
enthält.
-
Zugang mit private
.
Felder und Methoden können innerhalb einer Klasse als private
eingestuft werden. Der Zugang derartiger Felder und Methoden ist nur innerhalb
der selben Klasse möglich.
Die nachfolgende Quelldatei Test.java
kann in das gewählte
Ausgangsverzeichnis kopiert werden und soll dem Default-Paket angehören.
Innerhalb der main
-Methode wird zunächst ein Objekt der Klasse
ScopeExample
aus Listing 1.22 beschafft.
Anschließend wird versucht, auf die einzelnen Felder dieser Klasse zuzugreifen.
Auch der Aufruf der Methoden dieser sich im Paket p1
befindlichen
Klasse führt zu Fehlermeldungen, falls der Zugang der Methoden mit entsprechenden
Modifiern eingeschränkt wurde.
/* Test.java */
public class Test {
public static void main(String[] args) {
p1.ScopeExample se = new p1.ScopeExample();
se.a = 3;
// Fehler (Compiler): a is not public in p1.ScopeExample;
// cannot be accessed from outside package
se.b = 4;
se.c = 5;
// Fehler (Compiler): c has protected access in p1.ScopeExample
se.d = 6;
// Fehler (Compiler): d has private access in p1.ScopeExample
se.method1();
// Fehler (Compiler): method1() is not public in p1.ScopeExample;
// cannot be accessed from outside package
se.method2();
se.method3();
// Fehler (Compiler): method3() has protected access in p1.ScopeExample
se.method4();
// Fehler (Compiler): method4() has private access in p1.ScopeExample
}
}
Die Fehlermeldungen durch den Compiler zeigen, dass ein paketübergreifender
Feldzugriff nur erfolgreich ist, wenn das Feld als public
deklariert
wurde. Ein Methodenaufruf gelingt ebenfalls nur, wenn auch die entsprechende
Methode mit dem Modifier public
versehen wurde. Der Aufruf von
protected
- bzw. private
-Methoden ist nicht erfolgreich.
Auch der paketübergreifende Aufruf von method1
der Klasse
ScopeExample
führt zu einer Fehlermeldung, da die Methode ohne
Modifier angegeben wurde und damit nur Paket-Sichtbarkeit besitzt.
Der Quelltext Test.java
kann geändert werden, um auch
protected
-Zugriffe zu ermöglichen. Dazu kann die Klasse
Test
des Default-Pakets die Klasse ScopeExample
des
Pakets p1
ableiten (siehe Abschnitt 1.7.2).
Innerhalb der statischen Hauptmethode kann nun eine Objekt von Test
erzeugt werden und damit kann auf das protected
-Feld c
zugegriffen werden. Auch der Aufruf der protected
-Methode ist
nun erfolgreich.
/* Test.java */
public class Test extends p1.ScopeExample {
public static void main(String[] args) {
Test t = new Test();
t.c = 5;
t.method3();
}
}
Mit Test.java
wurde der Feldzugriff bzw. Methodenaufruf
paketübergreifend durchgeführt. Mit Listing TestP1.java
sollen
die Zugriffe bzw. Aufrufe innerhalb des Pakets erfolgen. Dazu kann die Datei
TestP1.java
im Unterverzeichnis p1
erstellt werden.
Das Testprogramm beginnt mit dem Schlüsselwort package
und der
folgenden Angabe p1
. Die Klassen TestP1
und
ScopeExample
gehören nun dem selben Paket an. Alle Feldzugriffe sind
bis auf das private
-Feld erfolgreich. Auch bei den
Methodenaufrufen ist nur der Aufruf der private
-Methode erfolglos.
/* TestP1.java */
package p1;
public class TestP1 {
public static void main(String[] args) {
ScopeExample se = new ScopeExample();
se.a = 3;
se.b = 4;
se.c = 5;
se.d = 6;
// Fehler (Compiler): d has private access in p1.ScopeExample
se.method1();
se.method2();
se.method3();
se.method4();
// Fehler (Compiler): method4() has private access in p1.ScopeExample
}
}
Auf private Felder und Methoden kann innerhalb derselben Klassen
zugegriffen werden. Die Deklaration von method2
aus
Listing 1.22 kann ergänzt werden, um z.B. die private
Methode method4
aufzurufen.
public void method2() {
System.out.println("method2");
method4();
}
Ebenso wie bei Methoden können Konstruktoren mit den
Modifiern public
, protected
oder private
deklariert werden. Auch eine Deklaration eines Konstruktors ohne die Angabe
eines der genannten Modifier ist zulässig. Das Beispiel ScopeExample3.java
deklariert insgesamt vier Konstruktoren mit unterschiedlichen Zugangsberechtigungen.
Die Klasse ScopeExample3
soll dem Paket p1
angehören.
Listing 1.24. ScopeExample3.java
. Konstruktoren mit unterschiedlichen
Zugangsberechtigungen.
package p1;
public class ScopeExample3 {
int a;
int b;
int c;
int d;
ScopeExample3(int a) {
this.a = a;
}
public ScopeExample3(int a, int b) {
this.a = a;
this.b = b;
}
protected ScopeExample3(int a, int b, int c) {
this.a = a;
this.b = b;
this.c = c;
}
private ScopeExample3(int a, int b, int c, int d) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
public static void main(String[] args) {
ScopeExample3 se = new ScopeExample3(3, 4, 5, 6);
}
}
Test
soll im Gegensatz zu ScopeExampl3
dem
Default-Paket zugeordnet sein. Innerhalb der main
-Methode des
Testprogramms soll ein Objekt der Klasse ScopeExample3
erzeugt
werden. Dazu werden die unterschiedlichen Konstruktoren mit unterschiedlichen
Zugangsberechtigungen aufgerufen. Es zeigt sich, dass eine paketübergreifende
Objekterzeugung nur mit dem public
-Konstruktor gelingt.
/* Test.java */
public class Test {
public static void main(String[] args) {
p1.ScopeExample3 se = new p1.ScopeExample3(3);
// Fehler (Compiler): cannot find constructor ScopeExample3(int)
p1.ScopeExample3 se2 = new p1.ScopeExample3(3, 4);
p1.ScopeExample3 se3 = new p1.ScopeExample3(3, 4, 5);
// Fehler (Compiler): cannot find constructor ScopeExample3(int,int,int)
p1.ScopeExample3 se4 = new p1.ScopeExample3(3, 4, 5, 6);
// Fehler (Compiler): ScopeExample3(int,int,int,int) has private access
// in p1.ScopeExample3
}
}
Die einzelnen Konstruktoraufrufe sollen nun innerhalb desselben Pakets
erfolgen. Dazu kann die Datei TestP1.java
erstellt werden und in
das Unterverzeichnis p1
kopiert werden. Bis auf den
private
-Konstruktor mit vier Parametern können nun alle
Konstruktoren zur Objekterzeugung genutzt werden.
Der Aufruf des private
-Konstruktors ist hingegen innerhalb
der Hauptmethode aus Listing 1.24 erfolgreich.
/* TestP1.java */
package p1;
public class TestP1 {
public static void main(String[] args) {
ScopeExample3 se = new ScopeExample3(3);
ScopeExample3 se2 = new ScopeExample3(3, 4);
ScopeExample3 se3 = new ScopeExample3(3, 4, 5);
ScopeExample3 se4 = new ScopeExample3(3, 4, 5, 6);
// Fehler (Compiler): ScopeExample3(int,int,int,int) has private access
// in p1.ScopeExample3
}
}