详解Java中的值传递和clone()作用以及深浅拷贝

写在前言:
我觉得整个知识体系是首先要明白Java里参数传递的规则(也就是为什么值传递)然后是借此引出“=”拷贝的问题,然后是clone()的作用,接着是深拷贝以及浅拷贝以及实现深拷贝的方法。最后对Java创建对像的四种方法进行总结。
整篇文章有我自己理解的地方,当然也有参考一些其他的技术博客。
一、Java里面只有值传递
1、形式参数以及实际参数
(1)形式参数是函数定义时候括号里的变量名(str是形参)

public String print(String str){
//函数体
}

(2)实际参数是调用函数时侯括号里的变量名(str1是实参)

print(str1);
//其中str1是已经被定义好了的字符串

2、经典理解误区:(可以这么理解,但是一定要知道这句话是错误的)
传递的参数如果是普通类型,那就是值传递,如果是对象,那就是引用传递。
3、正确的理解:java种只有值传递
(1)当参数类型是基本类型的时候,就是值传递。相当于把实参复制了一份然后给了形参,所以形参拿的是实参的复制品,所以这个时候对形参有任何的更改都不会对实参有任何影响

public static void main(String[] args) {
   ParamTest pt = new ParamTest();

   int i = 10;
   pt.pass(i );
   System.out.println("print in main , i is " + i);
}

public void pass(int j) {
   j = 20;
   System.out.println("print in pass , j is " + j);
}

结果

print in pass , j is 20
print in main , i is 10

(2)当参数类型是引用类型的话,其实也是值传递。这时候的值是对象的引用,所以就相当于实参(对象的引用)复制了一份(这里复制的是引用)传给了形参,所以形参拿到的是对象引用的复制品,这个时候形参也是引用,只不过这个时候形参和实参会指向同一个对象。所以这个时候通过形参来改变对象里的成员变量,那么对象实际的成员变量就会被改变(这是因为实参引用和形参引用是指向同一个对象)。那么有人就会说这不就是引用传递了吗?其实对于用形参的改变是否会影响到实参来判定是指传递还是引用传递这本身就是一个错误的参照。下面举一个例子就会明白了。

1)你有一把钥匙,当你的朋友想要去你家的时候,如果你直接把你的钥匙给他了,这就是引用传递。这种情况下,如果他对这把钥匙做了什么事情,比如他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字。
2)你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,自己的还在自己手里,这就是值传递。这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。
但是,不管上面哪种情况,你的朋友拿着你给他的钥匙,进到你的家里,把你家的电视砸了。那你说你会不会受到影响?而我们在pass方法中,改变user对象的name属性的值的时候,不就是在“砸电视”么。你改变的不是那把钥匙,而是钥匙打开的房子。
(java里的引用类型的参数传递就属于上面的第二种情况,所以即便参数是引用类型的,那么实参传给形参的也是指向对象引用的复制引用,所以你对这个引用做什么引用改变(这里说的改变是指让这个引用指向别的对象,那么不会改变实参的指向)。但是你不对这个形参做改变,而是对这个形参指向的对象做改变的话,那么实际的对象也会被改变,这是因为实参和形参这个时候是指向同一个对象的。)。
所以你改变了形参(这里的改变指的是改变了形参的指向,因为形参是引用,所以对引用做改变只能是改变它的指向)不会影响到实参的指向,但是你要是通过形参改变对象,那么实际的对象也会被改变。但是这种改变已然超乎了参数本身的改变,所以说这也是值传递。
两个例子一个是改变形参,另一个是通过形参改变对象。
第一个

public static void main(String[] args) {
   ParamTest pt = new ParamTest();

   User hollis = new User();
   hollis.setName("Hollis");
   hollis.setGender("Male");
   pt.pass(hollis);
   System.out.println("print in main , user is " + hollis);
}

public void pass(User user) {
   user = new User();
   user.setName("hollischuang");
   user.setGender("Male");
   System.out.println("print in pass , user is " + user);
}

结果

print in pass , user is User{name='hollischuang', gender='Male'}
print in main , user is User{name='Hollis', gender='Male'}

在这里插入图片描述
稍微解释下这张图,当我们在main中创建一个User对象的时候,在堆中开辟一块内存,其中保存了name和gender等数据。然后hollis持有该内存的地址0x123456(图1)。

