从C/C++与Java的语言设计看代码质量问题
2011-10-17 00:00
686 查看
历代编程语言演化,都尝试着去解决不同的附属性困难问题。从Java反省C++的诸般问题而进行改良的结果,我们可以看出它所尝试要解决的问题的困难。
惯用C/C++程序语言的程序设计者都知道,自己配置的内存必须自己妥善释放,无论是没有释放或是重复释放,都会造成程序的Bug。程序设计者自己配置内存、自己释放是一种程序设计模型,而只管配置、不管释放,又是另一种程序设计模型。
Java继承了C/C++的血统,却选择了另外一种截然不同处理内存配置与释放的程序设计模型,为什么呢?在Java问世之前,垃圾收集机制已经存在,但鲜少看到十分主流的程序语言采用,原因就是在于它的效能问题。因为自动判断每一块内存区块是否该被回收及加以回收,都需要不少计算量,因而影响到程序的效能。而Java发明者决心以效能换得这个垃圾收集机制,是一个重大的决定,为什么呢?这自然是因为让程序设计者自己配置内存、自己释放的程序设计模型,存在着影响深远的问题,容易滋生出Bug。
指针是C/C++中十分具有威力的语法元素,运用指针可以达到许多神奇的效果,也能写出相当优雅的程序代码。指针的运用,可以说是C/C++语言中的一门艺术。但是不幸的,水能载舟亦能覆舟,指针相较于内存配置与释放的问题,更是容易成为Bug的温床。相信有足够C/C++经验的程序设计者,都能理解指标的误用、错用,十分容易引发程序的Bug,而且相当不易察觉。指针允许你指向内存的任意位置,但是一旦指向不属于应用程序可存取的内存地址范围、或是以不正确的方式解读所指向内存中的数据,就会引发严重的错误。
而数组的越位存取也是和指针类似的问题,这一类的问题,甚至可以被刻意操作,衍生成为缓冲区溢位攻击。这类的问题,只要有过足够C/C++程序设计经验的程序设计者,应当都能体会这些在程序设计过程中所可能造成的困扰。它们不仅容易造成Bug,而且所长出来的Bug有很高的比例,都会藏在程序中最隐晦的角落,让你难以发觉。
显然Java的发明者选择不这么做,他宁可放弃有威力的武器,也不希望这个武器在伤敌的同时,也同样伤到自己。
而这也反映Java语言在众多面向上的设计哲学,宁可削除一些看似弹性、看似有威力的语法元素,也要换取降低程序设计者因而犯错的机会。所以,Java才会得到一个 “C++ --“ 的评语。身为一个后继、流着相同血源的程序语言,却试图削减,可以想见其中一定充满着许多的反省。
所以,我们可以看见Java中取消了指针,改用参照(reference)来指向对象,没有指针指向任意内存地址的能力。一旦程序设计者做了越界的数组操作,Java的虚拟机器就会掷出异常,并且不允许执行该操作。同样的,程序设计者也无法对指向null的空参照值进行存取,一样会受到虚拟机器的控管,掷出对应的异常对象,待应用程序处理。
为什么Java这么重视这件事?因为如此才能大大提升软件开发时的生产力。事实上的确如此,正如前段所提的,一旦写下了一个错误,就必须花费数倍、甚至数十倍撰写出该错误的心力和时间,才能揪出这个错误并加以修正。你可以想象,这样开发对生产力会有多大的影响。
花多少时间写下程序代码或许是生产力的关键之一,但是,总共花多少时间得到足够质量的程序代码,才是最后所展现出来的真正生产力指针。我们可以说,Java是把生产力当做最高指导原则之一的程序语言,而它想要得到生产力的重要途径之一,就是在语言层次上尽量避免程序设计者犯错。
C和C++这两个语言中,存在不少容易让程序设计者犯错的地方,这是Java之所以要C加加之后再减减的原因。
举例来说:
这样的写法无论在C或C++里都是合法的,但是,你有没有注意到,在if的条件式中,程序设计者有可能少打了一个 = 号,他原先想写下的条件式可能是 a == b,但因为疏失写成了 a = b,而这仍然是可通过编译的C/C++程序代码,它的语义从原先的若a值等于b值则条件成立,变成了将b值指派给变数a,若a在指派后的值大于0,则条件成立。
相信写过C/C++的程序设计者都可能犯下这样子的错误,而且也都能理解这样子的错单纯只是因为输入程序代码的错误,却有可能花上你许多的时间才能找到。从语法上来看,这是从C时代就具备的语法,它很强大、很有弹性,可以写出很取巧的程序代码,但是也留下了让程序设计者犯下上述错误的空间。当然,这样子的语法到了Java,已经不允许了。
你可以在C++中,找到更多更有威力、更有弹性的语法,程序设计者得以凭借来达成很厉害的效果,但是,使用它们要更小心、更谨慎、甚至需要对语言更了解,也更需要技巧,才能将它们运用地淋漓尽致,而且,一不小心,就会犯下难以修正的错误。而其中并不是难在纠正本身,而是难在找到为什么而错。
像C++中的运算子重载,基本上在Java程序语言中是没有的,而像C++中的多重继承,在Java中也仅提供多重接口的实作。从这种角度来看,你可以说Java比较弱,但这是透过刻意削减容易出错的语言元素,来减少程序设计者犯错的机会,即使语言威力变弱了、看起来不那么巧妙了、或许要用比较多的程序代码才能做到一样的事,而程序代码或许看起来很笨,但因为不容易犯错,反而可以提高生产力。
这就是Java尝试提高生产力的一个重要方向。
为什么Java要采用垃圾收集机制?
Java中有一个很重大的特色,就是垃圾收集机制(Garbage Collection,GC)。垃圾收集机制允许程序设计者尽管放心的去配置所需的内存空间,而毋需自行处理这些内存空间的释放问题,因为垃圾收集器(Garbage Collector)会自动判断已经不被程序所使用的内存空间,并且自动释放。惯用C/C++程序语言的程序设计者都知道,自己配置的内存必须自己妥善释放,无论是没有释放或是重复释放,都会造成程序的Bug。程序设计者自己配置内存、自己释放是一种程序设计模型,而只管配置、不管释放,又是另一种程序设计模型。
Java继承了C/C++的血统,却选择了另外一种截然不同处理内存配置与释放的程序设计模型,为什么呢?在Java问世之前,垃圾收集机制已经存在,但鲜少看到十分主流的程序语言采用,原因就是在于它的效能问题。因为自动判断每一块内存区块是否该被回收及加以回收,都需要不少计算量,因而影响到程序的效能。而Java发明者决心以效能换得这个垃圾收集机制,是一个重大的决定,为什么呢?这自然是因为让程序设计者自己配置内存、自己释放的程序设计模型,存在着影响深远的问题,容易滋生出Bug。
从指针与数组来看C/C++
除了内存配置与释放的问题之外,还有许多问题长期以来困扰着C/C++的程序设计者,例如指针的问题。指针是C/C++中十分具有威力的语法元素,运用指针可以达到许多神奇的效果,也能写出相当优雅的程序代码。指针的运用,可以说是C/C++语言中的一门艺术。但是不幸的,水能载舟亦能覆舟,指针相较于内存配置与释放的问题,更是容易成为Bug的温床。相信有足够C/C++经验的程序设计者,都能理解指标的误用、错用,十分容易引发程序的Bug,而且相当不易察觉。指针允许你指向内存的任意位置,但是一旦指向不属于应用程序可存取的内存地址范围、或是以不正确的方式解读所指向内存中的数据,就会引发严重的错误。
而数组的越位存取也是和指针类似的问题,这一类的问题,甚至可以被刻意操作,衍生成为缓冲区溢位攻击。这类的问题,只要有过足够C/C++程序设计经验的程序设计者,应当都能体会这些在程序设计过程中所可能造成的困扰。它们不仅容易造成Bug,而且所长出来的Bug有很高的比例,都会藏在程序中最隐晦的角落,让你难以发觉。
效果强与易犯错之间如何取舍?
C/C++的程序设计者在内存处理的动作上,得到极为高度的自由,但又吃尽苦头。C/C++在内存处理的程序设计模型很有威力,却也容易让程序设计者犯错,而且一旦犯了错,程序设计者必须花费数倍、甚至数十倍撰写出该错误的心力和时间,才能揪出这个错误并加以修正。这当然是一个值得思考的问题,我们是否要在一个语言中,放入一个极有威力、却容易造成程序设计者犯错的程序模型?显然Java的发明者选择不这么做,他宁可放弃有威力的武器,也不希望这个武器在伤敌的同时,也同样伤到自己。
而这也反映Java语言在众多面向上的设计哲学,宁可削除一些看似弹性、看似有威力的语法元素,也要换取降低程序设计者因而犯错的机会。所以,Java才会得到一个 “C++ --“ 的评语。身为一个后继、流着相同血源的程序语言,却试图削减,可以想见其中一定充满着许多的反省。
所以,我们可以看见Java中取消了指针,改用参照(reference)来指向对象,没有指针指向任意内存地址的能力。一旦程序设计者做了越界的数组操作,Java的虚拟机器就会掷出异常,并且不允许执行该操作。同样的,程序设计者也无法对指向null的空参照值进行存取,一样会受到虚拟机器的控管,掷出对应的异常对象,待应用程序处理。
从生产力来看,质量才是关键
上述这几个例子,都是传统C/C++程序设计容易犯错的地方,Java从语言的根本上尝试杜绝这几类问题的发生。这些在语言上的设计,也都反映Java的基本哲学──减少程序设计者犯错的机会。为什么Java这么重视这件事?因为如此才能大大提升软件开发时的生产力。事实上的确如此,正如前段所提的,一旦写下了一个错误,就必须花费数倍、甚至数十倍撰写出该错误的心力和时间,才能揪出这个错误并加以修正。你可以想象,这样开发对生产力会有多大的影响。
花多少时间写下程序代码或许是生产力的关键之一,但是,总共花多少时间得到足够质量的程序代码,才是最后所展现出来的真正生产力指针。我们可以说,Java是把生产力当做最高指导原则之一的程序语言,而它想要得到生产力的重要途径之一,就是在语言层次上尽量避免程序设计者犯错。
C和C++这两个语言中,存在不少容易让程序设计者犯错的地方,这是Java之所以要C加加之后再减减的原因。
举例来说:
int a = 3; int b = 1; if( a = b ) // if( a == b ) { // 执行一些动作 }
这样的写法无论在C或C++里都是合法的,但是,你有没有注意到,在if的条件式中,程序设计者有可能少打了一个 = 号,他原先想写下的条件式可能是 a == b,但因为疏失写成了 a = b,而这仍然是可通过编译的C/C++程序代码,它的语义从原先的若a值等于b值则条件成立,变成了将b值指派给变数a,若a在指派后的值大于0,则条件成立。
相信写过C/C++的程序设计者都可能犯下这样子的错误,而且也都能理解这样子的错单纯只是因为输入程序代码的错误,却有可能花上你许多的时间才能找到。从语法上来看,这是从C时代就具备的语法,它很强大、很有弹性,可以写出很取巧的程序代码,但是也留下了让程序设计者犯下上述错误的空间。当然,这样子的语法到了Java,已经不允许了。
你可以在C++中,找到更多更有威力、更有弹性的语法,程序设计者得以凭借来达成很厉害的效果,但是,使用它们要更小心、更谨慎、甚至需要对语言更了解,也更需要技巧,才能将它们运用地淋漓尽致,而且,一不小心,就会犯下难以修正的错误。而其中并不是难在纠正本身,而是难在找到为什么而错。
像C++中的运算子重载,基本上在Java程序语言中是没有的,而像C++中的多重继承,在Java中也仅提供多重接口的实作。从这种角度来看,你可以说Java比较弱,但这是透过刻意削减容易出错的语言元素,来减少程序设计者犯错的机会,即使语言威力变弱了、看起来不那么巧妙了、或许要用比较多的程序代码才能做到一样的事,而程序代码或许看起来很笨,但因为不容易犯错,反而可以提高生产力。
这就是Java尝试提高生产力的一个重要方向。
相关文章推荐
- 提高你的Java代码质量吧:多使用异常,把性能问题放一边
- 我使用过的Linux命令之swig - 把C/C++的代码嵌入Java等语言的开发工具
- C++、Java和Matlab不同语言关于 y=(++x)+(++x);结果不同的问题
- 如何在Linux下用C/C++语言操作数据库sqlite3(很不错!设计编译链接等很多问题!)
- 【高质量代码】如何写出更高质量的C/C++代码(2):函数设计
- 如何在Linux下用C/C++语言操作数据库sqlite3(很不错!设计编译链接等很多问题!)
- C++实现w3cshool设计模式教程--设计模式中的Java代码
- 约瑟夫问题java语言实现代码
- 提高你的Java代码质量吧:多使用异常,把性能问题放一边
- 瓶子质量问题的Java代码实现
- 微信 开发 聊天机器人设计方案 java语言 程序和数据库代码
- 关于java代码质量的问题
- 用Java语言解决的汉诺塔问题的代码
- JAVA代码—算法基础:0-1背包问题的回溯算法设计(续)
- java与c/c++进行socket通信的一些问题(1)-JSP教程,Java技巧及代码
- C++代码设计:向Java借鉴Builder模式塈OpenCL内核代码编译
- 动态规划排线算法问题java语言实现 在一块电路板的上、下2端分别有n个接线柱。根据电路设计,要求用导线(i,π(i))将上端接线柱与下端接线柱相连
- Android NDK 中C/C++ 代码java.lang.UnsatisfiedLinkError , Native method not found 问题
- 关于A+B+C问题4种语言的解决办法,Java、C语言、C++、Python
- java与c/c++进行socket通信的一些问题(1)-JSP教程,Java技巧及代码