二、类与对象(下)

2.4 类的属性

属性是一种特殊的“字段”。

先来看一个用于表示学生信息的类Student:

class Student

{

public String Name;  //姓名

public DateTime Birthday;  //生日

public int Age;  //年龄

}

Student类中使用公有字段来表达学生信息,这种方式无法保证数据的有效性。比如外界完全可以这样使用Student类:

Student stu=new Student();

stu.Name="";  //非法数据,名字怎能为空?

stu.Birthday=new DateTime(3000,1,3);  //公元3000年出生,他来自未来世界?

stu.Age=-1;  //年龄必须大于0!

在设计类时使用属性(Property)可以保证只有合法的数据可以传给对象。

以Name这个字段为例,它要求不能为空。

首先,定义一个私有的_Name字段;

private String _Name="姓名默认值";

接着,即可定义一个Name属性:

public String Name

{

get   //读

{

return _Name;

}

set   //写,使用隐含变量value

{

if(value.Length==0)

throw new Exception("名字不能为空");

_Name=value;

}

}

Name属性由两个特殊的读访问器(get)和写访问器(set)组成。

当读取Name属性时,读访问器被调用,仅简单的向外界返回私有字段_Name的值。

当设置Name属性时,写访问器被调用,先检查外界传入的值是不是空串,再将传入的值保存于私有字段中。

经过这样的设计,以下的代码在运行时会抛出一个异常提醒程序员出现了错误需要更正:

Student stu=new Student();

stu.Name=" ";  //非法数据,名字怎能为空?

写访问器中有一个特殊的变量value必须特别注意,它代表了外界传入的值,例如以下代码向Name属性赋值:

Student stu=new Student();

stu.Name="张三";

“张三”这一字串值将被保存到value变量中,供写访问器使用。

由上述例子可知,编写属性的方法如下:

(1)设计一个私有的字段用于保存属性的数据;

(2)设计get读访问器和set写访问器存取私有字段数据。

C#中还允许定义只读属性和只写属性。只读属性只有get读访问器,而只写属性只有set写访问器。

2.5 深入理解类与对象

(1)类和对象的区别

① 对象是以类为模板创建出来的。类与对象之间是一对多的关系。

② 在C#中,使用new关键字创建对象。

③ 在程序中“活跃”的是对象而不是类。

在面向对象领域,对象有时又被称为是“类的实例”,“对象”与“类的实例”这两个概念是等同的。

(2)类的构造函数

当使用new关键字创建一个对象时,一个特殊的函数被自动调用,这就是类的构造函数(constructor)。

在C#中,类的构造函数与类名相同,没有返回值。

class A

{

//类A的构造函数

public A()

{

}

}

类的构造函数在以类为模板创建对象时被自动调用。构造函数一般用于初始化类的私有数据字段。

(3)引用类型与值类型

.NET将变量的类型分为“值类型”与“引用类型”两大类。诸如int和float之类的变量属于值类型,而“类”类型的变量则属于“引用类型”。

值类型变量与引用类型变量在使用上是有区别的。

值类型的变量一定义之后就马上可用。比如定义“int i;”之后,变量i即可使用。

引用类型的变量定义之后,还必须用new关键字创建对象后才可以使用,我们在前面已经多次这样使用过引用变量了。

在Visual Studio随机文档中,详细地列出了每种数据类型属于值类型还是引用类型:

  类别 说明
值类型 简单类型 有符号整型:sbyte,short,int,long
    无符号整型:byte,ushort,uint,ulong
    Unicode字符:char
    IEEE浮点型:float,double
    高精度小数:decimal
    布尔型:bool
  枚举类型 enum E{...}形式的用户定义类型
  结构类型 struct S{...}形式的用户定义类型
引用类型 类类型 所有其他类型的最终基类:object
    Unicode字符串:string
    class C{...}形式的用户定义类型
  接口类型 interface I{...}形式的用户定义类型
  数组类型 一维和多维数组,例如int[]和int[,]
  委托类型 delegate T D(...)形式的用户定义类型

值类型变量与引用类型变量的内存分配模型也不一样。为了理解清楚这个问题,读者首先必须区分两种不同类型的内存区域:线程堆栈(Thread Stack)和托管堆(Managed Heap)。

每个正在运行的程序都对应着一个进程(process),在一个进程内部,可以有一个或多个线程(thread),每个线程都拥有一块“自留地”,称为“线程堆栈”,大小为1M,用于保存自身的一些数据,比如函数中定义的局部变量、函数调用时传送的参数值等,这部分内存区域的分配与回收不需要程序员干涉。

