Java中克隆clone()与赋值引用的区别及实现

Aria
Aria
发布时间:2026-10-13| 4人浏览过
原创

在Java开发中,理解对象克隆(clone)与普通引用赋值的区别是编写健壮代码的基础。很多开发者容易混淆两者,导致在数据修改时出现意想不到的副作用。本文将通过具体的学生类(Student)代码案例,详细演示如何实现对象克隆,并对比其与直接赋值引用在内存和属性变更上的不同表现。

核心概念:引用赋值 vs 对象克隆

Java中的对象赋值(如 Student s3 = s;)仅仅是将堆内存中对象的地址赋予新的变量。这意味着 s 和 s3 指向同一个内存实体,任何一方的属性修改都会直接影响另一方。

而对象克隆则是创建了一个全新的对象副本,拥有独立的内存空间。修改克隆对象不会影响原始对象。要实现这一功能,需要涉及以下几个关键步骤:

  • 实现 Cloneable 接口:这是一个标记接口,用于告知JVM该对象允许被克隆。
  • 重写 clone() 方法:从 protected 改为 public 或保持保护级别但通过包装调用,以公开克隆能力。
  • 处理 CloneNotSupportedException:虽然实现了接口,但语法上仍需声明可能抛出的异常。

实践操作:实现与测试克隆

第1步:定义支持克隆的学生类

首先,我们需要创建一个基础的学生类。为了实现克隆,必须实现 java.lang.Cloneable 接口。注意,这个接口没有任何方法,它仅作为一个“通行证”。

package 克隆clone;

/*要克隆必须实现这个借口:Cloneable,以标记这个对象可以克隆
 Cloneable:此类实现了 Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。
 这个接口是标记接口,告诉我们实现该接口的类就可以实现对象的复制了。
 */
public class Student implements Cloneable {
	private String name;
	private int age;

	public Student() {
	}

	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	// 重写克隆方法子列才可以调用
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}

}

第2步:编写测试代码验证差异

接下来,我们创建一个测试类 StudentClone 来对比两种方式的实际效果。我们将观察在对新变量进行修改时,原始对象的变化情况。

package 克隆clone;

/*
 *	protected void finalize():当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。用于垃圾回收,但是什么时候回收不确定。
 *	protected Object clone():创建并返回此对象的一个副本。
 *		A:重写该方法
 *
 * Cloneable:此类实现了 Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。
 * 	这个接口是标记接口,告诉我们实现该接口的类就可以实现对象的复制了。
 */
public class StudentClone {
	public static void main(String[] args) throws CloneNotSupportedException {
		//创建学生对象
		Student s = new Student();
		s.setName("林青霞");
		s.setAge(27);
		
		//克隆学生对象
		Object obj = s.clone();
		Student s2 = (Student)obj;
		System.out.println("---------");
		
		System.out.println(s.getName()+"---"+s.getAge());
		System.out.println(s2.getName()+"---"+s2.getAge());
		
		//以前的做法:直接赋值引用
		Student s3 = s;
		System.out.println(s3.getName()+"---"+s3.getAge());
		System.out.println("---------");
		
		//其实是有区别的
		//修改引用指向的对象
		s3.setName("刘意");
		s3.setAge(30);
		System.out.println(s.getName()+"---"+s.getAge());
		System.out.println(s2.getName()+"---"+s2.getAge());
		System.out.println(s3.getName()+"---"+s3.getAge());
		System.out.println("---------");
		
		//把克隆的改一下呢?
		//修改克隆的对象
		s2.setName("克隆改的名称");
		s2.setAge(66);
		System.out.println(s.getName()+"---"+s.getAge());
		System.out.println(s2.getName()+"---"+s2.getAge());
		System.out.println(s3.getName()+"---"+s3.getAge());
		
		//也就是说把一个对象的引用赋值给一个引用,当这个引用发什么该改变的时候,同一个引用指向的对象会发生改变,但是克隆的不会变化,
		//把克隆的对象改变他的一些属性,那么也不会影响到其他的对象的值的改变
		
	}
}

结果分析与排错指南

通过上述代码的运行结果,我们可以得出以下明确结论:

  1. 引用赋值(Shallow Reference):当执行 Student s3 = s; 后,s3 和 s 指向同一块内存。后续通过 s3 修改姓名和年龄,s 的内容也随之改变。这在数据不需要隔离的场景下可能方便,但在需要备份或防止副作用时必须避免。
  2. 对象克隆(Clone):执行 s.clone() 后得到的 s2 是一个独立的新对象。即使后续大幅修改 s2 的属性(如改名改年龄),原始的 s 对象内容保持不变。这证明了克隆成功建立了内存隔离。

注意事项:

  • 浅拷贝问题:上述示例中的 Student 只包含基本数据类型(int)和不可变字符串(String)。如果类中包含可变对象引用(如自定义类数组或HashMap),默认的 super.clone() 执行的是浅拷贝,即克隆对象内的引用仍指向原对象里的内部对象。如需深拷贝,需手动递归克隆成员变量。
  • 线程安全:在多线程环境下,直接使用 clone() 可能存在风险,建议配合锁或使用 Serialization 方式进行深拷贝。

总结

Java中的 clone() 方法是实现对象独立副本的关键机制,区别于简单的引用赋值。掌握 Cloneable 接口的使用以及重写 clone 方法的正确姿势,能够有效避免因数据共享导致的逻辑错误。以上就是Java中的克隆clone()方法和赋值引用的详细区别的全部内容,更多关于Java对象内存管理的资料请关注本站其它相关文章!

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。