引用和指针

C/C++中引用和指针的区别:

  • 指针是一个实体,而引用仅是个别名;
  • 引用使用时无需解引用(*),指针需要解引用;
  • 引用只能在定义时被初始化一次,之后不可变;指针可变;
  • 引用没有 const,指针有 const;
  • 引用不能为空,指针可以为空;
  • “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;
  • 指针和引用的自增(++)运算意义不一样;
  • 从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。

C++的引用是给现有的变量取别名,二者是同一个对象,只是名称不同,修改了引用变量的值,原先变量的值也会跟着改变,引用变量一旦指定了是哪一个对象的引用后,就不能更改了。

Java中的引用和C++的引用不同:修改引用变量不会影响原来的变量,但是修改引用变量所指的对象,会作用到目标对象。

Java中的引用类似C++的指针:通过一个变量(别名)存储实际对象的地址,找到要操作的目标(对象)。

值传递和引用传递

误区

  • 误区1:Java中对于基础数据类型是值传递,对于引用类型是引用传递
  • 误区2:如果传递的是内容就是值传递,如果传递的是引用地址,就是引用传递

实参和形参

  • 形式参数:定义方法的时候使用的参数名称,用于接收调用函数时传入的数据
  • 实际参数:调用方法时真正传递给方法的数据

求值策略

实际参数传递给形式参数的时候分为两种情况:值传递和引用传递。

  • 传值调用(值传递):获取实际参数的值,复制值,传递给方法。由于形参拿到的是个拷贝,因此在方法中修改形参,实参的值不会改变。
  • 传引用调用(引用传递):直接把实参的引用传递给方法,不拷贝。由于传递的是引用,因此在方法中修改形参,实参的值也会发生改变。
    • 修改形参引用的对象,外部变量引用的对象发生改变
    • 修改形参引用的对象的属性,外部变量引用的对象的属性发生改变
  • 传共享对象调用(共享对象传递):获取到实际参数的地址,复制地址,传递给方法。由于形参和实参的地址都指向同一个对象,所以也称为"传共享对象",在方法中修改形参,实参也会发生改变。
    • 修改形参引用的对象,外部变量引用的对象不会改变
    • 修改形参引用的对象的属性,外部变量引用的对象的属性发生改变

从结果上看,引用传递和共享对象传递都会对实参引用的对象产生影响。

从过程上看,值传递和共享对象传递都有一个关键的步骤:复制。

这里我们应该更关注过程,因此通常把共享对象传递当作值传递的特例。值传递和引用传递的主要区别在于是直接传递,还是传递一个副本。

举例:

  • 引用传递:你有一把钥匙,当你的朋友想要去你家的时候,如果你直接把你的钥匙给他了。这种情况下,如果他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字。
  • 值传递:你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,自己的还在自己手里。这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。

为什么说Java只有值传递?

可以确定的是,对于基本数据类型是值传递,有疑问的是引用类型的传递。

误区:由于Java中的变量和对象之间存在引用关系,通过对象的引用来操纵对象,因此很多人认为对引用类型传递是引用的传递。并且在方法中修改对象的属性之后,外部对象的属性也会发生改变。

在 《The Java™ Tutorials》中描述如下:

Primitive arguments, such as an int or a double, are passed into methods by value. This means that any changes to the values of the parameters exist only within the scope of the method. When the method returns, the parameters are gone and any changes to them are lost.

(基础类型参数通过值传递给方法。这意味着对参数值的任何更改都只存在于方法的范围内。当方法返回时,参数将消失,对它们的任何更改都将丢失。)

Reference data type parameters, such as objects, are also passed into methods by value. This means that when the method returns, the passed-in reference still references the same object as before. However, the values of the object’s fields can be changed in the method, if they have the proper access level.

(引用数据类型参数,如对象,也按值传递给方法。这意味着,当方法返回时,传入的引用仍然引用与以前相同的对象。但是,如果对象字段具有适当的访问级别,则可以在方法中更改这些字段的值。)

可以看出Java中只有值传递,对于引用类型,传递的是对象地址的拷贝(即上文提到的传共享对象调用)。

为什么共享对象传递不属于引用传递,而是属于值传递?为什么值传递和共享对象传递现象不一致?

主要在于如何理解“改变值”:

  1. 对形参指向的对象进行修改:值传递中外部对象的属性不会改变,共享对象传递中外部对象的属性会发生改变。
  2. 修改形参指向的对象:值传递和共享对象传递都不会改变外部变量指向的对象。

举例:

对形参指向的对象进行修改:你复制了一把你家里的钥匙给到你的朋友,他拿到钥匙以后,并没有在这把钥匙上做任何改动,而是通过钥匙打开了你家里的房门,进到屋里,把你家的电视给砸了。这个过程,对你手里的钥匙来说,是没有影响的,但是你的钥匙对应的房子里面的内容却是被人改动了。

修改形参指向的对象:你复制了一把钥匙给到你的朋友,你的朋友拿到你给他的钥匙之后,找个锁匠把他修改了一下,他手里的那把钥匙变成了开他家锁的钥匙。这时候,他打开自己家,就算是把房子点了,对你手里的钥匙,和你家的房子来说都是没有任何影响的。

class House {}
class Key {
    House house; //一把钥匙,能够打开一个房子
}

public class Main {
  public static void main(String[] args) {
    Key yourKey = new Key(new House());
    handle(yourKey);
  }
  private static void handle(Key copyKey) {
    //对形参指向的对象进行修改
    copyKey.house = null; //修改的是你的房子
    //修改形参指向的对象
    copyKey = new Key(new House()); //yourKey不会改变。如果是引用传递,yourKey指向的对象也会改变
    copyKey.house = null; //烧掉房子不会对你的房子造成影响
  }
}

总结:Java中的对象传递是共享对象传递。修改引用(形参)不会对原来的对象产生影响,但是如果修改共享对象的属性的值,是会对原来的对象有影响的。

共享对象传递是值传递的特例,所以一般也说Java中只有值传递

results matching ""

    No results matching ""