零基础 “入坑” Java--- 十二、抽象类和接口

文章目录

一、抽象类

1.何为抽象类

在面向对象的思想中,所有的对象都是通过类来描述的;但是,并不是所有的类都是用来描述对象的,当一个类中没有包含足够的信息来描述一个具体的对象时,我们就将这样的类称为抽象类。

我们之前经常使用到的Animal类就可以被设计为一个抽象类。

2.抽象类语法

在Java中,被abstract修饰的类称为抽象类;被abstract修饰的方法称为抽象方法,抽象方法不用给出具体的实现(我们以Animal类为例):

java 复制代码
//抽象类
abstract class Animal {
    public String name;
    public int age;
    //抽象方法
    abstract public void eat();
}

注意:

1.抽象类也是类,在抽象类中可以包含普通成员方法和普通成员变量以及构造方法。
2.抽象类的构造方法依靠子类的调用,帮助抽象类初始化成员。
3.当一个类中有抽象方法时,那么该类一定为抽象类。
4.抽象类中不一定有抽象方法。

3.抽象类特性

当我们定义完一个抽象类之后,我们想去使用,此时我们尝试使用抽象类实例化一个对象:

java 复制代码
    public static void main(String[] args) {
        Animal animal = new Animal();
    }

在编写代码时程序会报错,编译器会提示我们抽象类不能被实例化。

一个类不能被实例化,那么这个类是干什么的呢?抽象类就是为了被继承的:

java 复制代码
class Dog extends Animal {
    
}

当我们定义一个狗类继承动物类时,此时代码会报错,这又是为什么呢?

当一个普通的类继承抽象类之后,在普通类中一定要重写抽象类中所有的抽象方法:

java 复制代码
class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("吃狗粮");
    }
}

此时,代码就可以正常运行。

对于抽象类我们还需要知道:抽象方法不能被final、static和private修饰,因为抽象方法在子类中一定要被重写。

当一个普通类A继承了抽象类B之后,不想重写B中的抽象方法,可以将普通类A也设计为抽象类;但是当另一个普通类C继承抽象类A之后,需要重写A和B中所有的抽象方法。


使用抽象类时,依旧可以发生向上转型和动态绑定

java 复制代码
abstract class Animal {
    public String name;
    public int age;
    abstract public void eat();
}
class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("吃狗粮");
    }
}
class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("吃猫粮");
    }
}
public class Test {
    public static void test(Animal animal) {
        animal.eat();
    }
    public static void main(String[] args) {
        //向上转型
        Animal animal = new Dog();
        Animal animal1 = new Cat();
        test(animal);
        test(animal1);
        test(new Dog());
    }
}

在传递参数的时候,我们也可以使用匿名对象,如"new Dog()";但是,匿名对象也有缺点:每次使用都需要重新实例化。

4.抽象类作用

抽象类并不能被实例化,主要就是用来被继承的,那抽象类的存在有什么意义呢?

当我们使用抽象类时,就相当于增加了一重编译器的校验,通过编译器提示可能出现的问题,帮助我们尽早发现。

抽象类存在的意义就是为了让编译器进行更好的校验,如:像Animal这样的类我们并不会直接使用,而是使用其子类,但万一我们创建了一个Animal的实例,编译器就会及时提醒我们。

二、接口

1.何为接口

接口在我们的生活中随处可见,笔记本电脑上的USB接口,墙上的电源接口等等。在Java中,也存在接口的概念:在Java中,接口可以理解为多个类的公共规范,是一种引用数据类型。

2.接口语法及特性

接口的语法定义格式与类的语法定义格式基本相同,只需将class换为interface关键字即可。

java 复制代码
interface IAnimal {
    
}

此时,我们就定义了一个接口。

我们想在接口中添加一些变量和方法:

java 复制代码
interface IAnimal {
    int a = 10;
    void test();

    public static final int b = 20;
    public abstract void test1();
}

以上这两种写法都是可以的:

1.接口中的成员变量默认为"public static final"修饰的,可省略不写。
2.接口中的成员方法默认为"public abstract"修饰的,可省略不写。

原则上来说,在接口中不可以存在普通的成员方法

java 复制代码
interface IAnimal {
    //error
    public void test2() {
        
    }
}

