Study guide: Packages organize classes and interfaces

Brush up on Java terms, learn tips and cautions, review homework assignments, and read Jeff's answers to student questions

Glossary of terms

library
One or more files that contain frequently used code for use in various programs.
metadata
Data that describes a jar file's contents.
namespace
A collection of names.
package
A collection of classes and interfaces.

Tips and cautions

These tips and cautions will help you write better programs and save you from agonizing over why the compiler produces error messages.

Tips

  • Just as you cannot store both a file and a directory with identical names in the same directory, you cannot store a class or interface and a package with identical names in the same package. For example, given a package named accounts, you cannot store both a package and a class named payable in accounts. To avoid conflicting names, uppercase the first letter of class and interface names, and lowercase the first letter of package names. Using the previous example, store class Payable in package accounts as accounts.Payable and package payable in package accounts as accounts.payable. Learn more about this and other naming conventions from Sun's Code Conventions for the Java Programming Language.

Cautions

  • Only one package directive can appear in a source file. Furthermore, the package directive must be the first code (apart from comments) in that file. Violating either rule causes Java's compiler to report an error.
  • As with the package directive, import directives must appear before any other code, with three exceptions: a package directive, other import directives, or comments.
  • A single-type import directive that imports a reference type name also declared in a source file causes the compiler to report an error.
  • The compiler reports an error upon encountering a reference type name and two or more type-on-demand import directives whose packages include that type name.
  • Any attempt to import nonpublic classes or interfaces from a package causes the compiler to report an error.

Homework

  • What is the unnamed package?
  • What is the purpose of classpath?
  • Create a shapes package with classes Point, Circle, Rectangle, and Square. Of those classes, ensure that Point is the only class not accessible outside its package. Use implementation inheritance to derive Circle from Point and Rectangle from Square. Provide an Area interface with a double getArea() method that returns the area of a Circle, a Square, or a Rectangle. Once you finish creating the package, create a TestShapes program that imports class and interface names from shapes, creates objects from shape classes, and computes the area of the shape each object represents. After compiling and running TestShapes (successfully), move shapes to another location on your hard drive and change classpath so that a second attempt to run TestShapes results in the same output as the previous run.

Answers to last month's homework

