Try Catch Statement Question [2]

Started by
7 comments, last by rip-off 6 years, 9 months ago

Hi guys,

So I managed to get a book on Java! and I'm at a point where it's talking about try/catch/finally statements, from what I understand say I've got a java program and the user clicks on a button called "load recent save" .. the button click event would call a method called load_recent and have a try catch statement, within the try it will check say an ini file for the last known location, but now say the user deleted the file.. so the last known location would be incorrect and it would throw the exception java.io.FileNotFoundException, this is where I'd like to know what happens, say I catch the exception and popup a dialog allowing the user to browse for the file and also it will clear that location. Okay so I've handled the exception.. but now I'm wondering when the code block of the catch has finished running, does the try statement start over ? I'm kind of confused on how this works.

 

The other thing is, I get the impression that try catch can be entirely avoided by just coding properly. For example if a person divides 0 by 0 it'll throw some exception, but it'll only throw that exception because as a coder I didn't think hey instead of using try catch what I can do is prevent 0 from being divided by 0 in the first place. So my guess is that reason try catch exists is to avoid extra code. By extra code I mean without using try catch a dev has to add more code to prevent that from happening by checking which values and operators are being used. How ever if try catch is used then no extra code is read, only when an exception is thrown does the compiler look for the relevant catch exception.. does that make sense ?

So first question is when an exception is found, does the compiler move back to the try statement or carry on from the caught exception ? or carry on from where an exception was last caught ? the third possibility sounds about right.

And then the second question is, is try catch really useful when errors can be predicted and handled with code in the first place. 

Advertisement

Once an uncaught exception is thrown from within the try block, it immediately goes to the catch block, and no code in the try block after that exception will be ran. If no exception is thrown from the try block, the catch block will not run.

If you know that an exception will not be thrown, then you do not need a try catch block. That includes if you made code to prevent the exception from happening in the first place. You can't always code to prevent exceptions though, a lot if times they are out of your control, so you need to make sure you handle them gracefully by using a try catch

Quote

So first question is when an exception is found, does the compiler move back to the try statement or carry on from the caught exception ? or carry on from where an exception was last caught ? the third possibility sounds about right.

This is the kind of thing you can figure out in two minutes. Knock up a simple test program like this one:


class Foo {
    public static void main(String[] args) {
        try {
            throwIfLessThan(5, 10);
            System.out.println("After function call");
        } catch(Exception e) {
            e.printStackTrace();
        }
        System.out.println("Outside the catch block!");
    }

    static int throwIfLessThan(int i, int n)
            throws Exception {
        if(i < n) {
            throw new Exception("i is less than n!");
        }
        return i + n;
    }
}

Then


javac Foo.java

followed by


java Foo

should answer your question. Spoiler alert: The first println is never executed if the exception is thrown.

Quote

And then the second question is, is try catch really useful when errors can be predicted and handled with code in the first place. 

Yes. For one, it gives you the freedom to move the error handling from the lowest level call site to the most appropriate place for it to be handled, without worrying that error codes are checked appropriately all along the chain. For another, it provides a mechanism for resources to be automatically cleaned up regardless of whether or not the exception is thrown (in Java, via the AutoClosable interface, in C++, by ensuring destructors are called as the stack unwinds).

So once the catch has run it'll continue on after it ?

I'm using eclipse and it's such a mission to create a new project every time just to test something out. Besides for that I had two questions and community discussions often lead to more information than just testing it out my self. 

 

The other thing I read about is adding exceptions to a method signature, using "throw exception". What it says is that only objects that inherit the java.lang.throwable class can throw exceptions. I'm not too sure how that works because in the example I gave I was talking about an arithmetic exception. And expressions/data types aren't objects other than arrays and class objects which isn't what I was using. So how does a sum result in an exception ?

 

And then, what I read was about try catch which is used within methods, but now this book is saying that a person would add 'throw exception' to a method name like so:


static double squareRoot (double x) throws IllegalArgumentException { ... }

how is that different than using 'try ... catch (exception e)    {}' ?

My guess is that since I'm creating a custom method then I can personally choose which exceptions it may throw, compared to objects like "server = new ServerSocket(arg1,arg2);' which shows that it'll throw the particular exception "IOException" so I'm assuming that within the ServerSocket method's signature there's the 'throws which ever exception'.  

 

So then my guess is, the way I catch these exceptions from custom methods is when I actually invoke the method I'd do it within a try catch, and since I made the method myself I'd know that I have exceptions to possibly catch. 

 

Does that seem correct ? 

12 minutes ago, Xer0botXer0 said:

What it says is that only objects that inherit the java.lang.throwable class can throw exceptions.

No, that's not quite right. You can throw an exception from inside any class method, but the object you throw has to be in the java.lang.Throwable hierarchy. For instance, you can't, for example, do "throw new Integer(1)" because the class Integer is not a subclass of Throwable.

11 minutes ago, Xer0botXer0 said:

I'm not too sure how that works because in the example I gave I was talking about an arithmetic exception. And expressions/data types aren't objects other than arrays and class objects which isn't what I was using. So how does a sum result in an exception ?

The compiler sometimes inserts instructions into the generated byte code that check certain operations and throw exceptions if you've done something the language spec says you aren't supposed to. For example, accessing a null class reference will cause a NullPointerException to be thrown, or you get an ArrayOutOfBoundsException if you try to access an array with an invalid index. ArithmeticExceptions are thrown when you do something like divide by zero, or some other invalid operation. Conceptually, it's no different than if you were doing the check yourself, perhaps something like this:
 


