2006年06月15日

几个鉴别DDR 的网址:

金士顿(Kingston):

http://www.bbstag.com/s/2156/2157/06-05-23/155049.html

http://itbbs.pconline.com.cn/topic.jsp?tid=2333080&topicPage=1

2006年05月29日

写动态DIV的脚本:

<script language="javascript">
   function scroll( number )
   {    
    switch( number )
    {
     case 1 :
      //document.all.divGrid2.scrollTop = document.all.divGrid1.scrollTop;
      //document.all.divGrid3.scrollTop = document.all.divGrid1.scrollTop;
      break;
     case 2 :
      document.all.divGridHeader.scrollLeft = document.all.divGrid.scrollLeft;
      document.all.scrollLeftLocation.value = document.all.divGrid.scrollLeft;
      //document.all.divGrid3.scrollTop = document.all.divGrid2.scrollTop;
      break;
     case 3 :
      //document.all.divGrid1.scrollTop = document.all.divGrid3.scrollTop;
      //document.all.divGrid2.scrollTop = document.all.divGrid3.scrollTop;
      break;
     default :
      break;
    } 
   }
  </script>

 <script language="javascript">
   function lockhead(cell)
   {
    var table=cell.parentElement.parentElement;
    while(table&&table.tagName.toLowerCase()!="table")
    table=table.parentElement;
    var _div=table.parentElement;
    return _div.scrollTop+(cell.parentElement.rowIndex)*(parseInt(table.border)+parseInt(table.cellSpacing)-1);
   }
    </script>
    <script language="javascript">
   function LockHead(table,cnt)
   {

    if(table&&table.rows.length>=cnt)
    {
     for(k=0;k<cnt;k++)
     {
      for(i=0;i<table.rows[k].cells.length;i++)
      {
       table.rows[k].cells[i].className="lockedhead";
      }
     }
    }
   }
   LockHead(Form1.all.drgMenuMasterDet,1);
    </script>

 <DIV id="divGridHeader" style="OVERFLOW-Y: hidden; Z-INDEX: 100; OVERFLOW-X: scroll"> </DIV>
           <DIV id="divGrid" onkeydown="gridDown(‘drgShobumon’)" style="OVERFLOW-Y: scroll; OVERFLOW-X: scroll" onscroll="scroll(2)" ms_positioning="FlowLayout"><</DIV>

2006年02月09日

这一段时间(06.2.4--06.2.9)总结:2.4号来的时候通知我的实用期过了,通过上级批准,从今天起开始进入试用期,心情非常之好,不光是一个试用的问题.好境不长,我的生活开始有了转折,从2.7号开始被调入.NET组,心情非常之低落,原因很多,一个从一个自己的专业调到自己陌生的地方,有一种孤单无助的感觉,那种感觉真的像被世界遗弃了一样,到今天一直也无法进入工作状态,第一进入了一个陌生的境界,自己根本不知道从何入手,第二,从一个好的组,被调出来不知是好是坏,未来也不知会怎么样.

2006年02月07日

開発編では、統一プロセス(UP)を使って説明を進めていきます。今回と次回(最終回)の説明には、UPベースの開発プロセスマップ(図1)をガイド役として登場させます。ここでこの図の見方について説明しましょう。現在説明している個所は黄色で範囲を示します。

図1 UPベースの開発プロセスマップ

- PR - 

 図1は、要求分析ディシプリン中の「(1)システム範囲を決める」という観点での作業を説明する際のガイドとなります。さて、この図で注意していただきたいことがあります。それは、「(1)システム範囲を決める」という作業が、「(3)要求の概念構造のモデル化」や「(4)アーキテクチャのプロト検証」といったほかの作業と並行して進められているということです。このように反復開発の本質は、実は並行的に進めていくものなのです。しかし、説明を並行して進めるすべはありませんので、(1)~(6)の順に進めていきます。この図を見ながら頭の中で常にどの部分が並行で進んでいるかというイメージをしっかり持ちながら読み進めてください。

 ここで反復プロセスについてもう少し突っ込んだ話をしましょう。反復プロセスは、ウォーターフォール型開発の繰り返し版と思われがちです。なぜなら反復プロセスの説明図の中では分析、設計、実装、テストを繰り返しているかのように描かれているからです。しかし、実際にはそうではありません。反復開発のイメージは、責務の異なる複数チームによる並行開発といったニュアンスの方が適切です。図2は、一般的な反復開発でとられるメンバーのロール(役割)別チーム構成でUPを分割し、説明したものです。

図2 チーム構成別UP作業分割

 この図で示すように、それぞれのチームが並行的に協調しあいながら作業を進めていきます。図1図2をまとめたものを表1に示します。なお、今回解説するのは(1)~(3)までです。

ディシプリン名
UP(統一プロセス)
ディシプリン名
(本連載での名前)
チーム名 作業内容
(作業観点)
要求と分析 ・システム分析
・要求分析
システム分析チーム (1) システム範囲を決める
(2) システム要求を定め、管理する
(3) 要求の概念構造のモデル化
設計
(前半)
システム設計の初期段階 アーキテクトチーム (4) アーキテクチャのプロト検証
(5) コア・アーキテクチャの確立
設計
(後半)
システム設計の後半 設計・実装チーム (6) システムの詳細設計・実装・テスト
表1 チームによるディシプリンの実施

 この表のように、それぞれのディシプリンは、そのディシプリンを行う責務を持つチームにより作業内容が実施されます。実際の開発では、各ディシプリンにおける作業内容はこれだけではありませんので、「作業を行う観点」ととらえていただいた方が適切かもしれません。

 では、この表で注意していただきたい個所だけを説明しましょう。まず、「(3)要求の概念構造のモデル化」作業には、システム分析チームだけではなくアーキテクトチームからも参加することで、システム分析モデルを、要求分析者とアーキテクトの共通の知識としなければなりません。

 次は、設計(システム設計)について説明します。設計(システム設計)の前半は、アーキテクトチームによって進められます。アーキテクトチームはIT技術に優れた少人数の精鋭メンバーでなければなりません。比較的大きなシステム開発でも、アーキテクト1人と補佐1~2人程度で構成することを検討してみてください。

 設計(システム設計)の後半、比較的大きなシステムにおいては、サブシステムに分割され複数のチームで開発を進めることになります。よって、分割されたサブシステムに対して、システム設計者、実装者、テスターがアサインされるようになるでしょう。この段階では、アーキテクトは、それぞれのサブシステムのインターフェイス部分とアーキテクチャを厳密にコントロールする役割となり、システム分析者は、システムのテストについてコントロールする役割となります。
 
 このような考え方は、小規模開発でも利用できます。その場合、要求分析、システム分析、システム設計は、開発者が問題領域を見る際の「観点」と考えて各作業を実施することになります。つまりは、システム開発において対象問題を、要求の洗い出しの視点、要求構造の分析的な視点、設計的な視点、という3つの視点で具現化していくのです。

 ではここから、それぞれの開発作業について、どのようにUMLのモデルが作成されるのか説明していきます。その前に読者にここで一言お断りがあります。この説明は、モデリングに関する部分に重点を置いた説明です。開発作業全体を説明しているわけではありません。その点、ご注意を。

 それでは、表1の作業内容にそって解説を行っていきます。

