一、核心知识点:虚方法 VS 抽象方法(必考对比)
虚方法和抽象方法是C#面向对象多态的核心知识点,二者核心区别清晰明确,是面试、考试高频考点,精准区分如下:
|--------|----------------------------------|-----------------------------|
| 对比维度 | 虚方法(virtual) | 抽象方法(abstract) |
| 所属类限制 | 可定义在普通类、抽象类中 | 只能定义在抽象类中 |
| 方法体 | 必须有完整方法体(有实现代码) | 无方法体(仅方法声明,以分号结尾) |
| 子类重写规则 | 可选重写:子类可重写、也可直接使用父类原方法 | 强制重写:子类必须实现所有继承的抽象方法 |
| 核心关键字 | virtual(声明)、override(重写)、new(隐藏) | abstract(声明)、override(强制实现) |
知识点总览(核心4条对比考点)
本节重点区分 虚方法 virtual 和 抽象方法 abstract,是多态必考核心,四条规则必须完全熟记:
-
虚方法可以定义在普通类中,抽象方法只能定义在抽象类中。
-
虚方法可以有方法体(有默认实现),抽象方法不能有方法体(只有声明,无实现)。
-
虚方法在派生类中可以不用重写实现,抽象方法在派生类中必须强制实现。
-
虚方法在子类中可以配合 new(隐藏) 或 override(重写) 使用;抽象方法只能使用 override 实现。
二、虚方法完整概念详解
1. 虚方法定义语法
在父类中使用virtual 关键字修饰的方法,称为虚方法。
语法特征:必须拥有完整方法体,自带默认业务逻辑。
2. 虚方法核心特点
虚方法存在的目的:为子类提供可选择性重写的入口,实现动态多态。
虚方法不强制子类重写:子类如果不重写,默认继承并执行父类原本的方法逻辑;子类如果重写,就覆盖父类逻辑,执行子类自定义逻辑。
3. 虚方法可存放位置
虚方法兼容性极强:既可以写在 普通类 中,也可以写在 抽象类 中。
三、案例代码父类解析(People 父类)
public class People
{
public string Name { get; set; }
// 虚方法:允许子类重写
public virtual void SayHellow()
{
Console.WriteLine("People打招呼");
}
}
代码解析:
-
People 是一个普通类,证明 虚方法可以定义在普通类中。
-
SayHellow 方法带有完整大括号方法体,拥有默认输出逻辑,符合 虚方法必须有方法体 的规则。
-
该方法被 virtual 修饰,所有继承 People 的子类,都可以自由选择:重写该方法 或 不重写、直接使用父类方法。
四、子类重写虚方法详解(override 用法)
1. 重写规则说明
父类虚方法,子类可以使用 override 关键字完成重写覆盖。
重写的作用:完全覆盖父类原有的虚方法逻辑,子类对象调用该方法时,优先执行子类重写后的自定义逻辑。
如果子类不写 override 重写,就默认沿用父类的 SayHellow 方法,不会报错。
2. 多个子类差异化重写(多态核心体现)
同一个父类虚方法,不同子类可以重写出完全不同的效果,这就是多态:同一个行为,不同实现。
① China 子类重写
public class China :People
{
public override void SayHellow()
{
Console.WriteLine("吃了吗,抽个烟");
}
}
中国人打招呼:重写父类方法,自定义专属逻辑。
② Japan 子类重写
public class Japan : People
{
public override void SayHellow()
{
Console.WriteLine("汪汪");
}
}
日本人打招呼:完全不同于父类和其他子类,实现个性化重写。
③ HanGuo 子类重写
public class HanGuo : People
{
public override void SayHellow()
{
Console.WriteLine("啊你赛有");
}
}
韩国人打招呼:独立重写逻辑,体现多态特性。
五、测试代码执行逻辑
China c = new China();
c.SayHellow();
执行结果:吃了吗,抽个烟
逻辑解析:实例化子类对象,调用被 override 重写后的方法,直接执行子类逻辑,不再执行父类虚方法逻辑。
六、虚方法 子类两种处理方式(重点背诵)
方式1:子类不重写虚方法
子类不写 override 方法,调用方法时,执行 父类原本的虚方法逻辑,程序正常运行,无报错。
方式2:子类重写虚方法
子类使用 override 重写,覆盖父类逻辑,实现子类专属功能,是多态的核心用法。
方式3:子类使用 new 隐藏(拓展考点)
虚方法除了override,还可以用new隐藏,生成独立子类方法,不覆盖父类,属于静态绑定。
七、虚方法 VS 抽象方法 终极对比总结(满分简答题)
1. 定义位置不同
虚方法:普通类、抽象类都可以定义;抽象方法:只能定义在抽象类中。
2. 方法体不同
虚方法:有完整方法体,具备默认实现;抽象方法:无方法体,仅声明无实现。
3. 子类实现规则不同
虚方法:子类可重写、可不重写,自由选择;抽象方法:普通子类必须强制重写实现。
4. 修饰搭配不同
虚方法:支持 new 隐藏、override 重写;抽象方法:只能使用 override 强制实现。
八、核心考点总结
-
虚方法不强制子类重写,灵活性高,用于已有通用逻辑,子类可微调的场景。
-
抽象方法强制子类重写,用于没有通用逻辑,必须子类自定义实现的场景。
-
所有子类重写虚方法,实现不同逻辑,是典型的运行时动态多态。
------------------new方法隐藏 & override方法重写------------------
一、学习前置:核心场景
本节知识点基于虚方法(virtual) 展开,仅被virtual 修饰的父类方法,子类才可以使用new 或 override 改写。
重点区分两种对象接收方式(所有考题的核心场景):
-
向上转型 :父类变量接收子类对象
父类 变量 = new 子类(); -
原生接收 :子类变量接收子类对象
子类 变量 = new 子类();
二、完整分层代码案例
1. 父类代码(定义虚方法)
父类中通过virtual 定义3个虚方法,允许子类后续改写,预留多态扩展空间。
// 父类:人类基类
public class People
{
// 虚方法:允许子类隐藏/重写
public virtual void Test1()
{
Console.WriteLine("People的Test1");
}
public virtual void Test2()
{
Console.WriteLine("People的Test2");
}
public virtual void Test3()
{
Console.WriteLine("People的Test3");
}
}
2. 子类代码(两种改写方式对比)
子类继承父类,分别使用 new 方法隐藏、override 方法重写两种方式改写同名虚方法。
// 子类:学生类,继承People父类
public class Student : People
{
// new方式:隐藏父类Test1方法
public new void Test1()
{
Console.WriteLine("Student的Test1");
}
// new方式:隐藏父类Test2方法
public new void Test2()
{
Console.WriteLine("Student的Test2");
}
// override方式:重写覆盖父类Test3方法
public override void Test3()
{
Console.WriteLine("Student的Test3");
}
}
3. 测试调用代码(核心测试逻辑)
class Program
{
static void Main(string[] args)
{
// 1. 向上转型:父类变量接收子类对象
People p1 = new Student();
// 2. 原生接收:子类变量接收子类对象
Student s1 = new Student();
// 【new 方法隐藏】调用测试
p1.Test1(); // 父类变量 → 执行父类方法
s1.Test1(); // 子类变量 → 执行子类方法
p1.Test2(); // 父类变量 → 执行父类方法
s1.Test2(); // 子类变量 → 执行子类方法
// 【override 方法重写】调用测试
p1.Test3(); // 无论变量类型,执行子类重写方法
s1.Test3(); // 无论变量类型,执行子类重写方法
}
}
三、程序运行结果
People的Test1
Student的Test1
People的Test2
Student的Test2
Student的Test3
Student的Test3
四、new 方法隐藏 核心详解(Test1、Test2)
1. 本质
new 是方法隐藏,不会覆盖删除父类的虚方法。
相当于:子类新建了一个同名、独立的全新方法,父类原方法完整保留,父子两个同名方法互不干扰、同时存在。
2. 核心调用规则(必考死记)
new 隐藏:看【变量的类型】(编译类型)
-
父类变量接收子类对象 → 调用 父类方法
-
子类变量接收子类对象 → 调用 子类方法
3. 代码逻辑对应
p1.Test1() / p1.Test2():变量是父类类型,执行父类原有方法
s1.Test1() / s1.Test2():变量是子类类型,执行子类新建的隐藏方法
4. 特性总结
new 隐藏没有多态性,属于静态绑定,编译阶段就确定了调用哪个方法。
五、override 方法重写 核心详解(Test3)
1. 本质
override 是方法覆盖重写,会彻底替换、覆盖父类的虚方法。
父类原本的虚方法会被覆盖失效,程序中只会保留子类重写后的方法逻辑。
2. 核心调用规则(必考死记)
override 重写:看【对象的实际类型】(运行类型)
-
只要 new 的是子类对象,不管用父类变量还是子类变量接收
-
一律执行 子类重写后的方法
3. 代码逻辑对应
p1.Test3():变量是父类,但对象本质是子类 → 执行子类重写方法
s1.Test3():变量、对象都是子类 → 执行子类重写方法
4. 特性总结
override 重写具备动态多态性,属于运行期绑定,程序运行时动态判断方法。
六、new 与 override 终极对比(考试满分版)
| 对比维度 | new 方法隐藏 | override 方法重写 |
|---|---|---|
| 核心本质 | 子类新建独立同名方法,父类方法保留 | 覆盖替换父类虚方法,父类方法失效 |
| 调用依据 | 根据变量类型判断 | 根据对象实际类型判断 |
| 多态性 | 无动态多态(静态绑定) | 实现动态多态(运行绑定) |
| 场景特点 | 父子方法相互独立,互不影响 | 统一重写逻辑,实现多态效果 |
七、极简背诵口诀(做题秒杀)
new 看变量,父变父、子变子,分家两路
override 看对象,只要是子类,统统走子类
八、简答题标准答案(直接默写)
问:简述 new 隐藏和 override 重写的区别?
答:new 是方法隐藏,子类会生成独立的同名方法,父类原有方法保留,调用方法由变量类型决定,不具备动态多态;override 是方法重写,会覆盖父类的虚方法,调用方法由对象实际类型决定,无论变量类型如何,都会执行子类重写的方法,是实现动态多态的核心方式。