随笔-31  评论-2  文章-0  trackbacks-0
  2009年7月2日

1 set和multiset容器的能力
set 和multiset容器的内部结构通常由平衡二叉树(balanced binary tree)来实现。当元素放入容器中时,会按照一定的排序法则自动排序,默认是按照less<>排序规则来排序。这种自动排序的特性加速了元 素查找的过程,但是也带来了一个问题:不可以直接修改set或multiset容器中的元素值,因为这样做就可能违反了元素自动排序的规则。如果你希望修 改一个元素的值,必须先删除原有的元素,再插入新的元素。

2 set和multiset容器的操作
Constructor and Destructor
  • set c: 创建一个空的set或multiset容器
  • set c(op): 创建一个空的使用op作为排序法则的set或multiset容器
  • set c1(c2): 创建一个已存在的set或multiset容器的复制品,容器的类型和所有元素一同复制
  • set c(beg, end): 创建一个set或multiset容器,并且以[beg, end)范围中的元素进行初始化
  • set c(beg, end, op): 创建一个使用op作为排序法则的set或multiset容器,并且以[beg, end)范围中的元素进行初始化
  • c.~set(): 容器的析构函数,销毁所有的元素,释放所有的分配内存
上面的set可以是下面几种形式:
  • set<type>: 以less<>为排序法则的set
  • set<type, op>: 以op为排序法则的set
  • multiset<type>: 以less<>为排序法则的multiset
  • multiset<type, op>: 以op为排序法则的multiset
从上面我们可以看到,可以从两个地方来指定排序法则:
(1)作为模板参数
例如:std::set<int, greater<int> > col1;
这种情况下,排序法则本身作为容器类型的一部分。对于一个set或者multiset容器,只有当元素类型和排序法则类型都相同时,他们的类型才被认为相同,否则就是不同类型的容器。

(2)作为构造函数参数
例如:std::set<int> col1(greater<int>);
这种情况下指定的排序法则不作为容器类型的一部分,你可以为相同类型的容器指定不同的排序规则。这通常应用于要求相同的容器类型,但排序规则可以不同的场合。

Size and Comparing
set 和multiset容器同样提供size(), empty(), max_size()三个关于查询元素数目的接口,提供==, !=, <, <=, >, >=等比较操作符。但值得注意的是比较操作符只针对相同类型的容器,元素类型和排序法则类型都必须相同。

Special Search Operations
set和multiset容器的内部结构对于元素的查找提供了优化空间,所以它们提供了一些特殊的查找接口,这些查找操作通常要比同名的通用算法高效,所以在相同的条件下应该优先使用这些接口。
  • count(val): 返回容器中值等于val的元素数目。
  • find(val): 返回容器中值等于val的第一个元素的iterator位置;如果没有匹配元素,则返回end()位置。
  • lower_bound(val): 返回容器中第一个值大于或等于val的元素的iterator位置。
  • upper_bound(val): 返回容器中第一个值大于val的元素的iterator位置。
  • equal_range(val): 返回容器中值等于val的所有元素的范围[beg, end)组成的pair<beg, end> 。
下面我们看一个使用lower_bound(), upper_bound和equal_range(val)例子,以加深对它们的理解:
#include <iostream>
#include <set>
#include "print.hpp"
using namespace std;
int main()
{
    multiset<int> col1;

    col1.insert(2);
    col1.insert(5);
    col1.insert(4);
    col1.insert(6);
    col1.insert(1);
    col1.insert(5);

    PRINT_ELEMENTS(col1, "col1: ");
    cout << endl;

    multiset<int>::const_iterator pos;
    pair<multiset<int>::iterator, multiset<int>::iterator> range;

    cout << "lower_bound(3): " << *col1.lower_bound(3) << endl;
    cout << "upper_bound(3): " << *col1.upper_bound(3) << endl;
    range = col1.equal_range(3);
    cout << "equal_range(3): " << *range.first << " " << *range.second << endl;
    cout << "elements with value(3): ";
    for (pos = range.first; pos != range.second; ++pos)
    {
        cout << *pos << " ";
    }
    cout << endl;
    cout << endl;

    cout << "lower_bound(5): " << *col1.lower_bound(5) << endl;
    cout << "upper_bound(5): " << *col1.upper_bound(5) << endl;
    range = col1.equal_range(5);
    cout << "equal_range(5): " << *range.first << " " << *range.second << endl;
    cout << "elements with value(5): ";
    for (pos = range.first; pos != range.second; ++pos)
    {
        cout << *pos << " ";
    }
    cout << endl;
}
执行结果如下:
col1: 1 2 4 5 5 6 

lower_bound(3): 4
upper_bound(3): 4
equal_range(3): 4 4
elements with value(3): 

lower_bound(5): 5
upper_bound(5): 6
equal_range(5): 5 6
elements with value(5): 5 5 

Assignment
set和multiset容器只提供最基本的赋值操作:
  • c1 = c2: 把c2的所有元素复制到c1中,同时c1原有的元素被销毁。
  • c1.swap(c2): 交换c1和c2的元素。
  • swap(c1, c2): 同上,只不过这是一个通用算法。
需要注意的是两个容器的类型要一致(包括元素类型和排序法则类型)。

Inserting and Removing Elements
set和multiset容器的插入和删除元素接口跟其他容器也非常类似,但在细节上却存在差别。
  • c.insert(elem): 在容器中插入元素elem的一份拷贝,并返回新元素的iterator位置;如果是set容器,同时还返回是否插入成功的标志。
  • c.insert(pos, elem): 在容器中插入元素elem的一份拷贝,并返回新元素的iterator位置;因为set和multiset容器的元素是自动排序的,所以pos位置只是插入位置的一个提示,设置恰当的话,可以提高插入元素的效率。
  • c.insert(beg, end): 在容器中插入[beg, end)范围中所有元素的拷贝,没有返回值。
  • c.erase(val): 删除容器中所有值为val的元素,返回删除元素的数目。
  • c.erase(pos): 删除容器中位置pos处的元素,没有返回值。
  • c.erase(beg, end): 删除容器中[ben, end)范围内所有的元素,没有返回值。
  • c.clear(): 删除容器中所有元素,使容器成为空容器。

其中我们重点说一下c.insert(elem)接口。
对于set容器,它的定义如下:
pair<iterator, bool> insert(const TYPE& val);
而对于multiset容器,它的定义如下:
iterator insert(const TYPE& val);
它 们的不同就是set容器的insert接口返回的是一个pair<iterator, bool>,而multiset容器的insert接口直接返回一个iterator。这是因为set容器中不允许有重复的元素,如果容器中已经存 在一个跟插入值相同的元素,那么插入操作就会失败,而pair中的bool值就是标识插入是否成功的。而multiset不存在这个问题。

3 set和multiset容器的异常处理
因为set和multiset容器的独特内部结构,当发生异常时,也可以把影响减到最小。也就是说,跟list容器一样,set和multiset容器的操作要么成功,要么对原有容器没有影响。

4 运行时指定排序法则
通常情况下,我们是在定义容器时指定排序法则,就像下面形式:
std::set<int, greater<int> > col1;
或者
std::set<int> col1;    //use default sorting criterion less<>

然而,如果你需要在运行时动态指定容器的排序法则,或者你希望对于相同的容器类型却有着不同的排序法则,那么就要做一些特殊处理。下面我们看一个例子:
#include <iostream>
#include <set>
#include "print.hpp"
using namespace std;

template <typename T>
class RuntimeCmp 
{
    public:
        enum cmp_mode {normal, reverse};
    private:
        cmp_mode mode;
    public:
        RuntimeCmp(cmp_mode m = normal) : mode(m) {}

        bool operator() (const T& t1, const T& t2)
        {
            return mode == normal ? t1 < t2 : t1 > t2;
        }

        bool operator== (const T& rhv) 
        {
            return mode == rhv.mode;
        }
};

typedef set<int, RuntimeCmp<int> > IntSet;

//pre-declare
void fill(IntSet& col1);

int main()
{
    IntSet col1;
    fill(col1);
    PRINT_ELEMENTS(col1, "col1: ");

    RuntimeCmp<int> reverse_cmp(RuntimeCmp<int>::reverse);
    IntSet col2(reverse_cmp);
    fill(col2);
    PRINT_ELEMENTS(col2, "col2: ");

    if (col1 == col2) 
    {
        cout << "col1 and col2 is equal" <<endl;
    }
    else
    {
        if (col1 < col2) 
        {
            cout << "col1 is less than col2" << endl;
        }
        else 
        {
            cout << "col1 is greater than col2" << endl;
        }
    }
    return 0;
}

void fill(IntSet& col1) 
{
    col1.insert(2);
    col1.insert(3);
    col1.insert(6);
    col1.insert(5);
    col1.insert(1);
    col1.insert(4);
}
运行结果如下:
col1 1 2 3 4 5 6 
col2 6 5 4 3 2 1 
col1 is less than col2

这里例子中,col1和col2有着相同的类型:set<int, RuntimeCmp<int> >,但是它们的排序法则却不相同,一个升序,一个降序。这都是通过自定义的函数对象来实现的,所以函数对象比普通函数有着更加灵活与强大的控制,可 以满足一些特殊的需求。

posted @ 2010-10-29 13:51 xiaoxinchen 阅读(1761) | 评论 (0)编辑 收藏
  众所周知,Linux动态库的默认搜索路径是/lib/usr/lib。动态库被创建后,一般都复制到这两个目录中。当程序执行时需要某动态库,并且该动态库还未加载到内存中,则系统会自动到这两个默认搜索路径中去查找相应的动态库文件,然后加载该文件到内存中,这样程序就可以使用该动态库中的函数,以及该动态库的其它资源了。在Linux 中,动态库的搜索路径除了默认的搜索路径外,还可以通过以下三种方法来指定。

方法一:在配置文件/etc/ld.so.conf中指定动态库搜索路径。

可以通过编辑配置文件/etc/ld.so.conf来指定动态库的搜索路径,该文件中每行为一个动态库搜索路径。每次编辑完该文件后,都必须运行命令ldconfig使修改后的配置生效。我们通过例1来说明该方法。

例1:

我们通过以下命令用源程序pos_conf.c(见程序1)来创建动态库 libpos.so,详细创建过程请参考文[1]。

# gcc -c pos_conf.c
      # gcc -shared -fPCI -o libpos.so pos_conf.o
      #

#include <stdio.h>
      void pos()
      {
          printf("/root/test/conf/lib\n");

}

       程序1: pos_conf.c

接着通过以下命令编译main.c(见程序2)生成目标程序pos。

# gcc -o pos main.c -L. -lpos
      #

void pos();
      int main()
      {
          pos();
               return 0;
      }

程序2: main.c

然后把库文件移动到目录/root/test/conf/lib中。

# mkdir -p /root/test/conf/lib
      # mv libpos.so /root/test/conf/lib
      #

最后编辑配置文件/etc/ld.so.conf,在该文件中追加一行"/root/test/conf/lib"。

运行程序pos试试。

# ./pos
        ./pos: error while loading shared libraries: libpos.so: cannot open shared object file: No such file or directory
      #

出错了,系统未找到动态库libpos.so。找找原因,原来在编辑完配置文件/etc/ld.so.conf后,没有运行命令ldconfig,所以刚才的修改还未生效。我们运行ldconfig后再试试。

# ldconfig
      # ./pos     /root/test/conf/lib
      #

程序pos运行成功,并且打印出正确结果。

方法二:通过环境变量LD_LIBRARY_PATH指定动态库搜索路径(!)。

通过设定环境变量LD_LIBRARY_PATH也可以指定动态库搜索路径。当通过该环境变量指定多个动态库搜索路径时,路径之间用冒号":"分隔。

    不过LD_LIBRARY_PATH的设定作用是全局的,过多的使用可能会影响到其他应用程序的运行,所以多用在调试。(LD_LIBRARY_PATH的缺陷和使用准则,可以参考《Why LD_LIBRARY_PATH is bad》)。通常情况下推荐还是使用gcc的-R或-rpath选项来在编译时就指定库的查找路径,并且该库的路径信息保存在可执行文件中,运行时它会直接到该路径查找库,避免了使用LD_LIBRARY_PATH环境变量查找。

下面通过例2来说明本方法。

例2:

我们通过以下命令用源程序pos_env.c(见程序3)来创建动态库libpos.so。

# gcc -c pos_env.c
      # gcc -shared -fPCI -o libpos.so pos_env.o
      #

#include <stdio.h>
          void pos()
          {
                printf("/root/test/env/lib\n");
          }
      程序3: pos_env.c

测试用的可执行文件pos可以使用例1中的得到的目标程序pos,不需要再次编译。因为pos_conf.c中的函数pos和pos_env.c中的函数pos 函数原型一致,且动态库名相同,这就好比修改动态库pos后重新创建该库一样。这也是使用动态库的优点之一。

然后把动态库libpos.so移动到目录/root/test/conf/lib中。

# mkdir -p /root/test/env/lib
      # mv libpos.so /root/test/env/lib
      #

我们可以使用export来设置该环境变量,在设置该环境变量后所有的命令中,该环境变量都有效。

例如:

# export LD_LIBRARY_PATH=/root/test/env/lib
      #

但本文为了举例方便,使用另一种设置环境变量的方法,既在命令前加环境变量设置,该环境变量只对该命令有效,当该命令执行完成后,该环境变量就无效了。如下述命令:

# LD_LIBRARY_PATH=/root/test/env/lib ./pos  /root/test/env/lib
      #

程序pos运行成功,并且打印的结果是"/root/test/env/lib",正是程序pos_env.c中的函数pos的运行结果。因此程序pos搜索到的动态库是/root/test/env/lib/libpos.so。

方法三:在编译目标代码时指定该程序的动态库搜索路径。

还可以在编译目标代码时指定程序的动态库搜索路径。这是通过gcc 的参数"-Wl,-rpath,"指定(如例3所示)。当指定多个动态库搜索路径时,路径之间用冒号":"分隔。

例3:

我们通过以下命令用源程序pos.c(见程序4)来创建动态库libpos.so。

# gcc -c pos.c
      # gcc -shared -fPCI -o libpos.so pos.o
      #

#include <stdio.h>
      void pos()
      {
                printf("./\n");
      }

      程序4: pos.c

因为我们需要在编译目标代码时指定可执行文件的动态库搜索路径,所以需要用gcc命令重新编译源程序main.c(见程序2)来生成可执行文件pos。

# gcc -o pos main.c -L. -lpos -Wl,-rpath,./
      #

再运行程序pos试试。

# ./pos   ./
      #

程序pos运行成功,输出的结果正是pos.c中的函数pos的运行结果。因此程序pos搜索到的动态库是./libpos.so。

以上介绍了三种指定动态库搜索路径的方法,加上默认的动态库搜索路径/lib和/usr/lib,共五种动态库的搜索路径,那么它们搜索的先后顺序是什么呢?

在 介绍上述三种方法时,分别创建了动态库./libpos.so、 /root/test/env/lib/libpos.so和/root/test/conf/lib/libpos.so。我们再用源程序 pos_lib.c(见程序5)来创建动态库/lib/libpos.so,用源程序pos_usrlib.c(见程序6)来创建动态库 /usr/lib/libpos.so。

#include <stdio.h>
      void pos()
      {
                   printf("/lib\n");
      }

      程序5: pos_lib.c

#include <stdio.h>
      void pos()
      {
                 printf("/usr/lib\n");
      }

      程序6: pos_usrlib.c

这样我们得到五个动态库libpos.so,这些动态库的名字相同,且都包含相同函数原型的公用函数pos。但存储的位置不同和公用函数pos 打印的结果不同。每个动态库中的公用函数pos都输出该动态库所存放的位置。这样我们可以通过执行例3中的可执行文件pos得到的结果不同获知其搜索到了哪个动态库,从而获得第1个动态库搜索顺序,然后删除该动态库,再执行程序pos,获得第2个动态库搜索路径,再删除第2个被搜索到的动态库,如此往复,将可得到Linux搜索动态库的先后顺序。程序pos执行的输出结果和搜索到的动态库的对应关系如表1所示:

程序pos输出结果 使用的动态库 对应的动态库搜索路径指定方式
./ ./libpos.so 编译目标代码时指定的动态库搜索路径
/root/test/env/lib /root/test/env/lib/libpos.so 环境变量LD_LIBRARY_PATH指定的动态库搜索路径
/root/test/conf/lib /root/test/conf/lib/libpos.so 配置文件/etc/ld.so.conf中指定的动态库搜索路径
/lib /lib/libpos.so 默认的动态库搜索路径/lib
/usr/lib /usr/lib/libpos.so 默认的动态库搜索路径/usr/lib
表1: 程序pos输出结果和动态库的对应关系

创建各个动态库,并放置在相应的目录中。测试环境就准备好了。执行程序pos,并在该命令行中设置环境变量LD_LIBRARY_PATH。

# LD_LIBRARY_PATH=/root/test/env/lib ./pos  ./
      #

根据程序pos的输出结果可知,最先搜索的是编译目标代码时指定的动态库搜索路径。然后我们把动态库./libpos.so删除了,再运行上述命令试试。

# rm libpos.so
        rm: remove regular file `libpos.so'? y
      # LD_LIBRARY_PATH=/root/test/env/lib ./pos /root/test/env/lib
      #

根据程序pos的输出结果可知,第2个动态库搜索的路径是环境变量LD_LIBRARY_PATH指定的。我们再把/root/test/env/lib/libpos.so删除,运行上述命令。

# rm /root/test/env/lib/libpos.so
        rm: remove regular file `/root/test/env/lib/libpos.so'? y
      # LD_LIBRARY_PATH=/root/test/env/lib ./pos  /root/test/conf/lib
      #

第3个动态库的搜索路径是配置文件/etc/ld.so.conf指定的路径。删除动态库/root/test/conf/lib/libpos.so后再运行上述命令。

# rm /root/test/conf/lib/libpos.so
        rm: remove regular file `/root/test/conf/lib/libpos.so'? y
      # LD_LIBRARY_PATH=/root/test/env/lib ./pos  /lib
      #

第4个动态库的搜索路径是默认搜索路径/lib。我们再删除动态库/lib/libpos.so,运行上述命令。

# rm /lib/libpos.so
        rm: remove regular file `/lib/libpos.so'? y
      # LD_LIBRARY_PATH=/root/test/env/lib ./pos  /usr/lib
      #

