“加哇”到底是不是一種純面向對象語(yǔ)言?
“加哇”——是否確實(shí)的 “純面向對象”?讓我們深入到“加哇”的世界,試圖來(lái)證實(shí)它。
本文引用地址:http://dyxdggzs.com/article/201607/294234.htm在我剛開(kāi)始學(xué)習“加哇”的前面幾年,我從書(shū)本里知道了 “加哇” 是遵循 “面向對象編程范式(Object Oriented Programming paradigm)”的。在“加哇”世界內一切都是對象,甚至包括字符串(String)這些都是對象(在 C 語(yǔ)言中,字符串是字符數組),那時(shí)候,我認為“加哇”是一種面向對象的語(yǔ)言。
但是在后來(lái),我在互聯(lián)網(wǎng)站上陸續看到不少開(kāi)發(fā)者說(shuō) “加哇”實(shí)際上不是純粹的面向對象,因為并不是所有的東西在“加哇” 世界都是一個(gè)對象”。他們很多的論點(diǎn)都可以概括為以下兩點(diǎn):
所有的靜態(tài)內容( static 關(guān)鍵修飾的變量和方法)不屬于任何對象,所以這些是非對象的東西。
所有基本類(lèi)型(char,boolean,byte,short,int,long,float,double)都不是對象,因為我們不能做類(lèi)似正常對象的所具有的操作(例如:使用“.”來(lái)訪(fǎng)問(wèn)對象的屬性和方法)。
在那時(shí),由于個(gè)人知識經(jīng)驗儲備有限,我又很容地相信上面的論點(diǎn),并且也開(kāi)始認為 “加哇”不是純粹的面向對象編程語(yǔ)言”。
到了更后來(lái),在我的一次JVM學(xué)習過(guò)程中,我有了新的發(fā)現:
JVM 在創(chuàng )建對象的時(shí)候,實(shí)際上會(huì )創(chuàng )建兩個(gè)對象:
一個(gè)是實(shí)例對象。
另一個(gè)是Class 對象。該 Class 對象在JVM內僅僅會(huì )裝載一次,該類(lèi)的靜態(tài)方法和靜態(tài)屬性也一同裝載,JVM使用該 Class 對象來(lái)創(chuàng )建具體的實(shí)例對象(如上面的對象)。
例如,在下面的 “加哇” 語(yǔ)句中,將有兩個(gè)對象被創(chuàng )建:
Employee emp = new Employee();
一個(gè)是實(shí)例對象 emp ;另一個(gè)則是 Class對象,我們可以通過(guò) Employee.class 引用到它;這個(gè) Class 對象擁有所有的這個(gè)類(lèi)定義的靜態(tài)變量和靜態(tài)方法,同時(shí),如果我們訪(fǎng)問(wèn) 通過(guò) emp 對象來(lái)訪(fǎng)問(wèn)靜態(tài)內容,會(huì )發(fā)現它其實(shí)指向的對象就是 Employee.class 。
這也揭開(kāi)了另一個(gè)迷:為什么靜態(tài)內容在一個(gè)對象中(不管是emp還是emp2)改變了,在另一個(gè)對象中也同時(shí)改變,因為這兩個(gè)對象改變的都是在 Employee.class 同一個(gè)對象里面的內容。
現在,上面說(shuō)到的第一個(gè)論點(diǎn)我們要取消了。因為,靜態(tài)內容確實(shí)被證實(shí)屬于一個(gè)對象。
但是我們還要確認第二個(gè)論點(diǎn):正如早前提到的,原始類(lèi)型在“加哇”中不是對象,它們無(wú)法做類(lèi)似對象的操作。為了解決這個(gè)問(wèn)題,“加哇”官方為每一個(gè)原始類(lèi)型推出了對應的包裝類(lèi)(比如:Integer 對應 int,Long 對應 long,Character 對應 char),所以,其實(shí)現在我們可以為原始類(lèi)型創(chuàng )建一個(gè)包裝對象,同時(shí)對它們做對象相關(guān)的操作。并且,由于自動(dòng)拆裝箱,我們可以把一個(gè)原始類(lèi)型值賦值給它對應的包裝類(lèi)的引用。但是我們仍然不能對這些原始類(lèi)型做對象的操作——我們需要創(chuàng )建對應包裝類(lèi)的對象。
例如:
Integer obj = new Integer(5); // here we can do i.toString();
int i = 5; // but we can't do i.toString() here
到目前為止,從一個(gè)最終用戶(hù)的角度上來(lái)看的,我們可以確認 “原始類(lèi)別不是對象”。( “加哇”開(kāi)發(fā)人員是“加哇”的最終用戶(hù),因為我們正在使用它,而不是創(chuàng )造它 )。
如果站在JVM的視角,會(huì )有新的發(fā)現:
其實(shí),在JVM看來(lái)它把所有的 “原始類(lèi)型” 都是當作對象處理” ,要證明這一點(diǎn)可以通過(guò) Class類(lèi)的源代碼 或者“加哇”doc中Class類(lèi)的說(shuō)明。
根據“加哇”.lang.Class 類(lèi)的源代碼,該類(lèi)的注釋是:
“加哇”官方描述:
Instances of the class Class represent classes and interfaces in a running J**a application. An enum is a kind of class and an annotation is a kind of interface. Every array also belongs to a class that is reflected as a Class object that is shared by all arrays with the same element type and number of dimensions. The primitive J**a types (boolean, byte, char, short, int, long, float, and double), and the keyword void are also represented as Class objects.
參考譯文:
Class類(lèi)的實(shí)例表示正在運行的“加哇”應用程序的類(lèi)和接口。像枚舉是一種類(lèi)和注解則是一種接口。每個(gè)數組也屬于被反射作為由具有相同的元素類(lèi)型和尺寸的數目的所有陣列共享一類(lèi)對象的類(lèi)。原始的“加哇”類(lèi)型(boolean, byte, char, short, int, long, float, and double)和關(guān)鍵字void也表示為Class對象。
同時(shí)也根據“加哇”doc中對Class.isPrimitive()方法的定義,來(lái)判斷
“加哇”官方描述:
public boolean isPrimitive()
Determines if the specified Class object represents a primitive type.
There are nine predefined Class objects to represent the eight primitive types and void. These are created by the J**a Virtual Machine, and h**e the same names as t he primitive types that they represent, namely boolean,byte, char, short, int, long, float, and double.
These objects may only be accessed via the following public static final variables, and are the only Class objects for which this method returns true.
Returns:
true if and only if this class represents a primitive type
Since:
JDK1.1
參考翻譯:
public boolean isPrimitive()
判斷指定的Class對象是否代表一個(gè)基本類(lèi)型。
一共有9種設定好的Class對象來(lái)表示對應的基本類(lèi)型和void關(guān)鍵字。這些對象都是由JVM創(chuàng )建的。…
return
當且僅當該類(lèi)表示一個(gè)真正的基本類(lèi)型
以上都說(shuō)明,在JVM內部,其實(shí)原始類(lèi)型就是對象。
當你打開(kāi) J**adoc 對 Class 類(lèi)的定義中,通過(guò) “CTRL+F ” 查找關(guān)鍵字 “primitive”, 將會(huì )發(fā)現證據在表面 “在JVM里,它把基本類(lèi)型當作對象來(lái)處理的”。
我們可以再來(lái)看一個(gè)例子: Integer.TYPE,在這部分文檔清晰記錄著(zhù):
“加哇”官方描述:
public static final Class TYPE
The Class instance representing the primitive type int.
以上都說(shuō)明,在JVM內部,其實(shí)原始類(lèi)型就是對象。
那么,既然說(shuō) “JVM”會(huì )為所有的基本類(lèi)型創(chuàng )建一個(gè)對象,那我們?yōu)槭裁催€那么常用 “原始類(lèi)型”, 而不是直接使用對應的包裝類(lèi)對象呢?
這是因為,為 “原始類(lèi)型” 創(chuàng )建的對象,在JVM內部是很輕量級的,相對與我們直接創(chuàng )建的對應包裝類(lèi)對象做了許多優(yōu)化; 也正因為輕量的緣故,這些原始類(lèi)的功能就比較少(例如我們不能調用其內部的方法,因為他們內部已經(jīng)優(yōu)化成沒(méi)有方法了)
使用實(shí)際的例子來(lái)說(shuō)明,為什么我們更應該使用 “原始類(lèi)型”:
“原始類(lèi)型”有更快的速度(例如,下面的代碼執行,在我們的機器上需要9秒,但當我把 Long 改成 long 之后,0秒內就完成了)
public static void main(String[] args) {
long millis = System.currentTimeMillis();
Long sum = 0L; // uses Long, not long
for (long i = 0; i <= Integer.MAX_VALUE; i++) {
sum += i;
}
System.out.println(sum);
System.out.println((System.currentTimeMillis() - millis) / 1000);
}
“原始類(lèi)型”允許我們直接使用 “==”來(lái)進(jìn)行比較
new Integer(3) == new Integer(3); // false
new Integer(100) == new Integer(100); // false
Integer.valueOf(5) == Integer.valueOf(5); //true
Integer.valueOf(200) == Integer.valueOf(200); //false
我們注意看第四句,輸出結果確實(shí)為 “false” 。這個(gè)是因在 [-128; 127] 這個(gè)區間的265個(gè)整數會(huì )被 JVM 緩存存放, 所以在這個(gè)區間, JVM返回相同的對象;然而,超出這個(gè)區間, JVM就不再有緩存了,將會(huì )創(chuàng )建新的對象,所以結果是不等的。
所以總結一下是: 在JVM內部,原始類(lèi)型就是被當作對象來(lái)處理的。但是我們開(kāi)發(fā)者直接把 “原始類(lèi)型” 當作對象使用,開(kāi)發(fā)者應該使用對應的包裝來(lái)。
以上就是為什么我說(shuō) “ “加哇”確實(shí)是一個(gè)純粹的面向對象語(yǔ)言 ”的證實(shí)過(guò)程。如果你們對這個(gè)有什么其他的觀(guān)點(diǎn),請在評論留言,一起討論,大家也可以微信關(guān)注華清遠見(jiàn),回復“干貨”400元電子書(shū)相贈,每天下午5點(diǎn)30,精彩內容喂飽你。
注:因該語(yǔ)言易被屏蔽所以全文用中文名“加哇”。
評論