热门问题
时间线
聊天
视角

Java和C++的对照

来自维基百科,自由的百科全书

Remove ads

本条目为Java语言C++语言之间的比较

设计目标

C++和Java语言之间的不同可以追溯到它们各自的传统,它们有着不同的设计目标。

  • C++ 被设计成主要用在系统性应用程式设计上的语言,对C语言进行了扩展。基于C语言这个为执行效率设计的过程序程式设计语言,C++添加了对这些特性的支持:静态类型面向对象程式设计的支持、异常处理RAII以及泛型。另外它还添加了一个包含泛型容器和算法的C++库函数
  • Java 最初被设计用来支持网络计算。它依赖一个虚拟机来保证安全可移植性。Java包含一个可扩展的库以提供一个完整的下层平台抽象。Java是一种静态面向对象语言,它使用的语法和C++类似,但并不与之兼容。为了使更多的人到使用更易用的语言,它进行了全新的设计。

不同的开发目标导致C++和Java这两种语言的不同的规则以及设计上的平衡点不同。如下列出不同点:

更多信息 C++, Java ...

C++ 是一门强大的语言,设计用在系统程式设计方面。Java语言是设计成简单易用易学习,并有一个强大的跨平台的库。Java函数库对一个函数库来说相当的大。但Java并不会提供所在平台的所有特性和接口。C++函数库简单健壮,提供容器关系数组的支持。[2]

语言特性

语法

  • Java语法上下文无关文法,可以用一个简单的LALR语法分析器来分析。而分析C++就复杂多了;例如 Foo<1>(3); ,如果 Foo 是一个变量,那么它是一个比较的表达式,但如果 Foo 是一个类范本的名字,那么它会创建一个物件。
  • C++允许名字空间级别的常量,变量和函数. 而所有这样的 Java 声明必须在一个类或者接口当中。
  • 在 C++ 的声明中,一个类名可以用来声明一个此类物件的值。 Java 里没办法做到这点,在Java里对象不是值。在 Java 的声明中,一个类名声明的是对此类的一个物件的引用。而在 C++ 里与之等价的做法是用 "*" 来声明一个指针。
  • 在 C++ 里,"."操作符将一个物件作为一个左指令引数来访问这个物件的成员。因为对象在 Java 里不是值,所有的对象都通过引用来访问,刚才的做法在 Java 里是无法实现的。在 Java 里,"." 操作符是将一个物件的引用作为左指令引数来访问这个物件的成员,在C++中和这种做法等价的是 "->"。
更多信息 C++, Java ...
  • 在 C++ 里,定义一个指向常量的指针(只读指针)是可能的,也就是说,你不能修改这个指针指向的物件的内容。函数和方法也都保证不会修改用 "const" 关键字的指针指向的物件的内容,是强制常量正确性英语const-correctness的。在 Java 里这是不可能做到的,你可以定义一个引用为 "final"(就像在 C++ 里定义一个指针"常量"),但这只是阻止你重新绑定这个引用;你还是可以修改这个 "final" 引用指向的物件的。
更多信息 C++, Java ...
  • C++ 支持 goto 语句;Java 强制结构化流程控制structured control flow),依赖break标签continue标签 语句来提供类似于 goto 的部分。. 一些评论者指出这些标签化的流程控制打破了结构化程式设计的单退出点的。点.[3]
  • C++ 提供了一些 Java 缺乏的低级特性。在 C++ 里,指针可以用来操作特定的存储器位置,这是在写低级操作系统模块的时候必须用到的。类似的,许多 C++ 编译期支持内联汇编,在 Java 里,这样的代码只能放在外来的库中,而且在调用的时候只能通过JNI来访问这些外来库提供的接口.
Remove ads