最后的动态库搜索路径是默认搜索路径/usr/lib。

综合以上结果可知,动态库的搜索路径搜索的先后顺序是:

1.编译目标代码时指定的动态库搜索路径;

2.环境变量LD_LIBRARY_PATH指定的动态库搜索路径;

3.配置文件/etc/ld.so.conf中指定的动态库搜索路径;

4.默认的动态库搜索路径/lib;

5.默认的动态库搜索路径/usr/lib。

在上述1、2、3指定动态库搜索路径时,都可指定多个动态库搜索路径,其搜索的先后顺序是按指定路径的先后顺序搜索的。对此本文不再举例说明,有兴趣的读者可以参照本文的方法验证。

posted @ 2010-09-14 11:03 xiaoxinchen 阅读(200) | 评论 (0)编辑 收藏

序论
我曾发表过文件输入输出的文章,现在觉得有必要再写一点。文件 I/O 在C++中比烤蛋糕简单多了。 在这篇文章里,我会详细解释ASCII和二进制文件的输入输出的每个细节,值得注意的是,所有这些都是用C++完成的。

一、ASCII 输出
为了使用下面的方法, 你必须包含头文件<fstream.h>(译者注:在标准C++中,已经使用<fstream>取 代<fstream.h>,所有的C++标准头文件都是无后缀的。)。这是 <iostream.h>的一个扩展集, 提供有缓冲的文件输入输出操作. 事实上, <iostream.h> 已经被<fstream.h>包含了, 所以你不必包含所有这两个文件, 如果你想显式包含他们,那随便你。我们从文件操作类的设计开始, 我会讲解如何进行ASCII I/O操作。 如果你猜是"fstream," 恭喜你答对了! 但这篇文章介绍的方法,我们分别使用"ifstream"?和 "ofstream" 来作输入输出。
如果你用过标准控制台流"cin"?和 "cout," 那现在的事情对你来说很简单。 我们现在开始讲输出部分,首先声明一个类对象。

ofstream fout; 

这就可以了,不过你要打开一个文件的话, 必须像这样调用ofstream::open()。

fout.open("output.txt"); 

你也可以把文件名作为构造参数来打开一个文件.

ofstream fout("output.txt");

  这是我们使用的方法, 因为这样创建和打开一个文件看起来更简单. 顺便说一句, 如果你要打开的文件不存在,它会为你创建一个, 所以不用担心文件创建的问题. 现在就输出到文件,看起来和"cout"的操作很像。 对不了解控制台输出"cout"的人, 这里有个例子。

int num = 150;char name[] = "John Doe";fout << "Here is a number: " << num << "\n";fout << "Now here is a string: " << name << "\n";

  现在保存文件,你必须关闭文件,或者回写文件缓冲. 文件关闭之后就不能再操作了, 所以只有在你不再操作这个文件的时候才调用它,它会自动保存文件。 回写缓冲区会在保持文件打开的情况下保存文件, 所以只要有必要就使用它。 回写看起来像另一次输出, 然后调用方法关闭。像这样:

fout << flush; fout.close(); 

现在你用文本编辑器打开文件,内容看起来是这样:

Here is a number: 150 Now here is a string: John Doe 

  很简单吧! 现在继续文件输入, 需要一点技巧, 所以先确认你已经明白了流操作,对 "<<" 和">>" 比较熟悉了, 因为你接下来还要用到他们。继续…

二、ASCII 输入
输入和"cin" 流很像. 和刚刚讨论的输出流很像, 但你要考虑几件事情。在我们开始复杂的内容之前, 先看一个文本:

12 GameDev 15.45 L This is really awesome! 

为了打开这个文件,你必须创建一个in-stream对象,?像这样。

ifstream fin("input.txt"); 

  现在读入前四行. 你还记得怎么用"<<" 操作符往流里插入变量和符号吧?好,?在 "<<" (插入)?操作符之后,是">>" (提取) 操作符. 使用方法是一样的. 看这个代码片段.

int number; float real; char letter, word[8]; fin >> number; fin >> word; fin >> real; fin >> letter; 

也可以把这四行读取文件的代码写为更简单的一行。

fin >> number >> word >> real >> letter; 

  它是如何运作的呢? 文件的每个空白之后, ">>" 操作符会停止读取内容, 直到遇到另一个>>操作符. 因为我们读取的每一行都被换行符分割开(是空白字符), ">>" 操作符只把这一行的内容读入变量。这就是这个代码也能正常工作的原因。但是,可别忘了文件的最后一行。

This is really awesome! 

  如果你想把整行读入一个char数组, 我们没办法用">>"?操作符,因为每个单词之间的空格(空白字符)会中止文件的读取。为了验证:

char sentence[101]; fin >> sentence; 

  我们想包含整个句子, "This is really awesome!" 但是因为空白, 现在它只包含了"This". 很明显, 肯定有读取整行的方法, 它就是getline()。这就是我们要做的。

fin.getline(sentence, 100); 

  这是函数参数. 第一个参数显然是用来接受的char数组. 第二个参数是在遇到换行符之前,数组允许接受的最大元素数量. 现在我们得到了想要的结果:“This is really awesome!”。
你应该已经知道如何读取和写入ASCII文件了。但我们还不能罢休,因为二进制文件还在等着我们。

三、二进制 输入输出
二进制文件会复杂一点, 但还是很简单的。 首先你要注意我们不再使用插入和提取操作符(译者注:<< 和 >> 操作符). 你可以这么做,但它不会用二进制方式读写。你必须使用read() 和write() 方法读取和写入二进制文件. 创建一个二进制文件, 看下一行。

ofstream fout("file.dat", ios::binary); 

  这会以二进制方式打开文件, 而不是默认的ASCII模式。首先从写入文件开始。函数write() 有两个参数。 第一个是指向对象的char类型的指针, 第二个是对象的大小(译者注:字节数)。 为了说明,看例子。

int number = 30; fout.write((char *)(&number), sizeof(number)); 

  第一个参数写做"(char *)(&number)". 这是把一个整型变量转为char *指针。如果你不理解,可以立刻翻阅C++的书籍,如果有必要的话。第二个参数写作"sizeof(number)". sizeof() 返回对象大小的字节数. 就是这样!
二进制文件最好的地方是可以在一行把一个结构写入文件。 如果说,你的结构有12个不同的成员。 用ASCII?文件,你不得不每次一条的写入所有成员。 但二进制文件替你做好了。 看这个。

struct OBJECT { int number; char letter; } obj; obj.number = 15;obj.letter = ‘M’; fout.write((char *)(&obj), sizeof(obj)); 

  这样就写入了整个结构! 接下来是输入. 输入也很简单,因为read()?函数的参数和 write()是完全一样的, 使用方法也相同。

ifstream fin("file.dat", ios::binary); fin.read((char *)(&obj), sizeof(obj)); 

  我不多解释用法, 因为它和write()是完全相同的。二进制文件比ASCII文件简单, 但有个缺点是无法用文本编辑器编辑。 接着, 我解释一下ifstream 和ofstream 对象的其他一些方法作为结束.

四、更多方法
我已经解释了ASCII文件和二进制文件, 这里是一些没有提及的底层方法。

检查文件

你已经学会了open() 和close() 方法, 不过这里还有其它你可能用到的方法。
方法good() 返回一个布尔值,表示文件打开是否正确。
类似的,bad() 返回一个布尔值表示文件打开是否错误。 如果出错,就不要继续进一步的操作了。
最后一个检查的方法是fail(), 和bad()有点相似, 但没那么严重。

读文件
方法get() 每次返回一个字符。
方法ignore(int,char) 跳过一定数量的某个字符, 但你必须传给它两个参数。第一个是需要跳过的字符数。 第二个是一个字符, 当遇到的时候就会停止。 例子,

fin.ignore(100, ‘\n’); 

会跳过100个字符,或者不足100的时候,跳过所有之前的字符,包括 ‘\n’。
方法peek() 返回文件中的下一个字符, 但并不实际读取它。所以如果你用peek() 查看下一个字符, 用get() 在peek()之后读取,会得到同一个字符, 然后移动文件计数器。
方法putback(char) 输入字符, 一次一个, 到流中。我没有见到过它的使用,但这个函数确实存在。

写文件
只有一个你可能会关注的方法.?那就是 put(char), 它每次向输出流中写入一个字符。

打开文件
当我们用这样的语法打开二进制文件:

ofstream fout("file.dat", ios::binary); 

  "ios::binary"是你提供的打开选项的额外标志. 默认的, 文件以ASCII方式打开, 不存在则创建, 存在就覆盖. 这里有些额外的标志用来改变选项。

ios::app 添加到文件尾
ios::ate 把文件标志放在末尾而非起始。
ios::trunc 默认. 截断并覆写文件。
ios::nocreate 文件不存在也不创建。
ios::noreplace    文件存在则失败。

文件状态
我用过的唯一一个状态函数是eof(), 它返回是否标志已经到了文件末尾。 我主要用在循环中。 例如, 这个代码断统计小写‘e’ 在文件中出现的次数。

ifstream fin("file.txt"); char ch; int counter; while (!fin.eof()) {      ch = fin.get();       if (ch == ‘e’) counter++; }fin.close(); 

  我从未用过这里没有提到的其他方法。 还有很多方法,但是他们很少被使用。参考C++书籍或者文件流的帮助文档来了解其他的方法。

posted @ 2010-08-08 17:37 xiaoxinchen 阅读(174) | 评论 (0)编辑 收藏

什么是Socket
Socket接口是TCP/IP网络的API,Socket接口定义了许多函数或例程,程序员可以用它们来开发TCP/IP网络上的应用程序。要学Internet上的TCP/IP网络编程,必须理解Socket接口。
Socket接口设计者最先是将接口放在Unix操作系统里面的。如果了解Unix系统的输入和输出的话,就很容易了解Socket了。网络的 Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用Socket(),该函数返 回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。常用的Socket类型有两种:流式Socket (SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;数据 报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。

Socket建立
为了建立Socket,程序可以调用Socket函数,该函数返回一个类似于文件描述符的句柄。socket函数原型为:
int socket(int domain, int type, int protocol);
domain指明所使用的协议族,通常为PF_INET,表示互联网协议族(TCP/IP协议族);type参数指定socket的类型: SOCK_STREAM 或SOCK_DGRAM,Socket接口还定义了原始Socket(SOCK_RAW),允许程序使用低层协议;protocol通常赋值"0"。 Socket()调用返回一个整型socket描述符,你可以在后面的调用使用它。
Socket描述符是一个指向内部数据结构的指针,它指向描述符表入口。调用Socket函数时,socket执行体将建立一个Socket,实际上"建立一个Socket"意味着为一个Socket数据结构分配存储空间。Socket执行体为你管理描述符表。
两个网络程序之间的一个网络连接包括五种信息:通信协议、本地协议地址、本地主机端口、远端主机地址和远端协议端口。Socket数据结构中包含这五种信息。

Socket配置
通过socket调用返回一个socket描述符后,在使用socket进行网络传输以前,必须配置该socket。面向连接的socket客户端通过 调用Connect函数在socket数据结构中保存本地和远端信息。无连接socket的客户端和服务端以及面向连接socket的服务端通过调用 bind函数来配置本地信息。
Bind函数将socket与本机上的一个端口相关联,随后你就可以在该端口监听服务请求。Bind函数原型为:
int bind(int sockfd,struct sockaddr *my_addr, int addrlen);
Sockfd是调用socket函数返回的socket描述符,my_addr是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针;addrlen常被设置为sizeof(struct sockaddr)。
struct sockaddr结构类型是用来保存socket信息的:
struct sockaddr {
unsigned short sa_family; /* 地址族, AF_xxx */
char sa_data[14]; /* 14 字节的协议地址 */
};
sa_family一般为AF_INET,代表Internet(TCP/IP)地址族;sa_data则包含该socket的IP地址和端口号。
另外还有一种结构类型:
struct sockaddr_in {
short int sin_family; /* 地址族 */
unsigned short int sin_port; /* 端口号 */
struct in_addr sin_addr; /* IP地址 */
unsigned char sin_zero[8]; /* 填充0 以保持与struct sockaddr同样大小 */
};
这个结构更方便使用。sin_zero用来将sockaddr_in结构填充到与struct sockaddr同样的长度,可以用bzero()或memset()函数将其置为零。指向sockaddr_in 的指针和指向sockaddr的指针可以相互转换,这意味着如果一个函数所需参数类型是sockaddr时,你可以在函数调用的时候将一个指向 sockaddr_in的指针转换为指向sockaddr的指针;或者相反。
使用bind函数时,可以用下面的赋值实现自动获得本机IP地址和随机获取一个没有被占用的端口号:
my_addr.sin_port = 0; /* 系统随机选择一个未被使用的端口号 */
my_addr.sin_addr.s_addr = INADDR_ANY; /* 填入本机IP地址 */
通过将my_addr.sin_port置为0,函数会自动为你选择一个未占用的端口来使用。同样,通过将my_addr.sin_addr.s_addr置为INADDR_ANY,系统会自动填入本机IP地址。
注意在使用bind函数是需要将sin_port和sin_addr转换成为网络字节优先顺序;而sin_addr则不需要转换。
计算机数据存储有两种字节优先顺序:高位字节优先和低位字节优先。Internet上数据以高位字节优先顺序在网络上传输,所以对于在内部是以低位字节优先方式存储数据的机器,在Internet上传输数据时就需要进行转换,否则就会出现数据不一致。
下面是几个字节顺序转换函数:
·htonl():把32位值从主机字节序转换成网络字节序
·htons():把16位值从主机字节序转换成网络字节序
·ntohl():把32位值从网络字节序转换成主机字节序
·ntohs():把16位值从网络字节序转换成主机字节序
Bind()函数在成功被调用时返回0;出现错误时返回"-1"并将errno置为相应的错误号。需要注意的是,在调用bind函数时一般不要将端口号置为小于1024的值,因为1到1024是保留端口号,你可以选择大于1024中的任何一个没有被占用的端口号。

连接建立
面向连接的客户程序使用Connect函数来配置socket并与远端服务器建立一个TCP连接,其函数原型为:
int connect(int sockfd, struct sockaddr *serv_addr,int addrlen);
Sockfd 是socket函数返回的socket描述符;serv_addr是包含远端主机IP地址和端口号的指针;addrlen是远端地质结构的长度。 Connect函数在出现错误时返回-1,并且设置errno为相应的错误码。进行客户端程序设计无须调用bind(),因为这种情况下只需知道目的机器 的IP地址,而客户通过哪个端口与服务器建立连接并不需要关心,socket执行体为你的程序自动选择一个未被占用的端口,并通知你的程序数据什么时候到 打断口。
Connect函数启动和远端主机的直接连接。只有面向连接的客户程序使用socket时才需要将此socket与远端主机相连。无连接协议从不建立直接连接。面向连接的服务器也从不启动一个连接,它只是被动的在协议端口监听客户的请求。
Listen函数使socket处于被动的监听模式,并为该socket建立一个输入数据队列,将到达的服务请求保存在此队列中,直到程序处理它们。
int listen(int sockfd, int backlog);
Sockfd 是Socket系统调用返回的socket 描述符;backlog指定在请求队列中允许的最大请求数,进入的连接请求将在队列中等待accept()它们(参考下文)。Backlog对队列中等待 服务的请求的数目进行了限制,大多数系统缺省值为20。如果一个服务请求到来时,输入队列已满,该socket将拒绝连接请求,客户将收到一个出错信息。
当出现错误时listen函数返回-1,并置相应的errno错误码。
accept()函数让服务器接收客户的连接请求。在建立好输入队列后,服务器就调用accept函数,然后睡眠并等待客户的连接请求。
int accept(int sockfd, void *addr, int *addrlen);
sockfd是被监听的socket描述符,addr通常是一个指向sockaddr_in变量的指针,该变量用来存放提出连接请求服务的主机的信息(某 台主机从某个端口发出该请求);addrten通常为一个指向值为sizeof(struct sockaddr_in)的整型指针变量。出现错误时accept函数返回-1并置相应的errno值。
首先,当accept函数监视的 socket收到连接请求时,socket执行体将建立一个新的socket,执行体将这个新socket和请求连接进程的地址联系起来,收到服务请求的 初始socket仍可以继续在以前的 socket上监听,同时可以在新的socket描述符上进行数据传输操作。

数据传输
Send()和recv()这两个函数用于面向连接的socket上进行数据传输。
Send()函数原型为:
int send(int sockfd, const void *msg, int len, int flags);
Sockfd是你想用来传输数据的socket描述符;msg是一个指向要发送数据的指针;Len是以字节为单位的数据的长度;flags一般情况下置为0(关于该参数的用法可参照man手册)。
Send()函数返回实际上发送出的字节数,可能会少于你希望发送的数据。在程序中应该将send()的返回值与欲发送的字节数进行比较。当send()返回值与len不匹配时,应该对这种情况进行处理。
char *msg = "Hello!";
int len, bytes_sent;
……
len = strlen(msg);
bytes_sent = send(sockfd, msg,len,0);
……
recv()函数原型为:
int recv(int sockfd,void *buf,int len,unsigned int flags);
Sockfd是接受数据的socket描述符;buf 是存放接收数据的缓冲区;len是缓冲的长度。Flags也被置为0。Recv()返回实际上接收的字节数,当出现错误时,返回-1并置相应的errno值。
Sendto()和recvfrom()用于在无连接的数据报socket方式下进行数据传输。由于本地socket并没有与远端机器建立连接,所以在发送数据时应指明目的地址。
sendto()函数原型为:
int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen);
该函数比send()函数多了两个参数,to表示目地机的IP地址和端口号信息,而tolen常常被赋值为sizeof (struct sockaddr)。Sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。
Recvfrom()函数原型为:
int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen);
from是一个struct sockaddr类型的变量,该变量保存源机的IP地址及端口号。fromlen常置为sizeof (struct sockaddr)。当recvfrom()返回时,fromlen包含实际存入from中的数据字节数。Recvfrom()函数返回接收到的字节数或 当出现错误时返回-1,并置相应的errno。
如果你对数据报socket调用了connect()函数时,你也可以利用send()和recv()进行数据传输,但该socket仍然是数据报socket,并且利用传输层的UDP服务。但在发送或接收数据报时,内核会自动为之加上目地和源地址信息。

