java獲取數據長度的方法


為什么獲取數組的長度用.length(成員變量的形式),而獲取String的長度用.length()(成員方法的形式)?”

 

我當時一聽,覺得問得很有道理。做同樣一件事情,為什么采用兩種風格迥異的風格呢?況且,Java中的數組其實是完備(full-fledged)的對象,直接暴露成員變量,可能不是一種很OO的風格。那么,設計Java的那幫天才為什么這么做呢?

 

帶着這個疑問,我查閱了一些資料,主要是關於“JVM是如何處理數組”的。

 

數組對象的類是什么?

 

既然數組都是對象,那么數組的類究竟是什么呢?當然不是java.util.Arrays啦!我們以int一維數組為例,看看究竟。

 

Java代碼  
  1. public class Main {  
  2.     public static void main(String args[]){  
  3.         int a[] = new int[10];  
  4.         Class clazz = a.getClass();  
  5.         System.out.println(clazz.getName());  
  6.     }  
  7. }  

 

在SUN JDK 1.6上運行上述代碼,輸出為:

[I

看起來數組的類很奇怪,非但不屬於任何包,而且名稱還不是合法的標識符(identifier)。具體的命名規則[1]可以參見java.lang.Class.getName()的javadoc。簡單的說,數組的類名由若干個'['和數組元素類型的內部名稱組成,'['的數目代表了數組的維度。

具有相同類型元素和相同維度的數組,屬於同一個類。如果兩個數組的元素類型相同,但維度不同,那么它們也屬於不同的類。如果兩個數組的元素類型和維度均相同,但長度不同,那么它們還是屬於同一個類。

 

數組的類有哪些成員呢?

 

既然我們知道了數組的類名是什么,那么就去看看數組的類究竟是什么樣的吧?有哪些成員變量?有哪些成員方法?length這個成員變量在哪?是不是沒有length()這個成員方法?

找來找去,在JDK的代碼中沒有找打'[I'這個類。想想也對,'[I'都不是一個合法的標識符,肯定不會出現public class [I {...}這樣的Java代碼。我們暫且不管[I類是誰聲明的,怎么聲明的,先用反射機制一探究竟吧。

Java代碼  
  1. public class Main {  
  2.     public static void main(String[] args) {  
  3.     int a[] = new int[10];  
  4.     Class clazz = a.getClass();  
  5.     System.out.println(clazz.getDeclaredFields().length);  
  6.     System.out.println(clazz.getDeclaredMethods().length);  
  7.     System.out.println(clazz.getDeclaredConstructors().length);  
  8.     System.out.println(clazz.getDeclaredAnnotations().length);  
  9.     System.out.println(clazz.getDeclaredClasses().length);  
  10.     System.out.println(clazz.getSuperclass());  
  11.     }  
  12. }  

 

在SUN JDK 1.6上運行上述代碼,輸出為:

0 0 0 0 0 class java.lang.Object

 

可見,[I這個類是java.lang.Object的直接子類,自身沒有聲明任何成員變量、成員方法、構造函數和Annotation,可以說,[I就是個空類。我們立馬可以想到一個問題:怎么連length這個成員變量都沒有呢?如果真的沒有,編譯器怎么不報語法錯呢?想必編譯器對Array.length進行了特殊處理哇!

 

數組的類在哪里聲明的?

 

先不管為什么沒有length成員變量,我們先搞清楚[I這個類是哪里聲明的吧。既然[I都不是合法的標識符,那么這個類肯定在Java代碼中顯式聲明的。想來想去,只能是JVM自己在運行時生成的了。JVM生成類還是一件很容易的事情,甚至無需生成字節碼,直接在方法區中創建類型數據,就差不多完工了。

 

還沒有實力去看JVM的源代碼,於是翻了翻The JavaTM Virtual Machine Specification  Second Edition,果然得到了驗證,相關內容參考5.3.3 Creating Array Classes

規范的描述很嚴謹,還摻雜了定義類加載器和初始化類加載器的內容。先不管這些,簡單概括一下:

  1. 類加載器先看看數組類是否已經被創建了。如果沒有,那就說明需要創建數組類;如果有,那就無需創建了。
  2. 如果數組元素是引用類型,那么類加載器首先去加載數組元素的類。
  3. JVM根據元素類型和維度,創建相應的數組類。

呵呵,果然是JVM這家伙自個偷偷創建了[I類。JVM不把數組類放到任何包中,也不給他們起個合法的標識符名稱,估計是為了避免和JDK、第三方及用戶自定義的類發生沖突吧。

 

再想想,JVM也必須動態生成數組類,因為Java數組類的數量與元素類型、維度(最多255)有關,相當相當多了,是沒法預先聲明好的。

 

居然沒有length這個成員變量!

 

我們已經發現,偷懶的JVM沒有為數組類生成length這個成員變量,那么Array.length這樣的語法如何通過編譯,如何執行的呢?

讓我們看看字節碼吧!編寫一段最簡單的代碼,使用jclasslib查看字節碼。

Java代碼  
  1. public class Main {  
  2.     public static void main(String[] args) {  
  3.     int a[] = new int[2];  
  4.         int i = a.length;  
  5.     }  
  6. }  

 

使用SUN JDK 1.6編譯上述代碼,並使用jclasslib打開Main.class文件,得到main方法的字節碼:

0 iconst_2                   //將int型常量2壓入操作數棧 1 newarray 10 (int)    //將2彈出操作數棧,作為長度,創建一個元素類型為int, 維度為1的數組,並將數組的引用壓入操作數棧 3 astore_1                 //將數組的引用從操作數棧中彈出,保存在索引為1的局部變量(即a)中 4 aload_1                  //將索引為1的局部變量(即a)壓入操作數棧 5 arraylength            //從操作數棧彈出數組引用(即a),並獲取其長度(JVM負責實現如何獲取),並將長度壓入操作數棧 6 istore_2                 //將數組長度從操作數棧彈出,保存在索引為2的局部變量(即i)中 7 return                    //main方法返回

可見,在這段字節碼中,根本就沒有看見length這個成員變量,獲取數組長度是由一條特定的指令arraylength實現(怎么實現就不管了,JVM總有辦法)。編譯器對Array.length這樣的語法做了特殊處理,直接編譯成了arraylength指令。另外,JVM創建數組類,應該就是由newarray這條指令觸發的了。

 

很自然地想到,編譯器也可以對Array.length()這樣的語法做特殊處理,直接編譯成arraylength指令。這樣的話,我們就可以使用方法調用的風格獲取數組的長度了,這樣看起來貌似也更加OO一點。那為什么不使用Array.length()的語法呢?也許是開發Java的那幫天才對.length有所偏愛,或者拋硬幣拍腦袋隨便決定的吧。 形式不重要,重要的是我們明白了背后的機理。

 

Array in Java

 

最后,對Java中純對象的數組發表點感想吧。

 

相比C/C++中的數組,Java數組在安全性要好很多。C/C++常遇到的緩存區溢出或數組訪問越界的問題,在Java中不再存在。因為Java使用特定的指令訪問數組的元素,這些指令都會對數組的長度進行檢查。如果發現越界,就會拋出java.lang.ArrayIndexOutOfBoundsException。

 

Java數組元素的靈活性比較大。一個數組的元素本身也可以是數組,只要所有元素的數組類型相同即可。我們知道數組的類型和長度無關,因此元素可以是長度不同的數組。這樣,Java的多維數組就不一定是規規矩矩的矩陣了,可以千變萬化。

 

[1] The JavaTM Language Specification, Third Edition. Page 75, “see the specification of Class.getName for details”.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM