java SE ----数组

一、数组的基本概念

1、引入

举例

例如当我们需要保存多个学生的成绩时,如果没有数组我们就需要创建多个变量,分别存储数据。

代码示例


public class Test {
    public static void main(String[] args) {
        int score1 = 60;
        int score2 = 70;
        int score3 = 80;
        int score4 = 90;

        System.out.println(score1);
        System.out.println(score2);
        System.out.println(score3);
        System.out.println(score4);
    }

}

缺点:如果有100个数据需要存储,难道我们要创建100个变量吗?这样就太过于麻烦了。

但这些数据是相同类型的,所以我们可以使用数组来存储数据。

代码示例

 public static void main(String[] args) {
        int[] score = {60, 70, 80, 90};

        for (int i = 0; i < score.length; i++) {
            System.out.println(score[i]);
        }
 }

需要创建多个相同类型变量存放数据时,可以使用数组,因为数组可以存放相同数据类型的变量

2、什么是数组

概念

可以看成是相同类型元素的一个集合

特点

  1. 数组中存放的元素类型相同
  2. 在内存中是连续存放
  3. 每个空间有自己的编号,起始位置的编号为0,即数组的下标

举例

就像车库里的停车位,一排车位,车辆停在车位上就像数据存储在数组里,车位有编号从 0 开始依次递增。

3、数组创建及初始化

创建

语法

T[] 数组名 = new T[N];

解释:

T:表示数组中存放元素的类型
T[]:表示数组的类型
N:表示数组的长度

代码示例

public static void main(String[] args) {
        int[] arr1 = new int[10]; //创建一个可以存放10个 int 类型元素的数组
        double[] arr2 = new double[5]; //创建一个可以存放5个 double 类型元素的数组
}

初始化

动态初始化

在创建数组时,直接指定数组中元素的个数。
代码示例:

int[] array = new int[10];

静态初始化

在创建数组时不直接指定数据元素个数,而直接将具体的数据内容进行指定。
代码示例:

public static void main(String[] args) {
       //静态初始化数组
       int[] arr1 = new int[] {1, 2, 3, 4, 5};
       String[] arr2 = new String[] {"xx", "yy", "zz"};
       
}

注意事项

  1. 静态初始化没有指定数组长度,但是编译器在编译时会根据{}中元素个数来确定数组的长度。
  2. 静态初始化时, {} 中数据类型必须与 [] 前数据类型一致
  3. 静态初始化可以简写,省去后面的 new T[]。

代码示例:

int[] arr1 = {1, 2, 3, 4, 5};
  1. 数组也可以按照C语言个数创建
int arr[] = {1, 2, 3};
//不推荐,数据类型与 [ ],意思更清晰,不容易看走眼
  1. 静态和动态初始化也可以分为两步,但是省略格式不可以

代码示例:

public static void main(String[] args) {
       //动态初始化
       int[] arr1;
       arr1 = new int[10];

       //静态初始化
       int[] arr2;
       arr2 = new int[] {1, 2, 3, 4, 5};

       //省略new 数据类型[]
       //省略格式不能拆分,不然编译失败
      /* int[] arr3;
       arr3 = {1, 1, 1, 1, 1};*/
}
  1. 数组若没有初始化,其元素有 默认值

(1)、元素为 基本数据类型

数据类型默认值
byte0
short0
long0
int0
char\u0000
flaot0.0f
double0.0
booleanfalse

(2)、元素为 引用数据类型

默认值为 null

4、数组的使用

数组中元素访问

数组在内存中,是一块连续的空间,从下标为 0 的位置开始,依次递增,我们可以通过 下标 随机访问数组中的元素

代码示例
public static void main(String[] args) {
        //下标访问数组
        int[] arr = {1, 2, 3};
        System.out.println(arr[0]);
        System.out.println(arr[1]);
        System.out.println(arr[2]);

        arr[2] = 100;
        System.out.println(arr[2]);
}
注意事项
  1. 数组是一段连续的内存空间,因此支持随机访问,即通过下标访问快速访问数组中任意位置的元素。
  2. 下标从0开始,介于[0, N)之间不包含N,N为元素个数,不能越界,否则会报出下标越界异常。

代码示例:

public static void main(String[] args) {
       //下标访问数组越界
       int[] arr = {1, 2, 3};
       System.out.println(arr[3]);

}

执行结果:抛出了 java.lang.ArrayIndexOutOfBoundsException 异常. 使用数组一定要下标谨防越界

遍历数组

概念

遍历:将数组中的所有元素都访问一遍, 访问 是指对数组中的元素进行某种操作

