javaGrowing

            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            92 隨筆 :: 33 文章 :: 49 評論 :: 0 Trackbacks

          #

          JDBC基礎(chǔ)(一)

          本來不想寫這部份入門級的內(nèi)容,但既然欄目定為JDBC專欄,還是簡單寫一些吧.
          JDBC基礎(chǔ)(一)

              來,我們認(rèn)識一下!
              JDBC,JAVA平臺的DATABASE的連通性.白話一句,什么意思啊?
              就是JAVA平臺上和數(shù)據(jù)庫進(jìn)行連結(jié)的"工具".

              還是先一起來回顧一下接口吧:從下向上,接口是對"案例"的抽象,由一個案例抽象出一些規(guī)則.
          反過來,從上向下,被抽象出來的接口是對案例的一種承諾和約束.
              也就是說,只要你實(shí)現(xiàn)我規(guī)定的接口,你的類就已經(jīng)具有了接口對外承諾的方法,只要"客戶"會
          操作接口,不需要重新學(xué)習(xí)就會操作實(shí)現(xiàn)了該接口的新類!
              好了,用行話來說:
              1.通過接口可以實(shí)現(xiàn)不相關(guān)的類的相同行為.
              2.通過接口可以指明多個類需要實(shí)現(xiàn)的方法.
              3.通過接口可以了解對象的交互方法而不需要了解對象所對應(yīng)的類藍(lán)本.
              這幾句話很明白吧?好象有一本什么模式的書把這段話用了30多頁寫出來,結(jié)果別人看了還不如
          我這幾句話明白,不過我明白了為什么有些人要寫書了.

              搞懂了以上這東西,JDBC就好明白了.
              為了通用,JAVA中要求有一種機(jī)制,在操作不同廠商數(shù)據(jù)庫時有相同的方法去操作,而不是每接
          觸一種數(shù)據(jù)庫就要學(xué)習(xí)新的方法.完成這種機(jī)制的"東西"就叫"JDBC"了.
              簡單地分,JDBC有兩部分組成,JDBC API和JDBC Driver Interface.
              JDBC API就是提供給"客戶"(就是象你我這種菜鳥級程序員來用的,如果是高手都自己寫JDBC了,
          哈哈)的一組獨(dú)立于數(shù)據(jù)庫的API,對任何數(shù)據(jù)庫的操作,都可以用這組API來進(jìn)行.那么要把這些通用的API
          翻譯成特定數(shù)據(jù)庫能懂的"指令",就要由JDBC Driver Interface來實(shí)現(xiàn)了,所以這部份是面向JDBC驅(qū)動程
          序開發(fā)商的編程接口,它會把我們通過JDBC API發(fā)給數(shù)據(jù)庫的通用指令翻譯給他們自己的數(shù)據(jù)庫.


              還是通過實(shí)際操作來看看JDBC如何工作的吧.

              因為JDBC API是通用接口,那么程序是如何知道我要連結(jié)的是哪種數(shù)據(jù)庫呢?所以在和數(shù)據(jù)庫連
          結(jié)時先要加載(或注冊可用的Driver),其實(shí)就是JDBC簽名.加載驅(qū)動程序和好多方法,最常用的就是先把驅(qū)
          動程序類溶解到內(nèi)存中,作為"當(dāng)前"驅(qū)動程序.注意"當(dāng)前"是說內(nèi)存中可以有多個驅(qū)動程序,但只有現(xiàn)在加
          載的這個作為首選連結(jié)的驅(qū)動程序.
              Class.forName("org.gjt.mm.mysql.Driver");
              Class.forName方法是先在內(nèi)存中溶解簽名為"org.gjt.mm.mysql.Driver"的Driver類,Driver類
          就會把相應(yīng)的實(shí)現(xiàn)類對應(yīng)到JDBC API的接口中.比如把org.gjt.mm.mysql.Connection的實(shí)例對象賦給
          java.sql.Connection接口句柄,以便"客戶"能通過操作java.sql.Connection句柄來調(diào)用實(shí)際的
          org.gjt.mm.mysql.Connection中的方法.之于它們是如果映射的,這是廠商編程的,"客戶"只要調(diào)用
          Class.forName("org.gjt.mm.mysql.Driver");方法就可以順利地操作JDBC API了.

              一個普通數(shù)據(jù)庫的連結(jié)過程為:
              1.加載驅(qū)動程序.
              2.通過DriverManager到得一個與數(shù)據(jù)庫連結(jié)的句柄.
              3.通過連結(jié)句柄綁定要執(zhí)行的語句.
              4.接收執(zhí)行結(jié)果.
              5.可選的對結(jié)果的處理.
              6.必要的關(guān)閉和數(shù)據(jù)庫的連結(jié).
          JDBC基礎(chǔ)(二)

          因為是基礎(chǔ)篇,所以還是對每一步驟簡單說明一下吧:

              前面說是,注冊驅(qū)動程序有多方法,Class.forName();是一種顯式地加載.當(dāng)一個驅(qū)
          動程序類被Classloader裝載后,在溶解的過程中,DriverManager會注冊這個驅(qū)動類的實(shí)例.
          這個調(diào)用是自動發(fā)生的,也就是說DriverManager.registerDriver()方法被自動調(diào)用了,當(dāng)然
          我們也可以直接調(diào)用DriverManager.registerDriver()來注冊驅(qū)動程序,但是,以我的經(jīng)驗.
          MS的瀏覽中APPLET在調(diào)用這個方法時不能成功,也就是說MS在瀏覽器中內(nèi)置的JVM對該方法的
          實(shí)現(xiàn)是無效的.
              另外我們還可以利用系統(tǒng)屬性jdbc.drivers來加載多個驅(qū)動程序:
          System.setProperty("jdbc.drivers","driver1:driver2:.....:drivern");多個驅(qū)動程序之
          間用":"隔開,這樣在連結(jié)時JDBC會按順序搜索,直到找到第一個能成功連結(jié)指定的URL的驅(qū)動
          程序.
              在基礎(chǔ)篇里我們先不介紹DataSource這些高級特性.

              在成功注冊驅(qū)動程序后,我們就可以用DriverManager的靜態(tài)方法getConnection來得
          到和數(shù)據(jù)庫連結(jié)的引用:
              Connection conn = DriverManager.getConnection(url);
              如果連結(jié)是成功的,則返回Connection對象conn,如果為null或拋出異常,則說明沒有
          和數(shù)據(jù)庫建立連結(jié).
              對于getConnection()方法有三個重載的方法,一種是最簡單的只給出數(shù)據(jù)源即:
          getConnection(url),另一種是同時給出一些數(shù)據(jù)源信息即getConnection(url,Properties),
          另外一種就是給出數(shù)據(jù)源,用戶名和密碼:getConnection(url,user,passwod),對于數(shù)據(jù)源信息.
          如果我們想在連結(jié)時給出更多的信息可以把這些信息壓入到一個Properties,當(dāng)然可以直接壓
          入用戶名密碼,別外還可以壓入指定字符集,編碼方式或默認(rèn)操作等一些其它信息.
              
              在得到一個連結(jié)后,也就是有了和數(shù)據(jù)庫找交道的通道.我們就可以做我們想要的操
          作了.
              還是先來介紹一些一般性的操作:
              如果我們要對數(shù)據(jù)庫中的表進(jìn)行操作,要先緣故綁定一個語句:
              Statement stmt = conn.createStatement();
              然后利用這個語句來執(zhí)行操作.根本操作目的,可以有兩種結(jié)果返回,如果執(zhí)行的查詢
          操作,返回為結(jié)果集ResultSet,如果執(zhí)行更新操作,則返回操作的記錄數(shù)int.
              注意,SQL操作嚴(yán)格區(qū)分只有兩個,一種就是讀操作(查詢操作),另一種就是寫操作(更
          新操作),所以,create,insert,update,drop,delete等對數(shù)據(jù)有改寫行為的操作都是更新操作.

              ResultSet rs = stmt.executeQuery("select * from table where xxxxx");
              int x = stmt.executeUpdate("delete from table where ......");
              如果你硬要用executeQuery執(zhí)行一個更新操作是可以的,但不要把它賦給一個句柄,
          當(dāng)然稍微有些經(jīng)驗的程序員是不會這么做的.
              至于對結(jié)果集的處理,我們放在下一節(jié)討論,因為它是可操作的可選項,只有查詢操作
          才返回結(jié)果集,對于一次操作過程的完成,一個非常必要的步驟是關(guān)閉數(shù)據(jù)庫連結(jié),在你沒有了
          解更多的JDBC知識這前,你先把這一步驟作為JDBC操作中最最重要的一步,在以后的介紹中我會
          不斷地提醒你去關(guān)閉數(shù)據(jù)庫連結(jié)!!!!!!!!!!!

              按上面介紹的步驟,一個完成的例子是這樣的:(注意,為了按上面的步驟介紹,這個例
          子不是最好的)
              try{
                  Class.forName("org.gjt.mm.mysql.Driver");
              }catch(Exception e){
                  System.out.println("沒有成功加載驅(qū)動程序:"+e.toString());
                  return;
              }//對于象我這樣的經(jīng)驗,可以直接從e.toString()的簡單的幾個字判斷出異常原因,
               //如果你是一個新手應(yīng)該選捕獲它的子類,如何知道要捕獲哪幾個異常呢?一個簡單
               //的方法就是先不加try{},直接Class.forName("org.gjt.mm.mysql.Driver");,編
               //譯器就會告訴你要你捕獲哪幾個異常了,當(dāng)然這是偷機(jī)取巧的方法,最好還是自己
               //去看JDK文檔,它會告訴你每個方法有哪些異常要你捕獲.
              Connection conn = null;
              try{
                  conn = DriverManager.getConnection(
                                  "jdbc:mysql://host:3306/mysql",
                                  "user",
                                  "passwd");
                  Statement stmt = conn.createStatement();
                  ResultSet rs = stmt.executeQuery("select * from table");
                  //rs 處理
                  [rs.close();]
                  [stmt.close();]
              }
              catch(Exception e){
                  System.out.println("數(shù)據(jù)庫操作出現(xiàn)異常:"+e.toString());
              }
              finally{
                  try{conn.close();}catch(Exception){}
              }//不管你以前是學(xué)習(xí)到的關(guān)于數(shù)據(jù)庫流程是如何操作的,如果你相信我,從現(xiàn)在開始,
               //請你一定要把數(shù)據(jù)庫關(guān)閉的代碼寫到finally塊中,切切!
          JDBC基礎(chǔ)(三)

          關(guān)于Statement對象:
              前面說過,Statement對象是用來綁定要執(zhí)行的操作的,在它上面有三種執(zhí)行方法:
          即用來執(zhí)行查詢操作的executeQuery(),用來執(zhí)行更新操作的executeUpdate()和用來執(zhí)行
          動態(tài)的未知的操作的execute().
              JDBC在編譯時并不對要執(zhí)行的SQL語句檢測,只是把它看著一個String,只有在驅(qū)動
          程序執(zhí)行SQL語句時才知道正確與否.
              一個Statement對象同時只能有一個結(jié)果集在活動.這是寬容性的,就是說即使沒有
          調(diào)用ResultSet的close()方法,只要打開第二個結(jié)果集就隱含著對上一個結(jié)果集的關(guān)閉.所以
          如果你想同時對多個結(jié)果集操作,就要創(chuàng)建多個Statement對象,如果不需要同時操作,那么可
          以在一個Statement對象上須序操作多個結(jié)果集.
              
              這里我不得不特別說明一下,很多人會用一個Statement進(jìn)行嵌套查詢,然后就來問
          我說為什么不能循環(huán)?道理上面已經(jīng)說清楚了.我們來詳細(xì)分析一下嵌套查詢:
              Connection conn = null;
              Statement stmt = null;
              conn = .......;
              stmt = conm.createStatement(xxxxxx);
              ResultSet rs = stmt.executeQuery(sql1);
              while(rs.next()){
                  str = rs.getString(xxxxx);
                  ResultSet rs1 = stmt.executeQuery("select * from 表 where 字段=str");
              }
          當(dāng)stmt.executeQuery("select * from 表 where 字段=str");賦給rs1時,這時隱含的操作
          是已經(jīng)關(guān)閉了rs,你還能循環(huán)下去嗎?
          所以如果要同時操作多個結(jié)果集一定要讓它他綁定到不同的Statement對象上.好在一個connection
          對象可以創(chuàng)建任意多個Statement對象,而不需要你重新獲取連結(jié).

              關(guān)于獲取和設(shè)置Statement的選項:只要看看它的getXXX方法和setXXX方法就明白了,這兒
          作為基礎(chǔ)知識只提一下以下幾個:
              setQueryTimeout,設(shè)置一個SQL執(zhí)行的超時限制.
              setMaxRows,設(shè)置結(jié)果集能容納的行數(shù).
              setEscapeProcessing,如果參數(shù)為true,則驅(qū)動程序在把SQL語句發(fā)給數(shù)據(jù)庫前進(jìn)行轉(zhuǎn)義替
          換,否則讓數(shù)據(jù)庫自己處理,當(dāng)然這些默認(rèn)值都可以通過get方法查詢.

              Statement的兩個子類:
              PreparedStatement:對于同一條語句的多次執(zhí)行,Statement每次都要把SQL語句發(fā)送給數(shù)據(jù)
          庫,這樣做效率明顯不高,而如果數(shù)據(jù)庫支持預(yù)編譯,PreparedStatement可以先把要執(zhí)行的語句一次發(fā)
          給它,然后每次執(zhí)行而不必發(fā)送相同的語句,效率當(dāng)然提高,當(dāng)然如果數(shù)據(jù)庫不支持預(yù)編譯,
          PreparedStatement會象Statement一樣工作,只是效率不高而不需要用戶工手干預(yù).
              另外PreparedStatement還支持接收參數(shù).在預(yù)編譯后只要傳輸不同的參數(shù)就可以執(zhí)行,大大
          提高了性能.
                  
              PreparedStatement ps = conn.prepareStatement("select * from 表 where 字段=?");
              ps.setString(1,參數(shù));
              ResultSet rs = ps.executeQuery();
              
              CallableStatement:是PreparedStatement的子類,它只是用來執(zhí)行存儲過程的.
              CallableStatement sc = conn.prepareCall("{call query()}");
              ResultSet rs = cs.executeQuery();
              
              關(guān)于更高級的知識我們在JDBC高級應(yīng)用中介紹.
          JDBC基礎(chǔ)(四)

              作為基礎(chǔ)知識的最后部分,我們來說一說結(jié)果集的處理,當(dāng)然是說對一般結(jié)果集的處理.
          至于存儲過程返回的多結(jié)果集,我們?nèi)匀环旁诟呒墤?yīng)用中介紹.
              SQL語句如何執(zhí)行的是查詢操作,那就要返回一個ResultSet對象,要想把查詢結(jié)果最后
          明白地顯示給用戶,必須對ResultSet進(jìn)行處理.ResultSet返回的是一個表中符合條件的記錄,對
          ResultSet的處理要逐行處理,而對于每一行的列的處理,則可以按任意順序(注意,這只是JDBC規(guī)
          范的要求,有些JDBC實(shí)現(xiàn)時對于列的處理仍然要求用戶按順序處理,但這是極少數(shù)的).事實(shí)上,雖
          然你可以在處理列的時候可以按任意順序,但如果你按從左到右的順序則可以得到較高的性能.

              這兒從底層來講解一下ResultSet對象,在任何介紹JDBC的書上你是不會獲得這樣的知
          識的,因為那是數(shù)據(jù)庫廠商的事.ResultSet對象實(shí)際維護(hù)的是一個二維指針,第一維是指向當(dāng)前
          行,最初它指向的是結(jié)果集的第一行之前,所以如果要訪問第一行,就要先next(),以后每一行都
          要先next()才能訪問,然后第二維的指針指向列,只要當(dāng)你去rs.getXXX(列)時,才通過
          Connection再去數(shù)據(jù)庫把真實(shí)的數(shù)據(jù)取出來,否則沒有什么機(jī)器能真的把要取的數(shù)據(jù)都放在內(nèi)
          存中.
              所以,千萬要記住,如果Connection已經(jīng)關(guān)閉,那是不可能再從ResultSet中取到數(shù)據(jù)的.
          有很多人問我,我可不可以取到一個ResultSet把它寫到Session中然后關(guān)閉Connection,這樣就
          不要每次都連結(jié)了.我只能告訴你,你的想法非常好,但,是錯誤的!當(dāng)然在javax.sql包中JDBC高
          級應(yīng)用中有CacheRow和WebCacheRow可以把結(jié)果集緩存下來,但那和我們自己開一個數(shù)據(jù)結(jié)構(gòu)把
          ResultSet的行集中所有值一次取出來保存起來沒有什么兩樣.
              訪問行中的列,可以按字段名或索引來訪問.下面是一個簡單的檢索結(jié)果的程序:

              ResultSet rs = stmt.executeQuery("select a1,a2,a3 from table");
              while(rs.next()){
                  int i = rs.getInt(1);
                  String a = rs.getString("a2");
                  ..............
              }

              對于用來顯示的結(jié)果集,用while來進(jìn)行next()是最普通的,如果next()返回false,則
          說明已經(jīng)沒有可用的行了.但有時我們可能連一行都沒有,而如果有記錄又不知道是多少行,這時
          如果要對有記錄和沒有記錄進(jìn)行不同的處理,應(yīng)該用以下流程進(jìn)行判斷:

              if(rs.next()){
                  //因為已經(jīng)先next()了,所經(jīng)對記錄應(yīng)該用do{}while();來處理
                  do{
                      int i = rs.getInt(1);
                      String a = rs.getString("a2");
                  }while(rs.next());
              }
              esle{
                  System.out.println("沒有取得符合條件的記錄!");
              }

              類型轉(zhuǎn)換:
              ResultSet的getXXX方法將努力把結(jié)果集中的SQL數(shù)據(jù)類型轉(zhuǎn)換為JAVA的數(shù)據(jù)類型,事實(shí)
          大多數(shù)類型是可以轉(zhuǎn)換的,但仍然有不少糊弄是不能轉(zhuǎn)換的,如你不能將一個SQL的float轉(zhuǎn)換成
          JAVA的DATE,你無法將 VARCHAR "我們"轉(zhuǎn)換成JAVA的Int.

              較大的值:
              對于大于Statement中g(shù)etMaxFieldSize返回值的值,用普通的getBytes()或getString()
          是不能讀取的,好在JAVA提供了讀取輸入浪的方法,對于大對象,我們可以通過rs.getXXXStream()
          來得到一個InputStream,XXX的類型包括Ascii,Binay,Unicode.根據(jù)你存儲的字段類型來使用不
          同的流類型,一般來說,二進(jìn)制文件用getBinayStream(),文本文件用getAsciiStyream(),對于
          Unicode字符的文本文件用getUnicodeStream(),相對應(yīng)的數(shù)據(jù)庫字段類型應(yīng)該為:Blob,Clob和
          Nlob.

              獲取結(jié)果集的信息:
              大多數(shù)情況下編程人員對數(shù)據(jù)庫結(jié)構(gòu)是了解的,可以知道結(jié)果集中各列的情況,但有時并
          不知道結(jié)果集中有哪些列,是什么類型.這時可以通過getMetaData()來獲取結(jié)果集的情況:

              ResulSetMetaData rsmd = rs.getMetaData();
              rsmd.getColumnCount()返回列的個數(shù).
              getColumnLabel(int)返回該int所對應(yīng)的列的顯示標(biāo)題
              getColumnName(int)返回該int所對應(yīng)的列的在數(shù)據(jù)庫中的名稱.
              getColumnType(int)返回該int所對應(yīng)的列的在數(shù)據(jù)庫中的數(shù)據(jù)類型.
              getColumnTypeName(int)返回該int所對應(yīng)的列的數(shù)據(jù)類型在數(shù)據(jù)源中的名稱.
              isReadOnly(int)返回該int所對應(yīng)的列是否只讀.
              isNullable(int)返回該int所對應(yīng)的列是否可以為空
          posted @ 2006-01-09 10:04 javaGrowing 閱讀(343) | 評論 (0)編輯 收藏

          一、什么是嵌套類及內(nèi)部類?
              可以在一個類的內(nèi)部定義另一個類,這種類稱為嵌套類(nested classes),它有兩種類型: 靜態(tài)嵌套類和非靜態(tài)嵌套類。靜態(tài)嵌套類使用很少,最重要的是非靜態(tài)嵌套類,也即是被稱作為內(nèi)部類(inner)。嵌套類從JDK1.1開始引入。其中inner類又可分為三種:
              其一、在一個類(外部類)中直接定義的內(nèi)部類;
              其二、在一個方法(外部類的方法)中定義的內(nèi)部類;
              其三、匿名內(nèi)部類。
          下面,我將說明這幾種嵌套類的使用及注意事項。


           
          二、靜態(tài)嵌套類
              如下所示代碼為定義一個靜態(tài)嵌套類,

          public class StaticTest {
            private static String name = "javaJohn";
            private String id = "X001";

            static class Person{
              private String address = "swjtu,chenDu,China";
              public String mail = "josserchai@yahoo.com";//內(nèi)部類公有成員
              public void display(){
                //System.out.println(id);//不能直接訪問外部類的非靜態(tài)成員
                System.out.println(name);//只能直接訪問外部類的靜態(tài)成員
                System.out.println("Inner "+address);//訪問本內(nèi)部類成員。
              }
            }

             public void printInfo(){
               Person person = new Person();
               person.display();

               //System.out.println(mail);//不可訪問
               //System.out.println(address);//不可訪問

               System.out.println(person.address);//可以訪問內(nèi)部類的私有成員
               System.out.println(person.mail);//可以訪問內(nèi)部類的公有成員

             }

             public static void main(String[] args) {
                 StaticTest staticTest = new StaticTest();
                 staticTest.printInfo();
             }
          }

               在靜態(tài)嵌套類內(nèi)部,不能訪問外部類的非靜態(tài)成員,這是由Java語法中"靜態(tài)方法不能直接訪問非靜態(tài)成員"所限定。
          若想訪問外部類的變量,必須通過其它方法解決,由于這個原因,靜態(tài)嵌套類使用很少。注意,外部類訪問內(nèi)
          部類的的成員有些特別,不能直接訪問,但可以通過內(nèi)部類實(shí)例來訪問,這是因為靜態(tài)嵌套內(nèi)的所有成員和方法默認(rèn)為
          靜態(tài)的了。同時注意,內(nèi)部靜態(tài)類Person只在類StaticTest 范圍內(nèi)可見,若在其它類中引用或初始化,均是錯誤的。

          三、在外部類中定義內(nèi)部類
              如下所示代碼為在外部類中定義兩個內(nèi)部類及它們的調(diào)用關(guān)系:

          class Outer{
            int outer_x = 100;
          
            private class Inner{//私有的內(nèi)部類
             public int y = 10;
             private int z = 9;
             int m = 5;
             public void display(){
               System.out.println("display outer_x:"+ outer_x);
             }
          
             private void display2(){
              System.out.println("display outer_x:"+ outer_x);
             }
          
          }
          
           public Inner getInner(){//即使是對外公開的方法,外部類也無法調(diào)用
             return new Inner();
           }
          
           void test(){ 
             Inner inner = new Inner(); //可以訪問
             inner.display();
             inner.display2();
             //System.out.println("Inner y:" + y);//不能訪問內(nèi)部內(nèi)變量
             System.out.println("Inner y:" + inner.y);//可以訪問
             System.out.println("Inner z:" + inner.z);//可以訪問
             System.out.println("Inner m:" + inner.m);//可以訪問
          
             InnerTwo innerTwo = new InnerTwo();
             innerTwo.show();
           }
          
           class InnerTwo{
             Inner innerx = getInner();//可以訪問
             public void show(){
              //System.out.println(y);//不可訪問Innter的y成員
              //System.out.println(Inner.y);//不可直接訪問Inner的任何成員和方法
              innerx.display();//可以訪問
              innerx.display2();//可以訪問
              System.out.println(innerx.y);//可以訪問
              System.out.println(innerx.z);//可以訪問
              System.out.println(innerx.m);//可以訪問
             }
           }
          
          }
          
          public class Test
          {
           public static void main(String args[]){
            
            Outer outer = new Outer();
           // Outer.Inner a=outer.getInner();//Inner類是私有的,外部類不能訪問,如果Inner類是public ,則可以。
            outer.test();
           }
          }

              內(nèi)部類Inner及InnterTwo只在類Outer的作用域內(nèi)是可知的,如果類Outer外的任何代碼嘗試初始化類Inner或使用它,編譯就不會通過。同時,內(nèi)部類的變量成員只在內(nèi)部內(nèi)內(nèi)部可見,若外部類或同層次的內(nèi)部類需要訪問,需采用示例程序
          中的方法,不可直接訪問內(nèi)部類的變量。

          四、在方法中定義內(nèi)部類
              如下所示代碼為在方法內(nèi)部定義一個內(nèi)部類:

          public class FunOuter { 
           int out_x = 100;
          
           public void test(){
            class Inner{
              String x = "x";
              void display(){
                 System.out.println(out_x);
              }
            }
           Inner inner = new Inner();
           inner.display();
          }
          
          public void showStr(String str){
            //public String str1 = "test Inner";//不可定義,只允許final修飾
            //static String str4 = "static Str";//不可定義,只允許final修飾
            String str2 = "test Inner";
            final String str3 = "final Str";
            class InnerTwo{
              public void testPrint(){
              System.out.println(out_x);//可直接訪問外部類的變量
              //System.out.println(str);//不可訪問本方法內(nèi)部的非final變量
              //System.out.println(str2);//不可訪問本方法內(nèi)部的非final變量
              System.out.println(str3);//只可訪問本方法的final型變量成員
              }
            }
            InnerTwo innerTwo = new InnerTwo();
            innerTwo.testPrint();
          }
          
          public void use(){
             //Inner innerObj = new Inner();//此時Inner己不可見了。
             //System.out.println(Inner.x);//此時Inner己不可見了。
          }
          
          
           public static void main(String[] args) {
            FunOuter outer = new FunOuter();
            outer.test();
           }
          }
          
          


              從上面的例程我們可以看出定義在方法內(nèi)部的內(nèi)部類的可見性更小,它只在方法內(nèi)部 可見,在外部類(及外部類的其它方法中)中都不可見了。同時,它有一個特點(diǎn),就是方法內(nèi)的內(nèi)部類連本方法的成員變量都不可訪問,它只能訪問本方法的final型成員。同時另一個需引起注意的是方法內(nèi)部定義成員,只允許final修飾或不加修飾符,其它像static等均不可用。

          五、匿名內(nèi)部類
              如下所示代碼為定義一個匿名內(nèi)部類:匿名內(nèi)部類通常用在Java的事件處理上


          import java.applet.*;
          import java.awt.event.*;

              public class AnonymousInnerClassDemo extends Applet{
              public void init(){
                  addMouseListener(new MouseAdapter(){
                      public void mousePressed(MouseEvent me){
                       showStatus("Mouse Pressed!");
                  }
                  });
              }
              public void showStatus(String str){
                  System.out.println(str);
              }
              }


          在上面的例子中,方法addMouseListener接受一個對象型的參數(shù)表達(dá)式,于是,在參數(shù)里,我們定義了一個匿名內(nèi)部類,這個類是一個MouseAdapter類型的類,同時在這個類中定義了一個繼承的方法mousePressed,整個類做為一個參數(shù)。這個類沒有名稱,但是當(dāng)執(zhí)行這個表達(dá)式時它被自動實(shí)例化。同時因為,這個匿名內(nèi)部類是定義在AnonymousInnerClassDemo 類內(nèi)部的,所以它可以訪問它的方法showStatus。這同前面的內(nèi)部類是一致的。

          六、內(nèi)部類使用的其它的問題

               通過以上,我們可以清楚地看出內(nèi)部類的一些使用方法,同時,在許多時候,內(nèi)部類是在如Java的事件處理、或做為值對象來使用的。同時,我們需注意最后一個問題,那就是,內(nèi)部類同其它類一樣被定義,同樣它也可以繼承外部其它包的類和實(shí)現(xiàn)外部其它地方的接口。同樣它也可以繼承同一層次的其它的內(nèi)部類,甚至可以繼承外部類本身。下面我們給出最后一個例子做為結(jié)束:


          public class Layer {
            //Layer類的成員變量
            private String testStr = "testStr";
          
            //Person類,基類
            class Person{
              String name;
              Email email;
              public void setName(String nameStr){
               this.name = nameStr;
              }
          
              public String getName(){
                return this.name;
              } 
          
              public void setEmail(Email emailObj){
                this.email = emailObj;
              }
          
              public String getEmail(){
                return this.email.getMailStr();
              }
          
              //內(nèi)部類的內(nèi)部類,多層內(nèi)部類
              class Email{ 
               String mailID;
               String mailNetAddress;
          
               Email(String mailId,String mailNetAddress){
               this.mailID = mailId;
               this.mailNetAddress = mailNetAddress;
               }
          
               String getMailStr(){
                  return this.mailID +"@"+this.mailNetAddress;
               }
              } 
            }
          
             //另一個內(nèi)部類繼承外部類本身
             class ChildLayer extends Layer{
               void print(){
                 System.out.println(super.testStr);//訪問父類的成員變量
               }
             }
          
             //另個內(nèi)部類繼承內(nèi)部類Person
              class OfficePerson extends Person{ 
               void show(){
                 System.out.println(name);
                 System.out.println(getEmail()); 
               }
              }
          
              //外部類的測試方法 
              public void testFunction(){
                //測試第一個內(nèi)部類
                ChildLayer childLayer = new ChildLayer();
                childLayer.print();
          
                //測試第二個內(nèi)部類
                OfficePerson officePerson = new OfficePerson();
                officePerson.setName("abner chai");
                //注意此處,必須用對象.new 出來對象的子類對象
                //而不是Person.new Email(...)
                //也不是new Person.Email(...)
                officePerson.setEmail(officePerson.new Email("josserchai","yahoo.com"));
          
                 officePerson.show();
              }
          
               public static void main(String[] args) {
                Layer layer = new Layer();
                layer.testFunction();
               }
          }
          
          

          posted @ 2006-01-07 16:45 javaGrowing 閱讀(340) | 評論 (0)編輯 收藏

          用缺省設(shè)置創(chuàng)建時,ResultSet 是一種只能訪問一次(one-time-through)、只能向前訪問(forward-only)和只讀的對象。您只能訪問數(shù)據(jù)一次,如果再次需要該 數(shù)據(jù),必須重新查詢數(shù)據(jù)庫。

          然而,并不只有這一種方式。通過設(shè)置 Statement 對象上的參數(shù),您可以控制它產(chǎn)生的 ResultSet。例如:

          ...
                   Class.forName(driverName);
                   db = DriverManager.getConnection(connectURL);
                   Statement statement = db.createStatement(
                                      ResultSet.TYPE_SCROLL_SENSITIVE,
                                                            ResultSet.CONCUR_UPDATABLE
                                  );         
                   
                   String orderElName = xmlfileEl.getElementsByTagName("order").item(0)
                                                 .getFirstChild().getNodeValue();
          ...

          這個 Statement 現(xiàn)在將產(chǎn)生可以更新并將應(yīng)用其他數(shù)據(jù)庫用戶所作更改的 ResultSet。您還可以在這個 ResultSet 中向前和向后移動。

          第一個參數(shù)指定 ResultSet 的類型。其選項有:

          TYPE_FORWARD_ONLY:缺省類型。只允許向前訪問一次,并且不會受到其他用戶對該數(shù)據(jù)庫所作更改的影響。
          TYPE_SCROLL_INSENSITIVE:允許在列表中向前或向后移動,甚至可以進(jìn)行特定定位,例如移至列表中的第四個記錄或者從當(dāng)前位置向后移動兩個記錄。不會受到其他用戶對該數(shù)據(jù)庫所作更改的影響。
          TYPE_SCROLL_SENSITIVE:象 TYPE_SCROLL_INSENSITIVE 一樣,允許在記錄中定位。這種類型受到其他用戶所作更改的影響。如果用戶在執(zhí)行完查詢之后刪除一個記錄,那個記錄將從 ResultSet 中消失。類似的,對數(shù)據(jù)值的更改也將反映在 ResultSet 中。
          第二個參數(shù)設(shè)置 ResultSet 的并發(fā)性,該參數(shù)確定是否可以更新 ResultSet。其選項有:

          CONCUR_READ_ONLY:這是缺省值,指定不可以更新 ResultSet
          CONCUR_UPDATABLE:指定可以更新 ResultSet
          posted @ 2006-01-06 16:31 javaGrowing 閱讀(4435) | 評論 (1)編輯 收藏

               摘要: 作者:Rod Johnson 譯者:yanger,taowen 校對:taowen 關(guān)于Spring Framework,今年夏天你可能已經(jīng)聽見很多的議論。在本文中,我將試圖解釋Spring能完成什么,和我怎么會認(rèn)為它能幫助你開發(fā)J2EE應(yīng)用程序。 又來一個framework? 你可能正在想“不過是另外一個的framework”。當(dāng)已經(jīng)有許多...  閱讀全文
          posted @ 2006-01-06 09:24 javaGrowing 閱讀(338) | 評論 (0)編輯 收藏

          從 ASP.NET 服務(wù)器控件插入客戶端腳本

          Scott Mitchell

          2003 年 8 月

          適用于:
              Microsoft? ASP.NET

          前提條件:本文假設(shè)讀者熟悉 ASP.NET。

          難度: 2

          摘要:盡管從技術(shù)角度講,ASP.NET 服務(wù)器控件的所有功能都可以在服務(wù)器端執(zhí)行,但通常情況下通過添加客戶端腳本可以大大增強(qiáng)服務(wù)器控件的可用性。本文將探討服務(wù)器控件發(fā)送客戶端腳本的兩種方法,還將構(gòu)建兩個使用這些技術(shù)的服務(wù)器控件:PopupGreeting,一個在首次加載的 Web 頁面上顯示帶有特定消息的客戶端模式對話框的服務(wù)器控件;ConfirmButton,一個增強(qiáng)的 Button Web 控件,如果用戶點(diǎn)擊此按鈕,則在發(fā)回 Web 窗體前向用戶顯示一個 JavaScript confirm() 的對話框。(本文包含一些指向英文站點(diǎn)的鏈接。)

          下載 InjectingClientSideScript.msi

          目錄

          簡介
          使用 RegisterStartupScript() 和 RegisterClientScriptBlock() 添加客戶端腳本塊
          探討 IsStartupScriptRegistered() 和 IsClientScriptBlockRegistered()
          從 ASP.NET 服務(wù)器控件發(fā)送客戶端腳本塊
          發(fā)送 ASP.NET 服務(wù)器 Web 控件的 HTML 屬性
          小結(jié)

          簡介

          盡管從技術(shù)角度講,Microsoft? ASP.NET 服務(wù)器控件的所有功能都可以在服務(wù)器端執(zhí)行,但通常情況下通過添加客戶端腳本可以大大增強(qiáng)服務(wù)器控件的可用性。例如,ASP.NET 驗證 Web 控件可以在服務(wù)器端執(zhí)行所有的驗證檢查。但是,對于高版本瀏覽器,驗證 Web 控件也會發(fā)送客戶端腳本,以在客戶端進(jìn)行驗證。這就是說,這些瀏覽器的用戶可以獲得響應(yīng)效果更好的動態(tài)體驗。

          在開發(fā) ASP.NET 服務(wù)器控件時,您不妨問問自己,如何才能通過使用客戶端腳本來增強(qiáng)可用性。一旦找到可行的方案,其他要做的就是增強(qiáng)服務(wù)器控件的功能,以使其發(fā)送合適的客戶端腳本。

          ASP.NET 服務(wù)器控件可以發(fā)送兩種客戶端腳本:

          • 客戶端腳本塊
          • 客戶端 HTML 屬性

          客戶端腳本塊通常是用 JavaScript 編寫的,其中通常包含在發(fā)生特定的客戶端事件時執(zhí)行的函數(shù)??蛻舳?HTML 屬性提供將客戶端事件與客戶端腳本聯(lián)系在一起的方法。例如,以下的 HTML 頁面中包含了客戶端腳本塊,腳本塊中包含了名為 doClick() 的函數(shù)。該頁面同時還包含一個按鈕(通過 <input> HTML 元素創(chuàng)建),這個按鈕的 onclick 屬性與 doClick() 函數(shù)綁定。也就是說,只要用戶單擊該按鈕,就開始執(zhí)行 doClick() 函數(shù)中的客戶端代碼。在本示例中,將顯示一個彈出式對話框(圖 1)。

          <html>
          <body>
          <form>
          <script language="JavaScript">
          <!--
          function doClick() {
          alert("You clicked me!");
          }
          // -->
          </script>

          <input type="button" onclick="doClick()" value="Click Me!" />
          </form>
          </body>
          </html>

          圖 1 是單擊“Click Me!”按鈕時 HTML 頁面的屏幕快照。

          圖 1:單擊“Click Me!”按鈕時顯示的彈出式對話框

          對于以上 HTML 頁面中的客戶端腳本,有幾點(diǎn)值得注意。首先,客戶端腳本塊包含在 HTML 注釋(<!---->)中。之所以這樣,是因為如果不將腳本塊放入 HTML 注釋中,那些不能識別腳本的舊式瀏覽器就會顯示 <script> 塊的內(nèi)容。此外,還要注意,腳本塊中 HTML 注釋的結(jié)束標(biāo)記前有一個 JavaScript 注釋,即 //。這是因為舊版本的 Netscape 在遇到 --> 時,會拋出 JavaScript 分析異常,因此必須將其注釋掉。幸運(yùn)的是,現(xiàn)代的瀏覽器已不需要這一額外操作,所以在為 Intranet 或其他由瀏覽器控制的環(huán)境開發(fā) Web 頁面時,您就不必采取此類預(yù)防措施了。

          如果您對客戶端腳本不是很熟悉,alert(string) 函數(shù)的作用就是顯示一個模式彈出式對話框,對話框中包含的消息由 string 參數(shù)指定。所有 HTML 元素都有若干個可以綁定一段客戶端 JavaScript 代碼的客戶端屬性(例如,onclick、onmouseoveronmouseoutonfocusonblur 等等)。例如,在上面的 HTML 頁面中,<input> 元素的 onclick 屬性綁定到 doClick() 函數(shù),因此在單擊該按鈕時將執(zhí)行 doClick() 函數(shù)。有關(guān) JavaScript 事件及其關(guān)聯(lián)的 HTML 屬性的列表,請參閱 Introduction to Dynamic HTML 一文。有關(guān)客戶端 JavaScript 的詳細(xì)信息,請參閱 HTML and Dynamic HTML 一文。

          在本文中,我們將學(xué)習(xí)如何在 ASP.NET 服務(wù)器控件中發(fā)送客戶端腳本塊和 HTML 元素屬性。我們首先討論如何使用 System.Web.UI.Page 類中的兩個方法來向 ASP.NET Web 頁面添加客戶端腳本塊,這兩個方法是 RegisterStartupScript()RegisterClientScriptBlock()。 掌握這一知識后,我們將構(gòu)建一個簡單的服務(wù)器控件,讓這個控件在每次加載頁面時顯示一個客戶端彈出式對話框。之后,我們再來了解如何將 HTML 屬性添加到 ASP.NET 服務(wù)器控件的 HTML 元素。最后,我們將歸納所有知識,實(shí)際構(gòu)建一個 ConfirmButton Web 控件,當(dāng)單擊這個控件時,將向用戶提示一個對話框,詢問用戶是否要繼續(xù)。

          使用 RegisterStartupScript() 和 RegisterClientScriptBlock() 添加客戶端腳本塊

          System.Web.UI.Page 類包含的兩個方法可以將客戶端腳本代碼發(fā)送到由 ASP.NET Web 頁面提供的 HTML 中:

          • RegisterStartupScript(key, script)
          • RegisterClientScriptBlock(key, script)

          這兩個方法都接受兩個字符串作為輸入。第二個參數(shù) script 是要插入到頁面中的客戶端腳本,包括 <script> 的起始標(biāo)記和終止標(biāo)記。第一個參數(shù) key 是插入的客戶端腳本的唯一標(biāo)識符。

          這兩個方法唯一的不同之處在于從“何處”發(fā)送腳本塊。RegisterClientScriptBlock() 在 Web 窗體的開始處(緊接著 <form runat="server"> 標(biāo)識之后)發(fā)送腳本塊,而 RegisterStartupScript() 在 Web 窗體的結(jié)尾處(在 </form> 標(biāo)識之前)發(fā)送腳本塊。

          為什么會有兩種不同的方法來發(fā)送客戶端腳本?要更好地了解這一點(diǎn),我們必須首先了解,客戶端腳本可以分為兩類:一類是在加載頁面后立即運(yùn)行的代碼, 一類是在發(fā)生某些客戶端事件時才運(yùn)行的代碼。前者的常見示例是將焦點(diǎn)設(shè)置到文本框的客戶端代碼。例如,當(dāng)您訪問 Google 時,在頁面加載后就會執(zhí)行一小段客戶端代碼,以自動將焦點(diǎn)設(shè)置到搜索文本框。

          以下是后一類代碼(為響應(yīng)客戶端事件而運(yùn)行的代碼)的示例。具體而言,在該示例中,單擊按鈕時將顯示一個彈出式對話框:

          <html>
          <body>
          <form>
          <script language="JavaScript">
          <!--
          function displayPopup() {
          alert("Hello, world.");
          }
          // -->
          </script>

          <input type="button" value="Click Me!" onclick="displayPopup()" />
          </form>
          </body>
          </html>

          在這段代碼中,<input> 標(biāo)記中的 onclick="displayPopup()" 用于指明在單擊按鈕時,JavaScript 函數(shù) displayPopup() 應(yīng)該運(yùn)行。

          RegisterStartupScript() 方法可用于添加要在加載頁面后運(yùn)行的腳本塊。通過這種方法添加的腳本塊位于 Web 窗體的結(jié)尾處,因為必須在腳本運(yùn)行前定義腳本要修改的 HTML 元素。也就是說,如果您要使用客戶端腳本將焦點(diǎn)設(shè)置到文本框,必須確保文本框的 HTML 標(biāo)記位于設(shè)置該文本框的焦點(diǎn)的腳本之前。例如,下面的 HTML 將顯示一個文本框,并將焦點(diǎn)設(shè)置到該文本框:

          <input type="text" id="myTextBox" />

          <script language="JavaScript">
          <!--
          document.getElementById("myTextBox").focus();
          // -->
          </script>

          相反,以下 HTML 不會將焦點(diǎn)設(shè)置到文本框,因為文本框是在腳本塊“之后”定義的:

          <script language="JavaScript">
          <!--
          document.getElementById("myTextBox").focus();
          // -->
          </script>

          <input type="text" id="myTextBox" />

          因此,RegisterStartupScript() 方法將 <script> 塊置于 Web 窗體的結(jié)尾處,以保證在執(zhí)行客戶端腳本之前已聲明 Web 窗體中的所有 HTML 元素。

          RegisterClientScriptBlock() 方法用于為響應(yīng)客戶端事件而執(zhí)行的腳本代碼。通過此方法發(fā)送的腳本塊位于 Web 頁面的開始處,因為這種方法不要求將腳本塊置于所有 HTML 元素之后。

          探討 IsStartupScriptRegistered() 和 IsClientScriptBlockRegistered()

          RegisterStartupScript()RegisterClientScriptBlock() 方法之外,Page 類還包含兩個在發(fā)送客戶端腳本時常用的輔助方法:

          • IsStartupScriptRegistered(key)
          • IsClientScriptBlockRegistered(key)

          如上所述,在使用 RegisterStartupScript()RegisterClientScriptBlock() 插入客戶端腳本塊時,提供了一個唯一標(biāo)識腳本塊的關(guān)鍵字。這兩個方法都接受一個輸入(字符串 key),并返回一個布爾值,以指示帶有指定關(guān)鍵字的腳本塊是否已添加到頁面中。具體地說,如果帶有特定 key 的腳本塊已經(jīng)注冊,這些方法將返回 True,否則將返回 False。

          要了解如何使用這兩個方法,可以看一看 ASP.NET 驗證 Web 控件,如 RequiredFieldValidator、RegularExpressionValidator 等等。這些控件都會用到一個常用的驗證 JavaScript 文件 (WebValidation.js),該文件位于 ASP.NET Web 應(yīng)用程序的 aspnet_client/system_web/版本號 目錄中。因此,所有這些控件都會發(fā)送相同的腳本塊,這個腳本塊將調(diào)用在 WebValidation.js 文件中定義的相應(yīng)的 JavaScript 函數(shù),以啟動客戶端的驗證過程。要完成這個過程,這些控件會使用 Page 類的 RegisterClientScriptBlock() 方法,并使用關(guān)鍵字 ValidatorIncludeScript。

          接下來要考慮的是,如果一個 ASP.NET Web 頁面中包含多個驗證 Web 控件,會出現(xiàn)什么情況呢?所有這些 Web 控件都要使用相同的關(guān)鍵字發(fā)送相同的腳本塊。如果使用這個關(guān)鍵字調(diào)用兩次 RegisterClientScriptBlock()RegisterStartupScript() 方法,則第二次調(diào)用會被認(rèn)為是復(fù)制腳本塊而被忽略。因此,即使一個 Web 頁面上有多個驗證控件,也只是發(fā)送一個公共腳本塊的實(shí)例。但是,請注意,除第一個控件之外的其他所有驗證 Web 控件都會構(gòu)建要發(fā)送的公共客戶端腳本,而這只是在浪費(fèi)時間。

          這時就應(yīng)該使用 IsClientScriptBlock()IsStartupScript() 方法。這樣一來,驗證 Web 控件就不會先花時間構(gòu)建要發(fā)送的客戶端代碼,而是先檢查是否已經(jīng)存在使用關(guān)鍵字 ValidatorIncludeScript 注冊的腳本。如果存在,控件就會放棄構(gòu)建客戶端腳本塊,因為腳本塊已經(jīng)由頁面上的其他驗證控件構(gòu)建了。

          因此,每次構(gòu)建客戶端腳本時,應(yīng)該首先調(diào)用 IsClientScriptBlock()IsStartupScript() 方法,以確定是否需要生成客戶端腳本。在下面一節(jié),我們將看到一些示例,在這些示例中,IsClientScriptBlock()、IsStartupScript() 方法先后與 RegisterClientScriptBlock()RegisterStartupScript() 方法結(jié)合使用。

          從 ASP.NET 服務(wù)器控件發(fā)送客戶端腳本塊

          請記住,RegisterStartupScript()RegisterClientScriptBlock() 方法是 System.Web.UI.Page 類的方法。幸運(yùn)的是,可以容易地從 ASP.NET 服務(wù)器控件調(diào)用這兩個方法,因為 System.Web.UI.Control 類(所有 ASP.NET 服務(wù)器控件都直接或間接地從這個類導(dǎo)出)有一個包含對 Page 實(shí)例的引用的 Page 屬性,而這個 Page 實(shí)例包含服務(wù)器控件。因此,要從 ASP.NET 服務(wù)器控件添加客戶端腳本塊,您只需使用下面的語法:

          this.Page.RegisterClientScriptBlock(key, script);

          通常,添加客戶端腳本塊這個任務(wù)會使用 OnPreRender() 方法來處理,這個方法在控件生命周期的預(yù)呈現(xiàn)階段執(zhí)行。

          讓我們創(chuàng)建一個只顯示客戶端彈出式對話框的 ASP.NET 服務(wù)器控件。此示例將說明構(gòu)建一個發(fā)送客戶端腳本的控件是很容易的。

          首先,在 Microsoft? Visual Studio? .NET 中創(chuàng)建一個新的 Web Control Library(Web 控件庫)項目。這將創(chuàng)建一個只有一個類的新項目,這個類從 System.Web.UI.WebControls.WebControl 導(dǎo)出。但是,我們希望這個類從 System.Web.UI.Control 類導(dǎo)出。為什么呢?因為 WebControl 類用于支持顯示為 HTML 元素的服務(wù)器控件,而 Control 類則用于不會顯示為 HTML 元素的服務(wù)器控件。

          大多數(shù)內(nèi)置的 ASP.NET 服務(wù)器控件都會發(fā)送一個 HTML 元素。例如,TextBox Web 控件發(fā)送一個 <input> 元素,其類型屬性設(shè)置為 text;DataGrid Web 控件發(fā)送一個 <table> 元素,為每條要顯示的記錄發(fā)送 <tr> 元素,為每個字段發(fā)送 <td> 列。但是,不是所有的控件都需要發(fā)送 HTML 元素。例如,Literal 控件只是按原樣輸出它的 Text 屬性,而不將這個屬性放在 HTML 元素中。同樣,Repeater 也不將其輸出放在 HTML 元素中。那些顯示為 HTML 元素的服務(wù)器控件,如 TextBox、Button、DataGrid 等等,是從 System.Web.UI.WebControls.WebControl 類導(dǎo)出的,而那些產(chǎn)生 HTML 元素的控件,如 Literal、Repeater 等,是從 System.Web.UI.Control 類導(dǎo)出的。

          既然我們要創(chuàng)建的服務(wù)器控件不可見(它只是發(fā)送一個顯示彈出式控件的客戶端腳本塊),這個控件最好從 System.Web.UI.Control 導(dǎo)出,而不是從 System.Web.UI.WebControls.WebControl 導(dǎo)出。

          這個控件只需要兩個屬性:

          • PopupMessage - 表示要在彈出式對話框中顯示的消息的字符串。
          • Enabled - 表示是否啟用控件的布爾值。如果啟用控件,則顯示彈出式對話框,否則不顯示。(必須添加一個 Enabled 屬性,是因為導(dǎo)出該控件的 Control 類不包括 Enabled 屬性,此屬性只是隱含地由那些從 WebControl 導(dǎo)出的控件使用。)

          除了這兩種屬性之外,我們需要覆蓋 OnPreRender() 方法。在這里,我們需要調(diào)用 RegisterStartupScript(),并傳遞控件唯一的關(guān)鍵字和恰當(dāng)?shù)目蛻舳四_本以顯示彈出式對話框。這個類的完整代碼如下所示:

          using System;
          using System.Web.UI;
          using System.Web.UI.WebControls;
          using System.ComponentModel;

          namespace ClientSideScript
          {
          /// <summary>
          /// WebCustomControl1 的摘要描述。
          /// </summary>
          [DefaultProperty("Text"),
          ToolboxData("<{0}吐舌笑臉opupGreeting runat=server></{0}吐舌笑臉opupGreeting>")]
          public class PopupGreeting : System.Web.UI.Control
          {
          [Bindable(true),
          Category("Appearance"),
          DefaultValue("")]
          public string PopupMessage
          {
          get
          {
          // 檢查 ViewState 中是否存在該項目
          object popupMessage = this.ViewState["PopupMessage"];
          if (popupMessage != null)
          return this.ViewState["PopupMessage"].ToString();
          else
          return "Welcome to my Web site!";
          }

          set
          {
          // 指定 ViewState 變量
          ViewState["PopupMessage"] = value;
          }
          }

          [Bindable(true),
          Category("Appearance"),
          DefaultValue("")]
          public bool Enabled
          {
          get
          {
          // 檢查 ViewState 中是否存在該項目
          object enabled = this.ViewState["Enabled"];
          if (enabled != null)
          return (bool) this.ViewState["Enabled"];
          else
          return true;
          }

          set
          {
          // 指定 ViewState 變量
          ViewState["Enabled"] = value;
          }
          }


          protected override void OnPreRender(EventArgs e)
          {
          base.OnPreRender電子郵件;

          string scriptKey = "intoPopupMessage:" + this.UniqueID;

          if (!Page.IsStartupScriptRegistered(scriptKey) && this.Enabled &&
          !Page.IsPostBack)
          {
          string scriptBlock =
          @"<script language=""JavaScript"">
          <!--
          alert(""%%POPUP_MESSAGE%%"");
          // -->
          </script>";
          scriptBlock = scriptBlock.Replace("%%POPUP_MESSAGE%%", this.PopupMessage);

          Page.RegisterStartupScript(scriptKey, scriptBlock);
          }
          }
          }
          }

          請記住下面兩件事:首先,EnabledPopupMessage 屬性保存在 ViewState 中,這樣在回傳時這些值可以始終保持一致; 其次,在 OnPreRender() 方法中,用于腳本塊的關(guān)鍵字是文本 intoPopupMessage: 加上控件的 UniqueID 屬性。如果使用一個硬編碼的關(guān)鍵字,則當(dāng)頁面中有多個控件時,只有第一個控件能夠注冊其腳本塊,因此只顯示一個彈出式對話框。通過在腳本塊關(guān)鍵字中使用 UniqueID,就能保證該控件的每個實(shí)例都能獲取其腳本塊。

          在注冊腳本塊之前,代碼首先檢查三個條件:

          1. 沒有使用同一關(guān)鍵字注冊的腳本。這當(dāng)然是不可能的,因為每個控件實(shí)例都應(yīng)該有一個 UniqueID 屬性值。但是,不妨先練習(xí)使用 IsStartupScriptRegistered() 方法,然后再花時間創(chuàng)建和注冊啟動腳本。
          2. 控件的 Enabled 屬性為 True。
          3. 頁面沒有被回傳。這段代碼只允許彈出式對話框在第一次加載頁面時顯示,而不是在每次回傳頁面時都顯示。我們還可以增添更為靈活的功能,即為該控件添加一個布爾屬性,以允許用戶指定是否在回傳時也生成彈出式對話框。

          如果滿足這三個條件,則腳本被指定,并且 PopupMessage 屬性值被插入到腳本中適當(dāng)?shù)奈恢?。最后,調(diào)用 Page 屬性的 RegisterStartupScript() 方法,傳入關(guān)鍵字及腳本代碼。

          PopupGreeting 代碼可以從本文結(jié)尾處提供的下載中獲得。該下載包括名為 ClientSideControlsAndTester 的 Visual Studio .NET 解決方案,其中包含兩個項目:

          • ClientSideControls,包含 PopupGreeting 服務(wù)器控件
          • ClientSideTester,包括一個為測試 ClientSideControls 而設(shè)計的 ASP.NET Web 應(yīng)用程序

          ClientSideControls 項目編譯后的程序集名為 ClientSideControls.dll。要在您自己的 ASP.NET Web 應(yīng)用程序中使用 PopupGreeting 服務(wù)器控件,請將 ClientSideControls.dll 文件添加到您的 Web 應(yīng)用程序的引用中。然后,在設(shè)計器中,右鍵單擊 Toolbox(工具箱)并選擇“Add/Remove Items . . .”(添加/刪除項),再次選擇 ClientSideControls.dll 文件。這樣就向 Toolbox(工具箱)中添加了名為 PopupGreeting 的新項。然后,您可以從 Toolbox(工具箱)將該控件拖到設(shè)計器中。

          圖 2 顯示了 PopupGreeting 控件添加到 Toolbox(工具箱)并添加到設(shè)計器后,Visual Studio .NET 的屏幕快照。Toolbox(工具箱)中的 PopupGreeting 控件用紅色線圈出,設(shè)計器中的 PopupGreeting 輸出用藍(lán)色線圈出,在屏幕快照右側(cè)的“Properties”(屬性)窗格中可以查看 PopupGreeting 的屬性。

          圖 2:PopupGreeting 服務(wù)器控件已添加到 ASP.NET Web 窗體頁面

          發(fā)送 ASP.NET 服務(wù)器 Web 控件的 HTML 屬性

          如上所述,有兩種方法可以通過服務(wù)器控件發(fā)送客戶端腳本:

          • 通過使用客戶端腳本塊
          • 通過 HTML 元素屬性

          在上一節(jié)中,我們探討了如何使用 Page 類的 RegisterStartupScript()RegisterClientScriptBlock() 方法向 ASP.NET Web 頁面添加客戶端腳本塊。在最后這一節(jié),我們了解如何將 HTML 元素屬性添加到服務(wù)器控件的 HTML 元素。

          在開始之前,請注意這種方法通常只適用于從 System.Web.UI.WebControls.WebControl 類導(dǎo)出的服務(wù)器控件,因為從這個類導(dǎo)出的控件會發(fā)送某些 HTML 元素。不發(fā)送 HTML 元素的服務(wù)器控件(如上一節(jié)中的 PopupGreeting 服務(wù)器控件),則不必寫出 HTML 元素屬性,因為這些控件運(yùn)行時不會寫出 HTML 元素。

          WebControl 類包含一個將 HTML 元素屬性添加到由 Web 控件發(fā)出的 HTML 元素的方法。該方法稱為 AddAttributesToRender(),它只有一個輸入?yún)?shù),即 HtmlTextWriter 的實(shí)例。要向 Web 控件添加 HTML 屬性,您可以使用 HtmlTextWriter 的以下兩個方法之一:

          • AddAttribute()
          • AddStyleAttribute()

          AddAttribute() 方法用于將 title、class、styleonclick 等 HTML 屬性添加到 HTML 元素。AddStyleAttribute() 用于將樣式設(shè)置添加到 HTML 元素,如 background-color、colorfont-size 等。

          AddAttribute() 有幾個重載窗體,但在代碼中,我們將使用以下窗體:AddAttribute(HtmlTextWriterAttribute, value)。第一個參數(shù),即 HtmlTextWriterAttribute,應(yīng)該是 HtmlTextWriterAttribute 枚舉的成員。該枚舉包含像 Align、Bgcolor、ClassOnclick 等項。您可以在 .NET Framework Class Library,HtmlTextWriterAttribute Enumeration 中找到完整的列表。value 輸入?yún)?shù)用于指定分配給特定 HTML 屬性的值。最后,如果您想添加一個 HtmlTextWriterAttribute 枚舉中未定義的 HTML 屬性,可以使用 AddAttribute() 方法的替代形式 AddAttribute(attributeName, value),其中的 attributeNamevalue 均為字符串。

          為了運(yùn)用該信息,我們創(chuàng)建一個作為確認(rèn)按鈕的服務(wù)器 Web 控件。確認(rèn)按鈕是一種提交按鈕,當(dāng)用戶單擊此按鈕時,將顯示一個彈出式對話框,詢問用戶是否確定要繼續(xù)操作。用戶可以單擊“取消”,不提交窗體。此項功能 對用于刪除信息的按鈕特別有用,因為最終用戶(或網(wǎng)站管理員)可能會在無意中單擊鼠標(biāo)刪除數(shù)據(jù)庫中的條目,如果沒有機(jī)會取消,將是非常令人煩惱的事。

          為了減少工作量,我們從 System.Web.UI.WebControls.Button 類中導(dǎo)出 ConfirmButton Web 控件,因為這個類本身已完成了涉及呈現(xiàn)提交按鈕的所有繁重工作。在導(dǎo)出的類中,我們只需添加一個屬性,這樣用戶可以指定確認(rèn)消息,然后覆蓋按鈕的 AddAttributesToRender() 方法,并添加一個屬性以處理客戶端事件 onclick

          首先,在 Visual Studio .NET 中創(chuàng)建一個新的 Web Control Library(Web 控件庫)項目,或者在 ClientSideControls 項目中添加一個新的 Web Custom Control(Web 自定義控件)。ConfirmButton 類的完整源代碼如下所示:

          using System;
          using System.Web.UI;
          using System.Web.UI.WebControls;
          using System.ComponentModel;

          namespace ClientSideControls
          {
          /// <summary>
          /// ConfirmButton 的摘要描述。
          /// </summary>
          [DefaultProperty("Text"),
          ToolboxData("<{0}:ConfirmButton runat=server></{0}:ConfirmButton>")]
          public class ConfirmButton : Button
          {
          [Bindable(true),
          Category("Appearance"),
          DefaultValue("")]
          public string PopupMessage
          {
          get
          {
          // 檢查 ViewState 中是否存在該項目
          object popupMessage = this.ViewState["PopupMessage"];
          if (popupMessage != null)
          return this.ViewState["PopupMessage"].ToString();
          else
          return "Are you sure you want to continue?";
          }

          set
          {
          // 指定 ViewState 變量
          ViewState["PopupMessage"] = value;
          }
          }


          protected override void AddAttributesToRender(HtmlTextWriter writer)
          {
          base.AddAttributesToRender(writer);

          string script = @"return confirm(""%%POPUP_MESSAGE%%"");";
          script = script.Replace("%%POPUP_MESSAGE%%",
          this.PopupMessage.Replace("\"", "\\\""));

          writer.AddAttribute(HtmlTextWriterAttribute.Onclick, script);
          }
          }
          }

          首先要注意的是,ConfirmButton 類是從 Button 類導(dǎo)出的。由于 Button 類已包含 Button Web 控件使用的所有屬性和方法,因此我們所做的只是添加屬性和方法,以在用戶單擊按鈕時顯示一個確認(rèn)對話框?,F(xiàn)在我們需要一個屬性,即 PopupMessage, 它是要在確認(rèn)彈出式對話框中顯示的消息。默認(rèn)情況下,這條消息是“Are you sure you want to continue?”(您確定要繼續(xù)嗎?)如果使用 ConfirmButton 來確認(rèn)刪除,可能需要將該消息更改為“This action will permanently delete the selected item. Are you sure you want to do this?”(此操作將永久刪除所選項。您確定要繼續(xù)嗎?)

          我們只需覆蓋一個方法,即 AddAttributesToRender()。在此方法中,我們只要構(gòu)建當(dāng)觸發(fā) <input> 元素的 onclick 事件時要執(zhí)行的客戶端 JavaScript,然后通過傳入的 HtmlTextWriter 對象的 AddAttribute() 方法添加這段 JavaScript。關(guān)于這個方法,有一點(diǎn)要注意,必須將 PopupMessage 屬性值中的所有雙引號實(shí)例替換為轉(zhuǎn)義雙引號(即 \")。另外還要注意,默認(rèn)情況下,AddAttribute() 會對第二個參數(shù)中的字符進(jìn)行 HTML 編碼。也就是說,ASP.NET Web 頁面中如果包含 PopupMessage 屬性被設(shè)置為“Do you want to continue?”(要繼續(xù)嗎?)的 ConfirmButton,該頁面將發(fā)送以下 HTML 標(biāo)記:

          <input type="submit" name="ConfirmButton1" 
          value="Click Me!" id="ConfirmButton1" onclick="return confirm
          (&quot;Do you want to continue?&quot眨眼笑臉;" />

          如果您不熟悉 JavaScript 的 confirm(string) 函數(shù),那么請您注意,該函數(shù)只接受一個字符串參數(shù),并顯示一個帶有特定字符串的模式對話框。該對話框中包含兩個按鈕:“確定”和“取消”。如果單擊“確定”,confirm() 函數(shù)返回 True,否則返回 False。請注意,onclick 事件將返回 confirm() 函數(shù)調(diào)用的結(jié)果。當(dāng)通過單擊提交按鈕來提交表單時,如果提交按鈕的 onclick 事件返回 False,則表單未被提交。因此,只有在用戶確認(rèn)后,可以使用 confirm() 函數(shù)提交表單。有關(guān) confirm() 的詳細(xì)信息,請參閱 ASP Warrior 網(wǎng)站中的 Javascript Confirm Form Submission。

          圖 3:操作中的 ConfirmButton

          ConfirmButton 在按鈕的 onclick 事件處理程序中使用了內(nèi)嵌的 JavaScript,還可以在 ConfirmButton 的 OnPreRender() 方法的客戶端腳本塊中創(chuàng)建一個函數(shù),然后調(diào)整 onclick 屬性以調(diào)用該函數(shù)。

          小結(jié)

          在本文中,我們探討了兩種通過 ASP.NET 服務(wù)器控件插入客戶端腳本的方法。第一種方法是使用 Page 類的 RegisterStartupScript()RegisterClientScriptBlock() 方法插入客戶端腳本塊。第二種方法是向 HTML 元素的屬性添加客戶端腳本。后者通過覆蓋 Web 服務(wù)器控件的 AddAttributesToRender() 方法,并使用 HtmlTextWriterAddAttribute() 方法來完成。

          我們還在文中介紹了兩個簡單的服務(wù)器控件,它們都利用了客戶端腳本來改進(jìn)其功能。PopupGreeting 控件在頁面首次加載時顯示一個模式彈出式對話框,ConfirmButton Web 控件在用戶單擊按鈕提交表單時,提示用戶進(jìn)行確認(rèn)。

          您可以在自己的服務(wù)器控件中插入客戶端腳本,這將顯著改善用戶體驗。本文提供的兩個服務(wù)器控件相對比較簡單,在可用性和獨(dú)創(chuàng)性上沒有什么突出之處。MetaBuilders.com 中展示了很多利用從 ASP.NET 服務(wù)器控件中插入客戶端腳本而實(shí)現(xiàn)的功能,這些功能會給您留下深刻印象。在 MetaBuilders.com,您可以找到一些服務(wù)器控件,它們有的可以自動將焦點(diǎn)添加到文本框,有的可以在兩個下拉列表之間傳遞條目,有的可以向下 拉列表中添加或刪除條目,還有的可以在一系列下拉列表中顯示父子關(guān)系的數(shù)據(jù),等等。最大的好處是,這些控件是免費(fèi)的,并包括完整的源代碼。

          祝大家編程快樂!

          作者簡介

          Scott Mitchell 著有五本關(guān)于 ASP/ASP.NET 的書籍,是 4GuysFromRolla.com 網(wǎng)站的創(chuàng)始人,過去五年來一直從事 Microsoft Web 技術(shù)方面的研究。Scott 是 ASP 和 ASP.NET 社區(qū)非?;钴S的一名成員,十分熱愛 ASP 和 ASP.NET 技術(shù),并非常愿意幫助其他人了解這些令人振奮的技術(shù)。有關(guān) DataGrid、DataList 和 Repeater 控件的詳細(xì)信息,請參閱 Scott 的著作《ASP.NET Data Web Controls Kick Start》(ISBN 為 0672325012)。

          推薦鏈接:

          posted @ 2006-01-05 15:14 javaGrowing 閱讀(292) | 評論 (0)編輯 收藏

          僅列出標(biāo)題
          共19頁: First 上一頁 11 12 13 14 15 16 17 18 19 下一頁 
          主站蜘蛛池模板: 竹山县| 重庆市| 卫辉市| 岐山县| 通州市| 崇左市| 玛纳斯县| 上蔡县| 邢台市| 定陶县| 西华县| 鄂托克前旗| 玉环县| 苍南县| 逊克县| 高邮市| 望江县| 永和县| 平利县| 攀枝花市| 通山县| 满城县| 河池市| 页游| 内江市| 湾仔区| 东阿县| 合水县| 东辽县| 玛纳斯县| 东海县| 巴彦淖尔市| 武穴市| 鄂州市| 阿瓦提县| 阿克苏市| 西安市| 澄江县| 黔东| 海晏县| 姜堰市|