深入浅出Java数组及其内存机制

Java数组概述

1数组(Array), 是多个相同类型数据按一定顺序排列的集合, 并使用一个名字命名, 并通过编号的方式对这些数据进行统一管理。 2 3

相关概念

  • 数组名
  • 元素
  • 下标(或索引)
  • 数组的长度;元素的个数

数组的特点

  1. 数组本身是引用数据类型, 而数组中的元素可以是任何数据类型, 包括

基本数据类型和引用数据类型。

  1. 创建数组对象会在内存中开辟一整块连续的空间, 而数组名中引用的是

这块连续空间的首地址。

  1. 数组的长度一旦确定, 就不能修改。
  2. 我们可以直接通过下标(或索引)的方式调用指定位置的元素, 速度很快。
  3. 数组是有序排列的

数组的分类

按照维度进行分类

一维数组、 二维数组、 三维数组、 …

按照元素的数据类型分

基本数据类型元素的数组、 引用数据类型元素的数组(即对象数组)

数组使用

一维数组

声明方式

1type var[] 或 type[] var 2 3

Java语言中声明数组时不能指定其长度(数组中元素的数)

初始化

动态初始化

数组声明且为数组元素分配空间与赋值的操作分开进行

1int[] arr = new int[3 ]; 2arr[0] = 3; 3arr[1] = 9; 4arr[2] = 8; 5 6

静态初始化

在定义数组的同时就为数组元素分配空间并赋值。

1int arr[] = new int[]{3, 9, 8}; 2int[] arr1 = {3,9,8}; 3 4

数组元素的引用

定义并用运算符new为之分配空间后,才可以引用数组中的每个元素。

数组元素的引用方式

1数组名[数组元素下标] 2 3
  • 数组元素下标可以是整型常量或整型表达式。如a[3] , b[i] , c[6*i];
  • 数组元素下标从0开始;长度为n的数组合法下标取值范围: 0 —>n-1; 如int a[]=newint[3];可引用的数组元素为a[0]、 a[1]、 a[2]。

数组元素的长度

每个数组都有一个属性length指明它的长度,例如: a.length 指明数组a的长
度(元素个数)

  • 数组一旦初始化,其长度是不可变的

数组元素的默认初始化值

数组是引用类型,它的元素相当于类的成员变量,因此数组一经
分配空间,其中的每个元素也被按照成员变量同样的方式被隐式
初始化。

