Chapter 51
Java versus C(++)

by Scott Williams

The Java and C++ programming languages both share the C language as a common ancestor, and thus the three languages share many features and syntactic constructs. But it will already be clear to any C or C++ programmers reading this book that there are also many differences. The languages are similar, but certainly not identical. C programmers will find that Java is mostly additive, that it forms a superset of C. While it is true that some loved (and hated) features of C are missing in Java--most notably the pre-processor and support for pointers--there are far more areas where Java introduces new constructs and new features. Most obvious in the latter category is Java's support for the object-oriented programming paradigm.

C++ programmers will find the differences between C++ and Java more subtle. Some are fairly minor, like the scope of a variable declared within a for-loop control statement. Others, unfortunately, are quite profound, like the Java object reference model and its support for polymorphic behavior.

This chapter contains a concise comparison of C, C++, and Java. If you have prior experience with C or C++, you can use this chapter to accelerate your progress with Java. If you don't have prior experience, you can use this chapter to gain some insights into how Java evolved. And no matter what your background, you can use this chapter as a quick reference.

Basic Java Syntax

There are many small differences between C/C++ and Java. None of these is particularly difficult to understand, but there are quite a few of them and they can be difficult to remember. This first section will cover the most basic differences.

Lexical Structure

The lexical structures of Java and of C and C++ are essentially the same. The source code for the program is broken down into tokens that combine to form expressions and statements. All three languages are freeform languages, which means that there are no special rules for the positioning of tokens in the source stream. White space, consisting of blanks, tabs, and newlines, can be used in any combination to separate tokens. As in C and C++, semicolons are used to delimit statements. Anywhere a single statement is required, multiple statements may be combined within braces to form a single compound statement.


CAUTION:
Although white space can be used to separate tokens, it cannot be used to separate the components of a token. For example, white space cannot be placed between the two characters of an operator such as ++ or *=.


Comments

There are three forms of comments in Java:

For more information on Javadoc, see "JDK Tools: Javac, AppletViewer, Javadoc," Chapter 4

The following code fragment illustrates the three styles:

/* This is the first line of a multi-line comment
this is the second line of the comment */
int tableSize;  // This is a single line comment
/** This is a Javadoc comment */



NOTE: As in C and C++, Java comments do not nest. Take a look at the following example:
/* This is a line of a comment
/* Someone might think this is a nested comment */
But this line is no longer part of a comment
*/



CAUTION:
Many C and C++ programmers are used to being able to nest comments, despite the note above. This is because some C and C++ compiler vendors have added support for nested comments to their products. Don't be fooled into thinking that these extensions are actually part of the C or C++ language--they aren't!

Whats Missing

A number of constructs from C and C++ are not present and not supported in Java. Some of these, like the lack of pre-processor, get special attention later in this chapter. Others are more minor and are collected together here. In most situations you can achieve similar results using alternate Java syntax.

No Pointers
Pointers are not supported in Java in any way, although the Java reference model is very similar to the use of pointers in C++. There are no pointer operators, and there is no way of directly manipulating memory locations by address. Furthermore, memory cannot be allocated and de-allocated at programmer convenience as is the case in both C and C++.

No C++ References In C++, reference variables can be defined to refer to any native or abstract data type. In Java, all objects of class type are manipulated via references, so no special syntax is necessary. However, there is no way in Java to declare a reference to a native type. Instead, one of the Java "wrapper" classes defined in java.lang can be used (e.g., Integer, Float, and so on).

See
"Primitive Types and java.lang Wrapper Classes," later in this chapter.

No Structs, Unions, or Enums The C and C++ struct construct no longer exists. A C struct can be mimicked in Java by defining a class with all data elements public and with no methods. The enum construct isn't supported either, but can be mimicked by defining a class with no methods, and with all data elements public, static, and final. C and C++ unions are not supported and there is no trivial way to provide similar functionality.

No consts The const keyword, though reserved in Java also, isn't currently used for anything. In C++, use of the const modifier was encouraged in order to eliminate the need for #define constants. In Java, the final modifier can be used to achieve a similar effect: Data elements in a class that are declared to be final must be initialized when they are declared, and thereafter cannot be modified.

The Runtime Library

C programmers are accustomed to using the C runtime library in every program they write. The very first C program a programmer creates is traditionally the "Hello world" program, in which the following line appears:

printf ("Hello, world!\n");

The printf function is defined in the C runtime library, and is available to be used in any C program.

In C++, the equivalent functionality is usually provided by classes and methods in the iostream class library. The traditional first program for a C++ programmer might contain a line like the following:

cout << "Hello again, world!\n";

The functionality provided by the iostream class library is again available to any C++ program.

