by Clayton Walnum
String handling in C or C++ (the languages that inspired Java) is infamously clunky. Java solves that problem the same way many C++ programmers do: by creating a String class. Java's String class enables your programs to manage text strings effortlessly, using statements that are similar to those used in simpler languages like BASIC or Pascal. Java also makes it easy to handle fonts, which determine the way that your text strings appear on-screen.
So, what exactly is a string, anyway? In its simplest form, a string is nothing more than one or more text characters arranged consecutively in memory. You can think of a string as an array of characters, with this array having an index that starts at zero. (That is, the first character in the string is at array index 0.) Unfortunately, few computer languages deal with strings in such a simple form. This is because a program needs to know where a string ends, and there are several different solutions to the length problem. Pascal, for example, tacks the length of the string onto the front of the characters, whereas C++ expects to find a null character (a zero) at the end of the string.
In Java, strings are represented by one of two classes:
NOTE: With the String class, while you can do operations like find, compare, and concatenate characters, you cannot insert new characters into the string or change the length of the string (except through concatenation--which actually creates a new string anyway).
Within an object of the String or StringBuffer class, Java creates an array of characters much like that used for strings in C++ programs. However, because this character array is hidden within the class, it cannot be accessed except through the class's methods. This data encapsulation (a key feature of object-oriented programming, by the way) ensures that the string will be maintained properly and will be manipulated in accordance with the rules of the class (represented by the methods).
Figures 17.1 and 17.2 illustrate this concept. In Figure 17.1, a conventional
C++ string is left hanging in memory where the program can manipulate it at will,
whether or not said manipulation makes sense or results in a fatal error. In Figure
17.2, the string is protected by the methods of the class--the only way through which
the program can access the string.
FIG. 17.1
In conventional programs, strings can be accessed directly by the program, leading
to complications and errors.
FIG. 17.2
By using a string class, the string can be accessed only through the class's
methods, which eliminates many potential errors.
In Java, you create strings by creating an object of the String or StringBuffer class. This String object can be created implicitly or explicitly depending upon how the string is being used in the program. To create a string implicitly, you simply place a string literal in your program, and Java goes ahead and creates a String object for the string automatically. This is because, even internally, Java uses String objects to represent string literals. For example, look at this line:
g.drawString("This is a string", 50, 50);
You are (or rather Java is) implicitly creating a String object for the string literal "This is a string." Every time you refer to a string this way in a Java program, you're creating a String object.
The other way to create a String object is to explicitly instantiate an object of the String class. The String class has seven constructors, so there are plenty of ways to explicitly create a String object, the most obvious way being this:
String str = new String("This is a string");
You can also declare a String object and then set its value later in the program, like this:
String str; str = "This is a string";
Or, you can combine both of the approaches and end up with this:
String str = "This is a string";
Finally, any of the following lines create a null string:
String str = new String(); String str = ""; String str = "";
Although the previous examples are the most common ways of explicitly creating String objects, the String class offers several alternatives. The seven String class's constructors look like this:
public String() public String(String value) public String(char value[]) public String(char value[], int offset, int count) public String(byte ascii[], int hibyte, int offset, int count) public String(byte ascii[], int hibyte) public String (StringBuffer buffer)
These constructors create, respectively, the following:
CAUTION:
There's a big difference between a null String object and a null string. When you declare a String object with a line like String str;, you are declaring an object of the String class that has not yet been instantiated. That is, there is not yet a String object associated with str, meaning that the String object is null. When you create a String object with a line like String str = "";, you are creating a fully instantiated String object whose string contains no characters (has a string length of zero). This is called a null string.
Once you have your String object constructed, you can call upon the String class's methods in order to obtain information about the string. For example, to get the length of the string, you can call the length() method, like this:
String str = "This is a string"; int len = str.length();
These lines set len to 16, which is the length of the string (including spaces, of course).
If you want to know whether a string starts with a certain prefix, you can call the startsWith() method, like this:
String str = "This is a string"; boolean result = str.startsWith("This");
Here, the boolean variable result is equal to true, because str does indeed start with "This". In the following example, result is false:
String str = "This is a string"; boolean result = str.startsWith("is");
A similar method is endsWith(), which determines whether the string object ends with a given set of characters. You use that method as follows:
String str = "This is a string"; boolean result = str.endsWith("string");
In this example, result ends up equal to true, whereas the following code segment sets result equal to false:
String str = "This is a string"; boolean result = str.endsWith("This");
If you're setting up a table for strings that you want to be able to locate quickly, you can use a hash table. To get a hash code for a string, you can call the hashCode() method:
String str = "This is a string"; int hashcode = str.hashCode();
See "The Hashtable Class," Chapter 33
If you want to find the location of the first occurrence of a character within a string, use the indexOf() method:
String str = "This is a string"; int index = str.indexOf(`a');
In this example, index is equal to 8, which is the index of the first a in the string.
To find the location of subsequent characters, you can use two versions of the indexOf() method. For example, to find the first occurrence of "i," you might use these lines:
String str = "This is a string"; int index = str.indexOf(`i');
This gives index a value of 2. To find the next occurrence of "i," you can use a line similar to this:
index = str.indexOf(`i', index+1);
By including the index+1 as the method's second argument, you're telling Java to start searching at index 3 in the string (the old value of index, plus 1). This results in index being equal to 5, which is the location of the second occurrence of "i" in the string. If you called the previous line again, index would be equal to 13, which is the location of the third "i" in the string.
You can also search for characters backwards through a string, using the lastIndexOf() method:
String str = "This is a string"; int index = str.lastIndexOf("i");
Here, index is equal to 13. To search backwards for the next "i," you might use a line like this:
index = str.lastIndexOf(`i', index-1);
Now, index is equal to 5, because the index-1 as the second argument tells Java where to begin the backwards search. The variable index was equal to 13 after the first call to lastIndexOf(), so in the second call, index-1 equals 12.
There are also versions of indexOf() and lastIndexOf() that search for substrings within a string. For example, the following example sets index to 10:
String str = "This is a string"; int index = str.indexOf("string");
Listing 17.1 is an applet that gives you a chance to experiment with the indexOf()
method. Listing 17.2 is the HTML document that loads the applet. When you run the
applet, enter a string into the first text box and a substring for which to search
in the second box. When you click the Search button, the applet displays the index
at which the substring is located (see Figure 17.3).
FIG. 17.3
The StringApplet applet searches for substrings.
import java.awt.*; import java.applet.*; public class StringApplet extends Applet { TextField textField1; TextField textField2; Button button1; String displayStr; public void init() { Label label = new Label("String:"); add(label); textField1 = new TextField(20); add(textField1); label = new Label("substr:"); add(label); textField2 = new TextField(20); add(textField2); button1 = new Button("Search"); add(button1); displayStr = ""; resize(230, 200); } public void paint(Graphics g) { g.drawString(displayStr, 80, 150); } public boolean action(Event evt, Object arg) { if (arg == "Search") { String str = textField1.getText(); String substr = textField2.getText(); int index = str.indexOf(substr); displayStr = "Located at " + str.valueOf(index); repaint(); return true; } else return false; } }
<title>Applet Test Page</title> <h1>Applet Test Page</h1> <applet code="StringApplet.class" width=200 height=200 name="StringApplet"> </applet>
Often, you need to know when two strings are equal. For example, you might want to compare a string entered by the user to another string hard-coded in your program. There are two basic ways you can compare strings:
The equals() method returns true when the two strings are equal and false otherwise. Here's an example:
String str = "This is a string"; boolean result = str.equals("This is a string");
Here, the boolean variable result is equal to true. You could also do something similar using the comparison operator:
String str = "This is a string"; if (str == "This is a string") result = true;
This also results in result's being true.
Although these two methods are the easiest way to compare strings, the String class gives you many other options. The equalsIgnoreCase() method compares two strings without regard for upper- or lowercase letters. That is, the following code sets result to false, because equals() considers the case of the characters in the string:
String str = "THIS IS A STRING"; boolean result = str.equals("this is a string");
This code fragment, however, sets result to true:
String str = "THIS IS A STRING"; boolean result = str.equalsIgnoreCase("this is a string");
If you want to know more than just if the strings are equal, you can call upon the compareTo() method, which returns a value less than zero when the string object is less than the given string, zero when the strings are equal, and greater than zero if the string object is greater than the given string. The comparison is done according to alphabetical order (or, if you want to be technical about it, according to the ASCII values of the characters). So, this code segment sets result to a value greater than zero, because "THIS IS A STRING" is greater than "ANOTHER STRING":
String str = "THIS IS A STRING"; int result = str.compareTo("ANOTHER STRING");
The following comparison, however, results in a result being set to a value less than zero, because "THIS IS A STRING" is less than "ZZZ ANOTHER STRING":
String str = "THIS IS A STRING"; int result = str.compareTo("ZZZ ANOTHER STRING");
Finally, the following comparison results in zero, because the strings are equal:
String str = "THIS IS A STRING"; int result = str.compareTo("THIS IS A STRING");
C and C++ programmers will be very familiar with this form of string comparison.
If you really want to get fancy with your string comparisons, you can dazzle your Java programming buddies by using the regionMatches() method, which enables you to compare part of one string with part of another. Here's an example:
String str = "THIS IS A STRING"; boolean result = str.regionMatches(10, "A STRING", 2, 6);
The regionMatches() method's four arguments are:
The previous example sets result to true. In this case, Java starts looking in "THIS IS A STRING" at the 10th character (starting from 0), which is the "S" in "STRING." Java also starts its comparison at the second character of the given string "A STRING," which is also the "S" in "STRING." Java compares six characters starting at the given offsets, which means it is comparing "STRING" with "STRING," a perfect match.
There's also a version of regionMatches() that is case-insensitive. The following example sets result to true:
String str = "THIS IS A STRING"; boolean result = str.regionMatches(true, 10, "A string", 2, 6);
The new first argument in this version of regionMatches() is a boolean value indicating whether the comparison should be case-insensitive. A value of true tells Java to ignore the case of the characters. A value of false for this argument results in exactly the same sort of case-sensitive comparison you get with the four-argument version of regionMatches().
Listing 17.3 is an applet that gives you a chance to experiment with the compareTo()
method. Listing 17.4 is the HTML document that runs the applet. When you run the
applet, enter a string into each text box. When you click the Compare button, the
applet determines how the strings compare and displays the results (see Figure 17.4).
FIG. 17.4
Here's StringApplet2 comparing two strings.
import java.awt.*; import java.applet.*; public class StringApplet2 extends Applet { TextField textField1; TextField textField2; Button button1; String displayStr; public void init() { Label label = new Label("String 1:"); add(label); textField1 = new TextField(20); add(textField1); label = new Label("String 2:"); add(label); textField2 = new TextField(20); add(textField2); button1 = new Button("Compare"); add(button1); displayStr = ""; resize(230, 200); } public void paint(Graphics g) { g.drawString(displayStr, 30, 150); } public boolean action(Event evt, Object arg) { if (arg == "Compare") { String str1 = textField1.getText(); String str2 = textField2.getText(); int result = str1.compareTo(str2); if (result < 0) displayStr = "String1 is less than String2"; else if (result == 0) displayStr = "String1 is equal to String2"; else displayStr = "String1 is greater than String2"; repaint(); return true; } else return false; } }
<title>Applet Test Page</title> <h1>Applet Test Page</h1> <applet code="StringApplet2.class" width=200 height=200 name="StringApplet2"> </applet>
There may be many times in your programming career when you want to extract portions of a string. The String class provides for these needs with a set of methods for just this purpose. For example, you can determine the character at a given position in the string by calling the charAt() method, like this:
String str = "This is a string"; Char chr = str.charAt(6);
In these lines, the character variable chr ends up with a value of "s," which is the fifth character in the string. Why didn't chr become equal to "i"? Because, as in C and C++, you start counting array elements at zero rather than one.
A similar method, getChars(), enables you to copy a portion of a String object to a character array:
String str = "This is a string"; char chr[] = new char[20]; str.getChars(5, 12, chr, 0);
In this code sample, the character array chr ends up containing the characters "is a st." The getChars() method's arguments are the index of the first character in the string to copy, the index of the last character in the string, the destination array, and where in the destination array to start copying characters.
The method getBytes() does the same thing as getChars() but uses a byte array as the destination array:
String str = "This is a string"; byte byt[] = new byte[20];
str.getBytes(5, 12, byt, 0);
Another way to extract part of a string is to use the substring() method:
String str1 = "THIS IS A STRING"; String str2 = str1.substring(5);
In this case, the String object str2 ends up equal to the substring "IS A STRING." This is because substring()'s single argument is the index of the character at which the substring starts. Every character from the index to the end of the string gets extracted.
If you don't want to extract all the way to the end of the string, you can use the second version of the substring() method, whose arguments specify the beginning and ending indexes:
String str1 = "THIS IS A STRING"; String str2 = str1.substring(5, 9);
These lines set str2 to the substring IS A.
Listing 17.5 is an applet that gives you a chance to experiment with the indexOf()
method. Listing 17.6 is the HTML document that runs the applet. When you run the
applet, enter a string into the first text box. Then, enter the starting and ending
indexes for a substring in the second and third boxes. When you click the Extract
button, the applet finds and displays the selected substring (see Figure 17.5).
FIG. 17.5
StringApplet3 is running under Appletviewer.
CAUTION:
There's no error checking in Listing 17.5, so make sure your indexes are correct. Otherwise, Java generates an exception.
import java.awt.*; import java.applet.*; public class StringApplet3 extends Applet { TextField textField1; TextField textField2; TextField textField3; Button button1; String displayStr; public void init() { Label label = new Label("String:"); add(label); textField1 = new TextField(20); add(textField1); label = new Label("Start:"); add(label); textField2 = new TextField(5); add(textField2); label = new Label("End:"); add(label); textField3 = new TextField(5); add(textField3); button1 = new Button("Extract"); add(button1); displayStr = ""; resize(230, 200); } public void paint(Graphics g) { g.drawString("Selected substring:", 70, 130); g.drawString(displayStr, 70, 150); } public boolean action(Event evt, Object arg) { if (arg == "Extract") { String str1 = textField1.getText(); String str2 = textField2.getText(); String str3 = textField3.getText(); int start = Integer.parseInt(str2); int end = Integer.parseInt(str3); displayStr = str1.substring(start, end); repaint(); return true; } else return false; } }
<title>Applet Test Page</title> <h1>Applet Test Page</h1> <applet code="StringApplet3.class" width=200 height=200 name="StringApplet3"> </applet>
Although the String class is intended to be used for string constants, the class does provide some string-manipulation methods that "modify" the String object. I have the word "modify" in quotes because these string-manipulation methods don't actually change the String object, but rather create an additional String object that incorporates the requested changes. A good example is the replace() method, which enables you to replace any character in a string with another character:
String str1 = "THIS IS A STRING"; String str2 = str1.replace(`T', `X');
In this example, str2 contains "XHIS IS A SXRING," because the call to replace() requests that every occurrence of a "T" be replaced with an "X." Note that str1 remains unchanged and that str2 is a brand new String object.
Another way you can manipulate strings is to concatenate them. Concatenate is just a fancy term for "join together." So, when you concatenate two strings, you get a new string that contains both of the original strings. For example, look at these lines of Java source code:
String str1 = "THIS IS A STRING"; String str2 = str1.concat("XXXXXX");
Here, str2 contains "THIS IS A STRINGXXXXXX," whereas str1 remains unchanged. As you can see, the concat() method's single argument is the string to concatenate with the original string.
To make things simpler, the String class defines an operator, the plus sign (+), for concatenating strings. By using this operator, you can join strings in a more intuitive way. Here's an example:
String str1 = "THIS IS A STRING"; String str2 = str1 + "XXXXXX";
This code segment results in exactly the same strings as the previous concat() example. Note that you can use the concatenation operator many times in a single line, like this:
String str = "This " + "is " + "a test";
If you want to be certain of the case of characters in a string, you can rely on the toUpperCase() and toLowerCase() methods, each of which returns a string whose characters have been converted to the appropriate case. For example, look at these lines:
String str1 = "THIS IS A STRING"; String str2 = str1.toLowerCase();
Here, str2 is "this is a string," because the toLowerCase() method converts all characters in the string to lowercase. The toUpperCase() method, of course, does just the opposite: converting all characters to uppercase.
Sometimes, you have strings that contain leading or trailing spaces. The String class features a method called trim() that removes both leading and trailing whitespace characters. You use it like this:
String str1 = " THIS IS A STRING "; String str2 = str1.trim();
In this example, str2 contains the string "THIS IS A STRING," missing all the spaces before the first "T" and after the "G."
Finally, you can use the String class's valueOf() method to convert just about any type of data object to a string, thus enabling you to display the object's value on-screen. For example, the following lines convert an integer to a string:
int value = 10; String str = String.valueOf(value);
Notice that valueOf() is a static method, meaning that it can be called by referencing the String class directly, without having to instantiate a String object. Of course, you can also call valueOf() through any object of the String class, like this:
int value = 10; String str1 = ""; String str2 = str1.valueOf(value);
The StringBuffer class enables you to create string objects that can be changed in various ways, unlike the String class, which represents string constants. When you modify a string of the StringBuffer class, you're not creating a new string object, but rather operating directly on the original string itself. For this reason, the StringBuffer class offers a different set of methods than the String class, all of which operate directly on the buffer that contains the string.
The StringBuffer class offers several constructors that enable you to construct a StringBuffer object in various ways. Those constructors look like this:
StringBuffer() StringBuffer(int length) StringBuffer(String str)
These constructors create an empty StringBuffer, an empty StringBuffer of the given length, and a StringBuffer from a String object (or string literal), respectively.
Just as with regular strings, you might need to know the length of a string stored in a StringBuffer object. The class provides the length() method for this purpose. StringBuffer objects, however, also have a capacity() method that returns the capacity of the buffer. Simply put, a StringBuffer's length is the number of characters stored in the string, whereas capacity is the maximum number of characters that fits in the buffer. In the following code example, length is 2 and capacity is 17:
StringBuffer str = new StringBuffer("XX"); int length= str.length(); int capacity = str.capacity();
You've already had some experience with string extraction when you learned about the String class. The StringBuffer class has two of the same methods for accomplishing this task. Those methods are charAt() and getChars(), both of which work similarly to the String versions. Here's an example of using charAt():
StringBuffer str = new StringBuffer("String buffer"); char ch = str.charAt(5);
And here's an example of using getChars():
StringBuffer str = new StringBuffer("String buffer"); char ch[] = new char[20]; str.getChars(7, 10, ch, 0);
There are several ways you can modify the string that's stored in a StringBuffer object. Unlike with the string-modification methods in the String class, which create a new string, the methods in the StringBuffer class work directly on the buffer in which the original string is stored. The first thing you can do with a string buffer is set its length. You do this by calling the setLength() method:
StringBuffer str = new StringBuffer("String buffer"); str.setLength(40);
This method's single argument is the new length. If the new length is greater than the old length, both the string and buffer length are increased, with the additional characters being filled with zeroes. If the new length is smaller than the old length, characters are chopped off the end of the string, but the buffer size remains the same.
If you want to be guaranteed a specific buffer size, you can call the ensureCapacity() method, like this:
StringBuffer str = new StringBuffer("String buffer"); str.ensureCapacity(512);
The ensureCapacity() method's argument is the new capacity for the buffer.
You can change a character in the string buffer by calling the setCharAt() method:
StringBuffer str = new StringBuffer("String buffer"); str.setCharAt(3, `X');
The setCharAt() method's arguments are the index of the character to change and the new character. In the previous example, the string buffer becomes "StrXng buffer."
Finally, you can add characters to the end of the string with the append() method and insert characters anywhere in the string with the insert() method. Both of these methods come in several versions that enable you to handle many different types of data. For example, to add a character version of an integer to the end of the string, do something like this:
StringBuffer str = new StringBuffer("String buffer"); int value = 15; str.append(value);
After this code executes, str contains "String buffer15." Similarly, you insert characters like this:
StringBuffer str = new StringBuffer("String buffer"); int value = 15; str.insert(6, value);
This code results in a string of "String15 buffer." The two arguments in the previous version of insert() are the index at which to insert the characters and the data object to insert.
If you've been using your computer for a while, you may remember the old-fashioned text adventure games where you would enter a command from the keyboard such as GET KEY AND OPEN DOOR, and the computer would follow your instructions. Programs like these had to parse a text string and separate the string into separate words. These words are called tokens, and you may yourself run into times when you would like to extract tokens from a text string. Java provides the StringTokenizer class for just this purpose.
Because StringTokenizer is not part of the java.lang package as String and StringBuffer are, you must include the correct package in your applet. That package is java.util, and you import it like this:
import java.util.StringTokenizer;
Or, if you want to import the entire util package, you could write this:
import java.util.*;
You can construct a StringTokenizer object in several ways, but the easiest is to supply the string you want to tokenize as the constructor's single argument, like this:
StringTokenizer tokenizer = new StringTokenizer("One Two Three Four Five");
This type of string tokenizer uses space characters as the separators (called delimiters) between the tokens. To get a token, you call the nextToken() method:
String token = tokenizer.nextToken();
Each time you call nextToken(), you get the next token in the string. Usually, you extract tokens using a while loop. To control the while loop, you call the hasMoreTokens() method, which returns true as long as there are more tokens in the string. A typical tokenizer loop might look like this:
while (tokenizer.hasMoreTokens()) String token = tokenizer.nextToken();
You can also determine how may tokens are in the string by calling the countTokens() method:
StringTokenizer tokenizer = new StringTokenizer("One Two Three Four Five"); int count = tokenizer.countTokens();
In this example, count equals 5.
Listing 17.7 is an applet that tokenizes any string you enter. When you run the
applet, enter a string into the first text box. Then, click the Tokenize button to
get a list of tokens in the string (see Figure 17.6).
FIG. 17.6
The TokenApplet can extract individual words from a string.
import java.awt.*; import java.applet.*; import java.util.StringTokenizer; public class TokenApplet extends Applet { TextField textField1; Button button1; public void init() { textField1 = new TextField(30); add(textField1); button1 = new Button("Tokenize"); add(button1); resize(300, 300); } public void paint(Graphics g) { String str = textField1.getText(); StringTokenizer tokenizer = new StringTokenizer(str); int row = 110; while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); g.drawString(token, 80, row); row += 20; } } public boolean action(Event evt, Object arg) { if (arg == "Tokenize") { repaint(); return true; } else return false; } }
Because every system handles fonts in a different way, you have to be careful with how you use fonts in your applets. Although Java does its best to match fonts, to be sure that your displays look right--you have to handle fonts carefully. In order to help in this task, Java has a Font class that enables you to not only create and display fonts, but also to retrieve information about fonts. Because all text displayed in a Java program uses the current font (including the text used in components like buttons), no chapter on strings would be complete without a discussion of fonts.
Every font that you can use with your Java applets is associated with a group of attributes that determines the size and appearance of the font. The most important of these attributes is the font's name, which determines the font's basic style. You can easily get information about the currently active font. Start by calling the Graphics object's getFont() method, like this:
Font font = g.getFont();
The getFont() method returns a Font object for the current font. Once you have the Font object, you can use the Font class's various methods to obtain information about the font. Table 17.1 shows the most commonly used public methods of the Font class and what they do.
Method | Description |
getFamily() | Returns the family name of the font. |
getName() | Returns the name of the font. |
getSize() | Returns the size of the font. |
getStyle() | Returns the style of the font, where 0 is plain, 1 is bold, 2 is italic, and 3 is bold italic. |
isBold() | Returns a boolean value indicating whether the font is bold. |
isItalic() | Returns a boolean value indicating whether the font is italic. |
isPlain() | Returns a boolean value indicating whether the font is plain. |
toString() | Returns a string of information about the font. |
NOTE: Most of the general font handling methods are also available inside your applet class. For example, you can call getFont() from within your applet's init() method, without having to worry about Graphics objects. The same is true for getFontMetrics() and setFont(), which you learn about in the sections "Getting Font Metrics" and "Using the Font."
import java.awt.*; import java.applet.*; public class FontApplet extends Applet { public void paint(Graphics g) { Font font = getFont(); String name = font.getName(); String family = font.getFamily(); int n = font.getStyle(); String style; if (n == 0) style = "Plain"; else if (n == 1) style = "Bold"; else if (n == 2) style = "Italic"; else style = "Bold Italic"; n = font.getSize(); String size = String.valueOf(n); String info = font.toString(); String s = "Name: " + name; g.drawString(s, 50, 50); s = "Family: " + family; g.drawString(s, 50, 65); s = "Style: " + style; g.drawString(s, 50, 80); s = "Size: " + size; g.drawString(s, 50, 95); g.drawString(info, 20, 125); } }
As you can see from Listing 17.8, using the Font class's methods is fairly straightforward. Just call the method, which returns a value that describes some aspect of the font represented by the Font object.
In many cases, the information you can retrieve from a Font object is enough to keep you out of trouble. For example, by using the size returned by the getSize() method, you can properly space the lines of text. Sometimes, though, you want to know more about the font you're using. For example, you might want to know the width of a particular character or even the width in pixels of an entire text string. In these cases, you need to work with text metrics, which are more detailed font attributes.
True to form, the Java Developer's Kit includes the FontMetrics class, which makes it easy to obtain information about fonts. You create a FontMetrics object like this:
FontMetrics fontMetrics = getFontMetrics(font);
The getFontMetrics() method returns a reference to a FontMetrics object for the active font. Its single argument is the Font object for which you want the font metrics.
Once you have the FontMetrics object, you can call its methods in order
to obtain detailed information about the associated font. Table 17.2 lists the most
commonly used methods.
Method | Description |
charWidth() | Returns the width of a character. |
getAscent() | Returns the font's ascent. |
getDescent() | Returns the font's descent. |
getFont() | Returns the associated Font object. |
getHeight() | Returns the font's height. |
getLeading() | Returns the font's leading (line spacing). |
stringWidth() | Returns the width of a string. |
toString() | Returns a string of information about the font. |
TIP: If you haven't used fonts before, some of the terms--leading, ascent, and descent--used in Table 17.2 may be unfamiliar to you. Leading (pronounced "ledding") is the amount of whitespace between lines of text. Ascent is the height of a character, from the baseline to the top of the character. Descent is the size of the area that accommodates the descending portions of letters, such as the tail on a lowercase g. Height is the sum of ascent, descent, and leading. Refer to Figure 17.8 for examples of each.
You may think an applet that always uses the default font is boring to look at. In many cases, you would be right. An easy way to spruce up an applet is to use different fonts. Luckily, Java enables you to create and set fonts for your applet. You do this by creating your own font object, like this:
Font font = new Font("TimesRoman", Font.PLAIN, 20);
The constructor for the Font class takes three arguments: the font name, style, and size. The style can be any combination of the font attributes that are defined in the Font class. Those attributes are Font.PLAIN, Font.BOLD, and Font.ITALIC.
Although you can create fonts with the plain, bold, or italic styles, you may at times need to combine font styles. Suppose, for example, that you wanted to use both bold and italic styles. The line
Font font = new Font("Courier", Font.BOLD + Font.ITALIC, 18);
gives you an 18-point, bold, italic, Courier font.
NOTE: A point is a measurement of a font's height and is equal to 1/72 of an inch.
After you've created the font, you have to tell Java to use the font. You do this by calling the setFont() method, like this:
setFont(font);
The next text displayed in your applet uses the new font. However, although you request a certain type and size of font, you can't be sure of what you'll get. The system tries its best to match the requested font, but you still need to know at least the size of the font you end up with. You can get all the information you need by creating a FontMetrics object, like this:
FontMetrics fontMetrics = getFontMetrics(font);
To get the height of a line of text, call the FontMetrics object's getHeight() method, like this:
int height = fontMetrics.getHeight();
CAUTION:
When creating a font, be aware that the user's system may not have a particular font loaded. In that case, Java chooses a default font as a replacement. This possible font substitution is a good reason to use methods like Font.getName() in order to see whether you got the font you wanted. You especially need to know the size of the font, so you can be sure to position your text lines properly.
import java.awt.*; import java.applet.*; public class FontApplet2 extends Applet { TextField textField; public void init() { textField = new TextField(10); add(textField); textField.setText("32"); } public void paint(Graphics g) { String s = textField.getText(); int height = Integer.parseInt(s); Font font = new Font("TimesRoman", Font.PLAIN, height); g.setFont(font); FontMetrics fontMetrics = g.getFontMetrics(font); height = fontMetrics.getHeight(); int row = 80; g.drawString("This is the first line.", 70, row); row += height; g.drawString("This is the second line.", 70, row); row += height; g.drawString("This is the third line.", 70, row); row += height; g.drawString("This is the fourth line.", 70, row); } public boolean action(Event event, Object arg) { repaint(); return true; } }
When you run FontApplet2, you see the window shown in Figure 17.9. The
size of the active font is shown in the text box at the top of the applet, and a
sample of the font appears below the text box. To change the size of the font, type
a new value into the text box and press Enter.
FIG. 17.9
This is Appletviewer running FontApplet2.
The spacing of the lines is accomplished by first creating a variable to hold the vertical position for the next line of text:
int row = 80;
Here, the program not only declares the row variable, but also initializes it with the vertical position of the first row of text.
The applet then prints the first text line, using row for drawString()'s third argument:
g.drawString("This is the first line.", 70, row);
In preparation for printing the next line of text, the program adds the font's height to the row variable:
row += height;
Each line of text is printed, with row being incremented by the font's height in between, like this:
g.drawString("This is the second line.", 70, row); row += height; g.drawString("This is the third line.", 70, row);