參考資料:
1.《java jdk5.0 學(xué)習(xí)筆記》良葛格 第五章數(shù)組
2.如何理解數(shù)組的length?
http://blog.csdn.net/treeroot/archive/2005/01/22/264001.aspx
3.關(guān)于java數(shù)組的深度思考
http://dev.csdn.net/author/DeepNightTwo/afb7e220bdf5423ba656f84b6a183b44.html


一.為什么需要數(shù)組?(《java jdk5.0 學(xué)習(xí)筆記》良葛格)
例如,現(xiàn)在要整理全班的Java小考成績(jī),您希望寫個(gè)小程序,全班共有40名學(xué)生,所以必須有40個(gè)變量來存儲(chǔ)學(xué)生的成績(jī)?,F(xiàn)在問題來了,根據(jù)第3章學(xué)過的變量定義方式,難道要定義40個(gè)名稱不同的變量來存儲(chǔ)學(xué)生的成績(jī)數(shù)據(jù)嗎?

當(dāng)然不必這么麻煩,Java提供“數(shù)組”(Array)讓您可以定義一個(gè)以“索引”(Index)作為識(shí)別的數(shù)據(jù)結(jié)構(gòu)。在Java中,可以這么定義一個(gè)數(shù)組并初始數(shù)組內(nèi)容:

int[] score = {90, 85, 55, 94, 77};

二.什么是數(shù)組?
java語言中,數(shù)組是一種最簡(jiǎn)單的復(fù)合數(shù)據(jù)類型。數(shù)組是有序數(shù)據(jù)的集合,數(shù)組中的每個(gè)元素具有相同的數(shù)據(jù)類型,可以用一個(gè)統(tǒng)一的數(shù)組名和下標(biāo)來唯一地確定數(shù)組中的元素。數(shù)組有一維數(shù)組和多維數(shù)組。
(http://www.linux8.net/html/java/2006-3/29/21_59_23_560.html)

array的定義和使用:使用“[]”做為索引運(yùn)算符(indexing operator).(《TIJ》)

三.java中數(shù)組到底是什么?

1)不管在其他語言中是什么,數(shù)組在Java中可得看作一個(gè)對(duì)象,它有一些值得探討的特性。
(《java jdk5.0 學(xué)習(xí)筆記》良葛格)

這就意味著與C++中的數(shù)組的根本不同,相反,Java中的數(shù)組與C++中的STL或Java中的容器類反而更相像一些(只是作為對(duì)象,它的方法要比STL中的容器類或者Collection類少很多)。

Java中的數(shù)組其實(shí)是一個(gè)對(duì)象,但是確實(shí)是一個(gè)特殊的對(duì)象,實(shí)在是太特殊了,以致我們都不好把它多做對(duì)象處理。

剛剛開始接觸java數(shù)組的人都會(huì)聽到一句類似的話:java是純面向?qū)ο蟮恼Z言,他的數(shù)組也是一個(gè)對(duì)象。
于是乎,我就按照一個(gè)對(duì)象的方式來使用數(shù)組,心安理得。直到我接觸到C的數(shù)組后,才發(fā)現(xiàn)將數(shù)組作為一個(gè)類來使用在實(shí)現(xiàn)上是多么的“不自然”。
首先我們看一下表面現(xiàn)象,數(shù)組創(chuàng)建的時(shí)候采用的是如下語句:
MyClass[] arr = new MyClass[9];
而普通類采用的是如下語句:
MyClass obj = new MyClass();
就是說,創(chuàng)建數(shù)組的時(shí)候不使用小括號(hào)傳參。使得數(shù)組和普通類看起來就有很多不同,因?yàn)樾±ㄌ?hào)里的參數(shù)是傳遞給構(gòu)造方法的,進(jìn)而讓人感覺數(shù)組類是沒有構(gòu)造方法的。
(http://dev.csdn.net/author/DeepNightTwo/afb7e220bdf5423ba656f84b6a183b44.html)

2)java中數(shù)組是對(duì)象的依據(jù):

a)來源:(http://blog.csdn.net/treeroot/archive/2005/01/22/264001.aspx)

我們確定數(shù)組的父類是Object,
? new Object[0].getClass().getSuperClass()? 是Object.class

數(shù)組沒有對(duì)應(yīng)的類文件,String對(duì)應(yīng)String.class.但是數(shù)組卻沒有,而且他們的
? 類名字很古怪,可以這樣獲得 new int[2].getClass().getName();
? 這是和其他對(duì)象最大的不同點(diǎn),因?yàn)閿?shù)組類是在運(yùn)行時(shí)生成的。

java.lang.reflect.Array是final的,所以數(shù)組肯定不是它的子類
? 這個(gè)類用來動(dòng)態(tài)生成數(shù)組或者操作數(shù)組(獲得長(zhǎng)度等).



b)來源:(http://dev.csdn.net/author/DeepNightTwo/afb7e220bdf5423ba656f84b6a183b44.html)

再往深了想,還有很多讓人感覺不自然的東西??梢钥隙ǖ氖?,java確實(shí)將數(shù)組作為了一個(gè)類來處理。還是用上面的例子說明:
可以通過以下方法得到MyClass[]的Class實(shí)例:arr.getClass()或MyClass[].class。這樣,我就可以向數(shù)組類里面“窺探”了。
Class clazz = MyClass[].class;
System.out.println(clazz.getConstructors().length);
打印出來的結(jié)果是0;證明數(shù)組類確實(shí)沒有構(gòu)造方法。


再看看數(shù)組類的“廬山真面目”:
System.out.println(clazz);
輸出是:
[Larraytest.MyClass

對(duì)Java Class文件結(jié)構(gòu)稍有了結(jié)就知道,這個(gè)字符串的意思就是一個(gè)元素類型為arraytest.MyClass的一維數(shù)組。也就是說,數(shù)組類型不是和普通類一樣,以一個(gè)全限定路徑名+類名來作為自己的唯一標(biāo)示的,而是以[+一個(gè)或者多個(gè)L+數(shù)組元素類全限定路徑+類來最為唯一標(biāo)示的。這個(gè)()也是數(shù)組和普通類的區(qū)別。而這個(gè)區(qū)別似乎在某種程度上說明數(shù)組和普通java類在實(shí)現(xiàn)上有很大區(qū)別。因?yàn)閖ava虛擬機(jī)(java指令集)在處理數(shù)組類和普通類的時(shí)候,肯定會(huì)做出區(qū)分。我猜想,可能會(huì)有專門的java虛擬機(jī)指令來處理數(shù)組。

分析到這里,我基本上可以肯定:java對(duì)數(shù)組對(duì)象化的操作的支持是指令級(jí)的,也就是說java虛擬機(jī)有專門針對(duì)數(shù)組的指令。數(shù)組的Class類實(shí)例是java虛擬機(jī)動(dòng)態(tài)創(chuàng)建動(dòng)態(tài)加載的,其結(jié)構(gòu)與普通java類的Class實(shí)例有一些不同。

JDK API中有一個(gè)java.lang.reflect.Array類,這個(gè)類提供了很多方法(絕大多數(shù)是native方法,這在另一個(gè)方面證明了java對(duì)數(shù)組的支持是專用指令支持的,否則用本地方法干嘛^_^),用來彌補(bǔ)我們對(duì)數(shù)組操作的局限性。
下面這句話用來創(chuàng)建一個(gè)一維的、長(zhǎng)度為10的、類型為arraytest.MyClass的數(shù)組:
arraytest.MyClass[] arr = (arraytest.MyClass[]) Array.newInstance(arraytest.MyClass, 10);

下面這句話用來創(chuàng)建一個(gè)二維的、3乘5的、類型為arraytest.MyClass的數(shù)組:
int[] arrModel = new int[]{3,5};
Object arrObj = Array.newInstance(Sub.class, arrModel);
當(dāng)然你可以用一個(gè)數(shù)組的引用指向上面的二維數(shù)組,這里我們用一個(gè)Object的引用指向他。
使用的時(shí)候,我們也是可以利用Array類提供的方法來實(shí)現(xiàn):

System.out.println(Array.getLength(arrObj);//第一維長(zhǎng)度為3
System.out.println(Array.getLength(Array.get(arrObj, 2)));//第二維長(zhǎng)度為5,這里如果寫3,就會(huì)得到你意想之中的java.lang.ArrayIndexOutOfBoundsException

打印結(jié)果是如我所想的:
3
5

對(duì)于數(shù)組的Class類實(shí)例,還有一些奇怪的現(xiàn)象:
在運(yùn)行代碼 java.lang.reflect.Field fieldarr = clazz.getField("length");的時(shí)候,會(huì)拋出異常:java.lang.NoSuchFieldException: length,這似乎在說數(shù)組類沒有l(wèi)ength這個(gè)域,而這個(gè)域其實(shí)是我們用的最多的一個(gè)(也就是說這個(gè)域是肯定存在的)。我想關(guān)于數(shù)組的Class類實(shí)例、數(shù)組的實(shí)現(xiàn)等,還有很多“貓膩”在里面。

順便說一句,java數(shù)組最多只能是255維的。這個(gè)讓人看到了C的影子,嘿嘿。

“Java把數(shù)組當(dāng)作一個(gè)java類來處理”說起來容易,用起來自然,但是細(xì)細(xì)想來,還是有很多不簡(jiǎn)單的地方呀。



c)來源:《java jdk5.0 學(xué)習(xí)筆記》良葛格 第五章數(shù)組

從對(duì)數(shù)組對(duì)象的進(jìn)一步探討,可以稍微了解Java對(duì)對(duì)象處理的一些方法。首先來看看一維數(shù)組的引用名稱的定義:

int[] arr = null;

在這個(gè)定義中,arr表示一個(gè)可以參考引用自一維數(shù)組對(duì)象的變量名稱,但是目前將這個(gè)名稱參考引用自null,表示還沒有指定這個(gè)名稱參考引用自實(shí)際的對(duì)象。在Java中,=運(yùn)算用于基本數(shù)據(jù)類型時(shí),是將值復(fù)制給變量,但當(dāng)它用于對(duì)象時(shí),則是將對(duì)象指定給參考引用名稱來參考引用。也可以將同一個(gè)對(duì)象指定給兩個(gè)參考引用名稱,當(dāng)對(duì)象的值由其中一個(gè)參考引用名稱進(jìn)行操作而變更時(shí),另一個(gè)參考引用名稱所參考引用到的值也會(huì)變動(dòng)。下面來看看范例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取出數(shù)組值:");

??????? for(int i = 0; i < tmp1.length; i++)

??????????? System.out.print(tmp1[i] + " ");

?

??????? System.out.print("\n通過tmp2取出數(shù)組值:");

??????? for(int i = 0; i < tmp2.length; i++)

??????????? System.out.print(tmp2[i] + " ");

?

??????? tmp1[2] = 9;

??????? System.out.print("\n\n通過tmp1取出數(shù)組值:");

??????? for(int i = 0; i < tmp1.length; i++)

??????????? System.out.print(tmp1[i] + " ");

?

??????? System.out.print("\n通過tmp2取出數(shù)組值:");

??????? for(int i = 0; i < tmp2.length; i++)

??????????? System.out.print(tmp2[i] + " ");

??????? System.out.println();

??? }

}

執(zhí)行結(jié)果:

?

通過tmp1取出數(shù)組值:1 2 3 4 5

通過tmp2取出數(shù)組值:1 2 3 4 5

?

通過tmp1取出數(shù)組值:1 2 9 4 5

通過tmp2取出數(shù)組值:1 2 9 4 5

?

在這個(gè)范例中,通過tmp1名稱改變了索引2的元素值,由于tmp2也引用自同一數(shù)組對(duì)象,所以tmp2取出索引2的元素值是改變后的值。事實(shí)上在范例5.8中,有三個(gè)引用名稱引用自同一個(gè)數(shù)組對(duì)象,也就是arr1、tmp1與tmp2,所以,如果取出arr1索引2的元素,元素值也會(huì)是9。


了解到在Java中數(shù)組是一個(gè)對(duì)象,而使用=指定時(shí)是將對(duì)象指定給數(shù)組名來引用,而不是將數(shù)組進(jìn)行復(fù)制。如果想將整個(gè)數(shù)組的值復(fù)制給另一個(gè)數(shù)組該如何作呢?可以使用循環(huán),將整個(gè)數(shù)組的元素值遍歷一遍,并指定給另一個(gè)數(shù)組相對(duì)應(yīng)的索引位置。范例5.10示范了進(jìn)行數(shù)組復(fù)制的方法。

ü范例5.10? ArrayCopy.java

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();

??? }

}

執(zhí)行結(jié)果:

?

1 2 3 4 5

另一個(gè)進(jìn)行數(shù)組復(fù)制的方法是使用System類提供的arraycopy()方法。其語法如下:

System.arraycopy(來源, 起始索引, 目的, 起始索引, 復(fù)制長(zhǎng)度);

范例5.11改寫了范例5.10,使用System.arraycopy()進(jìn)行數(shù)組復(fù)制,執(zhí)行結(jié)果與范例5.10是相同的。

ü范例5.11? ArrayCopy2.java

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中的數(shù)組作為對(duì)象帶來的好處
1)越界檢查

2)length field:與傳統(tǒng)的C++中的數(shù)組相比,length字段可以方便的得到數(shù)組的大小;但要注意,僅僅可以得到數(shù)組的大小,不能得到數(shù)組中實(shí)際包含多少個(gè)元素,因?yàn)閘ength 只會(huì)告訴我們最多可將多少元素置入那個(gè)數(shù)組。

3) 初始化:對(duì)象數(shù)組在創(chuàng)建之初會(huì)自動(dòng)初始化成null,由原始數(shù)據(jù)類型構(gòu)成的數(shù)組會(huì)自動(dòng)初始化成零(針對(duì)數(shù)值類型),(Char)0 (針對(duì)字符類型)或者false (針對(duì)布爾類型)。

4) 數(shù)組作為返回值:首先,既然數(shù)組是對(duì)象,那么就可以把這個(gè)對(duì)象作為返回值;而且,不必?fù)?dān)心那個(gè)數(shù)組的是否可用只要需要它就會(huì)自動(dòng)存在而且垃圾收集器會(huì)在我們完成后自動(dòng)將其清除