Java基础语法之抽象类和接口

抽象类

什么是抽象类

并不是所有的类都是用来描述对象的,这样的类就是抽象类

例如,矩形,三角形都是图形,但图形类无法去描述具体图形,所以它的draw方法无法具体实现,这个方法就可以没设计成抽象方法,这个类就是抽象类

抽象类语法

被abstract修饰的类是抽象类,abstract修饰的方法是抽象方法,抽象方法不用给出具体的实现体 

Java基础语法之抽象类和接口_第1张图片

如上,draw方法没有具体实现体,也就是不用写代码块

继承了抽象类的子类必须重写所有的抽象方法。如下:

Java基础语法之抽象类和接口_第2张图片

如何使用该抽象类及其子类

Java基础语法之抽象类和接口_第3张图片

这就是一种使用方法,在main函数中调用了Test中的静态方法draw(注意一定要是静态的,在静态方法中不可以调用非静态方法,因为非静态方法的调用依赖对象,而静态方法不依赖对象,所以静态方法默认没有this参数,也就无法调用非静态方法)

draw方法的参数是Shape类,而我们传参时传的是其子类,这里就发生了向上转型;

在draw方法中调用shape.draw()时,又会发生动态绑定;

总之这也是多态。

抽象类的特性

1.不能实例化本抽象类的对象

Java基础语法之抽象类和接口_第4张图片

但是可以用它实例化一个子类对象,也就是可以发生向上转型

因为向上转型后,对象的本质还是其子类,只不过用父类来接收了,并不代表实例化了一个父类对象。

2.抽象方法不可以是private权限,也不可以被final,static修饰

首先,子类重写方法的访问权限要大于等于父类;

其次,final修饰的方法是静态方法,static修饰的方法在方法区;

总之,被private,final,static修饰的方法不可以被重写

3.继承抽象类的子类必须重写其方法,否则子类也要被abstract修饰,然后它的继承者要将其父类以及父类的父类中所有的抽象方法重写

Java基础语法之抽象类和接口_第5张图片

4.抽象类中不一定有抽象方法,但由抽象方法的一定是抽象类

5.抽象类中也可以有普通方法和成员

但它里面的普通成员变量及方法只能通过子类对象来调用

Java基础语法之抽象类和接口_第6张图片

Java基础语法之抽象类和接口_第7张图片

其中,shape引用是发生了向上转型的,而reck就是一个子类的引用,由于有继承关系,所以可以通过子类引用来调用抽象类的方法

6.抽象类中可以有构造方法,供子类创建对象时初始化父类的成员变量,但注意,构造方法不能是抽象方法,因为构造方法不可以被重写

抽象类的作用

很多工作不应由父类完成,而应由子类来完成,但如果是一个普通类,用父类的引用去调用某些方法时就不会报错,而要是抽象类就会报错

就比如画图,你要是用父类shape来调用draw方法,如果不是抽象类就能正常编译,但由于图形有很多种,所以shape这个引用画不出具体的图形;而要是抽象类就会及时报错,提醒你调用具体的子类来画图

接口

什么是接口

接口就是公共的行为标准,大家在实现时,只要符号规范标准就可以通用。

具体点说,接口就是多个类的公共规范,是一种引用数据类型。

语法规则

接口要用interface关键字定义,内部是抽象方法;例如:

Java基础语法之抽象类和接口_第8张图片

接口的使用

有了接口就必须要有具体的类来使用这就要用到implements关键字

public class 类名 implements 接口名{

}

如果这个类还和其他类由继承关系,则应该:

public class 类名 extends 父类名 implements 接口名{

}   表示该子类继承了某个类并且还有某个功能

既然都有继承关系了,为什么还要有接口呢?一个父类可以引出多个子类,它们都是由共性的,但也有特性,这些特性就不适合写在父类里面,这时就可以提供接口,有什么功能就使用什么接口

举例:

Java基础语法之抽象类和接口_第9张图片

接口的特性

1.接口默认是被abstract修饰的,因为它里面有抽象方法

 interface USB等价于abstract interface USB

2.接口中的方法默认是被public abstract修饰的(也只能是这种权限,其他的都会报错)

void func();  等价于public abstract void func();

这也好理解,接口是一种公共标准,一定得是公开的抽象的

3.接口是一种引用类型,但不能直接new一个接口对象

4.接口中可以有成员变量,但这些变量默认是public static final修饰的。既然默认被final修饰了,那么在定义的时候就必须初始化

首先  int a=10;等价于public static final int a=10;

其次,不可以直接int a;必须初始化

5.接口中一般不可以有普通方法,但被static default修饰的方法除外

