The General Language

 

"Arguing that Java is better than C++ is like arguing that grasshoppers taste better than tree bark."

 
 --Thant Tessman

This secion will discuss some general differences between the syntax used in the C++ and Java languages.

Data Types

There are some key differences between Java and C++ that must be noted when dealing with a discussion of data types:

  • Java has no pointers - all non-primitive types are passed by reference and memory management is automatically taken care of.

  • Java has no struct or union types - These are maintained in Java using classes.

  • Java has only 8 supported types - byte (1 byte), boolean (1 byte), char (2 bytes), short (2 bytes), int (4 bytes), long (8 bytes), float (4 bytes) and double (8 bytes). All are signed and all are initialised to zero when declared - e.g. int x; // x will have a value of 0.

  • Java also natively supports helper or wrapper classes to support these types - Integer, Boolean, Double etc.

In Java the char type has 2-bytes to allow support for international character sets as opposed to the single byte in C++. The boolean type in Java is a true boolean type and not just the int value of 0 and non-zero, as in C++. The value stored in a boolean type variable must either be true or false - you cannot perform an assignment like:


  boolean myFlag = 1; // not allowed - even if you try to use a cast

A reference in Java to use objects is similar to the way that we use objects in C++.


  Account a = new Account(500.0);

In C++ the new operator returns a pointer, but in Java the new operator returns a reference to an object and is the only access that we have to the object. This is the only way that we operate on objects in Java, we never operate on the objects directly, rather we only operate on the reference.

In Java when we create a reference without calling a constructor, it will be initialised to null . e.g.


  Account a;

This account reference a will be initialised to null and a NullPointerException will be thrown if you tried something like a.display(); as the constructor of Account has not yet been called.

Type conversion - Historically C++ casts between pointers and references were unchecked. This has been improved in recent years, preventing us from using a pointer to invoke a method on an object that the pointer "is aware of" but the object "is not aware of". In Java casts that always suceed are allowed, but casts that will always fail are detected at compile time.

In C++ and Java, the compiler performs type checking during the first pass, preventing the incorrect use of arguments in methods, assignments etc. This is called static type checking. The Java language includes dynamic type checking, where some type checking is also performed at run-time, providing for powerful checks, but leading to a performance decrease.

Comments

As discussed previously, we have the block comment and end of line comment formats in C++. These formats are supported by Java and have the exact same syntax. Java has the addition of a third type of comment beginning with /** and ending with */ relating to the javadoc tool that is in the /jdk1.x.x/bin directory. This tool allows the generation of automatic code documentation, created following this format. There are several keywords (e.g. @author, @version etc.) that have meaning within the comment block, and you can even embed HTML tags. For example:


  /** This is a method to lodge money to the account object
   * @author Derek Molloy  
   * @version 1.0
   * @param amount - The amount to be lodged
   * @return void
   */

  public void makeLodgement(float amount)
  { 
 	...
  }

Once you have written the code, you can run the javadoc application on the specified file/package. This will then generate the HTML format documentation for your code. The Java API documentation was created using an internal SUN version of javadoc.

Memory management

The Java garbage collector manages all memory in Java. It reclaims all memory for objects once there are no remaining references to that object. When the garbage collector runs, it searches for all objects that have no references and reclaims the memory.

C++ developers are required to provide their own memory management routines, otherwise a complex C++ application, running for a long period would simply cause the computer to run out of memory. Implementing memory management correctly in C++ is complicated, and so memory leaks are a very frequent problem with C++ developed applications. Garbage collection was not added to the C++ language specification, as all developers would be required to deal with a standard garbage collector and every C++ application would incur the overhead. There are many 3rd party C++ garbage collectors available, such as the Boehm-Demers-Weiser [10] conservative garbage collector, that replaces the C malloc() or C++ new calls (It can alternatively be used as a leak detector!).

Where are objects stored?

When discussing Memory management and Garbage Collectors it is useful to discuss some aspects of how memory is laid out while the program is running, in particular how memory is arranged. There are six different places to store data:

  • Registers: Registers are the fastest storage location because they exists inside the processor. However, the number of registers is severely limited, so registers are allocated by the compiler according to its needs. The programmer does not have direct control over the registers.

  • The Stack: The stack exists in general RAM (Random-Access Memory), but has direct support from the processor via its stack pointer. The stack pointer is moved down to create new memory and moved up to release that memory. This is an extremely fast and efficient way to allocate storage, second only to registers. The Java/C++ compilers must know, while they are creating the program, the exact size and lifetime of all the data that is stored on the stack, because it must generate the code to move the stack pointer up and down. This constraint places limits on the flexibility of our applications. In Java some storage exists on the stack - in particular, object references, but Java objects are not placed on the stack. Variables on the stack are often referred to as automatic (or scoped) variables.

  • The Heap: The heap is a general-purpose pool of memory in the RAM area (where all Java objects live). The difference between the heap and the stack is that the compiler does not need to know how much storage it needs to allocate from the heap or how long that storage must stay on the heap. There is a great deal of flexibility in using storage on the heap. In Java, whenever you need to create an object, you simply write the code to create it using the new keyword and the storage is allocated on the heap when that code is executed. The flexibility associated with the heap has a penalty in that it takes more time to allocate heap storage. In C++ we have the same flexibility, but the programmer must take more responsibility, creating an object on the heap using the new keyword, but importantly, also using the delete keyword when finished with the object.

  • Constant Storage: Constant values are often placed directly in the program code, which is safe since they can never change.

  • Static Storage: Static variables are also stored in RAM. Static storage contains data that is available for the entire time a program is running (Even when the object/reference is not in scope). You can use the static keyword to specify that a particular element of an object is static, but Java objects themselves are never placed in static storage. The static storage area is a fixed patch of memory that is allocated before the program begins running.

  • Non-RAM Storage: If data lives completely outside a program it can exist while the program is not running, outside the control of the program. The two primary examples of this are streamed objects, in which objects are turned into streams of bytes, generally to be sent to another machine, and persistent objects, in which the objects are placed on disk so they will hold their state even when the program is terminated. The trick with these types of storage is turning the objects into something that can exist on the other medium, and yet can be resurrected into a regular RAM-based object when necessary. Java provides some support for persistence and is frequently discussed in the context of database object mapping.

No Pointers

There are generally few problems with this fact as Java provides suitable replacement through the use of well-formatted references.

Global Functions

In Java all methods must be defined within a class. There are no global methods or variables. This might seem unreasonable, but it works well. For example, the mathematical routine square root can be accessed through the Math class - a static class that allows you to call methods like Math.sqrt(25.0); allowing us to even replace mathematical operations, perhaps with different precision operations, by importing our own mathematical class.



[10] See: http://www.hpl.hp.com/personal/Hans_Boehm/ and http://www.hpl.hp.com/personal/Hans_Boehm/gc/index.html