Skip to content

Java抽象类、接口与final关键字完全指南

一、核心知识点

1. 抽象类与抽象方法

1.1 抽象的概念

抽象概念图

抽象方法和抽象类的形成:

  • 抽取出来的方法无法做具体实现,需要延伸到子类中做具体实现,就可以定义成抽象方法
  • 抽象方法所在的类一定是抽象类

1.2 定义格式

抽象类定义:

java
public abstract class 类名 {}

抽象方法定义:

java
修饰符 abstract 返回值类型 方法名(形参);

1.3 使用步骤

  1. 定义子类,继承抽象父类
  2. 必须重写父类中所有抽象方法
  3. 创建子类对象(抽象类不能new对象)
  4. 调用重写方法

核心意义: 抽象是一种"代码的设计理念",可以将抽象类看做是一类事物的"标准",只要是我的子类,都必须要遵守我的规范,拥有我的功能。

1.4 抽象的注意事项

注意事项说明
不能new对象抽象类不能直接new对象,只能创建非抽象子类的对象
构造方法抽象类中可以有构造方法,是供子类创建对象时,初始化父类中属性使用的
成员内容抽象类中可以有成员变量、构造、成员方法
抽象方法抽象类中不一定非得有抽象方法,但是有抽象方法的类一定是抽象类
子类要求抽象类的子类,必须重写父类中的所有抽象方法,否则编译无法通过,除非该子类也是抽象类

2. 接口

2.1 接口的概念

接口概念图

接口: 是一种标准、规范

2.2 定义格式

接口定义:

java
public interface 接口名 {}

实现类定义:

java
public class 实现类 implements 接口 {}

2.3 接口中的成员

JDK8之前:

  • 抽象方法:public abstract 返回值类型 方法名(形参) → 即使不写public abstract,默认也有
  • 成员变量:public static final 数据类型 变量名 = 值 → 即使不写public static final,默认也有

JDK8开始:

  • 默认方法:
java
public default 返回值类型 方法名(形参) {
    方法体
    return 结果
}
  • 静态方法:
java
public static 返回值类型 方法名(形参) {
    方法体
    return 结果
}

JDK9开始:

  • 私有方法:将public改成private

2.4 接口的使用步骤

  1. 定义接口
  2. 定义实现类,实现接口
  3. 重写接口中的所有抽象方法
  4. 创建实现类对象(接口不能new对象)
  5. 调用重写方法

2.5 接口的特点

特点说明示例
接口多继承一个接口可以继承一个或者多个接口public interface 接口A extends 接口B,接口C{}
接口多实现一个实现类可以同时实现一个或者多个接口public class 实现类 implements 接口A,接口B{}
继承+实现一个子类可以继承一个父类的同时实现一个或者多个接口public class Zi extends Fu implements 接口A,接口B{}

重要规则:

  • 当一个类实现多个接口时,如果接口中的抽象方法有重名且参数一样的,只需要重写一次
  • 当一个类实现多个接口时,如果默认方法有重名的,参数一样,默认方法必须要重写一次

2.6 接口和抽象类的区别

接口和抽象类的区别

相同点:

  • 都位于继承的顶端,用于被其他类实现或者继承
  • 都不能new
  • 都包含抽象方法,其子类都必须重写这些抽象方法

不同点:

对比项抽象类接口
定位一般作为父类使用成员单一,抽取的都是方法,是功能的大集合
成员可以有成员变量、构造、成员方法、抽象方法等主要是抽象方法、常量
继承类不能多继承接口可以多继承

3. final关键字

3.1 final概述

final: 代表的是最终的

3.2 final的使用

修饰对象格式特点
public final class 类名{}被final修饰的类不能被继承
方法修饰符 final 返回值类型 方法名(形参){}被final修饰的方法不能被重写;final和abstract不能一起使用
成员变量final 数据类型 变量名被final修饰的成员变量,不能二次赋值,相当于常量
局部变量final 数据类型 变量名 = 值不能二次赋值
对象final 类名 对象名 = new 类名()被final修饰的对象,地址值不能改变,但是对象的属性值可以改变

4. 权限修饰符

