本文目录
1. 创建对象的5种方式:
(1)通过new关键字
Object obj = new Object()
(2)通过Class类的newInstance方法
这种方式默认时调用类的无参构造函数,如Person p = Class.forName(“com.oymn.test.Person”).newInstance()
(3)通过Constructor类的newInstance方法
这种方式和第二种类似,都是通过反射来实现。通过java.lang.relect.Constructor类的newInstance方法来指定某个构造器来创建对象。如Person p = Person.class.getConstructors()[0].newInstance()
实际上第二种方式利用Class类的newInstance方法创建对象, 其内部调用还是Contstructor的newInstance方法。
(4)利用clone方法
clone是Object类中的一个方法,通过对象.clone方法可以克隆一个内容和对象A一模一样的对象B,该方法创建的对象是浅拷贝,具体关于浅拷贝和深拷贝详情见以下。
Person p2 = p1.clone()
(5)反序列化
序列化:将堆内存中的Java对象数据,通过某种方式把对象存储到磁盘中或者传递到其他网络结点(在网络上传输)。
反序列化:将磁盘中的数据或者网络节点上的数据,恢复成Java对象。
2. clone方法
clone方法是Object类的一个方法,实现浅拷贝。
使用clone需要实现Cloneable接口。
protected native Object clone() throws CloneNotSupportedException;
先看一个例子:
public class Address {
private String province;
private String city;
public Address(String province, String city) {
this.province = province;
this.city = city;
}
}
//实现Cloneable接口
public class Person implements Cloneable {
private String name;
private Integer age;
//引用数据类型
private Address address;
public Person(String name, Integer age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public static void main(String[] args) {
Person p1 = new Person("hh",10,new Address("广东","广州"));
try {
Person p2 = (Person) p1.clone();
System.out.println(p1 == p2); //false
System.out.println(p1.getAddress() == p2.getAddress()); //true
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
可以发现,使用clone方法克隆一个新的Person对象,两个Person对象的引用是不同的,说明确实是创建了新的Person对象,但是里面的Address成员变量却是指向了同一个内存地址,这便是浅拷贝。
3. 浅拷贝
浅拷贝:克隆一个新的对象,如果成员变量是值类型的,则复制一份到新的对象, 如果成员变量是引用类型,则也仅仅是复制引用值而不是复制引用的对象。
上面的clone方法只能实现浅拷贝。
4. 深拷贝
学习了浅拷贝,深拷贝自然也知道是什么了。
深拷贝:克隆一个对象,无论其成员变量是值类型还是引用类型,都是会单独复制一份,当你修改其中一个对象的任何内容时,都不会影响另一个对象的内容。
既然Object自带的clone方法只能实现浅拷贝,那我们就只能自己来实现深拷贝了。
实现深拷贝的两种方式:
(1)让每个引用类型属性都重写clone方法
就上面的例子来说,我们让Address类也实现Cloneable接口,重写clone方法:
//实现Cloneable接口
public class Address implements Cloneable {
private String province;
private String city;
public Address(String province, String city) {
this.province = province;
this.city = city;
}
//重写clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
而在Person的clone方法中,手动帮address进行克隆。
@Override
protected Object clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
//手动克隆address
person.address = (Address) person.address.clone();
return person;
}
这样就能实现深拷贝。
但是这样有很严重的弊端,当Address类中也有引用数据类型的成员变量,也需要实现Cloneable接口,实现clone方法,每一个都需要手动修改,代码量会很大,很不方便。
于是推荐使用序列化方式
(2)利用序列化
序列化是将对象写到流中进行传输,而反序列化是把对象从流中读取出来。
这里写到流中的对象是原始对象的一个拷贝,因为原始对象还在JVM中,所以可以通过对象的序列化产生克隆对象,然后通过反序列化获取这个对象。
注意:每个需要序列化的类都要实现Serializable接口,如果有哪个属性不需要序列化,可以将其声明为transient,即将其排除在克隆之外。
在Person类中通过序列化方式实现深拷贝:
public Object deepClone(){
//输出
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
//输入
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
return ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}