9 Object Lifetime

9.1 Garbage Collection

Efficient memory management is essential in a runtime system. Storage for objects is allocated in a designated part of the memory called the heap which has a finite size. Garbage collection is a process of managing the heap efficiently; i.e., reclaiming memory occupied by objects that are no longer needed and making it available for new objects. Java provides automatic garbage collection, meaning that the runtime environment can take care of memory management without the program having to take any special action. Objects allocated on the heap (through the new operator) are administered by the automatic garbage collector. The automatic garbage collection scheme guarantees that a reference to an object is always valid while the object is needed by the program. The object will not be reclaimed, leaving the reference dangling.

Having an automatic garbage collector frees the programmer from the responsibility of writing code for deleting objects. By relying on the automatic garbage collector, a Java program also forfeits any significant influence on the garbage collection of its objects (see p. 398). However, this price is insignificant when compared to the cost of putting the code for object management in place and plugging all the memory leaks. Time-critical applications should bear in mind that the automatic garbage collector runs as a background task and may have a negative impact on their performance.

9.2 Reachable Objects

An automatic garbage collector essentially performs two tasks:

• decides if and when memory needs to be reclaimed

• finds objects that are no longer needed by the program and reclaims their storage

A program has no guarantees that the automatic garbage collector will be run during its execution. A program should not rely on the scheduling of the automatic garbage collector for its behavior (see p. 398).

In order to understand how the automatic garbage collector finds objects whose storage should be reclaimed, we need to look at the activity going on in the JVM. Java provides thread-based multitasking, meaning that there can be several threads executing in the JVM, each doing its own task (see Chapter 13). A thread is an independent path of execution through the program code. A thread is alive if it has not completed its execution. Each live thread has its own runtime stack, as explained in Section 6.5 on page 235. The runtime stack contains activation records of methods that are currently active. Local references declared in a method can always be found in the method’s activation record, stored on the runtime stack associated with the thread in which the method is called. Objects, on the other hand, are always created on the heap. If an object has a field reference, the field is to be found inside the object in the heap, and the object denoted by the field reference is also to be found in the heap.

An example of how memory is organized during execution is depicted in Figure 9.1. It shows two live threads (t1 and t2) and their respective runtime stacks with the activation records. The diagram shows which objects in the heap are referenced by local references in the method activation records. The diagram also shows field references in objects, which refer to other objects in the heap. Some objects have several aliases.

Figure 9.1 Memory Organization at Runtime

Memory Organization at Runtime

An object in the heap is said to be reachable if it is referenced by any local reference in a runtime stack. Additionally, any object that is denoted by a reference in a reachable object is also said to be reachable. Reachability is a transitive relation. Thus, a reachable object has at least one chain of reachable references from the runtime stack. Any reference that makes an object reachable is called a reachable reference. An object that is not reachable is said to be unreachable.

A reachable object is alive, and is accessible by a live thread. Note that an object can be accessible by more than one thread. Any object that is not accessible by a live thread is a candidate for garbage collection. When an object becomes unreachable and is waiting for its memory to be reclaimed, it is said to be eligible for garbage collection. An object is eligible for garbage collection if all references denoting it are in eligible objects. Eligible objects do not affect the future course of program execution. When the garbage collector runs, it finds and reclaims the storage of eligible objects. However, garbage collection does not necessarily occur as soon as an object becomes unreachable.

From Figure 9.1 we see that objects o4, o5, o11, o12, o14, and o15 all have reachable references. Objects o13 and o16 have no reachable references and are, therefore, eligible for garbage collection.

From the discussion above we can conclude that if a composite object becomes unreachable, its constituent objects also become unreachable, barring any reachable references to the constituent objects. Although the objects o1, o2, and o3 form a circular list, they do not have any reachable references. Thus, these objects are all eligible for garbage collection. On the other hand, the objects o5, o6, and o7 form a linear list, but they are all reachable, as the first object in the list, o5, is reachable. The objects o8, o10, o11, and o9 also form a linear list (in that order), but not all objects in the list are reachable. Only the objects o9 and o11 are reachable, as object o11 has a reachable reference. The objects o8 and o10 are eligible for garbage collection.

The lifetime of an object is the time from its creation to the time it is garbage collected. Under normal circumstances, an object is accessible from the time when it is created to the time when it is unreachable. The lifetime of an object can also include a period when it is eligible for garbage collection, waiting for its storage to be reclaimed. The finalization mechanism (see p. 396) in Java does provide a means for resurrecting an object after it is eligible for garbage collection, but the finalization mechanism is rarely used for this purpose.

9.3 Facilitating Garbage Collection

The automatic garbage collector determines which objects are not reachable and, therefore, eligible for garbage collection. It will certainly go to work if there is an imminent memory shortage. Automatic garbage collection should not be perceived as a license for uninhibited creation of objects and then forgetting about them. However, certain programming practices can nevertheless help in minimizing the overhead associated with garbage collection during program execution.

Certain objects, such as files and network connections, can tie up resources and should be disposed of properly when they are no longer needed. In most cases, the finally block in the try-catch-finally construct (see Section 6.7, p. 245) provides a convenient facility for such purposes, as it will always be executed, thereby ensuring proper disposal of any unwanted resources.

To optimize its memory footprint, a live thread should only retain access to an object as long as the object is needed for its execution. The program can allow objects to become eligible for garbage collection as early as possible by removing all references to the object when it is no longer needed.

Objects that are created and accessed by local references in a method are eligible for garbage collection when the method terminates unless reference values to these objects are exported out of the method. This can occur if a reference value is returned from the method, passed as argument to another method that records the reference value, or thrown as an exception. However, a method need not always leave objects to be garbage collected after its termination. It can facilitate garbage collection by taking suitable action, for example, by nulling references.

import java.io.*;

class WellBehavedClass {
  // ...
  void wellBehavedMethod() {
    File aFile;
    long[] bigArray = new long[20000];
    // ... uses local variables ...
    // Does cleanup (before starting something extensive)
    aFile = null;                    // (1)
    bigArray = null;                 // (2)

    // Start some other extensive activity
    // ...
  }
  // ...
}

In the previous code, the local variables are set to null after use at (1) and (2), before starting some other extensive activity. This makes the objects denoted by the local variables eligible for garbage collection from this point onward, rather than after the method terminates. This optimization technique of nulling references need only be used as a last resort when resources are scarce.

Here are some other techniques to facilitate garbage collection:

• When a method returns a reference value and the object denoted by the value is not needed, not assigning this value to a reference also facilitates garbage collection.

• If a reference is assigned a new value, the object that was previously denoted by the reference prior to the assignment can become eligible for garbage collection.

• Removing reachable references to a composite object can make the constituent objects become eligible for garbage collection, as explained earlier.

