tory320

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  10 随笔 :: 0 文章 :: 1 评论 :: 0 Trackbacks

2006年12月18日 #

Design Principle
Identify the aspects of your application that vary and separate them from what stays the same.
Here's another way to think about this principle: take the parts that vary and encapsulate them, so that later you can alter or extends the parts that vary without affecting those that don't.
As simple as this concept is, it forms the basis for almost every design pattern. All patterns provide a way to let some part of a system vary independently of all other parts.

Each set of class will hold all the implementations of their respective behavior. For instance, we might have one clss that implements quarking, another implements squaking, and another that implements silence.

To separate thest behaviors from the Duck class, we'll pull both methods out of the duck class and create a new set of class to represent each behavior.

This is in contrast to the way we were doing things before, where a behavior either came from a concrete implementation in the suprerclass Duck, or by providing a specialized implementation in the sub class itself. In both cases we were relying on an implementation. We were locked into using that specific implemetation and there was no room for changing out the behavior.

And the same is true for the duck's flying behavior.

Okay, now that we've done the deep dive on the duck simulator design, it's time to come back up for air and take a look at the big picture.

Below is the entire reworked class structure. We have everything you'd expect: ducks extending Duck. fly behavior implementing FlyBehavior and quack behavior implementing QuackBehavior.

Notice also that we've started to describe things a little differntly. Instead of thinking of the duck behaviors as a set of behaviors, we'll start thinking of them ad a family of algorithms. Think about it: in the SimUDuck design, the algorithms represent things a duck would do , but we could just as easily use the same techniques for a set of classes that implement the ways to compute state sales tax by different states.

posted @ 2008-03-07 17:58 tory 阅读(123) | 评论 (0)编辑 收藏

/**
 * //FileOperate.java
 * 文件的各种操作
 * 杨彩 http://blog.sina.com.cn/m/yangcai
 * 文件操作 1.0
 */
 
//package common;
 
import java.io.*;
 
public class FileOperate
{
 static boolean exitnow=false;
 static String aa,bb;
  public FileOperate() {
  }
 
  /**
   * 新建目录
   */
  public void newFolder(String folderPath) {
    try
    {
      String filePath = folderPath;
      filePath = filePath.toString();
      File myFilePath = new File(filePath);
      if(!myFilePath.exists())
      {
        myFilePath.mkdir();
      }
      System.out.println("新建目录操作 成功执行");
    }
    catch(Exception e)
    {
      System.out.println("新建目录操作出错");
      e.printStackTrace();
    }
  }
 
  /**
   * 新建文件
   */
  public void newFile(String filePathAndName, String fileContent)
  {
 
    try
    {
      String filePath = filePathAndName;
      filePath = filePath.toString();
      File myFilePath = new File(filePath);
      if (!myFilePath.exists())
      {
        myFilePath.createNewFile();
      }
      FileWriter resultFile = new FileWriter(myFilePath);
      PrintWriter myFile = new PrintWriter(resultFile);
      String strContent = fileContent;
      myFile.println(strContent);
      resultFile.close();
      System.out.println("新建文件操作 成功执行");
    }
    catch (Exception e) {
      System.out.println("新建目录操作出错");
      e.printStackTrace();
 
    }
 
  }
 
  /**
   * 删除文件
   */
  public void delFile(String filePathAndName) {
    try {
      String filePath = filePathAndName;
      filePath = filePath.toString();
      File myDelFile = new File(filePath);
      myDelFile.delete();
      System.out.println("删除文件操作 成功执行");
    }
    catch (Exception e) {
      System.out.println("删除文件操作出错");
      e.printStackTrace();
 
    }
 
  }
 
  /**
   * 删除文件夹
   */
  public void delFolder(String folderPath)
  {
    try
    {
      delAllFile(folderPath); //删除完里面所有内容
      String filePath = folderPath;
      filePath = filePath.toString();
      File myFilePath = new File(filePath);
      myFilePath.delete(); //删除空文件夹
      System.out.println("删除文件夹操作 成功执行");
    }
    catch (Exception e)
    {
      System.out.println("删除文件夹操作出错");
      e.printStackTrace();
 
    }
 
  }
 
  /**
   * 删除文件夹里面的所有文件
   * @param path String 文件夹路径 如 c:/fqf
   */
  public void delAllFile(String path)
  {
    File file = new File(path);
    if(!file.exists())
    {
      return;
    }
    if(!file.isDirectory())
    {
      return;
    }
    String[] tempList = file.list();
    File temp = null;
    for (int i = 0; i < tempList.length; i++)
    {
      if(path.endsWith(File.separator))
      {
        temp = new File(path + tempList[i]);
      }
      else
      {
        temp = new File(path + File.separator + tempList[i]);
      }
      if (temp.isFile())
      {
        temp.delete();
      }
      if (temp.isDirectory())
      {
        delAllFile(path+"/"+ tempList[i]);//先删除文件夹里面的文件
        delFolder(path+"/"+ tempList[i]);//再删除空文件夹
      }
    }
          System.out.println("删除文件操作 成功执行"); 
  }
 
  /**
   * 复制单个文件
   * @param oldPath String 原文件路径 如:c:/fqf.txt
   * @param newPath String 复制后路径 如:f:/fqf.txt
   */
  public void copyFile(String oldPath, String newPath) {
    try {
      int bytesum = 0;
      int byteread = 0;
      File oldfile = new File(oldPath);
      if (oldfile.exists())
      { //文件存在时
        InputStream inStream = new FileInputStream(oldPath); //读入原文件
        FileOutputStream fs = new FileOutputStream(newPath);
        byte[] buffer = new byte[1444];
        int length;
        while ( (byteread = inStream.read(buffer)) != -1) {
          bytesum += byteread; //字节数 文件大小
          System.out.println(bytesum);
          fs.write(buffer, 0, byteread);
        }
        inStream.close();
      }
            System.out.println("删除文件夹操作 成功执行"); 
    }
    catch (Exception e) {
      System.out.println("复制单个文件操作出错");
      e.printStackTrace();
 
    }
 
  }
 