(1) システム範囲を決める

 これは、要求分析という観点で、要求分析チームが中心となって行う作業です(図3)。

図3 システム範囲を定める

 システム範囲を決めるということは、戦略分析の成果物に基づき、どのようなシステムをいつまでに開発し、業務モデルにどのように適合させることでビジネス全体の最適化をどう図るかを決めることです。また、ビジネスが急速な勢いで発展している中、リリース時期は非常に重要なものとなりますので、システムに要求される課題に対して優先順位を付け、最優先すべき要求をいつまでに作成すべきか、投資効果に配慮しどこまで作るべきか判断を下さなければなりません。
 
 つまり、前回説明した子どものオモチャ・オネダリモデルの「分析とは、現状を理解し、戦略を立て、財布(能力)と相談する」というところの「財布と相談する」という観点がここでは重要となります。

では、前回の図を使って説明しましょう。図4を見てください。この作業で考えるべき点は、アクティビティとシステムの間でやりとりされるインターフェイス(赤色のだ円)部分となります。業務分析フェイズで明らかになったシステム導入戦略について開発期間・開発コストを加味して現実的な解を探るわけです。

図4 コンピュータシステムによるビジネス最適化

 RacingObjective社の例では、社長と話し合った結果、できるだけシンプルにビジネスとして重要なものからシステム化の範囲にしていきたいという意向がありました。

 まずは、システムに対する要求をシンプルに示す文書を書きます。ここでは、この文章のことを「ビジネスIT化シナリオ」と呼びます。この文章の目的は、システムにとって最も重要な課題・要求をコンセプトベースで記述することです。これにはダラダラとすべての要求を書く必要はありません。開発者全員で、何が最もシステムにとって重要で死守すべきことなのかを、いつでも再確認するためのものです。例えば、システム開発過程では、システムに対する要求がどんどん累積されるうちに、結局何をやりたかったのかという点が不明確になることがあります。このようなときに、かっちりとシステムのコンセプトを押さえている「ビジネスIT化シナリオ」は役に立つことになるでしょう。また、ユースケースモデルは、この文書をベースに作成されることになります。

販売管理システム
ビジネスIT化シナリオ

RacingObjective社

◎ビジネスシナリオ

 今回のシステム開発の大きな狙いは、顧客からの受注工程において商品予約をスピーディに行うよう改善することである。顧客から部品を予約された場合に、自社在庫を確認、在庫がなければ即本社に問い合わせを行い、他支社が保有する在庫を確認できるようにIT化を図る。そうすることで、顧客からの「商品がいつ届くかよく分からない」という苦情や、「営業担当が在庫商品を確認する業務処理がまちまちで業務過誤を犯しやすい」といった問題を早急に解決したい。さらに、顧客からの入金を経理が確認した段階でシステムにその情報を入力し、商品発送が可能状態になったことをシステムにより在庫部品センターの担当者に通知できるようにする。
 
◎今後の方向性

 今後のビジネス拡大を狙って、各支社に存在する在庫部品センターはITシステムによる情報統合を急ぐ必要がある。このシステム開発は、このような在庫部品センターの情報統合の第1ステップとして実現する狙いがあるため、作成されるITシステムは今後在庫管理面で大幅な拡張を予定している。また、来年には、顧客からのWeb予約を導入する予定である。さらに、今後は、経理システムの日時売り上げオンライン化なども考えたい。
 
◎要求されるレスポンス

・自社在庫の確認予約は10秒以内
・他支社が保有する在庫問い合わせ処理は30秒以内

◎ビジネス課題

 このシステムを導入するに当たって、各支社の在庫部品センターは、自社からの部品予約だけではなくほかの支社からの予約受け付けを行うようになる。本社としては、支社間の連携による部品在庫の振り替えについて、どのような方法で行うのか平成15年11月末までに具体化しなければならない。

図5 ビジネスIT化シナリオ



(注)本内容においては、開発プロセスやマネジメントにフォーカスした内容ではないため、開発範囲をあまり明確にせずに説明を進めています。一応、ユースケースにて開発範囲は示していますが、その後の設計で紹介するモデルは、要求範囲の一部分であったりします。また、実際の開発では、方向付けフェイズの段階で、ユーザーとディスカッションすることによって、性能要求・拡張要求・再利用性の要求など(これらを非機能要求といいます)を明確にしていきます。そして、その非機能要求を満たすモデルを作ることが設計の目標となるのですが、ここでは割愛しています。あくまでモデルを中心にしながらの説明に終始していることを理解していただき、開発・勉強の参考にしてください。

(2) システムの要求を定め・管理する

 この作業も、要求分析という観点で、要求分析チームが中心となって行う作業です(図6)。システムの利用者(ユーザー)が要求分析チームの一員として入っていることが重要となります。

図6 システムの要求を定め・管理する

 ここでようやくユースケースモデルの登場です。ユースケースモデルとは、ユースケース図と、ユースケース記述をセットにしたものをいいます。ユースケース図は、システム開発におけるユーザーから見たシステムの「使い方の例(ユースケース)」を示したものです。ユースケース図の詳しい説明は、下記を参考にしてください。

【改訂版】初歩のUML 第8回 顧客の要求をユースケースに反映させる

 ユースケース記述は、先に挙げた「ビジネスIT化シナリオ」をもう少し詳細化したものとなります。図7にユースケース図、図8にユースケース記述の例を示します。商品マスタなどの商品マスタメンテナンス機能は別途ユースケースにまとめていきます。図7は、まだ初期段階のユースケースモデルと考えてください。

図7 RacingObjective社 販売管理システム(ユースケース図)



ユースケース名 UC0002:商品を予約する
概要 お客様から注文部品を受け付ける
アクター 営業担当
事前条件 すでに登録済のユーザであること
メインフロー
    1.アクターは予約したい商品リストを入力する

    2.システムは自社内在庫を確認して、在庫がある商品に在庫ありマークを入れる

    3.アクターは、すべての予約商品に在庫マークがつけられていれば、6へ

    4.アクターは他支店在庫を問い合わせする

    5.システムは、他社の在庫を確認し、在庫が他社にある場合は、在庫ありマークを付与し、その在庫の問い合わせ支社・担当者名を表示する

    6.アクターは、表示された商品予約の在庫状況と顧客からの依頼を受けつけ、予約したい商品について、再度選択できる

    7.アクターは、予約を中止したい場合は、このフローをぬける

    8.アクターは、顧客Noを入力し予約を実行する
