Module 8: The Java Library - A First Look


What is a library or API?

 
 
How to use a library:
 

Exercise 8.1: Go to the Java API documentation and see if you can find classes related to files.
 

 
Third-party APIs:
 
Making your own library:
 


API overview

 

Java has organized its vast library into packages:

 

We will list a few important ones below, as a quick "tour" of the "core" packages:

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


Java versions

 

Java has evolved over the years. It is sometimes useful to understand the distinction between different versions:

 


Some classes in java.lang

 

Consider the class java.lang.Math:



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



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:



Consider next the class java.lang.Integer:

 


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:

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:

 

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:

 


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:

 

First, consider formatting integers:

 

Next, consider formatting double's:

 

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
 
One option is to use the printf() method in System.out to perform C-like formatting:

public class TestNumber5 {

    public static void main (String[] argv)
    {
	for (int i=0; i<=1000; i+=200) {
	    int sqr = i * i;
	    double root = Math.sqrt (i);
	    System.out.printf ("The square of %5d is %10d and the square root is %6.3f\n", i, sqr, root);
	}
    }
}
 


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?

What it it used for?

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:

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.




© 1998, Rahul Simha (revised 2017)