Example 9.1 illustrates how a program can influence garbage collection eligibility. Class HeavyItem represents objects with a large memory footprint, on which we want to monitor garbage collection. Each composite HeavyItem object has a reference to a large array. The class overrides the finalize() method from the Object class to print out an ID when the object is finalized. This method is always called on an eligible object before it is destroyed (see finalizers, p. 396). We use it to indicate in the output if and when a HeavyItem is reclaimed. To illustrate the effect of garbage collection on object hierarchies, each HeavyItem object may also have a reference to another HeavyItem.

In Example 9.1, the class RecyclingBin defines a method createHeavyItem() at (4). In this method, the HeavyItem created at (5) is eligible for garbage collection after the reassignment of reference itemA at (6), as this object will have no references. The HeavyItem created at (6) is accessible on return from the method. Its fate depends on the code that calls this method.

In Example 9.1, the class RecyclingBin also defines a method createList() at (8). It returns the reference value in the reference item1, which denotes the first item in a list of three HeavyItems. Because of the list structure, none of the HeavyItems in the list are eligible for garbage collection on return from the method. Again, the fate of the objects in the list is decided by the code that calls this method. It is enough for the first item in the list to become unreachable, in order for all objects in the list to become eligible for garbage collection (barring any reachable references).

Example 9.1 Garbage Collection Eligibility

class HeavyItem {                                                      // (1)
  int[]     itemBody;
  String    itemID;
  HeavyItem nextItem;

  HeavyItem(String ID, HeavyItem itemRef) {        // (2)
    itemBody = new int[100000];
    itemID   = ID;
    nextItem = itemRef;
  }
  protected void finalize() throws Throwable {      // (3)
    System.out.println(itemID + ": recycled.");
    super.finalize();
  }
}
//____________________________________________________
public class RecyclingBin {

  public static HeavyItem createHeavyItem(String itemID) {         // (4)
    HeavyItem itemA = new HeavyItem(itemID + " local item", null); // (5)
    itemA = new HeavyItem(itemID, null);                           // (6)
    System.out.println("Return from creating HeavyItem " + itemID);
    return itemA;                                                  // (7)
  }

  public static HeavyItem createList(String listID) {              // (8)
    HeavyItem item3 = new HeavyItem(listID + ": item3", null);     // (9)
    HeavyItem item2 = new HeavyItem(listID + ": item2", item3);    // (10)
    HeavyItem item1 = new HeavyItem(listID + ": item1", item2);    // (11)
    System.out.println("Return from creating list " + listID);
    return item1;                                                  // (12)
  }

  public static void main(String[] args) {                         // (13)
    HeavyItem list = createList("X");                              // (14)
    list = createList("Y");                                        // (15)

    HeavyItem itemOne = createHeavyItem("One");                    // (16)
    HeavyItem itemTwo = createHeavyItem("Two");                    // (17)
    itemOne = null;                                                // (18)
    createHeavyItem("Three");                                           // (19)
    createHeavyItem("Four");                                                        //(20)
    System.out.println("Return from main().");
  }
}

Possible output from the program:

    Return from creating list X
    Return from creating list Y
    X: item3: recycled.
    X: item2: recycled.
    X: item1: recycled.
    Return from creating HeavyItem One
    Return from creating HeavyItem Two
    Return from creating HeavyItem Three
    Three local item: recycled.
    Three: recycled.
    Two local item: recycled.
    Return from creating HeavyItem Four
    One local item: recycled.
    One: recycled.
    Return from main().

In Example 9.1, the main() method at (13) in the class RecyclingBin uses the methods createHeavyItem() and createList(). It creates a list X at (14), but the reference to its first item is reassigned at (15), making objects in list X eligible for garbage collection after (15). The first item of list Y is stored in the reference list, making this list noneligible for garbage collection during the execution of the main() method.

The main() method creates two items at (16) and (17), storing their reference values in the references itemOne and itemTwo, respectively. The reference itemOne is nulled at (18), making HeavyItem with identity One eligible for garbage collection. The two calls to the createHeavyItem() method at (19) and (20) return reference values to HeavyItems, which are not stored, making each object eligible for garbage collection right after the respective method call returns.

The output from the program bears out the observations made above. Objects in list Y (accessible through the reference list) and HeavyItem with identity Two (accessible through the reference itemTwo) remain non-eligible while the main() method executes. Although the output shows that HeavyItems with identity Four was never garbage collected, it is not accessible once it becomes eligible for garbage collection at (20). Any objects in the heap after the program terminates are reclaimed by the operating system.

9.4 Object Finalization

Object finalization provides an object with a last resort to undertake any action before its storage is reclaimed. The automatic garbage collector calls the finalize() method in an object that is eligible for garbage collection before actually destroying the object. The finalize() method is defined in the Object class.

   protected void finalize() throws Throwable

An implementation of the finalize() method is called a finalizer. A subclass can override the finalizer from the Object class in order to take more specific and appropriate action before an object of the subclass is destroyed. Note that the overridden method cannot narrow the visibility of the method and it must be declared either protected or public.

A finalizer can, like any other method, catch and throw exceptions (see Section 6.7, p. 245). However, any exception thrown but not caught by a finalizer that is called by the garbage collector is ignored, and the finalization of this object is terminated. The finalizer is only called once on an object, regardless of whether any exception is thrown during its execution. In case of finalization failure, the object still remains eligible for disposal at the discretion of the garbage collector (unless it has been resurrected, as explained in the next subsection). Since there is no guarantee that the garbage collector will ever run, there is also no guarantee that the finalizer will ever be called.

In the following code, the finalizer at (1) will take appropriate action if and when called on objects of the class before they are garbage collected, ensuring that the resource is freed. Since it is not guaranteed that the finalizer will ever be called at all, a program should not rely on the finalization to do any critical operations.

public class AnotherWellBehavedClass {
  SomeResource objRef;
  // ...
  protected void finalize() throws Throwable {         // (1)
    try {                                                                             // (2)
      if (objRef != null) objRef.close();
    } finally {                                                                    // (3)
      super.finalize();                                                        // (4)
    }
  }
}

Note that an enum type cannot declare a finalizer. Therefore, an enum constant may never be finalized.

9.5 Finalizer Chaining

Unlike subclass constructors, overridden finalizers are not implicitly chained (see Section 7.5, p. 302). Therefore, the finalizer in a subclass should explicitly call the finalizer in its superclass as its last action, as shown at (4) in the previous code. The call to the finalizer of the superclass is in a finally block at (3), guaranteed to be executed regardless of any exceptions thrown by the code in the try block at (2).

Example 9.2 illustrates chaining of finalizers. It creates a user-specified number of large objects of a user-specified size. The number and size are provided through command-line program arguments. The loop at (7) in the main() method creates Blob objects, but does not store any references to them. Objects created are instances of the class Blob defined at (3). The Blob constructor at (4) initializes the field size by constructing a large array of integers. The Blob class extends the BasicBlob class that assigns each blob a unique number (blobId) and keeps track of the number of blobs (population) not yet garbage collected. Creation of each Blob object by the constructor at (4) prints the ID number of the object and the message "Hello". The finalize() method at (5) is called before a Blob object is garbage collected. It prints the message "Bye" and calls the finalize() method in the class BasicBlob at (2), which decrements the population count. The program output shows that two blobs were not garbage collected at the time the print statement at (8) was executed. It is evident from the number of "Bye" messages that three blobs were garbage collected before all the five blobs had been created in the loop at (7).

