【JavaSE篇】——抽象类和接口

本文详细解释了抽象类和接口在Java编程中的概念、语法特点,包括抽象类的使用场景、抽象方法的定义、接口的定义与实现规则,以及它们之间的区别,强调了抽象类的抽象方法无需重写,而接口的所有方法必须被子类实现。
摘要由CSDN通过智能技术生成

目录

🎓抽象类

🎈抽象类语法

🎈抽象类特性

🎈抽象类的作用

🎓接口

🎈语法规则

🎈接口特性

🎈接口使用(实现USB接口)

🎈实现多个接口

🎈接口间的继承

🎈接口使用实例 (给对象数组排序)

🌈Comparable接口<比较>

🌈Comparator接口<比较器>

🚩抽象类和接口的区别

🎓抽象类

抽象类自己本身是不具有什么特性的,就比如Shape类中我们有draw方法,但是Shape并没有什么具体的形状,而继承的子类比如三角形类,矩形类,圆形类等都是在有具体图形,我们称Shape类是抽象类。

在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的, 如果 一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。

 


在打印图形例子中 , 我们发现 , 父类 Shape 中的 draw 方法好像并没有什么实际工作 , 主要的绘制图形都是由 Shape的各种子类的 draw 方法来完成的 . 像这种没有实际工作的方法 , 我们可以把它设计成一个 抽象方法 (abstract method) , 包含抽象方法的类我们称为 抽象类 (abstract class)

🎈抽象类语法

Java 中,一个类如果被 abstract 修饰 称为抽象类,抽象类中被 abstract 修饰的方法称为抽象方法, 抽象方法不用给出具体的实现体
// 抽象类:被abstract修饰的类
abstract class Shape1{
    // 抽象方法:被abstract修饰的方法,没有方法体
    abstract public void draw();
    abstract void calcArea();
    // 抽象类也是类,也可以增加普通方法和属性
    public int age;
    public void func(){};
}
注意:抽象类也是类,内部可以包含普通方法和属性,甚至构造方法

🎈抽象类特性

1. 抽象类不能直接实例化对象 new   

肯定很多人疑问,抽象类不能实例化对象,那么存在意义在哪?—抽象类存在的意义是为了被继承

public class Test {
    Shape1 shape1=new Shape1();
}
//Shape1是抽象类,不能实例化

2.抽象类和普通类不一样的是抽象类可以包含抽象方法,可以包含普通类所包含的成员(上述语法已讲述)

3.抽象类和抽象方法都是由abstract修饰的,方法中没有具体的实现体(上述语法)
 4.抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也是抽象类,必须要使用 abstract 修饰(被abstract修饰就不用重写方法,应用于有些类不需要这个方法)
abstract class Shape1{
    // 抽象方法:被abstract修饰的方法,没有方法体
    abstract public void draw();
}
class Cycle1 extends Shape1{
    @Override
    public void draw() {
        System.out.println("画⚪");
    }
}

5.如果一个抽象类B继承了一个抽象类A,此时B当中不需要重写A的重写方法,但如果B再被

普通类继承,就需要重写。


