Lehrgebiet Programmiersysteme Prof. Dr. Friedrich Steimann _________________________________________________________ (Name, Vorname) _________________________________________________________ _________________________________________________________ (Straße, Nr.) ______________ (PLZ) ________________________________________ (Wohnort) _________________________________________________________ (Land, falls außerhalb Deutschlands) Kurs 01618 Einführung in die objektorientierte Programmierung (Kursdurchführung des Sommersemester 2016) Nachklausur am 18.03.2017 Zweistündige Klausur (10.00 – 12.00 Uhr) Lesen Sie zuerst die Hinweise auf der folgenden Seite! Matrikelnummer: Geburtsdatum: . . Klausurort: _________________________ Aufgabe 1 2 3 4 5 6 7 8 Summe 10 10 10 10 10 10 10 10 80 habe bearbeitet maximal erreicht Korrektur ○ Herzlichen Glückwunsch, Sie haben die Klausur bestanden. Note: .......... ○ Sie haben die Klausur leider nicht bestanden. Für den nächsten Versuch wünschen wir Ihnen MUSTERLÖSUNG viel Erfolg. Die nächste Klausur findet im Sommersemester 2017 statt. Hagen, den 29.03.2017 Im Auftrag © 2016 FernUniversität in Hagen Nachklausur zum Kurs 01618 im Sommersemester 2016 Lehrgebiet Programmiersysteme Prof. Dr. Friedrich Steimann Hinweise zur Bearbeitung 1. Prüfen Sie die Vollständigkeit Ihrer Unterlagen. Die Klausur umfasst auf insgesamt 18 Seiten: 1 Deckblatt Diese Hinweise zur Bearbeitung Eine Bescheinigung zur Vorlage beim Finanzamt auf Seite 3 8 Aufgaben auf Seite 4-16 Zwei zusätzliche Seiten 17 und 18 für weitere Lösungen 2. Füllen Sie jetzt bitte zuerst das Deckblatt und (sofern gewünscht) die Bescheinigung zur Vorlage beim Finanzamt aus: Name, Vorname und Adresse Matrikelnummer, Geburtsdatum und Klausurort 3. Schreiben Sie Ihre Lösungen mit Kugelschreiber oder Füllfederhalter (kein Bleistift) direkt in den bei den jeweiligen Aufgaben gegebenen, umrahmten Leerraum. Benutzen Sie auf keinen Fall die Rückseiten der Aufgabenblätter. Versuchen Sie, mit dem vorhandenen Platz auszukommen; Sie dürfen auch stichwortartig antworten. Sollten Sie wider Erwarten nicht mit dem vorgegebenen Platz auskommen, benutzen Sie bitte die beiden dieser Klausur angefügten Leerseiten. Es werden nur Aufgaben gewertet, die sich auf dem offiziellen Klausurpapier befinden. Eigenes Papier ist nur für Ihre persönlichen Notizen erlaubt. 4. Kreuzen Sie die bearbeiteten Aufgaben auf dem Deckblatt an. Schreiben Sie unbedingt auf jedes Blatt Ihrer Klausur Ihren Namen und Ihre Matrikelnummer, auf die Zusatzblätter auch die Nummer der Aufgabe. 5. Geben Sie die gesamte Klausur ab. Lösen Sie die Blätter nicht voneinander. 6. Es sind keine Hilfsmittel zugelassen. 7. Lesen Sie vor der Bearbeitung einer Aufgabe den gesamten Aufgabentext sorgfältig durch. 8. Es sind maximal 80 Punkte erreichbar. Wenn Sie mindestens 40 Punkte erreichen, haben Sie die Klausur bestanden. 9. Sie erhalten die korrigierte Klausur zurück, zusammen mit einer Bescheinigung für das Finanzamt und ggf. dem Übungsschein. 10. Legen Sie jetzt noch Ihren Studierendenausweis und einen amtlichen Lichtbildausweis bereit, dann kann die Arbeit beginnen. Viel Erfolg! Seite 2 von 18 Nachklausur zum Kurs 01618 im Sommersemester 2016 Lehrgebiet Programmiersysteme Prof. Dr. Friedrich Steimann BESCHEINIGUNG (zur Vorlage beim Finanzamt) Herr / Frau ________________________________________________ Matrikelnummer ________________________________________________ geboren am ________________________________________________ hat am 18. März 2017 in ________________________________________________ an der Klausur zur Lehrveranstaltung „Einführung in die objektorientierte Programmierung“ (01618) teilgenommen. gez. Sarah Lang Seite 3 von 18 Nachklausur zum Kurs 01618 im Sommersemester 2016 Lehrgebiet Programmiersysteme Prof. Dr. Friedrich Steimann Aufgabe 1: Klassenimplementierung (10 Punkte) Gegeben sei das folgende Programmfragment: public class UniDemo { public static void main(String[] args) { Universitaet fernUni = new Universitaet("Hagen", 3); Mitarbeiter max = new Mitarbeiter("Max"); Mitarbeiter martina = new Mitarbeiter("Martina"); Kurs derKurs = new Kurs(1234); derKurs.betreutDurch(max); derKurs.geschriebenVon(martina); fernUni.kursAnbieten(0, derKurs); } } Geben Sie Implementierungen für die Klassen Universitaet, Mitarbeiter und Kurs an, sodass dieses Programmfragment kompiliert und bei Terminierung der main(.)-Methode das folgende Objektgeflecht erzeugt und im Speicher abgelegt wurde. Die Belegungen der Attribute sollen hierbei selbstverständlich durch die oben gezeigten Methodenaufrufe vorgenommen werden. (Fortsetzung der Aufgabe auf der folgenden Seite) Seite 4 von 18 Nachklausur zum Kurs 01618 im Sommersemester 2016 Lehrgebiet Programmiersysteme Prof. Dr. Friedrich Steimann (Fortsetzung von Aufgabe 1) class Universitaet{ private String ort; private Kurs[] kursangebot; Universitaet (String ort, int kursangebotsGroesse){ this.ort = ort; this.kursangebot = new Kurs[kursangebotsGroesse]; } public void kursAnbieten(int kursPosition, Kurs kurs) { this.kursangebot[kursPosition] = kurs; } } class Kurs { private int nummer; private Mitarbeiter betreuer; private Mitarbeiter autor; Kurs(int nummer){ this.nummer = nummer; } void betreutDurch(Mitarbeiter betreuer){ this.betreuer = betreuer; } void geschriebenVon(Mitarbeiter autor){ this.autor = autor; } } class Mitarbeiter { private String name; Mitarbeiter (String name){ this.name = name; } } Seite 5 von 18 Lehrgebiet Programmiersysteme Prof. Dr. Friedrich Steimann Nachklausur zum Kurs 01618 im Sommersemester 2016 Aufgabe 2: Kontrollstrukturen (10 Punkte) Vervollständigen Sie das folgende Java Programm, sodass bei Terminierung der main-Methode die folgenden Zeilen auf der Konsole ausgegeben werden: Hänschen Hänschen Hänschen Hänschen klein klein klein klein Implementieren ging ging ging ging Sie allein... allein... allein... allein... dazu vier verschiedene Schleifen, die innerhalb der Methode schreiben(List<String> liste) über die Elemente des Parameters liste iterieren, und zwar: jeweils einmal mit den beiden Varianten einer for-Schleife und jeweils einmal mit den beiden Varianten einer while-Schleife. Hinweis: Jeweils eine Variante der beiden Schleifenimplementierungen nutzt aus, dass das Interface List<T> einen indizierten Zugriff ermöglicht, während die andere Variante ausnutzt, dass das Interface List<T> das Interface Iterable<T> erweitert (also Iterable<T> ein Supertyp von List<T> ist.). import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class SchleifenDemo { public static void main(String[] args) { List<String> eineListe = new ArrayList<String>(); eineListe.add("Hänschen"); eineListe.add("klein"); eineListe.add("ging"); eineListe.add("allein"); eineListe.add("..."); schreiben(eineListe); } static void schreiben(List<String> liste) { // for-Schleife mit indiziertem Zugriff auf die Elemente. (2,5 Punkte) for (int i = 0; i < liste.size(); i++) { System.out.print(liste.get(i) + " "); } System.out.println(); (Fortsetzung der Aufgabe auf folgender Seite) Seite 6 von 18 Nachklausur zum Kurs 01618 im Sommersemester 2016 Lehrgebiet Programmiersysteme Prof. Dr. Friedrich Steimann (Fortsetzung von Aufgabe 2) // for-Schleife, die Iterable<String> ausnutzt. (2,5 Punkte) for (String s : liste) { System.out.print(s + " "); } System.out.println(); // while-Schleife mit indiziertem Zugriff auf die Elemente. (2,5 Punkte) int i = 0; while (i < liste.size()) { System.out.print(liste.get(i++) + " "); } System.out.println(); // while-Schleife, die Iterable<String> ausnutzt. (2,5 Punkte) Iterator<String> iterator = liste.iterator(); while (iterator.hasNext()) { System.out.print(iterator.next() + " "); } } } Seite 7 von 18 Nachklausur zum Kurs 01618 im Sommersemester 2016 Lehrgebiet Programmiersysteme Prof. Dr. Friedrich Steimann Aufgabe 3: Überladen und Überschreiben (10 Punkte) Gegeben sei das folgende Java-Programm: class Ort {} class Weg {} class Trampelpfad extends Weg {} abstract class Sportler { void sportTreiben(Weg weg) { System.out.println("Sportler treibt Sport auf Weg."); } void gemeinsamSportTreiben(Weg weg, Läufer nachbar) { System.out.println("Sportler und Läufer treiben Sport auf Weg."); } void gemeinsamSportTreiben(Trampelpfad ort, Fahrradfahrer nachbar) { System.out.println("Sportler und Fahrradfahrer treiben Sport auf Trampelpfad."); } } class Fahrradfahrer extends Sportler { void sportTreiben(Trampelpfad pfad) { System.out.println("Fahrradfahrer treibt Sport auf Trampelpfad."); } } class Mountainbiker extends Fahrradfahrer { void sportTreiben(Trampelpfad weg) { System.out.println("Mountainbiker treibt Sport auf Trampelpfad."); void gemeinsamSportTreiben(Weg weg, Läufer nachbar) { System.out.println("Mountainbiker und Läufer treiben Sport auf Weg."); } } } class Läufer extends Sportler { void gemeinsamSportTreiben(Trampelpfad weg, Sportler nachbar) { System.out.println("Läufer und Sportler treiben Sport auf Trampelpfad."); } class Sprinter extends Läufer { void sportTreiben(Trampelpfad weg) { System.out.println("Sprinter treibt Sport auf Trampelpfad."); } } } public class VonSportlern { public static void main(String[] args) { Mountainbiker mountainbiker = new Mountainbiker(); Sprinter sprinter = new Sprinter(); Fahrradfahrer fahrradfahrer = mountainbiker; Läufer läufer = sprinter; Trampelpfad pfad = new Trampelpfad(); Weg weg = pfad; /* [1] */ } } (Fortsetzung der Aufgabe auf folgender Seite) Seite 8 von 18 Nachklausur zum Kurs 01618 im Sommersemester 2016 Lehrgebiet Programmiersysteme Prof. Dr. Friedrich Steimann (Fortsetzung von Aufgabe 3) An der mit /* [1] */ markierten Stelle werden nun (einzeln und nacheinander) die folgenden Befehle eingefügt. Kann das Programm nun kompiliert werden? Wenn ja, geben Sie die durch diese Anweisung ausgegebene Zeile an; wenn nein: Warum nicht? mountainbiker.sportTreiben(pfad); (1 Punkt) Das Programm kann kompiliert werden; die folgende Zeile wird ausgegeben: Mountainbiker treibt Sport auf Trampelpfad. fahrradfahrer.sportTreiben(pfad); (1 Punkt) Das Programm kann kompiliert werden; die folgende Zeile wird ausgegeben: Mountainbiker treibt Sport auf Trampelpfad. läufer.sportTreiben(pfad); (1 Punkt) Das Programm kann kompiliert werden; die folgende Zeile wird ausgegeben: Sportler treibt Sport auf Weg. fahrradfahrer.gemeinsamSportTreiben(pfad, fahrradfahrer); (1 Punkt) Das Programm kann kompiliert werden; die folgende Zeile wird ausgegeben: Sportler und Fahrradfahrer treiben Sport auf Trampelpfad. mountainbiker.gemeinsamSportTreiben(weg, läufer); (1 Punkt) Das Programm kann kompiliert werden; die folgende Zeile wird ausgegeben: Mountainbiker und Läufer treiben Sport auf Weg. sprinter.gemeinsamSportTreiben(weg, läufer); (2 Punkte) Das Programm kann kompiliert werden; die folgende Zeile wird ausgegeben: Sportler und Läufer treiben Sport auf Weg. läufer.gemeinsamSportTreiben(pfad, läufer); (3 Punkte) Dieser Methodenaufruf kann durch den Most-Specific-Algorithmus nicht eindeutig aufgelöst werden. Die deklarierten Typen der aktuellen Parameter pfad und läufer sind Trampelpfad und Läufer. Die Überladung der Methode gemeinsamSportTreiben in der Klasse Läufer (mit den Parametertypen Trampelpfad und Sportler) sowie die Überladung in der Klasse Sportler mit den Parametertypen Weg und Läufer haben mit diesen Typen kompatible Parametertypen. Unter diesen beiden Methodendeklarationen kann eine speziellere Methode nicht eindeutig identifiziert werden: gemeinsamSportTreiben(Trampelpfad weg, Sportler nachbar) ist in Bezug auf den ersten Parameter vom Typ Trampelpfad die speziellere; gemeinsamSportTreiben(Weg weg, Läufer nachbar) ist in Bezug auf den zweiten Parameter vom Typ Läufer die speziellere. Der Compiler meldet daher, dass der Methodenaufruf gemeinsamSportTreiben(pfad, läufer) mehrdeutig ist. Seite 9 von 18 Nachklausur zum Kurs 01618 im Sommersemester 2016 Lehrgebiet Programmiersysteme Prof. Dr. Friedrich Steimann Aufgabe 4: (Beschränkt) Parametrische Polymorphie(10 Punkte) Gegeben sei die Implementierung der folgenden main-Methode: public class TupelDemo { public static void main(String[] args) { Tupel<String, Long> t1 = new Tupel<String, Long>("Zeichenkette", 42L); System.out.println(t1.toString()); // Liefert „(Zeichenkette, 42)“. String t1ErsterWert = t1.ersterWert(); Long t1ZweiterWert = t1.zweiterWert(); Tupel<Character, Boolean> t2 = new Tupel<Character, Boolean>('?', true); System.out.println(t2.toString()); // Liefert „(?, true)“. Character t2ErsterWert = t2.ersterWert(); Boolean t2ZweiterWert = t2.zweiterWert(); } } Geben Sie eine Implementierung für die Klasse Tuple<S, T> an, sodass diese Implementierung kompiliert werden kann und durch die System.out.println(…)-Aufrufe die in den Kommentaren angegebenen Zeichenketten auf die Konsole geschrieben werden. (5 Punkte) class Tupel<S, T> { S erster; T zweiter; public Tupel(S erster, T zweiter) { super(); this.erster = erster; this.zweiter = zweiter; } public S ersterWert() { return erster; } public T zweiterWert() { return zweiter; } public String toString() { return "(" + erster + ", " + zweiter + ")"; } } (Fortsetzung der Aufgabe auf der folgenden Seite) Seite 10 von 18 Nachklausur zum Kurs 01618 im Sommersemester 2016 Lehrgebiet Programmiersysteme Prof. Dr. Friedrich Steimann (Fortsetzung von Aufgabe 4) Die Klasse Tupel soll nun so verändert werden, dass 1. der erste und der zweite Wert des Tupels vom selben Typ sind; 2. die Objekte dieses Typs sortierbar sind (also miteinander verglichen werden können); 3. die in den Kommentaren angedeuteten Aufrufe der toString()-Methode die angegebenen Rückgaben erzeugen würden. (Die Kommentare sind so zu verstehen, dass ein Aufruf von ti.toString() nach der Initialisierung der Variablen ti bzw. nach dem Aufruf von ti.sortiere() die angegebene Zeichenkette zurückgäbe.) public static void main(String[] args) { Tupel<Long> t1 = new Tupel<Long>(1L, 2L); // t1.toString() gäbe „(1, 2)“ t1.sortiere(); // t1.toString() gäbe „(1, 2)“ Tupel<Long> t2 = new Tupel<Long>(4711L, 42L); // t2.toString() gäbe „(4711, 42)“ t2.sortiere(); // t2.toString() gäbe „(42, 4711)“ Tupel<String> t3 = new Tupel<String>("xy", "ab"); // t3.toString() gäbe „(xy, ab)“ t3.sortiere(); // t3.toString() gäbe „(ab, xy)“ } Hinweis: Verwenden Sie das Interface java.lang.Comparable: public interface Comparable<T> { /** Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object. */ public int compareTo(T o); } (5 Punkte) class Tupel<S extends Comparable<S>> { S erster; S zweiter; public Tupel(S erster, S zweiter) { super(); this.erster = erster; this.zweiter = zweiter; } public void sortiere() { if (erster.compareTo(zweiter) > 0) { S tmp = erster; erster = zweiter; zweiter = tmp; } } public String toString() { return "(" + erster + ", " + zweiter + ")"; } } Seite 11 von 18 Nachklausur zum Kurs 01618 im Sommersemester 2016 Aufgabe 5: Polymorphie Lehrgebiet Programmiersysteme Prof. Dr. Friedrich Steimann (10 Punkte) Welche vier Arten von Polymorphie kennen Sie? (2 Punkte) Subtyp-Polymorphie parametrische Polymorphie beschränkt parametrische Polymorphie (hierbei handelt es sich um eine spezielle Form der parametrischen Polymorphie) Ad-hoc Polymorphie Charakterisieren Sie jede Art kurz! (jeweils 2 Punkte) Subtyp-Polymorphie wird durch Subtyping erreicht. Das bedeutet, dass an den Stellen, an denen ein Objekt vom Typ Typ erwartet wird, auch solche Objekte stehen dürfen, die Instanzen von einem Subtyp von Typ sind. Parametrische Polymorphie wird dadurch realisiert, dass bestimmte Konstrukte einer Programmiersprache – z.B. Methoden oder Klassen – durch Typparameter parametrisiert werden. Bei der Verwendung eines so parametrisierten Konstrukts wird der Typparameter durch einen konkreten Typen ersetzt. So ist es möglich, eine generische Implementierung einer Funktionalität anzugeben, die in verschiedenen Kon-texten typsicher genutzt werden kann. Die beschränkt parametrische Polymorphie begegnet dem Problem der (einfachen) parametrischen Polymorphie, dass innerhalb der generischen Klasse keine Informationen über den Typen, mit dem der Typparameter belegt wird, zur Verfügung stehen. Hierin können so ohne eine explizite Typkonvertierung (Type Casts) keine spezifischen Methoden verwendet werden. Durch die Angabe einer Schranke zu dem Typparameter kann nun hingegen festgelegt werden, dass dieser lediglich durch Subtypen der Schranke belegt werden kann. So kann auch ohne Typkonvertierung (Type Cast) innerhalb der generischen Implementierung auf die durch die Schranke deklarierte Schnittstelle zugegriffen werden. Als Ad-hoc Polymorphie wird das Überladen von Operatoren bzw. Methodennamen bezeichnet. Für identifizierte Operationen können so für unterschiedliche Argumenttypen jeweils optimierte Implementie-rungen angegeben werden. Seite 12 von 18 Nachklausur zum Kurs 01618 im Sommersemester 2016 Lehrgebiet Programmiersysteme Prof. Dr. Friedrich Steimann Aufgabe 6: Lokale und anonyme Klassen (10 Punkte) Geben Sie eine kompilierbare Java-Klasse an, in der eine lokale Klasse deklariert, eine Instanz von ihr erzeugt und diese einer Variablen zugewiesen wird. (2 Punkte) public class DemoLokaleKlasse { void jetztKommtEineLokaleKlasse() { class LokaleKlasse { } LokaleKlasse l = new LokaleKlasse(); } } Geben Sie eine kompilierbare Java-Klasse an, in der eine Instanz einer anonymen Klasse erzeugt und einer Variablen zugewiesen wird. (2 Punkte) public class LokalUndAnonym { void jetztKommtEineAnonymeKlasse() { LokalUndAnonym a = new LokalUndAnonym() { }; } } In welchem Kontext werden anonyme Klassen in Java häufig verwendet? Warum eignen sie sich hier besonders gut? (2 Punkte) Anonyme Klassen werden oft im Rahmen der Entwicklung von grafischen Benutzeroberflächen verwendet, z.B. um Beobachter-Objekte an Bedienelementen zu registrieren. Hierzu eignen sie sich besonders gut, da von einem Beobachter-Objekt, welches ein spezifisches Verhalten implementiert, in der Regel nur ein einziges Objekt benötigt wird. Welche Gemeinsamkeiten und welche wesentlichen Unterschiede bestehen zwischen einer lokalen und einer anonymen Klasse? (4 Punkte) Sowohl lokale als auch anonyme Klassen können nur in Anweisungsblöcken, also z.B. im Rumpf einer Methode, deklariert werden. Beide sind damit auch innere Klassen, die Komponenten einer umschließenden Klasse darstellen. Gegenüber einer anonymen Klasse hat eine „einfache“ lokale Klasse einen Namen, sodass von ihr mehrere Instanzen erzeugt werden können. Aus gleichem Grund können auch Variablen mit ihr typisiert werden, was einen Aufruf neu deklarierter Methoden von außen erlaubt. Seite 13 von 18 Lehrgebiet Programmiersysteme Prof. Dr. Friedrich Steimann Nachklausur zum Kurs 01618 im Sommersemester 2016 Aufgabe 7: Threads (10 Punkte) Gegeben sei folgendes Java-Programm. import java.util.LinkedList; public class ThreadDemo { public static void main(String[] args) { Kühlschrank kühlschrank = new Kühlschrank(); kühlschrank.hineinlegen("Joghurt"); kühlschrank.hineinlegen("Käse"); kühlschrank.hineinlegen("Milch"); kühlschrank.hineinlegen("Butter"); Verbraucher Verbraucher Verbraucher Verbraucher v1 v2 v3 v4 = = = = new new new new Verbraucher(kühlschrank); Verbraucher(kühlschrank); Verbraucher(kühlschrank); Verbraucher(kühlschrank); v1.start(); v2.start(); v3.start(); v4.start(); } } class Kühlschrank { LinkedList<Object> inhalt = new LinkedList<Object>(); public Object entnehmen() { Object letztes = inhalt.getLast(); inhalt.removeLast(); return letztes; } public void hineinlegen(Object gegenstand) { inhalt.addLast(gegenstand); } } class Verbraucher extends Thread { Kühlschrank kühlschrank; Verbraucher(Kühlschrank kühlschrank){ this.kühlschrank = kühlschrank; } public void run() { Object gegenstand = kühlschrank.entnehmen(); System.out.println(gegenstand); } } (Fortsetzung der Aufgabe auf folgender Seite) Seite 14 von 18 Nachklausur zum Kurs 01618 im Sommersemester 2016 Lehrgebiet Programmiersysteme Prof. Dr. Friedrich Steimann (Fortsetzung von Aufgabe 7) Nach einer Ausführung des Programms erscheinen die folgenden Zeilen auf der Konsole. Butter Butter Käse Joghurt Erläutern Sie, wie es hierzu gekommen ist. Hinweis: Beachten Sie, dass in der Ausgabe Butter zwei Mal, Milch hingegen gar nicht ausgegeben wird. (5 Punkte) Zunächst nimmt einer der vier Threads 𝑡 seine Arbeit auf. Durch die erste Anweisung in der run()Methode der Klasse Verbraucher wird die Methode entnehmen() auf dem durch das Feld inhalt referenzierten Objekt der Klasse Kühlschrank aufgerufen, in welcher zunächst eine Referenz auf das letzte Element (Butter) der List inhalt geholt und in der lokalen Variable letztes gespeichert wird. Noch bevor dieses letzte Element in der folgenden Anweisung aus der Liste inhalt entfernt wird, wird nun die Ausführung des Threads durch den Scheduler unterbrochen und die Ausführung eines anderen Threads 𝑡 ′ begonnen (oder fortgesetzt). Erreicht dieser Thread nun ebenfalls die erste Zeile der Methode entnehmen(), wird eine weitere Referenz auf Butter geholt und anschließend durch 𝑡 ′ Butter aus der Liste inhalt des Kühlschranks entfernt. Nun wird die Ausführung von 𝑡 forgesetzt. In der folgenden Anweisung wird das letzte Element der Liste entfernt; welches nun jedoch Milch ist, obwohl die lokale Variable letztes den Butter referenziert. Während Butter also durch zwei Threads behandelt wird, wird Milch aus der Liste inhalt entfernt, ohne jemals eine Behandlung erfahren zu haben. An welcher Stelle im Programmcode liegt das Problem? (3 Punkte) In der Methode entnehmen findet ein nicht synchronisierter Zugriff auf die gemeinsam genutzte Ressource (die Liste inhalt) statt. Mindestens die Aufrufe inhalt.getLast() und inhalt.removeLast() müssen in einem synchronisierten Code-Block liegen, was aber nicht der Fall ist. Geben Sie eine alternative Implementierung an, die dieses Problem vermeidet. (2 Punkte) public synchronized Object entnehmen() { Object letztes = inhalt.getLast(); inhalt.removeLast(); return letztes; } Seite 15 von 18 Nachklausur zum Kurs 01618 im Sommersemester 2016 Lehrgebiet Programmiersysteme Prof. Dr. Friedrich Steimann Aufgabe 8: Programmgerüste (10 Punkte) Wodurch zeichnen sich Programmgerüste aus? Nennen Sie die wesentlichen Merkmale und erläutern Sie sie anhand eines Ihnen bekannten Beispiels. (Abs. 5.1) Ein Programmgerüst ist ein erweiterbares und anpassbares System von Klassen, das für einen allgemeinen, übergeordneten Aufgabenbereich eine Kernfunktionalität mit entsprechenden Bausteinen bereitstellt. Beispiel: Java AWT zur Gestaltung von Benutzeroberflächen (vgl. Abs. 5.2.2.5) „anpassbar“ Das AWT erlaubt die Anpassung der vordefinierten Komponenten, um z.B. die Darstellung auf dem Display des Benutzers oder das Verhalten der Komponenten anzupassen. „erweiterbar“ Das AWT erlaubt die Definition eigener Komponenten, die selbst bei der Implementierung verschiedener Benutzeroberflächen wiederverwendet werden können. „System von Klassen“ Die einzelnen Klassen des AWT sind eng miteinander gekoppelt. Um ihre eigene Aufgabe zu erledigen, müssen sie in der Regel viele Nachrichten mit Instanzen anderer Klassen austauschen, da sie von ihren Funktionalitäten abhängig sind. „allgemeiner, übergeordneter Aufgabenbereich“ Das AWT unterstützt die Implementierung von Benutzeroberflächen im Allgemeinen, ohne Details, z.B. zu ihrem Aufbau, festzulegen. „Kernfunktionalität“ Das AWT selbst bietet keine vordefinierte Benutzeroberfläche an, sondern lediglich die Bestandteile, um Elemente zu definieren, zu positionieren oder die Interaktion der Elemente festzulegen. Erst durch eine individuelle Konfiguration von Instanzen verschiedener Klassen entsteht eine darstellbare Benutzerschnittstelle. Seite 16 von 18 Nachklausur zum Kurs 01618 im Sommersemester 2016 Lehrgebiet Programmiersysteme Prof. Dr. Friedrich Steimann Zusätzlicher Platz für Ihre Lösungen Ergänzung zu Aufgabe Nr. Ergänzung zu Aufgabe Nr. Seite 17 von 18 Nachklausur zum Kurs 01618 im Sommersemester 2016 Lehrgebiet Programmiersysteme Prof. Dr. Friedrich Steimann Zusätzlicher Platz für Ihre Lösungen Ergänzung zu Aufgabe Nr. Ergänzung zu Aufgabe Nr. Seite 18 von 18