硅基计划2.0 学习总结 陆 抽象类与接口

图 (878)


文章目录

    • 一、抽象类
      • 1. 定义
      • 2. 示例代码
      • 3. 特性
    • 二、接口初识
      • 1. 定义
      • 2. 命名与语法
      • 3. 示例代码
      • 4. 常见特性
      • 5. 多接口实现
      • 6. 接口的继承
    • 三、Object类初识
      • 1. equals方法
      • 2. hascode方法


一、抽象类

1. 定义

  • 请你假设这样一个场景,我们定义一个人的类,这个类中我们定义一个成员方法void sleep(),然后我们派生出两个子类Student()类和Teacher()类,然后再在子类中写出各自的sleep()方法,也能实现多态
  • 但是,今天我们要讲的是抽象类和抽象方法,因此在语法上,抽象类指的是被abstract关键字修饰的类,进而抽象方法就指的是被abstract关键字修饰的方法
  • 在抽象类和抽象方法中,没有具体的实现,在人的类中,我们描述这个人的类很虚幻,很宽泛,因而我们的abstract void sleep();方法中并无具体的内容,就单纯是个方法的展示,说明人具有睡觉的习性,而未定义具体怎么睡的
  • 那你可能就会问了,那我在此处不定义,难道在子类中再重新定义吗(即重写),是的,我们创建抽象方法的意义就是为了在子类中重写
  • 那聪明的你也会问了,我都有继承了我为什么还要定义抽象类呢,其实这样主要是为了在子类继承父类的时候,检查你是否有重写父类中的方法,便于校验,以免造成遗漏,而且这样规范性也更高了,进一步提高了代码的稳定性

2. 示例代码

//抽象类Person
public abstract class Person {String name;public Person(String name) {this.name = name;}abstract void sleep();
}
//Student类
public class Student extends Person{String classes;String stage;public Student(String name, String classes,String stage) {super(name);this.classes = classes;this.stage = stage;}@Overridevoid sleep() {System.out.println(name+classes+"学生在睡觉");}
}
//Teacher类
public class Teacher extends Person{String department;String subject;public Teacher(String name,String department, String subject) {super(name);this.department = department;this.subject = subject;}@Overridevoid sleep() {System.out.println(name+department+"老师在睡觉");}
}
//Test测试类
public class Test {public static void func(Person person){person.sleep();}public static void main(String[] args) {Person per1 = new Student("张三","计科二班","大一");Person per2 = new Teacher("王五","教务处","计算机科学与技术");func(per1);func(per2);}
}

3. 特性

  1. 可以和普通的类一样拥有成员变量和成员方法,不一定要有抽象方法,但是反之一个类中有抽象方法那么这个类必须为抽象类
    image-20250606153842693
  2. 抽象类不可以被实例化,但是它可以被继承,而且它的主要目的就是继承
  3. 当一个普通的类继承抽象类之后,这个普通类需要去重写抽象类中所有的抽象方法,不过若你不想自己手动写,直接Alt+回车查看,然后点击重写选项即可
    image-20250606154147307
  4. 可以有构造方法,跟普通方法一样,通过子类去初始化抽象类(父类)中的成员变量,因此抽象类中成员变量和构造方法功能与普通的父类近乎没有差别,刚刚的示例代码中就有
  5. 抽象类也可以发生动态绑定、向上转型、多态等,比如示例代码的Test测试类中的func方法
  6. 因抽象类之间的可继承性,若抽象类B继承了抽象类A,那么在抽象类B中可以不用重写抽象类A中的抽象方法,但是当一个普通类C去继承抽象类B的时候,不光要重写抽象类B中的抽象方法,还要重写抽象类A中的抽象方法,因为普通类C间接继承了抽象类A
  7. 抽象方法不能被private,final,static等关键字修饰,因为这样会导致不可被继承,进而就不能在子类中重写抽象方法了

Tips:如果你细心,你会发现抽象类的图标和普通类中的图标不一样


二、接口初识

1. 定义

  • 其实我也不好描述,你就把它理解为多个类的公共准则,即让类具有某些属性,一种引用的数据类型的规范,因此它可被多个类引用,就决定类其高度公有性与多个类之间共性
  1. 跟抽象类一样,接口类指的是被interface关键字修饰的
  2. 成员变量默认都是被public static final修饰,当你写入权限限定符时会显示是冗余的,并且在定义的时候还需要初始化,这一点跟类不一样
    image-20250606164402153
  3. 成员方法默认都是被public abstract,即都是抽象方法,因此接口类方法中不能有方法的具体定义,但是,凡事都有特殊情况,如果被static关键字或defeault关键字修饰后就可以
    image-20250606165030556
  4. 接口跟抽象类一样,它不可以实例化
    image-20250606165127124

2. 命名与语法

  • 接口内部有一套新的语法体系,命名一般是大写字母I+形容词
  • 内部方法与成员变量不能添加任何修饰符比如public、private等

3. 示例代码

  • 我们这里就以我们之前写过的学生类的代码做个演示,整体思路就是借助implement关键字去实现类,并且去重写接口中所有抽象方法
//首先我们定义一个接口IPerosn
public interface IPerson {void Attendclass();//定义上课void Classisover();//定义下课
}
//其次我们再定义两个子类Teacher和Student
//重写接口中的抽象方法,使其上课和下课的方式各不同
public class Student implements IPerson{@Overridepublic void Attendclass() {System.out.println("学生在上课");}@Overridepublic void Classisover() {System.out.println("学生下课离开了");}void note(){//定义学生特有方法System.out.println("记笔记");}
}public class Teacher implements IPerson{@Overridepublic void Attendclass() {System.out.println("老师在上课");}@Overridepublic void Classisover() {System.out.println("老师下课离开了");}void blackboard(){//定义老师特有方法System.out.println("粉笔写字");}
}
//再定义一个教室类Classroom用于把两个子类集中起来,一边协作
public class Classroom {//这个大类把两个子类集中起来void openDoor(){System.out.println("打开教室门");}void closeDoor(){System.out.println("关机教室门");}void learn(IPerson person){//定义协作方法person.Attendclass();if(person instanceof Student){Student stu = (Student) person;stu.note();}else if(person instanceof Teacher){Teacher ter = (Teacher) person;ter.blackboard();}person.Classisover();}
}
//我们再定义一个测试类,分别实例化教室类Classroom、学生类Student和老师类Teacher
public class Test {public static void main(String[] args) {Classroom classroom = new Classroom();Student stu = new Student();Teacher ter = new Teacher();classroom.openDoor();classroom.learn(stu);//调用协作方法classroom.learn(ter);//调用协作方法classroom.closeDoor();}
}

我来解释下这些代码,以便让你更加明白

  1. 首先我们在接口中定义两个需要在实现类中重写的两个抽象方法,仅仅声明,并无具体实现方法
  2. 我们再定义两个类StudentTeacher,来实现接口,再重写接口中的抽象方法,做后我们分别定义了当前类中特有方法note()blackboard()
  3. 我们再定义一个教室类Classroom去整合两个类StudentTeacher,在教室类Classroom中先定义两个特有方法openDoor()closeDoor(),再定义一个方法实现当前类Classroom和其他两个类StudentTeacher的协作方法learn,虽然接口不可实例化,但是它也可以引用传参,因此参数列表我们写IPerson person
  4. 此时你想,如果我用向上转型,是不是只能调用被重写的方法,不能调用类中特有的方法(因为你形参是IPerson person,接口引用去引用类对象),那怎么办呢,还记得向下转型吗,是的,我们就用向下转型去访问,但是不是说向下转型有风险吗,你可以看到在转型前我们先是会检查这个类实例化的是谁,即这个接口类引用到底引用的是哪个类,是Student还是Teacher呢,检查完之后我们就可以用向下转型去访问类中特有的方法了
  5. 最后我们再定义个测试类Test,去new了三个对象,再把Student类和Teacher类的对象传给Classroom类中的协作方法,这样我们就完成了接口的实现,是不是很神奇呢,别介,后面还有终极大招等着
    image-20250606173618736

4. 常见特性

  1. 在重写接口方法时候,不可使用默认的权限,要满足重写权限>=原抽象方法,但是呢又因为接口中方法默认是public修饰的,因此你重写后只能用public修饰了,这也能理解,接口毕竟就是为了对外嘛
  2. 之前也说过,在接口中也可以定义变量,一般被隐式指定为public static final
  3. 接口中不可以有代码块类型,譬如静态代码块、构造代码块、普通代码块等
  4. 接口中不可以有构造方法,毕竟你接口中的变量都要定义的时候就初始化,还要构造方法干嘛
  5. 如果你的类没有重写接口中的所有抽象方法,那你当前的类就要设置为抽象类
  6. 即使接口并不是class类,但是编译后还是能产生class文件
  7. 在JDK8之后,可以在接口中定义被default修饰的方法了

5. 多接口实现

  • 原理:接口间也满足继承的关系
  • 痛点分析:继承有个什么痛点,就拿我们之前写过的Person类来举例,我们把Person定义为抽象类,我们写了个抽象方法睡觉,也写了成员变量name,好,我们让两个类StudentTeacher类继承了这个抽象类,重写了sleep()抽象方法,之后再在测试类中去调用即可
  • 但是你想,我们可以把Teacher类和Student类中的特有方法写在抽象类中吗,不可以,学生上课记笔记老师上课一般会记笔记吗,不会,一般是教书,那你如果写进抽象类中,就会导致在Student类和Tracher类中去重写这两个方法,虽然语法上可以,但是逻辑上不可
  • 诶,你是不是想到了把各个特有的方法定义成一个类,再让Teacher类和Student类去继承呗,很遗憾,Java不支持多继承,怎么办呢!!!

解决方法:创建多个接口去实现

  • 我们把特有方法定义在接口中,再让Teacher类和Student类去实现
  • 因此我们可以把记笔记这个特有方法“封装”在一个个的接口,如果想实现多个接口,那么各个接口之间用逗号隔开即可,此时我们实现特定接口,我们再重写接口中的方法即可
  • 同时再在测试类中定义两个特有方法的接口public static void noting(INoteable noteable)public static void blackborading(IBlackboardable blackboardable),此时我们再把对应的对象传给特定的方法,此时你会惊奇的发现,我们无需关心对象到底是什么类型,我们只关心他能做什么,能干什么就好,只要他具备了这些特定方法的功能,我们都可以去调用,不论你是什么类型,比如不管你是学生还是老师,只要你会记笔记,你直接调用特定方法即可,这样就相当于给外界创建了个接口,只要你能连接上,甭管你是什么设备,都可以调用这个接口而无需知道其具体实现内容
  • 以下是示例代码,我们新定义了两个具有特有抽象类方法的接口INoteableIBlackboardable,在Test测试类中,我们定义了两个配合接口的特有方法notingblackborading,我们直接传具有各个特有方法的能力的对象就好noting(stu);
    blackborading(ter);
//其他代码都没变,这里只展示变了的代码
//Noteable接口
public interface INoteable {void note();
}
//IBlackboardable接口
public interface IBlackboardable {void blackboard();
}
//Test测试类中
public class Test {public static void noting(INoteable noteable){noteable.note();}public static void blackborading(IBlackboardable blackboardable){blackboardable.blackboard();}public static void main(String[] args) {//究极大招Student stu = new Student();Teacher ter = new Teacher();noting(stu);blackborading(ter);}
}

image-20250606184510860

6. 接口的继承

之前我们就讲过接口是可以继承的,具体是怎么样的呢?说白了就是接口的扩展

  1. 比如有一名优秀的学生,他既会讲课又会记笔记,那我们是不是可以再定义一个接口让其具有这两个接口的功能,因此我们用手extends关键字,在刚刚代码中我们要写两个接口的实现,此时我们只需要写一个接口的实现就好了,之后就是跟之前实现接口一样
  2. 此时你会发现因为这个接口兼并了其他接口,因此在子类中实现的时候需要重写所有被继承接口的抽象方法,但是若这个兼并的接口里面还是有其他抽象方法,你还是要在接口实现的类中去重写这个兼并接口中的抽象方法
  3. 代码展示
//定义了IExcellent接口,暂时不写IExcellen类中特有抽象方法
public interface IExcellent extends INoteable,IBlackboardable{
}
//定义了ExcleentStudent方法,可以发现重写了继承来的所有抽象方法
public class ExcleentStudent implements IExcellent {@Overridepublic void blackboard() {System.out.println("优秀学生在讲课");}@Overridepublic void note() {System.out.println("优秀学生在记笔记");}
}
//我们再在Test测试类中定义一个特殊方法来调用这个接口
public static void excleenting(IExcellent iexcellent){iexcellent.blackboard();iexcellent.note();}
//再在main方法中创建对象,再传对象,大功告成
public static void main(String[] args) {//究极大招ExcleentStudent stus = new ExcleentStudent();excleenting(stus);}

图 (289)


三、Object类初识

  • 我们之前说过,object类是所有类的父类,因此类中有些方法默认继承了Object中的方法

  • 我们可以通过双击shift键输入Object来查看,点击左上角结构(快捷键Alt+7)查看我们Object类中所有方法
    image-20250606212654423

  • 那既然它是所有类的父类,那么说明其是万能的类型,比如之前的代码Student student = new Student();我们就可以改写为Object student = new Student(......);,甚至是int a = 10;改写成object a = 10,既然都这样了为什么不写成object类型呢?

  • 因为其太宽泛了,不好易于区分不同类型数据

1. equals方法

  • 我们给个示例代码,拿我们刚刚演示的代码为例
public static void main(String[] args) {Person per1 = new Student("张三","计科二班","大一");Person per3 = new Student("张三","计科二班","大一");System.out.println(per1==per3);//结果为false
}

为什么结果是false,因为你不同对象即使内容一样,地址却是不一样的,比较的主要是地址,你想我用equals不就行了吗,System.out.println(per1.equals(per3));结果还是false,为什么,你转到源码去看看
image-20250606213755336

你发现返回的就过是当前对象,这不还是跟System.out.println(per1==per3);没有半毛钱区别吗,所以我们需要对equals方法进行重写,我们以后也会经常碰到需要重写的方法

  • 我们可以右键让编译器重写
    image-20250606214358351

  • 也可我们自己重写,这边演示我们自己重写,代码有注释,应该可以看懂

@Overridepublic boolean equals(Object obj){if(obj == null){return false;//如果你传的是空对象直接返回}if(this == obj){//如果你传的另外个对象是你本身的话return true;}if(!(obj instanceof Person)){//实例化的不是同个类return false;}//此时的情况就是同一个类的情况了,只需要比较内容是否一致就好Person temp = (Person) obj;//为什么此时向下转型安全,因为之前已经检查过了if(temp.name.equals(this.name)){//假设名字一样就整体相等return true;}return false;}

当然也可以简化下代码

@Overridepublic boolean equals(Object obj){if(obj == null){return false;//如果你传的是空对象直接返回}if(this == obj){//如果你传的另外个对象是你本身的话return true;}if(!(obj instanceof Person)){//实例化的不是同个类return false;}//此时的情况就是同一个类的情况了,只需要比较内容是否一致就好Person temp = (Person) obj;//为什么此时向下转型安全,因为之前已经检查过了return temp.name.equals(this.name);//简化了这里}

2. hascode方法

用于在散列表中获取位置,底层是用C/C++写的,看不到具体实现方法,但是如果你直接用object类的会有问题,通过per1.hashcode()per3.hashcode()不一样,因此我们需要对hashcode进行重写操作,我们直接右键生成,此时返回的是散列码进行比较了,发现其散列码就一样了
image-20250606220200749
image-20250606220239340

  • 你是否会好奇为啥我通过对象直接使用.去访问equals和hashcode方法呢,还记不记得之前说object类是所有类的父类,因此我们默认继承了object类这些方法,难道我们不能访问吗,显然可以访问,毕竟每个类对位object的子类

这里补充一点:我们传对象时候可以直接传new的对象或者是定义一个传对象的数组去传

func(new Dog());
func(new horse());
Person [] person = {new Student(),new Teacher()};

文章难免会有错误,欢迎指正

Git码云仓库链接

END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/pingmian/83840.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux命令基础(2)

su和exit命令 可以通过su命令切换到root账户 语法:su [-] 用户名 -符号是可选的,表示是否在切换用户后加载环境变量,建议带上 参数:用户名,表示要切换的用户,用户名可以省略,省略表示切换到ro…

C++算法训练营 Day10 栈与队列(1)

1.用栈实现队列 LeetCode:232.用栈实现队列 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty): 实现 MyQueue 类: void push(int x)将元素x推到队列的末尾 int pop(…

设计模式域——软件设计模式全集

摘要 软件设计模式是软件工程领域中经过验证的、可复用的解决方案,旨在解决常见的软件设计问题。它们是软件开发经验的总结,能够帮助开发人员在设计阶段快速找到合适的解决方案,提高代码的可维护性、可扩展性和可复用性。设计模式主要分为三…

【QT】自定义QWidget标题栏,可拖拽(拖拽时窗体变为normal大小),可最小/大化、关闭(图文详情)

目录 0.背景 1.详细实现 思路简介 .h文件 .cpp文件 0.背景 Qt Linux;项目遇到问题,解决后特此记录 项目需要,个性化的标题栏(是个widget),在传统的三个按钮(最大化、最小化、关闭&#xf…

如何用 pnpm patch 给 element-plus 打补丁修复线上 bug(以 2.4.4 修复 PR#15197 为例)

背景 在实际项目开发中,依赖的三方库(如 element-plus)难免会遇到 bug。有时候官方虽然已经修复,但新版本升级成本高,或者有兼容性风险。这时,给依赖打补丁是最优雅的解决方案之一。 本文以 element-plus…

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…

优化电脑的磁盘和驱动器提高电脑性能和延长硬盘寿命?

磁盘优化 磁盘清理: 使用系统自带的磁盘清理工具(如Windows的“磁盘清理”)删除不必要的文件。清空回收站。删除临时文件和缓存。 磁盘碎片整理(针对机械硬盘): 定期进行磁盘碎片整理,以提高文…

EDA断供危机下的冷思考:中国芯片设计软件的破局之道优雅草卓伊凡

EDA断供危机下的冷思考:中国芯片设计软件的破局之道优雅草卓伊凡 一、EDA是什么?芯片行业的”隐形基石” 1.1 EDA技术解析 EDA(Electronic Design Automation,电子设计自动化)是用于设计和验证集成电路的软件工具链…

Jpackage

简介 jpackage - 用于打包自包含 Java 应用程序的工具,是 JDK 14 引入的一个工具。 该工具将 Java 应用程序和 Java 运行时映像作为输入,并生成包含所有必要依赖项的 Java 应用程序映像。它将能够生成特定于平台的格式的本机包,例如包括打包 …

CRM管理软件的数据可视化功能使用技巧:让数据驱动决策

在当今数据驱动的商业环境中,CRM管理系统的数据可视化功能已成为企业优化客户管理、提升销售效率的核心工具。据企销客研究显示,具备优秀可视化能力的CRM系统,用户决策效率可提升47%。本文将深入解析如何通过数据可视化功能最大化CRM管理软件…

智慧充电:新能源汽车智慧充电桩的发展前景受哪些因素影响?

全球能源结构转型与碳中和目标的推进,新能源汽车产业迎来爆发式增长,而智慧充电桩作为其核心基础设施,发展前景备受关注。智慧充电不仅关乎用户充电体验的优化,更是电网平衡、能源效率提升的关键环节。 然而,其发展并…

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…

多模态大语言模型arxiv论文略读(105)

UnifiedMLLM: Enabling Unified Representation for Multi-modal Multi-tasks With Large Language Model ➡️ 论文标题:UnifiedMLLM: Enabling Unified Representation for Multi-modal Multi-tasks With Large Language Model ➡️ 论文作者:Zhaowei…

SQLServer中的存储过程与事务

一、存储过程的概念 1. 定义 存储过程(Stored Procedure)是一组预编译的 SQL 语句的集合,它们被存储在数据库中,可以通过指定存储过程的名称并执行来调用它们。存储过程可以接受输入参数、输出参数,并且可以返回执行…

使用UDP连接ssh

使用UDP连接ssh mosh简介两端安装moshWindows安装mosh 放行端口使用mosh登录Linuxdebug mosh简介 Mosh最大的特点是基于UDP方式传输,支持在服务端创建一个临时的Key供客户端一次性连接,退出后失效;也支持通过SSH的配置进行认证,但…

软件功能模块归属论证方法

文章目录 **一、核心设计原则****二、论证方法****三、常见决策模式****四、验证方法****五、反模式警示****总结** 在讨论软件功能点应该归属哪些模块时,并没有放之四海而皆准的固定方法,但可以通过系统化的论证和设计原则来做出合理决策。以下是常见的…

ServBay 1.13.0 更新,新增第三方反向代理/内网穿透

ServBay 作为一款简化本地开发环境搭建与管理的强大工具,致力于打造一个开箱即用、稳定可靠的本地开发平台,让用户专注于代码编写,提升开发效率。 ServBay 1.13.0 正式发布!本次更新聚焦于提升本地开发项目的外部可访问性、增强国…

如何利用乐维网管进行IP管理

IP管理是网络管理中的关键环节,对于保障网络的正常运行、提升资源利用效率以及保障网络安全等方面都具有不可忽视的重要性。乐维网管在IP管理方面具有多种实用功能,以下从IP规划与分配、IP状态监测、IP冲突处理、IP审计与报表生成四个方面,介…

Go语言学习-->go的跨平台编译

Go语言学习–>go的跨平台编译 默认我们go build的可执行文件都是当前操作系统可执行的文件,Go语言支持跨平台编译——在当前平台下编译其他平台的可执行文件。 eg:在windows界面的代码,编译完成后在linux上面运行 实现方式&#…

SpringBoot自动配置原理深度解析

一、引言 SpringBoot的"约定优于配置"理念极大地简化了Spring应用的开发流程,而其核心魔法就是自动配置(Auto-Configuration)。本文将深入剖析自动配置的实现原理,帮助开发者更好地理解和定制SpringBoot应用。 二、自动配置核心机制 1. Ena…