🔥文章前提:
👋这里有几乎所有你需要的 Java 数组相关知识, 希望可以帮助大家的学习,谢谢大家的支持!!
📙博客主页:博客主页
👉欢迎大家交流学习👍期待大家留言以及一键三连❤️
📅文章发布时间:🌞2021年10月26日
🙏作者本人也是刚刚接触Java有一些地方如果存在错误,还请大家及时指出!谢谢大家!!❤️
gitee链接:博主平时练习的一些代码以及Java的笔记
📢文章目录:
💨 1.数组的基本用法
💦1.1 什么是数组
💦1.2 数组的创建方法
💦1.3 如何遍历数组
💦1.4 遍历数组容易遇到的问题
💦1.5 图解数组
💨2. 数组作为方法的参数
💦2.1 基本用法
💦2.2 理解引用类型
💦2.3 认识空对象 ( null )
💦2.4 初识 JVM 内存区域划分
💨3. 数组作为方法的返回值
💥4. Arrays实用的操作数组方法总结
💨5. 二维数组
💨 1.数组的基本用法
💦1.1 什么是数组
❓ 我们思考一个问题如果在解决实际问题时,如果相同类型的数据需要少量的时候(比如需要两个),分别按照类型去定义两个变量即可。如果这样的变量需要 100 个呢?难道需要重复定义 100 个变量吗?这就需要用到我们的数组去解决,几行代码就解决问题,大大提升了代码的可读性以及代码效率。
1️⃣ 数量较少的时候:👇
public class TestDemo {
public static void main(String[] args) {
int a = 1;
int b = 2;
}
}
2️⃣ 数量十分庞大的时候就需要运用到我们的数组了
🔥 所以我们可以得出一个结论:👇
数组本质上就是让我们能 "批量" 创建相同类型的变量.
❗️ 注意事项: 在 Java 中, 数组中包含的变量必须是相同类型
💦1.2 数组的创建方法
数组的三种定义方法:当在定义数组的时候赋予初值,数组大小不需要指定,IDEA 会通过初始化的数据个数定义数组的大小
1️⃣ 静态初始化
// 静态初始化
// 数据类型[] 数组名称 = { 初始化数据 };
public class TestDemo {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
}
}
2️⃣ 动态初始化(赋初值)
// 动态初始化
// 数据类型[] 数组名称 = new 数据类型 [] { 初始化数据 };
public class TestDemo {
public static void main(String[] args) {
int[] arrys = new int[]{1,2,3,4,5};
}
}
3️⃣ 动态初始化(不赋初值)
public class TestDemo {
public static void main(String[] args) {
int[] arrys = new int[5];
}
}
❗️ 不赋初值数组一定要指定大小
❗️ 数组不赋初值时候默认初值为 0
public class TestDemo {
public static void main(String[] args) {
int[] arrys = new int[5];
for (int i = 0; i < arrys.length; i++) {
System.out.print(arrys[i]+" ");
}
}
}
0 0 0 0 0
💦1.3 如何遍历数组
介绍一种可以求得数组长度的属性:
int ret = arr.length;
利用 arr.length 即可求得数组的长度
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
int ret = arr.length;
System.out.println(ret);
}
5
❓ 那么问题来了如何拿到数组中的元素呢 👇
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
System.out.println(arr[0]);
System.out.println(arr[1]);
}
数组元素的访问方法:arr [对应元素的下标] 即可得到对应的数组元素
❗️ 数组的下标是从 0 开始的,到数组长度 -1 结束
❓ 那思考如何快速遍历数组的所有元素 👇
答案:循环遍历数组
两种循环遍历数组方法:👇
方法一:循环下标 i 实现对数组的遍历
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
}
1 2 3 4 5
方法二:for - each 实现对数组的遍历
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
for (int x:arr) {
System.out.print(x+" ");
}
}
1 2 3 4 5
❓ 这两种方法适用场景:第一种可以按照需求得到某个元素的对应下标,第二种由于不涉及下标的使用,所以在单纯遍历的数组的时候可以利用 for-each 进行数组的遍历打印
💦1.4 遍历数组容易遇到的问题
❓ 在遍历数组容易在哪里出错呢
看这样一个代码:👇
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
for(int i = 0;i <= arr.length;i++){
System.out.println(arr[i]);
}
}
👉 在循环打印数组的时候循环的终止条件设置成数组的大小,我们知道数组下标的范围是从 0 到数组长度 -1 ,如图的代码就造成了越界访问,arr[arr.length] 不是数组 arr 的元素
❗️ 所以在遍历数组的时候一定要注意:控制好下标的范围,防止造成越界访问
💦1.5 图解数组
Java 的数组在内存中存储于堆上,属于引用类型
在动态开辟数组的时候关键字 new 即:
new 的作用:实例化一个对象,并且只要有 new 内存就在堆上
❓ 那怎么打印数组的地址呢 👇
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
System.out.println(arr);
}
[I@1b6d3586
❓ 我们可以看到打印出来的数组地址是看不懂的,原因如下:👇
❗️ 在 Java 中栈上的地址是拿不到的,堆上的地址可以拿到,但不是真实的地址,是经过哈希加密的,但可以当做真实的地址并且地址是唯一的,所以如上那个加密过的地址即是数组 arr 的地址
💨2. 数组作为方法的参数
💦2.1 基本用法
❓ 如果在其他方法中需要用到 main 方法中的数组,那么就需要把数组作为参数传过去,代码怎样实现呢 👇
利用方法去打印数组:👇
public static void print(int[] arr){
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
}
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
print(arr);
}
❓ 那形参是如何对实参的数组进行操作的呢 👇
因为形参也指向数组所以在方法中可以操作数组
💦2.2 理解引用类型
1️⃣ 我们先看一个内置类型
public static void main(String[] args) {
int num = 0;
func(num);
System.out.println("num = " + num);
}
public static void func(int x) {
x = 10;
System.out.println("x = " + x);
}
x = 10
num = 0
我们看到调用 func 函数后并未对 num 的数值造成影响,因为这里调用当在 func 方法中对形参的数值改变在方法结束后形参就会销毁,更新数值并没有被带回来,所以实参不会被修改。
2️⃣ 参数传数组类型
public static void main(String[] args) {
int[] arr = {1, 2, 3};
func(arr);System.out.println("arr[0] = " + arr[0]);
}
public static void func(int[] a) {
a[0] = 10;
System.out.println("a[0] = " + a[0]);
}
// 执行结果
a[0] = 10
arr[0] = 10
我们已经知道 Java 中的数组是引用类型,引用类型就是引用对应对象在堆上的地址,上图的数组传参代码可以清晰看到在栈上定义的形参和实参都指向了在堆上的数组 ,所以这里形参对数组首元素的修改会对实参造成影响 👇
❗️ 总结: 所谓的 "引用" 本质上只是存了一个地址. Java 将数组设定成引用类型, 这样的话后续进行数组参数传参, 其实只是将数组的地址传入到函数形参中. 这样可以避免对整个数组的拷贝(数组可能比较长, 那么拷贝开销就会很大).
💦2.3 认识空对象 ( null )
我们知道 C 语言中有空指针 NULL 指向的是 0 地址处,然而 Java 中的空对象 null 代表的是空对象,不指向任何位置
用法:当定义引用类型时候,当它不指向任何位置的时候,可以把其赋值为 null👇
int[] arr = null
❗️ 使用 null 需要注意,由于是空对象,所以不能对其进行访问👇
int[] arr = null;
System.out.println(arr[0]);
// 执行结果
Exception in thread "main" java.lang.NullPointerException
at Test.main(Test.java:6)
❗️ 如果对空对象进行访问会导致程序出错,就会抛出 NullPointerException.
💦2.4 初识 JVM 内存区域划分
JVM 的内存被划分成了几个区域, 如图所示:
程序计数器 (PC Register): 只是一个很小的空间, 保存下一条执行的指令的地址.
虚拟机栈(JVM Stack): 重点是存储局部变量表(当然也有其他信息). 我们刚才创建的 int[] arr 这样的存储地 址的引用就是在这里保存.
本地方法栈(Native Method Stack): 本地方法栈与虚拟机栈的作用类似. 只不过保存的内容是Native方法的局 部变量. 在有些版本的 JVM 实现中(例如HotSpot), 本地方法栈和虚拟机栈是一起的.
堆(Heap): JVM所管理的最大内存区域. 使用 new 创建的对象都是在堆上保存 (例如前面的 new int[]{1, 2, 3} )
方法区(Method Area): 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数 据. 方法编译出的的字节码就是保存在这个区域.
运行时常量池(Runtime Constant Pool): 是方法区的一部分, 存放字面量(字符串常量)与符号引用. (注意 从 JDK 1.7 开始, 运行时常量池在堆上)
Native 方法:
JVM 是一个基于 C++ 实现的程序. 在 Java 程序执行过程中, 本质上也需要调用 C++ 提供的一些函数进行和操 作系统底层进行一些交互. 因此在 Java 开发中也会调用到一些 C++ 实现的函数.
这里的 Native 方法就是指这些 C++ 实现的, 再由 Java 来调用的函数.
1️⃣ 局部变量和引用保存在栈上, new 出的对象保存在堆上.
2️⃣ 堆的空间非常大, 栈的空间比较小.
3️⃣ 堆是整个 JVM 共享一个, 而栈每个线程具有一份(一个 Java 程序中可能存在多个栈).
💨3. 数组作为方法的返回值
当编写一个方法时,需要返回整个数组,Java 中提供这样的功能,即定义方法的返回值类型为对应数组类型即可,返回值为需要返回的数组名👇
// 返回一个新的数组
class Test {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
int[] output = transform(arr);
printArray(output);
}
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
public static int[] transform(int[] arr) {
int[] ret = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
ret[i] = arr[i] * 2;
}
return ret;
}
}
这样返回一个新的数组,就不会破坏原有数组了.
另外由于数组是引用类型, 返回的时候只是将这个数组的首地址返回给函数调用者, 没有拷贝数组内容, 从而比较高效.
💥4. Arrays实用的操作数组方法总结
Arrays:操作数组的工具类👇
import java.util.Arrays;
其有很多实用的功能这里简单做一下汇总👇
❗️ 如果是在进行操作时存在范围,范围一定是左闭右开的
1. binarySearch(二分查找)在数组中查找对应元素返回其下标
无范围: 👇
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
int ret = Arrays.binarySearch(arr,3);
System.out.println(ret);
}
2
有范围 : 👇
int ret2 = Arrays.binarySearch(arr,1,4,3);
2
如果在查询没有此元素,返回的下标数值为负数,即没有查询到
2.copyOf(数组拷贝)把一个数组的元素拷贝到另一个数组中
拷贝的数组长度作为拷贝的范围:👇
public static void main(String[] args) {
int[] arr1 = {1,2,3,4,5};
int[] arr2 = new int[arr1.length];
arr2 = Arrays.copyOf(arr1,arr1.length);
System.out.println(Arrays.toString(arr2));
}
1,2,3,4,5
3.copyOfRange指定范围的拷贝数组
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
int[] ret = Arrays.copyOfRange(arr,0,3);
System.out.println(Arrays.toString(ret));
}
1,2,3
4.equals判断两个相同类型的数组是否相等
public static void main(String[] args) {
int[] a = {1,2,3};
int[] b = {2,4,6};
boolean flg = Arrays.equals(a,b);
System.out.println(flg);
}
false
5.deepToString以字符串的样式打印二维数组
public static void main(String[] args) {
int[][] arr = {{1,2,3},{2,4,6}};
System.out.println(Arrays.deepToString(arr));
}
[[1, 2, 3], [2, 4, 6]]
6.fill 把数值赋值给数组每一个元素
无范围:👇
public static void main(String[] args) {
int[] arr = new int[4];
Arrays.fill(arr,1);
System.out.println(Arrays.toString(arr));
}
[1, 1, 1, 1]
有范围:👇
public static void main(String[] args) {
int[] arr = new int[4];
Arrays.fill(arr,0,3,1);
System.out.println(Arrays.toString(arr));
}
[1, 1, 1, 0]
7.sort 给数组进行排序
public static void main(String[] args) {
int[] arr = {5,4,9,2,6};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
[2, 4, 5, 6, 9]
8.toString 以字符串形式打印数组
public static void main(String[] args) {
int[] arr = {5,4,9,2,6};
String ret = Arrays.toString(arr);
System.out.println(ret);
}
[5, 4, 9, 2, 6]
总结几个拷贝方法:
1️⃣ Arrays.copyOf() 上面介绍了这里不过多赘述
2️⃣ .clone()
public static void main(String[] args) {
int[] arr = {5,4,9,2,6};
int[] ret = arr.clone();
System.out.println(Arrays.toString(ret));
}
[5, 4, 9, 2, 6]
3️⃣ for 循环遍历元素进行拷贝方法过于简单不展开
4️⃣ System.arraycopy拷贝数组(native方法实现速度最快)
public static void main(String[] args) {
int[] arr = {5, 4, 9, 2, 6};
int[] ret = new int[arr.length];
System.arraycopy(arr,0,ret,0,arr.length);
System.out.println(Arrays.toString(ret));
}
👉 两种拷贝模式:
1️⃣ 深拷贝:👇
2️⃣ 浅拷贝: 👇
❗️ 这两种拷贝模式都是可以相互转换的这就看编程者怎么设计程序了,所以四种拷贝方法是哪种拷贝模式是没有定式的
💨5. 二维数组
二维数组本质上也就是一维数组, 只不过每个元素又是一个一维数组.
基本语法:👇
数据类型[][] 数组名称 = new 数据类型 [行数][列数] { 初始化数据 }
二维数组在内存中存储的方式:👇
二维数组的三种打印方法:
❗️ Java 中的二维数组的行必须指定,列可以自动推导
❗️ 对于没有初始化的元素,默认值并不是 0 ,就是对应位置没有元素
public static void main(String[] args) {
int[][] arr = {{1,2},{2,3,4}};
System.out.println(Arrays.deepToString(arr));
}
[[1, 2], [2, 3, 4]]
以上是 Java 中相关数组的所有实用知识点,如果哪里有错误还请及时告知博主,如果文章对你有所帮助还请一键三连,谢谢大家的支持!!!!!!
💞 💞 💞 💞 💞 💞 💞 💞 💞 💞 💞 💞 💞 💞 💞 💞 💞 💞 💞 💞 💞 💞 💞 💞 💞 💞