详解 继承(上)—— 工具的抽象与分层

本篇博文讲解的知识点比较实用,但是,相关知识点太多,所以本人将内容分为上下两册,
那么,本人就不多废话,直接进入主题进行讲解了!

说到“继承”,大家可能都会想到我们日常中对于这个词的定义:将先人的 物品 或 意志 传承给后人,而后人也可以“择优继承”,并在先人的基础上产生 新的物品 或 新的意志。

上面这一段话,并不是为了瞎扯才写出来的,上述的思想就是本人对于JAVA中的“继承”的一种通俗的理解:
将父类的 成员 或 方法 传承给 子类 , 而子类也可以选择自己声明一个同名的成员,或者 一个同名、同返回值、同参数的方法,并在父类的基础上 定义 新的成员 或 新的方法 。

通过这样描述,可能大家会对于继承的优点就有了基本的认识。
那么,现在,本人来总结一下 继承的优点

继承的优点

  • 提高了代码的复用性
  • 提高了代码的维护性
  • 让类与类之间产生了关系,是多态的前提

在这里,本人还要提出的一点是:

Object类 是 所有类的基类(即 父类)

这样一来,想必同学们对于这个知识点就有了大致的认识了,那么,现在本人来编写一段代码来实现一下上述的思想:
首先,先在com.mec.about_inheritance.classes包下,来编写一个 Parent.java 文件:

1package com.mec.about_inheritance.classes; 2 3public class Parent { 4 public int parentPublicMember; 5 private int parentPrivateMember; 6 protected int parentProtectedMember; 7 int parentNoneMember; 8 9 public Parent() { 10 parentPublicMember = 1; 11 parentPrivateMember = 2; 12 parentProtectedMember = 3; 13 System.out.println("Parent 无参构造方法"); 14 } 15 16 public int parentPublicMethod() { 17 parentPrivateMethod(); 18 System.out.println("执行Parent 类的 public 构造方法!"); 19 return parentPrivateMember; 20 } 21 22 private void parentPrivateMethod() { 23 System.out.println("执行Parent 类的 private 构造方法!"); 24 } 25 26 protected void parentProtectedMethod() { 27 System.out.println("执行Parent 类的 protected 构造方法!"); 28 } 29 30 void parentNoneMethod() { 31 System.out.println("执行Parent 类的 无修饰 构造方法!"); 32 } 33} 34 35

现在,我们在本包(com.mec.about_inheritance.classes)下,来编写它的子类——Child.java :

1package com.mec.about_inheritance.classes; 2 3public class Child extends Parent { 4 public Child() { 5 this. //这里的代码我们还没敲完就能看到下图现象: 6 } 7} 8 9

在这里插入图片描述我们能够观察到:它的父类的所有 无修饰 和 用protected、public修饰词修饰的成员 和 方法,在本类中可以调用!

那么,现在我们创建一个新的包com.mec.about_inheritance.test,在这个包下建立 Parent类 的 包外子类 Son类:

1package com.mec.about_inheritance.test; 2 3import com.mec.about_inheritance.classes.Parent; 4 5public class Son extends Parent { 6 public Son() { 7 this. //这里的代码我们还没敲完就能看到下图现象: 8 } 9} 10 11

在这里插入图片描述我们能够观察到:它的父类的所有 用protected、public修饰词修饰的成员 和 方法,在本类中可以调用!

那么,现在我们创建一个新的包com.mec.about_inheritance.test,在这个包下建立 Parent类 的 包外非子类 Demo类:

1package com.mec.about_inheritance.demo; 2 3import com.mec.about_inheritance.classes.Child; 4import com.mec.about_inheritance.classes.Parent; 5import com.mec.about_inheritance.test.Son; 6 7public class Demo { 8 9 public static void main(String[] args) { 10 Parent parent = new Parent(); 11 Child child = new Child(); 12 Son son = new Son(); 13 14 parent. //这里的代码我们还没敲完就能看到下图现象: 15 } 16 17} 18 19

在这里插入图片描述我们能够观察到:它调用的类的所有 用public修饰词修饰的成员 和 方法,在本类中可以调用!

讲到这里,相信好多同学都已经懵了,毕竟本人也是从学习这个知识点的时期过来的,为了方便同学们对比这几种的区别,本人现在将其总结罗列到一张表中:
权限修饰符 总结

包内子类 包外子类 包内其他类 包外其他类 public 能 能 能 能 private 否 否 否 否 protected 能 能 能 否 default (或 无修饰) 能 否 能 否

那么,本人现在来介绍下,这些 权限修饰符 的一般使用标准吧:
1.凡是打算为子类继承的成员和方法,用 protected 修饰;
2.不打算被 包外的类 引用的 成员 和 方法 ,不用写任何 权限修饰符 。

我们之前说过,在我们构建一个比较大的JAVA工程时,一般都会用到 “构造方法”。
那么,现在,本人来讲解一下 继承关系 中的 构造方法
我们还是通过代码的运行结果来总结结论:
首先,在 com.mec.about_inheritance.constructor 包下建立 Grandfather 类:

1package com.mec.about_inheritance.constructor; 2 3public class Grandfather { 4 public Grandfather() { 5 System.out.println("执行爷爷构造方法!"); 6 } 7} 8 9

其次,我们在本包( com.mec.about_inheritance.constructor )下建立Parent 类:

1package com.mec.about_inheritance.constructor; 2 3public class Parent extends Grandfather { 4 public Parent() { 5 System.out.println("执行爸爸构造方法!"); 6 } 7} 8 9

然后,我们在本包( com.mec.about_inheritance.constructor )下建立Child 类:

1package com.mec.about_inheritance.constructor; 2 3public class Child extends Parent{ 4 public Child() { 5 System.out.println("执行孩子构造方法!"); 6 } 7} 8 9

最后,我们建立一个新的包 com.mec.about_inheritance.constructor.test ,并在这个包下建立 Test类:

1package com.mec.about_inheritance.constructor.test; 2 3import com.mec.about_inheritance.constructor.Child; 4 5public class Test { 6 7 public static void main(String[] args) { 8 new Child(); 9 } 10} 11 12

然后,我们来观察下运行结果:
在这里插入图片描述由此,我们可以看出,构造方法 的执行顺序
有当前类开始,根据继承关系,追溯到祖先类,再从上往下执行构造方法

但是,本人在之前的博文中说过,若一个类没有定义无参构造方法,则JVM会默认执行一个无参构造方法。但是,在继承这里,正是由于这个原因,会出现一些错误,如下:
我们现在对 Grandfather类 进行如下改变:

1package com.mec.about_inheritance.constructor; 2 3public class Grandfather { 4 private int one; 5 private int two; 6 7 public Grandfather(int one, int two) { 8 this.one = one; 9 this.two = two; 10 System.out.println("执行爷爷构造方法!"); 11 } 12 13 public int getOne() { 14 return one; 15 } 16 17 public void setOne(int one) { 18 this.one = one; 19 } 20 21 public int getTwo() { 22 return two; 23 } 24 25 public void setTwo(int two) { 26 this.two = two; 27 } 28 29} 30 31

但是,就在这时,我们能够发现,Parent类那里竟然出现了错误:
在这里插入图片描述错误提示说:父类Parent 的无参构造方法未定义,Parent类 无参构造方法必须执行另一种 父类 的构造方法。
简单来讲,就是说:因为Grandfather 类没有无参构造方法。要 Parent 执行 另一种 父类构造方法。
其实,Grandfather 类并不是没有构造方法,只是没有无参构造方法!
所以,这里执行 Grandfather 类的双参构造方法!

现在,本人对 Parent类 做如下改变:

1package com.mec.about_inheritance.constructor; 2 3public class Parent extends Grandfather { 4 public Parent() { 5 super(0, 0); 6 System.out.println("执行爸爸构造方法!"); 7 } 8} 9 10 11

在这里插入图片描述
可以看到,现在没有问题了!

由上面的代码展示,我们可以看出继承有如下特点

继承的特点

  • Java只支持单继承,不支持多继承(即 一个类只能拥有一个父类)
  • Java支持 多层继承(继承体系)

但是,虽然这样很方便,继承也是存在着条件的:

  1. 子类只能继承父类所有非私有的成员(成员方法和成员变量)
  2. 子类不能继承父类的构造方法,但是可以通过super(待会儿讲)关键字去访问父类构造方法
  3. 不要为了 部分功能 而去 继承

那么,在本篇博文的末尾,本人要提出的一点是:我们虽然可以用“继承”提高代码的复用性,
但是,“继承”同样存在很大的弊端

继承的弊端: 使得 类的耦合性增强

这违背了我们的开发原则之一的 —— 高内聚,低耦合
(所谓“耦合”—— 类与类的关系
所谓 “内聚”—— 自己完成某件事情的能力)

相信看到这的同学们,一定对于super(0, 0) 这一行代码十分好奇吧,那么,请观看本人的下半篇博文——《详解 继承(下)—— super关键字 与 多态》

代码交流 2021