.Net学习笔记6-C#面向对象基础3 继承多态

1.继承(inherit)//C#为单继承
C#中一个类可以“继承”自其他类,如果A继承自B,则A叫做B的子类,B叫做A的父类(基类)。
子类会从父类继承所有非private成员、方法。子类还可以有子类。构造函数不能继承
继承的最大作用:复用父类的非private方法、行为
C#中一个类只能有一个父类,如果没指定父类,则System.Object为父类。
//如果一个类没有声明父类,则默认继承自system.Object类
所有无父类的类默认继承Object类,所有子类简接继承Object,
Object的 ToString()方法是虚方法(virtual),可以被重写(OverRide)。所有类中都可重载(OverRide)ToString()方法
ToString()方法默认的实现就是把对象的类的全名打印出来,数组的ToString就是"类型[]"

class FuLei
{
  private void method1()
  {
  }   
  public void method2()
  {
  }
}
class ZiLei : FuLei
{
  private void method3()
  {
//method1();//错误的,子类不能调用父类的private方法
method2();//
  }   
  public void method4()
  {
  }
}
ZiLei zl1 = new ZiLei();
zl1.method1();//继承父类的方法
zl1.method2();
zl1.method4();
zl1.toString();
继承中的构造函数调用顺序:
一个类如果没有定义其他构造函数,则有一个默认的无参构造函数
构造函数会默认调用父类的构造函数
class Fu

{

public Fu()

{
Console.WriteLine("fu");
}

}
class ZiLei : FuLei
{
//无其他构造函数时,默认有这个构造函数
//:base()调用父类构造函数,不写也是默认调用父类的无参构造函数
public ZiLei():base()//不管是否显式调用,控制台都会输出fu
{
Console.WriteLine("zi");
}
}
Zi z = new Zi();
执行结果:     fu
            zi
先执行父类的构造函数把父类初始化完成,再初始化子类的。
可以通过base(参数)去访问父类中的有参构造函数。可以通过this(参数...)去访问本类中的其他构造函数。
class Fu
{
public Fu(int a)//已定义有参构造函数,则无默认无参构造函数
{
Console.WriteLine("fu"+a);
}
}
class Zi : Fu
{
//public Zi(){}  不可这么写,因为无默认无参构造函数
public Zi():base(0)
{
Console.WriteLine("zi");
}
public Zi(int a):base(a)//必须显示调用父类的构造函数
{
Console.WriteLine("zi"+a);
}
public ZiLei(string  b,int a) : this(a)
{
Console.WriteLine("zi1"+b);
}
}
Zi z = new Zi();
Zi z1 = new Zi(5);
Zi z1 = new Zi("aaa",10);

执行结果:

 fu0

 zi
 fu5
 zi5
 fu10
 zi10
 zi1aaa
如果定义了构造函数,则类就不会有默认的无参构造函数;
如果父类中没有默认的,则子类构造函数必须显示调用父类的构造函数
2.private/public/protected
private成员无法被子类访问,子类只能通过父类的非private方法“间接”访问父类的private成员。
这样保证了父类private成员的安全性。
protected成员只能被自己以及子类(直接或者间接)访问,无法被“外姓人”访问。
public:公有的,可以被外部的类访问
private:只能自己类中访问,子类和外界无法访问
protected:只能自己和子类访问(直接或间接),外界不能访问
这也是封装的一个特性: 为了防止对方法细节的访问,提高安全性
3.多态
面向对象三大特征:封装、继承、多态。多态是面向对象最强大的一个特征,也是最难的一个特征,
设计模式等都是多态的体现。大型项目架构也大量应用多态。
OverRide(重写):子类中定义和父类中一样的方法就叫“重写(Override)或覆盖”,
Override和OverLoad区别:

OverLoad:重载,方法的名字一样,参数的个数或类型不一样
OverRide:重写、覆盖,子类有和父类一样的(名字、个数、类型、返回值)的方法(非private)
父类中可以被Override方法要声明为 virtual
class DiQiuRen
{
//父类中希望子类可以override的方法标注virtual
public virtual void speak()
{
Console.WriteLine("我是地球人");
}
}
class Japenese : DiQiuRen
{
}
class Chinese : DiQiuRen
{   
//子类中override父类的相同方法必须标注override
//否则会报警告!
public override void speak()
{
Console.WriteLine("我是中国人");
}
public void baiNian()
{
Console.WriteLine("过年好!");
}
}
DiQiuRen dqr1 = new DiQiuRen();
dqr1.speak();
Chinese zgr1 = new Chinese();
zgr1.speak();
Japenese j1=new Japenese();
j1.speak();
执行结果:我是地球人
      我是中国人
      我是地球人
//不能用子类变量指向父类的对象
//Chinese zgr2 = new DiQiuRen();//错误的,地球人不一定是中国人
//可以用父类的变量指向子类的对象
DiQiuRen dqr2 = new Chinese();//把Chinese对象看成是“地球人”
dqr2.speak();
//dqr2.baiNian()//编译失败,因为“地球人”不一定有baiNian方法
DiQiuRen dqr2 = zgr1;
dqr2.speak();
执行结果:我是中国人
       我是中国人
父类变量可以指向子类对象,内存中也还是子类对象,有且只有这一个对象。
变量是什么类型没关系,到底执行谁的方法主要取决于内存中的对象是什么类型。
变量类型是“把对象看成什么”,

一句话概括:
能够调用什么方法由变量类型决定,执行谁的方法由实际指向的对象决定。
类型转换(只是在指向相同对象,变量类型不同时转换):
Chinese zgr5 =  (Chinese)dqr2;//显示类型转换
zgr5.sayHello();
zgr5.baiNian();
Chinese zgr5 =  (Chinese)dqr1;
()类型转换可以把“被看做父类对象的实例”重新看成“子类”。(显式类型转换/强制类型转换)
//DIQiuRen dqr3 = new Japenese();
DIQiuRen dqr3 = new DIQiuRen();
Chinese zgr6 =  (Chinese)dqr3;//编译通过,但会抛异常
//如果转换的变量类型不是指向相同的对象,会抛出异常运行
如果对象就是父类对象,当被看成子类对象的时候会失败,抛出运行期异常,编译器无法发现这个错误。
类型转换只能在有父子关系的类中进行
1)定义一个方法 void test1(DiQiuRen dqr){dqr.sayHello();}
  如下调用test1(new Chinese());可以吗?运行结果是什么?
  可以   结果是:我是中国人
2)String s = (String)zgr1;可以吗?

不可以,因为变量s的类与变量zgr1的类不是父子关系,所以不能转换

4.抽象类(abstract):无法实例化
把类标记为abstract,这样的类无法被直接实例化(new),这就叫抽象类
DiQiuRen的sayHello输出“我是地球人”显然不合理,因为无法确定怎么说,
也就是DiQiuRen不知道如何sayHello,只有具体到中国人、日本人、美国人才知道如何sayHello
把DiQiuRen的sayHello的方法体去掉,并且方法增加abstract修饰,类也修饰为abstract:
abstract class DiQiuRen//一个类中如果有至少一个抽象方法,则类必须标记为abstract
{
public abstract void speak();//抽象方法,没有方法体,不能提供实现,有{}也不行
}
抽象方法没有方法体;一旦类中定义了抽象方法,类必须被修饰为抽象;抽象类无法实例化(new)。
抽象方法必须要实现(通过子类override来实现):
class Chinese:DIQiuRen
{
public override void speak()//抽象方法speak()无vritual标记,override意为替换
{
Console.writeLine("我是中国人");
}
}
DIQiuRen dqr1=new Chinese();
dqr1.speak();//调用父类的speak()方法,通过子类的speak()方法实现
抽象类的抽象方法主要用来限制子类“必须实现这些抽象方法”;
子类也可以不实现,那么子类也要是抽象类,由子类的子类……去实现。

