可替代 Java 中的 goto 语句

在 Java 中,Goto关键字的替代函数是什么?

因为 Java 没有 goto。

240274 次浏览

You could use a labeled BREAK statement:

search:
for (i = 0; i < arrayOfInts.length; i++) {
for (j = 0; j < arrayOfInts[i].length; j++) {
if (arrayOfInts[i][j] == searchfor) {
foundIt = true;
break search;
}
}
}

However, in properly designed code, you shouldn't need GOTO functionality.

If you really want something like goto statements, you could always try breaking to named blocks.

You have to be within the scope of the block to break to the label:

namedBlock: {
if (j==2) {
// this will take you to the label above
break namedBlock;
}
}

I won't lecture you on why you should avoid goto's - I'm assuming you already know the answer to that.

There isn't any direct equivalent to the goto concept in Java. There are a few constructs that allow you to do some of the things you can do with a classic goto.

  • The break and continue statements allow you to jump out of a block in a loop or switch statement.
  • A labeled statement and break <label> allow you to jump out of an arbitrary compound statement to any level within a given method (or initializer block).
  • If you label a loop statement, you can continue <label> to continue with the next iteration of an outer loop from an inner loop.
  • Throwing and catching exceptions allows you to (effectively) jump out of many levels of a method call. (However, exceptions are relatively expensive and are considered to be a bad way to do "ordinary" control flow1.)
  • And of course, there is return.

None of these Java constructs allow you to branch backwards or to a point in the code at the same level of nesting as the current statement. They all jump out one or more nesting (scope) levels and they all (apart from continue) jump downwards. This restriction helps to avoid the goto "spaghetti code" syndrome inherent in old BASIC, FORTRAN and COBOL code2.


1- The most expensive part of exceptions is the actual creation of the exception object and its stacktrace. If you really, really need to use exception handling for "normal" flow control, you can either preallocate / reuse the exception object, or create a custom exception class that overrides the fillInStackTrace() method. The downside is that the exception's printStackTrace() methods won't give you useful information ... should you ever need to call them.

2 - The spaghetti code syndrome spawned the structured programming approach, where you limited in your use of the available language constructs. This could be applied to BASIC, Fortran and COBOL, but it required care and discipline. Getting rid of goto entirely was a pragmatically better solution. If you keep it in a language, there is always some clown who will abuse it.

Just for fun, here is a GOTO implementation in Java.

Example:

   1 public class GotoDemo {
2     public static void main(String[] args) {
3         int i = 3;
4         System.out.println(i);
5         i = i - 1;
6         if (i >= 0) {
7             GotoFactory.getSharedInstance().getGoto().go(4);
8         }
9
10         try {
11             System.out.print("Hell");
12             if (Math.random() > 0) throw new Exception();
13             System.out.println("World!");
14         } catch (Exception e) {
15             System.out.print("o ");
16             GotoFactory.getSharedInstance().getGoto().go(13);
17         }
18     }
19 }

Running it:

$ java -cp bin:asm-3.1.jar GotoClassLoader GotoDemo
3
2
1
0
Hello World!

Do I need to add "don't use it!"?

public class TestLabel {


enum Label{LABEL1, LABEL2, LABEL3, LABEL4}


/**
* @param args
*/
public static void main(String[] args) {


Label label = Label.LABEL1;


while(true) {
switch(label){
case LABEL1:
print(label);


case LABEL2:
print(label);
label = Label.LABEL4;
continue;


case LABEL3:
print(label);
label = Label.LABEL1;
break;


case LABEL4:
print(label);
label = Label.LABEL3;
continue;
}
break;
}
}


public final static void print(Label label){
System.out.println(label);
}

While some commenters and downvoters argue that this isn't goto, the generated bytecode from the below Java statements really suggests that these statements really do express goto semantics.

Specifically, the do {...} while(true); loop in the second example is optimised by Java compilers in order not to evaluate the loop condition.

Jumping forward

label: {
// do stuff
if (check) break label;
// do more stuff
}

In bytecode:

2  iload_1 [check]
3  ifeq 6          // Jumping forward
6  ..

Jumping backward

label: do {
// do stuff
if (check) continue label;
// do more stuff
break label;
} while(true);

In bytecode:

 2  iload_1 [check]
3  ifeq 9
6  goto 2          // Jumping backward
9  ..

StephenC writes:

There are two constructs that allow you to do some of the things you can do with a classic goto.

One more...

Matt Wolfe writes:

People always talk about never using a goto, but I think there is a really good real world use case which is pretty well known and used.. That is, making sure to execute some code before a return from a function.. Usually its releasing locks or what not, but in my case I'd love to be able to jump to a break right before the return so I can do required mandatory cleanup.

try {
// do stuff
return result;  // or break, etc.
}
finally {
// clean up before actually returning, even though the order looks wrong.
}

http://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html

The finally block always executes when the try block exits. This ensures that the finally block is executed even if an unexpected exception occurs. But finally is useful for more than just exception handling — it allows the programmer to avoid having cleanup code accidentally bypassed by a return, continue, or break. Putting cleanup code in a finally block is always a good practice, even when no exceptions are anticipated.

The silly interview question associated with finally is: If you return from a try{} block, but have a return in your finally{} too, which value is returned?

Try the code below. It works for me.

for (int iTaksa = 1; iTaksa <=8; iTaksa++) { // 'Count 8 Loop is  8 Taksa


strTaksaStringStar[iCountTaksa] = strTaksaStringCount[iTaksa];


LabelEndTaksa_Exit : {
if (iCountTaksa == 1) { //If count is 6 then next it's 2
iCountTaksa = 2;
break  LabelEndTaksa_Exit;
}


if (iCountTaksa == 2) { //If count is 2 then next it's 3
iCountTaksa = 3;
break  LabelEndTaksa_Exit;
}


if (iCountTaksa == 3) { //If count is 3 then next it's 4
iCountTaksa = 4;
break  LabelEndTaksa_Exit;
}


if (iCountTaksa == 4) { //If count is 4 then next it's 7
iCountTaksa = 7;
break  LabelEndTaksa_Exit;
}


if (iCountTaksa == 7) { //If count is 7 then next it's 5
iCountTaksa = 5;
break  LabelEndTaksa_Exit;
}


if (iCountTaksa == 5) { //If count is 5 then next it's 8
iCountTaksa = 8;
break  LabelEndTaksa_Exit;
}


if (iCountTaksa == 8) { //If count is 8 then next it's 6
iCountTaksa = 6;
break  LabelEndTaksa_Exit;
}


if (iCountTaksa == 6) { //If count is 6 then loop 1  as 1 2 3 4 7 5 8 6  --> 1
iCountTaksa = 1;
break  LabelEndTaksa_Exit;
}
}   //LabelEndTaksa_Exit : {


} // "for (int iTaksa = 1; iTaksa <=8; iTaksa++) {"

The easiest is:

int label = 0;
loop:while(true) {
switch(state) {
case 0:
// Some code
state = 5;
break;


case 2:
// Some code
state = 4;
break;
...
default:
break loop;
}
}

Use a labeled break as an alternative to goto.

Java doesn't have goto, because it makes the code unstructured and unclear to read. However, you can use break and continue as civilized form of goto without its problems.


Jumping forward using break -

ahead: {
System.out.println("Before break");
break ahead;
System.out.println("After Break"); // This won't execute
}
// After a line break ahead, the code flow starts from here, after the ahead block
System.out.println("After ahead");

Output:

Before Break
After ahead

Jumping backward using continue

before: {
System.out.println("Continue");
continue before;
}

This will result in an infinite loop as every time the line continue before is executed, the code flow will start again from before.