面向对象_5_继承

Posted yigegaozhongsheng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象_5_继承相关的知识,希望对你有一定的参考价值。

继承的概念

父类(基类)

被继承的类

子类(派生类)

继承了父类的类,子类“天生”就拥有父类的所有(非private)的属性、字段、方法类成员。

继承(派生)

继承的作用之一是实现代码复用,另外它也可以用于在另一个类的基础之上,对其进行修改和补充。

继承的声明

首先,必须得存在一个父类,也就是你要继承的对象。

[访问修饰符] class 子类名:父类名
{
       子类成员
}

示例:

class Program
{
     static void Main(string[] args)
     {

             老鼠的儿子 jerry = new 老鼠的儿子();

             jerry.打洞();

             jerry.age = 1;

             jerry.上灯台();

             老鼠 舒克 = new 老鼠();

             舒克.打洞();

             Console.WriteLine(jerry.age);

      }
}
class 老鼠 
{
      public int age;
      public void 打洞() 
      {
              Console.WriteLine("我会打洞");
      }
}
class 老鼠的儿子 : 老鼠 
{
        public void 上灯台() 
       {
                Console.WriteLine("上灯台偷油吃下不来");
        }
}

其中,子类继承了父类的成员,但是父类无法调用子类的成员。

 访问修饰符与继承

父类成员的访问修饰符

子类能否继承

public

可以继承

private

不能继承

protected

只能在子类内部使用,子类的实例不能调用

internal

可以继承

 

注意:

class不能是private私有的 。

class的默认修饰符是internal

类成员的默认修饰符是private

关于多继承

C#中,一个父类可以有多个子类,但是一个子类只能有一个父类。只能单继承,不能多继承。

子类也可以作为另外一个类的父类,C#允许多级继承。

   

static void Main(string[] args)
        {
            人类 亚当 = new 人类(28,"亚当");
            亚当.sayHello();
            中国人 李四 = new 中国人();
            李四.Name = "李四"; 李四.Age = 25;
            李四.sayHello();
            李四.chineseKongFu();
            东北人 王老根 = new 东北人();
            王老根.Name = "王老根"; 王老根.Age = 48;
            王老根.sayHello();
            李四.chineseKongFu();
            王老根.二人传();
        }
class 人类 {
        public 人类() { }
        public 人类(int age,string name) {
            Age = age; Name = name;
        }
        public int Age { get; set; }
        public string Name { get; set; }
        public void sayHello() {
            Console.WriteLine("hello,I am {0} , I‘m {1} years old ",Name,Age);
        }
    }
class 中国人 : 人类 {
        public void chineseKongFu()   {
            Console.WriteLine("看招,阿达");
        }
    }
    class 东北人 : 中国人{
        public void 二人传() {
            Console.WriteLine("俺们内噶达都活雷锋,翠花上酸菜");
        }
    }
    class 韩国人 : 人类 {
        public void PaoCai() {
            Console.WriteLine("泡菜好吃斯米达");
        }
    }

继承系统的”自带的内置类”,并对其进行扩展

除了静态类、密封类、结构体或者一些比较特殊的类,除了这些类之外其他的类都可以继承并对其进行扩展。

base关键字

base代表基类,父类

子类中如果出现与父类同名同类型的成员的话,那么子类中的同名成员会覆盖父类的同名成员。但是使用base关键字,就可以调用父类中被“遮盖”掉的成员。

示例:

static void Main(string[] args)
      {
            dogSun d1 = new dogSun();
            d1.showColor();
        }
    class dog    {
        public string color = "黄色";
    }
    class dogSun : dog     {
        public string color = "棕色";
        public void showColor(){
            Console.WriteLine("我老爸的颜色是"+base.color);
        }
    }

构造函数的继承

当子类构造实例的时候,同时要构造父类中的成员。最简单的方法就是直接使用父类的构造函数。因此,很多时候我们需要调用父类的有参的构造函数以便在实例化的时候构造父类的成员