Example 9.2 Using Finalizers

class BasicBlob {                                                                                // (1)
  static    int idCounter;
  static    int population;
  protected int blobId;

  BasicBlob() {
    blobId = idCounter++;
    ++population;
  }
  protected void finalize() throws Throwable {                               // (2)
    --population;
    super.finalize();
  }
}
//____________________________________________________
class Blob extends BasicBlob {                                                         // (3)
  int[] size;

  Blob(int bloatedness) {                                                                    // (4)
    size = new int[bloatedness];
    System.out.println(blobId + ": Hello");
  }

  protected void finalize() throws Throwable {                               // (5)
    System.out.println(blobId + ": Bye");
    super.finalize();
  }
}
//____________________________________________________
public class Finalizers {
  public static void main(String[] args) {                                         // (6)
    int blobsRequired, blobSize;
    try {
      blobsRequired = Integer.parseInt(args[0]);
      blobSize = Integer.parseInt(args[1]);
    } catch(IndexOutOfBoundsException e) {
      System.err.println("Usage: Finalizers <number of blobs> <blob size>");
      return;
    }
    for (int i=0; i<blobsRequired; ++i) {                                            // (7)
      new Blob(blobSize);
    }
    System.out.println(BasicBlob.population + " blobs alive");  // (8)
  }
}

Running the program with the command

>java Finalizers 5 500000

resulted in the following output:

0: Hello
1: Hello
2: Hello
0: Bye
1: Bye
2: Bye
3: Hello
4: Hello
2 blobs alive

9.6 Invoking Garbage Collection Programmatically

Although Java provides facilities to invoke the garbage collection explicitly, there are no guarantees that it will be run. The program can only request that garbage collection be performed, but there is no way that garbage collection can be forced.

The System.gc() method can be used to request garbage collection, and the System.runFinalization() method can be called to suggest that any pending finalizers be run for objects eligible for garbage collection.

static void gc()

Requests that garbage collection be run.

static void runFinalization()

Requests that any pending finalizers be run for objects eligible for garbage collection.

Alternatively, corresponding methods in the Runtime class can be used. A Java application has a unique Runtime object that can be used by the application to interact with the JVM. An application can obtain this object by calling the method Runtime.getRuntime(). The Runtime class provides various methods related to memory issues.

static Runtime getRuntime()

Returns the Runtime object associated with the current application.

void gc()

Requests that garbage collection be run. However, it is recommended to use the more convenient static method System.gc().

void runFinalization()

Requests that any pending finalizers be run for objects eligible for garbage collection. Again, it is more convenient to use the static method System.runFinalization().

long freeMemory()

Returns the amount of free memory (bytes) in the JVM that is available for new objects.

long totalMemory()

Returns the total amount of memory (bytes) available in the JVM. This includes both memory occupied by current objects and that which is available for new objects.

Example 9.3 illustrates invoking garbage collection. The class MemoryCheck is an adaptation of the class Finalizers from Example 9.2. The RunTime object for the application is obtained at (7). This object is used to get information regarding total memory and free memory in the JVM at (8) and (9), respectively. Blobs are created in the loop at (10). The amount of free memory after blob creation is printed at (11). We see from the program output that some blobs were already garbage collected before the execution got to (11). A request for garbage collection is made at (12). Checking free memory after the request shows that more memory has become available, indicating that the request was honored. It is instructive to run the program without the method call System.gc() at (12), in order to compare the results.

Example 9.3 Invoking Garbage Collection

class BasicBlob {  /* See Example 9.2. */ }
class Blob extends BasicBlob { /* See Example 9.2.*/ }
//____________________________________________________
public class MemoryCheck {
  public static void main(String[] args) {                                                             // (6)
    int blobsRequired, blobSize;
    try {
      blobsRequired = Integer.parseInt(args[0]);
      blobSize = Integer.parseInt(args[1]);
    } catch(IndexOutOfBoundsException e) {
       System.err.println("Usage: MemoryCheck <number of blobs> <blob size>");
       return;
    }
    Runtime environment = Runtime.getRuntime();                                           // (7)
    System.out.println("Total memory: " + environment.totalMemory());     // (8)
    System.out.println("Free memory before blob creation: " +
                       environment.freeMemory());                                                        // (9)
    for (int i=0; i<blobsRequired; ++i) {                                                             // (10)
      new Blob(blobSize);
    }
    System.out.println("Free memory after blob creation: " +
                       environment.freeMemory());                                                      // (11)
    System.gc();                                                                                                     // (12)
    System.out.println("Free memory after requesting GC: " +
                       environment.freeMemory());                                                      // (13)
    System.out.println(BasicBlob.population + " blobs alive");                    // (14)
  }
}

Running the program with the command

>java MemoryCheck 5 100000

resulted in the following output:

Total memory: 2031616
Free memory before blob creation: 1773192
0: Hello
1: Hello
2: Hello
1: Bye
2: Bye
3: Hello
0: Bye
3: Bye
4: Hello
Free memory after blob creation: 818760
4: Bye
Free memory after requesting GC: 1619656
0 blobs alive

Certain aspects regarding automatic garbage collection should be noted:

• There are no guarantees that objects that are eligible for garbage collection will have their finalizers executed. Garbage collection might not even be run if the program execution does not warrant it. Thus, any memory allocated during program execution might remain allocated after program termination, but will be reclaimed by the operating system.

• There are also no guarantees about the order in which the objects will be garbage collected, or the order in which their finalizers will be executed. Therefore, the program should not make any assumptions based on these aspects.

• Garbage collection does not guarantee that there is enough memory for the program to run. A program can rely on the garbage collector to run when memory gets very low and it can expect an OutOfMemoryException to be thrown if its memory demands cannot be met.

Review Questions

Review Questions

9.1 Which statement is true?

Select the one correct answer.

(a) Objects can be explicitly destroyed using the keyword delete.

(b) An object will be garbage collected immediately after it becomes unreachable.

(c) If object obj1 is accessible from object obj2, and object obj2 is accessible from obj1, then obj1 and obj2 are not eligible for garbage collection.

(d) Once an object has become eligible for garbage collection, it will remain eligible until it is destroyed.

(e) If object obj1 can access object obj2 that is eligible for garbage collection, then obj1 is also eligible for garbage collection.

9.2 Identify the location in the following program where the object, initially referenced with arg1, is eligible for garbage collection.

