﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-乐在其中-随笔分类-J2SE</title><link>http://www.blogjava.net/charles/category/36714.html</link><description>以JEE为主攻，以Flex为点缀，以Eclipse RCP为乐趣</description><language>zh-cn</language><lastBuildDate>Fri, 13 Feb 2009 02:47:30 GMT</lastBuildDate><pubDate>Fri, 13 Feb 2009 02:47:30 GMT</pubDate><ttl>60</ttl><item><title>深入探索 高效的Java异常处理框架 </title><link>http://www.blogjava.net/charles/archive/2009/02/12/254461.html</link><dc:creator>suprasoft Inc,.</dc:creator><author>suprasoft Inc,.</author><pubDate>Thu, 12 Feb 2009 14:36:00 GMT</pubDate><guid>http://www.blogjava.net/charles/archive/2009/02/12/254461.html</guid><wfw:comment>http://www.blogjava.net/charles/comments/254461.html</wfw:comment><comments>http://www.blogjava.net/charles/archive/2009/02/12/254461.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/charles/comments/commentRss/254461.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/charles/services/trackbacks/254461.html</trackback:ping><description><![CDATA[摘要：本文从<a href="http://java.chinaitlab.com/" target="_blank">Java</a>异常最基本的概念、语法开始讲述了<a href="http://java.chinaitlab.com/" target="_blank">Java</a>异常处理的基本知识，分析了Java异常体系结构，对比Spring的异常处理框架，阐述了异常处理的基本原则。并且作者提出了自己处理一个大型应用系统异常的思想，并通过设计一个异常处理的框架来论述此思想。
<p>&nbsp;&nbsp;&nbsp; <strong><font color="#0000ff">一、 异常的概念和Java异常体系结构</font></strong></p>
<p>&nbsp;&nbsp;&nbsp; 异常是程序运行过程中出现的错误。本文主要讲授的是Java语言的异常处理。Java语言的异常处理框架，是Java语言健壮性的一个重要体现。</p>
<p>&nbsp;&nbsp;&nbsp; Java把异常当作对象来处理，并定义一个基类java.lang.Throwable作为所有异常的超类。在Java API中已经定义了许多异常类，这些异常类分为两大类，错误Error和异常Exception。Java异常体系结构呈树状，其层次结构图如图 1所示：</p>
<p align="center"><img src="http://java.chinaitlab.com/UploadFiles_8734/200806/20080602165248596.jpg" twffan="done"  alt="" /></p>
<p style="margin: 0cm 0cm 0pt; text-align: center" align="center"><a name="_Ref197949418"><font size="2"><span twffan="done">图</span></font></a><font size="2"><font face="Arial"><span twffan="done"><span twffan="done"><span twffan="done">1</span></span></span><span twffan="done"><span twffan="done">Java</span></span></font><span twffan="done"><span twffan="done">异常体系结构</span></span></font></p>
<p>&nbsp;&nbsp;&nbsp; Thorwable类所有异常和错误的超类，有两个子类Error和Exception，分别表示错误和异常。其中异常类Exception又分为运行时异常(RuntimeException)和非运行时异常，这两种异常有很大的区别，也称之为不检查异常（Unchecked Exception）和检查异常（Checked Exception）。下面将详细讲述这些异常之间的区别与联系：</p>
<p>&nbsp;&nbsp;&nbsp; <strong>1、Error与Exception</strong></p>
<p>&nbsp;&nbsp;&nbsp; Error是程序无法处理的错误，比如OutOfMemoryError、ThreadDeath等。这些异常发生时，Java虚拟机（JVM）一般会选择线程终止。</p>
<p>&nbsp;&nbsp;&nbsp; Exception是程序本身可以处理的异常，这种异常分两大类运行时异常和非运行时异常。程序中应当尽可能去处理这些异常。</p>
<p>&nbsp;&nbsp;&nbsp;<strong> 2、运行时异常和非运行时异常</strong></p>
<p>&nbsp;&nbsp;&nbsp; 运行时异常都是RuntimeException类及其子类异常，如NullPointerException、IndexOutOfBoundsException等，这些异常是不检查异常，程序中可以选择捕获处理，也可以不处理。这些异常一般是由程序逻辑错误引起的，程序应该从逻辑角度尽可能避免这类异常的发生。</p>
<p>&nbsp;&nbsp;&nbsp; 非运行时异常是RuntimeException以外的异常，类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常，如果不处理，程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常，一般情况下不自定义检查异常。</p>
<p><strong><font color="#0000ff">二、 异常的捕获和处理</font></strong></p>
<p>&nbsp;&nbsp;&nbsp; Java异常的捕获和处理是一个不容易把握的事情，如果处理不当，不但会让程序代码的可读性大大降低，而且导致系统性能低下，甚至引发一些难以发现的错误。</p>
<p>&nbsp;&nbsp;&nbsp; Java异常处理涉及到五个关键字，分别是：try、catch、finally、throw、throws。下面将骤一介绍，通过认识这五个关键字，掌握基本异常处理知识。</p>
<p><strong>&nbsp;&nbsp; 1、 异常处理的基本语法<br />
</strong>&nbsp;&nbsp;&nbsp; 在java中，异常处理的完整语法是：<br />
&nbsp;&nbsp;&nbsp;
<table bordercolor="#cccccc" cellspacing="0" cellpadding="1" width="80%" align="center" bgcolor="#ffffff" border="1">
    <tbody>
        <tr>
            <td>try{<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //（尝试运行的）程序代码<br />
            &nbsp;&nbsp;&nbsp; }catch(异常类型 异常的变量名){<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //异常处理代码<br />
            &nbsp;&nbsp;&nbsp; }finally{<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //异常发生，方法返回之前，总是要执行的代码<br />
            &nbsp;&nbsp;&nbsp; }</td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;&nbsp;&nbsp; 以上语法有三个代码块：<br />