6.抽象方法不能被private,final和static修饰,因为抽象方法要被子类重写
我们可以这样想,方法就是为了重写设置成私有的,怎么能被重写?
并且我们在 重写的时候说到 (java子类方法权限要等于大于父类的访问权限?)
7. 抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类
8. 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量
❗A类
abstract class A{
    public int age;
    public String name;
    public A(int age,String name){
        this.age=age;
        this.name=name;
    }
    abstract public void func();

❗B类

class B extends A{
    public B(){
        super(8,"chenle");
    }
    public void func(){
        System.out.println("sadasd");
    }
}

❗运行区


🎈抽象类的作用

抽象类本身不能被实例化 , 要想使用 , 只能创建该抽象类的子类 . 然后让子类重写抽象类中的抽象方法 .
有些同学可能会说了, 普通的类也可以被继承呀, 普通的方法也可以被重写呀, 为啥非得用抽象类和抽象方法
呢
确实如此 . 但是使用抽象类相当于多了一重编译器的校验 .
使用抽象类的场景就如上面的代码 , 实际工作不应该由父类完成, 而应由子类完成 . 那么此时如果不小心误用成父类了, 使用普通类编译器是不会报错的 . 但是父类是抽象类就会在实例化的时候提示错误 , 让我们尽早发现问题/。
很多语法存在的意义都是为了 " 预防出错 ", 例如我们曾经用过的 final 也是类似 . 创建的变量用户不去修改 , 不就相当于常量嘛? 但是加上 final 能够在不小心误修改的时候 , 让编译器及时提醒我们 .
充分利用编译器的校验 , 在实际开发中是非常有意义的 .

🎓接口

接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用
在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。

🎈语法规则

接口的定义格式与定义类的格式基本相同,将 class关键字换成 interface 关键字 ,就定义了一个接口。
public interface 接口名称{
// 抽象方法
public abstract void method1(); // public abstract 是固定搭配,可以不写
public void method2();
abstract void method3();
void method4();
// 注意:在接口中上述写法都是抽象方法,跟推荐方式4,代码更简洁
}
提示:
1. 创建接口时, 接口的命名一般以大写字母 I 开头.
2. 接口的命名一般使用 "形容词" 词性的单词.
3. 阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性

🎈接口特性

1.使用interface来修饰接口

2.接口当中的成员方法,不能有具体的实现  [默认是public]

所以子类的重写方法不能默认不写,子类的重写方法是default,而子类的访问权限要大于父类的访问权限,所以我们必须在子类中的重写方法前写上public

  • 抽象方法:默认是public abstract方法
  • JDK1.8开始 ,允许有可以实现的方法,但是这个方法只能由default修饰
  • 可以实现一个静态方法
interface Ishape{
    public abstract void draw();//抽象方法
    void draw();//这是最好的表达抽象方法方式

     default public  void draw1(){
        System.out.println("默认方法");
    }

    public static void draw2(){
        System.out.println("静态方法");
    }
}

3.成员变量默认是public static final 修饰的(是不能修改的)

   成员方法 默认是public abstract 修饰(其他修饰符都是错的)

 public static final int age=10;//public static final
    void draw3();//public abstract

4.接口不能被实例化(抽象类也不能实例化)因为接口中的方式默认为抽象方法

5.类和接口之间采用implements来实现多个接口

一旦类实现接口之后,就必须重写这个接口里面的抽象方法。接口里的普通成员方法可以重写也可以不重写。

interface Itest{
    void draw3();//public abstract
    default public  void draw1(){
        System.out.println("默认方法");
    }
    public static void draw2(){
        System.out.println("静态方法");
    }
}
class Cycle implements Itest{
    @Override
    public void draw3() {
        System.out.println("必须重写");
    }

    @Override
    public void draw1() {
        System.out.println("可以重写也可以不重写");
    }
}

6.将接口想象成特殊的类,我们可以当作参数,接口不能实例化,但是可以new普通的类。

interface Ishape{
    void draw();
}
class Cycle implements IShape{
    @Override
    public void draw() {
        System.out.println("画⚪");
    }
}
class Rect implements IShape{

    @Override
    public void draw() {
        System.out.println("画矩形");
    }
}

class Flowers implements IShape{

    @Override
    public void draw() {
        System.out.println("画❀");
    }
}
public class Test3 {
    public static void Map(IShape iShape){
        iShape.draw();
    }
    public static void main(String[] args) {
        Cycle cycle=new Cycle();
        Map(cycle);
        Map(new Flowers());
        Map(new Rect());
    }

相当于 Ishape ishape=new Cycle();

            ishape.draw();实现创建对象,进行对接口的调用。

7. 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class

一个接口就是一个java文件

 

8.接口中不能有静态代码块和构造方法 

9.如果不想实现接口的方法,那么就将这个类定义成抽象类,但是如果这个类被其他类继承,那么必须重写。(这个和抽象类类似)

 


🎈接口使用(实现USB接口)

接口不能直接使用,必须要有一个 " 实现类 " " 实现 " 该接口,实现接口中的所有抽象方法。
public class 类名称 implements 接口名称{
// ...
}
注意:子类和父类之间是extends 继承关系,类与接口之间是 implements 实现关系
请实现笔记本电脑使用 USB 鼠标、 USB 键盘的例子
1. USB 接口:包含打开设备、关闭设备功能
2. 笔记本类:包含开机功能、关机功能、使用 USB 设备功能
3. 鼠标类:实现 USB 接口,并具备点击功能
4. 键盘类:实现 USB 接口,并具备输入功能
package Interfaces;

//USB接口
interface Iusb{
    void openDevice();
    void closeDevice();
}
//鼠标类
class Mouse implements Iusb{

