精华内容
参与话题
问答
  • java中的参数传递(只有值传递没有引用传递)

    万次阅读 多人点赞 2019-07-31 19:25:14
    Java中只有传值调用(值传递),没有传址调用(址传递或者引用传递)。所以在java方法中改变参数的值是不会改变原变量的值的,但为什么改变引用变量的属性值却可以呢?请看下面的解答。 java中的数据类型 Java中...

    Java中只有传值调用(值传递),没有传址调用(址传递或者引用传递)。所以在java方法中改变参数的值是不会改变原变量的值的,但为什么改变引用变量的属性值却可以呢?请看下面的解答。

    java中的数据类型

    Java中数据类型分为两大类:基本类型和引用类型。相应的,变量也分这两种类型:基本类型和引用类型。

    基本类型的变量保存原始值,即它代表的值就是数值本身;

    而引用类型的变量保存的值是引用值,"引用值"指向内存空间的地址,代表了某个对象的引用,而不是对象本身,对象本身存放在这个引用值所表示的地址的位置。

    基本类型包括:byte,short,int,long,char,float,double,Boolean,returnAddress。

    引用类型包括:类、接口类型和数组。

    java中只有值传递

    在日常编码中,会经常看到如下现象:

    1、对于基本类型参数,在方法体内对参数进行重新赋值,不会改变原有变量的值。

    2、对于引用类型参数,在方法体内对参数进行重新赋予引用,不会改变原有变量所持有的引用。

    3、方法体内对参数进行运算,不会改变原有变量的值。

    4、对于引用类型参数,方法体内对参数所指向对象的属性进行操作,将改变原有变量所指向对象的属性值。

    举个例子:

    public class Main {
    
        private static void getMiddleOne(boolean b, Boolean boo, Boolean[] arr){
    
            b = true;
    
            boo = new Boolean(true);
    
            arr[0] = true;
    
        }
    
           //测试
    
        public static void main(String[] args) {
    
            boolean b = false;
    
            Boolean boo = new Boolean(false);
    
            Boolean[] arr = new Boolean[]{false};
    
            getMiddleOne(b, boo, arr);
    
            System.out.println(b);
    
            System.out.println(boo.toString());
    
            System.out.println(arr[0]);
    
            /**
    
            * output:
    
            * false
    
            * false
    
            * true
    
            */
    
        }
    
    }

    我们只要了解了下面两点就可以解答上面的现象了:

    1、基本数据类型的值就是数值本身,所以示例中的b的值就是false;包装类因为会自动装箱拆箱,所以可以和基本类型一样处理,所以示例中boo的值就是false;数组是引用类型,所以arr的值就是指向该Boolean[]的引用。

    2、java中只有值传递没有引用传递,所以传入getMiddleOne方法的三个参数分别是b的值拷贝, boo的值拷贝, arr的值拷贝。

    通过上面两点就可以清楚了,getMiddleOne方法中执行的 b=true 和 boo = new Boolean(true) 都是把新值赋给了他们的拷贝,所以不改变原变量的值;同样,arr[0] = true 是把true复制给了arr的拷贝所指向的数组的第一个元素,arr的值和arr的拷贝的值都是该数组的引用,所以arr的拷贝所指向的数组和arr所指向的数组是同一个,所以改变arr的拷贝的数组的元素会同样影响到原变量arr。

    总结

    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到底是值传递还是引用传递?

    千次阅读 多人点赞 2019-12-11 08:43:27
    在逛 Stack Overflow 的时候,发现了一些访问量像阿尔卑斯山一样高的问题,比如说这个:Java 到底是值传递还是引用传递?访问量足足有 188万+,这不得了啊!说明有很多很多的程序员被这个问题困扰过。实话实说吧,我...

    在逛 Stack Overflow 的时候,发现了一些访问量像阿尔卑斯山一样高的问题,比如说这个:Java 到底是值传递还是引用传递?访问量足足有 188万+,这不得了啊!说明有很多很多的程序员被这个问题困扰过。实话实说吧,就是其中之一。

    来回顾一下提问者的问题:

    我一直认为 Java 是按引用传递的,但是我看一些博客上说不是的。我就纳闷了,Java 到底是值传递还是引用传递?值传递和引用传递有什么区别呢?

    如果你也曾被这个问题困扰过,或者正在被困扰,就请随我一起来梳理一下问题的答案。打怪进阶喽!

    01、值传递和引用传递

    什么是值传递,什么是引用传递?我们需要先把这两个定义搞清楚,才能搞清楚 Java 是按值传递还是按引用传递。

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

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

    上面是比较官方的定义,读起来不免生硬。在我看来,值传递和引用传递的关键区别有两点:

    1)调用方法时有没有对实参进行复制。

    2)方法内对形参的修改会不会影响到实参。

    what?值传递和引用传递还没有搞清楚,又来两个新名词:实参和形参。别急,别急。

    02、实参和形参

    实参和形参理解起来比值传递和引用传递容易的多,前者就好像是一元一次方程,后者就像是一元二次方程。

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

    实参:在调用有参方法时传入的参数,方法名后面的括号中的参数通常被称为“实参”。

    大家应该都写过“hello world”程序了,就像下面这样。

    public class Cmower {
        public static void main(String[] args) {
            System.out.println("hello world");
        }
    }
    

    其中 args 就相当于是形参,而字符串“hello world”就相当于是实参。如果觉得这个例子不容易理解,那再来看一个。

    public class Cmower {
        public static void main(String[] args) {
            Cmower cmower = new Cmower();
            cmower.sop("沉默王二");
        }
    
        public void sop(String name) {
            System.out.println("hello " + name);
        }
    }
    

    其中“沉默王二”为实参;有参方法 sop(String name) 中的 name 为形参。形参就好像实参与被调用方法之间的一个桥梁,否则调用者没法传递参数,被调用的方法无法接收参数。

    03、基本类型是值传递的

    Java 中的数据类型可以分为两种,一种是基本类型,一种是引用类型。我相信大家在看本篇文章之前,就能够达成这样一个共识:基本类型是值传递的。这一点毫无疑问。

    public class Cmower {
        public static void main(String[] args) {
            Cmower cmower = new Cmower();
            int age = 18;
            cmower.sop(age);
            System.out.println("main 中的 age " + age);
        }
    
        public void sop(int age) {
            age = 28;
            System.out.println("sop 中的 age " + age);
        }
    }
    

    上面这段代码中,sop() 方法的实参 age 为 18,尽管 sop() 方法的形参被修改为 28,但并不会影响实参的值。这一点可以从输出结果中加以证明。

    sop 中的 age 28
    main 中的 age 18
    

    具体的执行过程如下图所示。

    04、引用类型是值传递吗?

    大家之所以不确定 Java 是值传递的还是引用传递的,原因就出在这个引用类型上面。单从字面的意思上就容易搞混:引用类型不是引用传递难道还是值传递?

    public class Cmower {
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public static void main(String[] args) {
            Cmower cmower = new Cmower();
            cmower.setName("沉默王二");
            cmower.sop(cmower);
            System.out.println("main 中的 cmower " + cmower.getName());
        }
    
        public void sop(Cmower cmower) {
            cmower.setName("沉默王三");
            System.out.println("sop 中的 cmower " + cmower.getName());
        }
    }
    

    main() 方法中,我们通过 new 关键字创建了一个对象 cmower,并将其 name 属性设置为“沉默王二”;然后将实参 cmower 传递给 sop() 方法,在 sop() 方法中将形参 cmower 的 name 属性修改为“沉默王三”。输出结果是什么样子呢?

    sop 中的 cmower 沉默王三
    main 中的 cmower 沉默王三
    

    呀!实参 cmower 的属性 name 竟然不是“沉默王二”而是“沉默王三”了!看看,看看,Java 不是值传递吧?

    别急别急。我们在 main 方法中追加几行代码。

    Cmower cmower = new Cmower();
    cmower.setName("沉默王二");
    
    Cmower old = cmower;
    cmower.sop(cmower);
    System.out.println("main 中的 cmower " + cmower.getName());
    
    System.out.println(old == cmower);
    

    old == cmower 会是 true 还是 false 呢?闭上眼睛想一想。如果实在是想不出,抛一枚硬币吧,反正不是 true 就是 false。假如引用类型是引用传递的,根据引用传递的定义(形参的修改将会影响到实参),那么结果一定就是 false。

    我们来看一下输出结果:

    sop 中的 cmower 沉默王三
    main 中的 cmower 沉默王三
    true
    

    true?开什么玩笑?

    不好意思,没有开玩笑。Java 的确是值传递的。只不过,引用类型在调用有参方法的时候,传递的是对象的引用,并不是对象本身。而对象的引用在传递的过程中并没有发生改变,虽然对象本身发生了变化。可以通过下面这幅图感受一下。

    这下理解了吧?

    05、总结

    来看下面这段代码。

    int age = 18;
    String name = "沉默王二";
    

    age 是基本类型,所以值就直接保存在变量中;而 name 是引用类型,变量中保存的只是对象的内存地址,这种变量一般称之为对象的引用。

    基本类型作为参数被传递时肯定是值传递;引用类型作为参数被传递时也是值传递,只不过“值”为对应的引用。


    好了各位读者朋友们,以上就是本文的全部内容了。能看到这里的都是最优秀的程序员,升职加薪就是你了👍。如果觉得不过瘾,我把本系列文章做了汇总,这里推荐给大家。

    五分钟学Java:打印数组最优雅的方式是什么?

    五分钟学Java:为什么不应该使用Java的原始类型?

    五分钟学Java:为什么会发生ArrayIndexOutOfBoundsException?

    五分钟学Java:Java 到底是值传递还是引用传递?

    五分钟学Java:如何比较 Java 的字符串?

    五分钟学Java:什么是 NullPointerException?

    谢谢大家的阅读,原创不易,喜欢就点个赞,这将是我最强的写作动力

    展开全文
  • 值传递和引用传递的区别

    万次阅读 热门讨论 2019-01-08 14:41:42
    值传递: 只要是基本类型传递 都是值传递 引用传递:针对于基本类型进行封装,对封装进行传递,是引用传递 代码:值传递(int类型) public class Test { public static void main(String[] args) { int int1 = ...

    值传递: 只要是基本类型传递 都是值传递

    引用传递:针对于基本类型进行封装,对封装进行传递,是引用传递

    代码:值传递(int类型)

     public class Test {
        public static void main(String[] args) {
    
            int int1 = 10;
    
            int int2 = int1;
            System.out.println("int1==" + int1);
            System.out.println("int2==" + int2);
    
            int2 = 20;
            System.out.println("改变之后");
            System.out.println("int1==" + int1);
            System.out.println("int2==" + int2);
    	}
    }
    

    运行代码:得到的当前的结果如下

    int1==10
    int2==10
    改变之后
    int1==10
    int2==20
    
    Process finished with exit code 0
    

    根据结果会发现int类型的传递,当int2值改变的时候,int1的值是没有任何变化的,所以基本类型都是根据它的值去传递的,传递之后,int2的值得改变和int1的值已经没有任何关系了

    String类型的传递

    代码:

     	    String str1 = "str1";
    
            String str2 = str1;
            System.out.println("str1==" + str1);
            System.out.println("str2==" + str2);
    
            str2 = "str2";
            System.out.println("改变之后");
            System.out.println("str1==" + str1);
            System.out.println("str2==" + str2);
    

    结果:

    str1==str1
    str2==str1
    改变之后
    str1==str1
    str2==str2
    
    Process finished with exit code 0
    

    结果证明String的结果 和int的结果是一样的,但是表达的方式不一样, String的表达方式和Integer是一样的,都是重新new的方式去传递值的。
    String str2 = str1; 这句话可以理解为 String str2 = new String(str1); 把str1当做参数传递过去,重新new了一个新的String,所以str2的值和str1的值 虽然是一样的,但是str2 和str1却不是同一个String

    类传递(引用传递)

    代码: 创建了一个TestDemo的类 ,里面有一个String类型的变量 txtProtected

     	 	TestDemo testDemo1 = new TestDemo();
            testDemo1.setTxtProtected("potected");
    
            TestDemo testDemo2 = testDemo1;
    
            TestDemo testDemo3 = new TestDemo();
            testDemo3.setTxtProtected(testDemo1.getTxtProtected());
            System.out.println("testDemo1==" + testDemo1.getTxtProtected());
            System.out.println("testDemo2==" + testDemo2.getTxtProtected());
            System.out.println("testDemo3==" + testDemo3.getTxtProtected());
    
            testDemo2.setTxtProtected("testDemo2");
            System.out.println("改变之后");
            System.out.println("testDemo1==" + testDemo1.getTxtProtected());
            System.out.println("testDemo2==" + testDemo2.getTxtProtected());
            System.out.println("testDemo3==" + testDemo3.getTxtProtected());
    

    运行代码的结果:

    testDemo1==potected
    testDemo2==potected
    testDemo3==potected
    改变之后
    testDemo1==testDemo2
    testDemo2==testDemo2
    testDemo3==potected
    
    Process finished with exit code 0
    

    从结果可看出 test2 和test1 是一样的结果,但是test3和 test1(test2)的结果并不是一样的
    那么test2这个类的指针所指向的地方 是和test1 所指向的地方 是一个 ,所以test2的 txtProtected 会导致 test1
    的txtProtected 的值发生改变。
    test3和 test1的值却是不一样的,其实test3的方式 和上面String的方式 是一样的,都是通过new的方式创建出来的,所以就不叙述了。

    展开全文
  • 若说值传递。基本类型是copy的值赋给形参,对象是把 堆中对象的 地址 传给 形参。 若说分为值传递和引用传递。。引用传递 是因为这个传的地址,导致实参和形参 操作的都是同一块内存。 有点乱,像文字层次上的撕...
  • 值传递和引用传递

    千次阅读 2018-09-20 21:35:45
    当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并 可返回变化后的结果,那么这里到底是值传递还是引用传递? 解答:值传递值传递和引用传递的区别: 值传递对象发送改变 不会对引用的内存...
  • 【JAVA】 什么是值传递和引用传递?

    万次阅读 多人点赞 2019-06-04 11:10:05
    值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量. 引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。 一般认为,java内的基础类型数据传递都是...
  • JS参数传递(值传递和引用传递)

    万次阅读 多人点赞 2018-06-07 15:11:01
    前端红宝书第一遍看过去之后,相当多的东西都忘记了,第二遍看的时候,也开始注意到一些细节的东西以及理解不到位的地方。...访问变量有按值和按引用两种方式,而参数只能按值传递。一、基本数据类型: Numbe...
  • 还有的人可能知道Java中的参数传递是值传递,但是说不出来为什么。 在开始深入讲解之前,有必要纠正一下大家以前的那些错误看法了。如果你有以下想法,那么你有必要好好阅读本文。 错误理解一:值传递和引用传递,...
  • C语言函数传递方式 值传递 定义 所谓的值传递,顾名思义就是使用变量、常量、数组等作为函数的参数。 地址传递
  • Java值传递和引用传递详细说明

    千次阅读 2020-07-14 15:53:17
    学过Java基础的人都知道:值传递和引用传递是初次接触Java时的一个难点,有时候记得了语法却记不得怎么实际运用,有时候会的了运用却解释不出原理,而且坊间讨论的话题又是充满争议:有的论坛帖子说Java只有值传递,...
  • 值传递与地址传递

    2016-07-11 21:29:40
    值传递与地址传递的区别:两者其实传递的都是一个内存单元的内容。不同的是,值传递传递的内容是一个变量的值,得到这个值后,对这个值的修改不能改变原变量的值;而地址传递传递的是一个变量的地址,得到传递的地址...
  • String是值传递还是引用传递

    千次阅读 2018-10-18 15:32:57
    Java参数传递分为值传递和引用传递,基本类型是值传递,封装的对象时引用传递。  先看一下值传递,引用传递和String参数传递的Demo: 值传递: /** * 值传递 * @author Administrator * */ public class ...
  • 而关于值传递,指针传递,引用传递这几个方面还会存在误区, 所有我觉的有必要在这里也说明一下~下文会通过例子详细说明哦值传递:形参是实参的拷贝,改变形参的值并不会影响外部实参的值。从被调用函数的角度来说,...
  • Python值传递还是引用传递

    万次阅读 多人点赞 2017-11-02 17:38:55
    Python值传递还是引用传递Python作为一门动态语言,变量本身的类型是不固定的,因此更加灵活。那Python到底是值传递还是引用传递呢?问题引出本人在本周写代码时,遇到这么一个让我吃惊的问题,问题可以抽象如下:def...
  • 将handler中解析出的值传递到同一activity种的oncreate方法中 public final Handler news_detailHandler = new Handler() {。。。。。。。。 url = port.getString("url");这个值传递到oncreate中} protected void ...
  • 探究Java方法的参数传递是值传递还是引用传递
  • java方法参数传递方式--按值传递、引用传递

    万次阅读 多人点赞 2017-12-25 21:33:28
    java的方法参数传递方式有两种,按值传递和引用传递 1.按值传递
  • 值传递和引用传递(地址传递)的区别

    千次阅读 2019-08-07 10:38:05
    值传递和引用传递(地址传递)的区别 也许大家对于被值传递和引用传递(看了些别人的博客也有人说只有值传递)弄得傻傻分不清楚,我个人之前也是这样过来的,自己也思考了一番,后来才弄清楚了,结合大多数人的想法...
  • JavaScript的值传递和引用传递

    千次阅读 2017-08-09 14:13:42
    JavaScript 值传递 引用传递
  • 参看:彻底高清值传递与地址传递的区别参看:值传递,引用传递与地址传递一、值传递当函数被调用的时候,形参被创建,调用时带的参数被拷贝到刚创建好的形参,函数结束时,形参被摧毁。由于是参数的一个副本被传递到...
  • java String 值传递 还是引用传递

    万次阅读 2017-07-28 10:56:44
    值传递与引用传递 最近学基础的时候,老师讲了值传递和引用传递,这个问题一直不太明白,上网查了很多资料,按照自己的理解整理了一遍,发现之前不太明白的地方基本上想明白了,如有不正确的地方,欢迎指正,谢谢...
  • Java中的值传递和地址传递

    万次阅读 2018-07-18 21:05:28
    被java中的“值传递”和“引用传递”困扰过一阵子,在实际代码中也犯过不少错,记录一下,方便查看。 1.问题 首先看看代码: public class Test { public static void main(String[] args) { String str = ...
  • 指针的值传递

    千次阅读 2019-10-19 14:16:21
    指针的值传递和变量的值传递一样,当参数是指针的时候,在函数内部只是对指针修改,那是没有意义的。 下面看一个例子: void GetInt(int *pInt) { pInt = new int; } void FreeInt(int *pInt) { if(pInt) ...
  • Java值传递和引用传递

    千次阅读 2017-11-10 10:57:10
    Java中传递的方式有两种,第一种是值传递,第二种是引用传递
  • JS引用传递和值传递

    2016-12-28 18:46:17
    本博客详细解释JS值传递和引用传递以及二者的区别。 函数调用中,传递是一个数值,我们称为 “值传递”。 函数调用中,传递是对象,一般称为 “引用传递”。
  • Python参数传递 - 引用传递&值传递

    万次阅读 2016-05-25 09:51:42
    Python值传递和引用传递区别,哪些类型值传递,哪些是引用传递?值传递:方法调用时,实际参数把它的值传递给对应的形式参数,方法执行中形式参数值的改变不影响实际参数的值。>>> a1 = 520 >>> a2 = a1 >>> print ...

空空如也

1 2 3 4 5 ... 20
收藏数 64,566
精华内容 25,826
关键字:

值传递