1 public static void main(String argv[]){ 2 int a[]= new int[5]; 3 System.out.println(a[3]); //a[3]的默认值为0 4 } 5 6
  • 对于基本数据类型而言,默认初始化值各有不同
  • 对于引用数据类型而言,默认初始化值为null(注意与0不同! )

在这里插入图片描述

数组的赋值和复制

赋值

1int[] arr1,arr2; 2arr1 = new int[]{2,3,5,7,9,15}; 3arr2 = arr1; 4arr2[0]=1; 5arr2[1]=1; 6//此时arr1和arr2都变为{1,1,5,7,9,15} 7 8

数组赋值只是将引用指向了数组对象,并没有实现复制,改变了arr2,arr1也会发生改变。

复制

1int[] arr1,arr2; 2arr1 = new int[]{2,3,5,7,9,15}; 3arr2 = new int[arr1 .length]; 4for(int i = 0;i < arr2.length;i++){ 5 arr2[i]=arr1[i]; 6} 7arr2[0]=1; 8arr2[1]=1; 9//此时arr1为{2,3,5,7,9,15},arr2为{1,1,5,7,9,15} 10 11

数组的反转

方式一:

1for(int i = 0;i < arr.length / 2;i++){ 2 int temp = arr[i] ; 3 arr[i] = arr[arr.length - i - 1]; 4 arr[arr.length - i -1] = temp ; 5} 6 7

方式二:

1for(int i = 0,j = arr.length - 1;i < j;i++,j--){ 2 String temp = arr[i]; 3 arr[i] = arr[j] ; 4 arr[j] = temp; 5} 6 7

图解数组的内存机制

Java的内存空间分配

Java程序为了提高程序的效率,就对数据进行了不同空间的分配。具体的是划分为了如下5个内存空间:

  1. 栈:存放的是局部变量
  2. 堆:存放的是所有new出来的东西
  3. 方法区: (面向对象部分详细讲解)
  4. 本地方法区::(和系统相关)
  5. 寄存器::(CPU使用)

堆内存的特点

  • 每一个new出来的东西都有地址值
  • 每个变量都有默认值

byte, short, int, long 0
float, double. 0. 0
char,\u0000’
boolean. false
引用类型null

  • 使用完毕就变成了垃圾,但是并没有立即回收。会在垃圾回收器空闲的时候回收。

数组的内存

数组是一种引用内存,数组引用变量只是一个引用,数组元素和数组变量在内存里是分开存放的。

实际的数组对象(即数组元素)被存放在堆内存(heap)中,数组的引用变量(即数组对象)被存储在栈内存中。

在这里插入图片描述

内存解析

数组对象如果重新赋值,那么数组对象重新指向新的实际数组的地址值。数去引用的旧数组变成垃圾,等待垃圾回收机制回收。具体分析如下图:
在这里插入图片描述

代码

1 public static void main(String[] args) { 2 //定义并静态初始化数组 3 int [] array1={1,2,3}; 4 //定义并动态初始化数组 5 int []array2=new int[4]; 6 //输出array2的长度 7 System.out.println("array2的长度:"+array2.length); 8 //循环输出array1数组的元素 9 for(int i=0;i<array1.length;i++){ 10 System.out.println(array1[i]); 11 } 12 System.out.println("---------------------------------"); 13 //循环输出array2的数组元素 14 for(int i=0;i<array2.length;i++){ 15 System.out.println(array2[i]); 16 } 17 array2=array1; 18 //再次输出array2的长度 19 System.out.println("array2的长度"+array2.length); 20 } 21 22

多维数组

Java 语言里提供了支持多维数组的语法。如果说可以把一维数组当成几何中的线性图形,那么二维数组就相当于是一个表格。

对于二维数组的理解,我们可以看成是一维数组array1又作为另一个一维数组array2的元素而存在。其实, 从数组底层的运行机制来看,其实没有多维数组。

二维数组的使用

格式一(动态初始化)

1 int[][] arr = new int[3][2]; 2 3

定义了名称为arr的二维数组
二维数组中有3个一维数组
每一个一维数组中有2个元素
一维数组的名称分别为arr[0], arr[1], arr[2]
给第一个一维数组1脚标位赋值为78写法是: arr[0][1] = 78;

格式二(动态初始化)

1 int[][] arr = new int[3][]; 2 3

二维数组中有3个一维数组。
每个一维数组都是默认初始化值null (注意:区别于格式1)
可以对这个三个一维数组分别进行初始化
arr[0] = new int[3]; arr[1] = new int[1]; arr[2] = new int[2];

注:
int[][]arr = new int[][3]; //非法

格式3(静态初始化)

1 int[][] arr = new int[][]{{3,8,2},{2,7},{9,0,1,6}}; 2 3

定义一个名称为arr的二维数组,二维数组中有三个一维数组
每一个一维数组中具体元素也都已初始化
第一个一维数组 arr[0] = {3,8,2};
第二个一维数组 arr[1] = {2,7};
第三个一维数组 arr[2] = {9,0,1,6};
第三个一维数组的长度表示方式: arr[2].length;

  • 注意特殊写法情况: int[] x,y[]; x是一维数组, y是二维数组。
  • Java中多维数组不必都是规则矩阵形式

二维数组的内存解析

1public static void main(String[] args) { 2 //1. 二维数组的声明与初始化 3 int[] arr = new int[]{1,2,3}; 4 //1.1 数组的静态初始化:数组的初始化和数组元素的赋值操作同时进行 5 int[][] id = new int[][] {{10,12},{14,16},{18,20}}; 6 //1.2 数组的动态初始化:数组的初始化和数组元素的赋值操作分开进行 7 String[][] names = new String[5][3]; 8 //1.3 错误的写法:不能将动态初始化与静态初始化同时进行 9 //char[][] = new char[5][3]{{'k','l'},{'y','u'}};错误的写法 10 //String[][] arr2 = new String[][4]; 11 12 //正确: 13 int[] arr4[] = new int[][]{{10,12},{14,16},{18,20}}; 14 int[] arr5[] = {{10,12},{14,16},{18,20}}; 15 16 二位数组元素默认的初始化 17 int[][] arr7 = new int[4][3]; 18 sysout(arr7[0]) //[@15bffst 地址值 19 sysout(arr7[0][0]); //0 数组中数据类型的默认值 20 21 } 22 23

在这里插入图片描述

Arrays工具类的使用

java.util.Arrays类即为操作数组的工具类, 包含了用来操作数组(比
如排序和搜索) 的各种方法。

boolean equals(int[] a,int[] b) 判断两个数组是否相等。 String toString(int[] a) 输出数组信息。 void fill(int[] a,int val) 将指定值填充到数组之中。 void sort(int[] a) 对数组进行排序。 int binarySearch(int[] a,int key) 对排序后的数组进行二分法检索指定的值。

数组常见的异常

1.数组脚标越界异常(ArrayIndexOutOfBoundsException)

1int[] arr = new int[2]; 2System.out.println(arr[2]); 3System.out.println(arr[-1]); 4 5

访问到了数组中的不存在的脚标时发生。

2.空指针异常(NullPointerException)

1int[] arr = null; 2System.out.println(arr[0]); 3 4

arr引用没有指向实体,却在操作实体中的元素时。

代码交流 2021