3. Ablaufsteuerung, Kontrollstrukturen

3. Ablaufsteuerung, Kontrollstrukturen Stefan Schneider Sun, 06/20/2010 - 15:51

3.1 Verzweigungen und Vergleichsoperationen

3.1 Verzweigungen und Vergleichsoperationen

Durch Verzweigungen können im Ablauffluß des Programmes verschiedene Fälle geprüft werden und man kann abhängig davon unterschiedliche Anweisungen ausführen.

Befehlsblöcke

Java erlaubt das Zusammenfassen von Javabefehlen durch Blöcke. Blöcke werden geschweiften Klammern beschrieben.

Für Blöcke gilt

  • Im Block deklarierte Variablen sind nur innerhalb des Blocks, nach der Deklaration benutzbar
  • Befehle innerhalb eines Blocks müssen mit Semikolon getrennt werden
  • Auch der letzte Befehl in einem Block muss mit Semikolon abgeschlossen werden
  • Blöcke können geschachtelt werden

Beispiel

{
   int i = 1;
   i++;
   int j = i*2;
}

Tipp: Systematisches Einrücken und Klammern die genau übereinander stehen helfen sehr bei der Lesbarkeit

Einfachverzweigung (if, if else)

Eine Einfachverzweigung wird in Java mit dem Schlüsselwort if eingeleitet. Anschließend folgt eine Bedingung in runden Klammern. Ist die Bedingung erfüllt, wird die folgende Anweisung bzw. der Anweisungsblock in geschweiften Klammern ausgeführt. Hierdurch ergibt sich die folgende Syntax:

if (Bedingung) Anweisung;

oder

if (Bedingung) {Anweisung(en);}

Ist die Bedingung nicht erfüllt wird die Anweisung ausgelassen und der normale Ausführungsfluß wird wieder aufgenommen. Ein Beispiel hierfür ist:

int x = 8;
if (x > 5) System.out.println("Wert  x = " + x + " ist größer als 5");
System.out.println ("Wert  x = " + x);

Hier ergeben sich die Ausgaben:

Wert x = 8 ist größer als 5
Wert x = 8

da die bedingte Anweisung sowie die letzte Anweisung durchlaufen wird.

UML Darstellung in Aktivitätsdiagrammen

if Bedingung ohne else Klausel kann man die geschweiften Klammer bei einer einzelnen Anweisung weglassen.

Entweder-oder Kontrollfluss mit dem Schlüsselwort else

Es gibt auch die Möglichkeit eines entweder-oder Kontrollflusses mit Hilfe des Schlüsselworts else:

if (Bedingung) {Anweisung(en);}
     else {Anweisung(en);};

Hier wird der eine oder der andere Block von Anweisungen ausgeführt.

Ein Beispiel:

int x = 8;
int y = 0;
if (x > 5) {
      System.out.println("Wert x = " + x + " ist größer als 5"); 
      y = x; 
   } 
   else { 
      System.out.println("Wert x = " + x + " ist kleiner oder gleich 5"); 
      y = -x; 
   } 
System.out.println ("Wert x = " + x );

UML Darstellung in Aktivitätsdiagrammen

if Anweisungen lassen sich schachteln um komplexe Bedingungen prüfen zu können. Anbei ein Beispiel:

int a = 5;
int b = 10;
int maximum =0;
if (a > b)  // Äussere if Bedingung
   if (a > 0) 
      { maximum = a;} // Innere if Bedingung
   else 
      { maximum = 0; } //innere else Bedingung
