zoukankan      html  css  js  c++  java
  • 值传递和引用传递

    此篇博客只是copy下列博客或文章整理出来的,谢绝转载,侵权删除:

    https://www.cnblogs.com/dolphin0520/p/3592498.html

    https://zwmf.iteye.com/blog/1738574

    https://www.cnblogs.com/aspirant/p/10320652.html

    https://www.zhihu.com/question/31203609


    概念

    • 按值调用:一个方法接收的是调用着提供的值(值传递)
    • 按引用调用:一个方法接收的是调用者提供的变量地址(如果是C语言的话来说就是指针啦,当然java并没有指针的概念)。

    如果想搞明白值传递和引用传递需要搞明白对象对象引用

    一、对象对象引用

    对象

    首先我们应该明白对象对象引用不是一回事。

    《Java编程思想》原话说:“按照通俗的说法,每个对象都是某个类(class)的一个实例(instance),这里,‘类’就是‘类型’的同义词。”

    从这句话可以得知:对象类的实例。比如,可以把人类看做类的实例,那么具体到某个人的张三他就是对象.


    对象对象引用

    再读《Java编程思想》的一段话:“每种编程语言都有自己的数据处理方式。有些时候,程序员必须注意将要处理的数据是什么类型。你是直接操纵元素,还是用某种基于特殊语法的间接表示(例如C/C++里的指针)来操作对象。所有这些在 Java 里都得到了简化,一切都被视为对象。因此,我们可采用一种统一的语法。尽管将一切都“看作”对象,但操纵的标识符实际是指向一个对象的“引用”(reference)。”

    "操纵的标识符实际是指向一个对象的“引用”(reference)"这句话明确的指出引用对象不是一回事。

    为了方便说明,定义一个简单的类:

    class Person {
        
        String name;
        int age;
        
    }
    

    有了这个类,就可以用它来创建对象:

    Person person1 = new Person();
    

    通常把这条语句的动作称之为创建一个对象,其实,它包含了四个动作。

    • 1)右边的“new Person”,是以Person类为模板,在堆空间里创建一个Person类对象(也简称为Person对象)。
    • 2)末尾的()意味着,在对象创建后,立即调用Person类的构造函数,对刚生成的对象进行初始化。构造函数是肯定有的。如果你没写,Java会给你补上一个默认的构造函数。
    • 3)左边的“Person person1”创建了一个Person类的引用变量。所谓Person类引用,就是以后可以用来指向Person这一类对象对象引用
    • 4)“=”操作符使对象引用指向刚创建的那个Person类的对象

    为了更加清楚了解原理,把Person person1 = new Person() 这条语句拆成两部分:

    Person person1;//第一句
    
    person1 = new Person(); //第二句
    

    为了形象地说明对象、引用及它们之间的关系,可以做一个或许不很妥当的比喻。对象好比是一只很大的气球,大到我们抓不住它。引用变量是一根绳, 可以用来系汽球。

    如果只执行了第一条语句,还没执行第二条,此时创建的引用变量person1还没指向任何一个对象,它的值是null.(引用变量可以指向某个对象,或者为null。)

    此时的person1好比一根还没有系上任何一个汽球的绳1。

    执行了第二句后,一只汽球1做出来了(new一个Person类对象),并被系在person1这根绳上。我们抓住这根绳,就等于抓住了那只汽球1

    在原来的基础上增加多几句代码:

    Person person1;//第一句
    
    person1 = new Person(); //第二句
    
    Person person2; //第三句
    
    person2 = person1; //第四句
    
    person2 = new Person(); //第五句
    
    person1 = person2; //第六句
    
    

    执行到第三句代码就又做了一根绳2,还没系上汽球。

    执行第四句代码,这里发生了复制行为,名为person1的对象引用复制给了person2(要说明的是,对象本身并没有被复制),person2也是对象引用,相当于绳2也系在汽球1上。

    执行第五句代码,则引用变量person2改指向第二个对象,绳2系向新的汽球。

    • 从以上叙述再推演下去,我们可以获得以下结论:

      • 1.一个对象引用可以指向0个或1个对象(一根绳子可以不系汽球,也可以系一个汽球);
      • 2.一个对象可以有N个引用指向它(可以有N条绳子系住一个汽球)。

    按上面的推断,person1也指向了第二个对象。这个没问题。问题是第一个对象呢?没有一条绳子系住它,它飞了。多数书里说,它被Java的垃圾回收机制回收了。这不确切。正确地说,它已成为垃圾回收机制的处理对象。至于什么时候真正被回收,那要看垃圾回收机制的心情了。

    由此看来,下面的语句应该不合法吧?至少是没用的吧?

    new Person();
    

    它是合法的,而且可用的。譬如,如果我们仅仅为了打印而生成一个对象,就不需要用引用变量来系住它。最常见的就是打印字符串:

    System.out.println(“Hello World”);
    

    字符串对象“Hello World”在打印后即被丢弃。有人把这种对象称之为临时对象。

    对象与引用的关系将持续到对象回收。


    值传递

    • Java只有一种参数传递方式:那就是按值传递,即Java中传递任何东西都是传值。如果传入方法的是基本类型的东西,你就得到此基本类型的一份拷贝。如果是传递引用,就得到引用的拷贝。

    基本类型

    public class TestBasicType {
    
        //基本类型的参数传递
        public static void testBasicType(int m) {
            System.out.println("m=" + m);//m=50
            m = 100;
            System.out.println("m=" + m);//m=100
    
        }
    
        public static void main(String[] args) {
            int i = 50;
            testBasicType(i);
            System.out.println("i=+"+i);//i=50
            
            //只是将值复制给参数m,m和i是两回事
        }
    
    }
    
    

    引用类型

    public class Person {
    
    
    
        public int age;//Person的属性,全局变量,初始age为0
    
    
        public void addAge(){
            this.age = this.age+1;
        }
    
    
      
        public static void changeAge(Person p){
            Person p1 = p;    //这里将对象引用p复制给p1,p和p1指向同一对象,因此无论p还是p1操纵对象,对象的内容都会改变
            p1.addAge();      //p1操纵 对象发生改变,p1指向的对象的age=1
            System.out.println("p1.age="+p1.age); //因此p1.age=1
        }
    
        public static void main(String[] args) {
    
            Person p = new Person();//创建一个Person对象,Person p 创建一个Person类的对象引用
            changeAge(p);//将引用p复制给了这个方法的形参
            System.out.println("p.age="+p.age);//p和p1指向同一对象,因此p.age=1
        }
    
    }
    

    可以将上面的p1比喻为汽车的刹车,p比作汽车的油门,汽车的速度同时受到油门和刹车的控制,无论踩下油门还是刹车,
    车速必有变化。当然控制车速的还会有离合,可以假设离合为新的并指向车速的引用p2,那么车速就由p,p1,p2控制,也就是说可以多个引用指向一个对象。


    下面我们通过一个反例来证明值传递

    public class CallByValue {
    
        private static User user=null;
        private static User stu=null;
    
        /**
         * 交换两个对象
         * @param x
         * @param y
         */
        public static void swap(User x,User y){
            User temp =x;
            x=y;
            y=temp;
            //只是复制了引用给了参数x,y
            //x,y变化影响不了外面的引用user,stu
        }
    
    
        public static void main(String[] args) {
            user = new User("user",26);
            stu = new User("stu",18);
            System.out.println("调用前user的值:"+user.toString());
            System.out.println("调用前stu的值:"+stu.toString());
            swap(user,stu);
            System.out.println("调用后user的值:"+user.toString());
            System.out.println("调用后stu的值:"+stu.toString());
        }
    }
    
    调用前user的值:User [name=user, age=26]
    
    调用前stu的值:User [name=stu, age=18]
    
    调用后user的值:User [name=user, age=26]
    
    调用后stu的值:User [name=stu, age=18]
    
    

    即使java函数在传递引用数据类型时,也只是拷贝了引用的值罢了,之所以能修改引用数据是因为它们同时指向了一个对象,但这仍然是按值调用而不是引用调用。


    • 总结:
      • 一个方法不能修改一个基本数据类型的参数(数值型和布尔型)。
      • 一个方法可以修改一个引用所指向的对象状态,但这仍然是按值调用而非引用调用。
      • Java只存在值传递。上面两种传递都进行了值拷贝的过程。

    "="号

    Java中对 = 的理解很重要啊!!

    • = 赋值操作 :在java中,= 是一个动作,一个可以改变内存状态的操作,一个可以改变变量的符号,而+ - * /却不会。

    • 这里的赋值操作其实是包含了两个意思:

      • 1.放弃了原有的值或引用.
      • 2.得到了=右侧变量的值或引用。
    • 对于基本数据类型变量,=操作是完整地复制了变量的值。换句话说,“=之后,你我已无关联”.

    • 对于非基本数据类型变量,= 操作是复制了变量的引用。

    • 参数本身是变量,所有我们对变量的操作、变量能有的行为,参数都有,参数传递本质就是一种 = 操作。

  • 相关阅读:
    group_concat的长度限制
    mb_strlen默认字符集问题
    &符号导致的一个bug
    python面向对象编程系列
    python基础之面向过程编程系列
    RPA流程自动化
    什么是DevOps?
    ansible详解
    saas和paas的区别
    CPT/cpt接口
  • 原文地址:https://www.cnblogs.com/qiyiguoandqijiguo/p/10856639.html
Copyright © 2011-2022 走看看