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類別的說明。
##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 lected class 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 key vresoid so keyoo 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 Classc0f559cc8d56b43654fcbe4aa9df7b4a 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到底是不是一種純粹物件導向語言的詳細介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!