Java(十)---抽象类和接口

news2024/12/25 9:23:57

文章目录

  • 前言
  • 知识回顾
  • 1.抽象类
    • 1.1.抽象类语法
    • 1.2 抽象类特性
  • 2.接口
    • 2.1.接口的概念
    • 2.2 语法规则
    • 2.3 接口使用
    • 2.4 接口特性
    • 2.5 实现多个接口
  • 3.Object类
    • 3.1 获取对象信息
    • 3.2.对象比较equals方法
  • 4.接口使用实例
    • 4.1.Comparable
    • 4.2.Comparator
    • 4.3.Cloneable深拷贝和浅拷贝


前言

上几篇文章我们学习完了多态,继承还有类与对象,还差两个内容,一个是抽象类,另外一个是接口,这节课我们把这两个大内容一学完,面向对象的基本内容就学完啦。


知识回顾

我们之前一直创造的是动物类(Animial),这回我们创造一个形状(Shape)类。
Shape类

public class Shape {
    public void draw(){
        System.out.println("画一个形状");
    }
}

Circle类

public class Circle extends Shape {
    @Override
    public void draw() {
        System.out.println("画一个○...");
    }
}

Flower类

public class Flower extends Shape{

    @Override
    public void draw() {
        System.out.println("画一个❀...");
    }
}

大家会发现这个形状类,有点奇怪,我们发现, 父类 Shape 中的 draw 方法好像并没有什么实际工作, 主要的绘制图形都是由 Shape的各种子类的 draw 方法来完成的. 像这种没有实际工作的方法, 我们可以把它设计成一个 抽象方法(abstractmethod), 包含抽象方法的类我们称为 抽象类(abstract class)。


1.抽象类

1.1.抽象类语法

在Java中,一个类如果被 abstract 修饰称为抽象类,抽象类中被 abstract 修饰的方法称为抽象方法,抽象方法不用给出具体的实现体。
在这里插入图片描述

注意:抽象类也是类,内部可以包含普通方法和属性,甚至构造方法。

1.2 抽象类特性

  • 抽象类不能直接实例化对象
    在这里插入图片描述
  • 抽象方法不能是 private 的

在这里插入图片描述

  • . 抽象方法不能被final和static修饰,因为抽象方法要被子类重写
    *在这里插入图片描述

  • 抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也是抽象类,必须要使用 abstract 修饰

  • 在这里插入图片描述

  • 抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类

  • 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量。


2.接口

现在我们不想使用抽象类方法,去实现形状类,可以使用接口来表示。


2.1.接口的概念

在现实生活中,接口的例子比比皆是,比如:笔记本上的USB口,电源插座等
在这里插入图片描述
电脑的USB口上,可以插:U盘、鼠标、键盘…所有符合USB协议的设备
电源插座插孔上,可以插:电脑、电视机、电饭煲…所有符合规范的设备
通过上述例子可以看出:接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。
在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型

2.2 语法规则

接口的定义格式与定义类的格式基本相同,将class关键字换成 interface 关键字,就定义了一个接口。
提示:

  1. 创建接口时, 接口的命名一般以大写字母 I 开头.
  2. 接口的命名一般使用 “形容词” 词性的单词.
  3. 阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性.

2.3 接口使用

接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法

public class 类名称 implements 接口名称{
// …
}

注意:子类和父类之间是extends 继承关系,类与接口之间是 implements 实现关系。

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

  1. USB接口:包含打开设备、关闭设备功能
  2. 笔记本类:包含开机功能、关机功能、使用USB设备功能
  3. 鼠标类:实现USB接口,并具备点击功能
  4. 键盘类:实现USB接口,并具备输入功能

USB接口

public interface USB {
    public abstract void OpenDevice();
    public abstract void CloseDevice();
}

KeyBoard类

public class KeyBored implements USB{
    @Override
    public void OpenDevice() {
        System.out.println("打开键盘...");
    }

    @Override
    public void CloseDevice() {
        System.out.println("关闭键盘...");
    }
    public void input(){
        System.out.println("输入文字...");
    }
}

Mouse类

public class Mouse implements USB{
    @Override
    public void OpenDevice() {
        System.out.println("打开鼠标...");
    }

    @Override
    public void CloseDevice() {
        System.out.println("关闭鼠标...");
    }
    public void Click(){
        System.out.println("点击鼠标...");
    }

}