所有值类型的变量都是在线程堆栈中分配的。

另一块内存区域称为“堆(heap)”,在.NET这种托管环境下,堆由CLR进行管理,所以又称为“托管堆(managed heap)”。

用new关键字创建的类的对象时,分配给对象的内存单元就位于托管堆中。

在程序中我们可以随意地使用new关键字创建多个对象,因此,托管堆中的内存资源是可以动态申请并使用的,当然用完了必须归还。

打个比方更易理解:托管堆相当于一个旅馆,其中的房间相当于托管堆中所拥有的内存单元。当程序员用new方法创建对象时,相当于游客向旅馆预订房间,旅馆管理员会先看一下有没有合适的空房间,有的话,就可以将此房间提供给游客住宿。当游客旅途结束,要办理退房手续,房间又可以为其他旅客提供服务了。

从列表可以看到,引用类型共有四种:类类型、接口类型、数组类型和委托类型。

所有引用类型变量所引用的对象,其内存都是在托管堆中分配的。

严格地说,我们常说的“对象变量”其实是类类型的引用变量。但在实际中人们经常将引用类型的变量简称为“对象变量”,用它来指代所有四种类型的引用变量。在不致于引起混淆的情况下,本节也采用了这种惯例。

在了解了对象内存模型之后,对象变量之间的相互赋值的含义也就清楚了。请看以下代码:

01     class A

02     {

03         public int i;

04     }

05     class Program

06     {

07         static void Main(string[] args)

08          {

09               A a;

10               a=new A();

11               a.i=100;

12               A b=null;

13               b=a;  //对象变量的相互赋值

14               Console.WriteLine("b.i="+b.i);  //b.i=?

15          }

16     }

注意第12和13句。

程序的运行结果是:

b.i=100;

请读者思索一下:两个对象变量的相互赋值意味着什么?

事实上,两个对象变量的相互赋值意味着赋值后两个对象变量所占用的内存单元其内容是相同的。

讲得详细一些:

第10句创建对象以后,其首地址(假设为“1234 5678”)被放入到变量a自身的4个字节的内存单元中。

第12句又定义了一个对象变量b其值最初为null(即对应的4个字节内存单元中为“0000 0000”)。

第13句执行以后,a变量的值被复制到b的内存单元中,现在,b内存单元中的值也为“1234 5678”。

根据前面介绍的对象内存模型,我们知道现在变量a和b都指向同一个实例对象。

如果通过b.i修改字段i的值,a.i也会同步变化,因为a.i与b.i其实代表同一对象的同一字段。

由此得到一个重要结论:

对象变量的相互赋值不会导致对象自身被复制,其结果是两个对象变量指向同一对象。

另外,由于对象变量本身是一个局部变量,因此,对象变量本身是位于线程堆栈中的。

严格区分对象变量与对象变量所引用的对象,是面向对象编程的关键之一。

由于对象变量类似于一个对象指针,这就产生了“判断两个对象变量是否引用同一对象”的问题。

C#使用“==”运算符比对两个对象变量是否引用同一对象,“!=”比对两个对象变量是否引用不同的对象。参看以下的代码:

//a1与a2引用不同的对象

A a1=new A();

A a2=new A();

Console.WriteLine(a1==a2);  //输出:false

a2=a1;  //a1与a2引用相同的对象

Console.WriteLine(a1==a2);  //输出:true

需要注意的是,如果“==”被用在值类型的变量之间,则对比的是变量的内容:

int i=0;

int j=100;

if(i==j)

{

Console.WriteLine("i与j的值相等");

}

理解值类型与引用类型的区别在面向对象编程中非常关键。

时间: 2024-08-05 23:03:51

二、类与对象(下)的相关文章

Java复习(二)类与对象的基本概念

