Person p =cornell new studentt();.........① Student t =cornell new studentt();........②

深圳网站建设|C#中易混淆概念:抽象类,抽象方法,深圳网站建设-深圳网站建设公司独占网络
深圳网站建设|C#中易混淆概念:抽象类,抽象方法
在介绍抽象类和抽象方法之前还是先提一下多态的基本概念。
其实在上一篇关于里氏替换原则就已经说明了多态的精髓“子类对象可以替换父类对象的位置,而程序的功能不受影响”。还是来看一段代码吧:
& & class Person
& & & &//定义虚方法以备子类重写,当子类替换父类对象的位置时,可以表现出多态
& & & & public virtual void Run()
& & & & & & Console.WriteLine("我是人,我会跑!");
& & & & public virtual void Say()
& & & & & & Console.WriteLine("我是人,我会说话!");
子类的代码如下:
& & //定义Teacher类继承Person
& & class Teacher:Person
& & & & public override void Run()
& & & & & & Console.WriteLine("我是老师,我必须慢速跑");
& & & & public override void Say()
& & & & & & Console.WriteLine("我是老师,我得说表扬的话!");
& & //定义Student类继承Person
& & class Student : Person
& & & & //子类重写了父类的虚方法
& & & & public override void Run()
& & & & & & Console.WriteLine("我是学生,我会加速跑!");
& & & & public override void Say()
& & & & & & Console.WriteLine("我是学生,我会说英语!");
下面需要一个实现多态的类,代码如下:
& & //实现多态的类
& & class FeatureHuman
& & & & /// &summary&
& & & & /// 这个方法就提现了多态,当传入的是子类的对象的时候,p指向的是子类对象,就可以调用子类重写父类方法后的方法
& & & & /// &/summary&
& & & & /// &param name="p"&父类或者子类对象&/param&
& & & & public void OutPutFeature(Person p)
& & & & & & p.Run();
& & & & & & p.Say();
主体代码和实现多态的方法如下:
&static void Main(string[] args)
& & & & & & FeatureHuman fea = new FeatureHuman();
& & & & & & //人的特点
& & & & & & Person p = new Person();
& & & & & & Program pro = new Program();
& & & & & & fea.OutPutFeature(p);
& & & & & & //学生的特点
& & & & & & Student s = new Student();
& & & & & & fea.OutPutFeature(s);
& & & & & & //老师的特点
& & & & & & Teacher t = new Teacher();
& & & & & & fea.OutPutFeature(t);
& & & & & & Console.ReadKey();
& & & & }&
运行,打印结果如下:
这里可以发现,我们outputFeature方法根据传入的实体对象不同(父类变量指向了子类的对象),而打印出了不同人物的特点,这就是多态。
代码图解如下:
多态总结如下:
二,抽象类和抽象方法
在C#中使用abstract关键字修饰的类和方法,叫做抽象类和抽象方法。
1)抽象类中可以拥有没抽象成员,为了继承给他的子类调用 (抽象类就是为了定义抽象成员,继承给子类去实现,同时子类也可以调用父类的非抽象成员)
abstract &class Person
& & & &//private int nA
& & & &//abstract string strN
& & & &//抽象类可以包含不抽象的成员,可以给继承的子类使用
& & & &public void Say()
& & & & & &Console.WriteLine("我是父类,我是人!");
& & & &public virtual void Sing()
& & & & & &Console.WriteLine("我是父类,我是人,我可以唱歌!");
& & & &//Run的抽象方法
& & & &public abstract void Run();
2)抽象类中可以有virtual修饰的虚方法
如上面的代码,在抽象类中定义了virtual修饰的方法,编译通过。抽象类就是为了定义抽象成员,继承给子类去实现,所以子类也可以实现抽象类中的虚方法。
3)抽象类不能实例化,因为有抽象成员,而抽象成员没有方法体,如下图,
4)抽象成员不能私有,如果私有子类没有办法访问&
我们可以在抽象类中定义私有成员,但是没有意义。因为子类根本访问不到这些私有成员,而抽象类本身也不能实例化,所以私有成员访问不到。
5)子类必须重写父类的抽象方法&
在上面代码的基础上,我们定义一个Student类,继承抽象类,但是不实现抽象类的抽象方法,编译报错。代码如下:
6)在子类中没有办法通过base关键字调用父类抽象方法&
原理同上,抽象类的抽象发放没有实现语句,就算调用也没有意义。但是可以使用base关键字调用非抽象方法,代码如下:
class Program
& & & & static void Main(string[] args)
& & & & & & //Person p = new Person();
& & & & & & Student s = new Student();
& & & & & & s.Run();
& & & & & & Console.ReadLine();
& & class Student : Person
& & & & public override void Run()
& & & & {&
& & & & & & base.Say();
& & & & & & Console.WriteLine("我是学生,继承了父类,我可以跑!");
1)抽象方法必须定义在抽象类中,
&class Student : Person
& & & & public abstract void Swiming();
& & & & public override void Run()
& & & & {&
& & & & & & base.Say();
& & & & & & Console.WriteLine("我是学生,继承了父类,我可以跑!");
2)抽象方法必须使用关键字修饰,示例代码如下:
abstract class Person
& & & & //private int nA
& & & & //abstract string strN
& & & & //抽象类可以包含不抽象的成员,可以给继承的子类使用
& & & & public void Say()
& & & & & & Console.WriteLine("我是父类,我是人!");
& & & & public virtual void Sing()
& & & & & & Console.WriteLine("我是父类,我是人,我可以唱歌!");
& & & & //Run的抽象方法,不能有方法体,留给子类实现
& & & & public abstract void Run();
抽象方法思维导图总结如下:
&那么什么时候使用抽象类呢?
①子类必须重写父类的方法(相当于定义了一个标准,规范)
②父类没有必要实例化,就用抽象类
③抽象类是为了继承,为了多态
最后来看一个示例代码:
定义一个抽象类,其中包含抽象方法Run()
&abstract class Person
& & {//Run的抽象方法,只要是继承我的子类都要实现这个方法
& & & & public abstract void Run();
分别定义两个子类,继承抽象类Person
class Student : Person
& & & & //public abstract void Swiming();
& & & & public override void Run()
& & & & {&
& & & & & &// base.Say();
& & & & & & Console.WriteLine("我是学生,继承了父类,我可以跑!");
& & class Worker:Person
& & & & public override void Run()
& & & & & & Console.WriteLine("我是工人,继承了父类,我每天在厂区跑!");
为了表现多态,我们编写一个方法如下:
&//该方法变现了多态,根据需要返回子类的对象
& & & & public static Person GetEntity(string str)
& & & & & & if(str=="学生")
& & & & & & {
& & & & & & & & return new Student();
& & & & & & }
& & & & & & else if(str=="工人")
& & & & & & {
& & & & & & & & return new Worker();
& & & & & & }
& & & & & &
& & & & }&
main函数中的代码如下:
&static void Main(string[] args)
& & & & & & //不直接实例化父类对象,只是声明一个父类对象的变量来接收方法的返回值
& & & & & & Person p = GetEntity(Console.ReadLine());
& & & & & & p.Run();
& & & & & & Console.ReadLine();
& & & & }&四、类和对象2
主要内容:Java类的继承、方法的重写、覆盖、访问控制、super 关键字、多态性及其应用
要求:Java只支持单继承,不允许多重继承
一个子类只能有一个父类
一个父类可以派生出多个子类
子类继承了父类,就继承了父类的方法和属性。
在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和方法。
因而,子类通常比父类的功能更多。
在Java 中,继承的关键字用的是&extends&,即子类是对父类的&扩展&。
注意:子类不能继承父类中私有的(private)成员变量和方法。
2.访问控制
3.方法重写
在子类中可以根据需要对从父类中继承来的方法进行改造重写方法,在程序执行时,子类的方法将覆盖父类的方法。
方法重写必须和被重写的方法具有相同的方法名、参数列表和返回值类型。
重写方法不能使用比被重写方法更严格的访问权限。
4.super关键字
在Java类中使用super关键字来引用父类的成分
super可用于访问父类中定义的属性
super可用于调用父类中定义的成员方法
super可用于在子类构造方法中调用父类的构造方法
示例:public class Person {
&&&&private S
&&&&public String getInfo() {
&&&&&&&&return "Name: " + name + "\nage: " +
public class Student extends Person {
&&&&private String school = "New Oriental";
&&&&public String getSchool(){
&&&&public String getInfo() {
&&&&&&&&&调用父类的方法
&&&&&&&&return super.getInfo() +"\nschool: " +
补充:super与父类构造方法的调用
子类继承父类所有的成员变量和成员方法,但不继承父类的构造方法
在子类的构造方法中可使用super(参数列表)形式的语句调用父类的构造方法
如果子类的构造方法中没有显示地调用父类构造方法,也没有使用this关键字调用重载的其它构造方法,则系统默认调用父类无参数的构造方法
如果子类构造方法中既未显式调用父类构造方法,而父类中又没有无参的构造方法,则编译出错
多态:子类的对象可以替代父类的对象使用
一个变量只能有一种确定的数据类型
一个引用类型变量可能指向(引用)多种不同类型的对象
Object o = new Person();Object类型的变量o,指向Person类型的对象
o = new Student(); Object类型的变量o,指向Student类型的对象
父类类型的变量可以指向子类的对象
Person p = new Student();
一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法。
Student s = new Student();
s.school = &hp&;
Person p = new Student();
p.school = &hp&; 非法
正常的方法调用
&&& Person p = new Person();
&& p.getInfo();
&& Student s = new Student();
&&& s.getInfo();
&&动态绑定
&& Person p = new Student();
&&&& p.getInfo(); 调用Student类的getInfo()方法
&&在执行期间(而非编译期间)判断所引用对象的实际类型,根据其实际类型调用其相应的方法。
方法声明的形参类型为父类类型,可以使用子类的对象作为实参调用该方法
public class Test{
public void method(Person p) {
&&&&&&&&&&&&&
&&&&&&&&&&&p.getInfo();
public static &void main(Stirng args[]){
&&&&&&&&&&&Test t = new Test();
&&&&&&&&&&&Student s = new Student();
&&&&&&&&&&&t.method(s); 子类的对象s传送给父类类型的参数p
6.instanceof操作符
x instanceof A:检验x是否为类A的对象,返回值为boolean型。
要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
如果x属于类A的子类B,x instanceof A值也为true。
public class Person extends Object {&}
public class Student extends Person {&}
public class Graduate extends Person {&}
-------------------------------------------------------------------
public void method1(Person p) {
&&&&if (p instanceof Person)
&&&&&&&&&处理Person类及其子类对象
&&&&if (p instanceof Student)
&&&&&&&&处理Student类及其子类对象
&&&&if (p instanceof Graduate)
&&&&&&&&处理Graduate类及其子类对象
7.对象类型转换
基本数据类型的转换:
小的数据类型可以自动转换成大的数据类型
如long g=20; &&&&&&&&&&double d=12.0f
可以把大的数据类型强制转换成小的数据类型
如 floate f=(float)12.0 &&int a=(int)1200L
对Java对象的强制类型转换
从子类到父类的类型转换可以自动进行
从父类到子类的类型转换必须通过强制类型转换实现
无继承关系的引用类型间的转换是非法的
在造型前可以使用instanceof操作符测试一个对象的类型
public class Test{
&&&&public void method(Person p) { &
&&&&&&&&System.out.pritnln(p.getschool()); &&不推荐
&&&&&&&&if(p &instanceof &Student){ &&&推荐
&&&&&&&&&&&&Student me = (Student)p;
&&&&&&&&&&&&System.out.pritnln(me.getschool());
&&&&&&&&} &&&&
&&&&public static &void main(Stirng args[]){
&&&&&&&&Test t = new Test();
&&&&&&&&Student s = new Student();
&&&&&&&&t.method(s);
五、类和对象3
主要内容:static 关键字、final 关键字、抽象类、接口、内部类
(一)static关键字:
静态的,可以用来修饰属性、方法、代码块(或初始化块)、内部类
static修饰属性(类变量):
&1.由类创建的所有的对象,都共用这一个属性
&2.当其中一个对象对此属性进行修改,会导致其他对象对此属性的一个调用。vs 实例变量(非static修饰的属性,各个对象各自拥有一套副本)
&3.类变量随着类的加载而加载的,而且独一份
&4.静态的变量可以直接通过&类.类变量&的形式来调用
&5.类变量的加载是要早于对象。所以当有对象以后,可以&对象.类变量&使用。但是"类.实例变量"是不行的。
&6.类变量存在于静态域中。
&&static修饰方法(类方法):
&&1.随着类的加载而加载,在内存中也是独一份
&&2.可以直接通过&类.类方法&的方式调用
&&3.内部可以调用静态的属性或静态的方法,而不能调用非静态的属性或方法。反之,非静态的方法是可以调用静态的属性或静态的方法
&& &静态的方法内是不可以有this或super关键字的!
&&注:静态的结构(static的属性、方法、代码块、内部类)的生命周期要早于非静态的结构,同时被回收也要晚于非静态的结构
public static void main(String[] args){
&&&&&方法体 &&&&
1.main()是一个方法,是主方法,为程序的入口
2.权限修饰符:public protected 缺省 private ---面向对象的封装性
3.对于方法来讲:static final abstract
4.方法的返回值:void / &具体的返回值类型(基本的数据类型 & 引用数据类型),方法内部一定要有return
5.方法名:命名的规则:xxxYyyZzz。给方法命名时,要见名之意
6.形参列表:同一个方法名不同的形参列表的诸多个方法间构成重载。 &&形参 & 实参---方法的参数传递机制:值传递
7.方法体:方法定义的是一种功能,具体的实现由方法体操作。
是类的第4个成员
作用:用来初始化类的属性
分类:只能用static来修饰。
&&静态代码块:
&&1.里面可以有输出语句
&&2.随着类的加载而加载,而且只被加载一次
&&3.多个静态代码块之间按照顺序结构执行
&&4.静态代码块的执行要早于非静态代码块的执行。
&&5.静态的代码块中只能执行静态的结构(类属性,类方法)
&&非静态代码块:
&&1.可以对类的属性(静态的 & 非静态的)进行初始化操作,同时也可以调用本类声明的方法(静态的 & 非静态的)
&&2.里面可以有输出语句
&&3.一个类中可以有多个非静态的代码块,多个代码块之间按照顺序结构执行
&&4.每创建一个类的对象,非静态代码块就加载一次。
&&5.非静态代码块的执行要早于构造器
&&关于属性赋值的操作:
①默认的初始化
②显式的初始化或代码块初始化(此处两个结构按照顺序执行)
③构造器中;
&&&&&&&&&以上是对象的属性初始化的过程&&&&&&&&&&&&&
④通过方法对对象的相应属性进行修改
(二)final关键字
&&final:最终的 ,可以用来修饰类、属性、方法
&&1.final修饰类:这个类就不能被继承。如:String类、StringBuffer类、System类
&&2.final修饰方法:不能被重写。如:Object类的getClass()
&&3.final修饰属性:此属性就是一个常量,一旦初始化后,不可再被赋值。习惯上,常量用大写字符表示。
&&此常量在哪里赋值:①此常量不能使用默认初始化 ②可以显式的赋值、代码块、构造器。
&&变量用static final修饰:全局常量。比如:Math 类的PI
&&&与finally finalize()区分开
final int I = 12;
final double PI;
final String NAME;
public void m1(){
System.out.println(I);
// I = 10;
PI = 3.14;
public D(){
NAME = "DD";
public D(String name){
(三)抽象类
abstract:抽象的,可以用来修饰类、方法
&&1.abstract修饰类:抽象类
&&1)不可被实例化
&&2)抽象类有构造器 (凡是类都有构造器)
&&3)抽象方法所在的类,一定是抽象类。
&&4)抽象类中可以没有抽象方法。
&&&当我们设计一个类,不需要创建此类的实例时候,就可以考虑将其设置为抽象的,由其子类实现这个类的抽象方法以后,就行实例化
&&2.abstract修饰方法:抽象方法
&&1)格式:没有方法体,包括{}.如:public abstract void eat();
&&2)抽象方法只保留方法的功能,而具体的执行,交给继承抽象类的子类,由子类重写此抽象方法。
&&3)若子类继承抽象类,并重写了所有的抽象方法,则此类是一个"实体类",即可以实例化
&&4)若子类继承抽象类,没有重写所有的抽象方法,意味着此类中仍有抽象方法,则此类必须声明为抽象的!
补充:抽象类和接口的区别
1.抽象类里可以有构造方法,而接口内不能有构造方法。
2.抽象类中可以有普通成员变量,而接口中不能有普通成员变量。
3.抽象类中可以包含非抽象的普通方法,而接口中所有的方法必须是抽象的,不能有非抽象的普通方法。
4.抽象类中的抽象方法的访问类型可以是public ,protected和默认类型,但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。
5.抽象类中可以包含静态方法,接口内不能包含静态方法。
6.抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static类型,并且默认为public static类型。
7.一个类可以实现多个接口,但只能继承一个抽象类。
8.接口更多的是在系统框架设计方法发挥作用,主要定义模块之间的通信,而抽象类在代码实现方面发挥作用,可以实现代码的重用。
(四)接口
&接口(interface) &是与类并行的一个概念
&&1.接口可以看做是一个特殊的抽象类。是常量与抽象方法的一个集合,不能包含变量、一般的方法。
&&2.接口是没有构造器的。
&&3.接口定义的就是一种功能。此功能可以被类所实现(implements)。
&&比如:class CC extends DD implements AA
&&4.实现接口的类,必须要重写其中的所有的抽象方法,方可实例化。若没有重写所有的抽象方法,则此类仍为一个抽象类
&&5.类可以实现多个接口。----java 中的类的继承是单继承的
&&6.接口与接口之间也是继承的关系,而且可以实现多继承
&&&5,6描述的是java中的继承的特点。
&&7.接口与具体的实现类之间也存在多态性
(五)内部类
&类的第5个成员:内部类
&&1.相当于说,我们可以在类的内部再定义类。
外面的类:外部类。 &&&&&&&&里面定义的类:内部类
&&2.内部类的分类:成员内部类(声明在类内部且方法外的) &vs 局部内部类(声明在类的方法里)
&&3.成员内部类:
&& 3.1是外部类的一个成员:①可以有修饰符(4个)②static final ③可以调用外部类的属性、方法
&& 3.2具体类的特点:①abstract ②还可以在其内部定义属性、方法、构造器
&&4.局部内部类:
&&5.关于内部类,掌握三点:
&&&&①如何创建成员内部类的对象(如:创建Bird类和Dog类的对象)
&&&&②如何区分调用外部类、内部类的变量(尤其是变量重名时)
③局部内部类的使用
总结及补充:
面向对象的三大特性:
&&封装性:
① 通过私有化类的成员变量,通过公共的getter和setter方法来调用和修改
&&&&&&&&② 还可以对类的其他结构进行&封装&
&&&&&&&&③ 权限修饰符:public protected 缺省 private
&&继承性:
通过让一个类A继承另一个类B,就可以获取类B中的结构(主要的:属性、方法、构造器)。子类:类A &父类:类B
&&&&&&&&&java中的类的继承性:单继承的。
&&多态性:
&&&&&&&&①体现:方法的重载与重写 &; 子类对象的多态性 &Person p = new Student();
&&&&&&&&②子类对象多态性的使用:虚拟方法调用。
&&&&&&&&③向上转型 &向下转型 Student s = (Student)p; &//建议在向下转型之前: if ( p instanceof Student)避免出现ClassCastException的异常
其它关键字:
&&&&1. this:修饰属性、方法、构造器 &。表示:当前对象或当前正在创建的对象
&&&&2. super:修饰属性、方法、构造器。显式的调用父类的相应的结构,尤其是子父类有重名的方法、属性
&&&&3. static : &修饰属性、方法、代码块、内部类。随着类的加载而加载!
&&&&4. final:修饰类、属性、方法。表示&最终的&
&&&&5. abstract : 修饰类、方法
&&&&6. &interface:表示是一个接口,(接口是与类并列的一个结构)。类与接口之间同时&implements&发生关系。
&&&&7. package import 。。。
&abstract不能修饰属性、构造器、不能与final static private共用。
阅读(...) 评论()解析里氏替换原则,虚方法
这一系列的文章在园子里还是比较受欢迎的。有一些留言指出了其中理论性的错误,还有问自己是否毕业,怎么写出来这些文章的,有没有培训过等等问题。
下面就一并的回答这些问题吧。
1)自己今年六月份毕业,现在在帝都实习。不过在学校已经做过一些C#开发了,现在也是做.NET开发工作。
2)文章中很多知识是自己以前在网上下载的视频教程,学习过程中所记的笔记。也就是在年前的时候,突然有一天发现自己的笔记本记了差不多块一本了,之前也没时间整理过,所以就想着把它们整理成博客文章,顺便温习一下这些笔记知识。
3)有园友问自己是不是在传智培训过。首先说我没有培训过,但是非常感谢传智公开的一些自学教程。因为自己也是这些视频的受益者,学到了很多知识,养成了一些好的学习习惯。
4)在整理笔记的过程中遇到了很多问题,其中自己参考了《C#本质论》,《CLR via C#》还有就是MSDN的官方文档。
3)不管怎样还是会遇到一些自己解决不掉或者弄不清楚的问题,这个过程使用了Google搜索并和请教了一些园友。
4)错误总是会存在。谢谢看我博客的读者你们的细心,指出了我博文中的错误。确定这些错误后,我都立即修改了自己的文章。
今天开始上班了。这几天研究学习了一下思维导图,感觉用它整理自己的知识非常的方便。所以,以后写博客完成一个知识点,都会用思维导图做一个总结。也能让大家对所要读的内容有一个整体的把握。
我用的思维导图软件是FreeMind(免费的,但是得装JDK),因为刚开始学习使用,很多操作技巧不是很熟练,做出来的导图估计也不是很好,希望大家见谅。
首先,里氏替换原则。
这是理解多态所必须掌握的内容。对于里氏替换原则维基百科给出的定义如下:
为什么子类可以替换父类的位置,而程序的功能不受影响呢?
当满足继承的时候,父类肯定存在非私有成员,子类肯定是得到了父类的这些非私有成员(**假设,父类的的成员全部是私有的,那么子类没办法从父类继承任何成员,也就不存在继承的概念了)**。既然子类继承了父类的这些非私有成员,那么父类对象也就可以在子类对象中调用这些非私有成员。所以,子类对象可以替换父类对象的位置。
来看下面的一段代码:
class Program
static void Main(string[] args)
Person p = new Person();
Person p1 = new Student();
Console.ReadKey();
class Person
    //父类的私有成员
    private int nA
public Person()
Console.WriteLine("我是Person构造函数,我是一个人!");
public void Say()
Console.WriteLine("我是一个人!");
class Student : Person
public Student()
Console.WriteLine("我是Student构造函数,我是一个学生!");
public void SayStude()
Console.WriteLine("我是一个学生!");
class SeniorStudent : Student
public SeniorStudent()
Console.WriteLine("我是SeniorStudent构造函数,我是一个高中生!");
void SaySenior()
Console.WriteLine("我是一个高中生!");
我们运行打印出的结果是:
根据前面的构造函数的知识很容易解释这个结果。那么我们在Main()函数中添加如下的代码:
static void Main(string[] args)
Person p = new Person();
Person p1 = new Student();
Console.ReadKey();
在访问的过程中,可以发现p只可以访问父类的say
而p1也只可以访问父类的Say方法
其实在上面的代码中,就满足了里氏替换原则。子类的Student对象,替换了父类Person对象的位置。
那么它们在内存中发生了些什么呢?如下图:
由上可以知道,当一个父类的变量指向一个子类对象的时候只能通过这个父类变量调用父类成员,子类独有的成员无法调用。
同理我们可以推理出,子类的变量是不可以指向一个父类的对像的
但是当父类变量指向一个子类变量的时候,可以不可以把父类的变量转化成子类的对象呢?看下图
关于引用类型的两种转换方式:
由上面的代码我们已经知道了一种转换,就是在变量钱直接加需要转换的类型,如下代码:
Student s2 = (Student)p1;
那么第二种转换方式就是使用as关键字,如下代码:
//将指向子类对象的变量转化成子类类型
Student s2 = (Student)p1;
//使用as关键字,转换失败返回一个null值
Student s3 = p1 as S
使用as关键字和第一种强制转换的区别就是,第一种如果转换失败会抛异常,第二种转换失败则返回一个null值。
思维导图总结如下:
二,虚方法
使用virtual关键字修饰的方法,叫做虚方法(一般都是在父类中)。
看下面的一段代码:
class Person
private int nA
public Person()
Console.WriteLine("我是Person构造函数,我是一个人!");
//这里定义了一个虚方法
public virtual void Say()
Console.WriteLine("我是一个人!");
class Student : Person
//子类使用override关键字改写了父类的虚方法
public override void Say()
Console.WriteLine("我是一个学生!");
public Student()
Console.WriteLine("我是Student构造函数,我是一个学生!");
public void SayStude()
Console.WriteLine("我是一个学生!");
紧接着在main()函数中添加如下的代码:
static void Main(string[] args)
Person p = new Person();
Person p1 = new Student();
Student s = new Student();
Console.ReadKey();
打印结果如下:
我们很明显的可以发现,第二个表达式满足里氏替换原则,p1.Say()执行的应该是父类的Say()方法,但是这里却执行了子类的Say()方法。
这就是子类使用override关键字的Say()方法覆盖了父类的用Virtual关键字修饰的Say()方法。
我们使用动态图片看一下调试过程,
①首先是没有使用任何关键字:
由上可以看出直接跳入父类,执行了父类的Say()方法;
②再看使用virtual和override关键字的动态调试图片,如下:
可以看到直接到子类去执行override关键字修饰的Say()方法。
那么如果父类使用virtual关键字修饰,而子类没有重写该方法时会怎么样呢?如下面的代码:
class Program
static void Main(string[] args)
Person p1 = new Student();
Console.ReadKey();
class Person
private int nA
public Person()
Console.WriteLine("我是Person构造函数,我是一个人!");
//这里定义了一个虚方法
public virtual void Say()
Console.WriteLine("我是一个人!");
class Student : Person
//子类中没有出现override关键字修饰的方法
public void SayStude()
Console.WriteLine("我是一个学生!");
执行结果如下:
所以,如果子类找不到override方法,则会回溯到该子类的父类去找是否有override方法,知道回溯到自身的虚方法,并执行。
虚方法知识总结的思维导图如下:}

我要回帖

更多关于 unit2 a new student 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信