@xiaohaizi
2019-03-13T05:00:47.000000Z
字数 3288
阅读 1004
java入门online
上集说道方法
是什么,为啥要弄个方法
的概念,方法
是由哪些部分组成的,方法
是怎么使用的, 应该大致上对方法
长什么样有个大致了解吧。。本集将会仔细剖析方法执行过程中到底是怎么操纵内存格子的(这个非常非常重要)。。。
继续看那个求两个数的和的方法:
public static void main(String[] args) {
int i = 1;
int j = 2;
int sum = add(i, j);
System.out.println("sum = " + sum);
}
public static int add(int x, int y) {
int result = x + y;
return result;
}
我们画一下这玩意在内存里是怎么跑的
首先:
int i = 1;
int j = 2;
内存格子分配(申请了2个int型的格子, 一个叫里边的值 是 1
, 一个里边的值 是 2
):
调用方法 add
int sum = add(i, j);
将i
, j
的值赋值给 add(int x, int y)
中的 x
, y
变量:
执行方法 add
,CPU
将 内存格子 x
, y
的值相加后存到格子result
中:
将返回结果result
赋值给sum
变量
int sum = add(i, j);
输出sum
变量里的值
System.out.println("sum = " + sum);
屏幕上输出:
sum = 3
一台电脑的内存就那么点,比如我这个本本的内存是8GB
,我们老早之前虽然说GB
已经是很大的地方了,可是你挡不住各种程序不断跑,不停地去申请内存呀。
有申请就有释放,你程序都跑完了还占着内存格子不就和占着茅坑不拉💩是一个道理么。我们说的释放内存的意思是这些内存格子可以被再次用来分配给别的程序使用。
这些内存格子,或者说变量
,是什么时候被回收的呢:
在方法执行的时候申请
在方法执行结束后释放
看到上边的两句至理名言了吧,我知道你看不懂,所以开始讲🌰:
public static void main(String[] args) {
int i = 1;
int j = 2;
int sum = add(i, j);
System.out.println("sum = " + sum);
}
public static int add(int x, int y) {
int result = x + y;
return result;
}
我们继续拿上边的例子说事,这段代码其实包含了两个方法:
1. main
方法
2. add
方法
其中,add
方法被 main
方法调用, 看图:
其中:
浅红色代表 main
方法,浅蓝色代表add
方法,在调用main
方法的过程中调用了add
方法
main
方法开始执行,在内存中申请变量 i
, j
add
方法 的时候 申请了 x
, y
变量,并且把i
, j
的值赋值到x
, y
变量里i
, j
相加之后的值交给变量 result
result
变量的值复制到sum
变量add
方法执行结束,x
, y
, result
变量都没用了,就可以把它们释放掉了。sum
变量的值main
方法执行结束,i
, j
, sum
这些变量也都木有用了,都可以被回收释放掉了。仔细读上边的这段话,然后再看这两句话
在方法执行的时候申请
在方法执行结束后释放
喔,我知道聪明的你应该能看懂了。
下边我们再看一个程序:
public static void main(String[] args) {
int[] arr = new int[]{13, 2, 4, 43, 12};
int result = findMax(arr);
System.out.println("result = " + result);
}
public static int findMax(int[] array) {
int max = Integer.MIN_VALUE;
for (int i : array) {
if (max < i) {
max = i;
}
}
return max;
}
我们再来一步步地瞅瞅这个程序的内存分配过程
分配数组内存
int[] arr = new int[]{13, 2, 4, 43, 12};
看图:
这个数组的内存分配有点奇怪唉。。。怎么一半在什么栈内存
,一半在什么堆内存
,这个栈内存
和堆内存
是个什么鬼?
栈内存
是为基本数据类型数据 或者 非基本数据类型的地址 而分配的内存。在方法调用时分配好,方法调用完被回收。
堆内存
是为非基本数据类型的数据 分配的内存,方法调用结束不立即回收。
上头说的你肯定有点晕吧,来来来,我们解释一下上边的图是个啥意思:
①. 我们要申请一个数组的内存,数组不是基本数据类型,所以我们在堆内存里开辟了一块儿空间,然后把 13, 2, 4, 43, 12
都塞了进去(右边蓝色的图)。
②. 在堆内存里开辟了一块儿空间,我们把这块空间的==地址==保存到栈内存的arr
变量中(这个变量实际具体存的数据是啥东西?这个不确定,每次运行的内存格子地址都不一样)。通过栈内存的arr
变量,我们就能访问堆内存中数组的实际内容
疑问😖:为啥弄这么弯弯绕,不能都放在一个地方存么,还要分什么栈内存,堆内存?搞得人家头都晕了。。
答:这是一个基础教程, 我们不负责解释为啥分两块, 其实事实上不止这两块内存区域😄😄
。如果你非要搞个为什么,请参考堆和栈访问效率哪个更高?(估计你看不懂😝😝😝),当然我们在讲高级部分会讲解为啥要区分的。
调用findMax
方法
int result = findMax(arr);
将arr
的值赋值给 findMax(int[] array)
中的 array
,本质上是把地址传过去了,在堆内存中的数组格子实际啥也没变,完成赋值之后array
变量里的地址也指向了堆内存中的数组数据。
执行方法 findMax
,执行结果就是 找到数组中的最大值,将最大值43
保存到了max
变量中:
将返回结果max
赋值给result
变量
int result = findMax(arr);
输出result
变量里的值
System.out.println("result = " + result);
屏幕上输出:
sum = 43
注意⚠️:
方法调用时,基本数据类型在做参数传递的时候复制的是变量的值,非基本数据类型是传递的地址。
举个例子:
①. 基本数据类型作参数
public static void main(String[] args) {
int x = 1;
modifyVariable(x);
System.out.println("x = " + x); //输出结果是1,为什么?自己画个调用方法时的变量图看看。
}
public static void modifyVariable(int x) {
x = 2;
}
②. 非基本数据类型作参数, 改变实际数据
public static void main(String[] args) {
int[] arr = {1, 2, 3};
modifyArray(arr);
for (int i = 0; i < 3; i++) {
System.out.println("arr[" + i + "] = " + a[i]);
}
//输出结果是 4, 5, 6,为什么?自己画个调用方法时的变量图看看。
}
public static void modifyArray(int[] arr) {
arr[0] = 4;
arr[1] = 5;
arr[2] = 6
}
③. 非基本数据类型作参数, 不改变实际数据,只操作了地址
public static void main(String[] args) {
int[] arr = {1, 2, 3};
modifyArray(arr);
for (int i = 0; i < 3; i++) {
System.out.println("arr[" + i + "] = " + arr[i]);
}
//输出结果是 1, 2, 3,为什么?自己画个调用方法时的变量图看看。
}
public static void modifyArray(int[] arr) {
arr = new int[]{4, 5, 6};
}
栈内存
和堆内存
的划分,基本数据类型和非基本数据类型的地址用栈内存储存,非基本数据类型的实际数据在堆内存中储存非基本类型申请内存时申请两个地方的内存
①堆内存
,里边储存实际数据
②栈内存
,用来放置指向实际数据(堆内存里的数据)的地址。