文章目录
一、方法签名
- 在Java中,所谓的方法签名就是 方法名 + 参数类型
- 例如一个方法
public void fun(int a) throws IOException{ }
,这里的方法签名就是fun(int)
- 注意: 返回类型不是方法签名的一部分
二、方法重载(Overload)
- 首先我们先来看一下方法重载的定义
方法重载是指在一个类中定义多个同名的方法,但要求每个方法具有不同的参数的类型或参数的个数。调用重载方法时,Java编译器能通过检查调用的方法的参数类型和个数选择一个恰当的方法。方法重载通常用于创建完成一组任务相似但参数的类型或参数的个数或参数的顺序不同的方法。 [1] Java的方法重载,就是在类中可以创建多个方法,它们可以有相同的名字,但必须具有不同的参数,即或者是参数的个数不同,或者是参数的类型不同。调用方法时通过传递给它们的不同个数和类型的参数,以及传入参数的顺序来决定具体使用哪个方法
- 然后我们再来探讨以下几种情况是否算方法重载?
2.1 方法同名且参数列表相同,但是返回值不同?
- 假如一个类中有
int fun(int a)
和double fun(int a)
两个方法,那么算方法重载吗?直接上代码
public class MyOverload {
public void fun(int a) {
}
public double fun(int a) {
return 0.0;
}
}
- 运行结果
- 编译报错,错误信息为:
'fun(int)' clashes with 'fun(int)'; both methods have same erasure 'fun(int)' is already defined in 'thinking.about_override_overload.MyOverload'
,大概意思就是说类中有相同的方法签名fun(int)
- 编译报错,错误信息为:
- 结论
- 如果两个方法的方法名和参数列表相同 ,但是返回值不相同,那么这样是不算重载的。因为方法名和参数列表相同,就是方法签名相同,而同一个类是 不允许有两个相同的方法签名存在的
2.2 方法同名,但是参数列表和返回值不同?
- 假如对上面例子的代码进行修改,如下所示
public class MyOverload {
public void fun(int a) {
}
public double fun(int a, int b) {
return 0.0;
}
}
- 运行结果
- 编译通过
- 结论
- 如果两个方法的方法名相同,但是参数列表和返回值都不同,那么这样是属于方法重载的。因为方法重载与返回值无关,两个方法既然参数列表不同,那么也就代表方法签名不同(方法名相同),所以属于重载。
2.3 方法同名且参数列表相同,但是抛出异常不同?
- 直接上代码
public class MyOverload {
public void fun(int a) throws IOException {
}
public void fun(int a) throws CloneNotSupportedException {
}
}
- 其实从上面两个例子就可以推出答案了。因为无论你怎么改变抛出的异常,这两个方法的方法签名是一样的,肯定不可以通过编译,所以也不属于重载。
2.4 小结
- 方法是否构成重载, 只与方法名和参数列表有关,也就是方法签名。当方法名相同且参数列表不同时,那么就构成了重载。 记住,方法重载与以下几点无关:
- 方法重载与 返回值无关
- 方法重载与 所抛出异常无关
- 方法重载与 参数名称无关
- 对于为什么方法重载只和方法名和参数列表有关,而和返回值无关呢?在《Java编程思想(第4版)》中有这么一段话:
5.2.3 以返回值区分重载方法
读者可能会想:“在区分重载方法的时候,为什么只能以类名和方法的参数列表作为标准呢?能否考虑用方法的返回值来区分?”
比如下面两个方法,虽然它们有同样的名字和形式参数,但却很容易区分他们:
void f(){}
it f(){return 1;}
只要编译器可以根据语境明确判断出语义,比如在int x=f()中,那么的确可以据此区分重载方法。不过,有时你并不关心方法的返回值,你想要的是方法调用的其他效果(这常被称作为“为了副作用而调用”),这时你可能会调用方法而忽略其返回值。所以,如果像下面这样调用方法:
f();
此时Java如何才能判断该调用那一个f()呢?别人该如何理解这中代码呢?
因此 ,根据方法的返回值来区分重载方法是行不通的。
- 也就是说,假如Java允许一个类中有两个具有相同方法签名而返回值不同的方法存在,那么当我们忽略返回值而调用方法的时候,JVM将不知道调用哪个方法,所以这是不允许的。
三、方法重写(Override)
- 我们一样先来看一下方法重写的定义
- 在Java和其他一些高级面向对象的编程语言中,子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖。
- 若子类中的方法与父类中的某一方法 具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法。 如需父类中原有的方法,可使用super关键字,该关键字引用了当前类的父类。
- 了解完定义后,我们来验证以下情况是否符合方法重写
3.1 重写父类方法是否可以更改参数列表?
- 首先定义父类A,其中的方法声明为 protected void fun(int a, int b)
public class A {
protected void fun(int a, int b) {
}
}
- 定义类B继承类A,并将其方法声明改为 protected void fun(int a)
public class B extends A {
@Override
protected void fun(int a) {
}
}
- 运行结果
- 如果有@Override注解的话会编译报错,错误信息为
Method does not override method from its superclass
大概意思是说该方法不是从父类所继承,所以不能加@Override注解
- 如果有@Override注解的话会编译报错,错误信息为
- 结论
- 重写父类方法不可以更改参数列表,编译报错的原因是加上了@Override注解,而此时的方法签名与父类方法签名不一致,所以出错。去掉@Override的话不报错,但是由于方法签名不一致,已经不算重写了
3.2 重写父类方法是否可以更改返回值?
- 从上面的例子我们不妨大胆猜想,如果返回值与父类不一致,那么在加上@Override的情况下就会报错。真实情况是否是这样呢?我们来验证一下
- 第一种情况:当返回值是基本数据类型时,更改子类B的代码如下,将其返回值改为
void
public class B extends A {
@Override
protected double fun(int a, int b) {
return 0.0;
}
}
- 运行结果
- 编译报错,错误信息为
'fun(int, int)' in 'thinking.about_override_overload.B' clashes with 'fun(int, int)' in 'thinking.about_override_overload.A'; attempting to use incompatible return type
,大概意思是说B类中的fun()与A类中的fun()返回类型冲突
- 编译报错,错误信息为
- 结论
- 当返回值是基本数据类型时方法重写不可以更改返回值
- 第二种情况:当返回值不是基本数据类型,也就是引用数据类型时,更改A类和B类的代码如下
public class A {
protected A fun(int a, int b) {
return new A();
}
}
public class B extends A {
@Override
protected A fun(int a, int b) {
return new A();
}
}
- 运行结果
- 编译通过
- 继续修改子类B,将其返回值改为
B
(B 是 A 的子类)
public class B extends A {
@Override
protected B fun(int a, int b) {
return new B();
}
}
- 运行结果
- 程序一样编译通过
- 继续修改子类B,将其返回值修改为 Object (Object 是 A 的父类)
public class B extends A {
@Override
protected Object fun(int a, int b) {
return new Object();
}
}
- 运行结果
- 编译报错,提示类型冲突。
- 结论
- 从上面的例子我们可以得出,重写方法时是否可以更改返回值 由根据父类方法的返回值决定*
- 如果父类方法返回值是基本数据类型,那么子类重写父类方法时 不可以更改其返回值
- 如果父类方法返回值是引用数据类型,那么子类重写父类方法时 可以更改其返回值,但是前提是子类方法的返回值类型必须与父类方法的返回值类型一致,或者子类方法的返回值类型是父类返回值类型的子类
- 从上面的例子我们可以得出,重写方法时是否可以更改返回值 由根据父类方法的返回值决定*
3.3 重写父类方法是否可以更改访问权限修饰符?
- 父类A的fun()的访问权限修饰符为
protected
,将子类B的fun()的访问权限修饰符改为private
,直接上代码
public class B extends A {
@Override
private void fun(int a, int b) {
}
}
- 运行结果
- 编译报错,错误信息为
'fun(int, int)' in 'thinking.about_override_overload.B' clashes with 'fun(int, int)' in 'thinking.about_override_overload.A'; attempting to assign weaker access privileges ('private'); was 'protected'
,大概意思是说子类使用了更严格的访问权限
- 编译报错,错误信息为
- 结论
- 方法重写可以更改访问权限修饰符,但是前提是子类的访问权限不能比父类更严格。也就是说,如果父类使用的是public,那么子类就不可以使用protected、default、private;如果父类使用的是protected,那么子类就不可以使用default、private。以此类推…
- 说明
- 访问权限的范围从大到小依次是:public > protected > default(包访问权限) > private
3.4 重写父类方法是否可以更改所抛出异常?
- 修改父类A,为其抛出 IOException
public class A {
protected void fun(int a, int b) throws IOException {
}
}
- 修改子类B,为其抛出 IOException 和 CloneNotSupportedException
public class B extends A {
@Override
protected void fun(int a, int b) throws IOException, CloneNotSupportedException {
}
}
- 运行结果
- 编译报错,出错信息为
'fun(int, int)' in 'thinking.about_override_overload.B' clashes with 'fun(int, int)' in 'thinking.about_override_overload.A'; overridden method does not throw 'java.lang.CloneNotSupportedException'
,意思是说类B的fun()不能抛出CloneNotSupportedException
- 编译报错,出错信息为
- 继续修改子类B,将抛出的异常全部去除
public class B extends A {
@Override
protected void fun(int a, int b) {
}
}
- 运行结果
- 编译通过
- 继续修改子类B,为其抛出 Exception
public class B extends A {
@Override
protected void fun(int a, int b) throws Exception {
}
}
- 运行结果
- 编译不通过,因为抛出的 Exception是IOException的父类
- 继续修改子类B,为其抛出 IOException 和 FileNotFoundException
public class B extends A {
@Override
protected void fun(int a, int b) throws IOException, FileNotFoundException {
}
}
- 运行结果
- 编译通过,因为 FileNotFoundException是IOException的子类
- 结论
- 方法重写可以更改抛出的异常,但是前提是子类方法所抛出的异常不能比父类方法所抛出的异常范畴更大
- 也就是说,假如父类抛出IOException,那么子类不能抛出Exception (因为其是IOException的父类),也不可以多抛出CloneNotSupportedException (因为CloneNotSupportedException并不是IOException的子类),但是可以多抛出一个FileNotFoundException (因为其是IOException的子类)
3.5 小结
- 对于方法重写来说,需要注意以下几点
- 发生方法重写的两个方法 返回值、方法名、参数列表必须完全一致 (子类重写父类的方法)
- 子类抛出的异常下 不能超过父类相应方法抛出的异常 (子类异常不能大于父类异常)
- 子类方法的 访问级别不能比父类相应方法的访问级别更严格 (子类访问级别不能低于父类访问级别)
- 子类重写父类方法的 返回值类型不能大于父类方法的返回值类型,即是说 子类方法的返回值必须和父类方法的返回值相同或是其子类
4. 重载和重写的区别
- 从定义来说
- 重载是函数名称相同,参数的类型或者个数不同
- 重写是函数名称、参数类型及个数完全相同
- 从范围来说
- 重载是发生在一个类中
- 重写是发生在继承关系中
- 从限制来说
- 重载没有访问权限和返回值的限制
- 重写不能拥有比父类更严格的访问权限,不能抛出比父类范畴更大的异常,返回值不能是父类方法返回值的父类