Java Exception Handling



Java SE 11 Developer certification (exam number 1Z0-819)

Exception Handling

Headlines
Exception Handling in Java

Exception Handling is a programming style that favors the implementation of a defense strategy when facing up abnormal behaviors, failures or, worst, stronger errors that may come from the Operating System -OS- itself (e.g., no more memory).

Promoted by Ada, “exception management” relies on a self-contained thread of control compared to the “normal” program execution. Exceptions are raised and caught depending upon the chosen defense strategy. In OO programming, exceptions are plain objects and, in Java, they are instantiated from a predefined (extensible) hierarchy of classes.

Raising and catching exceptions

Example Exception_management.Java.zip 

 // A constructor may have declared exceptions as any Java method:
public Temperature(float value,Temperature_unit unit) throws Exception,Invalid_temperature_exception {
    switch(unit) {
        case Celsius: _value = value;
            break;
        case Fahrenheit: _value = (value - 32.F) * 5.F / 9.F;
            break;
        case Kelvin: _value = value + Min;
            break;
        default: throw new Exception("Illegal temperature unit"); // Raising
    }
    if(_value < Min) throw new Invalid_temperature_exception(_value); // Raising
    …
}
…
Temperature t;
try {
    t = new Temperature(18.F,Temperature_unit.Celsius);
}
catch(Exception e) { // Catching: normal program execution is diverted from here
    System.exit(1); // Horrible style, to be avoided!
} // Normal execution goes on from here when the 'e' exception does not occur

User-defined exceptions

Example Exception_management.Java.zip 

public class Invalid_temperature_exception extends Exception {
    private float _value; // Exceptions may have attributes as ordinary objects
    public Invalid_temperature_exception(float value) {
        super("Invalid temperature"); // One calls the one-argument constructor of 'java.lang.Exception'
        _value = value;
    }
    public float value() { // Exceptions may have methods as ordinary objects
        return _value;
    }
}

Precise catching

Example Exception_management.Java.zip 

public void decrement() throws Invalid_temperature_exception {
    _value -= _step;
    if(_value < Min) throw new Invalid_temperature_exception(_value);
}
…
Temperature t = new Temperature();
try {
    t.decrement();
}
catch(Invalid_temperature_exception ite) {
    ite.printStackTrace(); // Old Java style, for debugging purposes only!
}

finally clause

Example Exception_management.Java.zip 

Temperature t = new Temperature();
try {
    t.decrement();
}
catch(Invalid_temperature_exception ite) {
    // 'ite' analysis, partial correction, etc.
    throw ite; // Routing for final processing
}
finally {
    // Resource release, etc.
}

Good and bad practices

Example Exception_management.Java.zip 

try {
    // Any Java statement
}
catch(Throwable t) { // Generic catching is useless
    if(t instanceof NullPointerException) … // Very bad idea because it seems that we intend to test 't' against all of its possible types?
}
…
My_class c1 = new My_class(),c2 = null;
try {
    c2 = (My_class)c1.clone(); // 'clone' in 'java.lang.Object' by default raises an exception
}
catch(CloneNotSupportedException cnse) { // Good style: closer probable exceptions are caught first!
    // 'cnse' as instance of 'CloneNotSupportedException' (including subtypes) is caught and processed…
}
catch(Throwable t) {
    // Any other exception whose type does not comply with 'CloneNotSupportedException'
    // However, one may note that poor information is available since one only knows that 't instanceof CloneNotSupportedException == false'
}

Multi-catch

Example

try {
    // Java statement possibly raising occurrences of 'NoSuchAlgorithmException' or 'UnsupportedEncodingException'
}
catch(java.security.NoSuchAlgorithmException | java.io.UnsupportedEncodingException e) { // The processing of the 'e' exception is shared by two exception types
    // 'e' is instance of 'NoSuchAlgorithmException' or 'UnsupportedEncodingException' (including subtypes of both)…
}

Rule(s)

Example

java.net.URL url = null;
try {
    url = new java.net.URL("https://api.fr.carrefour.io/v1/openapi/items");
    javax.net.ssl.HttpsURLConnection connection = (javax.net.ssl.HttpsURLConnection) url.openConnection();
    …
// 'java.io.MalformedURLException' inherits from 'java.io.IOException' that prevents multi-catch:
} catch (java.net.MalformedURLException murle) { // Daughter class first...
    …
} catch (java.io.IOException ioe) { // Mother class next...
    …
}

Assertions

Rule(s)

Example Exception_management.Java.zip 

public void decrement() { // No declared exception since, potentially, an instance of 'AssertionError' may be raised…
    _value -= _step;
    assert(_value >= Min); 
}
…
Temperature t = new Temperature();
try {
    t.decrement();
} catch(AssertionError ae) { // Run program with '-ea' option
    // 'ae' as instance of 'AssertionError' (including subtypes) is caught and processed…
}

Rule(s)

Example

class SHELL
feature
	… -- Public properties
