收藏 分销(赏)

实验4 继承、多态、抽象类、接口.doc

上传人:s4****5z 文档编号:8867746 上传时间:2025-03-05 格式:DOC 页数:15 大小:132KB 下载积分:10 金币
下载 相关 举报
实验4 继承、多态、抽象类、接口.doc_第1页
第1页 / 共15页
实验4 继承、多态、抽象类、接口.doc_第2页
第2页 / 共15页


点击查看更多>>
资源描述
实验4 继承、多态、抽象类、接口 注意:有些程序由于Word的关系,复制后,tab缩位可能会变成其它符号。需要你去调整一下,删除缩位,重新Tab 一、实验目的 1 二、实验要求 1 三、实验内容 2 1. 类的继承与覆盖练习 2 1.0 父类的哪些成员可以被继承? 2 1.1 父类Student(学生)与子类HiStudent(大学生) 2 1.2 实例方法为什么需要覆盖 4 1.3 验证成员方法的覆盖方式 5 1.4 this、super和super()的使用 6 1.5 变量、静态方法的隐藏。 9 1.6 隐藏与覆盖的综合性实例 10 2. 类的多态性练习 11 2.1 普通方法的重载(实验3,本实验略) 11 2.2 构造方法的重载(实验3,本实验略) 11 2.3 运行时多态与动态绑定 11 3. 抽象类 13 4. 接口 14 一、实验目的   通过编程和上机实验理解Java语言的面向对象特性,了解类的继承性和多态性的作用,掌握它们的实现方法,了解数据域和静态方法的隐藏,了解抽象类和接口的作用。 二、实验要求 1、 编写体现类的继承性(成员变量、成员方法、成员变量隐藏)的程序; 2、 编写体现类的多态性的程序; 3、 编写体现抽象类和接口功能的程序。 三、实验内容 1. 类的继承与覆盖练习   例如,圆是一种形状。圆类Circle是形状类Shape的子类。父类也叫基类、超类,子类也叫次类、扩展类、派生类。子类可从父类中产生,保留父类的成员变量和方法,并可根据需要对它们加以修改。新类还可添加新的变量和方法。这种现象就称为类的继承。   当建立一个父类时,不必写出全部成员变量和成员方法。只要简单地声明这个类是从一个已定义的类继承下来的,就可以引用被继承类的全部成员。 Java 提供了一个庞大的类库让开发人员继承和使用。设计这些类是出于公用的目的,因此,很少有某个类恰恰满足你的需要。你必须设计自己的能处理实际问题的类,如果你设计的这个类仅仅实现了继承,和父类一样,那样没什么用。所以,通常要对父类进行扩展,即添加新的属性和方法。这使得子类要比父类大,但更具特殊性,代表着一组更具体的对象。继承的意义就在于此。 1.0 父类的哪些成员可以被继承? 可以访问的成员才能被继承。 具体分2种情况: * 当父类和子类在同一个包时,子类可以继承父类中的public/protected/无修饰 级别的变量和方法; * 当父类和子类不在同一个包时,且父类是public的,那么子类可以继承父类的public/protected 级别的变量和方法。如果父类前没有public,那么子类无法访问父类,更谈不上继承的问题。 1.1 父类Student(学生)与子类HiStudent(大学生) (1) Student.java程序源代码如下。 public class Student //学生类 { protected String xm; //姓名 protected int xh; //学号 void setData(String m,int h) //设置姓名和学号 { xm =m; xh = h; } public void print() //输出姓名和学号 { System.out.println(xm+", "+xh); } } (2) HiStudent.java的描述和代码如下 程序功能:通过Student类产生子类HiStudent,其不仅具有父类的成员变量xm(姓名)、xh(学号),还定义了新成员变量xy(学院)、xi(系)。在程序中调用了父类的print方法,同时可以看出子类也具有该方法。 程序源代码如下 public class HiStudent extends Student{ //大学生类继承自学生类 protected String xy;//所在学院或大学名称 protected String xi;//所在系的名称 public static void main(String args[]){ Student s1 = new Student(); s1.setdata("李四",12321) ; s1.print(); HiStudent s2 = new HiStudent() ; s2.setdata("张三",12345); //调用父类的成员方法 s2.xy="XX大学"; //访问本类的成员变量 s2.xi="计算机系"; //访问本类的成员变量 s2.print();//调用父类的方法,就象调用它自己的一样 System.out.print(s2.xm+", "+s2.xy+", "+s2.xi); } } (3)编译并运行,结果如下图所示。 1.2 实例方法为什么需要覆盖 上例中,s2.print();语句调用父类的方法,只能输出父类的数据(姓名和学号)。如果想用函数输出姓名、学号、学院、系,需要在子类中另外添加一个函数。如果这个函数也叫print(),(嗯,这样好记),并且如果形参、返回类型都一样,并且其可见性修饰符和父类print()方法的一样甚至更开放,并且它们都不是静态的,并且它所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类,或者什么也不抛出,那么,我们可以说子类的print()方法覆盖(override)了父类的print()方法。子类中添加的print()可以这样写: public void print() //输出姓名、学号、学院、系 { System.out.println(xm+", "+xh+", "+xy+", "+xi); } 可见,子类的覆盖方法重写(修改)了父类的被覆盖方法。实现了不同的功能。当然,父类的被覆盖方法仍然存在,在子类中可以用super.print()来引用。 嗯,如果参数不一样,那也叫重载。如果返回类型不一样,或者一个静态一个非静态,或者子类方法的开放性比父类被覆盖方法的更低,都会编译错。如果两个方法其它一样,又都是静态的,这没问题,叫做隐藏,而非覆盖。另外,父类中用final修饰的方法不能被覆盖。 再重复一下方法覆盖的概念:如果在子类中定义一个方法,其名称、返回类型及参数签名正好与父类中某个方法的名称、返回类型及参数签名相匹配,那么可以说,子类的方法覆盖了父类的方法。 覆盖的说明: · 子类的方法名称返回类型及参数签名 必须与父类的一致 · 子类方法不能缩小父类方法的访问权限 · 子类方法不能抛出比父类方法更多的异常 · 方法覆盖只存在于子类和父类之间,同一个类中只能重载 · 父类的静态方法不能被子类覆盖为非静态方法 · 子类可以定义于父类的静态方法同名的静态方法,以便在子类中隐藏父类的静态方法(满足覆盖约束),     · Java虚拟机把静态方法和所属的类绑定,而把实例方法和所属的实例绑定。 · 父类的非静态方法不能被子类覆盖为静态方法 · 父类的私有方法不能被子类覆盖 · 父类的抽象方法可以被子类通过两种途径覆盖(即实现和覆盖) · 父类的非抽象方法可以被覆盖为抽象方法 1.3 验证成员方法的覆盖方式   方法覆盖为子类提供了修改父类实例方法的能力。例如,Object类的toString方法返回的字符串是“类名@随机码”这样的。子类可以修改层层继承下来的Object 根类的toString 方法,让它输出一些更有用的信息。下面的程序显示了在子类Circle 中添加toString 方法,用来返回圆半径和圆面积信息。 (1) 编写Circle类,覆盖Object 类的toString方法,Circle类和Test类源代码如下。 class Circle { private int radius; Circle(int r) { setRadius(r); } public void setRadius(int r) { radius=r; } public int getRadius() { return radius; } public double area() { return Math.PI*radius*radius; } public String toString() {//覆盖继承自Object的toString()方法 return "圆半径:"+getRadius()+" 圆面积:"+area(); } } public class Test{ public static void main(String args[]) { Circle c=new Circle(10); System.out.println(c.toString()); } } 1.4 this、super和super()的使用 (1) 程序功能:说明this.、this()、super. 和super()的用法。程序首先定义Point(点)类,然后创建点的子类Line(线)。Line继承了Point的点,它自己又定义了一个点。最后通过Test类输出线段的长度。Line对象程序中通过super(a,b)调用父类Point 的构造方法为父类的x和y赋值。在子类Line 的setLine方法中,因为参数名和成员变量名相同,为给成员变量赋值,使用this 引用,告诉编译器是为当前类的成员变量赋值。在length 和toString 方法中使用父类成员变量时,使用super 引用,告诉编译器使用的是父类的成员变量。 (2) 程序源代码如下。 class Point { protected int x, y; Point(int a, int b) { setPoint(a, b); } public void setPoint(int a, int b) { x=a; y=b; } } class Line extends Point { protected int x, y; Line(int a, int b) { super(a, b); setLine(a, b); } public void setLine(int x, int y) { this.x=x+x; this.y=y+y; } public double length() { int x1=super.x, y1=super.y, x2=this.x, y2=this.y;//this.可省略 return Math.sqrt((x2-x1) * (x2-x1) + (y2-y1) * (y2-y1)); } public String toString() { return "直线端点:[" + super.x + "," + super.y + "] [" +x + "," + y + "] 直线长度:" + this.length(); } } public class Test{ public static void main(String args[]) { Line line=new Line(50, 50); System.out.println(line.toString()); } } 说明:这个例子只为验证super(),super.,this.等的用法。当然,线并不是一种点,所以用Line继承Point不合适。用聚合的形式更合适些。例如可以如下修改: class Point { protected int x, y; Point(int x, int y) { setPoint(x, y); } public void setPoint(int x, int y) { this.x = x; this.y = y; } } class Line{ Point a, b; public double length() { return Math.sqrt((a.x-b.x) * (a.x-b.x) + (a.y-b.y) * (a.y-b.y)); } public Line(){} public Line(Point a, Point b){ this.a = a; this.b = b; } public Line(int x1, int y1, int x2, int y2){ a = new Point(x1, y1); b = new Point(x2, y2); } } public class Test{ public static void main(String args[]) { Line line=new Line(1,1,2,0); System.out.println("直线端点:[" + line.a.x + "," + line.a.y + "] - [" +line.b.x + "," + line.b.y + "] 直线长度:" +line.length()); } } 1.5 变量、静态方法的隐藏。 父类和子类定义了同名的变量,或者相同的静态方法。 (1)父类和子类有相同的变量。父类变量被隐藏。将B类中的x所在行屏蔽起来或者不屏蔽,比较输出结果有何变化。 class A{ int x=1; } class B extends A{ //int x = 2; void printX(){//输出父类对象的x值 System.out.println(super.x);//如果B类没有声明x,那么super.可省略。但如果B类也声明了变量x,那么父类的x被隐藏,要访问父类对象的成员,super.不能省略 } } public class Test{ public static void main(String []s){ B p = new B(); System.out.println(p.x); p.printX(); //System.out.println(p.super.x);//语法错误。要用super.x引用B类的父类对象的成员,可以在B类中进行 } } (2)父类和子类有相同的静态方法。父类静态方法被隐藏。下面B类中的print()方法,屏蔽或不屏蔽,输出结果有何不同? class A{ static void print(){ System.out.println("A"); } } class B extends A{ /* static void print(){ System.out.println("B"); } */ } public class Test{ public static void main(String []s){ B p = new B(); p.print(); } } 【说明】上面A、B两类中的print()方法都是static的,称为静态方法隐藏。如果都是非静态的,则称为覆盖。如果其中一个是静态的,另一个非静态,则编译错,提示“无法覆盖”。 1.6 隐藏与覆盖的综合性实例 注意看蓝色或红色的注释。其中,红色的注释是容易出错的地方。 class A{ int x = 1;//被隐藏 void print(){//被覆盖 System.out.println("这里是父类方法,x="+x);//父类A的方法中访问的变量必然是A类或A的父类的,不可能访问B类的。 m();//父类A的方法中调用的实例方法m()是子类B的,由于发生了覆盖 } void m(){//被覆盖 System.out.println("这里是父类的实例方法m()"); } static void m2(){//被隐藏 System.out.println("这里是父类的静态方法m2()"); } } class B extends A{ int x = 2; void print(){ System.out.println("这里是子类方法,x="+x);//子类方法访问的变量是子类对象的(当然条件是子类中声明了这个变量) System.out.println("这里是子类方法,super.x="+super.x);//super.x是父类对象的 super.print();//调用父类的print()方法 m();//调用本对象的m()方法 } void m(){ System.out.println("这里是子类的实例方法m()"); } static void m2(){ System.out.println("这里是子类的静态方法m2()"); } } public class Test{ public static void main(String []s){ A p = new B(); System.out.println(p.x);//通过引用变量p来访问变量或静态方法,要看p的声明类型。所以x是A类的。 p.m2();//同上。静态方法m2()是A类的。 p.print();//通过引用变量p来访问实例方法,要看p指向的对象的实际类型。由于覆盖,调用的print()方法是子类的。 } } 2. 类的多态性练习 c++允许多重继承(多个直接父类),功能强大,但复杂的继承关系也给c++开发者带来了很大麻烦。为了规避风险,java只允许单重继承,即,只能有一个直接父类,所以继承关系简单明了。但在功能上又给开发者提出了难题。因此,Java用多态性、抽象类、接口的概念来弥补此项不足。 多态性: 在程序中同一符号或名字在不同情况下具有不同解释 Ø 编译时多态性: 指在程序编译阶段即可确定下来的多态性。重载发生时,编译器根据方法签名来确定未来会调用哪个方法。 Ø 运行时多态性: 指必须等到程序动态运行时才可确定的多态性,JVM根据引用变量指向的实际对象来绑定方法。 2.1 普通方法的重载 (参考实验3,本实验略) 2.2 构造方法的重载 (参考实验3,本实验略) 2.3 运行时多态与动态绑定 一组方法,在多个类中都有定义。由于继承与覆盖,到底调用哪个方法,需要根据不同的对象来进行动态绑定。因此结果呈现多态性。 如果A是父类,B是子类,那么A p = new B();是合法的。p的类型被声明成A类型,但p实际上指向一个B类对象。 (1) 如果A、B两类中都有实例方法m(),那么p.m()调用的是B中的m()。 (2) 如果A中有而B中没有,那么p.m()调用的是B类对象从A继承而来的m()方法。这是动态绑定。 (3)如果A中没有m()方法,则无论B中有没有,都会编译错。因为编译并非执行,编译器发现p是A类型的,而A类型以及A的父类都没有m()方法,也就是说,A类没有这个方法,也没有通过继承获得这个方法。 另外,关于静态方法: (1)如果m()方法在A、B两个类中,一个静态一个非静态,则编译错。(无法覆盖) (2)如果m()方法在A中是静态的,在B中没有,或者也是静态的,那么p.m()调用的是A类的静态方法。编译的时候就把p.m()换成A.m()。读1.6隐藏与覆盖的综合性实例。 (3)如果A中没有静态方法m(),那么编译器要看A的父类有没有。静态方法虽然无法覆盖,但也可继承。如果其父类也没有,则会编译错。 下面的程序,Test类中函数showInformation的形参是Person类型的引用变量x。实参如果是其子类对象,那么就是让x指向子类对象,这和A p = new B()的原理是一样的。 //代码Test.java class Person{ protected String name; //姓名 Person(){} Person(String name){ this.name = name; } String information(){ return "类Person: "+name; } } class Student extends Person{ //学生类 protected int id; //学号 Student(){} Student(String name, int id){ super(name); this.id = id; } /* String information(){ return "类Student: "+name+id; } */ } class HiStudent extends Student{ //大学生类继承自学生类 protected String school;//大学名称 protected String department;//系的名称 HiStudent(){} HiStudent(String name, int id, String school, String department){ super(name, id); this.school = school; this.department = department; } String information(){ return "类HiStudent: "+name+id+school+department; } } public class Test{ public static void main(String []as){ showInformation(new Person("张三")); showInformation(new Student("张三", 201122));//Student类没有information方法,因此showInformation将调用Student对象继承来的information方法。这是动态绑定。 showInformation(new HiStudent("张三", 201122, "西南大学", "计科系")); } static void showInformation(Person x){ System.out.println(x.information()); } } 问题: 1、如果在Student类中,将对information()方法的屏蔽取消,结果有何变化? 2、Test类showInformation方法的形参类型是Person。将其改为Object,编译能通过吗? 3. 抽象类 将父类设计得非常抽象,让它包含所有子类的共同属性、方法,以至于它没有具体的实例。如果子类不是抽象类,就必须实现(覆盖)抽象父类中的所有抽象方法。这其实也就规定了子类必须实现的的共同行为。 abstract class GeometricShape{//几何形状类 public abstract double getArea();//不知道具体形状,无法计算,只能定义为抽象方法 public abstract double getPerimeter(); } class Circle extends GeometricShape{ private double radius; public Circle(double radius){ this.radius = radius; } public double getArea(){ return Math.PI*radius*radius; } public double getPerimeter(){ return 2*Math.PI*radius; } } class Rect extends GeometricShape{ private double width, height; public Rect(double w, double h){ width = h; height = h; } public double getArea(){ return width*height; } public double getPerimeter(){ return 2*(width+height); } } public class Test{ public static void main(String []s){ GeometricShape g1 = new Circle(5); GeometricShape g2 = new Rect(5, 3); System.out.println("面积相同吗?" + equalArea(g1,g2)); printGeometricShape(g1); printGeometricShape(g2); } public static boolean equalArea(GeometricShape g1, GeometricShape g2){ return g1.getArea()==g2.getArea(); } public static void printGeometricShape(GeometricShape x){ System.out.println(); if(x instanceof Circle) System.out.println("这是一个圆"); else if(x instanceof Rect) System.out.println("这是一个矩形"); else return; System.out.println("面积是" + x.getArea()); System.out.println("周长是" + x.getPerimeter()); } } 问:如果将抽象类GeometricShape中的抽象方法删除,编译能通过吗? 下面是关于抽象类的说明: l 注意: – 1 抽象类前需加修饰符abstract – 2 不能使用new方法进行实例化,故,抽象类必须被继承 – 3 抽象类可包含常规类能够包含的任何东西,例如构造方法等非抽象方法,其构造方法在子类的构造方法中调用 – 4 没有抽象方法的类也可被声明为抽象类 – 5 包含抽象方法的类必须声明为抽象类 – 6 若子类没有实现父类的全部抽象方法,它也必须声明为抽象类 – 7 抽象方法必须是非静态的,子类中的实现也必须是非静态的,否则无法覆盖 – 8 即便父类是具体类,子类也可能是抽象类 – 9 抽象类虽然无法用new实例化,但可以用作数据类型 例:抽象类 x = new 子类(); 4. 接口 接口允许我们在看起来不相干的对象之间定义共同属性和共同行为。 下面的例子中,电视机TV和人Person看似不相干,但它们有相同的行为,比如,都有年龄信息。它们都实现了年龄接口Age。注意:TV和Person都不是Age的子类,但它们又可以看做是Age的子类,还可以直接访问Age中的常量。 更多的接口学习,将在以后的图形界面和事件的章节中学习。这里学习接口的基本知识。 interface Age{ //定义年龄接口 int month = 3;//int前面自动隐含了public static final String ageInformation();//void前面自动隐含了public abstract //String ageInformation(){}//编译错,接口中的方法不能有{}主体 } class TV implements Age{//电视机有年龄,TV要实现Age接口 public String ageInformation(){//实现接口中的抽象方法pringAge(),这也是覆盖 return "这台电视机的年龄是5岁"+month+"个月";//可访问接口中的常量month } } class Person implements Age{//人有年龄,Person要实现Age接口 public String ageInformation(){//实现接口中的抽象方法pringAge(),这也是覆盖 return "这个人的年龄是20岁"+month+"个月";//可访问接口中的常量month } } public class Test{ static void print(Age x){//Age是接口,虽然无法用new来建立对象,但它可作为类型,相当于是TV和Person的父类 System.out.println(x.ageInformation()); } public static void main (String args[]){ print(new TV()); print(new Person()); } } 编译并运行。
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 包罗万象 > 大杂烩

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2025 宁波自信网络信息技术有限公司  版权所有

客服电话:4009-655-100  投诉/维权电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服