In Java, the functionality you have come to expect in the C runtime library, or in the standard C++ class libraries, is provided in the Java API. The API defines a large number of classes, each with many different methods. The traditional first program might now contain the line:

System.out.println ("Hello from Java!");

The println method is defined in the PrintStream class. System.out is an instance of the PrintStream class. The important thing is that the functionality provided by the Java API, which includes everything that the old C runtime library does but much more besides, is available to every Java program you write.

The Structure of Java Programs

When you first start reading Java code, two kinds of things will strike you right away. First will be the minor syntax differences like the ones listed in the previous section. Second will be the overall structure of the Java program.

The Big Picture

In C, the big picture of a program is that it is a collection of procedures or functions that interact with one another. Processing begins with the function called main, and from there all the functions call one another. In Java, the big picture is of a collection of class definitions, the instances of which are interrelated and interact. In some respects this is the essence of object-oriented programming.

Java programs themselves are classes, and come in two main flavors: Applications are classes that have a main method, and applets are classes that extend the Applet class.


NOTE: The definitions of application and applet are not mutually exclusive. By creating a class that both extends the Applet class and has a main method, it is possible to write one program that can be used as either application or applet at the user's choice.


In this context, as in many others, C++ is "halfway" between C and Java. C programs are collections of functions, Java programs are collections of classes, and C++ programs are collections of both functions and classes.

Methods: Yes, Functions: No

As pointed out, C programs are just collections of functions. In C++, most programs are collections of classes, similar to programs in Java. However a C++ program begins execution with the function called main, just as C programs do. Moreover, C++ provides explicit support for functions that behave like C functions, in addition to methods that are part of class definitions. Java is much more pure in its object orientation than C++, however, and, as such, provides no support for functions except as methods defined within a class.


TIP: If you must create functionality similar to that provided by C and C++ functions (a math library, for instance) you can do so by creating a utility class that consists only of static methods and static data items.


No Pre-Processor

When many C and C++ programmers learn that the pre-processor no longer exists in Java, they are quite skeptical. The truth is that the pre-processor isn't supported in Java because the language no longer has need of it.

Constant Values
There are no #define constants in Java. As previously seen, data elements in a class definition can be declared to be final, however, in which case their initial values can never be changed. To get as close as possible to the functionality of a #define, you would create those data elements to be public, static, and final.


NOTE:
The Java equivalent to the C #define is far superior. Its name is protected by the compiler, it has a true data type, and its use can be verified for correctness by the compiler.


Macros C macros, also implemented with the #define construct of the pre-processor, were designed to provide a function-like construct that could be implemented at compile time, thus avoiding the runtime overhead associated with a function invocation. Java does not provide an equivalent to the C macro. Remember, however, that C macros are extremely error-prone, and are a fairly consistent source of program bugs. C++ programmers are encouraged to forego the #define macro in favor of the C++ inline function. Java does not explicitly support the inline keyword, but Java compilers are free to inline any function they choose to as part of their general optimization process.

Source File Inclusion Java does not provide anything equivalent to the C #include directive for source file inclusion. In practice, however, there is little need for this in Java code. The two most common uses of the #include in C are for the creation of #define constants and for the declaration of function prototypes. As just seen, Java supports constant values through quite different syntax which doesn't require source file inclusion. And since Java doesn't support a method declaration separate from its definition, there is no need for prototypes.


NOTE: C compilers only support very limited forms of forward reference, meaning that symbols--function names in this case--must be declared before they are used. In practice, this means that C programmers must either provide a function prototype--a declaration--or must ensure that the function definition comes before the first time the function is used. Java, on the other hand, supports very liberal forward references, thus eliminating the need for separate function declarations.


The following code fragments illustrate the different pre-processor issues just discussed. The first fragment is C++:

#include <iosteam.h>
#define PLAYERS 2
   
class Game
       {
public:
Game()
{ cout << "Constructing the Game\n"; }
       };

Now look at the Java equivalent:

import java.lang.*;
class Game{
public Game()
{ System.out.println ("Constructing the Game"); }
public static final int PLAYERS=23;

     }

Notice how the import statement appears to take the place of the #include. The two statements are superficially similar, but in fact accomplish quite different objectives. The #include does a straight source file inclusion on the part of the C pre-processor. Since the pre-processor doesn't understand C syntax (it's basically a batch-mode text editor), the source file being included could contain anything. The import statement doesn't actually include anything, but rather tells the compiler where to look for classes that you want to make use of.


TIP: The example above imports the java.lang package. In fact, that is the one package that the Java compiler will import automatically, with or without an import statement. You can make use of classes in java.lang without explicitly importing them yourself.


Source File Names

At the moment, most development kits for Java, including the Sun JDK, require that the definition of a public class be contained in a source file of the same name. Thus, the definition of the InventoryItem class would have to be contained in a source file named InventoryItem.java.

