目录
一、封装
封装 被定义为"把一个或多个项目封闭在一个物理的或者逻辑的包中"。在面向对象程序设计方法论中,封装是为了防止对实现细节的访问。
抽象和封装是面向对象程序设计的相关特性。抽象允许相关信息可视化,封装则使程序员实现所需级别的抽象。
封装使用 访问修饰符 来实现。一个 访问修饰符 定义了一个类成员的范围和可见性。C# 支持的访问修饰符如下所示
1.public:公有的,是类型和类型成员的访问修饰符。对其访问没有限制。
2.internal:内部的,是类型和类型成员的访问修饰符。同一个程序集中的所有类都可以访问
3.private:私有的,是一个成员访问修饰符。只有在声明它们的类和结构中才可以访问。
4.protected:受保护的,是一个成员访问修饰符。只能在它的类和它的派生类中访问。
5.protected internal:访问级别为 internal 或 protected。即同一个程序集中的所有类,以及所有程序集中的子类都可以访问
封装的使用方法并不难理解,比如现在需要一个通用的加法算法,那么封装起来就是
public int Add(int x,int y)
{
return x + y;
}
二、继承
继承是面向对象程序设计中最重要的概念之一。继承允许我们根据一个类来定义另一个类,这使得创建和维护应用程序变得更容易。同时也有利于重用代码和节省开发时间。
基类和派生类
派生类只能有一个直接基类,但一个基类可以有多个直接派生类。继承是可以传递的。
定义要从其他类派生的类时,派生类会隐式获得基类的所有成员(除了其构造函数和终结器)。
派生类因而可以重用基类中的代码,而无需重新实现。在派生类中,可以添加更多成员。通过这种方法,派生类可扩展基类的功能。
比如:B 继承了 A,那么 A 就是 基类,也叫父类。那么 B 就是 派生类,也叫子类。
通俗点来讲,子类继承父类后,就可以获得父类的所有字段和方法,这就相当于把父类的方法和字段复制到自己的类中是一个道理,但是,父类和子类是分开的,父类不能从子类拿走什么,这就相当于:你的都是我的,我的还是我的。
为什么要使用继承
先看一段代码
//德牧
public class GermanShepherdDog
{
public string color;
public int age;
public int heigth;
public int state;
}
//泰迪
public class Poodle
{
public string color;
public int age;
public int heigth;
public int state;
}
可以看到,这两个类字段基本都是重复,那么使用继承就可以解决这个问题,看下面的代码
using System;
namespace Test1
{
internal class Program
{
static void Main(string[] args)
{
GermanShepherdDog germanShepherdDog = new GermanShepherdDog();
germanShepherdDog.age = 3;
germanShepherdDog.heigth = 100;
Poodle poodle = new Poodle();
poodle.age = 1;
poodle.heigth = 40;
Console.ReadKey();
}
}
public class Dog
{
public string color;
public int age;
public int heigth;
public int state;
}
public class GermanShepherdDog : Dog
{
}
public class Poodle : Dog
{
}
}
这样代码是不是干净了?但继承的作用可不是这样就完了,继承还有一个作用:重写抽象方法和虚方法。
重写虚方法和抽象方法
virtual 虚方法,abstract 抽象方法,可以在子类进行重写,这两种类型使用方法大不相同,在面试时,经常有相关的题目,比如问你虚方法和抽象方法有那些区别。
下面是对虚方法重写的示例:
using System;
namespace Test1
{
internal class Program
{
static void Main(string[] args)
{
GermanShepherdDog germanShepherdDog = new GermanShepherdDog();
germanShepherdDog.BitePeople();
Console.ReadKey();
}
}
public class Dog
{
public string color;
public int age;
public int heigth;
public int state;
//咬人
public virtual void BitePeople()
{
Console.WriteLine("Dog-BitePeople");
}
}
public class GermanShepherdDog : Dog
{
public override void BitePeople()
{
base.BitePeople();
Console.WriteLine("GermanShepherdDog-BitePeople");
}
}
}
输出:
Dog-BitePeople
GermanShepherdDog-BitePeople
这里注意这一句:base.BitePeople(),它的作用是调用父类中的 BitePeople() 方法,关于 base 的用法,下面会有介绍,在后面的帖子里,我会介绍多态,也会详细的介绍抽象类和抽象方法。
构造方法的执行顺序
继承父类后,构造函数的执行顺序也需要注意下,因为,有时候面试可能会问到
using System;
namespace Test1
{
internal class Program
{
static void Main(string[] args)
{
GermanShepherdDog germanShepherdDog = new GermanShepherdDog();
Console.ReadKey();
}
}
public class Dog
{
public string color;
public int age;
public int heigth;
public int state;
public Dog()
{
Console.WriteLine("Dog构造方法");
}
}
public class GermanShepherdDog : Dog
{
public GermanShepherdDog()
{
Console.WriteLine("GermanShepherdDog构造方法");
}
}
}
输出:
Dog构造方法
GermanShepherdDog构造方法
可以看出,先是执行了父类的构造函数,然后才执行了子类的构造方法
base 关键字
base关键字是用来调用基类的有参数的构造函数,因为子类不能直接继承父类的构造函数。
base的作用:
1.base可以完成创建派生类实例时调用其基类构造函数或者调用基类上已被其他方法重写的方法。
2.base 关键字用于从派生类中访问基类的成员的构造函数的形参。
3.调用基类上已被其他方法重写的方法。
4.指定创建派生类实例时应调用的基类构造函数。
base的注意事项:
1.基类访问只能在构造函数、实例方法或实例属性访问器中进行;
2.如果 基类/父类 中没有形参时,会自己进行调用默认的 base 关键字;
3.在运用 base 关键字时,必须和子类中声明的名称一样;
下面是 base 的一些基本使用
using System;
namespace Test1
{
internal class Program
{
static void Main(string[] args)
{
GermanShepherdDog germanShepherdDog = new GermanShepherdDog("狼狗");
Console.ReadKey();
}
}
public class Dog
{
public string color;
public int age;
public int heigth;
public int state;
public string name;
public Dog()
{
Console.WriteLine("Dog构造方法");
}
public Dog(string name)
{
this.name = name;
}
}
public class GermanShepherdDog : Dog
{
public GermanShepherdDog() : base()
{
Console.WriteLine("GermanShepherdDog构造方法");
}
public GermanShepherdDog(string name) : base(name)
{
Console.WriteLine("GermanShepherdDog构造方法");
}
}
}
输出:
GermanShepherdDog构造方法
只有 GermanShepherdDog 构造函数中的参数,才能在 base 中进行传递,使用别的参数会报错的,如下图
而且,只能调用父类中参数类型匹配的构造函数,比如,这里都是 string 类型, 而且数量只有一个。
在这里你可能会有疑问, 这个 base 主要是给 name 赋值,那我在子类中直径赋值不就好了,看下面代码
using System;
namespace Test1
{
internal class Program
{
static void Main(string[] args)
{
GermanShepherdDog germanShepherdDog = new GermanShepherdDog("狼狗");
Console.ReadKey();
}
}
public class Dog
{
public string color;
public int age;
public int heigth;
public int state;
public string name;
public Dog()
{
Console.WriteLine("Dog构造方法");
}
}
public class GermanShepherdDog : Dog
{
public GermanShepherdDog(string name)
{
Console.WriteLine("GermanShepherdDog构造方法");
this.name = name;
}
}
}
输出
Dog构造方法
GermanShepherdDog构造方法
效果确实是一样的,区别在于,如果
this.name = name;
这一句代码已经写在父类的构造函数中,那么子类就没去修改了,直径用 base 就可以把这些值赋值过去了。
end