Posted on Leave a comment

Sealed Interfaces – Object-Oriented Programming

Sealed Interfaces

Analogous to sealed classes, sealed interfaces can also be defined. However, a sealed interface can have both subinterfaces and subclasses as its permitted direct subtypes. The permitted subtypes can be subclasses that implement the sealed interface and subinterfaces that extend the sealed interface. This is in contrast to a sealed class that can have direct subclasses, but not direct subinterfaces—as classes cannot be extended or implemented by interfaces.

Figure 5.10 shows the book domain from Figure 5.9 that has been augmented with a sealed interface that specifies its permitted direct subtypes in a permits clause:

Click here to view code image

public sealed interface Subscribable permits Ebook, Audiobook, VIPSubcribable {}

The sealed superinterface Subscribable has two final permitted direct subclasses (that implement the superinterface) and one non-sealed direct subinterface (that extends the superinterface). The declarations of the permitted direct subclasses Ebook and Audiobook have been updated accordingly so that they implement the direct superinterface Subscribable.

Click here to view code image

public final class Ebook extends Book implements Subscribable {}
public final class Audiobook extends Book implements Subscribable {}
public non-sealed interface VIPSubcribable extends Subscribable {}

The rest of the type declarations from Figure 5.9 remain the same in Figure 5.10:

Click here to view code image

public abstract sealed class Book permits PrintedBook, Ebook, Audiobook {}
public non-sealed class PrintedBook extends Book {}

Note that it is perfectly possible for a class or an interface to be a permitted direct subtype of more than one direct supertype—as is the case for the Ebook and the Audiobook subclasses in Figure 5.10.

Figure 5.10 Sealed Classes and Interfaces

We see from the discussion above that the permitted direct subtypes of a sealed superinterface abide by the same contract rules as for sealed superclasses:

  • A permitted direct subclass or subinterface must extend or implement its direct superinterface, respectively.
  • Any permitted subclass of a sealed interface must be declared either sealed, non-sealed or final, but any permitted subinterface can only be declared either sealed or non-sealed. The modifier final is not allowed for interfaces.
  • The same rules for locality also apply for sealed interfaces and their permitted direct subtypes: All are declared either in the same named module or in the same package (named or unnamed) in the unnamed module.
Enum and Record Types as Permitted Direct Subtypes

By definition, an enum type (p. 287) is either implicitly final (has no enum constants that have a class body, as shown at (2)) or implicitly sealed (has at least one enum constant with a class body that constitutes an implicitly declared direct subclass, as shown at (3) for the constant DVD_R that has an empty class body). Thus an enum type can be specified as a permitted direct subtype of a sealed superinterface, as shown at (1). The modifiers final and sealed cannot be explicitly specified in the enum type declaration.

Click here to view code image

sealed interface MediaStorage permits CD, DVD {}       // (1) Sealed interface
enum CD implements MediaStorage {CD_ROM, CD_R, CD_W}   // (2) Implicitly final
enum DVD implements MediaStorage {DVD_R {}, DVD_RW}    // (3) Implicitly sealed

Analogously, a record class (p. 299) is implicitly final, and can be specified as a permitted direct subtype of a sealed superinterface. The sealed interface MediaStorage at (1a) now permits the record class HardDisk as a direct subtype. Again note that the modifier final cannot be specified in the header of the HardDisk record class declared at (4).

Click here to view code image

sealed interface MediaStorage permits CD, DVD, HardDisk {}// (1a) Sealed interface
record HardDisk(double capacity) implements MediaStorage {}// (4) Implicitly final

Posted on Leave a comment

Rethrowing Exceptions – Exception Handling

Rethrowing Exceptions

Rethrowing an exception refers to throwing an exception in the body of a catch clause. The catch clause catches an exception, but then throws this exception or another exception in its body. This allows an exception to be partially handled when it is caught the first time, and then again when the rethrown exception is caught later. Typically, the first exception handler is a common handler for the situation and the later exception handler is a more specific one.

Exception parameters that are explicitly, implicitly, or effectively final in catch clauses allow the compiler to perform improved analysis of exception handling in the code, especially when it comes to rethrowing exceptions.

For the examples in this section, it is important to keep in mind that the exception type IOException is the supertype of both EOFException and FileNotFoundException.

Example 7.11 illustrates how the compiler is able to identify unreachable code by precise analysis of rethrown exceptions that are either final or effectively final. The body of the try statement at (1) can only throw a FileNotFoundException that is caught by the catch clause at (3). This exception is effectively final in the catch clause at (3), as no assignment is made to it in the body of the catch clause. This exception is rethrown in the nested try statement at (4), but the catch clause at (6) of this try statement can only catch an EOFException. Since parameter ex is effectively final, it can only denote a FileNotFoundException, never an EOFException. The catch clause at (6) is unreachable, and the compiler flags an error.

Example 7.11 Precise Rethrowing of Exceptions

Click here to view code image

import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ExceptionAnalysis {
  public static void main(String[] args) throws IOException {
    try {                                // (1)
      throw new FileNotFoundException(); // (2)
    } catch (IOException ex) {           // (3)
      try {                        // (4) Nested try statement
        throw ex;                  // (5) Can only rethrow FileNotFoundException
      } catch (EOFException se) {  // (6) Compile-time error: clause unreachable
        System.out.println(“I am unreachable.”);
      }
}
  }
}

