java面向对象-抽象类、接口

147 阅读5分钟

面向对象-抽象类、接口

抽象类

什么是抽象类

在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开始,接口中新增的三种方法

  1. 默认方法:必须使用default修饰,默认会被public修饰,实例方法:对象的方法,必须使用实现类的对象来访问
default void test1(){
   System.out.println("==默认方法===")
}
  1. 私有方法:必修使用private修饰(JDK9开始才支持),实例方法对象的方法,不可以通过对象访问

    private void test1(){
       System.out.println("==私有方法===")
    }
    
  2. 静态方法:必须使用static修饰,默认会被public修饰,可以通过类点的方法的方式访问

    static void test1(){
       System.out.println("==静态方法===")
    }
    

    那为什么要新增这些方法呢?

    增强了接口的能力,更便于项目的扩展和维护。

    就比如默认default,在一个有100多个实现该接口的类,需要新增一个接口,那么就需要这100个类都要在实现这个接口,维护成成本很大,所以可以直接写一个默认的接口,这样每一个类都可以直接调用了

接口的多继承、使用接口的注意事项

  1. 一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承。
  2. 一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。
  3. 一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。
  4. 一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。