Home Java Exceptions in Java, Part II (checked/unchecked)

Exceptions in Java, Part II (checked/unchecked)

by admin

This is the second part of the article (the first part is try-catch-finally ) on a Java language mechanism called exceptions. It is introductory in nature and is designed for beginner developers or those who are just beginning to learn the language.
I also teach a course "Scala for Java Developers." on the online education platform udemy.com (analogous to Coursera/EdX).
1. Magic checked/unchecked
2. Pessimistic mechanism.
3. throws with an unchecked exception
4. Multiple exceptions
5. Either catch or throws
6. Compiler/JVM behavior
7. Overriding and throws
8. Inheritance

1. "Magic" checked/unchecked

The mechanism of exceptional situations in Java is associated with two elements of "magic", i.e., behavior that is not reflected in the source code in any way :
1. "Magic" of java.lang.Throwable – throw, catch, and throws can be exclusively Throwable or its descendants ( we already dealt with in the previous lesson ). This "right" to be in throw, catch, and throws is not reflected in the source code in any way.
2. All exceptional situations are divided into "checked" and "unchecked". This property is inherent to the "root" (Throwable, Error, Exception, RuntimeException) and is inherited. Not visible in the source code of the exception class.
In further examples, just consider that
– Throwable and Exception and all their descendants (except for Error’s and RuntimeException’s descendants) are checked
– Error and RuntimeException and all their descendants – unchecked
checked exception = compiler checked exception.
What exactly the compiler checks, we’ll discuss in this lesson.
Recalling the exception hierarchy

Object|Throwable/ Error Exception|RuntimeException

Set the value of the checked/unchecked property

Object|Throwable(CHECKED)/ Error(UNCHECKED) Exception(CHECKED)|RuntimeException(UNCHECKED)

2. Pessimistic mechanism.

I call the relationship between verifiable exceptions and throws "pessimistic, " in that we can "scare" about more than can actually happen, but not the other way around
We can’t quit, but we can’t warn

