您的位置:首页 > 编程语言 > C#

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

2017-12-23 23:14 513 查看
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("过年好!");
1385b

}

}

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()方法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  .net c#