有时候,类的同一种功能有多种实现方式,到底采用那种实现方式,取决于调用者 给定的参数。例如杂技师能训练动物,对于不同的动物有不同的训练方式。
public void train (Dog dog){
// 训练小狗站立,排队,做算数
}
public void train(Monkey monkey){
// 训练猴子骑自行车等
}
再如某个类的一个功能是比较两个城市是 否相同,一种方式是比较两个城市的名字,一种是除了比较两个城市的名字外,还要比较所在国家的名字。
publi boolean isSameCity (String city1,String city2){
return city1.equals(city2);
}
public boolean isSameCity(String city1,String city2,Stirng Country1,String Country2){
return isSameCity(city1,city2)&&Country1.equals(Country2);
}
在例如 java.lang.Math 类的 max ()方 法能够从两个数字中取出最大值,它有多种实现方式。
public static int max(int a,int b)
public static int max(long a, long b)
public static int max(float a,float b)
以下程序多次调用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 虚拟机 在运行时就无法决定到底执行哪个方法。参数签名是指参数的类型,个数和顺序。
例如 :public class Sample {
· public void amethod(int i,String s){} }
下面哪个方法可以加入到 Sample 类中去?
· public void amethod(String s,int i) //OK
· public int amethod(int i,String s){return 0} //NO
¨ // 不行,参数签名和类中方法一样
· public void amethod(int i,String myString){} //NO
¨ // 不行,参数签名和类中方法一样
· public void Amethod (int i,Sting s){} // OK
¨ // 可以,因为 Amethod 和amethod 是两个不同的方法名称。
· abstract void amethod( int i); //NO
¨ 尽管它的参数列和类中方法参数不一样,但是,此处的Sample 类不是抽象类,所以不能包括这个抽象方法。假如把Sample 类改为抽象类,就 能把这个方法加入到 Sample 类中了。
· (源码)
· public boolean compareCity(String city1,String city2){
· return city1.equals(city2);
· }
·
· public int compareCity(String city1,String city2){
·
· if(city1.equals(city2)){
· return 1;
· }else{
· return 0;
· }
· }
· 编译错误:compareCity(java.lang.String,java.lang.String) is already defined
· // compareCity(String ,String ) 方法已经被定义过
作为程序的入口 main ()方法也可以被重载。
public static void main(String args[]){
}
public void main(String s,int i){} // 可以
private void main(int i,String myString []){} // 可以
public void main(String s)throws Exception{} // 可以
public final static int main(String args[]){} // 不可以
它已经和已有的 main ()方法有相同的签名,因此不允许再加到这个类中来。
main(java.lang.String []) is already defined in 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 ) 子类方法的名称,参数签名和返回类型必须与父类方法的名称,参数签名和返回类型一致
· 例如,下列代码将发生编译错误
¨ public class Base{
¨ public void method(){ ……….}
¨ }
¨ public class Sub extends Base{
¨ public int method(){ ……………. return 0};// 编译错误
¨ }
¨ 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() 方法就必须和被覆盖的方法具有相同的返回类型。
· 以下代码中子类覆盖了父类的一个方法,然 后又定义了一个重载方法,这是合法的。
¨ public class Base{
¨ public void method(){ …………..}
¨ }
¨ public class Sub extends Base{
public void method(){ ……….}// 覆 盖 Base 类的method 方法
public int mehod(int a){ ………return 0.} // 重载method 方法
¨ }
2) 子类方法不能缩小父类方法的访问权限。 例如以下代码中子类的 method() 方法是私用的,父类的 method () 方法是公共的,子类缩小了 父类方法的访问权限,这是无效的方法覆盖,将导致编译错误。
¨ public class Base{
¨ public void method(){ …………..}
¨ }
¨ public class Sub extends Base{
private void method(){ ……….} // 覆盖 Base 类的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 类的子类,则以下代码是合法的。
¨ public class Base{
¨ void method () throws ExceptionBase{}
¨ }
¨ public class Sub1 extends Base{
¨ void method () throws ExceptionSub1{}
¨ }
¨ public class Sub2 extends Base{
¨ void method () throws ExceptionSub1,ExceptionSub2
¨ }
¨ public class Sub3 extends Base{
¨ void methos () throws ExceptionBase
¨ }
¨ 以下代码不合法
¨ public class Base{
¨ void method() throws ExceptionSub1{ }
¨ }
¨ public class Sub1 extends Base{
¨ void method() throws ExceptionBase{ } // 编译出错
¨ }
¨ public class Sub2 extends Base{
¨ void method() throws ExceptionSub1,ExceptionSub2{}// 编译出错
¨ }
· 为什么子类不允许抛出比父类方法更多的异 常呢?这时因为假如没有这个限制,将会与Java 语言的多态机制发生冲突。
¨ Base base = new Sub2() ;//base 变量被定 义为Base 类型,但引用Sub2 的实 例。
¨ try{
¨ base.method();
¨ }catch(ExceptionSub1 e){ ……..} // 仅仅描述ExceptionSub1 异常
· Java 编译器认为以上是合法的代码。但在运行时,根据动态绑 定规则,Java 虚拟机会调用 base 变 量所引用的Sub2 实例的 method() 方 法。
· 假如 Sub2 实例的 method () 方法抛出 ExceptionSub2 异常,由于该异常没有被捕获,将导致程序异常终止。