初始Java篇(JavaSE基础语法)(7)抽象类和接口(下)

news2024/12/26 0:11:17

找往期文章包括但不限于本期文章中不懂的知识点:

个人主页:我要学编程(ಥ_ಥ)-CSDN博客

所属专栏:JavaSE

接上文:初始Java篇(JavaSE基础语法)(7)抽象类和接口(上)-CSDN博客 

目录

实现多个接口 

接口间的继承 

使用Java内置的部分接口 

Comparable 接口 

Cloneable 接口

深拷贝 

抽象类和接口的区别

Object类 


实现多个接口 

在Java中,类和类之间是单继承的,一个类只能有一个父类,即Java中不支持多继承,但是一个类可以实现多个接口。下面通过类来表示一组动物。

class Animal {
    String name;
    int age;

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

上面是一些动物都有的特征,但是还有一部分是某些动物特有的。例如:飞,跑,游泳……

没有学习接口之前,我们可能会用继承的思想来实现。

class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age);
    }
    public void run() {
        System.out.println(name+" 正在遛!");
    }
}


class Cat extends Animal {
    public Cat(String name, int age) {
        super(name, age);
    }
    public void run() {
        System.out.println(name+" 正在遛!");
    }
}

应用接口之后:

//    狗   是一个   动物     具备    跑的能力
class Dog extends Animal implements IRun{
    public Dog(String name, int age) {
        super(name, age);
    }
    //重写接口的方法
    @Override
    public void run() {
        System.out.println(name+" 正在遛!");
    }
}

//    猫   是一个   动物     具备    跑的能力
class Cat extends Animal implements IRun{
    public Cat(String name, int age) {
        super(name, age);
    }
    //重写接口的方法
    @Override
    public void run() {
        System.out.println(name+" 正在遛!");
    }
}

interface IRun {
    void run();
}

而实现了接口之后,在以后的编程中,我们就只需要知道哪些是具备跑的能力,就可以用接口来实现其跑的方法,而不在需要关注是谁调用的它。

了解基本的用法之后,就可以开始实现,用一个类继承一个类,实现多个接口了。

狗是一种动物,具有会跑的特性。

青蛙也是一种动物,既能跑,也能游泳。

鸭子也是一种动物,既能跑,也能游,还能飞。 

class Animal {
    String name;
    int age;

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


class Dog extends Animal implements IRun{
    public Dog(String name, int age) {
        super(name, age);
    }
    @Override
    public void run() {
        System.out.println(name+" 正在遛!");
    }
}


class Cat extends Animal implements IRun{
    public Cat(String name, int age) {
        super(name, age);
    }
    @Override
    public void run() {
        System.out.println(name+" 正在遛!");
    }
}


class Frog extends Animal implements IRun, ISwim {
    public Frog(String name, int age) {
        super(name, age);
    }
    @Override
    public void run() {
        System.out.println(name+" 正在跳来跳去!");
    }

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


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

    @Override
    public void run() {
        System.out.println(name+" 正在飞快地跑!");
    }

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

    @Override
    public void fly() {
        System.out.println(name+" 正在飞得更高!");
    }
}


interface IRun {
    void run();
}


interface ISwim {
    void swim();
}


interface IFly {
    void fly();
}


public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog("大黄",4);
        dog.run();
        Duck duck = new Duck("唐老鸭", 3);
        duck.fly();
        duck.run();
        duck.swim();
        Frog frog = new Frog("青蛙王子", 3);
        frog.run();
        frog.swim();
    }
}

这就是接口的妙处:只要这个类有这个功能,那么就可以实现该接口,继而间接实现多继承。并且也不用管实现这个接口是什么类,只要有这个功能就行。

例如:机器人也具有跑的功能。

接口间的继承 

在Java中,类和类之间是单继承的,一个类可以实现多个接口,接口与接口之间可以多继承。即:用接口可以达到多继承的目的。 接口可以继承一个接口, 达到复用的效果. 使用 extends 关键字。

interface A {
    void a();
}


interface B extends A{
    void b();
}

这里的 extends 可以理解为拓展的意思,即B接口拓展了A接口的功能。那也就意味着当有类去实现B接口时,也要把A接口的抽象方法也给重写。

interface A {
    void a();
}


interface B extends A{
    void b();
}


class C implements B {
    @Override
    public void a() {
        
    }

