2008年9月17日星期三

转载:关于三鹿奶粉事件的最新报道

关于三鹿奶粉事件的最新报道:

目前国家把责任推给三鹿,三鹿把责任推给奶农,奶农把责任推给奶牛,警方正全力抓捕不法奶牛。

据报道,责任奶牛已携二奶潜逃,仅捕获一小撮不明真相的牛群。


目前母牛们情绪稳定。。。。。


另据最新消息,水牛和蜗牛已通过半岛电视台发表声明声称和此事件无关。

2008年9月10日星期三

怎样的青春奔向怎样的未来

切格瓦拉 ——

怎样的青春奔向怎样的未来

引用:JAVA基础--在Java中轻松实现界面跳转

JAVA基础--在Java中轻松实现界面跳转

作者:jiziba 来源:IT专家网

 假设这样一种情况,我们的系统的界面使用javax.swing包构建,界面的基础是BaseView,他是一个容器,当然他应当提供获取控件元素的功能,比如得到按钮,下拉框,表格等,当然仅仅是一个容器而已,而我们的界面的元素全部部署在JPanel上。

  描述为:

  一个界面就是一个BaseView,他只包含一个JPanel,这个包含JPanel包含所有我们的Swing控件,例如JButton,JLable等等。

  问题出现了:我们通常因为业务的需要完成一个界面的操作要自动跳转到下一个界面,完成下一个界面又能跳回来(题外话:由于我们的操作是基于 GUI的,所以往往能保存Session信息,而Web却做不到),而这往往成为系统实现过程中效率低下的一个因素,我就见到我现在的系统中有人用600 行代码判断上一个界面应该是哪一个来跳转过来,因为很多界面都可以跳到当前界面。

  当然有一种做法是,在下一个界面类中包含指向上一个界面的变量,我们说,这不方便,也增加了依赖性,这对软件是不利的。

  接下来,我给出我的解决方法,希望对采用这种界面结构的朋友有所裨益。

  (以下全部用简化模型来讲述.)

  1.简单点,我们假设BaseView继承JWindow,当然可以是别的容器(依据你的实现),大概象这样:

  public abstract class BaseView extends JWindow{
  ...
  (实现一些取得界面控件,和界面信息的方法).

  }

  2.每个界面类都象这样定义:  

public class MyView extends BaseView{
  JPanel myPanel;
  public void playoutPanel(){
  JButton myButton = new JButton("OK");
  myPanel.add(myButton);
  ......
  (添加你需要的控件和布局到myPanel上)
  }
  }
  3.假设有其他的界面OneView,TwoView,ThreeView处理完操作后都需要跳转到myView,在myView中的ok按钮按下的时候需要回到原始界面。

  原来臃肿的代码需要在myView中添加一个变量BaseView anyView;用来存放转来的那个界面anyView,赋值在三者中的跳转代码中引用myView来设定.跳转代码象这样:  

public void jump(){
  MyView myView = new MyView();
  myView.anyView = this;
  this.remove(this.xxPanel);
  this.add(myView.getPanel());
  this.repaint();
  }
  看起来还不错,虽然需要引用MyView类,并调用他的变量和方法.但是跳转回来却不那么容易,否则怎么会用600行!

  大概象这样:(这已经是被我简化的) 

public void goBack(){
  if(anyView instanceof OneView){
  anyView.remove(this.myView);
  OneView ov = (OneView)anyView;
  anyView.add(ov.getPanel());
  anyView.repaint();
  }
  if(anyView instanceof TwoView){
  ....
  }
  ...
  }

  不经大量应用别的业务用例界面,这种编译依赖性真不是什么好事,更何况用了大量的低效的instanceof判断和转型操作.

  为了优化这种情形,彻底解决这个问题,我想应该设计一个第三方类来消除这种依赖性,并且让界面跳转不要这么费劲。这个第三方的类是这样设计的:

  在这个类中,必须有一个变量来保存某一个界面跳转的路径,如A->B->C.路径一旦被保存,你就拥有了控制显示任何一个界面的权 利了。在这个链中,第一个位置的界面应该是这次跳转的第一站,最后一个位置是当前站。这里存在一个因果关系:只有跳转了才可以跳回去。这样使得我们可以用 数组来保存这个路径。现实中,跳转的情形应该不会超过10次,所以我们把路径长度设为10(当然你可以根据需要更改)。这个类的样子大概象这样: 

 class ViewPath{
  JPanel[] pnlPath = null; //跳转的界面路径,界面跳转最大10个层次吧!!!
  int index = 0; //路径中的当前下标
  BaseView bsView = null; //当前路径所在的同一个View
  //在路径中寻找目标的方法
  public int find(JPanel pnl){ //该路径下是否有某个Panel,有的话返回下标,没有的话返回-1
  if(bsView==null) return -1; //没有初始化,该路径下没有任何Panel
  for(int i=0;i  if(pnl==pnlPath[i]){
  index = i;
  return i; //如果找到了则返回位置,并且把当前位置设为目标位置
  }
  }
  return -1; //没有找到,返回-1
  }
  //构造函数
  ViewPath(JPanel myPanel,BaseView myView){
  pnlPath = new JPanel[10]; //设置路径最大长度为10
  bsView = myView; //设置该路径所属的那个View
  pnlPath[0] = myPanel; //设立起始站
  index = 0; //设立起始站索引
  }
  }
  这样一个类就完成了保存一次跳转路径的作用.(当然,是否应该在find方法中设立目标位置是否合适有待商榷)

  那么我们如何使用这样一个路径?

  我们设立一个辅助类来完成这个工作,我们命名为ViewJump,我们知道作为辅助的类最好是不要有实例,特别是象这样的起接口作用的类,只提供静态方法.它的框架象这样: 

public class ViewJump{
  private static ViewPath[] viewPath = null; //路径池,系统多处使用,静态但私有,因为供内部用
  private ViewJump(){} //私有构造方法,辅助类只提供静态方法
  private static int find(JPanel pnl); //寻找给定的Panel是否在已有路径中,私有
  private static int newPath(JPanel myPanel,BaseView myView); //建立一个新路径,私有
  /**
  * 每个类需要使用该辅助类时都需要第一步注册自己,然后才能做其他操作
  * 返回一个注册码id,辅助类需要使用这个注册码进行其他操作
  */
  public static int registerPath(JPanel myPanel,BaseView myView);
  /**
  * 设立下一个界面.
  */
  public static void setNext(int id,JPanel aim);
  /**
  * 回到上一个界面
  */
  public static void back(int id);
  /**
  * 回到第一个界面
  */
  public static void backHome(int id);
  /**
  * 跳转到下一个界面
  */
  public static void jump(int id);
  }

  完成这样一个类的代码量并不多,一百多行,但是却使得用户完全脱离了处理不同界面的烦恼.稍后会把该类的源码附上,值得一提的是,这个类的实现 固然可以用到类似的实现当中,但是如果用户的界面结构并不是如此搭建,你就需要更改参数类型了.如果能把这些抽象出来,得到一个抽象类或接口,参数用 Object类型.用户根据自己的需要去实现这些方法,岂不妙哉!

  使用这个类,你可以简便的多的完成诸如上面的任务:  

