An In-depth Look at C++ vs. Java
Deciding on a language can be intimidating when you don’t have deep experience with the available options. This comparison explores the fundamental differences between C++ and Java, and what to consider when choosing between them.
Deciding on a language can be intimidating when you don’t have deep experience with the available options. This comparison explores the fundamental differences between C++ and Java, and what to consider when choosing between them.
Timothy is an experienced software architect who has created multiple game engines with C++, including one used in more than 100 titles. His extensive background in Java ranges from Android game and application development to industry experience at Amazon, building an Android client for the AWS AppStream service. He has used more than 20 programming languages in his career, several of which he custom created to meet a specific need.
PREVIOUSLY AT
Countless articles compare C++ and Java’s technical features, but which differences are most important to consider? When a comparison shows, for example, that Java doesn’t support multiple inheritance and C++ does, what does that mean? And is it a good thing? Some argue that this is an advantage of Java, while others declare it a problem.
Let’s explore the situations in which developers should choose C++, Java, or another language altogether—and, even more importantly, why the decision matters.
Examining the Basics: Language Builds and Ecosystems
C++ launched in 1985 as a front end to C compilers, similar to how TypeScript compiles to JavaScript. Modern C++ compilers typically compile to native machine code. Though some claim C++’s compilers reduce its portability, and they do necessitate rebuilds for new target architectures, C++ code runs on almost every processor platform.
First released in 1995, Java doesn’t build directly to native code. Instead, Java builds bytecode, an intermediate binary representation that runs on the Java Virtual Machine (JVM). In other words, the Java compiler’s output needs a platform-specific native executable to run.
Both C++ and Java fall into the family of C-like languages, as they generally resemble C in their syntax. The most significant difference is their ecosystems: While C++ can seamlessly call into libraries based on C or C++, or the API of an operating system, Java is best suited for Java-based libraries. You can access C libraries in Java using the Java Native Interface (JNI) API, but it is error-prone and requires some C or C++ code. C++ also interacts with hardware more easily than Java, as C++ is a lower-level language.
Detailed Trade-offs: Generics, Memory, and More
We can compare C++ to Java from many perspectives. In some cases, the decision between C++ and Java is clear. Native Android applications should typically use Java unless the app is a game. Most game developers should opt for C++ or another language for the smoothest possible real-time animation; Java’s memory management often causes lag during gameplay.
Cross-platform applications that aren’t games are beyond the scope of this discussion. Neither C++ nor Java are ideal in this case because they’re too verbose for efficient GUI development. For high-performance apps, it’s best to create C++ modules to do the heavy lifting, and use a more developer-productive language for the GUI.
Cross-platform applications that aren’t games are beyond the scope of this discussion. Neither C++ nor Java are ideal in this case because they’re too verbose for efficient GUI development.
For some projects, the choice may not be clear, so let’s compare further:
Feature | C++ | Java |
---|---|---|
Beginner-friendly | No | Yes |
Runtime performance | Best | Good |
Latency | Predictable | Unpredictable |
Reference-counting smart pointers | Yes | No |
Global mark-and-sweep garbage collection | No | Required |
Stack memory allocation | Yes | No |
Compilation to native executable | Yes | No |
Compilation to Java bytecode | No | Yes |
Direct interaction with low-level operating system APIs | Yes | Requires C code |
Direct interaction with C libraries | Yes | Requires C code |
Direct interaction with Java libraries | Through JNI | Yes |
Standardized build and package management | No | Maven |
Aside from the features compared in the table, we’ll also focus on object-oriented programming (OOP) features like multiple inheritance, generics/templates, and reflection. Note that both languages support OOP: Java mandates it, while C++ supports OOP alongside global functions and static data.
Multiple Inheritance
In OOP, inheritance is when a child class inherits attributes and methods from a parent class. One standard example is a Rectangle
class that inherits from a more generic Shape
class:
// Note that we are in a C++ file
class Shape {
// Position
int x, y;
public:
// The child class must override this pure virtual function
virtual void draw() = 0;
};
class Rectangle: public Shape {
// Width and height
int w, h;
public:
void draw();
};
Multiple inheritance is when a child class inherits from multiple parents. Here’s an example, using the Rectangle
and Shape
classes and an additional Clickable
class:
// Not recommended
class Shape {...};
class Rectangle: public Shape {...};
class Clickable {
int xClick, yClick;
public:
virtual void click() = 0;
};
class ClickableRectangle: public Rectangle, public Clickable {
void click();
};
In this case we have two base types: Shape
(the base type of Rectangle
) and Clickable
. ClickableRectangle
inherits from both to compose the two object types.
C++ supports multiple inheritance; Java does not. Multiple inheritance is useful in certain edge cases, such as:
- Creating an advanced domain-specific language (DSL).
- Performing sophisticated calculations at compile time.
- Improving project type safety in ways that are simply not possible in Java.
However, using multiple inheritance is generally discouraged. It can complicate code and impact performance unless combined with template metaprogramming—something best done by only the most experienced C++ programmers.
Generics and Templates
Generic versions of classes that work with any data type are practical for code reuse. Both languages offer this support—Java through generics, C++ through templates—but the flexibility of C++ templates can make advanced programming safer and more robust. C++ compilers create new customized classes or functions each time you use different types with the template. Moreover, C++ templates can call custom functions based on the types of the parameters of the top-level function, allowing particular data types to have specialized code. This is called template specialization. Java doesn’t have an equivalent feature.
In contrast, when using generics, Java compilers create general objects without types through a process called type erasure. Java performs type-checking during compilation, but programmers cannot modify the behavior of a generic class or method based on its type parameters. To understand this better, let’s look at a quick example of a generic std::string format(std::string fmt, T1 item1, T2 item2)
function that uses a template, template<class T1, class T2>
, from a C++ library I created:
std::string firstParameter = "A string";
int secondParameter = 123;
// Format printed output as an eight-character-wide string and a hexadecimal value
format("%8s %x", firstParameter, secondParameter);
// Format printed output as two eight-character-wide strings
format("%8s %8s", firstParameter, secondParameter);
C++ would produce the format
function as std::string format(std::string fmt, std::string item1, int item2)
, whereas Java would create it without the specific string
and int
object types for item1
and item2
. In this case, our C++ template knows the last incoming parameter is an int
and therefore can perform the necessary std::to_string
conversion in the second format
call. Without templates, a C++ printf
statement trying to print a number as a string as in the second format
call would have undefined behavior and could crash the application or print garbage. The Java function would only be able to treat a number as a string in the first format
call and would not format it as a hexadecimal integer directly. This is a trivial example, but it demonstrates C++’s ability to select a specialized template to handle any arbitrary class object without modifying its class or the format
function. We can produce the output correctly in Java using reflection instead of generics, though this method is less extensible and more error-prone.
Reflection
In Java, it is possible to find out (at runtime) structural details like which members are available in a class or class type. This feature is called reflection, presumably because it’s like holding up a mirror to the object to see what’s inside. (More information can be found in Oracle’s reflection documentation.)
C++ doesn’t have full reflection, but modern C++ offers runtime type information (RTTI). RTTI allows for runtime detection of specific object types, though it cannot access information like the object’s members.
Memory Management
Another critical difference between C++ and Java is memory management, which has two major approaches: manual, where developers must keep track of and release memory manually; and automatic, where software tracks which objects are still in use to recycle unused memory. In Java, an example is garbage collection.
Java requires garbage-collected memory, providing easier memory management than the manual approach and eliminating memory-releasing errors that commonly contribute to security vulnerabilities. C++ doesn’t provide automatic memory management natively, but it does support a form of garbage collection called smart pointers. Smart pointers use reference counting and are secure and performant if used correctly. C++ also offers destructors that clean up or release resources upon an object’s destruction.
While Java only offers heap allocation, C++ supports both heap allocation (using new
and delete
or the older C malloc
functions) and stack allocation. Stack allocation can be faster and safer than heap allocation because a stack is a linear data structure while a heap is tree-based, so stack memory is much simpler to allocate and release.
Another advantage of C++ related to stack allocation is a programming technique known as Resource Acquisition Is Initialization (RAII). In RAII, resources such as references tie to the life cycle of their controlling object; the resources will be destroyed at the end of that object’s life cycle. RAII is how C++ smart pointers work without manual dereferencing—a smart pointer referenced at the top of a function is automatically dereferenced upon exiting the function. The connected memory is also released if this is the last reference to the smart pointer. Though Java offers a similar pattern, it’s more awkward than C++’s RAII, especially if you need to create several resources in the same code block.
Runtime Performance
Java has solid runtime performance, but C++ still holds the crown since manual memory management is faster than garbage collection for real-world applications. Though Java can outperform C++ in certain corner cases due to JIT compilation, C++ wins most non-trivial cases.
In particular, Java’s standard memory library overworks the garbage collector with its allocations compared to C++’s reduced use of heap allocations. However, Java is still relatively fast and should be acceptable unless latency is a top concern—for example, in games or applications with real-time constraints.
Build and Package Management
What Java lacks in performance, it makes up for in ease of use. One component affecting developer efficiency is build and package management—how we build projects and bring external dependencies into an application. In Java, a tool called Maven simplifies this process into a few easy steps and integrates with many IDEs such as IntelliJ IDEA.
In C++, however, no standardized package repository exists. There isn’t even a standardized method to build C++ code in applications: Some developers prefer Visual Studio, while others use CMake or another custom set of tools. Further adding to the complexity, certain commercial C++ libraries are binary-formatted, and there is no consistent way to integrate those libraries into the build process. Moreover, variations in build settings or compiler versions can cause challenges in getting binary libraries to work.
Beginner-friendliness
Build and package management friction isn’t the only reason C++ is far less beginner-friendly than Java. A programmer may have difficulty debugging and using C++ safely unless they’re familiar with C, assembly languages, or a computer’s lower-level workings. Think of C++ like a power tool: It can accomplish a lot but it is dangerous if misused.
Java’s aforementioned memory management approach also makes it much more accessible than C++. Java programmers do not have to worry about releasing object memory since the language takes care of that automatically.
Decision Time: C++ or Java?
Now that we’ve explored the differences between C++ and Java in-depth, we return to our original question: C++ or Java? Even with a deep understanding of the two languages, there is no one-size-fits-all answer.
Software engineers unfamiliar with low-level programming concepts might be better off selecting Java when restricting the decision to either C++ or Java, except for real-time contexts like gaming. Developers looking to expand their horizons, on the other hand, might learn more by choosing C++.
However, the technical differences between C++ and Java may only be a small factor in the decision. Certain types of products require particular choices. If you still aren’t sure, you can consult the flowchart—but keep in mind that it may ultimately point you to a third language.
Further Reading on the Toptal Blog:
- After All These Years, the World Is Still Powered by C Programming
- Server-side I/O Performance: Node vs. PHP vs. Java vs. Go
- C# vs. C++: What’s at the Core?
- Buggy Java Code: The Top 10 Most Common Mistakes That Java Developers Make
- How C++ Works: Understanding Compilation
- How C++ Competitive Programming Can Help Hiring Managers and Developers Alike
Understanding the basics
Which is more powerful, C++ or Java?
C++ generally outperforms Java and has access to lower-level features that Java lacks, but each language has its advantages.
Is Java more useful than C++?
Java and C++ have different strengths. Deciding which is more useful for your project depends on your goals; for example, if beginner-friendliness is a top priority, Java is the optimal choice.
What is the difference between C++ and Java?
Think of it as the difference between two cities. In some cities, it’s easier to bike but harder to use the subway. Instructions on how to get from one place to another will vary. General differences between C++ and Java include compiler outputs, compatible libraries, and productivity features.
Are Java and C++ related?
Both C++ and Java are C-family languages, meaning that their syntaxes have elements in common with C. The initial design of Java was modeled after C++, which explains their many similarities.
Timothy Mensch
Lafayette, CO, United States
Member since August 5, 2020
About the author
Timothy is an experienced software architect who has created multiple game engines with C++, including one used in more than 100 titles. His extensive background in Java ranges from Android game and application development to industry experience at Amazon, building an Android client for the AWS AppStream service. He has used more than 20 programming languages in his career, several of which he custom created to meet a specific need.
PREVIOUSLY AT