Java中的抽象类和接口

一、抽象类

1.1 引例

在学习多态时,有一段这样的代码

class Shape{
    public void draw(){
        System.out.println("画图形!");
    }
}
class Cycle extends Shape{
    public void draw() {
        System.out.println("⚪");
    }
}
class Rect extends Shape{
    public void draw() {
        System.out.println("▭");
    }
}
public class Main1 {
    public static void drawMap(Shape shape) {
        shape.draw();
    }
    public static void main(String[] args) {
        Cycle cycle=new Cycle();
        Rect rect=new Rect();
        drawMap(cycle);
        drawMap(rect);
    }
}

在这里插入图片描述
可以发现,在Shape类中的draw方法被重写后,体现出了不同对象的多态性,但是Shape类中的draw方法内容根本没有用了,那么内容可以不写,但是花括号得写;
对于上述示例,当我这个方法没有具体实现的时候,并且花括号都不想写时,这里可以使用abstract修饰该方法,这个方法就叫做抽象方法;包含抽象方法的类也必须使用abstract修饰,该方法就叫做抽象类
以下为示例代码中被abstract修饰的Shape类

abstract class Shape{
    public abstract void draw();
}

1.2 抽象类的概念

在面向对象的概念当中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。 如下:
在这里插入图片描述
说明:

1)矩形、圆形都是图形,因此和图形类是继承关系
2)虽然图形类存在draw方法,但因为图形类并不是具体的图形,因此其内部的draw方法是无法实现的
3)由于图形类没办法描述一个具体的图形,导致draw无法具体实现,因此可以将图形类设计为抽象类

1.3 抽象类语法

被abstract修饰的方法叫做抽象方法;包含抽象方法的类也必须使用abstract修饰,叫做抽象类

//抽象类Shape
public abstract class Shape{
    //抽象方法draw,没有方法体
    public abstract void draw();
    //抽象类可以有普通方法、构造方法、属性
    protected double area;//面积
    public double getArea(){
    	return area;
    }
}

抽象类也是类,内部可以包含普通方法和属性,甚至构造方法

1.4 抽象类的特性

  1. 包含抽象方法的类也必须使用abstract修饰,此时这个类叫做抽象类
  2. 抽象类不能被实例化(但可以向上转型、动态绑定、体现多态),只能被继承
//报错
Shape shape = new Shape(); 
  1. 当抽象类被继承时,子类必须要重写父类中的抽象方法,否则子类也是抽象类,必须要用abstract修饰
abstract class Shape{
    public abstract void draw();
    abstract void area();
}
class Cycle extends Shape {
	//重写
    public void draw() {
        System.out.println("⚪");
    }
	//重写
    public void area(){
    }
}
  1. 抽象方法不能被private、final,static修饰
  2. 抽象类和普通类的区别在于多了抽象方法、不能实例化
  3. 抽象类中不一定含有抽象方法,但含有抽象方法的类一定是抽象类
  4. 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量
abstract class School{
    public String name;
    public int year;

    public School(String name, int year) {
        this.name = name;
        this.year = year;
    }
}
class Student extends School{
    public Student(String name, int year){
        super(name, year);
    }
}

抽象类可以包含构造方法,这个构造方法并不是在实例化时使用(因为抽象类不能实例化对象),主要是在子类当中让子类调用,帮助父类进行初始化

1.5 抽象类的作用

抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法
使用抽象类相当于多了一重编译器的校验
在文件列表也可以发现,Shape和School类为抽象类(图标类似棒球)
在这里插入图片描述

二、接口

2.1 接口的概念

在现实生活中,接口的例子比比皆是,比如:笔记本上的USB口,电源插座
电脑的USB口上,可以插:U盘、鼠标、键盘…所有符合USB协议的设备
电源插座插孔上,可以插:电脑、电视机、电饭煲…所有符合规范的设备
通过上述例子可以看出:
接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。

2.2 语法规则

接口的定义格式与定义类的格式基本相同,将class关键字换成 interface 关键字,就定义了一个接口。

//定义一个名为Ishape的接口
interface IShape{
}

接口不能直接使用,必须要有一个“实现类”来“实现”该接口,实现接口当中的所有抽象方法

public class implements 接口名称{
     //...
}

注意:子类和父类之间是extends继承关系,类与接口之间是implements实现关系

2.3 接口特性

  1. 接口当中的方法如果没有被实现,那么它默认就是抽象方法
