Thursday, May 10, 2012

GIST NOTES 5 - Java Flow Control, Exceptions & Assertions


GIST NOTES 5 - Java Flow Control, Exceptions & Assertions

[DISCLAIMER: This is solely for non-commercial use. I don't claim ownership of this content. This is a crux of all my readings studies and analysis. Some of them are excerpts from famous books on  the subject. Some of them are my contemplation upon experiments with direct hand coded code samples using IDE or notepad.


I've created this mainly to reduce an entire book into few pages of critical content that we should never forget. Even after years, you don't need to read the entire book again to get back its philosophy. I hope these notes will help you to replay the entire book in your mind once again.]


[JDK 7]

>Exceptions and Assertions are new flavors of flow controls specific to java language
>conditionals: if and switch
>looping constructs: for, while, do
>Exceptions: way to organize code that deals with problems that might crop up at runtime
>Assertions: (from 1.4 onwards) way to do testing and debugging checks on conditions that are apparent at development time; helps to avoid runtime overhead associated with exception handling

if and switch
-------------
>these two are referred to as "decision statements"
>if takes only expressions that evaluate to a boolean value true or false
>one can have zero or one "else" for a given "if" and it must come after all "else ifs"
>zero or more "else ifs" are permitted but should come before "else" if "else" is there at all
>once an "else if" succeeds none of the remaining "else ifs" or "else" will be tested
>when 'if's are nested, and when in confusion regarding which 'if' the 'else' is paired to, go for the closest preceding 'if' that doesn't have 'else'

if(check)
if(check1)
statement;
else
statement1; //this else belongs to check1 if

> in (x && y | z) expression, (y|z) is evaluated first(really?) and with the result && is performed(really??)
  sadly if(($+="c")!="" && ($+="b")!="" | ($+="a")!="")
System.out.println("condition passed, $="+$);
  prints:: condition passed, $=cba

> if(0) is illegal in java; only boolean is allowed in conditionals

>switch: a way to simulate the use of multiple if statements
>general form of switch-case

swtich (expression) {

case constant1: code block
case constant2: code block
default: code block
}

>switch's expression must evaluate to char, byte, short, int or enum; except for enum all others can be automatically promoted to int
>so, essentially switch expression is only friendly to int and enums
>long, float and double values are not allowed(compile error)
>case constants must evaluate to the same type as the switch expression can use
>case constant MUST be a compile time constant or a final variable that was initialized in declaration statement itself

final int a = 1;
final int b;
b = 2;
int x = 3;
switch(x){
case a: //OK
case b: //Compile Error
}

>switch can only check for equality with case constants; other relational operators are unusable in a case
>when case constant is too large for the data type of the expression used in switch, compilation fails

byte b = 2;
switch(b){
case 128: //compile error
}

>two cases with same constant doesn't compile
>simple expression is allowed in place of case constant; but it must resolve at compile time
>autoboxing can be used in switch expression; but cannot be used in case constant
>no wrapper objects are allowed for case constant(compile error)
>default block is optional in switch

Break and Fall-through in switch
--------------------------------

>execution goes from top to bottom
>execution entry point starts at the first case that matches
>all the cases(including default) that follow the first matched case also will be executed; it is up to us to break away
>once a break statement is found, control exits the switch block
>the execution falls through all the cases starting from entry point case
>default option is just like any other case and it can also come in any place not necessarily at the last; fall-through also happens through default option

while loop
----------
>variables used in while condition should be declared prior to while loop
>while(int x = 0) {} is ILLEGAL
>while loop may not run ever at all if the condition fails at the first attempt itself
>expression should resolve to boolean

do-while loop
-------------
>first executes and then only checks for condition to continue
>guaranteed to run at least once
>semicolon is needed at the end of the loop
>expression should resolve to boolean

for loops
----------
>basic for loop: older version of for loop
>for-in/for-each/enhanced-for: newer version (from 6.0 onwards)

for(statement1 ; bool_expression; statement2)
{
//loop body
}

bool_expression - should evaluate to boolean value

statement1 - should be an assignment statement or variable declaration (some kind of variable assignment is expected here); or variable declaration even without initialization is fine; multiple variables of the same type also allowed here, but they should be comma separated