4.1 四种权限修饰符

修饰符说明
public公共的访问权限,被public修饰的在哪里都能使用
protected受保护的
默认成员变量和方法前面的权限修饰符啥也不用,不写
private私有的,只能在本类中使用

4.2 访问权限表

publicprotected默认private
同类yesyesyesyes
同包不同类yesyesyesno
不同包子父类yesyesnono
不同包非子父类yesnonono

4.3 使用建议

  • 属性: private修饰 → 封装思想
  • 构造: public修饰 → 便于创建对象(工具类除外)
  • 方法: public修饰 → 便于调用

二、常见面试题

⭐ 基础题

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

答:不一定。抽象类中不一定非得有抽象方法,但是有抽象方法的类一定是抽象类。

2. 抽象类能使用final修饰吗?

答:不能。抽象类是被用来继承的,final修饰的类不能被继承,两者冲突。

3. 接口中可以有构造方法吗?

答:不能。接口是用来被实现的,不是用来继承的,所以不需要构造方法。

4. 一个类可以实现多个接口吗?

答:可以。一个实现类可以同时实现一个或者多个接口,这是Java实现多继承效果的方式。

⭐⭐ 进阶题

5. 抽象类和接口有什么区别?

答:

  • 相同点:

    • 都位于继承的顶端,用于被其他类实现或者继承
    • 都不能new对象
    • 都包含抽象方法,其子类都必须重写这些抽象方法
  • 不同点:

    • 抽象类一般作为父类使用,可以有成员变量、构造、成员方法、抽象方法等
    • 接口成员单一,一般抽取接口,抽取的都是方法,是功能的大集合
    • 类不能多继承,接口可以多继承

6. 当一个类实现多个接口时,如果接口中有同名同参数的抽象方法,需要重写几次?

答:只需要重写一次。因为方法签名相同,重写一次即可满足所有接口的要求。

7. 当一个类实现多个接口时,如果接口中有同名同参数的默认方法,需要重写吗?

答:必须重写。因为编译器不知道该使用哪个接口的默认方法实现,所以必须由实现类重写该方法。

8. final修饰基本数据类型和引用数据类型有什么区别?

答:

  • 基本数据类型: 值不能改变
  • 引用数据类型: 地址值不能改变,但是对象内部的属性值可以改变

⭐⭐⭐ 高级题

9. JDK8之后接口中新增的默认方法和静态方法有什么意义?

答:

  • 将来开发都是面向接口编程,接口相当于"功能的大集合"
  • 如果要临时加一个小功能,不需要几行代码,没必要定义抽象方法再在实现类中实现
  • 可以直接在接口中定义默认方法或静态方法,在接口中直接实现
  • 默认方法可以被实现类继承和重写,静态方法只能通过接口名调用

10. 什么时候用抽象类?什么时候用接口?

答:

  • 使用抽象类的场景:

    • 需要定义一组相关类的通用特征
    • 需要共享代码实现
    • 需要声明非static和non-final字段
    • 需要有构造方法初始化状态
  • 使用接口的场景:

    • 需要为不相关的类定义共同的行为
    • 需要多继承的效果
    • 需要定义行为规范,而不关心具体实现
    • 需要利用默认方法提供额外功能

三、实用代码示例

1. 抽象类基础示例

java
public abstract class Animal {
    public abstract void eat();
    public abstract void drink();
}

public class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗啃骨头");
    }

    @Override
    public void drink() {
        System.out.println("狗用舌头卷水喝");
    }
}

public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }

    @Override
    public void drink() {
        System.out.println("猫用舌头舔水喝");
    }
}

public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.eat();
        dog.drink();
        
        Cat cat = new Cat();
        cat.eat();
        cat.drink();
    }
}

2. 抽象类构造方法示例

java
public abstract class Employee {
    private String name;
    private int age;

    public Employee() {
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public abstract void work();
}

public class Teacher extends Employee {
    public Teacher() {
    }

    public Teacher(String name, int age) {
        super(name, age);
    }

