<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 63  文章 - 0  trackbacks - 0
    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    常用鏈接

    留言簿(2)

    隨筆分類

    隨筆檔案

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    1. package cn.edu.tongji.cims.wade.system;   
    2.   
    3. import java.io.*;   
    4.   
    5. public class FileOperate {   
    6.   public FileOperate() {   
    7.   }   
    8.   
    9.   /**  
    10.    * 新建目錄  
    11.    * @param folderPath String 如 c:/fqf  
    12.    * @return boolean  
    13.    */  
    14.   public void newFolder(String folderPath) {   
    15.     try {   
    16.       String filePath = folderPath;   
    17.       filePath = filePath.toString();   
    18.       java.io.File myFilePath = new java.io.File(filePath);   
    19.       if (!myFilePath.exists()) {   
    20.         myFilePath.mkdir();   
    21.       }   
    22.     }   
    23.     catch (Exception e) {   
    24.       System.out.println("新建目錄操作出錯");   
    25.       e.printStackTrace();   
    26.     }   
    27.   }   
    28.   
    29.   /**  
    30.    * 新建文件  
    31.    * @param filePathAndName String 文件路徑及名稱 如c:/fqf.txt  
    32.    * @param fileContent String 文件內容  
    33.    * @return boolean  
    34.    */  
    35.   public void newFile(String filePathAndName, String fileContent) {   
    36.   
    37.     try {   
    38.       String filePath = filePathAndName;   
    39.       filePath = filePath.toString();   
    40.       File myFilePath = new File(filePath);   
    41.       if (!myFilePath.exists()) {   
    42.         myFilePath.createNewFile();   
    43.       }   
    44.       FileWriter resultFile = new FileWriter(myFilePath);   
    45.       PrintWriter myFile = new PrintWriter(resultFile);   
    46.       String strContent = fileContent;   
    47.       myFile.println(strContent);   
    48.       resultFile.close();   
    49.   
    50.     }   
    51.     catch (Exception e) {   
    52.       System.out.println("新建目錄操作出錯");   
    53.       e.printStackTrace();   
    54.   
    55.     }   
    56.   
    57.   }   
    58.   
    59.   /**  
    60.    * 刪除文件  
    61.    * @param filePathAndName String 文件路徑及名稱 如c:/fqf.txt  
    62.    * @param fileContent String  
    63.    * @return boolean  
    64.    */  
    65.   public void delFile(String filePathAndName) {   
    66.     try {   
    67.       String filePath = filePathAndName;   
    68.       filePath = filePath.toString();   
    69.       java.io.File myDelFile = new java.io.File(filePath);   
    70.       myDelFile.delete();   
    71.   
    72.     }   
    73.     catch (Exception e) {   
    74.       System.out.println("刪除文件操作出錯");   
    75.       e.printStackTrace();   
    76.   
    77.     }   
    78.   
    79.   }   
    80.   
    81.   /**  
    82.    * 刪除文件夾  
    83.    * @param filePathAndName String 文件夾路徑及名稱 如c:/fqf  
    84.    * @param fileContent String  
    85.    * @return boolean  
    86.    */  
    87.   public void delFolder(String folderPath) {   
    88.     try {   
    89.       delAllFile(folderPath); //刪除完里面所有內容   
    90.       String filePath = folderPath;   
    91.       filePath = filePath.toString();   
    92.       java.io.File myFilePath = new java.io.File(filePath);   
    93.       myFilePath.delete(); //刪除空文件夾   
    94.   
    95.     }   
    96.     catch (Exception e) {   
    97.       System.out.println("刪除文件夾操作出錯");   
    98.       e.printStackTrace();   
    99.   
    100.     }   
    101.   
    102.   }   
    103.   
    104.   /**  
    105.    * 刪除文件夾里面的所有文件  
    106.    * @param path String 文件夾路徑 如 c:/fqf  
    107.    */  
    108.   public void delAllFile(String path) {   
    109.     File file = new File(path);   
    110.     if (!file.exists()) {   
    111.       return;   
    112.     }   
    113.     if (!file.isDirectory()) {   
    114.       return;   
    115.     }   
    116.     String[] tempList = file.list();   
    117.     File temp = null;   
    118.     for (int i = 0; i < tempList.length; i++) {   
    119.       if (path.endsWith(File.separator)) {   
    120.         temp = new File(path + tempList[i]);   
    121.       }   
    122.       else {   
    123.         temp = new File(path + File.separator + tempList[i]);   
    124.       }   
    125.       if (temp.isFile()) {   
    126.         temp.delete();   
    127.       }   
    128.       if (temp.isDirectory()) {   
    129.         delAllFile(path+"/"+ tempList[i]);//先刪除文件夾里面的文件   
    130.         delFolder(path+"/"+ tempList[i]);//再刪除空文件夾   
    131.       }   
    132.     }   
    133.   }   
    134.   
    135.   /**  
    136.    * 復制單個文件  
    137.    * @param oldPath String 原文件路徑 如:c:/fqf.txt  
    138.    * @param newPath String 復制后路徑 如:f:/fqf.txt  
    139.    * @return boolean  
    140.    */  
    141.   public void copyFile(String oldPath, String newPath) {   
    142.     try {   
    143.       int bytesum = 0;   
    144.       int byteread = 0;   
    145.       File oldfile = new File(oldPath);   
    146.       if (oldfile.exists()) { //文件存在時   
    147.         InputStream inStream = new FileInputStream(oldPath); //讀入原文件   
    148.         FileOutputStream fs = new FileOutputStream(newPath);   
    149.         byte[] buffer = new byte[1444];   
    150.         int length;   
    151.         while ( (byteread = inStream.read(buffer)) != -1) {   
    152.           bytesum += byteread; //字節數 文件大小   
    153.           System.out.println(bytesum);   
    154.           fs.write(buffer, 0, byteread);   
    155.         }   
    156.         inStream.close();   
    157.       }   
    158.     }   
    159.     catch (Exception e) {   
    160.       System.out.println("復制單個文件操作出錯");   
    161.       e.printStackTrace();   
    162.   
    163.     }   
    164.   
    165.   }   
    166.   
    167.   /**  
    168.    * 復制整個文件夾內容  
    169.    * @param oldPath String 原文件路徑 如:c:/fqf  
    170.    * @param newPath String 復制后路徑 如:f:/fqf/ff  
    171.    * @return boolean  
    172.    */  
    173.   public void copyFolder(String oldPath, String newPath) {   
    174.   
    175.     try {   
    176.       (new File(newPath)).mkdirs(); //如果文件夾不存在 則建立新文件夾   
    177.       File a=new File(oldPath);   
    178.       String[] file=a.list();   
    179.       File temp=null;   
    180.       for (int i = 0; i < file.length; i++) {   
    181.         if(oldPath.endsWith(File.separator)){   
    182.           temp=new File(oldPath+file[i]);   
    183.         }   
    184.         else{   
    185.           temp=new File(oldPath+File.separator+file[i]);   
    186.         }   
    187.   
    188.         if(temp.isFile()){   
    189.           FileInputStream input = new FileInputStream(temp);   
    190.           FileOutputStream output = new FileOutputStream(newPath + "/" +   
    191.               (temp.getName()).toString());   
    192.           byte[] b = new byte[1024 * 5];   
    193.           int len;   
    194.           while ( (len = input.read(b)) != -1) {   
    195.             output.write(b, 0, len);   
    196.           }   
    197.           output.flush();   
    198.           output.close();   
    199.           input.close();   
    200.         }   
    201.         if(temp.isDirectory()){//如果是子文件夾   
    202.           copyFolder(oldPath+"/"+file[i],newPath+"/"+file[i]);   
    203.         }   
    204.       }   
    205.     }   
    206.     catch (Exception e) {   
    207.       System.out.println("復制整個文件夾內容操作出錯");   
    208.       e.printStackTrace();   
    209.   
    210.     }   
    211.   
    212.   }   
    213.   
    214.   /**  
    215.    * 移動文件到指定目錄  
    216.    * @param oldPath String 如:c:/fqf.txt  
    217.    * @param newPath String 如:d:/fqf.txt  
    218.    */  
    219.   public void moveFile(String oldPath, String newPath) {   
    220.     copyFile(oldPath, newPath);   
    221.     delFile(oldPath);   
    222.   
    223.   }   
    224.   
    225.   /**  
    226.    * 移動文件到指定目錄  
    227.    * @param oldPath String 如:c:/fqf.txt  
    228.    * @param newPath String 如:d:/fqf.txt  
    229.    */  
    230.   public void moveFolder(String oldPath, String newPath) {   
    231.     copyFolder(oldPath, newPath);   
    232.     delFolder(oldPath);   
    233.   
    234.   }   
    235. }  
    posted @ 2009-05-07 11:16 lanxin1020 閱讀(405) | 評論 (0)編輯 收藏

            如果在Java程序中你使用Java Native Interface(JNI) 來調用某個特定平臺下的本地庫文件,你就會發現這個過程很單調、乏味。Jeff Friesen一直在介紹一個知名度很低的Java開源項目:Java Native Access---它能夠避免因使用JNI導致的錯誤和乏味,同時它還能讓你通過編程的方式調用C語言庫。

            在Java語言沒有提供必要的APIs的情況下,Java程序使用Java Native Interface (JNI)來調用特定平臺下的本地庫是必要的。例如:在Windows XP平臺中,我使用過JNI來調用通用串行總線和基于TWAIN的掃描儀器的庫;在更古老的Windows NT平臺中,調用過智能卡的庫。

            我按照一個基本的、乏味的流程來解決這些問題:首先,我創建一個Java類用來載入JNI-friendly庫(這個庫能過訪問其他的庫)并且聲明這個類的本地方法。然后,在使用JDK中的javah工具為JNI-friendly庫中的函數---函數和這個類中的本地方法一一對應---創建一個代理。最后,我使用C語言寫了一個庫并用C編譯器編譯了這些代碼。

            盡管完成這些流程并不是很困難,但是寫C代碼是一個很緩慢的過程---例如: C語言中的字符串處理是通過指針來實現的,這會很復雜的。而且,使用JNI很容易出現錯誤,導致內存泄漏、很難找到程序崩潰的原因。

            在Java開源系列的第二篇文章中,我要介紹一個更簡單、更安全的解決方法:Todd Fast and Timothy Wall的Java Native Access (JNA) 項目。JNA能夠讓你在Java程序中調用本地方法時避免使用C和Java Native Interface。在這篇文章中,讓我以簡要的介紹        JNA和運行示例必需的軟件來開始下面的內容。然后,向你展示如何使用JNA將3個Windows本地庫中的有用代碼移植到Java程序中。

    Get started with JNA(JNA入門)

    Java Native Access 項目 在Java.net上,你可以到這個網站上現在這個項目的代碼和在線幫助文檔。雖然在下載有5個相關的jar文件,在本文中你僅僅需要下載其中的jna.jar和example.jar。

    Jna.jar提供基本的、運行這些示例文件必需的jna運行環境。這個jna.jar文件除了有Unix、Linux、Windows和Mac OS X平臺相關的JNT-friendly本地庫外,還包含其他幾個類包。每一個本地庫都是用來訪問相對應平臺下的本地方法的。

            example.jar包含了不同的示例來表明JNA的用途。其中的一個例子是使用JNA來實現一個在不同平臺下的透明視窗技術的API。在文章最后的示例中將要展示如何使用這個API修復上個月的文章關于VerifyAge2應用中辨認透明效果的問題。

    獲取本地時間(Get local time)

    如果你在Java Native Access 首頁 看過“JNA如何入門”,你就會知道一個很簡單的關于調用Windows 平臺下的API函數:GetSystemTime() 的JNA示例。這個不完整的例子只是展示了JNA的基本特點。(在例子的基礎上,我做了一個更完整的基于Windows的例子來介紹JNA)我在Windows平臺下完善了這個例子來介紹JNA。

    第一例子基于Windows GetLocalTime() API函數返回本地當前的時間和日期。和GetSystemTime()不同的是,返回的時間/日期是協調通用時間(UTC)格式的,GetLocalTime()返回的時間/日期信息的格式是根據當前時區來表示。

    在一個Java程序中使用JNA調用GetLocalTime,你需要知道這個函數所在的Windows平臺下的動態鏈接庫(DLL)的名稱(和可能所在的地理區域)。我們發現GetLocalTime()和GetSystemTime在同一個DLL文件中:kernel32.dll。你還需要知道GetLocalTime()在C語言環境中的申明。申明如下Listing 1:

    Listing 1. GetLocalTime在C語言中的申明

    typedef struct
    {
       WORD wYear;
       WORD wMonth;
       WORD wDayOfWeek;
       WORD wDay;
       WORD wHour;
       WORD wMinute;
       WORD wSecond;
       WORD wMilliseconds;
    }
    SYSTEMTIME, *LPSYSTEMTIME;

    VOID GetLocalTime(LPSYSTEMTIME lpst);


    這個基于C語言的申明表明傳到這個函數的參數數目和類型。在這個例子中,只有一個參數---一個指向Windows SYSTEMTIME結構體的指針。而且,每個結構體成員的類型是16bit長度的無符號整型。根據這些信息,你能夠創建一個完全描述GetLocalTime()函數的接口,如Listing 2中所示:

    Listing 2. Kernel32.java

    // Kernel32.java

    import com.sun.jna.*;
    import com.sun.jna.win32.*;

    public interface Kernel32 extends StdCallLibrary
    {
       public static class SYSTEMTIME extends Structure
       {
          public short wYear;
          public short wMonth;
          public short wDayOfWeek;
          public short wDay;
          public short wHour;
          public short wMinute;
          public short wSecond;
          public short wMilliseconds;
       }

       void GetLocalTime (SYSTEMTIME result);
    }


    Kernel32 接口(The Kernel32 interface)

    因為JNA使用通過一個接口來訪問某個庫中的函數,Listing 2表示了一個描述GetLocalTime()的接口。根據約定,我把接口命名為Kernel32是因為GetLocalTime()在Windows的kernel32.dll庫。

    這個接口必須繼承com.sun..jna.Library接口。因為Windows API函數遵循stdcall調用協議(stdcall calling convention),為Windows API申明的接口也必須繼承com.sun.jna.win32. StdCallLibrary接口。因此這個接口共繼承了Library 和 com.sun.jna.win32.StdCall兩個接口。

    在前面,你已經知道了GetLocalTime() 需要一個指向SYSTEMTIME結構體的指針作為它唯一的參數。因為Java不支持指針,JNA是通過申明一個com.sun.jna.Structure的子類來代替的。根據java文檔中抽象類的概念,在參數環境中,Structure相當于C語言的struct*。

    在SYSTEMTIME類中的字段和C結構體中的相對應的屬性字段的順序是一一對應的。保證字段順序的一致性是非常重要的。例如,我發現交換wYear和wMonth會導致wYear和wMonth值互換。

    每個字段在java中是short integer類型的。按照JNA首頁上 “默認類型映射”章節給出的提示,這個short integer分配類型是正確。然而,我們應該知道一個重要的區別:Windows平臺下的WORD類型等同于C語言環境中的16-bit的無符號的short integer,而java中short integer是16-bit有符號的short integer。

    一個類型映射的問題

    通過比較一個API 函數返回的整型值,你會發現Windows/C 語言的無符號整型和Java語言的有符號整型的JNA類型映射是有問題的。在比較的過程中,如果你不細心,那么錯誤的執行過程可能導致決定性情況。導致這種后果是因為忘記任何數值的符號位的確定是根據:在無符號整型的情況下會被解釋為正號,而在有符號整型的進制中被理解為負號的。

    通過Kernel32獲取本地時間(Access the local time with Kernel32)

    JNA首頁上的GetSystemTime()示例已經表明必須使用預先申明的接口為本地庫分配一個實例對象。你可以通過com.sun.jna.Native類中靜態公用方法loadLibrary(String name, Class interfaceClass)來完成上述的目標。Listing 3 所示:

    Listing 3. LocalTime.java

    // LocalTime.java

    import com.sun.jna.*;

    public class LocalTime
    {
       public static void main (String [] args)
       {
          Kernel32 lib = (Kernel32) Native.loadLibrary ("kernel32",
                                                        Kernel32.class);
          Kernel32.SYSTEMTIME time = new Kernel32.SYSTEMTIME ();
          lib.GetLocalTime (time);
          System.out.println ("Year is "+time.wYear);
          System.out.println ("Month is "+time.wMonth);
          System.out.println ("Day of Week is "+time.wDayOfWeek);
          System.out.println ("Day is "+time.wDay);
          System.out.println ("Hour is "+time.wHour);
          System.out.println ("Minute is "+time.wMinute);
          System.out.println ("Second is "+time.wSecond);
          System.out.println ("Milliseconds are "+time.wMilliseconds);
       }
    }


    Listing 3 執行Kernel32 lib = (Kernel32) Native.loadLibrary ("kernel32", Kernel32.class);來分配一個Kernel32實例對象并且裝載kernel32.dll。因為kernel32.dll是Windows平臺下標準的dll文件,所以不要指定訪問這個庫的路徑。然而,如果找不到這個dll文件,loadLibrary()會拋出一個UnsatisfiedLinkError異常。

    Kernel32.SYSTEMTIME time = new Kernel32.SYSTEMTIME ();創建了一個SYSTEMTIME結構體的示例。初始化后下面是lib.GetLocalTime (time);,這句話使用本地的時間/日期來給這個實例賦值。幾個System.out.println()語句是輸出這些值。

    編譯和運行這個應用(Compile and run the application)

    這部分很容易。假設jna.jar、Kernel32.java和LocalTime.java是放在當前文件夾中,調用java –cp jna.jar;. LocalTime.java來編譯這個應用的源代碼。如果在Windows平臺下,調用invoke java –cp jna.jar;. LocalTime 來運行這個應用。你可以得到類似與Listing 4的輸出結果:

    Listing 4. 從LocalTime.java生成的輸出

    Year is 2007
    Month is 12
    Day of Week is 3
    Day is 19
    Hour is 12
    Minute is 35
    Second is 13
    Milliseconds are 156


    獲取操縱桿信息(Accessing joystick device info)

    上面的例子已經介紹了JNA,但是這個獲取本地時間和日期的例子并沒有很好的利用這個技術,甚至也沒有體現JNI的價值。Java語言中的System.currentTimeMillis()函數已經以毫秒的格式返回了這些信息。因為Java語言沒有為游戲控制器提供API,所以獲取操縱桿的信息更適合JNA的使用。

    例如,你要構建一個平臺無關的Java庫,而且這些庫使用JNA調用Linux, Mac OS X, Windwos和Unix平臺中本地的操縱桿API。為了簡潔和方便起見,這個例子僅僅是調用Windows平臺下的操縱桿API。而且我將重點介紹這個API很小的一部分。

    類似GetLocalTime(),第一步是辨別出操作桿API的DLL,這個DLL是winmm.dll,和kernel32.dll在同一個文件夾中,它包含了操作桿的API和其他的多媒體APIs。還需知道要被使用的操作桿函數基于C語言的聲明。這些函數聲明已經在Listing 5中列出來了。

    Listing 5. C-based declarations for some Joystick API functions

    #define MAXPNAMELEN 32

    typedef struct
    {
       WORD  wMid;                  // manufacturer identifier
       WORD  wPid;                  // product identifier
       TCHAR szPname  MAXPNAMELEN ; // product name
       UINT  wXmin;                 // minimum x position
       UINT  wXmax;                 // maximum x position
       UINT  wYmin;                 // minimum y position
       UINT  wYmax;                 // maximum y position
       UINT  wZmin;                 // minimum z position
       UINT  wZmax;                 // maximum z position
       UINT  wNumButtons;           // number of buttons
       UINT  wPeriodMin;            // smallest supported polling interval when captured
       UINT  wPeriodMax;            // largest supported polling interval when captured
    }
    JOYCAPS, *LPJOYCAPS;

    MMRESULT joyGetDevCaps(UINT IDDevice, LPJOYCAPS lpjc, UINT cbjc);

    UINT joyGetNumDevs(VOID);


    操作桿API的函數(Functions of the Joystick API)

    在Windows平臺下是通過以joy作為函數名開始的函數以及被各種函數調用的結構體來實現操作桿API的。例如,joyGetNumDevs()返回的是這個平臺下支持的操作桿設備最多的數目;joyGetDevCaps()返回的是每個連接上的操縱桿的質量。

    joyGetDevCaps()函數需要3個參數:
    * 處在0到joyGetNumDevs()-1之間的操作桿ID
    * 保存返回的質量信息的JOYCAPS結構體的地址
    * JOYCAPS結構體的字節大小
    雖然它的結果不同,這個函數返回的是一個32位的無符號整型結果,而且0表示一個已經連接的操縱桿。

    JOYCAPS結構體有3種類型。Windows平臺下的WORD(16位無符號短整型)類型對應的是Java語言中16位有符號短整型。除此之外,Windows下的UINT(32位無符號整型)類型是和Java語言中32位有符號整型相對應的。而Windows平臺上的text character就是TCHAR類型。

    微軟通過TCHAR類型使開發人員能夠從ASCII類型的函數參數平滑的轉移到Unicode字符類型的函數參數上。而且,擁有text類型參數的函數的實現是通過宏轉變為對應的ASCII或者wide-character的函數。例如,joyGetDevCaps()是一個對應joyGetDevCapsA() 和 joyGetDevCapsW()的宏。

    使用TCHAR(Working with TCHAR)

    使用TCHAR和將TCHAR轉變的宏會導致基于C語言的申明向基于JNA接口的轉換
    變得有點復雜—你在使用ASCII或者wide-character版本的操縱桿函數嗎?兩種版本都在如下的接口中展示了:

    Listing 6. WinMM.java

    // WinMM.java

    import com.sun.jna.*;
    import com.sun.jna.win32.*;

    public interface WinMM extends StdCallLibrary
    {
       final static int JOYCAPSA_SIZE = 72;

       public static class JOYCAPSA extends Structure
       {
          public short wMid;
          public short wPid;
          public byte szPname [] = new byte [32];
          public int wXmin;
          public int wXmax;
          public int wYmin;
          public int wYmax;
          public int wZmin;
          public int wZmax;
          public int wNumButtons;
          public int wPeriodMin;
          public int wPeriodMax;
       }

       int joyGetDevCapsA (int id, JOYCAPSA caps, int size);

       final static int JOYCAPSW_SIZE = 104;

       public static class JOYCAPSW extends Structure
       {
          public short wMid;
          public short wPid;
          public char szPname [] = new char [32];
          public int wXmin;
          public int wXmax;
          public int wYmin;
          public int wYmax;
          public int wZmin;
          public int wZmax;
          public int wNumButtons;
          public int wPeriodMin;
          public int wPeriodMax;
       }

       int joyGetDevCapsW (int id, JOYCAPSW caps, int size);

       int joyGetNumDevs ();
    }


    Listing 6沒有介紹JNA的新特性。實際上,JNA強調了對本地庫的接口命名規則。同時,還展示了如何將TCHAR映射到Java語言中的byte和char數組。最后,它揭示了以常量方式聲明的結構體的大小。Listing 7展示了當調用joyGetDevCapsA() 和 joyGetDevCapsW()時如何使用這些常量。

    Listing 7. JoystickInfo.java

    // JoystickInfo.java

    import com.sun.jna.*;

    public class JoystickInfo
    {
       public static void main (String [] args)
       {
          WinMM lib = (WinMM) Native.loadLibrary ("winmm", WinMM.class);
          int numDev = lib.joyGetNumDevs ();

          System.out.println ("joyGetDevCapsA() Demo");
          System.out.println ("---------------------\n");

          WinMM.JOYCAPSA caps1 = new WinMM.JOYCAPSA ();
          for (int i = 0; i < numDev; i++)
               if (lib.joyGetDevCapsA (i, caps1, WinMM.JOYCAPSA_SIZE) == 0)
               {
                   String pname = new String (caps1.szPname);
                   pname = pname.substring (0, pname.indexOf ('\0'));
                   System.out.println ("Device #"+i);
                   System.out.println ("  wMid = "+caps1.wMid);
                   System.out.println ("  wPid = "+caps1.wPid);
                   System.out.println ("  szPname = "+pname);
                   System.out.println ("  wXmin = "+caps1.wXmin);
                   System.out.println ("  wXmax = "+caps1.wXmax);
                   System.out.println ("  wYmin = "+caps1.wYmin);
                   System.out.println ("  wYmax = "+caps1.wYmax);
                   System.out.println ("  wZmin = "+caps1.wZmin);
                   System.out.println ("  wZmax = "+caps1.wZmax);
                   System.out.println ("  wNumButtons = "+caps1.wNumButtons);
                   System.out.println ("  wPeriodMin = "+caps1.wPeriodMin);
                   System.out.println ("  wPeriodMax = "+caps1.wPeriodMax);
                   System.out.println ();
               }

          System.out.println ("joyGetDevCapsW() Demo");
          System.out.println ("---------------------\n");

          WinMM.JOYCAPSW caps2 = new WinMM.JOYCAPSW ();
          for (int i = 0; i < numDev; i++)
               if (lib.joyGetDevCapsW (i, caps2, WinMM.JOYCAPSW_SIZE) == 0)
               {
                   String pname = new String (caps2.szPname);
                   pname = pname.substring (0, pname.indexOf ('\0'));
                   System.out.println ("Device #"+i);
                   System.out.println ("  wMid = "+caps2.wMid);
                   System.out.println ("  wPid = "+caps2.wPid);
                   System.out.println ("  szPname = "+pname);
                   System.out.println ("  wXmin = "+caps2.wXmin);
                   System.out.println ("  wXmax = "+caps2.wXmax);
                   System.out.println ("  wYmin = "+caps2.wYmin);
                   System.out.println ("  wYmax = "+caps2.wYmax);
                   System.out.println ("  wZmin = "+caps2.wZmin);
                   System.out.println ("  wZmax = "+caps2.wZmax);
                   System.out.println ("  wNumButtons = "+caps2.wNumButtons);
                   System.out.println ("  wPeriodMin = "+caps2.wPeriodMin);
                   System.out.println ("  wPeriodMax = "+caps2.wPeriodMax);
                   System.out.println ();
               }
       }
    }

    盡管和LocalTime這個示例類似,JoystickInfo執行WinMM lib = (WinMM) Native.loadLibrary ("winmm", WinMM.class);這句話來獲取一個WinMM的實例,并且載入winmm.dll。它還執行WinMM.JOYCAPSA caps1 = new WinMM.JOYCAPSA (); 和 WinMM.JOYCAPSW caps2 = new WinMM.JOYCAPSW ();初始化必需的結構體實例。

    編譯和運行這個程序(Compile and run the application)

    假如jna.jar,WinMM.java和JoystickInfo.java在同一個文件夾中,調用 javac -cp jna.jar;. JoystickInfo.java 來編譯這個應用的源代碼。
    在windows平臺下,調用java -cp jna.jar;. JoystickInfo就可以運行這個應用程序了。如果沒有操縱桿設備,你應該得到Listing 8中的輸出。

    將C語言中的string類型轉換為Java語言的String類型

    pname = pname.substring (0, pname.indexOf ('\0')); 這段代碼將一個C string 轉換成了Java string. 如果不使用這個轉換,C語言的string結束符’\0’和string后面的無用字符都會成為Java語言中String實例對象的內容。

    Listing 8. 輸出操縱桿信息(Output of JoystickInfo)

    joyGetDevCapsA() Demo
    ---------------------

    joyGetDevCapsW() Demo
    ---------------------


    上面的輸出是因為每次調用joyGetDevCap()返回的是一個非空值,這表示沒有操縱桿/游戲控制器設備或者是出現錯誤。為了獲取更多有意思的輸出,將一個設備連接到你的平臺上并且再次運行JoystickInfo。如下,將一個微軟SideWinder即插即用游戲觸摸板設備聯上之后我獲取了如下的輸出:

    Listing 9. 操縱桿連接上之后的運行結果(Output after running JoystickInfo with a joystick attached)

    joyGetDevCapsA() Demo
    ---------------------

    Device #0
      wMid = 1118
      wPid = 39
      szPname = Microsoft PC-joystick driver
      wXmin = 0
      wXmax = 65535
      wYmin = 0
      wYmax = 65535
      wZmin = 0
      wZmax = 65535
      wNumButtons = 6
      wPeriodMin = 10
      wPeriodMax = 1000

    joyGetDevCapsW() Demo
    ---------------------

    Device #0
      wMid = 1118
      wPid = 39
      szPname = Microsoft PC-joystick driver
      wXmin = 0
      wXmax = 65535
      wYmin = 0
      wYmax = 65535
      wZmin = 0
      wZmax = 65535
      wNumButtons = 6
      wPeriodMin = 10
      wPeriodMax = 1000


    窗口透明度(Transparent windows)

    在這系列文章中上篇文章是關于Bernhard Pauler's 氣泡提示(balloontip)工程的。我構建了一個叫做VerifyAge的、包含有一個氣泡提示的GUI應用。Figure 1中顯示了這個GUI應用的一個小問題:這個氣泡提示的沒有經過修飾的對話框部分遮住了應用窗口的一部分邊框,導致了無法點擊這個邊框的最小化和最大化按鈕,并且使整個GUI很難看.
    image
    盡管未修飾部分的對話框不能顯示氣泡提示的透明度,java語言不支持窗口透明度。幸運的是,我們可以通過使用com.sun.jna.examples.WindowUtils類調用JNA的examples.jar文件來解決這個問題。
    WindowUtils提供在Unix,Linux,Mac OS X和Windows平臺上使用JNA’s來實現窗口透明的工具方法。例如, public static void setWindowMask(final Window w, Icon mask) 讓你根據像素而不是通過預定的掩罩(mask)參數來選取某部分的窗口。這個功能將在Listing 10中展示:

    Listing 10. Using JNA to render a window transparent


    // Create a mask for this dialog. This mask has the same shape as the
    // dialog's rounded balloon tip and ensures that only the balloon tip
    // part of the dialog will be visible. All other dialog pixels will
    // disappear because they correspond to transparent mask pixels.

    // Note: The drawing code is based on the drawing code in
    // RoundedBalloonBorder.

    Rectangle bounds = getBounds ();
    BufferedImage bi = new BufferedImage (bounds.width, bounds.height,
                                          BufferedImage.TYPE_INT_ARGB);
    Graphics g = bi.createGraphics ();
    g.fillRoundRect (0, 0, bounds.width, bounds.height-VERT_OFFSET,
                     ARC_WIDTH*2, ARC_HEIGHT*2);
    g.drawRoundRect (0, 0, bounds.width-1, bounds.height-VERT_OFFSET-1,
                     ARC_WIDTH*2, ARC_HEIGHT*2);
    int [] xPoints = { HORZ_OFFSET, HORZ_OFFSET+VERT_OFFSET, HORZ_OFFSET };
    int [] yPoints = { bounds.height-VERT_OFFSET-1, bounds.height-VERT_OFFSET
                       -1, bounds.height-1 };
    g.fillPolygon (xPoints, yPoints, 3);
    g.drawLine (xPoints [0], yPoints [0], xPoints [2], yPoints [2]);
    g.drawLine (xPoints [1], yPoints [1], xPoints [2], yPoints [2]);
    g.dispose ();
    WindowUtils.setWindowMask (this, new ImageIcon (bi));


    在Listing 10中的代碼段是從本文代碼文檔(code archive)里的加強版的VerifyAge2 應用中的TipFrame的構造函數結尾部分摘錄的。這個構造函數定義了圍繞提示氣泡的掩罩(mask)的形狀,在這個形狀范圍里描繪不透明的像素。
    假如你當前文件夾中有examples.jar, jna.jar, 和 VerifyAge2.java,調用 javac -cp examples.jar;balloontip.jar VerifyAge2.java 來編譯源文件.然后調用java -Dsun.java2d.noddraw=true -cp examples.jar;balloontip.jar;. VerifyAge2運行這個應用. Figure 2 展示了透明示例.
    image

    總結(In conclusion)

    JNA項目有很長的歷史了(追溯到1999年),但是它第一次發布是在2006年11月。從此以后它慢慢的被需要將本地C代碼整合到Java工程中的開發者注意到了。因為JNA能夠用來解決JuRuby中常見一個問題:缺乏對POSIX調用的支持(lack of support for POSIX calls),它也在JRuby程序員中掀起些波浪。JNA也同樣被作為實現用低級C代碼繼承Ruby的一種解決方案(extending Ruby with low-level C code)。
    我喜歡使用JNA來工作,相信你也會發現它比使用JNI來訪問本地代碼更簡單、更安全。無需多言,JNA還有更多的特性在本文中沒有體現出來。查閱它的資源部分:獲取這個開源java項目更多的信息(learn more about this open source Java project)。用它做demo,而且在論壇(discussion forum )上共享你的經驗。 下一個月我會帶著另一個開源項目回來的,這個開源項目會給你每天的java開發帶來益處。

    附錄:WindowUtils.setWindowMask()的替代品

    在剛剛寫完這篇文章后,我發現java語言支持在6u10版本中支持窗口的透明和形狀定制。讀完Kirill Grouchnikov的博客后,我用WindowUtils.setWindowMask()的替代品修改了VerifyAge2,如下:
    // Create and install a balloon tip shape to ensure that only this part
    // of the dialog will be visible.

    Rectangle bounds = getBounds ();
    GeneralPath gp;
    gp = new GeneralPath (new RoundRectangle2D.Double (bounds.x, bounds.y,
                                                       bounds.width,
                                                       bounds.height-
                                                       VERT_OFFSET,
                                                       ARC_WIDTH*2-1,
                                                       ARC_HEIGHT*2-1));
    gp.moveTo (HORZ_OFFSET, bounds.height-VERT_OFFSET);
    gp.lineTo (HORZ_OFFSET, bounds.height);
    gp.lineTo (HORZ_OFFSET+VERT_OFFSET+1, bounds.height-VERT_OFFSET);
    AWTUtilities.setWindowShape (this, gp);


    這段代碼使用新類AWTUtilities(在com.sun.awt包中),而且public void setWindowShape(Window w, Shape s)函數將TipFrame和JDialog窗口設置氣泡形狀。
    posted @ 2009-05-07 11:08 lanxin1020 閱讀(817) | 評論 (0)編輯 收藏
    Java代碼
    1. 簡單介紹及應用如下:    
    2.   一、JAVA中所需要做的工作    
    3.   在JAVA程序中,首先需要在類中聲明所調用的庫名稱,如下:    
    4.   
    5. static {    
    6. System.loadLibrary(“goodluck”);    
    7. }   
    8.   
    9.   
    10.   在這里,庫的擴展名字可以不用寫出來,究竟是DLL還是SO,由系統自己判斷。    
    11.   
    12.   還需對將要調用的方法做本地聲明,關鍵字為native。且只需要聲明,而不需要具體實現。如下:    
    13.   
    14. public native static void set(int i);    
    15. public native static int get();   
    16.   
    17.   
    18.   然后編譯該JAVA程序文件,生成CLASS,再用JAVAH命令,JNI就會生成C/C++的頭文件。    
    19.   
    20.   例如程序testdll.java,內容為:    
    21.   
    22. public class testdll    
    23. {    
    24. static    
    25. {    
    26. System.loadLibrary("goodluck");    
    27. }    
    28. public native static int get();    
    29. public native static void set(int i);    
    30. public static void main(String[] args)    
    31. {    
    32. testdll test = new testdll();    
    33. test.set(10);    
    34. System.out.println(test.get());    
    35. }    
    36. }   
    37.   
    38.   
    39.   用javac testdll.java編譯它,會生成testdll.class。    
    40.   再用javah testdll,則會在當前目錄下生成testdll.h文件,這個文件需要被C/C++程序調用來生成所需的庫文件。    
    41.   
    42.   二、C/C++中所需要做的工作    
    43.   對于已生成的.h頭文件,C/C++所需要做的,就是把它的各個方法具體的實現。然后編譯連接成庫文件即可。再把庫文件拷貝到JAVA程序的路徑下面,就可以用JAVA調用C/C++所實現的功能了。    
    44.   接上例子。我們先看一下testdll.h文件的內容:    
    45.   
    46. /* DO NOT EDIT THIS FILE - it is machine generated */    
    47. #include    
    48. /* Header for class testdll */    
    49. #ifndef _Included_testdll    
    50. #define _Included_testdll    
    51. #ifdef __cplusplus    
    52. extern "C" {    
    53. #endif    
    54. /*   
    55. * Class: testdll   
    56. * Method: get   
    57. * Signature: ()I   
    58. */    
    59. JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass);    
    60. /*   
    61. * Class: testdll   
    62. * Method: set   
    63. * Signature: (I)V   
    64. */    
    65. JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint);    
    66. #ifdef __cplusplus    
    67. }    
    68. #endif    
    69. #endif   
    70.   
    71.   
    72.   在具體實現的時候,我們只關心兩個函數原型    
    73.   JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass); 和    
    74.   JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint);   
    75.   
    76.   這里JNIEXPORT和JNICALL都是JNI的關鍵字,表示此函數是要被JNI調用的。而jint是以JNI為中介使JAVA的int類型與本 地的int溝通的一種類型,我們可以視而不見,就當做int使用。函數的名稱是JAVA_再加上java程序的package路徑再加函數名組成的。參數 中,我們也只需要關心在JAVA程序中存在的參數,至于JNIEnv*和jclass我們一般沒有必要去碰它。    
    77.   
    78.   好,下面我們用testdll.cpp文件具體實現這兩個函數:    
    79.   
    80. #include "testdll.h"    
    81. int i = 0;    
    82. JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass)    
    83. {    
    84. return i;    
    85. }    
    86. JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint j)    
    87. {    
    88. i = j;    
    89. }   
    90.   
    91.   
    92.   編譯連接成庫文件,本例是在WINDOWS下做的,生成的是DLL文件。并且名稱要與JAVA中需要調用的一致,這里就是goodluck.dll 。把goodluck.dll拷貝到testdll.class的目錄下,java testdll運行它,就可以觀察到結果了。    
    93.   我的項目比較復雜,需要調用動態鏈接庫,這樣在JNI傳送參數到C程序時,需要對參數進行處理轉換。才可以被C程序識別。   
    94.   大體程序如下:   
    95.   
    96. public class SendSMS {    
    97. static    
    98. {    
    99.   
    100.   
    101.     
    102. System.out.println(System.getProperty("java.library.path"));    
    103. System.loadLibrary("sms");    
    104. }    
    105. public native static int SmsInit();    
    106. public native static int SmsSend(byte[] mobileNo, byte[] smContent);    
    107. }    
    108.   
    109.   在這里要注意的是,path里一定要包含類庫的路徑,否則在程序運行時會拋出異常:   
    110.   java.lang.UnsatisfiedLinkError: no sms in java.library.path   
    111.   at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1491)   
    112.   at java.lang.Runtime.loadLibrary0(Runtime.java:788)   
    113.   at java.lang.System.loadLibrary(System.java:834)   
    114.   at com.mobilesoft.sms.mobilesoftinfo.SendSMS.(SendSMS.java:14)   
    115.   at com.mobilesoft.sms.mobilesoftinfo.test.main(test.java:18)   
    116.   Exception in thread "main"  
    117.   
    118.   指引的路徑應該到.dll文件的上一級,如果指到.dll,則會報:   
    119.   java.lang.UnsatisfiedLinkError: C:\sms.dll: Can't find dependent libraries   
    120.   at java.lang.ClassLoader$NativeLibrary.load(Native Method)   
    121.   at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1560)   
    122.   at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1485)   
    123.   at java.lang.Runtime.loadLibrary0(Runtime.java:788)   
    124.   at java.lang.System.loadLibrary(System.java:834)   
    125.   at com.mobilesoft.sms.mobilesoftinfo.test.main(test.java:18)   
    126.   Exception in thread "main"  
    127.   
    128.   通過編譯,生成com_mobilesoft_sms_mobilesoftinfo_SendSMS.h頭文件。(建議使用Jbuilder進行編 譯,操作比較簡單!)這個頭文件就是Java和C之間的紐帶。要特別注意的是方法中傳遞的參數jbyteArray,這在接下來的過程中會重點介紹。   
    129.   
    130. /* DO NOT EDIT THIS FILE - it is machine generated */    
    131. #include    
    132. /* Header for class com_mobilesoft_sms_mobilesoftinfo_SendSMS */    
    133. #ifndef _Included_com_mobilesoft_sms_mobilesoftinfo_SendSMS    
    134. #define _Included_com_mobilesoft_sms_mobilesoftinfo_SendSMS    
    135. #ifdef __cplusplus    
    136. extern "C" {    
    137. #endif    
    138. /*   
    139. * Class: com_mobilesoft_sms_mobilesoftinfo_SendSMS   
    140. * Method: SmsInit   
    141. * Signature: ()I   
    142. */    
    143. JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsInit    
    144. (JNIEnv *, jclass);    
    145. /*   
    146. * Class: com_mobilesoft_sms_mobilesoftinfo_SendSMS   
    147. * Method: SmsSend   
    148. * Signature: ([B[B)I   
    149. */    
    150. JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsSend    
    151. (JNIEnv *, jclass, jbyteArray, jbyteArray);    
    152. #ifdef __cplusplus    
    153. }    
    154. #endif    
    155. #endif   
    156.   
    157.   
    158.   
    159.   對于我要調用的C程序的動態鏈接庫,C程序也要提供一個頭文件,sms.h。這個文件將要調用的方法羅列了出來。   
    160.   
    161. /*   
    162. * SMS API   
    163. * Author: yippit   
    164. * Date: 2004.6.8   
    165. */    
    166. #ifndef MCS_SMS_H    
    167. #define MCS_SMS_H    
    168. #define DLLEXPORT __declspec(dllexport)    
    169. /*sms storage*/    
    170. #define SMS_SIM 0    
    171. #define SMS_MT 1    
    172. /*sms states*/    
    173. #define SMS_UNREAD 0    
    174. #define SMS_READ 1    
    175. /*sms type*/    
    176. #define SMS_NOPARSE -1    
    177. #define SMS_NORMAL 0    
    178. #define SMS_FLASH 1    
    179. #define SMS_MMSNOTI 2    
    180. typedef struct tagSmsEntry {    
    181. int index; /*index, start from 1*/    
    182. int status; /*read, unread*/    
    183. int type; /*-1-can't parser 0-normal, 1-flash, 2-mms*/    
    184. int storage; /*SMS_SIM, SMS_MT*/    
    185. char date[24];    
    186. char number[32];    
    187. char text[144];    
    188. } SmsEntry;    
    189. DLLEXPORT int SmsInit(void);    
    190. DLLEXPORT int SmsSend(char *phonenum, char *content);    
    191. DLLEXPORT int SmsSetSCA(char *sca);    
    192. DLLEXPORT int SmsGetSCA(char *sca);    
    193. DLLEXPORT int SmsSetInd(int ind);    
    194. DLLEXPORT int SmsGetInd(void);    
    195. DLLEXPORT int SmsGetInfo(int storage, int *max, int *used);    
    196. DLLEXPORT int SmsSaveFlash(int flag);    
    197. DLLEXPORT int SmsRead(SmsEntry *entry, int storage, int index);    
    198. DLLEXPORT int SmsDelete(int storage, int index);    
    199. DLLEXPORT int SmsModifyStatus(int storage, int index); /*unread -> read*/    
    200. #endif   
    201.   
    202.   
    203.   在有了這兩個頭文件之后,就可以進行C程序的編寫了。也就是實現對JNI調用的兩個方法。在網上的資料中,由于調用的方法實現的都比較簡單,(大多是打印字符串等)所以避開了JNI中最麻煩的部分,也是最關鍵的部分,參數的傳遞。     
    204. 由于Java和C的編碼是不同的,所以傳遞的參數是要進行再處理,否則C程序是會對參數在編譯過程中提出警告,例如;warning C4024: 'SmsSend' : different types for formal and actual parameter 2等。   
    205.   Sms.c的程序如下:   
    206.   
    207. #include "sms.h"    
    208. #include "com_mobilesoft_sms_mobilesoftinfo_SendSMS.h"    
    209. JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsInit(JNIEnv * env, jclass jobject)    
    210. {    
    211. return SmsInit();    
    212. }    
    213.   
    214. JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsSend(JNIEnv * env, jclass jobject, jbyteArray mobileno, jbyteArray smscontent)    
    215. {    
    216. char * pSmscontent ;    
    217. //jsize theArrayLengthJ = (*env)->GetArrayLength(env,mobileno);    
    218. jbyte * arrayBody = (*env)->GetByteArrayElements(env,mobileno,0);    
    219. char * pMobileNo = (char *)arrayBody;    
    220. printf("[%s]\n ", pMobileNo);    
    221. //jsize size = (*env)->GetArrayLength(env,smscontent);    
    222. arrayBody = (*env)->GetByteArrayElements(env,smscontent,0);    
    223. pSmscontent = (char *)arrayBody;    
    224. printf("<%s>\n", pSmscontent);    
    225. return SmsSend(pMobileNo,pSmscontent);    
    226. }   
    227.   
    228.   
    229.   
    230.   對于C或C++,在程序上是會有稍微的不同,這可以由讀者對其進行適當的修改。這里要注意的是GetArrayLength,GetByteArrayElements等這些JNI中已經包含的方法,這些方法是專門對轉換參數類型而提供的。具體的方法有很多,在下一篇中會做專門的介紹。   
    231.   在完成了上述的文件后,可以對sms.c進行編譯,生成.dll文件(建議在release中編譯,這樣動態鏈接庫的容積會比較小!)   
    232.   完成.dll文件的編譯后,就可以在Java中調用C程序中的方法了。例如文件test.java   
    233.   
    234. public class test {    
    235. public test() {    
    236. }    
    237. public static void main(String[] args) {    
    238. byte[] mobileno = {    
    239. 0x310x330x360x360x310x360x330x300x360x360x370x00};    
    240. String smscontentemp = "早上好";    
    241. byte[] temp = {0};    
    242. try {    
    243. byte[] smscontentdb = smscontentemp.getBytes("gbk");    
    244. byte[] smscontent = new byte[smscontentdb.length + temp.length];    
    245. System.arraycopy(smscontentdb, 0, smscontent, 0, smscontentdb.length);    
    246. System.arraycopy(temp, 0, smscontent, smscontentdb.length, temp.length);    
    247. SendSMS sendSMS = new SendSMS();    
    248. sendSMS.SmsInit();    
    249. if (sendSMS.SmsSend(mobileno, smscontent) >= 0) {    
    250. System.out.println("chenggong !");    
    251. }    
    252. else {    
    253. System.out.println("shibai !");    
    254. }    
    255. }catch (Exception ex) {}    
    256. }    
    257. }   
    258.   
    259.   
    260.   
    261.   在這個文件中要注意的有一點,就是在傳遞字節數組到C程序中時,最后的結尾一定要以0結束。這是一個偷懶的做法,不過是個有效的做法。因為大多數情況 下,接口是由第三方提供的。所以我們一般是不知道在C的方法里,具體是怎么處理參數的。而C又是要求數組是有長度。所以,在Java中,如果你不想寫程序 傳數組的長度,那么在數組中以0結尾就是最方便的方法了。當然,如果有更好的方法也希望大家提出。   
    262.   
    263.   到這里,一個完整的Java通過JNI調用動態鏈接庫的程序就完成了。實際上也不是很復雜。只要多注意一下細節,是很容易得出來的。  
    posted @ 2009-05-07 11:04 lanxin1020 閱讀(173) | 評論 (0)編輯 收藏
    Properties 基本知識
    如果不熟悉 java.util.Properties 類,那么現在告訴您它是用來在一個文件中存儲鍵-值對的,其中鍵和值是用等號分隔的,如清單 1 所示。

    清單 1. 一組屬性示例
    foo=bar
    fu=baz

    將清單 1 裝載到 Properties 對象中后,您就可以找到兩個鍵( foo 和 fu )和兩個值( foo 的 bar 和 fu 的 baz )了。這個類支持帶 \u 的嵌入 Unicode 字符串,但是這里重要的是每一項內容都當作 String 。

    清單 2 顯示了如何裝載屬性文件并列出它當前的一組鍵和值。只需傳遞這個文件的 InputStream 給 load() 方法,就會將每一個鍵-值對添加到 Properties 實例中。然后用 list() 列出所有屬性或者用 getProperty() 獲取單獨的屬性。
    清單 2. 裝載屬性
    Java代碼
    1. import java.util.*;   
    2. import java.io.*;   
    3.   
    4. public class LoadSample {   
    5.     public static void main(String args[]) throws Exception {   
    6.       Properties prop = new Properties();   
    7.       FileInputStream fis =    
    8.         new FileInputStream("sample.properties");   
    9.       prop.load(fis);   
    10.       prop.list(System.out);   
    11.       System.out.println("\nThe foo property: " +   
    12.           prop.getProperty("foo"));   
    13.     }   
    14. }  

    運行 LoadSample 程序生成如清單 3 所示的輸出。注意 list() 方法的輸出中鍵-值對的順序與它們在輸入文件中的順序不一樣。 Properties 類在一個散列表(hashtable,事實上是一個 Hashtable 子類)中儲存一組鍵-值對,所以不能保證順序。

    清單 3. LoadSample 的輸出

    -- listing properties --
    fu=baz
    foo=bar

    The foo property: bar

    XML 屬性文件
    這里沒有什么新內容。 Properties 類總是這樣工作的。不過,新的地方是從一個 XML 文件中裝載一組屬性。它的 DTD 如清單 4 所示。

    清單 4. 屬性 DTD

    dtd 寫道
    Java代碼
    1. <?xml version="1.0" encoding="UTF-8"?>    
    2. <!-- DTD for properties -->    
    3. <!ELEMENT properties ( comment?, entry* ) >    
    4. <!ATTLIST properties version CDATA #FIXED "1.0">    
    5. <!ELEMENT comment (#PCDATA) >    
    6. <!ELEMENT entry (#PCDATA) >    
    7. <!ATTLIST entry key CDATA #REQUIRED>  



    如果不想細讀 XML DTD,那么可以告訴您它其實就是說在外圍 <properties> 標簽中包裝的是一個 <comment> 標簽,后面是任意數量的 <entry> 標簽。對每一個 <entry> 標簽,有一個鍵屬性,輸入的內容就是它的值。清單 5 顯示了 清單 1中的屬性文件的 XML 版本是什么樣子的。

    清單 5. XML 版本的屬性文件
    Java代碼
    1. <?xml version="1.0" encoding="UTF-8"?>   
    2. <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">   
    3. <properties>   
    4. <comment>Hi</comment>   
    5. <entry key="foo">bar</entry>   
    6. <entry key="fu">baz</entry>   
    7. </properties>  

    如果清單 6 所示,讀取 XML 版本的 Properties 文件與讀取老格式的文件沒什么不同。
    清單 6. 讀取 XML Properties 文件
    Java代碼
    1. import java.util.*;   
    2. import java.io.*;   
    3.   
    4. public class LoadSampleXML {   
    5.     public static void main(String args[]) throws Exception {   
    6.       Properties prop = new Properties();   
    7.       FileInputStream fis =   
    8.         new FileInputStream("sampleprops.xml");   
    9.       prop.loadFromXML(fis);   
    10.       prop.list(System.out);   
    11.       System.out.println("\nThe foo property: " +   
    12.           prop.getProperty("foo"));   
    13.     }   
    14. }  

    關于資源綁定的說明
    雖然 java.util.Properties 類現在除了支持鍵-值對,還支持屬性文件作為 XML 文件,不幸的是,沒有內置的選項可以將 ResourceBundle 作為一個 XML 文件處理。是的, PropertyResourceBundle 不使用 Properties 對象來裝載綁定,不過裝載方法的使用是硬編碼到類中的,而不使用較新的 loadFromXML() 方法。

    運行清單 6 中的程序產生與原來的程序相同的輸出,如 清單 2所示。

    保存 XML 屬性
    新的 Properties 還有一個功能是將屬性存儲到 XML 格式的文件中。雖然 store() 方法仍然會創建一個類似 清單 1 所示的文件,但是現在可以用新的 storeToXML() 方法創建如 清單 5 所示的文件。只要傳遞一個 OutputStream 和一個用于注釋的 String 就可以了。清單 7 展示了新的 storeToXML() 方法。

    清單 7. 將 Properties 存儲為 XML 文件
    Java代碼
    1. import java.util.*;   
    2. import java.io.*;   
    3.   
    4. public class StoreXML {   
    5.     public static void main(String args[]) throws Exception {   
    6.       Properties prop = new Properties();   
    7.       prop.setProperty("one-two""buckle my shoe");   
    8.       prop.setProperty("three-four""shut the door");   
    9.       prop.setProperty("five-six""pick up sticks");   
    10.       prop.setProperty("seven-eight""lay them straight");   
    11.       prop.setProperty("nine-ten""a big, fat hen");   
    12.       FileOutputStream fos =   
    13.         new FileOutputStream("rhyme.xml");   
    14.       prop.storeToXML(fos, "Rhyme");   
    15.       fos.close();   
    16.     }   
    17. }  

    運行清單 7 中的程序產生的輸出如清單 8 所示。

    清單 8. 存儲的 XML 文件
    Java代碼
    1. <?xml version="1.0" encoding="UTF-8"?>   
    2. <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">   
    3. <properties>   
    4. <comment>Rhyme</comment>   
    5. <entry key="seven-eight">lay them straight</entry>   
    6. <entry key="five-six">pick up sticks</entry>   
    7. <entry key="nine-ten">a big, fat hen</entry>   
    8. <entry key="three-four">shut the door</entry>   
    9. <entry key="one-two">buckle my shoe</entry>   
    10. </properties>  

    結束語
    使用 XML 文件還是使用老式的 a=b 類型的文件完全取決于您自己。老式文件從內存的角度看肯定是輕量級的。不過,由于 XML 的普遍使用,人們會期望 XML 格式流行起來,因為它已經被廣泛使用了,只不過沒有用到 Properties 對象。選擇完全在您。分析軟件包 private XMLUtils 類的源代碼以獲得關于所使用的 XML 解析的更多信息。
    Java代碼
    1. import java.io.FileInputStream;   
    2. import java.io.IOException;   
    3. import java.io.InputStream;   
    4. import java.util.Properties;   
    5. /**  
    6. * 實現properties文件的讀取  
    7. * @author bbflyerwww  
    8. * @date 2006-08-02  
    9. */  
    10. public class PTest {   
    11.       public static void main(String[] args) {   
    12.           try {   
    13.               long start = System.currentTimeMillis();   
    14.               InputStream is = new FileInputStream("conf.properties");   
    15.               Properties p = new Properties();   
    16.               p.load(is);   
    17.               is.close();   
    18.               System.out.println("SIZE : " + p.size());   
    19.               System.out.println("homepage : " + p.getProperty("homepage"));   
    20.               System.out.println("author : " + p.getProperty("author"));   
    21.               System.out.println("school : " + p.getProperty("school"));   
    22.               System.out.println("date : " + p.getProperty("date"));   
    23.               long end = System.currentTimeMillis();    
    24.               System.out.println("Cost : " + (end - start));   
    25.           } catch (IOException ioe) {   
    26.               ioe.printStackTrace();   
    27.           }   
    28.       }   
    29. }  

    conf.properties
    Java代碼
    1. # Configuration fileauthor = bbflyerwww   
    2. school = WuHan University   
    3. date = 2006-08-02  


    Result

    SIZE:4
    author : bbflyerwww
    school : WuHan University
    date : 2006-08-02
    Cost : 0
    posted @ 2009-05-06 22:52 lanxin1020 閱讀(468) | 評論 (0)編輯 收藏

    一、LOG4J組成

        LOG4J主要由三大組件組成:
        . Logger: 決定什么日志信息應該被輸出、什么日志信息應該被忽略;
        . Appender: 指定日志信息應該輸出到什么地方, 這些地方可以是控制臺、文件、網絡設備;
        . Layout: 指定日志信息的輸出格式;

        一個Logger可以有多個Appender,也就是說日志信息可以同時輸出到多個設備上,每個Appender對應
        一種Layout(示例見下圖)。

                  ↗  Appender1  →  Layout
         /    
        Logger
         ﹨  
                  ↘  Appender2  →  Layout


    二、Logger組件

        1. Logger組件提供的方法:

           Logger組件是LOG4J的核心組件,它代表了Log4J的日志記錄器,它能夠對日志信息進行分類篩選。它由org.apache.log4j.Logger類實現,提供了如下方法:

     

    java 代碼
    1. package org.apache.log4j;   
    2.   
    3. public class Logger {   
    4.   
    5.            // Creation & retrieval methods:   
    6.            public static Logger getRootLogger();   
    7.            public static Logger getLogger(String name);   
    8.   
    9.            // printing methods:   
    10.            public void debug(Object message);   
    11.            public void info(Object message);   
    12.            public void warn(Object message);   
    13.            public void error(Object message);   
    14.            public void fatal(Object message);   
    15.       
    16.            // generic printing method:   
    17.            public void log(Priority p, Object message);   
    18. }   

        2. 在配置文件中配置Logger組件

           可在Log4J配置文件中配置自己的Logger組件,示例:

           log4j.logger.myLogger=WARN

           以上代碼定義了一個Logger組件,名稱為myLogger,日志級別為WARN。
     
        3. 日志級別種類:

           一共有五種,級別由高到低依次是:fatal、error、warn、info、debug。獲得Logger實例后,我們可調用以下方法之一輸出日志信息:

           public void debug(Object message);          //輸出debug級別的日志信息;
           public void info(Object message);           //輸出info級別的日志信息;
           public void warn(Object message);           //輸出warn級別的日志信息;
           public void error(Object message);          //輸出error級別的日志信息;
           public void fatal(Object message);          //輸出fatal級別的日志信息;
           public void log(Priority p, Object message);//輸出參數Priority指定級別的日志信息;

           以上方法只有當它的級別大于或等于Logger組件配置的日志級別時才調用。以前面我們配置的myLogger為例,它的日志級別為WARN, 那么在程序中,它的warn()、error()、fatal()方法會被執行。對于log()方法,只有當它的參數Priority指定的日志級別大于或等于WARN時,它才會被執行。

        4. 為什么需要對日志進行分級?
       
           在寫程序的時候,為了調試程序,我們會在很多出錯的地方輸出大量的日志信息。當程序調試完,不需要這些信息時,將程序中這些輸出日志信息代碼刪除嗎?這樣費時費力,對于大型程序幾乎不可行。通過對日志分級,假如不想輸出WARN級別的日志信息,則Logger組件的級別調高即可,省時省心。

        5. Logger組件的繼承性

           Log4J提供了一個root Logger,它是所有Logger組件的“祖先”,它永遠存在,且不能通過名字檢索或引用,通過Logger.getRootLogger()方法取得它。配置root Logger代碼:

           log4j.rootLogger=INFO,console

           可在配置文件中方便地配置存在繼承關系的Logger組件,凡是在符號“.”后面的組件都會成為在符號“.”前面的Logger組件的子類。例如:

           log4j.apache.myLogger=WARN
           log4j.apache.myLogger.mySonLogger=,file

           以上代碼中, mySonLogger是myLogger的子類Logger組件。Logger組件的繼承關系:
           . 如果子類Logger組件沒有定義日志級別,則將繼承父類的日志級別;
           . 如果子類Logger組件定義了日志級別,就不會繼承父類的日志級別;
           . 黙認情況下,子類Logger組件會繼承父類所有的Appender,把它們加入到自己的Appener;
           . 如果把子類Logger組件的additivity標志設為false,那么它就不會繼承父類Appender。additivity標志 默認值為false;

           以上配置的三個Logger繼承關系示例如圖:
        
           root Logger: 日志級別=INFO  appender清單=console
                                ↑
           myLogger: 日志級別=WARN appender清單=null
                                ↑
           mySonLogger: 日志級別=null appender清單=file

           這三個Logger組件實際日志級別和Appender如下表:

           Logger組件          日志級別          Appender清單
           root Logger         INFO              console
           myLogger            WARN              console(繼承)
           mySonLogger         WARN(繼承)        file,console(繼承)
          
    三、Appender組件

        Appender組件決定將日志信息輸出到什么地方。支持以下目的地:
        . 控制臺(Console);
        . 文件(File);
        . GUI組件(GUI component);
        . 套接口服務器(Remote socket server);
        . NT的事件記錄器(NT Event Logger);
        . UNIX Syslog守護進程(Remote UNIX Syslog daemon);

        一個Logger可同時對應多個Appender,示例:myLogger配置二個Appender: 一個file, 一個是console:

        log4j.logger.myAppender=WARN,file,console

        log4j.appender.file=org.apache.log4j.RollingFileAppender
        log4j.appender.file.File=log.txt

        log4j.apender.console=org.apache.log4j.ConsoleAppender

    四、Layout組件

        Layout組件決定日志輸出格式,有以下幾種類型:
        . org.apache.log4j.HTMLLayout(以HTML表格形式布局);
        . org.apache.log4j.PatternLayout(可以靈活地指定布局模式);
        . org.apache.log4j.SimpleLayout(包含日志信息的級別和信息字符串);
        . org.apache.log4j.TTCCLayout(包含日志產生的時間、線程和類別等信息);
       
        為名稱為console的Appender配置SimpleLayout,代碼如下:

        log4j.appender.console.layout=org.apache.log4j.SimpleLayout

        輸出日志格式如下:

        WARN - This is a log message from the myLogger
       
        為名稱為file的Appender配置PatternLayout,代碼如下:

        log4j.appender.file.layout=org.apache.log4j.PatternLayout
        log4j.appender.file.layout.ConversionPattern=%t %p - %m%n

        輸出日志格式如下:

        THREAD-1 WARN - This is a log message from the myLogger

        PatternLayout讓開發者依照ConversionPattern定義輸出格式。ConversionPattern中一些指定日志內容和格式的預定義符號說明如下:

        符號         描述
        %r           自程序開始后消耗的毫秒數
        %t           表示日志記錄請求生成的線程
        %p           表示日專語句的優先級
        %r           與日志請求相關的類別名稱
        %c           日志信息所在的類名
        %m%n         表示日志信息的內容

    五、Log4J的基本用法

        1. 定義配置文件
           Log4J支持二種配置文件格式:XML和Java屬性文件(采用“鍵=值”形式)。以下為Java屬性文件
           格式配置文件:
         
           . 配置Logger組件
            
             配置root Logger語法為:log4j.rootLogger=[priority],appenderName,appenderName,...
             配置自定義Logger組件語法為:log4j.logger.loggerName=[priority],appenderName,appenderName,...

             其中:priority為日志級別,可選值包括FATAL、ERROR、WARN、INFO、DEBUG、ALL;
                   appenderName指定Appender組件,可指定多個;        

           . 配置Appender組件

             配置日志信息輸出目的地Appender, 語法為:
             log4j.appender.appenderName=fully.ualified.name.of.appender.class
             log4j.appender.appenderName.option1=value1
             ...
             log4j.appender.appenderName.optionN=valueN

             Log4J提供的Appender有以下幾種:

             a. org.apache.log4j.ConsoleAppender(控制臺);
             b. org.apache.log4j.FileAppender(文件);
             c. org.apache.log4j.DailyRollingFileAppender(每天產生一個日志文件);
             d. org.apache.log4j.RollingFileAppender(文件大小到指定尺寸產生一個新的文件);
             e. org.apache.log4j.WriteAppender(將日志信息以流格式發送到任意指定地方);

           . 配置Layout組件

             配置Layout組件語法為:
             log4j.appender.appenderName.layout=fully.ualified.name.of.appender.class
             log4j.appender.appenderName.layout.option1=value1
             ...
             log4j.appender.appenderName.layout.optionN=valueN

             下面為一配置文件示例,文件名為log4j.properties:

             ## LOGGERS ##

             #configure root logger
             log4j.rootLogger=INFO,console
             #define a logger named myLogger
             log4j.logger.myLogger=WARN
             #define a second logger that is a child to myLogger
             log4j.logger.myLogger.mySonLogger=,file

             ## APPENDERS ##

             #define an appender named console, which is set to be a ConsoleAppender
             log4j.appender.console=org.apache.log4j.ConsoleAppender

             # define an appender named file, which is set to be a RollingFileAppender
             log4j.appender.file=org.apache.log4j.FileAppender
             log4j.appender.file.File=log.txt

             ## LAYOUTS ##
             # assian a SimpleLayout to console appender
             log4j.appender.console.layout=org.apache.log4j.SimpleLayout

             # assian a PatternLayout to file appender
             log4j.appender.file.layout=org.apache.log4j.PatternLayout
             log4j.appender.file.layout.ConversionPattern=%t%p-%m%n
            
        2. 程序中使用Log4j

           . 獲得日志記錄器:

             獲得rootLogger:Logger rootLogger=Logger.getRootLogger();
             獲得自定義Logger:Logger myLogger = Logger.getLogger("log4j.logger.myLogger");

           . 讀取日志記錄器,配置Log4J環境;

             a. BasicConfigurator.configure(): 自動快速地使用默認Log4J環境;
             b. Property.configurator.configure(String configFilename): 讀取使用Java屬性格式的配置文件并配置Log4J環境;
             c. DOMConfigurator.configure(String filename): 讀取XML形式的配置文件并配置LOG4J環境;

           . 輸出日志信息;

             在程序代碼中需要生成日志的地方,調用Logger的各種輸出日志方法輸出不同級別的日志,例如:
            
             myLogger.debug("Thie is a log message from the " + myLogger.getName());

             下面為一使用Log4J的程序,程序名為Test.java:

    java 代碼
    1.  import org.apache.log4j.Logger;   
    2.  import org.apache.log4j.PropertyConfigurator;   
    3.     
    4.  public class Test {   
    5.   
    6.    public static void main(String[] args) {   
    7.      //Get an instance of the myLogger   
    8.      Logger myLogger = Logger.getLogger("myLogger");   
    9.       
    10.      //Get an instance of the childLogger   
    11.      Logger mySonLogger = Logger.getLogger("myLogger.mySonLogger");   
    12.      //Load the proerties using the PropertyConfigurator   
    13.      PropertyConfigurator.configure("log4j.properties");   
    14.   
    15.      //Log Messages using the Parent Logger   
    16.      myLogger.debug("Thie is a log message from the " + myLogger.getName());   
    17.      myLogger.info("Thie is a log message from the " + myLogger.getName());   
    18.      myLogger.warn("Thie is a log message from the " +  myLogger.getName());   
    19.      myLogger.error("Thie is a log message from the " + myLogger.getName());   
    20.      myLogger.fatal("Thie is a log message from the " + myLogger.getName());   
    21.   
    22.      mySonLogger.debug("Thie is a log message from the " + mySonLogger.getName());   
    23.      mySonLogger.info("Thie is a log message from the " + mySonLogger.getName());   
    24.      mySonLogger.warn("Thie is a log message from the " +  mySonLogger.getName());   
    25.      mySonLogger.error("Thie is a log message from the " + mySonLogger.getName());   
    26.      mySonLogger.fatal("Thie is a log message from the " + mySonLogger.getName());   
    27.    }   
    28. }   

            程序運行結果為:

            WARN - Thie is a log message from the myLogger
            ERROR - Thie is a log message from the myLogger
            FATAL - Thie is a log message from the myLogger
            WARN - Thie is a log message from the myLogger.mySonLogger
            ERROR - Thie is a log message from the myLogger.mySonLogger
            FATAL - Thie is a log message from the myLogger.mySonLogger

            另在Test.class所在的目錄下看到一個log.txt文件,內容如下:

            WARN - Thie is a log message from the myLogger.mySonLogger
            ERROR - Thie is a log message from the myLogger.mySonLogger
            FATAL - Thie is a log message from the myLogger.mySonLogger

            如將配置文件log4j.properties中語句

     log4j.logger.myLogger.mySonLogger=,file

     改為

     log4j.logger.myLogger.mySonLogger=,file,console

     再次運行程序,結果如下:

            WARN - Thie is a log message from the myLogger
            ERROR - Thie is a log message from the myLogger
            FATAL - Thie is a log message from the myLogger
            WARN - Thie is a log message from the myLogger.mySonLogger
            WARN - Thie is a log message from the myLogger.mySonLogger
            ERROR - Thie is a log message from the myLogger.mySonLogger
            ERROR - Thie is a log message from the myLogger.mySonLogger
            FATAL - Thie is a log message from the myLogger.mySonLogger         
            FATAL - Thie is a log message from the myLogger.mySonLogger

            mySonLogger的日志在控制臺上輸出了二次,這是因為mySonLogger繼承了父類console Appender,
            本身又定義了一個console Appender, 因而有二個console Appender。

    六、在web應用中使用Log4J

        創建一個Servlet,在它初始化方法中讀取Log4J配置文件并配置Log4J環境,這個Servlet在Web應用啟
        動時候被加載和初始化,然后就可在其它Web組件中獲取Logger對象并輸出日志。

        1. 創建用于配置Log4J環境的Servlet

    java 代碼
    1. import javax.servlet.*;   
    2. import javax.servlet.http.*;   
    3. import java.io.*;   
    4. import java.util.*;   
    5.   
    6. import org.apache.log4j.PropertyConfigurator;   
    7.   
    8. public class Log4JServlet extends HttpServlet {   
    9.       public void init() throws ServletException {   
    10.            String path = getServletContext().getRealPath("/");   
    11.            //getInitParameter("propfile")方法從web.xml文件中讀取Log4J配置文件的名字"profile"。   
    12.            String propfile = path + getInitParameter("propfile");   
    13.            PropertyConfigurator.configure(propfile);   
    14.       }   
    15. }   
    16.   

          該Servlet在web.xml中的配置如下:

    xml 代碼
    1. <servlet>  
    2.   <servlet-name>log4jServlet</servlet-name>  
    3.   <servlet-class>Log4JServlet</servlet-class>  
    4.   <init-param>  
    5.     <param-name>propfile</param-name>  
    6.     <param-value>/WEB-INF/log4j.properties</param-value>  
    7.   </init-param>  
    8.   <load-on-startup>1</load-on-startup>  
    9. </servlet>  

    2. 在login.jsp中輸出日志
           <%@page import="org.apache.log4j.Logger"%>
           <html>
             <head>
               <title>login</title>
             </head>
             <body>
               <%
                 Logger myLogger = Logger.getLogger("myLogger");
                 Logger mySonLogger = Logger.getLogger("myLogger.mySonLogger");
                 myLogger.debug("Thie is a log message from the " + myLogger.getName());
                 myLogger.info("Thie is a log message from the " + myLogger.getName());
                 myLogger.warn("Thie is a log message from the " +  myLogger.getName());
                 myLogger.error("Thie is a log message from the " + myLogger.getName());
                 myLogger.fatal("Thie is a log message from the " + myLogger.getName());

                 mySonLogger.debug("Thie is a log message from the " + mySonLogger.getName());
                 mySonLogger.info("Thie is a log message from the " + mySonLogger.getName());
                 mySonLogger.warn("Thie is a log message from the " +  mySonLogger.getName());
                 mySonLogger.error("Thie is a log message from the " + mySonLogger.getName());
                 mySonLogger.fatal("Thie is a log message from the " + mySonLogger.getName());
               %>
               <br>
                 <form name="loginForm" method="post" action="dispatcher">
                 username: <input type="text" name="username">
                 <br>
                 password: <input type="text" name="password">
                 <br>
                 <input type="submit" name="submit" value="submit">
               </form>
             </body>
           </html>
                  
        3. 發布運行使用Log4J的web應用
           1) 將Log4J的JAR文件拷貝至目錄:<WEB應用所在目錄>/WEB-INF/lib
           2) 創建Log4J的配置文件log4j.properties, 存放目錄為:<WEB應用所在目錄>/WEB-INF。內容同前面配置文件示例。
           3) 編譯Log4JServlet, 存放至目錄: <WEB應用所在目錄>/WEB-INF/classes
           4) 修改web.xml文件,加入以下內容:

    xml 代碼
    1. <servlet>  
    2.   <servlet-name>log4jServlet</servlet-name>  
    3.   <servlet-class>Log4JServlet</servlet-class>  
    4.   <init-param>  
    5.     <param-name>profile</param-name>  
    6.     <param-value>/WEB-INF/log4j.properties</param-value>  
    7.   </init-param>  
    8.   <load-on-startup>1</load-on-startup>  
    9. </servlet>  


           5) 啟動服務器,訪問login.jsp頁面,在服務器控制臺上看到如下日志:
              WARN - Thie is a log message from the myLogger
              ERROR - Thie is a log message from the myLogger
              FATAL - Thie is a log message from the myLogger
              WARN - Thie is a log message from the myLogger.mySonLogger
              ERROR - Thie is a log message from the myLogger.mySonLogger
              FATAL - Thie is a log message from the myLogger.mySonLogger

              另在<WEB應用所在目錄>/WEB-INF目錄下看到一個log.txt文件,內容如下:

              WARN - Thie is a log message from the myLogger.mySonLogger
              ERROR - Thie is a log message from the myLogger.mySonLogger
              FATAL - Thie is a log message from the myLogger.mySonLogger

    posted @ 2009-05-04 17:19 lanxin1020 閱讀(167) | 評論 (0)編輯 收藏

    在實際編程時,要使Log4j真正在系統中運行事先還要對配置文件進行定義。定義步驟就是對Logger、Appender及Layout的分別使用。
      Log4j支持兩種配置文件格式,一種是XML格式的文件,一種是java properties(key=value)【Java特性文件(鍵=值)】。下面我們介紹使用Java特性文件做為配置文件的方法
      具體如下:
      
      1、配置根Logger 其語法為:
      log4j.rootLogger = [ level ] , appenderName1, appenderName2, …
      level : 是日志記錄的優先級,分為OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定義的級別。Log4j建議只使用四個級別,優先級從高到低分別是ERROR、WARN、INFO、DEBUG。通過在這里定義的級別,您可以控制到應用程序中相應級別的日志信息的開關。比如在這里定 義了INFO級別,則應用程序中所有DEBUG級別的日志信息將不被打印出來。
      appenderName:就是指定日志信息輸出到哪個地方。您可以同時指定多個輸出目的地。
      例如:log4j.rootLogger=info,A1,B2,C3
      
      2、配置日志信息輸出目的地 ,其語法為:
      log4j.appender.appenderName = fully.qualified.name.of.appender.class   //
         "fully.qualified.name.of.appender.class" 可以指定下面五個目的地中的一個:
          1.org.apache.log4j.ConsoleAppender(控制臺)
          2.org.apache.log4j.FileAppender(文件)
          3.org.apache.log4j.DailyRollingFileAppender(每天產生一個日志文件)
          4.org.apache.log4j.RollingFileAppender(文件大小到達指定尺寸的時候產生一個新的文件)
          5.org.apache.log4j.WriterAppender(將日志信息以流格式發送到任意指定的地方)
            1.ConsoleAppender選項
                  Threshold=WARN:指定日志消息的輸出最低層次。
                  ImmediateFlush=true:默認值是true,意謂著所有的消息都會被立即輸出。
                  Target=System.err:默認情況下是:System.out,指定輸出控制臺
            2.FileAppender 選項
                  Threshold=WARN:指定日志消息的輸出最低層次。
                  ImmediateFlush=true:默認值是true,意謂著所有的消息都會被立即輸出。
                  File=mylog.txt:指定消息輸出到mylog.txt文件。
                  Append=false:默認值是true,即將消息增加到指定文件中,false指將消息覆蓋指定的文件內容。
            3.DailyRollingFileAppender 選項
                  Threshold=WARN:指定日志消息的輸出最低層次。
                  ImmediateFlush=true:默認值是true,意謂著所有的消息都會被立即輸出。
                  File=mylog.txt:指定消息輸出到mylog.txt文件。
                  Append=false:默認值是true,即將消息增加到指定文件中,false指將消息覆蓋指定的文件內容。
                  DatePattern='.'yyyy-ww:每周滾動一次文件,即每周產生一個新的文件。當然也可以指定按月、周、天、時和分。即對應的格式如下:
                  1)'.'yyyy-MM: 每月
                  2)'.'yyyy-ww: 每周
                  3)'.'yyyy-MM-dd: 每天
                  4)'.'yyyy-MM-dd-a: 每天兩次
                  5)'.'yyyy-MM-dd-HH: 每小時
                  6)'.'yyyy-MM-dd-HH-mm: 每分鐘
            4.RollingFileAppender 選項
                  Threshold=WARN:指定日志消息的輸出最低層次。
                  ImmediateFlush=true:默認值是true,意謂著所有的消息都會被立即輸出。
                  File=mylog.txt:指定消息輸出到mylog.txt文件。
                  Append=false:默認值是true,即將消息增加到指定文件中,false指將消息覆蓋指定的文件內容。
                  MaxFileSize=100KB: 后綴可以是KB, MB 或者是 GB. 在日志文件到達該大小時,將會自動滾動,即將原來的內容移到mylog.log.1文件。
                  MaxBackupIndex=2:指定可以產生的滾動文件的最大數。 實際應用:
      log4j.appender.A1=org.apache.log4j.ConsoleAppender //這里指定了日志輸出的第一個位置A1是控制臺ConsoleAppender
      
      3、配置日志信息的格式 其語法為:
      A. log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
              "fully.qualified.name.of.layout.class" 可以指定下面4個格式中的一個:
            1.org.apache.log4j.HTMLLayout(以HTML表格形式布局),
           2.org.apache.log4j.PatternLayout(可以靈活地指定布局模式),
           3.org.apache.log4j.SimpleLayout(包含日志信息的級別和信息字符串),
           4.org.apache.log4j.TTCCLayout(包含日志產生的時間、線程、類別等等信息)
                1.HTMLLayout 選項
                  LocationInfo=true:默認值是false,輸出java文件名稱和行號
                  Title=my app file: 默認值是 Log4J Log Messages.
                2.PatternLayout 選項
                  ConversionPattern=%m%n :指定怎樣格式化指定的消息。
                3.XMLLayout   選項
                  LocationInfo=true:默認值是false,輸出java文件和行號
      實際應用:
        log4j.appender.A1.layout=org.apache.log4j.PatternLayout
        B . log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ssS} %c %m%n
            這里需要說明的就是日志信息格式中幾個符號所代表的含義:
             -X號: X信息輸出時左對齊;
                %p: 輸出日志信息優先級,即DEBUG,INFO,WARN,ERROR,FATAL,
                %d: 輸出日志時間點的日期或時間,默認格式為ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},輸出類似:2002年10月18日 22:10:28,921
                %r: 輸出自應用啟動到輸出該log信息耗費的毫秒數
                %c: 輸出日志信息所屬的類目,通常就是所在類的全名
                %t: 輸出產生該日志事件的線程名
                %l: 輸出日志事件的發生位置,相當于%C.%M(%F:%L)的組合,包括類目名、發生的線程,以及在代碼中的行數。舉例:Testlog4.main(TestLog4.java:10)
                %x: 輸出和當前線程相關聯的NDC(嵌套診斷環境),尤其用到像java servlets這樣的多客戶多線程的應用中。
                %%: 輸出一個"%"字符
                %F: 輸出日志消息產生時所在的文件名稱
                %L: 輸出代碼中的行號
                %m: 輸出代碼中指定的消息,產生的日志具體信息
                %n: 輸出一個回車換行符,Windows平臺為"\r\n",Unix平臺為"\n"輸出日志信息換行
            可以在%與模式字符之間加上修飾符來控制其最小寬度、最大寬度、和文本的對齊方式。如:
                  1)%20c:指定輸出category的名稱,最小的寬度是20,如果category的名稱小于20的話,默認的情況下右對齊。
                  2)%-20c:指定輸出category的名稱,最小的寬度是20,如果category的名稱小于20的話,"-"號指定左對齊。
                  3)%.30c:指定輸出category的名稱,最大的寬度是30,如果category的名稱大于30的話,就會將左邊多出的字符截掉,但小于30的話也不會有空格。
                  4)%20.30c:如果category的名稱小于20就補空格,并且右對齊,如果其名稱長于30字符,就從左邊交遠銷出的字符截掉。
      這里上面三個步驟是對前面Log4j組件說明的一個簡化;下面給出一個具體配置例子,在程序中可以參照執行:
      log4j.rootLogger=INFO,A1,B2
      log4j.appender.A1=org.apache.log4j.ConsoleAppender
      log4j.appender.A1.layout=org.apache.log4j.PatternLayout
      log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ssS} %c %m%n
      根據上面的日志格式,某一個程序的輸出結果如下:
      0  INFO 2003-06-13 13:23:46968 ClientWithLog4j Client socket: Socket[addr=localhost/127.0.0.1,port=8002,localport=2014]
      16  DEBUG 2003-06-13 13:23:46984 ClientWithLog4j Server says: 'Java server with log4j, Fri Jun 13 13:23:46 CST 2003'
      16  DEBUG 2003-06-13 13:23:46984 ClientWithLog4j GOOD
      16  DEBUG 2003-06-13 13:23:46984 ClientWithLog4j Server responds: 'Command 'HELLO' not understood.'
      16  DEBUG 2003-06-13 13:23:46984 ClientWithLog4j HELP
      16  DEBUG 2003-06-13 13:23:46984 ClientWithLog4j Server responds: 'Vocabulary: HELP QUIT'
      16  DEBUG 2003-06-13 13:23:46984 ClientWithLog4j QUIT

      4. # 當輸出信息于回滾文件時
          log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender   //指定以文件的方式輸出日志
            log4j.appender.ROLLING_FILE.Threshold=ERROR
            log4j.appender.ROLLING_FILE.File=rolling.log   //文件位置,也可以用變量${java.home}、rolling.log
            log4j.appender.ROLLING_FILE.Append=true
            log4j.appender.ROLLING_FILE.MaxFileSize=10KB   //文件最大尺寸
            log4j.appender.ROLLING_FILE.MaxBackupIndex=1   //備份數
            log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout
            log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n     
    ××××××××××××××××××××××××××××××××××××××××××××××××

    附:Log4j比較全面的配置
    LOG4J的配置之簡單使它遍及于越來越多的應用中了:Log4J配置文件實現了輸出到控制臺、文件、回滾文件、發送日志郵件、輸出到數據庫日志表、自定義標簽等全套功能。擇其一二使用就夠用了,
    log4j.rootLogger=DEBUG,CONSOLE,A1,im
    log4j.addivity.org.apache=true
    # 應用于控制臺
    log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
    log4j.appender.Threshold=DEBUG
    log4j.appender.CONSOLE.Target=System.out
    log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
    log4j.appender.CONSOLE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
    #log4j.appender.CONSOLE.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD] n%c[CATEGORY]%n%m[MESSAGE]%n%n
    #應用于文件
    log4j.appender.FILE=org.apache.log4j.FileAppender
    log4j.appender.FILE.File=file.log
    log4j.appender.FILE.Append=false
    log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
    log4j.appender.FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
    # Use this layout for LogFactor 5 analysis
    # 應用于文件回滾
    log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender
    log4j.appender.ROLLING_FILE.Threshold=ERROR
    log4j.appender.ROLLING_FILE.File=rolling.log   //文件位置,也可以用變量${java.home}、rolling.log
    log4j.appender.ROLLING_FILE.Append=true     //true:添加   false:覆蓋
    log4j.appender.ROLLING_FILE.MaxFileSize=10KB   //文件最大尺寸
    log4j.appender.ROLLING_FILE.MaxBackupIndex=1   //備份數
    log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout
    log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n

    #應用于socket
    log4j.appender.SOCKET=org.apache.log4j.RollingFileAppender
    log4j.appender.SOCKET.RemoteHost=localhost
    log4j.appender.SOCKET.Port=5001
    log4j.appender.SOCKET.LocationInfo=true
    # Set up for Log Facter 5
    log4j.appender.SOCKET.layout=org.apache.log4j.PatternLayout
    log4j.appender.SOCET.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD]%n%c[CATEGORY]%n%m[MESSAGE]%n%n

    # Log Factor 5 Appender
    log4j.appender.LF5_APPENDER=org.apache.log4j.lf5.LF5Appender
    log4j.appender.LF5_APPENDER.MaxNumberOfRecords=2000
    # 發送日志給郵件
    log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender
    log4j.appender.MAIL.Threshold=FATAL
    log4j.appender.MAIL.BufferSize=10
    log4j.appender.MAIL.From=web@www.wuset.com
    log4j.appender.MAIL.SMTPHost=www.wusetu.com
    log4j.appender.MAIL.Subject=Log4J Message
    log4j.appender.MAIL.To=web@www.wusetu.com
    log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout
    log4j.appender.MAIL.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
    # 用于數據庫
    log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender
    log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/test
    log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver
    log4j.appender.DATABASE.user=root
    log4j.appender.DATABASE.password=
    log4j.appender.DATABASE.sql=INSERT INTO LOG4J (Message) VALUES ('[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n')
    log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout
    log4j.appender.DATABASE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n

    log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender
    log4j.appender.A1.File=SampleMessages.log4j
    log4j.appender.A1.DatePattern=yyyyMMdd-HH'.log4j'
    log4j.appender.A1.layout=org.apache.log4j.xml.XMLLayout
    #自定義Appender
    log4j.appender.im = net.cybercorlin.util.logger.appender.IMAppender
    log4j.appender.im.host = mail.cybercorlin.net
    log4j.appender.im.username = username
    log4j.appender.im.password = password
    log4j.appender.im.recipient = corlin@cybercorlin.net
    log4j.appender.im.layout=org.apache.log4j.PatternLayout
    log4j.appender.im.layout.ConversionPattern =[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n

    posted @ 2009-05-04 14:40 lanxin1020 閱讀(130) | 評論 (0)編輯 收藏
    Log4j下載
    在apache網站:jakarta.apache.org/log4j 可以免費下載到Log4j最新版本的軟件包。

    Log4j使用
    Log4j的包下載完成后,解壓,將其中打包好的的log4j-1.x.x.jar導入你的工程LIB中。
    Log4j之所以受歡迎的原因之一是它的靈活性。Log4j提供了靈活的配置方法,默認是調用BasicConfigurator.configure()來進行配置,但如果只是簡單的調用BasicConfigurator.configure()來進行配置工作,那么所有的配置都是固定的,不方便以后修改配置。另一種是動態配置,Log4j提供了PropertyConfigurator.configure(……)來動態配置,參數可以是一個properties文件所在路徑的String對象,可以是一個properties文件所在路徑的URL對象,也可以是一個properties對象。如果要用XML文件來配置信息,則可用類型的DOMConfigurator()函數來從一個XML文件中加載配置信息。這種方式更方便修改配置。

    動態配置

    Java代碼
    1. package http;       
    2.       
    3. import org.apache.log4j.BasicConfigurator;       
    4. import org.apache.log4j.Logger;       
    5. import org.apache.log4j.PropertyConfigurator;       
    6. import org.apache.log4j.xml.DOMConfigurator;       
    7.       
    8. public class Log4jDemo {       
    9.     static Logger log = Logger.getLogger(Log4jDemo.class.getClass());       
    10.     /**     
    11.      * main     
    12.      * @param args     
    13.      */      
    14.     public static void main(String[] args) {       
    15.         BasicConfigurator.configure();//默認配置       
    16.         PropertyConfigurator.configure("c:/log4j.properties");       
    17.         //動態配置,參數可以是一個properties文件所在路徑的String對象       
    18.         //可以是一個properties文件所在路徑的URL對象,也可以是一個properties對象       
    19.                
    20.         DOMConfigurator.configure("c:/log4j.xml");//XML配置文件       
    21.       
    22.         //PropertyConfigurator.configure()的參數還可以是XML、Properties對象       
    23.                
    24.         //下面就可使用log4j       
    25.         log.info("info");       
    26.         log.debug("debug");       
    27.         log.error("error");       
    28.         log.warn("warn");       
    29.     }       
    30.       
    31. }      


    J2EE應用log4j
    上面代碼描述了Log4j的簡單應用,其實使用Log4j也就是這樣簡單方便。當然除了上面的配置方法,還有其它,比如做一個J2EE應用,在J2EE應用使用Log4j,必須先在啟動服務時加載Log4j的配置文件進行初始化,可以在web.xml中進行。


    java 代碼
    Java代碼
    1. import java.io.IOException;       
    2. import java.io.PrintWriter;       
    3.       
    4. import javax.servlet.ServletException;       
    5. import javax.servlet.http.HttpServlet;       
    6. import javax.servlet.http.HttpServletRequest;       
    7. import javax.servlet.http.HttpServletResponse;       
    8.       
    9.       
    10.       
    11. public class J2eeLog4jDemo extends HttpServlet {       
    12.       
    13.     public void destroy() {       
    14.         super.destroy();        
    15.     }          
    16.     public void doGet(HttpServletRequest request, HttpServletResponse response)       
    17.             throws ServletException, IOException {       
    18.     }       
    19.     public void doPost(HttpServletRequest request, HttpServletResponse response)       
    20.             throws ServletException, IOException {             
    21.     }       
    22.     public void init() throws ServletException {       
    23.         //通過web.xml來動態取得配置文件       
    24.         String prefix = getServletContext().getRealPath("/");       
    25.         String file = getInitParameter("log4j");       
    26.       
    27.         //如果沒有給出相應的配置文件,則不進行初始化       
    28.         if(file != null)        
    29.         {       
    30.             PropertyConfigurator.configure(prefix+file);       
    31.         }       
    32.     }       
    33.       
    34. }      

    Web.xml 代碼
    Java代碼
    1. <servlet>      
    2. <servlet-name>j2eelog4jdemoservlet-name>      
    3. <servlet-class>J2eeLog4jDemoservlet-class>      
    4. <init-param>      
    5. <param-name>log4jparam-name>      
    6. <param-value>log4j.propertiesparam-value>      
    7. init-param>      
    8. <load-on-startup>1load-on-startup>  //設為1時,Web容器啟動即進行加載    
    9. servlet  


    Spring配置Log4j
    Spring中配置Log4j只要配置applicationContext.xml文件,Log4j的配置文件放在Web工程的根目錄下,默認是objectname/root下,可以在web.xml中設置工程根目錄.

    設置根目錄


    web.xml 代碼
    Java代碼
    1. <!--不定義webAppRootKey參數,webAppRootKey就是缺省的"webapp.root"-->      
    2.  <context-param>      
    3.   <param-name>webAppRootKeyparam-name>      
    4.   <param-value>webapp.rootparam-value>      
    5.  context-param>  


    配置applicationContext.xml


    applicationContext.xml 代碼
    Java代碼
    1. <!--由Sprng載入的Log4j配置文件位置-->      
    2. <context-param>      
    3.  <param-name>log4jConfigLocationparam-name>      
    4.  <param-value>/WEB-INF/log4j.propertiesparam-value>      
    5.  <!--在這里定位配置文件,需要的是從root開始的絕對路徑-->      
    6. context-param>      
    7.       
    8.       
    9.       
    10. <!--Spring默認刷新Log4j配置文件的間隔,單位為millisecond-->      
    11. <context-param>      
    12.  <param-name>log4jRefreshIntervalparam-name>      
    13.  <param-value>60000param-value>      
    14. context-param>      
    15.       
    16. <!--Spring log4j 監聽器-->      
    17. <listener>      
    18.  <listener-class>org.springframework.web.util.Log4jConfigListenerlistener-class>      
    19. listener>     


    同時使用commons-logging和Log4j


    1)首先在classpath下尋找自己的配置文件commons-logging.properties,如果找到,則使用其中定義的Log實現類
    2)如果找不到commons-logging.properties文件,則在查找是否已定義系統環境變量org.apache.commons.logging.Log,找到則使用其定義的Log實現類
    3)否則,查看classpath中是否有Log4j的包,如果發現,則自動使用Log4j作為日志實現類
    4)否則,使用JDK自身的日志實現類(JDK1.4以后才有日志實現類)
    5)否則,使用commons-logging自己提供的一個簡單的日志實現類SimpleLog


    將commons-logging和Log4j的jar包都放置到classpath下,同時也將Log4j的配置文件放到classpath中,兩者就可以很好的合作,實現如下:
    Java代碼
    1. package com.doctorcom.model;       
    2.       
    3. import org.apache.commons.logging.Log;       
    4.       
    5. public class LogFactorySupport {           
    6.           
    7.     public Log getLog(){       
    8.         Log log = org.apache.commons.logging.LogFactory.getLog(LogFactorySupport.class);       
    9.         log.info("");       
    10.         log.debug("");       
    11.     }       
    12.            
    13. }   


    java 代碼

    Log4j配置內容
    看一個簡單的java屬性配置文件log4j.properties:

    properties 代碼
    Java代碼
    1. #指定根Logger,及日志輸出級別,大于等于該級別的日志將被輸出( DEBUG < INFO < WARN < ERROR < FATAL ) 設為OFF可以關閉日志       
    2. log4j.rootLogger=DEBUG, A1,A2       
    3. #指定log輸出目的,這里設為輸出日志到指定目錄的文件my.log中       
    4. log4j.appender.A1=org.apache.log4j.FileAppender       
    5. log4j.appender.A1.File=\\logs\\my.log   #當前根目錄下    
    6. #指定日志信息的格式       
    7. log4j.appender.A1.layout=org.apache.log4j.PatternLayout        
    8. log4j.appender.A1.layout.ConversionPattern=%r %d{yyyy-MM-dd HH:mm:ss} %c %p -%m%n       
    9.       
    10. #把A2輸出到控制臺       
    11. log4j.appender.A2=org.apache.log4j.ConsoleAppender       
    12. log4j.appender.A2.layout=org.apache.log4j.SimpleLayout        
    13.       
    14. #還可以單獨指定輸出某個包的日志級別       
    15. #log4j.logger.com.study.HelloLog4j=INFO   


    1、配置根Logger,其語法為:
    log4j.rootLogger = [ level ] , appenderName, appenderName2
    level:日志的級別,指定這條日志信息的重要性。分為ALL < DEBUG < INFO < WARN <error fatal=""></error>一般常用的為 DEBUG , INFO ,WARN ,ERROR四種,分別對應Logger類的四種方法
    debug(Object message ) ;
    info(Object message ) ;
    warn(Object message ) ;
    error(Object message ) ;
    如果設置級別為INFO,則優先級大于等于INFO級別(如:INFO、WARN、ERROR)的日志信息將可以被輸出,小于該級別的如:DEBUG將不會被輸出
    appenderName :就是指定日志信息輸出目的地,比如(打印到控制臺,輸出到文件等)。同一條日志信息可以配置多個輸出目的地。

    2、配置log輸出目的地
    Log4j提供以下幾種:
    org.apache.log4j.ConsoleAppender(控制臺)
    org.apache.log4j.FileAppender(文件)
    org.apache.log4j.DailyRollingFileAppender(每天產生一個日志文件)
    org.apache.log4j.RollingFileAppender(文件大小到達指定尺寸的時候產生一個新的文件)
    org.apache.log4j.WriterAppender(將日志信息以流格式發送到任意指定的地方)
    3、log信息的格式
    org.apache.log4j.HTMLLayout(HTML表格形式)
    org.apache.log4j.SimpleLayout(簡單格式的日志,只包括日志信息的級別和指定的信息字符串 ,如:DEBUG - Hello)
    org.apache.log4j.TTCCLayout(日志的格式包括日志產生的時間、線程、類別等等信息)
    org.apache.log4j.PatternLayout(靈活地自定義日志格式)

    當使用org.apache.log4j.PatternLayout來自定義信息格式時,可以使用
    log4j.appender.A1.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p -%m%n 來格式化信息
    %c    輸出所屬類的全名,可寫為 %c{Num} ,Num類名輸出的范圍  如:"com.sun.aaa.classB", %C{2}將使日志輸出輸出范圍為:aaa.classB
    %d    輸出日志時間其格式為 可指定格式 如 %d{HH:mm:ss}等
    %l    輸出日志事件發生位置,包括類目名、發生線程,在代碼中的行數
    %n    換行符
    %m    輸出代碼指定信息,如info(“message”),輸出message
    %p    輸出日志的優先級,即 FATAL ,ERROR 等
    %r    輸出從啟動到顯示該條日志信息所耗費的時間(毫秒數)
    %t    輸出產生該日志事件的線程名
    posted @ 2009-05-04 14:37 lanxin1020 閱讀(161) | 評論 (0)編輯 收藏

    HashMap 與 TreeMap的區別

    HashMap通過hashcode對其內容進行快速查找,而TreeMap中所有的元素都保持著某種固定的順序,如果你需要得到一個有序的結果你就應該使用TreeMap(HashMap中元素的排列順序是不固定的)。

    集合框架”提供兩種常規的Map實現:HashMapTreeMap (TreeMap實現SortedMap接口)。在Map 中插入、刪除和定位元素,HashMap 是最好的選擇。但如果您要按自然順序或自定義順序遍歷鍵,那么TreeMap會更好。使用HashMap要求添加的鍵類明確定義了hashCode()equals()的實現。  這個TreeMap沒有調優選項,因為該樹總處于平衡狀態。

    2、兩個對象值相同(x.equals(y) == true),但卻可有不同的hash code,這句話對不對?hash code是什么意思

     hashcode是給一系列hash算法用的,比如hashtable。不同的對象應該有不同的hashcode,同一個對象應該有同樣的hashcode

    更正,不是同一個對象,而是相等的對象,應該有相同的hashcode

    hash算法是什么啊,作用? hash算法基本就是為了將一個對象和一個整數對應起來,不同的對象對應不同的整數。
    (x.equals(y) == true)那這個的話就是去比較它們所對應的整數?
    不是。有一個equals()函數,和一個hashcode()函數

    3、String a="abc";String b=new String("abc");String c="abc";

    System.out.println(a==b);f
    System.out.println(a==c);t
    System.out.println(b==c);f
    System.out.println(a.equals(b));
    輸出結果是什么?
    為什么?

    4、a=0;b=0;
    if((a=3)>0|(b=3)>0){}
    if((a=3)>0||(b=3)>0){}分別說出a,b的值

    posted @ 2009-04-18 09:42 lanxin1020 閱讀(214) | 評論 (0)編輯 收藏
         摘要: TREEMAP的排序機制   1package com.sf;  2  3import java.text.CollationKey;  4import java.text.Collator;  5import java.util.Comparator;  6import ...  閱讀全文
    posted @ 2009-04-18 09:36 lanxin1020 閱讀(1091) | 評論 (0)編輯 收藏

    內部類:定義在其他類里面的類。
    使用內部類的理由:
    1.內部類方法能夠訪問外部類的任何數據成員包括私有成員。
    2.對同一個包的其他類,內部類是不可見的。
    3.匿名內部類能夠方便的定義回調而不用寫太多方法。

    非靜態內部類沒有默認的構造函數,非靜態內部類的構造函數都有一個外圍類對象的引用。
    內部類的特殊語法規則:
    1.相對內部類,引用其外部類隱式對象的形式:OuterClass.this
    2.調用內部類的構造函數:outerObject.new InnerClass(construction parameters);
    3.外部類外面引用內部類:OuterClass.InnerClass

    內部類是一種編譯器現象與虛擬機無關。編譯器將內部類翻譯為用$分隔外部類名和內部類名的常規類文件,虛擬機對此并無所知。
    使用javap -private OuterClass$InnerClass。javap這個工具確實挺不錯的,對分析字節碼和源碼都有很大的幫助。
    可以看出詳細的內部類源碼清單,其中包括了編譯器自動添加的部分:
    public class Outer
    {
     public class Inner
     {
     }
    }
    當內部類是非靜態內部類時相應的內部類的詳細源碼如下:
    Compiled from "Outer.java"
    public class Outer$Inner extends java.lang.Object{
        final Outer this$0;  //編譯器自動在內部類里面添加了指向外部類對象的引用
        public Outer$Inner(Outer);  //內部類的構造函數默認有一個外部類對象作為參數。
    }

    當內部類是靜態內部類時:
    Compiled from "Outer.java"
    public class Outer$Inner extends java.lang.Object{
        public Outer$Inner(); //沒有了對外部類對象的引用
    }


    如下代碼模擬了上面內部類的情形唯一不同的是這里的Inner沒有訪問Outer私有數據的權限:
    class Outer{
       Inner in = new Inner(this);
    }

    class Inner{
       public Inner(Outer outer){
          this.outer = outer;
       }
      
       private Outer outer;
    }

     

    //那么權限是如何控制的呢?當內部類中的方法訪問到外部類的私有數據時(注意如果內部類沒有方法去訪問外部類的私有數據不會生成該靜態方法static int access$000(Outer);)
    public class Outer
    {
     private int i;
     public void methodOne()
     {
     }

     class Inner
     {
      public void print(){
       System.out.println(Outer.this.i);
      }
     }
    }

    相應的外部類詳細源碼如下:
    public class Outer
    {
       public Outer();

       public void methodOne();
       static int access$000(Outer); //由編譯器合成的用于內部類對外部類進行特殊訪問權限的控制:這也是
                                    //為什么內部類能夠訪問外部類中的私有數據的原因。
       private int i;
    }

    內部類訪問外部類的private數據的這種方式很可能導致危險,雖然access$000不是一個合法的Java方法名,但是熟悉class文件結構的黑客可以使用十六進制編輯器輕松創建一個用虛擬機指令調用那個方法的類文件。由于隱秘的訪問方法需要擁有包可見性,所以攻擊代碼需要與被攻擊類放在同一個包中。總之,如果內部類訪問了外部類的私有數據域,就有可能通過附加在外部類所在包中的其他類訪問私有數據。

    局部內部類:定義在方法之中,因此局部類沒有public和private修飾符。對外界是完全隱藏的。
    局部類不僅能夠訪問外部類,還能訪問方法中的局部變量(或方法的參數)。不過那些局部變量要聲明為final的。


    匿名內部類:用外部類+$+數字的形式表示。一個外部類中有多個匿名內部類時,其生成的class文件對應有Outer$(1、2、3)的形式表示。注意到該匿名內部類是final的。
    final class Outer$1
    {
       Outer$1(Outer);

       public void method();

       final Outer this$0;
    }

    嵌套類:當內部類不需要訪問外部類的數據成員時應該使用嵌套類。注意只有內部類可以聲明為static的其他不行。
    在接口中聲明的內部類自動轉換為public static的,在接口中定義的成員自動都是public static的。

    內部類構造函數的可見性與其類的可見性相同。

     

    posted @ 2009-04-16 14:06 lanxin1020 閱讀(148) | 評論 (0)編輯 收藏
    僅列出標題
    共7頁: 上一頁 1 2 3 4 5 6 7 下一頁 
    主站蜘蛛池模板: 精品亚洲成在人线AV无码| 亚洲国产无套无码av电影| 国产无遮挡裸体免费视频| 亚洲精品无码专区久久同性男| 综合久久久久久中文字幕亚洲国产国产综合一区首 | 亚洲精品无码中文久久字幕| 国产成人亚洲精品91专区高清| 4hu四虎免费影院www| 无码人妻一区二区三区免费n鬼沢| 亚洲精品免费网站| 免费国产在线观看| 久久精品国产亚洲| 亚洲日韩乱码中文字幕| 一区二区三区免费电影| 97青青草原国产免费观看| 女性自慰aⅴ片高清免费| 亚洲一区二区精品视频| 91亚洲国产成人久久精品| 国产成人精品亚洲一区| 亚洲一区免费观看| 国产真实伦在线视频免费观看| 亚洲人成色77777| 亚洲sss综合天堂久久久| 一级一级一片免费高清| 四虎在线免费视频| 亚洲av麻豆aⅴ无码电影| 99ri精品国产亚洲| 免费福利资源站在线视频| 99久久久国产精品免费牛牛四川| 国产精品免费视频一区| 精品无码一区二区三区亚洲桃色| 久久久久久亚洲精品无码| 久久免费看少妇高潮V片特黄| 尤物永久免费AV无码网站| 久久综合日韩亚洲精品色| 午夜亚洲WWW湿好爽| 91成人在线免费视频| 亚洲电影日韩精品 | 一级毛片免费在线播放| 亚洲视频在线观看免费视频| 一本久久综合亚洲鲁鲁五月天 |