目录
题目需求
Object的
clone
方法可以帮助我们克隆对象。现在需编写一个类Car
包含:1.属性:
private String name; private CarDriver driver; private int[] scores;
2.无参构造函数
public Car() { }
3.方法:
@Override public String toString() { return "Car [name=" + name + ", driver=" + driver + ", scores=" + Arrays.toString(scores) + "]"; }
setter/getter方法与
clone
方法。注意:clone
方法需实现对象的深度克隆。
CarDriver
为已经定义好的类,部分代码如下:class CarDriver { private String name; public CarDriver() {} //setter/getter //toString }
在main方法中,利用对象car1克隆出car2,并修改car2的driver信息。
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in) ; Car car1 = new Car() ; CarDriver driver1 = new CarDriver() ; CarDriver driver2 = new CarDriver() ; int[] scores = {10000000} ; driver1.setName(scanner.nextLine()); driver2.setName(scanner.nextLine()); car1.setName(scanner.nextLine()); car1.setDriver(driver1); car1.setScores(scores); //克隆 Car car2 = car1.clone() ; //修改信息 car2.setDriver(driver2); System.out.println(car1); System.out.println(car2); } } /* 请在这里填写答案,即Car类的完整代码 */
输入样例:
ZLX HHZ Honda
输出样例:
Car [name=Honda, driver=CarDriver{name='ZLX'}, scores=[10000000]] Car [name=Honda, driver=CarDriver{name='HHZ'}, scores=[10000000]]
Object clone() 方法
方法概述
clone 方法是 Java 中的一个方法,用于创建并返回对象的副本。该方法定义在java.lang.Object
类中,因此所有 Java 类都继承了这个方法。clone
方法的作用是创建一个新的对象,该对象与原始对象具有相同的状态。在使用 clone
方法时,需要确保实现了 Cloneable
接口,并且需要进行类型转换。
需要注意的是,默认情况下,clone
方法执行的是浅拷贝(Shallow Copy),即对于引用类型的属性,只会复制引用而不会复制实际的对象。如果需要实现深度克隆,则需要在 clone
方法中手动处理引用类型的属性,确保其内部的对象也得到复制而不是共享。
clone
方法提供了一种方便的方式来创建对象的副本,但在实现深度克隆时需要特别注意引用类型的处理。
深拷贝与浅拷贝
深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是在对象复制过程中两种不同的方式,它们之间的区别主要体现在复制引用类型属性时的处理方式:
-
浅拷贝:
- 浅拷贝是指创建一个新对象,该对象的所有非静态属性都与原始对象相同,如果属性是基本数据类型,则复制其值;如果属性是引用类型,则复制其引用(内存地址),新对象和原始对象的引用类型属性指向同一个对象。
- 在浅拷贝中,当修改新对象的引用类型属性时,会影响原始对象的引用类型属性,因为它们指向同一个对象。
-
深拷贝:
- 深拷贝是指创建一个新对象,该对象的所有属性(包括引用类型属性)都是全新复制的,即新对象拥有原始对象所有属性的副本。
- 在深拷贝中,新对象和原始对象的引用类型属性指向不同的对象,修改新对象的引用类型属性不会影响原始对象的引用类型属性。
总的来说,浅拷贝只复制对象本身和对象中的基本数据类型属性,而引用类型属性仍然指向相同的对象;而深拷贝则会递归复制对象及其所有引用类型属性,确保新对象拥有独立的副本,避免对象之间相互影响的情况发生。
标识接口
在调用clone方法时,我们需要确保实现Cloneable
接口,而类似Cloneable
这样的接口,被称为标识接口(Marker Interface)。
标识接口(Marker Interface)是一种不包含任何方法的接口,其主要作用是用于对类施加标记或标识。通过实现标识接口,可以告诉编译器或其他代码某个类具有特定的性质或特征。Java 中的常见标识接口有 Serializable
、Cloneable
等。
标识接口的特点
标识接口的特点包括:
- 不包含任何方法,只是一个空接口。
- 通过让类实现标识接口来表明该类具有某种特定的性质或需要参与某种处理。
- 标识接口主要通过类是否实现了接口来确定类的属性,而不是通过接口中的方法来实现功能。
标识接口的优点
使用标识接口的优点包括:
- 提供了更多的灵活性,可以根据接口的存在与否来进行条件判断。
- 在某些情况下,标识接口可以简化代码逻辑,提高代码的可读性和可维护性。
需要注意的是,由于标识接口本身没有定义任何方法,因此在使用时需要谨慎考虑,避免滥用标识接口导致代码混乱不清。
深拷贝的实现
思路分析
正如我们刚刚提到,直接调用clone方法时,实现的是浅拷贝,而现在我们需要实现对象的深度克隆,显然需要在Car类中手动重写clone方法。为了达到目的,我们可以在新的clone方法中,创建一个新的Car对象,然后将原始对象的属性值逐一复制到新对象中,最后返回新的对象,实现深度克隆。
代码实现
public Car clone() {
//创建新对象
Car clonedCar = new Car();
//将原始对象的属性值逐一复制到新对象中
clonedCar.setName(this.name);
clonedCar.setDriver(new CarDriver());
clonedCar.getDriver().setName(this.driver.getName());
clonedCar.setScores(Arrays.copyOf(this.scores, this.scores.length));
//返回新对象
return clonedCar;
}
完整代码
import java.util.Scanner;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in) ;
Car car1 = new Car() ;
CarDriver driver1 = new CarDriver() ;
CarDriver driver2 = new CarDriver() ;
int[] scores = {10000000} ;
driver1.setName(scanner.nextLine());
driver2.setName(scanner.nextLine());
car1.setName(scanner.nextLine());
car1.setDriver(driver1);
car1.setScores(scores);
//克隆
Car car2 = car1.clone() ;
//修改信息
car2.setDriver(driver2);
System.out.println(car1);
System.out.println(car2);
}
}
class Car {
private String name;
private CarDriver driver;
private int[] scores;
public Car() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public CarDriver getDriver() {
return driver;
}
public void setDriver(CarDriver driver) {
this.driver = driver;
}
public int[] getScores() {
return scores;
}
public void setScores(int[] scores) {
this.scores = scores;
}
@Override
public String toString() {
return "Car [name=" + name + ", driver=" + driver + ", scores=" + Arrays.toString(scores) + "]";
}
//新的clone方法
@Override
public Car clone() {
//创建新的对象
Car clonedCar = new Car();
//将原始对象的属性值逐一复制到新对象中
clonedCar.setName(this.name);
clonedCar.setDriver(new CarDriver());
clonedCar.getDriver().setName(this.driver.getName());
clonedCar.setScores(Arrays.copyOf(this.scores, this.scores.length));
//返回新对象
return clonedCar;
}
}
class CarDriver {
private String name;
public CarDriver() {
}
//setter/getter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//toString
@Override
public String toString() {
return "CarDriver{" +
"name='" + name + '\'' +
'}';
}
}
运行效果
后言
深度拷贝,意味着创建了一个全新的对象,与原对象互不干扰(主要是新对象和原始对象的引用类型属性不共享内存地址),在对拷贝出来的对象进行修改时出差错的概率会更小。实际开发中,我们应当根据需求选择合适的克隆方式。
貌似还可以通过将对象写入流中,再从流中获取对象的方式来进行深度拷贝,但在解决上述问题时,我并没有采取这种方式,大家可以尝试实现一下。
新人创作,水平有限,如有错误,敬请指出!