[Java 基础]Object 类

news/2025/6/19 18:23:13

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())。

https://dhexx.cn/news/show-5537641.html

相关文章

el-select下拉框 添加 el-checkbox 多选框

效果 vue <el-select v-model"value" multiple style"width: 100%" popper-class"select-popover-class" placeholder"请选择试验项目"><el-option v-for"item in options" :key"item.value" :value&qu…

python基础语法Ⅰ

python基础语法Ⅰ 常量和表达式变量是什么变量的语法1.定义变量使用变量 变量的类型1.整数2.浮点数(小数)3.字符串4.布尔5.其他 动态类型特征注释注释是什么注释的语法1.行注释2.文档字符串 注释的规范 常量和表达式 我们可以把python当作一个计算器&#xff0c;来进行一些算术…

WebRTC调研

WebRTC是什么&#xff0c;为什么&#xff0c;如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…

MyBatis中关于缓存的理解

MyBatis缓存 MyBatis系统当中默认定义两级缓存&#xff1a;一级缓存、二级缓存 默认情况下&#xff0c;只有一级缓存开启&#xff08;sqlSession级别的缓存&#xff09;二级缓存需要手动开启配置&#xff0c;需要局域namespace级别的缓存 一级缓存&#xff08;本地缓存&#…

DingDing机器人群消息推送

文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人&#xff0c;点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置&#xff0c;详见说明文档 成功后&#xff0c;记录Webhook 2 API文档说明 点击设置说明 查看自…

智能体革命:企业如何构建自主决策的AI代理?

OpenAI智能代理构建实用指南详解 随着大型语言模型&#xff08;LLM&#xff09;在推理、多模态理解和工具调用能力上的进步&#xff0c;智能代理&#xff08;Agents&#xff09;成为自动化领域的新突破。与传统软件仅帮助用户自动化流程不同&#xff0c;智能代理能够自主执行工…

6.计算机网络核心知识点精要手册

计算机网络核心知识点精要手册 1.协议基础篇 网络协议三要素 语法&#xff1a;数据与控制信息的结构或格式&#xff0c;如同语言中的语法规则语义&#xff1a;控制信息的具体含义和响应方式&#xff0c;规定通信双方"说什么"同步&#xff1a;事件执行的顺序与时序…

分布式系统常见的四种数据一致性模型

欢迎来到啾啾的博客&#x1f431;。 记录学习点滴。分享工作思考和实用技巧&#xff0c;偶尔也分享一些杂谈&#x1f4ac;。 有很多很多不足的地方&#xff0c;欢迎评论交流&#xff0c;感谢您的阅读和评论&#x1f604;。 目录 引言强一致性关键技术权衡使用案例 顺序一致性关…

[Linux] 命令行管理文件

目录 FHS 文件路径导航 ls命令 tree命令 stat命令 touch命令 命令行管理文件 mkdir命令 cp命令 mv命令 rm和rmdir命令 软链接 硬链接 软连接硬链接区别 shell扩展匹配文件 FHS FHS采用树形结构组织文件&#xff0c;定义了系统中每个区域的用途、所需要的最小构…

STM32标准库-ADC数模转换器

文章目录 一、ADC1.1简介1. 2逐次逼近型ADC1.3ADC框图1.4ADC基本结构1.4.1 信号 “上车点”&#xff1a;输入模块&#xff08;GPIO、温度、V_REFINT&#xff09;1.4.2 信号 “调度站”&#xff1a;多路开关1.4.3 信号 “加工厂”&#xff1a;ADC 转换器&#xff08;规则组 注入…