语义

  • C++ 允许给函数/方法的偏好设置默认值,Java 不提供这个特性。但是方法重载可以达到同样的效果。
  • C++ 里最小的编译单位是一个函数;Java 里最小的编译单位是一个类. 在 C++ 里,函数可以被单独编译。在 Java 里,要编译和维护单独的方法需要把它们移到超类子类或者使用其他的代码重构的技巧。
  • C++ 允许基类型之间的一些隐式的转换,也允许程序员对于用户自定义类型相关的隐式转换规则。在 Java 里,只有基类型之间变宽类型的转换可以是隐式的;其余的转换需要显式的类型转换语法。
    • 这造成的一个后果是,虽然在 Java 和 C++ 里循环的条件(ifwhilefor 里的退出条件)预期的都是一个布尔表达式,但 if(a = 5) 这样的代码在 Java 里会导致编译错误,因为没有从整型到布尔的隐式变窄转换。如果代码是 if(a == 5) 的输错的情况那么是很方便发现这个错误的。而目前的 C++ 编译器一般来说只会针对这种情况产生一个警告。
  • 对于传参数给函数的情况,C++ 支持引用传递值传递。在 Java 里,参数总是值传递的。[4] 但在 Java 里,所有的非基类型的值都只是对于对象的引用(用 C++ 的术语来说,它们是智慧指针)。对象在 Java 里不是作为值直接被使用的,只有对象的引用可以被直接操作;习惯于将对象当做值直接使用的 C++ 开发者经常会把这个跟引用传递搞混。
  • Java 内建的类型在字节宽度和取值范围上是被虚拟机定义好的;在 C++ 里,内建的类型有定义一个最小取值范围,但是其他的部分(字节宽度)可以被映射成具体平台上支持的原生类型。
    • 举个例子,Java 字符是16位的Unicode字符,字符串是由这样的字符组成的序列。 C++ 提供窄和宽两种字符,但实际的字符宽度是和平台相关的,视所用的字符集而定。字符串可以由这两种字符中的一种组成。
  • 浮点数及其操作的精度和舍入方式在 C++ 里是平台相关的. Java 提供了一个可选的严格的浮点数模型英语strictfp,保证跨平台的一致性,不过可能会导致运行时效率比较差.
  • 在 C++ 里,指针可以作为内存地址直接操作. Java 没有指针 — 它只有对象引用和数组引用,这两者都不允许直接用来访问内存地址。在 C++ 里可以构造一个指向指针的指针,而 Java 的引用只能指向对象。。
  • 在 C++ 里,指针可以指向函数或者方法(函数指针)。在 Java 里的等价物是对象或者接口的引用。
  • 虽然有使用栈内存分配的对象,C++ 还是支持区域资源管理,一个用来自动管理内存和其他系统资源的技术,此技术支持确定性对象销毁(deterministic object destruction)。不过,区域资源管理在 C++ 里是不被保证的;它只是一个设计模式,所以需要依赖程序员遵守相关的规则. Java 通过使用垃圾搜集来支持自动内存管理,但对于其他的系统资源(窗口,通讯端口,线程),如果垃圾搜集器无法决定它们是否不再被用到,那通常还是需要显式的释放的。
  • C++ 的用户可自定义操作符重载的特性在 Java 里是不支持的。唯一在 Java 里可以重载的操作符是 "+" 和 "+=" 操作符,在字符串里重载为连接字符串。
  • Java 的标准应用程式接口支持反射动态加载任意代码。
  • C++ 支持静态和动态的库连接。
  • Java 支持泛型,其主要目的是提供类型安全的容器. C++ 支持模板,在泛型编程方面提供了更强的支持。
  • Java 和 C++ 都对基类型(也叫"内建"类型)和用户自定义类型(也叫"复合"类型)。在 Java 里,基类型只有值的语义,复合类型只有引用的语义. 在 C++ 里所有的值都有值语义,可以创建对于任何类型的引用,这样就允许通过引用语义来操作对象。
  • C++ 支持任意类型的多重继承. 在 Java 里一个类只能从单个的类继承而来,但一个类可以实现多个的接口(换句话说,它支持类型的多重继承,但对于实现只能单继承)。
  • Java 对于类和接口是显式区分的。在 C++ 里多重继承和纯虚函数使得定义出类似于 Java 的接口的类是可能的,不过会有少许区别。
  • Java 在语言和标准库都对多线程有良好的支持。synchronized 这个 Java 的关键字为了支持多线程应用提供了简单而安全的的互斥锁,但同步(synchronized)区只能用 LIFO 的顺序离开。Java 也为更高阶的多线程同步提供了健壮而复杂的库。在 C++ 里没有专门为多线程定义的内存模型;但第三方库提供了和 Java 差不多的功能;不过这些 C++ 库之间差异较大,一致性不好。
  • C++ 方法可以声明为虚函数,虚函数是在运行期根据对象的类型才确定的。 C++ 方法缺省情况下不是虚的. 在 Java 里,方法缺省情况下是虚的,但可以使用final关键字使之声明为非虚的。
  • C++ 枚举属于基类型,支持和其他整数类型之间的转换和比较。Java 枚举实际上是类的实例(它们从 java.lang.Enum<E> 扩展而来),象其他的类一样可以定义构造函数,数据成员及方法。

资源管理

  • Java 提供了自动化的垃圾搜集。在 C++ 里内存管理通常通过构造函数,析构函数以及智慧指针。C++ 标准允许垃圾搜集,但并不强制要求;实际使用上垃圾搜集极少被用到。强制使用自动垃圾搜集导致了在 Java 里编写实时软件是困难的。[3]
  • C++ 可以申请任意的内存块。Java 只能通过对象实例化来申请内存。(注意:在 Java 里,程序员可以通过创建一个字节数组模拟申请任意的内存块,不过 Java 数组仍然是对象。)
  • Java 和 C++ 在资源管理上使用不同的习语。Java 主要依赖只能回收内存的垃圾搜集机制,因为该机制如果用于回收使用中的非内存的系统资源可能是非常危险的。而 C++ 主要依赖 RAII (资源的获取就是初始化)。这反映了这两种语言的几方面的不同:
    • 在 C++ 里在栈里申请复合类型的对象是很平常的,一旦退出栈的范围就会被销毁。在 Java 里复合类型的对象总是在堆里申请的内存,而后被垃圾搜集器搜集(除非在虚拟机里使用了逃逸分析技术来将堆的内存申请转成栈的。)
    • C++ 有析构函数,而 Java 有finalizer(finalizer). 两者都会在对象释放之前被调用,但是它们有显著的不同. 一个 C++ 对象的析构函数必须被隐式(栈变量对象的情况)或者显式地调用来释放对象. 析构函数在对象释放之前同步地执行. 同步,协调的反初始化以及释放在 C++ 里满足 RAII 的要求. 在 Java 里,对象的释放是被垃圾搜集器隐式处理的. 一个 Java 对象的 finalizer 在它被最后一次访问之后和在实际释放之前的某个时间点被异步异步)地调用,这个调用有可能一直不产生. 非常少的对象需要 finalizer;只有那些在释放前必须保证一些清理工作一定要做的对象来说才是需要的 — 典型的情况是:释放对 JVM 来说是外部的资源. 在 Java 里,企图安全同步的释放某些系统资源,只能用显式的 try/finally 结构来进行.
    • 在 C++ 里是有可能有一个迷途指针的 – 过时的对一个已释放的对象的引用引用);试图使用一个迷途指针的结果是导致程序错误. 在 Java 里,垃圾搜集器不会销毁一个正在被引用的对象.
    • 在 C++ 里未初始化过的基类型对象是有可能存在的,Java 强制要做缺省初始化.
    • 在 C++ 里有可能申请了一个对象,但对它没有任何引用. 这样的不可达对象不可访问内存)是无法被销毁的,导致了内存泄漏. 作为对比,在 Java 里一个对象不会被回收直到它变得不可达(对于用户程序来说). (注意: 弱引用(弱引用)是被支持的,这个特性让 Java 的垃圾搜集器能够识别不同 程度的可达性.)在 Java 里垃圾搜集阻止了很多内存泄漏的情况,但某些情况下泄漏仍然是可能的.[5]
    • Java 更容易泄漏非内存资源,而 C++ 的惯用做法更不会导致这种泄漏.

  • C++ 对于许多平台相关的特性提供了跨平台的访问方式. 从 Java 到原生的操作系统和硬件相关的函数的直接访问需要用到JNIJava本地接口)。

运行时

  • C++ 通常来说会直接被编译成机器码,被操作系统直接执行。Java 通常会被编译成字节码,被Java虚拟机解释器或者即时编译器编译成机器码然后执行。
  • 因为表达方式不受限制,低级的 C++ 语言特性(例如:不被检查的数组访问,原始指针,类型双关语(type punning英语type punning)不能在编译期间或者运行期间可靠地被检查. 相关的编程错误会导致低级的缓存溢出段错误存储器区段错误). 标准模板库 提供了高级的抽象(例如 vector,list 和 map)来帮助避免这样的错误.在 Java 里,低级错误不会发生或者会被JVM检测到并以异常的形式报告给应用.
  • Java 语言在越界访问数组的时候一般来说会对数组进行边界检查(bounds checking). 这消除了导致程序不稳定的一个可能因素,但这是以执行速度更慢一些作为代价的. 在一些情况下,编译器分析compiler analysis英语compiler analysis)可以检测到不必要的边界检查并去掉。C++ 对于原生数组的越界访问没有要求特定的处理,所以需要对于原生数组确认不越界. 但C++ 标准库里的一部分库象 std::vector 也提供了可选的边界检查. 总的来说,Java 数组是"总是安全;严格限制;开销较多" ,而 C++ 原生数组是"可选的开销; 完全不限制;有潜在的不安全."

模板 vs. 泛型

C++ 和 Java 都提供泛型编程的能力,分别是模板泛型(Generics in Java英语Generics in Java). 虽然它们被创造用来解决类似的问题,有类似的语法,但实际上很不一样.

更多信息 C++ 模板, Java 泛型 ...

杂项

  • Java 和 C++ 在使代码在不同的文件分开方面使用了不同的技术. Java 使用了一个包系统,这个系统对所有的程序都要指定了文件名和路径. 在 Java 里,编译器负责导入可执行的类文件. C++ 使用了头文件原始码的包含系统来在不同的文件共享声明.
  • 编译好的 Java 代码一般来说比 C++ 文件小,因为Java字节码(Java bytecode)一般来说比机器码要更紧凑[来源请求],Java 程序都不是静态链接的.
  • C++ 编译多了一个文本预处理过程,Java 是没有的. 因此一些用户在他们的编译过程之前增加了一个预处理的过程,这样能更好的支持需要条件编译的情况.
  • 两个语言里数组都是定长的. 在 Java 里,数组是头等对象,而在 C++ 里它们只是它们的基本类型元素的连续的序列,经常用一个指向第一个元素的指针和一个可选的长度来引用. 在 Java 里,数组是被边界检查的,而且知道它们的长度,而在 C++ 里你可以将任意的序列当成一个数组. C++ 和 Java 都提供了相关的容器类(分别为std::vectorjava.util.ArrayList),可以改变大小.
  • Java 的除法和模除操作符是定义成零截断的. C++ 没有定义这两个操作符是零截断的还是"负无穷截断"的. 在 Java 里-3/2 总是得到 -1,但一个 C++ 编译器可能会返回 -1 或 -2,视平台而定. C99 定义了和 Java 一样的除法方式. 两种语言都保证对于所有的 a 和 b(b!=0)(当 a 和 b都是整型的时候)(a/b)*b + (a%b) == a. C++ 版本有时候会更快,因为它允许直接使用处理器的截断方式.
  • 整型的长度在 Java 里是已定义好的(int 为 32-bit, long 为 64-bit),而在 C++ 里整型和指针的长度是和编译器以及应用二进制接口相关的. 因此仔细编写的 C++ 代码可以利用64位处理器的能力而又可以在32位处理器上工作. 但是需要很仔细的用可移植的方式编写. 作为对比,Java 的固定整型大小使得程序员无法做到这样,没办法利用处理器的字长会导致 Java 在64位处理器上表现较差.

