[Java 基础]Object 类

java.lang.Object 是 Java 所有类的直接或间接父类,Java 中每个类都默认继承 Object 类(即使你没写 extends Object)。

Object 中的常用方法:

方法名功能简介
toString()返回对象的字符串表示
equals(Object)判断两个对象是否“逻辑相等”
hashCode()返回哈希值,常用于集合类
getClass()返回此对象的运行时类
clone()克隆对象(实现 Cloneable 时有效)
finalize()对象被 GC 回收前调用(不推荐使用)

toString

我们使用 System.out.println() 打印对象,调用的就是对象的 toString 方法,将对象的 toString 方法返回的结果打印出来。

public class Person {private String name;public Person(String name) {this.name = name;}// 默认输出是:类名@哈希值// 重写 toString() 提供可读性@Overridepublic String toString() {return "Person[name=" + name + "]";}
}

equals

equals 方法是用来比较两个对象是否逻辑相等,Object 类的 equals 方法直接比较的是两个对象地址。我们一般需要修改 equals 方法,比如:两个公民对象,如果身份证 id 是一样的,从我们的现实生活的经验中判断,这两个人肯定是同一个人,即他们是相等的。

对于基本类型,我们可以直接使用 == 比较他们是不是相等的,如果他们的值是相等的,==返回 true,否则返回 false,对于对象,我们需要使用 equals 方法判断两个对象是不是相等的。

重写 equals 方法的要点,要遵循如下的特性:

  1. 自反性:x.equals(x) 必须返回 true
  2. 对称性:x.equals(y) 和 y.equals(x) 必须返回相同结果
  3. 传递性:如果 x.equals(y)且y.equals(z),则 x.equals(z) 必须为 true
  4. 一致性:多次调用 x.equals(y) 应该返回相同结果
  5. 非空性:x.equals(null) 必须返回 false

重写 equals() 方法后,必须重写 hashCode() 方法,以维护 Java 对象的通用约定,并确保基于哈希的集合(如 HashSet、HashMap 和 HashTable)能够正常工作。Java 的 Object 类对 equals() 和 hashCode() 方法之间有明确的约定:

  • 如果两个对象根据 equals() 方法相等,那么它们的 hashCode() 方法必须返回相同的值
  • 如果两个对象的 hashCode() 方法返回相同的值,它们根据 equals() 方法不一定相等(哈希冲突)
  • 哈希表的功能: 哈希表使用 hashCode() 方法来确定对象在表中的位置(桶)。如果 equals() 被重写但 hashCode() 没有,则两个逻辑上相等的对象(equals() 返回 true)可能具有不同的哈希码。这会导致以下问题:1. 无法找到对象: 当你试图在 HashSet 中查找一个对象时,HashSet 会使用对象的 hashCode() 来定位它所在的桶。如果 hashCode() 与原始对象的 hashCode() 不同(即使它们逻辑上相等),HashSet 将无法找到该对象,即使它已经存在于集合中。2. 重复元素: HashSet 旨在防止重复元素。如果两个逻辑上相等的 对象具有不同的哈希码,HashSet 会将它们视为不同的对象,从而允许将重复元素添加到集合中。3. HashMap 的不一致性: HashMap 也依赖于 hashCode() 和 equals() 来正确存储和检索键值对。如果键的 hashCode() 和 equals() 不一致,HashMap 的行为将变得不可预测。

下面是一个重写 equals 方法的例子:

public class Person {private String name;public Person(String name) {this.name = name;}// 重写equals方法@Overridepublic boolean equals(Object obj) {// 1. 检查是否是同一个对象if (this == obj) {return true;}// 2. 检查是否为null或类型不同if (obj == null || getClass() != obj.getClass()) {return false;}// 3. 类型转换Person person = (Person) obj;// 4. 比较关键字段return name != null ? name.equals(person.name) : person.name == null;}// 重写equals时也应该重写hashCode@Overridepublic int hashCode() {return name != null ? name.hashCode() : 0;}
}
Person p1 = new Person("Tom");
Person p2 = new Person("Tom");// == 比较的是地址
System.out.println(p1 == p2); // false// 默认 equals 也是比较地址
// 可通过重写实现“内容相等”
System.out.println(p1.equals(p2)); // true(需重写后)