  /**
   * 复制整个文件夹内容
   * @param oldPath String 原文件路径 如:c:/fqf
   * @param newPath String 复制后路径 如:f:/fqf/ff
   */
  public void copyFolder(String oldPath, String newPath) {
 
    try
    {
      (new File(newPath)).mkdirs(); //如果文件夹不存在 则建立新文件夹
      File a=new File(oldPath);
      String[] file=a.list();
      File temp=null;
      for (int i = 0; i < file.length; i++)
      {
        if(oldPath.endsWith(File.separator))
        {
          temp=new File(oldPath+file[i]);
        }
        else{
          temp=new File(oldPath+File.separator+file[i]);
        }
 
        if(temp.isFile())
        {
          FileInputStream input = new FileInputStream(temp);
          FileOutputStream output = new FileOutputStream(newPath + "/" +
              (temp.getName()).toString());
          byte[] b = new byte[1024 * 5];
          int len;
          while ( (len = input.read(b)) != -1)
          {
            output.write(b, 0, len);
          }
          output.flush();
          output.close();
          input.close();
        }
        if(temp.isDirectory())
        {//如果是子文件夹
          copyFolder(oldPath+"/"+file[i],newPath+"/"+file[i]);
        }
      }
            System.out.println("复制文件夹操作 成功执行"); 
    }
    catch (Exception e) {
      System.out.println("复制整个文件夹内容操作出错");
      e.printStackTrace();
 
    }
 
  }
 
  /**
   * 移动文件到指定目录
   * @param oldPath String 如:c:/fqf.txt
   * @param newPath String 如:d:/fqf.txt
   */
  public void moveFile(String oldPath, String newPath) {
    copyFile(oldPath, newPath);
    delFile(oldPath);
 
  }
 
  /**
   * 移动文件到指定目录
   * @param oldPath String 如:c:/fqf.txt
   * @param newPath String 如:d:/fqf.txt
   */
  public void moveFolder(String oldPath, String newPath) {
    copyFolder(oldPath, newPath);
    delFolder(oldPath);
 
  }
 
  public static void main(String args[])
  {
   System.out.println("使用此功能请按[1]  功能一:新建目录");
   System.out.println("使用此功能请按[2]  功能二:新建文件");
   System.out.println("使用此功能请按[3]  功能三:删除文件");
   System.out.println("使用此功能请按[4]  功能四:删除文件夹");
   System.out.println("使用此功能请按[5]  功能五:删除文件夹里面的所有文件");
   System.out.println("使用此功能请按[6]  功能六:复制文件");
   System.out.println("使用此功能请按[7]  功能七:复制文件夹的所有内容");
   System.out.println("使用此功能请按[8]  功能八:移动文件到指定目录");
   System.out.println("使用此功能请按[9]  功能九:移动文件夹到指定目录");
   System.out.println("使用此功能请按[10] 退出程序");
   
 while(!exitnow)
 {
    FileOperate fo=new FileOperate();
    try
    {
    BufferedReader Bin=new BufferedReader(new InputStreamReader(System.in));
    String a=Bin.readLine();
    int b=Integer.parseInt(a);
    
    switch(b)
    {
     case 1:System.out.println("你选择了功能一  请输入目录名");  
        aa=Bin.readLine();
        fo.newFolder(aa);
        break;
     case 2:System.out.println("你选择了功能二  请输入文件名");  
        aa=Bin.readLine();
        System.out.println("请输入在"+aa+"中的内容");
        bb=Bin.readLine();
        fo.newFile(aa,bb);
        break;
     case 3:System.out.println("你选择了功能三  请输入文件名");  
        aa=Bin.readLine();
        fo.delFile(aa);
        break;
     case 4:System.out.println("你选择了功能四  请输入文件名");  
        aa=Bin.readLine();
        fo.delFolder(aa);
        break;
     case 5:System.out.println("你选择了功能五  请输入文件名");  
        aa=Bin.readLine();
        fo.delAllFile(aa);
        break;  
     case 6:System.out.println("你选择了功能六  请输入文件名");  
        aa=Bin.readLine();
        System.out.println("请输入目标文件名"); 
        bb=Bin.readLine();
        fo.copyFile(aa,bb);
        break;
     case 7:System.out.println("你选择了功能七  请输入源文件名");  
        aa=Bin.readLine();
        System.out.println("请输入目标文件名"); 
        bb=Bin.readLine();
        fo.copyFolder(aa,bb);
        break;       
     case 8:System.out.println("你选择了功能八  请输入源文件名");  
        aa=Bin.readLine();
        System.out.println("请输入目标文件名"); 
        bb=Bin.readLine();
        fo.moveFile(aa,bb);
        break;
       case 9:System.out.println("你选择了功能九  请输入源文件名");  
        aa=Bin.readLine();
        System.out.println("请输入目标文件名"); 
        bb=Bin.readLine();
        fo.moveFolder(aa,bb);
        break;       
     case 10:exitnow=true;
         System.out.println("程序结束,请退出");
        break;
     default:System.out.println("输入错误.请输入1-10之间的数");               
     }
    
    
    System.out.println("请重新选择功能");
    
    
    }
    catch(Exception e)
    {
    System.out.println("输入错误字符或程序出错");
    }
    
 }   
 }
}
posted @ 2007-01-30 13:03 tory 阅读(169) | 评论 (0)编辑 收藏

本文是在参阅了http://ivanl.javaeye.com/blog/24739基础上完成的
在看JPetStore的代码时,发现它的分页处理主要是通过返回PaginatedList对象来完成的。如:在CatalogService类中
public PaginatedList getProductListByCategory(String categoryId) 
    
return productDao.getProductListByCategory(categoryId); 
  }
 

分页是操作数据库型系统常遇到的问题。分页实现方法很多,但效率的差异就很大了。iBatis是通过什么方式来实现这个分页的了。查看它的实现部分:
 
返回的PaginatedList实际上是个接口,实现这个接口的是PaginatedDataList类的对象,查看PaginatedDataList类发现,每次翻页的时候最后都会调用下面这段函数
private List getList(int idx, int localPageSize) throws SQLException 
    
return sqlMapExecutor.queryForList(statementName, parameterObject, (idx) * pageSize, localPageSize); 
  }
 
由于
public interface SqlMapClient extends SqlMapExecutor, SqlMapTransactionManager {……} 

所以实际的调用次序如下:
SqlMapClientImpl.queryForPaginatedList->SqlMapSessionImpl.queryForPaginatedList 
->SqlMapExecutorDelegate.queryForPaginatedList->GeneralStatement.executeQueryForList 
->GeneralStatment.executeQueryWithCallback->GeneralStatment.executeQueryWithCallback 
->SqlExecutor.executeQuery->SqlExecutor.handleMultipleResults()->SqlExecutor.executeQuery-> handleResults 
分页处理的函数如下
private void handleResults(RequestScope request, ResultSet rs, int skipResults, int maxResults, RowHandlerCallback callback) throws SQLException 
    
