微型机与应用
WEIXINGJI YU YINGYONG
1999年3月 第18卷 第3期 vol.18 No.3



Java战胜C/C++的奥秘
―从语言和实现机制角度剖析Java与C/C++
徐鹏　王克宏
　　摘　要：针对Java语言与C/C++语言在功能、安全性、可移植性等方面各自不同的特点进行技术分析，同时展望Java语言的发展前景。
　　关键词：Java语言　C/C++语言　Java虚拟机　即时编译器
1　从C、C++语言到Java的发展
　　C程序设计语言自推出以来就一直十分流行。它的流行应当归功于一些非常具有使用价值的特性(例如针对计算机硬件的特定数据类型、程序小型化以及易于掌握等)、其标准的运行时类库(其中包括了用于输入/输出、存储定位、字符串操作等功能)以及数量庞大的开发工具集(包括UNIX操作系统在内的很多操作系统上都具有很多C语言开发工具)。
　　随着计算机应用程序的复杂化，C++语言出现了，C++语言主要用于设计大型的应用程序，同时由于充分结合了面向对象的技术，使利用这种语言设计出的程序增加了可读性和可维护性。由于C++语言过多地顾及了与C语言的兼容性，因此导致这种新语言自身存在很多问题。其中典型的问题包括：指针运算、缺少数组索引、缺少数组越界检测、函数参数的数量可变以及对字母的大小写不进行检测等。
　　Java是一种新型的面向对象的程序设计语言，与C和C++语言相比起来，它更加易于学习和使用。Java扬弃了C和C++语言中的一些复杂结构，增添了可移植性和安全性等功能，Java应用程序(包括Applet程序)可以在各种不同的网络环境中运行。Java之所以能够在短时间内就战胜C++，成为众多程序员首选的面向对象程序设计语言，这与其自身的特性是密不可分的。
2　Java语言与C、C++语言的比较
　　在变量声明、参数传递、操作符、控制语句等方面，Java语言使用了和C、C++相同的风格，使得那些熟悉C、C++语言的程序员可以很方便地利用这种新型的程序设计语言进行编程工作。同时Java为了实现其简单、鲁棒、安全等特性，也摒弃了C和C++语言中许多不合理的内容。
2.1　语言特性
　　1.指针类型和算术运算。Java语言不支持C/C++中的指针，因为由于指针所进行的内存地址操作常常会造成不可预知的错误，同时通过指针对某个内存地址进行显式类型转换后，可以访问1个C++中的私有成员，从而破坏系统的安全性，造成系统的崩溃。Java对指针进行完全的控制，程序员不能够直接进行任何指针操作，例如把整数转化为指针，或者通过指针释放某一内存地址等。
　　2.头文件。在C和C++语言中用头文件来声明类的原型以及全局变量、库函数等，在较大型的系统中，维护这些头文件是很困难的。Java不支持头文件，类成员的类型和访问权限都封装在1个类中，运行时系统对访问进行控制，防止对私有成员的操作。同时，Java中用import语句来与其他类进行通信，以使用它们的方法。
　　3.结构、联合和函数。C、C++中的结构(Structure)和联合(Union)中所有成员均为公有，这样就带来了安全性问题。Java中不包含结构和联合，所有的内容都封装在类中。同时Java语言通过类中的方法来实现C和C++语言中函数所实现的功能。
　　4.GOTO语句。Java不支持C/C++中的GOTO语句，而是通过例外处理语句try、catch、finally等来代替C/C++中遇到错误时用goto来进行跳转的情况，使程序更具有可读性，并且更加结构化。
　　5.多继承。C++语言支持多重继承，多重继承指1个类可以同时成为多个类的子类，它使得类的层次关系不清楚，而且当多个父类同时拥有相同的成员变量和方法时，子类的行为并不容易确定，这就给编程工作带来了困难。然而Java语言只支持单一继承，也就是说每个Java类只能够具有1个父类。单一则清楚地表明了类的层次关系，指明子类和父类各自的行为。但是Java语言又提供了1个“接口”类型，接口把方法的定义和类的层次区分开来，通过它可以在运行时动态地定位所调用的方法，同时接口实现了“多重继承”，且1个类可以实现多个接口。正是这些机制使接口提供了比多重继承更加简单、灵活且强劲的功能。
　　6.类型转换。在C、C++中，可以通过指针进行任意的类型转换，这常常会带来不安全性，而在Java中，运行时系统对对象的处理要进行类型相容性检查，以防止不安全的转换。
　　7.浮点运算。Java语言在浮点运算的精确度上存在缺陷。目前计算机的处理器可以产生精确度很高的浮点数，但是Java只要求处理器返回与其所产生数据相比精确度较低的中介结果。为了满足Java规范，必须放弃对精确度的特殊要求。这不但导致了这种语言缺少精确的浮点运算，而且也直接影响到了Java程序的执行性能，而相同的问题在C和C++语言中则不存在。
　　8.数组越界检测。在Java语言中，数组大小为负数或者程序运行过程中数组越界都作为运行时例外来处理，而在C或C++语言中没有提供对这种错误的检测功能。
　　9.字符串。Java语言的字符串是作为1个字符串类的实例出现的。与C语言的字符串不同，Java字符串是可变的。程序员可以通过使用StringBuffers类作为1个Java字符串对象，这样就可以任意修改这个字符串了。
　　10.多线程技术。Java语言支持多线程操作，而在C/C++语言规范中并不包含多线程的内容。另外，通过在程序中运用多线程技术，使得这些程序与单一线程的程序比起来性能有所提高。
　　11.内存管理与垃圾回收机制。在C语言中，程序员通过库函数malloc()和free()来分配和释放内存，C++中则通过运算符new和delete来分配和释放内存。再次释放已经释放过的内存块或者未被分配的内存块都会造成系统的崩溃；同样，如果程序员忘记释放不再使用的内存块也会逐渐耗尽系统资源。而在Java语言中，所有的数据结构都是对象，通过运算符new为它们分配内存堆。通过new得到对象的处理权，实际分配给对象的内存可能随着程序运行而改变，Java语言对此自动地进行管理并且进行垃圾收集操作，有效地防止了由于程序员的误操作而导致的错误，并且更好地利用了系统资源。
　　12.网络功能。Java语言支持网络操作，通过URL类可以从网络上读取数据，同时还可以向服务器端的CGI程序发送数据。Java还隐藏了socket接口，同时它使得客户/服务器应用程序更加便于开发。而这些功能在C和C++语言中都没有提供。
2.2开发和执行环境
　　Java语言及其技术得到了飞速的发展，这与业界巨大的推动是分不开的。由于Java语言还是1个相对新生的事物，所以开发工作所需的工具还不很成熟，这也是可以理解的。我们认为判断Java推广程度的最好例证之一就是Java RAD公司的发展，随着Java RAD工具逐渐走向成熟，这种语言将对客户-服务器形成挑战。
　　运行性能仍旧是Java执行环境的1个重要问题，这与C和C++语言是完全不同的。Java应用程序是通过解释执行，而不是直接通过编译执行的，并像C/C++程序那样与本机代码进行链接。
　　Java应用程序可以首先被编译成字节代码，之后通过网络以本地或者远程调用方式由Java虚拟机来解释。Java字节代码的规模很小，然而，由于每次调用都需要对相同的代码进行解释，而C和C++程序都是在本地执行的，所以这造成了Java与C/C++相比在执行速度上相对缓慢。
　　即时编译器JIT(Just-In-Time)将字节代码翻译成可以动态执行的本机代码，由于这种翻译操作在整个过程中只进行一次，而不是每次调用都需要执行，因此它提高了程序的性能。JIT编译器可以较好地提高Java程序的远程调用执行速度，但是不足之处在于它执行本地代码时的速度并不理想。而本地Java编译器可望填补这个空缺。
2.3　可移植性
　　当用户将C/C++程序移植到另外一些机器上执行时，这些程序往往会出现一些问题。例如，这些程序不具备用于分配原始数据类型的一些标准。当进行原始类型转换时，C和C++语言不能够指定正确的结果。而利用Java语言设计出的应用程序可以在任何1台具有1个Java虚拟机的计算机上运行。Java的口号“一次编写，到处运行”就是指导方针。
　　与C和C++程序比起来，Java程序更加易于进行移植。然而，虽然在各种不同的平台下都实现了Java运行环境，但是这个环境也具有针对特定主机的模块。这样，一名程序员怎么能够保证在不需要于不同平台上对他们所设计的应用程序进行测试的前提下就可以保证这些程序在这些平台上完成相同的操作呢?例如，线程的排序处理就要依赖于主机的操作系统，这样就造成了1个多线程的Java应用程序在不同的计算机环境下可能进行完全不同的操作。因此，1个“100%纯Java”程序仍旧应当在不同的平台上进行测试，这样才能够保证其运行质量。
2.4　性能
　　与Java语言相比，C和C++语言更加成熟。目前，许多软件开发商都在各自的Java应用程序中使用了一定数量的本地代码，这些公司包括IBM、Netscape、Microsoft、Dimension X、RandomNoise和NetDynamics等。
　　在进行程序开发的过程中，许多开发人员都发现单纯地使用Java语言来编写其应用程序十分困难，其原因是多方面的，一个最普遍的原因是运行性能。由于Java代码是解释执行的，因此其执行速度比起C代码来要慢很多。即时编译器JIT的出现缓解了这种情况。而Java本地编译器可望成为三者中性能最佳的编译器(这种编译器的运行速度与另外两种编译器相比分别提高50倍和4～7倍)。
　　总之，Java语言并没有提供像C/C++语言中注册关键字或汇编程序那样的标准挂接程序来实现程序优化，它不支持那些协助代码生成工作的内联本地汇编代码。在Java语言的设计思想中，简便性和可移植性比起运行性能来是更加重要的因素。Java程序更加依靠编译器和运行时环境来改善运行性能。
　　根据一个独立的研究机构Market Decisions公司的调查表明，有96%的Java开发人员是在Windows平台上开发并使用Java应用程序的。这个数字表明大多数Java应用程序是在Intel体系结构的平台上运行的。随着Intel系统处理器主频的不断提高，Java程序的解释执行速度也会间接得到提高。
　　当对Java语言的内部机制进行考察后，就会发现每次方法调用都必须搜索其方法表，Java程序在方法调用方面的开销要高于C语言，接近于C++语言，而它在错误校验方面比起后2种语言来要耗费更多的运行时资源。因此有理由得出这样一个结论，在具有1个优秀的本地Java编译器的条件下，Java程序的运行性能能够接近于C++程序。 
3　结论
　　Java是一种与C++类似，但更加简单的面向对象程序设计语言。对于熟悉C和C++的程序设计人员来说应当很容易掌握Java语言，因为这3种语言具有相似的语言结构。然而Java程序剔除了在C++语言中一些复杂的功能，并增加了错误校验、垃圾收集和多线程支持等功能，因此Java语言相对更加健壮。通过内置在Java语言中的安全特性，应用程序可以在具有1个Java虚拟机的不同网络环境中自由地被解释运行。
　　目前，Java开发环境的成熟程度还无法与本地C/C++开发环境相比，Java语言的解释代码运行速度还相对较慢。即时编译器和Java本地编译器正在不断改善，但是目前还没有证据表明它能够超过本地C和C++编译器的速度。
　　Java开发人员必需对Java所提供的功能有一个正确的认识。Java所提供的并不仅仅是一种传统意义上的编程语言，它还是一个由虚拟机和网络化环境(包括Internet)组成的完整的环境。为了成为一种主流语言，Java必定需要具备一个完整的功能集，同时应当易于使用、可靠且节省费用。Java语言虽然已经取得了前所未有的成功，但是其自身仍然存在许多需要改进之处，前进的道路还很漫长。 
作者单位：清华大学计算机系(100084)
参考文献
　1　Harbison S P,Steele G L.C-A Reference Manual.Tartan Laboratories,1996
　2　Stroubtrup B.The C++program Language.AT&T Bell Laboratories,1996.
　3　O'Connell M.Java:The inside story.Sun World Online,1997
　4　Gosling J,Joy B,Steele G.The Java Language Specification.Addison-Wesley,1996
　5　Gosling J,McGiltion H.The Java Language Environment,A White Paper.Sun Microsystems Computer Company,1996
　6　Ricciuti M.Java hype aside,C++still rules.C/Net,1997
　7　100% Pure Java watered down,http://www.news.com/News/item,1997.5
　8　Volkmann M.Java-What's All The Excitement About?.Object Computing Inc,1998
　9　Shavlor N.JCC-A Java to C converter.Java Developer Inc,1997.
　10　IBM High performance Compiler for Java:An Optimizing Native Code Compiler for Java Application,white paper.IBM Corp,1998
　11　Jain P，Schmidt D C.Experiences Converting a C++ Communication Software Framework to Java.Java Developer Inc,1998
(收稿日期：1998-08-29)
