当前位置: 首页 > news >正文

硅基计划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

相关文章:

  • JavaWeb笔记
  • MySQL查询语句
  • pikachu靶场通关笔记17 CSRF关卡03-CSRF(Token)
  • [蓝桥杯]矩阵翻硬币
  • maven微服务${revision}依赖打包无法识别
  • 王自如走上了罗永浩的老路
  • 使用VTK还是OpenGL集成到qt程序里哪个好?
  • 马氏链(Markov Chain Model)模型知识详解(1)
  • 使用 espeak-ng 实现文本转语音
  • 基于 Vue 和 Spring Boot 实现滑块验证码的机器验证
  • Qwen2.5-VL - FFN(前馈神经网络)Feedforward Neural Network
  • Java持久层技术对比:Hibernate、MyBatis与JPA的选择与应用
  • npm error Cannot read properties of null (reading ‘matches‘)
  • mamba架构和transformer区别
  • 使用Python和TensorFlow实现图像分类
  • Qemu arm操作系统开发环境
  • PDF文件如何转换格式?简单教程来了
  • 【知识扫盲】如何由inq,ouq和totaltime计算tokens/s
  • Java项目中常用的中间件及其高频问题避坑
  • Day14
  • 视频网站做游戏分发/品牌推广方案怎么写
  • 德州做网站公司/nba西部排名
  • 欧美设计网站推荐/百度推广手机版
  • 2015年做那个网站能致富/免费推广网站推荐
  • 艺术公司网站定制/百度网络推广
  • 微信小程序怎么添加/杭州网站运营十年乐云seo