[.net 面向对象编程基础] (15) 抽象类

[.net 面向对象编程基础] (15) 抽象类

前面我们已经使用到了虚方法(使用 Virtual修饰符)和抽象类及抽象方法(使用abstract修饰符)我们在多态一节中说到要实现类成员的重写必须定义为一个虚方法或抽象方法。这节单独把抽象类提出来,是因为抽象是.net实现面向对象编程重要的重要思想,定义抽象类就象一个模板一个提纲,它可以理解为中央的指导思想,我们通过派生类去具体实现它。由此可见,抽象类本身没有任何作用,也不能被实例化,因为他本身就不具有具体的事物。比如上节的动物类的例 子,我们实例化一个动物类并没有意义,我们需要实例的类是具体的动物,比如Dog、Cat、Sheep这些。

而我们使用面向对象编程,就要在具体的应用过程中,学会抽象出事物的共同点,形成一个基类,其它类去继承他,实现他。为我们提高编程、学习设计模式很有益处。

1.什么是抽象类

如果一个类不与具体的事物相联系,而只是表达一种抽象的概念,仅仅是作为其派生类的一个基类,这样的类就是抽象类,在抽象类中声明方法时,如果加上abstract时就是抽象方法

1.抽象类的声明

抽象类使用abstract 关键词进行声明。

2.抽象类的特点:

A.抽象类不能被实例化

B.抽象类是一种特殊的类,具有类的特性,其成员可以是抽象的,也可以不是抽象的。

C.抽象方法必须定义在抽象类中,而不能定义在非抽象类中

D.抽象类可以继承一个抽象类,其派生类仍然是一个抽象类

E.抽象类不能被密封(不能使用sealed(sealed指该类不可被继承))

3.抽象方法的特点:

A.抽象方法是隐式的虚方法

B.抽象方法只允许声明在抽象类中

C.抽象方法不能提供实际的实现,所以没有方法体;抽象方法的实现是在非抽象的派生类中以override重写实现的

D.抽象方法声明中不可以使用static或者virtual修饰符

E.abstract关键字不能修饰静态方法或静态属性

6.抽象类的构造函数

A.不要再抽象类中定义public或protected internal访问权限的构造函数

B.应在抽象类中定义protected或private访问权限的构造函数

C.如果在抽象类中定义一个protected构造函数,则在实例化派生类时,基类可以执行初始化任务

7.抽象方法和虚方法的区别

虚方法(使用Virtual修饰符)有实现部分,并且派生类对其重写是可选的;抽象方法没有实现部分,并且强制非抽象派生类对其重写

8.抽象类和接口的区别

其实这个区别,本来是要在下一节,接口中讲的,在这里提前说一下,有点印象也好。

A.它们的派生类只能继承一个基类,即只能继承一个抽象类,但是可以继承多个接口。

B.抽象类中可以定义成员的实现,但接口中不可以。

C.抽象类中包含字段、构造函数、析构函数、静态成员或常量等,接口中不可以。

D.抽象类中的成员可以私有的(只要不是抽象的)、受保护的、内部的或受保护的内部成员,但接口中的成员必须是公共的。

PS:抽象类和接口这两种类型用于完全不同的目的。抽象类主要用作对象系列的基类,共享某些主要特性,例如共同的目的和结构。接口则主要用于类,这些类在基础水平上有所不同,但仍然可以完成某些相同的任务。

7. 举例

我们举一个例子来说明一下抽象类的使用,还是我们前面的例子,基类动物(Animal),我们定义成一个抽象类,有几个派生类狗、猫、羊,假如我们猫还有波斯猫,狗还有牧羊犬,那么我们还可以让它们再次继承,因为猫和狗本身也是动物,我们可以不定义为抽象类,让他们支持实例化。下面看一下UML图:

关于UML图我会在后面抽时间写的,里面的成员+表示公有,-表示私有,字段、属性、方法使用分割线

