32位和64位的麻烦
如果没有8G内存,还是不要装64位的Windows。今天又被折腾了一下。
装了个淘宝旺旺,店主发了个链接,我打开之后拍下宝贝去付款,IE反复提示安装控件,但怎么都装不上。而且还看到提示某SmartCard驱动无法安装。重启了一下还是不行,连网上银行都出毛病了,以前明明是好的啊。
原来淘宝旺旺里面的链接会默认打开64位的IE。难怪安全控件会不兼容。还好是用Windows7,有图标分组的功能。如果不是看到两个IE图标,我估计还得折腾半天。看了一下,旺旺倒是32位的。不知道做旺旺的人是咋整成这样的。
最后说一个常见的误解。如果有4G内存,虽然32位的系统会导致部分内存无法访问,也许只能用到3.0-3.5G的内存,但其实你换成64位的系统之后未必就提高了内存的使用效率。以前一个指针占32bit,换成64位后每个指针都大了一倍,所以每个程序都会占更多的内存,最终又还回去了。再考虑到种种潜在的兼容性问题,还是用32位安心啊。
时光
今天读到一篇好文章,互联网迟到的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了一下……果然已经有“真空抽气保鲜盒”这样的东西了……
由无数细节组成的系统
计算机科学总是讲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很容易。不过事情一旦很容易,于工业上看,就不值钱了;于学术上看,就没啥好研究的了。所以,重视各种细节吧。
C#和CLR的15个细节
这两周一直在training,一共听了4个课程,相当的累啊。
今天先把Jeffrey Richter的培训课:C# and CLR中讲到的一些东西整理一下:
- foreach的性能问题
foreach(string s in rows) { foo(s); }的实现是:
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。值得注意。
- 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的函数都会被编译器做成一个状态机。每调一次,就接着上次的状态继续运行。简单有效啊。我一直以为要有什么特殊的办法呢。
- exception handling的实现决定了throw的performance较差。
可以用Int32.TryParse代替try{Int32.Parse…}catch{…},稍快一点。类似地建议使用Dictionary.TryGetValue。
- .Net CLR执行引擎对应于MSCorWks.dll和MSCorEE.dll这两个文件。
- .Net 3.0, 3.5没有对CLR作任何修改。
所有增加的东西(比如LINQ)都是syntactic sugar,只改了C#编译器而已。
- 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里,以实现动态装卸。
- 动态载入Assembly的陷阱
Sytem.Reflection.Assembly.LoadFrom(pathName)并不会载入pathName所指定的dll,而是看看pathName那个dll的名字、版本,然后到系统默认位置去找。(陷阱啊)
- C#里用reflection创建一个新对象 用Activator.CreateInstance。(奇怪的名字啊。)
- 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比较?)
- 匿名函数的背后。。。
在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函数就是匿名函数,改了改语法而已。
- 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的简写。
- 属性(property)的简单声明
public int x {get; private set;}是个很好用的句式。
注意,public int x {get;}是错误的,不能通过编译。
- 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就用到了这个技术。
- 匿名类型的背后。。。
var o = new {name = “Xiangpeng”, id = 123 };
在这背后是编译器生成的一个匿名类,包含了两个只读属性,形如public int id { get {return _id;} }为什么不做成可读写的呢?
很微妙。匿名类自动生成了GetHashCode(),返回的是对所有属性的hash code做XOR的结果。如果允许修改属性值,那么Hash code的值就会变化;而这个可能会出问题~保险起见,只读吧。
- 每个thread占1M物理内存
在Win32编程中thread的1M stack空间是Reserve的,直到真正用时才占用物理内存;而在.net中,这1M空间直接被commit。
还好,可以在新建thread时指定stack size。不过这也比较危险,设小了怕不够。实际上,最好尽量避免创建thread——太多的thread要么导致CPU竞争和context switch,要么都block着浪费内存。建议是:能用ThreadPool就用ThreadPool。
累死了,写了这么多……Jeffrey还有一个更牛的课:.net threading。里面的东西更多……要不要再总结一下呢。。。![]()
一台牛机器
xb一下最近在用的服务器,昵称“栅栏机”,如图。
32G内存挺好的。我的开发用机有5G内存,感觉刚刚够。
操作系统必须考虑大多数用户的配置,以便采用最佳的管理策略,所以Windows的内核要不断升级。可以出一道考试题:如果有32G内存,那应该对Windows这样的Desktop OS做哪些修改,才能方便用户?
还要多少年,一般的PC才能装上32G内存呢~
SQL Server 2008 Reporting Service使用技巧
SSRS用来做报表很方便,据说比Crystal Reports好用。简单的说,你的报表数据来自SQL语句,然后就可以在类似Excel的环境中画图了。
说是这么说,实际做起来还是会遇到很多小麻烦。下面是总结的一些技巧:
-
改变图表上字体大小(如X轴、Y轴上的数字):
先把LabelsAutoFitDisabled属性设为True ,然后再设置字体大小
-
Data Label显示设置:
在Series的SmartLabels里面有一些有用的选项。AllowOutsidePlotArea设为True比较好。
-
自定义Legend标题:
要右键选Series的属性,不是Legend的属性
-
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"))
-
Pie chart排序
右键点一个Category,在Category Group Properties中设置Sort表达式即可
-
在Pie chart饼图外侧显示DataLabel:
设置Series的CustomAttributes,PieLabelStyle=Outside
注意:参考AdventureWorks示例,有一个很好的饼图。(google上搜到的都是过时的东西)
-
Asp.net中使用ReportViewer控件:
必须在IIS的Handler Mappings中新建一个Managed Handler才能正常显示。 可以google搜索Reserved.ReportViewerWebControl.axd。
另一个解决方法:据说配置下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>
-
ReportViewer控件:
可以设置ZoomMode属性为PageWidth,这样可以自动放缩。
图表放大时会失真,因为不是矢量图。建议把原始报表拉大一些,在页面中留一个小点的空,默认ZoomMode=PageWidth,用户选择100%就可以放大。
所有的toolbar按钮都可以选择显示或关闭,设置ShowXXX属性即可。
-
其他:
对报表修改之后要及时Deploy,才能看到效果。可以在VS的configuration manager里面设置,debug时自动deploy,比较方便。这个默认是没有打开的,如下图所示,建议把Deploy的对勾打上。
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#真的很好很强大。
用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做,所有的细节都忘光了。还是花半个小时记下来比较安全。
T43风扇清洗记
T43风扇的噪声实在太大了,说它是拖拉机毫不为过。买新风扇太贵,买到旧风扇就更郁闷。自己动手吧!万一坏了呢……就去村里买个新的台式机~~
抱着必死的决心,开始实施计划。按照网上的攻略,要用分析纯的无水酒精清洗风扇。哪里有卖的?攻略没说。从北大东门出来,过了马路向南走到中关村十字路口附近,东边有个凹进去的小路,路北侧有个不起眼的小店卖实验试剂,那里就有卖无水酒精的,一瓶10块钱,500ml。
回家之后准备好螺丝刀,开拆。我先找了张白纸,把背面所有的螺丝钉位置画在纸上,把拆下来的每个螺丝钉直接插在纸上的相应位置,这样就不会记错位置了。这个idea还不错吧:)
拆完背面的螺丝就可以取下掌托和键盘,然后再按攻略拆掉键盘下面、靠近左下角的一颗螺丝,就可以把边框打开。最后把风扇上的螺丝拆掉,很容易就把风扇取下来了。
风扇上的灰很多。我拿了个小盆儿,倒了300ml酒精进去,然后把风扇扔进去泡了泡,摇了摇,就当洗萝卜那么洗。5分钟后觉得差不多了,电吹风吹干,把所有螺丝装回去,搞定~
虽然有的攻略说要用超声波清洗机,有的说要加黄油,甚至还有的说要把风扇拆开了洗……但实践证明简单泡一泡的效果也很好。只花10块钱,风扇安静了,又能听到硬盘的声音了~:)
貌似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吧。