继承、多态、抽象、接口

发布于 2023-07-11  790 次阅读


继承

使用继承可以节省空间,减少代码长度

访问方式

  • public:
    • 本类中,同一个包下的其他类里,任意包下的子类里,任意包下的任意类里
  • protected:
    • 本类中,同一个包下的其他类里,任意包下的子类里
  • 缺省:
    • 本类中,同一个包下的其他类里
  • private:
    • 本类中

*任意包下的子类内能够访问potected类型的成员
但是子类对象无法访问potected类型的成员(仅限子类内部访问)

单继承

java的继承均为单继承,子类无法同时继承多个父类,但是支持多层继承

使用接口可以实现类似多继承的效果

Object类

Object类是所有类都默认直接或间接继承的父类

方法重写

当父类对象的方法不符合子类需求时,可以在子类重写与父类相同的方法,调用时采用就近原则,调用子类的新方法

  • 注意事项:
    • 1.方法重写前加@Override注解 可以检查重写格式正确性,提高代码可读性
    • 2.子类重写父类方法时,访问权限需要>=父类方法权限(public>protected>缺省)
    • 3.重写方法的返回值范围要<=父类返回值范围
    • 4.不能重写私有方法,静态方法

子类中访问其他成员的特点:

就近原则(局部>本类>父类)
局部-直接访问/本类-this.X访问/父类-super.x/祖先类super.super…

子类中构造器的特点

  • 先调用父类构造器再执行自身构造器(同C++)
  • 如果父类没有无参构造器,子类构造器必须指定选择父类的有参构造器[super(a,b)]否则产生报错
  • 可以用this(A,B)调用该类的构造器(在一个函数内不能同时出现this()和super())
  • this()和super()调用都必须是构造函数的第一行语句

多态

定义:有继承或实现关系,存在父类引用子类对象,存在方法重写(只有对象和行为具有多态,成员变量没有多态)

People p1 = new Student();
此时People为引用类型 而Student为实际类型,函数根据实际类型来访问,变量通过引用类型访问

好处:

  • 1.多态形势下,右边的对象时解耦合的,方便扩展和维护
  • 2.定义方法时,使用父类类型的形参,可以接收一切子类对象,扩展性更强,更便利
    • public static void f(people p)//可以接收所有以people为父类的子类对象

问题:

多态中不能使用子类的独有功能

解决:

强制类型转换 -> 存在继承或实现关系可以在编译阶段进行强制类型转换

如:Student s1 = (Student) p1;//People p1

问题:

编译时期有继承关系就可以强转,但是运行时容易出现异常

People p1 = new Student();
Teacher t1 = (Teacher)p1;

编译器认为p1实际对象为People但实际值为Student [异常ClassCastException]

解决:

使用关键字instanceof判断当前对象的真实类型

if(p1 instanceof Student)
    Student s2 = (Student) p1;
else
    Teacher s2 = (Teacher) p1;

抽象

使用abstract修饰的类或方法称为抽象类/方法

抽象类

  • 抽象类中不一定要有抽象方法,有抽象方法的类一定是抽象类
  • 抽象类中可以有普通类的所有成员(变量,方法,构造器)
  • *抽象类不能创建对象,仅作为父类使用,让子类继承并实现
  • 如果子类继承抽象类,必须重写完抽象类的所有抽象方法,否则这个子类也要定义为抽象类

抽象方法

用abstract修饰,只有方法签名,不能有方法体

使用场景:

父类知道每个子类的行为,但每个子类的方法实现不一样,父类可以定义为抽象方法,由子类重写实现,设计成抽象类可以更好的支持多态

//将f方法定义为抽象方法,然后让两个类继承一个抽象父类,并重写f方法,就可以实现共用一个方法的效果
public abstract class Parent {
    public void f(xxx) {
        //代码块A
        implement();
        //代码块B
    }
    public abstract void implement();
}

public class Child1 extends Parent {
    @Override
    public void implement() {
        //实现1
    }
}

public class Child2 extends Parent {
    @Override
    public void implement() {
        //实现2
    }
}

接口

使用关键字interface定义接口,(JDK8之前)

接口里只能有成员变量和成员方法(默认为常量,抽象方法)

接口不能创建对象,接口是用来被类实现(implements)的,实现接口的类被称为实现类

一个类可以实现多个接口

修饰符 class 实现类 implements 接口1,接口2,…{}

优点:

  • 弥补了单继承的不足,一个类可以同时实现多个接口
  • 让程序可以面向接口编程,程序员可以灵活方便的切换各种业务实现
  • 增强接口能力,便于扩展和维护项目

JDK8开始新增的三种方法

默认方法

用 default 修饰的非抽象方法,它可以在接口中提供一个默认的实现,而不需要实现类去重写它

默认方法只能通过接口的实现类的对象来调用,不能通过接口名来调用

如果一个类实现了多个接口,而这些接口中有相同签名的默认方法,那么这个类必须重写这个默认方法,否则会有编译错误

私有方法

用 private 修饰的方法,它可以在接口中封装一些辅助性的逻辑,而不暴露给外部

私有方法只能在接口内部被调用,不能被接口的实现类或子接口继承或访问

私有方法可以是静态的或非静态的,但不能是默认的

私有方法可以在默认方法或其他私有方法中访问

静态方法

用 static 修饰的方法,它可以在接口中定义一些与实例无关的操作,而不需要实现类去重写它

静态方法只能通过接口名来调用,不能通过接口的实现类的对象或子接口来调用

静态方法也不能被接口的实现类或子接口继承或重写

接口的多继承

一个接口可以同时继承多个接口

注意事项

  • 一个接口继承多个接口,如果多个接口中存在方法签名(方法名+参数类型)冲突,则不支持多继承(相同的方法签名返回值也要相同)
  • 一个接口实现多个接口,如果多个接口中存在方法签名(方法名+参数类型)冲突,则不支持多实现
  • 如果一个类同时继承了父类A和实现了接口B,对于A和B中同名的默认方法,实现类会优先调用父类的方法
  • 如果一个类实现了多个接口,多个接口中存在同名默认方法,会产生冲突(解决方法:重写(Override)同名的默认方法)

final关键字

作用:修饰类,方法,变量

  • 修饰类:最终类,不能被继承
  • 修饰方法:最终方法,不能被重写
  • 修饰变量:常量,只能被赋值一次

常量:使用static final修饰的成员变量叫做常量

作用:记录配置信息

优点:可读性,维护性好,性能与使用字面量相同

命名规范:使用大写英文单词,多个单词用下划线链接(SCHOOL_NAME)