变量、对象和引用之间的区别是什么?

78

变量、对象和引用之间的区别是什么?

例如:它们都指向某种类型,并且它们必须都保存值(除非您使用了临时的可空类型),但是它们的功能和实现方式究竟有何不同?

示例:

Dog myDog = new Dog(); //variable myDog that holds a reference to object Dog
int x = 12; //variable x that hold a value of 12

它们拥有相同的概念,但是它们有什么不同之处呢?


11
我最合理的反对意见是这个标签既打了C#又打了Java。这种做法假设了两种语言处理对象的方式相同,但实际情况并非如此。 - maniak1982
2
@katzenhut,它已经是这样了,并且先前链接的重复内容已经显示出来了,甚至已经在这里提供了。 - Servy
5个回答

180

仅针对Java和C#语言的说明,不要假设它适用于其他语言,尽管其中有些部分可能适用于其他语言。

我喜欢使用一个比喻来告诉别人我住在哪里。我可能会把我的地址写在一张纸上:

  • 变量就像是一张纸,它保存一个值,但它本身并不是这个值。你可以划掉上面的任何东西,然后写下其他东西。
  • 我写在纸上的地址就像一个引用。它不是我的房子,但它是通向我的房子的一种方式。
  • 我的房子本身就像一个对象。我可以给同一个对象提供多个引用,但只有一个对象。

这是否有帮助?

值类型和引用类型之间的区别在于纸上写的内容。例如,在这里:

int x = 12;

就像直接在一张纸上写着数字12一样。

Dog myDog = new Dog();

它并没有在纸上写下Dog对象的内容 - 它创建了一个新的Dog,然后在那张纸上写下狗的引用。

非类比术语:

  • 变量代表内存中的存储位置。它有一个名称,您可以在编译时引用它,在执行时它具有一个值,该值始终与其编译时类型兼容。 (例如,如果您有一个Button变量,则该值将始终是对Button或某个子类对象的引用 - 或null引用。)
  • 对象是一种独立的实体。重要的是,变量或任何表达式的值永远不会是对象,只能是引用。对象实际上由以下组成:
    • 字段(状态)
    • 类型引用(在对象的生命周期内无法更改)
    • 监视器(用于同步)
  • 引用是用于访问对象的值 - 例如,调用其方法、访问字段等。通常使用.运算符浏览引用。例如,如果foo是一个Person变量,则foo.getAddress().getLength()将获取foo的值(引用),并在该引用所指的对象上调用getAddress()。结果可能是一个String引用...然后我们在引用所指的对象上再调用getLength()

36

当我解释这些概念时,我经常使用以下类比:


想象一个物体是气球。一个变量是一个人。每个人都可以在值类型组或引用类型组中。他们都在玩下面的游戏:

值类型规则:

  • 你手里拿着一个充满空气的气球。(值类型变量存储对象。)
  • 你必须始终抓住一个气球。(值类型不可为空。)
  • 当其他人想要你的气球时,他们可以自己吹一个完全相同的气球,并将其握在手中。(在值类型中,对象被复制。)
  • 两个人不能同时拥有同一个气球。(值类型不共享。)
  • 如果您想抓住另一个气球,则必须将已经捉到的气球弄破并抓取另一个气球。(替换值类型对象时,原来的对象被销毁。)

