《面向对象:继承和多态.ppt》由会员分享,可在线阅读,更多相关《面向对象:继承和多态.ppt(43页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、Java程序设计程序设计重庆交大2上一章内容回顾5.面向对象:构造方法,封装与隐藏:构造器理解构造器构造器的重载:封装理解封装访问控制符:包(package)包及其作用package和import语句Java的常用包1/8/202336.面向对象:继承和多态6.1 类的继承6.1.1 继承的特点6.1.2 重写父类的方法6.1.3 父类实例的super引用6.1.4 调用父类的构造器6.2 多态6.2.1 多态性6.2.2 引用变量的强制类型转换6.2.3 instanceof运算符6.3 继承和组合6.4 课后作业1/8/202346.1 类的继承6.1.1 继承的特点:继承是面向对象三大特
2、征之一,也是实现软件复用的重要手段。:Java的继承通过关键字extendsextends来实现,实现继承的类称为子类子类子类子类,被继承的类称为基类基类基类基类、超类超类超类超类、父类父类父类父类。父类与子类的关系,是一种一般和特殊一般和特殊一般和特殊一般和特殊的关系。例如水果和苹果的关系,苹果继承了水果,苹果是水果的子类,则苹果是一种特殊的水果。:因为子类是一种特殊的父类,因此父类包含的范围总比子类包含的范围要大,因此父类是大类,子类是小类因此父类是大类,子类是小类。:Java的继承是单继承单继承单继承单继承,每个类最多只有一个直接父类。1/8/202356.1 类的继承6.1.1 继承的
3、特点:Java里子类继承父类的语法格式如下:修饰符修饰符 class subclass extendsextends superclass /类定义部分类定义部分:“extends”的含义是子类扩展了父类,将可以获得父类的全部属性和方法,但子类不能获得父类构造方法。:以下程序示范了子类继承父类的特点。1/8/202366.1 类的继承6.1.1 继承的特点:程序清单:chapter06test1Fruit.java、Apple.javapackage chapter06.test1;public class Fruit public double weight;public void info
4、()System.out.println(我是一个水果!重+weight+g!);package chapter06.test1;public class Apple extends Fruit public static void main(String args)/创建Apple的对象 Apple a=new Apple();/Apple对象本身没有weight属性。/因为Apple的父类有weight属性,也可以访问Apple对象的属性 a.weight=56;a.weight=56;/调用Apple对象的info方法 a.info();a.info();该程序的输出结果为:该程序的输出
5、结果为:我是一个水果!重我是一个水果!重56.0g!1/8/202376.1 类的继承6.1.1 继承的特点1/8/202386.1 类的继承6.1.2 重写父类的方法:子类扩展了父类,子类是一个特殊的父类。大部分时候,子类总是以父类为基础,额外增加新的属性和方法。但有一种情况例外:子类需要重写父类的方法子类需要重写父类的方法子类需要重写父类的方法子类需要重写父类的方法。:例如,鸟类都包含了飞翔(fly)的方法,其中鸵鸟是一种特殊的鸟类,因此鸵鸟应该是鸟的子类,因此它也将从鸟类获得飞翔方法,但这个飞翔方法明显不适合鸵鸟,因此,这个飞翔方法明显不适合鸵鸟,因此,鸵鸟需要重写鸟类的方法鸵鸟需要重写
6、鸟类的方法。:下面程序先定义一个Bird类。1/8/202396.1 类的继承6.1.2 重写父类的方法:程序清单:chapter06test1Bird.javapackage chapter06.test1;public class Bird /Bird类的fly方法 public void fly()System.out.println(我在天空里自由自在地飞翔.);1/8/2023106.1 类的继承6.1.2 重写父类的方法:下面再定义一个Ostrich类,这个类扩展了Bird类,但重写了Bird类的fly方法。:程序清单:chapter06test1Ostrich.javapacka
7、ge chapter06.test1;public class Ostrich extends Bird /重写Bird类的fly方法 publicpublic voidvoid fly()fly()System.out.println(System.out.println(我只能在地上奔跑我只能在地上奔跑我只能在地上奔跑我只能在地上奔跑.);.);public static void main(String args)/创建Ostrich对象 Ostrich os=new Ostrich();/执行Ostrich对象的fly方法,将输出我只能在地上奔跑.os.fly();该程序的输出结果为:
8、该程序的输出结果为:我只能在地上奔跑我只能在地上奔跑.1/8/2023116.1 类的继承6.1.2 重写父类的方法:这种子类包含父类同名方法的现象被称为方法重写方法重写方法重写方法重写,也称为方方方方法覆盖法覆盖法覆盖法覆盖(Override)(Override)。可以说子类重写了父类的方法,也可以说子类覆盖了父类的方法。方法的重写要遵循“两同两小一大两同两小一大两同两小一大两同两小一大”。:两同两同两同两同:1.方法名相同;2.形参列表相同。:两小两小两小两小:1.子类方法返回值类型应比父类方法返回值类型更小(即子类即子类)或相同;2.子类方法声明抛出的异常应比父类方法声明抛出的异常类更小
9、或相同。:一大一大一大一大:1.子类方法的访问控制权限应比父类方法更大或相等。1/8/2023126.1 类的继承6.1.2 重写父类的方法:注意:1.覆盖方法和被覆盖方法要么都是类方法,要么都是实例方法;不能一个是类方法,一个是实例方法,否则编译出错。2.当子类覆盖了父类方法后,子类的对象将无法直接访问父类中被覆盖的方法,如果需要访问,可以使用super(super(被覆被覆被覆被覆盖的是实例方法盖的是实例方法盖的是实例方法盖的是实例方法)或者父类名父类名父类名父类名(被覆盖的是类方法被覆盖的是类方法被覆盖的是类方法被覆盖的是类方法)作为调用者来调用父类被覆盖的方法。3.如果父类方法具有pr
10、ivate访问权限,则该方法对其子类是隐藏的,因此其子类无法访问该方法,也就无法重写该方法;如果子类定义了一个与父类private方法具有相同方法名、相同形参列表、相同返回值类型的方法,依然不是重写,只是在子类中重新定义了一个新方法。1/8/2023136.1 类的继承 父类实例的super引用(1)通过通过super引用调用父类被覆盖的方法引用调用父类被覆盖的方法:如果需要在子类中调用父类被覆盖的实例方法,可以通过关键字supersuper作为调用者来调用父类被覆盖的方法。:super是Java提供的一个关键字,它是直接父类的默认引用。:例如,为上面的Ostrich类添加callOverri
11、dedMethod方法,在其中调用Bird类被覆盖的fly方法。:完整的Ostrich类代码如下。:程序清单:chapter06test1Ostrich.java1/8/2023146.1 类的继承 父类实例的super引用package chapter06.test1;public class Ostrich extends Bird /重写Bird类的fly方法 public void fly()System.out.println(我只能在地上奔跑.);public void callOverridedMethod()/在子类方法中通过super来显式调用父类被覆盖的方法。supersu
12、per.fly();.fly();public static void main(String args)/创建Ostrich对象 Ostrich os=new Ostrich();/执行Ostrich对象的fly方法,将输出我只能在地上奔跑.os.fly();os.callOverridedMethod();该程序的输出结果为:该程序的输出结果为:我只能在地上奔跑我只能在地上奔跑.我在天空里自由自在地飞翔我在天空里自由自在地飞翔.1/8/2023156.1 类的继承 父类实例的super引用1/8/2023166.1 类的继承 父类实例的super引用1/8/2023176.1 类的继承 父
13、类实例的super引用(2)通过通过super引用访问父类的属性引用访问父类的属性:如果子类定义了和父类同名的属性,也会发生子类属性覆子类属性覆子类属性覆子类属性覆盖父类属性盖父类属性盖父类属性盖父类属性的情形。正常情况下,子类里定义的方法访问该属性,都是访问子类属性,无法访问到父类被覆盖的属性。但在子类定义的实例方法中可以通过supersuper引用引用引用引用来访问父类被覆盖的属性。详见下面的例子:程序清单:chapter06SubClass.java1/8/2023186.1 类的继承 父类实例的super引用package chapter06;class BaseClass publi
14、c int a=5;public class SubClass extends BaseClass public int a=7;public void accessOwner()System.out.println(a);public void accessBase()/通过super来访问方法调用者对应的父类对象 System.out.println(System.out.println(supersuper.a);.a);public static void main(String args)SubClass sc=new SubClass();/直接访问SubClass对象的a属性将会
15、输出7 System.out.println(sc.a);sc.accessOwner();/输出7 sc.accessBase();/输出5 该程序的输出结果为:该程序的输出结果为:7751/8/2023196.1 类的继承 调用父类的构造器(1)通过通过super引用调用父类的构造器引用调用父类的构造器:在一个构造器中调用另一个重载的构造器要使用thisthis引用引用引用引用来调用。在子类构造器中调用父类构造器要使用supersuper引引引引用用用用来调用。详见下面的例子。:程序清单:chapter06test1Sub.java1/8/2023206.1 类的继承 调用父类的构造器pa
16、ckage chapter06.test1;class Base public double size;public String name;public Base(double size,String name)this.size=size;this.name=name;public class Sub extends Base public String color;public Sub(double size,String name,String color)/通过super调用来调用父类构造器的初始化过程 supersuper(size,name);(size,name);this.c
17、olor=color;public static void main(String args)Sub s=new Sub(5.6,测试对象,红色);/输出Sub对象的三个属性 System.out.println(s.size+-+s.name+-+s.color);该程序的输出结果为:该程序的输出结果为:5.6-测试对象测试对象-红色红色1/8/2023216.1 类的继承 调用父类的构造器(2)构造器的调用顺序构造器的调用顺序:在调用子类的构造器创建一个子类实例时,父类构造器总父类构造器总父类构造器总父类构造器总会在子类构造器之前执行会在子类构造器之前执行会在子类构造器之前执行会在子类构造
18、器之前执行;不仅如此,执行父类构造器时,系统会再次上溯执行其父类的构造器,;以此类推,创建任何Java对象,最先执行的总是类的构造器。详见下面的例子。:程序清单:chapter06test1Wolf.java1/8/2023226.1 类的继承 调用父类的构造器package chapter06.test1;class Creature public Creature()System.out.println(Creature无参数的构造器);class Animal extends Creature public Animal(String name)System.out.println(An
19、imal带一个参数的构造器,该动物的name为+name);public Animal(String name,int age)/使用this调用同一个重载的构造器 this(name);System.out.println(Animal带2个参数的构造器,其age为+age);public class Wolf extends Animal public Wolf()/显式调用父类有2个参数的构造器 super(土狼,3);System.out.println(Wolf无参数的构造器);public static void main(String args)new Wolf();该程序的输出
20、结果为:该程序的输出结果为:Creature无参数的构造器无参数的构造器Animal带一个参数的构造器,该动物的带一个参数的构造器,该动物的name为土狼为土狼Animal带带2个参数的构造器,其个参数的构造器,其age为为3Wolf无参数的构造器无参数的构造器1/8/2023236.2 多态:Java引用变量有两个类型两个类型两个类型两个类型:一个是编译时的类型编译时的类型编译时的类型编译时的类型,一个是运行时的类型运行时的类型运行时的类型运行时的类型,编译时的类型由声明该变量时使用的类型编译时的类型由声明该变量时使用的类型编译时的类型由声明该变量时使用的类型编译时的类型由声明该变量时使用的
21、类型决定,运行时的类型由实际赋给该变量的对象决定决定,运行时的类型由实际赋给该变量的对象决定决定,运行时的类型由实际赋给该变量的对象决定决定,运行时的类型由实际赋给该变量的对象决定。:如果编译时类型和运行时的类型不一致如果编译时类型和运行时的类型不一致如果编译时类型和运行时的类型不一致如果编译时类型和运行时的类型不一致,这就有可能出现所谓的多态多态多态多态(Polymorphism)(Polymorphism)。1/8/2023246.2 多态6.2.1 多态性:先看以下例子。:程序清单:chapter06test2SubClass.javapackage chapter06.test2;cl
22、ass BaseClass public int book=6;public void base()System.out.println(父类的普通方法);public void test()System.out.println(父类的被覆盖的方法);public class SubClass extends BaseClass /重新定义一个book实例属性覆盖父类的book实例属性 public String book=轻量级J2EE企业应用实战;public void test()System.out.println(子类的覆盖父类的方法);public void sub()System
23、.out.println(子类的普通方法);public static void main(String args)/下面编译时类型和运行时类型完全一样,因此不存在多态 BaseClass bc=new BaseClass();/输出 6 System.out.println(bc.book);/下面两次调用将执行BaseClass的方法 bc.base();bc.test();该程序的输出结果为:该程序的输出结果为:6父类的普通方法父类的普通方法父类的被覆盖的方法父类的被覆盖的方法轻量级轻量级J2EE企业应用实战企业应用实战父类的普通方法父类的普通方法子类的覆盖父类的方法子类的覆盖父类的方法
24、子类的普通方法子类的普通方法6父类的普通方法父类的普通方法子类的覆盖父类的方法子类的覆盖父类的方法1/8/2023256.2 多态6.2.1 多态性package chapter06.test2;class BaseClass public int book=6;public void base()System.out.println(父类的普通方法);public void test()System.out.println(父类的被覆盖的方法);/下面编译时类型和运行时类型完全一样,因此不存在多态 SubClass sc=new SubClass();/输出轻量级J2EE企业应用实战 Sys
25、tem.out.println(sc.book);/下面调用将执行从父类继承到的base方法 sc.base();/下面调用将执行从当前类的test方法 sc.test();/下面调用将执行从当前类的sub方法 sc.sub();/下面编译时类型和运行时类型不一样,多态发生 BaseClass ploymophicBc=BaseClass ploymophicBc=newnew SubClass();SubClass();/输出 6 表明访问的是父类属性 System.out.println(ploymophicBc.book);/下面调用将执行从父类继承到的base方法 ploymophic
26、Bc.base();/下面调用将执行当前类的test方法 ploymophicBc.test();/因为ploymophicBc的编译类型是BaseClass,BaseClass类没有 /提供sub方法,所以下面代码编译时会出现错误。/ploymophicBc.sub();该程序的输出结果为:该程序的输出结果为:6父类的普通方法父类的普通方法父类的被覆盖的方法父类的被覆盖的方法轻量级轻量级J2EE企业应用实战企业应用实战父类的普通方法父类的普通方法子类的覆盖父类的方法子类的覆盖父类的方法子类的普通方法子类的普通方法6父类的普通方法父类的普通方法子类的覆盖父类的方法子类的覆盖父类的方法1/8/2
27、023266.2 多态6.2.1 多态性:上述例子中,第三个引用变量polymopicBc比较特殊,它的编译时类型是BaseClass,而运行时类型是SubClass,当调用该引用变量的test方法(BaseClass类定义了该方法,子类SubClass覆盖了父类的该方法),实际执行的是SubClass类中覆盖后的test方法,这就是多态多态多态多态。:因为子类其实是一种特殊的父类,因此Java允许把一个子类对象直接赋给一个父类引用变量,无须任何类型转换,或者被称为向上转型向上转型向上转型向上转型(upcasting)(upcasting)、或上溯上溯上溯上溯。向上转型由系统自动完成。/下面编
28、译时类型和运行时类型不一样,多态发生BaseClass ploymophicBc=BaseClass ploymophicBc=newnew SubClass();SubClass();1/8/2023276.2 多态6.2.1 多态性1/8/2023286.2 多态6.2.1 多态性:当把一个子类对象直接赋给父类引用变量,例如上述例子中的语句“BaseClass ploymophicBc=new SubClass();”,这个ploymophicBc引用变量的编译时类型是BaseClass,而运行时类型是SubClass,当运行时调用该引用变量的方法时,其方法行为总是像子类方法的行为,而不是
29、像父类方法的行为,这将出现相同类型的变量、执行同一个方法时呈现出不同的行为特征,这就是多态多态多态多态。1/8/2023296.2 多态6.2.2 引用变量的强制类型转换:C语言里也有强制类型转换。:在Java程序里,引用变量只能调用它编译时类型的方法,引用变量只能调用它编译时类型的方法,引用变量只能调用它编译时类型的方法,引用变量只能调用它编译时类型的方法,而不能调用它运行时类型的方法而不能调用它运行时类型的方法而不能调用它运行时类型的方法而不能调用它运行时类型的方法(详见节中被注释的代码),即使它实际所引用对象确实包含该方法。如果需要让这个引用变量来调用它运行时类型的方法,则必须把它强制强
30、制强制强制类型转换类型转换类型转换类型转换成运行时类型。:强制类型转换运算符运算符运算符运算符是小括号小括号小括号小括号,语法如下:(type)variable;1/8/2023306.2 多态6.2.2 引用变量的强制类型转换:Java语言里的强制类型转换运算要注意:基本类型之间的转换基本类型之间的转换基本类型之间的转换基本类型之间的转换只能在数值类型之间进行,这里所说的数值类型包括整数型、字符型和浮点型。但数值型不能和布尔型之间进行类型转换。引用类型之间的转换引用类型之间的转换引用类型之间的转换引用类型之间的转换只能把一个父类变量转换成子类类型,如果是两个没有任何继承关系的类型,则无法进行
31、类型转换,否则编译时就会出现错误。如果试图把一个父类实例转换成子类类型,则这个实例必须实际上是子类实例才行(即编译时类型为父类类型,而运行时类型是子类类型),否则将会在运行时引发ClassCastException异常。:下面是进行强制类型转换的示范程序,下面程序详细说明了哪些情况可以进行类型转换,哪些情况不可以进行类型转换。1/8/2023316.2 多态6.2.2 引用变量的强制类型转换:程序清单:chapter06test2TestConversion.javapackage chapter06.test2;public class TestConversion public stati
32、c void main(String args)double d=13.4;longlong l=(l=(longlong)d;)d;System.out.println(l);int in=5;/下面代码编译时出错:试图把一个数值型变量转换为boolean型,/编译时候会提示:不可转换的类型 /boolean b=(boolean)in;/boolean b=(boolean)in;Object obj=Hello;/obj变量的编译类型为Object,是String类型的父类,可以强制类型转换 /而且obj变量实际上类型也是String类型,所以运行时也可通过 String objStr=
33、(String)obj;String objStr=(String)obj;System.out.println(objStr);/定义一个objPri变量,编译类型为Object,实际类型为Integer Object objPri=new Integer(5);/objPri变量的编译类型为Object,是String类的父类,可以强制转换 /而objPri变量实际上类型是Integer类型,所以下面代码运行时引发异常 /String str=(String)objPri;/String str=(String)objPri;该程序的输出结果为:该程序的输出结果为:13Hello1/8/2
34、023326.2 多态6.2.2 引用变量的强制类型转换:考虑到进行强制类型转换时可能会出现异常,因此进行类型转换前应先通过instanceofinstanceof运算符来判断是否可以成功转换。例如上面的代码“String str=(String)objPri;”运行时会引发ClassCastException异常,这是因为objPri不可转换成String类型,为了让程序更健壮,可以将代码修改为:if(objPri instanceof String)String str=(String)objPri;:在进行强制类型转换前,先用instanceofinstanceof运算符判断是否可以成功
35、转换,从而避免出现ClassCastException异常。1/8/2023336.2 多态6.2.3 instanceof运算符:instanceofinstanceof和类型转换运算符一样,都是Java提供的运算符。其语法格式为:引用变量引用变量 instanceof 类类(或接口或接口):instanceof运算符的左操作数通常是一个引用类型的变量,右操作数通常是一个类(也可以是接口),它用于判断左边的对象是否是右边类(或者其子类)的实例。如果是返回true,否则返回false。:下面的程序示范了instanceof运算符的用法。:程序清单:chapter06test2TestInsta
36、nceof.java1/8/2023346.2 多态6.2.3 instanceof运算符package chapter06.test2;public class TestInstanceof public static void main(String args)/声明hello为Object类,则hello的编译类型是Object,Object是所有类的父类 /但hello变量的实际类型是String Object hello=Hello;/String是Object类的子类,所以返回true。(字符串是否是Object类的实例:+(hello instanceof Objecthello
37、 instanceof Object);/返回true。(字符串是否是String类的实例:+(hello instanceof Stringhello instanceof String);/返回false。(字符串是否是Math类的实例:+(hello instanceof Mathhello instanceof Math);/String实现了Comparable接口,所以返回true。(字符串是否是Comparable接口的实例:+(hello instanceof hello instanceof ComparableComparable);String a=Hello;/Stri
38、ng类既不是Math类,也不是Math类的父类,所以下面代码编译通不过 /(字符串是否是Math类的实例:+(a instanceof Matha instanceof Math);该程序的输出结果为:该程序的输出结果为:字符串是否是字符串是否是Object类的实例:类的实例:true字符串是否是字符串是否是String类的实例:类的实例:true字符串是否是字符串是否是Math类的实例:类的实例:false字符串是否是字符串是否是Comparable接口的实例:接口的实例:true1/8/2023356.3 继承和组合6.3.1 使用继承的注意事项:继承是实现类重用的重要手段,但继承也带来了
39、一个最大的坏处:破坏封装破坏封装破坏封装破坏封装。组合是实现类重用的另一种方式。下面介绍继承和组合之间的练习和区别。:子类扩展父类时,子类将可以从父类继承得到属性和方法,如果访问权限允许,子类将可以直接访问父类的属性和方法,相当于子类可以直接复用父类的属性和方法,确实非常方便。:继承带来了高度复用的同时,也带来了一个严重的问题:继承严重地破坏了父类的封装性。前面介绍封装时提到:每个类都应该封装它内部信息和实现细节,而只暴露必要的方法给其他类使用。但在继承关系中,子类可以直接访问父类的属性和方法,从而造成子类和父类的严重耦合。1/8/2023366.3 继承和组合6.3.1 使用继承的注意事项:
40、为了保证父类良好的封装性,不会被子类随意改变,设计父类通常应该遵循如下规则:尽量隐藏父类的内部数据。不要让子类可以随意访问、修改父类方法。尽量不要在父类构造器中调用将要被子类重写的方法。如果想把某些类设置成最终类,即不再派生出子类,则可以用final修饰这个类。:到底何时需要从父类派生新的子类?不仅需要保证子类是一种特殊的父类,而且还需要具备以下两个条件之一:子类需要额外增加属性,而不仅仅是属性值的改变。子类需要增加自己独有的行为方式(包括增加新的方法或重写父类的方法)。1/8/2023376.3 继承和组合6.3.2 利用组合实现复用:继承继承继承继承要表达的是一种“是是是是(is-a)”(
41、is-a)”的关系,而组合组合组合组合表达的是“有有有有(has-a)”(has-a)”的关系。:【提问】试从现实生活中例举继承和组合的例子。:假设有三个类:Animal、Wolf和Bird,它们之间有如下图所示的继承树。1/8/2023386.3 继承和组合6.3.2 利用组合实现复用:程序清单:chapter06test3TestInherit.javapackage chapter06.test3;class Animal private void beat()(心脏跳动.);public void breath()beat();(吸一口气,吐一口气,呼吸中.);/继承Animal,直接
42、复用父类的breath方法class Bird extends Animal public void fly()(我在天空自在的飞翔.);/继承Animal,直接复用父类的breath方法class Wolf extends Animal public void run()(我在陆地上的快速奔跑.);public class TestInherit public static void main(String args)Bird b=new Bird();b.breath();b.fly();Wolf w=new Wolf();w.breath();w.run();1/8/2023396.3
43、继承和组合6.3.2 利用组合实现复用:如果仅从软件复用的角度仅从软件复用的角度仅从软件复用的角度仅从软件复用的角度,将上面三个类的定义改为如下形式也可实现相同的复用。:程序清单:chapter06TestComposite.java1/8/2023406.3 继承和组合6.3.2 利用组合实现复用package chapter06;class Animal private void beat()System.out.println(心脏跳动.);public void breath()beat();System.out.println(吸一口气,吐一口气,呼吸中.);class Bird /
44、将原来的父类嵌入原来的子类,作为子类的一个组合成分 private Animal a;public Bird(Animal a)this.a=a;/重新定义一个自己的breath方法 public void breath()/直接复用Animal提供的breath方法来实现Bird的breath方法。a.breath();public void fly()System.out.println(我在天空自在的飞翔.);class Wolf /将原来的父类嵌入原来的子类,作为子类的一个组合成分 private Animal a;public Wolf(Animal a)this.a=a;/重新定义
45、一个自己的breath方法 public void breath()/直接复用Animal提供的breath方法来实现Bird的breath方法 a.breath();public void run()System.out.println(我在陆地上的快速奔跑.);public class TestComposite public static void main(String args)/此时需要显式创建被嵌入的对象 Animal a1=new Animal();Bird b=new Bird(a1);b.breath();b.fly();/此时需要显式创建被嵌入的对象 Animal a2=
46、new Animal();Wolf w=new Wolf(a2);w.breath();w.run();1/8/2023416.3 继承和组合6.3.2 利用组合实现复用:对于上面定义的三个类:Animal、Wolf和Bird,它们对应的UML图如下图所示。1/8/2023426.4 课后作业1.(面向对象设计)某项目涉及到的实体包括:1.乐器(instrument):包含名称(name)和品牌(brand)属性,具有弹奏(play)方法。2.钢琴(Piano):一种特殊的乐器,新增按键数(keyNum)属性,具有特有的弹奏(play)方法。3.小提琴(Violin):一种特殊的乐器,新增琴弦数(chordNum)属性,具有特有的弹奏(play)方法。4.吉他(Guitar):一种特殊的乐器,新增按键数(keyNum)属性,具有特有的弹奏(play)方法。5.手风琴(Accordion):一种特殊的乐器,新增琴弦数(chordNum)属性,具有特有的弹奏(play)方法。:上述5个类的类图如下图所示,请用Java代码实现这5个类,(方法的实现只需输出提示信息即可);另外再新建一个Test类,用于测试这5个类,要求充分测试多态。1/8/2023436.4 课后作业1/8/2023