本文中主要以代码及部分源码为示例介绍:final、Collections.unmodifiableXXX、ImmutableXXX 三种不可变对象的使用,主要是演示使用,main中的代码并不是完整符合逻辑,主要为了呈现出错的效果方便理解。
环境:jdk1.8 boot
jar:
<!--日志--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.10</version> </dependency> <!--guava--> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>23.0</version> </dependency>
一、不可变对象-final
定义:
●修饰类——该不能被继承(参考String的实现)
注意:使用final修饰类的时候,其所有方法都会被隐式的声明为final(慎重选择将类声明为final类)
●修饰方法——锁定该方法不被继承类修改
注意:一个类的private方法会被隐式的声明为final方法
●修饰变量——基本数据类型的变量值不能被改变,引用型变量其指向的地址不能被改变
代码示例(可以自行运行测试):
import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; import java.util.Map; /** * 不可变对象-final * @author stx * @date 2018/5/16 14:14 * @desc */ @Slf4j public class ImmutableExample { private final static int num = 0; private final static String a = "aa"; private final static Map<Integer, String> map = new HashMap<Integer, String>(){{put(1,"a");put(2,"b");put(3,"b");}}; public static void main(String[] args) { //num = 0; //编辑器直接提示错误 //a = "b";//编辑器直接提示错误 // map = new HashMap<Integer, String>();//编辑器直接提示错误 map.put(2, "bb"); log.info(map.get(2)); } }
这里只是针对final修饰变量做了示例。
分析下:由示例我们可知被final修饰基本数据类型的变量值不能被改变,引用型变量其指向的地址不能被改变。 值得注意的是final修饰的map在进行初始化后,地址虽然没有发生改变,但是map里面的对象对应的值缺发生了改变;由此我们可以发现被final修饰的引用型变量示例中的做法是不安全的,那我们改怎么办呢?下面我推荐两种解决方案,请接着往下看示例。
二、不可变对象-Collections.unmodifiableXX map、list、set
代码示例(可以自行运行测试):
import com.google.common.collect.Lists; import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; import java.util.Collections; import java.util.List; import java.util.Map; /** * 不可变对象-Collections.unmodifiableXX map、list、set * @author stx * @date 2018/5/16 14:14 * @desc */ @Slf4j public class ImmutableExample1 { private static Map<Integer, String> map = Maps.newHashMap(); private static List<Integer> list = Lists.newArrayList(); static { map.put(1,"a"); map.put(2,"b"); map.put(3,"c"); map = Collections.unmodifiableMap(map); list.add(1); list.add(2); list.add(3); list = Collections.unmodifiableList(list); } public static void main(String[] args) { log.info(String.valueOf(list.get(2))); map.put(2, "bb"); log.info(map.get(2)); } }
先贴下运行结果(list 执行成功,map 执行抛异常:UnsupportedOperationException)
16:19:51.162 [main] INFO com.yan.spring.springboot.example.ImmutableExample1 - 3 Exception in thread "main" java.lang.UnsupportedOperationException at java.util.Collections$UnmodifiableMap.put(Collections.java:1457) at com.yan.spring.springboot.example.ImmutableExample1.main(ImmutableExample1.java:38)
针对map的异常我们一起分析下,map对象执行这行代码之后
map = Collections.unmodifiableMap(map); 我们跟进unmodifiableMap的源码,发现这里new了一个新的UnmodifiableMap对象
public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m) { return new UnmodifiableMap<>(m); }
而创建对象的类中除了读取方法基本都替换成如下实现:
throw new UnsupportedOperationException();
所以运行结果时map抛出了不支持操作的异常;那么我们平时使用不可变容器的时候就可以使用以上方式了。
这里没有提供set的示例,set使用:Collections.unmodifiableSet
三、不可变对象-ImmutableXX set、list、set
代码示例(可以自行运行测试):
import com.google.common.collect.*; import lombok.extern.slf4j.Slf4j; import java.util.Collections; import java.util.List; import java.util.Map; /** * 不可变对象-ImmutableXX set、list、set * * @author stx * @date 2018/5/16 14:14 * @desc */ @Slf4j public class ImmutableExample3 { //map 初始化 private static ImmutableMap<Integer, String> map = ImmutableMap.<Integer, String>builder() .put(1,"a") .put(2,"b") .put(3,"c") .build(); //map2 初始化 参数顺序:K,V,K,V private static ImmutableMap<Integer, String> map2 = ImmutableMap.of(1,"a",2,"b"); //list 初始化 private static ImmutableList<Integer> list = ImmutableList.of(1,2,3); //list 初始化 of 方式省略 private static ImmutableSet set = ImmutableSet.copyOf(list); public static void main(String[] args) { map.put(2, "bb");//方法过时 log.info(map.get(2)); list.add(3);//方法过时 log.info(String.valueOf(list.get(2))); } }
相信有编程基础的同学看到上面的代码基本上已经会用了,这里运行结果也是抛出异常:UnsupportedOperationException 我就不做过多解释了。
map.put(2, "bb");//方法过时 list.add(3);//方法过时
这两行代码的调用对应的put、add 方法实现也是:
throw new UnsupportedOperationException();
总结:不可变对象的使用比较简单,以上只是演示最常用的几个案例,不可变对象上面演示的源码包中还有很多类,有兴趣的可以自己去学习。