龙空技术网

天真,居然还有人认为java的参数传递方式是引用传递

一点用都没有的知识 2597

前言:

今天看官们对“java传入函数参数”大概比较讲究,各位老铁们都需要分析一些“java传入函数参数”的相关资讯。那么小编也在网上搜集了一些关于“java传入函数参数””的相关文章,希望各位老铁们能喜欢,咱们快快来了解一下吧!

代码写的越急,程序跑得越慢。—— Roy Carlson

时间过得真快,2020已经过去了一半,但是疫情好像还没有真正的消灭,人们出行还是得带着口罩,天气越来越热,受罪啊。

言归正传,都2020年了,居然还有人认为java的参数传递方式是引用传递,今天我就来讲一讲java的参数传递,好好看,写的不对的地方,请大声说出来,反正我也不会改,憋坏了就不好了。

基本数据类型传递

我们先来看一个普通的例子

package com.ymy.param;/** * @ProjectName: demo * @Package: com.ymy.param * @ClassName: BaseTypeTest * @Author: 流星007 * @Description: 基本数据类型传递 * csdn: * 今日头条: * @Date: 2020/7/5 12:52 * @Version: 1.0 */public class BaseTypeTest {    public static void main(String[] args) {        int a = 1;        dosomthing(a);        System.out.println("主函数a的值 = "+a);    }    private static void dosomthing(int a) {        a = a-1;        System.out.println("修改过后,a = "+a);    }}1234567891011121314151617181920212223242526272829

这是一个很简单的一个方法,在主函数main中对变量进行了初始化a=1,然后将a传递给dosomthing(),然后再dosomthing中输出了修改之后的值,最后在主函数中打印a的值,你们觉得这几句输出中a的值分别是多少呢?

第一种:修改过后,a = 0主函数a的值 = 1第二种:修改过后,a = 0主函数a的值 = 0第三种:修改过后,a = 1主函数a的值 = 1

想要得到答案的话就得先明白参数传递的两个类型:值传递和引用传递。

什么是引用传递?在C++中,函数参数的传递方式有引用传递。所谓引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

什么是值传递?值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。

我们再回过头来看上面的例子,如果是引用传递的话打印结果应该是第二种情况,如果是值传递,打印结果应该是第一种情况,所以到底打印的结果是什么呢?

我们一起看一看控制台输出

Connected to the target VM, address: '127.0.0.1:59333', transport: 'socket'修改过后,a = 0主函数a的值 = 1Disconnected from the target VM, address: '127.0.0.1:59333', transport: 'socket'Process finished with exit code 0123456

这就是第一种情况,很明显,在dosomthing函数中修改了a的值,但是主函数中的a并没有受到影响,所以肯定不会是引用传递,如果是引用传递,主函数的a应该会变成0,只有在参数传递的时候将主函数的中参数复制一份给dosomthing,才能在dosomthing中修改a不会对主函数造成影响,所以从基本数据类型来看,java的参数传递方式为:值传递。

这个时候你可能会有疑问了,这只是基本数据类型的传递方式,其他的参数类型呢?下面我们一起来看看引用类型和对象类型的传递方式。

follow me !!!!!

引用类型传递

我们都知道java中的String类型不属于基本数据类型,它是一个引用类型,也可以说是一个对象,那么它的传递方式是什么呢?

我们还是先来看例子

package com.ymy.param;/** * @ProjectName: demo * @Package: com.ymy.param * @ClassName: StringTypeTest * @Author: 流星007 * @Description: String类型传递 * csdn: * 今日头条: * @Date: 2020/7/5 14:22 * @Version: 1.0 */public class StringTypeTest {    public static void main(String[] args) {        String a = "hello";        dosomthing(a);        System.out.println("主函数a的值 = "+a);    }    private static void dosomthing(String a) {        a = a+" bug";        System.out.println("修改过后,a = "+a);    }}123456789101112131415161718192021222324252627282930

打印结果

修改过后,a = hello bug主函数a的值 = helloProcess finished with exit code 01234

我们发现主函数的a并没有受到dosomthing函数的影响,所以这并不是引用传递,这个时候你说是因为a = a+" bug";这行代码生成了新的对象,所以才会导致数据不一致,我们先来看看a的赋值情况吧