事後条件 アクターが予約を実行した場合のみ、商品が予約されていること。
特記事項 システムが商品予約する期間は、商品予約画面で変更ができるようにすること。デフォルトで3週間とする。アクターが本ユースケース実行時に、予約途中の商品は仮予約となる。仮予約の解除は、このユースケースが終了した場合と、適切なタイムオーバーになった時(警告表示)とする。
図8 「商品を予約する」ユースケース記述の例

(3) 要求の概念構造のモデル化

 この作業は、システム分析という観点で、要求分析チームとアーキテクトチームの間で行われる作業です(図9)。

図9 要求の概念構造のモデル化

 要求の概念構造のモデル化とは、名前のとおり要求に潜んでいる本質的な概念構造を明らかにすることで、ユーザー、要求分析者、アーキテクトといったさまざまな利害関係者のシステムに対する共通な分析モデルを作り上げることです。実装のためのモデル化が目的ではありません。よってこのモデルを評価する場合は、いかに要求に潜む概念構造をシンプルに表現しているかということに尽きます。
 
 図10がシステム分析で作られるクラス図(分析モデル)です。分析モデルとは、システム分析の段階で要求の概念構造のモデル化を目的として作成されるクラス図を中心としたダイアグラムのことです。分析モデルには、クラス図のほかに、コラボレーション図(またはシーケンス図)、オブジェクト図、ステートチャート図などが使用されることがあります。クラス図以外の図は、必要であれば作成するという程度に考えておいてください。

図10 分析モデル(クラス図) クリックすると拡大

 今回の開発事例におけるクラス図の特徴としては、ビジネス分析時に作成したクラス図をすべて反映したものとなりますが、通常は、ビジネス分析時に作成したクラス図の中でシステム化対象となる部分にフォーカスしたクラス図になることを覚えておいてください。

 分析モデルでは、要求の概念構造を明確にするために、クラスの属性要求に必要となるデータをクラスの操作に入れています。このとき、操作についてあまり詳細な内容を入れるとモデルがやたらと複雑になったり、本質的に強調したい概念が分かりづらくなったりしますので注意してください。

 次に、注文された商品が顧客に配達されるまでにどのようなライフサイクルがあるのかを考察しましょう。図11に注文商品のライフサイクルをステートチャート図により示しています。ステートチャート図はUMLのダイアグラムの1つで、このようにシステムまたはオブジェクトの状態をとらえる際に使います。

図11 注文商品のライフサイクル(ステートチャート図)

 ステートチャート図は初めて登場しましたので、ここで図11を使って簡単に説明します。まずステートチャート図(状態図)は、「(1)状態」とその遷移をとらえるための図です。状態が次の状態へ遷移するためには何らかのきっかけが必要となりますが、そのことを「(2)イベント」と呼びます。イベントが発生する際に、何らかの「(3)アクション」が必要な場合、「イベント名/アクション名」と書きます。アクションは業務処理であったりシステムに要求される機能であったりします。イベントには「もしXXだったらイベントを実行する」といった条件が付けられます。これは、ガード条件と呼ばれ「[ガード条件]イベント名/アクション名」というようにイベント名の前に“[ガード条件]”を書き、そこに書かれた文章が真(True)であればアクションを実行するという表現もできます。状態の中には「(4)入状(Entry)」、「Do」、「退状(Exit)」といったアクションが書けます。ステートチャート図は、「(5)開始状態」で始まり「(6)終了状態」で終わります。

 では、図中の予約状態から発送済み状態に遷移する個所で具体的な説明をしましょう。ここでは、予約状態から発送済み状態へ遷移するためには、入金確認(顧客からの入金を確認)イベントが来なければならないことが分かります。また、入金確認イベントが来たときに、商品発送という業務アクションを実施しなければなりません。発送済み状態から抜け出る可能性として、顧客が商品を返品するというケースもありますので、返品イベントも定義しています。

コラム:ステートチャート図とアクティビティ図の違い
 ステートチャート図とアクティビティ図の違いがよく分からなくなる人がいます。一時的な状態(イベント名が書かれていないイベントの遷移)がステートチャート図では描けるからです。ステートチャート図では、このような遷移をラムダ遷移というのですが、ラムダ遷移が多用されるステートチャート図の状態は、アクションのみの定義となってしまいます。このような図は、アクティビティとさほど違いがないようにみえます。しかしながら、やはりステートチャート図の関心事は、対象領域における状態の把握と、状態と状態の遷移にあります。アクションは、発見された状態の中の入状(Entry)、最中(Do)、出状(Exit)にて、どのようなアクションを行うのかということを記述するものですから、アクションについては補足的な扱いとなります。一方、アクティビティ図は、アクション(つまりはアクティビティ)とアクションの流れに着目するものですから、両者には大きな違いがあるのです。

 しかし、なぜステートチャート図をここで利用したのでしょうか。その理由は、今回の要求の中で見落とされがちになるのが注文された商品内容の管理状態であるからです。商品を販売し発送した後においても、注文された商品は履歴として残さなければなりません。うっかりこのことを忘れてしまっては、間違った注文オブジェクトの管理方法を設計してしまうでしょう。そこで、要求分析の中で、注文商品のライフサイクルを明らかにしておき、設計のリファレンスとして使用していくのです。

 次は、図10をユースケースに対応させてみましょう。

 図10は、ユースケースを動かすという観点から考えると、適切なものではありません。そこで、アクターからイベントを受け取る役割オブジェクト(Boundary)、制御を受け持つオブジェクト(Control)、データ管理を行うオブジェクト(Entity)というようにユースケースを実行する際に、ソフトウェア実現のための役割ごとに類別化する方法があります。この方法はロバストネス分析という手法として知られています。図10をこのような役割の観点で分けていくと、アクターからイベントを受け取る役割オブジェクト(Boundary)、つまりはユーザーインターフェイスを実現するオブジェクトが欠落しているように思えます。そこで、図12にロバストネス風クラス図を示します。この図は、ユースケースのサービスを満たすためにBoundaryを設け、さらに、EntityとControlにクラスを類別化したものです。また、自社在庫がない場合、本社に問い合わせするための仕組みをモデル上で表すために「本社商品在庫管理」というクラスを置いています。なお、Boundaryクラスは、クラス名から想像できる操作については省略しています。これは、Entityクラスの属性Set、Getを行う操作を省略としているものと同じ感覚で考えてください。


図12 ソフトウェア実現のための役割にクラスを類別化する(クラス図)

 今回はここまでです。次回は、いよいよ最終回です。

