Chapter 4

Java for C++ Programmers


CONTENTS


If you are coming to Java from a C++ background, you are off to a great head start. In fact, you already know most of the Java syntax. This chapter will take you through a whirlwind tour of Java, pointing out the key differences between Java and C++. You'll read about C++ features that were left out of Java and about features that were added to Java to take it beyond C++. You'll see how Java has done away with the C++ preprocessor and multiple inheritance but makes up for those through a cleaner object model and interface inheritance. This chapter doesn't attempt to teach you everything you need to know to be a Java programmer, but it will help you put Java in perspective relative to your knowledge as a C++ programmer.

Data Types

Because Java was based on C and C++, the data types supported by Java are very similar to those of C and C++. This section describes the key differences between Java types and C++ types.

Primitive Types

A language's primitive types are the building blocks from which more complicated types (such as classes) are built. Java supports a set of eight primitive types, which are shown in Table 4.1.

Table 4.1. Java primitive types.
TypeDescription
byte 8-bit signed integer
short 16-bit signed integer
int 32-bit signed integer
long 64-bit signed integer
float 32-bit floating-point number
double 64-bit floating-point number
char 16-bit Unicode characters
boolean Can hold true or false

From this list, you can tell that Java adds both the byte and boolean types. Some recent C++ compilers have added support for the new C++ boolean type, so you may already be using it in your code. Because Java provides Unicode support, you should notice that its char type is 16 bits wide. This is also why the 8-bit byte type is included as a primitive type. In C++, you probably have been emulating a byte type with something similar to the following:

type unsigned char byte;

There are a couple of other extremely important differences between the Java and C++ primitive types. The Java primitives are each of a known and guaranteed size. This is critical to Java because of its goal of portability across hardware and operating systems. If an int is 16 bits on one platform and 32 bits on another platform, a program is asking for trouble if it expects to be run on both platforms. C++ guarantees certain relationships among its primitive types. For example, a C++ long is guaranteed to be at least as big as a C++ int. Java takes this further and prescribes an exact size for each primitive.

Because most of the machines that will run Java programs will do so in a 32-bit environment, the sizes of the primitive types have been defined with 32-bit optimization in mind. This means that some Java primitives may use more storage space than you are accustomed to with their C++ equivalents. In particular, you should notice that a Java int is 32 bits and a Java long is 64 bits.

A final difference worth pointing out is that all Java primitive types are signed. This means that C++ declarations like the following are not allowed in Java:

unsigned long bigLong;     // not legal in Java
unsigned double salary;    // not legal in Java

The Java boolean primitive can be set to a value of true or false. In traditional C and C++ programming, true and false were defined by using the preprocessor to be equal to 1 and 0, respectively.

Casting

In both Java and C++, it is possible to cast a variable from one type to another. However, because Java is a more strongly typed language than is C++, Java protects you and prevents you from making some casts. For example, because Java's boolean type is not a disguised int in Boolean clothing, you cannot cast between a Boolean and a numeric type. This means that you cannot do the following:

boolean aBool = false;    // not legal in Java
int anInt = (int)aBool;   // not legal in Java

Because Java doesn't rely on programming conventions that indicate false equals zero, Java cannot cast aBool to anInt in this example.

Automatic Coercions

Related to casting is the concept of automatic coercion. Automatic coercion occurs when a compiler coerces, or casts, a variable of one type into another automatically. For example, consider the following C++ code:

long aLong = 65536L;
unsigned int justAnInt;
justAnInt = aLong;
printf("%d", justAnInt);

In this example, the 65,536 stored in aLong is also placed into justAnInt. Because no explicit cast is performed, an automatic coercion from a long to an unsigned int is performed. Unfortunately, on a 16-bit platform, this will result in an error because the value in aLong is too large to fit in justAnInt. The automatic coercion will place 0 into justAnInt instead of the desired 65,536.

Because Java does not perform automatic coercions, you may need to slightly alter your thinking about some of your C++ programming habits. For example, in C++ you could write the following loop:

int count=10;
while (count) {
    // use count to do something
    count-;
}

In C++, the while loop will execute as long as count is non-zero. However, a Java while loop must be formed according to the following syntax:

while (booleanExpression)
    statement

What this means is that statements like while(count) do not work in Java because there is no automatic coercion of an integer (such as count) to the boolean that a Java while loop expects. You need to rewrite the C++ code fragment to work in Java as follows:

