面向对象(一)—继承与多态

又一次深入的学习设计模式,发现了很多以前感觉不是问题的问题,这才发现原来自己不是真的理解了。通过这次的深入学习,才开始慢慢感受到OO的魅力所在。

从C#学习到设计模式,再到机房收费系统个人版和合作版,再到我们做的项目,我们真正的朝着面向对象编程了吗?我的项目中,先不说泛型、委托、集合的利用率,就是基本的继承、多态用的少之又少。

下面将为大家解说“OO引领编程”之——继承和多态

继承篇

一、简介

俗话说:龙生龙凤生凤,老鼠的儿子会打洞。可以理解为,继承就是小辈拥有父辈流传下来的东西。

在编程的世界里也一样,后一代继承前一代的非私有功能并有自己新的发展。如果没有发展,小辈和父辈就是一样的,没有什么新的发展,社会岂不是要停滞不前。即子类继承父类的所有特性,同时还定义新的特性。

二、实例

1、基本实例

<span style="font-size:18px;">using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace JiCheng

{

    class Program

    {

        static void Main(string[] args)

        {

             QueryStudentOnLineInfo studentA=new QueryStudentOnLineInfo();

             //调用父类的查询学生上机信息

   		 studentA.QueryStudentOnLine();

  		  //调用子类的查询学生余额

    		 studentA.QueryStudentBalance();

        }

    }

    //父类拥有查询学生上机信息

    class QueryStudentInfo{

        public void QueryStudentOnLine(){

        Console.WriteLine("查询学生是否正在上机");        

     }

   }

    //子类即拥有父类的方法,也拥有自己的查询学生余额方法

    class QueryStudentOnLineInfo: QueryStudentInfo{

     public void QueryStudentBalance(){

          Console.WriteLine("查询学生余额");        

       }

    }

}

</span>

注意:

继承中子类中隐藏了实现父类的部分,如

    //子类即拥有父类的方法,也拥有自己的查询学生余额方法

    class QueryStudentOnLineInfo: QueryStudentInfo{

     public void QueryStudentOnLine(){

         Base.QueryStudentOnLine();

     }

     public void QueryStudentBalance(){

        Console.WriteLine("查询学生余额");        

     }

  }

2、Base关键字的引入

Base关键字用于从派生类中访问基类成员,并且可以使用Base关键字调用基类的构造函数。下面看它的使用方法。

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace JiChengBase

{

    class Program

    {

        static void Main(string[] args)

        {

            QueryStudentOnLineInfo studentA = new QueryStudentOnLineInfo(7);

        }

    }

    //父类定义构造函数

    class QueryStudentInfo

    {

      protected int id; //定义卡号

        public  QueryStudentInfo(int id)

        {

            this.id = id;

            Console.WriteLine("我的卡号为{0}",id);

        }

    }

    //调用基类的构造函数

    class QueryStudentOnLineInfo : QueryStudentInfo

    {

        public QueryStudentOnLineInfo(int id):base(id){}

    }

}

三、特点

(1)子类拥有父类非private得属性和功能;

(2)子类具有自己的属性和功能,即子类可以扩展父类没有的属性和功能;

(3)子类还可以以自己的方式实现父类的功能(即方法重写)

四、优缺点

优点:继承机制允许和鼓励类的重用,派生类既具有自己新定义的属性和行为,又具有继承下来的属性和行为。

缺点:

破坏封装,子类和父类间紧密耦合

增加系统复杂度

五、小结

继承的应用很普遍也很常用,但使用时一定要判断是否二者符合继承关系,即is-a关系。继承是一种强关系,复杂的继承会导致难以复用,所以要参考结合“合成聚合原则”使用。

 多态篇

一、简介

同一操作作用于不同的对象,产生不同的结果,这就是多态。

二、实例

1、编译时多态

编译时多态就是我们所谓的重,包含构造函数重载和方法重载

2、运行时多态

(1)virtual-override实现多态

<span style="font-size:18px;">using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace JiCheng

{

    class Program

    {

        static void Main(string[] args)

        {

            QueryStudentInfo studentA = new QueryStudentOnLineInfo();

            //调用父类的查询学生上机信息

               studentA.QueryStudentOnLine();

        }

    }

    //父类拥有查询学生上机信息

    public class QueryStudentInfo{

     public  virtual void QueryStudentOnLine(){

        Console.WriteLine("查询学生是否正在上机");        

     }

    }

    //子类重写父类学生是否正在上机的信息

   public  class QueryStudentOnLineInfo: QueryStudentInfo{

       public override void QueryStudentOnLine() {

           Console.WriteLine("查询学生是否已购卡");        

       }

    }

}

</span>

(2)abstract-override实现多态

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace JiCheng

{

    class Program

    {

        static void Main(string[] args)

        {

            QueryStudentInfo studentA = new QueryStudentOnLineInfo();

            //调用父类的查询学生上机信息

                studentA.QueryStudentOnLine();

        }

    }

    //父类拥有查询学生上机信息

    public abstract class QueryStudentInfo{

        public abstract void QueryStudentOnLine();

    }

    //子类重写父类学生是否正在上机的信息

   public  class QueryStudentOnLineInfo: QueryStudentInfo{

       public override void QueryStudentOnLine() {

           Console.WriteLine("查询学生是否已购卡");        

       }

    }

}

区别

1、抽象方法只有声明没有实现代码,需要在子类中实现;虚方法有声明和实现代码,并且可以在子类中重写,也可以不重写使用父类的默认实现。

2、抽象类不能被实例化(不可以new),只能实例化实现了全部抽象方法的派生类;而包含虚方法的类可以实例化。

联系

抽象方法是虚拟方法两个相像的一点是都用override重写。

三、特点

1.子类以父类身份出现

2.子类工作时以父类身份出现

3.子类以父类身份出现时,子类特有属性不可使用

四、具备条件

1.继承关系的存在

2.方法的重写

3.父类的引用指向子类对象

五、优缺点

优点:提高了代码的扩展性。

不足:前期建立父类的引用虽然可以接收后期所有该类的子类对象。但是只能使用父类中的功能,不能使用子类中的特有功能,

六、小结

多态是面向对象的核心。继承是子类使用了父类的方法;多态是父类使用了子类的方法。继承实现了功能传递和延展;多态实现了单一行为的多种表示。充分利用好这两大关系,会使我们的编程更具弹性。

注意:

使用父类类型的引用指向子类的对象,该引用只能调用父类中定义的方法和变量;

变量不能被重写(覆盖),”重写“的概念只针对方法,如果在子类中”重写“了父类中的变量,那么在编译时会报错。

重写、重载、多态篇

一、重写和重载

1、重写(覆写):

(1)重写方法的参数列表和返回值必须与被重写方法一致

(2)重写方法的访问修饰符一定要大于被重写方法的访问修饰符(public>protected>default>private)。

(3)重写的方法所抛出的异常必须和被重写方法的所抛出的异常一致,或者是其子类;

(4)被重写的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行重写。

(5)静态方法不能被重写为非静态的方法(会编译出错)。

2、重载

(1)相同的方法名,不同的参数形式。不同的参数形式可以是不同的参数类型,不同的参数个数,不同的参数顺序(参数类型必须不一样);

(2)不能通过访问权限、返回类型、抛出的异常进行重载;

(3)方法的异常类型和数目不会对重载造成影响;

常见实例:构造函数

二、重载和多态

1、重载,是指允许存在多个同名方法,而这些方法的参数不同。重载的实现是:编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。

2、多态:是指子类重新定义父类的虚方法(virtual,abstract)。当子类重新定义了父类的虚方法后,父类根据赋给它的不同的子类,动态调用属于子类的该方法,这样的方法调用在编译期间是无法确定的。

区别在于编译器何时去寻找所要调用的具体方法,对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;而对于多态,只有等到方法调用的那一刻,编译器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。

new和override篇

一、new

当派生类的方法使用new修饰时,子类对象的对象转换为父类对象后,调用的是父类中的QueryStudentOnLine()方法。可以理解为,使用new关键字后,使得子类中的QueryStudentOnLine()方法和父类中的QueryStudentOnLine()方法成为毫不相关的两个方法,只是它们的名字碰巧相同而已。

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

)

namespace duotai

{

    class Program

    {

        static void Main(string[] args)

        {

            //实例化父类对象

            QueryStudentInfo studentA = new QueryStudentInfo();

            //父类对象指向子类引用

            QueryStudentInfo studentB = new QueryStudentOnLineInfo();

            //实例化子类对象

            QueryStudentOnLineInfo studentC = new QueryStudentOnLineInfo();

            studentA.QueryStudentOnLine();

            studentB.QueryStudentOnLine();

            studentC.QueryStudentOnLine();

        }

    }

    //父类拥有查询学生上机信息

    public class QueryStudentInfo

    {

        public virtual void QueryStudentOnLine()