public class MyClass {
  public static void main(String[] args) {
    String msg;
    String pre = "This program was called with ";
    String post = " as first argument.";
    String arg1 = new String((args.length > 0) ? "'" + args[0] + "'" :
                              "<no argument>");
    msg = arg1;
    arg1 = null;             // (1)
    msg = pre + msg + post;  // (2)
    pre = null;              // (3)
    System.out.println(msg);
    msg = null;              // (4)
    post = null;             // (5)
    args = null;             // (6)
  }
}

Select the one correct answer.

(a) After the line labeled (1).

(b) After the line labeled (2).

(c) After the line labeled (3).

(d) After the line labeled (4).

(e) After the line labeled (5).

(f) After the line labeled (6).

9.3 How many objects are eligible for garbage collection when control reaches (1)?

public class Eligible {
  public static void main(String[] args) {
    for (int i = 0; i < 5; i++) {
      Eligible obj = new Eligible();
      new Eligible();
    }
    System.gc(); // (1);
  }
}

Select the one correct answer.

(a) 0.

(b) 5.

(c) 10.

(d) Hard to say.

9.4 How many objects are eligible for garbage collection when control reaches (1)?

public class Link {
  private Link next;
  Link(Link next) { this.next = next; }
  public void finialize() { System.out.print("X"); }

  public static void main(String[] args) {
    Link p = null;
    for (int i = 0; i < 5; i++) {
      p = new Link(p);
    }
    System.gc();                          // (1);
  }
}

Select the one correct answer

(a) 0

(b) 5

(c) 10

(d) Hard to say.

9.5 How many objects are eligible for garbage collection when control reaches (1)?

public class Elements {
  public static void main(String[] args) {
    int[] array = new int[4];
    for (int i = 0; i < 4; i++) {
      array[i] = i;
    }
    array[0] = array[1] = array[2] = array[3] = 0;
    System.gc();                          // (1);
  }
}

Select the one correct answer

(a) 0

(b) 1

(c) 4

(d) Hard to say.

9.6 How many objects are reachable when control reaches (1)?

public class Nullify {

  private static void nullify(Integer[] array) { array = null; }

  public static void main(String[] args) {
    Integer[] array = new Integer[4];
    for (int i = 0; i < 4; i++) {
      array[i] = i;
    }
    nullify(array);
    System.gc();                         // (1);
  }
}

Select the one correct answer

(a) 0

(b) 1

(c) 4

(d) 5

(e) Hard to say.

9.7 Which statement is true?

Select the one correct answer

(a) If an exception is thrown during the execution of the finalize() method of an eligible object, the exception is ignored and the object is destroyed.

(b) All objects have a finalize() method.

(c) Objects can be destroyed by explicitly calling the finalize() method.

(d) The finalize() method can be declared with any accessibility.

(e) The compiler will fail to compile code that defines an overriding finalize() method that does not explicitly call the overridden finalize() method from the superclass.

9.8 Which statement is true?

Select the one correct answer

(a) The compiler will fail to compile code that explicitly tries to call the finalize() method.

(b) The finalize() method must be declared with protected accessibility.

(c) An overriding finalize() method in any class can always throw checked exceptions.

(d) The finalize() method can be overloaded.

(e) The body of the finalize() method can only access other objects that are eligible for garbage collection.

9.9 Which statement describes guaranteed behavior of the garbage collection and finalization mechanisms?

Select the one correct answer

(a) Objects will not be destroyed until they have no references to them.

(b) The finalize() method will never be called more than once on an object.

(c) An object eligible for garbage collection will eventually be destroyed by the garbage collector.

(d) If object A became eligible for garbage collection before object B, then object A will be destroyed before object B.

(e) An object, once eligible for garbage collection, can never become accessible by a live thread.

9.10 Which method headers will result in a correct implementation of a finalizer for the following class?

public class Curtain {
  // (1) INSERT METHOD HEADER HERE ...
  {
    System.out.println("Final curtain");
    super.finalize();
  }
}

Select the two correct answers.

(a) void finalize() throws Throwable

(b) void finalize() throws Exception

(c) void finalize()

(d) protected void finalize() throws Throwable

(e) protected void finalize() throws Exception

(f) protected void finalize()

(g) public void finalize() throws Throwable

(h) public void finalize() throws Exception

(i) public void finalize()

(j) private void finalize() throws Throwable

(k) private void finalize() throws Exception

(l) private void finalize()

9.11 Which scenario cannot definitely be the result of compiling and running the following program?

public class Grade {
  private char grade;
  Grade(char grade) { this.grade = grade; }

  public void finalize() throws Throwable {
    System.out.print(grade);
    super.finalize();
  }
  public static void main(String[] args) {
    new Grade('A'), new Grade('F'),
    System.gc();
  }
}

Select the one correct answer

(a) The program may print AF.

(b) The program may print FA.

(c) The program may print A.

(d) The program may print F.

(e) The program may print AFA.

(f) The program may not print anything.

9.12 Which scenario can be the result of compiling and running the following program?

public class MyString {
  private String str;
  MyString(String str) { this.str = str; }

  public void finalize() throws Throwable {
    System.out.print(str);
    super.finalize();
  }

  public void concat(String str2) {
    this.str.concat(str2);
  }

  public static void main(String[] args) {
    new MyString("A").concat("B");
    System.gc();
  }
}

Select the two correct answers

(a) The program may print AB.

(b) The program may print BA.

(c) The program may print A.

(d) The program may print B.

(e) The program may not print anything.

9.7 Initializers

Initializers can be used to set initial values for fields in objects and classes. There are three different types of initializers:

field initializer expressions

• static initializer blocks

instance initializer blocks

The rest of this section provides details on these initializers, concluding with a discussion on the procedure involved in constructing the state of an object when the object is created by using the new operator.

9.8 Field Initializer Expressions

Initialization of fields can be specified in field declaration statements using initializer expressions. The value of the initializer expression must be assignment compatible to the declared field (see Section 5.5, p. 169 and Section 7.9, p. 320). We distinguish between static and non-static field initializers.

class ConstantInitializers {
  int minAge = 12;                    // (1) Non-static
  static double pensionPoints = 10.5; // (2) Static
  // ...
}

The fields of an object are initialized with the values of initializer expressions when the object is created by using the new operator. In the previous example, the declaration at (1) will result in the field minAge being initialized to 12 in every object of the class ConstantInitializers created with the new operator. If no explicit initializer expressions are specified, default values (see Section 2.4, p. 33) are assigned to the fields.

When a class is loaded, it is initialized, i.e., its static fields are initialized with the values of the initializer expressions. The declaration at (2) will result in the static field pensionPoints being initialized to 10.5 when the class is loaded by the JVM. Again, if no explicit initializers are specified, default values are assigned to the static fields.

An initializer expression for a static field cannot refer to non-static members by their simple names. The keywords this and super cannot occur in a static initializer expression.

Since a class is always initialized before it can be instantiated, an instance initializer expression can always refer to any static member of a class, regardless of the member declaration order. In the following code, the instance initializer expression at (1) refers to the static field NO_OF_WEEKS declared and initialized at (2). Such a forward reference is legal. More examples of forward references are given in the next subsection.