一。主题:
编写优质无错的代码—讨论会主题。
相信每个程序员都有这种希望,谁都不愿意自己写出来的代码在release之后出错,需要不停的修改维护。但是,主持人提出了这样一个问题:"编写优质无错代码是否必要?" 为什么呢?我稍微解释一下。在项目的时间很紧张的时候,是按期完成任务重要,还是代码的稳定性,优质无错重要呢?

主持人提出的四个具体问题是:
1、编写优质无错的代码的代价是什么?
2、代码的质量重要还是编写效率重要?
3、在压力的情况下,你会牺牲质量来提高效率么? 
4、编写优质无错的代码是否意味着效率的降低?

对于第一个问题,编写优质无错代码的代价当然是时间,不过随着编程人员的经验逐渐丰富,所需要的时间也逐渐减少。
对于第二个问题,代码的质量比编写效率重要。当你花了1周时间写出来的代码需要你花一个月或者更长的时间去debug, 去修改错误,这种效率的损失是得不偿失的。
对于第三个问题, 这需要看项目经理或者产品经理的态度和专业精神了。如果在一个专业的项目经理或者产品经理的指挥下,当然是首先保证质量其次提高效率。而对于某些项目经理或者产品经理来说,按时完成任务是最重要的,他们往往不在乎在软件发布之后花比开发时间长得多的时间去修改程序,维护错误。因为,对于他们来说,首先是要完成任务,好给上级领导交差,至于后期维护,就是另外一个任务了,维护花的时间多,正说明了他这个项目的复杂性和难度。而对于开发人员来讲,所希望的则正好相反。开发人员不喜欢花太多的时间在一个烂摊子上。所以,在讨论会上,大家纷纷表示,应该让项目经理或者产品经理也来听一听这个讨论会:-)。
对于第四个问题,当然优质无错代码不是意味着效率的降低,而是正好相反,对提高效率有很好的促进作用。一个版本发布之后,如果因为错误太多,开发人员不得不去花很多时间修改bug, 甚至要从系统的体系结构方面去做大的改动,重新编写部分代码,这种效率的降低才是更大,更不能承受的。而且,花了太多的时间在老版本的维护上,必然影响到新版本的工程进度,直接影响到整个产品线的质量和进度,严重的甚至会毁掉整个产品。

对于这一个主题,我的回答是,在时间允许的范围之内,尽量提高代码的质量,不追求慢工出细活,不追求代码的100%无错,但是要保证99%以上的无错。这样,在时间的压力下,在质量要求的束缚下,就要求程序员有一个良好的习惯,和稳健的编程风格,以保证代码的优质无错。这就是第二个问题:什么是编写优质无错的代码的核心思想?优质无错是相对的,而不是绝对的。任何代码,都不可能说是绝对无错的,但是在绝大部分情况下,是稳定的,强健的,优质的,无错的。每次发布的时候,都会对上次的发布版本做若干修改,增强功能的同时,也要修改若干bug。那么,核心思想就是:怎样才能自动地查出这个错误。怎样才能避免这个错误。

三.编写优质无错代码的经验
在说了上面很多理论性的问题之后,来看一看具体问题。先来看一看一个具体的题目:(我本人就是先在网上看了这个题目,才对这个讨论会发生兴趣的)

题目1:
作为开发团队的一员,你需要实现一些库函数提供给其他人使用。假设你实现的一个函数原型如下:

int DoSomeThing(char* pParam)
{

}

你们约定好参数pParam不能为NULL,但为了防止调用者错误传递NULL,你需要在你的函数里做判断处理。
请问你会选择那种方式,并说明原因?

(a) if (!pParam)
return 0;

(b) if (!pParam)
return ERROR_PARAM;

(c) if (!pParam)
pParam = "";

(d) if (!pParam)
throw EXCEPTION_ERROR_PARAM;

(e) if (!pParam)
MessageBox(…);

(f) assert(!pParam); 

(附加说明一点,基于目前开发人员技术分布情况和参与讨论会的人员的技术分布情况,这次所列举的例子都是C/C++和Java方面的,不涉及到VB, PB, Delphi等语言。不过对于这些程序员,讨论也是有借鉴作用的。)关于这个问题,大概是所有的程序员都会遇到的。所以,在网上和讨论会上,都发生了激烈的争论和意见交换。我大概把主要的几种观点记录了一下,列举在下面:

1、选择f的理由
因为非NULL是约定,所以可以确定是调用者的问题,f可以明确地指出这一点,防止错误扩散。
我的附加说明: 防止错误扩散的意思是,如果用其他方式,比如throw exception的方式,这个异常不一定会在调用此函数的上一层被捕捉到,可能会被继续抛出直到最上一层或者直到在某一层被catch到,这样的话,错误就会距离发生地点很远,扩散开来。这一观点,代表了一大部分的程序员的观点。

2、反对用f
不赞成assert, assert更重要的作用是程序体里面的一个注释, 在阅读程序的时候起作用不能依赖他来检测错误, 很大程度上assert容易使使用者依赖它本不应该依赖的东西。
这也代表了部分程序员的观点,认为assert是不可依赖的,而应该依赖于错误检测,比如返回值或者异常。

3、另外一种观点
f和d都可取。如果没有系统开销的考虑,d则更好些。可以一举两得。如果没人catch这个exception,其结果就跟f一样,按bug处理,dump core留下一stack trace。如果有人catch,那就按运行错误处理……但是返回一特初值表示错误,只是将错误上交,掩耳盗铃而已。最终总得有个人assert,messagebox,throw exception,perror+exit,或别得什么的。既然已经是约定,就干脆付起责任。

4、一种反对d的理由
不可用d, 这就像你用人,却不相信人一样,偏要try,catch防范他。其实那个错是自己造成的,如果看到异常就容易不检讨自己。 

5、关于观点3的支持意见
讨论过程中,有人认为assert检查的是bug, 而异常是可以恢复的意外情况。所以,观点3的支持者说:可恢复的意外是可以理解的,但可恢复的bug就没什么意义了。既然已经约定好了,你再违背,就属于是bug而不是意外了(比如打不开文件什么的)。很多库函数都不检查指针的合法性(除了系统调用以外,因为总不能让系统dump core吧),也不检查指针是否为NULL(因为如果层层都检查,必定劳民伤财,干脆让最上面调用的人在调用之外查)。

6、选择d+f
选f+d, 好处如下:
a以最激烈的方式,充分暴露调用都的错误!能及时修改BUG
b便于调试,问题出现后,直接到事故现场。比120还快!
c对于realse版的代码没有任何副作用。 
d以处理的代价来看 采用断言也是编写最小一种。
e它是多语种,多平台所通用的方式, 如:C /C++ VB,Java1.4 在win ,unix通吃, 便于移置!
如果在现实中,测试没有能找到所有的BUG,那可能就要用异常来帮忙了! 

