Java抽象类和接口(2)

 🐵本篇文章继续对接口相关知识进行讲解


一、排序

1.1 给一个对象数组排序:

class Student {
    public String name;
    public int age;
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String toString() {
        return "name:"+name+" age:"+age;
    }
}

public class Test {

    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("Sans", 18);
        students[1] = new Student("Frisk", 8);
        students[2] = new Student("Chara", 9);

        Arrays.sort(students); //给数组排序
        System.out.println(Arrays.toString(students)); //将排序后数组排序后转化为字符串打印

        //Arrays类中有一个toString静态方法,但这个方法并不是重写的Object类的方法,
        //在这个方法中,间接调用了Object中的toString方法
    }
}

可以发现,这样写编译会报错,原因就是Student类中有name和age两种属性,但在进行排序时编译器并不知道按什么排序,错误信息显示如下:

在ComparableTimSort.java文件的320行代码中有这样一条语句:

311    private static int countRunAndMakeAscending(Object[] a, int lo, int hi) {
314        assert lo < hi;
315        int runHi = lo + 1;
316        if (runHi == hi)
317            return 1;
318
319        // Find end of run, and reverse range if descending
320       if (((Comparable) a[runHi++]).compareTo(a[lo]) < 0) { // Descending
...        ...

在311行代码的形参有一个Object[]a,这个参数其实接收的就是我们要排序的数组(这个方法是由Arrays.sort间接调用的)在320行代码可以看到数组被强制类型转换为了Comparable这个接口,我们的数组是Student类,Student类并没有实现Comparable这个接口,所以不能进行强转,那么现在就要实现这个接口,实现一个接口就必须重写其包含的抽象方法,在Comparable接口中有下面方法:

public interface Comparable<T> {
    public int compareTo(T o);
}

也就是要在Student类中对compareTo方法根据我们的需求进行重写,如果根据学生的姓名进行比较:

class Student implements Comparable<Student>{

.......

public int compareTo(Student student) {
    return this.name.compareTo(student.name);
}

<>中用来写要比较的对象所属的类,在compareTo方法中也调用了compareTo方法,这里是调用的String类中的方法,因为this.name是String类型,在String类中也重写了compareTo方法,它的作用就是用来比较字符串

现在完整代码显示如下:

import java.util.Arrays;

class Student implements Comparable<Student>{
    public String name;
    public int age;
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String toString() {
        return "name:"+name+" age:"+age;
    }

    public int compareTo(Student student) {
        return this.name.compareTo(student.name);
    }
}

public class Test {

    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("Sans", 18);
        students[1] = new Student("Frisk", 8);
        students[2] = new Student("Chara", 9);

        Arrays.sort(students); //给数组排序
        System.out.println(Arrays.toString(students)); //将排序后数组排序后转化为字符串打印

        //Arrays类中有一个toString静态方法,但这个方法并不是重写的Object类的方法,
        //在这个方法中,间接调用了Object中的toString方法
    }
}

简单来说compareTo就是我们给编译器提供的比较方法,使其按照这个进行排序

也可以自己模拟一个排序方法(冒泡排序思想):

public static void mySort(Comparable[] comparables) { //任何实现Comparable接口的类都可以通过这个方法排序,类似于向上转型
    for (int i = 0; i < comparables.length-1; i++) {
        for (int j = 0; j < comparables.length-1-i; j++) {
            if(comparables[j].compareTo(comparables[j+1]) > 0) { //这一条语句发生了动态绑定,调用的是Student类中的compareTo方法
                //交换
                Comparable tmp = comparables[j];
                comparables[j] = comparables[j+1];
                comparables[j+1] = tmp;
            }
        }
    }
}

1.2 Comparator接口

Comparator接口中的compare方法也可以用来比较两个对象

public interface Comparator<T> {
    int compare(T o1, T o2);
}

举一个例子

import java.util.Comparator;
class Student {
public String name;

    public Student(String name) {
        this.name = name;
    }
}
class NameComparator implements Comparator<Student> {
    public int compare(Student student1, Student student2) {
        return student1.name.compareTo(student2.name);
        //前者>后者:返回大于0的数
        //前者<后者:返回小于0的数
        //前者=后者:返回0
    }
}

public class Test {
    public static void main(String[] args) {
        Student student = new Student("Sans");
        Student student1 = new Student("Frisk");

        NameComparator nameComparator = new NameComparator();
        System.out.println(nameComparator.compare(student, student1)); //13
    }
}

二、Cloneable接口

clone()方法是Object类中的一个成员方法,下面举一个克隆对象的实例:

class Person implements Cloneable{
    public String name;
    public Person(String name) {
        this.name = name;
    }

    public Object clonePerson() throws CloneNotSupportedException {
        return super.clone();
    }
}

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person("Sans");
        Person person2 = (Person) person1.clonePerson()
        System.out.println(person2.name);
    }
}

