Module 8: The Java Library - A First Look


Library overview

Java has organized its vast library into packages:

Java 1.1 (onwards) provides the following 15 "standard" packages:

Other packages will be added to the library of future versions.


Java versions: 1.0, 1.1, 1.2, 1.3, 1.4

It is important to understand the distinction between these two versions:

  • Java 1.1 is a major change from Java 1.0.
  • Java 1.1's AWT is significantly different from Java 1.0.
  • In the period 1996-97, only the HotJava browser supported Java 1.1, but now most browswers do.
    (Note: Netscape 3.0x does not support Java 1.1).
  • To maintain compatibility, Java 1.1 is a sort-of superset of Java 1.0.
  • All Java 1.0 classes and methods are available in Java 1.1.
  • However, they want you to stop using methods in 1.0 - these are called deprecated methods.
  • Thus, the 1.1 compiler will warn you if you are using a deprecated method, but will go ahead and compile it.
  • Sometimes, the Java 1.1 approach will look a lot more convoluted than the Java 1.0 approach (e.g., see the java.util.Date class).
  • Java makes no promises about retaining deprecated methods in future versions.
  • Java 1.2 adds the Swing package as part of the standard library.
    (Java 1.2 is also, confusingly, called Java 2).
  • Versions 1.3 and 1.4 add no significant changes. Version 1.5 is expected to add new language features (enum type, templates).


Some classes in java.lang

Consider the class java.lang.Math:

  • Note: useful math functions are in java.lang.Math and not in the java.math package.


  • Math only has static methods.


  • You cannot create an instance of Math as in
  •      Math m = new Math();  // Won't compile.
    


  • Math is similar to math.h in C/C++.


  • Here are some methods:
  •   public final class Math {
        // Two constants:
        public static final double E;
        public static final double PI;
        // Some class methods:
        public static int abs (int i);
        public static double abs (double x);  // long and float versions
                                              // also available
        public static native double sin (double x);
        public static native double cost (double x);
        public static native double log (double x);
        public static native double exp (double x);
        public static native double sqrt (double x);
        public static native double pow (double x, double y);
        public static native long round (double);
      }
    
  • The reserved word native suggests implementation in C.


  • Actually, the fdlibm math library implemented in C is used. See this site for more details.


The String class in java.lang implements some class methods and many instance methods:

  • The definition of String is:
  •   public final class String implements Serializable {
        // ...
      }
    

    Note: the Serializable interface indicates that String instances can be written to files using serialization
     

  • String has 11 constructors. For example:
  •     public String (byte[] b);  // Ascii representation.
    

     

     

  • Here is a useful class method:
  •     public static String valueOf (double d);
    

    This converts a double into a String.
     

  • Examples of useful instance methods:
  •     // Get the character at i-th position.
        public char charAt (int i);
    
        // Compare strings: return -1 (less), 0 (equals) or 1.
        public int compareTo (String s);
    
        // Does it end with suffix s?
        public boolean endsWith (String s);
    
        // Does the instance equal string s?
        public boolean equals (String s);
    
        // Ignore case in testing equality.
        public boolean equalsIgnoreCase (String s);
    
        // Number of characters.
        public int length();
    
        // Replace all occurences of a character.
        public String replace (char old, char new);
    
        // Does it start with prefix s?
        public boolean startsWith (String s);
    
        // Convert to lowercase.
        public String toLowerCase ();
    
        // Convert to uppercase.
        public String toUpperCase ();
    
        // Remove blanks on either side, but
        // not in the middle.
        public String trim ();
    


Next, consider the class Object:

  public class Object {
    // Constructor.
    public Object (); 

    // Test equality: really intended for
    // overriding.
    public boolean equals (Object obj);

    // Get class information dynamically.
    // Cannot be overridden.
    public final native Class getClass();

    // Compute a hashcode.
    public native int hashCode();

    // Convert to String. Intended to
    // be overridden.
    public String toString ();

    // Bitwise copy if Cloneable interface
    // is declared as implemented.
    protected native Object clone ()
      throws CloneNotSupportedException.

    // For cleaning up. Meant to be
    // overridden.
    protected void finalize ()
      throws Throwable;

    // These are all for Thread synchronization:
    public final native void notify ();
    public final native void notifyAll ();
    public final native void wait (long millis)
      throws InterruptedException;
    public final native void wait (long millis, long nanos)
      throws InterruptedException;
    public final native void wait ()
      throws InterruptedException;
  }

Note:

  • The native methods indicate system-level implementation.


  • Consider getClass():
    • Recall: Object is a superclass for every class.
    • getClass() be called by any Java class.
    • It returns an instance of a class called (confusingly) Class
    • This instance can be used to obtain information about a class at run-time.


  • hashCode, if not overridden, computes a bitwise hash-code based on the heapblock contents.


  • clone(), if not overridden, computes a bitwise clone.

  • (To use this, the Cloneable interface must be declared).

  • finalize(), if not overridden, does nothing.


  • Note: several methods throw exceptions:
    • these methods must be called in a try block, or,
    • the calling method must throw the same exception.


