© Peter Späth and Jeff Friesen 2020
P. Späth, J. FriesenLearn Java for Android Developmenthttps://doi.org/10.1007/978-1-4842-5943-6_8

8. Exploring the Basic APIs, Part 2

Peter Späth1  and Jeff Friesen2
(1)
Leipzig, Sachsen, Germany
(2)
Winnipeg, MB, Canada
 

There are more basic APIs in the java.lang package and also in java.lang.ref, java.lang.reflect, and java.util to consider for your Android apps. For example, you can add timers to your games.

Exploring Random

In Chapter 7, we formally introduced you to the java.lang.Math class’s random() method. If you were to investigate this method’s source code from the perspective of Java 8 or later (at least up to Java 12), you would encounter the following implementation:
public static double random() {
    return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}
...
// class Math
private static final class RandomNumberGeneratorHolder {
   static final Random randomNumberGenerator = new Random();
}

This code excerpt shows you that Math’s random() method is implemented in terms of a class named Random, which is located in the java.util package. Random instances generate sequences of random numbers and are known as random number generators .

Note

These numbers are not truly random because they are generated from a mathematical algorithm. As a result, they are often referred to as pseudorandom numbers . However, it is often convenient to drop the “pseudo” prefix and refer to them as random numbers. Also, delaying object creation (e.g., new Random()) until the first time the object is needed is known as lazy initialization.

Random generates its sequence of random numbers by starting with a special 48-bit value that is known as a seed. This value is subsequently modified by a mathematical algorithm, which is known as a linear congruential generator .

Note