当尝试调用pass方法,并且hollis作为实际参数传递给形式参数user的时候,会把这个地址0x123456交给user,这时,user也指向了这个地址(图2)。

然后在pass方法内对参数进行修改的时候,即user = new User();,会重新开辟一块0X456789的内存,赋值给user。后面对user的任何修改都不会改变内存0X123456的内容(图3)。

上面这种传递是什么传递?肯定不是引用传递,如果是引用传递的话,在执行user = new User();的时候,实际参数的引用也应该改为指向0X456789,但是实际上并没有。

通过概念我们也能知道,这里是把实际参数的引用的地址复制了一份,传递给了形式参数。所以,上面的参数其实是值传递,把实参对象引用的地址当做值传递给了形式参数。(这才是真的改变了形参,但是我们发现实参指向的对象并没有被改变)

第二个(大家常遇到的,也就是经常会让大家误以为参数是引用类型就是引用传递的情况,也就是上面说的砸电视的情况)

public static void main(String[] args) {
   ParamTest pt = new ParamTest();

   User hollis = new User();
   hollis.setName("Hollis");
   hollis.setGender("Male");
   pt.pass(hollis);
   System.out.println("print in main , user is " + hollis);
}

public void pass(User user) {
   user.setName("hollischuang");
   System.out.println("print in pass , user is " + user);
}

结果是:

print in pass , user is User{name='hollischuang', gender='Male'}
print in main , user is User{name='hollischuang', gender='Male'}

在这里插入图片描述
同样的,在参数传递的过程中,实际参数的地址0X1213456被拷贝给了形参,只是,在这个方法中,并没有对形参本身进行修改,而是修改的形参持有的地址中存储的内容。

所以,值传递和引用传递的区别并不是传递的内容。而是实参到底有没有被复制一份给形参。
(3)现在我们明白了java中无论传递参数是基本类型还是引用类型,都是值传递。其实在引用类型里面还有一种特殊的情况那就是字符串。看下面的例子。

public static void main(String[] args) {
   ParamTest pt = new ParamTest();

   String name = "Hollis";
   pt.pass(name);
   System.out.println("print in main , name is " + name);
}

public void pass(String name) {
   name = "hollischuang";
   System.out.println("print in pass , name is " + name);
}

结果是

print in pass , name is hollischuang
print in main , name is Hollis

这里我们会发现这里实参对象没有变。那么按照上面的解释不应该是指向“Hollis”的引用name复制了一份引用传给了形参name嘛,那么形参改变了字符串的值为“hollischuang”,这不就是上面说的砸电视问题嘛,为什么实参对象没有变呢?
其实这恰恰相反,这不是砸电视问题,而是属于真正的改变了形参,因为字符串String一经初始化,是不可以改指的,如果要改,其实是创建了一个新的对象而已。所以

name = "hollischuang";

等价于

String name=new String("hollischuang")

所以这相当于改变了形参(这里的改变值得是改变了形参的指向,这时候指向了新的对象"hollischuang"),所以现在实参形参指向不一样了,所以实参的对象不变。
图解:
在这里插入图片描述
所以说,Java中其实还是值传递的,只不过对于对象参数,值的内容是对象的引用。
对于上面的(1)(2)(3)要牢记,会分析和理解,尤其是(2)中的两种情况。
以上图片和代码来自于:添加链接描述

二、关于利用”=“来进行对象的拷贝
在了解了java参数传递全是值传递的原理之后。接下俩该引申出利用”=“进行拷贝的情况。看下面的代码例子

class Obj{
	private int aInt=0;
	public int getAInt() {
		return aInt;
	}
	public void setAInt(int int1)
	{
		aInt=int1;
	}
	public void changeInt() {
		this.aInt=1;
	}
}
public class TestRef{
	public static void main(String[] args) {
		Obj a=new Obj();
		System.out.println("a:"+a.getAInt());
		Obj b=a;
		b.changeInt();
		System.out.println("a:"+a.getAInt());
		System.out.println("b:"+b.getAInt());
	}
}

结果是:

a:0
a:1
b:1

1、Obj b=a;这句话就是类似于值传递中参数是引用类型的值传递,所以此时ab都指向同一个对象,就是a指向的对象,所以可以看出通过b对对象作出更改同时也会导致实际对象被更改(砸电视)。
2、所以”=“的作用就是让两个引用同时指向同一个对象。