结束传输
当所有的数据操作结束以后,你可以调用close()函数来释放该socket,从而停止在该socket上的任何数据操作:
close(sockfd);
你也可以调用shutdown()函数来关闭该socket。该函数允许你只停止在某个方向上的数据传输,而一个方向上的数据传输继续进行。如你可以关闭某socket的写操作而允许继续在该socket上接受数据,直至读入所有数据。
int shutdown(int sockfd,int how);
Sockfd是需要关闭的socket的描述符。参数 how允许为shutdown操作选择以下几种方式:
·0-------不允许继续接收数据
·1-------不允许继续发送数据
·2-------不允许继续发送和接收数据,
·均为允许则调用close ()
shutdown在操作成功时返回0,在出现错误时返回-1并置相应errno。

Socket编程实例
代码实例中的服务器通过socket连接向客户端发送字符串"Hello, you are connected!"。只要在服务器上运行该服务器软件,在客户端运行客户软件,客户端就会收到该字符串。
该服务器软件代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#define SERVPORT 3333 /*服务器监听端口号 */
#define BACKLOG 10 /* 最大同时连接请求数 */
main()
{
int sockfd,client_fd; /*sock_fd:监听socket;client_fd:数据传输socket */
struct sockaddr_in my_addr; /* 本机地址信息 */
struct sockaddr_in remote_addr; /* 客户端地址信息 */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket创建出错!"); exit(1);
}
my_addr.sin_family=AF_INET;
my_addr.sin_port=htons(SERVPORT);
my_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(my_addr.sin_zero),8);
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) {
perror("bind出错!");
exit(1);
}
if (listen(sockfd, BACKLOG) == -1) {
perror("listen出错!");
exit(1);
}
while(1) {
sin_size = sizeof(struct sockaddr_in);
if ((client_fd = accept(sockfd, (struct sockaddr *)&remote_addr, &sin_size)) == -1) {
perror("accept出错");
continue;
}
printf("received a connection from %s\n", inet_ntoa(remote_addr.sin_addr));
if (!fork()) { /* 子进程代码段 */
if (send(client_fd, "Hello, you are connected!\n", 26, 0) == -1)
perror("send出错!");
close(client_fd);
exit(0);
}
close(client_fd);
}
}
}
服务器的工作流程是这样的:首先调用socket函数创建一个Socket,然后调用bind函数将其与本机地址以及一个本地端口号绑定,然后调用 listen在相应的socket上监听,当accpet接收到一个连接服务请求时,将生成一个新的socket。服务器显示该客户机的IP地址,并通过 新的socket向客户端发送字符串"Hello,you are connected!"。最后关闭该socket。
代码实例中的fork()函数生成一个子进程来处理数据传输部分,fork()语句对于子进程返回的值为0。所以包含fork函数的if语句是子进程代码部分,它与if语句后面的父进程代码部分是并发执行的。

客户端程序代码如下:
#include<stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define SERVPORT 3333
#define MAXDATASIZE 100 /*每次最大数据传输量 */
main(int argc, char *argv[]){
int sockfd, recvbytes;
char buf[MAXDATASIZE];
struct hostent *host;
struct sockaddr_in serv_addr;
if (argc < 2) {
fprintf(stderr,"Please enter the server's hostname!\n");
exit(1);
}
if((host=gethostbyname(argv[1]))==NULL) {
herror("gethostbyname出错!");
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
perror("socket创建出错!");
exit(1);
}
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(SERVPORT);
serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(serv_addr.sin_zero),8);
if (connect(sockfd, (struct sockaddr *)&serv_addr, \
sizeof(struct sockaddr)) == -1) {
perror("connect出错!");
exit(1);
}
if ((recvbytes=recv(sockfd, buf, MAXDATASIZE, 0)) ==-1) {
perror("recv出错!");
exit(1);
}
buf[recvbytes] = '\0';
printf("Received: %s",buf);
close(sockfd);
}
客户端程序首先通过服务器域名获得服务器的IP地址,然后创建一个socket,调用connect函数与服务器建立连接,连接成功之后接收从服务器发送过来的数据,最后关闭socket。
函数gethostbyname()是完成域名转换的。由于IP地址难以记忆和读写,所以为了方便,人们常常用域名来表示主机,这就需要进行域名和IP地址的转换。函数原型为:
struct hostent *gethostbyname(const char *name);
函数返回为hosten的结构类型,它的定义如下:
struct hostent {
char *h_name; /* 主机的官方域名 */
char **h_aliases; /* 一个以NULL结尾的主机别名数组 */
int h_addrtype; /* 返回的地址类型,在Internet环境下为AF-INET */
int h_length; /* 地址的字节长度 */
char **h_addr_list; /* 一个以0结尾的数组,包含该主机的所有地址*/
};
#define h_addr h_addr_list[0] /*在h-addr-list中的第一个地址*/
当 gethostname()调用成功时,返回指向struct hosten的指针,当调用失败时返回-1。当调用gethostbyname时,你不能使用perror()函数来输出错误信息,而应该使用herror()函数来输出。

  无连接的客户/服务器程序的在原理上和连接的客户/服务器是一样的,两者的区别在于无连接的客户/服务器中的客户一般不需要建立连接,而且在发送接收数据时,需要指定远端机的地址。

阻塞和非阻塞
阻塞函数在完成其指定的任务以前不允许程序调用另一个函数。例如,程序执行一个读数据的函数调用时,在此函数完成读操作以前将不会执行下一程序语句。当 服务器运行到accept语句时,而没有客户连接服务请求到来,服务器就会停止在accept语句上等待连接服务请求的到来。这种情况称为阻塞 (blocking)。而非阻塞操作则可以立即完成。比如,如果你希望服务器仅仅注意检查是否有客户在等待连接,有就接受连接,否则就继续做其他事情,则 可以通过将Socket设置为非阻塞方式来实现。非阻塞socket在没有客户在等待时就使accept调用立即返回。
#include <unistd.h>
#include <fcntl.h>
……
sockfd = socket(AF_INET,SOCK_STREAM,0);
fcntl(sockfd,F_SETFL,O_NONBLOCK);
……
通过设置socket为非阻塞方式,可以实现"轮询"若干Socket。当企图从一个没有数据等待处理的非阻塞Socket读入数据时,函数将立即返 回,返回值为-1,并置errno值为EWOULDBLOCK。但是这种"轮询"会使CPU处于忙等待方式,从而降低性能,浪费系统资源。而调用 select()会有效地解决这个问题,它允许你把进程本身挂起来,而同时使系统内核监听所要求的一组文件描述符的任何活动,只要确认在任何被监控的文件 描述符上出现活动,select()调用将返回指示该文件描述符已准备好的信息,从而实现了为进程选出随机的变化,而不必由进程本身对输入进行测试而浪费 CPU开销。Select函数原型为:
int select(int numfds,fd_set *readfds,fd_set *writefds,
fd_set *exceptfds,struct timeval *timeout);
其中readfds、writefds、exceptfds分别是被select()监视的读、写和异常处理的文件描述符集合。如果你希望确定是否可以 从标准输入和某个socket描述符读取数据,你只需要将标准输入的文件描述符0和相应的sockdtfd加入到readfds集合中;numfds的值 是需要检查的号码最高的文件描述符加1,这个例子中numfds的值应为sockfd+1;当select返回时,readfds将被修改,指示某个文件 描述符已经准备被读取,你可以通过FD_ISSSET()来测试。为了实现fd_set中对应的文件描述符的设置、复位和测试,它提供了一组宏:
FD_ZERO(fd_set *set)----清除一个文件描述符集;
FD_SET(int fd,fd_set *set)----将一个文件描述符加入文件描述符集中;
FD_CLR(int fd,fd_set *set)----将一个文件描述符从文件描述符集中清除;
FD_ISSET(int fd,fd_set *set)----试判断是否文件描述符被置位。
Timeout参数是一个指向struct timeval类型的指针,它可以使select()在等待timeout长时间后没有文件描述符准备好即返回。struct timeval数据结构为:
struct timeval {
int tv_sec; /* seconds */
int tv_usec; /* microseconds */
};

POP3客户端实例
下面的代码实例基于POP3的客户协议,与邮件服务器连接并取回指定用户帐号的邮件。与邮件服务器交互的命令存储在字符串数组POPMessage中,程序通过一个do-while循环依次发送这些命令。
#include<stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define POP3SERVPORT 110
#define MAXDATASIZE 4096

main(int argc, char *argv[]){
int sockfd;
struct hostent *host;
struct sockaddr_in serv_addr;
char *POPMessage[]={
"USER userid\r\n",
"PASS password\r\n",
"STAT\r\n",
"LIST\r\n",
"RETR 1\r\n",
"DELE 1\r\n",
"QUIT\r\n",
NULL
};
int iLength;
int iMsg=0;
int iEnd=0;
char buf[MAXDATASIZE];

if((host=gethostbyname("your.server"))==NULL) {
perror("gethostbyname error");
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
perror("socket error");
exit(1);
}
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(POP3SERVPORT);
serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(serv_addr.sin_zero),8);
if (connect(sockfd, (struct sockaddr *)&serv_addr,sizeof(struct sockaddr))==-1){
perror("connect error");
exit(1);
}

do {
send(sockfd,POPMessage[iMsg],strlen(POPMessage[iMsg]),0);
printf("have sent: %s",POPMessage[iMsg]);

iLength=recv(sockfd,buf+iEnd,sizeof(buf)-iEnd,0);
iEnd+=iLength;
buf[iEnd]='\0';
printf("received: %s,%d\n",buf,iMsg);

iMsg++;
} while (POPMessage[iMsg]);

close(sockfd);
}

来自:Li

posted @ 2010-03-21 21:01 xiaoxinchen 阅读(213) | 评论 (0)编辑 收藏
总体思路是先打成jar再把jar打成exe。主要看1.3和2.3里的内容就可以了。


1.将项目打成jar:


1.1 要将项目打包成jar文件,方法很多,可以用Eclipse自带的打包工具Ant打包,也可以用Eclipse的Export生成jar。经过尝试后,我 不推荐用Ant打包,因为要自己编写xml脚本语言,还要增加一些外部的jar,所以我打了好几次都没打成。


1.2 在这里介绍两种方法生成jar,第一种是用Eclpise的Export功能。在要打包的项目上击右键,选择Export,在窗口中选择Java里的 JAR file。Next后的窗口中已经自动选好了要打包的项目,用户可以点击加号查看项目里被打包的内容。在下面的JAR file里设置你打包生成jar文件的输出目录,下一步在出现的窗口中选择Use existing manifest from workspace,在下面的Main class后面直接点Browse,它会自动列出你项目中有主函数main的类。选择主类后点Finish即可生成jar文件。在此说明一下,这种打包方 法不能把项目中的外部的jar包打进来,因该是也要编写一些脚本语言,没往深研究。所以生成后的jar有些是不能执行的。


1.3 第二种方法是利用Eclipse的一个第三方插件fatjar生成jar文件,也是本人觉得最简单最方便的一种生成方式。先从网上下载些 插件,解压后是一个plugins的文件夹,里面只有一个文件夹,我的是“net.sf.fjep.fatjar_0.0.24”将它copy到 Eclipser plugins文件夹下,此插件就安装成功了,重启Eclipse在项目上右击就会看到多出一个“Build Fat Jar”在前面有个绿色的“+”号,这时你就可以用此插件打包你的项目了。进去后第一个界面Jar-Name里增入要生成的jar文件名,我的是 “CAMP_fat.jar”。在Main-Class后点Browse像Export一样它也会列出你项目中的主类,选择后其它默认即可,Next后会 列出你要打包的所有内容,这个插件的优势就是可以将你项目中的外部jar也打进来,有三个先项,其中Export ANT是生成build.xml脚本文件,方便用户以后修改脚本,其它两个按钮没用。在这里什么都不点,直接点Finish就可以生成jar文件。


2.将jar打成.exe文件:


2.1 虽然此时的jar文件已经可以执行了。生成.exe的文件我也是用两种方法实现的,用到的打包工具是j2ewiz和exe4j,它们的不同会在我下面的介 绍中体现出来。


2.2 首先是j2ewiz,这个软件是绿色的,不用安装,解压后可以直接运行,但这个软件生成的 .exe文件不是跨平台的。运行此程序首先就是输入要打包的jar文件,我们浏览JAR选择我们之前用fatjar生成的“CAMP_fat.jar”项 目文件(详见1.3),下面那个选项是提示用户最低要求的JRE版本,一般选1.3。下一步,因为我们的寝室管理系统是图形界面,所以在这里选 “Windows窗口程序”下一步它也是自动生成要执行的主类,你只要选择就可以。下面的选框可以选择你启动程序显示的图片。下一步后这个窗可按个人喜好 选择。下一步,如果你的程序还有什么依赖的外部jar文件,可以从这里加上,但因为之前的fatjar以经将我们项目所用的那三个连数据库的外部类打进 CAMP_fat.jar包里了,所以这里不用再添加。如果你之前是用Export打的jar 包,那么这里就需要再把那个三个数据库的包加进来了(详见1.2)。下一步是添入要生成的.exe文件名,再选一个程序图标就可以了,下一步后生 成.exe文件,点完成。双击生成的.exe文件就能看到运行效果了,这种exe文件还没有脱离JDK环境,还不能跨平台使用,只能用于小组成员测试使 用。


2.3 下面进入最关键的,如何打包跨平台的.exe文件。用到的软件是exe4j,我用的是V4.0版的,此软件需要破解。安装后运行左窗窗口标有十步,其实打 包过程也非常简单。第一步完全略过,直接点Next第二步我们选择“JAR in EXE mode” 就是选择我们已经有制作好的jar文件。第3步上面是项目名称,可随便填写,下面一个写出你想要将打包后的exe文件输出的目录我的是“桌 面\project\”。第4步,由于我的演示程序是图形的,所以选第一个,如果你的程序是控制台的,则选择第二个,Executable name写你将要生成的.exe文件的名字,Icon File可以选择生成文件的图标。第5步,先别管上面的,先在下面单击绿色的“+”号,在弹出的窗口中点Archive,然后找到起初已经做好的 CAMP_fat.jar(详见1.3)文件,"OK"后返回,在下面的Class Path里就出现jar文件路径后,再在上面Main Class栏内点击找到main所在的类。第6步,你系统的JRE版本,一般是填个1.3,下面填1.6在这里单击advanced options,选择search sequence。选这个就是因为我们要把JDK环境也打包进来,好让程序能跨平台使用。首先要从你系统的JDK下的JRE目录copy到你.exe文件 的输出目录下“桌面\project\JRE”,然后回到exe4j中在弹出窗口删除列表中的所有项。我的是三项,一个注册表的,一个JAVA环境变量 的,一个JDK环境变量的,都不要。然后单击绿“+”,选择directory并选择JRE的根目录,我的是“桌面\project\JRE”就是 copy后的目录,选完后exe4j弹出窗口中的Directory里会显示“.\JRE”。点OK关闭该窗口,返回exe4j的主窗口,你就可以看到刚 加的路径。再从主窗口左侧窗口中单击advanced options,并选择preferred VM,在弹出的窗口中选择client hostspot VM,单击next按钮继续。7、8步是一些个性设置默认即可。第9步编译完后第10步你点那个“Click Here to Start the Application”按钮就可以看到程序运行效果了,然后再点”Seave as”保存一个exe4j生成的一个文件,随便存哪里都行,和我们的.exe程序无关。全部制作过程就完工了。
posted @ 2010-03-13 18:06 xiaoxinchen 阅读(3898) | 评论 (0)编辑 收藏
首先要弄清楚,在Linux系统中,内核为每一个新创建的文件分配一个Inode(索引结点),每个文件都有一个惟一的inode号。文件属性保存在索引结点里,在访问文件时,索引结点被复制到内存在,从而实现文件的快速访问。

链接是一种在共享文件和访问它的用户的若干目录项之间建立联系的一种方法。Linux中包括两种链接:硬链接(Hard Link)和软链接(Soft Link),软链接又称为符号链接(Symbolic link)。

一、软链接(符号链接)

软链接克服了硬链接的不足,没有任何文件系统的限制,任何用户可以创建指向目录的符号链接。因而现在更为广泛使用,它具有更大的灵活性,甚至可以跨越不同机器、不同网络对文件进行链接。

建立软链接,只要在ln后面加上选项 –s。



二、硬链接

硬链接说白了是一个指针,指向文件索引节点,系统并不为它重新分配inode。可以用:ln命令来建立硬链接。语法

ln [options] existingfile newfile
ln[options] existingfile-list directory

用法: 第一种:为”existingfile”创建硬链接,文件名为”newfile”。第二种:在”directory”目录中, 为”existingfile-list”中包含的所有文件创建一个同名的硬链接。常用可选[options] –f 无论”newfile”存在与否,都创建链接。-n 如果”newfile”已存在,就不创建链接。 
posted @ 2010-03-11 16:07 xiaoxinchen 阅读(212) | 评论 (0)编辑 收藏
(1)Jre 是java runtime environment, 是java程序的运行环境。既然是运行,当然要包含jvm,也就是大家熟悉的虚拟机啦, 还有所有java类库的class文件,都在lib目录下打包成了jar。大家可以自己验证。至于在windows上的虚拟机是哪个文件呢? 学过MFC的都知道什么是dll文件吧,那么大家看看jre/bin/client里面是不是有一个jvm.dll呢?那就是虚拟机。

(2)Jdk 是java development kit,是java的开发工具包,里面包含了各种类库和工具。当然也包括了另外一个Jre. 那么为什么要包括另外一个Jre呢?而且jdk/jre/bin同时有client和server两个文件夹下都包含一个jvm.dll。 说明是有两个虚拟机的。这一点不知道大家是否注意到了呢?
  相信大家都知道jdk的bin下有各种java程序需要用到的命令,与jre的bin目录最明显的区别就是jdk下才有javac,这一点很好理解,因为 jre只是一个运行环境而已。与开发无关,正因为如此,具备开发功能的jdk自己的jre下才会同时有client性质的jvm和server性质的jvm, 而仅仅作为运行环境的jre下只需要client性质的jvm.dll就够了。

