您的位置:首页 > 编程语言 > Java开发

不可变类

2016-05-25 14:56 525 查看

不可变类

先来科普2个概念,可变类和不可变类。

1),不可变类的意思就是创建该类的实例后,该实例的实例变量是不可改变的。Java提供的8个包装类和String类都是不可变类,当创建他们的实例后,其实例的实例变量是不

可改变的。

2),与不可变类对应的是可变类,可变类的含义是该类的实例变量是可变的。大部分时候所创建的类都是可变类,特别是JavaBean,因为总是在其实例变量提供了setter和

getter方法。

看下面的代码:

Double d = new Double(6.5);
String linkin = "LinkinPark";
上面的程序创建了一个Double对象和一个String对象,并为这两个对象传入了6.5和"LinkinPark"字符串作为参数,那么Double类和String类肯定需要提供实例变量来保存这两个

参数,但程序无法修改这两个实例变量的值,因此Double类和String类没有提供修改它们的方法。

如果需要创建自定义的不可变类,要遵守以下的规则:

1),使用private和final修饰该类的成员变量

2),提供带参数的构造器,用于根据传入参数来初始化该类的成员变量

3),仅为该类提供getter方法,不要提供setter方法,因为普通的方法不能改变这个类的属性

4),如果有必要,重写equals和hashcode方法。

/**
* 不可变类
*
* @author LinkinPark
*
*         <pre>
*         1,属性使用private final修饰
*         2,构造器对属性赋值
*         3,只提供get方法,不提供set方法
*         4,如果有需要就重写equals和hashCode方法
*         </pre>
*/
public class LinkinPark
{

private final String name;
private final Integer age;

public LinkinPark(String name, Integer age)
{
super();
this.name = name;
this.age = age;
}

public String getName()
{
return name;
}

public Integer getAge()
{
return age;
}

public static void main(String[] args)
{
new LinkinPark("LinkinPark", 25);
}

}


与可变类相比,不可变类的实例在整个生命周期中永远出于初始化阶段,它的实例变量不可改变,因此对不可变类的实例的控制将更加简单。

前面介绍final关键字时提到,当使用final修饰引用类型变量时,仅表示这个引用类型变量不可被重新赋值,但引用类型变量所指向的对象依然可以改变。

这就产生了一个问题,当创建一个不可变类时,如果它包含成员变量的类型是可变的,那么其对象的成员变量的值依然是可变的,那这个不可变类其实是失败的。

看下面的例子:

/**
* 引用类型的变量导致不可变类失败
*
* @author LinkinPark
*/
public class LinkinPark
{

private final String name;
private final Linkin linkin;

public LinkinPark(String name, Linkin linkin)
{
super();
this.name = name;
this.linkin = linkin;
}

public String getName()
{
return name;
}

public Linkin getLinkin()
{
return linkin;
}

@Override
public String toString()
{
return getClass().getSimpleName() + " [name=" + name + ", linkin=" + linkin + "]";
}

public static void main(String[] args)
{
Linkin linkin = new Linkin();
linkin.setName("NightWish1");
linkin.setAge(25);
LinkinPark linkinPark = new LinkinPark("LinkinPark", linkin);
System.out.println(linkinPark);

linkin.setAge(24);
linkin.setName("NightWish2");
System.out.println(linkinPark);
// LinkinPark [name=LinkinPark, linkin=Linkin [name=NightWish1, age=25]]
// LinkinPark [name=LinkinPark, linkin=Linkin [name=NightWish2, age=24]]
}

}

class Linkin
{
private String name;
private Integer age;

public String getName()
{
return name;
}

public void setName(String name)
{
this.name = name;
}

public Integer getAge()
{
return age;
}

public void setAge(Integer age)
{
this.age = age;
}

@Override
public String toString()
{
return getClass().getSimpleName() + " [name=" + name + ", age=" + age + "]";
}

}
运行上面的代码,我们也看到了,引用类型的变量导致我创建了一个失败的不可变类。那应该要怎么做呢?看下面代码:

/**
* 引用类型的变量导致不可变类失败
* 所以要针对引用类型的变量做专门的处理
*
* <pre>
* 1,构造器中不要直接使用传入的引用类型变量,自己取值然后重新new一次
* 2,引用类型的变量用来存储刚才那个初始化的对象
* 3,防止get方法直接返回刚才那个变量从而改变引用的那个对象,同样的方式处理
* </pre>
*
* @author LinkinPark
*/
public class LinkinPark
{

private final String name;
private final Linkin linkin;

/**
* @param name
* @param linkin
*/
public LinkinPark(String name, Linkin linkin)
{
super();
this.name = name;
this.linkin = new Linkin(linkin.getName(), linkin.getAge());
}

public String getName()
{
return name;
}

public Linkin getLinkin()
{
return new Linkin(linkin.getName(), linkin.getAge());
}

@Override
public String toString()
{
return getClass().getSimpleName() + " [name=" + name + ", linkin=" + linkin + "]";
}

public static void main(String[] args)
{
Linkin linkin = new Linkin("NightWish1", 25);
LinkinPark linkinPark = new LinkinPark("LinkinPark", linkin);
System.out.println(linkinPark);

linkin.setAge(24);
linkin.setName("NightWish2");
System.out.println(linkinPark);
// LinkinPark [name=LinkinPark, linkin=Linkin [name=NightWish1, age=25]]
// LinkinPark [name=LinkinPark, linkin=Linkin [name=NightWish2, age=24]]
}

}