try 
      request.setResultSet(rs); 
      ResultMap resultMap 
= request.getResultMap(); 
      
if (resultMap != null
        
// Skip Results 
        if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) 
          
if (skipResults > 0
            rs.absolute(skipResults); 
          }
 
        }
 else 
          
for (int i = 0; i < skipResults; i++
            
if (!rs.next()) 
              
return
            }
 
          }
 
        }
 
  
        
// Get Results 
        int resultsFetched = 0
        
while ((maxResults == SqlExecutor.NO_MAXIMUM_RESULTS || resultsFetched < maxResults) && rs.next()) 
          Object[] columnValues 
= resultMap.resolveSubMap(request, rs).getResults(request, rs); 
          callback.handleResultObject(request, columnValues, rs); 
          resultsFetched
++
        }
 
      }
 
    }
 finally 
      request.setResultSet(
null); 
    }
 
  }
 

由此可见,iBatis的分页主要依赖于jdbcdriver的如何实现以及是否支持rs.absolute(skipResults)。它并不是一个好的分页方式。它先要取出所有的符合条件的记录存入ResultSet对象,然后用absolute方法进行定位,来实现分页。当记录数较大(比如十万条)时,整体的查询速度将会变得很慢。
所以分页还是要考虑采用直接操作sql语句来完成。当然小批量的可以采用iBatis的分页模式。一般分页的sql语句与数据库的具体实现有关
mysql: 
select * from A limit startRow,endRow 
oracle: 
select b.* from (select a.*,rownum as linenum from (select * from A) a where rownum <= endRow) b where linenum >= startRow 

Hibernate的Oracle分页采用的就是是拼凑RowNum的Sql语句来完成的。参考代码如下: 
 
        public String createOraclePagingSql(String sql, int pageIndex, int pageSize)
            
int m = pageIndex * pageSize; 
            
int n = m + pageSize; 
            
return "select * from ( select row_.*, rownum rownum_ from ( " + sql 
                    
+ " ) row_ where rownum <= " + n  
                    
+ ") where rownum_ > " + m; 
        }
 
综上,小批量(<2w)可以采用ibatis自带的分页类,大批量的还是直接操纵sql,当然也可以将这些sql自己进行封装,或在包中封装都可以。包封装的示例代码如下:
一个封装了分页功能的Oracle Package
create or replace package body FMW_FY_HELPER is
PROCEDURE GET_DATA(pi_sql in varchar,pi_whichpage in integer,pi_rownum in integer,
po_cur_data out cur_DATA,po_allrownum out 
integer,pio_succeed in out integer)
as 
v_cur_data cur_DATA;
v_cur_temp cur_TEMP;
v_temp 
integer;
v_sql 
varchar(5000);
v_temp1 
integer;
v_temp2 
integer;
begin
pio_succeed :
= 1;
v_sql :
= 'select count(''a'') from ( ' || pi_sql || ')';
execute immediate v_sql into v_temp;

po_allrownum:
=ceil(v_temp/pi_rownum);

v_sql :
= '';
v_temp :
=pi_whichpage*pi_rownum + 1;
v_temp1:
=(pi_whichpage-1)*pi_rownum + 1;
v_temp2:
=pi_whichpage*pi_rownum;
v_sql:
= 'select * from (select rownum as rn,t.* from (' || pi_sql ||') t where rownum<' || to_char(v_temp) || ')  where rn between ' || to_char(v_temp1) || ' and ' || to_char(v_temp2);
open v_cur_data for v_sql;
if v_cur_data %notfound
then
pio_succeed:
=-1;
return;
end if;
po_cur_DATA :
= v_cur_data;
end;
posted @ 2007-01-19 13:02 tory 阅读(817) | 评论 (0)编辑 收藏

使用JFreeChart生成热点图表
2006-12-14 11:54
<一>前言:

  JFreeChart是开放源代码站点SourceForge.net上的一个JAVA项目。它的功能十分强大,能创建饼图、柱状图(普通柱状图以及堆栈柱状图)、线图、区域图、分布图、混合图、甘特图以及一些仪表盘等等,并可生成PNG或JPG图片格式文件。
  本人在学习过程中发现,网上很多文章都是讲一些JFreeChart的基本应用,而对JFreeChart生成热点图表这样常用的功能虽有所提及却没有一个完整的例子,所以我就写一个简单示例供大家参考,希望对大家的学习有所帮助。 

  <二>示例说明:

  假设有一个关于程序员北京,上海,广洲三地程序员学历,开发语言,薪金情况的调查。首先要以饼图显示程序员学历的分布情况(index.jsp)。点击饼图的每一部分会以柱状图显示该层次程序员所用开发语言和薪金的情况(barview.jsp)。重点演示怎样在饼图上添加链接。 

  <三>准备工作:

  1.下载最新版本的JFreeChart,当前为jfreechart-1.0.0-rc1
下载地址:http://www.jfree.org/jfreechart/index.html

  2.解压文件,将jfreechart-1.0.0-rc1/lib下的jcommon-1.0.0-rc1.jar,jfreechart-1.0.0-rc1.jar复制到WEB应用的lib目录下。

  3.在web.xml文件中增加以下内容:

<servlet> 
<servlet-name>DisplayChart</servlet-name> 
<servlet-class>org.jfree.chart.servlet.DisplayChart</servlet-class> 
</servlet> 
<servlet-mapping> 
<servlet-name>DisplayChart</servlet-name> 
<url-pattern>/servletDisplayChart</url-pattern> 
</servlet-mapping> 

  <四>饼图页面代码(index.jsp) 


<%@ page contentType="text/html;charset=GBK"%> 
<%@ page import="org.jfree.data.general.DefaultPieDataset"%> 
<%@ page import="org.jfree.chart.*"%> 
<%@ page import="org.jfree.chart.plot.*"%> 
<%@ page import="org.jfree.chart.servlet.ServletUtilities"%> 
<%@ page import="org.jfree.chart.labels.StandardPieItemLabelGenerator"%> 
<%@ page import="org.jfree.chart.urls.StandardPieURLGenerator"%> 
<%@ page import="org.jfree.chart.entity.StandardEntityCollection"%> 
<%@ page import="java.io.*"%> 
<HTML> 
<HEAD> 
<META http-equiv=Content-Type content="text/html; charset=GBK"> 
<TITLE>nacl_zhuang@hotmail.com</TITLE> 
</HEAD> 
<BODY> 
<% 