但注意:static void func(){}等价于public static void func(){},不可以改成除public的其他权限,default也一样

对于被static修饰的方法,可以直接用接口名调用,而default修饰的方法只能用子类对象调用或者被重写

例如:

package Demo2;
interface A{
    void Testa();
    static void Testb(){
        System.out.println("A 的static方法");
    }
    default void Testc(){
        System.out.println("A的default方法");
    }
}
class B implements A{
    @Override
    public void Testa() {
        System.out.println("B重写的testa方法");
    }

    @Override
    public void Testc() {
        A.super.Testc();
    }
}

public class Test {
    public static void main(String[] args) {
        A.Testb();
        B b=new B();
        b.Testa();
        b.Testc();
    }
}

用static修饰的方法可以在main中直接用接口名调用,但default方法要么被重写,要么用子类实例化的对象来调用

6.重写接口中的抽象方法时,只可以设置为public权限,因为接口中的抽象方法默认是public的,所以子类的方法权限要大于等于public

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

8.如果它的子类没有重写抽象方法,那这个类必须设置为抽象类

举例

abstract interface USB{
    void openDevice() ;

    void closeDevice();
}
class Mouse implements USB{
    @Override
    public void openDevice() {
        System.out.println("打开鼠标");
    }
    public void click(){
        System.out.println("疯狂点击鼠标");
    }
    @Override
    public void closeDevice() {
        System.out.println("关闭鼠标");
    }
}
class KeyBoard implements USB{
    @Override
    public void openDevice() {
        System.out.println("打开键盘");
    }
    public void input(){
        System.out.println("疯狂打字");
    }
    @Override
    public void closeDevice() {
        System.out.println("关闭键盘");
    }
}
class Computer {
    public void powerOn() {
        System.out.println("打开电脑");
    }
    public void powerOff() {
        System.out.println("关闭电脑");
    }
    public void useDevice(USB usb){
        usb.openDevice();

        if(usb instanceof Mouse){
            Mouse mouse=(Mouse) usb;
            mouse.click();
        }
        else if(usb instanceof KeyBoard){
            KeyBoard keyBoard=(KeyBoard) usb;
            keyBoard.input();
        }
        usb.closeDevice();

    }
}
public class Test {
    public static void main(String[] args) {
        Computer computer=new Computer();
        computer.powerOn();
        KeyBoard keyBoard=new KeyBoard();
        Mouse mouse=new Mouse();
        computer.useDevice(keyBoard);
        computer.useDevice(mouse);
        computer.powerOff();
    }
}

如上,电脑有usb接口,可以实现打开关闭键盘鼠标的操作

注意,电脑本身不需要usb接口来打开,所以电脑是一个独立的类,它通过自己的powerOn powerOff 方法来打开,然后它又有使用设备的方法,然后就是设备要通过usb接口来打开关闭,即usb.openDevice(); usb.closeDevice();但是在打开后,还要工作,可是只用USB类无法调用设备独有的功能,所以发生了向下转型,注意instanceof的判断和括号的类型强转

然后在main函数中,首先实例化一个电脑对象,将电脑打开,再实例化键盘和鼠标,然后就是电脑要开始使用设备了,调用useDevice函数(这里发生了向上转型,因为是用USB这个引用去接收各种需要USB的对象)。最后就关闭电脑

实现多个接口

在Java中,类和类之间只能单继承,一个类只能有一个父类,但是一个类可以有多个特殊的功能,即一个类可以有多个接口

class 类名 implements 接口1,接口2

例如:青蛙会跑也会游泳,鸭子会跑会游泳也会飞,如下

class Animal{
    String name;
    int age;
    public void eat(){
    }

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
 interface Running{
    public abstract void run() ;
}
interface Swimming{
    public abstract void swim();
}
interface Flying{
    public abstract void fly();
}
class Dog extends Animal implements Running{
    public Dog(String name, int age) {
        super(name, age);
    }
    @Override
    public void run() {
        System.out.println("正在用囧囧跑步步");
    }

