写这篇文章的起因
java中Final关键字
如何构建不可变对象
Guava中不可变对象和Collections工具类的unmodifiableSet/List/Map/etc的区别
实验代码
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对象之间的关系。不可变对象就是对象一旦创建就是不能被改变的。下面列一下创建不可变类的步骤
声明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(); }
1、定义Final,防止子类改变父类的行为。
2、执行修改操作的时候,直接抛异常。