☰
【Java 多态详解】:一次讲透重写、转型与绑定机制
2026/10/1 11:48:08 网站建设 项目流程

引言

多态是面向对象编程的三大特性之一,也是 Java 中最能体现“抽象与灵活”的设计思想。本文将从重写与重载的区别讲起,逐步深入向上转型、向下转型,以及动态绑定与静态绑定的底层机制,帮助你真正理解多态的本质。

文章目录

    • 引言
    • 1.1 多态
      • 多态的条件
    • 1.2 重写(Override)
      • 重写规则
      • 重写与重载的区别
    • 1.3 向上转型
      • 向上转型使用场景
    • 1.4 向下转型
    • 1.5 instanceof 关键字的使用
    • 1.6 动态绑定与静态绑定
      • 静态绑定(编译时)
      • 动态绑定(运行时)
      • 动态绑定 vs 静态绑定
    • 多态优点

1.1 多态

多态的定义:同一操作作用于不同对象,产生不同的执行结果

多态的条件

继承、重写、通过父类的引用调用重写的方法

多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。

packagedemo1;//父类publicclassAnimal{publicStringname;publicintage;publicAnimal(Stringname,intage){this.name=name;this.age=age;}publicvoideat(){System.out.println(this.name+"吃饭");}}
publicclassCatextendsAnimal{publicCat(Stringname,intage){super(name,age);}@Overridepublicvoideat(){System.out.println(this.name+"吃鱼");}}publicclassDogextendsAnimal{publicDog(Stringname,intage){super(name,age);}@Overridepublicvoideat(){System.out.println(this.name+"吃骨头");}}
publicclassmain{//这里形参必须是父类 能够接收子类publicstaticvoideat(Animalanimal){animal.eat();}//编译器在编译代码时 不知道要调子类中谁的eat的⽅法//这里是动多态 详细可看动态绑定publicstaticvoidmain(String[]args){Catc=newCat("uu",2);Dogd=newDog("ww",5);eat(c);eat(d);}}//运行结果:uu吃鱼 ww吃骨头

1.2 重写(Override)

重写的定义:子类重新实现父类中已有的方法

重写规则

  1. 必须与父类方法保持一致 :返回值类型 方法名(参数)
  2. 重写的返回值类型可以不同,但是必须是父子关系(协变类型)
publicAnimaleat(){System.out.println(this.name+"吃饭");returnnull;}@OverridepublicDogeat(){System.out.println(this.name+"吃骨头");returnnull;}
  1. 访问权限 子类要>=父类
  2. 被static,final,private修饰的方法都不能被重写

static方法属于类本身,不是属于对象实例的,而重写 是面向对象里「子类对象覆盖父类对象的实例方法」的机制
final方法的实现逻辑是父类定死的,不允许子类改写/重写,防止子类通过重写破坏父类设计。
private只能访问一个类,重写跨类了

  1. @override注解可以对重写方法进行合法性校验,比如方法名写错之类的

重写与重载的区别

【重载】同一个类中方法名相同、参数列表不同(参数个数、类型、顺序至少一个不同)

对比维度重写(Override)重载(Overload)
发生位置子类中同一个类中
方法名必须相同必须相同
参数列表必须相同必须不同(个数、类型、顺序)
返回值类型相同或协变类型可以不同
访问权限子类不能低于父类无限制
修饰符不能是 static、final、private无限制
绑定方式动态绑定(运行时)静态绑定(编译期)
目的子类重新实现父类方法提供多个同名但参数不同的方法

1.3 向上转型

向上转型的定义:子类对象赋值给父类引用

向上转型使用场景

  1. 直接赋值
publicclassAnimal{//省略了}publicclassCatextendsAnimal{//省略了}//向上转型Animala=newCat();
  1. 方法传参
publicclassmain{//这里形参必须是父类 能够接收子类publicstaticvoideat(Animalanimal){animal.eat();}publicstaticvoidmain(String[]args){//传参eat(newCat("uu",2));}}
  1. 方法返回
publicstaticAnimalgetAnimal(){returnnewDog();// 向上转型 Dog → Animal}

向上转型后只能调用父类中声明的方法

//父类publicclassAnimal{publicStringname;publicintage;publicAnimal(Stringname,intage){this.name=name;this.age=age;}publicvoideat(){System.out.println(this.name+"吃饭");}}
//子类publicclassCatextendsAnimal{publicCat(Stringname,intage){super(name,age);}@Overridepublicvoideat(){System.out.println(this.name+"吃鱼");}//子类独有的方法publicvoidrun(){System.out.println(this.name+"逛街");}}
publicclassmain{publicstaticvoidmain(String[]args){Animala1=newCat("mimi",5);Animala2=newDog("kimi",4);a1.eat();//这里报错 Animal类中没有run⽅法a1.run();}}

1.4 向下转型

  • 向下转型的定义:父类引用强制转换为子类类型
  • Why:由于向上转型无法调用子类独有的方法,所以向下转型,简单讲就是还原

向下转型的语法与风险(ClassCastException)

publicclassmain{publicstaticvoidmain(String[]args){Animala1=newCat("mimi",5);Animala2=newDog("kimi",4);a1.eat();//这里报错 Animal类中没有run⽅法a1.run();//此时还原 之前对象是猫 所以还原安全Catcat=(Cat)a1;cat.run();//如果强制还原为狗//会触发ClassCastException异常Dogdog=(Dog)a1;dog.run();

1.5 instanceof 关键字的使用

向下转型不安全,所以引入instanceof关键字判断,如果表达式为true,则可以安全转换

publicclassmain{publicstaticvoidmain(String[]args){Animala1=newCat("mimi",5);Animala2=newDog("kimi",4);a1.eat();//判断 如果true就强制转换对应的类型if(a1instanceofCat){Catcat=(Cat)a1;cat.run();}elseif(a1instanceofDog){Dogdog=(Dog)a1;dog.run();}}}

1.6 动态绑定与静态绑定

前面讲多态时提到「编译器在编译代码时不知道要调子类中谁的 eat 方法」,这背后就是绑定机制在起作用。
绑定指的是:方法调用与具体方法实现之间建立关联的过程。

静态绑定(编译时)

  • 定义:在编译阶段就能确定调用哪个方法,由编译器直接完成绑定。
  • 特点:效率高,运行时不需额外判断。
  • 适用场景:static方法、private方法、final方法,以及重载(Overload)。
publicclassCalculator{// 重载:编译期就能确定调用哪个 addpublicintadd(inta,intb){returna+b;}publicdoubleadd(doublea,doubleb){returna+b;}}// 编译时:根据参数类型 (int, int) 直接绑定到第一个 addCalculatorc=newCalculator();c.add(1,2);// 静态绑定 → int addc.add(1.0,2.0);// 静态绑定 → double add

动态绑定(运行时)

  • 定义:在运行阶段才根据对象的实际类型确定调用哪个方法。
  • 特点:灵活,是多态实现的底层机制。
  • 适用场景:重写(Override),即通过父类引用调用被子类重写的方法。
publicclassmain{publicstaticvoideat(Animalanimal){// 编译期:只知道 animal 是 Animal 类型// 运行期:根据实际传入的对象(Cat / Dog)动态绑定到对应 eat()animal.eat();}publicstaticvoidmain(String[]args){eat(newCat("uu",2));// 动态绑定 → Cat.eat() 输出 uu吃鱼eat(newDog("ww",5));// 动态绑定 → Dog.eat() 输出 ww吃骨头}}

动态绑定 vs 静态绑定

对比维度动态绑定静态绑定
绑定时机运行时编译时
判断依据对象的实际类型变量的声明类型
对应机制重写重载
典型方法实例方法(非 final/static/private)static、private、final 方法

小结:多态之所以能「同一操作、不同结果」,正是因为动态绑定在运行时根据对象的真实类型,把方法调用绑定到正确的实现上。
这也是为什么向上转型后调用eat()依然能输出子类的结果——编译器看的是父类引用,运行时绑定的却是子类方法。

多态优点

//父类publicclassScope{publicvoiddraw(){System.out.println("画人体!");}}//子类publicclassbodyextendsScope{@Overridepublicvoiddraw(){System.out.println("身体body就绪!");}}publicclasshandextendsScope{@Overridepublicvoiddraw(){System.out.println("手长出来了!");}}publicclassheadextendsScope{@Overridepublicvoiddraw(){System.out.println("头头头头!");}}publicclasstailextendsScope{@Overridepublicvoiddraw(){System.out.println("隐形尾巴!");}}

【未运用多态】

publicclassTest{publicstaticvoidmain(String[]args){body b=newbody();hand a=newhand();head h=newhead();tail t=newtail();String[]scopes={"body","hand","head","tail"};//增强for循环for(Strings:scopes){if("body".equals(s)){b.draw();}elseif("hand".equals(s)){a.draw();}elseif("head".equals(s)){h.draw();}elseif("tail".equals(s)){t.draw();}}}}//运行结果//身体body就绪!//头头头头!//手长出来了!//隐形尾巴!

【运用多态】
运用多态则不需要那么多循环,可以简化代码,可扩展性强。

假如我现在要加入一个eyes类,只需要新建类,修改Scope数组,之前未使用多态则需要再写if-else

publicstaticvoidmain(String[]args){Scope[]scopes={newbody(),newhead(),newhand(),newtail()};for(Scopes:scopes){s.draw();}}

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询