hashCode

hashCode 方法返回对象的 hashCode 编码,在上面的 equals 方法中我们讲过,要确保两个对象如果 equals 方法判断他们是相等的,那么他们的 hashCode 方法返回的哈希码也必须是相等的。

// 在集合类(如 HashMap、HashSet)中要重写 hashCode 与 equals 保持一致性
@Override
public int hashCode() {return name.hashCode(); // 简单写法
}

getClass

getClass 方法返回的是此 Object 的运行时类。

Person p = new Person("Jerry");
System.out.println(p.getClass().getName()); // 输出类的全名

clone

按照惯例,如果一个类希望能够被克隆,它应该重写Object.clone()方法,并将其访问修饰符改为public,通常返回它自己的类型(需要进行类型转换)。在重写的方法中,通常会先调用super.clone()来获得一个基本的副本。

Object.clone()的默认行为是执行浅拷贝。 这里涉及到连个概念,浅拷贝(Shallow Copy)与深拷贝(Deep Copy)。

浅拷贝:

  • 对于原始数据类型(int, double, boolean等),直接复制值。
  • 对于引用类型(对象引用),复制的是引用本身,而不是被引用的对象。这意味着原始对象和克隆对象中的引用字段将指向内存中的同一个对象。
  • 后果: 如果原始对象或克隆对象修改了共享引用对象的状态,这种改变会同时反映在另一个对象中。在大多数情况下,这可能不是期望的行为,因为它打破了克隆对象与原始对象之间的独立性。

深拷贝:

  • 为了实现深拷贝,你需要在重写的clone()方法中,对所有引用类型的字段(如果这些字段也是可克隆的)手动调用它们的clone()方法,从而创建这些引用对象的独立副本。
  • 复杂性: 如果对象包含多层嵌套的引用类型,实现深拷贝可能会变得非常复杂,因为你需要递归地克隆所有被引用的对象。
