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

Java数组概述

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

相关概念

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

数组的特点

  1. 数组本身是引用数据类型, 而数组中的元素可以是任何数据类型, 包括
    基本数据类型和引用数据类型。
  2. 创建数组对象会在内存中开辟一整块连续的空间, 而数组名中引用的是
    这块连续空间的首地址。
  3. 数组的长度一旦确定, 就不能修改。
  4. 我们可以直接通过下标(或索引)的方式调用指定位置的元素, 速度很快。
  5. 数组是有序排列的

数组的分类

按照维度进行分类

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

按照元素的数据类型分

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

数组使用

一维数组

声明方式
type var[] 或 type[] var

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

初始化
动态初始化

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

int[] arr = new int[3 ];
arr[0] = 3;
arr[1] = 9;
arr[2] = 8;
静态初始化

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

int arr[] = new int[]{3, 9, 8};
int[] arr1 = {3,9,8};
数组元素的引用

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

数组元素的引用方式
数组名[数组元素下标]
  • 数组元素下标可以是整型常量或整型表达式。如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的长
度(元素个数)

  • 数组一旦初始化,其长度是不可变的
数组元素的默认初始化值

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

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

深入浅出Java数组及其内存机制_第1张图片

数组的赋值和复制
赋值
int[] arr1,arr2;
arr1 = new int[]{2,3,5,7,9,15};
arr2 = arr1;
arr2[0]=1;
arr2[1]=1;
//此时arr1和arr2都变为{1,1,5,7,9,15}

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

复制
int[] arr1,arr2;
arr1 = new int[]{2,3,5,7,9,15};
arr2 = new int[arr1 .length];
for(int i = 0;i < arr2.length;i++){
	arr2[i]=arr1[i];
}
arr2[0]=1;
arr2[1]=1;
//此时arr1为{2,3,5,7,9,15},arr2为{1,1,5,7,9,15}
数组的反转

方式一:

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

方式二:

for(int i = 0,j = arr.length - 1;i < j;i++,j--){ 
	String temp = arr[i];
	arr[i] = arr[j] ;
	arr[j] = temp;
}

图解数组的内存机制

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)中,数组的引用变量(即数组对象)被存储在栈内存中。

深入浅出Java数组及其内存机制_第2张图片

内存解析

数组对象如果重新赋值,那么数组对象重新指向新的实际数组的地址值。数去引用的旧数组变成垃圾,等待垃圾回收机制回收。具体分析如下图:
深入浅出Java数组及其内存机制_第3张图片

代码
	public static void main(String[] args) {
        //定义并静态初始化数组
        int [] array1={1,2,3};
        //定义并动态初始化数组
        int []array2=new int[4];
        //输出array2的长度
        System.out.println("array2的长度:"+array2.length);
        //循环输出array1数组的元素
        for(int i=0;i<array1.length;i++){
            System.out.println(array1[i]);
        }
        System.out.println("---------------------------------");
        //循环输出array2的数组元素
        for(int i=0;i<array2.length;i++){
            System.out.println(array2[i]);
        }
        array2=array1;
        //再次输出array2的长度
        System.out.println("array2的长度"+array2.length);
    }

多维数组

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

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

二维数组的使用
格式一(动态初始化)
 int[][] arr = new int[3][2];

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

格式二(动态初始化)
 int[][] arr = new int[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(静态初始化)
 int[][] arr = new int[][]{{3,8,2},{2,7},{9,0,1,6}};

定义一个名称为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中多维数组不必都是规则矩阵形式
二维数组的内存解析
public static void main(String[] args) {
        //1. 二维数组的声明与初始化
        int[] arr = new int[]{1,2,3};
        //1.1 数组的静态初始化:数组的初始化和数组元素的赋值操作同时进行
        int[][] id = new int[][] {{10,12},{14,16},{18,20}};
        //1.2 数组的动态初始化:数组的初始化和数组元素的赋值操作分开进行
        String[][] names = new String[5][3];
        //1.3 错误的写法:不能将动态初始化与静态初始化同时进行
        //char[][] = new char[5][3]{{'k','l'},{'y','u'}};错误的写法
        //String[][] arr2 = new String[][4];
        
       //正确:
         int[] arr4[] = new int[][]{{10,12},{14,16},{18,20}};
         int[] arr5[] = {{10,12},{14,16},{18,20}};
        
        二位数组元素默认的初始化
        int[][] arr7 = new int[4][3];
        sysout(arr7[0])    //[@15bffst  地址值
        sysout(arr7[0][0]); //0 数组中数据类型的默认值
    
    }

深入浅出Java数组及其内存机制_第4张图片

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)
int[] arr = new int[2];
System.out.println(arr[2]);
System.out.println(arr[-1]);

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

2.空指针异常(NullPointerException)
int[] arr = null;
System.out.println(arr[0]);

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

你可能感兴趣的:(Java基础,java,数组,数组的内存解析,数据结构)