The Essence of OOP using Java, Array Objects, Part 3

Baldwin discusses various details regarding array objects in Java, including:  members of an array object, interfaces implemented by array objects, Class objects and array objects, and the classes named Array and Arrays.

Published:  July 5, 2002
By Richard G. Baldwin

Java Programming Notes # 1626


Preface

This series of lessons is designed to teach you about the essence of Object-Oriented Programming (OOP) using Java.

The first lesson in the group was entitled The Essence of OOP Using Java, Objects, and Encapsulation.  The previous lesson was entitled The Essence of OOP using Java,  Array Objects, Part 2.

You may find it useful to open another copy of this lesson in a separate browser window.  That will make it easier for you to scroll back and forth among the different listings while you are reading about them.

For further reading, see my extensive collection of online Java tutorials at Gamelan.com. A consolidated index is available at Baldwin's Java Programming Tutorials.

Preview

This lesson discusses various details regarding the use of array objects in Java, including:

Discussion and Sample Code

Members of an array object

An array object has the following members (in addition to the data stored in the object):

Implements Cloneable and Serializable

Also, every array object implements the Cloneable and Serializable interfaces.  (Note that neither of these interfaces declares any methods.)

What is the Cloneable interface?

Here is what Sun has to say about the Cloneable interface:

"A class implements the Cloneable interface to indicate to the Object.clone() method that it is legal for that method to make a field-for-field copy of instances of that class.  Attempts to clone instances that do not implement the Cloneable interface result in the exception CloneNotSupportedException being thrown."
Thus, the fact than an array object implements the Cloneable interface makes it possible to clone array objects.

A cloned array is shallow

While it is possible to clone arrays, care must be exercised when cloning multidimensional arrays.  That is because a clone of a multidimensional array is shallow.

What does shallow mean?

Shallow means that the cloning process creates only a single new array.

Subarrays are shared between the original array and the clone.