    @Override
    public void b() {

    }
}

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

使用Java内置的部分接口 

Comparable 接口 

首先,创建一个数组,里面放入多个学生对象。 

class Student {
    String name;
    int age;

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

public class Test {
    public static void main(String[] args) {
        Student[] students = {new Student("张三",20), 
                new Student("李四", 18), 
                new Student("王五",13)};
    }
}

现在要对这个数组进行排序(根据年龄),我们之前在学习数组时,学过一个方法sort可以对数组进行排序。

之所以会出现这样的情况就是因为我们给得是两个对象,而之前我们在比较的时候是用的两个整数。而两个对象要进行比较就得用到一个接口 Comparable ,用这个接口里的 compareTo 方法。

//                                要比较的类型 
class Student implements Comparable<Student>{
    String name;
    int age;

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

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

    @Override
    public int compareTo(Student o) {
        if (this.age > o.age) {
            return -1;
        }else if (this.age < o.age) {
            return 1;
        }else {
            return 0;
        }
    }

}


public class Test {
    public static void main(String[] args) {
        Student[] students = {new Student("张三", 20),
                new Student("李四", 13),
                new Student("王五", 18)};
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));
    }
}

在 sort 方法中会自动调用 compareTo 方法。

注意:对于 sort 方法来说,需要传入的数组的每个对象都是 "可比较" 的, 需要具备 compareTo 这样的能力。通过重写 compareTo 方法的方式, 就可以定义比较规则。

但是上面的方法也有局限性。当我们写的是年龄比较时,那么就只能比较年龄,而不能比较其他的。这时,就可以把这个比较方法单独写成一个类,我们需要用的时候,就可以通过对象的引用来调用这个方法。

 这样就更加便捷了。

Cloneable 接口

Object 类(待会解释)中存在一个 clone 方法,调用这个方法可以创建一个对象的 "拷贝",但是要想合法调用 clone 方法, 必须要先实现 Cloneable 接口, 否则就会抛出 CloneNotSupportedException 异常。

直接拷贝,肯定是不行的。得先实现 Cloneable 这个接口,再重写这个clone方法。至于为什么要重写这个方法?是因为这个方法是由 protected 修饰的,在外部类中只有拥有继承关系的才能够直接调用这个方法,因此我们得重写这个方法。

class Animal implements Cloneable{
    String name;
    int gae;
    public Animal(String name, int age) {
        this.name = name;
        this.gae = age;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Animal animal = new Animal("狗类", 20);
        //现在要实现对象的拷贝
        Animal animal1 = (Animal)animal.clone();
    }
}

从编译器生成的方法来看,的确只是因为访问权限的问题,毕竟只是调用父类的clone方法。

这里的 throws CloneNotSupportedException 声明是一种防御性编程的体现,确保了API的使用者明白克隆操作可能存在的限制或前提条件。但实际上,如果你的类已经实现了 Cloneable 接口,并且确定所有引用的对象也支持克隆,那么在正常情况下,这个异常通常不会被抛出。

简单理解就是不在使用clone方法的后面加上 throws CloneNotSupportedException ,会导致程序报错,抛出不支持克隆异常。如下图:

我们也要注意一下,这个clone方法的返回值。返回的是一个Object - 父类(待会解释), 父类对象给到子类引用,有很大危险!除非是同型的。例如:父类的也是Animal,那么就没问题了。

IDEA上生成重写方法等的快捷的方法:

从上图我们可以看到有许多可以生成的快捷方法。例如:重载,获取private修饰的成员变量的方法等。 下图是克隆对象的流程图:

深拷贝 

在了解深拷贝之前,我们先改一改前面的代码。

class Food {
    int food = 100;
}

class Animal implements Cloneable{
    String name;
    int gae;
    public Animal(String name, int age) {
        this.name = name;
        this.gae = age;
    }
    //对象里面又有一个对象
    Food animalFood = new Food();

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Animal animal = new Animal("狗类", 20);
        //现在要实现对象的拷贝
        Animal animal1 = (Animal)animal.clone();
        System.out.println("改变前:");
        System.out.println("没有被拷贝的animal:"+animal.animalFood.food);
        System.out.println("被拷贝的animal:"+animal1.animalFood.food);
        animal.animalFood.food = 50;
        System.out.println("改变后:");
        System.out.println("没有被拷贝的animal:"+animal.animalFood.food);
        System.out.println("被拷贝的animal:"+animal1.animalFood.food);
    }
}