int divideInts(int lhs, int rhs) {
  if(rhs == 0)  {
    throw new ArithmeticException("Divide by zero!");
  }
  return lhs / rhs;
}

Except the compiler inserts the check for you.

23 minutes ago, Xer0botXer0 said:

And then, what I read was about try catch which is used within methods, but now this book is saying that a person would add 'throw exception' to a method name like so

This is not "throw" but "throws". It's called an Exception Specification and tells both the compiler and the programmer which exceptions a function might possibly throw. Your Java book should have told you that there are two types of exceptions in Java: checked exceptions and runtime exceptions. Any checked exceptions a method might throw must be specified in an exception specification in the method signature. Any methods calling that method must either catch and handle those exceptions, or also include an exception specification. Runtime exceptions are exempt from these rules. They may still be caught, but they are not required to be and they are never included in exception specifications. NullPointerException and ArithmeticException are examples of runtime exceptions (as is any exception type that extends java.lang.RuntimeException).

29 minutes ago, Xer0botXer0 said:

My guess is that since I'm creating a custom method then I can personally choose which exceptions it may throw, compared to objects like "server = new ServerSocket(arg1,arg2);' which shows that it'll throw the particular exception "IOException" so I'm assuming that within the ServerSocket method's signature there's the 'throws which ever exception'.  

If you have a method that includes a "new ServerSocket", then you need to either wrap that up in a try..catch and handle any IOException that is thrown or add a "throws IOException" specification to your method signature, since IOException is a checked exception. Any other checked exceptions your method might throw, either because you choose to throw them or because you call a method that throws them and you don't catch them, will also need to be added to the specification.

I don't know which book you're reading, but I feel I should recommend Core Java by Clay Horstmann. He goes into a lot of detail and covers a number of corner cases that most books don't. He explains exception handling particularly well.

2 hours ago, Xer0botXer0 said:

The other thing is, I get the impression that try catch can be entirely avoided by just coding properly. For example if a person divides 0 by 0 it'll throw some exception, but it'll only throw that exception because as a coder I didn't think hey instead of using try catch what I can do is prevent 0 from being divided by 0 in the first place.

As I said in your last thread, "If you can modify code to prevent the possibillity of certain exceptions being raised, that is usually preferable. But exceptions exist to handle conditions that you can't prevent, or events that you couldn't easily predict."

For example, it's easy to check if a value is zero before you divide it, and don't do it. But what if you have to write a complex function which, internally, needs to calculate X, then divide something by X, and X turns out to be zero? How can you make it practical to avoid ever calling that complex function with arguments that might cause X to be zero?

59 minutes ago, Xer0botXer0 said:

I'm using eclipse and it's such a mission to create a new project every time just to test something out.

Nah :)

Make a project for random garbage experiments, scratch space where you can do test stuff so it doesn't pollute your real projects.

For a test, make a single Java file (or more if you need more than 1 class), and add a "public static int main(String[] args)" method to it. With that method, right-click the file: "run-as -> java application" (at least I think that's the name of the option, I don't have a running Eclipse nearby atm)

Quote

I get the impression that try catch can be entirely avoided by just coding properly.

You are partially correct, many exceptions can be avoided by placing explicit checks instead. In the example you quoted, catching a FileNotFoundException could be considered poor style (abusing exceptions as flow control), an alternative approach would be to check if the file exists explicitly before opening it.

However, as Kylotan mentioned, exceptions can still happen - in your example, even if you manually check whether the file exists, there is a "race condition" between the two steps - maybe the file is on a network share and the Wi-Fi suddenly drops!

Another source of exceptions can be misunderstanding the code. When the codebase becomes large, and the team working on it grows, it can happen that two pieces of code with differing expectations can interact. For example, maybe one module was written with a strict policy of no "null" references being passed or returned, but another module allows null. If someone who doesn't remember this fact is writing code on the boundary of the two modules, they could pass "null" references into code that isn't prepared to handle it. This can happen even as a solo developer, with enough time passing the code you wrote a year or more ago will feel like it was written by someone else!

Given that exceptions can occur despite your best intentions, it is very common in Java programs to have a high level try ... catch statement to handle all unexpected exceptions that were thrown, and try to show some kind of error report to the user. Even if you're quite thorough in trying to avoid exceptions, once you release software into the wild world someone will eventually break your program, so having a way to capture the error so that the user can submit a useful bug report to you can be quite useful.

A related issue is throwing your own exceptions. Obviously, you have limited choice when it comes to exceptions that the language, standard library or crucial third party libraries might throw at you. It can be a quick way to "bail out" of an operation, but overuse of exceptions can make the program difficult to reason about and can contribute to requiring catch blocks to handle them. Whether to use exceptions over return values is somewhat of a philosophical question.

I would say that try ... catch is a useful construct, but one that you should try to use sparingly. If you find yourself using it often, consider whether there is some other way to avoid the error in the first place. Note that try ... finally is a different construct, you should not worry about using that where necessary.

My advice would be to avoid throwing exceptions, but don't be afraid to do so when the alternative would be worse. They are particularly suitable for handling programmer error, where something is wrong with the internals of the program, for example a critical array is empty but it should never be. An example of this kind of usage is the "assert" keyword, which can result in an AssertionError being thrown. Trying to handle all unexpected situations with return codes can lead to lots of complexity, because "unexpected" errors can happen in lots of places and you would need to propagate this error to every caller.

This topic is closed to new replies.

Advertisement