Terence Gronowski SCJP - content.schweitzer-online.de€¦ · SCJP Sun Certifi ed Java Programmer....

46
Terence Gronowski Die neuen Java 6-Prüfungen: CX-310-065 und CX-310-066 Gezielte und strukturierte Vorbereitung Deckt alle Prüfungsziele ab Zweite, aktualisierte und erweiterte Auflage Vorbereitung auf die Java 6-Zertifizierung: CX-310-065 / CX-310-066 SCJP Sun Certified Java Programmer

Transcript of Terence Gronowski SCJP - content.schweitzer-online.de€¦ · SCJP Sun Certifi ed Java Programmer....

Terence Gronowski

Die neuen Java 6-Prüfungen: CX-310-065 und CX-310-066

Gezielte und strukturierte Vorbereitung

Deckt alle Prüfungsziele ab

Zweite, aktualisierte und erweiterte Aufl age

Vorbereitung auf die Java 6-Zertifi zierung: CX-310-065 / CX-310-066

SCJP Sun Certifi ed Java Programmer

21

Kapitel 1

Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

Das erste Prüfungsziel »Deklarationen, Initialisierung und Scoping«, aufgeteilt insechs Teilziele, behandelt korrekte Deklarationen von Klassen in allen Formen,Schnittstellen und Aufzählungen (Enums). Damit verbunden ist das Verständnisvon Package- und Importanweisungen sowie die Fähigkeit, alle Typen von Variab-len richtig deklarieren zu können, wobei es in erster Linie nur um formal korrekteDeklarationen geht. Wir werden aber in diesem Kapitel einige Themen eingehen-der behandeln, insbesondere das der Referenzvariablen.

Unter Deklaration versteht man die Beschreibung einer Variablen oder einerKlasse; dabei wird weder ein Objekt kreiert noch Speicherplatz reserviert.

Unter Initialisierung versteht man die Zuweisung von Anfangswerten an Variab-len. Zum Beispiel werden primitive Member-Variablen (int, double etc.) automa-tisch auf 0 bzw. 0.0 initialisiert. Als Initialisierung kann auch die Erstellung(Instanziierung) eines Objekts angesehen werden; damit wird Speicherplatz reser-viert.

Unter Scope versteht man einen Gültigkeitsbereich oder einen Sichtbarkeitsbe-reich von Variablen. In einem Anweisungsblock definierte Variablen sind nurlokal im gleichen Anweisungsblock sichtbar bzw. verwendbar.

Weil Scoping in den Teilprüfungszielen nicht mehr erwähnt wird, folgt nungerade an dieser Stelle ein Beispiel:

Dieses Programm müsste doch 3 ausgeben, weil bei der dritten Iteration der Blockder For-Schleife verlassen wird, oder? Das Programm kompiliert aber nicht, weil

public class Scoping{

public static void main(String[] args){

for (int i=0;i<5;i++){

if (i>2) break;

}

System.out.println(i);

}

}

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

22

das i innerhalb des For-Schleifen-Blocks deklariert wurde und außerhalb diesesBlocks nicht sichtbar ist; das i in System.out.println(i) ist nicht bekannt.

Die Themen dieses Kapitels überschneiden sich zum Teil. Schnittstellen werdenzum Beispiel in mehreren Teilzielen behandelt. Wir gehen jeweils dort darauf ein,wo ein Thema zuerst auftritt, und verweisen später auf das entsprechende voran-gegangene Kapitel.

1.1 Prüfungsziel 1-1

In diesem Prüfungsziel geht es um formal korrekte Deklarationen von Klassen,Schnittstellen, Enums, Package- und Importanweisungen.

1.1.1 Grundstruktur eines Java-Programms

Klassen sind eng mit der Struktur von Java-Programmen verknüpft. Ein Java-Pro-gramm hat folgende Struktur:

Die Elemente der Grundstruktur kommen in vorgegebener Reihenfolge vor:

� Paketdeklaration (optional),

� Importdeklaration (optional),

� Typendeklaration (Klasse oder Interface).

Wichtig

Develop code that declares classes (including abstract and all forms of nestedclasses), interfaces, and enums, and includes the appropriate use of package andimport statements (including static imports).

Entwickeln von Code, der Klassen (einschließlich abstrakter und aller Formenverschachtelter Klassen), Schnittstellen und Enums deklariert und eine kor-rekte Verwendung von Package- und Importanweisungen (einschließlich stati-scher Importe) enthält.

/* Grundlegende Struktur eines Java-Programms (Struktur.java) */

package kapitel1;

import java.io.*;

public class Strukur{

public static void main(String [] args){

System.out.println(”Hallo Welt”);

}

}

Listing 1.1: Grundstruktur eines Java-Programms

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

23

Innerhalb des Klassenkörpers ist die Reihenfolge nicht vorgeschrieben, jedoch hatsich folgende Reihenfolge der Elemente eingebürgert:

� Member-Variablen (auch als Felder bezeichnet)

� Konstruktor

� Methoden (Member-Funktionen)

� Main-Methode

� Instanz-Erzeugung.

Der Übersichtlichkeit halber folgen wir in unseren Beispielen dieser Reihenfolgeder Deklarationen. Wie Sie aber an den Testfragen am Ende des jeweiligen Kapitelssehen werden, können die Elemente des Klassenkörpers in der Prüfung in »wilder«Reihenfolge präsentiert werden. Es braucht dann schon einige Übung, die Ele-mente ordnen zu können, damit sich die Struktur des Programms erkennen lässt.

Falls Ihnen die Elemente des Klassenkörpers nicht ganz geläufig sind, sollten Sieein Grundlagenbuch zu Java konsultieren. Hier eine kurze Erläuterung:

� Membervariablen sind Eigenschaften einer Klasse, z.B. Farbe der Klasse Auto.

� Ein Konstruktor ist eine spezielle Methode mit gleichem Namen wie die Klas-se, aber ohne Rückgabewert. Konstruktoren werden beim Erzeugen eines Ob-jekts ausgeführt.

� Methoden sind Codezusammenfassungen, die eine gewisse Aufgabe überneh-men. Durch das Zusammenfassen des Codes erspart man sich Tipparbeit,denn eine Methode kann mehrmals aufgerufen werden. In anderen, nicht ob-jektorientierten Programmiersprachen nennt man Methoden Funktionen. Me-thoden sind an Objekte gebundene Funktionen. Das Programmieren mithilfevon Funktionen nennt man strukturierte Programmierung.

� Die Main-Methode ist der Einstiegspunkt einer Java-Applikation, also diejenigeMethode, die beim Programmstart zuerst ausgeführt wird.

� Die Instanz-Erzeugung ist die Herstellung eines Objekts aus einer Klasse. DieKlasse ist sozusagen der Bauplan und das Objekt der reale Gegenstand.

1.1.2 Paketdeklaration und Klassenpfad

Die Paketdeklaration ist immer die erste Anweisung (Kommentare und Leerzeilendavor werden ignoriert). Sie zeigt, wo sich die .class-Datei relativ zum Klassenpfadbefindet. Auf sie kann verzichtet werden, falls nur mit einer einzigen Datei imaktuellen Verzeichnis gearbeitet wird. Bei den Beispielen in diesem Buch wird diePaketdeklaration weggelassen – außer für Beispiele, die zur Erklärung eben dieserPaketdeklarationen dienen.

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

24

Falls Sie den Code des obigen Listings alleine mit einer Java-IDE (Integrated Deve-lopment Environment, z.B. ein Editor wie JavaEditor oder Eclipse) zu kompilierenversuchen (javac Struktur.java), geschieht dies ohne ein Meckern des Compilers,sofern der Pfad (Path) auf javac.exe im Betriebssystem gesetzt ist und der Klassen-pfad innerhalb der Entwicklungsumgebung gesetzt ist. Der Klassenpfad ent-spricht dem Wurzelverzeichnis einer Java-Anwendung. Es können mehrereKlassenpfade nebeneinander existieren.

Starten Sie hingegen die erzeugte .class-Datei mit java Struktur per Kommando-zeile, erscheint unter Umständen die Fehlermeldung Exception in thread main,java.lang.NoClassDefFoundError: kapitel1/Struktur. Das deutet darauf hin, dass dieJava Virtual Machine die Datei Struktur.class nicht findet. Funktioniert das Pro-gramm, ist der Klassenpfad schon korrekt gesetzt.

Abb. 1.1: Angabe der Umgebungsvariablen CLASSPATH in Windows

Damit die Klasse gefunden wird, muss der Klassenpfad, der in Windows per Eigen-schaften von Arbeitsplatz >> Reiter Erweitert >> Umgebungsvariablen >> CLASS-PATH, angegeben werden (Vista: Systemsteuerung >> System >> ErweiterteSystemeinstellungen >> Umgebungsvariablen). Der Klassenpfad deklariert wie schonerwähnt das Wurzelverzeichnis eines Java-Programms, d.h. den Ordner an derSpitze der Hierarchie, in dem sich Class-Dateien befinden können. In der obigenAbbildung steht der folgende Eintrag:

.;c:\java-buch;c:\java-buch\mylibrary.jar

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

25

Der Punkt am Anfang der CLASSPATH-Variable bedeutet, dass der Pfad ins aktu-elle Verzeichnis gelegt wird, gefolgt von den weiteren Verzeichnissen, z.B. einemPfad auf eine selbst erstellte jar-Library (siehe weiter unten).

Den Pfad können Sie auch per DOS-Kommandozeile mit folgendem Befehl setzen:

Mehrere Befehle könnten Sie in eine Batch-Datei verpacken (normale Textdateimit der Endung ».bat«) und jeweils vor Gebrauch ausführen, in dem Sie die .bat-Datei doppelklicken. Falls Sie den Klassenpfad unter »Systemvariablen«, also imunteren Feld geändert haben, müssen Sie Ihren PC nach der Änderung neu star-ten. Sie gilt dann für alle Benutzer. Die Änderung beim aktuellen Benutzer, alsobei »Benutzervariablen«, sollte ohne Neustart aktiv werden. Ob der Klassenpfadkorrekt gesetzt wurde, können Sie mit dem DOS-Befehl »set CLASSPATH« über-prüfen, der Ihnen die gesetzten Pfade auflistet.

Wie noch genauer in Kapitel 7 beschrieben wird, kann mit der Kompiler- undInterpreteroption -classpath der Klassenpfad des Systems für die aktuelle Kompi-lierung/Ausführung des Programms überschrieben werden.

In vielen Entwicklungsumgebungen wie z.B. dem Java-Editor kann der Klassen-pfad per Menü eingerichtet werden, ohne dass eine Systemvariable editiert werdenmuss. In einer integrierten Entwicklungsumgebung erübrigt sich meist die Spei-cherung des Klassenpfades im Betriebssystem oder sie wird wie z.B. in Eclipsejedes Mal beim Start neu erfragt. Wir empfehlen hier aber dringend, die Beispieleausschließlich per Kommandozeile zu kompilieren und zu starten: SämtlicheKommandozeilen-Varianten werden in der Prüfung verlangt.

Wenn Sie gerade dabei sind, die Umgebungsvariablen zu bearbeiten, können Sieauch den Path auf das bin-Verzeichnis des JDK setzen, damit javac.exe in jedemOrdner aufgerufen werden kann.

Nehmen wir ein einfaches Szenario: Der Klassenpfad sei der Ordner c:\java-buch.Die Datei Struktur.class muss sich dann in einem Unterordner von c:\java-buchbefinden. Falls sich Struktur.class im Ordner c:\java-buch\kapitel1 befindet, müssenwir package kapitel1 in der Datei Struktur.java deklarieren. Also sucht Java dendeklarierten Paketpfad immer ausgehend vom Klassenpfad, und die Package-Deklaration muss immer dem relativen Pfad entsprechen, in welchem die Java-Datei liegt. Eine Package-Deklaration darf nie den Klassenpfad enthalten, sondernnur jene Pfade, die den Unterordnern des Klassenpfads entsprechen.

Um Verwechslungen zu vermeiden, empfiehlt Sun, Pakete mit dem umgekehrtenDomänennamen zu bezeichnen und eine entsprechende Ordnerstruktur anzule-gen. Nehmen wir an, Ihre Domäne hieße ihredomaene.ch. Entsprechend würde derImportpfad korrekterweise

Set CLASSPATH=.;c:\java-buch;

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

26

lauten, was sich auf den Dateipfad wie folgt abbilden würde, falls der Klassenpfadc:\java-buch lautete:

Hat man eine solche Deklaration in einer Java-Datei, aber noch kein Verzeichnisch\ihredomaene\java-buch\pruefungsziel\kapitel1 erzeugt, erstellt derJava Kompiler diesen Ordner ausgehend vom Klassenpfadverzeichnis automa-tisch, sofern bei der Kompilierung die Option »-d« gewählt wird:

Hallo.class wird dann im Verzeichnis

abgelegt. Befindet man sich im Verzeichnis c:\javaprogs\, reicht auch der Befehl

Die Unterordner gemäß der Paket-Anweisung in Hallo.java werden ausgehendvom aktuellen Pfad automatisch angelegt.

Will man Hallo.class mit Java starten, muss der vollqualifizierte Pfad angegebenwerden:

java ch.ihredomaene.java-buch.pruefungsziel1.kapitel1.Hallo

Achten Sie auf die Punkte im Pfad! Weiteres über die Befehle javac und java findenSie bei Prüfungsziel 7-2.

Hier die wichtigsten Eigenschaften von Paketen und Klassenpfaden:

� Der Klassenpfad entspricht dem Wurzelpfad eines Java-Programms.

� Die in Package-Anweisungen angegebenen Pfade gehen immer relativ vomKlassenpfad aus. Klassenpfad und Package-Anweisung ergänzen sich zum ab-soluten Pfad.

� Steht in einer Java-Datei eine Package-Anweisung, muss sich die Quelldatei imUnterordner gleichen Namens befinden.

� Wird ein Package-Pfad in einer Java-Datei angegeben, ohne dass bereits dieDateistruktur existiert, erzeugt Java die entsprechende Dateistruktur automa-tisch.

package ch.ihredomaene.java-buch.kapitel1

c:\javaprogs\ch\ihredomaene\java-buch\kapitel1\

javac –d c:\java-buch Hallo.java

c:\javaprogs\ch\ihredomaene\java-buch\pruefungsziel1\kapitel1\

javac –d . Hallo.java

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

27

� Befinden sich zwei Klassen im gleichen Verzeichnis unterhalb des Klassenpfa-des, kann sowohl auf eine Package- als auch auf eine Importanweisung ver-zichtet werden.

� Fehlende oder falsche Klassenpfade sind häufige Fehler, die das Ausführen vonProgrammen verhindern. Meist weist die Fehlermeldung Exception in thread„main“ java.lang.NoClassDefFoundError auf einen falsch gesetzten Pfad hin.

Wir gehen auf dieses Thema in Kapitel 7-2 nochmals ein und schauen uns dieKommandozeilenoptionen des Kompilers javac und der Virtuellen Maschine javaan.

1.1.3 Importanweisungen

Es gibt mehrere Möglichkeiten, eine Klasse in einer anderen sichtbar zu machen:

� Befindet sich die Klassendatei im gleichen Paket bzw. im gleichen Verzeichnis,ist sie in einer anderen Klasse sichtbar, ohne dass ein Import notwendig ist.

� Befindet sich die Klasse in einem anderen Paket, kann beim Aufruf einesMembers der Klasse der volle Paketpfad vor den Klassennamen gestellt wer-den. Auf diese Weise ist ebenfalls keine Importanweisung notwendig.

Verwendet man eine Importanweisung, muss man zwischen nicht-statischem undstatischem Import unterscheiden. Ist das zu importierende Member statisch, kannvor die Importanweisung das Schlüsselwort static gesetzt werden. Der Ausdrucklautet dann import static MeineKlasse. Der Aufruf des Members eines statischenImports erfolgt ohne das Voranstellen des Klassennamens.

Ohne statischen Import werden statische Member wie nicht-statische Memberimportiert, wie Sie im nächsten Listing sehen.

Statischer und nicht-statischer Import unterscheidet sich bei der Importanweisungdadurch, dass nicht-statischer Import eine oder mehrere Klassen importiert undstatischer Import ein oder mehrere Member importiert.

Die Syntax einer statischen Importanweisung lautet:

Die Syntax des nicht-statischen Imports lautet dagegen:

Die eckigen Klammern zeigen optionale Elemente und die runden Klammerneine Auswahl zwischen zwei Möglichkeiten.

import static package1[.package2].(Klasse[.Klasse2]).(Statisches-Member | *);

import package1[.package2].(Klasse | *);

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

28

Wir erstellen im Ordner kapitel1 einen Unterordner bibliothek. In diesen Unterord-ner fügen wir die Datei Berechnung.class als unsere persönliche Mathematik-Bibli-othek ein. Die darin enthaltenen Methoden sind nicht-statisch und statisch, damitdeutlichwird, dass es bei einem normalen Import zwischen statischen und nicht-statischen Membern keinen Unterschied gibt:

Wir wollen die Methoden quadriere() und addiere() in einer Klasse verwenden, diesich im Ordner kapitel1 befindet. Wir nennen diese Klasse Aufruf. Da sich die Klas-sen in unterschiedlichen Verzeichnissen befinden, müssen wir die Importanwei-sung oder einen qualifizierten Klassennamen verwenden. Wir importieren hierdas Package »bibliothek« in die Klasse »Aufruf«. Eine statische Methode kann mit-hilfe der Referenzvariablen oder mithilfe des Klassennamens aufgerufen werden(#1 und #2 im folgenden Beispiel).

package kapitel1.bibliothek;

public class Berechnung{

public int quadriere(int i){//nicht-statisch

return i*i;

}

public static int addiere(int i, int j){//statisch

return i+j;

}

}

Listing 1.2: Bibliotheksroutine mit einer nicht-statischen und einer statischen Methode

package kapitel1;

import kapitel1.bibliothek.*; //Import aller Klassen

//import kapitel1.bibliothek.Berechnung; //Import einer einzelnen Klasse

public class Aufruf{

public static void main(String[] args) {

Berechnung b=new Berechnung();//Instanz erzeugen System.out.println("Qua-drierung: "+b.quadriere(2));

System.out.println("Addition: "+b.addiere(2,3));//#1

System.out.println("Addition: "+Berechne.addiere(4,5));//#2

}

}

OutputQuadrierung: 4

Addition: 5

Addition: 9

Listing 1.3: Aufrufende Klasse »Aufruf« mit Import-Anweisung: Der Import von nicht-stati-schen oder statischen Membern erfolgt entweder als mehrere Klassen oder als einzelne Klasse.

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

29

Die Importanweisung import kapitel1.bibliothek.*; importiert alle Klassen diesesPackages. Mit import kabitel1.bibliothek.Berechnung; importieren wir nur genau dieKlasse Berechnung.

Gestartet werden muss Aufruf.class vollqualifiziert, d.h. in der Konsole mitdem Befehl java kapitel1.Aufruf, ansonsten erscheint die Fehlermeldung»NoClassDefFoundError«.

Falls Sie ganz sicher sein wollen, dass die class-Dateien in die richtigen Verzeich-nisse zu stehen kommen, kompilieren Sie am besten wie folgt:

und starten mit

Mit diesen javac-Befehlen wird das Verzeichnis kapitel1/bibliothek automatischunterhalb der Position der .java-Dateien erzeugt. -cp. bedeutet, dass der Klassen-pfad auf das aktuelle Verzeichnis festgelegt wird. -cp. ist überflüssig, falls Sieschon bei der CLASSPATH-Umgebungsvariable einen Punkt gesetzt haben. Wei-teres über die Optionen von java und javac finden Sie im Prüfungsziel 7-2.

In der folgenden Abbildung ist das Verhältnis zwischen Packages und Pfadennochmals veranschaulicht: Package- und Importpfade müssen sich mit dem Klas-senpfad zum absoluten Pfad zur Datei ergänzen.

Abb. 1.2: Package-Struktur im Vergleich mit dem Speicherort im Verzeichnis: Der Klassen-pfad ist der »Wurzelpfad« des Java-Pakets. Klassenpfad und Package-Anweisung ergänzen sich zum absoluten Pfad, aber auch Klassenpfad und Import-Pfad.

Statische Importe sind seit der Version 1.5 möglich (siehe Java-Dokumentationhttp://java.sun.com/j2se/1.5.0/docs/guide/language/static-import.html). Mit stati-

javac –d . –cp . Berechnung.java

javac –d . –cp . Aufruf.java

java kapitel1.Aufruf

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

30

schen Importen können nur einzelne statische Klassen und Schnittstellen impor-tiert werden, aber keine gesamten Packages wie mit dem nicht-statischen Import.Die zu importierten Methoden müssen beim statischen Import statisch deklariertsein.

Der Unterschied der beiden Importmöglichkeiten ist, dass der statische ImportMember importiert, während der nicht-statische Klassen importiert.

Mit dem nicht-statischen Import kann man ganze Packages importieren (importpackage1.*). Man rät zum Import einzelner Klassen, da damit der Kompiliervor-gang beschleunigt wird. Einmal kompiliert, wirkt sich der Import eines gesamtenPackages nicht auf die Performance des Programms aus.

Bis zur Java-Version 1.4 mussten statische Memberfunktionen vollqualifiziert auf-gerufen werden. Mit der Math-Bibliothek musste der Cosinus und die Kreiszahl PImit dem vorangestellten »Math. « aufgerufen werden, Math.cos(Math.PI *

winkel).

Nun kann man sich mit einem statischen Import das vorangestellte Math einspa-ren, also auf die Qualifizierung verzichten.

Dies bietet bei der Programmierung komplexer Formeln grosse Vorteile. Der spe-zifische Import mit der Angabe der zu verwendenden Funktion ist dem pauscha-len Import vorzuziehen, da sonst Unklarheiten über die Herkunft der Formelnentstehen könnten.

Enums, die weiter unten behandelt werden, sind implizit statisch und könnenebenfalls statisch importiert werden.

Shadowing von statischen Membern

Fügt man im obigen Listing mit dem Marker #1 private static double

PI=3.1; ein, gibt das Programm anstatt der Kreiszahl PI 3.14…. nur 3.1 aus!

package kapitel1;

import static java.lang.Math.PI; //spezifischer Import einer Konstante

import static java.lang.System.*; //pauschaler Import von Membern

public class AufrufStaticMath{ #1

//private static double PI=3.1;//vorsicht, Shadowing

public static void main(String[] args){

out.println("Kreiszahl: "+ PI); //bis Anhin: Math.PI und

//System.out.println()

}

}

Listing 1.4: Statischer Import der Math-Bibliothek: Die Qualifizierung mit »Math.« oder »System.« entfällt.

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

31

Wir können einen statischen Import auch selbst programmieren, indem wirunsere Bibliothek Berechnung statisch importieren.

Achten Sie darauf, dass in diesem Falle die Klasse Berechnung im Importpfad ange-geben werden muss, da sonst die Fehlermeldung: »static import only from classesand interfaces« resultiert, d.h. statische Importe sind nur von Klassen und Inter-faces gestattet.

Ein vollqualifizierter Aufruf einer statischen Variable oder Methode funktioniertebenfalls: Im obigen Beispiel ist der Aufruf quadrierung(2) als auchkapitel1.bibliothek.Berechnung.quadrierung(2) korrekt. Import static istin diesem Fall überflüssig.

package kapitel1;

import static kapitel1.bibliothek.Berechnung.*;

import static java.lang.System.*;

//import static java.lang.System.out; //zweite Möglichkeit

public class AufrufStatic{

public static void main(String[] args) {

out.println("Resultat: "+quadrierung(2));//"System" oder "Berechnung"

//nicht nötig/erlaubt

out.println("Resultat: "+kapitel1.bibliothek.Berechnung.quadrierung(2));

}

}

Listing 1.5: Statischer Import einer eigenen Bibliotheksklasse und der Java-Library java.lang.System. Werden Bibliotheksklassen statisch importiert, kann man bei der Verwendung der Bibliotheksroutinen auf die spezifizierenden Klassennamen wie z.B. »System« oder »Berechnung« verzichten. out ist eine statische Variable, deshalb kann die Import-Anweisung auch mit System.out enden.

Hinweis

Mit statischen Importen können nur Member, aber keine Klassen importiert wer-den, und zwar nur statische Member. Die Bibliothek Math ist also eine Klasse.Math.* bedeutet alle Member (Methoden und Variablen) einer Klasse:

import static java.lang.Math.PI; //spezifischer Import eines einzelnen Membersimport static java.lang.System.*; //pauschaler Import von Membern

Ein Spezialfall ist der Import von System.out. out ist eine statische Variable, undneben import static java.lang.System.* kann auch import static java.lang.System.out geschrieben werden:

Aufruf: out.println("Hallo");

println() selbst ist eine nicht-statische Methode und kann nicht statisch impor-tiert werden.

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

32

1.1.4 Import einer jar-Datei

Eine jar-Datei ist eine komprimierte Datei analog zu einer zip-Datei. Hier greifenwir ein wenig vor, weil das Erstellen einer jar-Bibliothek und deren Nutzung inengem Zusammenhang mit dem Import eines Packages steht. Weitere Details fol-gen in Kapitel 7-5. Wir haben die Absicht, Berechnung.class in eine jar-Datei zupacken und später nur auf diese jar-Datei zuzugreifen, wenn wir die Methode qua-drierung() nutzen wollen.

Vorgehensweise

� Für alle Befehle befinden wir uns auf c:\java-buch im Klassenpfad. Wir arbei-ten mit der Kommandozeile.

� Wir verstauen Berechnung.class in einem Archiv namens testarchiv.jar. Dastestarchiv.jar befindet sich im Klassenpfadordner c:\java-buch.

� Konsolenbefehl zur Erstellung des Archivs: jar cfv testarchiv.jar

kapitel1\bibliothek\Berechnung.class. Achten Sie darauf, dass der Pfadein MS-DOS-Pfad mit Backslashes ist. Das c von cfv steht für »create = erzeu-gen«, das f für »file = Dateiname« und v für »verbose = mit Worten«, was be-wirkt, dass Meldungen ausgegeben werden.

� Wir überprüfen den Inhalt von testarchiv.jar: jar tf testarchiv.jar.

� Es sollte folgender Inhalt aufgelistet werden (Verzeichnisse in der jar-Datei):META-INF/

META-INF/MANIFEST.MF

kapitel1/bibliothek/Berechnung.class

Beim nicht-statischen Import werden eine oder mehrere Klassen importiert, egalob die Member statisch sind oder nicht:

import kapitel1.bibliothek.*;

Beim statischen Import entfällt beim Aufruf der Methode die Qualifizierung: Nicht-statischer Aufruf: Math.PIStatischer Aufruf: PI

import kapitel1.bibliothek.Berechnung;

Gefahr beim statischen Import: Statische Importe werden von Member-Metho-den oder Member-Variablen überdeckt, falls Letztere den gleichen Namenbenutzen (Shadowing).

Hinweis (Forts.)

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

33

� Wir benennen Berechnung.class in Berechnung_.class um, damit wir sichersind, dass auf die Berechnung.class in der Archivdatei testarchiv.jar zugegrif-fen wird.

� Der Klassenpfad muss um den Pfad zu testarchiv.jar erweitert werden und lau-tet .;c:\java-buch;c:\java-buch\testarchiv.jar. Der Name des jar-Files muss am Ende des Pfades angegeben werden.

� Der Klassenpfad kann auch einfach per DOS-Konsole konfiguriert werden: setCLASSPATH = .;c:\java-buch;c:\java-buch\testarchiv.jar;. Eine wei-tere Möglichkeit wäre, testarchiv.jar in den Ordner %JAVA_HOME%\lib\extzu kopieren (%JAVA_HOME% ist meistens c:\programme\java). Librarys indiesem Verzeichnis werden ohne Angabe des Klassenpfades gefunden. Sunempfiehlt diese Technik nur zu Testzwecken.

� Im aufrufenden Programm Aufruf.class muss die Library wie folgt importiertwerden: »import kapitel1.bibliothek.*«.

� Der Code für die Bibliothek und den Aufruf lautet:

Bibliothek:

Aufruf:

Die Klasse Aufruf enthält kein Package-Statement, da sie sich im Ordner des Klas-senpfades befindet.

Das Thema Packages, jar-Archive und Konsolenbefehle ist hier noch nichtabschließend behandelt worden. Weitere Informationen finden Sie im Abschnittzu Prüfungsziel 7-5.

package kapitel1.bibliothek;

public class Berechnung {

public static int quadriere(int i){//static für stat. Importe obligatorisch

return i*i;

}

}

import kapitel1.bibliothek.*; //Import aller Klassen von einem Package

public class Aufruf{

public static void main(String[] args){

System.out.println("Resultat: "+Berechnung.quadriere(2));

}

}

Listing 1.6: Aufruf der Klasse Berechnung mit der Methode quadriere() aus der Library testar-chiv.jar. Bei der richtigen Wahl der Pfade ist große Sorgfalt geboten.

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

34

1.1.5 Schnittstellen

Schnittstellen (Interfaces) sind Klassen sehr ähnlich, insbesondere abstraktenKlassen. Ein Interface wird mit dem Bezeichner interface anstatt class deklariert.Alle Methoden eines Interfaces sind immer abstrakt und implizit öffentlich(public), auch wenn das Schlüsselwort public oder abstract fehlt! Eine Methode ineinem Interface darf nie mit einem private- oder static-Modifizierer ergänzt wer-den! Neben Methoden kann ein Interface auch Felder, die immer Konstanten sind,enthalten. Diese sind immer static und final. Member-Variablen eines Interfacessind also immer implizit public, static und final. Vorsicht ist beim Überschreibenvon Variablen/Konstanten aus implementierten Interfaces geboten.

Interfaces definieren, wie ihr Name suggeriert, eine Schnittstelle ohne Implemen-tierung, also nur einen Methodenkopf, aber keinen Methodenkörper. Die Imple-mentierung, also die Programmierung des Methodenkörpers, muss in jenerKlasse erfolgen, welche die Schnittstelle implementiert. Schnittstellen können imGegensatz zu Klassen mehrfach vererbt werden.

Allgemein lautet die Definition eines Interfaces wie folgt:

interface <Name> [extends <anderes_interface><,><weiteres_interface>]{

[<Member-Variablen>]

[<Methoden-Köpfe>]

}

Hinweis

Für Interfaces gelten folgende Regeln:

Ein Interface kann mehrere andere Interfaces erweitern: interface3 extendsinterface1,interface2; (interface3 implements interface1,interface2 gibt es nicht!).

Eine abstrakte Klasse kann ein Interface implementieren, ohne dass sie derenMethoden implementieren muss: abstract class Class1 implements interface1;.

Eine nicht abstrakte Klasse muss die Methoden des Interfaces implementieren.Die zu implementierenden Methoden müssen public sein.

Falls eine Elternklasse schon ein bestimmtes Interface implementiert hat unddie notwendigen Methoden liefert, muss die Kindklasse keine Methoden mehrimplementieren, auch wenn sie diese Schnittstelle explizit implementiert. Esresultiert keine Fehlermeldung, falls das gleiche Interface mehrmals in der Ver-erbungshierarchie implementiert wurde.

Felder in Interfaces sind immer Konstanten und implizit public, final und static.Methoden in Interfaces sind implizit immer abstract und public. Beim Über-schreiben von Methoden ist dies wichtig; die überschreibende Methode mussimmer public sein.

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

35

Konkret lautet die Definition für ein einfaches Beispiel wie folgt:

Die Implementierung des Interfaces:

interface Monster{

void bedrohen();//implizit public, nur Methodenkopf

}

1 class Vampir implements Monster{

2 //class Vampir{

3 private String name;

4 public void bedrohen(){ //Methode public, da im Interface implizit public

5 System.out.println("Zaehne fletschen");

6 }

7 public void setName(String name){

this.name=name;

8 }

9 public String getName(){

10 return this.name;

11 }

12} //Ende Klasse Vampir

13 class BoeserVampir extends Vampir implements Monster {//implements nicht

notwendig

14 //class BoeserVampir extends Vampir {

15 private String name;

16 public void bedrohen(){

17 System.out.println("Reisszaehne fletschen");

18 }

19} //Ende Klasse BoeserVampir

20 public class VampirTest{

21 public static void main(String[] args){

22 Vampir v=new Vampir();

23 v.setName("Lestat");

24 System.out.println(v.getName());

25 v.bedrohen();

26 BoeserVampir bv=new BoeserVampir();

27 bv.bedrohen();

28 }

29 }

OutputLestat

Zaehne fletschen

Reisszaehne fletschen

Listing 1.7: Der BoeseVampir kann normal vom Vampir abgeleitet werden.

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

36

Im obigen Beispiel kann eigentlich auf die Implemantation des Interfaces Mons-ter beim bösen Vampir verzichtet werden. Der »BoeseVampir« kann ganz normalaus dem »Vampir« erweitert (extended, vererbt) werden, der böse Vampir benötigtkeine Implementierung des »Monsters« (Zeile 13 und 14, Listing 1.7). Ebensokann der »Vampir« ohne die Implementierung des Interfaces »Monster« erstelltwerden (Zeile 1 und 2, Listing 1.7).

Eine Schnittstelle bringt also nicht viel, außer dass sie einen Bauplan des Metho-denkopfs vorgibt. Falls man die Schnittstelle in einer Klasse implementiert, istman gezwungen, die vorgegebenen Methoden auszuprogrammieren.

Eine implementierte Schnittstelle gilt immer als Elternklasse der implementieren-den Klasse – es besteht eine sogenannte »ist-ein Beziehung«. Die Möglichkeit,Referenzvariablen aus Schnittstellentypen zu erzeugen, ermöglicht aber weitereAnwendungen. Wir müssen hier ziemlich weit ausholen und uns mit Referenzva-riablen beschäftigen, damit wir dieses Thema verstehen können:

a) Eine Referenzvariable ermöglicht den Zugriff auf die Methoden und Variableneines (dynamischen) Objekts: Beispiel ElternKlasse e = new ElternKlasse(). Links desGleichheitszeichens steht die Referenzvariable, rechts das Objekt. e wird durchdie Instanziierung erzeugt. Die Referenzvariable ist ein Verweis oder ein Zeigerauf das Objekt, das sich auf dem Heap-Speicher des Computers befindet. Auf dasObjekt kann nur mittels der Referenzvariable e zugegriffen werden. Als Vergleicheignet sich ein Fernseher (Objekt), auf den nur mit der Fernbedienung (Referenz-variable) zugegriffen werden kann.

b) Referenzvariablen können zum Fernzugriff auf Daten genutzt werden: Wollenwir von einem Objekt auf ein anderes zugreifen, müssen wir die Referenzvariable

//zu a)

class ElternKlasse {

private String memberVariable;

public ElternKlasse(){

this.memberVariable="Membervariable";

}

public String getMemberVariable(){

return memberVariable;

}

}

public class Test{

public static void main(String[] args){

ElternKlasse e=new ElternKlasse();

System.out.println(e.getMemberVariable());

}

}

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

37

dem Konstruktor des anderen Objekts übergeben und einer Membervariablen desanderen Objekts zuweisen.

c) Die Referenzvariable einer Kindklasse ermöglicht den Zugriff auf alle ererbtenMember-Methoden ihrer Elternklasse sowie ihrer eigenen Klasse. Ausnahme:Falls eine Methode mit gleicher Signatur in der Kindklasse überschrieben wurde,kann nicht mehr auf die ererbte Methode in der Elternklasse zugegriffen werden,außer man wendet das reservierte Wort super an. Unter der Signatur einer

//zu b)

class ErsteKlasse {

private String memberVariable;

public ErsteKlasse(){

this.memberVariable="Membervariable erste Klasse";

}

public String getMemberVariable(){

return memberVariable;

}

}

class ZweiteKlasse {

private String memberVariable;

private ErsteKlasse ref_erste_klasse;

public ZweiteKlasse(ErsteKlasse e){ //Konstruktor mit Referenzvariable der

//ersten Klasse

this.ref_erste_klasse=e;

this.memberVariable="Membervariable zweite Klasse";

}

public String getMemberVariable(){

//Zugriff auf die Membervariable der ersten Klasse

return ref_erste_klasse.getMemberVariable();

}

}

public class b{

public static void main(String[] args) {

ErsteKlasse erst = new ErsteKlasse();

System.out.println(erst.getMemberVariable());

ZweiteKlasse zweit = new ZweiteKlasse(erst);

System.out.println(zweit.getMemberVariable());

}

}

OutputMembervariable erste Klasse

Membervariable erste Klasse

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

38

Methode versteht man die Anzahl und der Typ der Parameter ebenso wie denMethodennamen, aber nicht den Rückgabetyp.

d) Weist man einer Variable vom Typ der Elternklasse eine Referenz der Kind-klasse zu, heißt dies »Upcasting« (Elternklasse e = new Kindklasse();). Mitder Referenzvariablen e kann man auf alle ererbten Methoden zugreifen, die inder Elternklasse definiert sind, nicht aber auf die Methoden, die in der Kindklasseneu deklariert wurden:

//zu c)

class ElternKlasse {

private String memberVariable;

public ElternKlasse(){

this.memberVariable="Membervariable Eltern-Klasse";

}

public String getMemberVariableElternklasse(){

return memberVariable;

}

}

class KindKlasse extends ElternKlasse{

private String memberVariable;

public KindKlasse(){

this.memberVariable="Membervariable Kind-Klasse";

}

public String getMemberVariableKindklasse(){

return memberVariable;

}

}

public class C{

public static void main(String[] args) {

ElternKlasse e = new ElternKlasse();

System.out.println(e.getMemberVariableElternklasse());

KindKlasse k = new KindKlasse();

System.out.println(k.getMemberVariableKindklasse());

System.out.println(k.getMemberVariableElternklasse());

}

}

OutputMembervariable Elternklasse

Membervariable Kindklasse

Membervariable Elternklasse

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

39

e) Upcasting ermöglicht ein sogenanntes polymorphes Verhalten von Methoden(Polymorphie von Operationen). Dabei können verschiedene (überschriebene)Methoden mit gleichem Namen, aber unterschiedlicher Funktion aufgerufen wer-den. Von einem Interface kann eine Variable erzeugt werden, da ein Interface eineElternklasse ist. Alle von einem Interface abgeleiteten Klassen können dann sobehandelt werden, als stammten sie von der gleichen Basisklasse ab.

//zu d)

class ElternKlasse {

private String memberVariable="memberEltern";

public ElternKlasse(){

this.memberVariable="Membervariable Eltern-Klasse";

}

public String getMemberVariableElternklasse(){

return memberVariable;

}

}

class KindKlasse extends ElternKlasse{

private String memberVariable;

public KindKlasse(){

this.memberVariable="Membervariable Kind-Klasse";

}

public String getMemberVariableKindklasse(){

return memberVariable;

}

}

public class Test{

public static void main(String[] args) {

ElternKlasse e = new KindKlasse();

System.out.println(e.getMemberVariableElternklasse());

//System.out.println(e.getMemberVariableKindklasse()); //kein Zugriff!

}

}

OutputMembervariable Elternklasse

//zu e)

interface Basis{

String getMemberVariable();

}

class ErsteKlasse implements Basis {

private String memberVariable;

public ErsteKlasse(){

this.memberVariable="Membervariable Erste-Klasse";

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

40

Wie das obige Beispiel zeigt, erhalten wir den gleichen Output wie mit b.getMem-bervariable(), falls wir e.getMemberVariable() oder z.getMemberVariable() ausgeben.Es gibt jedoch Anwendungen, bei denen man nicht auf den Basistyp eines Inter-faces verzichten kann, nämlich dort, wo Variablen gleichen Typs gefordert sind,wie z.B. in Arrays, wo alle Elemente zwingend vom gleichen Typ sein müssen(Punkt f).

f ) Ein gutes Beispiel ist das Speichern von Objektreferenzen verschiedenen Typsin ein Array (Arrays werden im nächsten Teilkapitel behandelt).

}

public String getMemberVariable(){

return memberVariable;

}

}

class ZweiteKlasse implements Basis {

private String memberVariable;

public ZweiteKlasse(){

this.memberVariable="Membervariable Zweite-Klasse";

}

public String getMemberVariable(){

return memberVariable;

}

}

public class E{

public static void main(String[] args) {

ErsteKlasse e=new ErsteKlasse();

ZweiteKlasse z=new ZweiteKlasse();

Basis b;

b=e; //Upcasting

System.out.println(b.getMemberVariable());

b=z; //Upcasting

System.out.println(b.getMemberVariable());

//Direkte Ausgabe

System.out.println(e.getMemberVariable());

//Direkte Ausgabe

System.out.println(z.getMemberVariable());

}

}

OutputMembervariable Erste-Klasse

Membervariable Zweite-Klasse

Membervariable Erste-Klasse

Membervariable Zweite-Klasse

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

41

In der Praxis wird ein Interface zum Beispiel bei Threads verwendet, damit derVererbungsweg für andere Klassen frei bleibt (Kapitel 4).

//zu f)

interface Basis{

String getMemberVariable();

}

class ErsteKlasse implements Basis {

private String memberVariable;

public ErsteKlasse(String name){

this.memberVariable=name;

}

public String getMemberVariable(){

return memberVariable;

}

}

class ZweiteKlasse implements Basis {

private String memberVariable;

public ZweiteKlasse(String name){

this.memberVariable=name;

}

public String getMemberVariable(){

return memberVariable;

}

}

public class f{

public static void main(String[] args) {

Basis[] b_array = new Basis[4]; //Arraydeklaration

b_array[0]=new ErsteKlasse("1"); //Upcasting

b_array[1]=new ZweiteKlasse("2"); //Upcasting

b_array[2]=new ErsteKlasse("3"); //Upcasting

b_array[3]=new ErsteKlasse("4"); //Upcasting

for (int i=0;i<b_array.length;i++){

System.out.print(b_array[i].getMemberVariable());

}

}

}

Output1234

Listing 1.8: Listings a bis f zeigen die Eigenheiten von Interfaces und Referenzvariablen.

package kapitel1.interfaces;

public class MeinThread extends Object implements Runnable

{

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

42

Speziell ist die Instanziierung Thread t =new Thread(new MeinThread());, wodie Referenzvariable dem Konstruktor Thread() übergeben wird, damit das ObjektThread die Methode run() des Objekts MeinThread findet.

Wir versuchen, die gesamte Struktur des obigen Thread-Programms nachzubil-den, damit die Rolle des Interfaces klar wird. Allen Bezeichnern wurde im folgen-den Listing eine Eins angehängt, damit sie nicht mit den echten Thread-Methodenkollidieren. Sehr wichtig ist der Mechanismus der Übertragung der Referenzvari-able zum Objekt Thread1. Eigentlich müsste ja die lokale Membervariable i inThread1 vom Typ MeinThread1 sein. Dies ist aber eine schlechte Lösung, weil derName dieses Typs sich je nach Objekt ändert. Also verwenden wir anstattMeinThread1 einfach den allgemeineren Typ Runnable1 des Interfaces. Es findethier also auch ein Upcasting statt, eine spezifischere Referenzvariable (newMeinThread1()) wird an eine allgemeinere Referenzvariable (i) zugewiesen.

public void run(){

//hier findet die Aktion statt

int i;

for (i=0;i<10;i++) {

System.out.println(Thread.currentThread().getName()+" Nr: "+i);

}

} //ende run()

public static void main(String[] args){

Thread t =new Thread(new MeinThread()); //Übergabe der Referenzvariable

t.setName("Thread 1");

t.start();

}//Ende Main

}//Ende class

OutputThread 1 Nr: 0

Thread 1 Nr: 1

Thread 1 Nr: 2

Thread 1 Nr: 3

Thread 1 Nr: 4

Thread 1 Nr: 5

Thread 1 Nr: 6

Thread 1 Nr: 7

Thread 1 Nr: 8

Thread 1 Nr: 9

Listing 1.9: Einfacher Thread definiert mithilfe des Interfaces »Runnable« (siehe Prüfungsziel 4). Speziell ist die Übergabe der Referenzvariable an den Konstruktor »Thread«.

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

43

Es handelt sich in diesem Fall und generell bei Java nicht um eine Mehrfachverer-bung. Mehrfachvererbungen sind in Java nicht möglich. Mithilfe von Schnittstellenist es vielmehr möglich, Methoden von anderen Objekten über die Polymorphie zunutzen.

interface Runnable1{

void run1();

}

class Thread1 implements Runnable1{

public void run1(){};

private Runnable1 i; //anstatt MeinThread1 Supertyp davon !!!

Thread1(Runnable1 i){ //anstatt MeinThread1 Supertyp davon !!!

this.i=i;

i.run1();

}

}

public class MeinThread1 implements Runnable1{

public void run1(){

//hier findet die Aktion statt

int i;

for (i=0;i<10;i++)

{System.out.println("hallo");

}

} //ende run1()

public static void main(String[] args){

Thread1 t =new Thread1(new MeinThread1());

}//Ende Main

}//Ende class

Listing 1.10: Erklärung des Mechanismus der Übergabe einer Referenzvariable an den Kon-struktor einer externen Klasse, damit die externe Klasse auf die Methode run1() zugreifen kann.

Hinweis

Interfaces können in Verbindung mit Upcasting verwendet werden (Castingbedeutet Typumwandlung und Upcasting eine Typumwandlung die Vererbungs-hierarchie hinauf).

Aus einem Interface lässt sich eine Referenzvariable erzeugen, die dann als ein-zige Referenzvariable verwendet werden kann, um verschiedene Methoden mitgleichem Namen, aber unterschiedlicher Funktion aufzurufen (Polymorphie,Prüfungsziel 5-2).

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

44

1.1.6 Enums

Enum ist ein Aufzählungstyp. Er existiert erst ab der Version 1.5 von Java. Die Defi-nition von Enum besteht aus folgenden Komponenten:

Die Deklaration eines Enums lautet wie folgt:

Am Ende der Deklaration der Konstanten ist auch ein »;« erlaubt:

oder das Semikolon kann ganz weggelassen werden:

»enum« steht anstelle von »class«, »Rgb« steht für den Klassennamen. enum ist dasneue Schlüsselwort, das mit der Version 1.5 eingeführt wurde.

Achten Sie darauf, dass innerhalb der geschweiften Klammern Konstanten (ohneHochkommas) stehen, die mit einem Komma getrennt sind. Diese Konstantensind implizit public static final. Per Konvention verwendet man für sie die Groß-schreibung, die Verwendung von Kleinschreibung führt allerdings zu keinemKompilierfehler. Die Konstanten müssen den Regeln für die Identifizierer genü-gen ($, _, Buchstaben sind erlaubt, Zahlen, Sonderzeichen nicht). Die Konstantendürfen nicht mit Anführungszeichen umgeben werden!

Auch kann die Referenzvariable eines Interfaces dazu verwendet werden, eineZugriffsmöglichkeit von einem Objekt auf ein anderes Objekt zu schaffen.Upcasting wird auch »widening conversion« (erweiternde Konvertierung)genannt, also die Umwandlung eines spezifischeren (Kind-)Typs in einen allge-meineren (Eltern-)Typ. Bei primitiven Datentypen bedeutet eine »widening con-version« die Zuweisung eines Typs mit kleinerer Präzision an einen Typ mitgrößerer Präzision (z.B. die Zuweisung einer short- an eine long-Integer-Vari-able).

[<Modifizierer>] enum <KlassenBezeichner>{<Identifizierer1>,<Identifizierer2>, ...};

public enum Rgb {ROT,GRUEN,BLAU};

public enum Rgb {ROT,GRUEN,BLAU;};

public enum Rgb {ROT,GRUEN,BLAU}

Hinweis (Forts.)

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

45

Die Deklaration von farbe1 als eine Variable vom Enumerations-Typ Rgb:

Farbe darf nun nur ROT, GRUEN oder BLAU als Wert annehmen.

Die Initialisierung mit einer einzelnen Konstante erfolgt mit:

Die Deklaration und Initialisierung kann auch in einer einzigen Zeile erfolgen:

Die Nutzung erfolgt so:

was ROT ausgibt.

Enums können, obwohl sie wie Klassen aufgebaut sind, nicht mit new instanziiertwerden.

Hier ein komplettes Beispiel:

Enums können außerhalb und innerhalb einer Klasse, nicht aber innerhalb einerMethode deklariert werden. Je nach Deklaration innerhalb oder außerhalb einerKlasse ist der Zugriffsweg ein anderer: Innerhalb der Klasse braucht der Klassen-name nicht spezifiziert zu werden. Falls jedoch die Deklaration der Enums ineiner fremden Klasse liegt, muss der Klassenname angegeben werden.

Rgb farbe1;

farbe1=Rgb.ROT;

Rgb farbe2=Rgb.GRUEN;

Rgb farbe3=Rgb.BLAU;

System.out.println(farbe1);

public class EnumFarben{

public enum Rgb {ROT,GRUEN,BLAU}; //Deklaration

public static void main(String... args){

Rgb farbe1=Rgb.ROT; //Konstruktion

Rgb farbe2=Rgb.GRUEN; //Konstruktion

Rgb farbe3=Rgb.BLAU; //Konstruktion

System.out.println(farbe1+" "+ farbe2+" "+ farbe3);

}

}

OutputROT GRUEN BLAU

Listing 1.11: Deklaration und Konstruktion eines Enum-Typs

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

46

Enums besitzen die Methoden Enum-Typ[] values() und Enum-Typ valueOf(String s).Die erste Methode gibt ein Array und die zweite die Enum-Konstante zurück, fallses eine Entsprechung zum übergebenen String gibt.

Beispiel:

Auf ein Enum in einer anderen Klasse greift man wie folgt zu:

public class EnumTest {

enum Qualifikation{ //Deklaration innerhalb der aufrufenden Klasse

SEHRGUT, GUT, ZUFRIEDENSTELLEND, GENUEGEND, UNGENUEGEND

} //";" ist optional

public static void main(String[] args){

Qualifikation sg=Qualifikation.SEHRGUT; //"Instanziierung"

System.out.println(sg.toString()); //Ausgabe als Variable

for (int i=0;i<5;i++)

System.out.println(Qualifikation.values()[i]); //Ausgabe als Array

}

}

OutputSEHRGUT

SEHRGUT

GUT

ZUFRIEDENSTELLEND

GENUEGEND

UNGENUEGEND

Listing 1.12: Deklaration eines Enums, Speichern eines Enum-Wertes in eine Variable und Auf-listung der Menge aller Enums mit der Methode values()

class EnumContainer{

enum Qualifikation{ //Deklaration ausserhalb der aufrufenden Klasse

SEHRGUT, GUT, ZUFRIEDENSTELLEND, GENUEGEND, UNGENUEGEND

}

}

public class EnumTest1 {

public static void main(String[] args){

EnumContainer.Qualifikation sg=EnumContainer.Qualifikation.SEHRGUT;

System.out.println(sg.toString()); //Ausgabe als String

for (Object o:Qualifikation.values())

System.our.println(o); //Ausgabe als Object

}

}

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

47

Enums finden dort Verwendung, wo man sich auf eine feste Anzahl von Wertenbeschränken will.

Enums können auch in Switch-Case-Anweisungen verwendet werden (siehe Prü-fungsziel 2-1).

OutputSEHRGUT

SEHRGUT

GUT

ZUFRIEDENSTELLEND

GENUEGEND

UNGENUEGEND

Listing 1.13: Deklaration eines Enums in einer anderen Klasse. Mit der Methode values() der Klasse java.lang.enum lässt sich auf den Inhalt eines Enums per Index zugreifen, Qualifikation.values() ist ein Array.

class EnumContainer{

enum Qualifikation{ //Deklaration ausserhalb der aufrufenden Klasse

SEHRGUT, GUT, ZUFRIEDENSTELLEND, GENUEGEND, UNGENUEGEND

}

}

public class EnumTest3 {

public static void main(String[] args){

EnumContainer.Qualifikation beurteilung=EnumContainer.Qualifikation.SEHR-GUT;

switch (beurteilung){

case SEHRGUT:System.out.println("6");

break;

case GUT:System.out.println("5");

break;

case ZUFRIEDENSTELLEND:System.out.println("4.5");

break;

case GENUEGEND:System.out.println("4.0");

break;

case UNGENUEGEND:System.out.println("3.0");

break;

}

}

}

Output6

Listing 1.14: Verwendung einer Switch-Case-Anweisung mit einem Enum als Selektor. Achten Sie darauf, dass nur die Konstanten bei den Case-Statements stehen.

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

48

1.1.7 Enums und Konstruktoren, Methoden und Variablen

Enums können analog zu Klassen auch Konstruktoren, Methoden und Variablenbesitzen und werden genau gleich wie Klassen kompiliert und ausgeführt:

Das obige Enum enthält drei mit Kommas abgegrenzte Konstanten. Die Methodemain() befindet sich in der dritten Konstante. Die Methode wird von der Konstantemit einem Semikolon getrennt.

Es lassen sich auch weitere Methoden einbauen. Die Methode hallo() gehört zumgesamten Enum und kann auch mit der Referenz gruen aufgerufen werden:

public enum Farbe{

ROT, GRUEN, BLAU;

public static void main(String[] args){

Farbe blau=Farbe.BLAU;

System.out.println(blau);

}

}

OutputBLAU

Listing 1.15: Enums verhalten sich wie Klassen. Sie können Methoden enthalten und lassen sich kompilieren und ausführen.

public enum Farbe{

ROT, GRUEN, BLAU;

public void hallo(){

System.out.println("Hallo");

}

public static void main(String[] args){

Farbe blau=Farbe.BLAU;

System.out.println(blau);

blau.hallo();

Farbe gruen=Farbe.GRUEN;

gruen.hallo();

}

}

OutputBLAU

Hallo

Hallo

Listing 1.16: Beliebig viele Methoden können zu einer Konstante hinzugefügt werden. Die Methode hallo() gehört zum gesamten Enum und kann auch mit der Referenz gruen aufgerufen werden.

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

49

Will man pro Konstante eine spezielle Funktion ausführen, kann man die Funk-tion in geschweiften Klammern der Konstante anfügen. Allerdings muss dann diespezielle Funktion im allgemeinen Bereich eine Entsprechung haben, damit dieseüberschrieben werden kann.

Bei Prüfungen ist der Einsatz von Enums in Verbindung mit der MethodetoString() beliebt. Das folgende Beispiel ist gleich wie das vorangehende mitdem Unterschied, dass toString() nicht im allgemeinen Teil anzugeben ist,weil toString() schon in Object eingebaut ist.

public enum Farbe{

ROT,

GRUEN{

public void zuGruen(){

System.out.println("nur von gruen aufrufbar");//spezifische Methode

}

},

BLAU; //allgemein zugreifbare Methoden

public void hallo(){

System.out.println("hallo");

}

public void zuGruen(){//diese Methode wird überschrieben

System.out.println("ich sollte überschrieben werden");

}

public static void main(String[] args){

Farbe blau=Farbe.BLAU;

System.out.println(blau);

blau.hallo();

Farbe gruen=Farbe.GRUEN;

gruen.hallo();

gruen.zuGruen();

blau.zuGruen();

}

}

OutputBLAU

hallo

hallo

nur von gruen aufrufbar

ich sollte überschrieben werden

Listing 1.17: Will man an einer Konstante eine spezifische Methode ausführen, muss diese im allgemeinen Teil (hinter dem Semikolon bei einer beliebigen Konstante) deklariert werden.

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

50

Es können auch Konstruktoren in einem Enum vorkommen, wie das folgende Bei-spiel zeigt:

public enum Farbe{

ROT{public String toString(){

return "ff0000";

}

},

GRUEN{public String toString(){

return "00ff00";

}

},

BLAU{public String toString(){

return "0000ff";

}

}; //Semikolon!!!

//toString() muss hier nicht deklariert werden, da sie in Object schon vorhanden //ist

public static void main(String[] args){

Farbe rot=Farbe.ROT;

System.out.println(rot.toString());

Farbe gruen=Farbe.GRUEN;

System.out.println(gruen.toString());

Farbe blau=Farbe.BLAU;

System.out.println(blau.toString());

}

}

Outputff0000

00ff00

0000ff

Listing 1.18: Einsatz der Methode toString() bei Enums.

enum Ungeheuer{

GOZILLA("grunzen"), VAMPIR("beissen"), MONSTER("schlagen");

String bedrohung;

private String farbe;

Ungeheuer(String s) {bedrohung = s;}

public void setFarbe(String farbe){this.farbe=farbe;}

public String getFarbe(){ return farbe;}

}; //Ende enum

public class EnumMethodenVariablen {

static Ungeheuer u;

public static void main(String[] args){

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

51

Beachten Sie, dass das gesamte Enum-Konstrukt im obigen Listing ohne einenZeilenumbruch vorliegt, bzw. die Zeilenumbrüche nach den Semikola erfolgen.Das Enum Ungeheuer hat nur drei Werte, die mit Kommas getrennt sind.GOZILLA("grunzen"), VAMPIR("beissen") und MONSTER("schlagen") sindAufrufe des Konstruktors. String bedrohung; ist eine Membervariable, undUngeheuer(String s){bedrohung = s; ist der Konstruktor. getFarbe() und set-Farbe() sind Methoden. Mit u.GOZILLA.bedrohung lesen wir den Wert der Member-variable bedrohung aus. Mit setFarbe() setzen wir die Farbe auf »gruen« und lesendiesen Wert mit getFarbe() wieder aus.

Noch ein weiteres, ziemlich komplexes Beispiel. Hier ist den Konstanten ADD undSUB direkt die Methode berechne() zugeordnet. Die zu überschreibende Methodebefindet sich im »allgemeinen« Bereich hinter einem Semikolon.

System.out.println(u.GOZILLA.bedrohung+" "+u.VAMPIR.bedrohung+" " +

u.MONSTER.bedrohung);

u.GOZILLA.setFarbe("gruen");

System.out.println(u.GOZILLA.getFarbe());

}

}

Outputgrunzen beissen schlagen gruen

Listing 1.19: Enums können Konstruktoren, Methoden und Variablen speichern. Beachten Sie, dass das Trennzeichen zwischen Element und Methode ein Semikolon ist.

enum Aendere {

ADD {int berechne(int x){return ++x;}},

SUB {int berechne(int x){return --x;}};

abstract int berechne(int x);

}

public class EnumMethoden{

public static void main(String... args){

int x=100;

for (Aendere a: Aendere.values()){//erweiterte for-Schleife

System.out.println(a+" von "+x+"="+a.berechne(x));

}

}

}

OutputADD von 100=101

SUB von 100=99

Listing 1.20: Mit einer abstrakt deklarierten Methode und dem Überschreiben der Methode in der Konstante wird eine sogenannte konstant-spezifische (constant-specific) Methode konstruiert.

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

52

Die abstract definierte Methode wird in den beiden Konstanten überschrieben. Mitder erweiterten for-Schleife for (Aendere a: Aendere.values()) iteriert mandurch das Enum-Konstrukt. (Iterieren bedeutet alle Werte durchgehen.)

Statischer Import von Enums: Enums sind implizit statisch und können damitstatisch importiert werden. Wie unten gezeigt, müssen entweder alle Konstantenmit der Wildcard »*« oder als einzelne Konstanten importiert werden. Die Qualifi-zierung mit Farbe auf der rechten Seite des Gleichheitszeichens muss entfallen.

Zum Abschluss des Teilkapitels folgt nun ein Beispiel, das die Struktur einesEnums nochmals veranschaulichen soll. Ein solches Beispiel könnte z.B. in derPrüfung als Drag & Drop-Frage vorkommen:

import static ch.geo.Rgb.*;

import static ch.geo.Farbe.ROT

Farbe rot= ROT;

//Farbe rot=Farbe.ROT; //beim nicht-statischen Import

public enum Element{

FEUER{

public String eigenschaft(){

return "heiss";

}

},

WASSER{

public String eigenschaft(){

return "nass";

}

},

LUFT {

public String eigenschaft(){

return "unsichtbar";

}

};//hier folgen allgemeine Elemente

public String eigenschaft(){

return "element";

}

public static void main(String[] args){

Element f=Element.FEUER;

System.out.println(f.eigenschaft());

System.out.println(FEUER.eigenschaft());

}

} //ende ELEMENT

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

53

1.1.8 EnumSet und EnumMap

Neu ab der Version 1.5 sind die beiden Collection-Klassen EnumSet undEnumMap (siehe auch Prüfungsziel 6). Sie ermöglichen es, durch einen Enum-Konstrukt zu iterieren.

Beispiel EnumSet:

Wie Sie in Kapitel 6 sehen werden, ist eine Map ein Konstrukt, bei dem jedem ein-zelnen Eintrag ein Schlüssel zugewiesen wird. Am Beispiel von Ausstattungsvari-anten von Autos wäre der Schlüssel der Preis der jeweiligen Variante. Hier einBeispiel einer EnumMap.

Outputheiss

heiss

Listing 1.21: Struktur eines Enums. Die Methode eigenschaft() im allgemeinen Teil (hinter dem Semikolon deklariert) wird bei den einzelnen Konstanten überschrieben.

Wichtig

Die Konstanten bei Enums werden mit Kommas getrennt, den Konstanten kanneine geschweifte Klammer folgen, um spezifische Informationen anzufügen.Nach dem Semikolon folgen Methoden, die für alle Konstanten gelten.

Enum EnumStruktur{

KONSTANTE0,

KONSTANTE1{spezifische Methode},

KONSTANTE2{spezifische Methode},

KONSTANTE3;allgemeine Methoden

}

import java.util.*;

enum tag {MONTAG,DIENSTAG,MITTWOCH,DONNERSTAG,FREITAG,SAMSTAG,SONNTAG};

public class EnumSetTest{

public static void main(String []args){

for (tag t:EnumSet.range(tag.MONTAG,tag.SONNTAG)){

System.out.print(t+" ");

}

}

}

OutputMONTAG DIENSTAG MITTWOCH DONNERSTAG FREITAG SAMSTAG SONNTAG

Listing 1.22: Die Collection-Klasse EnumSet erlaubt die Manipulation von Enums. Mit range() kann man mithilfe einer enhanced-for-Schleife durch die Enum-Menge iterieren.

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

54

1.1.9 Abstrakte Klassen (abstract classes)

Eine abstrakte Klasse wird mit dem Schlüsselwort abstract markiert. Eine abstrakteKlasse kann (im Gegensatz zu Schnittstellen!) nicht instanziiert werden.

Abstrakte Klassen werden für sehr allgemein gehaltene Klassen verwendet unddienen als Elternklasse für diverse konkrete Klassen. Einige Beispiele für allge-meine Klassen sind z.B. Form für die konkreteren Klassen Kreis, Viereck, Rechteckusw., Fahrzeug für Schiff, Auto, Flugzeug etc. oder Hund für Terrier, Schäferhund etc.

Genau wie bei Interfaces können abstrakte Klassen Methoden deklarieren, diedann von den erbenden Klassen implementiert werden müssen. Im Gegensatz zuSchnittstellen, können aber auch vollständige Methoden (mit Kopf und Körper) inabstrakten Klassen programmiert werden.

Abstrakte Methoden können nicht in nichtabstrakten Klassen vorkommen.

import java.util.*;

public class EnumMapTest {

enum ausstattung {SPARTANISCH,MITTEL,LUXUS;}

public static void main(String args[]) {

Map<ausstattung, Double> map = new EnumMap<ausstattung,

Double>(ausstattung.class);

map.put(ausstattung.SPARTANISCH, 10000.0);

map.put(ausstattung.MITTEL, 12000.0);

map.put(ausstattung.LUXUS, 15000.0);

for (Map.Entry<ausstattung, Double> eintrag : map.entrySet()){

System.out.println("Map Eintrag: " + eintrag);

}

}

}

OutputMap Eintrag: SPARTANISCH=10000.0

Map Eintrag: MITTEL=12000.0

Map Eintrag: LUXUS=15000.0

Listing 1.23: Beispiel einer EnumMap. Eine Map kann als assoziatives Array angesehen wer-den.

abstract class Hund{

//Eigenschaften

protected String name;

protected double groesse;

protected String gebell;

public Hund(){ //Konstruktor

}

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

55

public abstract void fressen();

public abstract void bellen();

public String getName(){

return name;

}

public void setName(String name){

this.name=name;

}

}//Ende Objekt Hund

class Dackel extends Hund{

public void fressen(){

System.out.println("schmatz schmatz");

}

public void bellen(){

System.out.println("wau wau");

}

}//Ende Dackel

class SchaeferHund extends Hund{

public void fressen(){

System.out.println("schlurp schlurp");

}

public void bellen(){

System.out.println("wuff wuff");

}

}//Ende SchaeferHund

public class AbstractTest{

public static void main(String [] args){

//Dackel d=new Dackel();

Hund d=new Dackel();//Polymorphe Deklaration

d.setName("Waldi");

System.out.println(d.getName());

d.fressen();

d.bellen();

//SchaeferHund s=new SchaeferHund();

Hund s=new SchaeferHund(); //Polymorphe Deklaration

s.setName("Rex");

System.out.println(s.getName());

s.fressen();

s.bellen();

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

56

Wie oben gezeigt, kann Java per Referenzvariable die zugehörige Methode zurLaufzeit (Dynamic Method Lookup) erkennen (siehe Prüfungsziel 5.2). Währendder Programmausführung werden die richtigen Methoden fressen() und bellen()ausgewählt. Dieses Verhalten, wie schon früher erwähnt, wird Polymorphie (Viel-gestaltigkeit) genannt.

Polymorphie ist hingegen bei privaten, finalen oder statischen Methoden nichtmöglich, weil solche Methoden nicht sichtbar sind oder nicht überschrieben wer-den können.

1.1.10 Verschachtelte und innere Klassen (nested classes, inner classes)

Seit Java 1.1 ist es möglich, Klassen innerhalb von Klassen und Interfaces inner-halb von Interfaces zu deklarieren. Klassen, die nicht verschachtelt sind, werdenunverschachtelte oder äußere Klassen genannt (top level classes, outer classes). Top-Level-Klassen können im Gegensatz zu verschachtelten Klassen nicht statischdeklariert werden.

Verschachtelte Klassen (nested classes) werden vor allem gebraucht, um Informati-onen zu verstecken und um die Kommunikation zwischen Objekten zu optimie-ren.

Es gibt zwei Varianten von verschachtelten Klassen: statische verschachtelte Klassen(static nested classes) und innere Klassen (inner classes).

Innere Klassen haben eine Verbindung zu einer Instanz derjenigen Klasse, in dersie definiert sind, und somit Zugriff auf die Membervariablen der umschließen-den Klasse. Innere Klassen können nicht ohne eine Instanz der äußeren Klasseinstanziiert werden und dies nur in einem nicht-statischen Kontext. Innere Klas-sen kommen in zwei Varianten vor: benannt oder anonym (d.h. ohne Namen). Sta-tische innere Klassen gibt es nicht, sondern nur statische verschachtelte Klassen! Die

//Hund h = new Hund(); ergibt eine Fehlermeldung, da keine Instanziierung

//erlaubt ist.

}

}

OutputWaldi

schmatz schmatz

wau wau

Rex

schlurp schlurp

wuff wuff

Listing 1.24: Die abstrakte Klasse Hund kann nicht instanziiert werden. Vielmehr müssen die in der Klasse Hund deklarierten Methoden in den abgeleiteten Klassen implemen-tiert werden.

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

57

Kompilierung produziert zusätzliche Klassendateien mit dem Namen Aeussere-Klasse$InnereKlasse.class.

Statische verschachtelte Klassen bzw. verschachtelte Klassen im engeren Sinn habenkeine Verbindung zu einer Instanz der äußeren Klasse. Die gleiche Unterschei-dung gilt für Interfaces.

Verschachtelte Klassen werden dort verwendet, wo die Funktionalität nur in deräußeren/besitzenden Klasse benötigt wird. Sie sollten nicht größer als die besit-zende Klasse werden, da die Übersichtlichkeit darunter leidet.

Typen von statischen verschachtelten Klassen/Interfaces

Verschachtelte statische Klasse (nested static class)

(Statisches) Verschachteltes Interface (nested interface)

Typen von inneren Klassen

Innere Member-Klasse, nicht-statisch

Innere lokale Klasse

Innere anonyme Klasse

Abb. 1.3: Systematik der verschachtelten Klassen

Damit die Begriffsvielfalt nicht noch größer wird, behandeln wir Interfaces erst imnächsten Teilkapitel.

Statische verschachtelte Klassen

Im folgenden Listing wollen wir diese Aspekte aufzeigen:

� Eine verschachtelte statische Klasse wird wie ein sonstiges Member innerhalbder äußeren Klasse definiert (#1).

Verschachtelte Klassen(nested classes)

Statische verschachtelte Klassen (static nested

classes ) oder verschachtelte Klassen

ieS.

Innere Klassen (inner classes )

Verschachteltes Interface

Innere lokale Klasse (inner local class )

Innere member Klasse (inner

member class)

Innere anonyme Klasse (inner

anonymous class)

Verschachtelte statische Klasse (nested static

class )

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

58

� Eine verschachtelte statische Klasse kann selbst wieder statische oder dynami-sche Membervariablen haben (#2 und #3).

� Die Methoden können statisch oder dynamisch sein (#4 und #6), von der stati-schen Methode kann nicht auf dynamische Member der äusseren Klassen zu-gegriffen werden (#5).

� Von der äußeren Klasse kann auf Member der verschachtelten Klasse perPunktnotation zugegriffen werden (#7).

� Damit auf dynamische Member der verschachtelten Klasse zugegriffen werdenkann, muss die innere Klasse instanziiert werden (#8 und #9).

� Der Unterschied der Zugriffsarten wird bei #10 und #11 deutlich.

public class Aeussere{

private int memberAeussereDynamisch=0;

private static int memberAeussereStatisch=10;

static class StatischVerschachtelt{ //#1 statische Memberklasse!!

private int memberInnereDynamisch=100; //#2

private static int memberInnereStatisch=1000; //#3

public void getDynamisch(){//#4

System.out.println("Innere dynamische: "+memberInnereDynamisch);

//System.out.println("Aeussere dynamische:

//"+memberAeussereDynamisch);//#5 kein Zugriff

System.out.println("Innere statische: "+memberInnereStatisch);

System.out.println("Aeussere statische: "+memberAeussereStatisch);

}

public static void getStatisch(){//#6

System.out.println("Innere statische: "+memberInnereStatisch);

System.out.println("Aeussere statische: "+memberAeussereStatisch);

}

}//Ende innere statische Klasse

public static void main(String []args){

StatischVerschachtelt.getStatisch();//#7

StatischVerschachtelt sv=new StatischVerschachtelt();//#8

sv.getDynamisch();//#9

System.out.println("stat. Member:

"+StatischVerschachtelt.memberInnereStatisch); //#10

System.out.println("dynamisches Member innere Kl.:

"+sv.memberInnereDynamisch); //#11

}

}

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

59

Bei verschachtelten statischen Klassen ist ein gegenseitiger Zugriff auf private Mem-bervariablen möglich – natürlich mit der Einschränkung, dass im statischen Kon-text nur auf statische Methoden zugegriffen werden kann.

Auf ein statisches Member der verschachtelten statischen Klasse kann von deräußeren Klasse mit folgendem Konstrukt zugegriffen werden: Aeussere-

Klasse.InnereKlasse.Variablenname (siehe //#10 in obigem Listing).

Auf ein dynamisches Member der äußeren Klasse hingegen kann von der (stati-schen) inneren Klasse nicht zugegriffen werden, weil der Kontext nicht stimmt.

Die Referenz einer inneren statischen Klasse kann wie folgt erzeugt werden:

Hier ein Beispiel:

OutputInnere statische Membervariable 1000

Aeussere statische Membervariable 10

Innere dynamische Membervariable 100

Innere statische Membervariable 1000

Aeussere statische Membervariable 10

Innere statische Membervariable 1000

Listing 1.25: Statische verschachtelte Klasse (nested static class): Die innere Klasse kann auf die privaten statischen Membervariablen der äußeren Klasse zugreifen, nicht aber auf deren dynamischen Membervariablen. Ebenso kann die äußere Klasse auf dynamische Variablen der inneren Klasse zugreifen.

Aeussere.Innere s = new Aeussere.Innere();

class Aeussere{

public static class Innere{

public void hallo(){System.out.println("Hallo");

}// Ende hallo()

}//Ende Innere

}//Ende Aeussere

public class AeussereTest{

public static void main(String [] args){

Aeussere.Innere s = new Aeussere.Innere();

s.hallo();

}

}

OutputHallo

Listing 1.26: Erzeugung einer Referenz einer statischen verschachtelten Klasse

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

60

Innere Memberklasse

Im folgenden Listing wollen wir zeigen, dass:

� die innere Memberklasse nicht-statisch (ohne Modifizierer static) deklariertwird (#1),

� die innere Memberklasse nur dynamisch deklarierte Membervariablen habenkann und keine statischen (#2 und #3),

� eine Methode der inneren Memberklasse sowohl auf dynamische als auch aufstatische Variablen der äußeren Klasse zugreifen kann (#4),

� die Instanziierung der inneren Klasse (#6) erst nach der Instanziierung der äu-ßeren Klasse erfolgen kann (#5),

� der Zusammenzug der Instanziierungen ein ungewohntes Bild ergibt: Aeussere.Innere i=new Aeussere().new Innere(); (#7).

public class Aeussere{ //Beispiel lokale innere Memberklasse

private int mAeussereDynamisch=0;

private static int mAeussereStatisch=10;

public class Innere extends Aeussere {//#1

private int mInnere=100;//#2

//private static int mInnereStatisch=1000; //#3 nicht möglich

public void getVarDyn(){

System.out.println("Innere dynamische: "+mInnere);

System.out.println("Aeussere dynamische: "+mAeussereDynamisch);//#4

System.out.println("Aeussere statische: "+mAeussereStatisch);//#4

}

} //Ende innere Klasse

public static void main(String []args){

//Aeussere a1=new Aeussere();//#5

//Aeussere.Innere i=a1.new Innere(); //#6

Aeussere.Innere i=new Aeussere().new Innere();//#7

i.getVarDyn();

System.out.println("Innere dynamische: "+i.mInnere);

}

}

Listing 1.27: Instanziierung einer inneren statischen Klasse

OutputInnere dynamische: 100

Aeussere dynamische: 0

Aeussere statische: 10

Innere dynamische: 10

Listing 1.28: Nicht-statische innere Memberklasse

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

61

Die innere Klasse kann von der äußeren Klasse erben (keine Notwendigkeit). Dieinnere Klasse kann auf alle privaten Variablen der äußeren Klasse zugreifen. Aufdie privaten Membervariablen der inneren Klasse kann über die Referenz derinneren Klasse zugegriffen werden.

Bei #1 wurde die innere Klasse public deklariert. Dies bedeutet, dass eine andereKlasse außerhalb des Packages auf ein öffentliches Member dieser Klasse zugrei-fen könnte. Würde der Modifizierer fehlen (default), könnten nur andere Klasseninnerhalb des Packages auf ein öffentliches Member der inneren Klasse zugreifen.

Die innere Klasse kann nur durch eine Instanz der äußeren Klasse instanziiertwerden. Der Zusammenzug der Instanziierung der äußeren und inneren Klasseist übrigens ein Steckenpferd der Prüfer, merken Sie sich das Muster

Aeussere.Innere i=new Aeussere().new Innere();.

Innere lokale Klasse

»Lokal« bedeutet in Java immer »methodenlokal«, d.h. eine innere Klasse wird ineiner Methode deklariert.

Das folgende Listing zeigt:

� Eine Klasse kann innerhalb einer Methode deklariert werden (#1).

� Falls auf eine methodenlokale Variable (automatische Variable) von der metho-denlokalen Klasse zugegriffen werden will (#1), muss diese final deklariert sein(unveränderbar, Konstante). Ein Zugriff auf eine normale automatische Variab-le ist nicht möglich (#5).

� Auf Member der äußeren Klasse kann direkt zugegriffen werden, seien sie sta-tisch oder nicht-statisch (#3 und #4).

� Die methodenlokale Klasse wird innerhalb der Methode instanziiert (#6), au-ßerhalb der Methode ist dies nicht möglich (#9). Die Methode der methodenlo-

public class Aeussere{

class Innere extends Aeussere{

public void machWas(){}

}

public static void main(String... args){

Aeussere a=new Aeussere();

Aeussere.Innere i=a.new Innere();

//oder

//Aeussere.Innere i=new Aeussere().new Innere();

}

}

Listing 1.29: Muster der Instanziierung einer inneren Klasse

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

62

kalen Klasse wird auch innerhalb der umschließenden Methode aufgerufen(#7).

� Mit dem Aufruf der umschließenden Methode() (#8) wird die Klasse innerhalbder Methode instanziiert (#6) und die Methode der methodenlokalen Klasseausgeführt (#7).

Wichtig: Methodenlokale Variablen müssen final deklariert werden, damit voneiner methodenlokalen Klasse aus auf sie zugegriffen werden kann (siehe #1 imobigen Listing). Methoden innerhalb methodenlokaler Klassen müssen innerhalbder sie umschließenden Klasse aufgerufen werden (siehe #7 im obigen Listing).

public class Aeussere{ //Beispiel lokale innere Memberklasse

private int mAeussereDynamisch=0;

private static int mAeussereStatisch=10;

public void umschliessendeMethode(){

final String mMethodenlokal="Methodenlokale Variable"; //#1 muss final sein

String mMethodenlokal2="nicht final";

class InnereLokal { //#2

private int mInnereMember=100;

public void getVarDyn(){

System.out.println("Innere dynamische: "+mInnereMember);

System.out.println("Aeussere dynamische: "+mAeussereDynamisch);//#3

System.out.println("Aeussere statische: "+mAeussereStatisch);//#4

System.out.println(mMethodenlokal);

//System.out.println(mMethodenlokal2); //funktioniert nicht//#5

}

} //Ende innere lokale Klasse

InnereLokal il=new InnereLokal(); //#6

il.getVarDyn();//#7 Aufruf der Methode der inneren Klasse

}//Ende umschliessendeMethode()

public static void main(String []args){

Aeussere a=new Aeussere();

a.umschliessendeMethode(); //#8

//a.umschliessendeMethode().getVarDyn();//#9 funktioniert nicht!

}

}

OutputInnere dynamische: 100

Aeussere dynamische: 0

Aeussere statische: 10

Methodenlokale Variable

Listing 1.30: Methodenlokale innere Klasse. Methodenlokale Klassen müssen final deklariert werden.

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.1Prüfungsziel 1-1

63

Innere anonyme Klasse

Innere anonyme Klassen werden in einem einzigen Schritt definiert und instanzi-iert. Sie sind (methoden-)lokale Klassen ohne Namen (anonym). Das Schlüssel-wort class, die Zugriffsmodifizierer (z.B. public) fehlen, ebenso extends undimplements.

Innere anonyme Klassen finden vor allem dort Anwendung, wo eine Referenzvari-able innerhalb einer Methode benötigt wird. In der Praxis werden sie in folgendenSituationen angewandt:

� als Lieferant des Wertes einer Rückgabe innerhalb einer Methode (return State-ment),

� innerhalb eines Arguments eines Methodenaufrufs,

� bei der Initialisierung von Variablen,

� bei der Thread-Programmierung,

� bei Event-Listeners in GUI-Applikationen (nicht Gegenstand dieser Prüfung).

Eine anonyme Klasse wird wie folgt konstruiert:

Dieser Ausdruck selbst ist eine Referenzvariable und kann einer Variablen zuge-wiesen werden:

Das Semikolon am Ende der Klasse ist obligatorisch, da es sich um einen Aus-druck handelt. Im Klassenkörper steht die gesamte Implementation.

In folgendem Beispiel aus Prüfungsziel 4 wird ein Thread-Objekt mit einer anony-men Klasse definiert:

new Anonym() {/* Klassenkörper*/};

Anonym a = new Anonym(){/* Klassenkörper */};

public class MeinThread {

public static void main(String[] args) {

Thread t1 = new Thread() {//Objekt-Körper! Beginn anonyme Klasse//#1

public void run() {//Implementierung der run()-Methode

int i=0;

for (i=0;i<10;i++)

System.out.println("Zaehler: "+i);

}// Ende run()

}; // Ende der anonymen Klasse //#2

t1.start(); //Aufruf der start()-Methode

//Bremse

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

Kapitel 1Deklarationen, Initialisierung und Scoping (Prüfungsziel 1)

64

Bei //#1 wird nach dem Konstruktor direkt die geschweifte Klammer des Objekt-körpers geöffnet. Bei //#2 wird die innere Klasse geschlossen.

Die Grundstruktur aus obigem Listing sieht wie folgt aus:

Der ganze Ausdruck InnereKlasse i= new InnereKlasse(){//Implementie-rung}; liefert eine Referenzvariable. Dieser Ausdruck kann also an allen Stellenin einem Programm stehen, wo eine Referenzvariable benötigt wird, unter ande-rem auch im Parameterfeld einer Funktion.

Anonyme Klassen werden oft zur Implementation (run()-Methode im obigen Bei-spiel) oder zum Überschreiben von Methoden verwendet.

Will man von einer inneren anonymen Klasse auf eine methodenlokale Variableder sie umgebenden Methode zugreifen, muss diese automatische Variable wiebei der inneren lokalen Klasse auch wieder final deklariert sein.

Innere anonyme Klassen finden gemäss der Prüfungsthemen nur noch beiThreads Verwendung. Deshalb ist es vor allem ratsam, die Details von innerenanonymen Klassen in Verbindung mit Threads zu kennen (Prüfungsziel 4).

try { Thread.sleep(5000);

}

catch(InterruptedException e) {}

} // ende main()

} // ende Klasse

Listing 1.31: Anonyme Klassen finden bei Threads Verwendung, weil die Schreibweise sehr kompakt ist und der Code übersichtlich bleibt.

AeussereKlasse{

methode(){

InnereKlasse i= new InnereKlasse(){

//Körper der anonymen Klasse mit Implementierung

};//Ende innere anonyme Klasse

}//Ende Methode

}//Ende AeussereKlasse

Wichtig

Merken Sie sich die Art, wie nicht-statische und verschachtelte statische Klassenerzeugt werden:

Verschachtelt statisch:

Aeussere.Innere s = new Aeussere.Innere();

Innere member:

Aeussere.Innere i=new Aeussere().new Innere();

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963

1.2Prüfungsziel 1-2

65

1.2 Prüfungsziel 1-2

Wie Schnittstellen funktionieren, haben wir schon beim Prüfungsziel 1-1 gesehen.Abstrakte Klassen haben wir ebenfalls behandelt. Hier sehen wir uns den Code an,der mehrere Schnittstellen implementiert und erweitert, sowie die Behandlungvon Schnittstellen bei anonymen Klassen, mit denen wir jetzt starten werden.

1.2.1 Schnittstellen und anonyme Klassen

Anonyme Klassen können nur eine Schnittstelle implementieren. Auch kann eineanonyme Klasse nicht gleichzeitig von einer anderen Klasse erben und eineSchnittstelle implementieren. Sie kann also entweder nur erben oder nur imple-mentieren.

Hier ein Beispiel für eine Implementierung:

Innere methodenlokal:

InnereLokal il= new InnereLokal();

Innere anonyme:

new Anonym(){/*Klassenkörper*/};

Wichtig

Develop code that declares an interface. Develop code that implements orextends one or more interfaces. Develop code that declares an abstract class.Develop code that extends an abstract class.

Entwickeln von Code, der eine Schnittstelle deklariert. Entwickeln von Code, dereine oder mehrere Schnittstellen implementiert oder erweitert. Entwickeln vonCode, der eine abstrakte Klasse deklariert. Entwickeln von Code, der eine abs-trakte Klasse erweitert.

import java.io.*;

import java.util.Scanner;

interface Onlinetest{

public void stelleFrage(String frage);

public String getTestAntwort();

}

public class JavaTest {

private String antwort;

//Onlinetest o =new Onlinetest(); //geht nicht, Implementierung fehlt!

Wichtig (Forts.)

© des Titels »SCJP - Sun Java Certified Programmer« (ISBN 978-3-8266-5963-8) 2009 by Verlagsgruppe Hüthig Jehle Rehm GmbH, Heidelberg

Nähere Informationen unter: http://www.mitp.de/5963