class Linkin
{
private String name;
private Integer age;

public Linkin(String name, Integer age)
{
super();
this.name = name;
this.age = age;
}

public String getName()
{
return name;
}

public void setName(String name)
{
this.name = name;
}

public Integer getAge()
{
return age;
}

public void setAge(Integer age)
{
this.age = age;
}

@Override
public String toString()
{
return getClass().getSimpleName() + " [name=" + name + ", age=" + age + "]";
}

}


缓存实例的不可变类

不可变类的实例状态不可改变,可以很方便的被多个对象所共享。如果程序经常需要使用相同的不可变实例,则应该考虑缓存这种不可变类的实例。毕竟重复创建相同的对象没

有太大的意义,而且加大系统开销。如果可能,应该将已经创建的不可变类的实例进行缓存。

缓存是软件设计中一个非常有用的模式,缓存的实现方式也有很多种,不同的实现方式可能存在较大的性能差别,关于缓存的性能问题和实现方式我会在后面的博客中整理一个

分类,此处不做赘述。

OK,前面我已经使用了不可变类LinkinPark,现在我自己用一个数组写一个缓存池,从而实现一个缓存LinkinPark实例的缓存池。

当然也可以直接在LinkinPark类中写缓存,这样子将实现一个缓存自己实例的不可变类。

public class LinkinParkCache
{
// 定义一个数组+一个下标+数组最大容量
private static int POS_INDEX = 0;
private static final int MAX_SIZE = 10;
private static final LinkinPark[] cache = new LinkinPark[MAX_SIZE];

// 定义一个name标示用来重写hashCode方法
private final String name;

private LinkinParkCache(String name)
{
this.name = name;
}

public String getName()
{
return name;
}

public static LinkinPark valueOf(String name)
{
// 1,循环获取缓存的实例
for (int i = 0; i < cache.length; i++)
{
if (cache[i] != null && cache[i].getName().equals(name))
{
return cache[i];
}
}
// 2,循环结束后没有找见实例,则向缓存中添加
if (POS_INDEX == MAX_SIZE)
{
cache[0] = new LinkinPark(name, new Linkin("LinkinPark", 25));
POS_INDEX = 1;
}
else
{
cache[POS_INDEX++] = new LinkinPark(name, new Linkin("LinkinPark", 25));
}
return cache[POS_INDEX - 1];
}

@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj != null && obj.getClass() == this.getClass())
{
LinkinPark linkinPark = (LinkinPark) obj;
return linkinPark.getName().equals(this.getName());
}
return false;
}

@Override
public int hashCode()
{
return name.hashCode();
}

public static void main(String[] args)
{
LinkinPark linkin = LinkinParkCache.valueOf("林肯的缓存池");
LinkinPark linkinPark = LinkinParkCache.valueOf("林肯的缓存池");
// 下面代码输出true,使用了缓存
System.out.println(linkin == linkinPark);
}

}

/**
* 不可变类
*
* @author LinkinPark
*/
class LinkinPark
{

private final String name;
private final Linkin linkin;

public LinkinPark(String name, Linkin linkin)
{
super();
this.name = name;
this.linkin = new Linkin(linkin.getName(), linkin.getAge());
}

public String getName()
{
return name;
}

public Linkin getLinkin()
{
return new Linkin(linkin.getName(), linkin.getAge());
}

@Override
public String toString()
{
return getClass().getSimpleName() + " [name=" + name + ", linkin=" + linkin + "]";
}

public static void main(String[] args)
{
Linkin linkin = new Linkin();
linkin.setName("NightWish1");
linkin.setAge(25);
LinkinPark linkinPark = new LinkinPark("LinkinPark", linkin);
System.out.println(linkinPark);

linkin.setAge(24);
linkin.setName("NightWish2");
System.out.println(linkinPark);
// LinkinPark [name=LinkinPark, linkin=Linkin [name=NightWish1, age=25]]
// LinkinPark [name=LinkinPark, linkin=Linkin [name=NightWish1, age=25]]
}

}

/**
* 不可变类中的引用类型变量的定义
*
* @author LinkinPark
*/
class Linkin
{
private String name;
private Integer age;

public Linkin()
{
super();
}

public Linkin(String name, Integer age)
{
super();
this.name = name;
this.age = age;
}

public String getName()
{
return name;
}

public void setName(String name)
{
this.name = name;
}

public Integer getAge()
{
return age;
}

public void setAge(Integer age)
{
this.age = age;
}

@Override
public String toString()
{
return getClass().getSimpleName() + " [name=" + name + ", age=" + age + "]";
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息