2010年6月25日

          1、Log4j是什么?
            Log4j可以幫助調試(有時候debug是發揮不了作 用的)和分析,要下載和了解更詳細的內容,還是訪問其官方網站吧: http://jakarta.apache.org/log4j .

            2、Log4j的概念
            Log4j中有三個主要的組件,它們分別是 Logger、Appender和Layout,Log4j 允許開發人員定義多個Logger,每個Logger擁有自己的名字,Logger之間通過名字來表明隸屬關系。有一個Logger稱為Root,它永遠存在,且不能通過名字檢索或引用,可以通過Logger.getRootLogger()方法獲得,其它Logger通過 Logger.getLogger(String name)方法。
            Appender則是用來指明將所有的log信息存放到什么地方,Log4j中支持多種appender,如 console、files、GUI components、NT Event Loggers等,一個Logger可以擁有多個Appender,也就是你既可以將Log信息輸出到屏幕,同時存儲到一個文件中。
            Layout的作用是控制Log信息的輸出方式,也就是格式化輸出的信息。
            Log4j中將要輸出的Log信息定義了5種級別,依次為DEBUG、INFO、WARN、ERROR和FATAL,當輸出時,只有級別高過配置中規定的 級別的信息才能真正的輸出,這樣就很方便的來配置不同情況下要輸出的內容,而不需要更改代碼,這點實在是方便啊。

            3、Log4j的配置文件
            雖然可以不用配置文件,而在程序中實現配置,但這種方法在如今的系統開發中顯然是不可取的,能采用配置文件的地方一定一定要用配置文件。Log4j支持兩 種格式的配置文件:XML格式和Java的property格式,本人更喜歡后者,首先看一個簡單的例子吧,如下:
             log4j.rootLogger=debug, stdout, R
             log4j.appender.stdout=org.apache.log4j.ConsoleAppender
             log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

             # Pattern to output the caller's file name and line number.
             log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

             log4j.appender.R=org.apache.log4j.RollingFileAppender
             log4j.appender.R.File=example.log
             log4j.appender.R.MaxFileSize= 100KB

             # Keep one backup file
             log4j.appender.R.MaxBackupIndex=1

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

            首先,是設置root,格式為 log4j.rootLogger=[level],appenderName, ……,其中level就是設置需要輸出信息的級別,后面是appender的輸出的目的地,appenderName就是指定日志信息輸出到哪個地方。您可以同時指定多個輸出目的地。 配置日志信息輸出目的地Appender,其語法為
          log4j.appender.appenderName = fully.qualified.name.of.appender.class
             log4j.appender.appenderName.option1 = value1
             ...
             log4j.appender.appenderName.option = valueN


            Log4j提供的appender有以下幾種:
            org.apache.log4j.ConsoleAppender(控制臺)
            org.apache.log4j.FileAppender(文件)
            org.apache.log4j.DailyRollingFileAppender(每天產生一個日志文件)
            org.apache.log4j.RollingFileAppender(文件大小到達指定尺寸的時候產生新文件)
            org.apache.log4j.WriterAppender(將日志信息以流格式發送到任意指定的地方)

            配置日志信息的格式(布局),其語法為:
          log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
             log4j.appender.appenderName.layout.option1 = value1
             ....
             log4j.appender.appenderName.layout.option = valueN

            Log4j提供的layout有以下幾種:
            org.apache.log4j.HTMLLayout(以HTML表格形式布局),
            org.apache.log4j.PatternLayout(可以靈活地指定布局模式),
            org.apache.log4j.SimpleLayout(包含日志信息的級別和信息字符串),
            org.apache.log4j.TTCCLayout(包含日志產生的時間、線程、類別等等信息)
               www.jhaccp.com.cn
            Log4J采用類似C語言中的printf函數的打印格式格式化日志信息,打印參數如下: %m 輸出代碼中指定的消息
            %p 輸出優先級,即DEBUG,INFO,WARN,ERROR,FATAL
            %r 輸出自應用啟動到輸出該log信息耗費的毫秒數
            %c 輸出所屬的類目,通常就是所在類的全名
            %t 輸出產生該日志事件的線程名
            %n 輸出一個回車換行符,Windows平臺為“rn”,Unix平臺為“n”
            %d 輸出日志時間點的日期或時間,默認格式為ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},輸出類似: 2002年10月18日 22:10:28,921
            %l 輸出日志事件的發生位置,包括類目名、發生的線程,以及在代碼中的行數。舉例:Testlog4.main(TestLog4.java:10)

            4、Log4j在程序中的使用
            要在自己的程序中使用Log4j,首先需要將commons- logging.jar和logging-log4j-1.2.9.jar導入到構建路徑中。然后再將log4j.properties放到src根目錄下。這樣就可以在程序中使用log4j了。在類中使用log4j,首先聲明一個靜態變量 Logger logger=Logger.getLog("classname");現在就可以使用了,用法如下:logger.debug("debug message")或者logger.info("info message"),看下面一個小例子:

          import com.foo.Bar;
             import org.apache.log4j.Logger;
             import org.apache.log4j.PropertyConfigurator;
             public class MyApp {
               static Logger logger = Logger.getLogger(MyApp.class.getName());
               public static void main(String[] args) {
                 // BasicConfigurator replaced with PropertyConfigurator.
                 PropertyConfigurator.configure(args[0]);
                 logger.info("Entering application.");
                 Bar bar = new Bar();
                 bar.doIt();
                 logger.info("Exiting application.");
               }
             }

          posted @ 2010-06-29 11:27 FaithChen 閱讀(167) | 評論 (0)編輯 收藏

          java1.5方法參數不確定

          Java中"Test(Object...args){}"方法聲明的理解——J2SE5的“Varargs”機制收藏

          轉自:

          孫海濤 (sun.haitao@126.com) http://blog.csdn.net/avius

          2004年8月5日最初發表,2004年8月19日最后修訂

          J2SE 1.5提供了“Varargs”機制。借助這一機制,可以定義能和多個實參相匹配的形參。從而,可以用一種更簡單的方式,來傳遞個數可變的實參。本文介紹這一機制的使用方法,以及這一機制與數組、泛型、重載之間的相互作用時的若干問題。


          J2SE 1.5提供了“Varargs”機制。借助這一機制,可以定義能和多個實參相匹配的形參。從而,可以用一種更簡單的方式,來傳遞個數可變的實參。本文介紹這一機制的使用方法,以及這一機制與數組、泛型、重載之間的相互作用時的若干問題。

           

          到J2SE 1.4為止,一直無法在Java程序里定義實參個數可變的方法——因為Java要求實參(Arguments)和形參(Parameters)的數量和類型都必須逐一匹配,而形參的數目是在定義方法時就已經固定下來了。盡管可以通過重載機制,為同一個方法提供帶有不同數量的形參的版本,但是這仍然不能達到讓實參數量任意變化的目的。

          然而,有些方法的語義要求它們必須能接受個數可變的實參——例如著名的main方法,就需要能接受所有的命令行參數為實參,而命令行參數的數目,事先根本無法確定下來。

          對于這個問題,傳統上一般是采用“利用一個數組來包裹要傳遞的實參”的做法來應付。

          1. 用數組包裹實參

          “用數組包裹實參”的做法可以分成三步:首先,為這個方法定義一個數組型的參數;然后在調用時,生成一個包含了所有要傳遞的實參的數組;最后,把這個數組作為一個實參傳遞過去。

          這種做法可以有效的達到“讓方法可以接受個數可變的參數”的目的,只是調用時的形式不夠簡單。

          J2SE 1.5中提供了Varargs機制,允許直接定義能和多個實參相匹配的形參。從而,可以用一種更簡單的方式,來傳遞個數可變的實參。

          Varargs的含義

          大體說來,“Varargs”是“variable number of arguments”的意思。有時候也被簡單的稱為“variable arguments”,不過因為這一種叫法沒有說明是什么東西可變,所以意義稍微有點模糊。

          2. 定義實參個數可變的方法

          只要在一個形參的“類型”與“參數名”之間加上三個連續的“.”(即“...”,英文里的句中省略號),就可以讓它和不確定個實參相匹配。而一個帶有這樣的形參的方法,就是一個實參個數可變的方法。

          清單1:一個實參個數可變的方法

          private static int sumUp(int... values) { }

          注意,只有最后一個形參才能被定義成“能和不確定個實參相匹配”的。因此,一個方法里只能有一個這樣的形參。另外,如果這個方法還有其它的形參,要把它們放到前面的位置上。

          編譯器會在背地里把這最后一個形參轉化為一個數組形參,并在編譯出的class文件里作上一個記號,表明這是個實參個數可變的方法。

          清單2:實參個數可變的方法的秘密形態

          private static int sumUp(int[] values) { }

          由于存在著這樣的轉化,所以不能再為這個類定義一個和轉化后的方法簽名一致的方法。

          清單3:會導致編譯錯誤的組合

          private static int sumUp(int... values) { } private static int sumUp(int[] values) { }

          空白的存亡問題

          根據J2SE 1.5的語法,在“...”前面的空白字符是可有可無的。這樣就有在“...”前面添加空白字符(形如“Object ... args”)和在“...”前面不加空白字符(形如“Object... args”)的兩種寫法。因為目前和J2SE 1.5相配合的Java Code Conventions還沒有正式發布,所以無法知道究竟哪一種寫法比較正統。不過,考慮到數組參數也有“Object [] args”和“Object[] args”兩種書寫方式,而正統的寫法是不在“[]”前添加空白字符,似乎采取不加空白的“Object... args”的寫法在整體上更協調一些。

          3. 調用實參個數可變的方法

          只要把要傳遞的實參逐一寫到相應的位置上,就可以調用一個實參個數可變的方法。不需要其它的步驟。

          清單4:可以傳遞若干個實參

          sumUp(1, 3, 5, 7);

          在背地里,編譯器會把這種調用過程轉化為用“數組包裹實參”的形式:

          清單5:偷偷出現的數組創建

          sumUp(new int[]{1, 2, 3, 4});

          另外,這里說的“不確定個”也包括零個,所以這樣的調用也是合乎情理的:

          清單6:也可以傳遞零個實參

          sumUp();

          這種調用方法被編譯器秘密轉化之后的效果,則等同于這樣:

          清單7:零實參對應空數組

          sumUp(new int[]{});

          注意這時傳遞過去的是一個空數組,而不是null。這樣就可以采取統一的形式來處理,而不必檢測到底屬于哪種情況。

          4. 處理個數可變的實參

          處理個數可變的實參的辦法,和處理數組實參的辦法基本相同。所有的實參,都被保存到一個和形參同名的數組里。根據實際的需要,把這個數組里的元素讀出之后,要蒸要煮,就可以隨意了。

          清單8:處理收到的實參們

          private static int sumUp(int... values) {      int sum = 0;      for (int i = 0; i < values.length; i++) {          sum += values[i];      }      return sum; }

          5. 轉發個數可變的實參

          有時候,在接受了一組個數可變的實參之后,還要把它們傳遞給另一個實參個數可變的方法。因為編碼時無法知道接受來的這一組實參的數目,所以“把它們逐一寫到該出現的位置上去”的做法并不可行。不過,這并不意味著這是個不可完成的任務,因為還有另外一種辦法,可以用來調用實參個數可變的方法。

          在J2SE 1.5的編譯器的眼中,實參個數可變的方法是最后帶了一個數組形參的方法的特例。因此,事先把整組要傳遞的實參放到一個數組里,然后把這個數組作為最后一個實參,傳遞給一個實參個數可變的方法,不會造成任何錯誤。借助這一特性,就可以順利的完成轉發了。

          清單9:轉發收到的實參們

          public class PrintfSample {      public static void main(String[] args) {          //打印出“Pi:3.141593 E:2.718282”          printOut("Pi:%f E:%f\n", Math.PI, Math.E);      }      private static void printOut(String format, Object... args) {          //J2SE 1.5里PrintStream新增的printf(String format, Object... args)方法          System.out.printf(format, args);      } }

          Java里的“printf”和“sprintf”

          C語言里的printf(按一定的格式輸出字符串)和sprintf(按一定的格式組合字符串)是十分經典的使用Varargs機制的例子。在 J2SE 1.5中,也分別在java.io.PrintStream類和java.lang.String類中提供了類似的功能。

          按一定的格式輸出字符串的功能,可以通過調用PrintStream對象的printf(String format, Object... args)方法來實現。

          按一定的格式組合字符串的工作,則可以通過調用String類的String format(String format, Object... args)靜態方法來進行。

          6. 是數組?不是數組?

          盡管在背地里,編譯器會把能匹配不確定個實參的形參,轉化為數組形參;而且也可以用數組包了實參,再傳遞給實參個數可變的方法;但是,這并不表示“能匹配不確定個實參的形參”和“數組形參”完全沒有差異。

          一個明顯的差異是,如果按照調用實參個數可變的方法的形式,來調用一個最后一個形參是數組形參的方法,只會導致一個“cannot be applied to”的編譯錯誤。

          清單10:一個“cannot be applied to”的編譯錯誤

          private static void testOverloading(int[] i) {      System.out.println("A"); } public static void main(String[] args) {      testOverloading(1, 2, 3);//編譯出錯 }

          由于這一原因,不能在調用只支持用數組包裹實參的方法的時候(例如在不是專門為J2SE 1.5設計第三方類庫中遺留的那些),直接采用這種簡明的調用方式。

          如果不能修改原來的類,為要調用的方法增加參數個數可變的版本,而又想采用這種簡明的調用方式,那么可以借助“引入外加函數(Introduce Foreign Method)”和“引入本地擴展(Intoduce Local Extension)”的重構手法來近似的達到目的。

          7. 當個數可變的實參遇到泛型

          J2SE 1.5中新增了“泛型”的機制,可以在一定條件下把一個類型參數化。例如,可以在編寫一個類的時候,把一個方法的形參的類型用一個標識符(如T)來代表,至于這個標識符到底表示什么類型,則在生成這個類的實例的時候再行指定。這一機制可以用來提供更充分的代碼重用和更嚴格的編譯時類型檢查。

          不過泛型機制卻不能和個數可變的形參配合使用。如果把一個能和不確定個實參相匹配的形參的類型,用一個標識符來代表,那么編譯器會給出一個“generic array creation”的錯誤。

          清單11:當Varargs遇上泛型

          private static <T> void testVarargs(T... args) {//編譯出錯 }

          造成這個現象的原因在于J2SE 1.5中的泛型機制的一個內在約束——不能拿用標識符來代表的類型來創建這一類型的實例。在出現支持沒有了這個約束的Java版本之前,對于這個問題,基本沒有太好的解決辦法。

          不過,傳統的“用數組包裹”的做法,并不受這個約束的限制。

          清單12:可以編譯的變通做法

          private static <T> void testVarargs(T[] args) {      for (int i = 0; i < args.length; i++) {          System.out.println(args[i]);      } }

          8. 重載中的選擇問題

          Java支持“重載”的機制,允許在同一個類擁有許多只有形參列表不同的方法。然后,由編譯器根據調用時的實參來選擇到底要執行哪一個方法。

          傳統上的選擇,基本是依照“特殊者優先”的原則來進行。一個方法的特殊程度,取決于為了讓它順利運行而需要滿足的條件的數目,需要條件越多的越特殊。

          在引入Varargs機制之后,這一原則仍然適用,只是要考慮的問題豐富了一些——傳統上,一個重載方法的各個版本之中,只有形參數量與實參數量正好一致的那些有被進一步考慮的資格。但是Varargs機制引入之后,完全可以出現兩個版本都能匹配,在其它方面也別無二致,只是一個實參個數固定,而一個實參個數可變的情況。

          遇到這種情況時,所用的判定規則是“實參個數固定的版本優先于實參個數可變的版本”。

          清單13:實參個數固定的版本優先

          public class OverloadingSampleA {      public static void main(String[] args) {          testOverloading(1);//打印出A          testOverloading(1, 2);//打印出B          testOverloading(1, 2, 3);//打印出C      }      private static void testOverloading(int i) {          System.out.println("A");      }      private static void testOverloading(int i, int j) {          System.out.println("B");      }      private static void testOverloading(int i, int... more) {          System.out.println("C");      } }

          如果在編譯器看來,同時有多個方法具有相同的優先權,它就會陷入無法就到底調用哪個方法作出一個選擇的狀態。在這樣的時候,它就會產生一個 “reference to 被調用的方法名 is ambiguous”的編譯錯誤,并耐心的等候作了一些修改,足以免除它的迷惑的新源代碼的到來。

          在引入了Varargs機制之后,這種可能導致迷惑的情況,又增加了一些。例如現在可能會有兩個版本都能匹配,在其它方面也如出一轍,而且都是實參個數可變的沖突發生。

          清單14:左右都不是,為難了編譯器

          public class OverloadingSampleB {      public static void main(String[] args) {          testOverloading(1, 2, 3);//編譯出錯      }      private static void testOverloading(Object... args) {      }      private static void testOverloading(Object o, Object... args) {      } }

          另外,因為J2SE 1.5中有“Autoboxing/Auto-Unboxing”機制的存在,所以還可能發生兩個版本都能匹配,而且都是實參個數可變,其它方面也一模一樣,只是一個能接受的實參是基本類型,而另一個能接受的實參是包裹類的沖突發生。

          清單15:Autoboxing/Auto-Unboxing帶來的新問題

          public class OverloadingSampleC {      public static void main(String[] args) {          /* 編譯出錯 */          testOverloading(1, 2);          /* 還是編譯出錯 */          testOverloading(new Integer(1), new Integer(2));      }      private static void testOverloading(int... args) {      }      private static void testOverloading(Integer... args) {      } }

          9. 歸納總結

          和“用數組包裹”的做法相比,真正的實參個數可變的方法,在調用時傳遞參數的操作更為簡單,含義也更為清楚。不過,這一機制也有它自身的局限,并不是一個完美無缺的解決方案。

          posted @ 2010-06-28 17:55 FaithChen 閱讀(1067) | 評論 (0)編輯 收藏


          posts - 4, comments - 0, trackbacks - 0, articles - 4

          Copyright © FaithChen

          主站蜘蛛池模板: 广宁县| 财经| 板桥市| 疏勒县| 鲁甸县| 瑞昌市| 西城区| 绍兴市| 驻马店市| 黄龙县| 涿鹿县| 封丘县| 饶阳县| 迁西县| 凉山| 平陆县| 新晃| 马龙县| 建阳市| 扬中市| 扎赉特旗| 岱山县| 博野县| 通江县| 呼和浩特市| 牙克石市| 渭南市| 东阳市| 察雅县| 丽水市| 蕉岭县| 乌什县| 河南省| 达拉特旗| 宜丰县| 星子县| 汝阳县| 龙海市| 尤溪县| 林西县| 淳安县|