继承、抽象类、接口和枚举
类的继承
基本知识
- 程序代码复用:通过类的继承方式,可以不用编写相同的代码就能开发出新的类。
- 被继承的类称为父类或超类(superclass),由继承而得到的类称为子类(subclass)。
- Java语言中不支持多重继承,所以一个类只能有一个直接父类。
- 父类是所有子类的公共成员的集合,而每个子类则是父类的特殊化,是对公共成员变量和方法在功能、内涵方面的扩展和延伸。
- 子类继承父类的成员变量和方法,同时还可修改、重写和添加父类的成员变量和方法。
- Java语言中有一个名为
java.lang.Object
的特殊类,所有的类都是直接或间接地继承该类而得到的。
子类的创建
- Java语言中类的继承是通过extends关键字来实现的,其格式如下:
class SubClass extends SuperClass{ …… }
- 子类可以从父类那里继承所有非private的成员作为自己的成员。
- 若无extends关键字,则该类默认为java.lang.Object类的子类。
- 子类的每个对象也是父类的对象(“即是”性质),但父类对象不一定是子类的对象。
见教材P116例8.1
- 例8.1程序说明:
- 使用继承过来的成员时,可利用
“子类对象.父类成员名”
格式进行。 - 构造方法是不能被继承的,但子类可以调用父类的构造方法。
- 在执行子类的构造方法之前,将先调用父类中没有参数的构造方法,其目的是为了帮助继承自父类的成员做初始化的操作。
- 调用父类中特定的构造方法
- 如果父类中有多个构造方法时,如何才能调用父类中某个特定的构造方法呢?
- 在子类的构造方法中通过
super()
来调用父类特定的构造方法。 例8.2 具体说明见教材。
Person类(一个公司中有普通员工(Employees)及管理人员(Magagers)两类人员)
- 说明:子类不能直接访问从父类中继承的私有属性及方法,但可使用公有(及保护)方法进行访问
- Java语言中类的继承是通过extends关键字来实现的,其格式如下:
在子类中访问父类的成员
- 使用super不但可以访问父类的构造方法,还可以访问父类的成员变量和成员方法,但super不能访问在子类中添加的成员。访问父类成员的格式如下:
super.变量名; super.方法名;
- 由于在子类中不能继承父类中的private成员,所以无法在子类(类外)里访问父类中的这种成员。
- 用protected修饰的成员,可以被该类自身、同一包中的其他类、其他包中的类三种类访问。
见教材P120 例8.3
- 子类中声明了与父类中相同的成员变量名,则从父类继承的变量将被隐藏;
- 子类拥有了两个相同名字的变量,一个继承自父类,另一个由自己声明;
- !!!****当子类执行继承自父类的方法时处理的是继承自父类的变量,而当子类执行它自己声明的方法时,所操作的就是它自己声明的变量;
- 使用
super.属性
访问被隐藏的父类属性; - 调用从父类继承的方法,则操作的是从父类继承的属性。
- 使用super不但可以访问父类的构造方法,还可以访问父类的成员变量和成员方法,但super不能访问在子类中添加的成员。访问父类成员的格式如下:
覆盖
- 覆盖与重载相似,均是Java“多态性”的体现。
- 覆盖:指在子类中,定义名称、参数个数与类型均与父类完全相同的方法,用以重写父类里同名方法的功能。
- 重载:是指在同一个类定义名称相同,参数列表不同的方法。
- 在子类中覆盖父类的方法时,可扩大父类中的方法权限,但不能缩小其权限。
- 不能覆盖父类中声明的final或static的方法。
见教材P121 例8.4
- 用父类的变量访问子类的成员:只限于“覆盖”的情况发生。
- 格式:
父类 对象 = new 子类(); 对象.子类方法;
见教材 P123例8.5
- 方法覆盖的应用场合:
- 子类中实现与父类相同的功能,但采用不同的算法或公式;
- 在名字相同的方法中,要做比父类更多的事情;
- 在子类中需要取消从父类继承的方法。
不可被继承的成员与最终类
- 如果用
final
来修饰类的成员,则该成员为最终成员。 - 若父类的成员不希望被子类的成员所覆盖,则可将它们声明为
final
。 - 最终类(final类):用
final
修饰的类。该类不能有子类。 - 成员变量若同时被static和final修饰,则表示常量,若没有初始化则按默认值初始化。
- 仅用final不用static修饰则必须且只能赋值一次,不能默认。方式:定义时赋值;构造方法赋值。
- 如果用
抽象类
基本概念
- 抽象类有点类似“模板”的作用,其目的是根据它的格式来创建和修改新的类。
- 但是并不能直接由抽象类创建对象,只能通过抽象类派生出新的子类,再由其子类来创建对象。
- 也就是说,抽象类就是不能用new运算符来创建实例对象的类,它可以作为父类被它的所有子类所共享。
抽象类与抽象方法
- 抽象类的定义格式:
//抽象方法,在抽象方法里,不能定义方法体。只需声明不需实现 abstract class 类名{ 声明成员变量; 返回值的数据类型 方法名(参数表){ …… } abstract 返回值的数据类型 方法名(参数表); }
- 抽象类的子类必须实现父类中的所有抽象方法,或将自己也声明成抽象类。
- 由于抽象类是需要被继承的,所以abstract类不能用final来修饰。也就说,一个类不能既是最终类,又是抽象类,即关键字abstract与final不能合用。
- 抽象类中不一定包含抽象方法,但包含抽象方法的类一定要声明为抽象类。
- 默认实现:JDK1.8之后,在接口里面可以定义default方法,default方法里面是可以具备方法体的,当子类实现该接口之后,不需要重写该方法即可以调用该方法。 (in another word:默认实现:被继承,并且没有被重写,可以被调用了,就称它默认实现了)
见教材P131例8.10
- 抽象类的定义格式:
接口
- 接口的定义
- 接口与抽象类非常相似,区别:
- 接口的数据成员都是静态常量;
- 接口中除了抽象方法外,还可以定义默认方法和静态方法,但不能有普通方法。
- 接口的定义语法:
//public若没有则为默认访问控制符 [public] interface 接口名称 [extends 父接口名列表]{ //默认为public static final,成员变量必须赋初值且不能修改 [public][static][final] 数据类型 成员变量名=常量; //抽象方法,默认为public abstract [public][abstract] 返回值的数据类型 方法名(参数表); [public]static返回值类型 方法名(参数表){方法体} [public]default返回值类型 方法名(参数表){方法体} }
- 接口与抽象类非常相似,区别:
- 接口的实现与引用
- 接口的实现:利用接口的特性来建造类的过程。类似与继承,但不是extends,而是使用implements。
- 接口实现的语法格式:
class 类名称 implements 接口名表 { …… }
- 一个类实现一个接口应注意的问题:
- 如果实现某接口的类不是abstract的抽象类,则在类的定义部分必须实现指定接口的所有抽象方法。
- 一个类在实现某接口的抽象方法时,必须使用完全相同的方法头。
- 接口中抽象方法被指定为public,所以类在实现方法时,必须显示地使用public修饰符。
- 接口可以作为一种引用类型来使用,可以声明接口类型的变量或数组,并用它来访问实现该接口的子类的对象。
- 非抽象类中不能有抽象方法
- 其他类要和实现了这些接口的类对接的 就靠这些公开的类的方法、属性来对接 类之间一定会有相互作用,一起干活,才是一个“系统” 所以相互作用就靠这些接口
见教材P134例8.11
- 接口实现的语法格式:
- 接口的继承(扩展)
- 定义一个接口时可通过extends关键字声明该新接口是某个已存在的父接口的派生接口,它将继承父接口的常量、抽象方法和默认方法,但不能继承父接口的静态方法,也不能被实现类继承;
- 接口继承与类继承的区别:一个接口可以有一个以上的父接口,它们之间用逗号隔开,形成父接口列表。
- 如果接口中定义了与父接口同名的常量或相同的方法,则父接口中的常量被隐藏,方法被覆盖。
见教材P135例8.12
- 利用接口实现类的多重继承
- 多重继承:一个子类可以有一个以上的直接父类,该子类可以继承它所有直接父类的成员。
- Java语言虽不支持多重继承,但可以利用接口间接地解决多继承问题。
- 一个类只能有一个直接父类,但是它可以同时实现若干个接口。一个类实现多个接口时,在implements子句中用逗号分隔各个接口名。
- Java中接口的主要作用是可以帮助实现多重继承。
见教材P137例8.13
- 接口中静态方法和默认方法
- 接口中的静态方法与普通类中的静态方法定义相同。
- 接口中的默认方法用default修饰符来定义,默认方法可以被子接口或被实现该接口的类所继承,但子接口中若定义名称相同的默认方法,则父接口中的默认方法被隐藏。
- 如果一个类只extend父类,不重写父类的方法,会怎么样:会得到父类方法
- interface里的default方法也是一样的东西,如果implement了这个interface,但不override的话,就会获得interface里的那个方法实现
- interface有三种方法?
- 一种static类方法(使用
接口名.静态方法
即可调用) - 一种default默认方法(默认方法其实是子类可实现可不实现,子类也可以选择把它override掉)
- 一种需要子类实现的方法(没有static没有default没有方法体,等着子类实现)
- 一种static类方法(使用
见教材P138例8.14
- 解决接口多重继承中名字冲突问题
- 如果子接口中定义了与父接口同名的常量或者相同的方法,则父接口中的常量被隐藏,方法被覆盖。但在接口的多重继承中可能存在常量名或方法名重复的问题,即名字冲突问题。
- 对于常量,若名称不冲突,子接口可以继承多个父接口中的常量,如果多个父接口中有同名的常量,则子接口不能继承,但子接口中可以定义一个同名的常量。
- 对于多个父接口中存在同名的方法时,此时必须通过特殊的方式加以解决.
见教材P139-140
- 补充:方法覆盖与多态
- 从相同的基类派生出来的多个类型可被当作同一种类型对待,可对这些不同的类型进行同样的处理,由于多态性,这些不同派生类对象响应同一方法时的行为是有所差别的。
- 技术基础
- 向上塑型技术:一个父类的引用变量可以指向不同的子类对象;
- 动态绑定技术:运行时根据父类引用变量所指对象的实际类型执行相应的子类方法,从而实现多态性。
- 绑定:指将一个方法调用同一个方法主体连接到一起。
- 根据绑定时期的不同,可分为:
- 早期绑定:程序运行之前执行绑定
- 晚期绑定:也叫作“动态绑定”或“运行期绑定”,基于对象的类别,在程序运行时执行绑定
例:以绘图为例
- 塑型(type-casting):又称为类型转换
- 隐式(自动)的类型转换
- 显式(强制)的类型转换
- 塑型的对象包括:
- 基本数据类型:将值从一种形式转换成另一种形式;
- 引用变量:将对象暂时当成更一般的对象来对待,并不改变其类型;
- 只能被塑型为:
- 任何一个父类类型;对象所属的类实现的一个接口
例:塑型的概念的一个例子
- 隐式(自动)的类型转换
- 基本数据类型:相容类型之间存储容量低的自动向存储容量高的类型转换
- 引用变量
- 被塑型成更一般的类:
//将Manager类型的对象直接赋给Employee类的引用变量,系统会自动将Manage对象塑型为Employee类 Employee emp; emp = new Manager();
- 被塑型为对象所属类实现的接口类型:
Car jetta = new Car(); Insurable item = jetta;
- 被塑型成更一般的类:
- 显式(强制)的类型转换
- 基本数据类型
(int)871.34354; // 结果为 871 (char)65; // 结果为‘A’ (long)453; // 结果为453L
- 引用变量:还原为本来的类型
Employee emp; Manager man; emp = new Manager(); man = (Manager)emp; //将emp强制塑型为本来的类型
- 基本数据类型
- 当一个类对象被塑型为其父类后,它提供的方法会减少。
案例图
- 当Manager对象被塑型为Employee之后,它只能接收getName()及getEmployeeNumber()方法,不能接收getSalary()方法
- 将其塑型为本来的类型后,又能接收getSalary()方法了
- 接口的实现:利用接口的特性来建造类的过程。类似与继承,但不是extends,而是使用implements。
枚举
- 枚举类型的定义
- 如何写枚举
[修饰符] enum 枚举类型名{ 枚举成员 方法 }
- 修饰符可以是public、private、internal。
- 枚举类型名:有两层含义,一是作为枚举名使用;二是表示枚举成员的数据类型,因此,枚举成员也称为枚举实例或枚举对象。
- 如何写枚举
- 不包含方法的枚举类
- 每个枚举类型的成员都可以看作是Enum类的实例,
- 这些枚举成员默认被public final static修饰。
- 当访问枚举类型的成员时,直接使用枚举名调用枚举成员即可,即“
枚举名.枚举成员
”。 - 也可使用Enum类定义的valueOf()方法通过“
枚举名.valueOf()
”的形式进行调用来获取枚举的对象。 见教材例8.16
- 包含属性和方法的枚举类
- 因为枚举也是一种类,所以它具有与其他类几乎相同的特性,因此可以定义枚举的属性、构造方法以及方法。
- 但是,枚举的构造方法只是在构造枚举实例值时被调用。
- 每一个枚举实例值都是枚举的一个对象,因此创建每个枚举实例时都需要调用该构造方法。
见教材例8.17
Java语言中的常用包(见补充材料)
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达,可以邮件至 963614756@qq.com。