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)數值(點擊放大)
由於原始類型與對象型的內存管理方法不同
因此就無法生成統一兩種數據的類庫
比如
如果只是對象型數據就能夠構築包含任意數據的類庫
可變長的數組類就是其中的一個例子
它是作為名為java
util
Vector的類而生成的
可以將任意的對象追加到數組中
還可以提取或刪除
能夠以此為引數指定任意的對象
但是
由於原始類型數據不是對象
因此無法直接引入
因此在Java中還存在相當於原始類型的類
比如int型變量就可以使用java
lang
Integer類
重新生成Integer類
然後保存數據
就可以追加到Vector矢量類中(LIST
)
但是稍微想一想就會明白
這種方法並不是很靈活的做法
由於加入了多余的代碼
因此看起來感覺比較亂
而且還會浪費內存空間
原來的值暫且不說
還必須確保新建對象所需的內存
不僅存在表面上的問題
還存在實質上的問題
就是說無法保證數據的同一性
作為對象型保存的值與作為原始類型而保存的值完全不同
即便改變了原始類型的值
也不會反映到原來的int型數據
C#利用Boxing(裝箱)解決的只是一部分
這一問題並非是Java特有的
比如
作為與Java類似的語言為用戶熟知的C#也存在相同的問題
C#利用稱為Boxing的方法部分地解決了這個問題
但是所解決的也只是可以不寫多余代碼的部分
內存問題和同一性問題仍舊存在
即便C#
int
double和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#是考慮到性能問題才生成原始數據型數據的
因此並不能說是
純粹的
面向對象語言
也許可以說是考慮到實用性的穩妥做法吧
(記者
大森 敏行
八木 玲子)
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19244.html