什么是数组?
数组是编程语言中一种常见的数据结构,可以用于储存多个数据,每个数组元素存放一个数据。也可把它看做一个存放数据的容器,数组既可以存储基本数据类型,也可以存储引用数据类型。但是一个数组中只能存储一种数据类型的数据。
定义数组
格式:
- 格式1:数据类型[] 数组名;
实例:int[] a; 定义了一个int类型的数组a; - 格式2:数据类型 数组名[];
实例 int a[]; 同上;
通常建立使用第一种方式来定义数组。
注意:
定义数组时不能指定数组的长度,因为定义数组时仅仅表示定义一个引用变量,这时引用变量还未指向任何内存空间,所以这时是不能使用的,还有对数组进行初始化之后才能使用。
数组初始化
Java中的数组必须先初始化后才能使用,所谓初始化其实就是为数字中的元素分配空间,并为每个数字元素赋值。一旦初始化完成,数组中的占用内存被固定下来,数组的长度将不可再变;
静态初始化
由程序员显式地给出初始化值,由系统决定数组长度;
初始化格式:
arrayName=new type[] {element1,element2...};
实例:
//定义一个名为a的数组
int[] a;
//初始化刚才定义的数组a(静态方法)
a=new int[]{1,2,3,4,5};
静态初始化还有以下的方法:将定义和初始化同时完成。(建议使用这种方法)
type[] ArrayName={element1,element2...}
实例:
int[] b={5,4,3,2,1};
动态初始化
初始化时程序员只指定数组长度,由系统为数组分配初始值。
格式:
type[] arrayName=new type[length];
实例:
//动态初始化,初始化一个长度为5的数组
int[] c=new int[5];
执行动态初始化时,程序员只需指定数组的长度,即为每个元素指定所需空间,系统将为这些元素分配初始值;
数组元素的类型 | 数组元素的初始值 |
---|---|
整数类型(byte、short、int、long) | 0 |
浮点类型(float、double) | 0.0 |
字符型(char) | ‘\u0000’ |
布尔值(boolean) | false |
引用数据类型 | null |
注意
不要同时对数组进行静态初始化又进行动态初始化。
使用数组
// 给数组元素赋值
c[0]=1;
c[2]=2;
//访问数组元素
//通过数组的索引访问,数组的索引从0开始
System.out.println(c[0]);
//输出数组中的元素
如果访问数组的内存小于0或者大于数组长度-1则会报错:
System.out.println(c[6]);
// Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 6
//遍历数组a
//a.length表示数组的长度
for(int i=0;i<a.length;i++){
System.out.println(a[i]);
}
/*1
2
3
4
5
*/
深入数组
数组是一种引用数据类型,数组引用变量只是一个引用数组元素和数组变量在内存中是分开存放的。
内存中的数组
数组引用变量只是一个引用,这个引用变量可以指向任何有效的内存,只有当该引用变量指向有效的内存之后,才可通过该数组变量访问数组元素。引用变量是访问真实对象的根本方式。
实际对象被储存在堆内存中,如果引用该数组的数组引用是一个局部变量,那么它被存储在栈内存中。
栈内存和堆内存的区别
当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法中定义的变量将会逐个放进这块栈内存里,随着这个方法的结束,这个方法的栈内存也会被销毁。因此所有在方法中定义的局部变量都是放在栈在内存中的。
在程序中创建一个对象时,这个对象将被保存在运行时数据区中,以便反复利用,因为创建对象时的成本很大。这个运行时数据区就是堆内存。堆内存中的数据不会随方法的结束而销毁,即使方法结束,这个对象可能被另一个引用变量所引用。则这个对象依然不会被销毁。只有当一个对象没有任何引用变量引用它时,系统的垃圾回收机器才会在合适的时候回收它。为了让垃圾回收器回收它,可以将该数组变量赋值为null,也就切断了数组引用变量和数组之间的关系。
实例图解
package org.westos.array_practice;
public class array02 {
public static void main(String[] args) {
//静态化初始
int[] a={1,2,3};
System.out.println(a);
//动态初始并赋值
int[] b=new int[3];
b[0]=10;
b[1]=20;
b[2]=30;
System.out.println(b);
// 将b数组的引用指向a数组
b=a;
System.out.println(a);
System.out.println(b);
// 运行结果
/*[I@1540e19d
[I@677327b6
[I@1540e19d
[I@1540e19d*/
}
}
第一步:
将程序从方法区读入到栈内存;
第二步:
定义并初始化数组a,在堆内存中按照定义数组的大小开辟一块区域,开始数组中元素都是0,由于是静态初始化,所以很快被初始化值所覆盖;
第三步:
定义并初始化数组b,和数组a相似,但是由于b是动态初始化,所以只初始化了数组长度,赋值由下面的赋值语句来完成;
第四步:
让b数组引用指向a指向的数组,这样a,b就指向了同一个数组,所以打印出来的地址是一样的。而第三步的指向就作废,b原先指向的数组由于没有数组引用就变成了垃圾,等待回收垃圾,但它的长度依然不会改变。
栈内存中的数据和堆内存数据的区别
来看一个实例
package org.westos.array_practice;
public class array03 {
public static void main(String[] args) {
int a=10;
int b=20;
int[] c={10,20};
System.out.println(a);//10
System.out.println(b);//20
// 调用方法
Sum(a,b);
System.out.println(a); //10
System.out.println(b);//20
for (int i=0;i<c.length;i++){
System.out.println(c[i]);
} //10,20
//调用方法
Sum(c);
for (int i=0;i<c.length;i++){
System.out.println(c[i]);
}//20,30
}
public static void Sum(int x,int y) {
x+=10;
y+=10;
System.out.println(x); //20
System.out.println(y);//30
}
public static void Sum(int[] args) {
args[0]+=10;
args[1]+=10;
}
}
可以看到Sum函数都是给传入的数或数组元素加10,为什么调用之后,传入的数字没有被改变,而数组的元素却被改变了?
这是因为int是基本数据类型,它定义的变量随着程序的和执行被放在栈内存中,而栈内存中的语句运行完就会被移除,相当于第一个Sum函数刚运行完就失效了,所以输出的a,b就是刚开始定义的那两个值,没有发生改变;
然而,int[] 是数组的定义类型,属于引用数据类型,它真实的数据存储在堆内存中,而堆内存中的数据不会随着程序执行完而被移除,只是移除数组引用,所以堆内存中的数据被改变但是它现在还没有被回收。在主函数中又有c这个引用变量来指向它,所以可以输出并且永久被改变;
方法重载
上面的实例中还用到了方法重载;定义了两个方法名相同的函数,但是参数个数和参数类型都不相同,由内部机制自动匹配参数,比如传入两个整型数就会调用第一个方法,传入一个数组就会调用第二个方法,这样做是为了提高开发效率。
多维数组
二维数组
在java中二维数组的定义机制其实是让引用变量指向一个数组,而这个数组中的每个元素又是引用变量,每一个引用变量又指向一个数组,这样看起来就是二维数组。
定义语法
type[] [] arrName;
这样定义的数组其实还是一维数组,只是其数组元素是引用。
初始化
arrName=new type[length][];
length是存储引用的数组的,相当于二维数组中的行数;
//定义一个数组
int[][] arrname;
//初始化
arrname=new int[4][];
第一步:
定义了一个引用变量,现在还未指向任何内存空间;
第二步:
初始化这个一维数组,长度为4,由于这个数组存储的元素是引用类型,所以初始化值为null,并将第一步定义的引用变量指向它;
第三步:
再定义四个一维数组,初始化元素数值为0,将各个数组的引用变量指向第二步定义的数组中的各个元素。
静态初始化二维数组
//静态初始化一个二维数组
String[][] arrname1={new String[3],new String[]{"hello"}};
//遍历二维数组
for (int i=0;i<arrname1.length;i++){
for (int j=0;j<arrname1[i].length;j++){
System.out.println(arrname1[i][j]);
}
}
/*null
null
null
hello*/
}
动态初始化二维数组
String[][] arrname2=new String[4][];
//遍历赋值
for (int i=0;i<arrname2.length;i++){
arrname2[i]=new String[2];
}