www.r-krell.de
Webangebot für Schule und Unterricht, Software, Fotovoltaik und mehr

Willkommen/Übersicht  >  Informatik  >  Java-Seite b2) (2017)

Informatik mit Java

Teil b2)_2017
Fortsetzung: Einführung in das Programmieren mit Swing-Oberflächen



Eine vollständige Übersicht aller meiner Seiten "Informatik mit Java" gibt's auf der Informatik-Hauptseite! Unmittelbarer Vorgänger ist die Seite b1)_2017.

Auf dieser Seite b2)_2017 finden Sie:

Und nach jedem Programm gibt's vertiefende bzw. weiterführende Übungsaufgaben!




zum Seitenanfang / zum Seitenende



Geschicktes Programmieren von Swing-Anwendungen

Nach den ersten Programmierversuchen auf der Seite b1) soll jetzt etwas mehr darauf geachtet werden, nicht nur lauffähige, sondern auch 'schöne' Programme zu schreiben, die übersichtlich, dadurch besser verständlich und damit weniger fehleranfällig und auch später noch wartbar sind. Folgende Prinzipien helfen dabei:


zum Seitenanfang / zum Seitenende

Programm P5
grüßt mit privater Methode und Wiederholungsschleife



Auf der Seite b1) gab es am Ende eine Arbeitsblatt mit einer Zusammenfassung aller Kontrollstrukturen. Da die dort erwähnten Wiederholungsschleifen noch nicht in einem Programm vorkamen, soll das hier passieren. Gesucht ist ein Programm, das den Benutzer einmal, ein paar mal oder oft grüßt.

Oberfläche des Programms P5 bei meheren Grüßen   Oberfläche des Programms P5 bei einen Gruß   Oberfläche des Programms P5 bei oftmaligem Gruß

Die unten als erste angegeben main-Methode wird automatisch vom Javaeditor erzeugt. Sie startet das ganze Programm und wird nicht verändert.

Die in allen Fällen gleiche Erzeugung der Anrede wird in eine eigene Methode ausgelagert, die so nur einmal geschrieben werden braucht, aber von allen drei Knöpfen benutzt/aufgerufen werden kann. Eigentlich sind nur zwei Geschlechter vorgesehen. Es werden aber außer den verlangten Kleinbuchstaben m und w auch Großbuchstaben sowie f bzw. F für die weibliche Form der Anrede anerkannt; alle übrigen Angaben werden als sächlich interpretiert. Da somit drei Fälle unterschieden werden, wird eine switch-case-Anweisungen für die mehrseitige Verzweigung verwendet. Bei den leeren Fällen geht - wegen fehlendem break - das Programm automatisch in die nächstecase-Zeile (auch wenn die neue case-Bedingung nicht zutrifft), sodass hier mehrere verschiedene Angaben mit der gleichen Anweisung behandelt werden können. Alle nicht explizit genannten Geschlechtseingaben landen im default-Fall. Dort würde sogar auch die Eingabe "männlich" landen, weil kein passender Fall vorgesehen ist.

Die Methode anrede hat als Rückgabetyp einen String: an die aufrufende Stelle wird der mit return zurückgegebene Wert der lokalen Variablen ansprache übergeben. Dadurch kann der Aufruf anrede() in den ActionPerformed-Methoden der drei Knöpfe wie eine Variable verwendet werden. Da die Methode anrede nur innerhalb dieser Klasse/Datei verwendet wird, braucht sie nicht öffentlich (public) zu sein; ihre Sichtbarkeit wurde auf private eingeschränkt.

Im Programm ist übrigens - wie oben an den abgebildeten Beispielen zu sehen - "einmal" immer als einmal, "mehrfach" als dreimal und "öfter" als genau 11-mal angelegt. Während man drei Gruß-Anweisungen für den "mehrfach"-Knopf noch dreimal von Hand in den Programmtext schreiben kann, sind elf Kopien des gleichen Befehls für den "öfter"-Knopf unschön. Hier ist eine Wiederholstruktur eleganter und übersichtlicher. Und weil man schon beim Eintritt in die Schleife weiß, wie oft gegrüßt werden soll, bietet sich die Zähl- bzw. for-Schleife an.


  // Anfang Methoden
  
  
public static void main(String[] args) {
    
new P5_MehrfachGruss();
  } 
// end of main
  
  
private String anrede()  // erstellt Anrede aus Textfeld-Eingaben
  {
    String name = jTfName.getText();
    String geschlecht = jTfGeschlecht.getText();
    String ansprache;
    
switch (geschlecht) 
    {
      
case "M" : ;
      
case "m" : ansprache = ", lieber "+name;   break;
      
case "F" : ;
      
case "f" : ;
      
case "W" : ;
      
case "w" : ansprache = ", liebe " +name;   break;
      
default  : ansprache = ", liebes "+name;   break
    } 
// end of switch    
    return (ansprache);
  }
  
  
public void jBtEinmal_ActionPerformed(ActionEvent evt) {
    jTaAusgabe.setText(
"Viele Grüße"+anrede()+"\n");
  } 
// end of jBtEinmal_ActionPerformed

  public void jBtMehrfach_ActionPerformed(ActionEvent evt) {
    jTaAusgabe.setText(
"Viele Grüße"+anrede()+"\n");
    jTaAusgabe.append (
"Viele Grüße"+anrede()+"\n");
    jTaAusgabe.append (
"Viele Grüße"+anrede()+"\n");
  } 
// end of jBtMehrfach_ActionPerformed

  public void jBtOefter_ActionPerformed(ActionEvent evt) {
    jTaAusgabe.setText(
"");
    
for (int i=0; i<11; i=i+1)
    {
      jTaAusgabe.append (
"Viele Grüße"+anrede()+"\n");
    } 
// end of for    
  } // end of jBtOefter_ActionPerformed

  // Ende Methoden