interface IShape{
//正确
    public void func();//abstract默认存在
//报错
    public void fun(){
    }
}
  1. 接口当中的方法不能有具体的实现,除非用default、static修饰的方法
interface IShape{
    public default void fun(){
        System.out.println("被default修饰的方法可以被实现");
    }
    public static void fun1(){
        System.out.println("被static修饰的方法可以被实现");
    }
}
  1. 在接口当中的成员变量,全部默认被public static final所修饰;抽象方法全部默认被public abstract所修饰
    为了保证代码的简洁性,定义成员变量时省略public static final;定义抽象方法时省略public abstract
    //等同int a=90;
    public static final int a=90;
    //等同于void fun();
    public abstract void fun();
  1. 接口不可以被实例化
		//报错,不可实例化
        IShape iShape=new IShape();
  1. 接口可以实现向上转型、动态绑定,从而实现多态
interface IShape{
	void fun2();
}
class Cycle implements IShape{
    @Override
    public void fun2() {
        System.out.println("⚪");
    }
}
class Rect implements IShape{
    @Override
    public void fun2() {
        System.out.println("□");
    }
}
public class text {
    public static void func(IShape iShape){
        iShape.fun2();
    }
    public static void main(String[] args) {
        IShape iShape=new Cycle();
        func(iShape);
        IShape iShape1=new Rect();
        func(iShape1);
    }
}

在这里插入图片描述
6. 接口也是可以产生字节码文件
在这里插入图片描述
7. 接口当中不能有构造方法和代码块(静态、实例),可以有静态方法
8. 如果类没有实现接口中的所有抽象方法,则类必须设置为抽象类
9. 一个类可以继承一个抽象类/普通类 同时还可以实现这个接口(顺序不能乱)

interface A{
}
abstract class B{
}
//一定要是先继承B再实现A
class C extends B implements A{
}

注意:

创建接口时,接口的命名一般以大写I开头
接口命名一般使用“形容词”词性的单词
阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性.

2.4 接口的简单使用

请实现笔记本电脑使用USB鼠标、USB键盘的例子:
1 USB接口:包含打开设备、关闭设备功能
2 笔记本类:包含开机功能、关机功能、使用USB设备功能
3 鼠标类:实现USB接口,并具备点击功能
4 键盘类:实现USB接口,并具备输入功能

// USB接口
interface USB {
    void openDevice();
    void closeDevice();
}
// 鼠标类,实现USB接口
class Mouse implements USB {
    @Override
    public void openDevice() {
        System.out.println("打开鼠标");
    }
    @Override
    public void closeDevice() {
        System.out.println("关闭鼠标");
    }
    public void click(){
        System.out.println("鼠标点击");
    }
}
// 键盘类,实现USB接口
class KeyBoard implements USB {
    @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("关闭笔记本电脑");
    }
    //USB usb=new Mouse或KeyBoard相当于向上转型
    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 TestUSB {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.powerOn();
        // 使用鼠标设备
        computer.useDevice(new Mouse());
        // 使用键盘设备
        computer.useDevice(new KeyBoard());
        computer.powerOff();
    }
}

2.5 实现多个接口

  • Java中为什么会引入接口的概念?
    比如有一个Animal类,其中动物可以有多个行为,如:跑,游泳,飞;但不是所有的动物都有以上几个行为,所以如果把上面几个行为写入一个类Animal中不合适;又因为Java不支持多继承,所以如果写入多个类中的话,一个Dog狗类就不可以继承多个类;并且,使用接口不必关心你是什么类型,只关心你是否具备该功能因此,这里引入了接口的概念,如下代码所示:
package pack7;
interface IFly{
    void fly();
}
interface ISwim{
    void swim();
}
interface IRun{
    void run();
}
//为什么要把这三个方法写在接口当中,而不是直接写到Animal这个类当中?
//因为不是所有的Animal都有接口当中的功能,如狗类,不能飞
abstract class Animal{
    public String name;

    public Animal(String name) {
        this.name = name;
    }
}
//狗是动物,并且能跑、游泳
//类只能继承一个,但是接口可以实现多个(Java不支持多继承)
class Dog extends Animal implements IRun,ISwim{
    //先实现构造方法
    public Dog(String name){
        super(name);
    }
    //重写抽象方法
    public void swim(){
        System.out.println(this.name+"正在游泳");
    }
    public void run(){
        System.out.println(this.name+"正在用四条腿跑");
    }
}
//并且使用接口不必关心你是什么类型,只关心你是否具备该功能
//如机器人不是动物类,但是也可以跑,这也是接口的重要功能
class Roboot implements IRun{
    public void run(){
        System.out.println("机器人正在跑");
    }
}