下面是具体实现代码:

  1 /// <summary>
  2 /// 动物类(父类 抽象类)
  3 /// </summary>
  4 abstract class Animal
  5 {
  6     /// <summary>
  7     /// 名字
  8     /// 说明:类和子类可访问
  9     /// </summary>
 10     protected string name;
 11
 12
 13     /// <summary>
 14     /// 构造函数
 15     /// </summary>
 16     /// <param name="name"></param>
 17     public Animal(string name)
 18     {
 19         this.name = name;
 20     }
 21
 22     private int shoutNum = 3;
 23     public int ShoutNum
 24     {
 25         get { return shoutNum; }
 26         set { shoutNum = value; }
 27     }
 28
 29     /// <summary>
 30     /// 名字(虚属性)
 31     /// </summary>
 32     public virtual string MyName
 33     {
 34         get { return this.name; }
 35
 36     }
 37
 38     /// <summary>
 39     /// 叫声,这个方法去掉虚方法,把循环写在这里
 40     /// </summary>
 41     public void Shout()
 42     {
 43         string result = "";
 44         for (int i = 0; i < ShoutNum; i++)
 45             result += getShoutSound()+"!";
 46
 47         Console.WriteLine(MyName);
 48         Console.WriteLine(result);
 49     }
 50
 51     /// <summary>
 52     /// 创建一个叫声的虚方法,子类重写
 53     /// </summary>
 54     /// <returns></returns>
 55     public  virtual string  getShoutSound()
 56     {
 57         return "";
 58     }
 59
 60
 61 }
 62
 63 /// <summary>
 64 /// 狗(子类)
 65 /// </summary>
 66 class Dog : Animal
 67 {
 68     string myName;
 69     public Dog(string name)
 70         : base(name)
 71     {
 72         myName = name;
 73     }
 74
 75     /// <summary>
 76     /// 名字(重写父类属性)
 77     /// </summary>
 78     public override string MyName
 79     {
 80         get { return "我是:狗狗,我叫:" + this.name; }
 81     }
 82
 83     /// <summary>
 84     /// 叫(重写父类方法)
 85     /// </summary>
 86     public override string getShoutSound()
 87     {
 88         return "汪!";
 89     }
 90 }
 91
 92 /// <summary>
 93 /// 狗(子类)
 94 /// </summary>
 95 class ShepherdDog : Dog
 96 {
 97     string myName;
 98     public ShepherdDog(string name): base(name)
 99     {
100         myName = name;
101     }
102
103     /// <summary>
104     /// 名字(重写父类属性)
105     /// </summary>
106     public override string MyName
107     {
108         get { return "我是:牧羊犬,我叫:" + this.name; }
109     }
110
111     /// <summary>
112     /// 叫(重写父类方法)
113     /// </summary>
114     public override string getShoutSound()
115     {
116         return "汪~呜!";
117     }
118 }
119
120 /// <summary>
121 /// 猫(子类)
122 /// </summary>
123 class Cat : Animal
124 {
125     string myName;
126     public Cat(string name)
127         : base(name)
128     {
129         myName = name;
130     }
131     /// <summary>
132     /// 名字(重写父类属性)
133     /// </summary>
134     public override string MyName
135     {
136         get { return "我是:猫咪,我叫:" + this.name; }
137
138     }
139
140     /// <summary>
141     /// 叫(重写父类方法)
142     /// </summary>
143     public override string getShoutSound()
144     {
145         return "喵!";
146     }
147 }
148
149     /// <summary>
150 /// 猫(子类)
151 /// </summary>
152 class PersianCat : Cat
153 {
154     string myName;
155     public PersianCat(string name): base(name)
156     {
157         myName = name;
158     }
159     /// <summary>
160     /// 名字(重写父类属性)
161     /// </summary>
162     public override string MyName
163     {
164         get { return "我是:波斯猫,我叫:" + this.name; }
165
166     }
167
168     /// <summary>
169     /// 叫(重写父类方法)
170     /// </summary>
171     public override string getShoutSound()
172     {
173         return "喵~呜!";
174     }
175 }
176
177
178 /// <summary>
179 /// 羊(子类)
180 /// </summary>
181 class Sheep : Animal
182 {
183     string myName;
184     public Sheep(string name)
185         : base(name)
186     {
187         myName = name;
188     }
189     /// <summary>
190     /// 名字(重写父类属性)
191     /// </summary>
192     public override string MyName
193     {
194         get { return "我是:羊羊,我叫:" + this.name; }
195
196     }
197     /// <summary>
198     /// 叫(重写父类方法)
199     /// </summary>
200     public override string getShoutSound()
201     {
202         return "咩!";
203     }
204 }

 

调用及结果如下:

//调用
Animal persianCat = new PersianCat("好猫");
persianCat.Shout();
Console.ReadLine();
//结果如下:
//我是:波斯猫,我叫:好猫
//喵~呜!!喵~呜!!喵~呜!!

10要点:

抽象类是指:一个类不与具体的事物相联系,而只是表达一种抽象的概念,仅仅是作为其派生类的一个基类。

抽象类是一种特殊类,使用abstract声明

抽象类不可被实例化

抽象类可以继承一个抽象类

抽象方法只能存在于抽象类中,可以和虚方法一样,在派生类中重写。

抽象类的存在就是被继承用的,因此不允许被密封(即不能使用sealed修饰)

最后,抽象,是面向对象程序设计的重要思想。。

==============================================================================================

返回目录 <如果对你有帮助,记得点一下推荐哦,有不明白的地方或写的不对的地方,请多交流>

==============================================================================================

时间: 2024-12-10 07:07:43

[.net 面向对象编程基础] (15) 抽象类的相关文章

[.net 面向对象编程基础] (17) 数组与集合

[.net 面向对象编程基础] (17) 数组与集合 学习了前面的C#三大特性,及接口,抽象类这些相对抽象的东西以后,是不是有点很累的感觉.具体的东西总是容易理解,因此我们在介绍前面抽象概念的时候,总是举的是具体的实例以加深理解. 本节内容相当具体,学起来也相当轻松. 1.数组 1.1 什么是数组? 数组是一种数据结构,包含同一个类型的多个元素. 1.2数组的初始化 string[] mystringArray; 类型+方框号 数组名 1.3数组初始化 我们知道数组是引用类型,所以需要给他分配堆

[.net 面向对象编程基础] (16) 接口

