Operator Precedence
postfixexpr++ expr--
unary++expr --expr +expr -expr ~ !
multiplicative* / %
additive+ -
shift<< >> >>>
relational< > <= >= instanceof
equality== !=
bitwise AND&
bitwise exclusive OR^
bitwise inclusive OR|
logical AND&&
logical OR||
ternary? :
assignment= += -= *= /= %= &= ^= |= <<= >>= >>>=

Switch Statement
The top expression for the switch statement:
  • Must be in parentheses
  • Can be a variable or expression that results in one of the following types:
        byte, char, short, int, enum
        Character, Byte, Short, Integer 
    Attempting to use anything else (boolean, long, float, double, other wrapper classes, etc.) will result in a compile time error.
  • If a wrapper class is used (Character, Byte, Short, or Integer only), the case statements must match in type to the unboxed expression - it won't be widened or narrowed to fit. For example, if you use a Byte as the switch statement variable, all the case statements must be byte, not char, short, int, or enum.
The remainder of the switch statement MUST be encased in curly braces, and contains the values to be compared against, plus the optional default statement to execute if the variable doesn't match any other expression. The default statement can appear anywhere in this block - it doesn't have to appear at the end.
The constant after each case statement:
  • May optionally be contained in parentheses.
  • Must be a variable or expression which results in a byte, char, short, int, or enum value
  • Must NOT be a wrapper class!

    The reason is this: Let's say you have a new Integer locX you wish to use in a case statement. With autounboxing, the compiler would convert the following case statement:

        case locX:
    into:
        case locX.intValue():
    which means it would not be a compile time constant. The implication is that a wrapper class can not be used ANYWHERE in Java, that requires a compile time constant.

  • Can only check a single value for equality - it can't check a range of values, or multiple values.
  • Must be unique in the switch statement - it is not legal to have two case statements with the same value.
  • Must be a compile time constant - the keyword final is not enough! The variable must be declared and initialized on the same line to be eligible as a case expression.
  • Must be within range of the variable being checked - thus, if the variable is a byte, the constant can't be 300, which is out of range for a byte.
The expression is checked against each case statement, and execution starts at the first matching case statement. Execution WILL FALL THROUGH all the other case statements until the end of the switch statement, or the first break is encountered.
Examine the following code for clarification:
package test;

public class Main {

    public static void main(String[] args) {
        // Won't work because it's not a compile time constant
        final byte a;           
        a = 1;
        final byte b = 2;       // OK
        final char c = 3;       // OK
        final short s = 4;      // OK
        final int i = 5;        // OK
        // Won't work because it won't promote to an int
        final long l = 6;   
        // Won't work because it is a wrapper class		
        final Integer f = 7;    

        byte var = 2;

        switch (var)
        {
            // Compile error - not a compile time constant
            //case a:                     
            //  System.out.println("1 "); 
            case b:
                System.out.print("2 ");
            case c:
                System.out.print("3 ");
            case s:
                System.out.print("4 ");
            case i:
                System.out.print("5 ");
            // Compile error - can't use long
            //case l:                     
            //  System.out.println("6 "); 
            // Compile error - can't use wrapper classes
            //case f:                     
            //  System.out.println("7 "); 
            case (i + 3):
                System.out.print("8 ");
            // Compile error - duplicate case label
            //case (s + 4):               
            //  System.out.print("8 ");
            case i + 4:
                System.out.print("9 ");
            case (10):
                System.out.print("10 ");
            // default can appear anywhere
            default:                      
                System.out.print("default "); 
                break;
            case 11:
                System.out.print("11 ");
            // Compile error - out of range for var.
            //case 256:                   
            //  System.out.print("256 "); 
        }
    }
}
The output of the above program is 2 3 4 5 8 9 10 default.
This is because none of the case statements have a "break" at the end, except for the default: statement! This is probably NOT what you expected, or want, in most cases.

The moral of this story is, put a 'break' at the end of each case statement code block, or a "//fall through to next statement" comment to remind yourself that the fallthrough was intentional.

Here's the same code, but with an Integer instead of a byte used in the switch statement. Some lines no longer compiled when this change was made, and were commented out. This is because byte, char, and short variables can't autobox to Integer.
package test;