2.6 接口间的继承

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

interface IRunning {
	void run();
}
interface ISwimming {
	void swim();
}
// 两栖的动物, 既能跑, 也能游
interface IAmphibious extends IRunning, ISwimming {
}
//青蛙类实现IAmphibious接口,而IAmphibious接口多继承了IRunning和ISwimming接口
//等同于青蛙类实现了IAmphibious、IRunning、ISwimming这三个接口
class Frog implements IAmphibious {
	@Override
    public void run() {
    }
    @Override
    public void swim() {
    }
}

通过接口继承创建一个新的接口 IAmphibious 表示 “两栖的”. 此时实现接口创建的 Frog 类, 就继续要实现 run 方法, 也需要实现 swim 方法.
接口间的继承相当于把多个接口合并在一起

2.7 抽象类和接口的区别

抽象类和接口都是java中多态的使用方式
抽象类当中,可以包含和普通类一样的成员方法和成员变量,而接口当中的成员方法只能是public abstract修饰的,成员变量只能是public static final的
一个类只能继承一个抽象类,但可以同时实现多个接口,解决了Java中不能多继承的特性

2.8 Object类

Object类是所有类的父类,Java里除了Object类,所有类都是存在继承关系的。默认会继承Object类,即所有类的对象都可以使用Object的引用进行接收

package pack1;
class Animal{
}
class Dog extends Animal{
}
public class Main11 {
    public static void fun(Object obj){
        System.out.println(obj);
    }
    public static void main(String[] args) {
        fun(new Dog());
        fun(new Animal());
    }
}

在这里插入图片描述
其中Dog继承Animal,而Animal默认继承Object;fun函数实现了向上转型,且打印地址的哈希值

  • Object类有以下几个方法
    在这里插入图片描述
    发现Object类中有toString方法,而打印方法println方法内就包括toString,所以在任意类中可以重写toString方法
    在这里插入图片描述
class Dog {
    public String name="Dog";
    public String toString(){
        return name+"类";
    }
}
public class Main11 {
    public static void fun(Object obj){
        System.out.println(obj);
    }
    public static void main(String[] args) {
        fun(new Dog());
    }
}

在这里插入图片描述

2.9 对象比较equals方法

在Java中,==进行比较时:

  1. 如果==两侧是基本类型变量,比较的是变量中值是否相等
  2. 如果==两侧是引用类型变量,比较的是引用变量地址是否相同
  3. 如果要比较对象中的内容,必须重写Object中的equals方法,因为equals也是按照地址进行比较

例如我想比较两个对象之间的内容是否相同,不能简单的使用==来判断

class Person{
    public String name;
    public Person(String name) {
        this.name = name;
    }
}
public class Main2 {
    public static void main(String[] args) {
        Person p1=new Person("张三");
        Person p2=new Person("张三");
        System.out.println(p1==p2);
    }
}

结果为false,因为p1和p2都为对象的引用,所以比较的是地址,因为p1和p2都是分别不同实例化对象的引用,固然地址不一样;
对象内容的比较需要使用equals方法来解决,但是使用equals时需要注意,如下代码所示:

public class Main2 {
    public static void main(String[] args) {
        Person p1=new Person("张三");
        Person p2=new Person("张三");
        String s1="李四";
        String s2="李四";
        System.out.println("s1和s2比较结果为:"+s1.equals(s2));
        System.out.println("p1和p2比较结果为:"+p1.equals(p2));
    }
}

在这里插入图片描述
为什么p1和p2的结果是false呢?
因为p1是Person类对象的引用,而该类并没有对equals方法进行重写,调用的是Object类中的equals方法,而Object中的equals方法比较的还是地址:
在这里插入图片描述
而String类中对equals方法进行了重写,与Object类中equals不同,所以要想正确的比较出结果,需要在Person类中对equals方法进行重写

    public boolean equals(Object obj){
        Person tmp=(Person) obj;
        return tmp.name.equals(this.name);
    }

在这里插入图片描述
与上述equals方法类似的还有hashcode方法,可以让编译器自动重写改方法(与构造方法类似)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值