Table 6.1 summarizes the types found in Java. Only primitive data and reference values can be stored in variables. Only class and array types can be instantiated to create objects.
Primitive data types
Primitive data values
Class, interface, and array types (reference types)
Arrays are objects in Java. Array types (boolean, Object, StackImpl) implicitly augment the inheritance hierarchy. The inheritance hierarchy depicted in Figure 6.3 can be augmented by the corresponding array types. The resulting type hierarchy is shown in Figure 6.4. An array type is shown as a "class" with the  notation appended to the name of the element type. The class SafeStackImpl is a subclass of the class StackImpl. The corresponding array types, SafeStackImpl and StackImpl, are shown as subtype and supertype, respectively, in the type hierarchy. Figure 6.4 also shows array types corresponding to some of the primitive data types.
From the type hierarchy in Figure 6.4, we can summarize the following:
All reference types are subtypes of Object type. This applies to classes, interfaces, and array types, as these comprise all reference types.
All arrays of reference types are also subtypes of the array type Object, but arrays of primitive data types are not. Note that the array type Object is also a subtype of Object type.
If a reference type is a subtype of another reference type, then the corresponding array types also have an analogous subtype-supertype relationship.
There is no subtype-supertype relationship between a type and its corresponding array type.
We can create an array of an interface type, but we cannot instantiate an interface (as is the case with abstract classes). In the declaration statement below, the reference iSafeStackArray has type ISafeStack, (i.e., array of interface type ISafeStack). The array creation expression creates an array whose element type is ISafeStack. The array object can accommodate five references of type ISafeStack. The following statement does not initialize these references to denote any objects:
ISafeStack iSafeStackArray = new ISafeStack;
An array reference exhibits polymorphic behavior like any other reference, subject to its location in the type hierarchy (see Section 6.7, p. 272). However, a runtime check can be necessary when objects are inserted in an array, as the following example illustrates.
The following assignment is valid, as a supertype reference (StackImpl) can denote objects of its subtype (SafeStackImpl):
StackImpl stackImplArray = new SafeStackImpl; // (1)
Since StackImpl is a supertype of SafeStackImpl, the following assignment is also valid:
stackImplArray = new SafeStackImpl(10); // (2)
The assignment at (2) inserts a SafeStackImpl object in the SafeStackImpl object (i.e., array of SafeStackImpl) created at (1).
Since the type of stackImplArray[i], (0 i < 2), is StackImpl, it should be possible to do the following assignment as well:
stackImplArray = new StackImpl(20); // (3) ArrayStoreException
At compile time there are no problems, as the compiler cannot deduce that the array variable stackImplArray will actually denote an SafeStackImpl object at runtime. However, the assignment at (3) causes an ArrayStoreException to be thrown at runtime, as a SafeStackImpl object cannot possibly contain objects of type StackImpl.