我是靠谱客的博主 敏感纸飞机,最近开发中收集的这篇文章主要介绍复用类的两种机制1 组合2 继承,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Java中复用类主要使用的的方法有两种。
第一种是直接在一个新的类中产生以前类的对象,由于新的类是现有类的对象组成,因此这种方法成为组合,注意该方法只是使用了现有类中程序代码的功能,并没有复用其形式。
第二种是按照现有类的类型来创建新类,并且沿用现有类的形式,而且可以在现有类的形式中添加新的代码。这种方式称为继承。继承是面向对象程序设计的基石之一。

1 组合

组合语法是我们最经常用到的,就是将对象引用置于新类中,然后进行初始化,一般来说,我们可以在代码中的四个位置进行初识化:

  1. 在定义对象的地方就初始化,这种方式在构造器被调用之前就完成了初始化。
  2. 在类的构造器中。
  3. 什么时候需要使用这些对象什么时候初始化,这种方式称为惰性初始化。在生成对象不值得且不必每次都生成对象的情况下,这种方式可以减少额外的负担。
  4. 使用实例初始化。

以下是这四种方式的测试代码:

package com.thinkjava.reusing;

/**
 * Bath.java
 * @description 进行初始化的四个位置
 * @author Trigl
 * @date 2016年1月20日下午8:58:03
 */
class Soap {
    private String s;
    Soap() {
        System.out.println("Soap()");
        s = "Constructed";
    }
    public String toString() { return s; }
}
public class Bath {
    private String // 在定义对象的地方初始化
        s1 = "Happy",
        s2 = "Happy",
        s3,s4;
    private Soap castille;
    private int i;
    private float toy;
    public Bath() {
        System.out.println("Inside Bath()");
        s3 = "Joy";
        toy = 3.14f;
        castille = new Soap(); // 在构造器中初始化
    }
    { i = 47; } // 实例初始化
    public String toString() {
        if(s4 == null) //惰性初始化
            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
*/

在上面的例子中,注意一个方法的用法:toString()。每一个非基本类型的对象都有一个toString()方法,而且当编译器需要一个String而你只有一个对象时,该方法就会被调用。

2 继承

继承语法首先是通过在类主题的左边花括号之前,书写关键字extends加上基类名称来声明“新类与旧类相似”。这样,就会自动得到基类的所有域和方法。例如:

package com.thinkjava.reusing;

/**
 * Detergent.java
 * @description 继承语法
 * @author Trigl
 * @date 2016年1月20日下午9:12:45
 */
class Cleanser {
    private String s = "Cleanser";
    public void append(String a) { s += a; }
    public void dilute() { append(" dilute()"); }
    public void apply() { append(" apply()"); }
    public void scrub() { append(" scrub()"); }
    public String toString() { return s; }
    public static void main(String[] args) {
        Cleanser x = new Cleanser();
        x.dilute(); x.apply(); x.scrub();
        System.out.println(x);
    }
}
public class Detergent extends Cleanser {
    //改变一个方法
    public void scrub() {
        append(" Detergent.scrub()");
        super.scrub(); // 调用基类版本
    }
    //添加一个方法
    public void foam() { append(" foam()"); }
    //测试新类
    public static void main(String[] args) {
        Detergent x = new Detergent();
        x.dilute();
        x.apply();
        x.scrub();
        x.foam();
        System.out.println(x);
        System.out.println("测试基类:");
        Cleanser.main(args);
    }
}
/* Output:
Cleanser dilute() apply() Detergent.scrub() scrub() foam()
测试基类:
Cleanser dilute() apply() scrub()
*/

这个程序示范了Java的许多特性。首先,Cleanser和Detergent都含有main()方法。我们可以为每一个类创建一个main()方法,这样方便进行测试。并且即使一个程序中含有多个类,也只有命令行所调用的那个类的main()方法会被调用。例如在此例中,如果命令行打出的是:Java Detergent,那么Detergent.main()将会被调用。如果命令行打出的是:Java Cleanser,那么Cleanser.main()将会被调用,即使Cleanser不是一个public类。在本例中,可以看到Detergent.main()明确调用了Cleanser.main(),并将从命令行获取的参数传递给了它。当然,也可以传递任意的String数组。
其次,注意Cleanser中所以的方法都必须是public的,这一点非常重要。如果没有加任何的访问权限修饰词,那么成员默认的访问权限是包访问权限,仅允许包内的成员访问。但是在这种情况下,如果其他包中的某个类想要从Cleanser中继承,则只能访问public成员。所以,为了实现继承,一般的规则是将所以的数据成员都指定为private,将所有的方法指定为public。
第三,在Cleanser的接口中有这样一组方法: append( ), dilute( ), apply( ), scrub( )和 toString( ),由于Detergent是由关键字extends从Cleanser中导出的,所以它可以在其接口中自动获得这些方法,尽管并没有在Detergent看到定义它们。因此,可以将继承看作是对类的复用。
第四,正如scrub()方法所见,可以对基类中定义的方法进行修改。在此例中,我们还想要在新版本中调用从基类中继承而来的原始方法,但是在scrub()中,并不能直接调用scrub(),因为这样会产生递归,此时,super关键字就起作用了,这个关键字表示超类的意思,因此,使用super.scrub()来调用基类版本的scrub()方法。
最后,在继承的过程中并不一定非得使用基类中的方法,也可以在新类中添加新方法,如本例的foam()方法。

2.1 创建导出类对象时构造器的调用顺序

当创建导出类的对象时,由于要继承基类接口中的域和方法,所以实际上该对象还包含了一个基类的子对象。这个子对象与你用基类直接创造的对象是一样的,二者的区别在于,后者来自于外部,而基类的子对象则是被包装在导出类对象内部。
Java会自动在导出类的构造器中插入对基类构造器的调用,当然你自己也可以手动进行调用。下面的例子展示了上诉机制在三层继承关系上是如何工作的:

package com.thinkjava.reusing;

/**
 * Cartoon.java
 * @description 在继承时调用构造器
 * @author Trigl
 * @date 2016年1月20日下午10:12:43
 */
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) {
        Cartoon x = new Cartoon();
    }
}
/* Output
Art constructor
Drawing constructor
Cartoon constructor
*/

我们会发现,构造过程是从基类“向外”逐层扩散,这样保证基类在导出类构造器可以访问它之前就已经完成了初始化。

最后

以上就是敏感纸飞机为你收集整理的复用类的两种机制1 组合2 继承的全部内容,希望文章能够帮你解决复用类的两种机制1 组合2 继承所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(52)

评论列表共有 0 条评论

立即
投稿
返回
顶部