statement2 - should be an assignment statement usually; no variable declaration is allowed here; multiple assignment statements separated by comma are allowed as well; any other java statement is also allowed here

Both statement1 and statement2 cannot be mathematical expressions evaluating to a value or an object

for(Thread t; b
{
//LEGAL for loop provided b and c are valid variables
}

for(Thread t = null, t2 = null, t3 = new Thread(); 1 < 2; b = c, c = b, b = c)
{
//LEGAL for loop; b and c can be any objects as well
}

Execution:

1. statement1: runs only once at the beginning of the for loop initially
2. bool_expression and statement2 are run on every iteration of the for loop
3. scope of the for loop variable ends with for loop; it is not visible outside of the for loop
4. command separated boolean expressions in bool_expressions is NOT permitted
5. statement2 runs from 2nd iteration only; it is not touched on first iteration of the for loop
6. on first iteration statement1 and bool_expression are executed; then loop body is executed
7. on a normal exit from a for loop, evaluation of statement2 and then the evaluation of bool_expression are the last operations done before exiting the for loop (exception: forced exit using break/return/System.exit() statement)

>statement1, bool_expression and statement2 are all optional; and they are all independent

for(;;)
{
//LEGAL for loop which runs infinitely
}

for(Thread t = null, t2 = null, t3 = new Thread(); 1 < 2; b = c, c = b, b = c, System.out.print("Hello"),lp.hi(),new Object())
{
//LEGAL for loop; any arbitrary code with comma separation can go in statement2
}


Enhanced for loop
------------------

>can be used to iterate through an array or collection
>for(declaration : expression) is the general format
>declaration should be of type compatible with the array/collection returned in expression part; this variable receives the element of the array on each iteraion
>'expression' can be a direct array/collection or a method call that returns array/collection which is compatible with the 'declaration' part
>array can be of primitive, objects or array of arrays type

Break and continue
------------------
break - stops the entire loop; should be placed inside a loop (except for labeled break statements)
continue - stops just the current iteration; should be placed inside a loop; otherwise compile error

>when continue ends the current iteration; still the "iteration expression" (stnatement2 in our case) runs; continue just tells to finish up the current iteration prematurely, that is all; nothing else changes

>both break and continue do not change in behavior in case of nested loops; they only act on the inner most loop (except for labeled break)

>continue can be used inside if loop(which is inside a for loop); but it still acts only on for/while/do loops; 'if' is not considered a loop for break or continue

>both continue and break can be unlabeled or labeled

Labeled Statements
------------------
>Labeled varieties are required only when there are nested loops.
>Instead of operating on the innermost loop, they operate on the loop whose lebel is specified
>a label(ending with colon) is placed just before the loop
>label names should be valid variable names

e.g.
outer:
for(;;)
{
while(true)
{
if(true)
break outer; //comes out of for loop instead of while loop
}
}

//"continue outer;" skips the current iteration and goes to the next iteration of the 'for' loop

>> the break and continue statements should be inside the labeled loop; using arbitrary loop names(labels) invites compile error

Exception Handling
==================

>The term "Exception" means "exceptional condition" that alters the normal program flow
>hardware failure, resource exhaustion, coding bugs can cause exceptions
>when this exceptional event occurs in java, an exception is said to be "thrown"
>the code that is responsible for doing something about the exception is called "exception handler", and it "catches" the thrown exception
>exception handling is nothing but transferring the execution of the program to an appropriate exception handler when the exception occurs
>we need a way to tell the JVM what code to execute when exception occurs; that is where try-catch block comes to use
>try block holds the code that may throw exceptions; this region is called "guarded region", that is risky code goes inside try block
>followed by try, one or more catch blocks are provided each block matching a specific exception or a group of exceptions; each catch block has appropriate exception handling code

try{
//dangerous code goes here
} catch(ExceptionOne e) {
//handle exception 1
} catch(ExceptionTwo e) {
//handle exception 2
}

> all catch blocks should immediately follow try block; no code is allowed in-between these blocks;
> order of the catch blocks is crucial
> when an exception occurs in try block at a particular line, the control immediately jumps to one of the catch blocks; remaining code lines in try block are skipped
> after executing one of the matching catch blocks the control comes out of the entire try-catch block; that is only one catch block executes and other catch blocks are skipped

finally block
-------------
>when an exception happens, you might want to do some cleanup; you can't put clean up code in try because some code lines are skipped when exception occurs; if you put it in catch block, then you have to duplicate the clean up code in all catch blocks; so we are saved with finally block to do just that
>finally block executes always regardless of whether exception occurs or not
>even if there is a return statement in try block, finally block still executes right after the control encounters the return statement and before return executes
>so finally block is the ideal place to close files, release sockets, db connections and other cleanup codes
>finally block executes right after try or catch executes depending upon whether exception happened or not
>finally always runs
>if exception is not caught by any catch blocks, still finally runs
>finally block is optional (code will compile without finally)
>catch block also can be omitted if there is try and finally blocks (just the 'try' block will not compile)
>try with only finally is used sometimes to pass the exception back to the calling method

try{ //pass the exception back to the caller
//do stuff
} finally {
//clean up
}

>catch block is not allowed after finally block

>propagating uncaught exception - not all exceptions need to be caught; try without any catch block helps to duck(pass the buck) the exceptions; to let the calling method handle for us; so here the exception is propagated to the previous method in the call stack; the exception may be further passed/ducked by subsequent methods in the call stack till someone is willing to handle it; if no method ever handles that exception even main method, then JVM finally handles it and terminates with a crash

Defining Exceptions
-------------------
>all exceptions in java are objects whose super class is java.lang.Exception
>throwing exception means, some object of subtype of Exception is created and handed down to you
>who create these Exception objects? well, anyone can create one. You can create. Java standard libraries create. The APIs you use would create. JVM creates.

Exception Class Hierarchy
-------------------------

Throwable derives (extends) from Object class.
Error and Exception classes derive from Throwable.
RuntimeException derives from Exception.
If you want to define your own exception, you should derive it from Exception class.

>class Error is thrown when unusual situations occur that are not caused by program errors; e.g. JVM running out of memory; generally application won't be able to recover from these Errors and hence you are not required to handle errors;
>Errors are not Exceptions because Error does not derive from Exception class; not handling Error will not cause compilation failure

>Exception generally indicates resources not availble, communication failure and such genuine conditions, not just bugs
>RuntimeException deriving from Exception do sometimes indicate bugs in the program or program errors; they also indicate rare difficult to handle exceptional conditions

>printStackTrace() method from Throwable class provides a description about the Error or Exception; this method prints all the methods from where the exception started all the way through the stack till the point where it is being caught; if not caught, it prints till main  method

>Throwable class indicates that its objects can be used with keywords 'throw', 'throws', 'catch'

>IndexOutOfBoundsException has two subclasses: ArrayIndexOutOfBoundsException and StringIndexOutOfBoundsException; by catching the superclass exception you can handle both subclass exceptions (handling entire class hierarchy of exceptions)

>catching too many types of exceptions in one 'catch' defeats the design objective; might reduce the reliability of the program because it may not know how to handle certain exceptions caught by it

>more specific exceptions should be caught first and then generic exceptions should be caught; if any catch block tries to catch a specific exception which is already caught by a generic catch block, it causes compile error; trying to catch same exception in two catch blocks causes compile error;

>FileNotFoundException is a subclass of IOException

>unrelated exceptions(no polymorphic relationship) can be caught in any order

>any method that can cause an exception should either handle the exception or declare that in method signature using 'throws' clause; if the exception in question is of type RuntimeException, then no need to handle or declare; unhandled and undeclared RuntimeExceptions will not cause compile error but will terminate the program (if not handled all the way up to main) in the middle of the execution

>all interfaces and classes should declare the exceptions in their method declarations

>if you call another method that throws an exception from your method, either you can handle it, or tell the world an exception might come from your method by declaring it in your method; this is exception propagation

>even methods that are "ducking" the exceptions should declare those exceptions in their signature

>any exception that is not a subtype of RuntimeException is called 'Checked Exception'; all checked exceptions should have been caught and handled or declared to be thrown during compile time; (called 'handle or declare requirement')

>no need to declare RuntimeException; even if you declare, compiler will not complain; also compiler will not make sure the users of your code handle this exception; nobody(compiler, your API users) cares whether you handle RuntimeException or not; but for your program to function properly you have to handle it somewhere of course;

>RuntimeException, Error and all of their subtypes are unchecked exceptions; unchecked exceptions do not have to be specified or handled

>examples for unchecked exceptions: NullPointerException, ArrayIndexOutOfBoundsException, OutOfMemoryError

>checked exceptions are called 'checked' because compiler checks to see if all possible such exceptions are handled in one place or the other; these are the exceptions that are evident in the code structure itself

>you can throw existing java API exceptions or your own exception class

>even main() method can pass the buck by declaring exceptions originating from it

class A {

public static void main(String[] args) throws Exception //LEGAL
{
throw new Exception();
}
}


>Error is not an Exception; but still both of them are throwable; no need to catch or handle Errors; but you can; you can throw an Error object also; usually when error occurs it is impossible to rectify and continue the program - usually JVM exits on Errors.

>neither the compiler, nor the JVM, or me, or the designer, or your manager or anyone else for that matter, expect you to handle Errors (like OutOfMemoryError) in your code

>and I don't know why in the world you would want to throw Errors like OutOfMemoryError yourself in your code

>not only you can throw a new exception, you can also rethrow an exception you just caught in your catch block; that rethrown exception will not be handled by any other peer catch blocks in the same try; after running finally, the rethrown exception is pushed to next method in the call stack

>suppose if you rethrow a checked exception, you have to either handle it or declare it in your method signature; rethrown exception is treated as a fresh one.

Common Exceptions/Errors
-------------------------

JVM thrown exceptions:: errors or exceptions thrown by JVM

NullPointerException: is thrown by JVM while attempting to use a null reference to access members

StackOverflowError: when infinite recursive method calls happen, RAM memory runs out due to growing stack memory and this error is thrown by JVM

Programmatically Thrown Exceptions:: exceptions explicitly thrown by application or API programmers

NumberFormatException: thrown by API when parsed number is malformed

AssertionError: though it is not an exception, it is thrown programmatically

Your own homemade exceptions also fall into the category of programmatically thrown exceptions.

JVM Thrown
-----------
>ArrayOutOfBoundsException
>ClassCastException
>NullPointerException
>ExceptionInInitializerError - from static block
>StackOverflowError
>NoClassDefFoundError - jvm can't find the class file (command line error or classpath issue)

Programmatically Thrown
-----------------------
>IllegalArgumentException - a method receiving bad input argument
>IllegalStateException - state of the environment doesn't match; e.g. trying to use already closed Scanner
>NumberFormatException
>AssertionError

Assertion Mechanism(from java 1.4)
==================================

Assertions let you test your assumptions during development, without the expense of writing exception handlers for exceptions that you assume will never happen once the program is out of development and fully deployed.

>so assertion is not a replacement to exception mechanism?

>It's like this: Say you have a bunch of assumptions about your application during the development time (you are in the phase of figuring out your own application design as you develop one might say); you want to validate whether your assumptions are right or wrong; so you have to put some checks in your program at appropriate points; after you are done with cross checking, you no longer need those check points as part of your application; so you go ahead and remove those check points; well, imagine the effort gone into putting those check points in the first place and then removing them; those check points could be try-catch blocks, if-else blocks; print statements; formal exception handling mechanism is a way costlier to your development time as well as if you leave them around, it might be a performance hit to your application in the deployment; so what do we do? use assertions as check points; when you ship your product, your check points will vanish by turning off the assertions in deployment; no performance hit, no code removal work.

>assertion statement is a little odd; when you assert on a condition, it gets activated only when the condition fails; activated in the sense, it throws AssertionError (yes Error, not Exception); so you can say, assertion raises alarm when one of your good old assumptions goes wrong :)

