java|重学java之继承与组合

重学java,是针对我自己而言的,在学java的初期并没有很认真的阅读相关书籍,导致只是单纯地写代码,对于java的很多基础知识以及一些该掌握的没有很好的掌握,所以抽暑假的时间好好巩固一下。

对于继承,大家都很熟悉,简单介绍一下,是面对对象三大特征之一,也是实现软件复用的重要手段之一。java的继承具有单继承的特点,每个子类只能有一个直接父类。

java的继承通过extends实现,实现继承的类称为子类,被继承的类称为父类(超类、基类)。子类和父类的关系,是一般和特殊的关系,例如家具和书桌的关系,书桌继承了家具,书桌是家具的子类,则书桌是特殊的家具。

java中的继承格式:
修饰符 class subClass extends baseClass{
//类定义部分
【java|重学java之继承与组合】}

子类继承了父类,同时也是扩展了父类,在子类中包含与父类同名方法的现象交方法重写(方法覆盖Override),方法重写遵循的规则:
两同两小一大:
“两同”即方法名相同、形参列表相同;
“两小”指子类方法返回值类型应比父类方法返回值类型更小或者相等,子类方法声明抛出的异常应比父类方法声明抛出的异常类更小或相等(这个在之前的编程中没有遇到过也没有发现过);
“一大”指的是子类方法的访问权限应比父类方法更大或相等。当父类方法被覆盖之后用super或者父类类名可以调用父类中的方法。


另外我们要明白的是,在我们创建一个子类对象时,程序总是从该类所继承树最顶层类的构造器开始执行,然后依次向下执行,最后执行本类的构造器。最顶层类(java.lang.Object)总是会在创建子类对象时被隐式调用。

说了这么多继承,我们要明白继承在实现类重用时,会带来一个非常大的坏处:严重破坏了父类的封装性。当然,我们可以通过设计父类来保证其良好的封装性,这时,我们应遵循以下规则:
1.尽量隐藏父类的内部数据。尽量把父类的虽有属性设置成private访问类型。
2.不让子类可以随意访问、修改父类方法。父类中那些仅为辅助其他的工具方法,应用private修饰;若父类中的方法需要被外部类调用,则必须用public修饰,但不希望子类重写改方法,则可以用final修饰;如果希望父类的某个方法被子类重写,到不希望被其他类访问,则可以用protected修饰。
3.不在父类构造器中调用被子类重写的方法。

另外,如果需要复用一个类,还可以把该类当成另一个类的组合成分,从而允许新类直接复用该类的public方法。组合是把旧类对象作为新类的属性嵌入,从而实现新类的功能,这样,用户看到的是新类的方法,而不能看到嵌入对象的方法。在新类中使用private修饰嵌入的旧类对象。

下面用代码实现看看继承和组合二者的用法以及区别:

/** * 用继承实现代码复用 */ class Animal { private void beat() { System.out.println("心脏跳动..."); } public void breath() { beat(); System.out.println("吸一口气,吐一口气,呼吸中..."); } }// 继承Animal,直接复用父类的breath方法 class Bird extends Animal { public void fly() { System.out.println("我在天空自在的飞翔..."); } }// 继承Animal,直接复用父类的breath方法 class Wolf extends Animal { public void run() { System.out.println("我在陆地上的快速奔跑..."); } }public class TestInherit { public static void main(String[] args) { Bird b = new Bird(); b.breath(); b.fly(); Wolf w = new Wolf(); w.breath(); w.run(); } }



/** * 用组合实现代码复用 */ class Animal { private void beat() { System.out.println("心脏跳动..."); } public void breath() { beat(); System.out.println("吸一口气,吐一口气,呼吸中..."); } }class Bird { // 将原来的父类嵌入原来的子类,作为子类的一个组合成分 private Animal a; public Bird(Animal a) { this.a = a; } // 重新定义一个自己的breath方法 public void breath() { // 直接复用Animal提供的breath方法来实现Bird的breath方法。 a.breath(); } public void fly() { System.out.println("我在天空自在的飞翔..."); } }class Wolf { // 将原来的父类嵌入原来的子类,作为子类的一个组合成分 private Animal a; public Wolf(Animal a) { this.a = a; } // 重新定义一个自己的breath方法 public void breath() { // 直接复用Animal提供的breath方法来实现Bird的breath方法。 a.breath(); } public void run() { System.out.println("我在陆地上的快速奔跑..."); } }public class TestComposite { public static void main(String[] args) { // 此时需要显式创建被嵌入的对象 Animal a1 = new Animal(); Bird b = new Bird(a1); b.breath(); b.fly(); // 此时需要显式创建被嵌入的对象 Animal a2 = new Animal(); Wolf w = new Wolf(a2); w.breath(); w.run(); } }


在我们实际运用中明显用继承多与组合,但是,对于我们来说,在使用之前,考虑使用哪个方法是必须要做的。我们要知道,继承是对已有的类做一番改造,从此获得一个特殊的版本,也就是将一个较为抽象的类改造成能用于某些特定需求的类。例如Wolf和Animal的关系。

而组合则是表示整体与部分的关系,例如Person类需要复用Arm类的方法,则应使用组合来实现复用。

总之,继承是要表达一种“是(is-a)”关系,组合是要表达“有(has-a)”的关系。



    推荐阅读