Consider next the class java.lang.Integer:

  • The definition of Integer is:
  •   public final class Integer extends Number {
        // Constructors.
    
        // Constants.
    
        // Class methods.
      
        // Instance methods.
      }
    
  • Number is an abstract class that forces implementation of methods like doubleValue (returns the double version of a number).


  • An instance of Integer stores a single integer value.


  • An Integer instance is often used to wrap an object around a basic int value.


  • Integer has two constructors:
  •     // Takes in the integer value.
        public Integer (int i);
    
        // Takes in a String and parses it.
        public Integer (String s)
          throws NumberFormat
    

     

     

  • Useful constants in Integer:
  •     // Largest possible integer.
        public static final int MAX_VALUE;
    
        // Smallest (most negative) integer.
        public static final int MIN_VALUE;
    
  • Here are some useful class (static) methods in Integer:
  •     // Parses a String into an int.
        public static int parseInt (String s)
          throws NumberFormatException;
        // A base or radix can be specified.
        public static int parseInt (String s, int radix)
          throws NumberFormatException;
    
        // Convert int value to binary (String).
        public static String toBinaryString (int i);
        // Convert to hex.
        public static String toHexString (int i);
    
        // These are different from the standard toString().
        public static String toString (int i);
        public static String toString (int i, int radix);
    
  • Here are some useful instance methods:
  •     // Return the int as a double.
        public double doubleValue ();
    
        // equals() and toString() have
        // been overridden.
        public boolean equals (Object obj);
        public String toString ();
    


Arbitrary precision using java.math.BigInteger

The class java.math.BigInteger can be used for arbitrary precision arithmetic.

Consider computing the powers of 2 using int's:

  • An int is 4 bytes (32 bits)
  • Thus, the 32-nd power of 2 cannot be represented in an int.

Here is how BigInteger can be used: (source file)

import java.math.*;

public class TestBig {

  public static void main (String[] argv)
  {
    // Initialize.
    int i = 1;
    BigInteger I = new BigInteger ("1");

    // We will need this constant.
    BigInteger Two = new BigInteger ("2");

    // Compute successive powers of 2.
    for (int j=1; j<=64; j++) {
      i = i * 2;
      I = I.multiply (Two);
      System.out.println ("2 to the power " + j);
      System.out.println ("i = " + i);
      System.out.println ("I = " + I);
      System.out.print ("\n");
    }
  }

}

Note:

  • BigInteger has several constructors, one of which takes a String as parameter.


  • BigInteger provides instance methods like add, subtract, multiply and divide.


  • Here are some other useful methods:
  •    // Absolute value.
       public BigInteger abs ();
    
       // Comparison.
       public int compareTo (BigInteger I);
    
       // Usual arithmetic operations.
       public BigInteger add (BigInteger I);
       public BigInteger subtract (BigInteger I);
       public BigInteger multiply (BigInteger I);
       public BigInteger divide (BigInteger I);
       public BigInteger pow (int a);
    
       // Operations specific to integers.
       public BigInteger mod (BigInteger I);
       public BigInteger remainder (BigInteger I);
    
       // Overriding methods from Object.
       public boolean equals (Object obj);
       public String toString ();
    
       // Recover basic types.
       public int intValue ();
       public long longValue ();
       public double doubleValue ();
    

In-class Exercise 8.1: Write a program to print out numbers in the Fibonacci sequence. Print out the first 200 numbers in the sequence.

Micro-tutorial on Fibonacci numbers:

  • The i-th Fibonacci number f_i is computed as: f_i = f_{i-2} + f_{i-1}
  • Then, depending on f_1 and f_2, you get different series.
  • The most well-known sequence has f_1=0 and f_2=1.


  • The first few terms in this sequence are: 0,1,1,2,3,5,8,...etc.


Creating and manipulating dates

Java provides the class Date in package java.util to store a date.

For example, the following code (source file)

import java.util.*;

public class TestDate {

  public static void main (String[] argv)
  {
    Date d = new Date ();
    System.out.println (d);
  }

}

creates a Date instance initialized to the current date and time, and prints it out:

Mon Oct 05 15:46:12 EDT 1998

In Java 1.0, you could set the date using a constructor:

    Date d2 = new Date ("October 5, 1998");
    System.out.println (d2);

However, this method is deprecated in Java 1.1.

Instead, you need to use the following library classes in combination:

  • java.util.Date:
    • Date represents a time-date combination using a single long value.
    • Use a Date instance usually as a temporary way to pass data from a GregorianCalendar instance to a SimpleDateFormat instance.


  • java.util.Calendar:
    • Calendar is an abstract class.
    • GregorianCalendar is an implementation of this abstract class that you will most likely use.
    • However, you will use Calendar for documentation, since inherited constants and methods are described here.
    • Instead of using GregorianCalendar below, you can get an instance using Calendar.getInstance().
    • Calendar defines an int constant for each month and each day of the week, as well as for other things like AM and PM.
    • Example: Calendar.OCTOBER is an int code for October.
    • Calendar defines important methods like before, after and set that are inherited by GregorianCalendar.


  • java.util.GregorianCalendar:
    • GregorianCalendar inherits from the abstract class Calendar.
    • See the Calendar documentation for many of GregorianCalendar's methods and constants.
    • There are two advantages to using GregorianCalendar over a Calendar instance: it has useful constructors and has a isLeapYear() method.
    • However, code can be made system-dependent by using a Calendar instance.

    • (At this time, only a Gregorian Calendar is implemented in Java).



  • java.text.SimpleDateFormat.
    • This class inherits from DateFormat but is easier to use.
    • It has two main functions: extract a date from a String and converting a date to a String.
    • To use it (for either parsing or formatting), you have to specify the format of the String
    • Specifying the format consists of using a peculiar code to describe the string format, such as "dd MMM yy".
    • The entire code is given in the libary description for SimpleDateFormat.

