继承
使用super调用应该放在第一行调用
在进行强制类型转换时可能出现异常,因此进行类型强制转换之前应先通过instanceof运算符来判断是否可以成功转换。(在使用instanceof运算符时需要注意instanceof运算符前面操作数的编译时类型要么与后面的类型相同,要么与后面的类具有父子继承关系,否则会编译错误。)
把子类对象赋给父类引用变量时,被称为向上转型(upcasting)。
继承会破坏封装。相比之下,组合也是实现类复用的重要方式,而采用组合方式来实现类复用则能提供更好的封装性。
为保证父类有良好的封装性:
- 尽量隐藏父类内部数据。尽量将所有成员变量设置成private访问类型。
- 不要让子类可以随意访问、修改父类的方法。
- 尽量不要在父类构造器中调用将要被子类重写的方法。
何时需要从父类派生新的子类呢?不仅需要保证子类是一种特殊的父类,而且需要具备以下两个条件之一:
- 子类需要额外增加属性,而不仅仅是属性值的改变。例如从person类派生出student子类,person类里没有提供grade(年级)属性,而student类需要grade属性来保存student对象就读的年级,这种父类到子类的派生,就符合Java继承的前提。
- 子类需要增加自己独有的行为方式(包括增加新的方法或者重写父类方法)。例如从person类派生出teacher类,期中teacher类需要增加一个teaching()方法,用于描述teacher对象独有的行为方式:教学。
如果只是出于对类复用的目的,不一定需要继承,完全可以使用组合来实现。
- 继承是将一个较为抽象的类改造成能适用于某些特定需求的类,如果两个类之间有明确的整体、部分的关系,例如person类需要复用Arm类的方法(Person对象由Arm对象组合而成),此时就应该采用组合关系来实现复用,把Arm类作为Person类的组合成员变量,借助于Arm的方法来实现Person的方法。
- 继承(父类内部细节对子类可见):Is – a关系,表示一种组成,如狗是一个动物
- 组合(通过对现有的对象进行拼装(组合)产生新的、更复杂的功能且各自内部的细节不可见): Has – a关系,表示整体与部分,如狗有一条尾巴。
- 接口
接口的定义
【修饰符】 interface 接口名 extends 父接口1,父接口2……
{
/*零到多个常量定义
零到多个抽象方法定义
零到多个内部类、接口、枚举定义
零到多个默认方法或类方法定义*/
}//一个接口可以有多个父接口,但接口只能继承接口,不能继承类
接口里不能包含构造器和初始化块定义。接口里可以包含成员变量(只能是静态常量)、方法(只能是抽象实例方法、类方法或默认方法)、内部类(包括内部接口、枚举)定义。
接口里定义的方法只能是抽象方法、类方法或默认方法。
接口的主要用途是被类实现:
定义变量,也可进行强制类型转换;
调用接口中定义的常量;
被其他类实现
一个类可以实现一个或多个接口,继承使用extends关键字,实现则使用implements关键字:
【修饰符】class 类名 extends 父类 implements 接口1,接口2......
{
//类体部分
}
一个类可以继承一个父类,并实现多个接口,implements部分必须放在extens部分之后。
抽象类(待补充)
有抽象方法的类只能被定义成抽象类,抽象类里可以没有抽象方法。抽象类必须使用abstract修饰符来修饰,抽象方法不能有方法体。
抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例。抽象类的构造器不能用于创建实例,主要是被其他子类调用。
final修饰符(待补充)
final可用于修饰类、变量和方法,类似于C#中的sealed(c const?)
final修饰的变量不可被改变。一旦获取初始值,该final变量值则不能重新赋值(未指定默认值可以在后续代码中对final修饰变量赋值,但只可一次,不可重复,且final修饰的成员变量必须由程序员显示地指定初始值,系统不会对final成员进行隐式初始化)。
final修饰的方法不可以被重写,如果出于某些原因不希望父类的某个方法被重写,则可以用final修饰该方法。
fina修饰的方法仅仅是不能被重写,并不是不能被重载(?);
当final修饰类是,不可以有子类;为了保证某个类不可被继承,则可以使用final修饰这个类。