Computer类

public class Computer {
    public void Open(){
        System.out.println("打开电脑...");
    }
    public void Close(){
        System.out.println("关闭电脑...");
    }
    public void UseUSB(USB usb){
        usb.OpenDevice();
        if(usb instanceof Mouse){
            Mouse mouse=new Mouse();
            mouse.Click();
        } else if (usb instanceof KeyBored) {
            KeyBored keyBored=new KeyBored();
            keyBored.input();
        }
    }
}

Test类

public class Test {
    public static void main(String[] args) {
        Computer computer=new Computer();
        computer.Open();
        computer.UseUSB(new Mouse());
        computer.UseUSB(new KeyBored());
        computer.Close();
    }
}

2.4 接口特性

  1. 接口类型是一种引用类型,但是不能直接new接口的对象
    在这里插入图片描述
  2. 接口中每一个方法都是public的抽象方法, 即接口中的方法会被隐式的指定为 public abstract(只能是public abstract,其他修饰符都会报错)
    在这里插入图片描述
  3. 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现
  4. 重写接口中方法时,不能使用默认的访问权限
  5. 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量
  6. 接口中不能有静态代码块和构造方法
  7. 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class
  8. 如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类

2.5 实现多个接口

在Java中,类和类之间是单继承的,一个类只能有一个父类,即Java中不支持多继承,但是一个类可以实现多个接口。下面通过类来表示一组动物.
咱们再跟动物打一次交到,分别创造Animal抽象类,Dog类,Duck类,Fish类,以及Test类
Animal类

public abstract class Animal {
    public int age;
    public String name;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public abstract void eat();
    // 跑     飞    游泳  都写到当前Animal这个类?
}

Dog类

public class Dog extends Animal{
    public Dog(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(this.name+" 正在吃狗粮...");
    }
}

Duck类

public class Duck extends Animal{
    public Duck(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(this.name+" 正在吃鸭粮...");
    }
}

Fish类

public class Fish extends Animal{
    public Fish(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(this.name+" 正在吃鱼粮...");
    }
}

我们想个问题:每种动物都有其活动的行为方式,甚至有些动物有多种,那么这些方法—跑 飞 游泳 都写到当前Animal这个类?
答案是:使用接口
分别创造三个接口,分别是IRun,ISwim,IFly
ISwim接口

public interface ISwim {
    public abstract void Swim();
}

IFly接口

public interface IFly {
    public abstract void Fly();
}

IRun接口

public interface IRun {
    public abstract void Run();
}

Fish类+ISwim接口

public class Fish extends Animal implements ISwim {
    public Fish(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(this.name+" 正在吃鱼粮...");
    }

    @Override
    public void Swim() {
        System.out.println(this.name+ " 正在游泳...");
    }
}

Dog类+ISwim和IRun接口

public class Dog extends Animal implements ISwim, IRun{
    public Dog(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(this.name+" 正在吃狗粮...");
    }

    @Override
    public void Run() {
        System.out.println(this.name+ " 正在跑步...");
    }

    @Override
    public void Swim() {
        System.out.println(this.name+" 正在游泳...");
    }
}

Duck类+IFly和IRun和ISwim接口

