Software-Krise (2)
Auslöser für Software Engineering
Rasant gestiegener Bedarf an Software
Gestiegener Aufwand der Softwareentwicklung —> Komplexität steigt exponentiell mit Größe
Entwicklerproduktivität nur langsam gestiegen
—-> Lücke zwischen Aufwand Softwareentwicklung und Entwicklerproduktivität
Software-Krise (2): Magisches Dreieck Projektmanagement
Leistung/Qualität
Zeit
Kosten
Software-Krise (2): Definition Software Engineering
Software Engineering beschäftigt sich mit allen Aspekten der Softwareentwicklung, angefangen bei der initialen Konzeption, über Verwendung bis zur Wartung.
3 Bestandteile:
Prozesse
Methoden/Techniken
Tools
Wasserfallmodell (3): 5 Phasen
Innerhalb Software Enginering gehört zu Prozesse:
Requirements Engineering
Design
Implementation
Testing
Maintenance
Wasserfallmodell (3): Vor- und Nachteile
Vorteile:
klare einfache Struktur
es lässt sich (theoretisch) ein (global) optimales Design erarbeiten
Nachteile:
kein Zurückspringen in frühere Phasen möglich
langsam: streng sequentielle Abarbeitung
starr, wenig flexibel
großer Scope —> sehr große Komplexität
Fehler werden ggf. zu spät erkannt —> hohe Bereinigungskosten
keine Reaktion aus sich ändernde Anwendung
Voraussetzungen für eine erfolgreiche Anwendung oft nicht erfüllt
Programmierparadigmen (4)
3 Programmierparadigmen:
Imperative: Typische Programmelemente: Befehle (“Wie“), Fokus/Denkweise: Vorgänge, Typische Kontrollstrukturen: Verzweigungen, Schleifen, Strukturierungselement: Funktionen/Prozeduren
Deklarative: Typische Programmelemente sind Definitionen
(„Was“), Fokus/Denkweise: Ergebnis, Typische Kontrollstruktur: Rekursion, Strukturierungselement: Funktionen, Regeln
Objektorientierte: Ist aus Imperativer Programmierung entstanden, Typische Programmelemente sind Objekte
(beschreiben „Dinge“), Fokus/Denkweise: Dinge, Beziehungen zwischen diesen Strukturierungselemente: Klassen, Geeignete Methode in Design-Phase: UML,
z.B. Klassendiagramm, Eigenschaften: Sehr vielseitig einsetzbar, Weit verbreitet
UML (5): Definition UML
Unified Modelling Language: grafische Modellierungssprache von Software-Systemen für Spezifikationen, Dokumentation und Visualisierung
UML (5): Definition Klassendiagramm
Statische Beschreibung von Domänenkonzepten und ihrer
Beziehungen
UML-Vererbung (6): Polymorphismus
Vielgestaltigkeit
setzt “is-a”-Relation um
polymorph:
Klasse kann unterschiedliche Typen haben
überschriebene Methoden
UML-Vererbung (6): Vorteile von Vererbung
Wiederverwendung
Wartbarkeit verbessert
Übersichtlichkeit
reduzierter Schreibaufwand
Vermeidung von Code-Duplizierung
Vermeidung von Copy & Paste-Fehlern
keine mehrfachen Anpassungen, nur Änderungen an der zentralen Methode
einfachere Testbarkeit
schnellerer Compile-Vorgang
kosteneffiziente Softwarenentwicklung
Mehrfachvererbung (7)
Diamond Problem
Klasse D stammt über zwei verschiedene Vererbungspfade (C, B) von der Basisklasse A ab
Problem: wenn in B eine Methode auftaucht, diese aber auch in C und A auftaucht, dann weiß man nicht, welche der Methoden an D vererbt werden soll
Einführung Java (9): 3 Bestandteile von Java
Programmiersprache
Java Virtual Machine (JVM): Laufzeitumgebung
Java Class Library (Standardbibliothek)
Einführung Java (9): Vom Programmcode zur Ausführung
Schritte:
Softwareentwickler schreibt Quellcode (.java-Dateien)
Entwickler „übersetzt“ den Quellcode mittels Java-Compiler javac in Bytecode (.class-Dateien)
Der Bytecode wird auf der Zielplattform mittels „java“-Aufruf durch die JVM ausgeführt („just-in-time compilation“)
Beispiel
Übersetzung des Codes in Java
Befehl: javac HelloHSBund.java
Ausführung des Codes
Befehl: java HelloHSBund
Einführung Java (9): Beschreiben Sie, was Bytecode ist. Welche vorteilhafte Eigenschaft von Java wird durch ihn ermöglicht?
Der Java-Compiler übersetzt den Quellcode (*.java-Dateien) in einen plattformunabhängigen Zwischencode, der Bytecode genannt wird (*.class-Dateien). Dieser wird durch die Java Virtual Machine (JVM) auf der Zielplattform ausgeführt. Dadurch ist ein Programm ohne erneute Übersetzung auf allen Plattformen direkt lauffähig („write once, run anywhere“).
Durch den Bytecode wird Plattformunabhängigkeit erreicht.
Einführung Java (9): Bei der Java-Programmierung können Fehler sowohl vor als auch während der Programm-ausführung auftreten. Nennen Sie die für diese Fehlerklassen benutzen Fachbegriffe
Fehler vor Ausführung:
Compilefehler (Syntaxfehler, Typfehler)
Java ist statisch typisiert: Typ der Variable kann sich nicht ändern, Typfehler werden vor Ausführung erkannt
Fehler während Ausführung:
Laufzeitfehler (Arbeitsspeicher nicht ausreichend, IO-Fehler, NullPointerException)
Grundbegriffe (11): Java
Anweisung
Bezeichner (identifier): Name für Variable, Methode, Klasse
Schlüsselwort (key word)
Modifizierer (modifier): sind auch Schlüsselwörter, z.B. Sichtbarkeitsmodifizierer (public, private) oder static
Grundbegriffe (11): Von Programmausführung zur Anweisung
Ziel: Ausgeführt werden soll die Anweisung: System.out.println("Hello world!");
Anweisungen können in Java nicht einfach in eine Datei geschrieben und dem Compiler übergeben werden. Sie müssen in den passenden Rahmen eingebettet werden:
Anweisungen stehen in Methoden
Methoden stehen in Klassen.
Eine Klasse steht in einer Datei mit demselben Namen und der Endung „.java“. *
Der Compiler übersetzt komplette Klassendatei (compilation unit).
Die Klassendeklaration erfolgt mit dem Schlüsselwort class und innerhalb geschweifter Klammern.
Grundbegriffe (11): main-Methode
Jedes ausführbare Java-Programm muss besitzen:
Eine main-Methode mit folgender Signatur:
public static void main(String[] args)
Eine solche main-Methode innerhalb einer Klasse mit Modifizierer public. Man nennt eine solche Klasse umgangssprachlich auch „Main-Klasse“.
• Magic: Bei Aufruf der JVM mittels „java <CLASS_NAME>“ führt die Laufzeitumgebung die Anweisungen innerhalb der main-Methode aus.
Grundbegriffe (11): Java Syntax
Strukturierung durch {}
Code-Einrückung optional
fast jede Anweisung mit ; beenden
Ausnahme: Schleifen, Verzweigungen, Klassen, Methoden
Anweisungen über mehrere Zeilen sind möglich
Punktoperator . verbindet Klassen und Objekte mit dazugehörigen Methoden
String (Zeichenkette) mit “ “
Java unterscheidet Groß/Kleinschreibung
Variablen, Parameter, Methoden werden mit Datentyp deklariert (void falls keiner)
Java macht Speicherverwaltung selbst, Speicherplatz von nicht benötigten Variablen wird automatisch freigegeben
Grundbegriffe (11): Konventionen für Bezeichner
Klassen, Enums, Interfaces: erster Buchstabe groß
Methoden, Variablen, Parameter: erster Buchstabe klein, CamelCase
Konstante: GROSSBUCHSTABEN
Variablen (12): 2 Datentypen
Primitive Datentypen: für Zahlen, Wahrheitswerte und
(Unicode-) Zeichen
Referenztypen: für Verweise auf Objekte, d.h. die referenzierten Objekte sind in Klassen implementiert. Bsp.: Strings, Arrays, Listen oder Fahrräder
Variablen (12): 8 primitive Datentypen
Variablen (12): Anweisungsblöcke
Ein Anweisungsblock fasst eine Menge von Anweisungen zusammen, die hintereinander ausgeführt werden.
Ein Block ist selbst eine Anweisung, die in
geschweiften Klammern eine Folge von Anweisungen zu einer neuen zusammenfasst.
Ein Block kann überall dort verwendet werden, wo auch eine einzelne Anweisung stehen kann.
Blöcke können geschachtelt werden.
Variablen (12): Literal
Ein Literal (literal) ist ein konstanter Ausdruck.
Beispiele:
Wahrheitswerte: true, false
Ganzzahlen (int), z.B.: 42
Gleitkommazahlen (float, double), z.B.: 3.14159
Einzelne Unicode-Zeichen (char), z.B.: 'X' in einfachen Hochkomma
Zeichenketten (String), z.B.: "Dauert's noch lange?"
Nullreferenz, d.h. eine Variable von einem Referenztyp verweist auf kein Objekt: null
Definition
Operator
Schlüsselwort
Ausdruck
Literal
Referenztyp
Bezeichner
Modifizierer
Anweisung: z.B. Methodenaufrufe, Zuweisungen, Kontrollstrukturen, Anweisungsblöcke, leere Anweisung
Operator: verknüpft einen oder mehrere sogenannter Operanden und liefert bei Auswertung einen Wert (8 Arten von Operatoren: 1. Arithemetisch, 2. Zuweisungs, 3. Relationale/Gleichheits, 4. Unäres Plus/Minus, 5. Logische, 6. String-Konkatenation, 7. Inkrement/Dekrement, 8. Verbundoperator)
Schlüsselwort: class, static, void etc.
Ausdruck: ergibt bei der Auswertung zur Laufzeit ein Ergebnis, hat bestimmten Typ
Literal: konstanter Ausdruck, Beispiele: Wahrheitswerte/boolean (true, false), Ganzzahlen (int), Gleitkommazahlen (double), Einzelne Unicode-Zeichen (char), Zeichenketten (String), Nullreferenz (null)
Referenztyp: Verweise auf Objekte, d.h. die referenzierten Objekte sind in Klassen implementiert, Beispiele: Strings, Arrays, Listen
Bezeichner (identifier): für Benennung und Referenzierung von Variablen, Methoden und Klassen
Modifizierer (sind auch Schlüsselwörter): für Klassen, Methoden und Attribute angegeben, bspw. um die Nutzung einzuschränken (Sichtbarkeitsmodifizierer)
Ordnen Sie die Fachbegriffe Anweisung, Operator, Schlüsselwort, Ausdruck, Literal und Referenztyp den folgenden Beispielen zu:
Primitive Datentypen vs. Referenztypen
int i1 = 42;
int i2 = i1;
i1 = 23
System.out.println(i2)
—> Ausgabe: 42
—> Kopiert wird der Wert von i1 (Call-by-value)
i1 wird zunächst der Wert von 42 zugewiesen
i2 wird der Wert von i1 = 42 zugewiesen
DANACH wird i1 auf 23 geändert, aber i2 = 42 bleibt so
———————————————————————————-
int[] a1 = new int[] {42};
int[] a2 = a1;
a1[0] = 23;
System.out.println(Arrays.toString(a2));
—> Ausgabe: 23
—> Referenz auf das Array a1 wird erstellt (Call-by-reference)
Array a1 wird mit dem Wert 42 angelegt
Array a2 wird mit einer Referenz auf a1 angelegt
Array a1 wird von 42 auf 23 geändert
Da Array a2 auf a1 verweist, ändert sich dadurch auf Array a2
————————————————————————————-
Point p1 = new Point(42, 42);
Point p2 = p1;
p1.x = 23;
p2.x = 23;
System.out.println(p2.createRepresentation());
—> Ausgabe: (23, 23)
—> Referenz auf das Point-Objekt p1 wird erstellt
Point-Objekt p1 wird mit (42, 42) erstellt
Point-Objekt p2 wird mit Referenz auf p1 erstellt
Point-Objekt p1 wird mit neuen Werten (23, 23) belegt
Da p2 auf p1 verweist, ändert sich dadurch auch p2
Gegeben Sie für die folgenden Anweisungsblöcke jeweils an, was ausgegeben wird.
Ausdrücke, Operatoren (13): Operatorpräzedenz
Bei komplexen Ausdrücken mit mehr als einem Operator ist die Anwendungsreihenfolge von Bedeutung.
Die Anwendungsreihenfolge wird bestimmt durch
Operatorrangfolge („operator precedence“): Jeder Operator hat einen bestimmten Rang. Operatoren mit niedrigerem Rang werden zuerst angewendet.
Beispiel: 3 + 3 * 13 == 42
Assoziativität: Operatoren mit demselben Rang sind meist „linkassoziativ“, d.h. sie werden von links nach rechts angewendet. Beispiel: 56 / 4 * 3 == 42
Klammerung: Teilausdrücke in runden Klammern haben bei
Anwendung Vorrang.
Ausdrücke, Operatoren (13): Typecasting
Manchmal ist es erforderlich, dass Datentypen konvertiert werden müssen. Der Fachbegriff hierfür ist Typumwandlung („casting“).
Zwei Arten:
Automatische (implizite) Typumwandlung: Daten eines kleineren Typs können automatisch in einen größeren Typ umgewandelt werden („widening conversion“).
Explizite Typumwandlung: Größerer Typ kann in kleineren Typ konvertiert werden, ggf. mit Informationsverlust. Der Ergebnistyp wird in runden Klammern vorangestellt.
Ausdrücke, Operatoren (13): 8 Arten von Operatoren
Arithmetische Operatoren: z.B. +, -, *, /, %
Zuweisungsoperator: =
Relationale Operatoren bzw. Gleichheitsoperator: <, >, <=, >=, ==, !=
Unäres Plus/Minus: +, -
Logische Operatoren, z.B. !, &&, ||, &, |, ^
String-Konkatenation: +
Inkrement/Dekrement: ++, --
Verbundoperator: +=, -=, *=, /=
Arrays (16): Hilfsmethoden
Klassen (17): Method-Chaining Beispiel
Methode muss jetzt heißen wie Klasse (Wizard), return-Anweisung notwendig, weil Rückgabetyp Wizard ist (nicht void)
Aufruf über:
Konstruktoren (18): Constructor-Chaining
Konstruktoren (18): Aufruf Konstruktor der Oberklasse mit super (Konstruktoren werden nicht vererbt)
Oberklasse
Unterklasse
Konstruktoren vs. Methoden
Gemeinsamkeiten
Besitzen Programmcode/Anweisungen
Haben eine Parameterliste (ggf. leer)
Können auf Objektvariablen zugreifen
Können die this-Referenz verwenden
Werden im UML-Klassendiagramm im „Methoden-Unterkästchen“ dargestellt
Unterschiede
Rückgabetyp:
Methoden: muss angegeben werden (ggf. void)
Konstruktoren: kein Rückgabetyp
Name:
Methoden: frei wählbarer Name; per Konvention ein Verb/Imperativ und klein geschrieben
Konstruktoren: Name ist der Klassenname; daher per Konvention ein Nomen und groß geschrieben
Aufruf:
Methoden: beliebig oft; vom Benutzer aufgerufen
Konstruktoren: genau einmal pro Objekt; implizit durch JVM bei Erzeugung eines Objektes via new
Rekursion (18b): Fibonacci
Rekursion (18 b): Fakultät
Implementieren Sie die Methode void fizzBuzz(int n), welche die Zahlen von 1 bis 𝑛 zeilenweise auf der Standardausgabe ausgibt.
Es gelten folgende Besonderheiten:
Wenn die Zahl ein Vielfaches von 3 ist, soll anstelle der Zahl das Wort „Fizz“ ausgegeben werden.
Wenn die Zahl ein Vielfaches von 5 ist, soll anstelle der Zahl das Wort „Buzz“ ausgegeben werden.
Wenn die Zahl sowohl ein Vielfaches von 3 als auch ein Vielfaches von 5 ist, soll anstelle der Zahl das Wort „FizzBuzz“ ausgegeben werden.
Methode überschreiben (21): @Override
Überschreiben der toString()-Methode aus Object
Methode teilweise überschreiben (21): super
Kopieren / Klonen von Arrays
Sichtbarkeiten (25): Übersicht
Reihenfolge nach abnehmender Sichtbarkeit:
public
protected
package-private
private
paketsicher = package-private
getter und setter Methoden
Werden allgemein als Zugriffsmethoden bezeichnet
Methoden mit Schreib‐ bzw. Lesezugriff für private Attribute
Festes Namensschema nach JavaBeans‐Konvention
getAttribute(); ‐> Getter Methoden
setAttribute(); ‐> Setter Methoden
Ausnahme bei boolean. Hier sieht man auch oft isAttribute(); anstatt getAttribute();
Methoden schützen
Methoden als Klassenmethode deklarieren
Enums (27): Implementierung von Aufzähltypen
(Sonst Behelfsimplementierung von Aufzähltypen über int-Konstanten)
Typsicherheit
Nutzung bei Vergleichen und switch/case
Enums als interne Klasse
Jede Konstante (GROSSBUCHSTABEN) als einziges Objekt
Enums sind Referenztypen
Exceptions (28): Vererbungshierarchie
Exceptions sind Fehler, die während der Laufzeit bei der Ausführung auftreten.
Exception: Checked Exception —> muss behandelt werden: fangen oder weiterwerfen
RunTimeException: Unchecked Exception —> kann behandelt werden
Error: harter Fehler —> keine Behandlung möglich
Throwable: direkte Ableitung unüblich
Exceptions (28): Eigene Exception auslösen
Exceptions (28): Eigene Exception-Klasse schreiben
Schreiben Sie zur Umsetzung der Exception-Klasse de.hsbund.dacs.DrinkException den Inhalt der Datei „DrinkException.java“.
Setzen Sie die Klasse als eine Unterklasse von java.io.IOException um. Implementieren Sie die für Exception-Klassen üblichen vier Konstruktoren, um bei der Objekterzeugung gegebenenfalls eine Fehlerbeschreibung und/oder einen Grund für die Exception angeben zu können.
Immer gleiche Vorgehensweise mit String(message), Throwable(cause) und vier Konstruktoren (1. Standardkonstruktor, 2. K mit String message, 3. K mit String message und Throwable cause, 4. K mit Throwable cause).
Die Klassen Exception, RuntimeException, Error und Throwable stehen zueinander in
gewissen Vererbungsbeziehungen.
Geben Sie für die catch-Klausel jeweils an, von welchen der vier angegebenen Typen/Klassen diese Objekte fangen würde.
Eigene Exception und niedrigere Exceptions werden gecatcht
Exceptions (28):
Die Methode String drink(double amountInLiters) soll die DrinkException genau dann werfen, wenn der Parameter amountInLiters größer als 0.5 ist. Andernfalls soll sie ein Trinkgeräusch Ihrer Wahl zurückgeben.
Die zugehörige Klasse liegt in einem anderen package als die Exception-Klasse. Gehen Sie davon aus, dass die Methode in einer .java-Datei steht, die keine imports enthält.
Implementieren Sie die Methode und verwenden Sie eine aussagekräftige Fehlerbeschreibung für das Exception-Objekt.
Geben Sie jeweils die Laufzeit-Komplexitätsklasse der folgenden Methodenimplementierungen
für den Worst Case an. Die Anzahl der gespeicherten Elemente sei 𝑛.
Collections - Lists (29): Hierarchie
Collections - Lists (29): Listen erzeugen
Collections - Lists (29): Wichtige Operationen (generische Liste List<E>)
Collections - Lists (29): Iterator
Collections - Sets (30): Hierarchie
Keine Duplikate
Typ der Elemente: Object
Collections - Sets (30): Sets erzeugen
8 Wrapper-Klassen
Wrapper-Klasse (großgeschrieben!) - primitiver Datentyp
Byte - byte
Short - short
Integer - int
Long - long
Double - double
Float - float
Boolean - boolean
Character - char
Wrapper-Klassen dienen zur Speicherung von primitiven Datentypen in einer Liste. Weil in Listen nur Referenztypen gespeichert werden können und keine primitiven Datentypen, nimmt man Wrapper-Klassen.
Wrapper-Klassen: UML
Name lautet wie primitiver Datentyp, der verwendet wird
unter Attribut: value : int
Die Datenstrukturen aus dem Collections-Framework können keine primitiven Datentypen als Elemente direkt speichern.
Erläutern Sie, wie diese Einschränkung umgangen werden kann. Mit welchen Nachteilen ist dieser Workaround verbunden, verglichen mit einer (fiktiven) direkten Speichermöglichkeit?
Durch Verwendung von sogenannten Wrapper-Klassen, die jeweils ein Attribut vom Typ eines
primitiven Datentyps besitzen, lässt sich die Einschränkung umgehen.
- Erhöhter Speicherbedarf
- Laufzeit-Overhead
Implementieren Sie die Klasse UndoHistory.
Gehen Sie davon aus, dass die entsprechende Sourcecode-Datei „UndoHistory.java“ keine
imports enthält.
Substitutionsprinzip von Livkov
Überall, wo Typ der Oberklasse zulässig ist, ist auch Typ der Unterklasse erlaubt.
Last changed6 months ago