笔记7 面向对象的三大特性、值类型和引用类型、抽象类、接口、委托
特性1:封装
将类里面的成员变量和方法进行私有化,如果外界要访问,向外界提供统一的访问方法。
基于;访问修饰符。
优点:减少耦合,防止外界进行修改。
实现方式:
1、编写public方法进行私有变量或方法的封装。
2、属性。
对成员变量的封装
public class Person
{
private int age; //privare私有的。
//变量首选都设为私有的,如果外界要访问,就开一个方法。
public void SetAge(int value) //外界可以传入参数value
{
if (value > 0 && value < 100)
{
age = value;
}
}
public int GetAge()
{
return age;
}
}
对方法的封装
public class Person
{
private int age; //privare私有的。
//变量首选都设为私有的,如果外界要访问,就开一个方法。
//暴露给外界
public void NewFunc() //写一个方法,不写参数
{
Eat(age);
}
//内部
private void Eat(int age)
{
Console.WriteLine("我的年龄是" + age);
//加号可以将age转换成字符串类型,然后与前面相加。
}
特性2:继承
创建新的类(派生类、子类),可以继承现有类(基类、父类)的特性,并且还可以进行修改和扩充。
C#是单继承,C++是多继承。
单继承:一个父类可以有多个子类,但父类只有一个。
多继承:一个父类可以有多个子类,且父类可以有多个。
继承现有类(基类、父类)的特性
修改现有类特性(子类想更改)
覆盖(覆写):未经父类允许(new)
覆盖(覆写):把原本的方法(父类的)覆盖了
父类
public class LaoWang //这个类叫LaoWang
{
public void Play()
{
Console.WriteLine("我要去喝酒");
}
子类
public class XiaoWang : LaoWang
{
public XiaoWang()
{
}
//覆盖(覆写):把原本的方法(父类的)覆盖了
//确定不用父类的某个方法,要用自己的,那么就new
public new void Play()
{
Console.WriteLine("我要玩游戏");
}
}
重写:获得父类允许(父类:虚方法virtual、子类override)
重写:利用virtual和override这一对
父类(虚方法virtual:可以被重写)
public class LaoWang //这个类叫LaoWang
{
//虚方法:可以被重写
public virtual void Eat() //virtual表示其子类此方法可以重写
{
Console.WriteLine("我喜欢吃青椒");
}
}
子类(override)
//写一个子类,即便子类里什么也没写,它也具有父类的特性。
//格式:子类名字:父类名字
public class XiaoWang : LaoWang
{
public XiaoWang()
{
}
//重写:利用virtual和override这一对
public override void Eat() //override表示告诉父类,我重写了这个方法
{
Console.WriteLine("我喜欢吃肉");
}
外围
class Program
{
static void Main(string[] args)
{
XiaoWang xw = new XiaoWang(); //实例化一个xw
xw.Play();
xw.Eat();
Console.ReadKey();
}
}
扩充现有类特性
普通方法(需要在重写的基础上进行,利用关键词base)
关键词this表示当前类所产生的对象,关键词base代表我的父类。
在写我的特性前,利用base调用父类的特性。
子类
//重写:利用virtual和override这一对
public override void Eat() //override表示告诉父类,我重写了这个方法
{
//关键词this表示当前类所产生的对象
//关键词base代表我的父类
base.Eat(); //在写我的特性前,调用父类的特性
Console.WriteLine("我喜欢吃肉");
/*外界调用结果:我喜欢吃青椒
我喜欢吃肉
*/
}
构造方法
调用构造方法时,默认先调用父类的构造方法,之后调用子类的构造方法。
构造方法无参数(不需要利用关键词base)
父类
//构造方法
public LaoWang()
{
Console.WriteLine("我是老王");
}
子类
public XiaoWang() //构造方法
{
Console.WriteLine("我是小王");
}
外界
XiaoWang xw = new XiaoWang(); //new,调用XiaoWang的方法
/*结果:我是老王
我是小王
*/
构造方法带参数(有无base)
1、无base:无论子类的构造方法写成什么样,都会调用父类默认的构造方法(不带参数)。
2、有base,且base有参数:通过base,可以指定调用父类的有相同类型参数的构造方法。
3、有base,但base无参数:调用的便是没有参数的构造方法。结果和无base一样。
XiaoWang xw = new XiaoWang(); //new,调用XiaoWang的方法
//构造方法可以带参数
//“:base(age)”表示先调父类的这个有int类型参数构造方法。把20传给父辈
public XiaoWang(int age):base(age)
{
Console.WriteLine("我是小王,今年" + age);
}
方法的重载(区别于重写)
方法的重载:写了一个同名、但不同参数的的方法。
这两个方法是可以同时存在的。区别在于外界调用的时候,依据参数的个数或者类型,确定调用何种方法。
重写与重载的区别
重写,涉及父类与子类的关系。
重载,是当前类里的关系。
特性3:多态
C#:父类类型可以接收子类对象,以至于调用同样方法产生不同的结果。
C++:同样的指针可以指向不同的子类对象。
接口也是实现多态的一种方式。
实例化时,可用父类类型接收
//实例化一个xb
XiaoBai xb = new XiaoBai();
//NPC xb = new XiaoBai();
NPC npc1 = xb;
多态的作用
如同概念,创建一个父类类型的数组,接收子类对象,以至于调用同样方法产生不同的结果。
此处由于xb和cs都是文字,所以“不同结果”没展示出来。
//new一个NPC数组。数组里包含xb和cs。xb和cs是模具内实例化出的两个变量。
NPC[] npcs = new NPC[] { xb, cs };
/*此处,我们不需要知道xb和cs的具体类型的。
只要知道二者都是一个父类,父类里面是Say方法,
通过遍历,就可以得到二者内容。*/
//快速遍历
foreach (NPC npc in npcs)
{
npc.Say();
//原本调方法是:“名称()”。此处是“实例名称.模具里该方法的名称()”。
}
值类型和引用类型的区别
值类型:其余类型
引用类型:数组、类(别忘了string(数组))、接口、委托
逻辑上将内存分为两部分:栈(一个个格子)、堆(剩余大片区域)。
抽象类(abstract,对应override)
提供了部分功能的实现,不能被实例化的类(不能把它new出来)。除了不能实例化和可能包含抽象方法两个特点,其余使用和正常类一样。
父类
//抽象类(不能被实例化) 添加关键字abstract
public abstract class NPC
{
/*可以包含抽象方法:子类必须实现的方法。
抽象方法只能包含在抽象类当中。添加关键字abstract。*/
public abstract void Say();
/*抽象方法是子类必须实现的方法,在本类当中是不允许实现的。
所以,把原本的{}去掉,直接分号结束。
{}是方法体,有{}即代表方法实现。*/
}
子类
public class XiaoBai : NPC //创建XiaoBai,XiaoBai继承NPC
{
//此处,XiaoBai必须把父类不可以实现的方法实现。
//添加关键字override
public override void Say()
{
}
}
外界
/*NPC npc1 = new NPC();
因为此处NPC是个抽象类,不能new,只能new XiaoBai()。*/
//一new XiaoBai(),可以用NPC接收。这是因为多态性依旧保留。
NPC npc = new XiaoBai();
接口
接口写法(interface)
同样不能被实例化,包含的方法必须在继承该接口的类上实现。一个类可以继承多个接口。(c#是单继承(只有一个父类),所以它通过接口实现了多继承的问题)
//创建XiaoBai,XiaoBai继承NPC。
//XiaoBai除了是NPC,还是Enemy。在父类后面添加逗号接口名称。
public class XiaoBai : NPC, Enemy
{
//此处,XiaoBai必须把父类不可以实现的方法实现。
//添加关键字override
public override void Say()
{
}
//必须实现接口里的方法
/*通过接口,强制让XiaoBai实现GetHit(受到攻击)的方法。
XiaoBai就变成了敌人,我们就可以攻击它了。*/
public void GetHit()
{
}
}
//接口 interface
public interface Enemy
{
//接口里面不能写变量,只能写方法。而且方法默认为public,所以不用写public。
void GetHit(); //敌人有个方法是受到攻击。
//谁实现了Enemy这个接口,谁就有了Enemy的特性:受到攻击。
}
接口可以继承
//创建XiaoBai,XiaoBai继承NPC。
//XiaoBai除了是NPC,还是Enemy。在父类后面添加逗号接口名称。
public class XiaoBai : NPC, Boss
{
//XiaoBai必须把父类不可以实现的方法实现。添加override
public override void Say()
{
}
/*必须实现接口里的方法。
注意因为Boss继承了Enemy受伤害的方法,自身又有扔技能的方法,所以它其实是有两个方法!
所以在实现时,注意要实现两个方法!*/
public void GetHit()
{
}
//必须在实现Boss特有的技能
public void Skill()
{
}
}
//接口 interface
public interface Enemy
{
void GetHit(); //敌人有个方法是受到攻击。
}
//接口是可以继承的
public interface Boss : Enemy //Boss继承于Enemy
{
void Skill(); //Boss会扔技能
}
接口也可以实现多态性
委托
第一种写法(需要new)
public class Person
{
/*创建一个委托类型(关键字delegate)。
要求:存放一个返回值为void,有一个参数为int类型的方法,类型为Func。
public delegate void Func(int bbb);
//通过刚才创建的委托,声明一个委托
public Func func;
public Person() //构造方法
{
//委托需要new。在()里写上要保存的方法名称。
//func = new Func(Say); 第一种写法
func(2); //作用就是可以直接将委托当方法去用
}
public void Say(int a)
{
Console.WriteLine("我叫小白");
}
}
第二种写法(利用+=和-=)
public Person() //构造方法
{
//第二种写法:
func += Say;
func(2);
}
这种写法的好处:
委托可以保存多个方法。如果它保存了多个方法,那么在执行时,它保存的方法会被依次调用。