在TIOBE榜单中常年霸榜的两门老牌热门语言Java和C++因为有很多相似之处所以经常被拿出来作比较,这两门语言的编程风格很相似,不过主要用途、特性和技术栈有些不同。通过这些不同之处可以间接看到软件技术的发展历程,所以有必要了解一下它们的关系和差异。
在了解Java和C++的关系之前,我们先熟悉一下这两门语言的概况:
C++ | Java | |
---|---|---|
创始人 | Bjarne Stroustrup | James Gosling |
发布时间 | 1983 | 1995 |
定位 | 高级语言 | 高级语言 |
基础 | C | C++ |
特点 | 灵活、高效 接近底层 | 简洁、严谨 跨平台 严格面向对象 |
应用领域 | 系统编程 大型应用 嵌入式 高性能场景 游戏 | 企业级应用 Web应用 Android 大数据 |
技术栈 | 计算机原理 操作系统 计算机网络 数据库 数据结构与算法 面向对象技术 … | 计算机网络 数据库 面向对象技术 高并发 分布式 Web技术栈 … |
Java和C++之间的关系很有渊源,甚至可以说这两门语言是血亲。
C++: 从名字就能看出来C++是C的延续和扩展,最大的扩展就是支持了面向对象。由于C++和C的兼容性非常好而且经常以混合编程的形式出现,所以经常会把这两门语言统一称为C/C++。
C++和C有非常亲密的关系和很好的兼容性,但并不意味着C++代码可以无差别兼容C代码。
C++独有的特性C不能使用,这毋庸置疑。
C和C++在编译和语法上也会有一些差别,C++的语法更严格,混合编程需要注意这些差别带来的影响。最常见的一个例子——C和C++同名函数的编译结果不一致(诱因是C++支持重载机制)。
由于C++的语法比C更严格,C的一些编程技巧对C++不适用。相比而言,C++特性更多功能更强大,C代码更灵活、更简洁。
Java:在设计Java之前,高司令(James Gosling)试图修改、扩展C++解决当时遇到的的问题,不过后来放弃了这种思路,而直接创造了一门新语言Oak(1995年之前叫Oak,后更名为Java)。所以我们会发现Java和C++无论是语法风格还是编程思想上都有很多相似之处,可以说Java是C++的 “精简+加强” 形态。
相同点
既然Java和C++有如此密切的关系,那他们之间一定少不了共同点:
- 都是静态、强类型语言;
- 都支持面向对象技术;
- 有很多相似的特性,如泛型、lambda、foreach等;
- 语法风格相似;
不同点
前面提到Java可以看成是C++的“精简+加强”形态,具体是如何“精简+加强”的呢?
- 精简
- 基本数据类型
- 指针
- 宏
- 重载操作符
- 多重继承
- 加强
- 垃圾回收
- 内存模型
- 严格面向对象
- 模板 --> 泛型
上面列举的是Java和C++在使用和语法特性上的一些主要差异,那么为什么会存在这些差异?难道只是为了让Java特立独行,为了证明Java与C++不一样?真正的程序员不会这么无聊,开发编程语言的大牛程序员更不会这么无聊。所以到底是为什么呢?这要从Java和C++两门语言本身的设计说起,语言本身的差异主要体现在三个方面
- 运行环境
- 内存管理方式
- 对面向对象技术的支持形式
运行环境
C++和Java代码的编译、执行过程如下:
- C++ --> 机器码 --> 计算机。 代码被编译成二进制形式的机器码,直接在CPU执行。
- Java -> 字节码 --> 虚拟机 --> 计算机。 代码被编译成Java字节码,在Java虚拟机上执行。
可以看到Java和C++的运行环境存在很大不同,忽略编译过程,C++代码是直接在CPU上运行的,而Java代码则运行在JVM上。JVM的设计原理比较复杂,这里可以把JVM简单理解为一个解释器软件,它可以把Java字节码翻译成CPU能执行的控制命令,所以Java代码只要有JVM就能运行而不关心硬件。而C++编译生成的机器码直接和硬件交互。
也正是因为JVM的存在,让Java拥有了一些C++不具备的优秀特性,如可移植性、垃圾回收等等;同时也因为JVM的存在,让Java很难支持C++的一些强大特性,如操作内存。
内存管理方式
Java和C++的内存模型中都存在堆、栈、常量区的概念,不过它们在对象的内存分配方式和内存释放两方面存在很大的区别。
对象的内存分配方式:Java中的对象只能在堆上分配内存;C++中的对象可以在堆 、 栈 或者 静态区分配内存。
- Java中创建对象的方式主要有new操作符、反射、反序列化几种方式,无论通过哪种方式创建对象,都需要在堆区申请内存分配给对象;所以在Java中,栈区一般只存放基本数据类型的变量和对象引用。
- C++中创建对象的方式非常灵活:直接定义变量、new操作符、在给定内存上初始化对象。第一种方式,直接定义变量,在静态区或栈区为对象申请内存(全局变量的内存在静态区、局部变量的内存在栈区);第二种方式,new操作符创建对象,在堆区申请内存;至于第三种方式,内存可以是指定的任意位置内存,详情参考placement new的用法。
内存释放:Java有垃圾回收机制,不需要手动释放申请的堆内存;C++中需要手动释放申请的堆内存;
- Java的垃圾回收机制来自于它强大的运行环境——JVM,而不是它自身的语法特性;JVM提供了一个清洁工,我们在使用Java编程的时候可以随便制造“垃圾”并且可以完全不用管理垃圾该怎么处理,因为“清洁工”会帮我们管理。
- C++没有垃圾回收机制,需要手动释放申请的堆内存;有一些编程技巧可以帮我们更方便地管理内存,甚至达到类似Java那样不用关心“垃圾”的效果,比如智能指针、RAII等技术。
对面向对象技术的支持形式
Java和C++虽然都支持面向对象,但是它们对面向对象的支持程度、支持方式和一些技术细节有些不一样
-
支持形式不同。Java是严格的面向对象语言;C++支持面向对象,也仍然保留面向过程的语法。
-
继承技术不同。Java不支持多重继承、有接口的概念;C++支持多重继承,没有接口的概念;
在Java中类是用关键字class定义的,接口是用关键字interface定义的;接口的作用是定义行为、限制类型。因为多重继承存在一些弊端如菱形继承问题,Java取消了多重继承这种特性引入接口的概念,就是为了解决这些问题
除前面列举的典型差距外,Java和C++在语法和某些技术的实现上也有一些不同,这也能体现出两种语言在编程理念上的细微差异。
基本数据类型
C++有7种基本数据类型,外加4个类型修饰符;Java有8种基本数据类型,外加引用类型;
- C++的数据类型分为有符号和无符号两种,Java只有有符号的数值类型;
- C++的某些数值类型的宽度在不同机器上是不一样的(如long int),Java的各种数值类型的宽度是固定的;
泛型技术的原理
Java和C++编码都支持泛型的概念,不过在C++中称为模板而不叫泛型,其实在了解C++和Java支持泛型的原理后就能理解为什么在C++叫做模板而不是泛型了。
- C++的模板是定义了一个类或函数的模板,在编译阶段会根据这个模板生成一个或多个同名的重载函数,至于具体生成多少个同名函数要根据调用方来确定,调用方的入参类型每多种,编译时就会多生成一个形参列表不一样的同名函数。
这也解释了为什么模板类或模板函数必须定义在头文件里,而且多次包含头文件相当于多次定义了类或函数,但编译期间不会报重复定义的错误,因为编译后就已经不是同一个函数了。
- Java泛型用的是类型擦除技术,泛型类内部操作数据时会擦除入参对象的类型,把对象当做Object来操作。
所以基本数据类型不能作为泛型的类型参数,而必须要用对应的封装类型。