1. 继承概述
1.1 什么是继承
在程序中,可以使用extends关键字可以让一个类继承另外一个类。继承的类为子类(派生类),被继承的类为父类(超类,基类)。子类会自动继承父类所有的方法和属性。
1.2 为什么要使用继承
当我们发现一个类的功能不行,方法不够用时,就可以派生子类,增加方法。当我们需要定义一个能实现某项特殊功能的类时,就可以使用继承。最终还是为了一个目的,实现代码的复用性。
当我们定义一个类时,发现另一个类的功能这个类都需要,而这个类又要增加一些新功能时,就可以使用extends关键字继承那个类,这样那个被继承类的功能就都有了,不必重写编写代码。这时只要在新的类中编写新的功能即可,原有代码就被复用了。
1.3 继承的特点
子类中可以使用super调用父类成员,super用法和this类似,this是谁调用该方法就引用谁,super是调用该方法的对象的父类对象。
1.4 继承的优点
- 提高了代码的复用性。
- 让类与类之间产生了关系。有了这个关系,才有了多态的特性。
注意: 千万不要为了获取其他类的功能,简化代码而继承。必须是类与类之间有所属关系才可以继承。所属关系 is a
Java语言中:java只支持单继承,不支持多继承。
原因 :因为多继承容易带来安全隐患:当多个父类中定义了相同功能,当功能内容不同时,子类对象不确定要运行哪一个。但是java保留这种机制。并用另一种体现形式来完成表示。多实现。
java支持多层继承。也就是一个继承体系
想要使用体系,先查阅体系父类的描述,因为父类中定义的是该体系中共性功能。通过了解共性功能,就可以知道该体系的基本功能。那么这个体系已经可以基本使用了。那么在具体调用时,要创建最子类的对象,为什么呢?一是因为有可能父类不能创建对象,二是创建子类对象可以使用更多的功能,包括基本的也包括特有的。
简单一句话: 查阅父类功能,创建子类对象使用功能。
2. 组合设计模式
2.1 什么时候用组合
组合是一种实现代码复用的方式,当我们在定义一个类的时候需要用到另外一个类的方法时,就可以用组合。
2.2 怎么用组合
定义一个所需要的类类型的成员变量通过构造函数进行装配,接收一个该类类型的对象,用成员变量引用在需要使用另一个类的方法时通过成员变量访问
2.3 组合的优点
如果两个类没有父子关系,不合适用继承。Java只支持单继承,组合不占用继承位置。
3. super 关键字
子父类出现后,类成员的特点:
super和this的区别
- 当子类出现和父类一模一样的函数时,当子类对象调用该函数,会运行子类函数的内容。如同父类的函数被覆盖一样。这种情况是函数的另一个特性:重写(覆盖)
- 当子类继承父类,沿袭了父类的功能,到子类中,但是子类虽具备该功能,但是功能的内容却和父类不一致,这时,没有必要定义新功能,而是使用覆盖特殊,保留父类的功能定义,并重写功能内容。
重写注意:
- 子类覆盖父类,必须保证子类权限(方法的修饰符)大于等于父类权限,才可以覆盖,否则编译失败。
- 静态只能覆盖静态。
- 重载:只看同名函数的参数列表。与返回值无关
- 重写:子父类方法要一模一样。
3.3 子父类中的构造函数
在对子类对象进行初始化时,父类的构造函数也会运行,那是因为子类的构造函数默认第一行有一条隐式的语句super();super():会访问父类中空参数的构造函数。而且子类中所有的构造函数默认第一行都是super();
为什么子类一定要访问父类中的构造函?
因为父类中的数据子类可以直接获取。所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的。所以子类在对象初始化时,要先访问一下父类中的构造函数。如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。注意: super语句一定定义在子类构造函数的第一行。
4. 抽象类(abstract)
- 抽象类的特点:
- 有抽象函数的类,该类一定是抽象类。
- 抽象类中不一定要有抽象函数。
- 抽象类不能使用new创建对象
- 抽象类主要为了提高代码的复用性,让子类继承来使用
- 编译器强制子类实现抽象类父类的未实现的方法。
- 抽象类的优点:
- 提高代码复用性
- 强制子类实现父类中没有实现的功能
- 提高代码的扩展性,便于后期的代码维护
- 抽象类的应用场景:
描述一类事物的时候,发现该事物确实存在着某种行为,但是目前该行为是不具体的,那么这时候我们应该抽取该方法的声明,不去实现该方法,这时候我们应该使用抽象类。 - 抽象类要注意的细节:
- 如果一个方法没有方法体,那么该方法必须使用abstract修饰。
- 如果一个类有抽象的方法,那么该类也必须使用abstract修饰。
- 非抽象类继承抽象类的时候,必须要把抽象类中的所有抽象方法全部实现。
- 抽象类可以定义抽象方法以及非抽象方法。
- 抽象类是可以不存在抽象方法的。
- 抽象类不能创建对象。
- 疑问: 为什么抽象类不能创建对象呢?
- 因为抽象类一旦创建了对象就可以使用对象去调用方法了,一旦调用了抽象方法就没有任何的意义了。
- 抽象类是存在构造方法的。
- 疑问:既然抽象类不能创建对象,那么存在构造方法的意义在那?
- 因为抽象类的构造方法是留给子类调用初始化从父类继续下去的成员变量的。
- abstract 关键字,和哪些关键字不能共存。
- final:被final修饰的类不能有子类。而被abstract修饰的类一定是一个父类。
- private: 抽象类中的私有的抽象方法,不被子类所知,就无法被复写。而抽象方法出现的就是需要被复写。
- static:如果static可以修饰抽象方法,那么连对象都省了,直接类名调用就可以了。可是抽象方法运行没意义。
5. 接口(interface)
5.1 什么是接口
- 接口是一种特殊的抽象类,接口中声明的所有方法都是抽象的
- 使用interface关键字修饰一个接口
5.2 接口的用法
- 我们可以定义一个类来实现接口,使用implements关键字
- 实现一个接口需要实现接口中所有的方法,抽象类除外
- 通常使用匿名内部类来实现一个接口
- 接口可以继承接口(多继承),使用extends关键字。 接口不能继承抽象类,因为抽象类中可能有不抽象的方法。
- 一个类可以实现多个接口,为了实现多态
5.3 接口中的方法和变量
- 接口中定义的方法默认是公有的抽象的,被public abstract修饰
- 接口中定义的变量默认为全局常量,使用public static final修饰
5.4 abstract class和interface的区别
- 抽象类中可以有不抽象的方法,接口中全是抽象的方法
- 抽象类用extends继承,接口用implements实现
- 抽象类中的变量和方法没有默认修饰符,接口中的变量默认为public static final的,接口中的方法默认为public abstract
- 一个类只能继承一个抽象类,一个类可以实现多个接口
5.5 什么时候用抽象类,什么时候用接口
- 如果能用接口,就不用抽象类,因为别人实现接口可以不占用继承的位置。
- 如果定义一个抽象的父类,其中所有方法都是抽象的,那么就定义为接口。
- 如果定义一个抽象的父类的时候,需要有不抽象的方法,那么只能定义为抽象类。
6. 多态(Polymorphism)
- 多态: 父类引用类型变量指向了子类的对象或者是接口的引用类型变量指向了接口实现类的对象。 (一个对象具备多种形态)
- 多态的前提: 必须存在继承或者实现关系。
A. 多态要注意的细节:- 多态情况下,子父类存在着同名的成员变量时,默认会访问父类的成员变量。
- 多态情况下,子父类存在着同名的非静态函数时,默认是访问子类的成员函数
- 多态情况下,子父类存在着同名的静态函数时,默认是访问父类的成员函数。
- 多态情况下,不能访问子类特有的成员。
- 总结: 多态情况下,子父类存在同名的成员时,默认都会访问父类的成员.只有存在非静态的同名函数时,才是访问子类的成员。
B. 编译与运行的报错规则- 成员函数:编译看左边,运行看右边。
- 成员变量: 编译看左边,运行看左边(引用型变量所属的类)。
- 静态成员函数: 编译看左边,运行看左边
- 多态的应用场景:
- 多态用于形式参数类型的时候,可以接收更多类型 的参数。
- 多态用于返回值类型的时候,可以返回更多类型的参数。
- 多态的好处:提高了程序的拓展性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38C.多态的问题理解
lass 孔子爹 {
public int age = 40;
public void teach() {
System.out.println("讲解JavaSE");
}
}
class 孔子 extends 孔子爹 {
public int age = 20;
public void teach() {
System.out.println("讲解论语");
}
public void playGame() {
System.out.println("英雄联盟");
}
}
//Java培训特别火,很多人来请孔子爹去讲课,这一天孔子爹被请走了
//但是还有人来请,就剩孔子在家,价格还挺高。孔子一想,我是不是可以考虑去呢?
//然后就穿上爹的衣服,带上爹的眼睛,粘上爹的胡子。就开始装爹
//向上转型
孔子爹 k爹 = new 孔子();
//到人家那里去了
System.out.println(k爹.age); //40
k爹.teach(); //讲解论语
//k爹.playGame(); //这是儿子才能做的
//讲完了,下班回家了
//脱下爹的装备,换上自己的装备
//向下转型
孔子 k = (孔子) k爹;
System.out.println(k.age); //20
k.teach(); //讲解论语
k.playGame(); //英雄联盟