当然,我也提出了我的观点, 我支持观点6。理由如下:
assert只在debug标志的时候有用,而在编译release版本的时候不起作用。assert对于检查硬编码的错误,是非常有用的,能够及时的查处编码的错误。比如borland c++的类库源代码中就有很多这样的assert。但是assert
不是万能的,因为有很多错误的发生不是完全在编译时发生的,而是运行时的错误。在release后,assert是不可能依赖的。那么,我们就需要exception这一机制来检测运行时错误,并相应的做出处理。当然,在异常检测和处理过程中还有许多需要讨论的问题,由于不是这一题目的范围,我们没有必要继续讨论得太多,但是,提出来希望大家注意:异常不是捕获了就完成任务了,而要对于不同的情况,采取不同的处理办法,千万不能只是捕获,而不做任何处理,那样和不捕获异常没有任何区别。

在题目刚刚提出的时候,选择各种答案的人都有,所以,我有必要在这里把其他答案为什么不能选的理由说一下。

(a) if (!pParam)
return 0;
这是很多初级程序员常常采取的一种方式。返回值设为0。 因为函数的返回值往往是计算的结果,不赞成把错误标志值和计算结果混在一起使用,容易造成使用者的误会。当然,在很多unix函数中,由于历史原因,还存在很多这样子的函数,所以需要指出,不要沿用这种方式。

(b) if (!pParam)
return ERROR_PARAM;
b比a稍微好一点点,返回了一个常量或者预定义的宏。 从返回值的字面上,调用者能知道发生了什么错误,但是,这也不是一种好的方法。
(c) if (!pParam)
pParam = "";

这是最不好的方式。直接给pParam赋予空字符串,然后继续函数过程,这容易造成不可预料的后果,是程序不稳定的根源。
(d) if (!pParam)
throw EXCEPTION_ERROR_PARAM;
抛出异常,刚刚已经讨论过了,不再赘述。
(e) if (!pParam)
MessageBox(…);
这是一种比较可笑的方式,当然也有不少人用。MessageBox是直接弹出一个对话框,告诉使用者,出错了。但是并不做任何处理,程序继续往下执行,直到出错崩溃。呵呵
(f) assert(!pParam); 
断言,刚刚已经讨论过了,不再赘述。

以上这个题目,引发了所有与会者的兴趣,讨论异常热烈,最后,主持人也给出了自己的观点:d+f。当然这并不是标准答案,因为编程这一门课程本来就没有什么标准答案,大家见仁见智,这个答案只是经验的积累。

主持人紧接着列出了"编写优质无错代码的经验":
a.理想的编译器和实际的编译器
b.使用断言 
c.函数的界面设计
d.考虑风险
e.态度的问题

以上是本节的主要内容。断言,刚刚的问题中已经讨论过了,来看看其他的内容。

理想的编译器和实际的编译器:

题目二:
下面memcpy函数实现有什么问题:
Void *memcpy(void *pvTo,void *pvFrom,size_t size){
byte *pbTo=(byte *) pvTo;
byte *pbFrom=(byte *)pvFrom;
while(size – >0);
*pbTo++= *pbFrom++;
return pvTo;
}

呵呵,粗略一看,这函数还真找不出问题来。但是仔细看看,你就会发现while(size – >0);
这里多了一个分号,导致下面的*pbTo++= *pbFrom++;不是在while循环中执行多次,而是只执行了一次。当然这不是函数设计者的预期结果,而编译器却不会报告错误,因为while(size – >0);从语法上来讲,并没有错误。这就是理想的编译器和实际的编译器的区别所在。

那么,该怎么检查这种错误呢?主持人提出了如下办法:
while(size – >0) NULL; 可以加入NULL来解决空语句. 这样子,当你需要 while(size – >0)
*pbTo++= *pbFrom++;
这种形式的时候,就不会发生错误了,只需要用眼睛看看,就能发现了。两点好处 1 无冗余代码,2 使人更明白。减少风险. 

还有人会这么写
if( (n=read(….)) == 1) ….
在这里,赋值符号=和判断相等的符号==容易敲错,而编译器又检查不出来,可能就会有如下错误:
If(ch = ‘ ’)…;这也是需要注意的问题。

理想的编译器和实际的编译器小结:
a.把屡次出错的合法的C习惯用法看成程序中的错误 
b.增强编译器的警告级别
c.使用其它的工具来检查代码 如 Lint 等
d.进行单元测试
e.消除程序错误的最好方法是尽可能早、尽可能容易地发现错误,要寻求费力最小的自动查错的方法
f.努力减少程序员查错所需的技巧 

使用断言
题目三
下面函数实现,哪一个好,为什么?
a.
char Uptolower(char ch){
if(ch >= ‘A’ && ch <= ‘Z’)
return ch+=‘a’-’A’;
return -1;
}
b.
char Uptolower(char ch){
assert(ch >= ‘A’ && ch <= ‘Z’);
if(ch >= ‘A’ && ch <= ‘Z’)
return ch+=‘a’-’A’;
return ch;
}
c.
char Uptolower(char ch){
assert(ch >= ‘A’ && ch <= ‘Z’);
return ch+(‘a’-’A’);
}
分析:
a.该函数检查ch是否在A..Z之间,如果是,则返回相应的小写字符,如果不是,则返回-1。
缺点在于:把错误标志值和计算结果混在一起使用,容易造成使用者的误会。

b.该函数使用了断言,如果ch在A..Z之间则返回相应的小写字符,如果不是,断言会起作用,程序发生错误并退出。而最后一个return ch;则是在release的时候,如果不是A..Z之间,则返回原来的字符。但是,从书写效率上来说,这个函数稍微罗嗦了一点。因为它重复使用了断言和if判断。

c.该函数也使用了断言,返回相应大写字母的小写字母。

使用断言的好处:
a.暴露了调用者的错误 
b.便于调试
c.对代码没有代价 
d.最少的处理代价 

断言使用举例:
void memcpy(void * pvTo,void *pvFrom,size_t size){
void *pbTo= (byte *)pvTo;
void *pbFrom= (byte * pvFrom);
assert(pvTo !=NULL && pvFrom !=NULL);
assert(pbTo >= pbFrom +size’ ’pbFrom >= pbTo+size);

}

使用断言的规则:
a.要使用断言对函数参数进行确认 
b.要从程序中删去无定义的特性或者在程序中使 用断言来检查出无定义特性的非法使用 
c.不要浪费别人的时间-详细说明不清楚的断言
d.消除所做的隐式假定,或者利用检查其正确性 
e.在进行防错性程序设计时,不要隐瞒错误防错性程序设计虽然被誉为有较好的编码风格,但它却隐瞒了错误。
要记住,我们正在谈论的错误决不应该再发生,而对这些错所进行的安全处理又编写无错代码变得更加困难
f.要利用不同的算法对程序的结果进行确认 
g.不要等待错误发生,要使用初始检查程序