问题:
那么有时候我就想通过”A=B“来创建一个新的对象(这个对象的引用是B),让B指向对象的内容和A指向的对象内容一致,这个时候就类似于上诉值传递(2)中的第一个改变形参的例子,所以这个时候通过B改变对象的内容,就不会影响到A指向对象的内容。
这个就要用到接下来说的clone()的作用

三、clone()的作用(实现对象拷贝)
1、引用拷贝,像”=“以及值传递的(2)中的第二个情况都是引用拷贝,就是让两个引用指向同一个对象,并没有创建新的对象

Teacher teacher = new Teacher("Taylor",26);
Teacher otherteacher = teacher;
System.out.println(teacher);
System.out.println(otherteacher);

结果:

blog.Teacher@355da254
blog.Teacher@355da254

在这里插入图片描述
结果分析:由输出结果可以看出,它们的地址值是相同的,那么它们肯定是同一个对象。teacher和otherteacher的只是引用而已,他们都指向了一个相同的对象Teacher(“Taylor”,26)。 这就叫做引用拷贝。
2、对象拷贝(创建对象的一个副本)
前文说到有时候就想创建一个新的对象,让这个对象和原来的对象内容一样,这就是对象拷贝。这时候就用到clone()。

Teacher teacher = new Teacher("Swift",26);
Teacher otherteacher = (Teacher)teacher.clone();  //关键写法,在这之前,应该让Teacher类继承Cloneable以及重写clone()方法。这个后面会讲到。
System.out.println(teacher);
System.out.println(otherteacher);

结果:

blog.Teacher@355da254
blog.Teacher@4dc63996