这就是浅拷贝,只是拷贝了一级对象本身,并没有拷贝一级对象里的二级对象等。

而深拷贝就是只要是对象,那么就都会被拷贝到我们的那个引用里。

从这个结果来看,肯定是我们的拷贝方法出了问题。那么我们就得重新写这个方法。

浅拷贝示意图:

深拷贝示意图:

思路:先把animal的对象拷贝一份,放到 tmp 中,再把animal对象里的 animalfood 的对象拷贝一份放到animal1food里,最后把 animal1 返回。(这里要注意对象之间的类型转换) 

class Food implements Cloneable {
    int food = 100;

    //克隆Food
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Animal implements Cloneable{
    String name;
    int gae;
    public Animal(String name, int age) {
        this.name = name;
        this.gae = age;
    }
    //对象里面又有一个对象
    Food animalFood = new Food();

    @Override
    protected Object clone() throws CloneNotSupportedException {
        //克隆了animal对象
        Animal tmp = (Animal)super.clone();
        //现在要克隆animalfood对象,先得在animalfood这个类中实现克隆接口,并且重写克隆方法
        tmp.animalFood  = (Food)this.animalFood.clone();
        return tmp;
    }
}
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Animal animal = new Animal("狗类", 20);
        //现在要实现对象的拷贝
        Animal animal1 = (Animal)animal.clone();
        System.out.println("改变前:");
        System.out.println("没有被拷贝的animal:"+animal.animalFood.food);
        System.out.println("被拷贝的animal:"+animal1.animalFood.food);
        animal.animalFood.food = 50;
        System.out.println("改变后:");
        System.out.println("没有被拷贝的animal:"+animal.animalFood.food);
        System.out.println("被拷贝的animal:"+animal1.animalFood.food);
    }
}

这里我们来解析一下上面的代码。主要目的是拷贝这个 animalfood 对象。而克隆这个对象之前,先得实现Cloneable 这个接口,再重写克隆方法,这样就可以克隆该对象了。做完准备工作之后,就可以开始克隆animal这个对象,再把这个对象里的animalfood对象再克隆一个放到刚刚拷贝的对象里的animalfood 。即  tmp.animalfood = (Food)this.animalfood.clone(); 最后再返回一级对象。

抽象类和接口的区别

抽象类和接口都是 Java 中多态的常见使用方式。都需要重点掌握,同时又要认清两者的区别。

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

例如: Animal 中包含一个 name 这样的属性, 这个属性在任何子类中都是存在的。因此此处的 Animal 只能作为一个抽象类, 而不应该成为一个接口。(因为接口中的字段必须是一个常量)

抽象类和接口的区别
区别抽象类接口
结构组成普通类的基础上+抽象方法(可无)抽象方法和常量
权限private default protected publicpublic
子类使用使用extends关键字继承抽象类使用implements关键字实现接口
关系一个抽象类可以实现多个接口接口不能继承抽象类,但是接口可以使用extends关键字拓展多个接口
子类限制一个子类只能继承一个抽象类一个子类可以实现多个接口

Object类 

Object是Java默认提供的一个父类,也就是说不管是什么类,都是Object的子类。Java里面除了Object类,所有的类都是存在继承关系的。默认会继承Object父类。即所有类的对象都可以使用Object的引用进行接收。

例如:

对象比较equals方法 

在Java中,==进行比较时:

如果==左右两侧是基本类型变量,比较的是变量中值是否相同。

如果==左右两侧是引用类型变量,比较的是引用变量存放的地址是否相同。

如果要比较对象中内容,必须重写Object中的equals方法,因为equals方法默认也是按照地址比较的。

// Object类中的equals方法
public boolean equals(Object obj) {
    return (this == obj); //比较的是两个引用的值,也就是地址
}

如果没有重写 equals 方法,那么就会直接比较对象引用的值,也就是地址会失败。 

可能有小伙伴有疑惑:为什么都是狗和20岁,地址会不相同。

因为在new一个对象时,jvm(Java虚拟机)都会给这个对象在堆区里分配空间,而第一份空间和第二份空间的地址是不相同的(内存中不可能存在两个相同的地址)。