// class version 52.0 (52)// access flags 0x21public class com/ymy/param/StringTypeTest {  // compiled from: StringTypeTest.java  // access flags 0x1  public <init>()V   L0    LINENUMBER 14 L0    ALOAD 0    INVOKESPECIAL java/lang/Object.<init> ()V    RETURN   L1    LOCALVARIABLE this Lcom/ymy/param/StringTypeTest; L0 L1 0    MAXSTACK = 1    MAXLOCALS = 1  // access flags 0x9  public static main([Ljava/lang/String;)V    // parameter  args   L0    LINENUMBER 17 L0    LDC "hello"    ASTORE 1   L1    LINENUMBER 18 L1    ALOAD 1    INVOKESTATIC com/ymy/param/StringTypeTest.dosomthing (Ljava/lang/String;)V   L2    LINENUMBER 19 L2    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;    NEW java/lang/StringBuilder    DUP    INVOKESPECIAL java/lang/StringBuilder.<init> ()V    LDC "\u4e3b\u51fd\u6570a\u7684\u503c = "    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;    ALOAD 1    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V   L3    LINENUMBER 21 L3    RETURN   L4    LOCALVARIABLE args [Ljava/lang/String; L0 L4 0    LOCALVARIABLE a Ljava/lang/String; L1 L4 1    MAXSTACK = 3    MAXLOCALS = 2  // access flags 0xA  private static dosomthing(Ljava/lang/String;)V    // parameter  a   L0    LINENUMBER 24 L0    NEW java/lang/StringBuilder    DUP    INVOKESPECIAL java/lang/StringBuilder.<init> ()V    ALOAD 0    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;    LDC " bug"    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;    ASTORE 0   L1    LINENUMBER 25 L1    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;    NEW java/lang/StringBuilder    DUP    INVOKESPECIAL java/lang/StringBuilder.<init> ()V    LDC "\u4fee\u6539\u8fc7\u540e\uff0ca = "    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;    ALOAD 0    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V   L2    LINENUMBER 27 L2    RETURN   L3    LOCALVARIABLE a Ljava/lang/String; L0 L3 0    MAXSTACK = 3    MAXLOCALS = 1}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485

这是上面代码的字节码代码,我们可以清楚的看到a在赋值的时候都调用了StringBuilder的同String方法,现在我们来看看这个神奇的同String方法。

