赵翔鹏的Blog Xiangpeng's Thinkpad

23五/102

32位和64位的麻烦

如果没有8G内存,还是不要装64位的Windows。今天又被折腾了一下。

装了个淘宝旺旺,店主发了个链接,我打开之后拍下宝贝去付款,IE反复提示安装控件,但怎么都装不上。而且还看到提示某SmartCard驱动无法安装。重启了一下还是不行,连网上银行都出毛病了,以前明明是好的啊。

这时忽然发现任务栏上有两个IE图标。。。image

原来淘宝旺旺里面的链接会默认打开64位的IE。难怪安全控件会不兼容。还好是用Windows7,有图标分组的功能。如果不是看到两个IE图标,我估计还得折腾半天。看了一下,旺旺倒是32位的。不知道做旺旺的人是咋整成这样的。

image

最后说一个常见的误解。如果有4G内存,虽然32位的系统会导致部分内存无法访问,也许只能用到3.0-3.5G的内存,但其实你换成64位的系统之后未必就提高了内存的使用效率。以前一个指针占32bit,换成64位后每个指针都大了一倍,所以每个程序都会占更多的内存,最终又还回去了。再考虑到种种潜在的兼容性问题,还是用32位安心啊。

17三/100

时光

今天读到一篇好文章,互联网迟到的80后 (http://tech.sina.com.cn/i/2010-03-10/13573925303.shtml)

想了很多。

有商业模式的技术才是好技术。

最近无用的idea很多……列几个在这里吧:

  • sql语言里能不能加入Debug.Assert这样的东西?写stored procedure的时候,或者是用sql来做一些数据挖掘工作的时候,一旦有一堆join,或是用了临时表,就很难验证最终数据的正确性。敲错一个地方,数据有个小偏差,很可能看不出来。

    这本质上是个data constraint。也许可以用check constraint来实现,比如:

    ALTER TABLE dbo.Vendors ADD CONSTRAINT CK_Vendor_CreditRating
        CHECK (CreditRating >= 1 AND CreditRating <= 5)

    但是表达更复杂的约束的话,只能写个自定义函数,太麻烦了,管理起来也不容易。

    总之,SQL是个不容易扩展的语言……也没有一个很好用的编辑环境。

  • 怎么实现一个山寨版MapReduce。现有的开源实现都太复杂了,如果我就是想做一个最简单的多机并行计算系统呢?
    比如,能不能用Windows自带的DFS做分布式文件系统,用powershell 2.0来控制多台机器间的通讯。
  • 能不能压缩一个进程占用的内存。至少把所有的VirtualAlloc重定向到一块特殊的内存空间。在系统内存不足时启用这个策略。
  • 能不能给一个进程的private working set做一个单独的pagefile,从而降低pagefile.sys里的碎片。
  • 最后这个也许更靠谱:好茶叶跟红酒一样怕氧化。能不能做个真空抽气袋来保存,每次喝点之后,抽光空气存起来。
    Google了一下……果然已经有“真空抽气保鲜盒”这样的东西了……
15八/091

由无数细节组成的系统

计算机科学总是讲abstraction。这是源自数学的思维。formal methods可以说把这个发挥到了极致。但实际系统并不是这样。不要说构建Windows这样的庞然大物,就只是要写一个实用的小软件,就会遇到无数的细节。performance,security,scalability,debugging……除了functionality之外,有无数的事情要考虑。

传统的formal methods注重functionality也没错,因为在某些情形下(比如算法领域)functionality的正确性已经够难了。但这却不是软件工程中的主要问题。

我们能做的,就是尽量挣扎着把细节包装起来。比如,工程师做出一个个框架,分出若干个层次。框架可以帮助我们,但框架并不能解决一切问题,因为好的程序员必须了解各种细节。最近读一个基于ATL/WTL的真正的product code,发现这一年来看的n本书都没白看。比如,不懂C++内存布局,不懂各种calling convention,compiler的差异,就不能理解COM的设计(btw,强烈推荐《COM本质论》的第一章)。

researcher也自有办法。因为很多细节是正交的。在TASE'09上听到Zhong Shao做的invited talk,分不同的aspect去验证汇编代码的不同性质,这个是正确的思路,而且我觉得可以做更多的东西出来。以前做business process的security验证,这个也可以归到这个思路,不过high level的东西比较虚,做起来虽然不难但并不容易很深入。而Shao对汇编和机器本身建模,比如对interrupt handling建模,这个就很有新意,且竞争者较少——很多数学系出身做formal methods的人,都不太懂系统底层。

是的,跟C++和汇编相比,写Java很容易。不过事情一旦很容易,于工业上看,就不值钱了;于学术上看,就没啥好研究的了。所以,重视各种细节吧。

21二/095

C#和CLR的15个细节

这两周一直在training,一共听了4个课程,相当的累啊。

今天先把Jeffrey Richter的培训课:C# and CLR中讲到的一些东西整理一下:

  1. foreach的性能问题

    foreach(string s in rows) { foo(s); }的实现是:

  2. IEnumerator e = rows.GetEnumerator();
    try {
      string s;
      while (e.MoveNext()) {
        s = (String) e.Current;
        foo(s);
      }
    }
    finally {
      IDisposable d = e as IDisposable;
      if (d != null) d.Dispose();
    }

    每一步都调用了e.MoveNext()和e.Current两个方法;而大多数时候,完全有可能优化为一次调用。显然这对性能是有影响的。虽然foreach对于数组作了单独的优化(编译成for循环),但这还是值得注意的。

    那么,怎么做比较快?

    对于List等Collection,可以用ForEach(Action<T> action),FindAll(Predicate<T> match),ConvertAll<U>(Converter<T, U> converter)等方法。它们比较快,但不是所有实现IEnumerable的类都提供。

    LINQ追求compatiblity,而不是performance。因此LINQ的实现完全采用了foreach。值得注意。

  1. yield的实现原理

    实现一个支持IEnumerable的对象时,一般会用到yield关键字,这样foreach遍历这个对象时,可以做到lazy evaluation。例如:

    class MyCollection: IEnumerable<char> {
      private string s; ...
      public IEnumerable<char> GetEnumerator() {
        for (int i=0; i<s.Length; i++) {
          yield return s[i];
        }
      }
    }

    执行到yield时函数返回,下次调用时,接着上次运行的位置继续运行。这个continuation的效果是怎么做的呢?

    包含yield的函数都会被编译器做成一个状态机。每调一次,就接着上次的状态继续运行。简单有效啊。我一直以为要有什么特殊的办法呢。

  2. exception handling的实现决定了throw的performance较差。

    可以用Int32.TryParse代替try{Int32.Parse…}catch{…},稍快一点。类似地建议使用Dictionary.TryGetValue。

  3. .Net CLR执行引擎对应于MSCorWks.dll和MSCorEE.dll这两个文件。

  4. .Net 3.0, 3.5没有对CLR作任何修改。

    所有增加的东西(比如LINQ)都是syntactic sugar,只改了C#编译器而已。

  5. AppDomain

    如果把.Net虚拟机看成一个虚拟操作系统,AppDomain的概念则类似于操作系统中的进程。

    可以用代码创建一个AppDomain,然后动态加载/卸载assembly,还可以设置权限,相当于提供了一个沙箱。

    跨AppDomain的调用类似于RPC。

    调用某个AppDomain内部的obj.foo(x)时,.net会自动帮你做出一个proxy object,你所调用的obj其实是一个proxy object。传给foo的参数x会先被被marshal,以保证AppDomain被安全隔离。

    谁用AppDomain?SQL Server用这个技术实现managed存储过程。IIS会把不同的Web Application放在不同的AppDomain里,以实现动态装卸。

  6. 动态载入Assembly的陷阱

    Sytem.Reflection.Assembly.LoadFrom(pathName)并不会载入pathName所指定的dll,而是看看pathName那个dll的名字、版本,然后到系统默认位置去找。(陷阱啊)

  7. C#里用reflection创建一个新对象

    用Activator.CreateInstance。(奇怪的名字啊。)

  8. C#泛型之“where”

    可以用“where”来限定T的接口。例如

    static T min<T>(T arg1, T arg2) where T: IComparable<T> {…}

    不写where的话,就不能调arg1.CompareTo(arg2)。

    为啥不把T换成IComparable?一是为保证arg1, arg2一定是同一个类型,二是泛型的效率更高。(JIT会为不同类型的T各生成一份native code,从而避免了boxing)

    更多where的细节:

    * 要想调T t1 = new T(),必须声明where T: new()或者where T: struct

    * 要写T t2 = null,必须声明where T: class

    * T z = default(T)是一个特殊的用法,会把T的每个bit都置为0。

    * 假设定义了Foo<T>(T x, T y),则if (x==null) … 是可以通过的,虽然C#中value type的值不允许为null(例如int a=null是错的)。这是因为,此时的语义是一致的,反正if里面的操作不被执行就是了,所以编译器对这种特殊情况网开一面。

    * if (x==y)不行,除非写了where T: baseclass。(这里我也没理解为啥。。。>_<好像说是不知道应该用reference比较还是value比较?)

  9. 匿名函数的背后。。。

    在C# 2.0以后可以用匿名的delegate,如ThreadPool.QueueWorkItem(delegate (Object obj) { Console.WriteLine(obj); })

    但编译器的实现会带来一点点overhead,会生成一个小小的静态WaitCallback对象,可以用Reflector看生成的代码。(不要打开Reflector的optimization,否则就看不到了)

    如果是自己写的话,可以选择每次动态建立一个WaitCallback对象然后销毁。当然这样做性能可能差一些,但这里的idea是:编译器会自动做一些事,但不一定是你所希望的。在使用这些高级feature前,最好先搞清楚背后发生了什么。

    另一个细节:如果匿名函数中使用了外层函数的局部变量(即所谓的function closure),会导致创建额外的shared-state object,把用到的局部变量做成一个新对象传给匿名函数。

    上述描述同样适用于lambda函数。因为C#的lambda函数就是匿名函数,改了改语法而已。

  10. Nullable type

    虽然C#要求value type的值不能是null,但写数据库程序时经常遇到某个值是null的情况。为此,C#2.0引入了Nullable type。例如,int? x = null。

    int? x其实就是一个缩写,等价于Nullable<int> x。Nullable是预定义的一个类,简单地对x作了封装。(因为增加了一个类,显然对性能稍微有点影响)

    这个小改动的实现其实很麻烦,需要修改CLR。为什么?因为原先的x是一个value type,现在则变成了一个object,看这个:

    void M(Object o) {
      if (o=null) {Bar();}
    }
    void F() {
      int? x = null;
      M(x);
    }

    如果CLR不专门做修正的话,上面的Bar()不会被执行。(思考题:想一想为什么~)

    另外,C#还引入了一个默认值运算符“??”,称为null-coalescing operator。

    一句话,x ?? value是 (x==null) ? value: x的简写。

  11. 属性(property)的简单声明

    public int x {get; private set;}是个很好用的句式。

    注意,public int x {get;}是错误的,不能通过编译。

  12. Extension method

    static class MyExtMethods {
      static public GetFirstLetter(this string s) {return s[0];}
    }

    然后就可以用string s = “hello”; char ch = s.GetFirstLetter()了。

    原理很简单,编译器把上面那句话翻译成MyExtMethods.GetFirstLetter(s)。LINQ就用到了这个技术。

  13. 匿名类型的背后。。。

    var o = new {name = “Xiangpeng”, id = 123 };

    在这背后是编译器生成的一个匿名类,包含了两个只读属性,形如public int id { get {return _id;} }为什么不做成可读写的呢?

    很微妙。匿名类自动生成了GetHashCode(),返回的是对所有属性的hash code做XOR的结果。如果允许修改属性值,那么Hash code的值就会变化;而这个可能会出问题~保险起见,只读吧。

  14. 每个thread占1M物理内存

    在Win32编程中thread的1M stack空间是Reserve的,直到真正用时才占用物理内存;而在.net中,这1M空间直接被commit。

    还好,可以在新建thread时指定stack size。不过这也比较危险,设小了怕不够。实际上,最好尽量避免创建thread——太多的thread要么导致CPU竞争和context switch,要么都block着浪费内存。建议是:能用ThreadPool就用ThreadPool。

累死了,写了这么多……Jeffrey还有一个更牛的课:.net threading。里面的东西更多……要不要再总结一下呢。。。Thinking

15一/097

一台牛机器

xb一下最近在用的服务器,昵称“栅栏机”,如图。Open-mouthed

32G内存挺好的。我的开发用机有5G内存,感觉刚刚够。

操作系统必须考虑大多数用户的配置,以便采用最佳的管理策略,所以Windows的内核要不断升级。可以出一道考试题:如果有32G内存,那应该对Windows这样的Desktop OS做哪些修改,才能方便用户?

还要多少年,一般的PC才能装上32G内存呢~

clip_image001

30十二/082

SQL Server 2008 Reporting Service使用技巧

SSRS用来做报表很方便,据说比Crystal Reports好用。简单的说,你的报表数据来自SQL语句,然后就可以在类似Excel的环境中画图了。

说是这么说,实际做起来还是会遇到很多小麻烦。下面是总结的一些技巧:

  1. 改变图表上字体大小(如X轴、Y轴上的数字):

    先把LabelsAutoFitDisabled属性设为True ,然后再设置字体大小

  2. Data Label显示设置:

    在Series的SmartLabels里面有一些有用的选项。AllowOutsidePlotArea设为True比较好。

  3. 自定义Legend标题:

    要右键选Series的属性,不是Legend的属性

  4. Pie chart显示百分比:

    在Report properties - code中加入如下代码:

    Public Function GetLabel(ByVal currentValue As Integer, ByVal totalValue As Integer) As String

    If currentValue / totalValue < 0 Then '表示百分比小于1的不显示出来

    Return " "

    Else

    Return Format(currentValue / totalValue, "P1")

    End If

    End Function

    然后设置data label表达式:=Code.GetLabel(Count(Fields!IssueID.Value), Count(Fields!IssueID.Value, "Issues"))

  5. Pie chart排序

    右键点一个Category,在Category Group Properties中设置Sort表达式即可

    clip_image001

  6. 在Pie chart饼图外侧显示DataLabel:

    设置Series的CustomAttributes,PieLabelStyle=Outside

    clip_image002

    注意:参考AdventureWorks示例,有一个很好的饼图。(google上搜到的都是过时的东西)

  7. Asp.net中使用ReportViewer控件:

    必须在IIS的Handler Mappings中新建一个Managed Handler才能正常显示。 可以google搜索Reserved.ReportViewerWebControl.axd。

    clip_image003

    clip_image004

    另一个解决方法:据说配置下web.config文件也可以搞定:

    <httpHandlers>

        <add path="Reserved.ReportViewerWebControl.axd" verb="*"

             type="Microsoft.Reporting.WebForms.HttpHandler,

                   Microsoft.ReportViewer.WebForms,

                   Version=8.0.0.0, Culture=neutral,

                   PublicKeyToken=?????????????"

             validate="false" />

    </httpHandlers>

    http://www.knowsky.com/340782.html

  8. ReportViewer控件:

    可以设置ZoomMode属性为PageWidth,这样可以自动放缩。

    图表放大时会失真,因为不是矢量图。建议把原始报表拉大一些,在页面中留一个小点的空,默认ZoomMode=PageWidth,用户选择100%就可以放大。

    所有的toolbar按钮都可以选择显示或关闭,设置ShowXXX属性即可。

  9. 其他:

    对报表修改之后要及时Deploy,才能看到效果。可以在VS的configuration manager里面设置,debug时自动deploy,比较方便。这个默认是没有打开的,如下图所示,建议把Deploy的对勾打上。

    clip_image005

标签: 2 评论
21十二/080

LINQ学习笔记

什么是LINQ

LINQ是C# 3.0的新功能,可以用统一的的语法(类似sql、或者是函数式语言)访问各种数据,包括数据库、XML、对象。

Anders Hejlsberg说LINQ是"It's about turning query set operations and transforms into first-class concepts of the language" (http://channel9.msdn.com/showpost.aspx?postid=114680)。我的感受是:LINQ可以取代SQL,成为一种统一Database、XML世界的查询语言。

举个例子:

var bycity = from p in db.Products
             join o in db.Orders
             on p.productID equals o.productID
             where o.ShipToCity == "Beijing"
             select new {o.orderID, p.productName};
foreach (d in bycity) ...
//或者GridView1.DataSource = bycity

这里的db.doctors可以是数据库DataSet,可以是个xml文件,也可以是个List<T>。

所有的新关键字,比如var, from, group, select...都是syntactic sugar。编译器会自动把它翻译成基于Lambda表达式的代码,最后翻译成.net 2.0的程序。var的类型是type inference推断出来的,是强类型的,不等于object;select相当于函数式语言中的Map,当然也支持函数式语言中的Reduce(叫做Aggregate);函数式语言用到的Lambda表达式其实很容易用函数指针delegate实现;最后生成的是一个匿名类型(anonymous type)。

推荐这本小书:LINQ: The Future of Data Access in C# 3.0,只要两小时就看完了。了解一点原理后再用LINQ就方便多了。

LINQ和ASP.net

对ASP.net程序员而言,LINQ主要的应用是用它代替SQL访问数据库。除了不用拼字符串了,一个好处是,LINQ是强类型的,所以用起来更安全。我可以这样写:

var query = db.Orders.Where(o=>o.Description.Contains(txtDescription.Text));
// Contains会被翻译成SQL的LIKE
DateTime sinceDate = DateTime.Parse(txtSinceDate.Text);
query = query.Where(o=>o.ShipDate > date);
// o.ShipDate也是DateTime类型的,所以可以直接比较

Lambda表达式很好用的。
比较麻烦的是,现在有两套数据库访问框架:一个是LINQ to SQL,一个是LINQ to Entity。在用LINQ前,又要花点时间考虑用哪个框架好。

二者对于各种基本CRUD操作有很好的支持。LINQ to SQL可以理解为SQL的替代品,基本上跟SQL能一一对应起来;而LINQ to Entity是2008年8月才推出的,是一个更强大的ORM框架,对多对多关系的支持更好。

举个例子:假设一个表linkProductOrder里只有两列,productID和orderID,用于表达product和order的联系。在LINQ to SQL中这个表会被映射到一个类linkProductOrder,还需要自己写join;而LINQ to Entity中就不会出现这个类,直接用product.Orders和order.Products就可以实现互相访问,完全消除了写join的必要。

我的体会是:LINQ to Entity目前还不够稳定,还是LINQ to SQL保险。用ORM框架的一个问题是,刚开始用时会有lose control的感觉,出了问题完全不知道怎么回事,不像SQL,模型很简单清晰,一个query下去肯定不会出毛病。

我用LINQ to Entity时遇到了几个问题,比如数据库改变后没法重新刷新对象模型,只能删掉对象模型文件重建;还有一次不知道为何新建的对象存不进数据库,这时调试起来非常麻烦。对于复杂的含有多个join的查询,我一般都会先用sql server测试一下,再写成代码;LINQ to Entity不用写join是省了一点事,但是这样反而要在脑子里维护两种模型翻过来译过去。相比之下LINQ to SQL就没出过什么问题。

希望等LINQ to Entity的下个版本出来之后,这种问题会少一些。

总结:不是做广告,C#真的很好很强大。

20十一/083

用Java调用.net的Web service

Web service可以提高interoperability,可以实现跨平台的应用……听起来不错。但真的做一下,还是有很多小陷阱。

下面是最近做的小例子,用Java Axis2作为客户端调用.net写的web服务。支持自定义的数据结构(这个不是那么简单……)。

准备:

1、下载axis2 ,注意,不要google “axis”,那个是旧版的,一定要google“axis2”,目前的最新版本是1.41。

2、Visual studio 2008,C#。 这个不用说什么了。

 服务端:

在visual studio里写个hello world服务很简单,函数前加个[WebMethod]就可以。但是, 如果用到自定义的类,比如下面定义的person类作为GetUserInfoByPerson服务的参数:

namespace WebService1
{
    public class Person
    {
        public string IdentityNumber
        {
            get { return m_IdentityNumber; }
            set { m_IdentityNumber = value; }
        }
        private string m_IdentityNumber;
    }
}

这时要注意,直接按Ctrl+F5后生成的wsdl不包括Person的定义。

正确的做法是加一行
[SoapRpcMethod(Action = "http://tempurl.org/GetUserInfoByPerson", RequestNamespace = "http://tempurl.org", Use=SoapBindingUse.Literal)]

代码如下:

[WebService(Namespace = "http://tempurl.org/")]
public class Service1 : System.Web.Services.WebService
{
[WebMethod]
[SoapRpcMethod(Action = "http://tempurl.org/GetUserInfoByPerson", RequestNamespace = "http://tempurl.org", Use=SoapBindingUse.Literal)]
// 不加这一句,wsdl中就不生成Person类型
public Person GetUserInfoByPerson(Person q)
{
Person p = new Person();
p.IdentityNumber = q.IdentityNumber+"123";
}
}

现在Ctrl+F5运行这个服务,假设地址是http://localhost:56765/Service1.asmx,那就可以在http://localhost:56765/Service1.asmx?WSDL里看到,Person的定义已经出现了。

客户端:

axis2有一个不错的quickstart教程。axis2目录下面samples\faulthandling这个例子值得参考,里面有详细的readme.txt和build.xml。

1、用axis2自带的wsdl2java工具生成代码框架:

%AXIS2_HOME%\bin\wsdl2java.bat -uri http://localhost:56765/Service1.asmx?WSDL -u -o <target dir>

这样会在target dir下生成一个目录,里面有现成的Person.java等代码。

2、写调用代码

在Eclipse里建好项目,加入axis2\lib目录下所有的jar包,把刚才生成的目录也拷进去。

然后可以写代码了:

package example;
import org.tempurl.*;
public final class MyClient {
  public static void main(String[] args) {
  try {
    Service1Stub service1Stub = new Service1Stub("http://localhost:8080/Service1.asmx");
    service1Stub._getServiceClient().getOptions().setProperty(org.apache.axis2.transport.http.HTTPConstants.CHUNKED, Boolean.FALSE);
    Service1 service1 = service1Stub;
    GetUserInfoByPerson req = new GetUserInfoByPerson();
    Person personReq = new Person();
    personReq.setIdentityNumber("123");
    req.setQ(personReq);
    GetUserInfoByPersonResponse response = service1.GetUserInfoByPerson(req);
    PersonE personResponse = response.getGetUserInfoByPersonResult();
    System.out.println("ID = " + personResponse.getIdentityNumber());
  } catch (Exception e) {
  e.printStackTrace();
}
}
}

注意这一句!service1Stub._getServiceClient().getOptions().setProperty(org.apache.axis2.transport.http.HTTPConstants.CHUNKED, Boolean.FALSE);

不做这个设置,一开始我怎么都调用失败,Axis2报告说HTTP send recv出错(记不清楚错误信息了)。用tcpmon查看调用服务时的tcp传输,发现.net的web server根本不接受axis2的soap包。(btw,tcpmon是个不错的工具啊。)

google了很久发现原因是,axis2在做http传输时采用了“chunked”模式,而.net的web server不支持。

“axis中使用的是HTTP/1.0协议,而.NET和axis2使用的是HTTP/1.1协议,后两者的区别在于.NET未使用ns1的命名空间前缀打包SOAP请求,且axis2使用了Content-Encoding: chunked头。 所以必须在axis2中设置一下。”

总结:

web服务调用还是很麻烦的。除了上面列举的问题,还可能有soap协议版本1.1和1.2不兼容之类的细节问题。在调试这类问题时,tcpmon是必备工具。

刚才讨论了如何用java调.net服务。如果反过来,不知是否会简单一点。

最后,其实我一年前就写过axis2 1.3的代码……可是昨天,除了知道这件事可以用axis2做,所有的细节都忘光了。还是花半个小时记下来比较安全。

20十/083

T43风扇清洗记

T43风扇的噪声实在太大了,说它是拖拉机毫不为过。买新风扇太贵,买到旧风扇就更郁闷。自己动手吧!万一坏了呢……就去村里买个新的台式机~~

抱着必死的决心,开始实施计划。按照网上的攻略,要用分析纯的无水酒精清洗风扇。哪里有卖的?攻略没说。从北大东门出来,过了马路向南走到中关村十字路口附近,东边有个凹进去的小路,路北侧有个不起眼的小店卖实验试剂,那里就有卖无水酒精的,一瓶10块钱,500ml。

回家之后准备好螺丝刀,开拆。我先找了张白纸,把背面所有的螺丝钉位置画在纸上,把拆下来的每个螺丝钉直接插在纸上的相应位置,这样就不会记错位置了。这个idea还不错吧:)

拆完背面的螺丝就可以取下掌托和键盘,然后再按攻略拆掉键盘下面、靠近左下角的一颗螺丝,就可以把边框打开。最后把风扇上的螺丝拆掉,很容易就把风扇取下来了。

风扇上的灰很多。我拿了个小盆儿,倒了300ml酒精进去,然后把风扇扔进去泡了泡,摇了摇,就当洗萝卜那么洗。5分钟后觉得差不多了,电吹风吹干,把所有螺丝装回去,搞定~

虽然有的攻略说要用超声波清洗机,有的说要加黄油,甚至还有的说要把风扇拆开了洗……但实践证明简单泡一泡的效果也很好。只花10块钱,风扇安静了,又能听到硬盘的声音了~:)

