精华内容
下载资源
问答
  • 主要介绍了JS是按值传递还是按引用传递(推荐)的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
  • 主要介绍了详解java的值传递、地址传递、引用传递的相关资料,需要的朋友可以参考下
  • 另一方面,值传递由于形参和实参内存地址不一样,有些功能并不能实现(swap()),因此,除了极为简单的函数会使用值传递外,大多数会采用引用传递。而对于指针传递,则在涉及数组的情况下使用较多,因为指针本身会给...
  • 在传统的观念里,都认为JavaScript函数传递的是引用传递(也称之为指针传递),也有人认为是值传递引用传递都具备。那么JS的参数传递到底是怎么回事呢?事实上以下的演示也完全可以用于Java 首先来一个比较简单的,...
  • 一个实例让你明白什么是值传递引用传递的!
  • 主要介绍了java到底是值传递还是引用传递的相关知识,本文通过几个例子给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
  • 下面小编就为大家带来一篇String类型传递是值传递,char[]类型传递是引用传递的实现。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看不
  • java 中没有引用传递,都是值传递的,可以通过传递副本修改对象的,副本交换,并不影响原引用
  • 主要介绍了 Java 值传递引用传递详解及实例代码的相关资料,需要的朋友可以参考下
  • Java:按值传递还是按引用传递详细解说
  • 参数是按值而不是按引用传递的说明 Java 应用程序有且仅有的一种参数传递机制,即按值传递。写它是为了揭穿普遍存在的一种神话,即认为 Java 应用程序按引用传递参数,以避免因依赖“按引用传递”这一行为而导致的...
  • Java 是值传递还是引用传递

    万次阅读 多人点赞 2019-03-20 02:40:16
    最近整理面试题,整理到值传递引用传递,到网上搜了一圈,争议很大。带着一脸蒙圈,线上线下查了好多资料。最终有所收获,所以分享给大家,希望能对你有所帮助。 首先说下我的感受,这个题目出的很好,但是在 ...

        最近整理面试题,整理到值传递、引用传递,到网上搜了一圈,争议很大。带着一脸蒙圈,线上线下查了好多资料。最终有所收获,所以分享给大家,希望能对你有所帮助。
        首先说下我的感受,这个题目出的很好,但是在 Java 中这个题目是有问题的(在下面我会解释)。并且,有很多结论是 Java 中只有 值传递。我认为这样说不够严谨。当然如果针对 Java 语言本身来讲,Java 中只有 值传递,没有引用传递,是正确的。但是如果针对 值传递,引用传递定义来说,Java 中还是有引用传递的。下面来分析:

    一、值传递、引用传递定义


        在深入分析问题之前,先让初问者简单明白一下什么是值传递,引用传递。我先用 Java 代码解释:

    public class StringBase {
    
        public static void main(String[] args) {
            int c = 66; //c 叫做实参
            String d = "hello"; //d 叫做实参
    
            StringBase stringBase = new StringBase();
            stringBase.test5(c, d); // 此处 c 与 d 叫做实参
    
            System.out.println("c的值是:" + c + " --- d的值是:" + d);
        }
        
        public void test5(int a, String b) { // a 与 b 叫做形参
            a = 55;
            b = "no";
        }
    }


    【运行结果】
    c的值是:66 --- d的值是:hello

    可以看出通过方法传递后,int 类型与 String 类型的原值并没有受到前面 test5 方法执行后的影响,还是输出了原值。这种形为通常被说成值传递。如果原值经过 test5 方法后被改变了,这种形为通常被描述为引用传递

    定义

        值传递:指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
        引用传递:是指在调用函数时将实际参数的地址直接传递到函数中(的形参),那么在函数中对参数所进行的修改,将影响到实际参数。
        引用传递:形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。(下面文章中 C++ 的定义,我觉得这样说更精简形象一些,所以放了两个定义,其实意思是一样的)

        以上,就是相关的定义,大家对这个定义几乎没有分歧,但是我建议大家,有必要去看看 C++ 中 值传递、引用传递的定义。因为在 C++ 中有三个定义:值传递、引用传递、指针传递,推荐一个地址: C++ 值传递、指针传递、引用传递详解

    //引用传递
    void change2(int &n) {
        cout << "引用传递--函数操作地址" << &n << endl;
        n++;
    }

        我们看上边 C++ 引用传递的代码,使用的 & 操作符。& 操作符在 C++ 中被定义为"引用",引用在 C++ 中的定义是“引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样”,再看引用其中的一个描述:“声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元”。因此这引用的概念在 Java 中根本不存在。Java 中哪有给变量起个别名的!!!
        因此说,这个题出的就有问题,在 Java 官方中我一直没有找到明确的证据说“Java 中 值传递、引用传递 的定义”我所看到的全是说 C++ 中关于值传递、引用传递的定义。但是,在 Java 中没有 C++ 里"引用"的概念。Java 里只有对象,new 关键字。这就很尴尬了,拿 C++ 中的定义,来解释 Java,我觉得这就是有问题的。问题就出在了引用传递!!!
        在 C++ 中关于引用传递的定义明确,代码解释清晰。在 C++ 中引用传递,传递的是一个别名,操作别名就跟操作原值一个样。
        然而在 Java 中,没有引用的概念,Java 中只要定义变量就会开辟一个存储单元。因此,对 Java 语言来说只有值传递,没有引用传递是正确的。
        虽然 Java 中没有引用(C++ 中 引用"&")。但是,引用传递的定义,在 Java 中还是有符合条件的。抛开语言中的特性。只针对:值传递、引用传递的定义我们来分析一下,Java 是属于值传递还是引用传递。
        要想知道 Java 是属于值传递还是引用传递,这就要从 Java 内存模型聊起了,我们来看基本数据类型与引用类型在内存中的存储方式。

    二、基本数据类型、引用类型

    1.基本数据类型、引用类型定义
        基本数据类:Java 中有八种基本数据类型“byte、short、int、long、float、double、char、boolean”
        引用类型:new 创建的实体类、对象、及数组
    2.基本数据类型、引用类型在内存中的存储方式
        基本数据类型:存放在栈内存中。用完就消失。
        引用类型:在栈内存中存放引用堆内存的地址,在堆内存中存储类、对象、数组等。当没用引用指向堆内存中的类、对象、数组时,由 GC回收机制不定期自动清理。
    3.基本类型、引用类型内存简单说明图


        好,看了基本的内存图,应该能明白 Java 是属于值传递还是引用传递。不明白,也没关系,下面会详细说明,先说引起争议的代码。

    三、在 Java 中 值传递 与 引用传递,产生模糊不清的代码

    public class TransmitTest {
    
        public static void main(String[] args) {
    
            String a = "hello"; //String 引用数据类型,调用 pass 方法后 b 的值没有改变,不是 hello
            int b = 1; //int 基本数据类型,调用 pass 方法后 a 的值没有改变,还是 1
    
            User user = new User(); //new Class 引用类型,调用 pass 方法后 name 与 age 的值改变了
            user.setName("main"); // 调用 pass 后,name 为 pass 了
            user.setAge(2); //调用 pass 后,age 为 4 了
    
            pass(user, a, b); //pass 方法调用
    
            System.out.println("main 方法 user 是:" + user.toString());
            System.out.println("main 方法 a 的值是:" + a + " --- b 的值是:" + b);
        }
    
        public static void pass(User user, String a, int b) {
    
            a = "你好";
            b = 3;
    
            user.setName("pass");
            user.setAge(4);
    
            System.out.println("pass 方法 user 是:" + user.toString());
            System.out.println("pass 方法 a 的值是:" + a + " --- b 的值是:" + b);
        }
    }
    
    class User {
    
        String name;
        int age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "name = " + name + " --- age = " + age;
        }
    }

    【运行结果】
    pass 方法 user 是:name = pass --- age = 4
    pass 方法 a 的值是:你好 --- b 的值是:3
    main 方法 user 是:name = pass --- age = 4
     

        main 方法 a 的值是:hello --- b 的值是:1    结果分析,int b,实参是 1,pass 方法调用后,值还是 1 没变,说明基本数据类型是值传递,大家对这个也几乎没争议。
    争议的在下边了:
            1.String a 是引用类型,pass 方法调用后,值还是 hello 没变。(结论:好像引用类型也是值传递啊!!!)
            2.new User() 也是引用类型,在方法调用后,值居然变了。原值不应该是 name = main,age = 2 的吗?为什么 name = pass,age = 4 了呢?(结论:引用类型好像是引用传递啊???)
        这就奇葩了,String 与 new 创建的类,同为引用类型,为什么产生的结果不一样呢?String 不也是一个类吗?User 不也是一个类吗?
        有人解释说这个代码比喻的不对,应该用如下代码比喻,在 pass 方法中添加一行代码,user = new User(),pass 方法修改如下:

    public static void pass(User user, String a, int b) {
    
        a = "你好";
        b = 3;
    
        user = new User();
        user.setName("pass");
        user.setAge(4);
    
        System.out.println("pass 方法 user 是:" + user.toString());
        System.out.println("pass 方法 a 的值是:" + a + " --- b 的值是:" + b);
    }


    【运行结果】
    pass 方法 user 是:name = pass --- age = 4
    pass 方法 a 的值是:你好 --- b 的值是:3
    main 方法 user 是:name = main --- age = 2
    main 方法 a 的值是:hello --- b 的值是:1

        这样一来,改变了形参的值,但是实参没有改变。因此有人得出结论,Java 中只有值传递,没有引用传递。(我并不这么认为,原因如下)
        使用 user = new User() 这个代码来做验证,我觉得是符合 String 类型做形参时的验证地,但是,此示例不符合引用传递的验证
        在验证之前,我们先看下使用 user=new User(); 语句之前与之后的内存模型图,能有助于我们更好的验证结果,同时也有助于更好的理解 Java 内存模型。我们看 TransmitTest 类在 Java 内存模型中的存储图:

    图1 pass() 方法中没有使用 user=new User() 语句的内存模型图


        在 图1 中,main() 方法中的 user 类,与 pass() 方法中的 user 类,指向的是同一个堆内存中的 User 类,红色虚线是在 main() 方法中初次给 name 属性赋的值"main"。实线部分,是在 pass() 方法中给 name 属性赋的值"pass"。因为在堆内存中只有一个 User 类实体,因此 main() 方法与 pass() 方法中的 user 指向的都是同一个 User 类 0x000031。因此,无论在 main() 方法还是 pass() 方法中,改变其 user 的属性值后,打印 User 类的属性值肯定是一样的,他们用的是一个实体类。

    图2 pass() 方法中使用了 user=new User() 语句的内存模型图


        在 图2 中,main() 方法中的 user 类首次加载,堆内存开辟了一个地址为 0x000031 的 User 类实体。当把 main() 方法中的实参 user 传递给 pass() 方法中形参 user 的时候,栈内存在 pass() 方法区中开辟了一个空间,并引用了地址为 0x000031 的 User 类。此时两个方法中的 User 类其实是一个。
        然而当 pass() 方法中的 user=new User()语句执行后,堆内存中新开辟了一个地址为 0x000032 的 User 类,pass() 方法中的 user 从此指向了地址为 0x000032 的 User 类。
        因为 pass() 方法 与 main() 方法中的 user 属性分别指向了不同的 User 类,所以两个方法中的 User 类的属性无论怎么修改,相互都不影响。
        但是,这种操作是不能验证引用传递定义的。因为实参传值给形参后,形参自己改变了地址,这就和引用传递无关了。我们再来用代码验证。
        我们可以使用 C++ 引用传递代码来验证,使用 user = new User() 语句验证引用传递的错误性
    C++ 中引用传递代码

    class User
    {
    public:
        int age;   // 长度
        string name;  // 宽度
    };
    
    
    //引用传递
    void pass(User &user) {
    
        cout << "引用传递 -- user的地址是:" << &user << endl;
    
        user.age = 2;
        user.name = "你好";
    }
    
    
    int main() {
    
        User user;
        user.age = 1;
        user.name = "hello";
    
        cout << "实参 -- user的地址是:" << &user << endl;
        pass(user);
        cout << "实参 -- user的值 age=" << user.age << ",name=" << user.name << endl;
    
        system("pause");
        return false;
    }


    【运行结果】
    实参 -- user的地址是:00DCF768
    引用传递 -- user的地址是:00DCF768
    实参 -- user的值 age=2,name=你好

        在 C++ 中,引用传递的实参与形参地址一致,在引用的方法中,使用的就是实参的地址。当修改形参值后,实参值也跟着变。现在我们按照 user=new User(); 的方法改变一下引用方法 pass 如下:

    //引用传递
    void pass(User &user) {
    
        cout << "引用传递 -- user的地址是:" << &user << endl;
    
        User user2 = user; //相当于 Java 中的 user=new User();
        cout << "引用传递 -- user2的地址是:" << &user2 << endl;
    
        user2.age = 2;
        user2.name = "你好";
    }


    【运行结果】
    实参 -- user的地址是:00CFFACC
    引用传递 -- user的地址是:00CFFACC
    引用传递 -- user2的地址是:00CFF9AC
    实参 -- user的值 age=1,name=hello

        我们看,改变引用传递中形参 user 的地址后(后期改变的地址,这跟引用传递,值传递还有什么关系?),再修改形参 user 的值,实参没有任何变化。这就破坏了引用传递的场景,因此不能使用 user=new User(); 语句来验证引用传递的定义。

        排除了其他异议,我们再来分析 Java 中有没有引用传递。

    先把引用传递的定义放上

        引用传递:是指在调用函数时将实际参数的地址直接传递到函数中(的形参),那么在函数中对参数所进行的修改,将影响到实际参数。
        引用传递:形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。

        经过上面的长篇大论,我想这时候你应该能明白了。在对引用类型做方法传递的时候,是不是先把实参的地址给形参的?之后对形参的操作是,是不是相当于操作实参?最后有没有影响到实际参数?
    答案肯定都是有的。

    定义关键1:是指在调用函数时将实际参数的地址直接传递到函数中(给形参了)
        证明:Java 在进行方法调用传递引用类型参数的时候,就是先给形参一个与实参相同的地址的(此处与 C++ 的不同之处是,C++ 是别名,没有在内存中给形参开辟空间,而 Java 给形参开辟了一个栈内存空间,存放与实参相同的引用地址。但是这与引用传递的定义不违背啊!!!定义可没说形参是否有开辟空间的概念)。

    定义关键2:在函数中对参数所进行的修改,将影响到实际参数。
        证明:Java 在进行方法调用传递引用类型参数后,修改形参的内容后,就是影响了实参的值。

    四、String 与包装类的特殊分析

        好了,解决了实例对象,我们再来说 String 与包装类,为什么 String 与包装类作为引用类型,却有值传递的功能,居然没有影响到实参!

    原因如下
        我们都知道。String 类型及其他七个包装类,是一群特殊群体。当使用 String a = "hello"; 语句时,相当于执行了 String a = new String("hello")。然而在 Java 中每一次 new 都是一次对象的创建。如果你创建的对象在堆中不存在,便会创建一个,如果是新创建的对象,那么地址都会变的,后期改变的地址,这跟引用传递,值传递还有什么关系?

        其实 String 型方法参数传值的过程,可以用以下代码来解释,我们先看 String 类型的还原:

    String a = "hello"; //a 相当于实参
    String a1 = a; //a1 就相当于形参
    a1 = "你好";
    System.out.println("a是:" + a + " --- a1是:" + a1);


    【运行结果】
    a是:hello --- a1是:你好

    逐步还原解释:
        String a = "hello"; 在 String 池中检查并创建一个常量:"hello",给 a 分配一个栈内存,在此存储常量 hello 的地址。
        String a1 = a; 给 a1 分配一个栈内存,在此存储常量 hello 的地址。相当于 a 把自己持有的地址,复制给了 a1。

    内存图如下


        a1 = "你好"; 等同于 a1 = new String("你好")。在 String 池中检查是否有 "你好" 的常量。如果有,将 a1 的地址指向 "你好" 的地址。如果 String 池中没有 "你好" 常量,在堆内存中创建 "你好" 常量,并将 a1 地址指向 "你好"。

    内存图如下


        总结如下:String 类型,在进行方法传参的时候,是先将实参地址,赋值给形参(形参在栈内存中确实新开辟了一个新的内存空间,用于存储地址)。但是当再次给 String 类型的形参赋值(与实参内容不一样的值时),形参地址变了,这就和引用传递无关了。我们可以用 C++ 代码中的引用传递,来验证 String 型的这一特殊情况,代码如下:

    //引用传递
    void pass(string &a, int &b) {
    
        cout << "引用传递 -- a的地址是:" << &a << " --- b的地址是:" << &b << endl;
        cout << "引用传递 -- a的值是:" << a << " --- b的值是:" << b << endl;
    
        string c = a; // 相当于 java 中的 new String
        int e = b;
    
        cout << "引用传递 -- c的地址是:" << &c << " --- e的地址是:" << &e << endl;
        cout << "引用传递 -- c的值是:" << c << " --- e的值是:" << e << endl;
    
        c = "你好"; //在引用传递中改变形参地址后做修改操作,不影响实参
        e = 2; //在引用传递中改变形参地址后做修改操作,不影响实参
    }
    
    int main() {
        string a = "hello";
        int b = 1;
    
        cout << "实参 -- a的地址是:" << &a << " --- b的地址是:" << &b << endl;
    
        pass(a, b);
    
        cout << "实参 -- a的值是:" << a << " --- b的值是:" << b << endl;
    
        system("pause");
        return false;
    }


    【运行结果】
    实参 -- a的地址是:00CFF9CC --- b的地址是:00CFF9C0
    引用传递 -- a的地址是:00CFF9CC --- b的地址是:00CFF9C0
    引用传递 -- a的值是:hello --- b的值是:1
    引用传递 -- c的地址是:00CFF8A0 --- e的地址是:00CFF894
    引用传递 -- c的值是:hello --- e的值是:1
    实参 -- a的值是:hello --- b的值是:1

        我们看,在 C++ 中的引用传递方法中,改变形参的地址后做修改操作,照样不影响实参的值,这就破坏了引用传递的本质,不能这样比喻。

        因此,String 与其他包装类,在做形参的时候,由于他们在赋不同于实参的值时,改变了形参的地址,因此使引用传递,看起来像值传递,其实本质还是引用传递。


    五、总结


    1.这个题目出的不严谨,但是很好(因为涉及了 Java 内存模型)
    2.就 Java 语言本身来说,只有值传递,没有引用传递。
    3.根据 值传递,引用传递的定义来说:
            Java 中的基本类型,属于值传递。
            Java 中的引用类型,属于引用传递。
            Java 中的 String 及包装类,属于特殊群体,作为形参时,由于每次赋值都相当于重新创建了对象,因此看起来像值传递,但是其特性已经破坏了,值传递、引用传递的定义。因此他们属于引用传递的定义,却表现为值传递。

    此题争议很大,我仅分享自己的理解,如有不同结论,欢迎指正,一起共勉!

    展开全文
  • 主要介绍了java通过实例了解值传递引用传递,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • Java中的参数传递,到底是值传递还是引用传递? 错误理解一:值传递引用传递,区分的条件是传递的内容,如果是个值,就是值传递。如果是个引用,就是引用传递。 错误理解二:Java是引用传递。 错误理解三:传递的...

    文章内容为转载,转载自公众号Hollis关于我要彻底给你讲清楚,Java就是值传递,不接受争辩的那种!

    Java中的参数传递,到底是值传递还是引用传递?

    结论:Java只有值传递,没有引用传递!

    错误理解一:值传递和引用传递,区分的条件是传递的内容,如果是个值,就是值传递。如果是个引用,就是引用传递。

    错误理解二:Java是引用传递。

    错误理解三:传递的参数如果是普通类型,那就是值传递,如果是对象,那就是引用传递。

    实参与形参

    我们都知道,在Java中定义方法的时候是可以定义参数的。比如ava中的main方法,public static void main(String[] args),这里面的args就是参数。参数在程序语言中分为形式参数和实际参数。

    形式参数:是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数。

    实际参数:在调用有参函数时,主调函数和被调函数之间有数据传递关系。在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数”。

    简单举个例子:


    实际参数是调用有参方法的时候真正传递的内容,而形式参数是用于接收实参内容的参数。

    基本类型与引用类型

    int num = 10;
    String str = "hello";
    


    如图所示,num 是基本类型,值就直接保存在变量中。而 str是引用类型,变量中保存的只是实际对象的地址。一般称这种变量为 “引用”,引用指向实际对象,实际对象中保存着内容。

    赋值运算符“=”的作用

    num = 20;
    str = "java";
    

    对于基本类型 num,赋值运算符会直接改变变量的值,原来的值被覆盖掉。

    对于引用类型 str,赋值运算符会改变引用中所保存的地址,原来的地址被覆盖掉。但是原来的对象不会被改变(重要)。

    如上图所示,“hello” 字符串对象没有被改变。(没有被任何引用所指向的对象是垃圾,会被垃圾回收器GC回收

    值传递与引用传递

    上面提到了,当我们调用一个有参函数的时候,会把实际参数传递给形式参数。但是,在程序语言中,这个传递过程中传递的两种情况,即值传递和引用传递。我们来看下程序语言中是如何定义和区分值传递和引用传递的

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

    引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

    • 值传递:将参数复制一份,修改形参不会对实参造成影响
    • 引用传递:将实参的地址传递给形参,修改形参也就是在修改实参

    我们来测试几段代码:

    上面的代码中,我们在 pass 方法中修改了参数 j 的值,然后分别在 pass 方法和 main 方法中打印参数的值。输出结果如下:

    print in pass , j is 20
    
    print in main , i is 10
    

    可见,pass 方法内部对 name 的值的修改并没有改变实际参数 i 的值。那么,按照上面的定义,有人得到结论:Java 的方法传递是值传递。

    但是,很快就有人提出质疑了(哈哈,所以,不要轻易下结论咯。)。然后,他们会给出以下代码:

    同样是一个 pass 方法,同样是在 pass 方法内修改参数的值。输出结果如下:

    print in pass , User{name='Tom', sex='man'}
    print in main , User{name='Tom', sex='man'}
    

    经过 pass 方法执行后,实参的值竟然被改变了,那按照上面的引用传递的定义,实际参数的值被改变了,这不就是引用传递了么。于是,根据上面的两段代码,有人得出一个新的结论:Java 的方法中,在传递普通类型的时候是值传递,在传递对象类型的时候是引用传递。

    但是,这种表述仍然是错误的。不信你看下面这个参数类型为引用类型的参数传递:

    print in pass , Tom
    print in main , Mr.Q
    

    那么,问题来了。String是引用类型,new String("Mr.Q")在堆上创建了对象,name指向了Mr.Q的引用。那按照上面来说,应该是引用传递了,输出的结果应该pass和main是相同的,可是,为什么会不同呢?

    这又作何解释呢?同样传递了一个对象,但是原始参数的值并没有被修改,难道传递对象又变成值传递了?

    其实,是传递的地址值发生了改变

    String类型在值传递和引用传递问题中比较特殊,为什么说特殊呢,因为对于一些常量字符串的创建,只要判断对象在堆中不存在,便会创建一个新的,如果是创建新对象,那么引用地址都会变。我们可以通过一个简单的例子来解释下:

    a是:hello --- b是:你好
    

    String a = “hello”; 在 String 池中检查并创建一个常量:“hello”,给 a 分配一个栈内存,在此存储常量 hello 的地址。

    String b= a; 给 b 分配一个栈内存,在此存储常量 hello 的地址。相当于 a 把自己持有的地址,复制给了 b。

    b = “你好”; 在 String 池中检查是否有 “你好” 的常量。

    • 如果有,将 b 的地址指向 “你好” 的地址。
    • 如果 String 池中没有 “你好” 常量,在堆内存中创建 “你好” 常量,并将 b 地址指向 “你好”。


    我们再来看一个反例,来验证 “Java中参数传递 没有引用传递”

    public class Test {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            Student s1 = new Student("小张");
            Student s2 = new Student("小李");
            Test.swap(s1, s2);
            System.out.println("s1:" + s1.getName());
            System.out.println("s2:" + s2.getName());
        }
    
        public static void swap(Student x, Student y) {
            Student temp = x;
            x = y;
            y = temp;
            System.out.println("x:" + x.getName());
            System.out.println("y:" + y.getName());
        }
    }
    

    结果:

    x:小李
    y:小张
    s1:小张
    s2:小李

    方法并没有改变存储在变量 s1 和 s2 中的对象引用。swap 方法的参数 x 和 y 被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝。


    Java中的值传递

    值传递和引用传递之前的区别到底是什么?

    两者的最主要区别就是是直接传递的,还是传递的是一个副本

    这里我们来举一个形象的例子。再来深入理解一下传值调用和传引用调用:


    • 你有一把钥匙,当你的朋友想要去你家的时候,如果你直接把你的钥匙给他了,这就是引用传递。
    • 这种情况下,如果他对这把钥匙做了什么事情,比如他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字。

    • 你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,自己的还在自己手里,这就是值传递。
    • 这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。

    那我们再说回到这段代码中:

    print in pass , User{name='Tom', sex='man'}
    print in main , User{name='Tom', sex='man'}
    

    看看在调用中,到底发生了什么?

    在参数传递的过程中,实际参数的地址0x666拷贝给了形参。这个过程其实就是值传递(这个值,理解为引用的地址),只不过传递的值得内容是对象的应用。

    那为什么我们改了user中的属性的值,却对原来的user产生了影响呢?

    其实,这个过程就好像是:你复制了一把你家里的钥匙给到你的朋友,他拿到钥匙以后,并没有在这把钥匙上做任何改动,而是通过钥匙打开了你家里的房门,进到屋里,把你家的电视给砸了。

    这个过程,对你手里的钥匙来说,是没有影响的,但是你的钥匙对应的房子里面的内容却是被人改动了。

    也就是说,Java对象的传递,是通过复制的方式把引用关系传递了,如果我们没有改引用关系,而是找到引用的地址,把里面的内容改了,是会对调用方有影响的,因为大家指向的是同一个共享对象。

    那么,如果我们改动一下pass方法的内容:

    上面的代码中,我们在pass方法中,重新new了一个user对象,并改变了他的值,输出结果如下:

    print in pass , User{name='Tom'}
    print in main , User{name='Mr.Q'}
    

    也就是说,我把我的钥匙复制给了我的朋友,但是我立马换了我家的锁。因为一new就会在堆上开辟新空间,地址就发生了改变,此时的user不再指向0x666了,理解为我换锁了,朋友当然进不了我家,砸不了电视了。所以此时在pass方法中修改name,不会对我家造成任何影响。

    上面这种传递是什么传递?肯定不是引用传递,如果是引用传递的话,在user = new User()的时候,实际参数的引用也应该改为指向0x999,但是实际上并没有。

    通过概念我们也能知道,这里是把实际参数的引用的地址复制了一份,传递给了形式参数。所以,上面的参数其实是值传递,把实参对象引用的地址当做值传递给了形式参数。


    所以,值传递和引用传递的区别并不是传递的内容,而是实参到底有没有被复制一份给形参

    在判断实参内容有没有受影响的时候,要看传的的是什么,如果你传递的是个地址,那么就看这个地址的变化会不会有影响,而不是看地址指向的对象的变化。

    所以说,Java 中其实还是值传递,只不过对于对象参数,值的内容是对象的引用。


    总结

    无论是值传递还是引用传递,其实都是一种求值策略 (Evaluation strategy)。在求值策略中,还有一种叫做按共享传递 (call by sharing)。其实 Java 中的参数传递严格意义上说应该是按共享传递。

    按共享传递,是指在调用函数时,传递给函数的是实参的地址的拷贝(如果实参在栈中,则直接拷贝该值)。

    在函数内部对参数进行操作时,需要先拷贝的地址寻找到具体的值,再进行操作。

    如果该值在栈中,那么因为是直接拷贝的值,所以函数内部对参数进行操作不会对外部变量产生影响。

    如果原来拷贝的是原值在堆中的地址,那么需要先根据该地址找到堆中对应的位置,再进行操作。因为传递的是地址的拷贝,所以函数内对值的操作对外部变量是可见的。

    简单点说,Java 中的传递,是值传递,而这个值,实际上是对象的引用。

    • 传递的值在栈中,直接拷贝一份值传递,改变的形参不会对实参造成影响
    • 传递的值在栈中存放的是地址(引用),先根据栈中的地址找到在堆上的值,然后把地址拷贝一份(拷贝的地址是一个值),此时形参和实参指向堆上同一个地址,形参的修改导致了实参的改变。

    Java中的参数传递,是值传递!

    Java只有值传递!

    Java只有值传递!

    Java只有值传递!

    展开全文
  • 函数间的参数传递包括值传递引用传递,详细示例接下来为大家详细介绍下,感兴趣的朋友不要错过
  • 1、关于值传递引用传递的定义 值传递:是指在调用函数时,将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,就不会影响到实际参数 如下图所示,当传递参数之前会将参数进行复制,函数中修改了...

    1、关于值传递

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

    如下图所示,当传递参数之前会将参数进行复制,函数中修改了参数,不会影响实际参数
    在这里插入图片描述
    值传递是对于是对基本数据而言,例如下面例子,number没有改变。

    public class Test01 {
        public static void main(String[] args) {
            int number = 10;
            new Test01().change(number);
            System.out.println(number);
        }
    
        public void change(int a){
            a+=1;
        }
    }
    

    在这里插入图片描述

    2、关于引用传递

    引用传递:是指在调用函数时,将实际参数的地址传递到函数中,那么在函数中对参数进行修改,将会影响到实际参数

    引用数据类型分为两个部分,引用变量和对象,这两个部分放在不同的地方,引用变量在栈中,而对象是放在堆内存中的,引用变量指向对象。
    如下图所示,当传递参数之前会将参数进行复制,但是复制的是引用变量,复制后的引用变量还是指向内存中的同一对象,所以引用传递中,函数修改了参数会影响实际参数
    在这里插入图片描述
    引用传递是对于引用数据类型而言,例如对于User类姓名的修改,会改变对象的name。

    public class Test01 {
        public static void main(String[] args) {
            User user = new User("小明", 12);
            new Test01().change(user);
            System.out.println(user);
        }
    
        public void change(User a){
            a.setName("小张");
        }
    }
    

    在这里插入图片描述


    注意点,引用类型中的String的值是放在常量池中,我们改变副本的值不会影响到原来的值。
    在这里插入图片描述

    例如在change放发中改变了副本的值,但是原来的str字符串不会改变。

    public class Test01 {
        public static void main(String[] args) {
            String str = "hello";
            new Test01().change(str);
            System.out.println(str);
        }
    
        public void change(String a){
            a="yes";
        }
    }
    

    在这里插入图片描述

    展开全文
  • java值传递引用传递1.数据类型1.1 基本数据类型1.2 引用数据类型2.形参与实参 观前提示: 本文所使用的IDEA版本为ultimate 2019.1,JDK版本为1.8.0_141。 1.数据类型 1.1 基本数据类型 基本数据类型包括数值型...

    观前提示:

    本文所使用的IDEA版本为ultimate 2019.1,JDK版本为1.8.0_141。

    1.数据类型

    1.1 基本数据类型

    基本数据类型包括数值型(整数型和浮点型),字符型和布尔型

    1. 整数型:byte,short,int,long
    2. 浮点型:float,double
    3. 字符型:char
    4. 布尔型:boolean

    以下程序代码展示了这些基本数据类型的位数、包装类、最大值、最小值。

    package testBasicDataType;
    
    public class Test {
    
        public static void main(String[] args) {
            //byte
            System.out.println("数据类型为 :" + Byte.TYPE);
            System.out.println("二进制位数 :" + Byte.SIZE);
            System.out.println("包装类为 :" + Byte.class);
            System.out.println("最大值为 :" + Byte.MAX_VALUE);
            System.out.println("最小值为 :" + Byte.MIN_VALUE);
            System.out.println("\n");
    
            //short
            System.out.println("数据类型为 :" + Short.TYPE);
            System.out.println("二进制位数 :" + Short.SIZE);
            System.out.println("包装类为 :" + Short.class);
            System.out.println("最大值为 :" + Short.MAX_VALUE);
            System.out.println("最小值为 :" + Short.MIN_VALUE);
            System.out.println("\n");
    
            //int
            System.out.println("数据类型为 :" + Integer.TYPE);
            System.out.println("二进制位数 :" + Integer.SIZE);
            System.out.println("包装类为 :" + Integer.class);
            System.out.println("最大值为 :" + Integer.MAX_VALUE);
            System.out.println("最小值为 :" + Integer.MIN_VALUE);
            System.out.println("\n");
    
            //long
            System.out.println("数据类型为 :" + Long.TYPE);
            System.out.println("二进制位数 :" + Long.SIZE);
            System.out.println("包装类为 :" + Long.class);
            System.out.println("最大值为 :" + Long.MAX_VALUE);
            System.out.println("最小值为 :" + Long.MIN_VALUE);
            System.out.println("\n");
    
            //float
            System.out.println("数据类型为 :" + Float.TYPE);
            System.out.println("二进制位数 :" + Float.SIZE);
            System.out.println("包装类为 :" + Float.class);
            System.out.println("最大值为 :" + Float.MAX_VALUE);
            System.out.println("最小值为 :" + Float.MIN_VALUE);
            System.out.println("\n");
    
            //Double
            System.out.println("数据类型为 :" + Double.TYPE);
            System.out.println("二进制位数 :" + Double.SIZE);
            System.out.println("包装类为 :" + Double.class);
            System.out.println("最大值为 :" + Double.MAX_VALUE);
            System.out.println("最小值为 :" + Double.MIN_VALUE);
            System.out.println("\n");
    
            //char
            System.out.println("数据类型为 :" + Character.TYPE);
            System.out.println("二进制位数 :" + Character.SIZE);
            System.out.println("包装类为 :" + Character.class);
            System.out.println("最大值为 :" + (int) Character.MAX_VALUE);
            System.out.println("最小值为 :" + (int) Character.MIN_VALUE);
            System.out.println("\n");
    
            //boolean
            System.out.println("数据类型为 :" + Boolean.TYPE);
            System.out.println("包装类为 :" + Boolean.class);
            System.out.println("真值为 :" + Boolean.TRUE);
            System.out.println("假值为 :" + Boolean.FALSE);
        }
    }
    

    运行结果如下
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    1.2 引用数据类型

    包括类、接口类型、数组类型、枚举类型、注解类型,字符串型

    2.形参与实参

    2.1 函数中使用

    1. 形参出现在函数定义中,在整个函数体内都可以使用, 离开该函数则不能使用。

    2. 实参出现在主调函数中,进入被调函数后,实参变量也不能使用。

    2.2 调用

    1. 形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量。

    2. 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值。

    3.值传递和引用传递

    3.1 定义

    1. 值传递
      在方法调用时,传递的参数是按值的拷贝传递。

    2. 引用传递
      在方法调用时,传递的参数是按引用进行传递,其实传递的引用的地址,也就是变量所对应的内存空间的地址。

    3.2 例子

    例1

    package testTransfer;
    
    public class Test {
        public static void main(String[] args) {
            int num = 10;
            num = 100;
            System.out.println(num);
    
            String str = "ab";
            str = "bd";
            System.out.println(str);
    
        }
    }
    

    运行结果如下
    在这里插入图片描述
    解析如下

    基本数据类型是直接赋值,引用类型是重新开辟了一块新地址,并将新地址赋值回来。
    在这里插入图片描述

    例2

    package testTransfer;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class Test {
        public static void main(String[] args) {
            int num = 10;
            System.out.println("num改变前:" + num);
            changeNum(10);
            System.out.println("num改变后:" + num);
    
            String str = "ab";
            System.out.println("str改变前:" + str);
            changeStr(str);
            System.out.println("str改变后:" + str);
    
            List<String> list = new ArrayList<>();
            list.add("a");
            list.add("b");
            System.out.println("list.get(1)改变前:" + list.get(1));
            changeList(list);
            System.out.println("list.get(1)改变后:" + list.get(1));
    
            String[] strings = new String[]{"a", "b"};
            System.out.println("strings[1]改变前:" + strings[1]);
            changeStrArray(strings);
            System.out.println("strings[1]改变后:" + strings[1]);
    
            User user = new User();
            user.name = "张三";
            user.age = 30;
            System.out.println("user.name改变前:" + user.name);
            System.out.println("user.age改变前:" + user.age);
            changeUserName(user);
            changeUserAge(user);
            System.out.println("user.name改变后:" + user.name);
            System.out.println("user.age改变后:" + user.age);
        }
    
        private static void changeNum(int num){
            num = 100;
        }
    
        private static void changeStr(String str){
            str = "abcd";
        }
    
        private static void changeList(List<String> list){
            list.set(1,"cd");
        }
    
        private static void changeStrArray(String[] strings){
            strings[1] = "cd";
        }
    
        private static void changeUserName(User user){
            user.name = "李四";
        }
    
        private static void changeUserAge(User user){
            user.age = 31;
        }
    }
    
    class User{
        String name;
        int age;
    }
    

    运行结果如下
    在这里插入图片描述
    解析如下

    1. 基本数据类型形参所传递的值,是这个实际值的拷贝,所以无论对这个拷贝赋其他任何值,都不会改变原值。

    2. 引用类型(除String外),传递的都是存储值的引用地址的拷贝(也就是传递前和传递后的引用相同),改变这个拷贝的引用地址指向的值,会改变原来值的内容。

    3.3 结论

    1. 基本数据类型的操作都是值传递。由于原始内容和副本都是存储实际值,并且是在不同的栈区,因此形参的操作,不影响原始内容。

    2. 引用类型的数据进行操作,基本上(String除外)都是“引用传递”(对引用的地址的传递)。 分两种情况,一种是形参和实参保持指向同一个对象地址,则形参的操作,会影响实参指向的对象的内容。一种是形参被改动指向新的对象地址(如重新赋值引用),则形参的操作,不会影响实参指向的对象的内容。

    展开全文
  • C++按值传递(产生临时对象) 按引用传递(不产生临时对象不占任何内存).rar
  • 值传递引用传递

    2020-12-23 01:08:15
    1.值传递:只要是基本数据类型传递就是值传递,传递的就是值 package arrayDemo; public class Demo1 { public static void main(String[] args) { int b =20; change(b);// 实参 实际上的参数 System.out.println...
  • C++与Java中的值传递和引用传递比较 开头先阐明一下我个人理解的值传递与引用传递。值传递,从字面上来说就是传递一个变量的值,其内在机制其实是变量当中所保存...其内在机制就是形参作为一种引用值与实参共享...
  • 学过Java基础的人都知道:值传递引用传递是初次接触Java时的一个难点,有时候记得了语法却记不得怎么实际运用,有时候会的了运用却解释不出原理,而且坊间讨论的话题又是充满争议:有的论坛帖子说Java只有值传递,...
  • 1:按值传递是什么 指的是在方法调用时,传递的参数是按值的拷贝传递。示例如下: [java] view plaincopy public class TempTest { private void test1(int a){ //做点事情 } public static void main(String[] args...
  • 主要介绍了探讨Java中函数是值传递还是引用传递问题,非常不错,具有参考借鉴价值,需要的朋友可以参考下
  • 值传递 VS. 按引用传递 按值传递(call by value)是最常用的求值策略:函数的形参是被调用时所传实参的副本。修改形参的值并不会影响实参。   按引用传递(call by reference)时,函数的形参接收实参的隐式引用,...
  • 当一个变量为一个参数传入方法内部的时候,会有两种不同的传递方式:值传递引用传递。  值传递的特点:不管方法内部对传进去的参数作任何改变,也不会影响方法外部的变量的值  引用传递的特点:方法内部对传...
  • 一、传递参数既可以通过值也可以通过引用传递参数。通过引用传递参数允许函数成员(方法、属性、索引器、运算符和构造函数)更改参数的值,...1. 通过值传递值类型: 代码如下:class PassingValByVal{static void Squar
  • 关于这个问题,在StackOverflow上也引发过广泛的讨论,看来很多程序员对于这个问题的理解都不尽相同,甚至很多人理解...错误理解一:值传递引用传递,区分的条件是传递的内容,如果是个值,就是值传递。如果是个引...
  • String是值传递还是引用传递 今天上班时,同事发现了一个比较有意思的问题。他把一个String类型的参数传入方法,并在方法内改变了引用的值。 然后他在方法外使用这个值,发现这个String还是之前的值,并没有改变。 ...
  • 《Java面试题系列》:一个长知识又很...本篇文章就带大家探究一下底层的原理,最终化繁为简,让大家通过一两句话就明白到底是值传递还是引用传递。 面试题 常见面试题,执行以下代码,查看对应的打印结果: @Test pub.

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,917,012
精华内容 766,804
关键字:

引用传递