关于C#中的抽象类、抽象方法和虚方法的探究
2016年的第一篇文章,容我先喷喷新年的情怀,..........,好了,喷的差不多了。
在面向对象中,我们需要对类进行横向和纵向的认识,不同的类有不同特色的成员,同时在不同类的继承中,子类获得父类的成员也是丰富多彩。
先概述:
1.抽象类
抽象类中不是所有的方法都是抽象方法,对于单纯抽象类而言,是限制类的实例化,可以没有抽象方法。甚至没有任何成员也没事。
2.抽象方法
抽象方法的目的是为了让派生类实现其方法,抽象的方法不实现,抽象方法没有方法体,没有大括号。包含抽象方法的类一定是抽象类,所以含有抽象方法(属性)是这个类是抽象类的充分非必要条件。
3.虚方法
虚方法是一种方法,不是抽象的,它有大括号,是实现的,子类可以重写,可以覆盖。虚方法是父类中不一定继承到子类中的,具体由子类决定(这里指的是子类可以重写这个方法,按自己子类的逻辑写,不用父类中的方法的逻辑,但是父类中的实方法就没得选了,会继承下来的,这里重点说的是方法内容上的继承),它与抽象方法不同,虚方法与一般的方法相比,可实现由子类选择是否继承。
上代码:
1.生命类 life
1 public abstract class life //抽象类 2 { 3 public string name; 4 5 public abstract void live(); 6 7 public abstract void death(); 8 9 public virtual void birth() 10 { 11 Console.WriteLine("life类的birth()虚方法"); 12 } 13 14 public void huozhe() 15 { 16 Console.WriteLine("life类的huozhe()方法"); 17 } 18 public life() 19 { 20 Console.WriteLine("life被构造"); 21 } 22 }
2.动物类 animal 继承 life
1 public class animal :life 2 { 3 public animal() 4 { 5 Console.WriteLine("animal被构造"); 6 } 7 public string chi; 8 9 //子类必须实现父类中的所有的抽象方法,即abstract的方法 10 11 public override void live() 12 { 13 Console.WriteLine( "animal类必须实现live的live()抽象方法"); 14 } 15 public override void death() 16 { 17 Console.WriteLine("animal类必须实现live的death()抽象方法"); 18 } 19 20 public override void birth() //重写父类life中的虚方法用override //重写后将以实方法看待 21 { 22 Console.WriteLine("animal完成对life类中的birth()虚方法的重写"); 23 } 24 25 public new void huozhe() //覆盖父类life中的huozhe()的方法 实例化时,用该方法 26 { 27 Console.WriteLine("animal 活着"); 28 } 29 }
3.人类 people 密封类 继承 animal
1 //覆盖与重写的不同 在形式上 2 public sealed class people :animal 3 { 4 public void shuo() 5 { 6 Console.WriteLine("people中的shuo()方法"); 7 } 8 9 public people() 10 { 11 Console.WriteLine("people被构造"); 12 } 13 public new void birth() //对animal完成life中的birth()虚方法进行重写后的birth()(已变为实方法)进行覆盖 14 { 15 Console.WriteLine("people中覆盖animal重写后的birth()虚方法"); 16 } 17 public override void death() //people同样可以重写animal中的death的life的抽出方法的重新实现 18 { 19 Console.WriteLine("people对animal中实现life的death()抽象方法重新实现"); 20 } 21 public new void huozhe() //覆盖父类animal中的huozhe()的方法 实例化时,用该方法 22 { 23 Console.WriteLine("people 活着"); 24 } 25 }
4.女人类 woman 无法继承 people
开始测试
1 //覆盖不会改变父类的方法 重写会改变父类的方法 当以子类创建父类的时候会体现出来 2 class Program 3 { 4 static void Main(string[] args) 5 { 6 people p1 =new people(); life被构造 animal被构造 people被构造 7 // life s1 = new life(); 作为abstract的抽象类不能被声明 8 animal liv1 = new animal(); //life被构造 animal被构造 9 animal p2 = new people(); //父类调用子类对象 10 liv1.death(); //输出animal 中的 11 liv1.huozhe(); //输出animal 中的 12 liv1.live(); //输出animal 中的 13 liv1.birth(); //输出animal 中的 14 Console.WriteLine("---------------------"); 15 people p1 = new people(); 16 p1.death(); //输出people 中的 17 p1.huozhe(); //输出people 中的 18 p1.live(); //输出继承animal 中的 //由于没有重写 19 p1.birth(); //输出people 中的 20 Console.WriteLine("---------------------"); 21 liv1.death(); //输出animal 中的 22 liv1.huozhe(); //输出animal 中的 23 liv1.live(); //输出animal 中的 24 liv1.birth(); //输出animal 中的 25 Console.WriteLine("---------------------"); 26 p2.death();//输出people 中的 27 p2.huozhe();//输出animal 中的 28 p2.live();//输出animal 中的 29 p2.birth();//输出animal中的 30 Console.WriteLine("-----------------------"); 31 Console.WriteLine("---------转换----------"); 32 33 34 // people p3 = new animal(); //出错子类引用不能直接引用父类对象, 除非将父类对象的数据类型强制转换成子类 35 // people p4 = (people)liv1; // 如果你创建实例的时候没有将父类引用到子类对象,是无法转换的 36 // p2.shuo(); //错误,引用不了, 父类引用自雷对象时无法子类中的新方法 37 people p5 = (people)p2; //这样转后,能调用 子类中的新方法,说明父类引用时没用丢到子类中的新方法,只是不能调用 38 p5.shuo(); 39 Console.ReadKey(); 40 } 41 }
测试结果
好了,先细细琢磨吧!
其实执行哪个方法,可以按如下规律:
A a=new D(); a.Fun1() 会从A开始找,最长找到D为止,如果 Fun1() 是实例方法,或者new修饰的,那么就是执行A下的Fun1(),如果是虚方法,或者 override方法,继续给下找,直到首次找到 Fun1()是new 的,它父类的Fun1()方法这个方法就是要执行的, 比如 B:A C:B D:C ,如果 Fun()1 在A是virtual , B中是 override Fun1(),C中是 override Fun1() , 在D 中有new修饰,那么 执行的是C中的Fun1(), 再看如下示例:
其中:
Parent s1 = new LitterSun();
Son s2 = new LitterSun();
Sun s3 = new LitterSun();
Parent s4 = new Sun();
Parent s5 = new Son();
s1.Show(); //输出的嘻嘻
s2.Show(); //输出的嘻嘻
s3.Show(); //输出的嘻嘻
s4.Show(); //输出的嘻嘻
s5.Show(); //输出的嘶嘶
相信 应该对上面结果理解是ok的
而对于这种
Parent s0 = new Son();
s0.Show();
显然应该 应该是先走的 Parent的Show,内部不走Parent的DoSome,走的是Son的DoSome ,输出的是 嘶嘶
再看一个 有意思的
Parent s0 = new Son();
s0.Show();
这个应该输出 哼哼 还是 嘻嘻 还是 嘶嘶了, 其实我们发现virtual 也是具体和new一样的隔断作用的, 这样代码是先走的Parent的show(),因为被Son的virtual隔断了,,然后走Dosome()时选择走 Son的DoSome(),最后输出的是 嘶嘶
同理: 下面这个
Parent s0 = new Sun();
s0.Show(); // 走的Son的Show,走的Sun()的DoSome, 最后输出的是 喔喔 嘻嘻
随便再说一句,如果想起阻段作用的 我们可以使用new进行覆盖变成新实方法,如果还想让子类再继续能重写的话可以再加个 virtual,如下
另外 类中的属性是没有多态性的,即你在引用上面使用属性时,系统只会去找引用的静态类型中的那个属性,而与它的实际类型无关。
静态方法也是没有多态性的。