方法重写和多态

3.多态 :父类的引用指向子类的对象

方法的重写:子类对父类方法的重写

定义:

  • 方法重写是子类重新定义继承自父类的方法,并提供自己版本的实现。子类方法名、返回值类型和参数列表必须与父类的被重写方法完全一致

特点:

  • 方法名相同:子类方法必须与父类方法的名称相同。
  • 参数列表相同:子类方法的参数列表必须与父类方法相同(包括参数个数、类型和顺序)。
  • 返回类型相同:子类方法的返回类型必须与父类方法相同,Java 5 引入了协变返回类型,即子类方法可以返回父类方法返回类型的子类(子类方法返回类型可以是父类方法返回类型的子类)。
  • 访问权限子类方法的访问权限可以与父类方法相同或更宽松不能更严格例如,父类方法是 public,子类方法也必须是 publicprotected,不能是 private
  • 异常处理子类方法抛出的异常类型可以与父类方法抛出的异常类型相同或更少但不能更多

要注意的点:

  • 子类不能重写父类的 privatefinalstatic 方法
    • private:父类的方法被标记为 private 时,它对子类是不可见的,因此子类无法重写该方法。
    • final:父类的方法被标记为 final 时,表示该方法不允许被重写。
    • static:静态方法是与类绑定的,不能被实例化的对象调用,因此无法重写静态方法(但可以隐藏父类的静态方法)。
  • @Override 注解:虽然不是必须的,但推荐在子类重写父类方法时使用 @Override 注解,这样可以帮助我们检查是否正确地重写了方法。如果子类方法与父类方法不一致,编译器会报错。

方法的重载:同一类中方法的扩充

  • 方法重载是在同一个类中,方法名相同但参数列表不同(参数类型、参数个数或参数顺序不同)。重载方法根据调用时传入的参数类型或个数来确定调用哪个版本的方法。

特点:

  • 方法名相同:重载的方法必须有相同的方法名。
  • 参数列表不同:参数的数量、类型、顺序必须不同。
  • 返回类型可以不同:方法的返回类型可以与其他重载方法不同,但这不是重载的条件,重载是根据参数来判断的,返回类型并不影响方法的重载。
  • 与返回类型无关:仅仅改变返回类型不足以构成方法的重载。

父类A

package com.oop.demo05;

public class A {
 //定义父类非静态方法
 public void run(){
     System.out.println("A===>run");
 }

 //定义静态方法
 public static void test(){
     System.out.println("A===>test");
 }
 public void test2(){
     System.out.println("A===>test2");
 }
}

子类 B

package com.oop.demo05;

public class B extends A {
 //定义子类非静态方法
 @Override //alt+insert 快速插入重写
 public void run() {
     System.out.println("B===>run");
 }

 public int add(int a, int b) {
     return a + b;
 }

 //定义子类静态方法    静态的好像不支持重写
 public static void test(){
     System.out.println("B===>test");
 }

}
package com.oop.demo05;
//方法重写 子类改写父类的方法
//多态
public class Application {
 public static void main(String[] args) {
     B  b1 = new B();
     b1.run();//是子类B类型 指向的是子类B实例  调用的是子类中的 run方法
     b1.test();//静态的要关注对象的类型(左边) B类的就执行B中的静态方法

     A b2 = new B();//父类引用指向子类对象   b2是父类A 类型  指向的是子类B的实例
     b2.run();//尽管这里b2 是父类A中的类型 指向的却是B中的实例,如果run只在父类A中定义 就执行A类中的run方法,但是子类B将run方法重写了,执行就得执行子类中得方法
     // 注意如果没有在子类重写方法 调用B类单独的方法(在子类未重写得方法)是会报错的 但是可以强转子类的类型就可以调用了 看第15行
     b2.test();//调用静态方法 看左边 用的是父类A 中的静态方法

     //b2.add(3,2) 方法不重写 调用子类单独的方法会报错!
     //((B) b2).add(3,  把父类类型强转到子类类型就可以使用子类方法了
     System.out.println(((B)b2).add(3, 2)); //把父类类型强转到子类类型就可以使用子类方法了

     b2.test2();//单纯继承 且子类中没有重写  直接使用父类中test2方法

     A a1 = new A();
     a1.run();

 }
}

输出结果

B===>run
B===>test
B===>run
A===>test
5
A===>test2
A===>run

总结:

1. 方法重写(Override)

  • 子类可以 重写 父类的方法,改变父类方法的行为。

  • 实例方法重写

    如果子类重写了父类的方法,

    调用的是子类重写后的方法

    例如:

    public class B extends A {
        @Override
        public void run() {
            System.out.println("B===>run");
        }
    }
    

    在 B类中重写了 run()方法,那么通过 B类的实例调用 run() 方法时,会执行 B 类中的实现。

2. 多态(Polymorphism)

  • 多态允许

    父类类型的引用指向子类的对象

    即:

    A b2 = new B();  // 父类引用指向子类对象
    
  • 在这种情况下,虽然 b2 是 A 类型的引用,但它指向的是 B类型的对象。调用实例方法时,实际执行的是 B 类中的重写方法因为在运行时,会根据实际对象的类型来决定调用哪个方法。即:

    b2.run();  // 调用的是 B 中重写的 run 方法
    
  • 静态方法与多态的关系

    静态方法 :不支持重写

    但会根据引用类型来调用静态方法。因此,静态方法调用时,看左边的引用类型

    例如:

    b2.test();  // 调用的是 A 中的静态方法
    