(You can also use only Calendar and DateFormat, but using them is more complicated).

A simple creation of a date: (source file)

import java.util.*;
import java.text.*;

public class TestDate2 {

  public static void main (String[] argv)
  {
    GregorianCalendar cal = new GregorianCalendar (
       1998,                       // Year
       GregorianCalendar.OCTOBER,  // Month
       5,                          // Day
       14,                         // Hour of day (2 pm)
       30,                         // Minutes
       22                          // Seconds
    );

    // Get a Date instance.
    Date d = cal.getTime ();

    // Print.
    System.out.println (d);
  }

}

Note:

  • The constructor was passed everything from year to seconds.


  • Other constructors take fewer parameters.


  • Alternatively, one of several set methods can be used: (source file)
  •     GregorianCalendar cal = new GregorianCalendar ();
        cal.set (
           1998,                       // Year
           GregorianCalendar.OCTOBER,  // Month
           5,                          // Day
           14,                         // Hour of day (2 pm)
           30,                         // Minutes
           22                          // Seconds
        );
    
  • Individual fields can be set: (source file)
  •     GregorianCalendar cal = new GregorianCalendar ();
        cal.set (Calendar.YEAR, 1998);
        cal.set (Calendar.MONTH, Calendar.OCTOBER);
        cal.set (Calendar.DAY_OF_MONTH, 5);
        cal.set (Calendar.HOUR_OF_DAY, 14);  // Use this instead of 
                                             // Calendar.HOUR
        cal.set (Calendar.MINUTE, 30);
        cal.set (Calendar.SECOND, 22);
        cal.set (Calendar.MILLISECOND, 13);
    
        // Get a Date instance.
        Date d = cal.getTime ();
    
        // Print.
        System.out.println (d);
    
  • GregorianCalendar does not override toString.


  • Hence, a Date is extracted with its getTime() method and printed.


More about GregorianCalendar:

  • Calendar and therefore GregorianCalendar has the following constants defined:
  •   public static final int
        JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE,
        JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER;
      public static final int
        MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, 
        SATURDAY, SUNDAY;
      public static final int
        AM, PM;
    
  • The following codes are also defined:
  •   public static final int 
        ERA, YEAR, MONTH, WEEK_OF_MONTH, DATE,
        DAY_OF_WEEK, DAY_OF_MONTH, DAY_OF_YEAR, 
        DAY_OF_WEEK_IN_MONTH, ZONE_OFFSET, 
        AM_PM, HOUR, HOUR_OF_DAY, MINUTE,
        SECOND, MILLISECOND;
    
  • GregorianCalendar defines two additional constants:
  •   public static final int
        AD, BC;
    
  • GregorianCalendar permits comparisons using the equals, before and after methods.
  • For example: (source file)

    import java.util.*;
    import java.text.*;
    
    public class TestDate5 {
    
      public static int compare (GregorianCalendar c1, 
                                 GregorianCalendar c2)
      {
        if ( c1.equals(c2) )       // The equals method.
          return 0;
        else if ( c1.before(c2) )  // The before method.
          return -1;
        else
          return 1;
      } 
    
      public static void main (String[] argv)
      {
        // Create a date.
        GregorianCalendar cal = new GregorianCalendar ();
        cal.set (Calendar.YEAR, 1998);
        cal.set (Calendar.MONTH, Calendar.OCTOBER);
        cal.set (Calendar.DAY_OF_MONTH, 5);
        Date d = cal.getTime ();
    
        // Create a second date.
        GregorianCalendar cal2 = new GregorianCalendar ();
        cal2.set (Calendar.YEAR, 1998);
        cal2.set (Calendar.MONTH, Calendar.OCTOBER);
        cal2.set (Calendar.DAY_OF_MONTH, 5);
        Date d2 = cal2.getTime ();
    
        // Compare.
        int comp = compare (cal, cal2);
        if (comp == 0) 
          System.out.println (d + " and " + d2 + " are the same");
        else if (comp < 0)
          System.out.println (d + " comes before " + d2);
        else 
          System.out.println (d + " comes after " + d2);
      }
    
    }
    
    
  • GregorianCalendar inherits a get method to obtain the value of any field.
    • Note: you would expect that a field like DAY_OF_YEAR would be computed for you at the time of instance creation.
    • However, with this code:
    •     GregorianCalendar cal = new GregorianCalendar (
            1998,              // Year.
            Calendar.OCTOBER,  // Month.
            5                  // Day.
          );
      
          System.out.println ("  DAY_OF_YEAR = " 
                              + cal.get (Calendar.DAY_OF_YEAR) );
      

      the following gets printed out:

        DAY_OF_YEAR = 0
      
    • It appears that all fields are not computed at the time of instance creation.


    • However, Calendar provides a method called complete that forces these calculations.


    • Unfortunately, for undiscernible reasons, complete is a protected method.


    • Thus, only a subclass can un-protect it: (source file)
    • class GCal extends GregorianCalendar {
        public void complete ()
        {
          super.complete ();
        }
      }
      
      public class TestDate6 {
      
        public static void main (String[] argv)
        {
          GCal cal = new GCal ();
          cal.set (Calendar.YEAR, 1998);
          cal.set (Calendar.MONTH, Calendar.OCTOBER);
          cal.set (Calendar.DAY_OF_MONTH, 5);
          cal.complete();
      
          // Get a Date instance.
          Date d = cal.getTime ();
      
          // Print attributes
          System.out.println ("Date " + d + " has the following attributes:");
          System.out.println ("  ERA = " 
                              + cal.get (Calendar.ERA) );
          System.out.println ("  WEEK_OF_YEAR = " 
                              + cal.get (Calendar.WEEK_OF_YEAR) );
          System.out.println ("  WEEK_OF_MONTH = " 
                              + cal.get (Calendar.WEEK_OF_MONTH) );
          System.out.println ("  DAY_OF_YEAR = " 
                              + cal.get (Calendar.DAY_OF_YEAR) );
          System.out.println ("  DAY_OF_WEEK = " 
                              + cal.get (Calendar.DAY_OF_WEEK) );
          System.out.println ("  DAY_OF_WEEK_IN_MONTH = " 
                              + cal.get (Calendar.DAY_OF_WEEK_IN_MONTH) );
      
        }
      
      }
      

      This prints out:

        ERA = 1
        WEEK_OF_YEAR = 40
        WEEK_OF_MONTH = 1
        DAY_OF_YEAR = 278
        DAY_OF_WEEK = 2
        DAY_OF_WEEK_IN_MONTH = 1
      
  • GregorianCalendar has a isLeapYear method:
  •   public boolean isLeapYear (int year);
    


