Kapitel 1 STRUKTURIERUNG VON PROGRAMMEN Techniken der Programmentwicklung Prof. Dr. Wolfgang Schramm Übersicht 1 1. Strukturierung von Programmen 2. Vererbung 3. Abstrakte Klassen und Interfaces 4. Ausnahmebehandlung 5. Datenströme: die Java io-­Bibliothek 6. Multithreading 7. Innere Klassen 8. Collections 9. Generics 10. Reflection Lernziele des Kapitels 2 2 ¨ ¨ ¨ ¨ Sichtbarkeit und Lebensdauer von Variablen verstehen und unterscheiden. Nachvollziehen, dass Softwarestruktur organisiert werden muss und kennenlernen der Aufteilung in Pakete. Verstehen des Konzepts des Information Hiding und Umsetzung in Java. Grundlagen für Datenabstraktion/ Abstrakte Datentypen kennenlernen. Inhalt 3 o Lebensdauer und Sichtbarkeit von Variablen o Pakete ¤ Anlegen ¤ Export und Import von Namen ¤ Pakete und Verzeichnisse o Information Hiding o Datenabstraktion, Abstrakte Datentypen Variablen 4 o o o o Variablen dienen dazu, Daten im Hauptspeicher eines Programms abzulegen und gegebenenfalls zu lesen oder zu verändern. Instanzvariablen: werden im Rahmen einer Klassendefinition definiert und zusammen mit dem Objekt angelegt. Klassenvariablen: werden ebenfalls im Rahmen einer Klassendefinition definiert, existieren aber unabhängig von einem konkreten Objekt. Lokale Variablen: werden innerhalb einer Methode oder eines Blocks definiert und existieren nur dort. Variablen – Lebensdauer und Sichtbarkeit 5 Lokale Variablen o Sichtbar von der Stelle ihrer Deklaration bis zum Ende der Methode, in der sie deklariert wurden. Falls innerhalb eines Blocks lokale Variablen angelegt wurden, sind sie bis zum Ende des Blocks sichtbar. o Lebensdauer beginnt, wenn die zugehörige Deklarationsanweisung ausgeführt wird. Sie endet mit dem Ende des Methodenaufrufs. Falls innerhalb eines Blocks lokale Variablen angelegt wurden, endet ihre Lebensdauer mit dem Verlassen des Blocks. o Es ist in Java nicht erlaubt, lokale Variablen zu deklarieren, die gleichnamige lokale Variablen eines weiter außen liegenden Blocks verdecken. Das Verdecken von Klassen-­‐ oder Instanzvariablen ist dagegen zulässig. Instanzvariablen o Lebensdauer beginnt mit dem Zeitpunkt des Erzeugens einer neuen Instanz einer Klasse. Mit dem Zerstören des zugehörigen Objektes werden auch alle Instanzvariablen zerstört. o Sie sind innerhalb der ganzen Klasse sichtbar, solange sie nicht von gleichnamigen lokalen Variablen verdeckt werden. Klassenvariablen o leben während der kompletten Laufzeit des Programms. Die Regeln für ihre Sichtbarkeit entsprechen denen von Instanzvariablen. Variablen 6 o o o o Variablen dienen dazu, Daten im Hauptspeicher eines Programms abzulegen und gegebenenfalls zu lesen oder zu verändern. Instanzvariablen: werden im Rahmen einer Klassendefinition definiert und zusammen mit dem Objekt angelegt. Klassenvariablen: werden ebenfalls im Rahmen einer Klassendefinition definiert, existieren aber unabhängig von einem konkreten Objekt. Lokale Variablen: werden innerhalb einer Methode oder eines Blocks definiert und existieren nur dort. Variablen – Lebensdauer und Sichtbarkeit 7 Lokale Variablen o Sichtbar von der Stelle ihrer Deklaration bis zum Ende der Methode, in der sie deklariert wurden. Falls innerhalb eines Blocks lokale Variablen angelegt wurden, sind sie bis zum Ende des Blocks sichtbar. o Lebensdauer beginnt, wenn die zugehörige Deklarationsanweisung ausgeführt wird. Sie endet mit dem Ende des Methodenaufrufs. Falls innerhalb eines Blocks lokale Variablen angelegt wurden, endet ihre Lebensdauer mit dem Verlassen des Blocks. o Es ist in Java nicht erlaubt, lokale Variablen zu deklarieren, die gleichnamige lokale Variablen eines weiter außen liegenden Blocks verdecken. Das Verdecken von Klassen-­‐ oder Instanzvariablen ist dagegen zulässig. Instanzvariablen o Lebensdauer beginnt mit dem Zeitpunkt des Erzeugens einer neuen Instanz einer Klasse. Mit dem Zerstören des zugehörigen Objektes werden auch alle Instanzvariablen zerstört. o Sie sind innerhalb der ganzen Klasse sichtbar, solange sie nicht von gleichnamigen lokalen Variablen verdeckt werden. Klassenvariablen o leben während der kompletten Laufzeit des Programms. Die Regeln für ihre Sichtbarkeit entsprechen denen von Instanzvariablen. Warum Strukturierung von Software (Programmen)? 8 o o o o Die einzige Möglichkeit bei großen (aber auch kleinen) Softwaresystemen die Kontrolle zu behalten à Teilen und Herrschen (divide et impera). Die Software selbst und auch das Problem müssen in kleine, beherrschbare Teile zerlegt werden. Diese Teile müssen über definierte Schnittstellen miteinander interagieren. Diese Zerlegung erfolgt im allgemeinen auf mehreren Ebenen und mit jeder Ebene wird das Abstraktionsniveau angehoben. Programmelemente zur Strukturierung von Programmen 9 o Anweisungen o Blöcke o Methoden o Klassen o Pakete o Applikationen o Applets o Java-­‐Archive Anweisungen / Blöcke 10 o Anweisungen = elementarste ausführbare Programm-­‐ elemente; alle anderen Elemente bauen darauf auf. ¤ o Eine Anweisung kann eine Deklaration (z.B. for-­‐ Schleife) enthalten, einen Ausdruck auswerten oder den Programmablauf steuern. Block = Kollektion von Anweisungen, die nacheinander ausgeführt werden. ¤ Ein Block ist selbst eine Anweisung. Pakete 11 o o o o Werden nach Architekturgesichtspunkten angelegt. Strukturieren größere Softwaresysteme. Enthalten Klassen oder selbst wieder Pakete. Paket bildet eine Einheit, obwohl seine Klassen über viele Dateien verstreut sein können. Pakete 12 o Sammlung zusammengehöriger Klassen. o Jede Klasse in Java ist Bestandteil genau eines Pakets. o Schaffen einen eigenen Sichtbarkeitsbereich für Namen. o o o Paketnamen können aus mehreren Teilen bestehen und beliebig tiefe Hierarchien ausdrücken. Der Name einer Methode oder einer Variablen besteht grundsätzlich aus drei Elementen: ¤ Paketname(n) ¤ Klassen-­‐ oder Objektname ¤ Methoden-­‐ bzw. Variablenname Beispiel für einen Methodennamen ist java.lang.Math.sqrt. Anlegen von Paketen 13 o Paketnamen sind hierarchisch und durch Punkte getrennt. o Deklaration: package packageName; // erste Anweisung in der Quelldatei à o alle in der Datei deklarierten Klassen gehören zum angegebenen Paket. Ohne explizite Paketangabe ® Klassen der Datei werden einem namenlosen Standardpaket – dem default Paket – zugeordnet. Möglichst nicht verwenden!!! Pakete und Sichtbarkeit 14 o o Sichtbarkeitsgrenze: alles was zu einem Paket gehört, ist zunächst einmal lokal zu diesem Paket und in anderen Paketen unsichtbar. Klassen können explizit exportiert werden; sie werden dadurch für andere Klassen sichtbar. à Gezielte Festlegung von Schnittstellen zwischen Subsystemen. Export von Namen 1/2 15 o Was zu einem Paket gehört, ist außerhalb des Pakets unsichtbar. Beispiel: package myPackage; class C1 { kein Zugriff von außerhalb int i; void m () { ... } } class C2 { ... } ® ® In verschiedenen Paketen können die gleichen Namen verwendet werden. Namenskonflikte kann es nur innerhalb des „eigenen“ Pakets geben. Export von Namen 2/2 16 ¨ Namen können von einem à Zugriffsspezifizierer public Paket exportiert Beispiel: package myPackage; public class C1 { // Export von C1 public int i; // Export von i public void m (int a) { // Export von m int b; ... } public C1() { ... } // Export des Konstruktors int j; void n() { ... } C1(int i) { ... } // Lokaler Konstruktor } class C2 { ... } ¨ Export von: Klassen, Methoden, Feldern und Konstruktoren. werden: Qualifizierter Import von Namen 17 o Exportierte Klassen können in anderen Paketen importiert werden à qualifizierte Import-­‐Anweisung. Beispiel: package otherPackage; myPackage.C1 obj1; // Import der Klasse C1 aus myPackage obj1 = new myPackage.C1(); // Erzeugen eines Objekts von C1 obj1.m(4); // Zugriff auf importierte Methode m ... myPackage2.C1 obj2; // Import der Klasse C1 aus myPackage2 ¤ ¤ Durch Angabe des vollständigen Pfadnamens des Pakets, in welchem die Klasse deklariert ist, vor dem Klassennamen wird diese Klasse importiert. Der Klassenname wird immer mit dem vorangestellten Paketpfad verwendet. Expliziter Import von Namen 18 o Exportierte Klassen können in anderen Paketen importiert werden à explizite Import-­‐Anweisung. Beispiel: package otherPackage; import myPackage.C1; C1 obj1; obj1 = new C1(); obj1.m(4); ... myPackage2.C1 obj2; o o o o o o // // // // // Import der Klasse C1 aus myPackage Verwendung der Klasse C1 ohne Qualifikation Erzeugen eines Objekts von C1 Zugriff auf importierte Methode m qual. Import der Klasse C1 aus myPackage2 Die import-­‐Zeile muss direkt auf die package-­‐Zeile folgen. Für jede Klasse wird eine import-­‐Zeile benötigt. Die Klassen in den import-­‐Zeilen müssen mit ihrem Paketnamen qualifiziert werden. Alle Klassen in import-­‐Zeilen müssen verschiedene Namen haben. Alle Klassen eines Pakets können mit einer einzigen Zeile importiert werden: import packagename.*; Geltungsbereich einer import-­‐Zeile: Datei-­‐ nicht das ganze Paket! Import statischer Datenelemente und Methoden 19 o Þ Seit Java 5 mit spezieller import-­‐Anweisung. Man kann statische Variablen oder Methoden anderer Klassen ohne Angabe des Klassennamens verwenden. Syntax: import static <Qualifier>.<Name> Beispiel: Import aller statischen Elemente der Klasse Math import static java.lang.Math.* class Sample { void foo() { ... double x = sin(PI); // sin, PI, sqrt und E sind aus Math double y = sqrt(E); // können ohne // benutzt werden Qualifizierung Organisation von Paketen 1/2 20 o Abbildung: Klassen ® Dateien und Pakete ® Verzeichnisse mit folgenden Regeln: ¤ ¤ Eine public Klasse C muss in einer Datei namens C.java implementiert werden. Alle Klassendateien (.class) eines Pakets p müssen in einem Verzeichnis namens p liegen. ¤ Die Quellecode-­‐Dateien (.java) sollten genauso organisiert sein. ¤ Beispiel: Paket p enthält Klassen A, B und C. p A.java B.java C.java IDEs (z.B. Eclipse) erledigen das für Sie. Organisation von Paketen 2/2 21 ¨ ¨ Zusammenfassung von Paketen zu größeren Paketen ist möglich. ¤ Pakethierarchie/ Hierarchie von Subsystemen ¤ Pakethierarchie wird auf die Verzeichnisstruktur übertragen. Beispiel: Paket p1 enthält Klassen A und B, Paket p2 enthält Klassen C und D. Die Pakete p1 und p2 werden zu einem neuen Paket p zusammengefasst. p package p.p1; p1 public class A; A.java B.java .... import p.p1.A; // Import von A // Angabe des gesamten Paketpfads import p.p1.*; // Import aller Klassen des Pfads p2 C.java D.java import p.*; // Vorsicht // Import aller Klassen von p Pakete der Java-­‐Bibliothek 22 o o o Die wichtigsten Klassen (Standardklassen) z.B. Object, String à java.lang. Alle Klassen des Pakets java.lang werden automatisch importiert. Weitere wichtige Pakete des JDK: java.io Paket zum Lesen und Schreiben von Datenströmen (Dateien, Bildschirmausgabe, Tastatureingabe). java.util Paket mit nützlichen Hilfsklassen (z.B. Random, Date) java.swing Paket für grafische Benutzeroberflächenelemente (Knöpfe, Textfelder, Fenster). Eindeutige Paketnamen 24 o Vermeidung Projekten. ¤ ¤ o Namenskollisionen bei sehr großen Viele Entwickler: jeder vergibt eigene Namen für „seine“ Klassen / Pakete. Verwendung einer großen Anzahl unterschiedlicher Klassen-­‐ bibliotheken von verschiedenen Herstellern. Vergabe von Paketnamen à angelehnt an das domain name system der Internet-­‐Adressen ¤ o von Domain-­‐Namen sind weltweit eindeutig Paketename = Domain-­‐Namen mit den Namensbestand-­‐ teilen in umgekehrter Reihenfolge. ⇒Namenskollisionen zwischen Paketen unterschiedlicher Hersteller werden vermieden. o Beispiel: diverse Pakete des JDK com.oracle.* Eine weitere Organisationseinheit: Java Archive 26 o o Java Archive (JAR) – Sammlung von (mehreren) Java-­‐Klassen (und Hilfsdateien) im ZIP-­‐Format gepackt. Vorteile ¤ Komprimiert à weniger Speicherplatzverbrauch. ¤ Eine einzelne Datei (statt viele) à mehr Ordnung. ¤ ¤ ¤ Zusammengehörige Klassen à gruppieren. Kann direkt ausgeführt werden (java −jar <Archivname>.jar). Können genauso auf den Klassenpfad gelegt werden, wie Verzeichnisse mit Klassen. jar -­‐ Vorteile 27 o o o Sicherheit: Man kann den Inhalt digital signieren und so vor Veränderungen schützen. Sealing: Man kann festlegen, dass alle Klassen eines Paketes in einem JAR enthalten sein müssen, d.h. es können keine Klassen eingeschmuggelt werden. Versionierung: In den Metadaten der JAR-­‐Datei kann man Versionsinformationen für die enthaltenen Klassen ablegen. jar-­‐Dateien erzeugen 28 o o o Programm jar erzeugt *.jar-­‐Dateien: jar −cvf <archivname>.jar. Syntax ist ähnlich zum Unix-­‐Kommando tar. Zusätzlich zu den eingepackten Dateien enthält ein JAR Metadaten im META−INF-­‐Verzeichnis. Einbinden von Archiven 29 o Eigene und fremde JARs können eingebunden werden ¤ ¤ in Eclipse in den Projekteigenschaften unter „Java Build Path“ und Libraries. auf der Kommandozeile bei Compiler und Java-­‐VM über die Option −classpath. Einbinden von jar-­‐Dateien in Eclipse 30 Information Hiding / Datenabstraktion 31 o o Wichtiges Prinzip der Softwaretechnik. Hilft, die Komplexität großer Programm in den Griff zu bekommen. Verstecke die Implementierung komplexer Daten in einer Klasse und erlaube den Zugriff ausschließlich über Methoden dieser Klasse. Es interessiert nur WAS man mit den Daten macht, aber nicht WIE sie implementiert sind. Probleme des direkten Zugriffs auf Daten 32 o Beim Zugriff auf eine komplexe Datenstruktur (z.B. Baum, Graph) muss man sonst die Struktur der Daten genau kennen, z.B. um Ketten von Referenzen zu durchlaufen, um die gewünschten Daten zu erhalten. ® Kompliziert und fehleranfällig. ® Unerwünschte enge Kopplung zwischen der Klasse und ihren Klienten. o Datenstrukturen, auf die direkt zugegriffen wird, kann man nur schwer ändern, denn man muss dann auch alle zugreifenden Programme ändern. ® Wenn die Zugriffe über ein ganzes Programm verteilt sind, ist es schwierig, alle zu finden. Alles was man übersieht, führt zu Fehlern. Datenabstraktion durch eine Klasse 33 Zugriffsmethoden (Klassenschnittstelle) Daten Klasse Abstrakter Datentyp (ADT) Vorteile der Datenabstraktion 34 o o o Das Arbeiten mit den Daten wird für Klienten einfacher, weil sie von den Implementierungsdetails abstrahieren können und nur mit den Zugriffsmethoden arbeiten. Die Implementierung der Daten kann jederzeit geändert werden, ohne dass die Klienten ihre Programme ebenfalls ändern müssen -­‐ solange die Schnittstelle unverändert bleibt. Versteckte Daten sind vor mutwilliger oder versehentlicher Zerstörung durch andere Klassen geschützt Datenabstraktion in Java 35 o Erfolgt mittels bestimmter Modifier (auch Zugriffs-­‐ oder Sichtbarkeitsattribute genannt), welche die Sichtbarkeit von Datenelementen und Methoden festlegen: ¤ public private ¤ protected (nur im Zusammenhang mit Vererbung) ¤ ohne Modifier: Standard-­‐Sichtbarkeit oder package scoped ¤ Hinweis: ¤ n n Die Modifier beziehen sich auf Datenelemente und Methoden von Klassen. Klassen selbst haben andere Modifier. Sichtbarkeitsattribute in Paketen 36 Beispiel Sichtbarkeit int i; Die Namen sind in allen Klassen des deklarierenden Pakets sichtbar. void m() { ... } public int i; public void m() { ... } protected int i; protected void m() { ... } private int i; private void m() { ... } Die Namen sind in allen Paketen sichtbar, die die deklarierende Klasse importieren. Ebenso im deklarierenden Paket. Die Namen sind in allen Unterklassen sowie in allen Klassen des deklarierenden Pakets sichtbar. Die Namen sind nur in der deklarierenden Klasse sichtbar und sonst nirgendwo. Sichtbarkeit und Vererbung: Einschränkungen der Sichtbarkeit 37 o In Java kann eine Klasse die Sichtbarkeit von Methoden ihrer Oberklasse(n) nicht einschränken. o Beispiel: abstract public } class GeometrischeFigur void verschiebe ( ... ) { ... { } class Kreis extends GeometrischeFigur { private void verschiebe ( ... ) { ... } } private verursacht einen Compiler-­Fehler! Grund (im Beispiel): Sonst würde Code, der mit Objekten der Klasse GeometrischeFigur funktioniert hat, mit Kreis-­‐Objekten nicht mehr funktionieren! Sichtbarkeit und Vererbung: Erweiterung der Sichtbarkeit 38 o In Java kann eine Klasse die Sichtbarkeit von Methoden ihrer Oberklasse(n) (indirekt) erweitern. o Realisierungs-­‐Idee: Eine Unterklasse überdeckt eine ererbte Methode durch eine Methode mit erweiterten Rechten und ruft die ererbte auf (auf die sie Zugriff hat). o Beispiel: class GeometrischeFigur { protected void verschiebe ( ... ) { ... } } class Kreis extends GeometrischeFigur { public void verschiebe ( ... ) { super.verschiebe( ... ); } } Ergebnis: Für Instanzen der Klasse Kreis ist der Code der eingeschränkt sichtbaren Methode verschiebe() aus der Klasse GeometrischeFigur nun verfügbar. o Noch Fragen ? 39