OneView中:
  public void jump(){
  MyView myView = new MyView();
  int id = ViewJump.registerPath(this.xxPanel,this);
  ViewJump.setNext(id,myView.getPanel());
  ViewJump.jump(id);
  }
  MyView中退回的部分:
  protected void goBack(){
  int id = ViewJump.registerPath(this.myPanel,this);
  ViewJump.back(id);
  }
  天哪,这并不神奇,600行代码仅仅用了两行就实现了!
  好了,我就说这么多了,一切都掌握在你手中,用你的智慧来优化我们的冗余代码吧,因为这样它看起来相当不错.
  附:完整代码:(我把ViewPath类放在同一个文件ViewJump.java里,代码上面已经给出)
  public class ViewJump{
  private static ViewPath[] viewPath = null;
  //私有构造函数
  private ViewJump(){}
  //寻找该Panel是不是在路径中
  /**
  * 找到了返回在实例数组中的下标
  * 没有找到返回-1
  * @param pnl
  * @return
  */
  private static int find(JPanel pnl){
  // System.out.println("执行find() in ViewJump");
  if(viewPath == null || viewPath.length==0) return -1;
  for(int i = 0;i   ViewPath vp = viewPath[i]; //对该路径检查
  if(vp.find(pnl) != -1){
  return i;
  }
  }
  return -1;
  }
  //建立一个新的路径
  /**
  *
  * @param myPanel
  * @param myView
  */
  private static int newPath(JPanel myPanel,BaseView myView){
  System.out.println("执行newPath() in ViewJump");
  //检验一下看有没有无效的路径,有则清除
  if(viewPath == null || viewPath.length==0) {
  viewPath = new ViewPath[];
  return 0;
  }
  ViewPath[] vjArr = new ViewPath[viewPath.length];
  int count = 0;
  for(int i = 0;i   if(viewPath[i].bsView!=null){ //把不为空的值取出来
  vjArr[count++] = viewPath[i];
  }
  }
  viewPath = new ViewPath[count+1];
  System.arraycopy(viewPath,0,vjArr,0,count); //复制到原来的数组变量中
  //最后一个位置留给新加入的元素
  viewPath[count] = new ViewPath(myPanel,myView);
  return count;
  }
  //获得实例的方法

/**

  * 必须检查该Panel是不是已经在路径中了,如果在路径中,

  * 则返回注册的编号,用此编号扁可以访问到正确的类型了

  * 如果不在路径中,则以此为开始新建一个新的路径

  * 本来检查路径的时候没有必要检查路径的第一个元素,

  * 因为一个元素不可能是开端,但是为了防止用户连续两次registerPath的错误

  * 请把第一个元素也给检查一下  

* myView 参数只有当该界面为跳转的起始点时才需要,否则保持原始的View
  * @param me
  * @param other
  * 返回实例数组的下标,
  */
  public static int registerPath(JPanel myPanel,BaseView myView){
  System.out.println("执行registerPath() in ViewJump");
  int idx = find(myPanel);
  System.out.println("idx="+idx);
  if(idx==-1){ //返回-1表示没有找到,建立一个新的路径
  System.out.println("新建一个路径");
  idx = newPath(myPanel,myView);
  }
  System.out.println("执行完注册路径..");
  return idx; //返回实例下标
  }
  //设定要跳转的下一个目标
  public static void setNext(int id,JPanel aim){
  if(id<0||id>=viewPath.length){
  return;
  }
  ViewPath vp = viewPath[id];
  //设定目标,从这里看,这是存在安全漏洞的,如果使用者乱传递id进来的话
  JPanel[] path = vp.pnlPath;
  path[vp.index+1] = aim;
  }
  //回到上一个
  public static void back(int id){
  if(id<0||id>=viewPath.length){
  return;
  }
  ViewPath vp = viewPath[id];
  //回到上一个界面
  if(vp.index>0){ //只有当前面有路径时才作
  vp.bsView.remove(vp.pnlPath[vp.index]); //移去当前的
  vp.index--; //游标往前走一步
  vp.bsView.add(vp.pnlPath[vp.index],BorderLayout.CENTER); //增加当前的到界面
  vp.bsView.validate();
  vp.bsView.repaint();
  }
  }
  //回到起源处
  public static void backHome(int id){
  if(id<0||id>=viewPath.length){
  return;
  }
  ViewPath vp = viewPath[id];
  //直接回到第一步,需要清除该路径吗?中途断裂怎么办?办法是检查View是否已为空
  //选择不清除,每次在建立新的路径时,检查路径是不是已经无效了
  vp.bsView.remove(vp.pnlPath[vp.index]); //移去当前的
  vp.index = 0; //游标往前走一步
  vp.bsView.add(vp.pnlPath[vp.index],BorderLayout.CENTER); //增加当前的到界面
  vp.bsView.validate();
  vp.bsView.repaint();
  }
  //跳转到下一处
  public static void jump(int id){
  if(id<0||id>=viewPath.length){
  return;
  }
  ViewPath vp = viewPath[id];
  if(vp.pnlPath[vp.index+1]==null){
  return; //下一步根本没有设置
  }
  vp.bsView.remove(vp.pnlPath[vp.index]); //移去当前的
  vp.index++;
  vp.bsView.add(vp.pnlPath[vp.index],BorderLayout.CENTER);
  vp.bsView.validate();
  vp.bsView.repaint();
  }
  }

引用:JAVA高级:关于数据库设计中的14个技巧

下述十四个技巧,是许多人在大量的数据库分析与设计实践中,逐步总结出来的。对于这些经验的运用,读者不能生帮硬套,死记硬背,而要消化理解,实事求是,灵活掌握。并逐步做到:在应用中发展,在发展中应用。

  1. 原始单据与实体之间的关系

  可以是一对一、一对多、多对多的关系。在一般情况下,它们是一对一的关系:即一张原始单据对应且只对应一个实体。在特殊情况下,它们可能是一对 多或多对一的关系,即一张原始单证对应多个实体,或多张原始单证对应一个实体。这里的实体可以理解为基本表。明确这种对应关系后,对我们设计录入界面大有 好处。

  〖例1〗:一份员工履历资料,在人力资源信息系统中,就对应三个基本表:员工基本情况表、社会关系表、工作简历表。这就是“一张原始单证对应多个实体”的典型例子。

  2. 主键与外键

  一般而言,一个实体不能既无主键又无外键。在E?R 图中, 处于叶子部位的实体, 可以定义主键,也可以不定义主键(因为它无子孙), 但必须要有外键(因为它有父亲)。

  主键与外键的设计,在全局数据库的设计中,占有重要地位。当全局数据库的设计完成以后,有个美国数据库设计专家说:“键,到处都是键,除了键之 外,什么也没有”,这就是他的数据库设计经验之谈,也反映了他对信息系统核心(数据模型)的高度抽象思想。因为:主键是实体的高度抽象,主键与外键的配 对,表示实体之间的连接。

  3. 基本表的性质

  基本表与中间表、临时表不同,因为它具有如下四个特性:

  (1) 原子性。基本表中的字段是不可再分解的。

  (2) 原始性。基本表中的记录是原始数据(基础数据)的记录。

  (3) 演绎性。由基本表与代码表中的数据,可以派生出所有的输出数据。

  (4) 稳定性。基本表的结构是相对稳定的,表中的记录是要长期保存的。

  理解基本表的性质后,在设计数据库时,就能将基本表与中间表、临时表区分开来。

  4. 范式标准

  基本表及其字段之间的关系, 应尽量满足第三范式。但是,满足第三范式的数据库设计,往往不是最好的设计。为了提高数据库的运行效率,常常需要降低范式标准:适当增加冗余,达到以空间换时间的目的。

  〖例2〗:有一张存放商品的基本表,如表1所示。“金额”这个字段的存在,表明该表的设计不满足第三范式,因为“金额”可以由“单价”乘以“数量”得到,说明“金额”是冗余字段。但是,增加“金额”这个冗余字段,可以提高查询统计的速度,这就是以空间换时间的作法。

  在Rose 2002中,规定列有两种类型:数据列和计算列。“金额”这样的列被称为“计算列”,而“单价”和“数量”这样的列被称为“数据列”。

  表1 商品表的表结构

  商品名称 商品型号 单价 数量 金额

  电视机 29? 2,500 40 100,000

  5. 通俗地理解三个范式

