java

位置:IT落伍者 >> java >> 浏览文章

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


发布日期:2018年05月30日
 
Java中鲜为人知的缺点(上)

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

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

存在非对象的数据类型

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

上一篇:Java SE 6 新特性: Instrumentatio

下一篇:关于java.util.regex 包中新增字符替换方法的比较