java中的抽象类与接口(面试常考,重要)!!,kafka面试题汇总

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

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);

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

=================================================================================================

Comparable接口


刚才的关于例子比较抽象, 我们再来一个更能实际的例子.

给对象数组排序

给定一个学生类

class Student {

private String name;

private int score;

public Student(String name, int score) {

this.name = name;

this.score = score;

}

@Override

public String toString() {

return “[” + this.name + “:” + this.score + “]”;

}

}

再给定一个学生对象数组

Student[]students=new Student[]{

new Student(“张三”,95),

new Student(“李四”,96),

new Student(“王五”,97),

new Student(“赵六”,92),

};

现对这个对象数组中的元素进行排序(按分数降序).

按照我们之前的理解, 数组我们有一个现成的 sort 方法, 能否直接使用这个方法呢?

Arrays.sort(students);

System.out.println(Arrays.toString(students));

// 运行出错, 抛出异常.

Exception in thread"main"java.lang.ClassCastException:Student cannot be cast to java.lang.Comparable

我们呢会发现此时会发生类型转换异常,仔细思考, 不难发现, 和普通的整数不一样, 两个整数是可以直接比较的, 大小关系明确. 而两个学生对象的大小关系怎么确定? 需要我们额外指定.

假设我们不去指定的话,我们也不知道到底是按照学生姓名比较,还是学生的年龄来进行比较.

此时就需要让我们的 Student 类实现 Comparable 接口, 并重写 其中的 抽象的compareTo 方法,下面来看代码:

1.//自定义类型比较大小需要实现Comparable接口,<>为泛型

2.//对于Comparable接口来说,一般都是在类的内部定义的

3.class Student implements Comparable {

  1. public String name;

  2. public int age;

  3. public int score;

  4. public Student(String name, int age, int score) {

  5.    this.name = name;  
    
  6.    this.age = age;  
    
  7.    this.score = score;  
    
  8. }

  9. //重写toString方法

  10. @Override

  11. public String toString() {

  12.    return "Student{" +  
    
  13.            "name='" + name + '\'' +  
    
  14.            ", age=" + age +  
    
  15.            ", score=" + score +  
    
  16.            '}';  
    
  17. }

  18. //因为此时实现了Compareable接口,则需要重写其内部的抽象方法compareTo

  19. @Override

  20. public int compareTo(Student o) {

  21.    //通过分数来进行排序,如果想通过年龄等直接修改score替换成age即可  
    
  22.    //如果大于的时候return 1,小于return -1,说明是按照从小到大的顺序排列的
    
  23.    //如果大于的时候return -1,小于return 1,说明是按照从大到小的顺序排列的
    
  24.    if(this.score > o.score) {  
    
  25.        return 1;  
    
  26.    }else if(this.score == o.score) {  
    
  27.        return 0;  
    
  28.    }else {  
    
  29.        return -1;  
    
  30.    }  
    
  31. }

38.}

39.public class TestDemo2 {

  1. public static void main(String[] args) {

  2.    //如果想要对Student类的引用进行大小比较,就需要Student类去实现Comparable接口  
    
  3.    Student student1 = new Student("bit",18,79);  
    
  4.    Student student2 = new Student("gao",29,70);  
    
  5.    Student student3 = new Student("shasha",17,99);  
    
  6.    Student[] students = new Student[3];  
    
  7.    students[0] = student1;  
    
  8.    students[1] = student2;  
    
  9.    students[2] = student3;  
    
  10.    //sort方法默认从小到打排序  
    
  11.    Arrays.sort(students);  
    
  12.    System.out.println(Arrays.toString(students));  
    
  13. }

56.}

此时我们通过分数进行排序,在 sort 方法中会自动调用 compareTo 方法. compareTo 的参数是 Object , 其实传入的就是 Student 类型的对象. 然后比较当前对象和参数对象的大小关系(按分数来算).

如果当前对象应排在参数对象之前, 返回小于 0 的数字;

如果当前对象应排在参数对象之后, 返回大于 0 的数字;

如果当前对象和参数对象不分先后, 返回 0;

再次执行程序, 结果就符合预期了.

// 执行结果

[Student{name=‘gao’, age=29, score=70}, Student{name=‘bit’, age=18, score=79}, Student{name=‘shasha’, age=17, score=99}]

注意:

但是上述的比较方式是有局限性的,因为上述的比较方式中是直接把比较写死的,可以说只能比较年龄,不能比较姓名等其他东西,但是我现在就想比较除掉年龄的其他东西该怎么办?此时就用到了比较器.

对于 sort 方法来说, 需要传入的数组的每个对象都是 “可比较” 的, 需要具备 compareTo 这样的能力. 通过重写 compareTo 方法的方式, 就可以定义比较规则.

Comparator接口(比较器)


Comparable接口 与Comparator接口的区别

首先我们知道的是Compareable接口是定义在类的内部 来实现对象的比较的,而我们比较器Comparator是专门定义在类的外部的

代码示例

还是之前的学生类,不同的是Student类不再实现Compareable接口:

此时我们想根据分数来对学生进行排序,那就在类的外部重新定义一个public类去实现Comparator接口重写Comparator接口中的compare方法,以此来实现我们对于分数的比较和排序:来看代码:

首先来看Comparator接口中的compare方法:

在这里插入图片描述

来看对于分数比较的代码

//ScoreComparator.java类

public class ScoreComparator implements Comparator {

@Override

public int compare(Student o1, Student o2) {

return o1.score - o2.score;

}

}

如果还想要对于学生的名字进行比较:来看代码实现:

public class NameComparator implements Comparator {

@Override

public int compare(Student o1, Student o2) {

return o1.name.compareTo(o2.name);

}

}

这块的代码就不能再使用o1.name-o2.name这样的写法了,原因是我们的name是String类型,是不能进行相加减的,所以此处应该使用compareTo方法,有些同学可能就会纳闷了,compareTo方法出现于Compareable接口中,为什么要在这里使用这个呢?

答案如下:

我们先来看String类的源码:

在这里插入图片描述

可以看到String类实现了我们的Compareable接口,并重写了我们Compareable接口中的compareTo方法

在这里插入图片描述

所以关于名字的比较器我们可以知道它虽然实现了Comparator接口但是其内部使用的compareTo方法来源于Compareable接口.

下面来看主函数中是如何实现对于名字和分数的比较的:

class Student {

public String name;

public int score;

public Student(String name, int score) {

this.name = name;

this.score = score;

}

@Override

public String toString() {

return “Student{” +

“name='” + name + ‘’’ +

“, score=” + score +

‘}’;

}

}

public class TestDemo2 {

public static void main(String[] args) {

Student[] students = new Student[3];

students[0] = new Student(“bit”, 89);

students[1] = new Student(“abc”, 19);

students[2] = new Student(“htf”, 59);

System.out.println(“=没有排序前”);

System.out.println(Arrays.toString(students));

System.out.println(“=根据分数排序后”);

ScoreComparator scoreComparator = new ScoreComparator();

//传两个参数进来

Arrays.sort(students, scoreComparator);

//输出结果为:[Student{name=‘abc’, score=19}, Student{name=‘htf’, score=59}, Student{name=‘bit’, score=89}]

System.out.println(Arrays.toString(students));

System.out.println(“=根据姓名进行排序后”);

NameComparator nameComparator = new NameComparator();

Arrays.sort(students, nameComparator);

//输出结果为:[Student{name=‘abc’, score=19}, Student{name=‘bit’, score=89}, Student{name=‘htf’, score=59}]

//以上姓名的排序是按照首字母进行排序的

System.out.println(Arrays.toString(students));

}

}

接口间的继承(扩展)

=========================================================================

接口可以继承一个接口,也可以继承(扩展)多个接口,已达到复用的效果. 使用 extends 关键字.当然这里的继承关系我们把它理解成扩展的意思更为准确

interface IRunning { void run();

}

interface ISwimming { void swim();

}

// 两栖的动物, 既能跑, 也能游

interface IAmphibious extends IRunning, ISwimming {

//此时IAmphibious这个接口相当于有了两个方法,一个是run,一个是swim

}

class Frog implements IAmphibious {

//此时Frog这个类必须重写IAmphibious这个接口的两个抽象方法,一个run方法,一个swim方法

}

通过接口继承创建一个新的接口 IAmphibious 表示 “两栖的”. 此时实现接口创建的 Frog 类, 就继续要实现 run 方法, 也需要实现 swim 方法.

总结

=================================================================

抽象类和接口都是 Java 中多态的常见使用方式. 都需要重点掌握. 同时又要认清两者的区别(重要!!! 常见面试题).

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

如之前写的 Animal 例子. 此处的 Animal 中包含一个 name 这样的属性, 这个属性在任何子类中都是存在的. 因此此处的 Animal 只能作为一个抽象类, 而不应该成为一个接口.

最后

如果觉得本文对你有帮助的话,不妨给我点个赞,关注一下吧!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

现接口创建的 Frog 类, 就继续要实现 run 方法, 也需要实现 swim 方法.

总结

=================================================================

抽象类和接口都是 Java 中多态的常见使用方式. 都需要重点掌握. 同时又要认清两者的区别(重要!!! 常见面试题).

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

如之前写的 Animal 例子. 此处的 Animal 中包含一个 name 这样的属性, 这个属性在任何子类中都是存在的. 因此此处的 Animal 只能作为一个抽象类, 而不应该成为一个接口.

最后

如果觉得本文对你有帮助的话,不妨给我点个赞,关注一下吧!

[外链图片转存中…(img-GJdJKdDU-1713614306623)]

[外链图片转存中…(img-UpH05Hl2-1713614306623)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-8vKNOogG-1713614306624)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值