前言
其实,一直很想研究一下这样一个命题。但内心一直在怀疑自己是否有把握把这篇文章写好。不管怎样,我今天终于下决心开始这样一个命题。我会持续更新这篇文章,以保持内容的准确性,丰富性和完整性。
Why
可能很多人会疑问,为什么要写这样一篇文章呢?为什么要对比这三门语言呢?
大家都知道,Java/OC/C++都具有面向对象的特性,Java由C++改进而来,Java语言其实可以称之为C++--。因为Java语言其实是在C++的基础上去掉了很多特性。使之成为了一个纯粹的面向对象的语言。而C++语言则是在C语言的基础上添加了面向对象特性改进而来,早期的C++语言还被称之为C With Classes。OC语言和C++一样,也是在C语言的基础上添加了面向对象的特性。从这个角度来看,其实这三种语言具有共同的祖先C语言。并且他们又都具有面向对象的特性。所以,这三门语言的设计难免有很多相似之处,或者说很多值得探讨,非常有趣的地方。这篇文章,我们将从几个不同的角度来对比这三门语言,
历史
1995年,由James Gosling带领的Green团队正式发布了全新的编程语言Java
1980年,Brad Cox发明了新的编程语言Objective-C,最初只是作为C语言的简单扩展
1979年,Bjarne Stroustrup还在上博士期间,就产生了开发一门面向对象语言的想法,也就是现在大名鼎鼎的C++
从语言的历史来看,Java语言的诞生相对OC/C++晚了10年多,而OC,C++几乎是同时代的作品。这也导致了Java语言吸取了很多前期面向对象语言优点。同时,规避了前期面向对象语言的很多缺点。这也是造就了后面Java语言盛况空前的原因之一。
语法
虽然三门语言都源自于C语言,但语法却有着很大的不同。
方法调用
Java语言使用点语法调用方法
C++调用方法需要分不同的情况,如果是对象或者引用,则使用点语法调用。而如果是指针,则使用->调用,从某种层面来说,这也增加了程序员的学习成本,容易给新手产生很多迷惑。
OC的方法调用最特别,使用中括号的方式实现方法的调用。其实,OC通常不把这个叫做方法调用,而称之为给对象发送消息。
从对方法调用的设计上面来看,Java/C++语言设计的相对较为保守,却更容易被人接受,也更容易理解。而OC的设计却颇为前卫,这也导致了后来一批程序员不愿接触这门语言,原因仅仅是因为语法太过奇葩。
Java
public class Bird {
public void fly() {
System.out.println("I'm a bird,I can fly...");
}
public static void main(String[] args) {
Bird d = new Bird();
d.fly();
}
}
OC
Bird.h
#import <Foundation/Foundation.h>
@interface Bird : NSObject
- (void)fly;
@end
Bird.m
@implementation Bird
- (void)fly {
NSLog(@"I'm a bird,I can fly...");
}
@end
调用
Bird *bird = [[Bird alloc] init];
[bird fly];
C++
Bird.hpp
#ifndef Bird_hpp
#define Bird_hpp
#include <stdio.h>
#include <iostream>
class Bird {
public:
Bird();
~Bird();
void fly();
};
#endif
Bird.cpp
#include "Bird.hpp"
Bird::Bird() {}
Bird::~Bird() {}
void Bird::fly() {
std::cout << "I'm a bird,I can fly..." << std::endl;
}
调用
Bird b;
b.fly();
Bird *b = new Bird;
b->fly();
语法特性
OC/C++同时支持面向过程编程,语言设计相对而言更加灵活。但却增加了极大的学习成本。
Java语言是一门纯粹的面向对象的编程语言,使用相对较为简单。
使用一个简单的表格来对比一下三种语言的一些基本特性支持情况,主要对比三者支持不同的情况。
语言特性 | Java | OC | C++ |
---|---|---|---|
接口 | 支持 | 支持 | 不支持 |
泛型 | 支持 | (支持)Xcode7以前不支持 | 支持 |
运算符重载 | 不支持 | 不支持 | 支持 |
函数重载 | 支持 | 不支持 | 支持 |
多重继承 | 不支持 | 不支持 | 支持 |
在这一轮PK上面,Java语言的语法相对而言,最为简单,但支持的语法特性也最少。C++支持的语言特性较多,难度最大,但灵活性最高。OC位于二者中间。这一轮的PK上,可以认为三者齐平。
学习难度
说到学习难度,毫无疑问,三门语言中难度最大的,非C++莫属。
C++为了增加语言的灵活度,增加了很多其它两门语言没有的特性,例如:运算符重载,模板,函数指针等等,这些特性使用起来都比较麻烦,极大地增加了新手的学习成本。从另一方面来讲,C++有三种内存模型:对象,引用和指针。这也会让新手感到非常迷惑。我曾经在学习这部分知识的时候就非常迷惑。正是由于C++语言设计的复杂,也导致了后来的编译器开发难度很大,导致了大名鼎鼎的GCC编译器体积一再扩大,但支持的依然不是很完美。
OC和Java对比来看的话,我认为OC语言的难度要稍大一些,OC语言自开发出来以后,基本上一直都是苹果公司在支持和完善。到目前版本来说,已经非常完善了。现代OC语言在传统面向对象语言的基础上增加很多特性,例如:Category,Extension,消息转发,方法替换等等。同时,OC还保留它的祖先的部分特性。这些都增加了学习成本。
Java语言相对而言,学习成本是最低的,因为它只有一种内存模型。所有的调用都是通过指针实现的,你甚至都感觉不到指针的存在。
从学习难度的对比来看,Java语言的学习难度最低,最优。OC语言其次,C++学习难度最高,最次。
面向对象特性
在这一轮的PK上,OC/C++语言同时支持面向过程编程,这将不作为我们讨论的内容。
三门语言在面向对象的支持上,大体相同,但略有差异:
Java/OC有接口的概念,Java的接口关键字是interface,接口里面所有的方法都必须实现。
OC的接口关键字是protocol(OC里面其实称之为协议,这里简单起见,统一叫做接口),OC接口里面的方法不一定要全部实现,取决于你的设置。
从接口的设计对比来看,OC稍微好一些。
C++没有接口的概念,但使用纯虚函数实现类似接口的概念。
C++/Java语言支持函数的重载,而OC不支持函数重载,这或多或少算是一种遗憾。
在这一轮的PK中,三门语言的设计不相上下。
性能
在这一轮的PK上,毫无疑问,Java语言的性能是最差的。
对于OC/C++,没有确切的数据说明谁的性能更好。这两者都是基于C语言进行了面向对象的扩展。但是,OC的Runtime可能会消耗一些性能。所以,可能OC的性能会稍弱于C++
在这一轮的PK中,Java最次,OC/C++持平。
文档支持
在自动生成API文档方面,Java语言的JDK自带了文档生成工具,生成的文档非常漂亮,美观。
而苹果官方也为OC提供了一个非常棒的文档生成工具,生成的文档同样非常漂亮。
而C++并没有官方支持的文档生成工具,它需要依赖于第三方的文档生成Doxygen来生成文档。
在这一轮的PK中,Java/OC持平,C++最次
IDE
从IDE的数量上来说,支持C++/Java的IDE较多。
但从IDE调试功能上来说,Java最优,Java IDE都支持变量的预执行,甚至替换执行等等。
而OC/C++ IDE都需要命令行操作才能实现。
从IDE的智能补全来说,其实也毫无疑问,Java IDE支持的最好。
IDE辅助代码检查,修正,错误提示等,这一点上来说,依然是Java IDE支持的最好。其次是OC IDE,最后才是C++。
在这一轮的PK中,Java最优,OC其次,C++最次
可移植性
从可移植性来说,毫无疑问,Java的跨平台性是做的的最好的。Java的口号是:一次编译,到处运行。
而OC/C++需要针对不同的平台编写不同的代码,才能保证在不同的平台上面稳定地运行。
垃圾回收
编程的同学都知道,垃圾回收是编程过程中一件繁琐且容易出错的事情。
在三门语言对垃圾回收的支持上,Java语言有非常成熟的垃圾回收机制,几乎不需要程序员显式处理。
OC有ARC(自动引用计数器),通过引用计数器,也可以较好地实现垃圾回收,并且保证了程序的高效性。但是,如果处理不当,依然有可能出现内存泄露。从设计上来说,并没有Java垃圾回收器智能。
C++不支持垃圾回收,学习C++编程的同学必须牢记的一句话就是:谁创建,谁释放。
在这一轮的PK中,Java的垃圾回收器设计最为智能,但是效率不及OC的ARC。所以,可以认为二者水平相当。而C++不支持自动垃圾回收,最次。
新特性
在语法一节,我已经谈到OC语言的设计最为前卫,而Java/C++语言的设计最为保守。
OC语言中的很多新特性让我非常喜欢。
- 使用
@property
可以自动生成set/get方法。同时,还可以给set/get方法增加同步,ARC等设置,这极大地简化了代码编写的工作量,减少了很多重复的工作量。 - OC的Category支持对已有类进行扩展,这可以减少很多冗余的继承类,保证了代码的简洁性。同时,Category并不会破坏原有类的整体一致性。
- OC的Runtime可以实现轻松地在已有类的方法中插入新的逻辑代码,甚至可以实现方法的替换等等,所谓的面向切面编程(AOP)使用OC语言实现起来最为便捷。
- OC的Runtime设计基本可以摆脱try...catch,虽然这有利有弊,但确实保证了代码的整洁一致性。
而对于Java,实现特性1要依赖第三方组件,并且实现的没有OC的那么彻底,特性2 Java没有办法实现。实现AOP,Java语言要依赖自己的反射调用,语法较为繁琐,实现难度较大。
相对于C++,上面的所有特性它几乎都不具备,属于一门依然相对较为原始的语言。
总结
C++为了兼容C语言,同时为了提供尽可能高的灵活性,已经使之成为了最难学习的编程语言。同时,由于设计时需要考虑的因素太多,程序员学习的成本太高,导致其背上了陷阱太多的骂名。
最新的C++17标准正在设计当中,依然有一些新的特性加入到C++标准当中。当然,设计人员也开始考虑到C++的一些编程规范和约束的制定。我认为,C++似乎不应该考虑太多的加法,在这个编程语言百花齐放的时代,简单应该是编程语言追求的目标,而不是一味地追求过多的特性,过多冗余的设计,希望C++设计委员会也能够认为到这一点,给C++适当地做做减法。
Java语言在一开始发布的时候,其运行性能受到的极大的质疑,人们纷纷拿它和C语言对比,其结果也是让人大跌眼镜。而经过这么多年的发展,Java语言的性能有了长足的进步。虽然,与C语言还有很大的差距,但它的确在进步。而在吸取其它新语言如Python等语言的新特性上面,也有了一定的进步,希望Java语言未来能够越来越好。
OC语言从发布之初,到如今的最新版本,已经发生了很大的变化。前面的新特性*一节也讲到OC语言的很多新特性让人非常惊喜和赞叹。但不得不吐槽OC的时,无数的@符号实在让人抓狂,还有错误提示稍微有些薄弱。
PS:虽然进行了这么多的对比,但我要说,语言无好坏。真正的程序员,应该是不区分语言的。即所谓的无招胜有招的状态。所以,不要被这篇文章误导,我只是站在三门语言对程序员以及系统构建的友好程度上做了一个简单的对比,希望它不要破坏你对自己喜欢语言的热爱。
参考文档
Oracle
Objective-C History
Cplusplus History
如果你喜欢这篇文章,请到Fork我的github仓库:
https://github.com/yuanhoujun/jianshu.git
如果你对这篇文章有任何的修改建议,请给我发送Pull Request。如果你想给我留言,可以加我的QQ:626306805,如果你想和更多的人一起讨论iOS,请加入iOS交流群:468167089
网友评论
其次面向对象特性,OC 的接口因为苹果爸爸不怎么使用,接口在苹果开发中并没普及起来,仅仅在委托模式中普及了,面向对象的程度并不高。
而性能,GC 比 ARC 慢吗?在非 GCing 的时候,GC 真的就是什么都不做(有些可能也会做点事但肯定比每个赋值都要更改计数少很多),所以肯定是要比 ARC 快的,慢的只是正在 GCing 的那一段时间。除了程序出现内存抖动而导致频繁的 GC 可能会耗时较久,但 GC 问题还是可以通过使用复用对象去优化。