1. Person类实现Cloneable接口才能对这个类的对象进行克隆

2. person1调用clone()方法,并强转为Person赋给perosn2

3. 由于Object类是父类,所以应该用super来调用clone()方法

4. main方法为静态方法,所以必须在Person类中再写一个方法来使用super来调用clone()方法

5. throws CloneNotSupportedException暂且不管,先模仿

2.1 浅拷贝

浅拷贝是指创建一个新对象,并将原始对象的值拷贝到新对象中,但是这里原始对象中引用类型的成员的值也拷贝到了新对象中,这也就说明,原始对象和新对象中的原始引用和新引用都指向了同一个对象

class Money {
    public double money = 12.5;
}

class Person implements Cloneable{ //必须实现Cloneable这个接口才能克隆这个Person类
    public String name;
    public Money m;

    public Person(String name) {
        this.name = name;
    }
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException{
        Person person1 = new Person("Sans");
        Person person2 = (Person) person1.clone(); //将person1所指向的对象拷贝一份给person2

        System.out.println("修改前:"+ person1.m.money); //12.5
        System.out.println("修改前:"+ person2.m.money); //12.5
        person2.m.money = 99.9;
        System.out.println("修改后:"+ person1.m.money); //99.9
        System.out.println("修改后:"+ person2.m.money); //99.9
    }
}

上述代码执行完perosn2.m.money = 99.9后person1.m.money和person2.m.money都变成了99.9

2.2 深拷贝

深拷贝就是在浅拷贝的基础上,新对象的引用字段所指向的对象也是原始对象的被拷贝引用字段所指向对象的拷贝,可以简单理解为将一个对象完全拷贝了一份

class Money implements Cloneable{
    public double money = 12.5;
    public Object cloneMoney() throws CloneNotSupportedException {
        return super.clone(); //由于要拷贝一份m所指对象,所以Money类也要写一个方法并使用super调用clone()方法
    }
}

class Person implements Cloneable{ //必须实现Cloneable这个接口才能克隆这个Person类
    public String name;

    public Money m;

    public Person(String name) {
        this.name = name;
        m = new Money();
    }
    public Object clonePerson() throws CloneNotSupportedException {
        Person tmp = (Person)super.clone(); //拷贝一份person1所指对象
        tmp.m = (Money) this.m.cloneMoney(); //拷贝一份m所指对象并赋值给新对象的m引用
        return tmp; 
    }
}

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException{
        Person person1 = new Person("Sans");
        Person person2 = (Person) person1.clonePerson(); //将person1所指向的对象拷贝一份给person2

        System.out.println("修改前:"+ person1.m.money);
        System.out.println("修改前:"+ person2.m.money);
        person2.m.money = 99.9;
        System.out.println("修改后:"+ person1.m.money);
        System.out.println("修改后:"+ person2.m.money);


    }
}

深拷贝后,执行完person2.m.money,person1.m.money的值就不会改变

三、内部类

内部类定义在类中,相当于在类中嵌套了一个类

3.1 静态内部类

静态内部类由static修饰的一个类,如下:

public class Test {
    private static int a;
    public int b;

    static class Inner {
        public int c;

        public void test() {
            Test test = new Test();
            System.out.println(a); //内部类只能访问外部类的静态成员
            System.out.println(test.b); //访问其它成员必须实例化外部类的对象
                                    //并用外部类对象的引用来访问非静态成员
            System.out.println(c);
        }
    }
    public static void main(String[] args) {
        Test.Inner inner = new Test.Inner(); //实例化静态内部类对象
        inner.test(); //用内部类对象的引用访问内部类的test方法
    }
}

3.2 匿名内部类

public class Test {
    public void func() {
        System.out.println("test()");
    }