通俗地理解三个范式,对于数据库设计大有好处。在数据库设计中,为了更好地应用三个范式,就必须通俗地理解三个范式(通俗地理解是够用的理解,并不是最科学最准确的理解):

  第一范式:1NF是对属性的原子性约束,要求属性具有原子性,不可再分解;

  第二范式:2NF是对记录的惟一性约束,要求记录有惟一标识,即实体的惟一性;

  第三范式:3NF是对字段冗余性的约束,即任何字段不能由其他字段派生出来,它要求字段没有冗余.

  没有冗余的数据库设计可以做到。但是,没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据。具 体做法是:在概念数据模型设计时遵守第三范式,降低范式标准的工作放到物理数据模型设计时考虑。降低范式就是增加字段,允许冗余。

  6. 要善于识别与正确处理多对多的关系

  若两个实体之间存在多对多的关系,则应消除这种关系。消除的办法是,在两者之间增加第三个实体。这样,原来一个多对多的关系,现在变为两个一对 多的关系。要将原来两个实体的属性合理地分配到三个实体中去。这里的第三个实体,实质上是一个较复杂的关系,它对应一张基本表。一般来讲,数据库设计工具 不能识别多对多的关系,但能处理多对多的关系。

  〖例3〗:在“图书馆信息系统”中,“图书”是一个实体,“读者”也是一个实体。这两个实体之间的关系,是一个典型的多对多关系:一本图书在不 同时间可以被多个读者借阅,一个读者又可以借多本图书。为此,要在二者之间增加第三个实体,该实体取名为“借还书”,它的属性为:借还时间、借还标志(0 表示借书,1表示还书),另外,它还应该有两个外键(“图书”的主键,“读者”的主键),使它能与“图书”和“读者”连接。

  7. 主键PK的取值方法

  PK是供程序员使用的表间连接工具,可以是一无物理意义的数字串, 由程序自动加1来实现。也可以是有物理意义的字段名或字段名的组合。不过前者比后者好。当PK是字段名的组合时,建议字段的个数不要太多,多了不但索引占用空间大,而且速度也慢。

  8. 正确认识数据冗余

  主键与外键在多表中的重复出现, 不属于数据冗余,这个概念必须清楚,事实上有许多人还不清楚。非键字段的重复出现, 才是数据冗余!而且是一种低级冗余,即重复性的冗余。高级冗余不是字段的重复出现,而是字段的派生出现。

  〖例4〗:商品中的“单价、数量、金额”三个字段,“金额”就是由“单价”乘以“数量”派生出来的,它就是冗余,而且是一种高级冗余。冗余的目 的是为了提高处理速度。只有低级冗余才会增加数据的不一致性,因为同一数据,可能从不同时间、地点、角色上多次录入。因此,我们提倡高级冗余(派生性冗 余),反对低级冗余(重复性冗余)。

  9. E--R图没有标准答案

  信息系统的E--R图没有标准答案,因为它的设计与画法不是惟一的,只要它覆盖了系统需求的业务范围和功能内容,就是可行的。反之要修改E-- R图。尽管它没有惟一的标准答案,并不意味着可以随意设计。好的E?R图的标准是:结构清晰、关联简洁、实体个数适中、属性分配合理、没有低级冗余。

  10. 视图技术在数据库设计中很有用

  与基本表、代码表、中间表不同,视图是一种虚表,它依赖数据源的实表而存在。视图是供程序员使用数据库的一个窗口,是基表数据综合的一种形式, 是数据处理的一种方法,是用户数据保密的一种手段。为了进行复杂处理、提高运算速度和节省存储空间, 视图的定义深度一般不得超过三层。 若三层视图仍不够用, 则应在视图上定义临时表, 在临时表上再定义视图。这样反复交迭定义, 视图的深度就不受限制了。

对于某些与国家政治、经济、技术、军事和安全利益有关的信息系统,视图的作用更加重要。这些系统的基本表完成物理设计之后,立即在基本表上建立第一 层视图,这层视图的个数和结构,与基本表的个数和结构是完全相同。并且规定,所有的程序员,一律只准在视图上操作。只有数据库管理员,带着多个人员共同掌 握的“安全钥匙”,才能直接在基本表上操作。请读者想想:这是为什么?

  11. 中间表、报表和临时表

  中间表是存放统计数据的表,它是为数据仓库、输出报表或查询结果而设计的,有时它没有主键与外键(数据仓库除外)。临时表是程序员个人设计的,存放临时记录,为个人所用。基表和中间表由DBA维护,临时表由程序员自己用程序自动维护。

  12. 完整性约束表现在三个方面

  域的完整性:用Check来实现约束,在数据库设计工具中,对字段的取值范围进行定义时,有一个Check按钮,通过它定义字段的值城。参照完整性:用PK、FK、表级触发器来实现。用户定义完整性:它是一些业务规则,用存储过程和触发器来实现。

  13. 防止数据库设计打补丁的方法是“三少原则”

  (1) 一个数据库中表的个数越少越好。只有表的个数少了,才能说明系统的E--R图少而精,去掉了重复的多余的实体,形成了对客观世界的高度抽象,进行了系统的数据集成,防止了打补丁式的设计;

  (2) 一个表中组合主键的字段个数越少越好。因为主键的作用,一是建主键索引,二是做为子表的外键,所以组合主键的字段个数少了,不仅节省了运行时间,而且节省了索引存储空间;

  (3) 一个表中的字段个数越少越好。只有字段的个数少了,才能说明在系统中不存在数据重复,且很少有数据冗余,更重要的是督促读者学会“列变行”,这样就防止了 将子表中的字段拉入到主表中去,在主表中留下许多空余的字段。所谓“列变行”,就是将主表中的一部分内容拉出去,另外单独建一个子表。这个方法很简单,有 的人就是不习惯、不采纳、不执行。

  数据库设计的实用原则是:在数据冗余和处理速度之间找到合适的平衡点。“三少”是一个整体概念,综合观点,不能孤立某一个原则。该原则是相对 的,不是绝对的。“三多”原则肯定是错误的。试想:若覆盖系统同样的功能,一百个实体(共一千个属性) 的E--R图,肯定比二百个实体(共二千个属性) 的E--R图,要好得多。

  提倡“三少”原则,是叫读者学会利用数据库设计技术进行系统的数据集成。数据集成的步骤是将文件系统集成为应用数据库,将应用数据库集成为主题 数据库,将主题数据库集成为全局综合数据库。集成的程度越高,数据共享性就越强,信息孤岛现象就越少,整个企业信息系统的全局E?R图中实体的个数、主键 的个数、属性的个数就会越少。

  提倡“三少”原则的目的,是防止读者利用打补丁技术,不断地对数据库进行增删改,使企业数据库变成了随意设计数据库表的“垃圾堆”,或数据库表 的“大杂院”,最后造成数据库中的基本表、代码表、中间表、临时表杂乱无章,不计其数,导致企事业单位的信息系统无法维护而瘫痪。

  “三多”原则任何人都可以做到,该原则是“打补丁方法”设计数据库的歪理学说。“三少”原则是少而精的原则,它要求有较高的数据库设计技巧与艺术,不是任何人都能做到的,因为该原则是杜绝用“打补丁方法”设计数据库的理论依据。

  14. 提高数据库运行效率的办法

  在给定的系统硬件和系统软件条件下,提高数据库系统的运行效率的办法是:

  (1) 在数据库物理设计时,降低范式,增加冗余, 少用触发器, 多用存储过程。

  (2) 当计算非常复杂、而且记录条数非常巨大时(例如一千万条),复杂计算要先在数据库外面,以文件系统方式用C++语言计算处理完成之后,最后才入库追加到表中去。这是电信计费系统设计的经验。

  (3) 发现某个表的记录太多,例如超过一千万条,则要对该表进行水平分割。水平分割的做法是,以该表主键PK的某个值为界线,将该表的记录水平分割为两个表。若发现某个表的字段太多,例如超过八十个,则垂直分割该表,将原来的一个表分解为两个表。

  (4) 对数据库管理系统DBMS进行系统优化,即优化各种系统参数,如缓冲区个数。

  (5) 在使用面向数据的SQL语言进行程序设计时,尽量采取优化算法。

  总之,要提高数据库的运行效率,必须从数据库系统级优化、数据库设计级优化、程序实现级优化,这三个层次上同时下功夫。

