<%@ page contentType="text/html; charset=gb2312"%> 关于不可变类和可变类
网站公告:   ◆北天JAVA技术网热情为java爱好者服务,本网内容包括JAVA(JSP、servlet、EJB、webservice、j2ee、javabean、应用服务器、JavaScript),数据库(MYSQL、SQL Server、Sybase、Oracle、DB2、数据库综合知识),设计研究(设计模式、Struts、Spring、Hibernate、设计框架、设计综合知识),WEB2.0新技术(主要介绍AJAX),以及各种技术的入门、实例、例子等等,欢迎各位多来坐坐!◆  诚邀各位JAVA爱好者加盟!◆  本网站内容丰富,更新快,保证每周20篇以上!  
加入收藏
设为首页
联系站长
承接项目
  相关资源:网站首页 | 免费培训学院 | 技术论坛 | JAVA聊天室 | 作家专栏 | 开发工具 | 认证考试 | 会员俱乐部
  JAVA技术初学者园地 | jsp与servlet | javascript | Java源代码 | EJB | web service | 应用服务器 | JAVA综合知识
  设计研究设计模式 | 设计框架 | Struts | Spring | Hibernate | 开源项目 | 面向对象设计 | 设计综合知识
  数 据 库MYSQL | SQL Server | Sybase | Oracle | DB2 | Informix | Access | 数据库综合知识
  其他资源:AJAX新技术 | 网站开发 | ERP软件 | OA办公软件 | 商业智能BI | 开发综合知识 | 承接项目 | 项目试用

 
 
关于不可变类和可变类
     发布者: 发布时间:2006-12-30

     所谓不可变类,是指当创建了这个类的实例后,就不允许修改它的属性值。在JDK的基本类库中,所有基本类型的包装类,如Integer和Long类,都是不可变类,java.lang.String也是不可变类。以下代码创建了一个String对象和Integer对象,它们的值分别为“Hello”和10,在程序代码中无法再改变这两个对象的值,因为Integer和String类没有提供修改其属性值的接口。

String s=new String("Hello");

Integer i=new Integer(10);

用户在创建自己的不可变类时,可以考虑采用以下设计模式:

1..把属性定义为private final类型。

2.不对外公开用于修改属性的setXXX()方法。

3.只对外公开用于读取属性的getXXX()方法。

4.在构造方法中初始化所有属性。

5.覆盖Object类的equals()和hashCode()方法。在equals()方法中根据对象的属性值来比较两个对象是否相等,并且保证用equals()方法判断为相等的两个对象的hashCode()方法的返回值也相等,这可以保证这些对象能正确地放到HashMap或HashSet集合中。

     如果需要的话,提供实例缓存和静态工厂方法,允许用户根据特定参数获得与之匹配的实例。

       例程11-9的Name类就是不可变类,它仅仅提供了读取sex和description属性的getXXX()方法,但没有提供修改这些属性的setXXX()方法。

例程11-9 Name.java

public class Name {

private final String firstname;

private final String lastname;

public Name(String firstname, String lastname) {

   this.firstname = firstname;

   this.lastname = lastname;

}

public String getFirstname(){

   return firstname;

}

public String getLastname(){

   return lastname;

}

public boolean equals(Object o){

   if (this == o) return true;

   if (!(o instanceof Name)) return false;

   final Name name = (Name) o;

   if(!firstname.equals(name.firstname)) return false;

   if(!lastname.equals(name.lastname)) return false;

  return true;

}

public int hashCode(){

  int result;

  result= (firstname==null?0:firstname.hashCode());

  result = 29 * result + (lastname==null?0:lastname.hashCode());

  return result;

}

public String toString(){

  return lastname+" "+firstname;

}

}

假定Person类的name属性定义为Name类型:

public class Person{

  private Name name;

  private Gender gender;

  …

}

     以下代码创建了两个Person对象,他们的姓名都是“王小红”,一个是女性,另一个是男性。在最后一行代码中,把第一个Person对象的姓名改为“王小虹”。

Name name=new Name("小红","王");

Person person1=new Person(name,Gender.FEMALE);

Person person2=new Person(name,Gender.MALE);

name=new Name("小虹","王");

person1.setName(name); //修改名字

      与不可变类对应的是可变类,可变类的实例属性是允许修改的。如果把以上例程11-9的Name类的firstname属性和lastname属性的final修饰符去除,并且增加相应的public类型的setFirstname()和setLastname()方法,Name类就变成了可变类。以下程序代码本来的意图也是创建两个Person对象,他们的姓名都是“王小红”,接着把第一个Person对象的姓名改为“王小虹”:

//假定以下Name类是可变类

Name name=new Name("小红","王");

Person person1=new Person(name,Gender.FEMALE);

Person person2=new Person(name,Gender.MALE);

