logo

Java vs C++

Last Updated: 2022-02-12

Memory Management

  • Java: uses virtual machines(JVM) which has built-in garbage collection (managed runtime).
    • you freely create new objects, JVM will clean up the objects when they are no longer used.
    • you do not need to know where the objects are located in memory; and JVM may move the objects around due to garbage collection.
  • C++: no garbage collection.
    • you have to manually track the usage of the objects and free the memory.
    • you can directly work with memory locations: once an object is allocated in moemory it does not move. You can find the location using address-of operator &, like &x.
    • you need to understand and work with pointers.

References and Pointers

C++ references behave differently than Java references.

  • Java: references behave similarly to C++ pointers; for example, you can change the object that a Java reference refers to, and you can assign null to a Java reference.
  • C++: think of a C++ reference as an alias — another name for an existing object. Unlike pointers, references cannot (legally) be null; they always refer to something. (a reference referring to null is one type of Undefined Behavior).

Stack vs Heap

  • stack: stack pointer move down to create new memory, move up to release. efficient.
    • Variables within the function are allocated on the stack — created within a stack frame upon entering the function and destroyed when destroying that stack frame upon exiting the function.
  • heap: unlike the stack, the compiler does not need to know how long the storage must stay on the heap; dynamically create and release; takes longer time than stack.
    • for an object needs to outlive the scope where it was created, for example by a factory function.

