5.5 - Differences and Similarities between C++ and Java


Introduction

 

"Claiming Java is easier than C++ is like saying that K2 is shorter than Everest."

 
 --Larry O'Brien (editor, Software Development)

Now that we have covered C++ in some detail, discussing concepts of object-oriented programming as applied to C++, it makes sense to discuss the similarities and differences between C++ as we have used it so far, and Java as we have begun to use it. The good news is that there are enough similarities between the languages to allow a programmer to have a choice to use the correct language for the application to be developed, without having to enter a whole new thought process.

The General Language

 

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

 
 --Thant Tessman

This section 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 types are passed by value in Java (but non-primitive types appear to be 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 - IntegerBoolean, 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 succeed 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 /jdk7/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 


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!).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.

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.

Strings

Native strings in C++ are represented by an array of characters, terminated by '/0'. Strings in Java are objects of the String class and are immutable, i.e. cannot be modified. There is no operator overloading in Java (as there is in C++) but the + operator when applied to strings causes the creation of a new String object which is the sum of the two strings to which the operator is applied. The '+','=' and '==' operators are also valid when dealing with the String class, however be aware that the '==' is used to compare the reference addresses and not the data contained by the objects.

Arrays

In Java, when you declare an array of a class, you could say:

	Integer[] a = new Integer[5];

But this creates only an array of references, you must then instantiate each reference, using

	a[1] = new Integer(); 

Even for the default constructor (as shown). In C++, when you declare:

	Integer[5];

C++ would call the default constructor for each instance of the array, and there would be no need to explicitly call 'new' again to instantiate each reference of the array.

This form of initialisation only allows you to call the default constructor (or constructor with no parameters). In C++, you can create an array of objects, with each object sitting right next to each other in memory, which lets you move from one object to another, simply by knowing the address of the first object. This is impossible in Java.

In C++ you can also create arrays, of pointer or references to objects. This is more closely related to how Java arrays work. Java arrays are always an array of references to objects (except if you create arrays of the basic data types, intfloatdouble etc.). If you were to use an array of pointers or references, you would have an array of null references (just like Java). The syntax would be like: Integer **f = new Integer*[10];

This C++ code segment shows how to create an array of A objects arr[10] as the state of the B class, and have the non-default constructor called for the objects.

#include <iostream>
#include <string>

using namespace std;
 
 class A{
   private:
     int x;
   public:
     A(int);
     A();
     virtual void display();
   };
   
   A::A() {}
   A::A(int y): x(y) {}
   
   void A::display()
   {
     cout << "Object has the value " << x << endl;
   }
   
   class B{
     private:
       A arr[10];  //default A constructor A() is called here (10 times).
     public:
       B();
       virtual void display();
   };
   
   B::B()
   {
     for(int i = 0; i < 10; i++)
       arr[i] = A(i);	//the non-default constructor 
		              // A(int) is called here (10 times).
   }
   
   void B::display()
   {
     for(int i = 0; i < 10; i++)
       arr[i].display();
   }
   
   int main(){
     B b;
     b.display();
   }

A Java array is guaranteed to be initialized and cannot be accessed outside of its range. The range checking comes at the price of having a small amount of memory overhead on each array as well as verifying the index at run time, but the assumption is that the safety and increased productivity is worth the expense Java has run-time bounds checking on arrays, throwing an exception when an operation is performed that is out of bounds. There is a special instance variable called length that holds the number of elements in the array. Arrays in Java must be created using the new keyword.

When you create an array of objects in Java, you are really creating an array of references, and each of those references is automatically initialized to a special value with its own keyword null. When Java sees null, it recognizes that the reference in question is not pointing to an object. You must assign an object to each reference before you use it, and if you try to use a reference that is still null, the problem will be reported at run-time. Thus, typical array errors are prevented in Java. You can also create an array of primitives. Again, the compiler guarantees initialisation because it zeroes the memory for that array.

Method Parameters

In Java all variables of the standard types (as detailed in the section called “Data Types”) and references are passed by value (i.e., never by reference or by pointer). For example:

public class Test
 {
   public void square(int x) { x = x*x; }

   public Test()
   {
     int y = 5;
     System.out.println(" The value of y is " + y);  // outputs 5
     square(y);
     System.out.println(" The value of y is " + y);  // outputs 5
   }
   
   public static void main(String[] args)
   {
     new Test();
   }
 }

In Java if you pass an object to a method, you are always passing a reference to the object (NB: NOT passing by reference, you are passing this reference by value). This means that even though you are passing the reference by value, you are always operating on the original object., just as if you had used pass-by-reference in C++. For Example:

 1 
 2   
 3 public class SomeClass 
 4   {
 5    public int x = 2; // just for demonstration - set public
 6   }
 7   
 8 public class Test
 9  {
10    public void square(SomeClass s) { s.x = s.x * s.x; }
11 
12    public Test()
13    {
14 	  SomeClass y = new SomeClass();
15       System.out.println(" The value of SomeClass x is " + y.x);
16       // outputs 2
17       
18       square(y);
19       System.out.println(" The value of SomeClass x is " + y.x);  
20       // outputs 4
21    }
22    
23    public static void main(String[] args)
24    {
25      new Test();
26    }
27  }
28 
29 

When dealing with assignments in Java we have a very important difference between code written in C++ and code written in Java, that on initial inspection seems exactly the same.

Looking at the C++ version:

	/* In C++ */
	Account a(600);
	CurrentAccount b(500,5000); //bal = 500, overdraft = 5000
	a = b;
	a.display(); // results in "I am an account" being displayed
                 // with no mention of overdraft and a balance 
                 // of 500. The compiler would have prevented b = a;

Looking at the equivalent Java version:

 
	/* In Java */
	Account a;
	CurrentAccount b = new CurrentAccount(500,5000);
	a = b;
	a.display();  // results in "I am a current account"
                  // with an overdraft of 5000 and a balance of 500

In C++ when we assign a=b; the CurrentAccount object is simply sliced into an Account object - i.e. "fitting into the box for an Account object". In Java we simply have a reference to the original object so that object never changes - there is no slicing performed.



OOP Concepts in C++ and Java

Similar Constructors

Constructors work almost exactly the same way in C++ and Java. If you do not define a constructor for a class then it is allocated a default constructor. If we do define a constructor then we must use it. The only significant difference is that there is no copy constructor in Java.

No Destructors!

There are no destructors in Java, even thought there is a new keyword, there is no corresponding delete keyword, as Java takes care of all of the memory management. Java has a special finalize() method that you can add to any class. The strange thing about this method is that you can use it for tidying up (printing a paper record for Account etc.), but you do not know when it will be called. In C++ this was easier, as the destructor is called when the object goes out of scope, but in Java the finalize() method will be called when the garbage collector destroys the object. We do not know when this will be called! As discussed previously, we can request that the garbage collector should run, but we do not know when it will run! You can help the garbage collector slightly by setting all your references to null when you no longer need them, e.g. a = null; - will send a hint to the garbage collector that the object is no longer being used.

Access Specifiers

Like C++, in Java we have publicprivate and protected access specifiers, but we also have another access specifier "package". This is the default access specifier and means that all states and methods are accessible to all classes within the same package. There is no package access specifier keyword, it is simply the default if publicprivate or protected are not used. Access specifiers in Java are:

  • public - accessible everywhere, an interface method (same as C++)

  • private - accessible only in the class where it was defined (same as C++)

  • protected - accessible in the class where it is defined, the derived classes and in the same package (almost the same as C++ with the exception of the package)

  • "package" - default (there is no package specifier keyword) and means that it is accessible by any class in the same package. You can equate this to the C++ friendly condition by saying that all of the classes in the same package (directory) are friendly.

In Java there is no direct equivalent to protected in C++. In Java version 1.0 there was! it was called "private protected", but was deemed complex and unnecessary, and so was removed.

Access Specifiers when Inheriting Classes

Inheritance in Java does not change the protection level of the members in the base class. You cannot specify publicprivate or protected inheritance in Java, as you can in C++. Over-ridden methods in a derived class cannot reduce the access of the method in the base class. For example, if a method is public in the base class and you over-ride it, your over-ridden method must also be public. The compiler will enforce this rule.

Virtual and Non-Virtual Methods

In C++ methods are non-virtual by default, so to replace the behaviour (allow over-riding) of a method in the derived class you have to explicitly use the virtual keyword in the parent class. You are able to replace the behaviour of a non-virtual method but this causes serious difficulties when the behaviour of the object depends on the static rather than the dynamic type! In Java, methods are virtual by default and we always operate on the dynamic type of the object. You cannot specify a method as non-virtual, but there is a final keyword. Once you use the final keyword on a method you cannot replace it - so there are no issues with static and dynamic types.

Multiple Inheritance

C++ allows multiple inheritance - Java does not! As discussed previously in the C++ section of the notes, multiple inheritance is complex for the programmer, in that they must make complex decisions about the way that the data of the base class is to be inherited. In Java, we do not have multiple inheritance but we do have the use of Interfaces. Interfaces are a special kind of abstract class that have no data or implemented code. Interfaces might not sound too useful, but they allow a form of multiple inheritance, without having the associated difficulties of dealing with data. This replacement behaviour works very well and does not impact on development, once you get used to the change.

No scope resolution operator

There is no scope resolution operator in Java - such as Account::display(); as used previously in C++. This is possible because you must define all methods and states within a class so there is no global namespace as we had in C++. We have the super and this keywords which allow us to access the parent (only through the direct parent) and the states of a class. Packages are used instead of namespaces. When you use the super()method call in the constructor to call the parent constructor it must be the first line of the child constructor implementation.

Nested Classes

In Java we have the facility to define a class inside of another class. We refer to this class as an inner class. The only issue worth mentioning with inner classes is that they have full access to the private data and methods of the class in which they are nested. Because of this, we do not need to pass state pointers to methods that need callback.

Inline Methods

In C++ we had inline methods, generally short methods that the compiler in effect cuts-and-pastes the method code into the final program, leading to greater efficiency as the assembly language program counter is not jumping around in the final executable. There is of course a trade-off between speed of execution and size of the executable. There are no inline methods in Java - well that's not entirely true. It is up to the compiler to decide whether a method should or should not be inline. There is a keyword in Java called final which suggests to the compiler that the method should be inline, but you have no guarantee that the compiler will agree!


The Compiler

General Compiler issues

There are no forward declarations of classes/methods necessary in Java. The compiler takes care of all definitions of methods and does not throw an error until it is sure that the method or class has not been defined elsewhere.

Libraries

The standard Java libraries are supplied by Oracle and are comprehensive. In C++, the programmer uses third party code libraries that do not integrate very well with each vendor. Java provides comprehensive standard libraries from application development to networking and database access providing us with a fully integrated development language.

Exceptions

Exception handling allows run-time errors to be handled directly by the programmer. An exception object is thrown from the line of code where the error occurred and is caught by a suitable exception handler, specially designed for that type of error. Exceptions are in effect control statements that allow a different path of execution when errors occur to the normal path when no errors occur. In the Java and C++ languages exceptions cannot be ignored by the programmer, rather they must deal with them, leading to more robust applications. Exceptions exist outside of object-oriented languages, but in object-oriented languages an object is used to describe the exception.

While we did not cover exceptions in C++, they are available. However, exception specifications in Java are vastly superior to those in C++. Instead of the C++ approach of calling a function at run-time when an exception is thrown, Java exception specifications are checked and enforced at compile-time. In addition, Java over-ridden methods must conform to the exception specification of the base-class version of that method: they can throw the specified exceptions or exceptions derived from those. This provides much more robust exception-handling code.

Default Arguments

There is no support for default arguments in Java.

No Preprocessor

There are no compiler directives, no #define, no #includes

Multithreading

Java has support built into the language for multi-threading, C++ does not.

I will add more to this document as issues arise. If you notice any errors or omissions in the document then please drop me an e-mail.


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





Subpage Listing



These notes are copyright Dr. Derek Molloy, School of Electronic Engineering, Dublin City University, Ireland 2013-present. Please contact him directly before reproducing any of the content in any way.
Comments