因此,我们想要比较对象的内容,就得重写 equals 方法,不再比较对象。

结论:比较对象中内容是否相同的时候,一定要重写equals方法。 

好啦!本期 初始Java篇(JavaSE基础语法)(7)抽象类和接口(下)的学习之旅就到此结束了!我们下一期再一起学习吧!

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

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

相关文章

Unity Material(材质)、Texture(纹理)、Shader(着色器)简介

文章目录 一、概念二、Rendering Mode三、Main Maps三、参考文章 一、概念 Material(材质)&#xff1a;物体的“色彩”、“纹理”、“光滑度”、“透明度”、“反射率”、“折射率”、“发光度”等&#xff0c;材质的本质是shader的实例(载体)Texture(贴图)&#xff1a;附件到…

熟悉Redis吗,那Redis的过期键删除策略是什么

对于Redis&#xff0c;我们业务开发一般都只关心Redis键值对的查询、修改操作&#xff0c;可能因为懒或者只想能用就行&#xff0c;呵呵。很少关心键值对存储在什么地方、键值对过期了会怎么样、Redis有没什么策略处理过期的键、Redis处理过期键又有什么作用&#xff1f;但这些…

FPGA第一篇,FPGA现场可编程门阵列,从0开始掌握可编程硬件开发(FPGA入门指南)

简介&#xff1a;FPGA全称Field-Programmable Gate Array&#xff0c;是一种可编程逻辑器件&#xff0c;它通过可编程的逻辑单元和可编程的连接网络实现了灵活的硬件实现。与固定功能的集成电路&#xff08;ASIC&#xff09;相比&#xff0c;FPGA具有更高的灵活性和可重新配置性…

实现日期类

前言&#xff1a; 本篇我们要实现一个日期类&#xff0c;其实非常困难&#xff0c;因为要对日期实现加加减减&#xff0c;也就是要对前面的知识做出一个巩固。 头文件&#xff1a; 因为要判断闰年&#xff0c;所以我们要去创建一个数组。也就是创建一个返回每月日期的函数。因…

Isaac Sim 4 键盘控制小车前进方向(学习笔记5.8.2)

写的乱糟糟&#xff0c;主要是这两周忘了记录了...吭哧吭哧往下搞&#xff0c;突然想起来要留档&#xff0c;先大致写一个&#xff0c;后面再往里添加和修改吧&#xff0c;再不写就全忘了 有一个一直没解决的问题&#xff1a; 在保存文件时出现问题&#xff1a;isaac sim mism…

蜜蜂收卡系统 加油卡充值卡礼品卡自定义回收系统源码 前后端开源uniapp可打包app

本文来自&#xff1a;蜜蜂收卡系统 加油卡充值卡礼品卡自定义回收系统源码 前后端开源uniapp可打包app - 源码1688 卡券绿色循环计划—— 一项旨在构建卡券价值再利用生态的社会责任感项目。在当前数字化消费日益普及的背景下&#xff0c;大量礼品卡、优惠券因各种原因未能有效…

2024年软件测试最全渗透测试工具_下载地址1下载地址2下载地址3(1),我了解到的面试的一些小内幕

网上学习资料一大堆&#xff0c;但如果学到的知识不成体系&#xff0c;遇到问题时只是浅尝辄止&#xff0c;不再深入研究&#xff0c;那么很难做到真正的技术提升。 需要这份系统化的资料的朋友&#xff0c;可以戳这里获取 一个人可以走的很快&#xff0c;但一群人才能走的更…