    @Override
    public void work() {
        System.out.println("涛哥在台上叭叭叭讲课");
    }
}

public class Test {
    public static void main(String[] args) {
        Teacher t1 = new Teacher("涛哥", 14);
        System.out.println(t1.getName() + "..." + t1.getAge());
        t1.work();
    }
}

3. 接口基础示例

java
public interface USB {
    public abstract void open();
    public abstract void close();
}

public class Mouse implements USB {
    @Override
    public void open() {
        System.out.println("鼠标打开");
    }

    @Override
    public void close() {
        System.out.println("鼠标关闭");
    }
}

public class Test {
    public static void main(String[] args) {
        Mouse m = new Mouse();
        m.open();
        m.close();
    }
}

4. 接口默认方法示例

java
public interface USB {
    public default void methodDef() {
        System.out.println("接口中的默认方法");
    }
}

public class Mouse implements USB {
    @Override
    public void methodDef() {
        System.out.println("重写的接口中的默认方法");
    }
}

public class Test {
    public static void main(String[] args) {
        Mouse mouse = new Mouse();
        mouse.methodDef();
    }
}

5. 接口静态方法示例

java
public interface USB {
    public static void methodSta() {
        System.out.println("接口中的静态方法");
    }
}

public class Test {
    public static void main(String[] args) {
        USB.methodSta();
    }
}

6. 接口常量示例

java
public interface USB {
    public static final int NUM = 10;
    int NUM2 = 20;
}

public class Test {
    public static void main(String[] args) {
        System.out.println(USB.NUM);
        System.out.println(USB.NUM2);
    }
}

7. 接口多实现示例

java
public interface InterfaceA {
    public abstract void method();
    public abstract void method2();
    
    public default void methodDef() {
        System.out.println("接口A中的默认方法");
    }
}

public interface InterfaceB {
    public abstract void method();
    public abstract void method2(int num);

    public default void methodDef() {
        System.out.println("接口B中的默认方法");
    }
}

public class InterfaceImpl implements InterfaceA, InterfaceB {
    @Override
    public void method() {
        System.out.println("重写的默认方法");
    }

    @Override
    public void method2(int num) {
        System.out.println("重写的有参的method02");
    }

    @Override
    public void method2() {
        System.out.println("重写的无参的method02");
    }

    @Override
    public void methodDef() {
        System.out.println("重写的默认方法");
    }
}

8. final修饰类示例

java
public final class Animal {
}

// 编译错误:无法继承final修饰的类
// public class Dog extends Animal {
// }

9. final修饰方法示例

java
public class Animal {
    public final void eat() {
        System.out.println("吃吃吃");
    }
}

public class Dog extends Animal {
    // 编译错误:无法重写final修饰的方法
    // @Override
    // public void eat() {
    // }
}

10. final修饰对象示例

java
public class Student {
    private int id;
    private String name;

    public Student() {
    }

    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

public class Test {
    public static void main(String[] args) {
        final Student s1 = new Student(1, "张三");
        System.out.println(s1);
        // s1 = new Student(); // 编译错误:地址值不能改变
        s1.setName("李四"); // 正确:属性值可以改变
        System.out.println(s1.getName());
    }
}

四、经典练习题

练习1:员工体系设计

题目描述: 某IT公司有多名员工,按照员工负责的工作不同,进行了部门的划分(研发部、维护部)。

  • 研发部(Developer)根据所需研发的内容不同,分为JavaEE工程师、Android工程师
  • 维护部(Maintainer)根据所需维护的内容不同,分为网络维护工程师(Network)、硬件维护工程师(Hardware)

公司的每名员工都有他们自己的员工编号、姓名,并要做他们所负责的工作。

工作内容:

  • JavaEE工程师:员工号为xxx的xxx员工,正在研发电商网站
  • Android工程师:员工号为xxx的xxx员工,正在研发电商的手机客户端软件
  • 网络维护工程师:员工号为xxx的xxx员工,正在检查网络是否畅通
  • 硬件维护工程师:员工号为xxx的xxx员工,正在修复电脑主板

要求: 根据描述,完成员工体系中所有类的定义,并指定类之间的继承关系。进行XX工程师类的对象创建,完成工作方法的调用。

参考实现:

java
public abstract class Employee {
    private int id;
    private String name;

