【Java】/*抽象类和接口*/

目录

一、抽象类和抽象方法

1.1 概念

1.2 特性

1.3 作用

二、接口

2.1 概念及定义

2.2 特性

2.3 实例:笔记本电脑

2.4 一个类可以实现多个接口

2.5 一个接口可以继承多个接口

2.6 Comparable接口

2.7 Comparator接口

2.8 Cloneable接口

2.9 浅拷贝和深拷贝

2.10 抽象类和接口的区别

三、Object类

3.1 概念

3.2 获取对象信息

3.3 对象比较equals方法

2.4 hashcode方法


一、抽象类和抽象方法

1.1 概念

1. 抽象类:如果一个类没有足够的信息来描述一个对象(这个类通常是父类),那么这个类可以用abstract修饰成为抽象类

2. 抽象方法:抽象类中被abstract修饰的方法称为抽象方法,抽象方法可以不具体实现

1.2 特性

① 抽象类也是类,内部可以有成员和构造方法等,也可以发生向上转型,方法重写,多态。

② abstract不能修饰被private、static、final修饰的方法,因为抽象方法要被子类重写(被重写的方法要满足能被重写的条件)

③ 抽象方法可以不具体实现,如果具体实现了反而会报错。

④ 如果一个类中包含抽象方法,那么这个类必定是抽象类;但抽象类中不一定有抽象方法。

抽象类不能实例化对象只能被继承(和被final修饰的类效果相反)并且继承抽象类的子类A必须要重写抽象类中的抽象方法,如果不重写,得将子类A也得写成抽象类,那么当子类B继承子类A后就得重写子类A的父类和子类A中所有没被重写的方法(出来混总是要还的)。