abstract class Japenese:DIQiuRen
{
//可以不实现父类抽象方法,但此类必须声明为抽象类,而且此类的子类要去实现
}
class DongJingRen:Japenese
{
public override void speak()
{
Console.writeLine("aaaaa");
}
}
如果一个类中有一个抽象方法,则类必须是抽象方法;
如果一个类是抽象类,不一定要有抽象方法。

5.接口(interface) 
接口是一种用来声明“能力”的类型,不提供具体实现
语法:
public interface ISpeakable
{
void speak();//接口方法不能声明为public,默认就是public的
}
不提供实现方法,连{}都不能有。
接口无法实例化(new),只能被类“实现”,但是, 可以用一个接口指向一个对象,
前提是这个对象继承了这个接口 . 就好像是可以用一个父类指向一个子类一样.
public class TeacherCang : ISpeakable
{
public void speak()
{
Console.WriteLine("我会说");
}
}
ISpeakable sp=new TeacherCang();
sp.speak();
TeacherCang sp1=new TeacherCang();
sp1.speak();
既可以使用接口类型变量又可以使用类类型变量调用speak
接口的意义是定义“做什么”,类定义“怎么做”,接口只是“能力”不是“实现”
接口只提供方法,不提供实现,所以没有方法体,不能声明变量(字段),不能也必要定义构造函数
接口方法不能声明为public,默认就是public的
接口中可以定义多个方法,也可以不定义任何方法(* 标识接口)。
类只能有一个父类(C#是单继承),但类可以实现多个接口。
例:
class Program
{
static void Main(string[] args)
{
IFlyable if1 = new Brid();
if1.Fly();
IEatable ie1 = (IEatable)if1;// IEatable ie1 = (Brid)if1也行,前者遵循最小接口原则
ie1.eat();
Console.WriteLine("-----------");
Brid br1 = new Brid();
br1.eat();
br1.Fly();
br1.Speak();
Console.ReadKey();
}
}
class Brid : Person,IFlyable, IEatable//只能有一个父类,但可以有多个接口
{
public void eat()
{
Console.WriteLine("我在吃");
}
public void Fly()
{
Console.WriteLine("我在飞");
}
public override void Speak()
{
Console.WriteLine("我是小鸟,说鸟语");
}
}
abstract class Person
{
public abstract void Speak();
}
interface IEatable
{
void eat();
}
interface IFlyable
{
void Fly();
}
接口和抽象类的区别和联系:
接口是对动作的抽象,抽象类是对根源的抽象。
抽象类表示的是,这个对象是什么。接口表示的是,这个对象能做什么。
一个类只能继承一个类(抽象类),但是可以实现多个接口。
接口和抽象类都是继承树的上层,他们的共同点如下:
1) 都是上层的抽象层。
2) 都不能被实例化
3) 都能包含抽象的方法,这些抽象的方法用于描述类具备的功能,但是不比提供具体的实现。
他们的区别如下:
1) 在抽象类中可以写非抽象的方法,从而避免在子类中重复书写他们,这样可以提高代码的复用性,这
是抽象类的优势;接口中只能有抽象的方法。
2) 一个类只能继承一个直接父类,这个父类可以是具体的类也可是抽象类;但是一个类可以实现多个接
口。
当一个类是只是用于继承的时候,就用抽象类,抽象类常作为基类。在在c#类属于单继承,当我们需要从不同的类中
获取信息时,就需要用到接口,因为一个类可以实现多个接口
6.关键字 is 和 as
is:用来判断一个变量指向的对象是否是指定的类型(或者子类)的类型
检查一个对象是否兼容于指定的类型,并返回一个Boolean值:true或者fasle。
ClassA
{
.... 
}
Object o=new Object();
Boolean b1=(o is Object); //b1为true. 判断o指向的对象是否是Object类或Object子类的类型对象
Boolean b2=(o is ClassA) ; b2为false.//is操作符永远不会抛出异常
as:用来进行(引用类型的)类型转换,如果转换失败则返回null;
而类型 l1=(类型)l2;显式转换(强制转换)如果转换失败则会抛出异常
Object obj=new Object(); 
ClassA a= obj  as ClassA;
if(a!=null)  //在if判断a是否为null
{
...
}
Object obj=new Object(); //创建一个object对象.
ClassA a =obj as ClassA;//将obj转型为ClassA,此时转型操作会失败,不会抛出异常,但a会被设为null.
a.ToString();//访问a会抛出一个NullReferenceException异常。
一般is和类型 l1=(类型)l2;配合使用,用as转换就省得判断一下is
   if (p is Child)
            {
                 Console.WriteLine("是child类型");
                 Child c1 = (Child)p1;
            }
            else
            {
                 Console.WriteLine("bu是child类型");
            }
            Child c2 = p as Child;
            if (c2 == null)
            {
                  Console.WriteLine("null");
            }
            else
            {
                 Console.WriteLine("是Child");

            }

