服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - 解析JavaSe的抽象类和接口

解析JavaSe的抽象类和接口

2022-08-15 09:47小小茶花女 Java教程

这篇文章主要为大家详细介绍了JavaSe的抽象类和接口,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助

1. 抽象类和抽象方法

抽象方法和抽象类必须使用abstract修饰符来定义,有抽象方法的类只能被定义成抽象类,抽象类里可以没有抽象方法。抽象方法和抽象类的规则如下:

(1) 抽象类和抽象方法必须使用abstract修饰符来修饰,抽象方法不能有方法体。

(2) 抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例。即使抽象类里不包含抽象方法,这个抽象类也不能创建实例。

(3) 抽象类可以包含Field、方法、构造器、初始化块、内部类、枚举类6种成分。抽象类的构造器不能用于创建实例,主要是用于被其子类调用。

(4) 含有抽象方法的类只能被定义成抽象类。

(5) abstract不能用于修饰Field,不能用于修饰局部变量,即没有抽象变量、没有抽象Field等说法;abstract也不能用于修饰构造器,没有抽象构造器,抽象类里定义的构造器只能是普通构造器。

定义抽象方法只需在普通方法上增加abstract修饰符,并把普通方法的方法体(也就是方法后花括号括起来的部分)全部去掉,并在方法后增加分号即可。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public abstract class Person {
    // 初始化块
    {
        System.out.println("执行Person类的初始化块");
    }
    private int age;
    // 该构造器并不是为了创建对象,而是为了子类调用
    public Person(){
    }
    public Person(int age){
        this.age = age;
    }
    // 抽象方法,没有方法体
    public abstract void show();
    public void test(){
        System.out.println("非抽象方法");
    }
}

抽象类不能用于创建实例,只能当作父类被其他子类继承。

?
1
2
3
4
5
6
7
8
9
public class Student extends Person {
    public Student(int age){
        super(age);
    }
    @Override
    public void show() {
        System.out.println("重写抽象类中的抽象方法");
    }
}

注意:

(1) 当使用abstract修饰类时,表明这个类只能被继承;当使用abstract修饰方法时,表明这个方法必须由子类提供实现(即重写)。而final修饰的类不能被继承,final修饰的方法不能被重写。因此final和abstract永远不能同时使用。

(2) 当使用static修饰一个方法时,表明这个方法属于该类本身,即通过类就可调用该方法,但如果该方法被定义成抽象方法,则将导致通过该类来调用该方法时出现错误(调用了一个没有方法体的方法肯定会引起错误)。因此static和abstract不能同时修饰某个方法,即没有所谓的类抽象方法。

(3) 抽象类不能创建实例,只能当成父类来被继承。从语义的角度来看,抽象类是从多个具体类中抽象出来的父类,它具有更高层次的抽象。

2. 相关面试题

1、抽象类必须要有抽象方法吗?

抽象类不一定非要有抽象方法,抽象类中可以含有抽象方法和非抽象方法,可以只含有抽象方法,也可以只含有非抽象方法

?
1
2
3
4
5
public abstract class Person {
    public void test(){
        System.out.println("非抽象方法");
    }
}

2、普通类和抽象类有哪些区别?

(1) 普通类不能包含抽象方法,抽象类可以包含抽象方法。

(2) 抽象类不能直接实例化,只能当作父类被其他子类继承,普通类可以直接实例化。

?
1
2
3
4
5
6
7
8
public abstract class Person {
    // 抽象方法
    public abstract void show();
    // 非抽象方法
    public void test(){
        System.out.println("非抽象方法");
    }
}

外部类的形式继承抽象类Person

?
1
2
3
4
5
6
public class Student extends Person {
    @Override
    public void show() {
        System.out.println("重写抽象类中的抽象方法");
    }
}

内部类的形式继承抽象类Person

?
1
2
3
4
5
6
7
8
9
10
11
public class Teacher {
    public static void main(String[] args) {
        Person person = new Person() {
            @Override
            public void show() {
                System.out.println("重写抽象类的抽象方法");
            }
        };
        person.show();
    }
}

3、抽象类能使用 final 修饰吗?

当使用abstract修饰类时,表明这个类只能被继承;当使用abstract修饰方法时,表明这个方法必须由子类提供实现(即重写)。而final修饰的类不能被继承,final修饰的方法不能被重写。因此final和abstract永远不能同时使用。

3. 接口

接口只是对一类事物的属性和行为更高层次的抽象。对修改关闭,对扩展(不同的实现implements)开放,接口是对开闭原则的一种体现。

接口体:

?
1
2
3
4
5
6
7
interface 接口名{  常量    抽象方法    静态方法    默认方法}interface 接口名
{
    常量
    抽象方法
    静态方法
    默认方法
}

3.1 接口中的常量

接口中的所有成员变量都默认是由public static final修饰的,且由于接口里没有构造器和初始化块,因此需要在定义Field时就指定初始值;

