Aufgabe 1: Kurz gefasst (10 Punkte)

Werbung
Klausur zum Kurs 1618 im Sommersemester 2002 am 28.9.2002
Aufgabe 1:
Kurz gefasst
1
(10 Punkte)
Kreuzen Sie in der Tabelle am Ende der Aufgabe fur jeden Aufgabenteil an, ob die entsprechende Aussage richtig oder falsch ist und geben Sie im zugehorigen Feld eine kurze
Begrundung an. Fur jedes Paar aus richtiger Antwort und richtiger Begrundung gibt es 2
Punkte. Fur jede richtige Antwort ohne Begrundung gibt es 0.5 Punkte. Fur jede richtige
Antwort mit falscher oder nicht stichhaltiger Begrundung wird 1 Punkt abgezogen. Fur jede
falsche Antwort wird unabhangig von der Begrundung ebenfalls 1 Punkt abgezogen. Ansonsten werden fur eine Teilaufgabe 0 Punkte vergeben.
Fur die Gesamtaufgabe erhalten Sie als Punktzahl das Maximum aus 0 und der Summe
der in den Teilaufgaben erzielten Punkte. Fur die Gesamtaufgabe sind Ihnen also 0 Punkte
sicher. Maximal konnen Sie 10 Punkte in dieser Aufgabe erreichen.
a) Jede Ausnahme, die nicht in der Methode aufgefangen und behandelt werden soll, in
der sie auftreten kann, muss selbst oder vertreten durch einen ihrer Supertypen in der
throws-Klausel dieser Methode aufgefuhrt sein.
b) sum1 und sum2 sollen die Werte der ganzzahligen Elemente eines als Argument ubergebenen Arrays aufaddieren. sum2 erfullt diesen Zweck, sum1 nicht.
public static int sum1 ( int[] a ) {
int s=0, i=0;
while( i < a.length )
i++;
s = s + a[i];
return s;
}
public static int sum2 ( int[] a ) {
int s=0, i=0;
while( i < a.length )
s = s + a[i++];
return s;
}
c) Das folgende Programm liefert die Ausgabe:
Zunaechst mal hier,
dann dort - was sonst?
import java.io.*;
class A {
String s = "dann dort - was sonst?";
void m() {
System.out.println("Erst hier,");
}
public static void main (String[] args) {
A a = new B();
Klausur zum Kurs 1618 im Sommersemester 2002 am 28.9.2002
}
2
a.m();
System.out.println(a.s);
}
class B extends A {
String s = "dann dort - ist das 'ne Frage?";
}
void m() {
System.out.println("Zunaechst mal hier,");
}
d) Konstruktoren werden auch dann nicht an Subtypen auerhalb des umfassenden Pakets
vererbt, wenn sie mit protected gekennzeichnet sind.

