不可变类
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 + "]"; } }
相关文章推荐
- Android中的Bitmap缓存池使用详解
- cell重用时,老是提示找不到标识的cell,让我们注册cell
- go语言的官方包sync.Pool的实现原理和适用场景
- iOS 的tabelView 重用机制 缓存池
- java中的不可变类
- 【连载】关系型数据库是如何工作的?(6) - Hash表
- JAVA的可变类与不可变类
- 可变类与不可变类
- 不可变类详解
- Java可变类与不可变类
- Java不可变类
- Java中的可变类和不可变类
- Java中的不可变类
- Java可变类与不可变类
- golang sync.Pool试用说明及注意事项
- Java中String类型的不可变性和驻留池
- JAVA不可变类(immutable)机制与String的不可变性
- [疯狂Java]面向对象:不可变类
- 游戏设计 -- 资源缓存池
- unity 之 缓存池 基础篇