当前位置: 首页 > 后端技术 > Java

Java的多态

时间:2023-04-01 19:24:02 Java

1.1多态形式多态是继封装和继承之后面向对象的第三大特征。多态性发生在继承或实现关系中。多态的格式:父类类型变量名=新建子类/实现类构造函数;变量名。方法名();多态前提:存在继承关系,子类对象是可以赋值给父类类型的变量。例如,Animal是一种动物类型,而Cat是一种猫类型。Cat继承自Animal,Cat对象也是Animal类型,自然可以赋值给父类类型的变量。1.2多态使用场景如果没有多态,下图中的register方法只能传递学生对象,其他teacher和administrator对象不能传递给register方法。在这种情况下,只有三种不同的注册方法分别接受学生、教师和管理员。有了多态性,方法的形参可以定义为公共父类Person。需要注意的是:当一个方法的形参是一个类时,我们可以传递这个类的所有子类对象。当一个方法的形参是一个接口时,我们可以传递这个接口的所有实现类对象(后面会学习)。而且多态还可以根据传递的不同对象调用不同类中的方法。代码示例:父类:publicclassPerson{privateStringname;私人年龄;具有所有参数构造的空参数构造get和set方法publicvoidshow(){System.out.println(name+","+age);}}子类1:publicclassAdministratorextendsPerson{@Overridepublicvoidshow(){System.out.println("管理员信息:"+getName()+","+getAge());}}子类2:publicclassStudentextendsPerson{@Overridepublicvoidshow(){System.out.println("学生信息:"+getName()+","+getAge());}}子类3:publicclassTeacherextendsPerson{@Overridepublicvoidshow(){System.out.println("老师的信息是:"+getName()+","+getAge());}}测试类:publicclassTest{publicstaticvoidmain(String[]args){//创建三个对象并调用注册方法Students=newStudent();s.setName("张三");s.setAge(18);老师t=newTeacher();t.setName("王建国&“;”);t.setAge(30);管理员admin=newAdministrator();admin.setName("管理员");admin.setAge(35);注册;注册(t);注册(管理员);}//该方法可以接收教师、学生、管理员//参数只能写成这三种类型的父类publicstaticvoidregister(Personp){p.show();}}1.3多态的定义和前提多态:指同一个行为有多种不同的表现形式。从上面的案例可以看出,Cat和Dog都是动物,都吃这个行为,只是效果(表现形式)不同。前提【重点】存在实现关系方法的继承或重写【含义:无重写,无意义】父类引用指向子类对象【格式表达】父类类型:指子类继承的父类类型对象,或实现的父接口类型。1.4多态操作特点调用成员变量时:向左编译,向左运行调用成员方法时:向左编译,向右运行代码示例:Fuf=newZi();//编译看左边父类如果没有name属性,会报错//实际运行时,打印出父类的name属性的值System.out.println(f.name);//编译看左边父类中有没有show方法如果没有则报错//实际运行时运行子类中的show方法f.show();1.5多态的缺点我们已经知道,多态编译阶段取决于左边父类的类型。该类具有一些独特的功能。这时候多态写法就不能访问子类特有的功能了。classAnimal{publicvoideat(){System.out.println("Animalseat!")}}classCatextendsAnimal{publicvoideat(){System.out.println("Eatfish");}publicvoidcatchMouse(){System.out.println("捉老鼠");}}classDogextendsAnimal{publicvoideat(){System.out.println("Eatbones");}}classTest{publicstaticvoidmain(String[]args){Animala=newCat();a.吃();a.catchMouse();//编译错误,向左编译,Animal没有这个方法}}1.6引用类型转换1.6.1为什么要转换成多态写法,会访问到unique子类的功能。以多态方式调用方法时,首先检查该方法是否存在于父类中,如果不存在,则会出现编译错误。也就是说,你不能调用子类拥有的方法,而超类拥有的方法。编译都错了,更别说运行了。这也是多态给我们带来的一点“小麻烦”。因此,如果要调用子类特有的方法,就必须进行向下转型。复习基本数据类型转换自动转换:小范围到大范围的赋值。自动补全:doubled=5;强制转换:大范围赋值给小范围,强制转换:inti=(int)3.14多态转换有两种:向上转换(自动转换)和向下转换(强制转换)。1.6.2Upcasting(自动转换)Upcasting:多态性本身就是从子类类型向上转换(自动转换)到父类类型的过程,这个过程是默认的。当父类引用指向子类对象时发生向上转型。使用格式:父类类型变量名=新子类类型();如:Animala=newCat();原因是:父类类型是比子类范围更广的类型,Animal是动物类,是父类。Cat是猫类,是子类类型。Animal类型的范围当然非常大,包括所有的动物。因此,如果子类的作用域较小,可以直接自动转化为父类类型的变量。1.6.3Downcasting(强制转换)Downcasting:从父类类型向下转换为子类类型的过程,这个过程是强制的。对于已经向上转型的子类对象,可以将父类引用转换为子类引用,可以使用强制类型转换格式,这就是向下转型。使用格式:子类类型变量名=(子类类型)父类变量名;如:Aniamla=newCat();Catc=(Cat)a;1.6.4案例演示以多态方式调用方法时,首先检查该方法是否存在于父类中,如果不存在则编译错误。也就是说,你不能调用子类拥有的方法,而超类拥有的方法。编译都错了,更别说运行了。这也是多态给我们带来的一点“小麻烦”。因此,如果要调用子类特有的方法,就必须进行向下转型。改造演示,代码如下:Defineclass:abstractclassAnimal{abstractvoideat();}classCatextendsAnimal{publicvoideat(){System.out.println("吃鱼");}publicvoidcatchMouse(){System.out.println("捉老鼠");}}classDogextendsAnimal{publicvoideat(){System.out.println("Eatbones");}publicvoidwatchHouse(){System.out.println("Housekeeping");}}定义测试类:publicclassTest{publicstaticvoidmain(String[]args){//向上变换Animala=newCat();a.吃();//调用是Cat'seat//向下转型Catc=(Cat)a;c.catchMouse();//调用Cat的catchMouse}}1.6.5在异常转换的过程中,不小心会遇到这样的问题,请看下面的代码:publicclassTest{publicstaticvoidmain(String[]args){//向上转型Animala=newCat();a.吃();//调用Cat'seat//DowncastDogd=(狗)一个;d.watchHouse();//调用Dog的watchHouse【运行报错】}}这段代码可以编译,但是运行时报ClassCastException,类型转换异常!这是因为,很明显,创建了一个Cat类型的对象,当然不能在运行时将其转换为Dog对象。1.6.6instanceof关键字为了避免ClassCastException的发生,Java提供了instanceof关键字来检查引用变量的类型。格式如下:变量名instanceof数据类型如果变量属于该数据类型或其子类类型,则返回真。如果变量不属于数据类型或其子类类型,则返回false。所以,在转换之前,我们最好先做一个判断,代码如下:a.吃();//Cat的eat被调用//Downcastif(ainstanceofCat){Catc=(Cat)a;c.catchMouse();//Cat的catchMouse被调用}elseif(ainstanceofDog){Dogd=(Dog)a;d.watchHouse();//调用Dog的watchHouse}}}1.6.7JDK14提出了instanceof的新特性,将判断和强制转换合二为一//新特性//首先判断a是否是Dog类型,如果是,会转换成Dog类型,转换后的变量名是d//如果不是,则不转换,结果直接为falseif(ainstanceofDogd){d.lookHome();}elseif(ainstanceofCatc){c.catchMouse();}else{System.out.println("Cannotbeconvertedwithoutthistype");}1.7综合练习要求:按要求完成代码:1.定义dogattributes:age,colorBehavior:eat(Stringsomething)(somethingmeanstoeat)管家lookHome方法(无参数)2.定义猫属性:age,colorBehavior:eat(Stringsomething)Method(somethingmeanstoeat)抓住鼠标catchMouse方法(无参数)3.定义Person类//breeder属性:name,ageBehavior:keepPet(Dogdog,Stringsomething)方法功能:feedthepetdog,somethingmeansfeedingBehavior:keepPet(Catcat,Stringsomething)方法功能:喂养宠物猫,something代表喂养的东西生成带参数结构的空参数,set和get方法4.定义测试类(完成如下打印效果):keepPet(Dogdog,Stringsomethingthind)方法打印内容如下:30岁的老王,养了一只2岁的黑狗。2岁的黑狗用两条前腿紧贴着骨头吃掉了。keepPet(Catcat,Stringsomethind)方法打印内容如下:老李,25岁,养了一只3岁的灰猫。3岁的灰猫眯着眼睛,侧着头吃鱼。五、思考:1、Dog和Cat都是Animal的子类,在上面的例子中,为不同的动物定义了不同的keepPet方法,过于繁琐。你能简化它并实现简化的好处吗?2.Dog和Cat虽然都是Animal的子类,但是它们都有自己独特的方法。你能找到一种方法来调用keepPet中的独特方法吗?绘图分析:代码示例://animalclass(父类)publicclassAnimal{privateintage;私有字符串颜色;publicAnimal(){}publicAnimal(intage,Stringcolor){this.age=age;这。颜色=颜色;}publicintgetAge(){返回年龄;}publicvoidsetAge(intage){this.age=age;}公共字符串getC颜色(){返回颜色;}publicvoidsetColor(Stringcolor){this.color=color;}publicvoideat(Stringsomething){System.out.println("Theanimaliseating"+something);}}//猫类(子类)publicclassCatextendsAnimal{publicCat(){}publicCat(intage,Stringcolor){super(age,color);}@Overridepublicvoideat(Stringsomething){System.out.println(getAge()+"岁"+getColor()+"彩猫眯着眼睛侧着头吃东西"+something);}publicvoidcatchMouse(){System.out.println("猫捉老鼠");}}//狗类(子类)publicclassDogextendsAnimal{publicDog(){}publicDog(intage,Stringcolor){super(age,color);}//behavior//eat(Stringsomething)(something表示吃的东西)//看家lookHome方法(无参数)@Overridepublicvoideat(Stringsomething){System.out.println(getAge()+"year-old"+getColor()+"color狗用两条前腿紧紧抱住了"+something+"狠狠地eat");}publicvoidlookHome(){System.out.println("Thedogiswatchingthehouse");}}//breederclasspublicclassPerson{privateStringname;privateintage;publicPerson(){}publicPerson(Stringname,intage){this.name=name;this.age=age;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicintgetAge(){returnage;}publicvoidsetAge(intage){this.age=age;}//养狗/*publicvoidkeepPet(Dogdog,Stringsomething){System.out.println("Ageis"+age+"岁"+名字+"养了一只狗"+dog.getColor()+"颜色"+dog.getAge()+"岁狗");dog.eat(something);}//养一只猫publicvoidkeepPet(Catcat,Stringsomething){System.out.println("年龄是"+年龄+"岁"+名字+"养一只"+cat.getColor()+"颜色“+cat.getAge()+"岁猫");猫吃(东西);}*///我想要一个方法,可以接收所有的动物,包括猫,包括狗//方法的形式参数:可以写成这些类的父类AnimalpublicvoidkeepPet(Animala,Stringsomething){if(ainstanceofDogd){System.out.println("年龄是"+年龄+"岁"+姓名+"养狗"+a.getColor()+"颜色"+a.getAge()+“狗的年龄”);d.吃(东西);}elseif(ainstanceofCatc){System.out.println("年龄是"+age+"岁"+name+"养了一只猫"+c.getColor()+"color"+c.getAge()+"岁猫");c.吃(东西);}else{System.out.println("没有这种动物");}}}//测试类publicclassTest{publicstaticvoidmain(String[]args){//创建对象并调用方法/*Personp1=newPerson("老王",30);Dogd=newDog(2,"Black");p1.keepPet(d,"骨头");Personp2=newPerson("老Li",25);Catc=newCat(3,"Ash");p2.keepPet(c,"Fish");*///创建饲养员Person的对象p=newPerson("老王",30);Dogd=newDog(2,"Black");Catc=newCat(3,"Grey");p.keepPet(d,"Bone");p.keepPet(c,"Fish");}}