JavaSE-- 抽象类和接口(上)

本文详细介绍了抽象类和接口在Java中的概念、使用特性及规则。抽象类是一个自下而上的抽象,提供通用实现,子类必须重写所有抽象方法;接口则是自上而下的规范,定义行为标准,不允许实例化且方法默认为public抽象。两者在多继承方面有所不同,类只能单继承,但可以实现多个接口。通过实例和代码演示,阐述了抽象类与接口的使用场景和约束。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

抽象类和接口(上)

一丶抽象类

什么是抽象类?
顾名思义,首先抽象类它是一个类,那么抽象怎样来理解呢?我们对抽象的定义就是难以理解的,难以用言语形容的。所以依据这里,给出了如下的定义:

在类中没有足够的信息来描绘一个具体的对象,这样的类就叫抽象类。

再具体:

抽象类是一个自下而上的抽象过程,其提供了通用实现,是事物的抽象。我们在实现类的过程中,可以发现有些类有几乎相同的实现,我们把这些几乎相同的实现抽取出来成为抽象类。然后如果有一些差异,那么我们支持抽象方法自定义实现。

写法

抽象类的写法很是简单(具体如下):

public abstract class Persion {//使用abstract关键字来定义一个抽象类
    public String name;
    public int age;

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

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

    abstract void eat();//使用abstract关键字来定义抽象方法

    abstract public void body();
}

在这里,我定义了一个“人”这样一个抽象类。我们每个人都会使用两条腿进行奔跑,但是由于南北方的差异和气候环境,人们的饮食习惯,还有身材比例各不相同,所以这里用抽象方法仅仅声明有这个特性。

特性

我们定义抽象类肯定是有许多的规则,具体有哪些,接下来进行分析。

1. 抽 象 类 不 能 直 接 实 例 化 对 象 \color{red}{1.抽象类不能直接实例化对象} 1.

先放代码:
在这里插入图片描述可以看到,这里我尝试定义一个Persion的对象,但是报出来了一个错误:Persion是抽象的,不能被实例化。

2. 抽 象 方 法 不 能 是 p r i v a t e 的 \color{red}{2. 抽象方法不能是 private 的} 2.private

在继承关系当中,虽然子类也继承了父类的私有制方法,但是它不能进行访问,所以当把方法定义为private的时候,这个时候这个方法不能被抽象类的子类进行重写。同理: 抽 象 方 法 不 能 是 s t a t i c 、 p r i v a t e , f i n a l 修 饰 的 \color{blue}{抽象方法不能是static、private,final修饰的} staticprivatefinal
接下来进行验证:
首先,我定义一个“南方人类”和“北方人类”。

public class SouthPeople extends Persion{//定义南方人类
    public SouthPeople(String name, int age) {
        super(name, age);
    }

    @Override
    void eat() {
        System.out.println(name + "吃面条");
    }

    @Override
    public void body() {
        System.out.println(name + "身体小巧");
    }
}

public class NorthPeople extends Persion{//定义北方人类
    public NorthPeople(String name, int age) {
        super(name, age);
    }

    @Override
    void eat() {
        System.out.println(name + "吃面条");
    }

    @Override
    public void body() {
        System.out.println(name + "身体小巧");
    }
}

那么,接下来我们对抽象方法进行测试,看看它会有哪些错误。

1. 关 于 权 限 修 饰 符 问 题 \color{violet}{1.关于权限修饰符问题} 1.
首先是对于private,很明显报了错误,说abstract和private不能同时出现

在这里插入图片描述在这里插入图片描述
再接着对于default 和不写
在这里插入图片描述
这里可以看出,不写和default是两码事,不写可以,但是default就不行。
这是因为:
抽 象 方 法 不 加 权 限 修 饰 符 的 时 候 默 认 是 p u b l i c \color{blue}{抽象方法不加权限修饰符的时候默认是public} public

然后最后对于protect
在这里插入图片描述

此刻并没有报错,所以是可以的。
2. 关 于 s t a t i c 问 题 \color{violet}{2.关于static问题} 2.static
如果给一个方法加上了static,那么就是静态方法,此时这个方法就是属于类的行为,不是对象的。
在这里插入图片描述
如果加上了static,那么编译器会报错,static和abstract不能同时出现。

