<?xml version="1.0" encoding="UTF-8"?> <chapter annotations="slide" version="5.1" xml:id="sw1ChapterErrorHandling" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xila="http://www.w3.org/2001/XInclude/local-attributes" xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:svg="http://www.w3.org/2000/svg" xmlns:ns="http://docbook.org/ns/transclusion" xmlns:m="http://www.w3.org/1998/Math/MathML" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:db="http://docbook.org/ns/docbook"> <title>Error Handling</title> <figure xml:id="sd1_fig_compileRuntime"> <title>Compile- and runtime errors</title> <programlisting language="java">final int public <co linkends="sd1_fig_compileRuntime-1" xml:id="sd1_fig_compileRuntime-1-co"/> = 33; final String s = null; System.out.println(s.length())<co linkends="sd1_fig_compileRuntime-2" xml:id="sd1_fig_compileRuntime-2-co"/> ;</programlisting> <calloutlist> <callout arearefs="sd1_fig_compileRuntime-1-co" xml:id="sd1_fig_compileRuntime-1"> <para>Compile time error: public is a <xref linkend="glo_Java"/> keyword not to be used as variable's name.</para> </callout> <callout arearefs="sd1_fig_compileRuntime-2-co" xml:id="sd1_fig_compileRuntime-2"> <para>Run time error: De-referencing <code language="java">null</code> yields a <classname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/NullPointerException.html">NullPointerException</classname>.</para> </callout> </calloutlist> </figure> <figure xml:id="sd1_fig_npe"> <title><classname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/NullPointerException.html">NullPointerException</classname> (<acronym>NPE</acronym> for short)</title> <programlisting language="java">final String s = null; System.out.println(s.length());</programlisting> <screen>Exception in thread "main" java.lang.NullPointerException at exceptionhandling.Npe.main(Npe.java:7)</screen> </figure> <figure xml:id="sd1_fig_npe_is_a_class"> <title><classname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/NullPointerException.html">NullPointerException</classname> is a class</title> <mediaobject> <imageobject> <imagedata fileref="Ref/ErrorHandling/npe.multi.svg"/> </imageobject> </mediaobject> </figure> <figure xml:id="sd1_fig_exceptionsAreBeingThrown"> <title>Throwing an exception</title> <programlisting language="java">... if (somethingBadHappens) { throw new <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/NullPointerException.html#%3Cinit%3E()">NullPointerException()</link>; } ...</programlisting> <note> <para>Without countermeasures your program will terminate</para> </note> </figure> <figure xml:id="sd1_fig_npeErrorMessage"> <title>Catching an exception by <code language="java">try {...} catch {...}</code></title> <programlisting language="java">final String s = null; try { System.out.println(s.length()) ; } catch (final <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/NullPointerException.html">NullPointerException</link> e) { System.out.println("Dear user, something bad just happened"); } System.out.println("Business as usual ...");</programlisting> <screen>Dear user, something bad just happened Business as usual ...</screen> </figure> <qandaset defaultlabel="qanda" xml:id="sd1_errorhandling_qanda_CatchingWrongException"> <title>Mind your prey</title> <qandadiv> <qandaentry> <question> <para>We reconsider:</para> <programlisting language="java">final String s = null; try { System.out.println(s.length()) ; } catch (final NullPointerException e) { System.out.println("Dear user, something bad just happened"); } System.out.println("Business as usual ...");</programlisting> <para>What happens if <classname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/NullPointerException.html">NullPointerException</classname> is being replaced by <classname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/OutOfMemoryError.html">OutOfMemoryError</classname>?</para> <para>Is there a way to catch all possible exceptions?</para> </question> <answer> <para>We have:</para> <programlisting language="java">final String s = null; try { System.out.println(s.length()) ; } catch (final OutOfMemoryError e) { System.out.println("Dear user, something bad just happened"); } System.out.println("Business as usual ...");</programlisting> <para>The runtime system throws a <classname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/NullPointerException.html">NullPointerException</classname> which is no subclass of <classname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/OutOfMemoryError.html">OutOfMemoryError</classname>:</para> <mediaobject> <imageobject> <imagedata fileref="Ref/ErrorHandling/qandaNpeMemory.svg"/> </imageobject> </mediaobject> <para>If <classname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/NullPointerException.html">NullPointerException</classname> was a subclass of <classname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/OutOfMemoryError.html">OutOfMemoryError</classname> it would still be caught. But lacking an (upcast) inheritance relationship we are being left with the runtimes default terminating behaviour:</para> <screen>Exception in thread "main" <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/NullPointerException.html">java.lang.NullPointerException</link> at exceptionhandling.NpeMsg.main(NpeMsg.java:8)</screen> </answer> </qandaentry> </qandadiv> </qandaset> <figure xml:id="sd1_fig_tryCatchSyntax"> <title><code language="java">try {...} catch {...}</code> syntax</title> <programlisting language="java">try { [code that may throw an exception] }[catch (ExceptionType-1 e) { [code that is executed when ExceptionType-1 is thrown] }] [catch (ExceptionType-2 e) { [code that is executed when ExceptionType-2 is thrown] }] ... } [catch (ExceptionType-n e) { [code that is executed when ExceptionType-n is thrown] }] [finally { [code that runs regardless of whether an exception was thrown]] }]</programlisting> </figure> <section xml:id="sd1_errorhandling_sect_checkedVsUnchecked"> <title>Checked vs unchecked exceptions</title> <figure xml:id="sd1_errorhandling_fig_checkedVsUnchecked"> <title>Checked and unchecked exceptions</title> <informaltable border="0"> <tr> <td valign="top"><programlisting language="none">public static void main(String[] args) { final Path sourcePath = Paths.get("/tmp/test.txt"), destPath = Paths.get("/tmp/copy.java"); // Compile time error: // <emphasis role="red" xlink:href="https://docs.oracle.com/javase/10/docs/api/java/io/IOException.html">Unhandled exception: java.io.IOException</emphasis> Files.copy(sourcePath, destPath); ...</programlisting></td> <td valign="top"><programlisting language="none">public static void main(String[] args) { final String s = null; // <emphasis role="red">No problem</emphasis> System.out.println(s.length());</programlisting></td> </tr> </informaltable> </figure> <figure xml:id="sd1FigExceptionBasics"> <title>Checked and unchecked exceptions</title> <mediaobject> <imageobject> <imagedata fileref="Ref/ErrorHandling/exception.multi.svg"/> </imageobject> </mediaobject> </figure> </section> <section xml:id="sd1_errorhandling_exceptionsAndJunit"> <title>Exceptions and <xref linkend="glo_Junit"/></title> <figure xml:id="sd1_errorHandling_fig_junitExpectedException"> <title>Expected exceptions in <xref linkend="glo_Junit"/></title> <programlisting language="java"><link xlink:href="https://junit.org/junit4/javadoc/latest/org/junit/Test.html">@Test</link>(<link xlink:href="https://junit.org/junit4/javadoc/latest/org/junit/Test.html#expected()">expected</link> = <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/nio/file/FileAlreadyExistsException.html">FileAlreadyExistsException</link>.class) public void copyFile() throws IOException { final Path source = Paths.get("/tmp/source.txt"), dest = Paths.get("/tmp/dest.txt"); <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/nio/file/Files.html#copy(java.nio.file.Path,java.nio.file.Path,java.nio.file.CopyOption...)">Files.copy</link>(source, dest); // May work. <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/nio/file/Files.html#copy(java.nio.file.Path,java.nio.file.Path,java.nio.file.CopyOption...)">Files.copy</link>(source, dest); // Failure: <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/nio/file/FileAlreadyExistsException.html">FileAlreadyExistsException</link> }</programlisting> </figure> <qandaset defaultlabel="qanda" xml:id="sd1_errorhandling_qanda_junitExpectedException"> <title>Expected exception test failure</title> <qandadiv> <qandaentry> <question> <para>We reconsider:</para> <programlisting language="java">@Test(expected = FileAlreadyExistsException.class) public void copyFile() throws IOException { final <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/nio/file/Path.html">Path</link> source = <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/nio/file/Paths.html#get(java.lang.String,java.lang.String...)">Paths.get</link>("/tmp/source.txt"), dest = <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/nio/file/Paths.html#get(java.lang.String,java.lang.String...)">Paths.get</link>("/tmp/dest.txt"); <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/nio/file/Files.html#copy(java.nio.file.Path,java.nio.file.Path,java.nio.file.CopyOption...)">Files.copy</link>(source, dest); // May work. <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/nio/file/Files.html#copy(java.nio.file.Path,java.nio.file.Path,java.nio.file.CopyOption...)">Files.copy</link>(source, dest); // Failure: <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/nio/file/FileAlreadyExistsException.html">FileAlreadyExistsException</link> }</programlisting> <para>Modify this code by catching the exception inside <methodname>copyFile()</methodname> using <code language="java">try {...} catch {...}</code>. Then execute the test. What do you observe?</para> </question> <answer> <para>We catch the exception in question:</para> <programlisting language="java"><link xlink:href="https://junit.org/junit4/javadoc/latest/org/junit/Test.html">@Test</link>(<link xlink:href="https://junit.org/junit4/javadoc/latest/org/junit/Test.html#expected()">expected</link> = <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/nio/file/FileAlreadyExistsException.html">FileAlreadyExistsException</link>.class) public void copyFile() throws IOException { final Path source = Paths.get("/tmp/source.txt"), dest = Paths.get("/tmp/dest.txt"); try { <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/nio/file/Files.html#copy(java.nio.file.Path,java.nio.file.Path,java.nio.file.CopyOption...)">Files.copy</link>(source, dest); // May work. <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/nio/file/Files.html#copy(java.nio.file.Path,java.nio.file.Path,java.nio.file.CopyOption...)">Files.copy</link>(source, dest); // Failure: FileAlreadyExistsException } catch (final <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/nio/file/FileAlreadyExistsException.html">FileAlreadyExistsException</link> e) { System.out.println("Destination file already exists"); } }</programlisting> <para>Since we swallow the <classname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/nio/file/FileAlreadyExistsException.html">FileAlreadyExistsException</classname> ourselves it is no longer being thrown. Due to the <code>@Test(expected = FileAlreadyExistsException.class)</code> annotation test execution now fails:</para> <screen>Destination file already exists java.lang.AssertionError: Expected exception: <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/nio/file/FileAlreadyExistsException.html">java.nio.file.FileAlreadyExistsException</link></screen> </answer> </qandaentry> </qandadiv> </qandaset> </section> <section xml:id="sd1_errorhandling_sect_variants"> <title>Variants</title> <figure xml:id="sd1_errorHandling_fig_justFinally_"> <title>Just <code language="java">finally</code>, no <code language="java">catch</code></title> <programlisting language="java"><link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/util/Scanner.html">Scanner</link> scanner = null; try { scanner = new <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/io/InputStream.html">Scanner(</link>System.in); ... // Something may fail } finally { if (null != scanner) { scanner.<link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/util/Scanner.html#close()">close()</link>; // Clean up, save resources! } }</programlisting> </figure> <figure xml:id="sd1_errorHandling_fig_tryWithResources"> <title><code language="java">try-with-resources</code> (<xref linkend="glo_Java"/> 7)</title> <programlisting language="java">try (final <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/util/Scanner.html">Scanner</link><co linkends="sd1_errorHandling_fig_tryWithResources-1" xml:id="sd1_errorHandling_fig_tryWithResources-1-co"/> scanner<co linkends="sd1_errorHandling_fig_tryWithResources-2" xml:id="sd1_errorHandling_fig_tryWithResources-2-co"/> = new Scanner(System.in)) { ... // Something may fail }<co linkends="sd1_errorHandling_fig_tryWithResources-3" xml:id="sd1_errorHandling_fig_tryWithResources-3-co"/></programlisting> <calloutlist> <callout arearefs="sd1_errorHandling_fig_tryWithResources-1-co" xml:id="sd1_errorHandling_fig_tryWithResources-1"> <para>Class must implement interface <classname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/AutoCloseable.html">AutoCloseable</classname>.</para> </callout> <callout arearefs="sd1_errorHandling_fig_tryWithResources-2-co" xml:id="sd1_errorHandling_fig_tryWithResources-2"> <para>Variable <code language="java">scanner</code>'s scope limited to block.</para> </callout> <callout arearefs="sd1_errorHandling_fig_tryWithResources-3-co" xml:id="sd1_errorHandling_fig_tryWithResources-3"> <para><methodname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/util/Scanner.html#close()">close()</methodname> method will be called automatically before leaving block scope.</para> </callout> </calloutlist> </figure> </section> <section xml:id="sd1_errorhandling_sect_classException"> <title>Class <classname xlink:href="https://docs.oracle.com/javase/10/docs/api/index.html?java/io/File.html">java.lang.Exception</classname></title> <figure xml:id="sd1_errorHandling_fig_stackTrace"> <title>Method <methodname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/Throwable.html#printStackTrace()">printStackTrace()</methodname></title> <informaltable border="0"> <tr> <td valign="top"><programlisting language="java" linenumbering="numbered">package exceptionhandling; public class StackTrace { public static void main( String[] args){ a(); } static void a() { b();} static void b() { c();} static void c() { String s = null; s.length(); } }</programlisting></td> <td valign="top"><screen>Exception in thread "main" <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/NullPointerException.html">java.lang.NullPointerException</link> at ex.Trace.c(Trace.java:10) at ex.Trace.b(Trace.java:7) at ex.Trace.a(Trace.java:6) at ex.Trace.main(Trace.java:4)</screen></td> </tr> </informaltable> </figure> <figure xml:id="sd1_errorHandling_fig_ascendingInheritOrder"> <title>Ascending inheritance ordering</title> <informaltable border="0"> <tr> <td valign="top"><programlisting language="java">try { <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/io/FileInputStream.html">FileInputStream</link> f = new <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/io/FileInputStream.html#%3Cinit%3E(java.io.File)">FileInputStream( new File("test.txt"))</link>; } catch(final <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/io/FileNotFoundException.html">FileNotFoundException</link> e) { System.err.println( "File not found"); } catch (final <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/io/IOException.html">IOException</link> e) { System.err.println( "IO error"); } catch(final <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/Exception.html">Exception</link> e) { System.err.println("General error"); }</programlisting></td> <td valign="top"><mediaobject> <imageobject> <imagedata fileref="Ref/ErrorHandling/fileNotFoundHierarchy.svg"/> </imageobject> </mediaobject></td> </tr> </informaltable> </figure> <figure xml:id="sd1_errorHandling_fig_descendingInheritOrder"> <title>Descending inheritance ordering</title> <informaltable border="0"> <tr> <td valign="top"><programlisting language="none">try { FileInputStream f = new <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/io/FileInputStream.html">FileInputStream</link>( new <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/io/File.html#%3Cinit%3E(java.lang.String)">File("test.txt"))</link>; } catch(Exception e) { System.err.println("General error"); } catch (<emphasis role="red" xlink:href="https://docs.oracle.com/javase/10/docs/api/java/io/IOException.html">IOException e</emphasis>) { System.err.println( "IO error"); } catch(<emphasis role="red" xlink:href="https://docs.oracle.com/javase/10/docs/api/java/io/FileNotFoundException.html">FileNotFoundException e</emphasis>) { System.err.println("File not found"); }</programlisting></td> <td valign="top"><mediaobject> <imageobject> <imagedata fileref="Ref/ErrorHandling/fileNotFoundHierarchy.svg"/> </imageobject> </mediaobject></td> </tr> </informaltable> </figure> <figure xml:id="sd1_errorHandling_fig_implementCardinalSimple"> <title>Implementing <methodname>convert</methodname></title> <programlisting language="java">/** * Translate {"one", "two", "three"} to {"first", "second", "third"} * @param input The input String to be translated. * @return See above explanation. */ static public String convert(final String input) { switch (input) { case "one": return "first"; case "two": return "second"; case "three": return "third"; default: return "no idea for " + input; } }</programlisting> </figure> <figure xml:id="sd1_errorHandling_fig_cardinalThrowError"> <title>Problem: <quote>Silent</quote> errors</title> <itemizedlist> <listitem> <para>Return false result, application continues.</para> </listitem> <listitem> <para>Solution: Throw an exception. Steps:</para> <orderedlist> <listitem> <para>Find a suitable exception base class.</para> </listitem> <listitem> <para>Derive a corresponding exception class</para> </listitem> <listitem> <para>Throw the exception accordingly.</para> </listitem> <listitem> <para>Test correct behaviour.</para> </listitem> </orderedlist> </listitem> </itemizedlist> </figure> <figure xml:id="sd1_errorHandling_fig_cardinalThrowErrorStep1"> <title>Step 1: Find exception base class</title> <itemizedlist> <listitem> <para>Problem happens on wrong argument to <methodname>convert(...)</methodname>.</para> </listitem> <listitem> <para>Use <classname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/IllegalArgumentException.html">IllegalArgumentException</classname>.</para> </listitem> </itemizedlist> </figure> <figure xml:id="sd1_errorHandling_fig_cardinalThrowErrorStep2"> <title>Step 2: Derive <classname>CardinalException</classname></title> <programlisting language="java">public class CardinalException extends <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/IllegalArgumentException.html">IllegalArgumentException</link> { public CardinalException(final String msg) { <link xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/IllegalArgumentException.html#%3Cinit%3E(java.lang.String)">super(msg)</link>; } }</programlisting> </figure> <figure xml:id="sd1_errorHandling_fig_cardinalThrowErrorStep3"> <title>Step 3: Throwing <classname>CardinalException</classname></title> <programlisting language="java">/** * Translate {"one", "two", "three"} to {"first", "second", "third"} * @param input The input String to be translated. * @return See above explanation. * @throws CardinalException If input not from list. */ static public String convert(final String input) throws CardinalException { switch (input) { case "one": return "first"; case "two": return "second"; case "three": return "third"; } throw new CardinalException( "Sorry, no translation for '" + input + "' on offer"); }</programlisting> </figure> <figure xml:id="sd1_errorHandling_fig_cardinalThrowErrorStep4"> <title>Step 4: Unit test throwing <classname>CardinalException</classname></title> <programlisting language="java"><link xlink:href="https://junit.org/junit4/javadoc/latest/org/junit/Test.html">@Test</link> public void testRegular() { Assert.<link xlink:href="https://junit.org/junit4/javadoc/latest/org/junit/Assert.html#assertEquals(java.lang.Object,%20java.lang.Object)">assertEquals</link>("second", Cardinal.convert("two")); } <link xlink:href="https://junit.org/junit4/javadoc/latest/org/junit/Test.html">@Test</link>(<link xlink:href="https://junit.org/junit4/javadoc/latest/org/junit/Test.html#expected()">expected</link> = CardinalException.class) public void testException() { Assert.assertEquals("X", Cardinal.convert("four")); }</programlisting> </figure> </section> </chapter>