如何在Java中创建一个不可变类
简介
本文概述了在Java编程中如何创建一个不可变类。
当一个对象在初始化后其状态不发生改变时,它是不可变的。举个例子,String是一个不可变类,一旦被实例化,String对象的值就不会改变。了解更多关于为什么Java中的String类是不可变的。
因为不可变对象无法更新,程序需要为每次状态改变创建一个新对象。然而,不可变对象还具有以下好处:
- An immutable class is good for caching purposes because you don’t have to worry about the value changes.
- An immutable class is inherently thread-safe, so you don’t have to worry about thread safety in multi-threaded environments.
了解Java中的多线程,并浏览Java多线程面试问题。
在Java中创建一个不可变的类
要在Java中创建一个不可变类,您需要遵循以下一般原则:
-
- 将类声明为final,以防止其被继承。
-
- 将所有的字段设置为私有,以防止直接访问。
-
- 不提供变量的setter方法。
-
- 将所有可变字段设置为final,以防止字段的值被多次赋值。
-
- 使用执行深拷贝的构造方法初始化所有字段。
- 在getter方法中进行对象的克隆,返回副本而不是实际的对象引用。
以下的类是一个例子,用来说明不可变性的基础知识。FinalClassExample类定义了字段,并提供了构造方法,该方法使用深拷贝来初始化对象。FinalClassExample.java文件的主方法中的代码测试了对象的不可变性。
创建一个名为FinalClassExample.java的新文件,并复制以下代码。
import java.util.HashMap;
import java.util.Iterator;
public final class FinalClassExample {
// fields of the FinalClassExample class
private final int id;
private final String name;
private final HashMap<String,String> testMap;
public int getId() {
return id;
}
public String getName() {
return name;
}
// Getter function for mutable objects
public HashMap<String, String> getTestMap() {
return (HashMap<String, String>) testMap.clone();
}
// Constructor method performing deep copy
public FinalClassExample(int i, String n, HashMap<String,String> hm){
System.out.println("Performing Deep Copy for Object initialization");
// "this" keyword refers to the current object
this.id=i;
this.name=n;
HashMap<String,String> tempMap=new HashMap<String,String>();
String key;
Iterator<String> it = hm.keySet().iterator();
while(it.hasNext()){
key=it.next();
tempMap.put(key, hm.get(key));
}
this.testMap=tempMap;
}
// Test the immutable class
public static void main(String[] args) {
HashMap<String, String> h1 = new HashMap<String,String>();
h1.put("1", "first");
h1.put("2", "second");
String s = "original";
int i=10;
FinalClassExample ce = new FinalClassExample(i,s,h1);
// print the ce values
System.out.println("ce id: "+ce.getId());
System.out.println("ce name: "+ce.getName());
System.out.println("ce testMap: "+ce.getTestMap());
// change the local variable values
i=20;
s="modified";
h1.put("3", "third");
// print the values again
System.out.println("ce id after local variable change: "+ce.getId());
System.out.println("ce name after local variable change: "+ce.getName());
System.out.println("ce testMap after local variable change: "+ce.getTestMap());
HashMap<String, String> hmTest = ce.getTestMap();
hmTest.put("4", "new");
System.out.println("ce testMap after changing variable from getter methods: "+ce.getTestMap());
}
}
编译和运行程序:
- javac FinalClassExample.java
- java FinalClassExample
Note
你得到了以下的输出:
Performing Deep Copy for Object initialization ce id: 10 ce name: original ce testMap: {1=first, 2=second} ce id after local variable change: 10 ce name after local variable change: original ce testMap after local variable change: {1=first, 2=second} ce testMap after changing variable from getter methods: {1=first, 2=second}
输出结果显示HashMap的值没有改变,这是因为构造函数使用了深拷贝,并且getter函数返回原始对象的克隆体。
当你不使用深拷贝和克隆时会发生什么?
您可以对FinalClassExample.java文件进行更改,以展示在使用浅拷贝而不是深拷贝并返回对象而不是副本时会发生的情况。对象不再是不可变的,并且可以被更改。对示例文件进行以下更改(或从代码示例中复制粘贴):
- Delete the constructor method providing deep copy and add the constructor method providing shallow copy that is highlighted in the following example.
- In the getter function, delete return (HashMap<String, String>) testMap.clone(); and add return testMap;.
现在示例文件应该长得像这样:
import java.util.HashMap;
import java.util.Iterator;
public final class FinalClassExample {
// fields of the FinalClassExample class
private final int id;
private final String name;
private final HashMap<String,String> testMap;
public int getId() {
return id;
}
public String getName() {
return name;
}
// Getter function for mutable objects
public HashMap<String, String> getTestMap() {
return testMap;
}
//Constructor method performing shallow copy
public FinalClassExample(int i, String n, HashMap<String,String> hm){
System.out.println("Performing Shallow Copy for Object initialization");
this.id=i;
this.name=n;
this.testMap=hm;
}
// Test the immutable class
public static void main(String[] args) {
HashMap<String, String> h1 = new HashMap<String,String>();
h1.put("1", "first");
h1.put("2", "second");
String s = "original";
int i=10;
FinalClassExample ce = new FinalClassExample(i,s,h1);
// print the ce values
System.out.println("ce id: "+ce.getId());
System.out.println("ce name: "+ce.getName());
System.out.println("ce testMap: "+ce.getTestMap());
// change the local variable values
i=20;
s="modified";
h1.put("3", "third");
// print the values again
System.out.println("ce id after local variable change: "+ce.getId());
System.out.println("ce name after local variable change: "+ce.getName());
System.out.println("ce testMap after local variable change: "+ce.getTestMap());
HashMap<String, String> hmTest = ce.getTestMap();
hmTest.put("4", "new");
System.out.println("ce testMap after changing variable from getter methods: "+ce.getTestMap());
}
}
编译并运行程序。
- javac FinalClassExample.java
- java FinalClassExample
你得到以下输出结果:
Performing Shallow Copy for Object initialization ce id: 10 ce name: original ce testMap: {1=first, 2=second} ce id after local variable change: 10 ce name after local variable change: original ce testMap after local variable change: {1=first, 2=second, 3=third} ce testMap after changing variable from getter methods: {1=first, 2=second, 3=third, 4=new}
输出显示HashMap的值已经被更改,因为构造方法使用了浅拷贝,在getter函数中直接引用了原始对象。
结论 (jié
在创建Java中的不可变类时,您已经学到了一些要遵循的一般原则,包括深拷贝的重要性。接下来,请继续学习更多的Java教程。