System.out.println ("Wert maximum = " + maximum ");
else-Regel

 

Im oben gezeigten Beispiel gibt es eine else Bedingung von der es nicht offensichtlich ist zu welchem if Block sie gehört.

else-Regel
Ein else-Block gehört immer zum direkt vorhergehenden if-Block

Tipp: Der Quellcode wird bei geschachtelten if Bedingungen schnell unübersichtlich und fehleranfällig.

Verwenden Sie besser die im Folgenden vorgestellte switch Bedingung um komplexe Abfragen zu implementieren.

Mehrfachverzweigung (switch-case)

Eine Alternative zu if-else Anweisungen ist die switch-case Anweisung. Sie erlaubt eine sehr viel übersichtlichere Programmierung wenn es bei einem ganzzahligen Ausdruck mehr als zwei Alternativen gibt.

Im einfachen Fall sieht eine switch-case Anwendung wie folgt aus:

int wochentag;
...
switch (wochentag) {
   case 7:
      System.out.println("Sonntag");
      break;
   case 6:
      System.out.println("Samstag");
      break;
   case 5: case 4: case 3: case 2: case 1:
      System.out.println("Wochentag");
      break;
   default:
      System.out.println("Fehler");
}
Switch Anweisung in UML

 

int wochentag;
...
switch (wochentag) {
   case 7:
      System.out.println("Sonntag");
      break;
   case 6:
      System.out.println("Samstag");
      break;
   case 5: case 4: case 3: case 2: case 1:
      System.out.println("Wochentag");
      break;
   default:
      System.out.println("Kein gültiger Wochentag!");
}

Besonderheiten der switch-case Anweisung

  • Der zu prüfende Ausdruck muss bestimmten Typen genügen: 
    • byte, short, int, char, enum, String. (Die Unterstützung von String ist neu seit JDK 7, siehe JSR 334, Projekt Coin)
  • Die einzelnen Fälle (cases) werden nicht mit geschweiften Klammern geklammert
  • Das Schlüsselwort break dient zum Rücksprung aus einem Fall. Der nächste Fall wird mit abgearbeitet wenn es weggelassen wird!
  • Es können mehrere Fälle mit Doppelpunkt aufgeführt werden. Der darauf folgende Block wird dann für alle Fälle abgearbeitet.
  • Das Schlüsselwort default erlaubt einen Ausführungsblock anzugeben, der ausgeführt wird wenn kein anderer Fall zum Zuge kam
  • Der Wert in einem Fall (case) muss eine Konstante sein.

Allgemeine Form:

...
switch (Ausdruck) {
   case konstante1:
      Anweisung(en);
      break;
...
   case konstante2: case konstante3: ... case konstanteN:
      Anweisung(en);
      break;
   default:
      Anweisung(en);
      break;
}

Beispiel mit unterschiedlichen Break-Anweisungen

int wochentag;
...
switch (wochentag) {
   case 7:
      System.out.println("Sonntag");
      break;
   case 6:
      System.out.println("Samstag");
   case 5: case 4: case 3: case 2: case 1:
      System.out.println("Wochentag");
      break;
   default:
      System.out.println("Fehler");
}
Switch Anweisung mit komplexen break statements

 

 

Stefan Schneider Sun, 07/11/2010 - 16:26

Anonymous (not verified)

Tue, 11/18/2014 - 14:15

Fehlt die break;-Anweisung, werden die folgenden Blöcke ohne Fallüberprüfung ausgeführt? Im UML-Diagramm ("Beispiel mit untersch. Break-Anweisungen) ist dargestellt, dass im case 6: automatisch auch die cases 5-1: angenommen werden und somit "Samstag" UND "Wochentag" gedruckt werden würden.

Ist das tatsächlich der Fall, oder werden die folgenden Cases trotz fehlender break-Anweisung zumindest geprüft? Das "der nächste Fall wird mitabgearbeitet" ist etwas undeutlich formuliert.

Besten Dank

Anonymous (not verified)

Mon, 12/12/2016 - 18:29

Im Bsp unter "Besonderheiten der switch-case Anweisung" wird die default-Anweisung mit einem "break;" beendet, im "Beispiel mit unterschiedlichen Break-Anweisungen" jedoch ohne.
Unterliegt beides der korrekten Syntax?

Gruß.

3.2 Schleifen und Felder

3.2 Schleifen und Felder

Schleifen erlauben die Wiederholungen von Einzelanweisung. Schleifen bestehen typischerweise aus den folgenden Komponenten

  • einen Schleifenrumpf mit den auszuführenden Anweisungen
  • einem Kopf oder Fuß mit einer Ausführungsbedingung
  • oft einer Laufvariablen mit der die Durchläufe kontrolliert werden

Die while Schleife 

Die while Schleife überprüft vor dem Schleifeneintritt ob die Ausführungsbedingung (noch) erfüllt ist. Sie enthält die Schleifenbedingung im Kopf der Schleife.

Die Syntax der while Schleife ist die folgende:
while ( Bedingung) { Anweisung ; } 

oder

while ( Bedingung) Anweisung; 

Beispiel: Arithmetische Summe 1+2+3+4+5

int i = 1;
int summe= 0;
while (i<=5) {
   summe = summe + i;
   i++;
}

UML Diagramm while Schleife

Die do-while-Schleife

Die do-while-Schleife überprüft nach dem ersten Schleifendurchlauf ob die Ausführungsbedingung erfüllt ist. Sie enthält die Schleifenbedingung im Fuß der Schleife. Die Syntax der do-while Schleife ist die folgende:

do { Anweisung ; } while ( Bedingung );

oder

do Anweisung; while ( Bedingung );

Beispiel: Beende Schleife wenn Wert durch 3 ohne Rest teilt

int i = 9;
int j = 0; // Zählt Schleifendurchläufe
do {
   j++;
   i--;
} while (i%3 != 0);

Die Schleife wird dreimal durchlaufen.

UM Diagramm do while Schleife

Regel:

  • Die while Schleife ist eine abweisende Schleife: Sie wird nicht notwendigerweise durchlaufen.
  • Die do-while ist eine nicht abweisende Schleife: Sie wird mindestens einmal durchlaufen.

Diese Unterscheidung ist in verschiedenen Bereichen wichtig

  • Variablen werden bei einer abweisenden Schleife eventuell nicht belegt
  • Es wird manchmal in der Qualitätssicherung gefordert, dass alle Zeilen eines Programmes duchlaufen werden. Bei abweisenden Schleifen muss man unter Umständen bei der Implementierung der Testabdeckung mehr investieren.

Die for-Schleife

Die for-Schleife überprüft die Schleifenbedingung vor dem Eintritt in die Schleife.

Ihre Syntax ist die anspruchsvollste der drei Schleifenarten:

for (Initialiserung; Bedingung; Veränderung) {Anweisung ;}

oder

for (Initialiserung; Bedingung; Veränderung) Anweisung;

Der Kopf der for-Schleife besteht aus den folgenden drei Teilen:

  • Initialisierungsteil: Laufvariable und Startwert werden festgelegt.
  • Bedingungsteil: Wird der Wert der Bedingung unwahr (false) wird die Schleife verlassen oder nicht betreten. Ansonsten wird sie weiter fortgesetzt.
  • Veränderungsteil: Nach jedem Durchlauf der Schleife wird die Laufvariable entsprechen verändert (Typischerweise inkrementiert oder dekrementiert).

Beispiel einer einfachen for-Schleife

Im folgenden Beispiel wird die for-Schleife "b mal" durchlaufen. Durch das Aufaddieren der Variablen a wird eine Multiplikation von positiven Zahlen ausgeführt.

Ist b gleich Null oder negativ wird die Schleife nicht durchlaufen.

einfache for-Schleife

Geschachtelte Schleifen

Oft ist es notwendig Schleifen zu schachteln. Dies kommt oft bei mehrdimensionalen Datenstrukturen vor bei denen jedes Feld bearbeitet werden muss. Bei jedem Durchlauf der äusseren Schleife wird die innere aufgerufen, die ihre eigenen Durchläufe komplett bei jedem Durchlauf der äusseren Schleife durchführt. Anbei ein Beispiel einer naiven Multiplikation die auf Inkrementieren beruht:

class MultiplizierenNaiv {
    public static void main(String[] args) {
        int a = 5;
        int b = 10;
        int result = 0;
        for (int i = 1; i <= a; i++) {
            System.out.println("Äussere Schleife i = " + i);
            for (int j = 1; j <= b; j++) {
                System.out.println("Innere Schleife j = " + j);
                result++;
            }
        }
        System.out.println(a +"*"+b+" = "+result);
    }
}

Geschachtelte Schleifen sind sehr mächtig und sie haben ein viel größeres Potential Rechnerleistung zu binden. Die Aufwände bei zwei geschachtelten Schleifen können quadratisch steigen im Vergleich zum linearen Aufwand einer einfachen Schleife. Generell gilt, dass eine teure Anweisung in einer Schleife n-mal statt einmal durchlaufen werden kann. Es ist daher wichtig auf die folgenden Dinge zu achten:

  • Lassen Sie alle Anweisungen die man vor oder nach der Schleife ausführen kann aus dem Schleifenblock draussen
  • Definieren und Initialisieren Sie Variablen wenn möglich ausserhalb der Schleife und verwenden Sie die Variablen wieder. Die Variable muss sonst jedes mal neu angelegt und wieder gelöscht werden.
  • Verzichten Sie auf unnötige Schleifendurchläufe
  • Seien Sie vorsichtig bei mehrfach geschachtelten Schleifen. Die Anzahl der Durchläufe kann theoretisch sehr schnell, sehr groß werden. Eine dreifach geschachtelte Schleife mit jeweils 1000 Durchläufen wird eine Milliarde mal ausgeführt!

Sprunganweisungen und Schleifenabbrüche mit continue und break Schlüsselwörtern

Java verfügt über zwei Möglichkeiten Schleifen in der Mitte von Ausführungsblöcken zu verlassen. Diese Programmiertechnik sollte man normalerweise vermeiden.

In manchen Fällen ist sie jedoch nützlich. Schleifenabbrüche werden durch die beiden folgenden Schlüsselwörter gesteuert:

  • break: die weitere Abarbeitung im Schleifenblock/rumpf wird abgebrochen. Das Programm verlässt die Schleife und nimmt die Abarbeitung hinter der Schleife wieder auf. die Abbruchkriterien der Schleife werden hier nicht mehr beachtet.
  • continue: die Abarbeitung des aktuellen Schleifendurchlaufs wird beendet. Die Schleife wird jedoch nicht verlassen. Die nächste Iteration wird geplant ausgeführt

Die break Anweisung ist schon vom switch Befehl her bekannt. Mit ihr kann man auch eine if Bedingung beenden.

Beispiel einer continue Anweisung:

... 
for (int i=0; i<= 100; i++) {
   if (i%2 == 0) continue;
   System.out.println("Die Zahl " + i + " ist ungerade");
}

Bei geraden Zahlen wird die Druckanweisung nach der if Anweisung nicht mehr ausgeführt. Die Schleifen werden jedoch alle durchlaufen.

for mit continue Befehl
Beispiel einer break-Anweisung:
...
for (int i=1; i<= 100; i++) {
   if (i== 32) break;
   System.out.println("Die Zahl " + i + " wurde bearbeitet");
}

Die Zahlen von 1 bis 31 werden ausgedruckt. Anschließend wird die Schleife abgebrochen. Die break und continue Anweisungen beziehen sich immer auf die nächst äussere Schleife. Mit Hilfe von Labels (Marken) kann man auch über mehrere geschachtelte Schleifen nach aussen springen.

for mit break Befehl

Verlassen von Blöcken mit Hilfe von Sprungzielen (Label)

Java erlaubt das Benennen von Blöcken mit Hilfe von "Labeln" (Marken). Mit ihnen kann man mit einer break-Anweisung beliebig viele Blöcke auf einmal verlassen.

Das folgende Beispiel zeigt wie man zum Label "Label1" springt um beide Schleifen auf einmal zu verlassen:

class MultiplizierenNaivLabel {
    public static void main(String[] args) {
        int a = 5;
        int b = 10;
        int result = 0;
        Label1: for (int i = 1; i <= a; i++) {
            System.out.println("Äussere Schleife i = " + i);
            for (int j = 1; j <= b; j++) {
                System.out.println("Innere Schleife j = " + j);
                if ((i == 3) && (j == 3)) break Label1;
                result++;
            }
        }
        System.out.println(a +"*"+b+" = "+result);
        System.out.println("Dieses Ergebnis ist falsch...");
    }  
}

Endlosenschleifen

Endlosenschleifen sind Schleifen die nicht terminieren. Ein Programm mit einer Endlosschleife sieht aus wie ein "hängendes" Programm. Es bindet jedoch sehr wahrscheinlich (mindestens) einen physikalischen Prozessor vollständig!

Anbei einige Beispiele mehr oder weniger leicht zu erkennende Endloschleifen:

while ( true) { /* Anweisung(en) */ }
...
for ( ; ; ) { /* Anweisung(en) */ }
...
int i=10;
while (i>5) i++;
...
int i=0;
while ( i!=99) {i++; i++;}

Der Übersetzer erkennt diverse einfache Endlosschleifen und meldet die darauf folgende Codezeile als "unreachable code".

Trivia: Welcher Obsthändler hat die Adresse 1 Infinite Loop, Cupertino, CA 95014?

Apple Hauptquartier

Felder (Arrays), Einführung

Die folgende Kurzüberblick von Feldern ist notwendig um die Übungen zu den Kontrollstrukturen zu lösen. Felder werden später noch einmal aufgegriffen und genauer behandelt.

Felder sind Datenbehälter mit einer oder mehreren Dimensionen um eine Anzahl primitive Datentypen gleichen Typs aufzunehmen.

Man kann auf die einzelnen Feldelemente wahlfrei, also direkt mit Hilfe eines Index zugreifen. Java benutzt rechteckige Klammern [] um auf einzelne Elemente eines Feldes zuzugreifen.

Initialisieren und Deklarieren von Feldern

Felder sind im Gegensatz zu Variablen mit primitiven Datentyp Objekte. Dies bedeutet, dass die die Variable nur aus einer Referenz besteht. Das Feld selbst muss dynamisch angelegt werden. Hierfür gibt es eine Reihe von Gründen.

  • Der Übersetzer kann nicht wissen wie groß ein Feld werden kann
  • Man kann mit einer Feldvariablen dynamisch neue Felder verwalten
  • Felder können sehr groß sein. Es ist oft klüger das Feld erst anzulegen wenn man es benötigt und die Größe kennt.

Die Deklaration von Feldern geschieht wie folgt:

int[] x;  // Anlegen einer Referenz, es ist noch keine Größe vorgeben, 
                    // es können noch keine Feldelemente benutzt werden<
double[] y; // Anlegen einer Referenz, es ist noch keine Größe vorgeben, 
                    //es können noch keine Feldelemente benutzt werden

Um das eigentliche Feld anzulegen muss der new Operator verwendet werden wie für reguläre Objekte auch um den Speicher zu allozieren. Dies geschieht wie folgt:

int [] x;
x = new int[4];
double[] y;
y = new double[200];

Jetzt zeigt x auf 4 Datenbehälter für Ganzzahlen. Der Zugriff auf die vier Elemente erfolgt über einen Index im Bereich von 0 (erster Wert) bis 3.

Zugriff auf Felder

Das Feld kann nun wie folgt belegt und bearbeitet werden:

int [] x = new int[4];

x[0] = 99;
x[1] = 88;
x[2] = x[0]+x[1];
x[3] = x[0]*x[1];

Bestimmen der Länge eines Feldes

...geschieht mit dem Attribut .length. Hier ein Beispiel:

int[] x;
int groesse;
...
groesse=x.length;

Erweiterte for Schleife (enhanced for-loop)

Seit Java 5 ist es möglich mit der for Schleife Felder komfortabler abzuarbeiten. Man kann mit einer Laufvariable alle Elemente des gegeben Feldes abarbeiten. Wie zum Beispiel:

int [] xxx = { 11, 22, 33, 44, 55};
for ( int position : xxx)  // für Element auf Position position in xxx[]
  System.out.println("Wert: " + position);

Wird das folgende Ergebnis liefern:

Wert: 11 
Wert: 22
Wert: 33
Wert: 44
Wert: 55

 Die Oracle Java Dokumentation erklärt die Einsatzmöglichkeiten und Grenzen der "enhanced for-loop".

Stefan Schneider Sun, 07/11/2010 - 16:28

Anonymous (not verified)

Mon, 11/24/2014 - 19:29

"int i = 10
while (i > 5 ) i++;"

fehlen nach der Bedingung nicht die { }?
Also {i++;} ?

Das  wurde hier in Syntax nicht vollständig korrekt dargestellt.

Die allgemeine Regel für Programmierblöcke ist: Man darf anstatt mehrerer Befehle in einem Block mit geschweiften Klammern immer auch einen einzelnen Befehl ohne geschweifte Klammer schreiben.

Die Syntax müßte wohl als regulärer Ausdruck so aussehen:

while (bedingung) [{ anweisung; {anweisung;}} , anweisung; ]

Anonymous (not verified)

Mon, 11/09/2015 - 19:18

Im Beispiel einer einfachen for Schleife steht, dass sie b mal durchlaufen wird, jedoch ist die Bedingung der for Schleife i

Stefan Schneider

Thu, 11/12/2015 - 18:26

In reply to by Anonymous (not verified)

Gute Überlegung.
Die Variable i dient zum Schleifenabruch und wird bei jedem Durchlauf imkrementiert. Sie startet bei 1 und endet bei b. Das sollte schon hinkommen.

Anonymous (not verified)

Fri, 11/15/2019 - 15:35

Ich glaube es fehlt bei der while, do-while und for Schleife immer ein Semikolon hinter Anweisung(en).
while (Bedingung) {Anweisung(en) ; }
do {Anweisung(en) ; } while (Bedingung);
for (Initialisierung; Bedingung; Veränderung) {Anweisung(en) ; }

3.2.1 Schleifentransformationen

3.2.1 Schleifentransformationen

Die drei Java Schleifentypen sind für unterschiedliche Zwecke entwickelt worden. Man Sie jedoch ineinander Überführen.

Hier sind einige Regeln

While Schleife in Do-While Schleife überführen

  von While Schleife zu do-While Schleife
Java
 while (Bedingung) {Block A }
do
   if (Bedingung)
      { Block A }
while ( Bedingung )
UML UML DIagramm do-While Schleife While Schleife

Do-While Schleife in While Schleife überführen

Eine Do-While Schleife führt einen Block mindestens einmal aus. Man kann Sie in eine While Schleife tranformieren in dem man den Block kopiert und voran stellt.

Vorsicht: Durch das duplizieren des Codes wird der Code schlechter wartbar!

  von Do-While Schleife zu While Schleife
Java
do
           
  Block A
while ( Bedingung )
Block A
while (Bedingung) {Block A }
UML UML DIagramm While Schleife do While Schleife

For-Schleife in eine While Schleife überführen 

  von For-Schleife zu While Schleife
Java
for (Initialiserung; Bedingung; Veränderung) {Block A}
Initialisierung
while (Bedingung) 
   {
     Block A;
     Veränderung
   }

While Schleife in For-Schleife überführen

While Schleifen kan man recht einfach in For-Schleifen überführen.

  von While-Schleife zu For-Schleife
Java
while (Bedingung) { Block A;}
for (; Bedingung;) {Block A}

 Eine solche Überführung wird aber recht selten angewendet, da der Initialisierungsanteil und die Veränderung der for Schleife irgendwo im Block A verborgen sind.

Do-While Schleife in For-Schleife überführen

Do-While Schleifen kan man auch recht einfach in For-Schleifen überführen.

  von While-Schleife zu For-Schleife
Java
do { Block A;} while (Bedingung)
Block A; for (; Bedingung;) {Block A;}

 Eine solche Überführung wird aber recht selten angewendet, da der Initialisierungsanteil und die Veränderung der for Schleife irgendwo im Block A verborgen sind.

Stefan Schneider Fri, 11/11/2016 - 19:14

Anonymous (not verified)

Sun, 12/11/2016 - 16:02

Hallo,

muss bei der Transformation von der For Schleife zu der While Schleife hinter die Initialisierung ein Semikolon?

Und mir ist aufgefallen, dass bei der letzten Transformation die Begriffe in der Tabelle nicht stimmen.Es handelt sich um eine Transformation von einer Do While Schleife zu einer For Schleife, aber in der Tabelle steht von einer While Schliefe zu einer While Schleife.

Mit freundlichen Grüßen!

3.3 Übungen

3.3 Übungen

Duke als Boxer

3.3.1 Übung: Schleifenterminierung

Welche der folgenden Schleifen terminieren?
    int i = 1, j = 1;
    do {
      i = i + j;
      j++;
    } while (i < 200);
b)  
    int i = 1, j = 20;
    while (i + j < i) {
      i = i + 2;
      j--;
    }
c)  
    int i = 1, j = 20;
    while (i + j > i) {
      i = i + 2;
      j--;
    }
