<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>Vertex</title>
    <description></description>
    <link>http://vertex.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>JRuby安装手册</title>
        <author>simon_zhang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://vertex.javaeye.com">simon_zhang</a>&nbsp;
          链接：<a href="http://vertex.javaeye.com/blog/188271" style="color:red;">http://vertex.javaeye.com/blog/188271</a>&nbsp;
          发表时间: 2008年04月30日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          JRuby安装手册<br /><br />安装手册是自己总结出来的，希望对大家有帮助<br />1.下载JRuby-1.0<br />2.解压到/usr/local/jruby-1.0<br />3.vi /etc/profile 插入<br />  ---<br />  export JRUBY_HOME=/usr/local/jruby-1.0<br />  export PATH=/usr/local/jruby-1.0/bin:$PATH<br />  source /etc/profile<br />  ----------<br />4.安装rails:jruby --command gem install rails -r -y --no-ri --no-rdoc<br />  如果没有响应，使用:jruby --command gem install rails，一步一步来。<br />  ---<br />  jruby --command gem install rails<br />  Install required dependency activesupport? [Yn]  y<br />  Install required dependency activerecord? [Yn]  y<br />  Install required dependency actionpack? [Yn]  y<br />  Install required dependency actionmailer? [Yn]  y<br />  Install required dependency actionwebservice? [Yn]  y<br />  Successfully installed rails-1.2.3<br />  -----------<br />5.安装rails数据库接口ActiveRecord-JDBC:<br />  ----<br />  jruby --command gem install ActiveRecord-JDBC<br />  Successfully installed ActiveRecord-JDBC-0.4<br />  Installing ri documentation for ActiveRecord-JDBC-0.4...<br />  Installing RDoc documentation for ActiveRecord-JDBC-0.4...<br />  ---------<br />6.将JRUBY_HOME/bin下面的gem,rake,rails等文件改名为jgem,jrake,jrails防止命令冲突<br />7.安装JAVA语言MySQL数据库接口软件包<br />  其实这里只要把mysql-connector-java-3.1.14-bin.jar代码包放到JRUBY_HOME/lib下就好了<br />  ---<br />  export CLASSPATH=$JRUBY_HOME/lib/mysql-connector-java-3.1.14-bin.jar:$CLASSPATH<br />  source /etc/profile<br />  --------<br />8.修改rails项目中的数据库配置文件 database.yml<br />  ---<br />  adapter: jdbc<br />  driver: com.mysql.jdbc.Driver<br />  url: jdbc:mysql://127.0.0.1:3306/wap #wap为数据库名<br />  username: root<br />  password:<br />  -------<br />9.修改rails config目录下的environment.rb，插入<br />  ---<br />  RAILS_CONNECTION_ADAPTERS = 'jdbc'<br />  --------<br />10.大功告成！<br />  ---<br />  http://127.0.0.1:3000/<br />  ---------<br />  出现rails欢迎界面！<br /><br />windows下安装的步骤也是一样的，在环境变量里修改CLASSPATH和PATH<br />下面是环境变量的配置：<br />------linux------<br />export JAVA_HOME=/usr/java/j2sdk1.4.2_07<br />export JRUBY_HOME=/usr/local/jruby-1.0<br />export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRUBY_HOME/lib/mysql-connector-java-3.1.14-bin.jar<br />export PATH=$PATH:$JAVA_HOME/bin<br />export PATH=/usr/local/ruby/bin:$PATH<br />export PATH=/usr/local/jruby-1.0/bin:$PATH<br />-------------<br />-----windows--------<br />JAVA_HOME=c:\j2sdk1.4.2_07<br />JRUBY_HOME=c:\jruby-1.0<br />CLASSPATH=.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;%JRUBY_HOME%\lib\mysql-connector-java-3.1.14-bin.jar<br />PATH=%JAVA_HOME%\bin;%RUBY_HOME%\bin;%JRUBY_HOME%\bin<br />----------------<br /><br /><br />经过实践，环境变量中只需要配置PATH，加入JAVA_HOME即可运行，以上环境变量配置是本人的配置。
          <br/>
          <span style="color:red;">
            <a href="http://vertex.javaeye.com/blog/188271#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 30 Apr 2008 09:14:56 +0800</pubDate>
        <link>http://vertex.javaeye.com/blog/188271</link>
        <guid>http://vertex.javaeye.com/blog/188271</guid>
      </item>
      <item>
        <title> Unable to find a value ...using operator &quot;.&quot;</title>
        <author>simon_zhang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://vertex.javaeye.com">simon_zhang</a>&nbsp;
          链接：<a href="http://vertex.javaeye.com/blog/187885" style="color:red;">http://vertex.javaeye.com/blog/187885</a>&nbsp;
          发表时间: 2008年04月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <pre name="code" class="提示错误：">javax.servlet.jsp.el.ELException: Unable to find a value 
for "Name" in object of class "com.cabletech.plan.util.TreeNode" using operator "."</pre><br /><br />javaBean 中的变量首字母不能大写，要遵守java编码规范。<br /><pre name="code" class="java">public class TreeNode {
	private String id;
	private String name;
	....
}</pre><br />不能写成<br /><pre name="code" class="java">public class TreeNode {
	private String ID;
	private String Name;
	....
}</pre><br />这样之后就有可能找不到哦！
          <br/>
          <span style="color:red;">
            <a href="http://vertex.javaeye.com/blog/187885#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 29 Apr 2008 09:27:40 +0800</pubDate>
        <link>http://vertex.javaeye.com/blog/187885</link>
        <guid>http://vertex.javaeye.com/blog/187885</guid>
      </item>
      <item>
        <title>Connection reset by peer: socket write error</title>
        <author>simon_zhang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://vertex.javaeye.com">simon_zhang</a>&nbsp;
          链接：<a href="http://vertex.javaeye.com/blog/187884" style="color:red;">http://vertex.javaeye.com/blog/187884</a>&nbsp;
          发表时间: 2008年04月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          在Ajax请求时提示java.net.SocketException: Connection reset by peer: socket write error错误而且不能返回数据信息<br /><pre name="code" class="java">ClientAbortException:  java.net.SocketException: Connection reset by peer: socket write error
	at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:366)
	at org.apache.tomcat.util.buf.ByteChunk.append(ByteChunk.java:352)
	at org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:392)
	at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:381)
	at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:89)
	at org.apache.catalina.servlets.DefaultServlet.copy(DefaultServlet.java:1784)
	at org.apache.catalina.servlets.DefaultServlet.serveResource(DefaultServlet.java:874)
	at org.apache.catalina.servlets.DefaultServlet.doGet(DefaultServlet.java:376)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:690)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:269)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
	at com.cabletech.commons.web.SystemFilter.doFilter(SystemFilter.java:33)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:215)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:174)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:117)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:108)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:174)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:874)
	at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:665)
	at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:528)
	at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:81)
	at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:689)
	at java.lang.Thread.run(Thread.java:595)
Caused by: java.net.SocketException: Connection reset by peer: socket write error
	at java.net.SocketOutputStream.socketWrite0(Native Method)
	at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
	at java.net.SocketOutputStream.write(SocketOutputStream.java:136)
	at org.apache.coyote.http11.InternalOutputBuffer.realWriteBytes(InternalOutputBuffer.java:747)
	at org.apache.tomcat.util.buf.ByteChunk.flushBuffer(ByteChunk.java:432)
	at org.apache.tomcat.util.buf.ByteChunk.append(ByteChunk.java:347)
	at org.apache.coyote.http11.InternalOutputBuffer$OutputStreamOutputBuffer.doWrite(InternalOutputBuffer.java:770)
	at org.apache.coyote.http11.filters.IdentityOutputFilter.doWrite(IdentityOutputFilter.java:118)
	at org.apache.coyote.http11.InternalOutputBuffer.doWrite(InternalOutputBuffer.java:580)
	at org.apache.coyote.Response.doWrite(Response.java:560)
	at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:361)
	... 26 more
</pre><br />改前AJAX代码：<br /><pre name="code" class="java">&lt;script type="text/javascript">
	var xmlHttp;
    function createXMLHttpRequest() {
        if (window.ActiveXObject) {
           xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
        } 
        else if (window.XMLHttpRequest) {
           xmlHttp = new XMLHttpRequest();                
        }
    }
	function getTaskSubline() {
        createXMLHttpRequest();
        var linelevel = document.getElementById("linelevel").value;
        if(" "==linelevel){
        	return ;
        }
        var url="/WebApp/plan.do?method=getTaskSublinePoint&value="+linelevel;
        
        xmlHttp.open("get", url, false);
        xmlHttp.onreadystatechange = startCallback;
        xmlHttp.send(null);
    }
	function startCallback() {
        if (xmlHttp.readyState == 4) {
            var left = document.getElementById("task");
        	left.innerHTML = xmlHttp.responseText;
        }
    }
&lt;/script></pre><br />改后Ajax代码：<br /><pre name="code" class="java">&lt;script type="text/javascript">
	var xmlHttp;
    function createXMLHttpRequest() {
        if (window.ActiveXObject) {
           xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
        } 
        else if (window.XMLHttpRequest) {
           xmlHttp = new XMLHttpRequest();                
        }
    }
	function getTaskSubline() {
        createXMLHttpRequest();
        var linelevel = document.getElementById("linelevel").value;
        if(" "==linelevel){
        	return ;
        }
        var url="/WebApp/plan.do?method=getTaskSublinePoint&value="+linelevel;
        
        xmlHttp.open("post", url, false);
        xmlHttp.onreadystatechange = startCallback;
        xmlHttp.send(null);
    }
	function startCallback() {
		if (xmlHttp.readyState == 4) {
        	  if (xmlHttp.status == 200) { // 信息已经成功返回，开始处理信息
        	  	var htmltree = xmlHttp.responseText;
                var taskdiv = document.getElementById("taskdiv");
        		taskdiv.innerHTML = htmltree;
              } else { //页面不正常
                    alert("您所请求的页面有异常。");
              }
        }
    }
