重载(overload)
重载概念
在一个类里,两个或者两个以上的同名方法,只要参数列表不同(不同的参数类型或者参数顺序或者参数个数),即可称之为该方法重载了。编译器必须挑选出具体执行哪个方法,它通过用各个方法给出的参数类型与特定方法调用所使用的值类型进行匹配来挑选出相应的方法。如果编译器找不到匹配的参数,或者找出多个可能的匹配,就会产生编译时错误(这个过程被称为重载解析(overloading resolution))。重载方法时,最好加上@Overload注解
class Demo{
public void overload() {
}
public void overload(int s, double d) {
}
public void overload(double d, int s) {
}
private void overload(double d) {
}
private int overload(int s, int t) {
return 1;
}
}
重载规则
l 被重载的方法必须改变参数列表(参数个数或类型不一样);
l 被重载的方法可以改变返回类型;
l 被重载的方法可以改变访问修饰符;
l 被重载的方法可以声明新的或更广的检查异常;
注:无法以方法返回值类型和访问修饰符作为区分重载函数的标准。如下所示的3个方法并没有实现重载,编译器编译无法通过
public void overload() {
}
private void overload() {
}
public int overload() {
return 1;
}
重写(override)
重写概念
当子父类中出现了一模一样的方法(方法名称、参数个数、类型都完全一致)时,建立子类对象会运行子类中的方法,称作重写。
重写应用场景
当子类继承父类时,父类的功能内容需要修改时,可以通过重写来实现
重写规则
1. 子类重写父类时,必须要保证,子类方法的权限必须大于等于父类方法权限。
2. 如果一个方法不能被继承,则不能重写它。对于不能重写的方法,如果子类再写一个同名的方法并不是对父类方法进行重写(Override),而是重新生成一个新的方法
l 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为private和final的方法。
l 子类和父类不在同一个包中,那么子类只能够重写父类的声明为public和protected的非final方法。
3. 重写时,要么都静态,要么都不静态。 (静态只能重写静态,或者被静态重写)
4. 被final修饰的方法不可以被重写。final方法可以被继承、但是不能被重写、一个方法如果被final修饰、那么也就意味着、这个方法不会被改动(声明一个final方法的主要目的是防止方法的内容被修改)。
5. 子类重写父类的函数的时候,返回值类型必须是父类函数的返回值类型或该返回值类型的子类。不能返回比父类更大的数据类型
6. 子类重写父类的方法时,只能比父类抛出更少的异常,或者是抛出父类抛出的异常的子异常,因为子类可以解决父类的一些问题,不能比父类有更多的问题。
7. 当需要在子类中调用父类的被重写方法时,要使用super关键字
class Animal{
public void move(){
System.out.println("动物可以移动");
}
}
class Dog extends Animal{
public void move(){
super.move(); //应用父类的move方法
System.out.println("狗可以跑和走");
}
}
重载和重写的区别
1. 方法的重写是子类和父类之间的关系,是垂直关系;方法的重载是同一个类中方法之间的关系,是水平关系。
2. 重写只能由一个方法,或只能由一对方法产生关系;方法的重载是多个方法之间的关系。
3. 重写要求参数列表相同;重载要求参数列表不同。
4. 重写关系中,调用那个方法体,是根据对象的类型来决定;重载关系,是根据调用时的实参表与形参表来选择方法体的。
5. 被重载的方法可以声明新的或更广的检查异常;子类重写的方法只能比父类抛出更少的异常,或者是抛出父类抛出的异常的子异常
6. 重载方法的选择是静态的,是编译时已经确定好了;重写方法的选择则是动态的,是运行时通过动态绑定(dynamic binding)技术来实现的。重写方法的选择是依据执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。也就是说,只有程序运行起来,你才知道调用的是哪个子类的方法
class Animal{
public void move(){
System.out.println("Animal");
}
}
class Dog extends Animal{
public void move(){
System.out.println("Dog");
}
}
class Cat extends Animal{
public void move(){
System.out.println("Cat");
}
}
public class Test{
public static void main(String[] args) {
Animal[] a = new Animal[]{new Animal(), new Dog(), new Cat()};
//重写
for(Animal an: a)
an.move();
//重载
for(Animal an: a)
isMove(an);
}
public static void isMove(Animal a) {
System.out.println("Animal");
}
public static void isMove(Dog a) {
System.out.println("Dog");
}
public static void isMove(Cat a) {
System.out.println("Cat");
}
}
运行结果:
Animal
Dog
Cat
Animal
Animal
Animal
分析:isMove方法虽然被重载了,但是重载方法的选择是静态的,调用哪个重载方法是在编译时做出决定的。因此虽然for循环中的三次迭代的运行时类型是不同的,但这并不影响对重载方法的选择。因为该参数的编译时类型为Animal类,所以,唯一合适的重载方法是第三个:isMove(Animal a)。而对于重写方法move,由于重写方法的选择是动态的,由调用重写方法的引用对象在运行时决定的。因此for循环中的三次迭代的运行时类型是不同的,调用的重写方法也是不同的。