下面我帶大家一起加深一下對Java數組的認識:
1.理解數組
數組也是一種數據類型,本身就是一種引用類型,我們從它的初始化方法,通過關鍵字new去完成定義及初始化就可以知道。
數組的長度是不變的,一旦數組完成初始化后,它的長度就固定下來了,在內存中占有的空間也就固定了,即使里面的數據被清空了,占有的空間還是保留下來了,依然是屬於數組的,當然長度依舊是不變的。
數組里邊存放的數據類型要一致,可以基本數據類型,也可以是引用數據類型,但是唯一的標准就是相同的類型。在Java中,類與類是支持繼承關系的,所以就可能造成數組里面可以存在多中數據類型的假象:
示例:
class Animal { public String name; public Animal(String name) { this.name = name; } } class Cat extends Animal { public Cat(String name) { super(name); } } class Dog extends Animal { public Dog(String name) { super(name); } } public class ArrayDemo { public static void main(String[] args) { Animal[] animals = new Animal[2]; Cat cat = new Cat("little cat"); Dog dog = new Dog("little dog"); animals[0] = cat; animals[1] = dog; System.out.println(animals[0].name); System.out.println(animals[1].name); } }
這樣看上去,好像數組里面存放了Cat類型和Dog類型,但是實際上他們都是Animal類型,數組里面都是相同的類型,請大家不要搞混淆了,哈哈。
2.定義數組和初始化數組
定義數組的語法格式:
type[] arrayName;
type arrayName[];
這兩種格式,我是強烈推薦第一種的,第一種格式給人非常的明確,定義了一個變量,它是type數組類型,但是第二種就可能給人一種假象,定義了一種變量,它是type類型的,為了不給別人誤會,還是選擇第一種好點咯。
定義完了數組,這是內存中還沒給數組分配空間,真正分配內存空間的時候就是在給數組初始化的時候。
初始化數組分兩種:
靜態初始化:
arrayName = new type[]{element1,element2, element3......};
由上面很容易看出靜態的初始化就是顯式指定數組每個元素的初始值,由系統決定數組的長度。
動態初始化:
arrayName = new type[length];
由上面看出動態初始化就是只指定數組的長度,由系統為數組分配初始值。
對於不同類型,系統分配的初始值也是不一樣的。
類型 |
初始值 |
整數類型(byte、short、int、long) |
0 |
浮點類型(float、double) |
0.0 |
字符類型(char) |
'\u0000'(代表空格) |
布爾類型(boolean) |
false |
引用類型(類、接口、數組) |
null |
你可能產生這樣的猜想:不是可以同時使用靜態初始化和動態初始化嗎?
答案是不可以的,編譯器是提示Cannot define dimension expressions when an array initializer is provided,因為你定義了長度,說明你已經初始化完了,我想想也是覺得這樣不合理的,如果可以兩者混合使用的話,那么我定義好了數組的長度后,系統是不是要給它附上默認的初始值,但是你后面又是靜態的初始化,指定了特定的值,系統又要為你改變里面的值,這樣做系統真累,我也覺得累。當然編譯器是不支持兩者混合使用的,大家注意了!
3.數組的使用
數組的使用就是引用數組的索引,數組的索引是從0開始的,到length-1為止,如果超過了這樣范圍就是拋出java.lang.ArrayIndexOutOfBoundException異常,就是數組索引越界異常,引用數組的使用太簡單了,下面就簡單用for和foreach來演示一下訪問:
public class ArrayDemo2 { public static void main(String[] args) { String[] strings = new String[]{"hello", "world"}; for(int i = 0; i < strings.length; i++) System.out.println(strings[i]); for(String string : strings) System.out.println(string); } }
4.內存中的數組:
type[] arrayName = new type[]{element1, element2, element3......};
arrayName就是一個引用的變量,這個數組的引用變量可以指向任何有效的內存,只有當指向有效的內存后才可以方位數組元素,可以這么說,引用變量是訪問真是對象的根本方式。
實際的數組對象被存儲在堆(heap)內存中,如果引用該數組獨享的數組引用變量是一個局部變量的話,那么它被存儲在棧(stack)內存中,示意圖如下:
由這里我們可以很清楚的看出局部數組引用變量是怎么訪問到內存中的數組的,黨當一個方法調用完后,局部變量就沒了,也就是從棧內存中消失了,如果堆內存中數組不再有任何引用變量指向自己,這個數組將成為垃圾,該數組所占的內存就會被系統的垃圾回收機制回收,因此,為了讓垃圾回收機制回收一個數組所占的內存空間,可以將數組變量賦值為null,也就切斷了數組引用變量和實際數組之間的關系,實際的數組也就成為了垃圾。
在這里,我們讓一個引用變量指向另外一個實際的數組的時候,可能產生數組長度可變的假象,大家來看看一個例子:
public class ArrayDemo3 { public static void main(String[] args) { int[] a = new int[]{1, 2}; int[] b = new int[4]; System.out.println("length of b:" + b.length); b = a; System.out.println("length of b:" + b.length); } }
結果:
length of b:4
length of b:2
從上面看我們b數組的長度好像發生了變化,但是實際上不是這樣的,我們來分析一下內存中的變化:
先是這樣:
然后這樣:
我們所說的數組的長度不變是針對堆內存中真正數組的長度,引用變量是可以改變指向的,指到哪里肯定就顯示指到的數組的長度了,但是真正的長度是不曾改變的。
5.操作數組的工具類:
Java提供的Arrays類里包含的一些static修飾的方法可以直接操作數組
int binarySearch(type[] a, type key):使用二分法查詢key元素值在a數組中出現的索引,如果a數組不包含key元素值,就返回負數,調用該方法的前提是數組中的元素已經按照升序排列好了。
type[] copyOf(type[] original, int newLength):這個方法可以將original數組復制到一個新的數組,這個新的數組的長度為newLength。
boolean equals(type[] a, type[] b):如果a數組和b數組的長度相等,而且數組中的元素一一相等,該方法就返回true。
void fill(type[] a, type val):該方法會將數組中的全部元素賦值為val。
void sort(type[] a):該方法對a數組進行(升序)排序。