作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
无数文章比较了c++和Java的技术特性, 但哪些差异是最需要考虑的? 当比较显示, 例如, Java不支持多重继承,而c++支持, 这是什么意思?? 这是一件好事吗? 有些人认为这是Java的优势,而其他人则认为这是一个问题.
让我们探讨一下开发人员应该选择c++的情况, Java, 或者是另一种语言, 更重要的是, why 决定很重要.
c++于1985年作为C编译器的前端发布,类似于TypeScript编译成JavaScript的方式. Modern C++ compilers 通常编译为本机机器码. 尽管有些人声称c++的编译器降低了它的可移植性, 它们确实需要为新的目标架构进行重建, c++代码几乎可以在所有处理器平台上运行.
Java于1995年首次发布,它不能直接构建到本机代码. Instead, Java构建字节码, 在Java虚拟机(JVM)上运行的中间二进制表示. 换句话说,Java编译器的输出需要特定于平台的本机可执行文件才能运行.
c++和Java都属于类C语言家族, 因为它们的语法与C语言相似. 最显著的区别是它们的生态系统:c++可以无缝地调用基于C或c++的库, 或者操作系统的API, Java最适合基于Java的库. 类来访问Java中的C库 Java本机接口 (JNI) API,但它容易出错,并且需要一些C或c++代码. c++还比Java更容易与硬件交互,因为c++是一种低级语言.
我们可以从多个角度比较c++和Java. 在某些情况下,c++和Java之间的选择是很明确的. 原生Android应用通常应该使用Java,除非它是一款游戏. 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.
非游戏的跨平台应用不在此讨论范围之内. 在这种情况下,c++和Java都不是理想的选择,因为它们对于高效的GUI开发来说过于冗长. 对于高性能应用程序, 最好创建c++模块来完成繁重的工作, 并为GUI使用更高效的开发人员语言.
非游戏的跨平台应用不在此讨论范围之内. 在这种情况下,c++和Java都不是理想的选择,因为它们对于高效的GUI开发来说过于冗长.
对于一些项目,选择可能并不明确,所以让我们进一步比较:
Feature | C++ | Java |
---|---|---|
系统 | No | Yes |
运行时性能 | Best | Good |
Latency | 可预测的 | 不可预测的 |
引用计数智能指针 | Yes | No |
全局标记-清除垃圾收集 | No | Required |
堆栈内存分配 | Yes | No |
编译为本机可执行文件 | Yes | No |
编译成Java字节码 | No | Yes |
与低级操作系统api的直接交互 | Yes | 需要C代码 |
直接与C库交互 | Yes | 需要C代码 |
与Java库的直接交互 | 通过JNI | Yes |
标准化的构建和包管理 | No | Maven |
除了表中比较的特征, 我们还将关注面向对象编程(OOP)特性,如多重继承, 泛型/模板, 和反射. 注意,这两种语言都支持OOP: Java强制这样做, 而c++支持OOP以及全局函数和静态数据.
在OOP中,继承是指子类从父类继承属性和方法. 一个标准的例子是a Rectangle
类继承自更泛型的 Shape
class:
//注意,我们在一个c++文件中
类Shape {
/ /位置
int x, y;
public:
//子类必须重写这个纯虚函数
虚拟虚空draw() = 0;
};
类矩形:公共形状{
//宽度和高度
int w, h;
public:
空白画();
};
多重继承是指子类从多个父类继承. 下面是一个例子 Rectangle
and Shape
类和额外的 Clickable
class:
//不推荐
类Shape {...};
类矩形:公共形状{...};
类Clickable {
int xClick, yClick;
public:
虚拟void click() = 0;
};
类ClickableRectangle:公共矩形,公共Clickable {
无效点击();
};
在这种情况下,我们有两个基本类型: Shape
的基本类型 Rectangle
) and Clickable
. ClickableRectangle
从两者继承以组成两个对象类型.
C++ supports multiple inheritance; Java does not. 多重继承在某些极端情况下很有用,例如:
但是,通常不鼓励使用多重继承. 它可能会使代码复杂化并影响性能,除非与模板元编程结合使用——最好只使用模板元编程 最有经验的c++程序员.
使用任何数据类型的类的泛型版本对于代码重用是实用的. 两种语言都提供这种支持——java通过泛型提供, c++通过模板——但是c++模板的灵活性可以使高级编程更安全、更健壮. 每次在模板中使用不同的类型时,c++编译器都会创建新的自定义类或函数. Moreover, c++模板可以根据顶层函数的参数类型调用自定义函数, 允许特定的数据类型具有专门的代码. 这叫做 模板特殊化. Java没有类似的特性.
与此形成鲜明对比的是, 使用泛型时, Java编译器通过称为类型擦除的过程创建没有类型的一般对象. Java在编译期间执行类型检查, 但是程序员不能根据泛型类或方法的类型参数修改其行为. 为了更好地理解这一点,让我们看一个通用的快速示例 std::string format(std::string fmt, T1 item1, T2 item2)
使用模板的函数, template
,从我创建的c++库:
std::string firstParameter = "字符串";
int secondParameter = 123;
//将打印输出设置为8个字符宽的字符串和十六进制值
format("%8s %x", firstParameter, secondParameter);
//将打印输出格式化为两个8字符宽的字符串
format("%8s %8s", firstParameter, secondParameter);
c++会生成 format
函数作为 Std::string格式(Std::string fmt, Std::string item1, int item2)
,而Java将在没有特定的 string
and int
对象类型 item1
and item2
. 在本例中,c++模板知道最后一个传入形参是an int
因此可以执行必要的 std:: to_string
第二种转换 format
call. 没有模板,一个c++ printf
语句尝试像第二个语句一样将数字打印为字符串 format
调用将具有未定义的行为,并可能使应用程序崩溃或打印垃圾. Java函数在第一个函数中只能将数字视为字符串 format
调用并不直接将其格式化为十六进制整数. 这是一个微不足道的例子, 但它展示了c++选择专用模板来处理任意类对象而不修改其类或对象的能力 format
function. 我们可以在Java中使用反射而不是泛型正确地生成输出, 尽管这种方法的可扩展性较差,而且更容易出错.
In Java, 可以(在运行时)找出结构细节,例如在类或类类型中哪些成员可用. 这种特性称为反射, 大概是因为这就像拿着一面镜子对着物体看里面是什么. (更多信息可以在Oracle的 反映文档.)
c++没有完整的反射,但是现代c++提供了 运行时类型信息 (RTTI). RTTI允许对特定对象类型进行运行时检测, 尽管它不能像对象的成员那样访问信息.
c++和Java之间的另一个关键区别是内存管理, 主要有两种方法: manual, where developers must keep track of and release memory manually; and 自动的,软件跟踪哪些对象仍在使用,以回收未使用的内存. 在Java中,一个示例是 垃圾收集.
Java需要垃圾回收内存, 提供比手动方法更容易的内存管理,并消除通常会导致安全漏洞的内存释放错误. c++本身不提供自动内存管理, 但是它确实支持一种叫做 智能指针. 智能指针使用引用计数,如果正确使用,它是安全和高性能的. c++还提供了析构函数,用于在对象销毁时清理或释放资源.
Java只提供堆分配,而c++同时支持堆分配(使用 new
and delete
还是老一点的C malloc
函数)和堆栈分配. 堆栈分配可能比堆分配更快、更安全,因为堆栈是线性数据结构,而堆是基于树的, 因此,堆栈内存的分配和释放要简单得多.
c++与堆栈分配相关的另一个优点是一种称为资源获取即初始化(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是c++智能指针在没有手动解引用的情况下工作的方式——在函数顶部引用的智能指针在退出函数时自动解引用. 如果这是对智能指针的最后一次引用,所连接的内存也会被释放. 尽管Java提供了类似的模式, 这会更尴尬 而不是c++的RAII,特别是当您需要在同一代码块中创建多个资源时.
Java具有可靠的运行时性能, 但是c++仍然是最好的,因为对于实际应用程序来说,手动内存管理比垃圾收集要快. 虽然Java在某些特殊情况下可以胜过c++,因为 JIT编译, c++赢得了大多数重要案例.
特别是, 与c++对堆分配的减少使用相比,Java的标准内存库的分配会使垃圾收集器过度工作. However, Java仍然相对较快,应该是可以接受的,除非延迟是最主要的问题——例如, 在具有实时限制的游戏或应用中.
Java在性能上的不足,在易用性上得到了弥补. 影响开发人员效率的一个组件是构建和包管理——我们如何构建项目并将外部依赖项引入应用程序. 在Java中,有一个工具叫做 Maven 将此过程简化为几个简单的步骤,并集成了许多ide,例如 IntelliJ IDEA.
然而,在c++中,不存在标准化的包存储库. 在应用程序中构建c++代码甚至没有标准化的方法:一些开发人员更喜欢Visual Studio, 而其他人则使用CMake或其他自定义工具集. 进一步增加了复杂性, 某些商业c++库是二进制格式的, 并且没有一致的方法将这些库集成到构建过程中. Moreover, 构建设置或编译器版本的变化可能会导致二进制库无法正常工作.
构建和包管理的摩擦并不是c++远不如Java对初学者友好的唯一原因. 程序员可能很难安全地调试和使用c++,除非他们熟悉C, 汇编语言, 或者是计算机的低级工作. 把c++想象成一个强大的工具:它可以完成很多工作,但如果滥用它是危险的.
Java前面提到的内存管理方法也使其比c++更易于访问. Java程序员 不必担心释放对象内存,因为语言会自动处理这个问题.
既然我们已经深入探讨了c++和Java之间的差异, 我们回到最初的问题:c++还是Java? 即使对这两种语言有深入的了解,也没有一个放之四海而皆准的答案.
不熟悉低级编程概念的软件工程师在限制在c++或Java之间选择时,最好选择Java, 除了像游戏这样的实时环境. 另一方面,希望扩展视野的开发人员可以通过选择c++学到更多.
然而,c++和Java之间的技术差异可能只是决定的一个小因素. 某些类型的产品需要特殊的选择. 如果你还不确定, 您可以参考流程图,但请记住,它最终可能会指向第三种语言.
c++通常优于Java,并且可以访问Java所缺乏的较低级别的特性, 但每种语言都有其优势.
Java和c++有不同的优势. Deciding which is more useful for your project depends on your goals; 例如, 如果初学者友好是最重要的, Java是最佳选择.
你可以把它想象成两个城市之间的差异. 在一些城市,骑自行车更容易,但乘坐地铁就更难了. 关于如何从一个地方到另一个地方的说明各不相同. c++和Java之间的一般区别包括编译器输出, 兼容库, 生产力特征.
c++和Java都是C族语言,这意味着它们的语法与C有一些共同的元素. Java的最初设计是模仿c++的,这就解释了它们有很多相似之处.
世界级的文章,每周发一次.
世界级的文章,每周发一次.