月度归档:2011年08月

设计模式 —— Bridge(桥接模式)

类的继承使得子类具有基类的属性和方法,从而在很大程度上实现了代码重用。但是,继承关系会导致子类和基类之间建立了强耦合关系,当基类改变时,子类必须也跟着改变。桥接模式中,软件的抽象化和实现化使用了组合关系。相对于继承关系,组合关系是一种弱耦合关系,因此桥接模式可以让类的抽象化和实现化相对独立变化。GoF对桥接模式意图的定义就是:将抽象部分和它的实现部分分离,使它们可以独立地变化[1]

桥接模式的结构图如下所示:

其中,Abstract是系统抽象化部分的接口,它维护着一个实现化部分的接口Implementor的引用,Implementor接口与Abstract接口可以相同也可以不同,一般而言,Implementor声明一些较底层的接口,而Abstract声明的接口处于较高层次,要通过调用Implementor提供的接口来实现。RefinedAbstraction类扩展Abstract接口,提供Abstract接口的具体实现。ConcreteImplementorA和ConcreteImplementorB实现了Implementor接口。

网上关于桥接模式最多的例子就是设计日志系统。假设某个日志系统待记录的日志种类包含业务日志、异常日志、外部接口调用日志,而日志的存储形式可能是数据库、文本文件、XML文件。这样,这个日志系统就有两个维度的变化,一个维度是日志种类,另一个维度是日志存储形式。如果采用继承关系设计这个日志系统中的必须设计实现3*3共9个类,而且当一个维度增加一项功能时,需要增加另一个维度长度个数(本例中是3个)的类。当采用桥接模式时,因为采用了组合关系,所以只需设计3+3共6个类,任一维度新增一项功能,只需新增一个类即可。

