Chapter 12

Portable Software and the java.lang Package


CONTENTS


In this you'll learn how to use the java.lang package. This package contains the core API classes of the JDK. It includes the Object class, which is the top class in the Java class hierarchy, and the Class class, which provides runtime class information for all Java objects. You'll learn about classes that control the operation of the Java runtime system and about the all-important System class. You'll also learn how "wrapped" classes are used to convert primitive data types into usable objects. By the time you have completed this , you will have been introduced to all the classes contained in the java.lang package.

Note
The objective of this part of the book is to familiarize you with each of the packages of the Java API. In order to make the best use of these s, you should browse through the pages of the API User's Guide that discuss each of the classes as they are covered here.

The Object and Class Classes

Object and Class are two of the most important classes in the Java API. The Object class is at the top of the Java class hierarchy. All classes are subclasses of Object and therefore inherit its methods. The Class class is used to provide class descriptors for all objects created during Java program execution.

Object

The Object class does not have any variables and has only one constructor. However, it provides 11 methods that are inherited by all Java classes and that support general operations that are used with all objects. For example, the equals() and hashCode() methods are used to construct hash tables of Java objects. Hash tables are like arrays, but they are indexed by key values and dynamically grow in size. They make use of hash functions to quickly access the data that they contain. The hashCode() method creates a hash code for an object. Hash codes are used to quickly determine whether two objects are different. You learn more about hash tables in Chapter 14, "Useful Tools in the java.util Package."

The clone() method creates an identical copy of an object. The object must implement the Cloneable interface. This interface is defined within the java.lang package. It contains no methods and is used only to differentiate cloneable from noncloneable classes.

The getClass() method identifies the class of an object by returning an object of Class. You'll learn how to use this method in the next programming example. (See the "A Touch of Class" section.)

The toString() method creates a String representation of the value of an object. This method is handy for quickly displaying the contents of an object. When an object is displayed, using print() or println(), the toString() method of its class is automatically called to convert the object into a string before printing. Classes that override the toString() method can easily provide a custom display for their objects.

The finalize() method of an object is executed when an object is garbage-collected. The method performs no action, by default, and needs to be overridden by any class that requires specialized finalization processing.

The Object class provides three wait() and two notify() methods that support thread control. These methods are implemented by the Object class so that they can be made available to threads that are not created from subclasses of class Thread. The wait() methods cause a thread to wait until it is notified or until a specified amount of time has elapsed. The notify() methods are used to notify waiting threads that their wait is over.

Class

The Class class provides eight methods that support the runtime processing of an object's class and interface information. This class does not have a constructor. Objects of this class, referred to as class descriptors, are automatically created and associated with the objects to which they refer. Despite their name, class descriptors are used for interfaces as well as classes.

The getName() and toString() methods return the String containing the name of a class or interface. The toString() method differs in that it prepends the string class or interface, depending on whether the class descriptor is a class or an interface. The static forName() method is used to obtain a class descriptor for the class specified by a String object.

The getSuperclass() method returns the class descriptor of the superclass of a class. The isInterface() method identifies whether a class descriptor applies to a class or an interface. The getInterface() method returns an array of Class objects that specify the interfaces of a class, if any.

The newInstance() method creates an object that is a new instance of the specified class. It can be used in lieu of a class's constructor, although it is generally safer and clearer to use a constructor rather than newInstance().

The getClassLoader() method returns the class loader of a class, if one exists. Classes are not usually loaded by a class loader. However, when a class is loaded from outside the CLASSPATH, such as over a network, a class loader is used to convert the class byte stream into a class descriptor. The ClassLoader class is covered later in this .

A Touch of Class

In order to give you a feel for how the Object and Class methods can be used, let's create and run a small program called ClassApp. If you have not already done so, create a ch12 directory to be used for this lesson. The program's source code is shown in Listing 12.1.


Listing 12.1. The source code of the ClassApp program.

import java.lang.System;
import java.lang.Class;
import jdg.ch05.Point;
import jdg.ch06.CGTextPoint;

