我们这里先定义两个类:Student和Major,分别表示学生和所学的专用,二者是包含关系:
class Major{
var majorName:String?=null
var majorId:Long=0
}
class Student{
var name:String?=null
var age:Int=0
var major:Major?=null
}
对象赋值
赋值时日常编程过程中最常见的操作,最简单的比如:
var student=Student()
var student1=student
严格来说,这种不算对象拷贝,因为拷贝的仅仅只是引用关系,并没有生成新的实际对象。
浅拷贝
浅拷贝属于对象克隆方式的一种,重要的特性体现在浅字上。主要实现逻辑是 基本类型的字段会复制一份,而引用类型的字段拷贝的仅仅是引用地址,而且该引用地址指向的实际对象空间其实只有一份。
深拷贝
深拷贝相较于浅拷贝,除了值类型字段会复制一份,引用类型字段所指向的对象,会在内存中也创建一个副本。
我们来看看具体的代码实现:
浅拷贝代码实现
open class Student :Cloneable{
var name:String?=null
var age:Int=0
var major:Major?=null
public override fun clone(): Student {
return super.clone() as Student
}
override fun toString(): String {
return "Student(name=$name, age=$age, major=$major)"
}
}
测试代码:
companion object{
@JvmStatic
fun main(args: Array<String>) {
var major=Major()
major.majorId=6666
major.majorName="计算机科学与技术"
var student=Student()
student.age=20
student.name="毛大哥"
student.major=major
var cloneStudent=student.clone()
println(cloneStudent==student)
println("cloneStudent:${cloneStudent}")
println("student:${student}")
student.age=22
major.majorName="计算机应用技术"
major.majorId=3333
println("------------after-----------")
println("cloneStudent:${cloneStudent}")
println("student:${student}")
}
}
运行得到如下结果:
false
cloneStudent:Student(name=毛大哥, age=20, major=com.ble.kotlinscope.clone.Major@5e481248)
student:Student(name=毛大哥, age=20, major=com.ble.kotlinscope.clone.Major@5e481248)
------------after-----------
cloneStudent:Student(name=毛大哥, age=20, major=com.ble.kotlinscope.clone.Major@5e481248)
student:Student(name=毛大哥, age=22, major=com.ble.kotlinscope.clone.Major@5e481248)
从结果可以看出:
- cloneStudent==student 返回false,说明clone方法的确克隆出了一个新对象;
- 修改值类型字段并不影响克隆出来的新对象。
- 修改了student内部的引用对象,克隆对象也改变了,说明内部还是关联在一起,都是同一个对象。
深拷贝代码实现
虽然clone方法可以完成对象的拷贝工作,但是注意:clone()方法默认是浅拷贝行为,若想实现深拷贝需要覆写clone方法实现对象的深度遍历式拷贝。
对于上面的例子,如果想实现深拷贝,首先需要对更深一层的引用类Major做改造,让其也实现Cloneable接口并重写clone方法:
class Major :Cloneable {
var majorName:String?=null
var majorId:Int=0
public open override fun clone(): Major {
return super.clone() as Major
}
override fun toString(): String {
return "Major(majorName=$majorName, majorId=$majorId)"
}
}
其次我们还需要在顶层的调用类中重写clone方法,来调用引用类型字段的clone方法来实现深度拷贝。
open class Student :Cloneable{
var name:String?=null
var age:Int=0
var major:Major?=null
public override fun clone(): Student {
var student=super.clone() as Student
student.major=major?.clone()
return student
}
override fun toString(): String {
return "Student(name=$name, age=$age, major=$major)"
}
}
上面测试用例不变,运行结果如下:
false
student:Student(name=毛大哥, age=20, major=Major(majorName=计算机科学与技术, majorId=6666))
cloneStudent:Student(name=毛大哥, age=20, major=Major(majorName=计算机科学与技术, majorId=6666))
------------after-----------
student:Student(name=毛大哥, age=22, major=Major(majorName=计算机应用技术, majorId=3333))
cloneStudent:Student(name=毛大哥, age=20, major=Major(majorName=计算机科学与技术, majorId=6666))
很明显,这时候student和cloneStudent两个对象完全独立,不受互相的干扰.
网友评论