&nbsp;&nbsp;&nbsp; try语句块，表示要尝试运行代码，try语句块中代码受异常监控，其中代码发生异常时，会抛出异常对象。</p>
<p>&nbsp;&nbsp;&nbsp; catch语句块会捕获try代码块中发生的异常并在其代码块中做异常处理，catch语句带一个Throwable类型的参数，表示可捕获异常类型。当try中出现异常时，catch会捕获到发生的异常，并和自己的异常类型匹配，若匹配，则执行catch块中代码，并将catch块参数指向所抛的异常对象。catch语句可以有多个，用来匹配多个中的一个异常，一旦匹配上后，就不再尝试匹配别的catch块了。通过异常对象可以获取异常发生时完整的JVM堆栈信息，以及异常信息和异常发生的原因等。</p>
<p>&nbsp;&nbsp;&nbsp; finally语句块是紧跟catch语句后的语句块，这个语句块总是会在方法返回前执行，而不管是否try语句块是否发生异常。并且这个语句块总是在方法返回前执行。目的是给程序一个补救的机会。这样做也体现了Java语言的健壮性。</p>
<p>&nbsp;&nbsp;<strong> 2、 try、catch、finally三个语句块应注意的问题<br />
</strong>&nbsp;&nbsp;&nbsp; 第一、try、catch、finally三个语句块均不能单独使用，三者可以组成 try...catch...finally、try...catch、try...finally三种结构，catch语句可以有一个或多个，finally语句最多一个。<br />
&nbsp;&nbsp;&nbsp; 第二、try、catch、finally三个代码块中变量的作用域为代码块内部，分别独立而不能相互访问。如果要在三个块中都可以访问，则需要将变量定义到这些块的外面。<br />
&nbsp;&nbsp;&nbsp; 第三、多个catch块时候，只会匹配其中一个异常类并执行catch块代码，而不会再执行别的catch块，并且匹配catch语句的顺序是由上到下。</p>
<p>&nbsp;&nbsp;&nbsp; <strong>3、throw、throws关键字<br />
</strong>&nbsp;&nbsp;&nbsp; throw关键字是用于方法体内部，用来抛出一个Throwable类型的异常。如果抛出了检查异常，则还应该在方法头部声明方法可能抛出的异常类型。该方法的调用者也必须检查处理抛出的异常。如果所有方法都层层上抛获取的异常，最终JVM会进行处理，处理也很简单，就是打印异常消息和堆栈信息。如果抛出的是Error或RuntimeException，则该方法的调用者可选择处理该异常。有关异常的转译会在下面说明。</p>
<p>&nbsp;&nbsp;&nbsp; throws关键字用于方法体外部的方法声明部分，用来声明方法可能会抛出某些异常。仅当抛出了检查异常，该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候，应该继续抛出，而不是囫囵吞枣一般在catch块中打印一下堆栈信息做个勉强处理。下面给出一个简单例子，看看如何使用这两个关键字：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<table bordercolor="#cccccc" cellspacing="0" cellpadding="1" width="80%" align="center" bgcolor="#ffffff" border="1">
    <tbody>
        <tr>
            <td>public static void test3() throws Exception{<br />
            &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; //抛出一个检查异常<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new Exception("方法test3中的Exception");<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
            <p>&nbsp;&nbsp;&nbsp; 3、 Throwable类中的常用方法<br />
            &nbsp;&nbsp;&nbsp; getCause()：返回抛出异常的原因。如果 cause 不存在或未知，则返回 null。<br />
            &nbsp;&nbsp;&nbsp; getMessage()：返回异常的消息信息。<br />
            &nbsp;&nbsp;&nbsp; printStackTrace()：对象的堆栈跟踪输出至错误输出流，作为字段 System.err 的值。</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<strong><font color="#0000ff">三、 异常处理的一般原则</font></strong>
<p>&nbsp;&nbsp;&nbsp; 1、 能处理就早处理，抛出不去还不能处理的就想法消化掉或者转换为RuntimeException处理。因为对于一个应用系统来说，抛出大量异常是有问题的，应该从程序开发角度尽可能的控制异常发生的可能。<br />
&nbsp;&nbsp;&nbsp; 2、 对于检查异常，如果不能行之有效的处理，还不如转换为RuntimeException抛出。这样也让上层的代码有选择的余地――可处理也可不处理。<br />
&nbsp;&nbsp;&nbsp; 3、 对于一个应用系统来说，应该有自己的一套异常处理框架，这样当异常发生时，也能得到统一的处理风格，将优雅的异常信息反馈给用户。</p>
<p>&nbsp;&nbsp;&nbsp; <strong><font color="#0000ff">四、 异常的转译与异常链</font></strong></p>
<p>&nbsp;&nbsp;&nbsp; <strong>1、异常转译的原理</strong></p>
<p>&nbsp;&nbsp;&nbsp; 所谓的异常转译就是将一种异常转换另一种新的异常，也许这种新的异常更能准确表达程序发生异常。</p>
<p>&nbsp;&nbsp;&nbsp; 在Java中有个概念就是异常原因，异常原因导致当前抛出异常的那个异常对象，几乎所有带异常原因的异常构造方法都使用Throwable类型做参数，这也就为异常的转译提供了直接的支持，因为任何形式的异常和错误都是Throwable的子类。比如将SQLException转换为另外一个新的异常DAOException，可以这么写：</p>
<p>&nbsp;&nbsp;&nbsp; 先自定义一个异常DAOException：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;
<table bordercolor="#cccccc" cellspacing="0" cellpadding="1" width="80%" align="center" bgcolor="#ffffff" border="1">
    <tbody>
        <tr>
            <td>public class DAOException extends RuntimeException {<br />
            &nbsp;&nbsp;&nbsp; //(省略了部分代码)<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public DAOException(String message, Throwable cause) {<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super(message, cause);<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
            &nbsp;&nbsp;&nbsp; }</td>
        </tr>
    </tbody>
</table>
&nbsp;&nbsp;&nbsp; 比如有一个SQLException类型的异常对象e，要转换为DAOException，可以这么写：</p>
<p>&nbsp;&nbsp;&nbsp; DAOException daoEx = new DAOException ( "SQL异常", e);</p>
<p>&nbsp;&nbsp;&nbsp; 异常转译是针对所有继承Throwable超类的类而言的，从编程的语法角度讲，其子类之间都可以相互转换。但是，从合理性和系统设计角度考虑，可将异常分为三类：Error、Exception、RuntimeException，笔者认为，合理的转译关系图应该如图 2：</p>
<p align="center"><img height="222" src="http://java.chinaitlab.com/UploadFiles_8734/200806/20080602165616877.jpg" width="337" border="0" twffan="done"  alt="" /></p>
<p style="margin: 0cm 0cm 0pt; text-align: center" align="center"><a name="_Ref197949474"><font size="2"><span twffan="done">图</span></font></a><font size="2"><font face="Arial"><span twffan="done"><span twffan="done"><span twffan="done">2</span></span></span></font><span twffan="done">异常转译</span></font></p>
<p>&nbsp;&nbsp;&nbsp; 为什么要这么做呢？笔者认为，异常的处理存在着一套哲学思想：<strong>对于一个应用系统来说，系统所发生的任何异常或者错误对操作用户来说都是系统"运行时"异常，都是这个应用系统内部的异常。这也是异常转译和应用系统异常框架设计的指导原则。</strong>在系统中大量处理非检查异常的负面影响很多，最重要的一个方面就是代码可读性降低，程序编写复杂，异常处理的代码也很苍白无力。因此，很有必要将这些检查异常Exception和错误Error转换为RuntimeException异常，让程序员根据情况来决定是否捕获和处理所发生的异常。</p>
<p>&nbsp;&nbsp;<strong> 图中的三条线标识转换的方向，分三种情况：</strong></p>
<p>&nbsp;&nbsp;&nbsp; ①：Error到Exception：将错误转换为异常，并继续抛出。例如Spring WEB框架中，将org.springframework.web.servlet.DispatcherServlet的doDispatch()方法中，将捕获的错误转译为一个NestedServletException异常。这样做的目的是为了最大限度挽回因错误发生带来的负面影响。因为一个Error常常是很严重的错误，可能会引起系统挂起。</p>
<p>&nbsp;&nbsp;&nbsp; ②：Exception到RuntimeException：将检查异常转换为RuntimeException可以让程序代码变得更优雅，让开发人员集中经理设计更合理的程序代码，反过来也增加了系统发生异常的可能性。</p>
<p>&nbsp;&nbsp;&nbsp; ③：Error到RuntimeException：目的还是一样的。把所有的异常和错误转译为不检查异常，这样可以让代码更为简洁，还有利于对错误和异常信息的统一处理。</p>
<p>&nbsp;&nbsp;&nbsp; <strong>1、 异常链</strong></p>
<p>&nbsp;&nbsp;&nbsp; 异常链顾名思义就是将异常发生的原因一个传一个串起来，即把底层的异常信息传给上层，这样逐层抛出。Java API文档中给出了一个简单的模型：</p>
<p>&nbsp;&nbsp;&nbsp;
<table bordercolor="#cccccc" cellspacing="0" cellpadding="1" width="80%" align="center" bgcolor="#ffffff" border="1">
    <tbody>
        <tr>
            <td>try {<br />
            &nbsp;&nbsp;&nbsp; lowLevelOp();<br />
            &nbsp;&nbsp;&nbsp; } catch (LowLevelException le) {<br />
            &nbsp;&nbsp;&nbsp; throw (HighLevelException)<br />
            &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; new HighLevelException().initCause(le);<br />
            &nbsp;&nbsp;&nbsp; }</td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;&nbsp;&nbsp; 当程序捕获到了一个底层异常le，在处理部分选择了继续抛出一个更高级别的新异常给此方法的调用者。这样异常的原因就会逐层传递。这样，位于高层的异常递归调用getCause()方法，就可以遍历各层的异常原因。这就是Java异常链的原理。异常链的实际应用很少，发生异常时候逐层上抛不是个好注意，上层拿到这些异常又能奈之何？而且异常逐层上抛会消耗大量资源，因为要保存一个完整的异常链信息。</p>
<p><strong><font color="#0033ff">五、 设计一个高效合理的异常处理框架</font></strong></p>
<p>&nbsp;&nbsp;&nbsp; 对于一个应用系统来说，发生所有异常在用户看来都是应用系统内部的异常。因此应该设计一套应用系统的异常框架，以处理系统运行过程中的所有异常。</p>
<p>&nbsp;&nbsp;&nbsp; 基于这种观点，可以设计一个应用系统的异常比如叫做AppException。并且对用户来说，这些异常都是运行应用系统运行时发生的，因此AppException应该继承RuntimeException，这样系统中所有的其他异常都转译为AppException，当异常发生的时候，前端接收到AppExcetpion并做统一的处理。画出异常处理框架如图 3 ：</p>
<p align="center"><img src="http://java.chinaitlab.com/UploadFiles_8734/200806/20080602165812436.jpg" twffan="done"  alt="" /></p>
<p style="margin: 0cm 0cm 0pt; text-align: center" align="center"><a name="_Ref197949559"><font size="2"><span twffan="done">图</span></font></a><font size="2"><font face="Arial"><span twffan="done"><span twffan="done"><span twffan="done">3</span></span></span></font><span twffan="done"><span twffan="done">一个应用系统的异常处理框架</span></span></font></p>
<p>&nbsp;&nbsp;&nbsp; 在这个设计图中，AppRuntimeException是系统异常的基类，对外只抛出这个异常，这个异常可以由前端（客户端）接收处理，当异常发生时，客户端的相关组件捕获并处理这些异常，将"友好"的信息展示给客户。</p>
<p>&nbsp;&nbsp;&nbsp; 在AppRuntimeException下层，有各种各样的异常和错误，最终都转译为AppRuntimeException，AppRuntimeException下面还可以设计一些别的子类异常，比如AppDAOException、OtherException等，这些都根据实际需要灵活处理。在往下就是如何将捕获的原始异常比如SQLException、HibernateException转换为更高级一点AppDAOException。</p>
<p>&nbsp;&nbsp;&nbsp; 有关异常框架设计这方面公认比较好的就是Spring，Spring中的所有异常都可以用org.springframework.core.NestedRuntimeException来表示，并且该基类继承的是RuntimeException。Spring框架很庞大，因此设计了很多NestedRuntimeException的子类，还有异常转换的工具，这些都是非常优秀的设计思想。</p>
<p><strong><font color="#0000ff">&nbsp;&nbsp;&nbsp; 六、 Java异常处理总结</font></strong></p>
<p>&nbsp;&nbsp;&nbsp; <strong>回顾全文，总结一下Java异常处理的要点：</strong></p>
<p>&nbsp;&nbsp;&nbsp; 1、 异常是程序运行过程过程出现的错误，在Java中用类来描述，用对象来表示具体的异常。Java将其区分为Error与Exception，Error是程序无力处理的错误，Exception是程序可以处理的错误。异常处理是为了程序的健壮性。<br />
&nbsp;&nbsp;&nbsp; 2、 Java异常类来自于Java API定义和用户扩展。通过继承Java API异常类可以实现异常的转译。<br />
&nbsp;&nbsp;&nbsp; 3、 异常能处理就处理，不能处理就抛出，最终没有处理的异常JVM会进行处理。<br />
&nbsp;&nbsp;&nbsp; 4、 异常可以传播，也可以相互转译，但应该根据需要选择合理的异常转译的方向。<br />
&nbsp;&nbsp;&nbsp; 5、 对于一个应用系统，设计一套良好的异常处理体系很重要。这一点在系统设计的时候就应该考虑到。</p>
<img src ="http://www.blogjava.net/charles/aggbug/254461.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/charles/" target="_blank">suprasoft Inc,.</a> 2009-02-12 22:36 <a href="http://www.blogjava.net/charles/archive/2009/02/12/254461.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>高效的Java异常处理模式</title><link>http://www.blogjava.net/charles/archive/2009/02/12/254457.html</link><dc:creator>suprasoft Inc,.</dc:creator><author>suprasoft Inc,.</author><pubDate>Thu, 12 Feb 2009 14:29:00 GMT</pubDate><guid>http://www.blogjava.net/charles/archive/2009/02/12/254457.html</guid><wfw:comment>http://www.blogjava.net/charles/comments/254457.html</wfw:comment><comments>http://www.blogjava.net/charles/archive/2009/02/12/254457.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/charles/comments/commentRss/254457.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/charles/services/trackbacks/254457.html</trackback:ping><description><![CDATA[<p><font face="Arial"><strong>1&nbsp;基本信息</strong></font></p>
<p><font face="Arial"><strong>摘要：</strong>本文倡导一种对异常条件本质的思考方式，并描述一些有助于设计的模式。最后，本文还将在AOP模型中，作为相互渗透的问题，来讨论异常的处理。当你能正确使用异常时，它们会有极大的好处。本文将帮助你做到这一点。</font></p>
<p><font face="Arial"><strong>原作者：</strong>Barry Ruzek&nbsp;&nbsp; <strong>译者：</strong> 易晓斓，原文：<a href="http://www.yeeyan.com/articles/view/2091/976"><a href="http://www.yeeyan.com/articles/view/2091/976" target="_blank">http://www.yeeyan.com/articles/view/2091/976</a></a></font></p>
<p><font face="Arial"><strong>2&nbsp;为何异常是如此重要</strong></font></p>
<p><font face="Arial">　　Java应用中的异常处理在很大程度上揭示了其所基于架构的强度。架构是在应用程序各个层次上所做出并遵循的决定。其中最重要的一个就是决定应用程序中的类，亚系统，或层之间沟通的方式。Java异常是Java方法将另类执行结果交流出去的方式，所以值得在应用架构中给予特殊关注。</font></p>
<p><font face="Arial">　　一个衡量Java设计师水平和开发团队纪律性的好方法就是读读他们应用程序里的异常处理代码。首先要注意的是有多少代码用于捕获异常，写进日志文件，决定发生了什么，和在不同的异常间跳转。干净，简捷，关联性强的异常处理通常表明开发团队有着稳定的使用Java异常的方式。当异常处理代码的数量甚至要超过其他代码时，你可以看出团队之间的交流合作有很大的问题（可能在一开始就不存在），每个人都在用他们自己的方式来处理异常。</font></p>
<p><font face="Arial">　　对突发异常的处理结果是可以预见的。如果你问问团队成员为什么异常会被抛出，捕获，或在特定的一处代码里忽视了异常的发生，他们的回答通常是，&#8220;我没有别的可做&#8221;。如果你问当他们编写的异常真的发生了会怎么样，他们会皱皱眉，你得到的回答类似于这样，&#8220;我不知道。我们从没测试过。&#8221;</font></p>
<p><font face="Arial">　　你可以从客户端的代码判断一个java的组件是否有效利用了java的异常。如果它们包含着大堆的逻辑去弄清楚在何时一笔操作失败了，为何失败，是否有弥补的余地，那么原因很有可能要归咎于组件的报错设计。错误的报错系统会在客户端产生大量的&#8220;记录然后忘掉&#8221;的代码，这些代码鲜有用途。最差的是弄拧的逻辑，嵌套的try/catch/finally代码块，和一些其他的混乱而导致脆弱而难于管理的应用程序。</font></p>
<p><font face="Arial">　　事后再来解决Java异常的问题，或根本就不解决，是软件项目产生混乱并导致滞后的主要原因。异常处理是一个在设计的各个部分都急需解决的问题。对异常处理建立一个架构性的约定是项目中首要做出的决定。合理使用Java异常模型对确保你的应用简单，易维护，和正确有着长远的影响。</font></p>
<p><font face="Arial"><strong>3&nbsp;解析异常</strong></font></p>
<p align="left"><font face="Arial">　　正确使用Java异常模型所包含的内容一直以来有着很大的争议。Java不是第一种支持异常算法语义的；但是，它却是第一种通过编译器来执行声明和处理某些异常的规则的语言。许多人都认为编译时的异常检查对精确的软件设计颇有帮助。图1显示的Java异常的等级。<br />
&nbsp; <br />
<img height="721" alt="" src="http://gocom.primeton.com/uploads/FCKUserFiles/Image/1(233).jpg" width="451" twffan="done" /><br />
图1：Java异常的等级</font></p>
<p><font face="Arial">　　通常，Java编译器强迫抛出基于java.lang.Throwable的异常的方法要在它声明中的&#8220;throws&#8221;部分加上那个异常。而且，编译器还会证实客户端的方法或者捕获已声明的异常，或者特别声明自己也抛出同样的异常。这些简单的规则对世界范围的Java程序员都有深远的影响。</font></p>
<p><font face="Arial">　　编译器放松了对Throwable继承树中两个分支的异常检查。java.long.Error和java.lang.RuntimeException 的子类免于编译时的检查。在这两类中，软件工程师通常对运行中异常更感兴趣。&#8220;不检查&#8221;的异常指的是这一组，以便和所有其它&#8220;检查&#8221;的异常区别开。</font></p>
<p><font face="Arial">　　我可以想象那些接受&#8220;检查&#8221;的异常的人，也会很看重Java的数据类型。毕竟，编译器对数据类型施加的限制鼓励严格的编码和精确的思维。编译时的类型检查对减少运行时的严重问题有帮助。编译时的异常检查也能起到类似的作用，它会提醒开发人员某个方法可能会有预想不到的结果需要处理好。</font></p>
<p><font face="Arial">　　早期的建议是尽可能的使用&#8220;检察的异常&#8221;，以此来最大限度的利用编译器提供的帮助来写出无错误的软件。Java类库API的设计者们都认同这一点，他们广泛地使用&#8220;检察的异常&#8221;来模拟类库方法中几乎所有的紧急应变措施。在J2SE5.1 API规格中，&#8220;检察的异常&#8221;类型已2比1的比率超过了&#8220;未检查的异常&#8221;类型。</font></p>
<p><font face="Arial">　　对程序员而言，看上去在Java类库中大多数的常用方法对每一个可能的失败都声明了&#8220;检察的异常&#8221;。例如，java.io包<br />
对IOException这个&#8220;检察的异常&#8221;就有着很大的依赖。至少有63个Java类库包，或直接，或通过十几个下面的子类，抛出这个异常。</font></p>
<p><font face="Arial">　　I/O的失败极其稀有，但是却很严重。而且，一旦发生，从你所写的代码里基本上是无法补救的。Java程序员意识到他们不得不提供IOException 或类似的不可补救的事件，而一个简单的Java类库方法的调用就可能让这些事件发生。捕获这些异常给本来简单的代码带来了一定的晦涩，因为即使在捕获的代码块里也基本上帮不上忙。但是不加以捕获又可能更糟糕，因为编译器要求你的方法必须要抛出那些异常。这样你的实施细则就不得不暴露在外了，而通常好的面向对象的设计都是要隐藏细节的。</font></p>
<p><font face="Arial">　　这样一个不可能赢的局面导致了我们今天所警告的绝大多数臭名卓著的异常处理的颠覆性格局。同时也衍生了很多正确或错误的补救之道。</font></p>
<p><font face="Arial">　　一些Java界的知名人物开始质疑Java的&#8220;检察的异常&#8221;的模型是否是一个失败的试验。有一些东西肯定是失败的，但是这和在Java语言里加入对异常的检查是毫无关联的。失败是由于在Java API的设计者们的思维里，大多数失败的情形是雷同的，所以可以通过同一种异常传达出去。</font></p>
<p><font face="Arial"><strong>4&nbsp;故障和应变</strong></font></p>
<p><font face="Arial">　　让我们来考虑在一个假想的银行应用中的CheckingAccount类。一个CheckingAcccount属于一个用户，记载着用户的存款余额，也能接受存款，接受止兑的通知，和处理汇入的支票。一个CheckingAcccount对象必须协调同步线程的访问，因为任何一个线程都可能改变它的状态。</font><font face="Arial">CheckingAcccount类里processCheck的方法会接受一个Check对象为参数，通常从帐户余额里减去支票的金额。但是一个管理支票清算的用户端程序调用processCheck方法时，必须有两种可能的应变措施。一，CheckingAccount对象里可能对该支票已有一个止付的命令；二，帐户的余额可能不足已满足支票的金额。</font></p>
<p><font face="Arial">　　所以，processCheck的方法对来自客户端的调用可以有3种方式回应。正常的是处理好支票，并把方法签名里声明的结果返回给调用方。两种应变的回应则是需要与支票清算端沟通的在银行领域实实在在存在的情况。processCheck方法所有3种返回结果都是按照典型的银行支票帐户的行为而精心设计的。</font></p>
<p><font face="Arial">　　在Java里，一个自然的方法来表示上述紧急的应变是定义两种异常，比如StopPaymentException（止付异常）和 InsufficientFundsException（余额不足异常）。一个客户端如果忽略这些异常是不对的，因为这些异常在正常操作的情况下一定会被抛出。他们如同方法的签名一样反映了方法的全面行为。</font></p>
<p><font face="Arial">　　客户端可以很容易的处理好这两种异常。如果对支票的兑付被停止了，客户端把该支票交付特别处理。如果是因为资金不足，用户端可以从用户的储蓄帐户里转移一些资金到支票帐户里，然后再试一次。</font></p>
<p><font face="Arial">　　在使用CheckingAccount的API时，这些应变都是可以预计的和自然的结果。他们并不是意味着软件或运行环境的失败。这些异常和由于CheckingAccount类中一些内部实施细则引起的真正失败是不同的。</font></p>
<p><font face="Arial">　　设想CheckingAccount对象在数据库里保持着一个恒定的状态，并使用JDBC API来对之访问。在那个API里，几乎所有的数据库访问方法都有可能因为和CheckingAccount实施无关的原因而失败。比如，有人可能忘了把数据库服务器运行起来，一个未有连上的网络数据线，访问数据库的密码改变了，等等。</font></p>
<p><font face="Arial">　　JDBC依靠一种&#8220;检查的异常&#8221;，SQLException，来汇报任何可能的错误。可能出错的绝大多数原由都是数据库的配置，连接，或其所在的硬件设施。对processCheck方法而言，它对上述错误是无计可施的。这不应该，因为processCheck至少了解它自己的实施细则。在调用栈里上游的方法能处理这些问题的可能就更小。</font></p>
<p><font face="Arial">　　CheckingAccount这个例子说明了一个方法不能成功返回它想要的结果的两个基本原因。这里是两个描述性的术语：</font></p>
<p><font face="Arial"><strong>应变</strong><br />
&nbsp;&nbsp;&nbsp; 与实际预料相符，一个方法给出另外一种回应，而这种回应可以表达成该方法所要达到的目的之一。这个方法的调用者预料到这个情况的出现，并有相对的应付之道。</font></p>
<p><font face="Arial"><strong>故障</strong><br />
&nbsp;&nbsp;&nbsp; 在未经计划的情况下，一个方法不能达到它的初衷，这是一个不诉诸该方法的实施细则就很难搞清的情况。<br />
&nbsp;&nbsp;&nbsp; 应用这些术语，对processCheck方法而言，一个止付的命令和一个超额的提取是两种可能的应变。而SQLException反映了可能的故障。processCheck方法的调用者应该能够提供应变，但却不一定能有效的处理好可能发生的故障。</font></p>
<p><font face="Arial"><strong>Java异常的匹配<br />
</strong>&nbsp;&nbsp;&nbsp; 在建立应用架构中Java异常的规则时，以应变和故障的方式仔细考虑好&#8220;什么可能会出错&#8221;是有长远意义的。</font></p>
<p><img height="240" alt="" src="http://gocom.primeton.com/uploads/FCKUserFiles/Image/1(234).jpg" width="566" twffan="done" /></p>
<p><font face="Arial">　　应变情况恰如其分地匹配给了Java检查的异常。因为它们是方法的语义算法合同中不可缺少的一部分，在这里借助于编译器的帮助来确保它们得到解决是很有道理的。如果你发现编译器坚持应变的异常必须要处理或者在不方便的时候必须要声明会给你带来些麻烦，你在设计上几乎肯定要做些重构了。这其实是件好事。</font></p>
<p><font face="Arial">　　出现故障的情况对开发人员而言是蛮有意思的，但对软件逻辑而言却并非如此。那些软件&#8221;消化问题&#8220;的专家们需要关于故障的信息以便来解决问题。因此，未检查的异常是表示故障的很好方式。他们让故障的通知原封不动地从调用栈上所有的方法滤过，到达一个专门来捕获它们的地方，并得到它们自身包含的有利于诊断的信息，对整个事件提供一个有节制的优雅的结论。产生故障的方法不需要来声明（异常），上游的调用方法不需要捕获它们，方法的实施细则被正确的隐藏起来－ 以最低的代码复杂度。</font></p>
<p><font face="Arial">　　新一些的Java API，比如像Spring架构和Java Data Ojects类库对检查的异常几乎没有依赖。Hibernate ORM架构在3.0版本里重新定义了一些关键功能来去除对检查的异常的使用。这就意味着在这些架构举报的绝大部分异常都是不可恢复的，归咎于错误的方法调用代码，或是类似于数据库服务器之类的底层部件的失败。特别的，强迫一个调用方来捕获或声明这些异常几乎没有任何好处。<br />
设计里的故障处理</font></p>
<p><font face="Arial">　　在你的计划里，承认你需要去做就迈好了有效处理好故障的第一步。对那些坚信自己能写出无懈可击的软件的工程师们来说，承认这一点是不容易的。这里是一些有帮助的思考方式。首先，如果错误俯拾即是，应用的开发时间将很长，当然前提是程序员自己的bug自己修理。第二，在Java类库中，过度使用检查的异常来处理故障情形将迫使你的代码要应对好故障，即使你的调用次序完全正确。如果没有一个故障处理的架构，凑合的异常处理将导致应用中的信息丢失。</font></p>
<p><font face="Arial">一个成功的故障处理架构一定要达到下面的目标：<br />
&#8226;&nbsp;减少代码的复杂性 <br />
&#8226;&nbsp;捕获和保存诊断性信息 <br />
&#8226;&nbsp;对合适的人提醒注意 <br />
&#8226;&nbsp;优雅地退出行动 </font></p>
<p><font face="Arial">　　故障是应用的真实意图的干扰。因此，用来处理它们的代码应尽量的少，理想上，把它们和应用的语义算法部分隔离开。故障的处理必须满足那些负责改正它们的人的需要。开发人员需要知道故障发生了，并得到能帮助他们搞清为何发生的信息。即使一个故障，在定义上而言，是不可补救的，好的故障处理会试着优雅地结束引起故障的活动。<br />
</font></p>
<p><font face="Arial"><strong>对故障情况使用未检查的异常</strong></font></p>
<p><font face="Arial">　　在做框架上的决定时，用未检查的异常来代表故障情况是有很多原因的。Java的运行环境对代码的错误会抛出&#8220;运行时异常&#8221;的子类，比如， ArithmeticException或ClassCastException。这为你的框架设了一个先例。未检查的异常让上游的调用方法不需要为和它们目的不相关的情况而添加代码，从而减少了混乱。</font></p>
<p><font face="Arial">　　你的故障处理策略应该认识到Java类库的方法和其他API可能会使用检查的异常来代表对你的应用而言只可能是故障的情况。在这种情形下，采用设计约定来捕获API异常，将其以故障来看待，抛出一个未检查的异常来指示故障的情况和捕获诊断的信息。</font></p>
<p><font face="Arial">　　在这种情况下抛出的特定异常类型应该由你的框架来定义。不要忘记一个故障异常的主要目的是传递记录下来的诊断信息，以便让人们来想出出错的原因。使用多个故障异常类型可能有些过，因为你的架构对它们都一视同仁。多数情况下，一条好的，描述性强的信息将单一的故障类型嵌入就够用了。使用Java基本的 RuntimeException来代表故障情况是很容易的。截止到Java1.4，RuntimeException，和其他的抛出类型一样，都支持异常的嵌套，这样你就可以捕获和报出导向故障的检查的异常。</font></p>
<p><font face="Arial">　　你也许会为了故障报告的目的而定义你自己的未检查的异常。这样做可能是必要的，如果你使用Java1.3或更早的版本，它们都不支持异常的嵌套。实施一个类似的嵌套功能来捕获和转换你应用中构成故障的检查的异常是很简单的。你的应用在报错时可能需要一个特殊的行为。这可能是你在架构中创建 RuntimeException子类的另一个原因。</font></p>
<p><font face="Arial"><strong>建立一个故障的屏障</strong></font></p>
<p><font face="Arial">　　对你的故障处理架构而言，决定抛出什么样的异常，何时抛出是重要的决定。同样重要的是，何时来捕获一个故障异常，之后再怎么办。这里的目的是让你应用中的功能性部分不需要处理故障。把问题分开来处理通常都是一件好事情，有一个中央故障处理机制长远来看是很有裨益的。</font></p>
<p><font face="Arial">　　在故障屏障的模式里，任何应用组件都可以抛出故障异常，但是只有作为&#8220;故障屏障&#8221;的组件才捕获异常。采用此种模式去除了大多数程序员为了在本地处理故障而插入的复杂的代码。故障屏障逻辑上位于调用栈的上层，这样在一个默认的行动被激发前，一个异常向上举报的行为就被阻止了。根据不同的应用类型，默认的行动所指也不同。对一个独立的Java应用而言，这个行动指活着的线程被停止。对一个位于应用服务器上的Web应用而言，这个行动指应用服务器向浏览器送出不友好的（甚至令人尴尬的）回应。</font></p>
<p><font face="Arial">　　一个故障屏障组件的第一要务就是记录下故障异常中包含的信息以为将来所用。到现在为止，一个应用日志是做成此事的首选。异常的嵌套的信息，栈日志，等等，都是对诊断有价值的信息。传递故障信息最差的地方是通过用户界面。把应用的使用者卷进查错的进程对你，对你的用户而言都不好。如果你真的很想把诊断信息放上用户界面，那可能意味着你的日志策略需要改进。</font></p>
<p><font face="Arial">　　故障屏障的下一个要务是以一种可控的方式来结束操作。这具体的意义要取决于你应用的设计，但通常包括产生一个可通用的回应给可能正在等待的客户端。如果你的应用是一个Web service，这就意味着在回应中用soap:Server的&lt;faultcode&gt;和通用的失败信息&lt; faultstring&gt;来建立一个SOAP故障元素&lt;fault&gt;。如果你的应用于浏览器交流，这个屏障就会安排好一个通用的 HTML回应来表明需求是不能被处理的。</font></p>
<p><font face="Arial">　　在一个Struts的应用里，你的故障屏障会以一种全局异常处理器的形式出现，并被配置成处理RuntimeException的任何子类。你的故障屏障类将延伸org.apache.struts.action.ExceptionHandler类，必要的话，重写它的方法来实施用户自己的特别处理。这样就会处理好不小心产生的故障情况和在处理一个Struts动作时发现的故障。图2显示的就是应变和故障异常。<br />
&nbsp;<br />
<img height="721" alt="" src="http://gocom.primeton.com/uploads/FCKUserFiles/Image/1(235).jpg" width="451" twffan="done" /><br />
图2 应变和故障异常</font></p>
<p><font face="Arial">　　如果你使用的是Spring MVC架构，你可以继承SimpleMappingExceptionResolver类，并配置成处理RuntimeException和它的子类们，这样很容易的就建起了故障屏障。通过重写resolveException的方法，你可以在使用父类的方法来把需求导引到一个发出通用错误提示的view 组件之前，加入你需要的用户化的处理。</font></p>
<p><font face="Arial">　　当你的架构包含了故障屏障，程序员都知晓了后，再写出一次性的故障异常的冲动就会锐减。结果就是应用中出现更干净，更易于维护的代码。</font></p>
<p><font face="Arial"><strong>5&nbsp;架构中应变的处理</strong></font></p>
<p><font face="Arial">　　将故障处理交与屏障后，主要组件间的应变交流变得容易多了。一个应变代表着与主要返回结果同等重要的另外一种方法结果。因此，检查的异常类型是一个能够很好地传递应变情况的存在并提供必要的信息来与它竞争的工具。这个方式借助于Java编译器的帮助来提醒程序员关于他们所用的API的方方面面以及提供全套的方法输出的必要性。</font></p>
<p><font face="Arial">　　仅仅使用方法的返回值类型来传递简单的应变是可能的。比如，返回一个空引用，而不是一个具体的对象，可以意味着对象由于一个已定义的原因不能被建立。 Java I/O的方法通常返回一个整数值－1，而不是字节的值或字节的数来表示文件的结尾。如果你的方法的语义简单到可以允许的地步，另一种返回值的方法是可以使用的，因为它摒弃了异常带来的额外的花销。不足之处是方法的调用方要检测一下返回的值来判断是主要结果，还是应变结果。但是，编译器没有办法来保证方法调用者会使用这个判断。</font></p>
<p><font face="Arial">　　如果一个方法有一个void的返回类型，异常是唯一的方法来表示应变发生了。如果一个方法返回的是一个对象的引用，那么返回值只可能是空或非空（null and non-null)。如果一个方法返回一个整数型，选择与主要返回值不冲突的，可以表示多种应变情况的数值是可能的。但是这样的话，我们就进入了错误代码检查的世界，而这正式Java异常模式所着力避免的。<br />
提供一些有用的信息</font></p>
<p><font face="Arial">　　定义不同的故障报告的异常类型是没什么道理的，因为故障屏障对所有异常类型一视同仁。应变异常就有很大的不同，因为它们的原意是要向方法调用者传递各种情况。你的架构可能会指出这些异常应该继承java.lang.Exception或一个指定的基类。</font></p>
<p><font face="Arial">　　不要忘记你的异常应该是百分百的Java类型，你可以用它来存放为你的特殊目的服务的特殊字段，方法，甚至是构造器。比如，被假想的 processCheck()方法抛出的InsufficientFundsException这个异常类型就应该包含着一个 OverdraftProtection的对象，它能够从另外一个帐户里把短缺的资金转过来。</font></p>
<p><font face="Arial"><strong>日志还是不要日志</strong></font></p>
<p><font face="Arial">　　记录下故障异常是有用处的，因为日志的目的是在一些需要改正的情况下，日志可以吸引人们的注意力。但对应变异常而言却并非如此。应变异常可能代表的只是极少数情况，但是在你的应用里，每一个情况还是会发生的。它们意味着你的应用正在如最初的设计般正常工作着。经常把日志代码加进应变的捕获块里会使你的代码晦涩难懂，而又没有实际的好处。如果一个应变代表了一重要的事件，在抛出一个异常应变来警醒调用者之前，产生一笔日志，记录下这个事件可能会让这个方法更好些。</font></p>
<p><font face="Arial"><strong>6&nbsp;异常的各个方面</strong></font></p>
<p><font face="Arial">　　在Aspect Oriented Programming（AOP）的术语里，故障和应变的处理是互相渗透的问题。比如，要实施故障屏障的模式，所有参与的类必须遵循通用规格：</font></p>
<p><font face="Arial">&#8226;&nbsp;故障屏障方法必须存活在遍历参与类的方法调用图的最前端 <br />
&#8226;&nbsp;参与类必须使用未检查的异常来表示故障情况 <br />
&#8226;&nbsp;参与类必须使用故障屏障期望得到的有针对性的未检查的异常类型 <br />
&#8226;&nbsp;参与类必须捕获并从低端方法中把在执行情境下注定的故障转换成检查的异常 <br />
&#8226;&nbsp;参与类不能干扰故障异常被传递到故障屏障的过程</font></p>
<p><font face="Arial">　　这些问题超越了那些本不相干的类的边界。结果就是少数零散的故障处理代码，以及屏障类和参与类间暗含的耦合（这已经比不使用模式进步多了！）。AOP让故障处理的问题被封装在通用的可以作用到参与类的层面上。如AspectJ和Spring AOP这样的Java AOP架构认为异常的处理是添加故障处理行为的切入点。这样，把参与者绑定在故障屏障的模式可以放松些。故障的处理可以存活在一个独立的，不相干的方面里，从而摒弃了屏障方法需要放在方法激活次序的最前头的要求。</font></p>
<p><font face="Arial">　　如果在你的架构里利用了AOP，故障和应变的处理是理想的在应用里用到的在方面上的候选。对故障和应变的处理在AOP架构下的使用做一个完整的勘探将是将来论文里一个很有意思的题目。</font></p>
<p><font face="Arial"><strong>7&nbsp;结论</strong></font></p>
<p><font face="Arial">　　虽然Java异常模型自它出现以来就激发了热烈的讨论，如果使用正确的话，它的价值还是很大的。作为一个设计师，你的任务是建立好规格来最大限度地利用好这个模型。以故障和应变的方式来考量异常可以帮助你做出正确的决定。合理使用好Java异常模型可以让你的应用简单，易维护，和正确。AOP技术将故障和应变定位为相互渗透的问题，这个方法可能会对你的架构提供一些帮助。</font></p>
<p><font face="Arial">8&nbsp;引用</font></p>
<p><font face="Arial">&#8226;&nbsp;Sun's Exception Tutorial Java异常的基本知识<br />
&#8226;&nbsp;Does Java Need Checked Exception? Bruce Eckel对Java中检查的异常的异议 <br />
&#8226;&nbsp;Exceptional Java&nbsp; 关于异常的很好的讨论，有架构式的异常规则来模仿<br />
&#8226;&nbsp;The Exceptions Debate&nbsp; 来自于developerWorks的关于异常的来龙去脉<br />
&#8226;&nbsp;The Apache Struts Web Application Framework&nbsp; Struts的信息源<br />
&#8226;&nbsp;The Spring Framework&nbsp; Spring框架的信息源<br />
&#8226;&nbsp;Wikipedia: Aspect Oriented Programming&nbsp; 一个很好的对AOP概念的介绍<br />
&#8226;&nbsp;The AspectJ Project&nbsp; AspectJ的信息源 </font></p>
 <img src ="http://www.blogjava.net/charles/aggbug/254457.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/charles/" target="_blank">suprasoft Inc,.</a> 2009-02-12 22:29 <a href="http://www.blogjava.net/charles/archive/2009/02/12/254457.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>The Essence of OOP using Java, Static Initializer Blocks(zz)</title><link>http://www.blogjava.net/charles/archive/2009/01/21/252212.html</link><dc:creator>suprasoft Inc,.</dc:creator><author>suprasoft Inc,.</author><pubDate>Wed, 21 Jan 2009 06:43:00 GMT</pubDate><guid>http://www.blogjava.net/charles/archive/2009/01/21/252212.html</guid><wfw:comment>http://www.blogjava.net/charles/comments/252212.html</wfw:comment><comments>http://www.blogjava.net/charles/archive/2009/01/21/252212.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/charles/comments/commentRss/252212.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/charles/services/trackbacks/252212.html</trackback:ping><description><![CDATA[<p> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td valign="top"> <table cellspacing="0" cellpadding="3" width="100%" border="0"> <tbody> <tr> <td class="text"> <p><font face="Arial, Helvetica" size="+1"><b>The Essence of OOP using Java, Static Initializer Blocks</b></font><br>By <a href="http://www.developer.com/feedback.php/http://www.developer.com/java/other/article.php/2238491">Richard G. Baldwin</a><br> <p><font face="Verdana, Arial, Helvetica" size="-1"> <p>Java Programming Notes # 1632 </p> <ul> <li><a href="http://www.developer.com/#Preface">Preface</a>  <li><a href="http://www.developer.com/#Preview">Preview</a>  <li><a href="http://www.developer.com/#Discussion%20and%20Sample%20Programs">Discussion and Sample Code</a>  <li><a href="http://www.developer.com/#Run_the_Program">Run the Program<br></a> <li><a href="http://www.developer.com/#Summary">Summary</a>  <li><a href="http://www.developer.com/#What%27s%20next">What's Next</a>  <li><a href="http://www.developer.com/#Complete_program_listing">Complete Program Listing</a><br></li></ul> <hr align="center" width="100%" size="3">  <center> <h2><a name="Preface"></a>Preface</h2></center> <p>This series of lessons is designed to teach you about the essence of Object-Oriented Programming <i>(OOP) </i>using Java. </p> <p>The first lesson in the series was entitled <a href="http://softwaredev.earthweb.com/java/article/0,,12082_935351,00.html">The Essence of OOP Using Java, Objects, and Encapsulation</a>.&nbsp; The previous lesson was entitled <a href="http://www.developer.com/java/article.php/1455891">The Essence of OOP Using Java, Exception Handling</a>.</p> <p>You may find it useful to open another copy of this lesson in a separate browser window.&nbsp; That will make it easier for you to scroll back and forth among the different figures and listings while you are reading about them. </p> <p>For further reading, see my extensive collection of online Java tutorials at <a href="http://softwaredev.earthweb.com/java">Gamelan.com</a>. A consolidated index is available at <font color="#000000"><a href="http://www.DickBaldwin.com">www.DickBaldwin.com</a>.</font> </p> <center> <h2><a name="Preview"></a><font color="#000000">Preview</font></h2></center> <p><font color="#ff0000"><b>Proper initialization is important</b></font><br></p> <p>Proper initialization of variables is an important aspect of programming.&nbsp;&nbsp; Unlike other programming languages, it is not possible to write a Java program in which the variables are initialized with the garbage left over in memory from the programs that previously ran in the computer.<br></p> <p><font color="#ff0000"><b>Automatic initialization to default values</b></font><br></p> <p>Instance and class <i>(static)</i> variables are automatically initialized to standard default values if you fail to purposely initialize them.&nbsp; Although local variables are not automatically initialized, you cannot compile a program that fails to either initialize a local variable or assign a value to that local variable before it is used.<br></p> <p>Thus, Java programmers are prevented from committing the cardinal sin of allowing their variables to be initialized with random garbage.<br></p> <p><font color="#ff0000"><b>Initialization during declaration</b></font><br></p> <p>You should already know that you can initialize instance variables and class variables when you declare them, by including an initialization expression in the variable declaration statement.&nbsp; Figure 1 shows an example of a primitive class variable named <b>var1</b> that is purposely initialized to the value 6, along with a reference variable of type <b>Date</b> that is allowed to be automatically initialized to the default value null.<br></p> <table cols="1" width="400" bgcolor="#ccffff" border="1"> <tbody> <tr> <td><pre>  static int var1 = 6;<br><br>  Date date;</pre><pre><b>Figure 1</b></pre></td></tr></tbody></table>
