java 数组
文章内容发布于 4 天前;最后修改于 1 日前。其中的信息可能发生变化或产生更改,敬请留意。

说明

数组是一种固定长度的容器,用于存储相同数据类型的多个数据值。

数组是一个引用数据类型,数组变量在栈内存中存储堆内存地址,数组元素在堆内存中存储实际的数据。

数组在存储数据的时候,需要结合隐式转化的问题。

  • 举例:如果int类型的数组,可以存储原始数据类型byteshortint类型的数据,但是byteshort会自动拓宽为int类型的数据。
  • 举例:如果double类型的数组,可以存储原始数据类型byteshortintlongfloatdouble类型的数据,但是byteshortintlongfloat会自动拓宽为double类型的数据。

📌隐式转化说明(复习)(或称拓宽转换)

隐式转换(类型自动提升):取值范围小的自动提升为取值范围为大的

  • 由程序自动完成
    1. 取值范围小的和取值范围大的运算,先将取值范围小的的数据类型提升,后进行运算
    2. byteshortchar 类型在运算时,都会先直接提升为 int,再进行运算。
  • 存储范围大小关系:double > float > long > int > short > byte
  • 建议:数组容器的类型,和存储的数据类型保持一致。

数组的定义和初始化

数组的定义

  • 格式1(推荐):<数据类型>[] <数组名>;。举例:int[] arr;
  • 格式2:<数据类型> <数组名>[];。举例:int arr[];

两种定义都没有报错,但是仍然建议使用第一种定义方式,即<数据类型>[] <数组名>;

数组的定义方式:某大厂的编码规则

中括号是数组类型的一部分,数组定义如下:String[] args;

反例:使用String args[]的方式来定义。

数组的初始化

初始化的说明:在内存中开辟存储空间,并将数据存储到数组容器的过程。

静态初始化

  • 完整格式:<数据类型>[] <数组名> = new <数据类型>[] {元素};,比如:int[] arr = new int[] {1, 2, 3};
  • 简化格式:<数据类型>[] <数组名> = {元素};,比如:int[] arr = {1, 2, 3};

数组一旦创建完毕后,数组的长度无法改变,即无法增加长度,也无法缩短长度。

使用静态初始化,JVM会根据传入数组的个数自动判断数组长度。

动态初始化

初始化时,只指定数组长度,由系统自动为数组分配初始值。

  • 格式:<数据类型>[] <数组名> = new <数据类型>[<数组长度>];,例如:int[] arr = new int[10];,即创建一个int类型的数组,长度为10。

关于系统自动为数组分配初始值的理解:

public static void main(String[] args) {
    int[] arr = new int[10];
    for (int i = 0; i < arr.length; i++) {
        System.out.print(arr[i]);
    }
 }
0000000000

这里的初始值,并不是随机的数值,而是默认数值的,如下:

数据类型 数据类型关键字 默认值
整数类型 byteshortintlong 0
小数类型 floatdouble 0.0
字符类型 char '\u0000'(空字符)
布尔类型 boolean false
引用数据类型 null

动态初始化和静态初始化的区别

  • 动态初始化:手动指定数组长度,由系统给出初始化值。(适用于明确元素个数)
  • 静态初始化:手动指定数组元素,系统根据元素个数计算出数组长度。(适用于明确元素的数据)

数组的地址值

我们编写如下代码

int arr[] = new int[] {1, 2, 3};
System.out.print(arr);

可以看到,输出的并不是数组内容,而是一串“乱码”,这串“乱码”,就是数组在内存中的地址值(数组在内存中的位置)。

扩展: 地址值的格式定义