    public static void main(String[] args) {
        new Test().func(); //匿名对象
        new Test().func(); //如果有对象只使用一次,则使用匿名对象
        //这里创建了两个对象
    }
}

接下来看匿名内部类:

interface InterFace {
    void func();
}

class A implements InterFace{

    public void func() {
        System.out.println("A");
    }

}

public class Test {
    public static void main(String[] args) {
        InterFace a = new InterFace() {
            public void func() {
                System.out.println("B");
            }
        };

        a.func(); //打印结果为B
}

下图红框部分即为匿名内部类


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

爆肝整理! Python 网络爬虫 + 数据分析 + 机器学习教程来了

前段时间&#xff0c;有小伙伴多次在后台留言询问 Python 爬虫教程的问题。经过这两个多月以来的收集与整理&#xff0c;汇集了多个高校以及公开课视频教程&#xff0c;包括 python 爬虫的入门、进阶与实践&#xff0c;共 9G 左右。爬虫作为机器学习语料库构建的主要方式&#…

Python中的sys模块详解

1. 简介 sys模块是Python标准库中的一个内置模块&#xff0c;提供了与Python解释器和运行环境相关的功能。它包含了一些与系统操作和交互相关的函数和变量&#xff0c;可以用于获取命令行参数、控制程序的执行、管理模块和包、处理异常等。 2. 常用函数和变量 2.1 命令行参数…

虹科Pico汽车示波器 | 汽车免拆检修 | 2016款东风悦达起亚K5车发动机怠速抖动严重、加速无力

一、故障现象 一辆2016款东风悦达起亚K5车&#xff0c;搭载G4FJ发动机&#xff0c;累计行驶里程约为8.2万km。该车发动机怠速抖动严重、加速无力&#xff0c;同时发动机故障灯异常点亮&#xff0c;为此在其他维修厂更换了所有点火线圈和火花塞&#xff0c;故障依旧&#xff0c;…

自驾游汽车托运是交智商税吗?

自驾游汽车托运是交智商税吗? 亲爱的小伙伴们 你们有没有遇到过这样的困扰&#xff1a; 自驾游时&#xff0c;车辆的运输问题让你头疼不已? 是选择自己驾驶还是托运呢? 今天&#xff0c;我就来给大家种草一下汽车托运的好处&#xff0c; 让你的自驾游之旅更加轻松愉快! 1️.…

idea spring initializr创建项目报错

闲来无事就想搞个项目练练手&#xff0c;没想到直接给我卡在项目创建上了&#xff0c;一个个问题最终迎刃而解。 1.上来就给我报了个maven的错 未解析的插件: ‘org.apache.maven.plugins:maven-resources-plugin:3.3.1’ 不慌&#xff0c;应该是maven的路径有问题&#xff0c…

本地Nginx服务搭建结合内网穿透实现多个Windows Web站点公网访问

文章目录 1. 下载windows版Nginx2. 配置Nginx3. 测试局域网访问4. cpolar内网穿透5. 测试公网访问6. 配置固定二级子域名7. 测试访问公网固定二级子域名 1. 下载windows版Nginx 进入官方网站(http://nginx.org/en/download.html)下载windows版的nginx 下载好后解压进入nginx目…

Flask WTForms 表单插件的使用

在Web应用中&#xff0c;表单处理是一个基本而常见的任务。Python的WTForms库通过提供表单的结构、验证和渲染等功能&#xff0c;简化了表单的处理流程。与此同时&#xff0c;Flask的扩展Flask-WTF更进一步地整合了WTForms&#xff0c;为开发者提供了更便捷、灵活的表单处理方式…

Linux(9):正规表示法与文件格式化处理

简单的说&#xff0c;正规表示法就是处理字符串的方法&#xff0c;他是以行为单位来进行字符串的处理行为&#xff0c;正规表示法透过一些特殊符号的辅助&#xff0c;可以让使用者轻易的达到【搜寻/删除/取代】某特定字符串的处理程序。 正规表示法基本上是一种【表示法】&…

【运营思维】美团面试题:如何把梳子卖给寺庙和尚?

Hello 小米的小伙伴们~ 欢迎来到小米的微信公众号&#xff01;今天小米要和大家分享一道美团运营面试题&#xff0c;题目可真是独特——“如何把梳子卖给寺庙和尚&#xff1f;”想必大家一定兴奋不已吧&#xff01; 首先&#xff0c;让我们理清思路&#xff0c;挑战这个看似不…

[学习笔记]IK分词器的学习

IK分词器有几种模式 # 测试分词器 POST /_analyze {"text":"黑马程序员学习java太棒了","analyzer": "standard" }# 测试分词器 POST /_analyze {"text":"黑马程序员学习java太棒了","analyzer": &quo…

最新AI创作系统ChatGPT系统运营源码+DALL-E3文生图+支持OpenAI-GPT全模型+国内AI全模型

一、AI创作系统 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI…

1评论收藏分享抖店不要再无脑铺货了!这个方法学会,7天流量就起飞~

这2023年都马上过完了&#xff0c;你还在上一堆链接到抖店吗&#xff1f;要知道这样无脑铺货是拿不到大流量的。 哪今天我给大家分享一个&#xff0c;比较适合新手操作&#xff0c;也能快速起流量出单的方法。 。首先你的店铺拿不到流量&#xff0c;一定要先查清楚你为什么拿…

海外Leads Generation产业:中国出海群体的行业大机会

Leads Generation&#xff08;简称LeadsGen&#xff09;指的是集中精力吸引和开发潜在客户的营销策略。通过引导式的营销策略&#xff0c;企业分发内容吸引潜在客户&#xff0c;引导客户留下电话/邮件/姓名等信息。基于这些信息&#xff0c;企业可建立潜在客户数据库&#xff0…

P8A002-CIA安全模型-配置Linux描述网络安全CIA模型之可用性案例

【预备知识】 可用性(Availability) 数据可用性是一种以使用者为中心的设计概念,易用性设计的重点在于让产品的设计能够符合使用者的习惯与需求。以互联网网站的设计为例,希望让使用者在浏览的过程中不会产生压力或感到挫折,并能让使用者在使用网站功能时,能用最少的努力…

数据结构与算法编程题27

计算二叉树深度 #define _CRT_SECURE_NO_WARNINGS#include <iostream> using namespace std;typedef char ElemType; #define ERROR 0 #define OK 1 #define Maxsize 100 #define STR_SIZE 1024typedef struct BiTNode {ElemType data;BiTNode* lchild, * rchild; }BiTNo…

基于单片机的智能鱼缸(论文+源码)

1.总体设计 在本次设计中&#xff0c;其系统整个框图如下图2.1所示。其主要的核心控制模块由单片机模块&#xff0c;LCD显示模块&#xff0c;喂食模块&#xff0c;蜂鸣器模块&#xff0c;按键模块&#xff0c;复位电路&#xff0c;抽水电路&#xff0c;加热电路&#xff0c;加…

麒麟V10服务器搭建FTP服务

概念 1.1介绍 FTP&#xff1a;File transfer protocol 文件传输协议 1.2原理 默认采用被动模式 被动模式FTP 为了解决服务器发起到客户的连接的问题&#xff0c;人们开发了一种不同的FTP连接方式。这就是所谓的被 动方式&#xff0c;或者叫做PASV&#xff0c;当客户端通…

C#开发的OpenRA游戏之属性SelectionDecorations(10)

C#开发的OpenRA游戏之属性SelectionDecorations(10) 前面分析了选择属性,继续分析前面的内容,不过这里不再是选择,而是选择相关的属性。 当用玩家选择地图上一个物品,或者士兵,或者坦克时,就会在周边画上一些指示标记,并且有一个状态条。 通过上图,可以看到建筑物周…

Eureka简单使用做微服务模块之间动态请求

创建一个eureka模块,引入eureka 为启动项加上EnableEurekaServer注解 配置信息 orderService和userService的操作是一样的 这里以orderService为例: 引入eureka客户端 加上 LoadBalanced注解 配置 orderService和userService都配置好了之后 启动 这样我们在http://localhos…

k8s环境排查nginx转发nacos请求失败问题

一、问题背景 k8s部署两个服务,一个nginx&#xff0c;一个nacos, 服务信息如下(nacos有两个端口): 服务 serviceNameservice类型porttargetPort nodePortnginxmonitor-cp-nginxNodePort808031082nacosmonitor-cp-nacosClusterIP88488848-98489848- ng的default.conf配置文件…