Although the current naming restrictions apply only to the definition of public classes, it is common practice in the Java community to define one class per source file, regardless of whether the class is public or not. This practice will be a little unfamiliar to many C and C++ programmers, who might be amazed at the number of source files used in the creation of a single Java program. However, it does make Java source code much easier to find--both for you and for the compiler--and lends some consistency to file naming.

Java Data Types

Java support for data types is stronger and more specific than that in C or C++. In this section you'll look at integral data types, Unicode characters, Booleans, floating point types, and aggregate types. You'll also take a look at converting from one Java data type to another.

Integral Data Types

It is in the integral data types that you see most of the differences between Java and C/C++. Much of what was platform-dependent in C or C++ is now clearly defined in Java. Table 51.1 summarizes the differences.
Table 51.1 Comparison of Java and C/C++ Data Types
Data Type C/C++ Java
byte Didn't exist; usually used char instead 8 bits
char 8 bits; one ASCII or 16 bits
EBCDIC character one Unicode character
short At least 16 bits 16 bits
int Usually 16 or 32 bits 32 bits
long At least 32 bits 64 bits


NOTE: There are no unsigned integral data types in Java.


Unicode Characters

In Java, the char and String data types are not single-byte ASCII or EBCDIC values, but are instead 16-bit values taken from the Unicode character set. The larger data type means that a larger number of possible values are supported, giving greater flexibility to programmers in non-English or multilingual environments. In the Unicode set, the first 256 characters are identical to the ASCII characters 0x00-0xFF (which is also ISO standard ISO8859-1).

Specific values from the Unicode character set can be represented in Java using the syntax \uhhhh, where h is a hexadecimal character representing four of the 16 bits in the unicode character. In fact, you don't need to specify all four hex digits; if fewer than four digits are specified, then the high-order bits are set to zero.


NOTE: Java also supports C and C++ escape sequences such as \n, \r, and \t to represent non-printing control characters.


The boolean Data Type

Neither C nor C++ provides a native boolean data type. In both those languages, most programmers use int variables to hold Boolean values, with the convention that a value of 0 represents false and a value of 1 represents true. Furthermore, the logical and relational operators in C and C++ all produce integer values, and the control statements on loops or an if statement both take integer values as control expressions.

In Java, the native boolean data type is used in all these situations. The boolean data type supports only two possible values, true and false. The relational and logical operators all produce values of boolean type, and the logical operators will only accept operands that are of boolean type. The control expression in Java loops and the Java if statement may only have boolean type.


CAUTION:
The words true and false are values of the boolean type, just as 1, 2, or 3 are values of an integer type. Even though they are not defined as keywords in the Java language grammar, they are nonetheless reserved, so you can't use them as variable or class names.


Floating Point Types

Java supports float and double data types, just as C and C++ do. However, in those languages the behavior of those data types is platform-dependent. In Java, all floating point behavior--including the limits that float and double variables can take--is defined by the IEEE 754 standard for floating point arithmetic.


NOTE: Most modern C and C++ compilers also provide support that is consistent with IEEE 754. For most programmers, therefore, Java formalizes behavior that they are already familiar with.


Aggregate Data Types

Aggregate data types, including strings and arrays, are implemented somewhat differently in Java. Most notably, String is a native data type in Java, and the + operator can be used to concatenate strings. Also, arrays and strings both have known lengths that can be reliably depended on at runtime. As in C and C++, arrays must be statically sized. Dynamically sized aggregates in Java are supported by way of the Vector class. (See the section on aggregates later in this chapter for more information.)

Type Conversion and Casting

C and C++ both support ad hoc type conversions all over the place: in expression evaluation, assignments, and function invocations, to name a few places. Java also supports ad hoc conversions, but only under very controlled circumstances. In particular, if a conversion is a narrowing conversion (one that could result in a loss of precision) then the conversion will only be allowed with an explicit cast. Consider the following example:

byte b=126;
int i=17;
float f=3.51;
     b = (byte) (b+i);   // cast to byte required
     i = (int) f;          // cast to int required
     f = i;               // no cast required...
// this is not a narrowing conversion

Objects and Classes

Java and C++ both support the object-oriented programming paradigm, and hence they both have support for objects and classes, which C does not. When you are comparing Java and C, all the syntax that supports object orientation, including support for objects and classes, for inheritance, and for interfaces, will all be new. If you are a C programmer unfamiliar with object-orientation, then it would be best for you to refer back to the material in Chapter 5, "Object-Oriented Programs," and Chapter 11, "Classes."

Even though both C++ and Java support object-orientation, and hence both include support for objects and classes, there are a number of subtle and important differences between the two languages in these areas. This section is a collection of the major differences.

Declaring Reference Types

