数字的定义及使用
1.数组基本用法
1.1 什么是数组
数组本质上就是让我们能
"
批量
"
创建相同类型的变量
.
例如
:
如果需要表示两个数据
,
那么直接创建两个变量即可
int a
;
int b
如果需要表示五个数据
,
那么可以创建五个变量
int a1
;
int a2
;
int a3
;
int a4
;
int a5
;
但是如果需要表示一万个数据
,
那么就不能创建一万个变量了
.
这时候就需要使用数组
,
帮我们批量创建
.
注意事项:
在
Java
中
,
数组中包含的变量必须是
相同类型
.
1.2 创建数组
代码示例
// 动态初始化
数据类型[] 数组名称 = new 数据类型 [] { 初始化数据 };
// 静态初始化
数据类型[] 数组名称 = { 初始化数据 };
int[] arr = new int[]{1, 2, 3};
int[] arr = {1, 2, 3};
其实数组也可以写成
int arr[] = {1, 2, 3};
注意事项:
静态初始化的时候
,
数组元素个数和初始化数据的格式是一致的
.
1.3 数组的使用
代码示例
:
获取长度
&
访问元素
int[] arr = {1, 2, 3};
// 获取数组长度
System.out.println("length: " + arr.length); // 执行结果: 3
// 访问数组中的元素
System.out.println(arr[1]); // 执行结果: 2
System.out.println(arr[0]); // 执行结果: 1
注意事项
1.
使用
arr.length
能够获取到数组的长度
.
.
这个操作为成员访问操作符
.
后面在面向对象中会经常用到
.
2.
使用
[ ]
按下标取数组元素
.
需要注意
,
下标从
0
开始计数
3.
使用
[ ]
操作既能读取数据
,
也能修改数据
.
4.
下标访问操作不能超出有效范围
[0, length - 1] ,
如果超出有效范围
,
会出现下标越界异常
代码示例
:
下标越界
arr[2] = 100;
System.out.println(arr[2]); // 执行结果: 100
int[] arr = {1, 2, 3};
System.out.println(arr[100]);
// 执行结果
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 100
at Test.main(Test.java:4)
抛出了
java.lang.ArrayIndexOutOfBoundsException
异常
.
使用数组一定要下标谨防越界
.
代码示例
:
遍历数组
所谓
"
遍历
"
是指将数组中的所有元素都访问一遍
,
不重不漏
.
通常需要搭配循环语句
.
代码示例
:
使用
for-each
遍历数组
for-each
是
for
循环的另外一种使用方式
.
能够更方便的完成对数组的遍历
.
可以避免循环条件和更新语句写错
.
2. 数组作为方法的参数
2.1 基本用法
代码示例
:
打印数组内容
int[] arr = {1, 2, 3};
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
// 执行结果
1
2
3
int[] arr = {1, 2, 3};
for (int x : arr) {
System.out.println(x);
}
// 执行结果
1
2
3
public static void main(String[] args) {
int[] arr = {1, 2, 3};
printArray(arr);
}
public static void printArray(int[] a) {
for (int x : a) {
System.out.println(x);
}
}
// 执行结果
1
2
3
在这个代码中 int[] a 是函数的形参, int[] arr 是函数实参. 如果需要获取到数组长度, 同样可以使用 a.length
2.2 理解引用类型
我们尝试以下代码
代码示例
参数传数组类型
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
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
我们发现
,
在函数内部修改数组内容
,
函数外部也发生改变
.
此时数组名
arr
是一个
"
引用
" .
当传参的时候
,
是按照引用传参
.
这里我们要先从内存开始说起
.
a)
当我们创建
new int[]{1, 2, 3}
的时候
,
相当于创建了一块内存空间保存三个
int
b)
接下来执行
int[] arr = new int[]{1, 2, 3}
相当于又创建了一个
int[]
变量
,
这个变量是一个引用类型
, 面只保存了一个整数(
数组的起始内存地址
)

c)
接下来我们进行传参相当于
int[] a = arr
,
内存布局如图

d)
接下来我们修改
a[0] ,
此时是根据
0x100
这样的地址找到对应的内存位置
,
将值改成
100