public class Main {

    public static void main(String[] args) {
        // Won't work because it's not a compile time constant
        final byte a;           
        a = 1;
        final byte b = 2;       // OK
        final char c = 3;       // OK
        final short s = 4;      // OK
        final int i = 5;        // OK
        // Won't work because it won't promote to an int
        final long l = 6;  
        // Won't work because it is a wrapper class		
        final Integer f = 7;    

        Integer var = 2;

        switch (var)
        {
            // Compile error - not a compile time constant
            //case a:                     
            //  System.out.println("1 "); 
            // Compile error - byte can't autobox to Integer 
            //case b:                     
            //  System.out.print("2 "); 
            // Compile error - char can't autobox to Integer
            //case c:                     
            //  System.out.print("3 ");   
            // Compile error - short can't autobox to Integer
            //case s:                     
            //  System.out.print("4 ");
            case i:
                System.out.print("5 ");
            // Compile error - can't use long
            //case l:                     
            //  System.out.println("6 "); 
            // Compile error - can't use wrapper classes 
            //case f:                     
            //  System.out.println("7 "); 
            case (i + 3):
                System.out.print("8 ");
            // Compile error - duplicate case label
            //case (s + 4):               
            //  System.out.print("8 ");
            case i + 4:
                System.out.print("9 ");
            case (10):
                System.out.print("10 ");
            // default can appear anywhere
            default:                      
                System.out.print("default "); 
                break;
            case 11:
                System.out.print("11 ");
            // No longer out of range, so it compiles   
            case 256:                                    
              System.out.print("256 "); 
        }
    }
}
The output of the above program is default.
When you're using a wrapper class as the switch statement expression, all the case statement expressions must be able to autobox to the same type as the switch statement. Since byte, char, and short variables can't autobox to an Integer, they cause compile time errors.
The moral of this story is, if you can avoid it, don't use wrapper classes as the expression in a switch statement - it limits the available case statements for no good reason.

Looping
Following is an example of a while loop:
while (expression) {
	// body of loop
}
where expression is an expression that results in a boolean value. You can't declare a variable in the expression.
Following is an example of a do loop:
do {
	// body of loop
} while (expression);
where expression is an expression that results in a boolean value. You can't declare a variable in the expression.
Following is an example of a for loop:
for (init statement; check condition; iterator statement) {
	// body of loop
}
where:
  • init statement can be empty, initialize pre-existing variable(s), or declare and initialize new variable(s).
    You can't combine declaring and initializing a variable, with initializing a second variable, because if you declare any variable, it attempts to declare all the variables. Thus, all variables in the init statement must be pre-existing, or newly declared - you can't mix and match.
  • check condition can be empty, or can be a single boolean expression.
    You can't have multiple boolean expressions here - it must be a single boolean expression that results in true or false. However, your boolean expression can be complex.
  • iterator statement can be empty, or execute one or more statements.
At the end of the for loop body, the iterator statement(s) are executed, and the check condition is checked. If the check condition is true, execution starts against at the top of the loop.
Following is an example of an enhanced for loop:
for (declared variable : array or collection expression) {
	// body of loop
}
where:
  • declared variable is a newly declared variable that matches the type of the elements in the array.
  • array expression is an expression that results in a array or collection of elements.
You MUST use a newly declared variable in the 'declared variable' portion - you can't use a previously declared variable.

Example:

        int[] arr = {1, 2, 3};
        for (int i : arr)
            System.out.println(i);
Following is an example illustrating these concepts:
public class TestLoops {
    public static void main(String[] args) {

        GoodWhile();
        BadWhile();

        GoodDo();
        BadDo();

        GoodFor();
        BadFor();

        GoodEnhancedFor();
        BadEnhancedFor();
        
        System.out.println("\nDone!\n");
    }
    public static void GoodWhile() {
        boolean done = false;
        while (!done) {
            System.out.println("While loop not done.");
            done = true;
        }
        System.out.println("While loop done.");
    }