在这里插入图片描述
结果分析:由输出结果可以看出,它们的地址是不同的,也就是说创建了新的对象, 而不是把原对象的地址赋给了一个新的引用变量,这就叫做对象拷贝。
(上诉图片和部分代码来自于添加链接描述
现在我们知道了clone()的作用就是实现对象拷贝,那么clone()怎么用还不知道,那么接下来就来具体说一下clone()的用法。再用clone()之前需要介绍一下深浅拷贝。

四、浅拷贝与深拷贝的(我不喜欢会给出很抽象的定义,我希望大家可通过例子来明白)
1、(浅拷贝例子)

package cloneTest;
class Address{
	private String province;
	private String city;
	public void setProvince(String province)
	{
		this.province=province;
	}
	public void setCity(String city) {
		this.city = city;
	}
	public String getProvince() {
		return province;
	}
	public String getCity() {
		return city;
	}
	//同时改变省和市
	public void setAddress(String province,String city)
	{
		this.province=province;
		this.city=city;
	}
	
}

public class Test implements Cloneable {
	private String name;
	private int age;
	private String sex; 
	private Address address;
	//类成员变量也要在构造函数中初始化
	public Test(String name,int age,String sex)
	{
		this.name=name;
		this.age=age;
		this.sex=sex;
		this.address=new Address();
	}
	public void setName(String name)
	{
		this.name=name;
	}
	public void setAge(int age)
	{
		this.age=age;
	}
	public void setSex(String sex)
	{
		this.sex=sex;
	}
	public String getName()
	{
		return name;
	}
	public int getAge()
	{
		return age;
	}
	public String getSex()
	{
		return sex;
	}
	public void Print() {
		System.out.println(name+":"+age+","+sex+","+address.getProvince()+"——"+address.getCity());
	}
	//想在Test中同时改变Address的省市值,需要通过address对象调用Address中setAddress
	//下面两个setAddress不同,一个是Test中的,一个是Address中的
	//同理,分别改变省,市也是一个道理
	public void setAddress(String province,String city)
	{
		address.setAddress(province, city);
	}
	public void setProvince(String province)
	{
		address.setProvince(province);
	}
	public void setCity(String city) {
		address.setCity(city);

	}
	@Override
	protected Object clone() throws CloneNotSupportedException {
		
		return super.clone();
	}
public static void main(String[] args) throws CloneNotSupportedException {
	Test t=new Test("战",24,"Male");
	t.setAddress("辽宁", "大连");
	Test t1=(Test)t.clone();
	t.Print();
	t1.Print();
	t1.setName("施");
	t1.setAge(23);
	t1.setSex("Female");
	t1.setProvince("安徽");
	t1.setCity("黄山");
    t.Print();
    t1.Print();
	
}
}

结果:

:24,Male,辽宁——大连
战:24,Male,辽宁——大连
战:24,Male,安徽——黄山
施:23,Female,安徽——黄山

结果分析:
可以看出通过Test t1=(Test)t.clone();这句话实现了重新创建了一个对象,这个对象的引用是t1。此时t和t1分别指向两个对象(这两个对象的值是一样的,输出的一二行)。但是为什么通过t1更改对象的属性之后,t指向的对象普通类型的属性和字符串属性没有变,但是引用类型的值却变了(这里的引用类型就是指别的类的类型,在这里就是Address类型,因为Test中的address属性是Address类型的)都是新建一个对象了,为什么改变t1的值会影响到t的对象呢?而且还是改变一部分(引用类型那一部分),这好像违背了之前说的值传递(2)中的第一种改变参数这一说法。那这是怎么回事呢?(看下面两张图,第一张是我们以为的,而第二张才是真实情况)。
在这里插入图片描述
在这里插入图片描述
所以为什么会出现这种情况呢?这就是clone()的浅拷贝。所谓浅拷贝就是:
官方定义:创建一个新对象,然后将当前对象的非静态字段复制到该新对象,如果字段是值类型的,那么对该字段执行复制;如果该字段是引用类型的话,则复制引用但不复制引用的对象。因此,原始对象及其副本引用同一个对象。
我自己的理解就是:对于对象里除了别的类之外的其他成员都是拷贝了一份给新的对象,但是对于别的类型的对象(address),只是把引用拷贝过去了,所以才会出现上面图二的情况,所以对t1修改,t中其他的都不会变,但是t
中其他类型的对象的属性会变。

所以自然而然想到我就想创建另一个类,然后对他无论做什么都不会影响到原来的类也就是所谓地深拷贝就是:
官方定义:创建一个新对象,然后将当前对象的非静态字段复制到该新对象,无论该字段是值类型的还是引用类型,都复制独立的一份。当你修改其中一个对象的任何内容时,都不会影响另一个对象的内容。
我觉得这个官方定义通俗易懂

那么问题来了
怎么实现深拷贝呢?
两种方式
(1)给需要拷贝的引用类型也实现Cloneable接口并覆写clone方法(上面的代码我们只是在Test类实现了Cloneable和重写了clone,现在需要在Address中也实现Cloneable以及重写clone()方法)。
(2)利用序列化(这个在下一章节讲)。
2、(深拷贝例子)

package cloneTest;
class Address implements Cloneable{
	private String province;
	private String city;
	public void setProvince(String province)
	{
		this.province=province;
	}
	public void setCity(String city) {
		this.city = city;
	}
	public String getProvince() {
		return province;
	}
	public String getCity() {
		return city;
	}
	//同时改变省和市
	public void setAddress(String province,String city)
	{
		this.province=province;
		this.city=city;
	}
	@Override
	protected Object clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		return super.clone();
	}
	
}

public class Test implements Cloneable {
	private String name;
	private int age;
	private String sex; 
	private Address address;
	//类成员变量也要在构造函数中初始化
	public Test(String name,int age,String sex)
	{
		this.name=name;
		this.age=age;
		this.sex=sex;
		this.address=new Address();
	}
	public void setName(String name)
	{
		this.name=name;
	}
	public void setAge(int age)
	{
		this.age=age;
	}
	public void setSex(String sex)
	{
		this.sex=sex;
	}
	public String getName()
	{
		return name;
	}
	public int getAge()
	{
		return age;
	}
	public String getSex()
	{
		return sex;
	}
	public void Print() {
		System.out.println(name+":"+age+","+sex+","+address.getProvince()+"——"+address.getCity());
	}
	//想在Test中同时改变Address的省市值,需要通过address对象调用Address中setAddress
	//下面两个setAddress不同,一个是Test中的,一个是Address中的
	//同理,分别改变省,市也是一个道理
	public void setAddress(String province,String city)
	{
		address.setAddress(province, city);
	}
	public void setProvince(String province)
	{
		address.setProvince(province);
	}
	public void setCity(String city) {
		address.setCity(city);

	}
	@Override
	protected Object clone() throws CloneNotSupportedException {
		Test tt=(Test)super.clone();
		tt.address=(Address)address.clone();
		
		return tt;
	}
public static void main(String[] args) throws CloneNotSupportedException {
	Test t=new Test("战",24,"Male");
	t.setAddress("辽宁", "大连");
	Test t1=(Test)t.clone();
	t.Print();
	t1.Print();
	t1.setName("施");
	t1.setAge(23);
	t1.setSex("Female");
	t1.setProvince("安徽");
	t1.setCity("黄山");
    t.Print();
    t1.Print();
	
}
}