Example 7.12 illustrates how final or effectively final catch parameters allow more precise exceptions to be specified in the throws clause of a method. The thing to note is that the parameter e in the catch clause at (5) is not effectively final, as an assignment is made to the parameter at (6). All bets are off when the parameter is not final and the exception is rethrown. The throws clause must specify the same type as the type of the catch parameter, as shown at (3a). This has consequences for the caller method main(). Its try statement at (1) must include the catch clause at (2) to catch an IOException as well, or the compiler will flag an error about an uncaught checked exception. The contract of the checkIt() method allows for all exceptions that are either IOException or its subtypes.

If the assignment statement at (6) is commented out, the catch parameter e is effectively final in the catch body. The compiler can deduce that the rethrown exception can only be of type FileNotFoundException or EOFException. The throws clause of the checkIt() method can be made more specific, as at (3b). Note that the type of the catch parameter is the supertype IOException of the subtypes specified in the throws clause at (3b). Commenting out the assignment statement at (6) and uncommenting the more precise throws clause at (3b) has consequences for the caller method main() as well. The catch clause at (2) becomes unreachable, and the compiler issues a warning. Note also that the type of the exception parameter e at (5) is IOException, which is the supertype of the exception types specified in the throws clause. However, static analysis by the compiler is able to confirm that the exception parameter e can only denote objects of either FileNotFoundException or EOFException, but not of supertype IOException.

Example 7.12 Precise throws Clause

Click here to view code image

import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.IOException;
public class MorePreciseRethrow {
  public static void main(String[] args) {                        // (1)
    try {
      checkIt(1);
    } catch (FileNotFoundException fnfe) {
      System.out.println(“Check that the file exits.”);
    } catch (EOFException eofe) {
      System.out.println(“Check the contents of the file.”);
    } catch (IOException ioe) { // (2) mandatory with (3a), but compiler warning
                                //     that clause is unreachable with (3b).
      System.out.println(“This should never occur.”);
    }
  }
public static void checkIt(int value) throws IOException {      // (3a)
//public static void checkIt(int value)                           // (3b)
//    throws FileNotFoundException, EOFException {
    try {                                                         // (4)
      switch (value) {
        case 1:
          throw new FileNotFoundException(“File not found”);
        case 2:
          throw new EOFException(“End of file”);
        default:
          System.out.println(“OK”);
      }
    } catch (IOException e) {                                      // (5)
      System.out.println(e.getMessage());
      e = new EOFException(“End of file”);         // (6) not effectively final,
                                                   //     requires (3a).
                                                   //     When commented out,
                                                   //     can use (3b).
      throw e;
    }
  }
}

Program output with (3a) and (6) uncommented, and (3b) commented out:

Click here to view code image

File not found
Check the contents of the file.

Program output with (3a) and (6) commented out, and (3b) is uncommented:

Click here to view code image

File not found
Check that the file exits.

In summary, a throw statement in the body of a catch clause can throw a final or an effectively final exception parameter that has exception type E if all of the following conditions are satisfied:

  • Exception type E can be thrown in the body of the try statement with which the catch clause is associated.
  • Exception type E is assignment compatible with any of the exception types declared for the parameter in the catch clause.
  • Exception type E is not assignment compatible with any of the exception types declared for the parameters in any preceding catch clause in the same try statement.

In Example 7.13, the throw statements at (1), (2), and (3) all try to rethrow an exception that is effectively final in the body of the catch clause.

  • The throw statement at (1) in the main() method satisfies all the conditions. The try block throws an exception of the right type (EOFException). The exception thrown (EOFException) is assignment compatible with the type declared for the parameter in the catch clause (IOException). There is no preceding catch clause that handles the exception (EOFException).
  • The throw statement at (2) in the rethrowA() method cannot throw the exception, as the first condition is not satisfied: The try block does not throw an exception of the right type (EOFException). The compiler flags an error for the catch clause that is unreachable.
  • The throw statement at (3) in the rethrowB() method cannot throw the exception, as the third condition is not satisfied: A preceding catch clause can handle the exception (EOFException). The compiler flags a warning for the catch clause which is unreachable.

Example 7.13 Conditions for Rethrowing Final Exceptions

Click here to view code image

import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.IOException;
public class RethrowMe {
  public static void main(String[] args) throws EOFException {
    try {
      switch (1) {
        case 1: throw new FileNotFoundException(“File not found”);
        case 2: throw new EOFException(“End of file”);
        default: System.out.println(“OK”);
      }
    } catch (FileNotFoundException fnfe) {
      System.out.println(fnfe);
    } catch (IOException ioe) {
      throw ioe;                                                    // (1)
    }
  }
  public static void rethrowA() throws EOFException {
    try {
      // Empty try block.
    } catch (EOFException eofe) { // Compile-time error: exception not thrown
                                  //                     in try block.
      throw eofe;                                                   // (2)
    }
  }
  public static void rethrowB() throws EOFException {
    try {
      throw new EOFException(“End of file”);
    } catch (EOFException eofe) {
      System.out.println(eofe);
    } catch (IOException ioe) {   // Compile-time warning: unreachable clause
      throw ioe;                                                    // (3)
    }
  }
}

Posted on Leave a comment