引用类型规则:

  • 你可以拿着一条串联着充满氦气的气球的线。(引用类型变量存储到对象的引用。)
  • 你可以拿着一条线,也可以不拿。(引用类型变量可为空。)
  • 当其他人想要你的气球时,他们可以获得自己的一条线并将其系在与您相同的气球上。(引用类型中,引用被复制。)
  • 多个人可以拿着指向同一个气球的线。(引用类型对象可以共享。)
  • 只要仍有至少一个人抓住某个气球的线,该气球就是安全的。(只要引用类型对象可达,它就是活动的。)
  • 对于任何一个气球,如果每个人最终都放开了它,那么这个气球就会飞走,没人能再去拿到它了。(引用类型的对象有可能在某一时刻变得不可访问。)
  • 在游戏结束之前的某个时刻,一个丢失的气球可能会因大气压力自己破裂。(不可访问的对象有资格进行垃圾回收,这是非确定性的。)

  • 了解3个简单概念之间的差异需要掌握12个要点。 - Sergey Shcherbakov
    3
    @Smollet 嗯,是的。既然楼主问了,显然对他/她来说并不那么简单(也可能对其他来到这里的人来说也是如此)。我相信可以在更短的答案中给出一个更简单的类比,但我个人认为上述所有要点都很重要,值得包括在内。 - Theodoros Chatzigiannakis
    让我扩展一下你的类比,以解释“按值传递”和“按引用传递”。 按值传递:在调用方法中由发送者持有的气球(或字符串)被移交给被调用方法中的另一个人(接收者)。 按引用传递:持有气球(或字符串)的人从调用方法移动到被调用方法。 - Second Person Shooter

    8
    您可以将其视为回答问题。
    对象是什么......
    它就像世界上的任何物理事物一样,是一个“物体”,本身是可识别的,并具有与其他“物体”不同的显着属性。 就像你知道狗是狗,因为它会叫,摇动尾巴,并在你扔球时追球。

    变量是哪个......
    就像你看自己的手。每只手都是一只手本身。他们有皮肤内的手指,指甲和骨头,但你知道其中一只是左手,另一只是右手。 也就是说,您可以拥有两个相同类型/种类的“物体”,但每个“物体”在自己的方式上都可能不同,可以有不同的值。

    引用是哪里......
    如果您看一条街上的两座房子,虽然它们有自己的正面,但您可以通过其独特的地址到达每一个,这意味着,即使您离得很远,比如三个街区或在另一个国家,您也可以告诉房子的地址,因为它们仍然在您离开它们的地方,即使您无法直接指出它们。

    现在为了编程的缘故,以C++方式举例

    class Person{...}
    Person Ana = new Person(); //An object is an instance of a class(normally)
    

    也就是说,Ana是一个人,但她具有独特的属性,使她与其他人区分开来。
    &Ana //This is a reference to Ana, that is to say, a "where" does the variable 
         //"Ana" is stored, wether or not you know it's value(s)
    

    Ana本身是用于存储名为“Ana”的人的属性的变量。


    根据这些定义,不确定对象/变量之间的区别是什么。 - Mdev
    你的 C++ 示例中 Person Ana = new Person(); 中的星号 * 在哪里?应该是 Person* Ana = new Person();,对吧? - Second Person Shooter

    7

    如果您需要更具体的表述,我可以帮忙。

    让我们先从变量开始。变量是一个[有名字的]东西,它包含一个值。例如,int x = 3定义了一个名为x的变量,其中包含整数3。如果我跟着这个赋值操作x=4,那么x现在包含整数4。关键的一点是,我们没有替换变量。我们没有一个新的 "变量x,其值现在是4",我们仅仅用一个新值替换了x的值。

    现在让我们来看看对象。对象是有用的,因为通常你需要一个“东西”被多个地方引用。例如,如果你在编辑器中打开一个文档,并想将其发送到打印机,只需要一个文档被引用即可。这样可以节省你不必要的复制次数。

    然而,因为你不希望复制它超过一次,我们不能只把一个对象放在一个变量中。变量保存一个值,所以如果两个变量持有一个对象,它们将不得不制造两个副本,每个变量一个。引用是解决这个问题的中间人。引用是容易复制的小值,可以存储在变量中。

    因此,在代码中,当您键入Dog dog = new Dog()时,new运算符将创建一个新的Dog对象,并返回对该对象的引用,以便可以将其分配给变量。然后赋值将给dog一个对新创建对象的引用的值。


    3

    new Dog()会实例化一个名为Dog的对象,也就是说它将为对象创建一块内存。你需要访问变量以执行一些操作。为此,你需要一个引用,即myDog狗。如果您尝试打印该对象,它将打印一个不可读的值,这就是地址。

          myDog -------> new Dog().
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接