public class ClassApp {
 public static void main(String args[]) {
  CGTextPoint p = new CGTextPoint(new Point(7,11));
  Object obj = new Object();
  Class cl = p.getClass();
  Class objcl = obj.getClass();
  do {
   describeClass(cl);
   cl = cl.getSuperclass();
  } while(cl!=objcl);
 }
 public static void describeClass(Class classDesc){
  System.out.println("Class: "+classDesc.getName());
  System.out.println("Superclass: "+classDesc.getSuperclass().getName());
  Class interfaces[] = classDesc.getInterfaces();
  for(int i=0;i<interfaces.length;++i)
   System.out.println("has interface: "+interfaces[i].getName());
  System.out.println();
 }
}


The program shows how the Object and Class methods can be used to generate runtime class and interface information about an arbitrary object. It creates an instance of class CGTextPoint by importing the classes developed in Chapters 5, "Classes and Objects," and 6, "Interfaces." It also creates a generic instance of class Object in order to obtain the class descriptor of that class. The following lines of code use the Object getClass() method to obtain the class descriptors of the CGTextPoint and Object classes:

Class cl = p.getClass();
Class objcl = obj.getClass();

These class descriptors are instances of Class. They are used in a simple do loop. The loop invokes the describeClass() method for the class identified by cl and then assigns cl to its superclass. The loop repeats until cl becomes the class descriptor for Object.

The describeClass() method uses the getName() method to get the name of the class and its superclass. The describeClass() method displays this information to the console. It uses the getInterfaces() method to get all interfaces implemented by a class and the getName() method to get and display the name of each interface.

The program's output is as follows:

Class: jdg.ch06.CGTextPoint
Superclass: jdg.ch05.CGPoint
has interface: jdg.ch06.CGTextEdit

Class: jdg.ch05.CGPoint
Superclass: jdg.ch05.CGObject

Class: jdg.ch05.CGObject
Superclass: java.lang.Object

It steps up the class hierarchy from CGTextPoint to CGObject to display information about each class. See if you can modify the program to work with objects of other classes. You can do this by assigning the class of these objects to the cl variable in the main() method.

The ClassLoader, SecurityManager, and Runtime Classes

The ClassLoader, SecurityManager, and Runtime classes provide a fine level of control over the operation of the Java runtime system. However, most of the time you will not need or want to exercise this control because Java is set up to perform optimally for a variety of applications. The ClassLoader class allows you to define custom loaders for classes that you load outside of your CLASSPATH-for example, over a network. The SecurityManager class allows you to define a variety of security policies that govern the accesses that classes may make to threads, executable programs, your network, and your file system. The Runtime class provides you with the capability to control and monitor the Java runtime system. It also allows you to execute external programs.

ClassLoader

Classes that are loaded from outside CLASSPATH require a class loader to convert the class byte stream into a class descriptor. ClassLoader is an abstract class that is used to define class loaders. It uses the defineClass() method to convert an array of bytes into a class descriptor. The loadClass() method is used to load a class from its source, usually a network. The resolveClass() method resolves all the classes referenced by a particular class by loading and defining those classes. The findSystemClass() method is used to load classes that are located within CLASSPATH and, therefore, do not require a class loader.

SecurityManager

The SecurityManager class is an abstract class that works with class loaders to implement a security policy. It contains several methods that can be overridden to implement customized security policies. This class is covered in Chapter 39, "Java Security," which gets into the details of Java security. For right now, just be aware that it is in java.lang.

Runtime

The Runtime class provides access to the Java runtime system. It consists of a number of methods that implement system-level services.

The getRuntime() method is a static method that is used to obtain access to an object of class Runtime. The exec() methods are used to execute external programs from the Java runtime system. The exec() methods provide a number of alternatives for passing parameters to the executed program. These alternatives are similar to the standard C methods for passing command-line and environment information. The exec() methods are subject to security checking to ensure that they are executed by trusted code. See Chapter 39 for more information about runtime security checking.