Check out Wikipedia’s “linear congruential generator” entry (http://en.wikipedia.org/wiki/Linear_congruential_generator) to learn about this algorithm for generating random numbers.

Random declares a pair of constructors:
  • Random() creates a new random number generator. This constructor sets the seed of the random number generator to a value that is very likely to be distinct from any other call to this constructor.

  • Random(long seed) creates a new random number generator using its seed argument. This argument is the initial value of the internal state of the random number generator, which is maintained by the protected int next(int bits) method.

Because Random() doesn’t take a seed argument, the resulting random number generator always generates a different sequence of random numbers. This explains why Math.random() generates a different sequence each time an application starts running.

Tip

Random(long seed) gives you the opportunity to reuse the same seed value, allowing the same sequence of random numbers to be generated. You will find this capability useful when debugging a faulty application that involves random numbers.

Random(long seed) calls the void setSeed(long seed) method to set the seed to the specified value. If you call setSeed() after instantiating Random, the random number generator is reset to the state that it was in immediately after calling Random(long seed).

The previous code excerpt demonstrates Random’s double nextDouble() method , which returns the next pseudorandom, uniformly distributed double-precision floating-point value between 0.0 and 1.0 in this random number generator’s sequence.

Random also declares the following methods for returning other kinds of values:
  • boolean nextBoolean() returns the next pseudorandom, uniformly distributed Boolean value in this random number generator’s sequence. Values true and false are generated with (approximately) equal probability.

  • void nextBytes(byte[] bytes) generates pseudorandom byte integer values and stores them in the bytes array. The number of generated bytes is equal to the length of the bytes array.

  • float nextFloat() returns the next pseudorandom, uniformly distributed floating-point value between 0.0 and 1.0 in this random number generator’s sequence.

  • double nextGaussian() returns the next pseudorandom, Gaussian (“normally”) distributed double-precision floating-point value with mean 0.0 and standard deviation 1.0 in this random number generator’s sequence.

  • int nextInt() returns the next pseudorandom, uniformly distributed integer value in this random number generator’s sequence. All 4,294,967,296 possible integer values are generated with (approximately) equal probability.

  • int nextInt(int n) returns a pseudorandom, uniformly distributed integer value between 0 (inclusive) and the specified value (exclusive) drawn from this random number generator’s sequence. All n possible integer values are generated with (approximately) equal probability.

  • long nextLong() returns the next pseudorandom, uniformly distributed long integer value in this random number generator’s sequence. Because Random uses a seed with only 48 bits, this method will not return all possible 64-bit long integer values.

The java.util.Collections class declares a pair of shuffle() methods for shuffling the contents of a list. In contrast, the java.util.Arrays class doesn’t declare a shuffle() method for shuffling the contents of an array. Listing 8-1 addresses this omission.
import java.util.Random;
public class Shuffle {
   public static void main(String[] args) {
      Random r = new Random();
      int[] array = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
      for (int i = 0; i < array.length; i++) {
         int n = r.nextInt(array.length);
         // swap array[i] with array[n]
         int temp = array[i];
         array[i] = array[n];
         array[n] = temp;
      }
      for (int i = 0; i < array.length; i++)
         System.out.print(array[i] + " ");
      System.out.println();
   }
}
Listing 8-1

Shuffling an Array of Integers

Listing 8-1 presents a simple recipe for shuffling an array of integers—this recipe could be generalized. For each array entry from the start of the array to the end of the array, this entry is swapped with another entry whose index is chosen by int nextInt(int n).

When you run this application, you will observe a shuffled sequence of integers that is similar to the following sequence that we observed:
9 0 5 6 2 3 8 4 1 7

Exploring Reflection

Chapter 4 presented two forms of runtime type identification (RTTI). Java’s Reflection API offers a third RTTI form in which applications can dynamically load and learn about loaded classes and other reference types. The API also lets applications instantiate classes, call methods, access fields, and perform other tasks reflectively. This form of RTTI is known as reflection .

Caution

Reflection should not be used indiscriminately. Application performance suffers because it takes longer to perform operations with reflection than without reflection. Also, reflection-oriented code can be harder to read and might jeopardize the original application architecture, and the absence of compile-time type checking can result in runtime failures.

Chapter 6 presented a StubFinder application that used part of the Reflection API to load a class and identify all of the loaded class’s public methods that are annotated with @Stub annotations. This tool is one example where using reflection is beneficial. Another example is the class browser, a tool that enumerates the members of a class.

The Class Entry Point

The java.lang package’s Class class is the entry point into the Reflection API, whose types are stored mainly in the java.lang.reflect package. Class is generically declared as Class<T>, where T identifies the class, interface, enum, or annotation type that’s being modeled by the Class object. T can be replaced by ? (as in Class<?>) when the type being modeled is unknown.

The API documentation at https://docs.oracle.com/javase/8/docs/api lists and describes all of Class’s methods.

Obtaining a Class Object

The forName() method of class Class reveals one way to obtain a Class object. This method loads, links, and initializes a class or interface that isn’t in memory and returns a Class object representing the class or interface. Listing 8-2 demonstrates forName() and some additional methods of use for this class.
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class Investigator {
   public static void main(String[] args) {
      if (args.length != 1) {
         System.err.println("usage: java Investigator classname");
         return;
      }
      Try {
         investigateClass(Class.forName(args[0]), 0);
      } catch (ClassNotFoundException cnfe) {
         System.err.println("could not locate " + args[0]);
      }
   }
   public static void investigateClass(Class<?> clazz, int indentLevel) {
      indent(indentLevel * 3);
      System.out.print(Modifier.toString(clazz.getModifiers()) + " ");
      if (clazz.isEnum())
         System.out.println("enum " + clazz.getName());
      else if (clazz.isInterface()) {
         if (clazz.isAnnotation())
            System.out.print("@");
         System.out.println(clazz.getName());
      } else
         System.out.println(clazz);
      indent(indentLevel * 3);
      System.out.println("{");
      Field[] fields = clazz.getDeclaredFields();
      for (int i = 0; i < fields.length; i++) {
         indent(indentLevel * 3);
         System.out.println("   " + fields[i]);
      }
      Constructor[] constructors = clazz.getDeclaredConstructors();
      if (constructors.length != 0 && fields.length != 0)
         System.out.println();
      for (int i = 0; i < constructors.length; i++) {
         indent(indentLevel * 3);
         System.out.println("   "+constructors[i]);
      }
      Method[] methods = clazz.getDeclaredMethods();
      if (methods.length != 0 &&
          (fields.length != 0 || constructors.length != 0))
         System.out.println();
      for (int i = 0; i < methods.length; i++) {
         indent(indentLevel * 3);
         System.out.println("   "+methods[i]);
      }
      Method[] methodsAll = clazz.getMethods();
      if (methodsAll.length != 0 &&
          (fields.length != 0 || constructors.length != 0 ||
           methods.length != 0))
         System.out.println();
      if (methodsAll.length != 0) {
         indent(indentLevel * 3);
         System.out.println("   ALL PUBLIC METHODS");
         System.out.println();
      }
      for (int i = 0; i < methodsAll.length; i++){
         indent(indentLevel * 3);
         System.out.println("   "+methodsAll[i]);
      }
      Class<?>[] members = clazz.getDeclaredClasses();
      if (members.length != 0 && (fields.length != 0 ||
          constructors.length != 0 || methods.length != 0 ||
          methodsAll.length != 0))
         System.out.println();
      for (int i = 0; i < members.length; i++)
         if (clazz != members[i]) {
            investigateClass(members[i], indentLevel + 1);
            if (i != members.length - 1)
               System.out.println();
         }
      indent(indentLevel * 3);
      System.out.println("}");
   }
   static void indent(int numSpaces) {
      for (int i = 0; i < numSpaces; i++)
         System.out.print(' ');
   }
}
Listing 8-2

Using Reflection to Investigate a Type

Listing 8-2 presents the source code to an investigation tool that uses reflection to obtain information about the command-line argument, which must be a Java reference type (e.g., a class). The investigator outputs the type and name information for a class’s fields, constructors, methods, and nested types. It also outputs the members of interfaces, enums, and annotation types.

After verifying that one command-line argument has been passed to this application, main() calls forName() to return a Class object representing the class or interface identified by this argument.

forName() throws an instance of the checked ClassNotFoundException class when it cannot locate the class’s class file (perhaps the class file was erased before executing the application). It also throws LinkageError when a class’s class file is malformed and ExceptionInInitializerError when a class’s static initialization fails.

Note

ExceptionInInitializerError is often thrown as the result of a class initializer throwing an unchecked exception. For example, the class initializer in the following FailedInitialization class results in ExceptionInInitializerError because someMethod() throws java.lang.NullPointerException:

public class FailedInitialization {
   static {
      ... some code which for example throws a NullPointerException
   }
}

Assuming that forName() is successful, the returned object’s reference is passed to investigateClass(), which introspects the type. (investigateClass() is recursive in that it invokes itself for every encountered nested type.)

The investigateClass() method invokes Class’s getModifiers(), isEnum(), getName(), isInterface(), isAnnotation(), getDeclaredFields(), getDeclaredConstructors(), getDeclaredMethods(), getMethods(), and getDeclaredClasses() methods to return different pieces of information about the loaded class or interface, which is subsequently output.

Much of the printing code focuses on making the output look nice as if it was a source code listing. The code manages indentation and only allows a newline character to be output to separate one section from another; a newline character isn’t output unless content appears before and after the newline.

Compile Listing 8-2 (javac Investigator.java) and run this application, for example, with java.lang.Byte as the solitary command-line argument (java Investigator java.lang.Byte).

Another way to obtain a Class object is to call Object’s getClass() method on an object reference, for example, Employee e = new Employee(); Class<? extends Employee> clazz = e.getClass();. The getClass() method doesn’t throw an exception because the class from which the object was created is already present in memory.

There is one more way to obtain a Class object, and that is to employ a class literal, which is an expression consisting of a class name, followed by a period separator that’s followed by reserved word class. Examples of class literals include Class<Employee> clazz = Employee.class; and Class<String> clazz = String.class.

Perhaps you’re wondering about how to choose between forName(), getClass(), and a class literal. To help you make your choice, the following list compares each competitor:
  • forName() is very flexible in that you can dynamically specify any reference type by its package-qualified name. If the type isn’t in memory, it’s loaded, linked, and initialized. However, lack of compile-time type safety can lead to runtime failures.

  • getClass() returns a Class object describing the type of its referenced object. When called on a superclass variable containing a subclass instance, a Class object representing the subclass type is returned. Because the class is in memory, type safety is assured.

  • A class literal returns a Class object representing its specified class. Class literals are compact and the compiler enforces type safety by refusing to compile the source code when it cannot locate the literal’s specified class.

Note

You can use class literals with primitive types, including void. Examples include int.class, double.class, and void.class. The returned Class object represents the class identified by a primitive type wrapper class’s TYPE field or java.lang.Void.TYPE. For example, each of int.class == java.lang.Integer.TYPE and void.class == Void.TYPE evaluates to true.

You can also use class literals with primitive type–based arrays. Examples include int[].class and double[].class. For these examples, the returned Class objects represent Class<int[]> and Class<double[]>.

Instantiating a Dynamically Loaded Class

One of the Class’ API not yet further investigated is newInstance() , which is useful for instantiating a dynamically loaded class provided that the class has a noargument constructor. The following code fragment demonstrates this:
try {
   Class<?> clazz = Class.forName("codecs.AVI");
   Codec codec = (Codec) clazz.newInstance();
   VideoPlayer player = new VideoPlayer(codec);
   player.play("movie.avi");
} catch( ClassNotFoundException
      | IllegalAccessException
      | InstantiationException e) {
   e.printStackTrace(System.err);
}

This code fragment uses Class.forName() to attempt to load a hypothetical Audio Video Interleave (AVI) compressor/decompressor (codec), which is stored as AVI.class in the codecs package. If successful, the codec is instantiated via the newInstance() method. A hypothetical VideoPlayer class is instantiated and its instance is initialized to the codec, and the player is told to play the contents of movie.avi, which was encoded via this codec.

forName() throws ClassNotFoundException when it cannot find AVI.class. newInstance() throws IllegalAccessException when it cannot locate a noargument constructor in AVI.class and InstantiationException when instantiation fails (perhaps the class file describes an interface or an abstract class).

Constructor, Field, and Method

Instances of java.lang.reflect.Constructor, Field, and Method (same package) represent a class’s constructors and a class’s or an interface’s fields and methods.

Constructor represents a constructor and is generically declared as Constructor<T> where T identifies the class in which the constructor represented by Constructor is declared. Constructor declares various methods you can learn about by looking at the API documentation.

Tip

If you want to instantiate a class via a constructor that takes arguments, you cannot use Class’s newInstance() method. Instead, you must use Constructor’s T newInstance(Object... initargs) method to perform this task. Unlike Class’s newInstance() method, which bypasses the compile-time exception checking that would otherwise be performed by the compiler, Constructor’s newInstance() method avoids this problem by wrapping any exception thrown by the constructor in an instance of the java.lang.reflect.InvocationTargetException class.

Field represents a field and declares various methods which allow to investigate fields.

Method get() returns the value of any type of field. In contrast, the other get*() methods return the values of specific types of fields. These methods throw a NullPointerException instance when object is null and the field is an instance field, an IllegalArgumentException instance when object is not an instance of the class or interface declaring the underlying field (or not an instance of a subclass or interface implementor), and an IllegalAccessException instance when the underlying field cannot be accessed (perhaps the field is private).

Listing 8-3 demonstrates Field’s getInt(Object) and getDouble(Object) methods along with their void setInt(Object obj, int i) and void setDouble(Object obj, double d) counterparts.
import java.lang.reflect.Field;
class X {
   public int i = 10;
   public static final double PI = 3.14;
}
public class FieldAccessDemo {
   public static void main(String[] args) {
      try {
         Class<?> clazz = Class.forName("X");
         X x = (X) clazz.newInstance();
         Field f = clazz.getField("i");
         System.out.println(f.getInt(x)); // Output: 10
         f.setInt(x, 20);
         System.out.println(f.getInt(x)); // Output: 20
         f = clazz.getField("PI");
         System.out.println(f.getDouble(null)); // Output: 3.14
         f.setDouble(x, 20);
         System.out.println(f.getDouble(null)); // Never executed
      } catch (Exception e) {
         System.err.println(e);
      }
   }
}
Listing 8-3

Reflectively Getting and Setting the Values of Instance and Class Fields

FieldAccessDemo’s main() method first attempts to load X and then tries to instantiate this class via newInstance(). If successful, the instance is assigned to reference variable x.

main() next invokes Class’s Field getField(String name) method to return a Field instance that represents the public field identified by name, which happens to be i (in the first case) and PI (in the second case). This method throws java.lang.NoSuchFieldException when the named field doesn’t exist.

Continuing, main() invokes Field’s getInt() and setInt() methods (with an object reference) to get the instance field’s initial value, change this value to another value, and get the new value. The initial and new values are output.

At this point, main() demonstrates class field access in a similar manner. However, it passes null to getInt() and setInt() because an object reference isn’t required to access a class field. Because PI is declared final, the call to setInt() results in a thrown instance of the IllegalAccessException class.

Note

For simplicity we’ve specified catch(Exception e) to avoid having to specify multiple catch blocks.

Method represents a method and declares various methods which we can use to learn about a method.

Listing 8-4 demonstrates Method’s invoke(Object, Object...) method.
import java.lang.reflect.Method;
class X {
   public void objectMethod(String arg) {
      System.out.println("Instance method: " + arg);
   }
   public static void classMethod() {
      System.out.println("Class method");
   }
}
public class MethodInvocationDemo {
   public static void main(String[] args) {
      try {
         Class<?> clazz = Class.forName("X");
         X x = (X) clazz.newInstance();
         Class[] argTypes = { String.class };
         Method method = clazz.getMethod("objectMethod", argTypes);
         Object[] data = { "Hello" };
         method.invoke(x, data); // Output: Instance method: Hello
         method = clazz.getMethod("classMethod", (Class<?>[]) null);
         method.invoke(null, (Object[]) null); // Output: Class method
      } catch (Exception e) {
         System.err.println(e);
      }
   }
}
Listing 8-4

Reflectively Invoking Instance and Class Methods

Listing 8-4 declares classes X and MethodInvocationDemo. MethodInvocationDemo’s main() method first attempts to load X and then tries to instantiate this class via newInstance(). When successful, the instance is assigned to reference variable x.

main() next creates a one-element Class array that describes the types of objectMethod()’s parameter list. This array is used in the subsequent call to Class’s Method getMethod(String name, Class<?>... parameterTypes) method to return a Method object for invoking a public method named objectMethod with this parameter list. This method throws java.lang.NoSuchMethodException when the named method doesn’t exist.

Continuing, main() creates an Object array that specifies the data to be passed to the method’s parameters; in this case, the array consists of a single String argument. It then reflectively invokes objectMethod() by passing this array along with the object reference stored in x to the invoke() method.

At this point, main() shows you how to reflectively invoke a class method. The (Class<?>[]) and (Object[]) casts are used to suppress warning messages that have to do with variable numbers of arguments and null references. Notice that the first argument passed to invoke() is null when invoking a class method.

Accessibility of Objects

The java.lang.reflect.AccessibleObject class is the superclass of Constructor, Field, and Method. This superclass provides annotation-related methods, and methods for reporting a constructor’s, field’s, or method’s accessibility (is it private?) and making an inaccessible constructor, field, or method accessible. Please look at the API documentation for all AccessibleObject’s methods.

Package

Another class of the reflection API is Package, a class that provides information about a package (see Chapter 5 for an introduction to packages). This information includes version details about the implementation and specification of a Java package, the name of the package, and an indication of whether or not the package has been sealed (all classes that are part of the package are archived in the same JAR file).

We have created a PackageInfo application that demonstrates many of the Package class’s methods. Listing 8-5 presents this application’s source code.
public class PackageInfo {
   public static void main(String[] args) {
      if (args.length == 0) {
         System.err.println("usage: java PackageInfo packageName [version]");
         return;
      }
      Package pkg = Package.getPackage(args[0]);
      if (pkg == null) {
         System.err.println(args[0] + " not found");
         return;
      }
      System.out.println("Name: " + pkg.getName());
      System.out.println("Implementation title: " +
                         pkg.getImplementationTitle());
      System.out.println("Implementation vendor: " +
                         pkg.getImplementationVendor());
      System.out.println("Implementation version: " +
                         pkg.getImplementationVersion());
      System.out.println("Specification title: " +
                         pkg.getSpecificationTitle());
      System.out.println("Specification vendor: " +
                         pkg.getSpecificationVendor());
      System.out.println("Specification version: " +
                         pkg.getSpecificationVersion());
      System.out.println("Sealed: " + pkg.isSealed());
      if (args.length > 1)
         System.out.println("Compatible with " + args[1] + ": " +
                            pkg.isCompatibleWith(args[1]));
   }
}
Listing 8-5

Obtaining Information About a Package

After compiling Listing 8-5 (javac PackageInfo.java), specify at least a package name on the command line. For example, java PackageInfo java.lang returns the following output under Java 8:
Name: java.lang
Implementation title: Java Runtime Environment
Implementation vendor: Oracle Corporation
Implementation version: 1.8.0_101
Specification title: Java Platform API Specification
Specification vendor: Oracle Corporation
Specification version: 1.8
Sealed: false

PackageInfo also lets you determine if the package’s specification is compatible with a specific version number. A package is compatible with its predecessors.

For example, java PackageInfo java.lang 1.6 outputs Compatible with 1.6: true, whereas java PackageInfo java.lang 1.8 outputs Compatible with 1.8: false.

You can also use PackageInfo with your own packages, which you learned to create in Chapter 5. For example, that chapter presented a logging package.

Copy PackageInfo.class into the directory containing the logging package directory (which contains the compiled class files), and execute the following command:
java PackageInfo logging
PackageInfo responds by displaying the following output:
logging not found

This error message is presented because getPackage() requires at least one class file to be loaded from the package before it returns a Package object describing that package.

The only way to eliminate the previous error message is to load a class from the package. Accomplish this task by merging the following code fragment into Listing 8-5:
if (args.length == 3) try {
   Class.forName(args[2]);
} catch (ClassNotFoundException cnfe) {
   System.err.println("cannot load " + args[2]);
   return;
}

This code fragment, which must precede Package pkg = Package.getPackage(args[0]);, loads the class file named by the revised PackageInfo application’s third command-line argument.

Run the new PackageInfo application via java PackageInfo logging 1.5 logging.File and you will observe the following output, provided that File.class exists (you need to compile this package before specifying this command line)—this command line identifies logging’s File class as the class to load:
Name: logging
Implementation title: null
Implementation vendor: null
Implementation version: null
Specification title: null
Specification vendor: null
Specification version: null
Sealed: false
Exception in thread "main" java.lang.NumberFormatException: Empty version string
      at java.lang.Package.isCompatibleWith(Unknown Source)
      at PackageInfo.main(PackageInfo.java:41)

It’s not surprising to see all of these null values because no package information has been added to the logging package. Also, NumberFormatException is thrown from isCompatibleWith() because the logging package doesn’t contain a specification version number in dotted form (it is null).

Perhaps the simplest way to place package information into the logging package is to create a logging.jar file in a similar manner to the example shown in Chapter 5. But first, you must create a small text file that contains the package information. You can choose any name for the file. Listing 8-6 reveals our choice of manifest.mf.
Implementation-Title: Logging Implementation
Implementation-Vendor: Jeff Friesen
Implementation-Version: 1.0a
Specification-Title: Logging Specification
Specification-Vendor: Jeff "JavaJeff" Friesen
Specification-Version: 1.0
Sealed: true
Listing 8-6

manifest.mf Containing the Package Information

Note

Make sure to press the Return/Enter key at the end of the final line (Sealed: true). Otherwise, you will probably observe Sealed: false in the output because this entry will not be stored in the logging package by the JDK’s jar tool; jar is a bit quirky.

Execute the following command line to create a JAR file that includes logging and its files, and whose manifest, a special file named MANIFEST.MF that stores information about the contents of a JAR file, contains the contents of Listing 8-6:
jar cfm logging.jar manifest.mf logging
Alternatively, specify one of the following slightly longer command lines, which are equivalent to the former command line:
jar cfm logging.jar manifest.mf logging*.class
jar cfm logging.jar manifest.mf logging/*.class

Either command line creates a JAR file named logging.jar (via the c [create] and f [file] options). It also merges the contents of manifest.mf (via the m [manifest] option) into MANIFEST.MF, which is stored in the package’s/JAR’s file META-INF directory.

Note

To learn more about a JAR file’s manifest, read the “JAR Manifest” section of the JDK documentation’s “JAR File Specification” page (http://docs.oracle.com/javase/7/docs/technotes/guides/jar/jar.html#JAR_Manifest).

Assuming that the jar tool presents no error messages, execute the following Windows-oriented command line (or a command line suitable for your platform) to run PackageInfo and extract the package information from the logging package:
java -cp logging.jar;. PackageInfo logging 1.0 logging.File

The -cp command-line option lets you specify the classpath, which consists of logging.jar and the current directory (represented by the dot [.] character). Fail to specify the dot and java outputs an error message complaining that it cannot locate PackageInfo.class.

This time, you should see the following output:
Name: logging
Implementation title: Logging Implementation
Implementation vendor: Jeff Friesen (IV)
Implementation version: 1.0a
Specification title: Logging Specification
Specification vendor: Jeff Friesen (SV)
Specification version: 1.0
Sealed: true
Compatible with 1.0: true

Array

The java.lang.reflect package also includes an Array class whose class methods make it possible to reflectively create and access Java arrays. Listing 8-7 provides a demonstration.
import java.lang.reflect.Array;
public class ArrayDemo {
   public static void main(String[] args) {
      String[] argsCopy = (String[]) Array.newInstance(
            String.class, args.length);
      for (int i = 0; i < args.length; i++)
         Array.set(argsCopy, i, args[i]);
      for (int i = 0; i < args.length; i++)
         System.out.println(Array.get(argsCopy, i));
   }
}
Listing 8-7

Reflectively Creating and Accessing an Array

Listing 8-7 first invokes Array’s Object newInstance(Class<?> componentType, int length) class method to create an array that can store String objects. It then copies all passed String arguments to this array, invoking Array’s void set(Object array, int index, Object value) class method to store each object. Finally, it retrieves each stored object by invoking Array’s Object get(Object array, int index) class method.

Compile Listing 8-7 (javac ArrayDemo.java) and run this application. For example, consider the following command:
java ArrayDemo a b c
This command generates the following output:
a
b
c

Exploring StringTokenizer

You’ll occasionally want to extract a string’s individual components. For example, you have the string "int x = 4;" and want to extract int, x, =, 4, and ; separately. Alternatively, you might have a comma-separated values (CSV) file where each line consists of multiple data items separated by commas and you want to read each value separately.

Note

The procedure described in this section is not able to handle general CSV files with escaped delimiters. For that aim you can use one of the generally available CSV parsing libraries.

The task of breaking up a string into its individual components is known as parsing or tokenizing . The components themselves are known as tokens. (Technically, a token is a category, such as identifier, and the component is known as a lexeme, such as int.)

Java 1.0 introduced the java.util.StringTokenizer class to let applications tokenize strings. StringTokenizer lets you specify a string to be tokenized and a set of delimiters that separate successive tokens. This class declares three constructors for specifying these items:
  • StringTokenizer(String str) constructs a string tokenizer for the specified string. The tokenizer uses the default delimiter set, which is “ f”: the space character, the tab character, the newline character, the carriage return character, and the form feed character. Delimiter characters themselves won’t be treated as tokens. This constructor throws NullPointerException when you pass null to str.

  • StringTokenizer(String str, String delim) constructs a string tokenizer for the specified string. The characters in the delim argument are the delimiters for separating tokens. Delimiter characters themselves won’t be treated as tokens. This constructor throws NullPointerException when you pass null to str. Although it doesn’t throw an exception when you pass null to delim, trying to invoke other methods on the resulting StringTokenizer instance may result in NullPointerException.

  • StringTokenizer(String str, String delim, boolean returnDelims) constructs a string tokenizer for the specified string. All characters in the delim argument are the delimiters for separating tokens. When the returnDelims flag is true, the delimiter characters are also returned as tokens. Each delimiter is returned as a string of length one. When returnDelims is false, the delimiter characters are skipped and only serve as separators between tokens. This constructor throws NullPointerException when you pass null to str. Although it doesn’t throw an exception when you pass null to delim, invoking other methods on the resulting StringTokenizer instance may result in NullPointerException.

For all the methods the StringTokenizer provides, please consult the API documentation.

You would typically use StringTokenizer in a loop context, as follows:
StringTokenizer st = new StringTokenizer("this is a test");
while (st.hasMoreTokens())
   System.out.println(st.nextToken());
This loop uses the standard set of delimiters. It generates the following output:
this
is
a
test
Alternatively, you could specify the following loop context:
StringTokenizer st = new StringTokenizer("this is a test");
Enumeration<String> e = (Enumeration) st;
while (e.hasMoreElements())
   System.out.println(e.nextElement());
Note

StringTokenizer implements Enumeration so that you can create common code for enumerating tokens and legacy vector/hashtable (see Chapter 9) content.

Although you’ll find StringTokenizer easy to use in your Android apps or non-Android Java programs, Java provides a more powerful alternative in the form of regular expressions (discussed in Chapter 14). To give you a taste for the power of regular expressions, you can easily bypass the previous loops for obtaining tokens by employing the following code fragment:
String[] values = "this is a test".split("\s");

The String class declares String[] split(String regex) and String[] split(String regex, int limit) methods that let you split a string into components that are separated by delimiters identified by the specified regular expression (regex). For example, \s represents the s regular expression (backslashes must be doubled when placed in string literals), which stands for whitespace character. The string is split around whitespace character delimiters.

If you were to specify the following loop
for (int i = 0; i < values.length; i++)
   System.out.println(values[i]);
you would observe the same output as for the preceding StringTokenizer code.

Exploring Timer and TimerTask

It’s often necessary to schedule a task (a unit of work) for one-shot execution (the task runs only once) or for repeated execution at regular intervals. The java.util.Timer and java.util.TimerTask classes provide a way to accomplish task scheduling.

Note

Android apps can use Timer and TimerTask, but for many scheduling tasks in Android, it is better to use a Handler or the AlarmManager. Here we describe Java’s Timer and TimerTask in case the Android classes don’t suit your needs.

Timer provides a facility for scheduling TimerTasks for future execution on a background thread. Timer tasks may be scheduled for one-shot execution or for repeated execution at regular intervals. Before delving into the internals of these classes, check out Listing 8-8.
import java.util.Timer;
import java.util.TimerTask;
public class TimerDemo {
   public static void main(String[] args) {
      TimerTask task = new TimerTask(){
                  @Override
                  public void run() {
                     System.out.println(System.currentTimeMillis());
                  }
               };
      Timer timer = new Timer();
      timer.schedule(task, 0, 1000);
   }
}
Listing 8-8

Displaying the Current Millisecond Value at Approximately 1-Second Intervals

Listing 8-8 describes an application that outputs the current time (in milliseconds) approximately every second. It first instantiates a TimerTask subclass (in this case, an anonymous class is used) whose overriding run() method outputs the time. It then instantiates Timer and invokes its schedule() method with this task as the first argument. The second and third arguments indicate that the task is scheduled for repeated execution after no initial delay and every 1000 milliseconds.

Compile Listing 8-8 (javac TimerDemo.java) and run this application (java TimerDemo). You should observe output that’s similar to the following truncated output:
1380933893664
1380933894666
1380933895668
1380933896668
1380933897670
1380933898672

For more details about Timer and TimerTask, please consult the API documentation. There we learn how to schedule one-shot executions, and executions with fixed delay or at fixed rate, and how to cancel times.

Exercises
The following exercises are designed to test your understanding of Chapter 8’s content:
  1. 1.

    What does the Random class accomplish?

     
  2. 2.

    What are some of the capabilities offered by the Reflection API?

     
  3. 3.

    Reflection should not be used indiscriminately. Why not?

     
  4. 4.

    Identify the class that is the entry point into the Reflection API.

     
  5. 5.

    True or false: All of the Reflection API is contained in the java.lang.reflect package?

     
  6. 6.

    What are the three ways to obtain a Class object?

     
  7. 7.

    True or false: You can use class literals with primitive types.

     
  8. 8.

    How do you instantiate a dynamically loaded class?

     
  9. 9.

    What method do you invoke to obtain a constructor’s parameter types?

     
  10. 10.

    What does Class’s Field getField(String name) method do when it cannot locate the named field?

     
  11. 11.

    How do you determine if a method is declared to receive a variable number of arguments?

     
  12. 12.

    True or false: You can reflectively make a private method accessible.

     
  13. 13.

    What is the purpose of Package’s isSealed() method?

     
  14. 14.

    True or false: getPackage() requires at least one class file to be loaded from the package before it returns a Package object describing that package.

     
  15. 15.

    How do you reflectively create and access a Java array?

     
  16. 16.

    What is the purpose of the StringTokenizer class?

     
  17. 17.

    Identify the standard class library’s convenient and simpler alternative to the Threads API for scheduling task execution.

     
  18. 18.

    Create a Guess application that uses Random to generate a random integer from 0 to 25. Then, the application repeatedly asks the user to guess which integer was chosen by entering a letter from a through z. The application continues by comparing the entered letter with the random integer (which is offset by lowercase letter a) to learn if the user’s choice was too low, too high, or just right, and outputs an appropriate message.

     
  19. 19.

    Create a Classify application that uses Class’s forName() method to load its single command-line argument, which will represent a package-qualified annotation type, enum, interface, or class (the default). Use a chained if-else statement along with the appropriate Class methods and System.out.println() to identify the type and output Annotation, Enum, Interface, or Class.

     
  20. 20.

    Create a Tokenize application that uses StringTokenizer to extract the month, day, year, hour, minute, and second fields from the string 03-12-2014 03:05:20, which are then output.

     
  21. 21.

    Create a BackAndForth application that uses Timer and TimerTask to repeatedly move an asterisk forward 20 steps and then backward 20 steps. The asterisk is output via System.out.print().

     

Summary

The Math class’s random() method is implemented in terms of the Random class, whose instances are known as random number generators. Random generates a sequence of random numbers by starting with a special 48-bit seed. This value is subsequently modified via a mathematical algorithm that is known as a linear congruential generator.

Random declares a pair of constructors, a setSeed() method for setting the random number generator’s seed, and several “next”-prefixed methods that return the next number in the sequence as a Boolean value, an integer, a long integer, a floating-point value, or a double-precision floating-point value. There is even a method for generating a sequence of random bytes.

Java’s Reflection API offers a third RTTI form in which applications can dynamically load and learn about loaded classes and other reference types. The API also lets applications instantiate classes, call methods, access fields, and perform other tasks reflectively. This form of RTTI is known as reflection or introspection.

The java.lang package’s Class class is the entry point into the Reflection API, whose types are stored mainly in the java.lang.reflect package. You can obtain a Class object by invoking Class’s forName() method, by invoking Object’s getClass() method, or by using a class literal, which is the name of a class followed by a .class suffix.

The java.lang.reflect package declares Constructor, Field, and Method classes that represent constructors, fields, and methods. Each of these classes extends the AccessibleObject class, which provides methods for determining if the constructor, field, or method is accessible, and for changing the constructor’s, field’s, or method’s accessibility.

The Package class provides access to package information. This information includes version information about the implementation and specification of a Java package, the package’s name, and an indication of whether the package is sealed or not. Also, the Array class provides class methods that make it possible to reflectively create and access Java arrays.

You’ll occasionally want to extract a string’s individual components. The task of breaking up a string into its individual components is known as parsing or tokenizing. The components themselves are known as tokens. Java 1.0 introduced the StringTokenizer class to let applications tokenize strings.

StringTokenizer provides constructors for specifying the string to be tokenized and the set of delimiters that separate successive tokens. The number of tokens can be obtained by invoking the countTokens() method.

The hasMoreElements() and hasMoreTokens() methods tell you whether there are more tokens to extract. The nextToken() and nextElement() methods return the next token. One of the nextToken() methods also lets you specify a new set of delimiters.

Timer provides a facility for scheduling TimerTasks for future execution on a background thread. Timer tasks may be scheduled for one-shot execution or for repeated execution at regular intervals. Corresponding to each Timer object is a single background thread that’s used to execute all of the timer tasks sequentially.

This chapter completes our tour of Java’s basic APIs. Chapter 9 explores Java’s Collections Framework and classic collections APIs.

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

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