In addition to the primitive data types (char, int, and so on) are objects, arrays, vectors, and strings. All these are manipulated by reference rather than by value, and are referred to as reference types. Consider the following declaration:

GameTable chessBoard;

If GameTable is a class you have previously defined somewhere, then the above declaration would be syntactically correct in both C++ and Java. However, in C++ this would have created a new instance of the GameTable class. In Java, the above declaration is only a declaration of the variable chessBoard to be of reference type; it is a reference to a GameTable, but it doesn't yet refer to any particular instance of that class. In Java you must also instantiate the object, using the new operator:

GameTable chessBoard;
chessBoard = new GameTable ();


In Java, as in C and C++, the declaration of a variable and its initialization can be combined into one statement:

GameTable chessBoard = new GameTable ();




The above line both declares the variable chessBoard to be a reference to a GameTable, and also instantiates a new GameTable that chessBoard now refers to.


Manipulating References

When manipulating objects in Java, it is again important to remember that you are manipulating references, not instances. In the following code fragment, variables b1 and b2 are distinct variables, and therefore can hold distinct references, but following the assignment they both refer to the same instance:

Box b1, b2;        // 2 references to type Box
b1=new Box(2,2);   // instantiate b1
b2=b1;             // b1 and b2 now both refer to the
                        // same instance

The object/reference relationship in Java is also significant when you are comparing references. Take a look at the following code fragment:

Box b1, b2;        // 2 references to type Box
b1=new Box(2,2);   // instantiate b1
b2=new Box(2,2);   // instantiate b2 with same values
if (b1==b2)        // This expression compares false

In the final line of the example, the references b1 and b2 are being compared. By default, this will only compare true if the two references are actually referring to the same instance. Since b1 and b2 are referring to two different instances (albeit with the same values) the comparison has to yield false.


TIP: All classes in Java inherit from the Object base class, which defines an isEquals method with the above mentioned behavior. If you want comparisons such as the one you've just seen to behave differently, just override isEquals in your own class to produce true or false according to your own criteria.


Method Invocation: Call-by-Value and Call-by-Reference

Like C and C++, the default behavior of method invocations in Java is call-by-value. Remember, though, that objects are always manipulated by reference. This means that when you call a method and attempt to pass an object as an argument, you are really passing a reference to that object: The "value" being passed is a reference to an object. In effect, then, passing native types as arguments to a Java method, it is call-by-value; when passing reference types, it is call-by-reference.


TIP: If you need to call a method and pass a reference "by value," you can achieve the same effect by taking a local copy of the object first, as in the following code:

Box b1 = new Box (2,2);
valueMethod ( new Box (b1) );




In this case the reference being passed to valueMethod is a new instance that is being initialized with b1. Any changes made to the reference within the method will not affect b1.


Primitive Types and java.lang Wrapper Classes

In the java.lang package, several classes are defined which "wrap" a primitive data type in a reference object. For example, an int value can be "wrapped" in an Integer object. One of these wrapper classes exists for each of the primitive data types. There are a number of interesting ways in which these wrapper classes can be used to make your life easier.

Passing Primitive Types by Reference
As previously noted, there is no explicit reference syntax in Java analogous to the & reference syntax in C++. For example, in C++ a method can be defined to take a reference to an argument being passed, rather than taking the argument's value. In Java, to pass a primitive data type by reference, you must first "wrap" the value in an object as follows:

int j = 17;
Integer arg = new Integer (j);
methodCalledByReference ( arg );


CAUTION
Note that the wrapper classes supplied in java.lang do not provide methods for setting or updating the values of primitive type that they contain, which limits their usefulness in situations such as that outlined above. If you find yourself trying this too often, you might be "thinking C++" instead of "thinking Java."


Adding Primitive Types to a Vector The Java Vector class allows you to create a dynamically sized aggregate of reference types. However, since the elements in the vector must be references, you can't add values of the primitive types to a vector. Instead, wrap the primitive value in a wrapper object and add that new object to the vector. Here's an example:

Vector sizes = new Vector ();
Integer small= new Integer (5);
sizes.addElement (small);

The int value 5 has been wrapped in the small object, which has then been added to the sizes vector.


NOTE: Since Java hashtables also are designed to store object references, the above comments apply to hashtables as well as to vectors.


Converting Primitive Types to Strings The java.lang wrapper classes each define a method called toString that converts the primitive value to a Java String. This can be very useful when you need a String but only have a value of a primitive data type. In the following example, you need a String version of a floating point value, so that you can pass it as an argument to the drawString method from within an Applet's paint method:

Float temp = new Float (this.interestRate);
g.drawString ( temp.toString(), 10, 10 );

As can be seen, you first wrap the interestRate value in a Float object. Then you invoke the Float object's toString method to create a valid Java String, which you then pass to drawString.