DefaultPieDataset data = new DefaultPieDataset(); 
data.setValue("高中以下",370); 
data.setValue("高中",1530); 
data.setValue("大专",5700); 
data.setValue("本科",8280); 
data.setValue("硕士",4420); 
data.setValue("博士",80); 

PiePlot3D plot = new PiePlot3D(data);//3D饼图 
plot.setURLGenerator(new StandardPieURLGenerator("barview.jsp"));//设定链接 
JFreeChart chart = new JFreeChart("",JFreeChart.DEFAULT_TITLE_FONT, plot, true); 
chart.setBackgroundPaint(java.awt.Color.white);//可选,设置图片背景色 
chart.setTitle("程序员学历情况调查表");//可选,设置图片标题 
plot.setToolTipGenerator(new StandardPieItemLabelGenerator()); 
StandardEntityCollection sec = new StandardEntityCollection(); 
ChartRenderingInfo info = new ChartRenderingInfo(sec); 
PrintWriter w = new PrintWriter(out);//输出MAP信息 
//500是图片长度,300是图片高度 
String filename = ServletUtilities.saveChartAsPNG(chart, 500, 300, info, session); 
ChartUtilities.writeImageMap(w, "map0", info, false); 

String graphURL = request.getContextPath() + "/servlet/DisplayChart?filename=" + filename; 

%> 

<P ALIGN="CENTER"> 
<img src="<%= graphURL %>" width=500 height=300 border=0 usemap="#map0"> 
</P> 
</BODY> 
</HTML> 

  生成的图片如下


  在浏览器中点右键->查看源文件会发现有以下一段HTML代码: 

<map id="map0" name="map0"> 
<area shape="poly" coords="247,61,250,61,250,123,250,123" title="博士 = 80" alt="" href="barview.jsp?category=博士&pieIndex=0"/> 
<area shape="poly" coords="148,112,153,102,160,92,170,83,182,76,196,70,212,65,229,62,247,61,250,123,250,123" title="硕士 = 4,420" alt="" href="barview.jsp?category=硕士&pieIndex=0"/> 
<area shape="poly" coords="324,167,311,173,297,179,282,182,266,185,250,186,234,185,217,183,202,179,188,173,175,167,
  165,159,157,151,151,142,147,132,146,122,148,112,250,123,250,123" title="本科 = 8,280" alt="" 
 href="barview.jsp?category=本科&pieIndex=0"/> 
<area shape="poly" coords="307,72,324,80,338,91,347,103,352,117,352,131,347,144,338,156,324,167,250,123,250,123" title="大专 = 5,700" alt="" href="barview.jsp?category=大专&pieIndex=0"/> 
<area shape="poly" coords="261,62,285,65,307,72,250,123,250,123" title="高中 
 = 1,530" alt="" href="barview.jsp?category=高中&pieIndex=0"/> 
<area shape="poly" coords="250,61,261,62,250,123,250,123" title="高中以下 = 370" alt="" href="barview.jsp?category=高中以下&pieIndex=0"/> 
</map> 


  这就是MAP信息,我们在IMG标签中加入usemap="#map0"就可以为饼图的每一部分加入链接。

  <五>柱状图页面代码:(barview.jsp)

<HTML> 
<HEAD> 
<META http-equiv=Content-Type content="text/html; charset=GBK"> 
<TITLE>nacl_zhuang@hotmail.com</TITLE> 
</HEAD> 

<body> 

<%@ page contentType="text/html;charset=GBK"%> 
<%@ page import="org.jfree.chart.ChartFactory, 
org.jfree.chart.JFreeChart, 
org.jfree.chart.plot.PlotOrientation, 
org.jfree.chart.servlet.ServletUtilities, 
org.jfree.data.category.*"%> 
<% 
CategoryDataset dataset; 
String category=request.getParameter("category"); 
category= new String(category.getBytes("ISO8859_1"), "GBK"); 
if(category.equals("本科")||category.equals("高中")||category.equals("大专")) 

 dataset=getDataSet(); 

else if(category.equals("硕士")||category.equals("博士")) 

 dataset=getDataSet2(); 
}else 

 dataset=getDataSet3(); 

String title=category+"程序员在各城市薪金情况统计"; 
JFreeChart chart = ChartFactory.createBarChart3D(title, 
"城市", 
"薪金", 
dataset, 
PlotOrientation.VERTICAL, 
true, 
false, 
false); 

