面向对象的三大特性是封装、继承、多态,C#和Java都是面向对象的语言所以他们的类、继承、重写与多态有很多共同点,但是实现上也存在一定的区别。其中Java中其实没有虚函数的概念,也可以认为Java的函数默认都是虚函数都可以被重写;而在C#中要用关键字virtual指明特定的函数为虚函数才可以实现重写。
多态
多态的意思的一个接口或对象的变量在运行时可以有多种不同的形态,这是通过运行时绑定(晚绑定)实现的。
一个接口或对象的变量在运行时可以动态地指向多个对象中的一个,所以多态的结果是直到运行的那一刻才知道具体执行的是哪个对象的方法。
通过继承接口实现多态
假如有一个接口A,A中有一个方法SayHi(),然后B和C实现了接口A,关系如下:
A <- B
A <- C
接口的多态:
- 声明一个接口A的变量:A a = null;
- 在运行的过程中动态地给a赋予B或C的对象
- 调用A的SayHi()方法,这时实现了动态地调用B或C的SayHi()
通过重写方法实现多态
重写指的是在继承的关系中,子类重写(override)了父类的相同签名的方法(相同方法名并且相同的参数)。
假如有一个类A,A中有一个虚函数(virtual)SayHi(),然后B和C继承了类A,并都重写了父类A的方法SayHi()关系如下:
A <- B
A <- C
重写的多态:
- 声明一个父类A的变量:A a = null;
- 在运行的过程中动态地给a赋予B或C的对象
- 调用a的SayHi()方法,这时实现了动态地调用B或C的SayHi()
C#中的重写
C#的重写必须使用关键字virtual和override,其中virtual用于父类的方法,指明该方法为虚函数,override用于子类的相应方法,指明该方法重写父类的方法。
class Program { static void Main(string[] args) { BaseClass obj = new DerivedClass(); obj.SayHi(); Console.ReadKey(); } class BaseClass { public virtual void SayHi() { Console.WriteLine("Hi Base."); } } class DerivedClass : BaseClass { public override void SayHi() { Console.WriteLine("Hi Derived."); } } }
输出:
Hi Derived.
Java中的重写
java默认所有的方法都是虚函数(可以通过final关键字把方法声明为非虚函数),默认都可以被重写,所以java的重写要比C#简单一些:
public class Example { public static void main(String[] args) { BaseClass base = new DerivedClass(); base.SayHi(); } } class BaseClass { public void sayHi() { System.out.println("Hi Base."); } } class DerivedClass extends BaseClass { public void sayHi() { System.out.println("Hi Derived."); } }
输出:
Hi Derived.
C#特有的new修饰符
C#的new修饰符的作用有点难以理解,官方定义是用于隐藏父类具有同签名的方法,隐藏是针对子类的,可以简单理解为:
声明一个和父类具有同样签名的方法并告诉编译器我这个方法和父类的方法没有任何联系。
单单从定义看很难理解它的真正含义,用代码来说话:
class Program { static void Main(string[] args) { BaseClass obj1 = new DerivedClass(); obj1.SayHi(); DerivedClass obj2 = new DerivedClass(); obj2.SayHi(); Console.ReadKey(); } class BaseClass { public void SayHi() { Console.WriteLine("Hi Base."); } } class DerivedClass : BaseClass { public new void SayHi() { Console.WriteLine("Hi Derived."); } } }
输出:
Hi Base.
Hi Derived.
由运行结果可以看出来,obj1变量虽然在运行的过程中被赋予了DerivedClass对象,但是在调用SayHi()的时候实际是是调用了BaseClass的SayHi()。
结果分析:
- 因为new修饰的方法只是隐藏,SayHi()没有被重载,所以obj1的输出是:"Hi Base.",注意这是new修饰词的一个不好理解的坑。
- 在子类中用新的SayHi()隐藏了父类的SayHi(),所以obj2的输出是:"Hi Derived."
由此可见:
隐藏与多态无关
override和new的区别:
- override要和virtual配合使用,而new并不需要(new也可以隐藏非虚函数)
- 用virtual/override是为了实现多态,而new恰好相反
总结
从语法上看Java的重写与多态在实现上要比C#简洁一些,没有那么多的关键字,也没有那么多复杂难懂的语法。但是C#比Java多了一个特性就是子类可以拥有一个和父类具有同样签名的新方法,费那么多力气去换一个也不是太常用的特性是不是有点不值得呢。