The Object Life Cycle

Like any other data element or data structure, Java objects have a definite life cycle: They are created, they have a useful life, and then they are destroyed. This is also true of objects created as part of a C++ program. There are differences, however, in how the two languages manage the life cycle of an object.

When Objects Are Created
In C++, an object is created in one of two ways. If an object is being stored in a variable, then the object is instantiated-- created--when the variable comes into scope. It is destroyed when the object goes out of scope. If a C++ object is being manipulated only via a pointer, however, it is only instantiated when the new operator is used. Life in Java is a little simpler than this. There is only one way to create and use Java objects, and that is by reference. Hence, an object doesn't exist until it is created with the new operator:

Table r;           // Table object doesn't yet exist
r = new Table ();  // Only now does the Table object exist

This is very similar to the C++ syntax that would be used if the object were to be manipulated via a pointer:

Table *r;          // C++ code to create pointer
r = new Table ();  // C++ code to instantiate Table object

As in C++, it is only when the object is actually instantiated that a constructor method is invoked.


NOTE: Both Java and C++ constructor methods can be overloaded. That is, there can be more than one of them as long as their signatures (the number and types of the arguments) are different.


When Objects Are Destroyed How a C++ object is destroyed depends on how it was created. If an object is being stored in a variable, then the object is destroyed when the variable goes out of scope. If a C++ object was created using the new operator, then it is only destroyed when the programmer explicitly requests it with the delete operator. Once again, things in Java are a little different. An object is created when it is instantiated with the new operator. An object is destroyed, at least in principle, when there are no longer any references to it. Consider the following little code fragment:

Square s1 = new Square (5);
Square s2 = new Square (10);
s2=s1;

As soon as you perform the final assignment, both s1 and s2 refer to the same object. There are no longer any references to the Square object that was created as Square(10). It is at that point that the object created as Square(10) will, in principle, be destroyed.

Why do I keep saying "in principle?" That's because Java uses a form of dynamic memory management known as garbage collection. This means that memory deallocation is done automatically by the Java garbage collector, rather than under the control of the programmer. Since the garbage collector usually runs in a separate thread, it would be more accurate to say that an object "is eligible for garbage collection" rather than to say that it is "destroyed." The distinction is really only academic, however; whether it is destroyed or just made ready for garbage collection, the reality is that the object can no longer be used in the program. For all practical purposes, that object has "ceased to exist."


NOTE: Although Java supports constructors, it doesn't support explicit destructors, as in C++. There is a finalize method, however, which you can override in your class definition, and which will be invoked when the object is garbage collected. Since the timing of garbage collection is not predictable, however, the finalize method is not as commonly used as a destructor is in C++.


Java References versus C++ Pointers

It has probably become clear to you in this section that much of the Java syntax for manipulating objects is very similar to that used in C++--right down to the use of the new operator to instantiate an object. But when using the new operator in C++ you are given an address of an object, which you can then assign to a pointer variable. In Java, the new operator returns an object reference to you that you can then assign to a reference variable.

Some people, especially those new to Java, feel that the use of references in Java is really the same as the use of pointers in C++--that it is the same construct with a different name. There is some truth to this, and if you are familiar with C++ syntax you will likely find the Java syntax quite easy to pick up. But there is an important difference. In Java all the memory management is done for you automatically, and you can never manipulate a memory address directly. Whether the Java interpreter uses pointers underneath it all to implement references isn't really important. What's important is that Java programs will, in this regard, be more stable and reliable than their C and C++ counterparts.


NOTE: C++ programmers will be familiar with this, which is a pointer variable containing the address of the object for which a method was invoked. In Java, you have this as well, only now it is a reference, instead of a pointer. Other than that, its meaning is the same.


Aggregates: Strings, Arrays, and Vectors

Like C and C++, Java provides a number of mechanisms for creating aggregates of values, whether those values be of primitive type or of a reference type. In this section you will look at the three most common aggregates in Java: strings, arrays, and vectors.

Strings

In Java, strings are handled much as they are in C++. The most significant exception is that once a String is declared, its contents may not be changed. To actually modify a String, you must create a StringBuffer object that can be initialized with a String and then modified. You can then create a new String with the contents of the StringBuffer. The following code fragment illustrates such a process:

//create the initial String object
String badString = new String("This is a String");
// create the StringBuffer that we can modify
StringBuffer correction = new StringBuffer(badString);
// make the modification to the StringBuffer object
correction.insert(12,=i=);
// create a new String object with the corrected contents
String goodString = new String(correction);


TIP: Java Strings, like many other Java objects, have known and dependable sizes. For a String object, the length method returns the length of the String as an int. It is not possible for Strings to "overflow" as it is in C and C++.


Arrays

