| Operator Precedence | |
|---|---|
| postfix | expr++ 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:
|
| 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:
|
| 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:
|
Following is an example of an enhanced for loop:
for (declared variable : array or collection expression) {
// body of loop
}
where:
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 | |
|---|---|
| Continue | Skips execution of the rest of the loop, and starts again at the top of the loop. |
| Break | Skips execution of the rest of the loop, and starts at the first statement past the loop. |
| Labeled Continue | Indicates 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 Break | Indicates 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:
| |
| Common Exceptions Thrown by the Java Virtual Machine (JVM) |
|---|
|
| Common Exceptions Thrown Programmatically |
|---|
|
| 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:
| |
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.xyzThis 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. | |