我们以 [I@b4c966a 这个地址为例:

  • [:表示这是一个数组;
  • I:表示这是一个int类型的数组; 类型编码 数据类型
    I int
    D double
    F float
    J long
    Z boolean
    C char
    B byte
    S short
    L 引用类型
  • @:是间隔符号,是固定格式;
  • b4c966a:对象哈希码的十六进制表示,用于对象的唯一标识,不是真实的地址值。

数组和引用数据类型

数组是一个引用数据类型,引用数据类型在栈内存中只存储指向堆内存的地址值。

而在堆内存中,存储的是实际的数据值。

我们仍然以int[] arr = new int[1]为例:

栈内存:arr变量 -> 存储数组对象在堆内存中的地址

堆内存:数组对象包含:

  • 数组元数据(长度、类型等)
  • 连续的内存空间存储各个元素的值

通过arr[0]访问的是堆内存中数组对象的第一个元素存储的实际数据值。

数组的元素访问

  • 格式:<数组名>[<索引>]; 需要说明的是,数据的索引(索引亦称下标,角标)由0开始,连续不间断。

数组的遍历

  • java中提供一个数组的长度属性,为<数组名>.length,如果希望遍历数组,则可以使这个length属性。
    int[] arr = new int[] {1, 2, 3, 4, 5};
    for (int i = 0; i < arr.length; i++) {
      System.out.println(arr[i]);
    }

数组的内存图

java的内存分配

java JVM虚拟机本地内存分为:

  • :方法运行时使用的内存。
  • :存储对象或者数组,所有new来创建的,都存储在堆内存,会生成地址值。
  • 方法区(元空间):存储可以运行的class文件
  • 本地方法栈:JVM在使用操作系统功能的时候使用,与开发java程序无关。
  • 寄存器:给CPU使用,与开发java程序无关。

说明:在JDK 7以前,方法区是在一起的,在JDK 8+,将方法区拆封:新增元空间;原方法区部分功能移动到堆中,有些则移动到元空间中。

数组的内存

  1. 只要使用new关键字创建的内容,是在堆内存中的;
  2. 如果new关键字使用了多次,则在堆中创建多个内存块。
  3. 当多个数组指向同一个堆内存时,对其中一个数组修改,则所有数组的相同索引全部会被修改。

    arr2arr1指向同一个堆内存块。修改arr1[1],同时也会修改arr2[1]如下是浅拷贝

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

多维数组

多个低维数组可以组合为高维数组,比如多个一维数组可以组合成二维数组,多个二维数组可以组合成三维数组。

多维数组中,高维数组初始化的时候,允许低维数组长度不同。在申明多维数组变量的时候,需要申明最高维度数组的数量。

高维数组在栈内存中存的是高维数组在堆内存数据的引用,高维数组在堆内存中存储的是对低维数组的引用。

如下以二维数组为例。

静态初始化

  • 格式:<数据类型>[][] <数组名> = new <数据类型>[][] {{<元素1>, <元素2>}, {<元素1>, <元素2>}};
  • 简化:<数据类型>[][] <数组名> = {{<元素1>, <元素2>}, {<元素1>, <元素2>}};
  • 举例:int[][] arr2d = new int[][] {{1, 2, 3}, {2,3}};(高纬数组初始化的时候,允许低维数组长度不同)

动态初始化

  • <数据类型>[][] <数组名> =new <数据类型>[<最高维度数组的数量>][];
  • 举例:int[][] arr2d = new int[2][];(需要申明最高维度数组的数量)

说明

  • 动态初始化后需要单独初始化每个低维数组
  • 访问未初始化的低维数组会导致NullPointerException
  • 遍历时需使用array[i].length适应不等长情况

数组的常见问题

索引越界

当访问了数组不存在的索引,就会引发索引越界。

示例代码:

int arr[] = {1, 2, 3, 4, 5};
System.out.println(arr[10]);

报错如下

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 5
  at Demo_20251031_1.main(Demo_20251031_1.java:4)

需要说明的是:java不存在-1这样的负数值索引

Java内存的说明

哈希码与内存地址的关系

  • 在早期的JVM中,哈希码基于内存地址计算得出
  • 在现代JVM中,哈希码不保证与内存地址相关
  • 这是出于安全和性能的考虑

java与内存地址

  • Java 被设计为一种内存安全的语言,​不允许直接操作内存地址。这是 Java 的核心安全特性之一。
  • 用于防止:缓冲区溢出攻击、野指针访问、内存泄漏(相对C/C++而言)、非法内存访问导致的程序崩溃。
  • Java 程序运行在 JVM 上,真实内存地址对开发不可见,即使"获取"了某个地址,JVM可能会移动对象,导致地址失效。

内存回收

jvm会判断所有引用数据类型的指向,如果这个某个堆内存空间没有被引用的其他变量、对象引用,则会适时触发清理。

存在被引用

堆内存被str引用,不回收内存空间。

String str = new String("Hello");

不存在被引用

arr数组被指向null,引用解除,原arr对象无引用,适时被jvm回收

int[] arr = new int[1]{0};
arr = null;

空指针异常

  • 数组是引用数据类型,数组变量在栈内存中存储的是指向堆内存中数组对象的地址引用
  • 当数组在栈内存中引用为null时(当数组引用被显式赋值为null,或者未正确初始化时),访问元素会抛出空指针异常。

数组的深浅拷贝

深浅拷贝的定义

浅拷贝 (Shallow Copy)

创建一个新对象,但只复制原始对象中基本数据类型的值,对于引用类型的字段,只复制引用地址而不复制引用的对象本身。

特点

  • 新对象和原对象共享引用类型的数据
  • 修改新对象中的引用类型数据会影响原对象
  • 复制速度快,内存占用少

深拷贝 (Deep Copy)

创建一个新对象,并递归地复制原始对象及其所有引用对象,直到最基本的数据类型为止。

特点

  • 新对象和原对象完全独立,不共享任何数据
  • 修改新对象不会影响原对象
  • 复制速度慢,内存占用多

数组的深浅拷贝

浅拷贝:新数组变量(栈内存)指向原数组在堆内存中的同一数据对象,两个数组变量共享同一份堆内存数据。

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

深拷贝:新数组变量(栈内存)指向堆内存中新建的独立数据对象,两个数组变量拥有各自独立的堆内存数据。

int[] arrSource = new arrSource[]{1, 2, 3, 4, 5};
int[] arrNew = new int[arrSource.length];
for (int i = 0; i < arrSource.length; i++) {
    arrNew[i] = arrSource[i];
}

数组的深拷贝

使用**Object类的clone**方法

使用Object类的clone方法,以深拷贝数组。

public class Main {
    public static void main(String[] args) {
        int[] arrSource = new int[]{1, 2, 3, 4, 5};
        int[] arrNew = arrSource.clone();
    }
}

使用**java.util.Arrays类的copyOf**方法

需要引入java.util.Arrays类。

copyOf方法的语法为Arrays.copyOf(<原始数组>, <新数组的长度>)

import java.util.Arrays;
public class Main {
    public static void main(String[] args) {
        int[] arrSource = new int[]{1, 2, 3, 4, 5};
        int[] arrNew = Arrays.copyOf(arrSource, arrSource.length);
    }
}
文章「java 数组」,由本站用户「Admin」发布。文章仅代表Admin观点,不代表本站立场。
页面网页地址「https://xiaozhiyuqwq.top/p/2742」。
如您对文章及其附件提出版权主张,或进行引用转载等,请查看我们的【版权声明】
本页面暂时没有评论......

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