2008年9月2日星期二

引用:优秀Java程序员必须了解的GC工作原理

引用:开发者在线 Builder.com.cn 作者: 来源:搜讯网社区

一个优秀的Java程 序员必须了解GC的工作原理、如何优化GC的性能、如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统、实时系统等,只有全面 提升内存的管理效率 ,才能提高整个应用程序的性能。本篇文章首先简单介绍GC的工作原理之后,然后再对GC的几个关键问题进行深入探讨,最后提出一些Java程序设计建议,从GC角度提高Java程序的性能。

GC的基本原理

Java的内存管理实际上就是对象的管理,其中包括对象的分配和释放。

对于程序员来说,分配对象使用new关键字;释放对象时,只要将对象所有引用赋值为null,让程序不能够再访问到这个对象,我们称该对象为"不可达的".GC将负责回收所有"不可达"对象的内存空间。

对于GC来说,当程序员创建对象时,GC就开 始监控这个对象的地址、大小以及使用情况。通常,GC采用有向图的方式记录和管理堆(heap)中的所有对象(详见 参考资料1 )。通过这种方式确定哪些对象是"可达的",哪些对象是"不可达的".当GC确定一些对象为"不可达"时,GC就有责任回收这些内存空间。但是,为了保证 GC能够在不同平台实现的问题,Java规范对GC的很多行为都没有进行严格的规定。例如,对于采用什么类型的回收算法、什么时候进行回收等重要问题都没 有明确的规定。因此,不同的JVM的实现者往往有不同的实现算法。这也给Java程序员的开发带来行多不确定性。本文研究了几个与GC工作相关的问题,努 力减少这种不确定性给Java程序带来的负面影响。

增量式GC( Incremental GC )

GC在JVM中通常是由一个或一组进程来实现 的,它本身也和用户程序一样占用heap空间,运行时也占用CPU.当GC进程运行时,应用程序停止运行。因此,当GC运行时间较长时,用户能够感到 Java程序的停顿,另外一方面,如果GC运行时间太短,则可能对象回收率太低,这意味着还有很多应该回收的对象没有被回收,仍然占用大量内存。因此,在 设计GC的时候,就必须在停顿时间和回收率之间进行权衡。一个好的GC实现允许用户定义自己所需要的设置,例如有些内存有限有设备,对内存的使用量非常敏 感,希望GC能够准确的回收内存,它并不在意程序速度的放慢。另外一些实时网络游戏,就不能够允许程序有长时间的中断。增量式GC就是通过一定的回收算 法,把一个长时间的中断,划分为很多个小的中断,通过这种方式减少GC对用户程序的影响。虽然,增量式GC在整体性能上可能不如普通GC的效率高,但是它 能够减少程序的最长停顿时间。

Sun JDK提供的HotSpot JVM就能支持增量式GC.HotSpot JVM缺省GC方式为不使用增量GC,为了启动增量GC,我们必须在运行Java程序时增加-Xincgc的参数。HotSpot JVM增量式GC的实现是采用Train GC算法。它的基本想法就是,将堆中的所有对象按照创建和使用情况进行分组(分层),将使用频繁高和具有相关性的对象放在一队中,随着程序的运行,不断对 组进行调整。当GC运行时,它总是先回收最老的(最近很少访问的)的对象,如果整组都为可回收对象,GC将整组回收。这样,每次GC运行只回收一定比例的 不可达对象,保证程序的顺畅运行。

详解finalize函数

finalize 是位于Object类的一个方法,该方法的访问修饰符为protected,由于所有类为Object的子类,因此用户类很容易访问到这个方法。由 于,finalize函数没有自动实现链式调用,我们必须手动的实现,因此finalize函数的最后一个语句通常是super.finalize()。 通过这种方式,我们可以实现从下到上实现finalize的调用,即先释放自己的资源,然后再释放父类的资源。

根据Java语言规范,JVM保证调用finalize函数之前,这个对象是不可达的,但是JVM不保证这个函数一定会被调用。另外,规范还保证finalize函数最多运行一次。

很多Java初学者会认为这个方法类似与C++中 的析构函数,将很多对象、资源的释放都放在这一函数里面。其实,这不是一种很好的方式。原因有三,其一,GC为了能够支持finalize函数,要对覆盖 这个函数的对象作很多附加的工作。其二,在finalize运行完成之后,该对象可能变成可达的,GC还要再检查一次该对象是否是可达的。因此,使用 finalize会降低GC的运行性能。其三,由于GC调用finalize的时间是不确定的,因此通过这种方式释放资源也是不确定的。

通 常,finalize用于一些不容易控制、并且非常重要资源的释放,例如一些I/O的操作,数据的连接。这些资源的释放对整个应用程序是非常关键的。在这 种情况下,程序员应该以通过程序本身管理(包括释放)这些资源为主,以finalize函数释放资源方式为辅,形成一种双保险的管理机制,而不应该仅仅依 靠finalize来释放资源。

下面给出一个例子说明,finalize函数被调用以后,仍然可能是可达的,同时也可说明一个对象的finalize只可能运行一次。

class MyObject{

Test main; //记录Test对象,在finalize中时用于恢复可达性

public MyObject(Test t)

{

main=t; //保存Test 对象

}

protected void finalize()

{

main.ref=this;// 恢复本对象,让本对象可达

System.out.println("This is finalize");//用于测试finalize只运行一次

}

}

class Test {

MyObject ref;

public static void main(String[] args) {

Test test=new Test();

test.ref=new MyObject(test);

test.ref=null; //MyObject对象为不可达对象,finalize将被调用

System.gc();

if (test.ref!=null) System.out.println("My Object还活着");

}

}

运行结果:

This is finalize

MyObject还活着

此例子中,需要注意的是虽然MyObject对象在finalize中变成可达对象,但是下次回收时候,finalize却不再被调用,因为finalize函数最多只调用一次。

程序如何与GC进行交互

Java2 增强了内存管理功能, 增加了一个java.lang.ref包,其中定义了三种引用类。这三种引用类分别为SoftReference、WeakReference和 PhantomReference.通过使用这些引用类,程序员可以在一定程度与GC进行交互,以便改善GC的工作效率。这些引用类的引用强度介于可达对 象和不可达对象之间。

创 建一个引用对象也非常容易,例如如果你需要创建一个Soft Reference对象,那么首先创建一个对象,并采用普通引用方式(可达对象);然后再创建一个SoftReference引用该对象;最后将普通引用 设置为null.通过这种方式,这个对象就只有一个Soft Reference引用。同时,我们称这个对象为Soft Reference 对象。

Soft Reference的主要特点是据有较强的引用功能。只有当内存不够的时候,才进行回收这类内存,因此在内存足够的时候,它们通常不被回收。另外,这些引 用对象还能保证在Java抛出OutOfMemory 异常之前,被设置为null.它可以用于实现一些常用图片的缓存,实现Cache的功能,保证最大限度的使用内存而不引起OutOfMemory.以下给 出这种引用类型的使用伪代码;

//申请一个图像对象

Image image=new Image();//创建Image对象

//使用 image

//使用完了image,将它设置为soft 引用类型,并且释放强引用;

SoftReference sr=new SoftReference(image);

image=null;

//下次使用时

if (sr!=null) image=sr.get();

else{

//由于GC由于低内存,已释放image,因此需要重新装载;

image=new Image();

sr=new SoftReference(image);

}

Weak 引用对象与Soft引用对象的最大不同就在于:GC在进行回收时,需要通过算法检查是否回收Soft引用对象,而对于Weak引用对象,GC总是进行回 收。Weak引用对象更容易、更快被GC回收。虽然,GC在运行时一定回收Weak对象,但是复杂关系的Weak对象群常常需要好几次GC的运行才能完 成。Weak引用对象常常用于Map结构中,引用数据量较大的对象,一旦该对象的强引用为null时,GC能够快速地回收该对象空间。

Phantom 引用的用途较少,主要用于辅助finalize函数的使用。Phantom对象指一些对象,它们执行完了finalize函数,并为不可达对象,但是它们 还没有被GC回收。这种对象可以辅助finalize进行一些后期的回收工作,我们通过覆盖Reference的clear()方法,增强资源回收机制的 灵活性。

一些Java编码的建议

根据GC的工作原理,我们可以通过一些技巧和方式,让GC运行更加有效率,更加符合应用程序的要求。以下就是一些程序设计的几点建议。

1. 最基本的建议就是尽早释放无用对象的引用。大多数程序员在使用临时变量的时候,都是让引用变量在退出活动域(scope)后,自动设置为null.我们在 使用这种方式时候,必须特别注意一些复杂的对象图,例如数组,队列,树,图等,这些对象之间有相互引用关系较为复杂。对于这类对象,GC回收它们一般效率 较低。如果程序允许,尽早将不用的引用对象赋为null.这样可以加速GC的工作。

2.尽量少用finalize函数。finalize函数是Java提供给程序员一个释放对象或资源的机会。但是,它会加大GC的工作量,因此尽量少采用finalize方式回收资源。

3.如果需要使用经常使用的图片,可以使用soft应用类型。它可以尽可能将图片保存在内存中,供程序调用,而不引起OutOfMemory.

4.注意集合数据类型,包括数组,树,图,链表等数据结构,这些数据结构对GC来说,回收更为复杂。另外,注意一些全局的变量,以及一些静态变量。这些变量往往容易引起悬挂对象(dangling reference),造成内存浪费。

5.当程序有一定的等待时间,程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行。使用增量式GC可以缩短Java程序的暂停时间。

2008年9月1日星期一

引用:IBM development 中国:RUP与Scrum的对话

来自于 Rational Edge:本文介绍了被称为Scrum的灵活软件开发过程。作者阐述了软件开发团队在已有RUP环境中加入Scrum理念的技术。

illustration正 如你所知道的,RUP(Rational Unified Process,Rational 统一过程),是一种被广泛使用的软件过程框架。它可以很好地迎合你的软件开发过程的需要,还可以容纳其他技术。Scrum是一系列有趣的,用来包装灵活软 件项目的项目管理模式。本文介绍了Scrum的一些重要特性,并阐述了可以让你在已有RUP环境中加入Scrum理念的技术。我在工具条内提供了关于 Scrum和“灵活”的术语的词汇表,并且在下文中这些术语首次出现的地方用星号作了标记。

什么是Scrum?

Scrum 是一种灵活的软件管理过程,它可以帮助你驾驭迭代,递增的软件开发过程。Scrum于1995年由Advanced Development Methodologies,Inc提出,并在2001年“敏捷联盟(Agile Alliance)”形成后受到了更多欢迎。这个轻量的过程可以作为包装器,也就是说你可以把Scrum与其它灵活的过程框架组合起来,比如说RUP。

Scrum提供了一种经验方法,它使得团队成员能够独立地,集中地在创造性的环境下工作。它发现了软件工程的社会意义。Scrum一词来源于橄榄球运动,暗指这种情况:“在橄榄球比赛中,双方前锋站在一起紧密相连,当球在他们之间投掷时他们奋力争球。2

这 一过程是迅速,有适应性,自组织的,它代表了从顺序开发过程以来的重大变化。Scrum认为软件的开发不应使用和一般制造业相同的方法,也就是不应采用一 种反复的模式。这种重复使得输入和输出参数更加容易预测和描述,但这并不是当今软件工程的有益目标。现代软件工程的主要挑战包括上市时间,投资回报,以及 影响顾客的需要等。RUP和其他敏捷软件工程过程能够很好地迎接这些挑战。






Scrum如何工作

Scrum区别于其他开发过程之处是什么?假设你是一个Scrum项目的初次观察者,那么最显而易见的不同将是每天的短会,通常在每天的同一时间在同一个房间内举行。这个会议也叫Scrum,在会议中每个团队成员仅就以下三点发言:

  • 自上次Scrum会议后你做了什么?
  • 从现在到下次Scrum会议的时间里你准备做什么?
  • 你在工作中遇到了哪些困难?







Scrum团队的组成

由于一个Scrum团队最多由7人组成,会议应当不超过15分钟。Scrum管理者*主持会议,并且对整个项目的成败负责。他倾听每个成员的发言并设法解 决会议中提到的各种障碍。Scrum管理者在会上对障碍提出即时的解决方案或指导,使团队不断向着目标前进。Scrum会议不同于项目会议,对团队来说, 它起到了快速简报的作用。如果问题得不到解决,团队成员应向Scrum管理者或大项目成员提出质疑。