举例

代码示例

public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};

        System.out.println(arr[0]);
        System.out.println(arr[1]);
        System.out.println(arr[2]);
        System.out.println(arr[3]);
        System.out.println(arr[4]);
}

以上代码有几个问题:

  1. 问题一:如果数组中增加了一个元素,就需要增加一条打印语句。
  2. 问题二:如果数组中有100个元素,就需要写100个打印语句。
  3. 问题三:如果现在要把打印修改为给数组中每个元素加1,修改起来非常麻烦。

解决方法

  1. 问题二、三可以使用 循环 来解决。
    代码示例
public static void main(String[] args) {
       int[] arr = {1, 2, 3, 4, 5};

       for (int i = 0; i < 5; i++) {
           System.out.println(arr[i]);
       }
}
  1. 问题一有两个解决办法

方法一: 使用 for - each 遍历数组。

  1. 认识 for - each

for 循环的另外一种使用方式.,能够更方便的完成对数组的遍历,可以避免循环条件和更新语句写错。

  1. 代码示例
public static void main(String[] args) {
       int[] arr = {1, 2, 3, 4, 5};
       
       //for - each
       for (int x : arr) {
           System.out.println(x + "");
       }    
   }

方法二: 使用 数组对象 . length 获取数组长度。

  1. 代码示例
public static void main(String[] args) {
       int[] arr = {1, 2, 3, 4, 5};
       
       //数组对象.length遍历数组
       for (int i = 0; i < arr.length; i++) {
           System.out.println(arr[i]);
       }
}
  1. for循环 和 for-each 的区别
  1. for循环依赖下标来遍历数组,而for-each不依赖下标。
  2. for-each 不需要写循环条件和更新语句。

二、数组是引用类型

1. JVM内存分布

内存概念

内存:一段连续的存储空间,主要用来 存储程序运行时数据的
例如:

  1. 程序运行时代码 需要加载到内存。
  2. 程序运行产生的中间数据 要存放在内存。
  3. 程序中的常量 也要保存。
  4. 有些数据可能需要长时间存储,而有些数据当方法运行结束后就要被销毁。

JVM内存分布

JVM 对所使用的内存按照 功能的不同 进行了划分

  1. 程序计数器(PC Register)

只是一个很小的空间, 保存下一条执行的指令的地址

  1. 虚拟机栈(JVM Stack)
  1. 与方法调用相关的一些信息,每个方法在执行时,都会先创建一个栈帧

栈帧中包含有:局部变量表操作数栈动态链接返回地址以及其他的一些信息,保存的都是与方法执行时相关的一些信息。

  1. 当方法结束,栈帧销毁,栈帧中的数据也被销毁。
  2. 平时说的栈,就是虚拟机栈。
  1. 本地方法栈(Native Method Stack)

与虚拟机栈的作用类似. 只不过保存的内容是Native方法的局部变量. 在有些版本的 JVM 实现中(例如HotSpot), 本地方法栈和虚拟机栈是一起的。

  1. 堆(heap)
  1. JVM所管理的最大内存区域。
  2. 使用 new 创建的对象 都是在堆上保存。

代码示例

public static void main(String[] args) {
       int[] arr = new int[] {1, 2, 3, 4, 5};
       System.out.println(arr[0]);
   }

所以,我们创建的数组其实是存储在堆上的。

  1. 堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销毁。
  1. 方法区(Method Area)

存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。例如:方法编译出的字节码就是保存在这个区域。

总结

每块内存,都有自己的使命,不同内存储存的数据就不同。

2. 基本数据类型变量和引用数据类型变量的区别

概念

  1. 基本数据类型创建的变量

称为 基本变量,该变量空间中直接存放的是其所对应的

  1. 引用数据类型创建的变量

称为 对象的引用,其空间中存储的是对象所在空间的地址

代码示例

	public static void main(String[] args) {
        int a = 0;
        int b = 100;

        int[] arr = new int[] {1, 2, 3, 4, 5};
        
	}

图解

  1. a,b是内置类型的变量:空间中存储的就是变量初始化的值。
  2. arr是数组类型的引用变量:内部保存的内容可以简单理解成是 数组在堆空间中的首地址,可以说,arr 这个引用 指向了 数组对象

3. 再谈引用变量

两个引用指向同一个对象

代码示例

public static void main(String[] args) {
        int[] arr1 = {1, 2, 3, 4};
        int[] arr2 = new int[4];
        
		arr2[0] = 11;
        arr2[1] = 22;
        arr2[2] = 33;
        arr2[3] = 44;
        
        arr1 = arr2;
       
        for (int i = 0; i < arr1.length; i++) {
            System.out.println(arr1[i]);
        }

}