Access Modifiers – Access Control

6.5 Access Modifiers

In this section, we discuss accessibility of top-level type declarations that can be encapsulated into packages and accessibility of members that can be encapsulated in a top-level type declaration. A top-level reference type is a reference type (class, interface, enum, record) that is not declared inside another reference type.

Access modifiers are sometimes also called visibility modifiers.

Access Modifiers for Top-Level Type Declarations

The access modifier public can be used to declare top-level reference types that are accessible from everywhere, both from inside their own package and from inside other packages. If the access modifier is omitted, the reference types can be accessed only in their own package and not in any other packages—that is, they have package access, also called package-private or default access.

The packages shown in Figure 6.2, p. 327, are implemented by the code in Example 6.7. Class files with Java bytecode for top-level type declarations are placed in designated packages using the package statement. A top-level type declaration from one package can be accessed in another packages either by using the fully qualified name of the type or by using an import statement to import the type so that it can be accessed by its simple name.

Example 6.7 Access Modifiers for Top-Level Reference Types

Click here to view code image

// File: Clown.java
package wizard.pandorasbox;                  // Package declaration
import wizard.pandorasbox.artifacts.Ailment; // Importing class Ailment
public class Clown implements Magic {        // (1)
  LovePotion tlc;                            // Class in same package
  Ailment problem;                           // Simple class name
  Clown() {
    tlc = new LovePotion(“passion”);
    problem = new Ailment(“flu”);            // Simple class name
  }
  @Override public void levitate()  {        // (2)
    System.out.println(“Levitating”);
  }
  public void mixPotion()   { System.out.println(“Mixing ” + tlc); }
  public void healAilment() { System.out.println(“Healing ” + problem); }
  public static void main(String[] args) {
    Clown joker = new Clown();
    joker.levitate();
    joker.mixPotion();
    joker.healAilment();
  }
}
interface Magic { void levitate(); }         // (3)

Click here to view code image

// File: LovePotion.java
package wizard.pandorasbox;                  // Package declaration

public class LovePotion {                    // (4) Accessible outside package
  String potionName;
  public LovePotion(String name) { potionName = name; }
  public String toString()       { return potionName; }
}

Click here to view code image

// File: Ailment.java
package wizard.pandorasbox.artifacts;        // Package declaration

public class Ailment {                       // Accessible outside package
  String ailmentName;
  public Ailment(String name) { ailmentName = name; }
  public String toString() { return ailmentName; }
}

Click here to view code image

// File: Baldness.java
package wizard.spells;                       // Package declaration

import wizard.pandorasbox.*;                 // Redundant
import wizard.pandorasbox.artifacts.*;       // Import of subpackage

public class Baldness extends Ailment {      // Simple name for Ailment
  wizard.pandorasbox.LovePotion tlcOne;      // Fully qualified name
  LovePotion tlcTwo;                         // Class in same package
  Baldness(String name) {
    super(name);
    tlcOne = new wizard.pandorasbox.         // Fully qualified name
                 LovePotion(“romance”);
    tlcTwo = new LovePotion();               // Class in same package
  }
}
class LovePotion /* implements Magic */ {    // (5) Magic is not accessible
  // @Override public void levitate() {}     // (6) Cannot override method
}

Compiling and running the program from the current directory gives the following results:

Click here to view code image

>
javac -d . Clown.java LovePotion.java Ailment.java Baldness.java
>
java wizard.pandorasbox.Clown

Levitating
Mixing passion
Healing flu

In Example 6.7, the class Clown at (1) and the interface Magic at (3) are placed in a package called wizard.pandorasbox. The public class Clown is accessible from everywhere. The Magic interface has package accessibility, and can only be accessed within the package wizard.pandorasbox. It is not accessible from other packages, not even from subpackages.

The class LovePotion at (4) is also placed in the package called wizard.pandorasbox. The class has public accessibility, and is therefore accessible from other packages. The two files Clown.java and LovePotion.java demonstrate how several compilation units can be used to group classes in the same package, as the type declarations in these two source files are placed in the package wizard.pandorasbox.

In the file Clown.java, the class Clown at (1) implements the interface Magic at (3) from the same package. We have used the annotation @Override in front of the declaration of the levitate() method at (2) so that the compiler can aid in checking that this method is declared correctly as required by the interface Magic.

In the file Baldness.java, the class LovePotion at (5) wishes to implement the interface Magic at (3) from the package wizard.pandorasbox, but this is not possible, although the source file imports from this package. The reason is that the interface Magic has package accessibility, and can therefore only be accessed within the package wizard.pandorasbox. The method levitate() of the Magic interface therefore cannot be overridden in class LovePotion at (6).

Table 6.2 summarizes accessibility of top-level reference types in a package. Just because a reference type is accessible does not necessarily mean that members of the type are also accessible. Accessibility of members is governed separately from type accessibility, as explained in the next subsection.

Table 6.2 Access Modifiers for Top-Level Reference Types (Non-Modular)

ModifiersTop-level types
No modifierAccessible in its own package (package accessibility)
publicAccessible anywhere
Posted on Leave a comment

Defining Packages – Access Control

Defining Packages

