Chap 9 学习笔记-静态类成员和类关系

来源:互联网 发布:iphone4有4g网络吗 编辑:程序博客网 时间:2024/06/07 12:55

Chap 9 Notes

--------------------------

 

1、              单个的对象一般都是彼此独立的,在对象上调用的方法不会影响到其他对象。这在技术上称为对象实例成员,它包括对象实例属性和对象实例方法

2、              如果需要在某种类型的对象之间共享某些功能,就需要定义在整个“类”上应用的方法和属性,并且这些方法和属性与特定的对象没有关系。

3、              在整个“类”上定义的方法和属性称为“静态成员”

4、              静态属性实现功能的例子:可以实现统计从这个类中被实例化的对象的个数(总数)

5、              静态方法实现功能的例子:把从此类实例化的所有对象的某个属性一次性设置为某个特定的值

6、              静态成员是在类的所有实例上执行操作的成员,它可以通过对象类本身来访问,也可以通过类的任何一个特定实例来访问

7、              使用静态属性的例子:

<%@ Page Language=”C#” runat=server Debug=true%>

<script runat=server>
       //
开始定义User

public class User

{

       //定义一个静态私有变量

       //注意static private int这三个字的位置!

static private int counter;

       static public int Counter

       {

              get

              {

                     return counter;

}

}

//定义了静态构造函数

//静态构造函数由.NET运行时调用,且只执行一次!

static User()

{

       counter=0;

}

//定义了类的构造函数

//类的构造函数只有在类实例化的时候才执行

public User()

{

       counter++;

}

}

//开始定义用户显示部分

void Page_Load()

{

       Response. Write(User. Counter+”<br>”);

       User A=new User();

Response. Write(User. Counter+”<br>”);

User V=new User();

Response. Write(User. Counter);

}

</script>

8、              在这段代码中应该注意:

(1)        注意:每次打开此文件(或刷新)时,counter的值都会上升,并且此值是存储在IIS端的,需要重启IIS才能清空此值

(2)        有关静态构造函数:这种构造函数用于初始化静态变量的值。其函数名称与类名相同,但前面加上了static关键字,且没有参数。在程序运行过程中,它只执行一次(这就解释了上面的第(1)点中为什么要重启IIS才能清空counter的值),而非静态构造函数在每次创建对象的时候都要激活。静态构造函数没有修饰符,它不是由代码显式调用,而是由.NET运行时调用,所以必须是公开访问的

(3)        即使没有实例化此“类”的任何对象,也可以访问此类的静态属性(User. Counter)。这是因为此时在代码中,已经执行了静态构造函数,设置了counter的初始值

9、              静态方法:不需要创建类的实例,就可以调用类的静态方法。

10、          实际上,所有的构造函数(方法)都是静态的!它们不可能是对象实例的方法,因为在new过之后才创建了对象实例,才能调用对象实例的方法

11、          类之间的关系——使用关系(相关关系)、包含关系、继承关系

12、          类的使用(相关)关系:把一个对象提交为另一个对象方法调用时的一个参数

13、          如果类A的对象执行完其全部功能,但是完全没有意识到类B,则称类A没有使用类B

14、          “类使用关系”代码示例:见Chap9/rm286.aspx文件

15、          在此示例中应注意的问题:

(1)        若类中使用了带参数的构造函数,在实例化此类的一个对象的时候要带上参数!

(2)        注意此代码示例中的关键部分:将一个对象作为另一个对象的参数进行提交

16、          类的包含关系:一个类包含用另一个类定义的对象(包括私有数据成员和对象属性)。通过类包含关系可以把一个类(包含)的数据成员定义为派生与另一个类(被包含)的对象

17、          如果说类A定义了类B的一个数据成员,就可以说类A的对象包含类B的对象

18、          “类包含关系”代码示例:见Chap9/rm292_Car_Engine.aspx文件

19、          在此示例中应注意的问题:

(1)            MyCar.objEngine=new Engine(“Audi A8 quattro”);语句中,左边MyCar.objEngine的作用是返回一个Engine类的对象,这是由于调用使用“被包含类”定义的函数的时候,返回的只是“被包含类”的一个对象!!!右边      new Engine(“Audi A8 quattro”)的作用是实例化这个Engine

(2)            MyCar.objEngine.EngType语句中,注意到EngType本身是Engine类的一个属性,这同样是由于MyCar.objEngine只返回了被包含类Engine的一个对象,要使用MyCar.objEngine.EngType来访问Engine对象的一个具体属性的缘故

