目录
一、继承
1.1 为什么需要继承
1.2 继承的概念
1.3 继承的语法
1.4 访问父类的成员
1.4.1 在子类方法中访问父类的成员
1.4.2 在子类外的方法中访问父类的成员
1.5 super关键字
1.6 子类和父类的构造方法
1.7 super 和 this异同点(待改进)
1.8 再谈初始化顺序
1.9 protected关键字
1.10 继承的方式
1.11 final关键字
二、组合
一、继承
1.1 为什么需要继承
Java中使用类对现实世界中的实体进行描述(Java中使用类实例化对象后,该对象可用来表示现实中的实体)。但是现实世界错综复杂,事物之间可能会存在一些关联,这一点在程序设计时是需要考虑的。例如:用Java语言去描述狗 和 猫时,我们可能会写出如下的代码,可以看到图中用绿色和蓝色方框框起来的部分,这些成员变量/方法有着了大量的重复,我们是否能将这些共性的部分进行抽取,从而达到代码复用,简化代码的目的呢?答案是可以的,面向对象思想中提出了继承的概念,其存在的意义就是对共性的抽取,从而实现代码复用。
1.2 继承的概念
1. 继承机制是面向对象程序设计中使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增添新功能,这样产生的类,称为派生类/子类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。
2. 继承主要解决的问题是:对共性的抽取,从而实现代码复用。
3. <1.1>的例子中狗类 和 猫类它们有着一些共有的成员,同属于上级抽象类别:动物。于是我们可以先创建动物类将狗类 和 猫类共有的成员在动物类中定义,让狗类 和 猫类继承动物类的成员即可达到代码复用的效果(狗类 和 猫类得有自己独有的成员,否则没必要使用继承了),于是有了如下示意图。
1.3 继承的语法
1. 在Java中如果想要表示类之间是继承关系,需要借助extends关键字,具体如下:
修饰符 + class + 子类 + extends + 父类 + { //... },extends是扩展的意思。
2. 对<1.2>中示意图代码的实现,如下图所示。
3. 注意:父类中的成员变量不能用private修饰了,如果用private修饰成员变量,这些成员变量就不能在其他类中被调用了。
1.4 访问父类的成员
1.4.1 在子类方法中访问父类的成员
【Ⅰ】子类的非静态方法中:
【①】访问父类非静态的成员变量/方法:
用super访问即可。
【②】访问父类的静态的成员变量/方法:
用父类访问即可(父类访问前缀也可以不写)。
【Ⅱ】子类的静态方法中:
【①】访问父类非静态的成员变量/方法:
先在静态方法中实例化子类对象,然后用对象的引用去访问(如果出现子类和父类的非静态成员变量/方法有同名的情况,对于同名的非静态成员方法来说编译器会根据调用方法的参数或是否在子类中发生了重写来判断到底调用哪个非静态的成员方法;对于同名的非静态成员变量来说,一般情况下我们不会傻到这么写🤬)。
【②】访问父类的静态的成员变量/方法:
用父类访问即可(父类访问前缀也可以不写)。
1.4.2 在子类外的方法中访问父类的成员
【Ⅰ】首先实例化子类对象
【Ⅱ】其次需要通过父类中成员变量/方法前面的访问限定符判断当前所在的位置是否能够调用想要调用的父类成员变量/方法,如果可以,非静态的成员变量/方法用对象的引用去访问即可(当子类和父类有同名的非静态成员变量/方法时,对于同名的非静态成员方法来说编译器会根据调用方法的参数或是否在子类中发生了重写来判断到底调用哪个非静态的成员方法;对于同名的非静态成员变量来说,一般情况下我们不会傻到这么写🤬),静态的成员变量/方法用父类访问即可(这里的访问前缀不能省略)。
1.5 super关键字
1. 由于设计不好,或者因场景需要,子类和父类中可能会存在相同名称的成员,如果要在子类方法中访问父类同名成员时,该如何操作?直接访问是无法做到的 (因为编译器访问的逻辑是"先在子类中找,找不到再到父类中找,父类中找不到就报错"),Java提供了super关键字,该关键字主要作用:在子类的非静态成员方法中访问父类的非静态成员。
2. 特点:super和this一样只能在非静态的成员方法中使用。
3. super()的用法在后面会介绍。
1.6 子类和父类的构造方法
1. 下图代码中当我们写了父类带参数的构造方法时,子类会报错的原因是由于子类对象在构造时,需要先调用父类的构造方法,然后再执行子类的构造方法 (父子父子,肯定是先有父再有子,所以在构造子类对象时候 ,先要调用父类的构造方法,将从父类继承下来的成员初始化完整 ,然后才会再调用子类自己的构造方法,将子类自己新增加的成员初始化完整)。
2. 子类和父类构造方法的简介:
① 若父类显式定义无参的构造方法或者没有定义构造方法,在子类构造方法第一行默认有隐含的super()调用(意思是,可以不用自己写这个super()),表示在子类构造方法中调用父类的无参构造方法。
② 如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败。
③ 在子类构造方法中,super(...)调用父类构造时,必须是子类构造函数中第一条语句。
④ super(...)只能在子类构造方法中出现一次,并且不能和this()同时出现。
⑤ 如果在父类和子类中都没有显示定义构造方法,则编译器会在父类中默认生成一个不带参数的父类构造方法,在子类中默认生成一个不带参数的子类构造方法且在这个子类构造方法的第一行是super()。
3. 使用开发工具在子类中快速生成构造方法: (和在<类和对象>中一样)
1.7 super 和 this异同点(待改进)
【相同点】
① super和this都是Java中的关键字。
② 都只能在本类/子类的非静态成员方法中使用。
③ super(...)和this(...)都是用于在构造方法中调用构造方法的,且只能位于构造方法的第一行,但它们两个不能同时出现在一个构造方法中。
② 如果不显示定义父类/子类的构造方法,编译器会提供默认的不带参数的父类/子类的构造方法。
【不同点】
① 在子类的非静态方法中,this可用于调用子类自己的或从父类继承下来的非静态成员变量/方法;super只能调用父类的非静态成员变量/方法。
② super(...)用于调用父类的构造方法,this(...)用于调用子类的构造方法。
1.8 再谈初始化顺序
结论:
1. 静态的只执行一次。
2. 静态的先执行,且父类静态先于子类静态。
3. 父类的实例代码块和构造方法 先于 子类的实例代码块和构造方法。
1.9 protected关键字
1. 在类和对象章节中,为了实现封装特性,Java中引入了访问限定符,主要限定:类或者类中成员能否在类外或者其他包中被访问。那父类中不同访问权限的成员,在子类中的可见性又是什么样子的呢?
2. 根据上表可以发现,如果父类的成员变量被private修饰在不管子类是否与父类在同一个包下,在子类中都是无法访问该父类的成员变量的;如果父类的成员变量没有被任何修饰具有包访问权限,那么当子类和父类处于同一个包下时,子类是可以访问该父类的成员变量的;如果父类的成员变量被protected修饰,那么子类与父类处于同一个包下或不同包下,子类都是可以访问父类的该成员变量的。
3. 注意:父类中private成员变量虽然在子类中不能直接访问,但是也继承到了子类中。
4. 什么时候下用哪一种呢? 我们希望类要尽量做到 "封装", 即隐藏内部实现细节, 只暴露出必要的信息给类的调用者。因此我们在使用的时候应该尽可能的使用比较严格的访问权限。例如,如果一个方法能用 private, 就尽量不要用 public。另外, 还有一种简单粗暴的做法:将所有的字段设为 private, 将所有的方法设为 public,不过这种方式属于是对访问权限的滥用。
1.10 继承的方式
1. 现实生活中,事物之间的关系是非常复杂,灵活多样,比如:
2. 但在Java中只支持以下几种继承方式:
3. 注意:Java中不支持多继承。时刻牢记,我们写的类是现实事物的抽象,而我们真正在公司中所遇到的项目往往业务比较复杂, 可能会涉及到 一系列复杂的概念, 都需要我们使用代码来表示, 所以我们真实在项目中所写的类也会有很多,类之间的关系也会更加复杂,但是即使如此,也并不会希望类之间的继承层次太复杂 (一般不希望出现超过三层的继承关系),如果继承层次太多,就需要考虑对代码进行重构了。
4. 如果想从语法上进行限制继承, 可以使用 final 关键字。
1.11 final关键字
final关键字可用于修饰类、成员变量和成员方法。
1. 被final修饰的类不能被继承。我们平时是用的 String 字符串类, 就是用 final 修饰的, 不能被继承。在定义类时不能同时用abstract和final修饰类,因为abstract修饰的类是抽象类,只能被继承不能实例化对象,它和final的作用是对立面。
2. 被final修饰的成员变量是不可变的(相当于是一个常量,所以在定义final修饰的成员变量时,变量名的字母一般全部大写)。如果成员变量是基本数据类型,初始化之后成员变量的值不能被改变;如果成员变量时引用数据类型,那么它只能指向初始化时指向的哪个对象,不能再指向别的对象,但对象当中的内容是允许改变的。
3. 被final修饰的方法不能被重写,但可以被重载。
二、组合
1. 和继承类似, 组合也是一种表达类之间关系的方式, 也是能够达到代码复用的效果。组合并没有涉及到特殊的语法 (诸如 extends 这样的关键字),仅仅是将一个类的对象作为另外一个类的成员变量。 继承表示对象之间是is-a的关系,比如:狗是动物,猫是动物;组合表示对象之间是has-a的关系,比如:汽车和其轮胎、发动机、方向盘、车载系统等的关系就应该是组合,因为汽车是由这些部件组成的。
2. 组合和继承都可以实现代码复用,应该使用继承还是组合,需要根据应用场景来选择,一般建议:能用组合尽量用组合。
深入理解Java中的组合和继承-HollisChuang's Blog
本篇文章已完结,谢谢支持哟 ^^ !!!