[关闭]
@xiaohaizi 2019-03-13T05:00:47.000000Z 字数 3288 阅读 892

java入门online

8、java方法简介(下)

上集回顾

上集说道方法是什么,为啥要弄个方法的概念,方法是由哪些部分组成的,方法是怎么使用的, 应该大致上对方法长什么样有个大致了解吧。。本集将会仔细剖析方法执行过程中到底是怎么操纵内存格子的(这个非常非常重要)。。。

方法中内存格子的分配

继续看那个求两个数的和的方法:

  1. public static void main(String[] args) {
  2. int i = 1;
  3. int j = 2;
  4. int sum = add(i, j);
  5. System.out.println("sum = " + sum);
  6. }
  7. public static int add(int x, int y) {
  8. int result = x + y;
  9. return result;
  10. }

我们画一下这玩意在内存里是怎么跑的

  1. 首先:

    1. int i = 1;
    2. int j = 2;

    内存格子分配(申请了2个int型的格子, 一个叫里边的值 是 1, 一个里边的值 是 2):

    image_1bqc1crsi3ikbf71ppilkaan79.png-11.4kB

  2. 调用方法 add

    1. int sum = add(i, j);

    i, j的值赋值给 add(int x, int y) 中的 x, y变量:

    image_1bqc1e0qj1va01mpm10ad7uj10htm.png-28.7kB

  3. 执行方法 addCPU将 内存格子 x, y的值相加后存到格子result中:

    image_1bqc1f9n415l81jupsm1jla1avo13.png-45kB

  4. 将返回结果result 赋值给sum变量

    1. int sum = add(i, j);

    image_1bqc1g9r21bupeot1iq4hf91u091g.png-53.3kB

  5. 输出sum变量里的值

    1. System.out.println("sum = " + sum);

    屏幕上输出:

    1. sum = 3

内存格子(变量)是什么时候被回收的

一台电脑的内存就那么点,比如我这个本本的内存是8GB,我们老早之前虽然说GB已经是很大的地方了,可是你挡不住各种程序不断跑,不停地去申请内存呀。

有申请就有释放,你程序都跑完了还占着内存格子不就和占着茅坑不拉💩是一个道理么。我们说的释放内存的意思是这些内存格子可以被再次用来分配给别的程序使用。

这些内存格子,或者说变量,是什么时候被回收的呢:

  1. 在方法执行的时候申请
  2. 在方法执行结束后释放

看到上边的两句至理名言了吧,我知道你看不懂,所以开始讲🌰:

  1. public static void main(String[] args) {
  2. int i = 1;
  3. int j = 2;
  4. int sum = add(i, j);
  5. System.out.println("sum = " + sum);
  6. }
  7. public static int add(int x, int y) {
  8. int result = x + y;
  9. return result;
  10. }

我们继续拿上边的例子说事,这段代码其实包含了两个方法:
1. main方法
2. add方法

其中,add方法被 main方法调用, 看图:

image_1bqc1im961lvpekikh61mu13c1t.png-249.3kB

其中:

浅红色代表 main方法,浅蓝色代表add方法,在调用main方法的过程中调用了add方法

  1. main方法开始执行,在内存中申请变量 i, j
  2. 调用add方法 的时候 申请了 x, y变量,并且把i, j的值赋值到x, y变量里
  3. CPU将i, j相加之后的值交给变量 result
  4. result变量的值复制到sum变量
  5. add方法执行结束,x, y, result变量都没用了,就可以把它们释放掉了。
  6. 输出sum变量的值
  7. main方法执行结束,i, jsum这些变量也都木有用了,都可以被回收释放掉了。

仔细读上边的这段话,然后再看这两句话

  1. 在方法执行的时候申请
  2. 在方法执行结束后释放

喔,我知道聪明的你应该能看懂了。

另类的内存分配方式

下边我们再看一个程序:

  1. public static void main(String[] args) {
  2. int[] arr = new int[]{13, 2, 4, 43, 12};
  3. int result = findMax(arr);
  4. System.out.println("result = " + result);
  5. }
  6. public static int findMax(int[] array) {
  7. int max = Integer.MIN_VALUE;
  8. for (int i : array) {
  9. if (max < i) {
  10. max = i;
  11. }
  12. }
  13. return max;
  14. }