Using SimpleDateFormat:

  • SimpleDateFormat is in the package java.text.


  • Use SimpleDateFormat instances to parse strings and extract a date, or to print a date in a specific format.


  • For either parsing or printing, use the same method for specifying the format.


  • Formats are specified using a pattern string.

Since creating a pattern string can be complicated, let's do this in small steps:

  • We will consider parsing first: a date-time combination is input as a string and we need to tell the parser what the string will look like.


  • First, let's look at some examples of pattern strings:
    1. "yyyy MM d"
    2. "yy, MMM, EEEE, dd, HH, a"


  • The first thing to note is that pattern strings contain strange looking codes like yyyy and EEEE.


  • Each code specifies what to expect in actual data.


  • For example, the pattern string "yyyy MM d" says that input strings will have a 4-digit year, a 2-digit month and a 1-or-2 digit day, with whitespace between.


  • Here's a sample input string that matches the pattern: "1998 10 5" (October 5, 1998).


  • Note:
    • It's important NOT to think that the number of letters have anything to do with length:
    • For example, "MM" is a month-number, but "MMM" is a month name like July.


  • Here are some sample codes:
    • Year:
      • yyyy: 4-digit, e.g. 1998.
      • yy: 2-digit, e.g., 98.
    • Month:
      • MMMM: full name, e.g., September.
      • MMM: 3-letter name, e.g., Sep.
      • MM: 2-digit number, e.g., 09.
      • M: 1-or-2 digit number, e.g, 9 or 11.
    • Day:
      • dd: 2-digit number, e.g., 03.
      • d: 1-or-2 digit number, e.g., 3 or 25
    • Day of week:
      • EEEE: full name, e.g., Wednesday.
      • EEE: 3-letter name, e.g., Wed.
    • Hour:
      • HH: 2-digit number in [0,24], e.g. 16 (4 pm).
      • hh: 2-digit number in [0,12], e.g. 04. (Specify am/pm separately).
    • AM/PM:
      • (Default - nothing written): AM.
      • a: specifies PM (Confusing, isn't it?).
    • Minutes, seconds, milliseconds:
      • mm: 2-digit minute, e.g., 05.
      • ss: 2-digit second, e.g., 09.
      • SSS: 3-digit millisecond, e.g., 321.


  • Now, let us use pattern strings in parsing: (source file)
  • import java.util.*;
    import java.text.*;
    
    public class TestDate7 {
    
      public static void main (String[] argv)
      {
        // Create three patterns.
        SimpleDateFormat 
          sdf1 = new SimpleDateFormat ("yyyy MMMM dd"),
          sdf2 = new SimpleDateFormat ("EEEE, MMM dd, yyyy"),
          sdf3 = new SimpleDateFormat ("M d HH mm ss");
    
        // try is required: parse can throw an exception.
        try {
          // In the form "yyyy MMM dd"
          Date d1 = sdf1.parse ("1998 October 06");
          System.out.println ("Date 1: " + d1);
    
          // In the form "EEEE, MMM dd, yyyy".
          // Note the commas.
          Date d2 = sdf2.parse ("Tuesday, Oct 06, 1998");
          System.out.println ("Date 2: " + d2);
    
          // In the form "M d HH mm ss"
          Date d3 = sdf3.parse ("12 25 17 30 11");
          System.out.println ("Date 3: " + d3);
    
          // Let's deliberately provide bad input.
          Date d4 = sdf1.parse ("October 6, 1998");
          System.out.println ("Date 4: " + d4);
        }
        catch (ParseException e) {
          System.out.println (e);
        }
      }
    
    }
    

    Here's what gets printed out:

    Date 1: Tue Oct 06 03:00:00 EDT 1998
    Date 2: Tue Oct 06 03:00:00 EDT 1998
    Date 3: Fri Dec 25 20:30:11 EST 1970
    java.lang.NumberFormatException: Octo
            at java.lang.Integer.parseInt(Integer.java)
            at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java)
            at java.text.SimpleDateFormat.parse(SimpleDateFormat.java)
            at java.text.DateFormat.parse(DateFormat.java)
            at TestDate7.main(TestDate7.java:27)
    