但是,从Java8开始,允许在接口中定义一个default方法,此方法可以有具体的实现

java 复制代码
interface IAnimal {
    default public void test2() {
        System.out.println("嘻嘻");
    }
}

在接口中,被static修饰的方法,也可以有具体的实现

java 复制代码
interface IAnimal {
    public static void test3() {
        System.out.println("嘿嘿");
    }
}

接口不能通过new来实例化对象

java 复制代码
    public static void main(String[] args) {
        //error
        IAnimal iAnimal = new IAnimal();
    }

类和接口之间,可以通过implements关键字来实现接口

java 复制代码
class Dog implements IAnimal {
    
}

但此时代码会报错,这是因为接口中存在抽象方法,我们需要对抽象方法进行重写

java 复制代码
class Dog implements IAnimal {
    @Override
    public void test() {
        
    }
    @Override
    public void test1() {

    }
}

此时,代码就可以正常运行。


在使用接口时,我们还需注意:

1.接口也可以发生向上转型和动态绑定。
2.重写接口中的抽象方法时,必须使用public修饰,因为重写的 方法的 访问权限 不能比 被重写的 方法的 访问权限低。
3.接口中不能存在静态代码块和构造方法。
4.一个接口也会产生独立的字节码文件(×××.class)。
5.当一个类没实现接口中所有的抽象方法时,该类一定为抽象类。

6.父类和子类之间是继承关系(extends),类和接口之间是实现关系(implements)。

三、接口使用

接口不能直接使用,需要有一个类来实现接口,并重写接口中所有的抽象方法。


我们举个例子来熟悉一下接口的使用:

定义一个接口:USB,接口中包含打开设备功能,关闭设备功能。

定义一个鼠标类:实现USB接口,并具备点击功能。

定义一个键盘类:实现USB接口,并具备输入功能。

定义一个笔记本类:包含开机,关机及使用USB设备功能。

根据题意,我们先定义一个接口:

java 复制代码
public interface IUSB {
    void openDevice(); //打开设备
    void closeDevice(); //关闭设备
}

定义一个鼠标类实现接口(注意:重写抽象方法):

java 复制代码
public class Mouse implements IUSB {
    @Override
    public void openDevice() {
        System.out.println("打开鼠标");
    }
    @Override
    public void closeDevice() {
        System.out.println("关闭鼠标");
    }
    public void click() {
        System.out.println("点击功能");
    }
}

定义一个键盘类实现接口(注意:重写抽象方法):

java 复制代码
public class Keyboard implements IUSB {
    @Override
    public void openDevice() {
        System.out.println("打开键盘");
    }
    @Override
    public void closeDevice() {
        System.out.println("关闭键盘");
    }
    public void inPut() {
        System.out.println("输入功能");
    }
}

定义一个笔记本类,包含开机关机功能:

java 复制代码
public class Computer {
    public void open() {
        System.out.println("打开笔记本");
    }
    public void close() {
        System.out.println("关闭笔记本");
    }
    public void use(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 static void main(String[] args) {
        Computer computer = new Computer();
        computer.open();
        computer.use(new Mouse());
        computer.use(new Keyboard());
        computer.close();
    }
}

运行结果为:

四、实现多个接口

在Java中,类和类之间是不支持多继承的,但是一个类可以实现多个接口。

我们举个例子来说明一下:

定义三个接口,分别表示三种功能:飞、跑、游:

java 复制代码
public interface IFlying {
    void fly();
}
java 复制代码
public interface IRunning {
    void run();
}
java 复制代码
public interface ISwimming {
    void swim();
}

再定义一个动物类,定义为抽象类,因为类中需要有被重写的eat抽象方法:

java 复制代码
abstract public class Animal {
    public String name;
    public int age;
    public abstract void eat();
    public Animal(String name) {
        this.name = name;
    }
}

再定义一个狗类,继承动物类,并实现跑和游的接口:

java 复制代码
public class Dog extends Animal implements IRunning,ISwimming {
    public Dog(String name) {
        super(name);
    }
    @Override
    public void eat() {
        System.out.println(name + "吃狗粮");
    }
    @Override
    public void run() {
        System.out.println(name + "小狗跑");
    }
    @Override
    public void swim() {
        System.out.println(name + "小狗游");
    }
}

在狗类中,我们要重写继承和实现的所有抽象方法。

再定义一个鸟类,继承动物类,并实现跑和飞的接口:

java 复制代码
public class Bird extends Animal implements IFlying,IRunning {
    public Bird(String name) {
        super(name);
    }
    @Override
    public void eat() {
        System.out.println(name + "吃鸟粮");
    }
    @Override
    public void fly() {
        System.out.println(name + "小鸟飞");
    }
    @Override
    public void run() {
        System.out.println(name + "小鸟跑");
    }
}

同样,在鸟类中,我们要重写继承和实现的所有抽象方法。

我们再给一个测试类,用来测试写过的代码:

java 复制代码
public class Test {
    public static void test1(Animal animal) {
        animal.eat();
    }
    public static void test2(IRunning iRunning) {
        iRunning.run();
    }
    public static void test3(IFlying iFlying) {
        iFlying.fly();
    }
    public static void test4(ISwimming iSwimming) {
        iSwimming.swim();
    }
    public static void main(String[] args) {

    }
}

此时,代码的使用就会非常灵活:

java 复制代码
    public static void main(String[] args) {
        Dog dog = new Dog("小狗");
        Bird bird = new Bird("小鸟");
        test1(dog);
        test1(bird);
        System.out.println("===========");
        test2(dog);
        test2(bird);
        System.out.println("===========");
        //test3(dog); error:没实现此接口
        test3(bird);
        System.out.println("===========");
        test4(dog);
        //test4(bird); error:没实现此接口
    }

运行结果为:

对于test2,test3,test4方法,方法的参数为接口类型,当调用方法时,只要传递的参数实现了这个接口,就可以使用。因此,我们还可以这样使用接口:

我们再定义一个车类,实现跑的接口:

java 复制代码
public class Car implements IRunning {
    @Override
    public void run() {
        System.out.println("小车跑");
    }
}

此时,在测试类中调用test2方法,并将实例化的小车作为参数进行传递,代码可以正常运行:

java 复制代码
    public static void main(String[] args) {
        test2(new Car()); //小车跑
    }

当使用了接口时,就无需关注类的类型,只需考虑类是否实现了接口,具不具备接口中的功能即可,大大提高了代码的灵活性。

五、接口间的继承

在Java中,类和类之间不能是多继承关系的,但是接口和接口之间可以实现多继承,即可以使用接口达到多继承的目的。

接口使用 extends关键字 拓展(继承)一个接口,从而达到代码复用的效果:

java 复制代码
interface A {
    void testA();
}
interface B {
    void testB();
}
interface C extends A,B {
    void testC();
}
public class Test implements C {
    @Override
    public void testA() {

    }
    @Override
    public void testB() {

    }
    @Override
    public void testC() {

    }
}

在上面这个例子中,接口C继承了接口A和接口B,表示C这个接口具备了B接口和A接口的功能 ,当一个普通类实现C接口时,需要重写A、B、C中所有的抽象方法

六、抽象类和接口的区别

二者的核心区别在于:抽象类中可以包含普通方法和普通变量,而接口中不能包含普通方法。


Ending。

相关推荐
basketball61611 分钟前
Linux C 信号操作
linux·c语言·开发语言
Kiri霧12 分钟前
Kotlin比较接口
android·java·前端·微信·kotlin
阿华的代码王国22 分钟前
【Android】EditText使用和监听
android·xml·java
PythonicCC30 分钟前
Python高级数据类型:字典(Dictionary)
开发语言·python
Kiri霧36 分钟前
Kotlin属性重写
android·开发语言·kotlin
皮卡蛋炒饭.1 小时前
初识C++——开启新旅途
开发语言·c++
菜还不练就废了1 小时前
7.19 Java基础 | 异常
java·开发语言
GalaxyPokemon2 小时前
全局变量与局部变量的对比
开发语言·c++
Xxtaoaooo2 小时前
手撕Spring底层系列之:注解驱动的魔力与实现内幕
java·开发语言·后端开发·spring框架·原理解析
街霸星星2 小时前
使用 vfox 高效配置 Java 开发环境:一份全面指南
java