(3)            注意:“类的包含关系”存在的目的在于,当我们得到一个已经编写好的“类”的程序,这个“类”可能是其他人编写的,并且是已经编译好的。要使用它,我们只需要在自己的类当中包含进用这个类定义的私有数据成员以及类的属性,就可以按照上面的规则,对这个类的属性进行访问了

20、          .NET中,把对象用做类的属性是非常常见的。

21、          .NET中的包含关系示例:Request.Browser.Type;这个语句在引用Request对象时再引用该对象上的一个Browser属性,该Browser属性返回一个HttpBrowser类型的对象,通常表示Browser对象。这个对象有自己的属性,在本例中是Type属性

22、          类的继承关系:当新类具备原有类的所有功能与特性(即方法与属性)时,可以根据类的继承关系,根据新类和已有类之间的区别定义新类

23、          使用类的继承关系的例子:

<%@ Page Language=”C#” runat=server Debug=true%>

<script runat=server>

       //现在认为原有已经定义好了Car

       //注意下面一句类的继承的写法

       public class FlyingCar:Car

       {

              private int pressure;

              public int Altitude

              {

                     get

                     {
                            return PressureToAltitude(pressure);

}

}

private int PressureToAltitude(int Pressure)

{

       ……

}

public void Ascend()

{

       ……

}

public void Descend()

{

       ……

}

//注意:必须调用基类的构造函数,以及“基类”的“基类”的……构造函数

//调用基类的构造函数,并给它传送需要的参数

//说明新类在实例化的时候(构造函数)还是需要一个参数的

//这个参数是传递给基类的构造函数的,因为基类的构造函数需要

public FlyingCar(int IgnitionShape):base(IgnitionShape)

{

}

}

</script>

24、          在这段代码中应注意:

(1)        在新类定义过程中继承关系的表示方法:public class FlyingCar:Car

(2)        新类的构造函数:在创建新类时,要调用基类的构造函数,这其中包括System.Object基类的构造函数

(3)        新类的构造函数所需的参数是直接传送给基类的构造函数使用的

25、          理解类的继承关系:如果类A是类B的一个特定变体,就可以从B派生出A

26、          有关“基类”的内容对所有“子类”都是正确的;如果发现基类A中有任何一个不能应用于子类B的内容,则B就不是A的一个有效子类

27、          一些面向对象的系统可以继承于多个类(多重继承),但C#不能这样。.NET类只能直接从另一个类继承功能,但继承可以有多级

28、          重要内容:不需要(基)类的源代码,就可以从该(基)类派生新类。只要有(基)类的编译版本即可,不需要(基)类的定义就可以扩展它。找到可执行所需操作的已有(基)类(系统类或第三方提供的类),再从它派生一个子类,而只需要根据自己的目的添加需要的功能即可

29、          过度的继承会使程序过于复杂

30、          .NET中的每个类(以及每个对象)都继承自一个类Object,它在System名称空间中定义,提供了一小组标准属性和方法。这些属性和方法可用于支持所有的对象,无论对象的类型是什么。也就是说,System. Object是所有类和对象最终的基类

31、          示例:

Car MyCar=new Car(123);

Response. Write(MyCar.GetType().FullName);

//保存为文件名:car_gettype.aspx

//结果如下

ASP.car_gettype_aspx+Car

32、          “类继承关系”代码示例:见Chap9/rm301_Sci_Calculator_object.aspx文件

33、          在这段代码中应注意:

(1)        在派生类定义过程中,若调用基类的方法或访问基类的属性,仅需要直接写出属性或方法的名称即可!注意在方法调用时可能需要提供参数!

(2)        在派生类的实例(对象)中使用基类的方法或属性,应写明实例名和基类方法名或属性名。语法如下:

派生类对象名称.基类属性名;      派生类对象名称.基类对象名(参数);

(3)        一个类中的私有数据成员(设置了private)对其外部的任何代码(包括其派生类)都是不可访问的

(4)        如果将类的数据成员设置为protected,就允许这个类的所有派生类使用这个数据成员,但还是不允许外部程序访问。但这样做会访问原本不该被访问的基类源代码,会带来一些安全隐患

(5)        .NET中定义一个类的派生类的时候,要依次调用此派生类的所有基类的构造函数,一直到System. Object基类的构造函数

34、          高级的继承概念:C#可以创建函数和属性只能由派生类使用的特定类,这些类不能实例化,即不能使用new操作符创建它们的实例。这样的类包括:抽象类(抽象基类)和接口