The same pattern strings are also used for output:

  • Specify the desired output format using a pattern string.


  • Then, use the format method to obtain a String from a Date.


  • Example: (source file)
  •     // Create three patterns.
        SimpleDateFormat 
          sdf1 = new SimpleDateFormat ("yyyy MMMM dd"),
          sdf2 = new SimpleDateFormat ("EEEE, MMM dd, yyyy"),
          sdf3 = new SimpleDateFormat ("M d HH mm ss");
    
        // try is required: parse can throw an exception.
        try {
          // Format a date from an string.
          Date d1 = sdf1.parse ("1998 October 06");
          System.out.println ("Date 1: " + d1);
    
          // Next, we will format the output differently.
          String s2 = sdf2.format (d1);
          System.out.println (s2);
    
          String s3 = sdf3.format (d1);
          System.out.println (s3);
        }
        catch (ParseException e) {
          System.out.println (e);
        }
    

    The result is:

    Date 1: Tue Oct 06 03:00:00 EDT 1998
    Tuesday, Oct 06, 1998
    10 6 00 00 00
    

Now we are in a position to combine these classes:

  • Here are the steps:
    1. Parse an input date using a SimpleDateFormat instance.
    2. In parsing, it keeps the date internally in a Calendar instance.
    3. Obtain this Calendar instance from SimpleDateFormat.
    4. Make changes, comparisons etc.
    5. Set up a SimpleDateFormat instance with the desired output format.
    6. Pass it the Date from the Calendar instance.
    7. Call the format method to obtain the output String.


  • Example: (source file)
  •     // Create input and output patterns.
        SimpleDateFormat 
          input_format = new SimpleDateFormat ("yyyy MMMM dd"),
          output_format = new SimpleDateFormat ("EEEE, MMM dd, yyyy, HH mm ss");
    
        try {
          // Provide a format for input.
          Date d1 = input_format.parse ("1998 October 06");
          System.out.println ("Date: " + d1);
    
          // Next, wrap a Calendar around a date.
          GregorianCalendar cal = (GregorianCalendar) input_format.getCalendar();
    
          // Make changes as desired.
          cal.set (Calendar.HOUR_OF_DAY, 17);
          cal.set (Calendar.MINUTE, 22);
          cal.set (Calendar.SECOND, 35);
         
          // Get back the date.
          d1 = cal.getTime();
    
          // Format the output.
          String s = output_format.format (d1);
    
          // Print.
          System.out.println (s);
        }
        catch (ParseException e) {
          System.out.println (e);
        }
    

