数组

首先回顾Java数据类型

(1) 基本数据类型

  • 数值型
    • 整数类型:存放整数
      • byte (1字节)
      • short (2字节)
      • int (4字节)
      • long (8字节)
    • 浮点(小数)类型:
      • float (4字节)
      • double (8字节)
  • 字符型
    • char (2字节):存放单个字符,如 'a'
  • 布尔型
    • boolean (1字节):存放 truefalse

(2)引用数据类型

  • (class)
  • 接口 (interface)
    [[ - 数组 ([]) ]] ---->数组也是一种数据类型(引用类型,数组型数据是对象(object))

(3)说明

  1. Java数据类型分为两大类:基本数据类型引用类型
  2. 基本数据类型有8种:
    • 数值型:byte, short, int, long, float, double
    • char, boolean
  3. 引用类型:, 接口, 数组

1. 为什么需要数组?(Array)

当你需要同时管理多个相同类型的变量时,使用数组能让代码更简洁、逻辑更清晰。数组是存储多个相同类型数据的容器。

定义数组的语法:

  1. 声明数组并开辟空间
  2. 给数组各个元素赋值
  3. 使用数组
数据类型[] 数组名 = new 数据类型[长度];// 数据类型后加[],表示是该数据类型的数组
示例
  1. (动态初始化:只告诉 JVM 我要一个几位长的数组,值我后面再填):
int[] scores = new int[5];// 定义一个长度为5的int数组
//数据类型[] 数组名 / 数据类型 数组名[] = new 数据类型[大小]

(1)适合:需要用户输入、从接口拿数据、或初始化长度但不确定值的时候
(2)数组创建后,如果没有赋值,有默认值(不同的数据类型默认值如下):
int 0,short 0, byte 0, long 0, float 0.0,double 0.0,char \u0000,boolean false,String null

  1. 你也可以用静态初始化(定义数组的同时就把元素值也写上了,适合你已经知道内容时用):
int[] nums = {1, 2, 3, 4, 5};//好处:可以使用for遍历数组得到数组的所有元素的和
//第一个元素对应的下标是0 nums[0],第二个对应的下标是1 nums[1],下标是从 0~length-1,不能超,length就是数组里面数的个数

2 .数组快速入门

public class Main {
    public static void main(String[] args) {
        // 1. 定义并初始化数组
        int[] arr = {10, 20, 30};

        // 2. 输出数组第一个元素(索引为 0)
        System.out.println(arr[0]);  // 输出: 10

        // 3. 修改数组第二个元素的值(索引为 1)
        arr[1] = 99;

        // 4. 遍历数组并输出每个元素
        for (int i = 0; i < arr.length; i++) {  //i表示数组的下标,可以通过数组名.length得到数组的大小/长度
            System.out.println("元素" + i + ": " + arr[i]);
        }
    }
}
/*
创建一个char 类型的26 个元素的数组,分别放置'A'-'Z'。
使用for 循环访问所有元素并打印出来。
提示:char 类型数据运算'A'+1 -> 'B'
思路分析 :
1. 定义一个数组char[] chars = new char[26]
2. 因为'A' + 1 = 'B' 类推,所以老师使用for 来赋值
3. 使用for 循环访问所有元素
   */
   public class ArrayDemo01 {
   public static void main(String[] args) {
   char[] character = new char[26];   //注意! character是char[]类型,而character[i]是char类型
   for (int i = 0; i <= 25; i++) {    //写成 i < character.length 挺好的 ^-^
   character[i] = (char)('A' + i);    //(char)('A' + i) :
   //'A' 的 ASCII 编码是 65
   //('A' + 1)是 66,强转成 char 就是 'B'
   //所以 ('A' + i) 可以一步步生成 'A' 到 'Z'
   System.out.print(character[i]+" ");
   }
  }
 }

(‘A’+i)的数据类型是int,因此要加上(char)来强制转换,理由如下

补充:数据类型转换规则:
  • 当java程序进行赋值/运算时,精度小的类型会自动转换为精度大的数据类型(自动类型转换)
  • 数据类型按精度(容量)大小排序顺序如下:
    1. char ->int ->long ->float ->double
    2. byte ->short ->int ->long ->float ->double
初步了解JVM:Java Virtual Machine(Java虚拟机)
  1. JVM 帮你执行 Java 程序,也就是:把 .java 写的代码 → 编译成 .class 字节码
  2. JVM 读取 .class 文件 → 翻译成本地机器能懂的指令去跑
  3. 所以你写的代码根本不是给 CPU 的,是写给 JVM 的,JVM 再帮你翻译给 CPU 跑起来
  4. JVM 的作用:跨平台 — 因为只要每个平台装了对应的 JVM,不管你在哪,Java 都能“Write Once, Run Anywhere”。
  5. JVM 内存结构(重点)
    JVM 把它管理的内存分为几个大块,每块干的事不一样,数组赋值就发生在其中一块叫“堆”的地方 :
    🔧 1. 方法区(Method Area)
    存放类的信息(比如类名、字段、方法、常量池)属于共享内存区域
    🔧 2. 堆(Heap)
    存放所有 new 出来的对象和数组,是 GC(垃圾回收)的主战场,所有线程共享的
    你一写:int[] arr = new int[10]; 这个数组就是分配在“堆”上的。
    🔧 3. 栈(Stack)
    每个线程一个栈,里面存储方法调用时的局部变量表、操作数栈,基本数据类型(int、char 等)变量放这
    引用变量也在这(注意是引用,不是对象本体)
    所以你写 int[] arr = new int[3]; 变量 arr 是在栈中,指向的是堆中的数组。
  6. 数组在 JVM 中的创建与赋值过程(详细)
