ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Java多态 学习笔记

Java多态 学习笔记 Java继承 学习笔记1.多态1.1多态实现条件1.2向上转型和向下转型1.2.1向上转型1.2.2向下转型1.3重写与重载1.3.1重写1.3.2重写与重载的区别1.3.3动态绑定与静态绑定1.4回顾多态1.4.1多态思想1.4.2多态的优缺点1.多态对一个事物产生不同的态度1.1多态实现条件多态实现的三个必要条件必须在父类的继承体系下子类必须要对父类中方法进行重写通过父类的引用调用重写的方法当使用多态方式调用方法时首先检查父类中是否有该方法如果没有则编译错误。如果有再去调用子类的同名方法。1.2向上转型和向下转型1.2.1向上转型概念创建一个子类对象将其当成父类对象来使用。语法格式父类类型 对象名 new 子类类型()AnimalanimalnewCat(元宝,2);animal是父类类型但可以引用一个子类对象因为是从小范围向大范围的转换。向上转型的几种方法直接赋值方法的传参方法的返回值注意事项通过父类的引用调用子类特有的方法是无法直接调用的这里只能调用父类自己的。classAnimal{publicStringname;publicAnimal(Stringname){this.namename;}}classDogextendsAnimal{publicDog(Stringname){super(name);}}classCatextendsAnimal{publicCat(Stringname){super(name);}}publicclassTest{//方法二方法的传参publicstaticvoidfunction(Animalanimal){}//方法三返回值publicstaticAnimalfunction2(){DogdognewDog(Papy Dog);returndog;}publicstaticvoidmain(String[]args){//方法一直接赋值AnimaldognewDog(Papy Dog);CatcatnewCat(Papy Cat);//方法二方法的传参function(cat);//方法三返回值Animaldog2function2();}}1.2.2向下转型将一个子类对象经过向上转型之后当成父类方法使用再无法调用子类的方法但有时候可能需要调用子类特有的方法此时将父类引用再还原为子类对象即可即向下转换。继续根据向上转型的类举例classAnimal{publicStringname;publicAnimal(Stringname){this.namename;}}classDogextendsAnimal{publicDog(Stringname){super(name);}}classCatextendsAnimal{publicCat(Stringname){super(name);}}publicclassTest{publicstaticvoidmain(String[]args){DogdognewDog(Papy Dog);CatcatnewCat(Papy Cat);AnimalanimalnewDog(Papy Dog);//向上转型Dogdog(Dog)animal;//合法的向下转型//cat (Cat)animal; //非法的向下转型}}注意向下转型的要求必须先有向上转型前提对象的真实类型必须是目标子类或者其子类需要强制类型转换Cast推荐instanceof检查类型安全转型方式if(ainstanceofDog){Dogd(Dog)a;d.bark();}来个比喻呢就是向上转型你把 “iPhone” 当成 “手机” 来用没问题。向下转型你把 “手机” 强行当成 “iPhone” 用只有当它本来就是 iPhone 才行如果本来是 “小米”那么立马报错。1.3重写与重载1.3.1重写classAnimal{publicStringname;publicAnimal(Stringname){this.namename;}publicvoideat(){System.out.println(Animal在吃饭);}}classDogextendsAnimal{publicDog(Stringname){super(name);}publicvoideat(){System.out.println(Dog在吃饭);}}//输出结果Dog在吃饭以上例子构成了重写按规则是要调用父类的eat的但是构成了动态绑定伏笔重写(override)也称为覆盖。重写是子类对父类非静态、非private修饰非final修饰非构造方法等的实现过程进行重新编写,形参列表与被重写方法的参数列表必须完全相同。方法重写的规则子类在重写父类的方法时一般必须与父类方法原型一致 返回值类型方法名 (参数列表) 要完全一致被重写的方法返回值类型可以不同但是必须是具有父子关系的访问权限不能比父类中被重写的方法的访问权限更低。例如如果父类方法被public修饰则子类中重写该方法就不能声明为 protected父类被static、private修饰的方法、构造方法都不能被重写。重写的方法, 可以使用 Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法构成重写.对第二点解释classParent{NumbergetValue(){return1;}}classChildextendsParent{OverrideIntegergetValue(){return2;}// OKInteger 是 Number 的子类}1.3.2重写与重载的区别区别点重载Overload重写Override参数列表必须修改一定不能修改返回类型可以修改一定不能修改[除非构成父子类关系]访问限定符可以修改可以减少或删除一定不能抛出新的或者更广的异常访问可以修改一定不能做更严格的限制可以降低限制重写的原则对于已经投入使用的类尽量不要进行修改。最好的方式是重新定义一个新的类来重复利用其中共性的内容并且添加或者改动新的内容。父类DrinkMachine饮料机classDrinkMachine{publicvoidmakeDrink(){System.out.println(制作普通饮料);}}子类CoffeeMachine咖啡机classCoffeeMachineextendsDrinkMachine{OverridepublicvoidmakeDrink(){System.out.println(制作咖啡);}}eg比如饮料机。父类DrinkMachine 子类咖啡机CoffeeMachine如果直接改DrinkMachine的makeDrink()方法把“普通饮料“换成”咖啡“那以后所有买饮料的人都只能喝咖啡了 果汁党就会抗议 。正确做法应该就是新建CoffeeMachine只改子类逻辑不影响父类1.3.3动态绑定与静态绑定静态绑定也称为前期绑定(早绑定)即在编译时根据用户所传递实参类型就确定了具体调用那个方法。典型代表函数重载。动态绑定也称为后期绑定(晚绑定)即在编译时不能确定方法的行为需要等到程序运行时才能够确定具体调用那个类的方法。解释如下classParent{voidhello(){System.out.println(Parent hello);}}classChildextendsParent{Overridevoidhello(){System.out.println(Child hello);}}publicclassMain{publicstaticvoidmain(String[]args){ParentpnewChild();// 向上转型p.hello();// 打印什么}}(1)编译期编译器只知道p的静态类型是Parent所以它会去Parent类中查找有没有hello()方法。查到以后编译器就说“行这个调用是合法的。”但它不会决定最终调用哪个版本。 所以编译期只检查方法是否存在但不绑定到具体实现。(2)运行期运行时p实际上引用的是一个Child对象。Java 虚拟机JVM在调用p.hello()的时候会根据对象的真实类型运行时类型来决定到底执行Parent.hello()还是Child.hello()。因为Child重写了hello()所以 JVM 调用的是Child的版本。这就是动态绑定Dynamic DispatchJVM底层原理在JVM的方法区Java8后都是元空间Metaspace里每个类都会有一张方法表Method Table类似 C 的 vtable 。1.类加载时JVM 为Parent类建立方法表里面有hello - Parent.hello的映射。JVM 为Child类建立方法表发现hello被重写了就把hello - Child.hello替换掉。2.对象创建时当new Child()执行时对象头里会有一个指针指向Child的类信息包括方法表。3.方法调用时执行p.hello()JVM 并不会直接调用Parent.hello()而是看p的运行时类型 → 发现它指向Child在Child的方法表里查找hello()→ 找到Child.hello()→ 调用它。 因此真正起作用的是运行时对象的类信息 方法表查找而不是变量的声明类型 。我们可以类比为编译期老师点名检查-”这个学生‘回答问题’的功能吗“有运行期真正回答问题的是具体学生可能每个人回答的方式不同。1.4回顾多态1.4.1多态思想classAnimal{publicStringname;publicAnimal(Stringname){this.namename;}publicvoideat(){System.out.println(this.name在吃饭);}}classDogextendsAnimal{publicDog(Stringname){super(name);}Overridepublicvoideat(){System.out.println(this.name在吃饭);}}classCatextendsAnimal{publicCat(Stringname){super(name);}Overridepublicvoideat(){System.out.println(this.name在吃饭);}}publicclassTest{publicstaticvoidfunc(Animalanimal){animal.eat();}publicstaticvoidmain(String[]args){DogdognewDog(Papy Dog);CatcatnewCat(Papy Cat);func(dog);func(cat);}}输出结果PapyDog在吃饭PapyCat在吃饭调用同一个方法得到不同的结果多态思想1.4.2多态的优缺点优点publicstaticvoiddrawShapes(){RectrectnewRect();CyclecyclenewCycle();FlowerflowernewFlower();String[]shapes{cycle,rect,cycle,rect,flower};for(Stringshape:shapes){if(shape.equals(cycle)){cycle.draw();}elseif(shape.equals(rect)){rect.draw();}elseif(shape.equals(flower)){flower.draw();}}}我们使用多态的话就能优化一下我们的代码publicstaticvoiddrawShapes(){// 我们创建了一个 Shape 对象的数组.Shape[]shapes{newCycle(),newRect(),newCycle(),newRect(),newFlower()};for(Shapeshape:shapes){shape.draw();}}缺点多态的缺陷–代码的运行效率降低1.属性没有多态性当父类和子类都有同名属性的时候通过父类引用只能引用父类自己的成员属性2.构造方法没有多态性以上是我关于Java多态的笔记分享感谢你读到这里这也是我学习路上的一个小小记录。希望以后回头看时能看到自己的成长~
返回列表