d)  
    int i = 100, j = 27;
    while (i != j) {
      i = i / 2;
      j = j / 3;
    }

3.3.2 Übung: Ziffern einer Zahl

Schreiben Sie ein Java-Programm, das eine positive ganze Zahl n einliest und
feststellt, aus wie vielen Ziffern sie besteht.

Hilfestellung:

Einlesen von Zahlenwerten beim Starten von der Kommandozeile als Option wie zum Beispiel:

$ java Main 17 19

erfolgt durch Einlesen von Zeichenketten und Umwandlung in Zahlen. Anbei ein Rahmenprogramm welches das Problem löst:

public class Main {
    public static void main(String[] args) {
    int firstArg=0;
    int stellen=0;
    if (args.length > 0) {
        try {
            firstArg = Integer.parseInt(args[0]);
        } catch (NumberFormatException e) {
            System.err.println("Argument muss Ganzzahl sein");
            System.exit(1);
            }
        System.out.println("Die Zahl firstArg = " + firstArg + " wurde eingelesen");
        // Implementierung gehört hierher
        System.out.println(" Die Zahl " + firstArg + " hat " + stellen + " Stellen!");
    }
}

Eine andere Möglichkeit besteht darin, dass man das Programm startet und bei Bedarf eine Eingabe von der Konsole anfordert.

Achten Sie hier auf die fettgedruckte Importanweisung der Klasse Scanner. Sie ist notwendig um die Klasse zu benutzen:

import java.util.Scanner;
public class Eingabe {
    public static void main(String[] args) {
        Scanner eingabe = new Scanner(System.in);
        System.out.print("Geben Sie die erste Zahl ein: ");
        int zahl1 = eingabe.nextInt();
        System.out.print("Geben Sie die zweite Zahl ein: ");
        int zahl2 = eingabe.nextInt();
        System.out.println("Ergebnis: " + zahl1);
        System.out.println("Ergebnis: " + zahl2);
    }
}

Das Programm Eingabe kann man auf der Konsole wie folgt bedienen:

$ java Eingabe
Geben Sie die erste Zahl ein: 34
Geben Sie die zweite Zahl ein: 56
Ergebnis: 34
Ergebnis: 56 

Am einfachsten ist es die Eingaben in der ersten Zeile des Programmes hart zu belegen...

3.3.3 Übung: Quersumme

Schreiben Sie ein Java-Programm, das die Quersumme einer positiven ganzen Zahl berechnet und ausgibt.

Beispiel: Zahl 4711 --> Quersumme 13.

3.3.4 Übung: Schleifentransformation

Wandeln Sie die folgende for-Schleife in eine while-Schleife, sowie in eine
do-while-Schleife um:
  int s = 0;
  for(;;) {
    int x = System.in.read(); //liest ein Zeichen von der Konsole
    if (x < 0) break;
    s = s + x;
}
Hinweis: Da Zeichen intern durch ganze Zahlen codiert werden, erlaubt Java, mit
Zeichen zu rechnen. char-Werte sind eine Teilmenge der int-Werte.

Der Quellcode wird erst ausführbar wenn man einen potentiellen IO Fehler behandelt. Siehe

...
import java.io.IOException;
...
            try {
                x = System.in.read(); //liest ein Zeichen von der Konsole
            } catch (IOException ex) {
                System.out.println(" Java IO Exception");
            }

3.3.5 Übung: Zahlenstatistik

Schreiben Sie ein Java-Programm, das eine Zahlenfolge liest und ihren
größten und kleinsten Wert sowie ihren Mittelwert berechnet und ausgibt.

Tipp: Die Wertebereiche für die Zahlen sind Ihnen freigestellt. Der Durchschnittswert sollte korrekt sein

Hilfestellung:

Einlesen eines Feldes (Arrays) von der Kommandozeile

package s1.block3;
public class Zahlenstatistik {
    public static void main(String[] args) {
        int feld[];
        if (args.length > 0) {
            feld = new int[args.length];
            try {
                for (int i=0;i<args.length;i++) {
                    feld[i] = Integer.parseInt(args[i]);
                    // Einlesen der Kommandozeilenargumente und umwandeln in Ganzzahlen
                    }
            } catch (NumberFormatException e) {
                System.err.println("Argument muss Ganzzahl sein");
                System.exit(1);
                }
        // Ab hier steht das Feld mit allen Werten zur Verfügung
    }
}

3.3.6 Übung: Primfaktorzerlegung

Schreiben Sie ein Java-Programm, das eine positive ganze Zahl n einliest und in ihre Primfaktoren zerlegt.
Beispiel:
  • Die Zahl 100 besteht aus den Primfaktoren 2, 2, 5, 5;
  • die Zahl 252 aus den Primfaktoren 2, 2 , 3, 3, 7.

Option: Optimieren sie Ihr Programm auf die kürzeste Laufzeit. Verwenden Sie hierzu den Nanotimer von JDK 6.0 wie folgt:

package s1.block3;
public class Primzahlzerlegung {
    public static void main(String[] args) {
        int firstArg = 0;
        int p=0;
        long time;
        time= System.nanoTime();
        if (args.length > 0) {
            try {
                p = Integer.parseInt(args[0]);
            } catch (NumberFormatException e) {
                System.err.println("Argument muss Ganzzahl sein");
                System.exit(1);
                }
            System.out.println("Eingelesene Zahl: " + p);
        }
    // Die Eingabe der Kommandozeile liegt als Variable p vor.
    // Implementierung ...
    time= System.nanoTime() - time;
    System.out.println("Zeit in m: "+ time);
    }
}

Überlegungen:

  • Welche Zeit wird hier gemessen?
  • Was hat ausser dem Algorithmus noch Einfluss auf die Ausführungsgeschwindigkeit?
  • Sind Ihre Messungen wiederholbar?
  • Welche Risiken hat eine Optimierung des Algorithmus?
  • Wie wichtig ist die Schnelligkeit eines Programms?

3.3.7 Übung: Codevereinfachung

Vereinfachen Sie folgende Codestücke:
 
a)  
// Annahme: j >= 0
    i = 0;
    while (i != j) i++;
b)  
  while (a < b) {
      c = a;
      a = b;
      b = c;
    }

3.3.8 Übung: Wochentagberechnung

Lesen Sie ein Datum in Form dreier Zahlen für den Tag, den Monat und das Jahr sowie eine weitere Zahl zwischen 0 und 6 ein, die den Wochentag (Sonntag bis Samstag) des 1. Januars dieses Jahres darstellt. Berechnen Sie den Wochentag des eingelesenen Datums und geben Sie diesen aus. Sie können davon ausgehen, dass die Eingaben korrekt sind. Berücksichtigen Sie auch Schaltjahre.

3.3.9 Übung: Überlaufprüfung

Lesen Sie zwei 32 Bit Ganzahlen (int) a und b ein und prüfen Sie, ob bei ihrer Addition ein Überlauf stattfindet, also eine Summe entstehen würde, die größer als 231 - 1 oder kleiner als  -231   ist.

3.3.10 Übung: Schnitt zweier Linien

Lesen Sie die Endpunkte zweier horizontaler oder vertikaler Linien in Form ihrer x- und y-Koordinaten ein und prüfen Sie, ob sich die beiden Linien schneiden. Die beiden Linien befinden sich in einem zweidimensionalen kartesischen System:

Hinweis: Wenn es einen Schnittpunkt gibt ist er (c,b)

3.3.11 Übung: Codevereinfachung

Vereinfachen Sie folgende Codestücke:

a)  

    if (b == 0)
      a = 2 * c;
    else
      if (c != 0)
        a = a * b + 2 * c;
      else
        a = a * b;

b)  

    if (x < 0 && y < 0)
      a = x * y;
    else
      if (x < 0)
        a = x * (-y);
      else
        if (y > 0)
          a = (-x) * (-y);
        else
          a = x * (-y);

3.3.12 Übung: Dreiecksbestimmung

Schreiben Sie ein Java-Programm, das die Seitenlängen eines Dreiecks einliest und prüft, ob es ein

  • gleichseitiges
  • gleichschenkeliges
  • rechtwinkeliges
  • sonstiges gültiges
  • ungültiges Dreieck ist.

Ein Dreieck ist ungültig, wenn die Summe zweier Seitenlängen kleiner oder gleich der dritten Seitenlänge ist. Beachten Sie, dass ein Dreieck sowohl rechtwinkelig als auch gleichschenkelig sein kann!

3.3.13 Übung: Sortieren

Schreiben Sie ein Java-Programm, das 3 Zahlen a, b und c einliest und sie in sortierter Reihenfolge wieder ausgibt. Im Rahmenprogramm muss die letzte Zeile durch einen Sortieralgorithmus und Ausgaben ersetzt werden.

public class Sortieren {
   public static void main(String[] args) {
       int a = 0;
       int b = 0;
       int c = 0;

      if (args.length > 2 ) {
         try {
            a = Integer.parseInt(args[0]);
            b = Integer.parseInt(args[1]);
            c = Integer.parseInt(args[2]);
         } catch (NumberFormatException e) {
            System.err.println("Argument muss Ganzzahl sein");
            System.exit(1);
         }
      }
      System.out.println("Eingelesene Werte: " + a + ", " + b + ", " + c);

      System.out.println("Anstatt dieser Zeile muss ein Sortieralgorithmus implementiert werden...";
   }
}

3.3.14 Übung: Plausibilitätsprüfung

Lesen Sie ein Datum in Form dreier Zahlen für den Tag, den Monat und das Jahr ein. Prüfen Sie, ob es sich um ein gültiges Datum handelt. Berücksichtigen Sie auch Schaltjahre.

Hinweis: Gregorianischer Kalender: Ein Jahr ist ein Schaltjahr, wenn es durch 4 teilbar ist. Jahre, die durch 100, aber nicht durch 400 teilbar sind, sind keine Schaltjahre.

3.3.15 Übung: Textverschlüsselung

Schon im alten Rom verschlüsselte man Nachrichten. Ein einfaches Verschlüsselungsverfahren ist von Julius Cäsar überliefert. Es verschiebt jeden Buchstaben der Nachricht um einen fixen Wert n. Ist n gleich 2, so wird 'A' auf 'C', 'B' auf 'D' und 'Z' auf 'B' verschoben. Ziffern und Sonderzeichen werden nicht verschoben. Schreiben Sie ein Programm, das eine Zahl n und einen beliebigen Text liest und ihn nach obigem Verfahren verschlüsselt wieder ausgibt.

3.3.16 Übung: Verzweigungen

Schreiben Sie ein Java-Programm, das drei Werte x, y und z einliest und prüft, ob

  • x, y und z nicht lauter gleiche Werte enthalten,
  • x, y und z lauter verschiedene Werte enthalten,
  • mindestens zwei Werte gleich sind.

3.3.17 Übung: Häufigkeit von Zeichen

Schreiben Sie ein Programm, das einen Text liest und die Häufigkeit der darinvorkommenden Zeichen berechnet. Geben Sie die Zeichenhäufigkeit als Histogramm aus. Beispiel:

a ****
b **
c ********
...