Diff in Java and C++

  • Java:
    • primitive on stack: boolean, char, byte, short, int, long, float, double, void. more efficient.
    • all objects are created on the heap, using new.
    • object reference on the stack.
    • A a; means a is just a reference, nothing is created yet; A a = new A(); will actually create an object on heap.
    • both stack and heap in Java are managed by JVM, will be auto cleaned up.
  • C++:
    • A a; will allocate memory in stack;
    • A* a = new A(); or auto a = std::make_unique<A>(); will allocate memory on the heap, and a is a pointer to that memory. Raw pointers needs manual calls to delete; smart pointers will free the memory automatically.
    • can also be created on the heap using C apis like malloc().
    • stack memory will be auto-released as soon as the variable is out of scope, but not for heap (so if they are not freed there's a memory leak).

Objects

  • Java: int i is not an object, Integer i is.
  • C++: int i is an object (int itself is a type, which is not an object).

Memory Overhead

  • Java:
    • 32-bit Java: 8-byte overhead per object.
    • 64-bit Java: 16-byte overhead per object.
  • C++:
    • no garbage collection and cannot use arbitrary objects for synchronization.
    • If you have classes with overridden methods, each object has a pointer to its vtable, just like the Java object's reference to its class.
    • std::unique_ptr has memory overhead only if you provide it with some non-trivial deleter.
    • std::shared_ptr has overhead because of it's memory allocation for the control block (which keeps the ref counter and a pointer list to all weak references); std::shared_ptr is always a 2 pointer tuple (one to the object, one to the control block).

Early Binding vs Late Binding

  • Java: Early binding, know the type during compiling.
  • C++: Late binding, do not know the exact type during compiling, but know in run time. Use virtual.

Reference Semantics vs Value Semantics

  • Java: variables have reference semantics.
  • C++: variables have value semantics.

Reference Semantics

When you assign an object variable to another variable in Java, you end up with two variables that refer to the same object.

// Java
String a = new String("Foo");
String b = a;
String c = new String("Foo");
assert(a == b);      // a and b refer to the same object
assert(a != c);      // a and c refer to different objects
assert(a.equals(c)); // a happens to have the same value as c

You can do the same thing in C++, using pointer syntax or reference syntax:

// C++
std::string a = "Foo";
std::string& r = a;
std::string\* p = &a;
std::string b = "Foo";
assert(&a == &r); // a and r both refer to an object at the same address
assert(&a == p); // a and p both refer to an object at the same address
assert(&a != &b); // a and b refer to objects at different addresses
assert(a == b); // a happens to have the same value as b

Value Semantics

In Java, when you assign one primitive variable (e.g. int, float, boolean, etc) to another (int a = b), rather than making a new reference, Java simply copies the value of the first variable into the second. Such copying value semantics are what C++ uses for all variables! C++ uses a type's copy constructor or assignment operator (operator=) to assign one value to another, and if you don't provide these methods for your type, C++ will provide a default for each.

Here's an example to help show this fundamental difference between Java and C++:

// Java
Set<String> s1 = new TreeSet<>();
Set<String> s2 = s1;

s1.add("Alice");

assert(s2.contains("Alice")); // True, because s1 and s2 refer to the same Set.
// C++
std::set<std::string> s1;
std::set<std::string> s2 = s1;

s1.insert("Alice");

assert(s2.empty()); // True, because s2 was copied from s1 at construction time.

Pass By Value vs Pass By Reference

  • Java: parameters (including object's reference) are always passed by value.
  • C++: parameters could be passed by value, pointer or reference.

Type

Unsigned types

  • Java does not support unsigned arithmetic
  • C++ does.

Byte

  • C/C++: byte ranges from 0 to 255
  • Java: byte is signed: -127 to 128.

Compare Strings

  • C++: string::operator==() is using string::compare() under the hood, so the 2 strings can be compared directly using ==.
  • Java: == checks if two strings are pointing to the same string, but to compare string content, use equlas().

Classes

Constructors

Constructors in C++ and Java behave very similarly.

  • types:
    • Java: constructor
    • C++: constructor, copy constructor, move constructor
  • Default constructor: In both languages, the compiler provides a default constructor if you don't provide one yourself. A default constructor simply creates an "empty" instance of an object.
  • Constructor Ordering
    • The order C++ calls constructors is similar to the order Java would call them.
    • C++ has a bigger difference between initialization and assignment than Java does — assignment is disallowed for some types (such as const types).
    • Unlike Java, the order of construction within a function argument list or other subexpression is unspecified in most cases. Given run(Foo(a), Bar(b)) you can't assume Foo will be constructed before Bar!
    • The rules for variables at namespace scope are complex. Notably, there is no relative ordering between different translation units! By contrast, Java has a well-defined order for initializing static variables, so this difference forms an area of C++ with subtle and potentially significant pitfalls. This is one of the reasons Google Style prohibits non-trivial global variables.

Destructor

  • C++: destructors run within the program's flow of control, guaranteed to run.
    • The general rule is that destructors are called in the reverse order that the related constructors were called.
  • Java: does not have destructors; the closest concept is the finalize() method, it is triggered during garbage collection, outside of program's flow of constrol, may or may not be called. finalize() is deprecated in JDK 18.

Inheritance

  • Java: single rooted hierarchy, inherit from Object, does not support multiple inheritance.
  • C++: supports multiple inheritance.

Overloading

  • Java does not support operator overloading, only method overloading.
  • C++ C++ supports both method overloading and operator overloading.

Portability

  • C++: platform dependent, the source must be recompiled for different platforms.
  • Java: platform independent, Java code will be compiled to bytecode, which will be used by JVMs. No need to recompile since JVM will handle differences among different platforms.

Functions

  • C++: Functions and data may exist external to any class, global and namespace scopes are available.
  • Java: All function and data exist within classes; package scope are available.

Return Code

  • C++: The integer returned from the main function is the process exit status, indicating if the code is successful.
  • Java: Not the return code from main, instead System.exit(int status) has to be called.
public static void main(String[] args) {
  System.exit(1);
}

Actually, Java's main function has to return void, otherwise an error will be thrown:

Error: Main method must return a value of type void in class XXX, please define the main method as: public static void main(String[] args)

Interacting with native systems

  • C++: can direct call native system libraries.
  • Java: only through JNI (Java Native Interface) or JNA (Java Native Access) or the new Foreign-Memory Access API / Foreign Linker API.

Filenames

  • Java: class name must match filename, e.g class A muast be stored in A.java.
  • C++: no such restriction.

C++ in JVM

OpenJDK is implemented in C++ and Java.

From JDK 16, C++14 features can be used in JVM.

Exceptions

Run-time checking: C++ exception specifications are checked at runtime rather than at compile time, so they offer no programmer guarantees that all exceptions have been handled.