No. For many reasons.
Code is pointless
Exception
is the supertype of both other exceptions, but the code is the same. You've written:
- If the animal is a Pomeranian, say "Woof".
- If the animal is a german shepherd, say "Woof".
- If the animal is a dog of any kind, say "Woof".
You can just delete the first two lines in that strategy for the exact same effect. When an exception occurs, all 'catch' blocks are checked, in order (except more efficiently than literally checking one by one) - the first catch block whose exception type is what was thrown or a supertype of it, is triggered.
Exception messages aren't reliable
An exception is just an object. It has a type (such as ArithmeticException
, and note that the exception type tree is very hierarchical. For example, FileNotFoundException
extends IOException
extends Exception
extends Throwable
. (Which is the root type of all things you can throw and catch).
They 'communicate' what the problem is, and do so in ways that are intended for the programmer, and in ways that are intended for the user, but mostly the programmer. All exceptions have the following information available in them, which gives information about what happened:
- The type (example:
FileNotFoundException
)
- The message (example:
"Access denied: foo.txt"
)
- The stack trace (this shows the call stack and lets you know where in the code it happened)
- The 'causal chain' - Often an exception occurs because of another exception, and often that other 'underlying' exception is the more interesting one. For example, a
SaveGameException
occurs, but the underlying reason for that is an IOException that contains the text 'disk full' which is much more useful to fix the problem. Given that a cause can have a cause which can have a cause, and so on - it's a chain.
- The suppressed list. This is a list of exceptions that also occurred but which are likely less important. It's rarely interesting to look at these, and they occur when using try-with-resources.
- Custom stuff. As I said, exceptions are just types, and therefore you are free to add more stuff. SQLException in particular has plenty of extremely useful extra's, such as the SQL DB engine's error code.
Of all that data, the only one that's potentially useful to relay directly to a user is .getMessage()
, but most exception types (it's just code - its up to the person who writes that code to decide how it works!) do not work that way - their message contains the specifics, meant for the programmer, not the user. They are the parameterized data that 'goes with' the type. In other words, the message makes no sense without the context of what type the exception was, and in general isn't sensible to just verbatim relay to the user.
For example, a FileNotFoundException
will have as message just a file name. Telling the user: Error: foo.txt
is mostly useless; they have no idea what the heck happened or what that means. Showing them FileNotFoundException: foo.txt
, now we're getting somewhere.
Separate issue: Dividing by 0
(double)num1/num2
is parsed and compiled as this:
- First, take
num1
, and convert its value to a double
value.
- Then, execute the operation
someDouble / someInt
(where someDouble
is the converted-to-a-double
-already num1
variable, and the someInt
is the num2
variable). This isn't possible, of course (in pretty much all programming languages, both numeric types need to be identical before you can do any math on em), and the java spec dictates how to resolve that problem: By 'widening' the 'narrower' type. In the spec, int is the 'narrower' type, so...
- The compiler also adds a separate 'convert this to a double' instruction for
num2
. We now have 2 doubles.
- Now the compiler executes the math operation.
- ... and
0.0 / 0.0
does not throw any exceptions. Unlike in integer math, doubles have magic values for NaN (not a number), Infinity, and negative Infinity. 5.0/0.0
is simply Infinity. 0.0/0.0
is simply NaN. This, your double value is now NaN. This is then passed to the String.format
code which renders it as, I think, literally the text "NaN"
. No exception occurs.
To fix this, you have 2 options: Either first do integer math (so, just num1 / num2
, and THEN cast to a double: (double) (num1 / num2)
) - but I don't think you want that. In integer math, e.g. 5/2
is just 2 (not 2.5
! That would take a double and we're doing all integer math), and THEN you turn that into a double, which is 2.0, and not what you want.
The other option is simply to accept that exceptions is just not how it works.
If you want to print Error:
then either check if the double is nan/positive infinity/negative infinity, or perhaps simply check if num2
is 0:
if (num2 == 0) {
System.out.println("Error: Divisor cannot be 0");
} else {
// do the math stuff
}
No try/catch needed at all.
So how do I show exceptions to a user?
You either know what the exception is caused by and what the 'message' part of it means and you translate it all into something to show to your user, OR, you just show the entire thing verbatim. Yes, this will look like scary programmerese to the user. There is no generic 'hey, error, can you render yourself into a form that looks friendly to a near computer illiterate user for me'. It just does not exist.
Hiding information from users sucks, so don't be tempted by this. Just show them the scary screenful, but feel free to couch it in a warning first. Something like this:
} catch (IOException e) {
System.err.println("Saving your game failed!");
System.err.println("The problem appears to be related to the file system. Perhaps this will help you in figuring out the problem: ");
e.printStackTrace(); // see note
}
And note that the printStackTrace
method is a lie. It actually prints the type, the message, and the stack trace, and the causal chain. If you just want to print the type and the message, System.err.println(e.toString())
does that, but given that this hides the causal chain, that is somewhat likely to hide the actual useful info.