只有团队成员可以在Scrum会议上发言,但是允许有旁听者。对于人数多于7人的项目团队,Scrum建议与其扩大团队规模不如将团队分组。分组可依据功 能,结构主体,或者应用,包括子应用等进行。分组后各个子团队就可以并行工作了,而且Scrum管理者可以通过Scrum会议对各个子团队的工作进行同 步。Scrum甚至可以兼顾在其他地方工作的团队成员。

Scrum团队不止是一个程序员队伍,它由各种背景下的不同角色组合而成,包括商业分析者,设计师,程序员和测试者等等。更多时候,成员可以身兼多职;正确的组合决定了团队的能力和效率。




项目规划

Scrum的迭代过程被称为“疾跑”,时间为30天。在RUP中,迭代过程通常在2至6周之间,每次“疾跑”都以获得可执行可测试的代码为结束。

产品拥有者持有产品订单,他控制并区分功能的开发次序,但是工作量的评估是由Scrum团队来完成的。产品风险的所有承担者,包括Scrum团队和产品所有者,共同检视订单,然后根据优先级次序决定先开发哪一功能。除去优先级,RUP的迭代规划过程也是基于风险的。

现在团队定义的“疾跑”目标已经成为了进展控制的指导。“疾跑”过程一旦开始,团队全部与外界的交流都必须经由Scrum管理者进行。Scrum管理者务必保证团队能够专心于既定目标而不受外界干扰。

Scrum团队持有自己的“疾跑”订单,上面记录了更多关于待实现目标的具体任务的细节。在团队对“疾跑”的作用有更多了解以后,团队成员就可以调整原始的产品评估,并将“疾跑”过程中获得的信息加入到产品订单中。这些做法对Scrum进度回溯都是有益的。

Scrum 团队由每天的Scrum会议,每月的“疾跑”计划和“疾跑”审查会议紧密相连,鉴于此,整个组织必然存在一种纵向的透明度。这就使得组织上的问题和挑战清 晰明显。由于团队成员都亲自观察整个项目,交流也就变得简短,迅速和有效。团队是自组织的,着眼于“疾跑”的目标,这样就最大限度发挥了每一个团队成员的 作用。Scrum管理者充当一个问题和交流的“票据交换所”,而不是一个控制整个团队的老板。

“疾跑”审查会议持续半天。在会上,团队向项目的风险承担者展示完成的功能模块。团队按照既定的“疾跑”目标来演示完成的内容。

订 单,“疾跑”计划和回顾,管理承诺,每日Scrum会议,进度回溯,以及其他Scrum技术都是基于主要用于软件项目管理的进程模式的。这些模式在过去的 大小项目和不同商业领域中都获得了成功。如果你打算采用Scrum进程模式并与RUP结合起来,下面一部分将向你解释更多要点。

Scrum与RUP的结合
现在使用RUP的软件项目可以很容易地从Scrum原则中获益,即使已经开始的项目也是如此。首先需要调研的是RUP过程模型,Scrum如何影响RUP 最好的实践活动,四个阶段(初始,细化,构建和产品化),以及RUP的九个原则。RUP各阶段和原则之间的关系如图一所示。

Figure 1: Each of the disciplines in RUP, listed at left, are correlated to the four project phases. While most disciplines are involved in each phase, the chart shows varying degrees of involvment by phase.

图一:左边列出的是RUP的各个原则,与项目的四个阶段相关联。

尽管大多数原则与各个阶段都相关,上图表示出了在各个阶段中原则的不同的相关度。








RUP:软件开发的最佳实践

让我们从RUP过程框架的六个最佳实践开始吧。

迭代开发
Scrum项目强制执行迭代的开发过程,以30天为一个合适的开发时长。尽管在对“迭代”的定义上RUP更为灵活,30天的限定仍然经常被RUP使用,并 且已被认为是一个有效的长度。工作总是按照30天的“疾跑”排列下来的,而不是定义一个RUP项目的一次迭代的长度。

管理需求
整个团队都可以管理需求,但是需求的控制权是由产品所有者掌握的。在RUP中,需求工程师对需求负责,但在Scrum中,这一责任属于产品所有者,通常表现为商业风险承担者。在RUP中,需求管理需要通过协作完成。

使用模块构建
体系架构是由公司的开发策略和指导决定的,或者由团队自身提出。RUP倡导的以体系架构为核心的方法导致了对各种应用结构的仔细考虑和选择。在Scrum 中,这一定义较松,并决定于发布给团队的指导和标准。很多灵活的软件项目使用现代软件工具和编程语言,比如,模块构建占主导的技术。

可视化模型
RUP提倡可视化模型。甚至RUP框架本身就是可视的,有很多UML的外壳。和RUP一样,Scrum虽然不要求开发团队建构某一特定的可视化组件,但是 委派Scrum团队完成这项工作。关于使用哪种组件,使用到何种程度和质量,取决于既定的“疾跑”目标。由于UML在工业界的广泛认可,很多工业专家都使 用可视化技术,并且在RUP和Scrum中,可视化模型的使用都是很普遍的。

持续的保证质量
迭代,递增的开发已经为每个软件项目的质量和进展提供了很好的度量手段。Scrum团队完全集中于他们的“疾跑”目标,在30天的周期内必须展示工作成 果。在这种方式下,功能是用一种重复的方式测试,度量和展示的。但是在大型组织中,质量控制是从各种角度观察的,而且需要更高质量管理的程序。Scrum 可以从RUP的质量保证计划中获益,该计划由项目管理者控制,是成功的迭代开发的重要组成部分。

管理变更
在任何现代软件开发项目中都会出现变更(变化应该是受到欢迎的!),但是Scrum管理者不会为了引入发生在达成共识的目标上的变更而打断一个“疾跑”周 期。他们抓住需要的变化,在本次“疾跑”结束时提出它们作为下一系列目标的一部分。这对Scrum的“疾跑”来说是最关键的。团队与外界隔离开工作,不予 许变化影响当前的“疾跑”。否则,迭代开发的领域(“疾跑”的目标)的不断调整将会导致团队失去中心,不能很好完成疾跑伊始确定的功能目标。







Scrum和RUP原则

为了在Scrum的上下文环境中讨论RUP的九个原则,我把它们分成了两类。第一类是关于“雨伞活动”的,它们间接的对系统开发或组织有益,比如“环境 ”,“项目管理”,和“配置和变更管理”。第二类是材料活动,包括“商业模型”,“需求”,“分析和设计”,“实现”,“测试”,和“部署”。







雨伞活动

雨伞活动是指Scrum团队上层的管理机制,应由外部的风险承担者处理,比如,项目管理。除了扮演项目经理角色的Scrum管理者,团队成员应该专注于对实现Scrum目标必要的活动,而不应陷于管理工作之中。

RUP 的“项目管理”原则,特别是项目规划,为软件开发团队提供了体验Scrum精髓的绝好机会。从Scrum管理者开始项目起,对团队来说RUP项目就如同一 个Scrum项目。内部的组织上,管理上,语言的,非语言的交流都应被Scrum团队消除,而应该交给Scrum管理者(在RUP中称为项目经理)处理。 一个RUP项目规划对如下工作来说是最理想的:

  • 概括项目中应用的Scrum规则;
  • 确定项目阶段标志;
  • 描述正在进行中的“疾跑”目标;
  • 呈现风险;
  • 描述评估和评估使用的技术。

