If you’ve been reading t his book sequentially, you’ve read all about the core Java language constructs, including the object-oriented aspects of the language and the use of threads. Now it’s time to shift gears and talk about the Java Application Programming Interface (API), the collection of classes that comes with every Java implementation. The Java API encompasses all the public methods and variables in the classes that make up the core Java packages. Table 9.1 lists the most important packages in the API and shows which chapters in this book discuss each of the packages.
Table 9-1. Packages of the Java API
Package |
Contents |
Chapter |
---|---|---|
|
Basic language classes |
4, 5, 6, 7, 8, 9 |
|
Reflection |
7 |
|
Input and output |
10 |
|
Utilities and collections classes |
9, 10, 11 |
|
International text classes |
9 |
|
Sockets and URLs |
11, 12 |
|
The applet API |
20 |
|
Swing and 2D graphics |
13, 14, 15, 16, 17 |
|
Event classes |
13, 14, 15 |
|
2D image-processing classes |
18 |
|
JavaBeans API |
19 |
|
RMI classes |
11 |
As you can see in Table 9.1, we’ve
already examined some of the classes in java. lang
in earlier chapters on the core language constructs. Starting with
this chapter, we’ll throw open the Java toolbox and begin
examining the rest of the classes in the API.
We’ll begin our exploration
with some of the fundamental language classes in
java.lang
, including strings and math utilities.
Figure 9.1 shows the class hierarchy of the
java.lang
package.
We’ll cover some of the classes in
java.util
, such as classes that support date and
time values, random numbers, vectors, and hashtables. Figure 9.2 shows the class hierarchy of the
java.util
package.
In this section, we take a closer look
at the Java String
class (or more specifically,
java.lang.String
). Because strings are used so
extensively throughout Java (or any programming language, for that
matter), the Java String
class has quite a bit of
functionality. We’ll test-drive most of the important features,
but before you go off and write a complex parser or regular
expression library, you should refer to a Java class reference manual
for additional details. For example, see Java Fundamental Classes
Reference, by Mark Grand and Jonathan Knudsen (O’Reilly &
Associates).
Strings are immutable; once you create a String
object, you can’t change its value. Operations that would
otherwise change the characters or the length of a string instead
return a new String
object that copies the needed
parts of the original. ( Java implementations make an effort to
consolidate identical strings and string literals in the same class
into a shared-string pool.)
To
create a string, assign a double-quoted constant to a
String
variable:
String quote = "To be or not to be";
Java automatically converts the string literal into a
String
object. If you’re a C or C++
programmer, you may be wondering if quote
is
null-terminated. This question doesn’t make any sense with Java
strings. The String
class actually uses a Java
character array internally. It’s private
to
the String
class, so you can’t get at the
characters and change them. As always, arrays in Java are real
objects that know their own length, so String
objects in Java don’t require special terminators (not even
internally). If you need to know the length of a
String
, use the length( )
method:
int length = quote.length( );
Strings
can take advantage of the only overloaded operator in Java, the
+
operator, for string concatenation. The
following code produces equivalent strings:
String name = "John " + "Smith"; String name = "John ".concat("Smith");
Literal strings can’t span lines in Java source files, but we can concatenate lines to produce the same effect:
String poem = "'Twas brillig, and the slithy toves\n" + " Did gyre and gimble in the wabe:\n" + "All mimsy were the borogoves,\n" + " And the mome raths outgrabe.\n";
Embedding lengthy
text in source code should now be a thing
of the past, given that we can retrieve a String
from anywhere on the planet via a URL. In Chapter 12, we’ll see how to do things like this:
String poem = (String) new URL( "http://myserver/~dodgson/jabberwocky.txt").getContent( );
In addition to making strings from literal expressions, we can
construct a String
from an
array of characters:
char [] data = { 'L', 'e', 'm', 'm', 'i', 'n', 'g' }; String lemming = new String( data );
byte [] data = { 97, 98, 99 }; String abc = new String(data, "8859_5");
The second argument to the String
constructor for
byte arrays is the name of an
encoding scheme.
It’s used to convert the given bytes to the string’s
Unicode characters. Unless you know something about Unicode, you can
use the form of the constructor that accepts a byte array only; the
default encoding scheme will be used.[30]
We can get the string representation of
most things with the static String.
valueOf( )
method. Various overloaded versions of
this method give us string values for all of the primitive types:
String one = String.valueOf( 1 ); String two = String.valueOf( 2.384f ); String notTrue = String.valueOf( false );
All objects in
Java have a toString( )
method, inherited from the
Object
class. For class-type references,
String.valueOf( )
invokes the object’s
toString( )
method to get its string
representation. If the reference is null
, the
result is the literal string “null”:
String date = String.valueOf( new Date( ) ); System.out.println( date ); // "Sun Dec 19 05:45:34 CST 1999" date = null; System.out.println( date ); // "null"
Producing
primitives like
numbers from String
objects is not a function of the String
class. For
that we need the primitive wrapper classes; they are described in the
"Math Utilities” section later in
this chapter. The wrapper classes provide
valueOf( )
methods that produce an object from a
String
, as well as corresponding methods to
retrieve the value in various primitive forms. Two examples are:
int i = Integer.valueOf("123").intValue( ); double d = Double.valueOf("123.0").doubleValue( );
In this code, the Integer.valueOf( )
call yields
an Integer object that represents the value 123. An
Integer
object can provide its primitive value in
the form of an int
with the intValue( )
method.
Although these techniques may work for simple cases, they will not work internationally. Let’s pretend for a moment that we are programming Java in the rolling hills of Tuscany. We would follow the local customs for representing numbers and write code like the following:
double d = Double.valueOf("1.234,56").doubleValue( ); // oops!
Unfortunately, this code throws a
NumberFormatException
.
The java.text
package, which we’ll discuss
later, contains the tools we need to generate and parse strings for
different countries and languages.
The
charAt( )
method of the String
class lets us get at the characters of a String
in
an array-like fashion:
String s = "Newton"; for ( int i = 0; i < s.length( ); i++ ) System.out.println( s.charAt( i ) );
This code prints the characters of the
string one at a time. Alternately, we can get the characters all at
once with toCharArray( )
. Here’s a way to
save typing a bunch of single quotes:
char [] abcs = "abcdefghijklmnopqrstuvwxyz".toCharArray( );
Just
as in C, you can’t compare strings for
equality with
==
because as in C, strings are accessed
by reference. Even the expression "foo" == "foo"
will return false, unless your Java compiler happens to coalesce
multiple instances of the same string literal to a single string-pool
item. String comparisons with
<
, >
,
<=
, and
>=
don’t work at all, because
Java can’t convert references to integers.
Use the equals( )
method to compare strings:
String one = "Foo"; char [] c = { 'F', 'o', 'o' }; String two = new String ( c ); if ( one.equals( two ) ) // true
An alternative version,
equalsIgnoreCase( )
, can be used to check the
equivalence of strings in a case-insensitive way:
String one = "FOO"; String two = "foo"; if ( one.equalsIgnoreCase( two ) ) // true
The compareTo( )
method compares the lexical value of the
String
against another String
.
It returns an integer that is less than, equal to, or greater than
zero, just like the C routine string( )
:
String abc = "abc"; String def = "def"; String num = "123"; if ( abc.compareTo( def ) < 0 ) // true if ( abc.compareTo( abc ) == 0 ) // true if ( abc.compareTo( num ) > 0 ) // true
On some systems, the behavior of lexical comparison is complex, and obscure alternative character sets exist. Java avoids this problem by comparing characters strictly by their position in the Unicode specification.
In Java 1.1 and later, the
java.text
package provides a sophisticated set of
classes for comparing strings, even in different languages. German,
for example, has vowels with umlauts over them and a beta-like
character that represents a double “s”. How should we
sort these? Although the rules for sorting these characters are
precisely defined, you can’t assume that the lexical comparison
we used earlier works correctly for languages other than
English.
Fortunately, the
Collator
class takes care of these complex sorting
problems.
In the following example, we use a Collator
designed to compare German strings. (We’ll talk about
Locales
in a later section.) You can obtain a
default Collator
by
calling the Collator.getInstance( )
method, with no arguments. Once you have an appropriate
Collator
instance, you can use its
compare( )
method, which returns values just like
String
’s
compareTo( )
method.
The following code creates two strings for the German translations of
“fun” and “later,” using Unicode constants
for these two special characters. It then compares them, using a
Collator
for the German locale; the result is that
“fun” (Spaß) sorts before “later”
(später).
String fun = "Spa\u00df"; String later = "sp\u00e4ter"; Collator german = Collator.getInstance(Locale.GERMAN); if (german.compare(fun, later) < 0) // true
Using collators is essential if you’re working with languages other than English. In Spanish, for example, ll and ch are treated as separate characters and alphabetized separately. A collator handles cases like these automatically.
The
String
class provides several methods for finding
substrings within a string. The startsWith( )
and endsWith( )
methods compare an argument String
with the
beginning and end of the String
, respectively:
String url = "http://foo.bar.com/"; if ( url.startsWith("http:") ) // true
Overloaded
versions of indexOf( )
search for the first
occurrence of a character or substring:
String abcs = "abcdefghijklmnopqrstuvwxyz"; int i = abcs.indexOf( 'p' ); // 15 int i = abcs.indexOf( "def" ); // 3
Correspondingly,
overloaded versions of lastIndexOf( )
search for
the last occurrence of a character or substring.
A
number of methods operate on the String
and return
a new String
as a result. While this is useful,
you should be aware that creating lots of strings in this manner can
affect performance. If you need to modify a string often, you should
use the StringBuffer
class, as we’ll discuss
shortly.
trim( )
is a useful
method that removes leading and trailing whitespace (i.e., carriage
return, newline, and tab) from the String
:
String str = " abc "; str = str.trim( ); // "abc"
In this example, we have thrown away the original
String
(with excess whitespace), so it will be
garbage-collected.
The toUpperCase( )
and toLowerCase( )
methods return a
new String
of the appropriate case:
String down = "FOO".toLowerCase( ); // "foo" String up = down.toUpperCase( ); // "FOO"
substring( )
returns a specified range of characters. The starting
index is inclusive; the ending is exclusive:
String abcs = "abcdefghijklmnopqrstuvwxyz"; String cde = abcs.substring(2, 5); // "cde"
Many
people complain when they discover that the Java
String
class is final
(i.e., it
can’t be subclassed). There is a lot of functionality in
String
, and it would be nice to be able to modify
its behavior directly. Unfortunately, there is also a serious need to
optimize and rely on the performance of String
objects. The Java compiler can optimize final
classes by inlining methods when appropriate. The implementation of
final
classes can also be trusted by classes that
work closely together, allowing for special cooperative
optimizations. If you want to make a new string class that uses basic
String
functionality, use a
String
object in your class and provide methods
that delegate method calls to the appropriate
String
methods.
Table 9.2 summarizes the methods provided by
the String
class.
Table 9-2. String Methods
Method |
Functionality |
---|---|
|
Gets at a particular character in the string |
|
Compares the string with another string |
|
Concatenates the string with another string |
|
Returns a string equivalent to the specified character array |
|
Checks whether the string ends with a specified suffix |
|
Compares the string with another string |
|
Compares the string with another string, ignoring case |
|
Copies characters from the string into a byte array |
|
Copies characters from the string into a character array |
|
Returns a hashcode for the string |
|
Searches for the first occurrence of a character or substring in the string |
|
Fetches a unique instance of the string from a global shared string pool |
|
Searches for the last occurrence of a character or substring in a string |
|
Returns the length of the string |
|
Checks whether a region of the string matches the specified region of another string |
|
Replaces all occurrences of a character in the string with another character |
|
Checks whether the string starts with a specified prefix |
|
Returns a substring from the string |
|
Returns the array of characters from the string |
|
Converts the string to lowercase |
|
Returns the string value of an object |
|
Converts the string to uppercase |
|
Removes leading and trailing white space from the string |
|
Returns a string representation of a value |
The
java.lang.StringBuffer
class is a growable buffer
for characters. It’s an efficient alternative to code like the
following:
String ball = "Hello"; ball = ball + " there."; ball = ball + " How are you?";
This example repeatedly produces new String
objects. This means that the character array must be copied over and
over, which can adversely affect performance. A more economical
alternative is to use a StringBuffer
object and
its append( )
method:
StringBuffer ball = new StringBuffer("Hello"); ball.append(" there."); ball.append(" How are you?");
The StringBuffer
class provides a number of
overloaded append( )
methods for appending various types of data to the
buffer.
We can get a String
from the StringBuffer
with its toString( )
method:
String message = ball.toString( );
You can also retrieve part of a StringBuffer
, as a
String
, using one of the substring( )
methods.
StringBuffer
also provides a number of overloaded insert( )
methods for inserting various types of data at a particular location
in the string buffer. Furthermore, you can remove a single character
or a range of characters with the deleteCharAt( )
and delete( )
methods. Finally, you can replace part of the
StringBuffer
with the contents of a
String
using the replace( )
method.
The String
and StringBuffer
classes cooperate, so that even in this last operation, no copy has
to be made. The string data is shared between the objects, unless and
until we try to change it in the StringBuffer
.
So, when should you use a StringBuffer
instead of
a String
? If you need to keep adding characters to
a string, use a StringBuffer
; it’s designed
to efficiently handle such modifications. You’ll still have to
convert the StringBuffer
to a
String
when you need to use any of the methods in
the String
class. But you can print a
StringBuffer
directly using
System.out.println( )
, because println( )
calls the toString( )
for
you.
Another thing you should know about StringBuffer
methods is that they are thread-safe, just like all public methods in
the Java API. This means that only one thread at a time can change
the state of a StringBuffer
instance. Any time you
modify a StringBuffer
, you don’t have to
worry about another thread coming along and messing up the string
while you are modifying it.
You might be interested to know that the
compiler uses a StringBuffer
to implement
String
concatenation. Consider the following
expression:
String foo = "To " + "be " + "or";
This is equivalent to:
String foo = new StringBuffer().append("To ").append("be ").append("or").toString( );
This kind of chaining of expressions is one of the things operator overloading hides in other languages.
A common programming task involves
parsing a string of text into words
or “tokens” that are separated by some set of delimiter
characters. The java.util.StringTokenizer
class is
a utility that does just this. The following example reads words from
the string text
:
String text = "Now is the time for all good men (and women)..."; StringTokenizer st = new StringTokenizer( text ); while ( st.hasMoreTokens( ) ) { String word = st.nextToken( ); ... }
First, we create a new
StringTokenizer
from the
String
. We invoke the hasMoreTokens( )
and nextToken( )
methods to loop over the
words of the text. By default, we use whitespace (i.e., carriage
return, newline, and tab) as delimiters.
The
StringTokenizer
implements the
java.util.Enumeration
interface, which means that
StringTokenizer
also implements two more general
methods for accessing elements: hasMoreElements( )
and nextElement( )
. These methods are defined by the
Enumeration
interface; they provide a standard way
of returning a sequence of values. The advantage of
nextToken( )
is that it returns a
String
, while nextElement( )
returns an Object
. (We’ll see an example in
the “Properties” section later in this chapter.) The
Enumeration
interface is implemented by many items
that return sequences or collections of objects. Those of you who
have used the C strtok( )
function should
appreciate how useful this object-oriented equivalent is.
You can also specify your own set of
delimiter
characters in the StringTokenizer
constructor,
using another String
argument to the constructor.
Any combination of the specified characters is treated as the
equivalent of whitespace for tokenizing:
text = "http://foo.bar.com/"; tok = new StringTokenizer( text, "/:" ); if ( tok.countTokens( ) < 2 ) // bad URL String protocol = tok.nextToken( ); // "http" String host = tok.nextToken( ); // "foo.bar.com"
This
example parses a URL specification to get at the protocol and host
components. The characters /
and
:
are used as separators. The
countTokens( )
method provides a fast way to see
how many tokens will be returned by nextToken( )
,
without actually creating the String
objects.
An overloaded form of nextToken( )
accepts a
string that defines a new delimiter set for that and subsequent
reads. The StringTokenizer
constructor accepts a
flag that specifies that separator characters are to be returned
individually as tokens themselves. By default, the token separators
are
not
returned.
Get Learning Java now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.