月度归档:2010年01月

LineNumberReader 和 FileWriter 同时使用碰到的问题

今天上班有个任务就是将之前产品中的示范代码有一些不规范的地方进行修改,使得能在多平台顺利运行,主要是文件名大小写的问题。由于之前示范代码编写者在跨平台上编码经验相对不足,所以编写者在写代码的时候并没有严格的大小写意识 (主要原因还是因为 Windows 不区分大小写,而代码的测试均是在 Windows 下进行的),造成很多遗留问题。产品中有示范代码 64 个,大小写问题几乎无处不在,还有一个就是跨平台产品支持的数据引擎与 Windows 不一致,之前的 Windows 版本中有不少的示范代码使用了 Windows 平台特定的引擎类型,也需要进行修改。

修改的方法可以有很多,最为简单和直接的方法,应该是一个个程序运行,一个个文件检查,逐个进行修改。我想作为程序员的我们,肯定是不愿意做这么无聊而重复的工作的。那么搞定这个无聊而重复的工作呢?首先分析一下已有的示范代码的特征,虽然之前的代码存在诸多不规范的地方,但是在一些方面还是存在很多共性的,大小写出现错误的肯定是在设置数据路径的时候出现的,设置引擎类型的代码更是固定的,之需要使用几个非常简单的正则表达式就可以将其完全正确替换了。所以我们选择了读写文件的方法来完成这个任务。

在实现这个功能的时候,使用到了 LineNumberReader 和 FileWriter 两个类,每读取一行,使用 replaceAll(String regx,String str) 方法对该行进行替换,之后再使用 FileWriter 将替换后的文本写入文件:

执行的结果是,64 个示范代码中,总会有几个会出现一些很奇怪的问题,那就是修改后的文件丢失了很多文本,导致编译不通过。那么问题在哪儿呢?经过多次分析和调试,最终发现当 LineNumberReader 和 FileWriter 指向同一个文件的时候会出现该问题,如果将源文件处理后的结果写入到一个新的文件中便不会存在这样的问题。那么究竟是哪里出了问题呢?来看一张图

在这张图中我们能看到一些信息,LineNumberReader 对象中有一个 cb 字符数组,大小为 8192=1024*8.

在我整个的调试过程中,我一直关注着最后一个字符,在整个过程中,这个位置的字符一直没有发生改变,而最终写入到文件中的最后一个字符就是该字符,这难道只是一个巧合吗?

现在让我们更换一下代码将原本使用同一个 File 对象的代码,改成使用另一个新文件对象的代码来重新调试一下。

先执行代码,查看结果发现,文本的内容完全被写入到新文件中,并没有出现丢失文本这样的情况,那么我们再来调试一下 ,我们再次来监视 cb[8191] 处的值,这次我们可以发现在 LineNumberReader 对象 lineNumber==319 的时候,该值发生改变。

====我是分割线======

综合上面的两种情况,我们可以得出一个结论,当 LineNumberReader 和 FileWriter 同时指向一个文件的时候,系统在写入文件的时候并不是逐行逐行写入,而是先记录下来逐行的内容,并且是记录到 LineNumberReader 的 cb 字符数组中。该数组的大小固定为内存最小单位 8K,也就是大小为 8192 的数组,该数组将保存初次初始化 FileWriter 对象时读取的文件内容,在之后的每次 FileWriter 写入操作均是改变该数组中存储的字符值。这样导致的结果就是,如果当前的读取的文件较大,就会存在丢失内容的问题 (最多保存 8192 个字符, 含空格和换行)。而使用一个新的文件来进行修改后内容的写入就不存在这样的问题,当 FileWriter 需要使用到 flush() 方法来将当前缓冲区中的所有内容写入文件之后,LineNumberReader 中的 cb 数组内容也将发生改变,指针下移,读取下一个块大小为 8192 的内容,直到文件末尾。

所以在使用 LineNumberReader 和 FileWriter 的时候需要注意这一点,如果实在不愿意多生成一堆新文件的话,可以在写入完成之后,将原始文件删除,而将新文件更名一下就好了。

SuperMap Objects 碎碎念

SuperMap Objects 是 SuperMap Software Co., Ltd. 成长的关键,最早公司创建之初公司便同时启动了两个项目,一个便是传统 GIS 中最为突出的桌面产品,另一个就是组件产品。