抽象类与接口:

1.虚方法就是virtual声明的函数,虚方法在编译期间是不会被静态编译的,而是会根据运行期间的对象实例,来动态

判断要调用的方法,也就是调用override重写后的方法,这样就实现了方法一对多的函数关系,这主要是满足了

面向对象思想原父类的某个虚方法不能满足你的需求时,你可以对它进行扩展。

2.虚方法和抽象类,接口之间也没什么直接联系,不过抽象类中的抽象方法都是隐式的virtual方法,它们也体现了面向

对象一个原则,把可变和不可变的分离,抽象类和接口就是定义为不可变的,具体怎么实现是可变的就交给子类去实现。


虚方法、抽象类、接口:

1、虚方法:可以被子类重写的方法,当类中的方法声明前加上了virtual 修饰符,就称为虚方法,在子类中可以用override重写

class DiQiuRen

{

public virtual void SayHello()

{

Console.WriteLine("我是地球人");

}

}

class ZhonGuoRen:DiQiuRen

{

//子类中重写父类一样的方法必须标注override

public override void SayHello()

{

Console.WriteLine("我是中国人");

}

}

2、抽象类:不能初始化的类被叫做抽象类,用 abstract 修饰

abstract class DiQiuRen

{

//声明为abstract的方法为抽象方法,

//不能提供实现,哪怕{}都不行

public abstract void SayHello();

}

class ZhongGuoren : DiQiuRen//继承抽象类,用override重写抽象类DiQiuRen中的抽象方法SayHello

{

public override void SayHello()

{

Console.WriteLine("我是中国人");



}

}

3、接口:不能被实例化,并且接口中的方法都是抽象方法,实现接口的时候都必须实现接口的所有成员

interface ISpeakable//接口

{

//不提供实现方法,连{}都不能有

void Speak();

void Test();

}

interface IFlyable//接口

{

void Fly();

}

class Bird : ISpeakable, IFlyable  //Bird类,并且实现了ISpeakable和IFlyable接口

{

public void Speak()

{

Console.WriteLine("唧唧唧");

}

public void Test()

{           

}

public void Fly()

{

Console.WriteLine("飞呀飞"); 

}

}

4、他们之间的区别是:虚方法在子类中可以重写,也可以不重写,但是抽象类中的抽象方法和接口中的方法必须重写

Object的 ToString()方法是虚方法(virtual),可以被重写(OverRide)。

ToString()方法默认的实现就是把对象的类的全名打印出来,数组的ToString就是"类型[]"

所有无父类的类默认继承Object类,所有子类简接继承Object,所有类中都可重载(OverRide)ToString()方法



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值