图解

注意:不能说引用指向引用,只能说引用指向引用指向的对象

4. 认识null

概念

在 Java 中表示 “空引用” , 也就是一个不指向任何对象的引用。

作用

类似C语言中的空指针,指向一块无效的内存位置

所以不能对该内存进行任何读写操作,一旦进行操作就会报错。

代码示例

public static void main(String[] args) {
       int[] arr = null;
       System.out.println(arr[0]);
   }

执行结果:报 空指针异常 (NullPointerException)

注意

Java 中并没有约定 null 和 0 号地址的内存有任何关联

三、数组的应用场景

1. 保存数据

代码示例

public static void main(String[] args) {
        //保存数据
        int[] arr = {1, 2, 3, 4};
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
}

2. 作为方法参数

参数传递基本数据类型

  1. 代码示例
public static void func(int x) {
        System.out.println("修改前x = " + x);
        x = 99;
        System.out.println("修改后x = " + x); // x 已经被修改为99
    }
    public static void main(String[] args) {
        int a = 100;
        System.out.println("修改前a = " + a);
        func(a);

        System.out.println("修改后a = " + a);
    }
  1. 执行结果

  1. 图解
  2. 结论

形参是在func方法中的变量,用来接收实参的值,实参是在main方法中的变量,本质是两个实体,所以对形参的修改不会影响实参。

参数传数组类型(引用数据类型)

  1. 代码示例
public static void func2(int[] a) {
        a[0] = 100;
}
public static void main(String[] args) {
        int[] arr = {1, 2 ,3, 4};
        System.out.println("修改前:arr[0] = " + arr[0]);

        func2(arr);

        System.out.println("修改后:arr[0] = " + arr[0]);
        System.out.println("成功修改!!!");

}
  1. 执行结果
  2. 图解

  1. 结论

本质就是,把引用作为实参传递给形参,形参是一个在func方法中创建的数组类型的引用,它们是两个变量,这个形参用来接收实参的值,而实参的值储存的是对象在堆中的起始地址。所以这两个引用才同时指向一个对象。

  1. 引用的本质
  1. 本质就是存了本质上只是存了一个地址
  2. Java 将数组设定成引用类型, 这样的话后续进行数组参数传参, 其实只是将数组的地址传入到函数形参中. 这样可以避免对整个数组的拷贝(数组可能比较长, 那么拷贝开销就会很大)。

3. 作为方法返回值

举例:获取斐波那契数列的前N项

  1. 代码示例
public static int[] fib(int n) {
        int[] arr1 = new int[n];
        arr1[0] = arr1[1] = 1;

        for (int i = 2; i < n; i++) {
            arr1[i] = arr1[i-1] + arr1[i-2];
        }

        return arr1;
}
public static void main(String[] args) {
        int[] arr = fib(10);
        System.out.println(Arrays.toString(arr));
}
  1. 执行结果

四、二维数组

1. 本质

是一个一维数组,只不过每个元素又是一个一维数组

  1. 代码示例
public static void main(String[] args) {
   int[][] arr1 = new int[][] {{1, 2, 3}, {4, 5, 6, 7}};
   System.out.println(arr1[0].length);
   System.out.println(arr1[1].length);
       
}
  1. 执行结果

  1. 图解

2. 基本语法

数据类型[][] 数组名称 = new 数据类型 [行数][列数] { 初始化数据 };

3. 不规整的二维数组

1.省略行数的二维数组

1.代码示例

public static void main(String[] args) {
       int[][] arr = new int[2][];
       arr[0] = new int[] {1, 2 ,3};
       arr[1] = new int[] {2, 4, 6, 8, 10};

       System.out.println(Arrays.deepToString(arr));

}
  1. 执行结果

  1. 图解

2. 对没有初始化 列数 的二维数组进行操作

  1. 代码示例
public static void main(String[] args) {
       //对没有初始化列数的二维数组进行操作
       int[][] arr = new int[2][];

       for (int i = 0; i < arr.length; i++) {
           for (int j = 0; j < arr[i].length; j++) {
               System.out.println(arr[i][j]);
           }
          System.out.println();
       }
}
  1. 执行结果

会报 空指针 错误。

  1. 图解

  1. 总结
  1. 没有设定列数时,二维数组中的元素(引用),没有指向任何数组,所以为null,对null,进行操作时,就会报错。
  2. 引用类型,未初始化,默认值为null
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值