3. 子 类 要 重 写 抽 象 类 中 所 有 的 抽 象 方 法 \color{red}{3.子类要重写抽象类中所有的抽象方法} 3.

首先,抽象类一定要被继承,并且子类要重写抽象类中所有的抽象方法
这里用“北方人类”进行测试。
在这里插入图片描述
我这里去掉了NorthPeople{ }中的body()抽象方法,这里出现了异常:NorthPeople必须被声明为抽象类或者重写Persion{ }类中的body()方法。
也就是说:
在这里插入图片描述
如果子类没有重写抽象类中所有的抽象方法,那么这个子类也要被声明为抽象类

好处

作为抽象类,子类要对父类方法进行重写,但是如果我不用抽象类,子类也可以对父类方法进行重写,并且对应的功能我也有,那使用抽象类的意义在哪里呢?

其实就相当于多了一层代码的效验器。

具体解释如下:
如上所示,很多工作是需要子类去完成的,而不是父类,如果这个时候我们不使用抽象类的时候,错误的使用了父类,那么此时编译器不会报错。但如果是抽象类的话那么就会报错。
所以:

使用抽象类的意义就在于效验,和final的使用意义相近,final是为了防止用户误
修改,而abstract是为了防止错误的使用。

二丶接口

什么是接口?
接口在生活中随处可见,USB接口,公牛插座接口,手机充电线接口…
那么在JAVA中,什么是接口?

接口是自上而下的抽象过程,其对某些行为进行了规范,是行为的抽象,可以看成是一种引用数据类型。如果说,我需要某一行为,那么我就去实现相对应的接口,但是具体怎么实现,由我自己说了算。

写法

接口的写法和抽象类类似,但是把abstract关键字换成了interface。这里呢,我设计了一个Sing{ }接口,来对此进行讲解。

public interface Sing {//定义一个Sing接口
    void sing();
}

这里的话对于写法进行规范:

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

使用

就像定义中说的,我们需要某个行为,那么我们就去实现相对应的接口。
在这里插入图片描述
在这里插入图片描述
那么接下来,我们就创建一个对应的对象,来对上述内容进行验证(这里补全上面的代码):

public class TestAbstract {//这是验证抽象类和接口的
    public static void main(String[] args) {
        NorthPeople p1 = new NorthPeople("北方人张三",18);
        p1.eat();
        p1.sing();
        SouthPeople p2 = new SouthPeople("南方人李四",18);
        p2.eat();
        p2.sing();
    }
}

运行结果如下:
在这里插入图片描述

特性

1. 不 能 直 接 n e w 接 口 的 实 例 \color{red}{1.不能直接new接口的实例} 1.new

什么意思?就是说:

虽然接口是一种引用数据类型,但是不能直接new出他的对象。

代码如下:

在这里插入图片描述
这里报错,Sing是抽象的,不能直接实现。

2. 关 于 接 口 的 修 饰 符 \color{red}{2.关于接口的修饰符} 2.

接口中每一个方法都是public的抽象方法, 即接口中的方法会被隐式的指定为 public abstract(只能是public abstract,其他修饰符都会报错)
在这里插入图片描述

在这里插入图片描述

然后来看一下正确的形式:
在这里插入图片描述
这里的话public abstract是灰色的,意味着他们其实都是可以省略的
在这里插入图片描述

2. 关 于 接 口 的 成 员 属 性 \color{red}{2.关于接口的成员属性} 2.

接口中不能有成员变量,只能有常量,如果说要定义一个变量,那么必须加上 public static final对它常量化!!
具体如下:
在这里插入图片描述
一旦定义了之后,不能修改!!因为被final化了。

3. 接 口 抽 象 方 法 不 能 有 方 法 体 \color{red}{3.接口抽象方法不能有方法体} 3.

这里是什么意思呢?就是说,接口定义的方法如果是抽象方法,那么这个方法不能有方法体,相对应的实现应该在子类中完成。
在这里插入图片描述
编译器报错如上:接口抽象方法不能有方法体
再接着继续,如果说想要有方法体,那么就不能是抽象方法,这个时候我们就要修改,上面我们说了: 接 口 中 抽 象 方 法 的 修 饰 符 必 须 是 p u b l i c a b s t r a c t , 那 么 我 们 就 要 接口中抽象方法的修饰符必须是public abstract, 那么我们就要 publicabstract 把 他 改 为 d e f a u l t 把他改为default default
这 里 注 意 , 是 d e f a u l t , 不 是 不 写 ! 不 是 不 写 ! 不 是 不 写 ! ! \color{red}{这里注意,是default,不是不写!不是不写!不是不写!!} default,