name.setFirstname("小虹"); //试图修改第一个Person对象的名字

    以上最后一行代码存在错误,因为它会把两个Person对象的姓名都改为“王小虹”。由此可见,使用可变类更容易使程序代码出错。因为随意改变一个可变类对象的状态,有可能会导致与之关联的其他对象的状态被错误地改变。

    不可变类的实例在实例的整个生命周期中永远保持初始化的状态,它没有任何状态变化,简化了与其他对象之间的关系。不可变类具有以下优点:

l 不可变类能使程序更加安全,不容易出错。

l 不可变类是线程安全的,当多个线程访问不可变类的同一个实例时,无须进行线程的同步。

     由此可见,应该优先考虑把类设计为不可变类,假使必须使用可变类,也应该把可变类尽可能多的属性设计为不可变的,即用final修饰符来修饰,并且不对外公开用于改变这些属性的方法。

      在创建不可变类时,假如它的属性的类型是可变类型,在必要的情况下,必须提供保护性拷贝,否则,这个不可变类实例的属性仍然有可能被错误地修改。这条建议同样适用于可变类中用final修饰的属性。

     例如例程11-10的Schedule类包含学校的开学时间和放假时间信息,它是不可变类,它的两个属性start和end都是final类型,表示不允许被改变,但是这两个属性都是Date类型,而Date类是可变类。

例程11-10 Schedule.java

import java.util.Date;

public final class Schedule{

  private final Date start; //开学时间,不允许被改变

  private final Date end; //放假时间,不允许被改变

  public Schedule(Date start,Date end){

  //不允许放假日期在开学日期的前面

  if(start.compareTo(end)>0)

    throw new IllegalArgumentException(start +" after " +end);

    this.start=start;

    this.end=end;

}

public Date getStart(){return start;}

public Date getEnd(){return end;}

}

    尽管以上Schedule类的start和end属性是final类型的,但由于它们引用Date对象,在程序中可以修改所引用Date对象的属性。以下程序代码创建了一个Schedule对象,接下来把开学时间和放假时间都改为当前系统时间。

Calendar c= Calendar.getInstance();

c.set(2006,9,1);

Date start=c.getTime();

c.set(2007,1,25);

Date end=c.getTime();

Schedule s=new Schedule(start,end);

end.setTime(System.currentTimeMillis()); //修改放假时间

start=s.getStart();

start.setTime(System.currentTimeMillis()); //修改开学时间

为了保证Schedule对象的start属性和end属性值不会被修改,必须为这两个属性使用保护性拷贝,参见例程11-11。

例程11-11 采用了保护性拷贝的Schedule.java

import java.util.Date;

public final class Schedule {

  private final Date start;

  private final Date end;

  public Schedule(Date start,Date end){

  //不允许放假日期在开学日期的前面

  if(start.compareTo(end)>0)throw new IllegalArgumentException(start +" after " +end);

  this.start=new Date(start.getTime()); //采用保护性拷贝

  this.end=new Date(end.getTime()); //采用保护性拷贝

}

  public Date getStart(){return (Date)start.clone();} //采用保护性拷贝

  public Date getEnd(){return (Date)end.clone();} //采用保护性拷贝

}

通过采用保护性拷贝,其他程序无法获得与Schedule对象关联的两个Date对象的引用,因此也就无法修改这两个Date对象的属性值。

Tips

如果Schedule类中被final修饰的属性所属的类是不可变类,就无须提供保护性拷贝,因为该属性所引用的实例的值永远不会被改变。这进一步体现了不可变类的优点。

(转载文章请保留出处:北天JAVA技术网(www.java114.com))
 
更多精彩文章:
Java SE 6中应用程序的启动界面
对象的创建和销毁
J2ee应用程序设计――多层框架
怎样设计框架
设计模式:设计自己的MVC框架
JAVA设计模式之事务处理
 
最近评论:
        
你曾悄悄的来过!
wow gold,wow gold,wow gold,ffxi gil max(703)
        
冰封的往事!
wow power leveling,wow gold,WoW Gold,wow gold max(2916)
        
飞舞的传奇!
传世私服,传世私服.传奇世界私服传奇世界私服,传世私服传世私服, 传奇世界私服传奇世界私服.传奇私服传奇私服. max(2908)
        
玻璃瓶、玻璃酒瓶、洋酒瓶、蒙砂瓶、饮料瓶——正华玻璃酒业包装公司
        
玻璃瓶、玻璃酒瓶、洋酒瓶、蒙砂瓶、饮料瓶——正华玻璃酒业包装公司
        
███月赚5000元██:QQ:80650040
        
███月赚5000元██:QQ:80650040
        
███月赚5000元██:QQ:80650040
        
███月赚5000元██:QQ:80650040
        
███月赚5000元██:QQ:80650040
        
标 题:   
内 容:   
 
                                  
 
免责声明:该文章由网友发表,如果对您造成侵权,请联系站长

首页 - 承接项目 - 网站地图 - 联系我们 -
版权所有北天JAVA技术工作室 ICP证号:粤ICP备06079815号