Java到底是不是一種純面嚮物件語言?

2016-06-28    分類:JAVA開發、程式設計開發、首頁精華8人評論發表於2016-06-28

本文由碼農網 – Dee1024原創翻譯,轉載請看清文末的轉載要求,歡迎參與我們的付費投稿計劃

Java——是否確實的 “純物件導向”?讓我們深入到Java的世界,試圖來證實它。

在我剛開始學習 Java 的前面幾年,我從書本里知道了 Java 是遵循 “物件導向程式設計正規化(Object Oriented Programming paradigm)”的。在Java世界內一切都是物件,甚至包括字串(String)這些都是物件(在 C 語言中,字串是字元陣列),那時候,我認為 Java是一種物件導向的語言。

但是在後來,我在網際網路站上陸續看到不少開發者說 “Java實際上不是純粹的物件導向,因為並不是所有的東西在 Java 世界都是一個物件”。他們很多的論點都可以概括為以下兩點:

  • 所有的靜態內容( static 關鍵修飾的變數和方法)不屬於任何物件,所以這些是非物件的東西。
  • 所有基本型別(char,boolean,byte,short,int,long,float,double)都不是物件,因為我們不能做類似正常物件的所具有的操作(例如:使用“.”來訪問物件的屬性和方法)。

在那時,由於個人知識經驗儲備有限,我又很容地相信上面的論點,並且也開始認為 “Java 不是純粹的物件導向程式語言”。

到了更後來,在我的一次JVM學習過程中,我有了新的發現:

JVM 在建立物件的時候,實際上會建立兩個物件:

  • 一個是例項物件。
  • 另一個是Class 物件。該 Class 物件在JVM內僅僅會裝載一次,該類的靜態方法和靜態屬性也一同裝載,JVM使用該 Class 物件來建立具體的例項物件(如上面的物件)。

例如,在下面的 Java 語句中,將有兩個物件被建立:

Employee emp = new Employee();

一個是例項物件 emp ;另一個則是 Class物件,我們可以通過 Employee.class 引用到它;這個 Class 物件擁有所有的這個類定義的靜態變數和靜態方法,同時,如果我們訪問 通過 emp 物件來訪問靜態內容,會發現它其實指向的物件就是 Employee.class 。

這也揭開了另一個迷:為什麼靜態內容在一個物件中(不管是emp還是emp2)改變了,在另一個物件中也同時改變,因為這兩個物件改變的都是在 Employee.class 同一個物件裡面的內容。

現在,上面說到的第一個論點我們要取消了。因為,靜態內容確實被證實屬於一個物件。

但是我們還要確認第二個論點:正如早前提到的,原始型別在Java中不是物件,它們無法做類似物件的操作。為了解決這個問題,Java 官方為每一個原始型別推出了對應的包裝類(比如:Integer 對應 int,Long 對應 long,Character 對應 char),所以,其實現在我們可以為原始型別建立一個包裝物件,同時對它們做物件相關的操作。並且,由於自動拆裝箱,我們可以把一個原始型別值賦值給它對應的包裝類的引用。但是我們仍然不能對這些原始型別做物件的操作——我們需要建立對應包裝類的物件。

例如:

Integer obj = new Integer(5); // here we can do i.toString();
int i = 5; // but we can't do i.toString() here

到目前為止,從一個終端使用者的角度上來看的,我們可以確認 “原始類別不是物件”。( Java開發人員是Java的終端使用者,因為我們正在使用它,而不是創造它 )。

如果站在JVM的視角,會有新的發現:

其實,在JVM看來它把所有的 “原始型別” 都是當作物件處理” ,要證明這一點可以通過 Class類的原始碼 或者 Javadoc中Class類的說明。

根據 java.lang.Class 類的原始碼,該類的註釋是:

Java官方描述:

Instances of the class Class represent classes and interfaces in a running Java 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 Java types (boolean, byte, char, short, int, long, float, and double), and the keyword void are also represented as Class objects.

參考譯文:

Class類的例項表示正在執行的Java應用程式的類和介面。像列舉是一種類和註解則是一種介面。每個陣列也屬於被反射作為由具有相同的元素型別和尺寸的數目的所有陣列共享一類物件的類。原始的Java型別(boolean, byte, char, short, int, long, float, and double)和關鍵字void也表示為Class物件。

同時也根據Javadoc中對Class.isPrimitive()方法的定義,來判斷

Java官方描述:

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 Java Virtual Machine, and have 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物件是否代表一個基本型別。
一共有9種設定好的Class物件來表示對應的基本型別和void關鍵字。這些物件都是由JVM建立的。…
return

當且僅當該類表示一個真正的基本型別

以上都說明,在JVM內部,其實原始型別就是物件。

當你開啟 Javadoc 對 Class 類的定義中,通過 “CTRL+F ” 查詢關鍵字 “primitive”, 將會發現證據在表面 “在JVM裡,它把基本型別當作物件來處理的”。

我們可以再來看一個例子: Integer.TYPE,在這部分文件清晰記錄著:

Java官方描述:

public static final Class<Integer> TYPE
The Class instance representing the primitive type int.

以上都說明,在JVM內部,其實原始型別就是物件。

那麼,既然說 “JVM”會為所有的基本型別建立一個物件,那我們為什麼還那麼常用 “原始型別”, 而不是直接使用對應的包裝類物件呢?

這是因為,為 “原始型別” 建立的物件,在JVM內部是很輕量級的,相對與我們直接建立的對應包裝類物件做了許多優化; 也正因為輕量的緣故,這些原始類的功能就比較少(例如我們不能呼叫其內部的方法,因為他們內部已經優化成沒有方法了)

使用實際的例子來說明,為什麼我們更應該使用 “原始型別”:

“原始型別”有更快的速度(例如,下面的程式碼執行,在我們的機器上需要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);
}

“原始型別”允許我們直接使用 “==”來進行比較

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

我們注意看第四句,輸出結果確實為 “false” 。這個是因在 [-128; 127] 這個區間的265個整數會被 JVM 快取存放, 所以在這個區間, JVM返回相同的物件;然而,超出這個區間, JVM就不再有快取了,將會建立新的物件,所以結果是不等的。

所以總結一下是: 在JVM內部,原始型別就是被當作物件來處理的。但是我們開發者直接把 “原始型別” 當作物件使用,開發者應該使用對應的包裝來。

以上就是為什麼我說 “ Java確實是一個純粹的面嚮物件語言 ”的證實過程。如果你們對這個有什麼其他的觀點,請在評論留言,一起討論。

譯文連結:http://www.codeceo.com/article/why-java-purely-object-oriented-language.html
英文原文:Why Java Is a Purely Object-Oriented Language... Or Why Not
翻譯作者:碼農網 – Dee1024
轉載必須在正文中標註並保留原文連結、譯文連結和譯者等資訊。]

相關文章