零基础 “入坑” Java--- 十三、再谈类和接口

文章目录

  • 一、Object类
    • 1.获取对象信息
    • 2.对象比较:equals方法
  • 二、再谈接口
    • 1.比较相关接口
    • 2.Cloneable接口和深拷贝
  • 三、内部类
    • 1.匿名内部类
    • 2.实例内部类
    • 3.静态内部类
    • 4.局部内部类

在之前的学习中,我们已经了解了有关类以及接口的知识,在本章节中,我们继续来探究类和接口相关的知识。

一、Object类

Object类是Java默认提供的一个类。在Java中,除了Object类,其余所有的类都存在继承关系,默认继承Object类。即所有类实例化出的对象都可以使用Object类的引用接收,如:

class A {}
class B {}
public class Test {public static void test(Object object) {System.out.println(object);}public static void main(String[] args) {test(new A());test(new B());}
}

Object类中也存在一些定义好的方法,在编写代码过程中经常使用,接下来我们就来学习一下。

1.获取对象信息

如果想要打印对象中的内容,可以通过重写toString的方式来完成,这一点在之前的学习中也已经介绍过,我们来看一下源码即可:
在这里插入图片描述

查看源码方式:双击Shift,因为toString方法在Object类中,所以输入Object,再寻找toString方法即可。

2.对象比较:equals方法

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

a.如果 == 两边为基本数据类型,则比较的是变量的值是否相同。
b.如果 == 两边为引用类型,则比较的是"地址"是否相同。
c.当 == 两边为引用类型时,如果想比较其内容是否相同,就可以重写Object中的equals方法,使用equals进行比较(equals默认按照"地址"进行比较)。

举个例子:

class Person {public String name;public int age;public Person(String name, int age) {this.name = name;this.age = age;}
}
public class Test {public static void main(String[] args) {Person person = new Person("小王", 20);Person person1 = new Person("小王", 20);System.out.println(person.equals(person1));}
}

定义一个Person类,实例化两个对象,并将其姓名和年龄都进行了统一,没重写equals方法时,运行结果为"false"。

class Person {public String name;public int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Person person = (Person) o;return age == person.age && Objects.equals(name, person.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);}
}
public class Test {public static void main(String[] args) {Person person = new Person("小王", 20);Person person1 = new Person("小王", 20);System.out.println(person.equals(person1));}
}

当我们重写了equals方法之后,此时运行结果就为"true"。

重写equals方法也很简单:在空行单击鼠标右键选择生成,再选择equals() 和 hashCode(),再一直点击下一步即可:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
当涉及到需要比较对象中的内容是否相同时,一定要重写equals方法。

对于什么是hashCode(),等我们学习完哈希表之后就会知道。

二、再谈接口

1.比较相关接口

我们来看这样一个例子:

class Person {public String name;public int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}
public class Test {public static void main(String[] args) {Person person = new Person("小王", 20);Person person1 = new Person("小李", 18);}
}