组件产品又作为桌面产品的基础,为桌面应用提供相应的功能。从 SuperMap Deskpro 2000 到现在的 SuperMap Deskpro 6R,一直都是这么一个模式,SFC 组件为 SuperMap Deskpro 系列产品保驾护航多年,不过近几年 SFC 组件逐渐进入后期维护阶段,SuperMap Deskpro 也进入维护阶段,主要对功能的集成和细化进行深入挖掘,更多的是从软件可用性提升以及功能正确性,分析高效性上来提升 Deskpro 产品的市场认知度和用户体验。基于 SFC(SuperMap Foundation Class) 组件有一系列的产品,有为我们所熟知的 SuperMap Deskpro、SuperMap IS .NET 和 eSuperMap,从桌面到服务器,再到嵌入式,甚至还有国土行业的一些深度行业应用等等。SFC 已经为 SuperMap 带来了十年的收益,更是为 SuperMap 品牌提供了前期非常坚实的基础沉淀。那么为什么不继续深入 SFC 组件产品的研发,而是转而进入了一个后期维护阶段,目前的工作主要集中在后期缺陷的维护呢?主要原因有:

  1. SFC 组件式基于 COM 技术的,称为 SuperMap Objects COM,现在依然有很多的产品和项目的基础。但是 COM 技术毕竟不再是如今软件开发中的主流,存在着诸多的不足,作为平台厂商,固守一方市场固然有利可图,可是此计并非长久之计啊。为用户以及二次开发商创造价值才是平台厂商应该做的,如今的软件开发中 (特别是项目实施中)Java/.NET 无疑已经是十足的宠儿了,那么尝试转型已成必然之势。
  2. SuperMap Objects Java/.NET 是基于 UGC(Universual GIS Core) 共相式 GIS 内核封装的组件产品,内核采用标准 C++(此为号称,到底标准程度多高,各位看官且看且猜就行了),与平台相关性降至最低,依托 Java/.NET 快速而高效的二次开发能力,已经逐渐成为了用户和二次开发商首选的项目实施和开发组件。
  3. 服务器产品需求的激增,由于服务式软件 SOA/SaaS 等概念的逐渐成熟,催生了服务式 GIS,GIS 领域顿时服务器产品的需求指数上扬。国家基础地理数据中心,各大城市共享数据平台的建设,国家技术设施的信息化项目,都能看到服务器端产品的身影。服务器端产品对效率和跨平台的要求更高,而 COM 组件明显在跨平台和效率上不及 UGC 系列产品,由此 SuperMap Objects Java/.NET 走上了大舞台。
  4. 云计算概念的成长和应用,诸多厂商开始宣称自己站在了云端,依托云计算的优势能为他们的客户带来更多的价值,节约更多的成本。云计算中最为美妙的就是,任何硬件、软件、服务资源均不以实际形态暴露,而以统一的接口提供给用户,所有的资源均以组件式形态存在于服务端,客户端只需处理请求与响应。那么组件式 GIS 无疑是 GIS 云计算中非常重要的一个环节,插件式可定制化的服务,跨平台多地域服务的共享,无疑将为更多的用户带来更多的价值,而成本会更低。

SuperMap Objects Java/.NET

上图是 SuperMap Objects Java/.NET 产品的实现原理,至于细节,相信众位看官稍微了解一些关于封装机制的都一目了然了。功能性的代码完全由 UGC 完成,而接口的设计与实现完全由组件层来完成,组件层可以再更具自身语言平台的需求增加一些控件和相关产品的设计开发。例如.NET 组件在 C/S 架构中应用广泛,用于对于桌面应用场景的需求较多,那么易用性高、定制性强、可设计的控件无疑会为客户带来更多的价值空间;而 Java 组件的优势应用在服务端,B/S 架构的产品也不少,在服务端的表现可以交由 iServer 系列产品 (基于 SuperMap Objects Java 开发的服务器产品),那么客户端呢?新近稍热一点的 JavaFX 是 Sun 公司首推的 RIA 解决方案,也许用户的应用场景也会随着产品的升级而出现这一方面的需求。

