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.
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.
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 *=.
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:
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!
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
No C++ References
See "Primitive Types and java.lang Wrapper Classes," later
in this chapter.
No Structs, Unions, or Enums
No consts 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: 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: 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: 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.
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.
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.
The Runtime Library
printf ("Hello, world!\n");
cout << "Hello again, world!\n";
System.out.println ("Hello from Java!");
The Structure of Java Programs
The Big Picture
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.
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.
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
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.
Source File Inclusion
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.
#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.
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 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.
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.
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.
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.
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.
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, 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.)
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
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.
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.
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.
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.
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
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."
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.
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.
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
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.
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++.
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.
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.
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++.
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
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.
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 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.
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.
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 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 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.
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.
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.
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 }
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!
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
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.
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 ) { }
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.
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:
See "The instanceof Operator" earlier in this chapter.
See "Boolean Data Type," earlier in this chapter.
See "No Scope Resolution Operator," earlier in this chapter.
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 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++.