&lt;/script></pre><br />前后代码比较也没有什么不同的地方，但是竟然不提上Connection reset by peer: socket write error错误了。
          <br/>
          <span style="color:red;">
            <a href="http://vertex.javaeye.com/blog/187884#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 29 Apr 2008 09:25:36 +0800</pubDate>
        <link>http://vertex.javaeye.com/blog/187884</link>
        <guid>http://vertex.javaeye.com/blog/187884</guid>
      </item>
      <item>
        <title>connection proxy not usable after transaction comp</title>
        <author>simon_zhang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://vertex.javaeye.com">simon_zhang</a>&nbsp;
          链接：<a href="http://vertex.javaeye.com/blog/187880" style="color:red;">http://vertex.javaeye.com/blog/187880</a>&nbsp;
          发表时间: 2008年04月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <pre name="code" class="java">public class PlanDAOImpl extends GenericHibernateDAO&lt;Plan, String> implements
		PlanDAO {
	private Logger logger = Logger.getLogger("MonthPlanDAOImpl");
	private QueryUtil query;

	public PlanDAOImpl() {
		query = new QueryUtil();
	}

	public int findByTime(String value,String groupid) {
		String hql = "from Plan where endtime>to_date('"+value+"','yyyy/mm/dd') and begintime&lt;to_date('"+value+"','yyyy/mm/dd')";
		Query hquery = getSession().createQuery(hql);
		//hquery.setParameter("month", value);
		return hquery.list().size();
	}

	// 任务线段及点
	public List&lt;TreeNode> getLines(String linelevel, String patrolgroupid)
			throws Exception {
		String sql = "select sublineid id,sublinename name from sublineinfo s,lineinfo l where l.lineid = s.lineid and s.state !="+SysConstant.STATE_DEL+" and l.linelevel ='"
				+ linelevel + "' and s.patrolgroupid='" + patrolgroupid + "' ";
		logger.info("getModuleMenu:" + sql);
		//if (query == null)
		//query = new QueryUtil();
		List&lt;TreeNode> lines = new ArrayList&lt;TreeNode>();
		List&lt;BasicDynaBean> linesBean = query.queryBeans(sql);
		for (BasicDynaBean bean : linesBean) {
			TreeNode node = new TreeNode();
			String ID = (String) bean.get("id");
			String Name = (String) bean.get("name");
			node.setID(ID);
			node.setName(Name);
			node.setSon(getPoints(ID));// 获得线段上的点
			lines.add(node);
		}
		return lines;
	}

	// 获得线段上的点
	private List&lt;TreeNode> getPoints(String id) throws Exception {
		String sql = "select pointname name,pointid id from pointinfo where sublineid='"
				+ id + "' order by inumber";
		//query = new QueryUtil();
		List&lt;TreeNode> points = new ArrayList&lt;TreeNode>();
		List&lt;BasicDynaBean> beanPoints = query.queryBeans(sql);
		for (BasicDynaBean bean : beanPoints) {
			TreeNode node = new TreeNode();
			String ID = (String) bean.get("id");
			String Name = (String) bean.get("name");
			node.setID(ID);
			node.setName(Name);
			points.add(node);
		}
		return points;
	}</pre><br />注意QueryUtil对象创建的位置。<br /><pre name="code" class="java">public class PlanDAOImpl extends GenericHibernateDAO&lt;Plan, String> implements
		PlanDAO {
	private Logger logger = Logger.getLogger("MonthPlanDAOImpl");
	private QueryUtil query;

	public PlanDAOImpl() {
		
	}

	public int findByTime(String value,String groupid) {
		String hql = "from Plan where endtime>to_date('"+value+"','yyyy/mm/dd') and begintime&lt;to_date('"+value+"','yyyy/mm/dd')";
		Query hquery = getSession().createQuery(hql);
		//hquery.setParameter("month", value);
		return hquery.list().size();
	}

	// 任务线段及点
	public List&lt;TreeNode> getLines(String linelevel, String patrolgroupid)
			throws Exception {
		String sql = "select sublineid id,sublinename name from sublineinfo s,lineinfo l where l.lineid = s.lineid and s.state !="+SysConstant.STATE_DEL+" and l.linelevel ='"
				+ linelevel + "' and s.patrolgroupid='" + patrolgroupid + "' ";
		logger.info("getModuleMenu:" + sql);
		//if (query == null)
		query = new QueryUtil();
		List&lt;TreeNode> lines = new ArrayList&lt;TreeNode>();
		List&lt;BasicDynaBean> linesBean = query.queryBeans(sql);
		for (BasicDynaBean bean : linesBean) {
			TreeNode node = new TreeNode();
			String ID = (String) bean.get("id");
			String Name = (String) bean.get("name");
			node.setID(ID);
			node.setName(Name);
			node.setSon(getPoints(ID));// 获得线段上的点
			lines.add(node);
		}
		return lines;
	}

	// 获得线段上的点
	private List&lt;TreeNode> getPoints(String id) throws Exception {
		String sql = "select pointname name,pointid id from pointinfo where sublineid='"
				+ id + "' order by inumber";
		query = new QueryUtil();
		List&lt;TreeNode> points = new ArrayList&lt;TreeNode>();
		List&lt;BasicDynaBean> beanPoints = query.queryBeans(sql);
		for (BasicDynaBean bean : beanPoints) {
			TreeNode node = new TreeNode();
			String ID = (String) bean.get("id");
			String Name = (String) bean.get("name");
			node.setID(ID);
			node.setName(Name);
			points.add(node);
		}
		return points;
	}
</pre>
          <br/>
          <span style="color:red;">
            <a href="http://vertex.javaeye.com/blog/187880#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 29 Apr 2008 09:18:53 +0800</pubDate>
        <link>http://vertex.javaeye.com/blog/187880</link>
        <guid>http://vertex.javaeye.com/blog/187880</guid>
      </item>
      <item>
        <title>Linux 定时关机</title>
        <author>simon_zhang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://vertex.javaeye.com">simon_zhang</a>&nbsp;
          链接：<a href="http://vertex.javaeye.com/blog/187879" style="color:red;">http://vertex.javaeye.com/blog/187879</a>&nbsp;
          发表时间: 2008年04月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          以root身份进入系统:<br /><br /><br />1、先用vi编辑器建立一个文件,如guanji<br /><pre name="code" class="java">
#vi guanji</pre><br /><br />输入如下内容:<br /><br /><pre name="code" class="java">30 17 * * * halt -p</pre><br /><br />退出保存:wq!<br /><br />(以上例子是下午17点30分关机挂起)<br /><br /><br />2、执行crontab命令:<br /><br /><pre name="code" class="java">#crontab guanji</pre><br /><br />既可<br /><br /><br />3,要检查下自动执行定时任务的服务有没有开启,如没有,要开启<br /><br />----------------------------------------------------------<br />crontab命令使用<br />名称 : crontab<br />使用权限 : 所有使用者<br />使用方式 :<br />crontab [ -u user ] file<br />crontab [ -u user ] { -l | -r | -e }<br />说明 :<br />crontab 是用来让使用者在固定时间或固定间隔执行程序之用，换句话说，也就是类似使用者的时程表。-u user 是指设定指定 user 的时程表，这个前提是你必须要有其权限(比如说是 root)才能够指定他人的时程表。如果不使用 -u user 的话，就是表示设定自己的时程表。<br />参数 :<br />crontab -e : 执行文字编辑器来设定时程表，内定的文字编辑器是 VI，如果你想用别的文字编辑器，则请先设定 VISUAL 环境变数来指定使用那个文字编辑器(比如说 setenv VISUAL joe)<br />crontab -r : 删除目前的时程表<br />crontab -l : 列出目前的时程表<br />crontab file [-u user]-用指定的文件替代目前的crontab。<br />时程表的格式如下 :<br />f1 f2 f3 f4 f5 program<br />其中 f1 是表示分钟，f2 表示小时，f3 表示一个月份中的第几日，f4 表示月份，f5 表示一个星期中的第几天。program 表示要执行的程序。<br />当 f1 为 * 时表示每分钟都要执行 program，f2 为 * 时表示每小时都要执行程序，其馀类推<br />当 f1 为 a-b 时表示从第 a 分钟到第 b 分钟这段时间内要执行，f2 为 a-b 时表示从第 a 到第 b 小时都要执行，其馀类推<br />当 f1 为 */n 时表示每 n 分钟个时间间隔执行一次，f2 为 */n 表示每 n 小时个时间间隔执行一次，其馀类推<br />当 f1 为 a, b, c,... 时表示第 a, b, c,... 分钟要执行，f2 为 a, b, c,... 时表示第 a, b, c...个小时要执行，其馀类推<br />使用者也可以将所有的设定先存放在档案 file 中，用 crontab file 的方式来设定时程表。<br /><br />例子1 :<br />#每天早上7点执行一次 /bin/ls :<br />0 7 * * * /bin/ls<br />在 12 月内, 每天的早上 6 点到 12 点中，每隔3个小时执行一次 /usr/bin/backup :<br />0 6-12/3 * 12 * /usr/bin/backup<br />周一到周五每天下午 5:00 寄一封信给 alex@domain.name :<br />0 17 * * 1-5 mail -s "hi" alex@domain.name &lt; /tmp/maildata<br />每月每天的午夜 0 点 20 分, 2 点 20 分, 4 点 20 分....执行 echo "haha"<br />20 0-23/2 * * * echo "haha"<br />注意 :<br />当程序在你所指定的时间执行后，系统会寄一封信给你，显示该程序执行的内容，若是你不希望收到这样的信，请在每一行空一格之后加上 > /dev/null 2>&1 即可<br /><br />例子2 :<br />#每天早上6点10分<br />10 6 * * * date<br />#每两个小时<br />0 */2 * * * date<br />#晚上11点到早上8点之间每两个小时，早上8点<br />0 23-7/2，8 * * * date<br />#每个月的4号和每个礼拜的礼拜一到礼拜三的早上11点<br />0 11 4 * mon-wed date<br />#1月份日早上4点<br />0 4 1 jan * date <br />----------------------------------------------------------<br /><br />Linux关机命令详解<br />日期：2005-05-27   来源： LinuxAid.com.cn   作者： sword_martin<br /><br />    在linux下一些常用的关机/重启命令有shutdown、halt、reboot、及init，它们都可以达到重启系统的目的，但每个命令的内部工作过程是不同的，通过本文的介绍，希望你可以更加灵活的运用各种关机命令。<br /><br />1.shutdown<br />   shutdown命令安全地将系统关机。有些用户会使用直接断掉电源的方式来关闭linux，这是十分危险的。因为linux与windows不同，其后台运行着许多进程，所以强制关机可能会导致进程的数据丢失﹐使系统处于不稳定的状态﹐甚至在有的系统中会损坏硬件设备。 <br /><br />    而在系统关机前使用shutdown命令﹐系统管理员会通知所有登录的用户系统将要关闭。并且login指令会被冻结﹐即新的用户不能再登录。直接关机或者延迟一定的时间才关机都是可能的﹐还可能重启。这是由所有进程〔process〕都会收到系统所送达的信号〔signal〕决定的。这让像vi之类的程序有时间储存目前正在编辑的文档﹐而像处理邮件〔mail〕和新闻〔news〕的程序则可以正常地离开等等。<br /><br />    shutdown执行它的工作是送信号〔signal〕给init程序﹐要求它改变runlevel。Runlevel 0被用来停机〔halt〕﹐runlevel 6是用来重新激活〔reboot〕系统﹐而runlevel 1则是被用来让系统进入管理工作可以进行的状态﹔这是预设的﹐假定没有-h也没有-r参数给shutdown。要想了解在停机〔halt〕或者重新开机〔reboot〕过程中做了哪些动作﹐你可以在这个文件/etc/inittab里看到这些runlevels相关的资料。<br />　　 shutdown 参数说明:<br />　　 [-t] 在改变到其它runlevel之前﹐告诉init多久以后关机。<br />　　 [-r] 重启计算器。<br />　　 [-k] 并不真正关机﹐只是送警告信号给每位登录者〔login〕。<br />　　 [-h] 关机后关闭电源〔halt〕。<br />　　 [-n] 不用init﹐而是自己来关机。不鼓励使用这个选项﹐而且该选项所产生的后果往往不总是你所预期得到的。<br />　　 [-c] cancel current process取消目前正在执行的关机程序。所以这个选项当然没有时间参数﹐但是可以输入一个用来解释的讯息﹐而这信息将会送到每位使用者。<br />　　 [-f] 在重启计算器〔reboot〕时忽略fsck。 <br />       [-F] 在重启计算器〔reboot〕时强迫fsck。<br />　　 [-time] 设定关机〔shutdown〕前的时间。<br />　　 　　<br />2.halt----最简单的关机命令<br />　　 其实halt就是调用shutdown -h。halt执行时﹐杀死应用进程﹐执行sync系统调用﹐文件系统写操作完成后就会停止内核。<br />　　 参数说明:<br />　　 [-n] 防止sync系统调用﹐它用在用fsck修补根分区之后﹐以阻止内核用老版本的超级块〔superblock〕覆盖修补过的超级块。<br />　　 [-w] 并不是真正的重启或关机﹐只是写wtmp〔/var/log/wtmp〕纪录。<br />　　 [-d] 不写wtmp纪录〔已包含在选项[-n]中〕。<br />　　 [-f] 没有调用shutdown而强制关机或重启。<br />　　 [-i] 关机〔或重启〕前﹐关掉所有的网络接口。<br />　　 [-p] 该选项为缺省选项。就是关机时调用poweroff。<br />　　 <br />3.reboot<br />    reboot的工作过程差不多跟halt一样﹐不过它是引发主机重启﹐而halt是关机。它的参数与halt相差不多。<br /><br />4.init<br />   init是所有进程的祖先﹐它的进程号始终为1﹐所以发送TERM信号给init会终止所有的用户进程﹑守护进程等。shutdown 就是使用这种机制。init定义了8个运行级别(runlevel)， init 0为关机﹐init 1为重启。关于init可以长篇大论﹐这里就不再叙述。另外还有 telinit命令可以改变init的运行级别﹐比如﹐telinit -iS可使系统进入单用户模式﹐并且得不到使用shutdown时的信息和等待时间。<br /><br />----------------------------------------------------------<br /><br />1.直接用crontab命令编辑<br /><br />cron服务提供crontab命令来设定cron服务的，以下是这个命令的一些参数与说明：<br /><br />crontab-u//设定某个用户的cron服务，一般root用户在执行这个命令的时候需要此参数<br /><br />crontab-l//列出某个用户cron服务的详细内容<br /><br />crontab-r//删除没个用户的cron服务<br /><br />crontab-e//编辑某个用户的cron服务<br /><br />比如说root查看自己的cron设置：crontab-uroot-l<br /><br />再例如，root想删除fred的cron设置：crontab-ufred-r<br /><br />在编辑cron服务时，编辑的内容有一些格式和约定，输入：crontab-uroot-e<br /><br />进入vi编辑模式，编辑的内容一定要符合下面的格式：*/1****ls>>/tmp/ls.txt<br /><br />这个格式的前一部分是对时间的设定，后面一部分是要执行的命令，如果要执行的命令太多，可以把这些命令写到一个脚本里面，然后在这里直接调用这个脚本就可以了，调用的时候记得写出命令的完整路径。时间的设定我们有一定的约定，前面五个*号代表五个数字，数字的取值范围和含义如下：<br /><br />分钟　（0-59）<br /><br />小時　（0-23）<br /><br />日期　（1-31）<br /><br />月份　（1-12）<br /><br />星期　（0-6）//0代表星期天<br /><br />除了数字还有几个个特殊的符号就是"*"、"/"和"-"、","，*代表所有的取值范围内的数字，"/"代表每的意思,"*/5"表示每5个单位，"-"代表从某个数字到某个数字,","分开几个离散的数字。以下举几个例子说明问题：<br /><br />每天早上6点<br /><br />06***echo"Goodmorning.">>/tmp/test.txt//注意单纯echo，从屏幕上看不到任何输出，因为cron把任何输出都email到root的信箱了。<br /><br />每两个小时<br /><br />0*/2***echo"Haveabreaknow.">>/tmp/test.txt<br /><br />晚上11点到早上8点之间每两个小时，早上八点<br /><br />023-7/2，8***echo"Haveagooddream：）">>/tmp/test.txt<br /><br />每个月的4号和每个礼拜的礼拜一到礼拜三的早上11点<br /><br />0114*1-3commandline<br /><br />1月1日早上4点<br /><br />0411*commandline<br /><br />每次编辑完某个用户的cron设置后，cron自动在/var/spool/cron下生成一个与此用户同名的文件，此用户的cron信息都记录在这个文件中，这个文件是不可以直接编辑的，只可以用crontab-e来编辑。cron启动后每过一份钟读一次这个文件，检查是否要执行里面的命令。因此此文件修改后不需要重新启动cron服务。<br /><br />2.编辑/etc/crontab文件配置cron<br /><br />cron 服务每分钟不仅要读一次/var/spool/cron内的所有文件，还需要读一次/etc/crontab,因此我们配置这个文件也能运用cron服务做一些事情。用crontab配置是针对某个用户的，而编辑/etc/crontab是针对系统的任务。此文件的文件格式是：<br /><div class="quote_title">引用</div><div class="quote_div"><br />SHELL=/bin/bash<br /><br />PATH=/sbin:/bin:/usr/sbin:/usr/bin</div><br /><br />MAILTO=root//如果出现错误，或者有数据输出，数据作为邮件发给这个帐号<br /><br />HOME=///使用者运行的路径,这里是根目录<br /><div class="quote_title">引用</div><div class="quote_div"><br />#run-parts<br /><br />01****rootrun-parts/etc/cron.hourly//每小时执行/etc/cron.hourly内的脚本<br /><br />024***rootrun-parts/etc/cron.daily//每天执行/etc/cron.daily内的脚本<br /><br />224**0rootrun-parts/etc/cron.weekly//每星期执行/etc/cron.weekly内的脚本<br /><br />4241**rootrun-parts/etc/cron.monthly//每月去执行/etc/cron.monthly内的脚本</div><br /><br />大家注意"run-parts"这个参数了，如果去掉这个参数的话，后面就可以写要运行的某个脚本名，而不是文件夹名了。
          <br/>
          <span style="color:red;">
            <a href="http://vertex.javaeye.com/blog/187879#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 29 Apr 2008 09:17:28 +0800</pubDate>
        <link>http://vertex.javaeye.com/blog/187879</link>
        <guid>http://vertex.javaeye.com/blog/187879</guid>
      </item>
      <item>
        <title>linux下的Resin自启动问题</title>
        <author>simon_zhang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://vertex.javaeye.com">simon_zhang</a>&nbsp;
          链接：<a href="http://vertex.javaeye.com/blog/132560" style="color:red;">http://vertex.javaeye.com/blog/132560</a>&nbsp;
          发表时间: 2007年10月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          我们使用的是resin-2.1.17<br /><br />首先按照bin/httpd.sh里的说明将resin配置为服务<br /><pre name="code" class="java"># To install, you'll need to configure JAVA_HOME and RESIN_HOME and
# copy httpd.sh to /etc/rc.d/init.d as resin.  Then
# use "unix# /sbin/chkconfig resin on"
cp httpd.sh /etc/rc.d/init.d/resin
/sbin/chkconfig resin on</pre><br /><br />配置JAVA_HOME 和 RESIN_HOME<br /><pre name="code" class="java">JAVA_HOME=/opt/j2sdk1.4.2_10
export JAVA_HOME
 
RESIN_HOME=/opt/resin-2.1.17
export RESIN_HOME
</pre><br />自动启动的时候必须把变量export进来<br /><br />修改<br /><pre name="code" class="java">bin=`dirname $script`</pre><br /><br />为<br /><pre name="code" class="java">bin=$RESIN_HOME/bin</pre><br /><br />理论上这样应该已经能作为服务启动了<br />事实上也确实启动了<br />但是发现所有使用了类似<br /><pre name="code" class="java">&lt;fmt:formatDate value='${rptDate}' type='date'/></pre><br /><br />的jstl表达式的地方日期格式都是英文的，而不是期望中的yyyy-MM-dd的格式<br /><br />一开始以为是系统重新启动默认使用的自带的jdk的问题<br />于是将系统默认jdk设置成/opt/j2sdk1.4.2_10<br />结果问题还是一样<br />这时候可能是某个同事操作cms系统的时候重建了页面<br />首页的中文全变成了??<br />这样就很明显是jdk的编码问题了<br />google之<br /><br />在最末行启动参数加上强制指定编码，即修改为<br /><pre name="code" class="java">exec $perl $bin/wrapper.pl -chdir -name "$name" -class "$class" -Dclient.encoding.override=GBK -Dfile.encodin
g=GBK -Duser.language=zh -Duser.region=CN -Djava.awt.headless=true $args $*</pre><br /><br />其中“ -Djava.awt.headless=true”是为了在linux下jfreechart的图像能正常显示<br />问题解决<br /><br />以上所做的修改都是针对httpd.sh<br />别忘了改完重新copy一份到/etc/rc.d/init.d<br /><br />造成这个问题的原因可能是因为服务加载的顺序问题<br />因为jdk的默认编码设置是跟系统设置一样的<br /><br />这件事情教育我们要使用<br /><pre name="code" class="java">&lt;fmt:formatDate value='${rptDate}' pattern='yyyy-MM-dd'/></pre><br /><br />补充：<br />一些地方还是出现乱码<br />发现数据中心的linux没有设置LC_ALL<br />修改<pre name="code" class="java">/etc/sysconfig/i18n
LANG="zh_CN.GB18030"
LANGUAGE="zh_CN.GB18030:zh_CN.GB2312:zh_CN"
LC_ALL="zh_CN.GB18030"
SUPPORTED="en_US.UTF-8:en_US:en:zh_CN.GB18030:zh_CN:zh:zh_TW.big5:zh_TW:zh:ja_JP.UTF-8:ja_JP:ja:ko_KR.eucKR:ko_KR:ko"
SYSFONT="lat0-sun16"
SYSFONTACM="8859-15"
export LC_ALL</pre><br /><br />当然编码具体情况具体分析
          <br/>
          <span style="color:red;">
            <a href="http://vertex.javaeye.com/blog/132560#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 16 Oct 2007 16:55:38 +0800</pubDate>
        <link>http://vertex.javaeye.com/blog/132560</link>
        <guid>http://vertex.javaeye.com/blog/132560</guid>
      </item>
      <item>
        <title>Linux下JDK安装</title>
        <author>simon_zhang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://vertex.javaeye.com">simon_zhang</a>&nbsp;
          链接：<a href="http://vertex.javaeye.com/blog/132552" style="color:red;">http://vertex.javaeye.com/blog/132552</a>&nbsp;
          发表时间: 2007年10月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          1.下载JDK文件bin格式<br />http://gceclub.sun.com.cn/NASApp/sme/docroot/ecommunity/management/login.jsp?goto=/NASApp/sme/docroot/ecommunity/down.jsp?file=J2SE/jdk-1_5_0_08-linux-i586.bin<br />2.到bin文件所在文件夹下chmod 755 jdk-1_5_0_08-linux-i586.bin<br />3.执行./jdk-1_5_0_08-linux-i586.bin进行解压<br />4.将解压好的文件夹拷贝到/opt/java/目录下<br />5.到/etc/profile.d/目录下<br />6.创建JAVA.sh文件<br />7.以文本编译器形式打开此文件写入设置环境变量<br />JAVA_HOME=/opt/java/jdk1.5.0_08       /*****注意这里是你拷贝到 /opt/java/下的文件夹的名称<br />CLASSPATH=$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar<br />PATH=$JAVA_HOME/bin:$PATH<br />RESIN_HOME=/home/resin<br />export JAVA_HOME RESIN_HOME CLASSPATH PATH<br />8.在控制台输入java 命令OK 以下的事不用我教你了吧...<br />如果没有JAVA成功,那么肯定是你的文件名或文件夹没拷贝到相应的路径..再试试来一遍ok ?<br />转载至<a href=" http://blog.sina.com.cn/s/blog_472b2a9b010005ru.html" target="_blank"> http://blog.sina.com.cn/s/blog_472b2a9b010005ru.html</a>
          <br/>
          <span style="color:red;">
            <a href="http://vertex.javaeye.com/blog/132552#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 16 Oct 2007 16:46:22 +0800</pubDate>
        <link>http://vertex.javaeye.com/blog/132552</link>
        <guid>http://vertex.javaeye.com/blog/132552</guid>
      </item>
      <item>
        <title>cvs中用户创建，权限，</title>
        <author>simon_zhang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://vertex.javaeye.com">simon_zhang</a>&nbsp;
          链接：<a href="http://vertex.javaeye.com/blog/131303" style="color:red;">http://vertex.javaeye.com/blog/131303</a>&nbsp;
          发表时间: 2007年10月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <strong>CVS 独自认证</strong><br />要求:<br />    1. 取消选择CVSnt ->  service control panel  ->advanced 下的 'impersonation enable' 选项  ;<br />    2. 选中 'use local user for pserver authentication instead of domain user';<br />    3. 启动'cvs service' 和 'cvs lock service' ;<br />    4. 使用 'pserver' 协议.<br /><strong>一、CVS版本库的搬移（服务器端）步骤：</strong><br />1、安装cvsnt，这没有多说的。下一步<br />2、设置cvs库的目录将原cvs库移动新的cvs库中，<br />  cvs库的结构：<br />   E:\CVS库 -|<br />             |-ctmoudle|<br />                       |- myproject  --移过来的cvs源码库<br />                       |- CVSROOT    --这个目录不用手工建,cvs自动生成.<br />3、启动cvs service control panal， stop CVS service 和stop CVS Lock Service <br />在Repositories中设置cvs库的路径指向E:\CVS库，然后添加valid Repositories Roots路径为E:\CVS库\ctmoudle\<br />4、启动cvs service 、cvs lock service。<br /><br /><strong>二、用户创建步骤:</strong><br />    (所有的操作在客户机完成)<br />    1. 用默认的系统管理员帐户(Administrator)创建CVS用户'cvsadmin' ,并且给'cvsadmin'在CVSRoot目录的全部的权限<br />       (1) 运行 winCVS;<br />       (2) 用默认的系统管理员帐户'Administrator'登陆库: <br />             选择菜单 'Admin->Login', 在CVSRoot方框内输入<br />            :pserver:Administrator@serverIP:/repositoryNAME<br />             输入服务器上的'Administratotr'帐户的密码<br />       (3) 创建一个临时目录 'c:\cvs\admintemp';<br />       (4) Check out 模块 'CVSRoot' 到这个临时目录, 转到'CVSRoot'目录;<br />       (5) 为当前的库添加一个新的CVS用户 'cvsadmin' , 打开 admin-> Command line 执行如下命令:<br />                cvs passwd -a cvsadmin   <br />                ( 为cvsadmin设置密码)<br />       (6) 给予 'cvsadmin'用户'CVSRoot' 库的所有权限,  执行如下命令:<br />               cvs chown cvsadmin    ( 设在cvsadmin为 'CVSRoot' 库的所有者)<br />               cvs chacl default:n   ( 取消所有的默认权限) <br />               cvs chacl cvsadmin:cwr    ( 给cvsadmin所有的权限: CREATE, WRITE, READ )<br />       (7) 关闭winCVS,删除'c:\cvs\admintemp' 目录;<br />    2. 为库创建管理员用户 'cvsadmin' <br />       (1) 用'cvsadmin'帐户登陆仓库:  'admin->login', 在CVSRoot框中输入 <br />            :pserver:cvsadmin@serverIP:/repositoryNAME <br />            输入'cvsadmin' 帐户的密码.<br />       (2) 为CVS管理创建一个目录'c:\cvs\admin' ;<br />       (3) Check out 'CVSRoot' 模块到这个目录(c:\cvs\admin);<br />       (4) 转到 'CVSRoot' , 创建文件admin, 内容为cvsadmin, 执行如下命令:<br />                cvs add admin     ( 添加'admin' 文件到库 )<br />                cvs commit -m "no msg" admin        ( check in 'admin' 文件)<br />                (或者是: cvs ci -m "no msg" admin)<br />       (6) 修改'checkoutlist'文件,在最后一行增加admin  error message , 执行如下命令:<br />                cvs commit -m "no msg" checkoutlist ( check in  修改)<br />       (7) 现在, 'cvsadmin' 用户已经成为CVSRoot库的管理员了;<br />   3.创建其他用户<br />    在admin文件中添加用户 ，每个用户占一行。<br />    将config中加上SystemAuth=no 这段  然后commit---好像可以不用<br />    然后执行 cvs passwd -a yangjun<br />    选中myproject 执行cvs chacl yangjun:cwr
          <br/>
          <span style="color:red;">
            <a href="http://vertex.javaeye.com/blog/131303#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 12 Oct 2007 11:17:03 +0800</pubDate>
        <link>http://vertex.javaeye.com/blog/131303</link>
        <guid>http://vertex.javaeye.com/blog/131303</guid>
      </item>
      <item>
        <title>JFreeChart demo</title>
        <author>simon_zhang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://vertex.javaeye.com">simon_zhang</a>&nbsp;
          链接：<a href="http://vertex.javaeye.com/blog/115012" style="color:red;">http://vertex.javaeye.com/blog/115012</a>&nbsp;
          发表时间: 2007年08月21日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <img src="aggbug/1752656.aspx" height="1" width="1" />
          <br/>
          <span style="color:red;">
            <a href="http://vertex.javaeye.com/blog/115012#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 21 Aug 2007 12:46:00 +0800</pubDate>
        <link>http://vertex.javaeye.com/blog/115012</link>
        <guid>http://vertex.javaeye.com/blog/115012</guid>
      </item>
      <item>
        <title>JFreeChart中文API</title>
        <author>simon_zhang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://vertex.javaeye.com">simon_zhang</a>&nbsp;
          链接：<a href="http://vertex.javaeye.com/blog/115013" style="color:red;">http://vertex.javaeye.com/blog/115013</a>&nbsp;
          发表时间: 2007年08月21日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          JFreeChart中文API<img src="aggbug/1752649.aspx" height="1" width="1" />
          <br/>
          <span style="color:red;">
            <a href="http://vertex.javaeye.com/blog/115013#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 21 Aug 2007 12:43:00 +0800</pubDate>
        <link>http://vertex.javaeye.com/blog/115013</link>
        <guid>http://vertex.javaeye.com/blog/115013</guid>
      </item>
      <item>
        <title>oracle 10g 无监听程序 解决办法</title>
        <author>simon_zhang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://vertex.javaeye.com">simon_zhang</a>&nbsp;
          链接：<a href="http://vertex.javaeye.com/blog/113174" style="color:red;">http://vertex.javaeye.com/blog/113174</a>&nbsp;
          发表时间: 2007年08月15日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          oracle 10g 无监听程序 解决办法<img src="aggbug/1745324.aspx" height="1" width="1" />
          <br/>
          <span style="color:red;">
            <a href="http://vertex.javaeye.com/blog/113174#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 15 Aug 2007 17:20:00 +0800</pubDate>
        <link>http://vertex.javaeye.com/blog/113174</link>
        <guid>http://vertex.javaeye.com/blog/113174</guid>
      </item>
      <item>
        <title>将weblogic注册为服务</title>
        <author>simon_zhang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://vertex.javaeye.com">simon_zhang</a>&nbsp;
          链接：<a href="http://vertex.javaeye.com/blog/109539" style="color:red;">http://vertex.javaeye.com/blog/109539</a>&nbsp;
          发表时间: 2007年08月06日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          将weblogic注册为win服务<img src="http://blog.csdn.net/simon_21/aggbug/1728624.aspx" height="1" width="1" />
          <br/>
          <span style="color:red;">
            <a href="http://vertex.javaeye.com/blog/109539#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 06 Aug 2007 15:36:00 +0800</pubDate>
        <link>http://vertex.javaeye.com/blog/109539</link>
        <guid>http://vertex.javaeye.com/blog/109539</guid>
      </item>
      <item>
        <title>HelloWorld  之dojo使用</title>
        <author>simon_zhang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://vertex.javaeye.com">simon_zhang</a>&nbsp;
          链接：<a href="http://vertex.javaeye.com/blog/106216" style="color:red;">http://vertex.javaeye.com/blog/106216</a>&nbsp;
          发表时间: 2007年07月27日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          dojo之Helloworld
<a href="http://dojo.jot.com/WikiHome/Tutorials/HelloWorld" target="_blank">http://dojo.jot.com/WikiHome/Tutorials/HelloWorld</a><img src="aggbug/1711651.aspx" height="1" width="1" />
          <br/>
          <span style="color:red;">
            <a href="http://vertex.javaeye.com/blog/106216#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 27 Jul 2007 16:03:00 +0800</pubDate>
        <link>http://vertex.javaeye.com/blog/106216</link>
        <guid>http://vertex.javaeye.com/blog/106216</guid>
      </item>
      <item>
        <title>使用poi包的hssf，让excel中的单元格cell里面的文字&quot;自动换行&quot;</title>
        <author>simon_zhang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://vertex.javaeye.com">simon_zhang</a>&nbsp;
          链接：<a href="http://vertex.javaeye.com/blog/105516" style="color:red;">http://vertex.javaeye.com/blog/105516</a>&nbsp;
          发表时间: 2007年07月24日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          excel中的单元格cell里面的文字"自动换行"  <img src="aggbug/1704768.aspx" height="1" width="1" />
          <br/>
          <span style="color:red;">
            <a href="http://vertex.javaeye.com/blog/105516#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 24 Jul 2007 09:53:00 +0800</pubDate>
        <link>http://vertex.javaeye.com/blog/105516</link>
        <guid>http://vertex.javaeye.com/blog/105516</guid>
      </item>
      <item>
        <title>定时执行任务的三种方法</title>
        <author>simon_zhang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://vertex.javaeye.com">simon_zhang</a>&nbsp;
          链接：<a href="http://vertex.javaeye.com/blog/66758" style="color:red;">http://vertex.javaeye.com/blog/66758</a>&nbsp;
          发表时间: 2007年03月28日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          定时执行任务的三种方法:1）java.util.Timer.2）ServletContextListener.3）org.springframework.scheduling.timer.ScheduledTimerTask
<img src="aggbug/1544188.aspx" height="1" width="1" />
          <br/>
          <span style="color:red;">
            <a href="http://vertex.javaeye.com/blog/66758#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 28 Mar 2007 16:27:00 +0800</pubDate>
        <link>http://vertex.javaeye.com/blog/66758</link>
        <guid>http://vertex.javaeye.com/blog/66758</guid>
      </item>
      <item>
        <title>定时执行任务的三种方法</title>
        <author>simon_zhang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://vertex.javaeye.com">simon_zhang</a>&nbsp;
          链接：<a href="http://vertex.javaeye.com/blog/66141" style="color:red;">http://vertex.javaeye.com/blog/66141</a>&nbsp;
          发表时间: 2007年03月28日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>文章有些内容显示不全，请访问<a href="http://blog.csdn.net/simon_21/archive/2007/03/28/1544188.aspx">http://blog.csdn.net/simon_21/archive/2007/03/28/1544188.aspx</a>这个地址。<br />
</p>
<p>定时执行任务的三种方法<span lang="EN-US">:</span></p>
<p><span lang="EN-US">1</span>）<span lang="EN-US">java.util.Timer.</span></p>
<p><span lang="EN-US">2</span>）<span lang="EN-US">ServletContextListener.</span></p>
<p><span lang="EN-US">3）org.springframework.scheduling.timer.ScheduledTimerTask</span></p>
<p><span lang="EN-US">1</span>）<span lang="EN-US">java.util.Timer<br />
</span>这个方法应该是最常用的，不过这个方法需要手工启动你的任务：<span lang="EN-US"><br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Timer timer=new Timer();<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; timer.schedule(new ListByDayTimerTask(),10000,86400000);</span><br />
这里的<span lang="EN-US">ListByDayTimerTask</span>类必须<span lang="EN-US">extends TimerTask</span>里面的<span lang="EN-US">run()</span>方法。<span lang="EN-US"><br />
<br />
2</span>）<span lang="EN-US">ServletContextListener<br />
</span>这个方法在<span lang="EN-US">web</span>容器环境比较方便，这样，在<span lang="EN-US">web server</span>启动后就可以<span lang="EN-US"></span>自动运行该任务，不需要手工操作。<span lang="EN-US"><br />
</span>将<span lang="EN-US">ListByDayListener implements ServletContextListener</span>接口，在<span lang="EN-US">contextInitialized</span>方法中加入启动<span lang="EN-US">Timer</span>的代码，在<span lang="EN-US">contextDestroyed</span>方法中加入<span lang="EN-US">cancel</span>该<span lang="EN-US">Timer</span>的代码；然后在<span lang="EN-US">web.xml</span>中，加入<span lang="EN-US">listener:<br />
&lt;-listener&gt;<br />
&lt;-listener-class&gt;com.qq.customer.ListByDayListener&lt;/listener-class&gt;<br />
&lt;-/listener&gt;<br />
<br />
3)org.springframework.scheduling.timer.ScheduledTimerTask</span>如果你用<span lang="EN-US">spring</span>，那么你不需要写<span lang="EN-US">Timer</span>类了，在<span lang="EN-US">schedulingContext-timer.xml</span>中加入下面的内容就可以了：<span lang="EN-US"><br />
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;<br />
&lt;!DOCTYPE beans PUBLIC &quot;-//SPRING//DTD BEAN//EN&quot; &quot;http://www.springframework.org/dtd/spring-beans.dtd&quot;&gt;<br />
<br />
&lt;beans&gt;<br />
&lt;bean id=&quot;timer&quot; class=&quot;org.springframework.scheduling.timer.TimerFactoryBean&quot;&gt;<br />
&lt;property name=&quot;scheduledTimerTasks&quot;&gt;<br />
&lt;list&gt;<br />
&lt;ref local=&quot;MyTimeTask1&quot;/&gt;<br />
&lt;/list&gt;<br />
&lt;/property&gt;<br />
&lt;/bean&gt;<br />
<br />
&lt;bean id=&quot;MyTimeTask&quot; class=&quot;com.qq.timer.ListByDayTimerTask&quot;/&gt;<br />
<br />
&lt;bean id=&quot;MyTimeTask1&quot; class=&quot;org.springframework.scheduling.timer.ScheduledTimerTask&quot;&gt;<br />
&lt;property name=&quot;timerTask&quot;&gt;<br />
&lt;ref bean=&quot;MyTimeTask&quot;/&gt;<br />
&lt;/property&gt;<br />
&lt;property name=&quot;delay&quot;&gt;<br />
&lt;value&gt;10000&lt;/value&gt;<br />
&lt;/property&gt;<br />
&lt;property name=&quot;period&quot;&gt;<br />
&lt;value&gt;86400000&lt;/value&gt;<br />
&lt;/property&gt;<br />
&lt;/bean&gt;<br />
&lt;/beans&gt;</span></p>
<p><span lang="EN-US">以上内容转载自：<a href="http://dev.csdn.net/author/xtpdcsse/ec8e8080a5b04fa79e7d4828bc807d3f.html">http://dev.csdn.net/author/xtpdcsse/ec8e8080a5b04fa79e7d4828bc807d3f.html</a></span></p>
<p><span lang="EN-US">下面是我的实现。</span></p>
<p><span lang="EN-US">1</span>）利用<span lang="EN-US">java.util.Timer. 代码如下</span></p>
<p><span lang="EN-US">&lt;1&gt;StartThread.java</span></p>
<p><span lang="EN-US">package com.jview.main;<br />
import java.util.Date;<br />
import java.util.Timer;<br />
import java.util.TimerTask;<br />
import org.apache.log4j.Logger;<br />
public class StartThread extends Thread {<br />
&nbsp;&nbsp;&nbsp; private static Logger logger = Logger.getLogger(&quot;StartThread&quot;);<br />
&nbsp;&nbsp;&nbsp; public static final int EXECUTE_CYC = 86400000; //24*60*60*1000毫秒<br />
&nbsp;&nbsp;&nbsp; int startH = 9;<br />
&nbsp;&nbsp;&nbsp; int startM = 52;<br />
&nbsp;&nbsp;&nbsp; private Timer _timer ;<br />
&nbsp;&nbsp;&nbsp; private Date _statDate;<br />
&nbsp;&nbsp;&nbsp; private Date _nowDate;<br />
&nbsp;&nbsp;&nbsp; public StartThread(){<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; _nowDate = new Date();<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; _timer = new Timer();<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; _statDate = new Date(_nowDate.getYear(),_nowDate.getMonth(),_nowDate.getDate(),startH,startM);<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; public void StartUp(){<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; _timer.schedule(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new TimerTask() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void run()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; logger.info(&quot;开始统计...&quot;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Class.forName(&quot;com.jview.stat.StatPlan&quot;).newInstance();<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; } catch (InstantiationException e) {<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // TODO Auto-generated catch block<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; } catch (IllegalAccessException e) {<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // TODO Auto-generated catch block<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; } catch (ClassNotFoundException e) {<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // TODO Auto-generated catch block<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; },_statDate,EXECUTE_CYC);<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; public static void main(String[] args) {<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; StartThread _statUp = new StartThread();<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; _statUp.StartUp();<br />
&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; <br />
}<br />
&lt;2&gt;</span>StatPlan.java<br />
package com.jview.stat;<br />
import java.util.Calendar;<br />
import org.apache.log4j.Logger;<br />
public class StatPlan {<br />
</p>
&nbsp;&nbsp; &nbsp;private static Logger logger = Logger.getLogger(&quot;StatPlan&quot;);<br />
&nbsp;&nbsp; &nbsp;private int i = 0;<br />
&nbsp;&nbsp; &nbsp;private StatPlanService _sps ;<br />
&nbsp;&nbsp; &nbsp;public StatPlan(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;_sps = new StatWeekPlan();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;statPlan();<br />
&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp;&nbsp; public void statPlan(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Calendar _c = Calendar.getInstance();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;logger.info(&quot;stat plan ... 执行&quot;+ i +&quot;次,时间：&quot;+_c.getTime());<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;_sps.StatPlan();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;i++;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;for(int i = 0; i&lt;9999;i++){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if(i==0 || i== 9998){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;logger.info(&quot;&quot;+i);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
}<br />
&lt;3&gt;StatWeekPlan .java<br />
import org.apache.log4j.Logger;<br />
<br />
public class StatWeekPlan extends StatPlanService {<br />
&nbsp;&nbsp;&nbsp; private static Logger logger = Logger.getLogger(&quot;StatWeekPlan&quot;);<br />
&nbsp;&nbsp;&nbsp; public void StatPlan(){<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; logger.info(&quot;this is statWeekPlan&quot;);<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
&lt;4&gt;StatPlanService .java<br />
public class StatPlanService {<br />
&nbsp;&nbsp;&nbsp; public void StatPlan(){&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<p><span lang="EN-US">2</span>）<span lang="EN-US">ServletContextListener.实现<br />
&lt;1&gt; </span><span lang="EN-US">SysStatListener </span><span lang="EN-US">.java</span><br />
<span lang="EN-US">import java.util.Date;<br />
import java.util.Timer;<br />
import java.util.TimerTask;<br />
import javax.servlet.ServletContextEvent;<br />
import javax.servlet.ServletContextListener;<br />
import org.apache.log4j.Logger;<br />
public class SysStatListener implements ServletContextListener{<br />
&nbsp;&nbsp;&nbsp; private static Logger logger = Logger.getLogger(&quot;ListByDayListener&quot;);<br />
&nbsp;&nbsp;&nbsp; private StatTask _sTask;<br />
&nbsp;&nbsp;&nbsp; public void contextDestroyed(ServletContextEvent scevent) {<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; String status = &quot;停止系统统计线程&quot;;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; scevent.getServletContext().log(status);<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; logger.info(status);<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; _sTask.shutDown();<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; public void contextInitialized(ServletContextEvent scevent) {<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; String status = &quot;启动系统统计线程&quot;;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; scevent.getServletContext().log(status);<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; logger.info(status);<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; _sTask = new StatTask();<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; _sTask.startUp();<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
&lt;2&gt; </span><span lang="EN-US">StatTask .java</span><br />
<span lang="EN-US">import java.util.Date;<br />
import java.util.Timer;<br />
import java.util.TimerTask;<br />
import org.apache.log4j.Logger;<br />
public class StatTask extends Thread {<br />
&nbsp;&nbsp;&nbsp; private static Logger logger = Logger.getLogger(&quot;StartThread&quot;);<br />
&nbsp;&nbsp;&nbsp; public static final int EXECUTE_CYC = 86400000;<br />
&nbsp;&nbsp;&nbsp; int startH = 9;<br />
&nbsp;&nbsp;&nbsp; int startM = 52;<br />
&nbsp;&nbsp;&nbsp; private Timer _timer ;<br />
&nbsp;&nbsp;&nbsp; private Date _statDate;<br />
&nbsp;&nbsp;&nbsp; private Date _nowDate;<br />
&nbsp;&nbsp;&nbsp; public StatTask(){<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; _nowDate = new Date();<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; _timer = new Timer();<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; _statDate = new Date(_nowDate.getYear(),_nowDate.getMonth(),_nowDate.getDate(),startH,startM);<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; public void startUp(){<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; _timer.schedule(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new TimerTask() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void run()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; logger.info(&quot;开始统计...&quot;);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Class.forName(&quot;</span><span lang="EN-US">com.jview.stat.StatPlan</span><span lang="EN-US">&quot;).newInstance();<br />
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; } catch (InstantiationException e) {<br />
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // TODO Auto-generated catch block<br />
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; e.printStackTrace();<br />
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; } catch (IllegalAccessException e) {<br />
&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // TODO Auto-generated catch block<br />
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; e.printStackTrace();<br />
&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; } catch (ClassNotFoundException e) {<br />
&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // TODO Auto-generated catch block<br />
&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; e.printStackTrace();<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; },_statDate,EXECUTE_CYC);<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; public void shutDown(){<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; _timer.cancel();<br />
&nbsp;&nbsp;&nbsp; }<br />
&lt;3&gt;在web.xml中添加下面的内容（注：下面的内容放到&lt;filter-mapping&gt;的后面）<br />
&nbsp;&lt;listener&gt;<br />
&nbsp; &nbsp;&nbsp;&nbsp; &lt;listener-class&gt;com.jview.auto.stat.</span><span lang="EN-US">SysStatListener </span><span lang="EN-US">&lt;/listener-class&gt;<br />
&nbsp; &lt;/listener&gt;<br />
</span></p>
          <br/>
          <span style="color:red;">
            <a href="http://vertex.javaeye.com/blog/66141#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 28 Mar 2007 16:19:18 +0800</pubDate>
        <link>http://vertex.javaeye.com/blog/66141</link>
        <guid>http://vertex.javaeye.com/blog/66141</guid>
      </item>
      <item>
        <title>体验JAVA 5的新增语言特性</title>
        <author>simon_zhang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://vertex.javaeye.com">simon_zhang</a>&nbsp;
          链接：<a href="http://vertex.javaeye.com/blog/65820" style="color:red;">http://vertex.javaeye.com/blog/65820</a>&nbsp;
          发表时间: 2007年03月27日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          时间：2006-01-05<br />
作者：<a href="http://dev2dev.bea.com.cn/author/434.html">Jess Garms</a>,&nbsp;<a href="http://dev2dev.bea.com.cn/author/435.html">Tim Hanson</a><br />
本文关键字：<a href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=java%205.0">java 5.0</a>,&nbsp;<a href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=generics">generics</a>,&nbsp;<a href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=%E6%B3%9B%E5%9E%8B">泛型</a>,&nbsp;<a href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=%E6%B3%A8%E9%87%8A">注释</a><br />
<br />
Java 5.0发布了，许多人都将开始使用这个JDK版本的一些新增特性。从增强的for循环到诸如泛型(generic)之类更复杂的特性，都将很快出现在您所 编写的代码中。我们刚刚完成了一个基于Java 5.0的大型任务，而本文就是要介绍我们使用这些新特性的体验。本文不是一篇入门性的文章，而是对这些特性以及它们所产生的影响的深入介绍，同时还给出了 一些在项目中更有效地使用这些特性的技巧。
<p><strong>简介</strong><br />
在JDK 1.5的beta阶段，我们为BEA的Java IDE开发了一个Java 5编译器。因为我们实现了许多新特性，所以人们开始以新的方式利用它们；有些用法很聪明，而有些用法明显应该被列入禁用清单。编译器本身使用了新的语言特 性，所以我们也获得了使用这些特性维护代码的直接体验。本文将介绍其中的许多特性和使用它们的体验。 <br />
我们假定您已经熟悉了这些新特性，所以不再全面介绍每个特性，而是谈论一些有趣的、但很可能不太明显的内容和用法。这些技巧出自我们的实际体验，并大致按照语言特性进行了分类。 <br />
我们将从最简单的特性开始，逐步过渡到高级特性。泛型所包含的内容特别丰富，因此占了本文一半的篇幅。</p>
<p><strong>增强的for循环</strong><br />
为了迭代集合和数组，增强的for循环提供了一个简单、兼容的语法。有两点值得一提： </p>
<p><strong>Init表达式</strong><br />
在循环中，初始化表达式只计算一次。这意味着您通常可以移除一个变量声明。在这个例子中，我们必须创建一个整型数组来保存 computeNumbers()的结果，以防止每一次循环都重新计算该方法。您可以看到，下面的代码要比上面的代码整洁一些，并且没有泄露变量 numbers： <br />
</p>
<pre class="code">未增强的For：<br />int sum = 0;<br />Integer[] numbers = computeNumbers();<br />for (int i=0; i &lt; numbers.length ; i++)<br />    sum += numbers[i];<br />增强后的For： <br />int sum = 0;<br /><br />for ( int number: computeNumbers() )<br />    sum += number;</pre>
<p><strong>局限性</strong><br />
有时需要在迭代期间访问迭代器或下标，看起来增强的for循环应该允许该操作，但事实上不是这样，请看下面的例子： </p>
<pre class="code">for (int i=0; i &lt; numbers.length ; i++) {<br />    if (i != 0) System.out.print(&quot;,&quot;);<br />    System.out.print(numbers[i]);<br />}</pre>
<p>　　我们希望将数组中的值打印为一个用逗号分隔的清单。我们需要知道目前是否是第一项，以便确定是否应该打印逗号。使用增强的for循环是无法获知这种信息的。我们需要自己保留一个下标或一个布尔值来指示是否经过了第一项。 　　这是另一个例子： </p>
<pre class="code">for (Iterator<integer></integer> it = n.iterator() ; it.hasNext() ; )<br />    if (it.next() &lt; 0)<br />        it.remove();</pre>
<p>　　在此例中，我们想从整数集合中删除负数项。为此，需要对迭代器调用一个方法，但是当使用增强的for 循环时，迭代器对我们来说是看不到的。因此，我们只能使用Java 5之前版本的迭代方法。 　　顺便说一下，这里需要注意的是，由于Iterator是泛型，所以其声明是Iterator<integer></integer>。许多人都忘记了这一点而使用了Iterator的原始格式。 </p>
<p><strong>注释</strong><br />
注释处理是一个很大的话题。因为本文只关注核心的语言特性，所以我们不打算涵盖它所有的可能形式和陷阱。　　我们将讨论内置的注释（SuppressWarnings，Deprecated和Override）以及一般注释处理的局限性。 </p>
<p><strong>Suppress Warnings</strong><br />
该注释关闭了类或方法级别的编译器警告。有时候您比编译器更清楚地知道，代码必须使用一个被否决的方法或执行一些无法静态确定是否类型安全的动作，而使用：</p>
<pre class="code">@SuppressWarnings(&quot;deprecation&quot;)<br />public static void selfDestruct() {<br />    Thread.currentThread().stop();<br />}</pre>
<p>　　这可能是内置注释最有用的地方。遗憾的是，1.5.0_04的javac不支持它。但是1.6支持它，并且Sun正在努力将其向后移植到1.5中。 <br />
Eclipse 3.1中支持该注释，其他IDE也可能支持它。这允许您把代码彻底地从警告中解脱出来。如果在编译时出现警告，可以确定是您刚刚把它添加进来&mdash;&mdash;以帮助查看那些可能不安全的代码。随着泛型的添加，它使用起来将更趁手。 </p>
<p><strong>Deprecated</strong><br />
遗憾的是，Deprecated没那么有用。它本来旨在替换@deprecated javadoc标签，但是由于它不包含任何字段，所以也就没有方法来建议deprecated类或方法的用户应该使用什么做为替代品。大多数用法都同时需 要javadoc标签和这个注释。 </p>
<p><strong>Override</strong> <br />
Override表示，它所注释的方法应该重写超类中具有相同签名的方法： </p>
<pre class="code">@Override<br />public int hashCode() {<br />    ...<br />}</pre>
<p>　　看上面的例子，如果没有在hashCode中将&ldquo;C&rdquo;大写，在编译时不会出现错误，但是在运行时将无法像期望的那样调用该方法。通过添加Override标签，编译器会提示它是否真正地执行了重写。 <br />
在超类发生改变的情况中，这也很有帮助。如果向该方法中添加一个新参数，而且方法本身也被重命名了，那么子类将突然不能编译，因为它不再重写超类的任何东西。 </p>
<p><strong>其它注释<br />
</strong>　　注释在其他场景中非常有用。当不是直接修改行为而是增强行为时，特别是在添加样板代码的情况下，注释在诸如EJB和<a href="http://dev2dev.bea.com/pub/a/2004/10/Anil_WServices.html" target="_blank">Web services</a>这样的框架中运行得非常好。 <br />
注释不能用做预处理器。Sun的设计特别预防了完全因为注释而修改类的字节码。这样可以正确地理解该语言的成果，而且IDE之类的工具也可以执行深入的代码分析和重构之类的功能。 <br />
注释不是银弹。第一次遇到的时候，人们试图尝试各种技巧。请看下面这个从别人那里获得的建议： </p>
<pre class="code">public class Foo {<br />&nbsp;<br />    @Property<br />    private int bar;<br />&nbsp;<br />}</pre>
<p>　　其思想是为私有字段bar自动创建getter和setter方法。遗憾的是，这个想法有两个失败之处：1)它不能运行，2)它使代码难以阅读和处理。 　　它是无法实现的，因为前面已经提到了，Sun特别阻止了对出现注释的类进行修改。 <br />
即使是可能的，它也不是一个好主意，因为它使代码可读性差。第一次看到这段代码的人会不知道该注释创建了方法。此外，如果将来您需要在这些方法内部执行一些操作，注释也是没用的。 　　总之，不要试图用注释去做那些常规代码可以完成的事情。 </p>
<p><strong>枚举 </strong><br />
enum非常像public static final int声明，后者作为枚举值已经使用了很多年。对int所做的最大也是最明显的改进是类型安全&mdash;&mdash;您不能错误地用枚举的一种类型代替另一种类型，这一点和 int不同，所有的int对编译器来说都是一样的。除去极少数例外的情况，通常都应该用enum实例替换全部的枚举风格的int结构。 <br />
枚举提供了一些附加的特性。EnumMap和EnumSet这两个实用类是专门为枚举优化的标准集合实现。如果知道集合只包含枚举类型，那么应该使用这些专门的集合来代替HashMap或HashSet。 <br />
大部分情况下，可以使用enum对代码中的所有public static final int做插入替换。它们是可比的，并且可以静态导入，所以对它们的引用看起来是等同的，即使是对于内部类（或内部枚举类型）。注意，比较枚举类型的时候， 声明它们的指令表明了它们的顺序值。 <br />
<br />
<strong>&ldquo;隐藏的&rdquo;静态方法 </strong><br />
两个静态方法出现在所有枚举类型声明中。因为它们是枚举子类上的静态方法，而不是Enum本身的方法，所以它们在java.lang.Enum的javadoc中没有出现。 <br />
第一个是values()，返回一个枚举类型所有可能值的数组。 <br />
第二个是valueOf()，为提供的字符串返回一个枚举类型，该枚举类型必须精确地匹配源代码声明。 <br />
<strong>方法<br />
</strong>　　关于枚举类型，我们最喜欢的一个方面是它可以有方法。过去您可能需要编写一些代码，对public static final int进行转换，把它从数据库类型转换为JDBC URL。而现在则可以让枚举类型本身带一个整理代码的方法。下面就是一个例子，包括DatabaseType枚举类型的抽象方法以及每个枚举实例中提供的 实现： <br />
</p>
<pre class="code">  public enum  DatabaseType {<br />  ORACLE {<br />  public String getJdbcUrl() {...}<br />  },<br />  MYSQL {<br />  public String getJdbcUrl() {...}<br />  };<br />  public abstract String getJdbcUrl();<br />  }</pre>
<p>　　现在枚举类型可以直接提供它的实用方法。例如：<br />
<br />
DatabaseType dbType = ...;<br />
String jdbcURL = dbType.getJdbcUrl();<br />
<br />
要获取URL，必须预先知道该实用方法在哪里。 </p>
<p><br />
<strong>可变参数(Vararg)</strong><br />
正确地使用可变参数确实可以清理一些垃圾代码。典型的例子是一个带有可变的String参数个数的log方法： <br />
</p>
<pre class="code">    Log.log(String code)<br />    Log.log(String code,  String arg)<br />    Log.log(String code,  String arg1, String arg2)<br />    Log.log(String code,  String[] args)</pre>
<p>　　当讨论可变参数时，比较有趣的是，如果用新的可变参数替换前四个例子，将是兼容的： <br />
Log.log(String code, String... args)<br />
所有的可变参数都是源兼容的&mdash;&mdash;那就是说，如果重新编译log()方法的所有调用程序，可以直接替换全部的四个方法。然而，如果需要向后的二进制兼容 性，那么就需要舍去前三个方法。只有最后那个带一个字符串数组参数的方法等效于可变参数版本，因此可以被可变参数版本替换。 <br />
<strong><br />
类型强制转换 </strong><br />
如果希望调用程序了解应该使用哪种类型的参数，那么应该避免用可变参数进行类型强制转换。看下面这个例子，第一项希望是String，第二项希望是Exception： <br />
</p>
<pre class="code">    Log.log(Object...  objects) {<br />    String message = (String)objects[0];<br />    if (objects.length &gt; 1) {<br />    Exception e = (Exception)objects[1];<br />    // Do something with the exception<br />    }<br />    }</pre>
<p>　　方法签名应该如下所示，相应的可变参数分别使用String和Exception声明： <br />
<br />
Log.log(String message, Exception e, Object... objects) {...}<br />
<br />
不要使用可变参数破坏类型系统。需要强类型化时才可以使用它。对于这个规则，PrintStream.printf()是一个有趣的例外：它提供类型信息作为自己的第一个参数，以便稍后可以接受那些类型。 <br />
<strong><br />
协变返回 </strong><br />
协变返回的基本用法是用于在已知一个实现的返回类型比API更具体的时候避免进行类型强制转换。在下面这个例子中，有一个返回Animal对象的 Zoo接口。我们的实现返回一个AnimalImpl对象，但是在JDK 1.5之前，要返回一个Animal对象就必须声明。:<br />
</p>
<pre class="code">    public interface Zoo  {<br />    public Animal getAnimal();<br />    }<br />  public class ZooImpl  implements Zoo {<br />  public Animal getAnimal(){<br />  return new AnimalImpl();<br />  }<br />  }</pre>
<p>　　协变返回的使用替换了三个反模式： </p>
<p>&nbsp;</p>
<ul>
    <li>直接字段访问。为了规避API限制，一些实现把子类直接暴露为字段： </li>
</ul>
<p align="left">ZooImpl._animal</p>
<ul>
    <li>另一种形式是，在知道实现的实际上是特定的子类的情况下，在调用程序中执行向下转换： </li>
</ul>
<p align="left">((AnimalImpl)ZooImpl.getAnimal()).implMethod();</p>
<ul>
    <li>我看到的最后一种形式是一个具体的方法，该方法用来避免由一个完全不同的签名所引发的问题： </li>
</ul>
<p align="left">ZooImpl._getAnimal();<br />
<br />
这三种模式都有它们的问题和局限性。要么是不够整洁，要么就是暴露了不必要的实现细节。 <br />
<strong><br />
协变 </strong><br />
协变返回模式就比较整洁、安全并且易于维护，它也不需要类型强制转换或特定的方法或字段： <br />
public AnimalImpl getAnimal(){<br />
return new AnimalImpl();<br />
}<br />
使用结果： <br />
ZooImpl.getAnimal().implMethod();<br />
<br />
<strong>使用泛型<br />
</strong>我们将从两个角度来了解泛型：使用泛型和构造泛型。我们不讨论List、Set和Map的显而易见的用法。知道泛型集合是强大的并且应该经常使用就足够了。 <br />
我们将讨论泛型方法的使用以及编译器推断类型的方法。通常这些都不会出问题，但是当出问题时，错误信息会非常令人费解，所以需要了解如何修复这些问题。 </p>
<p><strong>泛型方法<br />
</strong>除了泛型类型，Java 5还引入了泛型方法。在这个来自java.util.Collections的例子中，构造了一个单元素列表。新的List的元素类型是根据传入方法的对象的类型来推断的： </p>
<pre class="code">static <t></t> List<t></t> Collections.singletonList(T o)<br />示例用法：<br />public List<integer></integer> getListOfOne() {<br />    return Collections.singletonList(1);<br />}</pre>
<p>　　在示例用法中，我们传入了一个int。所以方法的返回类型就是List<integer></integer>。编译器把T推断为Integer。这和泛型类型是不同的，因为您通常不需要显式地指定类型参数。 <br />
这也显示了自动装箱和泛型的相互作用。类型参数必须是引用类型：这就是为什么我们得到的是List<integer></integer>而不是List<int></int>。 </p>
<p><strong>不带参数的泛型方法<br />
</strong>emptyList()方法与泛型一起引入，作为java.util.Collections中EMPTY_LIST字段的类型安全置换： </p>
<pre class="code">static <t></t> List<t></t> Collections.emptyList()<br />示例用法： <br />public List<integer></integer> getNoIntegers() {<br />    return Collections.emptyList();<br />}</pre>
<p>　　与先前的例子不同，这个方法没有参数，那么编译器如何推断T的类型呢？基本上，它将尝试使用一次参数。如果没有起作用，它再次尝试使用返回或赋值类型。在本例中，返回的是List<integer></integer>，所以T被推断为Integer。 <br />
如果在返回语句或赋值语句之外的位置调用泛型方法会怎么样呢？那么编译器将无法执行类型推断的第二次传送。在下面这个例子中，emptyList()是从条件运算符内部调用的： </p>
<pre class="code">public List<integer></integer> getNoIntegers() {<br />    return x ? Collections.emptyList() : null;<br />}</pre>
<p>　　因为编译器看不到返回上下文，也不能推断T，所以它放弃并采用Object。您将看到一个错误消息，比如：&ldquo;无法将List&lt; Object &gt;转换为List<integer></integer>。&rdquo; <br />
为了修复这个错误，应显式地向方法调用传递类型参数。这样，编译器就不会试图推断类型参数，就可以获得正确的结果： </p>
<pre class="code">return x ? Collections.<integer></integer>emptyList() : null;</pre>
<p>　　这种情况经常发生的另一个地方是在方法调用中。如果一个方法带一个List<string></string>参数，并且需要为那个参数调用这个传递的emptyList()，那么也需要使用这个语法。 </p>
<p><strong>集合之外<br />
</strong>　　这里有三个泛型类型的例子，它们不是集合，而是以一种新颖的方式使用泛型。这三个例子都来自标准的Java库： </p>
<ul>
    <li>Class<t></t><br />
    Class在类的类型上被参数化了。这就使无需类型强制转换而构造一个newInstance成为可能。 </li>
    <li>Comparable<t></t><br />
    Comparable被实际的比较类型参数化。这就在compareTo()调用时提供了更强的类型化。例如，String实现Comparable<string></string>。对除String之外的任何东西调用compareTo()，都会在编译时失败。 </li>
    <li>Enum<e extends=""></e>&gt;<br />
    Enum被枚举类型参数化。一个名为Color的枚举类型将扩展Enum<color></color>。getDeclaringClass()方法返回枚举类型的类对象，在这个例子中就是一个Color对象。它与getClass()不同，后者可能返回一个无名类。 </li>
</ul>
<p><strong>通配符<br />
</strong>泛型最复杂的部分是对通配符的理解。我们将讨论三种类型的通配符以及它们的用途。 <br />
首先让我们了解一下数组是如何工作的。可以从一个Integer[]为一个Number[]赋值。如果尝试把一个Float写到Number[]中，那么可以编译，但在运行时会失败，出现一个ArrayStoreException： </p>
<pre class="code">Integer[] ia = new Integer[5];<br />Number[] na = ia;<br />na[0] = 0.5; // compiles, but fails at runtime<br />如果试图把该例直接转换成泛型，那么会在编译时失败，因为赋值是不被允许的：<br />List<integer></integer> iList = new ArrayList<integer></integer>();<br />List<number></number> nList = iList; // not allowed<br />nList.add(0.5);</pre>
<p>　　如果使用泛型，只要代码在编译时没有出现警告，就不会遇到运行时ClassCastException。 </p>
<p><strong>上限通配符</strong><br />
我们想要的是一个确切元素类型未知的列表，这一点与数组是不同的。 <br />
List<number></number>是一个列表，其元素类型是具体类型Number。 <br />
List&lt;!--xtends Numb--&gt;是一个确切元素类型未知的列表。它是Number或其子类型。 </p>
<p><strong>上限<br />
</strong>如果我们更新初始的例子，并赋值给List&lt;!--xtends Numb--&gt;，那么现在赋值就会成功了： </p>
<pre class="code">List<integer></integer> iList = new ArrayList<integer></integer>();<br />List&lt;!--xtends Numb--&gt; nList = iList;<br />Number n = nList.get(0);<br />nList.add(0.5); // Not allowed</pre>
<p>　　我们可以从列表中得到Number，因为无论列表的确切元素类型是什么（Float、Integer或Number），我们都可以把它赋值给Number。 <br />
我们仍然不能把浮点类型插入列表中。这会在编译时失败，因为我们不能证明这是安全的。如果我们想要向列表中添加浮点类型，它将破坏iList的初始类型安全&mdash;&mdash;它只存储Integer。 <br />
通配符给了我们比数组更多的表达能力。 </p>
<p><strong>为什么使用通配符<br />
</strong>　　在下面这个例子中，通配符用于向API的用户隐藏类型信息。在内部，Set被存储为CustomerImpl。而API的用户只知道他们正在获取一个Set，从中可以读取Customer。 <br />
此处通配符是必需的，因为无法从Set<customerimpl></customerimpl>向Set<customer></customer>赋值： </p>
<pre class="code">public class CustomerFactory {<br />    private Set<customerimpl></customerimpl> _customers;<br />    public Set&lt;!--xtends Custom--&gt; getCustomers() {<br />        return _customers;<br />    }<br />}</pre>
<p><strong>通配符和协变返回</strong><br />
通配符的另一种常见用法是和协变返回一起使用。与赋值相同的规则可以应用到协变返回上。如果希望在重写的方法中返回一个更具体的泛型类型，声明的方法必须使用通配符： </p>
<pre class="code">public interface NumberGenerator {<br />    public List&lt;!--xtends Numb--&gt; generate();<br />}<br />public class FibonacciGenerator extends NumberGenerator {<br />    public List<integer></integer> generate() {<br />        ...<br />    }<br />}</pre>
<p>　　如果要使用数组，接口可以返回Number[]，而实现可以返回Integer[]。 </p>
<p><strong>下限 </strong><br />
我们所谈的主要是关于上限通配符的。还有一个下限通配符。List&lt;!--uper Numb--&gt;是一个确切&ldquo;元素类型&rdquo;未知的列表，但是可能是Mnumber，或者Number的超类型。所以它可能是一个List<number></number>或一个List&lt; Object&gt;。 <br />
下限通配符远没有上限通配符那样常见，但是当需要它们的时候，它们就是必需的。 </p>
<p><strong>下限与上限<br />
</strong></p>
<pre class="code">List&lt;!--xtends Numb--&gt; readList = new ArrayList<integer></integer>();<br />Number n = readList.get(0);<br /><br />List&lt;!--uper Numb--&gt; writeList = new ArrayList&lt; Object&gt;();<br />writeList.add(new Integer(5));</pre>
<p>　　第一个是可以从中读数的列表。 <br />
第二个是可以向其写数的列表。 </p>
<p><strong>无界通配符</strong><br />
最后，List列表的内容可以是任何类型，而且它与List&lt;!--xtends Obje--&gt;几乎相同。可以随时读取Object，但是不能向列表中写入内容。 </p>
<p><strong>公共API中的通配符 </strong><br />
总之，正如前面所说，通配符在向调用程序隐藏实现细节方面是非常重要的，但即使下限通配符看起来是提供只读访问，由于remove(int position)之类的非泛型方法，它们也并非如此。如果您想要一个真正不变的集合，可以使用java.util.Collection上的方法，比如 unmodifiableList()。 <br />
编写API的时候要记得通配符。通常，在传递泛型类型时，应该尝试使用通配符。它使更多的调用程序可以访问API。 <br />
通过接收List&lt;!--xtends Numb--&gt;而不是List<number></number>，下面的方法可以由许多不同类型的列表调用： </p>
<pre class="code">void removeNegatives(List&lt;!--xtends Numb--&gt; list);</pre>
<p><strong>构造泛型类型</strong><br />
现在我们将讨论构造自己的泛型类型。我们将展示一些例子，其中通过使用泛型可以提高类型安全性，我们还将讨论一些实现泛型类型时的常见问题。</p>
<p><strong>集合风格(Collection-like)的函数</strong><br />
第一个泛型类的例子是一个集合风格的例子。Pair有两个类型参数，而且字段是类型的实例： </p>
<pre class="code">public final class Pair {<br />    public final A first;<br />    public final B second;<br /><br />    public Pair(A first, B second) {<br />        this.first = first;<br />        this.second = second;<br />    }<br />}</pre>
<p>　　这使从方法返回两个项而无需为每个两种类型的组合编写专用的类成为可能。另一种方法是返回Object[]，而这样是类型不安全或者不整洁的。 <br />
在下面的用法中，我们从方法返回一个File和一个Boolean。方法的客户端可以直接使用字段而无需类型强制转换： </p>
<pre class="code">public Pair getFileAndWriteStatus(String path){<br />    // create file and status<br />    return new Pair(file, status);<br />}<br /><br />Pair result = getFileAndWriteStatus(&quot;...&quot;);<br />File f = result.first;<br />boolean writeable = result.second;</pre>
<p><strong>集合之外 </strong><br />
在下面这个例子中，泛型被用于附加的编译时安全性。通过把DBFactory类参数化为所创建的Peer类型，您实际上是在强制Factory子类返回一个Peer的特定子类型： </p>
<pre class="code">public abstract class DBFactory<t dbpeer="" extends=""></t> {<br />    protected abstract T createEmptyPeer();<br />    public List<t></t> get(String constraint) {<br />        List<t></t> peers = new ArrayList<t></t>();<br />        // database magic<br />        return peers;<br />    }<br />}<br />通过实现DBFactory<customer></customer>，CustomerFactory必须从createEmptyPeer()返回一个Customer：<br />public class CustomerFactory extends DBFactory<customer></customer>{<br /><br />    public Customer createEmptyPeer() {<br />        return new Customer();<br />    }<br />}</pre>
<p><strong>泛型方法<br />
</strong>　　不管想要对参数之间还是参数与返回类型之间的泛型类型施加约束，都可以使用泛型方法： <br />
例如，如果编写的反转函数是在位置上反转，那么可能不需要泛型方法。然而，如果希望反转返回一个新的List，那么可能会希望新List的元素类型与传入的List的类型相同。在这种情况下，就需要一个泛型方法： </p>
<p><br />
<t></t>List<t></t> reverse(List<t></t> list) </p>
<p><strong>具体化<br />
</strong>　　当实现一个泛型类时，您可能想要构造一个数组T[]。因为泛型是通过擦除(erasure)实现的，所以这是不允许的。 <br />
您可以尝试把Object[]强制转换为T[]。但这是不安全的。 </p>
<p><strong>具体化解决方案<br />
</strong>按照泛型教程的惯例，解决方案使用的是&ldquo;类型令牌&rdquo;，通过向构造函数添加一个Class<t></t>参数，可以强制客户端为类的类型参数提供正确的类对象： </p>
<pre class="code">public class ArrayExample<t></t> {<br />    private Class<t></t> clazz;<br /><br />    public ArrayExample(Class<t></t> clazz) {<br />        this.clazz = clazz;<br />    }<br /><br />    public T[] getArray(int size) {<br />        return (T[])Array.newInstance(clazz, size);<br />    }<br />}</pre>
<p>　　为了构造ArrayExample<string></string>，客户端必须把String.class传递给构造函数，因为String.class的类型是Class<string></string>。 <br />
拥有类对象使构造一个具有正确元素类型的数组成为可能。 </p>
<p><strong>结束语</strong><br />
总而言之，新的语言特性有助于从根本上改变Java。通过了解在什么场景下使用以及如何使用这些新特性，您将会编写出更好的代码。</p>
<p><strong>补充阅读 </strong></p>
<ul>
    <li><a href="http://java.sun.com/j2se/1.5.0/docs/guide/language/" target="_blank">Enhancements in JDK 5</a>&mdash;&mdash;JDK 5中新特性的官方列表 </li>
    <li><a href="http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf" target="_blank">Generics Tutorial</a> (PDF)&mdash;&mdash;Gilad Bracha的泛型教程 </li>
    <li>dev2dev <a href="http://dev2dev.bea.com/devcenters/" target="_blank">Developer Centers</a> </li>
</ul>
<p>&nbsp;</p>
<p><strong>原文出处:</strong>Experiences with the New Java 5 Language Features <a href="http://dev2dev.bea.com/pub/a/2005/09/java_5_features.html" target="_blank">http://dev2dev.bea.com/pub/a/2005/09/java_5_features.html</a> &lt;!--文章其他信息--&gt;</p>
<div class="dot001"><img src="http://dev2dev.bea.com.cn/images/_.gif" height="1" alt="" width="100%" /></div>
<p> </p>
<table cellspacing="0" border="0" cellpadding="3" width="100%">
    <tbody>
        <tr valign="bottom">
            <td height="20" colspan="2">&nbsp;<span class="h2b">作者简介</span></td>
        </tr>
        <tr>
            <td align="center" valign="top">&nbsp;</td>
            <td><a href="http://dev2dev.bea.com/pub/au/333" target="_blank">Jess Garms</a> 是BEA Systems中Javelin编译器团队的领导者。在此之前，Jess致力于BEA的 Java IDE，WebLogic Workshop。此外，他在密码学方面也具有丰富的经验。他还与他人合著了&ldquo;Professional Java Security&rdquo;，由Wrox出版社出版。</td>
        </tr>
        <tr>
            <td align="center" valign="top">&nbsp;</td>
            <td><a href="http://dev2dev.bea.com/pub/au/334" target="_blank">Tim Hanson</a> 是BEA Systems中Javelin编译器的架构师。Tim对BEA的Java编译器做了很多开发工作，该编译器是最早兼容1.5的实现之一。他曾经编写过许多其他的编译器，包括他在IBM时编写的CORBA/IDL编译器，以及XQuery编译器。</td>
        </tr>
    </tbody>
</table>
          <br/>
          <span style="color:red;">
            <a href="http://vertex.javaeye.com/blog/65820#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 27 Mar 2007 15:12:03 +0800</pubDate>
        <link>http://vertex.javaeye.com/blog/65820</link>
        <guid>http://vertex.javaeye.com/blog/65820</guid>
      </item>
      <item>
        <title>中文 Makefile 的教程</title>
        <author>simon_zhang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://vertex.javaeye.com">simon_zhang</a>&nbsp;
          链接：<a href="http://vertex.javaeye.com/blog/65053" style="color:red;">http://vertex.javaeye.com/blog/65053</a>&nbsp;
          发表时间: 2007年01月25日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <img src="http://blog.csdn.net/simon_21/aggbug/1492899.aspx" height="1" width="1" />
          <br/>
          <span style="color:red;">
            <a href="http://vertex.javaeye.com/blog/65053#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 25 Jan 2007 11:16:00 +0800</pubDate>
        <link>http://vertex.javaeye.com/blog/65053</link>
        <guid>http://vertex.javaeye.com/blog/65053</guid>
      </item>
      <item>
        <title>中文 Makefile 的教程</title>
        <author>simon_zhang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://vertex.javaeye.com">simon_zhang</a>&nbsp;
          链接：<a href="http://vertex.javaeye.com/blog/64913" style="color:red;">http://vertex.javaeye.com/blog/64913</a>&nbsp;
          发表时间: 2007年01月25日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          跟我一起写 Makefile<br /><br /><br />陈皓 (CSDN)<br /><br />概述<br />&mdash;&mdash;<br /><br />什么是makefile？或许很多Winodws的程序员都不知道这个东西，因为那些Windows的IDE都为你做了这个工作，但我觉得要作一个好的和 professional的程序员，makefile还是要懂。这就好像现在有这么多的HTML的编辑器，但如果你想成为一个专业人士，你还是要了解 HTML的标识的含义。特别在Unix下的软件编译，你就不能不自己写makefile了，会不会写makefile，从一个侧面说明了一个人是否具备完成大型工程的能力。<br /><br />因为，makefile关系到了整个工程的编译规则。一个工程中的源文件不计数，其按类型、功能、模块分别放在若干个目录中，makefile定义了一系列的规则来指定，哪些文件需要先编译，哪些文件需要后编译，哪些文件需要重新编译，甚至于进行更复杂的功能操作，因为 makefile就像一个Shell脚本一样，其中也可以执行操作系统的命令。<br /><br />makefile带来的好处就是&mdash;&mdash;&ldquo;自动化编译&rdquo;，一旦写好，只需要一个make命令，整个工程完全自动编译，极大的提高了软件开发的效率。make是一个命令工具，是一个解释makefile中指令的命令工具，一般来说，大多数的IDE都有这个命令，比如：Delphi的make，Visual C++的nmake，Linux下GNU的make。可见，makefile都成为了一种在工程方面的编译方法。<br /><br />现在讲述如何写 makefile的文章比较少，这是我想写这篇文章的原因。当然，不同产商的make各不相同，也有不同的语法，但其本质都是在&ldquo;文件依赖性&rdquo;上做文章，这里，我仅对GNU的make进行讲述，我的环境是RedHat Linux 8.0，make的版本是3.80。必竟，这个make是应用最为广泛的，也是用得最多的。而且其还是最遵循于IEEE 1003.2-1992 标准的（POSIX.2）。<br /><br />在这篇文档中，将以C/C++的源码作为我们基础，所以必然涉及一些关于C/C++的编译的知识，相关于这方面的内容，还请各位查看相关的编译器的文档。这里所默认的编译器是UNIX下的GCC和CC。<br /><br /><br /><br />关于程序的编译和链接<br />&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;<br /><br />在此，我想多说关于程序编译的一些规范和方法，一般来说，无论是C、C++、还是pas，首先要把源文件编译成中间代码文件，在Windows下也就是 .obj 文件，UNIX下是 .o 文件，即 Object File，这个动作叫做编译（compile）。然后再把大量的Object File合成执行文件，这个动作叫作链接（link）。<br /><br />编译时，编译器需要的是语法的正确，函数与变量的声明的正确。对于后者，通常是你需要告诉编译器头文件的所在位置（头文件中应该只是声明，而定义应该放在C/C++文件中），只要所有的语法正确，编译器就可以编译出中间目标文件。一般来说，每个源文件都应该对应于一个中间目标文件（O文件或是OBJ文件）。<br /><br />链接时，主要是链接函数和全局变量，所以，我们可以使用这些中间目标文件（O文件或是OBJ文件）来链接我们的应用程序。链接器并不管函数所在的源文件，只管函数的中间目标文件（Object File），在大多数时候，由于源文件太多，编译生成的中间目标文件太多，而在链接时需要明显地指出中间目标文件名，这对于编译很不方便，所以，我们要给中间目标文件打个包，在Windows下这种包叫&ldquo;库文件&rdquo;（Library File)，也就是 .lib 文件，在UNIX下，是Archive File，也就是 .a 文件。<br /><br />总结一下，源文件首先会生成中间目标文件，再由中间目标文件生成执行文件。在编译时，编译器只检测程序语法，和函数、变量是否被声明。如果函数未被声明，编译器会给出一个警告，但可以生成Object File。而在链接程序时，链接器会在所有的Object File中找寻函数的实现，如果找不到，那到就会报链接错误码（Linker Error），在VC下，这种错误一般是：Link 2001错误，意思说是说，链接器未能找到函数的实现。你需要指定函数的Object File.<br /><br />好，言归正传，GNU的make有许多的内容，闲言少叙，还是让我们开始吧。<br /><br /><br /><br />Makefile 介绍<br />&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;<br /><br />make命令执行时，需要一个 Makefile 文件，以告诉make命令需要怎么样的去编译和链接程序。<br /><br />首先，我们用一个示例来说明Makefile的书写规则。以便给大家一个感兴认识。这个示例来源于GNU的make使用手册，在这个示例中，我们的工程有8个C文件，和3个头文件，我们要写一个Makefile来告诉make命令如何编译和链接这几个文件。我们的规则是：<br />1）如果这个工程没有编译过，那么我们的所有C文件都要编译并被链接。<br />2）如果这个工程的某几个C文件被修改，那么我们只编译被修改的C文件，并链接目标程序。<br />3）如果这个工程的头文件被改变了，那么我们需要编译引用了这几个头文件的C文件，并链接目标程序。<br /><br />只要我们的Makefile写得够好，所有的这一切，我们只用一个make命令就可以完成，make命令会自动智能地根据当前的文件修改的情况来确定哪些文件需要重编译，从而自己编译所需要的文件和链接目标程序。<br /><br /><br />一、Makefile的规则<br /><br />在讲述这个Makefile之前，还是让我们先来粗略地看一看Makefile的规则。<br /><br />target ... : prerequisites ...<br />command<br />...<br />...<br /><br />target也就是一个目标文件，可以是Object File，也可以是执行文件。还可以是一个标签（Label），对于标签这种特性，在后续的&ldquo;伪目标&rdquo;章节中会有叙述。<br /><br />prerequisites就是，要生成那个target所需要的文件或是目标。<br /><br />command也就是make需要执行的命令。（任意的Shell命令）<br /><br />这是一个文件的依赖关系，也就是说，target这一个或多个的目标文件依赖于prerequisites中的文件，其生成规则定义在command中。说白一点就是说，prerequisites中如果有一个以上的文件比target文件要新的话，command所定义的命令就会被执行。这就是 Makefile的规则。也就是Makefile中最核心的内容。<br /><br />说到底，Makefile的东西就是这样一点，好像我的这篇文档也该结束了。呵呵。还不尽然，这是Makefile的主线和核心，但要写好一个Makefile还不够，我会以后面一点一点地结合我的工作经验给你慢慢到来。内容还多着呢。：）<br /><br /><br />二、一个示例<br /><br />正如前面所说的，如果一个工程有3个头文件，和8个C文件，我们为了完成前面所述的那三个规则，我们的Makefile应该是下面的这个样子的。<br /><br />edit : main.o kbd.o command.o display.o \<br />insert.o search.o files.o utils.o<br />cc -o edit main.o kbd.o command.o display.o \<br />insert.o search.o files.o utils.o<br /><br />main.o : main.c defs.h<br />cc -c main.c<br />kbd.o : kbd.c defs.h command.h<br />cc -c kbd.c<br />command.o : command.c defs.h command.h<br />cc -c command.c<br />display.o : display.c defs.h buffer.h<br />cc -c display.c<br />insert.o : insert.c defs.h buffer.h<br />cc -c insert.c<br />search.o : search.c defs.h buffer.h<br />cc -c search.c<br />files.o : files.c defs.h buffer.h command.h<br />cc -c files.c<br />utils.o : utils.c defs.h<br />cc -c utils.c<br />clean :<br />rm edit main.o kbd.o command.o display.o \<br />insert.o search.o files.o utils.o<br /><br />反斜杠（\）是换行符的意思。这样比较便于Makefile的易读。我们可以把这个内容保存在文件为&ldquo;Makefile&rdquo;或&ldquo;makefile&rdquo;的文件中，然后在该目录下直接输入命令&ldquo;make&rdquo;就可以生成执行文件edit。如果要删除执行文件和所有的中间目标文件，那么，只要简单地执行一下&ldquo;make clean&rdquo;就可以了。<br /><br />在这个makefile中，目标文件（target）包含：执行文件edit和中间目标文件（*.o），依赖文件（prerequisites）就是冒号后面的那些 .c 文件和 .h文件。每一个 .o 文件都有一组依赖文件，而这些 .o 文件又是执行文件 edit 的依赖文件。依赖关系的实质上就是说明了目标文件是由哪些文件生成的，换言之，目标文件是哪些文件更新的。<br /><br />在定义好依赖关系后，后续的那一行定义了如何生成目标文件的操作系统命令，一定要以一个Tab键作为开头。记住，make并不管命令是怎么工作的，他只管执行所定义的命令。make会比较targets文件和prerequisites文件的修改日期，如果prerequisites文件的日期要比targets文件的日期要新，或者target不存在的话，那么，make就会执行后续定义的命令。<br /><br />这里要说明一点的是，clean不是一个文件，它只不过是一个动作名字，有点像C语言中的lable一样，其冒号后什么也没有，那么，make就不会自动去找文件的依赖性，也就不会自动执行其后所定义的命令。要执行其后的命令，就要在make命令后明显得指出这个lable的名字。这样的方法非常有用，我们可以在一个makefile中定义不用的编译或是和编译无关的命令，比如程序的打包，程序的备份，等等。<br /><br /><br /><br />三、make是如何工作的<br /><br />在默认的方式下，也就是我们只输入make命令。那么，<br /><br />1、make会在当前目录下找名字叫&ldquo;Makefile&rdquo;或&ldquo;makefile&rdquo;的文件。<br />2、如果找到，它会找文件中的第一个目标文件（target），在上面的例子中，他会找到&ldquo;edit&rdquo;这个文件，并把这个文件作为最终的目标文件。<br />3、如果edit文件不存在，或是edit所依赖的后面的 .o 文件的文件修改时间要比edit这个文件新，那么，他就会执行后面所定义的命令来生成edit这个文件。<br />4、如果edit所依赖的.o文件也存在，那么make会在当前文件中找目标为.o文件的依赖性，如果找到则再根据那一个规则生成.o文件。（这有点像一个堆栈的过程）<br />5、当然，你的C文件和H文件是存在的啦，于是make会生成 .o 文件，然后再用 .o 文件生命make的终极任务，也就是执行文件edit了。<br /><br />这就是整个make的依赖性，make会一层又一层地去找文件的依赖关系，直到最终编译出第一个目标文件。在找寻的过程中，如果出现错误，比如最后被依赖的文件找不到，那么make就会直接退出，并报错，而对于所定义的命令的错误，或是编译不成功，make根本不理。make只管文件的依赖性，即，如果在我找了依赖关系之后，冒号后面的文件还是不在，那么对不起，我就不工作啦。<br /><br />通过上述分析，我们知道，像clean这种，没有被第一个目标文件直接或间接关联，那么它后面所定义的命令将不会被自动执行，不过，我们可以显示要make执行。即命令&mdash;&mdash;&ldquo;make clean&rdquo;，以此来清除所有的目标文件，以便重编译。<br /><br />于是在我们编程中，如果这个工程已被编译过了，当我们修改了其中一个源文件，比如file.c，那么根据我们的依赖性，我们的目标file.o会被重编译（也就是在这个依性关系后面所定义的命令），于是file.o的文件也是最新的啦，于是file.o的文件修改时间要比edit要新，所以edit也会被重新链接了（详见edit目标文件后定义的命令）。<br /><br />而如果我们改变了&ldquo;command.h&rdquo;，那么，kdb.o、command.o和files.o都会被重编译，并且，edit会被重链接。<br /><br /><br />四、makefile中使用变量<br /><br />在上面的例子中，先让我们看看edit的规则：<br /><br />edit : main.o kbd.o command.o display.o \<br />insert.o search.o files.o utils.o<br />cc -o edit main.o kbd.o command.o display.o \<br />insert.o search.o files.o utils.o<br /><br />我们可以看到[.o]文件的字符串被重复了两次，如果我们的工程需要加入一个新的[.o]文件，那么我们需要在两个地方加（应该是三个地方，还有一个地方在 clean中）。当然，我们的makefile并不复杂，所以在两个地方加也不累，但如果makefile变得复杂，那么我们就有可能会忘掉一个需要加入的地方，而导致编译失败。所以，为了makefile的易维护，在makefile中我们可以使用变量。makefile的变量也就是一个字符串，理解成 C语言中的宏可能会更好。<br /><br />比如，我们声明一个变量，叫objects, OBJECTS, objs, OBJS, obj, 或是 OBJ，反正不管什么啦，只要能够表示obj文件就行了。我们在makefile一开始就这样定义：<br /><br />objects = main.o kbd.o command.o display.o \<br />insert.o search.o files.o utils.o<br /><br />于是，我们就可以很方便地在我们的makefile中以&ldquo;$(objects)&rdquo;的方式来使用这个变量了，于是我们的改良版makefile就变成下面这个样子：<br /><br />objects = main.o kbd.o command.o display.o \<br />insert.o search.o files.o utils.o<br /><br />edit : $(objects)<br />cc -o edit $(objects)<br />main.o : main.c defs.h<br />cc -c main.c<br />kbd.o : kbd.c defs.h command.h<br />cc -c kbd.c<br />command.o : command.c defs.h command.h<br />cc -c command.c<br />display.o : display.c defs.h buffer.h<br />cc -c display.c<br />insert.o : insert.c defs.h buffer.h<br />cc -c insert.c<br />search.o : search.c defs.h buffer.h<br />cc -c search.c<br />files.o : files.c defs.h buffer.h command.h<br />cc -c files.c<br />utils.o : utils.c defs.h<br />cc -c utils.c<br />clean :<br />rm edit $(objects)<br /><br /><br />于是如果有新的 .o 文件加入，我们只需简单地修改一下 objects 变量就可以了。<br /><br />关于变量更多的话题，我会在后续给你一一道来。<br /><br /><br />五、让make自动推导<br /><br />GNU的make很强大，它可以自动推导文件以及文件依赖关系后面的命令，于是我们就没必要去在每一个[.o]文件后都写上类似的命令，因为，我们的make会自动识别，并自己推导命令。<br /><br />只要make看到一个[.o]文件，它就会自动的把[.c]文件加在依赖关系中，如果make找到一个whatever.o，那么whatever.c，就会是whatever.o的依赖文件。并且 cc -c whatever.c 也会被推导出来，于是，我们的makefile再也不用写得这么复杂。我们的是新的makefile又出炉了。<br /><br /><br />objects = main.o kbd.o command.o display.o \<br />insert.o search.o files.o utils.o<br /><br />edit : $(objects)<br />cc -o edit $(objects)<br /><br />main.o : defs.h<br />kbd.o : defs.h command.h<br />command.o : defs.h command.h<br />display.o : defs.h buffer.h<br />insert.o : defs.h buffer.h<br />search.o : defs.h buffer.h<br />files.o : defs.h buffer.h command.h<br />utils.o : defs.h<br /><br />.PHONY : clean<br />clean :<br />rm edit $(objects)<br /><br />这种方法，也就是make的&ldquo;隐晦规则&rdquo;。上面文件内容中，&ldquo;.PHONY&rdquo;表示，clean是个伪目标文件。<br /><br />关于更为详细的&ldquo;隐晦规则&rdquo;和&ldquo;伪目标文件&rdquo;，我会在后续给你一一道来。<br /><br /><br />六、另类风格的makefile<br /><br />即然我们的make可以自动推导命令，那么我看到那堆[.o]和[.h]的依赖就有点不爽，那么多的重复的[.h]，能不能把其收拢起来，好吧，没有问题，这个对于make来说很容易，谁叫它提供了自动推导命令和文件的功能呢？来看看最新风格的makefile吧。<br /><br />objects = main.o kbd.o command.o display.o \<br />insert.o search.o files.o utils.o<br /><br />edit : $(objects)<br />cc -o edit $(objects)<br /><br />$(objects) : defs.h<br />kbd.o command.o files.o : command.h<br />display.o insert.o search.o files.o : buffer.h<br /><br />.PHONY : clean<br />clean :<br />rm edit $(objects)<br /><br />这种风格，让我们的makefile变得很简单，但我们的文件依赖关系就显得有点凌乱了。鱼和熊掌不可兼得。还看你的喜好了。我是不喜欢这种风格的，一是文件的依赖关系看不清楚，二是如果文件一多，要加入几个新的.o文件，那就理不清楚了。<br /><br /><br />七、清空目标文件的规则<br /><br />每个Makefile中都应该写一个清空目标文件（.o和执行文件）的规则，这不仅便于重编译，也很利于保持文件的清洁。这是一个&ldquo;修养&rdquo;（呵呵，还记得我的《编程修养》吗）。一般的风格都是：<br /><br />clean:<br />rm edit $(objects)<br /><br />更为稳健的做法是：<br /><br />.PHONY : clean<br />clean :<br />-rm edit $(objects)<br /><br />前面说过，.PHONY意思表示clean是一个&ldquo;伪目标&rdquo;，。而在rm命令前面加了一个小减号的意思就是，也许某些文件出现问题，但不要管，继续做后面的事。当然，clean的规则不要放在文件的开头，不然，这就会变成make的默认目标，相信谁也不愿意这样。不成文的规矩是&mdash;&mdash;&ldquo;clean从来都是放在文件的最后&rdquo;。<br /><br /><br />上面就是一个makefile的概貌，也是makefile的基础，下面还有很多makefile的相关细节，准备好了吗？准备好了就来。<br /><br />Makefile 总述<br />&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;<br /><br />一、Makefile里有什么？<br /><br />Makefile里主要包含了五个东西：显式规则、隐晦规则、变量定义、文件指示和注释。<br /><br />1、显式规则。显式规则说明了，如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出，要生成的文件，文件的依赖文件，生成的命令。<br /><br />2、隐晦规则。由于我们的make有自动推导的功能，所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile，这是由make所支持的。<br /><br />3、变量的定义。在Makefile中我们要定义一系列的变量，变量一般都是字符串，这个有点你C语言中的宏，当Makefile被执行时，其中的变量都会被扩展到相应的引用位置上。<br /><br />4、文件指示。其包括了三个部分，一个是在一个Makefile中引用另一个Makefile，就像C语言中的include一样；另一个是指根据某些情况指定Makefile中的有效部分，就像C语言中的预编译#if一样；还有就是定义一个多行的命令。有关这一部分的内容，我会在后续的部分中讲述。<br /><br />5、注释。Makefile中只有行注释，和UNIX的Shell脚本一样，其注释是用&ldquo;#&rdquo;字符，这个就像C/C++中的&ldquo;//&rdquo;一样。如果你要在你的Makefile中使用&ldquo;#&rdquo;字符，可以用反斜框进行转义，如：&ldquo;\#&rdquo;。<br /><br />最后，还值得一提的是，在Makefile中的命令，必须要以[Tab]键开始。<br /><br /><br />二、Makefile的文件名<br /><br />默认的情况下，make命令会在当前目录下按顺序找寻文件名为&ldquo;GNUmakefile&rdquo;、&ldquo;makefile&rdquo;、&ldquo;Makefile&rdquo;的文件，找到了解释这个文件。在这三个文件名中，最好使用&ldquo;Makefile&rdquo;这个文件名，因为，这个文件名第一个字符为大写，这样有一种显目的感觉。最好不要用 &ldquo;GNUmakefile&rdquo;，这个文件是GNU的make识别的。有另外一些make只对全小写的&ldquo;makefile&rdquo;文件名敏感，但是基本上来说，大多数的make都支持&ldquo;makefile&rdquo;和&ldquo;Makefile&rdquo;这两种默认文件名。<br /><br />当然，你可以使用别的文件名来书写Makefile，比如：&ldquo;Make.Linux&rdquo;，&ldquo;Make.Solaris&rdquo;，&ldquo;Make.AIX&rdquo;等，如果要指定特定的Makefile，你可以使用make的&ldquo;- f&rdquo;和&ldquo;--file&rdquo;参数，如：make -f Make.Linux或make --file Make.AIX。<br /><br /><br />三、引用其它的Makefile<br /><br />在Makefile使用include关键字可以把别的Makefile包含进来，这很像C语言的#include，被包含的文件会原模原样的放在当前文件的包含位置。include的语法是：<br /><br />include &lt;filename&gt;<br /><br />filename可以是当前操作系统Shell的文件模式（可以保含路径和通配符）<br /><br />在include 前面可以有一些空字符，但是绝不能是[Tab]键开始。include和&lt;filename&gt;可以用一个或多个空格隔开。举个例子，你有这样几个Makefile：a.mk、b.mk、c.mk，还有一个文件叫foo.make，以及一个变量$(bar)，其包含了e.mk和f.mk，那么，下面的语句：<br /><br />include foo.make *.mk $(bar)<br /><br />等价于：<br /><br />include foo.make a.mk b.mk c.mk e.mk f.mk<br /><br />make 命令开始时，会把找寻include所指出的其它Makefile，并把其内容安置在当前的位置。就好像C/C++的#include指令一样。如果文件都没有指定绝对路径或是相对路径的话，make会在当前目录下首先寻找，如果当前目录下没有找到，那么，make还会在下面的几个目录下找：<br /><br />1、如果make执行时，有&ldquo;-I&rdquo;或&ldquo;--include-dir