其实组件产品分两大语言体系是一个比较科学的分类

  • .NET 技术依托微软强大的技术后盾,在桌面应用上日益成熟早已是桌面明星了,SuperMap Deskpro .NET 就是基于 SuperMap Objects .NET 使用 Ribbon 界面库开发的新一代 GIS 桌面 GIS 产品,从产品易用性和体验上作出了很大的提高,不过性能目前还存在一定的问题 (组件产品的瓶颈问题导致)。
  • Java 跨平台的特性无疑是最让人津津乐道的,类似于政府和大型服务企业均采用的 Unix 和类 Unix(各种 Linux 发行版) 系统和硬件,对跨平台的要求显而易见,SuperMap Objects Java 基于标准 C++,完全可以摆脱平台限制,在多平台上表现得游刃有余,填补市场上很大一块空白 (其他厂商也有类似的产品和解决方案,但是均是采用中间件方式完成,效率和易用性上相对较差,对用户和二次开发商的开发成本要求过高)。
  • 两种语言技术体系产品,保持同一份设计同时开发测试,接口文档高度一致,二次开发用户选择开发平台的决策成本大大降低。无疑将会让更多的二次开发商,在低成本的情况下可以尝试更多项目的投标生产,为其带来更多的价值。
  • 两个产品由一个领导带领设计开发,更能突出其特点的一致性和延续性,降低用户和市场对该产品和品牌的认知成本,大大增强了组件产品的产品化和标准化。

================我是分割线==================

来到公司已经快半年了,对项目产品和架构上曾经都颇有微词,迄今依然有些怨言,以上就是我对目前 SuperMap Objects Java/.NET 系列产品的一些碎碎念,其中均是纯个人想法和行为,有一些猜想更多的是臆断。不过猜想也好臆断也罢,终究是我自己思考的一些沉淀,摆出来,凉一凉,晒一晒,淋雨发霉也好招来板砖也罢,纯属博主个人呓语,众看官且听且过是也。

开通 SuperMap Objects 专栏

首先声明,本人确实是 SuperMap Software Co., Ltd. 的一名员工,目前就职于研发中心,从事软件开发的工作。

对于 SuperMap Objects 有着自己的理解和认识,有自己想说的一些话,不过说得对不对就不得而知了。对于职场上的一些常识和非常识,我一概不知,我会说我想说的,说我能说的,当然我不可能透露关于工作进度的只言片语,也不可能透露关于公司产品方向的任何信息。但是,我想我会猜的,反正我也听不到什么有价值的信息,那么为何不来一些自己的猜想呢?Yes, why not?

============我是分割线=============

该专栏主要针对 SuperMap Objects 的新特性和可公布的新消息作出广播,并偶尔来些剧透和爆料,也许会有不错的效果哦。我想作为一个 GIS 的从业人员,作为一个组件开发人员,报导关于自己产品的一种心态平衡非常重要。

首先组件是公司起家的法宝,其次是公司上层服务器产品的基石,是底层类库产品的延续,可所谓处于公司产品结构的腰部。我们都知道腰对于一个人的重要性,所谓无腰者不人也,就是这么一个意思啦。然而我们平日看美女的时候,先是头后是臀,腰经常是被略过的或者只是为了衬托臀的丰满而窈窕地存在的。那么作为一个腰,我们要如何做好这个产品呢?在做产品的过程中,如何勾起众位看官的欲望呢?也许腰风就在一夜起,顿时洛阳腰贵,作为腰子的我可能也会着实火一把哦。

所以该栏目的主要内容有以下:

  • SuperMap Objects 产品的最新小道消息 (绝非官方消息)
  • SuperMap Objects 产品的开发技巧
  • SuperMap Objects 产品的八八和卦卦

众位看官,看过且看过,切勿传播,一旦小站被领导发现,可能便有关闭之忧。还望各位慎重慎重谨慎谨慎。

“搜狗”——To Be 输入法巨人

记得我第一次上网是 2004 年 6 月份中旬,那时刚刚结束高考,跟同学一起去网吧上网,第一次学会使用 CTRL+SHIFT 切换输入法,开始知道怎么打字,当时大部分人都在使用智能 ABC 打字。这个输入法一直用到大二,大三的时候开始有同学使用搜狗拼音输入法,而且迅速在班级中流传开来,当时那个爽啊!直到现在,自己的电脑上也一直用的是搜狗输入法,期间试过谷歌拼音输入法和 QQ 拼音输入法,觉得都不错,但是毕竟使用搜狗习惯了,最终还是选择了搜狗。