feature {NONE}
	Enter : CHARACTER is '\r';
	Zero : INTEGER is 0;
	Buffer_size : INTEGER is 256;
 
	buffer : ARRAY[CHARACTER];
	cursor : INTEGER
 
	buffer_cleaning is
		require -- precondition
			not buffer.Void
		do
			buffer.clear_all;
		ensure -- postcondition
			buffer.all_cleared
		rescue
			if … then
			retry
	end; -- buffer_cleaning
	… -- Other private properties
invariant -- invariant
	cursor <= Buffer_size
end -- Shell
“try-with-resource”

The idea of try-with-resource statement block from Java 7 solves historical problems. Typically, having errors when executing any kind of operation on a file invites us to close it in a safely manner. However, the close operation on the java.util.stream.BaseStream<T,S extends BaseStream<T,S>> class itself raises an exception in case of (potential) closing problems.

Before Java 7

Example

My_class c = new My_class();
String filename = "c.My_class.ser"; // 'public class My_class implements java.io.Serializable…'
java.io.FileOutputStream fos = null;
java.io.ObjectOutputStream ous = null;
try {
    fos = new java.io.FileOutputStream(filename);
    ous = new java.io.ObjectOutputStream(fos);
    ous.writeObject(c);
}
catch (java.io.FileNotFoundException fnfe) {/* 'fos = new java.io.FileOutputStream(filename);' failed */}
catch (java.io.IOException ioe) {/* 'ous = new java.io.ObjectOutputStream(fos);' or 'ous.writeObject(c);' failed */}
finally {
    // if (ous != null) ous.close(); // Compilation problem here!
}

From Java 7

Rule(s)

Example Try_with_resources.Java.zip 

try (java.io.ObjectOutputStream ous = new java.io.ObjectOutputStream(fos)) {
    ous.writeObject(c);
}
catch (java.io.IOException ioe) {/* 'ous.writeObject(c);' failed, but 'ous' is nonetheless closed */}
Quiz

Download Heatwave.Java.zip  (or look at source code below to be able to answer to questions)

Q1. First version is replacable (no compilation error) by second version?

