java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java方法的参数传递

Java方法的参数传递机制详解

作者:luffylv

这篇文章主要介绍了Java方法的参数传递机制详解,对于Java初学者来说,刚学习Java的时候可能经常会听到调用方法时参数的值传递与引用传递,但是,实际上Java中方法的参数传递机制只有值传递,需要的朋友可以参考下

前言

对于Java初学者来说,刚学习Java的时候可能经常会听到调用方法时参数的值传递与引用传递。

但是,实际上Java中方法的参数传递机制只有值传递。 首先,我们要了解一个概念——栈帧。

栈帧位于java虚拟机栈中,用于支持虚拟机进行方法的调用和方法的执行。可以简单的理解为栈帧即方法。

每个方法有自己独立的栈帧。栈帧中有局部变量表、操作数栈、动态链接、返回地址等。

下面我们通过几个例子具体分析java方法的参数传递机制。

方法的参数为基本数据类型

首先我们来看下面代码:

public class ParamTransmit {
    public static void main(String[] args) {
        int i = 1;
        change(i);
        System.out.println("i = " + i);
    }
    public static void change(int j) {
        j += 1;
    }
}

程序的运行结果为:

在这里插入图片描述

当在main方法中调用change方法时,参数传递相当于将main栈帧中的参数i=1拷贝了一份给change栈帧,此时change栈帧中,变量j的值为1。然后在change方法中执行j += 1;运算,j的值变为2,change方法结束。main方法未结束,此时main栈帧中变量i的值仍为1。当传递的参数是基本数据类型时,传递的是数据值,即将参数拷贝一份到被调用方法的栈帧中。该方法中对变量的操作不影响原来栈帧中的变量的值,原栈帧中的变量不被修改。

在这里插入图片描述

方法的参数为引用类型

引用类型变量如String类型,包装类,数组和其他自定义类等。下面依次介绍。

String类型

先上代码

public class ParamTransmit {
    public static void main(String[] args) {
        String str = "hello";
        change(str);
        System.out.println("str = " + str);
    }
    public static void change(String str) {
        str += "world";
    }
}

程序运行结果为:

在这里插入图片描述

首先,在jdk1.7之后,运行时常量池从方法区中移了出来,在堆中开辟了一块区域存放运行时常量。

并且要知道String的值是不可变的,每次对String的操作都会产生新的String对象。

因此,在上面代码中,main方法中调用change方法时,首先是将变量str的值(即运行时常量池中hello的地址)拷贝一份到change栈帧中,此时change栈帧中的变量str也是指向“hello”。

然后执行str += "world";操作,由于String值不可变,此时change栈帧中的str指向新的字符串“helloworld”,str的地址值已改变。

而此时main栈帧中str的值并没有改变,仍指向“hello”,所以main方法中输出的str仍是“hello”。

在这里插入图片描述

包装类

先上代码:

public class ParamTransmit {
    public static void main(String[] args) {
        Integer num = 200;
        change( num);
        System.out.println("num = " + num);
    }
    private static void change(Integer num) {
        num += 1;
    }
}

程序运行结果为:

在这里插入图片描述

上面代码中,首先Integer num = 200;会自动装箱,先调用Integer.valueOf(200),在堆中创建一个值为200的Integer实例,并将该实例的地址赋值给num。

main方法在调用change方式时传递的是num的地址值,此时change栈帧中的num也指向值为200的Integer实例。

当执行num += 1;时,也自动装箱,先调用Integer.valueOf(201),在堆中创建一个职位201的Integer实例,并将该实例的地址赋值给change栈帧中的变量num,change方法结束。

但此时,main栈帧的num仍指向值为200的Integer实例,因此打印结果为200。

内存结构图如下:

在这里插入图片描述

注:上述内存结构图仅适用于不在-128至127之间的整数。Integer类的自动装箱机制是首先提供一个Integer cache[],用于存放-128至127的缓存。因此,若对实参及对形参操作的结果均在-128至127之间,则实参及形参应指向数组中的元素,而不是堆中新的Integer实例。

数组

直接上代码

import java.util.Arrays;
public class ParamTransmit {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        change(arr);
        System.out.println("arr = " + Arrays.toString(arr));
    }
    private static void change(int[] arr) {
        arr[0] += 1;
    }

程序运行结果为:

在这里插入图片描述

数组变量也是引用类型。main方法在调用change方法时,传递的是数组arr的地址值。

change方法内,将数组中第一个元素的值自增1,此时数组中第一个元素的值变为2。

change方法结束,main方法继续执行,此时main栈帧变量arr仍指向该数组,只不过该数组中的元素已被修改。所以输出的是被修改后的数组。

内存结构示意图如下:

在这里插入图片描述

可以引申为多维数组及数组元素为引用类型的情况。

其他自定义类

直接上代码

class MyData {
    int a = 10;
}
public class ParamTransmit {
    public static void main(String[] args) {
        MyData md = new MyData();
        change(md);
        System.out.println("md.a = " + md.a);
    }
    private static void change(MyData md) {
        md.a += 1;
    }
}

程序运行结果为:

在这里插入图片描述

main方法调用change方法时,同样是将main栈帧中的变量md的值传递到change栈帧中,此时传递的是地址值,指向堆中同一个MyData实例。

然后执行md.a += 1,此时该实例的成员变量a为11。

change方法执行完毕,main方法未结束,main栈帧中的变量md仍指向该实例,此时输出的a为11。内存结构如下图。

在这里插入图片描述

若将上面代码修改如下,会是什么结果呢?

class MyData {
    int a = 10;
}
public class ParamTransmit {
    public static void main(String[] args) {
        MyData md = new MyData();
        change(md);
        System.out.println("md.a = " + md.a);
    }
    private static void change(MyData md) {
        md = new MyData();
        md.a += 1;
    }
}

程序运行结果为:

在这里插入图片描述

main方法调用change方法时,同样传递的是地址值。

然而执行md = new MyData();时,在堆中创建了一个新的实例。

此时change栈帧中的变量md的值变成了新的MyData实例的地址。

然后继续执行md.a += 1;,修改的是新的实例的成员变量。

当change方法调用结束后,main方法继续执行,此时main栈帧中md指向的仍是原来的实例,原实例的成员变量未被修改,仍是10。因此输出结果是10。

内存结构如下图。

在这里插入图片描述

总结

通过以上分析,我们可以知道java的参数传递机制是值传递。

若参数是基本数据类型,传递的是数据值;若参数是引用数据类型,传递的是地址值。

若参数是基本数据类型,对形参的操作不影响实参,因其是不同栈帧的不同变量。

若参数是引用数据类型,并且是String、包装类等。

因其对象的不可变性,对形参的操作会导致其指向新的String或包装类等对象,但不影响实参,实参仍指向原来的对象且该对象并未被修改。

若参数是引用数据类型,并且是数组、StringBuffer及其他自定义类等时,对形参的操作会影响到实参,因其指向的是同一个实例。

到此这篇关于Java方法的参数传递机制详解的文章就介绍到这了,更多相关Java方法的参数传递内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文