发布新日志

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

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

  • 【转】云计算的新关键危害应用袭来

    2013-03-22 11:09:32

    摘要: 企业部署公有云(IaaS,基础设施即服务)进展缓慢的原因有许多种,迄今为止,业内人员尚未对云计算的下一代重量级应用展开过广泛讨论,而这些应用却恰恰是企业游戏的改变者。 

     企业部署公有云(IaaS,基础设施即服务)进展缓慢的原因有许多种,这些原因已经被广泛的讨论过。迄今为止,业内人员尚未对云计算的下一代重量级应用展开过广泛讨论,而这些应用却恰恰是企业游戏的改变者。


        针对企业的新型重量级应用将会对云集成数据中心(或是真正的混合云部署)产生影响,并根本性地改变IT运营模式。这些杀手级应用将包括云集成的灵活性、防护性和扩展性。


        云集成的灵活性(战略灵活性)


        VMware先在软件测试中,随后又在生产环境中引入了具有虚拟化功能的x86 ,并以此作为增加数据中心与硬件效率、降低日益增长的应用/服务器管理成本的办法。与大幅增长的营收相比,或许它们对IT产生的文化影响更具影响力。实际上,它们极有可能通过打破操作系统、应用和硬件之间的隔阂为史无前例的IT文化革命创造条件。这些隔阂的消融最终将成就软件定义数据中心,并实践证明灵活性是如何推动数据中心转型的。


        在VMware推出了大型机分时作业后, VMware近些年来在IT发展过程中可能已经具备了与IBM相同的影响力。不过,这种混合云模式目前实际上相当于一个可停两辆车的车库(或许一辆停的是特斯拉汽车,另一辆停的是奔驰汽车),而不是单一的统一环境(真正的混合)环境。通过近期的收购,VMware毫无疑问地可以提供一种真正的混合架构。近期的舆论似乎显示VMware仍然认为私有云才是混合云。


        私有云具有战术灵活性。通过企业级服务与控制以及消除封闭性,具有云计算特征的灵活性能够在数据中心和云之间无缝运行应用和服务。我们可以将云集成数据中心视为软件定义数据中心的自然进化。


        云集成的灵活性也具有战略灵活性。它能够让带有企业级服务与控制的预生产和生产基础设施具有无比的灵活性。这种可创建和拆卸应用和服务,将它们在各数据中心、预生产与生产环境、多个云服务提供商或地区间移动的能力就是一种杀手级应用。它们最终将促使应用和服务与特定的本地或云计算脱钩,并能够作用于整个云以及虚拟化了的x86服务器区域。


        软件测试灵活性


        虽然公有云正在作为一种优秀的软件测试(或云计算软件测试)环境兴起,但是混合云提供了更为强大的灵活性以及对生产级环境的模仿。应用和服务能够在真正的生产环境中被测试,而不是在一个封闭的复制环境中测试。它们还能够更为容易地投入生产。混合云运营模式可享受到跨预生产环境与生产环境的战略灵活性所具备的优势,而这对于IT和生产效率提升来说都是一个新的转折点。


        云集成安全防护


        (为可能的或罕见的使用)创建双重数据中心和主机托管环境是一种非常拙劣的使用案例,尤其是与投资创新、安全或扩展性相比。随着越来越多的首席信息官需要向首席财务官进行报告,传统面向基础设施的灾难恢复和业务持续性解决方案开始受到越来越多的详细审查。“预付费型”云模式的出现开始吸引用户转而使用云服务作为失效备援防护措施,而不再将维护和升级冗余物理基础设施作为失效备援防护措施。


        不幸的是,公有云服务提供商并不擅长于满足这些需求与服务,因为后者对确保正常运行时间提出了严格的要求。即便拥有人气较高的客户,较高的运行中断率也会降低用户对云可用性的预期值。虽然具备一些明显的经济优势,但是由于客户无法轻易从一个区域转至另一个区域导致公有云对研发人员和失效备援/持续性解决方案缺乏吸引力。


        目前,公有云没有针对持续性的应用保护推出一个企业级解决方案。它们只是一个与数据中心和其它区域/云计算进行有限整合的孤立环境。“要么选择要么放弃”的孤立策略加之“封闭”导致的恶果严重制约了公有云的持续发展潜力。


        混合云(或是云集成数据中心)与生俱来的优势弥补了公有云的弱点,尤其是战略灵活性方面。在新的云服务或区域内快速部署双重基础设施,以及在主工作点/站被修复后的回切恰恰需要这种战略灵活性。混合云是一种高级架构,其具有更高水平的灵活性,能够提供更高等级的应用防护与扩展。


        混合云可为数据中心的服务与控制提供 “按需付费”的持续性防护。与维持双重固定式基础设施相比,前者无疑具有更高的效率。对于业务持续性和灾难恢复来说,(相对于目前的公有云和冗余物理基础设施)混合云是一个优先选项。


        云集成的扩展性(可持续扩展性)


        云爆炸是讨论最多的混合云重量级应用之一。由于其首先需要灵活性,因此只有在灵活性和防护问题解决之后才可能被推广。它们允许用户“平时拥有基本性能,在高峰期租用性能”。许多熟悉高扩展性数据中心环境的内部人士透露称,研发人员正在使用与高级私有云基础设施相当的设施来模拟云爆炸,不过他们目前还没能借助云集成实现无缝扩展。话又说回来,云集成是公有云和私有云的正常演进。对于大多数公司来说,能够应对可预测的工作负载并且可通过租借方式应对偶发性的工作负载是他们优先选择的运营模式。


    转载CIO时代网(www.ciotimes.com

  • 用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. 微软自家杀毒软件被认定“不够格”?

          2013-03-20 11:41:46

          1月18日消息,微软在Windows 8中直接预装了自家的MSE杀毒软件(整合在Defender中),用户无需另行下载,非常方便。但最近的一条消息有些让人意外:MSE竟然没能获得AV-TEST组织最新的认证。

            在AV-TEST针对25款安全产品所进行的反恶意软件测试当中,有三款未能通过,MSE就是其中之一。测试结果显示,MSE没能充分保护计算机免受0-day恶意软件的攻击,得分71%,距离92%的行业标准还存在一段距离。

            微软对此事的回应是,公司已经进行了一次“严格的结果审查”,发现只有0.0033%的MSE用户被测试中未能检测出的恶意软件样本所影响。

            微软的意图似乎是想挑战反病毒测试,声称“独立的反恶意软件测试组织很难去设计出和现实情况相一致的测试”。微软同时表示自己会优先消费者影响,但28个未能检测出的0-day样本“不能代表我们消费者所遇到的情况”。同时,微软还表示公司将致力于把0.0033%降低到0。

        27. 云安全漫谈:闲话私有云安全问题

          2013-03-20 11:40:20

          今天我们来详细说说私有云的安全。

            公有云私有云的交锋焦点无疑指向了数据安全问题。HyperStratus咨询公司首席执行官伯纳德·戈尔登(Bernard Golden)撰文指出,一个接一个的调查表明,对于公有云计算,安全是潜在用户最担心的问题。戈尔登本人在去年七月份在台湾云SIG会议上也被如此提问:“公有云计算足够安全吗,我是否应该使用私有云以避免安全问题?”一时间人们几乎都把云安全的讨论总结为两个极端点,“公有云不安全,私有云安全”。

            我先表明自己的立场,我看好云计算服务,看好公有云在世界上的应用,但是我更看好私有云在中国的落地。再来谈上面的观点,这个观点引发了一个小规模的战争,行业内也给出了私有云不安全的论据:

            首先来说,云计算的技术基础是在一个应用的管理程序的基础上的。管理程序能够把计算(及其相关的安全威胁)与传统的安全工具隔离开,检查网络通讯中不适当的或者恶意的数据包。由于在同一台服务器中的虚拟机能够完全通过管理程序中的通信进行沟通,数据包能够从一个虚拟机发送到另一个虚拟机,不必经过物理网络。一般安装的安全设备在物理网络检查通讯流量。

            至关重要的是,这意味着如果一个虚拟机被攻破,它能够把危险的通信发送到另一个虚拟机,机构的防护措施甚至都不会察觉。换句话说,一个不安全的应用程序能够造成对其它虚拟机的攻击,用户采用的安全措施对此无能为力。

            由此,得出结论,私有云也是不安全的。

            对于上述的整体理论我都没有意见,只是对于结果,是否有些过于武断?上面的安全问题的确存在,但是我们得看到,它不是私有云带来的,而是虚拟化带来的。作为云计算与传统计算的最大区别,虚拟化的问题真实存在并将在未来一段时间持续。

            对于“公有云不安全,私有云安全”这种观点,当然是不正确的。我们可以来修改一下,“私有云比公有云安全”。一方面,私有云是在企业自己的数据中心边界范围内部署的,这相比起公有云在数据安全上有了更大的保障,不会由于提供商的安全问题而使自己受到任何损害,相当于我们把自己的数据存在了自己随身的保险箱里。

            另一方面,便是通讯协议。当公有云暴露在整个网络通讯协议之下,安全问题势必成倍上升,而作为私有云,虽然仍然像上面所说,不安全的应用程序同样会造成攻击性,但是,包裹于企业内网保护之下,受到的网络攻击是不是会比公有云要少呢?我想答案是肯定的。

            安全问题源于人们的安全意识,我们抛开单纯的理论安全不谈,我们来看二者对人们心理的影响。公有云,服务先进,价格公道;私有云,专业配置,安全可靠,这是人们的第一反应。如同上面的比喻一样,私有云相当与我们把自己的贵重物品锁在自己随身的保险箱里,而公有云呢,则是存放在一个职业托管处,当然,我们随身的保险箱确实也有可能会丢,但是带在身边我们放心,没事时候还可以摸摸看看;而放在一个口碑良好的托管处呢,即便这个地方很是安全,我们也难免每天睡觉都会想着我那重要的东西,甚至梦见这托管处让人抢了,让火烧了,惴惴而不能安。这比喻自然是个玩笑,但玩笑之下却符合人们真实的内心感受。

            最后呢,我们回归那个“私有云不安全”的论点。其实我觉得提出这些论点和论据的人思维也颇为奇怪,这是在告诉别人,“看,私有云有这么这么多的安全隐患,其实我们公有云也是有的,既然都有,那么还是公有云好”。这个理念难免让人感觉到有些不符合“云”的浪潮。其实,公有云、私有云包括混合云,都是大有可为的,希望有一天,“云”能够以一种无争议的方式真正在国内落地。

        28. 云安全相比普通安全有哪些优势

          2013-03-20 11:39:25

          一、分析局域网安全威胁所在

            业内人士认为:对单位内部网络的威胁主要来自于不是单位外部,而是单位内部。因此,将精力放在防止网络遭受来自于单位外部的攻击,而忽视来自单位内部的网络安全威胁是错误的。

            根据CISCO安全部门统计,实际上约70%以上的安全事故(特别是失泄密)来自于单位内部。所以,由于对内部网络安全威胁认识不足,单位安全部门没有妥善采取科学的防范措施,导致来自于内部的网络安全事故逐年增加。

            之所以说单位内部网络安全隐患除了来自安全系统的部署缺陷,更大的是来自于单位内部员工,其表现在:

            (1)移动存储介质的无序管理;

            (2)使用盗版软件、游戏等,造成病毒传播;

            (3)用户和用户组权限分配不合理;

            (4)账号及口令管理不符合严格规定或设置不合理;

            (5)过多开设服务端口;

            (6)网络管理员工作量过大或专业水平不够高、工作责任心不够强;

            (7)用户操作失误,可能会损坏网络设备如主机硬件等,误删除文件和数据、误格式化硬盘等;

            (8)内部的网络攻击。有的员工为了泄私愤或被策反成为敌方间谍,成为单位泄密者或破坏者。由于这些员工对单位内部的网络架构熟悉,可利用管理上的漏洞,侵入他人计算机进行破坏;

            (9)来自无线网络用户的内部攻击,企业局域网如果部署了无线接入AP,那么要是不做身份验证等安全措施进行防范的话,处于AP覆盖范围之内安装了无线网卡设备的普通计算机并便携设备都能自动加入本地无线局域网的网络中,这样一来本地局域网就非常容易遭遇非法攻击。

            二、云计算中的云安全技术

            云计算产业出现后,随即出现了“云安全”。“‘云安全’是云计算发展中出现的一项重要领域,绝对不能够忽视,必须把‘云安全’作为云计算发展的前提,对于云计算产业来讲要高度重视‘云安全’,如果没有‘云安全’的保障,我们云计算的发展,也会让大家失去信心。

            那么什么是“云安全”呢?通俗点说,“云安全”就是指防病毒厂商通过互联网络,利用客户端搜集到的病毒代码,通过分析、加工、处理,最后给出解决方案分发给用户,这样整个Internet就成了一个巨大的保障用户计算机安全的杀毒软件,你也不必天天为升级更新病毒软件库而烦恼了。

            有人说,云计算技术的出现,预示着PC后时代的到来,在软件业由C/S架构逐渐向B/S架构演进过程中,必然客户端的硬盘需求越来越小了,对于客户端的智能化要求越来也越低了,我们未来的个人PC将不再需要安装任何软件,也无须下载任何软件安装到PC上,而是直接通过浏览器或者专业设备对所有应用环境进行远程调用,充分利用云端服务器的资源,降低本地的负荷,减少TCO,充分让个人PC更加绿色。

            那么云安全技术比普通的安全技术有哪些优势呢?

            总体来说,云安全利用云端的大量服务器;云安全技术不占用各人用户电脑的内存,而现在的杀毒软件占用客户电脑大量的内存;运用云安全技术,如果互联网上的一个客户端受到病毒的攻击,云安全通过收集该病毒的代码,使得网上的其他人员免受这个病毒的侵扰;

            运用云安全,在病毒到来之前病毒就被拦截了,通常个人电脑的安全技术,是病毒发生之后,杀毒软件才能铲除,甚至杀毒软件不升级的话还不能有效防护。

611/41234>
Open Toolbar