1 final修饰类
final修饰的子类不能有子类。
2不可变类
不可变类的意思是创建该类的实例后,该实例的实例变量是不可以改变的。
例如:java提供的8个包装类和java.lang.String类都是不可变类,当创建它们的实例后,其实例的实例变量不可改变。
Double d = new Double(3.2);
String str = new String("dddd")
3 自己创建不可变类
如果需要自己创建不可变类,可遵守如下规则:
- 使用private和final修饰符来修饰该类的成员变量;
- 提供带参数构造器,用于根据传入参数来初始化类里的成员变量;
- 仅为该类的成员变量提供getter方法,不为该类的成员变量提供setter方法,因为普通方法无法修改final修饰的成员变量;
- 如果有必要,重写Object类的hashCode()和equals()方法。equals()方法根据关键成员变量来作为两个对象是否相等的标准,除此之外,还应该保证两个用equals()方法判断为相等的对象的hashCode()也相等。
package CaoGao;
public class NotChangeClass {
private final String a;
private final String b;
// 在构造器里初始化两个实例变量
public NotChangeClass(){
this.a = "";
this.b = "";
}
public NotChangeClass(String a,String b){
this.a = a;
this.b = b;
}
// 为两个实例变量仅提供getter方法
public String getA() {
return a;
}
public String getB() {
return b;
}
// 重写equals方法,判断两个对象是否相等
public boolean equals(Object obj){
if (this == obj)
{
return true;
}
if (obj != null && obj.getClass() == NotChangeClass.class)
{
NotChangeClass acc = (NotChangeClass)obj;
if (this.getA().equals(acc.getB())
&& this.getB().equals(acc.getA()))
{
return true;
}
}
return false;
}
public int hashCode()
{
return a.hashCode() + b.hashCode() *31;
}
}
4 不可变类包含可变的成员变量类型
如果不可变类包含可变的的成员变量类型(如引用变量),则其对象的成员变量的值依然可以改变,这时就说这个不可变类其实是失败的。
4.1 采取措施保护成员变量所引用的对象不会被修改
代码示例
内存变化示例
5 缓存实例的不可变类
不可变类的实例状态不可改变,可以很方便的被多个对象所共享。
如果程序经常需要使用相同的不可变实例,则应该考虑缓存这种不可变类的实例。(重复创建相同对象没有太大意义,且加大协调开销。)
缓存是软件设计中一个非常有用的模式,缓存的实现方式有很多种,不同的实现方式可能存在较大的性能差别,此处只介绍一个简单的。
6 使用数组作为缓存池实现一个缓存实例的不可变类
6.1自行设计的
代码示例:
1、有缓存机制的不可变类
package Class;
public class CacheImmutable {
private static int MAX_SIZE = 10;
// 使用数组来缓存已有的实例
private static CacheImmutable[] cache = new CacheImmutable[MAX_SIZE];
// 记录缓存实例在缓存中的位置,cache[pos-1]是最新缓存的实例
private static int pos = 0;
private final String name;
private CacheImmutable(String name){
this.name = name;
}
public String getName(){
return name;
}
public static CacheImmutable valueof(String name){
// 遍历已缓存的对象
for (int i = 0; i < MAX_SIZE; i++){
// 如果已有相同实例,则直接返回该缓存的实例
if (cache[i] != null && cache[i].getName().equals(name)){
return cache[i];
}
}
// 如果缓存已满
if (pos == MAX_SIZE){
// 把缓存的第一个对象覆盖,即把刚刚生成的对象放在缓存池的最开始位置
cache[0] = new CacheImmutable(name);
// 把pos设为1
pos = 1;
}
else {
// 把创建的对象缓存起来,pos加1
cache[pos++] = new CacheImmutable(name);
}
return cache[pos - 1];
}
public boolean equals(Object obj){
if (this == obj){
return true;
}
if (obj != null && obj.getClass() == CacheImmutable.class){
CacheImmutable ci = (CacheImmutable)obj;
return name.equals(ci.getName());
}
return false;
}
public int hashCode(){
return name.hashCode();
}
}
2、main代码
package Class;
public class CacheImmutableTest {
public static void main(String[] args) {
CacheImmutable c1 = CacheImmutable.valueof("hello");
CacheImmutable c2 = CacheImmutable.valueof("hello");
// 下面代码将输出true
System.out.println(c1 == c2);
}
}
6.2 java中lang包下的Integer类
package Class;
public class InterCacheTest {
public static void main(String[] args) {
// 生成新的Integer对象
Integer int1 = new Integer(6);
// 生成新的Integer对象,并缓存该对象
Integer int2 = Integer.valueOf(6);
// 直接从缓存中取出Integer对象
Integer int3 =Integer.valueOf(6);
System.out.println(int1 == int2); // false
System.out.println(int2 == int3); // true
/**由于Integer只缓存-128-127之间的值,
* 因此200对应的Integer对象没有被缓存*/
Integer int4 = Integer.valueOf(200);
Integer int5 = Integer.valueOf(200);
System.out.println(int4 == int5); // false
}
}
7 注意:
是否需要隐藏CacheImmutable类的构造器完全取决于系统需求,盲目乱用缓存也可能导致系统性能下降,缓存的对象会占用系统内存,如果某个对象只使用一次,重复使用的概率不大,缓存该实例就弊大于利;反之,如果某个对象需要频繁的重复使用,缓存该实例就利大于弊。