分享
 
 
 

Java中鲜为人知的缺点(上)

王朝java/jsp·作者佚名  2008-05-19
窄屏简体版  字體: |||超大  

java是当今使用最广泛的编程语言之一。自1995年发布以来,一直被用户高度评价为“消除了C++缺点的优秀编程语言”。不过,随着它的广泛使用,其缺点也在逐步地表现出来。

Java的缺点公认有如下三点:(1)存在非对象的数据类型;(2)不能够用一种描述方法来表达各种类(Class);(3)无法继续2个以上的类的装配。虽然也有人认为编程语言应该是一个什么样子会因人而异,不应该算成缺点。不过,上述三点却可以导致编程人员使用混乱,降低源码的可读性及程序的可维护性。

存在非对象的数据类型

表1●Java的原始类型(PRimitive)。原始类型包括表示真假的布尔型(Boolean)、字符型和数值型等(点击放大)

第一缺点是指虽说Java是面向对象的编程语言,但却存在非对象的数据类型。

“面向对象”的定义虽然有很多种,但无论何种定义,其最基本的概念都是利用包含数据和步骤的“对象”来表达系统。即便在Java领域,也会使用名为类的模型生成对象,并通过调用它的方法组织程序。

但是,其中却混杂着非对象的内容。原始类型又被称为基本数据类型(表1)。用于处理文字的char(字符型)、表示真假的boolean(布尔型)、int以及float等数值型就属于这种数据类型。

原始类型的内存治理方法不同

图1●Java的内存治理方法。Java内存区包括保存本地变量的内存堆栈区(stack)和保存对象的数据的内存堆区(heap)。堆栈区中存放的是用于引用(reference)对象时所需的信息(点击放大)

原始类型和对象型的内存治理方法不同。Java虚拟机所治理的内存区包括内存堆栈区和内存堆区(图1)。内存堆栈区用于存放本地变量的数据。按堆出的顺序保存本地变量的数据。一旦脱离变量的有效范围,该数据马上就被释放。

而内存堆区则用于存放对象本身。生成对象型的变量后,首先在内存堆栈区中为其预备存放位置。然后利用new运算符在这个位置生成新的对象后,对象及其数据就被存放到内存堆区。接着,内存堆区中的对象位置就会作为对象型的变量数据而被写入内存堆栈区。由于这些是引用对象时所用的信息,因此对象型变量被称为“引用型”。

而实际数据本身被写入内存堆栈区的是原始类型。采用这种内存治理方法的类型称为“数值型”。

引用型变量改变以后,就会引用保存内存堆区中的实际的对象数据,重新改写数据。比如,在方法的引数(arguments)中描述对象型变量时传递给方法的就是存放在这个位置中的信息。所以在方法内追加的变更还会被反映到调用的原始对象上。另一方面,数值型变量传递的是它的值。即便在方法内部进行了变更,也不会反映到原始变量中。

无法使用对象所具有的功能

LIST 1●将数值数据保存在Java的矢量类中的程序。生成Integer类,然后封装(Wrap)数值(点击放大)

点击查看大图

LIST 1●将数值数据保存在Java的矢量类中的程序。生成Integer类,然后封装(Wrap)数值(点击放大)

由于原始类型与对象型的内存治理方法不同,因此就无法生成统一两种数据的类库。比如,假如只是对象型数据就能够构筑包含任意数据的类库。

可变长的数组类就是其中的一个例子。它是作为名为java.util.Vector的类而生成的。可以将任意的对象追加到数组中,还可以提取或删除。能够以此为引数指定任意的对象。但是,由于原始类型数据不是对象,因此无法直接引入。

因此在Java中还存在相当于原始类型的类。比如int型变量就可以使用java..lang.Integer类。重新生成Integer类,然后保存数据,就可以追加到Vector矢量类中(LIST 1)。

但是稍微想一想就会明白,这种方法并不是很灵活的做法。由于加入了多余的代码,因此看起来感觉比较乱。而且还会浪费内存空间。原来的值暂且不说,还必须确保新建对象所需的内存。不仅存在表面上的问题,还存在实质上的问题。就是说无法保证数据的同一性。作为对象型保存的值与作为原始类型而保存的值完全不同。即便改变了原始类型的值,也不会反映到原来的int型数据。

C#利用Boxing(装箱)解决的只是一部分

这一问题并非是Java特有的。比如,作为与Java类似的语言为用户熟知的C#也存在相同的问题。C#利用称为Boxing的方法部分地解决了这个问题。但是所解决的也只是可以不写多余代码的部分。内存问题和同一性问题仍然存在。

即便C#,int、double和char等数据类型也无法作为对象进行处理。这些数据类型与Java的原始类型相同,也是数值型变量。

C#可以将其值代入到对象中。LIST 2中显示了具体的代码。已经将int型的值代入了对象型变量。此时先进行装箱,之后就开始静静地把基本数据类型的数据转换成对象型数据。在内存堆区中确保相应的内存,然后将数值型数据保存这里(图2)。对象型变量引用的就是这些数据。

点击查看大图

LIST 2●执行装箱的C#代码。将数值直接代入对象中。运行代码后,输出0和1。也就是说变量a和o没有同一性(点击放大)

图2●C#中的装箱法。对存放于内存堆栈区中的int型结构体(strUCts)装箱时,就会静静地在内存堆区中生成对象。因此就无法确保与初始值的匹配性。(点击放大)

笔者利用装箱法,用C#试着写了一段与在Java的Vector矢量类中保存数值类似的代码(LIST 3)。虽然ArrayList类要引数中提取对象型变量,但这里由于通过直接int型变量,因此代码非常整洁。

不过,并没有解决多余的内存消耗和数值的同一性问题。因为只是单纯地实现了自动向对象的转换(图3)。

LIST 3●与LIST 1起相同作用的C#代码。由于具有装箱法,因此可以直接向ArrayList中追加数值

点击查看大图

图3●利用Java和C#,将int型变量转换成对象的方法。尽管内部处理基本相同,但C#的特点是隐式转换

假如考虑到实用,也算得上是优点

从上述所讲来看,就会生出这样的疑问:为什么最新的Java和C#语言还存在着这样的问题呢?实际上这是因为对其性能的重视。

由于原始数据型数据在编程时使用得最多,因此利用能够对其进行快速处理的原始类型,性能就会提高。而对象型数据在生成对象,以及使用位置信息去引用内存堆区中的数据时则会产生一定的开销。另一个问题是内存堆区。假如全部是对象型,比如,只要执行简单的for循环语句,就会在内存堆区中生成大量的对象。由于内存堆区的消耗速度就会急剧上升,并且频繁地进行资源回收处理,因此性能就会降低。

Java和C#是考虑到性能问题才生成原始数据型数据的。因此并不能说是“纯粹的”面向对象语言。也许可以说是考虑到实用性的稳妥做法吧。(记者:大森 敏行、八木 玲子)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有