public class Duck extends Animal implements IFly, IRun, ISwim {
    public Duck(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(this.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 特性 .
鱼是一种动物, 具有会飞的特性.
狗也是一种动物, 既能跑, 也能游泳
鸭子也是一种动物, 既能跑, 也能游, 还能飞
这样设计有什么好处呢? 时刻牢记多态的好处, 让程序猿忘记类型. 有了接口之后, 类的使用者就不必关注具体类型,而只关注某个类是否具备某种能力.
例如机器人也可以跑,那么就可以使用IRun接口
机器人类

public class Robet implements IRun {

    @Override
    public void Run() {
        System.out.println("机器人在跑");
    }
}

接口的使用案例等会再讲,先将Object类


3.Object类

Object是Java默认提供的一个类。Java里面除了Object类,所有的类都是存在继承关系的。默认会继承Object父类。即所有类的对象都可以使用Object的引用进行接收
当然Object类也有属于自己的方法

在这里插入图片描述
对于整个Object类中的方法需要实现全部掌握。
本小节当中,我们主要来熟悉这几个方法:toString()方法,equals()方法,hashcode()方法

3.1 获取对象信息

如果要打印对象中的内容,可以直接重写Object类中的toString()方法,之前已经讲过了,此处不再累赘.
在这里插入图片描述

3.2.对象比较equals方法

在Java中,= =(等号连在一块)进行比较时:
a.如果= =(等号连在一块)左右两侧是基本类型变量,比较的是变量中值是否相同
b.如果= =(等号连在一块)左右两侧是引用类型变量,比较的是引用变量地址是否相同
c.如果要比较对象中内容,必须重写Object中的equals方法,因为equals方法默认也是按照地址比较的:
我们再比较一个狗是都一样,就是看两者的每个属性是否一样就可(名字,年龄,品种…)

 Dog dog1=new Dog("旺财",9);
        Dog dog2=new Dog("旺财",9);
        System.out.println(dog2.equal(dog1));

在这里插入图片描述
结果是
在这里插入图片描述
如果是使用==
在这里插入图片描述
结果仍是
在这里插入图片描述
我们看看equal的原码

在这里插入图片描述
dog1==dog2一样,那么就需要我们自己去重写
在这里插入图片描述
结果为
在这里插入图片描述
hashCode方法以后再讲


4.接口使用实例

Java中也有一些早已指定好的接口,供我们使用
例如Comparable,Comparator 以及Cloneable。

4.1.Comparable

我们可以对两个数进行比较,但是如果要对自定义类怎么进行比较呢?
先定义一个Student类

class Student{
    public String name;
    public int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

}

在这里插入图片描述
这样比肯定是不行的,因为首先Student是个引用类型,在栈上开辟一个地址,没法比较,第二是,跟Student的那个属性进行比较?都不清楚。因此可以使用新的接口来实现该操作—Comparable

class Student implements Comparable<Student>{
    public String name;
    public int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int compareTo(Student o) {
        return this.age-o.age;
    }
}

然后就可以进行比较啦,同理也可以对姓名进行比较
在这里插入图片描述
但是现在发现了几个问题,1.多个学生怎么比较?2.每次都要修改类里面的compareTo方法,有什么更简单的。
答案就是使用Comparator比较器。

4.2.Comparator

首先先解决第二个问题。
使用Comparator接口,需要复写compare方法
在这里插入图片描述
并且再分别创造两个类,分别用于对Age进行比较,以及Name进行比较。
NameComparator类

public class NameComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name);
    }
}

AgeComparator类

public class AgeComparator implements Comparator<Student> {

    @Override
    public int compare(Student o1, Student o2) {
        return o1.age-o2.age;
    }
}

其中NameComparator类中的compareTo是String中的方法

在这里插入图片描述
只需要分别对AgeComparator和NameComparator进行初始化,就可以使用里面的方法,第二个问题就解决啦。
对于第一个问题,我们在数组的是时候,我们使用的Arrays.sort()进行对数组的排序,下来,我们也使用这个

在这里插入图片描述

 public static void main(String[] args) {
        Student[]students=new Student[3];
        students[0]=new Student("zhangsan",20);
        students[1]=new Student("lisi",10);
        students[2]=new Student("wangwu",19);
        AgeComparator ageComparator=new AgeComparator();
        NameComparator nameComparator=new NameComparator();
        System.out.println("排序前:"+Arrays.toString(students));
        Arrays.sort(students,ageComparator);
        System.out.println("排序后:"+Arrays.toString(students));
    }

便可以进行比较啦。

4.3.Cloneable深拷贝和浅拷贝

我们学习一下克隆。
在Object中有Clone的方法,我们需要进行重写。

class Person implements Cloneable{
    public String name;
    public int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class Test {


    public static void main(String[] args)throws CloneNotSupportedException {
        Person person1=new Person("zhangsan",8);

        Person person2=(Person) person1.clone();
        System.out.println(person2.toString());
    }
}

这个是实现的流程在这里插入图片描述
这个是图解

在这里插入图片描述
下面如果我们在创建一个Money类,会有什么情况呢

class Money implements Cloneable{
    public double money=9.9;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
class Person implements Cloneable{
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public String name;
    public int age;
    public Money m=new Money();
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }


}
public class Test {