    public Employee() {
    }

    public Employee(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public abstract void work();
}

public abstract class Developer extends Employee {
    public Developer() {
    }

    public Developer(int id, String name) {
        super(id, name);
    }
}

public abstract class Maintainer extends Employee {
    public Maintainer() {
    }

    public Maintainer(int id, String name) {
        super(id, name);
    }
}

public class JavaEE extends Developer {
    public JavaEE() {
    }

    public JavaEE(int id, String name) {
        super(id, name);
    }

    @Override
    public void work() {
        System.out.println("员工号为" + getId() + "的" + getName() + "员工,正在研发电商网站");
    }
}

public class Android extends Developer {
    public Android() {
    }

    public Android(int id, String name) {
        super(id, name);
    }

    @Override
    public void work() {
        System.out.println("员工号为" + getId() + "的" + getName() + "员工,正在研发电商的手机客户端软件");
    }
}

public class Network extends Maintainer {
    public Network() {
    }

    public Network(int id, String name) {
        super(id, name);
    }

    @Override
    public void work() {
        System.out.println("员工号为" + getId() + "的" + getName() + "员工,正在检查网络是否畅通");
    }
}

public class Hardware extends Maintainer {
    public Hardware() {
    }

    public Hardware(int id, String name) {
        super(id, name);
    }

    @Override
    public void work() {
        System.out.println("员工号为" + getId() + "的" + getName() + "员工,正在修复电脑主板");
    }
}

public class Test {
    public static void main(String[] args) {
        JavaEE javaEE = new JavaEE(1, "张三");
        javaEE.work();

        Android android = new Android(2, "李四");
        android.work();

        Network network = new Network(3, "王五");
        network.work();

        Hardware hardware = new Hardware(4, "赵六");
        hardware.work();
    }
}

五、学习建议

1. 理解抽象类的设计理念

抽象类不仅仅是语法特性,更是一种设计理念:

  • 将抽象类看做是一类事物的"标准"
  • 子类必须遵守父类的规范
  • 通过抽象方法强制子类实现特定功能

2. 掌握接口的核心作用

接口是Java实现多继承效果的关键:

  • 接口是一种标准、规范
  • 接口是功能的大集合
  • 接口可以实现多继承

3. 理解final的使用场景

final关键字在开发中应用广泛:

  • 保护类不被继承(如String类)
  • 保护方法不被重写
  • 定义常量
  • 保护对象引用不被改变

4. 掌握权限修饰符的使用原则

权限修饰符体现了封装思想:

  • 属性使用private,实现封装
  • 构造方法使用public,方便创建对象
  • 方法使用public,方便调用

5. 多练习综合案例

通过综合案例(如员工体系):

  • 理解类与类之间的关系
  • 掌握继承、抽象、接口的综合应用
  • 培养面向对象的设计思维

六、扩展知识

1. 接口与抽象类的选择原则

优先使用接口:

  • 需要定义行为规范
  • 需要多继承效果
  • 不需要共享代码实现

使用抽象类:

  • 需要共享代码实现
  • 需要定义成员变量
  • 需要控制访问权限

2. 设计模式中的应用

模板方法模式:

  • 使用抽象类定义算法骨架
  • 子类实现具体步骤

策略模式:

  • 使用接口定义策略
  • 不同实现类代表不同策略

3. JDK中的典型应用

抽象类:

  • AbstractList:List接口的骨架实现
  • AbstractSet:Set接口的骨架实现
  • AbstractMap:Map接口的骨架实现

接口:

  • Comparable:自然排序接口
  • Comparator:定制排序接口
  • Cloneable:克隆标记接口

4. 面向接口编程的思想

核心思想:

  • 定义接口作为契约
  • 面向接口编程,而不是面向实现编程
  • 提高代码的灵活性和可扩展性

优势:

  • 降低耦合度
  • 提高代码复用性
  • 便于系统扩展和维护

学习提示:

  • 重点理解抽象类和接口的设计思想
  • 多写代码练习,掌握各种使用场景
  • 结合实际项目理解面向接口编程的优势
  • 注意final关键字在各种场景下的使用限制