java中的抽象类与接口(面试常考,重要)!!,美团c++面试

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

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

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

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

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

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

正文

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

很多语法存在的意义都是为了 “预防出错”, 例如我们曾经用过的 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(“□”);

}

}

在这里插入图片描述

类实现多个接口


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

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

接口使用实例(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;

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

最后

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

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

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

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

  1.    Arrays.sort(students);  
    
  2.    System.out.println(Arrays.toString(students));  
    
  3. }

56.}

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

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

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

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

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

最后

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

[外链图片转存中…(img-tNGKUWuQ-1713410341879)]

[外链图片转存中…(img-d1aJO0gz-1713410341879)]

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

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

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

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值