/* All about exceptions Many modern programming languages (including recent versions of C/C++) have a mechanism for "handling" exceptions. Exceptions are events that are generated out of the normal flow of the algorithm. Most often they indicate error conditions. For example, suppose you want to write a function that returns the smallest number in a list. If the list is empty, what should you return? If you know that all numbers in the list are positive, you can return 0 (or -1) to indicate the special condition. This situation does not require exceptions. But if the list could contain zero or negative numbers, then a conventional solution would not work. The correct way to handle this situation is to "throw an exception." The exception can be "caught" by code that "handles" the exception. WARNING: Please do not confuse exceptions with interrupt handling at the operating system level. Interrupts are global to the process while exceptions have *scope*. Exceptions are a high-level programming language feature. In Java, exception, like most everything else, are Objects. Different kinds of exception are *subclasses* of the superclass Exception. Common exception objects are NullPointerException and IOException. If an exception is not caught and handled, the program will terminate with an error message. In addition to built-in exceptions, you can also define your own exceptions by writing classes that are subclasses of 'Exception': */ class myEXP extends Exception // my own kind of exception { public int x; // information pertaining to my exception public myEXP(int x, String s) { super(s); this.x = x; } } public class exceptionsdemo { public static void f() throws myEXP // 'throws' required without try-catch { // try { int x = -2; if (x<0) throw new myEXP(x,"I thought it was positive"); // } // catches(myExp me) {} // RuntimeExceptions do not have to be in try-catch block // or declared to be thrown: if (x>100) throw new RuntimeException("x is too big"); } public static void main(String[] args) throws Exception { String A = null; int[] B = new int[10]; try { //System.out.println(A.length()); // throws null pointer exception. System.out.println(B[15]); // throws array index exception if (B[0]!=1) // true throw new Exception("some message..."); // user exception f(); } catch(ArrayIndexOutOfBoundsException aiobe) { System.out.println("try another index instead"); } catch(NullPointerException e) {System.out.println(e);} finally { System.out.println("finally caught"); } }//main } /* Some rules. There is one class of exceptions that do not have to have a try block or declaration. These are all subclasses of java.lang.RuntimeException, such as NullPointerException. Other exceptions must be caught within the method, or, if it is to be caught outside the method, the method must be declared to throw the exception. When defining your own exception classes, you can also make it a subclass of RuntimeException. What does "finally" mean? From Oracle's Java documentation: "The finally block always executes when the try block exits. This ensures that the finally block is executed even if an unexpected exception occurs. But finally is useful for more than just exception handling — it allows the programmer to avoid having cleanup code accidentally bypassed by a return, continue, or break. Putting cleanup code in a finally block is always a good practice, even when no exceptions are anticipated." */ /* More advanced discussion: Exceptions are Dynamically Scoped. With few exceptions all modern programming languages use *static scoping* when binding variables. This means (assume in same class): int x = 1; void f() { System.out.println(x); } void g() { int x = 2; f(); } Which x will the call to f print? It will print 1 because x in f is determined by the context in which f was DEFINED, not CALLED. The x that it's referring to is fixed at compile time. If it printed 2, then it would be using *dynamic scoping* and the x it's referring to can be different depending on where the function is called. But exceptions work differently: try { if (..) f1() else f2(); } catch (IOException ie) { ...exception handling code... } if the code inside the try{...} throws an IOException, we cannot be sure which "exception handler", i.e., which *catch* statement will catch the error. f1 and f2 may both have their own try-catch blocks. The exception is always caught by the nearest *catch* as determined during RUNTIME. An exception is a runtime event, not predictable at compile time, and where the exception is caught is also not predictable at compile time. This is a reason that a few languages (Rust) does not have exceptions; instead, functions that could result in errors returns an "Option" value: either OK(result), or Err(message). */