我们在最初学习时,在Java中,String被列为复合性数据类型,但现在来看,其实他真正的是一个不可变类,而与之属于一类的还有Integer,Long,Double,Float,Boolean等对象。所有这些类都有一个共同的特性,就是不可变性。一旦这个类被初始化,他的状态不再变化。这些类都被设计成final类型的,这些类只有一个构造方法。一旦被构造出来,就再也无法修改其内部状态。
这些作为原始数据类型的封装类,可以直接参与一些集合类的运算,比如,可以直接在一个String的list里面,将某个string给remove掉,可以将两个list做差集等等,总之,这些基本的不可构建类,对于一些集合操作jdk都已经帮我们封装好。但是我们自己定义的类,却不能拿起做差集和随便remove。
比如下面的代码,可以正确运行:
public class App {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("abcdefg");
list.add("higklmn");
System.out.println("before remove:"+list.size());
list.remove("higklmn");
System.out.println("after remove:"+list.size());
}
}
我举上面的例子是说明,这些不可变类,通常已经重写了默认的equals方法及hashcode方法,例子可能不太恰当,但这估计是JDK中唯一一类重写了equals和hashcode的类。但是下面的代码,却不能正常运行:
public class App {
public static void main(String[] args) {
List<AObject> list = new ArrayList<AObject>();
list.add(new AObject(1,2));
list.add(new AObject(3,4));
System.out.println("before remove:"+list.size());
list.remove(new AObject(3,4));
System.out.println("after remove:"+list.size());
}
static class AObject{
private int a;
private int b;
public AObject(int a,int b){
this.a = a;
this.b = b;
}
}
}
这就是我们在写Java的时候,不能使用两个Object的==或者equals来判断对象是否相同的原因。
同时,通过以上的用法,可以看出,String和int等基本数据类型相差比较的,相反,String是一个类,是一种特殊的类。
线程安全
不可变类除了具有以上特性外,最重要的莫过于线程安全了。Java并发编程实战中告诉我们,除非使用同步,否则尽可能的使用不可变对象。因为不可变对象即使不使用同步技术也不会出现线程安全的问题。根据我个人的理解,使用不可变对象时,Java总能保证对象在初始化完毕后,才将对象发布出去,这样就不会出现失效数据,不会出现对象处于一种不安全的发布装。
如果我们也要用到jdk的集合类,我们可能也要写一些不可变类,但有一个原则,就是对象一旦创建,不能在修改其状态或者方法的值。如何创建不可变类,这里有一遍文章比较好,大家可以参考:Java的可变类与不可变类