面向对象-抽象类、接口
抽象类
什么是抽象类
在Java中有一个关键字叫: abstract,它就是抽象的意思,可以用它修饰类、成员方法。
abstract修饰类,这个类就是抽象类;修饰方法,这个方法就是抽象方法
public abstract class Animal {
private String name;
//抽象方法:必须abstract修饰,只有方法签名,没有方法体
public abstract void cry(); //抽象类不可以有方法体
}
抽象类的注意事项以及特点
抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
类该有的成员(成员变量、方法、构造器)抽象类都可以有。
抽象类最主要的特点: 抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现。
一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
抽象类的场景和好处
父类知道每个子类都要做某个行为,但每个子类要做的情况不一样,父类就定义成抽象方法,交给子类去重写实现,我们设计这样的抽象类,就是为了更好的支持多态。
动物类:(抽象类)
public abstract class Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 动物都有叫的行为,可以把这个行为抽象出来
public abstract void cry(); //抽象类不可以有方法体
}
狗类:
public class Dog extends Animal{
//必须继承重写抽象方法
@Override
public void cry() {
System.out.println(this.getName()+"汪汪汪的叫");
}
}
猫类:
public class Cat extends Animal{
//必须继承重写抽象方法
@Override
public void cry() {
System.out.println(this.getName()+"喵喵喵的叫");
}
}
测试类:
public class Test {
public static void main(String[] args) {
//使用多态和抽象类结合
Animal animal=new Cat();
animal.setName("叮当猫");
animal.cry(); //叮当猫喵喵喵的叫
}
}
虽然猫和狗类都有叫的行为,但是叫的方式不同,所以就需要每个子类自己重写方法,实现具体的细节
接口
什么是接口
Java提供了一个关键字interface,用这个关键字我们可以定义出一个特殊的结构:接口。
public interface 接口名{
//成员变量(常量)
//成员方法(抽象方法)
}
注意:接口不能创建对象; 接口是用来被类实现(implements)的,实现接口的类称为实现类。
//修饰符class 实现类implements 接口1,,接口2,接口3,... {
}
一个类可以实现多个接口(接口可以理解成干爹,可以有多个干爹,但是自由一个父亲,所以继承是单继承) ,实现类实现多个接口,必须重写完全部接口的全部抽象方法,否则实现类需要定义成抽象类。
接口的好处
弥补了类单继承的不足,一个类同时可以实现多个接口。
让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现。和多态差不多,左边写接口=右边写实现,想3换司机了可以直接换右边的实现了,后面的代码都不用换的
下面是面向接口编程的案例帮助理解:
管理学生类:operatorStudent1为接口
public class manageStudent {
//管理学生数据
private ArrayList<Student> studentList=new ArrayList<>(); //定义集合用于存放所有学生
// private operatorStudent1 operstu= new oper1(); //实现面向接口编程了,操作1
private operatorStudent1 operstu= new oper2(); //实现面向接口编程了,操作2,可以随时直接更换后面,其他代码不用变
//实现多种方案可以灵活的切换
public void createStudent(){
//创建学生类
studentList.add(new Student("迪丽热巴",'女',99));
studentList.add(new Student("马尔扎哈",'女',96));
studentList.add(new Student("古力娜扎",'女',97));
studentList.add(new Student("杨沫",'男',67));
studentList.add(new Student("陈磊",'男',95));
studentList.add(new Student("胡歌",'男',74));
}
public void studentInfo(){
//展示学生详细信息
operstu.info(studentList); //
}
public void studentAverage(){
//展示学生详细信息
operstu.operator(studentList);
}
}
操作1:只显示学生详细信息;直接算出平均数
public class oper1 implements operatorStudent1 {
@Override
public void info(ArrayList<Student> students) {
//第一种方案只显示学生详细信息
System.out.println("展示所有学生信息");
for (int i = 0; i < students.size(); i++) {
Student stu = students.get(i);
System.out.println("==================");
System.out.println("学生的姓名为:" + stu.getSname());
System.out.println("学生的性别为:" + stu.getSex());
System.out.println("学生的成绩为:" + stu.getScore());
}
}
@Override
public void operator(ArrayList<Student> students) {
//直接算出平均数
double sum = 0;
for (int i = 0; i < students.size(); i++) {
Student stu = students.get(i);
sum +=stu.getScore();
}
System.out.println("学生成绩的平均数为:"+sum/students.size());
}
操作2:显示学生详细信息,并且展示男生和女生的人数;平均数去掉一个最高分和最低分,然后计算平均数
public class oper2 implements operatorStudent1 {
@Override
public void info(ArrayList<Student> students) {
//第一种方案只显示学生详细信息,并且展示男生和女生的人数
int count=0;// 男生人数
int nvcount=0; //定义女生人数
System.out.println("展示所有学生信息");
for (int i = 0; i < students.size(); i++) {
Student stu = students.get(i);
System.out.println("==================");
System.out.println("学生的姓名为:" + stu.getSname());
System.out.println("学生的性别为:" + stu.getSex());
System.out.println("学生的成绩为:" + stu.getScore());
if(stu.getSex()=='男'){
count++;
}else{
nvcount++;
}
}
System.out.println("男生人数为:"+count+",女生人数为:"+nvcount);
}
@Override
public void operator(ArrayList<Student> students) {
//直接算出平均数
//去掉一个最高分和最低分,然后计算平均数
double sum = 0;
double max=students.get(0).getScore();
double min=students.get(0).getScore();
for (int i = 0; i < students.size(); i++) {
Student stu = students.get(i);
sum +=stu.getScore();
if(stu.getScore()>max) max=stu.getScore();
if(stu.getScore()<min) min=stu.getScore();
}
System.out.println("最高成绩为:"+max);
System.out.println("最低成绩为:"+min);
System.out.println("学生成绩的平均数为:"+(sum-max-min)/students.size());
}
JDK8开始,接口中新增的三种方法
- 默认方法:必须使用default修饰,默认会被public修饰,实例方法:对象的方法,必须使用实现类的对象来访问
default void test1(){
System.out.println("==默认方法===")
}
-
私有方法:必修使用private修饰(JDK9开始才支持),实例方法对象的方法,不可以通过对象访问
private void test1(){ System.out.println("==私有方法===") } -
静态方法:必须使用static修饰,默认会被public修饰,可以通过类点的方法的方式访问
static void test1(){ System.out.println("==静态方法===") }那为什么要新增这些方法呢?
增强了接口的能力,更便于项目的扩展和维护。
就比如默认default,在一个有100多个实现该接口的类,需要新增一个接口,那么就需要这100个类都要在实现这个接口,维护成成本很大,所以可以直接写一个默认的接口,这样每一个类都可以直接调用了
接口的多继承、使用接口的注意事项
- 一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承。
- 一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。
- 一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。
- 一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。