寫這篇文章的原由html
java中Final關鍵字java
如何構建不可變對象app
Guava中不可變對象和Collections工具類的unmodifiableSet/List/Map/etc的區別ide
實驗代碼
函數
java項目在使用FindBugs掃描的時候報了一個不能使用可變對象,記得報的是相似以下的信息:工具
MS: Field is a mutable collection (MS_MUTABLE_COLLECTION) 官方解釋: A mutable collection instance is assigned to a final static field, thus can be changed by malicious code or by accident from another package. Consider wrapping this field into Collections.unmodifiableSet/List/Map/etc. to avoid this vulnerability. 參考FindBugs的描述:http://findbugs.sourceforge.net/bugDescriptions.html
因爲最近學習的東西較多,對有些基本的概念略有生疏,因此又溫故了一下,順便提供下上面問題的解決方案,僅供參考。
性能
Final是java的一個關鍵字,能夠修飾:變量、方法、類,大體用法,我寫了一個測試類,註釋也比較多。學習
package org.unrulylianzi.basis.javass.finalkeyword; /** * java final 基本用法 * <li>variable</li> * <li>method</li> * <li>class</li> * */ public class JavaFinalKeyWord { /** * 基本類型 * */ private final int a=3; private final int b;//must be initialized at the time of declaration or inside constructor or block { //a=4;不能重複對final的變量賦值 System.out.println(a); b=4; } /** * 引用類型 * */ private final Person person = new Person("unrulylianzi");//must be initialized at the time of declaration or inside constructor { //person = new Person("unrulylianzi_change_final");//不能重複對final的變量賦值 System.out.println(person); person.setName("unrulylianzi_change_value_not_reference");//能夠修改引用類型的值,可是不能賦予新的Person對象 System.out.println(person); } /** * method * */ public final String finalMethod(){ return "unrulylianzi_final"; } /** * method not final * */ public String notFinalMethod(){ return "unrulylianzi_notfinal"; } } final class SubClass extends JavaFinalKeyWord { @Override public String notFinalMethod() { // TODO Auto-generated method stub return super.notFinalMethod(); } /** * final method can not be overridden in Java final的方法不能被繼承 @Override public String finalMethod() { // TODO Auto-generated method stub return super.finalMethod(); } */ } /** * *Final class can not be inheritable in Java. Final修飾的類不能被繼承 class ExtendsFinalClass extends SubClass { } */ 其中Person類是一個簡單的bean public class Person { private String name; //getter&setter }
固然Final對性能也有不少好處,此次不會講到。測試
這個問題貌似沒太在乎過,也沒深刻思考過Final關鍵字和Immutable對象之間的關係。不可變對象就是對象一旦建立就是不能被改變的。下面列一下建立不可變類的步驟this
聲明class爲Final,以避免class被子類繼承,關於多態的概念就不講啦。
把全部類的屬性聲明成private,而且不提供set方法。
把全部可變的變量聲明爲Final,使得這些變量只能初始化一次。
暴露給外部的構造函數,在賦值的時候,要使用深拷貝。
在變量對應的get方法的時候要提供引用類型變量的clone,而不是直接返回。
我建立的一個不可變對象的示例代碼以下:
package org.unrulylianzi.basis.javass.immutable; import java.util.HashMap; import java.util.Iterator; /** * 不變對象 * <li>1:Declare the class as final so it can’t be extended.</li> * <li>2:Make all fields private so that direct access is not allowed.</li> * <li>3:Don’t provide setter methods for variables</li> * <li>4:Make all mutable fields final so that it’s value can be assigned only once.</li> * <li>5:Initialize all the fields via a constructor performing deep copy.</li> * <li>6:Perform cloning of objects in the getter methods to return a copy rather than returning the actual object reference.</li> * */ public final class ImmutableClass { private final int age; private final String name; private final HashMap<String,String> testMap; /** * shallow copy * */ // public ImmutableClass(int age, String name, HashMap<String, String> testMap) { // super(); // this.age = age; // this.name = name; // this.testMap = testMap; // } /** * deep copy * */ public ImmutableClass(int age, String name, HashMap<String, String> testMap) { super(); this.age=age; this.name=name; HashMap<String,String> tempMap=new HashMap<String,String>(); String key; Iterator<String> it = testMap.keySet().iterator(); while(it.hasNext()){ key=it.next(); tempMap.put(key, testMap.get(key)); } this.testMap=tempMap; } /** * @return the age */ public int getAge() { return age; } /** * @return the name */ public String getName() { return name; } public HashMap<String, String> getTestMap() { //return testMap return (HashMap<String, String>) testMap.clone(); } }
可見Final關鍵字是建立不可變對象的基礎。
guava是google的一個庫,彌補了java語言的不少方面的不足,不少在java8中已有實現,暫時不展開。Collections是jdk提供的一個工具類。二者之間的區別:
當Collections建立的不可變集合的wrapper類改變的時候,不可變集合也會改變,而Guava的Immutable集合保證確實是不可變的。 |
測試代碼以下:
//guava的實現 public static final List<String> immutableList; static { immutableList = ImmutableList.of("a"); } /** * 改變immutableList * */ public static void immutableListGuavaChange() { try { System.out.println("Try adding more elements to ImmutableList"); /** * 改變immutableList已存在元素的值,添加新的元素,都會報錯,拋出異常 */ immutableList.set(0, "b"); // immutableList.add("b"); } catch (UnsupportedOperationException e) { System.out.println( "Throws UnsupportedOperationException, ImmutableList is immutable, won't allow adding elements"); } }
Collections工具類的實現:
/** * Collections工具類實現的不可變list的示例 */ public class ImmutableListCollections { public static final Collection unmodifiableList; public static final List list; static { list = Arrays.asList("Once", "upon", "time", "there", "used", "to", "be", "king"); unmodifiableList = Collections.unmodifiableCollection(list); } /** * 改變immutableList * */ public static void immutableListCollectionsChange() { System.out.println("Try adding more elements to ImmutableList"); /** * 改變unmodifiableList已存在元素的值,添加新的元素,都會報錯,拋出異常 */ list.set(0, "b"); System.out.println("###修改wrapper類###" + unmodifiableList); // immutableList.add("b"); } }
查看Guava和Collections的實現,發現add方法是以下設計的:
public final void add(int index, E element) { throw new UnsupportedOperationException(); }
一、定義Final,防止子類改變父類的行爲。
二、執行修改操做的時候,直接拋異常。