多态和覆盖

多态是面向对象编程中最为重要的概念之一,而覆盖又是体现多态最重要的方面。对于像c#和java这样的面向对象编程的语言来说,实现了在编译时只检查接口是否具备,而不需关心最终的实现,即最终的实现方式是在运行时才会决定。这给强类型语言提供了强大的灵活性,请看下面的例子:

 using System;

 namespace study00
{
class Person
{
public string Name { set; get; }
public virtual void sayHello()
{
Console.WriteLine("Hello. My Name is " + Name);
}
} class Student :Person
{
public override void sayHello()
{
Console.WriteLine("Hello, i am a student. My Name is " + Name);
}
} class Teacher :Person
{
public override void sayHello()
{
Console.WriteLine("Hello, i am a teacher. My Name is " + Name);
}
} class Program
{
static void Main(string[] args)
{
Teacher t = new Teacher();
t.Name = "XiaoMing";
doSomething(t); Student s = new Student();
s.Name = "XiaoMing";
doSomething(s);
} static void doSomething(Person p)
{
p.sayHello();
}
} /* Output:
* Hello, i am a teacher. My Name is XiaoMing
* Hello, i am a student. My Name is XiaoMing
*/
}

在上面这段代码中,doSomething方法需要一个Person类型的对象,但是即使给他传递的是Student类型的对象和Teacher类型的对象,她也能够欣然接受,并根据响应的类型作出正确的处理。我们都知道,这是由于Student和Teacher都继承了Person类,当调用doSomething传递参数时,Student类型的对象发生了向上转型,也称为里氏替换,即父类能够出现的地方,子类一定能够进行替换。所以将Student类型的对象传递给doSomething方法,编译不会出错,而且doSomething方法能够正确执行。这便是面向对象给我们提供的便利,即我们不需要判断某个对象属于什么类型,只要他能够满足给定的要求(这里是继承了Person类),就能够正确运行。

实际上,从更抽象的角度来看,由于每一个类都可以看做某个接口的具体实现,Person可以看做是包含了sayHello()方法的接口的实现。那么我们可以说,Person与它的子类Student和Teacher都是同一个接口的实现,那么我们也就可以理解,doSomething()方法可以正确的处理实现了sayHello()方法的类型。对于doSomething()方法,他不需要知道传递过来的参数是Person类的对象还是它的子类,只要该对象实现了sayHello()方法(继承机制使得子类自动继承了父类的所有方法),那么就可以正确执行。

在上面的代码中,Person中已经有了相应的sayHello()方法的实现,但是当我们运行时却发现,程序运行的却是它的子类的同名方法。这就是所谓的覆盖,即子类隐藏了父类中的同名方法,通过覆盖Person的sayHello()方法,Person的子类实现了自己想要实现的方法,同时又可以向上转型为父类去参与doSomething()方法的运行。也就是说,通过覆盖父类的同名方法,程序实现了运行时的多态。

virtual和override关键字

实现多态时,用到了两个关键字,virtual和override,分别用在父类和子类的签名相同的方法中。什么是签名相同呢?签名相同就是方法同名、参数相同(个数相同、类型相同)、返回值相同。virtual用在父类方法中,表示该方法可以被覆盖。override用在子类的同签名方法中,表示重写了父类的同签名方法。override关键字修饰的的方法必须是和父类的方法是签名相同,而且父类的签名相同的方法必须是被virtual、abstract或者override关键字修饰。

new关键字

我们都知道在c#或java中,创建一个新的对象可以使用new关键字。而在c#中,new还有另一种用法,那就是作为方法的修饰符。new作为方法的修饰符起到了隐藏父类签名相同的方法,在某些情况下起到了和override关键字相同的效果,请看下面的代码:

 using System;

 namespace study001
{
class Person
{
public string Name { set; get; }
public virtual void sayHello()
{
Console.WriteLine("Hello. My Name is " + Name);
}
public void sayBey()
{
Console.WriteLine("Bey");
}
} class Student : Person
{
public override void sayHello()
{
Console.WriteLine("Hello, i am a student. My Name is " + Name);
}
public new void sayBey()
{
Console.WriteLine("Bey, don't forget me!");
}
} class OverrideAndNew
{
static void Main(string[] args)
{
Console.WriteLine("********demo1*********");
Student s1 = new Student();
s1.Name = "XiaoMing";
s1.sayHello();
s1.sayBey(); Console.WriteLine("********demo2*********");
Person p = new Student();
p.Name = "LiHua";
p.sayHello();
p.sayBey(); Console.WriteLine("********demo3*********");
Student s2 = new Student();
Person p2 = s2;
p2.Name = "LaoWang";
p2.sayHello();
p2.sayBey();
}
} /* Output:
* ********demo1*********
* Hello, i am a student. My Name is XiaoMing
* Bey, don't forget me!
* ********demo2*********
* Hello, i am a student. My Name is LiHua
* Bey
* ********demo3*********
* Hello, i am a student. My Name is LaoWang
* Bey
*/
}

在上面代码中的第一个示例中,Student类继承了Person类,并且覆盖了Person类的sayHello()方法,而对于父类中的sayBey()方法,子类中有对应的签名相同的方法,但是没有采用override关键字修饰,而是采用了new关键词修饰,并且父类的对应方法没有采用任何的关键字修饰。在这种情况下,从输出结果中可以看出,子类的对象也成功隐藏了父类的sayBey()方法,采用了自己的实现。这样的情况下,只需要子类的同名方法用new关键字修饰即可,貌似比override更加方便。而且更加重要的是,这个new关键字是可以省略的,也就是说默认情况下,子类的同名方法会隐藏父类的方法。

而从上面的第二个和第三个实例中我们就可以看到new关键字和override关键字的不同之处了。在第二个示例中,采用了父类类型Person的变量引用了子类类型Student的对象。在这种情况下,虽然被覆盖的sayHello()方法依然隐藏了父类的实现,采用了子类的实现,但是对于用new关键字修饰的sayBey()方法,却执行了父类的sayBey()方法。实际上,new关键字的作用是隐藏父类的方法,而并不是覆盖父类的方法。两者的不同就在于覆盖是完全覆盖父类的方法,任何时候通过子类对象都无法获取父类的方法;而隐藏则是在某种情况下是可以通过子类的对象去执行父类的方法的,这种情况就是采用了父类的变量引用了子类的具体对象。同样的,第三个实例和第二个实例相似,都是父类的变量引用了子类的对象,所以执行的都是父类的方法。

java中的方法覆盖