 Hinweis: Dieses Programm setzt Kenntnisse von Feldern vorraus die erst später behandelt werden

3.3.18 Übung: Weckzeiten implementieren

Implementieren Sie zwei logische Tests für die Kontrolle der Weckzeit einer Uhr.

Diese Aufgabe baut auf der Übung zur Implementierung einer analogen Uhr auf.

Sie benötigen 3 Klassen für diese Aufgabe die alle im gleichen Verzeichnis stehen müssen:

  • Klasse Zeiger (Lösung von 2.4.11) oder Ihre eigene Implementierung. Diese Klasse muss nicht modifiziert werden.
  • Klasse WeckerUhr (weiter unten). Die Klasse muss nicht modifiziert werden. Die Klasse enthält die main() Methode. Es muss immer diese Klasse (java WeckerUhr) zum Starten der Anwendung verwendet werden!
  • Klasse Weckzeit: Diese Klasse muss in der Übung modifiziert werden. Die Vorlage der Klasse ist bereits übersetzbasr und ausführbar.

Es reicht die Klasse WeckerUhr zu übersetzen. Die beiden anderen Klassen werden automatisch rekursiv mitübersetzt.

Nach dem Starten der Anwendung mit java WeckerUhr erscheint das folgende Fenster:

Weckeruhr im normalen Zustand

Uhr im normalen Zustand

Weckeruhr beim Klingeln

Uhr beim Klingeln (roter pulsierender Kreis)

Erste Aufgabe: Kontrolle der Wertebereiche

Die Klasse Weckzeit enthält eine Methode korrekteWeckzeit(int h, int m, int s). Diese Methode prüft die Eingaben des GUI auf korrekte Wertebereiche. Das Ergebnis wird in einer boolschen Variablen result gespeichert. Die Vorlage liefert bei allen Eingaben false. Belegen Sie die Variable so, dass vernünftige Werte akzeptiert werden.

Fragen:

  • Was sind vernünftige Werte für Stunde (h), Minute(m), Sekunde(s)?
  • Welche logische Verknüpfung muss man nutzen wenn alle Werte im korrekten Bereich sein sollen?

Der Erfolg Ihrer Implementierung können Sie daran erkennen, dass die Eingaben des GUI nach dem klicken des OK Knopfs in das Anzeigenfeld übernommen worden sind.

Zweite Aufgabe: Bestimmen der richtigen Zeitpunkte zum Wecken

Das Lösen der ersten Teilaufgabe ist eine Voraussetzung für den zweiten Teil (Ihr Wecker funktioniert sonst nur Mittags und um Mitternacht...)

Das Hauptprogramm der Klasse WeckerUhr ruft in der aktuellen Implementierung viermal pro Sekunde die Methode klingeln() auf um zu kontrollieren ob der Wecker klingeln soll. Ändern Sie die Referenzimplementierung derart, dass nur nach der eingebenen Weckzeit für eine bestimmte Periode geklingelt wird.

Die zu modifizierende Referenzimplementierung:

result = (aktS%10 == 0);

lässt den Wecker bei allen Sekundenwerten die durch 10 teilbar sind für eine Sekunde klingeln. 

Die Variablen aktH, aktM, aktS enthalten die aktuelle Zeit. Die Variablen wzh, wzm, wzs enthalten die Weckzeit.

Fragen:

  • Wie lange soll der Wecker klingeln?
  • (Zur Verfeinerung) Wie kann man gewährleisten, dass der Wecker auch um 11:59:50 für 15 Sekunden klingelt?

Klasse Weckzeit

package s1.block3;
public class Weckzeit {
    int wzh = 0; // Weckzeit in Stunden
    int wzm = 0; // Weckzeit in Minuten
    int wzs = 0; // Weckzeit in Sekunden
    public void setzeWeckzeit(int hh, int mm, int ss) {
        if (korrekteWeckzeit(hh,mm,ss)) {
            wzh = hh;
            wzm = mm;
            wzs = ss;
        }
    }
    public boolean korrekteWeckzeit(int h, int m, int s) {
        boolean result;
        // benutzen die Variablen h,m,s um eine gültige Zeit zu bestimmen
        result = false;
        return result;
    }
    public boolean klingeln(int aktH, int aktM, int aktS) {
        boolean result;
        // benutzen die Variablen der aktuellen Zeit aktH (Stunde),
        // aktM (Minute), aktS (Sekunde) und die Weckzeit wzmh, wzm, wzs
        // um zu bestimmern ob der Wecker klingeln soll
        // Verbessern Sie diese Zuweisung
        // In der aktuellen Implementieren klingelt der Wecker
        // alle 10 Sekunden für 1 Sekunde
        result = (aktS%10 == 0);
    return result;
    }
}

Klasse WeckerUhr

package s1.block3;
/*
 * Zeichnen einer analogen Uhr in einem JFrame
 */
import java.awt.*;
import java.awt.event.*;
import java.util.Calendar;
import javax.swing.*;
/**
 *
 * @author sschneid
 */
public class WeckerUhr extends JPanel {

int sekunde = 0;
int minute = 0;
int stunde = 0;
boolean klingeln = false;
String tzString; // aktuelle Zeitzone
int initialHeight;
float zoom = 1;
boolean an = false;
JFrame hf; // Das Fenster der Anwendung
Container myPane;
JTextField h,m,s;
JButton eingabe;
Weckzeit wz;
int klingelRadius = 0;
/**
* Konstruktor der Klasse. Er initialisiert die Grafik
*/
public WeckerUhr() {
wz = new Weckzeit();
hf = new JFrame("Uhr");
h = new JTextField(2);
m = new JTextField(2);
s = new JTextField(2);
eingabe = new JButton("OK");
eingabe.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int stunde, minute, sekunde;
try {
stunde = Integer.parseInt(h.getText());
minute = Integer.parseInt(m.getText());
sekunde = Integer.parseInt(s.getText());
}
catch (NumberFormatException ex) {
// Es wurde keine korrekte Zahl eingegeben
stunde = -100;
minute = -100;
sekunde= -100;
}
wz.setzeWeckzeit(stunde,minute,sekunde);
}
});
// Beenden der Anwendung bei Schließen des Fenster
hf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Aufbau des Contentpanes
myPane = hf.getContentPane();
myPane.add(this, BorderLayout.CENTER);
JPanel wzPanel = new JPanel(new GridLayout(1,0));
wzPanel.add(new JLabel("hh:mm:ss"));
wzPanel.add(h);
wzPanel.add(m);
wzPanel.add(s);
wzPanel.add(eingabe);
myPane.add(wzPanel,BorderLayout.SOUTH);
// Erzeuge einen Menüeintrag zum Beenden des Programms
JMenuBar jmb = new JMenuBar();
JMenu jm = new JMenu("Datei");
JMenuItem exitItem = new JMenuItem("Beenden");
exitItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
jm.add(exitItem);
jmb.add(jm);
hf.setJMenuBar(jmb);
hf.pack();
// Das JFrame sichtbar machen
// Gewünschte Größe setzen
// 1. Parameter: horizontale Größe in Pixel
// 2. Parameter: vertikale Größe in Pixel
hf.setSize(2 * Zeiger.maxRadius + 80,
2 * Zeiger.maxRadius + 130);
hf.setVisible(true);
hf.setAlwaysOnTop(true);
initialHeight = getHeight();
}
/**
* Hauptprogramm der Anwendung. Es werden keine Eingabeparameter benötigt
* @param args dieser Parameter wird nicht ausgewertet
*/
public static void main(String[] args) {
WeckerUhr dieUhr = new WeckerUhr();
dieUhr.tickTack();

}
/**
* Diese Methode verwaltet den Zeitgeber-thread. Dieser Thread belegt
* die statischen Variablen der Uhrzeit neu
*/
public void tickTack() {
try {
boolean blinken = false;
while (true) {
Thread.sleep(250); // Schlafe x Millisekunden
// Hole Systemzeit und belege statische Variablen
Calendar call = Calendar.getInstance();
tzString = call.getTimeZone().getDisplayName();
sekunde = call.get(Calendar.SECOND);
minute = call.get(Calendar.MINUTE);
stunde = call.get(Calendar.HOUR);
klingeln = wz.klingeln(stunde,minute,sekunde);
if (blinken){
klingelRadius=100;
}
else {
klingelRadius=30;
}
blinken = !blinken;
repaint();
}
} catch (InterruptedException e) {
System.out.println(
"Die Anwendung wird wegen einer Ausnahme beendet");
}
}
/**
* Überladene Paintmethode. Sie führt alle Zeichenoperationen im Panel aus
* @param g vom Laufzeitsystem übergebenes Graphikobjekt.
*/
@Override
public void paint(Graphics g) {
   super.paint(g);
   zoom = (float)getHeight()/(float)initialHeight;
   int maxRadius = Zeiger.maxRadius;
   int xCenter = (int)(maxRadius*zoom) + 40;
   int yCenter = (int)(maxRadius*zoom) + 20;
   float fontSize = g.getFont().getSize2D();
   int charCenterOffSet = (int)(fontSize/2);
   String timeString = stunde + ":" + minute + ":" + sekunde
      + " " + tzString;
   String klingelString;
   klingelString = wz.wzh +":"+wz.wzm + ":" +wz.wzs +" klingeln";
   if (klingeln) {
      g.setColor(Color.red);
      g.fillOval(xCenter-((int)(zoom*klingelRadius/2)),
         yCenter-((int)(zoom*klingelRadius/2)),
         (int)(klingelRadius*zoom),
         (int)(klingelRadius*zoom));
   }
   // Zeichne Uhrenhintergrung und Koordinatensystem
   g.setFont(g.getFont().deriveFont(fontSize));
   g.setColor(Color.BLACK); // Farbe
   g.drawArc(xCenter - 5, yCenter - 5, 10, 10, 0, 360);
   g.drawLine(xCenter, yCenter, xCenter + 40, yCenter);
   g.drawLine(xCenter, yCenter, xCenter, yCenter + 40);
   g.drawString("X", (int)(xCenter + 45*zoom),
   yCenter + +charCenterOffSet);
   g.drawString("Y", xCenter - charCenterOffSet,
      (int)(yCenter + 55*zoom));
   g.drawString("12",xCenter - charCenterOffSet,
      (int)(yCenter - maxRadius*zoom));
   g.drawString("3", (int)(xCenter + maxRadius*zoom),
      yCenter + charCenterOffSet);
   g.drawString("6", xCenter - charCenterOffSet,
      (int)(yCenter + 2*charCenterOffSet+maxRadius*zoom));
   g.drawString("9", (int)(xCenter - maxRadius*zoom - charCenterOffSet),
      yCenter + charCenterOffSet);
   // Zeichne aktuelle Zeit zum Debuggen
   g.drawString(timeString, 0,
      (int)(yCenter + maxRadius*zoom));
   // Zeichne Weckzeit zum Debuggen
   g.drawString(klingelString, 0, (int)(yCenter + maxRadius*zoom + 25));
   // Zeichne Stundenzeiger
   g.setColor(Color.BLACK);
   g.drawLine(xCenter, yCenter,
   (int)(xCenter + Zeiger.stundeX(stunde)*zoom),
   (int)(yCenter + Zeiger.stundeY(stunde)*zoom));
   g.drawString("h["
      + Zeiger.stundeX(stunde)
      + "," + Zeiger.stundeY(stunde) + "]",
      0, (int)(yCenter + maxRadius*zoom - (3*fontSize)));
   // Zeichne Minutenzeiger
   g.setColor(Color.RED);
   g.drawLine(xCenter, yCenter,
      (int)(xCenter + Zeiger.minuteX(minute)*zoom),
      (int)(yCenter + Zeiger.minuteY(minute)*zoom));
   g.drawString("m["
         + Zeiger.minuteX(minute) + ","
         + Zeiger.minuteY(minute) + "]", 0,
      (int)(yCenter + maxRadius*zoom - (2*fontSize)));
   // Zeichne Sekundenzeiger
   g.setColor(Color.BLUE);
   g.drawLine(xCenter, yCenter,
      (int)(xCenter + Zeiger.sekundeX(sekunde)*zoom),
      (int)(yCenter + Zeiger.sekundeY(sekunde)*zoom-fontSize));
   g.drawString("s["
         + Zeiger.sekundeX(sekunde) + ","
         + Zeiger.sekundeY(sekunde) + "]", 0,
      (int)(yCenter + maxRadius*zoom - fontSize));
   }
}
Stefan Schneider Sun, 07/11/2010 - 16:30

