📚博客主页:爱敲代码的小杨.
?专栏:《Java SE语法》
??感谢大家点赞👍🏻收藏?评论?🏻,您的三连就是我持续更新的动力??
假设现在要存储5个学生的年龄,按照之前掌握的知识点,我们会写出如下代码:声明5个变量存储学生变量
public class Test {
public static void main(String[] args) {
int age1;
int age2;
int age3;
int age4;
int age5;
}
}
如果我们有10个学生呢?我们就要声明20个变量,似乎没有什么问题。那如果有100,1000个学生呢,我们就要声明100,1000个变量,这样就有点离谱了,使用数组我们就可以解决一个问题。
数组,是指一组类型相同的数据的集合,数组中每个数据称为元素。数组可以存放任意类型的元素,但同一个数组里存放的元素类型必须一致。数组分为一维数组和多维数组。
数组在内存中是一段连续的空间,比如现实中的车库:
在 Java中,包含6个整形类型元素的数组,就相当于上图中连在一起的6个车位,从上图中可以看到:
数组中存放的元素其类型相同
数组的空间是连在一起的
每个空间有自己的编号,起始位置的编号为0,即数组的下标。
基本语法格式:
T[] 数组名 = new T[N];
T
:表示数组中存放元素的类型T[]
:表示数组类型N
:表示数组的长度代码示例:存储10个人的年龄
int[] ages = new int[10];
Java 数组初始化主要分为静态初始化以及动态初始化
动态初始化:在创建数组时,直接指定数组中元素的个数
int[] ages = new int[10];
动态初始化:在创建数组是不直接指定数据元素个数,而直接讲具体的数据内容进行指定
语法格式:
T[] 数组名 = {data1,data2,....data};
int[] ages = new {1,2,3,4,5};
【注意事项】
静态初始化虽然没有指定数组的长度,编译器在编译时会根据{}中元素个数来确定数组的长度。
静态初始化时, {}中数据类型必须与[]前数据类型一致。
静态初始化可以简写,省去后面的new T[]。
int[] arr = {1,3,2,5,4};
// 注意:虽然省去了new T[], 但是编译器编译代码时还是会还原
数组也可以按照如下C语言个数创建,不推荐
int arr[] = {1, 2, 3};
/*
该种定义方式不太友好,容易造成数组的类型就是int的误解
[]如果在类型之后,就表示数组类型,因此int[]结合在一块写意思更清晰
*/
静态和动态初始化也可以分为两步,但是省略格式不可以。
public class Main {
public static void main(String[] args) {
int[] array1;
array1 = new int[10];
int[] array2;
array2 = new int[]{10, 20, 30};
// 注意省略格式不可以拆分, 否则编译失败
//int[] array3;
//array3 = {1, 2, 3};
}
}
如果没有对数组进行初始化,数组中元素有其默认值
如果数组中存储元素类型为基类类型,默认值为基类类型对应的默认值,比如:
类型 | 默认值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0 |
float | 0.0f |
double | 0.0 |
char | /u0000 |
boolean | false |
如果数组中存储元素类型为引用类型,默认值为null
数组在内存中是一段连续的空间,空间的编号都是从0开始的,依次递增,该编号称为数组的下标,数组可以通过下标访问其任意位置的元素。比如:
public class Main {
public static void main(String[] args) {
int[] arr = new int[]{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]);
}
}
【注意事项】:
数组是一段连续的内存空间,因此支持随机访问,即通过下标快速访问数组中任意位置的元素
下标从0开始,介于[0,N) 之间不包含N,N为元素个数,不能越界,否则会报出下标越界异常。
抛出了 java.lang.ArrayIndexOutOfBoundsException
异常. 使用数组一定要下标谨防越界.
所谓 “遍历” 是指将数组中的所有元素都访问一遍, 访问是指对数组中的元素进行某种操作,比如:打印。
public class Main {
public static void main(String[] args) {
int[] arr = new int[]{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]);
}
}
上述代码可以起到对数组中元素遍历的目的,但问题是:
如果数组中增加了一个元素,就需要增加一条打印语句
如果输入中有100个元素,就需要写100个打印语句
如果现在要把打印修改为给数组中每个元素加1,修改起来非常麻烦。
通过观察代码可以发现,对数组中每个元素的操作都是相同的,则可以使用循环来进行打印。
1. 循环遍历数组
public class Main {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5};
for (int i = 0; i < 5; i++) {
System.out.println(arr[i]);
}
}
}
改成循环之后,上述三个缺陷可以全部2和3问题可以全部解决,但是无法解决问题1。那能否获取到数组的长度呢?
【注意】:在数组中可以通过 数组对象.length
来获取数组的长度
public class Main {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5};
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
2. 使用 for-each
遍历数组
语法格式:
public class Main {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5};
for (int x : arr) {
System.out.println(x);
}
}
}
for-each
是 for
循环的另外一种使用方式. 能够更方便的完成对数组的遍历. 可以避免循环条件和更新语句写错.
for-each
循环语句的循环变量将会遍历数组中的每个元素,而不是下标值。
3. 数组转字符串输出
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5};
String ret = Arrays.toString(arr);
System.out.println(ret);
}
}
代码分析:
内存是一段连续的存储空间,主要是用来存储程序运行时数据的。比如:
如果对内存中存储的数据不加区分的随意存储,那对内存管理起来将会非常麻烦。比如:
因此 JVM 也对所使用的内存按照功能的不同进行了划分:
new
创建的对象都是在堆上保存,堆是随着程序开始运行时而创建,随着程序的结束而销毁,堆中的数据只要还有在使用,就不会被销毁**基本数据类型的变量,称为基本变量,该变量空间中直接存放的是其所对应的值;
而引用数据类型创建的变量,一般称为对象的引用,其空间中存储的是对象所在空间的地址
public class Main {
public static void main(String[] args) {
int a = 10;
int[] arr = new int[]{1,2,3};
}
}
在上述代码中,a
、arr
,都是函数内部的变量,因此其空间都在main
方法对应的栈帧中分配。
a
是内置类型的变量,因此其空间中保存的就是给该变量初始化的值。
arr
是数组类型的引用变量,其内部保存的内容可以简单理解成是数组在堆空间中的首地址。
上图可以看出,引用变量并不直接存储对象本生,可以简单理解成存储的是对象在堆中空间的起始地址。通过该地址,引用变量便可以去操作对象。有点类似C语言中的指针,但是 Java 中引用要比指针的操作更简单。
public class Main {
public static void main(String[] args) {
int[] arr1 = new int[3];
arr1[0] = 1;
arr1[1] = 2;
arr1[2] = 3;
int[] arr2 = new int[]{1,2,3,4,5};
arr2[0] = 100;
arr2[1] = 200;
arr1 = arr2;
arr1[2] = 300;
arr1[3] = 400;
arr2[4] = 500;
for (int x : arr1) {
System.out.println(x);
}
}
}
null
在 Java 中表示“空引用”,也就是一个不指向对象的引用
public class Main {
public static void main(String[] args) {
int[] arr = null;
System.out.println(arr[0]);
}
}
null
的作用类似于C语言中的NULL
(空指针),都是表示一个无效的内存位置。因此不能对这个内存进行任何读写操作。一旦尝试读写,就会抛出NullPointerException
【注意】:Java 中并没有约定
null
和 0 下标地址的内存有任何关联。
public class Main {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3};
for (int x : arr) {
System.out.println(x);
}
}
}
参数传基本数据类型
public class Main {
public static void main(String[] args) {
int num = 0;
func(num);
System.out.println("num = " + num);// 0
}
private static void func(int x) {
x = 10;
System.out.println("x = " + x); // 10
}
}
上述代码我们可以发现func
方法中修改了形参x
的值,不影响实参的num
值。
参数传引用数据类型
public class Main {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3};
fun1(arr);
System.out.println(Arrays.toString(arr)); // [1,2,3]
fun2(arr);
System.out.println(Arrays.toString(arr)); // [99,2,3]
}
public static void fun1(int[] arr) {
arr = new int[]{11,22,33,44,55}; // 修改了形参的指向
}
public static void fun2(int[] arr) {
arr[0] = 99; // 形参改变了实惨的值
}
}
上述代码我们可以发现fun1
方法中修改了形参的指向,不影响实参数组的值
fun2
方法内部修改了数组的内容,方法外部的数组内容也发生了改变。因为数组是引用类型,按照引用类型进行传递,是可以修改其中存放的内容的。
【总结】:所谓的“引用”本质只是存了地址。Java 将数组设定为引用类型,这样的话后续进行数组参数传参,其实只是将数组的地址传入函数形参中,这样可以避免对整数数组的拷贝(数组可能比较长,那么拷贝开销就会很大)。
public class Main {
public static void main(String[] args) {
int[] ret = fun();
System.out.println(Arrays.toString(ret)); // [1, 2, 3, 4, 5]
}
public static int[] fun() {
int[] arr = new int[]{1,2,3,4,5};
return arr;
}
}
二维数组本质上也就是一维数组,只不过每个元素又是一个一维数组
基本语法:
数据类型[][] 数组名称 = new 数据类型 [行数][列数] { 初始化数据 };
代码示例:
public class Main {
public static void main(String[] args) {
int[][] arr = {{1, 2, 3},{4,5,6}};
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
System.out.println("=======");
for (int[] tempArr : arr) {
for (int x : tempArr) {
System.out.print(x + " ");
}
System.out.println();
}
System.out.println("=======");
String ret = Arrays.deepToString(arr); // deepToString()深度打印
System.out.println(ret);
}
}
Java 二维数组在定义的时候是可以省略列的
int[][] arr = new int[2][];
二维数组的用法和一维数组并没有明显差别, 因此我们不再赘述.
同理, 还存在 “三维数组”, “四维数组” 等更复杂的数组, 只不过出现频率都很低.
代码示例:
public class Main {
public static void main(String[] args) {
int[][] arr = new int[2][];
// 每一个一维数组 进行初始化
arr[0] = new int[3];
arr[1] = new int[5];
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
}
}
// 运行结果
0 0 0
0 0 0 0 0