    @Override
    public void eat() {
        System.out.println("吃狗粮");
    }
}
class Flog extends Animal implements Running,Swimming{
    @Override
    public void run() {
        System.out.println("正在用俩只大脚掌跑步步");
    }
    @Override
    public void swim() {
        System.out.println("正在用那小手手游泳");
    }
    public Flog(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("吃蛙粮");
    }
}
class Duck extends Animal implements Running,Flying,Swimming{
    @Override
    public void run() {
        System.out.println("正在小跑");
    }
    @Override
    public void swim(){
        System.out.println("正在划水");
    }
    @Override
    public void fly() {
        System.out.println("正在扇翅膀飞飞");
    }
    public Duck(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("正在吃鸭粮");
    }
}

这就是一个典型的例子

那如何使用呢?

Java基础语法之抽象类和接口_第10张图片

在main函数中可以调用这些static修饰的方法。在=========的上方,是多态的典型例子,将子类对象给到父类,再发生动态绑定,调用重写的方法,

在============的下方就是在使用接口了。注意public static void run(Running run),这个方法的参数是Running这个引用类型,当有此功能的对象给到这个引用类型时,就是发生了向上转型,所以在run.run()时,表面上是调用了接口的抽象方法,实际上是在调用子类的重写的方法

其实,只要是有这个功能的类,就可以实现这个接口

Java基础语法之抽象类和接口_第11张图片

接口间的继承

在Java中,类和类之间是单继承的,但接口与接口可以多继承

package Demo4;
interface A {
    void testA();
}

interface B {
    void testB();
}
interface C extends A,B{
    void testC();
    void testA();
}
class TestDemo1 implements C {
    @Override
    public void testC() {

    }

    @Override
    public void testA() {

    }

    @Override
    public void testB() {

    }
}

public class Test {
}

接口举例

comparable

想要比较俩个学生的大小

Java基础语法之抽象类和接口_第12张图片

单纯这样是不行的,我们要指定如何比较,拿什么比较,这就要用到comparable接口

Java基础语法之抽象类和接口_第13张图片

这是一个接口,尖括号里面的T是要比较的数据的类型,如果要比较student,就在里面写student。这个接口里面有一个抽象方法,所以我们要重写这个抽象方法;以用年龄比较为例

Java基础语法之抽象类和接口_第14张图片

Java基础语法之抽象类和接口_第15张图片

注意,尖括号不能省略

但是,如果需求改变,又想用姓名比较,我们就又得把重写的方法改了,而不可以再写一个重写方法(抽象方法只可以被重写一份)很麻烦

所以就有了下面的比较器

comparator

Java基础语法之抽象类和接口_第16张图片

它里面有很多抽象方法,但我们只用到了比较,所以重写比较即可

先看例子

Java基础语法之抽象类和接口_第17张图片

这是年龄比较器,那要是名字比较器呢?不能直接返回o1.name-o2.name,因为name是String类型,不可以相加减。这时就需要用到String下面的compareTo方法

Java基础语法之抽象类和接口_第18张图片

Java基础语法之抽象类和接口_第19张图片

使用的时候就要实例化比较器这个对象

comJava基础语法之抽象类和接口_第20张图片

上面这个是String类下面的compareTo方法,其实是String类使用了comparable接口,所以重写了comepareTo方法

总结

comparable对类的侵入性比较强,是要比较哪个类,就要让哪个类implements它,是在类里面重写方法。一旦写死了,就只可以用这一种比较方法

comparator更加灵活,只要传入要比较的对象即可,只不过注意要实例化比较器对象因为comparator这是一个接口,里面的抽象方法无法调用,只有放到类里面才能发挥作用,需要我们按自己的意愿重写

再例如

Java基础语法之抽象类和接口_第21张图片

我们可以这样对整型数组排序,那能否对学生数组排序呢?

Java基础语法之抽象类和接口_第22张图片

显然是不可以的,来看一下源码

Java基础语法之抽象类和接口_第23张图片

这里用到了Comparable接口,即将俩个对象强转为了comparable类型,所以要想成功排序,Student类就要使用这个接口,并且重写这个comepareTo方法

Java基础语法之抽象类和接口_第24张图片

然后就可以正常排序了,但最好再自己重写一个toString方法,要不然就会出现上面的打印情况(上面这个最终是调用的object类的toString方法,而object类是所有子类的父类,所以当我们再Student类中重写了方法后就会调用我们自己的方法),如下

其实,sort有很多重载方法,比如下面这个:

Java基础语法之抽象类和接口_第25张图片

它是有俩个参数,其中一个是要排序的数组,另一个就是Comparator这个接口(但不是说要传这个接口,而是要传实现这个接口的类型的引用)

那么我们就可以如下这种方式来排序

Java基础语法之抽象类和接口_第26张图片

总结:

既然对于整型数组可以直接用arrays.sort排序,那就说明一个事实,Integer这个类一定implements了comparable这个接口,我们可以看一下源码:

自己写一个排序方法

Java基础语法之抽象类和接口_第27张图片

因为排序不仅仅是为一个类提供,而是为多个类提供,所以参数要设置成Comparable类的数组,这样的话,只要是实现了这个接口的类,都可以用到这个排序方法。注意,里面的交换语句其实交换的是引用的指向(相当于C语言中指针的指向)。

你可能感兴趣的:(java,开发语言)