描述
1、浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。
2、深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。
在 Java 中,所有的 Class 都继承自 Object ,而在 Object 上,存在一个 clone() 方法,
它被声明为了 protected ,所以我们可以在其子类中,使用它。而无论是浅拷贝还是深拷贝,都需要实现 clone() 方法,来完成操作。
代码实践
public static void main(String[] args) {
FathClass fathClassA = new FathClass();
fathClassA.name = "张三";
fathClassA.age= 19;
fathClassA.childClass = new ChildClass();
fathClassA.childClass.cName = "李四";
fathClassA.childClass.cAge = 20;
FathClass fathClassB = (FathClass)fathClassA.clone();
// 深拷贝
log.info("fathClassA == fathClassB : {}", fathClassA == fathClassB);
log.info("fathClassA hashCode : {}", fathClassA.hashCode());
log.info("fathClassB hashCode : {}", fathClassB.hashCode());
log.info("fathClassA.name : {}", fathClassA.name);
log.info("fathClassB.name : {}", fathClassB.name);
log.info("-------------------------------------------------------");
// 浅拷贝
log.info("A.childClass == B.childClass : {}", fathClassA.childClass == fathClassB.childClass);
log.info("A.childClass hashCode : {}", fathClassA.childClass.hashCode());
log.info("B.childClass hashCode : {}", fathClassB.childClass.hashCode());
}
static class FathClass implements Cloneable{
public String name;
public int age;
ChildClass childClass;
@Override
protected Object clone() {
try {
// 浅拷贝
FathClass fathClone = (FathClass) super.clone();
fathClone.childClass = (ChildClass) this.childClass.clone();
return fathClone;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
static class ChildClass implements Cloneable{
public String cName;
public int cAge;
@Override
protected Object clone() {
try {
// 浅拷贝
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
深拷贝比较常用的方案有两种: 序列化(serialization)这个对象,再反序列化回来,就可以得到这个新的对象,无非就是序列化的规则需要我们自己来写。 继续利用 clone() 方法,既然 clone() 方法,是我们来重写的,实际上我们可以对其内的引用类型的变量,再进行一次 clone()。 总结: 实则浅拷贝和深拷贝只是相对的,如果一个对象内部只有基本数据类型,那用 clone() 方法获取到的就是这个对象的深拷贝,而如果其内部还有引用数据类型,那用 clone() 方法就是一次浅拷贝的操作。 但是如果其引用数据类型也使用了clone() 方法,取到的就是这个对象的深拷贝,对于引用数据类型对象是浅拷贝。
1.System.arraycopy()的理解 * public static native void arraycopy( * Object src, //源数组 * int srcPos, //源数组的起始位置 * Object dest, //目标数组 * int destPos, //目标数组的起始位置 * int length); //复制长度 深复制还是浅复制?线程安全?还是不安全高效还是低效?
/**
* 1.1.深复制还是浅复制
*/
// 初始化对象数组
User[] users = new User[]{
new User(1, "seven", "[email protected]"),
new User(2, "six", "[email protected]"),
new User(3, "ben", "[email protected]")};
// 新建一个目标对象数组
User[] target = new User[users.length];
// 实现复制
System.arraycopy(users, 0, target, 0, users.length);
// 判断
log.info("源对象与目标对象的物理地址是否一样:" + (users[0] == target[0] ? "浅复制" : "深复制"));
target[0].setEmail("[email protected]");
log.info("修改目标对象的属性值后源对象users:");
for (User user : users) {
log.info(user.toString());
}
/**
* 1.2.线程安全,还是不安全
*/
ArrayCopyThreadSafe threadSafe = new ArrayCopyThreadSafe();
try {
threadSafe.doThreadSafeCheck();
} catch (Exception e) {
e.printStackTrace();
}
/**
* 1.3.高效还是低效
*/
String[] srcArray = new String[1000000];
String[] forArray = new String[srcArray.length];
String[] arrayCopyArray = new String[srcArray.length];
//初始化数组
for(int index = 0 ; index < srcArray.length ; index ++){
srcArray[index] = String.valueOf(index);
}
long forStartTime = System.currentTimeMillis();
for(int index = 0 ; index < srcArray.length ; index ++){
forArray[index] = srcArray[index];
}
long forEndTime = System.currentTimeMillis();
log.info("for方式复制数组耗时(单位毫秒):" + (forEndTime - forStartTime));
long arrayCopyStartTime = System.currentTimeMillis();
System.arraycopy(srcArray,0,arrayCopyArray,0,srcArray.length);
long arrayCopyEndTime = System.currentTimeMillis();
log.info("System.arraycopy复制数组耗时(单位毫秒):" + (arrayCopyEndTime - arrayCopyStartTime));
}
static class User {
private Integer id;
private String username;
private String email;
// 无参构造函数
public User() {
}
// 有参的构造函数
public User(Integer id, String username, String email) {
super();
this.id = id;
this.username = username;
this.email = email;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", email=" + email + "]";
}
}
static class ArrayCopyThreadSafe {
private int[] arrayOriginal = new int[1024 * 1024 * 10];
private int[] arraySrc = new int[1024 * 1024 * 10];
private int[] arrayDist = new int[1024 * 1024 * 10];
private ReentrantLock lock = new ReentrantLock();
private void modify() {
for (int i = 0; i < arraySrc.length; i++) {
arraySrc[i] = i + 1;
}
}
private void copy() {
System.arraycopy(arraySrc, 0, arrayDist, 0, arraySrc.length);
}
private void init() {
for (int i = 0; i < arraySrc.length; i++) {
arrayOriginal[i] = i;
arraySrc[i] = i;
arrayDist[i] = 0;
}
}
private void doThreadSafeCheck() throws Exception {
for (int i = 0; i < 100; i++) {
log.info("run count: " + (i + 1));
init();
Condition condition = lock.newCondition();
new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
try{
condition.signalAll();
}catch (Exception e){
}finally {
lock.unlock();
copy();
}
}
}).start();
lock.lock();
try{
// 这里使用 Condition 来保证拷贝线程先已经运行了.
condition.await();
}catch (Exception e){
}finally {
lock.unlock();
}
Thread.sleep(2); // 休眠2毫秒, 确保拷贝操作已经执行了, 才执行修改操作.
modify();
// System.arraycopy 是线程不安全的, 那么 arrayOriginal 不等于 arrayDist.
if (!Arrays.equals(arrayOriginal, arrayDist)) {
throw new RuntimeException("System.arraycopy is not thread safe");
}
}
}
}
总结:
1.System.arraycopy() 在拷贝数组的时候,采用的使用浅复制,复制结果是一维的引用变量传递给副本的一维数组,修改副本时,会影响原来的数组。
2.System.arraycopy 是线程不安全的。
3.当测试数组的范围比较小的时候,两者相差的时间无几,当测试数组的长度达到百万级别, System.arraycopy的速度优势就开始体现了,根据对底层的理解,System.arraycopy是对内存直接进行复制,减少了for循环过程中的寻址时间,从而提高了效能。
以上