(3)记得在环境变量path中设置jdk/bin路径麽?这应该是大家学习Java的第一步吧, 老师会告诉大家不设置的话javac和java是用不了的。确实jdk/bin目录下包含了所有的命令。可是有没有人想过我们用的java命令并不是 jdk/bin目录下的而是jre/bin目录下的呢?不信可以做一个实验,大家可以把jdk/bin目录下的java.exe剪切到别的地方再运行 java程序,发现了什么?一切OK!
  那么有人会问了?我明明没有设置jre/bin目录到环境变量中啊?
试想一下如果java为了提供给大多数人使用,他们是不需要jdk做开发的,只需要jre能让java程序跑起来就可以了,那么每个客户还需要手 动去设置环境变量多麻烦啊?所以安装jre的时候安装程序自动帮你把jre的java.exe添加到了系统变量中,验证的方法很简单,大家看到了系统环境 变量的 path最前面有“%SystemRoot%\system32;%SystemRoot%;”这样的配置,那么再去Windows/system32下 面去看看吧,发现了什么?有一个java.exe。
  如果强行能够把jdk/bin挪到system32变量前面,当然也可以迫使使用jdk/jre里面的java,不过除非有必要,我不建议大家这么做。使用单独的jre跑java程序也算是客户环境下的一种测试。 
posted @ 2010-03-11 12:27 xiaoxinchen 阅读(267) | 评论 (0)编辑 收藏

关于2009年12月全国大学英语四、六级考试成绩发布时间的通知:

2009年12月全国大学英语四、六级考试成绩将于 2010年3月3日上午9点发布。

成绩查询方式

网上免费查分:

网址: cet.99sushe.com

运营商: 99宿舍网

客服电话: 010-58699163转867

收费短信查分(2010年3月3日上午9点开始):

中国移动、联通、电信手机用户:

发送A 加 15位准考证号到 1066335577

如A123456789012345到 1066335577查询成绩(1元/条,不含通信费)

特别注意:

河北省的中国移动手机用户:发送 8 加 15位准考证号到 10661660

如8123456789012345到 10661660 查询成绩(1元/条,不含通信费)

运营商: 空中网

客服电话: 010-68083018

注:2009年12月网考成绩发布方式和日期另行通知。

全国大学英语四、六级考试委员会办公室

2010年2月24日

posted @ 2010-03-03 00:50 xiaoxinchen 阅读(225) | 评论 (0)编辑 收藏
在许多平台中,Browser控件皆被做为一个必需的控件给出,并提供了DOM接口,用于访问Browser的内容,相对来说SWT中的Browser控件就比较薄弱,没有提供DOM的可控制接口,那么,如何和控件所加载的页面进行交互呢?比如需要在集成web应用环境中实现模仿登陆、自动填表等功能
SWT中对Browser有不同的实现,目前实现的有IE和Mozilla。在Browser的构造函数中根据不同的平台和不同的style设置类决定使用哪个类的实现。

org.eclipse.swt.browser.Mozilla org.eclipse.swt.browser.IE 是已经实现的,而其他的 org.eclipse.swt.browser.Safari org.eclipse.swt.browser.Voyager
来源:www.va1314.com/bc
则没有实现。


public Browser (Composite parent, int style) {

super (checkParent (parent), checkStyle (style));

String platform = SWT.getPlatform ();

Display display = parent.getDisplay ();

if ("gtk".equals (platform)) display.setData (NO_INPUT_METHOD, null); //$NON-NLS-1$

String className = null;

if ((style & SWT.MOZILLA) != 0) {

className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$

} else {

if ("win32".equals (platform) || "wpf".equals (platform)) { //$NON-NLS-1$ $NON-NLS-2$

className = "org.eclipse.swt.browser.IE"; //$NON-NLS-1$

} else if ("motif".equals (platform)) { //$NON-NLS-1$

className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$

} else if ("gtk".equals (platform)) { //$NON-NLS-1$

className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$

} else if ("carbon".equals (platform)) { //$NON-NLS-1$

className = "org.eclipse.swt.browser.Safari"; //$NON-NLS-1$

} else if ("photon".equals (platform)) { //$NON-NLS-1$

className = "org.eclipse.swt.browser.Voyager"; //$NON-NLS-1$

} else {

dispose ();

SWT.error (SWT.ERROR_NO_HANDLES);

}

}



try {

Class clazz = Class.forName (className);

webBrowser = (WebBrowser)clazz.newInstance ();

} catch (ClassNotFoundException e) {

} catch (IllegalAccessException e) {

} catch (InstantiationException e) {

}

if (webBrowser == null) {

dispose ();

SWT.error (SWT.ERROR_NO_HANDLES);

}



webBrowser.setBrowser (this);

webBrowser.create (parent, style);

}

public Browser (Composite parent, int style) {

super (checkParent (parent), checkStyle (style));

String platform = SWT.getPlatform ();

Display display = parent.getDisplay ();

if ("gtk".equals (platform)) display.setData (NO_INPUT_METHOD, null); //$NON-NLS-1$

String className = null;

if ((style & SWT.MOZILLA) != 0) {

className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$

} else {

if ("win32".equals (platform) || "wpf".equals (platform)) { //$NON-NLS-1$ $NON-NLS-2$

className = "org.eclipse.swt.browser.IE"; //$NON-NLS-1$

} else if ("motif".equals (platform)) { //$NON-NLS-1$

className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$

} else if ("gtk".equals (platform)) { //$NON-NLS-1$

className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$

} else if ("carbon".equals (platform)) { //$NON-NLS-1$

className = "org.eclipse.swt.browser.Safari"; //$NON-NLS-1$

} else if ("photon".equals (platform)) { //$NON-NLS-1$

className = "org.eclipse.swt.browser.Voyager"; //$NON-NLS-1$

} else {

dispose ();

SWT.error (SWT.ERROR_NO_HANDLES);

}

}



try {

Class clazz = Class.forName (className);

webBrowser = (WebBrowser)clazz.newInstance ();

} catch (ClassNotFoundException e) {

} catch (IllegalAccessException e) {

} catch (InstantiationException e) {

}

if (webBrowser == null) {

dispose ();

SWT.error (SWT.ERROR_NO_HANDLES);

}



webBrowser.setBrowser (this);

webBrowser.create (parent, style);

}

其中对IE的实现主要是采用调用IE的Activex控件,间接加载IE,对Mozilla由于代码过多,本人没有具体研究,其本身开源,有兴趣能够参看。

那么回归主题,如何实现与Browser控件的交互呢? 其实仔细看Browser控件的API,能够发觉一个execute()方法,这个方法适用于在web文档加载完毕时能够运行javascript code的。这样的话,交互就变得简单了,因为javascript是提供dom的支持的,既然能够调用javascript,那么就能够调用web页面 中的每个节点了。控制的问题处理了,可是另外的问题来了。 如何从javascript的code里边前往数据呢? 比如我需要将一个<input type=text id=textid />的值前往到java code中。其实采用的方法是很投机的,因为execute()方法前往的结果是true or false,那么对它做文章是没有用的,我们看其他的api,能够发觉:addStatusTextListener()方法。 这个方法能够监听web页面对于statusbar文本改变的值,并反映在java code里面,那么我们只需通过javascript把前往的值写到window.status,那么就能够在javacode里取到了。 具体代码请参考下面,对于Browser的承继重写,通过getValue能够取得指定id的html 控件的值,通过setValue能够设置值。 view plaincopy to clipboardprint?

import org.eclipse.swt.browser.Browser;

import org.eclipse.swt.browser.StatusTextEvent;

import org.eclipse.swt.browser.StatusTextListener;

import org.eclipse.swt.widgets.Composite;



public class CoolBrowser extends Browser implements StatusTextListener {



private final String DATA = "Browser_Data";



public CoolBrowser(Composite parent, int style) {

super(parent, style);

addStatusTextListener(this);

}



@Override

protected void checkSubclass() {

}



/**

* Get the value of one input control in the web

* @param id

* @return

*/

public String getValue(String id) {

if (execute("var obj = document.getElementById('" + id + "');"

+ "if( obj != null ) window.status=obj.value;")) {

return (String) getData(DATA);

}

return null;

}



/**

* Set the value of the input control

* @param id

* @param value

*/

public void setValue( String id, Object value ){

if (execute("var obj = document.getElementById('" + id + "');"

+ "if( obj != null ) obj.value='" + value + "';")) {

}

}



@Override

public void changed(StatusTextEvent event) {

setData(DATA, event.text);

}



}
posted @ 2009-12-29 16:28 xiaoxinchen 阅读(5260) | 评论 (1)编辑 收藏
下载地址:http://www.iplaysoft.com/windows7-msdn-iso.html
posted @ 2009-12-20 20:14 xiaoxinchen 阅读(399) | 评论 (0)编辑 收藏
  Proxy代理模式是一种结构型设计模式,主要解决的问题是:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。如下图:
   



       比如说C和A不在一个服务器上,A要频繁的调用C,我们可以在A上做一个代理类Proxy,把访问C的工作交给Proxy,这样对于A来说,就好像在直接访问C的对象。在对A的开发中我们可以把注意力完全放在业务的实现上。

       GoF《设计模式》中说道:为其他对象提供一种代理以控制这个对象的访问。

       Proxy模式的结构:
   



       下面通过一个场景来看看Proxy的实现,我们要使用代理类型ProxyClass的对象调用远程机器上的一个类型LongDistanceClass的对象。

    首先我们先模拟一个远程的类型:为了保持对被代理对象使用的透明性,我们使代理类型和被代理类型同时继承同一个接口IProxy

    接口实现:

    interface IProxy

    {

        string Function1();

        string Function2();

    }

    远程对象实现:

    /// <summary>

    /// 模拟的远程对象

    /// </summary>

    public class LongDistanceClass:IProxy

    {

        #region IProxy 成员

        public string Function1()

        {

            //do someting

            return "LongDistanceClass.Function1";

        }

        public string Function2()

        {

            //do someting

            return "LongDistanceClass.Function2";

        }

        #endregion

    }

    接下来就要实现代理类型,使用代理对象访问模拟的远程对象,代理类型实现如下:

    public class ProxyClass:IProxy

    {

        #region IProxy 成员

        public string Function1()

        {

            //to access LongDistanceClass.Function1

            LongDistanceClass obj = new LongDistanceClass();

            return obj.Function1();

        }

        public string Function2()

        {

            //to access LongDistanceClass.Function2

            LongDistanceClass obj = new LongDistanceClass();

            return obj.Function2();

        }

        #endregion

    }

 

    最后实现客户端代码:

    class Class1

    {

        [STAThread]

        static void Main(string[] args)

        {

            IProxy pro = new ProxyClass();

            Console.WriteLine(pro.Function1());

            Console.WriteLine(pro.Function2());

            Console.Read();

        }

    }

    运行结果如下:

    LongDistanceClass.Function1

LongDistanceClass.Function2

       Proxy模式的要点:

       1、“增加一层间接层”是软件系统中对许多负责问题的一种常见解决方法。在面向对象系统中,直接使用某些对象会带来很多问题,作为间接层的proxy对象便是解决这一问题的常用手段。

       在我们日常的工作中也常常用到代理模式,比如对于三层结构或者N- tiers结构中DAL数据访问层,它把对数据库的访问进行封装。BLL业务层的开发者只是调用DAL中的方法来获得数据。

       在比如前一段时间看了看AOP和Remoting方面的资料,对于跨越应用程序域的访问,要为客户应用程序提供一个TransparentProxy(透明代理),客户程序实际上是通过访问这个代理来访问实际的类型对象。

2、具体proxy设计模式的实现方法、实现粒度都相差很大,有些可能对单个对象作细粒度的控制,有些可能对组件模块提供抽象代理层,在架构层次对对象作proxy。

3、proxy并不一定要求保持接口的一致性,只要能够实现间接控制,有时候损及一些透明性是可以接受的。例如上面的那个例子,代理类型ProxyClass和被代理类型LongDistanceClass可以不用继承自同一个接口,正像GoF《设计模式》中说的:为其他对象提供一种代理以控制这个对象的访问。代理类型从某种角度上讲也可以起到控制被代理类型的访问的作用。
posted @ 2009-08-04 14:44 xiaoxinchen 阅读(139) | 评论 (0)编辑 收藏

1.策略模式-Strategy

策略模式
是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理。
策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。

一句话来形容:准备一组算法,并将每一个算法封装起来,使得他们可以互换


策略模式的结构
策略模式涉及到三个角色:
  • 环境角色:持有一个Strategy类(策略类)的引用
  • 抽象策略角色:策略类,通常由一个接口或者抽象类实现
  • 具体策略角色:包装了相关的算法和行为




《三国演义》中的故事
诸葛亮的精囊妙计三条妙计
走乔国老的后门,求孙国太放人,请孙夫人退兵
赵云按计行事
环境角色:赵云由他来决定选择策略
抽象策略角色:(接口)精囊妙计按计行事(抽象方法)
具体策略角色:三条妙计(单独使用的)


例子:一个策略模式的加减乘除
抽象策略角色: (精囊妙计) Operation抽象类(oper抽象方法)
具体策略角色: (三条妙计) 计算乘积,计算除法,计算加法,计算减法
环境角色:  (赵云)  有一个策略类( Operation )的引用


策略模式的优缺点:
优点:
1.提供了管理相关的算法族的办法。
2.提供了可以替换继承关系的办法。
3.避免使用多重条件转移语句

缺点:
1.客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
2.造成很多的策略类。
posted @ 2009-08-04 12:21 xiaoxinchen 阅读(228) | 评论 (0)编辑 收藏
原则内容:OCP原则就是"开-闭原则",一个软件应该对扩展开放,对修改关闭。
解释 :在设计一个模块的时候,应当使得这个模块可以在不被修改的前提下面被扩展。换言之,应该可以在不必修改源代码的情况下改变这个
模块的行为。这个原则有2点要求:
×:扩展开放
×:关闭修改
满足OCP原则系统的优点:
×:通过扩展已有的软件系统,提供新的行为,可以使得软件系统满足新需求
×:已有的软件模块,特别是重要的抽象层模块不能做变更,这使得变化中的软件系统有一定的稳定性和延续性。
如何实现OCP原则:
1、抽象是关键。
   对需要设计的系统进行抽象,在此前提下面,让抽象的实现机制千变万化。这个抽象层必须预见到所有的可能的扩展,任何实现的改变都不会改变该抽象结构。这样使得系统的抽象层无需修改,从而满足OCP原则的第二条-关闭修改。
2、对可变性的封装原则
   OCP从另一个角度来说,就是EVP(principle of Encapsulation Variation)原则。即找到系统的可变因素,将之封装起来。这个原则意味着2点:
×:一种可变性不应当散落在代码的很多角落里,而应当被封装到一个对象里面。继承应当被看    做是封装变化的方法,而不应当被认为是从一般的对象生成特殊的对象方法。
×:一种可变性不应当与另一种可变性混合在一起。所有的类图的继承结构一般不会超过两层,不然就意味着将两种不同的可变性混合在一起。
与其他设计原则的关系
LSP原则:这个原则是说任何基类出现的地方,子类一定可以出现。
这个原则是对OCP原则的补充,基类和子类的关系就是抽象化的具体体现,所以LSP原则是对实现抽象化的具体步骤的规范。一般来说,违背了LSP原则,一定违反了OCP原则,反之不一定成立。
CARP原则:这个原则讲的是要尽可能的多用合成/聚合,而不是继承关系达到复用的目的。
CARP原则和LSP原则相辅相成。二者都是对实现OCP原则的具体步骤的规范。前者要求设计师首先考虑合成/聚合关系;后者要求在使用继承关系的时候, 必须确定这个关系是符合一定条件的。CARP原则是OCP原则的必要条件,违反了这个原则,就无法使系统实现OCP原则这个目标。
DIP原则:这个原则是说要依赖于抽象,不要依赖于实现。
DIP原则和OCP原则是目标和手段的关系。OCP是目标,DIP是手段。要想实现OCP原则,必须坚持DIP原则。违反了DIP原则,则不可能达到OCP原则要求。

LoD原则:这个原则讲的是一个软件实体应该尽可能少的和其他实体发生作用。
当一个system面临功能扩展的时候,其中会有一些模块,他们需要修改的压力比其他的模块要大一些,如果这些模块是相对孤立的,那么他们就不会将修改的 压力传递给其他模块。根据LoD原则设计的系统,在功能需要扩展的时候,会相对容易的做到对修改的关闭。LoD原则是一条通向OCP原则的道路。
ISP原则:这个原则是说,应当为客户端提供尽可能小的单独接口,而不要提供大的总接口。ISP原则和LoD原则讲的都是一个软件实体与另一个软件实体的通讯限制。广义的LoD原则要求尽可能限制通讯的宽度和深度,ISP原则所限制的是通信宽度。
一个重构方法的讨论
“将条件转移语句改写成为多态性”是一条广为流传的代码重构做法。

这一做法本身并不能保证“开-闭”原则,应当以“开-闭”原则判断是否需要改写成多态。条件转移并不是错误,如果需要,完全可以选择使用条件转移。

如果一个条件转移语句确实封装了某种商务逻辑的可变性,那么将此种可变性封装起来就符合“开-闭”原则设计思想了。如果一个条件转移语句没有涉及重 要的商务逻辑,或者不会随着时间的变化而变化,也不意味着任何的可扩展性,那么它就没有涉及任何有意义的可变性。这时候将这个条件转移语句改写成多态性就 是一种没有意义的浪费。
抽象类应当拥有尽可能多的共同代码

 在一个继承的等级结构中,共同的代码应当尽量向等级结构的上方移动。把重复的代码从子类里面移动到超类里面,可以提高代码的复用率。在代码发生改变时,设计师之需要修改一个地方。

抽象类应当拥有尽可能少的数据

与代码的移动方向相反,数据的移动方向是从抽象类到具体类,向等级结构的下方移动。一个对象的数据不论是否使用都会占用资源,所以应当放到等级结构的低端。

 

什么时候才应当使用继承复用

1.子类是超类的一个特殊种类,而不是超类的一个角色,Is-A才符合继承关系。

2.永远不会出现需要将子类换成另一个类的子类的情况。

3.子类具有扩展超类的责任,而不是具有置换掉(Override)和注销掉(Nullify)超类的责任。