    public static void BadWhile() {
        /*
         * A while loop requires a boolean expression,
         * and doesn't declare variables.
         * The following won't even compile.
         *
         while (boolean done = false) { // Compile error - can't declare variable
             System.out.println("Not done.");
         }
         */
    }

    public static void GoodDo() {
        boolean done = false;
        do {
            System.out.println("Do loop not done.");
            done = true;
        } while (!done);
        System.out.println("Do loop done.");
    }
    
    public static void BadDo() {
        /*
         * A do loop requires a boolean expression,
         * and doesn't declare variables.
         * The following won't even compile.
         *
        int done = 0;
        do {
            System.out.println("Do loop not done.");
            done = 1;
        } while (!done);        // Compile error - done is an int, not a boolean
         */

    }

    public static void GoodFor() {
        // Declare and initialize variable i.
        for (int i = 0; i < 3; i++)
            System.out.print(i + " ");

        // j declared before the loop,
        // initialized and incremented within the loop.
        int j;
        for (j = 3; j < 6; j++) {
            System.out.print(j + " ");
        }

        // k declared and initialized before the loop,
        // incremented within the loop.
        int k = 6;
        for (; k < 9; k++) {
            System.out.print(k + " ");
        }

        // l declared and initialized before the loop,
        // incremented inside the body of loop.
        int l = 9;
        for (; l < 12; ) {
            System.out.print(l + " ");
            l++;
        }

        // m declared and initialized before the loop,
        // incremented and checked inside the body of the loop.
        int m = 12;
        for (; ;) {
            if (m > 14)
                break;
            System.out.print(m + " ");
            m++;
        }

        // n AND o declared and incremented in the loop
        // p declared before the loop, incremented in the loop
        System.out.println("");
        int p = 30;
        for (int n = 10, o = 20; n < 13; n++, o++, p++) {
            System.out.printf("n = %d, o = %d, p = %d\n", n, o, p);
        }

    }

    public static void BadFor() {
        // The statements in this method won't compile.

        //Can't both declare and initialize, and
        //initialize only a second variable.
        //If there's a declaration, it applies to all
        //variables declared in loop.
        // int k = 0;
        // for (int i = 0, k = 2; i++; i++, k++) { }

        //Can't have two iterator statements
        // for (int i = 0, k = 2; i < 10, k < 20; i++, k++) { }
    }

    public static void GoodEnhancedFor() {
        int[] nums = {1, 2, 3};

        // Types must be compatible
        for (int x : nums) {
            System.out.print(x);
        }

        // Can use widening
        for (long x : nums) {
            System.out.print(x);
        }

        // Can use autoboxing
        for (Integer x : nums) {
            System.out.print(x);
        }
    }

    public static void BadEnhancedFor() {
        Integer[] nums = {1, 2, 3};
        int x;

        // Code in this routine won't compile!

        // Types must be compatible
        //for (byte x : nums) {
        //    System.out.println(x);
        //}

        // Must declare inside the for expression
        //for (x : nums) {
        //    System.out.println(x);
        //}

    }
}
The output of the above program is:
While loop not done.
While loop done.
Do loop not done.
Do loop done.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
n = 10, o = 20, p = 30
n = 11, o = 21, p = 31
n = 12, o = 22, p = 32
123123123
Done!

Continue and Break
ContinueSkips execution of the rest of the loop, and starts again at the top of the loop.
BreakSkips execution of the rest of the loop, and starts at the first statement past the loop.
Labeled ContinueIndicates which loop you want to 'continue' from. It starts executing at the top of the loop specified in the label statement, skipping execution of the current loop and all loops nested inside the labeled loop statement.
Labeled BreakIndicates which loop you want to 'break' from. It starts executing at the point in code past the loop specified in the label statement, skipping execution of the current loop and all loops nested inside the labeled loop statement.
The following example may make things clearer:
public class testBreak {
    public static void main(String[] args) {
        testBreak();
        testContinue();
        testLabeledBreak();
        testLabeledContinue();
    }

    public static void testBreak() {
        // Prints 0 1 2
        for (int i = 0; i < 100; i++ ) {
            if (i > 2)
                break;
            System.out.print(i + " ");
        }
        System.out.println("");
    }