本例中将日志种类看成桥接模式中的抽象化部分,日志的存储形式看成实现化部分。代码如下:
1、抽象化部分接口类(结构图中的Abstract类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * Alipay.com Inc.
 * Copyright (c) 2004-2011 All Rights Reserved.
 */
package com.alipay.bridge;
 
/**
 * 
 * @author changjian.feng
 * @version $Id: Log.java, v 0.1 2011-8-28 下午10:24:18
 */
public abstract class Log {
    protected LogStoreMethod logStoreMethod;
 
    Log(LogStoreMethod logStoreMethod) {
        this.logStoreMethod = logStoreMethod;
    }
 
    /**
     * 客户调用接口
     */
    public abstract void makeLog();
}

2、实现化部分接口类(结构图中的Implementor类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 * Alipay.com Inc.
 * Copyright (c) 2004-2011 All Rights Reserved.
 */
package com.alipay.bridge;
 
/**
 * 
 * @author changjian.feng
 * @version $Id: LogStoreMethod.java, v 0.1 2011-8-28 下午10:27:06
 */
public abstract class LogStoreMethod {
    public abstract void store();
}

3、存储介质为数据库时的实现类(结构图中的ConcreteImplementor类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * Alipay.com Inc.
 * Copyright (c) 2004-2011 All Rights Reserved.
 */
package com.alipay.bridge;
 
/**
 * 
 * @author changjian.feng
 * @version $Id: DbStoreMethod.java, v 0.1 2011-8-28 下午10:28:40
 */
public class DbLogStoreMethod extends LogStoreMethod {
 
    /** 
     * @see com.alipay.bridge.LogStoreMethod#store()
     */
    @Override
    public void store() {
        System.out.println("Store via Database");
    }
}

4、存储介质为文本文件时的实现类(结构图中的ConcreteImplementor类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * Alipay.com Inc.
 * Copyright (c) 2004-2011 All Rights Reserved.
 */
package com.alipay.bridge;
 
/**
 * 
 * @author changjian.feng
 * @version $Id: FileStoreMethod.java, v 0.1 2011-8-28 下午10:30:38
 */
public class FileLogStoreMethod extends LogStoreMethod {
 
    /** 
     * @see com.alipay.bridge.LogStoreMethod#store()
     */
    @Override
    public void store() {
        System.out.println("Store via File");
    }
}

5、存储介质为XML文件时的实现类(结构图中的ConcreteImplementor类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * Alipay.com Inc.
 * Copyright (c) 2004-2011 All Rights Reserved.
 */
package com.alipay.bridge;
 
/**
 * 
 * @author changjian.feng
 * @version $Id: XMLStoreMethod.java, v 0.1 2011-8-28 下午10:29:31
 */
public class XMLLogStoreMethod extends LogStoreMethod {
 
    /** 
     * @see com.alipay.bridge.LogStoreMethod#store()
     */
    @Override
    public void store() {
        System.out.println("Store via XML");
    }
}

6、业务日志实现类(结构图中的RefinedAbstraction类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
 * Alipay.com Inc.
 * Copyright (c) 2004-2011 All Rights Reserved.
 */
package com.alipay.bridge;
 
/**
 * 
 * @author changjian.feng
 * @version $Id: BizLog.java, v 0.1 2011-8-28 下午10:41:37
 */
public class BizLog extends Log {
    BizLog(LogStoreMethod logStoreMethod) {
        super(logStoreMethod);
    }
 
    /** 
     * @see com.alipay.bridge.Log#makeLog()
     */
    @Override
    public void makeLog() {
        System.out.print("make Biz Log...");
        this.logStoreMethod.store();
    }
}

7、外部接口调用日志实现类(结构图中的RefinedAbstraction类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
 * Alipay.com Inc.
 * Copyright (c) 2004-2011 All Rights Reserved.
 */
package com.alipay.bridge;
 
/**
 * 
 * @author changjian.feng
 * @version $Id: ServiceLog.java, v 0.1 2011-8-28 下午10:43:30
 */
public class ServiceLog extends Log {
    ServiceLog(LogStoreMethod logStoreMethod) {
        super(logStoreMethod);
    }
 
    /** 
     * @see com.alipay.bridge.Log#makeLog()
     */
    @Override
    public void makeLog() {
        System.out.print("make Service Log...");
        this.logStoreMethod.store();
    }
}

8、异常日志实现类(结构图中的RefinedAbstraction类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
 * Alipay.com Inc.
 * Copyright (c) 2004-2011 All Rights Reserved.
 */
package com.alipay.bridge;
 
/**
 * 
 * @author changjian.feng
 * @version $Id: ExceptionLog.java, v 0.1 2011-8-28 下午10:38:12
 */
public class ExceptionLog extends Log {
    ExceptionLog(LogStoreMethod logStoreMethod) {
        super(logStoreMethod);
    }
 
    /** 
     * @see com.alipay.bridge.Log#makeLog()
     */
    @Override
    public void makeLog() {
        System.out.print("make Exception Log...");
        this.logStoreMethod.store();
    }
}

9、客户调用代码(结构图中的Client类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
 * Alipay.com Inc.
 * Copyright (c) 2004-2011 All Rights Reserved.
 */
package com.alipay.bridge;
 
/**
 * 
 * @author changjian.feng
 * @version $Id: Client.java, v 0.1 2011-8-28 下午10:44:46
 */
public class Client {
 
    /**
     * 
     * @param args
     */
    public static void main(String[] args) {
        /**
         * Exception Log
         */
        Log exXMLLog = new ExceptionLog(new XMLLogStoreMethod());
        exXMLLog.makeLog();
 
        Log exDBLog = new ExceptionLog(new DbLogStoreMethod());
        exDBLog.makeLog();
 
        Log exFileLog = new ExceptionLog(new FileLogStoreMethod());
        exFileLog.makeLog();
 
        /**
         * Biz Log
         */
        Log bizXMLLog = new BizLog(new XMLLogStoreMethod());
        bizXMLLog.makeLog();
 
        Log bizDBLog = new BizLog(new DbLogStoreMethod());
        bizDBLog.makeLog();
 
        Log bizFileLog = new BizLog(new FileLogStoreMethod());
        bizFileLog.makeLog();
 
        /**
         * Service Log
         */
        Log serviceXMLLog = new ServiceLog(new XMLLogStoreMethod());
        serviceXMLLog.makeLog();
 
        Log serviceDBLog = new ServiceLog(new DbLogStoreMethod());
        serviceDBLog.makeLog();
 
        Log serviceFileLog = new ServiceLog(new FileLogStoreMethod());
        serviceFileLog.makeLog();
 
    }
}

Client类的执行结果如下:

1
2
3
4
5
6
7
8
9
make Exception Log...Store via XML
make Exception Log...Store via Database
make Exception Log...Store via File
make Biz Log...Store via XML
make Biz Log...Store via Database
make Biz Log...Store via File
make Service Log...Store via XML
make Service Log...Store via Database
make Service Log...Store via File

桥接模式(Bridge)拥有着非常好的扩展性。假如我们想扩展这个日志系统,比如增加一种日志存储形式为控制台输出,那么只要新增一个扩展了LogStoreMethod抽象类的ConsoleLogStoreMethod类,再在业务日志、异常日志和外部接口调用日志等要输出到控制台的地方将ConsoleLogStoreMethod类地实例传到它们的构造方法里即可。反之,如果增加一种日志种类为错误日志,那么也只要新增一个扩展Log抽象类的ErrorLog类,这个错误日志要采用哪种存储方式,只需把改存储方式类的实例当成参数传给构造方法就可以了。

Reference:
[1] GoF. 设计模式:可复用面向对象软件的基础[M], 机械工业出版社, 2006-07. 
[2] 赵春霞,宫明明. 桥接模式在日志系统中的应用[J], 青岛职业技术学院学报, 2010.2, 23(1):57-59.

--EOF--

当你敲入一个网址时,实际上发生了什么?

这篇文章来自于微软Parallel Computing Platform小组Igor Ostrovsky的个人博客,原文题目为What really happens when you navigate to a URL,文章详细描述了当我们在浏览器中敲入一个网址之后,到浏览器呈现网站页面的完整过程。网上已有网友将它翻译成中文,我觉得这篇文章挺经典的,于是也拿来翻译一下,权当学习加复习一下这一整个过程,英文水平较烂,翻译以意译为主。以下就是正文内容了:

当你敲入一个网址时,实际上发生了什么?
作为一个软件开发者,我们必须清楚地知道一个web程序是如何运行的以及它运行过程中所包含的一系列技术:浏览器、HTTP协议、HTML、web服务器、请求处理器(Request Handler)等等。本文将会对用户输入URL之后发生的这一系列过程做一个更加深入的解析。

1、用户输入URL
用户输入一个网址facebook.com,像这样:

2、浏览器为这个域名查找IP地址

浏览器会为用户输入的域名查找IP地址,DNS的查找过程如下:

  • 浏览器缓存:浏览器有时候会缓存一些DNS记录,因为操作系统没有规定浏览器缓存中DNS记录的生存时间,因此不同的浏览器会设定不同的DNS记录缓存时间,一般为2-30分钟。
  • 操作系统缓存:操作系统自身有一个DNS记录缓存。当浏览器缓存中没有需要的DNS记录时,浏览器会调用一个系统调用(例如Windows中的gethostbyname()),向操作系统缓存请求DNS记录。
  • 路由器缓存:浏览器的查找请求被发到路由器里,一般路由器也有自己的DNS记录缓存。
  • ISP的DNS缓存服务器:当路由器缓存里没有想要的DNS记录时,请求被发到ISP的DNS服务器上,查询该服务器中的DNS记录缓存区。
  • 递归查找:当ISP的DNS服务器上都没有待查的记录时,该服务器就会开始递归查找,从根域名服务器开始,到.com这样的顶级域名服务器,再到Facebook的域名服务器。一般ISP的DNS服务器都缓存着像.com这样的顶级域名服务器地址,因此,递归查找不一定从根域名服务器开始,而直接会从.com域名服务器开始。
  • 下图就是ISP的DNS服务器递归查找www.wikipedia.org的过程:

    3、浏览器向Web服务器发送HTTP请求

    像Facebook首页这样的动态网页站点很少会从浏览器缓存中得到,因为它们的生存周期被设置得很短,会很快过期。所以,浏览器需重新向Facebook服务器发送HTTP请求:

    1
    2
    3
    4
    5
    6
    7
    
    GET http://facebook.com/ HTTP/1.1
    Accept: application/x-ms-application, image/jpeg, application/xaml+xml, [...]
    User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; [...]
    Accept-Encoding: gzip, deflate
    Connection: Keep-Alive
    Host: facebook.com
    Cookie: datr=1265876274-[...]; locale=en_US; lsd=WW[...]; c_user=2101[...]

    其中,GET命令指定了请求的URL地址:"http://facebook.com/",浏览器会把自身的信息填充到User-Agent头里,同时声明自己可以接受的应答信息类型(Accept和Accept-Encoding头)。Connection头向服务器请求为这次请求保持TCP连接,为接下的请求节省资源。

    请求信息里还包含着浏览器为这个域名创建的cookie。cookie就是一些键-值对的组合,它记录着浏览器向这个网站请求页面时的状态信息,它包含的信息包括用户登录名、密码和一些用户自定义的配置信息等。cookie存储在客户端的文本文件里,并且在每次客户端发出请求时发送到服务器端。

    4、Facebook服务器以一个重定向响应请求

    以下是Facebook服务器响应浏览器请求的信息:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    HTTP/1.1 301 Moved Permanently
    Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0,
          pre-check=0
    Expires: Sat, 01 Jan 2000 00:00:00 GMT
    Location: http://www.facebook.com/
    P3P: CP="DSP LAW"
    Pragma: no-cache
    Set-Cookie: made_write_conn=deleted; expires=Thu, 12-Feb-2009 05:09:50 GMT;
          path=/; domain=.facebook.com; httponly
    Content-Type: text/html; charset=utf-8
    X-Cnection: close
    Date: Fri, 12 Feb 2010 05:09:51 GMT
    Content-Length: 0

    服务器发出一个响应码301 Moved Permanently,告诉浏览器访问"http://www.facebook.com",替换当前请求中的"http://facebook.com"。

    服务器之所以要发给客户端一个重定向响应码,让客户端重新请求,而不是直接返回网站的首页给用户。这里面有两点原因:1、影响搜索引擎排名。当两个URL指向同一个页面时,例如,http://www.igoro.com和http://igoro.com,搜索引擎会把它们当成两个不同的网站,这样就分散了同一个页面的链接数,影响排名。但是,搜索引擎能够理解永久重定向响应码(301),这样就能把不同URL的相同页面统计到一个排名里。2、相同内容URL不同的同一个页面不是cache友好的。当一份内容有多个名称时,它在cache中实际上存在着多份拷贝,每个名称一份。

    5、浏览器根据重定向信息重新发送请求

    现在浏览器已经知道http://www.facebook.com才是正确的请求页面,所以它重新向服务器发送一个GET请求:

    1
    2
    3
    4
    5
    6
    7
    8
    
    GET http://www.facebook.com/ HTTP/1.1
    Accept: application/x-ms-application, image/jpeg, application/xaml+xml, [...]
    Accept-Language: en-US
    User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; [...]
    Accept-Encoding: gzip, deflate
    Connection: Keep-Alive
    Cookie: lsd=XW[...]; c_user=21[...]; x-referer=[...]
    Host: www.facebook.com

    上述请求报文的各字段含义与第一次请求相同。

    6、服务器处理请求

    服务器会接收那个GET请求,处理完请求之后再返回一个响应。这看起来好像是个很简单的流程但实际上这个过程包含了很多事情,即便是我的博客这样简单的站点也是如此,更别说Facebook那样的大规模可扩展站点了。

  • Web服务器软件:Web服务器软件(例如:IIS或Apache)接收HTTP请求并且决定哪种请求处理器去处理这个请求。请求处理器是一个能读取请求并且生成一个HTML响应的程序,这种程序可以是ASP.NET、PHP、Ruby……一个关于请求处理器最简单的例子是,将URL结构映射到文件目录结构。因此,http://example.com/folder1/page1.aspx直接对应着文件/httpdocs/folder1/page1.aspx。还可以通过配置Web服务器软件实现URL手动映射到请求处理器,使得page1.aspx的公开URL是http://example.com/folder1/page1。
  • 请求处理器:请求处理器可以获得请求、请求参数和cookie。它还可以读取或者更新服务器上的数据,最后,请求处理器会生成一个HTML的响应。
  • 每个动态网站都会碰到的难题是如何存储数据。小网站通常是用一个SQL数据库存储所有数据,但是对于那些存储大量数据或者拥有高访问量的站点,他们必须找到一种方法能够将数据库分散在不同的机器上,以满足需求。这类解决方案包括sharding技术(根据主键值将一张表分散存储在不同库中)、Replication技术或利用弱语义一致性简化数据库。

    另外,一种保证能廉价更新数据的技术是将一些可以推迟的工作放到批处理时完成。例如,Facebook的“你可能认识的人”功能的数据存储可能会在每天晚上更新(作者猜测)。批处理适用于一些不重要的数据更新,但是它可以确保更快速和简单地更新数据。

    7、服务器返回HTML响应

    以下是服务器生成并返回给浏览器的响应报文。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    HTTP/1.1 200 OK
    Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0,
        pre-check=0
    Expires: Sat, 01 Jan 2000 00:00:00 GMT
    P3P: CP="DSP LAW"
    Pragma: no-cache
    Content-Encoding: gzip
    Content-Type: text/html; charset=utf-8
    X-Cnection: close
    Transfer-Encoding: chunked
    Date: Fri, 12 Feb 2010 09:05:55 GMT
     
    2b3��������T�n�@����[...]

    整个响应报文为36kB,我省略了响应体结尾的blob类型数据。
    Content-Encoding头告诉浏览器响应体是用gzip算法压缩的,在这个blob数据被解压缩后,我们就可以看到如下的HTML:

    1
    2
    3
    4
    5
    6
    7
    8
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"   
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
          lang="en" id="facebook" class=" no_js">
    <head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <meta http-equiv="Content-language" content="en" />
    ...

    除了压缩信息之外,响应头还规定了是否以及如何缓存页面、设置cookie信息(上面的响应里没有涉及)、隐私信息等等。

    注意响应头中把Content-Type设置为text/html,它告诉浏览器用HTML将响应体内容展现出来,而不是将它当成一个文件进行下载。浏览器会根据这个字段对响应体进行解析,但是它还会考虑其他因素,比如URL中的扩展名部分。

    8、浏览器开始展现HTML
    在浏览器接到整个HTML文档之前,它就先开始将网页展现出来。

    9、浏览器向服务器请求内嵌在HTML里的对象

    当浏览器展现HTML时,它会注意到一些包含待获取资源URL的标签,浏览器会根据这些标签中的URL发送GET请求去获得这些所需的文件。

    以下是一些我访问facebook.com时获得的URL:

  • 图像

    1
    2
    3
    
    http://static.ak.fbcdn.net/rsrc.php/z12E0/hash/8q2anwu7.gif
    http://static.ak.fbcdn.net/rsrc.php/zBS5C/hash/7hwy7at6.gif
    …
  • CSS样式表

    1
    2
    3
    
    http://static.ak.fbcdn.net/rsrc.php/z448Z/hash/2plh8s4n.css
    http://static.ak.fbcdn.net/rsrc.php/zANE1/hash/cvtutcee.css
    …
  • JavaScript文件

    1
    2
    3
    
    http://static.ak.fbcdn.net/rsrc.php/zEMOA/hash/c8yzb6ub.js
    http://static.ak.fbcdn.net/rsrc.php/z6R9L/hash/cq2lgbs8.js
    …
  • 这里枚举的每个URL都与前面获取当前页面HTML的流程类似,因此,浏览器会重复之前的所有操作,包括去DNS查询IP,发送请求,根据重定向信息重新请求等等。

    然而,与动态页面不同的是,静态页面允许浏览器缓存它们。因此,有些静态页面可以直接从cache中获取,而不必每次都向服务器请求。浏览器知道为一个特定的文件缓存多少时间,因为服务器在响应报文中用Expires头设置了这个值。此外,每个响应头还可以包含一个ETag头,它类似于请求文件的版本号。当浏览器看到这个版本号的文件已经存在时,它可以立即停止传输。

    上面枚举的URL中,"fbcdn.net"意思是"Facebook content delivery network"。 Facebook利用了CDN(Content Delivery Network, 内容分发网络)来对静态内容(图像、CSS样式表、JavaScript文件)进行发布,因此,这类文件在全球的多台机器上拥有多个拷贝。静态内容占用了一个网站很大的带宽,并且通过CDN它们很容易复制。通常,网站更愿意找一个第三方CDN提供商,而不是自己组建一个CDN。例如,Facebook的静态内容都是由全球最大的CND提供商Akamai来存储的。

    当我们ping static.ak.fbcdn.net的时候,我们会得到一个来自akamai.net服务器的响应信息。同时,当我们ping这个URL多次时,我们可能获得来自不同服务器的响应。这就证明了服务器端有一个负载均衡的功能在运行。

    10、浏览器继续发送AJAX请求

    Web2.0的精神就是,即使一个页面已经完全呈现了,客户仍然可以继续与服务器进行通信。

    比如,Facebook chat功能能够即时更新你的在线状态。要更新你好友的现在状态,在你浏览器中执行的JavaScript必须发送一个异步请求给服务器。异步请求是一种由程序构造的GET或POST请求,并且要发送到一个特别的URL上。在Facebook的例子中,浏览器发送一个POST请求到http://www.facebook.com/ajax/chat/buddy_list.php上,去获得当前在线好友的列表。

    这种形式的异步请求也称为AJAX(Asynchronous JavaScript And XML),名字虽然如此,但是实际上服务器并不是一定要将响应封装成XML。比如,Facebook就会用一段JavaScript代码去响应AJAX请求。

    Facebook chat提供了一个关于AJAX很有趣的例子:服务器向客户端推送数据。因为HTTP是基于请求-响应的协议,聊天服务器无法主动推送新消息到客户端。所以,客户端必须每隔几秒钟到服务器上看看是否有新数据要拉下来,这会对服务器造成较大的压力。Long polling技术能减轻服务器响应AJAX请求时的负载。当客户在向服务器拉数据时,服务器发现没有新消息,那么它不会立即响应客户的这次请求,但是会hold住这个连接。只有在与当前客户建立的连接过期之前收到了新消息,服务器才会返回一个响应信息。

    结束语
    希望这篇文章能让你对不同的web模块如何在一起有效地工作有一个更加清楚地认识。

    最后再贴一下本文的原文地址:http://igoro.com/archive/what-really-happens-when-you-navigate-to-a-url/

    --EOF--

    Java继承体系下的类初始化

    Java每个类编译后都会独立存在于一个.class文件中,并且这个文件只有首次被使用时才会加载。在前几天的文章『Java类成员变量的初始化顺序』中,讲到类的静态变量(static关键字修饰的)会在初始化过程的最开始执行。这个原则在Java继承体系中依然存在,由于子类的static静态变量可能依赖于它的基类成员是否已被初始化,因此无论继承的层次有几层,根基类的静态成员变量是最先被初始化的。

    抛开继承,对于每个独立的类,其初始化过程为:
    1)静态成员初始化
    2)非静态成员初始化
    3)执行构造函数。

    引入继承之后,在构造一个子类对象时,其初始化顺序为:
    1)基类静态成员初始化
    2)子类静态成员初始化
    3)基类非静态成员初始化
    4)执行基类构造函数
    5)子类非静态成员初始化
    6)执行子类构造函数

    下面这个示例程序来自于『Java编程思想 (第3版)』,我只做了一小部分修改:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    
    /**
     * Alipay.com Inc.
     * Copyright (c) 2004-2011 All Rights Reserved.
     */
    package com.alipay.inittest;
     
    /**
     * 
     * @author changjian.feng
     * @version $Id: Beetle.java, v 0.1 2011-8-24 下午10:30:05
     */
    class Insect {
        private final int i = print("Insect.i initialized");
        protected int     j;
     
        Insect() {
            System.out.println("i = " + i + ", j = " + j);
            j = 39;
        }
     
        private static int x1 = print("static Insect.x1 initialized");
     
        static int print(String s) {
            System.out.println(s);
            return 47;
        }
    }
     
    public class Beetle extends Insect {
     
        private final int k = print("Beetle.k initialized");
     
        public Beetle() {
            System.out.println("k = " + k + ", j = " + j);
        }
     
        private static int x2 = print("static Beetle.x2 initialized");
     
        /**
         * 
         * @param args
         */
        public static void main(String[] args) {
            System.out.println("Beetle constructor");
            Beetle b = new Beetle();
        }
     
    }

    上述程序的执行结果如下:

    1
    2
    3
    4
    5
    6
    7
    
    static Insect.x1 initialized
    static Beetle.x2 initialized
    Beetle constructor
    Insect.i initialized
    i = 47, j = 0
    Beetle.k initialized
    k = 47, j = 39

    JVM加载Beetle.class文件时,注意到它extends一个基类Insect,于是先去加载Insect.class进行静态变量Insect.x1的初始化工作,然后在到Beetle.class进行静态变量Beetle.x2的初始化工作,无论Beetle.main()中语句是否执行,只要加载了Beetle.class,那么Insect.x1和Beetle.x2的初始化工作是肯定就能执行的。当程序执行到Beetle b = new Beetle()时,按初始化顺序步骤中的3)~6)步,分别进行Insect.i、Insect构造函数、Beetle.k、Beetle构造函数的初始化。

    Reference:
    1、Bruce Eckel. Java编程思想(第3版). 机械工业出版社. 2005.5

    --EOF--

    Apache核心(Core)指令<Location>和<Directory>区别

    <Directory>和<Location>指令都是Apache配置文件httpd.conf中的常见指令,它们都能对其指定的作用域进行访问控制,区别在于<Directory>针对的是文件系统,而<Location>针对的是网络空间。

    文件系统是指服务器所在机器上的所能看见的磁盘系统中的目录,比如Linux下的/usr/local/apache,或Windows系统中的C:/Program Files/Apache。<Directory>指令可以对真实存在于文件系统中的目录及其子目录进行访问控制,具体能进行怎样粒度的访控可以参考Apache手册的<Directory>使用说明。比如apache的DocumentRoot配置成/usr/local/www,该目录中有一个网页文件index.htm,那么以下配置代码就能阻止任何人访问www目录。

    1
    2
    3
    4
    
    <Directory /user/local/www>
    Order allow,deny
    Deny from all
    </Directory>

    此时,如果用http://yourdomain.com/index.htm来请求index.htm文件时,Apache就会根据以上配置信息拒绝所有外来请求。这里仅仅举个例子,在实际应用中,以上配置可能没什么意义。

    网络空间是指网站被服务器发送或者呈现在浏览器中的视图,例如有一个网页地址http://yourdomain.com/test1/index.htm,通过映射的方法,它的实际存储位置可以在服务器的/usr/local/www/test2/目录中,这个时候,想要对/test1这个网络路径进行访问控制,那么就可以配置<Location>属性。还有一种应用情况,现在很多网页是通过数据库信息动态生成的,或者用velocity这类模板引擎渲染而来,这样生成的看似静态的网页实际上并没有真实存在于服务器的文件系统中,因此对这类地址如果用<Directory>指令就无法达到期望的访问控制效果,就是说配<Directory>是不起作用的,而用<Location>指令就能达到目的。例如

    1
    2
    3
    4
    
    <Location /test1>
    Order allow,deny
    Deny from all
    </Location>

    以上配置能将所有发向http://yourdomain.com/test1/*的请求都拒绝。同<Directory>一样,具体能在<Location>指令中做怎样粒度的权限控制可以参考Apache手册的<Location>使用说明

    除此之外,Apache对于httpd.conf文件中<Directory>和<Location>(以及<Files>、<VirtualHost>)和它们各自的正则表达式版本(<DirectoryMatch>、<FilesMatch>、<LocationMatch>等)有指定的合并顺序,因此一个配置段的指令可能会被后边的配置段指令比如覆盖。这里有几个例子可以很好的说明这个情况,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    <Location />
    Order deny,allow
    Allow from all
    </Location>
     
    # 这个<Directory>段将不会实际生效
    <Directory />
    Order allow,deny
    Allow from all
    Deny from badguy.example.com
    </Directory>

    由于合并时处理<Directory>在先,处理<Location>在后,因此上面<Directory>配置里的Deny from badguy.example.com会被<Location>覆盖掉,这是配置httpd.conf时要注意的地方。

    还有一点,改过Apache的httpd.conf配置文件后,必须重启Apache服务。

    Reference:
    [1] Apache Core Features. http://httpd.apache.org/docs/2.0/mod/core.html. 2011.08.24 accessed.
    [2] 配置段(容器). http://www.phpchina.com/manual/apache/sections.html. 2011.08.24 accessed.

    --EOF--

    『决策与判断』

    一见这么鹾的装帧就没兴致仔细看了,不过一些实验结果还是蛮有意思的,仅仅摘取些片断,留作备忘。

    1、关于认知不协调理论
    如果一个当代的马基雅维利向君主建议,他可能会提供如下建议:
    1). 如果你希望一个人形成对某个事物的积极态度,那么就引导他,使他致力于得到这个事物。
    2). 如果你希望一个人对某种错误行为的道德判断变得温和一些,那么久引诱他做出这种行为。相反,如果你希望一个人对某种错误行为的道德判断更加强硬,那么久引诱他,但不足以使他做出这种行为。

    为达目的不择手段,这里就可以见得马基雅维利是一个多么操蛋的人!态度的改变能引起行为的改变,反之,行为的改变也能引起态度的改变。根据认知不协调理论,为保持一致性,人们常常不得不去改变信念使之与行为一致。比如决定要买一件完全用不到的东西,等买来之后就会感觉这东西非常有用。这就是一种观念的改变。

    2、初始效应和近因效应
    做出一个决策往往有一个很强的情境依赖的过程。所谓初始效应,就是指“第一印象”,当人们面临一个有争议问题的对立面信息时,初始效应使人们更容易受到先出现的信息的影响。而近因效应的概念恰恰相反,它的主要意思是最后出现的信息会比最先出现的信息影响更大。不同的情形下应该采取不同的策略来获得最大收益。比如对于某次评比,假设每人如果有3分钟表现时间,如果规则是每人表演完立刻接受评委的评分,这种规则下初始效应更强些,评委对第一个表演者地印象最深。而如果规则是所有人表演完了再统一接受评委打分,这种规则下近因效应更强些,评委会对最后一个表演者印象最深刻。

    3、手热现象
    手热现象普遍存在于篮球运动中,球员某些时候状态出色,手特别热,能比平时有更高的投篮命中率。实际上,这只是一种误区。从更大一点的区间看,这名篮球运动员的投篮命中率与其平均水平是一致的,造成这种误区的原因是人们错误认为一个随机序列必须具备局部代表性,即认为序列的每一部分看起来都必须像是随机的。所以当连续投币3次,3次都出现正面,那么再投一次硬币,出现正面和反面的概率还是一样的,不存在出现反面的概率更大一些的情况。对于两个序列:1、XOXXXOOOOXOXXOOOXXXOX。2、XOXOXOOOXXOXOXOOXXXO。通常人认为第2个序列更随机,但是实际上第一个序列才是随机序列,因为第一个序列X和O的转换率为50%(转换了10次),符合随机性。而第二个序列的转换率为70%(转换了14次),远远高于第一个序列50%的随机性。如果把X代表投篮命中,那么这个例子也能说明手热现象只不过是这名球员恰好落在了某个看起来不像是随机序列的区间里。一系列的坏运气就是一系列的坏运气,它不意味着好运气必然会来临。对于一个随机过程,过去的事情不会对未来的事情造成任何影响。

    4、社会易化和社会性懈怠
    对于那些简单的、熟练的行为,人们在有旁观者在场时通常能做得更好;但是对于那些复杂的、还没有掌握好的技能,人们在有他人在场时则会表现得没那么好,这就是社会易化效应。甚至,当个体自己预期自己的行为会受到其他人的评价时,即使其他人没有实际在场,社会易化效应的促进作用或损害作用也会存在,这应该是心理暗示的作用。

    社会性懈怠也是指个体在别人在场的情况下的表现出的一种现象。它是指,当人们作为群体的一员做一件事情时,就不会像自己独立完成时那么努力了。这个意思很好理解,“一个和尚挑水喝,三个和尚没水喝”。这是由于在社会性条件下,责任被分散到群体中所有人身上,而独立行动时,所有责任和后果要由个体一个人承担。

    5、关于判断中的准确度和信心的关系
    人们地信心评分实际上与他们的准确度并无联系,实验中答对题数多的人并不比成绩较差的答题者具备更强的信心,并且,高信心的答题者与低信心的答题者得分基本相同。因此,信心的高低其实并不能影响到准确度。而且,准确度与信心之间的关系与决策者的智商水平无关。

    6、延期陷阱
    就是拖延症。在延期陷阱中,暂时的满足与长期的后果相冲突,为避免短期的相对小的痛苦结果导致了长期的、破坏性的甚至是致命性的后果。在我身上的例子就是小时候不去医院看牙齿,拖到如今牙齿大装修,费力又劳神,彻底陷入了延期陷阱中。

    Reference:
    1、决策与判断(豆瓣·当当·京东), 斯科特·普劳斯 著, 施俊琦 王星 译, 人民邮电出版社, 2004.9.

    --EOF--