我们定义一个Person类,实例化两个对象,现在我们想比较两个对象的大小:

    public static void main(String[] args) {Person person = new Person("小王", 20);Person person1 = new Person("小李", 18);System.out.println(person > person1); //error}

对于二元运算符,两边的操作数应该为基本数据类型,但此时比较的为引用类型的数据,并不能进行比较。如果我们想比较引用类型的数据,可以实现一个接口:

class Person implements Comparable<Person> {public String name;public int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic int compareTo(Person o) {//原始语句//return 0;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) {Person person = new Person("小王", 20);Person person1 = new Person("小李", 18);System.out.println(person.compareTo(person1)); //1}
}

我们实现Comparable接口,"<>"中的为类名,并重写compareTo方法;在这个例子中,我们根据年龄的大小比较两个数据。

重写方法可以使用鼠标右键,生成的方式完成。

对于自定义类型,要想比较大小,就需要实现Comparable这个接口,实现这个接口就需要重写compareTo方法:
在这里插入图片描述
对于compareTo方法中的实现逻辑,还可以写为:

    public int compareTo(Person o) {return this.age - o.age;}

返回值为两个数据中年龄的差值。

equals和compareTo:equals比较的是 是否相等,返回值为布尔类型;compareTo比较的是 大小关系,返回值为整型。

使用Comparable这个接口虽然能解决比较大小的问题,但这个接口对类的侵入性较强:在上面这个例子中,我们通过年龄对 对象进行比较,但当我们想根据其他变量比较大小时,就需要重新编写和比较相关的所有的代码,修改成本过高。

我们可以通过Comparator这个接口来解决这个问题:

class Person {public String name;public int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}
//根据年龄比较
class AgeCom implements Comparator<Person> {@Overridepublic int compare(Person o1, Person o2) {//原始语句//return 0;return o1.age - o2.age;}
}
//根据姓名比较
class NameCom implements Comparator<Person> {@Overridepublic int compare(Person o1, Person o2) {//name为String类型,String中实现了Comparable接口,重写了compareTo方法return o1.name.compareTo(o2.name);}
}
public class Test {public static void main(String[] args) {Person person = new Person("小王", 20);Person person1 = new Person("小李", 18);//实例化对象,通过对象调用类中的方法AgeCom ageCom = new AgeCom();System.out.println(ageCom.compare(person, person1));NameCom nameCom = new NameCom();System.out.println(nameCom.compare(person, person1));}
}

在实现Comparator接口时,我们需要根据比较的逻辑重写compare方法。 实现Comparator接口后,代码的修改和增加 效率更高,使用更方便。

2.Cloneable接口和深拷贝

class Person {public String name;public int age;public Person(int age) {this.age = age;}//重写clone方法@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}@Overridepublic String toString() {return "Person{" +"age=" + age +'}';}
}
public class Test {public static void main(String[] args) {Person person = new Person(20);Person person1 = person.clone();}
}

此时代码并不能正常运行,会编译报错,我们需要解决三处错误才能使代码正常运行:
在这里插入图片描述
第一处错误是有关异常的知识,我们之后会学习到。

第三处错误:
Cloneable接口的源码为:
在这里插入图片描述
我们称之为空接口/标记接口,其作用为:证明当前类是可以被克隆的。

我们还需要注意一点:
在这里插入图片描述


解决完这三处问题后,代码就可以正常运行了:

class Person implements Cloneable {public String name;public int age;public Person(int age) {this.age = age;}//重写clone方法@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}@Overridepublic String toString() {return "Person{" +"age=" + age +'}';}
}
public class Test {public static void main(String[] args) throws CloneNotSupportedException {Person person = new Person(20);Person person1 = (Person) person.clone();System.out.println(person);System.out.println(person1);}
}

运行结果为:
在这里插入图片描述
执行图为:
在这里插入图片描述


我们对代码做出如下修改:

class Money {public double money = 66.6;
}
class Person implements Cloneable {public String name;public int age;//组合思想public Money m;public Person(int age) {this.age = age;this.m = new Money();}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}@Overridepublic String toString() {return "Person{" +"age=" + age +'}';}
}
public class Test {public static void main(String[] args) throws CloneNotSupportedException {Person person = new Person(20);Person person1 = (Person) person.clone();System.out.println(person.m.money); //66.6System.out.println(person1.m.money); //66.6System.out.println("===============");person1.m.money = 88.8;System.out.println(person.m.money); //88.8System.out.println(person1.m.money); //88.8}
}

我们使用组合的思想增加一个成员变量,并在构造方法中为其实例化一个对象。我们想修改person1的值,但最后却将person和person1的值都修改了。

在修改值的过程中,会发生如下逻辑
在这里插入图片描述
此时发生的这种现象,我们就称之为浅拷贝浅拷贝并没有将 对象中的对象进行克隆

但我们只想修改person1的值,应该怎么办呢?

我们对代码进行修改:

//实现接口
class Money implements Cloneable {public double money = 66.6;//重写方法@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}

对于Person类中的clone方法的内容进行修改 (核心!!!)

    @Overrideprotected Object clone() throws CloneNotSupportedException {//return super.clone();Person tmp = (Person) super.clone();tmp.m = (Money) this.m.clone();return tmp;}

修改完以上两处,再次运行代码,只有person1的值被修改了。

流程图为:
在这里插入图片描述
此时发生的这种现象,我们就称之为深拷贝区分深浅拷贝主要取决于代码的实现过程。

三、内部类

在Java中,可以将一个类定义在另一个类或者方法的内部,前者称为内部类,后者称为外部类。 内部类也是封装思想的一种体现。

内部类可以分为:静态内部类、实例内部类、匿名内部类、局部内部类(几乎不用)。

class A {class B {//实例内部类}static class C {//静态内部类}
}
interface D {void test();
}
public class Test {public static void main(String[] args) {new D() {@Overridepublic void test() {}}; //匿名内部类}
}

在编译之后,内部类也会生成独立的字节码文件。一个类对应一个字节码文件。

1.匿名内部类

对于匿名内部类,可以使用以下两种方式调用类中的方法:

        //方式一new D() {@Overridepublic void test() {System.out.println("嘻嘻");}}.test(); //匿名内部类
        //方式二D d = new D() {@Overridepublic void test() {System.out.println("嘻嘻");}}; //匿名内部类d.test();

对于匿名内部类我们可以认为:有一个类实现了一个接口,并在类中重写了接口中的方法。


    public static void main(String[] args) {int ret = 100;D d = new D() {@Overridepublic void test() {System.out.println("值为:" + ret);}}; //匿名内部类d.test();}

在匿名内部类中可以使用初始化的变量,

    public static void main(String[] args) {int ret = 100;ret = 200;D d = new D() {@Overridepublic void test() {System.out.println("值为:" + ret); //error}}; //匿名内部类d.test();}

但当变量的值被修改后,代码就会报错。在匿名内部类中,不能访问被修改的数据。

2.实例内部类

我们再来看实例内部类:

class Outer {public int a = 1;private int b = 2;public static int c = 3;//实例内部类class Inner {public int x = 1;private int y = 2;public void test() {System.out.println("内部类");}}public void test() {System.out.println("外部类");}
}
public class Test2 {public static void main(String[] args) {//实例化对象//方式一Outer.Inner inner = new Outer().new Inner();//方式二Outer outer = new Outer();Outer.Inner inner1 = outer.new Inner();System.out.println(inner.x); //1inner.test(); //内部类}
}

在使用内部类时,内部类的实例化需要通过外部类才能实现。

    class Inner {public int x = 1;private int y = 2;public static int y = 3;public void test() {System.out.println("内部类");}}

我们在实例内部类中添加一个静态成员变量,会报错,这是因为:static修饰的成员不依赖于对象,而内部类却需要依赖于外部类对象。

        public static final int z = 3;

使用final关键字修饰就可以解决这个错误,final修饰的为常量。


    class Inner {public int a = 666;public int x = 1;private int y = 2;public static final int z = 3;public void test() {System.out.println("内部类");System.out.println(a); //666System.out.println(Outer.this.a); //1}}

当外部类和实例内部类中存在同名的成员变量时,优先访问实例内部类中的成员变量;如果想要访问外部类中的成员变量,需要使用 “外部类类名.this.成员变量” 的方式访问。

实例内部类也受访问修饰限定符的约束。

3.静态内部类

class Outer1 {public int a = 1;private int b = 2;public static int c = 3;//静态内部类static class Inner {public int x = 1;private int y = 2;public static int z = 3;public void test() {System.out.println(x); //1System.out.println(c); //3//a为非静态System.out.println(a); //errorSystem.out.println("内部类");}}
}
public class Test3 {public static void main(String[] args) {//实例化Outer1.Inner inner = new Outer1.Inner();inner.test();}
}

静态内部类的实例化也较为特殊,需注意。

在静态内部类中不可以直接调用静态内部类外的 非静态的成员。如果想访问,需要通过外部类对象进行访问:

        public void test() {System.out.println(x); //1System.out.println(c); //3//实例化外部类对象Outer1 outer1 = new Outer1();//通过外部类对象访问System.out.println(outer1.a); //1System.out.println("内部类");}

4.局部内部类

    public void test() {//局部内部类class Inner {public int num = 1;}Inner inner = new Inner();System.out.println(inner.num);}

局部内部类只能在当前定义的 方法体的 内部使用,不能被访问修饰限定符 修饰,很少使用。


Ending。

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

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

相关文章

Spring Boot 一个注解搞定「加密 + 解密 + 签名 + 验签」

Spring Boot 一个注解搞定「加密 解密 签名 验签」本文基于 Spring Boot 3.x&#xff0c;通过一个自定义注解 AOP&#xff0c;一行注解即可给任何 Controller 方法加上 请求解密 → 验签 → 响应加密 → 加签 的完整链路&#xff0c;并可直接拷贝到生产环境使用。一、最终效…

《计算机网络》实验报告二 IP协议分析

目 录 1、实验目的 2、实验环境 3、实验内容 3.1 tcpdump 基本用法 3.2 wireshark基本用法 3.3 利用tcpdump抓包&#xff0c;wireshark分析包 4、实验结果与分析 4.1 tcpdump命令的基本用法 4.2 wireshark的基本用法 4.3 利用tcpdump抓包&#xff0c;wireshark分析包…

k8s学习记录(三):Pod基础-Node选择

一、前言 在上一篇文章中我们学习了Pod的一些基本的知识&#xff0c;今天我们将继续学习Pod。 二、K8S如何选择节点来运行Pod 我们知道在一个K8S集群中&#xff0c;会有多个工作节点&#xff08;Worker Node&#xff09;&#xff0c;那么k8s会选择那个node呢&#xff1f;接下…

3天功能开发→3小时:通义灵码2.0+DEEPSEEK实测报告,单元测试生成准确率92%的秘密

活动链接&#xff1a;https://developer.aliyun.com/topic/lingma-aideveloper?spma2c6h.29979852.J_9593490300.2.49b8110eeymlF8 前言 随着人工智能技术的迅猛发展&#xff0c;AI 赋能编程成为了必然趋势。通义灵码应运而生&#xff0c;它是阿里巴巴集团在人工智能与编程领…

【小沐学GIS】基于Rust绘制三维数字地球Earth(Rust、OpenGL、GIS)

&#x1f37a;三维数字地球GIS系列相关文章如下&#x1f37a;&#xff1a;1【小沐学GIS】基于C绘制三维数字地球Earth&#xff08;OpenGL、glfw、glut&#xff09;第一期2【小沐学GIS】基于C绘制三维数字地球Earth&#xff08;OpenGL、glfw、glut&#xff09;第二期3【小沐学GI…

ARM 学习笔记(三)

参考文献&#xff1a;《ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition》《ARM Cortex-A (ARMv7-A) Series Programmer’s Guide》1、内存类型 ARMv7-A 处理器中&#xff0c;将 Memory 定义为几种类型&#xff08;Memory Type&#xff09;&#xff1a; Strong…

Flask 框架(一):核心特性与基础配置

目录 一、为什么选择 Flask&#xff1f; 二、Flask 核心概念与初始化 2.1 程序实例初始化 2.2 运行配置&#xff1a;app.run () 参数详解 2.3 应用配置&#xff1a;三种参数设置方式 1. 字典直接配置&#xff08;简单临时场景&#xff09; 2. 配置文件导入&#xff08;生…

社交圈子系统开源社交源码 / 小程序+H5+APP 多端互通的底层技术分析

伴随社交产品向“圈子化”、“内容驱动”发展方向演进&#xff0c;打造一套支持小程序、H5、APP 互通的社交圈子系统&#xff0c;已经成为构建垂直社区的基础架构能力要求。本文围绕一套典型的多端社交兴趣平台&#xff08;即友猫社区平台&#xff09;的设计实践&#xff0c;对…

gitlab-runner配置问题记录

引言 笔者曾通过2种方式部署过 gitlab-runner&#xff0c;在 gitlab 中使用这个 runner 拉起 ci job 的过程中或多或少遇到些问题&#xff0c;主要都是 job 中无法访问宿主机的docker 等组件。本篇文档主要记录 gitlab-runner 安装及相关配置。 二进制部署 gitlab-runner 部署 …

每日面试题10:令牌桶

令牌桶算法&#xff1a;优雅的流量控制艺术在现代分布式系统中&#xff0c;流量控制如同交通信号灯般重要——它既不能让请求"堵死"系统&#xff0c;也不能放任流量"横冲直撞"。令牌桶算法&#xff08;Token Bucket Algorithm&#xff09;正是这样一种精妙…

【java】消息推送

文章目录Java网页消息推送解决方案 短轮询、长轮询、SSE、Websocket

STM32 | 有源蜂鸣器响,无源蜂鸣器播音乐

目录 Overview 有源蜂鸣器 无源蜂鸣器 有源蜂鸣器控制 GPIO配置 控制程序 无源蜂鸣器控制 反转GPIO控制 GPIO配置 控制接口 PWM控制 GPIO配置 控制函数 改变频率播音乐 原理 1. 频率决定音调 2. 占空比决定音量 GPIO初始化 结构体定义和音符频率表 播放接口 …

第十四章 gin基础

文章目录Gin快速搭建一个web服务Gin数据交互JSON串内容规范Gin使用结构体返回数据给前端Gin配置POST类型的路由Gin获取GET请求参数Gin获取POST请求参数-form-data类型Gin获取POST请求参数-JSON类型Gin获取参数绑定至结构体Gin快速搭建一个web服务 下载包 \\新建一个文件&…

Baumer工业相机堡盟工业相机如何通过YoloV8的深度学习模型实现PCB的缺陷检测(C#代码,UI界面版)

Baumer工业相机堡盟工业相机如何通过YoloV8的深度学习模型实现PCB的缺陷检测&#xff08;C#代码&#xff0c;UI界面版&#xff09;工业相机使用YoloV8模型实现PCB的缺陷检测工业相机实现YoloV8模型实现PCB的缺陷检测的技术背景在相机SDK中获取图像转换图像的代码分析工业相机图…

【Vivado那些事儿】AMD-XILINX 7系列比特流加密

前提&#xff1a;加密有风险&#xff0c;操作需谨慎前言在许多项目中&#xff0c;经过漫长的等待&#xff0c;我们的 FPGA 设计终于可以投入现场部署了。前期的资金的投入及知识产权的保护&#xff0c;我们需要对现场部署的 FPGA 进行比特流保护以防止逆向工程和未经授权的重复…

RK3588 安卓adb操作

adb&#xff08;Android Debug Bridge&#xff09;是一个用于与安卓设备进行通信和控制的工具。adb可以通过USB或无线网络连接安卓设备&#xff0c;执行各种命令&#xff0c;如安装和卸载应用&#xff0c;传输文件&#xff0c;查看日志&#xff0c;运行shell命令等。adb是安卓开…

【华为机试】70. 爬楼梯

文章目录70. 爬楼梯描述示例 1示例 2提示解题思路核心分析问题建模算法实现方法1&#xff1a;动态规划&#xff08;标准解法&#xff09;方法2&#xff1a;空间优化动态规划&#xff08;最优解&#xff09;方法3&#xff1a;递归 记忆化方法4&#xff1a;数学公式&#xff08;…

山东大学软件学院面向对象期末复习

面向对象 文章目录面向对象04 类封装接口 抽象类05 消息&#xff0c;实例化&#xff0c;静态变量方法消息动/静态类型语言对象创建类及实例具有下面特征对象数组的创建静态数据成员构造函数06_0 继承继承是向下传递的JAVA为什么不支持多重继承继承的形式特殊化继承替换原则规范…

让 Windows 用上 macOS 的系统下载与保姆级使用教程

模拟苹果桌面软件下载&#xff1a;https://xpan.com.cn/s/8NFAGT 还记得 Windows 11刚发布时&#xff0c;很多人就说“果里果气"的&#xff0c;但界面确实做的漂亮。 不知道现在有多少小伙伴正用着macOS&#xff0c;不过我敢确定&#xff0c;喜欢macOS的人绝对不少&#…

嵌入式硬件篇---继电器

继电器是一种通过小电流控制大电流的电磁开关&#xff0c;广泛应用于自动化控制、电力系统和电子设备中。以下从工作原理、应用场景和电路特点三个方面详细介绍&#xff1a;一、工作原理继电器本质是电磁控制的机械式开关&#xff0c;核心部件包括&#xff1a;线圈&#xff08;…