Anonymous (not verified)

Thu, 11/09/2017 - 12:11

die Zahl 252 aus den Primfaktoren 3, 3, 4, 7.
4 ist keine Primzahl hier müsste 2x die 2 hin

3.4 Lösungen

3.4 Lösungen

3.4.1 Schleifenterminierung

Welche der folgenden Schleifen terminieren?
 
a)  Terminiert
    int i = 1, j = 1;
    do {
      i = i + j;
      j++;
    } while (i < 200);
b)  Terminiert: Schleife wird nicht durchlaufen
    int i = 1, j = 20;
    while (i + j < i) {
      i = i + 2;
      j--;
    }
c)  Terminiert
    int i = 1, j = 20;
    while (i + j > i) {
      i = i + 2;
      j--;
    }
d)  Terminiert
    int i = 100, j = 27;
    while (i != j) {
      i = i / 2;
      j = j / 3;
    }

3.4.2 Ziffern einer Zahl

public class Main {
    public static void main(String[] args) {
    int firstArg = 0;
    int stellen = 0;
    int a;
    if (args.length > 0) {
        try {
            firstArg = Integer.parseInt(args[0]);
        } catch (NumberFormatException e) {
            System.err.println("Argument muss Ganzzahl sein");
            System.exit(1);
            }
        System.out.println("Die Zahl firstArg = " + firstArg + " wurde eingelesen");
        a = firstArg;            while (a !=0) {                  stellen++;                  a/=10;                 }
        }
        System.out.println(" Die Zahl " + firstArg + " hat " + stellen + " Stellen!");
    }
}

3.4.3 Quersumme

package s1.block3;
public class Quersumme {

    public static void main(String[] args) {
        int firstArg = 0;
        int querSumme = 0;
        int a;
        if (args.length > 0) {
            try {
                firstArg = Integer.parseInt(args[0]);
            } catch (NumberFormatException e) {
                System.err.println("Argument muss Ganzzahl sein");
                System.exit(1);
                }
            System.out.println("Die Zahl " + firstArg + " wurde eingelesen");
            a = firstArg;
            while (a !=0) {
                querSumme+= a%10;
                a/=10;
            }
        System.out.println("Die Zahl " + firstArg + " hat die Quersumme " + querSumme + "!");
        }
    }
}

3.4.4 Schleifentransformation

package s1.block3;
import java.io.IOException;

public class Schleifentransformation {
    public static void main(String[] args) {
        int s = 0;
        while(true);
        }
}

3.4.5 Zahlenstatistik

package s1.block3;
public class Zahlenstatistik {
    public static void main(String[] args) {
        int feld[];
        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        float average = 0;
        if (args.length > 0) {
            feld = new int[args.length];
            try {
                for (int i=0;i<args.length;i++) {
                    feld[i] = Integer.parseInt(args[i]);
                    if (feld[i] < min) {min=feld[i];}
                    if (feld[i] > max) {max=feld[i];}
                    average += feld[i];
                    }
            } catch (NumberFormatException e) {
                System.err.println("Argument muss Ganzzahl sein");
                System.exit(1);
                }
            average= average/(float)args.length;
            System.out.println("Anzahl eingelesene Werte: " + args.length);
            System.out.println("Kleinster Wert: " + min);
            System.out.println("Größter Wert: " + max);
            System.out.println("Durchschnitt: " + average);
    }
}
}

3.4.6 Primzahlzerlegung

package s1.block3;
public class Primzahlzerlegung {
    public static void main(String[] args) {
        int firstArg = 0;
        int p=1;
        long time;
        time= System.nanoTime();
        if (args.length > 0) {
            try {
                p = Integer.parseInt(args[0]);
            } catch (NumberFormatException e) {
                System.err.println("Argument muss Ganzzahl sein");
                System.exit(1);
                }
            System.out.println("Eingelesene Zahl: " + p); 
            while (p>1) {
                for (int i=2; i<= p; i++) {
                    while (p%i == 0) {
                        System.out.println("Primfaktor: " + i);
                        p /= i;
                    }
                }
            }
        }
    time= System.nanoTime() - time;
    System.out.println("Zeit in m: "+ time);
    }
}

Optimiert:

// Gute Kandidaten
// 2^30-1 = 1073741823
// 2^31-1 = 2147483647
public class Main {

    public static void Primzahlzerlegung(String[] args) {
        int firstArg = 0;
        long p = 1;
        long time;
        time = System.nanoTime();
        if (args.length > 0) {
            try {
                p = Integer.parseInt(args[0]);
            } catch (NumberFormatException e) {
                System.err.println("Argument muss Ganzzahl sein");
                System.exit(1);
            }
            long i = 2;
            System.out.println("Eingelesene Zahl: " + p);
            while ((p > 1) && (i <= p)) {
                if (p % i == 0) {
                    System.out.println("Primfaktor: " + i);
                    p /= i;
                    i = 2;
                } 
                else {
                    if (i > Math.sqrt(p)) {
                        // Beende Suche wenn Wurzel von P erreicht
                        i = p;
                    } else {
                        i++;
                    }
                }
            }
        }
        time = System.nanoTime() - time;
        System.out.println("Zeit in m: " + time);
    }
}

3.4.7 Codevereinfachung

a.)

// Annahme: j >= 0
    i = j;