断言小结:
a.要同时维护交付和调试两个版本。封装交付的版本,应尽可能地使用调试版本进行自动查错。
b.断言是进行调试检查的简单方法。要使用断言捕捉不应该发生的非法情况。不要混淆非法情况与错误情况之间的区别,后者是在最终产品中必须处理的。
c.使用断言对函数的参数进行确认,并且在程序员使用了无定义的特性时向程序员报警。涵数定义得越严格,确认其参数就越容易。
d.防错性程序设计会隐瞒错误。在进行防错编码时,如果”不可能发生”的情况确实发生了,要使用断言进行报警。


写到这里,我们初步探讨了编写优质无错代码的必要性,原则,和相关经验。
留几个练习题目,大家也参与一下讨论吧。
练习题目1:
下面的memset函数实现有什么问题?

void *memset(void *pv, byte b, size_t size)
{
byte *pb = (byte *)pv;
unsigned long l;
size_t sizeSize;

l = (b << 8) | b; /* 用4个字节拼成一个long */
l = (l << 16) | l;
pb = (byte *)longfill((long *)pb, l, size/4);
size = size % 4;

while (size– > 0)
*pb++ = b;
return (pv);
} 

练习题目2:

下面的代码用memset将三个局部变量置为0,请问可能会有什么问题?
void DoSomeThing(…)
{
int i;
int j;
int k;

memset(&k, 0, 3*sizeof(int)); // 将i,j,k置为0

}

练习题目3:

定义结构如下:
typedef struct
{
char c1;
char c2;
int n;
} stru;
请问sizeof(stru)等于多少?并说明理由。

练习题目4:

下面是C语言中两种if语句判断方式。请问哪种写法更好?为什么?
int n;

if (n == 10) // 第一种判断方式
if (10 == n) // 第二种判断方式

练习题目5:

下面的代码有什么问题?
void DoSomeThing(…)
{
char* p;

p = malloc(1024); // 分配1K的空间
if (NULL == p)
return;

p = realloc(p, 2048); // 空间不够,重新分配到2K
if (NULL == p)
return;

}

练习题目6:

下面的代码有什么问题?
char *DoSomeThing(…)
{
char str[16];


return str;
}

练习题目7:
下面的代码有什么问题?

char *_strdup( const char *strSource )
{
static char str[MAX_STR_LEN];

strcpy(str, strSource);
return str;
}

练习题目8:

下面的代码有什么问题?并请给出正确的写法。
try{
FILE* fp = fopen("c:1.dat");
if (NULL != fp)
{

}
fclose(fp);
}
except(EXCEPTION_EXECUTE_HANDLER){
}

我敲字敲累了,告一段落吧。不过,讨论会并不止讨论了这些内容,还有很多内容我没有写完,比如,函数的界面, 编写代码的风险, 编程的态度等等问题。作为补充,我把讨论会的幻灯片修改成了文本版本,作为另外一篇文章放在这里,以便对这个话题感兴趣的网友们参考。有什么问题,欢迎来信 ariesram@linuxaid.com.cn 继续探讨

对日文版「Javaコーディング規約」进行总结如下:

、命名用英文,不能用拼音等其它不便于理解的文字。

、JAVA严格区分大小写,包名全用小写,不能使用缩写,定义要具体化,

    不能用ID、番号等做包名,子包的名可以重复。

、类名首字母要大写,每个单词首字母也大写(匈牙利命名法),不能使用缩写,

         定义要具体化,不能用ID、番号等做类名

、Extends Exception时,类名应是类名 + Exception。

5、接口的命名与类名的命名一致,当一个类implements另一个类时,

          就在另一个类前+“ I ”。

6、Abstract类命名是类名+Abstract。interface类的实例类名后应+“ Impl ”,

          具有特定功能的类后+“able”。

7、测试类后+“test”,模块测试的类名应是模块名+“test”或“alltest”。

8、方法名不能与构造函数名一致。方法名首字母要大写,每个单词首字母也大写,

          不能使用缩写,定义要具体化。

9、方法名应对其功能在其名前加“create”、“to”、“get”、“set”等表示其功能。

2006年02月06日

letters.jpg一本小书《一生的忠告》。网上关于这本书的介绍是这样的:

  本书是英国著名外交家查斯特菲尔德勋爵写给儿子的人生忠告。此书问世至今,一直畅销不衰,成为有史以来最受推崇的家书,被誉为“一部使人脱胎换骨的人际关系和礼仪全书”和绅士们的“教科书”。牛津大学出版社特将此书列入该社《世界经典》之一。

很多年没有看这类励志的书籍了,在火车上闲来无聊,就翻了一遍,仍然看到一些值得推荐的话,摘录一些在这里。

 

◆要衡量一个人对时间的重要性知道多少,只需观察他利用时 间的方法就足够了。

◆一个无法集中精神、投入全部精力去做该做的事情的人,是无无法完满地去完成一项工作的,这样的人也无法成为你永久的知心朋友。

◆避免无谓麻烦的最佳方法是在选择朋友时一定要注意他的人品。

◆法国讽刺作家罗比莱斯,早在16世纪时便说过,“坚韧卓绝之人,必能成就万事”。

◆希望往往会落空,并且是在最有希望之时。

◆成功的意义并非总是一个“赢”字

书中的很多道理也许我们早就明白,可是能否执行才是最大的问题。

使用Java语言来操纵Excel文件并不是一件容易的事。在Web应用日益盛行的今天,通过Web来操作Excel文件的需求越来越强烈,目前较为流行的操作是在JSP或Servlet 中创建一个CSV (comma separated values)文件,并将这个文件以MIME,text/csv类型返回给浏览器,接着浏览器调用Excel并且显示CSV文件。这样只是说可以访问到 Excel文件,但是还不能真正的操纵Excel文件,本文将给大家一个惊喜,向大家介绍一个开放源码项目,Java Excel API,使用它大家就可以方便地操纵Excel文件了。

Java Excel API简介
Java Excel是一开放源码项目,通过它Java开发人员可以读取Excel文件的内容、创建新的Excel文件、更新已经存在的Excel文件。使用该 API非Windows操作系统也可以通过纯Java应用来处理Excel数据表。因为是使用Java编写的,所以我们在Web应用中可以通过JSP、 Servlet来调用API实现对Excel数据表的访问。
现在发布的稳定版本是V2.0,提供以下功能:
 从Excel 95、97、2000等格式的文件中读取数据;
 读取Excel公式(可以读取Excel 97以后的公式);
 生成Excel数据表(格式为Excel 97);
 支持字体、数字、日期的格式化;
 支持单元格的阴影操作,以及颜色操作;
 修改已经存在的数据表;