35、         抽象类:是在定义时用abstract关键字标记的一般类。它们可以包含属性、函数、构造函数和一般类的所有其他特性。还有一个额外的属性:抽象方法抽象方法是没有执行代码的函数标题,派生类必须提供其执行代码

36、          接口:是一种特殊的抽象类,用interface关键字指定,但在其定义中没有class关键字。它们可以包含属性和方法,但其方法只能是抽象方法,任何派生于接口的类都必须执行这些方法

37、          类只能继承自一个基类,但是,类可以派生于0个或多个接口。只需要所有接口定义的每个方法都已实现即可

38、          在需要的基本类本身没有什么用处或没有价值时,就可以使用这两种类,这会给派生于它们的任何类增加功能。比如一类事物,都具有某些属性或方法,而这类事物中每个个体都一定会有其特性,所以这个基类本身实例化也没有什么意义,不能将客观事物表示全面

39、          在基类中执行函数的默认代码,如果需要,可以在派生类的相同函数中添加一个修改过的执行代码,替代或重写默认的执行代码。这种方式适用于所有的类,包括一般类和抽象类。

40、          要完成以上的功能,首先要在基类中定义一个虚拟函数,在派生类中重写它。虚拟函数是一个可由派生类重写,但不一定非重写不可的一般函数。在派生类中重写的函数是与基类中的虚拟函数有相同的名称和签名,但其他的内容不同的一般函数

41、          关于基类虚拟函数以及在派生类重写此函数的代码示例:

//在基类定义中:使用virtual关键字

public class Vehicle

{

       public virtual bool Drive()

       {

              //基类定义的默认执行代码

}

}

//在派生类定义中:使用override关键字

public class Airplane:Vehicle

{

       public override bool Drive()

       {

              //专门为Airplane写的代码

}

}

//如何解决派生类函数名称可能与基类非虚拟函数名称相同的问题?

//看下面的代码示例:使用new关键字

public class MyClass:MyBase

{

       public new bool DoIt()

       {

              //使用new关键字,即使在基类中存在同名非虚拟函数,也不用担心出现错误

}

}

//在派生类中重写了基类虚拟函数后,对此基类虚拟函数的访问应采用以下方式

//使用base关键字:

base.DoIt();     base.Drive();

42、          在以上代码中应该注意:

(1)        virtual关键字表示,此(基类)函数可以由派生类重写,但不一定非重写不可

(2)        override关键字表示,此(派生类)函数不想执行此基类虚拟函数的默认执行代码,而使用这里重新定义的执行代码

(3)        如果派生类中有函数和基类中的非虚拟函数同名,就会产生错误。这时应在派生类函数定义中加入new关键字

(4)        注意在派生类中重写基类虚拟函数之后,基类虚拟函数还是可以被访问的,使用base关键字。注意base是一个关键字,直接使用即可

43、          可以使用this关键字,引用当前的对象实例。假定一个函数需要把某种类型的对象作为其参数,就可以使用Function(this);将当前对象实例作为参数传递。

44、          抽象类和接口的代码示例:

//定义一个抽象(基)类

public abstract class Vehicle

{

       public bool Drive()

       {

              //定义一个一般函数

}

//定义一个抽象方法

public abstract void SetSpecificVehicleProperties();

}

//在派生类中必须执行基类的虚拟函数,如下:

public class Car:Vehicle

{

       public override void SetSpecificVehicleProperties()

       {

              //针对Car类编写的代码

}

}

//定义一个接口:使用interface关键字

interface IVehicle

{

       void SetSpecificVehicleProperties();

       bool Drive();

       bool SlowDown();

       //还可以定义接口的属性

       string name

       {

              get;

              set;

}

}

45、          在这段代码中应该注意:

(1)        增加abstract关键字之后,Vehicle类就成为了一个抽象类,就不能被实例化了

(2)        有了抽象类之后,就(才)可以定义抽象方法。抽象方法只包含方法的签名。所有的派生类都必须执行这个抽象方法

(3)        在一个基类的所有派生类中,重写后的(抽象)方法唯一必须相同的是参数的数量和类型,以及返回值

(4)        在派生类中重写基类抽象方法时也要使用override关键字!这是因为:抽象方法也是虚拟方法!

(5)        在接口中:使用interface关键字代替了class关键字;不需要使用abstract关键字,因为接口本身就是抽象的;不需要使用public关键字,因为接口在使用时必须是可以公开访问的;接口定义中只有抽象方法,但不必写abstract关键字