性能

想运行一个编译好的 Java 程序,电脑上要运行JVM;而编译好的 C++ 程序不需要额外的应用。比较早期的 Java 版本在性能上比静态编译的语言如 C++ 差得很多,这是因为用 C++ 是直接编译成一些机器指令,而当 Java 编译成字节码以后用 JVM 解释执行的时候又牵涉了不少额外的机器指令。 例如:

更多信息 Java/C++ 语句, C++ 生成的代码 (x86) ...

C++ 在大部分的情况下都比 Java 要快,[7] 有几个数值方面的基准测试的研究争辩说 Java 在某些情况下可能会比 C++ 的性能好得多。[8][9][10] 但有人说数值方面的基准测试对于语言的评估是不合适的,因为编译器都可以做相关的优化,甚至可能将被测试的代码彻底删除。[11][12][13] 如果涉及到一个真正现实应用的程序,Java 会因为很多原因导致性能变差:[14][15][16]

  • 所有的对象都在堆里被申请。对于使用小对象的函数来说会导致很大的性能损失,因为在栈里申请内存几乎没有性能损失。
  • 方法缺省是虚的。这对于小对象来说会因为虚表增加好几倍的内存使用。它也会引起性能损失,因为 JIT 编译器不得不对查虚表的过程做额外的优化。
  • 即使使用标准的容器依然会有很多的类型转换,这会引起性能损失,因为需要遍历整个继承树。
  • 虚拟机更进一步增加了内存的使用,因此降低了内存的局部性,增加了缓存命中失败率,从而导致整个程序变慢。
  • 缺乏低级细节的操作方式使得开发者无法将程序进一步优化,因为编译器不支持。[17]

有人争论说,和 Java 相比 C++也有很多劣势:

  • 指针使得优化变得困难,因为它们可能指向任意的数据。当然现在这一点也并非完全正确,因为一些现代的编译器引入了 "严格别名" 的规则 [18] 并且支持 C99 的关键字 restrict,从而严格限制了指针的使用,使其只能用于指向已知的变量 [19]
  • Java 的垃圾搜集和使用malloc/new来申请内存相比能拥有更好的缓存连贯性,因为它的申请一般来说是顺序的。然而,始终有争论认为二者同样会导致内存的“零碎化”(即多次分配和回收之后内存空间会变得不连续),且并没有哪一个比对方有更明显的缓存优势。
  • 运行时编译可能可以更好的优化代码,因为可以利用运行时的额外的资讯,例如知道代码是在什么样的处理器上运行。然而当今的情况也并非完全如此,因为目前最先进的 C++ 编译器也会针对不同系统生成不同的目标代码,以期充分利用该系统的计算能力 [20]

此外,有争议的是,花在更复杂的 C++ 代码上的 debug 时间太多,用 Java 开发完全可以把这些时间用来优化 Java 代码。当然对于一个给定的程序来说两种语言能优化到什么程度也是一方面。最后,对于处理器负担很重的情况,例如视频渲染,C++ 能直接访问硬件,在同样一个硬件规格下 C++ 总是会比 Java 的表现好很多。

所有权控制

C++不是任何一个公司或者组织的商标,不被任何个人拥有。[21]Java原是Sun的商标,现在由甲骨文公司拥有。[22]

C++语言由 ISO/IEC 14882 定义,是一个ISO标准,由 ISO/IEC JTC1/SC22/WG21 委员会发布。Java语言由 Java Language Specification 定义,这是一本Sun公司(已被甲骨文收购)出版的书。[23]

其他

两者的对象访问格式也不一样。

参考文献

外部链接

Loading related searches...

Wikiwand - on

Seamless Wikipedia browsing. On steroids.

Remove ads