JavaSE|【JavaSE】数组


文章目录

  • 数组概念
  • 数组的创建和初始化
    • 数组的创建
    • 数组的定义
  • 数组的使用
    • 数组中元素访问
    • 遍历数组
    • 数组拷贝
  • 引用类型->数组
    • 初始JVM内存布局
    • 基本类型变量与引用类型变量的区别
    • null
  • 二维数组
    • 基本语法
    • 二维数组的创建
    • 二维数组的访问
    • 不规则数组

数组概念 概念: 数组是相同类型元素的集合。
内存布局: 在内存中是一段连续的空间。
注:
  1. 数组中存放的元素类型是相同的
  2. 在空间上是连续存放的
  3. 每个空间都有对应的编号,起始位置下标为0。
数组的创建和初始化 数组的创建
T[] 数组名 = new T[N]

T:数组元素类型
N: 元素个数
数组的定义 数组的定义有2种方式
1.动态初始化:在创建数组时,直接指定数组元素的个数
int[] arr = new int[10]

  1. 静态初始化:在创建数组的同时初始化其数据
语法格式:T[ ] arr = {data1, data2 …};
int[] arr1 = new int[]{0,1,2,3,4,5,6,7,8,9}; double[] arr2 = new double[]{1.0, 2.0, 3.0, 4.0, 5.0}; String[] arr3 = new String[]{"hello", "Java", "!!!"};

注:
  • 静态初始化虽然没有指定数组的长度,编译器在编译时会根据{}中元素个数来确定数组的长度。
  • 静态初始化时, {}中数据类型必须与[]前数据类型一致。
  • 静态初始化可以简写,省去后面的new T[]。
// 注意:虽然省去了new T[], 但是编译器编译代码时还是会还原 int[] array1 = {0,1,2,3,4,5,6,7,8,9}; double[] array2 = {1.0, 2.0, 3.0, 4.0, 5.0}; String[] array3 = {"hello", "Java", "!!!"};

  • 静态和动态初始化也可以分为两步,但是省略格式不可以
int[] arr; arr = new int[]{1,2,3}; int[] arr1; arr1 = new int[13]; //error int[] arr2; arr2 = {1,2,3};

  • 如果没有对数组初始化,那么数组其默认值根据数组类型来初始化(引用类型默认为null)
类型 默认值
byte 0
short 0
int 0
long 0
float 0.0f
double 0.0
char /u0000
boolean false
数组的使用 数组中元素访问 数组在内存中是一段连续的空间,空间的编号都是从0开始的,依次递增,该编号称为数组的下标,数组可以通过下标访问其任意位置的元素。
int[]array = new int[]{10, 20, 30, 40, 50}; System.out.println(array[0]); System.out.println(array[1]); System.out.println(array[2]); System.out.println(array[3]); System.out.println(array[4]); // 也可以通过[]对数组中的元素进行修改 array[0] = 100; System.out.println(array[0])

注:
  1. 数组是一段连续的内存空间,所以支持随机访问,通过下标访问快速访问数组中任意位置的元素
  2. 下标从0开始,介于[0, N)之间不包含N,N为元素个数,不能越界,否则会报出下标越界异常。
遍历数组 遍历: 是指将数组中的所有元素都访问一遍, 访问是指对数组中的元素进行某种操作,比如:打印
打印有三种方法:
  1. 知道数组元素个数,利用循环打印
int arr[] = new int[]{1,2,3,4,5,6}; for (int i = 0; i < 6 ; i++) { System.out.print(arr[i] + " "); }

  1. 利用数组对象.length 直接获取数组长度
int arr[] = new int[]{1,2,3,4,5,6}; for (int i = 0; i < arr.length ; i++) { System.out.print(arr[i] + " "); }

  1. 使用for - each遍历数组
    Java有一种功能很强的循环结构,可以用来依次处理数组中的每个元素(其他类型的元素集合亦可)而不必为指定下标值而分心。
语句格式:for (variable : collection) statement
collection这一集合表达式必须是一个数组或者是一个实现了Iterable接口的类对象(例如ArrayList).variable代表的是每个元素类型
int arr[] = new int[]{1,2,3,4,5,6}; for (int x : arr){ System.out.print(x + " "); }

这个循环叫做循环arr中的每一个元素,可以使语句更加简洁,更不容易出错(我们不必关系下标值和终止条件)
  1. 利用 Arrays类的toString方法
public static void main(String[] args) { int[] arr = new int[] {1,2,3,4}; System.out.println(Arrays.toString(arr)); }

JavaSE|【JavaSE】数组
文章图片

数组拷贝
  1. 在Java中,允许将一个数组变量拷贝给另一个数组变量。这时,两个变量将引用同一个数组:
int[] arr1 = new int[] {1, 2, 3, 4}; int[] arr2 = arr1;

注:由于arr1和arr2都指向同一对象,所以修改二者中任意一个元素,另一个也会改变。
  1. 如果我们希望把一个数组中所有元素拷贝到一个新数组中,这时候我们就需要使用Arrays类的copyof方法;
public static void main(String[] args) { int[] arr1 = new int[] {1, 3, 5, 7, 9}; int[] arr2 = Arrays.copyOf(arr1,arr1.length); }