e) Klasse A zeigt eine korrekte Anwendung des Uberladens
von Methodennamen in Java,
da sich die in A deklarierten Methoden mit Identikator m im Ergebnistyp und den
geworfenen Ausnahmen unterscheiden { eines hatte schon gereicht.
class A {
String m (int n) throws IndexOutOfBoundsException {
... }
boolean m (int n) throws ClassNotFoundException {
... }
}
Aufgabe Richtig Falsch
Weil...
a)
b)
c)
d)
e)
Aufgabe 2: Datenstrukturen und Vererbung
(35 Punkte)
Ein Graph ist ein Paar bestehend aus einer Menge von Knoten und einer Menge von Kanten
zwischen den Knoten. Sind die Kanten gerichtet, spricht man von gerichteten Graphen, sind
sie nicht gerichtet, von ungerichteten Graphen.
Klausur zum Kurs 1618 im Sommersemester 2002 am 28.9.2002
3
Die folgende Abbildung zeigt einen speziellen ungerichteten Graphen G, dessen Knoten
durch Landernamen gegeben sind. Zwischen zwei Knoten dieses Graphen gibt es genau dann
eine Kante, wenn sie eine gemeinsame Landgrenze haben.
Paraguay
Brasilien
Australien
Schweiz
Portugal
Deutschland
Spanien
England
Argentinien
Frankreich
In Programmiersprachen lassen sich Graphen auf sehr unterschiedliche Art implementieren.
Haug reprasentiert man die Knotenmenge als Liste und die Kantenmenge als Matrix, die
sog. Adjazenzmatrix. Eine Adjazenzmatrix ist ein 2-dimensionales Feld mit Booleschen Elementen. Das Element (i; j ) einer Adjazenzmatrix enthalt den Wert \wahr" gdw. die Knoten
i und j { identiziert durch ihre Position in der Liste { durch eine Kante verbunden sind.

Fur die Adjazenzmatrix ungerichteter Graphen gilt dabei folgende Aquivalenz:
ein Element
(i; j ) enthalt den Wert \wahr" gdw. auch das Element (j; i) den Wert \wahr" enthalt. Fur

gerichtete Graphen gilt diese Aquivalenz
nicht.
In der Folge ist das Fragment einer Java-Klasse Graph gegeben, mit deren Hilfe in dieser
Aufgabe (Lander-) Graphen nach obigem Muster implementiert werden sollen. Statt mit einer
Liste wird die Knotenmenge jedoch (einfacher) durch ein Feld entsprechender Lange realisiert,
dessen Elemente die Knotennamen, hier also die Landernamen aufnehmen.
public class Graph {
protected boolean[][] adjacencyMatrix;
protected String[] nodes;
protected int nodeNumber;
protected int indexOf (String node) {
for (int i=0; i <= nodeNumber; i++)
if (node.equals(nodes[i])) return i;
return -1;
}
public Graph (int nodeNumber) {
this.nodeNumber = nodeNumber;
nodes = new String[nodeNumber];
adjacencyMatrix = new boolean[nodeNumber][nodeNumber];
}
public Graph (String[] nodes) {
this(nodes.length);
for (int i=0; i < nodeNumber; i++) {
this.nodes[i] = nodes[i];
}
}
Klausur zum Kurs 1618 im Sommersemester 2002 am 28.9.2002
public
//
//
//
...}
4
String getNode (int index) {
...liefert den Namen des Knotens an der
durch den Wert des Parameters bezeichneten Position,
wenn diese einen Knoten bezeichnet; sonst eine Fehlermeldung.
public int getNumberOfNodes ( ) {
// ...liefert die Anzahl der Knoten zurueck.
...}
public
//
//
//
...}
void setNodes (String[] nodes) {
...weist den ersten k Elementen des Attributs nodes die
entsprechenden Werte des Parameters nodes zu. k ist dabei das
Minimum aus der Laenge des Attributs und des Parameters.
public boolean isAdjacent (int index1, int index2) {
// ...liefert "wahr", wenn die Knoten mit Index
// index1 und index2 benachbart sind, sonst "falsch".
...}
public boolean isAdjacent (String node1, String node2) {
// ...liefert "wahr", wenn die Laender mit Namen
// node1 und node2 benachbart sind, sonst "falsch".
...}
}
public
//
//
//
...}
void addEdge (int index1, int index2) {
...fuegt Kante zwischen den Knoten mit
Index index1 und index2 ein, wenn diese
Indizes von Laendern sind; sonst kein Effekt.
public
//
//
//
...}
void addEdge (String node1, String node2) {
...fuegt Kante zwischen den Knoten mit
Laendernamen node1 und node2 ein, wenn diese
Knoten des Graphen bezeichnen; sonst kein Effekt.
Aufgaben:
a) Geben Sie die Adjazenzmatrix des im obigen Landergraphen G fett hervorgehobenen
Teilgraphens T an. T besteht also aus den fett hervorgehobenen Knoten und Kanten
von G. Geben Sie insbesondere auch die Belegung des Attributs nodes an, von der Sie
ausgehen.
(2 Punkte)
b) Vervollstandigen Sie das Programmfragment der Klasse Graph so, dass Objekte der
Klasse Graph ungerichtete Landergraphen reprasentieren. Erganzen Sie dazu die Rumpfe
der Methoden getNode, getNumberOfNodes, setNodes und der uberladenen Methoden
isAdjacent und addEdge im Sinne des jeweils angegebenen Kommentars. (12 Punkte)
c) Die letzte Anweisung in der Methode indexOf der Klasse Graph ist die Anweisung
return -1. Wann, d.h. in welchen Situationen kommt es zur Ausfuhrung dieser Anweisung?
(2 Punkte)
Klausur zum Kurs 1618 im Sommersemester 2002 am 28.9.2002
5
d) Schreiben Sie eine Java-Klasse DirectedGraph, die die Klasse Graph erweitert. Objekte

der Klasse DirectedGraph sollen gerichtete Landergraphen darstellen. Uberlegen
Sie,

