如何在 Java 中创建不可变类
介绍
本文概述了如何在 Java 编程中创建不可变类。
如果对象在初始化后其状态不会改变,则该对象是不可变的String
。例如,是一个不可变的类,一旦实例化,String
对象的值就永远不会改变。详细了解Java 中类是不可变的原因String
。
由于不可变对象无法更新,因此程序需要为每次状态更改创建一个新对象。但是,不可变对象还具有以下优点:
- 不可变类适合于缓存目的,因为您不必担心值的变化。
- 不可变类本质上是线程安全的,因此您不必担心多线程环境中的线程安全。
了解有关Java 中的多线程的更多信息并浏览Java 多线程面试问题。
在 Java 中创建不可变类
要在 Java 中创建不可变类,需要遵循以下一般原则:
- 将该类声明为,
final
因此它不能被扩展。 - 使所有字段都
private
不允许直接访问。 - 不要为变量提供 setter 方法。
- 使所有字段可变
final
,以便字段的值只能分配一次。 - 使用执行深度复制的构造函数方法初始化所有字段。
- 在 getter 方法中执行对象克隆以返回副本而不是返回实际的对象引用。
以下类是说明不变性基础知识的示例。该类FinalClassExample
定义字段并提供使用深层复制来初始化对象的构造函数方法。文件main
方法中的代码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: FinalClassExample.java uses unchecked or unsafe operations
因为 getter 方法使用了从HashMap<String,String>
到 的未经检查的强制转换Object
。出于本示例的目的,您可以忽略编译器警告。
您将获得以下输出:
OutputPerforming 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
以显示使用浅拷贝而不是深拷贝并返回不带副本的对象时会发生什么。对象不再是不可变的,可以更改。对示例文件进行以下更改(或从代码示例中复制并粘贴):
- 删除提供深复制的构造函数方法,并添加提供浅复制的构造函数方法,如下例中突出显示的一样。
- 在 getter 函数中,删除
return (HashMap<String, String>) testMap.clone();
和添加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
您将获得以下输出:
OutputPerforming 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 函数中对原始对象的直接引用。
结论
您已经了解了在 Java 中创建不可变类时应遵循的一些一般原则,包括深度复制的重要性。继续学习更多Java 教程。