    public static void main(String[] args)throws CloneNotSupportedException{
        Person person1=new Person("zhangsan",8);
        Person person2 = (Person)person1.clone();
        System.out.println("修改前:"+person1.m.money);
        System.out.println("修改前:"+person2.m.money);
        person2.m.money=99.99;
        System.out.println("修改后:"+person1.m.money);
        System.out.println("修改后:"+person2.m.money);

    }
}

结果

在这里插入图片描述
图解:
在这里插入图片描述
那么如何做到深拷贝呢?
需要对Person的clone方法进行重写。
在这里插入图片描述
结果:
在这里插入图片描述

图解


这就是深拷贝和浅拷贝。


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

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

相关文章

CCF-GESP 等级考试 2023年9月认证C++四级真题

2023年9月 一、单选题&#xff08;每题2分&#xff0c;共30分&#xff09; 第 1 题 ⼈们所使⽤的⼿机上安装的App通常指的是&#xff08; &#xff09;。 A. ⼀款操作系统B. ⼀款应⽤软件C. ⼀种通话设备D. 以上都不对 第 2 题 下列流程图的输出结果是&#xff1f;( ) A. 9B.…

2024/5/22 学习杂记

为什么功率放大电路在模电中经常提到&#xff1f; 模拟信号&#xff1a;它是连续变化的电信号&#xff0c;它在时间上和幅度上都是连续的&#xff0c;能够代表信息的连续变化。大多数物理量为模拟信号&#xff0c;如&#xff1a;温度、压力、流量… 非电物理量通过传感器变换成…

RabbitMQ 消息队列安装及入门

市面常见消息队列中间件对比 技术名称吞吐量 /IO/并发时效性&#xff08;类似延迟&#xff09;消息到达时间可用性可靠性优势应用场景activemq万级高高高简单易学中小型企业、项目rabbitmq万级极高&#xff08;微秒&#xff09;高极高生态好&#xff08;基本什么语言都支持&am…

告别付费!这款开源软件让你免费看高清电视直播!

文章目录 📖 介绍 📖🏡 演示环境 🏡📝 开源详情 📝🎯 软件介绍🚀 软件特点🎈 获取方式 🎈⚓️ 相关链接 ⚓️📖 介绍 📖 🔮 揭秘一款神奇的软件,让你轻松畅游电视直播的海洋,无需付费,无需繁琐设置,即可畅享海量高清节目!想要知道它是什么吗?跟…

如何改变echo在Linux下的输出颜色

文章目录 问题回答常规输出字体加粗斜体字带下划线闪烁效果 参考 问题 我正在尝试使用 echo 命令在终端中打印文本。 我想把文本打印成红色。我该怎么做&#xff1f; 回答 你可以使用 ANSI escape codes 定义控制输出颜色的变量。 ANSI escape codes是一种用于在文本中设置…

Gitee在已有项目基础上创建仓库中遇到的问题和解决

问题一&#xff1a;fatal: remote origin already exists 解释&#xff1a;当前仓库添加了一个名为"origin"的远程仓库配置&#xff0c;此时输入 git remote add origin https://xxx就会提示上面的内容。 解决方案1:移除旧的origin git remote remove origin 解决方案…

pyqt6入门案例

效果预览 hello.ui <?xml version"1.0" encoding"UTF-8"?> <ui version"4.0"><class>Dialog</class><widget class"QDialog" name"Dialog"><property name"geometry"><…

2024年5月份最新独角数卡使用USDT详细小白教程

直观配套视频教程 2024年5月份最新独角数卡安装及USDT使用详细小白教程 1、创建服务器 Centos或者Ubuntu2、宝塔面板开心版安装寶塔 Linux 面版 8.0.5 開心版 - 2024年1月12日 - 开心专区 - 异次元 - Powered by Discuz!Centos安装命令&#xff08;默认安装是 8.0.1 直接在线升…

PyMySQL:连接Python与MySQL的桥梁

系列文章目录 更新ing... MySQL操作全攻略&#xff1a;库、表、数据、事务全面指南深入探索MySQL SELECT查询&#xff1a;从基础到高级&#xff0c;解锁数据宝藏的密钥MySQL SELECT查询实战&#xff1a;练习题精选&#xff0c;提升你的数据库查询技能PyMySQL&#xff1a;连接P…

代码随想录算法训练营第三十四天 | 理论基础、455.分发饼干、376、摆动序列、53.最大子序和

目录 理论基础 455.分发饼干 思路 代码 376.摆动序列 思路 代码 53.最大子序和 思路 代码 理论基础 代码随想录 455.分发饼干 代码随想录 思路 可以是大饼干优先满足大胃口&#xff0c;也可以是小饼干优先满足小胃口。 代码 class Solution:def findContentChildre…

【深度学习】与【PyTorch实战】

目录 一、深度学习基础 1.1 神经网络简介 1.2 激活函数 1.3 损失函数 1.4 优化算法 二、PyTorch基础 2.1 PyTorch简介 2.2 张量操作 2.3 构建神经网络 2.4训练模型 2.5 模型评估 三、PyTorch实战 3.1 数据加载与预处理 3.2 模型定义与训练 3.3 模型评估与调优 3…

与WAF的“相爱相杀”的RASP

用什么来保护Web应用的安全&#xff1f; 猜想大部分安全从业者都会回答&#xff1a;“WAF&#xff08;Web Application Firewall,应用程序防火墙&#xff09;。”不过RASP&#xff08;Runtime Application Self-Protection&#xff0c;应用运行时自我保护&#xff09;横空出世…

java操作Redis缓存设置过期时间

如何用java操作Redis缓存设置过期时间&#xff1f;很多新手对此不是很清楚&#xff0c;为了帮助大家解决这个难题&#xff0c;下面小编将为大家详细讲解&#xff0c;有这方面需求的人可以来学习下&#xff0c;希望你能有所收获。 在应用中我们会需要使用redis设置过期时间&…

android studio接入facebook踩坑1

今天在接入facebook第三方登录的时候&#xff0c;点击登录按钮&#xff0c;APP闪退&#xff0c;并报错 java.lang.RuntimeException Failure delivering result ResultInfo{whonull,request64206,result-1} 新文章链接https://lengmo714.top/facebook1.html 如下图&#xff1a;…

网络传输层

叠甲&#xff1a;以下文章主要是依靠我的实际编码学习中总结出来的经验之谈&#xff0c;求逻辑自洽&#xff0c;不能百分百保证正确&#xff0c;有错误、未定义、不合适的内容请尽情指出&#xff01; 文章目录 1.端口号的基础2.传输层两协议2.1.UDP 协议2.1.1.协议结构2.1.2.封…

【Redis7】Redis持久化机制之RDB

文章目录 1.RDB简介2.RDB配置触发设置3.RDB的优缺点4.如何检查修复RDB文件5.如何禁用RDB6.RDB参数优化7.总结 1.RDB简介 Redis持久化机制中的RDB&#xff08;Redis Database&#xff09;是一种将Redis在某个时间点的数据以快照形式保存到磁盘上的方法。 原理&#xff1a;RDB通…

期货交易的雷区

一、做自己看不懂的行情做交易计划一样要做有把握的&#xff0c;倘若你在盘中找机会交易&#xff0c;做自己看不懂的行情&#xff0c;即便你做进去了&#xff0c;建仓时也不会那么肯定&#xff0c;自然而然持仓也不自信&#xff0c;有点盈利就想平仓&#xff0c;亏损又想扛单。…

Go 和 Delphi 定义可变参数函数的对比

使用可变参数函数具有灵活性、重用性、简化调用等优点&#xff0c;各个语言有各自定义可变参数函数的方法&#xff0c;也有通用的处理方法&#xff0c;比如使用数组、定义参数结构体、使用泛型等。 这里总结记录一下 go、delphi 的常用的定义可变参数函数的方式&#xff01; 一…

句柄降权绕过CallBacks检查

看到前辈们相关的文章&#xff0c;不太明白什么是句柄降权&#xff0c;于是专门去学习一下&#xff0c;过程有一点波折。 句柄降权 什么是句柄 当一个进程利用名称来创建或打开一个对象时&#xff0c;将获得一个句柄&#xff0c;该句柄指向所创建或打开的对象。以后&#xf…

前端自动将 HTTP 请求升级为 HTTPS 请求

前端将HTTP请求升级为HTTPS请求有两种方式&#xff1a; 一、index.html 中插入meta 直接在首页 index.html 的 head 中加入一条 meta 即可&#xff0c;如下所示&#xff1a; <meta http-equiv"Content-Security-Policy" content"upgrade-insecure-requests&…