class MoreInitializers {
         int noOfDays = 7 * NO_OF_WEEKS;    // (1) Non-static
  static int NO_OF_WEEKS = 52;              // (2) Static
  // ...
}

Initializer expressions can also be used to define constants in interfaces (see Section 7.6, p. 309). Such initializer expressions are implicitly static, as they define values of final static fields.

Initializer expressions are also used to initialize local variables (see Section 2.3, p. 31). A local variable is initialized with the value of the initializer expression every time the local variable declaration is executed.

Forward References and Declaration Order of Initializer Expressions

When an object is created using the new operator, instance initializer expressions are executed in the order in which the instance fields are declared in the class.

Java requires that the declaration of a field must occur before its usage in any initializer expression if the field is used on the right-hand side of an assignment in the initializer expression. This essentially means that the declaration of a field must occur before the value of the field is read in an initializer expression. Using the field on the left-hand side of an assignment in the initializer expression does not violate the declaration-before-reading rule, as this constitutes a write operation. This rule applies when the usage of the field is by its simple name.

There is one caveat to the declaration-before-reading rule: it does not apply if the initializer expression defines an anonymous class, as the usage then occurs in a different class which has its own accessibility rules in the enclosing context. Restrictions outlined earlier help to detect initialization anomalies at compile time.

In the next example, the initialization at (2) generates a compile-time error, because the field width in the initializer expression violates the declaration-before-reading rule. The usage of the field width in the initializer expression at (2) does not occur on the left-hand side of the assignment. This is an illegal forward reference. To remedy the situation, the declaration of the field width at (4) can be moved in front of the declaration at (2). In any case, we can use the keyword this as shown at (3), but this will read the default value 0 in the field width.

class NonStaticInitializers {
  int length  = 10;                   // (1)
//double area = length * width;       // (2) Not Ok. Illegal forward reference.
  double area = length * this.width;  // (3) Ok, but width has default value 0.
  int width   = 10;                   // (4)

  int sqSide = height = 20;           // (5) OK. Legal forward reference.
  int height;                         // (6)
}

The forward reference at (5) is legal. The usage of the field height in the initializer expression at (5) occurs on the left-hand side of the assignment. The initializer expression at (5) is evaluated as (sqSide = (height = 20)). Every object of the class NonStaticInitializers will have the field height set to the value 20.

The declaration-before-reading rule is equally applicable to static initializer expressions when static fields are referenced by their simple names.

Example 9.4 shows why the order of field initializer expressions can be important. The initializer expressions in Example 9.4 are calls to methods defined in the class. Methods are not subject to the same access rules as initializer expressions. The call at (2) to the method initMaxGuests() defined at (4) is expected to return the maximum number of guests. However, the field occupancyPerRoom at (3) will not have been explicitly initialized; therefore, its default value 0 will be used in the method initMaxGuests(), which will return an incorrect value. The program output shows that after object creation, the occupancy per room is correct, but the maximum number of guests is wrong.

Example 9.4 Initializer Expression Order and Method Calls

class Hotel {
  private int noOfRooms        = 12;                                 // (1)
  private int maxNoOfGuests    = initMaxGuests();                    // (2) Bug
  private int occupancyPerRoom = 2;                                  // (3)

  public int initMaxGuests() {                                       // (4)
    System.out.println("occupancyPerRoom: " + occupancyPerRoom);
    System.out.println("maxNoOfGuests:    " + noOfRooms * occupancyPerRoom);
    return noOfRooms * occupancyPerRoom;
  }

  public int getMaxGuests() { return maxNoOfGuests; }                // (5)

  public int getOccupancy() { return occupancyPerRoom; }             // (6)
}
//____________________________________________________
public class TestOrder {
  public static void main(String[] args) {
    Hotel hotel = new Hotel();                                       // (7)
    System.out.println("After object creation: ");
    System.out.println("occupancyPerRoom: " + hotel.getOccupancy()); // (8)
    System.out.println("maxNoOfGuests:    " + hotel.getMaxGuests()); // (9)
  }
}

Output from the program:

occupancyPerRoom: 0
maxNoOfGuests:    0
After object creation:
occupancyPerRoom: 2
maxNoOfGuests:    0

Exception Handling and Initializer Expressions

Initializer expressions in named classes and interfaces must not result in any uncaught checked exception (see Section 6.6, p. 243). If any checked exception is thrown during execution of an initializer expression, it must be caught and handled by code called from the initializer expression. This restriction does not apply to instance initializer expressions in anonymous classes.

Example 9.5 illustrates exception handling for initializer expressions in named classes. The static initializer expression at (3) calls the static method createHotel-Pool() at (4), which catches and handles the checked TooManyHotelsException defined at (2). If the method createHotelPool() were to use the throws clause to specify the checked exception, instead of catching and handling it within a try-catch block, the initializer expression at (3), which called the method, would have to handle the exception. However, the initializer expression cannot specify any exception handling, as the compiler would complain.

The instance initializer expression at (5) calls the method initMaxGuests() at (6), which can throw the unchecked RoomOccupancyTooHighException. If thrown, this exception will be caught and handled in the main() method. Program output confirms that an unchecked RoomOccupancyTooHighException was thrown during program execution.

Example 9.5 Exceptions in Initializer Expressions

class RoomOccupancyTooHighException
      extends RuntimeException {}                     // (1) Unchecked Exception
class TooManyHotelsException
      extends Exception {}                            // (2) Checked Exception
//_______________________________________________________
class Hotel {
  // Static Members
  private static int noOfHotels = 12;
  private static Hotel[] hotelPool = createHotelPool();   // (3)

  private static Hotel[] createHotelPool() {              // (4)
    try {
      if (noOfHotels > 10)
        throw new TooManyHotelsException();
    } catch (TooManyHotelsException e) {
      noOfHotels = 10;
      System.out.println("No. of hotels adjusted to " + noOfHotels);
    }
    return new Hotel[noOfHotels];
  }
  // Instance Members
  private int noOfRooms        = 215;
  private int occupancyPerRoom = 5;
  private int maxNoOfGuests    = initMaxGuests();         // (5)

  private int initMaxGuests() {                           // (6)
    if (occupancyPerRoom > 4)
      throw new RoomOccupancyTooHighException();
    return noOfRooms * occupancyPerRoom;
  }
}
//_______________________________________________________
public class ExceptionsInInitializers {
  public static void main(String[] args) {
    try { new Hotel(); }
    catch (RoomOccupancyTooHighException exception) {
      exception.printStackTrace();
    }
  }
}

Output from the program:

No. of hotels adjusted to 10
RoomOccupancyTooHighException
      at Hotel.initMaxGuests(ExceptionsInInitializers.java:28)
      at Hotel.<init>(ExceptionsInInitializers.java:24)
      at ExceptionsInInitializers.main(ExceptionsInInitializers.java:35)