子类会自动调用父类中的无参的构造函数。

示例:

class Program
{
    static void Main(string[] args)
    {
    sun tom = new sun(3, "tom", "2358451245");
    }
}
class father {
    public int age;
    public string name;
    public father(string name,int age) {
            this.age = age; this.name = name;
            Console.WriteLine("我是父类的构造函数");
        }
    }
    class sun : father {
        public string studentNum;
        public sun(int age, string name, string studentNum) : base(name,age)
        {
            this.studentNum = studentNum;
            //不需要在这个构造函数里面来对父类的成员变量赋值。
            //因为我们已经调用了父类的构造函数
            Console.WriteLine("我是子类的构造函数");
        }
    }

重载 

方法的签名

就是指一个方法的特征,包括一个方法返回类型、传入参数的类型和个数、方法名等等特征。

相同签名的方法在一个类中只能出现一次。

方法的重载

就是指方法名相同,但是方法签名的传入参数部分不同的同名方法,允许在一个类之中多次出现。

判断一个方法是否重载,只要看方法名和传入参数的个数与类型,而不是去看传入参数的变量名和返回参数的类型。

  

      static void Main(string[] args)
        {
            student s1 = new student();
            s1.平均分(99, 100, "该生考试传纸条");
            s1.平均分(99, 100, 91,87);
        }
    class student
    {
        public float 平均分(float 语文成绩, float 数学成绩)
        {
            Console.WriteLine("这是一年级的平均分");
            return (语文成绩 + 数学成绩) / 2;
        }
        public string 平均分(float 语文成绩, float 数学成绩,string 监考老师留言)
        {
            Console.WriteLine(监考老师留言);
            return 监考老师留言;
        }
        public float 平均分(float 语文成绩, float 数学成绩, float 英语成绩)
        {
            Console.WriteLine("这是二年级的平均分");
            return (语文成绩 + 数学成绩 + 英语成绩) / 3;
        }
        public float 平均分(float 语文成绩, float 数学成绩, float 英语成绩,float 思想品德)
        {
            Console.WriteLine("这是三年级的平均分");
            return (语文成绩 + 数学成绩 + 英语成绩 + 思想品德) / 4;
        }
    }

构造函数的重载

传入参数的类型和个数相同的构造函数,在一个类中只允许出现一次。

但传入参数的类型和个数不同的构造函数,允许多次出现。并且实例化的时候,会自动根据参数的个数与类型来决定使用哪一个构造函数。

示例:

  

  class Program
    {
        static void Main(string[] args)
        {
            student s1 = new student("小芳", 162, sexEnum.女, 21);
            Console.WriteLine("我叫{0}身高{1}cm性别{2}年龄{3}岁",s1.name,s1.height,s1.sex,s1.age);
            student s2 = new student("小黑");
            Console.WriteLine("我叫{0}身高{1}cm性别{2}年龄{3}岁", s2.name, s2.height, s2.sex, s2.age);
            student s3 = new student();
            Console.WriteLine("我叫{0}身高{1}cm性别{2}年龄{3}岁", s3.name, s3.height, s3.sex, s3.age);
        }
    }
    public enum sexEnum { 男,女}
    class student
    {
        public string name;
        public float height;
        public sexEnum sex;
        public int age;
        public student()
        {
            Console.WriteLine("这是无参的构造函数");
            name = "小明"; height = 173; sex = sexEnum.男; age = 17;
        }
        public student(string name)
        {
            Console.WriteLine("这是一个参数的构造函数");
            this.name = name; height = 173; sex = sexEnum.男; age = 17;
        }
        public student(string name,float height,sexEnum sex,int age)
        {
            Console.WriteLine("这是四个参数的构造函数");
            this.name = name; this.height = height; this.sex = sex; this.age = age;
        }
    }

小练习:

创建一个员工类,员工类中的属性:姓名、基本工资、部门、工作天数。

构造员工对象的时候如果没有输入部门,则部门为”销售部”,如果没有基本工资,默认2500(用构造函数的重载来实现)

定义方法”本月工资”,该方法有多个重载,1、没有任何传入值,代表该员工本月全勤,在基本工资基础上,加全勤将100元。2、传入一个参数代表迟到次数,迟到一次扣30 ,并且没有全勤奖。3、传入两个参数分别是迟到次数和请假次数。4、传入三个参数分别代表迟到次数、请假次数、旷工次数。

通过this来调用当前类之中的其他构造函数

优势是提高了代码的复用率

public employee(string 姓名, float 基本工资, string 部门, int 工作天数)
{
this.姓名 = 姓名;
this.部门 = 部门;
this.工作天数 = 工作天数;
this.基本工资 = 基本工资;
}
public employee(string 姓名, int 工作天数)
: this(姓名, 2500, "销售部", 工作天数)
{
}

内部类

所谓内部类,就是指在一个类的{}内部范围中,再定义一个类,这个内部的类可以可以看作是外部类的类成员。

 

 class Program
    {
        static void Main(string[] args)
        {
            Program.aa.bb.cc.name = "fdasfds";
        }
        public class aa
        {
            public static string name;
            public class bb
            {
                public static string name;
                public class cc
                {
                    public static string name;
                }
            }
        }
    }

隐藏

个人认为叫做遮盖要更准确一点。

老爸和儿子都喜欢喝酒、看电影。

示例:

  

  class Program
    {
        static void Main(string[] args)
        {
            儿子 二毛 = new 儿子();
            二毛.看电影();
            二毛.喝酒();
        }
    }
    class 老爸
    {
        public void 看电影()
        {
            Console.WriteLine("我爱看刘老根");
        }
        public void 喝酒()
        {
            Console.WriteLine("我红星二锅头,我的最爱");
        }
    }
    class 儿子 : 老爸
    {
        new public void 看电影()
        {
            Console.WriteLine("我爱看科幻大片IMAX");
        }
        public new void 喝酒()
        {
            Console.WriteLine("青岛啤酒,一生朋友");
        }
    }

由子类中的同名方法去覆盖父类中的方法,这个过程就叫做隐藏。

隐藏时子类中的方法可以用new 关键字来表示,但并不是必须的,加上它的好处是可以很明显的看出来它覆盖了父类的方法。

访问被隐藏的父类成员,示例:

  public new void 喝酒()
        {
            //Console.WriteLine("青岛啤酒,一生朋友");
            base.喝酒();
        }

虚方法

虚方法也是基于隐藏的概念的。

比如,你的父辈小有所成,希望子类能够继承它的事业,但是子类心中也是雄心勃勃,他要闯荡自己的事业。他们父辈很无奈,但是他也很民主,他由子类来选择是继承还是覆盖父类的方法。

就是说覆盖还继承的选择权在子类。

语法

父类 virtual

子类 override

示例:

    class Program {
        static void Main(string[] args) {
            儿子 二毛 = new 儿子();
            二毛.做事业();
        }
    }
    class 老爸 {
        public virtual void 做事业(){
            Console.WriteLine("我的小店生意还算不错");
        }
        public virtual void 喝小酒(){
            Console.WriteLine("二锅头,好朋友");
        }
    }
    class 儿子 : 老爸 {
        public override void 做事业(){
            Console.WriteLine("我要开自己的公司");
        }
    }