The exit() method is used to exit the Java runtime system with an error code. It is similar to the exit function found in standard C libraries.

The totalMemory(), freeMemory(), and gc() methods are used to obtain information about and control the memory used by the runtime system. The totalMemory() method identifies the total memory available to the runtime system. The freeMemory() method identifies the amount of free (unused) memory. The gc() method is used to run the garbage collector to free up memory allocated to objects that are no longer being used. In general, you should not use the gc() method, but rather let Java perform its own automated garbage collection.

The getLocalizedInputStream() and getLocalizedOutputStream() methods are used to convert local (usually ASCII) input and output streams to Unicode-based streams.

The load() and loadLibrary() methods are used to load dynamic link libraries. This is usually performed in conjunction with native methods, which are described in Chapter 38, "Creating Native Methods."

The runFinalization() method causes the finalize() method of each object awaiting finalization to be invoked. The traceInstructions() and traceMethodCalls() methods are used to enable or disable instruction and method tracing. You will most likely never need to use any of these methods in your programs. They are used in programs such as the debugger to trace through the execution of Java methods and instructions.

Using Runtime

Most of the methods provided by Runtime are not typically used in application programs. However, some methods are pretty useful. The program in Listing 12.2 shows how the Runtime methods can be used to display memory status information.


Listing 12.2. The source code of the RuntimeMemApp program.

import java.lang.System;
import java.lang.Runtime;
import java.io.IOException;

public class RuntimeMemApp {
 public static void main(String args[]) throws IOException {
  Runtime r = Runtime.getRuntime();
  System.out.println(r.totalMemory());
  System.out.println(r.freeMemory());
 }
}


This program uses the static getRuntime() method to get an instance of Runtime that represents the current Java runtime system. The totalMemory() method is used to display the total number of bytes of runtime system memory. The freeMemory() method is used to display the number of bytes of memory that are unallocated and currently available.

When you run the program, you should get results that are similar to the following:

3145720
3135888

Listing 12.3 demonstrates how to use the Runtime exec() method to execute external programs. This example assumes that you are using Windows 95. It will not work with any other Java implementation. However, it can be easily tailored to launch application programs on other operating-system platforms.


Listing 12.3. The source code of the RuntimeExecApp program.

import java.lang.System;
import java.lang.Runtime;
import java.io.IOException;

public class RuntimeExecApp {
 public static void main(String args[]) throws IOException {
  Runtime r = Runtime.getRuntime();
  r.exec("C:\\Windows\\Explorer.exe");
 }
}


This program uses getRuntime() to get the current instance of the runtime system and then uses exec() to execute the Windows Explorer. The double backslashes (\\) are Java escape codes for a single backslash (\). When you run this program, it should launch a copy of the Windows Explorer. Under Windows 95, the exec() function works with true Win32 programs. It cannot be used to execute built-in DOS commands.

The System Class

You are no stranger to the System class because you have used it in several previous programming examples. It is one of the most important and useful classes provided by java.lang. It provides a standard interface to common system resources and functions. It implements the standard input, output, and error streams, and supplies a set of methods that provide control over the Java runtime system. Some of these methods duplicate those provided by the Runtime class.

Property-Related Methods

The System class provides three property-related methods. Properties are extensions of the Dictionary and Hashtable classes and are defined in the java.util package. A set of system properties is available through the System class that describes the general characteristics of the operating system and runtime system that you are using. The getProperties() method gets all of the system properties and stores them in an object of class Properties. The getProperty(String) method gets a single property, as specified by a key. The setProperties() method sets the system properties to the values of a Properties object. The sample program presented in Listing 12.4 introduces you to these system properties.

Security Manager-Related Methods

The getSecurityManager() and setSecurityManager() methods provide access to the security manager that is currently in effect. These methods are covered in Chapter 39.

Runtime-Related Methods

Several of the methods defined for the Runtime class are made available through the System class. These methods are exit(), gc(), load(), loadLibrary(), and runFinalization().

Odds and Ends

The arraycopy() method is used to copy data from one array to another. This function provides the opportunity for system-specific memory-copy operations to optimize memory-to-memory copies.

