Java函数的重载和覆盖的概念及其区别
Java中的函数重载(Overloading)和函数覆盖(Override)是两种常用的函数特性,它们都是用于实现函数多态性的。
函数重载:在Java中,一个类可以定义多个同名但形参列表不同的函数,这就被称为函数重载。
例如,对于一个类C,我们可以定义两个同名但形参列表不同的函数f1和f2:
class C{
public int f1(int x){
return x+1;
}
public double f2(double y){
return y+1;
}
}
这里,函数f1和f2都是类C的成员函数,它们都有相同的函数名,但是它们的形参列表不同(一个接收int类型的参数,另一个接收double类型的参数),因此可以说它们构成了函数重载。当我们在调用这两个函数时,Java编译器会自动匹配实参的类型和形参列表,从而选择调用合适的函数。
例如,下面的代码调用了类C的成员函数f1和f2:
public class Test{
public static void main(String[] args){
C obj = new C();
System.out.println(obj.f1(10)); // 输出11
System.out.println(obj.f2(3.14)); // 输出4.14
}
}
在调用obj.f1(10)时,Java编译器发现函数f1接收int类型的参数,实参是一个int类型的常量10,因此自动匹配调用f1函数,将返回值11输出;在调用obj.f2(3.14)时,Java编译器发现函数f2接收double类型的参数,实参是一个double类型的常量3.14,因此自动匹配调用f2函数,将返回值4.14输出。
函数覆盖:在Java中,当子类继承了父类的某个函数并且重新定义了该函数,这就被称为函数覆盖。
例如,对于一个父类P和一个子类S,我们可以定义一个父类函数f和子类函数g:
class P{
public void f(){
System.out.println("I am P");
}
}
class S extends P{
public void g(){
System.out.println("I am S");
}
@Override
public void f(){
System.out.println("I am overridden by S");
}
}
这里,类P定义了一个成员函数f,类S继承了类P并定义了自己的成员函数g,并重写了类P的函数f。注意,在类S中,我们使用了@Override注解来告诉Java编译器,我们正在对类P中的函数f进行覆盖。
当我们在调用S类的函数f时,会优先调用S类中的函数,因为S类已经重写了P类中的函数f。例如,下面的代码调用了S类的成员函数f和g:
public class Test{
public static void main(String[] args){
S obj = new S();
obj.f(); // 输出 I am overridden by S
obj.g(); // 输出 I am S
}
}
在调用obj.f()时,Java编译器发现S类重写了函数f,因此自动匹配调用S类中的函数f,将字符串"I am overridden by S"输出;在调用obj.g()时,Java编译器发现S类定义了一个成员函数g,因此自动匹配调用S类中的函数g,将字符串"I am S"输出。
函数重载和函数覆盖的区别:
1. 形参列表不同。函数重载依赖于形参列表的不同,而对于函数覆盖,子类必须和父类有相同的函数名、返回值类型、形参列表和访问修饰符。
2. 发生时间不同。函数重载在编译时期就确定了,而函数覆盖是在运行时期才确定的,也就是说,函数重载的选择过程由编译器完成,而函数覆盖的选择过程由JVM执行,根据实际类型来确定调用哪个函数。
3. 目的不同。函数重载是为了提高程序的灵活性和可读性,对于不同的参数类型和参数个数,使用同一函数名,不至于让函数名过于复杂,同时也方便程序员使用。而函数覆盖则是为了实现多态性,在程序运行时根据实际类型来调用相应的子类函数。