9.9 Static Initializer Blocks

Java allows static initializer blocks to be defined in a class. Although such blocks can include arbitrary code, they are primarily used for initializing static fields. The code in a static initializer block is executed only once, when the class is initialized.

The syntax of a static initializer block comprises the keyword static followed by a local block that can contain arbitrary code, as shown at (3).

class StaticInitializers {
  final static int ROWS = 12, COLUMNS = 10;         // (1)
  static long[][] matrix = new long[ROWS][COLUMNS]; // (2)
  // ...
  static {                                          // (3) Static Initializer
    for (int i = 0; i < matrix.length; i++)
      for (int j = 0; j < matrix[i].length; j++)
        matrix[i][j] = 2*i + j;
  }
  // ...
}

When the class StaticInitializers is first loaded in the previous example, the final static fields at (1) are initialized. Then the array of arrays matrix of specified size is created at (2), followed by the execution of the static block at (3).

If a class relies on native method implementations, a static initializer can be used to load any external libraries that the class needs (see Section 4.10, p. 151).

Note that the static initializer block is not contained in any method. A class can have more than one static initializer block. Initializer blocks are not members of a class nor can they have a return statement because they cannot be called directly.

When a class is initialized, the initializer expressions in static field declarations and static initializer blocks are executed in the order they are specified in the class. In the previous example, the initializer expressions at (1) and (2) are executed before the static initializer block at (3).

Similar restrictions apply to static initializer blocks as for static initializer expressions: the keywords this and super cannot occur in a static initializer block because such a block defines a static context.

Forward References and Declaration Order of Static Initializers

When making forward references using simple names, code in a static initializer block is also subject to the declaration-before-reading rule discussed in the previous subsection. Example 9.6 illustrates forward references and the order of execution for static initializer expressions and static initializer blocks. An illegal forward reference occurs at (4), where an attempt is made to read the value of the field sf1 before its declaration. At (11) the read operation is after the declaration and, therefore, allowed. Forward reference made on the left-hand side of the assignment is always allowed, as shown at (2), (5), and (7). The initializers are executed in their declaration order. A static field has the value it was last assigned in an initializer. If there is no explicit assignment, the field has the default value of its type.

Example 9.6 Static Initializers and Forward References

// Demonstrates forward references.
public class StaticForwardReferences {

  static {                // (1) Static initializer block
    sf1 = 10;             // (2) OK. Assignment to sf1 allowed
    //  sf1 = if1;        // (3) Not OK. Non-static field access in static context
    //  int a = 2 * sf1;  // (4) Not OK. Read operation before declaration
    int b = sf1 = 20;     // (5) OK. Assignment to sf1 allowed
    int c = StaticForwardReferences.sf1;// (6) OK. Not accessed by simple name
  }

  static int sf1 = sf2 = 30;  // (7) Static field. Assignment to sf2 allowed
  static int sf2;             // (8) Static field
  int if1 = 5;                // (9) Non-static field

  static {                    // (10) Static initializer block
    int d = 2 * sf1;          // (11) OK. Read operation after declaration
    int e = sf1 = 50;         // (12)
  }

  public static void main(String[] args) {
    System.out.println("sf1: " + StaticForwardReferences.sf1);
    System.out.println("sf2: " + StaticForwardReferences.sf2);
  }
}

Output from the program:

sf1: 50
sf2: 30

Exception Handling and Static Initializer Blocks

Exception handling in static initializer blocks is no different from that in static initializer expressions: uncaught checked exceptions cannot be thrown. A static initializer block cannot be called directly, therefore, any checked exceptions must be caught and handled in the body of the static initializer block. Example 9.7 shows a static initializer block at (3) that catches and handles a checked exception in the try-catch block at (4).

Example 9.7 also shows a static initializer block at (5) that throws an unchecked exception at (6) during class initialization. As the program output shows, this exception is handled by the default exception handler, resulting in termination of the program.

Example 9.7 Static Initializer Blocks and Exceptions

class BankrupcyException
      extends RuntimeException {}            // (1) Unchecked Exception
class TooManyHotelsException
      extends Exception {}                   // (2) Checked Exception

class Hotel {
  // Static Members
  private static boolean bankrupt = true;
  private static int noOfHotels = 11;
  private static Hotel[] hotelPool;

  static {                                   // (3) Static block
    try {                                    // (4) Handles checked exception
      if (noOfHotels > 10)
        throw new TooManyHotelsException();
    } catch (TooManyHotelsException e) {
      noOfHotels = 10;
      System.out.println("No. of hotels adjusted to " + noOfHotels);
    }
    hotelPool = new Hotel[noOfHotels];
  }

  static {                                    // (5) Static block
    if (bankrupt)
      throw new BankrupcyException();         // (6) Throws unchecked exception
  }
  // ...
}

public class ExceptionInStaticInitBlocks {
  public static void main(String[] args) {
    new Hotel();
  }
}

Output from the program:

No. of hotels adjusted to 10
Exception in thread "main" java.lang.ExceptionInInitializerError
        at ExceptionInStaticInitBlocks.main(ExceptionInStaticInitBlocks.java:32)
Caused by: BankrupcyException
        at Hotel.<clinit>(ExceptionInStaticInitBlocks.java:25)

9.10 Instance Initializer Blocks

Just as static initializer blocks can be used to initialize static fields in a named class, Java provides the ability to initialize fields during object creation using instance initializer blocks. In this respect, such blocks serve the same purpose as constructors during object creation. The syntax of an instance initializer block is the same as that of a local block, as shown at (2) in the following code. The code in the local block is executed every time an instance of the class is created.

class InstanceInitializers {

  long[] squares = new long[10];    // (1)
  // ...
  {                                 // (2) Instance Initializer
    for (int i = 0; i < squares.length; i++)
      squares[i] = i*i;
  }
  // ...
}

The array squares of specified size is created first at (1), followed by the execution of the instance initializer block at (2) every time an instance of the class InstanceInitializers is created. Note that the instance initializer block is not contained in any method. A class can have more than one instance initializer block, and these (and any instance initializer expressions in instance field declarations) are executed in the order they are specified in the class.

Forward References and Declaration Order of Instance Initializers

Analogous to other initializers discussed so far, an instance initializer block cannot make a forward reference to a field that violates the declaration-before-reading rule. In Example 9.8, an illegal forward reference occurs in the code at (4), which attempts to read the value of the field nsf1 before it is declared. The read operation at (11) is after the declaration and is, therefore, allowed. Forward reference made on the left-hand side of the assignment is always allowed, as shown at (2), (3), (5), and (7).

Example 9.8 Instance Initializers and Forward References

class NonStaticForwardReferences {
  {                        // (1) Instance initializer block
    nsf1 = 10;             // (2) OK. Assignment to nsf1 allowed
    nsf1 = sf1;            // (3) OK. Static field access in non-static context
    //  int a = 2 * nsf1;  // (4) Not OK. Read operation before declaration
    int b = nsf1 = 20;     // (5) OK. Assignment to nsf1 allowed
    int c = this.nsf1;     // (6) OK. Not accessed by simple name
  }