The currentTimeMillis() method returns the current time in milliseconds since January 1, 1970. If you want more capable date and time methods, check out the Date class in java.util.

The getenv() method is used to obtain the value of an environment variable. However, this method is identified as obsolete in the Java API documentation and can no longer be used.

Time and Properties

The short program in Listing 12.4 illustrates a few of the methods provided by the System class. If your heyday was in the 1960s, it will allow you to keep track of the number of milliseconds that have elapsed since the good old days. It also gets and displays the System properties. Take a look through these properties to get a feel for the type of information that is provided. Finally, the exit() method is used to terminate the program, returning a status code of 13.


Listing 12.4. The source code of the SystemApp program.

import java.lang.System;
import java.util.Properties;

public class SystemApp {
 public static void main(String args[]) {
  long time = System.currentTimeMillis();
  System.out.print("Milliseconds elapsed since January 1, 1970: ");
  System.out.println(time);
  Properties p=System.getProperties();
  p.list(System.out);
  System.exit(13);
 }
}


The program generated the following output on my computer:

Milliseconds elapsed since January 1, 1970: 825298554460
-- listing properties --
java.home=C:\JAVA\BIN\..
awt.toolkit=sun.awt.win32.MToolkit
java.version=1.0
file.separator=\
line.separator=

java.vendor=Sun Microsystems Inc.
user.name=jamie
os.arch=x86
os.name=Windows 95
java.vendor.url=http://www.sun.com/
user.dir=c:\java\jdg\ch12
java.class.path=.;c:\java;c:\java\lib\classes.zip;C:\...
java.class.version=45.3
os.version=4.0
path.separator=;
user.home=\home\jamie

Wrapped Classes

Variables that are declared using the primitive Java types are not objects and cannot be created and accessed using methods. Primitive types also cannot be subclassed. To get around the limitations of primitive types, the java.lang package defines class wrappers for these types. These class wrappers furnish methods that provide basic capabilities such as class conversion, value testing, hash codes, and equality checks. The constructors for the wrapped classes allow objects to be created and converted from primitive values and strings. Be sure to browse the API pages for each of these classes to familiarize yourself with the methods they provide.

The Boolean Class

The Boolean class is a wrapper for the boolean primitive type. It provides the getBoolean(), toString(), and booleanValue() methods to support type and class conversion. The toString(), equals(), and hashCode() methods override those of class Object.

The Character Class

The Character class is a wrapper for the char primitive type. It provides several methods that support case, type, and class testing, and conversion. Check out the API pages on these methods. We'll use some of them in the upcoming example.

The Integer and Long Classes

The Integer and Long classes wrap the int and long primitive types. They provide the MIN_VALUE and MAX_VALUE constants, as well as a number of type and class testing and conversion methods. The parseInt() and parseLong() methods are used to parse String objects and convert them to Integer and Long objects.

The Double and Float Classes

The Double and Float classes wrap the double and float primitive types. They provide the MIN_VALUE, MAX_VALUE, POSITIVE_INFINITY, and NEGATIVE_INFINITY constants, as well as the NaN (not-a-number) constant. NaN is used as a value that is not equal to any value, including itself. These classes provide a number of type and class testing and conversion methods, including methods that support conversion to and from integer bit representations.

The Number Class

The Number class is an abstract numeric class that is subclassed by Integer, Long, Float, and Double. It provides four methods that support conversion of objects from one class to another.

All Wrapped Up

The program in Listing 12.5 shows some of the methods that can be used with the primitive types when they are wrapped as objects. Look up these methods in the API pages for each class and try to figure out how they work before moving on to their explanations.


Listing 12.5. The source code of the WrappedClassApp program.

import java.lang.System;
import java.lang.Boolean;
import java.lang.Character;
import java.lang.Integer;
import java.lang.Long;
import java.lang.Float;
import java.lang.Double;