String filename = ServletUtilities.saveChartAsPNG(chart, 500, 300, null, session); 
String graphURL = request.getContextPath() + "/servlet/DisplayChart?filename=" + filename; 
%> 
<P ALIGN="CENTER"> 
<img src="<%= graphURL %>" width=500 height=300 border=0 usemap="#<%= filename %>"> 
</P> 
<%! 
private static CategoryDataset getDataSet() { 
 DefaultCategoryDataset dataset = new DefaultCategoryDataset(); 
 dataset.addValue(2000, "北京", "VB"); 
 dataset.addValue(1800, "上海", "VB"); 
 dataset.addValue(2200, "广州", "VB"); 
 dataset.addValue(3200, "北京", "JAVA"); 
 dataset.addValue(3500, "上海", "JAVA"); 
 dataset.addValue(3600, "广州", "JAVA"); 
 dataset.addValue(3300, "北京", "DOT NET"); 
 dataset.addValue(3400, "上海", "DOT NET"); 
 dataset.addValue(3700, "广州", "DOT NET"); 
 dataset.addValue(2500, "北京", "DELPHI"); 
 dataset.addValue(2800, "上海", "DELPHI"); 
 dataset.addValue(3200, "广州", "DELPHI"); 
 dataset.addValue(5000, "北京", "VC"); 
 dataset.addValue(3500, "上海", "VC"); 
 dataset.addValue(4600, "广州", "VC"); 
 return dataset; 

private static CategoryDataset getDataSet2() { 
 DefaultCategoryDataset dataset = new DefaultCategoryDataset(); 
 dataset.addValue(2000, "上海", "VB"); 
 dataset.addValue(3000, "北京", "JAVA"); 
 dataset.addValue(3330, "上海", "JAVA"); 
 dataset.addValue(3500, "广州", "JAVA"); 
 dataset.addValue(3500, "北京", "DOT NET"); 
 dataset.addValue(4000, "上海", "DOT NET"); 
 dataset.addValue(4800, "广州", "DOT NET"); 
 dataset.addValue(2600, "北京", "DELPHI"); 
 dataset.addValue(2200, "上海", "DELPHI"); 
 dataset.addValue(4000, "北京", "VC"); 
 dataset.addValue(4000, "上海", "VC"); 
 dataset.addValue(4200, "广州", "VC"); 
 return dataset; 

private static CategoryDataset getDataSet3() { 
 DefaultCategoryDataset dataset = new DefaultCategoryDataset(); 
 dataset.addValue(2100, "北京", "VB"); 
 dataset.addValue(2200, "上海", "VB"); 
 dataset.addValue(2100, "广州", "VB"); 
 dataset.addValue(3000, "北京", "JAVA"); 
 dataset.addValue(3200, "上海", "JAVA"); 
 dataset.addValue(3600, "广州", "JAVA"); 
 dataset.addValue(4100, "北京", "DOT NET"); 
 dataset.addValue(4200, "上海", "DOT NET"); 
 dataset.addValue(4160, "广州", "DOT NET"); 
 dataset.addValue(2400, "北京", "DELPHI"); 
 dataset.addValue(2600, "上海", "DELPHI"); 
 dataset.addValue(2500, "广州", "DELPHI"); 
 dataset.addValue(5400, "北京", "VC"); 
 dataset.addValue(5000, "上海", "VC"); 
 dataset.addValue(5500, "广州", "VC"); 
 return dataset; 

%> 
</body> 
</html> 

  生成图片如下: 


posted @ 2006-12-29 23:36 tory 阅读(375) | 评论 (0)编辑 收藏

 
在用 AJAX 开发的过程中, 不可避免的会遇到中文问题. 很多原来可以通过表单进行 POST 提交的字符, 到了用 AJAX 实现的时候, 就会出现烦人的乱码和丢特殊字符的现象. 另外服务器端返回值如何解析, 也是一个很烦人的问题. 本文将就个人的一点实践经验作出总结, 并给出一个尽量简单可行, 复用性高的方案. 目的不是替代你喜欢的 AJAX 框架, 而是希望帮助您理解和处理可能遇到的问题.

开始之前: 首先一个问题就是通常 XMLHttpRequest 默认的编码都是UTF-8的, 所以我们建议所有页面, 客户端和服务器端都使用 UTF-8 作为编码.

1. base64 encode 和 decode
    这个方案依赖于 JavaScript 实现的 base64 编码/解码方法, 在客户端发送参数的时候用 base64 进行编码, 服务器端通过 base64 进行解码后还原出原来的字符, 这个解决方案可以满足需要, 但是有个问题就是一是增加了客户端代码量, 还有个大问题就是编码后的内容比原始内容会大很多, 另外如果找到的 base64 JS 算法不够标准的话, 服务器端就无法还原原来的值了. 现在网上有很多种 base64 的 JS 实现代码, 例如如下的一个算法实现:

<HTML>
<HEAD>
<TITLE>Base64</TITLE>
<script language=javascript>
var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var base64DecodeChars = new Array(
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
  52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
  -1,  0, 1, 2, 3,  4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
  -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
  41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1);
function base64encode(str) {
  var out, i, len;
  var c1, c2, c3;
  len = str.length;
  i = 0;
  out = "";
  while (i < len) {
 c1 = str.charCodeAt(i++) & 0xff;
 if(i == len)
 {
   out += base64EncodeChars.charAt(c1 >> 2);
   out += base64EncodeChars.charAt((c1 & 0x3) << 4);
   out += "==";
   break;
 }
 c2 = str.charCodeAt(i++);
 if(i == len)
 {
   out += base64EncodeChars.charAt(c1 >> 2);
   out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
   out += base64EncodeChars.charAt((c2 & 0xF) << 2);
   out += "=";
   break;
 }
 c3 = str.charCodeAt(i++);
 out += base64EncodeChars.charAt(c1 >> 2);
 out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
 out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6));
 out += base64EncodeChars.charAt(c3 & 0x3F);
  }
   return out;
}
function base64decode(str) {
  var c1, c2, c3, c4;
  var i, len, out;
  len = str.length;
  i = 0;
  out = "";
  while (i < len) {
 /* c1 */
 do {
   c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
 } while(i < len && c1 == -1);
 if(c1 == -1)
   break;
 /* c2 */
 do {
   c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
 } while(i < len && c2 == -1);
 if(c2 == -1)
   break;
 out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
 /* c3 */
 do {
   c3 = str.charCodeAt(i++) & 0xff;
   if(c3 == 61)
 return out;
   c3 = base64DecodeChars[c3];
 } while(i < len && c3 == -1);
 if(c3 == -1)
   break;
 out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2));
 /* c4 */
 do {
   c4 = str.charCodeAt(i++) & 0xff;
   if(c4 == 61)
 return out;
   c4 = base64DecodeChars[c4];
 } while(i < len && c4 == -1);
 if(c4 == -1)
   break;
 out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
  }
   return out;
}
function utf16to8(str) {
  var out, i, len, c;
  out = "";
  len = str.length;
  for(i = 0; i < len; i++) {
 c = str.charCodeAt(i);
 if ((c >= 0x0001) && (c <= 0x007F)) {
   out += str.charAt(i);
 } else if (c > 0x07FF) {
   out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
   out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
   out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
 } else {
   out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
   out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
 }
  }
   return out;
}
function utf8to16(str) {
  var out, i, len, c;
  var char2, char3;
  out = "";
  len = str.length;
  i = 0;
  while (i < len) {
 c = str.charCodeAt(i++);
 switch(c >> 4)
 {
  case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
   // 0xxxxxxx
   out += str.charAt(i-1);
   break;
  case 12: case 13:
   // 110x xxxx  10xx xxxx
   char2 = str.charCodeAt(i++);
   out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
   break;
  case 14:
   // 1110 xxxx 10xx xxxx 10xx xxxx
   char2 = str.charCodeAt(i++);
   char3 = str.charCodeAt(i++);
   out += String.fromCharCode(((c & 0x0F) << 12) |
     ((char2 & 0x3F) << 6) |
     ((char3 & 0x3F) << 0));
   break;
 }
  }
   return out;
}

