java中的抽象类与接口(面试常考,重要)!!(一)

简介: java中的抽象类与接口(面试常考,重要)!!(一)

目录

抽象类

语法规则

注意事项(重要,全部掌握)

抽象类的作用

接口

语法规则

注意事项

提示

类实现多个接口

接口使用实例(Comparable 接口与Comparator接口)

Comparable接口

Comparator接口(比较器)

Comparable接口 与Comparator接口的区别

代码示例

接口间的继承(扩展)

总结

抽象类

语法规则

在多态关于形状的代码例子中, 我们发现, 父类 Shape 中的 draw 方法好像并没有什么实际工作, 主要的绘制图形都是由Shape 的各种子类的 draw 方法来完成的. 像这种没有实际工作的方法, 我们可以把它设计成一个 抽象方法(abstract method), 包含抽象方法的类我们称为 抽象类(abstract class).

abstract class Shape {
            abstract public void draw();
        }
  • 在 draw 方法前加上 abstract 关键字, 表示这是一个抽象方法. 同时抽象方法没有方法体(没有 { }, 不能执行具体代码).
  • 对于包含抽象方法的类, 必须加上 abstract 关键字表示这是一个抽象类.

注意事项(重要,全部掌握)

抽象方法:一个方法如果被abstract修饰,那么这个方法就是抽象方法。抽象方法可以没有具体的实现。


同时抽象方法不能被private修饰,因为一旦被private修饰后,非抽象子类是不能重写父类私有的抽象方法的。


包含抽象方法的类称作抽象类,其必须被abstract所修饰,一个抽象类中可以没有抽象方法,但是如果一个类中有抽象方法,那么这个类一定是抽象类,其必须被abstract所修饰


抽象类不可以被实例化。不能使用例如Shape shape = new Shape();这样的语句

image.png

但是不影响抽象类发生向上转型,所以说抽象类不可以被实例化,但是可以发生向上转型.


类内的数据成员,和普通类没有区别,可以包含其他的非抽象方法, 也可以包含字段. 这个非抽象方法和普通方法的规则都是一样的, 可以被重写,也可以被子类直接调用


抽象类主要就是用来被继承的.


如果一个非抽象类继承了这个抽象类,那么这个类必须重写抽象类当中的所有的抽象方法。(重要)


当抽象类A 继承 抽象类B 那么A可以不重写B中的抽象方法,但是一旦A要是再被一个非抽象类c继承,那么c类中一定还要重写A中和B中的抽象方法.

代码示例:

abstract class A {
    abstract public void eat();
    public void drink() {
    }
}
abstract class C extends A {
    abstract public void fly();
}
class b extends C {
    @Override
    public void eat() {
        System.out.println("eat");
    }
    @Override
    public void fly() {
        System.out.println("fly");
    }
}

抽象类和抽象方法一定是不能被final修饰的,因为一旦类被final修饰,便不能继承,方法被final修饰,不能被重写

抽象类不能实例化的目的也就是为了继承和重写,所以两者不能同时使用

抽象类实现接口时,可以不需要对接口方法进行重写,即可以重写一部分,不重写一部分

抽象类有构造方法,但是不能使用,即不能创建具体的对象

抽象类的作用

 抽象类存在的最大意义就是为了被继承.

抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法.

有些同学可能会说了, 普通的类也可以被继承呀, 普通的方法也可以被重写呀, 为啥非得用抽象类和抽象方法呢?

答:确实如此. 但是使用抽象类相当于多了一重编译器的校验.

使用抽象类的场景就如上面的代码, 实际工作不应该由父类完成, 而应由子类完成.

那么此时如果不小心误用成父类了, 使用普通类编译器是不会报错的. 但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题.

很多语法存在的意义都是为了 “预防出错”, 例如我们曾经用过的 final 也是类似. 创建的变量用户不去修改, 不就相当于常量嘛? 但是加上 final 能够在不小心误修改的时候, 让编译器及时提醒我们.

充分利用编译器的校验, 在实际开发中是非常有意义的.