public class WrappedClassApp {
 public static void main(String args[]) {
  Boolean b1 = new Boolean("TRUE");
  Boolean b2 = new Boolean("FALSE");
  System.out.println(b1.toString()+" or "+b2.toString());
  for(int j=0;j<16;++j)
   System.out.print(Character.forDigit(j,16));
  System.out.println();
  Integer i = new Integer(Integer.parseInt("ef",16));
  Long l = new Long(Long.parseLong("abcd",16));
  long m=l.longValue()*i.longValue();
  System.out.println(Long.toString(m,8));
  System.out.println(Float.MIN_VALUE);
  System.out.println(Double.MAX_VALUE);
 }
}


The program examines some of the more useful methods provided by each of the wrapped classes. It creates two objects of class Boolean from string arguments passed to their constructors. It assigns these objects to b1 and b2 and then converts them back to String objects when it displays them. They are displayed in lowercase, as boolean values are traditionally represented.

The program then executes a for loop that prints out the character corresponding to each of the hexadecimal digits. The static forDigit() method of the Character class is used to generate the character values of digits in a number system of a different radix.

The static parseInt() and parseLong() methods are used to parse strings according to different radices. In the example, they are used to convert strings representing hexadecimal numbers into Integer and Long values. These values are then multiplied together and converted to a string that represents the resulting value in base 8. This is accomplished using an overloaded version of the toString() method.

The sample program concludes by displaying the minimum float value and the maximum double value using the predefined class constants of the Float and Double classes.

The program's output is as follows:

true or false
0123456789abcdef
50062143
1.4013e-045
1.79769e+308

The Math Class

The Math class provides an extensive set of mathematical methods in the form of a static class library. It also defines the mathematical constants E and PI. The supported methods include arithmetic, trigonometric, exponential, logarithmic, random number, and conversion routines. You should browse the API page of this class to get a feel for the methods it provides. The example in Listing 12.6 only touches on a few of these methods.


Listing 12.6. The source code of the MathApp program.

import java.lang.System;
import java.lang.Math;

public class MathApp {
 public static void main(String args[]) {
  System.out.println(Math.E);
  System.out.println(Math.PI);
  System.out.println(Math.abs(-1234));
  System.out.println(Math.cos(Math.PI/4));
  System.out.println(Math.sin(Math.PI/2));
  System.out.println(Math.tan(Math.PI/4));
  System.out.println(Math.log(1));
  System.out.println(Math.exp(Math.PI));
  for(int i=0;i<5;++i)
   System.out.print(Math.random()+" ");
  System.out.println();

}

}


This program prints the constants e and p, |-1234|, cos(p/4), sin(p/2), tan(p/4), ln(1), ep, and then five random double numbers between 0.0 and 1.1. Its output is as follows:

2.71828
3.14159
1234
0.707107
1
1
0
23.1407
0.831965 0.573099 0.0268818 0.378625 0.164485

The random numbers you generate will almost certainly differ from the ones shown here.

The String and StringBuffer Classes

The String and StringBuffer classes are used to support operations on strings of characters. The String class supports constant (unchanging) strings, whereas the StringBuffer class supports growable, modifiable strings. String objects are more compact than StringBuffer objects, but StringBuffer objects are more flexible.

String Literals

String literals are strings that are specified using double quotes. "This is a string" and "xyz" are examples of string literals. String literals are different than the literal values used with primitive types. When the javac compiler encounters a String literal, it converts it to a String constructor. For example, the following:

String str = "text";

is equivalent to this:

String str = new String("text");

The fact that the compiler automatically supplies String constructors allows you to use String literals everywhere that you could use objects of the String class.

The + Operator and StringBuffer

If String objects are constant, how can they be concatenated with the + operator and be assigned to existing String objects? In the following example, the code will result in the string "ab" being assigned to the s object:

String s = "";
s = s + "a" + "b";

How can this be possible if Strings are constant? The answer lies in the fact that the Java compiler uses StringBuffer objects to accomplish the string manipulations. This code would be rendered as something similar to the following by the Java compiler:

String s = "";
s = new StringBuffer("").append("a").append("b").toString();

