Java——抽象类与接口

本文详细介绍了Java中的抽象类和接口,包括它们的定义、语法、特性、使用方法以及它们之间的区别。抽象类用于定义不能实例化的类,它可能包含抽象方法和普通方法,主要用于被继承。接口则定义了一组方法签名,实现接口的类必须实现接口中的所有抽象方法,接口支持多继承。此外,还讨论了Object类的基本功能,如toString()和equals()方法。
摘要由CSDN通过智能技术生成

今天的博客主要是来总结Java的抽象类和接口方面的知识。

目录

抽象类

什么是抽象类

抽象类语法

抽象类的特性

抽象类的作用

接口

什么是接口

接口的语法

接口的使用

接口的特性

怎么实现接口

接口之间的继承

抽象类和接口的区别

Object类

获取对象信息

对象比较的equals方法


抽象类

什么是抽象类

如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类

 在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来并不是所有的类都是用来描绘对象的。

抽象类语法

在Java中,一个类如果被 abstract 修饰则称这个类为抽象类,抽象类中被 abstract 修饰的方法成为抽象方法,抽象方法不用给出具体的方法体。

举例:

public abstract class Shape{
    // 抽象方法,被 abstract 修饰的方法,没有方法体
    abstract public void shape();
    abstract public void calcArea();

    // 抽象类也是类,所以也可以有普通方法
    public double getArea(){
        return area;
    }

    public double area;
}

抽象类的特性

下边出现的示例可以参考上边的举例!!!

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

// 编译出错(Shape 是抽象的,无法实例化)
Shape shape = new Shape();

2.抽象方法不是 private(如果是 private 的,就不能被覆写)

// 编译出错(非法的修饰符组合:abstract 和 private)
abstract class Shape{
    abstract private void draw();
}

3.抽象方法不能被 final 和 static 修饰(因为抽象方法要被覆写)

// 编译出错(非法的修饰符组合:final 和 abstract ; static 和 abstract)
public abstract class Shape{
    abstract final void methodA();
    abstract public static void methodB();
}

4.抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也是抽象类,必须要使用 abstract 修饰(@Override 为重写注解)

abstract class Shape{

    abstract public void shape();
    abstract public void calcArea();

    public double getArea(){
        return area;
    }

    public double area;
}
public class Text extends Shape{
    @Override
    public void shape(){
        System.out println("这是一个三角形");
    }
    @Override
    public coid calcArea(){
        System.out.println("这个三角形的面积为10平方厘米");
    }
}

5.抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类

6.抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量

抽象类是普通类的超集,抽象类中仍然可以存在普通方法和构造方法,且子类实例化时仍然满足继承的原则,先调用父类的构造方法再调用子类的构造方法。

抽象类的作用

        其实继承、重写方法这些普通类也可以实现,所以我们大可使用普通类来被继承,为什么还要用抽象类呢?确实,普通类也可以被继承,普通方法也可以被重写,但是被继承、方法被重写这些并不是抽象类的最大的作用。使用抽象类相当于多了一重编译器的校验,在实际操作中某些工作不应该有父类完成,而应该由子类完成,那么此时要是用成父类,就会出现错误,但是这种错误编译器是不会报错的,但是父类是抽象类的话在实例化的生活就会提示错误,这样我们就尽早发现了问题。所以使用抽象类是充分利用编译器的校验,这在实际开发中是非常有意义的。

接口

在生活中,我们几乎可以在任何地方看到接口,比如笔记本电脑上的USB口,电源插座,手机充电口等等……

什么是接口

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

接口的语法

接口的定义格式与普通类的定义格式几乎相同,只是把普通类中的 class 换成了 interface 关键字,这样就定义了一个接口。

与抽象类类似,接口内部的方法需要用 abstract 修饰,但是接口中不能存在普通方法和构造方法,当然这时候接口内部就只有抽象方法和全局变量,所以这个时候 abstract 关键字就可以省略。

在接口中 public 、abstract 、static 、final 统统可以省略

举例:

public interface 接口名称{
    // 下列方法中虽然有的方法没有被public 或者 abstract 修饰,但是其实还是有的,只不过是被省略了。
    // 在接口中方法的写法可以有以下几种,最推荐使用的是第四种,因为第四种写起来更加简洁。
    public abstract void method1();
    public void method2();
    abstract void method3();
    void method4();
}

接口的使用

接口不能直接使用,必须有一个类来实现接口,实现接口时要覆写接口中的所有抽象方法。

实现接口使用关键字 implements

格式:

public class 子类名称 implements 接口名称{
    // 类内部
}

注意:

  • 子类和父类之间是 extends 继承关系,类和接口之间是 implements 实现关系。
  • 众所周知,继承不能实现多继承 ,但是接口却可以被多重实现,所以我们可以使用接口来实现类的多继承。
  • 接口也可以使用 extends 来继承一个接口。
  • 接口不能使用 extends 继承类。
  • 如果一个子类同时要继承一个父类和实现一个接口,那么先使用 extends 继承父类,再使用 implements 实现接口。
  • 从JDK8开始,接口中也允许存在普通方法,接口中的普通方法使用 default 关键字定义,有方法体,子类实现接口之后可以直接使用接口中的普通方法。

接口的特性

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

// 出现错误,USB 是抽象的,无法实例化
public class Test{
    public static void main(String[] args){
        Usb usb = new Usb();    // USB 是一个接口
    }
}

2. 接口中的每一个方法都是 public 的抽象方法,即接口中的方法会被隐式的制定为 public abstract(只能是 public abstract ,其他的修饰符都会报错,因为抽象方法是要被覆写的,其他的修饰符权限没有 public 权限大)

public interface USB{
    // 出现错误,此处不允许使用修饰符 private
    private void openDevice();
    void closeDevice();
}

3.接口中的方法是不能在接口中实现的,只能由实现接口的类来实现

public interface USB{
    void openDevice();
    // 编译失败,因为接口中的方法默认为抽象方法
    // 出现错误,接口抽象方法不能有方法体
    void closeDevice(){
        System.out.println("关闭USB设备");
    }
}

 4.重写接口中的方法时,不能使用默认的访问权限

public interface USB {
    void openDevice(); // 默认是public的
    void closeDevice(); // 默认是public的
}
public class Mouse implements USB {
    @Override
    // 编译报错,重写USB中openDevice方法时,不能使用默认修饰符
    // 子类重写方法时权限不能低于本来的权限,这里默认的权限为 default 小于 public
    void openDevice() {
        System.out.println("打开鼠标");
    } 
} 

5. 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量

public interface USB{
    double brand = 3.0;
    void openDevice();
    void closeDevice();
}
public class Test{
    public static void main(String[] args){
        // 可以用过接口名访问,说明是 static 的
        System.out.println(USB.brand);
        // 出现错误,无法为最终变量 brand 分配值
        // 说明 brand 是final 的
        USB.brand = 2.0;
    }
}

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

public interface USB{
    // 编译失败
    public USB(){

    }
    // 编译失败
    static{

    }
    void openDevice();
    void closeDevice();
}

7.接口虽然不是类,但是接口编译之后的字节码文件的后缀仍然是 .class 

8.如果类没有实现接口中的所有抽象方法,则该类必须设置为抽象类

怎么实现接口

在上文中,我们提到了接口的多重实现,那么怎么进行接口的多重实现呢?下面我们来举例说明:

先定义一个动物类:

class Animal{
    protected String name;
    public Animal(String name){
        this.name = name;
    }
}

再定义一组接口,表示动物具有的能力:

// 会飞
interface IFlying{
    void fly();
}

// 会跑
interface IRunning{
    void run();
}

// 会游泳
interface ISwimming{

}

创建具体的类:

猫,会跑

class Cat extends Animal implements IRunning{
    public Cat(String name){
        super(name);
    } 
    @Override
    public void run(){
        System.out.println(this.name + "正在跑");
    }
}

鱼,会游泳