结果:

:24,Male,辽宁——大连
战:24,Male,辽宁——大连
战:24,Male,辽宁——大连
施:23,Female,安徽——黄山

代码分析;从这一结果可以看出来无论对t1怎么进行改变,都不会影响到t对象的值。真正实现了深拷贝,这里为了看得更清晰,所以把整个代码都写上了,其实相比于第一次浅拷贝代码,这里只改动了两处:下面进行说明:
(1)让Address类也实现了Cloneable接口,并且也重写clone函数

class Address implements Cloneable{
	private String province;
	private String city;
	public void setProvince(String province)
	{
		this.province=province;
	}
	public void setCity(String city) {
		this.city = city;
	}
	public String getProvince() {
		return province;
	}
	public String getCity() {
		return city;
	}
	//同时改变省和市
	public void setAddress(String province,String city)
	{
		this.province=province;
		this.city=city;
	}
	@Override
	protected Object clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		return super.clone();
	}
	
}

(2)对Test’中Clone进行了重写,除了调用父类中的clone方法得到新的对象, 还要将该类中的引用变量也clone出来

protected Object clone() throws CloneNotSupportedException {
		Test tt=(Test)super.clone();
		tt.address=(Address)address.clone();
		
		return tt;
	}

3、通过深浅拷贝的例子我们大概已经可以总结clone()的用法了。
(1)首先检查类中有无对象数据成员,若没有就是浅拷贝,就直接让这个类实现Cloneable接口,然后重写clone()方法就好,而且clone()方法中只需要有一句return super.clone()就好
(2)若类中有对象数据成员,就是深拷贝,那么这个类还有对象数据成员所在类都要实现Cloneable接口以及重写clone()函数,此时对象数据成员所在类中重写clone()也就是一句话:return super.clone()。但是类中的重写的clone(0函数除了调用父类中的clone方法得到新的对象, 还要将该类中的引用变量也clone出来就像上面的Test中clone()的重写:

protected Object clone() throws CloneNotSupportedException {
		Test tt=(Test)super.clone();
		tt.address=(Address)address.clone();
		return tt;
	}

格式就是:

		//类名 对象名=(类名)super.clone();
		//对象名.对象成员名=(对象数据成员所在类)对象成员名.clone()
		//return 对象名

(3)无论clone拷贝深浅,用于测试的主函数都要抛出CloneNotSupportedException异常。

public static void main(String[] args) throws CloneNotSupportedException {
//函数体
....
//都要以这种方式进行拷贝
Test t1=(Test)t.clone();
}

clone()的作用和深浅拷贝方法讲完了,刚才说还有另一种方法可以实现深拷贝,就是序列化,在讲序列化之前先讲个问题,就是java创建对象的几种方法。

五、Java创建对象的四种方法
1、通过new语句实例化一个对象
2、通过反射机制创建对象
3、通过clone()方法创建一个对象(注意深浅拷贝)
4、通过序列化反序列化的方式创建对象
这里主要说一下4,为什么通过序列化反序列化可以创建对象,而且可以实现深拷贝?
因为:
序列化是将对象写到流中便于传输,而反序列化则是把对象从流中读取出来。这里写到流中的对象则是原始对象的一个拷贝,因为原始对象还存在 JVM 中,所以我们可以利用对象的序列化产生克隆对象,然后通过反序列化获取这个对象。

六、利用序列化和反序列化实现深拷贝
话不多说直接看代码
1、核心代码:序列化与反序列化代码(要实现 Serializable接口)

package cloneTest;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class DeepClone implements Serializable {
	private static final long serialVersionUID=1l;
	
	  protected Object deepClone() throws Exception{
		  //序列化的过程
		  ByteArrayOutputStream bos=new ByteArrayOutputStream();
		  ObjectOutputStream oos=new ObjectOutputStream(bos);
		  oos.writeObject(this);
		  
		  //反序列化的过程
		  ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
		  ObjectInputStream ois=new ObjectInputStream(bis);
		  return ois.readObject();
	  }

}

2、这部分就是和上面差不多的Test类以及Address类。为了方便起见,先把整体代码写上,然后再说相比于利用clone()方法有什么改动(改动很小)

package cloneTest;
class Address extends DeepClone{
	private static final long serialVersionUID = 1L;
	private String province;
	private String city;
	public void setProvince(String province)
	{
		this.province=province;
	}
	public void setCity(String city) {
		this.city = city;
	}
	public String getProvince() {
		return province;
	}
	public String getCity() {
		return city;
	}
	//同时改变省和市
	public void setAddress(String province,String city)
	{
		this.province=province;
		this.city=city;
	}

	
}

public class Test extends DeepClone{
	private static final long serialVersionUID = 1L;
	private String name;
	private int age;
	private String sex; 
	private Address address;
	//类成员变量也要在构造函数中初始化
	public Test(String name,int age,String sex)
	{
		this.name=name;
		this.age=age;
		this.sex=sex;
		this.address=new Address();
	}
	public void setName(String name)
	{
		this.name=name;
	}
	public void setAge(int age)
	{
		this.age=age;
	}
	public void setSex(String sex)
	{
		this.sex=sex;
	}
	public String getName()
	{
		return name;
	}
	public int getAge()
	{
		return age;
	}
	public String getSex()
	{
		return sex;
	}
	public void Print() {
		System.out.println(name+":"+age+","+sex+","+address.getProvince()+"——"+address.getCity());
	}
	//想在Test中同时改变Address的省市值,需要通过address对象调用Address中setAddress
	//下面两个setAddress不同,一个是Test中的,一个是Address中的
	//同理,分别改变省,市也是一个道理
	public void setAddress(String province,String city)
	{
		address.setAddress(province, city);
	}
	public void setProvince(String province)
	{
		address.setProvince(province);
	}
	public void setCity(String city) {
		address.setCity(city);

	}
public static void main(String[] args) throws Exception {
	Test t=new Test("战",24,"Male");
	t.setAddress("辽宁", "大连");
	Test t1=(Test)t.deepClone();
	t.Print();
	t1.Print();
	t1.setName("施");
	t1.setAge(23);
	t1.setSex("Female");
	t1.setProvince("安徽");
	t1.setCity("黄山");
    t.Print();
    t1.Print();
	
}
}

结果:

:24,Male,辽宁——大连
战:24,Male,辽宁——大连
战:24,Male,辽宁——大连
施:23,Female,安徽——黄山

从结果可以看出,实现了深拷贝
改动之处
(1)取消了所有跟clone有关的。包括Test和Address这两个类不再需要实现Cloneable接口,也不用重写clone()函数。main抛出的异常也变了
(2)让Test和Address类都继承DeepClone类,而这个类实现了对象序列化(实现了Serializable接口),而一个类实现了序列话,它的子类也实现了序列化。
(3)主函数中由

Test t1=(Test)t.clone();

变成

Test t1=(Test)t.deepClone();

就是改变了一个函数,而这个函数deepClone就是DeepClone类中实现了序列化与反序列化的函数

这里需要注意的就是
(1)static以及transient(代表对象的临时数据),用这个两个关键字声明的数据成员是不可以被序列化的
所以想让那个数据成员不被序列化,就用transient声明
(2)序列化以及反序列化中每一各类中都有一个数据成员

private static final long serialVersionUID = 1L;

这是因为每个类都有一个特定的serialVersionUID,在反序列化的过程中,通过serialVersionUID来判定类的兼容性。如果待序列化的对象和目标对象的serialVersionUID不同,就会抛出InvalidClassException异常
增加这一句话的好处是
1)提高程序运行效率(要是不显示声明serialVersionUID,那么系统会自己计算一个serialVersionUID值)
2)提高兼容性

3、利用clone()实现深拷贝和利用序列化以及反序列化实现深拷贝的比较
clone()这种方法的缺点就是当一个类里面有很多引用类型时,需要手动调用很多clone,而且如果引用类型内部还有引用类型时,那么代码将会很恶心,量也很大。。。所以这种方式一般用于引用类型变量较少的时候。对于很多引用类型,可以使用序列化对象的方式进行深拷贝。

写在后面:
关于java输入输出流,序列化和反序列化具体细节我还没弄明白,等弄明白了再整理出来。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值