    public static void testContinue() {
        // Prints 3 4 5
        for (int i = 0; i < 6; i++) {
            if (i < 3)
                continue;
            System.out.print(i + " ");
        }
        System.out.println("");
    }

    public static void testLabeledBreak() {
        // Prints
        // 0 0, 0 1, 0 2,
        // 1 0, 1 1, 1 2,
        // 2 0, 2 1, 2 2,
        toplevel:
        for (int i = 0; i < 100; i++) {
            for (int j = 0; j < 3; j++) {
                if (i > 2)
                    break toplevel;
                System.out.printf("%d %d, ", i, j);
            }
            System.out.println("");
        }
    }

    public static void testLabeledContinue() {
        // Prints
        // 97 a, 97 b, 97 c,
        // 98 a, 98 b, 98 c, 
        // 99 a, 99 b, 99 c,
        String[] args = {"a", "b", "c"};
        toplevel:
        for (int i = 0; i < 100; i++) {
            for (int j = 0; j < args.length; j++) {
                if (i < 97)
                    continue toplevel;
                System.out.printf("%d %s, ", i, args[j]);
            }
            System.out.println("");
        }
    }
}

Exceptions
Exceptions are thrown on many error conditions. These exceptions can be caught with the try-catch-finally clause, as follows:
try { // some code to execute
} catch (Exception  e) { // Steps to take upon error
} catch (Exception2 e) { // (0 or more catch clauses may exist)
} finally {              // Steps to take at the end. 
}
Exceptions are objects in their own right. All exceptions:
  • Inherit from Throwable, which inherits from Object.
  • Error inherit from Throwable, and can't be caught by the programmer. The Error exception hierarchy is reserved for system use, for things such as out of memory conditions. This class hierarchy illustrates that Error is not an Exception.
  • Exception inherits from Throwable, and is the root of "checked exceptions" - ie, Exceptions that must be handled or declared by the programmer.
    • RuntimeException inherits from Exception, and is the root of "unchecked exceptions" - ie, exceptions that don't have to be explicitly handled in code. Thus, "catch (Exception ex)" can catch exceptions of type RuntimeException, as well.
The try statement:
  • Comes before the block of code you wish to perform error checking on.
The catch statement:
  • May be omitted, but if so, there must be a 'finally' clause.
  • There may be more than one catch statement.
  • An exception may be caught by a superclass of the exception class; ie, if MyException inherits from RuntimeException, a
        catch (RuntimeException ex)
    could handle an exception of type MyException.
  • The exceptions are checked in the order they appear in the code, and the first one the exception matches, wins.
  • For exceptions where one is a subtype of the other, you must place the catch statements in order from most specific (most subclassed), to least specific. For instance, placing a statement that catches the more general Exception, before the statement catching the more specific RuntimeException, will cause a compile-time error "Exception XXX has already been caught."
  • If no catch statement handles the current exception, the finally clause executes (if specified), then the exception is passed up the hierarchy for the caller to handle.
The finally statement:
  • May be omitted, but if so, there must be a 'catch' clause.
  • Executes before control leaves the try block. That means, if this block specified a try/finally clause without a catch clause, the try will run, then the finally run, then the exception will be passed up the hierarchy to be handled by the caller.
  • ALWAYS EXECUTES! Whether the exception is thrown, or not; and whether the exception is caught, or not.
  • The only exception to FINALLY ALWAYS EXECUTES is if system.exit() is called before that, in the try or catch clause. In that case, finally will not execute, and the JVM will shut down.
Common Exceptions Thrown by the Java Virtual Machine (JVM)
  • ArrayIndexOutOfBoundsException
  • ClassCastException
  • NullPointerException
  • ExceptionInitializerError
  • StackOverflowError
  • NoClassDefFoundError
Common Exceptions Thrown Programmatically
  • IllegalArgumentException
  • IllegalStateException
  • NumberFormatException
  • AssertionError
Exception Handling Examples
Following are some examples of throwing and catching exceptions:
public class testExceptions {
    public static void main(String[] args) {
        testTryCatch();
        testTryFinally();
        testUnhandledException();
    }