function doit() {
  var f = document.f
   f.output.value = base64encode(utf16to8(f.source.value))
   f.decode.value = utf8to16(base64decode(f.output.value))
}
</script>
</HEAD>
<BODY>
<H1>Base64</H1>
<FORM NAME="f">
原码& lt;BR>
<TEXTAREA NAME="source" ROWS=4 COLS=60 WRAP="soft"></TEXTAREA><BR><BR>
Base64 encode<BR>
<TEXTAREA NAME="output" ROWS=4 COLS=60 WRAP="soft"></TEXTAREA><BR><BR>
Base64 decode<BR>
<TEXTAREA NAME="decode" ROWS=4 COLS=60 WRAP="soft"></TEXTAREA><BR><BR>
<INPUT TYPE=BUTTON VALUE="转换" ONCLICK="doit()">
</FORM>
</BODY>

在每个表单值被提交之前调用 base64encode, 然后在服务器端调用 base64 解码器即可.
在 JSP 中可以通过这样做来实现:
        sun.misc.BASE64Decoder base64decoder = new sun.misc.BASE64Decoder();
        byte[] data = base64decoder.decodeBuffer(request.getParameter("input"));
        String result = new String(data, "UTF-8");// 注意建议这里制定字符集来欢迎到原来的字符串

在这种情况下服务器端返回的字符也可以通过先 base64 编码的方式传递到客户端, 客户端之后调用 JS 形式的解码器即可还原到原来的字符串. 服务器端可以使用 sun.misc.BASE64Encoder (不要用 java.netURLEncoder).

2.使用 JS 自带的 escape() & encodeURI() & encodeURIComponent()

escape() & encodeURI() & encodeURIComponent()这三个函数都可以用来对URI进行encode或过滤特殊字符(#/$&+=?/等)。我的经验是最好用encodeURIComponent()(需要IE 5.5以上,FireFox当然没问题),因为对UTF-8支持比较好,不会遇到中文乱码问题,否则还需要进行编码转换,很麻烦的。使用其它两个函数都会发生丢失特殊字符的问题,例如空格变+号或者空格,引号,&=?等丢失的问题, 至少使用 JSP 作为服务器端的话会发生这种情况, 有兴趣的朋友可以将本文最后的例子代码中的编码部分修改后做个测试.

下面是MSDN上对这三个函数的解释:

escape(charString)

The escape method returns a string value (in Unicode format) that contains the contents of charstring. All spaces, punctuation, accented characters, and any other non-ASCII characters are replaced with %xx encoding, where xx is equivalent to the hexadecimal number representing the character. For example, a space is returned as "%20."

Characters with a value greater than 255 are stored using the %uxxxx format.

Note   The escape method should not be used to encode Uniform Resource Identifiers (URI). Use encodeURI and encodeURIComponent methods instead.

http://msdn.microsoft.com/library/en-us/script56/html/js56jsmthescape.asp

 encodeURI(URIString)

The encodeURI method returns an encoded URI. If you pass the result to decodeURI, the original string is returned. The encodeURI method does not encode the following characters: ":", "/", ";", and "?". Use encodeURIComponent to encode these characters.

http://msdn.microsoft.com/library/en-us/script56/html/js56jsmthfencodeuri.asp

encodeURIComponent(encodedURIString)

The encodeURIComponent method returns an encoded URI. If you pass the result to decodeURIComponent, the original string is returned. Because the encodeURIComponent method encodes all characters, be careful if the string represents a path such as /folder1/folder2/default.html. The slash characters will be encoded and will not be valid if sent as a request to a web server. Use the encodeURI method if the string contains more than a single URI component.

http://msdn.microsoft.com/library/en-us/script56/html/js56jsmthencodeuricomponent.asp

3. 使用一个简便的客户端数据解析方案

最偷懒的办法就是返回一段 HTML 显示出来了. 至于如果是想带一些数据, 解析处理的话, 方案很多, 利用 XML 啊, JSON 啊什么的不一而足. 我这里呢就给出一个相当简便的方案: 使用 JS 内置的 eval 方法来解析. 这个方案是在帮助一个同事想最快的已最短的代码解析返回的对象的多个变量的时候提出的.

服务器端返回一个字符串:
var _dataObject = {
   username : "beansoft",
        age : 24
};

客户端在得到这个字符串后可以通过下面一段代码搞定:
var responseText = xmlhttp.responseText;
eval(responseText);
alert("_dataObject.username=" + _dataObject.username);

好了, 解析出来了!
如果要传递多个变量呢, 就用 var _dataObject1, var _dataObject2...这样就可以了, 客户端就依次是 _dataObject1.username, _dataObject2.username...

等等: 我的变量里写了特殊字符怎么办? 例如我用的字符串是 'abc"'', 这时候我不得不抛出杀手锏了, 这就是用 Java 实现的 escape(), unescape() 方法, 其实本例中只需要 escape() 的 Java 版本就可以了(这个方案也帮助另一个同事解决了从JSP端传递的变量含有'号结果导致客户端没法显示的问题):

    public static String escape(String src) {
        int i;
        char j;
        StringBuffer tmp = new StringBuffer();
        tmp.ensureCapacity(src.length() * 6);
        for (i = 0; i < src.length(); i++) {
            j = src.charAt(i);
            if (Character.isDigit(j) || Character.isLowerCase(j)
                    || Character.isUpperCase(j))
                tmp.append(j);
            else if (j < 256) {
                tmp.append("%");
                if (j < 16)
                    tmp.append("0");
                tmp.append(Integer.toString(j, 16));
            } else {
                tmp.append("%u");
                tmp.append(Integer.toString(j, 16));
            }
        }
        return tmp.toString();
    }

    public static String unescape(String src) {
        StringBuffer tmp = new StringBuffer();
        tmp.ensureCapacity(src.length());
        int lastPos = 0, pos = 0;
        char ch;
        while (lastPos < src.length()) {
            pos = src.indexOf("%", lastPos);
            if (pos == lastPos) {
                if (src.charAt(pos + 1) == 'u') {
                    ch = (char) Integer.parseInt(src
                            .substring(pos + 2, pos + 6), 16);
                    tmp.append(ch);
                    lastPos = pos + 6;
                } else {
                    ch = (char) Integer.parseInt(src
                            .substring(pos + 1, pos + 3), 16);
                    tmp.append(ch);
                    lastPos = pos + 3;
                }
            } else {
                if (pos == -1) {
                    tmp.append(src.substring(lastPos));
                    lastPos = src.length();
                } else {
                    tmp.append(src.substring(lastPos, pos));
                    lastPos = pos;
                }
            }
        }
        return tmp.toString();
    }

