一.继承的前奏(理论篇)
(1).为什么要用继承? 继承可以减少代码冗余
我们在写一些类的时候可能会重复重复写一些成员,我们可以将这些重复的成员单独封装到一个类中,作为这些类的父类 (2).继承中类的叫法: 子类 --->父类 或者 派生类---->基类
(3) 继承语法: public 子类 : 父类
*(4) 继承的两个特性:(1)继承的单根性(一个子类只有一个父类一个孩子只有一个爹)。(2)继承有传递性(祖宗的迟早是你的)
(5) 继承了什么?要看子类能访问什么。
* 1)子类继承了父类的属性和方法,但是没有继承父类的私有字段
* 2) 子类没有继承父类的构造函数没法在创建子类对象时调用(即不能Student s = new Person()),只能创在建子类 对象调用子类构造函数之前由系统默认的先调用父类的无参构造函数(不调用父类有参构造函数,父类的有参构造函数只能用base关键字显式的调用),创建父类对象,目的是让子类可以使用(继承)父类 中的成员。
* (6)如果在父类中重写了父类有参的构造函数之后,那个无参构造函数就被干掉了,子类就掉不到会报错
解决:1)在父类中重写一个无参构造函数
2)在子类中显示的调用父类有参构造函数,使用关键字:base() (此法较多见可以解决代码冗余少写代码)
代码分析:
1,继承前奏之不用继承我们会写很多重复代码例如我们写三个类会这样写
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _09继承的前奏 { class Program { static void Main(string[] args) { } } public class Student //学生类 { private string _name; //名字 public string Name { get { return _name; } set { _name = value; } } private char _gender; //性别 public char Gender { get { return _gender; } set { _gender = value; } } private int _age; //年龄 public int Age { get { return _age; } set { _age = value; } } private int _id; //学号 public int Id { get { return _id; } set { _id = value; } } public void CHLSS() //方法 { Console.WriteLine("吃喝拉撒睡"); } public void Study() //方法 { Console.WriteLine("我会学习"); } } public class Teacher //老师类 { private string _name; //名字 public string Name { get { return _name; } set { _name = value; } } private char _gender; //性别 public char Gender { get { return _gender; } set { _gender = value; } } private int _age; //年龄 public int Age { get { return _age; } set { _age = value; } } private double _salary; //工资 private double Salary { get { return _salary; } set { _salary = value; } } public void CHLAS() //方法 { Console.WriteLine("吃喝拉撒睡"); } public void TeachStudent() //方法 { Console.WriteLine("我会教学生"); } } public class Driver //司机类 { private string _name; //名字 public string Name { get { return _name; } set { _name = value; } } private char _gender; //性别 public char Gender { get { return _gender; } set { _gender = value; } } private int _age; //年龄 private int Age { get { return _age; } set { _age = value; } } private int _driveTime; public int DriveTime //驾龄 { get { return _driveTime; } set { _driveTime = value; } } public void SHLSS() //方法 { Console.WriteLine("吃喝拉撒睡"); } public void Drive() //方法 { Console.WriteLine("我会开车"); } } }
不难看出写了很多重复代码
2.继承呼之欲出,可以减少我们重复写一些相同的代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _0901继承 { class Program { static void Main(string[] args) { } } public class Person //父类 { private string _name; //名字 public string Name { get { return _name; } set { _name = value; } } private char _gender; //性别 public char Gender { get { return _gender; } set { _gender = value; } } private int _age; //年龄 public int Age { get { return _age; } set { _age = value; } } public void CHLSS() //方法 { Console.WriteLine("吃喝拉撒睡"); } } public class Student :Person //子类学生类 继承与人类 { private int _id; //学号 public int Id { get { return _id; } set { _id = value; } } public void Study() //方法 { Console.WriteLine("我会学习"); } } public class Teacher : Person //老师类继承于人类 { private double _salary; //工资 private double Salary { get { return _salary; } set { _salary = value; } } public void TeachStudent() //方法 { Console.WriteLine("我会教学生"); } } public class Driver : Person //司机类继承与人类 { private int _driveTime; //驾龄 public int DriveTime { get { return _driveTime; } set { _driveTime = value; } } public void Dirve() //方法 { Console.WriteLine("我会开车"); } } }类的关系图:
由上面的对比可以发现继承的使用可以减少代码冗余(不建议写项目时把所有的类都写到Program类中)
3.子类构造函数与父类构造函数关系:
子类没有继承父类的构造函数没法在创建子类对象时调用(即不能Student s = new Person()),只能创在建子类对象调用子类构造函数之前由系统默认的先调用父类的无参构造函数(不调用父类有参构造函数,父类的有参构造函数只能用base关键字显式的调用),创建父类对象,目的是让子类可以使用(继承)父类 中的成员。
通过设断点分析我们可知下面代码的调用顺序为:
Student st = new Student() --->Student()但是没执行跳过了---->Person()执行了父类构造函数---->Student()执行子类构造函数
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _0902继承了哪些 { class Program { static void Main(string[] args) { Student st = new Student();//new干三件事情1、在堆中开辟空间2、在堆中创建对象实例化对象(创建对象)3、调用构造函数初始化成员变量 } } public class Person { private string _name; public string Name { get { return _name; } set { _name = value; } } private char _gender; public char Gender { get { return _gender; } set { _gender = value; } } private int _age; public int Age { get { return _age; } set { _age = value; } } public void CHLSS() { Console.WriteLine("吃喝拉撒睡"); } public Person(string name, char gender, int age) { this.Name = name; this.Gender = gender; this.Age = age; } public Person() //此处设个断点 { } } public class Student : Person { private int _id; public int Id { get { return _id; } set { _id = value; } } public void Study() { Console.WriteLine("我会学习"); } //构造函数 public Student() //此处设个断点 { } } }
系统在创建子类对象调用子类构造函数之前调用父类的无参构造函数的目的是创建父类对象让子类可以使用(继承)父类成员:见下图:调用子类构造函数之后执行子类构造函数之前,系统先调用并执行父类的构造函数,然后回过头执行子类构造函数
3.1此处只为说明调用的数父类无参构造函不是有参构造函数
数分析下面代码执行顺序可知,在调用子类有参构造函数实例化子类之前系统也会默认的先执行父类的无参构造函数(不是调用父类有参构造函数)
代码执行过程:
Student s = new Student("张三", '男', 20);---->public Student(string name, char gender, int age )但是没有执行跳过 ----> public Person() 执行了------>public Student(string name, char gender, int age )这次执行了
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _0903继承中的构造函数的调用 { class Program { static void Main(string[] args) { Student s = new Student("张三", '男', 20,10086); } } public class Person //人类 { private string _name;//名字 public string Name { get { return _name; } set { _name = value; } } private char _gender; //性别 public char Gender { get { return _gender; } set { _gender = value; } } private int _age; //年龄 public int Age { get { return _age; } set { _age = value; } } public void CHLSS() { Console.WriteLine("吃喝拉撒睡"); } public Person(string name, char gender, int age) //父类有参构造函数 { this.Name = name; this.Gender = gender; this.Age = age; } public Person()//父类无参构造函数 { } } public class Student : Person { private int _id; public int Id { get { return _id; } set { _id = value; } } public void Study() { Console.WriteLine("我会学习"); } //构造函数 public Student(string name, char gender, int age)//子类有参构造函数 { this.Name = name; this.Gender = gender; this.Age = age; } } }
3.2由上面3.1可知当然我们可以调用父类无参构造函数,来初始化子类例如下面代码,此时父类要是有有参构造函数,那么系统就不会给我们提供缺省无参构造函数,我们要是想调用无参构造函数,我们必须在父类中写一个无参构造函数,要不程序会报错,
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _0903继承中的构造函数的调用 { class Program { static void Main(string[] args) { Student s = new Student("张三", '男', 20,10086); } } public class Person { private string _name; public string Name { get { return _name; } set { _name = value; } } private char _gender; public char Gender { get { return _gender; } set { _gender = value; } } private int _age; public int Age { get { return _age; } set { _age = value; } } public void CHLSS() { Console.WriteLine("吃喝拉撒睡"); } public Person(string name, char gender, int age) { this.Name = name; this.Gender = gender; this.Age = age; } public Person() { } } public class Student : Person { private int _id; public int Id { get { return _id; } set { _id = value; } } public void Study() { Console.WriteLine("我会学习"); } //构造函数 public Student(string name, char gender, int age, int id) { this.Name = name; this.Gender = gender; this.Age = age; this.Id = id; } } }
上面代码调用的是父类无参构造函数,看看还是有代码冗余,我们在调用子类有参构造函数执行子类有参构造函数之前显示的调用父类无参构造函数这样可以更加少写重复代码如下
4.显式调用父类有参构造函数,base关键字的使用
下面代码执行顺序:
调用顺序Student s = new Student("张三", '男', 20, 10086)---->public Student(string name, char gender, int age, int id): base(name, gender, age)只调用(实例化子类的对象)没有执行----->public Person(string name, char gender, int age)执行完----->public Student(string name, char gender, int age, int id): base(name, gender, age)这次执行了
也就是说当子类显式的调用父类的有参构造函数,那么父类的无参构造函数就不会发生作用,可写可不写
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _0904继承中父类构造函数显式调用 { class Program { static void Main(string[] args) { Student s = new Student("张三", '男', 20, 10086); } } public class Person { private string _name; public string Name { get { return _name; } set { _name = value; } } private char _gender; public char Gender { get { return _gender; } set { _gender = value; } } private int _age; public int Age { get { return _age; } set { _age = value; } } public void CHLSS() { Console.WriteLine("吃喝拉撒睡"); } public Person(string name, char gender, int age) { this.Name = name; this.Gender = gender; this.Age = age; } public Person() { } } public class Student : Person { private int _id; public int Id { get { return _id; } set { _id = value; } } public void Study() { Console.WriteLine("我会学习"); } //构造函数 public Student(string name, char gender, int age, int id) : base(name, gender, age)//Ctrl + K + D 对齐 //设个断点 { //this.Name = name; //this.Gender = gender; //this.Age = age; this.Id = id; } } }