  int nsf1 = nsf2 = 30;    // (7) Non-static field. Assignment to nsf2 allowed
  int nsf2;                // (8) Non-static field
  static int sf1 = 5;      // (9) Static field

  {                        // (10) Instance initializer block
    int d = 2 * nsf1;      // (11) OK. Read operation after declaration
    int e = nsf1 = 50;     // (12)
  }

  public static void main(String[] args) {
    NonStaticForwardReferences objRef = new NonStaticForwardReferences();
    System.out.println("nsf1: " + objRef.nsf1);
    System.out.println("nsf2: " + objRef.nsf2);
  }
}

Output from the program:

nsf1: 50
nsf2: 30

As in a instance initializer expression, the keywords this and super can be used to refer to the current object in an instance initializer block. As in a static initializer block, the return statement is also not allowed in instance initializer blocks.

An instance initializer block can be used to factor out common initialization code that will be executed regardless of which constructor is invoked. A typical usage of an instance initializer block is in anonymous classes (see Section 8.5, p. 377), which cannot declare constructors, but can instead use instance initializer blocks to initialize fields. In Example 9.9, the anonymous class defined at (1) uses an instance initializer block defined at (2) to initialize its fields.

Example 9.9 Instance Initializer Block in Anonymous Class

class Base {
  protected int a;
  protected int b;
  void print() { System.out.println("a: " + a); }
}

//____________________________________________________
class AnonymousClassMaker {
  Base createAnonymous() {
    return new Base() {           // (1) Anonymous class
      {                           // (2) Instance initializer
        a = 5; b = 10;
      }
      void print() {
        super.print();
        System.out.println("b: " + b);
      }
    };  // end anonymous class
  }
}
//____________________________________________________
public class InstanceInitBlock {
  public static void main(String[] args) {
    new AnonymousClassMaker().createAnonymous().print();
  }
}

Output from the program:

a: 5
b: 10

Exception Handling and Instance Initializer Blocks

Exception handling in instance initializer blocks is similar to that in static initializer blocks. Example 9.10 shows an instance initializer block at (3) that catches and handles a checked exception in the try-catch block at (4). Another instance initializer block at (5) throws an unchecked exception at (6). The runtime system handles the exception, printing the stack trace and terminating the program.

Exception handling in instance initializer blocks differs from that in static initializer blocks in the following aspect: the execution of an instance initializer block can result in an uncaught checked exception, provided the exception is declared in the throws clause of every constructor in the class. Static initializer blocks cannot allow this, since no constructors are involved in class initialization. Instance initializer blocks in anonymous classes have even greater freedom: they can throw any exception.

Example 9.10 Exception Handling in Instance Initializer Blocks

class RoomOccupancyTooHighException
      extends Exception {}                    // (1) Checked exception
class BankrupcyException
      extends RuntimeException {}             // (2) Unchecked exception
//____________________________________________________

class Hotel {
  // Instance Members
  private boolean bankrupt         = true;
  private int     noOfRooms        = 215;
  private int     occupancyPerRoom = 5;
  private int     maxNoOfGuests;

  {                                           // (3) Instance initializer block
    try {                                     // (4) Handles checked exception
      if (occupancyPerRoom > 4)
        throw new RoomOccupancyTooHighException();
    } catch (RoomOccupancyTooHighException exception) {
      System.out.println("ROOM OCCUPANCY TOO HIGH: " + occupancyPerRoom);
      occupancyPerRoom = 4;
    }
    maxNoOfGuests = noOfRooms * occupancyPerRoom;
  }

  {                                           // (5) Instance initializer block
    if (bankrupt)
      throw new BankrupcyException();         // (6) Throws unchecked exception
  }    // ...
}
//____________________________________________________
public class ExceptionsInInstBlocks {
  public static void main(String[] args) {
    new Hotel();
  }
}

Output from the program:

ROOM OCCUPANCY TOO HIGH: 5
Exception in thread "main" BankrupcyException
        at Hotel.<init>(ExceptionsInInstBlocks.java:26)
        at ExceptionsInInstBlocks.main(ExceptionsInInstBlocks.java:32)

9.11 Constructing Initial Object State

Object initialization involves constructing the initial state of an object when it is created by using the new operator. First, the fields are initialized to their default values (see Section 2.4, p. 33)—whether they are subsequently given non-default initial values or not—then the constructor is invoked. This can lead to local chaining of constructors. The invocation of the constructor at the end of the local chain of constructor invocations results in the following actions, before the constructor’s execution resumes:

• Implicit or explicit invocation of the superclass constructor. Constructor chaining ensures that the inherited state of the object is constructed first (see Section 7.5, p. 302).

Initialization of the instance fields by executing their instance initializer expressions and any instance initializer blocks, in the order they are specified in the class declaration.

Example 9.11 illustrates object initialization. The new operator is used at (8) to create an object of class SubclassB. The default constructor SubclassB() at (2) uses the this() construct to locally chain to the non-default constructor at (3). It is this constructor that leads to an implicit call of the superclass constructor. As can be seen from the program output, the execution of the superclass’s constructor at (1) reaches completion first. This is followed by the execution of the instance initializer block at (4) and instance initializer expression at (6). Then the execution of the body of the non-default constructor at (3) is resumed. Finally, the default constructor completes its execution, thereby completing the construction of the object state.