在实际开发中,抽象类的作用也是非常重要的:


抽象类可以降低接口实现类对接口实现过程难度,因为在实际开发中一个接口中可能会有很多接口是使用不到的,当一个非抽象类去继承这个接口的时候,就需要重写这个接口中的所有抽象方法,造成代码冗余,为了避免这种情况的发生,此时就需要抽象类将接口中不需要使用的抽象方法进行重写,将需要使用的抽象方法继承下来.

这样其他类只需要去继承不同的抽象类,依照自己业务的要求去寻找自己所需要的抽象类,然后对抽象类中的抽象方法进行重写就行了,从而降低了接口实现过程中的难度。

接口

接口是抽象类的更进一步. 抽象类中还可以包含非抽象方法, 和字段. 而接口中包含的方法都是抽象方法, 字段只能包含静态常量.

语法规则

我们直接通过一段代码来进行总结:

1.interface Shape1 {  
 -    //接口中定义的成员变量都会被默认为常量,由public  static final默认进行修饰,所以就算不写public  static final也无所谓,  
 -    int a = 10;  
 -    public static final String name = "sss";  
 -  
 -    //接口中的方法几乎都为抽象方法,默认为public abstract进行修饰,所以就算不写public abstract也无所谓  
 -    void draw();  
 -  
 -    //当然接口中也可以定义非抽象方法,用default关键字即可,default是在java8中引入的关键字,具体可看csdn博客  
 -    default void drink() {  
 -        System.out.println("喝水");  
 -    }  
13.}  
 -  
15.class Cycle1 implements Shape1 {  
 -    @Override  
 -    public void draw() {  
 -        System.out.println("画一个⚪");  
 -    }  
 -  
21.}  
 -  
23.class React1 implements Shape1 {  
 -    @Override//注解  
 -    public void draw() {  
 -        System.out.println("画一个□");  
 -    }  
 -  
29.}  
 -  
31.public class TestMain {  
 -    public static void fun(Shape1 shape) {  
 -        shape.draw();  
 -    }  
 -  
 -    public static void main(String[] args) {  
 -        //接口也是可以发生向上转型的,前提是一个类必须实现了这个接口  
 -        //例如下面的代码,因为Cycle1类实现了Shape1这个接口,所以此时接口类型的shape引用可以指向Cycle1类的实例了  
 -        Shape1 shape = new Cycle1();  
 -        Shape1 shape1=new React1();  
 -        shape.draw();  
 -        shape1.draw();  
 -    }  
44.}  

使用 interface 定义一个接口

接口中的方法一定是抽象方法, 因此可以省略 abstract

接口中的方法一定是 public,因此可以省略 public

Cycle 使用 implements 继承接口. 此时implements表达的含义不再是 “扩展”, 而是 “实现”

在调用的时候同样可以创建一个接口的引用, 对应到一个子类的实例.

接口不能单独被实例化.

注意事项

接口当中的方法都是抽象方法。其默认前缀为public abstract,在书写时是可以省略的,因为编译器默认这个方法就是 public abstract

抽象类其实可以有具体实现的方法。这个方法是被default修饰的(JDK1.8加入的)

接口当中只能包含静态常量,所有常量的前缀全部默认为public static

final,在书写时是可以省略的,因为编译器默认这个成员变量就是public static final

接口当中的成员变量默认是:public static final 成员方法是:public abstract

接口是不可以被实例化的。 Shape shape = new Shape();(不允许)

接口和类之间的关系 : implements(实现),当一个非抽象类实现了这个接口且接口中有抽象方法时,则这个类必须重写接口中的所有抽象方法,如果这个类是抽象类,则可以不实现接口中的所有方法,因为抽象类中允许有抽象方法的存在

接口的出现是为了实现多继承.一个类可以实现多个接口。但是只能继承一个父类

只要这个类 实现了该接口,那么就可以进行向上转型。

当然一个接口也可以去继承(扩展)多个接口

扩展(extends)与实现(implements)的区别

扩展指的是当前已经有一定的功能了, 进一步扩充功能.

实现指的是当前啥都没有, 需要从头构造出来.

提示

1.我们创建接口的时候, 接口的命名一般以大写字母 I 开头.

2.接口的命名一般使用 “形容词” 词性的单词.

3.阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性.

不加任何修饰符号的意思就是常量省略public static final ,抽象方法省略前缀public abstract

一个错误的代码:

interface IShape {
    // 即便不写public,也是默认为public权限
    abstract void draw();
}
class Rect implements IShape {
    void draw() {
        //权限更加严格了,所以无法覆写。意思就是Rect类中重写draw方法时必须加上public才可以
        System.out.println("□");
    }
}

image.png

类实现多个接口

有的时候我们需要让一个类同时继承自多个父类. 这件事情在有些编程语言通过 多继承 的方式来实现的.

然而 Java 中只支持单继承, 一个类只能 extends 一个父类. 但是可以同时实现多个接口, 也能达到多继承类似的效果. 现在我们通过类来表示一组动物.

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

另外我们再提供一组接口, 分别表示 “会飞的”, “会跑的”, “会游泳的”.

interface IFlying {
    void fly();
}
interface IRunning {
    void run();
}
interface ISwimming {
    void swim();
}

接下来我们创建几个具体的动物
猫, 是会跑的.

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 + "正在蹬腿游泳");
    }
}

有一种神奇的动物, 水陆空三栖, 叫做 “鸭子”

class Duck extends Animal implements IRunning, ISwimming, IFlying {
    public Duck(String name) {
        super(name);
    }
    @Override
    public void fly() {
        System.out.println(this.name + "正在用翅膀飞");
    }
    @Override
    public void run() {
        System.out.println(this.name + "正在用两条腿跑");
    }
    @Override
    public void swim() {
        System.out.println(this.name + "正在漂在水上");
    }
}

上面的代码展示了 Java 面向对象编程中最常见的用法: 一个类继承一个父类, 同时实现多种接口.

继承表达的含义是 is - a 语义, 而接口表达的含义是 具有 xxx 特性 .猫是一种动物, 具有会跑的特性.

青蛙也是一种动物, 既能跑, 也能游泳

鸭子也是一种动物, 既能跑, 也能游, 还能飞


这样设计有什么好处呢? 时刻牢记多态的好处, 让程序猿忘记类型. 有了接口之后, 类的使用者就不必关注具体类型, 而只关注某个类是否具备某种能力.

例如, 现在实现一个方法, 叫 “散步”

public static void walk(IRunning running) {
        running.run();
}

在这个 walk 方法内部, 我们并不关注到底是哪种动物, 只要参数是会跑的,就行,此时需要注意的是这个会跑的前提是这个类必须实现了IRunning接口才可以

        //因为此时Cat类实现的是IRunning接口,所以此时可以使用向上转型如下所示,若没有实现IRunning接口,则会报错
        IRunning iRunning = new Cat("猫猫");
        walk(iRunning);
        //同样的因为此时Frog类实现的是IRunning接口,所以此时可以使用向上转型如下所示,若没有实现IRunning接口,同样会报错
        IRunning iRunning1 = new Frog("青蛙");
        walk(iRunning1);


目录
打赏
0
0
0
0
11
分享
相关文章
JAVA接入DeepSeek大模型接口开发---阿里云的百炼模型
随着大模型的越来越盛行,现在很多企业开始接入大模型的接口,今天我从java开发角度来写一个demo的示例,用于接入DeepSeek大模型,国内的大模型有很多的接入渠道,今天主要介绍下阿里云的百炼模型,因为这个模型是免费的,只要注册一个账户,就会免费送百万的token进行学习,今天就从一个简单的可以执行的示例开始进行介绍,希望可以分享给各位正在学习的同学们。
242 3
JAVA接入DeepSeek大模型接口开发---阿里云的百炼模型
java面试-基础语法与面向对象
本文介绍了 Java 编程中的几个核心概念。首先,详细区分了方法重载与重写的定义、发生阶段及规则;其次,分析了 `==` 与 `equals` 的区别,强调了基本类型和引用类型的比较方式;接着,对比了 `String`、`StringBuilder` 和 `StringBuffer` 的特性,包括线程安全性和性能差异;最后,讲解了 Java 异常机制,包括自定义异常的实现以及常见非检查异常的类型。这些内容对理解 Java 面向对象编程和实际开发问题解决具有重要意义。
57 15
|
20天前
|
Java Lambda 表达式:以 Foo 接口为例深入解析
本文深入解析了 Java 8 中 Lambda 表达式的用法及其背后的函数式接口原理,以 `Foo` 接口为例,展示了如何通过简洁的 Lambda 表达式替代传统匿名类实现。文章从 Lambda 基本语法、函数式接口定义到实际应用层层递进,并探讨默认方法与静态方法的扩展性,最后总结常见误区与关键点,助你高效优化代码!
40 0
|
20天前
|
java中一个接口A,以及一个实现它的类B,一个A类型的引用对象作为一个方法的参数,这个参数的类型可以是B的类型吗?
本文探讨了面向对象编程中接口与实现类的关系,以及里氏替换原则(LSP)的应用。通过示例代码展示了如何利用多态性将实现类的对象传递给接口类型的参数,满足LSP的要求。LSP确保子类能无缝替换父类或接口,不改变程序行为。接口定义了行为规范,实现类遵循此规范,从而保证了多态性和代码的可维护性。总结来说,接口与实现类的关系天然符合LSP,体现了多态性的核心思想。
28 0
Java社招面试中的高频考点:Callable、Future与FutureTask详解
大家好,我是小米。本文主要讲解Java多线程编程中的三个重要概念:Callable、Future和FutureTask。它们在实际开发中帮助我们更灵活、高效地处理多线程任务,尤其适合社招面试场景。通过 Callable 可以定义有返回值且可能抛出异常的任务;Future 用于获取任务结果并提供取消和检查状态的功能;FutureTask 则结合了两者的优势,既可执行任务又可获取结果。掌握这些知识不仅能提升你的编程能力,还能让你在面试中脱颖而出。文中结合实例详细介绍了这三个概念的使用方法及其区别与联系。希望对大家有所帮助!
244 60
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
190 14
Java 面试必问!线程构造方法和静态块的执行线程到底是谁?
大家好,我是小米。今天聊聊Java多线程面试题:线程类的构造方法和静态块是由哪个线程调用的?构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节有助于掌握Java多线程机制。下期再见! 简介: 本文通过一个常见的Java多线程面试题,详细讲解了线程类的构造方法和静态块是由哪个线程调用的。构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节对掌握Java多线程编程至关重要。
80 13
java语言后台管理若依框架-登录提示404-接口异常-系统接口404异常如何处理-登录验证码不显示prod-api/captchaImage 404 (Not Found) 如何处理-解决方案优雅草卓伊凡
java语言后台管理若依框架-登录提示404-接口异常-系统接口404异常如何处理-登录验证码不显示prod-api/captchaImage 404 (Not Found) 如何处理-解决方案优雅草卓伊凡
436 5
Java线程调度揭秘:从算法到策略,让你面试稳赢!
在社招面试中,关于线程调度和同步的相关问题常常让人感到棘手。今天,我们将深入解析Java中的线程调度算法、调度策略,探讨线程调度器、时间分片的工作原理,并带你了解常见的线程同步方法。让我们一起破解这些面试难题,提升你的Java并发编程技能!
143 16
Java 高级面试技巧:yield() 与 sleep() 方法的使用场景和区别
本文详细解析了 Java 中 `Thread` 类的 `yield()` 和 `sleep()` 方法,解释了它们的作用、区别及为什么是静态方法。`yield()` 让当前线程释放 CPU 时间片,给其他同等优先级线程运行机会,但不保证暂停;`sleep()` 则让线程进入休眠状态,指定时间后继续执行。两者都是静态方法,因为它们影响线程调度机制而非单一线程行为。这些知识点在面试中常被提及,掌握它们有助于更好地应对多线程编程问题。
156 9

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等