在java中,方法的覆盖要简单的多。子类和父类的同名方法不需要添加任何多余的关键字修饰,只要子类的方法与父类的方法签名相同,子类就会自动覆盖父类的方法。也就是说,java中默认实现了virtual和override关键字。而对于c#中的new关键字,java却没有相应的机制实现。这和c#正好是相反的,c#中如果子类的方法和父类的方法签名相同,则会隐藏父类的方法;java中如果子类的方法和父类的方法签名相同,则会覆盖父类的方法。请看下面的代码:

 package study00.override;

 class Person {

     private String name;

     public void sayHello() {
System.out.println("Hello, i am " + this.getName());
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
} class Student extends Person {
public void sayHello() {
System.out.println("Hello, i am a student, i am " + this.getName());
}
} public class Program {
public static void main(String[] args) {
Person p = new Student();
p.setName("XiaoMing");
p.sayHello();
} /**
* Output:
* Hello, i am a student, i am XiaoMing
*/
}

总结

  • 多态是面向对象编程中最为重要的概念之一,而覆盖又是体现多态最重要的方面。
  • c#中可以采用virtual和override关键字实现方法的覆盖,无法通过子类的对象获得父类的同名方法调用。override修饰的方法只能是父类中被abstract、virtual或者override关键字修饰的方法的同名方法。
  • c#中可以采用new关键字修饰来实现隐藏父类的方法,在引用变量和实际对象的类型一致时与override关键字的效果相同,在引用变量是实际对象的父类会执行父类的方法,此时父类的方法不会被覆盖。
  • java中默认实现覆盖,但是没有与c#中new关键字效果相同的机制。

c#和java中的方法覆盖——virtual、override、new的更多相关文章

  1. java中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思?重写跟重载的区别?

    java中的方法重载发生在同一个类里面两个或者多个方法的方法名相同但是参数不同的情况.与此相对,方法覆盖是说子类重新定义了父类的方法.方法覆盖必须有相同的方法名,参数列表和返回类型. 覆盖者可能不会限 ...

  2. static关键字什么意思?Java中是否可以覆盖一个private或者是static的方法?

    答案:“static”关键字表明一个成员变量或者是成员方法可以在没有所属的类的实例变量的情况下被访问.Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译 ...

  3. JAVA - 请说明”static”关键字是什么意思?Java中是否可以覆盖(override)一个private或者是static的方法?

    请说明"static"关键字是什么意思?Java中是否可以覆盖(override)一个private或者是static的方法? "static"关键字表明一个成 ...

  4. java中equals方法和==的用法

    java中equals方法的用法以及==的用法(参考一)equals 方法是 java.lang.Object 类的方法.两种用法说明:(1对于字符串变量来说,使用“==”和“equals()”方法比 ...

  5. Java中的方法内联

    Java中的方法内联 1. 什么是方法内联 例如有下面的原始代码: static class B { int value; final int get() { return value; } } pu ...

  6. Java中,方法的重写、重载的区别,以及多态的实例

    首先我们要明白什么是重写和重载 重写(override):子类方法覆盖了父类的方法.    (类与类之间继承的关系) 例:父类代码 public class Deng { public void Qi ...

  7. Java中的方法应用

    一.如何定义java中的方法 所谓方法,就是用来解决一类问题的代码的有序组合,是一个功能模块. 语法: 1. 访问修饰符:方法允许被访问的权限范围, 可以是 public.protected.priv ...

  8. Java中的方法(形参及实参)return返回类型

    如何定义 Java 中的方法 所谓方法,就是用来解决一类问题的代码的有序组合,是一个功能模块. 一般情况下,定义一个方法的语法是: 其中: 1. 访问修饰符:方法允许被访问的权限范围, 可以是 pub ...

  9. 方法重载(overroad)和方法覆盖(override)------java基础知识总结

    a.什么是方法重载?(同一个类中)方法重载是指在同一个类中,出现方法名相同,参数列表不同的情况. b.什么是方法覆盖?(子父类中)方法覆盖是指在子类中,出现和父类一模一样的方法声明的时候,会运行子类的 ...

随机推荐

  1. Struts2, jquery, select二级联动

    1. 下载jquery.js文件放在webroot下js文件夹里 2. 配置struts.xml: <package name="default" namespace=&qu ...

  2. 고 해서: 表示在做B的各种理由中, A是代表性的理由

    1. 날씨도 좋고해서 산책이나 하려고 해요. 2. 할 일도 없고해서 일찍 돌어왔어요. 3. 기분도 우울하고 해서 친구란 술 마시기로 했어요. 可以加过去式和将来时使用 1. 수업도 끝 ...

  3. Entity Framework 学习初级篇6--EntityClient

    System.Data.EntityClient 命名空间是 实体框架的 .NET Framework 数据提供程序.EntityClient 提供程序使用存储特定的 ADO.NET 数据提供程序类和 ...

  4. MediaScanner与音乐信息扫描==

    http://www.eoeandroid.com/forum.php?mod=viewthread&tid=98713 =================================== ...

  5. zabbix agent自动安装脚本

    #!/bin/bash #desc: used for autoinstall zabbix client #说明:本脚本旨在批量安装zabbix_agent,在一个服务器上放好软件和配置文件,执行本 ...

  6. linux proxy

    ALL_PROXY=socks://192.168.2.1:3128/ HTTPS_PROXY=https://192.168.2.1:3128/HTTP_PROXY=http://192.168.2 ...

  7. Block 进阶

    转载自:http://www.cnblogs.com/xiaofeixiang/p/4666796.html 关于Block之前有一篇文章已经写过一篇文章Object-C-代码块Block回顾,不过写 ...

  8. (转) hadoop 一个Job多个MAP与REDUCE的执行

    http://blog.csdn.net/chaoping315/article/details/6221440 在hadoop 中一个Job中可以按顺序运行多个mapper对数据进行前期的处理,再进 ...

  9. android bitmap compress(图片压缩)

    android bitmap compress android的照相功能随着手机硬件的发展,变得越来越强大,能够找出很高分辨率的图片. 有些场景中,需要照相并且上传到服务,但是由于图片的大小太大,那么 ...

  10. Contaminated Milk

    Contaminated Milk 题目描述 Farmer John, known far and wide for the quality of the milk produced on his f ...