參考資料:
1.《java jdk5.0 學習筆記》良葛格 第五章數組
2.如何理解數組的length?
http://blog.csdn.net/treeroot/archive/2005/01/22/264001.aspx
3.關于java數組的深度思考
http://dev.csdn.net/author/DeepNightTwo/afb7e220bdf5423ba656f84b6a183b44.html
一.為什么需要數組?(《java jdk5.0 學習筆記》良葛格)
例如,現在要整理全班的Java小考成績,您希望寫個小程序,全班共有40名學生,所以必須有40個變量來存儲學生的成績?,F在問題來了,根據第3章學過的變量定義方式,難道要定義40個名稱不同的變量來存儲學生的成績數據嗎?
當然不必這么麻煩,Java提供“數組”(Array)讓您可以定義一個以“索引”(Index)作為識別的數據結構。在Java中,可以這么定義一個數組并初始數組內容:
int[] score = {90, 85, 55, 94, 77};
二.什么是數組?
java語言中,數組是一種最簡單的復合數據類型。數組是有序數據的集合,數組中的每個元素具有相同的數據類型,可以用一個統一的數組名和下標來唯一地確定數組中的元素。數組有一維數組和多維數組。
(http://www.linux8.net/html/java/2006-3/29/21_59_23_560.html)
array的定義和使用:使用“[]”做為索引運算符(indexing operator).(《TIJ》)
三.java中數組到底是什么?
1)不管在其他語言中是什么,數組在Java中可得看作一個對象,它有一些值得探討的特性。
(《java jdk5.0 學習筆記》良葛格)
這就意味著與C++中的數組的根本不同,相反,Java中的數組與C++中的STL或Java中的容器類反而更相像一些(只是作為對象,它的方法要比STL中的容器類或者Collection類少很多)。
Java中的數組其實是一個對象,但是確實是一個特殊的對象,實在是太特殊了,以致我們都不好把它多做對象處理。
剛剛開始接觸java數組的人都會聽到一句類似的話:java是純面向對象的語言,他的數組也是一個對象。
于是乎,我就按照一個對象的方式來使用數組,心安理得。直到我接觸到C的數組后,才發現將數組作為一個類來使用在實現上是多么的“不自然”。
首先我們看一下表面現象,數組創建的時候采用的是如下語句:
MyClass[] arr = new MyClass[9];
而普通類采用的是如下語句:
MyClass obj = new MyClass();
就是說,創建數組的時候不使用小括號傳參。使得數組和普通類看起來就有很多不同,因為小括號里的參數是傳遞給構造方法的,進而讓人感覺數組類是沒有構造方法的。
(http://dev.csdn.net/author/DeepNightTwo/afb7e220bdf5423ba656f84b6a183b44.html)
2)java中數組是對象的依據:
a)來源:(http://blog.csdn.net/treeroot/archive/2005/01/22/264001.aspx)
我們確定數組的父類是Object,
? new Object[0].getClass().getSuperClass()? 是Object.class
數組沒有對應的類文件,String對應String.class.但是數組卻沒有,而且他們的
? 類名字很古怪,可以這樣獲得 new int[2].getClass().getName();
? 這是和其他對象最大的不同點,因為數組類是在運行時生成的。
java.lang.reflect.Array是final的,所以數組肯定不是它的子類
? 這個類用來動態生成數組或者操作數組(獲得長度等).
b)來源:(http://dev.csdn.net/author/DeepNightTwo/afb7e220bdf5423ba656f84b6a183b44.html)
再往深了想,還有很多讓人感覺不自然的東西。可以肯定的是,java確實將數組作為了一個類來處理。還是用上面的例子說明:
可以通過以下方法得到MyClass[]的Class實例:arr.getClass()或MyClass[].class。這樣,我就可以向數組類里面“窺探”了。
Class clazz = MyClass[].class;
System.out.println(clazz.getConstructors().length);
打印出來的結果是0;證明數組類確實沒有構造方法。
再看看數組類的“廬山真面目”:
System.out.println(clazz);
輸出是:
[Larraytest.MyClass
對Java Class文件結構稍有了結就知道,這個字符串的意思就是一個元素類型為arraytest.MyClass的一維數組。也就是說,數組類型不是和普通類一樣,以一個全限定路徑名+類名來作為自己的唯一標示的,而是以[+一個或者多個L+數組元素類全限定路徑+類來最為唯一標示的。這個()也是數組和普通類的區別。而這個區別似乎在某種程度上說明數組和普通java類在實現上有很大區別。因為java虛擬機(java指令集)在處理數組類和普通類的時候,肯定會做出區分。我猜想,可能會有專門的java虛擬機指令來處理數組。
分析到這里,我基本上可以肯定:java對數組對象化的操作的支持是指令級的,也就是說java虛擬機有專門針對數組的指令。數組的Class類實例是java虛擬機動態創建動態加載的,其結構與普通java類的Class實例有一些不同。
JDK API中有一個java.lang.reflect.Array類,這個類提供了很多方法(絕大多數是native方法,這在另一個方面證明了java對數組的支持是專用指令支持的,否則用本地方法干嘛^_^),用來彌補我們對數組操作的局限性。
下面這句話用來創建一個一維的、長度為10的、類型為arraytest.MyClass的數組:
arraytest.MyClass[] arr = (arraytest.MyClass[]) Array.newInstance(arraytest.MyClass, 10);
下面這句話用來創建一個二維的、3乘5的、類型為arraytest.MyClass的數組:
int[] arrModel = new int[]{3,5};
Object arrObj = Array.newInstance(Sub.class, arrModel);
當然你可以用一個數組的引用指向上面的二維數組,這里我們用一個Object的引用指向他。
使用的時候,我們也是可以利用Array類提供的方法來實現:
System.out.println(Array.getLength(arrObj);//第一維長度為3
System.out.println(Array.getLength(Array.get(arrObj, 2)));//第二維長度為5,這里如果寫3,就會得到你意想之中的java.lang.ArrayIndexOutOfBoundsException
打印結果是如我所想的:
3
5
對于數組的Class類實例,還有一些奇怪的現象:
在運行代碼 java.lang.reflect.Field fieldarr = clazz.getField("length");的時候,會拋出異常:java.lang.NoSuchFieldException: length,這似乎在說數組類沒有length這個域,而這個域其實是我們用的最多的一個(也就是說這個域是肯定存在的)。我想關于數組的Class類實例、數組的實現等,還有很多“貓膩”在里面。
順便說一句,java數組最多只能是255維的。這個讓人看到了C的影子,嘿嘿。
“Java把數組當作一個java類來處理”說起來容易,用起來自然,但是細細想來,還是有很多不簡單的地方呀。
c)來源:《java jdk5.0 學習筆記》良葛格 第五章數組
從對數組對象的進一步探討,可以稍微了解Java對對象處理的一些方法。首先來看看一維數組的引用名稱的定義:
int[] arr = null;
在這個定義中,arr表示一個可以參考引用自一維數組對象的變量名稱,但是目前將這個名稱參考引用自null,表示還沒有指定這個名稱參考引用自實際的對象。在Java中,=運算用于基本數據類型時,是將值復制給變量,但當它用于對象時,則是將對象指定給參考引用名稱來參考引用。也可以將同一個對象指定給兩個參考引用名稱,當對象的值由其中一個參考引用名稱進行操作而變更時,另一個參考引用名稱所參考引用到的值也會變動。下面來看看范例5.8的示范。
ü 范例5.8? AdvancedArray.java
public class AdvancedArray {
??? public static void main(String[] args) {
??????? int[] arr1 = {1, 2, 3, 4, 5};
??????? int[] tmp1 = arr1;
??????? int[] tmp2 = arr1;
?
??????? System.out.print("通過tmp1取出數組值:");
??????? for(int i = 0; i < tmp1.length; i++)
??????????? System.out.print(tmp1[i] + " ");
?
??????? System.out.print("\n通過tmp2取出數組值:");
??????? for(int i = 0; i < tmp2.length; i++)
??????????? System.out.print(tmp2[i] + " ");
?
??????? tmp1[2] = 9;
??????? System.out.print("\n\n通過tmp1取出數組值:");
??????? for(int i = 0; i < tmp1.length; i++)
??????????? System.out.print(tmp1[i] + " ");
?
??????? System.out.print("\n通過tmp2取出數組值:");
??????? for(int i = 0; i < tmp2.length; i++)
??????????? System.out.print(tmp2[i] + " ");
??????? System.out.println();
??? }
}
執行結果:
?
通過tmp1取出數組值:1 2 3 4 5
通過tmp2取出數組值:1 2 3 4 5
?
通過tmp1取出數組值:1 2 9 4 5
通過tmp2取出數組值:1 2 9 4 5
?
在這個范例中,通過tmp1名稱改變了索引2的元素值,由于tmp2也引用自同一數組對象,所以tmp2取出索引2的元素值是改變后的值。事實上在范例5.8中,有三個引用名稱引用自同一個數組對象,也就是arr1、tmp1與tmp2,所以,如果取出arr1索引2的元素,元素值也會是9。
了解到在Java中數組是一個對象,而使用=指定時是將對象指定給數組名來引用,而不是將數組進行復制。如果想將整個數組的值復制給另一個數組該如何作呢?可以使用循環,將整個數組的元素值遍歷一遍,并指定給另一個數組相對應的索引位置。范例5.10示范了進行數組復制的方法。
public class ArrayCopy {
??? public static void main(String[] args) {
??????? int[] arr1 = {1, 2, 3, 4, 5};
??????? int[] arr2 = new int[5];
?
??????? for(int i = 0; i < arr1.length; i++)
??????????? arr2[i] = arr1[i];
?
??????? for(int i = 0; i < arr2.length; i++)
??? ????????System.out.print(arr2[i] + " ");
??????? System.out.println();
執行結果:
?
另一個進行數組復制的方法是使用System類提供的arraycopy()方法。其語法如下:
System.arraycopy(來源, 起始索引, 目的, 起始索引, 復制長度);
范例5.11改寫了范例5.10,使用System.arraycopy()進行數組復制,執行結果與范例5.10是相同的。
public class ArrayCopy2 {
??? public static void main(String[] args) {
??????? int[] arr1 = {1, 2, 3, 4, 5};
??????? int[] arr2 = new int[5];
?
??????? System.arraycopy(arr1, 0, arr2, 0, arr1.length);
?
??????? for(int i = 0; i < arr2.length; i++)
??????????? System.out.print(arr2[i] + " ");
??????? System.out.println();
??? }
}四、 Java中的數組作為對象帶來的好處
1)越界檢查
2)length field:與傳統的C++中的數組相比,length字段可以方便的得到數組的大?。坏⒁?,僅僅可以得到數組的大小,不能得到數組中實際包含多少個元素,因為length 只會告訴我們最多可將多少元素置入那個數組。
3) 初始化:對象數組在創建之初會自動初始化成null,由原始數據類型構成的數組會自動初始化成零(針對數值類型),(Char)0 (針對字符類型)或者false (針對布爾類型)。
4) 數組作為返回值:首先,既然數組是對象,那么就可以把這個對象作為返回值;而且,不必擔心那個數組的是否可用只要需要它就會自動存在而且垃圾收集器會在我們完成后自動將其清除