《《抽象类与接口》PPT课件.ppt》由会员分享,可在线阅读,更多相关《《抽象类与接口》PPT课件.ppt(83页珍藏版)》请在金锄头文库上搜索。
1、第第6章章 抽象类与接口抽象类与接口讲师:讲师:200906 08目目 标标 当完成本章后,你应该能够:当完成本章后,你应该能够:理解抽象方法理解抽象方法理解抽象类理解抽象类从抽象类继承从抽象类继承理解并使用接口理解并使用接口实现接口实现接口从接口继承从接口继承抽象类从形状开始抽象方法抽象类从形状开始从形状开始要开发一个图形系统 首先要定义相关的图形类 主要有: 三角形 平行四边形 矩形 正方形 父类父类根据继承的知识 以及形状之间的关系 设计了所有形状类的父类 类 Shape 该类的方法在继承时需要被覆盖 Shape.java父类父类 Shape未定义成员变量成员方法如下: public S
2、hape(); public double getPerimeter(); public double getArea(); public void draw();周长和面积周长和面积这是任何形状都有的属性 返回类型是 double 由于 Shape 是所有形状的父类 并不是具体的形状 那么这两个方法的方法体能否为空呢?答案是不能为空 因为方法的返回类型是 double 这两个方法必须返回一个 double 类型的值 否则产生编译错误,不能通过编译编译错误编译错误把两句 return 0.0; 注释后重新编译程序srcShape.java:9: 缺少返回语句缺少返回语句 srcShape.ja
3、va:13: 缺少返回语句缺少返回语句 2 错误子类子类 Triangle设计并实现子类三角形 Triangle Triangle.java 成员: private double a; private double b; private double c; public Triangle(); public Triangle(double a, double b, double c); public double getPerimeter(); public double getArea(); public void draw();子类子类 parallelogram设计并实现子类平行四边形
4、parallelogram Parallelogram.java成员: private double a; private double b; private double theta; public Parallelogram(); public Parallelogram(double a, double b, double theta); public double getA(); public double getB(); public double getPerimeter(); public double getArea(); public void draw();Math.sin
5、(theta)小问题 但影响最终计算结果经常使用角度 但该方法使用的是 弧度角度 转换为 弧度子类子类 Rectangle设计并实现子类矩形 rectangle Rectangle.java 该类从 Parallelogram 而非 Shape 进行继承成员: public Rectangle(); public Rectangle(double a, double b); public void draw();子类子类 Rectangle构造器的编写 通过 super 来调用父类的构造器 角度的赋值计算周长和面积 通过构造器已经完成了成员变量的赋值 因此该类不需要覆盖父类的这两个方法子类子类
6、 Square设计并实现子类正方形 square Square.java 该类从 Rectangle 进行继承成员: public Square(); public Square(double a); public void draw();子类子类 Square构造器的写法与其父类 Rectangle 类似 该类不需要覆盖计算周长和面积的方法类图类图可能的副作用可能的副作用成员的继承 包括成员变量和成员方法父类的修改对于子类的影响 子类对于父类有较强的依赖性 耦合,coupling 原则: 低耦合,高内聚分析分析父类 Shape 两个方法的方法体没有任何实际意义 但是为了编译能够通过 不能删去
7、返回语句 可是返回的值又没有任何实际意义两难境地 What can we do?解决办法解决办法可以告诉编译器 压根儿就没想实现这两个方法 因为即使定义了方法体 也没有任何实际用处 那还不如根本就别定义方法体不想实现的方法 只有方法头部,没有方法体抽象方法抽象方法抽象方法不需要实现的方法 之后方法的头部 没有方法体 用分号;代替正常的一对大括号 语法: public double getPerimeter(); public double getArea();重新编译 Shape.java编译错误编译错误srccnpetershapeShape.java:9: 缺少方法主体,或声明抽象 pub
8、lic double getPerimeter(); srccnpetershapeShape.java:11: 缺少方法主体,或声明抽象 public double getArea(); 2 错误抽象方法抽象方法必须把方法声明为抽象的使用 Java 语言的关键字 abstract改成如下形式: public abstract double getPerimeter(); public abstract double getArea(); public abstract void draw();重新编译 Shape.java编译错误编译错误srccnpetershapeShape.java:4
9、: cn.peter.shape.Shape 不是抽象的,并且未覆盖 cn.peter.shape.Shape 中的抽象方法 getArea() public class Shape 1 错误抽象类抽象类如果一个类中有一个或多个的抽象方法,则该类必须声明为抽象的,成为抽象类 方法: public abstract class Shape 重新编译 Shape.java编译错误编译错误srccnpetershapeShape.java:17: cn.peter.shape.Shape 是抽象的是抽象的 无法对其进行实例化无法对其进行实例化 Shape s = new Shape(); 1 错误原
10、因分析原因分析因为抽象类中可能有抽象方法,而抽象方法没有方法体,是不完整的 因此不允许创建抽象类的实例 抽象类不能实例化 new 后面不能出现抽象类 把类 Shape 的 main() 方法体注释 重新编译 Shape.java 重新编译其它子类的文件 javac -d classes srccnpetershape*.java类图类图 (抽象类版本抽象类版本)抽象方法抽象方法如果一个方法用 abstract 修饰 成为抽象方法 则该方法不能用以下修饰符修饰: private final static抽象方法抽象方法没有方法体的方法 用分号; 代替了一对大括号该方法在其所属的类被继承时由其子类
11、实现 即 方法覆盖 称 实现父类的抽象方法抽象类抽象类如果一个类中有一个或多个抽象方法 则该类必须声明为抽象类 否则产生编译错误,不能通过编译抽象类不能实例化 不能创建抽象类的对象 new 后不能出现抽象类的名称抽象类抽象类抽象类不能实例化,不能创建对象 那有什么用呢?专职工作: 父类 与 final 类恰好相反 final 类不能被继承,不能做父类 而抽象类不能实例化,只能做父类抽象类的子类抽象类的子类一个类如果从抽象类来继承 那么该类有两种选择: (1) 实现其父类所有的抽象方法 从而可以不声明为抽象类 (2) 如果至少有一个抽象方法没有实现 那么该子类也必须声明为抽象类 否则产生编译错误
12、,不能通过编译抽象类的好处抽象类的好处如果一个类从其父类 (抽象类) 继承 而该类不想作为抽象类 那么它必须实现其父类所有的抽象方法 如果有一个方法忘记实现 同时该类又没有声明为抽象类 则产生编译错误,不能通过编译抽象类的好处抽象类的好处通过编译器强制必须实现指定的方法 例如,抽象父类 Shape 有以下抽象方法: public abstract double getPerimeter(); public abstract double getArea(); public abstract void draw(); 那么,该类的所有直接子类或间接子类 必须实现这三种方法 否则,该子类就必须声明
13、为抽象类实现父类的抽象方法实现父类的抽象方法不一定每一个直接或间接子类 都必须由自己来亲自实现这三个方法 例如,类 Parallelogram 实现了这三个方法 而类 Rectangle 和类 Square 直接或间接从类Parallelogram 继承 这二者只覆盖了 draw() 方法 从其父类继承了 getPerimeter() 方法和 getArea() 方法抽象类的句柄抽象类的句柄虽然不能创建抽象类的对象 但是依然可以声明抽象类的句柄 例如 Shape s = new Triangle();一个类的句柄 可以引用其所有直接或间接子类的对象 这是由继承的语义得到的 这一点非常重要,也非
14、常普遍 在多态中会详细讨论抽象类例子抽象类例子类 java.lang.Number 即为一个抽象类 其抽象方法有: public abstract int intValue(); public abstract long longValue(); public abstract float floatValue(); public abstract double doubleValue();非抽象方法非抽象方法同时还有非抽象方法: public byte byteValue() return (byte) intValue(); public short shortValue() return
15、 (short) intValue(); 这说明 在抽象类中,非抽象方法可以调用抽象方法Number 的子类的子类该抽象类的子类有: java.lang.Byte java.lang.Short java.lang.Integer java.lang.Long java.lang.Float java.lang.Double java.math.BigInteger java.math.BigDecimal java.util.concurrent.atomic.AtomicInteger java.util.concurrent.atomic.AtomicLong特殊的抽象类特殊的抽象类如果
16、一个类没有任何抽象方法 那么,该类也可以声明为抽象类 结果,该类同样不能实例化 不能创建对象 这种情况比较少见例子 java.awt.event.MouseAdapterMouseAdapter该类的成员方法: public void mouseClicked(MouseEvent e) public void mousePressed(MouseEvent e) public void mouseReleased(MouseEvent e) public void mouseEntered(MouseEvent e) public void mouseExited(MouseEvent e)
17、 目的: 程序员不能直接创建该类的对象 必须从该类继承后再实例化 继承时可覆盖该类的 05 个方法 一般情况覆盖其中的 12 个方法抽象类抽象类包含抽象方法的类必须声明为 抽象类抽象类也可以没有任何抽象方法抽象类不能实例化抽象类主要作为父类被继承在设计类的层次结构时抽象类很有用抽象类抽象类Java 语言只支持单继承 一个类最多也只能有一个直接父类在定义类时经常会遇到下列情况 一个类 C 具有多方面的特征 既是一个 A,同时又是一个 B 可是 Java 又不支持多重继承 怎么办呢?解决方法:使用 接口,interface接口父类的选择接口接口的继承接口的例子抽象类抽象类一个抽象类中可能有抽象方法
18、 该抽象方法被延迟到 子类继承该抽象类时实现从抽象类继承父类的选择父类的选择在定义类时经常会遇到下列情况 一个类 C 具有多方面的特征 既是一个 A,同时又是一个 B 可是 Java 又不支持多重继承 只支持单继承 一个类最多也只能有一个直接父类 对 C 而言,A 和 B 只能选择一个作为父类 怎么办呢?例子例子文具管理系统 要定义 铅笔 Pencil 橡皮 Rubber 橡皮铅笔 RubberPencil 涂改液 CorrectionFluid铅笔铅笔打包 cn.peter.stationery Pencil.java成员: private String type; public Penci
19、l(); public Pencil(String type); public String getType(); public void setType(); public void write(String s);橡皮橡皮打包 cn.peter.stationery Rubber.java成员: public Rubber(); public void erase(String s);橡皮铅笔橡皮铅笔打包 cn.peter.stationery RubberPencil.java父类的选择 Pencil 或 Rubber 二者最多只能选一个橡皮铅笔橡皮铅笔一种不得已的办法: 选择 Penc
20、il 作为父类 同时实现类 Rubber 的方法主要和次要 主要:铅笔 Pencil 次要:橡皮 Rubber橡皮铅笔橡皮铅笔成员: public RubberPencil(); public RubberPencil(String type); public void write(String s); public void erase(String s); 其中方法 write(String) 覆盖了父类的方法同时从父类 Pencil 继承得到成员方法: public String getType(); public void setType();涂改液涂改液打包 cn.peter.sta
21、tionery CorrectionFluid.java成员: public CorrectionFluid(); public void erase(String s);类图类图改进方案改进方案由类图可以看出 类 Rubber 与类 CorrectionFluid 有共同的方法 erase(String)可以定义一个新的类 Eraser 唯一的成员是一个抽象方法: public void erase(String s); 同时类 Eraser 声明为抽象类 类 Rubber、CorrectionFluid 均从该抽象类继承类图类图 (抽象类版本抽象类版本)问题问题类 Rubber 与类 Co
22、rrectionFluid 继承了抽象类 Eraser 并实现了方法 erase(String) 但是 RubberPencil 与上述类仍然毫无关系 因此,如果该类没有实现 erase(String) 方法 也不会导致编译错误 这是因为该类只是从类 Pencil 继承 与抽象类 Eraser 无关问题问题但是 类 RubberPencil 的确实现了抽象类 Eraser的唯一抽象方法 erase(String) 但由于只能有一个直接父类 类 RubberPencil 选择了 Pencil 而对于类 Eraser 则只能忍痛割爱能否有两全其美的办法呢? 解决办法解决办法使用 接口,interf
23、ace接口接口使用关键字 interface 进行定义把抽象类 Eraser 改成接口 抽象类: public abstract class Eraser public abstract void erase(String s); 接口: public interface Eraser void erase(String s); 接口的特点接口的特点接口中的方法 默认都是 public abstract 接口中不允许存在非抽象方法 即有方法体的方法接口中的成员变量 默认都是 public static final修改原有程序修改原有程序Eraser 从抽象类变成接口后 原有程序要做哪些修改呢?
24、抽象类 因为抽象类 Eraser 不存在了 因此不能从该抽象类继承了类与接口类与接口类与类:继承,extends类与接口:实现,implements修改原抽象类 Eraser 的子类 Rubber 及 CorrectionFluid 把 extends Eraser 修改为 implements Eraser区别区别一个类从其父类继承 与实现一个接口 有什么区别呢?一个类实现一个接口的同时 还可以从另外一个类来继承 可以达到多重继承的效果 同时又不违背单继承的原则橡皮铅笔橡皮铅笔该类 RubberPencil 则修改为: public class RubberPencil extends Pe
25、ncil implements Eraser public RubberPencil(); public RubberPencil(String type); public void write(String s); public void erase(String s); 唯一的变化是增加了 implements Eraser原有问题的解决原有问题的解决抽象类版本的程序 如果类 RubberPencil 忘记实现了方法 erase(String) 则不会产生编译错误 当然,前提是 main() 中没有调用该方法接口版本的程序 由编译器强制保证类 RubberPencil 一定要实现方法 er
26、ase(String)类实现接口类实现接口一个类如果实现了某个接口 就必须实现该接口的所有方法 除非该类声明为抽象类 这些方法则被延迟到该类的子类来实现由于类 RubberPencil 没有声明为抽象类 因此必须实现接口 Eraser 的方法 否则产生编译错误,不能通过编译把类 RubberPencil 的 erase(String) 方法和 main() 方法注释,重新编译编译错误编译错误srccnpeterstationeryRubberPencil.java:4: cn.peter.stationery.RubberPencil 不是抽象的,并且未覆盖 cn.peter.statione
27、ry.Eraser 中的抽象方法 erase(java.lang.String) public class RubberPencil extends Pencil 1 错误类图类图 (接口版本接口版本)类的定义类的定义一个类 最多只能有一个直接父类 但同时可实现若干个接口 即实现接口的数量没有限制接口的继承接口的继承一个接口的主要作用是被类来实现 同时也能被另外一个接口来继承 即,接口可以继承 父接口、子接口关键字 类继承父类: extends 类实现接口: implements 接口继承接口:extends接口的继承接口的继承使用关键字 extends语法 public interface
28、A extends B / 方法列表 如果某个类实现了接口 A 那么该类必须实现接口 A 及其父接口 B 所有的方法 除非该类声明为抽象类接口的例子接口的例子字符序列 java.lang.CharSequence 代表一个字符序列,可从中进行读取 成员: int length(); char charAt(int index); CharSequence subSequence(int start, int end); public String toString();实现该接口的类: String StringBuffer StringBuilder接口的例子接口的例子可比较的 java.l
29、ang.Comparable 该接口只有一个方法 public int compareTo(T o);实现该接口的类很多,例如 String 字符串的比较 示例:TestStringComp.java类类 String该类的定义 public final class String implements java.io.Serializable, Comparable, CharSequence可以看出 该类实现了三个接口 由于没有明确使用 extends 因此其直接父类是 java.lang.Object字符串的比较字符串的比较示例:TestStringComp.java比较: 基本数据类型的
30、比较直接使用关系运算符 引用类型的比较则使用成员方法 该方法的返回类型:int 该方法的返回值 大于 0:当前对象大于参数对象 等于 0:当前对象等于参数对象 小于 0:当前对象小于参数对象类似于类似于与 C 语言的库方法 strcmp() 类似 该方法的方法原型如下 int strcmp(const char *, const char *); 返回类型也是 int 返回值代表的意义也很类似 其中关键词 const 与 Java 的 final 很类似 意义相同,修饰的对象也相同 此处均修饰方法的形式参数特殊的接口特殊的接口有一种特殊的接口 该接口中未定义任何成员 称为 标记接口,标志接口
31、例如 java.lang.Cloneable java.io.Serializable接口的特点接口的特点与抽象类一样 接口不能实例化,不能创建接口的对象 new 后面不能跟接口但是,可以声明接口的句柄 例如 Eraser eraser = null; eraser = new RubberPencil(); 接口的句柄可以引用任何实现了该接口的类的实例接口的用途接口的用途接口和抽象类一样,不能被实例化抽象类和接口 都可以作为方法的形参 这种情况很普遍,也很有用抽象类和接口 也都可以作为方法的返回类型出现接口接口以上讨论的接口 是指用关键字 interface 定义的实实在在的接口,也称为接口类型 它能够更加清晰地把系统的实现细节与接口分离广义的接口广义的接口指概念性的接口,即指系统对外提供的所有服务,类的所有能被外部使用者访问的方法构成了类的接口谢谢 谢谢