一、可变类型与不可变类型
不可变数据类型: 当该数据类型的对应变量的值发生了改变,那么它对应的内存地址也会发生改变,对于这种数据类型,就称不可变数据类型。
可变数据类型 :当该数据类型的对应变量的值发生了改变,那么它对应的内存地址不发生改变,对于这种数据类型,就称可变数据类型,当可变数据类型改变时它实际上是更改了内存中的内容。
可变类型
java中基本数据类型以及String类型均为不可变类型。
BigInteger,BigDecimal,List,Set,Map为可变类型。
举个简单的例子说明一下可变与不可变
String s = "a";
s = s + "b";
StringBuilder sb = new StringBuilder("a");
sb.append("b");
String类型的s会重新指向新的地址内容,而sb只是在原地址内容中做修改
不注意这一点的话,编代码很有可能出现错误
String s1 = new String("abc");
List<String> list = new ArrayList<String>();
list.add(s1);
s1 = s1.concat("d");
System.out.println(list.get(0));
String s2 = s1.concat("e");
list.set(0, s2);
System.out.println(list.get(0));
代码输出为:
abc
abcde
原因是String类型不可变,s1进行concat时s1十几指向了新的数据地址,而List中保存的仍然是老的地址内容。
二、不可变引用
在声明变量时用final进行修饰,此时变量不可改变其指向的地址,但可以改变其中存储的值。
final StringBuilder sb = new StringBuilder("abc");
sb.append("d");
sb = new StringBuilder("e");
System.out.println(sb);
以上代码在编译阶段会在第三行报错,但第二行不会报错。
三、不可变封装
此处的不可变与上述不可变并不是一回事,而是通过封装让数据只能看,不能修改。且这里的不可变是在运行阶段获得的,编译阶段无法发现。
利用以下三个函数进行封装
Collections.unmodifiableList Collections.unmodifiableSet Collections.unmodifiableMap
List<String> list = new ArrayList<>();
list.add("ab");
List<String> listCopy = Collections.unmodifiableList(list);
listCopy.add("c");
list.add("c");
System.out.println(listCopy.size());
以上代码运行到第四行会报错。