Der berechnete Sprung in Java
Erste Beispiele zum berechneten Sprung
Das folgende Beispiel zeigt die kürzestmögliche Form einer switch -Anweisung. Sie hat allerdings keine Wirkung.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ switch( 0 ){ } }}java.lang.System.out
- keine Ausgabe.
Das folgende Beispiel zeigt, daß im Rumpf case -Marken (/ˈkeɪz/-Marken) verwendet werden dürfen. Es hat allerdings immer noch keine Wirkung.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ switch( 0 ){ case 1: } }}java.lang.System.out
- keine Ausgabe.
Hinter einer case -Marke dürfen Blockeinträge geschrieben werden. Das folgende Beispiel hat allerdings immer noch keine Wirkung.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ switch( 0 )
{ case 1:
java.lang.System.out.println( "1" );
java.lang.System.out.println( "1" ); }}}java.lang.System.out
- keine Ausgabe.
Falls der Wert des Ausdrucks in den switch -Klammern mit dem Wert einer case -Marke übereinstimmt, so wird die Ausführung bei dieser case -Marke fortgesetzt.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ switch( 1 )
{ case 1:
java.lang.System.out.println( "1" );
java.lang.System.out.println( "1" ); }}}java.lang.System.out
1
1
Die Fortsetzung eines Programms an einer anderen Stelle als bei der in einem Block folgenden Anweisung wird auch als Sprung bezeichnet. Die case -Marke, welche den Punkt, an dem die Ausführung in bestimmten Fällen fortgesetzt wird, kennzeichnen, werden auch Sprungziele genannt.
Anweisungen vor der angesprungenen case -Marke werden nicht ausgeführt. Folgende Anweisung aber schon.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ switch( 1 )
{ case 0:
java.lang.System.out.println( "0" );
java.lang.System.out.println( "0" ); case 1:
java.lang.System.out.println( "1" );
java.lang.System.out.println( "1" ); case 2:
java.lang.System.out.println( "2" );
java.lang.System.out.println( "2" ); }}}java.lang.System.out
1
1
2
2
Es sind auch mehrere case -Marken direkt hintereinander gestattet.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ switch( 1 )
{ case 0:
java.lang.System.out.println( "0" );
java.lang.System.out.println( "0" ); case 1:
case 2:
java.lang.System.out.println( "12" );
java.lang.System.out.println( "12" ); case 3:
case 4:
java.lang.System.out.println( "34" );
java.lang.System.out.println( "34" ); }}}java.lang.System.out
12
12
34
34
Eine eventuell vorhandene default -Marke (/dɪ ˈfɔlt/-Marke) wird angesprungen, wenn es keine passende case -Marke gibt.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ switch( 0 )
{ default: java.lang.System.out.println( "default" ); }}}java.lang.System.out
default
Syntax des berechneten Sprungs (vereinfacht)
.-''''-. .''. .----------. .''.
--->( switch )--->( ( )--->| Ausdruck |--->( ) )------------. .-------------------------->
'-....-' '..' '----------' '..' | |
| |
.-------------------------------------------------------------' |
| |
| .------------------------------. |
| | .----------------------. | |
| .''. | v | V .-. |
'--->( { )---'---'---. .--->'---'--->( } )--------------'
'..' | | '-'
| |
| |
.---------------------' '-------------------------------------------------------.
| |
| .---------------------------------. |
| | | |
| .''''. .------. .-. | .----------------. v |
'---.---.--->( case )--->| Wert |---.--->( : )---.---'---.--->| Blockeintrag |---.---'----'
^ | '....' '------' ^ '-' | ^ '----------------' |
| | | | | |
| | .'''''''. | | '-------------------------'
| '--->( default )------------' |
| '.......' |
| |
'--------------------------------------------'
Typanforderungen (vereinfacht)
Der Ausdruck nach »switch« »(« muß den Typ »int« oder »java.lang.String« haben.
Der Wert nach »case« muß ein konstanter Ausdruck sein, dessen Wert schon durch den Quelltext festgelegt ist, er muß mit dem Typ des Ausdrucks nach »switch« »(« verträglich sein.
Semantik (vereinfacht)
Bei der Ausführung einer Anweisung dieser Art wird zunächst der Ausdruck nach »switch« »(« ausgewertet.
Die Ausführung wird dann in dem Block bei der case -Marke fortgesetzt, deren Wert dem Wert des Ausdrucks gleich ist.
Falls keine solche Marke existiert, wird die Ausführung nach »default« »:« fortgesetzt – falls in dem Block aber kein »default« »:« vorkommt, dann wird gar nichts weiter gemacht.
Die break -Anweisung
In einen switch -Block darf auch eine break -Anweisung verwendet werden.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ switch( 1 ){ case 1: break; }}}java.lang.System.out
- keine Ausgabe.
- Syntax der break -Anweisung
.-'''-. .-.
--->( break )--->( ; )--->
'-...-' '-'- Semantik der break -Anweisung
- Bei Ausführung der break -Anweisung wird die Ausführung des switch -Blocks abgebrochen
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ switch( 1 )
{ case 1:
java.lang.System.out.println( 1 );
break;
case 2:
java.lang.System.out.println( 2 ); }}}java.lang.System.out
1
- Genauer gesagt wird die Ausführung des nächsten umgebenden Blocks, der durch eine break -Anweisung abgebrochen werden kann, abgebrochen.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ switch( 1 )
{ case 1:
java.lang.System.out.println( 1 );
switch( 2 )
{ case 2:
java.lang.System.out.println( 2 );
break;
case 3:
java.lang.System.out.println( 3 ); }
case 4:
java.lang.System.out.println( 4 ); }}}java.lang.System.out
1
2
4
Schlüsselwörter
»switch«, »case«, und »break« sind Schlüsselwörter.
Die bisher behandelte Schlüsselwörter
- »boolean«, »break«, »case«, »double«, »else«, »if«, »public«, »return«, »int«, »final«, »static«, »switch«, »void«
Strukturierte Programmierung mit switch
Ein switch -Block genügt im allgemeinen nicht den Anforderungen der strukturierten Programmierung, weil der switch -Block mehrere Eingänge (die case -Marken) und mehrere Ausgänge (die break-Anweisungen) hat oder haben kann. In der strukturierten Programmierung darf aber jeder Block nur einen Eingang (seinen Anfang) und nur einen Ausgang (sein Ende) haben. (Der Eingang eines Blocks ist die Stelle jenes Blocks, an welcher mit der Ausführung von Anweisungen aus diesem Block begonnen wird. Der Ausgang eines Blocks ist die Stelle jenes Block, an welcher die Ausführung von Anweisungen aus jenem Block beendet oder abgebrochen wird.)
Durch eine bestimmte Kombination von switch und break kann man jedoch die mit einer switch -Anweisung strukturiert programmieren, was im allgemeinen als übersichtlicher angesehen wird. Dazu zerlegt man den switch -Block in eine Folge gedachter Blöcke, deren jeder mit case - oder default -Marken beginnt und mit einer break -Anweisung endet, aber sonst keine weiteren case - oder default -Marken oder break -Anweisungen enthält. Jeder dieser gedachten Blöcke hat denn wieder nur einen Eingang an seinem Anfang und einen Ausgang an seinem Ende. (Beim letzten dieser Blöcke könnte die break -Anweisung auch entfallen.)
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ switch( 1 )
{ case 1:
java.lang.System.out.println( 1 );
break; case 2:
java.lang.System.out.println( 2 );
break; }}}java.lang.System.out
1
Konstantheit von case -Marken
case -Marken müssen konstant sein, in dem Sinne, daß ihre Werte schon während der Übersetzung des Programms bekannt sind. Das folgende Programm zeigt einen erlaubten Ausdruck und – auskommentiert – einen nicht erlaubten.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ final int i = 1 + 1;
final int j = java.lang.Math.random() > 0.5 ? 0 : 1; switch( 1 )
{ case i:
java.lang.System.out.println( 1 );
break; // case j:
case 1:
java.lang.System.out.println( 2 );
break; }}}
Zeichenfolgen in switch
Auch Zeichenfolgen sind in einer switch -Anweisung erlaubt.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ switch( "alpha" )
{ case "alpha":
java.lang.System.out.println( "alpha" );
break; case "beta":
java.lang.System.out.println( "beta" );
break; }}}java.lang.System.out
alpha
Main.java
public final class Main
{ public static int schulnote( final java.lang.String text )
{ int result = -1;
switch( text )
{ case "sehr gut": result = 1; break;
case "gut": result = 2; break;
case "befriedigend": result = 3; break; }
return result; } public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( schulnote( "gut" )); }}transcript
2
Datentypen im switch -Ausdruck
Der Ausdruck in einer switch -Anweisung darf weder boolean noch double sein. Notfalls kann man einen Ausdruck entsprechend wandeln, wie das folgende Programm zeigt.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ switch( true ? 1 : 0 )
{ case 0:
java.lang.System.out.println( "false" );
break; case 1:
java.lang.System.out.println( "true" );
break; }}}java.lang.System.out
true
Nachbildung von if mit switch
Eine if -Anweisung kann mit einer switch -Anweisung nachgebildet werden, indem genau einer von zwei gedachten case -Blöcken in Abhängigkeit von einem Wahrheitswert ausgeführt wird.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ switch( true ? 1 : 0 )
{ case 0:
java.lang.System.out.println( "false" );
break; case 1:
java.lang.System.out.println( "true" );
break; }}}java.lang.System.out
true
Mehrfachverzweigungen
Das klassische Einsatzgebiet einer switch-Anweisung ist die Mehrfachverzweigung in Abhängigkeit von einem Wert. Hier ist switch besser als if, weil der Ausdruck für den Wert nicht immer wiederholt werden muß. Einige Java-Implementation können in manchen Fällen solche Mehrfachverzweigungen mit switch auch effizienter ausführen also solche mit if oder einer bedingten Auswertung.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ final double r = java.lang.Math.random();
final int a =( int )( r * 3 + 1 );
if( a == 1 )java.lang.System.out.println( "eins" );
else if( a == 2 )java.lang.System.out.println( "zwei" );
else if( a == 3 )java.lang.System.out.println( "drei" ); }}java.lang.System.out
zwei
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ final double r = java.lang.Math.random();
final int a =( int )( r * 3 + 1 );
switch( a )
{ case 1: java.lang.System.out.println( "eins" ); break;
case 2: java.lang.System.out.println( "zwei" ); break;
case 3: java.lang.System.out.println( "drei" ); break; }}}java.lang.System.out
zwei
Solche switch -Anweisungen können oft besonders effizient ausgeführt werden, wenn der Bereich der Werte der case -Marken wie in dem obigen Beispiel direkt aufeinanderfolgende Zahlen sind. Hier könnte die switch -Anweisung schneller als die entsprechende if -Anweisung sein.
Die obenstehende if -Anweisung zeigt auch, wie eine switch -Anweisung mit einer if -Anweisung nachgebildet werden kann.
Bereiche in switch
Es ist nicht möglich, in einer switch -Anweisung Zahlenbereiche anzugeben. Manchmal ist es jedoch möglich, statt dessen eine Auflistung von case -Marken zu verwenden.
Es sollen beispielsweise Ziffern ab 1 nach ihrer Größe klassifiziert werden.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ final double r = java.lang.Math.random();
final int a =( int )( r * 9 + 1 );
switch( a )
{ case 1: case 2: case 3: java.lang.System.out.println( "klein" ); break;
case 4: case 5: case 6: java.lang.System.out.println( "mittel" ); break;
case 7: case 8: case 9: java.lang.System.out.println( "gross" ); break; }}}java.lang.System.out
mittel
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ final double r = java.lang.Math.random();
final int a =( int )( r * 9 + 1 );
switch( a < 4 ? 0 : a < 7 ? 1 : 2 )
{ case 0: java.lang.System.out.println( "klein" ); break;
case 1: java.lang.System.out.println( "mittel" ); break;
case 2: java.lang.System.out.println( "gross" ); break; }}}java.lang.System.out
mittel
Programmablaufpläne für die switch -Anweisung
Programmablaufpläne erlauben es, Sprünge durch Pfeile darzustellen.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ final double r = java.lang.Math.random();
final int a =( int )( r * 9 + 1 );
switch( a < 4 ? 0 : a < 7 ? 1 : 2 )
{ case 0: java.lang.System.out.println( "klein" ); break;
case 1: java.lang.System.out.print( "mittel" );
case 2: java.lang.System.out.println( "gross" ); break; }}}java.lang.System.out
mittelgross
- Programmablaufplan
|
|
v
.-'-.
.-' '-.
.-' '-.
.----------------- a ? -----------------.
| '-. .-' |
| '-. .-' |
| '-.-' |
| | |
| a < 4 | a>=4 ^ a<7 | sonst
| | |
| | .------------>|
v v | v
------'--------- ------'--------- | ------'---------
/ Ausgabe / / Ausgabe / | / Ausgabe /
/ "klein" und / / "mittel" / | / "gross" und /
/ Zeilenende / / / | / Zeilenende /
---------.------ ---------.------ | ---------.------
| | | |
| '-----------' |
| |
'------------------------>.<------------------------'
|
|
|
v
Wir haben im Programmablaufplan einige Details der Implementation, wie die Verwendung der Werte »0«, »1« und »2« als Sprungziele, weggelassen. So ist der Programmablaufplan übersichtlicher.
Spaghetti-Code
Bei der Darstellung größerer Programme mit vielen Sprunganweisungen, können sich die vielen Pfeile in einem Programmablaufplan auch überkreuzen. Sie erscheinen dann als unübersichtlich wie Spaghettis auf einem Teller. Daher wrd solch unübersichtlicher Code auch als Spaghetti-Code bezeichnet, und es wird empfohlen Sprunganweisungen in Programmen möglichst zu vermeiden.
Das wirkliche Problem mit „Spaghetti-Code“ ist aber nicht „Unübersichtlichkeit“, sondern die Erschwerung von Überarbeitungen des Codes, wie etwa Erschwerungen des Refaktors „Extrahieren einer Methode“.
Für die switch -Anweisung bedeutet dies, daß die weiter oben vorgestellte strukturierte Form bevorzugt werden sollte.
Struktogramme für die switch -Anweisung
Struktogramme erlauben keine Darstellung von Sprüngen. Wie ihr Name schon nahelegt, sind sie nur für die Darstellung der strukturierten Form der Mehrfachverzweigung geeignet.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ final double r = java.lang.Math.random();
final int a =( int )( r * 9 + 1 );
switch( a < 4 ? 0 : a < 7 ? 1 : 2 )
{ case 0: java.lang.System.out.println( "klein" ); break;
case 1: java.lang.System.out.println( "mittel" ); break;
case 2: java.lang.System.out.println( "gross" ); break; }}}java.lang.System.out
mittel
- Struktogramm der Mehrfachverzweigung
.-----------------------------------------------.
|''--.. .'|
| ''--.. .' |
| ''--.. .' |
| | ''--.. .' |
| | ''--.. .' |
| a < 4 | a>=4 ^ a<7 ''-. .' sonst |
|----------------|-----------------|------------|
| | | |
| Ausgabe | Ausgabe | Ausgabe |
| "klein" | "mittel" | "gross" |
| | | |
'-----------------------------------------------'- Struktogramm der Mehrfachverzweigung (vereinfacht)
.-----------------------------------------------.
| a < 4 | a>=4 ^ a<7 | sonst |
|----------------|-----------------|------------|
| | | |
| Ausgabe | Ausgabe | Ausgabe |
| "klein" | "mittel" | "gross" |
| | | |
'-----------------------------------------------'