13十二/070

貌似Netbeans 6比Eclipse强大很多啊

今天看了一个介绍Netbeans的视频:http://www.javalobby.org/eps/netbeans55/
非常吃惊,原来Netbeans已经这么牛了,可以说绝不比Visual Studio差。

首先因为它是Sun的IDE,所以支持几乎所有的最新Java技术,比如JAX-WS,Mobility,BPEL,Java
Persistence(类似),JSF,还有一个很漂亮的visual web design界面;而且用起来非常容易(这主要归功于Java EE
5),极大的减少了代码量,把J2EE的门槛大大降低了。

其次是各个高级功能之间的互联。Eclipse的缺点是作些基本开发还好,但要开发复杂程序的时候要配一堆东西,像MyEclipse之类的第三方插件质
量虽然好,但毕竟不是“原厂出品”,插件一多就有点头痛,不仅风格不同,而且互相不能通讯。相比之下,Netbeans和Visual
Studio的好处是设计之初就考虑到了种种高级功能之间的互相关联,所以可以做到“从数据库管理窗口里拖一个table到Web设计页面上”这样的效
果。

Websphere的确提供了很多功能,但Websphere的使用离不开IBM自己的一堆服务端程序,感觉用Websphere做开发的人好像从来不会配合MySQL一起的。JBuilder现在也逐渐没落了,所以还是装个开源的Netbeans吧。

   下一页