Das Programm lässt sich durch eine zweite private Methode übrigens noch übersichtlicher gestalten, indem auch ein Gruß und drei Grüße durch ein- bzw. dreimalige Wiederholung der selben Anweisung erzeugt werden, die auch bei der 11-maligen Wiederholung verwendet wird. Dann muss der Methode grüßen beim Aufruf in Klammern die benötigte Zahl der Grüße bzw. Wiederholungen mitgegeben werden, die im Parameter anzahl (vom Typ Ganzzahl bzw. int) gespeichert und dann innerhalb der Methode aus dieser Variablen abgelesen wird:




  private String anrede()  // erstellt Anrede aus Textfeld-Eingaben
  {
    String name = jTfName.getText();
    String geschlecht = jTfGeschlecht.getText();
    String ansprache;
    
switch (geschlecht) 
    {
      
case "M" : ;
      
case "m" : ansprache = ", lieber "+name;   break;
      
case "F" : ;
      
case "f" : ;
      
case "W" : ;
      
case "w" : ansprache = ", liebe " +name;   break;
      
default  : ansprache = ", liebes "+name;   break
    } 
// end of switch    
    return (ansprache);
  }
  
  
private void grüßen (int anzahl) // schreibt anzahl Grüße ins Ausgabe-Area
  {
    jTaAusgabe.setText(
"");
    
for (int i=0; i<anzahl; i=i+1)
    {
      jTaAusgabe.append (
"Viele Grüße"+anrede()+"\n");
    } 
// end of for  
  } 
  
  
public void jBtEinmal_ActionPerformed(ActionEvent evt) {
    grüßen (
1);
  } 
// end of jBtEinmal_ActionPerformed

  public void jBtMehrfach_ActionPerformed(ActionEvent evt) {
    grüßen (
3);
  } 
// end of jBtMehrfach_ActionPerformed

  public void jBtOefter_ActionPerformed(ActionEvent evt) {
    grüßen(
11);  
  } 
// end of jBtOefter_ActionPerformed


Entsprechend dem Grundsatz, Methoden übersichtlich zu halten und deshalb nicht mehrere Aufgaben auf einmal machen zu lassen, bleibt es bei zwei Methoden. Es wird nicht versucht, beide Methoden anrede und grüßen zu einer zusammen zu fassen, obwohl das möglich (aber eben nicht sinnvoll) wäre.

