Moso's profile魔战士PhotosBlogLists Tools Help

Blog


    September 17

    Log4j的配置(ZZ)

    log4j使用进阶
    nepalon 原创   更新:2003-12-23 10:50:44  版本: 1.0   
    在公司的第二个项目了,看了这么久的log4j,终于受不了了。。。。

    二 动态配置log4j
    1 配置外部配置文件来配置的基本步骤
    1.1 一个运用配置文件的实例
    Log4j之所以能成功的原因之一是它的灵活性。但如果只是简单的调用BasicConfigurator.configure()来进行配置工作,那么所有的配置都是在函数中写死的,以后修改配置就要修改原代码,这就不能体现出log4j的灵活性了,所以基本上不会通过BasicConfigurator.configure()来进行配置工作的。
    为了增加软件的灵活性,最常用的做法就是使用配置文件,如web.xml之于J2EE,struts-config.xml之于struts一样,log4j也提供了让我们把配置信息从程序转移到配置文件中的方法。Log4j提供了两种方式的配置文件:XML文件和Java的property配置文件。通过把配置信息转移到外部文件中,当我们要修改配置信息时,就可以直接修改配置文件而不用去修改代码了,下面,我们就来完成一个通过配置文件来实现log4j的实例。
    例2-a:
    package TestLog4j;
    import org.apache.log4j.Logger;
    import org.apache.log4j.BasicConfigurator;
    import org.apache.log4j.PropertyConfigurator;
    import org.apache.log4j.Priority; public class TestLog4j 
    {
    static Logger logger = Logger.getLogger(TestLog4j.class.getName());
    public TestLog4j(){}

    public static void main(String[] args)
    {
    //通过BasicConfigurator类来初始化
    //BasicConfigurator.configure();
    //(1)通过配置文件来初始化
    PropertyConfigurator.configure("F:\\nepalon\\log4j.properties");

    logger.debug("Start of the main() in TestLog4j"); //代码(2)
    logger.info("Just testing a log message with priority set to INFO");
    logger.warn("Just testing a log message with priority set to WARN");
    logger.error("Just testing a log message with priority set to ERROR");
    logger.fatal("Just testing a log message with priority set to FATAL");
    logger.log(Priority.WARN, "Testing a log message use a alternate form");
    logger.debug(TestLog4j.class.getName()); //代码(2)
    }
    }
    在这个例子中,我们用PropertyConfigurator.configure("F:\\nepalon\\log4j.properties")代替BasicConfigurator.configure()进行配置。PropertyConfigurator.configure()函数的参数可以是一个properties文件所在路径的String对象,可以是一个properties文件所在路径的URL对象,也可以是一个properties对象。通过PropertyConfigurator.configure()可以通过指定的properties文件来配置信息。如果要用XML文件进行信息配置,可以在代码中调用DOMConfigurator()函数来进行配置工作。在这里,我们只以properties文件来完成例子。接着,我们来看一下log4j.properties文件中都有些什么东西:
    例2-b:
    log4j.rootLogger = DEBUG, A1
    log4j.appender.A1 = org.apache.log4j.ConsoleAppender
    log4j.appender.A1.layout = org.apache.log4j.PatternLayout
    log4j.appender.A1.layout.ConversionPattern = %-4r [%t] %-5p %c %x - %m%n
    运行这个实例,运行结果为
    0 [main] DEBUG TestLog4j.TestLog4j - Start of the main() in TestLog4j
    20 [main] INFO TestLog4j.TestLog4j - Just testing a log message with priority set to INFO
    20 [main] WARN TestLog4j.TestLog4j - Just testing a log message with priority set to WARN
    20 [main] ERROR TestLog4j.TestLog4j - Just testing a log message with priority set to ERROR
    20 [main] FATAL TestLog4j.TestLog4j - Just testing a log message with priority set to FATAL
    180 [main] WARN TestLog4j.TestLog4j - Testing a log message use a alternate form
    180 [main] DEBUG TestLog4j.TestLog4j - TestLog4j.TestLog4j
    下面,我们分析一下这个配置文件。
    1) 由于每一个Logger对旬都有一个级别,文件的第一行就是定义了一个Logger及其级别。在这里定义了一个根记录器(root logger),这涉及到记录器的层次问题,在些暂时不深入讨论,在后面的章节再进行讨论。
    2) 第二行定义了一个名为A1的输出流,这个流就是控制台,所以通过Logger对象打印的信息会在控制台输出。
    3) 第三行定义了打印信息的布局。在这里我们用PatternLayout作为此记录器的布局,PatternLayout允许你以灵活的格式来打印信息。
    4) 第四行指定的打印信息的具体格式,从结果可知,这个实例的打印格式为:当前打印语句所使用的时间 [日志所在的线程] 打印的级别 当前日志所在的类的全名 日志信息。
    现在我们来修改一下这个记录器的级别,把第一行的DEBUG改为INFO,再运行程序,结果将变为:
    0 [main] INFO TestLog4j.TestLog4j - Just testing a log message with priority set to INFO
    10 [main] WARN TestLog4j.TestLog4j - Just testing a log message with priority set to WARN
    10 [main] ERROR TestLog4j.TestLog4j - Just testing a log message with priority set to ERROR
    10 [main] FATAL TestLog4j.TestLog4j - Just testing a log message with priority set to FATAL
    10 [main] WARN TestLog4j.TestLog4j - Testing a log message use a alternate form
    由于这个Logger的级别变为INFO,而代码(2)是调用debug()函数来输出日志信息时只能当记录器级别为DEBUG时才输出信息,所以代码(2)将不输出信息。
    1.2 实例原理
    1.2.1 初始化配置信息
    如果要通过JAVA的properties文件来配置信息,那么在代码中就要通过PropertyConfigurator.configure()函数从properties文件中加载配置信息,这个函数有三种参数形式:一个properties文件所在路径的String对象,可以是一个properties文件所在路径的URL对象,也可以是一个properties对象。如果要用XML文件来配置信息,则可用类型的
    DOMConfigurator()函数来从一个XML文件中加载配置信息。
    1.2.2 输出端Appender
    在上面的例子中,我们都是简单的把日志信息输出到控制台中。其实在log4j中还可以把日志信息输出到其它的输出端,对于同一个日志信息,我们还可以让它同时输出到多个输出端中,如同时在控制台和文件中进行打印。一个输出端就是一个appender。要在配置文件中定义一个appender有三步:
    1) 在定义一个记录器的同时定义出该记录器的输出端appender。在例2的配置文件的第一句log4j.rootLogger = DEBUG, A1中,我们定义了一个根记录器,它的级别为DEBUG,它有一个appender名为A1。定义根记录器的格式为log4j.rootLogger = [ level ], appendName1, appendName2, …appendNameN。同一个记录器可有多个输出端。
    2) 定义appender的输出目的地。定义一个appender的输出目的地的格式为log4j.appender.appenderName = fully.qualified.name.of.appender.class。log4j提供了以下几种常用的输出目的地:
    ? org.apache.log4j.ConsoleAppender,将日志信息输出到控制台
    ? org.apache.log4j.FileAppender,将日志信息输出到一个文件
    ? org.apache.log4j.DailyRollingFileAppender,将日志信息输出到一个,并且每天输出到一个新的日志文件
    ? org.apache.log4j.RollingFileAppender,将日志信息输出到一个文件,通过指定文件的的尺寸,当文件大小到达指定尺寸的时候会自动把文件改名,如名为example.log的文件会改名为example.log.1,同时产生一个新的example.log文件。如果新的文件再次达到指定尺寸,又会自动把文件改名为example.log.2,同时产生一个example.log文件。依此类推,直到example.log. MaxBackupIndex,MaxBackupIndex的值可在配置文件中定义。
    ? org.apache.log4j.WriterAppender,将日志信息以流格式发送到任意指定的地方。
    ? org.apache.log4j.jdbc.JDBCAppender,通过JDBC把日志信息输出到数据库中。
    在例2中,log4j.appender.A1 = org.apache.log4j.ConsoleAppender定义了名为A1的appender的输出目的地为控制台,所以日志信息将输出到控制台。
    3) 定义与所选的输出目的地相关的参数,定义格式为:
    log4j.appender.appenderName.optionName1 = value1
    ……
    log4j.appender.appenderName.optionNameN = valueN
    其中一个最常用的参数layout将在下面介绍。
    1.2.3 输出格式(布局)layout
    通过appender可以控制输出的目的地,而如果要控制输出的格式,就可通过log4j的layout组件来实现。通过配置文件定义一个appender的输出格式,也通常需要两个步骤:
    1) 定义appender的布局模式。定义一个appender的布局模式的格式为log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class。Log4j提供的布局模式有以下几种:
    ? org.apache.log4j.HTMLLayout,以HTML表格形式布局
    ? org.apache.log4j.PatternLayout,可以灵活地指定布局模式
    ? org.apache.log4j.SimpleLayout,包含日志信息的级别和信息字符串
    在例2 中log4j.appender.A1.layout = org.apache.log4j.PatternLayout定义了名为A1的appender的布局模式为PatternLayout。
    2) 定义与所选的布局模式相关的设置信息,定义格式为:
    log4j.appender.appenderName.layout.optionName1 = value1
    ……
    log4j.appender.appenderName.layout.optionNameN = valueN
    选择了不同的布局模式可能会有不同的设置信息。实例2所选的布局模式PatternLayout的一个PatternLayout为ConversionPattern ,通过定义这个PatternLayout的值,我们可以指定输出信息的输出格式。在例2的配置文件中的定义如下log4j.appender.A1.layout.ConversionPattern = %-4r [%t] %-5p %c %x - %m%n。在下面,我们将介绍布局模式PatternLayout的参数ConversionPattern的各个值代表的含义。
    1.2.4 ConversionPattern参数的格式含义
    格式名 含义
    %c 输出日志信息所属的类的全名
    %d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy-MM-dd HH:mm:ss },输出类似:2002-10-18- 22:10:28
    %f 输出日志信息所属的类的类名
    %l 输出日志事件的发生位置,即输出日志信息的语句处于它所在的类的第几行
    %m 输出代码中指定的信息,如log(message)中的message
    %n 输出一个回车换行符,Windows平台为“\r\n”,Unix平台为“\n”
    %p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL。如果是调用debug()输出的,则为DEBUG,依此类推
    %r 输出自应用启动到输出该日志信息所耗费的毫秒数
    %t 输出产生该日志事件的线程名
    1.3 定义多个输出目的地的实例
    从上面的实例原理中我们已经知道,同一个日志信息可以同时输出到多个输出目的地,在这个例子中,我们将实现一个把日志信息同时输出到控制器、一个文件中的实例和数据库中。这个实例的Java代码我们沿用例2中的代码,我们只需修改配置文件即可。这也体现了log4j的灵活性。
    例3-a:
    create table log4j(
    logID int primary key identity,
    message varchar(1024),
    priority varchar(10),
    milliseconds int,
    category varchar(256),
    thread varchar(100),
    NDC varchar(256),
    createDate datetime,
    location varchar(256),
    caller varchar(100),
    method varchar(100),
    filename varchar(100),
    line int
    )
    例3-b:
    #1 定义了两个输出端
    log4j.rootLogger = INFO, A1, A2,A3

    #2 定义A1输出到控制器
    log4j.appender.A1 = org.apache.log4j.ConsoleAppender
    #3 定义A1的布局模式为PatternLayout
    log4j.appender.A1.layout = org.apache.log4j.PatternLayout
    #4 定义A1的输出格式
    log4j.appender.A1.layout.ConversionPattern = %-4r [%t] %-5p %c - %m%n

    #5 定义A2输出到文件
    log4j.appender.A2 = org.apache.log4j.RollingFileAppender
    #6 定义A2要输出到哪一个文件
    log4j.appender.A2.File = F:\\nepalon\\classes\\example3.log
    #7 定义A2的输出文件的最大长度
    log4j.appender.A2.MaxFileSize = 1KB
    #8 定义A2的备份文件数
    log4j.appender.A2.MaxBackupIndex = 3
    #9 定义A2的布局模式为PatternLayout
    log4j.appender.A2.layout = org.apache.log4j.PatternLayout
    #10 定义A2的输出格式
    log4j.appender.A2.layout.ConversionPattern = %d{yyyy-MM-dd hh:mm:ss}:%p %t %c - %m%n

    #11区 定义A3输出到数据库
    log4j.appender.A3 = org.apache.log4j.jdbc.JDBCAppender
    log4j.appender.A3.BufferSize = 40
    log4j.appender.A3.Driver = com.microsoft.jdbc.sqlserver.SQLServerDriver
    log4j.appender.A3.URL = jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=nepalon
    log4j.appender.A3.User = sa
    log4j.appender.A3.Password = 
    log4j.appender.A3.layout = org.apache.log4j.PatternLayout
    log4j.appender.A3.layout.ConversionPattern = INSERT INTO log4j (createDate, thread, priority, category, message) values(getdate(), '%t', '%-5p', '%c', '%m')
    配置文件中的6、7、8行显示了输出端为RollingFileAppender的特有参数及其运用的方法。11区显示了输出端为JDBCAppender的特有参数及其运用方法。在这着重讲解一下6、7、8行的作用。6行指定日志信息输出到哪个文件,7行指定日志文件的最大长度,最后要详细介绍8行。第8行的参数是设置备份文件的个数的参数,在这里我们设置为3,表示最多有3个备份文件,具体作用为:
    1) 当example3.log文件的大小超过K时,就把文件改名为example3.log.1,同时生成一个新的example3.log文件
    2) 当example3.log文件的大小再次超过1K,又把文件改名为example3.log.1。但由于此时example3.log.1已存在,则先把example3.log.1更名为example3.log.2,再把example3.log文件改名为example3.log.1
    3) 同理,当example3.log文件的大小再次超过1K,先把example3.log.2文件更名为example3.log.3,把example3.log.1文件更名为example3.log.2,再把example3.log文件改名为example3.log.1
    4) 当example3.log文件的大小再次超过1K,先把example3.log.2文件更名为example3.log.3,旧的example3.log.3文件将被覆盖;把example3.log.1文件更名为example3.log.2,旧的example3.log.2文件被覆盖;最后把example3.log文件改名为example3.log.1并覆盖掉旧的example3.log.1文件。
    运行结果将分为两部分
    在控制器中:
    0 [main] INFO TestLog4j.TestLog4j - Just testing a log message with priority set to INFO
    11 [main] WARN TestLog4j.TestLog4j - Just testing a log message with priority set to WARN
    21 [main] ERROR TestLog4j.TestLog4j - Just testing a log message with priority set to ERROR 21 [main] FATAL TestLog4j.TestLog4j - Just testing a log message with priority set to FATAL
    21 [main] WARN TestLog4j.TestLog4j - Testing a log message use a alternate form
    在文件example3.log中:
    2003-12-18 04:23:02:INFO main TestLog4j.TestLog4j - Just testing a log message with priority set to INFO
    2003-12-18 04:23:02:WARN main TestLog4j.TestLog4j - Just testing a log message with priority set to WARN
    2003-12-18 04:23:02:ERROR main TestLog4j.TestLog4j - Just testing a log message with priority set to ERROR
    2003-12-18 04:23:02:FATAL main TestLog4j.TestLog4j - Just testing a log message with priority set to FATAL
    2003-12-18 04:23:02:WARN main TestLog4j.TestLog4j - Testing a log message use a alternate form
    1.4 配置log4j的总结
    这个教程到这里,关于配置log4j的配置文件的基本原理已经讲完了,而且通过例3我们已经可以完成基本的日志工作了。现在,我们就做一个总结。配置一个配置文件的基本步骤如下:
    1) 定义一个Logger。在定义Logger时指定该Logger的级别级其输出目的地。定义Logger的格式为
    log4j.rootLogger = [ level ], appendName1, appendName2, …appendNameN。
    2) 定义appender的输出目的地。定义一个appender的输出目的地的格式为
    log4j.appender.appenderName = fully.qualified.name.of.appender.class。
    log4j提供的输出端有ConsoleAppender、FileAppender 、DailyRollingFileAppender、RollingFileAppender和WriterAppender。
    3) 定义appender的除布局模式外的其它相关参数,如例3中第6、7、8定义了A2的相关参数。定义格式为
    log4j.appender.appenderName.optionName1 = value1
    ……
    log4j.appender.appenderName.optionNameN = valueN
    如果除了布局模式外不需要定义别的参数,可跳过这一步(如例3中的A1)。
    4) 定义appender的布局模式。定义一个appender的布局模式的格式为
    log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class。
    布局模式其实也是步骤3)中的一个部分,只是布局模式参数是每一个appender必须定义的参数。Log4j提供的布局模式有HTMLLayout、PatternLayout和SimpleLayout。
    5) 定义与所选的布局模式相关的设置信息,定义格式为
    og4j.appender.appenderName.layout.optionName1 = value1
    ……
    log4j.appender.appenderName.layout.optionNameN = valueN
    2 记录器的层次Logger hierarchy
    2.1 何为记录器的层次hierarchy
    首先,我们先看一下何为层次,以我们最熟悉的继承为例,下面是一张类图

    在这个继承体系中,类B是类C的父类,类A是类C的祖先类,类D是类C的子类。这些类之间就构成一种层次关系。在这些具有层次关系的类中,子类都可继承它的父类的特征,如类B的对象能调用类A中的非private实例变量和函数;而类C由于继承自类B,所以类B的对象可以同时调用类A和类B中的非private实例变量和函数。
    在log4j中,处于不同层次中的Logger也具有象类这样的继承关系。
    2.2 记录器的层次
    如果一个应用中包含了上千个类,那么也几乎需要上千个Logger实例。如何对这上千个Logger实例进行方便地配置,就是一个很重要的问题。Log4J采用了一种树状的继承层次巧妙地解决了这个问题。在Log4J中Logger是具有层次关系的。它有一个共同的根,位于最上层,其它Logger遵循类似包的层次。下面我们将进行介绍。
    2.2.1 根记录器root logger
    就象一个Java中的Object类一样,log4j中的logger层次中有一个称之为根记录器的记录器,其它所有的记录器都继承自这个根记录器。根记录器有两个特征:
    1) 根记录器总是存在。就像Java中的Object类一样,因为用log4j输出日志信息是通过记录器来实现的,所以只要你应用了log4j,根记录器就肯定存在的。
    2) 根记录器没有名称,所以不能通过名称来取得根记录器。但在Logger类中提供了getRootLogger()的方法来取得根记录器。
    2.2.2 记录器的层次
    Logger遵循类似包的层次。如
    static Logger rootLog = Logger.getRootLogger();
    static Logger log1 = Logger.getLogger("test4j");
    static Logger log2 = Logger.getLogger("test4j.test4j2");
    static Logger log3 = Logger.getLogger("test4j.test4j2.test4j2");
    那么rootLog是log2的祖先子记录器,log1是log2的父子记录器,log3是log2的子记录器。记录器象Java中的类继承一样,子记录器可以继承父记录器的设置信息,也可以可以覆写相应的信息。
    首先先看一下记录器层次中的继承有什么用处。假设程序中的每个包都具有一些基本的日志信息,而包中的不同包可能会有些额外的日志信息要输出,这种情况就可以象处理Java中的类一样,运用Logger中的层次关系来达到目的。假设有个名为A的包,我包下的所有类都要把日志信息输出到控制台中;A.B包除了输出到控制台外还要输出到文件中;A.C包除了输出到控制台中还要输出到HTML文档中。这样我们就可以通过定义一个父记录器A,它负责把日志信息输出到控制台中;定义一个A的子记录器A.B,它负责把日志信息输出到文件中;定义一个A的子记录器A.C,它负责把日志信息输出到HTML文档中。
    记录器遵循的是类似包的层次,这样做为我们带来了大大的方便。Logger类中的getLogger()方法可以取得Logger对象,这个方法有三种参数形式String、Class和URL,其实不论是用哪一种,最终都是通过记录器的名字来取得记录器对象的。如果要取得一个名为A.B的记录器对象,我们可以Logger.getLogger(“A.B”)。但从上面的例子中,我们都是通过Logger.getLogger(TestLog4j.class.getName())这种方法来取得记录器对象。这是为什么呢?现在我们假设A.B的包下有一个类BClass,那么我们调用BClass.class.getName()得到的是这个类的全名A.B.BClass。所以当调用Logger.getLogger(BClass.class.getName())时,最理想的情况是返回名为A.B.BClass的记录器对象。但是如果不存在名为A.B.BClass的记录器时它会怎样呢?其实通过Logger类的getLogger方法取得记录器时存在下面两种情况:
    1) 如果存在与所要找的名字完全相同的记录器,则返回相应的记录器对象。
    当调用Logger.getLogger(BClass.class.getName())时,如果定义了名为A.B.BClass的记录器,它就返回该记录器的对象。
    2) 但如果找不到,它会尝试返回在记录器层次中与所要找的记录器最接近的记录器对象。
    当调用Logger.getLogger(BClass.class.getName())时,如果没有定义了名为A.B.BClass的记录器,那会尝试返回名为A.B的记录器的对象;如果又没有定义名为A.B的记录器,它会尝试返回名为A的记录器的对象;如果也没定义名为A的记录器,它就会返回根记录器的对象,而根记录器是必须存在的,所以你总能得到一个记录器对象。
    好了,现在我们回到前面的问题,我们为什么总要通过Logger.getLogger(BClass.class.getName())这种以类全名作为参数来取得记录器对象呢?其实这是为了管理方便。因为我们在定义设计Logger时也遵循类似包的规则,使设计器的名称与程序中的类包对应。如上面的假设中我们的程序中有A包,A包下有B包和C包,B包下又有类BClass,那么我们就可使设计器的名为A、A.B、A.C、A.B.BClass,以此类推。那么当我们通过类命名来取得设计器对象时,总能取到与所要的设计器最接近的设计器对象。
    2.3 如何应用记录器的层次
    2.3.1 如果定义及获取不同层次的记录器
    任何一个记录器的使用都有两个步骤:
    1) 在配置文件中定义相应的记录器。
    在配置文件中定义记录器的格式有两种
    ? 定义根记录器的格式为
    log4j.rootLogger = [ level ], appendName1, appendName2, …appendNameN
    ? 定义一个非根记录器的格式为
    log4j.logger.loggerName1 = [ level ], appendName1,…appendNameN
    ……
    log4j.logger.loggerNameM = [ level ], appendName1, …appendNameN
    我们可以定义任意个非根记录器。
    2) 在代码中调用Logger类的取得记录器方法取得相应的记录器对象。
    要取得根记录器对象可通过Logger.getRootLogger()函数,要取得非根记录器可通过Logger.getLogger()函数。
    理论知道就讲到这里,纸上得来终觉浅,下面,我们来小小演练一下。
    例4-a:
    package TestLog4j;
    import org.apache.log4j.Logger;
    import org.apache.log4j.PropertyConfigurator;
    import org.apache.log4j.Priority;
    import TestLog4j.TestLog4j2.TestLog4j2;

    public class TestLog4j 
    {
    static Logger logger = Logger.getLogger(TestLog4j.class.getName()); //(2)
    public TestLog4j(){}

    public static void main(String[] args)
    {
    //同时输出到控制台和一个文件的实例并实现了Logger的继承
    PropertyConfigurator.configure("F:\\nepalon\\log4j2.properties");

    logger.debug("Start of the main() in TestLog4j");
    logger.info("Just testing a log message with priority set to INFO");
    logger.warn("Just testing a log message with priority set to WARN");
    logger.error("Just testing a log message with priority set to ERROR");
    logger.fatal("Just testing a log message with priority set to FATAL");
    logger.log(Priority.WARN, "Testing a log message use a alternate form");
    logger.debug(TestLog4j.class.getName());
    TestLog4j2 testLog4j2 = new TestLog4j2(); //(1)
    testLog4j2.testLog();
    }
    }
    在类TestLog4j中我们调用了另一个类TestLog4j2,下面看一下类TestLog4j2的代码。
    例4-b:
    package TestLog4j.TestLog4j2;
    import org.apache.log4j.Logger;
    import org.apache.log4j.PropertyConfigurator;
    import org.apache.log4j.Priority;

    public class TestLog4j2 
    {
    static Logger logger = Logger.getLogger(TestLog4j2.class.getName()); //(1)
    public TestLog4j2(){}

    public void testLog()
    {
    //同时输出到控制台和一个文件的实例
    PropertyConfigurator.configure("F:\\nepalon\\log4j2.properties");
    logger.debug("2Start of the main()");
    logger.info("2Just testing a log message with priority set to INFO");
    logger.warn("2Just testing a log message with priority set to WARN");
    logger.error("2Just testing a log message with priority set to ERROR");
    logger.fatal("2Just testing a log message with priority set to FATAL");
    logger.log(Priority.DEBUG, "Testing a log message use a alternate form");
    logger.debug("2End of the main()");
    }
    }
    最后我们来看一下配置文件。
    例4-c:
    log4j2.properties文件内容
    #1区
    #### Use two appenders, one to log to console, another to log to a file
    log4j.rootLogger = debug, stdout

    #2区
    #Print only messages of priority WARN or higher for your category
    log4j.logger.TestLog4j= , R
    log4j.logger.TestLog4j.TestLog4j2=WARN

    #3区
    #### First appender writes to console
    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

    #4区
    #### Second appender writes to a file
    log4j.appender.R=org.apache.log4j.RollingFileAppender
    log4j.appender.R.File=F:\\nepalon\\classes\\TestLog4j\\example.log

    # Control the maximum log file size
    log4j.appender.R.MaxFileSize=100KB
    # Archive log files (one backup file here)
    log4j.appender.R.MaxBackupIndex=1

    log4j.appender.R.layout=org.apache.log4j.PatternLayout
    log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd hh:mm:ss}:%p %t %c - %m%n
    先看一下运行结果。
    在控制台中的结果为:
    DEBUG [main] (?:?) - Start of the main() in TestLog4j
    INFO [main] (?:?) - Just testing a log message with priority set to INFO
    WARN [main] (?:?) - Just testing a log message with priority set to WARN
    ERROR [main] (?:?) - Just testing a log message with priority set to ERROR
    FATAL [main] (?:?) - Just testing a log message with priority set to FATAL
    WARN [main] (?:?) - Testing a log message use a alternate form
    DEBUG [main] (?:?) - TestLog4j.TestLog4j
    WARN [main] (?:?) - 2Just testing a log message with priority set to WARN
    ERROR [main] (?:?) - 2Just testing a log message with priority set to ERROR
    FATAL [main] (?:?) - 2Just testing a log message with priority set to FATAL
    输出文件的结果为:
    2003-12-19 04:19:44:DEBUG main TestLog4j.TestLog4j - Start of the main() in TestLog4j
    2003-12-19 04:19:44:INFO main TestLog4j.TestLog4j - Just testing a log message with priority set to INFO
    2003-12-19 04:19:44:WARN main TestLog4j.TestLog4j - Just testing a log message with priority set to WARN
    2003-12-19 04:19:44:ERROR main TestLog4j.TestLog4j - Just testing a log message with priority set to ERROR
    2003-12-19 04:19:44:FATAL main TestLog4j.TestLog4j - Just testing a log message with priority set to FATAL
    2003-12-19 04:19:44:WARN main TestLog4j.TestLog4j - Testing a log message use a alternate form
    2003-12-19 04:19:44:DEBUG main TestLog4j.TestLog4j - TestLog4j.TestLog4j
    2003-12-19 04:19:44:WARN main TestLog4j.TestLog4j2.TestLog4j2 - 2Just testing a log message with priority set to WARN
    2003-12-19 04:19:44:ERROR main TestLog4j.TestLog4j2.TestLog4j2 - 2Just testing a log message with priority set to ERROR
    2003-12-19 04:19:44:FATAL main TestLog4j.TestLog4j2.TestLog4j2 - 2Just testing a log message with priority set to FATAL

    首先,先来看一下配置文件都有些什么东西。
    1) 在1区中定义了一个根记录器。这个根记录器具有DEBUG级别并有一个名称为stdout的输出端appender。
    2) 2区中的内容是这一节的重点,也是应用到记录器层次的地方,但其实也只有两句,充分体现了log4j的简单性。在这里,我们定义了两个名称分别为TestLog4j和TestLog4j.TestLog4j2设计器。
    ? 在定义TestLog4j记录器时没有指定级别,所以它的级别继承自它的父记录器,即要记录器,所以它的级别也为DEBUG。在定义TestLog4j记录器时又定义了一个名称为R的输出端,所以它的输出端有两个,一个从根记录器继承而来的名为stdout的输出端,另一个为在此定义的名为R的输出端。在此需要注意的是,在定义记录器时必须先定义记录器的级别,然后才是记录器的输出端。如果只想定义输出端而不定义级别,则虽然级别可以为空,但逗号分隔符不能省略。如定义TestLog4j记录器的做法。
    ? 在定义TestLog4j.TestLog4j2记录器时又指定了它的级别,由于一个记录器的级别只能有一个,所以新指定的级别将覆写掉它的父记录器的级别(这就象Java中的多态)。我们没有定义TestLog4j.TestLog4j2记录器的输出端,所以它的输出端将从它的父记录器中继承而来。它的父记录器为estLog4j记录器,所以它和estLog4j记录器一样具有两个名称分别为stdout和R的输出端。
    3) 剩下的3区和4区分别设置了两个输出端的参数值。
    接下来,回到我们的代码,看一下是如何取得记录器,在取记录器时又发生了什么。
    1) 例4-a中的代码(2)中,语句Logger.getLogger()中的参数TestLog4j.class.getName()的值为TestLog4j. TestLog4j,所以此语句的结果是取得一个名为TestLog4j. TestLog4j的记录器的对象。但在配置文件中并没有定义这样的记录器,所以最终将返回与所需的名称TestLog4j. TestLog4j最接近的记录器对象,即名为TestLog4j的记录器的对象。
    2) 例4-b中的代码(1)的原理与例4-a中的代码(2)相似,期望取得的是名为TestLog4j.TestLog4j2. TestLog4j2的记录器对象,但最终返回的是TestLog4j.TestLog4j2记录器的对象。
    三 log4j与J2EE的结合
    到目前为止,这篇文章讲的都是如何在application中应用log4j,而Java现在的应用主流是J2EE和J2ME。现在,我们来看一下要如何在J2EE开发中应用log4j。其实在Web application中应用log4j也很简单,与在application中应用log4j不同之处就是要在所有应用log4j的代码之前对log4j进行初始化。所以,我们在web application中就要把log4j的初始化工作独立出来,把它放在Servlet中。下面,我们看一个例子。
    例5-a:
    进行初始化的Servlet:
    import org.apache.log4j.PropertyConfigurator;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    /**
    * log4j.jar的初始化类,参考web.xml
    */
    public class Log4jInit extends HttpServlet 
    {
    public void init() 
    {
    //通过web.xml来动态取得配置文件
    String prefix = getServletContext().getRealPath("/");
    String file = getInitParameter("log4j-init-file");

    // 如果没有给出相应的配置文件,则不进行初始化
    if(file != null) 
    {
    PropertyConfigurator.configure(prefix+file); //(1)
    }
    }

    public void doGet(HttpServletRequest req, HttpServletResponse res) 
    {} 
    }
    下面来看一下这个Servlet在web.xml中的定义。
    例5-b:
    <servlet>
    <servlet-name>log4j-init</servlet-name>
    <servlet-class>TestLog4j.Log4jInit</servlet-class>
    <init-param>
    <param-name>log4j-init-file</param-name>
    <param-value>sort.properties</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    </servlet>
    因为log4j的初始化要在所有的log4j调用之前完成,所以在web.xml文件中,我们一定要把对应的Servlet定义的load-on-startup应设为1,以便在Web容器启动时即装入该Servlet。
    完成了这两个步骤这后,我们就可以象在application开发中一样在web application任何地方应用log4j了。下面是在javabean中的应用的一个例子。
    例5-c:
    import org.apache.log4j.Logger;

    public class InfoForm 
    {
    static Logger logger = Logger.getLogger(InfoForm.class);

    protected String title;
    protected String content;

    public InfoForm() {}

    public void setTitle(Object value) 
    {
    logger.debug("nepalon:title = " + title);
    title = value;
    }

    public String getTitle() 
    {
    logger.debug("nepalon:title = " + title);
    return title;
    }

    public void setContent(String value) 
    {
    content = value;
    logger.debug("nepalon: content() = " + content);
    }

    public String getContent() 
    {
    logger.debug("nepalon: content = \n" + content);
    return content;
    }
    }
    September 14

    没有假期的月圆日(某师妹对奶粉事件的看法)(zz)

    初闻三鹿奶粉事件,真的有点震惊,因为一直以来,我将三鹿集团看做国内奶业的领军企业
    ,认为国内就算其它品牌再不济,三鹿都始终会在前面牵引着这个行业向前发展,但真的没
    有想到最先出事,事件最大的就是这个心目中的“老大”,不禁有点难过或者怀疑。再后者
    ,了解了三鹿奶
    粉出事的源头在于三聚氰胺,又不禁心一阵抽搐,因为职业的关系,自己对于三聚氰胺的了
    解相信都超过周围的人。还很清楚地记得去年五月因为饲料中三聚氰胺事件,造成中国饲料
    业出口受阻,但真的没有想到过会添加到与生命息息相关的奶制品行业中,不管企业自行添
    加也好,或是奶
    源供应链收到污染也好,中国奶制业在全国以致全球的信誉肯定受到莫大的影响的,中国制
    造这个四个字又会进入贬值风潮了,对于一个爱国的国人来说,不禁觉得有点悲哀。
    奶粉三聚氰胺事件来的实在突然紧急,我们都没有作好充分的准备,虽然事先有点预感。昨
    天上午接到领导通知,中午会抽检大批各种品牌奶粉,下午必须得出结果。于是,牺牲午休
    时间摸方法,找条件,以最大的速度建立检测方法。样品进入仪器的每一刻,大家都异常紧
    张,包括领导。
    如果企业自行添加,那么很难保证同行业不会亦步亦趋;如果是奶源收到污染,那也更难保
    证其它品牌的产品的清白。自认为不喜欢加班的我在这个时刻真的放下了对加班的厌恶或者
    抱怨,只是也真的很不希望看到三聚氰胺在其它品牌奶粉中的超标现象,只是,很可惜,在
    最后的一刻,通
    过各种方式确认了另一国内品牌奶粉中三聚氰胺的超标现象严重。接着,国家局,省局不断
    去市场,厂家抽各种不同批次的奶粉样回来,我们只能留守阵地,在大家都享受假期的时刻
    ,我们在单位通宵达旦地运转自己,只求一个结果而已。也很可惜,我们发现国内各种品牌
    都几乎受到了不
    同程度的污染,有些浓度搞得我们只能相信这是企业自己添加的。这样的结果更突然,也更
    震惊,于是,推理到了液态奶是否也会受到污染的问题。今天,又有一批同事坚守在单位,
    又是通宵地对各种液态奶尽兴检测,有同事告诉我,有一一直有不错口碑的酸奶也受到了较
    为严重的污染。
    中秋佳节,月圆人圆,本是家人团聚的时光,然而,我心却非常沉重,或许,这样的结果是
    我们接受不了的。国内几乎所有的奶粉都受到不同程度的污染,甚至液态奶,企业为了追逐
    利益,减少成本,添加了不同程度的三聚氰胺,造成蛋白含量虚高,这是有可能的;而奶农
    为了降低成本,
    追求利益最大化,添加高浓度的三聚氰胺,造成整个奶制品供应链都受到污染,这也是有可
    能的;奶制品供应链过程中,中间供应者添加,也是有可能的;任何一个环节,都是有可能
    的。领域如此之广,真正要找出添加的元凶,困难程度可想而之,那么,依照国内惯例,或
    许,最后奶粉事
    件随着时间的推移不了了之;又或许,相关部门敷衍了事,随便找个代替的羔羊交代而已。
    今年月圆之日,我心注定无法随月而圆了。
    想想,本来应该是万物以人为尊,人的生命应该受到尊敬以及珍惜。不知道是因为中国人口
    太多,还是因为中国老百姓太过绵羊式的善良,总感觉老百姓的命运是掌握在他人手中的。
    试想,城市对于这些突发事件,应急机制或许比较完善,市民可以在第一时间作出拒绝或者
    反抗;而对于九
    亿乃至十亿的乡村百姓来说,他们能够对这些作出选择吗?
        又想想,对于流通领域的任关系民生的任何产品,国家相关部门监管应该是非常严格的
    ,而往往现实中却存在不少缺陷,令每个国民都在惶惶中度过每一天,对身边的人或者事或
    物都充满了怀疑,这些,相关部门是不是应该好好反省反省呢?
        大胆地畅想一下,何时国产两字能够开心地融入到国民乃至全世界人们的生活中呢?

    --
     ╲█◣           
     █◥◣           
     ◥◥      ◢█╱ 
        ◢█╱ ◢◢█ 
        ◢◢█   ◤◤ 
          ◤◤                                        


     
    September 11

    java String的split

    java.lang.String的split太恶了
    String str = "a:::";
    String[] ret = str.split(":")结果是 ret[0]="a"
    String str = "a:::b"
    String[] ret = str.split(":")结果是 ret[0]="a", ret[1]="", ret[2]="", ret[3]="b". 

    被国人误解了千年的七句话<ZZ>


      1.以德报怨
      
      2.民可使由之,不可使知之。
      
      3.无毒不丈夫
      
      4.唯女子与小人难养也
      
      5.吾生也有涯,而知也无涯。
      
      6.相濡以沫
      
      7.天地不仁,以万物为刍狗。
      
      1、以德抱怨   
         原句:“或曰:‘以德报怨,何如?’子曰:“何以报德?以直报怨,以德报德” ————《论语 宪问》
        
         以德抱怨,是我们常听到的一句话了,人们通常理解的“以德抱怨”什么意思呢?就是说:孔老夫子教我们,别人欺负你了,你要忍,被打碎牙齿也要往肚子里吞,别人来欺负你,你反而应该对他更好,要用你的爱心去感化他,用你的胸怀去感动他。这就让人感觉很有点肃然了。想想看,如果我一巴掌呼过去那个被打的人还笑笑说打得好啊,你越打我我就要对你越好……遇到这种情况我也会感动到傻眼,这多好一孩子,多伟大的情操啊……
        
         但事实上,我们根本曲解了孔子的原意,我当初,也万万没想到原来在孔子这句“以德抱怨”的后边还跟着另外一段话,什么话呢?子曰:“以德抱怨,何以报德?以直报怨,以德报德!”看完以后,幡然醒悟,原来我们都被某个断章取义的孔子FANS给玩了一把!当时的真实情况是怎么样的呢?孔子的一个弟子问他说:师傅,别人打我了,我不打他,我反而要对他好,用我的道德和教养羞死他,让他悔悟,好不好?孔子就说了,你以德抱怨,那“何以报德?”别人以德来待你的时候,你才需要以德来回报别人。可是现在别人打了你,你就应该“以直抱怨”,拿起板砖飞他!看!就因为被人故意省略了一句话,刚烈如火的孔老夫子一下就被扭曲成了现在这个温婉的受气包形象。
        
         与西方文化相比,东方文明一向被认为是谦逊坚忍的,同样是被人欺负,西方的带头大哥耶稣在圣经里就叫嚣要:“以眼还眼,以牙还牙。”小弟们!别人瞪了你一眼,你就要瞪回去,别人咬了你一口,你就要咬回来!而东方呢?佛家的精神领袖释迦牟尼说“我不入地狱,谁入地狱?”一副逆来顺受的样子。而且他可不是说说而已的,大鹰来欺负他,要吃他的肉,他也真从自己身上一刀刀帮大鹰割下肉来,这就伟大得有点近乎BT了。说完了国外的几位大领导,再回到中国,孔老宗师的这句话为什么会被别人有意地曲解呢?根源还是当时封建统治者的需要,他们的心思,无外乎就是要信仰孔子的万千民众成为“以德抱怨”的顺民,只有“以德抱怨”的民众,才会老老实实地服从他们的剥削和压迫。我们再来看看影响吧,孔老先生这句被曲解了的“以德抱怨”,在中国文化史上起的压迫作用,可真是大到了天上。皇帝残暴,我们要“以德抱怨”,地主剥削,我们要“以德抱怨”,八国联军都打到北京了,还是要“以德抱怨”,要卑躬屈膝,要割地称臣,要想尽一切办法彰现自己的“德”,要“量中华之物力,结大国之欢心”……就是没想过反抗。试想,如果中国的文化里,没有这种把正确的思想东篡西改来为封建统治阶级服务的恶习,如果孔子这句原话没有被刻意地曲解成这样子,我们中国人会养成这样一种懦弱的思维惯性吗?有人说西方人的骨子里本性是狼性,而东方人的骨子里的本性是羊性,这里如月影倒想问问了,是什么原因让我们变成了这样?如果我们从古以来信奉的是西方那种“以眼还眼,以牙还牙”的训诫,近代史上的中国,会给世界留下那么一个任人鱼肉的印象吗?
      
      2、民可使由之,不可使知之。
        
         原句:。“子曰:兴于诗,立于礼,成于乐。子曰:民可使由之,不可使知之。”——————《论语.秦伯》
        
         又是孔老先生的话,小小景仰一下下先。
        
         民可使由之,不可使知之,这句话什么意思呢?是说,国家统治人民,指使驱赶他们去做事就行了,不要让他们明白他们在做什么。这句话在现在看来,绝对很明显就是封建统治阶级几千年来一直在玩弄的愚民权术,小老百姓嘛,让他们知道那么多干什么?最好都是昏昏噩噩,只知道照着我们的意思去庸庸碌碌一辈子,这句训诫不可谓不恶毒,它被千百年来中国的大小封建统治者奉为至宝,抹杀了多少真理与人民的创造性,但同时,我们的问题就来了,这样的一条愚民之术,真是孔子这位致力于教化人民的教育家的本意吗?
      我们知道,在春秋时代的文章是没有标点符号的,后人要研读那时侯的文章,便要再经过一个“句断”的过程,即是根据上下文意思自己在句子的适当地方加上标点和停顿,这样才能得出一句句意通顺而连贯的话。在这里插一句,孔子的文章为什么被后人引用得非常多呢?这其一当然是孔子本身的名望使然,其二,则是因为他的语录涵盖的范围非常广,几乎包括了社会生活的方方面面,这样一来,后来的人无论是谁,抱着什么目的,都可以从孔子的话中断章取义地引用其中的相关部分,再加以自己个人倾向的理解和句断,用来证明自己的观点。比方说现在这句“民可使由之不可使知之”,从前文来看,我们孔老先生前边一直在讨论诗礼乐这三种东东的教育问题,可怎么后边一下子就变成去教帝王权术的训诫了呢?原来,这又是后人别有用心地断章取义,刻意在句子的中间用一个不恰当的“句断” 使这句话产生了歧义的缘故。我们结合上下文的语境,很容易就能得出这句话正确的分句方法:“子曰:兴于诗,立于礼,成于乐。民可,使由之,不可,使知之。”孔子的整句话就是说,诗、礼、乐这三样东西是教育民众的基础,一定要抓好,如果人民掌握了诗礼乐,好,让他们自由发挥,如果人民还玩不来这些东东,我们就要去教化他们,让他们知道和明白这些东西。”你看,这才是“有教无类”的大教育家孔老先生的本意嘛。好好的一句话,硬是被那些别有用心的混蛋生生地坳成了孔子要愚弄人民,要阻挠人民知书答理寻求知识的愚民之术。真不知道那些曲解这话的SB怎么想的,你要说这种P话是教育家孔子说出来的,谁信啊?都把人民当白痴来忽悠了?
        
         有人说古时候的中国人民是不幸的,即便是在物质丰富的盛世,他们的精神世界也贫乏得可怜,到了中近代,西方的思想领域渐渐迎来了文艺复兴,迎来了民主共和,迎来了一个又一个推动世界的发明进步与思想启蒙,而聪明的中华民族呢?在更早的时候他们就有了伟大的四大发明,走在了世界的前边,可为什么就是这么好的基础,到了近代我们反而远远于西方这些小字辈的民族?民可使由之,不可使知之,多恶毒的训诫啊!它象一条张开血盆大口的大蛇,狠狠地缠在中华民族的身上,泯灭了多少先进的思维火花,束缚了多少应有所作为的苗子。但反过来看看,故意曲解这句话的封建统治者固然可恨,但我们能完全把责任都推到他们身上吗?这本身就是多么可笑的一件事情,我们怎么就没想想,一个伟大的教育家,一个用一生置身于教化天下民众的孔子,又怎么会说出“民不可使知之”的蠢话来?更可笑的是,这句话我们竟然就这样相信了几千年膜拜了几千年,这最后该伤心的,是孔老先生呢?还是我们呢?
      
      3、无毒不丈夫
        
         原句:量小非君子,无度不丈夫。——————民间谚语联对
        
         量小非君子,无毒不丈夫,这句话绝对是中国众多以讹传讹的话中最搞笑的一个例子之一,就算是刚完成九年义务教育的初中生,也能轻易看出这其中的不妥之处,首先,这无毒不丈夫,就跟我们认识的古人崇尚的价值观念大大背离了,大丈夫,自然是说那些坦坦荡荡胸怀宽广的男人,什么时候恶毒阴损,暗箭伤人这种前缀也能放在前边来形容大丈夫了?
        
         原来,这句来自民间的谚语本来应该是“量小非君子,无度不丈夫”,这本来是个很好的句子,里边充分运用了对仗。显示出了一份阳刚有力的气魄,一个胸怀坦荡的男人形象就跃然于纸上,可惜劳动人民口耳相传的这一句话,到了朝廷上那些所谓的学高八斗的“君子”嘴里就变了个味。为什么呢?这要从古时候文人的习性说起,在这副对联式的谚语里,“度”为仄声字,犯了孤平,念着别扭,很容易读为平声字“毒”,那些对音律美感要求甚高的学者们某天吃饱了没事儿干,便发挥他们的专长自做主张,把这句改为“无毒不丈夫”了,于是这句话,终于成了典型的“信言不美,美言不信”的例句,成了迂腐文人的笔下的又一个牺牲品,“量小非君子,无度不丈夫”,原话里一个君子对一个丈夫,一个度对另一个量,本来是很完美的一个句子,可经过上千年的以讹传讹,竟成了“无毒不丈夫”这句现在我们挂在嘴边的口头禅。
        
         这句话的整个演变过程,我宁可相信是无意的,否则也太缺德了,这不故意往咱男人身上抹黑么?但就是这无意的一个讹传,却也给我们带来了些说大不大,说小不小的影响,我们都知道心理学上有个说法叫先入为主,潜移默化。无毒不丈夫,我们天天在耳濡目染这个被篡改了的男人形象之际,心里的价值观人生观,难免也会受到些不良影响,天天看着电视里奸人怂恿主角干坏事的时候都用这句:“干吧!无毒不丈夫!”然后主角想想也对,于是五指并拢手掌上举,狠狠地做一个切的动作:“无毒不丈夫,干!”你能说同样作为一个男人,这东西看久了你的思维能不收到一丁点儿的影响么?关于这种语言的影响力量,我再举个例子,比如隔壁顶撞了我一下,这时候我朋友张三来了:用的是原版的好话劝我:“老话说,量小非君子,无度不丈夫,大如啊你是个男人,就别跟他一般计较了。” 张三这样说,我这个大丈夫当然再拉不下面子去计较这种小事,可如果另一种情况,张三来和我这么说:“这老话怎么说的?无毒不丈夫!大如,你是男人,不能怂啊!给你刀,去做了他!” 看看,我听到他这话,除了接过刀速度冲出去乱砍以证明自己是个男人之外,还真没什么好办法,你说对不对?
      
      4、唯女子与小人难养也。
      
        
         原句:唯女子与小人难养也,近之则不孙,远之则怨。——————《论语.阳货》
        
         现今女权主义抬头,孔老夫子的这一句话便立时变成了女权精英批判者们群起而攻之的绝好靶子,看看,这就是你们中国男人歧视咱女性的最初起源,孔夫子这老混蛋啊!害我们女性被压迫了几千年啊几千年,好不容易翻身做主人哪,我们要顶上半边天再抢他们的半边~~天!一时间,群情激愤,凶狠异常,热闹非凡。但各位姐姐们阿姨们姑奶奶们,且听如月影说两句话,你们把这罪过都推到孔老人家身上,却是大大地冤枉了他了。
        
         先根据当年老孔的处境,提出一个疑问:“孔老先生当时,为什么要说出“唯女子与小人难养也”这样的话?任何人说话都不会是无缘无故的,当然,除了疯子,而我们的孔老先生应该不是疯子吧?而且这位孔老先生受《诗经》的影响很深,他说:“诗三百,一言以蔽之,思无邪”,他认为《诗经》是一部伟大的著作,而我们再来看看这部孔子都很推崇的巨著,里边倒有很大一部分歌颂了女子的活泼美丽,大方善良,歌颂了当时男女平等的浪漫爱情氛围,事实也是这样,在春秋时代,男女间是相当平等的,而孔夫子本人更是曾反复多次以诗经里的“妻子好合,如鼓瑟琴”来表达了自己对婚姻和女子的平等看待观点。所以,说孔子歧视妇女,不仅和孔子的思想不符,更与当时的民间社会整个大环境对不上号,因此这个说法,实在是大大地有待商榷。
      所以我们就要看看,孔子当初是在什么一种环境下说出 “唯女子与小人难养也”这句话的?这话又是对谁说的?他一宗师级别的身份,不可能在路边想着想着突然心情烦躁就开骂一八杆子打不着的妇女同志吧?在《史记 .孔子世家》里,提到了孔子之前的卫国之行,孔子“居卫月余,灵公与夫人同车,宦者雍渠参乘出,使孔子为次乘,招摇市过之。孔子曰:‘吾未见好德如好色者也。’于是丑之,去卫。”我来大致翻译一下这段话,当代全国教育劳模孔老先生受卫国国君的邀请,来到了卫国参观学习休养,但在这期间,孔老先生突然发现自己被涮了,人家根本是拿他的身份来炫耀自己抬高自己而已,并不是真正支持他来这教化卫国民众的,尤其是那个卫灵公的老婆,为了抬高自己的身望,公开炫耀,贬低了孔子,孔老先生那个郁闷啊,你卫灵公到底是喜欢德才多些,还是喜欢女色多些?在你心里我和你老婆哪个重要?干醋自己吃完,依然没人鸟他,孔子怒了,说:“吾未见好德如好色者也!丢!此处不留爷,自有留爷处!GO,GO,GO!”收拾行李就离开了卫国,离开之后,心情平复了,想起卫国公老婆那种仗着得宠,骄横跋扈乱政扰民的烂事儿,就发了感慨:“唯女子与小人难养也!近之则不孙,远之则怨。”
        
         你看,知道了这些历史背景和人生经历,孔子这话就很好理解了。我们知道,他骂的人是那种“被养”的女人和小人,女子还好说,可想想什么人才能养小人?君主啊!再看看孔子的卫国之行,一切都明白了,他这话断不是发神经突然开骂起包括自己老妈在内的所有女人,而是有一个特指的对象,这个对象,就是卫灵公那位老婆南子,就是那些“近之则不孙,远之则怨”的宫廷女权,近之则不孙,远之则怨,这后一句话怎么解释呢?就是说你作为一个君主,对那些后宫的女人和没什么本事的拍马小人太亲近了,她们就会得意忘形,忘了自己的身份,开始用你的权力胡做非为,而你疏远她们吧,她们又要埋怨,总之是非常麻烦。此前没见过什么宫廷里女人之间的斗争的孔子说出这话是可以理解的,他的担心也不是没有道理,孔子之前,妲己误国,烽火戏诸侯,这些事例还少吗?孔子之后,宦官小人当道,皇后外戚乱政的桥段更是屡屡上演,不可枚数。所以我们的女性同胞们,你们要清楚,孔子当时没发神经把你们全天下的女人不分青红皂白地乱骂一通,别再憋屈他老人家了~~
        
         最后一个问题,那么是谁误传了这句话,让我们可爱的女性同胞被压迫了几千年之久呢?俗话说冤有头,债有主,如月影要帮孔老夫子平反,自然也要再帮你们找到一个真正的罪魁祸首,让你们发泄出那几千年积下的怨恨(怎么说着说着象鬼片了-_-!),好了,拉回正题,西汉的时候有一位所谓的“大儒”董仲舒,此人大力主张“夫为妻纲”,就是这个家伙,借孔子的一句话断章取义,给广大的中华妇女头上戴上了千年的枷锁。看看这家伙在《基义》一书里提出的“三纲”: “君臣父子夫妻之义,皆与诸阴阳之道。君为阳,臣为阴;父为阳,子为阴;夫为阳,妻为阴。王道之三纲,可求于天。”他这东西就是一为讨好历任统治者的马屁之作。“君为臣纲”,为历代皇帝所接受,渐渐形成了封建统治的一套准则,而“夫为妻纲”,则为历代男人所推崇。这“纲”是什么意思?君为臣纲,则君要臣死,臣不得不死!知道这个“纲”的厉害了吧?你想想,各位苦命的女子没嫁人前都一可人的窈窕淑女,本来身份平等,嫁了人,就得时刻提心吊胆地按着老公的这个“纲”过日子,这三纲五常,束缚了中国多少年,害了多少代人啊!
        
      5、吾生也有涯,而知也无涯。
        
         原句:吾生也有涯,而知也无涯,以有涯随无涯,殆已 ——————《庄子.养生主》
      这句话是庄子说的,八卦一下,庄子此人,实在是如月影非常喜欢的一个人物,他的思想和性情,都非常十分很特别对我胃口,什么大家不爱听这些?那算了,本来想抖点猛料让大家深入了解一下我这个好同志的……好了好了别扔鸡蛋,我说正事还不行吗?好,先来看看,“吾生也有涯,而知也无涯”,这句话我们在什么地方见得最多呢?第一,是图书馆,第二,是全国各个小学的后墙上,按照现在人们的普遍理解,这句话实在是学习励志类的不二明言警句,生也有涯,知也无涯,多崇高的目标啊,这比什么孔老夫子的“学而时习之不亦乐乎”都来得有气势多了,古往今来,不知道多少学子在这句话的激励下凿壁偷光闻鸡起舞奋发图强追求上进读那些永远也读不完的书,动力啊,偶像啊,庄子七老八十还能说出这么振奋人心的话,咱年轻的小辈正当青春,怎么能输给他老人家?不狠狠地读书怎么对得起他老人家?
        
         但真相说出来,往往会让人哭笑不得,庄子的这句话其实是这样说的,“吾生也有涯,而知也无涯,以有涯随无涯,殆已。”我庄子的生命是有限的,但我面对的知识是无限的,要我以本来有限的生命,去追求那种永远看不到边的尽头,你当我是SB吗?这样会搞死自己的捏~~庄子是一个追崇顺其自然,清净无为的洒脱人物,他认为人吃个饭,饱了就行,没什么必要追求奢华,穿个衣,别冻着就行,实在不需与什么人攀比。同样,学问也是如此,学够了就行,学以致用,学问学来就是为了用,你一个人再牛B,还能全部用到这世间所有的学问?既然不能用到,那你老学老学把自己整个生命都拿来学这是干啥呢?时间都被你拿来学习了,还怎么有时间把学到的东西拿来用呢?这不就跟吃饭是为了炫耀穿衣是为了攀比的人一样,完全本末倒置了吗?所以他老人家就在《养生主》一书中给后世陷入这个怪圈的书呆子们提了个醒,你们这样苦学盲学乱学通学,对身体不好滴,会搞死自己滴~~
        
         当然,庄子的这句被人误解的话,其实并没有以上那些例子引起的反面作用那么大,读书是必须的,掌握一定的知识也是必须的,但咱们要记得自己把握好,有个度。你就算用半辈子武装得自己学富五车,却发现很多知识你根本用不上,这不浪费了么?庄子从来都不认为一个人能比自然更大,这是他所有的话中唯一的主题思想,实际上,就算我们拿现在的世界观来衡量一部《庄子》,仍能发现他的学说里,有许多是符合辨证唯物主义思想的。可叹的是,就是这么一位崇尚自然的思想家,他一句劝解读书人适可而止循序渐进的话却被一些叫嚷着“人定胜天”的人当成了激励年轻人拼命读书的名言来供着,科举改成了高考,八股改成了应试教育,于是大学生一年比一年多,可素质却一年比一年低下,最后整黄了吧?你看现在大学文凭都和废纸没什么两样了,这帮人还不醒悟,还是继续读,读完大学读硕士,读完硕士读博士,读完博士读博士后……读完出来一看,嘿,好象我已经老了捏,很多知识也用不着捏,这不自己跟自己过不去么?最后再说一句私人的牢骚话,最近我去图书馆,总看见一帮老头老太太天天在那侯着,一见有啥免费的学习班就往里钻,也根本不管别人老师是教着什么,各位老爷爷老奶奶们,听我这个后辈说句话,您们这不瞎捣蛋么?赶时髦啊?摆个性哪?想让我们称赞下您啊?还是真打算活到老学到老啊?自个儿细细思量一下有什么意义么?能不能腾些地方给些真正需要的人进去?M的连“量子力学基础浅谈”都有街道的老大妈在里边占着位置,我真是无语了……
      
      6、相濡以沫
        
         原句:相濡以沫,不如相忘于江湖——————《庄子.大宗师》
        
         说点轻松点的吧。
        
         沿着伟大的思想家庄老爷子的足迹,我们再来看看另外一条咱们耳熟能详的名句:“相濡以沫”。
        
         说起这句话,其实大有来头,我想大家都听过现在流行的一个词吧?江湖,这个“江湖”是从哪儿出来的呢?很多人以为最初是古龙小说里的一句“人在江湖,身不由己”,其实不然,江湖这个词最早的出处,便是在庄子说“相濡以沫”的这句话中。在《大宗师》篇中,庄子给我们讲了这么一个小故事:“泉涸,鱼相与处于陆,相呴以湿,相濡以沫,不如相忘于江湖。”他说的是有一天,一眼泉水干了,两条小鱼被困在了一个小水洼,为了生存下去,它们彼此从嘴中吐出泡泡,用自己的湿气来湿润对方的身体,互相扶持,互相依赖。但,与其在死亡边缘才这样互相扶持,还不如大家找到一条水路,开开心心地回到广阔的江河湖海,回到各自的天地,彼此相忘,自由自在。
        
         我们口口声声说的“江湖”就是这么来的,没想到吧?做下小广告哈,欢迎观看点评在下拙作:小说《书江湖》,不是什么特别牛B的书,主角比佐罗牛B一点而已,现正联系出版或许不日上市,先搜索来看一下也可以~~
        
         恩,恩,回来,相濡以沫,多美的情景,虽然这并不是爱情最理想的状态。当年街道居委会的老大妈就算再没文化,这句话肯定是会说的,在我们的父辈那个婚姻靠组织介绍的年代,这句话支撑了多少性格各异,却本没有多少感情基础的夫妻一直走到了今天,相濡以沫,多好的一个榜样啊,只是当年没曾想,只是当年不知道,既然需要相濡以沫才能支撑,为什么却不干脆洒脱地放手,把自己,也把对方放开去找各自的幸福,去找各自的江湖呢?
        
         相濡以沫,这句话的全部意思,又有多少人知道呢?
        
         我有个大学的同学,是个陕西的女孩子,她在上学时找了个陕西的男朋友,她把自己的一切都给了他,当然包括了身体,可是那个懒惰的男朋友对她很不好,常打她骂她象佣人一样地指使她,却又根本离不开她,我们惊奇的是,她竟然一直默默忍受了下来,一忍就是四年,我们忍不住问她,你们过得这么辛苦这么难受,为什么还要在一起呢?她却这样回答:“嫁鸡随鸡,嫁狗随狗,虽然在一起不是很快乐,但这不就是相濡以沫吗?”
        
         我无言,这能算是相濡以沫吗?为什么明明外边有更大的江湖,更宽广的天地,为什么明明知道知道自己有更好的未来,却要把双方都绑在一起,不开心地继续过下去呢?我们都身处一个传统思维占据非常强势地位的社会,一句相濡以沫,不知道让多少爱情做了这种传统思维的牺牲品。其实想一想,如果自己都不开心,别人的称赞又能怎么样?如果自己能找到属于自己的真正天地,别人的指责又能怎么样?唉,说大了,有点没边没际,父母也教育过我劝合不劝分的道理,这话题也到此为止吧,劝合不劝分,相濡以沫啊,原来我也不能免俗。
        
         只是叹息一声,我们都没曾想到,除了一句相濡以沫,原来后边,还可以相忘于江湖。
      
      
      7:天地不仁,以万物为刍狗
        
         原句:天地不仁,以万物为刍狗,圣人不仁,以百姓为刍狗——————《道德经》
        
         要说起这句话,大半的愤青都会告诉你,这话的意思是说:“天地残暴不仁,把万物都当成低贱的猪狗来看待,而那些高高在上的所谓圣人们也没两样,还不是把我们老百姓也当成猪狗不如的东西!”标准的无政府主义宣言,标准的对社会不满。这句话在各种愤青的文章中,在当前下三流的网络YY小说里那是随处可见,主角通常是弱小无助,饱受压迫和不公,于是有一天突然醒悟,大喊一声“天地不仁,以万物为刍狗!”立时或悟道飞升,或肉身成佛,或从一个人人可以欺负的小瘪三变成一方霸主,等等等等,不一而足……
        
         基本上,这些孩子的思维都可以总结为一种抑郁太久的被压迫感大爆发,就好象连老天都欠了他们个十万八万似的。可是呢?另一方面这帮无政府主义的家伙又没胆子喊出什么“王侯将相宁有种乎”之类更牛B的话,更不敢扯出个大旗子大叫 “苍天已死黄天当立!”最后,只好喊一喊这句“天地不仁,以万物为刍狗”,以表达一种受到不公平待遇的憋屈。说实话,其实我挺同情这些人的,无政府主义没什么了不起,一个人在学生时代没有经历过无政府主义的阶段,基本上可以被认为没有青春,我也经历过那种思想特别冲动觉得这世界上谁都跟自己过不去的时代,不同的是现在我知道了怨天尤人一点用都没有,社会是不公平,社会就是这么吊不公平,如果你觉得社会不公平,你只有自己努力去改变这一切。
        
         但就算我非常理解他们的青春年少,我还是有必要得跟这些后辈们说一句,你们这句话,其实恰好用错了。
        
         天地不仁,以万物为刍狗,这根本不是你们从字面上理解的,说天地残暴不仁,把世间万物都看成猪狗,圣人不仁,以百姓为猪狗,也根本不是你们想的那样说所谓高高在上的统治者们,把百姓看得低微卑贱。其实这句话的真正意思是说,天地不情感用事,对万物一视同仁,圣人不情感用事,对百姓一视同仁。你们推崇的这句话,它的原意中根本没有你们所需要的压迫与歧视,却恰恰相反,它说的是一个公平的道理,我们所有的人,所有的众生在天地的眼中,都是平等的,为什么现在你啃着干面包,别人却吃着海鲜大餐?孩子,在大叫不公平之前,为什么你不想想你是否和他流过一样的汗水?如果想不通,也行,那就别想了,为什么你不尝试通过自己的努力,来稍微扭转一下这样的“不公”?只是大叫着不公平大叫着没天理大叫着等着天上掉个馅饼下来补偿你的委屈你的遗憾你正损失着的一去不复返的青春年华,有用吗?

    September 04

    接口的意义(ZZ)

         大多数人认为,接口的意义在于顶替多重继承。众所周知Java没有c++那样多重继承的机制,但是却能够实作多个接口。其实这样做是很牵强的,接口和继承是完全不同的东西,接口没有能力代替多重继承,也没有这个义务。接口的作用,一言以蔽之,就是标志类的类别(type of class)。把不同类型的类归于不同的接口,可以更好的管理他们。OO的精髓,我以为,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如c++、java、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。(cowboy的名言是 “抽象就是抽去像的部分”,看似调侃,实乃至理)。

        设计模式中最基础的是工厂模式(Factory),在我最近的一个很简单的应用中,我想尽量的让我的程序能够在多个数据库间移植,当然,这涉及很多问题,单是如何兼容不同DBMS的SQL就让人头痛。我们不妨先把问题简单化,只考虑如何连接不同的数据库。

        假设我有很多个类,分别是Mysql.java、SQLServer.java、Oracle.java、DB2.java,他们分别连接不同的数据库,统一返回一个Connection对象,并且都有一个close方法,用于关闭连接。只需要针对你的DBMS,选择不同的类,就可以用了,但是我的用户他会使用什么数据库?我不知道,我希望的是尽量少的修改代码,就能满足他的需要。我可以抽象如下接口:

        package org.bromon.test;
        public interface DB
        {
        java.sql.Connection openDB(String url,String user,String password);
        void close();
        }

        这个接口只定义两个方法,没有任何有实际意义的代码,具体的代码由实作这个接口的类来给出,比如Mysql.java:

        Package org.bromon.test;
        import java.sql.*;
        public class Mysql implements DB
        {
        private String url=”jdbc:mysql:localhost:3306/test”;
        private String user=”root”;
        private String password=””;
        private Connection conn;
        public Connection openDB(url,user,password)

        {
        //连接数据库的代码
        }
        public void close()

        {
        //关闭数据库
        }
        }
        类似的当然还有Oracle.java等等,接口DB给这些类归了个类,在应用程序中我们这样定义对象:

        org.bromon.test.DB myDB;
        使用myDB来操作数据库,就可以不用管实际上我所使用的是哪个类,这就是所谓的“开-闭”原则。但是问题在于接口是不能实例化的,myDB=new DB(),这样的代码是绝对错误的,我们只能myDB=new Mysql()或者myDB=new Oracle()。麻烦了,我还是需要指定具体实例化的是哪个类,用了接口跟没用一样。所以我们需要一个工厂:

        package org.bromon.test;
        public class DBFactory
        {
        public static DB Connection getConn()

        {
        Return(new Mysql());
        }
        }

        所以实例化的代码变成:myDB=DBFactory.getConn();

        这就是23种模式中最基础的普通工厂(Factory),工厂类负责具体实例化哪个类,而其他的程序逻辑都是针对DB这个接口进行操作,这就是“针对接口编程”。责任都被推卸给工厂类了,当然你也可以继续定义工厂接口,继续把责任上抛,这就演变成抽象工厂(Abstract Factory)。

        整个过程中接口不负责任何具体操作,其他的程序要连接数据库的话,只需要构造一个DB对象就OK,而不管工厂类如何变化。这就是接口的意义----抽象。

        继承的概念不用多说,很好理解。为什么要继承呢?因为你想重用代码?这绝对不是理由,继承的意义也在于抽象,而不是代码重用。如果对象A有一个run() 方法,对象B也想有这个方法,所以有人就Class B extends A。这是不经大脑的做法。如果在B中实例化一个A,调用A的Run()方法,是不是可以达到同样的目的?如下:

        Class B
        {
        A a=new A();
        a.run();
        }
        这就是利用类的聚合来重用代码,是委派模式的雏形,是GoF一贯倡导的做法。

        那么继承的意义何在?其实这是历史原因造成的,最开始的OO语言只有继承,没有接口,所以只能以继承来实现抽象,请一定注意,继承的本意在于抽象,而非代码重用(虽然继承也有这个作用),这是很多Java烂书最严重的错误之一,它们所造成的阴影,我至今还没有完全摆脱,坏书害人啊,尤其是入门类的,流毒太大。什么时候应该使用继承?只在抽象类中使用,其他情况下尽量不使用。抽象类也是不能实例化的,它仅仅提供一个模版而已,这就很能说明问题。

        软件开发的万恶之源,一是重复代码而不是重用代码,二是烂用继承,尤以c++程序员为甚。Java中取缔多重继承,目的就是制止烂用继承,实是非常明智的做法,不过很多人都不理解。Java能够更好的体现设计,这是让我入迷的原因之一。