大纲
写这篇文章的起因
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
由于最近学习的东西较多,对有些基本的概念略有生疏,所以又温故了一下,顺便提供下上面问题的解决方案,仅供参考。
java中Final关键字
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中不可变对象和Collections工具类的unmodifiableSet/List/Map/etc的区别
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实现原理(只为说明不可变,未深入)
查看Guava和Collections的实现,发现add方法是如下设计的:
public final void add(int index, E element) {
throw new UnsupportedOperationException();
}
1、定义Final,防止子类改变父类的行为。
2、执行修改操作的时候,直接抛异常。
实验代码
来源:oschina
链接:https://my.oschina.net/u/587433/blog/510409