Note that the instance initializers are executed in the order they are specified in the class declaration. The forward reference to the field value at (5) is legal because the usage of the field value is on the left-hand side of the assignment (it does not violate the declaration-before-reading rule. The default value of the field value is overwritten by the instance initializer block at (5). The field value is again overwritten by the instance initializer expression at (6), and finally by the non-default constructor at (3).

Example 9.11 Object State Construction

class SuperclassA {
  public SuperclassA() {                        // (1)
    System.out.println("Constructor in SuperclassA");
  }
}
//____________________________________________________
class SubclassB extends SuperclassA {

  SubclassB() {                                 // (2) Default constructor
    this(3);
    System.out.println("Default constructor in SubclassB");
  }

  SubclassB(int i) {                            // (3) Non-default constructor
    System.out.println("Non-default constructor in SubclassB");
    value = i;
  }

  {                                             // (4) Instance initializer block
    System.out.println("Instance initializer block in SubclassB");
    value = 2;                                  // (5)
  }

  int value = initializerExpression();          // (6)

  private int initializerExpression() {         // (7)
    System.out.println("Instance initializer expression in SubclassB");

    return 1;
  }
}
//____________________________________________________
public class ObjectConstruction {
  public static void main(String[] args) {
    SubclassB objRef = new SubclassB();         // (8)
    System.out.println("value: " + objRef.value);
  }
}

Output from the program:

Constructor in SuperclassA
Instance initializer block in SubclassB
Instance initializer expression in SubclassB
Non-default constructor in SubclassB
Default constructor in SubclassB
value: 3

Some care should be exercised when writing constructors for non-final classes, since the object that is constructed might be a subclass instance. Example 9.12 shows a situation where use of overridden methods in superclass initializers and constructors can give unexpected results. The example intentionally uses the this reference to underline the fact that the instance methods and constructors are invoked on the current object, and that the constructor call results in the initialization of the object state, as expected.

The program output shows that the field superValue at (1) in class SuperclassA never gets initialized explicitly when an object of the SubclassB is created at (8). The SuperclassA constructor at (2) does have a call to a method that has the name doValue at (3). A method with such a name is defined in the class SuperclassA at (4), but is also overridden in SubclassB at (7). The program output indicates that the method doValue() from the SubclassB is called at (3) in the SuperclassA constructor. The implementation of the method doValue() at (4) never gets executed when an object of the SubclassB is created. Method invocation always determines the implementation of the method to be executed, based on the actual type of the object. Keeping in mind that it is an object of SubclassB that is being initialized, the call to the method named doValue at (3) results in the method from SubclassB being executed. This can lead to unintended results. The overriding method doValue() at (7) in the class SubclassB can access the field value declared at (5) before its initializer expression has been executed; i.e., the method invoked can access the state of the object before this has been completely initialized.

Example 9.12 Initialization under Object State Construction

class SuperclassA {
  protected int superValue;                              // (1)
  SuperclassA() {                                        // (2)
    System.out.println("Constructor in SuperclassA");
    this.doValue();                                      // (3)
  }
  void doValue() {                                       // (4)
    this.superValue = 911;
    System.out.println("superValue: " + this.superValue);
  }
}
//____________________________________________________
class SubclassB extends SuperclassA {
  private int value = 800;                               // (5)
  SubclassB() {                                          // (6)
    System.out.println("Constructor in SubclassB");
    this.doValue();
    System.out.println("superValue: " + this.superValue);
  }
  void doValue() {                                       // (7)
    System.out.println("value: " + this.value);
  }
}
//____________________________________________________
public class ObjectInitialization {
  public static void main(String[] args) {
    System.out.println("Creating an object of SubclassB.");
    new SubclassB();                                     // (8)
  }
}

Output from the program:

Creating an object of SubclassB.
Constructor in SuperclassA
value: 0
Constructor in SubclassB
value: 800
superValue: 0

Class initialization takes place before any instance of the class can be created or a static method of the class can be invoked. A superclass is initialized before its subclasses are initialized. Initializing a class involves initialization of the static fields by executing their static initializer expressions and any static initializer blocks.

Initialization of an interface only involves execution of any static initializer expressions for the static fields declared in the interface. An interface cannot specify instance initializer expressions as it has no instance fields, nor can it specify instance initializer blocks as it cannot be instantiated.

Review Questions

Review Questions

9.13 Given the following class, which of these static initializer blocks can be inserted at (1)?

public class MyClass {
  private static int count = 5;
  final static int STEP = 10;
  boolean alive;

  // (1) INSERT STATIC INITIALIZER BLOCK HERE
}

Select the three correct answers

(a) static { alive = true; count = 0; }

(b) static { STEP = count; }

(c) static { count += STEP; }

(d) static ;

(e) static {;}

(f) static { count = 1; }

9.14 What will be the result of compiling and running the following program?

public class MyClass {
  public static void main(String[] args) {
    MyClass obj = new MyClass(n);
  }

  static int i = 5;
  static int n;
  int j = 7;
  int k;

  public MyClass(int m) {
    System.out.println(i + ", " + j + ", " + k + ", " + n + ", " + m);
  }

  { j = 70; n = 20; } // Instance Initializer Block

  static { i = 50; }  // Static Initializer Block
}

Select the one correct answer

(a) The code will fail to compile because the instance initializer block tries to assign a value to a static field.

(b) The code will fail to compile because the field k will be uninitialized when it is used.

(c) The code will compile and print 50, 70, 0, 20, 0, when run.

(d) The code will compile and print 50, 70, 0, 20, 20, when run.

(e) The code will compile and print 5, 70, 0, 20, 0, when run.

(f) The code will compile and print 5, 7, 0, 20, 0, when run.

9.15 Given the following class, which instance initializer block inserted at (1) will allow the class to be compiled?

public class MyClass {
  static int gap = 10;
  double length;
  final boolean active;

  // (1) INSERT CODE HERE
}

Select the one correct answer

(a) instance { active = true; }

(b) MyClass { gap += 5; }

(c) { gap = 5; length = (active ? 100 : 200) + gap; }

(d) { ; }

(e) { length = 4.2; }

(f) { active = (gap > 5); length = 5.5 + gap;}

9.16 What will be the result of compiling and running the following program?

public class Initialization {
  private static String msg(String msg) {
    System.out.println(msg); return msg;
  }

  public Initialization() { m = msg("1"); }

  { m = msg("2"); }

  String m = msg("3");

  public static void main(String[] args) {
    Object obj = new Initialization();
  }
}

Select the one correct answer

(a) The program will fail to compile.

(b) The program will compile, and print 1, 2, and 3, when run.

(c) The program will compile, and print 2, 3, and 1, when run.

(d) The program will compile, and print 3, 1, and 2, when run.

(e) The program will compile, and print 1, 3, and 2, when run.

9.17 What will be the result of compiling and running the following program?

public class Initialization {
  private static String msg(String msg) {
    System.out.println(msg); return msg;
  }

  static String m = msg("1");

  { m = msg("2"); }

  static { m = msg("3"); }

  public static void main(String[] args) {
    Object obj = new Initialization();
  }
}

Select the one correct answer

(a) The program will fail to compile.

(b) The program will compile and print 1, 2, and 3, when run.

(c) The program will compile and print 2, 3, and 1, when run.

(d) The program will compile and print 3, 1, and 2, when run.

(e) The program will compile and print 1, 3, and 2, when run.

9.18 Which of the labeled lines in the following code can be uncommented by removing the // characters and still allow the code to compile correctly?

class GeomInit {
//int width = 14;             /* Line A */
  {
// area = width * height;     /* Line B */
  }
  int width = 37;
  {
//  height = 11;              /* Line C */
  }
  int height, area;
//area = width * height;      /* Line D */
  {
//  int width = 15;           /* Line E */
    area = 100;
  }
};

Select the two correct answers

(a) Line A

(b) Line B

(c) Line C

(d) Line D

(e) Line E

Chapter Summary

Chapter Summary

The following information was included in this chapter:

• discussion of automatic garbage collection, including the workings of the garbage collector and guidelines for facilitating garbage collection

• discussion of object finalization and chaining as part of garbage collection

• discussion of static and instance initializers, both as initializer expressions and as initializer blocks

• discussion of the role played by initializers in initializing objects, classes, and interfaces

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset