1、 继承的基本概念
继承是从已有的类创建新类的过程
- 继承是面向对象三大特征之一
- 被继承的类称为父类,继承父类的类称为子类
- 继承是指一个对象直接使用另一个对象的属性和方法
- 通过继承可以实现代码重用
继承一个父类,只能继承非私有的数据(属性与方法)
- 继承是发成在多个类之间
- 继承使用关键字extends
- Java 只能单继承,允许多层继承
- 被继承的类叫父类,继承父类的类叫子类
- 在父类中的非私有属性和方法可以被子类继承
- protected 修饰的属性或方法可以被子类继承
- 构造方法不能被继承
- 创建对象会调用构造方法,调用构造方法不一定就是创建该类对象
- 实例化子类对象,会先调用父类的构造方法,如果父类中没有默认的构造方法,那么子类必须显示的通过super( )来调用父类的带参数构造方法,super也只能在子类构造方法的第一句
如果父类没有默认构造函数,那么一定是定义了有参构造函数,也就是说如果要创建父类的对象,需要传入参数,所以继承父类的子类创建对象时,也要向父类的构造函数中传入参数,也就是说需要在子类的构造函数中调用父类的构造函数(使用super() 代替父类构造函数),必须放在子类的构造函数,这就意味着父类构造函数先于子构造函数结束
缺点:
增强了类与类之间的耦合性
高内聚,低耦合
2、 新建一个子类的执行过程
- 初始化父类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化
- 初始化子类中静态成员变量和静态代码块,按照在程序中出现的顺序初始化
- 初始化父类的普通成员变量和代码块,在执行父类的构造方法
- 初始化子类的普通成员变量和代码块,在执行子类的构造方法
3、子类的实例化过程
在子类进行实例化操作的时候,首先会让其父类进行初始化操作,之后子类再进行实例化操作
4、方法的重写
方法重写:
在java 中,子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不像原封不动地继承父类的方法,而是做一定的修改,这就需要采用方法的重写。方法重写又称为方法覆盖。
在子类和父类中,重写方法后,在调用时,以创建的对象类型为准,会调用谁的方法
方法重写的一些特性:
- 发生在子父类中,方法重写的两个方法返回值,方法名、参数列表必须完全一致(子类重写父类的方法)
- 子类抛出异常不能超过父类相应方法抛出的异常
- 子类方法的访问级别不能低于父类相应方法的访问级别(子类访问级别不能低于父类访问级别)
- 父类中的方法若使用private、static、final任意修饰符修饰,那么不能被子类重写
为什么要重写方法?
若从父类中继承过来的方法,不能满足子类特有的需求时,子类就需要重写父类中相应的方法,方法的重写也就是程序扩展的体现
5、super关键字
- 使用super 嗲用父类中的属性,可以从父类实例处获得信息
- 使用super 调用父类中的方法,可以委托父类对象帮助完成某件事情
- 使用super调用父类中的构造方法(super(实参)形式),必须在子类构造方法的第一条语句,嗲用父类中相应的构造方法,若不显示的写出来,默认调用父类的午餐构造方法,比如:super()
this 表示当前对象
使用super来调用父类的属性,方法,和构造方法
6、继承的应用示例
7、final关键字
- 使用final 关键字声明一个常量 修饰属性或者修饰局部变量
- 使用final 关键字声明一个方法, 该方法为最终方法,且只能被子类继承,但是不能被子类重写
- 使用final关键字声明一个类,该类就转变为最终类,没有子类的类,final修饰的类无法被继承
- 使用final声明一个属性,就是常量
8、抽象类
用abstract关键字声明的类称为抽象类,很多具有相同特征和行为的对象可以抽象为一个类,很多具有相同特征和行为的类可以抽象为一个抽象类
- 抽象方法只有声明,没有实现
继承抽象类的具体实现类必须实现所有抽象方法
抽象类可以没有抽象方法,有抽象方法的类必须是抽象类
- 非抽象类继承抽象类必须实现所有抽象方法
- 抽象类可以继承抽象类,可以不实现父类抽象方法
- 抽象类可以有方法实现和属性(可以有非抽象方法)
- 抽象类不能被实例化
- 抽象类不能声明为final
- 抽象类可以有构造方法
9、接口
使用规则:
- 定义一个接口,使用interface关键字
- 在一个接口中,只能定义常量、抽象方法,在JDK1.8后可以定义默认的实现方法
- 接口可以继承多个接口 extends
- 一个具体类可以实现接口使用implements关键字
- 一个类可以实现多个接口
- 抽象类实现接口可以不实现接口的方法
- 在接口中定义的方法没有声明访问修饰符,默认为public
- 接口不能有构造方法
interface IEat{
void eat();
int NUM = 10;
public default void print(){
System.out.print("默认实现");
}
}
实现一个功能,在接口中返回一个工厂类,工厂类中实现有具体代码
public class Main {
public static void main(String[] args) {
System.out.println(Calculator.getInstance().add(1, 2));
}
}
interface Calculator {
static Calculator getInstance() {
return new BasicCalculator();
}
int add(int first, int second);
int subtract(int first, int second);
int divide(int first, int second);
int multiply(int first, int second);
}
接口:
class BasicCalculator implements Calculator {
@Override
public int add(int first, int second) {
return first + second;
}
@Override
public int subtract(int first, int second) {
return first - second;
}
@Override
public int divide(int first, int second) {
return first / second;
}
@Override
public int multiply(int first, int second) {
return first*second;
}
}
10、多态性
什么是多态?
对象在运行过程中的多种形态,多态性我们大概可以分为两类
- 方法的重载与重写
- 对象的多态性
例如 用父类的引用指向子类对象(用大的类型去接受小的类型,向上转型、自动转换、向父类型转)
Chicken home = new HomeChicken();
多态: 父类方法 f() 在派生类 可能会被覆盖,由于f()方法 是被动态绑定的,所以即使是通过泛化的父类引用来调用f()方法,也可以产生正确行为;这就是多态;
结论
在编程时针对抽象类型编写代码,称为面向抽象编程(面向接口编程)
父类通常都定义为抽象类、接口
abstract class Chicken{
public void eat(){};
}
class HomeChicken extends Chicken{
public void eat(){
System.out.println("家鸡");
}
}
class YeChicken extends Chicken{
public void eat(){
System.out.println("野鸡");
}
}
class JianJiaoChicken extends Chicken{
public void sing(){
System.out.println("唧唧复唧唧");
}
public void eat(){
System.out.println("尖叫鸡");
}
}
class Test{
public static void main(String[] args){
Chicken homeChicken = new HomeChicken();
Chicken yeChicken = new YeChicken();
Chicken jianJiaoChicken = new JianJiaoChicken();
eat(homeChicken);
eat(yeChicken);
eat(jianJiaoChicken);
}
// 抽象(粒度) 面向抽象编程(面向接口编程)
public static void eat(Chicken c){
c.eat();
if( c instanceof JianJiaoChicken){ // 一般这里写什么类型,下面也写什么类型
// 因为父类没有Sing 方法,不能使用父类直接调用子类特有的方法
JianJiaoChicken jjc = (JianJiaoChicken)c;
jjc.sing();
}
}
}
对象的多态性:
对象多态性是从继承关系中的多个类而来
向上转型: 将子类实例转成父类引用
格式: 父类 父类对象 = 子类实例; ——> 自动完成
以基本数据类型操作为例: int i = ‘a’;
( 因为char 的容量比int 小,所以可以自动完成)
向下转型: 将父类实例转成子类实例
格式 : 子类 子类对象 = (父类实例); ——> 强制转换
以基本数据类型操作为例: byte c = (byte)97;
11、instanceof 关键字
instanceof 用于检查对象是否为指定的类型,通常在把父类引用强制转换为子类引用时要使用,以避免发生类型转换异常
父类的设计法则
通过instanceof 关键字,我们可以很方便的检查对象的类型,但如果一个父类的子类过多,这样的判断还是显得很繁琐,那么如何去设计一个父类呢?
- 父类通常情况下都设计为抽象类或接口,其中优先考虑接口,如接口不能满足才考虑抽象类
- 一个具体的类尽可能不去继承另一个具体类,这样的好处是无需检查对象是否为父类的对象。
12、抽象类应用—模板方法模式
模板方法模式(template method): 定义一个操作中的算法的骨架,而将一些可变部分的实现延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定的步骤。
13、接口应用—策略模式
策略模式(Strategy Pattern)定义了一系列的算法,将每一种算法封装起来并可以相互替换使用,策略模式让算法独立于使用它的客户应用而独立变化。(后台三层架构可以理解为策略模式,一个接口可以有多个实现类,不同实现类为不同的行为,dao层,不同的数据库有可能有不同的语句)
OO 设计原则:
- 面向接口编程(面向抽象编程)
- 封装变化
- 多用组合,少用继承
把可变的行为抽象出来,这样的好处是这些行为可以在真正使用时相互替换
14、Object 类
每个类都是用Object 作为超类,所有对象(包括数组)都实现这个类的方法
所有类都是Object类的子类
toString(): 返回该对象的字符串表示 通常,toString() 方法会返回一个”以文本方式表示” 此对象的字符串。结果应是一个简明且易于读懂的信息表达式。建议所有子类都重写此方法。
equals() :指示其他某个对象是否与此对象”相等”。 equals 方法在非空对象引用上实现相等关系:
- 自反性, 对于任何非空引用值x , x.equals(x) 都应返回 true
- 对称性, 对于任何非空引用值,x和y,当且仅当y.equals(x) 返回true时, x.equals(y) 才返回true
- 传递性, 对于任何非空引用值,x,y,z,如果x.equals(y) 返回true, 并且y.equals(z) 返回true, 那么x.equals(z) 应该返回true
- 一致性, 对于任何非空引用值x 和 y, 多次调用 x.equals(y) 始终返回true 或始终返回false, 前提是对象上equals 比较中所用的信息没有任何修改
- 对于任何非空引用值 x , x.equals(null) 都应返回false
finalize():
当垃圾回收器不确定不存在对该对象的更多引用时,由对象的垃圾回收期调用此方法。子类重写finalize方法,以配置系统资源或执行其他清除
getClass() : 返回此Object 的运行时类,(得到类信息)