A package hierarchy represents an organization of the Java classes and interfaces. It does not represent the source code organization of the classes and interfaces. The source code is of no consequence in this regard. Each Java source file (also called compilation unit) can contain zero or more type declarations, but the compiler produces a separate class file containing the Java bytecode for each of them. A type declaration can indicate that its Java bytecode should be placed in a particular package, using a package declaration.

The package statement has the following syntax:

Click here to view code image

package
fully_qualified_package_name
;

At most, one package declaration can appear in a source file, and it must be the first statement in the source file. The package name is saved in the Java bytecode of the types contained in the package. Java naming conventions recommend writing package names in lowercase letters.

Note that this scheme has two consequences. First, all the classes and interfaces in a source file will be placed in the same package. Second, several source files can be used to specify the contents of a package.

If a package declaration is omitted in a compilation unit, the Java bytecode for the declarations in the compilation unit will belong to an unnamed package (also called the default package), which is typically synonymous with the current working directory on the host system.

Example 6.1 illustrates how the packages in Figure 6.2 can be defined using the package declaration. There are four compilation units. Each compilation unit has a package declaration, ensuring that the type declarations are compiled into the correct package. The complete code can be found in Example 6.7, p. 345.

Example 6.1 Defining Packages and Using Type Import

Click here to view code image

// File name: Clown.java                     // This file has 2 type declarations
package wizard.pandorasbox;                  // Package declaration
import wizard.pandorasbox.artifacts.Ailment; // Importing specific class
public class Clown implements Magic { /* … */ }
interface Magic { /* … */ }

Click here to view code image

// File name: LovePotion.java
package wizard.pandorasbox;                  // Package declaration
public class LovePotion { /* … */ }

Click here to view code image

// File name: Ailment.java
package wizard.pandorasbox.artifacts;        // Package declaration
public class Ailment { /* … */ }

Click here to view code image

// File name: Baldness.java                  // This file has 2 type declarations
package wizard.spells;                       // Package declaration
import wizard.pandorasbox.*;                 // (1) Type-import-on-demand
import wizard.pandorasbox.artifacts.*;       // (2) Import from subpackage
public class Baldness extends Ailment {      // Simple name for Ailment
  wizard.pandorasbox.LovePotion tlcOne;      // (3) Fully qualified class name
  LovePotion tlcTwo;                         // Class in same package
  // …
}
class LovePotion { /* … */ }

Posted on Leave a comment

Importing Static Members of Reference Types – Access Control

Importing Static Members of Reference Types

Analogous to the type import facility, Java also allows import of static members of reference types from packages, often called static import. Imported static members can be used by their simple names, and therefore need not be qualified. Importing static members of reference types from the unnamed package is not permissible.

The two forms of static import are shown here:

  • Single static import: imports a specific static member from the designated type

import static fully_qualified_type_name.static_member_name;

  • Static import on demand: imports all static members in the designated type

import static fully_qualified_type_name.*;

Both forms require the use of the keyword import followed by the keyword static, although the feature is called static import. In both cases, the fully qualified name of the reference type we are importing from is required.

The first form allows single static import of individual static members, and is demonstrated in Example 6.2. The constant PI, which is a static field in the class java.lang.Math, is imported at (1). Note the use of the fully qualified name of the type in the static import statement. The static method named sqrt from the class java.lang.Math is imported at (2). Only the name of the static method is specified in the static import statement; no parameters are listed. Use of any other static member from the Math class requires that the fully qualified name of the class be specified. Since types from the java.lang package are imported implicitly, the fully qualified name of the Math class is not necessary, as shown at (3).

Static import on demand is easily demonstrated by replacing the two import statements in Example 6.2 with the following import statement:

Click here to view code image

import static java.lang.Math.*;

We can also dispense with the use of the class name Math at (3), as all static members from the Math class are now imported:

Click here to view code image

double hypotenuse = hypot(x, y);   // (3′) Type name can now be omitted.

Example 6.2 Single Static Import

Click here to view code image

import static java.lang.Math.PI;           // (1) Static field
import static java.lang.Math.sqrt;         // (2) Static method
// Only specified static members are imported.
public class Calculate3 {
  public static void main(String[] args) {
    double x = 3.0, y = 4.0;
double squareroot = sqrt(y);           // Simple name of static method
    double hypotenuse = Math.hypot(x, y);  // (3) Requires type name
    double area = PI * y * y;              // Simple name of static field
    System.out.printf(“Square root: %.2f, hypotenuse: %.2f, area: %.2f%n”,
                        squareroot, hypotenuse, area);
  }
}

Output from the program:

Click here to view code image

Square root: 2.00, hypotenuse: 5.00, area: 50.27

Example 6.3 illustrates how static import can be used to access interface constants (§5.6, p. 254). The static import statement at (1) allows the interface constants in the package mypkg to be accessed by their simple names. The static import facility avoids the MyFactory class having to implement the interface so as to access the constants by their simple name (often referred to as the interface constant antipattern):

Click here to view code image

public class MyFactory implements mypkg.IMachineState {
 // …
}

Example 6.3 Avoiding the Interface Constant Antipattern

Click here to view code image

package mypkg;
public interface IMachineState {
  // Fields are public, static, and final.
  int BUSY = 1;
  int IDLE = 0;
  int BLOCKED = -1;
}

Click here to view code image