int count=10;
while (count > 0) {
    // use count to do something
    count-;
}

This creates a Boolean expression that is evaluated on each pass through the loop. You will need to make similar adjustments with the Java for and do…while loops, as well.

Operators

The set of operators supported by Java is nearly identical to the set supported by C++. Although the operators perform nearly identical operations in the two languages, there are several relevant differences. Table 4.2 shows the Java operators on Boolean values. The contents of this table should be instantly recognizable because each operator is also available in C++.

Table 4.2. Operators on Boolean values.
OperatorOperation Example
!Negation !a
&& Conditional AND a && b
|| Conditional OR a || b
?: Conditionala ? expr1 : expr2

These operators are worth singling out because they operate only on Java boolean types. For example, consider the following C++ fragment:

int x = 1;
int y = 7;
if (x && y) {
    // do something
}

This same statement is illegal in Java. Because the && operator expects two boolean operands and there is no automatic coercion from an integer, the Java compiler does not know how to interpret this statement. In Java, it needs to be rewritten as follows:

int x = 1;
int y = 7;
if (x != 0 && y != 0) {
    // do something
}

In this case, the two integer values have been converted into explicit tests. Because these tests are Boolean expressions, the code can now be compiled.

Java also introduces two new operators, >>> and >>>=. Each of these performs a right shift with zero fill. The latter also will perform an assignment after the shift.

Another difference between operators in Java and C++ is that Java operators cannot be overloaded. Initially, operator overloading was an exciting feature of C++ that promised to allow programmers to treat all data types, whether primitive or not, equivalently. The reasoning went that if there was a logically intuitive action that should be performed by an operator, then the language should support overloading the operator to perform that action. Unfortunately, reality intervened, and many uses of operator overloading in C++ have led to unnecessary bugs. Because of the potential for introducing bugs through operator overloading, the developers of Java wisely chose to leave it out.

Pointers

If you've been programming in C or C++ for any significant amount of time, you probably mastered pointers a long time ago. You know how to use pointers to characters, integers, structures, classes, and probably even functions. Mastering the concept of pointers has long been a rite of passage in C and C++ programming. Pointers are behind much of the power and flexibility of C++. Unfortunately, pointers are also behind much of the complexity and bugs in C++ programs. So, with both regret and rejoicing, you can say farewell to pointers.

Java does not have a pointer type of any sort. When an object or array is passed to a method, it is passed by reference, rather than by value. However, a Java program still cannot access it as a pointer or memory location.

By removing pointers from Java, the language has been greatly simplified over C and C++. However, an additional benefit is that removing pointers is consistent with the design goals of Java that it be a secure environment. By removing the ability to create a pointer directly into a system's memory, a language goes a long way toward preventing the use of the language for writing deviant programs such as viruses.

Structures and Unions

One of the unfortunate problems with C++ has been its support for compiling legacy, or pre-existing, C code. Of course, this probably also has been the key reason that C++ has gained the widespread acceptance that it has. The drawback to continuing to provide support for pre-existing code is that it muddies the language, allowing and sometimes forcing programmers to create new code that is more difficult to understand than would otherwise be necessary.

Because C++ added to C the ability to define classes, it made superfluous the need to define structures and unions. Because Java is a new language with no requirement to support an existing base of code, its object model is much cleaner. In Java, you define classes. The C concepts of struct and union have been removed.

Arrays

Like C and C++, Java uses square brackets ([]) to declare and access arrays. However, when declaring an array in Java, there are two important differences from declaring an array in C or C++, as follows:

To see the effect of these differences, consider the following array declarations:

int intArray[];
float floatArray[];
double [] doubleArray;
char charArray[];

In these examples, there is no difference between these arrays based on where the brackets are located. As a C++ programmer, you may be inclined to continue placing them after the variable name. However, there is an advantage to changing your habits and placing the brackets before the variable name. Placing the brackets in front of the variable name allows you to more easily declare multiple arrays. For example, consider the following declaration:

int [] firstArray, secondArray;

This statement will declare two arrays of integers. Depending on your perspective, you may find it more intuitive and readable than the following more C-like version:

int firstArray[], secondArray[];

Allocation

Of course, in C++ you must specify the size or dimension of the array. In Java, this is not necessary (or even allowed) because Java requires that all arrays be allocated with new. It is not possible in Java to allocate the equivalent of a C automatic array. To allocate an array using new, you would use code similar to that shown in the following examples:

int intArray[] = new int[100];
float floatArray[];
floatArray = new float[100];
long [] longArray = new long[100];
double [][] doubleArray = new double[10][10];

From these examples, you can see that memory can be allocated on the same line on which the array is declared, as was done with intArray. Or, the array can be declared and allocated on two separate lines, as with floatArray. The variable doubleArray shows how to declare and allocate a multidimensional array in Java. In this case, a two-dimensional array is allocated. This is really an array of arrays in which each of 10 first dimension arrays contains its own array of 10 items.

An alternative way of allocating a Java array is to specify a list of element initializers when the array is declared. This is done as follows:

int intArray[] = {1,2,3,4,5};
char [] charArray = {'a', 'b', 'c'};
String [] stringArray = {"A", "Four", "Element", "Array"};

In this case, intArray will be a five-element array holding the values 1 through 5. The three-element array charArray will hold the characters 'a', 'b', and 'c'. Finally, stringArray will hold the four strings shown.

Array Access

You can access items in an array in the same way you would do in C++, as shown in the following:

int ages[] = {16, 18, 21, 65};

int canDrive = intArray[0];    // can drive at 16
int canVote = intArray[1];     // can vote at 18
int canDrink = intArray[2];    // can drink at 21
int canRetire = intArray[3];   // can retire at 65

In C++, you could even take this further and access elements outside the bounds of the array. Most of the time when you did this, however, it was accidental and caused unexpected program behavior like crashes and rebooting. Fortunately, Java protects you against this by only allowing access to allocated array elements. Java will throw the ArrayIndexOutOfBoundsException exception if you try to access an element beyond the bounds of the array.

Automatic Memory Management

Unless you're the manufacturer of one of the many debugging aids targeted at helping programmers find memory leaks, you have to be excited at the prospect of automatic memory management. Java's automatic memory management features mean that you no longer have to keep track of allocated memory and then explicitly free that memory.

Although memory for an object is still allocated with new, there is no corresponding delete that must be used to release memory. What happens instead is that the Java memory manager keeps track of which memory is in use, and once there are no objects referencing a particular area of memory, that memory is automatically released and available for reuse. This is very similar to the way C++ automatic variables are released once they go out of scope.

With automatic memory management (usually known as garbage collection), you will find yourself able to write programs more quickly and with fewer bugs.

Classes

The design of Java's object model and its support for classes was certainly influenced by C++. However, Java classes borrow less from C++ than do many other aspects of Java and its syntax. Although classes are undeniably important in C++, classes are mandatory and central to all that you will do in Java. In Java, there are no free-standing variables or functions. Everything must be encapsulated within a class. Further, every class in Java can trace back through its inheritance hierarchy and find itself a descendant of the Object class. In order to understand Java classes, consider the following class definition:

class Employee {
    public String firstName;
    public String lastName;
    protected int age;
    private float salary;
    public boolean CanVote() {
        return age >= 21;
    }
    public boolean CanDrink(int legalAge) {
        return age >= legalAge;
    }
}

You'll notice from the definition of Employee that Java classes support the familiar concepts of private, protected, and public members. However, in Java, members are not grouped into private, public, or protected sections as they typically are in C++. In the Employee class, each member had its access control modifier specified right with the type and name of the member. Although you may consider this a little more typing, it definitely makes the code more readable, and therefore easier to maintain, if you don't have to read backwards through the file to find out if a class member is accessible.

Each of the familiar private, protected, and public access control modifiers has the same meaning in Java that it has in C++. Additionally, however, Java has a fourth level of access control that is used as the default. If no access control modifier is specified for a member, that member is accessible throughout the package in which it is defined, but nowhere else.

A Java package shares attributes of a C++ library, a C++ source file, and a C++ header file. Java code is shared at the package level and a package contains the definitions and source code implementations of one or more classes.

Member Methods

Returning to the definition of the Employee class, you can also see that both the CanVote and CanDrink methods were written directly in the class definition without using the C++ inline keyword. In Java, there is no concept that is analogous to the C++ header file. Each class is defined, and its methods are written in the same place. Because of this, Java does not need an inline keyword, yet all methods are written as you would write a C++ inline function.

Setting Default Values

It's always the little things that make a difference. One little thing that makes a tremendous convenience improvement in Java over C++ is the ability to set a default value for a member variable at the time it is declared. For example, consider the following definition of the Employee class:

