Objects
Contents
- Overview
- Sample Program
- Instantiate an Object
- Variables
- Using Objects
- Variable Access
- Calling an Object's Methods
- Method Access
- Cleaning Up Unused Objects
- The Garbage Collector
- Finalization
- Characters
- Strings
- Creating Strings and StringBuffers
- Accessor Methods
- Modifying StringBuffers
- Inserting Characters
- Setting Characters
- Converting Objects to Strings
- The toString Method
- The valueOf Method
- Converting Strings to Numbers
- Strings and the Java Compiler
- Literal Strings
- Concatenation and the + Operator
- Arrays
- Creating and Using Arrays
- Declaring a Variable to Refer to an Array
- Creating an Array
- Accessing an Array Element
- Getting the Size of an Array
- Array Initializers
- Arrays of Objects
- Arrays of Arrays
- Copying Arrays
Overview
A Java program can create many objects, which interact with one another using messages. Spent objects, which have completed their life-cycle, are garbage-collected and their resources are recycled.
Sample Program
CreateObjectDemo creates a point object, p1, and two rectangle objects, r1 and r2.
It calls two classes, Point.java and Rectangle.java, which must be compiled.public class CreateObjectDemo { public static void main(String[] args) { // Create a point object and two rectangle objects Point p1 = new Point(23, 94); Rectangle r1 = new Rectangle(p1, 100, 200); Rectangle r2 = new Rectangle(50, 100); // Display r1's width, height, and area System.out.println("Width of r1: " + r1.width); System.out.println("Height of r1: " + r1.height); System.out.println("Area of r1: " + r1.area()); // Set r2's position r2.origin = p1; // Display r2's position System.out.println("X Position of r2: " + r2.origin.x); System.out.println("Y Position of r2: " + r2.origin.y); // move r2 and display its new position r2.move(40, 72); System.out.println("X Position of r2: " + r2.origin.x); System.out.println("Y Position of r2: " + r2.origin.y); } }Here is sample output:
Width of r1: 100 Height of r1: 200 Area of r1: 20000 X Position of r2: 23 Y Position of r2: 94 X Position of r2: 40 Y Position of r2: 72The new operator creates and allocates space for a new object, in a process called instantiation.
The new operator is followed by a call to a constructor. which initializes the new object.
To declare a variable, you write:
type nameIn addition to the primitive types, such as int and boolean, classes and interfaces are also types. The sample program above uses both the Point and the Rectangle class names as types. The code Point p1 does not create a new Point object; it just declares a variable. The reference is empty until assigned. An empty reference is known as a null reference.
Instantiating an Object
The "new" operator instantiates a class using a constructor, returning a reference to the object created. Here's the code for the Point class:public class Point { public int x = 0; public int y = 0; //A constructor! public Point(int x, int y) { this.x = x; this.y = y; } }This class contains a single constructor. You can recognize a constructor because it has the same name as the class and has no return type.
The constructor in the Point class takes two integer arguments, as declared by the code (int x, int y). The following statement provides 23 and 94 as values for those arguments:
Point p1 = new Point(23, 94);Here's the code for the Rectangle class, which contains four constructors:
Each constructor lets you provide initial values for different aspects of the rectangle: the origin; the width, and the height; all three; or none. If a class has multiple constructors, they all have the same name but a different number of arguments or different typed arguments. The Java platform differentiates the constructors, based on the number and the type of the arguments. When the Java platform encounters the following code, it knows to call the constructor in the Rectangle class that requires a Point argument followed by two integer arguments:public class Rectangle { public int width = 0; public int height = 0; public Point origin; //Four constructors public Rectangle() { origin = new Point(0, 0); } public Rectangle Point(p) { origin = p; } public Rectangle(int w, int h) { this(new Point(0, 0), w, h); } public Rectangle Point(p, int w, int h) { origin = p; width = w; height = h; } //A method for moving the rectangle public void move(int x, int y) { origin.x = x; origin.y = y; } //A method for computing the area of the rectangle public int area() { return width * height; } }This call initializes the rectangle's origin variable to the Point object referred to by p1. The code also sets width to 100 and height to 200. Now there are two references to the same Point object; an object can have multiple references to it.Rectangle r1 = new Rectangle(p1, 100, 200);Multiple references can refer to the same object. The following line of code calls the constructor that requires two integer arguments, which provide the initial values for width and height. If you inspect the code within the constructor, you will see that it creates a new Point object whose x and y values are initialized to 0:
The Rectangle constructor used in the following statement doesn't take any arguments, so it's called a no-argument constructor:Rectangle r2 = new Rectangle(50, 100);If a class does not explicitly declare any constructors, the Java platform automatically provides a no-argument constructor, called the default constructor, that does nothing. Thus, all classes have at least one constructor.Rectangle rect = new Rectangle();
Using Objects
You can basically do two things with Objects:
- Manipulate variables
- Call methods
You may use a simple name for an instance variable when the instance variable is in scope, that is, within code for the object's class. Code that is outside the object's class must use a qualified name. For example, the code in the CreateObjectDemo class is outside the code for the Rectangle class. So to refer to the origin, width, and height variables within the Rectangle object named r1, the CreateObjectDemo class must use the names r1.origin, r1.width, and r1.height, respectively. The program uses two of these names to display the width and the height of r1: The following is the general form of a qualified name, which is also known as a long name:objectReference.variableNameAttempting to use the simple names width and height from the code in the CreateObjectDemo class doesn't make sense-those variables exist only within an object-and results in a compiler error.System.out.println("Width of r1: " + r1.width); System.out.println("Height of r1: " + r1.height);Later, the program uses similar code to display information about r2. Objects of the same type have their own copy of the same instance variables. Thus, each Rectangle object has variables named origin, width, and height. When you access an instance variable through an object reference, you reference that particular object's variable. The two objects r1 and r2 in the CreateObjectDemo program have different origin, width, and height variables.
The first part of the variable's qualified name, objectReference, must be a reference to an object. You can use the name of a reference variable here, as in the previous examples, or you can use any expression that returns an object reference. Recall that the new operator returns a reference to an object. So you could use the value returned from new to access a new object's variables:
This statement creates a new Rectangle object and immediately gets its height. In essence, the statement calculates the default height of a Rectangle. Note that after this statement has been executed, the program no longer has a reference to the created Rectangle, because the program never stored the reference in a variable. The object is unreferenced, and its resources can be recycled by the Java platform.int height = new Rectangle().height;
Calling an Object's Methods
You also use qualified names to call an object's method. To form the qualified name of a method, you append the method name to an object reference, with an intervening period (.). Also, you provide, within enclosing parentheses, any arguments to the method. If the method does not require any arguments, use empty parentheses.The Rectangle class has two methods: area to compute the rectangle's area and move to change the rectangle's origin. Here's the CreateObjectDemo code that calls these two methods:objectReference.methodName(argumentList); or objectReference.methodName();The first statement calls r1's area method and displays the results. The second line moves r2 because the move method assigns new values to the object's origin.x and origin.y.System.out.println("Area of r1: " + r1.area()); ... r2.move(40, 72);As with instance variables, objectReference must be a reference to an object. You can use a variable name, but you also can use any expression that returns an object reference. The new operator returns an object reference, so you can use the value returned from new to call a new object's methods:
The expression new Rectangle(100, 50) returns an object reference that refers to a Rectangle object. As shown, you can use the dot notation to call the new Rectangle's area method to compute the area of the new rectangle.new Rectangle(100, 50).area()Some methods, such as area, return a value. For methods that return a value, you can use the method call in expressions. You can assign the return value to a variable, use it to make decisions, or control a loop. This code assigns the value returned by areato a variable:
Remember, invoking a method on a particular object is the same as sending a message to that object. In this case, the object that areais invoked on is the rectangle returned by the constructor.int areaOfRectangle = new Rectangle(100, 50).area();
Method Access
The methods in our Point and Rectangle classes are all declared public, so they are accessible to any other class. Sometimes, a class needs to restrict access to its methods. For example, a class might have a method that only subclasses are allowed to call. A class can use the same mechanism to control access to its methods as it uses to control access to its variables.
Cleaning Up Unused Objects
Some object-oriented languages require that you keep track of all the objects you create and that you explicitly destroy them when they are no longer needed. Managing memory explicitly is tedious and error prone. The Java platform allows you to create as many objects as you want (limited, of course, by what your system can handle), and you don't have to worry about destroying them. The Java runtime environment deletes objects when it determines that they are no longer being used. This process is called garbage collection.
An object is eligible for garbage collection when there are no more references to that object. References that are held in a variable are usually dropped when the variable goes out of scope. Or, you can explicitly drop an object reference by setting the variable to the special value null. Remember that a program can have multiple references to the same object; all references to an object must be dropped before the object is eligible for garbage collection.
The Garbage Collector
The Java runtime environment has a garbage collector that periodically frees the memory used by objects that are no longer referenced. The garbage collector does its job automatically, although, in some situations, you may want to run the garbage collection explicitly by calling the gc method in the System class. For instance, you might want to run the garbage collector after a section of code that creates a large amount of garbage or before a section of code that needs a lot of memory.
Finalization
Before an object gets garbage-collected, the garbage collector gives the object an opportunity to clean up after itself through a call to the object's finalize method. This process is known as finalization.Most programmers don't have to worry about implementing the finalize method. In rare cases, however, a programmer might have to implement a finalize method to release resources, such as native peers, that aren't under the control of the garbage collector.
The finalize method is a member of the Object class, which is the top of the Java platform's class hierarchy and a superclass of all classes. A class can override the finalize method to perform any finalization necessary for objects of that type. If you override finalize, your implementation of the method should call super.finalize as the last thing it does.
Characters
An object of Character type contains a single character value. You use a Character object instead of a primitive char variable when an object is required-for example, when passing a character value into a method that changes the value or when placing a character value into a data structure, such as a vector, that requires objects. The following sample program, CharacterDemo, creates a few character objects and displays some information about them. The code that is related to the Character class is shown in boldface:The following is the output from this program:public class CharacterDemo { public static void main(String args[]) { Character a = new Character('a'); Character a2 = new Character('a'); Character b = new Character('b'); int difference = a.compareTo(b); if (difference == 0) { System.out.println("a is equal to b."); } else if (difference < 0) { System.out.println("a is less than b."); } else if (difference > 0) { System.out.println("a is greater than b."); } System.out.println("a is " + ((a.equals(a2)) ? "equal" : "not equal") + " to a2."); System.out.println("The character " + a.toString() + " is " + (Character.isUpperCase(a.charValue()) ? "upper" : "lower") + "case."); } }The CharacterDemo program calls the following constructors and methods provided by the Character class:a is less than b. a is equal to a2. The character a is lowercase.
- Character(char)
- The Character class's only constructor, which creates a Character object containing the value provided by the argument. Once a Character object has been created, the value it contains cannot be changed.
- compareTo(Character)
- An instance method that compares the values held by two character objects: the object on which the method is called (a in the example) and the argument to the method (b in the example). This method returns an integer indicating whether the value in the current object is greater than, equal to, or less than the value held by the argument. A letter is greater than another letter if its numeric value is greater.
- equals(Object)
- An instance method that compares the value held by the current object with the value held by another. This method returns true if the values held by both objects are equal.
- toString()
- An instance method that converts the object to a string. The resulting string is one character in length and contains the value held by the character object.
- charValue()
- An instance method that returns the value held by the character object as a primitive char value.
- isUpperCase(char)
- A class method that determines whether a primitive char value is uppercase. This is one of many Character class methods that inspect or manipulate character data.
Strings
There are two string classes, String and StringBuffer, which provide for strings whose value will not change.For example, if you write a method that requires string data and the method is not going to modify the string in any way, pass a String object into the method. The StringBuffer class provides for strings that will be modified; you use string buffers when you know that the value of the character data will change. You typically use string buffers for constructing character data dynamically: for example, when reading text data from a file. Because strings are constants, they are more efficient to use than are string buffers and can be shared. So it's important to use strings when you can.
Following is a sample program called StringsDemo, which reverses the characters of a string. This program uses both a string and a string buffer.
The output from this program is:public class StringsDemo { public static void main(String[] args) { String palindrome = "Dot saw I was Tod"; int len = palindrome.length(); StringBuffer dest = new StringBuffer(len); for (int i = (len - 1); i >= 0; i--) { dest.append(palindrome.charAt(i)); } System.out.println(dest.toString()); } }In addition to highlighting the differences between strings and string buffers, this section discusses several features of the String and StringBuffer classes: creating strings and string buffers, using accessor methods to get information about a string or string buffer, and modifying a string buffer.doT saw I was toD
Creating Strings and StringBuffers
A string is often created from a string literal--a series of characters enclosed in double quotes. For example, when it encounters the following string literal, the Java platform creates a String object whose value is Gobbledygook.The StringsDemo program uses this technique to create the string referred to by the palindrome variable:"Gobbledygook"You can also create String objects as you would any other Java object: using the new keyword and a constructor. The String class provides several constructors that allow you to provide the initial value of the string, using different sources, such as an array of characters, an array of bytes, or a string buffer.String palindrome = "Dot saw I was Tod";Here's an example of creating a string from a character array:
The last line of this code snippet displays: hello.char[] helloArray = { 'h', 'e', 'l', 'l', 'o' }; String helloString = new String(helloArray); System.out.println(helloString);You must always use new to create a string buffer. The StringsDemo program creates the string buffer referred to by dest, using the constructor that sets the buffer's capacity:
This code creates the string buffer with an initial capacity equal to the length of the string referred to by the name palindrome. This ensures only one memory allocation for dest because it's just big enough to contain the characters that will be copied to it. By initializing the string buffer's capacity to a reasonable first guess, you minimize the number of times memory must be allocated for it. This makes your code more efficient because memory allocation is a relatively expensive operation.String palindrome = "Dot saw I was Tod"; int len = palindrome.length(); StringBuffer dest = new StringBuffer(len);
Accessor Methods
Getting the Length of a String or a String Buffer
Methods used to obtain information about an object are known as accessor methods. One accessor method that you can use with both strings and string buffers is the length method, which returns the number of characters contained in the string or the string buffer. After the following two lines of code have been executed, len equals 17:In addition to length, the StringBuffer class has a method called capacity, which returns the amount of space allocated for the string buffer rather than the amount of space used. For example, the capacity of the string buffer referred to by dest in the StringsDemo program never changes, although its length increases by 1 for each iteration of the loop.String palindrome = "Dot saw I was Tod"; int len = palindrome.length();A string buffer's length is the number of characters it contains; a string buffer's capacity is the number of character spaces that have been allocated. The String class doesn't have a capacity method, because a string cannot change.
Getting Characters by Index from a String or a String Buffer
You can get the character at a particular index within a string or a string buffer by using the charAt accessor. The index of the first character is 0; the index of the last is length()-1. For example, the following code gets the character at index 9 in a string:Indices begin at 0, so the character at index 9 is 'O'. Use the charAt method to get a character at a particular index. The figure also shows that to compute the index of the last character of a string, you have to subtract 1 from the value returned by the length method.String anotherPalindrome = "Niagara. O roar again!"; char aChar = anotherPalindrome.charAt(9);If you want to get more than one character from a string or a string buffer, you can use the substring method. The substring method has two versions, as shown in the following table:
Method Description String substring(int)
String substring(int, int)Returns a new string that is a substring of this string or string buffer.The first integer argument specifies the index of the first character. The second integer argument is the index of the last character -1. The length of the substring is therefore the first int minus the second int. If the second integer is not present, the substring extends to the end of the original string. The following code gets from the Niagara palindrome the substring that extends from index 11 to index 15, which is the word "roar":
Use the substring method to get part of a string or string buffer. Remember that indices begin at 0.String anotherPalindrome = "Niagara. O roar again!"; String roar = anotherPalindrome.substring(11, 15);
Modifying StringBuffers
The reverseIt method uses StringBuffer's append method to add a character to the end of the destination string: dest.If the appended character causes the size of the StringBuffer to grow beyond its current capacity, the StringBuffer allocates more memory. Because memory allocation is a relatively expensive operation, you can make your code more efficient by initializing a StringBuffer's capacity to a reasonable first guess, thereby minimizing the number of times memory must be allocated for it. For example, the reverseIt method constructs the StringBuffer with an initial capacity equal to the length of the source string, ensuring only one memory allocation for dest.class ReverseString { public static String reverseIt(String source) { int i, len = source.length(); StringBuffer dest = new StringBuffer(len); for (i = (len - 1); i >= 0; i--) { dest.append(source.charAt(i)); } return dest.toString(); } }The version of the append method used in reverseIt is only one of the StringBuffer methods that appends data to the end of a StringBuffer. There are several append methods that append data of various types, such as float, int, boolean, and even Object, to the end of the StringBuffer. The data is converted to a string before the append operation takes place.
Inserting Characters
At times, you may want to insert data into the middle of a StringBuffer. You do this with one of StringBufffer's insert methods. This example illustrates how you would insert a string into a StringBuffer:This code snippet prints:StringBuffer sb = new StringBuffer("Drink Java!"); sb.insert(6, "Hot "); System.out.println(sb.toString());With StringBuffer's many insert methods, you specify the index before which you want the data inserted. In the example, "Hot " needed to be inserted before the 'J' in "Java". Indices begin at 0, so the index for 'J' is 6. To insert data at the beginning of a StringBuffer, use an index of 0. To add data at the end of a StringBuffer, use an index equal to the current length of the StringBuffer or use append.Drink Hot Java!
Setting Characters
Another useful StringBuffer modifier is setCharAt, which replaces the character at a specific location in the StringBuffer with the character specified in the argument list.. setCharAt is useful when you want to reuse a StringBuffer.
Converting Objects to Strings
The toString Method
It's often convenient or necessary to convert an object to a String because you need to pass it to a method that accepts only String values. The reverseIt method used earlier in this lesson uses StringBuffer's toString method to convert the StringBuffer to a String object before returning the String.class ReverseString { public static String reverseIt(String source) { int i, len = source.length(); StringBuffer dest = new StringBuffer(len); for (i = (len - 1); i >= 0; i--) { dest.append(source.charAt(i)); } return dest.toString(); } }All classes inherit toString from the Object class and many classes in the java.lang package override this method to provide an implementation that is meaningful to that class. For example, the "type wrapper" classes--Character, Integer, Boolean, and the others--all override toString to provide a String representation of the object.
The valueOf Method
As a convenience, the String class provides the class method valueOf, You can use valueOf to convert variables of different types to Strings. For example, to print the value of pi:System.out.println(String.valueOf(Math.PI));
Converting Strings to Numbers
The String class itself does not provide any methods for converting a String to a floating point, integer, or other numerical type. However, four of the "type wrapper" classes (Integer, Double, Float, and Long) provide a class method named valueOf that converts a String to an object of that type. Here's a small, contrived example of the Float class's valueOf:String piStr = "3.14159"; Float pi = Float.valueOf(piStr);
Strings and the Java Compiler
The Java compiler uses the String and StringBuffer classes behind the scenes to handle literal strings and concatenation.
Literal Strings
In Java, you specify literal strings between double quotes:You can use literal strings anywhere you would use a String object. For example, System.out.println accepts a String argument, so you could use a literal string in place of a String there."Hello World!"You can also use String methods directly from a literal string.System.out.println("Might I add that you look lovely today.");Because the compiler automatically creates a new String object for every literal string it encounters, you can use a literal string to initialize a String.int len = "Goodbye Cruel World".length();String s = "Hola Mundo";The above construct is equivalent to, but more efficient than, this one, which ends up creating two Strings instead of one:
String s = new String("Hola Mundo");The compiler creates the first string when it encounters the literal string "Hola Mundo!", and the second one when it encounters new String.
Concatenation and the + Operator
In the Java programming language, you can use + to concatenate Strings together:This is a little deceptive because, as you know, Strings can't be changed. However, behind the scenes the compiler uses StringBuffers to implement concatenation. The above example compiles to:String cat = "cat"; System.out.println("con" + cat + "enation");You can also use the + operator to append values to a String that are not themselves Strings:String cat = "cat"; System.out.println(new StringBuffer().append("con"). append(cat).append("enation").toString());The compiler converts the non-String value (the integer 1 in the example) to a String object before performing the concatenation operation.System.out.println("Java's Number " + 1);The following code sample illustrates that s1 and s2 point to the same object until you use the += operator to assign a new String to s1.
Here's the output://Java programming language code String s1 = "hello"; String s2 = s1; System.out.println("s1 = " + s1 + "; s2 = " + s2); System.out.println("System.identityHashCode(s1) = " + System.identityHashCode(s1)); System.out.println("System.identityHashCode(s2) = " + System.identityHashCode(s2)); s1 += " world"; System.out.println("\ns1 = " + s1 + "; s2 = " + s2); System.out.println("System.identityHashCode(s1) = " + System.identityHashCode(s1)); System.out.println("System.identityHashCode(s2) = " + System.identityHashCode(s2));s1 points to a new address after " world" is appended.s1 = hello; s2 = hello System.identityHashCode(s1) = 2452092 System.identityHashCode(s2) = 2452092 s1 = hello world; s2 = hello System.identityHashCode(s1) = 7474923 System.identityHashCode(s2) = 2452092
Arrays
An array is a structure that holds multiple values of the same type. The length of an array is established when the array is created (at runtime). After creation, an array is a fixed-length structure. An array element is one of the values within an array and is accessed by its position within the array.If you want to store data of different types in a single structure, or if you need a structure whose size can change dynamically, use a Collection implementation, such as Vector, instead of an array.
Creating and Using Arrays
Here's a simple program, called ArrayDemo, that creates the array, puts some values in it, and displays the values.public class ArrayDemo { public static void main(String[] args) { int[] anArray; // declare an array of integers anArray = new int[10]; // create an array of integers // assign a value to each array element and print for (int i = 0; i < anArray.length; i++) { anArray[i] = i; System.out.print(anArray[i] + " "); } System.out.println(); } }
Declaring a Variable to Refer to an Array
This line of code from the sample program declares an array variable:Like declarations for variables of other types, an array declaration has two components: the array's type and the array's name. An array's type is written type[], where type is the data type of the elements contained within the array, and [] indicates that this is an array. Remember that all of the elements within an array are of the same type. The sample program uses int[], so the array called anArray will be used to hold integer data. Here are declarations for arrays that hold other types of data:int[] anArray; // declare an array of integersAs with declarations for variables of other types, the declaration for an array variable does not allocate any memory to contain the array elements. The sample program must assign a value to anArray before the name refers to an array.float[] anArrayOfFloats; boolean[] anArrayOfBooleans; Object[] anArrayOfObjects; String[] anArrayOfStrings;
Creating an Array
You create an array explicitly using Java's new operator The next statement in the sample program allocates an array with enough memory for ten integer elements and assigns the array to the variable anArray declared earlier.In general, when creating an array, you use the new operator plus the data type of the array elements, plus the number of elements desired enclosed within square brackets ('[' and ']').anArray = new int[10]; // create an array of integersIf the new statement were omitted from the sample program, the compiler would print an error like the following one and compilation would fail.new elementType[arraySize]ArrayDemo.java:4: Variable anArray may not have been initialized.
Accessing an Array Element
Now that some memory has been allocated for the array, the program assign values to the array elements:This part of the code shows that to reference an array element, either to assign a value to it, or to access the value, you append square brackets to the array name. The value between the square brackets indicates (either with a variable or some other expression) the index of the element to access. Note that in Java, array indices begin at 0 and end at the array length minus 1.for (int i = 0; i < anArray.length; i++) { anArray[i] = i; System.out.print(anArray[i] + " "); }
Getting the Size of an Array
To get the size of an array, you writeBe careful: Programmers new to the Java programming language are tempted to follow length with an empty set of parenthesis. This doesn't work because length is not a method. length is a property provided by the Java platform for all arrays.arrayname.lengthThe for loop in our sample program iterates over each element of anArray, assigning values to its elements. The for loop uses anArray.length to determine when to terminate the loop.
Array Initializers
The Java programming language provides a shortcut syntax for creating and initializing an array. Here's an example of this syntax:The length of the array is determined by the number of values provided between { and }.boolean[] answers = { true, false, true, true, false };
Arrays of Objects
Arrays can hold reference types as well as primitive types. You create such an array in much the same way you create an array with primitive types. Here's a small program, ArrayOfStringsDemo that creates an array containing three string objects then prints the strings in all lower case letters.This program creates and populates the array in a single statement. However, you can create an array without putting any elements in it. This brings us to a potential stumbling block, often encountered by new programmers, when using arrays that contain objects. Consider this line of code:public class ArrayOfStringsDemo { public static void main(String[] args) { String[] anArray = { "String One", "String Two", "String Three" }; for (int i = 0; i < anArray.length; i++) { System.out.println(anArray[i].toLowerCase()); } } }After this line of code is executed, the array called anArray exists and has enough room to hold 5 string objects. However, the array doesn't contain any strings yet. It is empty. The program must explicitly create strings and put them in the array. This might seem obvious, however, many beginners assume that the previous line of code creates the array and creates 5 empty strings in it. Thus they end up writing code like the following, which generates a NullPointerException:String[] anArray = new String[5];The problem is more likely to occur when the array is created in a constructor or other initializer and then used somewhere else in the program.String[] anArray = new String[5]; for (int i = 0; i < anArray.length; i++) { // ERROR: the following line gives a runtime error System.out.println(anArray[i].toLowerCase()); }
Arrays of Arrays
Arrays can contain arrays. ArrayOfArraysDemo creates an array and uses an initializer to populate it with four sub-arrays.Notice that the sub-arrays are all of different lengths. The names of the sub-arrays are cartoons[0], cartoons[1], and so on.public class ArrayOfArraysDemo { public static void main(String[] args) { String[][] cartoons = { { "Flintstones", "Fred", "Wilma", "Pebbles", "Dino" }, { "Rubbles", "Barney", "Betty", "Bam Bam" }, { "Jetsons", "George", "Jane", "Elroy", "Judy", "Rosie", "Astro" }, { "Scooby Doo Gang", "Scooby Doo", "Shaggy", "Velma", "Fred", "Daphne" } }; for (int i = 0; i < cartoons.length; i++) { System.out.print(cartoons[i][0] + ": "); for (int j = 1; j < cartoons[i].length; j++) { System.out.print(cartoons[i][j] + " "); } System.out.println(); } } }As with arrays of objects, you must explicitly create the sub-arrays within an array. So if you don't use an initializer, you need to write code like the following, which you can find in: ArrayOfArraysDemo2
You must specify the length of the primary array when you create the array. You can leave the length of the sub-arrays unspecified until you create them.public class ArrayOfArraysDemo2 { public static void main(String[] args) { int[][] aMatrix = new int[4][]; //populate matrix for (int i = 0; i < aMatrix.length; i++) { aMatrix[i] = new int[5]; //create sub-array for (int j = 0; j < aMatrix[i].length; j++) { aMatrix[i][j] = i + j; } } //print matrix for (int i = 0; i < aMatrix.length; i++) { for (int j = 0; j < aMatrix[i].length; j++) { System.out.print(aMatrix[i][j] + " "); } System.out.println(); } } }
Copying Arrays
Use System's arraycopy method to efficiently copy data from one array into another. The arraycopy method requires five arguments:The two Object arguments indicate the array to copy from and the array to copy to. The three integer arguments indicate the starting location in each the source and the destination array, and the number of elements to copy.public static void arraycopy(Object source, int srcIndex, Object dest, int destIndex, int length,The following program, ArrayCopyDemo, uses arraycopy to copy some elements from the copyFrom array to the copyTo array.
The arraycopy method call in this example program begins the copy at element number 2 in the source array. Recall that array indices start at 0, so that the copy begins at the array element 'c'. The arraycopy method call puts the copied elements into the destination array beginning at the first element (element 0) in the destination array copyTo. The copy copies 7 elements: 'c', 'a', 'f', 'f', 'e', 'i', and 'n'. Effectively, the arraycopy method takes the "caffein" out of "decaffeinated".public class ArrayCopyDemo { public static void main(String[] args) { char[] copyFrom = { 'd', 'e', 'c', 'a', 'f', 'f', 'e', 'i', 'n', 'a', 't', 'e', 'd' }; char[] copyTo = new char[7]; System.arraycopy(copyFrom, 2, copyTo, 0, 7); System.out.println(new String(copyTo)); } }Note that the destination array must be allocated before you call arraycopy and must be large enough to contain the data being copied.
Customizing Formats
You can use the DecimalFormat class to format decimal numbers into locale-specific strings. This class allows you to control the display of leading and trailing zeros, prefixes and suffixes, grouping (thousands) separators, and the decimal separator. If you want to change formatting symbols, such as the decimal separator, you can use the DecimalFormatSymbols in conjunction with the DecimalFormat class. These classes offer a great deal of flexibility in the formatting of numbers, but they can make your code more complex.The text that follows uses examples that demonstrate the DecimalFormat and DecimalFormatSymbols classes. The code examples in this material are from a sample program called DecimalFormatDemo.
Constructing Patterns
You specify the formatting properties of DecimalFormat with a pattern String. The pattern determines what the formatted number looks like. For a full description of the pattern syntax, see Number Format Pattern Syntax.The example that follows creates a formatter by passing a pattern String to the DecimalFormat constructor. The format method accepts a double value as an argument and returns the formatted number in a String:
The output for the preceding lines of code is described in the following table. The value is the number, a double , that is to be formatted. The pattern is the String that specifies the formatting properties. The output, which is a String, represents the formatted number.DecimalFormat myFormatter = new DecimalFormat(pattern); String output = myFormatter.format(value); System.out.println(value + " " + pattern + " " + output);
Output from DecimalFormatDemo Program
value pattern output Explanation 123456.789 ###,###.### 123,456.789 The pound sign (#) denotes a digit, the comma is a placeholder for the grouping separator, and the period is a placeholder for the decimal separator. 123456.789 ###.## 123456.79 The value has three digits to the right of the decimal point, but the pattern has only two. The format method handles this by rounding up. 123.78 000000.000 000123.780 The pattern specifies leading and trailing zeros, because the 0 character is used instead of the pound sign (#). 12345.67 $###,###.### $12,345.67 The first character in the pattern is the dollar sign ($). Note that it immediately precedes the leftmost digit in the formatted output. 12345.67 \u00A5###,###.### ¥12,345.67 The pattern specifies the currency sign for Japanese yen (¥) with the Unicode value 00A5.
Locale-Sensitive Formatting
The preceding example created a DecimalFormat object for the default Locale. If you want a DecimalFormat object for a nondefault Locale, you instantiate a NumberFormat and then cast it to DecimalFormat. Here's an example:NumberFormat nf = NumberFormat.getNumberInstance(loc); DecimalFormat df = (DecimalFormat)nf; df.applyPattern(pattern); String output = df.format(value); System.out.println(pattern + " " + output + " " + loc.toString());Running the previous code example results in the output that follows. The formatted number, which is in the second column, varies with Locale:
###,###.### 123,456.789 en_US ###,###.### 123.456,789 de_DE ###,###.### 123 456,789 fr_FRSo far the formatting patterns discussed here follow the conventions of U.S. English. For example, in the pattern ###,###.## the comma is the thousands-separator and the period represents the decimal point. This convention is fine, provided that your end users aren't exposed to it. However, some applications, such as spreadsheets and report generators, allow the end users to define their own formatting patterns. For these applications the formatting patterns specified by the end users should use localized notation. In these cases you'll want to invoke the applyLocalizedPattern method on the DecimalFormat object.
Altering the Formatting Symbols
You can use the DecimalFormatSymbols class to change the symbols that appear in the formatted numbers produced by the format method. These symbols include the decimal separator, the grouping separator, the minus sign, and the percent sign, among others.The next example demonstrates the DecimalFormatSymbols class by applying a strange format to a number. The unusual format is the result of the calls to the setDecimalSeparator, setGroupingSeparator, and setGroupingSize methods.
DecimalFormatSymbols unusualSymbols = new DecimalFormatSymbols(currentLocale); unusualSymbols.setDecimalSeparator('|'); unusualSymbols.setGroupingSeparator('^'); String strange = "#,##0.###"; DecimalFormat weirdFormatter = new DecimalFormat(strange, unusualSymbols); weirdFormatter.setGroupingSize(4); String bizarre = weirdFormatter.format(12345.678); System.out.println(bizarre);When run, this example prints the number in a bizarre format:
1^2345|678
Accessor Methods for the String Class
In addition to the length and charAt accessors you saw on the previous page, the String class provides two accessors that return the position within the string of a specific character or string: indexOf and lastIndexOf. The indexOf method searches forward from the beginning of the string, and lastIndexOf searches backward from the end of the string.
The indexOf and lastIndexOf methods are frequently used with substring, which returns a substring of the string. The following class illustrates the use of lastIndexOf and substring to isolate different parts of a filename.
The methods in the following Filename class don't do any error checking and assume that their argument contains a full directory path and a filename with an extension. If these methods were production code they would verify that their arguments were properly constructed.
// This class assumes that the string used to initialize // fullPath has a directory path, filename, and extension. // The methods won't work if it doesn't. public class Filename { private String fullPath; private char pathSeparator, extensionSeparator; public Filename(String str, char sep, char ext) { fullPath = str; pathSeparator = sep; extensionSeparator = ext; } public String extension() { int dot = fullPath.lastIndexOf(extensionSeparator); return fullPath.substring(dot + 1); } public String filename() { int dot = fullPath.lastIndexOf(extensionSeparator); int sep = fullPath.lastIndexOf(pathSeparator); return fullPath.substring(sep + 1, dot); } public String path() { int sep = fullPath.lastIndexOf(pathSeparator); return fullPath.substring(0, sep); } }Here's a small program that constructs a Filename object and calls all of its methods:
And here's the output from the program:public class FilenameDemo { public static void main(String[] args) { Filename myHomePage = new Filename("/home/mem/index.html", '/', '.'); System.out.println("Extension = " + myHomePage.extension()); System.out.println("Filename = " + myHomePage.filename()); System.out.println("Path = " + myHomePage.path()); } }The extension method uses lastIndexOf to locate the last occurrence of the period (.) in the filename. Then substring uses the return value of lastIndexOf to extract the filename extension--that is, the substring from the period to the end of the string. This code assumes that the filename actually has a period in it; if the filename does not have a period , then lastIndexOf returns -1, and the substring method throws a StringIndexOutOfBoundsException.Extension = html Filename = index Path = /home/memAlso, notice that extension uses dot + 1 as the argument to substring. If the period character is the last character of the string, then dot + 1 is equal to the length of the string which is one larger than the largest index into the string (because indices start at 0). However, substring accepts an index equal to but not greater than the length of the string and interprets it to mean "the end of the string."
Try this: Inspect the other methods in the Filename class and notice how the lastIndexOf and substring methods work together to isolate different parts of a filename.While the methods in the example above use only one version of the lastIndexOf method, the String class actually supports four different versions of both the indexOf and lastIndexOf methods. The four versions work as follows:
- indexOf(int character)
- lastIndexOf(int character)
- Return the index of the first (last) occurrence of the specified character.
- indexOf(int character, int from)
- lastIndexOf(int character, int from)
- Return the index of the first (last) occurrence of the specified character, searching forward (backward) from the specified index.
- indexOf(String string)
- lastIndexOf(String string)
- Return the index of the first (last) occurrence of the specified String.
- indexOf(String string, int from)
- lastIndexOf(String string, int from)
- Return the index of the first (last) occurrence of the specified String, searching forward (backward) from the specified index.
For the StringBuffer Class
Like String, StringBuffer provides length and charAt accessor methods. In addition to these two accessors, StringBuffer also has a method called capacity. The capacity method differs from length in that it returns the amount of space currently allocated for the StringBuffer, rather than the amount of space used. For example, the capacity of the StringBuffer in the reverseIt method shown here never changes, while the length of the StringBuffer increases by one for each iteration of the loop:public class ReverseString { public static String reverseIt(String source) { int i, len = source.length(); StringBuffer dest = new StringBuffer(len); for (i = (len - 1); i >= 0; i--) dest.append(source.charAt(i)); return dest.toString(); } }
Using Predefined Formats
By invoking the methods provided by the NumberFormat class, you can format numbers, currencies, and percentages according to Locale. The material that follows demonstrates formatting techniques with a sample program called NumberFormatDemo.
Numbers
You can use the NumberFormat methods to format primitive-type numbers, such as double, and their corresponding wrapper objects, such as Double.The following code example formats a Double according to Locale. Invoking the getNumberInstance method returns a locale-specific instance of NumberFormat. The format method accepts the Double as an argument and returns the formatted number in a String.
Double amount = new Double(345987.246); NumberFormat numberFormatter; String amountOut; numberFormatter = NumberFormat.getNumberInstance(currentLocale); amountOut = numberFormatter.format(amount); System.out.println(amountOut + " " + currentLocale.toString());The output from this example shows how the format of the same number varies with Locale:
345 987,246 fr_FR 345.987,246 de_DE 345,987.246 en_US
Currencies
If you're writing business applications, you'll probably need to format and to display currencies. You format currencies in the same manner as numbers, except that you call getCurrencyInstance to create a formatter. When you invoke the format method, it returns a String that includes the formatted number and the appropriate currency sign.This code example shows how to format currency in a locale-specific manner:
Double currency = new Double(9876543.21); NumberFormat currencyFormatter; String currencyOut; currencyFormatter = NumberFormat.getCurrencyInstance(currentLocale); currencyOut = currencyFormatter.format(currency); System.out.println(currencyOut + " " + currentLocale.toString());The output generated by the preceding lines of code is as follows:
9 876 543,21 F fr_FR 9.876.543,21 DM de_DE $9,876,543.21 en_USAt first glance this output may look wrong to you, because the numeric values are all the same. Of course, 9 876 543,21 F is not equivalent to 9.876.543,21 DM. However, bear in mind that the NumberFormat class is unaware of exchange rates. The methods belonging to the NumberFormat class format currencies but do not convert them.
Percentages
You can also use the methods of the NumberFormat class to format percentages. To get the locale-specific formatter, invoke the getPercentInstance method. With this formatter, a decimal fraction such as 0.75 is displayed as 75%.The following code sample shows how to format a percentage.
Double percent = new Double(0.75); NumberFormat percentFormatter; String percentOut; percentFormatter = NumberFormat.getPercentInstance(currentLocale); percentOut = percentFormatter.format(percent);
Formatting Numbers
Programs store and operate on numbers in a locale-independent way. Before displaying or printing a number, a program must convert it to a String that is in a locale-sensitive format. For example, in France the number 123456.78 should be formatted as 123 456,78, and in Germany it should appear as 123.456,78. In this section, you will learn how to make your programs independent of the locale conventions for decimal points, thousands-separators, and other formatting properties.
Using Predefined Formats
Using the the factory methods provided by the NumberFormat class, you can get locale-specific formats for numbers, currencies, and percentages.
Formatting with Patterns
With the DecimalFormat class you specify a number's format with a String pattern. The DecimalFormatSymbols class allows you to modify formatting symbols such as decimal separators and minus signs.
Number Format Pattern Syntax
You can design your own format patterns for numbers by following the rules specified by the following BNF diagram:The notation used in the preceding diagram is explained in the following table:pattern := subpattern{;subpattern} subpattern := {prefix}integer{.fraction}{suffix} prefix := '\\u0000'..'\\uFFFD' - specialCharacters suffix := '\\u0000'..'\\uFFFD' - specialCharacters integer := '#'* '0'* '0' fraction := '0'* '#'*In the preceding BNF diagram, the first subpattern specifies the format for positive numbers. The second subpattern, which is optional, specifies the format for negative numbers.
Notation Description X* 0 or more instances of X (X | Y) either X or Y X..Y any character from X up to Y, inclusive S - T characters in S, except those in T {X} X is optional Although not noted in the BNF diagram, a comma may appear within the integer portion.
Within the subpatterns, you specify formatting with special symbols. These symbols are described in the following table:
Symbol Description 0 a digit # a digit, zero shows as absent . placeholder for decimal separator , placeholder for grouping separator E separates mantissa and exponent for exponential formats ; separates formats - default negative prefix % multiply by 100 and show as percentage ? multiply by 1000 and show as per mille ¤ currency sign; replaced by currency symbol; if doubled, replaced by international currency symbol; if present in a pattern, the monetary decimal separator is used instead of the decimal separator X any other characters can be used in the prefix or suffix ' used to quote special characters in a prefix or suffix
Summary of Numbers
You use an instance of one of the Number classes-Byte, Double, Float, Integer, Long, and Short-to contain a number of primitive type. You can also use BigInteger and BigDecimal for arbitrary-precision numbers.The Number classes include class methods and constants, which are useful in a variety of ways. The MIN_VALUE and MAX_VALUE constants contain the smallest and largest values that can be contained by an object of that type. The byteValue, shortValue, and similar methods convert one numeric type to another. The valueOf method converts a string to a number, and the toString method converts a number to a string.
To format a number to display to an end user, you use the NumberFormat class in the java.text package. When using NumberFormat, you can get a default format for decimal numbers, percentages, or currency. Or, you can design a custom format using patterns.
The Math class contains a variety of class methods for performing mathematical functions. This class includes the trigonometric functions, such as computing sine, cosine, and so on. Math also includes functions for logarithm calculations, as well as basic arithmetic functions, such as rounding. Finally, Math contains a method, random, for generating random numbers.
Summary of Strings
The following table summarizes the methods found in the String and StringBuffer classes.
Summary of the String Class
Method or Constructor Purpose String()
String (byte[])
String (byte[], int, int)
String (byte[], int, int, String)
String (byte[], String)
String (char[])
String (char[], int, int)
String (String)
String (StringBuffer)Creates a new string object. For all constructors that take arguments, the first argument provides the value for the string.
Other Interesting Features
String and StringBuffer provide several other useful ways to manipul ate string data, including concatenation, comparison, substitution, and conversion to upper and lower case. java.lang.String and java.lang.StringBuffer summarize and list all of the methods and variables supported by these two classes.Here's a fun program, Palindrome, that determines whether a string is a palindrome. This program uses many methods from the String and the StringBuffer classes:
The output from this program is:public class Palindrome { public static boolean isPalindrome(String stringToTest) { String workingCopy = removeJunk(stringToTest); String reversedCopy = reverse(workingCopy); return reversedCopy.equalsIgnoreCase(workingCopy); } protected static String removeJunk(String string) { int i, len = string.length(); StringBuffer dest = new StringBuffer(len); char c; for (i = (len - 1); i >= 0; i--) { c = string.charAt(i); if (Character.isLetterOrDigit(c)) { dest.append(c); } } return dest.toString(); } protected static String reverse(String string) { StringBuffer sb = new StringBuffer(string); return sb.reverse().toString(); } public static void main(String[] args) { String string = "Madam, I'm Adam."; System.out.println(); System.out.println("Testing whether the following " + "string is a palindrome:"); System.out.println(" " + string); System.out.println(); if (isPalindrome(string)) { System.out.println("It IS a palindrome!"); } else { System.out.println("It is NOT a palindrome!"); } System.out.println(); } }Testing whether the following string is a palindrome: Madam, I'm Adam. It IS a palindrome!