Folien ohne Skriptkommentare
Transcription
Folien ohne Skriptkommentare
Grundlagen Programmierung Prof. Dr. Stephan Kleuker Hochschule Osnabrück Grundlagen Programmierung Stephan Kleuker 1 Ich • Prof. Dr. Stephan Kleuker, geboren 1967, verheiratet, 2 Kinder • seit 1.9.09 an der HS, Professur für Software-Entwicklung • vorher 4 Jahre FH Wiesbaden • davor 3 Jahre an der privaten FH Nordakademie in Elmshorn • davor 4 ½ Jahre tätig als Systemanalytiker und Systemberater in Wilhelmshaven • [email protected], Raum SI 0109 Grundlagen Programmierung Stephan Kleuker 2 Studium (meine Sicht) (1/2) • Sie haben ersten wichtigen Schritt geschafft, sich für ein praxisnahes, qualitativ hochwertiges Studium entschieden • Sie haben sich für einen recht schweren, sehr interessanten, sehr abwechslungsreichen und prägenden Bildungsweg entschieden • Studium an der Hochschule: Konzepte, Vorgehensweisen, weniger Visionen, mehr Zusammenhang zur Praxis • Versuchen, frühzeitig mit der Praxis in Kontakt zu kommen Grundlagen Programmierung Stephan Kleuker 3 Studium (meine Sicht) (2/2) • Die nächsten drei+x Jahre werden prägend sein, nicht so wie die vorherigen Jahre, aber mehr als alle folgenden • Sie werden viele Hochs und einige Tiefs erleben • Lernen Sie, in Gruppen zu arbeiten • Machen Sie sich nichts vor, seien aber nicht zu selbstkritisch • Menschen verändern sich • Versuchen sie, Ratschläge anzunehmen und zu suchen Grundlagen Programmierung Stephan Kleuker 4 Ablauf • 4h Vorlesung + 4h Praktikum = 10 CP d. h. etwa 300 Arbeitsstunden • Praktikum : – Anwesenheit = (Übungsblatt vorliegen + Lösungsversuche zum vorherigen Aufgabenblatt) – Übungsblätter mit Punkten ( 200), zwei Studis als Team – Praktikumsteil mit 160 oder mehr Punkten bestanden • Prüfung: Klausur recht kurz nach der Vorlesungszeit • Folienveranstaltungen sind schnell, bremsen Sie mit Fragen • von Studierenden wird hoher Anteil an Eigenarbeit erwartet Grundlagen Programmierung Stephan Kleuker 5 Vorlesung • Interaktiver Unterricht • vorbereite Folien und Beispielvorführungen am Rechner • Sie können, sollen und müssen Fragen stellen und Fragen beantworten • Vorlesungsteilnahme ist freiwillig; hilft auch, Lernen zu strukturieren • falls man nicht zur Vorlesung kommt: – unbedingt Folien nacharbeiten und Fragen im Praktikum oder am Anfang der nächsten Vorlesung stellen – Folien reichen nicht! Vertiefende Literatur parallel lesen (soll das ersetzen, was Dozent zu den Folien erzählt) Grundlagen Programmierung Stephan Kleuker 6 Praktikum • vorgegebene Aufgaben in einem vorgegebenen Zeitraum bearbeiten • Aufgaben bereiten Vorlesungsstoff nach, vertiefen ihn, ergänzen ihn und fordern selbstständige Einarbeitung • im Praktikum werden neue Aufgaben vorgestellt • im Praktikum werden Lösungen der Aufgaben abgenommen (oder Verbesserungen gefordert) • im Praktikum sind immer zwei betreuende Personen anwesend, die man befragen kann • im Praktikum herrscht Anwesenheits- und Aktivitätspflicht (Bearbeitung der Aufgaben, daddeln im Netz ist keine Anwesenheit) • Gesamtaufwand für Praktikumsaufgaben für durchschnittlich guten Studierende: 8+x h pro Woche !! Grundlagen Programmierung Stephan Kleuker 7 Praktikumsorganisation • Anmeldung über Stud.IP für Vorlesung und die Praktika (nur Vorlesungsordner relevant) • Informationen auch auf (Stud.IP ist Hauptquelle): http://home.edvsz.hs-osnabrueck.de/skleuker/WS11_GP/index.html • Aufgabenblätter online bis Freitag KW x • Vorstellung und betreute Bearbeitung in KW x+1 • Abnahme im ersten Praktikum KW x+2 (zweites Praktikum für restliche Abnahmen und geforderte Nacharbeiten) Grundlagen Programmierung Stephan Kleuker 8 Hardware / Software Software • Java SE in der Version 6 (http://www.oracle.com/technetwork/java/javase/downloa ds/jdk-6u27-download-440405.html) • Entwicklungsumgebung BlueJ 3.0.5 (http://www.bluej.org/download/download.html) • Programm für Screenshots, z. B. FastStone Capture 5.3 Hardware • im EDVSZ stehen Rechner zur Verfügung, im Praktikum "virtuelle Rechner", großer Vorteil; Rechner immer mit Internet-Verbindung nutzbar (https://sgd-02.edvsz.hsosnabrueck.de/) • es dürfen eigene Rechner genutzt werden Grundlagen Programmierung Stephan Kleuker 9 Klausur • ausgewählte vergleichbare Aufgaben in Praktika behandelt • Orientierung an Beispielklausur möglich • selbständiges Erklären von Fachbegriffen • was passiert in gegebenen Programmfragmenten • programmieren auf dem Papier Grundlagen Programmierung Stephan Kleuker 10 Verhaltenscodex • • • • Rechner sind zu Beginn der Veranstaltung aus Handys sind aus Wir sind pünktlich Es redet nur eine Person zur Zeit • Sie haben die Folien zur Kommentierung in der Vorlesung vorliegen, zwei Tage vor VL abends mit Aufgaben im Netz, Aufgabenzettel liegen in der Übung vor (Ihre Aufgabe) http://www.edvsz.hs-osnabrueck.de/skleuker/index.html • Probleme sofort melden • Wer aussteigt, teilt mit, warum Grundlagen Programmierung Stephan Kleuker 11 Voraussetzungen • außer Hochschulreife, keine beinhaltet • Fähigkeit, selbständig den Lernprozess zu organisieren • Fähigkeit zur Gruppenarbeit • Fähigkeit, Zeiten von konzentrierten Arbeiten und freier Zeitgestaltung zu trennen und zu koordinieren • Fähigkeit zur kritischen Selbstreflexion zum erreichten Lernstand • Kopieren ist nicht Kapieren • Vorlesungen und Praktika als Lernunterstützung zu sehen (Sie sind für Lernerfolg allein verantwortlich) • Umgang mit Windows-PC Grundlagen Programmierung Stephan Kleuker 12 Ziele • Konzepte der objektorientierten Programmierung verstehen und selbstständig nutzen • Eigenständig einfache OO-Programme entwickeln, Fehler beseitigen und längerfristig wartbar machen • Erlernen eines systematischen inkrementellen Prozesses zur Entwicklung größerer Programme • Erlernen und Anwenden der Fachbegriffe rund um die Programmierung • Verständnis für die grundsätzlichen Schritte vom Programmcode zur Ausführung auf dem Rechner Grundlagen Programmierung Stephan Kleuker 13 Literatur • Vorlesung orientiert sich nicht genau an einem Buch sicherlich sehr hilfreich: • David J. Barnes , Michael Kölling, Java lernen mit BlueJ: Eine Einführung in die objektorientierte Programmierung, 4. Auflage, Pearson Studium, 2009 ab Mitte des Semesters hilfreich: • Cornelia Heinisch, Frank Müller-Hofmann, Joachim Goll, Java als erste Programmiersprache, 6. Auflage, Vieweg+Teubner, 2011 • Sven Eric Panitz, Java will nur spielen, 2. Auflage, Vieweg+Teubner, 2011 • Christian Ullenboom, Java ist auch eine Insel, 9. Auflage, Galileo Computing, 2011 (auch: http://openbook.galileocomputing.de/javainsel/) • Guido Krüger, Thomas Stark, Handbuch der Java-Programmierung, 6. Auflage, Addison-Wesley, 2009 (5. Auflage auch unter http://www.javabuch.de/download.html) • Dietmar Abts, Grundkurs JAVA: Von den Grundlagen bis zu Datenbank und Netzanwendungen, Vieweg+Teubner, 2010 Grundlagen Programmierung Stephan Kleuker 14 Programmiersprache • Programmiersprache ist Hilfsmittel beim programmieren lernen • hier kein Java-Kurs, sondern Programmierkurs mit Java • keine auch nur annähernd vollständige Einführung in Java (für Expertise wichtige Programmkonstrukte fehlen) • Konzept: – erstes Semester grundlegende Ideen – zweites Semester, weitere Ideen mit neuen Programmiersprachen (C und C++) • Ziel: ab ende des zweiten Semesters grundsätzlich in der Lage sich in fehlende Sprachkonstrukte und andere ähnlich strukturierte Programmiersprachen einzuarbeiten (5. + 6. Semester: Projekte und Bachelor-Arbeiten auch in C#) Grundlagen Programmierung Stephan Kleuker 15 Hintergrund von Programmiersprachen (Ausschnitt) Sprache Fortran Cobol Smalltalk C seit basiert auf 1954 1959 1972 Simula 1972 Algol Anwendungsgebiet math. Berechnungen Banken, Versicherungen Einführung Objektorientierung Betriebssysteme, HW-nahe Programmierung Objective C 1981 C, Smalltalk jetzt: Apple-Apps C++ 1983 C C mit OO, Graphik, Bildverarbeitung, … PHP 1995 C, Pascal, einfache kleine Webgehacke Applikationen Java 1995 Smalltalk, große sklalierbare WebC++ Applikationen (ERP), Standardprogramme C# 2001 Java, C Java von Microsoft Grundlagen Programmierung Stephan Kleuker 16 Inhalt (Planung) (1/2) Programmierung Objekt Klasse Klassen in Java Konstruktor Objektmethoden Debugger Objektweitergabe Alternative equals Strings Schleife switch Schleife 2 Algorithmus geschachtelte Schleifen Grundlagen Programmierung Iterator for Unit Test iterativ inkrementelle Entwicklung Vererbung Überschreiben dynamische Polymorphie casten abstrakte Klasse Interface Kommentare Mehrfachvererbung Klasse Object Collection Framework Array Stephan Kleuker 17 Inhalt (Planung) (2/2) Exception Klassenvariablen und Klassenmethoden Start von Java-Programmen Entwicklungsumgebungen Pakete Einige Realitätsbeichten Programmieren ohne echte Klassen Laden und Speichern Aufzählungen Frames, Knöpfe und Ereignisbehandlung Layout, hierarchischer GUI-Aufbau Rundflug über GUI-Bausteine Grundlagen Programmierung Stephan Kleuker 18 Programmierung Grundlagen Programmierung Stephan Kleuker 19 Worum geht es bei der Programmierung • Menschen haben Maschinen erfunden, um sich das Leben leichter zu machen und Geld zu verdienen • Computer wurden entwickelt als flexible Maschinen, die Rechenaufgaben lösen und andere Maschinen steuern können • Computer sind dumm, ihnen muss beigebracht werden, was sie wann zu tun haben • Dieses „was wann“ heißt Computer-Programm • Computer-Programme müssen entwickelt werden, dies ist ein Ziel dieser Veranstaltung • Zentral für die Programmierung ist das Vorgehen zur Entwicklung einer Lösung, eines Algorithmus Grundlagen Programmierung Stephan Kleuker 20 Worum geht es in Programmen • Es werden Informationen verwaltet, d. h. angelegt, gesucht und gelesen, verändert und gelöscht • Es werden neue Ergebnisse berechnet, indem vorhandene Informationen genutzt werden • Beispiel: Studierende in einer Studierendenverwaltung, Prognose der Studienbeiträge der nächsten Semester • Informationen müssen in das System, z. B. Studierende in den Rechner • gesucht ist ein Modell der Realität, so dass gewünschte Berechnungen möglich werden • man kann relevante Daten des Studierenden-Modells festlegen (Vorname, Nachname, Geburtstag, Studiengang, …) • man kann Hilfsdaten einführen: Matrikelnummer Grundlagen Programmierung Stephan Kleuker 21 Wie finde ich die Programmieraufgabe Anforderungsanalyse • systematische Analyse der Kundenherausforderungen • Strukturierung der gewünschten Funktionalität in Hauptaufgaben • Klärung der relevanten Informationen zur Modellierung • Klärung vieler Randbedingungen • genauer: siehe Vorlesung Objektorientierte Analyse und Design • für diese Veranstaltung werden die Aufgabenstellungen als klar gegeben angenommen (große Vereinfachung) Grundlagen Programmierung Stephan Kleuker 22 Wie fange ich die Programmierung an • zunächst klären, ob Aufgabenstellung verstanden • dann Entwicklungsplanung; typisch inkrementell – Aufteilung in Teilaufgaben – Für jede Teilaufgabe das erwünschte typische Verhalten realisieren (z. B. Daten eines neuen Studierenden eintragen und speichern) – Über alle möglichen Alternativen beim Verhalten nachdenken und diese schrittweise einbauen (z. B. Abbruch der Dateneintragung, Fehler beim Speichern) • Grundlage ist dabei ein Modell der gewünschten Daten, die für die Teilaufgabe benötigt werden • Bei Datenmodellen spricht man oft von Objekten (Individuen mit konkreten Eigenschaften) Grundlagen Programmierung Stephan Kleuker 23 Objekt Grundlagen Programmierung Stephan Kleuker 24 Was heißt Objektorientierung • in Programmen werden Daten verarbeitet und neue berechnet • Daten gehören zu Objekten • Objekte sind damit eine Grundstruktur von Programmen Objekt: • eindeutig einzeln identifizierbar • besitzt konkrete Eigenschaften, die es charakterisieren • !!! Objekte können die gleichen Eigenschaften haben und trotzdem unterschiedlich sein (dasselbe oder das Gleiche, Sie besuchen dieselbe Veranstaltung, Sie sitzen auf gleichen Stühlen) Grundlagen Programmierung Stephan Kleuker 25 Objekt Adresse • jedes Haus hat eine eindeutige Adresse Eigenschaften: zwei Beispielobjekte: • Straße Barbarastr. • Hausnummer 16 • Postleitzahl 49076 • Stadt Osnabrück • Bundesland Niedersachsen Barbarastr. 16 79106 Freiburg Baden-Württemberg • es kann optionale Eigenschaften geben, bzw. Eigenschaften, die nur im bestimmten Kontext interessieren • (Stockwerk) • (Grundstücksgröße) Grundlagen Programmierung Stephan Kleuker 26 Objekt Datum • • • • • Identifiziert einen eindeutigen Tag Eigenschaften: zwei Beispielobjekte: Tag 31 29 Monat 12 2 Jahr 1999 2100 • Man spürt den Wunsch, die Gültigkeit von Objekten zu prüfen (-> später) Grundlagen Programmierung Stephan Kleuker 27 Objekt Person • jede(r) ein Individuum Eigenschaften: zwei Beispielobjekte: • Vorname Stephan • Nachname Dr. Kleuker • Geburtsort Wilhelmshaven • Geburtsland Deutschland • Adresse Grundlagen Programmierung Stephan Kleuker Eva Mustermann Wladiwostok Russland 28 Objekt Student • jede(r) ein Individuum Eigenschaften: zwei Beispielobjekte: • Vorname Stephan • Nachname Kleuker • Geburtsort Wilhelmshaven • Geburtsland Deutschland • Adresse … • eingeschrieben am 14.9.1986 • Studiengang Informatik • Matrikelnummer 368849 Grundlagen Programmierung Stephan Kleuker Eva Mustermann Wladiwostok Russland … 29.7.2011 Informatik 424142 29 Objekt Praktikum • individuell durch seine Eigenschaften Eigenschaften: Beispielobjekt: • Modul Grundlagen Programmierung • Semester Wintersemester 2011 • Veranstalter Stephan Kleuker • Mitarbeiter Friedhelm Tappe • Termine [Mo 6:30-8:00, Fr 18:30-20:00] • Teilnehmer [Eva Mustermann (424142), Kevin Meier (424345), Jaqueline Schmidt (422323)] • man erkennt, dass Eigenschaften mehrwertig sein können Grundlagen Programmierung Stephan Kleuker 30 Erkenntnisse über Objekte • können große Anzahl von Eigenschaften haben • relevante Eigenschaften hängen vom Anwendungsbereich ab (Erkennung durch Haarfarbe für Studierende irrelevant) • Eigenschaften können optional sein • Eigenschaften können wieder selbst Objekte sein • kann unterschiedliche Objektarten mit vielen Gemeinsamkeiten geben • Eigenschaft kann eine Sammlung von Werten sein, die leer sein kann Person Student Praktikum Grundlagen Programmierung Adresse Stephan Kleuker 31 Klasse Grundlagen Programmierung Stephan Kleuker 32 Der Begriff der Klasse • Objekte haben durch ihre Eigenschaften gleichartige Struktur • eine Klasse fasst diese Struktur zusammen und benennt die Eigenschaften Adresse strasse hausnummer postleitzahl stadt bundesland Praktikum modul semester veranstalter mitarbeiter termine teilnehmer • strasse ist eine Objektvariable (Name einer Eigenschaft, die für ein konkretes Objekt eine konkreten Wert annimmt) • Objektvariable = Instanzvariable = Attribut Grundlagen Programmierung Stephan Kleuker 33 Wie finde ich die Objektvariablen • Zentrale Teilaufgabe ist das Verstehen der Kundenwünsche • Das Verfahren zum Finden der Wünsche heißt Anforderungsanalyse (Regel: garbage in, garbage out) • Ansätze zur Anforderungsanalyse werden in der Vorlesung "Objektorientierte Analyse und Design" studiert • Basierend auf Gesprächen, Texten, Erfahrungen, … werden u.a. die projektrelevanten Objekte und ihre Objektvariablen als ein Analyseergebnis bestimmt Grundlagen Programmierung Stephan Kleuker 34 Zusammenhang zwischen Klasse und Objekten • Objekte haben eine Klasse als Grundlage • Klassen sind der Rahmen (das Formular), das durch Objekte individuell gefüllt wird • Objekte weisen den Objektvariablen konkrete Werte zu • Darstellung <Objektname>:<Klasse> ich:Adresse strasse= „Barabarastr.“ hausnummer= 16 postleitzahl= 49076 stadt= „Osnabrück“ bundesland=„Nds“ Grundlagen Programmierung nichtIch:Adresse strasse= „Barabarastr.“ hausnummer= 16 postleitzahl= 79106 stadt= „Freiburg“ bundesland=„B-W“ Stephan Kleuker 35 Typen : Integer • Variablen werden in Programmiersprachen oft Typen zugeordnet • Ein Typ definiert, welche Werte erlaubt sind und wie diese Werte dargestellt werden (korrekte Syntax) • Beispieltyp: Integer für ganze Zahlen – genauer: Wertebereich -231 bis 231-1 – Folge von einzelnen Ziffern, beginnend mit einer Zahl ungleich 0 [stimmt nicht ganz, siehe später] • Man kann Variablen vom Typ deklarieren Integer tag • Man kann Variablen erlaubte Werte zuweisen tag = 30 • Hinweis: Gibt auch Programmiersprachen, die ohne Typen auskommen Grundlagen Programmierung Stephan Kleuker 36 Nutzung von Typen • JEDER Objektvariablen wird ein Typ zugeordnet • Typen werden in der Klassendefinition festgehalten class Datum Integer Integer Integer } { tag; monat; jahr; • Typ kann diskutabel sein (möchte man den Monatsnamen?) • Für später: Jede Klasse definiert wieder einen Typen Grundlagen Programmierung Stephan Kleuker 37 Klassen in Java Grundlagen Programmierung Stephan Kleuker 38 Klassen in Java (Syntax) (1/3) class Datum Integer Integer Integer } { tag; monat; jahr; • Java hat Schlüsselworte (z. B. class), diese dürfen z. B. nicht als Variablennamen genutzt werden • viele Programmfragmente stehen in geschweiften oder runden Klammern (immer beide Klammern eintippen, dann füllen) • geschweifte Klammer am Anfang und am Ende der Klassendefinition • Befehle enden mit einem Semikolon • Objektvariable: <Typ> <Name>; Grundlagen Programmierung Stephan Kleuker 39 Klassen in Java (Syntax) (2/3) Variablen- und Klassennamen • beginnen mit einem Groß- oder Kleinbuchstaben (keine Umlaute oder Sonderzeichen) • werden gefolgt von beliebig vielen Groß- und Kleinbuchstaben, Ziffern oder _ keine Syntax, aber Konvention • Klassennamen groß, typisch Einzahl (Student nicht Studenten) • Objektvariablen klein, vollständige Worte, "sprechend" • bei zusammengesetzten Namen werden zweite Worte direkt hinten an gefügt und erster Buchstabe großgeschrieben, z. B. eingeschriebenAm, richtig: starttermin falsch: startTermin • eine Objektvariable pro Zeile deklarieren Grundlagen Programmierung Stephan Kleuker 40 Klassen in Java (Syntax) (3/3) • Platzierung von Klammern class Datum Integer Integer Integer } { tag; monat; jahr; • oder class Datum { Integer tag; Integer monat; Integer jahr; } • auf Folien obige Variante (spart eine Zeile) • Einrückungen haben keine semantische Bedeutung Grundlagen Programmierung Stephan Kleuker 41 Syntaxprüfung durch Compiler (genauer Parser) • Programmcode muss so übersetzt werden, dass der Computer Befehle verarbeiten kann (-> genauer später), Prozess heißt kompilieren (übersetzen) • Vor dem Kompilieren (genauer: erster Schritt dabei), wird die Syntax des Programms geprüft (geparst) • Bei fehlerhafter Syntax wird mehr oder minder genaue Fehlermeldung ausgegeben • Kompilierung findet typischerweise in einer Entwicklungsumgebung statt (wir nutzen zunächst BlueJ) Grundlagen Programmierung Stephan Kleuker 42 Erfolgreiche Kompilierung in BlueJ Grundlagen Programmierung Stephan Kleuker 43 Fehlerhafte Kompilierung in BlueJ Grundlagen Programmierung Stephan Kleuker 44 Woher kommen die Klassen • Java bietet bereits viele Klassen als Typen an, in der sogenannten Klassenbibliothek • Das Wissen über existierende Klassen ist Teil der Programmiererfahrung • In der Programmierausbildung werden teilweise existierende Klassen neu programmiert um Erfahrungen zu sammeln • Übersicht bietet u. a. Java Dokumentation (lokal herunterladbar) oder auch http://download.oracle.com/javase/6/docs/api/index.html • Woher die Objekte kommen bleibt noch unklar Grundlagen Programmierung Stephan Kleuker 45 Wichtige Klassen (Ausschnitt) Klasse Integer Double String Boolean Long ArrayList< Typ > ArrayList<String> Grundlagen Programmierung Nutzung ganze Zahlen Fließkommazahlen Texte, Werte stehen in Hochkommata Wahrheitswerte, false, true ganze Zahlen, größerer Zahlenbereich (mehr Speicher) Sammlung von beliebig vielen Objekten eines Typs; nicht einfach hinschreibbar; merkt sich die Reihenfolge beliebig viele Texte, z. B. ["Hai", "Wo", "Da"] Stephan Kleuker 46 Ordnung in der Klassenbibliothek • ähnlich wie große Dateimengen in Ordnern und Unterordnern strukturiert werden, werden auch Klassen in Ordnerstrukturen thematisch abgelegt • Ordner heißen in Java Pakete • Beispiel: Die Klasse ArrayList liegt im Paket java.util • Damit Klasse aus Paket genutzt werden kann, muss die Klasse im Quellcode bekannt gemacht werden, vor der Klasse steht dann import java.util.ArrayList • andere Basisklassen wie Integer und String liegen in java.lang (da häufig genutzt, müssen die Klassen nicht importiert werden) Grundlagen Programmierung Stephan Kleuker 47 Erstellung der Klasse Adresse (1/2) ich:Adresse strasse= „Barabarastr.“ hausnummer= 16 postleitzahl= 49076 stadt= „Osnabrück“ bundesland=„Nds“ Grundlagen Programmierung • jeweils genau einen Typ überlegen • Ist hausnummer vom Typ Integer? Nein, Nummern wie 16a wären syntaktisch nicht korrekt • Ist Postleitzahl vom Typ Integer? Nein, führende Nullen nicht möglich 04356 Leipzig • Fazit: Bild links ist falsch Stephan Kleuker 48 Erstellung der Klasse Adresse (2/2) Grundlagen Programmierung Stephan Kleuker 49 Klasse Student Grundlagen Programmierung Stephan Kleuker 50 fehlerhafte Klasse Praktikum Grundlagen Programmierung Stephan Kleuker 51 korrigierte Klasse Praktikum Grundlagen Programmierung Stephan Kleuker 52 Seltener genutzte Variante ohne import class Praktikum { String modul; String semester; String veranstalter; String mitarbeiter; java.util.ArrayList<String> termine; java.util.ArrayList<Student> teilnehmer; } Grundlagen Programmierung Stephan Kleuker 53 Projektübersicht in BlueJ Grundlagen Programmierung Stephan Kleuker 54 Konstruktor Grundlagen Programmierung Stephan Kleuker 55 Wie erstellt man Objekte • mit den bisher erstellten Code kann man genau genommen nichts anfangen, da Klassen nur Rahmen sind • zur Erzeugung eines Objektes muss es die Möglichkeit geben zu sagen: "Erzeuge mir ein Objekt dieser Klasse" • gut wäre: zusätzlich mitteilen, welche Werte die Objektvariablen annehmen sollen • Lösung heißt Konstruktor Grundlagen Programmierung Stephan Kleuker 56 Beispiel eines Konstruktors class Datum{ Integer tag; Integer monat; Integer jahr; Datum (Integer anInt1, Integer anInt2, Integer anInt3){ this.tag = anInt1; this.monat = anInt2; this.jahr = anInt3; } } Grundlagen Programmierung Stephan Kleuker 57 Aufbau eines Konstruktors • hat exakt gleichen Namen wie Klasse • hat immer Parameterliste in runden Klammern • Parameterliste besteht aus mit Komma separierten Parametern • Parameter hat Aufbau: <Typ> <Parametername> • im Konstruktorrumpf steht ein kleines Programm mit Zuweisungen • Zuweisung: <Variable> = <Ausdruck> • Der Variablen auf der linken Seite wird der Wert der Berechnung des Ausdrucks der rechten Seite zugewiesen • Zugriff auf Objektvariablen über this.<Variablenname> • this ist "dieses Objekt", aus Objektsicht "ich" Grundlagen Programmierung Stephan Kleuker 58 Parameter können beliebige Namen haben Datum (Integer tag, Integer monat, Integer jahr){ this.tag = tag; this.monat = monat; this.jahr = jahr; } • wenn Parameter gleiche Namen wie Objektvariablen haben, ist ihr Sinn aus der Signatur (erste Zeile des Konstruktors) ablesbar • mit this wird der Bezug auf das gerade betrachtete Objekt deutlich • Syntaxregeln für Variablennamen wieder beachten Grundlagen Programmierung Stephan Kleuker 59 Nutzung eines Konstruktors direkt in BlueJ Grundlagen Programmierung Stephan Kleuker 60 Analyse eines Objekts in BlueJ Grundlagen Programmierung Stephan Kleuker 61 Erstellung eines Objekts im Code Pad – Variante 1/3 <Return gedrückt> kleines Objekt am Rand kleines rotes Objekt am Rand auf Objektleiste ziehen Objekt benennen und wie vorher analysieren Grundlagen Programmierung Stephan Kleuker 62 Erstellung eines Objekts im Code Pad – Variante 2/3 • Anlegen einer lokalen Variable namens rente (wieder mit Typ) • Zeilenende mit Semikolon, mit Return in nächste Zeile • Objektname ohne Semikolon eintippen (Return) • gefordertes Objekt wird rechts am Rand angezeigt • Bearbeitung wie vorher Grundlagen Programmierung Stephan Kleuker 63 Klasse kann mehrere Konstruktoren haben Datum (Integer tag, Integer monat, Integer jahr){ this.tag = tag; this.monat = monat; this.jahr = jahr; } Datum (Integer jahr){ this.tag = 1; this.monat = 1; this.jahr = jahr; } • Konstruktoren müssen nur andere Parameterlisten (Typen, Variablennamen reichen nicht) haben Grundlagen Programmierung Stephan Kleuker 64 Konstruktoren nutzen Konstruktoren • nulltes Informatikgebot: Schreibe niemals Code doppelt • Konstruktor kann anderen Konstruktor nur in erster Zeile aufrufen, Variante der letzten Folie: Datum (Integer tag, Integer monat, Integer jahr){ this.tag = tag; this.monat = monat; this.jahr = jahr; } Datum (Integer jahr){ this(1, 1, jahr); } Grundlagen Programmierung Stephan Kleuker 65 gleiche Parameter(typ)liste ist Fehler Grundlagen Programmierung Stephan Kleuker 66 Objekte in Konstruktoren nutzen (1/2) • Erzeugung von Integer-Objekten in Code Pad (bei Übertragung nach links werden gleiche Namen verwendet) Grundlagen Programmierung Stephan Kleuker 67 Objekte in Konstruktoren nutzen (2/2) Grundlagen Programmierung Stephan Kleuker 68 Unvollständige Initialisierung • Was passiert, wenn Objektvariable keinen Wert bekommt • Variable hat keinen, genauer undefinierten Wert, nullReferenz oder null-Pointer genannt (auch null-Wert) Datum(){ this.tag = 1; } Grundlagen Programmierung Stephan Kleuker 69 Null-Wert selbst zuweisen • statt impliziter Zuweisungen (Ausnutzen, was standardmäßig passiert), besser immer explizit zuweisen Datum(){ this.tag = 1; this.monat = null; this.jahr = null; } Grundlagen Programmierung Stephan Kleuker 70 Variante: Startwerte bei Deklaration zuweisen (1/2) class Adresse{ String strasse; String hausnummer; String postleitzahl = "49076"; String stadt = "Osnabrueck"; String bundesland = "Nds"; Adresse(String strasse, String hausnummer){ this.strasse = strasse; this.hausnummer = hausnummer; } } Grundlagen Programmierung Stephan Kleuker 71 Variante: Startwerte bei Deklaration zuweisen (2/2) Grundlagen Programmierung Stephan Kleuker 72 Klasse ohne Konstruktor (gibt es nicht) • Wird kein Konstruktor für eine Klasse X angegeben, existiert automatisch (nicht sichtbar) der Default-Konstruktor X() { } • Alle Objektvariablen haben Standardwert oder Wert aus sofortiger Zuweisung bei der Deklaration • Grundregel der sauberen Programmierung: geben Sie mindestens einen Konstruktor an (schreiben Sie zumindest den Default-Konstruktor hin) • Wenn man Konstruktor hinschreibt, gibt es den automatischen Default-Konstruktor nicht! Grundlagen Programmierung Stephan Kleuker 73 ein sehr langer Konstruktor (unüblich) Student(String vorname, String nachname, String geburtsort, String geburtsland, String strasse, String hausnummer, Integer tag, Integer monat, Integer jahr, String studiengang, Integer matrikelnummer) { this.vorname = vorname; this.nachname = nachname; this.geburtsort = geburtsort; this.geburtsland = geburtsland; this.adresse = new Adresse(strasse, hausnummer); this.eingeschriebenAm = new Datum(tag, monat, jahr); this.studiengang = studiengang; this.matrikelnummer = matrikelnummer; } Grundlagen Programmierung Stephan Kleuker 74 sinnvoll: alle Objektvariablen initialisieren Praktikum(String modul, String semester, String veranstalter, String mitarbeiter){ super(); this.modul = modul; this.semester = semester; this.veranstalter = veranstalter; this.mitarbeiter = mitarbeiter; this.termine = new ArrayList<String>(); this.teilnehmer = new ArrayList<Student>(); } • Welche Alternative gibt es? Grundlagen Programmierung Stephan Kleuker 75 bisherige Konstruktoren kritisch betrachtet üblich ist der Aufruf mit Parameterliste this.eingeschriebenAm = new Datum(tag, monat, jahr); gibt Klassen mit Ausnahmen, bei denen das Objekt direkt hingeschrieben werden kann, dies sind • String stadt = "Osnabrueck"; geht aber auch: String stadt = new String("Osnabrueck"); • Integer jahr = 2023; geht aber auch: Integer jahr = new Integer(2023); • Double wert = 42.41; geht aber auch: Double wert = new Double(42.41); • Boolean allesToll = true; geht aber auch: Boolean allesToll = new Boolean(true); Grundlagen Programmierung Stephan Kleuker 76 Erstellung eines Objekts im Code Pad – Variante 3/3 • Nutzung lokaler Variablen Grundlagen Programmierung Stephan Kleuker 77 Variablen ? was passiert denn da (1/5) • Variablen referenzieren Werte im Speicher Integer day = 6; Integer month = 5; Integer year = 1989; Variablen day month year Speicher 6 5 1989 Datum aha = new Datum(day,month,year); in der Klasse steht Datum (Integer anInt1, Integer anInt2, Integer anInt3){ ... Ansatz: anInt1 wird lokale Variable in der Ausführung des Konstruktors, erhält Referenz auf das Objekt, auf das day zeigt Grundlagen Programmierung Stephan Kleuker 78 Variablen ? was passiert denn da (2/5) Variablen day month year Speicher 6 5 1989 anInt1 lokale Variablen, leben und sterben mit Konstruktorausführung anInt2 anInt3 • weiter im Konstruktor this.tag = anInt1; this.monat = anInt2; this.jahr = anInt3; Grundlagen Programmierung Stephan Kleuker 79 Variablen ? was passiert denn da (3/5) • Situation kurz vor Ende des Konstruktors Speicher Variablen day 6 month 5 year 1989 anInt1 anInt2 anInt3 Variablen, die zum gerade in Konstruktion befindlichen Objekt gehören Grundlagen Programmierung this.tag this.monat this.jahr Stephan Kleuker 80 Variablen ? was passiert denn da (4/5) • Situation nach Integer day = 6; Integer month = 5; Integer year = 1989; Datum aha = new Datum(day,month,year); Speicher Variablen day 6 month 5 year 1989 this.tag aha this.monat this.jahr Grundlagen Programmierung Stephan Kleuker 81 Variablen ? was passiert denn da (5/5) • neue nächste Anweisung: day = 32; Variablen Speicher day 6 month 5 year 1989 this.tag aha 32 this.monat this.jahr • Hinweis: Thema wird später noch vertieft Grundlagen Programmierung Stephan Kleuker 82 Erreichter Stand • • • • Klassen beschreiben Schablonen zur Objektbeschreibung Objektbeschreibung besteht aus Objektvariablen jede Objektvariable hat einen eindeutigen Typ Objekte werden durch Aufruf eines Konstruktors der Klasse erzeugt, Konstruktor kann Parameterliste zur Übergabe von Werten für die Objektvariablen (Belegung) enthalten • Objektvariablen ohne Wert enthalten null-Referenz • jede Klasse hat mindestens einen Konstruktor, wenn keiner geschrieben, dann der Default-Konstruktor • es ist sehr sinnvoll, alle Konstruktoren explizit hinzuschreiben • wir können aber Werte von Objektvariablen nicht verändern Grundlagen Programmierung Stephan Kleuker 83 Objektmethoden Grundlagen Programmierung Stephan Kleuker 84 Reden mit Objekten • Objekten können Methodenaufrufe geschickt werden • aufgerufene Methoden können Werte der Objektvariablen verändern • aufgerufene Methoden können Berechnungen ausführen • aufgerufene Methoden können ein Ergebnisobjekt als Antwort des Aufrufs liefern • Syntax <TypDesErgebnisses> <Methodenname> (<Parameterliste>){ <Programm> } wenn kein Ergebnisobjekt, dann Ergebnistyp void Grundlagen Programmierung Stephan Kleuker 85 Einfache Methoden (1/2) class Datum{ Integer tag; Integer monat; Integer jahr; Datum (Integer tag, Integer monat, Integer jahr){ this.tag = tag; this.monat = monat; this.jahr = jahr; } Integer getTag() { return this.tag; } void setTag(Integer tag) { this.tag = tag; } Grundlagen Programmierung Stephan Kleuker 86 Einfache Methoden (2/2) Integer getMonat() { return this.monat; } void setMonat(Integer monat) { this.monat = monat; } Integer getJahr() { return this.jahr; } void setJahr(Integer jahr) { this.jahr = jahr; } } Grundlagen Programmierung Stephan Kleuker 87 Analyse der Methoden (1/2) • Methode zum Lesen (Herausgeben) einer Variable var heißt typischerweise getVar (großen Buchstaben beachten) • Rückgabergebnis mit return <Ergebnis>; Integer getTag() { return this.tag; } Grundlagen Programmierung Stephan Kleuker 88 Analyse der Methoden (2/2) • Methode zum Schreiben (Verändern) einer Variable var heißt typischerweise setVar (großen Buchstaben beachten) void setTag(Integer tag) { this.tag = tag; } Grundlagen Programmierung Stephan Kleuker 89 Objekte nutzen Methoden anderer Objekte • Code Pad sehr sehr hilfreich beim Ausprobieren • Bei Code-Änderungen oder Schließen von BlueJ sind alle Experimente gelöscht Datum d = new Datum(29,2,2100); d.setTag(1); d.setTag(2); • Idee: Schreibe neue Klasse, die obiges Programmstück in einer Methode enthält, führe dann Methode in BlueJ aus Grundlagen Programmierung Stephan Kleuker 90 Experimentierklasse class Datumsspielerei{ Datum datum; Datumsspielerei(){ this.datum = new Datum(29,2,2100); } Datum einigeAenderungen(){ this.datum.setTag(1); this.datum.setMonat(3); return this.datum; } Datum neuerTagNeuerMonat(Integer tag, Integer monat){ this.datum.setTag(tag); this.datum.setMonat(monat); return this.datum; } } Grundlagen Programmierung Stephan Kleuker 91 Experiment in BlueJ (1/2) Grundlagen Programmierung Stephan Kleuker 92 Experiment in BlueJ (2/2) resultierendes Ergebnisobjekt wird zurückgegeben Grundlagen Programmierung Stephan Kleuker 93 Spielen mit Objekten in BlueJ (1/2) • Generell kann man bei direkten Aufrufen von Konstruktoren und Methoden direkt auf Objekte zugreifen, die in der Objektleiste liegen oder Objekte direkt erstellen Objekterzeugung Grundlagen Programmierung Stephan Kleuker 94 Spielen mit Objekten in BlueJ (2/2) Grundlagen Programmierung Stephan Kleuker 95 Visualisierung mit Sequenzdiagrammen Grundlagen Programmierung Stephan Kleuker 96 Aufbau von Sequenzdiagrammen new Datum() datum: Datum • Objekt der Klasse Datum mit dem Namen d datum: Datum • Objekterzeugung mit Konstruktoraufruf und Rückgabe des Objekts (gestrichelt) datum: Datum methode(42) 23 Grundlagen Programmierung • Methodenaufruf mit Parametern (Namen oder konkrete Werte) und Rückgabepfeil mit Ergebnis (oder ohne Beschriftung bei void) Stephan Kleuker 97 Methoden mit lokalen Variablen • innerhalb von Methoden können an beliebigen Stellen lokale Variablen deklariert und innerhalb der Methode genutzt werden • Syntax und Möglichkeit zur Setzung des Startwerts wie bei Objektvariablen • genauer: lokale Variablen können in Blöcken (Programmfragmente in geschweiften Klammern) deklariert werden; nach dem Verlassen des Blocks ist Variable selbst verloren (ihr Objekt kann z. B. als Rückgabewert weiter existieren) Grundlagen Programmierung Stephan Kleuker 98 Beispiel: Nutzung einer lokalen Variable class Datumsspielerei{ Datumsspielerei(){ } Datum heiraten(Integer wert){ Datum ergebnis = new Datum(); ergebnis.setTag(wert); ergebnis.setMonat(wert); ergebnis.setJahr(wert); return ergebnis; } } Grundlagen Programmierung Stephan Kleuker 99 Namensregeln und Sichtbarkeitsbereiche • In demselben Block darf es nicht zwei Variablen mit gleichem Namen geben (Syntaxfehler) • Beispiel: Kann nicht eine Objektvariable monat vom Typ Integer und eine Variable monat vom Typ String geben • Lokale Variablen können die gleichen Namen wie Objektvariablen haben, verdecken diese aber • Objektvariablen immer durch this. zugreifbar • Grundregel der guten Programmierung: Schaffen Sie niemals Situationen, in denen Sie über verdeckte Variablen nachdenken müssen Grundlagen Programmierung Stephan Kleuker 100 Verdecken einer Objektvariablen (1/3) class Datumsspielerei{ Datum datum; Datumsspielerei(){ this.datum = new Datum(29,2,2100); } Datum getDatum(){ return this.datum; } Datum heiraten(Integer wert){ Datum datum = new Datum(); datum.setTag(wert); datum.setMonat(wert); datum.setJahr(wert); return datum; } }Grundlagen Programmierung Stephan Kleuker 101 Verdecken einer Objektvariablen (2/3) Grundlagen Programmierung Stephan Kleuker 102 Verdecken einer Objektvariablen (3/3) Grundlagen Programmierung Stephan Kleuker 103 Methodennutzung • Neben dem Wissen über existierende Klassen ist das Wissen über die angebotenen Methoden Grundlage der Programmiererfahrung • In dieser Vorlesung sehen die objektorientierten Programmiergrundlagen im Vordergrund, deshalb Wissen über konkrete Java-Klassen im Hintergrund • Wichtig: Man muss wissen, wo Informationen über Klassen/Methoden stehen (z. B. Java-Dokumentation) und wie man mit ihnen experimentieren kann (z. B. in BlueJ) Grundlagen Programmierung Stephan Kleuker 104 Möglichkeiten mit Integer-Objekten • Erstellung mit Code Pad und Objekt in Objektleiste ziehen • Rechtsklick auf dem Objekt zeigt nutzbare Methoden (zur Zeit nur letzte Methode halbwegs verständlich) Grundlagen Programmierung Stephan Kleuker 105 Zusammenfassung • Objekte sind Grundlagen der objektorientierten Programmierung • Programme bestehen aus dem – Erstellen von Objekten – Bearbeiten von Objekten über Methodenaufrufe – Weitergeben von Objekten an Methoden über Parameter • Durch die systematische Bearbeitung aller Objekte entsteht ein Programm • Es ist Teil der Anforderungsanalyse festzustellen, welche Klassen und Methoden zur Lösung der Aufgabenstellung benötigt werden Grundlagen Programmierung Stephan Kleuker 106 Vereinfachte Klasse Student (1/3) class Student{ String vorname ="Eva"; String nachname ="Mustermann"; Integer geburtsjahr = 1990; String studiengang = "IMI"; Integer matrikelnummer = 232323; Student(String vorname, String nachname, Integer geburtsjahr, String studiengang, Integer matrikelnummer) { this.vorname = vorname; this.nachname = nachname; this.geburtsjahr = geburtsjahr; this.studiengang = studiengang; this.matrikelnummer = matrikelnummer; } Student(){ } Grundlagen Programmierung Stephan Kleuker 107 Vereinfachte Klasse Student (2/3) String getVorname() { return this.vorname; } void setVorname(String vorname) { this.vorname = vorname; } String getNachname() { return this.nachname; } void setNachname(String nachname) { this.nachname = nachname; } Integer getGeburtsjahr() { return this.geburtsjahr; } Grundlagen Programmierung Stephan Kleuker 108 Vereinfachte Klasse Student (3/3) void setGeburtsjahr(Integer geburtsjahr) { this.geburtsjahr = geburtsjahr; } String getStudiengang() { return this.studiengang; } void setStudiengang(String studiengang) { this.studiengang = studiengang; } Integer getMatrikelnummer() { return this.matrikelnummer; } void setMatrikelnummer(Integer matrikelnummer) { this.matrikelnummer = matrikelnummer; } } Grundlagen Programmierung Stephan Kleuker 109 Debugger Grundlagen Programmierung Stephan Kleuker 110 Bayrisches Problem • Student hat sich bei Einschreibung mit „Jo mei, I bian der Huber Xaver“ vorgestellt • Wunsch: Methode zum Vertauschen von Vor- und Nachnamen Grundlagen Programmierung Stephan Kleuker 111 (schlechter) Versuch 1 (1/3) • neue Methode void vertauscheNamen(){ this.vorname = this.nachname; this.nachname = this.vorname; } • neue Klasse zum Ausprobieren class Studentspielerei{ Student namenstausch(){ Student xaver= new Student("Huber", "Xaver", 1988 ,"MID", 424223); xaver.vertauscheNamen(); return xaver; } } Grundlagen Programmierung Stephan Kleuker 112 (schlechter) Versuch 1 (2/3) • Ausführung von namenstausch() liefert Grundlagen Programmierung Stephan Kleuker 113 (schlechter) Versuch 1 (3/3) • Analyse, was ist passiert: Student xaver= new Student("Huber", "Xaver", 1988 ,"MID", 424223); • vorname = "Huber" nachname = "Xaver" xaver.vertauscheNamen(); this.vorname = this.nachname; – vorname = "Xaver" nachname = "Xaver" this.nachname = this.vorname; – vorname = "Xaver" nachname = "Xaver" • vorname = "Xaver" nachname = "Xaver" Grundlagen Programmierung Stephan Kleuker 114 (guter) Versuch 2 (1/3) • Nutzung einer lokalen Variablen void vertauscheNamen(){ String tmp = this.vorname; this.vorname = this.nachname; this.nachname = tmp; } Grundlagen Programmierung Stephan Kleuker 115 (guter) Versuch 2 (2/3) • Ausführung von namenstausch() liefert Grundlagen Programmierung Stephan Kleuker 116 (guter) Versuch 2 (3/3) • Analyse, was ist passiert: Student xaver= new Student("Huber", "Xaver", 1988 ,"MID", 424223); • vorname = "Huber" nachname = "Xaver" xaver.vertauscheNamen(); String tmp = this.vorname; – vorname = "Huber" nachname = "Xaver" tmp = "Huber" this.vorname = this.nachname; – vorname = "Xaver" nachname = "Xaver" tmp = "Huber" this.nachname = tmp; – vorname = "Xaver" nachname = "Huber" tmp = "Huber" • vorname = "Xaver" nachname = "Huber" Grundlagen Programmierung Stephan Kleuker 117 Nutzung des Debuggers (1/6) - Vorbereitung • letzte Folie zeigt, dass genaue Verfolgung der Variablenwerte wichtiges Hilfsmittel sein kann • Ansatz ist, über Debugger (direkt übersetzt: Entwanzer) die Abarbeitung zu verfolgen • genauer: Zeilen, ab denen der Debugger die genaue Situation zeigen soll, werden markiert (Break Point, Klick am linken Rand des Editors, nur nach "Compile" sichtbar) Grundlagen Programmierung Stephan Kleuker 118 Nutzung des Debuggers (2/6) – Start der Nutzung • Methode wird für gegebenes Objekt gestartet • markierte Zeile wird erreicht; es öffnet sich Debugger und Editor (zeigt immer aktuelle Zeile an) Grundlagen Programmierung Stephan Kleuker 119 Nutzung des Debuggers (3/6) – mögliche Aktionen Typische Bedienmöglichkeiten eines Debuggers: Step: nächste Anweisung ausführen Step Into: bei anstehenden Methodenaufrufen kann man in die Methode hineinspringen Continue: verlasse DebugModus und lasse Programm "normal" weiterlaufen, bis nächster Break Point erreicht wird Grundlagen Programmierung Stephan Kleuker 120 Nutzung des Debuggers (4/6) - Anzeige welche Klassenvariablen (später!) gibt es welche Methoden laufen zur Zeit (Methoden können ja Methoden aufrufen) welche Objektvariablen gibt es (detailliertere Inhalte) durch Anklicken welche lokalen Variablen gibt es Grundlagen Programmierung Stephan Kleuker 121 Nutzung des Debuggers (5/6) – Ausführung 1/2 Grundlagen Programmierung Stephan Kleuker 122 Nutzung des Debuggers (6/6) – Ausführung 2/2 Grundlagen Programmierung Stephan Kleuker 123 Visualisierung im Aktivitätsdiagramm - Sequenz Startknoten (es kann nur einen geben) gerichtete Kanten zeigen Ablauf abgerundete Rechtecke enthalten Anweisungen (Aktionen) Endknoten zur Terminierung (es kann mehrere geben) Grundlagen Programmierung String tmp = this.vorname this.vorname = this.nachname this.nachname = tmp Stephan Kleuker 124 Objektweitergabe Grundlagen Programmierung Stephan Kleuker 125 Erinnerung Klasse Student • Anfang class Student{ String vorname ="Eva"; String nachname ="Mustermann"; Integer geburtsjahr = 1990; String studiengang = "IMI"; Integer matrikelnummer = 232323; Student(String vorname, String nachname, Integer geburtsjahr, String studiengang, Integer matrikelnummer) {... Student(){ }... • weiter mit get- und set-Methoden für alle Objektvariablen Grundlagen Programmierung Stephan Kleuker 126 Möglichkeit 1: Direkte Erzeugung in anderer Klasse class StudentSpielerei{ Student standardMIDStudi(){ Student ergebnis = new Student(); ergebnis.setStudiengang("MID"); return ergebnis; } } Grundlagen Programmierung Stephan Kleuker 127 Möglichkeit 2: Übergabe in Methode/ in Konstruktor class Anonymisierer { Student dummy; Anonymisierer(Student dummy){ this.dummy=dummy; } void anonymisieren(Student zuAendern){ zuAendern.setVorname(this.dummy.getVorname()); zuAendern.setNachname(this.dummy.getNachname()); } } Grundlagen Programmierung Stephan Kleuker 128 Nutzung in StudentSpielerei class StudentSpielerei{ Student anonymAusprobieren(){ Student def = new Student("Mr","X",1990,"IMI",234243); Anonymisierer ano = new Anonymisierer(def); Student test = new Student("Ms","Y",1980,"MID",234244); ano.anonymisieren(test); return test; } } Grundlagen Programmierung Stephan Kleuker 129 (k)eine Besonderheit: Objekt übergibt sich selbst (1/2) class Anonymisierer { Student dummy; Anonymisierer(Student dummy){ this.dummy=dummy; } void anonymisieren(Student zuAendern){ zuAendern.setVorname(this.dummy.getVorname()); zuAendern.setNachname(this.dummy.getNachname()); } void leseDummyAus(StudentSpielerei spielerei){ this.dummy = spielerei.getStudi(); } } Grundlagen Programmierung Stephan Kleuker 130 (k)eine Besonderheit: Objekt übergibt sich selbst (2/2) class StudentSpielerei{ Student studi; Student getStudi(){ return this.studi; } Student sichUebergeben(){ this.studi = new Student("Mr","X",1990,"IMI",234243); Anonymisierer ano = new Anonymisierer(null); ano.leseDummyAus(this); Student test = new Student("Ms","Y",1980,"MID",234244); ano.anonymisieren(test); return test; } Grundlagen Programmierung Stephan Kleuker 131 Abhängigkeiten • Generell wird meist gefordert, dass nicht gleichzeitig eine Klasse A eine Klasse B und eine Klasse B eine Klasse A nutzt Grundlagen Programmierung Stephan Kleuker 132 Alternative Grundlagen Programmierung Stephan Kleuker 133 Alternativen • bisher bestehen Programme aus der Nacheinanderausführung von Methodenaufrufen (Sequenz) • typisch ist die Notwendigkeit der Alternative wenn folgendes gilt, dann soll folgendes gemacht werden • oder wenn folgendes gilt, dann soll folgendes gemacht werden, sonst soll folgendes gemacht werden • wenn = if (dann wird implizit gesagt) sonst = else Grundlagen Programmierung Stephan Kleuker 134 Ausgabe des Studiengangsnamens • Die Methode soll einen Langnamen für den Studiengang als Ergebnis liefern, wenn der Name "IMI" ist, soll "Informatik – Medieninformatik" das Ergebnis sonst der im Objekt eingetragene Studiengangsname das Ergebnis sein • Realisierung in der Klasse Student String langnameBerechnen(){ String ergebnis = this.studiengang; if(this.studiengang.equals("IMI")){ ergebnis = "Informatik - Medieninformatik"; } return ergebnis; } Grundlagen Programmierung Stephan Kleuker 135 Ausprobieren (in Studentspielerei) (1/2) String einIMIStudent(){ Student eva= new Student("Eva", "Li", 1988, "IMI", 13); String ergebnis = eva.langnameBerechnen(); return ergebnis; } Grundlagen Programmierung Stephan Kleuker 136 Ausprobieren (in Studentspielerei) (2/2) String keinIMIStudent(){ Student eva= new Student("Udo", "Li", 1987, "MID", 14); String ergebnis = eva.langnameBerechnen(); return ergebnis; } Grundlagen Programmierung Stephan Kleuker 137 Syntaxanalyse von if if(this.studiengang.equals("IMI")){ ergebnis = "Informatik - Medieninformatik"; } • Boolescher Ausdruck (Bedingung) in runden Klammern, Ausdruck in der Klammer muss nach true oder false ausgewertet werden • Zugriff auf alle sichtbaren Objektvariablen, Methodenparameter und lokale Variablen der umgebenden Blöcke Grundlagen Programmierung • Programmzeilen in geschweiften Klammern; wird ausgeführt, wenn Ausdruck nach true ausgewertet wird • Zugriff auf alle sichtbaren Objektvariablen, Methodenparameter und lokale Variablen der umgebenden Blöcke Stephan Kleuker 138 Visualisierung im Aktivitätsdiagramm - generell if ( <Bedingung> ) { <Teilprogramm> } • Raute steht für Alternative und die Vereinigung verschiedener Abläufe • Bei einer Alternative stehen Bedingungen in eckigen Klammern auf den ausgehenden Kanten • ! steht für die Negation Grundlagen Programmierung [<Bedingung>] [!<Bedingung>] <Teilprogramm> Stephan Kleuker 139 Visualisierung im Aktivitätsdiagramm - konkret String ergebnis = this.studiengang [this.studiengang .equals("IMI")] [!this.studiengang .equals("IMI")] ergebnis = "Informatik - Medieninformatik" return ergebnis; Grundlagen Programmierung Stephan Kleuker 140 Boolescher Ausdruck • • • • benannt nach George Boole (1815-1864), Mathematiker wird nach false (falsch) oder true (wahr) ausgewertet kann Methode mit Booleschem Ergebnis sein kann einfacher Vergleich von Zahlen sein geburtsjahr < 1900 1900 > geburtsjahr geburtsjahr.equals(matrikelnummer) für Gleichheit • Operator == prüft Identität, nicht zum inhaltlichen Vergleich nutzbar • auch <= >= • negierbar, Negation mit Ausrufungszeichen ! (geburtsjahr < 1900) • Boolesche Variable Boolean jaja = false; ! jaja Grundlagen Programmierung Stephan Kleuker 141 Zusammengesetzter Boolescher Ausdruck (1/2) • Boolesche Ausdrücke können mit den Booleschen Operatoren "und" (&&) sowie "oder" (||) verknüpft werden • Boolesche Ausdrücke können in runden Klammern stehen • Auswertungsreihenfolge: Klammern binden mehr als Negation, diese bindet mehr als Und, das bindet mehr als Oder (einfacher Ansatz: immer Klammern setzen) • Wahrheitstafeln && true true false true false false false false Grundlagen Programmierung || true true false true true false true false Stephan Kleuker ! true false false true 142 Zusammengesetzter Boolescher Ausdruck (2/2) • Beispiele mit Variablen Integer alter=23 und Integer iq=42 alter > 20 && iq > alter true && true ergibt true alter > 20 || iq < alter true || false ergibt true alter > 20 || iq < 10 && iq >99 true || false && false ergibt true || false, ergibt true (alter > 20 || iq < 10) && iq >99 (true || false) && false ergibt true && false, ergibt false !(alter>23) || alter<24 (!false || true) ergibt (true || true), ergibt true !(alter>23 || alter<24) !(false || true) ergibt !(true), ergibt false ! alter>23 gibt Fehler, da Integer-Objekt nicht negiert werden kann Grundlagen Programmierung Stephan Kleuker 143 Analyse von Booleschen Ausdrücken in Code Pad • zur Auswertung kein Semikolon am Zeilenende Grundlagen Programmierung Stephan Kleuker 144 Wahrheitstabellen Boolean a; Boolean b; Boolean c; a false false false false true true true true b false false true true false false true true Grundlagen Programmierung c a || b && c false false true false false false true true false true true true false true true true Stephan Kleuker (a || b) && c false false false true false true false true 145 Rechenregeln für Boolesche Ausdrücke • nutzt Aussagenlogik • ≡ heißt "logisch äquivalent", bedeutet dass beide Ausdrücke immer den gleichen Wahrheitswert liefern • • • • • • • • a && (b || c) ≡ (a && b) || (a && c) a || (b && c) ≡ (a || b) && (a || c) ! (a && b) ≡ !a || !b a && b ≡ b && a ! (a || b) ≡ !a && !b a || b ≡ b || a a && !a ≡ false a && a ≡ a a || !a ≡ true a || a = a a && true ≡ a a && false ≡ false a || false ≡ a a || true ≡ true Grundlagen Programmierung Stephan Kleuker 146 Einsatz von Rechenregeln • Umformung Boolescher Ausdrücke mit dem Ziel der Vereinfachung (hier Integer-Variablen) if (iq<99 && bmi>25 || iq>98 && bmi>25 || iq<99 && bmi<=25 || iq>98 && bmi<=25){ <Teilprogramm> } iq<99 && bmi>25 || iq>98 && bmi>25 || iq<99 && bmi<=25 || iq>98 && bmi<=25 ≡ iq<99 && bmi>25 || iq<99 && bmi<=25 || iq>98 && bmi>25 || iq>98 && bmi<=25 ≡ iq<99 && (bmi>25 || bmi<=25) || iq>98 && (bmi>25 || bmi<=25) ≡ iq<99 && (true) || iq>98 && (true) ≡ iq<99 || iq>98 ≡ true (if ist überflüssig) Grundlagen Programmierung Stephan Kleuker 147 Ausdrücke mit ganzen Zahlen Integer-Variablen a, b, und c, es gibt folgende Operatoren • a + b (Addition) • a – b (Subtraktion) • a * b (Multiplikation) • a / b (ganzzahlige Division 7/4 == 1) • a % b (ganzzahliger Rest der Division (modulo) 7%4 == 3) • Boolesche Ausdrücke der Form a + b < a * b möglich • Ausdrücke können Variablen auf der linken Seite zugewiesen werden Integer oha = (7/4)*4 + (7%4); • man beachte unglückliche Nutzung des Gleichheitszeichens oha = oha + 1; Grundlagen Programmierung Stephan Kleuker 148 Analyse von ganzzahligen Ausdrücken in Code Pad • es gilt: erst Klammern, dann *, / und %, dann + und – • mathematische Operatoren binden stärker (werden zuerst ausgewertet) als Boolesche Operatoren • Division durch Null nicht erlaubt, man erhält in weiterem Fenster: Grundlagen Programmierung Stephan Kleuker 149 Ausdrücke mit Fließkomma-Zahlen Double-Variablen a, b, und c, es gibt folgende Operatoren • a + b (Addition) • a – b (Subtraktion) • a * b (Multiplikation) • a / b (Division 7.0/4.0 == 1.75) • a % b (Rest nach ganzzahliger Division 7.0%3.4 == 0.2) • Boolesche Ausdrücke der Form a + b < a * b möglich • Ausdrücke können Variablen auf der linken Seite zugewiesen werden Double oha = (7.0/4.0)*4.0; • man beachte unglückliche Nutzung des Gleichheitszeichens oha = oha + 1; Grundlagen Programmierung Stephan Kleuker 150 Analyse von Fließkommazahl-Ausdrücken • es gilt, erst Klammern, dann * und /, dann + und – • mathematische Operatoren binden stärker (werden zuerst ausgewertet) als Boolesche Operatoren • Division durch Null führt zu neuen Double-Werten (später mehr): -Infinity Infinity NaN • Rechengenauigkeit beachten Grundlagen Programmierung Stephan Kleuker 151 Programmiervarianten (1/2) • aktuell: String langnameBerechnen(){ String ergebnis = this.studiengang; if(this.studiengang.equals("IMI")){ ergebnis = "Informatik - Medieninformatik"; } return ergebnis; } • Variante: direkter Rücksprung (evtl. kürzere Berechnung aber schwerer zu Lesen, da es mehr als ein Ende gibt) String langnameBerechnen2(){ String ergebnis = this.studiengang; if(this.studiengang.equals("IMI")){ return "Informatik - Medieninformatik"; } return ergebnis; } Grundlagen Programmierung Stephan Kleuker 152 Visualisierung der Variante String ergebnis = this.studiengang [this.studiengang .equals("IMI")] [!this.studiengang .equals("IMI")] return "Informatik - Medieninformatik" Grundlagen Programmierung Stephan Kleuker return ergebnis 153 Programmiervarianten (2/2) • kürzer; zumindest für Anfänger ist es aber sinnvoll, eine ergebnis-Variable einzuführen und zurückzugeben String langnameBerechnen3(){ if(this.studiengang.equals("IMI")){ return "Informatik - Medieninformatik"; } return this.studiengang; } • Nutzung eines aus anderen Programmiersprachen übernommenen Operators String langnameBerechnen4(){ return this.studiengang.equals("IMI") ? "Informatik - Medieninformatik" : this.studiengang; } Grundlagen Programmierung Stephan Kleuker 154 Operator ? : • Operator hat folgende Syntax • <Boolescher Ausdruck> ? <Ausdruck1> : <Ausdruck2> • Wenn Boolescher Ausdruck nach wahr ausgewertet wird, dann ist das Ergebnis der Wert des Ausdrucks1 sonst des Ausdrucks2 • Ausdruck1 und Ausdruck2 müssen selben Typ haben Integer mo = name.equals("Li") ? 42 : 23; • äquivalent zu Integer mo; if(name.equals("Li"){ mo = 42; } else { mo = 23; } • Wunsch: verzichten Sie auf diesen coolen Operator Grundlagen Programmierung Stephan Kleuker 155 Nutzung von else • Anforderung: Basierend auf dem Alter soll eine Persönlichkeitseinstufung erfolgen String persoenlichkeit(){ String ergebnis; if(this.geburtsjahr < 1970){ ergebnis = "alter Sack"; } else { ergebnis = "Windelhopser"; } return ergebnis; } • Gibt wieder einige Programmiervarianten Grundlagen Programmierung Stephan Kleuker 156 Syntaxanalyse von if -else if(this.geburtsjahr < 1970){ ergebnis = "alter Sack"; } else { ergebnis = "Windelhopser"; } • syntaktisch genau gleich, wie das bisher beschriebene if ohne else Grundlagen Programmierung • Programmfragment in geschweiften Klammern; wird ausgeführt, wenn Ausdruck nach false ausgewertet wird • Zugriff auf alle sichtbaren Objektvariablen, Methodenparameter und lokale Variablen der umgebenden Blöcke Stephan Kleuker 157 Schachtelung von Programmfragmenten • Anforderung: Die Persönlichkeitseinstufung soll weiter detailliert werden String genauerePersoenlichkeit(){ String ergebnis; if(this.geburtsjahr < 1970){ ergebnis = "alter Sack"; } else { if (this.studiengang.equals("IMI")){ ergebnis = "Dynamit"; } else { ergebnis = "Windelhopser"; } } return ergebnis; } Programmierung Grundlagen Stephan Kleuker 158 Visualisierung von genauerePersoenlichkeit String ergebnis [this.geburtsjahr<1970] ergebnis = "alter Sack" [!this.geburtsjahr<1970] [this.studiengang .equals("IMI")] [!this.studiengang .equals("IMI")] ergebnis = "Dynamit" ergebnis = "Windelhopser" return ergebnis Grundlagen Programmierung Stephan Kleuker 159 Eine der Implementierungsalternativen String genauerePersoenlichkeit2(){ if(this.geburtsjahr < 1970){ return "alter Sack"; } if (this.studiengang.equals("IMI")){ return "Dynamit"; } return "Windelhopser"; } • Verschachtelung von Anweisungen und komplexe Boolesche Ausrücke machen Programme schwer lesbar Grundlagen Programmierung Stephan Kleuker 160 Visualisierung der Alternative • Hinweis: In Visualisierung nicht sichtbar, ob else genutzt (da mit return Ausführung endet String ergebnis [this.geburtsjahr<1970] return "alter Sack" [!this.geburtsjahr<1970] [this.studiengang .equals("IMI")] return "Dynamit" Grundlagen Programmierung Stephan Kleuker [!this.studiengang .equals("IMI")] return"Windelhopser" 161 Alternative mit Methodenaufteilung String genauerePersoenlichkeit3(){ String ergebnis; if(this.geburtsjahr < 1970){ ergebnis = "alter Sack"; } else { ergebnis = jungePersoenlichkeit(); } return ergebnis; } String jungePersoenlichkeit() { String ergebnis; if (this.studiengang.equals("IMI")){ ergebnis = "Dynamit"; } else { ergebnis = "Windelhopser"; } return ergebnis; Grundlagen Stephan Kleuker } Programmierung 162 Hinweise zur Methodenaufteilung • sehr sinnvoll: einfache kurze Methoden mit sinnvollen Methodennamen • Grundregel: wenn es zu komplex wird, neue Methode auslagern • Die hier gezeigte (für die Veranstaltung relevante) Methodenaufteilung wird in älteren Programmiersprachen kontrovers diskutiert • Pro: bessere Lesbarkeit, klarere Struktur (allerdings mehr Methoden) • Contra: zusätzliche Methodenaufrufe benötigen Zeit (in Java minimal, teilweise durch Compiler wegoptimiert) Grundlagen Programmierung Stephan Kleuker 163 Weitere, aber schwache Alternative String genauerePersoenlichkeit4(){ if ( !(this.geburtsjahr < 1970) && this.studiengang.equals("IMI")){ return "Dynamit"; } if ( !(this.geburtsjahr < 1970) && !this.studiengang.equals("IMI")){ return "Windelhopser"; } return "alter Sack"; } • unnötig komplizierte Boolesche Ausdrücke Grundlagen Programmierung Stephan Kleuker 164 Grundregel für Alternativen Müssen zur Ausführung mehrere Bedingungen berücksichtigt werden • müssen die Booleschen Ausdrücke zunächst einzeln formuliert werden • muss überlegt werden, ob ein Ausdruck wichtiger als ein anderer ist; falls es den gibt: if(<Bed.>) {… return…} • müssen die zusammengesetzten Booleschen Ausdrücke formuliert und in eine sinnvolle – Sequenz von if-else-Befehlen, oder – Verschachtelung von if-else-Befehlen gebracht werden Grundlagen Programmierung Stephan Kleuker 165 equals Grundlagen Programmierung Stephan Kleuker 166 Inhaltliche Objektgleichheit mit equals • • • • • Vergleich von Strings auf gleichen Inhalt mit equals genauer: jede vorhandene Java-Klasse hat equals-Methode Folgerung: wir müssen auch equals-Methode schreiben [Hinweis: ganz sauber etwas später] wir bestimmen wann etwas gleich sein soll: – typisch: alle Werte der Objektvariablen sollen übereinstimmen – Variante: Objekt hat eindeutigen Identifikationswert, z. B. Matrikelnummer; restliche Objektwerte müssen nicht übereinstimmen (Tippfehler, Heirat) Grundlagen Programmierung Stephan Kleuker 167 Methode equals für Klasse Student (erster Versuch) Boolean equals(Student other){ if (other == null){ return false; } if (this.vorname.equals(other.getVorname()) && this.nachname.equals(other.getNachname()) && this.geburtsjahr.equals(other.getGeburtsjahr()) && this.studiengang.equals(other.getStudiengang()) && this.matrikelnummer .equals(other.getMatrikelnummer())){ return true; } return false; } Grundlagen Programmierung Stephan Kleuker 168 Erinnerung: null-Referenz • Methoden können nur auf echten Objekten, nicht der nullReferenz ausgeführt werden • Methodenaufrufe auf der null-Referenz führen zu gefürchteten NullPointerExceptions • defensive Programmierung: zuerst auf null testen if (other == null){ return false; } • sonst: Grundlagen Programmierung Stephan Kleuker 169 Analyse des equals für Student • Parameter sauber auf null überprüft, aber bei this.nachname.equals(other.getNachname()) • • • • könnte nachname eine null-Referenz sein Antwort: Nein, da beide Konstruktoren garantieren, dass Objekte gesetzt werden (Eva Mustermann) Kommentar: Halt! Mit Methode setNachname kann nachträglich null-Referenz übergeben werden Also entweder set-Methoden absichern oder equals umschreiben Ansatz: prüfe für jeden Parameter ob er null-Referenz ist und mache dann inhaltliche Prüfung nehme an, dass Objekte inhaltlich gleich, gebe bei Ungleichheit false und nur ganz am Ende true zurück Grundlagen Programmierung Stephan Kleuker 170 Methode equals ohne NullPointerException Boolean equals(Student other){ if (other == null){ return false; } if (geburtsjahr == null) { if (other.getGeburtsjahr() != null) { return false; } } else { if (!geburtsjahr.equals(other.getGeburtsjahr())) { return false; } } // gleiche Untersuchungen fuer andere Objektvariablen return true; } Grundlagen Programmierung Stephan Kleuker 171 Sichtbarkeiten • beim Auslagern von Methoden gab es folgendes Ergebnis: String genauerePersoenlichkeit3(){ ... } String jungePersoenlichkeit() { ...} • Problem: Objekt-Nutzer können auch Methode jungePersoenlichkeit() aufrufen, obwohl dies vielleicht nicht gewünscht ist • Lösung: Klassen, Objektvariablen und Methoden können mit Sichtbarkeiten markiert werden, hier zunächst – public: alle können darauf zugreifen – private: nur innerhalb des Objektes kann darauf zugegriffen werden • Grundregel der Objektorientierung: Information-Hiding; nur über Methoden kann auf Objektvariablen zugegriffen werden Grundlagen Programmierung Stephan Kleuker 172 Sichtbarkeiten in der Klasse Student public class Student{ private String vorname ="Eva"; private String nachname ="Mustermann"; private Integer geburtsjahr = 1990; private String studiengang = "IMI"; private Integer matrikelnummer = 232323; public Student(String vorname, … public Student(){ … public void vertauscheNamen(){… public String genauerePersoenlichkeit3(){… private String jungePersoenlichkeit() {… public Boolean equals(Student other){… public String getVorname() {… public String getNachname() {… public void setVorname(String vorname) {… public void setNachname(String nachname) {… … Grundlagen Programmierung Stephan Kleuker 173 Kommentare - Einstieg • Kommentare sollen das Lesen von Programmen für Andere und sich selbst (!!!) einfacher machen • zwei Kommentar-Arten • Kurzkommentar nach // , Kommentar endet mit Zeile • Langkommentar, beginnt mit /* und endet mit */ • Anfänger sollten intensiver kommentieren • typische Kommentare • Kurzbeschreibung: wozu gibt es Objektvariable • Methode: wozu ist sie da, welche Parameter mit welchem Sinn gibt es, wie sieht das berechnete Ergebnis aus • meist wenig/keine Kommentare in Methoden, da durch Namen, ihre Kürze und sprechende Variablen selbsterklärend • systematische Kommentierung ( JavaDoc) später Grundlagen Programmierung Stephan Kleuker 174 Kommentare: Beispiel /** Klasse zur Verwaltung des aktuellen Chefs. */ public class Chef { private String name; // Name des Chefs private Integer level = 23; // erreichte Stufe /* Konstruktor fuer neues Chef-Objekt * Parameter name: neuer Name des Chefs */ public Chef(String name){ this.name = name; } /* Methode zur Pruefung, ob Chef Andreas heisst Ergebnis: heisst Chef Andreas */ public Boolean issesAndreas(){ if("Andreas".equals(this.name)){ // geht auch so return true; } return false; } Grundlagen Programmierung Stephan Kleuker } 175 Strings Grundlagen Programmierung Stephan Kleuker 176 Ein- und Ausgabe mit Klasse EinUndAusgabe • Ein- und Ausgabe etwas trickreich bzw. schmuddelig, deshalb hier objektorientiert saubere Klasse EinUndAusgabe Methode Rückgabetyp Aufgabe Default leseString leseDouble String Integer Double liest Text von Konsole liest ganze Zahl liest Fließkommazahl -1 -1.0 leseBoolean Boolean liest Wahrheitswert false leseInteger • Eingabe endet immer mit dem Drücken von "Return" • Default-Wert bei falscher Eingabe Methode Parametertyp Aufgabe ausgeben String gibt Text auf Konsole aus Grundlagen Programmierung Stephan Kleuker 177 Strings als Ausdrücke • neben den Objektmethoden gibt es Möglichkeit, Strings mit "+" zu verknüpfen (konkatenieren) • weiterhin können Strings mit beliebigen Objekten mit "+" verknüpft werden Grundlagen Programmierung Stephan Kleuker 178 Methode toString() • neue Klassen werden unbefriedigend in Strings umgesetzt • Lösung: Klasse erhält Methode public String toString() public String toString(){ return this.vorname+" "+this.nachname+" (" + this.matrikelnummer+"):"+this.studiengang } • Methode wird implizit bei String-Bearbeitung angestoßen • man kann toString als normale Methode nutzen eva.toString() Grundlagen Programmierung Stephan Kleuker 179 Besondere Zeichen (1/2) • • • • Ausgabe wird zur Zeit als unendliche Zeile ausgegeben Zeilenumbruch mit Zeichen "\n" Backslash leitet Sonderzeichen ein \u leitet Unicode-Zeichen ein (genauer UTF-16) Text Ausgabe Text Ausgabe \n \t \\ \" \u00c4 \u00e4 Zeilenumbruch Tabulator \ " Ä ä \u00d6 \u00f6 \u00dc \u00fc \u00df Ö ö Ü ü ß Grundlagen Programmierung Stephan Kleuker 180 Besondere Zeichen (2/2) • Ein- und Ausgabe erfolgt in BlueJ in "Terminal" • Beispielmethode: public void sonderzeichen(){ EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben("0123456789012345678\n"); io.ausgeben("01\t123\t45\n"); io.ausgeben("\"ein\\/Pfeil\"\n"); io.ausgeben("\u00c4 \u00e4 \u00d6 \u00f6 \u00dc \u00fc " +" \u00df \u00E9 \u00A6 \u00A2 \u20AC \u2020 "); } Grundlagen Programmierung Stephan Kleuker 181 Vision: alle Zeichen sichtbar mit Unicode • Unicode [ˈjuːnɪkoʊd] ist ein internationaler Standard, in dem langfristig für jedes sinntragende Schriftzeichen oder Textelement aller bekannten Schriftkulturen und Zeichensysteme ein digitaler Code festgelegt wird. Ziel ist es, die Verwendung unterschiedlicher und inkompatibler Kodierungen in verschiedenen Ländern oder Kulturkreisen zu beseitigen. Unicode wird ständig um Zeichen weiterer Schriftsysteme ergänzt. (wikipedia.de) • recht mächtig und häufig genutzt: ISO 8859-1 (Latin1, z. B. auch griechisch und hebräisch) • <?xml version="1.0" encoding="UTF-8"?> • <?xml version="1.0" encoding="ISO-8859-1"?> • http://www.unicode.org Grundlagen Programmierung Stephan Kleuker 182 Einige Methoden der Klasse ArrayList<Typ> MethoParameter Ergebnis Beschreibung denname add Typ fügt übergebenes Element hinten an add Integer, fügt übergebenes Element an Typ angegebener Position ein get Integer Typ remove Integer Typ set Integer, Typ Typ size Grundlagen Programmierung Integer gibt Element an gefragter Position zurück entfernt Element an gegebener Position (ist auch Ergebnis) ersetzt Objekt an angegebener Position, gibt altes Objekt zurück gibt Anzahl der Elemente Stephan Kleuker 183 Spielerei mit ArrayList import java.util.ArrayList; public class ListenSpielerei { public void spielen(){ ArrayList<Integer> zahlen = new ArrayList<Integer>(); EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben("1: "+zahlen+"\n"); zahlen.add(42); zahlen.add(41); io.ausgeben("2: "+zahlen+"\n"); 1: [] zahlen.add(1,23); 2: [42, 41] io.ausgeben("3: "+zahlen+"\n"); 3: [42, 23, 41] 4: 41 io.ausgeben("4: "+zahlen.get(2)+"\n"); 5: [67, 23, 41] zahlen.set(0, 67); 6: [23, 41] io.ausgeben("5: "+zahlen+"\n"); 7: 2 zahlen.remove(0); io.ausgeben("6: "+zahlen+"\n"); io.ausgeben("7: "+zahlen.size()+"\n"); Grundlagen Programmierung Stephan Kleuker } } 184 Schleife Grundlagen Programmierung Stephan Kleuker 185 Eingabedialog und Schleife • Student-Objekt soll durch Tastatureingabe im Dialog erstellt werden • Erstellung soll in neuer Klasse Studentenverwaltung liegen • Bei der Eingabe sollen Randbedingungen überprüft werden, wie: der eingegebene String ist nicht leer • Informeller Ansatz: Solange ein ungewünschter Wert eingegeben wird, soll die Eingabe wiederholt werden • Neues Sprachkonstrukt: Schleife Solange folgende Bedingung gilt, wiederhole folgende Schritte Grundlagen Programmierung Stephan Kleuker 186 Korrekte Eingabe des Vornamens public class Studentenverwaltung{ public Student studentEingeben(){ Student ergebnis = new Student(); EinUndAusgabe io = new EinUndAusgabe(); String tmpVorname = ""; while(tmpVorname.equals("")){ io.ausgeben("Vorname: "); tmpVorname = io.leseString(); } ergebnis.setVorname(tmpVorname); return ergebnis; } } Grundlagen Programmierung Stephan Kleuker 187 Ausprobieren in Studentspielerei (1/2) public class Studentspielerei{ public void eingeben(){ Studentenverwaltung sv = new Studentenverwaltung(); Student ein = sv.studentEingeben(); EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben(""+ein); } } Grundlagen Programmierung Stephan Kleuker 188 Ausprobieren in Studentspielerei (2/2) Grundlagen Programmierung Stephan Kleuker 189 Syntaxanalyse von while while(tmpVorname.equals("")){ io.ausgeben("Vorname: "); tmpVorname = io.leseString(); } • Boolescher Ausdruck (Bedingung) in runden Klammern, Ausdruck in der Klammer muss nach true oder false ausgewertet werden Grundlagen Programmierung • Programmfragment in geschweiften Klammern; wird ausgeführt, wenn Ausdruck nach true ausgewertet wird • nach Programmfragmentausführung wird Boolescher Ausdruck erneut ausgewertet Stephan Kleuker 190 Visualisierung im Aktivitätsdiagramm - generell while ( <Bedingung> ) { <Teilprogramm> } [!<Bedingung>] • Raute am Anfang, in der sich zwei Abläufe (Start und Wiederholung) vereinigen (auch als zwei Rauten darstellbar) • generell soll nur ein Pfeil in eine Aktion laufen Grundlagen Programmierung [<Bedingung>] <Teilprogramm> Stephan Kleuker 191 Visualisierung der Methode Student ergebnis = new Student() EinUndAusgabe io = new EinUndAusgabe() String tmpVorname = "" [!tmpVorname.equals("")] [tmpVorname.equals("")] io.ausgeben("Vorname: ") ergebnis.setVorname(tmpVorname) tmpVorname = io.leseString() Grundlagen Programmierung Stephan Kleuker return ergebnis 192 Klasse EinUndAusgabe.java Methode Nutzung EinUndAusgabe() Konstruktor leseString() Text einlesen bis "Return" Integer-Objekt einlesen, default -1 Boolean-Objekt einlesen, default false leseInteger() leseBoolean() leseDouble() ausgeben(String) zufall(Integer, Integer) Double-Objekt einlesen, default -1.0 Ausgabe des Strings auf der Konsole erzeugt zufälligen Integer-Wert zwischen den angegebenen Grenzen (inklusive) Grundlagen Programmierung Stephan Kleuker 193 Vervollständigung der Eingabe (1/4) • Man kann jetzt ein vergleichbares Programmfragment für alle Objektvariablen schreiben • aber Grundregel erinnern: kein Code wiederholen • Ansatz schreibe Hilfsmethoden – zum Einlesen eines nicht leeren Textes – zum Einlesen einer ganzen Zahl aus einem Intervall [untereGrenze, obereGrenze] • Hinweis 1: Software-Ergonomie nicht berücksichtigt • Hinweis 2: Eingabe mehrerer Leerzeichen wird als korrekter Text erkannt; String hat Methode trim() mit der alle umgebenden Leerzeichen entfernt werden Grundlagen Programmierung Stephan Kleuker 194 Vervollständigung der Eingabe (2/4) private String nichtLeererText(String aufforderung){ String ergebnis = ""; EinUndAusgabe io = new EinUndAusgabe(); while(ergebnis.equals("")){ io.ausgeben(aufforderung+": "); ergebnis = io.leseString(); ergebnis = ergebnis.trim(); } return ergebnis; } Grundlagen Programmierung Stephan Kleuker 195 Vervollständigung der Eingabe (3/4) private Integer zahlZwischen(String aufforderung, Integer untereGrenze, Integer obereGrenze ){ Integer ergebnis = untereGrenze - 1; EinUndAusgabe io = new EinUndAusgabe(); while(ergebnis < untereGrenze || ergebnis > obereGrenze){ io.ausgeben(aufforderung+": "); ergebnis = io.leseInteger(); } return ergebnis; } Grundlagen Programmierung Stephan Kleuker 196 Vervollständigung der Eingabe (4/4) public Student studentEingeben(){ Student ergebnis = new Student(); ergebnis.setVorname(nichtLeererText("Vorname")); ergebnis.setNachname(nichtLeererText("Nachname")); ergebnis.setStudiengang(nichtLeererText("Studiengang")); ergebnis.setGeburtsjahr( zahlZwischen("Geburtsjahr",1900,2000)); ergebnis.setMatrikelnummer( zahlZwischen("Matrikelnummer",99999,1000000)); return ergebnis; } Grundlagen Programmierung Stephan Kleuker 197 Beispiel für einen Nutzungsdialog Grundlagen Programmierung Stephan Kleuker 198 Analyse der Methodenschachtelung (1/2) • Möglichkeit 1: Ergebnisobjekt wird Parameter eines neuen Methodenaufrufs ergebnis.setVorname(nichtLeererText("Vorname")); alternativ: String tmp = nichtLeererText("Vorname"); ergebnis.setVorname(tmp); • Möglichkeit 2: auf berechnetem Ergebnisobjekt wird direkt nächste Methode aufgerufen ergebnis = io.leseString().ttrim(); alternativ: String tmp = io.leseString(); ergebnis = tmp.trim(); Grundlagen Programmierung Stephan Kleuker 199 Analyse der Methodenschachtelung (2/2) • Ist Methodenschachtelung besser als schrittweise Berechnung? • Vorteil: Programme werden kürzer • Vorteil: keine zusätzlichen Variablen (kein echter Vorteil, da Kompilierer den Zwischenschritt wegoptimiert) • Nachteil: wenn Hilfsvariable sinnvollen Namen hat, steigt die Lesbarkeit • Fazit: Gerade bei Anfängern sind Schachtelungen nicht notwendig • Grundregel: Programme müssen so geschrieben werden, dass der unerfahrenste Programmierer im Projekt diese verstehen und bearbeiten kann Grundlagen Programmierung Stephan Kleuker 200 Weitere Kürzungsmöglichkeit • statt public Boolean issesAndreas(){ if("Andreas".equals(this.name)){ return true; } return false; } • auch public Boolean issesAndreas(){ return "Andreas".equals(this.name); } Grundlagen Programmierung Stephan Kleuker 201 Gefahr der Endlosschleifen • Schleifen haben immer die Gefahr, nicht zu terminieren while(ergebnis < untereGrenze || ergebnis > obereGrenze){ • z. B. Nutzer "weigert sich" sinnvollen Wert einzugeben • aber auch wenn z. B. untereGrenze = 1 und obereGrenze = 0 Nutzer hat keine Chance für Eingabe, um Schleife zu beenden • Programme benötigen Abbruchmöglichkeiten • häufig: Betriebssystemprozess terminieren • Entwicklungsumgebungen bieten immer Abbruchmöglichkeit (in BlueJ Debugger aufrufen und "Terminieren" wählen) Grundlagen Programmierung Stephan Kleuker 202 Hölzerne Programmierung private String nichtLeererText(String aufforderung){ String ergebnis = ""; while(ergebnis.equals("")){ EinUndAusgabe io = new EinUndAusgabe(); // aua io.ausgeben(aufforderung+": "); ergebnis = io.leseString(); ergebnis = ergebnis.trim(); } return ergebnis; } • so muss immer wieder neues Objekt für io erzeugt werden (langsam; Java-Compiler optimiert zum Glück) Grundlagen Programmierung Stephan Kleuker 203 Regeln zur Nutzung lokaler Variablen • lokale Variablen möglichst genau da deklarieren, wo sie benötigt werden • wenn möglich, sollten lokale Variablen nicht lange leben (nicht mal genutzt und viele Zeilen später wieder genutzt werden) • lokale Variablen sollen sprechende Namen haben (irgendwas mit tmp vielleicht akzeptabel, wenn Variable drei Zeilen später stirbt) • lokale Variablen nur innerhalb von Schleifen deklarieren, wenn es sich um wahrscheinlich unterschiedlich genutzte Objekte handelt Grundlagen Programmierung Stephan Kleuker 204 Schleifen-Variante - Beispiel private String nichtLeererText(String aufforderung){ String ergebnis; EinUndAusgabe io = new EinUndAusgabe(); do{ io.ausgeben(aufforderung+": "); ergebnis = io.leseString(); ergebnis = ergebnis.trim(); }while(ergebnis.equals("")); return ergebnis; } Grundlagen Programmierung Stephan Kleuker 205 Analyse der do-Schleife do { <Teilprogramm> } while (<Bedingung>) <Teilprogramm> • wird mindestens einmal durchlaufen • Schleife wird wiederholt [<Bedingung>] [!<Bedingung>] solange Bedingung wahr ist • heißt auch fußgesteuerte Schleife (while ist kopfgesteuerte Schleife) • jede while-Schleife ist in do-Schleife verwandelbar und umgekehrt • viele Programmierer verzichten auf do-Schleife Grundlagen Programmierung Stephan Kleuker 206 switch Grundlagen Programmierung Stephan Kleuker 207 Einfacher Nutzungsdialog mit switch (1/3) • Typische Dialogstruktur mit Auswahlalternativen • würde langes verschachteltes if benötigen public class Minidialog { public void dialog(){ EinUndAusgabe io = new EinUndAusgabe(); Integer ein = -1; while(ein != 0){ io.ausgeben("(0) Programmende\n" +"(1) waschen, f\u00f6hnen, schneiden\n" +"(2) schneiden\n" +"(3) rasieren\n" +" Ihre Auswahl: "); ein = io.leseInteger(); Grundlagen Programmierung Stephan Kleuker 208 Einfacher Nutzungsdialog mit switch (2/3) } switch(ein){ case 0: { break; } case 1: { io.ausgeben("wash and dry\n"); } case 2: { io.ausgeben("cut\n"); break; } case 3: { io.ausgeben("shave\n"); break; } default: { io.ausgeben("Zahl zwischen 0 und 3\n"); break; } } Grundlagen Programmierung } } Stephan Kleuker 209 Einfache Nutzungsdialog mit switch (3/3) • Beispieldialog (0) Programmende (1) waschen, föhnen, schneiden (2) schneiden (3) rasieren Ihre Auswahl: 4 Zahl zwischen 0 und 3 (0) Programmende (1) waschen, föhnen, schneiden (2) schneiden (3) rasieren Ihre Auswahl: 3 shave (0) Programmende (1) waschen, föhnen, schneiden (2) schneiden (3) rasieren Ihre Auswahl: 2 cut Grundlagen Programmierung (0) Programmende (1) waschen, föhnen, schneiden (2) schneiden (3) rasieren Ihre Auswahl: 1 wash and dry cut (0) Programmende (1) waschen, föhnen, schneiden (2) schneiden (3) rasieren Ihre Auswahl: 0 Stephan Kleuker 210 Syntaxanalyse von switch switch(ein){ case 0: { break; } case 1: { io.ausgeben("wash"); } case 2: { io.ausgeben("cut\n"); break; } default: { io.ausgeben("Fehler"); break; } } Grundlagen Programmierung Stephan Kleuker Ausdruck, der zu einer Zahl oder einem Buchstaben (ab Java 7 auch String) ausgewertet wird Konstante, die zum Typ des Ausdruck passt; stimmen die Werte überein, wird zugehöriges Teilprogramm ausgeführt break zum Verlassen des Blocks, sonst werden Anweisungen des Folgeblocks ausgeführt default ist optional; genutzt, wenn kein anderer Fall zutrifft 211 Aktivitätsdiagramm für switch [ein==0] break [ein==1] [ein==2] io.ausgeben("wash") [!(ein==0 || ein==1 || ein==2)] io.ausgeben("Fehler") io.ausgeben("cut\n") break Grundlagen Programmierung Stephan Kleuker break 212 Programmfragment mit if statt switch } } if (ein == 0) { ; } else { if (ein == 1 || ein == 2) { io.ausgeben("wash and dry\n"); if (ein == 2) { io.ausgeben("cut\n"); } } else { if (ein == 3) { io.ausgeben("shave\n"); } else { io.ausgeben("Zahl zwischen 0 und 3\n"); } } } Grundlagen Programmierung Stephan Kleuker 213 Schleife 2 Grundlagen Programmierung Stephan Kleuker 214 Weitere Schleifenbeispiele • Innerhalb von Methoden "spürt" man wenig von Objektorientierung (hauptsächlich Aufruf von Methoden) • man greift auf Objektvariablen zu und deklariert lokale Hilfsvariablen • folgende Beispiele fokussieren Zahlen und Sammlungen von Zahlen (genauer ArrayList) • Methoden liegen in der Klasse public class Zahlenanalyse { private ArrayList<Integer> zahlen; private String name = "default"; public Zahlenanalyse(ArrayList<Integer> zahlen) { this.zahlen = zahlen; } … • name kann z. B. Messwerte oder Lottozahlen sein Grundlagen Programmierung Stephan Kleuker 215 weiterer Konstruktor, get – und set – Methoden public Zahlenanalyse(ArrayList<Integer> zahlen, String name) { this(zahlen); this.name = name; } public ArrayList<Integer> getZahlen() { return this.zahlen; } public void setZahlen(ArrayList<Integer> zahlen) { this.zahlen = zahlen; } public String getName() { return this.name; } public void setName(String name) { this.name = name; } Grundlagen Programmierung Stephan Kleuker 216 Ausgabe eines Zahlenintervalls • gebe auf der Konsole alle Zahlen ausgehend von einem Startwert bis zu einem Endwert (jeweils einschließlich) aus public void zeigeZahlen(Integer start, Integer ende){ Integer zaehler = start; EinUndAusgabe io = new EinUndAusgabe(); while(zaehler <= ende){ io.ausgeben(" "+zaehler); zaehler = zaehler +1; } } Grundlagen Programmierung Stephan Kleuker 217 Methode ausführen • Erzeuge Objekt der Klasse Zahlenanalyse mit Namen zahlenan1 -3 -2 -1 0 1 2 3 Grundlagen Programmierung Stephan Kleuker 218 Schmuddelige Variante • man kann in Methoden übergebene Objekte über deren Methoden verändern (wichtig, später mehr) • Grundregel: man soll nie Parameter als Hilfsvariablen nutzen public void zeigeZahlenX(Integer start, Integer ende){ EinUndAusgabe io = new EinUndAusgabe(); while(start <= ende){ io.ausgeben(" "+start); start = start +1; //geht, ist aber bah } } Grundlagen Programmierung Stephan Kleuker 219 Zeige Gerade Zahlen in einem Intervall • typisch: Innerhalb der Schleife wird jedes Element analysiert, ob es bestimmte Eigenschaft hat public void zeigeGeradeZahlen(Integer start, Integer ende){ Integer zaehler = start; EinUndAusgabe io = new EinUndAusgabe(); while(zaehler <= ende){ if(zaehler % 2 == 0){ io.ausgeben(" "+zaehler); } zaehler = zaehler + 1; } } • untypisch: Berechnung und Ausgabe innerhalb einer Methode, besser: eine Methode für Berechnungen, eine Methode für Ausgaben (u. a. Ausgabe leicht änderbar) Grundlagen Programmierung Stephan Kleuker 220 Aufteilung von Berechnung und Ausgabe (1/3) public void geradeZahlenAus(Integer start, Integer ende){ if (this.zahlen == null){ this.zahlen = new ArrayList<Integer>(); } Integer zaehler = start; while(zaehler <= ende){ if(zaehler % 2 == 0){ this.zahlen.add(zaehler); } zaehler = zaehler + 1; } } • Hier Nutzung der Objektvariable zahlen • generell erst prüfen, ob zahlen==null, sonst Fehler • was passiert bei mehrmaligem Aufruf? Grundlagen Programmierung Stephan Kleuker 221 Aufteilung von Berechnung und Ausgabe (2/3) • einfaches Iterieren über eine Liste mit get und size (etwas hölzern, siehe später) public void listeUntereinanderAusgeben(){ EinUndAusgabe io = new EinUndAusgabe(); Integer zaehler = 0; while(zaehler < this.zahlen.size()){ io.ausgeben("\t"+this.zahlen.get(zaehler)+"\n"); zaehler = zaehler + 1; } } Grundlagen Programmierung Stephan Kleuker 222 Aufteilung von Berechnung und Ausgabe (3/3) • Ausprobieren in nutzender Hilfsklasse import java.util.ArrayList; public class ZahlenanalyseSpielerei { public void zeigeGeradeZahlen(){ EinUndAusgabe io = new EinUndAusgabe(); Startwert: -3 Zahlenanalyse zana = new Zahlenanalyse(null); Endwert: 3 -2 io.ausgeben("Startwert: "); 0 Integer start = io.leseInteger(); 2 io.ausgeben("Endwert: "); Integer ende = io.leseInteger(); zana.geradeZahlenAus(start, ende); zana.listeUntereinanderAusgeben(); } } Grundlagen Programmierung Stephan Kleuker 223 Algorithmus Grundlagen Programmierung Stephan Kleuker 224 Primzahlanalyse – Aufgabenstellung • Primzahl: Natürliche Zahl n mit zwei Teilern: 1 und n • konkret: 2, 3 ,5 ,7, … • erste Überlegung zum Berechnungsverfahren (Algorithmus): – Eingabe soll ein Integer-Objekt n sein – nimm zunächst an, dass es eine Primzahl ist – prüfe von zwei aufsteigend bis n, ob n durch diese Zahl teilbar ist; ist es so, dann ist es keine Primzahl – gib Ergebnis zurück – da ganze Zahl übergeben wird, muss für nicht-natürliche Zahlen die Antwort false sein (da Primzahlen erst bei zwei beginnen, gilt das Ergebnis für alle n<2) Grundlagen Programmierung Stephan Kleuker 225 Primzahlanalyse – Version 0 (nicht schlecht) public Boolean istPrimzahl0(Integer zahl){ if (zahl<2){ return false; } Boolean ergebnis = true; Integer testwert = 2; while(testwert<zahl){ if(zahl%testwert == 0){ ergebnis = false; } testwert = testwert + 1; } return ergebnis; } Grundlagen Programmierung Stephan Kleuker 226 Primzahlanalyse – Version 1 (optimiert) • gerade Zahlen ausschließen • Schleife beenden, wenn es keine Primzahl ist public Boolean istPrimzahl(Integer zahl){ if (zahl<2){ return false; } Boolean ergebnis = ((zahl % 2 != 0) || (zahl == 2)); Integer testwert = 3; while(testwert<zahl && ergebnis == true){ if(zahl%testwert == 0){ ergebnis = false; } testwert = testwert + 2; } return ergebnis; } • fehlt: Schleife nur bis Wurzel aus n laufen lassen Grundlagen Programmierung Stephan Kleuker 227 Der Begriff des Algorithmus (1/2) • Ein Algorithmus ist ein Verfahren (genauer die Beschreibung eines Verfahrens) zur Lösung einer gegebenen Aufgabe • “Algorithmus” ist einer der zentralen Begriffe der Informatik, aber auch bekannt in vielen anderen Zusammenhängen, z. B. – Kochrezept – Anweisung zum Zusammenbau eines Geräts, Möbelstücks, . . . – Gebrauchsanweisung – Musik-Noten • Die Beschreibung eines Algorithmus kann in sehr unterschiedlicher Form (Sprache) gegeben werden, z. B. Programmiersprache Grundlagen Programmierung Stephan Kleuker 228 Der Begriff des Algorithmus (2/2) • Der Begriff leitet sich ab von der latinisierten Form des Namens Muhammad ibn Musa Al-Khwarizmi • (Abu Ja’far) Muhammad ibn Musa Al-Khwarizmi: – bedeutender Mathematiker und Astronom, – lebte ca. 780 – 840, hauptsächlich in Bagdad – führte Null und Dezimalsystem in westliche Welt ein – Sein Text Hisab al-jabr w’al-muqabala ist erstes Lehrbuch zur Algebra, – beschreibt insbesondere das Lösen von Gleichungen; – der Begriff “Algebra” ist vom Titel abgeleitet Grundlagen Programmierung Stephan Kleuker 229 Eigenschaften von Algorithmen Eigenschaft Bedeutung Komplexität charakterisiert den für die Ausführung erforderlichen Aufwand an Rechenzeit und Speicherplatz Effizienz der Algorithmus kommt mit möglichst wenig Zeit- und Speicheraufwand aus Endlichkeit Beschreibung des Algorithmus ist von endlicher Länge (statisch) Endlichkeit für Ausführung benötigten Ressourcen (Speicherplatz, (dynamisch) Rechenzeit) sind jederzeit endlich Terminiert- für jede zulässige Eingabe liefert der Algorithmus nach heit endlich vielen Schritten ein Ergebnis DetermiZu jeder Aktion existiert in einem Algorithmus nismus höchstens eine Folgeaktion und der Berechnungsablauf ist dadurch eindeutig bestimmt DetermiAusgabewerte eindeutig durch die Eingabewerte niertheit bestimmt (Determinismus impliziert Determiniertheit) Grundlagen Programmierung Stephan Kleuker 230 Eigenschaften von istPrimzahl(Integer zahl) Eigenschaft Bedeutung Komplexität Schrittanzahl klar von Eingabe abhängig, benötigte Variablen einfach ablesbar Effizienz Version 1 ist schneller als Version 0 (weniger Schritte) Endlichkeit (statisch) Endlichkeit (dynamisch) Terminiertheit Determinismus ca. 14 Zeilen Determiniertheit für jeden Integer-Wert jederzeit das gleiche Ergebnis Speicher nur für Variablen ergebnis und testwert, Rechenzeit begrenzt durch zahl*x Schritte klare, für bestimmte Zahl immer gleiche Antwort: true oder false Sequenz, Schleife mit ja oder nein, if mit einem Fall Grundlagen Programmierung Stephan Kleuker 231 Zentrale Aufgabe: Algorithmen optimieren • • • • 0. Schritt: Klare Definition der Aufgabenstellung 1. Schritt: Entwicklung einer Lösung 2. Schritt: Optimierung der Lösung Abhängig von der Relevanz der Aufgabenstellung steigt oder fällt die Optimierungsbedeutung • Bremsen im Auto, Turbulenzen beim Fliegen, andere Umwelteinflüsse beim Raketenstart, auf einen zufliegender Flugkörper, minimale Schwankung des Aktienpreises: Effizienz bekommt enorme Bedeutung! • Vorlesung "Algorithmen und Datenstrukturen" (nicht nur Verfahren, auch Schulung der Denkweise) Grundlagen Programmierung Stephan Kleuker 232 Bestimme Primzahl in einem Zahlenbereich • Aufgabe: Fülle Liste in Zahlenanalyse mit Primzahlen aus einem vorgegebenen Bereich public void zahlbereichMitPrim(Integer start, Integer ende) { if (zahlen == null){ this.zahlen = new ArrayList<Integer>(); } Integer zaehler = start; while (zaehler <= ende) { if (istPrimzahl(zaehler)) { this.zahlen.add(zaehler); } zaehler = zaehler + 1; } } Grundlagen Programmierung Stephan Kleuker 233 Listenauswahl • Suche alle Zahlen, die mit der Ziffer 3 enden und füge sie in ein neues Objekt der Klasse Zahlenanalyse public Zahlenanalyse zahlenMitDreiAmEnde() { ArrayList<Integer> ergebnis = new ArrayList<Integer>(); Integer zaehler = 0; if (this.zahlen != null) { while (zaehler < this.zahlen.size()) { Integer element = this.zahlen.get(zaehler); if (element % 10 == 3) { ergebnis.add(element); } zaehler = zaehler + 1; } } return new Zahlenanalyse(ergebnis); } Grundlagen Programmierung Stephan Kleuker 234 geschachtelte Schleifen Grundlagen Programmierung Stephan Kleuker 235 Einschub: Ineinander geschachtelte Schleifen • die innere Schleife wird für jedes Element der äußeren Schleife durchlaufen !!! public void schleifeInSchleife(){ EinUndAusgabe io = new EinUndAusgabe(); Integer zaehler = 0; while (zaehler < 5){ Integer zaehler2 = 20; while (zaehler2 < 25){ io.ausgeben(zaehler+":"+zaehler2+" "); zaehler2 = zaehler2 + 1; 0:20 0:21 0:22 } 1:20 1:21 1:22 zaehler = zaehler + 1; 2:20 2:21 2:22 io.ausgeben("\n"); 3:20 3:21 3:22 4:20 4:21 4:22 } } Grundlagen Programmierung Stephan Kleuker 0:23 1:23 2:23 3:23 4:23 0:24 1:24 2:24 3:24 4:24 236 Strukturierte Ausgabe in Zahlenanalyse • in jeder Zeile genau anzahl Elemente (Ausnahme letzte Zeile) • Zwei Schleifen mit einem äußeren und einem inneren Zähler public void strukturiertAusgeben(Integer anzahl){ if (this.zahlen == null){ return; } EinUndAusgabe io = new EinUndAusgabe(); Integer zaehler = 0; while(zaehler < this.zahlen.size()){ Integer zeilenzaehler = 0; while(zeilenzaehler<anzahl && zaehler < this.zahlen.size()){ io.ausgeben(""+this.zahlen.get(zaehler)+"\t"); zeilenzaehler = zeilenzaehler + 1; zaehler = zaehler + 1; } io.ausgeben("\n"); } Grundlagen Programmierung Stephan Kleuker 237 } Strukturierte Ausgabe - Variante • hier geht es auch ohne doppelte Schleifen public void strukturiertAusgeben(Integer anzahl) { if (zahlen == null){ return; } EinUndAusgabe io = new EinUndAusgabe(); Integer zaehler = 0; while (zaehler < this.zahlen.size()) { io.ausgeben("" + this.zahlen.get(zaehler) + "\t"); zaehler = zaehler + 1; if(zaehler % anzahl == 0){ io.ausgeben("\n"); } } } Grundlagen Programmierung Stephan Kleuker 238 Anwendung bisheriger Methoden public void primAnalysieren(){ Zahlenanalyse zana = new Zahlenanalyse(null); zana.zahlbereichMitPrim(100, 470); Zahlenanalyse tmp = zana.zahlenMitDreiAmEnde(); tmp.strukturiertAusgeben(5); } 103 113 163 173 193 223 233 263 283 293 313 353 373 383 433 443 463 letzten beiden Zeilen alternativ zusammengefasst als: zana.zahlenMitDreiAmEnde().strukturiertAusgeben(5) Grundlagen Programmierung Stephan Kleuker 239 Gemeinsamkeit (1/3) • Aufgabe: Gibt es in zwei Listen gemeinsame Elemente? • Ansatz: Annahme, es gibt keine gemeinsamen Elemente, bis eines gefunden ist • Nimm erstes Element der ersten Liste und überprüfe mit jedem Element der zweiten Liste ob diese gleich sind, wenn true, gibt Antwort aus • … • Nimm letztes Element der ersten Liste und überprüfe mit jedem Element der zweiten Liste ob diese gleich sind, wenn true, gibt Antwort aus • Gib false als Antwort aus Grundlagen Programmierung Stephan Kleuker 240 Gemeinsamkeit (2/3) public Boolean gibtGleiche(Zahlenanalyse other) { ArrayList<Integer> liste = other.getZahlen(); Integer zaehler1 = 0; while (zaehler1 < this.zahlen.size()) { Integer zaehler2 = 0; while (zaehler2 < liste.size()) { if(this.zahlen.get(zaehler1).equals(liste.get(zaehler2))){ return true; } Achtung, hier fehlt was: zaehler2 = zaehler2 + 1; Es fehlen die Prüfungen, } ob eine der Listen null ist zaehler1 = zaehler1 + 1; (Fehler werden wir später finden) } return false; } Grundlagen Programmierung Stephan Kleuker 241 Gemeinsamkeit (3/3) public void gleicheAnalysieren() { Zahlenanalyse tmp1 = new Zahlenanalyse(null); tmp1.geradeZahlenAus(1, 23); Zahlenanalyse tmp2 = new Zahlenanalyse(null); tmp2.geradeZahlenAus(23, 42); Zahlenanalyse tmp3 = new Zahlenanalyse(null); tmp3.geradeZahlenAus(22, 32); EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben("tmp1 und tmp2: "+tmp1.gibtGleiche(tmp2)+ io.ausgeben("tmp2 und tmp1: "+tmp2.gibtGleiche(tmp1)+ io.ausgeben("tmp1 und tmp3: "+tmp1.gibtGleiche(tmp3)+ io.ausgeben("tmp3 und tmp1: "+tmp3.gibtGleiche(tmp1)+ } tmp1 und tmp2: false tmp2 und tmp1: false tmp1 und tmp3: true tmp3 und tmp1: true Grundlagen Programmierung Stephan Kleuker "\n"); "\n"); "\n"); "\n"); 242 Iterator Grundlagen Programmierung Stephan Kleuker 243 Effizientere Listenbearbeitung - Iterator • Problem: Der Zugriff mit get(i) ist relativ langsam, man kann schneller von i zum i+1-Element manövrieren • Zur schrittweisen Bearbeitung von Listen gibt es IteratorObjekte; Objekte kennt aktuell betrachtetes Element • Listen haben Methode iterator(); gibt Iteratorobjekt • Auszug aus Methoden von Iterator<T> MethoParameter Ergebnis denname hasNext Boolean Beschreibung next gibt aktuelles Element des Iterators zurück und setzt ihn ein Element weiter Grundlagen Programmierung Typ gibt es für Iterator nächstes Element? Stephan Kleuker 244 Iteratornutzung - allgemein X Z liste Z liste Iterator<Integer> i = liste.iterator() i.hasNext() ergibt true X liste.next() ergibt X und Iterator wird Feld weiter gesetzt i.hasNext() ergibt true X Z liste liste.next() ergibt Z und Iterator wird Feld weiter gesetzt i.hasNext() ergibt false Grundlagen Programmierung Stephan Kleuker 245 Methodenvergleich public void listeUntereinanderAusgebenIterator() { EinUndAusgabe io = new EinUndAusgabe(); Iterator<Integer> it = this.zahlen.iterator(); while (it.hasNext()) { io.ausgeben("\t" + it.next() + "\n"); } } public void listeUntereinanderAusgebenAlt() { EinUndAusgabe io = new EinUndAusgabe(); Integer zaehler = 0; while (zaehler < this.zahlen.size()) { io.ausgeben("\t" + this.zahlen.get(zaehler) + "\n"); zaehler = zaehler + 1; } } Grundlagen Programmierung Stephan Kleuker 246 Analyse vom Iterator • sehr einfacher Durchlauf durch die Liste • (meist) etwas schnellerer Durchlauf durch die Liste • sicherer Durchlauf, da man nicht einfach auf nicht existente Elemente zugreifen kann • es ist allerdings nicht möglich, die Nummer (Position) des betrachteten Elements auszugeben (mit mitlaufender Zählvariable schon) Grundlagen Programmierung Stephan Kleuker 247 for Grundlagen Programmierung Stephan Kleuker 248 for-Schleife • häufig sehr ähnliche Struktur einer Schleife Integer zaehler = 0; while (zaehler …) { // do something zaehler = zaehler + 1; } • für Schleifen mit festem Start- und Endwert gibt es Variante for(Integer zaehler = 0; zaehler…; zaehler = zaehler+1) for( <Startanweisung>; <Abbruchbedingung>; <AnweisungNachTeilprogramm>) { <Teilprogramm> } Grundlagen Programmierung Stephan Kleuker 249 Schleifenvergleich public void zeigeZahlen(Integer start, Integer ende) { Integer zaehler = start; EinUndAusgabe io = new EinUndAusgabe(); while (zaehler <= ende) { io.ausgeben(" " + zaehler); zaehler = zaehler + 1; } } public void zeigeZahlenFor(Integer start, Integer ende) { EinUndAusgabe io = new EinUndAusgabe(); for(Integer zaehler = start; zaehler<= ende; zaehler = zaehler+1){ io.ausgeben(" " + zaehler); } } Grundlagen Programmierung Stephan Kleuker 250 for-Schleife als Aktivitätsdiagramm for(Integer zaehler = start; zaehler<= ende; zaehler = zaehler+1){ io.ausgeben(" " + zaehler); } Integer zaehler = start [!(zaehler<= ende)] [zaehler<= ende] io.ausgeben(" " + zaehler) zaehler = zaehler+1 Grundlagen Programmierung Stephan Kleuker 251 for-Schleife mit Iterator public void listeUntereinanderAusgebenIterator() { EinUndAusgabe io = new EinUndAusgabe(); Iterator<Integer> it = this.zahlen.iterator(); while (it.hasNext()) { io.ausgeben("\t" + it.next() + "\n"); } } public void listeUntereinanderAusgebenIteratorFor() { EinUndAusgabe io = new EinUndAusgabe(); for(Iterator<Integer> it = this.zahlen.iterator(); it.hasNext();) { io.ausgeben("\t" + it.next() + "\n"); } } Grundlagen Programmierung Stephan Kleuker 252 Regeln für for-Schleifen for( <Startanweisung>; <Abbruchbedingung>; <AnweisungNachTeilprogramm>) • erfahrener Programmierer erwartet beim Lesen einer ForSchleife, dass beim Start der Schleife bekannt ist, wie oft sie durchlaufen wird (Abbruchbedingung nicht im Schleifenrumpf verändern) • <Startanweisung> kann leer gelassen werden • <AnweisungNachTeilprogramm> kann weggelassen werden Grundlagen Programmierung Stephan Kleuker 253 for-Schleife mit impliziten Iterator • Listendurchlauf sieht häufig so aus: for(Iterator<Integer> it = liste.iterator(); it.hasNext();) • Ab Java 5: leichte Abkürzung public void listeUntereinanderAusgebenIteratorForEach() { EinUndAusgabe io = new EinUndAusgabe(); for(Integer el:this.zahlen) { io.ausgeben("\t" + el + "\n"); } } • Achtung: Nutzung des Iterator-Objekts ist wichtige, sich in vielen Programmierbereichen wiederholende, Idee zur Abarbeitung von Sammlungen (Listen, Mengen, Einlesen von Dateien, Einlesen von Daten aus Datenbank …) Grundlagen Programmierung Stephan Kleuker 254 Einfaches Durchlaufen von Sammlungen for(Integer el: this.zahlen) { io.ausgeben("\t" + el + "\n"); } • Sammlung , die durchlaufen werden soll • Typ der Elemente der Sammlung • lokale Variable vom Typ der Elemente der Sammlung; Variable nimmt nacheinander alle Werte der Sammlung an • Programm, dass für alle Elemente der Sammlung ausgeführt wird Grundlagen der Programmierung Stephan Kleuker 255 Unit test Grundlagen Programmierung Stephan Kleuker 256 Ausprobieren oder systematisch Testen • bisher werden Methoden zum Ausprobieren direkt in BlueJ aufgerufen und das Ergebnis manuell überprüft • um Schritte zusammenzufassen, wurden Hilfsklassen wie ZahlenAnalyseSpielerei geschrieben • dieser Ansatz wird durch die Nutzung spezieller Testklassen vereinfacht: – Nutzung von JUnit (hier Version 4) – Tests werden in neuer Klasse (ähnlich zu Spielerei) in Java geschrieben – Man kann für Tests gemeinsame Ausgangssituation schaffen – Jeder Test läuft unabhängig von anderen Tests – es gibt spezielle Zusicherungsmethoden zum Überprüfen Grundlagen Programmierung Stephan Kleuker 257 Testerstellung in BlueJ Grundlagen Programmierung Stephan Kleuker 258 Erste Testfälle (1/2) import org.junit.Assert; import org.junit.Test; public class ZahlenanalyseTest{ @Test public void testIstPrimzahl1() { Zahlenanalyse zana = new Zahlenanalyse(null); Boolean nicht42 = zana.istPrimzahl(42); Assert.assertTrue("42 ist Primzahl", !nicht42); } @Test public void testIstPrimzahl2() { Zahlenanalyse zana = new Zahlenanalyse(null); Boolean doch41 = zana.istPrimzahl(41); Assert.assertTrue("41 ist keine Primzahl", doch41); } } Grundlagen Programmierung Stephan Kleuker 259 Erste Testfälle (2/2) Grundlagen Programmierung Stephan Kleuker 260 Aufbau der Testklasse Klassenname beliebig, üblich: Name der zu testenden Klasse mit "Test" am Ende jeder Test in eigener Methode, sollte mit "public void test…" beginnen; Testname sollte sich auf getestete Methode beziehen diese Annotation (?!?) sorgt für die Ausführung als Test[genauer später] public class ZahlenanalyseTest { @Test public void testIstPrimzahl1() { Zahlenanalyse zana = new Zahlenanalyse(null); Boolean nicht42 = zana.istPrimzahl(42); Assert.assertTrue("42 ist Primzahl", !nicht42); } es gibt verschiedene Überprüfungsmethoden (assertTrue reicht); erster Parameter ist Meldung im Fehlerfall, zweiter Parameter ist Boolescher Ausdruck (erster Parameter optional) Grundlagen Programmierung Stephan Kleuker 261 Test-Fixture (Test-Ausgangskonfiguration) • Testfall sieht in der Regel so aus, dass eine bestimmte Konfiguration von Objekten aufgebaut wird, gegen die der Test läuft • Menge von Testobjekten wird als Test-Fixture bezeichnet • Damit fehlerhafte Testfälle nicht andere Testfälle beeinflussen können, wird die Test-Fixture für jeden Testfall neu initialisiert • In der mit @Before annotierten Methode public void setUp() werden Objektvariablen initialisiert • In der mit @After annotierten Methode public void tearDown() werden wertvolle Testressourcen wie zum Beispiel Datenbank- oder Netzwerkverbindungen wieder freigegeben Grundlagen Programmierung Stephan Kleuker 262 Testklasse leicht umgeschrieben public class ZahlenanalyseTest { private Zahlenanalyse zana; @Before public void setUp() { zana = new Zahlenanalyse(null); } @Test public void testIstPrimzahl1() { Boolean nicht42 = zana.istPrimzahl(42); Assert.assertTrue("42 ist Primzahl", !nicht42); } @Test public void testIstPrimzahl2() { Boolean doch41 = zana.istPrimzahl(41); Assert.assertTrue("41 ist keine Primzahl", doch41); } }Grundlagen Programmierung Stephan Kleuker 263 Prinzipieller Ablauf von JUnit • Suche in der Testklasse alle Methoden, die mit @Test annotiert sind, • führe für jede der gefundenen Methoden folgende Schritte aus: – führe setUp (mit @Before annotierte Methode) aus – führe den Test aus und protokolliere das Ergebnis – führe tearDown (mit @After annotierte Methode) aus • im Beispiel: setup(), testIstPrimzahl1(), setup(), testIstPrimzahl2() • sollte ein Test scheitern, wird dieser sofort beendet, die anderen Tests unabhängig davon ausgeführt • Tests sollten möglichst nur eine Zusicherung (assert…) haben Grundlagen Programmierung Stephan Kleuker 264 Wie erstellt man Testfälle • Grundsätzlich liest man intensiv die Aufgabenstellung und beantwortet folgende Fragen: • was sind die typischen Abläufe bzw. typischen Ergebnisse; für jeden der gefundenen Fälle wird ein Test geschrieben • was sind die besonderen Randfälle, die auftreten können; für jeden Randfall wird ein getrennter Testfall geschrieben • Beispiel: ein Parameter liste vom Typ ArrayList<Integer> – was passiert, wenn liste==null ist – was passiert bei einer leeren Liste – was passiert bei besonderen Listen, die z. B. nur ein Element, dies aber mehrfach enthalten Grundlagen Programmierung Stephan Kleuker 265 Weiteres Testbeispiel (1/4) // Imports fehlen public class ZahlenanalyseTest { private Zahlenanalyse zana42und23; private Zahlenanalyse zanaNull; private Zahlenanalyse zana23; @Before public void setUp() { ArrayList<Integer> l1 = new ArrayList<Integer>(); l1.add(42); l1.add(23); zana42und23 = new Zahlenanalyse(l1); zanaNull = new Zahlenanalyse(null); ArrayList<Integer>l2 = new ArrayList<Integer>(); l2.add(23); zana23 = new Zahlenanalyse(l2); } Grundlagen Programmierung Stephan Kleuker 266 Weiteres Testbeispiel (2/4) @Test public void testZahlenMitDreiAmEnde1(){ Zahlenanalyse tmp = zana42und23.zahlenMitDreiAmEnde(); Assert.assertTrue(tmp.getZahlen().size() == 1); Assert.assertTrue(tmp.getZahlen().get(0) == 23); } @Test public void testZahlenMitDreiAmEnde2(){ Zahlenanalyse tmp = zanaNull.zahlenMitDreiAmEnde(); Assert.assertTrue(tmp.getZahlen().size() == 0); } @Test public void testZahlenMitDreiAmEnde3(){ Zahlenanalyse tmp = zana23.zahlenMitDreiAmEnde(); Assert.assertTrue(tmp.getZahlen().size() == 1); Assert.assertTrue(tmp.getZahlen().get(0) == 23); } Grundlagen Programmierung Stephan Kleuker 267 Weiteres Testbeispiel (3/4) @Test public void testgibtGleiche1(){ Boolean tmp = zana42und23.gibtGleiche(zanaNull); Assert.assertTrue(!tmp); } @Test public void testgibtGleiche2(){ Boolean tmp = zana42und23.gibtGleiche(zana23); Assert.assertTrue(tmp); } @Test public void testgibtGleiche3(){ Boolean tmp = zana23.gibtGleiche(zana42und23); Assert.assertTrue(!tmp); //?!? } Grundlagen Programmierung Stephan Kleuker 268 Weiteres Testbeispiel (4/4) ein Test zeigt, dass nullReferenzen nicht berücksichtigt werden, anderer Test zeigt falschen Testfall Grundlagen Programmierung Stephan Kleuker 269 Systematische Entwicklung • Neben echten Denkfehlern befinden sich häufig viele Flüchtigkeitsfehler in Programmen • Sinnvoll: Inkrementelle Entwicklung, d. h. nachdem eine Methode implementiert wurde, werden sofort zugehörige Testfälle geschrieben • Variante ist "Test first"; nachdem man die Aufgabenstellung verstanden hat, programmiert man zunächst die Testfälle und schreibt dann schrittweise immer mehr Programmteile, so dass immer mehr Testfälle keine Fehler melden • man beachte, dass Entwickler betriebsblind gegenüber ihren eigenen Fehlern werden Grundlagen Programmierung Stephan Kleuker 270 Hinweise zu JUnit • JUnit kann auch zur Organisation von Tests (Gruppierungen) genutzt werden • JUnit oder TestNG Standard bei den meisten Entwicklern • JUnit kann so nicht (nicht einfach) Eingaben in der Konsole simulieren • es gibt viele weitere Testwerkzeuge, einige auf Basis von JUnit, die viele andere Tests automatisiert durchführen können • Man muss Programme aber auch in Richtung Testbarkeit entwickeln: – get- und set-Methoden für alle Objektvariablen – evtl. private-Methoden public machen Grundlagen Programmierung Stephan Kleuker 271 Systematische Objektbearbeitung (1/2) • hat ein Objekt eine Collection von Objekten (z. B. ArrayList) in einer Objektvariablen, so gilt folgende Regel zur Bearbeitung: • grundsätzlich stellt das Objekt selbst Methoden zur Verfügung, um die Collection zu bearbeiten, d. h. Elemente zu suchen • also schlecht: Zahlenanalyse tmp = zanaNull.zahlenMitDreiAmEnde(); if(tmp.getZahlen().size() == 0)) ... • besser: Zahlenanalyse bietet Methoden wie: – berechneListengroesse() – hinzufuegen(Integer) Grundlagen Programmierung Stephan Kleuker 272 Systematische Objektbearbeitung (2/2) • Veranschaulichung als schlechte Idee: Organe herausoperieren, einschicken, bearbeiten, zurückschicken und wieder einsetzen • also keine getZahlen()-Methode? Diskutabel • wir benötigen Methode zum effizienten Testen • in Tests darf gegen gewisse Regeln verstoßen werden (muss aber nicht) • Klassen müssen ab und zu Methoden enthalten, die das Testen erleichtern bzw. erst ermöglichen Grundlagen Programmierung Stephan Kleuker 273 iterativ inkrementelle Entwicklung Grundlagen Programmierung Stephan Kleuker 274 Fallstudie: Studentenverwaltung • Aufgabenstellung: Zu entwickeln ist eine auf Texteingaben und Textausgaben basierende Verwaltung von Studierenden, dabei soll folgende Funktionalität geboten werden: – neuen Studenten eintragen und in die Verwaltung übernehmen – Studenten anhand der Matrikelnummer suchen – Vor- und Nachname eines anhand seiner Matrikelnummer identifizierten Studenten ändern – Ermittlung der Anzahl der Studierenden in einem ausgewählten Studiengang • Die bisherige Klasse Student kann genutzt werden Grundlagen Programmierung Stephan Kleuker 275 Zentrale Idee 1: systematische Planung • welche Klassen benötigt man • welche Methoden müssen Objekte der Klassen zur Verfügung stellen • ersten beiden Schritte werden schrittweise durch Programmieraufgaben gelehrt und in der Veranstaltung "Objektorientierte Analyse und Design" aufgearbeitet • welchen Algorithmus nutzt man in den Methoden • Erfahrungen einfließen lassen • ähnliche Aufgaben mit zugehörigen Lösungen studieren • Algorithmus auf Papier als Aktivitätsdiagramm spezifizieren • Inkrementell und iterativ entwickeln Grundlagen Programmierung Stephan Kleuker 276 Zentrale Idee 2: Inkrementelle Entwicklung • Programm wird schrittweise entwickelt • zuerst eine Funktionalität vollständig realisieren, dann nächste ergänzen • jede entwickelte Funktionalität wird zuerst getestet, dann neue hinzugefügt • nach jeder neuen Funktionalität werden alte Tests wiederholt – Vorgehen auch auf Aktivitätsdiagramme übertragbar: – erst typischen Ablauf (einfache Sequenz) zeichnen – dann für jede Aktion nacheinander über mögliche Alternativen nachdenken Grundlagen Programmierung Stephan Kleuker 277 Zentrale Idee 3: Iterative Entwicklung • zuerst nur minimale Implementierung public void methodeOhneRueckgabe(<Parameter>){ } public Integer methodeMitRueckgabe(<Parameter>){ return null; } • dann typischen Ablauf ergänzen • dann Alternativen analysieren, Spezialfälle früh abarbeiten • bei komplexeren Berechnungen über Auslagerung in private Methoden nachdenken • bei Code-Wiederholung Auslagerung in private Methode • (eng verwandt mit Inkrement-Begriff) Grundlagen Programmierung Stephan Kleuker 278 Erste Programmplanung • statt Programmcode steht gewünschte Berechnung in Aktion Funktionalität auswählen [neuer Studi] Studentendaten eintragen und in Liste packen [Studi suchen] [Studi ändern] Matrikelnummer eingeben und zugehörigen Studenten ausgeben Grundlagen Programmierung [Anzahl berechnen] Matrikelnummer eingeben und Studentendaten ändern Stephan Kleuker [Ende] Studiengang eingeben und zugehörige Studentenzahl ausgeben 279 weitere Entwicklung • nach der ersten Planung kann mit der Realisierung begonnen werden • Planung erlaubt hier parallele Arbeiten an einzelnen Funktionalitäten, nachdem Klassenaufbau geklärt ist import java.util.ArrayList; public class Studentenverwaltung{ private ArrayList<Student> studenten; public Studentenverwaltung(){ this.studenten = new ArrayList<Student>(); } • hier nur ein Weg vorgestellt Grundlagen Programmierung Stephan Kleuker 280 Überlegung zu get- und set-Methoden • set-Methode und Konstruktor garantieren, dass man nicht über die leere Liste nachdenken muss • damit nicht auf "Innereien" gearbeitet wird, gibt es Methode hinzufuegen public ArrayList<Student> getStudenten() { return this.studenten; } public void setStudenten(ArrayList<Student> studenten) { if(studenten != null){ this.studenten = studenten; } } public void hinzufuegen(Student neu){ this.studenten.add(neu); } Grundlagen Programmierung Stephan Kleuker 281 Realisierung des Dialogs (Prinzip bekannt) (1/2) public void steuern(){ Integer ein = -1; EinUndAusgabe io = new EinUndAusgabe(); while(ein !=0){ io.ausgeben("N\u00e4chste Aktion:\n" + " (0) Programm beenden\n" + " (1) Neuen Studenten einf\u00fcgen\n" + " (2) Student mit Matrikelnummer suchen\n" + " (3) Studierendendaten \u00e4ndern\n" + " (4) Studierende pro Studiengang: "); ein = io.leseInteger(); switch(ein){ case 0: { break; } case 1: { studentEingeben(); break; } Grundlagen Programmierung Stephan Kleuker 282 Realisierung des Dialogs (Prinzip bekannt) (2/2) case 2: { studentSuchen(); break; } case 3: { studentBearbeiten(); break; } case 4: { studentenZaehlen(); break; } default: { io.ausgeben("Zahl zwischen 0 und 4 eingeben\n"); break; } } } } io.ausgeben("\n"); Grundlagen Programmierung Stephan Kleuker 283 Erstes Inkrement mit erster Iteration komplettieren • Steuerung manuell testbar durch folgende Methoden public void studentEingeben(){ } public void studentSuchen(){ } public void studentBearbeiten(){ } public void studentenZaehlen(){ } Grundlagen Programmierung Stephan Kleuker 284 studentEingeben() mit Hilfsmethoden (bekannt) • letzte Zeile leicht modifiziert public void studentEingeben(){ Student ergebnis = new Student(); ergebnis.setVorname(nichtLeererText("Vorname")); ergebnis.setNachname(nichtLeererText("Nachname")); ergebnis .setStudiengang(nichtLeererText("Studiengang")); ergebnis .setGeburtsjahr(zahlZwischen("Geburtsjahr",1900,2000)); ergebnis .setMatrikelnummer(zahlZwischen("Matrikelnummer" ,99999,1000000)); this.studenten.add(ergebnis); } Grundlagen Programmierung Stephan Kleuker 285 studentSuchen() – Aktivitätsdiagramm verfeinern Iteration 0 Matrikelnummer eingeben und zugehörigen Student ausgeben Iteration 2 Matrikelnummer eingeben Student suchen Iteration 1 Matrikelnummer eingeben [gefunden] Student suchen Student ausgeben [nicht gefunden] nicht gefunden ausgeben Student ausgeben Grundlagen Programmierung Stephan Kleuker 286 Aktion "Student suchen" verfeinert Student ergebnis = null Parameter: Matrikelnummer m Iterator<Student> it= this.studenten.iterator() [!it.hasNext()] [it.hasNext()] Student s = it.next() [s hat übergebene Matrikelnummer m] ergebnis = s Grundlagen Programmierung return ergebnis [!(s hat übergebene Matrikelnummer m)] Stephan Kleuker 287 studentSuchen() – Realisierung (1/2) • Berechnung von Ein- und Ausgabe trennen • Methoden mit gleichen Namen erlaubt, solange Parametertypen unterschiedlich public void studentSuchen(){ EinUndAusgabe io = new EinUndAusgabe(); Integer mat = zahlZwischen("Matrikelnummer:" , 99999, 1000000); Student s = studentSuchen(mat); if(s != null){ io.ausgeben("Daten: "+s+"\n"); } else { io.ausgeben("nicht gefunden\n"); } } Grundlagen Programmierung Stephan Kleuker 288 studentSuchen() – Realisierung (2/2) • einfacher Iterator, da Liste nicht leer sein kann • Hinweis: Implementierung erlaubt leider doppelte Matrikelnummern public Student studentSuchen(Integer mat){ Student ergebnis = null; for(Student s:this.studenten){ if(s.getMatrikelnummer().equals(mat)){ ergebnis = s; Erinnerung: } } return ergebnis; } Grundlagen Programmierung Stephan Kleuker 289 Aufbau einer Testumgebung import import import public org.junit.Assert; org.junit.Before; org.junit.Test; class StudentenverwaltungTest { private private private private private Studentenverwaltung studis; Student s1 =new Student("Uwe","X",1987,"IMI",424241); Student s2 =new Student("Ute","Y",1988,"IMI",424242); Student s3 =new Student("Ulf","X",1989,"MID",424243); Student s4 =new Student("Uta","B",1987,"MID",424244); @Before public void setUp(){ studis = new Studentenverwaltung(); studis.hinzufuegen(s1); studis.hinzufuegen(s2); studis.hinzufuegen(s3); studis.hinzufuegen(s4); } Grundlagen Programmierung Stephan Kleuker 290 studentSuchen() - Tests • Programme mit direkten Nutzerinteraktionen sind (für uns) recht schwer testbar, Rest geht aber problemlos @Test public void testSuchenErfolgreich(){ Assert.assertTrue(studis.studentSuchen(424242).equals(s2)); } @Test public void testSuchenErfolglos1(){ Assert.assertTrue(studis.studentSuchen(434242)==null); } @Test public void testSuchenErfolglos2(){ studis = new Studentenverwaltung(); Assert.assertTrue(studis.studentSuchen(424242)==null); } Grundlagen Programmierung Stephan Kleuker 291 studentBearbeiten() - Aktivitätsdiagramm Matrikelnummer eingeben Matrikelnummer eingeben und Studentendaten ändern Student suchen [gefunden] [nicht gefunden] neuen Vornamen eingeben nicht gefunden ausgeben neuen Nachnamen eingeben Student aktualisieren Grundlagen Programmierung Stephan Kleuker 292 studentBearbeiten() - Realisierung private void studentBearbeiten(){ EinUndAusgabe io = new EinUndAusgabe(); Integer mat = zahlZwischen("Matrikelnummer:" , 99999, 1000000); Student s = studentSuchen(mat); if(s == null){ io.ausgeben("nicht gefunden\n"); return; } String neuVor = nichtLeererText("neuer Vorname (" +"alt:"+s.getVorname()+"): "); String neuNach = nichtLeererText("neuer Nachname (" +"alt:"+s.getNachname()+"): "); s.setVorname(neuVor); s.setNachname(neuNach); io.ausgeben("neu: "+s); } Grundlagen Programmierung Stephan Kleuker 293 studentenZaehlen() – Aktivitätsdiagramm (1/2) Iteration 0 Studiengang eingeben und zugehörige Studentenzahl ausgeben Grundlagen Programmierung Iteration 1 Studiengangnamen eingeben Studierende zählen Anzahl ausgeben Stephan Kleuker 294 studentenZaehlen() – Aktivitätsdiagramm (2/2) Integer ergebnis = 0 Parameter: Studiengang fach Iterator<Student> it= this.studenten.iterator() [!it.hasNext()] [it.hasNext()] Student s = it.next() [s ist im übergebenen Studiengang fach] ergebnis =ergebnis +1 Grundlagen Programmierung return ergebnis [!(s ist im übergebenen Studiengang fach)] Stephan Kleuker 295 studentenZaehlen() – Realisierung private void studentenZaehlen(){ EinUndAusgabe io = new EinUndAusgabe(); String fach = nichtLeererText("Studiengang: "); Integer anzahl = studentenZaehlenIn(fach); io.ausgeben("in "+fach+": "+anzahl+"\n"); } private Integer studentenZaehlenIn(String fach){ Integer ergebnis = 0; for(Student s:this.studenten){ if(s.getStudiengang().equals(fach)){ ergebnis = ergebnis + 1; } } return ergebnis; } Grundlagen Programmierung Stephan Kleuker 296 studentenZaehlen() - Tests @Test public void testZaehlen1(){ Assert.assertTrue(studis.studentenZaehlenIn("IMI").equals(2)); } @Test public void testZaehlen2(){ Assert.assertTrue(studis.studentenZaehlenIn("MID").equals(2)); } @Test public void testZaehlen3(){ Assert.assertTrue(studis.studentenZaehlenIn("FBI").equals(0)); } @Test public void testZaehlen4(){ studis = new Studentenverwaltung(); Assert.assertTrue(studis.studentenZaehlenIn("IMI").equals(0)); } Grundlagen Programmierung Stephan Kleuker 297 Beispiel: Inkrementelle Entwicklung • nächstes Inkrement: Es sollen Studenten gelöscht werden können. Dazu wird eine Matrikelnummer eingegeben und der zugehörige Student gelöscht. • Problem für den Hinterkopf: Wenn man über eine Liste iteriert, kann man dann in der laufenden Bearbeitung Elemente entfernen? Matrikelnummer eingeben und Studenten mit dieser Nummer löschen Grundlagen Programmierung Stephan Kleuker 298 zentraler Löschalgorithmus Parameter: Matrikelnummer m Student ergebnis = null Iterator<Student> it= this.studenten.iterator() [!it.hasNext()] [it.hasNext()] Student tmp = it.next() [tmp hat übergebene Matrikelnummer m] studenten.remove(tmp) Grundlagen Programmierung Stephan Kleuker [!(tmp hat übergebene Matrikelnummer m)] 299 Änderung im Nutzungsdialog in steuern(): ... + " (4) Studierende pro Studiengang\n" + " (5) Student l\u00f6schen: "); // neu ein = io.leseInteger(); switch(ein){ ... case 5: { // neu studentLoeschen(); break; } ... Grundlagen Programmierung Stephan Kleuker 300 Löschen mit klassischem Iterator public void studentLoeschen(){ Integer mat = zahlZwischen("Matrikelnummer" , 99999, 1000000); studentloeschen(mat); } // mit Iterator löschen, recht einfach public void studentloeschen1(Integer mat) { EinUndAusgabe io = new EinUndAusgabe(); Iterator<Student> it = studenten.iterator(); while(it.hasNext()){ Student tmp = it.next(); if(tmp.getMatrikelnummer().equals(mat)){ it.remove(); io.ausgeben("Student gel\u00f6scht\n"); } } } Grundlagen Programmierung Stephan Kleuker 301 Testen @Test public void testLoeschenErfolg(){ studis.studentloeschen(424242); Assert.assertTrue(studis.studentSuchen(424242)==null); Assert.assertTrue(studis.studentenZaehlenIn("IMI").equals(1)); Assert.assertTrue(studis.studentenZaehlenIn("MID").equals(2)); } @Test public void testLoeschenNichtExistent(){ studis.studentloeschen(414242); Assert.assertTrue(studis.studentenZaehlenIn("IMI").equals(2)); Assert.assertTrue(studis.studentenZaehlenIn("MID").equals(2)); } Grundlagen Programmierung Stephan Kleuker 302 Genauere Analyse mit dem Debugger (1/2) Grundlagen Programmierung Stephan Kleuker 303 Genauere Analyse mit dem Debugger (2/2) Grundlagen Programmierung Stephan Kleuker 304 Löschvariante mit direktem Positionszugriff // mit einfacher for-Schleife; kritisch bearbeitete // Liste zu ändern, geht aber public void studentloeschen(Integer mat) { EinUndAusgabe io = new EinUndAusgabe(); for(Integer i=0; i<this.studenten.size(); i++){ Student tmp = this.studenten.get(i); if(tmp.getMatrikelnummer().equals(mat)){ this.studenten.remove(i); io.ausgeben("Student gel\u00f6scht\n"); } } } Grundlagen Programmierung Stephan Kleuker 305 Löschvariante mit Iterator und direktem Zugriff public void studentloeschenFalsch(Integer mat) { EinUndAusgabe io = new EinUndAusgabe(); for(Student tmp:this.studenten){ if(tmp.getMatrikelnummer().equals(mat)){ this.studenten.remove(tmp); io.ausgeben("Student gel\u00f6scht\n"); } } } • Methode funktioniert nicht !!! • es wird eine ConcurrentModificationException geworfen • durch impliziten Iterator wird Listenänderung verweigert Grundlagen Programmierung Stephan Kleuker 306 Löschvariante mit Iterator und späterem Zugriff public void studentloeschen3(Integer mat) { EinUndAusgabe io = new EinUndAusgabe(); Student weg = null; for(Student tmp:this.studenten){ if(tmp.getMatrikelnummer().equals(mat)){ weg = tmp; } } if (weg != null){ this.studenten.remove(weg); io.ausgeben("Student gel\u00f6scht\n"); } } Grundlagen Programmierung Stephan Kleuker 307 Löschen mehrerer Elemente • nächstes Inkrement: es soll der unwahrscheinliche Fall ergänzt werden, dass alle Studierenden eines Studiengangs gelöscht werden sollen • wieder sind alle vorgestellten Varianten möglich • hier: Trennung in zu löschende Elemente heraussuchen und danach alle Elemente entfernen • falls man sich Positionen der zu entfernenden Elemente merkt (ist weitere Variante), muss man beim Löschen beachten, dass sich Positionen durch das Löschen verändern können • Bsp: Lösche an Position 2 und 3, muss als remove(2) und remove(2) realisiert werden (oder: remove(3); remove(2)) Grundlagen Programmierung Stephan Kleuker 308 Studiengang löschen public void studiengangLoeschen(){ EinUndAusgabe io = new EinUndAusgabe(); String fach = nichtLeererText("Studiengang: "); Integer anzahl = studiengangLoeschen(fach); io.ausgeben("Es werden "+anzahl+" Studenten gel\u00f6scht\n"); } public Integer studiengangLoeschen(String fach) { ArrayList<Student> weg = new ArrayList<Student>(); for(Student tmp:this.studenten){ if(tmp.getStudiengang().equals(fach)){ weg.add(tmp); } } for(Student tmp:weg){ this.studenten.remove(tmp); } return weg.size(); Stephan Kleuker }Grundlagen Programmierung 309 Testfälle @Test public void testLoeschenExistierendenStudiengangs(){ Integer anzahl = studis.studiengangLoeschen("MID"); assertTrue(anzahl.equals(2)); assertTrue(studis.studentenZaehlenIn("IMI").equals(2)); assertTrue(studis.studentenZaehlenIn("MID").equals(0)); } @Test public void testLoeschenNichtExistentenStudiengangs(){ Integer anzahl = studis.studiengangLoeschen("ITI"); assertTrue(anzahl.equals(0)); assertTrue(studis.studentenZaehlenIn("IMI").equals(2)); assertTrue(studis.studentenZaehlenIn("MID").equals(2)); } Grundlagen Programmierung Stephan Kleuker 310 Erweiterung: Austauschstudierende – Ansatz 1 • Inkrement: es sollen Austauschstudenten mit verwaltet werden können, für die das Herkunftsland mit aufgenommen werden soll; bei einigen Methoden sollen Studierende unterschieden werden • ist keine rein funktionale Erweiterung, da neue Objektvariable • Ansatz 1: Ergänzung der Klasse Student um Objektvariablen – String land – Boolean istAustausch – machbar, aber Nachteil dass häufig if (this.isAustausch== true) in vielen Methoden steht Grundlagen Programmierung Stephan Kleuker 311 Erweiterung: Austauschstudierende – Ansatz 2 • Ansatz 2: Ergänzung der Klasse Student um Objektvariable – String land – Trick: wenn kein Austauschstudent dann land == null; – machbar, aber Nachteil dass häufig if (this.land == null) in vielen Methoden steht – cool, aber für andere schwer lesbar, da Variable land bzw. Wert null eine besondere Bedeutung zugeordnet wird Grundlagen Programmierung Stephan Kleuker 312 Vererbung Grundlagen Programmierung Stephan Kleuker 313 Erweiterung: Austauschstudierende - Vererbung • objektorientierte Antwort heißt: Erweiterung einer existierenden Klasse (meist Vererbung genannt) • Der Zugriff auf Teile der erweiterten Klasse erfolgt mit super statt this Grundlagen Programmierung Stephan Kleuker 314 Vererbung mit Konstruktornutzung public class Austauschstudent extends Student{ private String land = null; public Austauschstudent(String land, String vorname, String nachname, Integer geburtsjahr, String studiengang, Integer matrikelnummer){ super(vorname, nachname, geburtsjahr, studiengang, matrikelnummer); this.land = land; } // geht auch ohne super, da Student() existiert public Austauschstudent(){ } public String getLand() { return land; } public void setLand(String land) { this.land = land; Grundlagen Programmierung Stephan Kleuker } } 315 Vererbung und Konstruktoren genauer • Jeder Konstruktor in einer erbenden (= erweiternden) Klasse ruft Konstruktor der beerbten (= erweiterten) Klasse auf • dieser Konstruktoraufruf muss erste Zeile in Konstruktoren der erbenden Klasse sein • wird kein Konstruktoraufruf in der erbenden Klasse angegeben, wird automatisch der Default-Konstruktor (= parameterloser Konstruktor) der beerbten Klasse zuerst aufgerufen • hat dann beerbte Klasse keinen Default-Konstruktor wird ein Fehler ausgegeben • Grundregel der sauberen Programmierung: In Konstruktoren von erbenden Klassen wird als erstes immer ein Konstruktor der Oberklasse aufgerufen; also: public Austauschstudent(){ super(); } Grundlagen Programmierung Stephan Kleuker 316 Vererbung, was geht nicht Grundlagen Programmierung Stephan Kleuker 317 Vererbung – Nutzung von Gemeinsamkeiten • Grundsätzlich kann ein Objekt der erbenden Klasse an jeder Stelle genutzt werden, an der auch ein Objekt der beerbten Klasse stehen kann • damit sind alle public-Methoden aus der beerbten Klasse in der erbenden Klasse nutzbar! • Zuweisungen an Variablen vom Typ der beerbten Klasse sind erlaubt Student s = new Austauschstudent(); Austauschstudent aus = new Student(); // geht nicht, nie !!! • Sammlungen mit Objekten des Typs der beerbten Klasse können Objekte der erbenden Klasse aufnehmen ArrayList<Student> studis = new ArrayList<Student>(); studis.add(new Austauschstudent()); Grundlagen Programmierung Stephan Kleuker 318 Integration in die Studentenverwaltung (1/2) • in steuern(): + " (7) Austauschstudent einf\u00fcgen: "); // neu ein = io.leseInteger(); switch(ein){ ... case 7: { // neu austauschstudentenEingeben(); break; ... } public void austauschstudentenEingeben(){ Austauschstudent ergebnis = new Austauschstudent(); ergebnis.setVorname(nichtLeererText("Vorname")); ergebnis.setNachname(nichtLeererText("Nachname")); ergebnis.setStudiengang(nichtLeererText("Studiengang")); ergebnis.setGeburtsjahr( zahlZwischen("Geburtsjahr",1900,2000)); ergebnis.setMatrikelnummer( zahlZwischen("Matrikelnummer",99999,1000000)); ergebnis.setLand(nichtLeererText("Land")); this.studenten.add(ergebnis); } Grundlagen Programmierung Stephan Kleuker 319 Integration in die Studentenverwaltung (2/2) • leicht modifiziertes Test-Setup mit drei IMI-Studis private Studentenverwaltung studis; private Student s1 =new Student("Uwe","X",1987,"IMI",424241); private Student s2 =new Austauschstudent("CHN","Xi","Yu", 1988,"IMI",424242); private Student s3 =new Austauschstudent("USA","Mo","Jo", 1989,"MID",424243); private Student s4 =new Student("Uta","B",1987,"MID",424244); • Alle Tests laufen weiterhin! Grundlagen Programmierung Stephan Kleuker 320 Überschreiben Grundlagen Programmierung Stephan Kleuker 321 Vererbung: Möglichkeiten zur Individualisierung • Ab und zu möchte man, dass sich eine Methode bei einer erbenden Klasse etwas anders als bei der beerbten Klasse verhält; hierzu können Methoden überschrieben werden • aktuell wird für Austauschstudent und Student die gleiche toString-Methode genutzt public void austauschstudentAusgeben(){ Austauschstudent aus =new Austauschstudent("USA","Mo" ,"Jo",1989,"MID",424243); EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben("Ausgabe: "+aus); } Ausgabe: Mo Jo (424243):MID • Nun soll bei Austauschstudenten nur Vorname, Name, Land und Studiengang ausgegeben werden Grundlagen Programmierung Stephan Kleuker 322 Methoden überschreiben @Override public String toString(){ return super.getVorname() + " " + super.getNachname() +" (" + this.land+"): " + super.getStudiengang(); } Ausgabe: Mo Jo (USA):MID • Zugriff auf Methoden der beerbten Klasse über super.<Methode> Grundlagen Programmierung Stephan Kleuker 323 zu @Override • @Override ist eine Annotation (Erinnerung an @Test) • Annotationen werden zur deklarativen Programmierung genutzt; d. h. es wird beschrieben, was gemacht werden soll, aber nicht wie • hier: Der Compilier soll prüfen, dass eine Methode der beerbten Klasse überschrieben wird • Annotationen können beliebige Sprachkonstrukte (z. B. Klassen, Objektvariablen, Methoden, lokale Variablen) annotieren • Annotationen spielen in C# und Java bei fortgeschrittenen Programmierkonzepten häufig eine zentrale Rolle; wird in der Anfängerveranstaltung aber nicht betrachtet Grundlagen Programmierung Stephan Kleuker 324 Direkter Zugriff auf geerbte Objektvariablen • generell können mit get- und set-Methoden die Objektvariablen der beerbten Klasse bearbeitet werden • einfacher wird es, wenn man Sichtbarkeit protected nutzt public class Student{ protected String vorname ="Eva"; protected String nachname ="Mustermann"; protected Integer geburtsjahr = 1990; protected String studiengang = "IMI"; protected Integer matrikelnummer = 232323; • in Austauschstudent: @Override public String toString(){ return super.vorname + " " + super.nachname + " (" + this.land + "): " + super.studiengang; } • direkter Zugriff auf Objektvariablen der beerbten Klasse durch super.<Objektvariable> Grundlagen Programmierung Stephan Kleuker 325 Welche Ausgabemethode wird genutzt? • Methode in Klasse Analyse public void wasWirdGezeigt(){ ArrayList<Student> list = new ArrayList<Student>(); list.add(new Austauschstudent("USA", "Mo","Jo",1989,"MID",424243)); list.add(new Student("Mo","Jo",1989,"MID",424243)); EinUndAusgabe io = new EinUndAusgabe(); for(Student s:list){ io.ausgeben(s+"\n"); Mo Jo (USA): MID } Mo Jo (424243):MID } Grundlagen Programmierung Stephan Kleuker 326 dynamische Polymorphie Grundlagen Programmierung Stephan Kleuker 327 Grundregel der Vererbung • Jedes Objekt einer erbenden Klasse kann jedes Objekt ihrer Beerbten ersetzen, es folgt: – überschreibende Methoden dürfen in der abgeleiteten Klasse nur schwächere Vorbedingungen haben – überschreibende Methoden dürfen in der abgeleiteten Klasse nur stärkere Nachbedingungen haben Beispiele: • man kann statt eines Student-Objektes auch ein Austauschstudent-Objekt nutzen, da sich das Verhalten durch überschriebene Methoden nur im Detail verändert • Methode zum Speichern von Daten wird nicht mit einer Methode zum Formatieren der Festplatte überschrieben Dr. Stephan Kleuker Stephan Kleuker 328 Idee der dynamischen Polymorphie • Beim Aufruf einer Methode wird zur Laufzeit geprüft, zu welcher Klasse (genauer am weitesten abgeleiteten Klasse) das Objekt gehört • dann wird in dieser Klasse nach der Methode gesucht und wenn gefunden, ausgeführt • danach wird schrittweise jede beerbte Klasse nach und nach geprüft, ob sie die Methode realisiert und dann ausgeführt Student studi = x; studi.mach(); bedeutet nicht unbedingt, dass Methode mach() in Studi ausgeführt wird, wenn x zu einer direkt oder indirekt beerbten Klasse von Studi gehört • Hinweis: nicht einfach, macht aber die Entwicklung oft sehr viel einfacher und eleganter; zentrales Konzept Grundlagen Programmierung Stephan Kleuker 329 Beispiel zur dynamischen Polymorphie (1/6) public class A{ public void meth1(){ EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben("A1\n"); } public void meth2(){ meth3(); } public void meth3(){ EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben("A3\n"); } } Grundlagen Programmierung Stephan Kleuker 330 Beispiel zur dynamischen Polymorphie (2/6) public class B extends A{ @Override public void meth1(){ EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben("B1\n"); } @Override public void meth3(){ EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben("B3\n"); } } Grundlagen Programmierung Stephan Kleuker 331 Beispiel zur dynamischen Polymorphie (3/6) public class C extends B{ @Override public void meth1(){ EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben("C1\n"); } @Override public void meth2(){ super.meth2(); } @Override public void meth3(){ EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben("C3\n"); } } Grundlagen Programmierung Stephan Kleuker 332 Beispiel zur dynamischen Polymorphie (4/6) public class Analyse{ meth1: aus(A1) meth2: meth3() meth3: aus(A3) public void analyseA(){ A a = new A(); a.meth1(); a.meth2(); } meth1: aus(B1) meth3: aus(B3) A1 A3 meth1: aus(C1) meth2: super.meth2() meth3: aus(C3) Grundlagen Programmierung Stephan Kleuker 333 Beispiel zur dynamischen Polymorphie (5/6) meth1: aus(A1) meth2: meth3() meth3: aus(A3) public void analyseB(){ A b = new B(); b.meth1(); b.meth2(); } meth1: aus(B1) meth3: aus(B3) B1 B3 meth1: aus(C1) meth2: super.meth2() meth3: aus(C3) Grundlagen Programmierung Stephan Kleuker 334 Beispiel zur dynamischen Polymorphie (6/6) meth1: aus(A1) meth2: meth3() meth3: aus(A3) public void analyseC(){ A c = new C(); c.meth1(); c.meth2(); } meth1: aus(B1) meth3: aus(B3) C1 C3 meth1: aus(C1) meth2: super.meth2() meth3: aus(C3) Grundlagen Programmierung Stephan Kleuker 335 Einfache Erweiterung (1/2) • In erbenden Klassen können Methoden ergänzt werden, die dann von Objekten dieser Klasse (und davon erbenden Klassen) nutzbar sind • in Austauschstudent: public String gruss(){ if(this.land.equals("USA")){ return "Howdy " + super.vorname; } if(this.land.equals("OSF")){ return "Moin " + super.vorname; } return "Hello "+ super.vorname; } Grundlagen Programmierung Stephan Kleuker 336 Einfache Erweiterung (2/2) • Erweiterungen in der beerbten Klasse und für Variablen vom Typ der beerbten Klasse nicht nutzbar • in Analyse: public void gruessen(){ Austauschstudent aus = new Austauschstudent("USA", "Mo","Jo",1989,"MID",424243); EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben(aus.gruss()); Howdy Mo } Grundlagen Programmierung Stephan Kleuker 337 casten Grundlagen Programmierung Stephan Kleuker 338 Neues Inkrement • Die Verwaltung soll um eine Möglichkeit ergänzt werden, die Anzahl der Austauschstudenten zu berechnen • Problem: ArrayList<Student> kennt nur Objekte der Klasse Student und man kann mit bisherigem Wissen nicht feststellen, ob Objekt auch zu einer erbenden Klasse gehört • Gibt allerdings schmuddeligen (wieso? später!) Ansatz – Boolescher Operator instanceof, genauer <Objekt> instanceof <Klassenname> – gibt true, wenn Objekt auch zu dieser Klasse gehört; Objekt gehört zu der Klasse, von der es instanziiert wurde (new <Klassenname>) und zu allen Klassen, die diese Klasse beerbt hat (direkt oder indirekt) Grundlagen Programmierung Stephan Kleuker 339 Beispiel für instanceof public void instanceofSpielerei(){ Austauschstudent aus = new Austauschstudent("USA", "Mo","Jo",1989,"MID",424243); Student s0 = new Austauschstudent("USA", "Mo","Jo",1989,"MID",424243); Student s1 = new Student("Mo","Jo",1989,"MID",424243); EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben("aus: "+(aus instanceof Austauschstudent)+"\n"); io.ausgeben("aus: "+(aus instanceof Student)+"\n"); io.ausgeben("s0: "+(s0 instanceof Austauschstudent)+"\n"); io.ausgeben("s0: "+(s0 instanceof Student)+"\n"); io.ausgeben("s1: "+(s1 instanceof Austauschstudent)+"\n"); io.ausgeben("s1: "+(s1 instanceof Student)+"\n"); } aus: true aus: true s0: true s0: true s1: false s1: true Grundlagen Programmierung Stephan Kleuker 340 Casten • Wenn man weiß, dass ein Objekt eines Typs auch zu einer anderen Klasse gehört, kann man das Objekt in ein Objekt der anderen Klasse umwandeln (casten): (<NameDerErbendenKlasse) Objektvariable • Sollte man in eine falsche Klasse casten, erhält man einen Fehler (Exception) • Auch casten sollte man mit Bedacht einsetzen Grundlagen Programmierung Stephan Kleuker 341 Beispiel für Casten public void castenSpielerei(){ Student s0 = new Austauschstudent("USA", "Mo","Jo",1989,"MID",424243); Austauschstudent aus = (Austauschstudent) s0; EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben(aus.gruss()); s0 = new Student("Mo","Jo",1989,"MID",424243); aus = (Austauschstudent) s0; io.ausgeben(aus.gruss()); } Grundlagen Programmierung Stephan Kleuker 342 instanceof und casten nur im "Notfall" • Casten sowie instanceof sollte man nur mit allergrößter Vorsicht nutzen, da – der Programmcode viele if-Konstrukte enthält – bei Erweiterungen mit weiteren erbenden Klassen weitere Alternativen hinzukommen – wenn man instanceof und casten häufig benötigt, hat man beim Programmdesign einen großen Fehler gemacht • z. B. eine ArrayList<Student> und man immer wieder Alternativen für Austauschstudenten benötigt • Grundsätzlich deutet Nutzung auf schlechte Programmierung • Ausnahmen selten sinnvoll, nur bei Frameworks, bei denen Software für andere Software erstellt wird Grundlagen Programmierung Stephan Kleuker 343 Anzahl Austauschstudenten: schwächere Lösung public void anzahlAustauschstudentenAusgeben(){ EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben("Es gibt "+ anzahlAustauschstudenten() + " Austauschstudenten\n"); } public Integer anzahlAustauschstudentenNichtSoToll(){ Integer ergebnis = 0; for(Student s: this.studenten){ if(s instanceof Austauschstudent){ ergebnis = ergebnis +1; } } return ergebnis; } Grundlagen Programmierung Stephan Kleuker 344 Anzahl Austauschstudenten: bessere Alternative • Nutzung von dynamischer Polymorphie • Einführung in Student einer neuen Methode zaehlenAlsAustauschstudent • Methode gibt den Wert 0 für Student zurück • Methode wird in Austauschstudent überschrieben und gibt Wert 1 zurück • Werte aller Studierenden werden summiert • Hinweis: Ok, ist kleiner Trick, aber die Behandlung von Alternativen wurde in Polymorphie verschoben • Ansatz für weitere erbende Klassen von Student ohne CodeÄnderung in der Studentenverwaltung Grundlagen Programmierung Stephan Kleuker 345 Realisierung (1/2) • in Student: public Integer zaehlenAlsAustauschstudent(){ return 0; } • in Austauschstudent: @Override public Integer zaehlenAlsAustauschstudent(){ return 1; } Grundlagen Programmierung Stephan Kleuker 346 Realisierung (2/2) public Integer anzahlAustauschstudenten(){ Integer ergebnis = 0; for(Student s:this.studenten){ ergebnis = ergebnis + s.zaehlenAlsAustauschstudent(); } return ergebnis; } • zugehöriger Test: @Test public void testAustauschstudentenZaehlen(){ Integer anzahl = studis.anzahlAustauschstudenten(); Assert.assertTrue(anzahl.equals(2)); } Grundlagen Programmierung Stephan Kleuker 347 Überblick: Wiederverwendung durch OO • Im Laufe der Entwicklung der für eine Software benötigten Klassen kann es auffallen, dass man einige „ähnliche“ Klassen findet • Ähnlich bedeutet eine große Gemeinsamkeit bei gewissen Objektvariablen und Methoden und größere Unterschiede bei anderen Objektvariablen und Methoden • Die Grundidee bei der Objektorientierung ist es, die großen Gemeinsamkeiten in eine Klasse zu packen und die Spezialfälle in andere Klassen, die die gemeinsamen Eigenschaften der anderen Klasse erben können Dr. Stephan Kleuker Stephan Kleuker 348 abstrakte Klasse Grundlagen Programmierung Stephan Kleuker 349 Fallstudie: Arenenkampf • In einer Arena sollen verschiedene Kämpfer gegeneinander antreten können. Dabei soll der Nutzer sich einen Kämpfer wählen und gegen eine Gruppe anderer Kämpfer antreten. Jeder Kämpfer wird durch seinen Namen, sein Geschick und seine Stärke charakterisiert. Ein Kampf findet Eins-gegenEins statt, wobei die Kämpfer sich gegenseitig attackieren und versuchen die Attacke abzuwehren. Jeder Kämpfer hat eine Anzahl von Gesundheitspunkten, die, nachdem er attackiert wurde, sinken kann. Ist die Punktzahl nicht mehr positiv, scheidet der Kämpfer aus. Der Nutzer kämpft nacheinander gegen die Gruppe anderer Kämpfer. Ziel ist es, alle anderen Kämpfer zu besiegen. Grundlagen Programmierung Stephan Kleuker 350 Kämpfer (1/3) - Modellierung public class Kaempfer { protected String name; protected Integer gesundheit; protected Integer staerke; protected Integer geschick; public Kaempfer(){ } public Kaempfer(String name, Integer gesundheit, Integer staerke, Integer geschick) { this.name = name; this.gesundheit = gesundheit; this.staerke = staerke; this.geschick = geschick; } Grundlagen Programmierung Stephan Kleuker 351 Kämpfer (2/3) – übliche get- und set-Methoden public Integer getGesundheit() { return gesundheit;} public void setGesundheit(Integer gesundheit) { this.gesundheit = gesundheit; } public Integer getStaerke() { return staerke;} public void setStaerke(Integer staerke) { this.staerke = staerke; } public Integer getGeschick() { return geschick; } public void setGeschick(Integer geschick) { this.geschick = geschick; } public String getName() { return name;} public void setName(String name) { this.name = name; } Grundlagen Programmierung Stephan Kleuker 352 Kämpfer (3/3) – Kernfunktionalität • Angreifen: zufälliger Schadenswert wird berechnet public Integer angreifen(){ EinUndAusgabe io = new EinUndAusgabe(); return io.zufall(2, this.staerke) * io.zufall(2, this.geschick); } • Angegriffen werden: Angriff erfolgt mit einem Schadenswert, der noch abgemildert werden kann public Integer einstecken(Integer haue){ EinUndAusgabe io = new EinUndAusgabe(); Integer ergebnis = io.zufall(0,haue); this.gesundheit = this.gesundheit - ergebnis; return ergebnis; } } Grundlagen Programmierung Stephan Kleuker 353 Arena - Konzept Nutzer erstellt seinen Kämpfer (Held) Gegner werden erstellt [Kämpfer lebt und Gegner vorhanden] [!(Kämpfer lebt und Gegner vorhanden)] Herausforderer auswählen Held kämpft gegen Herausforderer bis Sieger feststeht, evtl. Herausforderer löschen Grundlagen Programmierung Stephan Kleuker [Kämpfer lebt] gewonnen [!(Kämpfer lebt)] verloren 354 Arena – Realisierung (1/4) import java.util.ArrayList; public class Arena { private ArrayList<Kaempfer> gegner = new ArrayList<Kaempfer>(); private Kaempfer held; Nutzer erstellt seinen Kämpfer public Kaempfer kaempferErstellen(){ Integer punkte = 30; EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben("Teilen Sie "+punkte + " in Kraft und Geschick auf:\n" + "Kraft (1-"+(punkte-1)+"): "); Integer kraft = 0; while (kraft<1 || kraft>punkte-1){ kraft = io.leseInteger(); } return new Kaempfer("Isch",500,kraft,30-kraft); } Grundlagen Programmierung Stephan Kleuker 355 Arena – Realisierung (2/4) Gegner werden erstellt public void gegnerErstellen() { for (Integer i = 1; i <= 4; i = i + 1) { gegner.add(new Kaempfer("Andy"+i, i * 100, i*3, i*3)); } } public Kaempfer kaempfen(Kaempfer k1, Kaempfer k2) { while (k1.getGesundheit() > 0 && k2.getGesundheit() > 0) { ersterHautZweiter(k1, k2); if (k2.getGesundheit() > 0) { Kämpfer1 kämpft ersterHautZweiter(k2, k1); gegen Kämpfer2 bis } Sieger feststeht } if (k1.getGesundheit() >= 0) return k1; return k2; } Grundlagen Programmierung Stephan Kleuker 356 Arena – Realisierung (3/4) private void ersterHautZweiter(Kaempfer k1, Kaempfer k2) { EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben(k1.getName() + " haut zu.\n"); Integer schaden = k2.einstecken(k1.angreifen()); io.ausgeben(k2.getName() + " verliert " + schaden + " Punkte.\n"); io.ausgeben(k1.getName() + " hat " + k1.getGesundheit() + " Punkte, " + k2.getName() + " hat " + k2.getGesundheit() + " Punkte.\n*****\n"); } Grundlagen Programmierung Stephan Kleuker 357 Arena – Realisierung (4/4) public void kampfOrganisieren() { EinUndAusgabe io = new EinUndAusgabe(); this.held = kaempferErstellen(); gegnerErstellen(); while (held.getGesundheit() > 0 && gegner.size() > 0) { Kaempfer herausforderer = gegner.get(0); Kaempfer sieger = kaempfen(held, herausforderer); if (held.equals(sieger)) { gegner.remove(0); } } if (held.getGesundheit() > 0) { io.ausgeben("Sie haben glorreich gewonnen.\n"); } else { io.ausgeben("Sie haben schmachvoll verloren.\n"); } } Grundlagen Programmierung Stephan Kleuker 358 Ausschnitt aus Kampfprotokoll Teilen Sie 30 in Kraft und Geschick auf: Kraft (1-29): 13 Isch haut zu. Andy1 verliert 154 Punkte. Isch hat 500 Punkte, Andy1 hat -54 Punkte. ***** Isch haut zu. Andy2 verliert 25 Punkte. Isch hat 500 Punkte, Andy2 hat 175 Punkte. ***** ... ***** Andy4 haut zu. Isch verliert 21 Punkte. Andy4 hat 19 Punkte, Isch hat -12 Punkte. ***** Sie haben schmachvoll verloren. Grundlagen Programmierung Stephan Kleuker 359 Inkrement: neue Kämpferarten • Erweiterung: Es sollen unterschiedliche Kämpfer mit verschiedenen Angriffs- und Abwehrverhalten spezifiziert werden können. • Ansatz 1 (machbar): neue Kaempfer-Klassen erben von Kaempfer und überschreiben angreifen() und einstecken() • logisches Problem: Die aktuell vorhandenen Implementierungen des beiden Methoden stehen auch nur für eine Art von Kämpfer • Lösung: Abstrakte Klasse Grundlagen Programmierung Stephan Kleuker 360 Idee: Abstrakte Klasse • abstrakte Klassen sind dazu konzipiert, dass von ihnen geerbt werden muss • abstrakte Klassen enthalten Teilimplementierungen, die vererbt werden sollen (trotzdem überschreibbar) • Methoden, die eine erbende Klasse realisieren muss (damit wird dynamische Polymorphie möglich) werden als abstract markiert und haben keine Implementierung • Klasse selbst wird auch als abstract markiert • von abstrakter Klasse erbende Klasse realisiert entweder alle abstrakten Methoden der Oberklasse oder ist selber abstrakt • von abstrakten Klassen können keine Objekte erzeugt werden • abstrakte Klasse aber als Typ für Variablen verwendbar Grundlagen Programmierung Stephan Kleuker 361 Abstrakter Kämpfer public abstract class Kaempfer { protected String name; protected Integer gesundheit; protected Integer staerke; protected Integer geschick; public Kaempfer(){} public Kaempfer(String name, Integer gesundheit, Integer staerke, Integer geschick) { this.name = name; this.gesundheit = gesundheit; this.staerke = staerke; this.geschick = geschick; } public abstract Integer angreifen(); public abstract Integer einstecken(Integer haue); } // folgen normale get- und set-Implementierungen Grundlagen Programmierung Stephan Kleuker 362 Beispielrealisierung 1 – von vorher übernommen public class Simplo extends Kaempfer { public Simplo() {} public Simplo(String name, Integer gesundheit, Integer staerke, Integer geschick) { super(name, gesundheit, staerke, geschick); } @Override public Integer angreifen(){ EinUndAusgabe io = new EinUndAusgabe(); return io.zufall(2, super.staerke) * io.zufall(2, super.geschick); } @Override public Integer einstecken(Integer haue){ EinUndAusgabe io = new EinUndAusgabe(); Integer ergebnis = io.zufall(0, haue); super.gesundheit = super.gesundheit - ergebnis; return ergebnis; } Grundlagen Programmierung Stephan Kleuker 363 Beispielrealisierung 2 – schwach, kann einstecken public class Nerdo extends Kaempfer { public Nerdo() {} public Nerdo(String name, Integer gesundheit, Integer staerke, Integer geschick) { super(name, gesundheit, staerke, geschick); } @Override public Integer angreifen(){ EinUndAusgabe io = new EinUndAusgabe(); return io.zufall(2, super.staerke) * io.zufall(2, super.geschick)/3; } @Override public Integer einstecken(Integer haue){ EinUndAusgabe io = new EinUndAusgabe(); Integer ergebnis = io.zufall(0,haue/3); super.gesundheit = super.gesundheit - ergebnis; return ergebnis; } Grundlagen Programmierung Stephan Kleuker 364 Beispielrealisierung 3 – stark, steckt nicht gut ein public class Brutalo extends Kaempfer { public Brutalo() {} public Brutalo(String name, Integer gesundheit, Integer staerke, Integer geschick) { super(name, gesundheit, staerke, geschick); } @Override public Integer angreifen(){ EinUndAusgabe io = new EinUndAusgabe(); return super.staerke * io.zufall(3, super.geschick); } @Override public Integer einstecken(Integer haue){ EinUndAusgabe io = new EinUndAusgabe(); Integer ergebnis = io.zufall(haue/2,haue); super.gesundheit = super.gesundheit - ergebnis; return ergebnis; } }Grundlagen Programmierung Stephan Kleuker 365 Notwendige Änderungen in Arena • Erinnerung: new nicht für abstrakte Klassen erlaubt return new Simplo("Isch",500,kraft,30-kraft); bleibt: ArrayList<Kaempfer> gegner, aber: public void gegnerErstellen(){ gegner.add(new Nerdo("Ulf",100,3,3)); gegner.add(new Simplo("Uta",200,6,6)); gegner.add(new Simplo("Urs",300,9,9)); gegner.add(new Brutalo("Ute",400,12,12)); } Grundlagen Programmierung Stephan Kleuker 366 Interface Grundlagen Programmierung Stephan Kleuker 367 Völlig Abstrakte Klassen • wichtig bei Nutzung der dynamischen Polymorphie sind die "gemeinsame Herkunft" (d. h. beerbte Klasse) und die in allen Klassen nutzbaren Methoden • nächster Schritt ist die vollständig abstrakte Klasse ohne Objektvariablen und mit nur abstrakten Methoden • dieses wird in Java auch Interface genannt • ein Interface I wird durch implements I (statt extends I) realisiert • Interfaces können andere Interfaces beerben (extends) Grundlagen Programmierung Stephan Kleuker 368 Beispiel: Interface public interface KaempferInterface{ public Integer angreifen(); public Integer einstecken(Integer haue); public Integer getGesundheit(); public String getName(); } Grundlagen Programmierung Stephan Kleuker 369 Beispiel: Realisierung eines Interface (1/2) public class Hulko implements KaempferInterface { private String name; private Integer gesundheit; public Hulko(String name, Integer gesundheit) { this.name = name; this.gesundheit = gesundheit; } @Override public Integer angreifen() { return 100; } Grundlagen Programmierung Stephan Kleuker 370 Beispiel: Realisierung eines Interface (2/2) @Override public Integer einstecken(Integer haue) { this.gesundheit = this.gesundheit - haue/10; return haue/10; } @Override public Integer getGesundheit() { return this.gesundheit; } @Override public String getName() { return this.name; } } • zur Nutzung in Arena Kaempfer durch KaempferInterface ersetzen; oder auch Kaempfer implements Interface Grundlagen Programmierung Stephan Kleuker 371 Design by Contract • Interfaces zentrale Idee zur Realisierung von Software in Teams • Ansatz: Man überlegt sich Klassen und definiert Interfaces für diese Klassen • Vertrag zwischen Nutzer des Interfaces und Realisierer • Nutzer weiß, dass er eine Klasse bekommt, die das Interface realisiert • Entwickler verpflichtet sich, das Interface zu realisieren; wie es gemacht wird, geht Nutzer nichts an • zum Testen kann Nutzer des Interfaces minimale Implementierung des Interfaces selbst vornehmen • grundsätzlich gilt: es reichen nicht nur Methodennamen; Vor- und Nachbedingungen zu definieren Grundlagen Programmierung Stephan Kleuker 372 Vervollständigtes Interface als Vertrag public interface KaempferInterface{ /** Die Methode gibt einen Angriffswert des Objekts * zurück, der abhängig von der momentanen Stärke ist. * @return ein nicht-negativer Wert kleiner-gleich 250 */ public Integer angreifen(); /** Methode berechnet den Effekt eines erhaltenen * Angriffs der Stärke haue. Es wird erwartet, dass der * Gesundheitswert negativ beeinflusst wird. * @param haue nicht negativer Wert des empfangenen Schlags * @return Stärke der Auswirkung auf das eigene Objekt, * Wert liegt zwischen 0 und haue */ public Integer einstecken(Integer haue); /** Gibt aktuellen Gesundheitswert zurück. * @return aktueller Gesundheitswert */ public Integer getGesundheit(); /** Gibt Namen des Kämpfers zurück. * @return Name des Kämpfers */ public String getName(); } Grundlagen Programmierung Stephan Kleuker 373 Kommentare Grundlagen Programmierung Stephan Kleuker 374 Einschub Dokumentation (1/5) • vorherige Dokumentationsform nicht zufällig • erlaubt Programmdokumentation aus dem Quellcode zu generieren • Generierung erfolgt durch Java-Hilfsprogramm javadoc • javadoc hat viele Parameter mit denen z. B. das Layout oder der Detaillierungsgrad der Information (sollen z. B. Objektvariablen in der Dokumentation sichtbar sein?) eingestellt werden kann • generierte Dokumentation besteht typischerweise aus einem Übersichtsteil und der Detaildokumentation • hier wird nur Grundprinzip erklärt • Ähnliches Konzept für weitere Sprachen: Doxygen http://www.stack.nl/~dimitri/doxygen/ Grundlagen Programmierung Stephan Kleuker 375 Einschub Dokumentation (2/5) Grundlagen Programmierung Stephan Kleuker 376 Einschub Dokumentation (3/5) Grundlagen Programmierung Stephan Kleuker 377 Einschub Dokumentation (4/5) • Grundsätzlich ist jedes Sprachelement (Klasse, Objektvariable, Methode) vor ihrer Deklaration zu dokumentieren • Javadoc-Kommentar beginnt mit /** • Text bis zum ersten Punkt wird in die Zusammenfassung aufgenommen, Rest steht bei Details • Jeder Übergabeparameter ist mit @param zu dokumentieren • Gibt es Methodenrückgabe, ist diese mit @return zu dokumentieren • In der Dokumentation können HTML4-Tags genutzt werden Grundlagen Programmierung Stephan Kleuker 378 Einschub Dokumentation (5/5) • sorgfältige Dokumentation ist zentrale Aufgabe des Entwicklers • man schreibt so, dass ein anderer Entwickler das Programm nutzen, aber auch weiterentwickeln kann • Textfragmente können durchaus redundante Informationen enthalten, da sie oft nicht zusammenhängend gelesen werden • wann entsteht Dokumentation (keine klare Antwort): – nach der Programmerstellung (keine aufwändige Aktualisierung; nicht mehr alle Details im Kopf) – nach Methodenerstellung (alle Details der Erstellung noch bekannt; muss bei Änderungen angepasst werden) – vor der Methodenerstellung (man kann sich so eine Aufgabenstellung definieren; Änderungen sind aufwändig) Grundlagen Programmierung Stephan Kleuker 379 Mehrfachvererbung Grundlagen Programmierung Stephan Kleuker 380 Mehrfachvererbung • einige objektorientierte Sprachen unterstützen Mehrfachvererbung • Problem: wenn Methode mit gleicher Signatur von verschiedenen Klassen geerbt (Notlösung: man muss Klasse angeben, deren Methode genutzt werden soll) • Auflösung dynamischer Polymorphie machbar, aber aufwändig Grundlagen Programmierung Stephan Kleuker Kaempfer Guru Lasko 381 Mehrfachvererbung mit Interfaces • Java erlaubt es, dass eine Klasse mehrere Interfaces realisiert public interface GuruInterface { public String weisheit(Integer nr); } KaempferInterface GuruInterface Lasko Grundlagen Programmierung Stephan Kleuker 382 Beispiel für Realisierung mehrerer Interfaces public class Lasko implements KaempferInterface, GuruInterface { @Override public String weisheit(Integer nr) { if(nr.equals(42)){ return "Das ist die Antwort"; } return "Suche die Frage"; } @Override public Integer angreifen() {return 0;} @Override public Integer einstecken(Integer haue) {return 0;} @Override public Integer getGesundheit() {return 1;} } @Override public String getName() {return "Lasko";} Grundlagen Programmierung Stephan Kleuker 383 Anmerkungen zur Vererbung • Vererbung ist ein wichtiger Ansatz, damit ähnlicher Code nicht doppelt geschrieben werden muss • Vererbung spielt beim Aufbau von Programmbibliotheken eine wichtige Rolle (Übergang vom generellen Konzept bis zur Speziallösung) • Aber, Vererbung ist kein „Muss“, sie ergibt sich in der Programmentwicklung als sinnvolle Vereinfachung • OO-Anfänger suchen oft krampfhaft nach Möglichkeiten zur Nutzung von Vererbung, das Ergebnis sind häufig schlecht strukturierte Programme Dr. Stephan Kleuker Stephan Kleuker 384 Doch Mehrfachvererbung? • Bei der Ausführung von Methoden fällt frühzeitig auf, dass jede Klasse eine Vererbung nutzt, erbende Klassen sogar zwei Grundlagen Programmierung Stephan Kleuker 385 Klasse Object Grundlagen Programmierung Stephan Kleuker 386 Klasse Object • In Java erben alle Klassen automatisch von der Klasse Object, dies ist nicht änderbar • Ansatz erlaubt es, über dynamische Polymorphie Gemeinsamkeiten von Objekten zu nutzen, bekannt: – toString(): String-Repräsentation des Objekt – equals(): Überprüfung auf Gleichheit • weiterer Ansatz: ArrayList<Object> kann beliebige Objekte aufnehmen (Rückumwandlung nur mit casten) • Zentrale Idee ist, diese Methoden (wenn benötigt) zu überschreiben • Überschreiben von toString() und equals() immer, hashCode() fast immer sinnvoll Grundlagen Programmierung Stephan Kleuker 387 jetzt variierte Beispielklasse public class Punkt{ private Integer x; private Integer y; public Punkt(Integer x, Integer y) { this.x = x; this.y = y; } public Integer getX() { return this.x; } public Integer getY() { return this.y; } public void setX(Integer x) { this.x = x; } } public void setY(Integer y) { this.y = y; } Grundlagen Programmierung Stephan Kleuker 388 Direkte Ausgabe import java.util.ArrayList; public class Analyse{ public void ausgeben(){ EinUndAusgabe io = new EinUndAusgabe(); Punkt p1 = new Punkt(0,0); Punkt p2 = new Punkt(0,0); io.ausgeben("p1: "+p1+"\n"); io.ausgeben("p2: "+p2+"\n"); } } p1: Punkt@42e816 p2: Punkt@190d11 • Hier wurde toString()-Methode der Klasse Object genutzt Grundlagen Programmierung Stephan Kleuker 389 Nutzung von toString • in Punkt: @Override public String toString(){ return "["+this.x+","+this.y+"]"; } • Aufruf von ausgeben() in Analyse liefert p1: [0,0] p2: [0,0] Grundlagen Programmierung Stephan Kleuker 390 Identität und Gleichheit • bisher verboten, Objekte mit == zu prüfen • == prüft auf Identität, handelt es sich um dieselben Objekte? • mit equals soll inhaltliche Gleichheit geprüft werden, also ob es sich um das gleiche Objekt handelt (fast immer das, was man wissen will) • Standardimplementierung von equals() in Object unterscheidet dasselbe und das gleiche Objekt nicht public Boolean equals (Object other){ return this == other; } • Jede Java-Klasse implementiert equals() selbst, ansonsten werden inhaltliche Gleichheit und Identität nicht unterschieden Grundlagen Programmierung Stephan Kleuker 391 Identität bei Strings public void stringgleicheit(){ EinUndAusgabe io = new EinUndAusgabe(); String s1 = "Hallo"; String s2 = "Hall"; s2 = s2 + "o"; io.ausgeben(s1+"=="+s2+": "+(s1==s2)+"\n"); io.ausgeben("s1.equals(s2): "+(s1.equals(s2))+"\n"); } Hallo==Hallo: false s1.equals(s2): true • Anmerkung: Da es in der Java-Spezifikation offen ist, ob es für inhaltlich gleiche Strings unterschiedliche Objekte geben muss, kann "Hallo"=="Hallo" auch in einem Block nach true ausgewertet werden Grundlagen Programmierung Stephan Kleuker 392 Identität bei Punkt-Objekten public void gleichUndIdentisch(){ EinUndAusgabe io = new EinUndAusgabe(); Punkt p1 = new Punkt(0,0); Punkt p2 = new Punkt(0,0); Punkt p3 = p1; io.ausgeben("p1==p2: "+(p1==p2)+"\n"); io.ausgeben("p1==p3: "+(p1==p3)+"\n"); io.ausgeben("p1.equals(p2): "+(p1.equals(p2))+"\n"); io.ausgeben("p1.equals(p3): "+(p1.equals(p3))+"\n"); } p1==p2: false p1==p3: true p1.equals(p2): false p1.equals(p3): true Grundlagen Programmierung Stephan Kleuker 393 Visualisierung von == • Bei der Identitätsprüfung wird geprüft, ob zwei Variablen auf den gleichen Speicherbereich verweisen Punkt p1 = new Punkt(0,0); Punkt p2 = new Punkt(0,0); Punkt p3 = p1; Variablen p1 p2 Speicher x 0 y 0 x 0 y 0 p3 Grundlagen Programmierung Stephan Kleuker 394 remove(Object) für ArrayList public void objektInListeLoeschen(){ EinUndAusgabe io = new EinUndAusgabe(); ArrayList<Punkt> ap = new ArrayList<Punkt>(); Punkt p1 = new Punkt(0,0); Variablen ap.add(p1); p1 ap.add(new Punkt(0,0)); ap.add(p1); 0 io.ausgeben("Liste1 : "+ap+"\n"); ap ap.remove(new Punkt(0,0)); 1 io.ausgeben("Liste2 : "+ap+"\n"); 2 ap.remove(p1); io.ausgeben("Liste3 : "+ap+"\n"); } Liste1 : [[0,0], [0,0], Liste2 : [[0,0], [0,0], Liste3 : [[0,0], [0,0]] Grundlagen Programmierung Stephan Kleuker Speicher x 0 y 0 x 0 y 0 [0,0]] [0,0]] 395 Nutzung von equals() • muss public boolean equals(Object) überschrieben werden • alle möglichen Fälle für Parameter vom Typ Object beachten – kann null sein – kann zu anderer Klasse gehören – muss dann inhaltlich untersucht werden @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof Punkt)) { return false; } Punkt other = (Punkt) obj; if (this.x.equals(other.getX()) && this.y.equals(other.getY())) { return true; } return false; Grundlagen Programmierung Stephan Kleuker } 396 Ausgaben für equals() • für gleichUndIdentisch() p1==p2: false p1==p3: true p1.equals(p2): true p1.equals(p3): true • für objektInListeLoeschen() Liste1 : [[0,0], [0,0], [0,0]] Liste2 : [[0,0], [0,0]] Liste3 : [[0,0]] • Anmerkung: Es gibt Situationen, in denen man nicht alle Objektvariablen vergleichen will/muss (z. B. sollte Matrikelnummer bei Studierenden reichen) Grundlagen Programmierung Stephan Kleuker 397 Erinnerung: Java arbeitet mit Objektreferenzen 1 public void zuweisen(){ EinUndAusgabe io = new EinUndAusgabe(); Punkt p1 = new Punkt(0,0); Punkt p2 = p1; p1: [0,42] p2.setY(42); p2: [0,42] io.ausgeben("p1: "+p1+"\n"); io.ausgeben("p2: "+p2+"\n"); } Speicher Variablen p1 x 0 y 42 p2 Grundlagen Programmierung Stephan Kleuker 398 Erinnerung: Java arbeitet mit Objektreferenzen 2 public void zuweisen1(){ EinUndAusgabe io = new EinUndAusgabe(); ArrayList<Punkt> ap = new ArrayList<Punkt>(); Punkt p1 = new Punkt(0,0); ap.add(p1); ap.add(new Punkt(0,0)); ap.add(p1); io.ausgeben("Liste1: "+ap+"\n"); p1.setX(41); ap.get(0).setY(42); io.ausgeben("Liste2: "+ap+"\n"); io.ausgeben("p1: "+p1+"\n"); } Liste1: [[0,0], [0,0], [0,0]] Liste2: [[41,42], [0,0], [41,42]] p1: [41,42] Grundlagen Programmierung Stephan Kleuker Variablen p1 ap Speicher x 0 y 0 0 x 0 1 y 0 x 41 y 42 0 x 0 1 y 0 2 p1 ap 2 399 wenn keine Referenzen erwünscht • in der Praxis muss fast nur mit Objektreferenzen gearbeitet werden; trotzdem soll es möglich sein, Ausgangsobjekte nicht zu verändern • Wunsch: Erstellung echter Objektkopien ohne gemeinsame Referenzen • Ansatz: Realisierung einer Methode clone() • In Java bereits durch Klasse Object etwas unterstützt, gibt Methode protected Object clone() • protected nicht direkt nutzbar, aber überschreibbar • Klasse soll dann Interface Cloneable realisieren • Beim Kopieren muss der clone()-Befehl an jede Objektvariable weitergegeben werden; diesen wieder weitergeben Grundlagen Programmierung Stephan Kleuker 400 Beispiel: Realisierung von Clone • Anmerkung: für elementare Datentypen und Klassen die Konstanten nutzen (Integer), wird kein clone() benötigt • Beobachtung: Integer hat keine set-Methode für den Wert public class Punkt implements Cloneable{ //.. @Override public Punkt clone(){ return new Punkt(this.x, this.y); } Grundlagen Programmierung Stephan Kleuker 401 Nutzung von clone() public void zuweisen2(){ EinUndAusgabe io = new EinUndAusgabe(); ArrayList<Punkt> ap = new ArrayList<Punkt>(); Punkt p1 = new Punkt(0,0); ap.add(p1.clone()); ap.add(new Punkt(0,0)); ap.add(p1.clone()); io.ausgeben("Liste1: "+ap+"\n"); p1.setX(41); ap.get(0).setY(42); io.ausgeben("Liste2: "+ap+"\n"); io.ausgeben("p1: "+p1+"\n"); } Liste1: [[0,0], [0,0], [0,0]] Liste2: [[0,42], [0,0], [0,0]] p1: [41,0] Grundlagen Programmierung Stephan Kleuker 402 Clone-Variante: Kopierkonstruktor • Wenn häufiger Kopien gewünscht, dann auch als Konstruktor realisierbar, der zu clonendes Objekt erhält public Punkt(Punkt other){ this.x = other.getX(); this.y = other.getY(); } • in Analyse, zuweisen2(): ap.add(new Punkt(p1)); ap.add(new Punkt(0,0)); ap.add(new Punkt(p1)); Liste1: [[0,0], [0,0], [0,0]] Liste2: [[0,42], [0,0], [0,0]] p1: [41,0] Grundlagen Programmierung Stephan Kleuker 403 Beispiel für (fast) vollständige Klasse (1/4) public class Linie implements Cloneable{ private Punkt start; private Punkt ende; public Linie(Punkt start, Punkt ende) { this.start = start; this.ende = ende; } public Punkt getStart() { return start; } public void setStart(Punkt start) { this.start = start; } Grundlagen Programmierung Stephan Kleuker 404 Beispiel für (fast) vollständige Klasse (2/4) public Punkt getEnde() { return ende; } public void setEnde(Punkt ende) { this.ende = ende; } @Override public String toString(){ return "["+this.start+"->"+this.ende+"]"; } @Override public Linie clone(){ return new Linie(this.start.clone(), this.ende.clone()); } Grundlagen Programmierung Stephan Kleuker 405 Beispiel für (fast) vollständige Klasse (3/4) @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof Linie)) { return false; } Linie other = (Linie) obj; if (this.ende == null) { if (other.getEnde() != null) { return false; } } else if (!this.ende.equals(other.getEnde())) { return false; } if (this.start == null) { if (other.getStart() != null) { return false; } } else if (!this.start.equals(other.getStart())) { return false; } return true; Grundlagen Programmierung Stephan Kleuker } } 406 Beispiel für (fast) vollständige Klasse (4/4) public void linienspielerei(){ EinUndAusgabe io = new EinUndAusgabe(); Linie l1 = new Linie(new Punkt(0,0),new Punkt(3,3)); Linie l2 = l1; Linie l3 = l2.clone(); io.ausgeben("l1==l2: "+(l1==l2)+"\n"); io.ausgeben("l1==l3: "+(l1==l3)+"\n"); io.ausgeben("l1.equals(l2): "+(l1.equals(l2))+"\n"); io.ausgeben("l1.equals(l3): "+(l1.equals(l3))+"\n"); l2.setStart(new Punkt(4,4)); l1==l2: true io.ausgeben("l1: "+l1+"\n"); l1==l3: false io.ausgeben("l2: "+l2+"\n"); l1.equals(l2): true io.ausgeben("l3: "+l3+"\n"); l1.equals(l3): true l3.setEnde(l2.getStart()); l1: [[4,4]->[3,3]] l1.getStart().setY(42); l2: [[4,4]->[3,3]] io.ausgeben("l1: "+l1+"\n"); l3: [[0,0]->[3,3]] io.ausgeben("l2: "+l2+"\n"); l1: [[4,42]->[3,3]] io.ausgeben("l3: "+l3+"\n"); l2: [[4,42]->[3,3]] } l3: [[0,0]->[4,42]] Grundlagen Programmierung Stephan Kleuker 407 Analyse mit Speicherbildern (1/2) Linie l1 = new Linie(new Punkt(0,0),new Punkt(3,3)); Linie l2 = l1; Linie l3 = l2.clone(); start l1 ende x 0 y 0 x 3 y 3 x 0 y 0 x 3 y 3 l2 start l3 ende Grundlagen Programmierung Stephan Kleuker 408 Analyse mit Speicherbildern (2/2) l2.setStart(new Punkt(4,4)); l3.setEnde(l2.getStart()); l1.getStart().setY(42); start l1 ende 0 0 x 4 y 42 x 3 y 3 x 0 y 0 x 3 y 3 l2 start l3 ende Grundlagen Programmierung Stephan Kleuker 409 Collection Framework Grundlagen Programmierung Stephan Kleuker 410 Ausschnitt aus Collection Framework (java.util) Grundlagen Programmierung Stephan Kleuker 411 Das Interface Collection (Ausschnitt) Method Summary boolean add(Object o) Ensures that this collection contains the specified element. boolean addAll(Collection c) Adds all of the elements in the specified collection to this collection. void clear() Removes all of the elements from this collection. boolean contains(Object o) Returns true if this collection contains the specified element. boolean isEmpty() Returns true if this collection contains no elements. Iterator iterator() Returns an iterator over the elements in this collection. boolean remove(Object o) Removes a single instance of the specified element from this collection, if it is present. boolean retainAll(Collection c) Retains only the elements in this collection that are contained in the specified collection int size() Returns the number of elements in this collection. Object[] toArray() Returns an array containing all of the elements in this collection. Benutzeroberflächen und Software-Ergonomie Stephan Kleuker 412 Das Interface List (Ausschnitt) Method Summary void add(int index, Object element) Inserts the specified element at the specified position in this list. boolean addAll(int index, Collection c) Inserts all of the elements in the specified collection into this list at the specified position. Object get(int index) Returns the element at the specified position in this list. int indexOf(Object o) Returns the index in this list of the first occurrence of the specified element, or -1 if this list does not contain this element. int lastIndexOf(Object o) Returns the index in this list of the last occurrence of the specified element, or -1 if this list does not contain this element. Object remove(int index) Removes (and returns) the element at the specified position in this list. Object set(int index, Object element) Replaces the element at the specified position in this list with the specified element. Benutzeroberflächen und Software-Ergonomie Stephan Kleuker 413 Warum verschiedene Sammlungen Jede Implementierung besonders geeignet unter bestimmten Rahmenbedingungen sollen Objekte doppelt vorkommen können? [ja] spielt die Reihenfolge eine Rolle? [nein] spielt die Reihenfolge eine Rolle? [ja] [ja] [nein] [nein] Liste ArrayList Vector ... Multimenge Bag (Apache Collections) Grundlagen Programmierung sortierte Menge SortedSet ... Stephan Kleuker Menge HashSet ... 414 Beispielnutzung einer Menge (mit Problem) public void objektInMengeLoeschen(){ EinUndAusgabe io = new EinUndAusgabe(); HashSet<Punkt> ap = new HashSet<Punkt>(); Punkt p1 = new Punkt(0,0); ap.add(p1); ap.add(new Punkt(0,0)); ap.add(p1); io.ausgeben("Menge1: "+ap+"\n"); ap.remove(new Punkt(0,0)); io.ausgeben("Menge2: "+ap+"\n"); Menge1: [[0,0], [0,0]] ap.remove(p1); Menge2: [[0,0], [0,0]] io.ausgeben("Menge3: "+ap+"\n"); Menge3: [[0,0]] } • Problem, da es den Punkt insgesamt nur einmal geben sollte, er aber doppelt ist, nur Hinzufügen eines identischen Objekts wird verhindert Grundlagen Programmierung Stephan Kleuker 415 Idee von hashCode() • equals-Berechnungen können sehr aufwändig sein • ergänze schnell zu berechnende Methode, die für gleiche Elemente immer gleichen Wert liefert und für unterschiedliche Objekte möglichst unterschiedlichen Wert • Gleichheitsprüfung: prüfe, ob schnell berechnete Werte übereinstimmen und nur dann wird equals ausgeführt • ohne HashCode gilt aber: public void zeigeHashCode(){ EinUndAusgabe io = new EinUndAusgabe(); Punkt p1 = new Punkt(0,0); Punkt p2 = new Punkt(0,0); io.ausgeben("p1: "+p1.hashCode()+"\n"); io.ausgeben("p2: "+p2.hashCode()+"\n"); } Grundlagen Programmierung Stephan Kleuker p1: 14576877 p2: 12677476 416 Realisierung mit hashCode() • in Punkt @Override public int hashCode() { return this.x + (this.y*33); } p1: 0 p2: 0 • Methode zeigeHashCode() liefert • Methode objektInMengeLoeschen() liefert Menge1: [[0,0]] Menge2: [] Menge3: [] Grundlagen Programmierung Stephan Kleuker 417 Array Grundlagen Programmierung Stephan Kleuker 418 Idee von Arrays • Nutzung der bisher bekannten Sammlungen kann recht aufwändig sein, da z. B. beim Hinzufügen immer neuer Platz im Speicher gesucht werden soll • Effizienter sind Objekte, die einmal gespeichert werden und ihre Größe nicht mehr ändern • praktisch einsetzbar, wenn man die maximale Anzahl von Objekten kennt, die alle den gleichen Typen haben und deren Anzahl nicht allzu groß ist • Zugehöriger Datentyp wird Array (Feld, Reihung) genannt • besondere Form der Typangabe <Typ>[] <Variablenname> ; oder äquivalent <Typ> <Variablenname> []; • Arrays können wie Sammlungen iteriert werden, Länge allerdings durch <Variablenname>.length bestimmbar Grundlagen Programmierung Stephan Kleuker 419 Varianten der Initialisierung public class Analyse { private String[] wo1 = {"Mo","Di","Mi","Do","Fr","Sa","So"}; private String[] wo2 = new String[7]; //null-Werte private String[] wo3; public void initialisieren(){ //this.wo3 = {"42"} geht nicht this.wo3 = this.wo2; EinUndAusgabe io = new EinUndAusgabe(); for(Integer i=0; i<this.wo1.length; i=i+1){ this.wo2[i]=this.wo1[i]; } for(Integer i=0; i<this.wo3.length; i=i+1){ io.ausgeben(this.wo3[i]); } MoDiMiDoFrSaSo io.ausgeben("\n"); } Grundlagen Programmierung Stephan Kleuker 420 Beispiel: Klasse für einen Lotto-Tipp (1/4) • sinnvoll, Arrays mit eigener Bedeutung in Klassen zu kapseln public class Tipp { private Integer[] werte = new Integer[6]; public Tipp() { EinUndAusgabe io = new EinUndAusgabe(); Integer i = 0; while (i < this.werte.length) { Integer kugel = io.zufall(1, 49); if (kommtNichtVor(kugel, i)) { this.werte[i] = kugel; i = i + 1; } } } Grundlagen Programmierung Stephan Kleuker 421 Beispiel: Klasse für einen Lotto-Tipp (2/4) public Boolean kommtNichtVor(Integer wert, Integer position){ for (Integer i = 0; i < position; i = i + 1) { if (this.werte[i].equals(wert)) { return false; } } return true; } public Integer[] getWerte() { return this.werte; } public void setWerte(Integer[] werte) { this.werte = werte; } Grundlagen Programmierung Stephan Kleuker 422 Beispiel: Klasse für einen Lotto-Tipp (3/4) public Boolean enthaelt(Integer wert) { for (Integer i : this.werte) { if (i.equals(wert)) { return true; } } return false; } @Override public String toString() { String ergebnis = "[ "; for (Integer i : this.werte) { ergebnis = ergebnis + i + " "; } return ergebnis + "]"; } Grundlagen Programmierung Stephan Kleuker 423 Beispiel: Klasse für einen Lotto-Tipp (4/4) • Analyse der Übereinstimmungen von zwei Tipps • Ansatz: Jedes Element des ersten Tipps wird mit jedem Element des zweiten Tipps verglichen (gibt keine Doppelten) public Integer gemeinsam(Tipp tip) { Integer ergebnis = 0; for (Integer i : this.werte) { for (Integer j : tip.getWerte()) { if (i.equals(j)) { ergebnis = ergebnis + 1; } } } return ergebnis; } } Grundlagen Programmierung Stephan Kleuker 424 Testen von Tipp (1/2) public class TippTest { private Tipp t1 = new Tipp(); private Tipp t2 = new Tipp(); @Test public void testUnterschiedlicheWerte(){ for(Integer i=0; i<100; i=i+1){ unterschiedlich(new Tipp()); } } private void unterschiedlich(Tipp tip){ Integer inhalt[] = tip.getWerte(); for(Integer i=0; i<inhalt.length-1; i=i+1){ Assert.assertTrue(inhalt[i]>0 && inhalt[i]<50); for(Integer j=i+1; j<inhalt.length; j=j+1){ Assert.assertTrue(!inhalt[i].equals(inhalt[j])); } } Grundlagen Programmierung Stephan Kleuker } 425 Testen von Tipp (2/2) - Ausschnitt @Test public void testNuller(){ t1.setWerte(new Integer[]{1,2,3,4,5,6}); t2.setWerte(new Integer[]{11,12,13,14,15,16}); Assert.assertTrue(t1.gemeinsam(t2).equals(0)); } @Test public void testFuenfer(){ t1.setWerte(new Integer[]{4,12,43,24,5,36}); t2.setWerte(new Integer[]{31,12,43,4,24,36}); Assert.assertTrue(t1.gemeinsam(t2).equals(5)); } @Test public void testSechser(){ t1.setWerte(new Integer[]{4,12,43,24,36,31}); t2.setWerte(new Integer[]{31,12,43,4,24,36}); Assert.assertTrue(t1.gemeinsam(t2)==.equals(6)); } } Grundlagen Programmierung Stephan Kleuker 426 Analyse von Häufigkeiten (1/3) public void haeufigkeiten(Integer anzahl){ Tipp mein = new Tipp(); Integer[] ergebnis = { 0, 0, 0, 0, 0, 0, 0 }; Integer[] gezogen = new Integer[49]; for (Integer i = 0; i < gezogen.length; i = i + 1) { gezogen[i] = 0; } losenUndAuswerten(mein, ergebnis, gezogen, anzahl); EinUndAusgabe io = new EinUndAusgabe(); for(Integer i = 0; i < ergebnis.length; i = i + 1){ io.ausgeben(i+"er: "+ergebnis[i]+"\n"); } for(Integer i = 0; i < gezogen.length; i = i + 1){ io.ausgeben((i+1)+":"+gezogen[i]+" "); if((i+1)%6 == 0){ io.ausgeben("\n"); } } } Grundlagen Programmierung Stephan Kleuker 427 Analyse von Häufigkeiten (2/3) public void losenUndAuswerten(Tipp mein, Integer[] ergebnis, Integer[] gezogen, Integer anzahl) { for (Integer i = 0; i < anzahl; i = i + 1) { Tipp tmp = new Tipp(); Integer treffer = mein.gemeinsam(tmp); ergebnis[treffer] = ergebnis[treffer] + 1; for (Integer j = 0; j < 49; j = j + 1) { if (tmp.enthaelt(j + 1)) { gezogen[j] = gezogen[j] + 1; } } } } Grundlagen Programmierung Stephan Kleuker 428 Analyse von Häufigkeiten (3/3) – 2000000 Versuche • Dauer: 14 Sekunden 0er: 870573 1er: 828056 2er: 263832 3er: 35540 4er: 1967 5er: 32 6er: 0 1:244608 2:244572 3:245968 4:245208 5:244385 6:245379 7:244733 8:244578 9:243898 10:244732 11:245054 12:244219 13:244770 14:245695 15:244858 16:244865 17:244949 18:244964 19:244590 20:245296 21:244815 22:244602 23:244568 24:244466 25:245505 26:244264 27:244369 28:245617 29:245516 30:245340 31:245063 32:244694 33:245223 34:244721 35:245899 36:244103 37:244962 38:245284 39:245049 40:245308 41:244512 42:244984 43:244649 44:244848 45:244985 46:245573 47:244426 48:244670 49:244664 Grundlagen Programmierung Stephan Kleuker 429 Mehrdimensionale Arrays 1 • Achtung niemals dadurch Klassen ersetzen! public void mehrdimensional1(){ EinUndAusgabe io = new EinUndAusgabe(); String[][] werte = {{"Mo","Jo","USA"} ,{"Luc","Lack","FRA"}}; for(String[] stud:werte){ for(String dat:stud){ io.ausgeben(dat+" "); } io.ausgeben("\n"); Mo Jo USA } Luc Lack FRA } Grundlagen Programmierung Stephan Kleuker 430 Mehrdimensionale Arrays 2 public void mehrdimensional2(){ EinUndAusgabe io = new EinUndAusgabe(); String[][][] all = {{{"Mo","Jo","USA", "NY"} ,{"Luc","Lack","FRA"}} ,{{"Sue","Wue","PRC"} ,{"Sam","Fox","GB", "ENG"}}}; for(String[][] sem:all){ for(String[] stud:sem){ Mo Jo USA NY for(String dat:stud){ Luc Lack FRA io.ausgeben(dat+" "); ***** } Sue Wue PRC io.ausgeben("\n"); Sam Fox GB ENG ***** } io.ausgeben("*****\n"); } } Grundlagen Programmierung Stephan Kleuker 431 Erinnerung: Arbeit mit Referenzen Ausgangspunkt • Gegeben Klasse Punkt – mit Objektvariablen x, y – zugehörigen get- und set-Methoden – passender equals-Methode – und toString-Methode • Klasse Interaktionsbrett u. a. zum Zeichnen von Linien void neueLinie(java.lang.Integer x1, java.lang.Integer y1, java.lang.Integer x2, java.lang.Integer y2) Methode zum Zeichnen einer neuen Linie. Grundlagen Programmierung Stephan Kleuker 432 Arrays und Referenzen (1/5) public class FigurMalerei{ private private private private Interaktionsbrett ib = new Interaktionsbrett(); Punkt p1 = new Punkt(20,30); Punkt p2 = new Punkt(50,30); Punkt[][] figuren = {dreieck(p1,p2,new Punkt(35,5)), dreieck(new Punkt(35,55),p2,p1)}; public Punkt[] dreieck(Punkt p1, Punkt p2, Punkt p3){ return new Punkt[]{p1,p2,p3}; } Grundlagen Programmierung Stephan Kleuker 433 Ausgangssituation im Speicher p1 p2 [0] figuren x 20 y 30 x 50 y 30 [0] x [1] 35 y 5 x 35 y 55 [0] [1] [2] [1] [2] Stephan Kleuker 434 Arrays und Referenzen (2/5) public void malen(Punkt[] punkte){ for(Integer i=0; i<punkte.length; i=i+1){ this.ib.neueLinie(punkte[i].getX(), punkte[i].getY(), punkte[(i+1)%punkte.length].getX(), punkte[(i+1)%punkte.length].getY()); } } public void malen(){ for(Punkt[] arr:this.figuren){ malen(arr); } } Grundlagen Programmierung Stephan Kleuker 435 Arrays und Referenzen (3/5) public void analyse1(){ malen(); } Grundlagen Programmierung Stephan Kleuker 436 Arrays und Referenzen (4/5) public void analyse2(){ this.figuren[0][2].setX(50); malen(); } Grundlagen Programmierung Stephan Kleuker 437 Arrays und Referenzen (5/5) public void analyse3(){ this.p1.setX(2); malen(); } Grundlagen Programmierung Stephan Kleuker 438 Auswahl einer Collection Grundlagen Programmierung Stephan Kleuker 439 Erinnerung und Erweiterung: Studentenverwaltung Nächste Aktion: (0) Programm beenden (1) Neuen Studenten einfügen (2) Student mit Matrikelnummer suchen (3) Studierendendaten ändern (4) Studierende pro Studiengang (5) Student löschen (6) Studenten eines Fachs löschen (7) Neuen Austauschstudenten einfügen (8) Anzahl Austauschstudenten ausgeben (9) Alle Studierenden anzeigen public class Studentenverwaltung { private ArrayList<Student> studenten; Grundlagen Programmierung Stephan Kleuker 440 Aufgabe: (9) Alle Studierenden anzeigen • einfache Realisierung public void zeigeAlleStudenten(){ EinUndAusgabe io = new EinUndAusgabe(); for (Student s: this.studenten){ io.ausgeben(s+"\n"); } } • ArrayList sehr einfach nutzbar, wenn Sammlung von Objekten vollständig verarbeitet werden soll • Vorüberlegung: HashSet etwas besser, da doppelte Studenten vermieden werden (aber keine Vergabe doppelter Matrikelnummern) Grundlagen Programmierung Stephan Kleuker 441 Analyse der Implementierung public Student studentSuchen(Integer mat){ for(Student s:this.studenten){ if(s.getMatrikelnummer().equals(mat)){ return s; } } return null; } • sehr aufwändige Realisierung, da immer alle Objekte durchsucht werden • zeigeAlleStudenten(): sehr einfache Realisierung, da immer alle Studierende durchlaufen werden müssen • wenn studentSuchen() sehr häufig genutzt, könnte das Programm hier als langsam angesehen werden • toll wäre Ansatz der schnellen Arrays mit direktem Zugriff auf das Objekt Grundlagen Programmierung Stephan Kleuker 442 java.util.HashMap • Idee: Speichere Objekte in Paaren (Key, Value), z. B. (Matrikelnummer, zugehöriger Student) • Ähnlich wie Array mit Paaren (Integer, Feldinhalt) • zentrale Methoden: – get (Key): gibt das zugehörige Value-Objekt (oder null) – put(Key,Value): trägt das Paar in die HashMap ein; sollte für Key anderer Value-Eintrag vorliegen, wird dieser überschrieben – remove(Key): löscht den Eintrag zum zugehörigen Key • hilfreich: – containsKey(Key): existiert zum Key ein Value-Objekt – containsValue(Value): existiert Value-Objekt in HashMap Grundlagen Programmierung Stephan Kleuker 443 Erinnerung: ordentlicher Punkt (ohne null) (1/2) public class Punkt { private Integer x; private Integer y; public Punkt(Integer x, Integer y) { this.x = x; this.y = y; } public Integer getX() { return this.x; } public Integer getY() { return this.y; } public void setX(Integer x) { this.x = x; } public void setY(Integer y) { this.y = y; } Grundlagen Programmierung Stephan Kleuker 444 Erinnerung: ordentlicher Punkt (ohne null) (2/2) @Override public String toString(){ return "["+this.x+","+this.y+"]"; } @Override public int hashCode() { return this.x + (this.y*33); } @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof Punkt)) { return false; } Punkt other = (Punkt) obj; return (this.x.equals(other.getX()) && this.y.equals(other.getY())); } } Grundlagen Programmierung Stephan Kleuker 445 Beispiel für HashMap-Nutzung (1/2) import java.util.HashMap; public class HashMapSpielerei{ public void analyse(){ HashMap<Integer,Punkt> punkte = new HashMap<Integer,Punkt>(); EinUndAusgabe io = new EinUndAusgabe(); Punkt p = new Punkt(1,1); punkte.put(17, new Punkt(0,0)); punkte.put(1, p); punkte.put(18, p); io.ausgeben("A: "+punkte+"\n"); A: {17=[0,0], 1=[1,1], 18=[1,1]} Grundlagen Programmierung Stephan Kleuker 446 Beispiel für HashMap-Nutzung (2/2) p.setY(4); io.ausgeben("B: "+punkte+"\n"); io.ausgeben("C: "+punkte.get(1)+"\n"); io.ausgeben("D: "+punkte.get(2)+"\n"); punkte.put(17, new Punkt(3,3)); io.ausgeben("E: "+punkte+"\n"); punkte.remove(18); io.ausgeben("F: "+punkte+"\n"); punkte.put(1,null); io.ausgeben("G: "+punkte+"\n") } } Grundlagen Programmierung B: C: D: E: F: G: {17=[0,0], [1,4] null {17=[3,3], {17=[3,3], {17=[3,3], Stephan Kleuker 1=[1,4], 18=[1,4]} 1=[1,4], 18=[1,4]} 1=[1,4]} 1=null} 447 Änderungen in Studentenverwaltung (1/4) import java.util.HashMap; public class Studentenverwaltung{ private HashMap<Integer,Student> studenten; public Studentenverwaltung(){ studenten = new HashMap<Integer,Student>(); } public void hinzufuegen(Student neu){ studenten.put(neu.getMatrikelnummer(),neu); } // weitere, zunächst kleine Anpassungen in get- und set Grundlagen Programmierung Stephan Kleuker 448 Änderungen in Studentenverwaltung (2/4) // machbar, aber diese Methode ist sehr rechenintensiv, // wenn häufiger benötigt, dann besser auf HashMap verzichten public void zeigeAlleStudenten(){ EinUndAusgabe io = new EinUndAusgabe(); for (Student s: this.studenten.values()){ io.ausgeben(s+"\n"); } } public void studentloeschen(Integer mat) { EinUndAusgabe io = new EinUndAusgabe(); Student tmp = this.studenten.remove(mat); if(tmp!=null){ io.ausgeben("Student gel\u00f6scht\n"); } } Grundlagen Programmierung Stephan Kleuker 449 Änderungen in Studentenverwaltung (3/4) // Nutzung unterschiedlicher Sammlungen zusammen public Integer studiengangLoeschen(String fach) { ArrayList<Integer> weg = new ArrayList<Integer>(); for(Student tmp:this.studenten.values()){ if(tmp.getStudiengang().equals(fach)){ weg.add(tmp.getMatrikelnummer()); } } for(Integer tmp:weg){ this.studenten.remove(tmp); } return weg.size(); } public Student studentSuchen(Integer mat){ return this.studenten.get(mat); } Grundlagen Programmierung Stephan Kleuker 450 Änderungen in Studentenverwaltung (4/4) // jetzt sehr einfache Programmverbesserung möglich public void studentEingeben(){ Student ergebnis = new Student(); ergebnis.setVorname(nichtLeererText("Vorname")); ergebnis.setNachname(nichtLeererText("Nachname")); ergebnis.setStudiengang(nichtLeererText("Studiengang")); ergebnis.setGeburtsjahr( zahlZwischen("Geburtsjahr",1900,2000)); Integer mat = zahlZwischen("Matrikelnummer",99999,1000000); if(studenten.get(mat) == null){ ergebnis.setMatrikelnummer(mat); hinzufuegen(ergebnis); } else { EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben("keine doppelten Matrikelnummern"); } Grundlagen Programmierung Stephan Kleuker 451 } ArrayList oder HashSet • Erinnerung: – ArrayList: Doppelte möglich, Ordnung beibehalten – HashSet: keine Doppelten, ungeordnet • Idee: HashSet besser, da so doppelte Einträge vermieden werden • Stimmt, da kein Student mehrfach eintragbar • bleibt kritisch, da Matrikelnummer immer noch doppelt vergebbar (außer wenn equals nur Matrikelnummer prüft; dann überschreibt aber neuer Student alten Studenten) • Fazit 1: Doppelte Matrikelnummernvergabe immer prüfen • Fazit 2: HashSet etwas besser als ArrayList geeignet Grundlagen Programmierung Stephan Kleuker 452 Exception Grundlagen Programmierung Stephan Kleuker 453 Erinnerung: Studentenverwaltung ***Semester XXXX*** Nächste Aktion: (0) Programm beenden (1) Neuen Studenten einfügen (2) Student mit Matrikelnummer suchen (3) Studierendendaten ändern (4) Studierende pro Studiengang (5) Student löschen (6) Studenten eines Fachs löschen (7) Neuen Austauschstudenten einfügen (8) Anzahl Austauschstudenten ausgeben (9) Alle Studierenden anzeigen (10) Semester eintragen public class Studentenverwaltung { private ArrayList<Student> studenten; private String semester = "Semester XXXX"; Grundlagen Programmierung Stephan Kleuker 454 Schreiben – Ansatz (1/2) • Basisansatz: Lesen (Input) oder Schreiben (Output) einzelner Bytes einer Datei(machbar, aber für Datentypen, die größer als Byte sind, unhandlich) • Es handelt sich um Streams • Schreiben: Datei öffnen und immer hinten anfügen • Lesen: Datei öffnen und von vorne nach hinten lesen • Streams sind immer unidirektional (lesen / schreiben) • Das Paket java.io stellt verschiedene Klassen zur Verfügung, die auf dem Streamkonzept basieren Grundlagen Programmierung Stephan Kleuker 455 Schreiben – Ansatz (2/2) • Prinzipieller Ansatz – öffne Datei – schreibe relevante Informationen – schließe Datei (WICHTIG !!!! -> schleichender Fehler) • Frage was wird geschrieben • Antwort: hängt von der genutzten Realisierung ab, abhängig vom Einsatzbereich • Ansatz hier: es können beliebige Beans geschrieben werden – Beans: • parameterloser Konstruktor • get- und set-Methoden für alle Objektvariablen • Typen der Objektvariablen sind Beans Grundlagen Programmierung Stephan Kleuker 456 Öffnen einer Datei • Java erlaubt einfaches Lesen und Speichern im XML-Format, wenn alle zu speichernden Objekte get- und set- Methoden in Standard-Form und parameterlosen Konstruktor haben • Zum Öffnen einer Datei "datei.xml" zum Schreiben von Daten wird folgende Konstruktion genutzt XMLDecoder file = null; file = new XMLEncoder( new BufferedOutputStream( new FileOutputStream("datei.xml"))); • Ermöglicht das sequentielle Schreiben (hintereinander) von Objekten in die Datei • Konstruktion wird (deutlich) später erläutert, muss jetzt erstmal hingenommen werden Grundlagen Programmierung Stephan Kleuker 457 Notwendige Imports • Hier kompakte Übersicht, weitere Erklärungen bei Nutzung import import import import import import import import import java.beans.XMLDecoder; java.beans.XMLEncoder; java.io.BufferedInputStream; java.io.BufferedOutputStream; java.io.File; java.io.FileInputStream; java.io.FileNotFoundException; java.io.FileOutputStream; java.util.ArrayList; Grundlagen Programmierung Stephan Kleuker 458 Programmerweiterungsfragmente + "(11) Daten speichern\n" + "(12) Daten laden: "); case 11: { datenSpeichern(); break; } case 12: { datenLaden(); break; } public void datenSpeichern() { String datei = nichtLeererText( "Dateiname (Abbruch mit \"Ende\")"); if (!datei.toUpperCase().equals("ENDE")) { datenSpeichern(datei); } } Grundlagen Programmierung Stephan Kleuker 459 Programmfragment – nicht lauffähig • der markierte Konstruktor kann eine sogenannte Exception (Ausnahme) werfen, die behandelt werden muss Grundlagen Programmierung Stephan Kleuker 460 Exception - Anwendungsszenario bisher bekannt • typischer Ablauf: Sequenz und Schleifen mit typischen Verhalten • alternative Abläufe: erwartete Spezialfälle, die den typischen Ablauf variieren (if- Anweisungen) neu • Ausnahme-Situationen, die keinen typischen Programmablauf ermöglichen, aber in denen das Programm sinnvoll reagieren kann, Beispiele: – erfolgreicher Aufbau einer Datenbankverbindung, Netzwerkverbindung oder zu einer Datei, die während der Nutzung abbricht • Wunsch: Programm wieder in einen nutzbaren Zustand führen Grundlagen Programmierung Stephan Kleuker 461 Exception - Ablaufprinzip Ausnahmesituation tritt auf • wenn Situation erkannt, wird typischer Programmablauf sofort verlassen und Programmstück für diesen Fehler angesprungen • das Programmstück ist entweder in der Methode, die gerade läuft, oder in einer aufrufenden Methode • sinnvoll: unabhängig ob Ausnahmesituation aufgetreten ist, sollten Abschlussaktionen möglich sein wichtige Hinweise: • Exception sollen nicht einfaches if ersetzen • Exception-Handling soll keine Programmierfehler kaschieren Grundlagen Programmierung Stephan Kleuker 462 Beispiel: lokale Behandlung public void datenSpeichern(String datei) { XMLEncoder file = null; try { file = new XMLEncoder( new BufferedOutputStream( new FileOutputStream(datei))); file.writeObject(semester); file.writeObject(studenten); } catch (FileNotFoundException e) { EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben("Probleme beim Schreiben: " + e); } finally { if (file != null) { file.close(); } } } Grundlagen Programmierung Stephan Kleuker 463 Fall 1: lokale Exception-Behandlung Grundlagen Programmierung Stephan Kleuker Typ1 Typ2 TypX 464 Methode weiterleiten (Fall2) ohne Exception an aufrufende public void machWas(...) { normalesProgramm; normalesProgramm; try { normalesProgramm; AnweisungEvtlException; normalesProgramm; } catch (ExceptionTyp1 e) { ReaktionAufException; } catch (ExceptionTyp2 e) { ReaktionAufException; } finally { Abschlussaktion; } normalesProgramm; } Speicherergebnis (Ausschnitt) Dateiname (Abbruch mit "Ende"): daten.xml <?xml version="1.0" encoding="UTF-8"?> <java version="1.6.0_12" class="java.beans.XMLDecoder"> <string>Semester XXXX</string> <object class="java.util.ArrayList"> <void method="add"> <object class="Student"> <void property="geburtsjahr"> <int>1987</int> </void> <void property="matrikelnummer"> <int>424241</int> </void> <void property="nachname"> <string>X</string> </void> ... Grundlagen Programmierung Stephan Kleuker 465 Einschub: BlueJ-Macke • BlueJ hat Schwierigkeiten mit der Nutzung der Klassen XMLDecoder und XMLEncoder • Typische Fehlermeldung: java.lang.reflect.InvocationTargetException Continuing ... • Lösung: einmal, z. B. im Konstruktor, muss vor der Nutzung folgende Zeilen ergänzt werden: public Studentenverwaltung() { Thread.currentThread().setContextClassLoader(getClass() .getClassLoader()); studenten = new ArrayList<Student>(); } Grundlagen Programmierung Stephan Kleuker 466 Laden - Ansatz • Der Ansatz zum Laden ist sehr ähnlich zum Speichern • Es wird wieder ein Stream benutzt, aus dem Daten gelesen werden können (eine Datei kann z. B. als Stream geöffnet werden) • die Daten werden wieder Schritt für Schritt eingelesen; dabei muss die Reihenfolge des Schreibens beachtet werden • mit readObject werden Objekte gelesen, die gecastet werden müssen • wieder ist das Schließen der Datei eminent wichtig • wieder müssen Ausnahmen (Datei nicht lesbar, oder Verbindung bricht ab) beachtet werden • Anmerkung: Klasse java.io.File kann zur Analyse von Dateien genutzt werden Grundlagen Programmierung Stephan Kleuker 467 Datei zum Laden auswählen public void datenLaden() { EinUndAusgabe io = new EinUndAusgabe(); String datei=nichtLeererText("Dateiname (Abbruch \"Ende\")"); if (datei.toUpperCase().equals("ENDE")){ return; } File file = new File(datei); if (!file.exists()){ io.ausgeben("Datei existiert nicht.\n"); return; } if (file.isDirectory()){ io.ausgeben("Verzeichnis nicht nutzbar.\n"); return; } if (!file.canRead()) { io.ausgeben("Datei nicht lesbar.\n"); return; } datenLaden(datei); } Grundlagen Programmierung Stephan Kleuker 468 Datei laden public void datenLaden(String datei) { EinUndAusgabe io = new EinUndAusgabe(); XMLDecoder file = null; try { file = new XMLDecoder( new BufferedInputStream( new FileInputStream(datei))); this.semester = (String) file.readObject(); this.studenten = (ArrayList<Student>) file.readObject(); io.ausgeben("Daten geladen.\n"); } catch (FileNotFoundException e) { io.ausgeben("Probleme beim Lesen: " + e); } finally { if(file!=null){ file.close(); } } Grundlagen Programmierung Stephan Kleuker 469 } Ausschnitt Nutzungsdialog (12) Daten laden: 12 Dateiname (Abbruch mit "Ende"): Hallo.text Datei existiert nicht. (12) Daten laden: 12 Dateiname (Abbruch mit "Ende"): .. Verzeichnis nicht nutzbar. (12) Daten laden: 12 Dateiname (Abbruch mit "Ende"): eNDe (12) Daten laden: 12 Dateiname (Abbruch mit "Ende"): daten.xml Daten geladen. Grundlagen Programmierung Stephan Kleuker 470 Nerviger Compiler • • • • Programm erzeugt beim Übersetzen eine Warning Grundregel: Warning beseitigen Hier: Warning hat Recht, kann aber nicht beseitigt werden Ansatz nur in solchen Fällen: spezielle Annotation @SuppressWarnings("unchecked") public void datenLaden(String datei) { Grundlagen Programmierung Stephan Kleuker 471 Test • Wichtig: Aufräumen nicht vergessen (noch besser in @Beforeund @After-Methoden, da dann garantiert ausgeführt) @Test public void testPersistenz1(){ studis.datenSpeichern("tmp.txt"); studis.studiengangLoeschen("ITI"); studis.studiengangLoeschen("MID"); studis.datenLaden("tmp.txt"); Assert .assertTrue(studis.studentenZaehlenIn("IMI").equals(2)); Assert .assertTrue(studis.studentenZaehlenIn("MID").equals(2)); File file = new File("tmp.txt"); file.delete(); } • weitere Testsszenarien denkbar und sinnvoll Grundlagen Programmierung Stephan Kleuker 472 Fall 2: Exception weiterreichen • Wird eine Exception nicht lokal behandelt, wird sie an die aufrufende Methode weitergeleitet • Damit Weiterleitung möglich, muss hinter der Parameterliste eine throws-Zeile mit allen möglichen Exceptiontypen stehen public void mach() throws ExceptionTyp1, ExceptionTyp2 • in der aufrufenden Methode muss Exception bearbeitet werden, wieder – Fall 1: lokal behandeln – Fall 2: über throws-Deklaration an aufrufende Methode weiterleiten • existiert aufrufende Methode nicht, endet Programm mit Fehlermeldung Grundlagen Programmierung Stephan Kleuker 473 Beispiel: Weiterreichen einer Exception (1/2) @SuppressWarnings("unchecked") public void datenLaden(String datei) throws FileNotFoundException { XMLDecoder file = null; file = new XMLDecoder(new BufferedInputStream( new FileInputStream(datei))); this.semester = (String) file.readObject(); this.studenten = (ArrayList<Student>) file.readObject(); file.close(); } Grundlagen Programmierung Stephan Kleuker 474 Beispiel: Weiterreichen einer Exception (2/2) public void datenLaden() { EinUndAusgabe io = new EinUndAusgabe(); String datei = nichtLeererText("Dateiname " +"(Abbruch mit \"Ende\")"); if (datei.toUpperCase().equals("ENDE")) { return; } try { datenLaden(datei); io.ausgeben("Daten geladen.\n"); } catch (FileNotFoundException e) { io.ausgeben("Problem beim Laden.\n"); } } Grundlagen Programmierung Stephan Kleuker 475 Variante • zumindest theoretisch könnte Datei offen bleiben • zu try muss es zumindest catch- oder finally-Block geben @SuppressWarnings("unchecked") public void datenLaden2(String datei) throws FileNotFoundException { XMLDecoder file = null; try { file = new XMLDecoder(new BufferedInputStream( new FileInputStream(datei))); this.semester = (String) file.readObject(); this.studenten = (ArrayList<Student>) file.readObject(); } finally { if (file != null) { file.close(); } } } Grundlagen Programmierung Stephan Kleuker 476 Testen von unerwünschten Exceptions @Test public void testPersistenz1(){ try { studis.datenSpeichern("tmp.txt"); studis.studiengangLoeschen("ITI"); studis.studiengangLoeschen("MID"); studis.datenLaden("tmp.txt"); Assert .assertTrue(studis.studentenZaehlenIn("IMI").equals(2)); Assert .assertTrue(studis.studentenZaehlenIn("MID").equals(2)); File file = new File("tmp.txt"); file.delete(); } catch (FileNotFoundException e) { Assert.assertTrue("unerwartete Exception",false); } } Grundlagen Programmierung Stephan Kleuker 477 Testen von erwünschten Exceptions @Test public void testPersistenz2(){ try { File file = new File("tmp.txt"); file.delete(); studis.datenLaden("tmp.txt"); Assert.assertTrue(false); } catch (FileNotFoundException e) { Assert .assertTrue(studis.studentenZaehlenIn("IMI").equals(2)); Assert .assertTrue(studis.studentenZaehlenIn("MID").equals(2)); } } Grundlagen Programmierung Stephan Kleuker 478 Fall 2: weiterleitende Exception-Behandlung Typ1 Typ2 Exception an aufrufende Methode weiterleiten (Fall2) public void rufAuf(...) throws ExTyp2 { ohne normalesProgramm; try { machWas(...); normalesProgramm; } catch (ExTyp1 e) { ReaktionAufException; } finally { Abschlussaktion; } } public void machWas(...) throws ExTyp1, ExTyp2 { normalesProgramm; AnweisungEvtlException; normalesProgramm; }Grundlagen Programmierung Stephan Kleuker 479 Exceptions selbst werfen (auslösen) • In unerwünschten Situationen kann man selbst Exceptions werfen; hierzu dient der Befehl throw, genauer throw new ExceptionTyp("Grund der Ausnahme"); • Man kann hier bereits in Java existierende Exceptions nutzen, die typischerweise einen Konstruktor mit zu übergebendem Grund enthalten • Man kann selbst Exception-Klassen schreiben; diese müssen von der Klasse Exception oder einer anderen existierenden Exception erben – üblich ist ein Konstruktor, dem der Grund als String übergeben werden kann • Konzept der Exception-Nutzung muss einheitlich im Projekt geklärt werden Grundlagen Programmierung Stephan Kleuker 480 Nutzung existierender Exception (1/2) public void datenSpeichern3(String datei) throws FileNotFoundException, IllegalArgumentException { if(!datei.startsWith("StudSave")){ throw new IllegalArgumentException("Dateianfang " + "mit StudSave ignoriert"); } XMLEncoder file = null; file = new XMLEncoder(new BufferedOutputStream( new FileOutputStream(datei))); file.writeObject(semester); file.writeObject(studenten); file.close(); } Grundlagen Programmierung Stephan Kleuker 481 Nutzung existierender Exception (2/2) public void datenSpeichern() { EinUndAusgabe io = new EinUndAusgabe(); String datei = nichtLeererText("Dateiname " +"(Abbruch mit \"Ende\")"); if (!datei.toUpperCase().equals("ENDE")) { try { datenSpeichern(datei); io.ausgeben("Speichern erfolgreich.\n"); } catch (FileNotFoundException e) { io.ausgeben("Speicherproblem: " + e.getMessage()+ "\n"); } catch (IllegalArgumentException e) { io.ausgeben("Namensproblem: " + e.getMessage()+ "\n"); } } (11) Daten speichern } (12) Daten laden: 11 Dateiname (Abbruch mit "Ende"): Sicherung Namensproblem: Dateianfang mit StudSave ignoriert Grundlagen Programmierung Stephan Kleuker 482 Eigene Exception public class DateinamenException extends Exception { public DateinamenException(){ super("Muss mit StudSave beginnen"); } public DateinamenException(String grund){ super(grund); } } Grundlagen Programmierung Stephan Kleuker 483 Nutzung eigener Exception (1/2) public void datenSpeichern4(String datei) throws FileNotFoundException, DateinamenException { if(!datei.startsWith("StudSave")){ throw new DateinamenException(); } XMLEncoder file = null; file = new XMLEncoder(new BufferedOutputStream( new FileOutputStream(datei))); file.writeObject(semester); file.writeObject(studenten); file.close(); } Grundlagen Programmierung Stephan Kleuker 484 Nutzung eigener Exception (2/2) public void datenSpeichern() { EinUndAusgabe io = new EinUndAusgabe(); String datei = nichtLeererText("Dateiname " +"(Abbruch mit \"Ende\")"); if (!datei.toUpperCase().equals("ENDE")) { try { datenSpeichern4(datei); io.ausgeben("Speichern erfolgreich.\n"); } catch (FileNotFoundException e) { io.ausgeben("Speicherproblem: " + e.getMessage()+ "\n"); } catch (DateinamenException e) { io.ausgeben("Namensproblem: " + e.getMessage()+ "\n"); } } (11) Daten speichern } (12) Daten laden: 11 Dateiname (Abbruch mit "Ende"): Sicherung Namensproblem: Muss mit StudSave beginnen Grundlagen Programmierung Stephan Kleuker 485 Ausschnitt aus Java-Exception-Klassen Grundlagen Programmierung Stephan Kleuker 486 Ausnahmenarten • Alle neuen Sprachen erlauben Exception Handling • Ausnahme werfen, wenn nicht erwarte Situation auftritt • Programmierfehler (z. B. NullPointerException), führt zum Programmabbruch; nie ernsthaft behandeln, nur z. B. in einer speziellen Log-Datei notieren • Echte Fehler (Error) sollen nie behandelt werden • Exceptions, die von RuntimeExeption erben, müssen nicht behandelt werden (unchecked Exception) und müssen nicht (dürfen schon) in throws-Listen stehen • Andere (checked) Exceptions müssen behandelt werden; nach außen geben (throws) oder behandeln (try-catch-finally) Grundlagen Programmierung Stephan Kleuker 487 Anmerkungen zu try-catch • Man kann try-catch-Blöcke beliebig ineinander schachteln • Man kann einen oder mehr catch-Blöcke zu einem try definieren • Compiler prüft, ob catch-Blöcke theoretisch erreichbar sind (nie erst allgemeine, dann spezielle Exception fangen) • Es können neue Exceptions auch in catch-Blöcken (weiter) geworfen werden; es beginnt neue Exception-Behandlung • Exceptions können gefangen und wieder geworfen werden catch(ExeptionTypX e){ throw e; • Bei Vererbung darf eine überschreibende Methode in einer erbenden Klasse nur die Exceptions der überschriebenen Methode der beerbten Klasse (oder weniger) werfen Grundlagen Programmierung Stephan Kleuker 488 Zusammenfassung zu Exceptions • In größeren Projekten wird festgelegt, wann es sich um unerwünschte, aber behandelbare Ausnahmen handelt • Für Ausnahmen werden eigene Klassen geschrieben (erben von Exception) • Für Ausnahmen wird festgelegt, wer reagieren kann (wenn lokal möglich, dann da, sonst weitergeben) • Für jeden Konstruktor und jede Methode wird dokumentiert, welche Exceptions sie werfen können (Erklärung in der Dokumentation) • Typisch ist, dass Ausnahmen protokolliert werden • catch (Throwable e){} ist schlechtester Stil Grundlagen Programmierung Stephan Kleuker 489 Klassenvariablen und Klassenmethoden Grundlagen Programmierung Stephan Kleuker 490 Objekte und Klassen • Bisher: Wir Programmieren das Verhalten von Objekten • Objekte können über Objektmethoden – verändert werden – Ergebnisse, d.h. neue Objekte, berechnen • Klassen beschreiben nur Objekte und erlauben über Konstruktoren die Erstellung beliebig vieler Objekte • außer Konstruktoren bieten Klassen selbst bisher nichts an • bisher nur recht aufwändig machbar: Vergabe eindeutiger Matrikelnummern • "letzte vergebene Matrikelnummer" ist keine ObjektEigenschaft, sondern Eigenschaft der Klasse Grundlagen Programmierung Stephan Kleuker 491 Klassenvariablen • Klassenvariable ist Eigenschaft einer Klasse, z. B. ein Zähler wieviele Objekte existieren, Schlüsselwort static private static Integer letzteNummer; • Klassenvariablen gehören zur Klasse und können damit ohne Objekte existieren • Klassenvariablen können in Objektmethoden genutzt werden • d. h. Objektvariablen kann der Wert von Klassenvariablen zugeordnet werden • einer Klassenvariable kann nicht ein zu einer Objektvariablen gehöriges Objekt zugewiesen werden (da Objekte gelöscht werden können) Grundlagen Programmierung Stephan Kleuker 492 Klassenmethoden • Klassenmethoden sind Methoden, die nur zur Klasse gehören und auch ohne Objekte ausgeführt werden können • In Klassenmethoden kann auf Klassenvariablen lesend und schreibend, andere Klassenmethoden und Parameter (auch Objekte) zugegriffen werden • Um deutlich zu machen, dass eine Klassenmethode aufgerufen wird, kann der Name der Klasse vor dem Methodennamen mit einem Punkt getrennt stehen <Klassenname>.<Klassenmethodenname>() • gleiche Schreibweise auch bei Klassenvariable möglich • wird Klassenmethode in anderer Klasse oder Objekt einer anderen Klasse genutzt, muss diese Schreibweise genutzt werden Grundlagen Programmierung Stephan Kleuker 493 Nutzung von Klassenvariablen und -methoden (1/2) public class Student{ private static Integer letzteNummer = 99999; protected String vorname ="Eva"; protected String nachname ="Mustermann"; ... public Student(){ Student.letzteNummer = Student.letzteNummer+1 this.matrikelnummer = Student.letzteNummer; } ; public static Integer leseLetzteNummer(){ return Student.letzteNummer; } public static void schreibeLetzteNummer(Integer m){ Student.letzteNummer = m; } Grundlagen Programmierung Stephan Kleuker 494 Nutzung von Klassenvariablen und -methoden (2/2) @SuppressWarnings("unchecked") public void datenLaden(String datei) { EinUndAusgabe io = new EinUndAusgabe(); XMLDecoder file = null; try { file = new XMLDecoder( new BufferedInputStream( new FileInputStream(datei))); this.semester = (String) file.readObject(); this.studenten = (ArrayList<Student>) file.readObject(); for(Student s: this.studenten){ if(s.getMatrikelnummer()>Student.leseLetzteNummer()){ Student.schreibeLetzteNummer(s.getMatrikelnummer()); } } io.ausgeben("Daten geladen.\n"); } catch (FileNotFoundException e) { ... Grundlagen Programmierung Stephan Kleuker 495 Beispiel: Ausgabe nächster Matrikelnummer // in Studentenverwaltung + "(13) n\u00e4chste Matrikelnummer: "); case 13: { naechsteMatrikelnummer(); break; } public void naechsteMatrikelnummer(){ EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben(" n\u00e4chste: "+Student.naechste()); } // in Student public static Integer naechste(){ return Student.letzteNummer+1; } Grundlagen Programmierung Stephan Kleuker 496 Nutzung einer Klassenmethode ohne Objekt Grundlagen Programmierung Stephan Kleuker 497 Analysebeispiel Vererbung (1/4) public class K1{ protected Integer objv; protected static Integer klav = 0; public K1(){ this.objv = K1.klav; K1.klav = K1.klav+1; } public static Integer klav(){ return K1.klav; } @Override public String toString(){ return"K1: "+this.objv; } }Grundlagen Programmierung Stephan Kleuker 498 Analysebeispiel Vererbung (2/4) public class KSpielerei{ } public void analyse1(){ K1[] ka = {new K1(), new K1(), new K1()}; for(K1 k:ka){ System.out.println(k); } K1: 0 System.out.println(K1.klav()); K1: 1 } K1: 2 3 Grundlagen Programmierung Stephan Kleuker 499 Analysebeispiel Vererbung (3/4) public class K2 extends K1{ public K2(){ super(); klav = klav+1; // besser K1.klav schreiben } //darf kein @Override stehen public static Integer klav(){ return K2.klav; // schlechtester erlaubter Stil } @Override public String toString(){ return"K2: "+super.objv; } } Grundlagen Programmierung Stephan Kleuker 500 Analysebeispiel Vererbung (4/4) • in KSpielerei public void analyse2(){ K1[] ka = {new K2(), new K1(), new K2()}; for(K1 k:ka){ System.out.println(k); K2: 0 } K1: 2 System.out.println(K1.klav()); K2: 3 System.out.println(K2.klav()); 5 } 5 Grundlagen Programmierung Stephan Kleuker 501 Anmerkung zu Sichtbarkeiten • Grundsätzlich sind Klassenvariablen auch private oder protected, Klassenmethoden public (oder auch private oder proteced) • theoretisch kann man Objektvariablen und Klassenvariablen auch public machen, so dass man direkt über die Punktnotation darauf zugreifen kann • zentrale Coding-Guideline: NIEMALS Objektvariablen und maximal nur konstante Klassenvariablen public machen • Java wird seit 1991 entwickelt, wenige historisch unsaubere Stellen, für Kompatibilität drin gelassen • public Objektvariable bei Array a: a.length • public Klassenvariable in System: System.out Grundlagen Programmierung Stephan Kleuker 502 Wann sind Klassenmethoden sinnvoll? • Klassenmethoden werden dann eingesetzt, wenn ausschließlich Klassenvariablen und andere Klassenmethoden betroffen sind • Klassenmethoden sind typischerweise Hilfsmethoden, die mit dem Konzept der Objektorientierung unmittelbar wenig zu tun haben • Grundsätzlich muss ein Entwickler in Objekten und ihrer Bearbeitung mit Objektmethoden denken • Erst wenn man feststellt, dass keine Objekte benötigt werden, kann man über die Erstellung von Klassenmethoden nachdenken • typisches Beispiel für Klasse mit ausschließlich Klassenmethoden ist java.lang.Math Grundlagen Programmierung Stephan Kleuker 503 Ausschnitt der Klassenmethoden von Math Modifier and Type Method and Description static double abs(double a) Returns the absolute value of a double value. static double cos(double a) Returns the trigonometric cosine of an angle. exp(double a) Returns Euler's number e raised to the power of static double a double value. log(double a) Returns the natural logarithm (base e) of a static double double value. pow(double a, double b) Returns the value of the first static double argument raised to the power of the second argument. random() Returns a double value with a positive sign, greater static double than or equal to 0.0 and less than 1.0. round(double a) Returns the closest long to the argument, with static long ties rounding up. sqrt(double a) Returns the correctly rounded positive square static double root of a double value. static double tan(double a) Returns the trigonometric tangent of an angle. Quelle: http://docs.oracle.com/javase/7/docs/api/index.html?overview-summary.html Grundlagen Programmierung Stephan Kleuker 504 Definition von Konstanten • • • • Konstanten sinnvoll in Klassenvariablen abgelegt diese Variablen sind dann public damit unveränderbar, wird Schlüsselwort final ergänzt damit Konstanten schnell erkennbar, werden sie ausschließlich in Großbuchstagen geschrieben • Beispiele in Math: Grundlagen Programmierung Stephan Kleuker 505 Beispiel: Nutzung der Math-Klassenmethoden public class MathAnalyse{ public void analyse(){ System.out.println(Math.PI); System.out.println(Math.E); // Math.PI = 42.0; wg. final nicht ok System.out.println(Math.random()); System.out.println(Math.pow(10,Math.pow(10,10))); System.out.println(Math.round(-0.5)); System.out.println(Math.round(0.5)); System.out.println(Math.sqrt(256.)); System.out.println(Math.log(Math.exp(42))); } 3.141592653589793 } 2.718281828459045 0.7704010465910343 Infinity 0 1 16.0 42.0 Grundlagen Programmierung Stephan Kleuker 506 Beispiel: Nutzung von Konstanten (1/3) • Auslagerung der Konstanten in eine neue Klasse (hier ggfls. auch Hilfsmethoden) public class Firma{ public static final String NAME = "Abmahnwahn KO"; public static final Double ZINS = 1.30; public static final Integer FRIST = 7; } public class Post{ public static void oeffnungsbrief(Schuldner s){ System.out.println("Ey "+s.getName()+",\n" +"Du hast "+s.getKohle()+" bei "+Firma.NAME +" geliehen.\nRück "+s.getKohle()*Firma.ZINS +" bis in "+Firma.FRIST+" Tagen raus,\n" +"sonst Stress.\n\tMfG ein Freund"); } } Grundlagen Programmierung Stephan Kleuker 507 Beispiel: Nutzung von Konstanten (2/3) public class Schuldner{ private String name; private Double kohle; public Schuldner(String n, Double k){ this.name = n; this.kohle = k; } public String getName(){ return this.name; } public Double getKohle(){ return this.kohle; } } Grundlagen Programmierung Stephan Kleuker 508 Beispiel: Nutzung von Konstanten (3/3) Ey Donald, Du hast 100.0 bei Abmahnwahn KO geliehen. Rück 130.0 bis in 7 Tagen raus, sonst Stress. MfG ein Freund Grundlagen Programmierung Stephan Kleuker 509 Analyse des Ansatzes • Recht einfache Änderung z. B. des Firmennamens, da nur eine Datei angepasst werden muss • Nachteil bleibt, dass Programm verändert wird und deshalb neu übersetzt werden muss • Flexiblerer Ansatz, wenn Firmendaten nur in einer mit normalen Editor änderbaren Datei stehen • Ansatz: Konstanten werden durch Klassenmethoden geliefert, bei erster Nutzung sollen die Werte aus einer Datei eingelesen werden Grundlagen Programmierung Stephan Kleuker 510 Optimierter Umgang mit Konstanten (1/6) public class Post{ public static void oeffnungsbrief(Schuldner s){ System.out.println("Ey "+s.getName()+",\n" + "Du hast "+s.getKohle()+" bei "+Firma.name() + " geliehen.\nRück "+s.getKohle()*Firma.zins() + " bis in "+Firma.frist()+" Tagen raus,\n" + "sonst Stress.\n\tMfG ein Freund"); } } Grundlagen Programmierung Stephan Kleuker 511 Optimierter Umgang mit Konstanten (2/6) import import import import import import import java.beans.XMLDecoder; java.io.BufferedInputStream; java.io.FileInputStream; java.io.FileNotFoundException; java.beans.XMLEncoder; java.io.BufferedOutputStream; java.io.FileOutputStream; public class Firma{ public static String NAME; public static Double ZINS; public static Integer FRIST; public static Boolean gelesen = false; public static final String DATEI ="config.xml"; Grundlagen Programmierung Stephan Kleuker 512 Optimierter Umgang mit Konstanten (3/6) public static String name(){ Firma.lesen(); return Firma.NAME; } public static Double zins(){ Firma.lesen(); return Firma.ZINS; } public static Integer frist(){ Firma.lesen(); return Firma.FRIST; } Grundlagen Programmierung Stephan Kleuker 513 Optimierter Umgang mit Konstanten (4/6) public static void schreiben(){ EinUndAusgabe io = new EinUndAusgabe(); XMLEncoder file = null; try { file = new XMLEncoder( new BufferedOutputStream( new FileOutputStream(Firma.DATEI))); System.out.print("Name: "); file.writeObject(io.leseString()); System.out.print("Zins: "); file.writeObject(io.leseDouble()/100+1); System.out.print("Frist: "); file.writeObject(io.leseInteger()); Firma.gelesen = false; } catch (FileNotFoundException e) { // unsauber } finally { if(file!=null){ file.close(); } Grundlagen Stephan Kleuker } Programmierung } 514 Optimierter Umgang mit Konstanten (5/6) public static void lesen(){ if(!Firma.gelesen){ XMLDecoder file = null; try { file = new XMLDecoder( new BufferedInputStream( new FileInputStream(Firma.DATEI))); Firma.NAME = (String) file.readObject(); Firma.ZINS = (Double) file.readObject(); Firma.FRIST = (Integer) file.readObject(); Firma.gelesen = true; } catch (FileNotFoundException e) { // unsauber } finally { if(file!=null){ file.close(); } } } } Programmierung Grundlagen Stephan Kleuker } 515 Optimierter Umgang mit Konstanten (6/6) Name: B. Trug Zins: 35 Frist: 5 Ey Uta, Du hast 10.0 bei B. Trug geliehen. Rück 13.5 bis in 5 Tagen raus, sonst Stress. MfG ein Freund Grundlagen Programmierung Stephan Kleuker 516 Start von Java-Programmen Grundlagen Programmierung Stephan Kleuker 517 Bisher • In BlueJ wurde Objekt erzeugt und dann Methode ausgewählt • alternativ könnte man jetzt direkt auf Klasse eine noch zu schreibende Klassenmethode ausführen • Ansatz weiter gedacht: Klasse heißt ausführbar, wenn sie eine Methode mit folgender Signatur hat public static void main(String[] arg){… Grundlagen Programmierung Stephan Kleuker 518 Nutzung einer Main-Klasse • sinnvoll ist es, eigene Klasse Main zu ergänzen, die ausschließlich zum Start des Programmes genutzt wird • Erstellung notwendiger Objekte und Aufruf von Methoden, damit Objekte "sich kennen" • Start einer Hauptmethode public class Main{ public static void main(String[] arg){ Studentenverwaltung s = new Studentenverwaltung(); s.steuern(); } } Grundlagen Programmierung Stephan Kleuker 519 Aufruf von main in BlueJ • Klassenmethode auswählen • Array kann direkt eingegeben werden (meist ohne Bedeutung, dann leer lassen) Grundlagen Programmierung Stephan Kleuker 520 Vom Source Code zum ausführbaren Programm Java Source (Punkt.java) Java Bytecode (Punkt.class) Java Compiler (javac) Just-in-time Compiler Java Virtual Machine (java) • Source Code wird vom Compiler in Bytecode übersetzt • Bytecode wird von JVM ausgeführt (d.h. interpretiert) • Ggf. werden über den JIT-Compiler bestimmte Befehlssequenzen in nativen Code übersetzt Grundlagen Programmierung Stephan Kleuker 521 Kompilierung mit der Konsole - Ausgangssituation Grundlagen Programmierung Stephan Kleuker 522 Kompilierung mit der Konsole - Kompilieren Grundlagen Programmierung Stephan Kleuker 523 Kompilierung mit der Konsole - Ausführen man erkennt, dass die Shell kein UTF-8 "spricht" Grundlagen Programmierung Stephan Kleuker 524 ausführbares Programm in BlueJ (1/4) • Annahme: gibt Klasse Main mit main-Methode • Packen des Programms in eine jar-Datei (vergleichbar einer zip-Datei mit einem bestimmten Aufbau) Grundlagen Programmierung Stephan Kleuker 525 ausführbares Programm in BlueJ (2/4) • Auswahl der Startklasse (könnte mehrere main geben) Grundlagen Programmierung Stephan Kleuker 526 ausführbares Programm in BlueJ (3/4) • Speichern der jar-Datei • jar-Datei unter jedem Betriebssystem nutzbar Grundlagen Programmierung Stephan Kleuker 527 ausführbares Programm in BlueJ (4/4) • Kommando-Zeile (oder Batch-Datei) • wenn graphisch (AWT oder Swing) und im Betriebssystem konfiguriert, dann Start durch Doppelklick Grundlagen Programmierung Stephan Kleuker 528 Nutzung von Übergabeparametern (1/2) public class MainAnalyse{ public static void main(String[] s){ if(s==null){ System.out.println("null"); } else { if(s.length==0){ System.out.println("kein Parameter"); } else { for(Integer i=0; i<s.length; i =i+1){ System.out.println(i+": "+s[i]); } } } } }Grundlagen Programmierung Stephan Kleuker 529 Nutzung von Übergabeparametern (2/2) Grundlagen Programmierung Stephan Kleuker 530 Entwicklungsumgebungen Grundlagen Programmierung Stephan Kleuker 531 Programmentwicklung in Java • Grundsätzlich reicht ein beliebiger Texteditor zur Programmerstellung, der Inhalt unformatiert speichert • Klasse K muss in Datei K.java stehen • java und javac benötigt • Problem: viele projekteinheitlich zu lösende Aufgaben müssen individuell geregelt werden – welche Einrückungen – woher kommen benötigte Klassen anderer – wer kompiliert Gesamtprojekt – wer erstellt wie die Dokumentation – ……… Grundlagen Programmierung Stephan Kleuker 532 Entwicklungsumgebung BlueJ BlueJ übernimmt einige typische Aufgaben: • Code-Formatierung (Einrückungen) • Syntax-Highlighting von Schlüsselwörtern • Einfaches Manövrieren in Klassen • Unmittelbare Möglichkeit zur Ausführung • Testklassendefinition und Testausführung (JUnit) • Einfach Debug-Möglichkeiten • aber, Entwicklungsumgebungen für Profis können wesentlich mehr • aber, nur BlueJ kann direkt mit Objekten kommunizieren • Fazit: BlueJ sehr gut zum Lernen geeignet, da man gezwungen wird, alles selbst zu tippen; für größere Projekte eher ungeeignet Grundlagen Programmierung Stephan Kleuker 533 Entwicklungsumgebung Eclipse • • • • • • • • • • • • Editor mit Syntax-Highlighting und Formatierung Anzeige aller Fehler einfaches Starten von Programmen einfaches Einbinden anderer Programme und Bibliotheken einfaches Ausführen von JUnit-Tests viele Möglichkeiten zur Code-Generierung: get-, set- Methoden, Varianten von Konstruktoren, equals, hashCode, toString Einbau umgebender try-catch-Blöcke mächtige Code-Completion (<Strg>+<Space>) einfache Nutzung eines sehr mächtigen Debuggers schnelles Manövrieren zu nutzenden und genutzten Methoden ……… Achtung: man benötigt zunächst nur sehr wenige der vielen Möglichkeiten, Schritt für Schritt vortasten Grundlagen Programmierung Stephan Kleuker 534 Beispiel: Studierendenverwaltung (1/4) - Eingabe Grundlagen Programmierung Stephan Kleuker 535 Beispiel: Studierendenverwaltung (2/4) - Ausführen Grundlagen Programmierung Stephan Kleuker 536 Beispiel: Studierendenverwaltung (3/4) - Debugging Grundlagen Programmierung Stephan Kleuker 537 Beispiel: Studierendenverwaltung (4/4) - Testen Grundlagen Programmierung Stephan Kleuker 538 Erweiterbarkeit von Eclipse • Eclipse kann einfach durch Plug Ins erweitert werden • viele verschiedene Programmiersprachen und Werkzeuge z. B. zur Datenbankverwaltung möglich • Vorwegwarnung: Jedes Plug In macht Eclipse langsamer • Einige weitere Unterstützung bei der Programmierung • statische Quellcode-Analyse – detaillierte Prüfungen von Style-Guides (Einrückungen bis magic Numbers) – Bewertung der Komplexität von Programmen (Anzahl der Verzeigungen) • Messung von Testüberdeckungen (wird Anweisung oder Zweig geprüft) • ……… Grundlagen Programmierung Stephan Kleuker 539 Beispiel: Eclemma für Testüberdeckung Grundlagen Programmierung Stephan Kleuker 540 Eclipse als IDE • IDE = Integrated Development Environment • Gesamter Entwicklungsprozess umfasst Phasen – Anforderungsanalyse – Design – Programmierung – Test • eine IDE unterstützt mehrere dieser Phasen • eine IDE unterstützt die Verwaltung der Ergebnisse (Versionsmanagement) • eine IDE unterstützt den Zusammenbau und die Installation der Software (Build-Management) • trotzdem bleibt IDE nur ein [zentraler] Teil der Entwicklung Grundlagen Programmierung Stephan Kleuker 541 Pakete Grundlagen Programmierung Stephan Kleuker 542 Idee von Paketen • Große Mengen von Klassen oft sehr unübersichtlich • Ähnlich zu Dateisystemen ist eine logische Ordnung in verschiedenen Ordnern sinnvoll • Ordner werden in Java Paket (package genannt) • Paketzugehörigkeitsdeklaration steht immer am Anfang der Klasse package de.ossoft.gui; public class Maske{ • Datei Maske.java muss im Unterordner de/ossoft/gui des Projekts liegen • Nutzung von Klassen aus anderen Paketen über import import de.ossoft.gui.Maske; Grundlagen Programmierung Stephan Kleuker 543 Ausschnitt: Java-Pakete der Klassenbibliothek java.util Contains the collections framework, legacy collection classes, event model, date and time facilities, internationalization, and miscellaneous utility classes (a string tokenizer, a randomnumber generator, and a bit array). java.util. concurrent Utility classes commonly useful in concurrent programming. java.util. A small toolkit of classes that support lock-free thread-safe concurrent.atomic programming on single variables. java.util. concurrent.locks java.util.jar java.util.logging Interfaces and classes providing a framework for locking and waiting for conditions that is distinct from built-in synchronization and monitors. Provides classes for reading and writing the JAR (Java ARchive) file format, which is based on the standard ZIP file format with an optional manifest file. Provides the classes and interfaces of the JavaTM 2 platform's core logging facilities. Grundlagen Programmierung Stephan Kleuker 544 Pakete in BlueJ • etwas aufwändig über das Menü Grundlagen Programmierung Stephan Kleuker 545 Klassen in Pakete verschieben (1/2) • leider funktioniert hier kein Drag & Drop in BlueJ Grundlagen Programmierung Stephan Kleuker 546 Klassen in Pakete verschieben (2/5) • Trick: Paketdeklaration direkt in Klasse eintragen Grundlagen Programmierung Stephan Kleuker 547 Klassen in Pakete verschieben (3/5) • Frage bei "Compile" bestätigen mit "Move" bestätigen Grundlagen Programmierung Stephan Kleuker 548 Klassen in Pakete verschieben (4/5) • Generell müssen Pakete erst angelegt werden, sonst Grundlagen Programmierung Stephan Kleuker 549 Klassen in Pakete verschieben (5/5) • Später Paket (Ordner) mit Doppelklick öffnen Grundlagen Programmierung Stephan Kleuker 550 Beispiel: einfache Software-Architektur (1/3) • bisher bei Studentenverwaltung alle Klassen in einem (dem default-) Paket eine typische Aufteilung: • entity-Paket: alle zu verwaltenden Datenklassen (typisch fast nur get- und set- Methoden) • control-Paket: Verwaltungsklassen, die Sammlungen von Entity-Objekten verwalten; Zugriff auf Entity-Objekte nur über Control-Klassen • boundary-Paket: externer Zugriff auf Verwaltungsklassen, z. B. Nutzungsoberfläche Grundlagen Programmierung Stephan Kleuker 551 Beispiel: einfache Software-Architektur (2/3) entity control (Zugriff auf entity-Objekte) Grundlagen Programmierung Stephan Kleuker 552 Beispiel: einfache Software-Architektur (3/3) • boundary (Zugriff auf control-Objekte) • fehlt: Aufteilung von Boundary und Control Grundlagen Programmierung Stephan Kleuker 553 Pakete und Imports package boundary; import control.Studentenverwaltung; public class Studentendialog { private Studentenverwaltung verwaltung = new Studentenverwaltung(); package control; import entity.Student; import entity.Austauschstudent; // weitere Imports public class Studentenverwaltung { private ArrayList<Student> studenten; private String semester = "Semester XXXX"; Grundlagen Programmierung Stephan Kleuker 554 Aufteilung in Boundary und Control-Funktionalität (1/2) //alt in Studentenverwaltung public void anzahlAustauschstudentenAusgeben() { EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben("Es gibt " + anzahlAustauschstudenten() + " Austauschstudenten\n"); } public Integer anzahlAustauschstudenten() { Integer ergebnis = 0; for (Student s : this.studenten) { ergebnis = ergebnis + s.zaehlenAlsAustauschstudent(); } return ergebnis; } Grundlagen Programmierung Stephan Kleuker 555 Aufteilung in Boundary und Control-Funktionalität (2/2) // neu: boundary (StudentenDialog) public void anzahlAustauschstudentenAusgeben() { EinUndAusgabe io = new EinUndAusgabe(); io.ausgeben("Es gibt " + verwaltung.anzahlAustauschstudenten() + " Austauschstudenten\n"); } // neu: control (Studentenverwaltung) public Integer anzahlAustauschstudenten() { Integer ergebnis = 0; for (Student s : this.studenten) { ergebnis = ergebnis + s.zaehlenAlsAustauschstudent(); } return ergebnis; } Grundlagen Programmierung Stephan Kleuker 556 Aufteilung in Boundary und Control-Funktionalität (1/4) //alt in Studentenverwaltung public void studentSuchen() { EinUndAusgabe io = new EinUndAusgabe(); Integer mat = zahlZwischen("Matrikelnummer" , 99999, 1000000); Student s = studentSuchen(mat); if (s != null) { io.ausgeben("Daten: " + s + "\n"); } else { io.ausgeben("nicht gefunden\n"); } } Grundlagen Programmierung Stephan Kleuker 557 Aufteilung in Boundary und Control-Funktionalität (2/4) //alt in Studentenverwaltung public Student studentSuchen(Integer mat) { Student ergebnis = null; for (Student s : this.studenten) { if (s.getMatrikelnummer().equals(mat)) { ergebnis = s; } } return ergebnis; } Grundlagen Programmierung Stephan Kleuker 558 Aufteilung in Boundary und Control-Funktionalität (3/4) // neu: boundary (StudentenDialog) public void studentSuchen() { EinUndAusgabe io = new EinUndAusgabe(); Integer mat = zahlZwischen("Matrikelnummer" , 99999, 1000000); String s = verwaltung.studentAusgeben(mat); if (s != null) { io.ausgeben("Daten: " + s + "\n"); } else { io.ausgeben("nicht gefunden\n"); } } Grundlagen Programmierung Stephan Kleuker 559 Aufteilung in Boundary und Control-Funktionalität (4/4) // neu: control (Studentenverwaltung) private Student studentSuchen(Integer mat) { Student ergebnis = null; for (Student s : this.studenten) { if (s.getMatrikelnummer().equals(mat)) { ergebnis = s; } } return ergebnis; } public String studentAusgeben(Integer mat){ Student s=studentSuchen(mat); if(s==null){ return null; } return s.toString(); } Programmierung Grundlagen Stephan Kleuker 560 alle Studieren in Control-Klasse public ArrayList<String> studentenAusgeben() { ArrayList<String> ergebnis = new ArrayList<String>(); for (Student s : this.studenten) { ergebnis.add(s.toString()); } return ergebnis; } • Beispiel zeigt deutlich, dass Boundary-Klassen keinen direkten Zugriff auf Entity-Klassen erhalten • (Anmerkung: Ansatz wird nicht immer so konsequent eingehalten) • Ansatz erlaubt leichte Erstellung einer grafischen Oberfläche Grundlagen Programmierung Stephan Kleuker 561 Ergänztes Main-Paket package main; import boundary.Studentendialog; public class Main{ public static void main( String[] arg){ Studentendialog s = new Studentendialog(); s.steuern(); } } Grundlagen Programmierung Stephan Kleuker 562 Kompilieren und Ausführen in der Konsole Grundlagen Programmierung Stephan Kleuker 563 Beispiel: Pakete und Compilieren \home\blubb\a package a; import a.d.D; import a.b.c.C; public class A{ dies ist File \home\blubb\a\A.java \home\blubb\a\d \home\blubb\a\b package a.d; import a.b.B; public class D{ private B b=null; } package a.b; public class B {…} class Ghost {…} \home\blubb\a\b\c package a.b.c; import a.d.D; public class C {} Grundlagen Programmierung Compiliert wird im Verzeichnis oberhalb von a (also in blubb) mit javac a\A.java Ausführung (auch in blubb) java a.A Stephan Kleuker 564 Nutzung von CLASSPATH • Damit Java Verzeichnisse mit Paketen findet, sollte die Umgebungsvariable CLASSPATH gesetzt werden • Diese Variable enthält die Start-Verzeichnisse, in denen bei einem Aufruf nach Klassen oder Paketen gesucht wird • Beim Aufruf >java C durchsucht das Laufzeitsystem sämtliche in CLASSPATH angegebenen Verzeichnisse nach C.class und führt diese aus • Beim Aufruf >java a.b.c.C sucht das Laufzeitsystem C.class in den Unterverzeichnissen a\b\c von Verzeichnissen aus CLASSPATH • Voreingestellt ist meist CLASSPATH=. (oder nichts), in Eclipse zusätzlich das Projektverzeichnis Grundlagen Programmierung Stephan Kleuker 565 Beispiel: Paketnutzung (1/2) package verwaltung.resourcen; import import import import import java.text.DateFormat; java.text.ParseException; java.text.SimpleDateFormat; java.util.Date; java.util.Locale; public class Person { private Date geburt; private String name; private Locale loc; public Person(String geburt, String name) throws ParseException{ DateFormat df = new SimpleDateFormat("dd.MM.yyyy"); this.geburt = df.parse(geburt); this.name = name; } Benutzeroberflächen und Software-Ergonomie Stephan Kleuker 566 Beispiel: Paketnutzung (2/2) public Person(String name){ this.geburt = new Date(); this.name=name; } public void setLoc(Locale loc){ this.loc=loc;} @Override public String toString(){ return name+": "+DateFormat.getDateInstance( DateFormat.LONG, loc).format(geburt); } } public static void main(String[] s) throws ParseException{ Person[] pers={new Person("31.2.2000","Ute"), new Person("Mischa")}; Locale[] loc={Locale.GERMAN, Locale.US, Locale.ITALIAN}; for(Person p:pers){ Ute: 2. März 2000 for(Locale l:loc){ Ute: March 2, 2000 p.setLoc(l); Ute: 2 marzo 2000 System.out.println(p); Mischa: 6. Februar 2012 } } Mischa: February 6, 2012 Benutzeroberflächen und Stephan Kleuker 567 } Software-Ergonomie Mischa: 6 febbraio 2012 Einige Realitätsbeichten Grundlagen Programmierung Stephan Kleuker 568 Bisher • Konsequente Orientierung an Objektorientierung • folgende Vokabeln auf alle objektorientierten Sprachen übertragbar: Klasse, Objekt, Objektvariable, Objektmethode, Sichtbarkeit, abstrakte Klasse, Klassenvariable, Klassenmethode, Paket • konsequente Orientierung an Klassen, deshalb Nutzung von Integer, Double, Boolean • Integer x=42; x ist echtes Objekt mit Methoden • Objekte der oben genannten Klassen (auch String) sind Konstanten, d. h. es können mit Methoden ihre Inhalte nicht verändert werden (gibt keine set-Methoden) Grundlagen Programmierung Stephan Kleuker 569 Java ist nicht konsequent objektorientiert • gleiches gilt für C++ und C# Hintergrund • C++ zunächst nur als Erweiterung von C konzipiert • Kompromiss, es sollten alle C-Programme weiter laufen • Resultat, C hatte einfache Datentypen, die keine Klassen sind und diese wurden nach C++ übernommen • Resultat, die durchgehende sehr saubere Idee der Objektorientierung wird mit Füßen getreten, es entsteht ein Mixed-Stil • Java sollte auch für C++-Entwickler schnell nutzbar sein, deshalb einfache, nicht objektorientierte Typen übernommen Hinweis: gibt echte objektorientierte Sprachen: Smalltalk (seit 1970er), Ruby Grundlagen Programmierung Stephan Kleuker 570 primitive Datentypen Typ repräsentiert boolean true or false char Unicode-Zeichen byte ganze Zahl mit Vorzeichen Größe 1 bit 16 bits 8 bits short - ´´ - 16 bits int - ´´ - 32 bits long - ´´ - 64 bits float IEEE 754-Fließkommazahl double void Prof. Dr. Stephan Kleuker 32 bits - ´´ - 64 bits - Stephan Kleuker 571 Beispielnutzung public class Analyse{ } public static void schleife(){ double[] arr = {0.2, 3., 7e300, 42.42}; for(int i=0; i<4; i=i+1){ System.out.println(i+" :"+ (arr[i]*i)); } 0 :0.0 } 1 :3.0 2 :1.4E301 3 :127.26 Grundlagen Programmierung Stephan Kleuker 572 Einfache Typen vor Java 5 • einfache Typen nicht in Sammlungen nutzbar • vor Java 5: Werte von Hand umwandeln (casten) public static void sammlung(){ ArrayList<Integer> al = new ArrayList<Integer>(); int x = 42; Integer xo = new Integer(x); al.add(xo); int y = (int)al.get(0); 42 System.out.println(y); } Grundlagen Programmierung Stephan Kleuker 573 Ab Java 5: Autoboxing • Kompiler wandelt bei Bedarf von int nach Integer und Integer nach int um • eher workaround, nicht wirklich sauber und auch langsam Integer x = 42; int y = x; Integer z = y; • einfache Typen können keine null-Werte annehmen • fieser Laufzeitfehler: Integer n = null; int m = n; Grundlagen Programmierung Stephan Kleuker 574 Beispiel: Autoboxing public static void boxen(){ ArrayList<Integer> al = new ArrayList<Integer>(); Integer x = new Integer(99999); al.add(42); // int nach Integer al.add(42+1); al.add(x); al.add(x+1); // Integer nach int, int nach Integer al.add(al.get(3)-1); for(int i:al){ System.out.print(i+" "); } boolean identisch = (al.get(2)==al.get(4)); System.out.println("\n identisch: "+ identisch); System.out.println(" gleich: "+ al.get(2).equals(al.get(4))); } 42 43 99999 100000 99999 identisch: false gleich: true Grundlagen Programmierung Stephan Kleuker 575 Zahlenbereiche und Genauigkeiten: int • einfache Datentypen und zugehörige Klassen können nur endlich viele Werte annehmen public static void intgenau(){ Integer max = Integer.MAX_VALUE; for(int i=0; i<3; i=i+1){ System.out.println(max+i); } Integer min = Integer.MIN_VALUE; for(int i=0; i<3; i=i+1){ System.out.println(min-i); } } Grundlagen Programmierung Stephan Kleuker 2147483647 -2147483648 -2147483647 -2147483648 2147483647 2147483646 576 Zahlenbereiche und Genauigkeiten: double (1/3) • sehr großer Zahlenbereich, Zahlen wie 1E12 ist 1*1012 • nicht jede Zahl darstellbar • bekannte besondere Werte Infinity, -Infinity, NaN public static void doubleGenau(){ Double max = Double.MAX_VALUE; for(double i=250; i<=300; i=i+10){ System.out.print(Math.pow(10,i)+" "); System.out.println(max+Math.pow(10,i)); } } 1.0E250 1.7976931348623157E308 9.999999999999999E259 1.7976931348623157E308 1.0E270 1.7976931348623157E308 1.0E280 1.7976931348623157E308 1.0E290 1.7976931348623157E308 1.0E300 Infinity Grundlagen Programmierung Stephan Kleuker 577 Zahlenbereiche und Genauigkeiten: double (2/3) public static void doublegenau2(){ Double winz = Double.MIN_NORMAL; System.out.println(winz); for(double i=10; i<=16; i=i+1){ System.out.println(Math.pow(10,-i)+": "+winz*Math.pow(10,-i)); } System.out.println(winz/0); System.out.println(0./0); 2.2250738585072014E-308 } 1.0E-10: 2.225074E-318 1.0000000000000001E-11: 2.22507E-319 1.0E-12: 2.2253E-320 1.0E-13: 2.223E-321 1.0E-14: 2.2E-322 1.0E-15: 2.5E-323 1.0E-16: 0.0 Infinity NaN Grundlagen Programmierung Stephan Kleuker 578 Zahlenbereiche und Genauigkeiten: double (3/3) public static void verschwindendeMillionen(){ Double konto = 0.0; Double abziehen = 1E6; konto = konto + 1E20 -abziehen - 1E20; System.out.println(konto); konto = 0.0; konto = konto + 1E21 -abziehen - 1E21; System.out.println(konto); konto = 0.0; konto = konto + 1E22 -abziehen - 1E22; System.out.println(konto); } -999424.0 -1048576.0 0.0 Grundlagen Programmierung Stephan Kleuker 579 Bearbeitung von Geldsummen • Beispiele zeigen: kein Double für Geldsummen • Beispiele zeigen: Integer (Long) eventuell zu klein • Ansatz: Schreibe eigene Klasse Geld • naiv: basierend auf Strings: "1234" + "567" = "1801" • alternativ ArrayList<Integer> mit den einzelnen Ziffern der Zahlen • Java bietet Klassen BigInteger und BigDecimal an • natürlich sehr viel langsamer als einfache Typen Grundlagen Programmierung Stephan Kleuker 580 Beispielnutzung von BigInteger import java.math.BigInteger; // … public static void big(){ BigInteger b = new BigInteger("12345"); for(int i=0; i<4; i=i+1){ b = b.multiply(b); System.out.println(b); } } 152399025 23225462820950625 539422123247359763587428687890625 290976227048689786699404610508159269970874403200651318511962890625 Grundlagen Programmierung Stephan Kleuker 581 Inkrement- und Dekrementoperatoren Operator Bedeutung x++ ergibt x und erhöht x um 1 ++x ergibt x+1 und erhöht x um 1 x-- ergibt x und verringert x um 1 --x ergibt x-1 und verringert x um 1 Prof. Dr. Stephan Kleuker Stephan Kleuker 582 Beispiele für Operatorennutzung public class IncTest { public static void main(String args[]){ int i = 1; System.out.println(“i: “+ i); i: 1 System.out.println(“++i: “+ ++i); ++i: System.out.println(“i++: “+ i++); i++: System.out.println(“i: “+ i); i: 3 System.out.println(“--i: “+ --i); --i: System.out.println(“i--: “+ i--); i--: System.out.println(“i: “+ i); i: 1 } 2 2 2 2 } Prof. Dr. Stephan Kleuker Stephan Kleuker 583 Typkonvertierungen (elementare Typen) Generell: Variablen von Typ x dürfen nur Elemente dieses Typs zugewiesen werden Bei Datentypen findet automatisch eine Konvertierung in größeren Typen statt (niemals automatisch in Gegenrichtung) byte short int long float double char • Umwandlung in die andere Richtung mit Casten (Typ in Klammern) int x= double double // int int z3 4; y= 2; z1= x/y; z2= x/y; gibt Fehler = (int) (x/y); Prof. Dr. Stephan Kleuker Stephan Kleuker 584 Logische Operatoren Operator Bedeutung & Und mit vollständiger Auswertung | Oder mit vollständiger Auswertung ^ wahr, wenn Operanden unterschiedlich sind ! Negation && Und mit Kurzschlussauswertung || Oder mit Kurzschlussauswertung Prof. Dr. Stephan Kleuker Stephan Kleuker 585 Beispielauswertung logischer Operatoren (1/2) public static boolean effekt(String s){ System.out.println("Aha "+s); return true; } public static void seiteneffekt(){ boolean wahr = true; boolean falsch = false; if(wahr || effekt("A")){ } if(wahr | effekt("B")){ } if(false && effekt("C")){ } if(false & effekt("D")){ } } Grundlagen Programmierung Stephan Kleuker Aha B Aha D 586 Beispielauswertung logischer Operatoren (2/2) public static void seiteneffekt2(){ if(effekt("E") ^ effekt("F")){ System.out.println("Hoho"); } if(effekt("G") ^ !effekt("H")){ System.out.println("Haha"); } if(!effekt("I") ^ effekt("J")){ System.out.println("Huhu"); } if(!effekt("K") ^ !effekt("L")){ System.out.println("Hihi"); } } Grundlagen Programmierung Stephan Kleuker Aha E Aha F Aha G Aha H Haha Aha I Aha J Huhu Aha K Aha L 587 Code-Formatierungen • bisherige Code-Formatierungen können so beibehalten werden, da sie Lesbarkeit garantieren • Möglichkeit: Weglassen von this und super • Möglichkeit: Weglassen geschweifter Klammern, wenn nur ein Befehl folgt • Möglichkeit: Direkter Zugriff auf Objektvariablen, wenn Objekte gleicher Klassen betrachtet werden • Anmerkung: Viele Coding-Guidelines in Unternehmen verbieten zumindest die ersten beiden Möglichkeiten • Rat: Wenn Sie bisherige Regeln anwenden, ändern Sie Ihren Stil nicht !!! Grundlagen Programmierung Stephan Kleuker 588 Beispiel: Klasse Punkt im Minimalstil (1/2) public class Punkt { private int x; private int y; public Punkt (int xWert, int yWert){ x = xWert; y = yWert; } public boolean istNullpunkt(){ if (x==0 && y==0) return true; else return false; } Grundlagen Programmierung Stephan Kleuker 589 Beispiel: Klasse Punkt im Minimalstil (2/2) public boolean equals(Object o){ if (o==null || !(o instanceof Punkt)) return false; Punkt p = (Punkt) o; if (x==p.x && y==p.y) return true; else return false; } } Grundlagen Programmierung Stephan Kleuker 590 Programmieren ohne echte Klassen Grundlagen Programmierung Stephan Kleuker 591 Braucht man Objektorientierung? • Ja, da so eine wesentliche Möglichkeit zur Strukturierung von Programmen geschaffen wird • Objekte sollen Ihre Aufgaben selbst erledigen, keine Hauptklasse, die alles über get- und set-Methoden steuert • Nein, da frühere Programmiersprachen (z. B. Fortran, Cobol, PL/1) keine Objektorientierung haben • da gab es aber massive Probleme mit großen Programmen • Nein, da für hardware-nahe Systeme oft keine objektorientierten Sprachen zur Verfügung stehen (aber C) Grundlagen Programmierung Stephan Kleuker 592 Programme ohne Objektorientierung • Anschaulich stehen nur Klassenvariablen und Klassenmethoden zur Verfügung • Weiterhin gibt es Klassen (Strukturen), die zur Beschreibung von Datenstrukturen genutzt werden (ähnlich zur Objektorientierung, Erzeugung einer neuen Struktur mit new) public class Punkt{ public int x; // in OO nie erlaubt public int y; // direkter Kündigungsgrund } Grundlagen Programmierung Stephan Kleuker 593 Beispiel: Datenbearbeitung (1/3) public class Punktbearbeitung{ public static Punkt neuerPunkt(int x, int y){ Punkt ergebnis = new Punkt(); ergebnis.x = x; ergebnis.y = y; return ergebnis; } public static Boolean gleich(Punkt p1, Punkt p2){ return (p1.x==p2.x && p1.y==p2.y); } Grundlagen Programmierung Stephan Kleuker 594 Beispiel: Datenbearbeitung (2/3) public static Boolean istNullpunkt(Punkt p){ return p.x==0 && p.y==0; } public static void verschieben(Punkt p, int dx, int dy){ p.x = p.x + dx; p.y = p.y + dy; } public static void ausgeben(Punkt p){ System.out.print("["+p.x+","+p.y+"]"); } } Grundlagen Programmierung Stephan Kleuker 595 Beispiel: Datenbearbeitung (3/3) public class Main{ public static void punktanalyse(){ Punkt p1; p1 = Punktbearbeitung.neuerPunkt(6,9); Punktbearbeitung.ausgeben(p1); System.out.println("\n" [6,9] +Punktbearbeitung.istNullpunkt(p1)); false Punkt p2 = Punktbearbeitung.neuerPunkt(3,6); [3,6] Punktbearbeitung.ausgeben(p2); false System.out.println("\n" true +Punktbearbeitung.gleich(p1,p2)); false Punktbearbeitung.verschieben(p2,3,3); System.out.println(Punktbearbeitung.gleich(p1,p2)); System.out.println((p1==p2)); } Grundlagen Programmierung Stephan Kleuker 596 Neue Datenstruktur: Liste (1/5) public class Punktliste{ public Punkt inhalt; public Punktliste weiter=null; // // // // // ähnlich zu Punkt und Punktbearbeitung wäre auch eine Aufteilung in Punktliste und Punktlistenbearbeitung sinnvoll, generell könnten auch alle Klassenmethoden in einer Datei (Klasse) stehen. public static Punktliste neuePunktliste(Punkt p){ Punktliste ergebnis = new Punktliste(); ergebnis.inhalt = p; return ergebnis; } Grundlagen Programmierung Stephan Kleuker 597 Neue Datenstruktur: Liste (2/5) public static void ausgeben(Punktliste pl){ Punktliste tmp = pl; System.out.print("["); while(tmp != null){ Punktbearbeitung.ausgeben(tmp.inhalt); tmp = tmp.weiter; } System.out.println("]"); } Grundlagen Programmierung Stephan Kleuker 598 Neue Datenstruktur: Liste (3/5) public static Punktliste anhaengen(Punktliste pl, Punkt p){ Punktliste tmp = pl; if(tmp == null){ return Punktliste.neuePunktliste(p); } while(tmp.weiter != null){ tmp = tmp.weiter; } tmp.weiter = Punktliste.neuePunktliste(p); return pl; } } Grundlagen Programmierung Stephan Kleuker 599 Neue Datenstruktur: Liste (4/5) // in Main public static void listenanalyse(){ Punkt p1 = Punktbearbeitung.neuerPunkt(6,9); Punkt p2 = Punktbearbeitung.neuerPunkt(3,6); Punktliste pe = null; Punktliste.ausgeben(pe); pe = Punktliste.anhaengen(pe,p1); Punktliste.ausgeben(pe); // weiter nächste Folie [] [[6,9]] Grundlagen Programmierung Stephan Kleuker 600 Neue Datenstruktur: Liste (5/5) pe = Punktliste.anhaengen(pe ,Punktbearbeitung.neuerPunkt(6,9)); pe = Punktliste.anhaengen(pe,p2); Punktliste.ausgeben(pe); pe = Punktliste.anhaengen(pe,p1); Punktliste.ausgeben(pe); p1.x = 9; Punktliste.ausgeben(pe); } } Grundlagen Programmierung [[6,9][6,9][3,6]] [[6,9][6,9][3,6][6,9]] [[9,9][6,9][3,6][9,9]] Stephan Kleuker 601 Laden und Speichern Grundlagen Programmierung Stephan Kleuker 602 Die Klasse File • Objekt von File wird zur Abfrage von Informationen über Dateien und Verzeichnisinhalte genutzt • Die Klasse File enthält z. B. Methoden zum – Auflisten von Dateien und Verzeichnissen – Prüfen, ob eine Datei existiert/lesbar/sichtbar schreibbar – Umbenennen / Löschen von Dateien bzw. Verzeichnissen • Beispiel: Wir wollen mit einem File arbeiten, dessen relativer Pfad unter Windows „ .\z\Hi.txt “ lautet – 1. Möglichkeit: File f= new File("./z/Hi.txt"); – man muss das Trennsymbol von Unix nutzen – 2. Möglichkeit: File f= new File(".\\z\\Hi.txt"); • Backslash (\) leitet in Java Sonderzeichen ein, deshalb \\ • 3. Möglichkeit: systemspezifischer Trenner in Klassenvariable String tr= File.separator; File f= new File("."+tr+"z"+tr+"Hi.txt"); Grundlagen Programmierung Stephan Kleuker 603 Einige Methoden der Klasse File (ohne Parameter) • boolean canRead() - testet, ob die Datei lesbar ist • boolean canWrite() - testet, ob die Datei beschreibbar ist • boolean delete() - löscht die Datei (bzw. das Verzeichnis) • boolean exists() - testet, ob die Datei existiert • boolean renameTo(File) - benennt die Datei um • boolean mkdir() - erzeugt ein Verzeichnis • boolean mkdirs() - erzeugt einen Verzeichnisbaum • String[] list() - liefert Liste von Dateien des Verzeichnisses • String getAbsolutePath(), String getPath() - liefert den entsprechenden Pfad der durch File beschriebenen Datei zurück • String getName() - gibt den Namen der Datei zurück • boolean isFile(), boolean isDirectory() - Abfrage der Dateiart Grundlagen Programmierung Stephan Kleuker 604 Dateianalyse (1/6) import java.io.File; import java.io.IOException; public class FileAnalyse{ public FileAnalyse(){ this.dialog(); } public void datei(File datei){ if(datei.canRead()){ System.out.println("lesbar"); } if(datei.canWrite()){ System.out.println("beschreibbbar"); } if(datei.canExecute()){ System.out.println("ausfuehrbar"); } } Grundlagen Programmierung Stephan Kleuker 605 Dateianalyse (2/6) public void dialog(){ EinUndAusgabe io = new EinUndAusgabe(); String datei = ""; File file; while(!datei.toLowerCase().equals("ende")){ System.out.print("zu untersuchende Datei: "); datei = io.leseString(); file = new File(datei); if (!file.exists()){ System.out.println("existiert nicht"); } else { this.allgemein(file); if(file.isDirectory()){ this.verzeichnis(file); } else { this.datei(file); } } } Grundlagen Stephan Kleuker } Programmierung 606 Dateianalyse (3/6) public void allgemein(File datei){ try{ System.out.println(" Pfad: "+datei.getAbsolutePath() +"\n Pfad: " + datei.getCanonicalPath() +"\n Pfad: " + datei.getPath() +"\n oben: " + datei.getParentFile() +"\n versteckt: " + datei.isHidden() +"\n veraendert: " + datei.lastModified() +"\n Laenge: " + datei.length()); } catch (IOException e){ System.out.println("Fehler: "+e); } } public void verzeichnis(File datei){ System.out.println("Enthaelt:"); for(String d: datei.list()){ System.out.println(" " + d); } Grundlagen Stephan Kleuker } Programmierung } 607 Dateianalyse (4/6) zu untersuchende Datei: . Pfad: F:\workspaces\BlueJWork11\FileAnalyse\. Pfad: F:\workspaces\BlueJWork11\FileAnalyse Pfad: . oben: null versteckt: false veraendert: 1324637498000 Laenge: 0 Enthaelt: package.bluej README.TXT FileAnalyse.java EinUndAusgabe.java EinUndAusgabe.class EinUndAusgabe.ctxt FileAnalyse.class FileAnalyse.ctxt Grundlagen Programmierung Stephan Kleuker 608 Dateianalyse (5/6) zu untersuchende Datei: C:\bootmgr Pfad: C:\bootmgr Pfad: C:\bootmgr Pfad: C:\bootmgr oben: C:\ versteckt: true veraendert: 1162461237049 Laenge: 438840 lesbar ausfuehrbar Grundlagen Programmierung Stephan Kleuker 609 Dateianalyse (6/6) zu untersuchende Datei: F:\workspaces\BlueJWork11\FileAnalyse\FileAnalyse.java Pfad: F:\workspaces\BlueJWork11\FileAnalyse\FileAnalyse.java Pfad: F:\workspaces\BlueJWork11\FileAnalyse\FileAnalyse.java Pfad: F:\workspaces\BlueJWork11\FileAnalyse\FileAnalyse.java oben: F:\workspaces\BlueJWork11\FileAnalyse versteckt: false veraendert: 1324641712000 Laenge: 1851 lesbar beschreibbbar ausfuehrbar Grundlagen Programmierung Stephan Kleuker 610 Ein- und Ausgabe in Java • Basisansatz: Lesen (Input) oder Schreiben (Output) einzelner Bytes ( machbar, aber für Datentypen, die größer als Byte sind, unhandlich) Schreiben Lesen • Es handelt sich um Streams • Streams sind immer unidirektional • Das Paket java.io stellt verschiedene Klassen zur Verfügung, die auf dem Streamkonzept basieren • Hinweis: wir arbeiten hier hauptsächlich mit 8-Bit-Versionen (gibt gleiche Klassen für Uni-Code, 16 Bit) Grundlagen Programmierung Stephan Kleuker 611 Einfaches Schreiben eines Files – Beispiel (1/2) import java.io.FileOutputStream; import java.io.FileNotFoundException; import java.io.IOException; public class Analyse { public static void fileOutput() throws FileNotFoundException, IOException{ FileOutputStream o = new FileOutputStream("xxx"); String s="Hello again"; for(int i=0; i<s.length(); i++){ o.write(s.charAt(i)); } o.close(); } }Grundlagen Programmierung Stephan Kleuker 612 Einfaches Schreiben eines Files – Beispiel (2/2) Grundlagen Programmierung Stephan Kleuker 613 Einfaches Schreiben eines Files - Analyse FileOutputStream o = new FileOutputStream("xxx"); String s="Hello again"; for(int i=0; i<s.length(); i++){ o.write(s.charAt(i)); } o.close(); • • • • Im Konstruktor wird ein Filename (oder File) übergeben Es kann nur Byte-Weise geschrieben werden nur verschiedene einfache Varianten von write() verfügbar Generell ist es wichtig, beim Arbeiten mit Files diese am Ende zu schließen ( close() ) • es gibt verschiedene Konstruktoren, z.B.: public FileOutputStream(String name, boolean append) Grundlagen Programmierung Stephan Kleuker 614 Einfaches Lesen eines Files FileInputStream i= new FileInputStream("xxx"); int in = i.read(); while(in!=-1) { System.out.print((char)in); in=i.read(); } i.close(); • Alle Bytes werden grundsätzlich als int eingelesen, müssen bei Bedarf in byte oder char gewandelt werden • Das File-Ende wird durch den Wert -1 erkannt • Vorgestelltes Lesen und Schreiben ineffizient, da jede Methode erneut auf das Betriebssystem zugreifen muss Grundlagen Programmierung Stephan Kleuker 615 Beispiel: Datei kopieren // mehrere Imports weggelassen public class Copy{ public static void main(String[] s) throws IOException{ FileInputStream in = new FileInputStream(s[0]); FileOutputStream out= new FileOutputStream(s[1]); int zeichen=0; while(zeichen!=-1){ zeichen=in.read(); if (zeichen!=-1) { out.write(zeichen); } } in.close(); out.close(); } }Grundlagen Programmierung Stephan Kleuker 616 Schnellere Ein- und Ausgabe in Java • einzulesende (bzw. auszugebende) Daten zwischenpuffern und bei vollem Puffer verarbeiten Ausgabe Puffer out(a) a out(b) a b out(c) a b c out(d) abc d Grundlagen Programmierung Stephan Kleuker 617 Die Klasse BufferedOutputStream • Daten werden in einen Puffer geschrieben, dessen Inhalt nur dann in den Stream geschrieben wird, wenn er voll ist BufferedOutputStream o = new BufferedOutputStream( new FileOutputStream("xxa"),4); String s="Hello again"; for(int i=0;i<s.length();i++) { o.write(s.charAt(i)); } o.close(); • BufferedOutputStream ermöglicht Daten zu schreiben, ohne bei jeden Aufruf physikalisch auf das Gerät zuzugreifen • explizites Leeren des Puffers mit flush() Grundlagen Programmierung Stephan Kleuker 618 Die Klasse BufferedInputStream • Daten werden blockweise in einen Puffer gelesen, auf dem bei jeder Leseaktion zugegriffen wird BufferedInputStream i= new BufferedInputStream( new FileInputStream("xxa"),4); int in = i.read(); while(in!=-1){ System.out.print((char)in); in=i.read(); } • BufferedInputStream ermöglicht Daten zu lesen, ohne bei jeden Aufruf physikalisch auf das Gerät zuzugreifen • BufferedInputStream enthält die Methoden • mark() zum markieren einer Position im Puffer • reset() zur Zurückführung des Pufferzeigers auf die letzte Markierung Grundlagen Programmierung Stephan Kleuker 619 Beispiel public static void bufferedIO() throws IOException{ BufferedOutputStream o = new BufferedOutputStream( new FileOutputStream("xxa"),4); String s="Hello again"; for(int i=0;i<s.length();i++){ o.write(s.charAt(i)); } BufferedInputStream i = new BufferedInputStream( new FileInputStream("xxa"),4); int in = i.read(); while(in!=-1){ Ausgabe: System.out.print((char)in); in=i.read(); Hello ag } i.close(); o.close(); } am Ende aber: Grundlagen Programmierung Stephan Kleuker 620 mächtigeres Arbeiten mit Files • Grundsätzlich kann man beliebige Speicher- und Lesefunktionalitäten auf FileInputStream und FileOutputStream aufbauen • Da sich diese Entwicklung komplexer Speicher- und Leseansätze sich häufig wiederholt, gibt es in der Klassenbibliothek verschiedene mächtigere Klassen zur Zusammenarbeit mit Files • Ein Ansatz wäre, von den genannten Klassen zu erben und die so zu erweitern • Java-Ansatz ist Aggregation, die mächtigeren Klassen haben ein Objekt der oben genannten Klasse als Objektvariable und verwalten den Zugriff Grundlagen Programmierung Stephan Kleuker 621 Die Klasse DataOutputStream • Mit Hilfe eines DataOutputStreams können einfache Datentypen binär geschrieben werden • • • • • • • • writeBoolean(); writeByte(); writeChar(); writeDouble(); writeFloat(); writeInt(); writeLong(); writeShort(); • Konstruktor erwartet einen Outputstream als Parameter public DataOutputStream(OutputStream os); • Analog existiert DataInputStream, mit zusätzlicher Methode readLine() zum Einlesen eines Strings Grundlagen Programmierung Stephan Kleuker 622 Beipiel (1/2) import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class Punkt { private int x; private int y; public Punkt(int a, int b) {this.x = a; this.y = b;} public void speichern(DataOutputStream o) throws IOException{ o.writeInt(this.x); o.writeInt(this.y); } public static Punkt lesen(DataInputStream i) throws IOException{ return new Punkt(i.readInt(),i.readInt()); } Grundlagen Programmierung Stephan Kleuker 623 Beispiel (2/2) public String toString(){ return "X: "+this.x+" Y: "+this.y; } } public static void main(String[] args) throws IOException { DataOutputStream o= new DataOutputStream( new FileOutputStream("aab")); Punkt p1= new Punkt(300,400); Punkt p2= new Punkt(500,600); p1.speichern(o); p2.speichern(o); o.close(); DataInputStream i= new DataInputStream( new FileInputStream("aab")); Punkt p3= Punkt.lesen(i); Punkt p4= Punkt.lesen(i); Ausgabe: System.out.println(p3+"\n"+p4); X: 300 Y: 400 i.close(); X: 500 Y: 600 }Grundlagen Programmierung Stephan Kleuker 624 Ein- und Ausgabe von Objekten (1/2) • Werden die Werte der Datenfelder eines Objektes in einen Bytestrom überführt, der sich rekonstruieren lässt, so spricht man von Objektserialisierung • Java stellt dazu zwei Klassen zur Verfügung ObjectOutputStream: public writeObject(Object o) throws IOException ObjectInputStream: public final Object readObject() throws OptionalDataException, ClassNotFoundException, IOException • Objekte werden beim Schreiben und Lesen rekursiv abgearbeitet (d. h. immer alle Objektvariablen) Grundlagen Programmierung Stephan Kleuker 625 Ein- und Ausgabe von Objekten (2/2) • Schnittstelle Serializable dient zur Kennzeichnung, dass ein Objekt einer Klasse serialisierbar ist • Schnittstelle selbst deklariert keine Methoden • Klasse nur serialisierbar, wenn alle Objektvariablen serialisierbar • bis auf einige graphische Klassen sind alle wichtigen Klassen der Klassenbibliothek serialisierbar • grundsätzlich ist "implements Serializable" immer gute Idee • Objekt-Serialisierung: – Vorteile: schnell, recht kompakte Daten – Nachteile: mit neuen Klassenversionen nicht mehr nutzbar, können von keinen anderen Programmen gelesen werden Grundlagen Programmierung Stephan Kleuker 626 Serialisierungsbeispiel (1/3) import java.io.Serializable; import java.io.IOException; import java.io.FileOutputStream; import java.io.ObjectOutputStream; import java.io.FileInputStream; import java.io.ObjectInputStream; public class Person implements Serializable{ private String name; private String vorname; private static final String DATEI = "text.txt"; private static final long serialVersionUID = 2L; public Person (String n, String v){ this.name = n; this.vorname = v; } @Override public String toString(){ return this.vorname+" "+this.name; } Programmierung Grundlagen Stephan Kleuker 627 Serialisierungsbeispiel (2/3) public static void schreiben() throws IOException{ Person pers1 = new Person ("Maier", "Anja"); ObjectOutputStream out= new ObjectOutputStream (new FileOutputStream(DATEI)); out.writeObject (pers1); out.writeObject (new Person ("Schulz", "Oleg")); out.writeObject ("Hallo"); out.close(); } public static void lesen() throws IOException, ClassNotFoundException{ ObjectInputStream in = new ObjectInputStream (new FileInputStream (DATEI)); for(int i=0; i<3; i=i+1){ Object o = in.readObject(); // sonst casten System.out.println(o); } in.close(); Grundlagen Programmierung Stephan Kleuker 628 } Serialisierungsbeispiel (3/3) public static void main(String[] s)throws IOException, ClassNotFoundException{ Person.schreiben(); Person.lesen(); } Grundlagen Programmierung Stephan Kleuker 629 SerialVersionUID • serialisierte Objekte typischerweise nur mit der gleichen Klasse einlesbar, die auch zum Schreiben genutzt wurde • Änderungen an Objektvariablen macht serialisierte Objekte nicht nutzbar • Lösung: Konverter schreiben • Lösung: XMLEncoder und XMLDecoder nutzen • woran erkennt neue Klasse, dass zu alte Objekte gelesen werden soll? – Nutzung eine speziellen vorgegebenen Klassenvariable serialVersionUID – Nummer wird mit serialisiert – stimmt sie beim Laden nicht überein, gibt es Fehlermeldung Grundlagen Programmierung Stephan Kleuker 630 serialVersionUID – Warning in Java • typische Eclipse-Meldung • Lösungsvorschläge in Eclipse (Linksklick) • im ersten Fall beginnt Nummerierung mit 1 (1L für long) • sonst zufällige Nummer Grundlagen Programmierung Stephan Kleuker 631 Beispielproblemfall public class Person implements Serializable{ private String name; private String vorname; private static final String DATEI = "text.txt"; private static final long serialVersionUID = 1L; • Person.schreiben() • Änderung: private static final long serialVersionUID = 2L; • Person.lesen() Grundlagen Programmierung Stephan Kleuker 632 transient Das Schlüsselwort transient: Unterdrückung von Objektvariablen beim Speichern public class person implements Serializable{ private String name; private String vorname; private transient int alter; .... } Grundlagen Programmierung Stephan Kleuker 633 Erinnerung: Uni-Code • Java als Sprache des Internets nutzt Uni-Code • Uni-Code ist echte Obermenge von ASCII und ISO 8859-1 (Latin 1) • In Java gibt es 8-Bit-Klassen (etwas schneller, einfacher) und 16-Bit-Klassen zum File-Handling (und Klassen, um 8Bit-Welt mit 16-Bit-Welt zu verknüpfen) • In Java hat Byte-Stream und Character-Stream Klassen Byte-Stream Character-Stream FileInputStream FileReader FileOutputStream FileWriter BufferedInputStram BufferedReader BufferedOutputStream BufferedWriter Grundlagen Programmierung Stephan Kleuker 634 Klassenvariablen von System public static final InputStream in The "standard" input stream. This stream is already open and ready to supply input data. Typically this stream corresponds to keyboard input or another input source specified by the host environment or user. public static final PrintStream out The "standard" output stream. This stream is already open and ready to accept output data. Typically this stream corresponds to display output or another output destination specified by the host environment or user. See the println methods in class PrintStream. public static final PrintStream err The "standard" error output stream. This stream is already open and ready to accept output data. Typically this stream corresponds to display output or another output destination specified by the host environment or user. Grundlagen Programmierung Stephan Kleuker 635 Einlesen von der Tastatur public class EinUndAusgabe { public String leseString(){ String ergebnis; BufferedReader in = new BufferedReader( new InputStreamReader(System.in)); try { ergebnis=in.readLine(); } catch (IOException e) { ergebnis=""; } return ergebnis; } public int leseInt(){ int erg; try { erg=Integer.decode(leseString()).intValue(); } catch (NumberFormatException e) { erg=-1; } return erg; Grundlagen Programmierung Stephan Kleuker } 636 Aufzählungen Grundlagen Programmierung Stephan Kleuker 637 Aufzählungen für Anfänger und Klassiker public class Programmiererfahrung { private String[] stufen={"nicht vorhanden", "Grundkenntnisse","alte Projekterfahrung", "Projektmitarbeiter", "Experte"}; private int stufenwert=0; ... } public class Erfahrung { public final static int NICHT_VORHANDEN=0; public final static int GRUNDKENNTNISSE=1; public final static int ALTE_PROJEKTERFAHRUNG=2; public final static int PROJEKTMITARBEITER=3; public final static int EXPERTE=4; } Zugriff mit: int meineErfahrung= Erfahrung.NICHT_VORHANDEN; Grundlagen Programmierung Stephan Kleuker 638 Aufzählungen mit Enum • Enum ist spezielle Art von Klasse (kann damit Methoden beinhalten) • Werte als einfache Texte, beginnend mit Buchstaben aufschreiben public enum Erfahrung { NICHT_VORHANDEN, GRUNDKENNTNISSE, ALTE_PROJEKTERFAHRUNG, PROJEKTMITARBEITER, EXPERTE } Grundlagen Programmierung Stephan Kleuker 639 Aufzählungen mit Enum - Nutzung public class Spielerei { public static void main (String[] s){ Erfahrung ich= Erfahrung.NICHT_VORHANDEN; System.out.println(ich); for(Erfahrung e: Erfahrung.values()){ System.out.println(e); } NICHT_VORHANDEN } NICHT_VORHANDEN } GRUNDKENNTNISSE ALTE_PROJEKTERFAHRUNG PROJEKTMITARBEITER EXPERTE Grundlagen Programmierung Stephan Kleuker 640 Weitere Methoden von enum-“Klassen“ (1/2) Erfahrung ich= Erfahrung.NICHT_VORHANDEN; System.out.println(ich.getClass()); System.out.println(ich.compareTo(Erfahrung.NICHT_VORHANDEN)); System.out.println(ich.compareTo(Erfahrung.EXPERTE)); System.out.println(ich.equals(0)); System.out.println(ich.equals(1)); System.out.println(ich.equals(Erfahrung.NICHT_VORHANDEN)); class Erfahrung 0 -4 false false true Grundlagen Programmierung Stephan Kleuker 641 Weitere Methoden von enum-“Klassen“ (2/2) System.out.println(ich.getDeclaringClass()); System.out.println(ich.name()); System.out.println(ich.ordinal()); System.out.println(Erfahrung.valueOf("EXPERTE")); try{ System.out.println(Erfahrung.valueOf("Experte")); }catch (IllegalArgumentException e){ System.out.println(e); } System.out.println(Erfahrung.valueOf(Erfahrung.class,"EXPERTE")); class Erfahrung NICHT_VORHANDEN 0 EXPERTE java.lang.IllegalArgumentException: No enum const Experte EXPERTE Grundlagen Programmierung Stephan Kleuker 642 Objekte von enum-Klassen • Mit enum werden besondere Klassen definiert, sind final (keine Vererbung möglich), keinen von außen nutzbaren Konstruktor • Durch Erfahrung ich= Erfahrung.NICHT_VORHANDEN; erhält man ein Objekt der Klasse Erfahrung • D. h. Enum-Klassen können zusätzlich „normale“ Exemplarmethoden und Exemplarklassen enthalten (auch z. B. toString()) • Klassenmethodenaufruf Erfahrung.klassenmethode(); • Methodenaufruf: ich.exemplarmethode(); Grundlagen Programmierung Stephan Kleuker 643 Erweiterte Möglichkeiten mit enum (1/2) • Aufzählungswerte können Parameter haben, die im Konstruktor verarbeitet werden public enum Coin { PENNY(1), NICKEL(5), DIME(10), QUARTER(25); private final int value; Coin(int value) { this.value = value; } public int value() { return value; } } Grundlagen Programmierung Stephan Kleuker 644 Erweiterte Möglichkeiten mit enum (2/2) public class CoinAnalyse { private enum CoinColor { COPPER, NICKEL, SILVER } public static void main(String[] args) { for (Coin c : Coin.values()){ System.out.println(c +":"+c.value()+"c "+color(c)); } } PENNY:1c COPPER private static CoinColor color(Coin c) { NICKEL:5c NICKEL DIME:10c SILVER switch(c) { case PENNY: return CoinColor.COPPER; QUARTER:25c SILVER case NICKEL: return CoinColor.NICKEL; case DIME: case QUARTER: return CoinColor.SILVER; default: throw new AssertionError("Unknown coin: " + c); } } }Grundlagen Programmierung Stephan Kleuker 645 Kartenstapel (1/3) public enum Farbe { KARO, HERZ, KREUZ, PIK; @Override public String toString(){ switch(this){ case KARO: return("Karo"); case HERZ: return("Herz"); case KREUZ: return("Kreuz"); case PIK: return("Pik"); default: return(""); } } } Prof. Dr. Stephan Kleuker Stephan Kleuker 646 Kartenstapel (2/3) public enum Wert { SIEBEN("7"), ACHT("8"), NEUN("9"), ZEHN("10"), BUBE("Bube"), DAME("Dame"), KOENIG("Koenig"), AS("As"); private String text; Wert(String text){ this.text=text; } @Override public String toString(){ return text; } } Prof. Dr. Stephan Kleuker Stephan Kleuker 647 Kartenstapel (3/3) public class Karte { private Farbe farbe; private Wert wert; ... public class Kartenstapel { private ArrayList<Karte> stapel=new ArrayList<Karte>(); public static Kartenstapel gibAlleKarten(){ Kartenstapel k= new Kartenstapel(); for(Farbe f: Farbe.values()) for(Wert w: Wert.values()) k.add(new Karte(f,w)); return k; } //... Prof. Dr. Stephan Kleuker Stephan Kleuker 648 Frames, Knöpfe und Ereignisbehandlung Grundlagen Programmierung Stephan Kleuker 649 AWT: Motivation • (AWT: Abstract Windowing Toolkit). • Einer der großen Vorteile von Java ist die PlattformUnabhängigkeit • Java bietet SW für integrierte graphische Benutzerschnittstelle, die auf allen Plattformen, auf denen eine Java-VM implementiert ist, ohne jede Änderung benutzt werden kann. • Bei bisherigen Programmierumgebungen galt eine eventuelle Plattform-Unabhängigkeit immer nur für das Kernsystem, also für die Programmiersprache, nicht aber für graphische Benutzerschnittstellen Grundlagen Programmierung Stephan Kleuker 650 Swing • Zu 100% in Java programmiert • AWT nutzt HW-Ressourcen, Swing unabhängig von HW, Gleiches Look-And-Feel auf allen Plattformen • Look-And-Feel kann vom Benutzer angepasst bzw. neu durch Swing-Komponenten erstellt werden • Vordefinierte Swing-Komponenten ermöglichen die Verwendung der Stil-Familien wie „Java“, „X-Windows“ und „Microsoft-Windows“ • Große Anzahl an graphischen Komponenten, die im Aussehen und Verhalten geändert werden können • vereinfachtes MVC-Entwurfsmuster benutzen • Langsamer als das AWT, nicht Thread-save • Eclipse nutzt eigene Bibliothek SWT Grundlagen Programmierung Stephan Kleuker 651 Ein erstes Fenster in Swing import javax.swing.JFrame; public class ErstesFenster extends JFrame{ public ErstesFenster(){ super("Ich habe einen Titel"); setSize(200,50); setLocation(100,30); setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE); setVisible(true); } public static void main(String[] args) { new ErstesFenster(); } } Grundlagen Programmierung Stephan Kleuker 652 Analyse des ersten Programms (1/3) import javax.swing.JFrame; public class ErstesFenster extends JFrame{ public ErstesFenster(){ super("Ich habe einen Titel"); ... } ... • Swing-Klassen beginnen mit einem J (z.B. JButton, JLabel, JApplet) • Fenster werden typischerweise von JFrame oder JDialog abgeleitet, oder Programm nutzt Objekt vom Typ JFrame • Zentrale Definitionen stehen im Konstruktor (oder dort aufgerufenen Methoden) • Konstruktor (genauer Methoden) der Oberklasse werden mit super(...) aufgerufen Grundlagen Programmierung Stephan Kleuker 653 Analyse des ersten Programms (2/3) public class ErstesFenster extends JFrame{ ... public static void main(String[] args) { new ErstesFenster(); } } • Ausführbare Klassen enthalten main-Methode • Erzeugung eines Fensters (also Aufruf des Konstruktors) durch new • Danach können Methoden von JFrame und ErstesFenster genutzt werden d.h. eine Oberfläche muss nicht von JFrame erben, es reicht wenn sie eine Objektvariable vom Typ JFrame hat Grundlagen Programmierung Stephan Kleuker 654 Analyse des ersten Programms (3/3) public ErstesFenster() setSize(200,50); setLocation(100,30); setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE); setVisible(true); } • verschiedene Methoden, damit ein Frame (auch JFrame) dargestellt wird, z. B.: – Größe – Position auf dem Bildschirm – Reaktion auf Beenden-Knopf , ohne wird Programm inaktiv, läuft aber weiter – Sichtbarkeit (immer zum Schluss) Grundlagen Programmierung Stephan Kleuker 655 Anmerkungen Einige Programmanteile dieser Vorlesungen sind ab hier als Teile des Konstruktors anzusehen. Das umgebende Programm wird nicht mehr angegeben. • Überlegen Sie sich bei angegebenen Methoden, zu welchen Klassen sie gehören (können) • Nutzen Sie die Java-Dokumentation und lesen Sie, welche Methoden welche Klassen anbieten • Versuchen Sie die Java-Klassenbibliothek genauer zu verstehen (welche Klasse erbt von wem, warum) Grundlagen Programmierung Stephan Kleuker 656 Aufbau eines JFrame (1/2) • Ein JFrame besteht aus den folgenden Komponenten innerhalb der sogenannten RootPane: Menu-Pane (JMenuBar) Content-Pane (Container) Layer-Pane (JLayerPane) Glass-Pane (Component) Grundlagen Programmierung Stephan Kleuker 657 Aufbau eines JFrame (2/2) • MenuPane: Platzierung des Hauptmenüs, falls vorhanden • ContentPane: Anordnung der Buttons, Texte, ... zentrale Fensterfläche • GlassPane: Popup-Menüs, Abfangen von Ereignissen, ... nicht Bestandteil der Vorlesung • LayerPane: Anordnung zusätzlicher Komponenten auch in zRichtung (Schichtung der Komponenten übereinander) nicht Bestandteil der Vorlesung • Grundidee: Panes sind Containter, die Sammlungen von GUI-Elementen aufnehmen, Methode: add Grundlagen Programmierung Stephan Kleuker 658 Label mit Beispielfunktionalität public class Labelanalyse extends JFrame{ public Labelanalyse(){ JLabel jl = new JLabel("Fisch", new ImageIcon("bild.png"),4); jl.setBorder(BorderFactory.createTitledBorder("Hai")); jl.setFont(new Font("Monospaced", Font.PLAIN, 20)); jl.setForeground(Color.BLUE); jl.setToolTipText("Nur ein kleiner Label"); add(jl); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); pack(); // ordentlich layouten } public static void main(String[] s) { new Labelanalyse(); } } Grundlagen Programmierung Stephan Kleuker 659 Implementierung eines Knopfes (V 1) (1/2) import javax.swing.JButton; import javax.swing.JFrame; public class EinKnopfV1 extends JFrame { private int count = 0; private JButton jb = new JButton("Gedrückt: " + count); public EinKnopfV1() { super("Ich habe einen Titel"); jb.addActionListener(new MyListener(this)); add(jb); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); pack(); } public void gedrueckt() { jb.setText("Gedrückt: " + (++count)); } public static void main(String[] args) { new EinKnopfV1(); Grundlagen Programmierung Stephan Kleuker } } 660 Implementierung eines Knopfes (V 1) (2/2) import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class MyListener implements ActionListener { private EinKnopfV1 knopf; public MyListener(EinKnopfV1 einKnopfV1) { knopf = einKnopfV1; } public void actionPerformed(ActionEvent ev) { knopf.gedrueckt(); } } • Varianten denkbar, z. B. JButton an MyListener übergeben Grundlagen Programmierung Stephan Kleuker 661 Implementierung eines Knopfes (V 2) public class EinKnopfV2 extends JFrame implements ActionListener{ private int count=0; private JButton jb= new JButton("Gedrückt: "+count); public EinKnopfV2(){ super("Knopf V2"); jb.addActionListener(this); add(jb); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); pack(); } public void actionPerformed(ActionEvent ev){ jb.setText("Gedrückt: "+(++count)); } public static void main(String[] args) { new EinKnopfV2(); } } Grundlagen Programmierung Stephan Kleuker 662 Implementierung eines Knopfes (V 3) public class EinKnopfV3 extends JFrame{ private int count=0; private JButton jb= new JButton("Gedrückt: "+count); public EinKnopfV3(){ super("Ich habe einen Titel"); jb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { jb.setText("Gedrückt: "+(++count)); } }); add(jb); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); pack(); } } public static void main(String[] args) { new EinKnopfV3(); } Grundlagen Programmierung Stephan Kleuker 663 Grober Vergleich der Varianten • Variante 2 und Variante 3: + schneller zu erstellen + weniger Klassen + Zugriff aus der Ereignis-Methode auf private Objektvariablen und Methoden der umgebenden Klasse - starke Tendenz zum Spaghetti-Code, da es viele Auslöser für ein Action-Event geben kann. • Variante 1: - teilweise aufwändiger zu erstellen - mehr Klassen + häufig flexibler bei vielen verschiedenen Ereignistypen + Widerverwendbarkeit von Ereignis-Code in anderen Klassen Grundlagen Programmierung Stephan Kleuker 664 Sauberer Start • bisher: new EinKnopfV3(); • es werden viele GUI-Komponenten angelegt, die eventuell bei Erstellung mit anderen Komponenten „reden“ • Event-Verwaltung findet in unabhängigen Prozessen statt (genauer: Threads -> Betriebssysteme) • Problem: Komponenten reden mit noch nicht erzeugten Komponenten ( -> NullPointerException) javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { new EinKnopfV3(); } }); Grundlagen Programmierung Stephan Kleuker 665 Model-View-Controller • Allgemeiner Aufbau des MVC: View GUI-Komponente Model Controller • Aufbau des MVC in Swing: View GUI-Komponente Model Controller Delegate Grundlagen Programmierung Stephan Kleuker 666 Button - Hierarchie • Klassenhierarchie für alle Buttons: JComponent AbstractButton JToggleButton JButton JMenuItem JCheckBox JMenu JRadioButton JCheckBoxMenuItem JRadioButtonMenuItem Grundlagen Programmierung Stephan Kleuker 667 JButton (1/4) • Ein JButton unterstützt u. a. die Ereignistypen ChangeEvent und ActionEvent der Basisklasse AbstractButton • Reihenfolge der Ereignisse beim JButton: Benutzer drückt die Taste (Maus oder Tastatur) 1. ChangeEvent (armed = true) 2. ChangeEvent (armed = true, pressed = true) Benutzer lässt die Taste (Maus oder Tastatur) wieder los 3. ActionEvent 4. ChangeEvent (armed = true, pressed = false) 5. ChangeEvent (armed = false, pressed = false) • Hinweis: armed zeigt an, ob der Mauszeiger bei gedrückter Maustaste noch über dem Button ist Grundlagen Programmierung Stephan Kleuker 668 JButton (2/4) Ablauf der Ereignisse für einen JButton • Der Mauszeiger wurde über dem Button losgelassen JButton ButtonModel Mouse Pressed ChangeListener ActionListener armed=true pressed=true Mouse Released pressed=false activated armed=false Grundlagen Programmierung Stephan Kleuker 669 JButton (3/4) Ablauf der Ereignisse für einen JButton • Die Maustaste ist gedrückt während der Mauszeiger den Button verlässt und wieder betritt. JButton ButtonModel Mouse Pressed ChangeListener ActionListener armed=true pressed=true Mauszeiger verlässt Button Mauzeigers betritt Button wieder armed=false armed=true • Möglichkeit zum Zugriff auf das Modell für ChangeEvent e: AbstractButton source=(AbstractButton)e.getSource(); ButtonModel model = source.getModel(); Grundlagen Programmierung Stephan Kleuker 670 JButton (4/4) Ablauf der Ereignisse für einen JButton • Die Maustaste wurde gedrückt • Der Mauszeiger verlässt den Button • Die Maustaste wird losgelassen JButton ButtonModel Mouse Pressed ChangeListener ActionListener armed=true pressed=true Mauszeiger verlässt Button Mouse Released Grundlagen Programmierung armed=false pressed=false Stephan Kleuker 671 Unterscheidung von Komponenten • Häufig möchte man verschiedene Knöpfe gerne in actionPerformed unterscheiden. Ein Ansatz ist es, jedem Knopf ein Kommando zuzuordnen: JButton jb4 = new JButton("Aloa"); jb4.setActionCommand("k4"); • Dieser zugewiesene Text kann vom Event abgefragt werden public void actionPerformed(ActionEvent ev){ if(ev.getActionCommand().equals("k4"))... • Der Programmierer ist für die Eindeutigkeit der KommandoNamen zuständig ; Konstanten nicht direkt im Code • auch sehr häufig genutzt: bestimme Quelle und nutze sie AbstractButton source = (AbstractButton) ev.getSource(); ButtonModel model = source.getModel(); Grundlagen Programmierung Stephan Kleuker 672 Menüs bestehen aus Knöpfen (1/2) public class MenueBeispiel extends JFrame implements ActionListener { private JLabel label = new JLabel("ein Text"); private JMenuItem machItem(String text){ JMenuItem menuItem = new JMenuItem(text); menuItem.addActionListener(this); return menuItem; } @Override public void actionPerformed(ActionEvent e) { label.setText(((AbstractButton)e.getSource()).getText()); } public static void main(String[] args) { new MenueBeispiel(); } Grundlagen Programmierung Stephan Kleuker 673 Menüs bestehen aus Knöpfen (2/2) public MenueBeispiel(){ add(label); JMenuBar menuBar = new JMenuBar(); JMenu menu=new JMenu("Hauptmenue1"); menu.add(machItem("Punkt1.1")); menu.add(machItem("Punkt1.2")); menuBar.add(menu); menu = new JMenu("Hauptmenue2"); menu.add(machItem("Punkt2.1")); JMenu menu2=new JMenu("Submenue2.2"); menu2.add(machItem("Punkt2.2.1")); menu2.add(machItem("Punkt2.2.2")); menu.add(menu2); menuBar.add(menu); setJMenuBar(menuBar); } setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocation(800, 100); setVisible(true); pack(); Grundlagen Programmierung Stephan Kleuker 674 Layout, hierarchischer GUI-Aufbau Grundlagen Programmierung Stephan Kleuker 675 Layoutmanager – Null-Layout (1/5) • Bisher wurden Komponenten (z.B. JButton) mit der Methode add zur Content-Pane hinzugefügt • Wie können mehrere Komponenten angeordnet werden (Position, Größe, ...)? • Plattformabhängige Lösung: absolute Position und Größe der Komponente werden direkt angegeben Grundlagen Programmierung Stephan Kleuker 676 Layoutmanager – Null-Layout (2/5) Problem 1: Unterschiedliche Plattformen • Der folgende Quelltext zeigt eine Anwendung, in der zwei Buttons absolut positioniert werden Problem: verschiedene Zeichensätze auf unterschiedlichen Plattformen und verschiedene Java-Versionen. Grundlagen Programmierung Stephan Kleuker 677 Layoutmanager – Null-Layout (3/5) public class Nulllayout extends JFrame { public Nulllayout() { super("Titeltext"); setLayout(null); JButton okButton = new JButton("Weiter"); JButton cancelButton = new JButton("Abbrechen"); okButton.setBounds(0, 0, 100, 25); cancelButton.setBounds(100, 0, 100, 25); add(okButton); add(cancelButton); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(50, 50, 250, 60); } Grundlagen Programmierung Stephan Kleuker 678 Layoutmanager – Null-Layout (4/5) public static void main( String args[] ) { NullLayout nl = new NullLayout(); nl.setVisible( true ); } } Bildschirmausgabe (Windows 2000, Windows 7): Bildschirmausgabe (SuSE LinuX 7.2, KDE 2, J2SDK 1.4) Bildschirmausgabe (SuSE LinuX 7.2, KDE 2, J2SDK 1.3): Grundlagen Programmierung Stephan Kleuker 679 Layoutmanager – Null-Layout (5/5) Problem 2: Interaktive Größenänderungen der Dialoge • Der Anwender ändert die Fenstergröße interaktiv der Inhalt passt sich nicht automatisch an (Platzverschwendung oder „Verschwinden“ von Inhalten): Grundlagen Programmierung Stephan Kleuker 680 Analyse des Null-Layouts Konsequenzen: • Plattformunabhängige Programmierung ist mit absoluten Layouts nicht sinnvoll möglich (unterschiedliche Zeichensätze oder Zeichengrößen) – Portierung einer Anwendung in mehrere Sprachen erfordert bei absoluten Layouts manuelle Nacharbeiten nicht praktikabel – Niemals ohne Layoutmanager mit Swing Benutzeroberflächen erstellen! Grundlagen Programmierung Stephan Kleuker 681 GridLayout (1/4) • GridLayout ordnet Komponenten im festen Zellenraster an • Jede Komponente füllt ihre eigene Zelle komplett aus Komp. 1 Komp. 2 Komp. 3 Komp. 4 Komp. 5 ... Komp. 6 ... Komp. n-1 Komp. n • Reihenfolge der Komponenten wird durch die Reihenfolge des Einfügens festgelegt (zeilenweise) • Darstellung im Raster wird durch die Sprachausrichtung des Containers festgelegt • Raster-Abstand zwischen den Komponenten kann eingestellt werden Grundlagen Programmierung Stephan Kleuker 682 GridLayout (2/4) Berechnung der Komponentengrößen • alle Zeilen des Rasters gleich hoch, alle Spalten gleich breit • Die Komponentengrößen entsprechen: – Breite: Breite des Containers / Anzahl Spalten – Höhe: Höhe des Containers / Anzahl Zeilen Einstellung des Layouts durch Konstruktor • GridLayout(): Ausrichtung aller Komponenten in einer Zeile. • GridLayout(int rows, int cols): Erzeugt ein Layout mit der angegebenen Anzahl Zeilen und Spalten; sind beide Werte !=0, wird cols ignoriert und durch die Anzahl Komponenten bestimmt, cols wird nur ausgewertet, wenn rows==0 ist • GridLayout(int rows, int cols, int hgap, int vgap): Zusätzlich horizontaler Zeilenabstand von hgap Pixeln, vertikaler Zeilenabstand von vgap Pixeln eingefügen Grundlagen Programmierung Stephan Kleuker 683 GridLayout (3/4) public class Gridlayout extends JFrame { public Gridlayout() { super("GridLayout"); setLayout(new GridLayout(0, 3, 2, 2)); for(int i= 1; i<=4; i=i+1){ add(new JButton("K "+i, new ImageIcon("Knopf"+i+".gif"))); } setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pack(); // optimale Groesse selbst berechnet setVisible(true); } public static void main(String args[]) { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { new Gridlayout(); } }); } Grundlagen Programmierung Stephan Kleuker } 684 GridLayout (4/4) Bildschirmausgaben (Start, manuell verkleinert und verzerrt): Grundlagen Programmierung Stephan Kleuker 685 Ermittlung von Größen (1/3) • Woher kennt Layoutmanager die Größen der Komponenten? – Komponenten kennen ihre Größen – Layoutmanager fragt die Komponenten ab – Größenangaben können vom Layoutmanager ignoriert werden – Swing-Komponenten erben von Basisklasse Component Grundlagen Programmierung Stephan Kleuker 686 Ermittlung von Größen (2/3) • Component hat Methoden, um die folgenden Größen auslesen bzw. schreiben zu lassen: Eigenschaft Dimension preferredSize Dimension minimumSize Dimension maximumSize Methoden Dimension getPreferredSize() setPreferredSize( Dimension dim ) Dimension getMinimumSize() setMinimumSize( Dimension dim) Dimension getMaximumSize() setMaximumSize( Dimension dim ) Grundlagen Programmierung Beschreibung Liest die bevorzugte Größe der Komponente Schreibt die bevorzugte Größe der Komponente Liest die minimale Größe der Komponente Schreibt die minimale Größe der Komponente Liest die maximale Größe der Komponente Schreibt die maximale Größe der Komponente Stephan Kleuker 687 Ermittlung von Größen (3/3) • Dimension: Klasse, die zwei öffentlich les- und schreibbare Attribute enthält: – width: Breite in Pixeln – height: Höhe in Pixeln • minimumSize: Layoutmanager versucht Komponente nicht kleiner als ihre minimale Größe werden zu lassen • maximumSize: Layoutmanager versucht Komponente nicht größer als ihre maximale Größe werden zu lassen • preferredSize: Layoutmanager versucht eine Komponente so groß wie ihre bevorzugte Größe werden zu lassen (solange dieses möglich ist); die preferredSize ergibt sich beispielsweise bei einem JButton aus dem Text des Buttons sowie eventuell vorhandenen Bildern und Rändern Grundlagen Programmierung Stephan Kleuker 688 BorderLayout (Überblick) • BorderLayout ist der Layoutmanager, der als Standard für ein Content-Pane eingestellt ist • BorderLayout besitzt 5 Bereiche, in die jeweils eine Komponente eingetragen werden kann (default ist Center) North West Center East South setLayout( new BorderLayout()); add( new JLabel("North-Label"),BorderLayout.NORTH); Grundlagen Programmierung Stephan Kleuker 689 BoxLayout (Überblick) • BoxLayout kann beliebig viele Komponenten vertikal oder horizontal anordnen. Komp. 1 Komp. 2 Komp. 3 ... Komp. 1 Komp. 2 ... Komp. n Komp. n • Reihenfolge der Komponenten wird durch die Reihenfolge des Einfügens festgelegt • Abstand zwischen den Komponenten ist 0 Pixel groß • BoxLayout berücksichtigt - im Gegensatz zu BorderLayout minimumSize und maximumSize. Grundlagen Programmierung Stephan Kleuker 690 Verschachtelung von Layouts (1/5) • Bisher wurde der Content-Pane immer ein Layoutmanager zugeordnet, in der Content-Pane wurden alle Komponenten angeordnet • Ansatz ist für komplizierte Layouts nicht flexibel genug • Ergänzung: Als Komponenten können auch andere Container eingesetzt werden, die ihrerseits wiederum einen eigenen Layoutmanager besitzen und Komponenten aufnehmen können hierarchische Schachtelung Content-Pane (GridLayout) Komp. 1 Grundlagen Programmierung Komp. 2 Stephan Kleuker JPanel (BoxLayout) in der Ecke 691 Verschachtelung von Layouts (2/5) • JPanel: – Container für andere Komponenten – Keine eigene Darstellung (außer Hintergrund) – Hintergrund kann transparent gesetzt werden – Ein JPanel kann wie eine andere Komponente einem Container hinzugefügt werden – Unterstützt das Schachtel in Schachtel in Schachtel ... – Prinzip – Wichtig: ein JPanel hat ein eigenes Layout! Grundlagen Programmierung Stephan Kleuker 692 Verschachtelung von Layouts (3/5) public class Schachtelung extends JFrame { public Schachtelung(){ setLayout(new GridLayout(2,2)); add(box()); add(grid()); add(border()); add(nulll()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); pack(); } public static void main(String[] args) { new Schachtelung(); } private JPanel box(){ JPanel jp = new JPanel(); jp.setLayout(new BoxLayout(jp, BoxLayout.X_AXIS)); for(int i=0;i<4;i++) jp.add(new JButton("42")); return jp; Grundlagen Programmierung Stephan Kleuker } 693 Verschachtelung von Layouts (4/5) private JPanel grid(){ JPanel jp= new JPanel(new GridLayout(2,2)); for(int i=0;i<4;i++) jp.add(new JButton("43")); return jp; } private JPanel border(){ JPanel jp= new JPanel(new BorderLayout()); jp.add(new JButton("44"),BorderLayout.NORTH); jp.add(new JButton("44")); jp.add(new JButton("44"),BorderLayout.EAST); return jp; } private JPanel nulll(){ JPanel jp= new JPanel(null); for(int i=0;i<3;i++){ JButton jb = new JButton("45"); jb.setBounds(50+20*i, 10*i, 60, 40); jp.add(jb); } return jp; } Grundlagen Programmierung Stephan Kleuker } 694 Verschachtelung von Layouts (5/5) Grundlagen Programmierung Stephan Kleuker 695 GridBagLayout – Übersicht (1/3) • GridBagLayout ist der flexibelste und komplexeste Standardlayoutmanager in Swing • GridBagLayout platziert die Komponenten in einem Raster aus Zeilen und Spalten • Komponenten dürfen sich über mehrere Zeilen und/oder Spalten erstrecken • Zeilen dürfen unterschiedliche Höhen, Spalten unterschiedliche Breiten besitzen • GridBagLayout platziert die Komponenten in rechteckigen Zellen, wobei die bevorzugten Größen (preferredSize) der Komponenten verwendet werden, um die Ausmaße der Zellen zu bestimmen Grundlagen Programmierung Stephan Kleuker 696 GridBagLayout – Übersicht (2/3) • Übersicht: Komp. 1 Komp. 2 Komp. 7 Komp. 3 Komp. 8 Komp. 6 Grundlagen Programmierung Komp. 5 Komp. 4 Stephan Kleuker 697 GridBagLayout – Übersicht (3/3) • Die Komponenten werden durch Angabe einer Zellenposition platziert (z.B. Zelle 2,3) • Der Ursprung (die Zelle 0,0) hängt von der Textausrichtung des Containers ab: – links rechts: Ursprung liegt in linker, oberer Ecke – rechts links: Ursprung liegt in rechter, oberer Ecke • Wird eine Komponente zu einem Container hinzugefügt, so muss ein sogenanntes GridBagConstraints-Objekt mit übergeben werden • Das Constraints-Objekt beschreibt, in welche Zelle die Komponente eingefügt werden soll und welche weiteren Bedingungen gelten sollen Grundlagen Programmierung Stephan Kleuker 698 GridBagLayout – Constraints (1/4) • GridBagConstraints unterstützt die folgenden Attribute: int gridx int gridy Spalten- und Zeilenindex der Zelle, in die die Komponente eingefügt werden soll Besonderheit: GridBagConstraints.RELATIVE Komponente wird direkt auf den folgenden Index der zuletzt eingefügten Komponente gesetzt Default: GridBagConstraints.RELATIVE int gridwidth Anzahl Spalten (gridwidth) oder Zeilen int gridheight (gridheight), die die Komponente belegen soll. Default: 1 int ipadx Anzahl Pixel, die jeweils links und rechts (ipadx) int ipady bzw. oben und unten (ipady) zur Minimumgröße der Komponente hinzugefügt werden Default: 0 Grundlagen Programmierung Stephan Kleuker 699 GridBagLayout – Constraints (2/4) int fill Wenn die Zelle größer als die bevorzugte Größe der Komponente ist, enthält fill das Verhalten der Komponente: GridBagConstraints.NONE: Komponente nicht verändern GridBagConstraints.HORIZONTAL: Komponente horizontal auf Zellenbreite ausdehnen GridBagConstraints.VERTICAL: Komponente vertikal auf Zellenhöhe ausdehnen GridBagConstraints.BOTH: Komponente horizontal und vertikal auf Zellengröße ausdehnen Default: GridBagConstraints.NONE Grundlagen Programmierung Stephan Kleuker 700 GridBagLayout – Constraints (3/4) double Bei Vergrößerung des Containers: Gewichtung zwischen weightx 0.0 und 1.0, anhand derer der neue Platz auf die double Komponenten verteilt werden soll: weighty 0.0 oder fill==NONE: Komponente nicht vergrößern Insets insets > 0.0 und fill!=NONE: Anteil, die die Komponente vom neuen Platz erhalten soll Default: 0 (meist nicht sinnvoll!) Extra Platz, der zwischen den Rändern der Komponente und dem Zellenrand eingefügt wird (leerer Rand um die Komponente) Insets.bottom: Freie Pixel unter der Komponente Insets.top: Freie Pixel über der Komponente Insets.left: Freie Pixel links von der Komponente Insets.right: Freie Pixel rechts von der Komponente Grundlagen Programmierung Stephan Kleuker 701 GridBagLayout – Constraints (4/4) int anchor Position, auf der die Komponente platziert werden soll, wenn sie kleiner als die Zelle ist: GridBagConstraints.NORTH: oben zentriert GridBagConstraints.SOUTH: unten zentriert GridBagConstraints.WEST: links, vertikal zentriert GridBagConstraints.EAST: rechts, vertikal zentriert GridBagConstraints.NORTHWEST: oben links GridBagConstraints.NORTHEAST: oben rechts GridBagConstraints.SOUTHWEST: unten links GridBagConstraints.SOUTHEAST: unten rechts GridBagConstraints.CENTER: mittig zentriert (Default) Weitere (bezogen auf die Textausrichtung des Containers, siehe API-Dokumentation) Grundlagen Programmierung Stephan Kleuker 702 Anmerkungen zu Constraints (1/2) • Einsatz einiger Attribute: gridx/gridy anchor Komponente Insets.bottom Insets.right gridheight Insets.left Insets.top Zelle gridwidth Grundlagen Programmierung Stephan Kleuker 703 Anmerkungen zu Constraints (2/2) • Beispiel (Bildschirmausgabe): • Zellenaufbau: gridwidth = 3 3 gridheight = 2 insets Grundlagen Programmierung Stephan Kleuker 704 Beispiel GridBagLayout (1/3) public class GridBagSpielerei extends JFrame { public GridBagSpielerei(){ GridBagLayout gbl= new GridBagLayout(); setLayout(gbl); for(int i=0;i<5;i++){ addItem(gbl,new JButton(""+i),0,i,1,1); addItem(gbl,new JButton(""+(i+5)),i+1,0,1,1); } addItem(gbl,new JButton("10"),1,1,5,1); addItem(gbl,new JButton("11"),1,2,3,1); addItem(gbl,new JButton("12"),4,2,2,3); addItem(gbl,new JButton("13"),1,3,3,2); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocation(800, 100); setVisible(true); pack(); } Grundlagen Programmierung Stephan Kleuker 705 Beispiel GridBagLayout (2/3) private void addItem(GridBagLayout gbl, Component c, int x,int y, int b, int h) { GridBagConstraints gbc = new GridBagConstraints(); gbc.anchor = GridBagConstraints.CENTER; gbc.gridx = x; gbc.gridy = y; gbc.gridwidth = b; gbc.gridheight = h; gbc.weightx = 1.0; gbc.weighty = 1.0; gbc.ipadx = 0; gbc.ipady = 0; gbc.insets = new Insets(5, 5, 5, 5); gbc.fill = GridBagConstraints.BOTH; gbl.setConstraints(c, gbc); add(c); } Grundlagen Programmierung Stephan Kleuker 706 Beispiel GridBagLayout (3/3) Grundlagen Programmierung Stephan Kleuker 707 GUI-Builder (1/2) • Fazit: manuelle Anwendung des GridBagLayout ist aufwändig • Die Programmierung von Oberflächen in Java ist zwar recht strukturiert möglich, viele Arbeitsschritte wiederholen sich aber monoton • GUI-Builder stellen einen anderen Ansatz zur Entwicklung von Oberflächen vor, Oberflächen werden per Drag-andDrop zusammengestellt • Die weitere Programmierung findet durch den Entwickler „unterhalb“ der GUI-Elemente statt, jeder Code muss einer Komponente zugeordnet werden • GUI-Builder eignen sich damit sehr gut, schnell einen GUIPrototypen zu erstellen, um mit den späteren Nutzern das Look-and-Feel zu diskutieren Grundlagen Programmierung Stephan Kleuker 708 GUI-Builder (2/2) • GUI-Builder haben aber auch Nachteile: – Software häufig nicht GUI-gesteuert entwickelbar, je komplexer das „Innenleben“ ist, desto weniger steht das GUI in der Entwicklung im Mittelpunkt – wer nicht weiß, wie ein GUI funktioniert, programmiert nur Fragmente und kennt das Zusammenspiel nicht – ohne GUI-Builder entwickelte Oberflächen können oft nicht mit dem GUI-Builder weiter bearbeitet werden – GUI-Builder geben die Struktur des Codes vor, d.h. man muss sich auf die Programmiereigenheiten des GUIBuilders zu 100% einlassen • Fazit: Einsatz eines GUI-Builders sollte nur in Betracht gezogen werden, wenn man die Grundsätze zur GUIEntwicklung in der jeweiligen Programmiersprache verstanden hat, danach muss getestet werden, ob man mit dem resultierenden Code leben kann Grundlagen Programmierung Stephan Kleuker 709 Layoutmanager – Live-Update • Seit JDK 1.4 kann für Dialoge ein sogenanntes Live-Update eingeschaltet werden: – Aufruf (Paket java.awt): Toolkit tk = Toolkit.getDefaultToolkit(); tk.setDynamicLayout( true ) – Nutzung: Z. B. im GUI-Konstruktor unmittelbar vor setVisible – bei Größenänderung des Dialogs werden permanent die Komponenten durch die Layout-Manager neu angeordnet – Vorteil: Sofortiger Eindruck der Änderungen – Nachteil: Relativ langsam (gerade bei großen Dialogen mit GridBagLayout) Grundlagen Programmierung Stephan Kleuker 710 Klassen Toolkit und UIManager andiskutiert • Flexible Einstellungsmöglichkeiten, z. B. Bildschirmgröße Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); System.out.println(d); Ausgabe: java.awt.Dimension[width=1024,height=600] • Applikation auf gesamten Bildschirm, statt pack(): setLocation( 0, 0 ); resize( Toolkit.getDefaultToolkit().getScreenSize() ); • Änderung der Font-Größe für alle Label-Objekte (weitere Einstellungen möglich) UIDefaults uiDefaults = UIManager.getDefaults(); uiDefaults.put( "Label.font", ((Font)uiDefaults.get("Label.font")).deriveFont(30f) ); Grundlagen Programmierung Stephan Kleuker 711 Layoutmanager – Platzhalter (1/3) • Problem: Die Buttons sollen rechtsbündig erscheinen, wie kann ein variabler Zwischenraum eingefügt werden? • Ziel: Buttons können ihre bevorzugte Größe einnehmen, Zwischenraum füllt den Rest • Lösung: Die Klasse Box enthält statische Methoden, die verschiedene unsichtbare Komponenten erzeugen können • Andere Lösung: Rechtsbündiges FlowLayout (soll hier nicht besprochen werden). Grundlagen Programmierung Stephan Kleuker 712 Layoutmanager – Platzhalter (2/3) Rigid Area • unsichtbare Komponente der angegebenen Größe • Erzeugung: – Component Box.createRigidArea( Dimension dim ) – dim: Größe. (ist ein Maß = 0 irrelevant) K1 K2 K1 Rigid Area K2 Glue • Komponente, die den restlichen freien Platz aufnimmt • Erzeugung: Component Box.createGlue() K1 K2 Grundlagen Programmierung K1 Stephan Kleuker Glue K2 713 Layoutmanager – Platzhalter (3/3) Custom filler • Erzeugt eine unsichtbare Komponente der mit angegebenen Minimum-, Maximum- und Preferred-Size • Erzeugung: – new Box.Filler( Dimension min, Dimension pref, Dimension max ) – min, pref, max: Entsprechende Größen – Filler ist eine statische innere Klasse (Innerclass) von Box K1 K2 Grundlagen Programmierung K1 Stephan Kleuker Custom K2 Filler 714 Personal Look and Feel (1/4) • In Swing kann man die Gestaltung der Oberfläche relativ leicht als Standard ändern • Individuelle Design-Grundlagen für die Oberflächen sind ebenfalls realisierbar, aber recht aufwändig • Das folgende Programm zeigt einige Einstellmöglichkeiten • Look and Feels können selbst geschrieben werden • Look and Feels stehen nicht auf jedem Betriebssystem zur Verfügung • Default-Look and Feel „Metal“ kann durch „Themes“ noch verändert werden • „Synth“ als leicht über XML konfigurierbares Layout Grundlagen Programmierung Stephan Kleuker 715 Personal Look and Feel (2/4) public class LookAndFeels extends JFrame implements ActionListener { private static final long serialVersionUID = 1L; private String[][] looks = {{"javax.swing.plaf.metal.MetalLookAndFeel", "Metal"}, {"javax.swing.plaf.synth.SynthLookAndFeel","Synth"}, {"com.sun.java.swing.plaf.motif.MotifLookAndFeel","Motif"}, {"com.sun.java.swing.plaf.windows.WindowsLookAndFeel", "Windows"}, {"com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel", "Windows Classic"}, {"com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel", "Nimbus"}, {"javax.swing.plaf.mac.MacLookAndFeel","Mac"}, {"com.sun.java.swing.plaf.gtk.GTKLookAndFeel","GTK"}, {"javax.swing.plaf.multi.MultiLookAndFeel","Multi"}}; private JPanel jp = new JPanel(new GridLayout(names.length, 1)); Grundlagen Programmierung Stephan Kleuker 716 Personal Look and Feel (3/4) public LookAndFeels() { for (int i = 0; i < looks.length; i++) { JButton jb = new JButton(looks[i][1]); jb.addActionListener(this); jp.add(jb); } add(jp); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); pack(); } @Override public void actionPerformed(ActionEvent ev) { for (int i = 0; i < looks.length; i++){ if (looks[i][1].equals(ev.getActionCommand())) { try { UIManager.setLookAndFeel(looks[i][0]); SwingUtilities.updateComponentTreeUI(this); pack(); } catch (Exception e) { System.out.println("L&F nicht anwendbar"); } Grundlagen Stephan Kleuker } Programmierung } 717 Personal Look and Feel (4/4) Grundlagen Programmierung Stephan Kleuker 718 Java – Was fehlt noch einfachere Frage: was ist schon da • fehlt: Rekursion • fehlt: Reflektion, Objekt kennt eigene Variablen und Methoden • fehlt: generische Programmierung, class X<T extends Y> • fehlt: Entwicklung und Nutzung von Annotationen • fehlt: Möglichkeit Bilder zu malen (paint), Java3D • fehlt: Nutzung vieler SW-Pakete, die in Java enthalten sind • fehlt: Nutzung vieler weiterer SW-Pakete und Frameworks (teilweise basierend auf Java-Standards) • fehlt: … Grundlagen Programmierung Stephan Kleuker 719