关于类的继承和组合的几点问题

1. 引用和对象的初始化方式:
① 在定义成员对象的地方直接进行初始化。这种初始化方式发生在构造器被调用之前。
② 在类的构造器中进行初始化。
③ 就在正要使用这些对象之前,这种方式被称为 惰性初始化(Delayed initialization)。在一些时候,类的成员对象不必每次都被使用,在这些对象被使用之前再进行初始化可以减少不必要的内存开销。
④ 类中用大括号{}表示的初始化块中进行初始化。

eg.

class Soap{
    private String s;
    public Soap() {
        System.out.println("Soap()");
        s="Constructed";
    }
    public String toString() {
        return s;
    };
}
 
public class Bath {
    private String        //Initializing at point of definition:
        s1="Happy",
        s2="Happy",
        s3,s4;
    private Soap castille;
    private int i;
    private float toy;
    public Bath(){     //Initializing in constructors:
        System.out.println("Inside Bath()");
        s3="joy";
        toy=3.14f;
        castille=new Soap();
    }
    //Instance initialization:
    {
        i=47;
    }
    public String toString(){
        if(s4==null){   //Delayed initialization
            s4="joy";
        }
        return
            "s1 = "+s1+"\n"+
            "s2 = "+s2+"\n"+
            "s3 = "+s3+"\n"+
            "s4 = "+s4+"\n"+
            "i = "+i+"\n"+
            "toy = "+toy+"\n"+
            "castille = "+castille;
    }
     
    public static void main(String[] args) {
        Bath b=new Bath();
        System.out.println(b);
    }
}

Output:
Inside Bath()
Soap()
s1 = Happy
s2 = Happy
s3 = joy
s4 = joy
i = 47
toy = 3.14
castille = Constructed

2. 无论类的访问权限是public还是包访问权限,其main()方法都可以被调用。因此如果有需要,可以在每个类中都设置一个main()方法,用于对类进行单元测试,而且在单元测试之后无需删除main(),可以将其留待下次测试。

3. 在子类中调用父类的构造器是来对父类进行初始化是很有必要的。如果子类的构造器不显式调用super(),系统在执行子类构造器代码之前会隐式调用父类无参构造器。但是注意, 若父类定义了带参数的构造器,同时没有定义无参构造器,那么子类必须使用super()显式调用父类的带参数构造器并传递相应的参数,且super()必须是子类构造器中的第一条可执行语句,否则将不能完成父类的初始化,编译器会报告一个错误。特别地,由于所有类均直接或间接继承Object类,故最终都会调用Object类的无参构造器。
eg. 下面用三个类相互继承的例子说明类的初始化和对象初始化的顺序
class Art{
    static{
        System.out.println("class Art initialized");
    }
    {
        System.out.println("Entering initialization block of Art");
    }
    public Art() {
        System.out.println("Constructor of Art invoked");
    }
}
 
class Drawing extends Art{
    static{
        System.out.println("class Drawing initialized");
    }
    {
        System.out.println("Entering initialization block of Drawing");
    }
    public Drawing(){
        System.out.println("Constructor of Drawing invoked");
    }
}
 
public class Cartoon extends Drawing{
    static{
        System.out.println("class Cartoon initialized");
    }
    {
        System.out.println("Entering initialization block of Cartoon");
    }
    public Cartoon(){
        System.out.println("Constructor of Cartoon invoked");
    }
    public static void main(String[] args) {
        new Cartoon();
    }
}
Output:
class Art initialized
class Drawing initialized
class Cartoon initialized
Entering initialization block of Art
Constructor of Art invoked
Entering initialization block of Drawing
Constructor of Drawing invoked
Entering initialization block of Cartoon
Constructor of Cartoon invoked

4. 除了继承和组合,还有一种比较有意思的关系,称为 代理。代理本质上属于组合,但又具有继承的一些特性。具体实现方式是:将一个成员对象置于所要构造的类中,但与此同时,在新类中暴露该成员对象的所有(或一部分)方法。
eg. 太空船需要一个控制模块:
public class SpaceShipControls {
    void up(int velocity){}
    void down(int velocity){}
    void left(int velocity){}
    void right(int velocity){}
    void forward(int velocity){}
    void back(int velocity){}
    void turboBoost(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("NSEA Protector");
        protector.forward(100);
    }
}
然而,SpaceShip并非真正的SpaceShipControls类型(可以说“SpaceShip  has a  SpaceShipControls”,但说“SpaceShip  is a  SpaceShipControls”则不妥),即便你可以“告诉”SpaceShip向前运动(forward())。与此同时,SpaceShipControls的所有方法在SpaceShip中都暴露了出来。代理就可以解决这个问题:
public class SpaceShipDelegation {
    private String name;
    private SpaceShipControls controls=new SpaceShipControls();
    public SpaceShipDelegation(String name) {
        this.name=name;
    }
    //Delegated methods:
    public void back(int velocity){
        controls.back(velocity);
    }
    public void down(int velocity){
        controls.down(velocity);
    }
    public void forward(int velocity){
        controls.forward(velocity);
    }
    public void left(int velocity){
        controls.left(velocity);
    }
    public void right(int velocity){
        controls.right(velocity);
    }
    public void turboBoost(){
        controls.turboBoost();
    }
    public void up(int velocity){
        controls.up(velocity);
    }
     
    public static void main(String args[]){
        SpaceShipDelegation protector=new SpaceShipDelegation("NSEA Protector");
        protector.forward(100);
    }
}
上面的代理定义了一些与底层的controls对象名称相同的方法,因此用户所得到的方法集合就与使用继承一样了。但是,这种方法可以拥有更强的控制力,因为可以选择只提供成员对象方法集合中的某个子集。
Java语言不支持代理,但是很多IDE可以直接生成代理方法,包括Eclipse、IDEA等。

5. 正确进行清理。
Java没有C++析构函数的概念,且不能判断GC什么时候工作,不能保证对象一定会被销毁。因此如果需要某各类清理一些东西,必须显式地编写一个特殊方法来做这些事,且在恰当的时机明确调用这个方法。将这个工作放在 finally子句中做是最合适的,因为无论在任何情况下,finally字句总会得到执行,确保资源总是能得到释放。
如果牵扯到类的继承,还要确保在清理方法中调用子类的清理方法。形式与C++编译器在其析构函数上所施加的形式相同:首先,执行类的所有特定的清理动作,其顺序同生成顺序相反;然后,调用父类的清理方法。
最好的办法是,除了内存以外,不依靠GC去做任何事,
不要使用finalize()进行清理,
不要使用finalize()进行清理,
不要使用finalize()进行清理,
重要的事情说三遍!

6.  final关键字的一些说明
① final修饰的 基本数据类型,表示一个永不改变的 编译时常量,必须定义时进行赋值。编译器碰到时会直接用对应的常量进行替换。
② final修饰的 对象引用,表示该引用恒定不变,不能再指向其他对象,但指向的对象本身是可以修改的。
③  “空白final”,指的是声明为final又没有赋初值的域。编译器会确保空白final在使用之前必须初始化,否则会报错。
④ final修饰的 方法形参,表示无法在方法中更改参数引用所指向的对象。 这一特性主要用来向匿名内部类传递数据(匿名内部类访问类外部方法的形参、定义的变量时,这些形参、变量必须设为final。编译器检测到final的前缀时,会在内部类中复制一份对应形参、变量的拷贝)。
⑤ final修饰的 方法,表示将方法锁定,在任何继承类中都不能被重写。
⑥ 类中所有的private方法都是隐式指定为final中。由于无法取用private方法,所以也就无法重写它。但是注意,在子类中依然可以定义和父类private方法相同的方法,但是和父类的同名方法并没有什么卵关系。
⑦ final修饰的 ,表示该类不能被继承,该类所有的方法都会隐式指定为final的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值