welche Methoden geerbt und welche u berschrieben werden mussen. Uberschreiben
Sie
Methoden nur, wenn es notig ist.
(7 Punkte)
e) Schreiben Sie eine Klasse GraphTest, mit der die Klassen Graph und DirectedGraph
mithilfe des Teilgraphen T aus Aufgabenteil a) getestet werden konnen. Vervollstandigen Sie dazu im Fragment der Klasse GraphTest die Implementierung der Methoden
createGraphT und printGraph.
Die gerichtete Version von T soll genau 2 Kanten enthalten; und zwar die gerichteten
Kanten \Deutschland-Frankreich" und \Frankreich-Spanien".
Die Methode printGraph soll zunachst die Knoten(namen) des Argumentgraphen, anschlieend seine Kanten in der Form <land1> - <land2> (Beispiel: Brasilien - Argentinien) ausgeben.
(7 Punkte)
import java.io.*;
class GraphTest {
static Graph createDummyGraph (String graphType, int numberOfNodes) {
switch (graphType.charAt(0)) {
case 'u': return new Graph(numberOfNodes);
case 'd': return new DirectedGraph(numberOfNodes);
default: System.out.println("Unknown Graph Type");
}
return null;
}
static Graph createGraphT (String graphType) {
String[] countries = {"Deutschland","Frankreich","Spanien"};
Graph g = createDummyGraph(graphType,countries.length);
...
return g;
}
static void printGraph (Graph g) {
// Ausgabe aller Laender in g
System.out.println("Laender in g:");
...
// Ausgabe aller Kanten in g:
System.out.println("Von diesen sind benachbart:");
...
}
}
public static void main (String[] args) {
printGraph(createGraphT("undirected"));
printGraph((DirectedGraph) createGraphT("directed"));
}
f) Die Klasse Graph bietet eine Methode zum Hinzufugen von Kanten an. Es liegt nahe,
sie auch Methoden zum Loschen von Kanten sowie zum Hinzufugen und Loschen von
6
Klausur zum Kurs 1618 im Sommersemester 2002 am 28.9.2002
Knoten anbieten zu lassen. Nicht fur jede dieser Methoden ist der Entwurf von Graph
wirklich zweckmaig. Warum? Begrunden Sie Ihre Antwort .
(5 Punkte)
Aufgabe 3:
Strome
(15 Punkte)
Gegeben sei folgendes Java-Programm:
import java.io.*;
1:
2:
3:
4:
5:
6:
7:
interface CharEingabeStrom {
int read() throws IOException;
}
class StringLeser implements CharEingabeStrom {
private char[] dieZeichen;
private int
index = 0;
public StringLeser( String s ) { dieZeichen = s.toCharArray(); }
8:
public int read() {
9:
if( index == dieZeichen.length ) return -1;
10:
else return dieZeichen[index++];
11:
}
12: }
13: class GrossBuchstabenFilter implements CharEingabeStrom {
14:
private CharEingabeStrom eingabeStrom;
15:
public GrossBuchstabenFilter( CharEingabeStrom cs ) { eingabeStrom = cs; }
16:
public int read() throws IOException {
17:
int z = eingabeStrom.read();
18:
if( z == -1 ) return -1;
19:
else return Character.toUpperCase( (char)z );
20:
}
21: }
22: class UmlautSzFilter implements CharEingabeStrom {
23:
private CharEingabeStrom eingabeStrom;
24:
private int naechstesZ = -1;
25:
public UmlautSzFilter( CharEingabeStrom cs ) { eingabeStrom = cs; }
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
public int read() throws IOException {
if( naechstesZ != -1 ) {
int z = naechstesZ;
naechstesZ = -1;
return z;
} else {
int z = eingabeStrom.read();
if( z == -1 ) return -1;
switch( (char)z ) {
case '\u00C4': naechstesZ = 'e'; return
case '\u00D6': naechstesZ = 'e'; return
case '\u00DC': naechstesZ = 'e'; return
case '\u00E4': naechstesZ = 'e'; return
case '\u00F6': naechstesZ = 'e'; return
case '\u00FC': naechstesZ = 'e'; return
case '\u00DF': naechstesZ = 's'; return
'A';
'O';
'U';
'a';
'o';
'u';
's';
7
Klausur zum Kurs 1618 im Sommersemester 2002 am 28.9.2002
42:
default:
43:
}
44:
}
45:
}
46: }
return z;
47: public class Main {
48:
public static void main(String[] args) throws IOException {
49:
String s = new String("f\u00DF\u00D6");
50:
51:
52:
53:
CharEingabeStrom cs;
cs = new StringLeser( s );
cs = new UmlautSzFilter( cs );
cs = new GrossBuchstabenFilter( cs );
54:
55:
56:
57:
58:
59:
60:
}
61: }
int z = cs.read();
while( z != -1 ) {
System.out.print( (char)z );
z = cs.read();
}
System.out.println("");
Aufgaben:
a) Welche Ausgabe erzeugt das obige Programm?
(1 Punkte)
b) Erklaren Sie, wie das Programm die Ausgabe aus a) erzeugt. Erlautern Sie dazu unter
Bezug auf die Zeilennummern den Programmablauf hinreichend detailliert. Dabei reicht
es, wenn sie den Programmablauf bis zur Ausgabe des ersten Zeichens beschreiben.
Gehen Sie in Ihrer Beschreibung insbesondere auf die Abfolge der read-Aufrufe ein und
erklaren Sie jeweils mithilfe des dynamischen Typs der involvierten Objekte warum und
wo welche read-Methode aufgerufen wird.
(10 Punkte)
c) Welche Ausgabe ergibt sich, wenn im obigen Programm die Zeilen 52 und 53 miteinander
vertauscht werden? Begrunden Sie Ihre Antwort.
(4 Punkte)
...
53:
52:
...
cs = new GrossBuchstabenFilter( cs );
cs = new UmlautSzFilter( cs );
Aufgabe 4:
Nebenlaugkeit
(40 Punkte)
Gegeben seien folgende Programmfragmente, die in Aufgabenteil b) benotigt werden:
Programmfragment I
//
bA
und
bB
sind von Thread A und Thread B gemeinsam benutzte Boolesche Variablen.
// Beim Programmstart sind
bA
und
bB
mit
true
initialisiert.
Klausur zum Kurs 1618 im Sommersemester 2002 am 28.9.2002
// Code fuer Thread A
while (true) {
bB = false;
if (bA) {
criticalSectionOfThreadA();
bB = true;
uncriticalSectionOfThreadA();
}
}
8
// Code fuer Thread B
while (true) {
bA = false;
if (bB) {
criticalSectionOfThreadB();
bA = true;
uncriticalSectionOfThreadB();
}
}
Programmfragment II
//
b
ist eine von Thread A und Thread B gemeinsam benutzte Boolesche Variable.
// Beim Programmstart ist
b
mit
true
initialisiert.
// Code fuer Thread A
while (true) {
if (b) {
criticalSectionOfThreadA();
b = false;
uncriticalSectionOfThreadA();
}
}
// Code fuer Thread B
while (true) {
if (!b) {
criticalSectionOfThreadB();
b = true;
uncriticalSectionOfThreadB();
}
}
Programmfragment III
//
bA
und
bB
sind von Thread A und Thread B gemeinsam benutzte Boolesche Variablen.
// Beim Programmstart sind
bA
und
// Code fuer Thread A
while (true) {
if (!bB) {
bA = true;
criticalSectionOfThreadA();
bA = false;
uncriticalSectionOfThreadA();
}
}
bB
mit
false
initialisiert.
// Code fuer Thread B
while (true) {
if (!bA) {
bB = true;
criticalSectionOfThreadB();
bB = false;
uncriticalSectionOfThreadB();
}
}
Aufgaben:

a) Welche inharent neuen Probleme stellen sich einem Programmierer beim Ubergang
von
sequentiellen zu nebenlaugen Programmen? Welche sprachlichen Mittel stellt Java zu
ihrer Behandlung bereit? Geben Sie eine kurze Erlauterung von Problemen und sprachlichen Mitteln.
(10 Punkte)
b) Bei der Programmierung nebenlauger Prozesse ist es haug notig, den wechselweisen Ausschluss auf gemeinsam benutzte Ressourcen sicherzustellen. In den Programmfragmenten I, II und III soll dies fur zwei Prozesse A und B ohne Benutzung spezieller programmiersprachlicher Mittel allein mit Hilfe einer bzw. zwei gemeinsamer
Boolescher Variablen erreicht werden. Der Zugri auf die von A und B gemeinsam
benutzte Ressource erfolgt dabei in den Methoden criticalSectionOfThreadA und
criticalSectionOfThreadB.
Untersuchen Sie, ob die Programmfragmente I, II und III den wechselweisen Ausschluss
sicherstellen. Falls nein, skizzieren Sie einen Berechnungsablauf, aus dem hervorgeht,
Klausur zum Kurs 1618 im Sommersemester 2002 am 28.9.2002
9
dass sich beide Prozesse gleichzeitig in ihrem kritischen Abschnitt aufhalten konnen, also
innerhalb der Methoden criticalSectionOfThreadA und criticalSectionOfThreadB.
Falls wechselweiser Ausschluss gewahrleistet ist, begrunden Sie dies und untersuchen
Sie zusatzlich, ob das zugehorige Programmfragment zweckmaig ist im Hinblick auf
Verklemmungsfreiheit, Verallgemeinerbarkeit (auf mehr als zwei parallele Prozesse) und
okonomischen Ressourceneinsatz (moglichst geringe wechselweise Behinderung beim Zugri auf die kritische Ressource durch die Synchronisation).
Zur Vereinfachung setzen wir dabei die Fortschrittsannahme voraus. Sie besagt, dass
nichtblockierte Prozesse stets nach endlicher Zeit wieder einen Fortschritt machen - \sie
bleiben nicht stehen und streiken nicht". Insbesondere konnen die Prozesse in Ihren
kritischen und unkritischen Abschnitten weder in eine Endlosschleife gelangen, noch
aus irgendeinem Grund blockieren.
(13 Punkte)
c) In dieser Aufgabe soll der wechselweise Ausschluss nebenlauger Prozesse auf eine gemeinsame Ressource mit Java-spezischen Mitteln realisiert werden.
Dazu soll das folgende Programmfragment bestehend aus der ausfuhrbaren Klasse Mutex,
der Klasse A und der davon abgeleiteten Klasse B an den mit \..." gekennzeichneten Stellen vervollstandigt werden.
Die gemeinsame Ressource, ein Integer-Array der Lange 1, initialisiert mit Wert 42, wird
als Attribut der Klasse Mutex realisiert. Die main-Methode von Mutex startet fur jedes
Element ihres Arguments mit geradem Index ein anonymes Thread-Objekt vom Typ A,
mit ungeradem Index ein anonymes Thread-Objekt vom Typ B. Der ganzzahlige Wert
( 0) des Elements gibt dabei an, wie oft das zugehorige A- bzw. B-Objekt den kritischen
und unkritischen Abschnitt durchlaufen soll. Jedes Thread-Objekt soll dabei durch seine
Objekt-Nummer eindeutig identiziert sein. Objekt-Nummern sind | beginnend mit 1
| fortlaufend vergebene naturliche Zahlen.
Beim Durchlauf durch den kritischen Abschnitt erhohen A-Objekte den Wert der kritischen Ressource um 10, B-Objekte erniedrigen ihn um 5. Beide geben eine Textmeldung

uber die Anderung
zusammen mit ihrer Objekt-Nummer und Objekt-Typ aus.
Im unkritischen Abschnitt geben A- und B-Objekte eine Textmeldung aus, dass sie sich
im unkritischen Abschnitt benden, wieder zusammen mit Objekt-Nummer und ObjektTyp.
Der Zugri auf die gemeinsame Ressource ist so zu synchronisieren, dass ausgeschlossen
ist, dass zwei Objekte gleichzeitig Zugri darauf haben.
(13 Punkte)
import ...
class Mutex {
// Ergaenze Attributdeklarationen
...
}
public static void main (String[] args) {
...
}
class A ... {
Klausur zum Kurs 1618 im Sommersemester 2002 am 28.9.2002
10
// Ergaenze Attributdeklarationen
...
// Ergaenze Konstruktordeklaration(en) fuer A-Objekte
...
void aufGehts (int durchlaeufe) {
/* aufGehts ruft in einer while-Schleife die Methode "dannMalRan"
auf. Der Wert des Parameters der Methode "aufGehts" gibt
dabei die Anzahl der Durchlaeufe der while-Schleife an.
*/
}
void dannMalRan ( ) {
criticalSection();
uncriticalSection();
}
void criticalSection() {
...
}
void uncriticalSection() {
...
}
}
public void run () {
aufGehts(durchlaeufe);
}
class B ... {
// Ergaenze Konstruktordeklaration(en) fuer B-Objekte
...
void criticalSection() {
...
}
}
void uncriticalSection() {
...
}
d) Wir betrachten den Aufruf java Mutex 2 3 4. Was lasst sich uber die Reihenfolge aussagen, in der die gestarteten Thread-Objekte auf die Variable gemeinsameRessource
zugreifen? Was lasst sich u ber den Endwert der Variable gemeinsameRessource aussagen, also uber den Wert nach Terminierung des Programms? Beides unter der Voraussetzung, dass das vervollstandigte Programm die Spezikation aus Aufgabenteil c)
erfullt.
(4 Punkte)
Zugehörige Unterlagen
Herunterladen