3. 访问子类独有的方法

  • 如果子类有在父类中没有定义的方法(如

    add()
    

    那么通过父类的引用

    不能直接调用子类的独有方法

    例如:

    b2.add(3, 2);  // 编译时错误,因为 A 中没有 add 方法
    
  • 要调用子类独有的方法,可以

    强制类型转换

    将父类类型转换为子类类型:

    System.out.println(((B)b2).add(3, 2));  // 强转为 B 类型,调用 B 中的 add 方法
    

4. 静态方法调用

  • 静态方法是 类级别的方法,与对象的实例无关。

  • 在多态中,静态方法的调用

    不会

    随对象类型的变化而变化,而是根据引用类型来决定调用哪个静态方法。因此,

    b2.test()
    

    仍然调用 A类的静态方法因为 b2是 A类型的引用尽管它指向的是 B 类型的实例

    b2.test();  // 调用的是 A 中的静态方法
    

5. 继承父类的方法

如果子类没有重写父类的方法,那么就会调用父类的方法。例如:

b2.test2();  // 调用 A 中的 test2 方法,因为 B 中没有重写

6. 总结

  • 实例方法:调用时,根据实际对象的类型决定调用父类还是子类的方法。

    A a =new B(); 是A类型 B实例

    B b =new B();是B类型 B实例

    所以a用A类中的方法或者B中重写好的方法注意若未重写,要么重写要么强制类型转换成子类,b用B类中的方法,子类一般不能强制转换成父类

  • 静态方法:调用时,根据引用类型决定调用父类还是子类的方法,静态方法不支持重写。

  • 多态:通过父类引用指向子类对象,调用实例方法时会调用子类重写的方法,但静态方法调用时看左边的引用类型。

  • 强制类型转换:可以通过强制转换,调用子类特有的方法。

    1. 关于 a(父类引用指向子类对象)

    • aA 类型的引用,但它指向的是 B 类型的对象。
    • 实例方法:调用时会首先检查子类是否重写了父类的方法。如果子类重写了,则调用子类的版本;如果没有重写,则调用父类的方法。
    • 静态方法:静态方法的调用决定于引用类型(即 A)。即使 a 指向的是 B 类型的对象,静态方法也会调用 A 中定义的静态方法。
    • 强制类型转换:如果你想调用子类中未在父类中定义的方法,必须将父类引用强制转换为子类类型。例如,((B) a).add(3, 2)
    • 补充a 不能直接调用 B 中独有的(未重写的方法),即使 a 指向的是 B 类的对象。只有当你进行强制类型转换时,才能调用子类特有的方法。

    总结

    • a 可以使用 A 中的方法,也可以使用 B 中重写的实例方法。
    • 若没有重写子类的方法,你可以通过强制类型转换调用子类特有的方法。
    • a 不能直接使用 B 类中独有的(未重写的)方法,除非通过强制类型转换。

    2. 关于 b(子类引用指向子类对象)

    • bB 类型的引用,指向的是 B 类型的对象。
    • b 可以直接调用 B 中的方法,包括子类特有的方法。
    • 静态方法:调用时,静态方法还是会根据引用类型(B)来调用 B 类中的静态方法。
    • b 不需要强制类型转换,因为它本来就是 B 类型。

    3. 子类不能强制转换为父类

    • 子类对象可以赋值给父类引用,但父类引用不能直接赋给子类类型的变量,因为编译时无法确定它的具体类型。

    • 错误的示例

      B b = new A();  // 这是错误的,不能将父类对象赋给子类引用
      

    总结归纳:

    • 父类引用指向子类对象
      1. 调用实例方法时,会调用子类重写的方法(如果子类没有重写,调用父类的方法)。
      2. 调用静态方法时,调用父类的静态方法,因为静态方法是编译时绑定的。
      3. 如果父类没有定义的方法,且子类有,必须强制转换为子类类型才能调用。
      4. 不能直接调用子类中独有的方法,如果想使用,必须强制转换。
    • 子类引用指向子类对象
      1. 可以直接调用子类重写的方法。
      2. 可以调用子类独有的非静态方法。
      3. 不需要强制类型转换。
    • 强制类型转换
      1. 如果父类引用指向子类对象,想调用子类特有的方法,可以通过强制类型转换来实现。

代码的核心行为:

  1. b2.run() 输出 "B===>run",因为 B 类重写了 A 类的 run() 方法。
  2. b2.test() 输出 "A===>test",因为静态方法是由引用类型决定的,b2A 类型的引用。
  3. ((B)b2).add(3, 2) 强制转换后调用 B 类中独有的 add() 方法。
  4. b2.test2() 输出 "A===>test2",因为 B 类没有重写 test2() 方法。
b2.test2();  // 调用 A 中的 test2 方法,因为 B 中没有重写
posted @ 2025-01-12 16:02  panghuhu~  阅读(50)  评论(0)    收藏  举报