java项目之智慧图书管理系统设计与实现(springboot+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的智慧图书管理系统设计与实现。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 智慧图书管理…

华为OD机试【城市聚集度】(java)(200分)

1、题目描述 一张地图上有N个城市&#xff0c;城市和城市之间有且只有一条道路相连&#xff0c;要么直接相连&#xff0c;要么通过其他城市中转相连(可中转一次或多次)。城市与城市之间的道路都不会成环。 当切断通往某城市i的所有道路后&#xff0c;地图上将分成多个连通的城…

【Linux】Linux——Centos7安装RabbitMQ

目录 安装包准备socaterlang 安装rabbitmq安装命令启动rabbitmq&#xff0c;两种方式查看rabbitmq 启动后的情况配置并开启网页插件关闭防火墙或开放端口测试登录问题配置web端访问账号密码和权限添加用户&#xff0c;后面两个参数分别是用户名和密码.添加权限修改用户角色再次…

通过 Java 操作 redis -- hash 哈希表基本命令

目录 使用命令 hset&#xff0c;hget 使用命令 hexists 使用命令 hdel 使用命令 hkeys&#xff0c;hvals 使用命令 hmget&#xff0c;hmset 关于 redis hash 哈希表类型的相关命令推荐看Redis - hash 哈希表 要想通过 Java 操作 redis&#xff0c;首先要连接上 redis 服务…

【C语言】static关键字用法

目录 一、static修饰局部变量 二、static修饰全局变量 三、static修饰函数 一、static修饰局部变量 首先我们来看两段代码: 代码1&#xff08;不加static&#xff09; #include <stdio.h> void test() {int i 0;i;printf("%d ", i); } int main() {int i…

【管理篇】如何向上沟通?

目录标题 向上沟通中下列问题最普遍和上级能不聊就不聊拿捏不好该不该和上级聊的分寸和尺度很难领会到上级的意图如何影响上级的一些观点和决策? 如何应对上述问题呢&#xff1f;&#x1f60e;如何管理上级&#xff1f;&#x1f44c; 向上沟通中下列问题最普遍 和上级能不聊就…

Ubuntu 24.04 LTS 安装 touchegg 开启触控板多指手势

文章目录 〇、概述一、安装 touchegg二、安装 gnome-shell 扩展 X11 Gestures三、安装可视化配置工具 touche 〇、概述 之前为了让笔记本支持多指手势&#xff0c;我安装的是 fusuma&#xff0c;安装教程详见 这篇文章 &#xff0c;考虑到 fusuma 安装过程繁琐且不支持可视化配…

Android ViewFlipper

Android ViewFlipper 在很多APP都有如下的头条/热榜效果&#xff08;上下自动翻滚&#xff09; 这种效果可以使用很多方式实现&#xff0c;有一个简便的方式可以使用ViewFlipper控件实现&#xff0c;ViewFlipper控件继承结果如下&#xff1a; 可以看出ViewFlipper 继承自ViewA…

解锁电脑潜能,提高办公效率:这个桌面助手您可千万不能错过!

文章目录 &#x1f4d6; 介绍 &#x1f4d6;&#x1f4dd; 你是否有这些烦恼&#xff1f;&#x1f4dd; 一站式效率工具平台&#x1f4dd; 插件市场&#xff1a;无限扩展的可能&#x1f4dd; 如何开始使用&#x1f3af; 结语 ⚓️ 相关链接 ⚓️ &#x1f4d6; 介绍 &#x1f4…

未授权访问:MongoDB未授权访问漏洞

目录 1、漏洞原理 2、环境搭建 3、未授权访问 防御手段 今天继续学习各种未授权访问的知识和相关的实操实验&#xff0c;一共有好多篇&#xff0c;内容主要是参考先知社区的一位大佬的关于未授权访问的好文章&#xff0c;还有其他大佬总结好的文章&#xff1a; 这里附上大佬…

OpenCV单窗口并排显示多张图片

OpenCV单窗口并排显示多张图片 效果代码 PS&#xff1a;本例的代码适合图片的宽度和高度都相同。 效果 原始三张图片&#xff1a; 合并显示&#xff1a; 代码 import cv2 import numpy as npdef opencv_multi_img():# 读取图片img1 cv2.imread(saw_1.jpeg)img2 cv2.im…

HTML学习|初识表单post和get提交、文本框和单选框、按钮、多选框和下拉框、文本域和文件域、搜索框滑块和简单验证、表单的应用、表单初级验证

初识表单post和get提交 form标签是表单&#xff0c;method控制表单提交方式&#xff0c;get方式&#xff0c;表单填写的参数能够在跳转的url地址中看到&#xff0c;post方式是看不到的&#xff0c;action是向何处跳转表单数据 input标签&#xff0c;且typetext&#xff0c;是…

Vue 3.3 编译宏 vue3.3新增了一些语法糖和宏,包括泛型组件、defineSlots、defineEmits、defineOptions

Vue 3.3新增了一些语法糖和宏&#xff0c;包括泛型组件、defineSlots、defineEmits、defineOptions defineProps 父组件传参 <template><Child name"my"></Child> </template> <script setup lang"ts"> import Child fro…