>other way to say is "assert condition" is equavalent to saying "ensure/mandate this condition";
that is make sure it is true!

>assertions are off by default(the code will run as though there were no assert statements); application will not process assert statements unless otherwise you explicitly turn them on; pretty handy for development time, eh?

>AssertionError is stop-the-world-for-good type; when your assert condition fails program exits then and there

private void doStuff()//flavor 1
{
assert(y>x);//ensure y>x
//more code assuming y greater than x
}

private void doStuff()//flavor 2
{
assert (y>x) : "y=" + y + ", x=" + x;//adds more info to your AssertionError
//more code assuming y greater than x
}

>assertions stick around in the code even in deployment but being ignored by JVM; if something goes wrong in deployment, you can turn assertions on, and start debugging at client site; pretty slick, huh?

>Flavor 2: assert condition : value; //condition should be a boolean expression; value should resolve to a variable of primitive or object type so that toString() can be performed on it to print the description; other code statements are not allowed in place of 'value'

>'assert' was not a keyword before 1.4; from 1.4 onwards you can't use it as an identifier(variable name); so, you can use 'assert' as a keyword or identifier, but not both(only when u have mix of old and new codes)

>if you want to use assertions, you must enable it when you compile;

For Java 1.4 Compiler:

javac -source 1.4 TestClass.java