接口中的Field都是常量,经常用来被定义常量的集合:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface FieldInterface {
    /**
     * 一年的天数
     */
    int DAY = 365;
    /**
     * 一年的星期数
     */
    int WEEK = 52;
    /**
     * 一年的月数
     */
    int MONTH = 12;
}
?
1
2
3
4
5
6
7
8
public class Main {
    public static void main(String[] args) {
        /**
         * 接口中定义的Field都是public static final修饰的常量,因此需要使用接口名来访问
         */
        System.out.println(FieldInterface.DAY);
    }
}

一个类实现某个接口时,该类将会获得接口中定义的Field :

?
1
2
3
public class FieldImpl implements FieldInterface {
    // 将会继承FieldInterface中定义的所有Field
}
?
1
2
3
4
5
6
7
public class Main {
    public static void main(String[] args) {
        System.out.println(FieldInterface.DAY);
        // 使用实现类调用接口中的常量
        System.out.println(FieldImpl.DAY);
    }
}

3.2 接口中的方法

接口中的三种方法:

(1) 接口中的普通方法,默认都是public abstract修饰的抽象方法,没有方法体;

(2) 接口中的静态方法,静态方法必须要有实现,且这个静态方法只能用public修饰或不写;

(3) 接口中的默认方法,默认方法必须要有实现,用default修饰且不可省略,用来供子类继承或重写,二选一,但只能在实现类中使用或者实现类对象中使用;

分别展开举例说明:

3.2.1 接口中的普通方法

接口中的普通方法,默认都是public abstract修饰的抽象方法,没有方法体;

  • 接口的实现类如果是普通类,那么必须重写接口中的所有抽象方法
  • 接口的实现类如果是抽象类,那么可以不用重写接口中的抽象方法
?
1
2
3
4
5
6
7
8
9
10
public interface BaseMethod {
    /**
     * 接口中的普通方法
     * 以下方法默认都是public abstract修饰的抽象方法,没有方法体
     */
    public abstract void show1();
    abstract void show2();
    public void show3();
    void show4();
}

① 接口的实现类如果是普通类,那么必须重写接口中的所有抽象方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
public class BaseMethodImpl implements BaseMethod {
    /**
     * 当一个类实现BaseMethod接口后,需要实现BaseMethod接口中的所有抽象方法
     */
    @Override
    public void show1() { }
    @Override
    public void show2() { }
    @Override
    public void show3() { }
    @Override
    public void show4() { }
}

② 接口的实现类如果是抽象类,那么可以不用重写接口中的抽象方法:

?
1
2
3
4
5
6
7
8
public abstract class BaseMethodImpl2 implements BaseMethod {
    /**
     * 抽象类会继承接口中定义的所有抽象方法和field
     * 这个类仍然是抽象类,可以不用实现接口中所有的抽象方法
     */
    @Override
    public void show1() { }
}

3.2.2 接口中的静态方法

  • 调用形式:接口名.静态方法名
  • 接口中定义的静态方法,不能被子接口继承
  • 接口中定义的静态方法,不能被实现该接口的类继承
?
1
2
3
4
5
6
7
8
9
public interface StaticMethod {
    /**
     * 接口中的静态方法
     * 接口中是可以定义静态方法的,静态方法必须要有实现。且这个静态方法只能用public修饰或不写。
     */
    public static void test1(){
        System.out.println("接口中定义静态方法");
    }
}
?
1
2
3
4
5
6
7
8
public class Main {
    public static void main(String[] args) {
        /**
         * 接口中的静态方法,只能通过接口名来调用,就像类的静态方法一样
         */
        StaticMethod.test1();
    }
}

① 接口中定义的静态方法不能被子接口继承:

?
1
2
public interface StaticMethod1 extends StaticMethod {
}
?
1
2
3
4
5
6
7
8
public class Main {
    public static void main(String[] args) {
        /**
         * 编译报错,说明接口中定义的静态方法不能被子接口继承
         */
        StaticMethod1.test1();
    }
}

② 接口中定义的静态方法不能被实现类继承:

?
1
2
public class StaticMethodImpl implements StaticMethod {
}
?
1
2
3
4
5
6
7
8
public class Main {
    public static void main(String[] args) {
        /**
         * 编译报错,说明接口中定义的静态方法不能被实现类继承
         */
        StaticMethodImpl.test1();
    }
}

3.2.3 接口中的默认方法

  • 接口中的默认方法,不能通过接口名调用,需要通过接口实现类的实例进行访问,即对象名.默认方法名()
  • 接口中的默认方法,可以被实现类继承,也可以在实现类中重写
  • 接口中的默认方法,可以被子接口继承,也可以在子接口中重写
?
1
2
3
4
5
6
7
8
9
public interface DefaultMethod {
    /**
     * 接口中的默认方法
     * 用default修饰,不可省略,供子类继承或重写,二选一,但只能在实现类中使用或者实现类对象中使用
     */
    public default void test2(){
        System.out.println("接口中定义默认方法");
    }
}

① 接口中的默认方法,可以被实现类继承,也可以在实现类中重写