Übungs-Aufgaben zu P5

  1. Erzeugen Sie die Oberfläche und damit dann das vollständige, lauffähige Programm!
  2. Ändern Sie den Programmtext, sodass die Knöpfe zu ein, vier oder siebzehn Grüßen führen. In welcher Version (hellgrün oder blassgrün) gelingt diese Änderung am einfachsten?
  3. Führen Sie in der Programm-Oberfläche ein weiteres Textfeld jTfAnzahl ein, in das Sie die gewünschte Anzahl von Grüßen (in Ziffernschreibweise) eintragen können. Dann reicht ein Knopf, um die entsprechende Anzahl von Grüßen ausführen zu lassen. Die eingetippte Zahl wird am besten mit der Befehlszeile int grußzahl = Integer.parseIn(jTfAnzahl.getText()); ausgelesen und in die Zahl grußzahl verwandelt, die als Parameter an grüßen weiter gereicht werden kann. Bei fehlerhafter Eingabe bricht das Programm dann bei parseInt ab. (Der Javaeditor erlaubt statt eines Textfeldes ein eigenes Zahlenfeld JNumberField, das allerdings nicht zum normalen Java-Standard gehört. Deshalb verwende ich das Zahlenfeld ungern, obwohl es bei fehlerhafter Eingabe den Benutzer durch roten Hintergrund auf ein Problem aufmerksam macht).
  4. Um den Umgang mit Kontrollstrukturen zu üben, sollten Sie die for-Schleife in grüßen auch einmal in eine while-Schleife verwandeln.
  5. Um den Umgang mit Kontrollstrukturen zu üben, sollten Sie die switch-case-Anweisung in anrede durch mehrere if- bzw. if-else-Anweisungen ersetzen. Wurde bereits eine richtige ansprache erzeugt, soll keine folgende if-Anweisung nochmal unnötigerweise eine Bedingung überprüfen. (Ergänzung: Bei if können auch kombinierte Bedingungen wie etwa if (geschlecht.equals("w") || geschlecht.equals("W") || geschlecht.equals("f") || geschlecht.equals("F")) verwendet werden: Zwei Striche || bedeuten 'oder', && bedeutet 'und'. Die gerade genannte 4-fach-Bedingung kann auch verkürzt werden zu if (geschlecht.equalsIgnoreCase("w") || geschlecht.equalsIgnoreCase("f")) .


zum Seitenanfang / zum Seitenende

Programm P6
gibt Geld trotz ungenauer Rechnung mit Wiederholungsschleifen zurück



Park-, Fahrschein- oder Verkaufsautomaten geben in der Regel Wechselgeld zurück, wenn man mehr Geld einwirft, als man bezahlen muss. Hier soll Hartgeld in möglichst großen Stücken zurück gegeben werden, wobei der Benutzer sowohl den zu zahlenden Betrag als auch den eingeschobenen bzw. eingeworfenen Betrag selbst festlegen kann. Bei einem echten Automat würde statt der Nennung einer zurückgegebenen Münze diese tatsächlich in den Ausgabeschacht fallen: Jede Zeile würde eine entsprechende Münze freigeben. Deshalb wird jede Münze hier auch einzeln genannt und nicht pauschal etwa "3 x 5 € + 1 x 2 €" angezeigt.

Oberfläche des Programms P6 mit Beispiel-Ein- und -Ausgabe

Der [Neu]-Knopf soll alle Eingaben löschen, d.h. alle Textfelder und das Ausgabe-Area leeren. Bei [Abbruch] soll der gegebene Betrag unverändert zurück gegeben werden, ohne dass eine Zahlung erfolgt. Insofern kann der zu zahlende Betrag noch im entsprechenden Textfeld stehen bleiben, während der gegebene Betrag als Rückzahlung im Ausgabe-Area auftaucht (und aus 'gegeben' gelöscht werden muss, damit er nicht doch noch zur Zahlung verwendet werden kann).

Das Verhalten beim Druck auf die Schaltfläche [Zahlung & Wechseklgeld] wird durch das abgebildete Beispiel deutlich. Da aus zwei verschiedenen Textfeldern jeweils Kommazahlen (die in Java double heißen) ausgelesen werden müssen, wird dafür einmal eine eigene Methode liesZahl erstellt, die mehrfach aufgerufen werden kann - wobei jeweils das auszulesende Textfeld in den Klammern als Parameter übergeben wird. Wie in Aufgabe 2 nach dem Programm P5 schon erwähnt, ist eine Umwandlung des Textes in eine Zahl nötig, was hier mit Double.parseDouble(.. gelingt. Mit der zusätzlichen umschließenden try-catch-Anweisung wird verhindert, dass das Programm bei einer Fehleingabe abbricht. Vielmehr wird ein beim Umwandlungs-Versuch entstehender Fehler aufgefangen, indem dann die Meldung aus dem catch-Block erfolgt. So funktioniert der Automat weiter und dem Benutzer wird geholfen, ihn richtig zu bedienen. Hier die entsprechende Methode:



  
  
private double liesZahl (JTextField tf)
  {
    
double zahl = 0;
    
try 
    {
      zahl = Double.parseDouble(tf.getText());
    } 
    
catch (NumberFormatException e) 
    {
      jTaAusgabe.append(
"** nur Ziffern und Dezimalpunkt erlaubt **\n");
    }
    
if ((zahl < 0.10) || (zahl > 50))
    {
      zahl = 
0;  
      jTaAusgabe.append(
"\n** nur Werte von 0.10 bis 50.00 erlaubt **\n");
    } 
// end of if
    return (zahl);
  }
 


Wichtig ist jetzt natürlich die Ausgabe des Rückgelds. Man macht sich schnell klar, dass von einer Sorte auch mehrere Münzen zurück gegeben werden können. Insofern scheint eine Wiederholstruktur für jede Münzsorte angebracht. Die Schleife kann selbst dann verwendet werden, wenn von bestimmten Münzen (etwa den 1-€-Münzen) höchstens eine ausgegeben wird, weil sonst vorher eine größere Münze ausgegeben worden wäre. Weil sich der noch zurück zu gebende Betrag während der Ausgabe ändert (mit jeder ausgespuckten Münze verringert sich ja die restliche Rückgabe), empfiehlt sich eine while-Schleife mit vorgeschalteter Kontrolle, zumal von manchen Münzen eventuell auch gar keine zurück gegeben wird.

Bitte versuchen Sie selbst, den Automaten entsprechend zu programmieren, bevor Sie meine nachfolgende Lösung lesen - wobei ich den [Zahlung & Wechseklgeld]-Knopf programmintern jBtOkay genannt habe. Ein \n im Ausgabetext wechselt in eine neue Zeile.


  public void jBtOkay_ActionPerformed(ActionEvent evt) {
    
double zuzahlen = liesZahl (jTfZuZahlen);
    
double gegeben  = liesZahl (jTfGegeben);
    
if (zuzahlen*gegeben == 0
    {
      jTaAusgabe.append (
"Bitte geben Sie gültige Beträge ein\nund versuchen Sie es erneut!\n\n"); 
    } 
    
else if (gegeben < zuzahlen)
    {
      jTaAusgabe.setText (
"Sie müssen mindestens "+zuzahlen+" € geben!\n"
        + "Bitte erhöhen Sie den Betrag und versuchen es erneut!\n\n");
    }
    
else
    {
      jTaAusgabe.setText (
"Zahlung erfolgt. Bitte entnehmen Sie Ihr Wechselgeld:\n");
      
double zurück = gegeben - zuzahlen + 0.0001;
      
while (zurück >= 5)
      { 
        jTaAusgabe.append (
"  5-€-Münze\n");
        zurück = zurück - 
5;  
      } 
// end of while
      while (zurück >= 2)
      { 
        jTaAusgabe.append (
"  2-€-Münze\n");
        zurück = zurück - 
2;  
      } 
// end of while  
      while (zurück >= 1)
      { 
        jTaAusgabe.append (
"  1-€-Münze\n");
        zurück = zurück - 
1;  
      } 
// end of while
      while (zurück >= 0.5)
      { 
        jTaAusgabe.append (
"  50-Cent-Stück\n");
        zurück = zurück - 
0.5;  
      } 
// end of while
      while (zurück >= 0.2)
      { 
        jTaAusgabe.append (
"  20-Cent-Stück\n");
        zurück = zurück - 
0.2;  
      } 
// end of while
      while (zurück >= 0.1)
      { 
        jTaAusgabe.append (
"  10-Cent-Stück\n");
        zurück = zurück - 
0.1;  
      } 
// end of while
      if (zurück > 0.009
      {
        jTaAusgabe.append (
"Keine Rückgabe von Beträgen unter 10 Cent.\n\n");
      } 
// end of if        
    }                                        
  } 
// end of jBtOkay_ActionPerformed


In der Lösung fällt auf, dass anfangs einmal zum Rückgabebetrag zurück ein Hundertsel Cent addiert wird, der gar nicht zurück gegeben werden kann: double zurück = gegeben - zuzahlen + 0.0001;. Verzichten Sie zunächst auf diesen Zusatz und probieren Sie das Programm mehrfach aus. Dann sollte Ihnen bald auffallen, dass bei manchen Kombinationen von zu zahlenden und gegebenen Beträgen ein 10-Cent-Stück zu wenig ausgegeben wird. Sie merken, dass Digitalrechner (also Computer oder Taschenrechner) mit Kommazahlen oft nur ungenau umgehen können. Bei der Verwandlung mancher endlicher Dezimalbrüche (wie etwa 0.10) in eine Dualzahl müsste eigentlich ein unendlich langer periodischer Dualbruch entstehen, der technisch irgendwo abgeschnitten werden muss. D.h. die im Computer gespeicherte und verwendete Dualzahl entspricht nicht genau dem eingegebenen Dezimalwert. In der Folge kann zurück auch gelegentlich einen Wert von dezimal 0.1000000002 oder auch 0.09999999 annehmen. Der erste Fall schadet hier nicht. Aber während wir Menschen wissen, dass eine Neunerperiode gleich einer glatten Eins in der nächsthöheren Stelle ist, hält der Computer im zweiten Fall zurück >= 0.1 für falsch und gibt kein 10-Cent-Stück mehr zurück. Der Zusatz sorgt dafür, dass immer ein bisschen mehr Geld da ist und die Rückgabe deshalb auch bei ungenauer Rechnung funktioniert. In kaufmännischen Programmen müssen besondere Vorkehrungen getroffen werden, dass kein Geld verloren geht (oder aus unerklärlichen Quellen dazu kommt)! Generell sollte man immer im Hinterkopf behalten, dass die Verwendung von Kommazahlen unvermeidlich Ungenauigkeiten und Fehler bedingt. Insbesondere sollte man nie auf die exakte Gleichheit zweier Kommazahlen hoffen; statt if (x==0.10) muss immer einen Bereich abgefragt werden wie etwa if (x>0.09999 && x < 0.100001).... Im vorgestellten Text steht in der letzten if-Abfrage dann auch if (zurück > 0.009) und nicht etwa if (zurück > 0), weil der Hinweis nur erscheinen soll, wenn vermutlich noch mindestens 1 Cent zurück zu geben wäre und nicht nur eine kleine Ungenauigkeit oder der Zusatz von 0.0001 € übrig ist.

Übungs-Aufgaben zu P6

  1. Erzeugen Sie die Oberfläche, ergänzen Sie die Knopf-Methoden und verbessern Sie Ihr Programm, bis es gut funktioniert!
  2. Ändern Sie den Programmtext für die jBtOkay_ActionPerformed-Methode ab, indem Sie statt while-Schleifen if-Anweisungen verwenden, wo dies möglich ist. Wird das Programm dadurch kürzer oder übersichtlicher?
  3. Im gegebenen Programm stehen sechs fast gleiche while-Schleifen hinter- bzw. untereinander, die sich nur an wenigen Stellen unterscheiden. Identifizieren Sie die Unterschiede, setzten Sie dafür zwei Variablen ein und schreiben Sie eine eigene Methode private double rückgabe (double zurück, double münzwert, String münzname), die auch auf das Ausgabe-Area jTaAusgabe zugreift. Verkürzen Sie so den Programmtext im Knopf auf 6 Aufrufe der Art zurück = rückgabe(zurück, 5.0, "5-€-Münze"); zurück = rückgabe(zurück, 2.0, "2-€-Münze"); ... . Überlegen Sie, warum zurück zurück gegeben werden sollte. Welche Probleme ergeben sich bei Verwendung einer void-Methode ohne explizite Übergabe von zurück (weder als Parameter noch als Funktionswert)? Lösen Sie das Problem (z.B. mit Ihren Erfahrungen im Programm P4), damit dann noch kürzer geschrieben werden kann: rückgabe( 5.0, "5-€-Münze"); rückgabe( 2.0, "2-€-Münze"); ... Überlegen Sie allerdings auch, welche Risiken mit immer kürzeren Schreibweisen einher gehen könnten.
  4. Schreiben Sie ein neues Programm, das ganze Dezimalzahlen in römische Zahlen verwandelt, also etwa bei der Eingabe von 28 die Zeichenfolge XXVIII ausgibt - und aus 34 möglichst die Zeichenfolge XXXIV und nicht etwa XXXIIII macht. Ebenso soll 19 zu XIX werden und nicht zu XVIIII. Nach reiner Lehre wird übrigens 49 nicht als IL geschrieben, sondern als XLIX und 45 ist nicht VL, sondern XLV, was das Programmieren sogar erleichtert. Wie man an manchen Inschriften sieht, haben sich die Römer selbst allerdings auch nicht immer an ihre Regeln gehalten - was hier allerdings nicht als Ausrede für eine schlechtes Programm anerkannt wird.


zum Seitenanfang / zum Seitenende

Programm P7
fragt Wissen ab und verwendet zwei Zusatzklassen



Die Schülerinnen und Schüler haben das Quiz mit viel Spaß programmiert. Die Oberfläche wurde mit dem im Javaeditor eingebauten GUI-Builder als JFrame rasch erstellt. Dann wurde viel Ehrgeiz auf das Erstellen eines eigenen Fragekatalogs verwendet. Das Verhalten des Programms wird durch die gezeigten Beispiel-Bildschirme deutlich:

Oberfläche des Programms P7 bei fehlender Antwort   Oberfläche des Programms P7 nach richtiger Antwort

Oberfläche des Programms P7 nach falscher Antwort   Oberfläche des Programms P7 nach [weiter]

Oberfläche des Programms P7 am Ende mit deaktivierten Knöpfen

Es ist klar, dass zu einer jeden Quizfrage immer sechs Element gehören: die eigentliche Frage, die vier Antwort-Vorgaben A bis D und ein Buchstabe, der sagt, welche der vier Vorgaben richtig ist. Bei Fernseh-Shows wie "Wer wird Millionär?" käme als siebtes Element eine Preisangabe hinzu, weil leichte Fragen eben nur als 50-€- oder 100-€-Frage verwendet werden sollen, während schwerere Fragen für höhere Preiskategorien taugen. Um dem Computer dieses Schema bei zu bringen, muss man eine eigene Klasse sozusagen als Musterbauplan für eine (und damit für jede) Frage erstellen. Dies geschieht in einer eigenen Textdatei (Im Javaeditor durch Klick aufs weiße Blatt oben links oder durch Datei > Neu > Java erzeugt).

Quelltext der Klasse P7_EineFrage im Javaeditor

In den Zeilen 5 bis 10 wird dem Computer mitgeteilt, welche Bestandteile zu einer Frage (künftiger Typ P7_EineFrage) gehören. Für richtig reicht eigentlich ein Datenfeld vom Typ char, das genau ein einziges Zeichen aufnehmen kann. Da aber von der Eingabe ein String abgelesen wird, wurde hier auch der String-Typ verwendet. Eine neue, noch leere Frage kann von anderer Stelle ab sofort immer mitP7_EineFrage frage1 = new P7_EineFrage(); erzeugt werden. Allerdings müssten dann noch die 6 Bestandteile einzeln gefüllt werden, etwa mit frage1.frage = "Wie viele Monde hat Jupiter?"; frage1.antwortA = "einen"; frage1.antwortB="sieben"; ... . Ein Konstruktor (= Methode mit gleichem Namen wie die Klasse und ohne Angabe des Rückgabetyps) erleichtert das Füllen, weil in der runden Klammer alle 6 Inhalte gemeinsam übergeben werden können (Zeile 12) und dann vom Programm den Attributen zugewiesen werden (Zeilen 14 bis 19). Jetzt kann bequem mit einem Befehl ein neues Frageobjekt frage1 nach diesem Bauplan erzeugt und gefüllt werden: P7_EineFrage frage1 = new P7_EineFrage("Wie viele Monde hat Jupiter?", "einen", "sieben", .. );

Für das Quiz ist es unangenehm, wenn die einzelnen Fragen alle verschiedene Namen tragen - wie etwa frage1, frage2, .. , schwereFrage, .. usw. Man weiß nach einer Frage nicht sicher, wie die nächste Frage heißt, die nun geholt und angezeigt werden soll. Deshalb bietet sich eine besondere Datenstruktur an: die Reihung (englisch als array bezeichnet), äußerlich erkennbar an eckigen Klammern. Hier können viele gleichartige Objekte unter einem gemeinsamen Namen zusammengefasst werden. Es muss allerdings anfangs angegeben werden, wie viele Objekte maximal in der Reihung untergerbacht werden sollen. Soll z.B. die Reihung fragenkatalog für bis zu sechs Fragen vom oben beschriebenen Typ P7_EineFrage ausgelegt werden, so wird definiert P7_EineFrage[ ] fragenkatalog = new P7_EineFrage[6]; . Danach stehen 6 gleichartige Speicherplätze an den Stellen fragenkatalog[0], fragenkatalog[1] bis fragenkatalog[5] zur Verfügung, die gefüllt und anschließend verwendet werden können. Auf den ersten Blick scheint fragenkatalog[0], fragenkatalog[1],.. keinen Vorteil gegenüber der sogar kürzen Schreibweise frage1, frage2,.. zu bieten. Aber in die eckigen Klammern der Reihung kann auch eine Variable eingesetzt werden, die später Werte zwischen 0 und 5 annimmt. Dadurch können Reihungen gut mit for-Schleifen bearbeitet werden.

Aber zuerst muss die Reihung erzeugt und gefüllt werden. Auch das geschieht am besten in einer eigenen Datei als neue Klasse (hier: P7_AlleFragen), wobei die Reihung jetzt nicht fragenkatalog, sondern schlicht alle heißt. Um später die Zahl der Fragen leichter erhöhen (und abfragen) zu können, wird eine Variable anzahl verwendet. In Zeile 6 sieht man dann schon die Verwendung dieser Variablen in der eckigen Klammer; Zeile 22 zeigt ein weiteres Beispiel: Wird irgendwann mal anzahl=5000 gesetzt, wird automatisch der Schlusstext in alle[4999] gespeichert (mehr später in P7_Quiz):



Quelltext der Klasse P7_AlleFragen mit Reihung alle im Javaeditor

Damit sind die Vorarbeiten erledigt; jetzt muss dafür gesorgt werden, dass die Fragen auch in die Oberfläche kommen und dort ordentlich angezeigt werden. Dazu muss die Klasse P7_Quiz mit den oben gezeigten Labeln, Textfeldern und Knöpfen zusammen geklickt und gezogen sein. Es reicht allerdings nicht, nur Text in die ActionPerformed-Methoden der Knöpfe zu schreiben - es müssen auch einige globale Attribute definiert und der Oberfläche gesagt werden, dass sie ein/das Objekt vom Typ P7_AlleFragen verwenden soll, das ich einfach q (für Quiz) genannt habe. Jedenfalls werden die eingerahmten Zeilen von Hand zum automatisch erzeugten Text hinzu geschrieben. Die Attribute lfdNr, geantwortet und pkt dienen der Verwaltung der Fragen und der Statistik; in Zeile 20 wird in das Attribut max die Fragenzahl aus dem Attribut anzahl vom P7_AlleFragen-Objekt q übernommen. Wird später mal in P7_AlleFragen die Fragenzahl erhöht, brauchen in P7_Quiz keinerlei Änderungen vorgenommen werden: Das Quiz bekommt hier mit, wie viele Fragen es dort gibt und stellt sie alle).



Das Ausgeben der verschiedenen Fragebestandteile in den richtigen Textfeldern der Oberfläche geschieht im Prinzip immer gleich (höchstens mit anderen Inhalten, nämlich denen der jeweils nächsten Frage) zu Beginn des Programms, beim Betätigen der [ohne Antwort zu nächsten Frage]-Schaltfläche oder nach dem Druck auf [weiter] (während nach dem Einloggen der Lösung mit [angeklickte Antwort bestätigen] noch keine neue Frage kommen soll, damit man sehen kann, ob die Antwort richtig war). Weil also an drei Stellen gleiche Befehle benötigt werden, werden sie sinnvollerweise in einer eigenen (privaten) Methode zeigeNächsteFrage zusammengefasst. Die Methode kann dann von den verschiedenen Orten aus aufgerufen werden. Der farbig hinterlegte Text zeigt die drei Aufrufe (rote Schrift) und die Knopf-Methoden von P7_Quiz:


    ...
    
// Ende Komponenten
    setVisible(true);
    
    
zeigeNächsteFrage();  // damit bei Progammstart eine Frage erscheint
  } // end of public P7_Quiz
  
  
// Anfang Methoden

...
 
  
public void jBtUeberspringen_ActionPerformed(ActionEvent evt) {
    
zeigeNächsteFrage ();
  } 
// end of jBtUeberspringen_ActionPerformed

  public void jBtAntworten_ActionPerformed(ActionEvent evt) {
    String antwort = buttonGroup_a_bis_d_getSelectedRadioButtonLabel();
    
if (antwort.equals("-")) 
    {
      jTfErgebnis.setText(
"Erst A, B, C oder D wählen!");
    } 
    
else
    {
      geantwortet = geantwortet + 
1;  // es wurde hiermit eine Antwort mehr gegeben
      if (antwort.equalsIgnoreCase(q.alle[lfdNr].richtig)) 
      {
        jTfErgebnis.setText(antwort+
" ist richtig!");
        pkt = pkt + 
1;    // für die richtige Antwort gibt es einen Punkt dazu
      } 
      
else
      {
        jTfErgebnis.setText(antwort+
" ist falsch ("+q.alle[lfdNr].richtig+" ist richtig)");
      }                    
// keine Punkte für falsche Antwort!
      jTfPunkte.setText (""+pkt+" von "+geantwortet);
      jBtWeiter.setEnabled(
true);          // [weiter]-Knopf als einzigen aktivieren
      jBtUeberspringen.setEnabled(false);  // [ohne Antwort..]-Knopf deaktivieren
      jBtAntworten.setEnabled(false);      // Antwort-Knopf selbst deaktivieren, damit ..
    }                                      // .. Antwort nicht wiederholt werden kann ..
  } // end of jBtAntworten_ActionPerformed // .. und nochmal Punkte bringt.

  public void jBtWeiter_ActionPerformed(ActionEvent evt) {
    
zeigeNächsteFrage ();
  } 
// end of jBtWeiter_ActionPerformed

  


Eigentlich sollte die Leserin/der Leser selbst die Methode zeigeNächsteFrage erstellen können. Nur zur anschließenden Kontrolle gebe ich sie nachfolgend an:

  
  
private void zeigeNächsteFrage ()
  {
    
if (lfdNr < max) {
      lfdNr = lfdNr + 
1;  // Nummer der nächsten Frage 
      jTfFrage.setText(q.alle[lfdNr].frage);
      jTf_a.setText(q.alle[lfdNr].antwortA);
      jTf_b.setText(q.alle[lfdNr].antwortB);
      jTf_c.setText(q.alle[lfdNr].antwortC);
      jTf_d.setText(q.alle[lfdNr].antwortD);
      jRB_nichts.setSelected(
true);
      jTfErgebnis.setText(
"");
      jTfPunkte.setText (
""+pkt+" von "+geantwortet);
      jBtWeiter.setEnabled(
false);  // [weiter]-Knopf deaktiviert
      if (lfdNr < max-1)
      {                                    
// Falls es noch Fragen gibt, ..
        jBtUeberspringen.setEnabled(
true); // .. werden diese beiden Knöpfe ..
        jBtAntworten.setEnabled(true);     // .. wieder aktiviert
      } // end of if
    } // end of if
  }


Das Aktivieren und Deaktivieren der verschiedenen Knöpfe ist in den oben gezeigten Beispielen vom Programmablauf gut zu erkennen.

In der Methode zeigeNächsteFrage ist vielleicht die Zeile jRB_nichts.setSelected(true); aufgefallen - und bei den Beispielbildern, dass nach der Anzeige einer neuen Frage keine der vier Antworten angeklickt ist, obwohl doch bei den Radiobuttons einer JButtongroup normalerweise genau ein Radiobutton aktiviert sein sollte. Ich habe einfach in der Oberfläche noch einen fünften Radiobutton (mit dem Namen jRB_nichts) an eine freie Stelle auf der Oberfläche platziert, ihn im Objektinspektor in die gleiche JButtongroup buttonGroup_a_bis_d wie alle übrigen Radiobuttons eingebunden und dort durch Visible = false als unsichtbar markiert. Man sieht den fünften Radiobutton also während der Programmausführung nicht, aber wenn er mit jRB_nichts.setSelected(true) aktiviert wird, schalten sich automatisch alle sichtbaren Knöpfe aus!



Übungs-Aufgaben zu P7

  1. Schreiben Sie das vollständige Programm!
  2. Im Moment werden mit [ohne Antwort zur nächsten Frage] übersprungene Antworten gar nicht gezählt. Ändern Sie das Programm so ab, dass ungelöste Aufgaben als falsch gelten.
  3. Nehmen Sie das gezeigte Programm P7 zum Anlass, ein eigenes Programm MeinFreundeVerzeichnis zu schreiben. Ein Freund (bzw. seine Computer-Repräsentation) hat einen Vor- und Nachnamen, eine Handynummer, WhatsApp- und e-Mail-Adresse. Wenn Ihnen weitere Eigenschaften wichtig sind, nehmen Sie die noch hinzu. Alle diese Daten sollen gleichzeitig in verschiedenen, eigenen Textfeldern der Oberfläche erscheinen (so wie in P7 die Frage und die 4 Antworten). Es gibt außerdem in der Oberfläche zwei Knöpfe [ <- zurück ] und [ vor -> ], mit denen Sie durch ihr Freundeverzeichnis blättern können, denn Sie haben (hoffentlich) mehr als einen Freund (so wie es im Programm P7 ja auch mehr als eine Quizfrage gab. In P7 konnte man allerdings nur weiter und nicht mehr zurück zur vorigen Frage gehen). Mit Ihren aktuellen Kenntnissen können Sie das Freundeverzeichnis allerdings nicht interaktiv ändern, erweitern oder auf der Festplatte speichern - bei Interesse sollten Sie einen Blick in meine 'Informatik mit Java'-Seite d) riskieren!


zum Seitenanfang / zum Seitenende

Programm P8
erzeugt zufälligen Buchstabensalat und trennt Oberfläche und Funktion



Die Idee zu diesem Anagramm-Spiel hat eine Schülerin beigesteuert: Die Buchstaben eines Wortes werden wild vermischt und der Mensch vorm Bildschirm versucht, trotzdem das Wort zu raten:

Oberfläche des Programms P8 nach fehlerhaftem Raten

Damit auch wiederholtes Spielen nicht langweilig wird, soll das Programm intern über eine größere Wortliste verfügen. Die Wörter sollen aber nicht nach jedem Start immer in der gleichen Reihenfolge kommen (wie das bei den Fragen in P7 noch der Fall war), sondern in zufälliger Folge erscheinen. Und das an Stelle eines gewählten Wortes angezeigte Durcheinander soll auch nicht immer gleich für dieses Wort sein, sondern möglichst jedesmal anders gemischt werden. Insofern spielt der Zufall hier eine doppelte Rolle.

Nachdem beim Quiz P7 schon weitere Klassen in eigenen Dateien verwendet wurden, sollen auch hier die Oberfläche und der Programmtext der Wortliste mit ihren zufälligen Reihenfolgen und Mischungen in zwei getrennten Dateien stehen. Das macht das Programm übersichtlicher (mehrere kleine Einheiten sind überschaubarer als ein großes Programm) und erlaubt, später jeden Teil einzeln verbessern zu können, ohne an der anderen Datei etwas zu ändern. So könnte die Oberfläche modernisiert werden, ohne die Wörter und die Mischprogramme überhaupt betrachten zu müssen. Oder die Wortliste wird erweitert, das Mischverfahren verbessert usw., ohne dass man sich das Programm für die Oberfläche ansehen muss -- und ohne dass man bei der Änderung versehentlich die Oberfläche beschädigen kann.

Die einzelnen Wörter werden in einer Reihung (Array) untergebracht (vgl. Text bei P7). Hier kann die Reihung namens reihe sogar direkt durch Angabe (Aufzählen) aller Wörter in einem Schritt definiert, erzeugt und gefüllt werden. Die Programmiererin bzw. der Programmierer muss nicht mal die eingetippten Wörter zählen, sondern lässt sich einfach von Java die Länge der Reihung nennen (-> Variable max).

Dann werden zwei Methoden benötigt: zufallswort sucht ein beliebiges Wort aus der reihe heraus (und sperrt es für die spätere Verwendung, damit es bei einem erneuten Aufruf von zufallswort nicht nochmal erscheint), während vermischt die Buchstaben eines übergebenen Wortes zufällig neu anordnet und so ein Durcheinander erzeugt. Das Mischen der Buchstaben kann leider nicht dem von Kindern gerne ausgeführten Verfahren - jeden Buchstaben auf ein einzelnes Kärtchen schreiben, alle Kärtchen auf den Tisch legen und dann die Kärtchen durch 'Herumrühren' mit beiden Händen in Unordnung bringen - programmiert werden. Java kann nicht mehrere Sachen gleichzeitig machen. Statt dessen wird hier eine zufällige Nummer zufall gezogen und dann der Buchstabe von der entsprechenden Position aus dem Wort (bzw. seiner Kopie) heraus genommen und an eine neue Stelle gelegt. Das verbleibende Wort wird zusammen geschoben und wieder wird daraus ein Buchstabe von einer beliebigen Position ausgewählt und als nächster Buchstabe an das neu entstehende Wort angelegt. So wird das alte Wort immer kürzer und das neue Wort immer länger, bis es zum Schluss alle Buchstaben der ursprünglichen Worts in neuer, zufälliger Reihenfolge enthält.

Natürlich wären auch andere Verfahren möglich: Merkt man sich, welche Zufallsposition schon gezogen wurde, und wählt sie später nicht nochmal (bzw. zieht schnell eine weitere Zufallszahl, falls man doch mal eine bereits verwendete Zahl erhält), dann kann man sich das Zusammenschieben des alten Wortes sparen. Diese Idee wird im vorgestellten Programm nicht beim Mischen. sondern - um zwei etwas unterschiedliche Ansätze in einem Programm zu zeigen - bei der Auswahl eines Worts aus der Reihung verwendet: die reihe wird nicht kürzer, aber ein bereits verwendetes Wort muss gemerkt bzw. markiert werden, hier durch Überschreiben mit dem Leertext "". Natürlich hätte auch jeder andere Text - wie etwa "** schon verwendet **" zum Kenntlichmachen verwendeter Wörter genommen werden können (sofern der spezielle Text nicht auch als gültiger Begriff in der Wortliste auftaucht oder auftauchen darf).

Auch hier gäbe es noch weitere Möglichkeiten zum Markieren, z.B. eine zusätzliche Reihung vom Typ boolean[ ]. Der Fantasie des Programmierers bzw. der Programmiererin sind kaum Grenzen gesetzt. Allerdings sollte man bei zusätzlichen Datenstrukturen im Hinterkopf behalten, dass ein Synchronisierungsproblem entstehen kann. Im Sinne der Objektorientierung wäre deshalb eine eigene Klasse Ratebegriff besser, die als ein Attribut das zu ratende Wort und als zweites Attribut einen boolean-Wert schonVerwendet hat. Die Reihung müsste dann ähnlich wie in P7_AlleFragen vom Typ Ratebegriff[ ] sein.

Hier aber jetzt meine Version der Funktionsklasse-Klasse. In der Oberfläche muss dann eine Objekt vom Typ P8_AnagarammFkt erzeugt werden (vgl. P7_Quiz). Dass durch zufallswort die Wortliste reihe zerstört wird, stört nicht, wenn bei einem neuen Spiel das ganze Programm neu gestartet oder zumindest das Objekt vom Typ P8_AnagarammFkt neu erzeugt (und somit die reihe neu erstellt) wird. Theoretisch kann die do-while-Schleife in zufallswort beliebig oft durchlaufen werden; praktisch ergibt sich auch bei ungünstigem Zufall keine merkbare Verzögerung bei den letzten Wörtern.




public class P8_AnagrammFkt
{
  String[] reihe = {
"Test","Buchstabe","Haus","Mensch","Schülerin","Gefühl","Musik","Altbier"};
  
int max = reihe.length;  // Gesamt-Anzahl der eingegebenen/vorhandenen Wörter/Begriffe
  int nr = 0;              // Anzahl der schon verwendeten Wörter (anfangs 0)
  
  
public String zufallswort()  // gibt ein aus reihe zufällig ausgewähltes Wort zurück
  {                            
    String gefunden = 
"";
    
if (nr < max)
    {
      
int pos;
      
do {
        pos = (
int)(Math.random() * max); // zufällige Ganzzahl zwischen 0 und max-1                                       
      } while (reihe[pos].equals(""));    // falls Wort schon verwendet, erneut suchen
      gefunden = reihe[pos];
      reihe[pos]= 
"";  // lässt gefundenes Wort in Reihung, löscht aber seine Buchstaben  
      nr = nr + 1;   
    }
    
return (gefunden); // gibt das an zufälliger pos gefundene Wort zurück 
  }
  
  
public String vermischt (String wort)   // vermischt die Buchstaben des übergebenen Worts ..
  {                                       // .. und gibt so entstandenes neues Wort zurück
    String kopie = new String(wort); // übernimmt wort in Variable kopie
    String neu = "";    // sammelt Buchstaben aus kopie für Ergebnis-Anagramm
    while (kopie!=null && kopie.length()>0
    { 
      
int zufall = (int)(Math.random() * kopie.length()); // Zufallszahl zw. 0 und Kopielänge-1
      neu = neu + kopie.charAt(zufall);   // zufälliges Zeichen aus kopie hinten an neu
      kopie = kopie.substring(0,zufall) + kopie.substring(zufall+1);
         
// ^ trennt aus kopie den nach neu übertragenen Buchstaben heraus (wort bleibt!)
    } // end of while
    return (neu);
  } 
}





Übungs-Aufgaben zu P8

  1. Erstellen Sie die Oberfläche, binden Sie P8_AnagrammFkt richtig ein und erzeugen Sie so das vollständige, lauffähige Programm und testen Sie es!
  2. Erklären Sie genau die do-while-Schleife und ihre Funktion in der Methode zufallswort
  3. Oben wurde behauptet, dass der Aufruf von zufallswort die reihe ändert bzw. zerstört. Begründen Sie diese Aussage und prüfen Sie außerdem, ob vermischt auch das übergebene wort zerstört. Überlegen Sie ggf. die Auswirkungen auf die Funktion der Schaltflächen [anders mischen] und [Prüfen]!
  4. Schätzen Sie ab, wie oft jede der Wiederholstrukturen in den beiden Methoden bei einem Aufruf durchlaufen werden.
  5. Realisieren Sie das Programm mit der oben angedeuteten Klasse Ratebegriff und einer in endlicher Laufzeit funktionierenden zufallswort-Methode




zum Seitenanfang / zum Seitenende

Programm P9
lässt Wörter anders erraten und besteht wieder aus 2 Klassen



Beim diesem Spiel ist wieder ein Wort zu erraten. Anders als in P8 kennt man die enthaltenen Buchstaben zunächst aber nicht. Vielmehr sieht man anfangs für jeden Buchstaben nur einen Unterstrich. Dann darf nach einem Zeichen des Alphabets gefragt werden. Ist der gefragte Buchstabe ein- oder mehrfach im Wort, wird er dort aufgedeckt. Es geht darum, das Wort möglichst schnell zu erraten, bevor allzu viele Buchstaben erfragt wurden. Das Spiel ist etwa aus der gerade reaktivierten Fernsehshow "Glücksrad" in Erinnerung; unter Schülerinnen und Schülern ist es aber eher als "Hangman" oder "Galgenmännchen" bekannt: Dann wird für jeden gefragten Buchstaben ein Element eines Strichmännchens am Galgen gezeichnet. Nach 10 Buchstaben ist die Zeichnung fertig. Wenn der/die Ratende das Wort dann immer noch nicht vollständig nennen kann, hat er/sie verloren.

Oberfläche des Programms P9 zu Anfang eines neuen Worts   Oberfläche des Programms P9 nach drei gefragten Buchstaben   Oberfläche des Programms P9 nach 8 Zeichen und noch ungeprüftem Raten

Damit man leichter sieht, wie viele Buchstaben schon gefragt wurden, steht deren Anzahl vor der Aufzählung der gefragten Buchstaben im entsprechenden Textfeld.

Die Realisierung sollte wieder in einer eigenen, von der Oberfläche getrennten Klasse P9_GalgenmammFkt eine Wortliste enthalten sowie eine Methode, um aus der Liste ein zufälliges Wort als neues Wort auswählen zu können. Darüber hinaus müssen dort die bereits gefragten Buchstaben gesammelt werden können und es sollte eine Methode geben, die an Stelle des Wortes die Kombination aus Unterstrichen und vorhandenen gefragten Buchstaben zurück gibt. Wegen ähnlicher Aufgaben in P8 sollte nicht alles schwer sein; als Anregung für die letztgenannte Anzeigefunktion habe ich meinen Schülerinnen und Schüler einige 'passende' Aufgaben gestellt:

Übungsblatt P9_Aufgaben.pdf (46 kByte)

Nachtrag vom Februar 2019: Die Funktion des Programms P9 wurde hier etwas verkürzt dargestellt und entspricht damit nicht dem üblichen Spiel "Galgenmännchen". Auf der nächsten Seite ( Seite b3)_2018 ) wurden deswegen Änderungen vorgenommen!





Übungs-Aufgaben zu P9

  1. Schreiben Sie das Galgenmann-Programm. Verbessern Sie evtl. Fehler, bis das Programm ordentlich läuft und das erhoffte Verhalten zeigt.
  2. Das Programm P9 war bisher so ausgelegt, dass eine Person gegen den Computer spielt (und das Programm deswegen eine umfangreichere Wortliste enthalten musste). Ändern Sie Oberfläche und Programm so ab, dass zwei Personen gegeneinander spielen können: Während A wegguckt, gibt B ein Wort ein, das schließlich - nach Druck auf die Taste [Wort nehmen und verbergen] - nur noch durch Unterstriche angezeigt wird. Jetzt spielt A wie in der hier vorgestellten Version. Hat A das Wort erraten oder verloren, muss er das Wort für B eingeben (während B wegschaut). Dann darf B nach Buchstaben fragen und raten. Geeignete Zähler sollen anzeigen, ob bisher A oder B besser war!
  3. Für Fortgeschrittene/Interessierte: Erweitern Sie das Programm so, dass auf der Oberfläche auch das Galgenmännchen nach und nach gezeichnet wird. Wer sich nicht allzu sehr mit den etwas sperrigen Grafik-Fähigkeiten von Java (Swing) auseinander setzen will, könnte wenigstens jeweils eine passende Bilddatei auf einem JLabel anzeigen lassen. Zehn Bilder - nämlich ein Galgenmännchen in den verschiedenen Stadien - müssten dann vorher einmal mit einem Grafik-Programm erstellt werden.
    (Jeweils eine mögliche Lösung wird zur Kontrolle bzw. Vertiefung auf der folgenden Seite b3)_2018 vorgestellt)



zum Seitenanfang / zum Seitenende


zurück zur Informatik-Hauptseite

zurück zur vorigen Seite (Programme P1 bis P4)  Seite b1)_2017
zur Vertiefungsseite (Lösung zu Aufgabe 3 von P9)  Seite b3)_2018
weiter zur (über-)nächsten Seite (Programme P10 bis P12)  Seite b4)_2018


zum Anfang dieser Seite
Willkommen/Übersicht  -  Was ist neu?  -  Software  -  Mathematik  -  Physik  -  Informatik  -   Schule: Lessing-Gymnasium und -Berufskolleg  -  Fotovoltaik  -  & mehr  -  Kontakt: e-Mail,  News-Abo, Gästebuch, Impressum  -  Grußkarten, site map, Download und Suche

Diese Seite ist Teil des Webangebots http://www.r-krell.de.