    @Override
    public void openDevice() {
        System.out.println("打开鼠标");
    }

    @Override
    public void closeDevice() {
        System.out.println("关闭鼠标");
    }
    public void click(){
        System.out.println("点击鼠标");
    }
}

// 键盘类,实现USB接口
class KeyBoard implements Iusb {
    @Override
    public void openDevice() {
        System.out.println("打开键盘");
    }
    @Override
    public void closeDevice() {
        System.out.println("关闭键盘");
    }
    public void inPut(){
        System.out.println("键盘输入");
    }
}

// 笔记本类:使用USB设备
class Computer {
    public void powerOn() {
        System.out.println("打开笔记本电脑");
    }

    public void powerOff() {
        System.out.println("关闭笔记本电脑");
    }
    public static void useDevice(Iusb iusb){
        iusb.openDevice();
        if(iusb instanceof Mouse){
            Mouse mouse=(Mouse) iusb;
            mouse.click();
        }else if(iusb instanceof KeyBoard){
            KeyBoard keyBoard=(KeyBoard) iusb;
            keyBoard.inPut();
        }
        iusb.closeDevice();
    }

}
public class Test4 {

    public static void main(String[] args) {
        Computer computer = new Computer();
        //打开设备
        computer.powerOn();
        //使用鼠标设备
        computer.useDevice(new Mouse());
        //使用键盘设备
        computer.useDevice(new KeyBoard());
        //关闭设备
        computer.powerOff();
    }
}


🎈实现多个接口

Java 中,类和类之间是单继承的,一个类只能有一个父类,即 Java 中不支持多继承 ,但是 一个类可以实现多个接 。【单继承多接口】下面通过类来表示一组动物 .

一个类可以实现多个接口。使用implements用逗号隔开。【可以解决多继承的问题】


我们可以定义一个动物类,然后狗类继承了动物类,我们实现多个接口,有些动物是又会飞又会跑,又会游泳的。

interface Running{
    void run();
}
interface Flying{
    void fly();
}
interface Swimming{
    void swim();
}

我们实现多个接口,而不是实现多个类,因为java中只能继承一个类,而不能继承多个类,但是一个类可以实现多个接口。我们不需要创建一个swim类,fiy类,run类,然后里面有自己的swim,fly,run的成员方法,然后狗类一个一个继承,因为java中不能多继承,所以代码的重复,而实现多个接口正是可以解决java中的多继承问题,让一个狗类同时implement多个接口,然后重写方法,即可完成了一个类继承多个接口的方法。

❗各接口

interface Running{
    void run();
}
interface Flying{
    void fly();
}
interface Swimming{
    void swim();
}

❗Animal类和各种子类

class Animal{
    int age;
    String name;
    Animal(int age,String name){
        this.age=age;
        this.name=name;
    }
    void eat(){
        System.out.println("正在吃饭");
    }
}

class Dog extends Animal implements Running,Swimming{

    public Dog(int age, String name) {
        super(age, name);
    }
    @Override
    public void run() {
        System.out.println(name+"正在跑");
    }

    @Override
    public void swim() {
        System.out.println(name+"正在游泳");
    }
    public void eat(){
        System.out.println(name+"吃狗粮");
    }

}
class Bird extends Animal implements Flying{
    public Bird(int age, String name) {
        super(age, name);
    }

    @Override
    public void fly() {
        System.out.println(name+"正在飞");
    }

    public void eat(){
        System.out.println(name+"吃鸟粮");
    }
}
class Duck extends Animal implements Flying,Swimming,Running{

    public Duck(int age, String name) {
        super(age, name);
    }

    public void run() {
        System.out.println(name+"正在跑");
    }

    @Override
    public void swim() {
        System.out.println(name+"正在游泳");
    }