/*抽象方法*/
abstract class Father {/*抽象类*/public abstract void funcA();
}/*抽象方法*/
abstract class Son extends Father {/*抽象类*/public abstract void funcB();
}class GrandSon1 extends Son {/*重写Father类中的抽象方法*/@Overridepublic void funcA() {System.out.println("我是爷爷的第一个孙子");}/*重写Son类中的抽象方法*/@Overridepublic void funcB() {System.out.println("我是爸爸的第一个儿子");}
}class GrandSon2 extends Son {/*重写Father类中的抽象方法*/@Overridepublic void funcA() {System.out.println("我是爷爷的第二个孙子");}/*重写Son类中的抽象方法*/@Overridepublic void funcB() {System.out.println("我是爸爸的第二个儿子");}
}public class Test01 {public static void funcDemo1(Father father) {//向上转型成Father类,只能调用Father类有的成员father.funcA();}public static void funcDemo2(Son son) {//向上转型成Son类,由于Son类继承了Father类,//此时能调用Father类和Son类有的成员,//所以无论在抽象类有没有继承抽象类的情况下//记住向上转型成最后一个抽象类即可son.funcA();son.funcB();}public static void main(String[] args) {GrandSon1 grandSon1 = new GrandSon1();GrandSon2 grandSon2 = new GrandSon2();//Test01.funcDemo1(grandSon1);//Test01.funcDemo1(grandSon2);Test01.funcDemo2(grandSon1);Test01.funcDemo2(grandSon2);}
}

1.3 作用

抽象类本身不能被实例化,要想使用只能创建该抽象类的子类,然后让子类重写抽象类中的抽象方法。有些人可能会说了, 普通的类也可以被继承呀, 普通的方法也可以被重写呀, 为啥非得用抽象类和抽象方法呢? 确实如此,但是使用抽象类相当于多了一重编译器的校验。使用抽象类的场景就如上面的代码, 实际工作不应该由父类的方法完成, 而应由子类重写的方法完成,那么如果不小心忘记在子类中重写父类中的方法了,在方法调用的过程中父类使用普通类编译器是不会报错的,但是父类是抽象类就会在子类继承父类后提示需要重写父类中的方法, 让我们尽早发现代码中的逻辑问题。

很多语法存在的意义都是为了 "预防出错",例如我们曾经用过的 final 也是类似。创建的变量用户不去修改, 不就相当于常量嘛? 但是加上 final 能够在不小心误修改的时候, 让编译器及时提醒我们。充分利用编译器的校验, 在实际开发中是非常有意义的。

二、接口

2.1 概念及定义

概念:接口(英文:Interface),在JAVA编程语言中是一个抽象引用数据类型,是抽象方法的集合,接口用interface关键字来定义。

定义:

① 定义接口要使用interface关键字(接口并不是类)。

② 接口的命名一般以大写字母I开头,并且命名一般使用形容词性的单词。

③ 接口中的成员可以有成员变量和成员方法,但成员变量必须是public static final修饰的,成员方法必须是public abstract修饰的(也就是说,接口中的成员变量必须是公开可以使用的常量,接口中的成员方法必须是抽象方法),阿里编码规范中约定,接口中的方法和属性不要加任何的修饰符号,因此我们在写接口中的成员时可以不写前面的修饰,编译器会自动修饰。

④ 接口前面不要用abstract修饰,因为接口本身比抽象类更抽象。

类实现接口的格式:

2.2 特性

① 抽象类和接口都不能实例化对象,抽象类用来被类继承,接口用来被类实现。

② 当一个类实现一个接口时意味着要在这个类中重写接口中所有抽象方法,如果在这个类中不想重写,则这个类必须定义成抽象类,继承这个抽象类的类得重写接口和这个抽象类中的所有抽象方法。

③ 接口也可以发生向上转型,动态绑定,多态(动态绑定是原理,多态是思想)。

④ 接口定义好后也会有一个单独的Java文件,编译完成后生成的字节码文件的后缀也是(.class)。

⑤ 接口中不能有静态代码块,构造代码块,构造方法等。

⑥ 重写接口中的方法时,方法只能是public修饰的(因为重写方法的权限要大于等于重写前的权限,而接口中的成员方法默认是public的)。

⑦ 接口中被 default 或 static 修饰的方法要写清楚具体实现的语句,且被实的接口中default成员方法可以不重写,也可以被重写,并不会像接口中的抽象方法一样强制要重写。

2.3 实例:笔记本电脑

实现笔记本电脑使用USB鼠标、USB键盘的例子

1. USB接口:包含打开设备、关闭设备功能

2. 笔记本类:包含开机功能、关机功能、使用USB设备功能

3. 鼠标类:实现USB接口,并具备点击功能

4. 键盘类:实现USB接口,并具备输入功能

 

2.4 一个类可以实现多个接口

1. 在Java中,类和类之间只能是单继承的,也就是说一个类只能有一个父类,但一个类可以实现多个接口,从而间接达到实现多继承的目的

2. 一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类

3. 例如:根据我们当前掌握的知识当我们想要描述狗、鱼、鸭这三种动物时,我们会定义狗、鱼、鸭这三个类,并且由于它们之间共属于动物,我们可以定义一个父类Animal,从而达到代码复用的效果。但是在定义的过程中我们会发现一个问题,游泳、跑步、飞行这三个行为,如果我们把它们定义在父类Animal中并不稳妥,因为继承是对子类共性的抽取,并不是所有的动物都同时具备这三个行为;如果我们把它们定义在狗、鱼、鸭的各自类中,这又会造成代码冗余;如果我们把它们定义在一个新的类A中,当我们定义狗、鱼、鸭类时又会发现类是不支持多继承的,继承了Animal类就不能再继承A类了。在这种场景下,我们把游泳、跑步、飞行这三个行为定义在一个接口中最为合适,这样子类既可以继承Animal类也可以根据需要拥有游泳/跑步/飞行这三个行为,而且不会造成代码冗余。于是我们可以写下如下图代码,该代码展示了 Java 面向对象编程中最常见的用法:一个类继承一个父类, 同时实现了多个接口。

4. 使用接口的好处:让程序员忘记类型。例如,下图中用绿色方框框起来的方法,参数部分采用了向上转型传参数的方式,在调用该方法传参时程序员可以不用考虑传入的参数是什么类,只需要关心传入的参数的类中是否实现了对应的接口。

 

2.5 一个接口可以继承多个接口

1. 一个接口可以继承多个接口,从而达到复用的效果,关键字也是extends。

2. 接口的继承相当于把多个接口合并在一起

3. 下图代码中接口C继承了接口A、B,实现接口C的TestNew类要重写接口A、接口B、接口C中所有的抽象方法,否则TestNew类得定义成抽象类。

 

2.6 Comparable接口

1. 关系运算符只能比较基本数据类型数据的大小,引用数据类型的数据比较大小要用到Comparable接口中的compareTo方法。

2. 具体做法:为将要比较大小的引用数据类型的对象它对应的类实现Comparable接口,在类中重写接口中的compareTo方法,compareTo方法的内部自定义根据类的哪一种成员变量进行比较。我们在比较该引用数据类型的对象的大小时,直接用对象的引用调用compareTo方法并传参即可,不用定义一个新的方法使用向上转型(这样写有点多此一举了),举例代码如下图所示。

3. Arrays.sort方法在排序装有引用数据类型的数组时,会先将数组中的元素强制转换成Comparable类型,然后调用compareTo方法比较两个相邻数据的大小,最后根据比较的结果对数组中相邻两个元素进行位置调整。

这也就是说使用Arrays.sort方法对装有引用数据类型数据的数组进行排序时,也得先让对应引用数据类型的类实现Comparable接口,并在类中重写了compareTo方法后才能正常进行排序,且值得注意的是,也就是说Arrays.sort排序的逻辑与compareTo内部实现比较大小的依据有着极大的关联,当compareTo内部根据年龄、姓名等比较大小时可能会造成不同的排序结果。

4. 使用Comparable接口中的compareTo方法来比较两个引用数据类型的大小的局限性在于,一旦把compareTo中的内容写下了之后,以后在比较该引用数据类型对象的大小时调用compareTo方法只能按照compareTo方法中依据的东西判断大小了。

 5. 写一个自己的sort方法,实现和Arrays.sort方法一样的功能(采用冒泡排序):

2.7 Comparator接口

1. 针对Comparable接口的局限性,在比较某个类实例化的两个对象的大小时,我们可以采用实现Comparator接口对里面的compare方法进行重写,而不去采用实现Comparable接口。

2. 具体做法:重新定义新的类(A)去实现Comparator接口,<>里面写上要比较的类,使用时直接new一个A类的对象a,使用a调用compare方法并传参即可;我们可以按照这种方式写多个比较器来达到目的;如果Comparator接口由比较对象的类实现,则会达不到想要的效果,如下图二所示。

3. 我们在调用Arrays.sort方法时也可以将比较器作为参数传递给sort方法,使得sort方法在排序装有引用数据类型的数组时,比较相邻的两个元素的大小采用比较器中的compare方法,而不再采用Comparable中的compareTo方法。

2.8 Cloneable接口

1. 使用场景:想要克隆某个对象时。

2. 实现步骤:

    ① 给该对象对应的类实现Clonable接口,但并不用重写Cloneable中的接口,因为这个接口里面并没有抽象方法,此时这个接口叫做空接口/标记接口,这种接口的意义在于只有实现了该接口才能证明你有该功能,除此之外没有什么特殊的意义。

   ② 调用Object类中的clone方法。但我们不能直接调用,因为Object类中的clone方法被protected修饰了,此时是不能在不同包的非子类中使用的,

于是我们可以先在子类中重写Object的克隆方法,在这个重写的克隆方法内部直接返回原来克隆方法的返回值即可,因为我们调用克隆方法的目的本来就是得到克隆后对象的地址,在不同包的子类中是可以调用Object的clone方法的。

-》补充:在子类中快速重写Object类中方法的快捷方式

3. 由于clone方法的返回值是Object类型的,因此在接收clone方法的返回值时,要强制类型转换一下(Object类是所有类的父类,这里要发生向下转型)。

4. 当上面的步骤都做好后,代码运行时还会报错,此时是由于异常原因,解决的方法是,在main方法参数列表的后面加上clone方法参数列表后的“throws CloneNotSupportedException”即可,异常问题后面的文章中会讲到。

-》下图是示例代码:

-》上图代码在内存中的示意图: 

 

2.9 浅拷贝和深拷贝

1. 浅拷贝:修改上文Cloneable接口中的代码,即给Student类增加一个成员变量(该成员变量是Money类创建的一个对象),并保持其他的代码不变。代码和代码在内存中的示意图如下图所示。

我们会发现一个问题,克隆出的student2中成员变量m指向的对象和student1中成员变量m指向的对象一样,当我们在修改student1/student2中m对象的money值时,student1和student2中m对象的money值都会变,这种现象被称为浅拷贝(并没有实现真正的拷贝效果)。

2. 深拷贝:为了实现真正的拷贝效果,我们可以修改上述代码中重写的clone方法来达到目的。具体操作是,在Student类重写的clone方法中先调用Object类中的clone方法,克隆一下this所指向的对象,并让临时的Student类对象tmp来接收,然后为Money类实现Cloneable接口并重写clone方法,接着克隆一下this中的m对象,并让tmp中的m接收,最后返回tmp即可达到真正的拷贝效果,代码和代码在内存中的分析如下图所示。

2.10 抽象类和接口的区别

1. 抽象类和接口都是 Java 中多态的常见使用方式。

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

3. 再次提醒: 抽象类存在的意义是为了让编译器更好的校验, 像 Animal 这样的类我们并不会直接使用, 而是使用它的子类. 万一不小心创建了 Animal 的实例, 编译器会及时提醒我们。

三、Object类

3.1 概念

1. Object类是所有类的父类。

2. 对于整个Object类中的方法需要实现全部掌握,本小节当中,我们主要来熟悉这几个方法:toString()方法,equals()方法,hashcode()方法。

3. 在ideal中如何看到Object类中所有的方法?

    做法:双击shift,在出现的对话框中勾选Include non-project items,并在搜索框中搜索Object,点击第一个Object,然后在ideal页面的左下角点击Structure即可看到Object类中的所有的方法。

3.2 获取对象信息

如果要打印对象的信息,可以通过重写Object类中的toString()方法,在打印时:对于单个对象直接用sout打印对象的名称即可,对于装有多个对象的数组来说用sout打印Arrays.toString的返回值即可,具体示例代码如下图所示。

3.3 对象比较equals方法

1. Object类中的equals方法的功能是比较两个引用所指向的对象是否为同一个对象,如果是就返回true,否就返回false,并且Object类中的equals方法是用public修饰的哦~。

2. 也就是说当我们想比较两个对象中的内容是否相同时得重写Object类中的equals方法。并且ideal提供了快捷方式,具体做法:单机右键,Generate,点击equals() and hashCode(),然后一路next。

2.4 hashcode方法

1. toString方法的源码中有一个hashCode()方法,它可以帮我们算一个具体的对象位置(我们可以暂时理解为内存地址)。Integer.toHexString()方法,可以将这个地址以16进制输出。

2. hashCode()方法是一个native方法,底层是由C/C++代码写的,所以我们看不到。

3. 如果在某个业务场景中我们需要实现,如果对象的成员变量都一致,我们就认为是同一个人的话,我们可以通过重写hashCode方法来达到目的。ideal提供了快捷方式,具体做法:单机右键,Generate,点击equals() and hashCode(),然后一路next。

4. 重写hashCode前:

 

    重写hashCode后:

  本篇文章已完结,谢谢支持哟 ^^ !!! 

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

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

相关文章

【开发心得】三步本地化部署llama3大模型

目录 第一步&#xff1a;启动ollama 第二步&#xff1a;启动dify 第三步&#xff1a;配置模型&#xff08;截图&#xff09; 最近llama3很火&#xff0c;本文追击热点&#xff0c;做一个本地化部署的尝试&#xff0c;结果还成功了&#xff01; 当然也是站在别人的肩膀上&…

嘉立创面板制作不规则图案技巧

首先附上效果图展示&#xff1a; 所需软件&#xff1a;嘉立创EDA(专业版)、photoshop、Adobe Illustrator 嘉立创EDA(专业版)&#xff1a; 嘉立创面板绘制很容易上手&#xff0c;只要了解这几个图层的作用便可以做出自己想要的面板。 材料边界层&#xff1a; 代表选⽤的材料…

验证码案例

目录 前言 一、Hutool工具介绍 1.1 Maven 1.2 介绍 1.3 实现类 二、验证码案例 2.1 需求 2.2 约定前后端交互接口 2.2.1 需求分析 2.2.2 接口定义 2.3 后端生成验证码 2.4 前端接收验证码图片 2.5 后端校验验证码 2.6 前端校验验证码 2.7 后端完整代码 前言…

EverWeb 强大的零基础Mac网页设计制作软件

搜索Mac软件之家下载EverWeb 强大的零基础Mac网页设计制作软件 EverWeb 4.2是非专业网页设计师的绝佳网页制作工具&#xff0c;无需编码即可创建美观、响应迅速的网站。只需拖放自己的图像、文本和其他任何html元素到网页布局的任何位置。 EverWeb的功能特性&#xff1a; 下…

《Brave New Words 》1.1 抛弃瓶子

Part I: Rise of the AI Tutor 第一部分&#xff1a;AI 导师的崛起 A great teacher can teach calculus with a paper clip and literature in an empty field. Technology is just another tool, not a destination. —Unknown 一位伟大的教师可以用回形针教微积分&#xff0…

R语言探索与分析18-基于时间序列的汇率预测

一、研究背景与意义 汇率是指两个国家之间的货币兑换比率&#xff0c;而且在国家与国家的经济交流有着举足轻重的作用。随着经济全球化的不断深入&#xff0c;在整个全球经济体中&#xff0c;汇率还是一个评估国家与国家之间的经济状况和发展水平的一个风向标。汇率的变动会对…

doris FE 在Windows环境下编译调试开发环境

前言&#xff1a; doris fe 在win下调试运行&#xff0c;和正常java项目有一些差异&#xff0c;主要是有与be&#xff08;c&#xff09;通信代码的生成 在win环境下不能直接生成&#xff0c;因此需要现在linux下生成之后&#xff0c;再拷贝到本地来&#xff0c;然后进行编译&a…

7天搞定Python必背500单词

必备必记-你的Python就牛掰了 每天只背100个就足够了 老话说的好基础不扎实,地动山摇,在学习Python的时候前期基础很重要. 下面是大家常用遇到的Python基础单词,帮助你更好地掌握Python语言: 1.变量 在Python中用来存储数值,文本或其他信息的名称. 2. 函数 用于执行特定…

什么是Docker ?

在软件开发的星辰大海中&#xff0c;有一个神奇的技术&#xff0c;它能够将应用程序及其依赖环境封装在一个轻量级的、可移植的容器中。这项技术就是Docker。它不仅简化了应用的部署流程&#xff0c;还让开发和运维之间的界限变得模糊&#xff0c;使得跨平台部署变得前所未有的…

kafka-消费者服务搭建配置简单消费(SpringBoot整合Kafka)

文章目录 1、使用efak 创建 主题 my_topic1 并建立6个分区并给每个分区建立3个副本2、创建生产者发送消息3、application.yml配置4、创建消费者监听器5、创建SpringBoot启动类6、屏蔽 kafka debug 日志 logback.xml7、引入spring-kafka依赖 1、使用efak 创建 主题 my_topic1 并…

cisco packet tracer 8.2.2 (思科模拟器) ospf路由协议

1 实验拓扑图 2 配置路由器和交换机 #sw1 en config t hostname sw1 ip routing int vlan 2 ip address 192.168.2.1 255.255.255.0 exit int vlan 3 ip address 192.168.3.1 255.255.255.0 exit int gigabitEthernet 1/0/1 switchport access vlan 2 exit int gigabitEthe…

每日5题Day19 - LeetCode 91 - 95

每一步向前都是向自己的梦想更近一步&#xff0c;坚持不懈&#xff0c;勇往直前&#xff01; 第一题&#xff1a;91. 解码方法 - 力扣&#xff08;LeetCode&#xff09; class Solution {public int numDecodings(String s) {int n s.length();//注意我们dp的范围是n1int[] d…

记一次postgresql拼接函数string_agg() 和row_number() 使用

PG两个函数使用需求和简单介绍 需求背景介绍第一个需求背景是这样的需求升级一下接下来讲讲STRING_AGG()基本语法排序 然后我们再说说ROW_NUMBER()基本语法使用 row_number() over (partition by) 进行分组统计使用 row_num限定每组数量 需求背景介绍 第一个需求背景是这样的 …

Paper速读-[Visual Prompt Multi-Modal Tracking]-Dlut.edu-CVPR2023

文章目录 简介关于具体的思路问题描述算法细节 实验结果模型的潜力模型结果 论文链接&#xff1a;Visual Prompt Multi-Modal Tracking 开源代码&#xff1a;Official implementation of ViPT 简介 这篇文章说了个什么事情呢&#xff0c;来咱们先看简单的介绍图 简单来说&am…

整除及求余运算符、数字的提取、顺序结构程序

1.运算符 在有余数的除法运算中&#xff0c;如果要知道商和余数分别是多少&#xff0c;可以用/和%这两个运算符号来得到。 (1)/(整除)&#xff0c;当被除数和除数均为整数时&#xff0c;结果也为整型&#xff0c;只取商的整数部分。 如:10/25 10/33 5/10 0 (2)%(求余)&…

NineData云原生智能数据管理平台新功能发布|2024年5月版

重点发布​ 数据库 DevOps - 表分组查询​ 在企业用户规模达到一定程度后&#xff0c;分库分表成为一种常见的数据库架构选择。在这种情况下&#xff0c;查询和维护数据需要高效的解决方案&#xff0c;以避免手动逐一查询、变更和汇总多个分库和分表的繁琐操作。 库分组变更…

电脑开机出现英文字母,如何解决这个常见问题?

电脑开机时出现英文字母的情况通常意味着系统在启动过程中遇到了问题。这些英文字母可能是错误信息、系统提示或BIOS设置问题。通过理解这些信息并采取适当的措施&#xff0c;您可以解决大多数启动问题。本文将介绍三种解决电脑开机出现英文字母问题的方法&#xff0c;帮助您恢…

C++笔试强训day42

目录 1.最大差值 2.兑换零钱 3.小红的子串 1.最大差值 链接https://www.nowcoder.com/practice/a01abbdc52ba4d5f8777fb5dae91b204?tpId182&tqId34396&rp1&ru/exam/company&qru/exam/company&sourceUrl%2Fexam%2Fcompany&difficulty2&judgeSta…

你还在纠结U盘怎么选吗?小白带你来看

前言 2024年的618活动已经开始了&#xff0c;这个活动买电子产品着实是比其他时间要便宜很多。 前几天小白的一个好朋友问我&#xff1a;U盘该怎么选&#xff1f; 呃&#xff0c;本来是想写“老朋友”的&#xff0c;结果她愣是要我改成“好朋友”。 行吧&#xff0c;那就好朋…

基于睡眠声音评估睡眠质量

随着健康意识的增强&#xff0c;人们越来越关注睡眠质量。确保获得充足的高质量睡眠对于维持身体健康和心理平衡至关重要。专业的睡眠状态测量主要通过多导睡眠图&#xff08;PSG&#xff09;进行。然而&#xff0c;PSG会给受试者带来显著的身体负担&#xff0c;并且在没有专业…