@Override    public String toString() {        // Create a copy, don't share the array        return new String(value, 0, count);    }12345

这是StringBuilder中的toString方法,确实是new了一个新的对象,就算是a变成了一个新的对象,如果是引用传递,主函数的a就不会受影响吗?这点我会讲完对象类型传递之后在进行讲解,请继续往下看。

对象类型传递

其实上面的两种其实很好区分,很多人都知道是值传递,很多人说java的传递方式是引用传递的原因就是出自这里:传递的参数为对象。

有些人看到对象传递的时候会改变主函数的值,就认为java的参数传递是引用传递,有些人又因为基本数据类型不会对主函数的值造成修改,所以他们的结论是:基本数据类型为值传递;对象类型为引用传递,想法很好,那我们现在一起来解开对象传递的神秘面纱,它到底是引用传递还是值传递呢?

go go go !!!!

还是老规矩,我们一起来看一个例子

package com.ymy.param.vo;/** * @ProjectName: demo * @Package: com.ymy.param.vo * @ClassName: LolVo * @Author: 流星007 * @Description: lol英雄属性 * csdn: * 今日头条: * @Date: 2020/7/5 15:11 * @Version: 1.0 */public class LolVo {    /**     * 姓名     */    private String name;    /**     * 职业     */    private String profession;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getProfession() {        return profession;    }    public void setProfession(String profession) {        this.profession = profession;    }    @Override    public String toString() {        return "LolVo{" +                "name='" + name + '\'' +                ", profession='" + profession + '\'' +                '}';    }}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051package com.ymy.param;import com.ymy.param.vo.LolVo;/** * @ProjectName: demo * @Package: com.ymy.param * @ClassName: ObjectTypeTest * @Author: 流星007 * @Description: 对象类型传递 * csdn: * 今日头条: * @Date: 2020/7/5 15:16 * @Version: 1.0 */public class ObjectTypeTest {    public static void main(String[] args) {        LolVo lolVo = new LolVo();        lolVo.setName("无极剑圣");        lolVo.setProfession("刺客");        dosomthing(lolVo);        System.out.println("主函数 lolVo = "+lolVo);    }    private static void dosomthing(LolVo lolVo) {        lolVo.setProfession("战士");        System.out.println("dosomthing lolVo = "+lolVo);    }}1234567891011121314151617181920212223242526272829303132333435

结果如下:

dosomthing lolVo = LolVo{name='无极剑圣', profession='战士'}主函数 lolVo = LolVo{name='无极剑圣', profession='战士'}Process finished with exit code 01234

主函数中剑圣的职业是刺客,在dosomthing中将他修改成战士,我们发现主函数中剑圣的职业也被修改成战士了,显然这符合引用传递的条件,被调用方修改会影响到调用方也就是主函数,所以这个时候很多人就认为java的对象传递的方式为引用传递,如果你也是这么认为,那么你就要认真看一下我后面的分析。

我们先来一个否定它是引用传递的例子,请看好,不要眨眼

package com.ymy.param;import com.ymy.param.vo.LolVo;/** * @ProjectName: demo * @Package: com.ymy.param * @ClassName: ObjectTypeTest * @Author: 流星007 * @Description: 对象类型传递 * csdn: * 今日头条: * @Date: 2020/7/5 15:16 * @Version: 1.0 */public class ObjectTypeTest {    public static void main(String[] args) {        LolVo lolVo = new LolVo();        lolVo.setName("无极剑圣");        lolVo.setProfession("刺客");        dosomthing(lolVo);        System.out.println("主函数 lolVo = "+lolVo);    }    private static void dosomthing(LolVo lolVo) {        lolVo = new LolVo();        lolVo.setProfession("战士");        System.out.println("dosomthing lolVo = "+lolVo);    }}123456789101112131415161718192021222324252627282930313233343536

做了小小的改动,仅仅只是在dosomthing方法中加了一行代码:lolVo = new LolVo();

我们再来看运行结果是什么呢?还会和上面一样吗?

dosomthing lolVo = LolVo{name='null', profession='战士'}主函数 lolVo = LolVo{name='无极剑圣', profession='刺客'}Process finished with exit code 01234

我们发现主函数中剑圣的属性变回了刺客,并没有受到dosomthing函数的影响,如果是引用传递的话,主函数中剑圣的职业应该是战士而不是刺客。这是为什么呢?为什么是应用传递主函数中剑圣的职业应该是战士呢?

下面我们一起来分析一波

我们假设对象的传递方式为引用传递

这是堆栈中的信息,当我们将对象lolVo传递给dosomthing的时候,是克隆了一个对象出来还是原来的那个对象呢?我们知道,不管传递的是不是它本身,值都是内存的地址引用,我们现在先假设主函数没有复制,是直接将lolVo传递给了dosomthing,那图形应该是什么样的呢?

如果是引用传递,格式是不是应该是这样呢?main主函数和dosomthing函数共用一个lolVo,这个时候我们在dosomthing函数中执行了一句:lolVo = new LolVo();那又会变成什么样呢?

在dosomthing方法中我们new了一个新的LolVo对象,并且将这个新的对象赋值给了lolVo,那是不是代表着main主函数核dosomthing函数中的lolVo应该是一样的呢,我们再来回顾一下上面的代码

private static void dosomthing(LolVo lolVo) {        lolVo = new LolVo();        lolVo.setProfession("战士");        System.out.println("dosomthing lolVo = "+lolVo);    }123456

我们做了修改之后,主函数剑圣的职业并没有修改成战士,所以,说java是引用传递是说不通的,那我们再来看看它正确的流程应该是什么样的呢?

尽管dosomthing对参数的修改会影响调用方,但是它还是属于值传递,会影响调用方是因为java转递的时候拷贝的是对象的引用地址。

举个栗子:比如某公司开发了一套员工的内部管理系统,有一个管理员的账号,你把这个账号给了你的同事,他直接使用你这个账号,这就是引用传递,如果你是在用户管理中添加了一条管理员的用户,再将这个账号给你的同事,这就是值传递,还需要解释一下,为什么值传递会影响调用方,如果你给的账号,你同事改了用户名称,这对你是不是没有影响,但如果他手抖把员工全删了,你这边还能看到员工信息吗?就是这个道理,lolVo引用地址相当于管理员账号,系统内的功能相当于LolVo对象,你修改自己的账号对别人当然没有影响,但是你把系统搞没了,你觉得有影响吗?这就是为什么java的参数传递方式为值传递却能影响调用方。

总结

如果还有人和你说java的参数传递是引用传递的话,请他来看一下我这篇博客,我把他劝退一下。

如果觉得小编讲的还不错,可以来一波关注哦。

标签: #java传入函数参数