Java arrays are very similar to C and C++ arrays: They are homogenous aggregates (that is, each element is of the same data type) and they have a fixed size. However, there are also some subtle differences.

Arrays of Primitive Types In Java, arrays are instances of a "hidden" array class-- hidden in the sense that there is no Array keyword to denote the class name. Nonetheless, arrays must be instantiated just as other objects must. The following example declares myArray to be an array of ints:

int[] myArray;


NOTE: In Java, the empty square brackets that indicate myArray are an array reference that can be placed just after the data type (as above), or can be placed after the variable name:

int myArray[];

Placing them immediately after the type name is the preferred Java style.


At this point, however, myArray is a reference variable that doesn't refer to anything. You must still instantiate the array object:

myArray = new int[10];


TIP: The size of a Java array is fixed at compile time, just as it is in C and C++. If you need to create a dynamically sized array, use a vector instead. As in C++, the two steps outlined above can be combined onto one statement, as follows:


int[] myArray = new int[10];

C and C++ programmers are used to being able to initialize arrays at the time they are declared, using syntax similar to the following:

// declaring and initializing a C array
int powers[3] = {3,9,27};

In such situations, the size of the array can be omitted; the compiler will determine the size of the array based on the number of initial values supplied.

The same syntax can be used in Java. The following example accomplishes three distinct tasks: it declares powers to be an array of ints, instantiates the array, and initializes each element in the array. The size of the array is taken from the number of initializers:

int[] powers = {3,9,27};

The empty square brackets are also used to denote an array when the array is being passed to a method as an argument. The following example shows an array of ints and an array of chars being accepted as arguments in a method definition:

public syntaxExample(int[]  thisInt,
char[] thisChar)
       {
// method body goes here
       }

Arrays of ReferencesAs in C and C++, it is also possible to have Java arrays of non-primitive types. In Java, such arrays are of reference types; that is, such an array will be an array of references. The basic syntax still holds. The following example creates an array of 31 Month references:

month[] year = new Month[12];

Once the line above has been executed, an array of 12 Month references will have been instantiated, and year will be a reference variable that refers to that array. But none of the 12 elements in the array yet refer to anything. You have instantiated the array, but you have not yet instantiated any of the 12 Months. If the Month class has a constructor that takes a String as an argument, you might go ahead and instantiate the elements of the array as follows:

year[0] = new Month ("January");
year[1] = new Month ("February");
year[2] = new Month ("March");
// ... and so on


NOTE: Java arrays, like their C and C++ counterparts, use only zero-origin subscripting. Thus an array of 12 elements will have valid subscripts from 0 to 11 inclusive.


TIP: Java arrays, like Java Strings, have fixed and dependable sizes. The Each array object has a length variable associated with it that contains the correct length of the array. In the above example, year.length would be 12.


Vectors

In C and C++, dynamic memory allocation under the control of the programmer is a time- honored tradition--unfortunately a tradition that has produced some pretty unstable code over the years. In Java such dynamic memory allocation is not directly possible. However, there are many situations in which a data structure of dynamic size is critical to the solution of the problem. In such situations, Java programmers can turn to the Vector class.

Java Vectors are like dynamically sized arrays. They consist of a dynamic number of references to Objects. References can be added to and removed from the Vector at will, using methods such as addElement and removeElement. Since all Java classes inherit from the Object class, it follows that the elements in a Java Vector can be references to any Java class.


NOTE: When a reference is retrieved from a Vector it will be of type Object. It must therefore be cast to be of the appropriate reference type before it can be used reliably.

NOTE: Each element in a Vector is numbered, with element numbers beginning at 0. This is consistent with the subscripting of Java arrays and with the use of arrays in C and C++.


TIP: Java Vectors, like Java arrays and strings, have dependable sizes. The size method in the Vector class returns the number of elements currently stored in the Vector.


The following code fragment shows a Vector of Months, very similar to the array of Months we had a few pages ago.

