发布新日志

  • 如何成为强大的程序员?

    2013-03-29 09:29:24

    Aaron Stannard是新创公司MarkedUp的CEO,他最近花费大量时间雇佣、评估很多不同的程序员,并和他们一起协作。在这个过程中他发现并总结了十种程序员无法意识到自己潜力的原因,意在让更多程序员发掘出自己的潜力,从而成为强大的程序员。

    Aaron提到,他的公司中所使用的技术非常复杂,某些大型企业都很难掌握,所以对于想要加入团队的程序员来说,入门门槛非常高。因此,尽管他们非常仔细地雇佣新人,但还是很难找到足够天才的程序员。于是,他总结出十种阻碍程序员职业生涯发展的行为,并据此来帮助想要提升自身的平凡的程序员们。

    1. 太害怕学不会新的工具、语言和框架

    一般的程序员会墨守他们最喜欢的工具,而不希望学习新的,因为他们认为,离开了那些语言和工具,多年的经验就会付诸东流。而强大的程序员会拥抱那些挑战和机会,积极地学习新的工作方式。

    2. 直到特性“完成”的时候才会提交。(但永远都不会完成!)

    他在MarkedUp公司中把这种行为叫做“囤积提交(commit hoarding)”。有些程序员没有足够的信心来承受团队中其他成员的批评和审查,因此会把自己的工作藏起来,直到“完成”状态才提交。

    这种开发者会损害团队中其他人员的生产力,因为团队看不到他每天的成果,而且他也不会在正常开发的过程中寻求帮助,这样就会造成很多“最后一分钟”的缺陷,从而让交付延迟。而强大的程序员会知道,代码并不是他们自己,因此会把代码经常自信地呈现在其他团队成员的眼前,获得批评和建议。

    3. 只是“知其然”会很危险

    在这里Aaron举了微软最近在C# 5.0中引入的async和await关键字为例,这两个关键字会让创建和管理异步调用变得很容易,但是也会造成上下文切换、对共享资源进行多线程访问的成本,仅仅对此有基本了解的程序员会盲目地使用这些特性,把所有I/O调用都封装成C#中的Task对象,这会创建出危险的、不可预测的而且非常难以测试的代码。

    好的开发者不仅“知其然”,而且会了解为什么这么做以及应该在什么样的条件下使用。

    4. 分析瘫痪(Analysis paralysis)

    分析瘫痪是指在程序开发初期进行系统分析,常因为太过执着于控制所有可能的变化和意外,而造成大量时间的浪费,裹足不前。这是一种很经典的问题,会影响很多一般的程序员。它通常是由过度分析造成的,但是Aaron认为其根本原因在于不敢做出坏的决定。一般的程序员会担心犯错,只想一次成功。

    而强大的程序员不会害怕,他们会编写很烂的代码,对其进行单元测试,如果认为无法达到目的,就会在45分钟之内把它抛弃。强大的程序员会积极地限制用来研究的时间,因为他们知道那是个陷阱——看起来是有效的,但经常都无效。

    5. 没有对工具和开发过程投入

    如果你想要成为天才程序员,那么就需要投入时间提升技能和知识,而将你和普通的代码工人区分开来的是快速编写出生产级别代码的能力。你可以同时拥有好的代码和速度,但是你需要先对你用于构建的过程投入。

    一般的程序员不会对工具、过程和环境投入,只会使用大量的时间学习新的语言特性和API如何工作,但那并不会改变什么。

    通常,你作为程序员所能够做出的最大改进并不是专注于你所编写的代码,而是优化你编写代码的过程。

    6. 羞于请求帮助

    一般的程序员羞于或者不想让人知道自己不懂,所以他们装作什么都知道,但这样就有可能提交某种非常可怕的代码到库中。说“我不知道怎么做。”没什么错,强大的程序员知道这一点,所以当被问题难住的时候就会请求帮助。

    7. 不知道如何让其他程序员更容易使用你的代码

    在所有技术团队中,工作很重要的一部分就是人员的并行(human parallelism),也就是多个人能够同时对同一代码库工作的能力。但是对于团队来说,能够异步工作也很重要,当你不在的时候我可以修改你的代码,反之亦然。

    一般的开发者并不这么认为,他们会开始对一项任务编写代码,认为他们会永远拥有这段代码。而强大的开发者会知道技术债务的说法,从而试图通过设计代码来对其限制,让它尽可能可维护和自解释。

    编写可读的代码需要程序员改变他们的看法——你的代码要比你在组织中存在的时间长。

    8. 不知道如何阅读其他人的代码(或者不想读)

    当一位一般程序员看到用他所不熟悉的语言或框架编写的代码库时,就想立刻重写,而不考虑业务价值或者推向市场的时间。而强大的程序员会接受这样的观点,重写所导致的业务成本通常是不可接受的,所以应该避免这种行为。他们会试图坐在计算机前,理解、学习然后修改现有的代码。

    阅读代码要比编写代码还难,但是强大的程序员会投入时间来学习如何超越。

    9. 不能从最终用户的角度编码(你考虑的范围太狭窄)

    有句话说得好:作为程序员,你的工作不是解决技术问题,你之所以解决技术问题,是为了解决业务问题。

    一般的程序员只会陷在技术问题之中,而不知道最初是为什么要解决这个问题。更严重的是,一般程序员无法从头开始创建出具有业务价值的东西。当被要求基于简单的用户设计新特性的时候,他们会死板地、照着字面对故事或者说明书做出解释,这样交付的产品用户根本无法使用。因为他们不会考虑相关的用例;不会考虑最终用户的体验;并且在做面向用户的内容时,设计都会很笨重。这导致他们无法编写业务应用,只能做产品。

    好的程序员会从最终用户的角度来看他们的代码。我怎样才能让它更轻松地解决用户的问题呢?故事的文字内容之外有哪些方面会让这个特性给用户带来更多收益呢?

    10. 无法判断任何编程任务的业务价值

    这个问题和上一个是相关的,很多技术上很强的程序员之所以无法意识到自己的潜力,是因为他们不会停下来,从业务或者组织本身的角度去看一下他们的工作。

    强大的程序员能够自我管理,对选择如何投入时间做出很好的业务决定,他们会问这样的问题:这是我现在应该做的最有价值的事情吗?我应该为之投入多少时间?离交付日期有两个星期,我现在能做什么,从而更容易满足那个日期呢?

    一般的程序员不会,他们只会拿着说明书,然后盲目地实现,直到结束,不关心他们的工作和公司的业务目标有什么关系,以及对其他团队和业务组会产生什么样的影响。这样,他们就会在业务价值很低的技术任务上浪费大量开发时间。

    Aaron在最后做出总结:如果你想要成为更好的程序员,那么就要从改变你看待代码以及编码的方式开始。你需要理解所编写的每行代码背后的业务成本;你需要从客户或者最终用户的角度来看待工作;你需要接受代码会比你在组织中存在的时间更长,所以要以其他开发者能够继承的方式来设计;最重要的,永远都不要害怕新的挑战,也不要害怕请求帮助,你无法独居一隅来提升工作效果,软件开发也是社会化的工作

  • 用mysqlslap进行mysql压力测试

    2013-03-21 14:06:38

    转载地址:http://my.oschina.net/javagg/blog/5060 

    mysqlslap是一个mysql官方提供的压力测试工具,通过模拟多个并发客户端访问mysql来执行测试,使用起来非常的简单。通过mysqlslap –help可以获得可用的选项。 
    下面我们就来看看一些比较重要的参数: 
    Java代码  收藏代码
    1. –defaults-file,配置文件存放位置  
    2. –create-schema,测试的schema,MySQL中schema也就是database  
    3. –concurrency,并发数  
    4. –engines,测试引擎,可以有多个,用分隔符隔开。  
    5. –iterations,迭代的实验次数  
    6. –socket,socket,文件位置  
    7. –debug-info,打印内存和CPU的信息  
    8. –only-print,只打印测试语句而不实际执行  
    9. –auto-generate-sql,自动产生测试SQL  
    10. –auto-generate-sql-load-type,测试SQL的类型。类型有mixed,update,write,key,read。  
    11. –number-of-queries,执行的SQL总数量  
    12. –number-int-cols,表内int列的数量  
    13. –number-char-cols,表内char列的数量  
    14. –query=name,使用自定义脚本执行测试,例如可以调用自定义的一个存储过程或者sql语句来执行测试。  

    测试例子如下: 
    Java代码  收藏代码
    1. [root@localhost ~]# /usr/local/mysql/bin/mysqlslap  –defaults-file=/etc/my.cnf –concurrency=200 –iterations=1 –number-int-cols=1 –auto-generate-sql –auto-generate-sql-load-type=write –engine=myisam,innodb –number-of-queries=200 -S/tmp/mysql.sock –debug-info  -uroot -p123  
    2. Benchmark  
    3.         Running for engine myisam  
    4.         Average number of seconds to run all queries: 0.087 seconds  
    5.         Minimum number of seconds to run all queries: 0.087 seconds  
    6.         Maximum number of seconds to run all queries: 0.087 seconds  
    7.         Number of clients running queries: 200  
    8.         Average number of queries per client: 1  
    9. Benchmark  
    10.         Running for engine innodb  
    11.         Average number of seconds to run all queries: 0.551 seconds  
    12.         Minimum number of seconds to run all queries: 0.551 seconds  
    13.         Maximum number of seconds to run all queries: 0.551 seconds  
    14.         Number of clients running queries: 200  
    15.         Average number of queries per client: 1  
    16. User time 0.03, System time 0.05  
    17. Maximum resident set size 0, Integral resident set size 0  
    18. Non-physical pagefaults 2826, Physical pagefaults 0, Swaps 0  
    19. Blocks in 0 out 0, Messages in 0 out 0, Signals 0  
    20. Voluntary context switches 3340, Involuntary context switches 96  

    对于INNODB引擎,200个客户端同时运行这些SQL语句平均要花0.551秒。相应的MYISAM为0.087秒,测试结果也很简明,就不多少说了。 
    指定数据库的测试: 
    Java代码  收藏代码
    1. –create-schema,指定数据库名称  
    2. –query,     指定SQL语句,可以定位到某个包含SQL的文件  
    3. [root@localhost ~]# /usr/local/mysql/bin/mysqlslap –defaults-file=/etc/my.cnf –concurrency=50 –iterations=1 –create-schema=test –query=/root/test.sql -S/tmp/mysql.sock -uroot -p123  
    4. Benchmark  
    5.         Average number of seconds to run all queries: 0.021 seconds  
    6.         Minimum number of seconds to run all queries: 0.021 seconds  
    7.         Maximum number of seconds to run all queries: 0.021 seconds  
    8.         Number of clients running queries: 50  
    9.         Average number of queries per client: 1  
  • 【转载】使用JMeter的Java请求功能测试Hetty性能

    2013-03-21 14:05:51

    转载地址:http://my.oschina.net/xishuixixia/blog/83348 
    1.JMeter介绍 
    JMeter是Apache组织的开放源代码项目,它是功能和性能测试的工具,100%的用java实现。JMeter可以用于测试静态或者动态资源的性能(文件、Servlets、Perl脚本、java对象、数据库和查询、ftp服务器或者其他的资源)。JMeter用于模拟在服务器、网络或者其他对象上附加高负载以测试他们提供服务的受压能力,或者分析他们提供的服务在不同负载条件下的总性能情况。 

    2.启动JMeter 
    进入JMeter的bin目录,然后执行: 
    Java代码  收藏代码
    1. sudo ./jmeter.sh  

    3.原始的测试方法 
    在没有使用JMeter前,我对hetty的性能测试,都是通过自己写多线程代码去完成的,相当苦逼,相当麻烦,不过也能锻炼自己的编码能力,我先贴出比较原始的测试方法,如下: 

    Java代码  收藏代码
    1. public class RpcHessianClient {  
    2.     public static void main(String[] args) {  
    3.         String url = "http://localhost:8081/apis/hello";  
    4.         HessianProxyFactory factory = new HessianProxyFactory();  
    5.         ExecutorService es = Executors.newFixedThreadPool(10);  
    6.         int size = 1000000;  
    7.         final CountDownLatch cdl = new CountDownLatch(size);  
    8.         try {  
    9.   
    10.             long start = System.currentTimeMillis();  
    11.             factory.setUser("client1");  
    12.             factory.setPassword("client1");  
    13.             factory.setOverloadEnabled(true);  
    14.             final Hello basic = (Hello) factory.create(Hello.class,url);  
    15.             for (int i = 0; i < size; i++) {  
    16.                 es.submit(new Runnable() {  
    17.                     @Override  
    18.                     public void run() {  
    19.                         String u = basic.hello("guolei");  
    20.                         //System.out.println(u);  
    21.                         cdl.countDown();  
    22.                     }  
    23.                 });  
    24.             }  
    25.   
    26.             cdl.await();  
    27.             long time = System.currentTimeMillis() - start;  
    28.             System.out.println("SayHello:");  
    29.             System.out.println("耗时:" + (double) time / 1000 + " s");  
    30.             System.out.println("平均:" + ((double) time) / size + " ms");  
    31.             System.out.println("TPS:" + (double) size / ((double) time / 1000));  
    32.             // System.out.println("Hello, " + s.getMail());  
    33.         } catch (MalformedURLException e) {  
    34.             e.printStackTrace();  
    35.         } catch (InterruptedException e) {  
    36.             e.printStackTrace();  
    37.         } finally {  
    38.             es.shutdown();  
    39.         }  
    40.     }  
    41. }  



    4.使用JMeter来进行现代化测试 
    我们要使用JMeter来测试hetty,由于hetty是一款基于hessian和netty的RPC产品,我们必须使用JMeter的JAVA请求功能来进行测试,Java请求是指JMeter对Java Class进行性能测试。首先我们需要编写测试用例: 

    1)新建JAVA工程。 

    2)引入ApacheJMeter_java.jar 、ApacheJMeter_core.jar以及测试所需要的jar(jar包在JMeter目录的lib/ext目录中)。 

    3)继承AbstractJavaSamplerClient类开始编写主业务。如下: 
    Java代码  收藏代码
    1. public class HettyTest extends AbstractJavaSamplerClient {  
    2.   
    3.     private static String label = "hettyTest";  
    4.     /** 
    5.  
    6.      * 执行runTest()方法前会调用此方法,可放一些初始化代码 
    7.  
    8.      */  
    9.     public void setupTest(JavaSamplerContext arg0) {  
    10.   
    11.     }  
    12.   
    13.     /** 
    14.  
    15.      * JMeter测试用例入口 
    16.  
    17.      */  
    18.   
    19.     public SampleResult runTest(JavaSamplerContext arg0) {  
    20.   
    21.         SampleResult sr = new SampleResult();  
    22.   
    23.         sr.setSampleLabel(label);  
    24.   
    25.         try { // 这里调用我们要测试的java类,这里我调用的是一个Test类  
    26.   
    27.             Map<String,String> map = getDefaultParameters().getArgumentsAsMap();  
    28.   
    29.             sr.sampleStart(); // 记录程序执行时间,以及执行结果  
    30.   
    31.             Test.execute(map.get("ip"),map.get("port"));  
    32.   
    33.             sr.sampleEnd();  
    34.   
    35.             sr.setSuccessful(true);  
    36.   
    37.         } catch (Throwable e) {  
    38.   
    39.             sr.setSamplerData(e.getMessage());  
    40.   
    41.             e.printStackTrace();  
    42.   
    43.             sr.setSuccessful(false); // 用于设置运行结果的成功或失败,如果是"false"则表示结果失败,否则则表示成功  
    44.   
    45.         }  
    46.   
    47.         return sr;  
    48.   
    49.     }  
    50.   
    51.    
    52.   
    53.     /** 
    54.  
    55.      * JMeter界面中可手工输入参数,代码里面通过此方法获取 
    56.  
    57.     */  
    58.   
    59.     public Arguments getDefaultParameters() {  
    60.   
    61.    
    62.   
    63.         Arguments args = new Arguments();  
    64.   
    65.         args.addArgument("ip""localhost");  
    66.   
    67.         args.addArgument("port""8081");  
    68.   
    69.         return args;  
    70.   
    71.     }  
    72.   
    73.     /** 
    74.  
    75.      * 执行runTest()方法后会调用此方法. 
    76.  
    77.      */  
    78.   
    79.     public void teardownTest(JavaSamplerContext arg0) {  
    80.   
    81.     }  
    82. 查看(405) 评论(0) 收藏 分享 管理

    83. 【转】学习技术的三部曲:WHAT、HOW、WHY

      2013-03-21 14:03:36

      转载地址:http://www.oschina.net/question/587361_87980 
      近几天有些网友在邮件里面问我关于学习的问题。有好几个人觉得工作了几年,也学会了不少的类库、框架、甚至语言,但是感觉自己的能力没有太大的提高。因此今天来说一下我个人对这方面的体会,希望对大伙儿(尤其是新手)有帮助。 

      先声明一下,本帖子讨论的三部曲是指你已经选定了某个技术方向之后,该如何学习;至于如何选定技术方向,则属于另一个话题,不在今天的讨论之列。 

      我把学习归类为三个步骤:What、How、Why。经过我对周围同事和朋友的观察,大部分感觉自己技术没有提高的人,都仅仅停留在What阶段。下面我把这三个步骤解释一下。 

      第一步:WHAT 

      所谓的“WHAT”,就是搞清楚某个东东是什么?有什么用?有什么语法?有什么功能特性?...... 
         
      举例如下:对于学习语言(比如C++、Java、Python),大部分人都能够掌握基本的语法和标准库,然后用它写一些小程序(诸如二分查找、冒泡排序、简单文件操作等)。 
         
      对于学习类库(比如JDBC类库),大部分Java程序员都能明白JDBC主要包含哪些类,也能够用JDBC进行简单的数据库查询和增删改操作。由于这个步骤是最基本的,假如你连这都做不到(可能你的理解力不够好),也别在IT界混了。 

      但是光会What是不够的。仅仅停留在这个步骤,导致了很多程序员只知其然,不知其所以然。这就是目前大部分开发人员的现状。 

      第二步:HOW 
         
      所谓的“HOW”,就是搞清楚某个东西内部是如何运作的?实现机制如何?等一系列相关问题。 
         
      举例如下:假如你在学习C++语言,你是否搞明白函数传参数的实现机制?虚函数是如何实现?抛出异常时的栈回退是怎么回事?...... 
         
      假如你在学习Java语言,你是否搞清楚GC如何实现?反射是如何实现? 
      假如你在学习JDBC库,你是否清楚JDBC Driver的4种类型?不同游标类型的实现机制?事务的机制? 
         
      在这个阶段,你必须多想想类似这些问题。然后通过各种途径(参见“关于自学能力”的几个方法),把问题彻底搞清楚。自然而然,你的提高就会比较明显。而且如果碰到一些深层次的问题(比如性能优化),也就知道该如何去解决。 
         
      完成这个阶段之后,你基本上就属于该技术领域最优秀的20%的人(根据二八原理,80%的人不会去思考HOW的问题)。 

      第三步:WHY 
         
      一般来说,只有你把HOW的问题想清楚,才开始考虑步骤WHY。所谓的“WHY”,就是搞清楚某个东西为什么设计成这样?为什么不是另外的样子?这样的设计有什么讲究? 
         
      说实在的,善于问“为什么”有一定的天赋成分?好像某个科学大牛曾经说过“提出问题有时候比解决问题更难”。一般来说,只有当你深刻理解了某个东西,才能够针对这个东东的设计问出一些问题。所以,我前面强调过,要先把HOW的问题搞清楚,再来考虑WHY的问题。 
         
      举例如下:对于C++语言:为什么C++没有类似Java的finally关键字?为什么C++当初没有考虑GC?...... 
         
      对于Java语言:为什么Java没有类似C++的类析构函数?为什么Java要同时提供String和StringBuffer两个似乎冗余的类?...... 
         
      对于Python语言:为什么Python不提供类似C++/Java的访问控制机制?...... 
         
      如果你能够自己问出诸如上述的“为什么”问题,并且能够通过各种途径找到解答,那你基本上已经吃透这个技术了,并且你已经有可能自己去设计一个类似的玩意儿了。到这时,你已经踏上了通向技术高手的康庄大道。 

      由于本博客偏重IT方面,所以今天举的这些例子多半都是IT相关的,但是这个三部曲在IT以外的行业/领域其实也能适用,就看读者自己的领悟了。
    84. 【转】IO的几个概念

      2013-03-21 14:02:21

      【转载地址】;http://sishuok.com/forum/blogPost/list/1036.html 

      在数据库优化和存储规划过程中,总会提到IO的一些重要概念,在这里就详细记录一下,个人认为对这个概念的熟悉程度也决定了对数据库与存储优化的理解程度,以下这些概念并非权威文档,权威程度肯定就不能说了。 

      读/写IO,最为常见说法,读IO,就是发指令,从磁盘读取某段扇区的内容。指令一般是通知磁盘开始扇区位置,然后给出需要从这个初始扇区往后读取的连续扇区个数,同时给出动作是读,还是写。磁盘收到这条指令,就会按照指令的要求,读或者写数据。控制器发出的这种指令+数据,就是一次IO,读或者写。 

      大/小块IO,指控制器的指令中给出的连续读取扇区数目的多少,如果数目很大,比如128,64等等,就应该算是大块IO,如果很小,比如1, 4,8等等,就应该算是小块IO,大块和小块之间,没有明确的界限。 

      连续/随机IO,连续和随机,是指本次IO给出的初始扇区地址,和上一次IO的结束扇区地址,是不是完全连续的,或者相隔不多的,如果是,则本次IO应该算是一个连续IO,如果相差太大,则算一次随机IO。连续IO,因为本次初始扇区和上次结束扇区相隔很近,则磁头几乎不用换道或换道时间极短;如果相差太大,则磁头需要很长的换道时间,如果随机IO很多,导致磁头不停换道,效率大大降底。 

      顺序/并发IO,这个的意思是,磁盘控制器每一次对磁盘组发出的指令套(指完成一个事物所需要的指令或者数据),是一条还是多条。如果是一条,则控制器缓存中的IO队列,只能一个一个的来,此时是顺序IO;如果控制器可以同时对磁盘组中的多块磁盘,同时发出指令套,则每次就可以执行多个IO,此时就是并发IO模式。并发IO模式提高了效率和速度。 

      IO并发几率。单盘,IO并发几率为0,因为一块磁盘同时只可以进行一次IO。对于raid0,2块盘情况下,条带深度比较大的时候(条带太小不能并发IO,下面会讲到),并发2个IO的几率为1/2。其他情况请自行运算。 

      IOPS。一个IO所用的时间=寻道时间+数据传输时间。 IOPS=IO并发系数/(寻道时间+数据传输时间),由于寻道时间相对传输时间,大几个数量级,所以影响IOPS的关键因素,就是降底寻道时间,而在连续IO的情况下,寻道时间很短,仅在换磁道时候需要寻道。在这个前提下,传输时间越少,IOPS就越高。 

      每秒IO吞吐量。显然,每秒IO吞吐量=IOPS乘以平均IO SIZE。 Io size越大,IOPS越高,每秒IO吞吐量就越高。设磁头每秒读写数据速度为V,V为定值。则IOPS=IO并发系数/(寻道时间+IO SIZE/V),代入,得每秒IO吞吐量=IO并发系数乘IO SIZE乘V/(V乘寻道时间+IO SIZE)。我们可以看出影响每秒IO吞吐量的最大因素,就是IO SIZE和寻道时间,IO SIZE越大,寻道时间越小,吞吐量越高。相比能显著影响IOPS的因素,只有一个,就是寻道时间。
    85. 【转】JDBC性能优化篇

      2013-03-21 13:58:27

      系统性能. 
      少用Metadata方法 
          与其它的JDBC方法相比, 由ResultSet对象生成的metadata对象的相对来说是很慢的. 应用程序应该缓存从ResultSet返回的metadata信息,避免多次不必要的执行这个操作. 
      几乎没有哪一个JDBC应用程序不用到metadata,虽然如此,你仍可以通过少用它们来改善系统性能. 要返回JDBC规范规定的结果集的所有列信息, 一个简单的metadata的方法调用可能会使JDBC驱动程序去执行很复杂的查询甚至多次查询去取得这些数据. 这些细节上的SQL语言的操作是非常消耗性能的. 
      应用程序应该缓存这些metadata信息. 例如, 程序调用一次getTypeInfo方法后就将这些程序所依赖的结果信息缓存. 而任何程序都不大可能用到这些结果信息中的所有内容,所以这些缓存信息应该是不难维护的. 
      避免null参数 
      在metadata的方法中使用null参数或search patterns是很耗时的. 另外, 额外的查询会导致潜在的网络交通的增加. 应尽可能的提供一些non-null的参数给metadata方法. 
      因为metadata的方法很慢, 应用程序要尽可能有效的调用它们. 许多应用程序只传递少量的non-null参数给这些方法. 
      Java代码  收藏代码
      1. 例如:  
      2. ResultSet WSrs = WSc.getTables (nullnull"WSTable"null);  
      3. 应该这样:  
      4. ResultSet WSrs = WSc.getTables ("cat1""johng""WSTable",  "TABLE");  

      在第一个getTables()的调用中, 程序可能想知道表'WSTable'是否存在. 当然, JDBC驱动程序会逐个调用它们并且会解译不同的请求. JDBC驱动程序会解译请求为: 返回所有的表, 视图, 系统表, synonyms, 临时表, 或存在于任何数据库类别任何Schema中的任何别名为'WSTable'的对象. 
      第二个getTables()的调用会得到更正确的程序想知道的内容. JDBC驱动程序会解译这个请求为: 返回当前数据库类别中所有存在于'johng'这个schema中的所有表. 
      很显然, JDBC驱动程序处理第二个请求比处理第一个请求更有效率一些. 
      有时, 你所请求信息中的对象有些信息是已知的. 当调用metadata方法时, 程序能传送到驱动程序的的任何有用信息都可以导致性能和可靠性的改善. 
      使用'哑元'(dummy)查询确定表的特性 
      要避免使用getColumns()去确定一个表的特性. 而应该使用一个‘哑元’查询来使用getMetadata()方法. 
      请考虑这样一个程序, 程序中要允许用户选取一些列. 我们是否应该使用getColumns()去返回列信息给用户还是以一个'哑元'查询来调用getMetadata()方法呢? 
      案例 1: GetColumns 方法 
      Java代码  收藏代码
      1. ResultSet WSrc = WSc.getColumns (... "UnknownTable" ...);  
      2. // getColumns()会发出一个查询给数据库系统  
      3. . . .  
      4. WSrc.next();  
      5. string Cname = getString(4);  
      6. . . .  
      7. // 用户必须从反复从服务器获取N行数据  
      8. // N = UnknownTable的列数  


      案例 2: GetMetadata 方法 
      Java代码  收藏代码
      1. // 准备'哑元'查询  
      2. PreparedStatement WSps = WSc.prepareStatement  
      3.   ("SELECT * from UnknownTable WHERE 1 = 0");  
      4. // 查询从来没有被执行,只是被预储  
      5. ResultSetMetaData WSsmd=WSps.getMetaData();  
      6. int numcols = WSrsmd.getColumnCount();  
      7. ...  
      8. int ctype = WSrsmd.getColumnType(n)  
      9. ...  

      // 获得了列的完整信息 
      在这两个案例中, 一个查询被传送到服务器. 但在案例1中, 查询必须被预储和执行, 结果的描述信息必须确定(以传给getColumns()方法), 并且客户端必须接收一个包含列信息的结果集. 在案例2中, 只要准备一个简单的查询并且只用确定结果描述信息. 很显然, 案例2执行方式更好一些. 
      这个讨论有点复杂, 让我们考虑一个没有本地化支持prepared statement的DBMS服务器. 案例1的性能没有改变, 但案例2中, 因为'哑元'查询必须被执行而不是被预储使得它的性能增强了一些. 因为查询中的WHERE子句总是为FALSE, 查询在不用存取表的数据情况的下会生成没有数据的结果集. 在这种情况下,第二种方式当然比第一种方式好一些. 
      总而言之,总是使用ResultSet的metadata方法去获取列信息,像列名,列的数据类型,列的数据精度和长度等. 当要求的信息无法从ResultSet的metadata中获取时才去用getColumns()方法(像列的缺省值这些信息等). 




      获取数据 
      要有效的获取数据,就只需返回你需要的数据, 以及很多用效的方法. 本节的指导原则将帮助你使用JDB获取数据时优化系统性能. 
      获取长数据 
      如非必要, 应用程序不应请求长的数据, 因为长的数据通过网络传输会非常慢和消耗资源. 
      大多数用户并不想看到大堆的数据. 如果用户不想处理这些长数据, 那么程序应能够再次查询数据库, 在SELECT子句中指定需要的列名. 这种方式允许一般用户获取结果集而不用消耗昂贵的网络流量. 
      虽然最好的方式是不要将长数据包括在SELECT子句的列名中,但还是有一些应用程序在发送查询给JDBC驱动程序时并没有在SELECT子句中明确指出列名 (确切一点, 有些程序发送这样的查询: select * from 
      ...). 如果SELECT子句的列名中包含长数据, 许多JDBC驱动程序必须在查询时重新获取数据, 甚至在ResultSet中这些长数据并没有被程序用到. 在可能情况下,开发者应该试着去实现一种不需获取所有列数据的方法. 

      例如,看以下的JDBC代码: 
      Java代码  收藏代码
      1. ResultSet rs = stmt.executeQuery (  
      2.    "select * from Employees where SSID = '999-99-2222'");  
      3. rs.next();  
      4. string name = rs.getString (4);  

      要记住JDBC驱动程序没有知觉. 当查询被执行时它不知道哪些列是程序所要的. 驱动程序只知道应用程序能请求任意的列. 当JDBC驱动程序处理 rs.next() 请求时, 它可能会跨过网络从数据库服务器至少返回一行结果. 在这种情况下, 每个结果行会包含所有的列数据– 如果Employees表有一列包含员工相片的话它也会被包含在结果里面. 限制SELECT子句的列名列表并且只包含有用的列名,会减少网络流量及更快的查询性能. 
      另外,虽然getClob()和getBlob()方法可以允许应用程序去如何控制获取长数据, 但开发者必须认识到在许多情况下, JDBC驱动程序缺少真正的LOB定位器的支持. 像这种情况下,在暴露getClob和getBlob方法给开发者之前驱动程序必须经过网络获取所有的长数据. 
      减少获取的数据量 
      有时必须要获取长数据. 这时, 要注意的是大多数用户并不想在屏幕上看到100k甚至更多的文字. 
      要减少网络交通和改善性能, 通过调用setMaxRows(), SetMaxFieldSize及SetFetchSize()方法, 你可以减少取获取的数据量. 另一种方式是减少数据的列数. 如果驱动程序允许你定义packet的大小, 使用最小的packet尺寸会适合你的需要. 
      记住: 要小心的返回只有你需要的行和列数据. 当你只需要2列数据而你却返回的5列数据时,性能会降低 – 特别是不需要的行中包含有长数据时. 
      选择合适的数据类型 
      接收和发送某些数据可能代价昂贵. 当你设计一个schema时, 应选择能被最有效地处理的数据类型. 例如, 整型数就比浮点数或实数处理起来要快一些. 浮点数的定义是按照数据库的内部规定的格式, 通常是一种压缩格式. 数据必须被 解压和转换到另外种格式, 这样它才能被数据的协议处理. 
      获取ResultSet 
      由于数据库系统对可滚动光标的支持有限, 许多JDBC驱动程序并没有实现可滚动光标. 除非你确信数据库支持可滚动光标的结果集, 否则不要调用rs.last()和rs.getRow()方法去找出数据集的最大行数. 因为JDBC驱动程序模拟了可滚动光标, 调用rs.last()导致了驱动程序透过网络移到了数据集的最后一行. 取而代之, 你可以用ResultSet遍历一次计数或者用SELECT查询的COUNT函数来得到数据行数. 
      通常情况下,请不要写那种依赖于结果集行数的代码, 因为驱动程序必须获取所有的数据集以便知道查询会返回多少行数据. 



      选用JDBC对象和方法 
      本节的指导原则将帮助你在选用JDBC的对象和方法时哪些会有最好的性能. 
      在存储过程中使用参数标记作为参数 
      当调用存储过程时, 应总是使用参数标记(?)来代替字面上的参数. JDBC驱动能调用数据库的存储过程, 也能被其它的SQL查询执行, 或者直接通过远程进程调用(RPC)的方式执行. 当你将存储过程作为一个SQL查询执行时, 数据库要解析这个查询语句, 校验参数并将参数转换为正确的数据类型. 
      要记住, SQL语句总是以字符串的形式送到数据库, 例如, “{call getCustName (12345)}”. 在这里, 即使程序中将参数作为整数赋给了getSustName, 而实现上参数还是以字符串的形式传给了服务器. 数据库会解析这个SQL查询, 并且根据metadata来决定存储过程的参数类型, 然后分解出参数"12345", 然后在最终将存储过程作为一个SQL查询执行之前将字串'12345’转换为整型数. 
      按RPC方式调用时, 之前那种SQL字符串的方式要避免使用. 取而代之, JDBC驱动会构造一个网络packet, 其中包含了本地数据类型的参数,然后执行远程调用. 
      案例 1 
      Java代码  收藏代码
      1. 在这个例子中, 存储过程不能最佳的使用RPC. 数据库必须将这作为一个普通的语言来进行解析,校验参数类型并将参数转换为正确的数据类型,最后才执行这个存储过程.  
      2. CallableStatement cstmt = conn.prepareCall (  
      3.    "{call getCustName (12345)}");   
      4. ResultSet rs = cstmt.executeQuery ();  

      案例 2 
      Java代码  收藏代码
      1. 在这个例子中, 存储过程能最佳的执行RPC. 因为程序避免了字面的的参数, 使用特殊的参数来调用存储过程, JDBC驱动能最好以RPC方式直接来执行存储过程. SQL语言上的处理在这里被避免并且执行也得到很大的改善.  
      2. CallableStatement cstmt - conn.prepareCall (  
      3.    "{call getCustName (?)}");  
      4. cstmt.setLong (1,12345);  
      5. ResultSet rs = cstmt.executeQuery();  

      使用Statement而不是PreparedStatement对象 
      JDBC驱动的最佳化是基于使用的是什么功能. 选择PreparedStatement还是Statement取决于你要怎么使用它们. 对于只执行一次的SQL语句选择Statement是最好的. 相反, 如果SQL语句被多次执行选用PreparedStatement是最好的. 
      PreparedStatement的第一次执行消耗是很高的. 它的性能体现在后面的重复执行. 例如, 假设我使用Employee ID, 使用prepared的方式来执行一个针对Employee表的查询. JDBC驱动会发送一个网络请求到数据解析和优化这个查询. 而执行时会产生另一个网络请求. 在JDBC驱动中,减少网络通讯是最终的目的. 如果我的程序在运行期间只需要一次请求, 那么就使用Statement. 对于Statement, 同一个查询只会产生一次网络到数据库的通讯. 
      对于使用PreparedStatement池的情况下, 本指导原则有点复杂. 当使用PreparedStatement池时, 如果一个查询很特殊, 并且不太会再次执行到, 那么可以使用Statement. 如果一个查询很少会被执行,但连接池中的Statement池可能被再次执行, 那么请使用PreparedStatement. 在不是Statement池的同样情况下, 请使用Statement. 
      使用PreparedStatement的Batch功能 
      Update大量的数据时, 先Prepare一个INSERT语句再多次的执行, 会导致很多次的网络连接. 要减少JDBC的调用次数改善性能, 你可以使用PreparedStatement的AddBatch()方法一次性发送多个查询给数据库. 例如, 让我们来比较一下下面的例子. 
      例 1: 多次执行Prepared Statement 
      Java代码  收藏代码
      1. PreparedStatement ps = conn.prepareStatement(  
      2.    "INSERT into employees values (?, ?, ?)");  
      3. for (n = 0; n < 100; n++) {  
      4.   ps.setString(name[n]);  
      5.   ps.setLong(id[n]);  
      6.   ps.setInt(salary[n]);  
      7.   ps.executeUpdate();  
      8. }  

      例 2: 使用Batch 
      Java代码  收藏代码
      1. PreparedStatement ps = conn.prepareStatement(  
      2.    "INSERT into employees values (?, ?, ?)");  
      3. for (n = 0; n < 100; n++) {  
      4.   ps.setString(name[n]);  
      5.   ps.setLong(id[n]);  
      6.   ps.setInt(salary[n]);  
      7.   ps.addBatch();  
      8. }  

      ps.executeBatch(); 
      在例 1中, PreparedStatement被用来多次执行INSERT语句. 在这里, 执行了100次INSERT操作, 共有101次网络往返. 其中,1次往返是预储statement, 另外100次往返执行每个迭代. 在例2中, 当在100次INSERT操作中使用addBatch()方法时, 只有两次网络往返. 1次往返是预储statement, 另一次是执行batch命令. 虽然Batch命令会用到更多的数据库的CPU周期, 但是通过减少网络往返,性能得到提高. 记住, JDBC的性能最大的增进是减少JDBC驱动与数据库之间的网络通讯. 
      选择合适的光标类型 
      选择适用的光标类型以最大限度的适用你的应用程序. 本节主要讨论三种光标类型的性能问题. 
      对于从一个表中顺序读取所有记录的情况来说, Forward-Only型的光标提供了最好的性能. 获取表中的数据时, 没有哪种方法比使用Forward-Only型的光标更快. 但不管怎样, 当程序中必须按无次序的方式处理数据行时, 这种光标就无法使用了. 
      对于程序中要求与数据库的数据同步以及要能够在结果集中前后移动光标, 使用JDBC的Scroll-Insensitive型光标是较理想的选择. 此类型的光标在第一次请求时就获取了所有的数据(当JDBC驱动采用'lazy'方式获取数据时或许是很多的而不是全部的数据)并且储存在客户端. 因此, 第一次请求会非常慢, 特别是请求长数据时会理严重. 而接下来的请求并不会造成任何网络往返(当使用'lazy'方法时或许只是有限的网络交通) 并且处理起来很快. 因为第一次请求速度很慢, Scroll-Insensitive型光标不应该被使用在单行数据的获取上. 当有要返回长数据时, 开发者也应避免使用Scroll-Insensitive型光标, 因为这样可能会造成内存耗尽. 有些Scroll-Insensitive型光标的实现方式是在数据库的临时表中缓存数据来避免性能问题, 但多数还是将数据缓存在应用程序中. 
      Scroll-Sensitive型光标, 有时也称为Keyset-Driven光标, 使用标识符, 像数据库的ROWID之类. 当每次在结果集移动光标时, 会重新该标识符的数据. 因为每次请求都会有网络往返, 性能可能会很慢. 无论怎样, 用无序方式的返回结果行对性能的改善是没有帮助的. 
      现在来解释一下这个, 来看这种情况. 一个程序要正常的返回1000行数据到程序中. 在执行时或者第一行被请求时, JDBC驱动不会执行程序提供的SELECT语句. 相反, 它会用键标识符来替换SELECT查询, 例如, ROWID. 然后修改过的查询都会被驱动程序执行,跟着会从数据库获取所有1000个键值. 每一次对一行结果的请求都会使JDBC驱动直接从本地缓存中找到相应的键值, 然后构造一个包含了'WHERE ROWID=?'子句的最佳化查询, 再接着执行这个修改过的查询, 最后从服务器取得该数据行. 
      当程序无法像Scroll-Insensitive型光标一样提供足够缓存时, Scroll-Sensitive型光标可以被替代用来作为动态的可滚动的光标. 
      使用有效的getter方法 
      JDBC提供多种方法从ResultSet中取得数据, 像getInt(), getString(), 和getObject()等等. 而getObject()方法是最泛化了的, 提供了最差的性能。 这是因为JDBC驱动必须对要取得的值的类型作额外的处理以映射为特定的对象. 所以就对特定的数据类型使用相应的方法. 
      要更进一步的改善性能, 应在取得数据时提供字段的索引号, 例如, getString(1), getLong(2), 和getInt(3)等来替代字段名. 如果没有指定字段索引号, 网络交通不会受影响, 但会使转换和查找的成本增加. 例如, 假设你使用getString("foo") ... JDBC驱动可能会将字段名转为大写(如果需要), 并且在到字段名列表中逐个比较来找到"foo"字段. 如果可以, 直接使用字段索引, 将为你节省大量的处理时间. 
      例如, 假设你有一个100行15列的ResultSet, 字段名不包含在其中. 你感兴趣的是三个字段 EMPLOYEENAME (字串型), EMPLOYEENUMBER (长整型), 和SALARY (整型). 如果你指定getString(“EmployeeName”), getLong(“EmployeeNumber”), 和getInt(“Salary”), 查询旱每个字段名必须被转换为metadata中相对应的大小写, 然后才进行查找. 如果你使用getString(1), getLong(2), 和getInt(15). 性能就会有显著改善. 
      获取自动生成的键值 
      有许多数据库提供了隐藏列为表中的每行记录分配一个唯一键值. 很典型, 在查询中使用这些字段类型是取得记录值的最快的方式, 因为这些隐含列通常反应了数据在磁盘上的物理位置. 在JDBC3.0之前, 应用程序只可在插入数据后通过立即执行一个SELECT语句来取得隐含列的值. 
      例如: 
      //插入行 
      Java代码  收藏代码
      1. int rowcount = stmt.executeUpdate (  
      2.    "insert into LocalGeniusList (name) values ('Karen')");   
      3. 查看(846) 评论(0) 收藏 分享 管理

      4. 【转】程序员学习能力提升三要素

        2013-03-21 13:57:21

        IT技术的发展日新月异,新技术层出不穷,具有良好的学习能力,能及时获取新知识、随时补充和丰富自己,已成为程序员职业发展的核心竞争力。本文中,作者结合多年的学习经验总结出了提高程序员学习能力的三个要点。 

        众所周知,现在是一个知识爆炸的时代,知识更新非常快。据测算,一个大学毕业生所学到的知识,在毕业之后2年内,有效的不过剩下5%。对于软件行业而言,这种形势更为明显,我们赖以立足的,不在于我们现在掌握了多少知识,而是我们有多强的学习能力。 

        学习人人都会,但不同的人学习效果却千差万别。一个善于学习的人,首先应该是一个善于读书的人,懂得如何高效地学习,并且拥有良好的心态。唯有如此,才能成为一个卓有成效的学习者,成就卓越的程序人生。 

        要善于读书 

        买书是最划算的投资 

        古人云:“书中自有黄金屋,书中自有颜如玉。”这说明先贤们早认识到,买书是最划算的投资。 

        我刚出道时,拿着非常微薄的工资。有一次向主管抱怨道:“现在的书真贵啊,这点工资连饭都吃不起,更别说买书了!”主管对我说:“不要吝惜买书的钱,宁可忍着不吃饭,也不要忍着不买书,因为买书是回报率最高的投资。” 

        主管的话让我非常震动。后来,我看到喜欢的书时,再也没有手软过。通过不断学习,我的开发能力不断提高,工资水平也大幅提高。一年后,我一个月工资的涨幅,就足够买两年的书了。你说,还有比这更划算的投资吗? 

        一本书,哪怕只有一页纸是有用的,它所将产生的潜在价值,也会远远超过书本身的价格。当然,书不在多,踏踏实实消化掉一本好书,比泛泛而读10本普通书,要有价值得多。 

        多读经典书 

        现在市面上给程序员读的书种类非常丰富。人生有涯,我们只能有选择性地看,要多看好书、多看经典书。 

        软件开发方面的图书大致分为三类。 

        1. 浅显的入门类图书。这类书的标题往往是《XX天精通XXX》、《XXX从入门到精通》、《XX开发实战》等,通常从软件的安装讲起。有人批评这类书为烂 书、毫无价值,这并不公平。至少我曾经从这些书中学到了一些东西。即使是21天系列书,也有适合看的人群。只不过,它一般也就只能看21天而已,过后就可 以扔到废纸堆。这类书只适于还没有入门的初学者,从中学到一些入门招式。在刚起步时一般买一本就够了。如果你善于使用搜索引擎,那几乎没有买这类书的必 要。 

        2. 国内外高手写的实战类图书。这类书实战性很强,将技术及原理讲得很透彻。比如《Windows环境下32位汇编语言程序设计》、《深入解析MFC》、 《Delphi深度探索》、《深入浅出WPF》、《深入剖析ASP .NET组件设计》等。以前这类书都是从国外翻译或从台湾引进的,现在国内高手越来越多,出自国内作者的图书也越来越多。如果能在学习的每个方向看两三本 这类图书,并通过实践消化掉,那么毫无疑问,你会成为一名优秀的程序员。 

        3. 国外大牛写的揭露本质、有丰富思想的书。这类书就是所谓的经典书,例如《代码大全》、《编程珠玑》、《设计模式》、《重构》、《代码整洁之道》等。经典书 就像一个有深度、有思想的朋友,能给你启发,每次阅读都会有新的收获,这类书具有真正的收藏价值。看经典书永远是正确的选择,它绝不会浪费你的时间,因为 经典书是无数人沙里淘金、帮你挑选出的结果。 

        然而,阅读这类书并不是一件容易的事情。读者需要有丰富的开发经验,才能与作者产生共鸣。如果一本经典书你看得很辛苦,那么很有可能是因为你功力未够,这种情况下不要着急,慢点来,不妨先将其束之高阁,多看看实战类图书。过一段时间再回头来看,也许你会有新的惊喜。 

        不要在上班时间看书 

        一个善于学习的人,首先要善于利用一切时间来学习。雷锋曾说过:“时间就像海绵里的水,只要愿意挤,总还是有的。”然而,当我们从上班时间中挤时间学习时,就千万要注意了,不要在上班时间看书! 

        上班时间看书,不但是一件很敏感的事情,而且非常吸引眼球,很快就会引起周遭的不爽。首先老板心里不爽:“我给你钱是让你来工作的,不是来学习的”;其次同事们也不爽:“我们工作都做不完,瞧,这小子真闲啊”。用不了多久,你就会成为被众人排斥的异类,这最终会让你自己“很受伤”。 

        要高效学习 

        只学习与工作相关的东西 

        我曾发现不少程序员在学习方面找不到方向,一会儿学学C#,一会儿学学Java,看了最新的编程语言排行榜,又觉得该学C++。这样左抓抓,右挠挠,只会让你觉得更痒。 

        学习最忌三心二意。俗话说:“伤其十指不如断其一指”,每门都学一点,还不如专心学好一个方向。这个道理谁都懂,可是又该学哪个方向呢?难道只能跟着感觉走吗?不!最实际的方向,应该跟着工作走,工作需要什么,我们就学什么,把工作需要的技能熟练掌握,有很多好处。 

        首先,可以集中精力,在某一方面钻研得更加深入。所谓“百招会不如一招绝”,有了绝招,你还怕不能在“武林”立足吗?《天龙八部》中的慕容复武功博学无比,最后还不是被只会一招六脉神剑的段誉打得落花流水? 

        其次,可以学得更快、更深入,因为学习更具有针对性,而且可以立即在工作中运用,可以马上检验出学习的效果。对存在的问题进行深入研究,掌握的知识也会更加牢固。 

        再次,学习与工作结合在一起,工作时间也就成了学习时间,这样就突破了三个8小时的限制:有人说,我们每天所拥有的时间可以分为三个8小时,工作8小时,睡觉8小时,另外还有8小时可以自己自由支配。工作和睡觉的两个8小时大家都一样,决定人生高度的是另外这个8小时。当我们把学习的焦点放到与工作相关的知识上时,工作时间中的很大一部分,同时也就成了宝贵的学习时间,这真是一举两得的美事啊。 

        问题是最好的学习机会 

        日本经营之神松下幸之助曾说过:“工作就是不断发现问题、分析问题、最终解决问题的过程,晋升之门将永远为那些随时解决问题的人敞开着。”可见,工作过程中有问题是正常,没有问题才是真正的问题。在发生问题时,能勇于面对问题、解决问题的人,才是公司真正的骨干。 

        现实中,很多人总是千方百计回避问题。当上司安排一项艰巨的任务时,也想尽办法推托。殊不知,对于个人而言,问题其实是最好的学习机会。往往那些愿意接受困难工作的人,能力会越来越强,那就是因为他们在克服困难的过程中取得了巨大的进步。 

        织网式学习 

        知识的广度和深度都很重要。作为一名程序员,能深入把握技术细节,是写出优质代码的保证。但对于一名项目经理而言,知识的广度更显重要。项目中碰到的问题往 往具有综合性,只有具备广博的知识,才能快速对问题进行分析和定位。在程序员成长的道路上,我们必须有意识地扩大自己的知识面,形成更完善的知识体系。 

        我曾经编写过一个网络信息采集软件,这个软件可以从具有列表页的网站中按字段设置采集信息,支持自定义字段、页面多级关联、下载附件、支持多种数据库、可视 化定义等特性。刚开始,我认为这个软件只是一个比较大的功能点而已,后来发现这个不起眼的功能关联着大量的知识点(如图1所示)。 



        在开发过程中,我顺藤摸瓜,逐个击破,对很多知识点进行了细致的学习研究。软件开发完成后,我的知识体系网也进一步得到了补充和完善。 

        经常思考总结 

        子曰:“学而不思则罔”。人只学习不思考,就会迷惑,难以把握事情的本质。这就好比一个学武之人,只习得其形,而未得其神,难以成为真正的高手。 

        从入门到成为高手的过程中,程序员往往要经过几次顿悟。记得有一次,我领悟到了一个很简单的结论:“原来高级编程语言中的类库是封装了Windows API来实现的。”后来碰到一些自带类库无法实现的功能时,我就会想到,其实可以通过调用Windows API来实现。利用这个思路,我解决了一些看起来很难的问题,得到老板的赏识,从而很快获得提升。 

        顿悟非常可贵,然而它不是随便发生的,而是经过一次次苦苦思索之后、灵光闪现的结果。思考的过程,其实就是将外在知识内化为自己的知识的过程。而顿悟,则批量实现这种内化,将无数个知识点连接在一起,达到融会贯通的境界。 

        克服“高原现象” 

        爱学习的人都会有这样的经历:学习持续了一段时间之后,往往会有一个瓶颈期,长时间似乎都没有什么进步,于是内心非常着急。 

        这种情况,实际上是由人的学习规律决定的一种“高原现象”。据研究,学习者在刚开始进步快,随后有一个明显的或长或短的进步停顿期,后期进步慢,中间的停顿期叫高原期(如图2所示)。 



        高原期,实质是一个消化期。由于前期的学习积累了太多的知识点,这些知识点在大脑中乱作一团,还没有形成一个知识体系。这时需要一定的时间来消化它,让它融会贯通,经常思考总结可以快速帮你跨过高原期。 

        处于高原期时,还可以换一个相关的方向来学习。例如,编程语言学不下去了,可以学习一下设计模式,设计模式也学不下去了,再换成数据库。通过学习这些相关的知识,不但补齐了知识体系中的短板,而且各个知识点之间可以互相启发,帮助你实现顿悟,跨过高原期。 

        要有好心态 

        学习要静心 

        急于求成是学习过程中普遍存在的一种心态,这可以理解。毕竟作为一名程序员,要学的东西实在太多,而社会又是那样的浮躁,让人觉得一切都是那样的不安全、不确定,似乎只有学得快一点,才能跟上社会的脚步。 

        可是“欲速则不达”,想快快地学,往往会形成东一榔头、西一棒槌的学习方式,每一个点都没有吃透。心沉不下去,知识也会沉不下去。要想成为真正的高手,只能静下心,一步一个脚印慢慢来。 

        学习是持续一生的过程 

        人生,就是一个自我完善过程。 

        子曰:“吾十有五而志于学,三十而立,四十而不惑,五十而知天命,六十而耳顺,七十而从心所欲,不逾矩。”可见孔子也不是天生的圣人,也在不停地学习、进步,从“志于学”到“从心所欲,不逾矩”,孔子一共花了55年的时间。 

        作为一名程序员,更需要不断丰富自己的知识库。我们所知道的东西,就像一个白色的圆圈,圈外则是黑暗的未知的世界。当圆圈越大,所接触到的黑暗部分就越多。我们只有不停地学习,才能打破更多的黑暗,找到更多光明。

        保持饥饿,保持愚蠢 

        我非常喜欢乔布斯的一句话:“求知若饥,虚心若愚”(Stay Hungry,Stay Foolish)。其实我更喜欢它更原生态的翻译“保持饥饿,保持愚蠢”。我们只有认识到自己还很饥饿和愚蠢,才会像没吃饱一样,由衷地需要学习、爱上学习。 

        作者尹华山,资深软件工程师,现从事IT项目管理工作,拥有PMP认证、信息系统项目管理师认证、系统集成高级项目经理认证。 
      5. 【转】风雨20年:我所积累的20条编程经验

        2013-03-21 13:55:31

        编者按:原文作者乔纳森·丹尼可(Jonathan Danylko)是一位自由职业的web架构师和程序员,编程经验已超过20年,涉足领域有电子商务、生物技术、房地产、医疗、保险和公用事业。正如乔纳森在文中所言,本文适合刚毕业的大学生和刚入门的程序员。如果你已是高级开发人员,或许你能在本文中看到自己的身影。 

        从11岁时,我就一直在编程,并且一直都很喜欢技术和编程。这些年来,我积累了一些艰难又容易的经验。作为一名程序员,你或许还没这些经验,但我会把它们献给那些想从中学到更多的朋友。 
        我会持续更新这些经验,我可能还会有更多的感想,但就我这20年来看,我想下面这个列表中基本不需要增添额外的东西了。下面就是我至今最难忘的经验。 

        1. 估算解决问题所需要的时间。不要怕,承认吧!我曾见过一些程序员为了解决一个特殊问题而坐在显示器前面8小时。为自己定一个时间限制吧,1小时、30分钟或甚至15分钟。如果在这期间你不能解决问题,那就去寻求帮助,或到网上找答案,而不是尝试去做“超级堆码员”。 

        2. 编程语言是一种语言,只是一种语言。随着时光推移,只要你理解了一种语言的原理,你会发现各种语言之间的相似之处 。你所选择的语言,你应该觉得“舒服”,并且能够写出有效(而且简洁)的代码。最重要的,让语言去适应项目,反之亦然。 

        3. 不要过于注重程序的“设计模式”。 有时候,写一个简单的算法,要比引入某种模式更容易。在多数情况下,程序代码应是简单易懂,甚至清洁工也能看懂。 

        4. 经常备份代码。在我年轻时,我就有过因硬盘故障而丢了大量代码的经历,这经历很恐怖的。只要你一次没有备份,就应当像有着严格的期限,客户明天就需要。此时就该源码/版本控制软件大显身手了。 

        5. 承认自己并不是最顶尖的程序员 – 知不足。我常想,我对编程了解已足够多,但是总有其他人比你优秀。正所谓,“一山总比一山高”。所以,向他们看齐吧! 

        6、学习再学习。正如第5点所说,我经常会在手里拿一本计算机或编程相关的杂志或书(不信,可以问我的朋友)。诚然,总有很多你不知道的技术,你可以从中学习以保持不落后。如果你有一种灵巧的方式来获取你需要的新技术,那你每天都应该坚持学习。 

        7. 永恒的变化。你对待技术/编程知识,就应像你对待股票一样:多样化。不要在某一特定技术上自我感觉良好。如果那种技术或语言已经没有足够支持,那你还不如现在就开始更新你的简历,并启动培训新计划。我能保持前行的主要原则是什么呢?至少了解两到三种语言,所以,如果某种语言过时了,你在学习新技术的时候还可以依靠另一种语言。 

        8. 提携新人。协助并且培养初级/入门的开发人员学习优秀的编程方法和技巧。也许你还不知道,在帮助他们向更高一层前进时,你自己也在向更高一层提升,你会更加自信。 

        9. 简化算法。代码如恶魔,在你完成编码后,应回头并且优化它。从长远来看,这里或那里一些的改进,会让后来的支持人员更加轻松。 

        10. 编写文档。无论是Web服务的API,还是一个简单的类,你尽量编写相应文档。我曾经引以为豪的代码注释,因过度注释而有人指责。给三行代码加一行注释,只需要你几秒时间。如果那是一个比较难以理解的技术,千万别担心过多注释。如果你能很好做好自己的工作,大多数架构师、后备程序员、支持组都会感激你。 

        11. 测试、测试再测试。我是一名黑盒测试粉丝。当你完成编码后,你“被认可”的时候就开始了。如果你们公司有QA部门,如果你的代码中有错误,那你得到的评论,会比项目经理还多。如果你不彻底测试自己的代码,那恐怕你开发的就不只是代码,可能还会声名狼藉。 

        12. 庆祝每一次成功。我见过很多程序员在解决编程技术难题后,会和同伴握手、击掌或甚至手舞足蹈。每个人在生命中都会碰到“顿悟”。如果一个程序员高兴地跑来叫你去看他的非凡代码,也许你已经看过这样的代码100遍了,但你也应该为了这个家伙而庆祝第101次。(编者注:《庆祝成功的九种方式》。) 

        13. 经常检查代码。 在公司,你的代码要经常检查(包括自查和其他同事检查)。不要把别人的检查,看成是对代码风格的苛求。应该把它们看作是有建设性的批评。对个人来说,经常检查你的代码并且自问,“我怎样才能写得更好呢?” 这会加速你的成长,让你成为一个更优秀的程序员。 

        14. 回顾你的代码。在看到自己以前的代码时,通常会有两种方式:“难以至信,这代码是我写的”和“难以至信,这代码是我写的”。第一种往往是厌恶的语气,并在想如何改进它。你也许会惊叹,旧代码也能复活成为一种更好的程序,甚至是一个完整的产品。第二种通常带着惊奇和成就感。开发人员应该一到两个自己完成的项目成果,能让众人不禁而立并注目而观的项目。同样,基于你优越的编程能力,你可以把过去的程序或项目拿出来,把它们更新为更加优秀的产品或想法。 

        15. 幽默是不可缺的。在我20年的开发生涯中,我还没有碰到哪位程序员是没有幽默感的。实际上,干我们这行,幽默是一项必备品。 
        16. 谨防那些无所不知的程序员,不愿分享的程序员,还有经验不足的程序员。当你遇到这几种程序员时,你自己要谦虚。无所不知的程序员,更想当一个英雄而不是团队成员;保守的程序员则是在编写着他们独享的代码;而经验不足的程序员则会每十分钟就来问你一下,当代码完成后,代码已经是你的,而不是他们。 

        17. 任何项目都不会那么简单。朋友、家人和同事曾请求我仓促做一些事情,仓促做一个程序或者网站。对于这样的事,应该从双方做计划,才能做出令双方都会满意的东西。如果某人起初只是需要一个使用 Microsoft Access的、只有有3个页面的网站,但来就很可能变成一个有15个页面的网站,并使用SQL Server,有一个论坛,还有一个定制的CMS(内容管理系统)。 

        18. 任何时候不要想当然。假如你承接一个简单的项目,你可能会认为某个部分可以轻松完成。千万别这样想!除非你有一个类、组件、或者一段已经写好的代码,并且在现有的项目已经测试通过。不要认为这将是很容易的。 

        19. 没有已经完成的软件。曾经有一位程序员告诉我,没有软件是已经完成的,它只是“暂时完成了”。这是明智的忠告。如果客户还在使用你写的程序,并经受了时间的考验。如果有机会,你仍在更新它,这并不是什么坏事,这让你不断地前行。 

        20. 耐心是一种美德。当客户、朋友或家庭成员用电脑的时候,他们也许会受挫,进而想砸电脑,或气冲冲地离开。我一直在告诉他们,“是你掌控电脑,不是电脑掌控你。”对于用作编程的电脑,你要有一定的耐心。一旦程序员知道问题所在后,他们就会站在电脑的角度看问题,并且说“哦,这就是为什么它是这样做。” 

        编者后话 
        对本文深有感触!虽然本文没有华丽的辞藻,其中朴实的道理,其实并非只适用程序员,同样可以扩展到其他行业。记得以前练字时,总感觉当时写得很好,但后来回头再看时,也会想“这居然是我写的字!” 
        在阅读本文的朋友,不知你是否也有看到了自己的身影呢?欢迎你在微博或评论中和大家一起分享感触。
      6. 【转】代码评审指导

        2013-03-21 13:50:52

        转载地址;http://www.oschina.net/translate/code-review-guidelines 

        代码评审就是源代码的系统性审核(也叫同业互查),目的是找到并且修复在开发最初阶段被忽视掉的一些问题,以此来改进软件质量,同时提高程序员的编码能力。 
        代码评审为什么重要呢? 
        引自“Code Complete” 
        “软件测试本身有一定的局限性,在检测软件缺陷中,单元测试发现缺陷的比例大概是25%,功能测试占到35%,集成测试占到45%。相比之下,设计和代码审查的效率要高很多,发现缺陷的比例可以达到55%-60%。对审查结果的案例学习也是很给力的。 
        以下是一些数据分析来说明通过代码审查可以及早发现软件问题从而降低成本的重要性。 


        代码审查的主要目标有: 
              1.尽早的发现和修复代码中的缺陷。 
              2.以更好的共享和理解代码作为团队成员相互学习的基础。 
              3.有助于保持设计和实现的一致性。 
              4.帮助发现大家容易犯的共同错误,从而减少团队的返工。 
              5.构建投资者对执行过程中技术质量的信心。 
              6.大家对代码有一个平均的理解,这有助于提高团队成员的互换性,特别当某个成员无法继续工作的时候就显得尤为重要。 
              7.从不同的角度看问题。“另外一双眼睛”增加了客观性。这个原因也是把开发和测试团队分开的原因,同行评审代码发现问题需要提供给他们一定的距离(译者注:这个距离可以使他们和代码的作者从不同的角度看问题)。
              8.骄傲/奖励。对他编码能力的认可对许多程序员来说,是一个显著的奖励。 
              9.团队凝聚力。一起工作可以让团队成员变得更加紧密。它还可以让因为写代码而造成的隔离得到一个短暂的释放。     


        代码审查者所关注的地方主要有以下几点: 
        通用的单元测试 
        注释和编码规范 
        错误处理 
        资源泄漏 
        线程安全 
        控制结构 
        性能 
        功能 
        安全 
        角色和职责 
        1.开发者:是要审查的代码的作者,并且也是发起审查活动的人。 
        2.审查者:是审查代码并且要报告审查结果给开发者的人。 


        像任何技能,再完美的评审工作都来源于实践。如下是一些提示,可能对您选择正确的途径有帮助: 
        对开发人员的提示: 
        1.最初的评审者应该是作者自己。 
        2.为自己的评审的代码更趋于关注的东西创建清单。这些清单中有些是很容易收集整理的。它应该遵循编码标准文档的大纲,因为这是你的评审清单,即便是有问题,你可以抓住你很难做的东西,并且跳过你认为很简单的东西。按照你列出来的清单往下测试你的代码,并修改你发现的问题。这样你不仅仅是减少了团队其他人发现的问题,而且也减少了评审会议的时间,大家都会非常高兴能够使用很短的时间开评审会议。 
        3.你和你的代码不等同。记得整个评审的目的是为了发现问题,并且问题是一定会被发现的。有人发现了你的问题,千万不要介意啊。 
        4.要理解并接受你所犯的错误。评审的目的是尽早的发现问题,避免将问题带到以后的产品中去。幸运的是,除了在JPL我们几个开发rocket guidance software,我们的行业很少有致命的错误,所以我们可以,我们应该学会笑,然后继续。 
        5.无论你知道的"karate"有多么的多,其他人也总是会知道的更多。如果你问,这样的人员可以教你一些新举措。寻求和接受别人的意见,特别是当你觉得不需要的时候。 
        6.未经咨询讨论,不重写代码。”重写代码“和”修改代码“只是一步之遥,知道差异、追求在代码审查的框架内的风格变化,不要一个人孤独的奋战。 
        7.世界上唯一不变的就是变化。敞开心扉,并微笑着接受它。留心对您的需求、平台或工具都作为新的挑战的每一个变化,不至于最后严重到不可收拾的地步。 
        8.为你的信念而战,但从容地接受失败。要明白,有时候你的想法会被推翻,即使你被证明是正确的,不被报复或者数落,"我告诉过你了"这句话至少在好几次以上,并且不让你离开的想法滋生。 
        9.不要做"房间里的人"。不要成为在黑暗处编码只为买可乐的人。房间里的人和外界失去了联系,看不见外面,并且失控,他没有开放、协作的工作环境。 
        10.请注意审查会议不是解决问题的会议。 
        11.有助于维护编码标准。提供要添加到编码标准中的东西,这些事我们讨论时编码标准中没有的东西。挑战之一,开发人员已经组织比较麻烦的代码审查工作,他们往往不知道接下来的问题将从哪里来。如果你把每个问题的文档编写标准,下次你的代码审查你可以用清单检查它。它还会帮到你巩固你已经掌握的概念,让您更不容易错过反馈的机会。 


        给审查者的提示: 
        1. 批评代码而不是人。体贴开发者而不是代码。尽可能的使用正面、积极的,并且有利于提高代码质量的评价。评价要与项目标准、开发守则、性能提升等因素相关。 
        2. 尊重了解程度不如你的人,并保持耐心。非技术人员在面对开发人员时,通常认为恃才傲物的比较牛逼,自卑的比较烂。不要用愤怒与焦躁来增强这样的陈规旧习。 
        3. 真正的权威来源于学识,而不是地位。学识造就权威、权威带来尊重。 
        4. 注意!审核代码的会议并不意味着解决问题的会议。 
        5. 使用疑问句而不是肯定句。肯定句是刺耳的。“在这里,你并没有遵循标准”,这样的话语就是一种攻击,无论你是否有意而为之。“是什么原因促使你使用现在的方法?”会得到更多的信息。很显然,这样的问题无法以一种讽刺或是傲慢的语气来表述;但这样做是正确的,如此的对话会让开发人员开始思考,继而寻找更好的方法。 
        6. 避免使用“为什么”。尽管很难避免,但如此而为能够充分地改善气氛。正如肯定句刺耳一样,“为什么”也如同一把尖刀。大多数的“为什么”能够经过变过而被省略,并达到激动人心的效果。例如,“你为什么没有遵循标准?”->”出于何处原因在这里对标准做了一些变化?” 
        7. 记得要赞美。审核代码的目的不仅仅是告诉开发人员如果提高,同样也要告诉他们做得好。人类的本性就是想要被认可,不要仅仅体现出我们的过错。因为开发是一项创造性的工作,开发人员用”心“在写代码,所以这更需要赞美,即使更多的批判。 
        8. 确保你有良好的编码规范作为参考。审核者遵循的依据应当是组织的编码规范。编码规范是一份被开发人员普遍认可的协议,用于编写优质的代码。如果要讨论一项并不在代码规范中的代码,你首先要做的工作是建立相关的代码规范。你应该不断地询问自己是否在讨论编码规范中的事项。 
        9. 条条大路通罗马。尽管开发者使用的方法与你想象的大相径庭,但不一定是错的。审核代码的目标是代码质量。如果达到了目标并遵循了标准,这就是你想要的。 
        10. 不要草草地进行代码审核,但需要及时的。你的同伴正在等你审核! 
        11. 一次审核200-400行代码。 

        安全代码审查 
        如果一个程序需要严密关注安全,那么 www.owasp.org 是一个获取资源的好地方。这里有一个非常好的安全代码审查文档可以参照: https://www.owasp.org/images/2/2e/OWASP_Code_Review_Guide-V1_1.pdf 
        代码审查结果的指定严重程度 
        代码中发现的问题的严重程度应该如下所示。审查者必须首先聚焦高级严重程度的问题,然后是中级严重程度的问题,最后是低级严重程度的问题。         
        命名规则和代码风格=低级 
        控制结构或逻辑问题=中级 
        冗余问题=高级 
        性能问题=高级 
        安全问题=高级 
        可测量性问题=高级 
        功能性问题=高级 
        错误处理=高级 
        可重用性问题=中级 


        开发者和审查者的检查表 
        对于代码审查来说,检查表是一个非常推荐的方式,它可以让你很容易的找到你忘记的那些事情,并且对不管是作者还是审查者来说都十分有用。遗漏是最难发现的缺陷,毕竟要去审查那些不存在的东西是很难发现的。检查表是最好的解决这个问题的办法,它可以提醒作者或审查者去花时间寻找是否遗漏了一些东西。检查表也可以让作者和审查者确认:所有的错误都将会被处理,所有的函数的参数都可以经受不正确的值测试,单元测试已经被建立起来。 
        另外一个实用的原则是个人的检查表。每个人通常都会犯同样的15-20个错误。如果你注意到你通常所犯的错误是哪些,你可以做一个你自己的检查表(PSP,SEI,CMMI也推荐这么做)。审查者通常检查你所犯的一般错误。所有你需要去做的是保持一个小的检查表列出你工作中的一般错误,特别是那些你忘记去做的事情而导致的错误。一旦你开始在一个审查表中记录你所犯的错误,你就必须开始让这个检查表逐渐变的小起来。这个规则将在你的头脑里不断的闪现,你的错误率会下降。我们已经看到了这种现象的反复发生。 


        为程序员准备的检查单 
        检查项
        是否符合? 
        代码没有编译错误
        代码不仅包含单元测试用例,而且测试均已通过。
        代码包含了恰当的注释和代码文档(如:JavaDoc)
        代码是清晰明了的,包括缩进、行长、没有保留注释的代码、没有单词拼写错误,诸如此类)
        恰当的使用了异常
        恰当的使用了Log,方便运维的定位错误。
        删除了未用的import语句
        消除了所有 Eclipse 报的警告错误
        已尽可能的考虑了NPE
        所有代码已符合代码规范
        代码中没有遗留模板代码和测试用代码
        代码中没有使用硬编码,也没有仅用于开发环境的代码。
        性能问题是否有考虑?
        安全问题是否有考虑?
        代码中是否适当的释放了资源,比如HTTP连接、数据库连接、文件句柄等等。
        框架中隐晦部分或特殊情况以文档化或者其它等价的方式说明。 
        是否优先使用可重用组件或库中相同功能的函数来替换自己的实现?
        确保线程完全,避免死锁。
        为评审人员准备的检查单 
        检查项
        是否符合? 
        评审意见要通俗易懂并侧重于代码的可维护性。
        评审意见是否言简意赅。
        尽可能的使用泛型。
        实参被恰当的使用。
        异常被恰当的使用。
        重复代码已被剔除。
        框架被恰当的使用 – 方法被恰当的定义。
        命令类被设计成只对应于某个单独的功能,而不是多合一。
        JSPs代码里不能包含业务代码
        代码中附带正确的单元测试用例
        常见问题被标出。
        潜在的线程问题被尽可能的排除。
        所有考虑到的安全问题都被解决。
        代码考虑了性能问题
        代码实现与当前的产品设计及技术架构保持一致。
        代码是可测试的
        代码中是否包含不必要的静态方法或代码段。
        代码完全符合代码标准
        恰当的使用了Log,包括正确的Log级别和Log描述。
        检查NPE和AIOB 
      7. 浅谈设计模式之工厂模式

        2013-03-21 13:41:12

        设计模式:是指在软件开发中,经过验证的,用于解决在特定环境下,重复出现的、特定问题的解决方案。

             在学习设计模式之前,我们要回顾一下接口的知识。java应用开发中,要“面向接口编程”。接口的核心思想就是“封装隔离”,使用接口的好处,由于外部调用和内部实现被接口隔离开了,那么只要接口不变,内部实现的变化不会影响到尾部应用,从而使得系统更灵活,具有更好的扩展性和可维护性,因此我们说,接口是系统可插拔性的保证。

             接口和抽象类的选择原则:1.优先选用接口;2.在既要定义子类的行为,又要为子类提供公共的功能时应选择抽象类。

         

        简单工厂

        定义:提供一个创建对象实例的功能,而无须关心其具体实现。被创建实例的类型可以是接口、抽象类,也可以是具体的实现。示例代码如下

        定义接口:

        Java代码  
        1. package com.wxl.simplefactory;  
        2.   
        3. /** 
        4.  * 接口的定义,该接口可以通过简单工厂来创建 
        5.  */  
        6. public interface Api {  
        7.     public void operation(String s);  
        8. }   

        对接口的实现:

        Java代码  
        1. package com.wxl.simplefactory;  
        2. /** 
        3.  * 对接口api的实现  
        4.  */  
        5. public class ImplA implements Api {  
        6.   
        7.     @Override  
        8.     public void operation(String s) {  
        9.         System.out.println("ImplA s == "+s);  
        10.     }  
        11. }  
        Java代码  收藏代码
        1. package com.wxl.simplefactory;  
        2. /** 
        3.  * 对接口api的实现  
        4.  */  
        5. public class ImplB implements Api {  
        6.   
        7.     @Override  
        8.     public void operation(String s) {  
        9.         System.out.println("ImplB s == "+s);  
        10.     }  
        11. }  

        Java代码  
        1.   

        工厂类:

        Java代码  
        1. package com.wxl.simplefactory;  
        2.   
        3. public class Factory {  
        4.     public static Api createApi(int condition){  
        5.         Api api = null;  
        6.         //可根据某些外在条件选择究竟创建哪一个具体的实现对象  
        7.         if(condition == 1){  
        8.             api = new ImplA();  
        9.         }else if(condition == 2){  
        10.             api = new ImplB();  
        11.         }  
        12.         return api;  
        13.     }  
        14. }  

        Java代码  
        1.   

        说明:使用简单工厂的时候,通常不用创建简单工厂类的类实例,没有创建实例的必要。因此可以把简单工厂类实现成一个工具类,直接使用静态方法就可以了,也就是说简单工厂的方法通常都是静态的,所以也被称为静态工厂。如果要防止客户端无谓的创造简单工厂实例,还可以把简单工厂的构造方法私有化了。

             虽然从理论上讲,简单工厂什么都能造,但对于简单工厂可创建对象的范围,通常不要太大,建议控制在一个独立的组件级别或者一个模块级别,也就是一个组件或模块一个简单工厂。否则这个简单工厂类会职责不明,有点大杂烩的感觉。

             由于是从客户端在调用工厂的时候,传入选择的参数,这就说明客户端必须知道每个参数的含义,也需要理解每个参数对应的功能处理。这就要求必须在一定程度上,向客户暴露一定的内部实现细节。

             另外,我们可以把要创建的类的实现写到配置文件里,在工厂里利用反射机制(Class.forName(...).newInstance())选择要创建的类。

         客户端:

        Java代码  
        1. package com.wxl.simplefactory;  
        2.   
        3. public class Client {  
        4.     public static void main(String[] args) {  
        5.         Api apiA = Factory.createApi(1);  
        6.         apiA.operation("ImplA");  
        7.         Api apiB = Factory.createApi(2);  
        8.         apiB.operation("ImplB");  
        9.     }  
        10. }  

         

         运行结果:

        Java代码  
        1. ImplA s == ImplA  
        2. ImplB s == ImplB  

        Java代码  
        1. <span style="font-size: x-small;"><p> </p></span>  

         下面我们来看看工厂方法模式。

         

        工厂方法模式

              首先我们回顾一下有关框架的知识简单点说:框架就是能完成一定功能的半成品软件。
              就其本质而言,框架是一个软件,而且是一个半成品的软件。所谓半成品,就是还不能完全实现用户需要的功能,框架只是实现用户需要的功能的一部分,还需要进一步加工,才能成为一个满足用户需要的、完整的软件。因此框架级的软件,它的主要客户是开发人员,而不是最终用户。

              如果没有框架,那么客户要求的所有功能都由开发人员自己来开发,没问题,同样可以实现用户要求的功能,只是开发人员的工作多点。
              如果有了框架,框架本身完成了一定的功能,那么框架已有的功能,开发人员就可以不做了,开发人员只需要完成框架没有的功能,最后同样是完成客户要求的所有功能,但是开发人员的工作就减少了。
              也就是说,基于框架来开发,软件要完成的功能并没有变化,还是客户要求的所有功能,也就是“事情还是那些事情”的意思。但是有了框架过后,框架完成了一部分功能,然后开发人员再完成一部分功能,最后由框架和开发人员合起来完成了整个软件的功能,也就是看这些功能“由谁做”的问题。基于框架开发,可以不去做框架所做的事情,但是应该明白框架在干什么,以及框架是如何实现相应功能的。

              接下来我们用工厂方法模式来做一个导出数据的小小框架。

        示例代码:

        Java代码  
        1. package com.wxl.factorymethod;  
        2. /** 
        3.  * 数据导出接口 
        4.  */  
        5. public interface ExportFileApi {  
        6.     public boolean export(String data);  
        7. }  

        Java代码  收藏代码
        1. <span style="font-size: x-small;"><p> 对接口的实现:</p>  
        2. <pre class="java" name="code">package com.wxl.factorymethod;  
        3. /** 
        4.  * 导出成文本格式 
        5.  */  
        6. public class ExportTxtFile implements ExportFileApi {  
        7.     @Override  
        8.     public boolean export(String data) {  
        9.         System.out.println("导出数据"+data+"到文本文件");  
        10.         return true;  
        11.     }  
        12. }</pre>  
        13. </span>  
        Java代码  
        1. <span style="font-size: x-small;"><pre class="java" name="code">package com.wxl.factorymethod;  
        2. /** 
        3.  * 导出成数据库备份文件形式 
        4.  */  
        5. public class ExportDB implements ExportFileApi {  
        6.     @Override  
        7.     public boolean export(String data) {  
        8.         System.out.println("导出数据"+data+"到数据库文件");  
        9.         return true;  
        10.     }  
        11. }</pre>  
        12. </span>  
        Java代码  
        1.   

         抽象类:

        Java代码 <
      8. cvs用户权限管理很不方便,有没有好的解决方案

        2013-03-21 11:39:44

        成功安装cvs后,用户默认为系统用户

        在dos窗口下设置

        set cvsroot=:pserver:主机名@115.24.161.33:/cvs仓库(主机名如:Administrator,cvs仓库如cvs_database)

        cvs login(提示输入密码,就是电脑的开机密码)

        cvs passwd -r 主机名 -a test(添加test用户)

        添加用户后登陆test用户不成功,用IDE(eclipse)也无法正常连接test账户,但是在操作系统中创建test用户后eclipse就能成功连接test用户,有没有直接在cvs中配置的方法而不用在系统中创建新用户就能使用cvs的用户管理功能?

         

        其他常用命令

           cvs lsacl 查看权限
           cvs ls     查看可访问的仓库
           cvs passwd -r administrator -a mini 添加用户mini并绑定到administrator mini是用户,随后会提示输入此用户密码

           添加新用户[aaa]:cvs passwd -a aaa 回车,设置密码,OK,完成.
           绑定[aaa]到[Administrator]: cvs passwd -r administrator aaa 回车,设置密码,OK,完成
           两次输入的密码可以不同,但以第二次输入的密码为最终密码.
           删除用户[aaa]: cvs passwd -x aaa 回车,OK,完成

      9. win7下MongoDB数据库安装

        2013-03-21 11:39:04

        1. 下载Windows版本的 MongoDB 数据库
        到官方的下载页面下载mongodb的windows版本,32位还是64位根据自己的情况自行选择,下载地址:http://www.mongodb.org/downloads

        2. 安装准备

        将下载的压缩包解压缩并放置到你想放置的位置,在目录下建立一个叫做DB的文件夹和一个log.txt的文件

        DB文件夹用于存储数据库

        log.txt用于记录MongoDB的日志

         

        3. 安装MongoDB

        这里讲的方法是将MongoDB安装为Windows的服务的方式,打开windows的命令行(注意:请使用个管理员权限启动命令行)并移动到你MongoDB目录下的Bin文件夹,我这里演示的路径是d:\DEV\ENV\mongodb

        输入下列命令可将MongoDB注册为Windows服务:

        mongod.exe --install --logpath=D:\DEV\ENV\mongodb\log.txt --dbpath=D:\DEV\ENV\mongodb\DB

        --install 参数是设定安装为服务器

        --logpath 参数是设定日志文件的路径,log.txt是在上一步建立好的文件

        --dbpath 参数是设定数据库文件的存放路径,DB文件夹在上一步骤已经创建好

        net start mongodb

        即可启动mongodb服务

        启动后,再到命令行输入

        mongo

        看到MongoDB已经启动成功,安装就完成了,很简单。

      10. 依据List内部对象的某字段进行排序的方法

        2013-03-21 11:37:35

        依据List内部对象的某字段进行排序的方法

        List内部对象类IntStringtest为测试用例类

        Collections.sort方法,实现对List的排序,需要重写Comparator方法

        代码如下:

        List里存放的实体

        Java代码  收藏代码
        1. package org.iti.wxl.listobjectsort;  
        2.   
        3. public class IntString {  
        4.       
        5.     private Integer no;  
        6.       
        7.     private String str;  
        8.   
        9.     public Integer getNo() {  
        10.         return no;  
        11.     }  
        12.   
        13.     public void setNo(Integer no) {  
        14.         this.no = no;  
        15.     }  
        16.   
        17.     public String getStr() {  
        18.         return str;  
        19.     }  
        20.   
        21.     public void setStr(String str) {  
        22.         this.str = str;  
        23.     }  
        24.   
        25.     @Override  
        26.     public String toString() {  
        27.         return "IntString [" + (no != null ? "no=" + no + ", " : "")  
        28.                 + (str != null ? "str=" + str : "") + "]";  
        29.     }  
        30.   
        31. }  

         

        Java代码  收藏代码
        1. package org.iti.wxl.listobjectsort;  
        2.   
        3. import java.util.ArrayList;  
        4. import java.util.Collections;  
        5. import java.util.Comparator;  
        6. import java.util.List;  
        7.   
        8. public class Test {  
        9.     public static void main(String[] args) {  
        10.         List<IntString> islist = new ArrayList<IntString>();  
        11.         IntString is1 = new IntString();  
        12.         is1.setNo(1);  
        13.         is1.setStr("计091");  
        14.   
        15.         IntString is3 = new IntString();  
        16.         is3.setNo(3);  
        17.         is3.setStr("计093");  
        18.   
        19.         IntString is2 = new IntString();  
        20.         is2.setNo(2);  
        21.         is2.setStr("计092");  
        22.   
        23.         islist.add(is1);  
        24.         islist.add(is3);  
        25.         islist.add(is2);  
        26.           
        27.         System.out.println(islist);  
        28.         Collections.sort(islist, new MyComparator());  
        29.         System.out.println(islist);  
        30.     }  
        31. }  
        32.   
        33. class MyComparator implements Comparator<Object> {  
        34.     @Override  
        35.     public int compare(Object o1, Object o2) {  
        36.         IntString iso1 = (IntString)o1;  
        37.         IntString iso2 = (IntString)o2;  
        38.         String str1 = iso1.getStr();  
        39.         String str2 = iso2.getStr();  
        40.         return str1.compareTo(str2);  
        41. //      Integer no1 = iso1.getNo();  
        42. //      Integer no2 = iso2.getNo();  
        43. //      if(no1 > no2){  
        44. //          return 1;  
        45. //      }else if(no1 < no2){  
        46. //          return -1;  
        47. //      }else{  
        48. //          return 0;  
        49. //      }  
        50.     }  
        51.   
        52. }  

         如果是String类型的数据比较大小,Comparator方法里用str1.compareTo(str2) 方法,如果是数值比较大小,则用<,=,>比较,>返回1,=返回0,<返回-1。

      11. 从JDK动态代理到SPRING AOP

        2013-03-21 11:36:33

        代理模式 
           代理模式是常用的java设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。 
           代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类对象的相关方法,来提供特定的服务。 
        代理分类 
           静态代理:由程序员创建或特定工具自动生成源代码。在程序运行前,代理类的.class文件就已经存在了。 
           动态代理:在程序运行时,运用反射机制动态创建而成。 
        静态代理代码示例

        首先创建接口:

        Java代码  收藏代码
        1. <span style="font-size: x-small;">package org.iti.wxl.staticproxy;  
        2. /** 
        3.  * 接口 
        4.  */  
        5. public interface UserService {  
        6.              public void saveUser();  
        7.              public void deleteUser();  
        8. }</span>  

         

        对接口的实现:(这里就不连接数据库了,只做简单的实例)

        Java代码  收藏代码
        1. <span style="font-size: x-small;">package org.iti.wxl.staticproxy;  
        2.   
        3. public class UserServiceImpl implements UserService {  
        4.     @Override  
        5.     public void saveUser() {  
        6.         System.out.println("==user saved!==");  
        7.     }  
        8.     @Override  
        9.     public void deleteUser() {  
        10.         System.out.println("==user deleted!==");  
        11.     }  
        12. }</span>  

         

        代理类:

        Java代码  收藏代码
        1. <span style="font-size: x-small;">package org.iti.wxl.staticproxy;  
        2.   
        3. public class UserServiceImplProxy implements UserService {  
        4.       
        5.     private UserServiceImpl userServiceImpl;  
        6.     public UserServiceImplProxy(UserServiceImpl userServiceImpl) {  
        7.         this.userServiceImpl = userServiceImpl;  
        8.     }  
        9.   
        10.     @Override  
        11.     public void saveUser() {  
        12.         System.out.println("save begin!");  
        13.         userServiceImpl.saveUser();  
        14.         System.out.println("save end!");  
        15.     }  
        16.       
        17.     @Override  
        18.     public void deleteUser() {  
        19.         System.out.println("delete begin!");  
        20.         userServiceImpl.deleteUser();  
        21.         System.out.println("delete end!");  
        22.     }  
        23. }</span>  

              代理类同样实现了UserService接口,代理类持有UserServiceImpl,并且重写了UserService接口里的方法,在方法里添加了逻辑。

         

        测试方法:

        Java代码  收藏代码
        1. <span style="font-size: x-small;">package org.iti.wxl.staticproxy;  
        2.   
        3. public class StaticProxy {  
        4.     //静态代理方法测试  
        5.     public static void main(String[] args) {  
        6.         UserServiceImpl userServiceImpl = new UserServiceImpl();  
        7.         UserServiceImplProxy userServiceImplProxy =   
        8.                 new UserServiceImplProxy(userServiceImpl);  
        9.         userServiceImplProxy.saveUser();  
        10.         System.out.println("=============");  
        11.         userServiceImplProxy.deleteUser();  
        12.     }  
        13. }  
        14. </span>  

        把创建的UserServiceImpl交给代理类UserServiceImplProxy,由它的实例执行数据逻辑操作。

         

        方法运行结果如下:

        Java代码  收藏代码
        1. <span style="font-size: x-small;">save begin!  
        2. ==user saved!==  
        3. save end!  
        4. =============  
        5. delete begin!  
        6. ==user deleted!==  
        7. delete end!</span>  

          代理类中添加的逻辑在方法运行时被执行了。 

         

            观察代码可以发现每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码。

            解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理完成。

         

         jdk动态代理

            jdk动态代理中包含一个类和一个接口,用到了java的反射机制:

        Java代码  收藏代码
        1. <span style="font-size: x-small;">public interface InvocationHandler {   
        2. public Object invoke(Object proxy,Method method,Object[] args);  
        3. }  
        4. </span>  

          参数说明: 
              Object proxy:代理类对象。    
              Method method:要调用的方法   
              Object[] args:方法调用时所需要的参数

            下面用代码说明jdk动态代理的使用

        接口类:

        Java代码  收藏代码
        1. <span style="font-size: x-small;">package org.iti.wxl.dynamicproxy;  
        2.   
        3. public interface UserService {  
        4.     public void addUser();  
        5.     public void deleteUSer();  
        6. }</span>  

         

        对接口的实现:

        Java代码  收藏代码
        1. <span style="font-size: x-small;">package org.iti.wxl.dynamicproxy;  
        2.   
        3. public class UserServiceImpl implements UserService {  
        4.     @Override  
        5.     public void addUser() {  
        6.         System.out.println("==add user==");  
        7.     }  
        8.     @Override  
        9.     public void deleteUSer() {  
        10.         System.out.println("==delete user==");  
        11.     }  
        12. }</span>  

         

        代理类:

        Java代码  收藏代码
        1. <span style="font-size: x-small;">package org.iti.wxl.dynamicproxy;  
        2.   
        3. import java.lang.reflect.InvocationHandler;  
        4. import java.lang.reflect.Method;  
        5. import java.lang.reflect.Proxy;  
        6. public class UserServiceImplProxy implements InvocationHandler {  
        7.     private Object target;  
        8.       
        9.     public Object bind(Object target){  
        10.         this.target = target;  
        11.         return Proxy.newProxyInstance(target.getClass().getClassLoader(),   
        12.                 target.getClass().getInterfaces(), this);  
        13.     }  
        14.     @Override  
        15.     public Object invoke(Object proxy, Method method, Object[] args)   
        16.             throws Throwable {  
        17.         Object result = null;  
        18.         System.out.println("user add begin!");  
        19.         result = method.invoke(target, args);  
        20.         System.out.println("user add end!");  
        21.         return result;  
        22.     }  
        23. 查看(576) 评论(0) 收藏 分享 管理

        24. 养成良好的编程习惯,提高代码质量

          2013-03-21 11:35:50

          “不积跬步,无以至千里,不积小流,无以成江海”,程序员如何提高代码质量?我们不仅要知其然,还要知其所以然,我们要从点点滴滴的积累开始的。这篇帖子里记录了编程时的应该注意的一些细节,如有需要后续还会补充,希望通过不断的积累,提高编程质量。

          可序列化接口Serializable

          类实现Serializable接口的目的是为了可持久化,比如网络传输或本地存储,为系统的分布或异步部署提供先决支持条件。若没有序列化,现在我们熟悉的远程调用,对象数据库都不可能存在。

              序列化和反序列化是对应的,以下用代码描述实现了序列化接口Serializable的类在磁盘上的存储过程及反序列化,其在网络上的传输道理也是一样的。

           

          Java代码  
          1. package org.iti.wxl.serializable;  
          2.   
          3. import java.io.Serializable;  
          4.   
          5. /** 
          6.  * 实现了Serializable的实体类 
          7.  * 
          8.  */  
          9. public class Person implements Serializable{  
          10.       
          11.     private static final long serialVersionUID = 6388172609871883630L;  
          12.     private String name;  
          13.   
          14.     public String getName() {  
          15.         return name;  
          16.     }  
          17.   
          18.     public void setName(String name) {  
          19.         this.name = name;  
          20.     }  
          21.   
          22. }  

           为模拟序列化的过程我们创建序列化工具类,如下:

          Java代码  
          1. package org.iti.wxl.serializable;  
          2.   
          3. import java.io.FileInputStream;  
          4. import java.io.FileOutputStream;  
          5. import java.io.IOException;  
          6. import java.io.ObjectInput;  
          7. import java.io.ObjectInputStream;  
          8. import java.io.ObjectOutputStream;  
          9. import java.io.Serializable;  
          10.   
          11. public class SerializableUtils {  
          12.     private static String FILE_NAME="e:/obj.bin";  
          13.     //序列化数据保存到磁盘  
          14.     public static void writeObject(Serializable s){  
          15.         try {  
          16.             ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FILE_NAME));  
          17.             oos.writeObject(s);  
          18.             oos.close();  
          19.         } catch (IOException e) {  
          20.             e.printStackTrace();  
          21.         }  
          22.     }  
          23.   
          24.     //反序列化从磁盘读取数据  
          25.     public static Object readObject(){  
          26.         Object obj = null;  
          27.         try {  
          28.             ObjectInput input = new ObjectInputStream(new FileInputStream(FILE_NAME));  
          29.             obj = input.readObject();  
          30.             input.close();  
          31.         } catch (Exception e) {  
          32.             // TODO Auto-generated catch block  
          33.             e.printStackTrace();  
          34.         }  
          35.         return obj;  
          36.     }  
          37. }  

          序列化保存数据到磁盘上

          Java代码  
          1. package org.iti.wxl.serializable;  
          2. public class Producer {  
          3.     public static void main(String[] args) {  
          4.         Person p = new Person();  
          5.         p.setName("小明");  
          6.         // 序列化保存到磁盘上  
          7.         SerializableUtils.writeObject(p);  
          8.     }  
          9. }  

          反序列化,从磁盘读取数据:

          Java代码  
          1. package org.iti.wxl.serializable;  
          2.   
          3. public class Consumer {  
          4.     public static void main(String[] args) {  
          5.         //反序列化  
          6.         Person p =(Person)SerializableUtils.readObject();  
          7.         System.out.println(p.getName());  
          8.     }  
          9. }  

          在反序列化时,若类的版本号serialVersionUID不一致,反序列化时会报一个InvalidClassexception异常,另外在反序列化时,类中的构造函数不会执行。

          奇偶校验用偶校验,避免使用奇校验

          JDK提供的奇偶校验代码模拟如下:

          Java代码  
          1. public class Remainder {  
          2.        //dividend被除数,divisor除数  
          3.     public static int remainder(int dividend, int divisor) {  
          4.         return dividend - dividend/divisor*divisor;  
          5.     }  
          6. }  

           当使用取模运算“%”做奇偶校验时,JDK会依照以上公式进行运算,此时dividend为要校验的数,divisor=2我们发现当被除数为负数且为奇数时,余数为-1,如果我们的奇偶校验公式为i%2 =1 ? "奇数":"偶数";那么当i为负奇数时结果判断会错误,因此我们做奇偶校验时要用偶校验,即:i%2 == 0 ? "偶数":"奇数";

          java中的引用问题

          java中没有指针的概念,但我们要理解另一个名词“引用”。看下面的例子:

          实体类

           

          Java代码  
          1. public class IntString {  
          2.     private Integer no;  
          3.     private String str;  
          4.     public Integer getNo() {  
          5.         return no;  
          6.     }  
          7.     public void setNo(Integer no) {  
          8.         this.no = no;  
          9.     }  
          10.     public String getStr() {  
          11.         return str;  
          12.     }  
          13.     public void setStr(String str) {  
          14.         this.str = str;  
          15.     }  
          16.     @Override  
          17.     public String toString() {  
          18.         return "IntString [" + (no != null ? "no=" + no + ", " : "")  
          19.                 + (str != null ? "str=" + str : "") + "]";  
          20.     }  
          21. }  

          测试类:

        25. 网站负载测试工具

          2013-03-21 11:35:00

          通常来说,负载测试可以采用手动和自动两种方式。手动测试会遇到很多问题,如无法模拟太多用户、测试者很难精确记录相应时间、连续测试和重复测试的工作量特别大等。因此对于负载测试,手动方式通常用于初级的负载测试。目前,绝大多数的负载测试都是通过自动化工具完成的。  


              负载测试的相关问题 

              从网站内容上看,每个网站都有自己的客户群和工作负载,不同网站其软硬件结构差异也很大。下图是一个网站的基本框架。 

              这种复杂性给负载测试带来了很大的挑战。一方面,由于一些设备的原因,有时我们不能直接进行负载测试,必须绕过某些设备,造成很大麻烦。另一方面,由于整个体系结构的复杂,也给寻找问题带来了一定的困难。例如防火墙常常阻止负载测试的进行,因此通常需要对防火墙进行调整,让它暂时支持负载测试; 有时服务器的应用程序采用加密方式与客户端进行交互,在这种情况下,还需要更改服务器应用程序。因此,负载测试人员需要对整个网络架构和应用系统非常熟悉。 

              另外,负载测试的成功与否,在很大程度上取决于自动化工具。在功能方面,主要考虑它所支持的协议、分析方式、监视目标种类等,以及该工具能否精确记录、回放用户的访问情况。在性能方面,则主要考虑它模拟虚拟用户的能力,例如在一定资源下可以模拟的用户的数量和速度。 

              负载测试的过程 

              负载测试是一项非常复杂的工作,一次测试常常要持续几天甚至几周。因此,在进行一次负载测试前,必须做好充分的准备,可以按照以下几个步骤来进行。 

              (1)系统分析 

              分析被测系统需要满足什么要求,例如支持多少人在线、支持连续多长时间的访问等。测试者的主要任务就是将系统的需求转换成测试目标,对系统进行全面的分析和评估,并结合测试的实际环境和条件以确保测试目标和测试计划的正确性。 

              (2)产生脚本 

              为了模拟多个用户访问服务器必须编写脚本。简单的脚本可以通过自动化工具提供的脚本编辑环境来编写。复杂的脚本则通常是通过记录单用户的活动生成最初的脚本,再在此基础上进行修改以保证该脚本可以支持多个用户。其中最主要的修改是关于数据池的,因为不同用户通常使用不同数据,例如用户名和密码等。因此,我们通常要将这些数据存储在数据库(或者数据池)中,以便在执行中被脚本程序调用。 

              (3)构造运行场景 

              有了脚本后,就可以通过一个场景来管理这些脚本的执行。场景是一个执行单位,可以通过场景来模拟一个工作负载。在场景中,我们将管理脚本的数量、执行次数、执行时间等,甚至还可以加上一些定时器、同步点等控制机制。另外,还可以将模拟用户分配到不同的计算机上。 

              (4)运行场景 

              设置好场景后,就可以运行了。通常,在运行场景的同时还要启动相关监控模块,监控服务器性能、网络状态、Web服务器性能和数据库性能等。自动化工具同时记录了各种客户端信息,包括相应时间、交易成功率等。 

              (5)分析报告 

              通常,在场景运行后,自动化工具会生成标准报告,可以通过分析这个报告来分析整个系统性能,找到系统瓶颈。这一步骤通常需要测试人员和开发人员共同完成。 

              负载测试市场和工具 

              负载测试市场的发展非常迅速,预计到2003年该市场将达到7亿美元。从目前来说,负载工具市场主要由一些大的软件公司分享,例如CA、Rational和Mercury Interactive。 

              (1) Load Runner 

              Load Runner是Mercury Interactive公司推出的专业负载测试工具,是目前世界上最强大的负载测试工具之一,它占领了负载测试工具市场的半壁江山。这是一个企业级的自动化工具,提供了非常强大的监视功能,能够监控各种软硬件模块。 

              从支持的协议来说,Load Runner支持HTTP(S)、WAP、i-Mode、 RealPlayer、LDAP、Winsock、RMI、FTP、POP3、SMTP、CORBA、COM/DCOM以及Tuxedo等。在监视器部分,它支持Windows NT/2000/XP、 SUN Solaris、HP UX、IBM AIX和Linux等操作系统,支持Apache、Web Logic等各种Web Server,还支持各种大型数据库。 

              (2) Astra LoadTest 

              Astra LoadTest也是Mercury Interactive 公司推出的负载测试工具。相对Load Runner来说,该工具更加容易使用,不需要使用者掌握复杂的编程语言,完全通过可视化界面进行操作。该工具支持大量HTML相关技术,例如 JavaScript、XML、ActiveX、Flash、DHTML以及SSL等,并支持大部分的浏览器。它的另外一个优点就是能够高效模拟更多虚拟用户,例如使用PIII 1G/512M机器最大可以支持100~250个用户。另外,Astra LoadTest 继承了LoadRunner的监视模块,可以监控所有LoadRunner支持的对象。 

              由于Astra LoadTest并不是定位在企业级用户,因此它对一些高级协议的支持不够充分,不能支持CORBA、LDAP、COM、WAP以及POP3等协议。 

              (3) Rational Robot 

              Rational在软件测试方面也有非常好的成绩。该公司推出的Robot工具支持SQABasic这种面向对象的记录语言。不过,在性能测试方面,Robot并不是很出名。但是Robot提供了一种新的脚本记录语言—VU语言,它基于传统的C语言,能够方便地访问Robot提供的环境变量。同时 Robot还提供了很多良好定义的库函数,调用通信函数更加方便。Robot还提供了其他许多相关测试技术,例如数据池(Datapool)、同步点等,并且通过TestManager可以对所有类型脚本进行管理。 

              从功能来说,Robot支持众多的网络协议,例如COM、DCOM、SOCKET、IIOP、Tuxedo等,并且可以对协议进行过滤,选取自己关心的协议。从操作方面来说,它对用户的要求也比较高,需要用户在整个访问过程中,对客户和服务器之间的交互类型和内容比较熟悉,同时对Robot也必须有足够的了解。 

              (4) WebLoad 

              WebLoad是RadView公司推出的专业负载测试软件。WebLoad提供了一种非常简单的脚本语言记录通信过程,同时它主要支持HTTP相关的技术和应用协议,例如JavaScript、XML、Java、EJB、ActiveX、WAP、HTTP、SNMP、Real以及 Microsoft Streaming Technologies等。该软件的操作非常容易,并且提供格式良好的分析报告。目前IBM公司大量采用该产品进行负载测试
        26. 12款浏览器兼容性测试工具推荐

          2013-03-20 10:22:27

          对于前端开发工程师和WEB测试工程师来说,确保代码在各种主流浏览器的各个版本中都能正常工作是件很费时的事情,幸运的是,有很多优秀的工具可以帮助测试浏览器的兼容性,让我们一起看看这些很棒的工具。

            1. Spoon Browser Sandbox

            点击你需要测试的浏览器环境,安装插件就可以进行测试了。帮助你测试网页在Safari、Chrome、Firefox和Opera浏览器中是否正常,IE以前也有的,网站上说应微软的要求去掉了。

            2. Superpreview

            这是为微软自己发布的跨浏览器测试工具,您可以同时查看您的网页在多个浏览器的呈现情况,对页面排版进行直观的比较。

            3. IETester

            专门用于测试网页在IE浏览器各个版本中兼容性的工具,版本包含IE5.5至IE9的各个版本,很不错的一款工具,推荐。

           

          4. BrowserShots

            BrowserShots 是一款免费的跨浏览器测试工具,捕捉网站在不同浏览器中的截图。这是最有名,也是最古老的浏览器兼容性测试工具。

            5. Multiple IEs

            这款工具同样用于测试网页在IE浏览器各个版本的兼容性。

            6. IE netrenderer

            Netrenderer 也是用于检查你的网站在IE浏览器中的呈现情况,包括各个常用版本的检测。

          7. Viewlike.Us!

            Viewlike 是一款新推出的工具,帮助你检查浏览器在不同分辨率下得呈现情况。

            8. BrowserSeal

            这款工具的两个主要特色是独立的浏览器支持和带有自动化脚本的命令行界面。

            9. Browsera

            Browsera 是一个可测试您的网站的跨浏览器布局的工具,您会看到您网站上存在的兼容性错误。

           10. WebDevLab

            这款工具专门用于测试你的网站在苹果Safari浏览器中是什么样子的。

            11. Litmus

            这个工具可以帮助你检查你的网站在多个浏览器中的呈现情况,跟踪Bug并创建报告。

            12. Browsercam

            最后这款工具是要付费的,可以帮助你检查 Javascript. 和 DHTML,提供不同的测试环境平台。

        27. 【转】分享对吞吐量、点击率、带宽大小等基础单位知识的总结

          2013-03-20 10:19:40

          一、位和字节
          1、 比特bit(b)  
          bit是字位(最小的存储单位,) 
          千位:Kbits(Kb,英文kilobit)
          计算机中的信息都是二进制的0和1来表示,其中每一个0或1被称作一个位。
          通常用bit来作数据传输的单位,因为物理层,数据链路层的传输对于用户是透明的,而这种通信传输是基于二进制的传输。

          2、 比特Byte(B)  
          Byte是字节(计算机储存容量大小的基本计算单位)
          千字节:Kbytes(KB,英文kilobyte)
          在应用层通常是用Byte来作单位,表示文件的大小,在用户看来就是可见的数据大小。比如一个字符就是1Byte,如果是汉字,则是2Byte。  
          我们所说的硬盘容量是40GB、80GB、100GB,这里的B指是的Byte也就是“字节”
          表示文件的大小单位,一般都使用字节(KB)来表示文件的大小。

          3、 兆(M,英文million=1000000)
          M是表示数值,不是单位,MB是量单位。 
          MB:一般指的是 兆字节,那个B是Byte而不是bit。
          Mb:一般指 兆位

          单位换算: 
          1Byte=8bit
          1KB=1024B=1024*8b 
          1kB=1000B=1000*8b   
          1Kb=1024b 
          1kb=1000b 
          1MB=1024KB
          1GB=1024MB
          1T=1024GB
          注意:小k通常表示1000,大K表示1024.

          二、传输速率
          1、带宽
          单位:Mbps(兆位/秒),又可写成Mb/sec,或Mb/s,ps就是每秒的意思,
          Mbps的含义是兆比特每秒:指每秒传输的比特位数
          MBps的含义是兆字节每秒:指每秒传输的字节数量
          Mbps代表每秒传输1048576比特(1MB=1024*1024b)。该缩写用来描述数据传输速度。例如:4Mbps=每秒钟传输4M比特
          传输速率是指集线器的数据交换能力,目前主流的集线器带宽主要有10Mbps、54Mbps/100Mbps自适应型、100Mbps和1GMbps四种。 

          由于数据传输速率的单位,字母b是比特和字母B是字节,相应的MBPS也就有两个解释了, 分别是:
          1、每秒百万个位元组 (megabyte);   
          2、每秒百万个位元(megebit)。
          通常说的Mbps是指第二个每秒百万个位元,也就是每秒百万个位。 

          由于在传输过程中为了保证信息传输的正确性需要在传输的每个字节之间增加仃码和校验码,因此如果换算成我们常用的MB单位就需要除以8,如下: 
          100Mbps=100/8=12.5MB/s 
          4Mbps = 4/8=0.5MB/s 

          常见的如:   
          一般主板仅仅能提供 100Mbps的接口  
          SATA II硬盘 理论上可以达到 3000Mbps 的速度  
          换算成我们常用的MB单位就是:   
          100Mbps=100/8=12.5MB/s  
          3000Mbps=12.5*30=375MB/s   
          这样就能看到适用于日常习惯性的传输速度单位了。   
          网络传输过程中还需要对信息进行打包,增加Ethernet Header, IP Header, TCP Header, ATM Header 等控制讯号,因此该数据的实际速率还需要再打些折扣,但最简单的估算方法除以10就可以了。

          在这里需要说明的问题是在单位换算上有一点是极其重要的,即:
          1Mb=1024kb=1024000b 
          1MB=1024KB=1024*1024B=1024*1024*8b=8388608b这在数量上差的很多
          1KB=1024B(大写K)
          1kB=1000B(小写k)
          1Mbps=1Mb=1024kb ,1024/8=128kb/s.

          KBps=1024Bps,kBps=1000Bps   也就是说KBps>kBps [1]
          小k代表kilo,千的意思,也就是1000,那么kBps就是1000Bps也就是一千字节每秒的意思。    
          而KBps(K为大写字母)就等于1024Bps。而kBps(k为小写字母)的意思是千字节每秒。   
          千字节每秒(KBps)与一千字节每秒(kBps)是不同的。  

          2、Kbps
          Kbps又称比特率,指的是数字信号的传输速率,也就是每秒钟传送多少个千位的信息。
          Kbps也可以表示网络的传输速度,为了在直观上显得网络的传输速度较快,一般公司都使用Kb(千位)来表示,即Kbps。如果是KBps,则表示每秒传送多少千字节。

          bps=bits per second = bits/s
          Bps=Bytes per second = Byte/s
          kbps=kilobits per second 千位/秒
          KBps=kilobytes per second 千字节/秒

          KBps表示的是每秒传送多少个千字节。
          换算的方式是:
          1KBps=8Kbps。
          1KB=8Kb
          1KBps=8Kbps
          1MB=8Mb
          1MBps=8Mbps
          1Mbps=1024Kbps
          1Kbps=1024bps

          1MBps=1024KBps
          1KBps=8Kbps
          1Gbps=1024Mbps
          1GBps=1024MBps
          1Kb=1024b=1024/8B=128B
          1MB=1024KB=1024*1024B=1024*1024/128Kb=8192Kb
          1 Mbps = 1024 Kbps=1024/8KBps=128KBps因此,通常说的2M带宽(网速),其实是2Mbps,即2Mbps=2*1024Kbps=2048/8KBps=256KBps,虽然理论上能到250KBps,实际约200KBPS就不错了.
          USB2.0标准接口传输速率是480兆位/秒,即480Mbps。这里的b指的是bit(与传输相关,属于底层)
          ADSL上网时的网速是512Kbps,如果转换成字节,就是512/8=64KBps(即64千字节每秒)。这里的64就是我们在下载软件中看到的下载速度。

          计算机存储单位一般用B,KB,MB,GB,TB,PB,EB,ZB,YB,BB来表示,它们之间的关系是:  
          1KB(Kilobyte 千字节)=1024B,   
          1MB (Megabyte 兆字节 简称“兆”)=1024KB,   
          1GB (Gigabyte 吉字节 又称“千兆”)=1024MB,   
          1TB(Trillionbyte 万亿字节 太字节)=1024GB,其中1024=2^10 ( 2 的10次方)
        28. 【转】Web网站的性能测试工具

          2013-03-20 10:16:46

          随着Web 2.0技术的迅速发展,许多公司都开发了一些基于Web的网站服务,通常在设计开发Web应用系统的时候很难模拟出大量用户同时访问系统的实际情况,因此,当Web网站遇到访问高峰时,容易发生服务器响应速度变慢甚至服务中断。为了避免这种情况,需要一种能够真实模拟大量用户访问Web应用系统的性能测试工具进行压力测试,来测试静态HTML页面的响应时间,甚至测试动态网页(包括ASP、PHP、JSP等)的响应时间,为服务器的性能优化和调整提供数据依据。

            我推荐各位Web 2.0开发测试人员使用Microsoft 的Web Application Stress Tool这个工具软件,这个微软提供的小工具仅9.58M,很小巧且实用。虽然功能上比不了专业的LoadRunner,但LoadRunner体积庞大,价格不菲,一般的企业也不会花那么多钱去购买LoadRunner,而微软的WAS则是完全免费,并且主要的功能都有,够用就行。

            Microsoft Web Application Stress Tool能有效测试一个网站的负载性能,这个软件可以通过脚本模拟100个强并发用户的访问,并模拟实际用户的一些点击操作,WAS还可以连接上远程Windows网站服务器的性能计数器(Performance Counter),通过对服务器性能(CPU/内存等)的性能分析来找到系统的瓶颈。CPU使用百分比反映了处理器开销,CPU使用百分比持续地超过75%是性能瓶颈在于处理器的一个明显的迹象。

            每次测试运行结束后WAS会生成详细的报表,WAS报表可以从View菜单选择Reports查看。

            另外,CSDN也总结了一些其他的测试工具,包括性能测试工具和功能测试工具,前十大测试工具排名如下:

            业级自动化测试工具WinRunner

            Mercury Interactive公司的WinRunner是一种企业级的功能测试工具,用于检测应用程序是否能够达到预期的功能及正常运行。通过自动录制、检测和回放用户的应用操作,WinRunner能够有效地帮助测试人员对复杂的企业级应用的不同发布版进行测试,提高测试人员的工作效率和质量,确保跨平台的、复杂的企业级应用无故障发布及长期稳定运行。

            工业标准级负载测试工具Loadrunner

            LoadRunner 是一种预测系统行为和性能的负载测试工具。通过以模拟上千万用户实施并发负载及实时性能监测的方式来确认和查找问题,LoadRunner 能够对整个企业架构进行测试。通过使用LoadRunner ,企业能最大限度地缩短测试时间,优化性能和加速应用系统的发布周期。

            全球测试管理系统testdirector

            TestDirector 是业界第一个基于Web的测试管理系统,它可以在您公司内部或外部进行全球范围内测试的管理。通过在一个整体的应用系统中集成了测试管理的各个部分,包括需求管理,测试计划,测试执行以及错误跟踪等功能,TestDirector极大地加速了测试过程。

            功能测试工具Rational Robot

            IBM Rational Robot 是业界最顶尖的功能测试工具,它甚至可以在测试人员学习高级脚本技术之前帮助其进行成功的测试。它集成在测试人员的桌面 IBM Rational TestManager 上,在这里测试人员可以计划、组织、执行、管理和报告所有测试活动,包括手动测试报告。这种测试和管理的双重功能是自动化测试的理想开始。

            单元测试工具xUnit系列

            目前的最流行的单元测试工具是xUnit系列框架,常用的根据语言不同分为JUnit(java),CppUnit(C++),DUnit (Delphi ),NUnit(.net),PhpUnit(Php )等等。该测试框架的第一个和最杰出的应用就是由Erich Gamma (《设计模式》的作者)和Kent Beck(XP(Extreme Programming)的创始人 )提供的开放源代码的JUnit.

            功能测试工具SilkTest

            Borland SilkTest 2006属于软件功能测试工具,是Borland公司所提出软件质量管理解决方案的套件之一。这个工具采用精灵设定与自动化执行测试,无论是程序设计新手或资深的专家都能快速建立功能测试,并分析功能错误。

            性能测试工具WAS

            Microsoft Web Application Stress Tool 是由微软的网站测试人员所开发,专门用来进行实际网站压力测试的一套工具。透过这套功能强大的压力测试工具,您可以使用少量的Client端计算机仿真大量用户上线对网站服务所可能造成的影响。

            自动化白盒测试工具Jtest

            Jtest是parasoft公司推出的一款针对java语言的自动化白盒测试工具,它通过自动实现java的单元测试和代码标准校验,来提高代码的可靠性。parasoft同时出品的还有C++ test,是一款C/C++白盒测试工具。

            功能和性能测试的工具JMeter

            JMeter是Apache组织的开放源代码项目,它是功能和性能测试的工具,100%的用java实现。

            性能测试和分析工具WEBLODE

            webload是RadView公司推出的一个性能测试和分析工具,它让web应用程序开发者自动执行压力测试;webload通过模拟真实用户的操作,生成压力负载来测试web的性能。

        29. 【转】cookie和session 区别and 联系

          2013-03-20 10:13:26

          cookie和session机制之间的区别与联系 
          具体来说cookie机制采用的是在客户端保持状态的方案。它是在用户端的会话状态的存贮机制,他需要用户打开客户端的cookie支持。cookie的作用就是为了解决HTTP协议无状态的缺陷所作的努力. 
          而session机制采用的是一种在客户端与服务器之间保持状态的解决方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的。而session提供了方便管理全局变量的方式 

          session是针对每一个用户的,变量的值保存在服务器上,用一个sessionID来区分是哪个用户session变量,这个值是通过用户的浏览器在访问的时候返回给服务器,当客户禁用cookie时,这个值也可能设置为由get来返回给服务器。 

          就安全性来说:当你访问一个使用session 的站点,同时在自己机子上建立一个cookie,建议在服务器端的SESSION机制更安全些.因为它不会任意读取客户存储的信息。 

          正统的cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie 

          从网络服务器观点看所有HTTP请求都独立于先前请求。就是说每一个HTTP响应完全依赖于相应请求中包含的信息状态管理机制克服了HTTP的一些限制并允许网络客户端及服务器端维护请求间的关系。在这种关系维持的期间叫做会话(session)。 

          Cookies是服务器在本地机器上存储的小段文本并随每一个请求发送至同一个服务器。IETF RFC 2965 HTTP State Management Mechanism 是通用cookie规范。网络服务器用HTTP头向客户端发送cookies,在客户终端,浏览器解析这些cookies并将它们保存为一个本地文件,它会自动将同一服务器的任何请求缚上这些cookies 



          cookie和session机制区别与联系 
          具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的,但实际上它还有其他选择。 

          cookie机制。正统的cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。然而纯粹的客户端脚本如JavaScript或者VBScript也可以生成cookie。而cookie的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给服务器。 
          cookie的内容主要包括:名字,值,过期时间,路径和域。路径与域一起构成cookie的作用范围。若不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失。这种生命期为浏览器会话期的cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。若设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。存储在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式 
          session机制。session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。 

          当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含了一个session标识(称为session id),如果已包含则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(检索不到,会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。 
          保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于SEEESIONID。但cookie可以被人为的禁止,则必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。
291/212>
Open Toolbar