我们再来一步步地瞅瞅这个程序的内存分配过程

  1. 分配数组内存

    1. int[] arr = new int[]{13, 2, 4, 43, 12};

    看图:

    image_1bqc1kuk3j3fmod1gks19k1jb62a.png-292.1kB

    这个数组的内存分配有点奇怪唉。。。怎么一半在什么栈内存,一半在什么堆内存,这个栈内存堆内存是个什么鬼?

    栈内存 是为基本数据类型数据 或者 非基本数据类型的地址 而分配的内存。在方法调用时分配好,方法调用完被回收。

    堆内存 是为非基本数据类型的数据 分配的内存,方法调用结束不立即回收。

    上头说的你肯定有点晕吧,来来来,我们解释一下上边的图是个啥意思:

    ①. 我们要申请一个数组的内存,数组不是基本数据类型,所以我们在堆内存里开辟了一块儿空间,然后把 13, 2, 4, 43, 12都塞了进去(右边蓝色的图)。

    ②. 在堆内存里开辟了一块儿空间,我们把这块空间的==地址==保存到栈内存的arr变量中(这个变量实际具体存的数据是啥东西?这个不确定,每次运行的内存格子地址都不一样)。通过栈内存的arr变量,我们就能访问堆内存中数组的实际内容

    疑问😖:为啥弄这么弯弯绕,不能都放在一个地方存么,还要分什么栈内存,堆内存?搞得人家头都晕了。。

    答:这是一个基础教程, 我们不负责解释为啥分两块, 其实事实上不止这两块内存区域😄😄。如果你非要搞个为什么,请参考堆和栈访问效率哪个更高?(估计你看不懂😝😝😝),当然我们在讲高级部分会讲解为啥要区分的。

  2. 调用findMax方法

    1. int result = findMax(arr);

    image_1bqc1pbg81haj133p1a261np61eoc2n.png-165kB

    arr的值赋值给 findMax(int[] array) 中的 array,本质上是把地址传过去了,在堆内存中的数组格子实际啥也没变,完成赋值之后array变量里的地址也指向了堆内存中的数组数据。

  3. 执行方法 findMax,执行结果就是 找到数组中的最大值,将最大值43保存到了max变量中:

    image_1bqc1qhlu10aa28v1274b3q17gk34.png-395.1kB

  4. 将返回结果max 赋值给result变量

    1. int result = findMax(arr);

    image_1bqc1rbep198bob0h5812np9if3h.png-334.3kB

  5. 输出result变量里的值

    1. System.out.println("result = " + result);

    屏幕上输出:

    1. sum = 43

注意⚠️:

方法调用时,基本数据类型在做参数传递的时候复制的是变量的值,非基本数据类型是传递的地址

举个例子:

①. 基本数据类型作参数

  1. public static void main(String[] args) {
  2. int x = 1;
  3. modifyVariable(x);
  4. System.out.println("x = " + x); //输出结果是1,为什么?自己画个调用方法时的变量图看看。
  5. }
  6. public static void modifyVariable(int x) {
  7. x = 2;
  8. }

②. 非基本数据类型作参数, 改变实际数据

  1. public static void main(String[] args) {
  2. int[] arr = {1, 2, 3};
  3. modifyArray(arr);
  4. for (int i = 0; i < 3; i++) {
  5. System.out.println("arr[" + i + "] = " + a[i]);
  6. }
  7. //输出结果是 4, 5, 6,为什么?自己画个调用方法时的变量图看看。
  8. }
  9. public static void modifyArray(int[] arr) {
  10. arr[0] = 4;
  11. arr[1] = 5;
  12. arr[2] = 6
  13. }

③. 非基本数据类型作参数, 不改变实际数据,只操作了地址

  1. public static void main(String[] args) {
  2. int[] arr = {1, 2, 3};
  3. modifyArray(arr);
  4. for (int i = 0; i < 3; i++) {
  5. System.out.println("arr[" + i + "] = " + arr[i]);
  6. }
  7. //输出结果是 1, 2, 3,为什么?自己画个调用方法时的变量图看看。
  8. }
  9. public static void modifyArray(int[] arr) {
  10. arr new int[]{4, 5, 6};
  11. }

总结

  1. 方法在执行过程中内存的分配过程。
  2. 栈内存堆内存的划分,基本数据类型和非基本数据类型的地址用栈内存储存,非基本数据类型的实际数据在堆内存中储存
  3. 非基本类型申请内存时申请两个地方的内存

    堆内存,里边储存实际数据

    栈内存,用来放置指向实际数据(堆内存里的数据)的地址。

  4. 方法调用时在参数传递的时候分清楚传递的是数据还是地址
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注