目录
1.数组的拷贝
在看深浅拷贝前,首先来看数组的拷贝:
整体拷贝
Arrays.copyOf(arr,arr.length);
import java.util.Arrays;
public class AllCopy {
public static void main(String[] args) {
int[] arr={1,3,5,7,9};
int[] ret= Arrays.copyOf(arr,arr.length);//整体拷贝
ret[0]=10;
arr[0]=4;
System.out.println(arrStr(arr));
System.out.println(arrStr(ret));
}
private static String arrStr(int[] arr) {
String ret="[";
for (int i = 0; i < arr.length; i++) {
ret+=arr[i];
if(i!= arr.length-1) {
ret += ",";
}
}
ret+="]";
return ret;
}
}
结果:[4,3,5,7,9]
[10,3,5,7,9]
分析:这里的拷贝把所有值都拷贝过来了,是属于整体拷贝。也属于深拷贝(相对来说的,它这里只有基本数据类型(即只有一层),原数组完全不影响新建数组)就是说ret[0]=10不影响原来的arr[0],arr[0]=4不影响新拷贝的数组中的ret[0]。拷贝后的数组对象确实是个新对象,开辟了新空间,只是将原数组的内容复制给新数组,这也就算属于深拷贝范畴。
部份拷贝
Arrays.copyOfRange(原数组名称,原数组起始位置,原数组结束位置)
起始位置和结束位置是左闭右开区间来取:[from,to)
import java.util.Arrays;
public class AllCopy {
public static void main(String[] args) {
int[] arr={1,3,5,7,9};
int[] ret= Arrays.copyOfRange(arr,2,4);//部份拷贝
System.out.println(arrStr(arr));
System.out.println(arrStr(ret));
}
private static String arrStr(int[] arr) {
String ret="[";
for (int i = 0; i < arr.length; i++) {
ret+=arr[i];
if(i!= arr.length-1) {
ret += ",";
}
}
ret+="]";
return ret;
}
}
模拟实现数组全拷贝与部份拷贝
public class AllCopy {
public static void main(String[] args) {
int[] arr={1,3,5,7,9};
int[] ret= arrCopy(arr);//全拷贝
int[] str=arrCopyRange(arr,2,4);//部份拷贝
ret[0]=10;
System.out.println(arrStr(arr));
System.out.println(arrStr(ret));
System.out.println(arrStr(str));
}
private static int[] arrCopy(int[] arr) {
//实现数组全拷贝
int[] ret=new int[arr.length];
for (int i = 0; i < arr.length; i++) {
ret[i]=arr[i];
}
return ret;
}
private static int[] arrCopyRange(int[] arr,int start,int end) {
//部份拷贝
int[] str=new int[end-start];
int j=0;
for (int i = 0; i < arr.length; i++) {
while(i==start&&start<end){
str[j]=arr[i];
j++;
start++;
}
}
return str;
}
private static String arrStr(int[] arr) {
String ret="[";
for (int i = 0; i < arr.length; i++) {
ret+=arr[i];
if(i!= arr.length-1) {
ret += ",";
}
}
ret+="]";
return ret;
}
}
在观察数组拷贝的基础上了解深浅拷贝的概念:
2.深浅拷贝的概念
浅拷贝
通过赋值的方式进行拷贝,只是把对象的表层赋给一个新的对象,拷贝后的对象和原对象仍指向同一块空间。此时原对象内部若有引用,则改变该引用的值,新对象中该引用的值也会随之变化。(外部开辟了新的空间内部并未开辟新空间,还是原来的)
浅拷贝对基本数据类型来说是值传递,对于引用类型来说,是进行引用传递般的拷贝,并未真实的创建一个新的对象。
深拷贝
原对象的修改不会影响拷贝后的对象(内外都开辟了新的空间)
对基本数据类型来说是值传递,但是对于引用类型来讲,原对象内部若有引用,则创建一个新的对象,并复制其内成员变量等内容
【ps:什么是引用?引用就是给对象起了个名字,保存的是对象的地址】
4.深浅拷贝的应用
浅拷贝
class A{
int num;
}
public class B implements Cloneable{
A a=new A();
@Override
protected B clone() throws CloneNotSupportedException {
return (B)super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
B b1=new B();
B b2=b1.clone();
System.out.println(b1==b2);
System.out.println(b2.a.num);
b1.a.num=100;
System.out.println(b2.a.num);
}
}
结果:false
0
100
分析:false说明b1,b2不是一个地址空间,b2实现了拷贝。而b1.a.num改变会影响b2.a.num的值,说明虽然b2是拷贝过来的,但是仍旧和b1指向同一块引用a,b1,b2中a的属性值是会同时改变的,实现了浅拷贝。
深拷贝-两种实现方式
1)递归实现的Cloneable接口
就上面浅拷贝例子而言,若要把它改成深拷贝,则在其的代码基础上,可以给A也实现Cloneable接口,即给A也实现克隆方法(都克隆-都有新空间)
class A implements Cloneable{
int num;
@Override
protected A clone() throws CloneNotSupportedException {
return (A)super.clone();
}
}
public class B implements Cloneable{
A a=new A();
@Override
protected B clone() throws CloneNotSupportedException {
B newB=(B)super.clone();
newB.a=a.clone();//让a也是克隆来的
return newB;
}
public static void main(String[] args) throws CloneNotSupportedException {
B b1=new B();
B b2=b1.clone();
System.out.println(b1==b2);
System.out.println(b2.a.num);
b1.a.num=100;
System.out.println(b2.a.num);
}
}
结果:false
0
0
分析:false说明b1b2是两个东西,开辟了新空间,b1中a.num改变影响不了b2中a.num,说明a也是被克隆了,开辟了新空间的,b1,b2内部包含的a确实不是一个对象,实现了深拷贝。
2)通过序列化来进行拷贝
1.序列化:将一个对象转为字符串就叫做序列化
2.将对象转为json字符串(序列化)进行拷贝,所有的序列化都是深拷贝