接下来我们修改
a[0] ,
此时是根据
0x100
这样的地址找到对应的内存位置
,
将值改成
100
此时已经将
0x100
地址的数据改成了
100 .
那么根据实参
arr
来获取数组内容
arr[0]
,
本质上也是获取
0x100
地址上的数据
,
也是
100.
总结
:
所谓的
"
引用
"
本质上只是存了一个地址
. Java
将数组设定成引用类型
,
这样的话后续进行数组参数传参
,
其实
只是将数组的地址传入到函数形参中
.
这样可以避免对整个数组的拷贝
(
数组可能比较长
,
那么拷贝开销就会很大
).
2.3 认识 null
null
在
Java
中表示
"
空引用
" ,
也就是一个无效的引用
.
null
的作用类似于
C
语言中的
NULL (
空指针
),
都是表示一个无效的内存位置
.
因此不能对这个内存进行任何读写操
作
.
一旦尝试读写
,
就会抛出
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
来调用的函数
.
我们发现
,
在上面的图中
,
程序计数器
,
虚拟机栈
,
本地方法栈被很多个原谅色的
,
名叫
Thread(
线程
)
的方框圈起来了
,
并且存在很多份
.
而 堆
,
方法区
,
运行时常量池
,
只有一份
. (
关于线程
,
这是我们后面重点讲解的内容
).
关于上面的划分方式
,
我们随着后面的学习慢慢理解
.
此处我们重点理解 虚拟机栈 和 堆
.
局部变量和引用保存在栈上
, new
出的对象保存在堆上
.
堆的空间非常大
,
栈的空间比较小
.
堆是整个
JVM
共享一个
,
而栈每个线程具有一份
(
一个
Java
程序中可能存在多个栈
).
3. 数组作为方法的返回值
代码示例
:
写一个方法
,
将数组中的每个元素都
* 2
// 直接修改原数组
class Test {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
transform(arr);
printArray(arr);
}
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
public static void transform(int[] arr) {
for (int i = 0; i < arr.length; i++) {
arr[i] = arr[i] * 2;
}
}
}
这个代码固然可行
,
但是破坏了原有数组
.
有时候我们不希望破坏原数组
,
就需要在方法内部创建一个新的数组
,
并由
方法返回出来
.
这样的话就不会破坏原有数组了
.
另外由于数组是引用类型
,
返回的时候只是将这个数组的首地址返回给函数调用者
,
没有拷贝数组内容
,
从而比较高
效
.
4. 数组练习
4.1 数组转字符串
代码示例
// 返回一个新的数组
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;
}
}
import java.util.Arrays
int[] arr = {1,2,3,4,5,6};
String newArr = Arrays.toString(arr);
System.out.println(newArr);
// 执行结果
[1, 2, 3, 4, 5, 6]
使用这个方法后续打印数组就更方便一些
.
Java
中提供了
java.util.Arrays
包
,
其中包含了一些操作数组的常用方法
.
什么是包
?
例如做一碗油泼面
,
需要先和面
,
擀面
,
扯出面条
,
再烧水
,
下锅煮熟
,
放调料
,
泼油
.
但是其中的
"
和面
,
擀面
,
扯出面条
"
环节难度比较大
,
不是所有人都能很容易做好
.
于是超市就提供了一些直接
已经扯好的面条
,
可以直接买回来下锅煮
.
从而降低了做油泼面的难度
,
也提高了制作效率
.
程序开发也不是从零开始
,
而是要站在巨人的肩膀上
.
像我们很多程序写的过程中不必把所有的细节都自己实现
,
已经有大量的标准库
(JDK
提供好的代码
)
和海量的
第三方库
(
其他机构组织提供的代码
)
供我们直接使用
.
这些代码就放在一个一个的
"
包
"
之中
.
所谓的包就相当
于卖面条的超市
.
只不过
,
超市的面条只有寥寥几种
,
而我们可以使用的
"
包
" ,
有成千上万
.
我们实现一个自己版本的数组转字符串
4.2 数组拷贝
代码示例
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6};
System.out.println(toString(arr));
}
public static String toString(int[] arr) {
String ret = "[";
for (int i = 0; i < arr.length; i++) {
// 借助 String += 进行拼接字符串
ret += arr[i];
// 除了最后一个元素之外, 其他元素后面都要加上 ", "
if (i != arr.length - 1) {
ret += ", ";
}
}
ret += "]";
return ret;
}
注意事项:
相比于
newArr = arr
这样的赋值
, copyOf
是将数组进行了
深拷贝
,
即又创建了一个数组对象
,
拷贝原有
数组中的所有元素到新数组中
.
因此
,
修改原数组
,
不会影响到新数组
.
实现自己版本的拷贝数组
4.3 找数组中的最大元素
给定一个整型数组
,
找到其中的最大元素
(
找最小元素同理
)
代码示例
import java.util.Arrays
int[] arr = {1,2,3,4,5,6};
int[] newArr = Arrays.copyOf(arr, arr.length);
System.out.println("newArr: " + Arrays.toString(newArr));
arr[0] = 10;
System.out.println("arr: " + Arrays.toString(arr));
System.out.println("newArr: " + Arrays.toString(newArr));
// 拷贝某个范围.
int[] newArr = Arrays.copyOfRange(arr, 2, 4);
System.out.println("newArr2: " + Arrays.toString(newArr2));
public static int[] copyOf(int[] arr) {
int[] ret = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
ret[i] = arr[i];
}
return ret;
}
类似于
"
打擂台
"
这样的过程
.
其中
max
变量作为
擂台
,
比擂台上的元素大
,
就替换上去
,
否则就下一个对手
.
4.4 求数组中元素的平均值
给定一个整型数组
,
求平均值
代码示例
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6};
System.out.println(max(arr));
}
public static int max(int[] arr) {
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
// 执行结果
6
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6};
System.out.println(avg(arr));
}
public static double avg(int[] arr) {
int sum = 0;
for (int x : arr) {
sum += x;
}
return (double)sum / (double)arr.length;
}
// 执行结果
3.5
注意事项:
结果要用
double
来表示
.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现