Learning outcome: Compare C++ and Java in terms of compilation, memory management, pointers, inheritance, portability, and program structure.
Java was designed partly in response to the complexity of C++. Both languages use familiar syntax, but they make different trade-offs. C++ gives the programmer more direct control over memory and machine-level behaviour. Java removes or restricts several low-level features to improve portability, safety, and maintainability.
High-Level Comparison
| Aspect | C++ | Java |
|---|---|---|
| Compilation target | Native machine code for a specific platform | Bytecode executed by the JVM |
| Platform portability | Usually recompile for each platform | Same .class file can run anywhere a JVM exists |
| Pointers | Direct pointer access and pointer arithmetic | References exist, but no direct pointer arithmetic |
| Memory cleanup | Manual control is possible with delete; modern C++ also uses smart pointers |
Automatic garbage collection |
| Multiple inheritance | A class can inherit from multiple classes | A class extends one class and can implement multiple interfaces |
| Program structure | Namespace-scope (“global”) functions are allowed | In the JDK 17 course baseline, methods and executable initialisers are declared inside classes, interfaces, enums, or records |
| Runtime environment | Program runs directly on the operating system | Program runs inside the JVM |
Compilation Model
C++ typically compiles source code into native machine code for a particular operating system and processor. Java compiles source code into bytecode, and the JVM translates bytecode into the machine instructions needed by the current platform.
In simple terms: C++ prints a book in one country’s language and ships the printed copies. Java ships the manuscript in a shared language, and each country prints its own edition on arrival.
Pointers and References
In C++, a pointer stores a memory address. C++ allows direct pointer manipulation, including pointer arithmetic.
In simple terms: A C++ pointer is a street address you can do arithmetic on — add one and you are next door, whether or not a house stands there. A Java reference is a name in a contacts list: it points at a real object or at nobody, and cannot be nudged sideways.
int x = 10;
int* p = &x;
p = p + 1; // pointer arithmetic
This control is powerful, but it can also cause serious errors such as dangling pointers, invalid memory access, and buffer overruns.
Java uses references to objects, but the programmer cannot read a reference as a raw memory address or perform arithmetic on it.
int[] numbers = {10, 20, 30};
int[] ref = numbers; // ref points to the same array object
// ref + 1 is not legal Java
The result is less low-level control, but much stronger memory safety for typical application programming.
Memory Management
C++ gives the programmer direct responsibility for memory in many situations.
In simple terms: In C++ you book the hall and must remember to release it. In Java the garbage collector notices when the last guest has left and clears the hall for you.
int* data = new int[100];
// use data
delete[] data;
Forgetting to release memory causes leaks. Releasing it twice or using it after release can crash a program. Modern C++ reduces these risks with RAII and smart pointers, but memory lifetime remains an important programmer responsibility.
Java allocates objects on the heap and uses garbage collection to reclaim memory that is no longer reachable.
int[] data = new int[100];
// no delete statement
Garbage collection reduces common memory errors. The trade-off is that the programmer has less direct control over exactly when memory is reclaimed. Garbage collection manages memory, not external resources such as files, sockets, and database connections; those still require deterministic closing, normally with try-with-resources.
Inheritance
C++ supports multiple inheritance of classes.
In simple terms: When a class has two parents and both answer the same question differently, the child has no principled way to choose. Java sidesteps the argument: one parent class, and as many interfaces as you like, because an interface states what must be done rather than how.
class Printer { };
class Scanner { };
class Copier : public Printer, public Scanner { };
This can be useful, but it can also create ambiguity. The classic example is the diamond problem, where a class inherits through two paths from the same ancestor.
Java avoids multiple inheritance of classes. A Java class can extend one class, but it can implement multiple interfaces.
interface Printable {
void print();
}
interface Scannable {
void scan();
}
class Copier implements Printable, Scannable {
public void print() {
System.out.println("Printing");
}
public void scan() {
System.out.println("Scanning");
}
}
This keeps the class hierarchy simpler while still allowing a class to provide multiple capabilities. Interfaces may contain default methods in modern Java. If two inherited defaults conflict, the implementing class must resolve the conflict explicitly; Java still does not inherit instance state from multiple classes.
Code Structure
C++ allows global functions.
int add(int a, int b) {
return a + b;
}
In the JDK 17 baseline, Java methods and executable initialisers live inside type declarations rather than as namespace-scope functions.
public class Calculator {
public static int add(int a, int b) {
return a + b;
}
}
For the course's standard source-file form, a Java program uses a class declaration, and the launcher begins application execution from the standard main method.
Common Mistakes
| Misconception | Correction |
|---|---|
| "Java has no pointers at all." | Java has references, but not direct pointer arithmetic or raw address access. |
| "Garbage collection means memory never matters." | Memory still matters, but the JVM handles many cleanup tasks automatically. |
| "Java is always slower." | Java has JVM overhead, but modern JIT compilation can make Java very fast for many workloads. |
| "Interfaces are the same as multiple inheritance of classes." | Interfaces can provide types, contracts, and default methods, but a class still extends only one class and does not inherit instance state from several parents. |
Practice
- List three C++ features Java restricts or removes.
- Explain why Java bytecode improves portability.
- Write one reason garbage collection is helpful and one trade-off it creates.
- Convert this idea into Java style: "a global function named
squarethat returnsn * n."
Quick Check
1. Why can the same Java bytecode run on different operating systems?
Each operating system uses its own compatible JVM to execute the same platform-neutral bytecode.
2. Does Java allow pointer arithmetic?
No. Java uses references to objects but does not expose raw memory addresses or pointer arithmetic.
3. How does Java avoid multiple inheritance of classes?
A class extends only one class, but it may implement multiple interfaces to acquire several capabilities.
Summary
C++ emphasizes control. Java emphasizes portability, safety, and consistency. Java replaces direct machine targeting with bytecode, direct pointer manipulation with references, manual memory cleanup with garbage collection, and multiple class inheritance with interfaces.