All Categories :
Java
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.
|
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.
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.
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 .
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 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.
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.
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.
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.
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.
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.
The getSecurityManager() and setSecurityManager()
methods provide access to the security manager that is currently
in effect. These methods are covered in Chapter 39.
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().
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.
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
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 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 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 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 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 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.
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 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 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
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.
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.
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.
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 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
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.
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.
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.
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.
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.
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 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.
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 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 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 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.
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.

Contact
reference@developer.com with questions or comments.
Copyright 1998
EarthWeb Inc., All rights reserved.
PLEASE READ THE ACCEPTABLE USAGE STATEMENT.
Copyright 1998 Macmillan Computer Publishing. All rights reserved.