如何在 Java 中創建不可變類

介紹

本文概述了如何在Java編程中創建不可變類。

當對象的狀態在初始化後不會更改時,該對象就是不可變的。例如,String是一個不可變類,一旦實例化,String對象的值就不會更改。了解更多關於為什麼String類在Java中是不可變的

由於不可變對象無法更新,程序需要為每個狀態變化創建一個新對象。但是,不可變對象也具有以下好處:

  • 不可變類非常適合用於緩存,因為您不必擔心值的更改。
  • 不可變類本質上是線程安全的,因此您不必擔心多線程環境中的線程安全性問題。

了解更多關於Java多線程的知識,並瀏覽Java多線程面試問題

在Java中創建一個不可變類

要在Java中創建一個不可變類,您需要遵循以下一般原則:

  1. 將類聲明為final,以防止它被擴展。
  2. 將所有字段設置為private,以防止直接訪問。
  3. 不要為變量提供setter方法。
  4. 將所有可變字段設置為final,這樣字段的值只能被分配一次。
  5. 使用執行深度複製的構造函數方法初始化所有字段。
  6. 在getter方法中執行對象的克隆,以返回一個副本而不是返回實際對象引用。

以下類是一個示例,說明了不可變性的基本知識。 FinalClassExample類定義了字段並提供了使用深度複製來初始化對象的構造函數方法。 FinalClassExample.java文件中main方法的代碼測試了對象的不可變性。

創建一個名為FinalClassExample.java的新文件,並複製以下代碼:

FinalClassExample.java
import java.util.HashMap;
import java.util.Iterator;

public final class FinalClassExample {

	// FinalClassExample类的字段
	private final int id;
	
	private final String name;
	
	private final HashMap<String,String> testMap;

	
	public int getId() {
		return id;
	}

	public String getName() {
		return name;
	}

	// 用于可变对象的获取函数

	public HashMap<String, String> getTestMap() {
		return (HashMap<String, String>) testMap.clone();
	}

	// 执行深度复制的构造方法
	
	public FinalClassExample(int i, String n, HashMap<String,String> hm){
		System.out.println("Performing Deep Copy for Object initialization");

		// "this"关键字指代当前对象
		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;
	}

	// 测试不可变类

	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);
		
		// 打印ce的值
		System.out.println("ce id: "+ce.getId());
		System.out.println("ce name: "+ce.getName());
		System.out.println("ce testMap: "+ce.getTestMap());
		// 更改局部变量的值
		i=20;
		s="modified";
		h1.put("3", "third");
		// 再次打印值
		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());

	}

}

编译并运行程序:

  1. javac FinalClassExample.java
  2. java FinalClassExample

注意:在编译文件时,可能会收到以下消息:注意:FinalClassExample.java使用未经检查或不安全的操作,因为getter方法使用了从HashMap<String,String>Object的未检查强制转换。出于本示例的目的,可以忽略编译器警告。

您将获得以下输出:

Output
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的值未更改,因为构造函数使用深度复制,获取函数返回原始对象的克隆。

當您不使用深度複製和克隆時會發生什麼

您可以更改FinalClassExample.java文件以顯示使用淺複製而不是深複製並返回對象而不是副本時會發生什麼。對示例文件進行以下更改(或從代碼示例中復制粘貼):

  • 刪除提供深度複製的構造函數並添加提供淺複製的構造函數,如下例所示。
  • 在getter函數中,刪除return (HashMap<String, String>) testMap.clone();並添加return testMap;

現在示例文件應如下所示:

FinalClassExample.java
import java.util.HashMap;
import java.util.Iterator;

public final class FinalClassExample {

	// FinalClassExample類的字段
	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函數

	public HashMap<String, String> getTestMap() {
		return testMap;
	}

	//執行淺複製的構造函數

	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;
	}

	// 測試不可變類

	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);
		
		// 打印ce值
		System.out.println("ce id: "+ce.getId());
		System.out.println("ce name: "+ce.getName());
		System.out.println("ce testMap: "+ce.getTestMap());
		// 更改局部變量值
		i=20;
		s="modified";
		h1.put("3", "third");
		// 再次打印值
		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());

	}

}

編譯並運行程序:

  1. javac FinalClassExample.java
  2. java FinalClassExample

你得到了以下輸出:

Output
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函數中對原始對象進行直接引用。

結論

當你在Java中創建不可變類時,已經學會了一些一般原則,包括深拷貝的重要性。繼續你的學習,了解更多Java教程

Source:
https://www.digitalocean.com/community/tutorials/how-to-create-immutable-class-in-java