(6)        接口的命名规则:在接口名称前加一个大写的I字母,表示这时一个接口

(7)        接口可以有属性,getset关键字分别代表了可读和可写

(8)        不要把过多的功能放在一个接口中,因为任何使用它的派生类都必须执行其所有的方法和属性!

(9)        接口可以继承自其他接口,因此可以构成一个接口层次结构,其中基接口提供一般的功能,派生的接口提供比较特殊的功能

46、          对象类型隐式转换:可以将已有派生类实例隐式转换为其基类实例,但转换后的基类实例仍是“派生类”类型

47、          隐式转换代码示例:

//现在假设FlyingCar类是已经定义好的Car类的派生类

FlyingCar MyFlyingCar=new FlyingCar(1234567890);

Car MyNewCar=MyFlyingCar;  //进行隐式转换

Response.Write(MyNewCar.GetType().FullName);

//得到的结果如下:

ASP.test_aspx+FlyingCar

48、          在这段代码中应注意:

(1)        图示:隐式转换的过程

MyFlyingCar

MyNewCar

 

 

 

 

 


(2)        注意:转换后的MyNewCar对象仍是FlyingCar类型

(3)        转换后的MyNewCar对象不能访问FlyingCar类中独有的方法和属性了,只能访问Car类中的方法和属性

49、          对象类型显式转换:在对象实例前边加上要转换的对象类型,用括号括起来

50、          只可以对以前显式转换为其基类的派生类进行转换操作

51、          显式转换代码示例:

FlyingCar MyRecoveredFlyingCar=(FlyingCar)MyNewCar;

//下面是一段错误的代码,它强迫基类对象直接转换为派生类对象

//这会产生一个错误

Car MyNewCar=new Car(1234567890);

FlyingCar MyFlyingCar=(FlyingCar)MyNewCar;

//注意:以上代码是错误的!

//下面的代码使用“替换芯儿”的方式将一个基类对象转换成其一个派生类对象

BaseClass BCObj=new BaseClass();

BCObj.propertyA=ValueA;

BCObj.propertyB=ValueB;

 

DerivedClass DCObj=new DericedClass();

//下面两行是“替换芯儿”的具体过程

DCObj.propertyA=BCObj.propertyA;

DCObj.propertyB=BCObj.propertyB;

52、          在这段代码中应注意:

(1)        MyNewCar本身就是FlyingCar类型,由于转换为Car类型而缩小了范围,现在只是把范围再扩大回原来的样子

(2)        现在的对象引用重新设置为指向外部,包括FlyingCar对象

(3)        不能迫使基类对象直接解释为派生类对象,即使在显式转换中,这也是不允许的!这虽然没有丢失数据,但是会得到没有分配内存的信息。无法把派生类独有的方法和属性添加到试图重新定义的基类实例上!

(4)        有一种方法可以把基类对象转换为派生类对象,即创建一个派生类对象,然后逐个给所有的(基类定义的)属性赋予已有(待转换)基类对象的相应属性值!这是一种“替换芯儿”的方式!参考前面的矩形图!

53、          装箱与拆箱:值类型和引用类型之间的相互转化

54、          装箱与拆箱的代码示例:

<%@ Page Language="C#" runat=server%>

<script runat=server>

       void Page_Load()

       {

              //装箱过程

int i=100;

              object o=i;

              Response.Write("<br>初始化时i的值是:"+i+"<br><br>");

              Response.Write("i装箱后,o的类型是:"+o.GetType().FullName);

              Response.Write("<br><br>");

              i=200;

              //拆箱过程

              int j=(int)o;

              Response.Write("修改后i的值为:"+i+"<br><br>");

              Response.Write("o拆箱后的值为:"+j);

       }

</script>

//输出后的ij的结果为:i=200j=100

55、          在这段代码中应注意:

(1)        术语“装箱”用于描述把值类型转换为引用类型的过程,特别是Object类型

(2)        装箱过程:在编译期间分配给int型的内存在堆栈上,而在运行期间分配给对象的内存在堆上。在装箱一个变量时,实际上是把它从堆栈上提取出来,制作一个拷贝,再把副本像对象一样包装放到堆上,而在堆栈中留下对象的一个引用

(3)        原来的int对象还在,没有被改变,只是做了一个拷贝装箱了而已

(4)        拆箱需要进行显式转换

56、          用户定义的类型转换(没弄明白)

原创粉丝点击