继承和多态 复习
里氏替换原则(lsp)
•Person p = new Teacher();
子类可以隐式的转换成父类.
•
•Teacher t=(Teacher)p;
父类必须强转成子类
•简单工厂
•is和as
–typeA is type B 仅判断A是否是B类型,或者 是否是B类型的子类
–typeA as TypeB 先判断,再转换(如果转换失败,则直接给null值,但不会报错)
多态——隐藏基类方法与多态的实现
•子类可以有与父类方法名相同的方法
–参数不同(重载)
–参数相同(隐藏基类方法)
•子类可以重写父类方法
–虚方法 virtual
–重写方法 override
–重写基类方法一样可以调用基类方法(基类方法仍然存在于对象中,只是不能访问而已)
多态就是对象可以表现多个类型的能力
虚方法virtual
•虚方法可以给父类中的方法一个实现,比如ToString()方法
•虚方法必须有实现部分,抽象方法没有提供实现部分
•问:员工类、部门经理类(员工有上班打卡的方法,考虑该方法是abstract还是virtual的)
虚方法和抽象方法的区别
虚方法必须有实现,抽象方法必须没有实现
抽象方法必须在抽象类中声明,虚方法可以出现在抽象类中
抽象方法必须在子类中重写,虚方法可以被重写
实现多态的主要手段
1.虚方法virtual
2.抽象方法abstract
3.接口。
关于虚方法需要注意的几点:
1.父类中如果有方法需要让子类重写,则可以将该方法标记为virtual
2.虚方法在父类中必须有实现,哪怕是空实现。
3.虚方法子类可以重写(override),也可以不重写。
多态——抽象方法与抽象类
•抽象类与抽象方法由abstract修饰
•abstract的使用注意
–抽象方法没有方法体
–抽象成员只能存在于抽象类中
–抽象类可以有非抽象成员
–抽象类的派生类必须实现抽象方法体
–抽象类只能用作基类,无法实例化
•
多态:为了程序的可扩展性。
•开放封闭原则(对修改封闭,对扩展开放。)
•ToString()方法
•多态就是指不同对象收到相同消息时,会产生不同行为
–同一个类在不同的场合下表现出不同的行为特征
•多态的作用:把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。
•里氏替换原则
–父类引用指向子类对象Person p=new Chinese();
–父类对象不能够替换子类Chinese c=new Person();(×)
•is-a:可以用来验证继承关系中是否合理。(can do接口*)
•if(obj is 类型A)//obj是父类类型对象,”类型A”是子类类型。
•关键字as (类型转换)、 is
=======================as====================
//如果转换成功则将结果赋值给cn变量,
//如果转换失败也不报错,会将null值赋值给cn;
//最好在转换后,加一个cn是否为null的验证。
Chinese cn = per as Chinese;
if (cn!=null)
{
//做一些事情
}
//如果转换失败会报错!!!
Chinese cn1 = (Chinese)per;
============================================================================
/// <summary>
/// 父类类型作为参数的一种多态
/// </summary>
/// <param name="per"></param>
static void RenKouDengji(Person per)
{
per.DengJi();
}
多态的作用:把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。
==============================================
//如果转换失败报异常
//Chinese newcn = (Chinese)per;
// Chinese newcn = per as Chinese;
////类型转换,如果转换成功,则newcn中为转换后的结果
////如果转换失败,则newcn为null
Chinese newcn = per as Chinese;
抽象类abstract
•抽象类不能被实例化。
•抽象类存在的意义:1.抽象类不能被实例化,只能被其他类继承2.继承抽象类的子类必须把抽象类中的所有抽象成员都重写(实现)(除非子类也是抽象类。)3.抽象类就是为了重写→多态。
•什么是抽象类(光说不做)
–不能被实例化的类(不能new)
•抽象类的特点
–见备注
•如:我们的算法父类其实没必要有具体代码实现,可以改成抽象类。
•练习1:动物animal 都有吃eat和叫bark的方法,狗dog和猫cat叫的方法不一样
•练习2:计算形状Shape(圆Circle,矩形Square ,正方形Rectangle)的面积、周长
//1.需要用abstract关键字标记
//2.抽象方法不能有任何方法实现。
//3.抽象成员必须包含在抽象类中。
//4.由于抽象成员没有任何实现,所以子类必须将抽象成员重写。
//5.抽象类不能实例化,既然不能实例化,
//抽象类的作用:抽象类的作用就是为了让子类继承。
//6.抽象类中可以包括抽象成员,可以包括有具体代码的成员。
//7. 还有抽象方法不能用static修饰
public abstract void ShowNationality();
做网站的公司(抽象类) 你给我活我去开发网站,但是网站需要一部分flash,我公司力都是程序员(抽象类中有实现的方法)。没人会做flash(抽象方法),于是我把做flash这部分工作给其它公司去做(重写抽象方法的类)
抽象类定义的是公共的实现和能力
抽象类不能被实例化
抽象类为子类提供所需要的成员
抽象类中的成员既可以有实现也可以无实现
抽象类必须由其子类实现它的抽象成员(除非子类也是抽象类)
一个类只能继承一个抽象类(类的单根继承性)
抽象方法(成员)不能有实现,必须被子类重写override(除非子类也是抽象类)
抽象方法(成员)的只能出现在抽象类中。
多态性的含义:使得能够利用基类的指针来引用不同子类的对象,以及根据所引用对象的不同,以不同的方式执行相同的操作。
多态的作用:把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。(多态)
知识点补充1:关于抽象类知识点补充
•抽象类中的抽象成员不能使用private访问修饰符,可以使用其他访问修饰符。
•常见问题:
–当鼠标放到父类上时,没有自动提示“必须实现父类中的抽象方法”,原因可能是父类不是abstract或,父类中没有abstract成员。
什么时候用虚方法?什么时候用抽象方法?
虚方法:
1.父类中必须有实现。
2.子类中可以不重写。
3.虚方法可以在普通类中。(有序方法的类是可以(可能)被实例化的。)
抽象法:
1.父类中不能有任何实现。
2.子类中必须重写(除非:子类也是一个抽象类。)
3.抽象方法必须在抽象类中。
员工类:
Employee
{
void DaKa();
}
Manager:Employee
{
override void DaKa(){}
}
//什么时候用虚方法:
1.父类本身需要被实例化
2.这个方法在父类中有实现的必要(有意义)(方法有默认的实现。)
//什么时候用抽象方法:
1.在当前系统中,父类绝对不会被实例化。Person p=new Person();
2.在父类中不知道如何去实现这个方法(没有默认的实现。)
多态——小结
•几种建立多态的方式
–用父类实现多态
–用抽象类实现多态
–用接口实现多态
•版本控制
–需要重写方法时使用override关键字
new关键字
new关键字:
1.表示在子类中隐藏了从父类继承过来的那个虚方法。子类类型 obj=new 子类类型();
2.用new标记的方法是子类中的一个全新的方法,与从父类中继承过来的方法没有一点关系(截断了)
3.不能多态了。
//在子类中使用new关键字标记方法的执行结果:
Person person = new American();
person.SayHello(); //调用的父类的SayHello()方法
//==================================
American us = new American();
us.SayHello();//调用的是子类的SayHello()方法。
======================
class Chinese : Person
{
//new关键字将父类中继承过来的SayHello()隐藏了。
//而下面的这个SayHello()与父类中的SayHello()没有任何半毛钱关系。
//这个不叫方法重写。
//当前类中没有重写父类中的SayHello()方法。
public new void SayHello()
{
Console.WriteLine("子类中的SayHello.");
}
}
接口:规则-完全是为了约束(统一)类的行为
•完全一种约定
•接口就是用来实现的
•语法:
[访问修饰符] interface 接口名
{
// 接口成员定义
}
•接口只有方法、属性、索引和事件的声明
•接口是用来实现的,所有成员默认为public
接口的实现
•接口的实现类似于继承
–补充方法体
•接口是“多继承”
–由于“多继承”,无法保证方法重名的问题
–可以显式实现接口的成员
•<接口名>.方法名(){ /* 方法体 */ }
–显式实现的接口成员必须由接口对象来调用
静态方法和实例方法(定义和调用)