Miscellany:

  • Note that, for date output, you can use SimpleDateFormat to move the day ahead of the month (British style).


  • As an alternative to SimpleDateFormat, you can use a DateFormat instance directly:
    • DateFormat is an abstract class.
    • However, you can get an instance by calling one of several static methods, e.g.,
    •    public static final DateFormat getDateTimeInstance ();
      
  • The advantage is that several "standard" formats have been defined for you:
    • There are 5 pre-defined styles for dates and 5 styles for times
    • These are specified using 5 constants: DateFormat.DEFAULT, DateFormat.FULL, DateFormat.LONG, DateFormat.MEDIUM, DateFormat.SHORT.
    • For example: (source file)
    •     // Create a date.
          GregorianCalendar cal = new GregorianCalendar (
             1998,                       // Year
             GregorianCalendar.OCTOBER,  // Month
             5,                          // Day
             14,                         // Hour of day (2 pm)
             30,                         // Minutes
             22                          // Seconds
          );
      
          // Extract the date.
          Date d = cal.getTime();
      
          // Use the default format.
          DateFormat def = DateFormat.getDateInstance (DateFormat.DEFAULT);
          String s = def.format (d);
          System.out.println ("DEFAULT: " + s);
      
          // The full format.
          DateFormat full = DateFormat.getDateInstance (DateFormat.FULL);
          s = full.format (d);
          System.out.println ("FULL: " + s);
      
          // The long format.
          DateFormat lg = DateFormat.getDateInstance (DateFormat.LONG);
          s = lg.format (d);
          System.out.println ("LONG: " + s);
      
          // Medium and default are the same.
          DateFormat med = DateFormat.getDateInstance (DateFormat.MEDIUM);
          s = med.format (d);
          System.out.println ("MEDIUM: " + s);
      
          // Short format.
          DateFormat sh = DateFormat.getDateInstance (DateFormat.SHORT);
          s = sh.format (d);
          System.out.println ("SHORT: " + s);
      

      Here's the output:

      DEFAULT: 05-Oct-98
      FULL: Monday, October 5, 1998
      LONG: October 5, 1998
      MEDIUM: 05-Oct-98
      SHORT: 10/5/98
      


  • The Locale class is used to accomodate local custom in formatting:
    • Dates and times can be output according to various local conventions.
    • Various countries (only a few, actually) are represented by constants in java.util.Locale.
    • Examples: Locale.JAPAN, Locale.GERMANY.
    • These constants can be passed to a DateFormat instance.
    • Example: (source file)
    •     // Create a date.
          GregorianCalendar cal = new GregorianCalendar (
             1998,                       // Year
             GregorianCalendar.OCTOBER,  // Month
             5,                          // Day
             14,                         // Hour of day (2 pm)
             30,                         // Minutes
             22                          // Seconds
          );
      
          // Extract the date.
          Date d = cal.getTime();
      
          DateFormat usa = DateFormat.getDateInstance (DateFormat.FULL,
                                                      Locale.US);
          String s = usa.format (d);
          System.out.println ("USA: " + s);
      
          DateFormat uk = DateFormat.getDateInstance (DateFormat.FULL, 
                                                      Locale.UK);
          s = uk.format (d);
          System.out.println ("UK: " + s);
      
          DateFormat france = DateFormat.getDateInstance (DateFormat.FULL, 
                                                      Locale.FRANCE);
          s = france.format (d);
          System.out.println ("FRANCE: " + s);
      
          DateFormat germany = DateFormat.getDateInstance (DateFormat.FULL, 
                                                      Locale.GERMANY);
          s = germany.format (d);
          System.out.println ("GERMANY: " + s);
      

      This prints out:

      USA: Monday, October 5, 1998
      UK: Monday, 5 October 1998
      FRANCE: lundi, 5 octobre 1998
      GERMANY: Montag, 5. Oktober 1998
      
  • Timing code execution:
    • You can obtain execution times by obtaining the system time in milliseconds before and after a piece of code.
    • Example: (source file)
    •     long start = System.currentTimeMillis ();
          for (int i=1; i<=100000; i++) {
            int j = i*i;
          }
          long stop = System.currentTimeMillis ();
          System.out.println ("Time taken: " + (stop-start));
      


In-Class Exercise 8.2: Use class GCal above (in this file) and the complete() method to find all years this century in which October 7 falls on a Wednesday. Print the years in reverse chronological order.


Formatting numbers

Unlike C/C++, Java's standard output streams do not provide convenient formatting mechanisms.