b)  
a = (a<b) ? b : a;
b = (a<b) ? a : b;
c = (a<b) ? a : c;

3.4.8 Wochentagberechnung

Hier wurde nur der julianische Kalender implementiert nicht der (aktuelle) gregorianische Kalender.

package s1.block3;
public class WochentagBerechnung {
    public static void main(String[] args) {
       int monatOffSet[] = new int[13];
       int tag = 0;
       int monat = 0;
       int jahr=0;
       int wochentag0101startJahr= 6;
       int jahresOffSet; // Bedeutung siehe Feld tagText
       String[] tagText = {"Sonntag", "Montag", "Dienstag", "Mittwoch",
                            "Donnerstag", "Freitag", "Samstag"};
int wochentag; int startJahr = 2010; int anzSchaltJahre; tagText[0] = "Sonntag"; tagText[1] = "Montag"; tagText[2] = "Dienstag"; tagText[3] = "Mittwoch"; tagText[4] = "Donnerstag"; tagText[5] = "Freitag"; tagText[6] = "Samstag"; if (args.length > 1 ) { try { tag = Integer.parseInt(args[0]); monat = Integer.parseInt(args[1]); jahr = Integer.parseInt(args[2]); wochentag0101startJahr = Integer.parseInt(args[3]); } catch (NumberFormatException e) { System.err.println("Argument muss Ganzzahl sein"); System.exit(1); } monatOffSet[1] = 0; monatOffSet[2] = (monatOffSet[1]+31)%7; monatOffSet[3] = (monatOffSet[2]+28)%7; monatOffSet[4] = (monatOffSet[3]+31)%7; monatOffSet[5] = (monatOffSet[4]+30)%7; monatOffSet[6] = (monatOffSet[5]+31)%7; monatOffSet[7] = (monatOffSet[6]+30)%7; monatOffSet[8] = (monatOffSet[7]+31)%7; monatOffSet[9] = (monatOffSet[8]+31)%7; monatOffSet[10] = (monatOffSet[9]+30)%7; monatOffSet[11] = (monatOffSet[10]+31)%7; monatOffSet[12] = (monatOffSet[11]+30)%7; jahresOffSet = (monatOffSet[12]+31)%7; if (monat>2) anzSchaltJahre=jahr/4-startJahr/4; else anzSchaltJahre=(jahr-1)/4-startJahr/4; wochentag = (monatOffSet[monat] + tag - 1 + wochentag0101startJahr + (jahr-startJahr)*jahresOffSet + anzSchaltJahre )%7; System.out.println ("Der 1.1."+ startJahr+" war ein " + tagText[wochentag0101startJahr]); System.out.println ("Der "+ tag + "."+monat+"."+jahr+ " ist ein " + tagText[wochentag]); // Optional: Datumsbestimmung mit Hilfe der Java Infrastruktur Calendar myCal = new GregorianCalendar(jahr,monat+1,tag); System.out.println ("Gregorianischer Java Kalender:"); System.out.println ("Der " + tag + "." + monat + "." + jahr+ " ist ein " + tagText[myCal.get(Calendar.DAY_OF_WEEK)]); } } }

3.4.9 Überlauf

Bei der Addition von Ganzzahlen gibt es keine Überlaufsprüfung.

package s1.block3;
public class Ueberlauf {
  public static void main(String[] args) {
       int a = 0;
       int b = 0;
       int result;
       if (args.length > 1 ) {
        try {
           a = Integer.parseInt(args[0]);
           b = Integer.parseInt(args[1]);
        } catch (NumberFormatException e) {
            System.err.println("Argument muss Ganzzahl sein");
            System.exit(1);
            }
        }
       System.out.println("Eingelesene Werte: " + a + ", " + b);
       result = a + b;
       if ((result < a) || (result < b))
           System.out.println("Überlauf!");
       else
           System.out.println(a + " + " + b + " = " + result);
    }
}

3.4.10 Schnitt zweier Linien

Eingabereihenfolge der Parameter ist: a1, a2, b ,c , d1, d2

package s1.block3;
public class GeradenSchnitt {

    public static void main(String[] args) {
        double a1 = 0.0;
        double a2 = 0.0;
        double b  = 0.0;
        double c  = 0.0;
        double d1 = 0.0;
        double d2 = 0.0;
        double tmp;
        if (args.length > 5) {
            try {
                a1 = Double.parseDouble(args[0]);
                a2 = Double.parseDouble(args[1]);
                b  = Double.parseDouble(args[2]);
                c  = Double.parseDouble(args[3]);
                d1 = Double.parseDouble(args[4]);
                d2 = Double.parseDouble(args[5]);
            } catch (NumberFormatException e) {
                System.err.println("Argument muss Fließkommazahl sein");
                System.exit(1);
            }
        }
        System.out.println("Linie 1: (" + a1 + "," + b + ") bis ("
                + a2 + "," + b + ")");
        System.out.println("Linie 2: (" + c + "," + d1 + ") bis ("
                + c + "," + d2 + ")");
        // Schnittpunkt ist (c,b)
        // Sortieren von a1, a2 .
        if (a1 > a2) {
            tmp = a1; a1 = a2; a2 = tmp;}
        // Sortieren von d1, d2 .
        if (d1 > d2) {
            tmp = d1;
            d1 = d2;
            d2 = tmp;
        }
        System.out.println("Nach sortieren...");
        System.out.println("Linie 1: (" + a1 + "," + b + ") bis ("
                + a2 + "," + b + ")");
        System.out.println("Linie 2: (" + c + "," + d1 + ") bis ("
                + c + "," + d2 + ")");
        if ((a1 <= c) && (c <= a2) && (d1 <= b) && (b <= a2)) 
            System.out.println("Die beiden Strecken schneiden sich");
        else
          System.out.println("Die beiden Strecken schneiden sich nicht");
    }
}

Beispielläufe

java s1.block3.GeradenSchnitt 3.1 8.2 4.0 5.2 11.1 4.9

Linie 1: (3.1,4.0) bis (8.2,4.0)
Linie 2: (5.2,11.1) bis (5.2,4.9)
Nach sortieren...
Linie 1: (3.1,4.0) bis (8.2,4.0)
Linie 2: (5.2,4.9) bis (5.2,11.1)
Die beiden Strecken schneiden sich nicht

java block3.GeradenSchnitt 3.1 8.2 6.0 5.2 11.1 4.9

Linie 1: (3.1,6.0) bis (8.2,6.0)
Linie 2: (5.2,11.1) bis (5.2,4.9)
Nach sortieren...
Linie 1: (3.1,6.0) bis (8.2,6.0)
Linie 2: (5.2,4.9) bis (5.2,11.1)
Die beiden Strecken schneiden sich

3.4.11 Codevereinfachung

a)

a = a * b + 2 * c;

b)

Wahrheitstafel
  x<0 x>=0
y<0 a=x*y a=x*(-y)
y>=0 a = x*(-y) a=(-x)*(-y)

 

y = ((x<0) && (y<0)) ?  y : -y;
x = ((y>=0) && (x>=0))? -x :  x;
a = x * y;;

3.4.12 Dreiecksbestimmung

package s1.block3;
public class Dreiecksvergleich {
public static void main(String[] args) {
    float x[] = new float[3];
    int j;
    if (args.length > 2 ) {
        try {
           float a;
           for (int i=0; i <3; i++) {
               x[i] = Float.parseFloat(args[i]);
               j=i; // Sortiere Eingabe in steigender Reihenfolge
               while (j>0)
                   if (x[j] < x[j-1]) {
                        a = x[j-1];
                        x[j-1] = x[j];
                        x[j]= a;
                        j--; }
                    else j=0;
                   }
               }
        catch (NumberFormatException e) {
            System.err.println("Argument muss Fließkommazahl sein");
            System.exit(1);
            }
        System.out.println("Eingebene Werte: " + x[0] + "; "
                                               + x[1] + "; "
                                               + x[2]);
        // Die folgenden Vergleiche verlassen sich darauf, dass das Feld
        // aufsteigend sortiert ist.
        if (x[0]+x[1]<=x[2])
            System.out.println("Dreieck ist ungültig");
        else if (x[0] == x[2])
            System.out.println("Dreieck ist gleichseitig (und gleichschenkelig)");
        else {
            if ((x[0] == x[1]) || (x[1] == x[2]))
                System.out.println("Dreieck ist gleichschenkelig (nicht gleichseitig)");
            if (x[0]*x[0]+x[1]*x[1]==x[2]*x[2])
                System.out.println("Dreieck ist rechtwinklig");
        }

    }
}
}

3.4.13 Sortieren

package s1.block3;
public class Sortieren {
public static void main(String[] args) {
       int a = 0;
       int b = 0;
       int c = 0;

       if (args.length > 2 ) {
        try {
           a = Integer.parseInt(args[0]);
           b = Integer.parseInt(args[1]);
           c = Integer.parseInt(args[2]);
        } catch (NumberFormatException e) {
            System.err.println("Argument muss Ganzzahl sein");
            System.exit(1);
            }
        }
       System.out.println("Eingelesene Werte: " + a + ", " + b + ", " + c);
       // alle Moeglichkeiten testen
       if ((a <= b) && (b<=c))
           System.out.println("Sortierte Werte: " + a + ", " + b + ", " + c);
       else if ( (a <= c) && (c <= b) )
           System.out.println("Sortierte Werte: " + a + ", " + c + ", " + b);
       else if ( (b <= a) && (a <= c) )
           System.out.println("Sortierte Werte: " + b + ", " + a + ", " + c);
        else if ( (b <= c) && (c <= a) )
           System.out.println("Sortierte Werte: " + b + ", " + c + ", " + a);
       else if ( (c <= a) && (a <= b) )
           System.out.println("Sortierte Werte: " + c + ", " + a + ", " + b);
       else if ( (c <= b) && (b <= a) )
           System.out.println("Sortierte Werte: " + c + ", " + b + ", " + a);
}
}

3.4.14 Plausibilitätsprüfung

package s1.block3;
public class Plausibilitaetspruefung {
    public static void main(String[] args) {
       int monatTage[] = {0, 31, 28, 31, 30, 31, 30,
                             31, 31, 30, 31, 30, 13};
int tag = 0; int monat = 0; int jahr = 0; if (args.length > 2 ) { try { tag = Integer.parseInt(args[0]); monat = Integer.parseInt(args[1]); jahr = Integer.parseInt(args[2]); } catch (NumberFormatException e) { System.err.println("Argument muss Ganzzahl sein"); System.exit(1); } System.out.println("Eingabe ist: " + tag + "." + monat + "." + jahr); if ((monat>0) && (monat < 13) && (tag>0)) if (monatTage[monat] >= tag) System.out.println("Datum ist gültig"); else if ((monat == 2) && (tag == 29) && (jahr%4 == 0) && ((jahr%100 !=0) || (jahr%400==0)) ) System.out.println("Datum ist gültig (Schalttag)"); else System.out.println("Datum ist ungültig"); } } }

3.4.15 Textverschlüsselung

Die vorgestellte Lösung ist eine naive, aber in diesem Fall korrekte Lösung da hier der implementierte Wert eines Zeichen im Vordergrund steht und nicht die Bedeutung des Zeichens.

Sie basiert auf der naiven Annahme, dass die den Zeichen (Buchstaben) zugeordnete Zahlenwerte der Sortierreihenfolge der Buchstaben entspricht. Diese Annahme ist für lateinische Zeichensätze meißten, mehr oder weniger richtig. Im Allgemeinen sollte man bei Programmentwicklung an die Internationalisierung denken. Hier gilt:

  • Die lexikografische Ordnung (Sortierreihenfolge) ist länderabhängig und hängt von der Landeseinstellung (locale, Codepage) der eingestellten Umgebung ab.
    • Beispiel: Die Buchstaben ß, ö, Ö, ü etc haben in Unicode oder ISO 8852 Werte die nicht der deutschen lexikographischen Ordnung entsprechen. 
  • Die von Java verwendete Unicodecodierung erlaubt die Codierung der meisten Zeichen der Welt gleichzeitig. Die Sortierreihenfolge ist hier nicht garantiert. 
    • Beispiel: Es gibt Zeichen die in der japanischen sowie in der chinesichen Sprache verwendet werden. Diese Zeichen sind identisch, haben jedoch eine unterschiedliche Bedeutung. Sie werden daher in China und Japan unterschieldlich sortiert.
package s1.block3;
public class Textverschluesselung {
    public static void main(String[] args) {
    String myText = "";
    int offSet = 0;
    char c;
 
    if (args.length > 1 ) {
        offSet = Integer.parseInt(args[0]);
        myText = args[1];
        }
    System.out.println("Eingabe: <<" + myText + ">>, Verschiebung: " + offSet);
    System.out.print("Ausgabe: <<");
    offSet = offSet%26; // Bei 26 Buchstaben kann der Versatz nur module 26 sein
    for (int i=0;i < myText.length(); i++) {
        // Lese jeden Wert der Zeichenkette aus und erhöhe den Zähler im Feld
        c = myText.charAt(i);
        if ((c>='A') && (c<='Z')) {
            c = (char)(c + offSet);
            if (c > 'Z') c-=(char)26; //Ziehe vom Ergebnis 26 ab da es jenseits von "Z" liegt.
            System.out.print(c);
            }
        }
        System.out.println(">>");
 }       
}

3.4.16 Verzweigung

package s1.block3;
public class Verzweigung {
public static void main(String[] args) {
    int x[] = new int[3];
    if (args.length > 2 ) {
        try {
           for (int i=0; i<3; i++)
               x[i]= Integer.parseInt(args[i]);
           }
        catch (NumberFormatException e) {
            System.err.println("Argument muss Ganzzahl sein");
            System.exit(1);
            }
    }
    System.out.println("Eingabe: " + x[0] +", " + x[1] + ", "+ x[2]);
    if ((x[0]==x[1]) && (x[1]==x[2]))
        System.out.println("Alle Werte sind gleich.");
    if ((x[0]==x[1]) || (x[1]==x[2]) || (x[0]==x[2]))
        System.out.println("Mindestens zwei Werte sind gleich.");
    if ((x[0]!=x[1]) && (x[1]!=x[2]) && (x[0]!=x[2]))
        System.out.println("Alle Werte sind verschieden.");
}
}

3.4.17 Häufigkeit von Zeichen

package s1.block3;
public class Haeufigkeitzeichen {
public static void main(String[] args) {
    String myText="";
    char c;
    int histogram[] = new int[Character.MAX_VALUE];

    if (args.length > 0 ) {
        myText=args[0];
        }
    System.out.println("Eingabe: <<" + myText + ">>");
    for (int i=0;i < myText.length(); i++) {
        // Lese jeden Wert der Zeichenkette aus und erhöhe den Zähler im Feld
        c = myText.charAt(i);
        histogram[c]++;
        }
    for (int i=0; i < Character.MAX_VALUE; i++)
        if (histogram[i]!= 0) {
            // Wichtig: unterdrücke alle leeren Einträge.
            // Das Feld hat ~65000 Zellen!
            System.out.print((char)i + ": ");
            for (int j=0; j< histogram[i]; j++)
                System.out.print('*');
            System.out.println();
        }
    }
}

 3.4.18 Weckzeiten implementieren

package s1.block3;
public class WeckzeitLoesung {
int wzh = 0; // Weckzeit in Stunden
int wzm = 0; // Weckzeit in Minuten
int wzs = 0; // Weckzeit in Sekunden
public void setzeWeckzeit(int hh, int mm, int ss) {
if (korrekteWeckzeit(hh,mm,ss)) {
wzh = hh;
wzm = mm;
wzs = ss;
}
}
public boolean korrekteWeckzeit(int h, int m, int s) {
boolean result;
// benutzen die Variablen h,m,s um eine gültige Zeit zu bestimmen
result = ((h>=0) && (h<=12 && (m>=0) && (m<=59)&& (s>=0) && (s<=59)));
return result;
}
public boolean klingeln(int aktH, int aktM, int aktS) {
boolean result;
// benutzen die Variablen der aktuellen Zeit aktH (Stunde),
// aktM (Minute), aktS (Sekunde) und die Weckzeit wzmh, wzm, wzs
// um zu bestimmern ob der Wecker klingeln soll
// Bestimme aktuelle Zeit in Sekunden
int aktZeit = aktH*3600 + aktM*60+aktS;
// Bestimme Weckzeit in Sekunden
int weckZeit = wzh *3600 + wzm *60+wzs;
// Ist die aktuelle Zeit größer aber nicht größer als 10 Sekunden?
result = (aktZeit-weckZeit>=0) && (aktZeit-weckZeit<10);
return result;
}

}

 

Stefan Schneider Sun, 07/11/2010 - 16:32

3.5 Lernziele

3.5 Lernziele

Am Ende dieses Blocks können Sie:

  • ... Befehlsblöcke im Javaquellcode erkennen und erstellen
  • ... die allgemeine Syntax von if, while, do while und for Kommandos und Schleifen nennen
  • ... in einer mehrfach, geschachtelten if Anweisung einen else Block der zugehörigen if-Bedingung zuordnen
  • ... die Typen nennen die man bei Mehrfachverzweigungen benutzen kann
  • ... den Ablauf einer Mehrfachverzweigung mit case, break und default Schlüsselwörten analysieren
  • ... for Schleifen in while bzw. do while Schleifen transformieren
  • ... die Syntax einer for-Schleife beschreiben

Lernzielkontrolle

Sie sind in der Lage die folgenden Fragen zur Ablaufsteuerung und die Frage aus der Übung 3.3.1 Schleifenterminierung zu beantworten.

Feedback

Zur Umfrage

QR Code für Umfrage

Stefan Schneider Sat, 08/18/2012 - 13:02