(Although I'm not certain, I suspect that this may also be the case for cloning array objects containing references to ordinary objects.  I will leave that determination as an exercise for the student.  In any event, be careful if you clone array objects.)
Serialization

Serialization of an object is the process of decomposing the object into a stream of bytes, which can later be recomposed into a copy of the object.  Here is what Sun has to say about the Serializable interface:

"Serializability of a class is enabled by the class implementing the java.io.Serializable interface. Classes that do not implement this interface will not have any of their state serialized or deserialized.

All subtypes of a serializable class are themselves serializable.

The serialization interface has no methods or fields and serves only to identify the semantics of being serializable."

Even though this quotation from Sun doesn't address array objects, because array objects implement the Serializable interface, they can be serialized and later reconstructed.

Class objects representing array objects

An object of the class named Class can be obtained (by invoking the getClass method of the Object class) to represent the class from which an ordinary object was instantiated.

The Class object is able to answer certain questions about the class that it represents (such as the name of the superclass), and has other uses as well.

(One of the other uses is to specify the type as a parameter to the methods of the Array class, which I will illustrate later in this lesson.)
Every array also has an associated Class object.

That Class object is shared with all other arrays with the same component type.

The superclass of an array type is Object(Think about this!)

An array of characters is not a string

For the benefit of the C/C++ programmers in the audience, an array of char is not a String(In Java, a string is an object of the String class or the StringBuffer class).

Not terminated by null

Also, neither a String object nor an array of type char is terminated by '\u0000' (the NUL character).  (This information is provided for the benefit of C programmers who are accustomed to working with so-called null-terminated strings.  If you're not a C programmer, don't worry about this.)

A String object in Java is immutable

Once initialized, the contents of a Java String object never change.

On the other hand, an array of type char has mutable elements. The String class provides a method named toCharArray, which returns an array of characters containing the same character sequence as a String.

StringBuffer objects

The class named StringBuffer also provides a variety of methods that work with arrays of characters.  The contents of a StringBuffer object are mutable.

The Array and Arrays classes

The classes named Array and Arrays provide methods that you can use to work with array objects.

The Array class provides static methods to dynamically create and access Java arrays.

The Arrays class contains various methods for manipulating arrays (such as sorting and searching). It also contains a static factory method that allows arrays to be viewed as lists.

A sample program named Array08

The sample program named Array08 (shown in Listing 12 near the end of the lesson) illustrates the use of some of these methods.

Will discuss in fragments

As usual, I will discuss this program in fragments.  Essentially all of the interesting code is in the method named main, so I will begin my discussion there.  The first few fragments will illustrate the creation, population, and display of a one-dimensional array object whose elements contain references to objects of type String.

The newInstance method of the Array class

The code in Listing 1 invokes the static method of the Array class named newInstance to create the array object and to store the object's reference in a reference variable of type Object named v1.

(Note that there are two overloaded versions of the newInstance method in the Array class.  I will discuss the other one later.)
    Object v1 = Array.newInstance(
              Class.forName(
               "java.lang.String"), 3);

Listing 1

Two parameters required

This version of the newInstance method requires two parameters.  The first parameter specifies the component type.  This must be a reference to a Class object representing the component type of the new array object.

The second parameter, of type int, specifies the length of the new array object.

The Class object

The second parameter that specifies the array length is fairly obvious.  However, you may need some help with the first parameter.  Here is part of what Sun has to say about a Class object.

"Instances of the class Class represent classes and interfaces in a running Java application. Every array also belongs to a class that is reflected as a Class object that is shared by all arrays with the same element type and number of dimensions. The primitive Java types (boolean, byte, char, short, int, long, float, and double), and the keyword void are also represented as Class objects."
Getting a reference to a Class object

I know of three ways to get (or refer to) a Class object.

Class objects for primitive types

There are nine predefined Class objects that represent the eight primitive types and void. These are created by the Java Virtual Machine, and have the same names as the primitive types that they represent: boolean, byte, char, short, int, long, float, and double.  You can refer to these class objects using the following syntax: boolean.class, int.class, etc.  I will illustrate this later in this lesson.

The getClass method

If you have a reference to a target object (ordinary object or array object), you can gain access to a Class object representing the class from which that object was instantiated by invoking the getClass method of the Object class, on that object.  The getClass method returns a reference of type Class that refers to a Class object representing the class from which the target object was instantiated.

The forName method

The static forName method of the Class class accepts the name of a class or interface as an incoming String parameter, and returns the Class object associated with the class or interface having the given string name.  (The forName method cannot be used with primitive types as a parameter.)

Class object for the String class

Referring back to Listing 1, you will see that the first parameter passed to the newInstance method was a reference to a Class object representing the String class.  Thus, the statement in Listing 1 creates a one-dimensional array object, of component type String, three elements in length.  The reference to the array object is saved in the generic reference variable of type Object.

(In case you haven't recognized it already, this is an alternative to syntax such as new String[3].  Note that there are no square brackets in this alternative approach.  Thus, it might be said that this approach is more mainstream OOP than the approach that requires the use of square brackets.)
Populate the array object

The code in Listing 2 uses two static methods of the Array class to populate the three elements of the array object with references to objects of type String.
 
    for(int i = 0; i < 
             Array.getLength(v1); i++){
      Array.set(v1, i, "a"+i);
    }//end for loop

Listing 2

The getLength method

The getLength method is used in Listing 2 to get the length of the array for use in the conditional expression of a for loop.  Note that unlike the sample programs in the previous lesson (that stored the array object's reference as type Object), it was not necessary to cast the reference to type String[] in order to get the length.

The set method

The set method is used in Listing 2 to store references to String objects in the elements of the array object.  Again, unlike the programs in the previous lesson, it was not necessary to cast the array reference to type String[] to access the elements.  In fact, there are no square brackets anywhere in Listing 2.

Display the data

Listing 3 uses a similar for loop to display the contents of the String objects whose references are stored in the elements of the array object.
 
    for(int i = 0; i < 
             Array.getLength(v1); i++){
      System.out.print(
               Array.get(v1, i) + " ");
    }//end for loop

Listing 3

No square brackets

Once again, note that no casts, and no square brackets were required.  In fact, this approach makes it possible to deal with one-dimensional array objects using a syntax that is completely devoid of square brackets.  Rather than using square brackets to access array elements, this is a method-oriented approach to the use of array objects.  This makes it possible to treat array objects much the same as we treat ordinary objects in Java.

A two-dimensional rectangular array object tree

Next, I will use the methods of the Array class to create, populate, and display a rectangular two-dimensional array object tree, whose elements contain references to objects of the class String.

Another overloaded version of newInstance

To accomplish this, I will use the other overloaded version of the newInstance method.  This is the version that requires a reference to an array object of type int as the second parameter.

(Note that the Sun documentation describes two different behaviors for this method, depending on the whether the first parameter represents a non-array class or interface, or represents an array type.  This sample program illustrates the first possibility.)
The second parameter

As mentioned above, the version of the newInstance method that I am going to use requires a reference to an array object of type int as the second parameter.

(The length of the array object of type int specifies the number of dimensions of the multi-dimensional array object.  The contents of the elements of the array object of type int specify the sizes of those dimensions.)
Thus, my first task is to create and populate an array object of type int.

An array object of type int

Listing 4 shows the code required to create and populate the array object of type int.  This is a one-dimensional array object having two elements (length equals 2).  The first element is populated with the int value 2 and the second element is populated with the int value 3.
 
    Object v2 = Array.newInstance(
                         int.class, 2);
    Array.setInt(v2, 0, 2);
    Array.setInt(v2, 1, 3);

Listing 4

Why do we need this array object?

When this array object is used later, in conjunction with the version of the newInstance method that requires a reference to an array object of type int as the second parameter, this array object will specify an array object having two dimensions (a rectangular array). The rectangular array will have two rows and three columns.

Same newInstance method as before
(Note that Listing 4 uses the same version of the newInstance method that was used to create the one-dimensional array object in Listing l.)
Class object representing int

Note the syntax of the first parameter passed to the newInstance method in Listing 4.  As mentioned earlier, this is a reference to the predefined Class object that represents the primitive type int.  This causes the component type of the array object to be type int.

The setInt method

You should also note the use of the setInt method of the Array class to populate each of the two elements in the array in Listing 4 (with int values of 2 and 3 respectively).

The two-dimensional array object tree

Listing 5 uses the other overloaded version of the newInstance method to create a two-dimensional array object tree, having two rows and three columns.
 
    Object v3 = Array.newInstance(
       Class.forName(
       "java.lang.String"), (int[])v2);

Listing 5

A reference to the array object at the root of the tree is stored in the reference variable of type Object named v3.  Note that the tree is designed to store references to objects of type String.

(The number of dimensions and the size of each dimension are specified by the reference to the array object of type int passed as the second parameter.)
Square-bracket cast is required here

The required type of the second parameter for this version of the newInstance method is int[].  Therefore, there was no way for me to avoid the use of square brackets.  I could either store the reference to the array object as type Object and cast it before passing it to the method, (which I did), or save it originally as type int[], (which I didn't).  Either way, I would have to know about the type int[].

Populate the leaf elements

The nested for loop in Listing 6 uses the various methods of the Array class to populate the elements in the leaf array objects with references to objects of the class String.
 
    for(int i=0;i<
              Array.getLength(v3);i++){
      for(int j=0;j<
               Array.getLength(
                 Array.get(v3,i));j++){
        Array.set(Array.get(v3,i), j, 
                    "b" + (i+1)*(j+1));
      }//end inner loop
    }//end outer loop

Listing 6

Admittedly, the code in Listing 6 is a little complex.  However, there is really nothing new there, so I won't discuss it further.

Display the data

Similarly, the code in Listing 7 uses the methods of the Array class in a nested for loop to get and display the contents of the String objects whose references are stored in the elements of the leaf array objects.  Again, there is nothing new here, so I won't discuss this code further.
 
    for(int i=0;i<Array.getLength(v3);
                                  i++){
      for(int j=0;j<Array.getLength(
                 Array.get(v3,i));j++){
        System.out.print(Array.get(
            Array.get(v3,i), j) + " ");
      }//end inner loop
      System.out.println();
    }//end outer loop
    System.out.println(); 

Listing 7

Very few square brackets

I will point out that with the exception of the requirement to create and pass an array object as type int[], it was possible to write this entire example without the use of square brackets.  This further illustrates the fact that the Array class makes it possible to create and work with array objects in a method-oriented manner, almost devoid of the use of square-bracket notation.

Sorting and Searching

Many college professors require their students to spend large amounts of time reinventing algorithms for sorting and searching (and for various collections and data structures as well).  There was probably a time in history when that was an appropriate use of a student's time.  However, in my opinion, that time has passed.

Reuse, don't reinvent

Through a combination of the Arrays class, and the Java Collections Framework, most of the sorting, searching, data structures, and collection needs that you might have are readily available without a requirement for you to reinvent them.  (One of the most important concepts in OOP is reuse, don't reinvent.)

I will now illustrate sorting and searching using static methods of the Arrays class.

(Note that the Arrays class is different from the Array class discussed earlier.)
Create, populate, and display an array object

To give us something to work with, Listing 8 creates, populates, and displays the contents of an array object.  Note that the array object is populated with references to String objects.  There is nothing new here, so I won't discuss the code in Listing 8 in detail.
 
    Object v4 = Array.newInstance(
              Class.forName(
               "java.lang.String"), 8);
    //Populate the array object.
    // Create a gap in the data.
    for(int i = 0; i < 
             Array.getLength(v4); i++){
      if(i<4){Array.set(v4, i, 
                           "c"+(8-i));}
      else{Array.set(v4, i,
                          "c"+(18-i));}
    }//end for loop
    
    //Display the raw data
    for(int i = 0; i < 
             Array.getLength(v4); i++){
      System.out.print(Array.get(v4, i)
                                + " ");
    }//end for loop

Listing 8

The output

The code in Listing 8 produces the following output on the screen:

c8 c7 c6 c5 c14 c13 c12 c11

Note that the order of this data is generally descending, and there is no string encapsulating the characters c4.

Sort and display the data

The code in Listing 9 uses the sort method of the Arrays class to sort the array data into ascending order.
 
    Arrays.sort((Object[])v4);
    
    //Display the sorted data
    for(int i = 0; i < 
             Array.getLength(v4); i++){
      System.out.print(
               Array.get(v4, i) + " ");
    }//end for loop

Listing 9

The output

The code in Listing 9 displays the sorted contents of the array object, producing the following output on the computer screen:

c11 c12 c13 c14 c5 c6 c7 c8

Note that the order of the data in the array object has been modified, and the array data is now in ascending order.

(This order is based on the natural ordering of the String data.  I discuss other ways to order sorted data in conjunction with the Comparable and Comparator interfaces in my lessons on the Java Collections Framework.)
Binary search

A binary search is a search algorithm that can very quickly find an item stored in a sorted collection of items.  In this case, the collection of items is stored in an array object, and the data is sorted in ascending order.

Search for an existing string

Listing 10 uses the binarySearch method of the Arrays class to perform a search for an existing String object whose reference is stored in the sorted array.  The code searches for the reference to the String object encapsulating the characters c5.
 
    System.out.println(
      Arrays.binarySearch((Object[])v4,
                                "c5"));

Listing 10

The result of the search

The code in Listing 10 displays the numeral 4 on the screen.  When the binarySearch method finds a match, it returns the index value of the matching element.  If you go back and look at the sorted contents of the array shown earlier, you will see that this is the index of the element containing a reference to a String object that encapsulates the characters c5.

Search for a non-existing string

The code in Listing 11 uses the binarySearch method to search for a reference to a String object that encapsulates the characters c4.  As I indicated earlier, a String object that encapsulates these characters is not represented in the sorted array object.
 
    System.out.println(
      Arrays.binarySearch((Object[])v4,
                                "c4"));

Listing 11

The result of the search

The code in Listing 11 produces the following negative numeral on the screen:  -5.  Here is Sun's explanation for the value returned by the binarySearch method:

"Returns:  index of the search key, if it is contained in the list; otherwise, (-(insertion point) - 1). The insertion point is defined as the point at which the key would be inserted into the list: the index of the first element greater than the key, or list.size(), if all elements in the list are less than the specified key. Note that this guarantees that the return value will be >= 0 if and only if the key is found."
Thus, the negative return value indicates that the method didn't find a match.  The absolute value of the return value can be used to determine the index of the reference to the target object if it did exist in the sorted list.  I will leave it as an exercise for the student to interpret Sun's explanation beyond this simple explanation.

Other capabilities

In addition to sorting and searching, the Arrays class provides several other methods that can be used to manipulate the contents of array objects in Java.

Summary

This lesson is the third in a series of lessons that discuss various aspects of array objects in Java.

An array object has the following members (in addition to the data stored in the object):

Every array object implements the Cloneable and Serializable interfaces.

A clone of a multidimensional array is shallow.  Therefore, you should exercise caution when cloning array objects.

Because array objects implement the Serializable interface, they can be serialized and later reconstructed.

Every array also has an associated Class object.

The classes named Array and Arrays provide methods that you can use to work with array objects.

The Array class provides static methods to dynamically create and access Java array objects.

The Arrays class contains various methods for manipulating arrays (such as sorting and searching). It also contains a static factory method that allows arrays to be viewed as lists.

Class objects are required when using the methods of the Array class to dynamically create Java array objects.

There are nine predefined Class objects that represent the eight primitive types and void. They are accessed using the following syntax: boolean.class, int.class, etc.

Three ways to get a Class object are:

The methods of the Array class make it possible to deal with one-dimensional array objects using a syntax that is completely devoid of square brackets.  This is a method-oriented approach to the use of array objects.  This makes it possible to treat array objects much the same as we treat ordinary objects in Java. The required syntax for multi-dimensional array objects is mostly devoid of square brackets.

The Arrays class provides methods for sorting and searching array objects as well as performing other operations on array objects as well.

Through a combination of the Arrays class, and the Java Collections Framework, most of the sorting, searching, data structures, and collection needs that you might have are readily available without a requirement for you to reinvent them.

One of the most important concepts in OOP is reuse, don't reinvent.

What's Next?

The next lesson in this miniseries will explain the use of the this and super keywords.

Complete Program Listing

A complete listing of the program is shown in Listing 12 below.
 
/*File Array08.java
Copyright 2002, R.G.Baldwin
Rev 2/10/02

This program illustrates the use of 
static methods of the Array class to 
dynamically create and access Java
arrays.

It also illustrates the use of static
methods of the Arrays class to sort
and search array objects. 

Tested using JDK 1.3 under Win 2000.
**************************************/
import java.lang.reflect.Array;
import java.util.Arrays;

public class Array08{
  public static void main(
                        String[] args){
  
  try{
    //Create, populate, and display a
    // one-dimensional array object
    // whose elements contain 
    // references to objects of type 
    // String.
    
    //Create the array object
    Object v1 = Array.newInstance(
              Class.forName(
               "java.lang.String"), 3);
    //Populate the array object
    for(int i = 0; i < 
             Array.getLength(v1); i++){
      Array.set(v1, i, "a"+i);
    }//end for loop
    //Display the data
    for(int i = 0; i < 
             Array.getLength(v1); i++){
      System.out.print(
               Array.get(v1, i) + " ");
    }//end for loop
    System.out.println();
    System.out.println();
    
    //Create, populate, and display a
    // rectangular two-dimensional
    // array object tree whose 
    // elements contain references
    // to objects of type String.
    
    //First create an array object of
    // type int required as a 
    // parameter to the newInstance
    // method.  Populate it to later
    // specify a rectangular array
    // object tree with two rows and
    // three columns.
    Object v2 = Array.newInstance(
                         int.class, 2);
    Array.setInt(v2, 0, 2);
    Array.setInt(v2, 1, 3);
    //Now create the actual two-
    // dimensional array object tree.
    Object v3 = Array.newInstance(
       Class.forName(
       "java.lang.String"), (int[])v2);

    //Populate the leaf elements with
    // references to objects of type
    // String.
    for(int i=0;i<

              Array.getLength(v3);i++){
      for(int j=0;j<
               Array.getLength(
                 Array.get(v3,i));j++){
        Array.set(Array.get(v3,i), j, 
                    "b" + (i+1)*(j+1));
      }//end inner loop
    }//end outer loop
    
    //Display the data encapsulated
    // in the String objects.
    for(int i=0;i<Array.getLength(v3);
                                  i++){
      for(int j=0;j<Array.getLength(
                 Array.get(v3,i));j++){
        System.out.print(Array.get(
            Array.get(v3,i), j) + " ");
      }//end inner loop
      System.out.println();
    }//end outer loop
    System.out.println(); 
    
    //Now illustrate sorting and
    // searching using methods of
    // the arrays class.   

    //Create the array object
    Object v4 = Array.newInstance(
              Class.forName(
               "java.lang.String"), 8);
    //Populate the array object.
    // Create a gap in the data.
    for(int i = 0; i < 
             Array.getLength(v4); i++){
      if(i<4){Array.set(v4, i, 
                           "c"+(8-i));}
      else{Array.set(v4, i,
                          "c"+(18-i));}
    }//end for loop
    
    //Display the raw data
    for(int i = 0; i < 
             Array.getLength(v4); i++){
      System.out.print(Array.get(v4, i)
                                + " ");
    }//end for loop
    System.out.println();
    
    //Sort array data into
    // ascending order.
    Arrays.sort((Object[])v4);
    
    //Display the sorted data
    for(int i = 0; i < 
             Array.getLength(v4); i++){
      System.out.print(
               Array.get(v4, i) + " ");
    }//end for loop
    System.out.println();
    
    //Search for an existing String
    System.out.println(
      Arrays.binarySearch((Object[])v4,
                                "c5"));
    //Search for a non-existing String
    System.out.println(
      Arrays.binarySearch((Object[])v4,
                                "c4"));
       
  }catch(ClassNotFoundException e){
                System.out.println(e);}
  }//end main
}//end class Array08
//===================================//

Listing 12


Copyright 2002, Richard G. Baldwin.  Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java and XML. In addition to the many platform-independent benefits of Java applications, he believes that a combination of Java and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects involving Java, XML, or a combination of the two.  He frequently provides onsite Java and/or XML training at the high-tech companies located in and around Austin, Texas.  He is the author of Baldwin's Java Programming Tutorials, which has gained a worldwide following among experienced and aspiring Java programmers. He has also published articles on Java Programming in Java Pro magazine.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

baldwin.richard@iname.com

-end-