2.1面向对象的程序设计方法概述 对象 程序中: 一切皆是对象 都具有标识,属性和行为 通过一个或多个变量来保存其状态 通过方法实现他的行为 类 将属性及行为相同或相似的对象归为一类 类可以看成是对象的抽象,代表了此类对象所具有的共有属性和行为 在面向对象的程序设计中,每一个对象都属于某个特定的类 2.1.1抽象 过程抽象 数据抽象 2.1.2封装 2.1.3继承 Java仅支持单继承 2.1.4多态 2.2类与对象 2.2.1类的声明 声明形式 [public] [abstract|final

Objective-C 笔记二 类、对象和方法

对象就是一个物件.面向对象的程序设计可以看成一个物件和你想对它做的事情.这与C语言不同,C语言通常称为过程性语言.在C语言中,通常是先考虑要做什么,然后才关注对象,这几乎总是与面相对象的思考过程相反. 在面向对象的用语中,(这里我们以汽车作为对象)你的汽车是汽车的一个实例.car就是类的名称,这个实例就是从该类创建的.因此,每制造一辆新汽车,就会创建汽车类的一个新实例,而且汽车的每个实例都称为一个对象. 对象 使用对象执行的操作 你的汽车 驾驶 加油 洗车 维修 每个实例或对象不仅包含从制造商那

JavaScript中的类数组对象

在javascript中,对象与数组都是这门语言的原生规范中的基本数据类型,处于并列的位置. 一般来说,如果我们有一个对象obj和一个数组a: obj["attr1"];    //取obj对象的attr1属性 a[1];   //取数组a中的第二个元素 但是,有些时候,也会将一个对象“伪装”成一个数组来用,我们把这种对象称为“类数组对象”,再此我们可以給它下一个定义,请看如下的代码: var a= {}; var i = 0; for(i=0; i<10 ; i++){ a[i

javascript:类数组 -- 对象

在javascript中,对象与数组都是这门语言的原生规范中的基本数据类型,处于并列的位置. 类数组:本质是一个对象,只是这个 对象  的属性有点特殊,模拟出数组的一些特性. 一般来说,如果我们有一个对象obj和一个数组a: obj["attr1"];    //取obj对象的attr1属性 a[1];              //取数组a中的第二个元素 但是,有些时候,也会将一个对象"伪装"成一个数组来用,我们把这种对象称为"类数组对象",再

PHP 类与对象 全解析( 二)

目录 PHP 类与对象 全解析( 一) PHP 类与对象 全解析( 二) PHP 类与对象 全解析(三 ) 7.Static关键字 声明类成员或方法为static,就可以不实例化类而直接访问.不能通过一个对象来访问其中的静态成员(静态方法除外). 静态成员属于类,不属于任何对象实例,但类的对象实例都能共享. 小结: 在类内部访问静态成员属性或者方法,使用 self::(没有 $ 符号),如:  self:: $country  //类内部访问静态成员属性  self:: myCountry()

oc语言学习之基础知识点介绍(二):类和对象的进一步介绍

一.类.对象在内存中的存储 /* 内存分区: 栈:局部变量 堆:程序员自己写代码申请开辟的 程序员自己维护,编译器现在帮我们自动优化了,它在合适的给我们加上了释放空间的语句,所以我们现在写的对象不会造成内存泄露 全局区:所有的全局变量和静态变量 常量区:所有的常量 代码区:程序编译后的指令集 类是模板,肯定需要存在内存里面,因为实例化对象的时候需要根据这个模板来创建,那么存在内存里面,存在哪呢?? 类模板存在:全局区! 存的是:类的描述,还有所有的方法实现 每个对象都会有一个系统给我们的isa指

二维数组 面向对象编程的概念、 类、对象以及引用 和成员方法

1.二维数组(熟悉)1.1 基本概念 一维数组本质上就是一段连续的存储单元,用于存放多个类型相同的数据内容. 二维数组本质上就是由多个一维数组组成的数组,也就是说二维数组中的每个元素都是一维数组,而一维数组的每个元素才是具体数据内容. 1.2 二维数组的声明(1)语法格式 数据类型[][] 数组名称 = new 数据类型[行数][列数];如: int[][] arr = new int[2][5]; - 声明一个具有2行5列元素类型为int的二维数组 其中行下标的范围是:0 ~ 1: 其中列下标

类和对象(二)

对象的自身引用是面向对象程序设计语言中特有的.十分重要的一种机制.在C++中,为这种机制设立了专门的表示:this指针变量. 在类的每一个成员函数的形参表中都有一个隐含的指针变量this,该指针变量的类型就是成员函数所属类的类型. 当程序中调用类的成员函数时,this指针变量被自动初始化为发出函数调用的对象的地址. 例如: 123456789101112131415161718 #include <iostream>using namespace std;class Sample{ int x

C++笔记二:类和对象

类和对象 1:面向对象基础 2: 定义类和对象 3: 类和对象的使用 4: 构造函数和析构函数 5: this指针和复制构造函数 6: static静态成员 7: const对象和const成员 8: friend友元 ---------------------------------------------------------------------------------------------------------------------- (1)面向对象基础 一.什么是面向对象程序