4.只有在分类学角度上有意义时,才可以使用继承,不要从工具类继承。

posted @ 2009-08-03 23:15 xiaoxinchen 阅读(963) | 评论 (0)编辑 收藏
什么是内聚?什么是耦合?
内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述
的是模块内的功能联系; 耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决
于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。
耦合性也称块间联系。指软件系统结构中各模块间相互联系紧密程度的一种度量。模块之间联系越紧密,其耦合性就越强,模块的独立性则越差。模块间耦合高低取决于模块间接口的复杂性、调用的方式及传递的信息。

2. 内聚分为哪几类?耦合分为哪几类?
      内聚有如下的种类,它们之间的内聚度由弱到强排列如下:
(1) 偶然内聚。模块中的代码无法定义其不同功能的调用。但它使该模块能执行不同
的功能,这种模块称为巧合强度模块。
(2) 逻辑内聚。这种模块把几种相关的功能组合在一起, 每次被调用时,由传送给模
块参数来确定该模块应完成哪一种功能
(3) 时间内聚:把需要同时执行的动作组合在一起形成的模块为时间内聚模块。
(4) 过程内聚:构件或者操作的组合方式是,允许在调用前面的构件或操作之后,马上调用后面的构件或操作,即使两者之间没有数据进行传递。
(5) 通信内聚:指模块内所有处理元素都在同一个数据结构上操作(有时称之为信息内聚),或者指各处理使用相同的输入数据或者产生相同的输出数据。
(6) 顺序内聚:指一个模块中各个处理元素都密切相关于同一功能且必须顺序执行,前一功能元素输出就是下一功能元素的输入。
(7) 功能内聚:这是最强的内聚,指模块内所有元素共同完成一个功能,缺一不可。
耦合可以分为以下几种,它们之间的耦合度由高到低排列如下:
(1) 内容耦合:如果发生下列情形,两个模块之间就发生了内容耦合
一个模块直接访问另一个模块的内部数据
一个模块不通过正常入口转到另一模块内部;
两个模块有一部分程序代码重叠(只可能出现在汇编语言中);
一个模块有多个入口。
(2) 公共耦合:若一组模块都访问同一个公共数据环境,则它们之间的耦合就称为公共耦合。公共的数据环境可以是全局数据结构、共享的通信区、内存的公共覆盖区等。
(3) 外部耦合:一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,则称之为外部耦合。
(4) 控制耦合:如果一个模块通过传送开关、标志、名字等控制信息,明显地控制选择另一模块的功能,就是控制耦合
(5) 标记耦合:一组模块通过参数表传递记录信息,就是标记耦合。这个记录是某一数据结构的子结构,而不是简单变量。其实传递的是这个数据结构的地址;也就是地址传递。
(6) 数据耦合:指两个模块之间有调用关系,传递的是简单的数据值,一个模块访问另一个模块时,彼此之间是通过简单数据参数 (不是控制参数、公共数据结构或外部变量) 来交换输入、输出信息的,相当于高级语言的值传递。
(7) 非直接耦合:两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。
耦合强度,依赖于以下几个因素:
(1)一个模块对另一个模块的调用;
(2)一个模块向另一个模块传递的数据量;
(3)一个模块施加到另一个模块的控制的多少;
(4)模块之间接口的复杂程度。


参考资料:

http://baike.baidu.com/view/156245.html

http://www.cnblogs.com/dqshll/articles/1116799.html

http://blog.zol.com.cn/858/article_857495.html

posted @ 2009-08-03 23:06 xiaoxinchen 阅读(1575) | 评论 (0)编辑 收藏

用 jQuery 的都知道,jQuery 的 get 和 post 方法有三个参数:地址,数据 和回调函数,但我们知道地址也可以跟随数据的(形如:get_data.php?v1=1&v2=2),而且第二个参数可以省略,即第二个参数可 以直接写回调函数,那么数据写在地址后面和写在 data 参数里有什么区别呢?

刚刚做了几个实验,看看下面的代码就清楚了:
以下内容需要回复才能看到

jquery_data.php

echo "post: ";
print_r($_POST);
echo "get: ";
print_r($_GET);
?>

jquery_test.html

实验1:

$(function() {
// post 方法,两处都有数据
$.post('jquery_data.php?v1=1', {v2: 2}, function(data) {
$('

').append(data).appendTo('body');
});
});

返回结果:
post: Array
(
[v2] => 2
)
get: Array
(
[v1] => 1
)

实验2:

$(function()
{
// post 方法,数据在地址后面, 第二个参数为回调函数
$.post('jquery_data.php?v1=1', function(data)
{
$('<pre/>').append(data).appendTo('body');
});
});

返回结果,数据在 get 中:
post: Array
(
)
get: Array
(
[v1] => 1
)

实验3:

$(function()
{
// get 方法,用 data 参数传值
$.get('jquery_data.php', {v2: 2}, function(data)
{
$('<pre/>').append(data).appendTo('body');
});
});

返回结果,数据在 get 中:
post: Array
(
)
get: Array
(
[v2] => 2
)

实验4:

$(function()
{
// get 方法,两处都有数据
$.get('jquery_data.php?v1=1', {v2: 2}, function(data)
{
$('<pre/>').append(data).appendTo('body');
});
});

返回结果,两处数据被合并了,都在 get 中:
post: Array
(
)
get: Array
(
[v1] => 1
[v2] => 2
)

实验5:

$(function()
{
// get 方法,两处都有数据,且变量名相同
$.get('jquery_data.php?v2=1', {v2: 2}, function(data)
{
$('<pre/>').append(data).appendTo('body');
});
});

返回结果,数据在 get 中,且 data 参数中的数据覆盖了地址后面的数据:
post: Array
(
)
get: Array
(
[v2] => 2
)

通过这几个简单的小例子不难看出,地址后面的数据永远是以 get 形式传递的,无论使用的是 get 方法还是 post 方法;而 data 参数中的数据是根据方法决定传递方式的。

因此,为了避免混淆,建议大家尽量不要把数据写在地址后面,而是统一放在 data 参数中。

当然,如果你想在用 post 方法时,同时利用 get 传值,那么就可以把要以 get 方式传递的数据写在地址后面,把要以 post 方式传递的数据写在 data 参数中。

总之方法是死的,人是活的,怎么用还要看实际情况。子曾经曰过:实践是检验真理的唯一标准。没事做做实验,掌握知识更牢固。
posted @ 2009-07-29 19:22 xiaoxinchen 阅读(902) | 评论 (1)编辑 收藏

(zz from http://blog.csdn.net/heiyeshuwu)

Web Service就是为了异构系统的通信而产生的,它基本的思想就是使用基于XML的HTTP的远程调用提供一种标准的机制,而省去建立一种新协议的需求。 目前进行Web Service通信有两种协议标准,一种是XML-RPC,另外一种是SOAP。XML-RPC比较简单,出现时间比较早,SOAP比较复杂,主要是一些 需要稳定、健壮、安全并且复杂交互的时候使用。

Web Service介绍

Web Service就是为了异构系统的通信而产生的,它基本的思想就是使用基于XML的HTTP的远程调用提供一种标准的机制,而省去建立一种新协议的需求。 目前进行Web Service通信有两种协议标准,一种是XML-RPC,另外一种是SOAP。XML-RPC比较简单,出现时间比较早,SOAP比较复杂,主要是一些 需要稳定、健壮、安全并且复杂交互的时候使用。

PHP中集成了XML-RPC和SOAP两种协议的访问,都是集中在xmlrpc扩 展当中。另外,在PHP的PEAR中,不管是PHP 4还是PHP 5,都已经默认集成了XML-RPC扩展,而且该扩展跟xmlrpc扩展无关,能够独立实现XML-RPC的协议交互,如果没有xmlrpc扩展,建议使 用PEAR::XML-RPC扩展。

我们这里主要是以XML-RPC来简单描述Web Service的交互过程,部分内容来自PHP手册,更详细内容,建议参考手册。

安装xmlrpc扩展

如果你的系统中没有安装xmlrpc的php扩展,那么请正确安装。

在 Windows平台下,首先把PHP安装目录下的扩展php_xmlrpc.dll放到C:\Windows或者C:\Winnt目录下, (PHP4的扩展在C:\php\extensions目录中,PHP5的扩展在C:\php\ext目录中),同时在C:\Windows \php.ini或者C:\Winnt\php.ini中把extension=php_xmlrpc.dll前面的分号";"去掉,然后重启Web服务 器后查看phpinfo()有没有XML-RPC项目就能够确定是否已经正确安装xmlrpc扩展。

在Unix/Linux平台下,如果没有安装xmlrpc扩展,请在重新编译PHP,在configure的时候请加入 --with-xmlrpc 选项,然后查看phpinfo()看是否正常安装xmlrpc。

(注意:以下操作都是建立在xmlrpc扩张正常安装前提下,请务必正确安装。)

XML-RPC工作原理

XML-RPC大致就是整个过程就是使用XML来进行通信。首先构造一个RPC 服务器端用来出来从RPC客户端传递过来的使用XML封装的请求,并且把处理结果通过XML的形式返回给RPC客户端,客户端就去分析XML获取自己需要的数据。

XML-RPC的服务器端必须有现成的函数提供给客户端调用,并且客户端提交的请求中的函数和方法必须和服务器端的一致,否则将无法获取所需要的结果。

下面我进行简单的代码来描述整个过程。

XML-RPC实践

服务器端使用xmlrpc_server_create函数产生一个服务器端,然后把需要需要暴露的RPC调用接口进行注册,接受RPC客户端POST过来的XML数据,然后进行处理,处理结果通过XML的形式显示给客户端。

代码如下: rpc_server.php

<?php
/**
* 函数:提供给RPC客户端调用的函数
* 参数:
* $method 客户端需要调用的函数
* $params 客户端需要调用的函数的参数数组
* 返回:返回指定调用结果
*/
function rpc_server_func($method, $params) {
$parameter = $params[0];
if ($parameter == "get")
{
$return = 'This data by get method';
}
else
{
$return = 'Not specify method or params';
}
return $return;
}

//产生一个XML-RPC的服务器端
$xmlrpc_server = xmlrpc_server_create();

//注册一个服务器端调用的方法rpc_server,实际指向的是rpc_server_func函数
xmlrpc_server_register_method($xmlrpc_server, "rpc_server", "rpc_server_func");

//接受客户端POST过来的XML数据
$request = $HTTP_RAW_POST_DATA;

//执行调用客户端的XML请求后获取执行结果
$xmlrpc_response = xmlrpc_server_call_method($xmlrpc_server, $request, null);

//把函数处理后的结果XML进行输出
header('Content-Type: text/xml');
echo $xmlrpc_response;

//销毁XML-RPC服务器端资源
xmlrpc_server_destroy($xmlrpc_server);
?>

服务器端构造好了,那么再构造我们的RPC客户端。客户端大致通过Socket访问XML-RPC服务器端的80端口,然后把需要调用的RPC接口封装到XML里,通过POST请求提交给RPC服务器端,最后获取服务器端返回结果。

代码如下:rpc_client.php

<?php
/**
* 函数:提供给客户端进行连接XML-RPC服务器端的函数
* 参数:
* $host 需要连接的主机
* $port 连接主机的端口
* $rpc_server XML-RPC服务器端文件
* $request 封装的XML请求信息
* 返回:连接成功成功返回由服务器端返回的XML信息,失败返回false
*/
function rpc_client_call($host, $port, $rpc_server, $request) {

//打开指定的服务器端
$fp = fsockopen($host, $port);

//构造需要进行通信的XML-RPC服务器端的查询POST请求信息
$query = "POST $rpc_server HTTP/1.0\nUser_Agent: XML-RPC Client\nHost: ".$host."\nContent-Type: text/xml\nContent-Length: ".strlen($request)."\n\n".$request."\n";

//把构造好的HTTP协议发送给服务器,失败返回false
if (!fputs($fp, $query, strlen($query)))
{
$errstr = "Write error";
return false;
}

//获取从服务器端返回的所有信息,包括HTTP头和XML信息
$contents = '';
while (!feof($fp))
{
$contents .= fgets($fp);
}

//关闭连接资源后返回获取的内容
fclose($fp);
return $contents;
}

//构造连接RPC服务器端的信息
$host = 'localhost';
$port = 80;
$rpc_server = '/~heiyeluren/rpc_server.php';

//把需要发送的XML请求进行编码成XML,需要调用的方法是rpc_server,参数是get
$request = xmlrpc_encode_request('rpc_server', 'get');

//调用rpc_client_call函数把所有请求发送给XML-RPC服务器端后获取信息
$response = rpc_client_call($host, $port, $rpc_server, $request);

//分析从服务器端返回的XML,去掉HTTP头信息,并且把XML转为PHP能识别的字符串
$split = '<?xml version="1.0" encoding="iso-8859-1"?>';
$xml = explode($split, $response);
$xml = $split . array_pop($xml);
$response = xmlrpc_decode($xml);

//输出从RPC服务器端获取的信息
print_r($response);

?>

大致我们上面的例子就是提交一个叫做rpc_server的方法过去,参数是get,然后获取服务器端的返回,服务器端返回的XML数据是:

<?xml version="1.0" encoding="iso-8859-1"?>
<methodResponse>
<params>
<param>
<value>
<string>This data by get method</string>
</value>
</param>
</params>
</methodResponse>

那么我们再通过xmlrpc_decode函数把这个XML编码为PHP的字符串,我们就能够随意处理了,整个Web Service交互完成。

结束语

不 管是XML-RPC也好,SOAP也罢,只要能够让我们稳定、安全的进行远程过程的调用,完成我们的项目,那么就算整个Web Service就是成功的。另外,如果可以的话,也可以尝试使用PEAR中的XML-RPC来实现上面类似的操作,说不定会更简单,更适合你使用。

参考

http://phpxmlrpc.sourceforge.net/

posted @ 2009-07-02 09:42 xiaoxinchen 阅读(214) | 评论 (0)编辑 收藏
JPA

JPA常用标记说明

Table

Table用来定义entity主表的name,catalog,schema等属性。
元数据属性说明:

  • name: 表名
  • catalog: 对应关系数据库中的catalog
  • schema:对应关系数据库中的schema
  • UniqueConstraints:定义一个UniqueConstraint数组,指定需要建唯一约束的列
     
@Entity
@Table(name="CUST")
public class Customer { ... }

SecondaryTable

一个entity class可以映射到多表,SecondaryTable用来定义单个从表的名字,主键名字等属性。
元数据属性说明:

  • name: 表名
  • catalog: 对应关系数据库中的catalog
  • schema:对应关系数据库中的schema
  • pkJoin: 定义一个PrimaryKeyJoinColumn数组,指定从表的主键列
  • UniqueConstraints:定义一个UniqueConstraint数组,指定需要建唯一约束的列

下面的代码说明Customer类映射到两个表,主表名是CUSTOMER,从表名是CUST_DETAIL,从表的主键列和主表的主键列类型相同,列名为CUST_ID。

                 
@Entity
@Table(name="CUSTOMER")
@SecondaryTable(name="CUST_DETAIL",pkJoin=@PrimaryKeyJoinColumn(name="CUST_ID"))
public class Customer { ... }

SecondaryTables

当一个entity class映射到一个主表和多个从表时,用SecondaryTables来定义各个从表的属性。
元数据属性说明:

  • value: 定义一个SecondaryTable数组,指定每个从表的属性。
                 
@Table(name = "CUSTOMER")
@SecondaryTables( value = {
@SecondaryTable(name = "CUST_NAME", pkJoin = { @PrimaryKeyJoinColumn(name = "STMO_ID", referencedColumnName = "id") }),
@SecondaryTable(name = "CUST_ADDRESS", pkJoin = { @PrimaryKeyJoinColumn(name = "STMO_ID", referencedColumnName = "id") }) })
public class Customer {}

UniqueConstraint

UniqueConstraint定义在Table或SecondaryTable元数据里,用来指定建表时需要建唯一约束的列。
元数据属性说明:

  • columnNames:定义一个字符串数组,指定要建唯一约束的列名。
                 
@Entity
@Table(name="EMPLOYEE", uniqueConstraints={@UniqueConstraint(columnNames={"EMP_ID", "EMP_NAME"})})
public class Employee { ... }

Column

Column元数据定义了映射到数据库的列的所有属性:列名,是否唯一,是否允许为空,是否允许更新等。
元数据属性说明:

  • name:列名。
  • unique: 是否唯一
  • nullable: 是否允许为空
  • insertable: 是否允许插入
  • updatable: 是否允许更新
  • columnDefinition: 定义建表时创建此列的DDL
  • secondaryTable: 从表名。如果此列不建在主表上(默认建在主表),该属性定义该列所在从表的名字。
         
public class Person {
@Column(name = "PERSONNAME", unique = true, nullable = false, updatable = true)
private String name;

@Column(name = "PHOTO", columnDefinition = "BLOB NOT NULL", secondaryTable="PER_PHOTO")
private byte[] picture;

...

JoinColumn

如果在entity class的field上定义了关系(one2one或one2many等),我们通过JoinColumn来定义关系的属性。JoinColumn的大部分属性和Column类似。
元数据属性说明:

  • name:列名。
  • referencedColumnName:该列指向列的列名(建表时该列作为外键列指向关系另一端的指定列)
  • unique: 是否唯一
  • nullable: 是否允许为空
  • insertable: 是否允许插入
  • updatable: 是否允许更新
  • columnDefinition: 定义建表时创建此列的DDL
  • secondaryTable: 从表名。如果此列不建在主表上(默认建在主表),该属性定义该列所在从表的名字。

下面的代码说明Custom和Order是一对一关系。在Order对应的映射表建一个名为CUST_ID的列,该列作为外键指向Custom对应表中名为ID的列。

                 
public class Custom {
@OneToOne
@JoinColumn(name="CUST_ID", referencedColumnName="ID", unique=true, nullable=true, updatable=true)
public Order getOrder() {
return order;
}

JoinColumns

如果在entity class的field上定义了关系(one2one或one2many等),并且关系存在多个JoinColumn,用JoinColumns定义多个JoinColumn的属性。
元数据属性说明:

  • value: 定义JoinColumn数组,指定每个JoinColumn的属性。

下面的代码说明Custom和Order是一对一关系。在Order对应的映射表建两列,一列名为CUST_ID,该列作为外键指向Custom对应表中名为ID的列,另一列名为CUST_NAME,该列作为外键指向Custom对应表中名为NAME的列。

                 
public class Custom {
@OneToOne
@JoinColumns({
@JoinColumn(name="CUST_ID", referencedColumnName="ID"),
@JoinColumn(name="CUST_NAME", referencedColumnName="NAME")
})

public Order getOrder() {
return order;
}

Id

声 明当前field为映射表中的主键列。id值的获取方式有五种:TABLE, SEQUENCE, IDENTITY, AUTO, NONE。Oracle和DB2支持SEQUENCE,SQL Server和Sybase支持IDENTITY,mysql支持AUTO。所有的数据库都可以指定为AUTO,我们会根据不同数据库做转换。 NONE需要用户自己指定Id的值。元数据属性说明:

  • generate():主键值的获取类型
  • generator():TableGenerator的名字(当generate=GeneratorType.TABLE才需要指定该属性)

下面的代码声明Task的主键列id是自动增长的。(Oracle和DB2从默认的SEQUENCE取值,SQL Server和Sybase该列建成IDENTITY,mysql该列建成auto increment。)

                
@Entity
@Table(name = "OTASK")
public class Task {
@Id(generate = GeneratorType.AUTO)
public Integer getId() {
return id;
}
}

IdClass

当entity class使用复合主键时,需要定义一个类作为id class。id class必须符合以下要求:类必须声明为public,并提供一个声明为public的空构造函数。必须实现Serializable接,覆写 equals()和hashCode()方法。entity class的所有id field在id class都要定义,且类型一样。
元数据属性说明:

  • value: id class的类名
                 
public class EmployeePK implements java.io.Serializable{
String empName;
Integer empAge;
public EmployeePK(){}

public boolean equals(Object obj){ ......}
public int hashCode(){......}
}

@IdClass(value=com.acme.EmployeePK.class)
@Entity(access=FIELD)
public class Employee {
@Id String empName;
@Id Integer empAge;
}

MapKey

在一对多,多对多关系中,我们可以用Map来保存集合对象。默认用主键值做key,如果使用复合主键,则用id class的实例做key,如果指定了name属性,就用指定的field的值做key。
元数据属性说明:

  • name: 用来做key的field名字

下面的代码说明Person和Book之间是一对多关系。Person的books字段是Map类型,用Book的isbn字段的值作为Map的key。

                 
@Table(name = "PERSON")
public class Person {
@OneToMany(targetEntity = Book.class, cascade = CascadeType.ALL, mappedBy = "person")
@MapKey(name = "isbn")
private Map books = new HashMap();
}

OrderBy

在一对多,多对多关系中,有时我们希望从数据库加载出来的集合对象是按一定方式排序的,这可以通过OrderBy来实现,默认是按对象的主键升序排列。
元数据属性说明:

  • value: 字符串类型,指定排序方式。格式为"fieldName1 [ASC|DESC],fieldName2 [ASC|DESC],......",排序类型可以不指定,默认是ASC。

下面的代码说明Person和Book之间是一对多关系。集合books按照Book的isbn升序,name降序排列。

                
@Table(name = "MAPKEY_PERSON")
public class Person {
@OneToMany(targetEntity = Book.class, cascade = CascadeType.ALL, mappedBy = "person")
@OrderBy(name = "isbn ASC, name DESC")
private List books = new ArrayList();
}

PrimaryKeyJoinColumn

在三种情况下会用到PrimaryKeyJoinColumn。

  • 继承。
  • entity class映射到一个或多个从表。从表根据主表的主键列(列名为referencedColumnName值的列),建立一个类型一样的主键列,列名由name属性定义。
  • one2one关系,关系维护端的主键作为外键指向关系被维护端的主键,不再新建一个外键列。

元数据属性说明:

  • name:列名。
  • referencedColumnName:该列引用列的列名
  • columnDefinition: 定义建表时创建此列的DDL

下面的代码说明Customer映射到两个表,主表CUSTOMER,从表CUST_DETAIL,从表需要建立主键列CUST_ID,该列和主表的主键列id除了列名不同,其他定义一样。

             
@Entity
@Table(name="CUSTOMER")
@SecondaryTable(name="CUST_DETAIL",pkJoin=@PrimaryKeyJoinColumn(name="CUST_ID",referencedColumnName="id"))
public class Customer {
@Id(generate = GeneratorType.AUTO)
public Integer getId() {
return id;
}
}

下面的代码说明Employee和EmployeeInfo是一对一关系,Employee的主键列id作为外键指向EmployeeInfo的主键列INFO_ID。

             
@Table(name = "Employee")
public class Employee {
@OneToOne
@PrimaryKeyJoinColumn(name = "id", referencedColumnName="INFO_ID")
EmployeeInfo info;
}

PrimaryKeyJoinColumns

如果entity class使用了复合主键,指定单个PrimaryKeyJoinColumn不能满足要求时,可以用PrimaryKeyJoinColumns来定义多个PrimaryKeyJoinColumn。
元数据属性说明:

  • value: 一个PrimaryKeyJoinColumn数组,包含所有PrimaryKeyJoinColumn。

下面的代码说明了Employee和EmployeeInfo是一对一关系。他们都使用复合主键,建表时需要在Employee表建立一个外键,从Employee的主键列id,name指向EmployeeInfo的主键列INFO_ID和INFO_NAME.

             
@Entity
@IdClass(EmpPK.class)
@Table(name = "EMPLOYEE")
public class Employee {
private int id;
private String name;

private String address;

@OneToOne(cascade = CascadeType.ALL)
@PrimaryKeyJoinColumns({
@PrimaryKeyJoinColumn(name="id", referencedColumnName="INFO_ID"),
@PrimaryKeyJoinColumn(name="name" , referencedColumnName="INFO_NAME")})
EmployeeInfo info;
}

@Entity
@IdClass(EmpPK.class)
@Table(name = "EMPLOYEE_INFO")
public class EmployeeInfo {

@Id
@Column(name = "INFO_ID")
private int id;

@Id
@Column(name = "INFO_NAME")
private String name;
}

Transient

Transient用来注释entity的属性,指定的这些属性不会被持久化,也不会为这些属性建表。

     
@Transient
private String name;

Version

Version指定实体类在乐观事务中的version属性。在实体类重新由EntityManager管理并且加入到乐观事务中时,保证完整性。每一个类只能有一个属性被指定为version,version属性应该映射到实体类的主表上。

下面的代码说明versionNum属性作为这个类的version,映射到数据库中主表的列名是OPTLOCK。

                 
@Version
@Column("OPTLOCK")
protected int getVersionNum() { return versionNum; }

Lob

Lob指定一个属性作为数据库支持的大对象类型在数据库中存储。使用LobType这个枚举来定义Lob是二进制类型还是字符类型。
LobType枚举类型说明:

  • BLOB 二进制大对象,Byte[]或者Serializable的类型可以指定为BLOB。
  • CLOB 字符型大对象,char[]、Character[]或String类型可以指定为CLOB。

元数据属性说明:

  • fetch: 定义这个字段是lazy loaded还是eagerly fetched。数据类型是FetchType枚举,默认为LAZY,即lazy loaded.
  • type: 定义这个字段在数据库中的JDBC数据类型。数据类型是LobType枚举,默认为BLOB。

下面的代码定义了一个BLOB类型的属性和一个CLOB类型的属性。

                 
@Lob
@Column(name="PHOTO" columnDefinition="BLOB NOT NULL")
protected JPEGImage picture;

@Lob(fetch=EAGER, type=CLOB)
@Column(name="REPORT")
protected String report;

JoinTable

JoinTable在many-to-many关系的所有者一边定义。如果没有定义JoinTable,使用JoinTable的默认值。
元数据属性说明:

  • table:这个join table的Table定义。
  • joinColumns:定义指向所有者主表的外键列,数据类型是JoinColumn数组。
  • inverseJoinColumns:定义指向非所有者主表的外键列,数据类型是JoinColumn数组。

下面的代码定义了一个连接表CUST和PHONE的join table。join table的表名是CUST_PHONE,包含两个外键,一个外键是CUST_ID,指向表CUST的主键ID,另一个外键是PHONE_ID,指向表PHONE的主键ID。

                 
@JoinTable(
table=@Table(name=CUST_PHONE),
joinColumns=@JoinColumn(name="CUST_ID", referencedColumnName="ID"),
inverseJoinColumns=@JoinColumn(name="PHONE_ID", referencedColumnName="ID")
)

TableGenerator

TableGenerator定义一个主键值生成器,在Id这个元数据的generate=TABLE时,generator属性中可以使用生成器的名字。生成器可以在类、方法或者属性上定义。
生成器是为多个实体类提供连续的ID值的表,每一行为一个类提供ID值,ID值通常是整数。
元数据属性说明:

  • name:生成器的唯一名字,可以被Id元数据使用。
  • table:生成器用来存储id值的Table定义。
  • pkColumnName:生成器表的主键名称。
  • valueColumnName:生成器表的ID值的列名称。
  • pkColumnValue:生成器表中的一行数据的主键值。
  • initialValue:id值的初始值。
  • allocationSize:id值的增量。

下面的代码定义了两个生成器empGen和addressGen,生成器的表是ID_GEN。

                 
@Entity
public class Employee {
...
@TableGenerator(
name="empGen",
table=@Table(name="ID_GEN"),
pkColumnName="GEN_KEY",
valueColumnName="GEN_VALUE",
pkColumnValue="EMP_ID",
allocationSize=1
)
@Id(generate=TABLE, generator="empGen")
public int id;
...
}

@Entity
public class Address {
...
@TableGenerator(
name="addressGen",
table=@Table(name="ID_GEN"),
pkColumnValue="ADDR_ID"
)
@Id(generate=TABLE, generator="addressGen")
public int id;
...
}

SequenceGenerator

SequenceGenerator定义一个主键值生成器,在Id这个元数据的generator属性中可以使用生成器的名字。生成器可以在类、方法或者属性上定义。生成器是数据库支持的sequence对象。
元数据属性说明:

  • name:生成器的唯一名字,可以被Id元数据使用。
  • sequenceName:数据库中,sequence对象的名称。如果不指定,会使用提供商指定的默认名称。
  • initialValue:id值的初始值。
  • allocationSize:id值的增量。

下面的代码定义了一个使用提供商默认名称的sequence生成器。

                 
@SequenceGenerator(name="EMP_SEQ", allocationSize=25)

DiscriminatorColumn

DiscriminatorColumn定义在使用SINGLE_TABLE或JOINED继承策略的表中区别不继承层次的列。
元数据属性说明:

  • name:column的名字。默认值为TYPE。
  • columnDefinition:生成DDL的sql片断。
  • length:String类型的column的长度,其他类型使用默认值10。

下面的代码定义了一个列名为DISC,长度为20的String类型的区别列。

                 
@Entity
@Table(name="CUST")
@Inheritance(strategy=SINGLE_TABLE, discriminatorType=STRING, discriminatorValue="CUSTOMER")
@DiscriminatorColumn(name="DISC", length=20)
public class Customer { ... }
posted @ 2009-07-02 09:41 xiaoxinchen 阅读(171) | 评论 (0)编辑 收藏

web.xml的配置

在web.xml中添加

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/applicationContext-resources.xml
classpath:/applicationContext-dao.xml
classpath:/applicationContext-service.xml
classpath*:/applicationContext.xml
/WEB-INF/applicationContext*.xml
/WEB-INF/security.xml
/WEB-INF/dealer-security.xml
</param-value>
</context-param>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>

事务的配置

<aop:config>
<aop:advisor id="managerTx" advice-ref="txAdvice" pointcut="execution(* *..service.*Manager.*(..))" order="0" />
</aop:config>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="find*" read-only="true"/>
<tx:method name="search*" read-only="true"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="*" rollback-for="Throwable"/>
</tx:attributes>
</tx:advice>

ApplicationContext.xml的配置(spring bean 的配置)

<!--ProductManager-START-->
<bean id="productManager" class="com.eryiju.service.product.impl.ProductManagerImpl">
<constructor-arg ref="productDao" />
<property name="brandDao" ref="brandDao"></property>
</bean>

<!--ProductManager-END-->
<bean id="productDao" class="com.eryiju.dao.product.impl.ProductDaoHibernate">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>

<bean id="brandDao" class="com.eryiju.dao.product.impl.BrandDaoHibernate">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>

常见问题

如何与hibernate整合

<!-- Hibernate SessionFactory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
hibernate.query.substitutions=true 'Y', false 'N'
hibernate.cache.use_second_level_cache=true
hibernate.cache.use_query_cache=true
hibernate.cache.provider_class=com.eryiju.util.cache.EhCacheProvider
hibernate.show_sql=false
hibernate.connection.release_mode=after_statement
hibernate.cglib.use_reflection_optimizer=false
hibernate.search.default.directory_provider=org.hibernate.search.store.FSDirectoryProvider
hibernate.search.default.indexBase=/opt/dev/static/index
hibernate.jdbc.batch_size=50
hibernate.jdbc.fetch_size=50
</value>
<!-- Turn batching off for better error messages under PostgreSQL -->
</property>
</bean>
h2. (1)使用spring过滤器解决中文问题

在web.xml中添加:
<filter>
<filter-name>Spring character encoding filter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>GBK</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>Spring character encoding filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

h2. (2)将applicationContext.xml分解成多个文件

applicationContext-common.xml
"dataSource"
"sessionFactory"
事务相关

applicationContext-dao.xml
UserDAO

applicationContext-biz.xml
UserManager

applicationContext-action.xml
Action

struts-config.xml中要作修改:
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation" value="/WEB-INF/classes/applicationContext-*.xml" />
</plug-in>
h2. Spring2.0中的注解实现事务管理

第一步:引入<tx:>命名空间 ,在spring的配置文件中修改, beans根元素里多了三行,如下
Xml代码

<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

第二步:在spring的配置文件中修改,将所有具有@Transactional 注解的bean自动配置为声明式事务支持
Java代码

<!--JDBC事务管理器,根据你的情况使用不同的事务管理器,如果工程中有Hibernate,就用Hibernate的事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref local="dataSource"/>
</property>
</bean>

<!-- 用注解来实现事务管理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

第三步: 在接口或类的声明处 ,写一个@Transactional. 要是只的接口上写, 接口的实现类就会继承下来.
接口的实现类的具体方法,还可以覆盖类声明处的设置.
Java代码

@Transactional
public class TestPOAOImpl extends POAOBase implements TestPOAO
{
@Transactional(isolation = Isolation.READ_COMMITTED)
public void test1()
{
String sql = "INSERT INTO sy_test (NAME,AGE) VALUES('注解赵云',30)";
execute(sql);

sql = "INSERT INTO sy_test (NAME,AGE) VALUES('注解张飞',26)";
execute(sql);

int a = 9 / 0; //异常

sql = "INSERT INTO sy_test (NAME,AGE) VALUES('注解关羽',33)";
execute(sql);
System.out.println("走完了");
}
//execute() 方法略...
}

注意的几点:

1 @Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.
2 默认情况下,一个有事务方法, 遇到RuntiomeException 时会回滚 . 遇到 受检查的异常 是不会回滚 的. 要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .
posted @ 2009-07-02 09:40 xiaoxinchen 阅读(177) | 评论 (0)编辑 收藏
     摘要: 概述 Maven的目标 对Maven的错误理解 Maven的版本 Maven的安装 Windows 2000/xp下的安装 基于Unxi-based的操作系统(Linux,Solaris and Mac OS X) 查看依赖库的位置 http://mvnrepository.com/ Maven主要功能 ...  阅读全文
posted @ 2009-07-02 09:39 xiaoxinchen 阅读(527) | 评论 (0)编辑 收藏

安装

sudo apt-get install mysql

命令行操作

登录

mysql -u用户名 -p密码 -h数据库地址(ip) 数据库名称

注意:尽量不要在-p后直接跟密码,否则其他人很容易通过查阅命令行历史记录(比如,history命令)看到你的密码。

SQL参考

MySQL参考

常见数据类型

integer(11) 11位字节的整数
tinyint(1)
bigint(20)
decimal(10,2) 小数
varchar(20) 最长为20位字节的可变字符串
char(20) 最长为20位字节的定长字符串(定长指的是存储空间定长)
text 文本,用于存大量不固定长度的文本信息
blob 二级制信息

常见函数

length(str) 字符串的长度
trim(str) 去掉字符串前后的空格
substring(str,1) 获取子串
convert(str using gbk) 将字符串转化为gbk编码
reverse(str) 倒序

增删改查

insert into product (sku,name) values ('123456','productname')

向表中添加sku=123456,name='productname' 的数据

update product set name='updated product name' where sku='123456'

将表product中的sku为'123456'的数据的name字段的值设置为'updated product name'

select sku,name from product where sku='123456'

从表product 中查询 sku为'123456'的数据

delete from product where sku='123456'

其他操作实例

多表查询

select p.sku,b.name from product p,brand b where p.brand_id=b.id and p.sku='123456'

子查询

select p.sku,p.name from product p where p.brand_id in (select id from brand where id=123)

左连接

select p.sku,p.name,b.name from product p left join brand b on p.brand_id=b.id

从一个表导入数据到另一个表

insert into product1 (sku,name,brand_id) (select sku,name,brand_id from product2)

查找不同的数据

select distinct p.sku from product p

查询时按照某个字段排序(asc升序,desc降序)

select * from product order by name desc

常见问题

如何创建表

CREATE TABLE  product (
`sku` char(6) NOT NULL COMMENT '商品的唯一标识\n',
`brand_id` int(11) default NULL,
`name` varchar(50) default NULL,
PRIMARY KEY (`sku`),
CONSTRAINT `brand_fk_constraint` FOREIGN KEY (`brand_id`) REFERENCES `brand` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

如何创建外键

alter table product add CONSTRAINT `brand_fk_constraint` FOREIGN KEY (`brand_id`) REFERENCES `brand` (`id`)

如何修改表中的字段

alter table product modify name varchar(200)

如何向表中添加字段

alter table product add comment varchar(200)

如何删除表中字段

alter table product drop name

存储过程和触发器

h3.mysql创建表

drop table if exists news;

/*==========================*/
/* Table: 消息表 */
/*==========================*/
create table news
(
NewsId bigint(20) not null unsigned primary key auto_increment,
NewsContext varchar(50) not null,
NewsType varchar(50) not null,
UsersId int(11) not null
);
alter table news add constraint FK_Id foreign key (NewsId)
references users (UsersId);

资源

官方参考:http://dev.mysql.com/doc/

posted @ 2009-07-02 09:38 xiaoxinchen 阅读(113) | 评论 (0)编辑 收藏

基本语法

声明

#set ($var=XXX)
左边可以是以下的内容:
  • Variable reference
  • String literal
  • Property reference
  • Method reference
  • Number literal #set ($i=1)
  • ArrayList #set ($arr=["yt1","t2"])
  • 算术运算符
  • References 引用的类型

注释

单行:

## this is a comment

多行:

#* this line
and this line
and this line, are comments...*#

变量 Variables

以 "$" 开头,第一个字符必须为字母。character followed by a VTL Identifier. (a .. z or A .. Z).
变量可以包含的字符有以下内容:
  • alphabetic (a .. z, A .. Z)
  • numeric (0 .. 9)
  • hyphen ("-")
  • underscore ("_")

Properties

$Identifier.Identifier
$user.name
hashtable user中的的name值.类似:user.get("name")

h2、Methods

object user.getName() = $user.getName()

h2、Formal Reference Notation

用{}把变量名跟字符串分开。如

#set ($user="csy"}
${user}name

返回csyname

$与$!的区别

当找不到username的时候,$username返回字符串"$username",而$!username返回空字符串""

双引号 与 引号

#set ($var="helo")

则 test"$var" 返回testhello,test'$var' 返回test'$var'。
可以通过设置 stringliterals.interpolate=false改变默认处理方式

条件语句

#if( $foo )
<strong>Velocity!</strong>
#end
#if($foo)
#elseif()
#else
#end

当$foo为null或为Boolean对象的false值执行.

逻辑运算符:

== && || !

循环语句

#foreach($var in $arrays ) // 集合包含下面三种Vector, a Hashtable or an Array
#end

#foreach( $product in $allProducts )
<li>$product</li>
#end

#foreach( $key in $allProducts.keySet() )
<li>Key: $key -> Value: $allProducts.get($key)</li>
#end

#foreach( $customer in $customerList )
<tr><td>$velocityCount</td><td>$customer.Name</td></tr>
#end

velocityCount变量在配置文件中定义

  1. Default name of the loop counter
  2. variable reference.
    directive.foreach.counter.name = velocityCount
  3. Default starting value of the loop
  4. counter variable reference.
    directive.foreach.counter.initial.value = 1

包含文件

#include( "one.gif","two.txt","three.htm" )

Parse导入脚本

#parse("me.vm" )

#stop 停止执行并返回

定义宏

Velocimacros, 相当于函数支持包含功能:

#macro( d )
<tr><td></td></tr>
#end
调用
#d()

带参数的宏

#macro( tablerows $color $somelist )
#foreach( $something in $somelist )
<tr><td bgcolor=$color>$something</td></tr>
#end
#end

Range Operator

#foreach( $foo in [1..5] )

与struts2的整合

模板装载位置(按顺序依次搜索)

  • Web application 应用程序路径,会覆盖掉类路径下的同名配置文件;
  • Class path 类路径,一般为缺省模板的配置,公共模板位置;

数据来源(按顺序依次搜索)

  • The value stack
  • The action context
  • Built-in variables

Struts2-Velocity集成的一些隐含变量

  • stack - ValueStack自身。调用方式:${stack.findString('ognl expr')}
  • action - 最新操作的action
  • reponse
  • request
  • session
  • applicaion - 获得servlet的环境
  • base - 请求环境的路径

Velocity Result 输出模板

模拟jsp执行环境,使用velocity的模板直接显示到servelet的输出流。

location - 模板位置,没有其它参数时的缺省配置。
parse - 默认true ,false将不解析Ognl expressions.

配置范例:

<result name="success" type="velocity">
<param name="location">foo.vm</param>
</result>

等价于以下的配置方式:

<result name="success" type="velocity">
foo.vm
</result>

Velocity语法

http://blog.csdn.net/alexwan/archive/2007/10/29/1853285.aspx

Struts 与 Velocity 的集成

http://www.ibm.com/developerworks/cn/java/j-sr1.html

posted @ 2009-07-02 09:38 xiaoxinchen 阅读(153) | 评论 (0)编辑 收藏
cdn

介绍

CDN 的全称是Content Delivery Network,即内容分发网络。其目的是通过在现有的Internet中增加一层新的网络架构, 将网站的内容发布到最接近用户的网络"边缘", 使用户可以就近取得所需的内容, 解决Internet网络拥挤的状况, 提高用户访问网站的响应速度。从技术上全面解决由于网络带宽小, 用户访问量大, 网点分布不均等原因所造成的用户访问网站响应速度慢的问题。

CDN互联网内容发布网络(Content Delivery Network)

CDN 技术是近年来在美国首先兴起并迅速发展起来的一种解决互联网性能不佳问题的有效手段。其基本思路就是尽可能避开互联网上有可能影响数据传输速度和稳定性的 瓶颈和环节,使内容传输的更快、更稳。通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,cdn系统能够实时地根据网络流 量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。

实际上,内容分发 布网络(CDN)是一种新型的网络构建方式,它是为能在传统的IP网发布宽带丰富媒体而特别优化的网络覆盖层;而从广义的角度,CDN代表了一种基于质量 与秩序的网络服务模式。简单地说,内容发布网(CDN)是一个经策略性部署的整体系统,包括分布式存储、负载均衡、网络请求的重定向和内容管理4个要件, 而内容管理和全局的网络流量管理(Traffic Management)是CDN的核心所在。通过用户就近性和服务器负载的判断,CDN确保内容以一种极为高效的方式为用户的请求提供服务。总的来说,内 容服务基于缓存服务器,也称作代理缓存(Surrogate),它位于网络的边缘,距用户仅有"一跳"(Single Hop)之遥。同时,代理缓存是内容提供商源服务器(通常位于CDN服务提供商的数据中心)的一个透明镜像。这样的架构使得CDN服务提供商能够代表他们 客户,即内容供应商,向最终用户提供尽可能好的体验,而这些用户是不能容忍请求响应时间有任何延迟的。据统计,采用CDN技术,能处理整个网站页面的 70%~95%的内容访问量,减轻服务器的压力,提升了网站的性能和可扩展性。

与目前现有的内容发布模式相比较,CDN强调了网络 在内容发布中的重要性。通过引入主动的内容管理层的和全局负载均衡,CDN从根本上区别于传统的内容发布模式。在传统的内容发布模式中,内容的发布由 ICP的应用服务器完成,而网络只表现为一个透明的数据传输通道,这种透明性表现在网络的质量保证仅仅停留在数据包的层面,而不能根据内容对象的不同区分 服务质量。此外,由于IP网的"尽力而为"的特性使得其质量保证是依靠在用户和应用服务器之间端到端地提供充分的、远大于实际所需的带宽通量来实现的。在 这样的内容发布模式下,不仅大量宝贵的骨干带宽被占用,同时ICP的应用服务器的负载也变得非常重,而且不可预计。当发生一些热点事件和出现浪涌流量时, 会产生局部热点效应,从而使应用服务器过载退出服务。这种基于中心的应用服务器的内容发布模式的另外一个缺陷在于个性化服务的缺失和对宽带服务价值链的扭 曲,内容提供商承担了他们不该干也干不好的内容发布服务。

纵观整个宽带服务的价值链,内容提供商和用户位于整个价值链的两端,中间 依靠网络服务提供商将其串接起来。随着互联网工业的成熟和商业模式的变革,在这条价值链上的角色越来越多也越来越细分。比如内容/应用的运营商、托管服务 提供商、骨干网络服务提供商、接入服务提供商等等。在这一条价值链上的每一个角色都要分工合作、各司其职才能为客户提供良好的服务,从而带来多赢的局面。 从内容与网络的结合模式上看,内容的发布已经走过了ICP的内容(应用)服务器和IDC这两个阶段。IDC的热潮也催生了托管服务提供商这一角色。但 是,IDC并不能解决内容的有效发布问题。内容位于网络的中心并不能解决骨干带宽的占用和建立IP网络上的流量秩序。因此将内容推到网络的边缘,为用户提 供就近性的边缘服务,从而保证服务的质量和整个网络上的访问秩序就成了一种显而易见的选择。而这就是内容发布网(CDN)服务模式。CDN的建立解决了困 扰内容运营商的内容"集中与分散"的两难选择,无疑对于构建良好的互联网价值链是有价值的,也是不可或缺的最优网站加速服务。

目前,国内访问量较高的大型网站如新浪、网易等,均使用CDN网络加速技术,虽然网站的访问巨大,但无论在什么地方访问都会感觉速度很快。而一般的网站如果服务器在网通,电信用户访问很慢,如果服务器在电信,网通用户访问又很慢。

它 采取了分布式网络缓存结构(即国际上流行的web cache技术),通过在现有的Internet中增加一层新的网络架构,将网站的内容发布到最接近用户的cache服务器内,通过DNS负载均衡的技 术,判断用户来源就近访问cache服务器取得所需的内容,解决Internet网络拥塞状况,提高用户访问网站的响应速度,如同提供了多个分布在各地的 加速器,以达到快速、可冗余的为多个网站加速的目的。

CDN的特点

  1. 本地Cache加速 提高了企业站点(尤其含有大量图片和静态页面站点)的访问速度,并大大提高以上性质站点的稳定性
  2. 镜像服务 消除了不同运营商之间互联的瓶颈造成的影响,实现了跨运营商的网络加速,保证不同网络中的用户都能得到良好的访问质量。
  3. 远程加速 远程访问用户根据DNS负载均衡技术 智能自动选择Cache服务器,选择最快的Cache服务器,加快远程访问的速度
  4. 带宽优化 自动生成服务器的远程Mirror(镜像)cache服务器,远程用户访问时从cache服务器上读取数据,减少远程访问的带宽、分担网络流量、减轻原站点WEB服务器负载等功能。
  5. 集群抗攻击 广泛分布的CDN节点加上节点之间的智能冗于机制,可以有效地预防黑客入侵以及降低各种D.D.o.S攻击对网站的影响,同时保证较好的服务质量 。

关键技术

  1. 内容发布:它借助于建立索引、缓存、流分裂、组播(Multicast)等技术,将内容发布或投递到距离用户最近的远程服务点(POP)处;
  2. 内容路由:它是整体性的网络负载均衡技术,通过内容路由器中的重定向(DNS)机制,在多个远程POP上均衡用户的请求,以使用户请求得到最近内容源的响应;
  3. 内容交换:它根据内容的可用性、服务器的可用性以及用户的背景,在POP的缓存服务器上,利用应用层交换、流分裂、重定向(ICP、WCCP)等技术,智能地平衡负载流量;
  4. 性能管理:它通过内部和外部监控系统,获取网络部件的状况信息,测量内容发布的端到端性能(如包丢失、延时、平均带宽、启动时间、帧速率等),保证网络处于最佳的运行状态。

P4P与传统CDN、P2P的对比

7 月30日消息:德国一个名为iPoque的研究机构在2007年研究了一百多万网民将近 3TB的匿名数据流量,调查地区包括澳大利亚、东欧、德国、中东和南欧地区。调查发现,目前网络带宽“消费大户”是P2P文件共享,在中东占据了49%, 东欧地区占据了84%。从全球来看,晚上时段的网络带宽有95%被P2P占据。据国内权威部门统计,当前P2P流量已经占整个互联网流量的约70%,并且 正在以每年350%的速度增长。P2P流量消耗了巨大的网络带宽,尤其是国际带宽,使网络基础设施不堪重负,运营商苦不堪言。

问题 的症结不在于P2P,而在于交换的机制。P2P过于强调“对等”,每个节点之间的交换完全是无序的。一个北京的用户,既可能和广州的用户进行文件片段的交 换,也可能和远在美国的某用户进行交换。显然,无序的交换导致了无谓的跨地区甚至是跨国的 “流量旅行”,这耗费了宝贵的国内和国际带宽资源,代价巨大。

如 果正好用户都在同一个地区,那么,本地化的交换的成本就会大大降低。这也正是P4P的简单原理——让P2P也玩“同城”。 P4P全称是“Proactive network Provider Participation for P2P(电信运营商主动参与P2P网络)”。与P2P随机挑选Peer(对等机)不同,P4P协议可以协调网络拓扑数据,能够有效选择节点,从而提高网络 路由效率。仍以上述例子来说,北京的用户就可以优先和北京同城的用户来实现文件片段的交换,再扩展至较远的地区,有十分的必要时,才会出国进行文件片段交 换。当然,P4P的运行机制,要远远超过“同城交换”的概念,它还会根据用户的上行、下载带宽进行综合判断,以进行最有效选择,最大化整体交换的效率。

举 几个例子可以说明CDN的普遍作用。例如2008年,北京奥运会之前,关于门票网络出售,很多国内的朋友都去登陆,而大家的登录的时刻几乎千篇一律,导致 中国政府的网站服务器支撑不了这么大的请求,谁都进去不了,都被堵死在门外。这中现象在国内许多网站都出现过,比如高考时期,学生上网填申请,大家都说是 自己网络原因,其实不然,这都跟被请求的服务器有关,来自四面八方的请求去请求他一个网站,他自然接受不了这么大的带宽,给人一种印象,就是互联网太慢, 落后了。这样的例子很多,我们就不纷纷介绍了。不过互联网,网聚全球人的智慧。解决方法很多,美国作为世界的互联网中心,提供了CDN技术,内容网络分 发。美国很多军方工程都是采用该技术。我国国内的香港海洋科技集团的GTONE产品,专业为我国国内大型公司提供海外服务,在全球都有很广的资源。国内很 多很有实力的公司都和他们合作了。

新建文件

posted @ 2009-07-02 09:37 xiaoxinchen 阅读(111) | 评论 (0)编辑 收藏
  1. 基本命令
  1. 常见文件操作
        建立目录:mkdir 目录名
删除空目录:rmdir 目录名
无条件删除子目录: rm -rf 目录名
改变当前目录:cd 目录名 (进入用户home目录:cd ~;进入上一级目录:cd -)
查看自己所在目录:pwd
查看当前目录大小:du
显示目录文件列表:ls -l (-a:增加显示隐含目录)
其中:蓝:目录;绿:可执行文件;红:压缩文件;浅蓝:链接文件;灰:其他文件;红底白字:错误的链接文件
浏览文件:more 文件名.txt;less 文件名.txt
复制文件: cp 源文件 目标文件 (-r:包含目录)
查找文件:(1)find (2)locate 命令名
链接:(1)建立hard链接:ln 来源文件 链接文件(-d:创建目录链接);(2)建立符号链接:ln -s 来源文件 链接文件
文本编码转换工具iconv:iconv -f gb2312 -t utf-8 -o new.txt old.txt
输入/输出格式规范
-f, --from-code=NAME 原始文本编码,
-t,--to-code=NAME 输出编码,信息
-l, --list 列出所有已知编码字符集

输出控制:

-c 忽略输出中的无效字符
-o, --output=FILE 输出文件
-s, --silent suppress warnings
# 进程管理
        列出当前进程ID:ps -auxw
终止进程:(1)终止单一进程:kill 进程ID号
终止该程序所有进程:killall 程序名
终止X-Window程序:xkill
查看资源占用情况:(1)top (2)free (3)dmesg
查看环境变量值:env
重启:(1)reboot (2)Ctrl Alt Del (3)init 6
关机:(1)shutdown -h now (2)halt (3)init 0
# 网络管理
        显示网络接口参数:ifconfig
联机状况:ping xxx.xxx.xxx.xxx
显示网络状况:netstat ,其中:options:-a==所有sockets;-l==包含网络设备;-n==数字IP;-o==其他信息;-r==路由表;-t==只列TCP sockets;-u==只列UDP sockets;-w==只列raw sockets;
-x==只列Unix Domain sockets

# 权限设定
        (1)chmod -a|u|g|o |-|=r|w|x 文件/目录名
其中:a--所有用户(all);u--本用户(user);g--用户组(group);o--其他用户(other users)
--增加权限;---删除权限;=--设置权限
文件:r--只读权限(read);w--写权限(write);x--执行权限(execute)
目录:r--允许列目录下文件和子目录;w--允许生成和删除目录下文件;x--允许访问该目录
(2)chmod xxx 文件/目录名
其中:execute=1;write=2;read=4
x取值:0--没有任何权限(常用);1--只能执行(不常见);2--只能写(不常见);3--只能写和执行(不常见);4--只读(常见);5--只读和执行(常见);6--读和写(常见);7--读.写和执行
# vim 常见命令
        进入后为命令模式:(1)插入i;(2)打开0;(3)修改c;(4)取代r;(5)替换s
经(1)后进入全屏幕编辑模式。
命令模式-->编辑模式(a/i);编辑模式-->命令模式(Esc);命令模式-->末行模式(:)。
:w/w newfile保存
:q/q!退出iv;:wq保存退出
http://vimcdoc.sourceforge.net/doc/help.html

#ln命令

ln 命令

用途 : 链接文件。

语法

1>将某个文件链接到一个文件上 ln [ -f | -n] [ -s ] SourceFile [ TargetFile ]

2>将一个或多个文件链接到一个目录上 ln [ -f | -n] [ -s ] SourceFile ... TargetDirectory

描述 ln 命令将在 SourceFile 参数中指定的文件链接到在 TargetFile 参数中指定的文件,或将其链接到在 TargetDirectory 参数中指定的另一个目录中的文件。

在缺省情况下,ln 命令会创建硬链接。如果需要使用 ln 命令来创建符号链接,请指明 -s 标志。

符号链接是指向文件的一个间接指针;它的目录项中包含了它所链接的文件名。符号链接可能会跨越文件系统,可能指向目录。

如果正在将某个文件链接到新的名字,那么只能列出一个文件。如果链接到一个目录,那么可以列出多个文件。

TargetFile 参数是可选的。

如果不指定目标文件,ln 命令会在当前的目录中创建一个新的文件。新的文件继承了指定在 SourceFile 参数中的文件名。

注意: 如果不使用 -s 标志,就不能在文件系统之间链接文件。 如果 TargetDirectory 已经是链接到目录上的一个符号链接,那么 ln 命令将现有的目标视为文件。 这意味着,类似于 ln -fs somepath/lname symdir 的命令不会遵循现有的 symdir 符号链接,作为代替,它会创建一个从 somepath/lname 到 symdir 的新的符号链接。

参数

-f 促使 ln 命令替换掉任何已经存在的目的路径。如果目的路径已经存在,而没有指定 -f 标志,ln 命令不会创建新的链接,而是向标准错误写一条诊断消息并继续链接剩下的 SourceFiles。

-n 指定,如果链接是一个现有的文件,那么不要覆盖文件的内容。 -f 标志重设了这个标志。这是缺省的行为。

-s 促使 ln 命令创建符号链接。符号链接中包含了它所链接的文件的名字。当对链接执行打开操作的时候,会使用到引用文件。对符号链接的 stat 调用会返回链接的目标文件;必须完成lstat 调用来获取链接的信息。可以使用 readlink 调用来读取符号链接的内容。符号链接可能跨越文件系统,指向目录。

注意:当为 -s 标志指定 SourceFile 参数的时候,必须使用绝对路径。如果没有指明绝对路径,那么当 SourceFile 和 TargetFile 参数位于不同的目录中的时候,可能会发生意外的结果。在创建符号链接之前,不需要存在源文件。

退出状态 此命令返回以下的退出值:

0 所有指定的文件都成功链接上了。

0 出现一次错误。

示例

1>为了创建到一个文件的另一个链接(别名),请输入:

ln -f file1 file2 这会将 file1 链接到新的名称, file2。如果 file2 不存在,那么会创建该文件名。如果 file2 已经存在了,那么这个文件会被替换为指向 file1的一个链接。然后 file1 和 file2 文件名会指向同一个文件。对其中任何一个的更改都会出现在另一个中。如果一个文件名被 rm 命令删除,那么该文件并没有完全被删除,因为它仍然以其它的名字存在。

2>为了将文件链接为另一个目录中的相同名字,请输入:

ln index dir1 这会将 index 链接到新的名称,dir1/index。

注意:在示例 1 中的 file2 是一个文件的名称;在示例 2 中的 dir1 是一个已经存在的目录。

3>为了将几个文件链接为另一个目录中的名称,请输入:

ln file2 dir2/file3 /home/dir1 这会将 file2 链接到新的名称 /home/dir1/file2;将 dir2/file3 链接到新的名称 /home/dir1/file3。

4>如果想要在 ln 命令中使用模式匹配字符,请输入:

ln dir1/* . 这会将 dir1 目录中的所有文件链接到当前目录中, . (点),给他们在 dir1 目录中同样的名称。

注意: 必须在星号和句点之间输入一个空格。

5>为了创建一个符号链接,输入:

ln -s /tmp/test test

这会在当前的目录中创建符号链接 test。 test 文件指向 /tmp/test 文件。如果 /tmp/test 文件已经存在了,那么 cat test 命令可以列出其内容。

6>如果想要在不指明 TargetFile 参数的情况下得到相同的结果,请输入:

ln -s /tmp/test

文件

/usr/bin/ln 包含了 ln 命令。

  1. 常见配置文件
posted @ 2009-07-02 09:37 xiaoxinchen 阅读(115) | 评论 (0)编辑 收藏

介绍

入门

安装

不用安装,直接解压即可。如果进行JEE开发,可以用eclipse-jee-ganymede-SR2文件,而PHP的话可以直接用PDT。Python可以用基于Apanta的PyDev。

基本操作

选项:Windows -> Preference。
安装新特性/插件:Help -> SoftwareUpdates

常用快捷键

快捷键的设置和修改在Windows -> Preference ->General -> Keys;

我最常用的:
Ctrl+Space 代码助手完成一些代码的插入(但一般和输入法有冲突,可以Alt+/来代替,在keys里找到command 为Content Assist的,把其Binding改为Alt+/) 这个是我最喜欢的功能,你可以把变量命名的很长,下次引用时只要打首个字母,再打Alt+/,就能写出变量。
Ctrl+Q 定位到最后编辑的地方
Ctrl+Shift+R 全局 打开资源
Ctrl+E 快速显示当前Editer的下拉列表(如果当前页面没有显示的用黑体表示)
Java编辑器 组织导入 Ctrl+Shift+O
Java编辑器 添加导入 Ctrl+Shift+M
Alt+← 前一个编辑的页面
Alt+→ 下一个编辑的页面(当然是针对上面那条来说了)
Ctrl+1 快速修复
Ctrl+/ 注释当前行,再按则取消注释
Ctrl+D: 删除当前行
Java编辑器 格式化 Ctrl+Shift+F
Alt+↓ 当前行和下面一行交互位置(特别实用,可以省去先剪切,再粘贴了)
Alt+↑ 当前行和上面一行交互位置(同上)
Ctrl+L 定位在某行 (对于程序超过100的人就有福音了)
Alt+Shift+R 重命名 (是我自己最爱用的一个了,尤其是变量和类的Rename,比手工方法能节省很多劳动力)
Alt+Shift+Z 使用try/catch块来包围

下面是网上转过来
Eclipse快捷键大全(转载)
Ctrl+1 快速修复(最经典的快捷键,就不用多说了)
Ctrl+D: 删除当前行
Ctrl+Alt+↓ 复制当前行到下一行(复制增加)
Ctrl+Alt+↑ 复制当前行到上一行(复制增加)
Alt+↓ 当前行和下面一行交互位置(特别实用,可以省去先剪切,再粘贴了)
Alt+↑ 当前行和上面一行交互位置(同上)
Alt+← 前一个编辑的页面
Alt+→ 下一个编辑的页面(当然是针对上面那条来说了)
Alt+Enter 显示当前选择资源(工程,or 文件 or文件)的属性
Shift+Enter 在当前行的下一行插入空行(这时鼠标可以在当前行的任一位置,不一定是最后)
Shift+Ctrl+Enter 在当前行插入空行(原理同上条)
Ctrl+Q 定位到最后编辑的地方
Ctrl+L 定位在某行 (对于程序超过100的人就有福音了)
Ctrl+M 最大化当前的Edit或View (再按则反之)
Ctrl+/ 注释当前行,再按则取消注释
Ctrl+O 快速显示 OutLine
Ctrl+T 快速显示当前类的继承结构
Ctrl+W 关闭当前Editer
Ctrl+K 参照选中的Word快速定位到下一个
Ctrl+E 快速显示当前Editer的下拉列表(如果当前页面没有显示的用黑体表示)
Ctrl+/(小键盘) 折叠当前类中的所有代码
Ctrl+×(小键盘) 展开当前类中的所有代码
Ctrl+Space 代码助手完成一些代码的插入(但一般和输入法有冲突,可以修改输入法的热键,也可以暂用Alt+/来代替)
Ctrl+Shift+E 显示管理当前打开的所有的View的管理器(可以选择关闭,激活等操作)
Ctrl+J 正向增量查找(按下Ctrl+J后,你所输入的每个字母编辑器都提供快速匹配定位到某个单词,如果没有,则在stutes line中显示没有找到了,查一个单词时,特别实用,这个功能Idea两年前就有了)
Ctrl+Shift+J 反向增量查找(和上条相同,只不过是从后往前查)
Ctrl+Shift+F4 关闭所有打开的Editer
Ctrl+Shift+X 把当前选中的文本全部变味小写
Ctrl+Shift+Y 把当前选中的文本全部变为小写
Ctrl+Shift+F 格式化当前代码
Ctrl+Shift+P 定位到对于的匹配符(譬如{}) (从前面定位后面时,光标要在匹配符里面,后面到前面,则反之)

下面的快捷键是重构里面常用的,本人就自己喜欢且常用的整理一下(注:一般重构的快捷键都是Alt+Shift开头的了)
Alt+Shift+R 重命名 (是我自己最爱用的一个了,尤其是变量和类的Rename,比手工方法能节省很多劳动力)
Alt+Shift+M 抽取方法 (这是重构里面最常用的方法之一了,尤其是对一大堆泥团代码有用)
Alt+Shift+C 修改函数结构(比较实用,有N个函数调用了这个方法,修改一次搞定)
Alt+Shift+L 抽取本地变量( 可以直接把一些魔法数字和字符串抽取成一个变量,尤其是多处调用的时候)
Alt+Shift+F 把Class中的local变量变为field变量 (比较实用的功能)
Alt+Shift+I 合并变量(可能这样说有点不妥Inline)
Alt+Shift+V 移动函数和变量(不怎么常用)
Alt+Shift+Z 重构的后悔药(Undo)

编辑
作用域 功能 快捷键
全局 查找并替换 Ctrl+F
文本编辑器 查找上一个 Ctrl+Shift+K
文本编辑器 查找下一个 Ctrl+K
全局 撤销 Ctrl+Z
全局 复制 Ctrl+C
全局 恢复上一个选择 Alt+Shift+↓
全局 剪切 Ctrl+X
全局 快速修正 Ctrl1+1
全局 内容辅助 Alt+/
全局 全部选中 Ctrl+A
全局 删除 Delete
全局 上下文信息 Alt+?
Alt+Shift+?
Ctrl+Shift+Space
Java编辑器 显示工具提示描述 F2
Java编辑器 选择封装元素 Alt+Shift+↑
Java编辑器 选择上一个元素 Alt+Shift+←
Java编辑器 选择下一个元素 Alt+Shift+→
文本编辑器 增量查找 Ctrl+J
文本编辑器 增量逆向查找 Ctrl+Shift+J
全局 粘贴 Ctrl+V
全局 重做 Ctrl+Y

查看
作用域 功能 快捷键
全局 放大 Ctrl+=
全局 缩小 Ctrl+-

窗口
作用域 功能 快捷键
全局 激活编辑器 F12
全局 切换编辑器 Ctrl+Shift+W
全局 上一个编辑器 Ctrl+Shift+F6
全局 上一个视图 Ctrl+Shift+F7
全局 上一个透视图 Ctrl+Shift+F8
全局 下一个编辑器 Ctrl+F6
全局 下一个视图 Ctrl+F7
全局 下一个透视图 Ctrl+F8
文本编辑器 显示标尺上下文菜单 Ctrl+W
全局 显示视图菜单 Ctrl+F10
全局 显示系统菜单 Alt+-

导航
作用域 功能 快捷键
Java编辑器 打开结构 Ctrl+F3
全局 打开类型 Ctrl+Shift+T
全局 打开类型层次结构 F4
全局 打开声明 F3
全局 打开外部javadoc Shift+F2
全局 打开资源 Ctrl+Shift+R
全局 后退历史记录 Alt+←
全局 前进历史记录 Alt+→
全局 上一个 Ctrl+,
全局 下一个 Ctrl+.
Java编辑器 显示大纲 Ctrl+O
全局 在层次结构中打开类型 Ctrl+Shift+H
全局 转至匹配的括号 Ctrl+Shift+P
全局 转至上一个编辑位置 Ctrl+Q
Java编辑器 转至上一个成员 Ctrl+Shift+↑
Java编辑器 转至下一个成员 Ctrl+Shift+↓
文本编辑器 转至行 Ctrl+L

搜索
作用域 功能 快捷键
全局 出现在文件中 Ctrl+Shift+U
全局 打开搜索对话框 Ctrl+H
全局 工作区中的声明 Ctrl+G
全局 工作区中的引用 Ctrl+Shift+G

文本编辑
作用域 功能 快捷键
文本编辑器 改写切换 Insert
文本编辑器 上滚行 Ctrl+↑
文本编辑器 下滚行 Ctrl+↓

文件
作用域 功能 快捷键
全局 保存 Ctrl+X
Ctrl+S
全局 打印 Ctrl+P
全局 关闭 Ctrl+F4
全局 全部保存 Ctrl+Shift+S
全局 全部关闭 Ctrl+Shift+F4
全局 属性 Alt+Enter
全局 新建 Ctrl+N

项目
作用域 功能 快捷键
全局 全部构建 Ctrl+B

源代码
作用域 功能 快捷键
Java编辑器 格式化 Ctrl+Shift+F
Java编辑器 取消注释 Ctrl+\
Java编辑器 注释 Ctrl+/
Java编辑器 添加导入 Ctrl+Shift+M
Java编辑器 组织导入 Ctrl+Shift+O
Java编辑器 使用try/catch块来包围 未设置,太常用了,所以在这里列出,建议自己设置。
也可以使用Ctrl+1自动修正。

运行
作用域 功能 快捷键
全局 单步返回 F7
全局 单步跳过 F6
全局 单步跳入 F5
全局 单步跳入选择 Ctrl+F5
全局 调试上次启动 F11
全局 继续 F8
全局 使用过滤器单步执行 Shift+F5
全局 添加/去除断点 Ctrl+Shift+B
全局 显示 Ctrl+D
全局 运行上次启动 Ctrl+F11
全局 运行至行 Ctrl+R
全局 执行 Ctrl+U

重构
作用域 功能 快捷键
全局 撤销重构 Alt+Shift+Z
全局 抽取方法 Alt+Shift+M
全局 抽取局部变量 Alt+Shift+L
全局 内联 Alt+Shift+I
全局 移动 Alt+Shift+V
全局 重命名 Alt+Shift+R
全局 重做 Alt+Shift+Y

检出项目

首先安装SVN插件(subversive或者subclipse),然后新建Project,从SVN检出项目。需要特别注意的是,要在general / workspace选项中正确设置字符编码,否则可能会出现编译错误。

用Maven构建项目

安装IAM之后,在项目的context菜单中选“Maven 2 / Use Maven Dependency Management”,然后就可以管理依赖。

查看数据库

打开 Database Perspective 即可。注意要先选择驱动程序,即mysql的java connector的jar,然后才是配置各个连接的jdbc地址。

关于插件

插件安装方法(zz)

英文教程:http://www.venukb.com/2006/08/20/install-eclipse-plugins-the-easy-way/

有关插件安装问题,四种常用的方法在此特别注明:

#“帮助”->“软件更新”->“查找并安装”->“搜索要安装的新功能部件”->“新建远程站点”(此种方式用于在线更新)
#“帮助”->“软件更新”->“查找并安装”->“搜索要安装的新功能部件”->“新建本地站点”(如果插件已经下载到了本地,请不要用第一种方法)
  1. 直接拷贝plugins和features两个目录下的内容置于$Eclipse_Home$/对应的plugins和features下面
  2. 用link外链接与外部插件关联

最菜的,一般用第一种方法,而大部分生手一般选择第二或者第三种方法,用得习惯的一般选择最后一种方式。此四类方法优劣势对比如下:
前三种方法都会将插件文件拷贝至相$Eclipse_Home$/对应的plugins和features目录下,从本质上看,没多大区别,并且插件只能 安装和禁用,不能卸载(当然,如果你对插件对应的目录和文件都很熟悉的话,可以通过直接删除拷进去的文件来达到卸载插件的目的),但方法一和方法二在安装 插件的时候很容易出错或者是产生冲突,特别是当你用了Myeclipse插件、中文包的同时,又想安装HibernateSynchronizer、 Jode Compiler(Class反编译工具)、Visual Editor等插件时,及有可能导致Myeclipse插件和中文包失效。

所以,如果插件已经下载到了本地,请直接拷贝至$Eclipse_Home$/对应的plugins和features目录下,也就是用方法三,这样能避免冲突。

方 法四是将所有的插件用一个外部目录存放起来,假如是D:\plug-in,将上面所示的插件目录文件全部拷贝到该目录下,比如Tomcat插件,此时的文 件路径就是D:\plug-in\tomcat_plug\eclipse\plugins \com.sysdeo.eclipse.tomcat_3.1.0.beta(请注意,方法四一定要严格这样的目录路径放置文件)。然后 在$Eclipse_Home$下新建一个links目录,并在links目录下建立关联文件,假如是tomcat.link,在建立的关联文件中加入如 下语句:

path=D:/plug-in/tomcat_plug

还可以写成相对路径的形式。剩下的事情,不用我说你肯定都知道了,就是重启Eclipse,在Dos窗口下进入Eclipse安 装目录,键入命令eclipse -clean,回车,或者进入$Eclipse_Home$/configuration目录,删除org.eclipse.update后再重新启动 Eclipse。

QA相关插件

参考:http://www.ibm.com/developerworks/cn/java/j-ap01117/

CheckStyle http://eclipse-cs.sourceforge.net/update/
Coverlipse http://coverlipse.sf.net/update
PMD http://pmd.sourceforge.net/eclipse/
JDepend http://andrei.gmxhome.de/eclipse/
Metrics http://metrics.sourceforge.net/update
FindBugs http://findbugs.cs.umd.edu/eclipse

参考资源

http://www.ibm.com/developerworks/cn/eclipse/resources.html
http://www.eclipseplugincentral.com/

posted @ 2009-07-02 09:36 xiaoxinchen 阅读(255) | 评论 (0)编辑 收藏

Web标准

我理解的Web标准
web标准就是浏览器的开发厂商和界面开发人员共同遵循的标准。由万维网联盟W3C(World Wide Web Consortium)组织制定。
Web标准化无异于工业标准化,就好像改锥和螺丝一样,他们都遵循同样的标准,易用,有效,甚至能赢得战争(注1)。
遵循Web标准的好处
  • 高效率。开发人员可以更容易地理解彼此的编码,web开发的团队协作将得到简化。
  • 易用性。大家都遵循标准,未来也许就不存在跨浏览器问题,也可以跨平台(比如移动电话),另外,标准也可以使残疾人士更容易地使用web。
  • 利于访问。更易被搜索引擎访问,也更易被准确地索引。

注1:德国的工业非常发达,二战时德国设计的武器都是标准件,即使一辆趟克被炸烂,其中的零件都还可以再利用,甚至可以应用到枪支或其他武器上。

Acid3

测试浏览器支持Acid3的程度:http://acid3.acidtests.org/

为什么要使用Web标准(zz)?

英文版:http://www.webstandards.org/learn/faq/
中文版:http://www.webstandards.org/learn/faq/faq_zh-simplified/

posted @ 2009-07-02 09:35 xiaoxinchen 阅读(103) | 评论 (0)编辑 收藏