A new object of class StringBuffer is created with the "" argument. The StringBuffer append() method is used to append the strings "a" and "b" to the new object, and then the object is converted to an object of class String via the toString() method. The toString() method creates a new object of class String before it is assigned to the s variable. In this way, the s variable always refers to a constant (although new) String object.

String Constructors

The String class provides seven constructors for the creation and initialization of String objects. These constructors allow strings to be created from other strings, string literals, arrays of characters, arrays of bytes, and StringBuffer objects. Browse through the API page for the String class to become familiar with these constructors.

String Access Methods

The String class provides a very powerful set of methods for working with String objects. These methods allow you to access individual characters and substrings; test and compare strings; copy, concatenate, and replace parts of strings; convert and create strings; and perform other useful string operations.

The most important String methods are the length() method, which returns an integer value identifying the length of a string; the charAt() method, which allows the individual characters of a string to be accessed; the substring() method, which allows substrings of a string to be accessed; and the valueOf() method, which allows primitive data types to be converted into strings.

In addition to these methods, the Object class provides a toString() method for converting other objects to String objects. This method is often overridden by subclasses to provide a more appropriate object-to-String conversion.

Character and Substring Methods

Several String methods allow you to access individual characters and substrings of a string. These include charAt(), getBytes(), getChars(), indexOf(), lastIndexOf(), and substring(). Whenever you need to perform string manipulations, be sure to check the API documentation to make sure that you don't overlook an easy-to-use, predefined String method.

String Comparison and Test Methods

Several String methods allow you to compare strings, substrings, byte arrays, and other objects with a given string. Some of these methods are compareTo(), endsWith(), equals(), equalsIgnoreCase(), regionMatches(), and startsWith().

Copy, Concatenation, and Replace Methods

The following methods are useful for copying, concatenating, and manipulating strings: concat(), copyValueOf(), replace(), and trim().

String Conversion and Generation

There are a number of string methods that support String conversion. These are intern(), toCharArray(), toLowerCase(), toString(), toUpperCase(), and valueOf(). You explore the use of some of these methods in the following example.

Stringing Along

The program in Listing 12.7 provides a glimpse at the operation of some of the methods identified in the previous subsections. Because strings are frequently used in application programs, learning to use the available methods is essential to being able to use the String class most effectively.


Listing 12.7. The source code of the StringApp program.

import java.lang.System;
import java.lang.String;

public class StringApp {
 public static void main(String args[]) {
  String s = " Java Developer's Guide ";
  System.out.println(s);
  System.out.println(s.toUpperCase());
  System.out.println(s.toLowerCase());
  System.out.println("["+s+"]");
  s=s.trim();
  System.out.println("["+s+"]");
  s=s.replace('J','X');
  s=s.replace('D','Y');
  s=s.replace('G','Z');
  System.out.println(s);
  int i1 = s.indexOf('X');
  int i2 = s.indexOf('Y');
  int i3 = s.indexOf('Z');
  char ch[] = s.toCharArray();
  ch[i1]='J';
  ch[i2]='D';
  ch[i3]='G';
  s = new String(ch);
  System.out.println(s);
 }
}


This program performs several manipulations of a string s that is initially set to " Java Developer's Guide ". It prints the original string and then prints upper- and lowercase versions of it, illustrating the use of the toUpperCase() and toLowerCase() methods. It prints the string enclosed between two braces to show that it contains leading and trailing spaces. It then trims away these spaces using the trim() method and reprints the string to show that these spaces were removed.

The program uses the replace() method to replace the letters 'J', 'D', and 'G' with 'X', 'Y', and 'Z' and prints out the string to show the changes. The replace() method is case sensitive. It uses the indexOf() method to get the indices of 'X', 'Y', and 'Z' within s. It uses toCharArray() to convert the string to a char array. It then uses the indices to put 'J', 'D', and 'G' back in their proper locations within the character array. The String() constructor is used to construct a new string from the character array. The new string is assigned to s and is printed.