现在还不支持以下功能,但不久就会提供了:
 不能够读取图表信息;
 可以读,但是不能生成公式,任何类型公式最后的计算值都可以读出;
 
 应用示例
1、从Excel文件读取数据表
Java Excel API既可以从本地文件系统的一个文件(.xls),也可以从输入流中读取Excel数据表。读取Excel数据表的第一步是创建Workbook(术语:工作薄),下面的代码片段举例说明了应该如何操作:(完整代码见ExcelReading.java)
import java.io.*;
import jxl.*;
… … … …
try
{
//构建Workbook对象, 只读Workbook对象
//直接从本地文件创建Workbook
//从输入流创建Workbook
InputStream is = new FileInputStream(sourcefile);
jxl.Workbook rwb = Workbook.getWorkbook(is);
}
catch (Exception e)
{
e.printStackTrace();
}
一旦创建了Workbook,我们就可以通过它来访问Excel Sheet(术语:工作表)。参考下面的代码片段:
//获取第一张Sheet表
Sheet rs = rwb.getSheet(0);
我们既可能通过Sheet的名称来访问它,也可以通过下标来访问它。如果通过下标来访问的话,要注意的一点是下标从0开始,就像数组一样。
一旦得到了Sheet,我们就可以通过它来访问Excel Cell(术语:单元格)。参考下面的代码片段:
//获取第一行,第一列的值
Cell c00 = rs.getCell(0, 0);
String strc00 = c00.getContents();
//获取第一行,第二列的值
Cell c10 = rs.getCell(1, 0);
String strc10 = c10.getContents();
//获取第二行,第二列的值
Cell c11 = rs.getCell(1, 1);
String strc11 = c11.getContents();
System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType());
System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType());
System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType());
如果仅仅是取得Cell的值,我们可以方便地通过getContents()方法,它可以将任何类型的Cell值都作为一个字符串返回。示例代码中Cell (0, 0)是文本型,Cell(1, 0)是数字型,Cell(1,1)是日期型,通过getContents(),三种类型的返回值都是字符型。
如果有需要知道Cell内容的确切类型,API也提供了一系列的方法。参考下面的代码片段:
String strc00 = null;
double strc10 = 0.00;
Date strc11 = null;
Cell c00 = rs.getCell(0, 0);
Cell c10 = rs.getCell(1, 0);
Cell c11 = rs.getCell(1, 1);
if(c00.getType() == CellType.LABEL)
{
LabelCell labelc00 = (LabelCell)c00;
strc00 = labelc00.getString();
}
if(c10.getType() == CellType.NUMBER)
{
NmberCell numc10 = (NumberCell)c10;
strc10 = numc10.getvalue();
}
if(c11.getType() == CellType.DATE)
{
DateCell datec11 = (DateCell)c11;
strc11 = datec11.getDate();
}
System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType());
System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType());
System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType());
在得到Cell对象后,通过getType()方法可以获得该单元格的类型,然后与API提供的基本类型相匹配,强制转换成相应的类型,最后调用相应的取值方法getXXX(),就可以得到确定类型的值。API提供了以下基本类型,与Excel的数据格式相对应,如下图所示:
每种类型的具体意义,请参见Java Excel API document.
当你完成对Excel电子表格数据的处理后,一定要使用close()方法来关闭先前创建的对象,以释放读取数据表的过程中所占用的内存空间,在读取大量数据时显得尤为重要。参考如下代码片段:
//操作完成时,关闭对象,释放占用的内存空间
rwb.close();
Java Excel API提供了许多访问Excel数据表的方法,在这里我只简要地介绍几个常用的方法,其它的方法请参考附录中的Java Excel API document.
Workbook类提供的方法
1. int getNumberOfSheets()
获得工作薄(Workbook)中工作表(Sheet)的个数,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
int sheets = rwb.getNumberOfSheets();
2. Sheet[] getSheets()
返回工作薄(Workbook)中工作表(Sheet)对象数组,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
Sheet[] sheets = rwb.getSheets();
3. String getVersion()
返回正在使用的API的版本号,好像是没什么太大的作用。
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
String apiVersion = rwb.getVersion();
Sheet接口提供的方法
1) String getName()
获取Sheet的名称,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
String sheetName = rs.getName();
2) int getColumns()
获取Sheet表中所包含的总列数,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
int rsColumns = rs.getColumns();
3) Cell[] getColumn(int column)
获取某一列的所有单元格,返回的是单元格对象数组,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
Cell[] cell = rs.getColumn(0);
4) int getRows()
获取Sheet表中所包含的总行数,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
int rsRows = rs.getRows();
5) Cell[] getRow(int row)
获取某一行的所有单元格,返回的是单元格对象数组,示例子:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
Cell[] cell = rs.getRow(0);
6) Cell getCell(int column, int row)
获取指定单元格的对象引用,需要注意的是它的两个参数,第一个是列数,第二个是行数,这与通常的行、列组合有些不同。
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
Cell cell = rs.getCell(0, 0);
2、生成新的Excel工作薄
下面的代码主要是向大家介绍如何生成简单的Excel工作表,在这里单元格的内容是不带任何修饰的(如:字体,颜色等等),所有的内容都作为字符串写入。(完整代码见ExcelWriting.java)
与读取Excel工作表相似,首先要使用Workbook类的工厂方法创建一个可写入的工作薄(Workbook)对象,这里要注意的是,只能通过API提供的工厂方法来创建Workbook,而不能使用WritableWorkbook的构造函数,因为类WritableWorkbook的构造函数为 protected类型。示例代码片段如下:
import java.io.*;
import jxl.*;
import jxl.write.*;
… … … …
try
{
//构建Workbook对象, 只读Workbook对象
//Method 1:创建可写入的Excel工作薄
jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(new File(targetfile));
//Method 2:将WritableWorkbook直接写入到输出流
/*
OutputStream os = new FileOutputStream(targetfile);
jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(os);
*/
}
catch (Exception e)
{
e.printStackTrace();
}
API 提供了两种方式来处理可写入的输出流,一种是直接生成本地文件,如果文件名不带全路径的话,缺省的文件会定位在当前目录,如果文件名带有全路径的话,则生成的Excel文件则会定位在相应的目录;另外一种是将Excel对象直接写入到输出流,例如:用户通过浏览器来访问Web服务器,如果HTTP头设置正确的话,浏览器自动调用客户端的Excel应用程序,来显示动态生成的Excel电子表格。
接下来就是要创建工作表,创建工作表的方法与创建工作薄的方法几乎一样,同样是通过工厂模式方法获得相应的对象,该方法需要两个参数,一个是工作表的名称,另一个是工作表在工作薄中的位置,参考下面的代码片段:
//创建Excel工作表
jxl.write.WritableSheet ws = wwb.createSheet("Test Sheet 1", 0);
"这锅也支好了,材料也准备齐全了,可以开始下锅了!",现在要做的只是实例化API所提供的Excel基本数据类型,并将它们添加到工作表中就可以了,参考下面的代码片段:
//1.添加Label对象
jxl.write.Label labelC = new jxl.write.Label(0, 0, "This is a Label cell");
ws.addCell(labelC);
//添加带有字型Formatting的对象
jxl.write.WritableFont wf = new jxl.write.WritableFont(WritableFont.TIMES, 18, WritableFont.BOLD, true);
jxl.write.WritableCellFormat wcfF = new jxl.write.WritableCellFormat(wf);
jxl.write.Label labelCF = new jxl.write.Label(1, 0, "This is a Label Cell", wcfF);
ws.addCell(labelCF);
//添加带有字体颜色Formatting的对象
jxl.write.WritableFont wfc = new jxl.write.WritableFont(WritableFont.ARIAL, 10, WritableFont.NO_BOLD, false,
Underlinestyle.NO_UNDERLINE, jxl.format.Colour.RED);
jxl.write.WritableCellFormat wcfFC = new jxl.write.WritableCellFormat(wfc);
jxl.write.Label labelCFC = new jxl.write.Label(1, 0, "This is a Label Cell", wcfFC);
ws.addCell(labelCF);
//2.添加Number对象
jxl.write.Number labelN = new jxl.write.Number(0, 1, 3.1415926);
ws.addCell(labelN);
//添加带有formatting的Number对象
jxl.write.NumberFormat nf = new jxl.write.NumberFormat("#.##");
jxl.write.WritableCellFormat wcfN = new jxl.write.WritableCellFormat(nf);
jxl.write.Number labelNF = new jxl.write.Number(1, 1, 3.1415926, wcfN);
ws.addCell(labelNF);
//3.添加Boolean对象
jxl.write.Boolean labelB = new jxl.write.Boolean(0, 2, false);
ws.addCell(labelB);
//4.添加DateTime对象
jxl.write.DateTime labelDT = new jxl.write.DateTime(0, 3, new java.util.Date());
ws.addCell(labelDT);
//添加带有formatting的DateFormat对象
jxl.write.DateFormat df = new jxl.write.DateFormat("dd MM yyyy hh:mm:ss");
jxl.write.WritableCellFormat wcfDF = new jxl.write.WritableCellFormat(df);
jxl.write.DateTime labelDTF = new jxl.write.DateTime(1, 3, new java.util.Date(), wcfDF);
ws.addCell(labelDTF);
这里有两点大家要引起大家的注意。第一点,在构造单元格时,单元格在工作表中的位置就已经确定了。一旦创建后,单元格的位置是不能够变更的,尽管单元格的内容是可以改变的。第二点,单元格的定位是按照下面这样的规律(column, row),而且下标都是从0开始,例如,A1被存储在(0, 0),B1被存储在(1, 0)。
最后,不要忘记关闭打开的Excel工作薄对象,以释放占用的内存,参见下面的代码片段:
//写入Exel工作表
wwb.write();
//关闭Excel工作薄对象
wwb.close();
这可能与读取Excel文件的操作有少少不同,在关闭Excel对象之前,你必须要先调用write()方法,因为先前的操作都是存储在缓存中的,所以要通过该方法将操作的内容保存在文件中。如果你先关闭了Excel对象,那么只能得到一张空的工作薄了。
3、拷贝、更新Excel工作薄
接下来简要介绍一下如何更新一个已经存在的工作薄,主要是下面二步操作,第一步是构造只读的Excel工作薄,第二步是利用已经创建的Excel工作薄创建新的可写入的Excel工作薄,参考下面的代码片段:(完整代码见ExcelModifying.java)
//创建只读的Excel工作薄的对象
jxl.Workbook rw = jxl.Workbook.getWorkbook(new File(sourcefile));
//创建可写入的Excel工作薄对象
jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(new File(targetfile), rw);
//读取第一张工作表
jxl.write.WritableSheet ws = wwb.getSheet(0);
//获得第一个单元格对象
jxl.write.WritableCell wc = ws.getWritableCell(0, 0);
//判断单元格的类型, 做出相应的转化
if(wc.getType() == CellType.LABEL)
{
Label l = (Label)wc;
l.setString("The value has been modified.");
}
//写入Excel对象
wwb.write();
//关闭可写入的Excel对象
wwb.close();
//关闭只读的Excel对象
rw.close();
之所以使用这种方式构建Excel对象,完全是因为效率的原因,因为上面的示例才是API的主要应用。为了提高性能,在读取工作表时,与数据相关的一些输出信息,所有的格式信息,如:字体、颜色等等,是不被处理的,因为我们的目的是获得行数据的值,既使没有了修饰,也不会对行数据的值产生什么影响。唯一的不利之处就是,在内存中会同时保存两个同样的工作表,这样当工作表体积比较大时,会占用相当大的内存,但现在好像内存的大小并不是什么关键因素了。
一旦获得了可写入的工作表对象,我们就可以对单元格对象进行更新的操作了,在这里我们不必调用API提供的add()方法,因为单元格已经于工作表当中,所以我们只需要调用相应的setXXX()方法,就可以完成更新的操作了。
尽单元格原有的格式化修饰是不能去掉的,我们还是可以将新的单元格修饰加上去,以使单元格的内容以不同的形式表现。
新生成的工作表对象是可写入的,我们除了更新原有的单元格外,还可以添加新的单元格到工作表中,这与示例2的操作是完全一样的。
最后,不要忘记调用write()方法,将更新的内容写入到文件中,然后关闭工作薄对象,这里有两个工作薄对象要关闭,一个是只读的,另外一个是可写入的
双击鼠标滚动屏幕的代码[JavaScript]
//双击鼠标滚动屏幕的代码
var currentpos,timer;
function initialize()
{
timer=setInterval ("scrollwindow ()",30);
}
function sc()
{
clearInterval(timer);
}
function scrollwindow()
{
currentpos=document.body.scrollTop;
window.scroll(0,++currentpos);
if (currentpos !=document.body.scrollTop)
sc();
}
document.onmousedown=sc
document.ondblclick=initialize
2006年02月05日

JAVA对文件的读写操作例子如下:

一、写操作:String username=indexForm.getUsername();
                     String password=indexForm.getPassword();
                     PrintWriter ww = new PrintWriter

                     (new BufferedWriter(new FileWriter("C:\\qidawei.txt")));
                     ww.write(username);
                     ww.write(password);
                     ww.close();

二、读操作: