f | © APSIS GmbH |
Seite 50
Seite 50
Seite 50
import lehrbuch.*; // (3.1) public class DreimalFuellen extends Programm { private Eimer eimer; // globale Referenz private void fuellenUndEntleeren() { // Definition einer Prozedur ¬ try { eimer.fuellen(); eimer.entleeren(); } catch (VollAusn ausnahme) {} catch (LeerAusn ausnahme) {} } // Ende der Definition public void start() { eimer = new Eimer(); // Objekterzeugung fuellenUndEntleeren(); // Aufruf der Prozedur ¬ fuellenUndEntleeren(); fuellenUndEntleeren(); // Prozedur wurde dreimal aufgerufen } }
Seite 51
import lehrbuch.EinEimer; // (3.2) public class FuenfmalFuellen extends lehrbuch.Programm { protected void zweimalFuellen() { EinEimer.fuellen(); EinEimer.entleeren(); EinEimer.fuellen(); EinEimer.entleeren(); } public void start() { zweimalFuellen(); // ¬ zweimalFuellen(); EinEimer.fuellen(); EinEimer.entleeren(); } } // Dieses Programm füllt und entleert einen Datenbehälter fünfmal hintereinander.
public class SechsmalFuellen extends FuenfmalFuellen { // (3.3) ¬ // Klasse aus dem Programm (3.2) wird erweitert public void start() { zweimalFuellen(); zweimalFuellen(); zweimalFuellen(); } }
Übung 3.1: Erweitern Sie die Klasse SechsmalFuellen aus dem Programm (3.3) auf Seite 51. Rufen Sie in Ihrer Klasse die Methode zweimalFuellen zweimal auf. Sie müssen dabei dafür sorgen, dass die notwendigen Klassen (FuenfmalFuellen und SechsmalFuellen) für den Compiler erreichbar sind (d.h. am einfachsten im selben Verzeichnis vorliegen). Was geschieht, wenn zweimalFuellen als private vereinbart wird?
Seite 52
public class Steuerfluss extends lehrbuch.Programm { // (3.4) private void prozedur1() { super.anweisung(3); } public void start() { super.anweisung(6); prozedur1(); prozedur2(); super.anweisung(9); prozedur2(); } private void prozedur2() { super.anweisung(13); prozedur1(); super.anweisung(15); } }
Übung 3.2: Schreiben Sie ein möglichst einfaches Programm mit Hilfe der Methode anweisung, in dem die Schachtelungstiefe 3 ist. Protokollieren Sie jeden Aufruf und verfolgen Sie den Steuerfluss mit Hilfe der am Bildschirm sichtbaren Zahlen. Untersuchen Sie die dynamische Reihenfolge der ausgeführten Programmzeilen.
Seite 54
import lehrbuch.*; // (3.5) public class Parametrisiert extends Programm { private Eimer ersterEimer, zweiterEimer, dritterEimer; // drei Referenzen private void fuellenUndEntleeren(Eimer eimer) throws VollAusn, LeerAusn { // ¬ // Definition der Prozedur; ihr formaler Parameter ist eimer der Klasse Eimer eimer.fuellen(); eimer.entleeren(); } public void start() { try { ersterEimer = new Eimer(); zweiterEimer = new Eimer(); dritterEimer = new Eimer(); // drei Datenbehälter ... // evtl. Eimer anzeigen fuellenUndEntleeren(ersterEimer); // Aufruf mit Parameter ersterEimer fuellenUndEntleeren(zweiterEimer); // Aufruf mit Parameter zweiterEimer fuellenUndEntleeren(ersterEimer); fuellenUndEntleeren(dritterEimer); fuellenUndEntleeren(dritterEimer); // Prozedur wurde fünfmal aufgerufen } catch (VollAusn ausnahme) { meldung("Eimer schon voll"); } catch (LeerAusn ausnahme) { meldung("Eimer noch leer"); } } }
Abb. 3.1: Parametereinsetzung beim Aufruf
Übung 3.3: Vereinbaren Sie Ihren Algorithmus aus der Übung 2.16 auf Seite 46 (d.h. die Anweisungsfolge in der Methode start) als Prozedur mit zwei Parametern vom Typ Kreis. Vereinbaren Sie in Ihrer Klasse nun drei Kreise und rufen Sie Ihre Prozedur dreimal für jeweils zwei Kreise (mit verschiedenen Kombinationen) auf. Verfolgen Sie den Ablauf Ihres Programms von Aufruf zu Aufruf.
Ein Tipp: Lagern Sie die Aufrufe der Methode zeichnen aus Ihrer Prozedur in die start-Methode aus. Dann werden die Kreise nicht immer wieder neu gezeichnet, sondern Sie können die Orte, wo sich Ihr Programm aufhält, leichter identifizieren.
Seite 56
import lehrbuch.*; // (3.6) public class GlobalerEimer extends Programm { private Eimer eimer; // globale Referenz ¬ private void prozedur() throws VollAusn { // parameterlos ¬ eimer.fuellen(); // oder ein komplexerer Algorithmus } public void start() { try { eimer = new Eimer(); // Objekt wird erzeugt eimer.anzeigen(); prozedur(); // Aufruf der Prozedur ¬ eimer.entleeren(); eimer = new Eimer(); // ein zweites Objekt wird erzeugt prozedur(); // erneuter Aufruf der Prozedur } catch (VollAusn ausnahme) { super.meldung("Eimer schon voll"); } catch (LeerAusn ausnahme) { super.meldung("Eimer noch leer"); } } }
import lehrbuch.*; // (3.7) public class ParameterEimer extends Programm { private void prozedur(Eimer e) throws VollAusn { // parametrisiert ¬ e.fuellen();// oder ein komplexerer Algorithmus } public void start() { try { Eimer eimer; // lokale Referenz eimer = new Eimer(); // Objekt wird erzeugt eimer.anzeigen(); prozedur(eimer); // Aufruf der Prozedur ¬ eimer.entleeren(); Eimer eimer2 = new Eimer(); // ein zweites Objekt wird erzeugt eimer2.anzeigen(); prozedur(eimer2); // erneuter Aufruf der Prozedur } catch ... // ähnlich
Seite 57
import lehrbuch.*; // (3.8) public class WertOderReferenz extends Programm { private void prozedur(Eimer e) throws VollAusn { // parametrisiert e.fuellen(); // Parameterobjekt wird verändert e = new Eimer(); // Parameterreferenz wird verändert ¬ e.anzeigen(); } public void start() { try { Eimer eimer; eimer = new Eimer(); eimer.anzeigen(); prozedur(eimer); // Aufruf der Prozedur ¬ // Eimer wurde in der Prozedur gefüllt, d.h. Parameterobjekt wurde verändert // Inhalt der Referenz eimer wurde in der Prozedur nicht verändert eimer.entleeren(); ...
private void prozedur(final Eimer e) throws VollAusn { e.fuellen(); // Veränderung des Parameterobjekts erlaubt e = new Eimer(); // Compiler meldet Fehler: Veränderung ist unterbunden ¬ }
private void prozedur(const Eimer e) throws VollAusn { // nicht Java, C++ e.fuellen(); // Compiler meldet Fehler: Veränderung des Parameterobjekts verboten ¬ e = new Eimer();// kein Fehler: Veränderung der Parameterreferenz ist erlaubt }
Übung 3.4: Zeigen Sie den Unterschied zwischen Wert- und Referenzübergabe mit Kreisen aus der Übung 3.3 auf Seite 56: Verändern Sie in einer Prozedur eines Ihrer Kreisobjekte (das Sie als Parameter übergeben haben) und zeigen Sie, dass das Objekt sich nach dem Aufruf in dem Zustand befindet, wie die Prozedur es hinterlassen hat. Verändern Sie auch eine Ihrer Parameterreferenzen durch Zuweisung in der Prozedur, und zeigen Sie, dass sie sich nach dem Aufruf in dem Zustand befindet, in dem sie vor dem Aufruf war (d.h. die Prozedur hat sie nicht verändert).
Seite 58
import lehrbuch.*; // (3.9) public class Kopieren extends Programm { public void start() { Eimer eimer1 = new Eimer(); Eimer eimer2 = new Eimer(); try { ... // die beiden Eimer anzeigen, füllen und entleeren eimer1.kopieren(eimer2); // ¬ // Zustand von eimer2 (gefüllt oder entleert) wird in eimer1 übertragen } ...
Seite 59
Seite 59
package lehrbuch; // (3.10) public class LeereKlasse { /** die Klasse exportiert und beinhaltet nichts */ } public class HalloWelt { // Klasse zum erweitern (Applet) für Textanimation ... // s. Programm (2.16) } public class EinEimer { // Klasse zum erweitern (Applet); statischer Datenbehälter /** gleich zu Beginn wird ein Eimer im Fenster angezeigt und animiert */ public static void fuellen() throws VollFehler; // der Eimer wird gefüllt; wenn schon gefüllt, ungeprüfte Ausnahme public static void entleeren() throws LeerFehler; // der Eimer wird entleert, wenn voll; wenn nicht, ungeprüfte Ausnahme /** Reihenfolgebedingungen: Als erster Aufruf darf nur fuellen aufgerufen werden. Anschließend dürfen entleeren und fuellen nur abwechselnd aufgerufen werden, ansonsten ungeprüfte Ausnahme */ public static void meldung(final String text); // gibt const text (Leseparameter) im Fenster aus, wartet auf Bestätigung public static void start(); // zum Überschreiben ... // weitere Teile der Spezifikation }
public class ZweiEimer { // zwei statische animierte Datenbehälter public static void fuellenLinks() throws VollAusn; // der erste Eimer wird gefüllt; wenn schon gefüllt, geprüfte Ausnahme ... // fuellenRechts, entleerenLinks, entleerenRechts ähnlich public static void anzeigenLinks(); // der erste Eimer wird im Fenster angezeigt public static void anzeigenRechts(); // der zweite Eimer im Fenster angezeigt }
public class ToleranterEimer { // animierter fehlertoleranter Datenbehälter public static void anzeigen(); // der Eimer wird im Fenster angezeigt public static void fuellen(); // der Eimer wird gefüllt; wenn gefüllt, Fehlermeldung public static void entleeren(); // der Eimer wird entleert; wenn leer, Fehlermeldung }
public class Eimer { // Klasse zum Ausprägen; animierter dynamischer Datenbehälter public void fuellen() throws VollAusn; // Eimer wird gefüllt, wenn leer; sonst geprüfte Ausnahme public void entleeren() throws LeerAusn; // Eimer wird entleert, wenn voll; sonst geprüfte Ausnahme /** Reihenfolgebedingungen wie bei EinEimer */ public void anzeigen(); // Eimer wird im Fenster angezeigt und animiert public void meldung(final String text); // const text im Fenster ... // weitere Teile der Spezifikation } ... // weitere Klassen
Seite 61
package paket;
Abb. 3.2: Zugriffsschutz-Ebenen
Übung 3.5: In der Übung 3.1 auf Seite 52 haben Sie die Klasse SechsmalFuellen aus dem Programm (3.3) auf Seite 51 erweitert. Der Compiler hat Ihre Klasse nur dann übersetzen können, wenn die Klassen FuenfmalFuellen und SechsmalFuellen im selben Verzeichnis (d.h. im anonymen Paket) lagen.
Lösen Sie nun dieselbe Aufgabe, indem Sie die beiden Klassen FuenfmalFuellen und SechsmalFuellen in ein eigenes Paket legen. Hierzu müssen Sie in den beiden Klassen ein package-Klausel vereinbaren und die beiden Klassen in ein Unterverzeichnis (mit demselben Namen) Ihres Arbeitsverzeichnisses verlagern. Ihre neue Klasse (die SechsmalFuellen erweitert) soll nach wie vor im anonymen Paket liegen.
Seite 62
Seite 63
public class Sichtbarkeit { // (3.11) private Eimer globalerEimer = new Eimer(); ... // Definition weiterer globalen Referenzen (evtl. mit Objekten) private void prozedur() { // ¬ Eimer lokalerEimer = new Eimer(); ... // Definition der lokalen Referenzen von prozedur ... // Benutzung der Referenzen aus der Klasse Sichtbarkeit // und aus prozedur (nicht aus start) } public void start() { Eimer weitererEimer = new Eimer(); ... // Definition der lokalen Referenzen von start ... // Benutzung der Referenzen aus Sichtbarkeit und start // ein Zugriff auf die Referenzen von prozedur ist nicht möglich prozedur(); // Aufruf (mit lokalerEimer) ¬ ... prozedur(); // ein weiterer Aufruf (mit einem anderen lokalerEimer) } }
Abb. 3.3: Statische und dynamische Struktur
Lebensdauer Ê Sichtbarkeit.
Sichtbarkeit | Kennzeichnung | Erreichbarkeit | Lebensdauer |
lokal | innerhalb einer Methode | aus der Methode | Methode läuft |
privat | private | aus der Klasse | Objekt erreichbar |
geschützt | protected | aus Unterklassen | " |
paketweit | innerhalb einer Klasse | aus Klassen des Pakets | " |
öffentlich | public | aus Kundenklassen | " |
Abb. 3.4: Sichtbarkeitstufen in Java
Seite 65
public class AuesserKlasse { // (3.12) private Eimer globalerEimer = new Eimer(); private Eimer andererEimer = new Eimer(); private class InnereKlasse1 { // ¬ private Eimer lokalerEimer; private void prozedur(Eimer parameter) { lokalerEimer.kopieren(parameter); globalerEimer.kopieren(lokalerEimer); // von innen erreichbar } } class InnereKlasse2 { // ¬ Eimer lokalerEimer; // ein ganz anderes Objekt als zuvor Eimer andererEimer; // gleicher Name, jedoch neues Objekt private void prozedur(Eimer parameter) { lokalerEimer.kopieren(andererEimer); // nicht das lokale InnereKlasse1 referenz = new InnereKlasse1(); referenz.prozedur(globalerEimer); // Aufruf mit Objekt möglich ¬ // InnereKlasse1.prozedur(globalerEimer); // nicht zulässig } } public void start() { InnereKlasse1 referenz = new InnereKlasse1(); referenz.prozedur(globalerEimer); // Aufruf nur mit Objekt möglich ¬ } }
Abb. 3.5: Statische Schachtelung
Seite 66
{ // Anfang eines Methodenrumpfs // (3.13) Eimer eimer1 = new Eimer(); // und andere Vereinbarungen ... // Anweisungen { // innerer Block ¬ Eimer eimer2 = new Eimer(); // lebt nur im inneren Block ... // hier sind weitere geschachtelte Blöcke möglich } // Ende des inneren Blockes ... // und weitere Anweisungen im Rumpf eimer2.fuellen(); // der Compiler meldet Fehler: eimer2 nicht sichtbar ¬ } // Ende des Rumpfes
Seite 67
void tolerant() { // (3.14) try { // Anfang des Prozedurrumpfs aufruf1(); // kann Ausnahme auslösen } catch (Ausnahme ausnahme) { ... } // aufruf2 wird ausgeführt, selbst wenn Ausnahme ausgelöst wurde: aufruf2(); // wenn Ausnahme auslösen kann, muss in tolerant spezifiziert werden // wenn Ausnahme auftritt, wird die Ausführung abgebrochen, da nicht geschützt try { // weiterer geschützter Block ¬ aufruf3(); // kann Ausnahme auslösen try { // innerer Block ¬ aufruf4(); // kann Ausnahme auslösen } catch (Ausnahme ausnahme) { ... } // Ausnahmebehandlung des inneren Blockes ¬ aufruf5(); // läuft weiter, selbst wenn aufruf4 Ausnahme ausgelöst hat } // Ende des inneren Blocks catch (Ausnahme ausnahme) { ... } // Ausnahmebehandlung des äußeren Blockes ¬ aufruf6(); // wenn Ausnahme auslöst, wird weitergereicht; muss spezifiziert werden // Anweisungen des äußeren Blocks werden auch im Ausnahmefall ausgeführt } // Ende des Prozedurrumpfs
Übung 3.6: In der Übung 2.13 auf Seite 39 haben Sie gezeigt, dass die Klasse ToleranterEimer im Fehlerfall keine Ausnahmen auslöst; der Fehler wurde innerhalb der Methoden abgefangen. Ein Fehler im Programm (2.26) auf Seite 41 hat eine Ausnahme ausgelöst. Sie konnte zwar aufgefangen werden, die Ausführung des Programms wurde aber unterbrochen. Lösen Sie nun das Problem, damit das Programm nach einem Fehler weiter läuft, auf zweierlei Weise: mit einer Prozedur und mit geschützten Blöcken.
Schreiben Sie nun eine Prozedur, in der Sie eine Ausnahme auslösen (z.B. einen vollen Eimer füllen). Rufen Sie diese Prozedur zweimal auf. Geben Sie anschließend eine Meldung aus, dass das Programm beendet wurde. Sie müssen natürlich die Ausnahme (z.B. VollAusn) in der start-Methode auffangen. Der zweite Aufruf wird nicht mehr ausgeführt. Auch die Meldung kommt nicht, da start mit einer Ausnahme unterbrochen wurde.
In einer zweiten Version fangen Sie die Ausnahme in der Prozedur auf (und geben Sie eine Fehlermeldung aus). Jetzt werden beide Prozeduraufrufe ausgeführt. Auch die Meldung kommt zum Schluss.
Drittens gestalten Sie Ihr Programm so um, dass die beiden Prozeduraufrufe in je einem geschützten Block stattfinden. Auch jetzt kommt Ihre Schlussmeldung.
Seite 68
Abb. 3.6: Dynamische Schachtelung
private static void prozedur() { // (3.15) ... // lokale Referenzen vereinbart return; // die lokalen Referenzen (u.U. ihre Objekte) werden vernichtet ¬ ... // die Anweisungen werden nicht mehr ausgeführt }
Seite 69
public static void rekursivDirekt() { // (3.16) ... rekursivDirekt(); ... }
public static void rekursivIndirekt1() { ... rekursivIndirekt2(); ... // ¬ } public static void rekursivIndirekt2() { ... rekursivIndirekt1(); ... // ¬ }
Seite 70
public static void rekursiv() { // (3.17) try { ... aufruf(); // throws Ausnahme (wenn die Rekursion unterbrochen werden soll) ... rekursiv(); // ¬ ... } catch (Ausnahme ausnahme) {} // der letzte Aufruf soll "normal" zu Ende gehen }
import lehrbuch.*; // (3.18) public class EimerRekursiv extends java.applet.Applet { private void eimerRekursiv() { try { Eimer eimer = new Eimer(); eimer.anzeigen(); // throws Eimer.MehrAlsVierFehler nach 4. Aufruf eimerRekursiv(); // rekursiver Aufruf ¬ eimer.fuellen(); } catch (Eimer.MehrAlsVierFehler ausnahme) {} // letzter Aufruf ¬ ...
Abb. 3.7: Stapeln von rekursiven Aufrufen
Übung 3.7: Gestalten Sie das Programm (3.18) auf Seite 70 so um, dass Sie den Eimer vor dem rekursiven Aufruf füllen und danach entleeren. Beobachten und erklären Sie die Reihenfolge, wie die vier Eimer gefüllt und entleert werden.
Seite 71
Seite 72
Seite 72
import paket.unterpaket.Klasse;
import paket.unterpaket.*;
package paket.unterpaket; public class ...
Seite 73
public class AuchNichtLeer extends java.applet.Applet {} // (3.19)
Seite 73
public class GrussAusBayern extends java.applet.Applet { // (3.20) public void paint(java.awt.Graphics grafik) { grafik.drawString("Grüß Gott!", 50, 25); // ¬ } }
protected void drawString(String, int, int);
Seite 74
public class GrussAusBerlin { // (3.21) public static void main(String[] args) { java.lang.System.out.println("Tag!"); // java.lang kann weggelassen werden ¬ } }
java . lang . System . out . println ( "Tag!" ); Paket Unterpaket Klasse Referenz Methode Parameter
java GrussAusBerlin
public class EimerHauptprogramm { // (3.22) public static void main(String[] args) { try { lehrbuch.Eimer eimer = new lehrbuch.Eimer(); eimer.anzeigen(); eimer.fuellen(); } catch (lehrbuch.VollAusn ausnahme) {} } }
Übung 3.8: Entwickeln Sie eine Anwendung sowie ein Applet mit Hilfe von Standardpaketen, die drei Ihrer Lieblingssprüche (mit jeweils einer Leerzeile dazwischen) auf dem Bildschirm sichtbar macht. Experimentieren Sie beim Applet mit verschiedenen Positionen im Fenster und erklären Sie den Zusammenhang zum Applet-Klausel in der HTML-Datei.
Übung 3.9: Gestalten Sie Ihre Lösung der Übung 3.3 auf Seite 56 zu einem Hauptprogramm um. Denken Sie daran, dass aus der static-Methode main heraus nur static-Methoden aufgerufen und static-Referenzen angesprochen werden können.
Seite 75
Seite 76
import java.lang.*;
© APSIS GmbH |