<p><font color="#ff0000"><b>Constructor</b></font><br></p>
<p>What if your initialization requirements are more complex than can be satisfied with a single initialization expression?&nbsp; You should already know that you can write a constructor to purposely initialize all instance variables when an object is instantiated from the class.&nbsp; The code in a constructor can be as complex as you need it to be.<br></p>
<p><font color="#ff0000"><b>Static initializer blocks</b></font><br></p>
<p>What you may not know, however, is that you can also write <i>static initializer blocks</i> to initialize static variables when the class is loaded.&nbsp; The code in a static initializer block can also be quite complex.<br></p>
<p>A static initializer block resembles a method with no name, no arguments, and no return type.&nbsp; It doesn't need a name, because there is no need to refer to it from outside the class definition.&nbsp; The code in a static initializer block is executed by the virtual machine when the class is loaded.<br></p>
<p>Like a constructor, a static initializer block cannot contain a return statement.&nbsp; Therefore, it doesn't need to specify a return type.<br></p>
<p>Because it is executed automatically when the class is loaded, parameters don't make any sense, so a static initializer block doesn't have an argument list.<br></p>
<p><font color="#ff0000"><b>Syntax</b></font><br></p>
<p>So, what does this leave as the syntax of a static initializer block?&nbsp;&nbsp; All that is left is the keyword <b>static</b> and a pair of matching curly braces containing the code that is to be executed when the class is loaded.<br></p>
<p><font color="#ff0000"><b>Multiple static initializer blocks</b></font><br></p>
<p>You may include any number of static initializer blocks in your class definition, and they can be separated by other code such as method definitions and constructors.&nbsp; The static initializer blocks will be executed in the order in which they appear in the code.<br></p>
<p>According to one of my favorite authors, David Flanagan, author of <u>Java in a Nutshell</u>, <br></p>
<blockquote>
<p><i>"What the compiler actually does is to internally produce a single class initialization routine that combines all the static variable initializers and all of the static initializer blocks of code, in the order that they appear in the class declaration.&nbsp; This single initialization procedure is run automatically, one time only, when the class is first loaded."</i><br></p></blockquote>
<p></p>
<p>The sample program that I will discuss in the next section will illustrate many aspects of static initializer blocks.</p>
<blockquote></blockquote>
<center>
<h2><a name="Discussion and Sample Programs"></a><font color="#000000">Discussion and Sample Code</font></h2></center>
<p>In this lesson, I will discuss and explain a Java program named <b>Init01</b>, which illustrates static initializer blocks.&nbsp;&nbsp; As is often the case, I will discuss the program in fragments.&nbsp; A complete listing of the program can be viewed in Listing 14 near the end of the lesson.<br></p>
<p>This program illustrates the use of static initializer blocks to execute initialization code that is too complex to be contained in a simple variable initializer expression.<br></p>
<p>The code demonstrates that multiple initializer blocks are allowed, and that other code can be inserted between the static initializer blocks.<br></p>
<p>Finally, the code demonstrates that static initializer blocks are combined in the order in which they appear in the class definition, and executed once only.<br></p>
<p>The program was tested using SDK 1.4.1 under WinXP.<br></p>
<p><font color="#ff0000"><b>Order of execution</b></font><br></p>
<p>Because the compiler combines multiple static initializer blocks into a single initialization procedure, the order of execution of program code will not necessarily be the same as the order in which the code appears in the program.&nbsp; <i>(Multiple static initializer blocks can be separated by non-static code.)</i>&nbsp; This causes the program to be a little difficult to explain.&nbsp;&nbsp; In some cases, I will present the code in a more meaningful order than the order in which it appears in the program.&nbsp; Time tags are displayed at various points in the program to help make sense of the order of execution.<br></p>
<p>Because the time tags are based on the system clock, the output produced by the program will be different each time the program is run <i>(at least the times will be different).</i>&nbsp; I will show you the output produce by a single running of the program.&nbsp; <i>(Note that in some cases, I manually inserted line breaks in the program output to force the material to fit in this narrow publication format.)</i><br></p>
<p><font color="#ff0000"><b>The main method</b></font><br></p>
<p>The code in Listing 1 shows the beginning of the controlling class and the beginning of the <b>main</b> method.&nbsp; The code in the <b>main</b> method in Listing 1 displays the time that the program starts running, which is also the time at which the class named <b>A</b> is caused to start loading <i>(as you will see in Listing 2).</i></p>
<table cols="1" width="400" bgcolor="#ffff00" border="1">
<tbody>
<tr>
<td><pre>public class Init01{<br>  public static void <b>main</b>(String[] args){<br>    System.out.println("Start load: " + <br>                                     <b>new Date()</b>);<br><b><font face="Courier New,Courier">Listing 1</font></b></pre></td></tr></tbody></table>
<p><font color="#000000">Figure 2 shows the time at which the class named <b>A</b> started loading.</font><br></p>
<table cols="1" width="400" bgcolor="#ccffff" border="1">
<tbody>
<tr>
<td><pre>Start load: Fri May 30 15:28:49 CDT 2003</pre><pre><b>Figure 2</b></pre></td></tr></tbody></table>
<p><font color="#ff0000"><b>Load class A</b></font><br></p>
<p>The code in Listing 2 causes the class named <b>A</b> to start loading.&nbsp;&nbsp; Flanagan refers to the term <i><b>A.class</b></i> in Listing 2 as a class literal.&nbsp; <i>(I will show you an alternative way to accomplish the same thing using a method call in Listing 3).</i><br></p>
<table cols="1" width="400" bgcolor="#ffff00" border="1">
<tbody>
<tr>
<td><pre>    Class aClass = A.class;<br><br><b><font face="Courier New,Courier">Listing 2</font></b></pre></td></tr></tbody></table><font color="#000000"><br><font color="#ff0000"><b>What does it mean to say that a class is loaded?</b></font><br><br>While I can't provide a description of exactly what happens from a technical viewpoint, I can tell you what seems to happen from a functional viewpoint.<br><br>Functionally, an object of the class whose name is <b>Class</b> is created and saved in memory.&nbsp; This object represents the class that is being loaded <i>(in this case, the class named <b>A</b>).</i>&nbsp; From that point forward, all static members of the class are available to the program by referring to the name of the class and the name of the member.&nbsp; Other information about the class is also available by invoking methods on a reference to the <b>Class</b> object.<br></font>
<blockquote><font color="#000000"><i>(The code in Listing 2 causes the <b>Class</b> object's reference to be saved in the reference variable named <b>aClass</b>.&nbsp;&nbsp; However, the reference isn't used for any purpose elsewhere in the program.&nbsp; The purpose of the statement in Listing 2 is to cause the class to load, and is not to get and save a reference to the <b>Class </b>object.)</i></font><br></blockquote><font color="#000000"><font color="#ff0000"><b>An alternative approach</b></font><br><br>The comments in Listing 3 show an alternative way to force the class named <b>A</b> to be loaded, and to cause the <b>Class</b> object's reference to be saved in a reference variable named <b>aClass</b>.&nbsp;&nbsp; If activated, this code would cause the class to be loaded by invoking the static <b>forName</b> method of the class named <b>Class</b>, passing the name of the class to be loaded to the method as a <b>String</b> parameter.<br></font>
<p></p>
<table cols="1" width="400" bgcolor="#ffff00" border="1">
<tbody>
<tr>
<td><pre>    //try{<br>      //Class aClass = <b>Class.forName("A")</b>;  <br>    //}catch(ClassNotFoundException e){<br>    // 	                    e.printStackTrace();}<br><b><font face="Courier New,Courier">Listing 3</font></b></pre></td></tr></tbody></table><font color="#000000"><br>If you find the code <i>(shown as comments)</i> in Listing 3 to be confusing, just ignore it.&nbsp; Understanding that code isn't critical to understanding static initializer blocks.<br><br><font color="#ff0000"><b>Display end of load time</b></font><br><br>Continuing in the <b>main </b>method, the code in Listing 4 causes the time to be displayed when the loading of the class named <b>A</b> is complete.<br></font>
<p></p>
<table cols="1" width="400" bgcolor="#ffff00" border="1">
<tbody>
<tr>
<td><pre>    System.out.println("End load: " + <br>                                     new Date());<br>    <br><b><font face="Courier New,Courier">Listing 4</font></b></pre></td></tr></tbody></table><font color="#000000"><br>The code in Listing 4 causes the date and time shown in Figure 3 to be displayed on the screen.<br></font>
<p></p>
<table cols="1" width="400" bgcolor="#ccffff" border="1">
<tbody>
<tr>
<td><pre>End load: Fri May 30 15:28:54 CDT 2003</pre><pre><b>Figure 3</b></pre></td></tr></tbody></table>
<blockquote><font color="#000000"><i>(You can view all of the screen output in the order in which it appears in the comments in Listing 14 near the end of the lesson.)</i></font><br></blockquote><font color="#ff0000"><b>Five seconds have elapsed</b></font><br><br>If you compare the time shown in Figure 3 with the time shown in Figure 2, you will note that five seconds have elapsed during the time that the class was being loaded.&nbsp; The reason for this will become obvious as we examine the static initializer blocks in the definition of class <b>A</b>.<br><br><font color="#ff0000"><b>Now discuss the class named A</b></font><br><br>At this point, I am going to defer the remaining discussion of the <b>main </b>method until later and discuss the static initialization defined in the class named <b>A</b>.<br><br>Listing 5 shows an abbreviated listing of the class named <b>A</b>, showing only the code involved in the static initialization of the class when it is loaded.&nbsp; A complete listing of the class definition is shown in Listing 14 near the end of the lesson.<br>
<p></p>
<table cols="1" width="400" bgcolor="#ffff00" border="1">
<tbody>
<tr>
<td><pre>class A{<br>  //Declare six static variables<br>  static int var1 = 6;<br>  static int var2 = 9;<br>  static int var3;//originally initialized to 0<br>  static long var4;//originally initialized to 0<br><br>  static Date date1;//initialized to null<br>  static Date date2;//initialized to null<br><br>  //instance var declaration omitted for brevity<br><br>  static{<br>    //First static initializer block<br>    date1 = new Date();<br>    for(int cnt = 0; cnt &lt; var2; cnt++){<br>      var3 += var1;<br>    }//end for loop<br>    System.out.println("End first static init: "<br>                                   + new Date());<br>  }//end first static initializer block<br><br>  //Constructor and instance method omitted<br>  // for brevity.<br><br>  static{<br>    //Second static initializer block<br>    try{<br>      //Sleep for five seconds<br>      Thread.currentThread().sleep(5000);<br>    }catch(Exception e){System.out.println(e);}<br>    date2 = new Date();<br>    var4 = date2.getTime() - date1.getTime();<br>    System.out.println("End second static init: "<br>                                   + new Date());<br>  }//end second static initializer block<br>}//end class A<br><br><b><font face="Courier New,Courier">Listing 5</font></b></pre></td></tr></tbody></table><font color="#000000"><br><font color="#ff0000"><b>Six ordinary static variables</b></font><br><br>A careful examination of Listing 5 shows that it includes the declaration of six ordinary static variables with two of them being initialized to integer values when they are declared.&nbsp; The remaining four are automatically initialized to their default values when they are declared.<br><br><font color="#ff0000"><b>Two static initializer blocks</b></font><br><br>Listing 5 also shows two static initializer blocks, physically separated by the class constructor and an instance method.&nbsp; According to Flanagan, the code in Listing 5 is all combined by the compiler into a single static initialization procedure, which is executed one time only when the class is loaded.<br><br>I will separate the code in Listing 5 into several fragments and discuss those fragments in the paragraphs that follow.<br><br><font color="#ff0000"><b>Ordinary static variables</b></font><br><br>Listing 6 shows the declaration of six ordinary static variables, and the purposeful initialization of two of them.&nbsp; The remaining four are automatically initialized to their default values, but code in the static initializer blocks to follow will also provide non-default initial values for some of them.</font> 
<p></p>
<table cols="1" width="400" bgcolor="#ffff00" border="1">
<tbody>
<tr>
<td><pre>class A{<br>  static int var1 = 6;<br>  static int var2 = 9;<br>  static int var3;//initialized to 0<br>  static long var4;//initialized to 0<br><br>  static Date date1;//initialized to null<br>  static Date date2;//initialized to null<br><br><b><font face="Courier New,Courier">Listing 6</font></b></pre></td></tr></tbody></table><font color="#000000"><br><font color="#ff0000"><b>First static initializer block</b></font><br><br>Listing 7 shows the first static initializer block.<i></i></font> 
<p></p>
<table cols="1" width="400" bgcolor="#ffff00" border="1">
<tbody>
<tr>
<td><pre>  static{<br>    date1 = new Date();<br><br>    for(int cnt = 0; cnt &lt; var2; cnt++){<br>      var3 += var1;<br>    }//end for loop<br><br>    System.out.println("End first static init: "<br>                                   + new Date());<br>  }//end first static initializer block<br><br><b><font face="Courier New,Courier">Listing 7</font></b></pre></td></tr></tbody></table><font color="#000000"><br></font><font color="#000000">The code in this static initializer block records the date and time in one of the static variables, <b>date1</b>, declared earlier.<br><br>Then it executes a <b>for</b> loop to compute an initial value for one of the other static variables, <b>var3</b>, also declared earlier.&nbsp; Note that the computation of the initial value for <b>var3</b> is based on the initial value given to <b>var1</b> when it was declared.<br></font>
<blockquote><font color="#000000"><i>(The contents of date1 and var3 will be displayed later.)</i></font></blockquote><font color="#000000"><font color="#ff0000"><b>Complex code</b></font><br><br>Note also that the code in this initializer block is far too complex to be included in a simple initialization expression when a static variable is declared.&nbsp; Thus, this initializer block demonstrates the primary purpose of static initializer blocks - to execute code that cannot be included in an initialization expression that is part of a static variable declaration.</font><br><br><font color="#ff0000"><b>Display the date and time</b></font><br><font color="#000000"><br>Finally, the code in Listing 7 displays the date and time that the code in the initializer block completes execution.&nbsp; This date and time is shown in Figure 4.<br></font>
<p></p>
<table cols="1" width="400" bgcolor="#ccffff" border="1">
<tbody>
<tr>
<td><pre>End first static init: <br>                     Fri May 30 15:28:49 CDT 2003</pre><pre><b>Figure 4</b></pre></td></tr></tbody></table><br>If you compare the time in Figure 4 with the time in Figure 2, you will see that the two times are indistinguishable.&nbsp; In other words, the time required to execute the code in the first static initializer block was so short that the granularity of the time-display mechanism was too large to show actual the time difference.<br><br><font color="#ff0000"><b>Second static initializer block</b></font><br><br><font color="#000000">If you refer back to Listing 5, or refer to Listing 14 near the end of the lesson, you will see that the static initializer block shown in Listing 7 is followed by the definition of the class constructor and an instance method of the class.&nbsp; This is followed by a second static initializer block, as shown in Listing 8.</font> 
<p></p>
<table cols="1" width="400" bgcolor="#ffff00" border="1">
<tbody>
<tr>
<td><pre>  static{<br>    try{<br>      Thread.currentThread().sleep(5000);<br>    }catch(Exception e){System.out.println(e);}<br><br>    date2 = new Date();<br>    var4 = date2.getTime() - date1.getTime();<br><br>    System.out.println("End second static init: "<br>                                   + new Date());<br>  }//end second static initializer block<br>}//end class A<br><br><b><font face="Courier New,Courier">Listing 8</font></b></pre></td></tr></tbody></table><font color="#000000"><br>The code in the second static initializer block purposely inserts a five-second time delay by putting the thread to sleep for five seconds.</font><font color="#000000">&nbsp; The static variable named <b>date2</b> is then initialized with a reference to a new <b>Date</b> object, which reflects the date and time following the five-second delay.</font><font color="#000000"><br><br><font color="#ff0000"><b>The time difference</b></font><br></font><font color="#000000"><br>Following this, the code in Listing 8 computes a new initial value for the previously declared static variable named <b>var4</b> based on values saved during previous initialization operations.</font><br><font color="#000000"><br>The variable named <b>var4</b> is initialized with a value that represents the time difference in milliseconds between the time recorded during the execution of the first static initializer block, and the time following the five-second delay in the second initializer block.&nbsp; Later, when the values stored in the static variables are displayed, we will see that the time difference was 5008 milliseconds.<br><br><font color="#ff0000"><b>Display the date and time</b></font><br><br>Finally, the code in the second initializer block shown in Listing 8 displays the date and time that the execution of the second initializer block is complete.&nbsp;&nbsp; This date and time is shown in Figure 5.<br></font>
<p></p>
<table cols="1" width="400" bgcolor="#ccffff" border="1">
<tbody>
<tr>
<td><pre>End second static init: <br>                     Fri May 30 15:28:54 CDT 2003<br></pre><pre><b>Figure 5</b></pre></td></tr></tbody></table><br>As you should expect, this date and time matches the date and time displayed by the <b>main</b> method in Figure 3 as the time that the loading operation for the class named <b>A</b> was completed.<br><br><font color="#ff0000"><b>Now back to the main method</b></font><br><br>After the class named <b>A</b> is loaded, the main method purposely causes the main thread to sleep for five seconds as shown in Listing 9.<br>
<p></p>
<table cols="1" width="400" bgcolor="#ffff00" border="1">
<tbody>
<tr>
<td><pre>//Back in discussion of the main method<br>    try{<br>      Thread.currentThread().sleep(5000);<br>    }catch(Exception e){System.out.println(e);}<br><br><b><font face="Courier New,Courier">Listing 9</font></b></pre></td></tr></tbody></table><font color="#000000"><br><font color="#ff0000"><b>A new object of the class named A</b></font><br><br>After the main thread has been allowed to sleep for five seconds, the code in the main method instantiates a new object of the class named <b>A</b> and invokes the <b>showData</b> method on that object.&nbsp; This code, which is shown in Listing 10, causes the values previously stored in the static variables to be displayed.<br></font>
<p></p>
<table cols="1" width="400" bgcolor="#ffff00" border="1">
<tbody>
<tr>
<td><pre>    new A().showData();<br>  }//end main<br>}//end class Init01<br><br><b><font face="Courier New,Courier">Listing 10</font></b></pre></td></tr></tbody></table><font color="#000000"><br>When the <b>showData</b> method returns, the program terminates.<b><br><br><font color="#ff0000">Resume discussion of class A definition</font></b><br><br>Some of the code that I skipped in my earlier discussion of the definition of class <b>A</b> was the declaration of an instance variable named <b>date3</b>, as shown in Listing 11.</font> 
<p></p>
<table cols="1" width="400" bgcolor="#ffff00" border="1">
<tbody>
<tr>
<td><pre>//Resume discussion of class A<br><br>  Date date3;<br><br><b><font face="Courier New,Courier">Listing 11</font></b></pre></td></tr></tbody></table><font color="#000000"><br><font color="#ff0000"><b>When does the initialization occur?</b></font><br><br>It is very important to understand that the initialization of static variables occurs when the class is originally loaded, while the initialization of instance variables occurs when an object of the class is instantiated.&nbsp; That characteristic of OOP is demonstrated in this program.<br><br><font color="#ff0000"><b>Record date and time of object instantiation</b></font><br><br></font><font color="#000000">The class constructor, shown in Listing 12, causes t</font><font color="#000000">he instance variable named <b>date3</b>, declared in Listing 11, to contain the date and time that the object is actually instantiated.<br></font>
<p></p>
<table cols="1" width="400" bgcolor="#ffff00" border="1">
<tbody>
<tr>
<td><pre>  A(){//constructor<br>    //Record the time in an instance variable.<br>    date3 = new Date();<br>  }//end constructor<br><br><b><font face="Courier New,Courier">Listing 12</font></b></pre></td></tr></tbody></table><font color="#000000"><br><font color="#ff0000"><b>Display data in the variables</b></font><br><br>The method named <b>showData</b> is shown in Listing 13.&nbsp; The code in this method displays the times that the variables were initialized, along with the values stored in those variables.<br></font>
<p></p>
<table cols="1" width="400" bgcolor="#ffff00" border="1">
<tbody>
<tr>
<td><pre>  void showData(){//an instance method<br>    System.out.println("var3 initialized: "<br>                                        + date1);<br>    System.out.println("var3 = " + var3);<br>    System.out.println("var4 initialized: "<br>                                        + date2);<br>    System.out.println("var4 = " + var4<br>                                      + " msec");<br>    System.out.println("Obj instantiated: "<br>                                        + date3);<br>  }//end showData<br><br><b><font face="Courier New,Courier">Listing 13</font></b></pre></td></tr></tbody></table><font color="#000000"><br><font color="#ff0000"><b>The output produced by the showData method</b></font><br><br>The <b>showData</b> method is invoked as soon as the new object is instantiated by the code in the <b>main</b> method, producing the screen output shown in Figure 6.<br></font>
<p></p>
<table cols="1" width="400" bgcolor="#ccffff" border="1">
<tbody>
<tr>
<td><pre>var3 initialized: Fri May 30 15:28:49 CDT 2003<br>var3 = 54<br>var4 initialized: Fri May 30 15:28:54 CDT 2003<br>var4 = 5008 msec<br>Obj instantiated: Fri May 30 15:28:59 CDT 2003</pre><pre><b>Figure 6</b></pre></td></tr></tbody></table><br><font color="#ff0000"><b>Five-second intervals</b></font><br><br>First, you should note the five-second intervals that separate the two initialization operations and the object instantiation.<br><br>Recall that the first five-second delay, that separates the two static initialization operations, was caused when the second static initializer block put the thread that was loading the class to sleep for five seconds.<br><br>Recall also that the second five-second delay was caused by the <b>main</b> method, which put the main thread to sleep for five seconds after the class named <b>A</b> was loaded, and before an object of the class named <b>A</b>, was instantiated.<br><br><font color="#ff0000"><b>Result of the loop computation</b></font><br><br>Also note the value of 54 stored in the static variable named <b>var3</b>. Recall that this value was computed by a <b>for</b> loop in the first static initializer block.<br><br><font color="#ff0000"><b>The time difference in milliseconds</b></font><br><br>Finally, note the value of 5008 milliseconds stored in the static variable named <b>var4</b>.&nbsp; This value was computed by code in the second static initializer block, after sleeping for 5000 milliseconds.&nbsp; This value represents the time difference between the execution of the first static initializer block and the completion of the second static initializer block following a five-second delay.<br>
<h2 align="center"><a name="Run_the_Program"></a>Run the Program</h2>
<p>At this point, you may find it useful to compile and run the program shown in Listing 14 near the end of the lesson.<br></p>
<center>
<h2><a name="Summary"></a>Summary</h2></center>
<p><font color="#ff0000"><b>No random garbage allowed</b></font><br></p>
<p>Unlike other programming languages, it is not possible to write a Java program in which the variables are initialized with the random garbage left over in memory from the programs that previously ran in the computer.<br></p>
<p>Instance and static variables are automatically initialized to standard default values if you fail to purposely initialize them.<br></p>
<p>You cannot compile a program that fails to either initialize a local variable or assign a value to that local variable before it is used.<br></p>
<p><font color="#ff0000"><b>Different approaches to variable initialization</b></font><br></p>
<p>You can initialize instance variables and class variables when you declare them, by including an initialization expression in the variable declaration statement.<br></p>
<p>If your initialization needs are more complex than can be satisfied with a single initialization expression, you can write a constructor to purposely initialize all instance variables when the object is instantiated.&nbsp; The code in a constructor can be as complex as needed.<br></p>
<p>You can also write a <i>static initializer block</i> to initialize static variables when the class is loaded.&nbsp; The code in a static initializer block, which is executed by the virtual machine when the class is loaded, can also be quite complex.<br></p>
<p><font color="#ff0000"><b>Static initializer blocks</b></font><br></p>
<p>A static initializer block resembles a method with no name, no arguments, and no return type.&nbsp; When you remove these items from the syntax, all that is left is the keyword <b>static</b> and a pair of matching curly braces containing the code that is to be executed when the class is loaded.<br></p>
<p>You may include any number of static initializer blocks within your class definition.&nbsp; They can be separated by other code such as method definitions and constructors.&nbsp; The static initializer blocks will be executed in the order in which they appear in the code, regardless of the other code that may separate them.<br></p><font color="#ff0000"><b>Instance initializers</b></font><br><br>Although not covered in this lesson, it is also possible to define an instance initializer block, which can be used to initialize instance variables in the absence of, or in addition to a constructor.&nbsp; As you will learn in a future lesson on anonymous classes, it is not always possible to define a constructor for a class, but it is always possible to define an instance initializer block.<br>
<center>
<h2><a name="What's next"></a>What's Next?</h2></center>
<p>The next lesson will explain and discuss instance initializer blocks that can be used in the absence of, or in addition to class constructors. <br></p>
<h2 align="center"><a name="Complete_program_listing"></a>Complete Program Listing</h2>A complete listing of the program discussed in this lesson is show in Listing 14 below.<br>
<p></p>
<table cols="1" width="641" bgcolor="#ffff00" border="1">
<tbody>
<tr>
<td width="639"><pre>/*File Init01.java<br>Copyright 2003 R.G.Baldwin<br><br>Illustrates the use of static initializer blocks<br>to execute code that is too complex to be<br>contained in a simple variable initializer.<br><br>Demonstrates that static initializer blocks are<br>executed in the order in which they appear in the<br>class definition.<br><br>Demonstrates that other code can be inserted<br>between static initializer blocks in a class<br>definition.<br><br>The output will change each time this program is<br>run.  The output for one run is shown below. Line<br>breaks were manually inserted to force the<br>material to fit in this narrow publication <br>format.<br><br>Start load: Fri May 30 15:28:49 CDT 2003<br>End first static init: <br>                     Fri May 30 15:28:49 CDT 2003<br>End second static init: <br>                     Fri May 30 15:28:54 CDT 2003<br>End load: Fri May 30 15:28:54 CDT 2003<br>var3 initialized: Fri May 30 15:28:49 CDT 2003<br>var3 = 54<br>var4 initialized: Fri May 30 15:28:54 CDT 2003<br>var4 = 5008 msec<br>Obj instantiated: Fri May 30 15:28:59 CDT 2003<br><br>Note the five-second time intervals that separate<br>the two initializations and the object<br>instantiation in the above output.<br><br>Tested using SDK 1.4.1 under WinXP<br>************************************************/<br>import java.util.Date;<br><br>public class Init01{<br>  public static void main(String[] args){<br>  	//Display start load time<br>    System.out.println("Start load: " + <br>  	                             new Date());<br>    //Force the class named A to load using a<br>    // class literal.<br>    Class aClass = A.class;<br>    <br>    //Alternative way to cause the class named A<br>    // to load.<br>    //try{<br>      //Class aClass = Class.forName("A");  <br>    //}catch(ClassNotFoundException e){<br>    // 	                    e.printStackTrace();}<br>    //Display end load time<br>    System.out.println("End load: " + <br>                                     new Date());<br>    <br>    //Sleep for five seconds after the class<br>    // loads<br>    try{<br>      Thread.currentThread().sleep(5000);<br>    }catch(Exception e){System.out.println(e);}<br>    <br>    //Instantiate a new object of the class named<br>    // A and display the data stored in the<br>    // variables.<br>    new A().showData();<br>  }//end main<br>}//end class Init01<br>//=============================================//<br><br>class A{<br>  //Declare six static variables and initialize<br>  // some of them when they are declared.  The<br>  // others will be automatically initialized to<br>  // either zero or null, but this may change<br>  // later due to the code in static initializer<br>  // blocks.<br>  static int var1 = 6;<br>  static int var2 = 9;<br>  static int var3;//originally initialized to 0<br>  static long var4;//originally initialized to 0<br><br>  static Date date1;//initialized to null<br>  static Date date2;//initialized to null<br><br>  //Declare an instance variable which is<br>  // originally initialized to null.<br>  Date date3;<br><br>  static{<br>    //First static initializer block records the<br>    // time and then executes a loop to<br>    // compute a new initial value for var3.<br>    date1 = new Date();<br>    for(int cnt = 0; cnt &lt; var2; cnt++){<br>      var3 += var1;<br>    }//end for loop<br>    System.out.println("End first static init: "<br>                                   + new Date());<br>  }//end first static initializer block<br>  //-------------------------------------------//<br><br>  //Note that the constructor and an instance<br>  // method physically separate the two static<br>  // initializer blocks.<br>  A(){//constructor<br>    //Record the time in an instance variable.<br>    date3 = new Date();<br>  }//end constructor<br>  //-------------------------------------------//<br><br>  void showData(){//an instance method<br>    //Display the times that the variables were<br>    // initialized along with the values stored<br>    // in those variables.<br>    System.out.println("var3 initialized: "<br>                                        + date1);<br>    System.out.println("var3 = " + var3);<br>    System.out.println("var4 initialized: "<br>                                        + date2);<br>    System.out.println("var4 = " + var4<br>                                      + " msec");<br>    System.out.println("Obj instantiated: "<br>                                        + date3);<br>  }//end showData<br>  //-------------------------------------------//<br><br>  static{<br>    //Second static initializer block sleeps for<br>    // five seconds, records the time, and then<br>    // computes a new intial value for var4 based<br>    // on values recorded during previous<br>    // initialization operations.<br>    try{<br>      //Sleep for five seconds<br>      Thread.currentThread().sleep(5000);<br>    }catch(Exception e){System.out.println(e);}<br>    date2 = new Date();<br>    var4 = date2.getTime() - date1.getTime();<br>    System.out.println("End second static init: "<br>                                   + new Date());<br>  }//end second static initializer block<br>}//end class A<br><br><b><font face="Courier New,Courier">Listing 14</font></b></pre></td></tr></tbody></table>
<hr align="center" width="100%" size="3">

<p>Copyright 2003, Richard G. Baldwin.&nbsp; Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited. </p>
<h4><a name="About the author"></a>About the author</h4>
<p><b><a href="mailto:baldwin@DickBaldwin.com">Richard Baldwin</a></b><i> is a college professor (at Austin Community College in Austin, Texas) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.</i> </p>
<p><i>Richard has participated in numerous consulting projects, and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas.&nbsp; He is the author of Baldwin's Programming <a href="http://www.DickBaldwin.com">Tutorials</a>, which has gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.</i> </p>
<p><i>Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.</i> </p>
<p><i><a href="mailto:baldwin@DickBaldwin.com">baldwin@DickBaldwin.com</a></i> </p>
<p>-end- </p></font></td></tr></tbody></table></td></tr></tbody></table></p><img src ="http://www.blogjava.net/charles/aggbug/252212.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/charles/" target="_blank">suprasoft Inc,.</a> 2009-01-21 14:43 <a href="http://www.blogjava.net/charles/archive/2009/01/21/252212.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>