public class App {public static void main(String[] args) {throw new Exception(); // there is a compilation error}}> > COMPILATION ERROR: unhandled exception: java.lang.Exception

We can’t quit, but warn about "less"

import java.io.IOException;public class App {public static void main(String[] args) throws IOException {throw new Exception(); // compilation error here}}> > COMPILATION ERROR: unhandled exception: java.lang.Exception

We can warn exactly what we are throwing

public class App {public static void main(String[] args) throws Exception { // warn about Exceptionthrow new Exception(); // and throw Exception}}

We can warn of more than we throw

public class App {public static void main(String[] args) throws Throwable { // warn "whole" Throwablethrow new Exception(); // throw only Exception}}

We can even warn you about something that doesn’t exist at all

public class App {public static void main(String[] args) throws Exception { // scare// but don't throw anything}}

Even if we warn of something that isn’t there, everyone is obliged to be afraid

public class App {public static void main(String[] args) {f(); // there is a compilation error}public static void f() throws Exception {}}> > COMPILATION ERROR: unhandled exception: java.lang.Exception

Although they (the scared ones) may scare the others even more

public class App {// they scare the whole Throwablepublic static void main(String[] args) throws Throwable {f();}// even though we were only frightening with an Exceptionpublic static void f() throws Exception {}}

What is the purpose of "pessimism"? It’s simple enough.
In prototyping mode you "sketched out", say, a class utility for downloading from the Internet

public class InternetDownloader {public static byte[] (String url) throws IOException {return "<html> <body> Nothing! It's stub!</body> </html> ".getBytes();}}

and would like to "force" the users of your class to ALREADY handle a possible IOException, even though you DON’T GENERATE such an exception from a pacifier implementation. But in the future, you intend to.

3. throws with an unckcked exception

Calling a method that "scares" an unchecked exception does not impose any obligations on us.

public class App {public static void main(String[] args) {f();}public static void f() throws RuntimeException {}}

This construct serves the purpose of "telling" the code reader that your method may throw some unchecked exception.
Example (java.lang.NumberFormatException – uncheckable exception):

package java.lang;public final class Integer extends Number implements Comparable<Integer> {.../*** ...** @param s a {@code String} containing the {@code int}* representation to be parsed* @return the integer value represented by the argument in decimal.* @exception NumberFormatException if the string does not contain a* parsable integer.*/public static int parseInt(String s) throws NumberFormatException {return parseInt(s, 10);}...}

Integer.parseInt() can throw an unchecked NumberFormatException on the wrong argument (int k = Integer.parseInt("123abc")), this was reflected by
– In the method signature : throws NumberFormatException
– in the documentation (javadoc): @ exception
But that doesn’t commit us to anything.

4. Multiple Exceptions

Consider the situation with code that can throw testable exceptions of different types.
Next consider that EOFException and FileNotFoundException are descendants of IOException.
We can specify exactly what we throw away

import java.io.EOFException;import java.io.FileNotFoundException;public class App {// scaring WITH BOTH exceptionspublic static void main(String[] args) throws EOFException, FileNotFoundException {if (System.currentTimeMillis() %2 == 0) {throw new EOFException();} else {throw new FileNotFoundException();}}}

or like this

import java.io.EOFException;import java.io.FileNotFoundException;public class App {// scaring WITH BOTH exceptionspublic static void main(String[] args) throws EOFException, FileNotFoundException {f0();f1();}public static void f0() throws EOFException {...}public static void f1() throws FileNotFoundException {...}}

And we can "scare" more (ancestor of both exceptions)

import java.io.EOFException;import java.io.FileNotFoundException;import java.io.IOException;public class App {// scaring the ANCESTOR of exceptionspublic static void main(String[] args) throws IOException {if (System.currentTimeMillis() %2 == 0) {throw new EOFException();} else {throw new FileNotFoundException();}}}

or like this

import java.io.EOFException;import java.io.FileNotFoundException;public class App {// scaring the ANCESTOR of exceptionspublic static void main(String[] args) throws IOException {f0();f1();}public static void f0() throws EOFException {...}public static void f1() throws FileNotFoundException {...}}

You could also do this: EOFException and FileNotFoundException "generalize to" IOException, and InterruptedException "skip intact" (InterruptedException is NOT a descendant of IOException)

import java.io.EOFException;import java.io.FileNotFoundException;public class App {public static void main(String[] args) throws IOException, InterruptedException {f0();f1();f2();}public static void f0() throws EOFException {...}public static void f1() throws FileNotFoundException {...}public static void f2() throws InterruptedException {...}}

5. Either catch or throws

Don’t be intimidated by the fact that you intercepted
so

public class App {public static void main(String[] args) {try {throw new Exception();} catch (Exception e) {// ...}}}

or like this (put a catch on the ancestor and intercept exactly)

public class App {public static void main(String[] args) {try {throw new Exception();} catch (Throwable e) {// ...}}}

But if only a descendant is intercepted, it will not work

public class App {public static void main(String[] args) {try {throw new Throwable();} catch (Exception e) {// ...}}}> > COMPILATION ERROR: unhandled exception: java.lang.Throwable

Not good, of course, and intercepting "brother"

public class App {public static void main(String[] args) {try {throw new Exception();} catch (Error e) {// ...}}}> > COMPILATION ERROR: unhandled exception: java.lang.Exception

If you have intercepted a part, you don’t have to be scared by it

import java.io.EOFException;import java.io.FileNotFoundException;public class App {// EOFException intercepted by catch, we don't scare thempublic static void main(String[] args) throws FileNotFoundException {try {if (System.currentTimeMillis() % 2 == 0) {throw new EOFException();} else {throw new FileNotFoundException();}} catch (EOFException e) {// ...}}}

6. Compiler/JVM behavior

It must be understood that
the chewed exception is checked at the time of compilation (compile-time checking)
catch happens at runtime checking

public class App {// scare Exception.public static void main(String[] args) throws Exception {Throwable t = new Exception(); // and it will fly Exceptionthrow t; // but there is a compilation error}}> > COMPILATION ERROR: unhandled exception: java.lang.Throwable

Full analogy with

public class App {public static void main(String[] args) throws Exception {Object ref = "Hello!" // ref points to the stringchar c = ref.charAt(0); // but there is a compilation error}}> > COMPILATION ERROR: Cannot resolve method 'charAt(int)'

Although the REFERENCE ref is pointing to an object of type java.lang.String (which has the charAt(int) method), but the TYPE of the REFERENCE is java.lang.Object (a reference of type java.lang.Object to an object of type java.lang.String).The compiler focuses on the "lefty type" (the type of the reference, not the referenced type) and won’t allow such code to pass.
Although in THIS SITUATION the compiler could make out that t refers to Exception and ref refers to String, this is no longer possible when compiling separately. Imagine we CAN compile such a class separately, pack it in a jar, and distribute it

// DOES NOT COMPILE! LET'S EXPLORE A HYPOTHETICAL SITUATION!public class App {public static void f0(Throwable t) throws Exception {throw t;}public static void f1(Object ref){char c = ref.charAt(0);}}

And someone takes that class, adds it to the classpath, and calls App.f0(new Throwable()) or App.f1(new Integer(42)). In that case, the JVM would be faced with a situation where it is required to throw a checkable exception that the compiler hasn’t traced (in the case of f0) or call a method that doesn’t exist (in the case of f1)!
Java is a language with static typing (i.e. the compiler tracks correctness of types usage (presence of used fields, presence of called methods, check for checked exceptions, …) and forbids this behavior. In some languages (languages with dynamic typing), type correctness tracking is performed by the execution environment (it is allowed, for example, in JavaScript).
The compiler will not let this code through, although the main method is GUARANTEED NOT to EXCEED the EXCEPTION

public class App {// scare Exception.public static void main(String[] args) throws Exception {try {Throwable t = new Exception(); // and it will fly Exceptionthrow t; // but there is a compilation error} catch (Exception e) {System.out.println("Intercepted!")}}}> > COMPILATION ERROR: unhandled exception: java.lang.Throwable

public class App {//Now we're scaring Throwable.public static void main(String[] args) throws Throwable {try {Throwable t = new Exception(); // this will be an Exceptionthrow t;} catch (Exception e) { // and we'll catch the ExceptionSystem.out.println("Intercepted!");}}}> > Intercepted!

7. Overriding and throws

When overriding, the descendant’s exception list does not have to be the same as the ancestor’s.
But it must not be "stronger" than the ancestor’s list:

import java.io.FileNotFoundException;import java.io.IOException;public class Parent {// ancestor scares IOException and InterruptedExceptionpublic void f() throws IOException, InterruptedException {}}class Child extends Parent {// and the descendant scares only the descendant IOException@Overridepublic void f() throws FileNotFoundException {}}

But here we have tried to "broaden the type" of exceptions thrown

import java.io.IOException;public class Parent {public void f() throws IOException, InterruptedException {}}class ChildB extends Parent {@Overridepublic void f() throws Exception {}}> > COMPILATION ERROR: overridden method does not throw 'java.lang.Exception'

Why can you narrow the type but not expand it?
Consider the following situation :

public class Parent {// ancestor scares Exceptionpublic void f() throws Exception {}}

// there is a compile-time error in Java, but ACCEPT that there isn't.public class Child extends Parent {// child extends Exception to Throwablepublic void f() throws Throwable {}}

public class Demo {public static void test(Parent ref) {// this is where everything compiles, Parent.f() scares the Exception and we catch it catchtry {ref.f();} catch(Exception e) {}}}

public class App {public static void main(String[] args) {// this is where everything compiles, Demo.test wanted Parent and we gave it a subtype - ChildDemo.test(new Child());}}

Look closely at this example – if a descendant could expand the type of ancestor exception thrown, then those places that "wait" for the ancestor and get an instance of the "expanded" descendant could uncontrollably throw checkable exceptions

8. Inheritance of a property

Recall exception hierarchy with checked/unchecked property flags

Object|Throwable(CHECKED)/ Error(UNCHECKED) Exception(CHECKED)|RuntimeException(UNCHECKED)

The logic of the location of a property is NOT RELATIVE to OWNERSHIP. We will discuss this logic later (in the following articles).
However, the checked/unchecked property of custom exception classes is built EXCLUSIVELY ON THE BASIS OF HERITAGE.
The rule is extremely simple :
1. If the exception is from Throwable, Error, Exception, RuntimeException – then your property should just be remembered.
2. If you’re not from a list, then your property is equal to the ancestor property. You can’t break inheritance here.
If we generate descendants A, B, C, D, E, F, G, H, I, J, K, L which inherit from the "root" as follows (Throwable, Error, Exception, RuntimeException), then the value of their checked/unchecked property can be seen in the diagram

Object|Throwable(CHECKED)/ | Error(UNCHECKED) | Exception(CHECKED)| | | | |A(UNC) D(UNC) | F(C) RuntimeException(UNCHECKED)/ | / | |B(UNC) C(UNC) | G(C) H(C) I(UNC) J(UNC)E(C) / K(UNC) L(UNC)

Contacts

I do online Java training ( programming courses ) and I publish some of the tutorials as part of the recycling of the Java Core course You can see videos of the classroom lectures at youtube channel , perhaps the channel’s videos are better systematized in this article
My method of teaching is that I

  1. Showing different applications
  2. I build a progressively more complicated sequence of examples for each option
  3. explain the logic that moved the authors (as much as possible)
  4. give a large number of tests (50-100) comprehensively testing understanding and demonstrating various combinations
  5. giving labs for independent work
  6. This article follows #1 (various options) and #2(sequence of examples for each option).
    skype: GolovachCourses
    email: GolovachCourses@gmail.com

You may also like