参考网址:http://xm-king.iteye.com/blog/765334
重载与覆盖区别
有时候,类的同一种功能有多种实现方式,到底采用那种实现方式,取决于调用者给定的参数。例如 杂技师能训练动物,对于不同的动物有不同的训练方式。
再如某个类的一个功能是比较两个城市是否相同,一种方式是比较两个城市的名字,一种是除了比较两个城市的名字外,还要比较所在国家的名字。
在例如 java.lang.Math 类的 max ()方法能够从两个数字中取出最大值,它有多种实现方式。
以下程序多次调用Math 类的max() 方法,运行时,Java 虚拟机先判断给定参数的类型,然后决定到底执行哪个 max()方法。
// 参数为 int 类型,因此执行max(int a, int b)方法
Math.max(1,2);
//参数为 flloat 类型,因此执行 max(float a, float b) 方法
Math.max(1.0F,2.9F);
对于类的方法(包括从父类中继承的方法)如果有两个方法的方法名相同,但参数不一致,那么可以说,一个方法是另一个方法的重载方法。
重载方法满足以下条件
方法名相同
方法的参数类型,个数,顺序至少有一项不相同
方法的返回类型可以不相同
方法的修饰符可以不相同
在一个类中不允许定义另个方法名相同,并且参数签名也完全相同的方法。假如存在这样的两个方法,Java 虚拟机 在运行时就无法决定到底执行哪个方法。参数签名是指参数的类型,个数和顺序。
例如 :
尽管它的参数列和 类中方法参数不一样,但是,此处的Sample 类不是抽象类,所以不能包括这个抽象方法。假如把Sample 类改为抽象类,就能把这个方法加入到 Sample 类中了。
(源码)
编译错误:
方法覆盖
假如有100个类,分别是 Sub1,Sub2,Sub3…….Sub100 ,它们的一个共同行为是写字,除了Sub1用脚写字外,其他都用手写字。可以抽象一个父类Base,它有一个表示写字的方法 write(),那么这个方法到底如何实现呢? 从尽可能提高代码可重用性的角度看,write() 方法应该采取适用于大多数子类的实现方式,这样就可以避免在大多数子类中重复定义 write()方法。因此Base 类的 write() 方法定义如下:
public void write(){ // Base 类的 write() 方法 用手写字}
由于 Sub1 类的写字的实现方式与Base 类不一样,因此在Sub1类中必须重新定义 write() 方法。
public void write(){// Sub1 类中的 write() 方法 // 用脚写字}
如果在子类中定义的一个方法,其名称,返回类型及参数签名正好与父类中某个方法的名称,返回类型及参数签名相匹配,那么可以说,子类的方法覆盖了父类的方法。
覆盖方法 必须满足多种约束
1)子类方法的名称,参数签名和返回类型必须与父类方法的名称,参数签名和返回类型一致
例如,下列代码将发生编译错误
method() in Simon.Sub cannot overrid method() in Simon.Base;
attempting to use incompatible return type
在Simon 包 Sub 中的方法不不能重写(覆盖) 在 Simon 包 Base类中的方法试图用不匹配的返回类型,Java编译器首先判断Sub 类的 method()方法与 Base 类的 method() 方法的参数签名。由于两者一致,所以Java 编译器就认为 Sub 类 的 method() 方法试图覆盖父类的方法,既然如此,Sub 类的 method() 方法就必须和被覆盖的方法具有相同的返回类型。
以下代码中子类覆盖了父类的一个方法,然后又定义了一个重载方法,这是合法的。
2) 子类方法不能缩小父类方法的访问权限。例如以下代码中子类的 method() 方法是私用的,父类的 method()方法是公共的,子类缩小了 父类方法的访问权限,这是无效的方法覆盖,将导致编译错误。
method() in Simon.Sub cannot override method() in Simon.Base;
attempting to assign weaker access privileges ;
was public Simon 包中 的 Sub 类 method()不能重写、覆盖 Simon 包中Base类的 method()方法。 试图分配一个更弱的访问权限原来是 public (现在却是 private) 为什么子类方法不允许缩小父类方法的访问权限呢?这时因为假如没有这个限制,将于Java 语言的多态机制发生冲突。Base base = new Sub() ;//base 变量被定义为Base 类型,但引用 Sub 的实例。
base.method(); Java 编译器认为以上是合法的代码,但是在运行时,根据动态绑定规则,Java 虚拟机会调用base 变量所引用的Sub 实例的 method()方法,如果这个方法为 private 类型,Java 虚拟机就没有办法访问它.所以为了避免这样的矛盾,Java 语言不允许子类方法缩小父类中被覆盖方法的权限。
3)子类方法不能抛出比父类方法更多的异常。
子类方法抛出的异常必须和父类方法抛出的异常相同,或者子类方法抛出的异常类是父类抛出的异常类的子类。
假设异常类ExceptionSub1 和 ExceptionSub2 是 ExceptionBase 类的子类,则以下代码是合法的。
为什么子类不允许抛出比父类方法更多的异常呢?这时因为假如没有这个限制,将会与Java 语言的多态机制发生冲突。
Java 编译器认为以上是合法的代码。但在运行时,根据动态绑定规则,Java虚拟机会调用 base 变量所引用的Sub2 实例的 method() 方法。 假如 Sub2 实例的 method() 方法抛出 ExceptionSub2 异常,由于该异常没有被捕获,将导致程序异常终止。
4)方法覆盖只存在于子类和父类(包括直接父类和间接父类)之间,在同一个类中方法只能被重载,不能被覆盖。
5)父类的静态方法不能被子类覆盖为非静态方法。
例如:
method () in Simon.Sub cannot override method() in Simon.Base;
Overridden method is static
在包Simon ,Sub类中的 method() 方法不能够重写 Base 类中的 method()方法。 被重写的方法是静态的。
6)子类可以定义与父类的静态方法同名的静态方法,以便在子类中隐藏父类的静态方法。
在编译时,子类定义的静态方法也必须满足与方法覆盖类似的约束:
方法的参数签名一致,返回类型一致,不能缩小父类方法的访问权限,不能抛出更多的异常。以下代码是合法的。
public class Base {
static int method(int a)throws BaseException{ return 0 }
}
public class Sub extends Base{
public static int method(int a)throws SubException{ return 0} }
子类隐藏父类的静态方法和子类覆盖父类的实例方法,这两者的区别就在于:运行时,JVM把静态方法和所属的类绑定,而把实例方法和所属的实例绑定。
引用变量sub1 和 sub2 都引用 Sub 类的实例,java 虚拟机在执行 sub1.method() 和 sub2.method() 时,都调用Sub 实例的 method()方法,此时父类Base 实例方法 method() 被子类覆盖。
引用变量sub1 被声明为Base 类型,java 虚拟机在执行 sub1.staticMethod()时,调用Base 类的staticMethod()方法,可见父类Base 类的静态方法staticMethod ()不能为被子类覆盖。
引用变量 sub2 被声明为 Sub 类型,java 虚拟机在执行sub2.staticMethod()时,调用Sub 类的 staticMethod 方法,Base 类的staticMethod()方法隐藏。
7)父类的非静态方法不能被子类覆盖为静态方法。
以下代码编译有错误
method () in Simon.Sub cannot override method() in Simon.Base;
overriding method is static .
8)父类的私有方法不能被子类覆盖
子类方法覆盖父类方法的前提是:子类必须能继承父类的特定方法。
由于父类中的某个私有方法无法被子类访问,继承,所以也不存在被子类覆盖的关系。
示例:
执行以上Sub 类的 main() 方法,会打印出结果 Base ,这时因为print()方法在Base 类中定义,因此print()方法会调用在Base 类中定义的 private 类型的 showMe() 方法
如果把Base类的showMe() 方法改为 public 类型,其他代码不变
public String showMe(){
return "Base";
}
在执行以上Sub 类的main ()方法,会打印出 Sub,这是因为此时Sub 类的 showMe() 方法覆盖了 Base 类的showMe() 方法。因此尽管print()方法在Base 类中定义,JVM 还是会调用当前Sub 实例的 ShowMe 方法。
9)父类的抽象方法可以被子类通过两种途径覆盖掉,一是子类实现父类的抽象方法,二是子类重新声明父类的抽象方法。
狭义的理解,覆盖仅指子类覆盖父类的具体方法,即非抽象方法,在父类中提供了方法的默认实现方式,而子类采用不同的实现方式。
例如,以下代码编译会有误。
Java 方法重载 (OverLoad)与方法覆盖(Override)的区别
方法覆盖和方法重载具有以下相同点
都要求方法名相同
都可以用于抽象方法和非抽象方法之间
不同点
方法覆盖要求参数签名必须一致,而方法重载要求参数签名必须不一致
方法覆盖返回类型必须一致,方法重载对此不做限制
方法覆盖只能用于子类覆盖父类的方法,方法重载用于同一个类的所有方法(包括从父类中继承而来的方法)
方法覆盖对方法的访问权限和抛出的异常有特殊的要求,而方法重载在这方面没有任何限制
父类的一个方法只能被子类覆盖一次,而一个方法在所在类中可以被重载多次