MonthVector = new Vector();
MonthVector.addElement (new Month ("January");
MonthVector.addElement (new Month ("February");
MonthVector.addElement (new Month ("March");
// and so on...

For more information on Vectors, take a look at Chapter 33, which covers the java.util package.

Class Hierarchies and Inheritance

Most Java programs, like most C++ programs, make extensive use of inheritance, in which one class is defined in terms of another. The new class, also called the derived class or the subclass, is said to inherit all the characteristics of the original class-- also called the base class or the superclass. When a base class itself inherits characteristics from another class, the result is said to be a class hierarchy. (For more on inheritance, see Chapter 5 on object-oriented programming.)


NOTE: In a class hierarchy, one class may be both a subclass of some class, and a superclass of another. This should not strike you as being strange: In real life, someone's parent is also someone else's child.

NOTE: In Java, all classes are subclasses of the Object base class and inherit all its characteristics.


The Syntax of Inheritance

The syntax for creating derived classes in Java is different from C++. When deriving a subclass from a superclass, Java uses the extends keyword in the new class's definition. The following example contrasts the syntax for both C++ and Java. In both cases, you are creating a class called CaptionedRectangle, which inherits from the Rectangle base class.

First, the C++ version:

class CaptionedRectangle : public Rectangle
       {
// definition of class here
       }

Now here's the Java equivalent:

class CaptionedRectangle extends Rectangle
       {
// definition of class here
       }

The instanceof Operator

The instanceof operator is a real bit of convenience. This operator takes two operands: a reference on the left and the name of a class on the right. The operator returns true if the object referred to by the reference is an instance of the class on the right, or of any of its subclasses. In the following example, you only want to invoke the checkInsurance method if obj refers to an object of the Vehicle class:

void quarterlyUpdates (Object obj)
       {
if ( obj instanceof Vehicle )
         {
obj.checkInsurance ();
         }
       }


NOTE: Since all classes are derived from Object, the expression (obj instanceof Object) will always return true.


Inheritance and Polymorphism

In Java, a variable declared to be of some class type can actually be used to refer to any object of that class or of any of its subclasses. Given the classes Rectangle and CaptionedRectangle above, you could have the following:

void updateScreen ( Rectangle r )
       {
r.display ();
       }

If r happens to refer to a Rectangle object, then the display method from the Rectangle class will be invoked (if there is one). If r refers to a CaptionedRectangle, then the display method of the CaptionedRectangle class will be invoked. This is the essence of polymorphism, a concept in object-oriented programming in which a single interface (in this case the display method) can actually have multiple implementations.


NOTE: C++ programmers will be familiar with this behavior. However, for a C++ class hierarchy to exhibit this behavior, the display method in the base class must be declared to be virtual. Java methods are all "virtual" in the C++ sense.


Interfaces versus Multiple Inheritance

In C++, one class may inherit the characteristics of multiple base classes--a concept known as multiple inheritance. A Java class, by contrast, may only inherit the characteristics of one base class. A Java class, however, may be said to implement a Java interface. For example, you might have the following:

class CaptionedRectangle
extends Rectangle
implements Displayable
       {
// class definition goes here
       }

A Java interface is similar to a class, but its data items are all static and final, and its methods are all abstract. Within the definition of the new class, each of the methods in the interface must be given actual definitions. The interface is really a form of guarantee: If a class is defined to implement an interface, it is a guarantee to the user of the class that each of the methods in the interface will be fully defined in the class. Interfaces are covered in depth in Chapter 12.


NOTE: The multiple inheritance mechanism in C++ has a number of subtle problem areas. As a result, the most reliable and common use of multiple inheritance in C++ programs is when there is one "primary" chain of inheritance, and any other classes used as base classes are collections of utility methods and constant values. It is this behavior that is formalized in the Java interface construct.


The super Reference

Just as you have a this reference, which refers to the object for which a method has been invoked, so too you have a super reference which refers to the this object's parent class. It can be used in situations when a method in the derived class needs to explicitly invoke a method from the superclass. Consider the following example of a display method defined within the captionedRectangle class:

void display ()
       {
System.out.println (this.caption);
super.display ();
       }

The super reference can also be used on the first line of a base class constructor method to explicitly invoke a superclass constructor. Once again, it is instructive to compare the C++ and Java equivalents here. The following is one possibility in C++ of a CaptionedRectangle constructor that takes five arguments: four that define the Rectangle, and one that defines the caption of the CaptionedRectangle:

// C++ constructor
CaptionedRectangle::CaptionedRectangle
( int x, int y, int width, int height, String caption ):
Rectangle ( x,y,width,height )
       {
// body of CaptionedRectangle constructor here
       }

The Java equivalent might be the following:

// Java constructor
CaptionedRectangle
( int x, int y, int width, int height, String caption )
       {
super ( x,y,width,height );
// balance of CaptionedRectangle constructor here
       }

No Scope Resolution Operator

Many C++ programmers will be alarmed to find that there is no scope resolution operator (the double colon ::) in Java. In fact there is no way in Java to elect to invoke a method from a specific class in a hierarchy.


TIP: If the method you wish to invoke is in the superclass, then the method can be invoked using the super reference.


NOTE: Many new Java programmers attempt to invoke a method from elsewhere in the hierarchy using multiple super's, something like super.super.display(). This is an interesting idea, but it's not Java!


Statements

By and large, Java supports the same control statements as C++, which in turn are pretty much the same as in the original C language. The areas of difference will be highlighted in this section.

See "Control Flow," Chapter 10

Loops

The loop statements in Java are virtually identical to their counterparts in C++. However, in Java it is possible to add a label to a loop. The label can then be used as an argument on a break or continue statement. Here is an example:

start:
for(int j=0;j<20;j++)
for(int k=0;k<20;k++)
for(int l=0;l<20;l++)
if ((j+k+l)==20) break start;

When the break statement is encountered in the if statement, the outermost loop will be broken. This is one effect that was very difficult to achieve in C or C++ without the use of goto.


NOTE: Because this was the one use of a goto that was generally accepted in practice, and because the goto is no longer required to achieve this effect, the goto statement has been eliminated from the Java syntax.


You will notice in the above example that the control variables (j, k, and l) are defined right in the for loop control statement. This is very convenient in those common situations where the control variable is only relevant within the loop and has no real meaning outside of the loop. This syntax is also legal in C++, but there is a subtle difference. In C++, the scope of the control variable begins with the for loop control statement but then continues to the end of the block. In Java, the scope of the control variable is only the body of the loop. The variable is undefined elsewhere in the block.

Conditionals

The explicit condition in the Java if statement, as well as the implicit conditions in the various Java loops, all require an expression that produces a Boolean value. This is not true in C or C++, where the expression can produce any value at all; a non-zero value is taken to be true, and a zero value is taken to be false. Thus a whole category of C and C++ errors is eliminated:

// The world's most common C error
// luckily enough, this will no longer compile in Java!
if ( x = 5 )
       {
       }

Synchronized Statements

With the addition of support for multithreading to the list of Java features comes a few problem areas. Specifically, you may have sections of code in which multiple threads might modify objects simultaneously, possibly corrupting the object. The synchronized statement deals with these critical sections by blocking the execution of code until exclusive access to the object can be acquired.

The syntax for the synchronized statement is:

synchronized (expression) statement

where the expression yields a reference to the object to be protected, and statement is a block of code to be executed once primary control of the object is acquired.

public swapFirstValues(int[] k)
       {
synchronized(k)
         {
int temp;
temp=k[0];
k[0]=k[1];
k[1]=temp;
         }
       }


CAUTION:
Do not use the synchronized statement if the object is never accessed by more than one thread, as it introduces needless processing overhead at runtime.


For more information on threads, refer back to the material in Chapter 13.

Operators and Expressions

The list of operators Java supports is almost identical to that of C++, as is the order of precedence and associativity of those operators (seeChapter 9). Here are the major differences:

  • The instanceof operator has been added, as you've already seen.

See "The instanceof Operator" earlier in this chapter.

  • The + operator can now be used with two String objects to concatenate them.

  • The right shift operator (>>) now explicitly sign-extends the value, while the new logical right shift operator (>>>) populates vacant spaces with zero-bits.

  • As you've seen, the relational and logical operators all produce Boolean results, and the logical operators only take Boolean operands.

See "Boolean Data Type," earlier in this chapter.

  • The scope resolution operator has been removed.

See "No Scope Resolution Operator," earlier in this chapter.

  • The comma operator has been removed.


NOTE: Although the comma has been removed, it can still be used within a for loop control statement to separate multiple control expressions, as in the following:

for (i=5,j=0; i>j; i--,j++)


Unlike in C++, Java operators may not be overloaded. This means that the meaning of an operator is fixed by the grammar of the language. Any special behavior that you wish to implement must be done by using an explicit method invocation.

Java expressions are evaluated in a much more predictable fashion than in C or C++. In any expression that involves one operator and two operands, the left operand is always evaluated first and the right operand is evaluated second. In method invocations, the argument list is evaluated strictly left-to-right.

Name Spaces

Name spaces essentially define the scope of a name or symbol --that portion of a program or collection of classes in which the name or symbol has meaning. More importantly, distinct name spaces protect variable and method names from conflicts--so-called name collisions.

The first and simplest difference between Java and C or C++ is that in Java programs there are no global variables or functions of any kind. This helps to keep name-space violations and conflicts to a minimum.

Java also incorporates the concept of packages to help you manage your name spaces. A package is a collection of classes. Each package has its own name space. In practice, this means that the name of a class is combined with its associated package to create a globally unique class name. And since method and variable names are managed locally within a class, the possibility of name collisions has essentially been eliminated.

See "Packages," Chapter 11


NOTE: Since the classes and methods of the Java API all belong to predefined packages, it is not possible for someone to create classes or methods that either deliberately or inadvertently conflict with system-supplied classes or methods. This is good both from the point-of-view of system security, as well as for protecting the user from programmer error.


Java was designed from the ground up to be Internet-enabled. Packages, and the name space protection that they provide, were necessary in order to provide robustness in the distributed Internet environment. But while packages may have been necessary because of the Internet, they have the added benefit of eliminating the name collisions that are possible with C and C++.