11

Suppose save throws and i is used only for save. Are the following code fragments the same? Please consider sematics, performance and other aspects.

void bob(){
  int i = calculate();
  try {
    save(i);
  } catch(Exception e){
    report(e)
  }
}

vs.

void bob(){
  try {
    int i = calculate();
    save(i);
  } catch(Exception e){
    report(e)
  }
}

Generally I want to know, should one place all statements of a function in a try-catch block or just the one throwing.

Binary Bob
  • 337
  • 3
  • 10

4 Answers4

12

Semantics-wise, if you have decided which method you're going to put your try-catch construct in (and you're comfortable that you've made that decision correctly), then the answer is fairly simple:

  • You should include in your try block a sequence of statements such that, if one of those statements fails, the rest of the sequence should be abandoned. No more and no fewer statements than that.

If you follow the above advice correctly, concerns such as the desired program flow and the most efficient scoping of the local variables will be resolved very easily and obviously (in most cases). You'll notice that this doesn't exclude the possibility of nested try blocks.

Performance-wise, the overhead of exception handling lies with actually throwing and catching a throwable object. In other words, there's really overhead only if an exception actually occurs. The mere presence of a try-catch construct in the code doesn't introduce any measurable overhead (possibly none at all). Also, the amount of statements (inside a given try-catch construct) is completely irrelevant to its performance.

Edit: I couldn't find any details in the JVM specification to link to, but there are many posts by users that examine and explain the generated bytecode, like this one and this one (among many others - a Google search will yield a few interesting results). To me, it looks like the Java compiler tries to emit as little as possible (except for, of course, the actual code you put in the try and the catch clauses and some inevitable program flow instructions to jump around said clauses or pop the exception object, if any). It leaves to the VM the responsibility of finding out where an exception is candidate to be caught. This most probably transfers more burden to the scenarios where the exception actually occurs but, as we know, exceptions are for exceptional cases, not control flow anyway.

I admit I have no idea how C++ exceptions are typically implemented, but it's very reasonable for them to radically differ from Java's, considering C++ programs don't usually run with the assistance of a VM.

Theodoros Chatzigiannakis
  • 28,773
  • 8
  • 68
  • 104
  • very nice answer, do you have some materials proving that the very existence of `try-catch` doesnt impose any cost? Because in C++ simply using this construct changes the way the compiled code looks adding multiple operations and elements on the stack and heap. – Binary Bob Jun 20 '13 at 20:08
  • I believe the JLS specifies that a `try/catch` block only imposes a cost when the exception is actually thrown. – Thorn G Jun 20 '13 at 20:13
  • 1
    If you are expecting that a particular exception will indicate a particular kind of failure in a particular method, it may be good idea to put that method in its own `try` block unless the exception in question is a checked exception, and only one method is declared as possibly throwing it. Otherwise, if the exception gets thrown by a method other than the one that was expected to throw it, the exception-handling code may make erroneous assumptions about what's wrong (and what isn't). – supercat Jun 20 '13 at 21:03
  • @supercat Sure, this is a possibility - although I personally prefer to resolve such cases in the `catch` block and still abandon the `try` block anyway. If there can't be enough information by then to resolve what went wrong, then I'd work around it by doing what you're suggesting. Admittedly, exceptions aren't perfect. – Theodoros Chatzigiannakis Jun 20 '13 at 21:18
5

They are not the same. The difference is the scope of the variable i. In 2nd case, you cannot use i outside the try-catch block.

Generally I want to know, should one place all statements of a function in a try-catch block or just the one throwing.

The better way is to just wrap the code vulnerable to throwing exception inside the try-catch block. This way, you can handle specific exception related to specific block of codes. So, 1st way is the one to go for.

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
2

In this case:

void bob(){
  try {
    int i = calculate();
    save(i);
  } catch(Exception e){
    report(e)
  }
}

all exception (except Errors) will be caught, because Exception is a super class of all kind of Exception (except Errors). So not just checked exceptions will be handled, but unchecked like RuntimeException as well. If this is your goal, then I suggest to follow this way. On the other hand I think minimizing the scope of a try...catch block is a good practice, because in case of an exception to locate the problematic line of code is easier.

JoshDM
  • 4,939
  • 7
  • 43
  • 72
Quirin
  • 738
  • 1
  • 5
  • 15
1

Rohit points out (Correctly) that the scope of variable i is a difference here.

The additional difference is if calculate() throws an Unchecked Exception; it will get caught by the catch block. Whether you should put the call to calculate() within the try block depends on whether you want to handle an Unchecked Exception from calculate() in the catch block here, or allow it to get thrown outwards. In my opinion, since it's Unchecked and therefore completely unexpected, I wouldn't put the call to calculate() in the try block.

Regarding performance, it is my opinion that there shouldn't be a difference since the performance hit is on encountering the try block and you only have one block in both scenarios. If you had separate try-catch blocks around each line as follows, then performance is likely going to be degraded, but probably not in any way that you'd ever notice:

// degraded performance example
void bob(){

    int i;

    try {
        i = calculate();
    } catch(Exception e){
        report(e)
    }

    try {
        save(i);
    } catch(Exception e){
        report(e)
    }
}
JoshDM
  • 4,939
  • 7
  • 43
  • 72
  • 2 `try-catch` blocks are slower than one? What about nested ones? – Binary Bob Jun 20 '13 at 20:11
  • Performance would likely be similar unless `calculate()` threw an exception, then exiting would be minimally faster than my sample code as the second `try-catch` block would never be encountered. – JoshDM Jun 20 '13 at 20:49