前面有篇自己总结的文章,是用final方法标注引用的,引用为final,表名这个引用只能指向这个对象,不能改变,但是对象本身可以变,java没有关键字来帮你实现不可变对象,必须自己写实现方法(thinking in java上面说“读者可以自己想”)。
不可变对象的几个基本原则:
1.class要声明为final
2.所有属性都声明为private访问权限
3.只提供get方法,不提供set方法
4.提供构造方法,并且在构造方法中初始化所有你想初始化的数据
5.类中有组合情况发生时,从get方法得到组合对象之前,先克隆这个对象。
6.组合对象传递给构造函数的时候,也要先克隆一份。
1.一般不可变类是这样的:
public final class Example
{
private String userName;
private String password;
public Example(String userName,String password)
{
this.userName = userName;
this.password = password;
}
public String getPassword()
{
return password;
}
public String getUserName()
{
return userName;
}
}
原理如下:
声明为final,以防它被子类继承修改,所以参数都是private,不能直接访问,而只能通过get访问,但是没有set方法,所以不能设定值,而值的设定是在构造器中设置的,这样就形成了不可变的类。它的属性都是String或者可以是一些基本类型,不需要克隆,而String本身就是final修饰的类,也是恒定的。这些都是很容易想到的。
现在扩展一下,类中需要组合另一个对象,也就是说,不光只有基本类型了,还有引用类型。下面是复杂一些的情况
这是另一个类,他要被组合到Example中
public class Address
{
private String homeAddr;
private String mail;
private int code;
public Address(String homeAddr, String mail, int code)
{
super();
this.homeAddr = homeAddr;
this.mail = mail;
this.code = code;
}
//以下是get/set,省略....
}
现在修改Example为Example1,加入了Address类的组合。
public class Example1
{
private String userName;
private String password;
private Address address;
public Example1(String userName,String password,Address address)
{
this.userName = userName;
this.password = password;
this.address = address;
}
public String getPassword()
{
return password;
}
public String getUserName()
{
return userName;
}
public Address getAddress()
{
return address;
}
}
现在这个类,貌似符合了不可变类的原则(1-4条),现在来测试一下。
public class TestExample1
{
public static void main(String[] args)
{
// 先填充Address对象
Address address = new Address("大拐弯幼儿园", "daguanwai.com", 1);
// 填充Example1
Example1 e1 = new Example1("王小黑", "123", address);
Address temp = e1.getAddress();
System.out.println("王小黑上的幼儿园名字是: " + temp.getHomeAddr());
// 修改address
address.setHomeAddr("大黄豆劳改所");
System.out.println("王小黑上的幼儿园名字是: " + temp.getHomeAddr());
}
}
打印结果:
王小黑上的幼儿园名字是: 大拐弯幼儿园
王小黑上的幼儿园名字是: 大黄豆劳改所
还是发生了改变,没达到不可变类的目的。
2.java总是按值传递的,引用的值就是地址,前面的blog已经说过(
http://doubleeo.iteye.com/admin/blogs/310082),所以现在共享的是同一个address ,对address的修改,都会体现在Example1 上面,为了达到不可变类的效果,要用克隆。
所以Example1修改成如下这样
public class Example1
{
private String userName;
private String password;
private Address address;
public Example1(String userName,String password,Address address)
{
this.userName = userName;
this.password = password;
this.address = (Address)address.clone();
}
public String getPassword()
{
return password;
}
public String getUserName()
{
return userName;
}
public Address getAddress()
{
return (Address)address.clone();
}
}
当然Address给重写clone方法
public class Address implements Cloneable
{
private String homeAddr;
private String mail;
private int code;
//构造方法及get/set省略...
@Override
protected Object clone()
{
try
{
return super.clone();
}
catch (Exception e)
{
throw new InternalError();
}
}
}
再测试一下Example1
打印结果:
王小黑上的幼儿园名字是: 大拐弯幼儿园
王小黑上的幼儿园名字是: 大拐弯幼儿园
这次正确了,把example1塑造为不可变的了。
以上的克隆叫
浅克隆(Shallow Cloning)
3.再近一步复杂,Example1中还是组合,但是这次组合的是容器,像List这种,用来装对象引用的容器,如果你还按刚才那种clone方法,肯定是达不到目的了,虽然List本身的引用克隆了一个新的,但是里面包裹着的对象的引用,还是那一份,所有改变它还是会破坏不可变类,因此要用到
深层克隆(Deep Cloning)
修改后的版本如下
public class Example1
{
private String userName;
private String password;
private List<Address> address;
public Example1(String userName,String password,List<Address> address)
{
this.userName = userName;
this.password = password;
this.address = cloneAddress(address);
}
public String getPassword()
{
return password;
}
public String getUserName()
{
return userName;
}
public List<Address> getAddress()
{
return cloneAddress(address);
}
//深层次clone方法
private List<Address> cloneAddress(List<Address> address){
int size = address.size();
List<Address> newList = new ArrayList<Address>(size);
for(Address addr:address)
newList.add((Address)addr.clone());
return newList;
}
}
也就是说,要把容器中的每个引用再一次克隆,如果引用是一个List,那么就要再把这个List中的所有引用再克隆。
最后再总结一下:
1.class要声明为final
2.所有属性都声明为private访问权限
3.只提供get方法,不提供set方法
4.提供构造方法,并且在构造方法中初始化所有你想初始化的数据
5.类中有组合情况发生时,从get方法得到组合对象之前,先克隆这个对象。
6.组合对象传递给构造函数的时候,也要先克隆一份。
7.如果浅克隆不能符合不可变对象的正常行为,就要实现深层克隆
分享到:
相关推荐
Java对象的深克隆与浅克隆详解.zip
java 深克隆 浅克隆
Java对象的复制与克隆,包含浅复制和深层复制。 免费下载啦,绝对值得一看。
深度克隆对象,不过效率没有显示的new对象快,不过很安全
学习java深克隆与浅克隆,希望对你有用
对象复制工具,基于cglib BeanCopier 实现对实体对象、持久化对象、代理对象的克隆和复制, 避免重复克隆和复制,避免无限循环引用,(校验hashCode) 可指定实体对象和集合属性的克隆深度
java 深克隆浅克隆···············
java的两种深度克隆方法,1cloneTest是用Cloneable接口的clone方法实现(对象必须要实现cloneable接口).2cloneSerialize.java是用对象流写对象到byte数组中,然后从byte数组中取得对象.(对象必须要实现serializble接口)
031111_【第11章:Java常用类库】_对象克隆技术笔记
强物理不可克隆函数的侧信道混合攻击_刘威强物理不可克隆函数的侧信道混合攻击_刘威强物理不可克隆函数的侧信道混合攻击_刘威强物理不可克隆函数的侧信道混合攻击_刘威
深度复制Java对象实例,复制后对象属性值改变不影响被复制对象,有注释
对象的克隆操作过程,展示了对象中数据字段是如何是实现克隆的
网络克隆是一种短时间内在大批量电脑上安装操作的一种先进的方法,目前实现的方法主要有两种,一种是在客户机上面使用存放引导文件,这种方法灵活性不太好,另一种方法是使用支持PXE的网卡来从服务器上面下载引导文件,...
克隆管理克隆管理克隆管理克隆管理克隆管理克隆管理克隆管理克隆管理克隆管理克隆管理克隆管理克隆管理克隆管理克隆管理克隆管理克隆管理克隆管理克隆管理克隆管理克隆管理克隆管理克隆管理克隆管理克隆管理克隆管理...
java设计模式【之】原型模式、深拷贝与浅拷贝【源码】【场景:克隆羊】 * 原型模式(Prototype) * 实现方式: * 需要被克隆的 class类, 重写Object中的clone()方法,并实现Cloneable接口(否则报错 ...
clone()方法示例(对象克隆)。作者: 初生不惑
在本篇文章中我们给大家分享了关于java对象中浅克隆和深克隆的相关知识点和相关代码内容,有兴趣的朋友们学习下。