class Fish extends Animal implements ISwimming{
    public Fish(String name){
        super(name);
    }
    @Override
    public void swim(){
        System.out.println(this.name + "正在游泳");
    }
} 

青蛙,会跑,也会游泳

class Frog extends Animal implements IRunning,ISwimming{
    public Frog(String name){
        super(name);
    }
    @Override
    public void run(){
        System.out.println(this.name + "正在跑");
    }
    @Override
    public void swim(){
        System.out.println(this.name + "正在游泳");
    }
}

上面的青蛙就是一次实现了两个接口,所以要实现这两个接口中的所有抽象方法。

接口之间的继承

像是上边例子中的青蛙,既会跑又会游泳,他就要实现两个接口,我们可以把他整理一下,让他只实现一个接口就好了,那我们就要再创建一个接口,继承 IRunning 和 ISwimming。

interface IRunning{
    void run();
}
interface ISwimming{
    void swim();
}
interface IAmphibious extends IRunning ,ISwimming{
    
}
class Frog implements IAmphibious {
    // 实现 run() 方法和 swim() 方法
}

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

抽象类和接口的区别

抽象类和接口都是Java中多态的常见使用方式,我们应该对其进行重点掌握,并且明确这两者的区别。

核心区别:抽象类中可以包含普通方法和普通字段,这样的普通方法和字段可以被子类直接使用(不必重写),而接口中不能包含普通方法,子类必须重写所有抽象方法。

区别抽象类(abstract)接口(interface)
1结构组成普通类 + 抽象方法抽象方法 + 全局常量
2权限各种权限public
3子类使用使用 extends 关键字继承抽象类使用 implements 关键字实现接口
4关系一个抽象类可以实现若干个接口接口不能继承抽象类,但是接口可以使用 extends 关键字继承多个父接口
5子类限制一个子类只能继承一个抽象类

一个子类可以实现多个接口

Object类

既然说到抽象类和接口,那就顺便也介绍一下 Object 类,Object 类是 Java 默认提供的一个类,在 Java 中,除了 Object 类,所有的类都是存在继承关系的。默认都会继承 Object 父类。即 Object 类是所有类的父类,并且不需要显示 extends。

获取对象信息

如果要打印对象中的内容,可以直接重写 Object 类中的 toString() 方法。当将类的对象传入 pritln 方法进行输出时,默认就会调用 toString() 。

// Object 类中的 toString() 方法实现:
public String toString(){
    return getClass().getName() + "@" +Integer.toHexString(hashCode());
}

对象比较的equals方法

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

1.如果 == 左右两边是基本类型变量,比较的是变量中值是否相同;

2.如果 == 左右两边是引用类型变量,比较但是引用变量地址是否相同;

3.如果要比较对象中的内容是否相同,必须要重写 Object 中的 equals 方法,因为 equals 方法默认也是比较引用类型变量地址比较的,所以在使用 equals 方法时,要对 equals 方法进行重写。

// Object 类中的 equals方法
public boolean equals(Object obj){
    return (this == obj);
}

下面是举例:

// 进行比较时未重写 equals 方法
class Person{
    private String name ;
    private int age ;
    public Person(String name, int age) {
        this.age = age ;
        this.name = name ;
    }
}

public class Test {
    public static void main(String[] args) {
        Person p1 = new Person("gaobo", 20) ;
        Person p2 = new Person("gaobo", 20) ;
        int a = 10;
        int b = 10;
        System.out.println(a == b);     // 输出true
        System.out.println(p1 == p2);     // 输出false
        System.out.println(p1.equals(p2));     // 输出false
    }
}
// 进行比较时重写了 equals 方法
class Person{
    private String name ;
    private int age ;
    public Person(String name, int age) {
        this.age = age ;
        this.name = name ;

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false ;
        } if(this == obj) {
            return true ;
        } 
        if (!(obj instanceof Person)) {
            return false ;
        } 
    Person person = (Person) obj ; // 向下转型,比较属性值
    return this.name.equals(person.name) && this.age==person.age;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值