不可变类详解
2015-04-01 21:05
211 查看
不可变类(immutable)类的意思是说创建给雷的实例后,该实例的Field是不可改变的。Java提供的8个包装类和Java.lang.String类都是不可变类
如果需要自己创建自定义的不可变类
(1)使用private和final修饰该类的Field
(2)提供带参数的构造器,用于根据传入参数来初始化类里的Field
(3)仅为该类的Field提供getter方法,不要为给类的field提供setter方法
(4)如果有必要,重写Object的equals方法和hashcode方法
/*
自己定义了个不可变类
*/
public class Address
{
//不可变类的两个成员都是String类,是不可变类,即Address满足不可变类的第一个条件
private final String detail;
private final String postCode;
public Address()
{
}
//提供带参数的构造器
public Address(String detail, String postCode)
{
this.detail = detail;
this.postCode = postCode;
}
//提供了getter方法,不提供setter方法
public String getDetail()
{
return this.detail;
}
public String getPostCode()
{
return this.postCode();
}
//重写了Object类的equals()方法和hashCode()方法
public boolean equals(Object obj)
{
if (obj == this)
{
return true;
}
if (obj != null && Address.class == obj.getClass())
{
Address ad = (Address)obj;
if (this.getAddress() == ad.getAddress() && this.getpostCode() == obj.getPostCode())
{
return true;
}
}
return false;
}
}
public boolean hashCode()
{
return detail.hashCode()+postCode.hashCode() * 31;
}
当一个内的内部是普通类的引用时,这个普通类可以随意更改类的Field,当使用final修饰引用变量时,仅表示这个引用类型变量不可被重新赋值,但引用类型变量所指向的对象依然可改变,这给就产生了一个问题:当创建不可变类时,如果它包含的Field的类型是可变的,那么其对象的Field依然是可变的---这个不可变类其实是失败的
class Name
{
private String firstName;
private String lastName;
public Name()
{
}
public Name(String firstName, String lastName)
{
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName()
{
return this.firstName;
}
public String getLastName()
{
return this.lastName;
}
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
public void setLastName(String lastName)
{
this.lastName = lastName;
}
}
public class Person
{
private final Name name;
public Person(Name name)
{
this.name = name;
}
public Name getName()
{
return this.name;
}
public static void main(String[] args)
{
Name n = new Name("陈", "奇");
Person p = new Person(n);
System.out.println(p.getName().getFirstName());
n.setFirstName("chen");
System.out.println(p.getName().getFirstName());
}
}
不难发现,Person对象的name的fiestName已经改变
为了保持Person对象的不可变类,必须保护好Person对象的引用类型Field:name;让程序无法访问到Person对象的name field,也就无法利用name Field的可变性来改变Person对象
public Person(Name name)
{
this.name = new Name(name.getFirstName(), name.getLastName());
}
public Name getName()
{
return new Name(name.getFirsName(), name.getLastName());
}
把Name对象的实例重新复制一份传入到Person对象的name Field,这样即使更改name,也改变不了Person对象的name实例
缓存实例的不可变类
不可变类的实例状态不可改变,可以很方便的呗多个对象共享,如果程序需要使用相同的不可变类实例,则应考虑缓存这种不可变类的实例,毕竟重复创建相同的对象没有太大的意义
class CacheImmutable
{
private static int MAX_SIZE = 10;
private static CacheImmutable[] cache = new CacheImmutable[MAX_SIZE];
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;
}
else
{
cache[pos++] = new CacheImmutable(name);
}
return cache[pos-1];
}
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj != null && CacheImmutable.class == obj.getClass())
{
CacheImmutable ci = (CacheImmutable)obj;
return name.equals(ci.getName());
}
return false;
}
public int hashCode()
{
return name.hashCode();
}
}
public class CacheImmutableTest
{
public static void main(String[] args)
{
CacheImmutable c1 = CacheImmutable.valueOf("hello");
CacheImmutable c2 = CacheImmutable.valueOf("hello");
System.out.println(c1 == c2);
}
}
Java.lang.Integer类也提供了缓存机制,如果采用new构造器来创建对象,则每次返回全新的Integer对象,如果采用valueOf()方法来创建Integer对象,则会缓存该方法创建的对象
static final Integer[] cache = new Integer[-(-128) + 127 + 1];
static {
for (int i=0; i<cache.length; i++)
{
cache[i] = new Integer(i-128);
}
}
如果需要自己创建自定义的不可变类
(1)使用private和final修饰该类的Field
(2)提供带参数的构造器,用于根据传入参数来初始化类里的Field
(3)仅为该类的Field提供getter方法,不要为给类的field提供setter方法
(4)如果有必要,重写Object的equals方法和hashcode方法
/*
自己定义了个不可变类
*/
public class Address
{
//不可变类的两个成员都是String类,是不可变类,即Address满足不可变类的第一个条件
private final String detail;
private final String postCode;
public Address()
{
}
//提供带参数的构造器
public Address(String detail, String postCode)
{
this.detail = detail;
this.postCode = postCode;
}
//提供了getter方法,不提供setter方法
public String getDetail()
{
return this.detail;
}
public String getPostCode()
{
return this.postCode();
}
//重写了Object类的equals()方法和hashCode()方法
public boolean equals(Object obj)
{
if (obj == this)
{
return true;
}
if (obj != null && Address.class == obj.getClass())
{
Address ad = (Address)obj;
if (this.getAddress() == ad.getAddress() && this.getpostCode() == obj.getPostCode())
{
return true;
}
}
return false;
}
}
public boolean hashCode()
{
return detail.hashCode()+postCode.hashCode() * 31;
}
当一个内的内部是普通类的引用时,这个普通类可以随意更改类的Field,当使用final修饰引用变量时,仅表示这个引用类型变量不可被重新赋值,但引用类型变量所指向的对象依然可改变,这给就产生了一个问题:当创建不可变类时,如果它包含的Field的类型是可变的,那么其对象的Field依然是可变的---这个不可变类其实是失败的
class Name
{
private String firstName;
private String lastName;
public Name()
{
}
public Name(String firstName, String lastName)
{
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName()
{
return this.firstName;
}
public String getLastName()
{
return this.lastName;
}
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
public void setLastName(String lastName)
{
this.lastName = lastName;
}
}
public class Person
{
private final Name name;
public Person(Name name)
{
this.name = name;
}
public Name getName()
{
return this.name;
}
public static void main(String[] args)
{
Name n = new Name("陈", "奇");
Person p = new Person(n);
System.out.println(p.getName().getFirstName());
n.setFirstName("chen");
System.out.println(p.getName().getFirstName());
}
}
不难发现,Person对象的name的fiestName已经改变
为了保持Person对象的不可变类,必须保护好Person对象的引用类型Field:name;让程序无法访问到Person对象的name field,也就无法利用name Field的可变性来改变Person对象
public Person(Name name)
{
this.name = new Name(name.getFirstName(), name.getLastName());
}
public Name getName()
{
return new Name(name.getFirsName(), name.getLastName());
}
把Name对象的实例重新复制一份传入到Person对象的name Field,这样即使更改name,也改变不了Person对象的name实例
缓存实例的不可变类
不可变类的实例状态不可改变,可以很方便的呗多个对象共享,如果程序需要使用相同的不可变类实例,则应考虑缓存这种不可变类的实例,毕竟重复创建相同的对象没有太大的意义
class CacheImmutable
{
private static int MAX_SIZE = 10;
private static CacheImmutable[] cache = new CacheImmutable[MAX_SIZE];
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;
}
else
{
cache[pos++] = new CacheImmutable(name);
}
return cache[pos-1];
}
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj != null && CacheImmutable.class == obj.getClass())
{
CacheImmutable ci = (CacheImmutable)obj;
return name.equals(ci.getName());
}
return false;
}
public int hashCode()
{
return name.hashCode();
}
}
public class CacheImmutableTest
{
public static void main(String[] args)
{
CacheImmutable c1 = CacheImmutable.valueOf("hello");
CacheImmutable c2 = CacheImmutable.valueOf("hello");
System.out.println(c1 == c2);
}
}
Java.lang.Integer类也提供了缓存机制,如果采用new构造器来创建对象,则每次返回全新的Integer对象,如果采用valueOf()方法来创建Integer对象,则会缓存该方法创建的对象
static final Integer[] cache = new Integer[-(-128) + 127 + 1];
static {
for (int i=0; i<cache.length; i++)
{
cache[i] = new Integer(i-128);
}
}
相关文章推荐
- Linux 中可重入函数与不可重入函数详解
- RAID详解[RAID0/RAID1/RAID10/RAID5](不可不知的硬件知识转)
- 双核不可阻挡!首款双核处理器Tegra2详解
- python中的不可变数据类型与可变数据类型详解
- 深入Java不可变类型的详解
- 可重入函数与不可重入函数详解
- 数据库问题原因详解(脏读、不可重复读、幻读)
- 数据库问题原因详解(脏读、不可重复读、幻读)
- GMT、UTC、CST详解--不可不知
- 【给将来学神的算法详解--高精】(6)不可避免的时间复杂度
- Java中String的不可变性特性详解
- pathchk命令_Linux pathchk 命令用法详解:检查文件中不可移植的部分
- Windows下Ping命令详解及使用小技巧
- 分享《21个项目玩转深度学习:基于TensorFlow的实践详解》PDF+源代码
- 基于Java字符编码的使用详解
- Java使用AES加密和解密的实例详解
- 将15位身份证补全为18位身份证的算法示例详解
- C#重点知识详解(一)
- C#重点知识详解(二)