java中的值传递以及引用传递的混淆

一、总结:java传参问题:Java只有一种参数传递方式:那就是按值传递,即Java中传递任何东西都是传值。所谓的引用也是通过值传递的。如果传入方法的是基本类型的东西,你就得到此基本类型的一份拷贝。如果是传递引用,就得到引用的拷贝。

二、对象和引用在内存中是怎么样存储和起作用的?

【为了说明这个问题,我引用一个博主的转载:https://blog.csdn.net/onlyoncelove/article/details/79533249

举个例子: 定义一个简单的类: 

  class Vehicle {
    int passengers;
    int fuelcap;
    int mpg;
  }

创建一个对象:Vehicle veh1 = new Vehicle();这句代码包含了四个动作:

  1)右边的“new Vehicle”,是以Vehicle类为模板,在堆空间里创建一个Vehicle类对象(也简称为Vehicle对象)。

  2)末尾的()意味着,在对象创建后,立即调用Vehicle类的构造函数,对刚生成的对象进行初始化。

  3)左边的“Vehicle veh1”创建了一个Vehicle类引用变量。所谓Vehicle类引用,就是以后可以用来指向Vehicle对象的对象引用。

  4)“=”操作符使对象引用指向刚创建的那个Vehicle对象。

我们可以把这条语句拆成两部分:
  Vehicle veh1;
  veh1 = new Vehicle();
效果是一样的。这样写,就比较清楚了,有两个实体:一是对象引用变量(放在内存栈中),一是对象本身(放在内存堆中),如下图一。

               图1

至于为什么要通过对象引用来间接的访问对象呢?因为一个Vehicle类可以据此创建出无数个对象,这些对象不可能全叫“Vehicle”,这个只是对象的创建模板,不是对象的本名。对象连名都没有,没法直接访问它。

为了形象地说明对象、引用及它们之间的关系,可以做一个或许不很妥当的比喻。对象好比是一只很大的气球,大到我们抓不住它。引用变量是一根绳,可以用来系汽球。如果只执行了第一条语句,还没执行第二条,那它是一根绳,一根还没有系上任何一个汽球的绳。执行了第二句后,一只新汽球做出来了,并被系在veh1这根绳上。我们抓住这根绳,就等于抓住了那只汽球。
再来一句:Vehicle veh2;
就又做了一根绳,还没系上汽球。如果再加一句:veh2 = veh1;
系上了。这里,发生了复制行为。但是,要说明的是,对象本身并没有被复制,被复制的只是对象引用。结果是,veh2也指向了veh1所指向的对象。两根绳系的是同一只汽球。【就如图一那样子】

如果用下句再创建一个对象:
veh2 = new Vehicle();
则引用变量veh2改指向第二个对象。
从以上叙述再推演下去,我们可以获得以下结论:
(1)一个对象引用可以指向0个或1个对象(一根绳子可以不系汽球,也可以系一个汽球);
(2)一个对象可以有N个引用指向它(可以有N条绳子系住一个汽球)。
如果再来下面语句:veh1 = veh2;
按上面的推断,veh1也指向了第二个对象。这个没问题。问题是第一个对象呢?没有一条绳子系住它,它飞了。多数书里说,它被Java的垃圾回收机制回收了。这不确切。正确地说,它已成为垃圾回收机制的处理对象。至于什么时候真正被回收,那要看垃圾回收机制的心情了。

                   图二


注意:new Vehicle();是合法且有用的,比如:如果我们仅仅为了打印而生成一个对象,就不需要用引用变量来系住它。

再举个例子:

StringBuffer s;
s = new StringBuffer("Java");
StringBuffer s1 = s;
s1.append(" World");
System.out.println("s1=" + s1.toString());//打印结果为:s1=Java World
System.out.println("s=" + s.toString());//打印结果为:s=Java World

解释:s1和s只是两个引用,它们只是操纵杆而已,它们指向同一个对象,操纵的也是同一个对象,通过它们得到的是同一个对象的内容。这就像汽车的刹车和油门,它们操纵的都是车速,假如汽车开始的速度是80,然后你踩了一次油门,汽车加速了,假如车速升到了120,然后你踩一下刹车,此时车速是从120开始下降的,假如下降到60,再踩一次油门,车速则从60开始上升,而不是从第一次踩油门后的120开始。也就是说车速同时受油门和刹车影响,它们的影响是累积起来的,而不是各自独立(除非刹车和油门不在一辆车上)。所以,在上面的程序中,不管使用s1还是s操纵对象,它们对对象的影响也是累积起来的(String除外,因为String始终不变,String s1=”AAAA”; String s=s1,操作s,s1由于始终不变,所以为s另外开辟了空间来存储s,)。

注意:String不可变StringBuffer可变的意思是指堆区的那片内存的可变性:对于String类,通过引用无法修改之前在堆区申请的那段内存,大小是固定的,也就是不能修改他的值,因为他的底层是char数组。当再次给变量new一个值时,他会指向另一个堆区内存,从表面上看也是改变了值。 而对于StringBuffer,程序员可以根据实际使用继续分配更多内存,例如调用append方法,这就是可变的意思。

三、知识点总结:

  1)引用传递(call by reference):引用数据类型:不可以改变原变量的地址,但可以改变原变量的内容;

    值传递(call by value):基本数据类型:不可以改变原变量的内容和地址。

  2)基本类型和基本类型变量被当做参数传递给方法时,是值传递。在方法实体中,无法给原变量重新赋值,也无法改变它的值。
    对象和引用型变量被当做参数传递给方法时,是引用传递(其实质也是根据值传递),在方法实体中,无法给原变量重新赋值,但是可以改变它所指对象的属性。

  3)值传递:java方法的形参传递都是传递原变量的副本,在方法中改变的是副本的值,跟原变量所指的地址和存放的内容没有关系。
    引用传递:当副本的引用改变时,原变量的引用并没有发生变化,当副本的内容改变时,由于副本引用指向的是原变量的地址空间,所以原变量的内容发生了变化

  4)总的来说,值传递只是借用了这个变量名,这个变量名原本的就不关后面操作的事了。而引用传递是相当于原变量的分身(分出一个一模一样的变量指向同样的地址),分身所指的地址一样,但是分身所指的地址存的内容可以改变。

原文地址:https://www.cnblogs.com/yangrongkuan/p/11995561.html