For Java 6 Compiler:

>this compiler will use assertions by default; that is, it validates assert statements also; if you have used assert keyword as identifier somewhere, by default java 6 compiler will complain; in that case you should mention to the compiler that you are really compiling a very old version of source code like 1.3

javac -source 1.3 OldCode.java

>compilers don't allow use of assert keyword as identifier in 1.4 or later source codes

javac -source 1.4 TestClass.java
javac -source 5 TestClass.java
javac -source 6 TestClass.java //all of these produce compilation error when assert keyword is used as identifier

Enabling Disabling Assertions at runtime
-----------------------------------------

Enabling: java -ea TestClass or java -enableassertions TestClass
Disabling: java TestClass or java -da TestClass or java -disableassertions TestClass

//assertions are disabled by default as shown above

Selective enabling/disabling
----------------------------
you can selectively enable assertions for certain classes/packages alone.

You just have to mention the class/package name along with -ea/-da switch. That's all.

java -ea:javax.swing TestClass (enables only for one package and its subpackages)
java -ea -da:java.util.ArrayList TestClass (enables for all, but disables for one class)

In the above example, assertions were enabled for javax.swing, and all other subpackages (like javax.swing.table, etc). But in general java doesn't treat any package as subpackage. For java javax.swing is a separate package from javax.swing.table; only here in the context of assertions we call them subpackages and have a way to address all of them together.