在Scrum中,Scrum管理者充当了管理和Scrum团队之间的协调人。管理者管理项目的使用域和功能,同内部和外部进行交流,但是并不参与更具体活动级别的管理。这些职责和义务的不同需要在项目规划中定义。另外,Scrum团队成员的角色通常是设计师。

在 一个Scrum管理的项目中,RUP环境原则下的开发工程是不断进行调整和修改的。开发工程把强制性的和可选择性的组件罗列出来作为成功的指路标。但是由 于Scrum团队是作为一个独立单位工作的,团队开发的组件应该都被称为“可选择性的”以反映团队在Scrum环境中的控制作用。另一方面,你也可以从开 发工程去掉所有和Scrum团队相关的组件。RUP中的一般开发工程模板都可以完成这种需要。经过一段时间,随着Scrum团队在“疾跑”过程中规律性地 获得有用的文档,真实的开发工程也就成形了。







收集素材

余下的六个RUP原则对软件工程有更直接的影响,它们为Scrum团队提供了一系列可选择的组件和活动。RUP过程框架可以作为指导,提供可供考虑的有用 步骤和组件。在特殊情况下,团队可以对每一条原则做出变更和修改,只要这种变更和修改有助于实现“疾跑”的目标。新手和经验不丰富的软件工程师可以把 RUP作为交流的指导和一个结构化的环境。

RUP项目的四个阶段体现了软件工程过程中的不同项目样式。尽管Scrum原则适用于项目的整个 生存周期,消耗大部分项目资源的细化和构建阶段最宜使用Scrum方法。与初始阶段相比,在细化和构建阶段团队的工作更加独立和指向切实目标。Scrum 的“疾跑”规划,审查,和每日会议在这两个阶段是非有用的手段。

如果在初始阶段,甚至在项目开始之前,项目团队中有各 种商业分析人员,那么在初始阶段Scrum也是很有益的。为了更好地保证项目的成功部署,产品化阶段可以单独作为一个项目拥有Scrum 部署团队人员。对整个RUP项目来说,Scrum项目管理技术可能不是最理想的,但是它们可以在项目的不同阶段以不同形式被采用。项目规划反映了这些决 定,而且阐述了在某些阶段不使用Scrum技术的原因。







使RUP适用于Scrum的工具

与IBM的 Rational Process Workbench 共用时,RUP作为进程框架体现了强大优势,而且是完全用户化的。Rational Process Workbench 产品包含了RUP模型设计,RUP组织和RUP构建。

RUP 模型设计是修改和管理核心模型的可视化工具。过程工程师可以对现有RUP框架进行修改以引入Scrum概念。使用RUP模型设计工具可以很容易的管理 Scrum的角色和组件之间的依赖性。过程工程师可以使用RUP组织工具描述修改后的Scrum工作流。这样做有助于项目团队成员更好的理解Scrum过 程。为了共享组织内的过程变化,过程工程师通过RUP构建工具发布进程。该工具将过程,组件和描述文档集成起来,并创建一个网络导航。

结束语
Scrum和RUP可以通过很多方式相互加强。RUP过程框架可以迎合你的项目需要;RUP开发工程和软件开发规划反映了你对使用Scrum技术的决定。 根据我的经验,Scrum团队会非常规律地进行开发活动,并完成最有效的开发工程。输出的组件可以作为组织内其它项目的输入文档。

概括来说,RUP通过引入结构化并已经证实的进程框架满足了组织上的需求,而且Scrum模式可 以为项目加入更多动态特性。比如说,可以很容易地向你当前的或未来的RUP项目中加入Scrum管理模式。至于软件工程的社会效应方面,Scrum已被证 实的过程模式可以即时地方便需求管理,变化控制以及项目管理提供。在RUP组件对开发过程和项目文档的指导下,Scrum团队成员,特别是新手或是经验不 足者,可以避免陷入软件开发的陷阱。







参考

您可以参阅本文在 developerWorks 全球站点上的 英文原文

Agile Alliance (2004) http://www.agilealliance.org/

Ken Schwaber 和 Mike Beedle, Agile Software Development with SCRUM Prentice Hall, 2002.

Scrum (2004). See http://www.controlchaos.com/ The diagram at http://www.controlchaos.com/about/ 的图表可以帮助你更直观的理解Scrum的项目迭代过程。

注意
1 参看Scrum的网页
http://www.controlchaos.com/

2 依据Merriam-Webster's Collegiate Dictionary, 11th Edition, Merriam-Webster, 2003。



关于作者


JJochen (Joe) Krebs (jkrebs@us.ibm.com) 是IBM软件组Rational软件系列的高级IT专家。他在金融部门工作,对Rational的产品和服务的成功激活负责。在加入IBM Rational之前,他是项目管理,需求管理,软件工程过程,以及Smalltalk和jave下的面向对象技术的指导人员和高级顾问。他在Open University的商业和工业计算专业取得了硕士学位。

2008年8月31日星期日

Talking about failure

A main can fail many times,
but he is not a failure
until he begins to blame somebody else.

2008年8月27日星期三

-> Ajax在jsp java中的案例-1