这样, 在服务器端的时候可以变成:
<%
String username = "'abc\"''";// 其实这个普通子串转换成 Java 语言中的字符串也有工具可以用的, 例如本人开发的 Native2JavaString, 改日再讲.
%>
var _dataObject = {
   username : "<%=escape(username)%>",
        age : 24
};
客户端呢, 就可以简单的来JS自带的 unescape() 函数来取出原来的字符串:
var responseText = xmlhttp.responseText;
eval(responseText);
alert("_dataObject.username=" + unescape(_dataObject.username));
就是服务器端用 Java 写的 escape(), 客户端呢就用 JS 自带的 unescape().

4. 实例代码
好了, 说了这么多, 就推出个人的解决方案吧. 简单的讲就是我写了一个脚本对象 AjaxFormer, 使用的是 escape来自动的将原来的 POST/GET 方式的提交代码自动的转换成 AJAX 的方式.

/**
 * @constructor
 * This is a ajax form helper class.
 *
 * @param form - the document form
 * @param resultDivId - the result div id
 */
function AjaxFormer (form, resultDivId);

构造器的第一个参数是个 form 对象, 第二个是个可选的结果 DIV 对象, 也就是说你可以指定服务器端返回的 HTML 代码显示的地方, 如果保持为空的话, 那么返回的 HTML 会被附加到文档的末尾. 本对象有一个名字为 ajaxSubmitForm() 的方法来自动的遍历所有表单元素, 然后将结果拼成一个字符串, 最后根据原来的表单的提交方式(get/post)来自动再客户端用 AJAX 模拟提交这个表单到原来的表单的 action 属性所指定的页面中去.

用法示例:
<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>AJAX Form Submit Test</title>
<script src='ajax_common.js'></script>

</head>

<body>
<h3>AJAX Form Submit Test</h3>
Fill the form and then click submit
<form method="POST" id="form1" name="form1"
action="form_action.jsp"
onSubmit="former.ajaxSubmitForm();return false;">
    <p><input type="hidden" name="hidden1" value="hiddenValue">
    text:<input type="text" name="textf&1" size="20" value="text1">
    checkbox:<input type="checkbox" name="checkbox1" value="ON" checked>
    radio:<input type="radio" value="V1" checked name="radio1">
    select:<select size="1" name="select1">
    <option selected value="option1">D1</option>
    </select>
    <br>
    <br>
    <input type="submit" name="B1" value="submit">
    <input type="reset" name="B2" value="reset">
    </p>
</form>

<script type="text/javascript">
var former = new AjaxFormer($('form1'));
</script>
</body>

</html>

红色的字体就是您从一个非 AJAX 的表单提交改变成一个 AJAX 的表单提交所需要做的工作, 看上去够简单吧?

运行时的效果如图所示:
http://www.blogjava.net/images/blogjava_net/beansoft/18680/o_ajaxFormer.png

下载本文的源码: 

AJAXFormer.zip4KB

将源码解压缩到JSP服务器的任意目录下即可, 例如 $TOMCAT_HOME\webapps\ROOT 下, 然后在浏览器里键入:
http://localhost:8080/ajax_form_submit.htm

作者: beansoft@126.com 2006.12.25

posted @ 2006-12-25 21:01 tory 阅读(754) | 评论 (1)编辑 收藏

  在这篇 ASP.NET 中Session 实现原理浅析[2] 状态管理器 Blog中, Flier 老大向大家推荐了Java中几种Cache的实现,于是我就按图索骥,首先是找到OSCache的老家: OpenSymphony 。哇,这里的资源真多啊,简直就是一个宝藏。为什么原来就没有发现呢?感谢Flier老大了!
        在OpenSymphony的主页上,我看到了两个让我觉得很亲切的项目:WebWork和Quarts。WebWork作为一个实现了IOC的轻量级Web Application Framework一直备受开发者的青睐;而Quarts更是在异步信息处理上大展拳脚了。看到了老朋友,我就只好暂时将新欢OSCache摆一边,找出我很久没有联系的老猫(Tomcat)去跟WebWork聊嗑了。
        做好了一切的准备工作之后[1],我尝试着做一个登陆注册的功能,以体验一下WebWork。跟其他的Web应用一样,我们首先要建立一个标准的WEB-INF目录(所谓标准就是目录下面包含lib和classes子目录以及Web.xml文件),接着在WEB-INF/lib下面放上WebWork所需要的.jar文件并在WEB-INF目录下建立Web.xml文件[2]。在完成了这些千篇一律的工作之后,我仔细的看了一下Web.xml中内容,它里面只定义了一个servlet:webwork,其对应的class为com.opensymphony.webwork.dispatcher.ServletDispatcher,然后由webwork这个servlet去处理所有.action的请求。看到这里,我暗自窃喜,这跟我熟悉的WAF框架是十分类似的,只不过MainServlet变成了ServletDispatcher,.do的请求变成了.action而已。
        接下来,就是到classes目录下建立xwork.xml文件,这个文件跟WAF中的mappings.xml很相似,因为对.action的处理都是在这里被定义的,而WAF中关于.do的处理则定义在mapping.xml中。但是也有一些我并不清楚的东西,如package和Interceptor。在classes目录下还要建立一个validator.xml文件,但是这个并不是必需的。做好了这些准备工作之后,就真正开始WebWork的体验之旅了。
        1、建立一个index.jsp(以下为主要部分):
 <form action="Login.action" method="post">
  
< table  cellspacing =0  width ="100%" >
    
< tr >
      
< td > Login ID:
        
< input  type ="text"  name ="loginId"  width =100  /> &nbsp;&nbsp;

          Password:
        
< input  type ="password"  name ="loginPassword"  width =100  /> &nbsp;&nbsp;
        
< input  type ="submit"  value ="Login"   />
      
</ td >
      
< td  align =right>
         
Hello, <ww:property value ="loginId"   />

      
</ td >
    
</ tr >
  
</ table >
</ form > < SPAN>

        需要注意的地方就是form的action属性的写法了;
        2、在xwork.xml中增加相应的处理action的节点

 <action name="Login" class="fantasysoft.webwork.Login">
   
< result  name ="error"  type ="dispatcher" > index.jsp </ result >
   