[.net 面向对象编程基础] (16) 接口 关于“接口”一词,跟我们平常看到的电脑的硬件“接口”意义上是差不多的.拿一台电脑来说,我们从外面,可以看到他的USB接口,COM接口等,那么这些接口的目的一就是让第三方厂商生产的外设都有相同的标准,也是提供一个对外通信或操作的入口. 只是C#的接口除了以上特点之外,还具有一种类似于模板的功能,我们定义一组接口,就像是一个模板.这点和抽象类不同,抽象类是先有子类或都子类的概念,从中抽象出一个类.而接口更像是我们要设计一台机器,先把这台机器对外的功能接

[.net 面向对象编程基础] (18) 泛型

[.net 面向对象编程基础] (18) 泛型 上一节我们说到了两种数据类型数组和集合,数组是指包含同一类型的多个元素,集合是指.net中提供数据存储和检索的专用类. 数组使用前需要先指定大小,并且检索不方便.集合检索和声明方便,但是存在类型安全问题,本来使一个类型安全的C#变得不安全了. 集合为了解决数组预设大小的问题,采取了一种自动扩容的办法,这样当大小不够时,他就创建一个新的存储区域,把原有集合的元素复制过来.如此又对性能上也是有很大的影响. 上节我们说到解决这些缺陷的方法,那就是.NET

[.net 面向对象编程基础] (9) 类的成员(字段、属性、方法)

[.net 面向对象编程基础] (9) 类的成员(字段.属性.方法) 前面定义的Person的类,里面的成员包括:字段.属性.方法.事件等,此外,前面说的嵌套类也是类的成员. a.类的成员为分:静态成员(static)和非静态成员 b.静态成员用static标识,不标识则默认为非静态成员 c.静态成员属于类所有,动态成员则属于实例所有,即对象 d.静态成员为类所有实例共享,无论类有多少实例或副本,静态成员只占用存中一块区域.非静态成员则在类的每个实例,都创建一个内存域. 下面主要说明一下类的主要

JAVA学习(五):Java面向对象编程基础

Java面向对象编程基础 面向对象(Object oriented programming,OOP)技术是一种强有力的软件开发方法,它採用数据抽象与信息隐藏技术,来使软件开发简单化,以达到代码重用的目的. 1.OOP的3个特性(封装.继承和多态性) 封装是类的基础.指把类的相关实现细节隐藏起来,在类中将数据和实现操作的代码集中起来放在对象的内部.调用这些类时仅仅需直接使用类预留的接口就能够了. 继承提供了子类自己主动拥有父类数据结构和方法的机制.它表示类之间的一种关系. 多态指使一个对象被看成还

[.net 面向对象编程基础] (22) 事件

[.net 面向对象编程基础] (22)  事件 事件(Event)是学习.net面向对象编程很重要的一部分,在学习事件之前,我们实际上已经在很多地方使用了事件,比如控件的click事件等,这些都是.net设计控件的时候已经定义好的事件.除此之外,我们同样可以自己定义事件. 事件实际上是一种消息机制,当然点击控件时,click就通知处理他的方法去处理,实际上就是前面说的委托.因此我们可以说:事件是一种具有特殊签名的委托.而事件/消息机制是windows的核心,因此我们必须掌握他. 为了更加容易理

[.net 面向对象编程基础] (20) LINQ使用

[.net 面向对象编程基础] (20)  LINQ使用 通过上节LINQ的基础知识的学习,我们可以开始使用LINQ来进行内存数据的查询了,我们上节说了LINQ的定义为:Language Integrated Query(语言集成查询)的简称,它是集成在.NET编程语言中的一种特性. 1.LINQ的构架 从这幅图中,我们可以知道LINQ包括五个部分:LINQ to Objects.LINQ to XML.LINQ to SQL.LINQ to DataSet.LINQ to Entities.

[.net 面向对象编程基础] (14) 重构

[.net 面向对象编程基础] (14) 重构 通过面向对象三大特性:封装.继承.多态的学习,可以说我们已经掌握了面向对象的核心.接下来的学习就是如何让我们的代码更优雅.更高效.更易读.更易维护.当然了,这也是从一个普通程序员到一个高级程序员的必由之路.就看病一样,普通医生只能治标,高级医生不但看好病,还能除病根. 1.什么时重构? 重构(Refactoring)就是在不改变软件现有功能的基础上,通过调整程序代码改善软件的质量.性能,使其程序的设计模式和架构更趋合理,提高软件的扩展性和维护性.

[.net 面向对象编程基础] (7) 基础中的基础——修饰符

[.net 面向对象编程基础] (7) 基础中的基础——修饰符 在进入C#面向对象核心之前,我们需要先对修饰符有所了解,其实我们在前面说到变量和常量的时候,已经使用了修饰符,并且说明了变量和常量的修改符. 首先 修饰符是什么? 修饰符用于限定类型以及类型成员的申明 从定义可以看出按功能分为两类:访问修饰符和声明修饰符 1.访问修饰符 (5个) 访问修饰符 说明 public 公有访问.不受任何限制. private 私有访问.只限于本类成员访问,子类,实例都不能访问. protected 保护访