zhu's profile三界九重天PhotosBlogLists Tools Help

Blog


    January 13

    Eclipse的让人抓狂的Link式装插件

             eclipse的Link式装插件很方便,但这次弄了一个莫名其妙的东东,而且大家都没有提到这个问题,搞的是焦头烂额,还好最后用无穷法给搞定了,真是郁闷死了。
             需要注意的只有2点而已:
             1.  删除的是configuration/org.eclipse.update   不是configuration目录。
             2.  links目录下一定不要放插件的jia包之类的东西,负责eclipse也会去预读,导致报错。
     ------------------------------------------------------------------------------------------------------------------------------
    一、使用links方式安装eclipse插件
      
      假设eclipse安装目录是D:\eclipse,待安装插件目录是D:\eclipseplugins。以lomboz,Sysdeo Eclipse Tomcat Launcher plugin和xmlbuddy为例说明如何使用links方式安装eclipse插件。
      
      1、安装Lomboz  
      在D:\eclipseplugins中建立如下的目录结构:  
      D:\eclipseplugins\lomboz\eclipse  
      将lomboz安装文件解压到此目录中,这个目录将包含一个plugins目录,即目录结构是这样的:  
      D:\eclipseplugins\lomboz\eclipse\plugins  
      在D:\eclipse\links目录下新建文件,命名为lomboz.link,编辑此文件,加入如下内容并保存。  
      path = D:/eclipseplugins/lomboz  
      eclipse将会到指定的目录下去查找eclipse\features目录和eclipse\plugins目录,看是否有合法的功能部件和(或)插件。也就是说,目标目录必须包含一个 \eclipse 目录。如果找到,附加的功能部件和插件在运行期配置是将是可用的,如果链接文件是在工作区创建之后添加的,附加的功能部件和插件会作为新的配置变更来处理。  
      其它压缩文件解压后若已经包含eclipse\plugins目录,则不需要建立eclipse目录。
      
      2、安装Sysdeo Eclipse Tomcat Launcher plugin  
      解压tomcatPluginV3.zip到目录D:\eclipseplugins\tomcatPlugin\eclipse\plugins目录下。  
      在D:\eclipse\links下新建文件,命名为tomcatPlugin.link,编辑此文件,加入如下内容并保存。  
      path = D:/eclipseplugins/tomcatPlugin

      3、安装xmlbuddy
      解压xmlbuddy安装文件到D:\eclipseplugins\xmlbuddy\eclipse\plugins目录下。
      在D:\eclipse\links下新建文件,命名为xmlbuddy.link,编辑此文件,加入如下内容并保存。
      path = D:/eclipseplugins/xmlbuddy
      
      二、插件管理
      启动eclipse,在菜单Help->Software Updates->Manage Configuration...下,启用或者禁用插件。
      
      三、说明
      
      1. 插件可以分别安装在多个自定义的目录中。
      
      2. 一个自定义目录可以安装多个插件。
      
      3. link文件的文件名及扩展名可以取任意名称,比如ddd.txt,myplugin都可以。
      
      4. link文件中path=插件目录的path路径分隔要用\\或是/
      
      5. link文件可以有多行path=插件目录,对应多个自定义插件目录,每一行的path参数都将生效。
      
      6. 在links目录也可以有多个link文件,每个link文件中的path参数都将生效。
      
      7. 插件目录可以使用相对路径。
      
      8. 可以在links目录中建立一个子目录,转移暂时不用的插件到此子目录中,加快eclipse启动。
      
      9. 如果安装后看不到插件,把eclipse安装目录下的configuration/org.eclipse.update/目录整个删除,重启eclipse。
    April 17

    Lock-Free

          关于CAS原语有个相当出名的ABA问题:我们把CAS的value值设为A,在更改value时,CAS询问value的值是否仍为A,如果为A,我们就把newValue的值赋给value。但是假如我们把value的值从A改为B,紧接着又从B改为A。CAS无法知道其中的变化,仍然认为比对成功。这时就可能带来无法预知的结果。这类问题就是ABA问题。注意,此处计数器的实现不受其困扰。通常,我们通过将标记或版本编号与要进行 CAS 操作的每个值相关联,并原子地更新值和标记,来处理这类问题。比如Java中的AtomicStampedReference类就支持这种方法。我们应当知道能够交换的位越长,ABA发生的几率越低。

        基于CAS的并发算法,我们称为Lock-Free算法,因为线程不必再等待锁定。当然,也有很多其他扩展的硬件原语可用于并发算法,比如DCAS,但是目前只有CAS是被广泛实现的。其他如FetchAndAdd,原子队列等则被证明不足以良好的同步两个以上的线程。无论 CAS 操作成功还是失败,在任何一种情况中,它都在可预知的时间内完成。如果 CAS 失败,调用者可以重试 CAS 操作或采取其他适合的操作。下面我们使用CAS原语来实现线程安全的计数器。

       public class CASCounter
        {
            private int value;

            public int GetValue()
            {
                return value;
            }

            public int Increment()
            {
                int oldValue;
                do
                {
                    oldValue = value;
                }
                while (Interlocked.CompareExchange(ref value, oldValue+1, oldValue) != oldValue);

                return oldValue + 1;
            }
            //
        }

    在Lock-Free的世界里,几乎任何操作都无法原子的完成。只有极少的操作可以原子完成,这一限制使得编程难度大大增加。但是Lock-Free的程序能够确保执行它的所有线程中至少有一个能够继续往下执行。这便意味着有些线程可能会被任意地延迟,然而在每一步都至少有一个线程能够往下执行。因此这个系统作为一个整体总是在“前进”的,尽管有些线程的进度可能不如其它线程来得快。而基于锁的程序则无法提供上述任何保证。

    关于Lock-Free的优缺点可以参考关于无锁编程。除了文章中所提到的内容外,Lock-Free编程还有三大优点:
    线程中止免疫:杀掉系统中的任何线程都不会导致其它线程被延迟。
    优先级倒置免疫:所谓“优先级倒置”就是指一个低优先级线程持有了一个高优先级线程所需要的互斥体。这种微妙的冲突必须由OS内核来解决。而等待无关和锁无关算法则对此免疫。
    死锁免疫:因为没有使用锁,所以也就不存在死锁的可能。但是乐观的并发,可能会导致活锁。

    虽然Lock-Free编程非常困难,但是它通常可以带来比基于锁编程更高的吞吐量。所以Lock-Free编程是大有前途的技术。它在线程中止、优先级倒置以及信号安全等方面都有着良好的表现。

     

    April 01

    关于23种设计模式的有趣见解

    创建型模式

    1、FACTORY—追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯德基,只管向服务员说“来四个鸡翅”就行了。麦当劳和肯德基就是生产鸡翅的Factory

    工厂模式:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:如何创建及如何向客户端提供。

    2、BUILDER—MM最爱听的就是“我爱你”这句话了,见到不同地方的MM,要能够用她们的方言跟她说这句话哦,我有一个多种语言翻译机,上面每种语言都有一个按键,见到MM我只要按对应的键,它就能够用相应的语言说出“我爱你”这句话了,国外的MM也可以轻松搞掂,这就是我的“我爱你”builder。(这一定比美军在伊拉克用的翻译机好卖)

    建造模式:将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化,客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。

    3、FACTORY METHOD—请MM去麦当劳吃汉堡,不同的MM有不同的口味,要每个都记住是一件烦人的事情,我一般采用Factory Method模式,带着MM到服务员那儿,说“要一个汉堡”,具体要什么样的汉堡呢,让MM直接跟服务员说就行了。

    工厂方法模式:核心工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。

    4、PROTOTYPE—跟MM用QQ聊天,一定要说些深情的话语了,我搜集了好多肉麻的情话,需要时只要copy出来放到QQ里面就行了,这就是我的情话prototype了。(100块钱一份,你要不要)

    原始模型模式:通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。

    5、SINGLETON—俺有6个漂亮的老婆,她们的老公都是我,我就是我们家里的老公Sigleton,她们只要说道“老公”,都是指的同一个人,那就是我(刚才做了个梦啦,哪有这么好的事)

    单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用。

    结构型模式

    6、ADAPTER—在朋友聚会上碰到了一个美女Sarah,从香港来的,可我不会说粤语,她不会说普通话,只好求助于我的朋友kent了,他作为我和Sarah之间的Adapter,让我和Sarah可以相互交谈了(也不知道他会不会耍我)

    适配器(变压器)模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。

    7、BRIDGE—早上碰到MM,要说早上好,晚上碰到MM,要说晚上好;碰到MM穿了件新衣服,要说你的衣服好漂亮哦,碰到MM新做的发型,要说你的头发好漂亮哦。不要问我“早上碰到MM新做了个发型怎么说”这种问题,自己用BRIDGE组合一下不就行了

    桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化,也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。

    8、COMPOSITE—Mary今天过生日。“我过生日,你要送我一件礼物。”“嗯,好吧,去商店,你自己挑。”“这件T恤挺漂亮,买,这条裙子好看,买,这个包也不错,买。”“喂,买了三件了呀,我只答应送一件礼物的哦。”“什么呀,T恤加裙子加包包,正好配成一套呀,小姐,麻烦你包起来。”“……”,MM都会用Composite模式了,你会了没有?

    合成模式:合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。

    9、DECORATOR—Mary过完轮到Sarly过生日,还是不要叫她自己挑了,不然这个月伙食费肯定玩完,拿出我去年在华山顶上照的照片,在背面写上“最好的的礼物,就是爱你的Fita”,再到街上礼品店买了个像框(卖礼品的MM也很漂亮哦),再找隔壁搞美术设计的Mike设计了一个漂亮的盒子装起来……,我们都是Decorator,最终都在修饰我这个人呀,怎么样,看懂了吗?

    装饰模式:装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。

    10、FACADE—我有一个专业的Nikon相机,我就喜欢自己手动调光圈、快门,这样照出来的照片才专业,但MM可不懂这些,教了半天也不会。幸好相机有Facade设计模式,把相机调整到自动档,只要对准目标按快门就行了,一切由相机自动调整,这样MM也可以用这个相机给我拍张照片了。

    门面模式:外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。每一个子系统只有一个门面类,而且此门面类只有一个实例,也就是说它是一个单例模式。但整个系统可以有多个门面类。

    11、FLYWEIGHT—每天跟MM发短信,手指都累死了,最近买了个新手机,可以把一些常用的句子存在手机里,要用的时候,直接拿出来,在前面加上MM的名字就可以发送了,再不用一个字一个字敲了。共享的句子就是Flyweight,MM的名字就是提取出来的外部特征,根据上下文情况使用。

    享元模式:FLYWEIGHT在拳击比赛中指最轻量级。享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态,它们是相互独立的。将可以共享的状态和不可以共享的状态从常规类中区分开来,将不可以共享的状态从类里剔除出去。客户端不可以直接创建被共享的对象,而应当使用一个工厂对象负责创建被共享的对象。享元模式大幅度的降低内存中对象的数量。

    12、PROXY—跟MM在网上聊天,一开头总是“hi,你好”,“你从哪儿来呀?”“你多大了?”“身高多少呀?”这些话,真烦人,写个程序做为我的Proxy吧,凡是接收到这些话都设置好了自动的回答,接收到其他的话时再通知我回答,怎么样,酷吧。

    代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入。

    行为模式

    13、CHAIN OF RESPONSIBLEITY—晚上去上英语课,为了好开溜坐到了最后一排,哇,前面坐了好几个漂亮的MM哎,找张纸条,写上“Hi,可以做我的女朋友吗?如果不愿意请向前传”,纸条就一个接一个的传上去了,糟糕,传到第一排的MM把纸条传给老师了,听说是个老处女呀,快跑!

    责任链模式:在责任链模式中,很多对象由每一个对象对其下家的引用而接

    起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。

    14、COMMAND—俺有一个MM家里管得特别严,没法见面,只好借助于她弟弟在我们俩之间传送信息,她对我有什么指示,就写一张纸条让她弟弟带给我。这不,她弟弟又传送过来一个COMMAND,为了感谢他,我请他吃了碗杂酱面,哪知道他说:“我同时给我姐姐三个男朋友送COMMAND,就数你最小气,才请我吃面。”,:-(

    命令模式:命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。系统支持命令的撤消。

    15、INTERPRETER—俺有一个《泡MM真经》,上面有各种泡MM的攻略,比如说去吃西餐的步骤、去看电影的方法等等,跟MM约会时,只要做一个Interpreter,照着上面的脚本执行就可以了。

    解释器模式:给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解释器模式将描述怎样在有了一个简单的文法后,使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合。在解释器模式中需要定义一个代表文法的命令类的等级结构,也就是一系列的组合规则。每一个命令对象都有一个解释方法,代表对命令对象的解释。命令对象的等级结构中的对象的任何排列组合都是一个语言。

     

    16、ITERATOR—我爱上了Mary,不顾一切的向她求婚。

    Mary:“想要我跟你结婚,得答应我的条件”

    我:“什么条件我都答应,你说吧”

    Mary:“我看上了那个一克拉的钻石”

    我:“我买,我买,还有吗?”

    Mary:“我看上了湖边的那栋别墅”

    我:“我买,我买,还有吗?”

    Mary:“你的小弟弟必须要有50cm长”

    我脑袋嗡的一声,坐在椅子上,一咬牙:“我剪,我剪,还有吗?”

    ……

    迭代子模式:迭代子模式可以顺序访问一个聚集中的元素而不必暴露聚集的内部表象。多个对象聚在一起形成的总体称之为聚集,聚集对象是能够包容一组对象的容器对象。迭代子模式将迭代逻辑封装到一个独立的子对象中,从而与聚集本身隔开。迭代子模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以上的迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色变化。

    17、MEDIATOR—四个MM打麻将,相互之间谁应该给谁多少钱算不清楚了,幸亏当时我在旁边,按照各自的筹码数算钱,赚了钱的从我这里拿,赔了钱的也付给我,一切就OK啦,俺得到了四个MM的电话。

    调停者模式:调停者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使他们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。调停者模式将对象的行为和协作抽象化,把对象在小尺度的行为上与其他对象的相互作用分开处理。

    18、MEMENTO—同时跟几个MM聊天时,一定要记清楚刚才跟MM说了些什么话,不然MM发现了会不高兴的哦,幸亏我有个备忘录,刚才与哪个MM说了什么话我都拷贝一份放到备忘录里面保存,这样可以随时察看以前的记录啦。

    备忘录模式:备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。

    19、OBSERVER—想知道咱们公司最新MM情报吗?加入公司的MM情报邮件组就行了,tom负责搜集情报,他发现的新情报不用一个一个通知我们,直接发布给邮件组,我们作为订阅者(观察者)就可以及时收到情报啦

    观察者模式:观察者模式定义了一种一队多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。

    20、STATE—跟MM交往时,一定要注意她的状态哦,在不同的状态时她的行为会有不同,比如你约她今天晚上去看电影,对你没兴趣的MM就会说“有事情啦”,对你不讨厌但还没喜欢上的MM就会说“好啊,不过可以带上我同事么?”,已经喜欢上你的MM就会说“几点钟?看完电影再去泡吧怎么样?”,当然你看电影过程中表现良好的话,也可以把MM的状态从不讨厌不喜欢变成喜欢哦。

    状态模式:状态模式允许一个对象在其内部状态改变的时候改变行为。这个对象看上去象是改变了它的类一样。状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子类。

    21、STRATEGY—跟不同类型的MM约会,要用不同的策略,有的请电影比较好,有的则去吃小吃效果不错,有的去海边浪漫最合适,单目的都是为了得到MM的芳心,我的追MM锦囊中有好多Strategy哦。

    策略模式:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模式把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。

    22、TEMPLATE METHOD——看过《如何说服女生上床》这部经典文章吗?女生从认识到上床的不变的步骤分为巧遇、打破僵局、展开追求、接吻、前戏、动手、爱抚、进去八大步骤(Template method),但每个步骤针对不同的情况,都有不一样的做法,这就要看你随机应变啦(具体实现);

    模板方法模式:模板方法模式准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。

    23、VISITOR—情人节到了,要给每个MM送一束鲜花和一张卡片,可是每个MM送的花都要针对她个人的特点,每张卡片也要根据个人的特点来挑,我一个人哪搞得清楚,还是找花店老板和礼品店老板做一下Visitor,让花店老板根据MM的特点选一束花,让礼品店老板也根据每个人特点选一张卡,这样就轻松多了;

    访问者模式:访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。访问者模式使得增加新的操作变的很容易,就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。当使用访问者模式时,要将尽可能多的对象浏览逻辑放在访问者类中,而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。

    December 04

    NetBean 6 发布

    NetBean6 发布,号称这次有很大突破。装了C++的版本,因为Size最小,11Mb,别的都是动辄几十M。
    安装后,发现需要g++环境,就找了下帮助。 可以参看5.5的帮助,对应各种平台。
     

    Netbeans C/C++ Development Pack 需要 C 编译器、C++ 编译器、make 实用程序和 gdb 调试器。

    Windows

    经测试,NetBeans C/C++ Development Pack 可以使用以下编译器和工具:

    • Cygwin 1.5.21
    • Cygwin gcc-core(C 编译器)3.4.x
    • Cygwin gcc-c++(C++ 编译器)3.4.x
    • Cygwin gdb(GNU 调试器)6.5.50
    • Cygwin make 3.80

    如果您已在 Windows 系统上安装了 Cygwin GNU 编译器、GNU makegdb 调试器,并且正确地设置了它们的路径(即,可以找到它们),请确保您使用的是正确的版本。如果您安装了正确的版本,则无需再进行设置。

    检查 Cygwin 编译器和工具的版本:

    1. 要查看 Cygwin 的版本,请键入:

      cygcheck -c cygwin
      
    2. 要查看编译器、makegdb 的版本,请键入:
      gcc --version
      g++ --version
      make --version
      gdb --version
      

    通过 cygwin.com 来安装 GNU 编译器、makegdb 调试器:

    1. 下载 Cygwin setup.exe 程序,方法是:单击位于该页面中间的 "Install or Update Now!" 图标。
    2. 运行 setup.exe 程序。接受缺省设置,直至转入 "Select Your Internet Connection" 页。在此页上选择最适合您的选项。单击“下一步”。
    3. 在 "Choose A Download Site" 页上,选择一个方便您下载的站点。单击“下一步”。
    4. 在 "Select Packages" 页上,选择要下载的包。单击 "Devel" 旁边的 "+" 号,以展开此开发工具类别。您可能需要调整窗口的大小,以便一次可以看到更多的内容。
    5. 通过单击包旁边的 "Skip" 标签来选择要下载的每个包。您至少要选择 gcc-core: C compiler、gcc-g++: C++ compiler、gdb: The GNU Debugger 和 make: the GNU version of the 'make' utility。
    6. 现在将编译器目录添加到您的 Path 变量中:

      1. 打开“控制面板”(“开始”>“设置”>“控制面板”),然后双击“系统”程序。
      2. 选择“高级”标签,然后单击“环境变量”。
      3. 在“环境变量”对话框的“系统变量”面板中,选择 "Path" 变量,然后单击“编辑”。
      4. cygwin-directory\bin 目录的路径添加到 Path 变量中,然后单击“确定”。缺省情况下,cygwin-directoryC:\cygwin。目录名称之间必须用分号进行分隔。
      5. 在“环境变量”和“系统属性”对话框中分别单击“确定”。

    March 17

    全面迎接SVN时代来临

    SourceForge开始全面支持Subversion,这真是个好消息,这预示着CVS独霸天下的时代快要结束,SVN时代就要来临。

    和CVS比起来,SVN的确很强大,这就像它的出现就是为了取代CVS一样,它的目标快要实现了。

    具体的功能特性大家可以上Subversion官方网站查看,这里没必要给出那段生涩不好翻译的英语短句了。

    官方中文网站在这儿,不过这个站的网络通讯太差劲了,一个礼拜5天都上不去:(

    欣慰的是这里有个网站提供一本免费的、非常棒的SVN图书,可以选择在线查看或者下载PDF,有中文版哦,SVN使用者必读。

    如果你对SVN还是表示怀疑可以在这里查看国外网友写的一篇各个版本控制系统功能比较的文章,相信看过后你不会再对SVN表示怀疑了。

    英文看不懂?幸好,有网友将那篇生涩的英文SVN特性用生涩的中文表述出来了 ,中文英文对照着看,凑合着还行。

    冲动的你这时已经手痒痒的想尝试下SVN的魅力,但苦于现在的项目已经建立在CVS上。别担心,这里正好有一篇文章介绍如何将CVS的Repository转换成SNV,转换方法来自这个程序http://cvs2svn.tigris.org/

    如何使用SVN我这里不再介绍,官方的那本书是最好的教程,网上还有大量的安装和使用的文章可以借鉴,这里简单罗列几个SVN辅助的软件:

    1、SubVersion,从 http://subversion.tigris.org/ 下载,是实现服务系统的软件,必装的。

    2、TortoiseSVN,从 http://tortoisesvn.tigris.org/ 下载,是很不错的SVN客户端程序,为windows外壳程序集成到windows资源管理器和文件管理系统的Subversion客户端,用起来很方便,commit动作变得就像Winrar右键压缩一样方便。

    3、SVNService.exe,从 http://dark.clansoft.dk/~mbn/svnservice/ 下载,是专为 SubVersion 开发的一个用来作为 Win32 服务挂接的入口程序。

    4、AnkhSVN,从http://ankhsvn.tigris.org/下载,这是一个专为Visual Studio提供SVN的插件。

    5、Subversive,从http://www.polarion.org/p_subversive.php下载,这时一个为Eclipse提供SVN的插件,据说已经和Eclipse自带的CVS功能有一拼。

    6、还有很多很多SVN相关的工具以及使用TIP介绍,大家可以上官方的相关链接页面中查看,地址:http://subversion.tigris.org/links.html

    August 12

    存取程序状态的几种方法--Java I/O应用杂谈

     超猛地哥哥写的东西,保存下,省得忘记。
     
        今天稍微聊一点关于“程序状态保存”方面的问题,我们很容易就会想到“序列化”(Serialization,有的书上又翻译为“顺序化”或者“串行化”,但“串行”一词总是让我联想到通信和硬件接口,所以我更习惯于“序列化”的叫法,何况这种叫法是有来头的,后面我会谈到这个名称的由来),当然,序列化是一种方便有效的数据存取方式,但它还有更加广泛的应用。广义上讲,就是讨论一下I/O的一些应用。 

    文件I/O:文件流→序列化


    文件流
         文件操作是最简单最直接也是最容易想到的一种方式,我们说的文件操作不仅仅是通过FileInputStream/FileOutputStream这么 “裸”的方式直接把数据写入到本地文件(像我以前写的一个扫雷的小游戏JavaMine就是这样保存一局的状态的),这样就比较“底层”了。 

    主要类与方法和描述 
    1. FileInputStream.read() //从本地文件读取二进制格式的数据 
    2. FileReader.read() //从本地文件读取字符(文本)数据 
    3. FileOutputStream.write() //保存二进制数据到本地文件 
    4. FileWriter.write() //保存字符数据到本地文件
     
    XML
        和上面的单纯的I/O方式相比,XML就显得“高档”得多,以至于成为一种数据交换的标准。以DOM方式为例,它关心的是首先在内存中构造文档树,数据保存在某个结点上(可以是叶子结点,也可以是标签结点的属性),构造好了以后一次性的写入到外部文件,但我们只需要知道文件的位置,并不知道I/O是怎么操作的,XML操作方式可能多数人也实践过,所以这里也只列出相关的方法,供初学者预先了解一下。主要的包是javax.xml.parsersorg.w3c.domjavax.xml.transform。 

    主要类与方法和描述 
    1. DocumentBuilderFactory.newDocumentBuilder().parse() //解析一个外部的XML文件,得到一个Document对象的DOM树 
    2. DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument() //初始化一棵DOM树 
    3. Document.getDocumentElement().appendChild() //为一个标签结点添加一个子结点 
    4. Document.createTextNode() //生成一个字符串结点 
    5. Node.getChildNodes() //取得某个结点的所有下一层子结点 
    6. Node.removeChild()  //删除某个结点的子结点 
    7. Document.getElementsByTagName() 查找所有指定名称的标签结点 
    8. Document.getElementById() //查找指定名称的一个标签结点,如果有多个符合,则返回某一个,通常是第一个 
    9. Element.getAttribute() //取得一个标签的某个属性的的值 
    10. Element.setAttribute() //设置一个标签的某个属性的的值 
    11. Element.removeAttribute() //删除一个标签的某个属性 
    12. TransformerFactory.newInstance().newTransformer().transform() //将一棵DOM树写入到外部XML文件

    序列化
        使用基本的文件读写方式存取数据,如果我们仅仅保存相同类型的数据,则可以用同一种格式保存,譬如在我的JavaMine中保存一个盘局时,需要保存每一个方格的坐标、是否有地雷,是否被翻开等,这些信息组合成一个“复合类型”;相反,如果有多种不同类型的数据,那我们要么把它分解成若干部分,以相同类型(譬如String)保存,要么我们需要在程序中添加解析不同类型数据格式的逻辑,这就很不方便。于是我们期望用一种比较“高”的层次上处理数据,程序员应该花尽可能少的时间和代码对数据进行解析,事实上,序列化操作为我们提供了这样一条途径。
        序列化(Serialization)大家可能都有所接触,它可以把对象以某种特定的编码格式写入或从外部字节流(即ObjectInputStream/ObjectOutputStream)中读取。序列化一个对象非常之简单,仅仅实现一下Serializable接口即可,甚至都不用为它专门添加任何方法: 
    1. public class MySerial implements java.io.Serializable
    2. {
    3.   //...
    4. }
     
    但有一个条件:即你要序列化的类当中,它的每个属性都必须是是“可序列化”的。这句话说起来有点拗口,其实所有基本类型(就是int,char,boolean之类的)都是“可序列化”的,而你可以看看JDK文档,会发现很多类其实已经实现了Serializable(即已经是“可序列化”的了),于是这些类的对象以及基本数据类型都可以直接作为你需要序列化的那个类的内部属性。如果碰到了不是“可序列化”的属性怎么办?对不起,那这个属性的类还需要事先实现Serializable接口,如此递归,直到所有属性都是“可序列化”的。 

    主要类与方法和描述 
    1. ObjectOutputStream.writeObject() //将一个对象序列化到外部字节流 
    2. ObjectInputStream.readObject() //从外部字节流读取并重新构造对象
     
        从实际应用上看来,“Serializable”这个接口并没有定义任何方法,仿佛它只是一个标记(或者说像是Java的关键字)而已,一旦虚拟机看到这个“标记”,就会尝试调用自身预定义的序列化机制,除非你在实现Serializable接口的同时还定义了私有的readObject()或writeObject()方法。这一点很奇怪。不过你要是不愿意让系统使用缺省的方式进行序列化,那就必须定义上面提到的两个方法: 
    1. public class MySerial implements java.io.Serializable
    2. {
    3.   private void writeObject(java.io.ObjectOutputStream out) throws IOException
    4.   {
    5.     //...
    6.   }
    7.   private void readObject(java.io.ObjectInputStream in) throws IOExceptionClassNotFoundException
    8.   {
    9.     //...
    10.   }
    11.   //...

        譬如你可以在上面的writeObject()里调用默认的序列化方法ObjectOutputStream.defaultWriteObject();譬如你不愿意将某些敏感的属性和信息序列化,你也可以调用ObjectOutputStream.writeObject()方法明确指定需要序列化那些属性。关于用户可定制的序列化方法,我们将在后面提到。 

    Bean
        上面的序列化只是一种基本应用,你把一个对象序列化到外部文件以后,用notepad打开那个文件,只能从为数不多的一些可读字符中猜到这是有关这个类的信息文件,这需要你熟悉序列化文件的字节编码方式,那将是比较痛苦的(在《Core Java 2》第一卷里提到了相关编码方式,有兴趣的话可以查看参考资料),某些情况下我们可能需要被序列化的文件具有更好的可读性。另一方面,作为Java组件的核心概念“JavaBeans”,从JDK 1.4开始,其规范里也要求支持文本方式的“长期的持久化”(long-term persistence)。
        打开JDK文档java.beans包里的有一个名为“Encoder”的类,这就是一个可以序列化bean的实用类。和它相关的两个主要类有XMLEcoderXMLDecoder,显然,这是以XML文件的格式保存和读取bean的工具。他们的用法也很简单,和上面ObjectOutputStream/ObjectInputStream比较类似。 

    主要类与方法和描述 
    1. XMLEncoder.writeObject() //将一个对象序列化到外部字节流 
    2. XMLDecoder.readObject() //从外部字节流读取并重新构造对象 

        如果一个bean是如下格式:
    1. public class MyBean
    2. {
    3.   int i;
    4.   char[] c;
    5.   String s;
    6.   //...(get和set操作省略)...

    那么通过XMLEcoder序列化出来的XML文件具有这样的形式: 

    <?xml version="1.0" encoding="UTF-8"?>
    <java version="1.4.0" class="java.beans.XMLDecoder">
      <object class="MyBean">
        <void property="i">
          <int>1</int>
        </void>
        <void property="c">
          <array class="char" length="3">
            <void index="0">
              <int>a</int>
            </void>
            <void index="1">
              <int>b</int>
            </void>
            <void index="2">
              <int>c</int>
            </void>
          </array>
        </void>
        <void property="s">
          <string>fox jump!</string> 
        </void>
      </object>
    </java> 

        像AWTSwing中很多可视化组件都是bean,当然也是可以用这种方式序列化的,下面就是从JDK文档中摘录的一个JFrame序列化以后的XML文件: 

    <?xml version="1.0" encoding="UTF-8"?>
    <java version="1.0" class="java.beans.XMLDecoder">
      <object class="javax.swing.JFrame">
        <void property="name">
          <string>frame1</string>
        </void>
        <void property="bounds">
          <object class="java.awt.Rectangle">
            <int>0</int>
            <int>0</int>
            <int>200</int>
            <int>200</int>
          </object>
        </void>
        <void property="contentPane">
          <void method="add">
            <object class="javax.swing.JButton">
              <void property="label">
                <string>Hello</string>
              </void>
            </object>
          </void>
        </void>
        <void property="visible">
          <boolean>true</boolean>
        </void>
      </object>
    </java> 

        因此但你想要保存的数据是一些不是太复杂的类型的话,把它做成bean再序列化也不失为一种方便的选择。 

    Properties
        在以前我总结的一篇关于集合框架的小文章里提到过,Properties是历史集合类的一个典型的例子,这里主要不是介绍它的集合特性。大家可能都经常接触一些配置文件,如Windows的ini文件,Apache的conf文件,还有Java里的properties文件等,这些文件当中的数据以“关键字-值”对的方式保存。“环境变量”这个概念都知道吧,它也是一种“key-value”对,以前也常常看到版上问“如何取得系统某某信息”之类的问题,其实很多都保存在环境变量里,只要用一条
    1. System.getProperties().list(System.out); 

    就能获得全部环境变量的列表: 

    -- listing properties --
    java.runtime.name=Java(TM) 2 Runtime Environment, Stand...
    sun.boot.library.path=C:\Program Files\Java\j2re1.4.2_05\bin
    java.vm.version=1.4.2_05-b04
    java.vm.vendor=Sun Microsystems Inc.
    java.vendor.url=http://java.sun.com/
    path.separator=;
    java.vm.name=Java HotSpot(TM) Client VM
    file.encoding.pkg=sun.io
    user.country=CN
    sun.os.patch.level=Service Pack 1
    java.vm.specification.name=Java Virtual Machine Specification
    user.dir=d:\my documents\项目\eclipse\SWTDemo
    java.runtime.version=1.4.2_05-b04
    java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment
    java.endorsed.dirs=C:\Program Files\Java\j2re1.4.2_05\li...
    os.arch=x86
    java.io.tmpdir=C:\DOCUME~1\cn2lx0q0\LOCALS~1\Temp\
    line.separator=

    java.vm.specification.vendor=Sun Microsystems Inc.
    user.variant=
    os.name=Windows XP
    sun.java2d.fontpath=
    java.library.path=C:\Program Files\Java\j2re1.4.2_05\bi...
    java.specification.name=Java Platform API Specification
    java.class.version=48.0
    java.util.prefs.PreferencesFactory=java.util.prefs.WindowsPreferencesFac...
    os.version=5.1
    user.home=D:\Users\cn2lx0q0
    user.timezone=
    java.awt.printerjob=sun.awt.windows.WPrinterJob
    file.encoding=GBK
    java.specification.version=1.4
    user.name=cn2lx0q0
    java.class.path=d:\my documents\项目\eclipse\SWTDemo\bi...
    java.vm.specification.version=1.0
    sun.arch.data.model=32
    java.home=C:\Program Files\Java\j2re1.4.2_05
    java.specification.vendor=Sun Microsystems Inc.
    user.language=zh
    awt.toolkit=sun.awt.windows.WToolkit
    java.vm.info=mixed mode
    java.version=1.4.2_05
    java.ext.dirs=C:\Program Files\Java\j2re1.4.2_05\li...
    sun.boot.class.path=C:\Program Files\Java\j2re1.4.2_05\li...
    java.vendor=Sun Microsystems Inc.
    file.separator=\
    java.vendor.url.bug=http://java.sun.com/cgi-bin/bugreport...
    sun.cpu.endian=little
    sun.io.unicode.encoding=UnicodeLittle
    sun.cpu.isalist=pentium i486 i386
     

    主要类与方法和描述 
    1. load() //从一个外部流读取属性 
    2. store() //将属性保存到外部流(特别是文件) 
    3. getProperty() //取得一个指定的属性 
    4. setProperty() //设置一个指定的属性 
    5. list() //列出这个Properties对象包含的全部“key-value”对 
    6. System.getProperties() //取得系统当前的环境变量 


        你可以这样保存一个properties文件: 

    1. Properties prop = new Properties();
    2. prop.setProperty("key1""value1");
    3. ...
    4. FileOutputStream out = new FileOutputStream("config.properties");
    5. prop.store(out, "--这里是文件头,可以加入注释--"); 

    Preferences
         如果我说Java里面可以不使用JNI的手段操作Windows的注册表你信不信?很多软件的菜单里都有“Setting”或“Preferences” 这样的选项用来设定或修改软件的配置,这些配置信息可以保存到一个像上面所述的配置文件当中,如果是Windows平台下,也可能会保存到系统注册表中。从JDK 1.4开始,Java在java.util下加入了一个专门处理用户和系统配置信息的java.util.prefs包,其中一个类Preferences是一种比较“高级”的玩意。从本质上讲,Preferences本身是一个与平台无关的东西,但不同的OS对它的SPI (Service Provider Interface)的实现却是与平台相关的,因此,在不同的系统中你可能看到首选项保存为本地文件、LDAP目录项、数据库条目等,像在Windows平台下,它就保存到了系统注册表中。不仅如此,你还可以把首选项导出为XML文件或从XML文件导入。 

    主要类与方法和描述 
    1. systemNodeForPackage() //根据指定的Class对象得到一个Preferences对象,这个对象的注册表路径是从“HKEY_LOCAL_MACHINE\”开始的 
    2. systemRoot() //得到以注册表路径HKEY_LOCAL_MACHINE\SOFTWARE\Javasoft\Prefs 为根结点的Preferences对象 
    3. userNodeForPackage() //根据指定的Class对象得到一个Preferences对象,这个对象的注册表路径是从“HKEY_CURRENT_USER\”开始的 
    4. userRoot() //得到以注册表路径HKEY_CURRENT_USER\SOFTWARE\Javasoft\Prefs 为根结点的Preferences对象 
    5. putXXX() //设置一个属性的值,这里XXX可以为基本数值型类型,如int、long等,但首字母大写,表示参数为相应的类型,也可以不写而直接用put,参数则为字符串 
    6. getXXX() //得到一个属性的值 
    7. exportNode() //将全部首选项导出为一个XML文件 
    8. exportSubtree() //将部分首选项导出为一个XML文件 
    9. importPreferences() //从XML文件导入首选项 

        你可以按如下步骤保存数据:
    1. Preferences myPrefs1 = Preferences.userNodeForPackage(this);// 这种方法是在“HKEY_CURRENT_USER\”下按当前类的路径建立一个注册表项
    2. Preferences myPrefs2 = Preferences.systemNodeForPackage(this);// 这种方法是在“HKEY_LOCAL_MACHINE\”下按当前类的路径建立一个注册表项
    3. Preferences myPrefs3 = Preferences.userRoot().node("com.jungleford.demo");// 这种方法是在“HKEY_CURRENT_USER\SOFTWARE\Javasoft\Prefs\”下按“com\jungleford\demo”的路径建立一个注册表项
    4. Preferences myPrefs4 = Preferences.systemRoot().node("com.jungleford.demo");// 这种方法是在“HKEY_LOCAL_MACHINE\SOFTWARE\Javasoft\Prefs\”下按“com\jungleford\demo”的路径建立一个注册表项
    5. myPrefs1.putInt("key1", 10);
    6. myPrefs1.putDouble("key2", -7.15);
    7. myPrefs1.put("key3""value3");
    8. FileOutputStream out = new FileOutputStream("prefs.xml");
    9. myPrefs1.exportNode(out);

    网络I/O:Socket→RMI


    Socket
        Socket编程可能大家都很熟,所以就不多讨论了,只是说通过socket把数据保存到远端服务器或从网络socket读取数据也不失为一种值得考虑的方式。

    RMI
        RMI机制其实就是RPC(远程过程调用)的Java版本,它使用socket作为基本传输手段,同时也是序列化最重要的一个应用。现在网络传输从编程的角度来看基本上都是以流的方式操作,socket就是一个例子,将对象转换成字节流的一个重要目标就是为了方便网络传输。
        想象一下传统的单机环境下的程序设计,对于Java语言的函数(方法)调用(注意与C语言函数调用的区别)的参数传递,会有两种情况:如果是基本数据类型,这种情况下和C语言是一样的,采用值传递方式;如果是对象,则传递的是对象的引用,包括返回值也是引用,而不是一个完整的对象拷贝!试想一下在不同的虚拟机之间进行方法调用,即使是两个完全同名同类型的对象他们也很可能是不同的引用!此外对于方法调用过程,由于被调用过程的压栈,内存“现场”完全被被调用者占有,当被调用方法返回时,才将调用者的地址写回到程序计数器(PC),恢复调用者的状态,如果是两个虚拟机,根本不可能用简单压栈的方式来保存调用者的状态。因为种种原因,我们才需要建立RMI通信实体之间的“代理”对象,譬如“存根”就相当于远程服务器对象在客户机上的代理,stub就是这么来的,当然这是后话了。
        本地对象与远程对象(未必是物理位置上的不同机器,只要不是在同一个虚拟机内皆为“远程”)之间传递参数和返回值,可能有这么几种情形:
    • 值传递:这又包括两种子情形:如果是基本数据类型,那么都是“可序列化”的,统统序列化成可传输的字节流;如果是对象,而且不是“远程对象”(所谓“远程对象”是实现了java.rmi.Remote接口的对象),本来对象传递的应该是引用,但由于上述原因,引用是不足以证明对象身份的,所以传递的仍然是一个序列化的拷贝(当然这个对象也必须满足上述“可序列化”的条件)。 
    • 引用传递:可以引用传递的只能是“远程对象”。这里所谓的“引用”不要理解成了真的只是一个符号,它其实是一个留在(客户机)本地stub中的,和远端服务器上那个真实的对象张得一模一样的镜像而已!只是因为它有点“特权”(不需要经过序列化),在本地内存里已经有了一个实例,真正引用的其实是这个“孪生子”。
     
        由此可见,序列化在RMI当中占有多么重要的地位。

    数据库I/O:CMP、Hibernate


    什么是“Persistence”
         用过VMWare的朋友大概都知道当一个guest OS正在运行的时候点击“Suspend”将虚拟OS挂起,它会把整个虚拟内存的内容保存到磁盘上,譬如你为虚拟OS分配了128M的运行内存,那挂起以后你会在虚拟OS所在的目录下找到一个同样是128M的文件,这就是虚拟OS内存的完整镜像!这种内存的镜像手段其实就是“Persistence”(持久化)概念的由来。

    CMP和Hibernate
        因为我对J2EE的东西不是太熟悉,随便找了点材料看看,所以担心说的不到位,这次就不作具体总结了,人要学习……真是一件痛苦的事情[cry]

    序列化再探讨


        从以上技术的讨论中我们不难体会到,序列化是Java之所以能够出色地实现其鼓吹的两大卖点??分布式(distributed)和跨平台(OS independent)的一个重要基础。TIJ(即“Thinking in Java”)谈到I/O系统时,把序列化称为“lightweight persistence”??“轻量级的持久化”,这确实很有意思。

    为什么叫做“序列”化?
        开场白里我说更习惯于把“Serialization”称为“序列化”而不是“串行化”,这是有原因的。介绍这个原因之前先回顾一些计算机基本的知识,我们知道现代计算机的内存空间都是线性编址的(什么是“线性”知道吧,就是一个元素只有一个唯一的“前驱”和唯一的“后继”,当然头尾元素是个例外;对于地址来说,它的下一个地址当然不可能有两个,否则就乱套了),“地址”这个概念推广到数据结构,就相当于“指针”,这个在本科低年级大概就知道了。注意了,既然是线性的,那“地址”就可以看作是内存空间的“序号”,说明它的组织是有顺序的,“序号”或者说“序列号”正是“Serialization”机制的一种体现。为什么这么说呢?譬如我们有两个对象a和b,分别是类A和B的实例,它们都是可序列化的,而A和B都有一个类型为C的属性,根据前面我们说过的原则,C当然也必须是可序列化的。
    1. import java.io.*;
    2. ...
    3. class A implements Serializable
    4. {
    5.   C c;
    6.   ...
    7. }
    8. class B implements Serializable
    9. {
    10.   C c;
    11.   ...
    12. }
    13. class C implements Serializable
    14. {
    15.   ...
    16. }
    17. A a;
    18. B b;
    19. C c1;
    20. ...

        注意,这里我们在实例化a和b的时候,有意让他们的c属性使用同一个C类型对象的引用,譬如c1,那么请试想一下,但我们序列化a和b的时候,它们的c属性在外部字节流(当然可以不仅仅是文件)里保存的是一份拷贝还是两份拷贝呢?序列化在这里使用的是一种类似于“指针”的方案:它为每个被序列化的对象标上一个“序列号” (serial number),但序列化一个对象的时候,如果其某个属性对象是已经被序列化的,那么这里只向输出流写入该属性的序列号;从字节流恢复被序列化的对象时,也根据序列号找到对应的流来恢复。这就是“序列化”名称的由来!这里我们看到“序列化”和“指针”是极相似的,只不过“指针”是内存空间的地址链,而序列化用的是外部流中的“序列号链”
        使用“序列号”而不是内存地址来标识一个被序列化的对象,是因为从流中恢复对象到内存,其地址可能就未必是原来的地址了??我们需要的只是这些对象之间的引用关系,而不是死板的原始位置,这在RMI中就更是必要,在两台不同的机器之间传递对象(流),根本就不可能指望它们在两台机器上都具有相同的内存地址。 

    更灵活的“序列化”:transient属性和Externalizable
        Serializable确实很方便,方便到你几乎不需要做任何额外的工作就可以轻松将内存中的对象保存到外部。但有两个问题使得Serializable的威力收到束缚:
        一个是效率问题,《Core Java 2》中指出,Serializable使用系统默认的序列化机制会影响软件的运行速度,因为需要为每个属性的引用编号和查号,再加上I/O操作的时间(I/O和内存读写差的可是一个数量级的大小),其代价当然是可观的。
        另一个困扰是“裸”的Serializable不可定制,傻乎乎地什么都给你序列化了,不管你是不是想这么做。其实你可以有至少三种定制序列化的选择。其中一种前面已经提到了,就是在 implements Serializable的类里面添加私有的writeObject()和readObject()方法(这种 Serializable就不裸了,[:E]),在这两个方法里,该序列化什么,不该序列化什么,那就由你说了算了,你当然可以在这两个方法体里面分别调用ObjectOutputStream.defaultWriteObject()和ObjectInputStream.defaultReadObject()仍然执行默认的序列化动作(那你在代码上不就做无用功了?呵呵),也可以用ObjectOutputStream.writeObject()和 ObjectInputStream.readObject()方法对你中意的属性进行序列化。但虚拟机一看到你定义了这两个方法,它就不再用默认的机制了。
        如果仅仅为了跳过某些属性不让它序列化,上面的动作似乎显得麻烦,更简单的方法是对不想序列化的属性加上transient关键字,说明它是个“暂态变量”,默认序列化的时候就不会把这些属性也塞到外部流里了。当然,你如果定义writeObject()和readObject()方法的化,仍然可以把暂态变量进行序列化。题外话,像transientviolatefinally这样的关键字初学者可能会不太重视,而现在有的公司招聘就偏偏喜欢问这样的问题 :(
        再一个方案就是不实现Serializable而改成实现Externalizable接口。我们研究一下这两个接口的源代码,发现它们很类似,甚至容易混淆。我们要记住的是:Externalizable默认并不保存任何对象相关信息!任何保存和恢复对象的动作都是你自己定义的。Externalizable包含两个public的方法:
    1. public void writeExternal(ObjectOutput out) throws IOException;
    2. public void readExternal(ObjectInput in) throws IOExceptionClassNotFoundException;

         乍一看这和上面的writeObject()和readObject()几乎差不多,但Serializable和Externalizable走的是两个不同的流程:Serializable在对象不存在的情况下,就可以仅凭外部的字节序列把整个对象重建出来;但Externalizable在重建对象时,先是调用该类的默认构造函数(即不含参数的那个构造函数)使得内存中先有这么一个实例,然后再调用readExternal方法对实例中的属性进行恢复,因此,如果默认构造函数中和readExternal方法中都没有赋值的那些属性,特别他们是非基本类型的话,将会是空(null)。在这里需要注意的是,transient只能用在对Serializable而不是Externalizable的实现里面。 

    序列化与克隆
        从“可序列化”的递归定义来看,一个序列化的对象貌似对象内存映象的外部克隆,如果没有共享引用的属性的化,那么应该是一个深度克隆。关于克隆的话题有可以谈很多,这里就不细说了,有兴趣的话可以参考IBM developerWorks上的一篇文章:JAVA中的指针,引用及对象的clone

    一点启示


         作为一个实际的应用,我在写那个简易的邮件客户端JExp的时候曾经对比过好几种保存Message对象(主要是几个关键属性和邮件的内容)到本地的方法,譬如XML、Properties等,最后还是选择了用序列化的方式,因为这种方法最简单, 大约可算是“学以致用”罢。这里“存取程序状态”其实只是一个引子话题罢了,我想说的是??就如同前面我们讨论的关于logging的话题一样??在Java面前对同一个问题你可以有很多种solution:熟悉文件操作的,你可能会觉得Properties、XML或Bean比较方便,然后又发现了还有Preferences这么一个东东,大概又会感慨“天外有天”了,等到你接触了很多种新方法以后,结果又会“殊途同归”,重新反省Serialization机制本身。这不仅是Java,科学也是同样的道理。

    参考资料


    August 11

    eclipse你最常用的快捷键

    ctrl  +  shift  +  g:查看引用  
    ctrl  +  shift  +  n:重命名  
    ctrl  +  shift  +  o:导入类  
    ctrl  +  shift  +  r:启动上次运行  
    ctrl  +  shift  +  f:格式化代码  
    ctrl  +  c:复制  
    ctrl  +  v:粘贴  
    ctrl  +  x:切剪  
    ctrl  +  a:全选  
    ctrl  +  f:查找  
    ctrl  +  z:撤销  
    ctrl  +  y:重做  
    ctrl  +  s:保存  
    ---------------------------------------------------------------  
     
    用的最多的应该就是CTRL+SHIFT+S  
    还有格式化用的也挺多。  
    ---------------------------------------------------------------  
     
    ctrl  +  shift  +  f  格式化代码  
    ctrl  +  shift  +  o  组织导入  
    F3    打开声明  
    Alt  +  shift  +  r    重命名变量  
     
    ---------------------------------------------------------------  
     
    up  
    ---------------------------------------------------------------  
     
    Alt+/        
     
    ---------------------------------------------------------------  
     
    alt  +  left  
    alt  +  right  
    ctrl  +  q  
    ---------------------------------------------------------------  
     
    ctrl  +  shift  +  g:查看引用  
    ctrl  +  shift  +  n:重命名  
    ctrl  +  shift  +  f:格式化代码  
    ctrl  +  c:复制  
    ctrl  +  v:粘贴  
    ctrl  +  a:全选  
    ctrl  +  f:查找  
    ctrl  +  z:撤销  
    ctrl  +  s:保存  
    Alt    +  /  智能提示  
    ---------------------------------------------------------------  
     
    ctrl  +  shift  +  g:查看引用  
    ctrl  +  shift  +  n:重命名  
    ctrl  +  shift  +  o:导入类  
    ctrl  +  shift  +  r:启动上次运行  
    ctrl  +  shift  +  f:格式化代码  
    ctrl  +  c:复制  
    ctrl  +  v:粘贴  
    ctrl  +  x:切剪  
    ctrl  +  a:全选  
    ctrl  +  f:查找  
    ctrl  +  z:撤销  
    ctrl  +  y:重做  
    ctrl  +  s:保存  
    Alt    +  /  智能提示  
    F3    打开声明  
    Alt  +  shift  +  r    重命名变量  
    其实最常用的就是下面几个:  
     
    ctrl  +  shift  +  o:导入类  
     
    ctrl  +  shift  +  f:格式化代码  
    ctrl  +  c:复制  
    ctrl  +  v:粘贴  
    ctrl  +  x:切剪  
    ctrl  +  z:撤销  
    ctrl  +  s:保存  
    Alt    +  /  智能提示  
     
    ---------------------------------------------------------------  
     
    Ctrl+M:  工作区最大化/最小化  
    Alt+/:    智能提示  
    F3:          察看声明  
    Crtl+1:  修正错误  
     
    Shift+Alt+T:  重构  
    Shift+Alt+M:  提取函数  
    Shift+Alt+R:  重命名  
    Shift+Alt+C:  更改函数标记  
     
    Ctrl+Shitf+F:  格式化代码  
     
    ---------------------------------------------------------------  
     
    ctrl  +  shift  +  o:导入类  
    atl  +  /:提示  
    ctrl  +  shift  +  t:查找相关信息  
    ---------------------------------------------------------------  
     
    Eclipse快捷键指南                                                                          
    编辑  
    作用域            功能            快捷键  
    全局            查找并替换            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  

    myeclipse 的序列号 (怕忘记了)

    myeclipse序列号
    myEclipse 3.8.2 for eclipse 3.0.x
    sub:            hello
    sub code:    uAR7ZL-835-56-54678656396403716

    sub: IceCraft
    sub code:  VAR7ZL-819-56-54678656108018950

    April 20

    日本語には片仮名と平仮名をチェックの方法

    U+30A0, in the Unicode standard, refers to a character whose hexadecimal representation is 30A0, which is 12448 in decimal. The way to represent that particular character in Java code is '\u30a0'.

    So if you want to know if a particular character is Katakana, then one way is

    char c = // something; if (c >= '\u30a0' && c <= '\u30ff') // it's Katakana

    A simpler way is

    char c = // something; if (Character.UnicodeBlock.of(c) == Character.UnicodeBlock.KATAKANA) // it's Katakana char c = // something; if (Character.UnicodeBlock.of(c) == Character.UnicodeBlock.HIRAGANA) // it's HIRAGANA