当前位置:Linux教程 - Linux - 反驳java无用论

反驳java无用论



        

    转自“万千新闻组-java"
    ==============================
    尽管 java 宣称它是100%面向对象,而C++不是。但是宣称100%面向 对象本身就不是什么面向对象的思想。面向对象的本质目的是提高生产率。讨论一种语言是否100%面向对象就好像讨论我们的社会性资性> 社一样滑稽。其实学习面向对象不一定非要使用java不可。我反而认 为120%面向对象的C++是不错的选择。面向对象的本质是一种认识世界的方法,提高生产率只是它的结果。JAVA是纯面向对象的语言,它强迫你使用面向对象的思想来考虑问题。用C++就可能写出C和C++混合的代码(许多C/C++程序员不知不觉中就这样做了)。

    Java号称跨平台,其实这是只一个梦想,也不妨用骗局这个词来形容 。软件的移植性仅仅和虚机相关吗?照此理论是不是可以说符合IA32体系的软件在任何IA32体系上都能正常运行。那我怎么没看见SCO: :lf 在 NT上运行?显然,除了虚机,软件二进制规范(执行文件格式、连接方法)、运行时间库和系统调用也是软件移植问题因素。而 java规范中显然不包括完整的运行时间库和系统调用,甚至二进制规范也有不兼容的地方(比如本原接口和固有接口)。软件总不能脱离它的运行环境,不兼容就从这里开始。 要提高软件生产效率。应当认认真真的研究系统结构、二进制规范、系统调用结构才是解决问题的方法。靠概念游戏这种投机取巧的方法是行不通的。请注意,每种语言都有它的适用范围。100%纯JAVA不能使用系统调用(不使用JNI),所以JAVA带有庞大的类库。写JAVA程序时要考虑JAVA是否能实现,不能的话就不要用。

    2. Java究竟是什么?
    Java是一种虚拟硬件系统结构,是一种软件二进制规范,是一种编程语言。当我们看到Java一词时,它可能表达上述三个含义钟的任何一个。尽管这三个东西可以独立存在,不过实际上他们总是在一起出现 。所以也很少有人分的那么明确。使用Fortran语言编写Java虚机上的代码理论上是可行的。但是有谁乐意做这件事呢?JavaVM是虚拟硬件系统结构,JAVA编译后的代码是一种软件二进制规范(也就是一次编译,到处运行),JAVA本身是编程语言。

    作为虚拟硬件体系结构,Java虚机和ALPHA、PowerPC、SPARC、IA32、51、960是同一概念上的东西。也许您会奇怪?他们怎是同样的东西呢?IA32不就是PIII、PIV那一大块吗?其实不然、我们知道386、P5、P6、P7的内部结构非常不同。属不同的微体系。但是他们实现了相同的IA32指令集合。IA32也不一定非要通过硬件实现,通过软件也可以。这种软件CPU产品广泛出现在操作系统和CPU密集软件的开发、调试工具上。主要用于在CPU出现之前调试软件。Java指令集同样也可以通过硬件实现。只是在半导体工业激烈竞争的时代,没有大公司(含SUN)愿意把这么做。SUN公司已经推出了这样的产品,但的确离它当初承诺的差了很多。SUN公司原先是把JAVA作为一种桌面操作系统的编程语言推出的,和网络计算机配套,所以急需硬件支持。但现在JAVA的应用范围更宽了,所以这种需求就不是很强烈了。

    SUN常常说M$很害怕java。他们之间的确存在很强的竞争关系。倒不是Java高级语言的问题,我认为是Java二进制规范和 Windows操作系统存在竞争。不妨考察Java中的(.class文件格式、装入方法、执行过程、JDBC接口、J3D接口)和Windows中的(PE文件格式、DLL文件格式、装入、连接方法、ODBC、Direct3D)。就知道竞争多么强烈。和NT/IA不同。有竞争不好吗?大家不会希望MS一统天下吧。

    Java.exe执行程序包含虚机规范和二进制规范。在NT/IA中是分离的产品。请注意,JAVA虚拟机规范和二进制代码规范只有一种,不同的JDK是它在各个软件/硬件平台的实现。这也是面向对象设计的一个重要原则:接口和实现分离。

    人们总喜欢把Java高级语言和C++类比,他们之间的确存在很过相似的地方。不过我认为C++和Java不存在什么竞争。它们使用在不同的场合。他们的区别(内存管理、对象类型、线程规范)决定他们的用途。用形似而神不似来形容C++和Java吧。仅从高级语言方面来看。Java真正竞争来自ECMAScript 。也就是JScript和VBScript。对,每种编程语言都有它的适用范围。我个人认为,一种编程语言的流行与否,主要在于程序员是否喜欢它。另外,C/C++并没有线程规范,线程的实现取决于系统调用,这也是C/C++程序移植的一个问题。

    3. Java虚机和IA32比较
    Java VM和IA32最大的区别是Java VM的主要实现不考虑效率问题。由此派生出寄存器少,指令结构简单(同样功能生成的程序代码长),寻址空间小,寻址方法少、数据类型少等。除了上述问题,Java VM没有涉及指令(显式或隐式)并行问题。
    3.1. 基本特性差别
    3.1.1. 数据类型比较
    Java VM
    IA32
    整数类型
    byte 
    short(16bit) char(UnionCode)
    int 
    long (64bit)
    {不存在无符号算术运算指令,另外注意这里没有boolean类型}
    byte
    Word(16bit)
    DoubleWord(32bit)
    QuotaWord(64bit)
    DoubleQuotaWord(2*64bit)
    BCD/PackedBCD
    BitField
    {存在有符号和无符号两种运算指令} 
    浮点类型
    float(32bit)
    double(64bit)
    float(32bit)
    double(64bit)
    long double(80bit)
    Packed double(2*64bit)
    地址
    returnAddress*
    8bit , 16bit , 32bit , 48bit都有没有显示的看到returnAddress类型的宽度,不过。通过指令goto(0xa7)和goto_w(0xc8)来看。returnAddress是可以是16位或32位的。历史上Intel的CMPXCHG8B(Pentum pro以上CPU提供)指令就存在过类似问题,一度被指责是CPU设计问题。好在CMPXCHG8B很少使用,没有造成什么事故。
    3.1.2. 指令集
    指令类型
    JavaVM
    IA32
    Load/Store
    1) 数据和运算栈之间单个移动
    2) 常数移入运算栈

    1) MOVE
    2) CMOVEXX
    3) 交换,琐操作
    4) PUSH/POP
    5) PUSHA/POPA
    6) IN/OUT
    元算指令
    add , sub , mul , div , neg , rem , shl , or , and , xor比Java多多了,主要多在显式并行上类型转换基本类型之间可以互相转换有(由于类型很多,转换指令也有很多)方法调用4条指令一大堆,主要多在权限切换(调用门)、长短跳传不同上。还是那句话,每种编程语言都有它的适用范围。作为一种新型的面向对象的高级语言,JAVA从来就不准备支持汇编语言。基本类型转换可能带来精度丢失(double to float)或数据错误(byte to long)。这不符合JAVA是一种健壮的编程语言的特性。

    关于double和Long的原子问题:在目前的java规范中,double和long作为两个独立的32 位数据独立存取。这样可能产生同步问题。这的确是一个不小的问题。现在只能用同步的方法来暂时避免。希望SUN公司能在以后的JAVA规范中解决。

    3.1.3. 异常
    虽然JavaVM有异常的支持,不过很简单。它不区分中断、陷阱和故障。也不能明确指出出错代码的PC,并返回那里重新执行。JAVA的确不支持机器指令级的异常处理。但它的异常处理系统要比C++丰富和强壮的多。JAVA有finally关键字,可以用于在出现异常时释放资源(不是内存)。例如网络传输时出现错误,就可以在finally块中关闭SOCKET。C++中缺省只能捕获基本数据类型的异常(除非使用..., 但这样你又怎么知道是谁出了错?),而JAVA可以捕获所有的异常(不知类型的就是UnknownException,嘻嘻)。JAVA中的异常可以抛出方法外,这
    样可以在一个地方集中进行异常处理。JAVA的异常处理可以显示调用堆栈,可以显示程序是在哪一行出错。在JAVA中一个未捕获的异常会引起一部分的功能不能使用,而在C++中会导致程序的退出。

    3.1.4. 执行态
    Java VM没有用户态和系统态执行区分。当然也没有MMU(管理内存分页、分段的内存管理单元).除非被Java高级语言限制,java VM汇编代码也能完成一些恶意操作。对,但这需要恶意和精心的编码。而在C/C++中,一个无意之失的指针就可能导致系统崩溃,这一点C/C++程序员应该深有体会吧。况且JAVA中有专门的安全管理器来进行安全检查。

    3.1.5. 小结
    总而言之,Java VM很简单。SUN总是认为通过编译优化Java可以达到C++速度。显然是谎言。如果要编译成本地代码,显然要丢失移植性。如果不编译成本地代码,效率问题如何解决?有一种及时编译的设计。对于很小的应用也许可行。对于规模的应用呢?要知道优化编译很费CPU和内存。像IA64体系的优化方案,要编译很多遍才能优化完成。要运行时刻编译是不是天方夜谭。

    4. Java高级语言和C++、JScript比较
    4.1. 效率,谎言
    很多书上写Java是很快的语言。这时一定要看清楚是比谁快。语句传了几本书难免有些误差,比较对象经常有意无意丢失。一般认为Java比JScript快。的确快很多,不只十倍。我也承认Java比JScript快。但有人说Java和C++效率接近。这就是骗人了。他们也能做出试验证明,试验结果好像也能证明结论。他们的实验大概都是形如这样。
    Java, C++测试代码:
    for ( I = 0 ; I<100 ; I++ )

    RemoteCall( I );  file://外部调用,调用一次用1s。

    结果:
    Java 102s , C++ 100s。
    结论:
    Java达到了C++ 98.0%的效率。
    这种测试违背了测量学中要把测试对象放到主要矛盾上这一基本原理。方法的不正确当然带来了测试结果的不正确。有本事比比
    for ( I = 0 ; I<10000; I++)
    y = x * z;  file://由Java语言或C++语言实现执行体   
    }
    我的结果是170倍。也就是PIII变成了286。如果使用指令并行优化 ,C++和Java的速度差异可以达到千倍以上。想用一下8088吗?在绝大部分的情况下,JAVA的执行速度不如C/C++。但我认为你的测试方法也并不正确。就好像从PIII500升级到PIII1000不可能使系统性能提高100%。我只想说,经过
    JavaVM的不断优化和JIT、HotSpot的出现,JAVA的性能已经可以满足大部分项目的要求了。

    4.2. 功能缺失导致代码膨胀

    简单的说,Java比C++缺少编译预处理、缺少运算符重载、多继承和类模板。我认为取消这些概念很粗暴。和C++不同。对于大规模编程来说,Java的代码量不会比C少多少。甚至由于类型约束太强。有可能比C的代码量还大。Java常称多继承比较困难,所以要取消。而且使用实现接口的方法可以代替C++的多继承。但是,实现接口要自己写所有的接口实现代码。不嫌烦就慢慢写吧。为什么要取消运算符重载?真是不可理解,既简单又好用。它居然说难,且容易造成混乱。

    1。JAVA不需要预处理。
    1。1 JAVA中不需要包含头文件或其他文件。包含头文件会增加类之间的依赖,并且这种依赖性有传递关 系。JAVA中的import就不存在类似的问题。
    1。2 JAVA中定义常量可以用静态变量实现。
    1。3 JAVA无需使用针对不同环境而使用的条件编译。
    2。运算符的实质是定义在类上的方法,在JAVA中可以通过对类的方法的重载来实现。我想再次重申,JAVA强迫 你用面向对象的思想思考问题。

    3。说到多重继承,C++和JAVA都没有很好的实现。这都是由于继承时没有指出继承的依据所导致的。JAVA的接口的确有你所说的问题,但它在实现对象的多态性上有更灵活的应用。而C++中的多重继承也有冲突的问题。

    4。模板的实现有利有弊,面向对象界对这个问题并没有统一的定论。如果真的要用的话,JAVA现在也有支持模板的编译器。

    5。编写JAVA程序要比C++轻松的多。这是因为JAVA的类库要比C/C++强大的多。下面说些C++所没有的:

    6。RTTI(运行期类型鉴定)。适用于多态性的概念。例如,有一个图形对象,想知道它具体是圆形还是三角形,就可以使用。但不提倡。

    7。单根结构。所有的类都从一个基础类继承。例如,你可以写出一个容器,包含所有类型的对象。也可以使用instantof关键字识别对象类型,写出一个类似C++的带有强制类型检查的容器。

    8。REFLECT(反射)。在分布式系统中特别有用。可以在不知道对象类型的情况下调用对象的方法。

    9。包的概念。包是一些高内聚,低耦合的类的集合。包是比类更高层次的抽象,使用包的概念可以对系统进行更高层次的划分。UML中就引入了包图的概念。

    10。持续化。保持对象的状态以便在以后使用。

    总之,处理这几个大方向的问题,还有很多小问题。据说AWT不好用,感觉他们为了完成任务写程序。据说???

    4.3. 简单的骗局

    自古便宜没好货。Java总是宣扬Java如何简单,主要宣扬的好处是垃圾自动收集和线程同步。其实不然,由于Java没有释放关键字。使得不能及时释放或定时释放不必的数据。要知道能够分配的不只是内存,还有数据库句柄、文件句柄、结果集等。这样不得不绕来绕去的完成这些任务。其实C++的析构函数是自动调用的,也不用自己手动掉用。遇到一些特殊情况,C++也能很好的解决。垃圾收集器是JAVA性能的一个瓶颈,SUN公司也知道这一点,所以在改进它的性能的同
    时,提供了控制它的行为的类,这也是一种无奈的选择。句柄、结果集之类的资源应当使用close()之类的方法明确的释放,而不能依赖析构函数。至于C++的析构函数是自动调用的,从来没听说过,不知你是在哪本书上看到的?也许java的线程比较简单吧,不过C++中也可以继承线程类。由于本人没有涉及线程编程。这里不好乱说。JAVA和C++的线程半斤八两,不说也罢。

    5. Java的兼容性
    相对本地代码来说,Java程序的确移植性比较好。归其原因不外有二。一来Java虚机只一种(不像天下有很多种CPU),二来Java二进制规范只有一种(不像这么多种操作系统)。但是,Java代码真的可以随心所欲的移植吗?其实不然,上述两条中的任何一种不再满足,都会影响ByteCode的移植性。现在,Java的主要版本已经有1.0 , 1.1.7 , 1.2(2.0)三个。更残酷的是1.1.7和1.2不兼容。一个很著名的例子。Websphere不能在Java1.2上运行。另外WebLogic不能在1.0上运行。1。0和1。1的确有兼容性问题。1。0版可以说是SUN为了对付MS而匆匆推出的测试版,但1。1以后就不存在兼容性的问题。java的二进制规范呢?jview( MS java)就自立门户。他的.class装入方法就和java for win32不同。另外它提出了其他的本地代码连接方法。显然这里也会造成不兼容。所以没有人使用MS JDK。

    除了上述两条重要的原因,还有就是程序的运行环境。比如操作系统是否区分文件名的大小写。数据库是否中调用都能返回执行成功。EJB的代码是否有EJB 的运行服务器。等等。JAVA代码根本不存在这样的问题。

    6. 在精通Java之前否定Java

    本人只是对Java略知一二。为什么就非要否定java呢。其实,从一般科学观点审视java。就会发现其中的问题。

    6.1. Java代码不符合从简单到复杂的认识规律
    代码比较:
    Java Code Example:
    import java.applet.*;
    import java.awt.Gaphics;
    public class HelloWorldApplet extends java.applet.Appple


    public void
    t(){
    resize( 200 , 150 );
    }
    public void paint( Graphics g){
    g.drawString("Hello World" , 50 , 100);
    }
    }

    C++ Code Example:
    #include
    class Hello :public Applet
    {
    public void init();
    public void paint(Graphics g);
    };

    Hello::init()
    {
    resize( 200 , 150 );
    }
    ......
    我们看到,java开始就要求写函数的实现。而C++先要描述轮廓。再写函数实现。对。C++实现了接口和实现的分离。

    6.2. Java对从一般到特殊的思维方法支持的不好

    从一般到特殊,是重要的思维方法。对应到计算机学科就是继承。而今多继承的概念已经广为流传。比如我们说“小花猫”,就是继承“ 小”、“花”、“猫”三个概念。如果在字典上看到小花猫这个词, 它一定不再描述“小”、“花”、“猫”这三个概念,而是继承他们 。但是java只能继承其中的一个。对另外连个还要仔细的描述。写起来是不是很烦。但也可能出现“小猫狗”。所有的C++书都说不提倡或慎用多重继承。

    6.3. Java设计不尊重“任何事物都有产生、发展、消亡的过程”这一自然规律Java没有析构函数。这一点很讨厌。我只想重复一遍“对象不都在内存空间中”。不是没有,而是不能由你调用。对象不都在内存空间中???如果指的是persistent,那只需实现一个接口就可以了(不要任何的操作)。


    6.4. Java 不能表达少数类之间的紧密相关
    java要求每个类使用一个文件。这种实现可能和它的自动编译执行方 式有关。注意这种限制不是实现上问题,而是规范中的语句。但是这 种方法合适吗?请看下面C++的例子。将这两个类分开为两个文件是否真的好读?

    struct
    uitem{
    String strX;
    int   iIndex;
    };

    class menu{
    menuitem item[100];
    ... ...
    };
    和这种情况类似,javadoc也不能对紧密相关的数据进行描述。至于类模板。java更是不支持。自己一个一个的慢慢写吧。如果其他的类要使用menuitem,就应该把它定义成一个单独的类。如果只在menu类中使用,就应该把它定义成menu的内部类,类的外部不可见。

    6.5. 批判Java的批判
    Java要诞生,必须批判原有C/C++语言的缺陷。列举其中一二。
    “C/C++语言 中的整形数据不限制长度。这样一来当程序从32位系统移植到16 位系统时会造成错误”。可是仔细想想。这种移植的概率究竟有多少?从8086(8位)到P4(64位)。十年里就有三次。 “Java不像C/C++,它完全不支持指针”。按此理论,同样不支持指针的Fortran77和basic是不是也很强壮。有指针就很不强壮。C/C++程序的大多数错误就是由指针引起的。
    “Java不允许隐式类型转换”。如果整形和浮点数据之间的转换都要写一下,是不是很烦。是有点烦,但是很安全。

    “联合体、全局变量他们都不是面向对象的东西”。面向对象一直就 没有什么定义。本着好用的东西就是好东西的原则,保留这些概念也 不错。像M_PI这类的常量或者项目中的确全局相关的准常量,使用全
    局变量是不是很方便?至于联合体,是优化存储效率用的,像 Java 这种不考虑效率的语言自然不会支持。JAVA是纯面向对象的语言,它强迫你使用面向对象的思想来考虑问题。

    “java存在比C++多的运算符”。好像是多一些,他们是(字符串 +),> > >> , (boolean)& and | , instanceof。但是我们分析一下。由于java不支持运算符重载,只好单独实现(字符串 +)。由于java 不 区分有符号和无符号数据,所以使用>>>来表达无符号右移、由于java中的boolean和整形之间没有联系,又不支持运算符重载,所以对boolean类型单独实现& and |。至于instanceof的确是Java 独创,不 过这个运算符的用处还要考虑。我在引用某变量时居然能不知道它的类型?看上面。

    6.6. 兼容以不兼容为代价
    使用java 必须一切从头开始。java不能继承原有的工作。而且它也不能很好的和其他语言混合编程。这就和C++区别大了。首先C++继承了大部分C语言代码。其次,C++可以和Pascal , Fortran , Basic,T-SQL等其他语言方便混合。尤其方便的是ASM语言和 C++混合。发挥不同语言的长处。俗话说“朋友多,天地宽”吗。而 java只能孤军奋战了。JAVA是纯面向对象的语言,决不会兼容过程型的语言。

    7. 结束语
    写这篇文章用了一天半的时间。又在Java上花了这么多时间,真是心 痛。但愿有些效果。在JAVA上所花的每一分钟都会有所收获。


    发布人:netbull 来自:LinuxAid