前面有篇自己总结的文章,是用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.如果浅克隆不能符合不可变对象的正常行为,就要实现深层克隆
分享到:
相关推荐
- **使用`clone()`或`copy()`方法**:如果集合中的元素是可克隆的,可以直接调用`clone()`方法;如果集合支持`copy()`方法(如`ArrayList`),也可以使用它进行复制。 - **使用工具类**:如Apache Commons ...
总结,Lukechampine-freeze 为 JavaScript 开发者提供了一种高效且全面的手段,用于创建和维护不可变对象。通过理解并合理运用这一工具,我们可以编写出更加安全、可维护的代码,提升整体项目的质量和稳定性。
这表明,尽管我们可以克隆对象,但是数组和对象内部的引用仍然会被共享,导致深层次的克隆问题。 为了处理这个问题,可以使用更复杂的克隆方法,如`JSON.parse(JSON.stringify(obj))`,或者使用库如lodash的`_....
在Java中,我们可以利用内置的`java.lang.Cloneable`接口,但需要注意的是,该接口本身不包含任何方法,只是声明一个对象是可克隆的。为了实现克隆,我们还需要重写`Object`类的`clone()`方法。 3. 请求克隆:在...
- 如果对象包含不可克隆的成员,如静态字段或不可变对象,那么可能需要调整拷贝策略。 总之,原型模式是软件设计中的一种有效工具,尤其适用于需要频繁复制对象的场景。Java提供的`Cloneable`接口和`clone()`方法...
然而,缺点包括需要为类添加克隆逻辑,可能会增加复杂性,特别是处理引用了不可串行化或循环引用的对象时;此外,每个可克隆的类都必须实现`Cloneable`接口。 原型模式的应用场景包括但不限于: 1. 资源密集型对象...
在Java等语言中,可以利用内置的克隆机制或实现Cloneable接口来实现对象的复制。这种方式避免了每次创建新对象时都执行完整的构造过程,特别是在复杂的对象层级结构中,原型模式能显著提高性能。 ### **主要角色** ...
在Java编程语言中,"继承接口克隆"是面向对象设计的重要概念,它们构成了Java类层次结构的关键组成部分。本文将详细探讨这两个主题,并通过实际示例来解释它们的应用。 首先,我们来理解“继承接口”。在Java中,...
4. **开放封闭原则**:这是另一个重要的面向对象原则,它提倡软件实体(如类、模块和函数)应该是可扩展的,但不可修改。这意味着添加新功能时,不应修改现有代码。 5. **依赖倒转原则**:它主张抽象不应该依赖细节...
- **String**:不可变字符串,适合少量修改场景。 - **StringBuffer**:线程安全的可变字符串,适用于多线程环境。 - **StringBuilder**:非线程安全的可变字符串,效率高于StringBuffer。 **11. ArrayList和...
如果引用的对象也是可克隆的,可以递归调用其clone()方法。例如: ```java public class DeepPrototype implements Cloneable { private String property; private OtherObject otherObject; // 省略getter和...
5. **Prototype模式**:原型模式通过克隆现有的对象实例来创建新对象,而不是通过创建新实例的方式。这种方式可以提高性能,尤其是在创建对象成本较高的情况下。在C++中,可以通过重载赋值运算符或者使用深拷贝来...
3. `final`用于声明常量或不可变对象,也可修饰方法和类,确保不可被重写或继承。 4. `Math.round(-1.5)`返回-1。 5. String不是基础数据类型,而是对象。 6. 操作字符串的类有`String`、`StringBuilder`和`...
5. **原型模式**: 原型模式通过克隆已有对象来创建新对象,减少类的实例化开销。C#中的`ICloneable`接口可以用来支持浅复制或深复制。 6. **装饰器模式**: 装饰器模式动态地给对象添加额外的职责,允许扩展对象的...
`final`关键字用于声明不可变的变量或方法,`finally`块用于确保代码段在异常发生或正常执行后总能执行,`finalize`方法是`Object`类的一个方法,用于垃圾回收前的资源清理,但不应被直接调用,而应由JVM自动调用。...
这是因为JavaScript对象的赋值是引用传递,而不是值传递,所以当对数据对象进行深度赋值时,Vue无法跟踪到这些深层次的变化。 在标题和描述中提到的问题是关于`vue data对象重新赋值无效(未更改)`的解决方法。这个...
- 创建不可变类,确保对象在其生命周期内状态不会改变。 - 使用最终变量(`final`)、只读集合等技术实现不可变性。 ##### Item14:偏好组合而非继承 - **目的**:避免类层次结构中的复杂性和问题。 - **实现方式...
原型模式通过克隆现有对象来创建新对象,分为浅克隆和深克隆。在客户类Customer的案例中,浅克隆只是复制对象引用,而深克隆则会复制对象及其引用的对象。比较两者,深克隆能完全复制整个对象树,而浅克隆只复制对象...