学编程,来源栈;先学习,再交钱
当前系列: 编程语言 修改讲义


引用 :对象的地址

Student wx = new Student();

这行代码,实际上做了三件事:

  1. 生成一个对象(new Student()),获得该对象的存储地址(可以想象成房屋的地址/门牌号
  2. 声明一个Student的变量wx
  3. 将在1中生成的对象存储地址赋值给2中的变量wx,从而把变量wx和对象关联起来

理解这三步非常重要。其关键点就在于:wx中存储的不是对象本身,而是对象的地址。

PS:对象和变量都是存放在内存中的

在面向对象的语言中,对象的地址又被称之为引用(reference)。(面向过程的语言,比如C语言中,称之为“指针”)

指针:&和*

眼见为实,C#中允许使用

  • &:通过变量拿到变量地址
  • *:通过变量地址拿到变量中存放的数据(值)

演示:使用&运算符……


数据存储位置

因为引用类型变量并不存放实质性数据,所以当我们运行这一行代码的时候:

wx.age = 18;

18并没有存放在wx中,而是存放在wx(通过内存地址)所指向的对象中,如图所示:

取值的时候也一样:

我们需要通过变量里面存储的内存地址,找到相应的对象,然后才能在对象中拿到值。

如果要改变变量本身的值,只有一种办法,重新给它赋值一个对象:

wx = new Student();
*&演示:wx中存储的地址发生了改变。



引用类型 vs 值类型

如果一个类,实例化之后的对象,不会直接存放在变量(栈)中,而是存放在另外的地方(堆),它就是引用类型;相对应的,就是值(value)类型

如果一个变量,存放的是对象的地址,而不是对象本身,这个变量就是引用类型变量;相对应的,就是值类型变量。

如何区分

值类型:
  • C#:由struct(类似于class)定义
    struct Student
    {
        public int age;
    
  • Java:基本类型
  • JavaScript:/捂脸.jpg

引用类型:值类型以外,或者由class/interface等定义……(注意:数组不是基本类型,而是引用类型)

&和*演示:值类型中存放的是值

  • 变量i中直接存放着18这个数值,如图所示:
    int i = 18; 
    
  • 变量wx中存放的是Student对象(struct定义)
    Student wx = new Student();

null值

同时注意,如果wx变量没有指向任何对象的话(即*&wx的值为0x00000……),我们可以说它的值为null 。

null值上不能进行任何运算,由于对象为null值而报的错误NullReferenceException是程序中最常见的错误。

Student wx = null;
wx.age = 18;

值类型变量的值不能为null,如果没有为其赋值,它会有一个默认值(参考:默认值表)。

int age = null;    //编译错误


同一个对象

理解值类型和引用类型的区别有什么作用呢?

我们来看这个代码:

Student wx = new Student();
wx.age = 18;
Student clone = wx;         -- 这里是把wx的什么赋值给了clone?
clone.age = 20;

Console.WriteLine(wx.age);

@想一想@:输出的结果是什么?为什么呢?

运行之后我们发现wx.age变成了20,因为 :

Student clone = wx; 这一行代码,实际上是把变量wx中存放的Student对象地址赋值给了clone,从而让clone变量中也存放了wx的对象地址。

所以,当我们修改clone.age的时候,实际上修改的是new Student()对象中的Age。而由于wx和clone都执行了new Student(),所以当我们获取wx.age的时候,获取的也是clone.age的值。

当Student是struct定义

演示:wx.age仍然是18


正本清源

网上有很多不严谨不规范的资料文章:

因为没有struct

Java/JavaScript中没有struct,只能这样演示:

int i = 18; 
int j = i; -- 注意这一行代码 
j = 20;

Console.WriteLine(i); -- i的值是18还是20? 

细想还是有一点问题的:因为j是被完全重新赋值,不是在修改j上的值。

上述代码完全转化成引用类型的话,应该这样:

Student wx = new Student();
wx.age = 18;

Student clone = wx;
clone = new Student();
clone.age = 20;

Console.WriteLine(wx.age); //还是18,体现不出来引用&值类型的区别

@想一想@:为什么会是这样的结果呢?

方法参数值传递

有些资料会用方法参数传递来演示:值类型和引用类型。

方法默认传递方式是值传递:传递的是变量的值的“副本”,所以方法不改变变量本身

static void grow(ref int age)
{
    age++;
    Console.WriteLine(age);
}

调用该方法:

Student atai = new Student();
atai.age = 18;
//值传递:传递是一个“值”,或者理解成:变量的值的副本 
grow(atai);
Console.WriteLine(atai.age);

这和变量的赋值是一样的道理。

但是,引用传递 呢?

注意:Java和JavaScript里面没有真正的引用传递,你以为的引用传递,实际上是引用类型的值传递

static void grow(Student student)//Student是一个引用类型而已
{
    student.age++;
    Console.WriteLine(student.age);
}

调用这个方法时,一样是把方法外面的变量(地址)值,拷贝一份给方法参数而已。

真正的引用传递,只有C#里面才有。


作业:

  1. 说一说引用类型和值类型的区别
  2. 给你一个类型,你能有哪些办法判断它是值类型,还是引用类型?
  3. 用代码和调试过程演示:
    1. 值类型参数的值传递
    2. 引用类型参数的值传递
  4. @想一想@:为什么要设计出引用类型?
学习笔记
源栈学历
键盘敲烂,月薪过万作业不做,等于没学

作业

觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 编程语言 中继续学习:

多快好省!前端后端,线上线下,名师精讲

  • 先学习,后付费;
  • 不满意,不要钱。
  • 编程培训班,我就选源栈

更多了解 加:

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

写代码要保持微笑 (๑•̀ㅂ•́)و✧

公众号:源栈一起帮

二维码