    public static void testTryCatch() {
        // Prints "an error!"
        try {
            throw new RuntimeException("an error!");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    public static void testTryFinally() {
        /* Prints:

        try
            test2 try
            test2 finally
        catch
        *** an error! ***
        finally runs whether or not an error occurs,
          and whether or not the error is caught.

         */
        try {
            System.out.println("try");
            test2();
        } catch (Exception e) {
            System.out.println("catch");
            System.out.println(e.getMessage());
        } finally {
            System.out.println("finally runs whether or not an error occurs, ");
            System.out.println("  and whether or not the error is caught.");
        }

        // Prints:
        // There will be no error here.
        // The finally runs anyway.
        try {
            System.out.println("There will be no error here.");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {
            System.out.println("Finally runs whether or not an error occurs.");
        }
    }

    private static void test2() {
        /*
        Normally, try, then catch, then finally, execute, in that order.
        However, in this routine, since there is no catch,
        the try runs first, then the finally,
        THEN the exception is passed up to the higher level routine,
        where it is caught, and the error message is displayed.
        The finally clause executes before control leaves this try block.
        */
        try {
            System.out.println("    test2 try");
            throw new RuntimeException("*** an error! ***");
        } finally {
            System.out.println("    test2 finally");
        }
    }

    public static void testUnhandledException() {
        /*
        This routine prints:

            testDefault
            end of testDefault

        then passes the RuntimeException to
        main() for handling, which causes a
        stack trace to be printed.

         */
        try {
            System.out.println("testDefault");
            throw new MyException();
        } catch (MyException2 e) {
            // This exception doesn't catch the MyException thrownabove.
            System.out.println(e.getMessage());
        } finally {
            System.out.println("end of testDefault.");
        }
    }
}

class MyException extends RuntimeException {}
class MyException2 extends RuntimeException{}

Assertions
Assertions were added to the language in version 1.4. Assertions let you validate a boolean condition before continuing. They can be specified in two ways:
    assert expr1;
    assert (expr1);
or
    assert expr1  : expr2;
    assert(expr1) : expr2;
where:
  • expr1 is a boolean expression, optionally enclosed in parentheses.
    If the value is true, nothing happens.
    If the value is false, an AssertionError is thrown at runtime.
  • expr2 is any expression, whose string value is printed along with the AssertionError if expr1 is false.
    expr2 can be a string, a primitive, a numeric expression, a boolean, a method call, a class, literally ANYTHING which results in a value.
    However, if you call a method from expr2, it can't return void! This will cause the compile time error "'void' type not allowed here."
Assertions are off by default. You must enable assertions to see any difference in your code, using the command-line switch -ea or -enableassertions. You can disable assertions by running with the command-line switch -da, or -disableassertions. Since assertions are off by default, this seems useless, except for the following: You can selectively turn on or off assertions for all non-system classes, all classes in a package plus subpackages, or for a single class, using command line parameters. Therefore, issuing the following command enables assertions for all classes, except for those in package xyz and its subpackages:
    java -ea -da:com.business.xyz
This command enables assertions for all classes in package xyz plus its subpackages, except for those in class A:
    java -ea:com.business.xyz -da:com.business.xyz.A
Assertions are typically turned on when debugging, and off when deployed to production. However, though turned off, they still exist in the code, so if need be, they can be turned on to assist in troubleshooting production problems.
Assertion Rules
Don't catch AssertionErrors! Assertions technically inherit from Throwable, and can be caught like any other Exception at runtime, but you're not supposed to do this! To discourage this, the AssertionError doesn't provide access to the object that generated it.
Don't use assertions to validate arguments to a public method, or command-line arguments! Though technically you could do this without compile time errors, your validations would be disabled in production code, which is probably not what you want. If you need to check arguments in a public method, do so in normal code, and throw an IllegalArgumentException if something doesn't validate.
You may use assertions to validate arguments to a private method.
You may use assertions to check for cases that you know should never happen, even in public methods; ie, default cases on switch statements.
Don't use assert expressions that can cause side effects! Though you technically can have expr2 call a method that changes state in the program, and compile your program without error, you shouldn't do that. Otherwise, when assertions are disabled, your code will mysteriously break and you'll have a hard time tracking down why.