第2个参数是新数组的长度。这个方法通常用来增加数组的大小:int[] arr2 = Arrays.copyof(arr1,arr.length * 2); 如果数组元素是数值型,那么多余的元素将被赋值为0; 如果数组元素是布尔型,则将赋值为false。相反,如果长度小于原始数组的长度,则只拷贝最前面的数据元素。
  1. 使用System.arraycopy(Object src, int srcPos,Object dest, int destPos,int length);
这个copy十分强大,可以copy任何类型的数据。
public static void main(String[] args) { int[] arr = {1,2,3,4}; /** * public static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length) * src - 源数组。 * srcPos - 源数组中的起始位置。 * dest - 目标数组。 * destPos - 目的地数据中的起始位置。 * length - 要复制的数组元素的数量。 */ int[] arr1 = new int[10]; System.arraycopy(arr,0,arr1,1,arr.length - 1); System.out.println(Arrays.toString(arr1)); //[0, 1, 2, 3, 0, 0, 0, 0, 0, 0] }

引用类型->数组 初始JVM内存布局
内存是一段连续的存储空间,主要用来存储程序运行时数据的

在程序运行时代码,数据,常量等需要加载到内存中,一些方法运行结束时需要销毁,如果我们不对数据加以区分管理,那么对内存管理将会十分麻烦,所以在JVM中,按照不同的功能对内存进行划分:
【JavaSE|【JavaSE】数组】JavaSE|【JavaSE】数组
文章图片

  • 程序计数器 (PC Register): 只是一个很小的空间, 保存下一条执行的指令的地址
  • 虚拟机栈( JVM Stack): 与方法调用相关的一些信息,每个方法在执行时,都会先创建一个栈帧 ,栈帧中包含有:局部变量表、操作数栈、动态链接、返回地址以及其他的一些信息,保存的都是与方法执行时相关的一些信息。比如:局部变量。当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了。
  • 本地方法栈(Native Method Stack): 本地方法栈与虚拟机栈的作用类似. 只不过保存的内容是Native方法的局部变量. 在有些版本的 JVM 实现中(例如HotSpot), 本地方法栈和虚拟机栈是一起的
  • 堆( Heap): JVM所管理的最大内存区域. 使用 new 创建的对象都是在堆上保存 (例如前面的 new int[]{1, 2,
    3} ),堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销毁。
  • 方法区(Method Area): 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据. 方法编译出的的字节码就是保存在这个区域
基本类型变量与引用类型变量的区别
基本数据类型创建的变量,称为基本变量,该变量空间中直接存放的是其所对应的值; 而引用数据类型创建的变量,一般称为对象的引用,其空间中存储的是对象所在空间的地址。

public static void func() { int a = 10; int b = 20; int[] arr = new int[]{1,2,3}; }

JavaSE|【JavaSE】数组
文章图片

注:引用变量并不直接存储对象本身,可以简单理解成存储的是对象在堆中空间的起始地址。通过该地址,引用变量便可以去操作对象
null
null 在 Java 中表示 "空引用" , 也就是一个不指向对象的引用

int[] arr = null; System.out.println(arr[0]); // 执行结果 Exception in thread "main" java.lang.NullPointerException at Test.main(Test.java:6)

null 的作用类似于 C 语言中的 NULL (空指针), 都是表示一个无效的内存位置. 因此不能对这个内存进行任何读写操作.一旦尝试读写, 就会抛出 NullPointerException.
注意: Java 中并没有约定 null 和 0 号地址的内存有任何关联

二维数组 二维数组本质上是一个一维数组,不同于C,Java中二维数组在空间上不是连续的。
基本语法
数据类型[][] 数组名称 = new [行数][列数] {初始化数据};

二维数组的创建 同一维数组,二维数组也有两种创建方式:
  • 动态初始化
int[][] array = {{1,2,3},{4,5,6}}; int[][] array2 = new int[][]{{1,2,3},{4,5,6}};

  • 静态初始化
int[][] array3 = new int[2][3];

二维数组的访问
  • 普通打印
int[][] array = {{1,2,3},{4,5,6}}; for (int i = 0; i < array.length; i++) { for (int j = 0; j < array[i].length; j++) { System.out.print(array[i][j] +" "); } System.out.println(); }

在这里二维数组中其实存放的两个一维数组的地址;所以通过array[i].length就可以得到一维数组的长度,这就衍生出了不规则数组。
  • 利用for-each打印
我们知道for-each中的两个参数一个是存放集合元素的变量,另一个是这个集合;我们知道这个集合是array,但存放元素变量不是int类型,而是int [ ],但是这样访问得到的是两个一维数组,现在就同一维数组一样打印。
int[][] array = {{1,2,3},{4,5,6}}; for(int[] tmp : array) { for(int x : tmp) { System.out.print(x+" "); } System.out.println(); }

不规则数组
int[][] array2 = new int[2][]; array2[0] = new int[2]; array2[1] = new int[4]; for (int i = 0; i < array2.length; i++) { for (int j = 0; j < array2[i].length; j++) { System.out.print(array2[i][j] +" "); } System.out.println(); }

不规则数组标题与我们在C中的二维数组,我们可以先定义我们需要几个一维数组,然后为其赋予空间。

    推荐阅读