我不喜欢有广告的软件 (无聊的广告),更不喜欢会弹窗体的软件,那么搜狗可以算作这一类的,之前我还会尝试使用自己曾经的搜狐账号登录一下,后来发现总是有一些我并不需要的信息,遂再也不登录了。现在经常会弹出一个窗体通知我更新了一些什么网络热词,同时还会有搜狐高清视频的一些广告,当然这些广告我一直都很不喜欢,甚至讨厌。相对这个广告来说,谷歌拼音和 QQ 拼音纯净的界面无广告让我觉得很清爽,但是为什么还选择搜狗呢?

  1. 习惯使然,我已经使用该输入法两年多了,早已经熟悉了它的词序排列,而且它的智能组词能力确实很突出,至少在我自己的体验山上来看,比谷歌拼音和 QQ 拼音好了不少,虽然这三家企业都有自己的搜索引擎,其中的谷歌更是搜索巨头,在网络热词和智能组词上都不逊色,但是问题就是这些产品都是在想搜狗学习,甚至可以说是抄袭,因为你不能跟搜狗区别太大,如果太大的话,对于其他想更换输入法的用户来说成本会太高,导致用户再度流失。既然跟在人家的屁股后头走,那么自然有很多东西要慢上一个节拍,那么用户首选的肯定不会是后者了。
  2. 轻度的广告,虽然我对软件有一定的洁癖,但是还不至于到一尘不染的境界。搜狗的广告不像某些软件,只要你登录立刻给你弹出一个窗体,一大堆的标签页,各种新闻信息扑面而来了,很可能就会打断你原本启动该软件的初衷,转而去浏览其中的一些能吸引你的信息了 (如网络美女和花边新闻等等)。搜狗的广告页面保持了一个低调的态度,首先它将广告区域选择了与系统托盘靠近的右下角,这个区域并不是用户的热点区域,并不会立刻将你的视线吸引过去,其次广告页面很小,而且不需要主动关闭,在几秒钟之后自行卷帘消失,在工作中的你完全可以不予理会。第三,搜狗的广告可以隐藏在当前输入面板,例如当你输入鼠标时,在你的输入面板上方 (拼音输入处) 显示一个第六选项,即“到淘宝找‘鼠标’:http://search8.taobao.com/browse/search_easy.htm?keyword=%CA%F3%B1%EA&catid=11&refpid=mm_14616170_2082030_8507977&isinner=06”,只要你输入 6,系统将会启动你的默认浏览器,并定位到淘宝的搜索页面,关键字为鼠标。
  3. 联想功能,同刚才的淘宝广告有点类似的是,只要你输入高考两字,同样在输入板上方有一个第六选项,网址为:http://www.sogou.com/gaokao,虽然可能我们大多时候均是通过正规的搜索引擎如 Google 和百度来进行搜索的,但是这种方式无疑更为一体化,至少它为我们带来了一定的便利,当然这不是我选择搜狗的主要原因。
  4. 跨平台。这个特性对我来说,让我有点欣喜如狂,因为在 Ubuntu 下,我对智能拼音的满足度并不是很高,因为我已经非常习惯于搜狗的智能词组功能了,智能拼音对我来说有点点残废,让我打字总是一顿顿的,感觉上厕所总被人打断的不快。当我发现搜狗云输入法的时候,我很开心,以后写博客 (也就写博客的时候,严重依赖中文输入法) 就可以键随心走了,达到人键合一了。之前的云输入法还不是非常的成熟,需要使用收藏标签来调用一段 JavaScript 代码,现在搜狗更是推出了 Firefox 下的插件 了,只要是在 Firefox 下,Ctrl+Space 便可以启动搜狗云输入法了,真正的跨平台,当然只要你喜欢跨浏览器也不成问题的,虽然别的浏览器暂时还没有插件 (相信 Chrome 下不久便会有了吧),但是只需要收藏一个书签,之后点击收藏便可以在当前页面启动输入法了。

我曾经一直以为输入法是一个系统必备的软件,曾经以为搜狗输入法只是一些程序员娱乐的产物,但是,逐渐发现,这是一个互联网利器,我们总是离不开浏览器,离不开手机,离不开电脑,我们总是想发出声音,所以我们总是需要一个能让我们更快更好发出我们声音的工具,那么搜狗无疑就是一个非常出色的一位。

另外,最近公司有同事闲来无事,做了一个搜狗的皮肤,觉得还不错。其实企业皮肤定制确实是一个非常不错的 idea,个性化的皮肤总是一些发烧友针对自己发烧的领域发挥,有很多动漫和卡通人物的皮肤,也有一些学校和机构的皮肤,企业的皮肤倒确实是第一次听说和见识。其实如今企业在日常的办公中,逐渐开始强调 CID 的概念,从办公用品的定制化和鼠标键盘上的各种公司 logo,甚至团队的 logo,我们看到了一个巨大的企业个性化的市场,输入法不失为一个好的切入点。logo 绝对不是唯一的一个切入点,输入法最让我们着迷的是它的智能词组功能和强大的词库,你有没有因为使用输入法输入公司同事名字苦恼过,absolutely,有过。那么公司员工姓名词库是不是一个非常好的 idea 呢?通过公司员工信息表自动生成更新词库难道不是很好吗?

也许,搜狗有一天真的会成长为一名巨人哦。

关于 JTree 的一些碎碎念

本博号称关注 Web2.0, Ruby/Rails,Java。但是据本人所知,到目前为止还没有任何一篇关于 Java 方面的文章和只言片语。作为本博的博主,确实有点大言不惭的感觉,一想到这个心中就颇不宁静啊。

其实作为一名博客,写东西的欲望一定要强烈,很多的时候我们并没有很多的素材可以写,因为生活几乎每天都是 Just so so,我们谁都不想书写平淡,总想语不惊人死不休。作为博客的人们可能这种感觉更为强烈,我是一名独立博客耶,我不能人云亦云啊,我得从我的文章里抒发我的思想啊。艹,你娘的思想也就大街上小摊上的胡萝卜包子一般廉价,还装。所以呢,我们还是需要不时的刺激一下自己的神经,同时呢,也给我们伟大的祖国发育不良的国联网添加一些有趣或无趣的素材资料吧,也算是为我国早日实现四个现代化添砖加瓦了。

===========我是分割线=========

树中特定的节点可以由 TreePath(封装节点及其所有祖先的对象)标识,或由其显示行(其中显示区域中的每一行都显示一个节点)标识。展开 节点是一个非叶节点(由返回 false 的 TreeModel.isLeaf(node) 标识),当 展开 其所有祖先时,该节点将显示其子节点。 折叠 节点是隐藏它们的节点。 隐藏 节点是位于折叠祖先下面的节点。所有 可查看 节点的父节点都是可以展开的,但是可以显示它们,也可以不显示它们。 显示 节点是可查看的并且位于可以看到它的显示区域。

这是 JDK 5.0 非官方中文文档中的描述,我想这个描述给我们的第一个直观印象就是这个控件应该和 Windows 资源管理器中的树状结构的表现是一致的。确实如此,JTree 确实是用来作为树形展示使用的,因为我们在很多的数据管理和业务处理上,秉承了我们自古就袭承的分类方式,所以我们有很多的业务可能用到或者说可以用到 JTree。

那么 JTree 怎么用的呢?这个问题我曾经问过自己很多次,还做过很多相关的工作来学习 JTree 的使用,记得来到北京实习做的第一个学习阶段作业就是使用公司已有的组件产品做一个叠加分析的 Demo。作为一个比较友好的叠加分析工具,就肯定需要一些 GIS 数据处理的功能,比如打开工作空间,数据源,数据集,地图,同时还应该能简单地做一些图层删除或者地图关闭之类的基础功能。这样一来,主体的工作就是基础功能模块的实现了,只是在基础功能模块的基础上,添加一个叠加分析功能模块,而在基础功能模块中的可视化显示 JTree 必然是首选控件。

在实现树状加载和显示工作空间,参考公司已有的桌面产品 UI 设计,需要再 JTree 的每个节点之前添加一些图标,用于标识当前节点的数据类型,例如线数据集和面数据集的图标是有明显差异的,这样对用户的友好度会提高很多。默认的 JTree 使用 Java 默认的 meta 风格显示文件夹 (根节点) 和文件 (叶节点) 的图标样式,那么如何添加自定义的图标显示呢?当时我就在 JTree 的文档中苦苦找寻,希望自己能找到一个 setIcon() 的方法,最终无耻的失败鸟。后来才知道之需要自己实现一个类,实现 TreeCellRenderer 就可以了,重写下面这个方法 (Sun 就是这么排版的,我觉得这样挺好的,不要怪我占太多行了)

Component getTreeCellRendererComponent(JTree tree,

在这个方法中,它的返回值为一个 Component,那么我们完全可以自由发挥了,比如返回一个 JLabel。当时欣喜若狂,赶紧将代码巴拉巴拉地敲好,一看效果显著啊,根据类型不同生成不同的 Icon 设置给将要返回的 JLabel 对象就 OK 了。

实习的工作相对简单,当时对 JTree 的理解也就点到为止了。那么此次项目组需要退出 Objects Java 的控件,其中就有一些控件需要使用到 JTree 来实现,功能要求相对就复杂了不少。

需要在一个节点文本前,显示多个图标,除了用于区别节点数据类型的图标外,还需要添加几个操作图标,用于可视化节点数据的操作,那么返回一个什么呢?JPanel 无疑是最好的选择,轻量化容器中的万金油。在 JPanel 中添加几个可视化的 Icon 并不是难事,只是 JLabel 多少的问题了,通过对节点数据类型的判断分别设置便好了。问题是,如何判断用户当前点击的位置落在 JPanel 中的那个操作图标上呢?又如何将这些操作直接反映到实际的数据上来呢?通过 JTree.getUI() 方法获取当前的 JTree 的 UI,然后调用 TreeUI.getPathBounds(JTree tree, TreePath path) 方法获取当前节点的绘制区域,之后通过鼠标事件 MouseEvent 的 getX() 和 getY() 方法获取当前鼠标的位置信息,由于控件的编写图标的大小由自己定义,所以可以确定图标的大小和 JPanel 的布局 (我使用的是 FlowLayout,并且使用 setHgap() 方法将水平控件间距设置为 0),通过像素坐标的计算便可以得出当前用户鼠标单击事件应该响应哪个对应索引处的动作。

在实现的过程中,还需要使用到编辑节点的功能,那么如何来做呢?首选实现 TreeCellEditor 接口,完全自己定制,可以避免像继承自 DefaultTreeCellEditor 的烦恼 (因为它能满足一般需求,但是会限制你拳脚)。TreeCellEditor 也有一个很嚣张的方法,号称自己要接管 TreeCellRenderer 的方法,就是这个啦

Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row)

重写这个方法,用来自定义编辑时的节点渲染,例如我的树形控件在编辑的时候只能编辑节点名称,那么前面的图标还是要保持与未编辑时一致的,编辑时只需要将后面的文本置换为一个 JTextField 就可以了。然后为该 JTextField 注册一个键盘事件监听器,当输入 Enter 的时候,调用 TreeCellEditor 的 stopEditing() 方法,触发停止编辑事件。如果我们需要将编辑保存至数据模型中的话,就在 stopEditing() 方法中编写业务代码即可。

综上 JTree 这个控件实际上是一个完全遵循 MVC 架构的设计,甚至比 MVC 还抽象一些。JTree 中只处理数据和显示的一些控制,例如判断当前节点是否能够被编辑的方法 isPathEditable(TreePath path) 等。数据模型完全可以委托为 TreeModel 来管理,之需要使用 setModel 便可以轻松地将 JTree 和 TreeModel 关联起来。而在其显示渲染上便是完全由 TreeCellRenderer 来接管的,只需实现一个类,该类实现 TreeCellRenderer 接口,重写 getTreeCellRendererComponent(…) 方法,便可实现完全自定义面板显示节点。抽象最高层的是,当节点为可编辑状态时,其渲染又交由 TreeCellEditor 接管了,在编辑节点时,TreeCellEditor 的 getTreeCellRendererComponent(…) 方法便将完全接管节点的渲染工作,并且控制编辑时的动作,例如取消编辑和停止编辑之类的种种。这么看来,其实 JTree 只是提供一个壳子,通过其内部的 TreeMod 存储数据,外部的 TreeCellRenderer 和 TreeCellEditor 来控制其显示渲染和编辑渲染,给开发人员提供了一个可高度定制化的控件接口。

==========我还是分割线=========

本文的形成是如行云流水账一般啊,我也有点不知所云了,众位看官,能看则看,不能看则拉倒吧。JDK API 文档在你家里叫你回家翻翻呢。