< result  name ="success"  type ="dispatcher" > index.jsp </ result >
</ action >

        这里需要注意的是action节点中name的值要与index.jsp中定义的action的名字要严格匹配,对于大小 写是敏感的。在action节点中还包含了节点,以说明处理action之后会可能出现的不同结果(name)和相应的处理方式(type)。譬如说,如果Login的这个action处理success了,则使用dispatcher将结果分(dispatch)到相应的页面。而在WAF框架中并没有这样的定义,因为在默认情况下如果不成功则会返回当前页面,不过可以定义FlowHandler,并拥有类似的功能且更加灵活;
        3、实现类Login的代码:

package fantasysoft.webwork;

import com.opensymphony.xwork.ActionSupport;

public   class  Login extends ActionSupport
{
    
private
 String loginId;
    
private
 String loginPassword;

    
public  String getLoginPassword() 
{
        
return
 loginPassword;
    }

    
public   void  setLoginPassword(String loginPassword)  {
        
this .loginPassword  =
 loginPassword;
    }

    
public  String getLoginId()  {
        
return
 loginId;
    }

    
public   void  setLoginId(String loginId)  {
        
this .loginId  =
 loginId;
    }

    
public  String execute() throws Exception {             
        
if  (!checkUserId() return
 ERROR;  // checkUserId is the method that will be implemented
            
else   return
 SUCCESS;
    }

}

        在代码中,你会发现有两个继承变量ERROR与SUCCESS。这两个变量是定义在Action这个接口的,而ActionSupport则实现了Action接口。在接口Action的代码中,我们可以看到ERROR = "error"、SUCCESS = "success"。我们可以发现这两个变量的值与xwork.xml中result子节点中的name的值是相匹配。除此之外,在代码中,我也找不到了原来在开发中经常要用到的一个API:getParameter。事实上,将表单中数据析取出来的工作是由webwork这个唯一定义的servlet去完成的,而这个类会调用Login类中set的方法将用户输入的数据赋给Login类的属性:loginId和loginPassword。在这里,我们也要跟前面index.jsp中的包含的标签联系起来。当数据被dispatch回index.jsp的时候,在index.jsp页面render的过程中是调用了get的方法去获取相应的数据的。
        最后,我们可以总结一下,整个Web应用程序的处理流程了:
        首先,当用户提交了表单(form)至Login.action后,由web.xml中定义的唯一的一个servlet:webwork去处理这个请求。webwork会以action的name:Login到xwork.xml中寻找相应的处理action的类,于是就找到了fantasysoft.webwork包中的Login类,由Login类中的execute方法来处理提交的form的数据了;
        然后,根据execute方法的返回值,再到xwork.xml中对应的action节点中去找匹配的result子节点;
        最后,根据result子节点的定义,将处理结果分发(dispatch)或者重定向(redirect)至下一个页面[3]

        
        [1] 准备工作可以参考
WebWork Getting Started
        [2] web.xml文件的具体内容,可以参考
WebWork Tutorial Lesson 2  
        [3] 对于result的Type的更多介绍,可以参考
WebWork Tutorial Lesson 3      

/SPAN>
posted @ 2006-12-24 22:42 tory 阅读(149) | 评论 (0)编辑 收藏

     摘要: 一 Ajax-- 整合的力量 2005 年,伴随着 Web2.0 的东风, Ajax 逐渐进入国内开发人...  阅读全文
posted @ 2006-12-21 22:40 tory 阅读(286) | 评论 (0)编辑 收藏

重载overloading和覆写overriding哪个更早执行-- visitor帮助篇

重载overloading和覆写overriding哪个更早执行--   visitor帮助篇
一:问题提出
虽然我们经常写程序用到重载和覆写,但是很少会考虑他们的执行顺序。下边的内容就是关于,他们同时出现时
哪个先起作用:
二:问题分析
Java是"动态单分派静态多分派语言",这个定义已经多次提起,如果你不了解这些概念,看这里"visitor模式准备"
所以就注定了重载(静态多分派)要早于覆写(动态单分派),因为静态分派是编绎期实现的,动态分派是执行期实现的。
三:验证
简单验证一下,顺变提高记忆

 1 public   class  Parent  {
 2      public   void  run(Object o) {
 3         System.out.println( " in Parent +param:object " );
 4     }

 5      public   void  run( int  i) {
 6         System.out.println( " in Parent + param:int " );
 7     }

 8 }

 9
10 public   class  Child  extends  Parent  {
11
12      public   void  run(Object o) {
13         System.out.println( " in Child +param:Object " );
14     }

15      public   void  run(String str) {
16         System.out.println( " in Child + param:String " );
17     }

18      public   static   void  main(String[] args)  {
19         Parent p  =   new  Child();
20         String str  =   new  String();
21         p.run(str);
22     }

23 }


运行结果是什么?
in Child +param:Object
inChild是确认的,但是为什么是object,而不是String,我们放入的就是String啊。
首先来分析执行过程。
定义韦类型Parent p在执行run(Str)的时候,

1,如果是先执行重载,然後是执行覆写的过程
重载时因为找不到对应的String参数的函数,所以定位到接受父类的run(Object o)函数,
覆写时因为传入时父类告诉子类的对象类型是Object,所以执行run(Object o);
正是我们看到的结果,所以在Java中执行的顺序是这样的。

2,为了对比,说一下先覆写后重载的过程
如果是先覆写,再重载
覆写时因为确定对象实际是子类,所以直接覆写到Child,然後重载,发现有对应的String为参数的函数
执行,run(String str);
应该输出的结果:in Child +param:String
但我们看到结果显然是1,所以验证了Java是先重载后覆写的。


最后的部分:

看完本文,如果你对visitor模式有更多的兴趣,想了解更多请看如下几篇文章。
1,静态分派,动态分派,多分派,单分派 --------------   visitor模式准备
2,访问差异类型的集合类 ------------------------   visitor模式入门
3,visitor模式理论及学术概念-------------------   visitor模式深入
4,visitor模式和其它模式的比较和关系-------------   visitor模式总结 
5,重载overloading和覆写overriding哪个更早执行--   visitor帮助篇 (本文)
虽然排列顺序是1,2,3,4,5 但是我个人建议的学习方式是2,1,3,4,5因为这个顺序更方便一般人理解

posted @ 2006-12-18 22:38 tory 阅读(231) | 评论 (0)编辑 收藏