整体而言,C++语义比Java细腻很多,表达力远胜于Java, 当然这也导致开发时更大的心理包袱。 接下来,本文将从多个层级上讨论C++和Java语言特性的主要区别,希望能对大家有所帮助。
1. 变量/表达式/语句
- C++ int/char/float/double等基础类型是变长的,这是为了能够更好地发挥具有不同字长的各体系架构的性能。 而Java因可移植性而选择运行在JVM中,因此这些基础类型都是固定长度。
- C++可以通过const描述变量自身或所指对象是否可以被修改,而Java只能通过final描述变量所指引用不能被修改。
- 异常
- C++可以抛出任何变量作为异常,而Java仅能抛出Throwable. 为了捕获所有类型的可捕获异常,C++需要使用
catch(...)
, 而Java仅需catch(Throwable)
. - C++仅有try-catch, 而Java还有finally语句,其被用于解决析构函数调用时机不明确的问题
- C++可以抛出任何变量作为异常,而Java仅能抛出Throwable. 为了捕获所有类型的可捕获异常,C++需要使用
2. 函数
- C++函数虽然不是一等公民,但其可以被以指针形式存入变量,而Java仅能将其通过lambda包装为匿名类对象后再存于变量中,因此多少有些笨重。
- C++函数可以修改入参本身(通过传引用或指针),而Java只能修改入参所指对象。 此外,Java包装类型和String等都是不可变的,因此为了达到C++修改函数入参的类似的效果,Java必须将基础类型包装为类。 因此,当函数需要返回多个值时(很多C函数返回值都只是错误码,而具体返回内容则通过入参返回),Java需要自定义类来包装它们,而这相对比较笨拙。
- C++函数允许后置出参类型,而这使得其出参类型可以根据入参来推导,而Java则不行。
- C++函数里定义static变量,其具有局部作用域和全局生命周期,而Java方法则不可以。
- C++和Java lambda都是基于匿名类实现的,但是C++可以更精确地控制对外部变量的捕获。
- C++函数可以独立存在,而Java方法必须依附于某个类。
- C++函数允许入参有默认参数,虽然其只是编译时的语法糖,其有时候也能简化代码撰写,而Java函数则无此功能。
3. 类
- 对象构造与销毁
- C++可以控制对象创建与销毁等各个阶段的行为,包括构造/析构/拷贝/移动/赋值。 另外,其析构函数调用时机明确,因此其特性还被用来实现RAII以管理资源. 最后,C++还可以通过placement new控制对象构造内存地点,这个特性这使得C++容器可以将所有元素内存聚集在一起。
- Java仅能控制构造和析构阶段行为,且析构时机不明确(垃圾回收时)。 为了实现资源管理,Java提供了try with resource和finally子句。
- 运行时反射
- 运行时反射可以在运行时获取对象元数据(类信息),而这是如Spring等Java框架能够存在的根本前提。
- Java反射的实现原理为对象头部具有对象元数据,其可以在运行时被提取出来。
- C++的设计原则之一为You don't pay for what you don't use, 因此不含虚函数的类对象的内存布局基本上等同于C struct, 进而不包含类信息,因此无法完成运行时反射。 不过,C++模板十分强大,可以通过其SFINE等模板黑魔法实现编译时反射。
- 多继承
- C++允许多继承,进而导致必须严格安排对象内存布局以确保对象内存中包含子类的对应的子对象。 此外,为了解决菱形继承问题,C++还引入了虚继承,而这使得对象内存布局更加复杂。
- Java不允许多继承,因此没有这些烦恼。
- 运算符重载。C++的另外一个设计原则为让自定义类型和内置类型一样好用,因此允许运算符重载,而Java则没这个能力。
- C++对象内存中保存的是属性本身,因此可以将对象所依赖的数据聚集在一起而具有很好的空间邻近性。 Java对象内存中保存的是属性引用,属性实际内存则散落于堆上。
- C++默认所有方法为非virtual方法,而Java则仅默认private方法为非virtual。 另外,Java可以通过final来表明虚函数不可被覆盖,C++则不行。
4. 模板/泛型
- C++模板的作用时机为编译时,其是自身是图灵完备的,进化出了模板元编程之类的黑魔法。 Java泛型仅有编译时参数类型检测的效果,能力相对较弱。
- C++函数模板具有参数完美转发能力,可以据此更好地实现高阶函数。 Java无类似功能,其方法甚至不能脱离类而独立存在。
5. 标准库
- STL容器接口比Java集合接口命名更规范,比如STL容器元素数量都可以用size()来获取,而Java有size()函数、length属性和length()方法等。 STL容器接口命名规范也是其可以将泛型算法独立于容器的根本原因。
- STL容器可以通过placement new将元素对象内存聚焦在一起,而Java集合里存的都是引用,实际对象内存散落在堆上,因此空间邻近性不太好。
- C++ 11之前不承认多线程,目前多线程标准库功能丰富性也远不如Java JUC, 不过以后情况应该会有所改善。
6. 其它
- C++有很多undefined behavior, 因为其为了性能最大化而基本上不检查边界条件(其认为边界条件检查是用户责任,因为并非所有应用都需要检查每一个边界条件)。 Java基本上没类似概念。
- C++标准委员会仅制定标准,而编译器厂商的实现可能并不完全符合标准(比如MSVC早期对C++标准的支持就不太好)。 Oracle则对Java有很强的控制力,因此较少出现这种情况。
7. 总结
本文从多个层级上讨论了C++和Java主要区别,暂时先写这些,以后想到时再补充。