class Employee {
    ...
    protected int age = 21;
    ...
}

In this example, the member variable age is declared and is given a default value of 21. In C++, this would have been done in the constructor. There are two advantages to setting a default value at the point where the variable is declared:

Static Members

In addition to class methods and variables that are associated with each instance of a class, a Java class can contain members that are associated with the class itself. Just as in C++, these are known as static members and are identified with the static keyword as follows:

class Employee {
    ...
    static double maxSalary = 1000000D;
    ...
}

In this example, the member variable maxSalary will exist once in the entire program, as opposed to once per instance of the class. Additionally, maxSalary has been set to an initial value of $1,000,000.

Constructors and Destructors

Each Java class you define may include one or more constructors. Just as in C++, the constructor is given the same name as the class. Java class constructors have no return value and are declared in the same manner as any other class method. This can be seen in the following
example:

class Employee {
    public String firstName;
    public String lastName;
    protected int age;
    private float salary;
    public Employee(String fName, String lName) {
        firstName = fName;
        lastName = lName;
    }
    public boolean CanVote() {
        return age >= 21;
    }
    public boolean CanDrink(int legalAge) {
        return age >= legalAge;
    }
}

Because Java includes a garbage collection feature for the automatic release of unreferenced memory, the role of destructors is much smaller than it is in C++. In C++, a destructor is necessary so that it can free any memory allocated by the object. Because of Java's automatic memory management, destructors are no longer needed to perform this job.

For these reasons, Java classes do not include C++ style destructors. Instead, each Java class can include a finalize method that can be used to perform any object cleanup. The finalize method is declared in the Object class, but because Object is the ultimate base class of all Java classes, finalize is available to every Java class. There is one danger, however, to consider when using finalize. It is possible for a Java program to terminate without this method being invoked on every object. If a program terminates with objects that are still referenced, the garbage collection process will never be used to release those objects, and finalize will never be called.

Inheritance

Inheritance in Java is indicated by the use of the extends keyword, as shown in the following example:

public class Employee extends Person {
    // member methods and variables
}
If a class is derived directly from Object, then the extends keyword is optional. The following two class declarations are equivalent:
public class Person extends Object {
    // member methods and variables
}

public class Person {
    // member methods and variables
}

Like C++, Java includes a this keyword that can be used by an object to reference itself. Additionally, Java includes a super keyword that an object can use to reference its parent, or superclass. The use of super is frequently seen in the constructor of a subclass, as shown in the following:

public class Person {
    String firstName;
    String lastName;
    Person() {}
    Person(String fName, String lName) {
        firstName = fName;
        lastName = lName;
    }
}

public class Employee extends Person {
    float salary;
    Employee(float sal, String fName, String lName) {
        super(fName, lName);
        salary = sal;
    }
}

In this example, the Person class includes a constructor that is passed a first name and a last name. The Employee class is derived from Person and includes a constructor that is passed salary, first name, and last name. The constructor for Employee first sets the internal salary member and then uses super to invoke the constructor for Person.

Abstract Classes

The Java version of a pure, virtual class is an abstract class. Instances of Java abstract classes cannot be created with new. An abstract class is identified by the use of the abstract keyword, as shown in the following:

abstract class Species {
    ...
    abstract void GiveBirth();
    ...
}

A class is considered abstract if it has one or more methods that are abstract. In the case of the Species class, the method GiveBirth is specified as abstract because some species have live births and others lay eggs. Because the method is abstract, no method body is given.

Interfaces and Multiple Inheritance

If you design a class that is entirely abstract, then that class is what Java refers to as an interface. A Java interface is similar to a class in that it defines a new type that contains both methods and variables. However, because an interface is completely abstract, its methods are not implemented within the interface. Instead, classes that are derived from an interface implement the methods of the interface.

An interface is declared in the same manner as a class except that instead of class, the keyword interface is used. For example, the following code will declare an interface named Clock:

interface Clock {
    public String GetTime(int hour);
}

To derive a class from an interface, use the keyword implements (similar to how extends is used when a class is derived from another class). To derive the classes Cuckoo and Watch from the Clock interface, you would do the following:

class Cuckoo implements Clock  {
    public String GetTime(int hour) {
        StringBuffer str = new StringBuffer();
        for (int i=0; i < hour; i++)
            str.append("Cuckoo ");
        return str.toString();
    }
}