Last month, I asked you to answer some questions and write some programs. Here are my answers (which appear in red):

  • If you do not specify a thread group for a newly created thread, to which thread group does the thread belong?
  • In the absence of a specific thread group, the newly created thread belongs to the creating thread's thread group. For example, if the main() method's thread creates a thread, the newly created thread belongs to the main thread's group, which is main.

  • Identify two uses for thread groups.
  • Two uses for thread groups are priority limiting and common interruption. Priority limiting ensures all threads that join the thread group have priorities that do not exceed the thread group's maximum priority. Common interruption refers to calling ThreadGroup's interrupt() method to interrupt all of that group's waiting threads.

  • Can you destroy the system thread group?
  • No. You cannot destroy the system thread group because that group contains system threads, like the reference handler thread.

  • Does the volatile keyword provide an alternative to synchronization?
  • No. volatile allows multiple threads to access the main-memory copy of a shared variable—not the working-memory copies (for those JVMs that use working memory). volatile does not allow several instructions to be treated as an indivisible unit, which is what synchronization accomplishes.

  • Why can you not declare a shared field variable to be volatile and final?
  • You cannot declare a shared field variable to be both volatile and final because a final variable is read-only, and all threads always see the same value in that variable. Therefore, also declaring the field variable as volatile accomplishes nothing.

  • Write a program that uses InheritableThreadLocal's childValue(Object parentvalue) method to compute a child thread's initial value, as a function of a parent thread's initial value, for the inheritable thread-local variable.
  • In the following FunctionOf source code, the parent thread (which happens to be the main thread) establishes an Integer object containing 5 as the parent thread's initial value. When each of the two child threads accesses its initial value, that thread obtains an Integer object containing 15 because the childValue(Object parentValue) method modifies the parent's initial value by adding 10 to that value:

    // FunctionOf.java
    class FunctionOf implements Runnable
    {
       static InheritableThreadLocal itl =
              new InheritableThreadLocal ()
              {
                  protected Object childValue (Object parentValue)
                  {
                     if (parentValue instanceof Integer)
                     {
                         Integer i = (Integer) parentValue;
                         parentValue = new Integer (i.intValue () + 10);
                     }
                     return parentValue;
                  }
              };
       public static void main (String [] args)
       {
          itl.set (new Integer (5));
          FunctionOf fo = new FunctionOf ();
          Thread child1 = new Thread (fo);
          Thread child2 = new Thread (fo);
          child1.start ();
          child2.start ();
       }
       public void run ()
       {
          System.out.println (itl.get ());
       }
    }
    
    
  • Write a program that uses the Timer and TimerTask classes to periodically run a pair of tasks. After one of those tasks runs five times, use TimerTask's cancel() method to cancel that task.
  • In the following FiveTimes source code, the main thread schedules two tasks to run every second and every two seconds. The first task, which runs every second, displays the current date. After that task runs five times, the task calls TimerTask's cancel() method to prevent itself from running any more. The second task, which runs every two seconds, repeatedly outputs and increments the value of integer variable i. Because that task never stops, you must type Ctrl+C or your platform's equivalent keystroke combination to terminate the program.

    // FiveTimes.java
    // Type Ctrl+C (or equivalent keystroke combination on non-Windows platform) 
    // to terminate
    import java.util.*;
    class FiveTimes
    {
       public static void main (String [] args)
       {
          Timer t = new Timer ();
          t.schedule (new TaskA (), 0, 1000); // Run every second.
          t.schedule (new TaskB (), 0, 2000); // Run every two seconds.
       }
    }
    class TaskA extends TimerTask
    {
       private int timesRun; // Defaults to zero.
       public void run ()
       {
          System.out.println (new Date ().toString ());
          if (++timesRun == 5)
              cancel (); // Cancel task so it never runs again.
       }
    }
    class TaskB extends TimerTask
    {
       private int i; // Defaults to zero.
       public void run ()
       {
          System.out.println ("i = " + i++);
       }
    }
    
    
  • In the article's Clock1 application, replace Timer t = new Timer (); with Timer t = new Timer (true);. Recompile the source code and execute java Clock1. What happens and why?
  • When you replace Timer t = new Timer (); with Timer t = new Timer (true);, you end up with a Timer thread that uses a daemon thread to execute tasks. Because daemon threads automatically die when the last user thread exits, the timer's daemon thread dies when the main (user) thread exits. As a result, you will probably see no output from the program. To prove that, compile the following modified Clock1 source code and execute java Clock1 several times:

    // Clock1.java
    import java.util.*;
    class Clock1
    {
       public static void main (String [] args)
       {
          Timer t = new Timer (true);
          t.schedule (new TimerTask ()
                      {
                          public void run ()
                          {
                             System.out.println (new Date ().toString ());
                          }
                      },
                      0,
                      1000);
       }
    }
    
    
  • Why does the reference handler thread have a higher priority than the finalizer thread?
  • The reference handler thread has a higher priority than the finalizer thread to ensure the reference handler thread still runs when the finalizer thread deadlocks or delays excessively while executing finalize() methods. Although deadlock or excessive delay never happens in many programs' finalize() methods, the possibility exists (depending on how developers write those methods).

    If finalization never occurs, objects with overridden finalize() methods never reach the unreachable state and continue to occupy memory. Eventually (depending on the program type), memory fills and the program prematurely terminates because it can no longer allocate memory for objects. However, even if the finalizer thread deadlocks or delays excessively, finalization can still occur through System.runFinalization (); (and that method's eventual creation of a secondary finalizer thread) and because the reference handler thread still runs thanks to its higher priority.