九、Java方法的重载重写

重载(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。

    既然可以通过改变参数的数目和类型来实现重载,那么可以改变方法的返回值类型来实现吗?——答案是不可以的。

    如果仅仅改变返回值类型的话,编译时报错会优先于运行时报错,所以当两个方法的名字相同、参数个数和类型也相同的时候,虽然返回值类型不同,但是依然会提示方法已经被定义的错误:

    JavaSE基础之(九)Java方法的重载和重写-小白菜博客
    方法的返回值只是作为方法运行后的一个状态,它是保持方法的调用者和被调用者进行通信的一个纽带,但是并不能作为某个方法的标识。

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、参数的隐式类型转换

由于可以通过改变参数类型的方式实现方法重载,那么当传递的参数没有找到匹配的方法时,就会发生隐式的类型转换。
JavaSE基础之(九)Java方法的重载和重写-小白菜博客
如上图所示,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() 方法的时候,编译器会提示错误:
    JavaSE基础之(九)Java方法的重载和重写-小白菜博客
    在调用时方法不明确,编译器会很为难,究竟是把第一个 20 从 int 转成 long 呢,还是把第二个 20 从 int 转成 long 呢,智障了!所以,不能写这样让编译器左右为难的代码。

9.2 方法重写

方法重写需要满足以下三个规则:

  1. 重写的方法必须和父类中的方法有着相同的名字;
  2. 重写的方法必须和父类中的方法有着相同的参数;
  3. 必须是 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、方法重写(两同一小一大)

两同:方法名相同,参数相同。
一小:子类方法声明的异常类型要比父类小一些或者相等。
一大:子类方法的访问权限应该比父类更大或者相等。