Without formatting explicitly, output can appear jagged, for example: (source file)

    for (int i=0; i<=1000; i+=200) {
      int sqr = i * i;
      double root = Math.sqrt (i);
      System.out.println ("The square of " + i + " is " + sqr +
                          " and the square root is " + root);

The output is:

The square of 0 is 0 and the square root is 0.0
The square of 200 is 40000 and the square root is 14.142135623730951
The square of 400 is 160000 and the square root is 20.0
The square of 600 is 360000 and the square root is 24.49489742783178
The square of 800 is 640000 and the square root is 28.284271247461902
The square of 1000 is 1000000 and the square root is 31.622776601683793

To format numbers, use DecimalFormat or NumberFormat instances:

  • Both classes are in package java.text.


  • NumberFormat is an abstract class, but has methods to return instances.


  • DecimalFormat inherits from NumberFormat and provides an implementation.


  • Just like DateFormat, these classes can be used for both parsing and output.


  • A pattern string is used to convey the desired format.


  • The pattern string is passed either via the constructor or via the applyPattern method.

First, consider formatting integers:

  • The symbol # (hash symbol) or 0 (zero) is used to signify a digit.


  • Observe: for leading zeroes, we can either ignore them, print blanks or actually fill in zeroes.


  • First consider filling in zeroes: (source file)
  •     for (int i=0; i<=1000; i+=200) {
          int sqr = i * i;
    
          // Create a DecimalFormat instance, specifying 
          // width in the constructor.
          DecimalFormat df = new DecimalFormat ("00000000");
    
          // Extract a string by passing an int.
          String s = df.format (sqr);
    
          // Print the string.
          System.out.println (s + " is the square of " + i);
        }
    

    The output is:

    00000000 is the square of 0
    00040000 is the square of 200
    00160000 is the square of 400
    00360000 is the square of 600
    00640000 is the square of 800
    01000000 is the square of 1000
    

    Thus, each integer occupies 8 characters, with leading zeroes filled in.
     

  • Consider what happens when we use #:
  •     for (int i=0; i<=1000; i+=200) {
          int sqr = i * i;
    
          // Create a DecimalFormat instance, specifying 
          // width in the constructor.
          DecimalFormat df = new DecimalFormat ("########");
    
          // Extract a string by passing an int.
          String s = df.format (sqr);
    
          // Print the string.
          System.out.println (s + " is the square of " + i);
    

    The output is:

    0 is the square of 0
    40000 is the square of 200
    160000 is the square of 400
    360000 is the square of 600
    640000 is the square of 800
    1000000 is the square of 1000
    

    Note: using the #-symbol does not achieve fixed-width formatting.
     

  • To print
  •       0 is the square of 0
      40000 is the square of 200
     160000 is the square of 400
     360000 is the square of 600
     640000 is the square of 800
    1000000 is the square of 1000
    

    we need to pad blanks.
     

  • Unfortunately, Java does not provide a mechanism for blank-padding.


  • Java does not truncate numeric strings, thus,
  •       DecimalFormat df = new DecimalFormat ("#");
    

    is equivalent to:

          DecimalFormat df = new DecimalFormat ("########");
    

Next, consider formatting double's:

  • To format a double, we need to specify what is desired both before and after the decimal point.


  • Example: (source file)
  •     double d1 = 9876.543;
        double d2 = 9876.0;
        double d3 = 0.543;
    
        DecimalFormat df = new DecimalFormat ("#.#");
        String s1 = df.format (d1);
        String s2 = df.format (d2);
        String s3 = df.format (d3);
        System.out.println ("d1=" + s1 + "  d2=" + s2 + "  d3=" + s3);
        // Prints "d1=9876.5  d2=9876  d3=.5"
    
        df = new DecimalFormat ("####.####");
        s1 = df.format (d1);
        s2 = df.format (d2);
        s3 = df.format (d3);
        System.out.println ("d1=" + s1 + "  d2=" + s2 + "  d3=" + s3);
        // Prints "d1=9876.543  d2=9876  d3=.543"
    
        df = new DecimalFormat ("0.0");
        s1 = df.format (d1);
        s2 = df.format (d2);
        s3 = df.format (d3);
        System.out.println ("d1=" + s1 + "  d2=" + s2 + "  d3=" + s3);
        // Prints "d1=9876.5  d2=9876.0  d3=0.5"
    
        df = new DecimalFormat ("0000.0000");
        s1 = df.format (d1);
        s2 = df.format (d2);
        s3 = df.format (d3);
        System.out.println ("d1=" + s1 + "  d2=" + s2 + "  d3=" + s3);
        // Prints "d1=9876.5430  d2=9876.0000  d3=0000.5430"
    
  • Thus,
    • Although using #'s before the decimal point does not restrict printing, a fixed number of #'s after the decimal restricts the precision.
    • Once again, 0 is used for specifying leading and trailing zeroes.

Finally, noting how complicated it is to use DecimalFormat, we provide some simple methods to specify precision: (source file)

class EasyFormat {

  // Pad blanks as required (for both int's and double's).
  static String pad_blanks (String s, int target_len, boolean is_int)
  {
    int slen = s.length();

    // No decimal point to worry about with int's.
    if (is_int) {
      if (slen >= target_len) 
        return s;
      // Otherwise we pad from front
      for (int i=1; i <= target_len-slen; i++)
        s = ' ' + s;
      return s;
    }

    // Otherwise check up to decimal point
    int i = s.indexOf ('.');
    if ((i < 0) || (i >= slen)) {
      System.out.println ("ERR: pad_blanks: no decimal point");
      System.exit(1);
    }
    if (i+1 >= target_len) 
      return s;

    // Otherwise, pad with blanks
    for (int j=1; j < =target_len-(i+1); j++)
      s = ' ' + s;
    return s;
  }

  // Format integers.
  public static String format (long i, int width) 
  {
    DecimalFormat df_i = new DecimalFormat ("#");    
    String s_i = df_i.format (i);
    s_i = pad_blanks (s_i, width, true);
    return s_i;
  }

  // Format double's.
  public static String format (double d, int before_dec_pt, 
                                    int after_dec_pt) 
  {
    // Use '#' symbols before decimal point.
    String s = "";
    for (int i=1; i < before_dec_pt; i++)
      s = s + '#';

    // Force a zero for numbers less than 1.0
    s = s + "0.";

    // Include zeroes as specified by width.
    for (int i=1; i < =after_dec_pt; i++)
      s = s + '0';

    DecimalFormat df_d = new DecimalFormat (s);    
    String s_d = df_d.format (d);

    // Pad blanks.
    s_d = pad_blanks (s_d, before_dec_pt, false);
    return s_d;
  }

}


public class TestNumber4 {
  public static void main (String[] argv)
  {
    for (int i=0; i<=1000; i+=200) {
      String s = EasyFormat.format (i, 4);

      int sqr = i * i;

      String sqr_string = EasyFormat.format (sqr, 8);

      double root = Math.sqrt (i);
      String root_string = EasyFormat.format (root, 5, 3);

      System.out.println ("The square of " + s + " is " + sqr_string +
                          " and the square root is " + root_string);
    }
  }
}

The output is:

The square of    0 is        0 and the square root is    0.000
The square of  200 is    40000 and the square root is   14.142
The square of  400 is   160000 and the square root is   20.000
The square of  600 is   360000 and the square root is   24.494
The square of  800 is   640000 and the square root is   28.284
The square of 1000 is  1000000 and the square root is   31.622

These useful methods have been included in the New_io class described earlier.


The Class class and java.lang.reflect package

One of the methods in class java.lang.Object is the following:

public class Object {
  // ...
  public final native Class getClass();
  // ...
}

Notice that the return value is something called Class (capital C).

What is this thing called Class?

  • Confusingly, Class is the name of a class in java.lang.


  • Let's take a look at the definition:
  • public final class Class implements Serializable {
    
      // Class methods:
      public static native Class forName (String name)
        throws ClassNotFoundException;
    
      // Some instance methods:
      public native String getName ();
    
      public Constructor[] getConstructors ()
        throws SecurityException;
    
      public Field[] getFields ()
        throws SecurityException;
    
      public Method[] getMethods ()
        throws SecurityException;
    }
    

What it it used for?

  • Observe that since Object has a getClass() method, every class has this method.

  • (Note: no class can override this method.)

  • Thus, if Obj_A is some class, then the following is possible:
  •     Obj_A a = new Obj_A ();
        Class c = a.getClass ();  // getClass() is inherited from Object.
    
  • A Class instance is obtained from the getClass method of any class.


  • The Class instance so obtained contains information about the instance whose getClass method was invoked.


  • Thus, in the above example, the Class instance c contains information about Obj_A.

A simple example:

    String s = "hello";      // A String is a class.
    Class c = s.getClass (); // Get info about String's.
    System.out.println (c);  // Print.

This is the output:

class java.lang.String

In this case, the name of the class (java.lang.String) is printed out.

Now suppose we define Obj_A as:

class Obj_A {

  // Data.
  int n;
  double x;

  // Constructors.
  public Obj_A (int n, double x)
  {
    this.n = n;  this.x = x;
  }

  public Obj_A ()
  {
    this (0, 0);
  }

  // Methods.
  public static void print ()
  {
    System.out.println ("Obj_A");
  }

  public String toString ()
  {
    return "Obj_A: n=" + n + ", x=" + x;
  }
}

Suppose now we want to extract information about the definition of Obj_A given an instance. Here's how:

    Obj_A a = new Obj_A ();
    c = a.getClass ();
    System.out.println (c);    

Instead of only getting the name, we can actually find out everything about Obj_A.

For example, we can count the constructors of Obj_A:

    // Get constructors of Obj_A.
    Constructor[] ctor = c.getConstructors ();
    System.out.println ("Obj_A has " + ctor.length + " constructors");

or print a list of its methods:

    // Get methods of Obj_A.
    Method[] method = c.getMethods();
    System.out.println ("Obj_A has " + method.length + " methods: ");
    for (int i=0; i < method.length; i++) {
      String name = method[i].getName();
      System.out.println ("  " + name);
    }

Note:

  • The above code uses classes like Method and Constructor.


  • These are in the package java.lang.reflect.


  • For example, Method is defined as follows:
  •   public final class Method implements Member {
        // Member is an interface in java.lang.reflect.
    
        // Some instance methods:
        public Class getDeclaringClass();   // Get name of class.
        public String getName();            // Get method name.
        public Class[] getParameterTypes(); // Get list of parameter types.
        public Class getReturnType();       // Get return value type.
    
        // A very useful method - for dynamic invocation.
        public native Object invoke (Object obj, Object[] params)
          throws IllegalAccessException, IllegalArgumentException,
                 InvocationTargetException;
      }
    

You can go a step further: given just the name of a class, you can create an instance and use it: (source file

import java.lang.reflect.*;

class Obj_A {

  // Data.
  int n;
  double x;

  // Constructors.
  public Obj_A (int n, double x)
  {
    this.n = n;  this.x = x;
  }

  public Obj_A ()
  {
    this (0, 0);
  }

  // Methods.
  public static void print ()
  {
    System.out.println ("Obj_A");
  }

  public String toString ()
  {
    return "Obj_A: n=" + n + ", x=" + x;
  }
}


public class TestClass {

  public static void call_toString (String obj_name)
  {
    try {
      // Get the Class instance first.
      Class c = Class.forName (obj_name);

      // Get a list of methods.
      Method[] method = c.getMethods();

      // Go through and look for toString()
      for (int i=0; i < method.length; i++) {

        // Get name of i-th method.
        String name = method[i].getName();

        // If it is toString ...
        if (name.equals ("toString")) {
          System.out.println (obj_name + " has a toString() method");
          try {
            // Create an instance of the object on the fly.
            Object instance = c.newInstance ();

            // No parameters, so a size=0 array is passed.
            Object[] obj_array = new Object[0];

            // Call the powerful invoke() method.
            // Note: toString returns a string.
            String output = (String) method[i].invoke (instance, obj_array);

            // Print out the string returned from toString()
            System.out.println ("String returned: " + output);
          }
          // Catch a whole bunch of exceptions.
          catch (InstantiationException e) {
            System.out.println (e);
          }
          catch (IllegalAccessException e) {
            System.out.println (e);
          }
          catch (InvocationTargetException e) {
            System.out.println (e);
          }
        }
      }
    }
    catch (ClassNotFoundException e) {
      System.out.println (e);
    }
  }

  public static void main (String[] argv)
  {
    call_toString ("Obj_A");
  }
}

Since Obj_A has a toString(), it gets called and here's what is printed out:

Obj_A has a toString() method
String returned: Obj_A: n=0, x=0.0

Thus, the class Class and the classes in java.lang.reflect can be used to dynamically create instances of any class.