int[] arr = new int[3];
arr[0] = 10;

🔨 步骤分解:

  • 编译器看你写了 new int[3],它就让 JVM 在堆中分配一个长度为 3 的 int 数组空间,所有 int 元素默认值为 0(Java 的规则)
  • JVM 返回这个数组在堆中的地址/引用 ,并把这个引用存在栈帧中的局部变量表里,变量名 arr 存在栈中,但值其实是个“指针”去堆找数组
  • 你执行 arr[0] = 10; JVM 通过 arr 找到堆里的数组 ,把第 0 个位置的值改成了 10
  • 所以说: 数组本体(真实内存)是在堆中,引用(arr 变量)是在栈中,对数组赋值,其实就是通过“引用”操作“堆上的对象”
数组赋值方法
  • 不同于基本数据类型赋值的值拷贝(n2的变化不会影响到n1的值)
    int n1 = 10; int n2 = n1; n2 = 80;
    先把n1 = 10放在栈里,再拷贝一个n2 = 10,再找到n2把值改成80,不会影响n1
  • 数组是引用传递,赋的是地址,赋值方式为引用赋值
    int[] arr1 = {1,2,3};
    int[] arr2 = arr1; //arr2的变化会影响arr1的值(这个不是拷贝!)
    arr2[0] = 10;
    现在栈里面有一个arr1,arr1并不指向一个值,而是指向一个地址,这个地址又指向里面的一个数据空间,该数据空间(在内存里面每个数据空间都对应一个地址)里面的子空间数等于数组个数,一一对应
    把arr1赋值给arr2时,在内存中相当于在栈里面拷贝了一个arr2指向同一个地址(同一个数据空间,同一个数组)
    对于下标为0的元素(这里是1),他可以通过arr1[0]返回,也可以通过arr2[0]返回
    因此通过arr2[0]改变了值,直接就改了堆上的同一个数组(类似共享文档)//这部分可以看一下赋值内存图
数组拷贝(解决此问题:怎么做到改变新数组不影响原数组,做到数据空间独立?)
int[] arr1 = {1, 2, 3};
// 要创建一个新的数组 arr2 --> 开辟新的数据空间,大小为 arr1.length
int[] arr2 = new int[arr1.length];
// 有 new,会在堆里面开一个新的数据空间,产生一个新地址,
// 再把这个地址拷贝给 arr2,此时新数据空间里面数组都为默认值 0

// 遍历 arr1,把每个元素拷贝到对应位置
for (int i = 0; i < arr1.length; i++) {
arr2[i] = arr1[i];
}
数组翻转
public class ArrayReverse {
//编写一个main 方法
public static void main(String[] args) {
//定义数组
int[] arr = {11, 22, 33, 44, 55, 66};
//规律
//1. 把arr[0] 和arr[5] 进行交换{66,22,33,44,55,11}
//2. 把arr[1] 和arr[4] 进行交换{66,55,33,44,22,11}
//3. 把arr[2] 和arr[3] 进行交换{66,55,44,33,22,11}
//4. 一共要交换3次= arr.length / 2
//5. 每次交换时,对应的下标是arr[i] 和arr[arr.length - 1 -i]  如0和5(0和6-1-0=5)
int temp = 0;
int len = arr.length; //计算数组的长度
for( int i = 0; i < len / 2; i++) {
temp = arr[len - 1 - i];//保存
arr[len - 1 - i] = arr[i];
arr[i] = temp;
}
System.out.println("===翻转后数组===");
for(int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + "\t");//66,55,44,33,22,11
}
}
}
数组添加(扩容):创建新数组 + 拷贝
int[] oldArr = {1, 2, 3};
int[] newArr = new int[6]; // 假设扩容2倍

for (int i = 0; i < oldArr.length; i++) { //遍历旧数组
newArr[i] = oldArr[i];
}
oldArr = newArr; //根据之前的知识,让oldArr的地址变为newArr的地址,原来那个空间就不被任何对象引用,这一步会把oldArr被GC清理删除掉
补充GC::Garbage Collection(垃圾回收)

上面例子最后一行出现的这个原来没用的数组被GC回收:

  • GC 是 JVM 自动负责释放不再被使用对象的内存空间的一种机制,不需要你手动释放,就能防止内存泄露和爆炸。 你创建一个对象,JVM 会给它分配内存;当这个对象再也没人用,它就成了“垃圾”,GC 就会自动回收掉它占的内存,留给别的对象用。
  • 被清理的对象 -没有“引用链”能访问到它,意思就是,它不再被任何变量、字段、参数、栈帧指着
Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