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所指向的对象拷贝一份给person2System.out.println("修改前:"+ person1.m.money); //12.5System.out.println("修改前:"+ person2.m.money); //12.5person2.m.money = 99.9;System.out.println("修改后:"+ person1.m.money); //99.9System.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所指向的对象拷贝一份给person2System.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
}

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


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

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

相关文章

2023年10个最受欢迎的性能测试工具

当下软件的性能也是软件成败的一个关键因素。在高负载情况下软件产品出现各种性能问题&#xff0c;比如变慢、宕机都会严重影响客户对产品的信任进而影响客户对公司的信任。比如本次双十一&#xff0c;阿里云全线产品宕机事件&#xff0c;都让阿里集团遭到技术、信誉层面的双重…

基于C#实现十字链表

上一篇我们看了矩阵的顺序存储&#xff0c;这篇我们再看看一种链式存储方法“十字链表”&#xff0c;当然目的都是一样&#xff0c;压缩空间。 一、概念 既然要用链表节点来模拟矩阵中的非零元素&#xff0c;肯定需要如下 5 个元素(row,col,val,down,right)&#xff0c;其中&…

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

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

数据资产入表在即,企业要从数据治理入手

数据是驱动数字经济发展的核心生产要素&#xff0c;数据资产化是释放数据要素价值的重要方式。2024年1月1日&#xff0c;《企业数据资源相关会计处理暂行规定》将正式实施。企业数据资产入表已进入倒计时&#xff0c;企业作如何充分发挥海量规模数据和应用场景等优势&#xff0…

数据结构与算法编程题28

计算二叉树结点总数 #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; }B…

8个无需编写代码即可使用Python内置库的方法!

近几年 Python 语言之所以流行&#xff0c;是因为我们可以使用它编写更少的代码来实现复杂的功能。Python 开发者社区非常欢迎那些封装了复杂实现但是对使用者十分友好的工具包。 然而&#xff0c;Python 的简便性不止如此。你能相信我们可以在不写任何代码的情况下使用 Pytho…

CloudCompare 源码编译

一、下载源码 二、cmake 编译 这里面有四个比较重要的地方 1、源码的位置 2、生成的位置 3、项目的位置 4、qt 的位置 三、编译 开始测试&#xff0c;先用那个项目做测试 没有问题 然后用build的那个打开 加入Qt 的相关库到qcc中 启动项目生成cloudcompare 启动 ok ,完成…

pikachu靶场:php反序列化漏洞

pikachu靶场:php反序列化漏洞 文章目录 pikachu靶场:php反序列化漏洞代码审计漏洞利用 代码审计 像这种反序列化的代码基本都是代码审计出的 // 定义一个名为S的类&#xff0c;该类有一个属性$test和一个构造函数 class S{ var $test "pikachu"; // $test是一个…

计算机毕业设计|基于SpringBoot+MyBatis框架的电脑商城的设计与实现(用户资料修改)

计算机毕业设计|基于SpringBootMyBatis框架的电脑商城的设计与实现&#xff08;用户资料修改&#xff09; 该项目分析着重于设计和实现基于SpringBootMyBatis框架的电脑商城。首先&#xff0c;通过深入分析项目所需数据&#xff0c;包括用户、商品、商品类别、收藏、订单、购物…

数据结构与算法编程题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…

【Java】使用IntelliJ IDEA搭建SSM(MyBatis-Plus)框架并连接MySQL数据库

步骤 0 准备工作1 创建Maven项目2 配置Maven依赖3 配置数据源4 项目结构5 创建实体类6 创建数据访问层7 创建服务层8 创建Controller层9 启动项目10 使用Postman测试接口 0 准备工作 下载并安装 IntelliJ IDEA下载并安装 MySQL 数据库下载并安装Postman测试工具使用 Navicat 创…

计算机组成原理-Cache替换算法

文章目录 总览随机算法&#xff08;RAND&#xff09;先进先出算法&#xff08;FIFO&#xff09;近期最少使用算法&#xff08;LRU&#xff09;最不经常使用算法&#xff08;LFU&#xff09;总结 总览 随机算法&#xff08;RAND&#xff09; 没有选择性地考虑替换哪一块Cache&a…