?
1
2
3
public class DefaultMethodImpl implements DefaultMethod {
    // 继承接口中的方法
}
?
1
2
3
4
5
6
7
public class DefaultMethodImpl implements DefaultMethod {
    // 重写接口中的方法
    @Override
    public void test2() {
        System.out.println("实现类重写接口中的方法");
    }
}
?
1
2
3
4
5
6
7
public class DefaultMethodImpl implements DefaultMethod {
    // 重写接口中的方法
    @Override
    public void test2() {
        System.out.println("实现类重写接口中的方法");
    }
}

② 接口中的默认方法,可以被子接口继承,也可以在子接口中重写

?
1
2
3
public interface DefaultMethod1 extends DefaultMethod {
    // 继承父接口中的方法
}
?
1
2
3
4
5
6
7
public interface DefaultMethod1 extends DefaultMethod {
    // 重写父接口中的方法
    @Override
    default void test2() {
        System.out.println("子接口中重写父接口中的方法");
    }
}

3.3 接口中的接口和枚举类

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public interface Base {
    /**
     * 在接口中定义接口
     */
    interface CommonConstant{
        /**
         * 一年的天数
         */
        int DAY = 365;
        /**
         * 一年的星期数
         */
        int WEEK = 52;
        /**
         * 一年的月数
         */
        int MONTH = 12;
        /**
         * 在接口中定义静态方法
         */
        static void show() {
            System.out.println("在接口中定义接口");
        }
    }
    /**
     * 在接口中定义枚举类
     */
    @AllArgsConstructor
    enum WhiteStatusEnum {
        /**
         * 未加白
         */
        NO_WHITE("未加白", "未加白"),
        /**
         * 部分加白
         */
        PART_WHITE("部分加白", "部分加白"),
        /**
         * 全部加白
         */
        ALL_WHITE("全部加白", "全部加白");
        @Getter
        private final String label;
        @Getter
        private final String value;
    }
}

3.4 接口和抽象类

共同点:

(1) 接口和抽象类都不能被实例化,它们都位于继承树的顶端,用于被其他类实现和继承;

(2) 接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法;

(3) 接口和抽象类中都可以包括抽象方法和非抽象方法;

不同点:

(1) 接口里只能定义静态常量Field,不能定义普通Field;抽象类里则既可以定义普通Field,也可以定义静态常量Field。

(2) 接口里不包含构造器;抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。

(3) 接口里不能包含初始化块;但抽象类则完全可以包含初始化块。

(4) 一个类最多只能有一个直接父类,包括抽象类;但一个类可以直接实现多个接口,通过实现多个接口可以弥补Java单继承的不足。

?
1
2
public class MethodImpl implements FieldInterface, MethodInterface {
}

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注服务器之家的更多内容!   

原文链接:https://hengheng.blog.csdn.net/article/details/122689653

延伸 · 阅读

精彩推荐
  • Java教程MyBatis批量插入(insert)数据操作

    MyBatis批量插入(insert)数据操作

    本文给大家分享MyBatis批量插入(insert)数据操作知识,非常不错,具有参考借鉴价值,感兴趣的朋友一起学习吧 ...

    chenzhou1235203012020-05-20
  • Java教程Java 小游戏开发之俄罗斯方块

    Java 小游戏开发之俄罗斯方块

    这篇文章主要介绍了Java 小游戏开发之俄罗斯方块的相关资料,这里实现俄罗斯方块的实例和实现效果给大家看下,学习java基础的朋友的好资料,需要的朋友...

    Renyi-Fan5112020-12-05
  • Java教程Resttemplate中设置超时时长方式

    Resttemplate中设置超时时长方式

    这篇文章主要介绍了Resttemplate中设置超时时长方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...

    dbave7052022-03-06
  • Java教程Java中的隐式参数和显示参数实例详解

    Java中的隐式参数和显示参数实例详解

    这篇文章主要介绍了Java中的隐式参数和显示参数是什么,另外还有两个小例子帮助大家理解,需要的朋友可以参考下。...

    1993Smile3672020-12-22
  • Java教程Mybatis数据批量插入如何实现

    Mybatis数据批量插入如何实现

    这篇文章主要介绍了Mybatis数据批量插入如何实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考...

    jock_javaEE3872020-07-29
  • Java教程Java Optional实践(小结)

    Java Optional实践(小结)

    这篇文章主要介绍了Java Optional实践(小结),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    张喜硕5492021-06-01
  • Java教程Spring boot按日切分spring boot的nohup.out日志文件的方法

    Spring boot按日切分spring boot的nohup.out日志文件的方法

    过大的日志文件维护起来存在诸多问题,所以最好是能够按日或按大小切分日志文件,下面小编给大家带来了Spring boot按日切分spring boot的nohup.out日志文件的...

    ShinobuChyan5982021-05-23
  • Java教程JAVA比较两张图片相似度的方法

    JAVA比较两张图片相似度的方法

    这篇文章主要介绍了JAVA比较两张图片相似度的方法,涉及java针对图片像素操作的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下 ...

    fzhlee2062019-12-28