学习Ajax技术请先不要使用ajax的框架,因为如DWR等优秀的框架虽然有很多的优势但其封装了ajax技术的细节,对初学者来说先了解其原理,对以后使用框架开发有相当大的帮助。

  1. 先看图片了解一下Ajax的运行机理。

    (点击图片看大图)

  2. 看看下面案例:(摘自Ajaxa hack):
    调用过程是这样的:
    首先由:ajax_hack3.html提交请求到后台的ActionServlet.java,然后由其将处理结果异步返回给请求页面ajax_hack3.html,此页面根据返回状态回调事先写好的回调函数将异步接受到的结果(可以接受xml或者txt两种格式的返回结果数据)写入到此页面适当的位置。
    (当然此html页面在以后的应用中也可换成 jsp,提交方式也可改为js的事件焦点改变触发)

  3. 分析代码:(关于j2ee的运行环境和serlet的配置在这里就不在罗嗦了,google一下了)

    ajax_hack3.html —— (发送,接受数据的页面)
    hack3.js ——(Ajax支持的javascript)
    ActionServlet.java —— (后台serlvet)

    ajax_hack3.html
    [因不能显示html代码,故右键查看源码,可以看到]
    --------------------------------------------------------------
    附上:form 的 action="javascript:void%200"
    onsubmit= "sendData();return false"

    First name:


    Last name:


    Gender:


    Country of origin:






    --------------------------------------------------------------

    hack3.js
    --------------------------------------------------------------
    var request;
    var queryString; //将要被post的数据;

    function sendData(){
    setQueryString();
    var url="http://localhost:8080/Ajax/actionSerlvet";//处理请求的serlvet映射地址
    httpRequest("POST",url,true);
    }

    //,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,事件处理函数
    function handleResponse(){
    if(request.readyState == 1){
    //,,,,,,,,,,,,,,,,,,,,readyState 五状态:(0)未初始化 (1)载入 (2)载入完成 (3)交互 (4)完成
    //,,,,,,,,,,,,,,,,,,,,, 当readyState是1,显示loading gif动画,可以从网上下载一个合适的gif
    var loadgif = "\loading ... ...";
    loadingDiv(loadgif,document.getElementById("loading"));
    }
    if(request.readyState == 2){
    var loadgif = ".";
    loadingDiv(loadgif,document.getElementById("loading"));
    }
    if(request.readyState == 4){
    //,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, actionServlet处理成功,则返回200!
    if(request.status == 200){
    alert(request.responseText);
    var doc = request.responseXML;
    var info = getDocInfo(doc);
    //,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,显示结果到div docDisplay
    stylizeDiv(info,document.getElementById("docDisplay"));
    } else {
    alert("A problem occurred with communicating between the XMLHttpRequest object and the server program.");
    }

    }//end outer if
    }


    /* 初始化request对象 */
    function initReq(reqType,url,bool){
    /*指定回调的事件处理函数来处理HTTP的回应 */
    request.onreadystatechange=handleResponse;
    request.open(reqType,url,bool);
    request.setRequestHeader("Content-Type",
    "application/x-www-form-urlencoded; charset=UTF-8");
    /* 网景,火狐*/
    //request.overrideMimeType("text/XML");
    request.send(queryString);
    }

    /* 根据浏览器到不同构造一个HttpRequest对象.
    参数:
    reqType: HTTP 请求类型可以是 GET or POST.
    url: 服务器后台处理地址.
    asynch: 是否是异步. */
    function httpRequest(reqType,url,asynch){
    //Mozilla-浏览器
    if(window.XMLHttpRequest){
    request = new XMLHttpRequest();
    } else if (window.ActiveXObject){
    // IE 浏览器
    request=new ActiveXObject("Msxml2.XMLHTTP");
    if (! request){
    request=new ActiveXObject("Microsoft.XMLHTTP");
    }
    }


    if(request){
    initReq(reqType,url,asynch);
    } else {
    alert("Your browser does not permit the use of all "+
    "of this application's features!");}
    }

    function setQueryString(){
    queryString="";
    var frm = document.forms[0];
    var numberElements = frm.elements.length;
    for(var i = 0; i <>
    if(i <>
    queryString += frm.elements[i].name+"="+
    encodeURIComponent(frm.elements[i].value)+"&";
    } else {
    queryString += frm.elements[i].name+"="+
    encodeURIComponent(frm.elements[i].value);
    }

    }
    }

    function stylizeDiv(bdyTxt,div){
    //reset DIV content
    div.innerHTML="";
    div.style.backgroundColor="yellow";
    div.innerHTML=bdyTxt;
    }
    function loadingDiv(bdyTxt,div){
    div.innerHTML="";
    div.style.backgroundColor="white";
    div.innerHTML=bdyTxt;
    }
    function getDocInfo(doc){
    var root = doc.documentElement;
    var info = "

    Document root element name:

    "+ root.nodeName;
    var nds;
    if(root.hasChildNodes()) {
    nds=root.childNodes;
    info+= "

    Root node's child node names/values:

    ";
    for (var i = 0; i <>
    info+= nds[i].nodeName;
    if(nds[i].hasChildNodes()){
    info+= " : \""+nds[i].firstChild.nodeValue+"\"
    ";

    } else {
    info+= " : Empty
    ";

    }
    }
    }
    return info;
    }

    --------------------------------------------------------------

    ActionServlet.java
    【记得导入apache的commons betwixt包,可以在apache.org网站下载】

    --------------------------------------------------------------
    package com.ata;

    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.Iterator;
    import java.util.Map;
    import org.apache.commons.betwixt.XMLUtils;

    public class ActionServlet extends HttpServlet{

    protected void doGet
    (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
    doPost(httpServletRequest, httpServletResponse);
    }

    protected void doPost(HttpServletRequest httpServletRequest,
    HttpServletResponse httpServletResponse) throws ServletException,
    IOException {
    Map reqMap = httpServletRequest.getParameterMap();
    String val=null;
    String tag = null;
    StringBuffer body = new StringBuffer("\n");
    boolean wellFormed = true;
    Map.Entry me = null;

    //,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,为了演示loading效果,在此sleep一秒钟。
    try {
    System.out.println("sleep 1s");
    Thread.sleep(6000);

    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    //,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,遍历j拿到传递的值并封装成xml格式。
    for(Iterator iter= reqMap.entrySet().iterator();iter.hasNext();) {
    me=(Map.Entry) iter.next();
    val= ((String[])me.getValue())[0];
    tag = (String) me.getKey();
    //,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,apache的XMLUtils工具类,检查标签

    if (! XMLUtils.isWellFormedXMLName(tag)){
    wellFormed=false; break;
    }
    body.append("<").append(tag).append(">").
    append(XMLUtils.escapeBodyValue(val)).append("
    append(">\n");
    }
    if(wellFormed) {
    body.append("");
    //,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,如果格式没问题,就将封装好的xml格式结果返回
    sendXML(httpServletResponse,body.toString());
    } else {
    sendXML(httpServletResponse,"");
    }
    }

    private void sendXML(HttpServletResponse response,String body) throws
    IOException{
    response.setContentType("text/xml; charset=UTF-8");
    response.setHeader("Cache-Control", "no-cache");
    String content = ""+body;
    System.out.println(content);
    response.getWriter().write(content);

    }
    }

    --------------------------------------------------------------



2008年8月26日星期二

-> JAVA mail 发送电邮 案例(Apache Commons Mail)

apache 提供了一个非常方便的Java Mail接口。

仅仅从其网站上下载commons-email-jar-1.1.jar包,即可轻松使用java编写mail程序。


  1. 到官方下载页面下载commons-email-jar-1.1.jar包,并导入到Eclipse相应的工程里面;

  2. 建立java类:SimpleEmail .java 并导入相关的类;

  3. 写方法:sentByGmai() 此方法是通过gmail信箱向163信箱发送电邮的案例;

    public static void sentByGmail() throws Exception{
    SimpleEmail email = new SimpleEmail();
    email.setDebug(true); //,,,,,,,,,,,,,,,,,,,,,,,,,,, 设置调试信息,对排查错误非常有帮助。
    //,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, 通过Gmail Server 发送邮件
    email.setHostName("smtp.gmail.com"); //设定smtp服务器
    email.setSSL(Boolean.TRUE); //,,,,,,,,设定是否使用SSL
    email.setSslSmtpPort("465"); //,,,,,,,,,设定SSL端口
    //,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,设定smtp服务器的认证资料信息
    email.setAuthentication("yourMailAdd@gmail.com", "yourPassword");

    email.addTo("mincatjava@163.com
    ","Mincat"); //,,,,,,,,,设定收信地址和名字
    email.setCharset("UTF-8");//,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,设定内容的语言集
    email.setFrom("liyang.javadoor@gmail.com");//,,,,,,设定发件人的地址
    email.setSubject("Hello,用java发来的问候!");//,,,设定电邮的主题
    email.setMsg("中国\n 你好!");//,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,设定邮件内容

    email.send();//,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,发送邮件
    }

  4. 将此方法在main方法中运行即可。

2008年8月20日星期三

-> 从现在开始!

开始了,
就从这里开始吧!