    public void fly() {
        System.out.println(name+"正在跑");
    }
    public void eat(){
        System.out.println(name+"吃鸭粮");
    }
}

测试方法

public class Test6 {
    public static void walk(Running running){
        running.run();
    }
    public static void func(Animal animal){
        animal.eat();
    }
    public static void main(String[] args) {
        walk(new Dog(13,"chq"));
        walk(new Duck(12,"chhh"));
    }
}

🎈接口间的继承

Java 中,类和类之间是单继承的,一个类可以实现多个接口, 接口与接口之间可以多继承 。即:用接口可以达到多继承的目的。
接口可以继承一个接口 , 达到复用的效果 . 使用 extends 关键字 .

就是说我们D1类是一个既能完成A1的行为,也能完成B1的行为,还能完成C1接口行为,然后我们接口C1继承了A1和B1的接口,其实C1继承A1和B1的行为,就相当于如果类实施了C1,那么这三个A1,B1,C1都是行动的。

接口间的继承相当于把多个接口合并在一起.

🎈接口使用实例 (给对象数组排序)

❗Animal类

class Student{
    public int age;
    public String name;

    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

❗运行 

这段代码是错误的,我们不管是按照什么来排序的,不管是年龄还是姓名都是错误的,我们可以来分析一下这段错误。

仔细思考 , 不难发现 , 和普通的整数不一样 , 两个整数是可以直接比较的 , 大小关系明确 . 而两个学生对象的大小关系怎么确定? 需要我们额外指定 .

🌈Comparable接口<比较>

让我们的 Student 类实现 Comparable 接口, 并实现其中的 compareTo 方法


✅按照年龄来比较(Comparable 接口, 并实现其中的 compareTo 方法)

class Student implements Comparable<Student>{
    public int age;
    public String name;

    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        if(this.age>o.age){
            return 1;
        }else if(this.age<o.age){
            return -1;
        }else{
            return 0;
        }
    }
}

当Student类实施了Comparable接口并且实现了compareTo方法,我们就可以根据年龄来进行比较。



我们可以看到底层,底层代码的比较是调用compareTo方法比较

 public static void main(String[] args) {
        Student student1=new Student(18,"chenle");
        Student student2=new Student(13,"zhang");
       if(student1.compareTo(student2)>0){
           System.out.println("student1>student2");
       }else {
           System.out.println("student2>student1");
       }
    }


🌈Comparator接口<比较器>

✅按照姓名来比较(实现Comparator接口,并实现compare方法)

我们是不能更改age的比较方法,那样势必回影响后面的运行,如果代码已经运行了一个月了,如果更改会影响很多。

所以我们需要利用 另外一个接口Comparator<T>以及接口里的compare方法

Comparator<T>接口中有很多方法,我们只需要compare抽象方法即可。



❗AgeCompare 

class AgeCompare implements Comparator<Student>{

    @Override
    public int compare(Student o1, Student o2) {
        return o1.age-o2.age;
    }
}

❗运行 

 public static void main(String[] args) {
        Student[] students=new Student[3];
        students[0]=new Student(18,"chenle");
        students[1]=new Student(13,"zhang");
        students[2]=new Student(9,"hao");
        AgeCompare ageCompare=new AgeCompare();
        Arrays.sort(students,ageCompare);
        System.out.println(Arrays.toString(students));
    }



❗NameCompare 

因为比较字符串的底层是利用Comparable接口中的compareTo方法

class NameCompare implements Comparator<Student>{

    @Override
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name);
    }
}

❗运行 

 public static void main(String[] args) {
        Student[] students=new Student[3];
        students[0]=new Student(18,"chenle");
        students[1]=new Student(13,"zhang");
        students[2]=new Student(9,"hao");
        NameCompare nameCompare=new NameCompare();
        Arrays.sort(students,nameCompare);
        System.out.println(Arrays.toString(students));
    }



我个人更推荐第二种,我们自己单独设置个类,然后我们直接利用比较器比较,如果比较整型直接return俩者相减即可,如果比较字符串,我们需要利用到compareTo方法进行比较。


🚩抽象类和接口的区别

核心区别 : 抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用 ( 不必重写 ), 接口中不能包含普通方法,只能是抽象方法 , 子类必须重写所有的抽象方法。
如之前写的 Animal 例子 . 此处的 Animal 中包含一个 name 这样的属性 , 这个属性在任何子类中都是存在的 . 因此此处的 Animal 只能作为一个抽象类 , 而不应该成为一个接口
再次提醒 :
抽象类存在的意义是为了让编译器更好的校验 , Animal 这样的类我们并不会直接使用 , 而是使用它的子类 .万一不小心创建了 Animal 的实例 , 编译器会及时提醒我们


祝你逃出苦难向春山。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值