Let's Go!

发布新日志

  • loadrunner与Winsock协议

    2009-05-04 16:13:09

    lrs_save_param

    Saves data from a static or received buffer to a parameter.

    int lrs_save_param ( char *s_desc,char *buf_desc, char *param_name, int offset, int param_len);

    s_desc
    A descriptor identifying a connected socket.
    buf_desc
    A descriptor identifying a buffer.
    param_name
    The name of a parameter to hold the buffer value.
    offset
    The offset of the data in the buffer to be saved to the parameter.
    param_len
    The length of the data to be saved to the parameter (in bytes).

    The lrs_save_param function saves data from a buffer to a parameter. This function is used for correlating or linking statements within a script.

    The first two parameters specify which buffer to use:

    Use a specific buffer from the data file:
    Use the last received buffer:
    The buffer descriptor has the form. "bufxx"
    The socket descriptor is not NULL and the buffer descriptor is NULL
    LRS_LAST_RECEIVED is specified as the buf_desc parameter

    After specifying an active socket and buffer, you specify a parameter name to store the data. Specify an offset to indicate the offset of the data in the buffer, and length of the data.

    Once you save the parameter, you can use it in both the script. and data file. Substitute all of the appropriate constant values with the parameter. Whenever you reference the parameter, enclose it in angle brackets (or other delimiters defined within VuGen options).

    Note: To save encoded data from a user buffer to a parameter, use the lrs_save_param_ex function.

     

     

    HP LoadRunner Online Function Reference > Windows Sockets Vuser Functions (LRS) > Example: lrs_save_param

    -->

    Example: lrs_save_param

    In the following example, a user performed a Telnet session. The user used a ps command to determine a process ID (PID), and killed the application based on its PID. Repeating the exact steps during replay will not work—during replay the PID will be different (UNIX assigns a new PID). Killing the PID that was recorded in the script. will be ineffective. To overcome this problem, lr_save_param saves the value of the PID to a parameter during replay. This parameter is referenced in the Send buffer which contains the kill command.

    The following code was recorded. The script. would not perform. the kill command during replay. To correct this, the following steps were performed:

    1. The recorded value of the PID, 28597 was located along with the socket and buffer descriptors. The offset and length of the data was determined. The buffer shown below, buf47, was received after the ps command. The offset of the PID within the buffer data is 67, and its length is 5.
    2. recv buf47 188 "\r" "\x0" "\r\n""PID TT STAT TIME COMMAND\r\n" "28469 q2 S 0:01 -tcsh (tcsh)\r\n" "28597 q2 T 0:00 vi log1.txt\r\n" "28602 q2 R 0:00 ps\r\n" "tears:/tmp_mnt/u/jay>"

      The PID also appeared in the Send buffer, buf48, when the user performed a kill operation to the process.

      send buf48 "kill -9 28597"

    3. An lrs_save_param function was placed in the Actions section, before the lrs_send statement, in this instance buffer "buf48". NULL was specified in place of a buffer descriptor, indicating that VuGen should use the last received buffer, "buf47". During each subsequent replay, the PID is saved to a parameter called param1. The parameter was evaluated and printed in the LoadRunner output window or Application Management agent log file using lr_output_message.
    4.                      lrs_receive("socket2", "buf47", LrsLastArg);
      lrs_save_param("socket2", NULL, "param1", 67, 5);
      lr_output_message ("param1: %s", lr_eval_string("<param1>"));
      lr_think_time(10);
      lrs_send("socket2", "buf48", LrsLastArg);

    5. The parameter was referenced in the Send buffer in place of the original PID constant. In this example angle brackets were used, but you can define the delimiters from VuGen's Option menu.
    6. send buf48 "kill -9 <param1>"

      Correlating Statements Functions

      Click one of the following functions for more information:

      Saves data from a static or received buffer to a parameter.
      Saves data from a static, received, or user buffer to a parameter.
      Searches for an occurrence of strings in a static or received buffer and saves a portion of the buffer, relative to the string occurrence, to a parameter.
       
       
       


       

       
      [翻译]loadrunner与Winsock协议
      在讨论winsock解决方案之前,我们先讨论一下各种协议是如何工作.从前面的简介可以了解到很多的高级协议,例如FTP,HTTP协议等.
      以及所有基于window的应用(例如IE,WS-FTP)底层都是在Winsocket层上通信,因此任何高级协议的底层都是用Winsocket通信。
          什么时候在LR中选择Winsocket协议呢?你要先了解LR是怎么样工作的:LR捕捉API请求然后再把它们回放。所以当你在
          创建LR WEB脚本的时候,VUGEN捕捉从IE出去的所有的HTTP请求。除此之外lr还支持其他很多协议,例如Oracle,ODBC等。
          在选择不同协议录制脚本的时候,LR是依靠hooks捕捉正确的API请求。所以既然大部分网络协议都是架构在winsocket协议之上的,
          那对于lr不支持的协议,我们都可以在winsocket层上录制脚本。所以当找不到合适协议的时候,可以选择winsocket来录制。
            录制WinSock协议脚本!
            Lr录制新的虚拟用户脚本,选择winsock协议
            在web虚拟用户脚本中录制的是URL信息,所以VUGEN启动流览器并运行就可以了,但选择Winsocket录制的时候,
            可能会是各种形式的应用,并不简简单单就是浏览器,所以在开始的时候我们不需要指定应用的地址。
           下面的例子我们选择winsock来录制web应用,正如上面说的我们开始要指定ie的地址。因为本来lr是支持http协议的,
           所以这个例子并无具体的意义,只是为了使例子简单。
      winsock脚本典型代码?
      lrs_create_socket("socket0", "UDP", "LocalHost=0", "RemoteHost=doors:2084", LrsLastArg);
      lrs_create_socket("socket1", "TCP", "LocalHost=0", "RemoteHost=www2.yahoo.com:80", LrsLastArg);
      lrs_send("socket0", "buf0", LrsLastArg);
      lrs_receive("socket0", "buf1", LrsLastArg);
      lrs_send("socket1", "buf2", LrsLastArg);
      lrs_send("socket0", "buf3", LrsLastArg);
      lrs_receive("socket0", "buf4", LrsLastArg);
      这是访问雅虎的一个应用,正如我们看到的,winsock先打开一个winsocket连接,然后收发包。
      我们发现录制的脚本中比web脚本(三个文件)多一个文件。
            第四个文件是data.ws,它里面记载了在action里面收发所有包的内容。
            下面是一个data.ws的例子。
      send buf0
      "!"
      recv buf1 1
      "!"
      send buf2
      "GET / HTTP/1.1\r\n"
      "Accept: */*\r\n"
      "Accept-Language: en-us\r\n"
      "Accept-Encoding: gzip, deflate\r\n"
      "User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)\r\n"
      "Host: www.yahoo.com\r\n"
      "Connection: Keep-Alive\r\n"
      "Cookie: B=5oj83bst12l6m&b=2; Y=v=1&n=8lln5lmi3f8g1&l=6ef8c0d34b0/o&p=m2a2s"
      "qa4110004&r=3f&lg=us&intl=us; T=z=4TVE6A4ZqE6A9dIIpt30.NQNTYGNDE3NTYwM081&"
      "a=AAE&sk=DAAEwinHlDtEm/&d=c2wBTWpFQk16WXdNakUzTkRneQFhAUFBRQF6egE0VFZFNkFn"
      "V0E-; I=i1=010g1q1u24252e2i2k2p2r494a4g4m4q55565b5g6g6t6u7172737678797a7f7"
      "g7k7n7o888f8k8p8q989c9f9i9k9l9n9qacanapb0b3bdbfbhblbqbrbuc0c1c4cgcmcscteie"
      "jgr&ir=73\r\n"
      "\r\n"
      send buf3
      "!"
      recv buf4 1
      "!"
      recv buf5 2048
      "HTTP/1.0 200 OK\r\n"
      "Content-Length: 16030\r\n"
      "Content-Type: text/html\r\n"
      "\r\n"
      "<html><head><titl
      buf2包含了发给www.yahoo.com的包,buf5包含了从server收到的回应。正如看到的winsock录制的脚本并不像web脚本那样具有可读性,
      因为winsock是我们最后的选择。在buf序列号的后面是buf的字节数。例如5号buffer后面的2048就是表示收到了2048个帧。
         注意:在很多buffer里面你会发现仅仅包含了一个“!”,这是一个网络常识,这个帧对于应用来说没有任何的作用,完全
         可以把这些桢清除掉,在data.ws清除这个帧是效果的,需要到action里把这些桢注释掉。譬如例子中收到和发出的buffer可以用//注释掉。
         这样可以使脚本运行得更快。下边的例子: buffers 0, 1, 3 和 4 可以注释掉:
      lrs_create_socket("socket0", "UDP", "LocalHost=0", "RemoteHost=doors:2084", LrsLastArg);
      lrs_create_socket("socket1", "TCP", "LocalHost=0", "RemoteHost=www2.yahoo.com:80", LrsLastArg);
      // lrs_send("socket0", "buf0", LrsLastArg);
      // lrs_receive("socket0", "buf1", LrsLastArg);
      lrs_send("socket1", "buf2", LrsLastArg);
      // lrs_send("socket0", "buf3", LrsLastArg);
      // lrs_receive("socket0", "buf4", LrsLastArg);
      lrs_receive("socket1", "buf5", LrsLastArg);
      lrs_send("socket0", "buf6", LrsLastArg);
      创建脚本步骤:
      1. VuGen录制脚本
      2.增强脚本
      3.参数化
      4.关联脚本
      5.设置运行参数
      6.运行脚本
      1. VuGen录制脚本
            按照之前章节说的步骤创建脚本,录下来的就是基本脚本,可以先把“!”的buffer注释掉。
      练习:
            创建一个简单的winsock脚本,可以选择MI公司的网站,把录下来的脚本命名为winsocket_1,再用Http协议方式创建一个Web脚本,
            把脚本保存为WebWinsock_1.请不要更改session_id把两种脚本比较,当你登陆的时候,可以收到一个“welcome,jojo....”的消息。
            既然所有的消息都放在data.ws文件里,那么data.ws里面应该能找到这条消息。
      1.1) 在几号bufffer里面包含“welcome,jojo”?怎样判断这是收到的帧而不是发出的帧?还有着个字符串正如显示的那样或者他是html的一个标签。
            仔细查看脚本,在data.ws文件中寻找MSO=SIDxxxxxxxxx,XXXXXX是9位数的号码 ,他表示自从January 1 st,1970以来流逝的所有的秒时间,
            cookie和session_id都是根据时间来生成的,所以脚本里面的9位数也就直接和脚本运行时间挂钩,所以我们要把这个数改成我们每
            次运行脚本的确切时间,首先把这个值更换成参数,在c里面有一个Time()的函数。他返回的就是自从January 1 st,1970以来流逝的所有的秒时间,
            在脚本的开始用这个函数获取时间值
            ,然后把刚才创建的参数指向这个值。然后在data.ws里面替换所有的id,这样每次脚本运行都能获得正确的id。
      1.2)把修改了参数的脚本运行一遍,在脚本的运行日志里面寻找”incorrectly”。会找到"You've reached this page incorrectly"这条消息,
      运行web脚本也会发现类似的消息。其中发生了什么呢?
            注意:在data.ws文件中收到的buffer内容在每次运行时不会改变,录制脚本时data.ws文件被创建并且回放过程中也不会改变?所以lr录制的
            脚本种buffer不是很重要,LR用发送包的数据发出请求,然后对比发送请求的数据包和脚本里面包存的数据,这里lr只判断数据包大小,而不
            是具体内容。如果收到的数据包包括“rob”,而server响应是“bob”,lr也会认为脚本运行成功。但如果server回复的是robot”,这样
            lr就会因为收到包的大小不对而认为脚本执行失败。另一种情况时lr期望收到500bytes大小的包,但是前10秒只收到了100bytes,这时r会认
            为超时判断脚本运行失败,可以通过lrs_set_recv_timeout 函数来设定超时时间。
          那么反面一种情况是,你不想接受所有的数据包,只是想收到包的前100bytes,可以通过函数ltr_receive_ex 来任意设定想要收到的字节。
      2.增强脚本
          在脚本中添加事务,集合点和控制语句等增强脚本,和web脚本不同的是winsock脚本可读性很差,所以要在录制脚本时添加注释,事务和集合点等。
          如果在脚本中有逻辑需求,那么插入逻辑语句 (通用声明不适应Winsock_1脚本).
      3.脚本参数化
            把脚本中变化的值参数化,只要用参数把这些值替换掉就可以,使用不同参数重复业务流程,例如在上面的例子里面jojo/bean就可以参数化
            成为userIDs/passwords
      4.关联脚本
      关联目的是为了让你在一个并发中用到一个商业流程的结果,在web脚本中有这样的过程,从web脚本中sessionid关系到后面的流程能不能运行,
      winsock脚本有同样的问题。所以需要捕获到session id然后把它关联起来,举个例子从下面的脚本中获取PID(lr函数的例子):
      "\r"
      "\x0 blah blah blah "
      "\r\n blah blah blah "
      "PID TT STAT TIME COMMAND\r\n PID 28469 q2"
      " S 0:01 -tcsh (tcsh)\r\n"
      在一个典型的web脚本中,你用web_create_html_param函数,用“PID“ 和“q2”定义边界扑获数据。
      在Winsock脚本中,用lrs_save_param函数从静态数据或收到的数据包中截获数据,看下面的例子:
      lrs_receive("socket2", "buf47", LrsLastArg);
      lrs_save_param("socket2", NULL, "param1", 67, 5);
      和web_create_html_param函数不同的是lrs_save_param在请求之后进行,这个例子中,第一行代码是接收到47号包。lrs_save_param函数的参数意义如下:
      socket2: 从socket2中扑获数据
          NULL: null参数意思是从最后一个buffer里截取,在这儿就是指buf47,如果你从其它的buf里面来获取数据,则你必须要指明buf的号码了
      param1:命名的参数值
      67:位移(下面截解释)
      5:捕获的长度
      位移:从buffer的开始多少位去捕获参数的值,在下面的例子里面,PID是从buf47开始往后67bytes来截取的,我们怎么确定这个值?
      在data.ws中选取需要截取参数的地方然后按F7键,这样会弹出一个窗体。如下图:
      在左边的列,你将看到符合这部分数据的偏移量,中间四行是用EBCDIC加密的数据包。最右边,是没有经过加密的数据。所以你应该看第五行
      包含PID的真实的数据,,很容易就可以根据64+3得出位偏移为67。
      (现在我们解释一下问什么添加这些多余的东西到数据包中,让它适合我的例子呢?)
      注意:在socket脚本中没有提供web_find函数。只有通过编程来找到你要截取的脚本。
      5.run-time的设置
      配置Run-Time可以控制脚本运行过程中的虚拟用户行为,包括loop,log和Time信息等设置
      6.VuGen运行脚本.
      保存并用VuGen运行脚本验证脚本是否正确
      搞定WinSock!
       
      =============================================== 
      实例:
      #include "lrs.h"
      int rc;
      Action()
      {
      
          lr_think_time(8);
      
          lrs_send("socket0", "buf0", LrsLastArg);
      
          lrs_receive("socket0", "buf1", LrsLastArg);
      
              lrs_save_param("socket0", "buf1", "param1", 6, 11); 
      
              rc = strcmp("<param1>","<param2>");
      
              if(rc != 0)
              {
              lr_error_message ("error:data != %s", lr_eval_string("<param1>"));        
      
              //return -1;
              }
              else return 0;
      } 
      
       
      winsocket 协议录制的脚本里进行参数化
       
      操作步骤:因为winsocket  协议录制的脚本

      输入的数据都被录入到data.ws里的,所以我是
      data.ws里进行的参数化.右键选择要参数化的数据->选择"replace
      with parameter"->输入列名->"parameter properties"进行了一些相
       
      关设置: ⑴在生成的文档里输入参数化数据 ⑵.select next row:unique ⑶.update value on:every iteration ->保存脚本.
        
       
       
      更多信息,请参看《LR 227个问题.CHM》
      问题很多呢
  • Servlet请求转发 RequestDispatcher接口

    2009-05-03 21:56:21

    RequestDispatcher的介绍
    2007年02月15日 星期四 下午 03:40

    RequestDispatcher是一个Web资源的包装器,可以用来把当前request传递到该资源,或者把新的资源包括到当前响应中。RequestDispatcher接口中定义了两个方法:include/forward

    由于<jsp:include>只能指定固定的jsp文件名,不能动态指定jsp文件名。我们需要把<jsp:include>翻译为Java code – RequestDispatcher.include();

    用法:
    <% request.getRequestDispatcher(filename).include(request, response); />

    服务器端的重定向可以有两种方式,一是使用HttpServletResponse的sendRedirect()方法,一是使用RequestDispatcher的forward()方法.

    HttpServletResponse.sendRedirect()方法将响应定向到参数location指定的、新的URL。location可以是一个绝对的URL,如response.sendRedirect("http://java.sun.com")也可以使用相对的URL。如果location以“/”开头,则容器认为相对于当前Web应用的根,否则,容器将解析为相对于当前请求的URL。这种重定向的方法,将导致客户端浏览器的请求URL跳转。从浏览器中的地址栏中可以看到新的URL地址,作用类似于上面设置HTTP响应头信息的实现。


    RequestDispatcher.forward()方法将当前的request和response重定向到该RequestDispacher指定的资源。这在实际项目中大量使用,因为完成一个业务操作往往需要跨越多个步骤,每一步骤完成相应的处理后,转向到下一个步骤。比如,通常业务处理在Servlet中处理,处理的结果转向到一个JSP页面进行显示。这样看起来类似于Servlet链的功能,但是还有一些区别。一个RequestDispatcher对象可以把请求发送到任意一个服务器资源,而不仅仅是另外一个Servlet。 include()方法将把Request Dispatcher资源的输出包含到当前输出中。

    注意,只有在尚未向客户端输出响应时才可以调用forward()方法,如果页面缓存不为空,在重定向前将自动清除缓存。否则将抛出一个IllegalStateException异常。

     

    Servlet请求转发 RequestDispatcher接口
    2008-12-31 15:33

    在Servlet中,利用RequestDispatcher对象,可以将请求转发给另外一个Servlet或JSP页面,甚至是HTML页面,来处理对请求的响应。

    一,RequestDispatcher接口方法简介  

          1,RequestDispatcher对象由Servlet容器来创建,封装一个由路径所标识的服务器资源。    

          2,RequestDispatcher接口中定义了二种方法用于请求转发:    

               forward(ServletRequest,ServletResponse)方法:      

                       将请求转发给服务器上另外一个Servlet,JSP页面,或者HTML文件       这个方法必须在响应被提交给客户端之前调用,否则抛出异常。      

                      方法调用后在响应中的没有提交的内容被自动消除。    

               include(ServletRequest,ServletResponse)方法 :     

                     用于在响应中包含其他资源(Servlet,JSP页面或HTML文件)的内容。      

                     即请求转发后,原先的Servlet还可以继续输出响应信息,转发到的Servlet对请求做出的响应将并入原先Servlet的响应对象中。      

          3,forward方法和include方法的区别:

                      forward方法调用后在响应中的没有提交的内容被自动消除。    include方法使原先的Servlet和转发到的Servlet都可以输出响应信息。   

    二,得到RequestDispatcher对象  

          三种方法可以得到RequestDispatcher对象:   

                 1,利用ServletRequest接口中的getRequestDispatcher(String path)方法。   

                  2,ServletContext接口中getNamedDispatcher(String path)和getRequestDispatcher(String path)方法。

         ServletRequest接口和ServletContext接口中getRequestDispatcher方法区别:   

                  1,参数的区别    

                          参数虽然都是资源路径名,ServletContext接口的中参数路径必须以“/”开始,是相对于当前Servlet上下文根,     ServletRequest接口中的参数路径不仅可以相对于当前Servlet上下文根,还可以相对与当前Servlet路径    

                 2,跨WEB应用程序访问资源    

                          通过ServletContext.getContext()方法获取另个WEB应用程序的上下文环境对象来     调用getRequestDispatcher(String path)方法 将请求转发到另个WEB应用程序的资源。                       

                         还需要在当前WEB应用程序配置中设置<context>元素,指定crossContext属性值为true。

    三,ServletResqonse接口中的sendReadirect()方法和forward()方法的区别  

            二个方法都是用于请求转发的方法,转发给另外的资源为客户端服务。但二者有本质的区别

           sendReadirect()方法原理:   

                  1,客户端发送请求,Servlet1做出处理。   

                  2,Servlet1调用sendReadirect()方法,将客户端的请求 重新定位 到Servlet2。   

                  3,客户端浏览器访问Servlet2.  

                 4,Servlet2对客户端浏览器做出响应。

           forward()方法原理:  

                  1,客户端发送请求,Servlet1做出处理。  

                  2,Servlet1调用sendReadirect()方法,将请求转发给Servlet2来处理请求,为客户端服务。   

                  3,Servlet2对客户端浏览器做出响应。      

          区别:   

               1,定位与转发   

                       sendReadirect()方法是重新定位到另外一个资源来处理请求,URL会重新定位,让客户端重新访问另外一个资源。     forward()方法是转发到另外一个资源来处理请求。URL不会变化。隐藏了处理对象的变化。   

               2,处理请求的资源的范围   

                     sendReadirect()方法可以跨WEB应用程序和服务器重新定位资源来处理请求。     forward()方法只能在应用程序内部转发。


     

  • 甲骨文74亿美元收购Sun 交易预计在今年夏季完成

    2009-05-02 12:17:00

    CNET科技资讯网4月21日国际报道 甲骨文(Oracle)同意以74亿美元收购Sun,每股现金9.50美元。

    甲骨文总裁萨弗瑞-凯兹(Safra Catz)表示:“达成这一交易后,按非通用会计原则计算,预计这一交易第一年将至少为甲骨文每股增加15美分的收益,贡献逾15亿美元的营业利润,第二年将提高至逾20亿美元。收购Sun后,每股利润将比我们收购BEA、PeopleSoft和Siebel的利润提高得更多。”

    甲骨文表示,双方董事会一致同意这一交易,预计在今年夏季完成这一交易。目前该交易还需要Sun股东同意、监管机构批准及符合特别成交条件。

     

    Oracle收购Sun后的10大值得关注焦点

     

    ·MySQL是死是活?

    MySQL作为SUN在业绩不好的情况下,仍然挥霍10亿美金购得的资产前途如何?有人分析,---MySQL可以让Oracle数据库得到更多的市场机会。但Oracle数据库主要走高端路线,MySQL是中小型市场的选择。干掉MySQL,就意味着Oracle要说服中小型用户去选择昂贵的Oracle数据库产品,或者等于把潜在用户赶到微软SQL Server 数据库的怀抱。所以,让MySQL和Oracle数据库分别在低端和高端市场平衡前行才是一个不坏的选择。

    ·Java 社区发扬光大?

    Java作为SUN的计算机语言,10多年来已经成为软件社区的一个品牌和开放的产业标准。SUN在近些年什麽都可以改变,就是Java牢牢地攥着自己的手里。Oracle通过收购得到Java,绝对是一笔大大的财富。不仅因为Oracle的很多产品都是基于Java平台,更主要是因为Oracle可以挺直腰板和.net说不,可以根正苗红的跟IBM说Java。所以,Oracle绝对有理由,也有资源继续支持Java社区,但关键是Oracle是一家比SUN更加封闭的商业公司,在忙着赚钱的时候,Oracle还有多少心思做学问,去推动Java呢?

    ·Solaris焕发青春?

    开放的太晚了。这是普遍对SUN的Solaris策略的看法。SUN一直固守Space+Solaris高端市场。直到被LAMP架构挤压的市场空间不断缩小,才开放了Solaris系统。Oracle拥有了Solaris系统是一个有趣的话题。一定还记得当年Oracle苦于没有操作系统而去撬redhat的客户,推出个Oracle Linux版本的事情。所以,Oracle终于得到了在产品层面上不错的操作系统。就看它怎么玩?

     

    ·对开源的影响?

    但是如果把开源看做一个商业模式的话,Oracle很早就乐于此道了。

     

    ·SUN硬件业务前途未卜?

    Oracle过去是个软件厂商,而且它也正在和HP等硬件厂商进行着合作,现在Oracle通过收购也有了自己的硬件。Oracle原有服务器市场的合作伙伴,一下子变成了竞争对手。有人大胆的预测,Oracle稳妥起见,将转手卖掉SUN的硬件部分,这样还可以减少本次收购的成本。但问题是,McNealy 和 Ellison 似乎一直被称为IT界的疯子,卖掉了SUN的硬件似乎使这件事变的没有那么有意思了。


    ·服务器市场新气象?

    还记得08年,Oracle和SUN的蜜月期,用户购买SUN指定的几款服务器,Oracle的企业版数据库软件将成为一个选件。SUN将为用户支付Oracle的License费用,用户只需要承担一定的支持维护费用。现在Oracle和SUN终于“结婚”了,Oracle软件+SUN的硬件服务器捆绑在一起的策略可以走的更大胆一些。现在,Oracle在服务器市场上的优势是, 操作系统(Solaris, Linux) + 数据库(Oracle, MySQL) + 中间件(Oracle Application Server, BEA, Sun Appserver), 虽然目前服务器市场份额SUN并不靠前,但是Oracle绝对可以给服务器市场带来新气象。


    ·中间件市场静悄悄?

    这次收购看起来并没有从技术产品角度,对中间件产品市场进行新的划分。但是一个值得注意的地方还是Java。Java是业界标准,但Java是Oracle的了。而中间件市场大户IBM同样对Java非常依赖。


    · IBM错失SUN好事坏事?

    IBM与SUN谈判的破裂,成就了Oracle-SUN,从产品线角度,Oracle-sun绝对使IBM面临不小的压力。但是IBM收购SUN也不一定就是好事,首先产品线重叠严重,芯片服务器市场:Sun 是Sparc ,IBM是Power架构,操作系统SUN 是Solaris,IBM是AIX UNIX,数据库,SUN 是MYSQL,IBM是DB2。其次,IBM近年来主要的收入已经从硬件转向了软件和服务,如果收购SUN无疑使IBM不断下滑的硬件业务又多了一个麻烦的问题。所以,虽然IBM没有收购SUN,不能说是一个坏事。


    ·微软是福是祸?

    有人认为Oracle-SUN的合作,将使很多硬件厂商比如HP转而考虑与微软走的更紧密些。也许吧,但从另一个角度看,一直以来,微软靠Intel这个兄弟,在企业服务器端市场打拼。先前只有IBM这么一个能提供软硬件整套服务的家伙捣下乱。现在Oracle也来了,从IT基础架构+企业应用软件。这势必进一步挤压Wintel的市场份额,失去了平台,微软本就不强的企业服务器端应用软件市更加不好活。目前看来微软只能专心的搞它的IE8,windows7,和Office 2010了。


    ·裁员

    不可避免,没有什么想象的空间。去年11月,Sun已经宣布裁员5000至6000人,占员工总数的18%,Oracle-SUN的合并,必然会对SUN原有结构进一步调整。不过对于一个创新公司,削减技术人员将是不明智的,还是干掉多余的销售和市场营销人员吧。

     

     

     

     

     

  • MVC(Model View Controller)模型-视图-控制器

    2009-05-02 03:25:50

    mvc

    copyright: Apple Inc.

    目录
    MVC与模板概念的理解
    MVC如何工作
    为什么要使用 MVC
    MVC的优点
    MVC的缺点
    开发方式
    常见的MVC组件
        
       
       
      MVC(Model View Controller)模型-视图-控制器
       

       
       
       
      MVC与模板概念的理解

        MVC本来是存在于Desktop程序中的,M是指数据模型,V是指用户界面,C则是控制器。使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。比如一批统计数据你可以分别用柱状图、饼图来表示。C存在的目的则是确保M和V的同步,一旦M改变,V应该同步更新。
        模型-视图-控制器(MVC)是Xerox PARC在八十年代为编程语言Smalltalk-80发明的一种软件设计模式,至今已被广泛使用。最近几年被推荐为Sun公司J2EE平台的设计模式,并且受到越来越多的使用 ColdFusion 和 PHP 的开发者的欢迎。模型-视图-控制器模式是一个有用的工具箱,它有很多好处,但也有一些缺点。

       
      MVC如何工作

        MVC是一个设计模式,它强制性的使应用程序的输入、处理和输出分开。使用MVC应用程序被分成三个核心部件:模型、视图、控制器。它们各自处理自己的任务。
        视图
        视图是用户看到并与之交互的界面。对老式的Web应用程序来说,视图就是由HTML元素组成的界面,在新式的Web应用程序中,HTML依旧在视图中扮演着重要的角色,但一些新的技术已层出不穷,它们包括Macromedia Flash和象XHTML,XML/XSL,WML等一些标识语言和Web services.
        如何处理应用程序的界面变得越来越有挑战性。MVC一个大的好处是它能为你的应用程序处理很多不同的视图。在视图中其实没有真正的处理发生,不管这些数据是联机存储的还是一个雇员列表,作为视图来讲,它只是作为一种输出数据并允许用户操纵的方式。
        模型
        模型表示企业数据和业务规则。在MVC的三个部件中,模型拥有最多的处理任务。例如它可能用象EJBs和ColdFusion Components这样的构件对象来处理数据库。被模型返回的数据是中立的,就是说模型与数据格式无关,这样一个模型能为多个视图提供数据。由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。
        控制器
        
        控制器接受用户的输入并调用模型和视图去完成用户的需求。所以当单击Web页面中的超链接和发送HTML表单时,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后确定用哪个视图来显示模型处理返回的数据。
        现在我们总结MVC的处理过程,首先控制器接收用户的请求,并决定应该调用哪个模型来进行处理,然后模型用业务逻辑来处理用户的请求并返回数据,最后控制器用相应的视图格式化模型返回的数据,并通过表示层呈现给用户。

       
      为什么要使用 MVC

        大部分Web应用程序都是用像ASP,PHP,或者CFML这样的过程化(自PHP5.0版本后已全面支持面向对象模型)语言来创建的。它们将像数据库查询语句这样的数据层代码和像HTML这样的表示层代码混在一起。经验比较丰富的开发者会将数据从表示层分离开来,但这通常不是很容易做到的,它需要精心的计划和不断的尝试。MVC从根本上强制性的将它们分开。尽管构造MVC应用程序需要一些额外的工作,但是它给我们带来的好处是无庸质疑的。
        首先,最重要的一点是多个视图能共享一个模型,现在需要用越来越多的方式来访问你的应用程序。对此,其中一个解决之道是使用MVC,无论你的用户想要Flash界面或是 WAP 界面;用一个模型就能处理它们。由于你已经将数据和业务规则从表示层分开,所以你可以最大化的重用你的代码了。
        由于模型返回的数据没有进行格式化,所以同样的构件能被不同界面使用。例如,很多数据可能用HTML来表示,但是它们也有可能要用Macromedia Flash和WAP来表示。模型也有状态管理和数据持久性处理的功能,例如,基于会话的购物车和电子商务过程也能被Flash网站或者无线联网的应用程序所重用。
        因为模型是自包含的,并且与控制器和视图相分离,所以很容易改变你的应用程序的数据层和业务规则。如果你想把你的数据库从MySQL移植到Oracle,或者改变你的基于RDBMS数据源到LDAP,只需改变你的模型即可。一旦你正确的实现了模型,不管你的数据来自数据库或是LDAP服务器,视图将会正确的显示它们。由于运用MVC的应用程序的三个部件是相互独立,改变其中一个不会影响其它两个,所以依据这种设计思想你能构造良好的松偶合的构件。
        对我来说,控制器的也提供了一个好处,就是可以使用控制器来联接不同的模型和视图去完成用户的需求,这样控制器可以为构造应用程序提供强有力的手段。给定一些可重用的模型和视图,控制器可以根据用户的需求选择模型进行处理,然后选择视图将处理结果显示给用户。

       
      MVC的优点

        ◆低耦合性。视图层和业务层分离,这样就允许更改视图层代码而不用重新编译模型和控制器代码,同样,一个应用的业务流程或者业务规则的改变只需要改动MVC的模型层即可。因为模型与控制器和视图相分离,所以很容易改变应用程序的数据层和业务规则。
        ◆高重用性和可适用性。随着技术的不断进步,现在需要用越来越多的方式来访问应用程序。MVC模式允许你使用各种不同样式的视图来访问同一个服务器端的代码。它包括任何WEB(HTTP)浏览器或者无线浏览器(wap),比如,用户可以通过电脑也可通过手机来订购某样产品,虽然订购的方式不一样,但处理订购产品的方式是一样的。由于模型返回的数据没有进行格式化,所以同样的构件能被不同的界面使用。例如,很多数据可能用HTML来表示,但是也有可能用WAP来表示,而这些表示所需要的仅令是改变视图层的实现方式,而控制层和模型层无需做任何改变。
        ◆较低的生命周期成本。MVC使降低开发和维护用户接口的技术含量成为可能。
        ◆快速的部署。使用MVC模式使开发时间得到相当大的缩减,它使程序员(Java开发人员)集中精力于业务逻辑,界面程序员(HTML和JSP开发人员)集中业务于表现形式上。
        ◆可维护性。分熟视图层和业务逻辑层也使得WEB应用更易于维护和修改。
        ◆有利于软件工程化管理。由于不同的层各司其职,每一层不同的应用具有某些相同的特征,有利于通过工程化、工具化管理程序代码。

       
      MVC的缺点

        MVC的缺点是由于它没有明确的定义,所以完全理解MVC并不是很容易。使用MVC需要精心的计划,由于它的内部原理比较复杂,所以需要花费一些时间去思考。
        你将不得不花费相当可观的时间去考虑如何将MVC运用到你的应用程序,同时由于模型和视图要严格的分离,这样也给调试应用程序到来了一定的困难。每个构件在使用之前都需要经过彻底的测试。一旦你的构件经过了测试,你就可以毫无顾忌的重用它们了。
        根据开发者经验,由于开发者将一个应用程序分成了三个部件,所以使用MVC同时也意味着你将要管理比以前更多的文件,这一点是显而易见的。这样好像我们的工作量增加了,但是请记住这比起它所能带给我们的好处是不值一提。
        MVC并不适合小型甚至中等规模的应用程序,花费大量时间将MVC应用到规模并不是很大的应用程序通常会得不偿失。
        MVC设计模式是一个很好创建软件的途径,它所提倡的一些原则,像内容和显示互相分离可能比较好理解。但是如果你要隔离模型、视图和控制器的构件,你可能需要重新思考你的应用程序,尤其是应用程序的构架方面。如果你肯接受MVC,并且有能力应付它所带来的额外的工作和复杂性,MVC将会使你的软件在健壮性,代码重用和结构方面上一个新的台阶。

       
      开发方式

        Java开发Web Application有几种符合MVC设计模式的开发方式。
        1:Jsp+Servlet+JavaBean(EJB)
        2:Jsp+JavaBean(Controller)+JavaBean(EJB)(Model)
        3:TDK(Turbine,Velocity...)
        4:Xsp 5:Jsp+Struts+JavaBean(EJB)

       
        .NET开发Web Application可以采用:
        1: ASP.NET MVC Framework(ASP.NET MVC Beta版)
        2:MonoRail (RC3)
        php 开发Web Application 可以采用:
        1. Zend framework PHP官方框架
        2. fleaphp/Qeephp 等国内流行框架
        3.CakePHP 等国外流行框架

       
      常见的MVC组件

        Struts: Apache的,最流行的MVC组件
        Struts2 :Apache用Struts 和 WebWork的组合出来的新产品,目前上升势头强劲
        WebWork: 这个可是老牌的MVC组件,后来组合成了Struts2, 不过自身仍在发展
        Spring MVC:SpringFramework自己整合自己Spring的优势推出的MVC组件,用户也不少
        JSF: 这个是一个规范,Sun的和 Apache的都有各自的实现。用户量很大,被众多IDE支持。
        Tapestry: 最彻底的MVC开发框架,丰富的组件资源,重用性很高。组件扮演着控制器Controller的角色,是模式层(Model) 中pure-domain objects和包含有组件的HTML模板之间的媒介。大多数情况下,这种方式应用于页面(页面也 是 Tapestry组件),但是在某些情况中,一个组件拥有自己的模板,包含着更多的组件,并且支持与使用者的互交。页面通过配置一系列属性表达式(Property expressions)连接模式层和表现层。属性表达式使用另外一种开源框架OGNL(Object Graph Navigation Language)。OGNL的开源工程(project)独立于Tapestry,但是在Tapestry中起很重要的作用。OGNL主要的目的在于读取和更新对象的Java Bean属性。
       
       
       
      对MVC的讲解不错
    • Java Web实践专题——基本MVC实例------经典转载

      2009-05-02 03:21:00

           Jsp + Servlet + javabean 

          本文介绍了一个MVC实例,涉及文件如下:

      l         login.jsp——视图部分的输入文件
      l         success.jsp——视图部分的输出文件
      l         failure.jsp——视图部分的输出文件
      l         LoginBean.java——模型部分
      l         LoginServlet.java——控制器部分
      l         web.xml——web应用的配置文件
      下面分别介绍:
      1、login.jsp
      该功能的输入文件,用户首先访问这个文件。主要用于输入用户名和口令。
      代码如下:
      <%@ page contentType="text/html;charset=gb2312"%>
      <script. language="JavaScript">
         function isValidate(form)
         {
                // 得到用户输入的信息
                username = form.username.value;
                userpass = form.userpass.value;
       
                // 判断用户名长度
                if(!minLength(username,6))
                {
                       alert("用户名长度小于6位!");
                       form.username.focus();
                       return false;
                }
                if(!maxLength(username,8))
                {
                       alert("用户名长度大于8位!");
                       form.username.focus();
                       return false;
                }
       
                // 判断口令长度
             if(!minLength(userpass,6))
                {
                       alert("口令长度小于6位!");
                       form.userpass.focus();
                       return false;
                }
                if(!maxLength(userpass,8))
                {
                       alert("口令长度大于8位!");
                       form.userpass.focus();
                       return false;
                }
       
       
                return true;
         }
         // 验证是否满足最小长度
         function minLength(str,length)
         {
                if(str.length>=length)
                       return true;
                else
                       return false;
         }
         // 判断是否满足最大长度
         function maxLength(str,length)
         {
                if(str.length<=length)
                       return true;
                else
                       return false;
         }
      </script>
      <html>
         <head>
            <title>用户登陆</title>
         </head>
         <body>
            <h2>用户登录</h2>
            <form. name="form1" action="login" method="post"
                   nsubmit="return isValidate(form1)">
                   用户名:<input type="text" name="username"> <br>
                   口令:<input type="password" name="userpass"><br>
                   <input type="reset" value="重置">
                   <input type="submit" value="提交"><br>
            </form>
         </body>
      </html>
      代码中提供了客户端验证功能(用户名和口令的长度为6-8位)。验证通过之后会把请求提交给控制器Servlet。
      2、success.jsp
      登录成功之后会跳转到这个界面,界面的代码如下:
      <%@ page contentType="text/html;charset=gb2312"%>
      <html>
         <head>
            <title>登录成功</title>
         </head>
         <body>
            <h2>${sessionScope.username}您好,欢迎登录网上书店!</h2>
         </body>
      </html>
      代码中使用表达式语言把登录后的用户信息显示在街面上。
      3、failure.jsp
      登录失败后会跳转到这个界面,界面的代码如下:
      <%@ page contentType="text/html;charset=gb2312"%>
      <html>
         <head>
            <title>登录失败</title>
         </head>
         <body>
            <h2>用户名或者口令不正确,请<a href="login.jsp">重新登录!</a></h2>
         </body>
      </html>
      代码中提供了一个超链接,能够链接到登录界面。
      4、LoginBean.java
          完成登录功能,这里假设用户名和口令相等表示登录成功。
      package beans;
       
      public class LoginBean {
       
             public boolean validate(String username,String userpass){
                    return username.equals(userpass);
             }
       
      }
      5、LoginServlet.java
      该文件完成控制,主要功能可以描述如下:
      l         从login.jsp获取用户输入的用户名和口令;
      l         创建LoginBean的对象,调用LoginBean的方法validate;
      l         根据方法返回的结果,选择success.jsp或者failure.jsp对用户响应。
      完整的代码如下:
      package servlets;
       
      import java.io.IOException;
      import java.io.PrintWriter;
       
      import javax.servlet.*;
      import javax.servlet.http.*;
       
      import beans.*;
       
      public class LoginServlet extends HttpServlet {
       
             public void doGet(HttpServletRequest request, HttpServletResponse response)
                           throws ServletException, IOException {
                      doPost(request,response);
       
             }
       
             public void doPost(HttpServletRequest request, HttpServletResponse response)
                           throws ServletException, IOException {
       
                   // 获取用户输入的用户ID和口令
                   String username = request.getParameter("username");
                   String userpass = request.getParameter("userpass");
                  
                   // 创建模型对象
                   LoginBean loginBean = new LoginBean();
                 
                   // 调用业务方法进行验证
                   boolean b = loginBean.validate(username,userpass);
       
                   // 要转向的文件
                   String forward;
       
                   // 如果登陆成功,把用户名写入session中,并且转向success.jsp,
                   // 否则转向failure.jsp
                   if(b){
                     
                      // 获取session
                      HttpSession session = (HttpSession)request.getSession(true);
              
                      // 把用户名保存到session中
                     session.setAttribute("username",username);
              
                      // 目标转向文件是success.jsp
                      forward = "success.jsp";
       
                   }else{
       
                      // 目标转向文件是failure.jsp
                      forward = "failure.jsp";
       
                   }
                        
                   // 获取Dispatcher对象
                   RequestDispatcher dispatcher = request.getRequestDispatcher(forward);
                   // 完成跳转
                   dispatcher.forward(request,response);
       
             }
       
      }
      代码中把登录用户的用户信息保存在了session中,在实际应用中同样也是这样处理的。
      6、web.xml
      主要代码是Servlet的配置,代码如下:
      <?xml version="1.0" encoding="UTF-8"?>
      <web-app version="2.4"
             xmlns="http://java.sun.com/xml/ns/j2ee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
             http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
       <servlet>
          <description>This is the description of my J2EE component</description>
          <display-name>This is the display name of my J2EE component</display-name>
          <servlet-name>LoginServlet</servlet-name>
          <servlet-class>servlets.LoginServlet</servlet-class>
       </servlet>
       
       <servlet-mapping>
          <servlet-name>LoginServlet</servlet-name>
          <url-pattern>login</url-pattern>
       </servlet-mapping>
       
      </web-app>
       
       
       
       
       
      请问我在开发中能否用多个类继承ActionServlet类来处理不同页面的请求
      如果可以   在web.xml里是否只需要简单的配置一下自己的ActionServlet就可以了
      比如      
      <servlet>
              <servlet-name> myServlet </servlet-name>
              <servlet-class> xxxx.xxx.myServlet </servlet-class>
      </servlet>
      谢谢
       
      发表于:2007-02-20 12:06:391楼 得分:15

      没有必要这样吧

      你的action   就是用来处理不同页面请求的

      ActionServlet   只是一个总的控制引擎   来分发不同页面请求到不同的action

      你如果想这样做   就没有必要写继承ActionServlet   了

      自己写几个简单的servlet   不就得   了

      那么如果我写了servlet的话   jsp页面到底应该发送个servlet还是action啊   麻烦回复下吧   谢谢

       

      那么如果我写了servlet的话   jsp页面到底应该发送个servlet还是action啊   麻烦回复下吧   谢谢

       
      请求   肯定只能   发给servlet   然后servlet帮你处理请求  
      哦   再通过servlet来调用action吗
      对啊
      那么action中
      public   ActionForward   execute(ActionMapping   mapping,   ActionForm.   form,
      HttpServletRequest   request,   HttpServletResponse   response)
      有四个参数呢   而servlet中的doPost只有2个参数   这样的话是不是要在doPost中先获取mapping   和form.   的值   然后再调用action中的execute方法啊  

      呵呵   问题是我不知道怎么获取mapping   和   form.   的值啊   麻烦讲下吧
       
      不用你写这个servletAction   ,   已经有现成的了,   struts自带.
      org.apache.struts.action.ActionServlet   这个类就是你要写的
      把这个配置进Web.xml就行了,   你找点struts的入门看看吧

           web.xml
       
      <?xml version="1.0" encoding="UTF-8"?>
      <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.4" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee   http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
       

        <servlet>
          <servlet-name>action</servlet-name>
          <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
          <init-param>
            <param-name>config</param-name>
            <param-value>/WEB-INF/struts-config.xml</param-value>
          </init-param>
          <init-param>
            <param-name>debug</param-name>
            <param-value>3</param-value>
          </init-param>
          <init-param>
            <param-name>detail</param-name>
            <param-value>3</param-value>
          </init-param>
          <load-on-startup>0</load-on-startup>
        </servlet>
        <servlet-mapping>
          <servlet-name>action</servlet-name>
          <url-pattern>*.do</url-pattern>
        </servlet-mapping>
      </web-app>
       




       
       
       
       
       
    • 用struts2开发一个简单的登录验证

      2009-05-02 01:18:20

       

        2.2.1 新建WebProject,添加Struts框架支持文件
        2.2.2 视图层V-View的添加   Form&Jsp
        2.2.3 控制层C-Controller的添加  Jsp,Forward
        2.2.4 模型层M-Model的添加  Action ,逻辑功能
        2.2.5 结尾前小小的改动,修改 <html:form. action="/login.do">
        

          2.2.6 struts-config.xml文件
        2.2.7 部署项目并运行

       

      一、开发环境:
      1.  jdk 1.5
      2.  eclipse 3.2.0/3.2.1
      3.  MyEclipse Enterprise Workbench 5.0.1GA
      4.  tomcat 5.5.20

      (环境搭建省略)

      二、第一个struts实例-----登录

        1 添加Struts框架支持文件
             新建一个webProject,
             然后为工程添加 Myeclipse---->Add struts Capabilities
        2 视图层V-View的添加 
         WebRoot节点 New-->Other-->MyEclipse-->Web-Struts-->Struts 1.2
               -->Struts 1.2 Form   -->login.jsp LoginForm.java
        3 控制层C-Controller的添加
             (1)创建JSP文件:
                form节点  new --> JSP(A T) --->true.jsp 和 false.jsp
             (2)创建ActionForward转发:
                 Web-INF-->struts-config.xml-->Design(模式)
                  New-->Forward-->Name:true;Path:/form/true.jsp
                                  Name:false;Path:/form/false.jsp
        4 模型层M-Model的添加 
        (1)WebRoot-->New-->Other-->MyEclipse-->Web-Struts-->Struts 1.2
           -->Struts 1.2 Action -->Use case: login
        (2) Form标签: Name:loginForm    Input Source: /form/login.jsp
        (3) 编写M(模型)层的逻辑功能程序, LoginAction.java
             核心代码:
             package com.yourcompany.struts.action;

             import javax.servlet.http.HttpServletRequest;
             import javax.servlet.http.HttpServletResponse;
             import org.apache.struts.action.Action;
             import org.apache.struts.action.ActionForm;
             import org.apache.struts.action.ActionForward;
             import org.apache.struts.action.ActionMapping;
             import com.yourcompany.struts.form.LoginForm;
            
          public class LoginAction extends Action {

       public ActionForward execute(ActionMapping mapping, ActionForm. form,HttpServletRequest request, HttpServletResponse response) {
        
           LoginForm. loginForm. = (LoginForm) form;
          String username = loginForm.getUsername();
          String password  = loginForm.getPassword();
        
          if((username.equals("abc"))&&(password.equals("xyz"))){
              return mapping.findForward("true");
             }
          else{
              return mapping.findForward("false");
          }
        
       }
      }

         5 结尾前小小的改动
          修改login.jsp中的<html:form. action="/login.do">


        6 struts-config.xml文件
      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://struts.apache.org/dtds/struts-config_1_2.dtd">

      <struts-config>
        <data-sources />
        <form-beans >
          <form-bean name="loginForm" type="com.yourcompany.struts.form.LoginForm" />

        </form-beans>

        <global-exceptions />
        <global-forwards >
          <forward name="true" path="/form/true.jsp" />
          <forward name="false" path="/form/false.jsp" />

        </global-forwards>

        <action-mappings >
          <action
            attribute="loginForm"
            input="/form/login.jsp"
            name="loginForm"
            path="/login"
            scope="request"
            type="com.yourcompany.struts.action.LoginAction" />

        </action-mappings>

        <message-resources parameter="com.yourcompany.struts.ApplicationResources" />
      </struts-config>


        7 部署项目并运行

    • 转载季风的空间2

      2009-04-30 23:59:46

      我的QTP学习方法及总结(一)51Testing软件测试网G"Y ~)q T7O Z8I

                              季风51Testing软件测试网&Sv p~ m

       

      ^2]F)XHl2Y-@6v|202848

      引子:51Testing软件测试网7[:HO z ~q

      借项目的空挡期来总结一下自己学习、使用QTP的一下方法,以及使用过程中的一些心得。以下内容仅是针对QTP一些基本知识以及我自己如何学习QTP做介绍,作为自己的一个阶段总结。对于QTP高手可以忽略本内容或是批评指正。

      OKa.^$U+_)D)u#]202848

       51Testing软件测试网*] T#c2Ce'B*BS/B

      QTP是目前市场上占有率最高的一款自动化测试工具,也是每一位测试工作者最想掌握的工具之一。QTP目前最主要的应用是用于回归测试、版本验证测试阶段。它本身是针对系统界面上的元素进行识别、操作,达到测试系统功能的目的。因此,自动化测试启动的时机或者说QTP开始介入的时机就要受到系统开发进度的制约。只有当系统的界面元素不会频繁的变化、系统功能基本稳定,已经通过一至两轮的手工测试,确定系统不会存在重大缺陷时,才可以考虑自动化的实施。这里说的实施主要侧重脚本的开发,其他如测试方案、开发规范、参数定义等内容可以提前制定。有的个别大公司可能介入会比较早,界面出来,就开始着手脚本的开发。但这种方式要有严格的开发、测试规范与之配套,并且每一环节的人员严格按规范、规则进行,否则脚本后期的维护将是非常头疼的一件事情。

      MK0}3B*s+T202848

       

      'l\.k/Y/?U8{[202848

      一、   第一次与QTP伤心的亲密接触51Testing软件测试网O*q-Py'E

       51Testing软件测试网 _a^+]zy_

      第一次与QTP的亲密接触是在07年,当时进入一家做GIS地理信息系统)系统的公司。当时公司想要引入自动化测试,由副总牵头,测试经理负责我和另一位MM具体实施。这也具备了实施自动化测试的一个辅助条件,公司高层的重视与支持。但对于没有相关技术积累,相关人员技术缺乏的条件下,为我这次自动化测试的失败埋下了伏笔。最终这次确实是一次失败的经历,失败的其他原因略过,接下来主要说一下过程中我是怎么学习QTP的。

      *Z7A W8Wh j _?202848

       51Testing软件测试网-z&iI*mO

      QTP的运行原理、对象识别机制

      (P*o(Z4E:fs0@ g:T202848

       51Testing软件测试网Wm.DqQdg o`$J"^

      作为一个QTP的使用者,首先要搞明白它的运行原理,识别对象的机制。这是以后掌握其他技术的根本,不管是录制方式还是手写代码这个都是非常重要的。

      j7Gh ]3hW|f202848

       

      7Z] |v p(oM']202848

      QTP是一款基于语言的工具,而LR是基于协议的。具体说,就是QTP针对不同的语言提供不同插件去识别对象,默认提供ActiveXVisual BasicWeb插件,其他.netjava插件单独安装。每一种插件提供了针对不同对象的识别机制,也就是提供了对象默认的识别属性。所以在运行QTP前,首先确定加载哪个addin,否则对象就不能正确识别,出现很多奇怪的问题。不加载正确的addin,最直接就是表现在代码上。以下是点击google主页上的搜索按钮的区别51Testing软件测试网N!{An3q"b/`]

       

      {(cK9@kO,g ?PA};B202848

      加载web插件的正确代码:Browser("Google").Page("Google").WebButton("Google搜索").Click51Testing软件测试网+rWH5en2E

      不加载web插件:Window("Windows Internet Explorer").WinObject("Internet Explorer_Server").Click 547,21751Testing软件测试网Bg{H$b9Y-M

       51Testing软件测试网X3h%W/R Q#uj

      这是典型的对象没有正确识别的问题。包括对象类型、识别属性、事件方法都会出现问题,这样会导致脚本的开发,维护,运行,阅读等等一系列问题。51Testing软件测试网lqVcA$eL4m

       51Testing软件测试网d_MS_m

      再说QTP的对象识别,加载正确addinQTP会定义一套针对具体类型对象的默认识别属性。通过Tools--Object Identification可以查看,修改,增加默认的识别属性。更改后即时生效,但对之前的对象不起作用。如下图,这里就是决定QTP如何识别对象的地方。包括识别对象的强制属性、辅助属性、智能识别、顺序表示符的配置。特别指出,Browser对象有一个CreationTime顺序表示符,这是一个非常有用的属性,它可以根据IE打开的顺序去识别web页,而不必指定其他属性,合理加以利用会给web脚本带来很大好处。

      F;OY{(Hc*k)e202848

       

      #a[N-^~%~:G,G m9LL202848

      51Testing软件测试网_ EqQ[ XJc

      V;aLC;bI1KQ7D202848

       51Testing软件测试网.Z,Tk|3X

      上图的配置,这些属性都会反应到对象库中。在录制的过程中,QTP会抓取对应属性的属性值,并映射到对象库(Object Repository)。如图,上图配置的属性均在对象库中反应出来了。这里的保存的对象,也就是我们说的TOTest Object。脚本一旦开发完毕,这些对象及对应的属性均保持不变,作为识别对象的基线。

      o } `"F!Guc6Wf202848

       

      SYp7o;q"z7g202848

      51Testing软件测试网KER6@LBk|

      51Testing软件测试网+W U UFIqO9j

       

      }5nmR'Zmw-X` x'h202848

      回放过程中,运行时系统的对象称作RORun-time Object)。QTP以对象库中保存对象的属性及属性值为标准,去匹配运行时系统的对象,如果匹配成功则正确识别对象并执行相应操作,否则会有提示信息。下图也是最为常见的对象不识别。解决这个问题,就是需要在录制之前配置好Object Identification中各种对象的识别属性,做到能够唯一标识对象的目标。这样就会减少脚本出错的几率。

      ? p#La!v'L0l202848

       

      X%W qOiOw9T202848

      1kcQfb/}l202848

      6^n j-^!Z9ao202848

       51Testing软件测试网["vr$Bh9^-I3U

      在对象库中也可以增加(加号)、删除(叉号)、修改属性及属性值。这是对象库强大的一处体现。Value可以修改正则表达式,可以参数化。支持随机值,DataTable取值,环境变量取值,这就让对象识别变的很灵活,甚至可以动态的去识别对象。

      2i(N GO;tvs*s#C8v202848

       51Testing软件测试网W0`0h-G G k D4`

      关于另一个常用的就是描述性编程,这个概念是QTP独有的。通过这个机制,使对象识别更加灵活。在开发脚本过程中,可以不依赖默认识别属性,通过指定对象的属性及相应的属性值去识别对象。这样可以使脚本脱离对象库,也就是不依赖于TO对象,省去维护对象库的步骤,但原理还是一样的。灵活运用描述性编程可以使脚本的重用性、稳定性、维护以及脚本的开发变的简单,而且直观易于阅读,简化团队成员之间的协作。但如果单纯的把录制的脚本改成描述性的,这样做不仅没有好处,还会给你的开发的工作量、脚本的维护带来很大的麻烦。如何灵活使用描述性编程,这里不作详述,原则是使脚本开发过程简单,维护工作量最小。

      J*n1C1X9j*{ Z9y202848

       51Testing软件测试网Bb*Q0| M}

      以上内容,从理论到具体的实践,都是掌握QTP的基础,也是很重要的环节。这部分内容的掌握程度,直接影响以后使用QTP开展测试的深度以及使用QTP的灵活度。所以这部分内容,建议多花时间把它搞透、搞明白。

      :Y(AI$c,F1~S202848

       

       

    • 以下转自 季风的测试生活 http://www.51testing.com/?92935

      2009-04-30 22:53:05

      我的QTP学习方法及总结(一)51Testing软件测试网G"Y ~)q T7O Z8I

                              季风51Testing软件测试网&Sv p~ m

       

      ^2]F)XHl2Y-@6v|202848

      引子:51Testing软件测试网7[:HO z ~q

      借项目的空挡期来总结一下自己学习、使用QTP的一下方法,以及使用过程中的一些心得。以下内容仅是针对QTP一些基本知识以及我自己如何学习QTP做介绍,作为自己的一个阶段总结。对于QTP高手可以忽略本内容或是批评指正。

      OKa.^$U+_)D)u#]202848

       51Testing软件测试网*] T#c2Ce'B*BS/B

      QTP是目前市场上占有率最高的一款自动化测试工具,也是每一位测试工作者最想掌握的工具之一。QTP目前最主要的应用是用于回归测试、版本验证测试阶段。它本身是针对系统界面上的元素进行识别、操作,达到测试系统功能的目的。因此,自动化测试启动的时机或者说QTP开始介入的时机就要受到系统开发进度的制约。只有当系统的界面元素不会频繁的变化、系统功能基本稳定,已经通过一至两轮的手工测试,确定系统不会存在重大缺陷时,才可以考虑自动化的实施。这里说的实施主要侧重脚本的开发,其他如测试方案、开发规范、参数定义等内容可以提前制定。有的个别大公司可能介入会比较早,界面出来,就开始着手脚本的开发。但这种方式要有严格的开发、测试规范与之配套,并且每一环节的人员严格按规范、规则进行,否则脚本后期的维护将是非常头疼的一件事情。

      MK0}3B*s+T202848

       

      'l\.k/Y/?U8{[202848

      一、   第一次与QTP伤心的亲密接触51Testing软件测试网O*q-Py'E

       51Testing软件测试网 _a^+]zy_

      第一次与QTP的亲密接触是在07年,当时进入一家做GIS地理信息系统)系统的公司。当时公司想要引入自动化测试,由副总牵头,测试经理负责我和另一位MM具体实施。这也具备了实施自动化测试的一个辅助条件,公司高层的重视与支持。但对于没有相关技术积累,相关人员技术缺乏的条件下,为我这次自动化测试的失败埋下了伏笔。最终这次确实是一次失败的经历,失败的其他原因略过,接下来主要说一下过程中我是怎么学习QTP的。

      *Z7A W8Wh j _?202848

       51Testing软件测试网-z&iI*mO

      QTP的运行原理、对象识别机制

      (P*o(Z4E:fs0@ g:T202848

       51Testing软件测试网Wm.DqQdg o`$J"^

      作为一个QTP的使用者,首先要搞明白它的运行原理,识别对象的机制。这是以后掌握其他技术的根本,不管是录制方式还是手写代码这个都是非常重要的。

      j7Gh ]3hW|f202848

       

      7Z] |v p(oM']202848

      QTP是一款基于语言的工具,而LR是基于协议的。具体说,就是QTP针对不同的语言提供不同插件去识别对象,默认提供ActiveXVisual BasicWeb插件,其他.netjava插件单独安装。每一种插件提供了针对不同对象的识别机制,也就是提供了对象默认的识别属性。所以在运行QTP前,首先确定加载哪个addin,否则对象就不能正确识别,出现很多奇怪的问题。不加载正确的addin,最直接就是表现在代码上。以下是点击google主页上的搜索按钮的区别51Testing软件测试网N!{An3q"b/`]

       

      {(cK9@kO,g ?PA};B202848

      加载web插件的正确代码:Browser("Google").Page("Google").WebButton("Google搜索").Click51Testing软件测试网+rWH5en2E

      不加载web插件:Window("Windows Internet Explorer").WinObject("Internet Explorer_Server").Click 547,21751Testing软件测试网Bg{H$b9Y-M

       51Testing软件测试网X3h%W/R Q#uj

      这是典型的对象没有正确识别的问题。包括对象类型、识别属性、事件方法都会出现问题,这样会导致脚本的开发,维护,运行,阅读等等一系列问题。51Testing软件测试网lqVcA$eL4m

       51Testing软件测试网d_MS_m

      再说QTP的对象识别,加载正确addinQTP会定义一套针对具体类型对象的默认识别属性。通过Tools--Object Identification可以查看,修改,增加默认的识别属性。更改后即时生效,但对之前的对象不起作用。如下图,这里就是决定QTP如何识别对象的地方。包括识别对象的强制属性、辅助属性、智能识别、顺序表示符的配置。特别指出,Browser对象有一个CreationTime顺序表示符,这是一个非常有用的属性,它可以根据IE打开的顺序去识别web页,而不必指定其他属性,合理加以利用会给web脚本带来很大好处。

      F;OY{(Hc*k)e202848

       

      #a[N-^~%~:G,G m9LL202848

      51Testing软件测试网_ EqQ[ XJc

      V;aLC;bI1KQ7D202848

       51Testing软件测试网.Z,Tk|3X

      上图的配置,这些属性都会反应到对象库中。在录制的过程中,QTP会抓取对应属性的属性值,并映射到对象库(Object Repository)。如图,上图配置的属性均在对象库中反应出来了。这里的保存的对象,也就是我们说的TOTest Object。脚本一旦开发完毕,这些对象及对应的属性均保持不变,作为识别对象的基线。

      o } `"F!Guc6Wf202848

       

      SYp7o;q"z7g202848

      51Testing软件测试网KER6@LBk|

      51Testing软件测试网+W U UFIqO9j

       

      }5nmR'Zmw-X` x'h202848

      回放过程中,运行时系统的对象称作RORun-time Object)。QTP以对象库中保存对象的属性及属性值为标准,去匹配运行时系统的对象,如果匹配成功则正确识别对象并执行相应操作,否则会有提示信息。下图也是最为常见的对象不识别。解决这个问题,就是需要在录制之前配置好Object Identification中各种对象的识别属性,做到能够唯一标识对象的目标。这样就会减少脚本出错的几率。

      ? p#La!v'L0l202848

       

      X%W qOiOw9T202848

      1kcQfb/}l202848

      6^n j-^!Z9ao202848

       51Testing软件测试网["vr$Bh9^-I3U

      在对象库中也可以增加(加号)、删除(叉号)、修改属性及属性值。这是对象库强大的一处体现。Value可以修改正则表达式,可以参数化。支持随机值,DataTable取值,环境变量取值,这就让对象识别变的很灵活,甚至可以动态的去识别对象。

      2i(N GO;tvs*s#C8v202848

       51Testing软件测试网W0`0h-G G k D4`

      关于另一个常用的就是描述性编程,这个概念是QTP独有的。通过这个机制,使对象识别更加灵活。在开发脚本过程中,可以不依赖默认识别属性,通过指定对象的属性及相应的属性值去识别对象。这样可以使脚本脱离对象库,也就是不依赖于TO对象,省去维护对象库的步骤,但原理还是一样的。灵活运用描述性编程可以使脚本的重用性、稳定性、维护以及脚本的开发变的简单,而且直观易于阅读,简化团队成员之间的协作。但如果单纯的把录制的脚本改成描述性的,这样做不仅没有好处,还会给你的开发的工作量、脚本的维护带来很大的麻烦。如何灵活使用描述性编程,这里不作详述,原则是使脚本开发过程简单,维护工作量最小。

      J*n1C1X9j*{ Z9y202848

       51Testing软件测试网Bb*Q0| M}

      以上内容,从理论到具体的实践,都是掌握QTP的基础,也是很重要的环节。这部分内容的掌握程度,直接影响以后使用QTP开展测试的深度以及使用QTP的灵活度。所以这部分内容,建议多花时间把它搞透、搞明白。

      :Y(AI$c,F1~S202848

       

       

       

    • kursk的个人空间转载: 武汉如何一年后拿8000元的月薪?

      2009-04-30 22:07:48

      刚才在网上找工作,不仅纳闷在武汉好公司在哪里?

      一些公司的介绍本身就很马虎,却在招聘信息中要求招聘人员“积极主动”,一般说这类要求的都是废话,谁找工作时不想表现得“积极主动”?感觉看外企招聘要求就非常正规,要求什么工作岗位能力说得很清楚,对编程语言要求、对数据库要求、对框架要求程度根据工作岗位性质不同,不像国内企业,什么都要求精通、熟悉,但是实际却用不上,工资还只开2000多,纯粹是浪费大家的时间。

      通过招聘信息,可以发现好企业对员工的培训也很重视,通常有培训员工的计划和实例。但是私企就差很多,简直卖给他就像是准备剥削的!

      我想,看来以后还是只能找外企、有实业背景的国企为依靠,私企民企是万万不可取的,多是些想捞一票就拉倒,不想长久发展的公司——至少在武汉是这样。

      有人说武汉的外企主要是在圈子内靠互相推荐,不知道如何才能进“圈子”,不过我也没那么多时间来套圈子。说到底,还是要靠自己努力,先把今年年底拿了OCP,到时候再看看吧。

      从工资体系上看,高薪的工资很多还是要研究生门槛的,但是我不想浪费时间去考,再说也觉得读个研究生对自己实力不会有大的提升,相反把时间浪费在给导师打工上了——不过据说导师可以帮助推荐好工作,但是我只能走另一条路了。

      从工作要求上来,JAVA开发人员还是要求那些,对常用框架熟悉,熟悉数据库——我选择ORACLE,有项目经验和行业背景,再就是什么团队精神等等。其实真正还是经验,java语言就那么个语法,你学我学能有什么区别、oracle等数据库也是,都可以学——比如paperOcp,真正有用的是经验,有经验就能解决问题。

      但是经验是要工作来积累的,没有相应的工作环境和项目体验,没有可供解决的问题,哪来的工作经验?这点上,也许就是我还留在现在公司的原因,虽然工资少,但是至少提供工作经验,还有些我可以学习的东西。

      我计划明年2月份找一份8K的工作,但是限于武汉市,想想应该也太难,只要今年能完成几项任务:
      1、拿到OCP;
      2、通过近半年时间的工作积累,熟悉常用开发框架——精通我不敢说,那太困难,而且关键是这个玩艺要项目和时间来积累;
      3、掌握一套好的程序设计的思想和分析模式,最近一段时间工作让我感觉这个东西对程序员来说太重要了,其实写程序大家都会写,但是怎么写才能满足功能、提高性能,又好维护,就这这套方法论了。其实我想作为程序员没有别的什么可以比出高下,类、方法查API就可以了,但是这个却是时间和经验积累的。

      大致这样吧,今天总结了一下,留给以后忘记时提醒自己。

       

       

      引用 删除 假装不在   /   2009-04-28 12:26:18

      原帖由jifeng于2009-04-28 09:48:06发表
      经验是玩出来的。 任何一款工具如果玩的时间不到一年,那经验几乎可以忽略不计!


      赞同
      季风的测试生活 引用 删除 jifeng   /   2009-04-28 09:48:06
      经验是玩出来的。 任何一款工具如果玩的时间不到一年,那经验几乎可以忽略不计!
      -DNA's home 引用 删除 huoxingyinzi   /   2009-04-27 13:55:25
      有想法,支持!

       

    • java数组的定义与使用方法

      2009-04-30 16:10:06

      数组是有序数据的集合,数组中的每个元素具有相同的数组名和下标来唯一地确定数组中的元素。

      §5.1一维数组

      一、一维数组的定义

      type arrayName[];

      其中类型(type)可以为Java中任意的数据类型,包括简单类型组合类型,数组名arrayName为一个合法的标识符,[]指明该变量是一个数组类型变量。例如:

      int intArray[];

      声明了一个整型数组,数组中的每个元素为整型数据。与C、C++不同,Java在数组的定义中并不为数组元素分配内存,因此[]中不用指出数组中元素个数,即数组长度,而且对于如上定义的一个数组是不能访问它的任何元素的。我们必须为它分配内存空间,这时要用到运算符new,其格式如下:

      arrayName=new type[arraySize];

      其中,arraySize指明数组的长度。如:

      intArray=new int[3];

      为一个整型数组分配3个int型整数所占据的内存空间。

      通常,这两部分可以合在一起,格式如下:

      type arrayName=new type[arraySize];

      例如:

      int intArray=new int[3];

      二、一维数组元素的引用

      定义了一个数组,并用运算符new为它分配了内存空间后,就可以引用数组中的每一个元素了。数组元素的引用方式为:

      arrayName[index]

      其中:index为数组下标,它可以为整型常数或表达式。如a[3],b[i](i为整型),c[6*I]等。下标 从0开始,一直到数组的长度减1。对于上面例子中的in-tArray数来说,它有3个元素,分别为:

      intArray[0],intArray[1],intArray[2]。注意:没有intArray[3]。

      另外,与C、C++中不同,Java对数组元素要进行越界检查以保证安全性。同时,对于每个数组都有一个属性length指明它的长度,例如:intArray.length指明数组intArray的长度。

      例5.1

      public class ArrayTest{
      public static void main(String args[]){
      int i;
      int a[]=newint[5];
      for(i=0;i<5;i++)
      a[i]=i;
      for(i=a.length-1;i>=0;i--)
      System.out.println("a["+i+"]="+a[i]);
      }
      }

      运行结果如下:

      C:\>java ArrayTest

      a[4]=4
      a[3]=3
      a[2]=2
      a[1]=1
      a[0]=0

      该程序对数组中的每个元素赋值,然后按逆序输出。

      三、一维数组的初始化

      对数组元素可以按照上述的例子进行赋值。也可以在定义数组的同时进行初始化。

      例如:

      int a[]={1,2,3,4,5};

      用逗号(,)分隔数组的各个元素,系统自动为数组分配一定空间。

      与C中不同,这时Java不要求数组为静态(static)。

      四、一维数组程序举例:

      例5.2Fibonacci数列

      Fibonacci数列的定义为:

      F1=F2=1,Fn=Fn-1+Fn-2(n>=3)

      public classFibonacci{
      public static void main(String args[]){
      int i;
      int f[]=new int[10];
      f[0]=f[1]=1;
      for(i=2;i<10;i++)
      f[i]=f[i-1]+f[i-2];
      for(i=1;i<=10;i++)
      System.out.println("F["+i+"]="+f[i-1]);
      }
      }

      运行结果为:

      C:\>java Fibonacci

      F[1]=1
      F[2]=1
      F[3]=2
      F[4]=3
      F[5]=5
      F[6]=8
      F[7]=13
      F[8]=21
      F[9]=34
      F[10]=55

      例5.3冒泡法排序(从小到大)

      冒泡法排序对相邻的两个元素进行比较,并把小的元素交到前面。

      public class BubbleSort{
      public static void main(String args[]){
      int i,j;
      int intArray[]={30,1,-9,70,25};
      int l=intArray.length;
      for(i=0;i<l-1;i++)
      for(j=i+1;j<l;j++)
      if(intArray[i]>intArray[j]){
      int t=intArray[i];
      intArray[i]=intArray[j];
      intArray[j]=t;
      }
      for(i=0;i<l;i++)
      System.out.println(intArray[i]+"");
      }
      }

      运行结果为:

      C:\>java BubbleSort
      -9
      1
      25
      30
      70

      §5.2多维数组

      与C、C++一样,Java中多维数组被看作数组的数组。例如二维数组为一个特殊的一维数组,其每个元素又是一个一维数组。下面我们主要以二维数为例来进行说明,高维的情况是类似的。

      一、二维数组的定义

      二维数组的定义方式为:

      type arrayName[][];

      例如:

      int intArray[][];

      与一维数组一样,这时对数组元素也没有分配内存空间,同要使用运算符new来分配内存,然后才可以访问每个元素。

      对高维数组来说,分配内存空间有下面几种方法:

      1直接为每一维分配空间,如:

      int a[][]=new int[2][3];

      2从最高维开始,分别为每一维分配空间,如:

      int a[][]=new int[2][];
      a[0]=new int[3];
      a[1]=new int[3];

      完成1中相同的功能。这一点与C、C++是不同的,在C、C++中必须一次指明每一维的长度。

      二、二维数组元素的引用

      对二维数组中每个元素,引用方式为:arrayName[index1][index2] 其中index1、index2为下标,可为整型常数或表达式,如a[2][3]等,同样,每一维的下标都从0开始。

      三、二维数组的初始化

      有两种方式:

      1直接对每个元素进行赋值。

      2在定义数组的同时进行初始化。

      如:int a[][]={{2,3},{1,5},{3,4}};

      定义了一个3×2的数组,并对每个元素赋值。

      四、二维数组举例:

      例5.4矩阵相乘

      两个矩阵Am×n、Bn×l相乘得到Cm×l,每个元素Cij=aik*bk (i=1..m,n=1..n)

      public class MatrixMultiply{
      public static void main(String args[]){
      int i,j,k;
      int a[][]=new int[2][3];
      int b[][]={{1,5,2,8},{5,9,10,-3},{2,7,-5,-18}};
      int c[][]=new int[2][4];
      for(i=0;i<2;i++)
      for(j=0;j<3;j++)
      a[i][j]=(i+1)*(j+2);
      for(i=0;i<2;i++){
      for(j=0;j<4;j++){
      c[i][j]=0;
      for(k=0;k<3;k++)
      c[i][j]+=a[i][k]*b[k][j];
      }
      }
      System.out.println("\n***MatrixA***");
      for(i=0;i<2;i++){
      for(j=0;j<3;j++)
      System.out.print(a[i][j]+"");
      System.out.println();
      }
      System.out.println("\n***MatrixB***");
      for(i=0;i<3;i++){
      for(j=0;j<4;j++)
      System.out.print(b[i][j]+"");
      System.out.println();
      }
      System.out.println("\n***MatrixC***");
      for(i=0;i<2;i++){
      for(j=0;j<4;j++)
      System.out.print(c[i][j]+"");
      System.out.println();
      }
      }
      }

      其结果为:

      C:\>java MatrixMultiply

      for(j=0;j<4;j++)
      System.out.print(c[i][j]+"");
      System.out.println();
      }
      }
      }

      其结果为:

      C:\>java MatrixMultiply

      ***MatrixA***
      2 3 4
      4 6 8
      ***MatrixB***
      1 5 2 8
      5 9 10 -3
      2 7 -5 -18
      ***MatrixC***
      25 65 14 -65
      50 130 28 -130


      如果你学过线性代数,应该可以比较好地理解多维数组。
      多维数组和矩阵结合紧密。

      a[i][j]就是第i-1行的第j-1列的元素,因为下标是从0开始的。
      比如:
      一个数组:1 2 3
      4 5 6
      a[0][0]=1 a[0][1]=2 a[0][2]=3
      a[1][0]=3 a[1][1]=5 a[1][2]=6
      我推荐你看几本书:
      1.Thinking in Java
      2.Java 2 核心技术
      3.Java2实用教程
      4.面向对象程序设计与java语言

    • 庆祝51Testing软件测试网成立五周年

      2009-04-30 12:59:39

      51Testing网站五周年啦!
      希望论坛越来越有活力,希望大家的测试水平不断提高。
      愿与各位同仁共同学习、进步!


      51Testing软件测试网http://www.51testing.com
    • 如何用java随机生成一个汉字?

      2009-04-30 11:13:44

       

      思路:生成一个随机数----转换成16进制或其他码值------转换成汉字字符---显示

      生成一个随机数,   在0x5000到0x9000之间的都是汉字

       

      我只讲原理,但具体到你使用什么编程工具是你自己的问题。

      任何编程开发工具都有随机数这个概念,而我们的区位码正好是由四位数字组成的,区位输入法可以输入GB2312-80的全部汉字,因此只需要随机产生一个四位的数字,并把这个数字换成区位码就成输出一个汉字,至于区位码中的空数你自己去查一查,用一段假设语句把它们排除掉。

      =============================================================

       区码:   汉字内码高位   -   0xA0  
        位码:   汉字内码低位   -   0xa0

      区号:01-87,汉字从16区开始,前15区为各种符号  
        位号:01-94  
        第一个汉字“啊”的区号为16,位号为01  
      续:  
        两字节内码:区号+0xA0   ,   位号+0xa0  
        如:第一个汉字“啊”的区号为16,位号为01,则其内码为     0xb0,0xa1

        ======>  区号:16---87 位号:01---94

       

      =========================================================

      JAVA 区位码于汉字的互换

      import java.io.UnsupportedEncodingException;

      public class Test {
      // 发代码之前先转段预备知识
      //
      // 计算机处理汉字信息的前提条件是对每个汉字进行编码,这些编码统称为汉字编码。汉字信息在系统内传送的过程就是汉字编码转换的过程。
      // 汉字交换码:汉字信息处理系统之间或通信系统之间传输信息时,对每一个汉字所规定的统一编码,我国已指定汉字交换码的国家标准“信息交换用汉字编码字符集——基本集”,代号为GB
      // 2312—80,又称为“国标码”。
      // 国标码:所有汉字编码都应该遵循这一标准,汉字机内码的编码、汉字字库的设计、汉字输入码的转换、输出设备的汉字地址码等,都以此标准为基础。GB
      // 2312—80就是国标码。该码规定:一个汉字用两个字节表示,每个字节只有7位,与ASCII码相似。
      // 区位码:将GB
      // 2312—80的全部字符集组成一个94×94的方阵,每一行称为一个“区”,编号为0l~94;每一列称为一个“位”,编号为0l~94,这样得到GB
      // 2312—80的区位图,用区位图的位置来表示的汉字编码,称为区位码。
      // 机内码:为了避免ASCII码和国标码同时使用时产生二义性问题,大部分汉字系统都采用将国标码每个字节高位置1作为汉字机内码。这样既解决了汉字机内码与西文机内码之间的二义性,又使汉字机内码与国标码具有极简单的对应关系。
      // 汉字机内码、国标码和区位码三者之间的关系为:区位码(十进制)的两个字节分别转换为十六进制后加20H得到对应的国标码;机内码是汉字交换码(国标码)两个字节的最高位分别加1,即汉字交换码(国标码)的两个字节分别加80H得到对应的机内码;区位码(十进制)的两个字节分别转换为十六进制后加A0H得到对应的机内码。

      public String bytes2HexString(byte b) {
      return bytes2HexString(new byte[] { b });
      }

      // 汉字转换成区位码
      public String bytes2HexString(byte[] b) {
      String ret = "";
      for (int i = 0; i < b.length; i++) {
      String hex = Integer.toHexString(b[i] & 0xFF);
      if (hex.length() == 1) {
      hex = '0' + hex;
      }
      ret += hex.toUpperCase();
      }
      return ret;
      }

      // 汉字转换成区位码
      public String getString(String chinese) {
      byte[] bs;
      String s = "";
      try {
      bs = chinese.getBytes("GB2312");

      for (int i = 0; i < bs.length; i++) {
      int a = Integer.parseInt(bytes2HexString(bs[i]), 16);
      s += (a - 0x80 - 0x20) + "";
      }
      } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
      }
      return s;
      }

      // 区位码转换成汉字
      public String CodeToChinese(String code) {
      String Chinese = "";
      for (int i = 0; i < code.length(); i += 4) {
      byte[] bytes = new byte[2];
      String lowCode = code.substring(i, i + 2);
      int tempLow = Integer.parseInt(lowCode);
      tempLow += 160;
      bytes[0] = (byte) tempLow;
      String highCode = code.substring(i + 2, i + 4);
      int tempHigh = Integer.parseInt(highCode);
      tempHigh += 160;
      bytes[1] = (byte) tempHigh;
      String chara = new String(bytes);
      Chinese += chara;
      }
      return Chinese;
      }

      // 测试
      public static void main(String[] args) throws Exception {
      String str = "创";
      Test test = new Test();
      String s = test.getString(str);
      System.out.println(s);
      String a = test.CodeToChinese(s);
      System.out.println(a);
      }

      }

       

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

      Excel2007生成随机姓名

      在实际生活中,我们经常需要列出一系列随机的数据用以辅助说明问题。使用Excel2007的RAND函数,可以很方便地生成随机数字;可是,当需要用到人员姓名的时候则很头痛,使用张1、张2、张3之类的名字太难看了,而若想要生成看上去“真实”的随机姓名,笔者找遍百度也没有发现合适的,只好自己来实现了。
        国人的姓名,是分别由姓和名两部分组成,因此我们只需要生成随机的姓,再生成随机的名,组合即可。
        常用的姓氏比较好查,在百度(
      www.baidu.com)里搜索“百家姓”就可以找到。打开Excel2007新建一个工作簿,将工作表Sheet2更名为“常用姓氏表”,把这些常见的姓氏复制到其中,并整理为一列。笔者找到的常用姓氏占单元格A2:A158共计157个。(提示:可以使用“数据-数据工具-分列”菜单项方便将数据拆分)
        用作名字的字词比较多,在百度里搜索“常用名”、“常用字”或“常用名字”等都找不倒合适的数据。最终搜索关键词“取名常用字”,找到了大量用在名字中的单字和词。同样的方法将其添加到的Sheet3中,整理为一列,并将工作表更名为“常用名表”。笔者找到的常用名字占单元格A2:A424共计423个。
        将工作表Sheet1更名为“随机姓名”,在A2
      单元格输入公式“=INDEX(常用姓氏表!$A$2:$A$158,INT(RAND()*157+1))&INDEX(常用名表!$A$2:$A$424,INT(RAND()*423 +1))”,然后将此公式复制到表格的其他位置,即可生成批量随机姓名。
        简单介绍一下公式各部分的含义:
        1、RAND函数,可以得到一个介于0和1之间的随机小数。
        2、INT函数,将得到数字的整数部分。
        3、生成指定范围内随机整数通常采用如下公式“INT(RAND()*(上界-下界+1)+下界”。例如“INT(RAND()*157+1)”可以生成1~157之间(含)的随机整数。
        4、INDEX函数可以得到指定范围内指定位置的数值。
        5、“&”符号,可以将两段文字连接起来生成一段新的文字。
        如此,把生成的随机姓名放到数据之中,数据也就显得正规多了。
      参考:http://www.officeba.com.cn/article/htmldata/detail/2008/7/3/2285.html

       

    • Java判断时间,Email,手机号是否为正确格式

      2009-04-30 10:10:29

    • JAVA的整型与字符串相互转换(简单实用)

      2009-04-30 09:29:37

      JAVA的整型与字符串相互转换

      int i = Integer.parseInt([String]);

      String s = Integer.toString([int]);


      1如何将字串 String 转换成整数 int? 
       
        有两个方法:
       
      1). int i = Integer.parseInt([String]); 或 
            int i = Integer.parseInt([String],[int radix]);
       
      2). int i = Integer.valueOf(my_str).intValue();
       
      注: 字串转成 Double, Float, Long 的方法大同小异.
       

      2 如何将整数 int 转换成字串 String ?
       

         有叁种方法:
       
      1.) String s = String.valueOf(i);
       
      2.) String s = Integer.toString(i);
       
      3.) String s = "" + i;
       
      注: Double, Float, Long 转成字串的方法大同小异.

       

      <%@ page contentType="text/html;charset=gb2312"%>
      <html>
      <body>
      <form. action="" method="post">
      <input type="text" name="name">加
      <input type="text" name="pass"><br>
      <input type="submit" value="提交">
      </form>
      <%
      String name=request.getParameter("name");
      String pass=request.getParameter("pass");
      if(name==null){name="1";}
      if(pass==null){pass="2";}
      int a = Integer.valueOf(name).intValue();
      int b = Integer.valueOf(pass).intValue();
      out.print(a+b);
      %>
      </body>
      </html> 

    • 身份证号编码规则[转]

      2009-04-30 09:26:13

      15位的身份证号
      dddddd yymmdd xx p
      18位的身份证号

      dddddd yyyymmdd xx p y
      其中dddddd为地址码(省地县三级)18位中的和15位中的不完全相同

      yyyymmdd yymmdd 为出生年月日

      xx顺号类编码

      p性别

      18位中末尾的y为校验码,在网上可以找到算法

      将前17位的ascii码值经位移、异或运算结果不在0-9的令其为
      x

      现将“18位身份证标准提供如下:

      18位身份证标准在国家质量技术监督局于199971实施

      GB11643-1999《公民身份号码》中做了明确的规定。

      GB11643-1999《公民身份号码》为GB11643-1989《社会保障

      号码》的修订版,其中指出将原标准名称社会保障号码更名

      公民身份号码,另外GB11643-1999《公民身份号码》从实

      施之日起代替GB11643-1989

      GB11643-1999《公民身份号码》主要内容如下:

      一、范围

      该标准规定了公民身份号码的编码对象、号码的结构和表现

      形式,使每个编码对象获得一个唯一的、不变的法定号码。

      二、编码对象

      公民身份号码的编码对象是具有中华人民共和国国籍的公民。

      三、号码的结构和表示形式

      1、号码的结构

      公民身份号码是特征组合码,由十七位数字本体码和一位校

      验码组成。排列顺序从左至右依次为:六位数字地址码,八位数

      字出生日期码,三位数字顺序码和一位数字校验码。

      2、地址码

      表示编码对象常住户口所在县(市、旗、区)的行政区划代码,

      GB/T2260的规定执行。

      3、出生日期码

      表示编码对象出生的年、月、日,按GB/T7408的规定执行,

      年、月、日代码之间不用分隔符。

      4、顺序码

      表示在同一地址码所标识的区域范围内,对同年、同月、同

      日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配

      给女性。

      5、校验码

      (1)十七位数字本体码加权求和公式

      S = Ai * Wi, i = 2, ... , 18
      Y = mod(S, 11)
      i: 表示号码字符从右至左包括校验码字符在内的位置序号

      Ai:表示第i位置上的身份证号码字符值

      Wi:表示第i位置上的加权因子

      i: 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
      Wi: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2 1
      (2)校验码字符值的计算

      Y: 0 1 2 3 4 5 6 7 8 9 10
      校验码
      : 1 0 X 9 8 7 6 5 4 3 2
      四、举例如下:

      北京市朝阳区
      : 11010519491231002X
      广东省汕头市
      : 440524188001010014

      15位的不需要研究了,很简单的:

      dddddd yymmdd xx p
      dddddd:地区码

      yymmdd:出生年月日

      xx:顺号类编码,无法确定

      p:性别,男的奇数女的偶数


      18位的:

      dddddd yyyymmdd xxx y
      dddddd:地区码

      yyyymmdd:出身年月日,为了应付千年虫问题,年升成4位数

      xxx:顺号类编码,无法确定。但是奇数分配给男性,偶数分配给女性

      y:校验码,最麻烦的地方

      校验码的计算方法:

      假如某身份号码34052419800101001,首先按照公式计算:

      (ai×Wi)(mod 11)……………………………………
      (1)
      公式(1)中:

      i----表示号码字符从由至左包括校验码在内的位置序号;

      ai----表示第i位置上的号码字符值;

      Wi----示第i位置上的加权因子

      i 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
      ai 3 4 0 5 2 4 1 9 8 0 0 1 0 1 0 0 1 a1
      Wi 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2 1 (加权因子,固定的数值
      )
      ai×
      Wi 21 36 0 25 16 16 2 9 48 0 0 9 0 5 0 0 2 a1
      根据公式(1)进行计算:

      (ai×Wi) =
      21+36+0+25+16+16+2+9+48++0+0+9+0+5+0+0+2) = 189
      189 ÷
      11 = 17 + 2/11
      (ai×
      Wi)(mod 11) = 2
      然后根据计算的结果,从下面的表中查出相应的校验码,其中X表示计算结果为10

      (ai×
      WI)(mod 11) 0 1 2 3 4 5 6 7 8 9 10
      校验码字符值
      ai 1 0 X 9 8 7 6 5 4 3 2
      根据上表,查出计算结果为2的校验码为所以该人员的公民身份号码应该为 34052419800101001X


      这样,按以上的算法,加上我的这个行政区代码数据库,除了顺号类编码以外所有的数字都可以确定了。

      数据库下在地址见附件


      注:数据库是根据中华人民共和国国家统计局截至2003630号的数据,但是之前有修改过的数据,详情见:


      http://www.stats.gov.cn/tjbz/index.htm。所以,假如是太早颁发的身份证在地区代码上可能有出入。可以参考国家统计局的数据
      进一步应用:这些资料加上ip地址省份分布数据库(追捕或者qqip地址数据库都可以,qq的地址数据库使用方法可以在精华区找),可以


      更准确地确认访问者的ip地址(不考虑使用代理或者访问者上网地不在其身份证颁发地的情况:>)。这个就不再深入了。

      另:

      我国自1999年实施公民身份号码制度以来,许多公民身份号码末位为“X”的公民,由于不明白“X”的含义,要求给予更换,产生了不必要的误会。目前我国公民身份证号码由18位数字组成:前6位为地址码,第7至14位为出生日期码,第15至17位为顺序码,第18位为校验码。检验码分别是“0、1、2、……10”共11个数字,当检验码为“10”时,为了保证公民身份证号码18位,所以用“X”表示。虽然校验码为“X”不能更换,但若需全用数字表示,只需将18位公民身份号码转换成15位居民身份证号码,去掉第7至8位和最后1位3个数码。

      当今的身份证号码有15位和18位之分。1985年我国实行居民身份证制度,当时签发的身份证号码是15位的,1999年签发的身份证由于年份的扩展(由两位变为四位)和末尾加了效验码,就成了18位。这两种身份证号码将在相当长的一段时期内共存。两种身份证号码的含义如下:

      18位的身份证号码 如:130429####%%%%0078

      1~6位为地区代码,其中1、2位数为各省级政府的代码,3、4位数为地、市级政府的代码,5、6位数为县、区级政府代码。如13(河北省)04(邯郸市)29(永年县)

      7~14位为出生年月日

      15~17位为顺序号,是县、区级政府所辖派出所的分配码,每个派出所分配码为10个连续号码,例如“000-009”或“060-069”,其中单数为男性分配码,双数为女性分配码,如遇同年同月同日有两人以上时顺延第二、第三、第四、第五个分配码。如:007的就是个男生 而且和他同年月日生的男生至少有两个 他们的后四位是001* 和 003*

      18位为效验位(识别码),通过复杂公式算出,普遍采用计算机自动生成。是前面17位的一种检验代码,如果你改变了前面某个数字而后面的效验代码不响应改变就会被计算软件判断为非法身份正号码。X也是效验代码的一中

      15位的身份证号码:

      (1)1~6位为地区代码

      (2)7~8位为出生年份(2位),9~10位为出生月份,11~12位为出生日期

      (3)第13~15位为顺序号,并能够判断性别,奇数为男,偶数为女。


    • Java 写入文件操作(转载)

      2009-04-30 01:11:08

      Java 写入文件操作
      2008年05月26日 星期一 13:49

      示例:

      从键盘中读入字符然后写入文件中,知道遇到“bye”退出

      /**
      * @(#)NewTest54.java
      *java试验五练习四
      *
      * @gaoandgao
      * @version 1.00 2008/5/26
      */
      import java.io.BufferedReader;
      import java.io.InputStreamReader;
      import java.io.IOException;
      import java.io.FileOutputStream;
      import java.io.FileWriter;
      import java.io.FileNotFoundException;
      import java.io.*;
      public class NewTest54 {
             
         
          public NewTest54() {
          }
         
         
          public static void main(String[] args){
          StringBuffer buffer=new StringBuffer();
              BufferedReader br=new BufferedReader(new InputStreamReader(System.in),1);
             
              for(;;){
              try{
              System.out.println("Input bye to exit");
              String string=br.readLine();
              if(string.equals("bye")){
                 File file=new File("output.txt");
                 DataOutputStream utput=new DataOutputStream(new FileOutputStream(file));
                 output.write(buffer.toString().getBytes());
                 output.close();
                 System.exit(0);
              }
                 else {
                  buffer.append(string);//先将字符串暂存到buffer中最后一起写入文件
                 }
              }catch(IOException ex){
              ex.printStackTrace();
              }
              }
             
          }
      }


      File temp = new File(outPut,"pro.xml");
      DataOutputStream uts = new DataOutputStream(new FileOutputStream(temp));
      outs.write(sb.toString().getBytes("utf-8")); //指定编码

      使用Java操作二进制文件一文中讲述了如何使用Java处理二进制的文件,这篇文章主要讲述如何使用java处理文本文件。

      最初java是不支持对文本文件的处理的,为了弥补这个缺憾而引入了Reader和Writer两个类,这两个类都是抽象类,Writer中write (char[] ch,int off,int length),flush()和close()方法为抽象方法,Reader中read(char[] ch,int off,int length)和close()方法是抽象方法。子类应该分别实现他们。

      当我们读写文本文件的时候,采用Reader是非常方便的,比如FileReader,InputStreamReader和 BufferedReader。其中最重要的类是InputStreamReader,它是字节转换为字符的桥梁。你可以在构造器重指定编码的方式,如果不指定的话将采用底层操作系统的默认编码方式,例如GBK等。当使用FileReader读取文件的时候。
      FileReader fr = new FileReader("ming.txt");
      int ch = 0;
      while((ch = fr.read())!=-1 )
      {
      System.out.print((char)ch);
      }
      其中read()方法返回的是读取得下个字符。当然你也可以使用read(char[] ch,int off,int length)这和处理二进制文件的时候类似,不多说了。如果使用InputStreamReader来读取文件的时候
      while((ch = isr.read())!=-1)
      {
      System.out.print((char)ch);
      }
      这和FileReader并没有什么区别,事实上在FileReader中的方法都是从InputStreamReader中继承过来的。read()方法是比较好费时间的,如果为了提高效率我们可以使用BufferedReader对Reader进行包装,这样可以提高读取得速度,我们可以一行一行的读取文本,使用readLine()方法。
      BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("ming.txt")));
      String data = null;
      while((data = br.readLine())!=null)
      {
      System.out.println(data);
      }
      当你明白了如何用Reader来读取文本文件的时候那么用Writer写文件同样非常简单。有一点需要注意,当你写文件的时候,为了提高效率,写入的数据会先放入缓冲区,然后写入文件。因此有时候你需要主动调用flush()方法。与上面对应的写文件的方法为
      FileWriter fw = new FileWriter("hello.txt");
      String s = "hello world";
      fw.write(s,0,s.length());
      fw.flush();

      OutputStreamWriter sw = new OutputStreamWriter(new FileOutputStream("hello2.txt"));
      osw.write(s,0,s.length());
      osw.flush();

      PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream("hello3.txt")),true);
      pw.println(s);
      不要忘记用完后关闭流!下面是个小例子,帮助新手理解。其实有的时候java的IO系统是需要我们多记记的,不然哪天就生疏了。测试文件的内容为
      ming.txt


      hello world i like java language
      import java.io.*;


      public class TestFile2
      {
      public static void main(String[] args) throws IOException
      {
      FileReader fr = new FileReader("ming.txt");
      char[] buffer = new char[1024];
      int ch = 0;
      while((ch = fr.read())!=-1 )
      {
      System.out.print((char)ch);
      }

      InputStreamReader isr = new InputStreamReader(new FileInputStream("ming.txt"));
      while((ch = isr.read())!=-1)
      {
      System.out.print((char)ch);
      }

      BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("ming.txt")));
      String data = null;
      while((data = br.readLine())!=null)
      {
      System.out.println(data);
      }

      FileWriter fw = new FileWriter("hello.txt");
      String s = "hello world";
      fw.write(s,0,s.length());
      fw.flush();

      OutputStreamWriter sw = new OutputStreamWriter(new FileOutputStream("hello2.txt"));
      osw.write(s,0,s.length());
      osw.flush();

      PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream("hello3.txt")),true);
      pw.println(s);

      fr.close();
      isr.close();
      br.close();
      fw.close();
      osw.close();
      pw.close();
      }
      }

    • Java生成18位身份证号-----终于功成

      2009-04-30 00:03:41

      package com.icss.oa.work;

      public class IDCard {
         // wi =2(n-1)(mod 11)
          final int[] wi = {7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2}; //

         // verify digit
          final int[] vi = {1,0,'X',9,8,7,6,5,4,3,2}; //

          private int[] ai = new int[18];

           public IDCard() {
            }
         //verify


         //get verify
          public  String getVerify(String eightcardid) { //eightcardid
          int remaining = 0;

          if (eightcardid.length() == 17) {
           int sum = 0;
           for (int i = 0; i < 17; i++) {
           String k = eightcardid.substring(i, i + 1);
           ai[i] = Integer.parseInt(k);
              }
       
           for (int i = 0; i < 17; i++) {
            sum = sum + wi[i]*ai[i];
              }
            remaining = sum % 11;
            return remaining == 2 ? "X" : String.valueOf(vi[remaining]);
       
        }
          else{
           return "出错了!";
          }
         
          }

          

         //用随机数  写出前17位

          public String getSheng(){
           // 第一二位 前两位 01- 64
              // int number = (int)(Math.random()*(tonum-fromnum))+fromnum;
             int number ;
          String str=null;
             number = (int)(Math.random()*(64-1)+1);
          if(number<10)
          {
           //转换成字符串
           
           str = Integer.toString(number);
           str = "0"+number;
           //System.out.println(str);
           return str;
          }
          else{
           
           //System.out.println(Integer.toString(number));
           return Integer.toString(number);
          }
          }
         
         
          public String getShi(){ 
           //第三四位 两位 01-02
             int number ;
             number = (int)(Math.random()*1+1);
          String str=null;
          if(number<10)
          {
           //转换成字符串
           
           str = Integer.toString(number);
           str = "0"+number;
           //System.out.println(str);
           return str;
          }
          else{
           
           //System.out.println(Integer.toString(number));
           return Integer.toString(number);
          }
                }  
        
         
           public String getXian(){
           //第五六位  两位  01-05
          int number ;
             number = (int)(Math.random()*4+1);
          String str=null;
          if(number<10)
          {
           //转换成字符串
           
           str = Integer.toString(number);
           str = "0"+number;
           //System.out.println(str);
           return str;
          }
          else{
           
           //System.out.println(Integer.toString(number));
           return Integer.toString(number);
          }    
              }
         
          public String getYear(){
           //第 7--10位   年份:1970-1990
        int number;
           number = (int)(Math.random()*(1990-1970)+1970);
        String str=null;
        if(number<10)
        {
         //转换成字符串
         
         str = Integer.toString(number);
         str = "0"+number;
         //System.out.println(str);
         return str;
        }
        else{
         
         //System.out.println(Integer.toString(number));
         return Integer.toString(number);
        }
          } 
        
          public String getMonth(){
           //第11 12 位      月份:01-12
        
        int number ;
           number = (int)(Math.random()*(12-1)+1);
        String str=null;
        if(number<10)
        {
         //转换成字符串
         
         str = Integer.toString(number);
         str = "0"+number;
         //System.out.println(str);
         return str;
        }
        else{
         
         //System.out.println(Integer.toString(number));
         return Integer.toString(number);
        }
          } 
        
       public String getDay(){
           //第13   14 位    日期:01-28
        
        int number ;
           number = (int)(Math.random()*(28-1)+1);
        String str=null;
        if(number<10)
        {
         //转换成字符串
         
         str = Integer.toString(number);
         str = "0"+number;
         //System.out.println(str);
         return str;
        }
        else{
         
         //System.out.println(Integer.toString(number));
         return Integer.toString(number);
        }
        
        
          }
         
          public String getSequence(){
           //第15  16 位        两位:01-99
        int number ;
           number = (int)(Math.random()*(99-1)+1);
        String str=null;
        if(number<10)
        {
         //转换成字符串
         
         str = Integer.toString(number);
         str = "0"+number;
         //System.out.println(str);
         return str;
        }
        else{
         
         //System.out.println(Integer.toString(number));
         return Integer.toString(number);
        }
          }
         
          public String getSex(){
               // 第17 位  一位性别:0-9
           int number ;
           number = (int)(Math.random()*(9-0)+0);
        return Integer.toString(number);  
          }

          public  String get17ID(){
           String str=null;
           str = this.getSheng()+this.getShi()+this.getXian()+ this.getYear()+ this.getMonth()+ this.getDay() + this.getSequence() + this.getSex()  ;
           return str;
          }
         
          public String getIDCard(){
           String str=null;
             str = this.get17ID() ;
           str = str + this.getVerify(str); 
           //String test = this.get17ID();
           //System.out.println("身份证号前17位:"+test);
           //System.out.println("身份证号:"+test+this.getVerify(test));
           return str;
          }

       

      public static void main(String[] args){
       
       IDCard card=new IDCard();
        
       //String idcard="41011119820316258";//  41112119821028551  41102219841112602  410111452105122564
      //String idcard = card.get17ID();
       //System.out.println("41124919810612243"+card.getVerify(idcard));
      //System.out.println("身份证的最后一位为:"+card.getVerify(idcard));
      //System.out.println("身份证的最后一位为:"+card.getVerify("你的身份证号前17位"));
       //System.out.println("身份证号前17位为:"+card.get17ID());
       System.out.println("身份证号为:"+card.getIDCard());

      }

      }

       

      /*
       *下面是 身份证号码的规则:

      身份证号码共18位.

      前6位是地址码. 省

      7--14位是出生年月日

      后面4位是生成的代码,

      第17位用来做性别判定:偶数 -- 女 ; 奇数--- 男 .

      前17位的构成: 2(省)+2(市)+2(县)+4(年)+2(月)+2(日)+2(序号)+1(性别)

      第十八位数字的计算方法为:
      1.将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为:7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2
      2.将这17位数字和系数相乘的结果相加。
      3.用加出来和除以11,看余数是多少?
      4余数只可能有0 1 2 3 4 5 6 7 8 9 10这11个数字。其分别对应的最后一位身份证的号码为1 0 X 9 8 7 6 5 4 3 2。
      5.通过上面得知如果余数是2,就会在身份证的第18位数字上出现罗马数字的Ⅹ。如果余数是10,身份证的最后一位号码就是2。


       */

       

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

      关于根据前17位计算第十八位的算法,那个是转的,自己又仿照练习了一遍

      package com.icss.oa.work;

      public class GetVerfycode {

       /**
        * @param args
        */
       
       /*
        *第十八位数字的计算方法为:
      1.将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为:7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2
      2.将这17位数字和系数相乘的结果相加。
      3.用加出来和除以11,看余数是多少?
      4余数只可能有0 1 2 3 4 5 6 7 8 9 10这11个数字。其分别对应的最后一位身份证的号码为1 0 X 9 8 7 6 5 4 3 2。
      5.通过上面得知如果余数是2,就会在身份证的第18位数字上出现罗马数字的Ⅹ。如果余数是10,身份证的最后一位号码就是2。
       
        */
       //定义一个无参构造方法
       public GetVerfycode(){
        
       }
       
       //准备基本数据
       final int[] coef = {7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};
       final int[] mapping = {1,0,'X',9,8,7,6,5,4,3,2};
       private int[] store = new int[18];
       
       public String getCode(String number){
        String str = null;
        if(number.length()== 17){

         //取出17位的每一个值
         for(int i= 0;i< number.length() ;i++){
          String s = number.substring(i,i+1);
          store[i]= Integer.parseInt(s);
         }
         
         //乘以不同的系数,将这17位数字和系数相乘的结果相加
         int sum  = 0 ;
         for(int i=0;i<17;i++){
          sum = sum +store[i]*coef[i];
         }
         
         //用加出来和除以11,看余数是多少?
         int remaining  = sum % 11;
         //remaining = 2:'X'?
            if(remaining == 2){
             str = "X"; 
            }
            else{
             str = Integer.toString(mapping[remaining]);
            }
        }
        else{
         System.out.println("位数不对,出错!");
        }
        return str;
       }
       
       public static void main(String[] args) {
        // TODO Auto-generated method stub
        GetVerfycode code = new GetVerfycode();
        System.out.println("身份证的最后一位为:"+code.getCode("17位身份证号"));
       }

      }

       

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

      后续,批量生成,将生成的数据,写入Excel表或者文本文件中

      //向文本中写入100个随机的身份证号,加入main方法中即可
      //import java.io.*;


       for(int n =1;n<100;n++){
        try{
         File f = new File("f:\\idcard.txt");
         if(f.exists()){
          //f.delete();
         }
         else{
          f.createNewFile();
         }
         FileWriter fw = new FileWriter(f,true);
         fw.write(card.getIDCard());
         fw.write('\n');
         fw.flush();
         fw.close();
        }catch(Exception e){
         System.out.println(e.getStackTrace());
        }
       }

       

      大功告成,呵呵

      虽然对于java高手来说小菜一碟,但对于我来说还是值得高兴的

      ======================================================

      关于地区码:可以采用已知的地区码常量来生成,利用数组随机取值

           // 得到六位的地区码

        public String getAreacode(){
         String[] area = {"110100","110101","110102","110103"};
           return area[(int)(Math.random()*(3519))] ;
           
          }

       

      身份证号码验证网站:http://qq.ip138.com/idsearch/index.asp
         

    • 测试人员的职业发展(转载)

      2009-04-29 10:24:27

      测试人员的职业生涯究竟如何发展,一直以来都是一个难以说清的问题。一方面,各种咨询机构都在说,测试是个很有前景的行业,另一方面,测试从业人员少,收入普遍较低仍然是一个不争的事实。很多同行都处在一种看不清前路的状态,测试人员转行的现象也比比皆是。这里,我想讲一下我对测试这个行业的理解,以及在我心中,测试人员的路在何方。

      首先,我想请大家先冷静下来,平静的看待测试这个行业,她不过是365行中的一种,非常普通。不可否认,在大多数中国软件企业中,测试团体和开发团体相比,在人数、技术水平、薪资收入上,都有差距。要探讨这个问题,不是三言两语能说清的,而且已经超出了单纯的技术层面,到达了一个人生哲学和价值观的范畴。

      IT行业不同于一般的制造业,充满创新和新技术的探索,而在这个过程中,开发工作是一种创造的工作,对公司来说,是一个从无到有的转变,成就感非常强烈。测试则是一种验证和控制的工作,其价值虽然也不可小窥,但是不太容易产生成就感。测试工作的价值提现在什么地方,这个我们在另外的文章里面讨论。

      不同类型的软件公司,对测试工作的重视程度也有很大不同。建议测试人员选择一些业务持续发展的公司。做项目东一榔头西一棒的公司,是不需要高质量的测试的,他们需要的是尽快把软件交出去,却无法静下心来思考,怎么把质量做好。选择这样的公司,要冒相当大的风险。

      接下来说一下大家关心的话题,如果选择了测试,怎么能从测试团队中脱颖而出呢?经常被提出的概念有“管理和技术两条路线”,这个概念太抽象,还是无法帮我们理清思路。有的观点认为,测试要学习开发技术,这个也没有说到关键点上。我认为测试人员的职业发展有下面两个,换句话说,软件企业最需要拥有这两种能力的测试人员。注意,这两种能力并不互相冲突。

      第一、不断改进测试策略,提高测试效率和质量

      目前很多公司的测试还是以手工的黑盒测试为主,测试策略比较单一,可能很多同学都遇到在写测试计划的时候,测试策略那一章不知该写什么的问题。如果只做黑盒测试,会在提高测试质量的路途上出现一道难以逾越的鸿沟。
       
      改进测试策略需要掌握开发技术,但是技术仅仅是必要条件,更重要的能力,是能够系统的规划一件事情,分析工作中的问题,选择最有效的解决方法,最终和大家一起实现一个共同的改进目标。
       
      改进测试策略一般会考虑以下几个方向:单元测试(白盒和灰盒)、自动化测试、性能测试、安全性测试、易用性测试等等。当然,具体的改进目标,要根据业务的不同,选择合适的方向。
       
      不过,很多测试团队的人力资源都比较紧张,无法投入太多的人手去改进测试策略,能够很好的解决这一矛盾就显得非常重要。公司需要的是,能够根据测试团队的当前状况,制定出有效的改进计划,并带领大家提高测试效率的人。

      第二、能够“吃”业务,控制业务的测试质量

      这里需要说明,“吃”业务并不等同于熟悉业务。对于测试工作来说,熟悉业务是非常重要的,大部分测试人员上岗以后,都会从了解业务开始,逐渐的掌握产品线的业务规则。但是,当一个测试人员熟悉了某个业务以后(成为业务专家),问题出现了:他/她可能会一直陷在这个业务的测试执行中,无法解脱,几年如一日的做着类似的工作。
       
      即使增加了新的测试人手,业务专家也没有感到多少轻松。一方面,业务专家要周而复始的对新人进行培训,解答他们工作中的问题,培训成本极高;另一方面,业务专家不放心把重要功能交给新人测,必须自己来测试,业务专家的职业发展出现了比较尴尬的局面。
       
      由此我们提出了“吃”业务的概念,它与熟悉业务最大的区别就在于,测试人员吃掉一个业务以后,可以把测试工作完全交给另一个测试人员来做,同时,也能保证测试的质量。而要达成这个目标,关键就在于文档。我们需要以业务为单位,完善测试用例、业务沉淀、测试设计、测试脚本等文档,并且,更重要的是,要把这些零散的文档组织成一个系统的文档体系。
       
      注意,吃业务并不等于为这个业务单元编写一套非常完整的文档,而是建立起一个基本的,可持续维护的文档体系即可。业务专家吃完一个业务以后,可以把这个业务交给其他人,然后开始吃第二个,同时,关注原先的业务的文档完善过程。这样,有吃业务能力的测试人员,能管理更多的业务需求和测试人员,而且由于他/她接触的业务越来越多,工作的视角也会逐渐提高,成为系统级的测试工程师和团队leader。
       
      要设计这一文档体系,也需要较好的系统设计能力,当然,最主要的是持之以恒的毅力。另外,每个业务单元的文档体系,也可以在一批批的测试人员中传承,每个人都会来维护、完善它,并从中学到很多经验。
       
      最后,我引用中国的一句古话:“行行出状元”。天下那么多职业,只是革命的分工不同,每个职业都有其独特的一面,就像黄宏在《买钉子》的小品里说的,这是“道”。
       
       
    • 使用LoadRunner的java Vuser协议调用jar文件

      2009-04-29 10:11:40

      使用LoadRunner8.1的java Vuser协议调用jar文件(一)

      很多时候,我们需要直接对Jboss下的java应用服务系统进行性能测试。这种服务有些是基于Jboss Remoting实现,以socket的方式提供接口函数,并没有一个可以看的见的界面可供测试。在这种情况下,有的测试组会选择,开发一个简单的web页面,调用这个服务,然后用loadRunner制作web脚本进行测试。


       

      这种设计方案的缺陷在于,性能测试的压力会被堵在web层这里,无法测出服务端的准确性能,甚至可能web服务器的资源已经耗尽,服务端还完全没达到最大吞吐量。要解决这个问题,只有使用LoadRunner直接调用服务端的接口,减少中间环节的干扰。


       

      首先,请大家安装LoadRunner8.1以上版本,因为8.1支持jdk1.5版本,现在很多应用都是在jdk1.5的框架下开发的。注意,8.1并不支持jdk1.6,所以请安装1.5版本。


       

      打开Vuser Generator,新建一个java Vuser协议的脚本。新脚本会自动创建一个class和3个函数:init、action、end。这时,大家先Compile一下,如果没有错误,再继续。如果报错,看看你的jdk版本。


       

      先把脚本保存在本地的文件夹中。然后,把调用服务所需要的一些jar文件,也拷贝到脚本目录里面。注意,一般调用远程Jboss服务时,都是需要一些描述接口函数类型的jar文件。另外,建议先使用eclipse,开发一个测试的框架类,通过这个框架,可以轻松的调用指定服务器的指定接口服务的指定方法,从而降低LoadRunner脚本编写的难度。


       

      jar文件准备好以后,点击toolbar上的“java ”按钮,然后点击“location”,把你需要的jar文件添加进来。这时,就能看到jar文件中的所有对象列表,以及对象的属性和方法。
       
          我的版本是9.10 ,toolbar上的“java Function”按钮,
           (插图很不方便哦,算了)
      回到脚本编辑窗口,我们可以直接遵照C++的语法,编写脚本代码,在代码中,可以对刚才引入的java对象进行面向对象的编程,比如下面的代码:
      com.Testing.ServiceCaller caller;
      caller = new com.Testing.ServiceCaller ();
      caller.init("192.168.0.1:4446", "ServiceName");
      caller.call("methodName" ,  "paras");


       

      在这里写脚本,几乎和写java代码一模一样。我们可以完全引用jar文件的各种对象,同样,也可以使用java ee框架中的各种对象,LoadRunner对java的支持真是非常的好。


       

      同时,LoadRunner也提供了一些好用的函数,方便大家准备测试参数、输出测试结果,比如:
      int rgId = lr.eval_int("2");
      lr.error_message ("出错喽");


       

      这些以lr开头的方法,参考文档在帮助中写的很清楚。比如lr.eval_int就是把字符型变量转换成int型,lr.error_message 是输出错误的提示,我们可以在代码中加入数据正确性的检查逻辑,这样在执行性能测试的时候,如果出错,就能很清楚的看到。
       
       
       
       
      使用LoadRunner8.1的java Vuser协议调用jar文件(二)
       
      上一篇文章里,我们讲了如何在java Vuser协议中,引用jar文件中的类。Loadrunner对java支持非常好,但是,在实际操作中,出现了一个新问题:如果要引用jar文件,需要把jar文件拷贝到当前脚本的目录下。如果我们为性能测试开发了一个测试框架,就需要把框架类的jar文件拷贝到每个脚本的目录下,这样做显然不合理,因为同一文件保存多份拷贝,很难管理,极易引起版本混乱。
       
      我们需要把测试框架的jar文件,放在一个统一的地方,让每个测试脚本都能自动找到,这样才能解决问题。开始,我准备利用java VM运行环境的ClassPath来解决。首选,在环境变量的ClassPath中增加一个目录,比如“d:\lib”。然后把测试框架的jar文件拷贝到这个目录,运行脚本,失败!提示找不到指定的类。
       
      之后,我修改了环境变量ClassPath的值,把jar文件的完整路径添加进去,例如“d:\lib\testBase.jar”,再次运行脚本,成功!似乎java的classPath并不支持目录,而必须要指定到具体的jar文件。这个问题困扰了我很久,如果有java的高手请指点一下。
       
      虽然设定环境变量,可以解决这个问题,不过以后如果需要增加一个jar文件,就要修改一次环境变量,还是很不方便。于是,我尝试把jar文件用winrar解压,直接把解压后的目录保存在d:\lib目录下面,然后在环境变量里面只设定d:\lib,运行脚本发现也能通过!!原来系统虽然不能找到jar文件,却能找到目录中的*.class文件。
       
      虽然jar文件解压后的文件比较多,和单独的jar文件相比,管理起来有些麻烦,不过最终我还是选择了这个方法。或者,大家还可以使用另一个方法:在一台电脑上,将测试需要用到的jar文件共享,然后,在编辑脚本时,按下F4进入“runtime setting”,在classPath中从网络中,指向你需要的jar文件。这样,就需要为每个脚本都设定一次classPath。
       
       
      使用LoadRunner8.1的java Vuser协议调用jar文件(三)
       
      今天我需要用loadrunner测试一个远程的java服务应用,在测试过程中controller加载脚本时总是报错:ClassLoader error,后来问题解决。
       
      我首先把这个java服务应用的客户端jar包拷贝到loadrunner脚本的目录下,这些jar包是定义服务接口的。然后编写脚本代码、调试。到此为止一切正常。然后我使用controller把这个脚本run起来,这时开始报错,错误信息主要是:GENERIC_FAILURE和ClassLoader error。
       
      为什么在Vuser Gen里面调试正常,在controller里运行却不正常,经过分析发现是ClassLoader的问题,很可能是,在controller的运行进程中,线程的ContextClassLoader没有被正确赋值,因此我们增加以下一句代码:
       
      Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
       
      然后再run脚本,问题解决。如果大家使用loadrunner遇到类似问题,试试这个方法。
       
       
       
      文章转载自: easytest.csai.cn
       
       
       
    • 数据统计

      • 访问量: 709077
      • 日志数: 415
      • 图片数: 1
      • 文件数: 3
      • 建立时间: 2008-12-07
      • 更新时间: 2015-07-14

      RSS订阅

      Open Toolbar