目录
一、什么是数组
二、数组的创建
1.静态初始化
2.动态初始化
三、数组的使用
1.基本使用
2.数组作为方法的参数
2.1引用类型
2.2java中的null
2.3虚拟机栈和堆
3.数组中作为方法的返回值
四、二维数组
一、什么是数组
数组是相同类型数据的有序集合,数组中每一个数据称作一个数组元素,每个数组元素都可以通过一个下标来访问
注意:数组中的每个元素都必须是相同类型
二、数组的创建
1.静态初始化
基本语法:
//格式一
数据类型[] 数组名称 = { 初始化数据 };
//格式二
数据类型 数组名称[] = { 初始化数据 };
代码示例:
//格式一
int[] arr = {1, 2, 3};
//格式二
int arr[] = {1, 2, 3};
2.动态初始化
基本语法:
//格式一
数据类型[] 数组名称 = new 数据类型 [] { 初始化数据 };
//格式二
数据类型 数组名称[] = new 数据类型 [] { 初始化数据 };
代码示例:
//格式一
int[] arr = new int[]{1, 2, 3};
//格式二
int arr[] = new int[]{1, 2, 3};
从上可以看出格式二和C语言相似,但是更推荐写成 int[ ] arr 的形式,int和 [ ] 是一个整体
三、数组的使用
1.基本使用
代码示例:获取长度 与 访问元素
public class TestDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
// 获取数组长度
System.out.println("length: " + arr.length); // 执行结果: 3
// 访问数组中的元素
System.out.println(arr[1]); // 执行结果: 2
System.out.println(arr[0]); // 执行结果: 1
arr[2] = 100;
System.out.println(arr[2]); // 执行结果: 100
}
}
注意事项
- 使用 arr.length 能够获取到数组的长度,这个操作为成员访问操作符,在面向对象中会经常用到
- 使用 [ ] 按下标取数组元素,需要注意,下标从 0 开始计数
- 使用 [ ] 操作既能读取数据,也能修改数据
- 下标访问操作不能超出有效范围 [0, length - 1] ,如果超出有效范围,会出现下标越界异常
代码示例:下标越界
public class TestDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
System.out.println(arr[100]);
}
}
//执行结果
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
at TestDemo.main(TestDemo.java:7)
代码示例:遍历数组
public class TestDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
//执行结果
1
2
3
代码示例:使用 for-each 遍历数组
public class TestDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
for (int x : arr) {
System.out.println(x);
}
}
}
2.数组作为方法的参数
2.1引用类型
代码示例:参数传内置类型
public static void main(String[] args) {
int num = 0;
func(num);
System.out.println("num = " + num);
}
public static void func(int x) {
x = 10;
System.out.println("x = " + x);
}
// 执行结果
x = 10
num = 0
从上述代码中可以发现:修改形参 x 的值,不影响实参的 num 值(这是因为形参本质是实参的一份拷贝,形参的改变不会影响实参)
图解如下:
代码示例:参数传数组类型
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3};
func(arr);
System.out.println("arr[0] = " + arr[0]);
}
public static void func(int[] a) {
a[0] = 10;
System.out.println("a[0] = " + a[0]);
}
// 执行结果
a[0] = 10
arr[0] = 10
从上述代码可以发现:在方法内部修改数组 a 中内容时,方法外部数组 arr 中的内容也发生改变,此时数组名 arr 是一个 "引用" ,当传参的时候,是按照引用传参
什么是引用?
引用其实就是地址的别名
举个例子:张三、李四、王五、老六这四个人上大学住在同一个宿舍,这间宿舍的编号为 "14-308",那么这个编号 "14-308" 其实就是这间宿舍的地址,然后他们四个又个这间宿舍取了个名字:"高颜值宿舍",那么这个 "高颜值宿舍" 也是这间宿舍的地址,也就是 "14-308" 的别名
针对 int[] arr = new int[]{1, 2, 3} 这样的代码, 内存布局如下:
- 当我们创建 new int[]{1, 2, 3} 的时候,相当于创建了一块内存空间保存三个 int 类型的元素
- 接下来执行 int[] arr = new int[]{1, 2, 3} 相当于又创建了一个 int[] 变量,这个变量是一个引用类型,里面只保存了一个整数(数组的起始内存地址)
- 接下来我们进行传参相当于 int[] a = arr ,内存布局如图
- 接下来我们修改 a[0] ,此时是根据 0x100 这样的地址找到对应的内存位置,将值改成 100
此时已经将 0x100 地址的数据改成了 100 ,那么根据实参 arr 来获取数组内容 arr[0] ,本质上也是获取 0x100 地址上的数据,也是 100
总结: 所谓的 "引用" 本质上只是存了一个地址,Java 将数组设定成引用类型,这样的话后续进行数组参数传参,其实只是将数组的地址传入到函数形参中,这样可以避免对整个数组的拷贝(数组可能比较长,那么拷贝开销就会很大)
可能有人会有疑问,不是说形参是实参的一份拷贝,形参的改变不会影响是实参吗?那这里怎么有改变了原数组的值呢?下面我们用一段代码来验证一下
代码示例:
public static void main(String[] args) {
int[] arr1 = new int[]{1,2,3};
System.out.println("调用前arr1: "+arr1);
System.out.println(Arrays.toString(arr1));
func(arr1);
System.out.println("调用后arr1: "+arr1);
System.out.println(Arrays.toString(arr1));
}
public static void func(int[] arr2) {
System.out.println("改变前arr2: "+arr2);
System.out.println(Arrays.toString(arr2));
int[] arr3 = {4,5,6};
arr2=arr3;
arr2[0] = 100;
System.out.println("改变后arr2: "+arr2);
System.out.println(Arrays.toString(arr2));
}
//执行结果
调用前arr1: [I@1b6d3586
[1, 2, 3]
改变前arr2: [I@1b6d3586
[1, 2, 3]
改变后arr2: [I@4554617c
[100, 5, 6]
调用后arr1: [I@1b6d3586
[1, 2, 3]
通过上述代码我们可以发现:形参数组 arr2 在改变前,arr2 存的地址和地址所指向的那块空间的存的数据元素是一致的;当 arr2 存的地址改变时,那么它指向的空间也发生改变,对着块空间存的数据元素进行改变,并不会影响原数组 arr1 指向的空间的数据元素
2.2java中的null
null 在 Java 中表示 "空引用" ,也就是一个无效的引用
public static void main(String[] args) {
int[] arr = null;
System.out.println(arr[0]);
}
//执行结果
Exception in thread "main" java.lang.NullPointerException
at TestDemo.main(TestDemo.java:8)
null 的作用类似于 C 语言中的 NULL (空指针),都是表示一个无效的内存位置,因此不能对这个内存进行任何读写操作,一旦尝试读写, 就会抛出 NullPointerException.
注意:Java 中并没有约定 null 和 0 号地址的内存有任何关联
2.3虚拟机栈和堆
- 局部变量和引用保存在栈上, new 出的对象保存在堆上
- 堆的空间非常大,栈的空间比较小
- 堆是整个 JVM 共享一个,而栈每个线程具有一份(一个 Java 程序中可能存在多个栈)
3.数组中作为方法的返回值
代码示例:写一个方法,将数组中的每个元素都 * 2
// 直接修改原数组
class TestDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
transform(arr);
printArray(arr);
}
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
public static void transform(int[] arr) {
for (int i = 0; i < arr.length; i++) {
arr[i] = arr[i] * 2;
}
}
}
这个代码固然可行,但是破坏了原有数组,有时候我们不希望破坏原数组,就需要在方法内部创建一个新的数组, 并由方法返回出来
// 返回一个新的数组
class TestDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
int[] output = transform(arr);
printArray(output);
}
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
public static int[] transform(int[] arr) {
int[] ret = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
ret[i] = arr[i] * 2;
}
return ret;
}
}
这样的话就不会破坏原有数组了,另外由于数组是引用类型,返回的时候只是将这个数组的首地址返回给函数调用者,没有拷贝数组内容,从而比较高效
四、二维数组
二维数组本质上也就是一维数组,只不过每个元素又是一个一维数组
基本语法:
数据类型[][] 数组名称 = new 数据类型 [行数][列数] { 初始化数据 };
代码示例:
public class TestDemo {
public static void main(String[] args) {
int[][] arr = new int[][]{{1,2,3},{4,5,6}};
//打印方式一
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j]+" ");
}
System.out.println();
}
//打印方式二
for (int[] temp:arr) {
for (int x:temp) {
System.out.print(x+" ");
}
System.out.println();
}
//打印方式三
System.out.println(Arrays.deepToString(arr));
}
}
// 执行结果
1 2 3
4 5 6
1 2 3
4 5 6
[[1, 2, 3], [4, 5, 6]]
代码示例:不定长二维数组
public class TestDemo {
public static void main(String[] args) {
int[][] arr = new int[3][];
arr[0] = new int[1];
arr[1] = new int[2];
arr[2] = new int[3];
// 获取二维数组的第一维长度(3)
System.out.println(arr.length);
// 获取二维数组的第一维的第一个数组长度(1)
System.out.println(arr[0].length);
}
}
// 执行结果
3
1
二维数组的用法和一维数组并没有明显差别,只是多了行数,同理,还存在 "三维数组", "四维数组" 等更复杂的数组,只不过出现频率都很低
注意:java中只能单独定义行数,不能单独定义列数