class Watch implements Clock  {
    public String GetTime(int hour) {
        return new String("It is " + hour + ":00");
    }
}

Java does not support multiple class inheritance. In other words, a class may have only one immediate superclass because only a single class name can follow extends in a Java class declaration. Fortunately, class inheritance and interface inheritance can be combined when deriving a new Java class. And, a subclass can implement more than one interface. For example, you can do the following:

class MySubClass extends MySuperClass implements FirstInterface,
        SecondInterface {
    // class implementation
}

The Preprocessor

The Java language does not include a preprocessor. Much of the complexity of C and C++ can be traced back to the preprocessor. How many times have you read a C++ function and then needed to trace a defined value or macro back through a hierarchy of headers in order to understand the function? The preprocessor brought a lot of flexibility to C and C++, but it also added artificial complexity.

Java's removal of the preprocessor means that you will need to unlearn a couple of old habits. For example, you will no longer be able to use typedef and #define. In Java, you would instead use classes and constants.

Other Changes

There are a number of additional differences between Java and C++ beyond those already mentioned in this chapter. This section will briefly describe some additional differences.

Comments

In addition to the // and /*…*/ comments of C++, Java introduces a new comment delimiter. Java's new comment delimiter begins with /** and ends with */. A comment that is enclosed within these delimiters can be extracted from the source code and used to create documentation for the class with the JavaDoc utility. JavaDoc is fully described in Chapter 16, "Using JavaDoc to Document Your Program."

Command-Line Arguments

In a C or C++ program, the program is passed command-line arguments in the familiar argc and argv parameters to the program's main function. These parameters represent a count of the number of parameters and an array of parameter values, respectively. There will always be at least one parameter passed to a C or C++ program because the first parameter is the program name.

In a Java application, the command-line arguments are based in an array of String objects. The signature for main is as follows:

public static void main(String args[]);

Each component of the array args is one of the command-line arguments. A difference between C++ and Java is that the program name is not passed to the program as the first command-line argument in Java. Consider two programs that are invoked in the following manner:

For C++: program 100 200
For Java: java program 100 200

The command lines of these two programs will be interpreted by C++ and Java as shown in Table 4.3.

Table 4.3. Command-line arguments in C++ and Java.
ArgumentC++ Java
programargv[0] (none)
100argv[1] args[0]
200argv[2] args[1]

Character Arrays and Strings

Because Java does not allow direct manipulation of pointers, it does not support C-style, null-terminated strings. For its string support, Java utilizes a String class. Although it is possible to allocate an array of type char to emulate a C++ string, the two types are not the same.

goto, break, and continue

You probably won't shed any tears, but the goto statement is not part of Java. On the other hand, it is still part of the reserved word list so it may come back at any time. Java does replace goto, however, with the ability to use break and continue with labels. You can still use break and continue as you are used to from C++, but you can now use them to pass control flow in other ways. For example, consider the following code:

int line=1;

outsideLoop:
for(int out=0; out<3; out++) {
    for(int inner=0;inner < 5; inner++) {
        if (foo(inner) < 10))
            break outsideLoop;
        else if (foo(inner) > 100)
            continue outsideLoop;
    }
}

In this example, if the foo method returns a value less than 10, the code break outsideLoop will execute. A normal break here would break out of the inner loop. However, because this is a named break statement, it will break out of the named outer loop. This example also demonstrates the use of continue with a label.

Runtime Type Identification

Runtime Type Identification (RTTI) is a relatively new feature to C++, or at least to many C++ compilers. A form of RTTI is included in Java through its instanceof keyword.

Missing Features

As you begin programming in Java, you will notice that it is missing a couple of other aspects of C++ that you may use. In most cases, there is either no need for the feature in Java, due to its simplified model for object-oriented programming, or there is an alternative way of accomplishing the goal.

Features left out of Java that you may rely on in C++ include templates, name spaces, friend functions, and default parameters. In many cases, you will find that these old friends are no longer necessary in Java, or, at least, that their removal is justified by how much doing so simplifies the rest of your code.

Summary

This chapter covered quite a lot of territory. Building on your background in C++, you learned how Java differs from C++ in regard to data types, operators, memory management, and classes. You learned how Java simplifies programming by removing those features of C++ that are likely to introduce bugs. You also learned how Java enables you to continue creating the powerful programs you are used to in C++.