重载(Overloading):一个类有多个名字相同但是参数个数不同的方法。如果方法的功能是一样的,但是参数不同,使用相同的名字可以提高程序的可读性。
重写(Overriding):子类具有和父类一样的方法(参数相同、返回类型相同、方法名相同,但是方法体可能不同)。方法重写用于提供父类已经声明的方法的特殊实现,是实现多态的基础条件。
9.1 方法重载
01、重载的两种方式
在 Java 中,有两种方式可以达到方法重载的目的:
-
改变参数的数目
/** * @author QHJ * @date 2022/9/19 15:48 * @description: 方法重载——改变参数的数目 */ public class OverloadingByParamNum { public static void main(String[] args) { System.out.println(Adder.add(10, 90)); System.out.println(Adder.add(10, 90, 900)); } } class Adder { static int add(int a, int b) { return a + b; } static int add(int a, int b, int c) { return a + b + c; } }
Adder 类有两个方法:第一个 add() 方法有两个参数,在调用的时候可以传递两个参数;第二个 add() 方法有三个参数,在调用的时候可以传递三个参数。
这个例子只是为了说明方法重载的一种类型。如果参数类型相同的话,可以使用 Java 提供的可变参数的方式:
static int add(int... args) { int sum = 0; for (int i : args) { sum += i; } return sum; }
-
改变参数类型
/** * @author QHJ * @date 2022/9/19 15:54 * @description: 方法重载——改变参数类型 */ public class OverloadingByParamType { public static void main(String[] args) { System.out.println(Adder2.add(10, 90)); System.out.println(Adder2.add(10.1, 90.2)); } } class Adder2 { static int add(int a, int b) { return a + b; } static double add(double a, double b) { return a + b; } }
Adder2 类有两个方法:第一个 add() 方法的参数类型为 int,第二个 add() 方法的参数类型为 double。
既然可以通过改变参数的数目和类型来实现重载,那么可以改变方法的返回值类型来实现吗?——答案是不可以的。
如果仅仅改变返回值类型的话,编译时报错会优先于运行时报错,所以当两个方法的名字相同、参数个数和类型也相同的时候,虽然返回值类型不同,但是依然会提示方法已经被定义的错误:
方法的返回值只是作为方法运行后的一个状态,它是保持方法的调用者和被调用者进行通信的一个纽带,但是并不能作为某个方法的标识。
02、main() 方法可以被重载
main() 方法也是个方法,只不过 Java 虚拟机在运行的时候只会调用带有 String 数组的那个 main() 方法:
/**
* @author QHJ
* @date 2022/9/19 16:23
* @description: main() 重载
*/
public class OverloadingMain {
public static void main(String[] args) {
System.out.println("String[] args");
}
public static void main(String args) {
System.out.println("String args");
}
public static void main() {
System.out.println("无参");
}
}
输出结果:
String[] args
第一个 main() 方法的参数形式为 String[] args,是最标准的写法;第二个 main() 方法的参数形式为 String args,少了中括号;第三个 main() 方法没有参数。
从结果中可以看出:尽管 main() 方法可以重载,但是程序只认标准写法。
03、参数的隐式类型转换
由于可以通过改变参数类型的方式实现方法重载,那么当传递的参数没有找到匹配的方法时,就会发生隐式的类型转换。
如上图所示,byte 可以向上转换为 short、int、long、float 和 double,short 可以向上转换为 int、long、float 和 double,char 可以向上转换为 int、long、float 和 double,依次类推。
-
例子一:
/** * @author QHJ * @date 2022/9/19 16:38 * @description: 重载——参数类型隐式转换 */ public class OverloadingTypePromotion1 { void sum(int a, int b) { System.out.println(a + b); } void sum(int a, int b, int c) { System.out.println(a + b + c); } public static void main(String[] args) { OverloadingTypePromotion1 obj = new OverloadingTypePromotion1(); obj.sum(20, 20); obj.sum(20, 20, 20); } }
执行 obj.sum(20, 20) 的时候,发现没有 sum(int a, int b) 的方法,所以此时第二个 20 向上转型为 long,所以调用的是 sum(int a, int b) 的方法。
-
例子二
/** * @author QHJ * @date 2022/9/19 16:45 * @description: 重载——参数类型隐式转换 */ public class OverloadingTypePromotion2 { void sum(int a, int b) { System.out.println("int"); } void sum(long a, long b) { System.out.println("long"); } public static void main(String args[]) { OverloadingTypePromotion2 obj = new OverloadingTypePromotion2(); obj.sum(20, 20); // int } }
执行 obj.sum(20, 20) 的时候,发现有 sum(int a, int b) 的方法,所以就不会向上转型为 long,调用 sum(long a, long b)。
-
例子三
/** * @author QHJ * @date 2022/9/19 16:47 * @description: 重载——参数类型隐式转换 */ public class OverloadingTypePromotion3 { void sum(long a, int b) { System.out.println("long int"); } void sum(int a, long b) { System.out.println("int long"); } public static void main(String args[]) { OverloadingTypePromotion3 obj = new OverloadingTypePromotion3(); obj.sum(20, 20); } }
当通过 obj.sum(20, 20) 来调用 sum() 方法的时候,编译器会提示错误:
在调用时方法不明确,编译器会很为难,究竟是把第一个 20 从 int 转成 long 呢,还是把第二个 20 从 int 转成 long 呢,智障了!所以,不能写这样让编译器左右为难的代码。
9.2 方法重写
方法重写需要满足以下三个规则:
- 重写的方法必须和父类中的方法有着相同的名字;
- 重写的方法必须和父类中的方法有着相同的参数;
- 必须是 is-a 的关系(继承关系)。
/**
* @author QHJ
* @date 2022/9/19 16:55
* @description:
*/
public class Bike extends Vehicle {
public static void main(String[] args) {
Bike bike = new Bike();
bike.run(); // 车辆在跑
}
}
class Vehicle {
void run() {
System.out.println("车辆在跑");
}
}
Bike is-a Vehicle,自行车是一种车。Vehicle 类有一个 run() 的方法,也就是说车辆可以跑,Bike 继承了 Vehicle,也可以跑。但如果 Bike 没有重写 run() 方法的话,自行车就只能是‘车辆在跑’,而不是‘自行车在跑’。这时就会用到方法重写:
/**
* @author QHJ
* @date 2022/9/19 16:55
* @description:
*/
public class Bike extends Vehicle {
@Override
void run() {
System.out.println("自行车在跑");
}
public static void main(String[] args) {
Bike bike = new Bike();
bike.run(); // 自行车在跑
}
}
class Vehicle {
void run() {
System.out.println("车辆在跑");
}
}
Bike 重写了 run() 方法,也就意味着,Bike 可以跑出自己的风格。
9.3 总结
01、方法重载(两同一不同)
两同:在同一个类,方法名相同。
一不同:参数不同。
02、方法重写(两同一小一大)
两同:方法名相同,参数相同。
一小:子类方法声明的异常类型要比父类小一些或者相等。
一大:子类方法的访问权限应该比父类更大或者相等。