class Address implements Cloneable {String city;String street;public Address(String city, String street) {this.city = city;this.street = street;}@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone(); // Address类的浅拷贝}@Overridepublic String toString() {return "Address [city=" + city + ", street=" + street + "]";}
}class Student implements Cloneable {String name;int age;Address address; // 引用类型public Student(String name, int age, Address address) {this.name = name;this.age = age;this.address = address;}// 浅拷贝的实现@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone(); // 默认是浅拷贝}// 深拷贝的实现(需要手动处理引用类型字段)public Object deepClone() throws CloneNotSupportedException {Student clonedStudent = (Student) super.clone();// 对引用类型字段进行深拷贝clonedStudent.address = (Address) address.clone();return clonedStudent;}@Overridepublic String toString() {return "Student [name=" + name + ", age=" + age + ", address=" + address + "]";}
}public class CloneDemo {public static void main(String[] args) throws CloneNotSupportedException {Address originalAddress = new Address("New York", "Broadway");Student originalStudent = new Student("Alice", 20, originalAddress);// 浅拷贝示例Student shallowClonedStudent = (Student) originalStudent.clone();System.out.println("Original Student (Shallow): " + originalStudent);System.out.println("Shallow Cloned Student: " + shallowClonedStudent);// 修改浅拷贝后的地址,观察原始对象的变化shallowClonedStudent.address.city = "Los Angeles";System.out.println("After changing shallowClonedStudent's city:");System.out.println("Original Student (Shallow): " + originalStudent); // Original也会改变System.out.println("Shallow Cloned Student: " + shallowClonedStudent);System.out.println("---");// 深拷贝示例Address originalAddress2 = new Address("London", "Oxford Street");Student originalStudent2 = new Student("Bob", 22, originalAddress2);Student deepClonedStudent = (Student) originalStudent2.deepClone();System.out.println("Original Student (Deep): " + originalStudent2);System.out.println("Deep Cloned Student: " + deepClonedStudent);// 修改深拷贝后的地址,观察原始对象的变化deepClonedStudent.address.city = "Paris";System.out.println("After changing deepClonedStudent's city:");System.out.println("Original Student (Deep): " + originalStudent2); // Original不会改变System.out.println("Deep Cloned Student: " + deepClonedStudent);}
}

尽管clone()方法提供了对象复制的功能,但它在实际开发中很少被推荐使用,并且存在一些缺点:

  • 破坏封装性:clone()方法需要访问对象的内部状态,这可能违反封装原则。
  • **Cloneable**接口的不足:Cloneable是一个标记接口,它没有定义clone()方法。这意味着编译器无法检查你是否正确地重写了clone()方法,或者是否处理了CloneNotSupportedException
  • 不调用构造函数:clone()方法通过直接复制内存来创建对象,不调用任何构造函数。这可能导致一些需要构造函数来正确初始化的逻辑被跳过。
  • 不可变对象问题: 对于包含final字段的对象,clone()方法无法修改这些final字段,因为它们只能在构造函数中初始化。
  • 链式克隆的复杂性: 当一个对象包含其他对象的引用时,为了实现深拷贝,你必须确保所有被引用的类也实现了Cloneable并正确地重写了clone()方法。这会导致一个“病毒式”的Cloneable实现。
  • 异常处理:Object.clone()会抛出受检查异常CloneNotSupportedException,即使你的类实现了Cloneable,也需要在签名中声明或捕获它。

鉴于这些缺点,通常有更好的替代方案来实现对象复制:

  1. 拷贝构造函数(Copy Constructor):创建一个接收同类型对象作为参数的构造函数,并在其中手动复制字段。 这是最常见和推荐的方式,它提供了更好的控制,允许你选择是进行浅拷贝还是深拷贝,并且可以处理final字段。
  2. 拷贝工厂方法(Copy Factory Method): 提供一个静态工厂方法来创建对象的副本。
  3. 序列化/反序列化(Serialization/Deserialization): 通过将对象序列化到字节流(例如内存中的ByteArrayOutputStream),然后从该字节流反序列化回来,可以实现深拷贝。 优点是简单,自动处理深拷贝。 缺点是性能开销较大,且所有涉及的类都必须实现Serializable接口。
  4. 第三方库: 许多现代框架和库提供了更强大、更灵活的对象映射和复制工具(如Apache Commons Lang的SerializationUtils.clone())。

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

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

相关文章

大数据学习(135)-Linux系统性指令

🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一…

【Fifty Project - D35】

今日完成记录 TimePlan完成情况7:00 - 7:40爬坡√8:30 - 11:30Rabbit MQ√17:30 - 18:30羽毛球√ RabbitMQ 消费者端如何保证可靠性? 消息投递过程出现网络故障消费者接收到消息但是突然宕机…

P3 QT项目----记事本(3.4)

3.4 文件选择对话框 QFileDialog 3.4.1 QFileDialog 开发流程 使用 QFileDialog 的基本步骤通常如下: 实例化 :首先,创建一个 QFileDialog 对象的实例。 QFileDialog qFileDialog;设置模式 :根据需要设置对话框的模式&…

学习笔记(26):线性代数-张量的降维求和,简单示例

学习笔记(26):线性代数-张量的降维求和,简单示例 1.先理解 “轴(Axis)” 的含义 张量的 “轴” 可以理解为 维度的方向索引 。对于形状为 (2, 3, 4) 的张量,3 个轴的含义是: 轴 0(axis0&…

健康档案实训室:构建全周期健康管理的数据基石

一、健康档案实训室建设背景 随着“健康中国2030”战略深入推进,健康档案作为居民健康数据的核心载体,在疾病预防、慢性病管理、医疗决策等领域的价值日益凸显。在此背景下,健康档案实训室建设成为职业院校对接政策要求、培养专业健康管理…

【MATLAB第119期】基于MATLAB的KRR多输入多输出全局敏感性分析模型运用(无目标函数,考虑代理模型)

【MATLAB第119期】基于MATLAB的KRR多输入多输出全局敏感性分析模型运用(无目标函数,考虑代理模型) 下一期研究SHAP的多输入多输出敏感性分析方法 一、SOBOL(无目标函数) (1)针对简单线性数据…

Linux常用文件目录命令

浏览目录命令: ls 、pwd目录操作命令:cd、mkdir、rmdir浏览文件命令:cat、more、less、head、tail文件操作命令:cp、rm、mv、find、grep、tar 浏览目录命令 ls ◼ 命令名称:ls ◼ 命令英文原意:list ◼ …

PIN码vs密码,电脑登录的快捷键你用对了吗?

你是否也遇到过这样的窘境:信心满满地输入电脑开机密码,屏幕却无情地提示“密码错误”。仔细一看,才发现登录界面悄悄地变成了要求输入“PIN码”。这种因为混淆了PIN码和账户密码而导致的开机失败,相信不少朋友都碰到过。 PIN码作…

【大模型科普】AIGC技术发展与应用实践(一文读懂AIGC)

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈人工智能与大模型应用 ⌋ ⌋ ⌋ 人工智能(AI)通过算法模拟人类智能,利用机器学习、深度学习等技术驱动医疗、金融等领域的智能化。大模型是千亿参数的深度神经网络(如ChatGPT&…

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…

XXE漏洞知识

目录 1.XXE简介与危害 XML概念 XML与HTML的区别 1.pom.xml 主要作用 2.web.xml 3.mybatis 2.XXE概念与危害 案例:文件读取(需要Apache >5.4版本) 案例:内网探测(鸡肋) 案例:执行命…

02-性能方案设计

需求分析与测试设计 根据具体的性能测试需求,确定测试类型,以及压测的模块(web/mysql/redis/系统整体)前期要与相关人员充分沟通,初步确定压测方案及具体的性能指标QA完成性能测试设计后,需产出测试方案文档发送邮件到项目组&…

STL优先级队列的比较函数与大堆小堆的关系

STL中的priority_queue&#xff08;优先级队列&#xff09;通过比较函数来确定元素的优先级顺序&#xff0c;从而决定其内部是形成大堆还是小堆。以下是关键点总结&#xff1a; 默认行为与大堆&#xff1a; 默认情况下&#xff0c;priority_queue使用std::less<T>作为比较…

React---day11

14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store&#xff1a; 我们在使用异步的时候理应是要使用中间件的&#xff0c;但是configureStore 已经自动集成了 redux-thunk&#xff0c;注意action里面要返回函数 import { configureS…

OD 算法题 B卷【反转每对括号间的子串】

文章目录 反转每对括号间的子串 反转每对括号间的子串 给出一个字符串s&#xff0c; 仅含有小写英文字母和英文括号’(’ ‘)’&#xff1b;按照从括号内到外的顺序&#xff0c;逐层反转每对括号中的字符串&#xff0c;并返回最终的结果&#xff1b;结果中不能包含任何括号&am…

如何做好一份技术文档?从规划到实践的完整指南

如何做好一份技术文档&#xff1f;从规划到实践的完整指南 &#x1f31f; 嗨&#xff0c;我是IRpickstars&#xff01; &#x1f30c; 总有一行代码&#xff0c;能点亮万千星辰。 &#x1f50d; 在技术的宇宙中&#xff0c;我愿做永不停歇的探索者。 ✨ 用代码丈量世界&…

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…

详细讲解Flutter GetX的使用

Flutter GetX 框架详解&#xff1a;状态管理、路由与依赖注入 GetX 是 Flutter 生态中一款强大且轻量级的全功能框架&#xff0c;集成了状态管理、路由管理和依赖注入三大核心功能。其设计理念是简洁高效&#xff0c;通过最小的代码实现最大的功能&#xff0c;特别适合快速开发…

【大模型:知识库管理】--Dify接入RAGFlow 知识库

ragflow的官方文档&#xff1a; HTTP API 接口 |抹布流 --- HTTP API | RAGFlow 接着前文&#xff0c;我们已经创建了知识库&#xff0c;那么如何才能使用它呢&#xff1f; 当然也是通过网络API的形式去调用它。本文将讲解两种方式&#xff1a; Dify调用python源码调用 目录…

Vue 模板配置项深度解析

Vue 模板配置项深度解析 在 Vue 组件开发中&#xff0c;template 是定义组件视图结构的核心配置项。作为 Vue 专家&#xff0c;我将全面解析模板的各个方面&#xff0c;帮助你掌握高效构建 Vue 组件的艺术。 一、模板基础概念 1. 模板的本质 声明式渲染&#xff1a;描述 UI…