import static mypkg.IMachineState.*;    // (1) Static import interface constants
public class MyFactory {
  public static void main(String[] args) {
    int[] states = { IDLE, BUSY, IDLE, BLOCKED }; // (2) Access by simple name
    for (int s : states)
      System.out.print(s + ” “);
  }
}

Output from the program:

0 1 0 -1

Static import is ideal for importing enum constants from packages, as such constants are static members of an enum type (§5.13, p. 287). Example 6.4 combines type and static imports. The enum constants can be accessed at (5) using their simple names because of the static import statement at (2). The type import at (1) is required to access the enum type State by its simple name at (4) and (6).

Example 6.4 Importing Enum Constants

Click here to view code image

package mypkg;
public enum State { BUSY, IDLE, BLOCKED }

Click here to view code image

// File: Factory.java (in unnamed package)
import mypkg.State;                  // (1) Single type import
import static mypkg.State.*;         // (2) Static import on demand
import static java.lang.System.out;  // (3) Single static import
public class Factory {
  public static void main(String[] args) {
    State[] states = {               // (4) Using type import implied by (1)
        IDLE, BUSY, IDLE, BLOCKED    // (5) Using static import implied by (2)
    };
    for (State s : states)           // (6) Using type import implied by (1)
      out.print(s + ” “);            // (7) Using static import implied by (3)
  }
}

Output from the program:

IDLE BUSY IDLE BLOCKED

Identifiers in a class can shadow static members that are imported. Example 6.5 illustrates the case where the parameter out of the method writeInfo() has the same name as the statically imported field java.lang.System.out. The type of the parameter out is ShadowImport and that of the statically imported field out is PrintStream. Both classes PrintStream and ShadowImport define the method println() that is called in the program. The only way to access the imported field out in the method write-Info() is to use its fully qualified name.

Example 6.5 Shadowing Static Import

Click here to view code image

import static java.lang.System.out;       // (1) Static import
public class ShadowImport {
  public static void main(String[] args) {
    out.println(“Calling println() in java.lang.System.out”);
    ShadowImport sbi = new ShadowImport();
    writeInfo(sbi);
  }
// Parameter out shadows java.lang.System.out:
  public static void writeInfo(ShadowImport out) {
    out.println(“Calling println() in the parameter out”);
    System.out.println(“Calling println() in java.lang.System.out”); // Qualify
  }
  public void println(String msg) {
    out.println(msg + ” of type ShadowImport”);
  }
}

Output from the program:

Click here to view code image

Calling println() in java.lang.System.out
Calling println() in the parameter out of type ShadowImport
Calling println() in java.lang.System.out

The next code snippet illustrates a common conflict that occurs when a static field with the same name is imported by several static import statements. This conflict is readily resolved by using the fully qualified name of the field. In the case shown here, we can use the simple name of the class in which the field is declared, as the java.lang package is implicitly imported by all compilation units.

Click here to view code image

import static java.lang.Integer.MAX_VALUE;
import static java.lang.Double.MAX_VALUE;
public class StaticFieldConflict {
  public static void main(String[] args) {
    System.out.println(MAX_VALUE);          // (1) Ambiguous! Compile-time error!
    System.out.println(Integer.MAX_VALUE);  // OK
    System.out.println(Double.MAX_VALUE);   // OK
  }
}

Conflicts can also occur when a static method with the same signature is imported by several static import statements. In Example 6.6, a method named binarySearch is imported 21 times by the static import statements. This method is overloaded twice in the java.util.Collections class and 18 times in the java.util.Arrays class, in addition to one declaration in the mypkg.Auxiliary class. The classes java.util.Arrays and mypkg.Auxiliary have a declaration of this method with the same signature (binarySearch(int[], int) that matches the method call at (2), resulting in a signature conflict that is flagged as a compile-time error. The conflict can again be resolved by specifying the fully qualified name of the method.

If the static import statement at (1) is removed, there is no conflict, as only the class java.util.Arrays has a method that matches the method call at (2). If the declaration of the method binarySearch() at (3) is allowed, there is also no conflict, as this method declaration will shadow the imported method whose signature it matches.

Example 6.6 Conflict in Importing a Static Method with the Same Signature

Click here to view code image

package mypkg;
public class Auxiliary {
  public static int binarySearch(int[] a, int key) { // Same in java.util.Arrays
    // Implementation is omitted.
    return -1;
  }
}

Click here to view code image

// File: MultipleStaticImport.java (in unnamed package)
import static java.util.Collections.binarySearch;  //    2 overloaded methods
import static java.util.Arrays.binarySearch;       // + 18 overloaded methods
import static mypkg.Auxiliary.binarySearch; // (1) Causes signature conflict
public class MultipleStaticImport {
  public static void main(String[] args) {
    int index = binarySearch(new int[] {10, 50, 100}, 50); // (2) Ambiguous!
    System.out.println(index);
  }
//public static int binarySearch(int[] a, int key) {       // (3)
//  return -1;
//}
}

Posted on Leave a comment

Block Scope for Local Variables – Access Control

Block Scope for Local Variables

Declarations and statements can be grouped into a block using curly brackets, {}. Blocks can be nested, and scope rules apply to local variable declarations in such blocks. A local declaration can appear anywhere in a block. The general rule is that a variable declared in a block is in scope in the block in which it is declared, but it is not accessible outside this block. It is not possible to redeclare a variable if a local variable of the same name is already declared in the current scope.

Local variables of a method include the formal parameters of the method and variables that are declared in the method body. The local variables in a method are created each time the method is invoked, and are therefore distinct from local variables in other invocations of the same method that might be executing (§7.1, p. 365).

Figure 6.6 illustrates block scope (also known as lexical scope) for local variables. It shows four blocks: Block 1 is the body of the method main(), Block 2 is the body of the for(;;) loop, Block 3 is the body of a switch statement, and Block 4 is the body of an if statement.

  • Parameters cannot be redeclared in the method body, as shown at (1) in Block 1.
  • A local variable—already declared in an enclosing block, and therefore visible in a nested block—cannot be redeclared in the nested block. These cases are shown at (3), (5), and (6).
  • A local variable in a block can be redeclared in another block if the blocks are disjoint—that is, they do not overlap. This is the case for variable i at (2) in Block 3 and at (4) in Block 4, as these two blocks are disjoint.

The scope of a local variable declaration begins from where it is declared in the block and ends where this block terminates. The scope of the loop variable index is the entire Block 2. Even though Block 2 is nested in Block 1, the declaration of the variable index at (7) in Block 1 is valid. The scope of the variable index at (7) spans from its declaration to the end of Block 1, and it does not overlap with that of the loop variable index in Block 2.

  

Figure 6.6 Block Scope

Posted on Leave a comment

Stack-Based Execution and Exception Propagation – Exception Handling

7.1 Stack-Based Execution and Exception Propagation

The exception mechanism is built around the throw-and-catch paradigm. To throw an exception is to signal that an unexpected event has occurred. To catch an exception is to take appropriate action to deal with the exception. An exception is caught by an exception handler, and the exception need not be caught in the same context in which it was thrown. The runtime behavior of the program determines which exceptions are thrown and how they are caught. The throw-and-catch principle is embedded in the try-catch-finally construct (p. 375).

Several threads can be executing at the same time in the JVM (§22.2, p. 1369). Each thread has its own JVM stack (also called a runtime stack, call stack, or invocation stack in the literature) that is used to handle execution of methods. Each element on the stack is called an activation frame or a stack frame and corresponds to a method call. Each new method call results in a new activation frame being pushed on the stack, which stores all the pertinent information such as the local variables. The method with the activation frame on the top of the stack is the one currently executing. When this method finishes executing, its activation frame is popped from the top of the stack. Execution then continues in the method corresponding to the activation frame that is now uncovered on the top of the stack. The methods on the stack are said to be active, as their execution has not completed. At any given time, the active methods on a JVM stack make up what is called the stack trace of a thread’s execution.

Example 7.1 is a simple program to illustrate method execution. It calculates the average for a list of integers, given the sum of all the integers and the number of integers. It uses three methods:

  • The method main() calls the method printAverage() with parameters supplying the total sum of the integers and the total number of integers, (1).
  • The method printAverage() in turn calls the method computeAverage(), (3).
  • The method computeAverage() uses integer division to calculate the average and returns the result, (7).

Example 7.1 Method Execution

Click here to view code image

public class Average1 {
  public static void main(String[] args) {
    printAverage(100, 20);                                         // (1)
System.out.println(“Exit main().”);                            // (2)
  }
  public static void printAverage(int totalSum, int totalCount) {
    int average = computeAverage(totalSum, totalCount);            // (3)
    System.out.println(“Average = ” +                              // (4)
        totalSum + ” / ” + totalCount + ” = ” + average);
    System.out.println(“Exit printAverage().”);                    // (5)
  }
  public static int computeAverage(int sum, int count) {
    System.out.println(“Computing average.”);                      // (6)
    return sum/count;                                              // (7)
  }
}

Output of program execution:

Click here to view code image

Computing average.
Average = 100 / 20 = 5
Exit printAverage().
Exit main().

Execution of Example 7.1 is illustrated in Figure 7.1. Each method execution is shown as a box with the local variables declared in the method. The height of the box indicates how long a method is active. Before the call to the method System.out.println() at (6) in Figure 7.1, the stack trace comprises the three active methods: main(), printAverage(), and computeAverage(). The result 5 from the method computeAverage() is returned at (7) in Figure 7.1. The output from the program corresponds with the sequence of method calls in Figure 7.1. As the program terminates normally, this program behavior is called normal execution.

If the method call at (1) in Example 7.1

Click here to view code image

printAverage(100, 20);                                // (1)

is replaced with

Click here to view code image

printAverage(100, 0);                                 // (1)

and the program is run again, the output is as follows:

Click here to view code image

Computing average.
Exception in thread “main” java.lang.ArithmeticException: / by zero
        at Average1.computeAverage(Average1.java:18)
        at Average1.printAverage(Average1.java:10)
        at Average1.main(Average1.java:5)

Figure 7.2 illustrates the program execution when the method printAverage() is called with the arguments 100 and 0 at (1). All goes well until the return statement at (7) in the method computeAverage() is executed. An error event occurs in calculating the expression sum/number because integer division by 0 is an illegal operation. This event is signaled by the JVM by throwing an ArithmeticException (p. 372). This exception is propagated by the JVM through the JVM stack as explained next.

Figure 7.1 Normal Method Execution

Figure 7.2 illustrates the case where an exception is thrown and the program does not take any explicit action to deal with the exception. In Figure 7.2, execution of the computeAverage() method is suspended at the point where the exception is thrown. The execution of the return statement at (7) never gets completed. Since this method does not have any code to deal with the exception, its execution is likewise terminated abruptly and its activation frame popped. We say that the method completes abruptly. The exception is then offered to the method whose activation is now on the top of the stack (printAverage()). This method does not have any code to deal with the exception either, so its execution completes abruptly. The statements at (4) and (5) in the method printAverage() never get executed. The exception now propagates to the last active method (main()). This does not deal with the exception either. The main() method also completes abruptly. The statement at (2) in the main() method never gets executed. Since the exception is not caught by any of the active methods, it is dealt with by the main thread’s default exception handler. The default exception handler usually prints the name of the exception, with an explanatory message, followed by a printout of the stack trace at the time the exception was thrown. An uncaught exception, as in this case, results in the death of the thread in which the exception occurred.

Figure 7.2 Exception Propagation

If an exception is thrown during the evaluation of the left-hand operand of a binary expression, then the right-hand operand is not evaluated. Similarly, if an exception is thrown during the evaluation of a list of expressions (e.g., a list of actual parameters in a method call), evaluation of the rest of the list is skipped.

If the line numbers in the stack trace are not printed in the output as shown previously, use the following command to run the program:

Click here to view code image >
java -Djava.compiler=NONE Average1

Posted on Leave a comment

public Members – Access Control

public Members

Public access is the least restrictive of all the access modifiers. A public member is accessible from anywhere, both in the package containing its class and by other packages where its class is accessible.

In Example 6.8, the public instance field pubInt in an instance of the class Superclass1 is accessible by all four clients. Subclasses can access their inherited public members by their simple names, and all clients can access public members in an instance of the class Superclass1.

protected Members

A protected member is accessible in all classes in the same package, and by all subclasses of its class in any package where its class is accessible.

In other words, a protected member cannot be accessed by non-subclasses in other packages. This kind of access is more restrictive than public member access.

In Example 6.8, the protected instance field proStr in an instance of the class Superclass1 is accessible within pkg1 by Client 1 and Client 2. Also as expected, Client 4—the class NonSubclass2 in pkg2—cannot access this protected member of the class Superclass1.

However, the compiler reports an error at (7) in the method printState1() of the class Subclass2 where the protected instance field proStr in an instance of the class Superclass1 cannot be accessed by the reference obj1.

In contrast, the method printState2() at (8) in the class Subclass2 uses the reference obj2 that refers to an instance of the class Subclass2 to access the instance fields declared in the class Superclass1, and now the protected instance field proStr in the superclass is accessible, as shown at (10).

This apparent anomaly is explained by the fact that a subclass in another package can only access protected instance members in the superclass via references of its own type—that is, protected instance members that are inherited by objects of the subclass. No inheritance from the superclass is involved in a subclass in another package when an object of the superclass is used.

Note that the above anomaly would not arise if a subclass in another package were to access any protected static members in the superclass, as such members are not part of any object of the superclass.

Posted on Leave a comment

The java.lang.RuntimeException Class – Exception Handling

The java.lang.RuntimeException Class

Runtime exceptions are all subclasses of the java.lang.RuntimeException class, which is a subclass of the Exception class. As these runtime exceptions are usually caused by program bugs that should not occur in the first place, it is usually more appropriate to treat them as faults in the program design and let them be handled by the default exception handler.

Click here to view code image

java.lang.ArithmeticException

This exception represents situations where an illegal arithmetic operation is attempted, such as integer division by 0. It is typically thrown by the JVM.

Click here to view code image

java.lang.ArrayIndexOutOfBoundsException

Java provides runtime checking of the array index value, meaning out-of-bounds array indices. The subclass ArrayIndexOutOfBoundsException of the RuntimeException class represents exceptions thrown by the JVM that signal out-of-bound errors specifically for arrays—that is, an error in which an invalid index is used to access an element in the array. The index value must satisfy the relation 0 ≤ index value < length of the array (§3.9, p. 120).

Click here to view code image

java.lang.ArrayStoreException

This exception is thrown by the JVM when an attempt is made to store an object of the wrong type into an array of objects. The array store check at runtime ensures that an object being stored in the array is assignment compatible with the element type of the array (§5.7, p. 260). To make the array store check feasible at runtime, the array retains information about its declared element type at runtime.

java.lang.ClassCastException

This exception is thrown by the JVM to signal that an attempt was made to cast a reference value to a type that was not legal, such as casting the reference value of an Integer object to the Long type (§5.11, p. 269).

Click here to view code image

java.lang.IllegalArgumentException

The class IllegalArgumentException represents exceptions thrown to signal that a method was called with an illegal or inappropriate argument. For example, the ofPattern(String pattern) method in the java.time.format.DateTimeFormatter class throws an IllegalArgumentException when the letter pattern passed as an argument is invalid (§18.6, p. 1134).

Click here to view code image

java.lang.IllegalThreadStateException

The class IllegalThreadStateException is a subclass of the IllegalArgumentException class. Certain operations on a thread can only be executed when the thread is in an appropriate state (§22.4, p. 1380). This exception is thrown when this is not the case. For example, the start() method of a Thread object throws this exception if the thread has already been started (§22.3, p. 1370).

Click here to view code image

java.lang.NumberFormatException

The class NumberFormatException is a subclass of the IllegalArgumentException class that is specialized to signal problems when converting a string to a numeric value if the format of the characters in the string is not appropriate for the conversion. This exception is thrown programmatically. The numeric wrapper classes all have methods that throw this exception when conversion from a string to a numeric value is not possible (§8.3, p. 434).

Click here to view code image

java.lang.NullPointerException

This exception is typically thrown by the JVM when an attempt is made to use the null value as a reference value to refer to an object. This might involve calling an instance method using a reference that has the null value, or accessing a field using a reference that has the null value.

This programming error has made this exception one of the most frequently thrown exceptions by the JVM. The error message issued provides helpful information as to where and which reference raised the exception. However, inclusion of variables names in the message can be a potential security risk.

Click here to view code image

Exception in thread “main” java.lang.NullPointerException: Cannot invoke
“String.toLowerCase()” because “msg” is null
    at StringMethods.main(StringMethods.java:162)

Click here to view code image

java.lang.UnsupportedOperationException

This exception is thrown programmatically to indicate that an operation invoked on an object is not supported. Typically, a class implements an interface, but chooses not to provide certain operations specified in the interface. Methods in the class corresponding to these operations throw this exception to indicate that an operation is not supported by the objects of the class.

The API documentation of the java.util.Collection interface (§15.1, p. 783) in the Java Collections Framework states that certain methods are optional, meaning that a concrete collection class need not provide support for such operations, and if any such operation is not supported, then the method should throw this exception.

java.time.DateTimeException

In the Date and Time API, the class DateTimeException represents exceptions that signal problems with creating, querying, and manipulating date-time objects (§17.2, p. 1027).

Click here to view code image

java.time.format.DateTimeParseException

In the Date and Time API, the class DateTimeParseException is a subclass of the Date-TimeException class that represents an exception that signals unexpected errors when parsing date and time values (§18.6, p. 1127).

Click here to view code image

java.util.MissingResourceException

This exception is thrown programmatically, typically by the lookup methods of the java.util.ResourceBundle class (§18.3, p. 1104). Get methods on resource bundles typically throw this exception when no resource can be found for a given key. Static lookup methods in this class also throw this exception when no resource bundle can be found based on a specified base name for a resource bundle. Resource bundles are discussed in §18.3, p. 1102.

The java.lang.Error Class

The class Error and its subclasses define errors that are invariably never explicitly caught and are usually irrecoverable. Not surprisingly, most such errors are signaled by the JVM. Apart from the subclass mentioned below, other subclasses of the java.lang.Error class define different categories of errors.

The subclass VirtualMachineError represents virtual machine errors like stack overflow (StackOverflowError) and out of memory for object allocation (OutOfMemoryError). The subclass LinkageError represents class linkage errors like missing class definitions (NoClassDefFoundError). The subclass AssertionError of the Error class is used by the Java assertion facility.

Posted on Leave a comment

The java.lang.Exception Class – Exception Handling

The java.lang.Exception Class

The class Exception represents exceptions that a program would normally want to catch. Its subclass java.lang.RuntimeException represents many common programming errors that can manifest at runtime (see the next subsection). Other subclasses of the Exception class, excluding the RuntimeException class, define what are known as checked exceptions (p. 374) that particularly aid in building robust programs. Some common checked exceptions are presented below.

Click here to view code image

java.lang.ClassNotFoundException

The class ClassNotFoundException is a subclass of the Exception class that signals that the JVM tried to load a class by its string name, but the class could not be found. A typical example of this situation is when the class name is misspelled while starting program execution with the java command. The source in this case is the JVM throwing the exception to signal that the class cannot be found.

java.io.IOException

The class IOException is a subclass of the Exception class that represents I/O-related exceptions that are found in the java.io package (EOFException, FileNotFound-Exception, NotSerializableException). Chapter 20, p. 1231, and Chapter 21, p. 1285, provide ample examples of contexts in which I/O-related exceptions can occur.

java.io.EOFException

The class EOFException is a subclass of the IOException class that represents an exception that signals that an end of file (EOF) or end of stream was reached unexpectedly when more input was expected—that is, there is no more input available. Typically, this situation occurs when an attempt is made to read input from a file when all data from the file has already been read, often referred to as reading past EOF.

Click here to view code image

java.io.FileNotFoundException

The class FileNotFoundException is a subclass of the IOException class that represents an exception that signals an attempt to open a file by using a specific pathname failed—in other words, the file with the specified pathname does not exist. This exception can also occur when an I/O operation does not have the required permissions for accessing the file.

Click here to view code image

java.io.NotSerializableException

The class NotSerializableException is a subclass of the IOException class that represents an exception that signals that an object does not implement the Serializable interface that is required in order for the object to be serialized (§20.5, p. 1261).

java.sql.SQLException

The class SQLException is a subclass of the Exception class that represents an exception that can provide information about various database-related errors that can occur. Chapter 24 provides examples illustrating such situations.

java.text.ParseException

The class ParseException is a subclass of the Exception class that represents an exception that signals unexpected errors while parsing. Examples of parsing date, number, and currency where this exception is thrown can be found in §18.5, p. 1116.