通过虚方法的关键关virtualoverride我们可以很直观的看到继承与覆盖的过程。

 隐藏与虚方法的区别

  

 class Program    {
        static void Main(string[] args) {
            //C#中的父类可以调用子类的构造函数来实例化对象。
            老爸 二毛 = new 儿子();
            二毛.做事业();
            老爸 大姐 = new 女儿();
            大姐.做事业();
        }
    }
    class 老爸  {
        public virtual void 做事业()    {
            Console.WriteLine("我老字号生意还算不错");
        }
    }
    class 儿子 : 老爸{
        public override void 做事业(){
            Console.WriteLine("我要开自己的公司");
        }
    }
    class 女儿 : 老爸  {
        new public void 做事业()  {
            Console.WriteLine("我要学医");
        }
    }

当我们用父类的类型调用子类的构造函数来实例化一个对象时,虚方法所覆盖的父类成员会真的被隐藏掉,但是由new来隐藏的父类成员则不会,而是直接可以被调用。

类型的兼容型

父类定义的变量可以存放子类构造函数构造出的对象。但这种方法构造出来的对象,无法调用子类的成员(除了虚方法)。

密封类

密封类的主要作用是防止派生。也就是让一个类不能作为父类。(也不能成为抽象类)

sealed关键字来实现。

示例:

  sealed class 老张
    {
        public int age;
        public string name;
    }
    class 小张 : 老张 {   //报错
    }

同时,密封类与其实例对象的调用时,性能会略快一些。

Object

它是C#中一切类的父类。

另外,C#中父类定义的变量可以存放子类构造函数构造的对象。

那也就是说,object类型的变量可以存放所有的类构造的对象。

示例:

 class Program
    {
        static void Main(string[] args)
        {
            object a = 1;
            object b = "tom";
            object c = false;
            object d = new DateTime(2016,6,6);
            object e = new student();
            Console.WriteLine(a.ToString()+b+c+d+e);
        }
    }
    class student { public int age;}

装箱

就是将其他类型的数值转换或定义为object类型,这个过程就叫做装箱。

因为装箱之后的对象就没有办法再调用其原本的类成员了。

就好像将类原本的特征隐藏了起来。

拆箱

就是将object类型的数据,转换为其原本的类型。

示例:

object e = new student(); //装箱
string age = e.age; //报错,装箱后就无法调用类成员age了
 ((student)e).age = 10; //拆箱

 object类中的方法

方法

作用

bool Equals(object 对象名)

判断传入的对象是否与该方法所有的object的引用相同。

object o1 = new father();

father d1 = (father)o1;

Console.WriteLine(o1.Equals(d1));

//返回true,即使它们的类型不一样,但指向的是同一个对象。

bool Equals(object 对象1object 对象2)

判断两个的变量的引用是否相同。

Type GetType()

运行时获取对象的类型。

object o1 = new father();

Console.WriteLine(o1.GetType().ToString());

//显示”father”,装箱之后依然可以得到对象原本的类型。

string ToString()

返回描述对象的字符串。

 小练习:

定义一个add()加法方法(返回string),接收两个object类型的参数,接收到之后在方法中用getType()来判断其原本的类型,

如果都是intfloat类型,则两数相加并将结果以字符串类型返回

如果都是string则将两数相连并返回结果。

如果都是datatime类型,则返回较大的那个时间。

如果不是上述类型,则返回“你输入的参数不能进行加法操作。”

提示:

   static void Main(string[] args)
        {
            add(12,11);
        }
        static string add(object a, object b)
        {
Console.WriteLine(a.GetType().ToString());
            return ((int)a + (int)b).ToString();
        }

以上是关于面向对象_5_继承的主要内容,如果未能解决你的问题,请参考以下文章

29.Python面向对象类:主要讲初始化方法__init__,垃圾回收,继承&多继承,方法重写,super()函数

29.Python面向对象类:主要讲初始化方法__init__,垃圾回收,继承&多继承,方法重写,super()函数

29.Python面向对象类:主要讲初始化方法__init__,垃圾回收,继承&多继承,方法重写,super()函数

13_Java面向对象_第13天(staticfinal匿名对象内部类包修饰符代码块)_讲义

面向对象三大特性之继承

面向对象三大特性之继承