| home | suche | kontakt/johner | institut | hinweise studierende | tech-docs | blog | mindmailer |
![]() |
Übung X: Die Monsteraufgabe (optional)
Um alle im Unterricht behandelten Techniken (und noch ein bisschen mehr) zu üben, habe ich Ihnen eine Monsteraufgabe gestellt. Sie ist freiwillig.
Übung 21: Java Collection Framework (eine absolute Muss-Aufgabe :-))
Gegen sei das unten gezeigte Klassendiagramm. Setzen Sie dieses in Java-Code um.
- Fügen Sie auf den Klassen Bank und Kunde die notwendigen Methoden ein.
- Achten Sie darauf, dass eine Assoziation nur einseitig navigierbar ist.
- Setzen Sie eine 1:n Beziehung mit einer Klasse, die das Interface Collection (oder ein davon erbendes Interface) implementiert, die andere Beziehung mit einer Klasse, die das Interface Map (oder ein davon erbendes Interface) implementieren.
- Testen Sie mit der Start-Klasse ihren Code und geben Sie nach jeder Einfüge- oder Lösch-Operation alle Kunden mit deren Konten aus.
Übung 20: HashSet (schwierig)
Implementieren Sie Ihr eigenes HashSet, das nur Instanzen der Auto aufnehmen kann. Gehen Sie wie folgt vor:
- Implementieren Sie die Klasse Auto.
- Ein Auto hat die zwei Attribute Typ und Farbe (beides Strings)
- Implementieren Sie die equals Methode
- Implementieren Sie die hashCode Methode, die int Werte zwischen 0 und 4 zurückliefert. Tipp: Berechnen Sie den Hashcode aus "typ + farbe".
- Implementieren Sie die Klasse MySimpleHashSet.
- Die Klasse speichert die Autos in einem Array der Größe 5 und hat eine Sondierungsfunktion. Falls mehr als 5 Autos gespeichert werden sollen, wirft die Klasse einen Fehler.
- Die Klasse verfügt über eine Methode public void add(Auto a)
- Die Klasse verfügt über eine Methode public boolean contains(Auto a)
- Die Klasse verfügt über eine Methode public int size()
- Führen Sie die beiden Testklassen aus. Ihre Implementierung ist erfolgreich, wenn die beiden Tests fehlerfrei laufen.
Hinweis: Die Berechnung der HashCode-Funktion ist hier besonders schwierig, weil der Bereich, auf den die Werte abgebildet werden, nur die Werte 0...4 umfasst. Daher muss die HashCode-Funktion auf die gegebenen Werte "getuned" werden. Beachten Sie deshalb den Tipp oben.
Übung 19: Linked List, Stack, Queue
- Modelliere die Klassen LinkedList mit Hilfe eines UML Klassendiagramms. Beschreiben Sie die wichtigsten Methoden (add, remove, size, contains) kurz (Übergabeparameter, Rückgabewerte, Funktionsweise) und zeichnen Sie auch Beispiele für die Instanzsicht:
- Drei Elemente hinzufügen
- Ein Element löschen
- Wie kann ein Stack und eine Queue implementiert werden? Zeichne hier für jeden Datentyp und jede Implementierung die UML und die Instanzsicht.
Die Lösung dieser Aufgabe sollten Sie am Overheadprojektor skizzieren können (Unterlagen dürfen genutzt werden).
Übung 18: ArrayList (Optional)
Implementieren Sie eine Klasse MeineArrayList. Diese verfügt intern über ein (möglichst kleines) Array Object[] und über nach außen sichtbare Methoden
- void add(Object o)
- void add(Object o, int position)
- void delete(Object o)
- void delete(int pos)
- printList (gibt alle Objekte aus)
Wir nehmen an, dass die Objekte über eine geeignete equals() und toString() Methoden verfügen.
Schreiben Sie eine zweite Klasse, welche Objekte (z.B. Strings) hinzufügt und löscht und dazwischen die printList() Methode aufruft.
Übung 17: Reguläre Ausdrücke 2
Verschiedene Aufgaben
- Kopieren Sie sich den Gesetzestext zu Medizinprodukten auf Ihre Festplatte.
- Finden Sie in diesem Text alle Worte, die mit einen Kleinbuchstaben beginnen.
- Ersetze Sie in diesem Text alle runden Klammern, die eine Zahl umgeben (z.B. "(2)") durch die entsprechenden eckigen Klammern.
- Nach was sucht (wahrscheinlich) dieser Ausdruck: "\d\d\.\d\d\.\d\d\d\d"?
- Wie würden Sie nach einem Euro-Betrag suchen und den in eine Variable einlesen?
- Was findet dieser Ausdruck: "Re\s*(\[\d+\])?:" Wo kommt er wahrscheinlich zum Einsatz?
- Erfinden Sie eine eigene Aufgabe.
Übung 16: Reguläre Ausdrücke
Schreiben Sie eine Klasse EmailCheck, die eine Methode public static boolean checkValidAdress(String email) hat. Diese Methode soll Strings darauf hin prüfen, ob sie gültige E-Mailadressen representieren.
Eine E-Mailadresse sei gültig falls
- Ein @-Zeichen enthalten ist
- Davor ein oder mehrere Zeichen stehen
- Das erste Zeichen nach dem @ muss ein Buchstabe sein
- Bindestriche sind erlaubt
- Subdomains sind erlaubt
- Es sind folgende Topleveldomains gültig: de, info, org, com, net
Schreiben Sie eine (JUnit-) Testklasse, die folgende Checks erfolgreich durchführt:
assertTrue(EmailCheck.checkValidAdress("mail@test.org"));
assertTrue(EmailCheck.checkValidAdress("super-student@inf.htwg-konstanz.de"));
assertTrue(EmailCheck.checkValidAdress("rookie@inf-sem1.htwg-konstanz.de"));
assertTrue(EmailCheck.checkValidAdress("4939@j999.gmx.de"));
assertTrue(EmailCheck.checkValidAdress("1@a.net"));
assertFalse(EmailCheck.checkValidAdress("mail@5aaa.org"));
assertFalse(EmailCheck.checkValidAdress("mail.5aaa.org"));
assertFalse(EmailCheck.checkValidAdress("mail@aaaa"));
assertFalse(EmailCheck.checkValidAdress("mail@aaaa.ger"));
=== Termin ===
Abgabe bis Aufgabe 15 zu Beginn(!) der ersten Übungsstunde im neuen Jahr.
Übung 15: Threads 2 (schwierig)
Das klassische Problem, an dem Multithreading erklärt wird, ist das der Philosophen, die um einen Tisch sitzen und entweder essen oder denken. Zum Essen benötigt jeder Philosoph zwei Gabeln (sic!), eine linke und eine rechte. Insgesamt liegt zwischen zwei Philosophen jedoch immer nur eine Gabel.
Würde jetzt jeder Philosoph beispielsweise seine linke Gabel greifen und warten bis die rechte frei würde, so hätten wir eine Deadlock-Situation geschaffen. Schreiben Sie eine Klasse Philosoph, die klüger ist.
Lösungshinweise
- Legen Sie ein neues Package an (z.B. threads)
- Legen Sie eine Klasse Philosoph an, die über drei Attribute verfügt, welche bereits im Konstruktor gesetzt werden:
- Nummer des Philosophen
- Seine rechte Gabel (den Code für die Gabel finden Sie weiter unten)
- Seine linke Gabel
- Philosoph erbt von Thread und überschreibt die run-Methode
- In der main-Methode
- werden 5 Gabeln und
- 5 Philosophen angelegt und dabei die rechte und linke Gabel zugewiesen. (Damit ist nur festgelegt welche Gabeln rechts und links eine Philosophen liegen, er hat noch keine gegriffen)
- sowie jeder Philosoph gestartet
- ein gestarteter Philosoph
- möchte 5 Gänge essen.
- Er beginnt vor jedem Gang mit Nachdenken (0 - 1 sec)
- Dann wartet er, bis die linke Gabel frei ist.
- Sobald dies der Fall ist versucht er die rechte Gabel zu greifen.
- Gelingt ihm das beginnt er zu essen (0 - 1 sec)
- Danach legt er die rechte Gabel wieder hin.
- In jedem Fall, d.h. ober er die rechte Gabel bekam und aß oder nicht, legt er dann die linke Gabel wieder hin.
- Falls er mit dem Gang fertig ist, beginnt er mit dem nächsten bzw. beendet nach dem 5. Gang das Menü
- Nutzen die folgende Klasse Gabel
package threads;
public class Gabel {
protected int nummer;
protected boolean liegtAufTisch = true;
public Gabel(int nummer) {
this.nummer = nummer;
}
protected synchronized boolean greifeGabel(Philosoph philosoph) {
if (liegtAufTisch) {
System.out.println(philosoph + " greift erfolgreich nach Gabel "+ nummer);
liegtAufTisch = false;
return true;
} else {
System.err.println(philosoph + " greift erfolglos nach Gabel" + nummer);
return false;
}
}
protected synchronized void legeGabelHin(Philosoph philosoph) {
System.out.println(philosoph + " legt Gabel " + nummer + " hin");
liegtAufTisch = true;
notify();
}
protected synchronized void warteAuf(Philosoph philosoph) {
while (!greifeGabel(philosoph)) {
try {
wait();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Übung 14: Threads 1
Schreiben Sie eine sehr einfache Klasse AusgabeThread, die von Thread erbt und in ihrem Konstruktor den Text übergeben bekommt, den sie ausgeben soll. Sie gibt den Text 10 mal aus, wartet dazwischen aber für eine zufällig lange (max. 1 Sekunde) Zeitspanne. In einer Startklasse werden drei dieser AusgabeThreads mit verschiedenen Texten aufgerufen.
Erst nachdem alle AusgabeThreads zu Ende gelaufen sind, soll das sich die Startklasse mit einer Meldung "habe fertig" beenden.
Übung 13: JavaDocs
Schreibe die JavaDocs für eine Funktion, welche Zufallswerte zwischen einer unteren Grenze a und einer oberen Grenze b mit einer Auflösung (d.h. dem kleinst möglichen Abstand zweier ungleicher Zufallszahlen) von c zurückliefert.
Beispiel: a=15, b=40, c=5. Die Funktion würde zufällig eine der folgenden Zahlen zurückliefern: 15, 20, 25, 30, 35, 40.
public double getZufallszahl(double a, double b, double c) throws IllegalArgumentException
Dabei kann ein Fehlergeworfen werden, wenn b <= a ist.
Erzeuge die JavaDocs (HTML-Dateien) mit Eclipse.
Achtung: Es geht nicht darum, dass Sie den Code implementieren! Sie brauchen nur die JavaDoc zu schreiben.
Übung 12: Annotations
Sie haben die Aufgabe gestellt bekommen, einen einfachen Codegenerator zu schreiben, mit dem SQL-Statements erstellt werden können. Dabei sollen die Attribute/Klassenvariablen von Klassen ausgelesen werden und anhand derer Annotations CREATE-TABLE-Ausdrücke mit Namen der Tabellenspalte und dem Datentyp (wie in Annotations festgelegt) geschrieben werden.
Z.B. so für eine Klasse Auto, welche die Attribute Fahrgestellnummer (int) und Typ (String) hat folgendes SQL-Statement erzeugt werden:
CREATE TABLE auto (fahrgestellnr INTEGER, type VARCHAR (64));
Beachten Sie, dass der Spaltenname nicht dem Attributnamen entsprechen muss, sondern über die Annotation bestimmt wird.
Gehen Sie wie folgt vor
- Legen Sie ein neues Package (optional auch ein neues Eclipse-Projekt) an.
- Schreiben Sie eine Annotation SqlAttribute mit den Methoden
- public boolean persist() und
- public SQLType sqlType(), wobei SQLType eine Enum mit den Werten INTEGER, FLOAT und VARCHAR sei.
- Wählen Sie für diese Annotation ein geeignetes @Target und eine geeignete @Retention
- Schreiben Sie eine einfache Klasse. Sie können hier das strapazierte Beispiel Studentin mit den Attributen namen:String und matrikelnummer:int wählen. Fügen Sie den Attributen die Annotations hinzu und wählen Sie einen geeigneten sqlType. Beide Attribute sollen persistiert werden, weshalb persist() true liefern sollte (dies kann auch als default auf der Annotation bereits definiert sein).
- Schreiben Sie eine Klasse Codegenerator, welche
- die Attribute (getDeclaredFields()) ausliest,
- von jedem Attribut die Annotation SQLType holt,
- von jedem Attribut dessen Namen ausliest und zum Schluss
- ein SQL-Statement erzeugt wie oben beschrieben.
Übung 11: Reflection
Sie wollen eine (sehr einfache) verteilte Anwendung schreiben. Dazu würde von einem Computer eine Textdatei zu einem zweiten übertragen. Wir gehen davon aus, dass diese Textdatei bereits beim zweiten Computer (Ihrem) angekommen sei und dass diese Datei folgende Informationen enthält:
- Namen eine zu instanzierenden Klasse
- Argumente für den Konstruktor
- Name einer zu instanzierenden Methode
- Wert, der an die zu instanzierende Methode übergeben werden soll
Der Einfachheit nehmen wir weiterhin an, dass sowohl Konstruktor als auch die genannte Methode nur ein Argument vom Typ String akzeptieren.
Tipps zum Vorgehen
- Legen Sie ein neues Projekt an (de.htwg.progstruct.reflection.SimpleRpc)
- Erstellen Sie in dem Package z.B. eine Klasse Mensch, die nur das Attribut Name (String) haben solle. Erzeugen Sie Konstruktor sowie getter und setter.
- Erstellen Sie in dem Package beispielsweise eine zweite Klasse, die ein gleiches Interface wie die Klasse Mensch implementiert. Erzeugen Sie auch hier den Konstruktor sowie getter und setter.
- Erstellen Sie nun eine Klasse RPC, welche aus einer Datei die obengenannten vier Strings einliest.
- Weiter soll RPC die in der Datei angegebene Klasse (z.B. Mensch) instanzieren und die (einzige) setter-Methode mit dem ebenfalls gegebenen Wert aufrufen.
- Stellen Sie sicher, dass der Wert richtig gesetzt wurde, in dem die aufgerufene Methode den neuen Wert über System.out ausgibt.
=== Termin ===
Übungen bis Nummer 10 bitte vor(!) Beginn der Übungsstunden am 5.12. bzw. 9.12. fertigstellen.
Update: In einer vorherigen Version lag der Termin eine Woche früher. Aber ich möchte ja großzügig sein :-).
Übung 10: Arbeiten mit Loggern
Beantworten Sie folgende Fragen:
- Wie viele Logger kann eine Klasse/Methode nutzen?
- Wie viele Handler kann ein Logger haben?
- Wie viele Formatter kann ein Handler haben?
Schreiben Sie einen eigenen Formatter, der die Nachrichten in folgendem Format ausgibt:
TIMESTAMP (Format yyyyMMdd:hh:mm:ss),KLASSE,METHODE,NACHRICHT,LOGLEVEL
Probieren Sie diesen Formatter aus und stellen Sie sicher, dass die Ausgaben nach mehrmaligem Starten des Programms nicht in der Ausgabedatei gelöscht sind.
Optional: Nutzen Sie einen andneren Handler als einen File- oder Console-Handler.
Übung 9: Fehlerbehandlung und Assertions
Teilaufgabe a)
Gegeben sei eine Klasse PoorMansDb, mit der man Strings unter einem Schlüssel in eine Datei schreiben und von dort wieder lesen kann. Sie stellt somit eine äußerst primitive Datenbank dar. Leider ist die PoorMansDb auch etwas fehlerträchtig, nicht jeder Zugriff ist erfolgreich.
Schreiben Sie eine Klasse, die den Zugriff auf diese Datenbank kapselt. Sie soll folglich auch Methoden wie speichern, laden, ändern enthalten. Diese Methoden sollen mit der Unzuverlässigkeit der PoorMansDb umgehen können, in dem sie es bei Fehler mehrere Versuche unternehmen, bevor sie selbst einen Fehler werfen.
Sie müssen die Klassen PoorMansDb nicht verstehen oder gar ändern, sondern nur benutzen.
Teilaufgabe b)
Schreiben Sie eine eigene Methode public int halbiere(int zahl), die einen Fehler wirft, wenn eine ungerade Zahl übergeben wird. Welche Exception schlagen Sie vor? Schreiben Sie ein Programm bzw. eine main-Methode, mit dem Sie diese Methode aufrufen und prüfen können.
Teilaufgabe c)
Ersetzen Sie in der Lösung zu Teilaufgabe b) den try-catch-Block durch ein assert-Statement.
Teilaufgabe d)
Versehen Sie Ihren Code mit Debugausgaben (Logger). Schreiben Sie Übergabeparameter sowie auftretende Fehler in eine Datei (z.B. XML) bzw. auf die Konsole.
Übung 8: Enums
Sie wollen nicht nur den Treibstoffverbrauch der Fahrzeuge erfassen, sondern auch die Treibstoffsorte (Super, Normal, Diesel). Daher möchten Sie die Fahrzeuge mit der jeweiligen Treibstoffsorte initialisieren und sicher sein, dass nur eine der drei Treibstoffsorten wählbar ist.
Implementieren Sie dies als interne oder externe Enum und überlegen Sie wie die jeweilig andere Implementierungsweise aussehen würde.
Sie möchte, dass über Enums iterieren und dabei die verschiedenen Treibstoffsorten ausgeben. Allerdings soll die Ausgabe anders lauten (High-Power-Super, Turbo-Normal und Renn-Diesel). Ändern Sie die Enum entsprechen.
Schließlich wollen Sie auch die Oktanzahl (100, 98 und 95) ausgeben. Welche Möglichkeit haben Sie dazu?
Übung 7: Interfaces
Aufgabe a
Sie wollen eine Software schreiben, die eine Datenbank nutzt. Allerdings wollen Sie in Ihrem Code nicht festlegen, welche konkrete Datenbank später eingesetzt wird (Oracle, DB2, MySQL etc.). Sie möchten aber, dass Ihre Datenbank Daten (Strings oder Object) abspeichern und wieder laden kann.
Implementieren Sie dies mit Hilfe eines Interfaces. Die implementierten Methoden müssen über keine wirklich Funktionalität verfügen.
Aufgabe b
Bisher wurden die Statusinformationen über Ihre Fahrzeuge nur über System.out.println ausgegeben. Nun möchten Sie alternativ diese Daten in eine Datei oder nach System.err schreiben. Um sich maximale Flexibilität zu gewähren, möchten Sie diese Entscheidung erst zum Startzeitpunkt (z.B. in der main-Methode) treffen.
Tipp: Schreiben Sie ein Interface names Logger, welches über eine Methode log(String text) verfügt. Es soll verschiedene Klassen geben, welche dieses Interface implementieren. Fügen Sie der Klasse Auto eine Methode ausgeben() hinzu, welche selbst auf dem Interface die Methode log(this.toString()) aufruft. Die konkrete Implementierung dieses Interfaces übergeben Sie dem Auto in dessen Konstruktor.
Interfaces (freiwillige Zusatzaufgabe)
Sie sind Automobilhersteller und denken darüber nach, für Ihr neustes Modell statt Verbrennungs- Elektromotoren einzubauen. Allerdings sind Sie nicht sicher, ob die Autos mit Elektromotoren nachgefragt werden. Daher möchten Sie für alle Eventualitäten gerüstet sein und erst beim Herstellen des Autos (Aufruf des Konstrutors) entscheiden, welche Motorensorte eingebaut wird. Jeder Motor, unabhängig vom Motorentyp, muss natürlich beschleunigen können (public void beschleunigen (int gasInProzent)).
Implementieren Sie diesen Sachverhalt. Sie werden wahrscheinlich ein Interface und folgende Klassen benötigen: Automobilhersteller (enthält die main), Auto, Elektromotor, Verbrennungsmotor.
Übung 6: B-Bäume (optional, aber empfohlen)
Sie fügen in einen B-Baum mit k=1 die folgenden Elemente ein: 9, 2, 6, 8, 7, 4, 5, 10, 11, 12, 13. Danach löschen Sie 2, 12, 13, 8.
Sie fügen in einen B-Baum mit k=2 die folgenden Elemente ein: 11, 2, 7, 4, 8, 6, 1, 5, 9, 15. Anschließend löschen Sie 6, 2, 15 und 5.
Zeichnen Sie die Bäume zum Schluss der Operationen sowie Zwischenschritte.
=== Termin ===
Termin: Abgabe am 31. Oktober bzw. 4. November. D.h. Sie sollten die Aufgaben 1-5 vor Beginn der Stunde fertiggestellt haben.
Übung 5: Binärbäume
Teil a)
Wie sehen die binären Suchbäume aus, bei denen die Elemente in folgender Reihenfolge eingefüt werden?
- 1,7,3,4,2,6,5
- 7,6,5,1,2,3,4
- 7,6,5,4,3,2,1
- 1,3,5,7,2,4,6
Teil b)
Wie würden die gleichen Zahlenfolgen in einen höhenbilanzierten Binärbaum eingefügt? Beschreiben Sie dazu die Zwischenschritte einschließich der Operation (z.B. nur einfügen, einfügen mit Rechtsrotation, einfügen mit Rechts-Linksrotation) und das Endergebnis.
Teil c)
Entwerfen Sie Ihre eigene Klausuraufgabe: Wählen Sie maximal 7 Ziffern so, dass beim Einfügen einmal eine Einfach- und einmal eine Mehrfachrotation notwendig wird.
Übung 4: Datum und Zeit
Lassen Sie sich mit Java folgende Fragen beantworten
- Welcher Tag war am 23.07.1998?
- Welcher Tag war 123 Tage später?
- Lese den Wert 23. Juli 1998 als String ein und wandele ihn in ein Objekt der Klasse GregorianCalendar.
Übung 3: Hashfunktion
- Über welche Eigenschaften sollte eine Hashfunktion verfügen?
- Schreibe jeweils eine einfache Hashfunktion für die Klassen MyInteger, MyString und Auto. Die Klasse MyInteger verfüge über ein Attribut int, die Klasse MyString über ein Attribut String und die Klasse Auto über die Attribute fahrgestellnummer (int) (eindeutig) und typ (String).
- Stelle fest, wie die hashCode() Methode der Klasse java.lang.String implementiert ist. (Sourcen von Java lesen)
Übung 2: Warm-up, equals
- Starten Sie Eclipse, legen Sie ein neues Java-Projekt “Fuhrpark” an, Erstellen Sie ein Package „de.htwg.progstruc.fuhrpark“
- Legen Sie eine Klasse Fahrer mit den Attributen Kürzel und Name an.
- Erzeugen Sie die den Konstruktor und die getter/setter-Methoden mit Eclipse. Tragen Sie dabei Sorge, dass es keine Zombie-Fahrer (ohne Name und Kürzel) gibt und das Kürzel nicht nachträglich geändert werden kann
- Legen Sie eine Klasse Auto an mit den Attributen Fahrgestellnummer, Typ (z.B. Audi A6), Tankfüllung in Liter (Gleitkommazahl), Tachostand (Ganzzahl) und Fahrer an.
- Schreiben Sie eine dritte Klasse Fuhrpark, die über eine main-Methode verfügt, in der Sie zwei Autos und zwei Fahrer erzeugen. Dann weisen Sie jedem Auto je einen der beiden Fahrer zu.
- Ergänzen Sie die Klassen so mit Ausgaben (System.out.println), dass Sie überprüfen können, welcher Fahrer auf welchem Auto gesetzt wurde.
- Implementieren Sie eine geeignete equals-Methode für die Klasse Auto.
Übung 1: Innere Klassen
Ein Auto (Klasse) habe einen Motor. Tritt man beim Auto aufs Gaspedal (gibGas(int gewichtaufgaspedal)), so wird auf der Motor-Klasse die Methode spritzufuhr(int prozentvonmaximal) aufgerufen.
Implementiere den Motor als innere Klasse, einmal als Member Class, einmal als Lokale Klasse und einmal als Anonyme Klasse.
Bei welchen inneren Klassen besteht die Möglichkeit, dass der Mechaniker direkt von außen die Methode spritzufuhr() aufruft?