        {

            Console.WriteLine("查询学生是否正在上机");

        }

    }

    //子类重写父类学生是否正在上机的信息

    public class QueryStudentOnLineInfo : QueryStudentInfo

    {

        public new void QueryStudentOnLine()

        {

            Console.WriteLine("查询学生是否已购卡");

        }

    }

}

不能说通过使用new来实现多态,只能说在某些特定的时候碰巧实现了多态的效果。

二、override

如果子类中重写了父类中的一个方法,那么在调用这个方法的时候,将会调用子类中的这个方法;(动态连接、动态调用)

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace duotai

{

    class Program

    {

        static void Main(string[] args)

        {

            //实例化父类对象

            QueryStudentInfo studentA = new QueryStudentInfo();

            //父类对象指向子类引用

            QueryStudentInfo studentB = new QueryStudentOnLineInfo();

            //实例化子类对象

            QueryStudentOnLineInfo studentC = new QueryStudentOnLineInfo();

            studentA.QueryStudentOnLine();

            studentB.QueryStudentOnLine();

            studentC.QueryStudentOnLine();

        }

    }

    //父类拥有查询学生上机信息

    public class QueryStudentInfo

    {

        public virtual void QueryStudentOnLine()

        {

            Console.WriteLine("查询学生是否正在上机");

        }

    }

    //子类重写父类学生是否正在上机的信息

    public class QueryStudentOnLineInfo : QueryStudentInfo

    {

        public override void QueryStudentOnLine()

        {

            Console.WriteLine("查询学生是否已购卡");

        }

    }

}

小结:

使用new关键字,即使父类引用指向子类对象,最终调用的是父类方法(特殊情况下会用到)

而使用override,父类引用指向子类对象时,动态调用子类方法

接口、抽象类篇

一、比较

1、抽象类是对类的抽象;接口是对行为的抽象

2、行为跨越不同对象可使用接口;对于相似类对象使用抽象类。

3、抽象类是从子类中泛化出父类,然后子类继承父类;接口并不知道子类的存在。

二、实际运用

1、如果要设计大的功能单元,则使用抽象类.如果要在组件的所有实现间提供通用的已实现功能,则使用接口。

2、抽象类主要用于关系密切的对象;而接口适合为不相关的类提供通用功能。

父类引用指向子类

一、几种情况

namespace duotai{

    class Program

    {

        public class Animal

        {

            public virtual void Eat()

            {

                Console.WriteLine("动物吃东西");

            }

        }

        public class Dog : Animal

        {

            public override void Eat()

            {

                Console.WriteLine("狗吃东西");

            }

        }

       static void Main(string[] args)

            {

                Animal A = new Animal();

                Animal B = new Dog();

                Dog C = new Dog();

                A.Eat();

                B.Eat();

                C.Eat();

            }

      }

}

1、当父类引用B指向其子类的对象的时候( Animal B = new Dog()),通过B无法访问专属于子类对象的成员。

因为B是父类引用,所以编译器只知道B拥有的信息,Animal以外的信息,编译器不知道,而子类的对象成员是在Dog里,也就是说在父类以外,所以B无法访问子类的对象成员

2、假如子类中有对父类方法的重写,那么根据多态机制,通过A访问这个方法的时候实际访问的是子类中重写的方法。

编译器:父类对象只知道自己是父类类型,不知道具体指向什么对象

运行期(调用):父类引用指向的是子类对象,所以调用子类方法

3、如果子类重写的方法中访问了专属于子类的成员变量,这时候通过父类引用A还可以调用那个被重写的方法吗?

可以,要分清编译期和运行期,编译期是编译器检查语法和类型,运行期是解析器解析伪代码为机器指令而执行,编译期编译器会检查B的访问范围,也就是B的访问不超过父类的信息就不会出错,运行期解析器会解析方法的代码指令,因为B指向子类对象,所以会解析子类重写的方法代码指令,而子类对象的内存空间是包含子类的成员变量的空间的,所以也不存在子类成员变量没有分配内存的问题,所以可以调用。

二、优点

定义一个父类类型的引用指向一个子类的对象既可以使用子类强大的功能,又可以抽取父类的共性。所以,父类类型的引用可以调用父类中定义的所有属性和方法,而对于子类中定义而父类中没有的方法,它是无可奈何的;

注意:

父类中的一个方法只有在在父类中定义而在子类中没有重写的情况下,才可以被父类类型的引用调用;

总结:

面向对象博大精深,需要我们一步步去深入,去了解。多用多思考会发现我们了解到的也许只是冰山一角。博文只是代表了个人的一些理解,如有不足,请指出,互相交流进步!

面向对象(一)—继承与多态

时间: 2024-11-04 07:36:15

面向对象(一)—继承与多态的相关文章

javascript面向对象:继承、多态

继承 js中同样可以实现类的继承这一面向对象特性,继承父类中的所有成员(变量和属性),同时可扩展自己的成员,下面介绍几种js中实现继承的方式: 1,对象模仿:通过动态的改变 this 指针的指向,实现对父类成员的重复定义,如下: function ClassA(paramColor) { this.color = paramColor; this.sayColor = function() { alert(this.color); } } function ClassB(paramColor,

php 面向对象之继承、多态和静态方法

<?php //继承:子类可以继承父类的一切 //特点:单继承 //父类 class Ren { public $name; public $sex; public $yuyan; function Say() { echo $this->name."正在讲话!"; } } //美国人的子类 class America extends Ren { public $ziben; //子类对父类的方法重写 function Say() { parent::Say(); //调用

Python 面向对象编程——继承和多态

1.1   继承和多态 1.1.1   继承 当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类.父类或超类(Base class.Super class). >>> class Animal(object): ...    def run(self): ...        print('Animal is running...') #父类 ... >>> class Dog(Ani

面向对象(4)--继承和多态

一.继承 继承的类称为子类,被继承的类称为基类,父类,或超类. 继承的优点: 简化了代码 提高了代码的健壮性 提高了代码的安全性 多态的前提 缺点:耦合与内聚.耦合性越低,内聚性越高,代码越好. 比如,有一个Animal类,有个run方法: In [1]: class Animal: ...: def run(self): ...: print('我正在跑步!!!') ...: 当我们需要创建Dog和Cat类时,可以直接继承Animal: In [3]: class Dog(Animal): .

PHP中面向对象的继承和多态基本用法

Extends 继承: 特点:单继承 一个子类只能有一个父类,一个父类可以有多个子类 例子: Class Ren { Public $name; Public $sex; Public $yuyan; Function say() { Echo $this->name.”正在讲话”: } } 美国人的子类: Class amercia extends Ren { Function say() { Parent::say();    调用父类的say()方法 Echo “hello”;    子类

面向对象:继承、多态

1.继承   子类名:父类名 子类可以继承父类所有的公共方法和属性 一个父类可以有无数个子类.后辈类 一个子类只能有一个亲爹 父类.基类子类.派生类.超类        --不同种说法 2.多态 virtual   虚方法 override  重写 overload 重载 用法: class grandfa { public string p() { return "奶奶"; } } -- class father:grandfa { public virtual string peo

面向对象编程——继承和多态(四)

在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类.父类或超类(Base class.Super class). 比如我们已经编写了一个名为Animal的class,有一个run()方法可以直接打印: class Animal(object): def run(self): #为啥没有__init__,因为不需要初始化,就self一个参数. print("Animal is running.

python 面向对象四 继承和多态

一.继承 1 class Animal(object): 2 def run(self): 3 print('Animal is running...') 4 5 class Dog(Animal): 6 7 def run(self): 8 print('Dog is running...') 9 10 def eat(self): 11 print('Eating meat...') 12 13 dog = Dog() 14 dog.run() 1 Dog is running... 当子类

面向对象(继承,多态)

二维数组的定义格式:第一种:直接给定了每一个一维数组中数组长度数据类型[][] 数组名称 = new 数据类型[m][n]第二种:给定了多少个一维数组,长度动态给定数据类型[][] 数组名称 = new 数据类型[m][] ;第三种:二维数组中直接给定元素值数据类型[][] 数组名称 = {{元素1,元素2},{元素1,元素2}-} 如何遍历二维数组 int [][] arr = {{11,22},{33,44},{55,66}} ; 外层循环:控制的是二维数组长度,内存循环,一维数组长度 代码

Java面向对象,继承,多态

1,继承 Java 中通过 extends 关键字可以继承一个类,implements可以继承多个接口.继承允许创建等级层次的类. 继承的机制提高了代码的复用性,让类与类之间产生了关系,有了这个关系,才有了多态的特性. 注:Java支持单继承,但是可以多层继承. 子类可以拥有父类的非private的方法和属性,子类可以继承父类的方法,也可以重写父类的方法,还可以对父进行拓展. 增加了类之间的联系,即提高了耦合. 一般格式为: class 父类 { } class 子类 extends 父类 {