Zenghui Bao's World

about life, programming, misc thoughts.

Java初学感想

最近花了一个月时间学习了Java,看了一些书,熟悉了一些工具,写了一些代码,算入门了。由于之前一直做C/C++开发,在学习Java的时候总会不自觉地思考 与C++有什么异同,这篇文章算是我的思考笔记吧。

  1. 编译、运行模型不同。
    Java:先将java代码(.java)编译成字节码(.class),然后由Jvm将字节码载入到内存中解释执行。其中,编译的时候是静态编译的,会有严格的语法检查,而执行的的时候是将字节码一句一句边翻译边执行。
    C++:将C++代码(.h,.cpp)编译成平台相关的二进制文件,然后由操作系统的加载器将二进制文件载入到内存执行。
    可以看出,Java利用中间 平台无关的字节码,达到到跨平台的功能。但是Jvm在加载字节码的时候,需要边翻译边执行,执行效率比C++低。

  2. Java中没有析构函数,而C++中有,导致资源管理的方式不同。
    C++可通过构造函数、析构函数 实现RAII,将相似资源的分配、释放封装到一个类中,使得资源的生命期容易控制,代码很清晰。而Java没有析构函数,当申请了一些资源(文件,数据库连接,socket)后,需要显式关闭、释放资源。
    注: Java可以在类中覆写finalize()方法,使得在GC在回收对象时 执行指定的释放操作。但是由于GC回收对象时间的不确定性,很少用这个特性来实现RAII。

  3. 管理内存的方式不同。
    Java中有GC(Garbage Collection),在堆中申请了一个对象后,不需要释放,GC会自动回收。而C++需要显示释放内存。一般来说,对于生命期比较模糊的对象,C++需要使用智能指针(boost::shared_ptr、boost::weak_ptr)来管理new出来的对象,由智能指针来自动释放内存。比如,在网络库中,TcpConnection是一个生命期模糊(网络库、用户都会使用,不知道什么时候用完、什么时候该释放)的对象,所以网络库对外提供shared_ptr<TcpConnection>,而不是TcpConnection

  4. 申请对象的方式不同。
    Java除了8种原生类型(boolean,byte,short,char,int,float,long,double)的对象是在栈上分配,其余类型(String、数组、自定义类型)都是在堆中分配。而C++可以灵活控制 任意类型的对象 是在堆上分配、还是在栈上分配。一般来说,对象的分配都应该在栈中(除了占大块内存的对象),栈上分配的速度快,程序就运行得快。
    注:Java的编码惯例是 提倡所有对象(8种基本类型都有对应的封装类)都从堆中分配,由GC来管理对象的生命期。但如果程序中要频繁分配、释放某类对象,会对GC造成巨大的压力。Java有内存池、对象池之类的东西吗?有什么好的编程实践吗?

  5. 用接口表示对外的功能,而C++中是用头文件。
    在实践中,Java一般用一个接口类封装对外的操作,它的实现类来封装复杂的实现逻辑。而C++用头文件来提供对外的接口,源文件来实现接口(除了模板类必须将接口、实现都放到同一个头文件中)。
    Java的库一般要借助继承、多态来提供 用户自定义的接口(比如线程库中 必须要继承Thread类、覆写run()方法来定义线程执行方法),而C++可以借助boost::function, boost::bind、函数指针的技术,使得用户的自定义更加灵活,且 函数指针是轻绑定的,逻辑直接,代码清晰。

  6. 错误处理。
    Java中 如果一个函数出错了,一般抛出一个异常来表示错误,函数调用方强制必须处理异常。C++中也提供异常,但可以不使用。异常的优缺点暂且不论,但是若是在程序段中调用的多个函数都会抛出异常,那程序会出现多个try{...}catch{...}块,程序会显得冗长不堪。

  7. 指针。
    Java中没有指针的概念,只有对象引用。C++中提供指针、引用、对象三种。从语义上说,Java的对象就是C++中的指针,Java中的参数传递都是 地址传递,对象赋值只是赋地址。所以编码时要小心:函数内不要修改 传入参数的对象值,函数返回的对象不要修改。对象的深拷贝 要用new调用拷贝构造函数分配一个相同的对象。

  8. 处理依赖、引用外部类的方式不同。
    C++为了兼容C语言,使用古老的头文件来引用外部类。头文件机制 使得多余的函数、类也会引入,导致不相关的代码依赖。在实践中 经常因为依赖的问题 编译报错:某个依赖的头文件不对,却找不到是哪里引用的这个头文件,是使用头文件的哪几个功能?
    而Java引入了import、package机制 完美得解决了这个问题:程序中要使用哪个类,就直接import这个类,代码很清晰,依赖关系很明确。
    在C++中经常要处理一个头疼的问题:程序链接的某个库的版本不对、或者 与库的编译参数不匹配,导致程序编译失败。Java的package、编译成中间代码机制解决了这个问题,将库文件编译打成一个jar包,由于这个jar包是跨平台的,外部import这个jar库时,就不会有问题。

  9. 编程范式不同。
    Java提供范型、面向对象 两种编程范式,但是在实践中主要是用面向对象。面向对象的核心思想是对象的封装、继承、运行时多态,继承是强绑定的,类继承体系建成后,一般很难再修改 其中的某条继承线(因为很多现有代码依赖了这些继承线)。而且,由于完全用面向对象,有时候只需要一两个函数,还得封装个类,导致程序代码 叠床架屋、难以理解。而C++提供了面向过程、范型、面向对象 三种编程范式(在实践中主要用面向过程、面向对象),可以组合最简单的技术(封装、面向过程、namespace),使得类实现得很平坦化,继承层级少,阅读得负担少,代码也清晰。而且由于代码逻辑更直接,执行效率更高。

  10. 部署方式不同。
    Java的部署很简单,只需要将生成的war包扔到tomcat中执行即可(maven会将依赖的程序文件编译打包),比较方便。而C++中要布署动态库、二进制文件、配制文件等,比较麻烦。

  11. 定义标准的厂商不同。
    为什么要关注制定语言标准的厂商?因为 标准的厂商 决定了 语言进化的方向,进化的速度和质量、应用领域。Java是由Oracle掌控的,由于个体是公司,它可以根据市场变化,快速优化性能、引入市场需要的功能、新的标准库。但是如果这个公司倒闭了,语言的发展会比较困难。C++由ISO C++委员会组织制定标准,各个编译器厂商跟进实现标准,然后用户才能使用它。标准的制定流程繁杂,各家编译器厂商实现标准的程度不同,市场上相关的知识培训、资料也要慢慢普及,所以C++引入新功能 的速度 比 Java 慢。

  12. Java标准(jdk)提供的类库很丰富,开源的库、项目非常多,工具(intelliJ, maven)很丰富。
    估计Java是所有语言中 开源项目最多、社区最活跃的一个。各种编程工具、打包工具、测试工具、监控、布署等,只需要在Github上逛一圈,基本上就能找到自己想要的。而且开源的编程框架非常多,程序员写代码基本上是在搭好的框架里 填填业务逻辑,很简单、方便。


总体来说,Java是个不错的语言。引入了先进的特性、社区活跃、开源的库多、工具丰富,它适合快速构建 上层的应用系统,不适合高性能的应用。

PS:我对Java的代码风格不是太喜欢(层级太多、看不清本质),或许我可以找到一个适合自己的Java编码风格。