如何在Java中创建不可变类?

时间:2020-02-23 14:34:17  来源:igfitidea点击:

今天,我们将学习Java中的不可变类。
什么是不可变类?深复制对于不变性的好处和重要性。

什么是Java中的不可变类?

不可变对象是实例,其状态在初始化后不会改变。
例如,String是一个不可变的类,一旦实例化,其值就永远不会改变。

阅读:为什么Java中的String不可变

Java中不可变类的好处

不变类非常适合用于缓存,因为您不必担心值的变化。

不可变类的另一个好处是它本质上是线程安全的,因此,在多线程环境中,您不必担心线程安全。

阅读:Java线程教程和Java多线程面试问题。

其中我通过示例提供了一种创建不可变类的方法,以更好地理解。

如何用Java创建一个不可变的类?

要使用Java创建不可变的类,您必须执行以下步骤。

  • 将程序声明为期末程序,因此无法扩展。

  • 将所有字段设为私有,以便不允许直接访问。

  • 不要提供变量的设置方法。

  • 将所有可变字段定为最终值,使其值只能分配一次。

  • 通过执行深度复制的构造函数初始化所有字段。

  • 在getter方法中执行对象的克隆以返回副本,而不是返回实际的对象引用。

要了解第4点和第5点,我们运行示例Final类,该类运行良好,并且实例化后的值不会改变。

FinalClassExample.java

package com.theitroad.java;

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

public final class 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;
	}

	/**
	 * Accessor function for mutable objects
	 */
	public HashMap<String, String> getTestMap() {
		//return testMap;
		return (HashMap<String, String>) testMap.clone();
	}

	/**
	 * Constructor performing Deep Copy
	 * @param i
	 * @param n
	 * @param hm
	 */
	
	public FinalClassExample(int i, String n, HashMap<String,String> hm){
		System.out.println("Performing Deep Copy for Object initialization");
		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;
	}
	
	
	/**
	 * Constructor performing Shallow Copy
	 * @param i
	 * @param n
	 * @param hm
	 */
	/**
	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;
	}
	*/
	
	/**
	 * To test the consequences of Shallow Copy and how to avoid it with Deep Copy for creating immutable classes
	 * @param args
	 */
	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);
		
		//Lets see whether its copy by field or reference
		System.out.println(s==ce.getName());
		System.out.println(h1 == ce.getTestMap());
		//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 accessor methods:"+ce.getTestMap());

	}

}

上面的示例程序的输出是:

Performing Deep Copy for Object initialization
true
false
ce id:10
ce name:original
ce testMap:{2=second, 1=first}
ce id after local variable change:10
ce name after local variable change:original
ce testMap after local variable change:{2=second, 1=first}
ce testMap after changing variable from accessor methods:{2=second, 1=first}

为什么Deep Copy对于不变性很重要?

让我们注释提供深拷贝的构造函数,并取消注释提供浅拷贝的构造函数。

同样,在getTestMap()方法中取消注释return语句,该方法返回实际的对象引用。

完成所有更改后,运行程序。
它将产生以下输出。

Performing Shallow Copy for Object initialization
true
true
ce id:10
ce name:original
ce testMap:{2=second, 1=first}
ce id after local variable change:10
ce name after local variable change:original
ce testMap after local variable change:{3=third, 2=second, 1=first}
ce testMap after changing variable from accessor methods:{3=third, 2=second, 1=first, 4=new}

从输出中可以看到,由于构造函数中的浅表复制,HashMap的值已更改。

之所以发生这种情况,是因为直接在getter函数中引用了原始对象。