Java中值传递,引用传递

前言

我们都知道,java中数据类型分为基本数据类型和引用数据类型。

  • 基本数据类型
    • 整型:byte,short,int,long
    • 浮点型:float,double
    • 字符型:char
    • 布尔型:boolean
  • 引用数据类型
    • 数组
    • 接口
  • 方法的参数分为实际参数和形式参数。
    • 形式参数:定义方法时括号内写的参数。
      如private void test(int a , byte[] b){……},此时的a和b均为形参。
    • 实际参数:调用方法时写的具体数值。
      int m=5;
      byte[] n={……};
      test(m , n);
      此处的m和n均为实参。

一般情况下,在数据做为参数传递的时候,基本数据类型是值传递,引用数据类型是引用传递(地址传递)。

值传递
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        int a = 2;
        int b = 15;
        test1(a, b);
        Log.d(TAG, "a = " + a);
        Log.d(TAG, "b = " + b);
    }

    private void test1(int m, int n) {
        m += 100;
        n += 100;
        Log.d(TAG, "m = " + m);
        Log.d(TAG, "n = " + n);
    }

    {//TODO:打印结果为:
        m = 102;
        n = 115;
        a = 2;
        b = 15;
    }
流程

1.主函数进栈,a、b初始化。
2.调用test1()方法,test1()进栈,将a和b的值,复制一份给m和n。
3.test1()方法中对m,n的值进行重新赋值运算。
4.test1()方法运行完毕,m,n的值已经被赋为最新值。
5.test1()方法弹栈。
6.主函数弹栈。

说明

在test1()方法中,m,n数值改变,并不会影响到a、b。因为, m , n中的值,只是从a、b的复制过来的。
也就是说,m、n相当于a、b的副本,副本的内容无论怎么修改,都不会影响到原件本身。

引用传递
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        //引用数据类型——类
        Cat c = new Cat();
        c.name = "小花"; //①
        test2(c);       //②
        Log.d(TAG, "c.name = " + c.name);//④

        //引用数据类型——数组
        int[] arr = {1, 2, 3};
        test3(arr);
        Log.d(TAG, " arr[0] = " + arr[0]);
    }

    private void test2(Cat cat) {
        cat.name = "小黑";//③
    }

    private void test3(int[] array) {
        if (0 == array.length) return;
        array[0] = 886;
    }

    private final class Cat {
        String name;
    }


    {//TODO:打印结果:

        c.name = 小黑;
        arr[0] = 886;
    }
流程:(以数组为例)

1.主函数进栈,int[] arr初始化。
2.调用test3()方法,test3( )进栈,将arr的地址值,复制一份给array。
3.test3()方法中,根据地址值,找到堆中的数组,并将第一个元素的值改为886。
4.test3()方法运行完毕,数组中第一个元素的值已经改变。
5.test3()方法弹栈。
6.主函数弹栈。

说明:

调用test3()的时候,形参array接收的是arr地址值的副本。并在test3()方法中,通过地址值,对数组进行操作。test3()方法弹栈以后,数组中的值已经改变。onCreate()方法中,打印出来的arr[0]也就从原来的1变成了886.

无论是onCreate()方法,还是test3()方法,操作的都是同一个地址值对应的数组。
就像你把自己家的钥匙给了另一个人,这个人拿着钥匙在你家一顿瞎折腾(增删改),然后走了。等你拿着钥匙回到家以后,家里已经变成了被折腾过后,惨不忍睹的样子。。
这里的钥匙就相当于地址值,家就相当于数组本身。
这里写图片描述

String类型传递
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

        //引用数据类型--String类
        String s1 = "AreYouOK?";
        test4(s1);
        Log.d(TAG, "s1 = " + s1);
    }

    private void test4(String str) {
        str = "NO";
    }


    {//TODO: 打印结果为

        s1 = AreYouOK ?;
    }

这么诡异??? 有点意思哦!!!
String是一个类,类是引用数据类型,做为参数传递的时候,应该是引用传递。但是从结果看起来却是值传递。

String的API中有这么一句话:“their values cannot be test4()d after they are created”
意思是:String的值在创建之后不能被更改。
API中还有一段:

String str = "abc";
//等效于:
char data[] = {'a', 'b', 'c'};
String str = new String(data);

也就是说:对String对象str的任何修改 等同于 重新创建一个对象,并将新的地址值赋值给str。

按照这个逻辑,上面代码可以这么写:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

        //引用数据类型--String类
        String s1 = "AreYouOK?";
        test4(s1);
        Log.d(TAG, "s1 = " + s1);
    }

    private void test4(String str) {
        char data[] = {'N', 'O'};
        String t = new String(data);
        str = t;
    }


    {//TODO: 打印结果为

        s1 = AreYouOK ?;
    }
流程:

1.主函数进栈,s1初始化。
2.调用test4()方法,test4( )进栈,将s1的地址值,复制一份给str。
3.test4()方法中,重现创建了一个String对象 ”NO”,并将str指向了新的地址值。
4.test4()方法运行完毕,str所指向的地址值已经改变。
5.test4()方法弹栈。
6.主函数弹栈。

说明:

String对象做为参数传递时,走的依然是引用传递,只不过String这个类比较特殊。
String对象一旦创建,内容不可更改。每一次内容的更改都是重现创建出来的新对象。
当test4()方法执行完毕时,str所指向的地址值已经改变。而str本来的地址值就是copy过来的副本,所以并不能改变s1的值。

String类似情况
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

        //引用数据类型--类
        Cat cc = new Cat();
        cc.name="Lily";   //1
        test5(cc);        //2
        Log.d(TAG, "cc.name = "+cc.name);//5
    }

    private void test5(Cat cat) {
        Cat t_cat = new Cat();
        t_cat.name = "Tom"; //3
        cat = t_cat;        //4
    }

    private final class Cat {
        String name;
    }


    {//TODO:打印结果:

        cc.name = Lily;
    }

这里写图片描述

综述:
  1. 值传递的时候,将实参的值,copy一份给形参。
  2. 引用传递的时候,将实参的地址值,copy一份给形参。

也就是说,不管是值传递还是引用传递,形参拿到的仅仅是实参的副本,而不是实参本身。

特别说明:

“在Java里面参数传递都是按值传递”这句话的意思是:按值传递是传递的值的拷贝,按引用传递其实传递的是引用的地址值,所以统称按值传递。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值