You are on page 1of 17

Table of Contents

1. Binary Literals
In Java SE 7, the integral types (byte, short, int, and long) can also be expressed using the binary number system. To specify a binary literal, add the prefix 0b or 0B to the number. The following examples show binary literals:
// An 8-bit 'byte' value: byte aByte = (byte)0b00100001; // A 16-bit 'short' value: short aShort = (short)0b1010000101000101; // Some 32-bit 'int' values: int anInt1 = 0b10100001010001011010000101000101; int anInt2 = 0b101; int anInt3 = 0B101; // The B can be upper or lower case. // A 64-bit 'long' value. Note the "L" suffix: long aLong = 0b1010000101000101101000010100010110100001010001011010000101000101L;

Binary literals can make relationships among data more apparent than they would be in hexadecimal or octal. For example, each successive number in the following array is rotated by one bit:
public static final int[] phases = { 0b00110001, 0b01100010, 0b11000100, 0b10001001, 0b00010011, 0b00100110, 0b01001100, 0b10011000 }

In hexadecimal, the relationship among the numbers is not readily apparent:


public static final int[] phases = { 0x31, 0x62, 0xC4, 0x89, 0x13, 0x26, 0x4C, 0x98 }

You can use binary integral constants in code that you can verify against a specifications document, such as a simulator for a hypothetical 8-bit microprocessor:
public State decodeInstruction(int instruction, State state) { if ((instruction & 0b11100000) == 0b00000000) { final int register = instruction & 0b00001111; switch (instruction & 0b11110000) { case 0b00000000: return state.nop(); case 0b00010000: return state.copyAccumTo(register); case 0b00100000: return state.addToAccum(register); case 0b00110000: return state.subFromAccum(register);

} } else { final int address = instruction & 0b00011111; switch (instruction & 0b11100000) { case 0b00100000: return state.jumpTo(address); case 0b01000000: return state.jumpIfAccumZeroTo(address); case 0b01000000: return state.jumpIfAccumNonzeroTo(address); case 0b01100000: return state.setAccumFromMemory(address); case 0b10100000: return state.writeAccumToMemory(address); case 0b11000000: return state.callTo(address); default: throw new IllegalArgumentException(); } } }

case 0b01000000: return state.multiplyAccumBy(register); case 0b01010000: return state.divideAccumBy(register); case 0b01100000: return state.setAccumFrom(register); case 0b01110000: return state.returnFromCall(); default: throw new IllegalArgumentException();

You can use binary literals to make a bitmap more readable:


public static final short[] HAPPY_FACE = { (short)0b0000011111100000; (short)0b0000100000010000; (short)0b0001000000001000; (short)0b0010000000000100; (short)0b0100000000000010; (short)0b1000011001100001; (short)0b1000011001100001; (short)0b1000000000000001; (short)0b1000000000000001; (short)0b1001000000001001; (short)0b1000100000010001; (short)0b0100011111100010; (short)0b0010000000000100; (short)0b0001000000001000; (short)0b0000100000010000; (short)0b0000011111100000; }

2. Underscores in Numeric Literals


In Java SE 7 and later, any number of underscore characters (_) can appear anywhere between digits in a numerical literal. This feature enables you, for example, to separate groups of digits in numeric literals, which can improve the readability of your code. For instance, if your code contains numbers with many digits, you can use an underscore character to separate digits in groups of three, similar to how you would use a punctuation mark like a comma, or a space, as a separator.

The following example shows other ways you can use the underscore in numeric literals:
long creditCardNumber = 1234_5678_9012_3456L; long socialSecurityNumber = 999_99_9999L; float pi = 3.14_15F; long hexBytes = 0xFF_EC_DE_5E; long hexWords = 0xCAFE_BABE; long maxLong = 0x7fff_ffff_ffff_ffffL; byte nybbles = 0b0010_0101; long bytes = 0b11010010_01101001_10010100_10010010;