java -ea -dsa ===> enables assertions in general, but disables assertions in system classes

Assertion use guideline
-----------------------
>though AssertionError is catchable(subtype of Throwable), never catch it; it defeats the purpose
>to emphasize this, AssertionError doesn't provide access to the object that generated it(getCause() returns null)
>using assert to validate input argument to a public method is inappropriate(it's part of your application logic you dummy, and also assertion is disabled by default, and user of your code will end up getting wrong behavior; so never rely on assert for application logic)
>but still you can use assert to validate input arguments if the method is private; because you have control over the source code in this case and its usage as well
>don't use assertion to validate command line arguments
>for check point purposes you can use assert everywhere private or public
>don't let your assert expressions (as part of condition or display msg expression) modify any state of your system/program which application logic depends on; because assert can be disabled and sure is, in deployment

e.g. assert(getCondition()); //getCondition() returns boolean but also alters application state before returning - dangerous!

# It is not guaranteed that finally block would be called when try or catch block issues System.exit()

# Assertion is a useful debugging tool

# Exception in finally block can jeopardize it from executing completely

# declaration is must in enhanced for loop; it can't use previously declared loop variable

int i=0;
for(i : new int[]{1,2,3}{} //will not compile

# infinite for/while loop will not cause stack overflow error, they will merely run forever(program hangs in there)

# Program can continue after catching StackOverflowError (but may not work with OutOfMemoryError)

# Assignment to a variable is allowed in the 2nd expression of assert statement (assert true : list=new ArrayList(); //compiles)

Reference: Kathy Sierra's SCJP Book


No comments:

Post a Comment