关于 Java 参数传递问题 ( 值传递与引用传递 )

1.基本类型和引用类型在内存中的保存

Java中数据类型分为两大类,基本类型和对象类型。相应的,变量也有两种类型:基本类型和引用类型。
基本类型的变量保存原始值,即它代表的值就是数值本身;
而引用类型的变量保存引用值,"引用值"指向内存空间的地址,代表了某个对象的引用,而不是对象本身,
对象本身存放在这个引用值所表示的地址的位置。

基本类型包括:byte,short,int,long,char,float,double,Boolean,returnAddress,
引用类型包括:类类型,接口类型和数组。

相应的,变量也有两种类型:基本类型和引用类型。

 

2.引用传递和值传递

这里要用实际参数和形式参数的概念来帮助理解,

值传递:

方法调用时,实际参数把它的值传递给对应的形式参数,函数接收的是原始值的一个copy,此时内存中存在两个相等的基本类型,即实际参数和形式参数,后面方法中的操作都是对形参这个值的修改,不影响实际参数的值。

引用传递:

也称为传地址。方法调用时,实际参数的引用(地址,而不是参数的值)被传递给方法中相对应的形式参数,函数接收的是原始值的内存地址;
在方法执行中,形参和实参内容相同,指向同一块内存地址,方法执行中对引用的操作将会影响到实际对象。

 

3. 例子展示

public class trans_args1 {
    public static void main(String[] args){
        trans_args1 trans=new trans_args1();
        //String类似基本类型,值传递,不会改变实际参数的值
        String str="Hello";
        trans.change(str);
        System.out.println(str);

        //StringBuffer和StringBuilder等是引用传递
        StringBuffer sb=new StringBuffer("Hello");
        trans.change(sb);

        System.out.println(sb.toString());
    }

    public void change(String str){
        str=str+"world";
    }

    public void change(StringBuffer sb){
        sb.append(" World");
    }
}

输出结果:

Hello
Hello World

从结果来看,sb的值被改变了,那么是不是可以说:对象作为参数传递时,是把对象的引用传递过去,如果引用在方法内被改变了,那么原对象也跟着改变。从上面例子的输出结果来看,这样解释是合理。

考虑下面的代码:

public class trans_args1 {
    public static void main(String[] args){
        trans_args1 trans=new trans_args1();

        //StringBuffer和StringBuilder等是引用传递
        StringBuffer sb1=new StringBuffer("Hello");
        trans.change(sb1);

        System.out.println(sb1.toString());
    }

    public void change(StringBuffer sb2){
        sb2 = new StringBuffer("Hello ");
        sb2.append(" World");
    }
}

在执行 sb2 = new StringBuffer(); 之前,sb1 和 sb2 指向内存中的同一个地址。  而执行完sb2 = new StringBuffer() 之后,sb2 将指向内存中的新的地址。 两者不再指向同一地址,所以,main 中的 sb1 的值肯定是没有改变的。

 

结尾

今天之所以查阅博客写了这几个例子,是因为今天碰到的一个坑。

import java.util.*;

public class Test1 {

    public static void main(String[] args){

        int[] src = new int[]{1,2,3,4,5,6,7,8,9,10};

        change(src);

        for(int item : src) {
            System.out.print(item + " ");
        }
    }

    public static void change(int[] src) {  // 改变数组的第一个值为100
        src[0] = 100;
    }
}
100 2 3 4 5 6 7 8 9 10 

这个输出结果应该是显然的。因为数组在传递参数时也是引用传递。

然而,今天碰到下面代码的使用情况:

public class Test1 {

    public static void main(String[] args){

        List<Integer> list = new ArrayList<>();

        int[] src = new int[]{1,2,3,4,5,6,7,8,9,10};

        grow(src);

        System.out.println(src.length);
    }

    public static void grow(int[] nums) {
        nums= Arrays.copyOf(nums, nums.length*2); // 将数组 nums 扩容两倍
    }
}

上述代码的输出: 10, 说明 main 中的 src 数组并没有扩容成功!

在一个题目中,有用到以下代码,扩容之后进行一系列操作,一直报错 越界,11

之后查阅博客,了解了参数传递的一些细节之后,才恍然大悟。

查看 Arrays.copyOf() 源码:

看到这个 new,原来在扩容的时候,new 了一个新的对象,使得传递过来的参数 (地址) 和 扩容之后的地址不一致!!。

所以,main 中的 src 数组并没有实际扩容!!

其次,如果你用的是 IDEA 的话,也可以得到一定的提示;

上面的提示显示 str 对象定义了但是没有使用,说明 change() 函数体内的 str 和 main 中的 str 并没有指向同一个对象。

参考博客:

https://www.cnblogs.com/binyue/p/3862276.html

https://www.cnblogs.com/hpyg/p/8005599.html

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值