具体如下:
在这里插入图片描述
那么这样定义的意义在哪里呢?

很多时候,我们可能要对抽象方法进行修改,但是呢一旦新增或者减少抽象方法那
么就要对所有其下的子类进行修改,如果子类少了还好说,一旦多了之后,那么无
疑是一个很繁琐的工程,这个时候,我们就要用到default定义的非抽象方法,因
为是非抽象方法,所以子类不需要对该方法进行重写的同时又可以访问到该方法。

5. 接 口 编 译 后 字 节 码 文 件 的 格 式 是 . c l a s s \color{red}{5. 接口编译后字节码文件的格式是.class} 5..class

关于这一点的验证首先我们运行相对应的代码。
然后打开对应项目下的out文件夹,然后找到对应的.class文件。

在这里插入图片描述
在这里插入图片描述

6. 重 写 接 口 的 方 法 时 不 能 用 d e f a u l t 修 饰 \color{red}{6.重写接口的方法时不能用default修饰} 6.default

在这里插入图片描述
这里的话,应该是说子类修饰符权限要大于等于父类。
在这里插入图片描述
这里意思是说:此修饰符仅仅可以在接口中被使用

其实不仅仅是default,因为接口中的抽象方法修饰符一般是public,所以呢,子类的修饰符也只能public了。

7. 接 口 中 不 能 有 静 态 代 码 块 和 构 造 方 法 。 \color{red}{7. 接口中不能有静态代码块和构造方法。} 7.

首先, 接 口 不 是 类 ! ! ! \color{red}{接口不是类!!!} ,所以不能有构造方法,因为它想初始化成员变量也没有变量让他初始化呀。然后对于静态代码块,静态代码块初始化成员变量,这一点和上面同理。

先是静态代码块:
在这里插入图片描述

再接着对于构造代码块:
在这里插入图片描述

多继承

接口允许多继承,但是类只能单继承,为什么?

如果说类之间运行多继承,那么当两个父类其中的某种方法完全相同时,子类不知
道要对那个父类的方法进行重写,但是如果是接口就没有这种顾虑,因为接口没有
方法体,不论写哪一个都不会冲突,不会造成额外影响!

一个类可以继承多个接口,那么,我们创建一个新的包来对此进行验证。

public class Animal {//这是父类动物类,所有动物共用的模板

    public String name;
    public int age;
    public String gender;

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


public interface Running {//接口跑:定义跑的行为
    void run();
}

public interface Jump {//接口跳:定义跳这个行为
    void jump();
}

public interface Swim {//定义游泳这个行为
    void swim();
}

接下来定义具体的动物类:

public class TianE extends Animal implements Running,Jump,Swim{//天鹅类
  
    public TianE(String name, int age, String gender) {
        super(name, age, gender);
    } 
    @Override
    public void jump() {
        System.out.println(name + "身体一摇一摆的跳");
    }

    @Override
    public void run() {
        System.out.println(name + "小脚掌噗嗤的跑");
    }

    @Override
    public void swim() {
       System.out.println(name + "在水里开心的游");
    }
}

public class Dog extends Animal implements Running,Jump,Swim{//狗类
	 public Dog(String name, int age, String gender) {
        super(name, age, gender);
    }

    @Override
    public void jump() {
        System.out.println(name +"两条狗腿开心的蹦");
    }

    @Override
    public void run() {
        System.out.println(name + "撒开狗腿开心的跑");
    }

    @Override
    public void swim() {
        System.out.println(name + "漏出狗头开心的游");
    }
}

然后接下来我们对这个多继承进行实验:
在这里插入图片描述
具体操作如上。
这里的话有一个点需要特别注意:

一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类。

接口之间的继承

接口和接口之间也是可以继承的。这里的继承,是多继承,使用extends关键字,代码示例如下(仅展示部分关键代码):


public interface RunningFu{
    void run();
}

public interface Running extends RunningFu{//接口跑:定义跑的行为
  
}

这样的话,在实现类中并没有报错。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值