You can place underscores only between digits; you cannot place underscores in the following places:

At the beginning or end of a number Adjacent to a decimal point in a floating point literal Prior to an F or L suffix In positions where a string of digits is expected

The following examples demonstrate valid and invalid underscore placements (which are highlighted) in numeric literals:
float pi1 = 3_.1415F; // Invalid; cannot put underscores adjacent to a decimal point float pi2 = 3._1415F; // Invalid; cannot put underscores adjacent to a decimal point long socialSecurityNumber1 = 999_99_9999_L; // Invalid; cannot put underscores prior to an L suffix int x1 = int x2 = int x3 = literal int x4 = _52; 5_2; 52_; 5_______2; // This is an identifier, not a numeric literal // OK (decimal literal) // Invalid; cannot put underscores at the end of a // OK (decimal literal) // Invalid; cannot put underscores in the 0x radix // Invalid; cannot put underscores at the // OK (hexadecimal literal) // Invalid; cannot put underscores at the end of a // OK (octal literal) // OK (octal literal) // Invalid; cannot put underscores at the end of a

int x5 = 0_x52; prefix int x6 = 0x_52; beginning of a number int x7 = 0x5_2; int x8 = 0x52_; number int x9 = 0_52; int x10 = 05_2; int x11 = 052_; number

3. Strings in switch Statements

In the JDK 7 release, you can use a String object in the expression of a switch statement:
public String getTypeOfDayWithSwitchStatement(String dayOfWeekArg) { String typeOfDay; switch (dayOfWeekArg) { case "Monday": typeOfDay = "Start of work week"; break; case "Tuesday": case "Wednesday": case "Thursday": typeOfDay = "Midweek"; break; case "Friday": typeOfDay = "End of work week"; break; case "Saturday": case "Sunday": typeOfDay = "Weekend"; break; default: throw new IllegalArgumentException("Invalid day of the week: " + dayOfWeekArg); } return typeOfDay; }

The switch statement compares the String object in its expression with the expressions associated with each case label as if it were using the String.equals method; consequently, the comparison of String objects in switch statements is case sensitive. The Java compiler generates generally more efficient bytecode from switch statements that use String objects than from chained if-then-else statements.

4. Type Inference for Generic Instance Creation


You can replace the type arguments required to invoke the constructor of a generic class with an empty set of type parameters (<>) as long as the compiler can infer the type arguments from the context. This pair of angle brackets is informally called the diamond. For example, consider the following variable declaration:
Map<String, List<String>> myMap = new HashMap<String, List<String>>();

In Java SE 7, you can substitute the parameterized type of the constructor with an empty set of type parameters (<>):
Map<String, List<String>> myMap = new HashMap<>();

Note that to take advantage of automatic type inference during generic class instantiation, you must specify the diamond. In the following example, the compiler generates an unchecked conversion warning because the HashMap() constructor refers to the HashMap raw type, not the Map<String, List<String>> type:

Map<String, List<String>> myMap = new HashMap(); // unchecked conversion warning

Java SE 7 supports limited type inference for generic instance creation; you can only use type inference if the parameterized type of the constructor is obvious from the context. For example, the following example does not compile:
List<String> list = new ArrayList<>(); list.add("A"); // The following statement should fail since addAll expects // Collection<? extends String> list.addAll(new ArrayList<>());

Note that the diamond often works in method calls; however, it is suggested that you use the diamond primarily for variable declarations. In comparison, the following example compiles:
// The following statements compile: List<? extends String> list2 = new ArrayList<>(); list.addAll(list2);

Type Inference and Generic Constructors of Generic and Non-Generic Classes Note that constructors can be generic (in other words, declare their own formal type parameters) in both generic and non-generic classes. Consider the following example:
class MyClass<X> { <T> MyClass(T t) { // ... } }

Consider the following instantiation of the class MyClass, which is valid in Java SE 7 and prior releases:
new MyClass<Integer>("")

This statement creates an instance of the parameterized type MyClass<Integer>; the statement explicitly specifies the type Integer for the formal type parameter, X, of the generic class MyClass<X>. Note that the constructor for this generic class contains a formal type parameter, T. The compiler infers the type String for the formal type parameter, T, of the constructor of this generic class (because the actual parameter of this constructor is a String object). Compilers from releases prior to Java SE 7 are able to infer the actual type parameters of generic constructors, similar to generic methods. However, the compiler in Java SE 7 can infer the actual type parameters of the generic class being instantiated if you use the diamond (<>). Consider the following examples, which are valid for Java SE 7 and later:
MyClass<Integer> myObject = new MyClass<>("");

In this example, the compiler infers the type Integer for the formal type parameter, X, of the generic class MyClass<X>. It infers the type String for the formal type parameter, T, of the constructor of this generic class.
MyClass<Integer> myObject = new <String> MyClass<>("");

In this example, the compiler infers the type Integer for the formal type parameter, X, of the generic class MyClass<X>. The statement explicitly specifies the type String for the formal type parameter, T, of the constructor of this generic class.

5. Improved Compiler Warnings and Errors When Using Non-Reifiable Formal Parameters with Varargs Methods
1. 2. 3. 4. 5. 6. This page covers the following topics:

Heap Pollution Variable Arguments Methods and Non-Reifiable Formal Parameters Potential Vulnerabilities of Varargs Methods with Non-Reifiable Formal Parameters Suppressing Warnings from Varargs Methods with Non-Reifiable Formal Parameters

Heap Pollution Most parameterized types, such as ArrayList<Number> and List<String>, are non-reifiable types. A non-reifiable type is a type that is not completely available at runtime. At compile time, non-reifiable types undergo a process called type erasure during which the compiler removes information related to type parameters and type arguments. This ensures binary compatibility with Java libraries and applications that were created before generics. Because type erasure removes information from parameterized types at compile-time, these types are non-reifiable. Heap pollution occurs when a variable of a parameterized type refers to an object that is not of that parameterized type. This situation can only occur if the program performed some operation that would give rise to an unchecked warning at compile-time. An unchecked warning is generated if, either at compile-time (within the limits of the compile-time type checking rules) or at runtime, the correctness of an operation involving a parameterized type (for example, a cast or method call) cannot be verified. Consider the following example:
List l = new ArrayList<Number>(); List<String> ls = l; // unchecked warning l.add(0, new Integer(42)); // another unchecked warning String s = ls.get(0); // ClassCastException is thrown

During type erasure, the types ArrayList<Number> and List<String> become ArrayList and List, respectively.

The variable ls has the parameterized type List<String>. When the List referenced by l is assigned to ls, the compiler generates an unchecked warning; the compiler is unable to determine at compile time, and moreover knows that the JVM will not be able to determine at runtime, if l refers to a List<String> type; it does not. Consequently, heap pollution occurs. As a result, at compile time, the compiler generates another unchecked warning at the add statement. The compiler is unable to determine if the variable l refers to a List<String> type or a List<Integer> type (and another heap pollution situation occurs). However, the compiler does not generate a warning or error at the get statement. This statement is valid; it is calling the List<String>.get method to retrieve a String object. Instead, at runtime, the get statement throws a ClassCastException. In detail, a heap pollution situation occurs when the List object l, whose static type is List<Number>, is assigned to another List object, ls, that has a different static type, List<String>. However, the compiler still allows this assignment. It must allow this assignment to preserve backwards compatibility with versions of Java SE that do not support generics. Because of type erasure, List<Number> and List<String> both become List. Consequently, the compiler allows the assignment of the object l, which has a raw type of List, to the object ls. Furthermore, a heap pollution situation occurs when the l.add method is called. The static type second formal parameter of the add method is String, but this method is called with an actual parameter of a different type, Integer. However, the compiler still allows this method call. Because of type erasure, the type of the second formal parameter of the add method (which is defined as List<E>.add(int,E)) becomes Object. Consequently, the compiler allows this method call because, after type erasure, the l.add method can add any object of type Object, including an object of Integer type, which is a subtype of Object. Variable Arguments Methods and Non-Reifiable Formal Parameters Consider the method ArrayBuilder.addToList in the following example. It is a variable arguments (also known as varargs) method that adds the objects of type T contained in the elements varargs formal parameter to the List listArg:
import java.util.*; public class ArrayBuilder { public static <T> void addToList (List<T> listArg, T... elements) { for (T x : elements) { listArg.add(x); } } public static void faultyMethod(List<String>... l) { Object[] objectArray = l; // Valid objectArray[0] = Arrays.asList(new Integer(42)); String s = l[0].get(0); // ClassCastException thrown here } }

import java.util.*; public class HeapPollutionExample { public static void main(String[] args) { List<String> stringListA = new ArrayList<String>(); List<String> stringListB = new ArrayList<String>(); ArrayBuilder.addToList(stringListA, "Seven", "Eight", "Nine"); ArrayBuilder.addToList(stringListA, "Ten", "Eleven", "Twelve"); List<List<String>> listOfStringLists = new ArrayList<List<String>>(); ArrayBuilder.addToList(listOfStringLists, stringListA, stringListB); ArrayBuilder.faultyMethod(Arrays.asList("Hello!"), Arrays.asList("World!")); } }

The Java SE 7 compiler generates the following warning for the definition of the method ArrayBuilder.addToList:
warning: [varargs] Possible heap pollution from parameterized vararg type T

When the compiler encounters a varargs method, it translates the varargs formal parameter into an array. However, the Java programming language does not permit the creation of arrays of parameterized types. In the method ArrayBuilder.addToList, the compiler translates the varargs formal parameter T... elements to the formal parameter T[] elements, an array. However, because of type erasure, the compiler converts the varargs formal parameter to Object[] elements. Consequently, there is a possibility of heap pollution. See the next section, Potential Vulnerabilities of Varargs Methods with Non-Reifiable Formal Parameters, for more information. Note: The Java SE 5 and 6 compilers generate this warning when the ArrayBuilder.addToList is called; in this example, the warning is generated for the class HeapPollutionExample. These compilers do not generate the warning at the declaration site. However, the Java SE 7 generates the warning at both the declaration site and the call site (unless the warnings are preempted with annotations; see Suppressing Warnings from Varargs Methods with Non-Reifiable Formal Parameters for more information). The advantage of generating a warning when a compiler encounters a varargs method that has a non-reifiable varargs formal parameter at the declaration site as opposed to the call site is that there is only one declaration site; there are potentially many call sites. Potential Vulnerabilities of Varargs Methods with Non-Reifiable Formal Parameters The method ArrayBuilder.faultyMethod shows why the compiler warns you about these kinds of methods. The first statement of this method assigns the varargs formal parameter l to the Object array objectArgs:
Object[] objectArray = l;

This statement can potentially introduce heap pollution. A value that does match the parameterized type of the varargs formal parameter l can be assigned to the variable objectArray, and thus can be assigned to l. However, the compiler does not generate an unchecked warning at this statement.

The compiler has already generated a warning when it translated the varargs formal parameter List<String>... l to the formal parameter List[] l. This statement is valid; the variable l has the type List[], which is a subtype of Object[]. Consequently, the compiler does not issue a warning or error if you assign a List object of any type to any array component of the objectArray array as shown by this statement:
objectArray[0] = Arrays.asList(new Integer(42));

This statement assigns to the first array component of the objectArray array with a List object that contains one object of type Integer. Suppose you call the ArrayBuilder.makeArray method with the following statement:
ArrayBuilder.faultyMethod(Arrays.asList("Hello!"), Arrays.asList("World!"));

At runtime, the JVM throws a ClassCastException at the following statement:


String s = l[0].get(0); // ClassCastException thrown here

The object stored in the first array component of the variable l has the type List<Integer>, but this statement is expecting an object of type List<String>. Suppressing Warnings from Varargs Methods with Non-Reifiable Formal Parameters If you declare a varargs method that has parameterized parameters, and you ensure that the body of the method does not throw a ClassCastException or other similar exception due to improper handling of the varargs formal parameter (as shown in the ArrayBuilder.faultyMethod method), you can suppress the warning that the compiler generates for these kinds of varargs methods by using one of the following options:

Add the following annotation to static and non-constructor method declarations:

@SafeVarargs

Unlike the @SafeVarargs annotation, the @SafeVarargs annotation is a documented part of the method's contract; this annotation asserts that the implementation of the method will not improperly handle the varargs formal parameter.

Add the following annotation to the method declaration:

@SuppressWarnings({"unchecked", "varargs"})

Unlike the @SafeVarargs annotation, the @SuppressWarnings("varargs") does not suppress warnings generated from the method's call site.

Use the compiler option -Xlint:varargs.

For example, the following version of the ArrayBuilder class has two additional methods, addToList2 and addToList3:
public class ArrayBuilder {

public static <T> void addToList (List<T> listArg, T... elements) { for (T x : elements) { listArg.add(x); } } @SuppressWarnings({"unchecked", "varargs"}) public static <T> void addToList2 (List<T> listArg, T... elements) { for (T x : elements) { listArg.add(x); } } @SafeVarargs public static <T> void addToList3 (List<T> listArg, T... elements) { for (T x : elements) { listArg.add(x); } } // ... } public class HeapPollutionExample { // ... public static void main(String[] args) { // ... ArrayBuilder.addToList(listOfStringLists, stringListA, stringListB); ArrayBuilder.addToList2(listOfStringLists, stringListA, stringListB); ArrayBuilder.addToList3(listOfStringLists, stringListA, stringListB); // ... } }

The Java compiler generates the following warnings for this example:
addToList: o At the method's declaration: [unchecked] Possible heap pollution from parameterized vararg type T o When the method is called: [unchecked] unchecked generic array creation for varargs parameter of type List<String>[] addToList2: When the method is called (no warning is generated at the method's declaration): [unchecked] unchecked generic array creation for varargs parameter of type List<String>[] addToList3: No warnings are generated either at the method's declaration or when it is

called.

Note: In Java SE 5 and 6, it is the responsibility of the programmer who calls a varargs method that has a non-reifiable varargs formal parameter to determine whether heap pollution would occur. However, if this programmer did not write such a method, he or she cannot easily determine this. In Java SE 7, it is the responsibility of the programmer who writes these kinds of varargs methods to ensure that they properly handle the varargs formal parameter and ensure heap pollution does not occur.

The try-with-resources Statement


The try-with-resources statement is a try statement that declares one or more resources. A resource is as an object that must be closed after the program is finished with it. The try-withresources statement ensures that each resource is closed at the end of the statement. Any object that implements java.lang.AutoCloseable, which includes all objects which implement java.io.Closeable, can be used as a resource. The following example reads the first line from a file. It uses an instance of BufferedReader to read data from the file. BufferedReader is a resource that must be closed after the program is finished with it:
static String readFirstLineFromFile(String path) throws IOException { try (BufferedReader br = new BufferedReader(new FileReader(path))) { return br.readLine(); } }

In this example, the resource declared in the try-with-resources statement is a BufferedReader. The declaration statement appears within parentheses immediately after the try keyword. The class BufferedReader, in Java SE 7 and later, implements the interface java.lang.AutoCloseable. Because the BufferedReader instance is declared in a try-withresource statement, it will be closed regardless of whether the try statement completes normally or abruptly (as a result of the method BufferedReader.readLine throwing an IOException). Prior to Java SE 7, you can use a finally block to ensure that a resource is closed regardless of whether the whether the try statement completes normally or abruptly. The following example uses a finally block instead of a try-with-resources statement:
static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException { BufferedReader br = new BufferedReader(new FileReader(path)); try { return br.readLine(); } finally { if (br != null) br.close(); } }

However, in this example, if the methods readLine and close both throw exceptions, then the method readFirstLineFromFileWithFinallyBlock throws the exception thrown from the finally block; the exception thrown from the try block is suppressed. In contrast, in the example

readFirstLineFromFile, if exceptions are thrown from both the try block and the try-withresources statement, then the method readFirstLineFromFile throws the exception thrown from the try block; the exception thrown from the try-with-resources block is suppressed. In Java SE 7

and later, you can retrieve suppressed exceptions; see the section Suppressed Exceptions for more information. You may declare one or more resources in a try-with-resources statement. The following example retrieves the names of the files packaged in the zip file zipFileName and creates a text file that contains the names of these files:
public static void writeToFileZipFileContents(String zipFileName, String outputFileName) throws java.io.IOException { java.nio.charset.Charset charset = java.nio.charset.Charset.forName("USASCII"); java.nio.file.Path outputFilePath = java.nio.file.Paths.get(outputFileName); // Open zip file and create output file with try-with-resources statement try ( java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName); java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset) ) { // Enumerate each entry for (java.util.Enumeration entries = zf.entries(); entries.hasMoreElements();) { // Get the entry name and write it to the output file String newLine = System.getProperty("line.separator"); String zipEntryName = ((java.util.zip.ZipEntry)entries.nextElement()).getName() + newLine; writer.write(zipEntryName, 0, zipEntryName.length()); } } }

In this example, the try-with-resources statement contains two declarations that are separated by a semicolon: ZipFile and BufferedWriter. When the block of code that directly follows it terminates, either normally or because of an exception, the close methods of the BufferedWriter and ZipFile objects are automatically called in this order. Note that the close methods of resources are called in the opposite order of their creation. The following example uses a try-with-resources statement to automatically close a java.sql.Statement object:
public static void viewTable(Connection con) throws SQLException {

String query = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from COFFEES"; try (Statement stmt = con.createStatement()) { ResultSet rs = stmt.executeQuery(query); while (rs.next()) { String coffeeName = rs.getString("COF_NAME"); int supplierID = rs.getInt("SUP_ID"); float price = rs.getFloat("PRICE"); int sales = rs.getInt("SALES"); int total = rs.getInt("TOTAL"); System.out.println(coffeeName + ", " + supplierID + ", " + price + ", " + sales + ", " + total); } } catch (SQLException e) { JDBCTutorialUtilities.printSQLException(e); } }

The resource java.sql.Statement used in this example is part of the JDBC 4.1 and later API. Note: A try-with-resources statement can have catch and finally blocks just like an ordinary try statement. In a try-with-resources statement, any catch or finally block is run after the resources declared have been closed. Suppressed Exceptions An exception can be thrown from the block of code associated with the try-with-resources statement. In the example writeToFileZipFileContents, an exception can be thrown from the try block, and up to two exceptions can be thrown from the try-with-resources statement when it tries to close the ZipFile and BufferedWriter objects. If an exception is thrown from the try block and one or more exceptions are thrown from the try-with-resources statement, then those exceptions thrown from the try-with-resources statement are suppressed, and the exception thrown by the block is the one that is thrown by the writeToFileZipFileContents method. You can retrieve these suppressed exceptions by calling the Throwable.getSuppressed method from the exception thrown by the try block. Classes That Implement the AutoCloseable or Closeable Interface See the Javadoc of the AutoCloseable and Closeable interfaces for a list of classes that implement either of these interfaces. The Closeable interface extends the AutoCloseable interface. The close method of the Closeable interface throws exceptions of type IOException while the close method of the AutoCloseable interface throws exceptions of type Exception. Consequently, subclasses of the AutoCloseable interface can override this behavior of the close method to throw specialized exceptions, such as IOException, or no exception at all.

7. Catching Multiple Exception Types and Rethrowing Exceptions with Improved Type Checking
This page covers the following topics:

Handling More Than One Type of Exception Rethrowing Exceptions with More Inclusive Type Checking

Handling More Than One Type of Exception In Java SE 7 and later, a single catch block can handle more than one type of exception. This feature can reduce code duplication and lessen the temptation to catch an overly broad exception. Consider the following example, which contains duplicate code in each of the catch blocks:
catch (IOException ex) { logger.log(ex); throw ex; catch (SQLException ex) { logger.log(ex); throw ex; }

In releases prior to Java SE 7, it is difficult to create a common method to eliminate the duplicated code because the variable ex has different types. The following example, which is valid in Java SE 7 and later, eliminates the duplicated code:
catch (IOException|SQLException ex) { logger.log(ex); throw ex; }

The catch clause specifies the types of exceptions that the block can handle, and each exception type is separated with a vertical bar (|). Note: If a catch block handles more than one exception type, then the catch parameter is implicitly final. In this example, the catch parameter ex is final and therefore you cannot assign any values to it within the catch block. Bytecode generated by compiling a catch block that handles multiple exception types will be smaller (and thus superior) than compiling many catch blocks that handle only one exception type each. A catch block that handles multiple exception types creates no duplication in the bytecode generated by the compiler; the bytecode has no replication of exception handlers. Rethrowing Exceptions with More Inclusive Type Checking The Java SE 7 compiler performs more precise analysis of rethrown exceptions than earlier releases of Java SE. This enables you to specify more specific exception types in the throws clause of a method declaration.

Consider the following example:


static class FirstException extends Exception { } static class SecondException extends Exception { } public void rethrowException(String exceptionName) throws Exception { try { if (exceptionName.equals("First")) { throw new FirstException(); } else { throw new SecondException(); } } catch (Exception e) { throw e; } }

This examples's try block could throw either FirstException or SecondException. Suppose you want to specify these exception types in the throws clause of the rethrowException method declaration. In releases prior to Java SE 7, you cannot do so. Because the exception parameter of the catch clause, e, is type Exception, and the catch block rethrows the exception parameter e, you can only specify the exception type Exception in the throws clause of the rethrowException method declaration. However, in Java SE 7, you can specify the exception types FirstException and SecondException in the throws clause in the rethrowException method declaration. The Java SE 7 compiler can determine that the exception thrown by the statement throw e must have come from the try block, and the only exceptions thrown by the try block can be FirstException and SecondException. Even though the exception parameter of the catch clause, e, is type Exception, the compiler can determine that it is an instance of either FirstException or SecondException:
public void rethrowException(String exceptionName) throws FirstException, SecondException { try { // ... } catch (Exception e) { throw e; } }

This analysis is disabled if the catch parameter is assigned to another value in the catch block. However, if the catch parameter is assigned to another value, you must specify the exception type Exception in the throws clause of the method declaration. In detail, in Java SE 7 and later, when you declare one or more exception types in a catch clause, and rethrow the exception handled by this catch block, the compiler verifies that the type of the rethrown exception meets the following conditions:

The try block is able to throw it. There are no other preceding catch blocks that can handle it.

It is a subtype or supertype of one of the catch clause's exception parameters.

The Java SE 7 compiler allows you to specify the exception types FirstException and SecondException in the throws clause in the rethrowException method declaration because you can rethrow an exception that is a supertype of any of the types declared in the throws. In releases prior to Java SE 7, you cannot throw an exception that is a supertype of one of the catch clause's exception parameters. A compiler from a release prior to Java SE 7 generates the error, "unreported exception Exception; must be caught or declared to be thrown" at the statement throw e. The compiler checks if the type of the exception thrown is assignable to any of the types declared in the throws clause of the rethrowException method declaration. However, the type of the catch parameter e is Exception, which is a supertype, not a subtype, of FirstException andSecondException.

You might also like