Heatwave_period(java.time.ZonedDateTime peak, Temperature temperature) throws Exception { …
Heatwave_period(java.time.ZonedDateTime peak, Temperature temperature) throws Hot, Warm { …
  1. Yes
  2. No

Q2. What statement(s) do(es) not create compilation errors?

  1. catch (Hot | Hotter | Hottest | Warm e) {}
  2. catch (Hot | Warm e) {}
  3. catch (Hot | Warm e) {} catch (Hotter e) {} catch (Hottest) {}
  4. catch (Hottest e) {} catch (Hotter e) {} catch (Hot | Warm e) {}

Q3. What is the best solution?

  1. catch (Hot | Hotter | Hottest | Warm e) {}
  2. catch (Hot | Warm e) {}
  3. catch (Hot | Warm e) {} catch (Hotter e) {} catch (Hottest) {}
  4. catch (Hottest e) {} catch (Hotter e) {} catch (Hot | Warm e) {}

Q4. First version is replacable (no compilation error) by second version?

public Hot() {super("Hot...");}
public Hot(final String message) {super(message);}
public Hot() {super("Chaud...");}
public Hot(final String message) {this(message);} 
  1. Yes
  2. No

Q5. First version is replacable (no compilation error) by second version?

public Hot() {super("Hot...");}
public Hot(final String message) {super(message);}
public Hot() {this("Hot...");}
public Hot(final String message) {super(message);}
  1. Yes
  2. No

Q6. If one wants to handle the "No way!" runtime exception then what is(are) the best format(s)?

  1. catch (Exception e) { … } catch (RuntimeException re) { … }
  2. catch (RuntimeException re) { … } catch (Exception e) { … }
  3. catch (Exception | RuntimeException e) { … }
  4. catch (Exception & RuntimeException e) { … }
  5. catch (RuntimeException | Exception e) { … }
  6. catch (RuntimeException & Exception e) { … }

Program source code

public class Hot extends Exception {
    public static Temperature Ceil;
    static {
        try { Ceil = new Temperature(27.F, Temperature.Temperature_unit.Celsius); }
        catch (Exception e) { System.exit(-1); }
    }
    public Hot() { super("Hot..."); }
    public Hot(final String message) { super(message); }
}
…
public class Hotter extends Hot {
    public static com.franckbarbier.Temperature.Temperature Ceil;
    static {
        try { Ceil = new Temperature(32.F, Temperature.Temperature_unit.Celsius); }
        catch (Exception e) { System.exit(-1); }
    }
    public Hotter() { super("Hotter..."); }
    public Hotter(final String message) { super(message); }
}
…
public class Hottest extends Hotter {
    public Hottest() { super("Heatwave..."); }
}
…
public class Warm extends Exception {
    public static Temperature Ceil;
    static {
        try { Ceil = new Temperature(21.F, Temperature.Temperature_unit.Celsius); }
        catch (Exception e) { System.exit(-1); }
    }
    public Warm() { super("Warm..."); }
}
…
public static void main(String[] args) {
    Temperature temperature;
    try {
        temperature = new Temperature(43.F, Temperature.Temperature_unit.Celsius);
        try {
            java.time.ZonedDateTime heatwave_date = java.time.LocalDateTime.of(2022, 06, 18, 16, 0, 0)
                .atZone(java.time.ZoneId.of("Europe/Paris"));
            Heatwave_period hwp = new Heatwave_period(heatwave_date, temperature);
        }
        catch (Exception e) { /* ... */ } // Q2 and Q3
        try {
            java.time.ZonedDateTime heatwave_date = java.time.LocalDateTime.of(2022, 07, 18, 16, 0, 0)
                .atZone(java.time.ZoneId.of("Europe/Paris"));
            Heatwave_period hwp = new Heatwave_period(heatwave_date, temperature);
        }
        catch (Exception e) { /* ... */ } // Q2 and Q3
    }
    catch (Exception e) { System.exit(-1); }
}
…
public class Heatwave_period {
    Heatwave_period(java.time.ZonedDateTime peak, Temperature temperature) throws Exception {
        switch (Season(peak)) { // Quelle saison ?
            case Summer:
                if (temperature.greaterThan(Hot.Ceil)) {
                    if (temperature.greaterThan(Hotter.Ceil)) {
                        throw new Hottest();
                    } else {
                        throw new Hotter();
                    }
                }
                throw new Hot();
            case Autumn: // On espère qu'il n'y pas de canicule en automne :
                throw new Warm();
            case Winter: // Ni en hiver d'ailleurs...
                // ...
                break;
            case Spring: // Canicule du 18 juin 2022 en France trois jours avant l'été :
                if (temperature.greaterThan(Warm.Ceil)) {
                    if (temperature.greaterThan(Hot.Ceil)) {
                        if (temperature.greaterThan(Hotter.Ceil)) {
                            throw new Hottest();
                        } else {
                            throw new Hotter();
                        }
                    }
                }
                throw new Warm();
            case Unknown:
            default:
                throw new RuntimeException("No way!");
        }
    }

    public enum Cities {
        Paris("Europe/Paris"), Sydney("Australia/Sydney");
        private final String _literal;
        Cities(String literal) {
            _literal = literal;
        }
        @Override
        public String toString() {
            return _literal;
        }
    }

    public enum Seasons {
        Summer, Autumn, Winter, Spring, Unknown
    }

    public static Seasons Season(java.time.ZonedDateTime date_time) {
        /*
        * On ne gère que Paris et Sydney : (!Paris => Sydney), ce qui est équivalent à (Paris || !Sydney)
         */
        assert (date_time.getZone().getId().equals(Cities.Paris) || !date_time.getZone().getId().equals(Cities.Sydney));
        int day = date_time.getDayOfMonth(); // [1 - 31]
        java.time.Month month = date_time.getMonth(); // Type énuméré...
        switch (month) {
            case JULY:
            case AUGUST:
                // C'est l'hiver à Sydney :
                return date_time.getZone().getId().equals(Cities.Paris) ? Seasons.Summer : Seasons.Winter;
            case SEPTEMBER:
                return (day < 21)
                        ? date_time.getZone().getId().equals(Cities.Paris) ? Seasons.Summer : Seasons.Winter
                        : date_time.getZone().getId().equals(Cities.Paris) ? Seasons.Autumn : Seasons.Spring;
            case OCTOBER:
            case NOVEMBER:
                // C'est le printemps à Sydney :
                return date_time.getZone().getId().equals(Cities.Paris) ? Seasons.Autumn : Seasons.Spring;
            case DECEMBER:
                return (day < 21)
                        ? date_time.getZone().getId().equals(Cities.Paris) ? Seasons.Autumn : Seasons.Spring
                        : date_time.getZone().getId().equals(Cities.Paris) ? Seasons.Winter : Seasons.Summer;
            case JANUARY:
            case FEBRUARY:
                // C'est l'été à Sydney :
                return date_time.getZone().getId().equals(Cities.Paris) ? Seasons.Winter : Seasons.Summer;
            case MARCH:
                return (day < 21)
                        ? date_time.getZone().getId().equals(Cities.Paris) ? Seasons.Winter : Seasons.Summer
                        : date_time.getZone().getId().equals(Cities.Paris) ? Seasons.Spring : Seasons.Autumn;
            case APRIL:
            case MAY:
                // C'est l'automne à Sydney :
                return date_time.getZone().getId().equals(Cities.Paris) ? Seasons.Spring : Seasons.Autumn;
            case JUNE:
                return (day < 21)
                        ? date_time.getZone().getId().equals(Cities.Paris) ? Seasons.Spring : Seasons.Autumn
                        : date_time.getZone().getId().equals(Cities.Paris) ? Seasons.Summer : Seasons.Winter;
            default:
                return Seasons.Unknown;
        }
    }
}