java复用类(组合、继承,代理,final关键字,初始化顺序) 知识点的总结
总结是源自java编程思想第四版
继承
在新的类中产生现有类的对象,这种方法称为 组合
在使用extends关键字+基类名 实现继承,子类会得到基类中所有的域和方法(对于基类的私有域有人解释是可以被继承但不能被子类访问)
当创建一个导出类(子类)对象时,该对象包含了一个基类的子对象,该子对象与用基类直接创建的对象是一样的,区别在于,后者来自于外部,而子对象被包装在导出类对象内部。在导出类的构造器中调用基类构造器来执行该子对象的初始化
java会自动在导出类的构造器中插入对基类构造器的调用
class Art {
Art(){
System.out.println("Art constructor");
}
}
class Drawing extends Art{
Drawing(){
System.out.println("Drawing constructor");
}
}
public class Cartoon extends Drawing{
public Cartoon(){
System.out.println("Cartoon Constructor");
}
public static void main(String args[]){
new Cartoon();
}
}
运行结果
Art constructor
Drawing constructor
Cartoon Constructor
- 默认(无参)构造器编译器可自动调用,若要调用基类的带参构造器,就必须用关键字super显示的编写调用基类构造器的语句并配以适当的参数列表
class Art {
Art(int i){
System.out.println("Art constructor");
}
}
class Drawing extends Art{
Drawing(int i){
super(i);//必须调用,且要放在最前面
System.out.println("Drawing constructor");
}
}
public class Cartoon extends Drawing{
public Cartoon(){
super(11);
System.out.println("Cartoon Constructor");
}
public static void main(String args[]){
new Cartoon();
}
}
结果
Art constructor
Drawing constructor
Cartoon Constructor
调用基类构造器必须是你在导出类构造器中要做的第一件事
@Override 注解可防止在应该覆盖基类方法时不小心写成重载的情况发生
protected 关键字修饰的域对于继承于此类的导出类以及同一包内的其他类来说是可以访问的。但最好将域保持为private 即保留更改底层实现的权利,然后通过protected方法来控制类的继承者的访问权限
向上转型
新类是现有类的一种类型,将导出类对象的引用转换为基类的引用的动作称为向上转型。
导出类是基类的一个超集,在向上转型的过程中唯一可能发生的事情就是丢失方法
代理
代理是组合与继承之间的中庸之道,将一个成员对象置于所要构造的类中(就像组合),但与此同时我们在新类中暴露了该成员对象的所有方法(就像继承)
继承与代理的应用区别
class SpaceShipControls{
void up(int velocity){}
void down(int velocity){}
void left(int velocity){}
void right(int velocity){}
}
public class SpaceShip extends SpaceShipControls{
private String name;
public SpaceShip(String name){
this.name = name;
}
public String toString(){
return name;
}
public static void main(String args[]){
SpaceShip protector = new SpaceShip("神州九号");
protector.up(100);
}
}
然而 SpaceShip并非SpaceShipControls类型(可理解成为不是is–a的关系),而是SpaceShipControls的方法都暴露在SpaceShip中,因此代理可解决这种问题:
public class SpaceShipDelegation {
private String name;
private SpaceShipControls controls = new SpaceShipControls();
public SpaceShipDelegation(String name){
this.name = name;
}
public void up(int velocity){
controls.up(velocity);
}
......
}
组合与继承的选择
书中推荐以新类是否需要向上转型作为选择判断的条件
组合技术通常用于想在新类中使用现有类的功能而非他的接口这种情形
final关键字
1、final数据
一个永不改变的编译时常量
一个在运行时被初始化的值,而你不希望他改变
在定义final常量(类对象)时必须对其进行赋值(可以在定义时,也可以在构造器中,必须初始化,不能再方法中赋值)
在final 修饰对象引用 使引用恒定不变一直指向该对象,而对象自身却是可以被修改的(如修改某个属性的值),这一限制同样适用于数组。
方法中的final参数表示你无法在方法中更改参数引用所指向的对象
2、final方法
把方法锁定,以防止任何继承类修改他的含义,确保在继承中使方法行为保持不变,并且不会被覆盖
类中所有的private方法都隐式的指定为final的
3、final类
当类整体定义为final是,就表明你不打算继承该类,而且也不允许别人这样做,对该类的设计永不需要做任何变动,或处于安全考虑,不要子类。
final类中所有的方法都隐式指定为是final的(因为无法继承和覆盖)
初始化及类的加载
每个类的编译代码都存在于他自己的独立文件中,该文件只在需要使用程序代码时才会加载,可以说类的代码在初次使用时才加载。
static对象和static代码段都会在加载时依程序中的顺序(即定义时的书写顺序)依次初始化,且只会被初始化一次。
继承关系的初始化顺序:
先初始化父类的静态代码—>初始化子类的静态代码–>
初始化父类的非静态代码—>初始化父类构造函数—>
初始化子类非静态代码—>初始化子类构造函数