The program's output is as follows:

  Java Developer's Guide
  JAVA DEVELOPER'S GUIDE
  java developer's guide
[  Java Developer's Guide  ]
[Java Developer's Guide]
Xava Yeveloper's Zuide
Java Developer's Guide

The StringBuffer Class

The StringBuffer class is the force behind the scene for most complex string manipulations. The compiler automatically declares and manipulates objects of this class to implement common string operations.

The StringBuffer class provides three constructors: an empty constructor, an empty constructor with a specified initial buffer length, and a constructor that creates a StringBuffer object from a String object. In general, you will find yourself constructing StringBuffer objects from String objects, and the last constructor will be the one you use most often.

The StringBuffer class provides several versions of the append() method to convert and append other objects and primitive data types to StringBuffer objects. It provides a similar set of insert() methods for inserting objects and primitive data types into StringBuffer objects. It also provides methods to access the character-buffering capacity of StringBuffer and methods for accessing the characters contained in a string. It is well worth a visit to the StringBuffer API pages to take a look at the methods that it has to offer.

Strung Out

The program in Listing 12.8 shows how StringBuffer objects can be manipulated using the append(), insert(), and setCharAt() methods.


Listing 12.8. The source code of the StringBufferApp program.

import java.lang.System;
import java.lang.String;
import java.lang.StringBuffer;

public class StringBufferApp {
 public static void main(String args[]) {
  StringBuffer sb = new StringBuffer(" is ");
  sb.append("Hot");
  sb.append('!');
  sb.insert(0,"Java");
  sb.append('\n');
  sb.append("This is ");
  sb.append(true);
  sb.setCharAt(21,'T');
  sb.append('\n');
  sb.append("Java is #");
  sb.append(1);
  String s = sb.toString();
  System.out.println(s);
 }
}


The program creates a StringBuffer object using the string " is ". It appends the string "Hot" using the append() method and the character '!' using an overloaded version of the same method. The insert() method is used to insert the string "Java" at the beginning of the string buffer.

Three appends are used to tack on a newline character (\n), the string "This is ", and the boolean value true. The append() method is overloaded to support the appending of the primitive data types as well as arbitrary Java objects.

The setCharAt() method is used to replace the letter 't' at index 21 with the letter 'T'. The charAt() and setCharAt() methods allow StringBuffer objects to be treated as arrays of characters.

Finally, another newline character is appended to sb, followed by the string "Java is #" and the int value 1. The StringBuffer object is then converted to a string and displayed to the console window.

The output of the program is as follows:

Java is Hot!
This is True
Java is #1

Threads and Processes

Chapter 8, "Multithreading," provides a detailed description of multithreading in Java. This section briefly describes the classes of java.lang that support multithreading. It also covers the Process class, which is used to manipulate processes that are executed using the System.exec() methods.

Runnable

The Runnable interface provides a common approach to identifying the code to be executed as part of an active thread. It consists of a single method, run(), which is executed when a thread is activated. The Runnable interface is implemented by the Thread class and by other classes that support threaded execution.

Thread

The Thread class is used to construct and access individual threads of execution that are executed as part of a multithreaded program. It defines the priority constants, MIN_PRIORITY, MAX_PRIORITY, and NORM_PRIORITY, that are used to control task scheduling. It provides seven constructors for creating instances of class Thread. The four constructors with the Runnable parameters are used to construct threads for classes that do not subclass the Thread class. The other constructors are used for the construction of Thread objects from Thread subclasses.

Thread supports many methods for accessing Thread objects. These methods provide the capabilities to work with a thread's group; obtain detailed information about a thread's activities; set and test a thread's properties; and cause a thread to wait, be interrupted, or be destroyed.

ThreadGroup

The ThreadGroup class is used to encapsulate a group of threads as a single object so that they can be accessed as a single unit. A number of access methods are provided for manipulating ThreadGroup objects. These methods keep track of the threads and thread groups contained in a thread group and perform global operations on all threads in the group. The global operations are group versions of the operations that are provided by the Thread class.

Process

The Process class is used to encapsulate processes that are executed with the System.exec() methods. An instance of class Process is returned by the Runtime class exec() method when it executes a process that is external to the Java runtime system. This Process object can be destroyed using the destroy() method and waited on using the waitFor() method. The exitValue() method returns the system exit value of the process. The getInputStream(), getOutputStream(), and getErrorStream() methods are used to access the standard input, output, and error streams of the process.

Hello Again

The simple program in Listing 12.9 actually performs some pretty complex processing. It is provided as an example of some of the powerful things that can be accomplished using the Process class.


Listing 12.9. The source code of the ProcessApp program.

import java.lang.System;
import java.lang.Runtime;
import java.lang.Process;
import java.io.DataInputStream;
import java.io.IOException;

public class ProcessApp {
 public static void main(String args[]) throws IOException {
  Runtime r = Runtime.getRuntime();
  Process p = r.exec("java jdg.ch04.HelloWorldApp");
  DataInputStream inStream = new DataInputStream(p.getInputStream());
  String line = inStream.readLine();
  System.out.println(line);
 }
}


The program uses the static getRuntime() method to get the current instance of the Java runtime system. It then uses the exec() method to execute another separate copy of the Java interpreter with the HelloWorldApp program that was developed in Chapter 4, "First Programs: Hello World! to BlackJack." It creates a DataInputStream object, inStream, that is connected to the output stream of the HelloWorldApp program. It then uses inStream to read the output of the HelloWorldApp program and display it on the console window as follows:

Hello World!

The exec() methods combined with the Process class provide a powerful set of tools by which Java programs can be used to launch and control the execution of other programs.

The Compiler Class

The Compiler class consists of five static methods that are used to compile Java classes in the rare event that you want to compile classes directly from a program or applet. These methods allow you to build your own customized Java development environment.

Exceptions and Errors

The java.lang package establishes the Java exception hierarchy and declares numerous exceptions and errors. Errors are used to indicate the occurrence of abnormal and fatal events that should not be handled within application programs. (See Chapter 7, "Exceptions.")

The Throwable Class

The Throwable class is at the top of the Java error-and-exception hierarchy. It is extended by the Error and Exception classes and provides methods that are common to both classes. These methods consist of stack tracing methods, the getMessage() method, and the toString() method, which is an override of the method inherited from the Object class. The getMessage() method is used to retrieve any messages that are supplied in the creation of Throwable objects.

The fillInStackTrace() and printStackTrace() methods are used to add information to supply and print information that is used to trace the propagation of exceptions and errors throughout a program's execution.

The Error Class

The Error class is used to provide a common superclass to define abnormal and fatal events that should not occur. It provides two constructors and no other methods. Four major classes of errors extend the Error class: AWTError, LinkageError, ThreadDeath, and VirtualMachineError.

The AWTError class identifies fatal errors that occur in the Abstract Window Toolkit packages. It is a single identifier for all AWT errors and is not subclassed.

The LinkageError class is used to define errors that occur as the result of incompatibilities between dependent classes. These incompatibilities result when a class X that another class Y depends on is changed before class Y can be recompiled. The LinkageError class is extensively subclassed to identify specific manifestations of this type of error.

The ThreadDeath error class is used to indicate that a thread has been stopped. Instances of this class can be caught and then rethrown to ensure that a thread is gracefully terminated, although this is not recommended. The ThreadDeath class is not subclassed.

The VirtualMachineError class is used to identify fatal errors occurring in the operation of the Java virtual machine. It has four subclasses: InternalError, OutOfMemoryError, StackOverflowError, and UnknownError.

The Exception Class

The Exception class provides a common superclass for the exceptions that can be defined for Java programs and applets. There are nine subclasses of exceptions that extend the Exception class. These exception subclasses are further extended by lower-level subclasses.

Summary

In this you have learned how to use the java.lang package. You have taken a tour of its classes and their methods and have written some sample programs that illustrate their use. In the next you'll learn to use the java.io package to perform stream-based I/O to files, memory buffers, and the console window.