首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 .NET Java 游戏 视频 人才 外包 数据库 第二书店 程序员

luohg666/ 


共28个网摘 [ 1 ]   |  访问luohg666的个人空间

选择Java接口还是抽象类

luohg666收录,使用标签:java,时间:2008-8-8 10:09:33 | 相关网摘我也收藏

选择Java接口还是抽象类


Java1.5泛型指南中文版

luohg666收录,使用标签:java,时间:2008-7-18 22:29:00 | 相关网摘我也收藏

Java1.5泛型指南中文版(Java1.5 Generic Tutorial) - explorers的专栏 - CSDNBlog


又到一年面试高峰期--面试总结

luohg666收录,使用标签:Java,时间:2008-6-20 13:07:06 | 相关网摘我也收藏

接到ZTE的面试通知是我始料未及的,之前也说过,题做得贼烂。没想到通知我去了。我的是5:00开始,杀到那4:40,没想到连4:00的都没面完,人叫做一个多字。等到5:40总算该我了。进去后两个面试官就开始翻简历,都问简历上的内容,没啥新鲜感,问到我在项目中做的一个改进,我开始吧唧吧唧的说,发现他们很认真在听,不过似乎没听懂,估计他们都是搞底层的吧。最后问有没有其他公司的Offer,说拿到HP的。我问了个问题,说公司准备招多少人,是不是以硬件居多。他们回答只要有能力都要,硬件的缺口最大。出来想肯定能进下一轮了,没想到最后莫名其妙的挂了,虽说没想通,但还是有点门道:一般大公司知道你有offer只会去把你竞争过来,但像ZTE这样的公司,大概自叹给的工资不如HP吧,所以直接不给下次面试的机会;另一个原因也是我做上层居多,估计不对口吧,总之,大家都互相BS。说起ZTE,就会想到和华为的争斗,非常搞笑。当初华为在科大宣讲的时间是11月5号左右,中兴决定提前10天来,口号是:宁可错招三千,绝不放过一个。华为来了之后发现很多人都已经有中兴的offer了,于是想出一招更毒的:只要拿到中兴offer的都可以免笔试和第一轮面试,并且工资加1K。晕倒。中兴一看又想出一招:当初签的三方只有在明年4月才能退回,因为那时许多公司都招聘结束了。两家明争暗斗特别有趣。成为茶余饭后的笑料。
之后就是海笔,主要是笔了两家:亚微电子和锐捷网络。亚微电子是刚好那天没事干,就跟同学说去见识下题,没想到真的见识到了,第一部分,高数,看见题目就一阵眩晕;第二部分,智力题,再晕;第三部分,C++,我想直接站起来走了……看看人家那么热情的给我拿试卷,又觉得不好意思,于是写C++,都拿不准,想想不如写高数,于是做了几题高数,没想到N年没做,求导求积分到是一点不含糊;再做了个智力题交卷走人,没报希望,果然也没通知。锐捷网络到还比较大,在福州,也是跟同学去霸王笔,黑板上写了有三套题:嵌入式,C++,java,没看见,结果稀里糊涂做了嵌入式的题,出来后被同学嘲笑了。不过题目还是有点深度的,印象比较深的有两题,一题无论在何种环境下都要赋给unsigned long l最大值;另一题编个函数模拟malloc,似乎以前见过,不过当天做的还是模糊,唉,好久没搞c了。
稍微正式点的是瞬联软件,瞬联是做外包的,给的工资很高,老早就查过。投了简历后特意通知我去听宣讲,宣讲何必通知,又没笔试,后来估计是自己的简历让对方产生了兴趣。最有竞争力的是它的福利,太人性化了,想到的都有了……宣讲时听着企业文化,第一条就是诚信,举了很多例子,突然觉得心就靠过去了,因为诚信,后面说的每句话都相当可靠,直到放出希望工程的捐赠图,觉得这个公司真的很善待员工。虽然是外包,但是都派到MOTO,朗讯,北电这样的大公司,接触的都是核心。第一个从企业文化上让我折服的公司……又投了纸质简历,知道一定会有我的面试机会,果然,还没到家呢,电话就响了。第二天早9点30的笔试和面试,带本科和研究生成绩单,带简历,开始做了份题,果然不简单,都是java相当基础相当细的东西,很多东西都不是平时能见到的,后来发现我也错了好几题,印象比较深的有一题问sleep和wait的区别,《多线程设计模式》讲过;还有题public String aaa(){ return stringArray[index++]}//其中stringArray和index已经在别处定义过,问这个函数会不会发生内存泄漏,记得在《effective java》上看过,是会发生内存泄漏,但具体如何却想不起了,只怪自己学艺不精。笔试完直接面试,一个GG,一个MM,一进去先是英文自我介绍,之后介绍一个项目,乱七八糟蹦出来的word,发现大家还是勉强可以交流的,因为面试官的英文更难听。面试主要集中在项目上,半小时很快就完了,没搞懂这么快就能要一个人了?结果1星期后收到Offer,赞一个,高效率。
然后是群硕的笔试。老早就摩拳擦掌的准备要去了,不是因为想进去,而是半年前这个公司直接将我的简历拒掉了,这次是报仇的好机会,决定好好做题上去BS它一把。第一份题是计算,数列之类的,又是考智商的,那些出题的人智商才有问题,20分钟,没做完,心想挂了。第二份题是编程,第一题取得链表中倒数第m个元素的值,《程序员面试攻略》上的原题,无语,几分钟搞定,第二题如何最快判断一个数是不是2的n次方,我想的方法是移位,当且仅当出现一个1判为是,不过思路错了,回去同学说起才发现。第三题又是测试。第四题问面向对象的open-close原则,举出相应例子,刷刷刷把设计模式和设计原则写上去,收工。下来后感觉第一份没做好,编程第二题也错了,顿觉得失望,没想到BS它的机会都没有。没想到过了几天通知面试了。说实话,群硕面试是我最爽的一次。先是英文自我介绍,有了早上的经历,下午说得是相当溜,又英文介绍项目,仍然被我命中,照溜。接着问我一个项目的业务逻辑,又被我准备过,仍然很溜。然后就是问ConcurrentHashMap的一些实现比Hashtable有哪些好处,撞到我枪口上了,当年读了ConcurrentHashMap的源码可不是白读的,解释一遍源码机制,那人有点茫然,连连点头;又问设计模式中的Facade模式,再晕,将bang的facade经典用法解释一遍,那人又点头;完了又问decorator模式,将java IO类中的decorator用法解释一遍,那人非常满意,开始跟我聊家常,心里窃喜,知道过了……果然,让我等HR面下一轮,HR问的问题都是我深有体会的,每个问题都举个例子说明,比如遇到的挫折,对我的影响,比如自己的优点,最后问我期望月薪,答8k,问怎么定位的,我说什么怎么定位,她问为什么要这个价位的薪资,答HP给了6.5*14,自己算去。面完出来一阵快感,终于达成梦想。一周后收到Offer,打开一看写了6k*13,还要签三年,还来句这是北京上海的薪资,如果不在北京上海,会适当调整,无论如何,都会给竞争力的薪资,差点一把撕掉,群硕的工作强度早有耳闻,不比别的公司高反而还降工资,完全是小公司的作为。看来还是它再次BS了我……
接下来又收到IBM的面试通知。当天正在听AT科尔尼的宣讲,看看那些商务人士,做的事情往往可以影响整个公司乃至国家,觉得这样的工作真有成就感,无奈,英语水平不到家。IBM面试通知是比较意外的,用纸记了半天时间地点,心想:妈的,这也能过,IBM也真够搞笑的了。一周后才开始面试,面试当天跑到古井假日酒店,又是5星,豪华得刺眼,突然发现当时记的时间地址里只有酒店名,没有房间号,狂汗!赶紧跟前台说,前台查了半天说有很多IBM的房间,不知道是谁?晕,赶紧跑出去打个电话给IBM总部,妈的还没开始上班。迟到了,疯了。还不放弃,跑回酒店问一个前台MM,让她随便给我一个IBM的房间,丫的直接拒绝,理由是不能将客人的信息随便透露,又去打电话……就这样来回跑了几次……最后幸亏酒店一个服务生帮我问到了,对这次面试完全失望。上去发现我是第一个到了,因为别人也没得到房间号通知。面试开始,是一个很有礼貌的GG,看他那台超小的IBM笔记本就知道是项目经理级别。照例是自我介绍,出乎我意料的是没有用英文,完了,之后不知道会有什么英文提问。提问不多,但是都很一针见血。比如见到我的项目经验有struts,问我struts在使用过程中有什么优缺点。想起来一个优点,但看他那么厉害就知道不能乱忽悠了,直接说了sorry。面试中突然问我个用英文回答的问题:为什么希望进IBM?虽然是常规得不能再常规的题型了。但我的英文回答仍然是结结巴巴,答完我自己就笑了。最后还问了个问题:你认为我们有什么理由要你,对于这种很tough的问题,还是得小心翼翼。实际上就是问自己的优点,我回答优点时每条都加上一个例子证明,结果面试官说让他很impressive,心想,也许有下一面了吧。
本文出自:http://blog.sina.com.cn/s/blog_58adc9e701000bx1.html


把一个java web应用包装成桌面应用的简单做法

luohg666收录,使用标签:Java,时间:2008-6-20 13:03:45 | 相关网摘我也收藏

昨天玩h2的时候想起来的, 这年头把在线应用报装成桌面应用是越来越流行了,几年前做过类似的事情,简单介绍一下。

1. 选择一个嵌入的web 服务器和 数据库。
web服务器我选择的是jetty,虽然tomcat也有类似的嵌入版本,但是体积和性能都还是有点差距。 如果你的应用使用了大量的ajax效果,建议选择6的版本,对并发支持更好。
数据库方面我原来用的是hsql,现在可以选择h2. 两者的sql 兼容性都比较好,扩展功能也比较强。如果原来的应用在mysql上进行开发,则基本不用做修改, h2现在有mysql兼容模式,如果原来应用使用的是db2或者oracle,则你可能需要把一些函数和过程改用java实现,h2提供了这方面的支持。 数据问题方面也不用担心,h2目前比较成熟了,而且最大可以提供256g的文件支持。我做过一些测试,关机,死机,都还可以恢复的比较好。

h2的一个特性是支持只读数据库,所以你也可以选择将数据库备份成zip文件以后,以光盘的方式发布应用,很适合做大量分发的演示光盘,比较酷吧。

jetty+h2的组合占用系统资源非常少,而且速度极快, 在我成功移植以后,我的一个同事吃惊的发现速度要比原来的online版本提高了很多。一般用户几乎感觉不到在线操作的那种停顿。

java6自带的那个java数据库就不要考虑了,速度慢,体积大,兼容问题多多。

2. 使用JDIC或者java6提供的一些native特性和桌面进行整合

主要使用诸如系统托盘来提供一些控制操作和辅助功能, 打开浏览器或者将浏览器嵌入到swing应用等等。我的做法是将浏览器嵌入到swing应用中,并做了一些小手脚,保证用户不能使用浏览器直接访问,这样看起来更安全,更象桌面应用。

java6在这些方面有比较好的支持,6以前的版本可以使用JDIC,反正都是一回事。 另外可以选择jgoodie之类的扩展包,让你的桌面看起来更骚包一些。

当然,使用6的话启动要快一些,特别是重复启动的时候,java6还有的一个特性是splash的支持, 这样在启动的时候可以自然一些。

3. 对配置文件进行适当精简,使用混淆工具编译关键包

发布到桌面以后就意味着受攻击指数增加了,增加适当的防范也是必要的,此处如果不怕麻烦,可以使用gcj来编译。

4. 将执行包编译成exe文件, 使用安装工具打包

把java启动类编译成可执行文件的工具很多,我经过比较,使用的是launch4j,主要是支持的特性多一些,对linux也提供支持。
然后再把jre和应用整理规划好目录,使用安装工具打包, 我选择的是nsis, 也是免费的东西, nisi如果不想深入研究的话,建议找supertoy开发的一个脚本生成向导工具,很好用。

5. 其他

* 需要考虑数据的备份和交换操作, 我一般使用dbunit来干这个事, 因为导出导入都可以是完整的xml文件,比较方便。
* 如果要制作光盘版本,再配置一个auto run 就可以自动启动了。
* 要防止应用多次启动, 实现思路有2种,一个是使用一个端口来检测, 另外一个就是使用文件锁。 我使用的是后者,盗用了hsql的部分代码。

基本思路很简单,工作量也不大, 呵呵,反正当初用户是看不出来这是java 做得,还误以为我们是用delphi之类重新开发的。


基于Ant+Velocity的简单代码生成器的思路与实现

luohg666收录,使用标签:java, Velocity,时间:2008-6-10 9:52:34 | 相关网摘我也收藏

在SSH项目中,我们应用了service layer模式,所以针对一个模块,它就存在pojo、dao、daoImpl、service、serviceImpl,再到struts中的action、form。假设设计是面向数据库的,针对一个数据库表,那么就要产生7个java文件,如果还要做异常处理,那么就是8个java文件。如果数据库有50个表,那么就是50*8=400个java文件。工程不小。

至于为什么要用service layer模式,论坛上已有讨论http://www.javaeye.com/topic/29867

然而我们都知道,web中出现最多的操作是CURD,这400个java文件中有多少代码是重复的?几乎占了80%甚至更多。编写这样重复的代码是很枯燥无味的,而且如果是由不同人负责不同的模块的分工方式,程序员编码的风格是各不相同(虽然可能有规范约束,但是最后出来的东西还是避免不了的带有程序员个人风格的)。

所以为了节省时间和精力,便做一个程序来生成程序。
只要配置好你的项目名,你的模块名,模块路径,就可以在几秒之内完成一个模块的CURD代码,同时你可以自定义模板。
....................


实现Java Web程序的自动登录

luohg666收录,使用标签:Java,时间:2008-6-7 18:49:05 | 相关网摘我也收藏

有很多Web程序中第一次登录后,在一定时间内(如2个小时)再次访问同一个Web程序时就无需再次登录,而是直接进入程序的主界面(仅限于本机)。实现这个功能关键就是服务端要识别客户的身份。而用Cookie是最简单的身从验证。

如果用户第一次登录,可以将用户名作为Cookie写到本地,代码如下:


Cookie cookie = new Cookie("user", user);
cookie.setMaxAge(365 * 24 * 3600);
cookie.setPath("/");
response.addCookie(cookie);

当用户再次访问程序时,服务端程序应该检测这个Cookie是否存在,代码如下:

Cookie[] cookies=request.getCookies();
for(Cookie cookie: cookies)
{
if(cookie.getName().equals(user))
{
// 如果user Cookie存在,进行处理
break;
}
}

尽管从客户端可以获得User Cookie,但这上Cookie可能存在很长时间,而且仅凭这个Cookie就自动登录并不安全,因此,可以在服务端使用一个Session来管理用户。也就是当第一次登录成功后,就创建一个Session,并将用户的某些信息保存在Session顺。代码如下:

HttpSession session =request.getSession();
session.setAttribute(user, user);
session.setMaxInactiveInterval(2 * 3600); // Session保存两小时

当再次访问程序时,确定了cookie存在后,就会继续验证User Session的存在,代码如下:

Cookie[] cookies=request.getCookies();
for(Cookie cookie: cookies)
{
if(cookie.getName().equals(user))
{
if(session.getAttribute(user) != null)
{
// 直接forward到主界面
break;
}
else
{
// forward到登录界面
}
}
}

虽然上面的代码可以很好地实现自动登录功能,但是当浏览器关闭,再次启动后,由于Servlet用于保存Session ID的JSESSIONID Cookie是临时的(也就是说不是持久Cookie,当浏览器关闭后,这个Cookie就会被删除),因此,需要将JSESSIONID进行持久化。代码如下:

HttpSession session = request.getSession();
session.setAttribute(user, user);
session.setMaxInactiveInterval(2 * 3600); // Session保存两小时
Cookie cookie = new Cookie("JSESSIONID", session.getId());
cookie.setMaxAge(2 * 3600); // 客户端的JSESSIONID也保存两小时
session.setMaxInactiveInterval(interval)
cookie.setPath("/");
response.addCookie(cookie);


如果使用上面的代码,即使浏览器关闭,在两小时之内,Web程序仍然可以自动登录。

如果我们自已加一个JSESSIONID Cookie,在第一次访问Web程序时,HTTP响应头有两个JSESSIONID,但由于这两个JSESSIONID的值完全一样,因此,并没有任何影响。如果在响应头的Set-Cookie字段中有多个相同的Cookie,则按着path和name进行比较,如果这两个值相同,则认为是同一个Cookie,最后一个出现的Cookie将覆盖前面相同的Cookie,如下面的两个Cookie,最后一个将覆盖前一个:

Set-Cookie: JSESSIONID=DDB2274CAC6739E1D6747B0043D5D90E; Path=/web

Set-Cookie: JSESSIONID=mysession; Expires=Thu, 05-Jun-2008 05:02:50 GMT; Path=/web

由于下面两个Cookie的path不同,因此,它们是完全不同的两个Cookie:

Set-Cookie: JSESSIONID=DDB2274CAC6739E1D6747B0043D5D90E; Path=/web1

Set-Cookie: JSESSIONID=mysession; Expires=Thu, 05-Jun-2008 05:02:50 GMT; Path=/web2


谁有其他的方法实现自动登录,请跟贴!


EasyMF 简易J2ME开发框架

luohg666收录,使用标签:Java, j2me,时间:2008-6-7 18:47:36 | 相关网摘我也收藏

作者:wuhua
空间:htt://wuhua.3geye.net
转载请保留上面的信息(请尊重知识产品)谢谢

EasyMF 简易J2ME开发框架
EasyMF --- Easy Mobile Framework
目标: 设计一个简单,稳定,可快速开发的J2ME开发框架。
一.说明:
此框架的意图是解决手机软件开发中常遇到,并且可以通用话的问题。
旨在提高无线应用程序的开发效率
二.EasyMF 能做什么
1.简化UI设计
2.RMS的简化操作
3.简单的日志框架
4.简单的联网框架
5.通用工具的设计
6.一些图形相关的特效
三.EasyMF 的功能列表
1.UI 框架
2.日志框架
3.RMS框架
4.通用工具框架
5.图形相关
6.一堆有用的DEMO
四.EasyMF 的设计原理
1.UI的设计原理
UI是所有应用程序的核心。 这些核心在PC上的变动性比较固定,而由于手机屏幕,
运算能力 等差异,会导致设计一个手机软件大部分时间都花在UI层的设计上,业务
逻辑层往往比较少,这也就是80/20原则吧。
如果有一个可以组装的UI的出现,将大大减少应用程序的开发时间,而EasyMF UI
层就是为了这个而设计的。
2.日志框架设计成尽量的简单,并且有效的记录下用户想需要记录的日志
3.联网框架,简化Http的联网,并十分的符合中国网络环境的东西。
4.工具集合
5.图形图片处理
6.RMS的简易操作

上面是基本结构

源代码 http://code.google.com/p/easymf/


61条面向对象设计的经验原则

luohg666收录,使用标签:java, 面向对象,时间:2008-5-20 16:05:34 | 相关网摘我也收藏

“你不必严格遵守这些原则,违背它们也不会被处以宗教刑罚。但你应当把这些原则看成警铃,若违背了其中的一条,那么警铃就会响起。”
----------Arthur J.Riel

(1)所有数据都应该隐藏在所在的类的内部。

(2)类的使用者必须依赖类的共有接口,但类不能依赖它的使用者。

(3)尽量减少类的协议中的消息。

(4)实现所有类都理解的最基本公有接口[例如,拷贝操作(深拷贝和浅拷贝)、相等性判断、正确输出内容、从ASCII描述解析等等]。

(5)不要把实现细节(例如放置共用代码的私有函数)放到类的公有接口中。
如果类的两个方法有一段公共代码,那么就可以创建一个防止这些公共代码的私有函数。

(6)不要以用户无法使用或不感兴趣的东西扰乱类的公有接口。

(7)类之间应该零耦合,或者只有导出耦合关系。也即,一个类要么同另一个类毫无关系,要么只使用另一个类的公有接口中的操作。

(8)类应该只表示一个关键抽象。
包中的所有类对于同一类性质的变化应该是共同封闭的。一个变化若对一个包影响,则将对包中的所有类产生影响,而对其他的包不造成任何影响 .

(9)把相关的数据和行为集中放置。
设计者应当留意那些通过get之类操作从别的对象中获取数据的对象。这种类型的行为暗示着这条经验原则被违反了。

(10)把不相关的信息放在另一个类中(也即:互不沟通的行为)。
朝着稳定的方向进行依赖.

(11)确保你为之建模的抽象概念是类,而不只是对象扮演的角色。

(12)在水平方向上尽可能统一地分布系统功能,也即:按照设计,顶层类应当统一地共享工作。

(13)在你的系统中不要创建全能类/对象。对名字包含Driver、Manager、System、Susystem的类要特别多加小心。
规划一个接口而不是实现一个接口。

(14)对公共接口中定义了大量访问方法的类多加小心。大量访问方法意味着相关数据和行为没有集中存放。

(15)对包含太多互不沟通的行为的类多加小心。
这个问题的另一表现是在你的应用程序中的类的公有接口中创建了很多的get和set函数。

(16)在由同用户界面交互的面向对象模型构成的应用程序中,模型不应该依赖于界面,界面则应当依赖于模型。

(17)尽可能地按照现实世界建模(我们常常为了遵守系统功能分布原则、避免全能类原则以及集中放置相关数据和行为的原则而违背这条原则) 。

(18)从你的设计中去除不需要的类。
一般来说,我们会把这个类降级成一个属性。

(19)去除系统外的类。
系统外的类的特点是,抽象地看它们只往系统领域发送消息但并不接受系统领域内其他类发出的消息。

(20)不要把操作变成类。质疑任何名字是动词或者派生自动词的类,特别是只有一个有意义行为的类。考虑一下那个有意义的行为是否应当迁移到已经存在或者尚未发现的某个类中。


在Java程序中处理数据库超时与死锁

luohg666收录,使用标签:Java,时间:2008-5-16 17:04:22 | 相关网摘我也收藏

在Java程序中处理数据库超时与死锁


简介
每个使用关系型数据库的程序都可能遇到数据死锁或不可用的情况,而这些情况需要在代码中编程来解决;本文主要介绍与数据库事务死锁等情况相关的重试逻辑概念,此外,还会探讨如何避免死锁等问题,文章以DB2(版本9)与Java为例进行讲解。


什么是数据库锁定与死锁
锁定(Locking)发生在当一个事务获得对某一资源的“锁”时,这时,其他的事务就不能更改这个资源了,这种机制的存在是为了保证数据一致性;在设计与数据库交互的程序时,必须处理锁与资源不可用的情况。锁定是个比较复杂的概念,仔细说起来可能又需要一大篇,所以在本文中,只把锁定看作是一个临时事件,这意味着如果一个资源被锁定,它总会在以后某个时间被释放。而死锁发生在当多个进程访问同一数据库时,其中每个进程拥有的锁都是其他进程所需的,由此造成每个进程都无法继续下去。


如何避免锁
我们可利用事务型数据库中的隔离级别机制来避免锁的创建,正确地使用隔离级别可使程序处理更多的并发事件(如允许多个用户访问数据),还能预防像丢失修改(Lost Update)、读“脏”数据(Dirty Read)、不可重复读(Nonrepeatable Read)及“虚”(Phantom)等问题。


隔离级别 问题现象
丢失修改 读“脏”数据 不可重复读 “虚”
可重复读取 No No No No
读取稳定性 No No No Yes
光标稳定性 No No Yes Yes
未提交的读 No Yes Yes Yes

表1:DB2的隔离级别与其对应的问题现象


在只读模式中,就可以防止锁定发生,而不用那些未提交只读隔离级别的含糊语句。一条SQL语句当使用了下列命令之一时,就应该考虑只读模式了:

1、JOIN
2、SELECT DISTINCT
3、GROUP BY
4、ORDER BY
5、UNION
6、UNION ALL
7、SELECT
8、FOR FETCH ONLY (FOR READ ONLY)
9、SELECT FROM


如果包含上述任一命令,可以说你的SQL语句有歧义性,因此,锁可能就是造成其中资源问题的源头。


另外,以下是一些可降低锁数目的建议:

1、 将CURRENTDATA设为NO。这条命令告诉DB2模糊光标为只读。
2、 在适当的时候,尽可能使用User Uncommitted Read(用户未提交的读)。
3、 尽可能关闭所有光标。
4、 有一个正确的提交策略。确保程序不再使用资源时就立即释放它。


如何处理死锁与超时
在程序中使用重试逻辑,可处理以下三种SQL错误代码:

1、 904:返回这个代码表示一条SQL语句是因为已达到资源限度而结束的。程序中可提交或回滚更改,并执行重试逻辑。
2、 911:程序收到这个SQL代码,表示因为没有为锁列表分配足够的内存,现在已达到数据库的最大锁数目。
3、 912:程序收到这个SQL代码,表示死锁或超时,依照904中的方法来解决。


以下是一段Java代码,其捕捉返回的-911、-912、-904代码,并进行重试:


for (int i = 0; i < MAX_RETRY_ATTEMPTS; i++) {
//以下代码模拟一次事务
try {
stmt = conn.createStatement();
System.out.println("Transaction started...");
stmt.executeUpdate("UPDATE 1..."); //SQL语句1
stmt.executeUpdate("UPDATE 2..."); // SQL语句2
stmt.executeUpdate("UPDATE 3..."); // SQL语句3
stmt.executeUpdate("UPDATE 3..."); // SQL语句4
//提交所有更改
conn.commit();
System.out.println("事务已完成。");
//确保只运行了一次。
i = MAX_RETRY_ATTEMPTS;
} catch (SQLException e) {
/**
*如果返回的SQL代码为-911,回滚会自动完成,程序回滚至前一次的提交状态。
*程序将进行重试。
*/
if (-911 == e.getErrorCode()) {
//等待RETRY_WAIT_TIME
try {
Thread.sleep(RETRY_WAIT_TIME);
} catch (InterruptedException e1) {
//即使休眠被打断,但仍要重试。
System.out.println("休眠被打断。");
}
}
/**
*如果返回的SQL代码为-912,表示死锁及超时。
*如果是-904,代表已达到资源限度。
*在这种情况下,程序将回滚并进行重试。
*/
else if (-912 == e.getErrorCode() || -904 == e.getErrorCode()) {
try {
//需要回滚
conn.rollback();
} catch (SQLException e1) {
System.out.println("无法回滚。"; color:black'> + e);
}
try {
//等待RETRY_WAIT_TIME
Thread.sleep(RETRY_WAIT_TIME);
} catch (InterruptedException e1) {
//即使休眠被打断,但仍要重试。
System.out.println("休眠被打断。" + e1);
}
} else {
//如果是其他错误,就不进行重试。
i = MAX_RETRY_ATTEMPTS;
System.out.println("有错误发生,错误代码:"
+ e.getErrorCode() + " SQL状态:"
+ e.getSQLState() + "其他信息:" + e.getMessage());
}


从上面也可看到,程序对死锁、超时、最大锁数目将会进行MAX_RETRY_ATTEMPTS次重试;其次,当“最大锁数目”的情况发生时(-911),程序不必手工进行回滚,因为此时的回滚是自动完成的;最后,无论何时返回-911、-904、-912代码,程序应在下次重试前等待RETRY_WAIT_TIME一段时间。


JFreeChart Study的一些代码

luohg666收录,使用标签:JFreeChart, java, 图表,时间:2008-5-15 10:08:30 | 相关网摘我也收藏

CategoryDataset dataset=createDataset();
JFreeChart chart=ChartFactory.createBarChart(
"世界傻瓜大奖赛",//图表标题
"比赛场次",//X轴标题
"傻瓜程度",//Y轴标题
dataset,//数据集合
PlotOrientation.VERTICAL,//图表显示方向(水平、垂直)
true,//是否使用图例
false,//是否使用工具提示
false//是否为图表增加URL
);

/*------------配置图表属性--------------*/
// 1,设置整个图表背景颜色
chart.setBackgroundPaint(Color.yellow);


Java SE 6之GUI:让界面更加绚丽(上)

luohg666收录,使用标签:Java,时间:2008-5-1 23:35:42 | 相关网摘我也收藏

http://www.blogjava.net/nokiaguy/archive/2008/04/29/197246.html

Java是一种非常强大的编程语言,但Java的GUI能力一直是它的软肋。虽然Java提供2种图形解决方案,AWT和Swing。但这2种图形库的功能十分有限,使用它们并不能带来更多的用户体验。而且AWT和Swing的更新速度很慢。一直到Java SE 5也没什么明显的变化。幸好Java SE 6终于为它们增加大更强大的功能。有了这些功能,使用AWT和Swing设计的用户界面可以更贴近用户。为了使读者能够更早地体验Java SE 6 GUI的美丽,本文就Java SE 6 GUI提供的主要功能进行讨论。

一、显示启动界面

现在很多商业和非商业软件在启动时都必须初始化,有时初始化的时间会很长,如photoshop、flash等,为了让用户有耐心继续等待,不要误以为死器了,这些软件在初始化之前总是显示一个图形界面来告诉用户软件正在做什么。如图1是Flash8在启动时的初始化界面。







图1 Flash8的启动界面

当我们在设计软件时可能也需要这样一个界面。当然,这在Java SE的早期版本中也可以做到,但比较费劲,基本的实现是将一个图形放到窗体上,然后先显示这个,这需要编写一定的代码,而Java SE 6提供了一个更简单的方法,可以不添加一行代码,而且在界面要改变时还不需要重新编译源程序。让我们先看一个简单的例子。


import javax.swing.*;
import java.awt.*;

public class TestSplash
{
public static void main(String args[])
{
try
{
// 为了让启动界面多显示3秒,
// 如果是正常的程序,这里应该时初始化代码
Thread.sleep(3000);
JFrame frame = new JFrame("Java SE 6 启动界面演示");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new JLabel("我的标签", JLabel.CENTER);
frame.add(label, BorderLayout.CENTER);
frame.setSize(300, 95);
frame.setVisible(true);
}
catch (InterruptedException e)
{
}

}
}


上面的程序是一个非常简单的界面演示程序,它的界面如图2所示。





图2

如果使用java TestSplash直接运行上面的程序,将立刻显示如图2的界面。Java SE 6在java命令上提供了一个选项,可以在显示程序主界面之前先显示一幅图。

java –splash:splash.gif TestSplash

如果运行以上的命令,在出现图2所示的界面之前会先显示splash.gif,然后再过几秒钟splash.gif自动关闭,然后会显示主界面。在上面程序中的Thread.sleep(3000); 是为了使splash.gif显示的时间长一些,可以将这句改为直正的初始化代码。splash.gif文件如图3所示。






图3 splash.gif

在发布时一般将程序所用到的.class文件打包成一个或几个.jar文件,如果是这样,可以将splash.gif一同打包在.jar文件中。然后在manifest.mf文件中使用SplashScreen-Image指定启动界面图象。

在打包之前,先建一个文本文件manifest.mf,内容如下

Manifest-Version: 1.0

Main-Class: TestSplash

SplashScreen-Image: splash.gif

然后通过以下命令进行打包

jar -mcvf manifest.mf Splash.jar TestSplash.class splash.gif

然后通过如下命令运行程序

java -jar Splash.jar

现在我们已经可以显示一个启动界面了,但这只是一幅静态的图,而在大多数时还需要在这幅图上显示一些信息,如正在初始化什么。在Java SE 6也提供了可以操作这幅图的功能。下面的代码演示了如何操作启动界面。



import javax.swing.*;
import java.awt.*;
public class TestSplash
{
public static void main(String args[])
{
try
{
String[] info = new String[]
{ "正在初始化API", "正在初始化网络", "正在初始化数据库" };
SplashScreen splash = SplashScreen.getSplashScreen();
Graphics g = splash.createGraphics();
if (splash != null)
{
for (int i = 0; i < 3; i++)
{
g.setColor(Color.BLACK);
g.drawString(info[i], 350, 170 + i * 15);
splash.update();
Thread.sleep((i + 1)*1000);
}
}
JFrame frame = new JFrame("Java SE 6 启动界面演示");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new JLabel("我的标签", JLabel.CENTER);
frame.add(label, BorderLayout.CENTER);
frame.setSize(300, 95);
frame.setVisible(true);
}
catch (Exception e)
{
}
}
}

上面的代码SplashSreen获得启动界面,然后再通过createGraphics得到一个图形句柄。本例将在启动界面显示按一定时间间隔显示三个字符串,这三个字符串保存在info数组中。启动界面如图4所示。






图4 在启动界面上显示三个字符串

另外需要提一点,在SplashScreen类中有一个close方法用于关闭启动界面,但这个方法一般不需要调用,因为在主窗口显示时这个方法自动被调用。当然,要想在初始化过程中关闭这启动界面,如发生初始化错误后,就不能再往下初始化了,因此,就要关闭这个界面,然后或退出程序,或直接进入主界面。

二、系统托盘

现在有很多程序除了提供了一个完整的主界面外,在它最小化或者启动后在右下角的系统托盘区总是有一个小图标,如金山词霸、QQ和MSN等。图5显示了一台计算机的系统托盘里的图标。








图5 系统托盘

如果使用C++、Delphi(Pascal)等语言实现这个功能非常简单,只要调用一下系统API即可,但在以前版本(Java SE 5以前)的Java SE中Sun并未提供访问系统托盘的类,在那时使用Java访问系统托盘的唯一方法就是使用JNI直接调用系统API,但这种方法实现比较复杂,而且如果这样做,就破坏了Java的跨平台特性(除非你为每一个操作系统平台都做一个这样的类)。Java的这个缺陷缓和终于在Java SE 6中得到弥补。在java.awt包中提供了一个SystemTray类可以直接访问系统托盘。下面的代码演示了一个简单的实现。


import javax.swing.*;
import java.awt.*;

public class MyTray
{
public static void main(String args[])
{
// 判断当前操作系统是否支持系统托盘
// 最好加上这个判断,否则这个程序可能在其它操作系统上无法正常工作
if (SystemTray.isSupported())
{
// 通过静态方法getSystemTray()得到系统托盘
SystemTray tray = SystemTray.getSystemTray();
// 装载托盘图象
Image image = Toolkit.getDefaultToolkit().getImage(
"tray.gif");
// 为这个托盘加一个弹出菜单
PopupMenu popup = new PopupMenu();
MenuItem item = new MenuItem("我的菜单");
popup.add(item);
// 为这个托盘加一个提示信息
TrayIcon trayIcon = new TrayIcon(image, "提示信息", popup);
try
{
tray.add(trayIcon);
}
catch (AWTException e)
{
System.err.println("无法向这个托盘添加新项: " + e);
}
}
else
{
System.err.println("无法使用系统托盘!");
}
}
}



在运行上面的程序之前,tray.gif一定要放到当前目录,如果放到其它的目录,要在程序中指明。图6是程序运行结果。最左侧的五彩光盘图标就是本程序加入了。





图6 运行结果

如果多次调用tray.add(trayIcon),可以在一个程序中向系统托盘添加多个图标。但要注意,图象可以使用一个Image对象,但弹出菜单要为每一个图标建一个,如果不建,就设为null。如:

TrayIcon trayIcon1 = new TrayIcon(image, "提示信息1", null);

TrayIcon trayIcon2 = new TrayIcon(image, "提示信息2", null);

TrayIcon trayIcon3 = new TrayIcon(image, "提示信息3", null);

tray.add(trayIcon1);

tray.add(trayIcon2);

tray.add(trayIcon3);

图7为显示界面图





图7

Java SE 6还为我们提供了3种提示信息,它们是警告、错误和信息。如使用如下语句添加提示信息。

添加警告提示

trayIcon.displayMessage("警告", "这是一个警告提示!", TrayIcon.MessageType.WARNING);





图8 警告提示

添加错误提示

trayIcon.displayMessage("错误", "这是一个错误提示!", TrayIcon.MessageType.ERROR);







图9 错误提示

添加信息提示

trayIcon.displayMessage("信息", "这是一个信息提示!", TrayIcon.MessageType.INFO);





图10 信息提示

交互对话框



在一般的程序语言或开发工具中都有一些标准的对话框以方便和用户交互,如Delphi中有InputBox对话框用来采集用户的输入信息。在Java SE 6中也提供了类似的标准对话框来满足这些需求。

在Java SE 6中提供了一种输入对话框,下面是一个使用这个对话框的简单例子。

JOptionPane pane = new JOptionPane("文本", JOptionPane.INFORMATION_MESSAGE);

pane.setWantsInput(true);

JDialog dialog = pane.createDialog(parent, "请输入文本");

dialog.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);

dialog.setVisible(true);

String msg = pane.getInputValue();

上面的代码使用JoptionPane对象的createDialog方法来建立一个输入对话框。上面的代码有几点需要注意一下。

1. 如果将pane.setWantsInput(true)改为pane.setWantsInput(false),那么输入对话框将消失。

2. 这个对话框使用的是APPLICATION_MODAL模式,Java SE 6还提供了一个DOCUMENT_MODAL模式,这两种模式最大的区别是第一种模式是对整个应用程序的,也就是说,使用这种模式弹出对话框后,整个程序都不访问,而使用第二种模式,只有弹出对话框的窗体不可访问,其它的窗体并不受影响。图11为以上代码的程序界面。





图11

图象转换



Java在图象处理上是比较强大的,但是以前的Java SE版本对于GIF图只能读,不能写。这并不是因为技术原因,而是因为GIF专利的原因。但最近这个专利到期了,因此,在Java SE 6中提供了读写GIF的功能。下面的代码演示了如何通过Java来将其它格式的图象转换为gif格式。


import javax.imageio.*;
import java.io.*;
import java.awt.image.*;
import java.util.*;

public class ToGIF
{
public static void main(String args[]) throws IOException
{
// 可以将abc.bmp换成其它格式的图象
File inputFile = new File(“abc.bmp”);
BufferedImage input = ImageIO.read(inputFile);
File outputFile = new File("abc.gif");
ImageIO.write(input, "GIF", outputFile);
}
}

如果你想知道在Java SE 6支持什么图象格式,可以使用如下语句进行查看。

System.out.println(Arrays.toString(ImageIO.getWriterFormatNames()));

显示结果

[BMP, bmp, jpg, JPG, wbmp, jpeg, png, PNG, JPEG, WBMP, GIF, gif]


JVM深度历险小记

luohg666收录,使用标签:Java,时间:2008-5-1 23:33:47 | 相关网摘我也收藏

如果安装JDK1.3那么安装程序一定会同时安装两套JRE。
一套位于 jdk\jre目录
一套位于program files\JavaSoft目录
如果是JDK 1.4可以选择是否安装program files\java目录下的jre,但是jdk安装目录下的jre这套jre必须安装

JRE与PC比较

JRE: java类函数库>原生函数库.dll>JAVA虚拟机(jvm.dll)>帮助函数库.dll
PC: Win32 API .dll>CPU

编写好的Java源文件必须要有JRE才能帮助我们运行,Java虚拟机只是JRE里的一个成员而已,或者说jvm只是jre里头一个动态连接函数库,
jdk里面的jre一般用于运行java本身的程序,比如javac,等等.programfiles下面的jre用于运行用户编写的java程序.
JRE下的bin\client 或者 bin\server 的jvm.dll就是JVM了

----------------------------
在刚装好jdk,没有对计算机进行任何设置时,进入命令行窗口


C:\Documents\Administrator>java -version
java version "1.5.0_11"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_11-b03)
Java HotSpot(TM) Client VM (build 1.5.0_11-b03, mixed mode, sharing)

C:\Documents\Administrator>java -server -version
Error: no `server' JVM at `C:\Program Files\Java\jre1.5.0_11\bin\server\jvm.dll'
-----------------------------------

当设置path路径中包含jdk\bin目录后

----------------------------
C:\>set path="C:\Program Files\Java\jdk1.5.0_11\bin";%path%;

C:\>java -version
java version "1.5.0_11"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_11-b03)
Java HotSpot(TM) Client VM (build 1.5.0_11-b03, mixed mode, sharing)

C:\>java -server -version
java version "1.5.0_11"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_11-b03)
Java HotSpot(TM) Server VM (build 1.5.0_11-b03, mixed mode)

-server 的-version就可以显示出来了
----------------------------

JDK里用Java所写的开发工具 如javac.exe jar.exe都存放在JDK安装目录\lib\tools.jar 这个档案中
javac.exe 只是一个包装器(wrapper),而制作目的是为了让开发者免于输入太长的指令。
实际等于:
java -classpath x:\jdk1.xx\lib\tools.jar com.sun.tools.javac.Main


当用j2me开发palm应用程序的时候,工具会帮我们打包jar档,然后用一个RPC档的外壳罩住,让java程序看起来像是一个原生(native)的应用程序。
用.NET 开发出来的执行文件也是一个包装器的概念。

JDK里面的工具几乎全是用java所写的,所以JDK本身就是Java应用程序,因此要用JDK附的工具来开发Java程序,
也必须要自行附一套JRE才行。这就是JDK安装目录\jre下需要一套JRE的原因。
位于program files\下的那套JRE就是拿来执行我们自己写的java应用程序。不过,两套中任何一套JRE 都可以拿来执行我们所撰写的Java 应用程序,
可是JDK 内附的开发工具在预设使用包装器(.exe) 来启动的情形下,都会自己去选用\jre 底下那套JRE。
------------------------

到底是执行哪一个java.exe
java xxx
当一台机器上有多个jvm可选择的时候,jvm的选择步骤:
1)当前目录有没有jre目录(不准确),
2)父目录下的jre子目录
3)注册表HEKY_LOCAL_MACHINE\SoftWare\Java\Java Runtime Environment\
所以当运行的是jdk\bin\java.exe的时候,用的jre是bin的父目录jdk下面的jre\
运行java.exe找到了jre后有一个验证程序,verify.dll验证jre和java.exe的版本是否一致,如果不一致则会发生错误


一般把常用的工具档放到JDK目录\jre\lib\ext下
把有关安全机制的配置文件放到
JDK目录\jre\lib\security下

调用了其他Java 函数库的程序,在编译阶段没有问题,可是却无法执行,显示ClassNotFoundException的原因可能是:
在system32和jdk\bin目录下都有java.exe 而 javac.exe 只有在jdk\bin目录下有
javac.exe 会自动调用JDK所在目录下的那套JRE ,因此在编译时JVM会找到函数库,所以编译不会发生问题,
但在执行时,键入java xxx的时候会优先执行 system32 下的java.exe
因此会自动调用program files目录下的那套JRE(稍后解释)
所以要执行就必须把外部jar文件放到相应jre\lib\ext目录下

JDK\jre\bin\下有两个目录 server,client
两个目录下都会有jvm.dll
client目录下的jvm.dll较小
server目录下的较大
-----------------------
系统默认path
C:\Documents\Administrator>set path
Path=C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH

在system32目录 底下找不到JRE 目录,在c:\windows目录 也找不到JRE 目录的情况下,根据下一个逻辑,就是去查询注册表
C:\Program Files\Java\jre1.xx 该目录下的bin 子目录却只有看到client 子目录,却没有看到server 子目录。这就是为何在一开始执行
java -server -version会报错的原因
------------------------------------

============================
============================
JAVA 类加载器
============================
============================

有了动态性,我们的应用程序就可以在不用全盘重新编译的情况下更新系统,或者在不用停止主程序运作的情况下,除去系统中原有的bug,或者是增加原本不具备的新功能。
一般来说,常见的程序语言先天上并不具有动态性的本质,如C、C++本身就不具备动态性。因此,为了让这些本身不具有动态性的程序语言具有某种程度的动态性,
就必须依赖底层的操作系统提供一些机制来实现动态性,Windows 操作系统底下的动态联结函式库(Dynamic Linking Library) 和Unix 底下的共享对象(Share Object)

要运用这些底层操作系统所提供的机制必须多费一些功夫来撰写额外的程序代码(例如Windows 平台上需要使用 LoadLibrary() 与GetProcAddress() ,
两个Win32 API 来完成动态性的需求),这些额外撰写的程序代码也会因为作业平台的不同而不同,毕竟这些额外的程序代码与程序本身的运作逻辑甚少关联,
所以维护起来虽不算复杂,但仍有其难度。

每个类对java机来说,都是一个独立的动态联结函数库,只不过扩展名不是.dll或.so 而是.class
所以可以在不重新编译其他java程序代码的情况下,只修改需要修改的执行单位,并放入文件系统中,等下次该java虚拟机重新启动时,
这个逻辑上的Java应用程序就会因为加载了新修改的.class文件,自己的功能也做了更新。这是一个最基本的动态性功能。

JSP/Servlet之类的Web Container或者高档的Application Server 里的EJB Container他们会提供一个Hot Deployment功能。
即在不关闭Web Server的情况下,放入已编译好的新Servlet以取代旧的Servlet,下次Http request时,就会自动释放旧的servlet而重载新的servlet。

程序运行时需要的核心类库位于 jre\lib\rt.jar中
类加载器的作用就是把类从表态的硬盘 .class文件,复制一份到内存中,并做一此 始化工作
java.exe就是利用几个原则找到JRE后,把.class直接转交给JRE运行后便功成身退

public class test
{
public static void main(String[] args)
{
System.out.println("Hello DD");
}
}

javac test.java
java -verbose:class test

------------------------------------------------------

classloader的两种载入方式:
1)pre-loading预先载入,载入基础类
2)load-on-demand按需求载入
只有实例化一个类时,该类才会被classloader载入,仅仅申明并不会载入

基础类库是预先加载的(pre-loading)

用户所写的一般类是按需加载的(load-on-demand)

按需加载

三个类
public class A
{
public void print()
{
System.out.println("Using Class A");
}
}

public class B
{
public void print()
{
System.out.println("Using Class B");
}
}

public class Main
{
public static void main(String[] args)
{
A a=new A();
B b;
a.print();
}
}

javac *.java
java -verbose:class Main


[Loaded Main from file:/C:/]
[Loaded A from file:/C:/]
Using Class A
[Loaded java.lang.Shutdown from shared objects file]
[Loaded java.lang.Shutdown$Lock from shared objects file]


没有看到
[Loaded Main from file:/C:/]
[Loaded A from file:/C:/]
[Loaded B from file:/C:/]

--------------------------------------------
动态加载的例子
三个类
public class Word
{
public void start()
{
System.out.println("Word start");
}
}

public class Excel
{
public void start()
{
System.out.println("Excel start");
}
}
public class Office
{
public static void main(String[] args)
{
if(args.length!=1)
{
return ;
}
if(args[0].equals("Word"))
{
Word w=new Word();
w.start();
}else if(args[0].equals("Excel"))
{
Excel e=new Excel();
e.start();
}
}
}

依需求加载的优点是节省内存,但是仍有其缺点。举例来说,当程序第一次用到该类别的时候,系统就必须花一些额外的时间来加载该类别,使得整体执行效能受到影响,
尤其是由数以万计的类别所构成的Java 程序。可是往后需要用到该类别时,由于类别在初次加载之后就会被永远存放在内存之中,直到Java 虚拟机关闭,
所以不再需要花费额外的时间来加载。

总的来说,就弹性上和速度上的考虑,如此的设计所带来的优点(弹性和省内存)远超过额外加载时间的花费(只有第一次用到时),因此依需求加载的设计是明智的选择。


如果我们新增了Access.java 和PowerPoint.java 这两个新类别时,
Office.java 里的主程序就必须增加两个if … else 的循环
那么如何来更好的展示java在可扩展性的优势呢

----------------------------------------------------------
使JAVA程序更有动态性的方法有两种

1)implicit隐式,即利用实例化才载入的特性来动态载入class
2)explicit显式方式,又分两种方式:
1)java.lang.Class的forName()方法
2)java.lang.ClassLoader的loadClass()方法

隐式的:new关键字 生成类的实例

第一种方法: Class.forName() 加载类
一个接口
public interface Assembly
{
public void start() ;
}
三个类
public class Office
{
public static void main(String args[]) throws Exception
{
Class c = Class.forName(args[0]) ;
/*Object o = c.newInstance() ;
Assembly a = (Assembly) o ;*/
Assembly a = (Assembly) c.newInstance();
a.start() ;
}
}

public class Word implements Assembly
{
public void start()
{
System.out.println("Word Start")
}
}

public class Excel implements Assembly
{
public void start()
{
System.out.println("Excel Start")
}
}
--------------------------------------------

有两个forName()方法,一个是只有一个参数的(就是之前程序之中所使用的):
public static Class forName(String className)
另外一个是需要三个参数的:
public static Class forName(String name, boolean initialize,ClassLoader loader)
这两个方法,最后都是连接到原生方法
forName0(),
其宣告如下:
private static native Class forName0(String name,boolean initialize, ClassLoader loader) throws ClassNotFoundException;
只有一个参数的forName()方法,最后调用的是:
forName0(className, true,ClassLoader.getCallerClassLoader());
而具有三个参数的forName()方法,最后调用的是:
forName0(name, initialize, loader);

关于名为loader 这个参数的用法
public class Office
{
public static void main(String args[]) throws Exception
{
Class c = Class.forName(args[0],true,null) ; //line
/*Object o = c.newInstance() ;
Assembly a = (Assembly) o ;*/
Assembly a = (Assembly) c.newInstance();
a.start() ;
}
}

C:\>java Office Excel
Exception in thread "main" java.lang.ClassNotFoundException: Excel
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:242)
at Office.main(Office.java:9)
//line前加上一行,再修改改为
Office o=new Office();
Class c=Class.forName(args[0],true,o.getClass().getClassLoader());


最终代码为
public class Office
{
public static void main(String args[]) throws Exception
{
Office o=new Office();
Class c = Class.forName(args[0],true,o.getClass().getClassLoader()) ;
Assembly a = (Assembly) c.newInstance();
a.start() ;
}
}
就可以运行了

只有一个参数的forName() 方法, 由于在内部使用了ClassLoader.getCallerClassLoader() 来取得加载呼叫他的类别所使用的类别加载器,
和我们自己写的程序有相同的效用。( 注意,ClassLoader.getCallerClassLoader()是一个private 的方法,所以我们无法自行叫用,
因此必须要自己产生一个Office 类别的实例,再去取得加载Office 类别时所使用的类别加载器)。


三个参数的 Class.forName()的第二参数
给类添加一个静态代码块
public class Word implements Assembly
{
static {
System.out.println("Word Static Initialization");
}
public void start()
{
System.out.println("Word start");
}
}

public class Office
{
public static void main(String args[]) throws Exception
{
Office o=new Office();
System.out.println("类准备载入");
//Class c=Class.forName(args[0],true,o.getClass().getClassLoader());
Class c=Class.forName(args[0],false,o.getClass().getClassLoader());
System.out.println("准备实例化");
Assembly a1=(Assembly)c.newInstance();
a1.start();
Assembly a2=(Assembly)c.newInstance();
a2.start();
}
}

为true 时
C:\>java Office Word
类准备载入
Word Static Initialization
准备实例化
Word start
Word start
为false时
C:\>java Office Word
类准备载入
准备实例化
Word Static Initialization
Word start
Word start

静态初始化区块是在类别第一次被实例化的时候才会被呼叫那仅仅一次

注意:
不管您使用的是new 来产生某类别的实例、或是使用只有一个参数的forName()方法,内部都隐含了”加载类别+呼叫静态初始化区块”的动作。
而使用具有三个参数的orName()方法时,如果第二个参数给定的是false,那么就只会命令类别加载器加载该类别,但不会叫用其静态初始化区块,
只有等到整个程序第一次实例化某个类别时,静态初始化区块才会被叫用

static块在什么时候执行?
1)当调用forName(String)载入class时执行,如果调用ClassLoader.loadClass并不会执行.forName(String,false,ClassLoader)时也不会执行.
2)如果载入Class时没有执行static块则在第一次实例化时执行.比如new ,Class.newInstance()操作
3)static块仅执行一次



public class Office
{
public static void main(String args[]) throws Exception
{
Office o=new Office();
System.out.println("类准备载入");
ClassLoader loader=o.getClass().getClassLoader();
Class c=loader.loadClass(args[0]);
System.out.println("准备实例化");
Assembly a1=(Assembly)c.newInstance();
a1.start();
Assembly a2=(Assembly)c.newInstance();
a2.start();
}
}
------------------------------------------------

第二种:直接使用ClassLoader 类别的loadClass() 方法来加载类
此方式只会把类加载至内存,并不会调用该类别的静态初始化区块,而必须等到第一次实例化该类别时,该类别的静态初始化区块才会被叫用。
这种情形与使用Class 类别的forName()方法时,第二个参数传入false 几乎是相同的结果。
另一种方式
public class Office
{
public static void main(String args[]) throws Exception
{
Office o=new Office();
Class co=Office.class;
System.out.println("类准备载入");
ClassLoader loader=co.getClassLoader();
Class c=loader.loadClass(args[0]);
System.out.println("准备实例化");
Assembly a1=(Assembly)c.newInstance();
a1.start();
Assembly a2=(Assembly)c.newInstance();
a2.start();
}
}

归纳
1.
Office o=new Office();
ClassLoader loader=o.getClass().getClassLoader();
调用对象的getClass()方式取得该对象的引用,再调用该引用的getClassLoader()方法取得该对象类加载器的引用
2.
Office o=new Office();
Class co=Office.class;
ClassLoader loader=co.getClassLoader();
直接定义一个此对象类的.class 类引用然后由此对象的getClassLoader()方法取得该对象类加载器的引用

然后 Class c=loader.loadClass(args[0]);



Class类的实例.
>>Class类无法手工实例化,当载入任意类的时候自动创建一个该类对应的Class的实例,
>>某个类的所有实例内部都有一个栏位记录着该类对应的Class的实例的位置.,
>>每个java类对应的Class实例可以当作是类在内存中的代理人.所以当要获得类的信息(如有哪些类变量,有哪些方法)时,都可以让类对应的Class的实例代劳。
java的Reflection机制就大量的使用这种方法来实现
>>每个java类都是由某个classLoader(ClassLoader的实例)来载入的,因此Class类别的实例中都会有栏位记录他的ClassLoader的实例,如果该栏位为null,
则表示该类别是由bootstrap loader载入的(也称root laoder),bootstrap loader不是java所写成,所以没有实例.

-------------------------------------------------------
自己建立类别加载器来加载类别
利用Java 本身提供的java.net.URLClassLoader

如实例化一个URLClassLoader. URLClassLoader ucl = new URLClassLoader(new URL[]{new URL("file:/e:/bin/")}),URLClassLoader优先找当前目录,再在url中找.class加载.URL中别忘在最后加"/"表示目录


import java.net.*;
public class Office
{
public static void main(String[] args) throws Exception
{
if(args.length!=1)
{
return ;
}else {
URL u=new URL("http://share/");
URLClassLoader ucl=new URLClassLoader(new URL[]{u});
Class c=ucl.loadClass(args[0]);
Assembly asm=(Assembly)c.newInstance();
asm.start();
}
}
}
------------------------------------------------
import java.net.*;
public class Office
{
public static void main(String[] args) throws Exception
{
URL u = new URL("Http://share/") ;
URLClassLoader ucl = new URLClassLoader(new URL[]{ u }) ;
Class c = ucl.loadClass(args[0]) ;
Assembly asm = (Assembly) c.newInstance() ;
asm.start() ;
URL u1 = new URL("Http://share/") ;
URLClassLoader ucl1 = new URLClassLoader(new URL[]{ u1 }) ;
Class c1 = ucl1.loadClass(args[0]) ;
Assembly asm1 = (Assembly) c1.newInstance() ;
asm1.start() ;
System.out.println(Office.class.getClassLoader()) ;
System.out.println(u.getClass().getClassLoader()) ;
System.out.println(ucl.getClass().getClassLoader()) ;
System.out.println(c.getClassLoader()) ;
System.out.println(asm.getClass().getClassLoader()) ;
System.out.println(u1.getClass().getClassLoader()) ;
System.out.println(ucl1.getClass().getClassLoader()) ;
System.out.println(c1.getClassLoader()) ;
System.out.println(asm1.getClass().getClassLoader()) ;
}
}
C:\>java Office Word
Word Static Initialization
Word start
Word start
sun.misc.Launcher$AppClassLoader@82ba41
null
null
sun.misc.Launcher$AppClassLoader@82ba41
sun.misc.Launcher$AppClassLoader@82ba41
null
null
sun.misc.Launcher$AppClassLoader@82ba41
sun.misc.Launcher$AppClassLoader@82ba41

Office.class 由AppClassLoader( 又称做System Loader,系统加载器)所加载,URL.class 与URLClassLoader.class 由Bootstrap Loader 所加载
(注意:输出null 并非代表不是由类别加载器所载入。
在Java 之中,所有的类别都必须由类别加载器加载才行,只不过Bootstrap Loader 并非由Java所撰写而成,
而是由C++ 制作而成,因此以Java 的观点来看,逻辑上并没有Bootstrap Loader 的类别实例) 。而Word.class 分别由两个不同的URLClassLoader 实例加载。
至于Assembly.class , 本身应该是由
AppClassLoader 加载,但是由于多型(Polymorphism) 的关系,所指向的类别实例(Word.class) 由特定的加载器所加载,
导致打印在屏幕上的内容是其所参考的类别实例之类别加载器。Interface 这种型态本身无法直接使用new 来实例化,所以在执行getClassLoader() 的时候,
调用的一定是所参考的类别实例的
getClassLoader() ,要知道Interface 本身由哪个类别加载器加载,您必须使用底下程序代码:
Assembly.class.getClassLoader()

-----------------------------------------------
-----------------------------------------------
一切都是由Bootstrap Loader 开始 : 类别加载器
-----------------------------------------------
-----------------------------------------------

当我们在命令行输入java xxx.class 的时候,java.exe 根据我们之前所提过的逻辑找到了JRE(Java Runtime Environment) ,
接着找到位在JRE 之中的jvm.dll( 真正的Java 虚拟机),最后加载这个动态联结函式库,启动Java 虚拟机。
虚拟机一启动,会先做一些初始化的动作,比方说抓取系统参数等。一旦初始化动作完成之后,就会产生第一个类别加载器,
即所谓的Bootstrap Loader,Bootstrap Loader 是由C++ 所撰写而成(所以前面我们说,以Java的观点来看,逻辑上并不存在Bootstrap Loader 的类别实例,
所以在Java 程序代码里试图印出其内容的时候,我们会看到的输出为null),这个Bootstrap Loader所做的初始工作中,
除了也做一些基本的初始化动作之外,最重要的就是加载定义在sun.misc 命名空间底下的Launcher.java 之中的ExtClassLoader
( 因为是inner class ,所以编译之后会变成Launcher$ExtClassLoader.class) ,
并设定其Parent 为null,代表其父加载器为Bootstrap Loader 。然后Bootstrap Loader ,
再要求加载定义于sun.misc 命名空间底下的Launcher.java 之中的AppClassLoader( 因为是inner class,所以编译之后会变成
Launcher$AppClassLoader.class) ,并设定其Parent 为之前产生的ExtClassLoader 实例。
这里要请大家注意的是,Launcher$ExtClassLoader.class与Launcher$AppClassLoader.class 都是由Bootstrap Loader 所加载,
所以Parent 和由哪个类别加载器加载没有关系

public class test{
public static void main(String args[]) {
ClassLoader cl = test.class.getClassLoader() ;
System.out.println(cl) ;
ClassLoader cl1 = cl.getParent() ;
System.out.println(cl1) ;
ClassLoader cl2 =
cl1.getParent() ;
System.out.println(cl2) ;
}
}

C:\>java test
sun.misc.Launcher$AppClassLoader@82ba41
sun.misc.Launcher$ExtClassLoader@923e30
null

AppClassLoader 和ExtClassLoader 都是URLClassLoader 的子类别。由于它们都是URLClassLoader 的子类别,所以它们也应该有URL 作为搜寻类别档的参考,
由原始码中我们可以得知,AppClassLoader 所参考的URL 是从系统参数java.class.path 取出的字符串所决定,而java.class.path 则是由我们在执行java.exe 时,
利用 –cp 或-classpath 或CLASSPATH 环境变量所决定。


在预设情况下,AppClassLoader的搜寻路径为”.”( 目前所在目录),如果使用-classpath 选项(与-cp 等效),就可以改变AppClassLoader 的搜寻路径,
如果没有指定-classpath 选项,就会搜寻环境变量CLASSPATH 。如果同时有CLASSPATH 的环境设定与-classpath 选项,则以-classpath 选项的内容为主,
CLASSPATH 的环境设定与-classpath 选项两者的内容不会有加成的效果。至于ExtClassLoader 也有相同的情形,不过其搜寻路径是参考系统参数java.ext.dirs

Bootstrap Loader ,我们可以经由查询由系统参数
sun.boot.class.path 得知Bootstrap Loader 用来搜寻类别的路径

java -Dsun.boot.class.path=

请回头看到java.class.path 与sun.boot.class.path,也就是说,AppClassLoader 与Bootstrap Loader 会搜寻它们所指定的位置(或JAR 文件),如果找不到就找不到了,
AppClassLoader 与Bootstrap Loader 不会递归式地搜寻这些位置下的其他路径或其他没有被指定的JAR 文件。反观ExtClassLoader,所参考的系统参数是java.ext.dirs,
意思是说,他会搜寻底下的所有JAR 文件以及classes 目录,作为其搜寻路径(所以您会发现上面我们在测试的时候,如果加入-Dsun.boot.class.path=c:\windows 选项时,
程序的起始速度会慢了些,这是因为c:\windows 目录下的文件很多,必须花额外的时间来列举JAR 文件)。


在命令行下参数时,使用–classpath / -cp / 环境变量CLASSPATH 来更改AppClassLoader 的搜寻路径,或者用 –Djava.ext.dirs 来改变ExtClassLoader的搜寻目录,
两者都是有意义的。可是用–Dsun.boot.class.path 来改变Bootstrap Loader 的搜寻路径是无效。这是因为AppClassLoader
与ExtClassLoader 都是各自参考这两个系统参数的内容而建立,当您在命令行下变更这两个系统参数之后,AppClassLoader 与ExtClassLoader
在建立实例的时候会参考这两个系统参数,因而改变了它们搜寻类别文件的路径;而系统参数sun.boot.class.path 则是默认与Bootstrap Loader 的搜寻路径相同,
就算您更改该系统参与,与BootstrapLoader 完全无关

AppClassLoader 与ExtClassLoader 在整个虚拟机之中只会存有一份,一旦建立了,其内部所参考的搜寻路径将不再改变,
也就是说,即使我们在程序里利用System.setProperty() 来改变系统参数的内容,仍然无法更动AppClassLoader 与
ExtClassLoader 的搜寻路径。因此,执行时期动态更改搜寻路径的设定是不可能的事情。


-------------------------------------------
委拖模型
Bootstrap Loader 所做的初始工作中,除了也做一些基本的初始化动作之外,,最重要的就是加载定义在sun.misc 命名空间底下的Launcher.java 之中的
ExtClassLoader,并设定其Parent 为null,然后Bootstrap Loader 再加载定义在sun.misc 命名空间底下的Launcher.java 之中的AppClassLoader,
并设定其Parent 为之前产生的ExtClassLoader实例。

类加载器有加载类别的需求时,会先请示其Parent 使用其搜寻路径帮忙加载,如果Parent 找不到,那么才由自己依照自己的搜寻路径搜寻类别

两个类
public class test{
public static void main(String args[]) {
System.out.println(test.class.getClassLoader()) ;
testlib tl = new testlib() ;
tl.print() ;
}
}

public class testlib{
public void print() {
System.out.println(this.getClass().getClassLoader()) ;
}
}
将这两个源文件编译成.class文件以后 复制两份
分别至于\classes 底下
( 注意, 您的系统下应该没有此目录, 您必须自己建立) 与\lib\ext\classes(注意,您的系统下应该没有此目录,您必须自己建立)底下,


C:\Program Files\Java\jdk1.5.0_11\jre\classes
C:\Program Files\Java\jdk1.5.0_11\jre\lib\ext\classes
c:\myclasses

测试一:
\classes 底下test.class testlib.class
\lib\ext\classes 底下test.class testlib.class
c:\myclasses 底下test.class testlib.class

结果:
C:\myclasses>java test
null
null

从输出我们可以看出,当AppClassLoader 要加载test.class 时,先请其Parent,也就是ExtClassLoader 来载入,而ExtClassLoader 又请求其Parent,即Bootstrap Loader
来载入test.class 。由于\classes 目录为Bootstrap Loader 的搜寻路径之一,所以Bootstrap Loader 找到了test.class ,因此将它加载。
接着在test.class之内有加载testlib.class 的需求,由于test.class 是由Bootstrap Loader 所加载,所以testlib.class 内定是由Bootstrap Loader
根据其搜寻路径来寻找,因为testlib.class 也位于Bootstrap Loader 可以找到的路径下,所以也被加载了。
最后我们看到test.class 与testlib.class 都是由Bootstrap Loader(null) 载入。

测试二:
\classes 底下test.class
\lib\ext\classes 底下test.class testlib.class
c:\myclasses 底下test.class testlib.class

结果:
C:\myclasses>java test
null
Exception in thread "main" java.lang.NoClassDefFoundError: testlib
at test.main(test.java:4)

从输出我们可以看出,当AppClassLoader 要加载test.class 时,先请其Parent,也就是ExtClassLoader 来载入,而ExtClassLoader 又请求其Parent,即Bootstrap Loader
来载入test.class 。由于\classes 目录为Bootstrap Loader 的搜寻路径之一,所以Bootstrap Loader 找到了test.class ,因此将它加载。
接着在test.class 之内有加载testlib.class 的需求,由于test.class是由Bootstrap Loader 所加载,所以testlib.class 内定是由Bootstrap Loader
根据其搜寻路径来寻找,但是因为Bootstrap Loader 根本找不到testlib.class( 被我们删除了),而Bootstrap Loader 又没有Parent,所以无法加载testlib.clss 。
输出告诉我们,test.class 由Bootstrap Loader 加载,且最后印出的讯息是NoClassDefFoundError,代表无法加载testlib.class 。这个问题没有比较简单的方法能够解决,
但是仍然可以透过较复杂的Context Class Loader 来解决

测试三:
\classes 底下testlib.class
\lib\ext\classes 底下test.class testlib.class
c:\myclasses 底下test.class testlib.class

结果:
C:\myclasses>java test
sun.misc.Launcher$ExtClassLoader@7259da
null

从输出我们可以看出,当AppClassLoader 要加载test.class 时,先请其Parent,也就是ExtClassLoader 来载入,而ExtClassLoader 又请求其Parent,即Bootstrap Loader
来载入test.class 。但是Bootstrap Loader 无法在其搜寻路径下找到test.class( 被我们删掉了),所以ExtClassLoader 只得自己搜寻。因此ExtClassLoader在其搜寻路径
\lib\ext\classes底下找到test.class ,因此将它加载。接着在test.class 之内有加载testlib.class 的需求,由于test.class 是由ExtClassLoader 所加载,
所以testlib.class 内定是由ExtClassLoader 根据其搜寻路径来寻找,但是因为ExtClassLoader 有Parent,所以要先由Bootstrap Loader 先帮忙寻找,testlib.class
位于Bootstrap Loader可以找到的路径下,所以被Bootstrap Loader 加载了。最后我们看到test.class 由ExtClassLoader 载入,而testlib.class 则是由
Bootstrap Loader(null)载入。

测试四:
\classes 底下
\lib\ext\classes 底下test.class testlib.class
c:\myclasses 底下test.class testlib.class

结果:
C:\myclasses>java test
sun.misc.Launcher$ExtClassLoader@7259da
sun.misc.Launcher$ExtClassLoader@7259da

从输出我们可以看出,当AppClassLoader 要加载test.class 时,先请其Parent,也就是ExtClassLoader 来载入,而ExtClassLoader 又请求其Parent,即Bootstrap Loader
来载入test.class 。但是Bootstrap Loader 无法在其搜寻路径下找到test.class( 被我们删掉了),所以ExtClassLoader 只得自己搜寻。因此ExtClassLoader在其搜寻路径
\lib\ext\classes底下找到test.class ,因此将它加载。接着在test.class 之内有加载testlib.class 的需求,由于test.class 是由ExtClassLoader 所加载,
所以testlib.class 内定是由ExtClassLoader 根据其搜寻路径来寻找,ExtClassLoader 一样要请求其Parent先试着加载,但是Bootstrap Loader 根本找不到testlib.class
( 被我们删除了),所以只能由ExtClassLoader 自己来,ExtClassLoader 在其搜寻路径\lib\ext\classes 底下找到testlib.class ,因此将它加载。
最后我们看到test.class 与testlib.clas 都是由ExtClassLoader 载入。


测试五:
\classes 底下
\lib\ext\classes 底下test.class
c:\myclasses 底下test.class testlib.class

结果:

C:\myclasses>java test
sun.misc.Launcher$ExtClassLoader@7259da
Exception in thread "main" java.lang.NoClassDefFoundError: testlib
at test.main(test.java:4)

从输出我们可以看出,当AppClassLoader 要加载test.class 时,先请其Parent,也就是ExtClassLoader 来载入,而ExtClassLoader 又请求其Parent,即Bootstrap Loader
来载入test.class 。但是Bootstrap Loader 无法在其搜寻路径下找到test.class( 被我们删掉了),所以ExtClassLoader 只得自己搜寻。因此ExtClassLoader在其搜寻路径
\lib\ext\classes底下找到test.class ,因此将它加载。接着在test.class 之内有加载testlib.class 的需求,由于test.class 是由ExtClassLoader 所加载,
所以testlib.class 内定是由ExtClassLoader 根据其搜寻路径来寻找,ExtClassLoader 一样要请求其Parent先试着加载,但是Bootstrap Loader 根本找不到testlib.class
( 被我们删除了),所以只能由ExtClassLoader 自己来,ExtClassLoader 也无法在自己的搜寻路径中找到testlib.class ,所以产生错误讯息。输出告诉我们,test.class
由ExtClassLoader 加载,且最后印出的讯息是NoClassDefFoundError,代表无法加载testlib.class 。要解决问题,我们必须让ExtClassLoader 找的到testlib.class 才行,
所以请使用选项 –Djava.ext.dirs= 来指定,请注意,ExtClassLoader 只会自动搜寻底下的classes 子目录或是JAR 文件,
其他的子目录或其他类型的档案一概不管。此外,这个错误亦可以透过Context Class Loader 的技巧来解决。

测试六:
\classes 底下
\lib\ext\classes 底下testlib.class
c:\myclasses 底下test.class testlib.class

结果:
C:\myclasses>java test
sun.misc.Launcher$AppClassLoader@197d257
sun.misc.Launcher$ExtClassLoader@7259da

从输出我们可以看出,当AppClassLoader 要加载test.class 时,先请其Parent,也就是ExtClassLoader 来载入,而ExtClassLoader 又请求其Parent,即Bootstrap Loader
来载入test.class 。Bootstrap Loader 无法在其搜寻路径下找到test.class( 被我们删掉了) ,所以转由ExtClassLoader 来搜寻。ExtClassLoader 仍无法在其搜寻路径
\lib\ext\classes 底下找到test.class,最后只好由AppClassLoader 加载它自己在搜寻路径底下找到的test.class 。接着在test.class 之内有加载
testlib.class 的需求,由于test.class 是由AppClassLoader 所加载,所以testlib.class 内定是由AppClassLoader 根据其搜寻路径来寻找,AppClassLoader
一样要请求其Parent 先试着加载,但是Bootstrap Loader 根本找不到testlib.class( 被我们删除了),所以回头转由ExtClassLoader 来搜寻,
ExtClassLoader 在其搜寻路径\lib\ext\classes 底下找到testlib.class ,因此将它加载。最后我们看到test.class 由AppClassLoader 载入,
而testlib.class 则是由ExtClassLoader 载入。

测试七:
\classes 底下
\lib\ext\classes 底下
c:\myclasses 底下test.class testlib.class

结果:
C:\myclasses>java test
sun.misc.Launcher$AppClassLoader@197d257
sun.misc.Launcher$AppClassLoader@197d257

从输出我们可以看出,当AppClassLoader 要加载test.class 时,先请其Parent,也就是ExtClassLoader 来载入,而ExtClassLoader 又请求其Parent,即Bootstrap Loader
来载入test.class 。但是Bootstrap Loader 无法在其搜寻路径下找到test.class( 被我们删掉了), ExtClassLoader 只无法在其搜寻路径下找到test.class(
被我们删掉了),最后只好由AppClassLoader 加载它在自己搜寻路径底下找到的test.class 。接着在test.class 之内有加载testlib.class 的需求,由于test.class
是由AppClassLoader 所加载,所以testlib.class 内定是由AppClassLoader 根据其搜寻路径来寻找,但是Bootstrap Loader 根本找不到testlib.class( 被我们删除了) ,
ExtClassLoader 也找不到testlib.class( 被我们删除了) ,所以回头转由AppClassloader 来搜寻,AppClassLoader 在其搜寻路径底下找到testlib.class ,
因此将它加载。最后我们看到test.class 和testlib.class 都是由AppClassLoader 载入。



总结:
各个java类由哪些classLoader加载?
1)java类可以通过实例.getClass.getClassLoader()得知
2)接口由AppClassLoader(System ClassLoader,可以由ClassLoader.getSystemClassLoader()获得实例)载入
3)ClassLoader类由bootstrap loader载入


ClassLoader hierachy:
jvm建立->初始化动作->产生第一个ClassLoader,即bootstrap loader->bootstrap loader在sum.misc.Launcher类里面的ExtClassLoader,
并设定其Parent为null->bootstrap loader载入sun.misc.Launcher$AppClassLoader,并设定其parent为ExtClassLoader
(但是AppClassLoader也是由bootstrap loader所载入的)->AppClassLoader载入各个xx.class,xx.class也有可能被ExtclassLoader或者bootstrap loader载入.
>>自定义的ClassLoader的.getParent()是AppClassLoader.parent和他的加载器并没有关系
>>ExtClassLoader和AppClassLoader都是URLClassLoader的子类.AppClassLoader的URL是由系统参数java.class.path取出的字符串决定,而java.class.path由
运行java.exe时 的-cp或-classpath或CLASSPATH环境变量决定
>>ExtClassLoader查找的url是系统变量java.ext.dirs,java.ext.dirs默认为jdk\jre\lib\ext
>>Bootstrap loader的查找url是sun.boot.class.path
>>在程序运行后调用System.setProperty()来改变系统变量并不能改变以上加载的路径,因为classloader读取在System.setProperty之前.sun.boot.class.path
是在程序中写死的,完全不能修改


当classloader有类需要载入时先让其parent搜寻其搜寻路径帮忙载入,如果parent找不到,在由自己搜寻自己的搜寻路径载入,ClassLoader hierachy本来就有这种性质

NoClassDefFoundError和ClassNotFoundException
NoClassDefFoundError:当java源文件已编译成.class文件,但是ClassLoader在运行期间在其搜寻路径load某个类时,没有找到.class文件则报这个错
ClassNotFoundException:试图通过一个String变量来创建一个Class类时不成功则抛出这个异常


关于能提高Java代码可重用性的三个措施

luohg666收录,使用标签:Java,时间:2008-5-1 23:31:05 | 相关网摘我也收藏

本文介绍了三种修改现有代码提高其可重用性的方法,它们分别是:改写类的实例方法,把参数类型改成接口,选择最简单的参数接口类型。



措施一:改写类的实例方法

通过类继承实现代码重用不是精确的代码重用技术,因此它并不是最理想的代码重用机制。换句话说,如果不继承整个类的所有方法和数据成员,我们无法重用该类里面的单个方法。继承总是带来一些多余的方法和数据成员,它们总是使得重用类里面某个方法的代码复杂化。另外,派生类对父类的依赖关系也使得代码进一步复杂化:对父类的改动可能影响子类;修改父类或者子类中的任意一个类时,我们很难记得哪一个方法被子类覆盖、哪一个方法没有被子类覆盖;最后,子类中的覆盖方法是否要调用父类中的对应方法有时并不显而易见。

任何方法,只要它执行的是某个单一概念的任务,就其本身而言,它就应该是首选的可重用代码。为了重用这种代码,我们必须回归到面向过程的编程模式,把类的实例方法移出成为全局性的过程。为了提高这种过程的可重用性,过程代码应该象静态工具方法一样编写:它只能使用自己的输入参数,只能调用其他全局性的过程,不能使用任何非局部的变量。这种对外部依赖关系的限制简化了过程的应用,使得过程能够方便地用于任何地方。当然,由于这种组织方式总是使得代码具有更清晰的结构,即使是不考虑重用性的代码也同样能够从中获益。

在Java中,方法不能脱离类而单独存在。为此,我们可以把相关的过程组织成为独立的类,并把这些过程定义为公用静态方法。

例如,对于下面这个类:

class Polygon {

.

.

public int getPerimeter() {...}

public boolean isConvex() {...}

public boolean containsPoint(Point p) {...}

.

.

}

我们可以把它改写成:

class Polygon {

.

.

public int getPerimeter() {return pPolygon.computePerimeter(this);}

public boolean isConvex() {return pPolygon.isConvex(this);}

public boolean containsPoint(Point p) {return pPolygon.containsPoint(this, p);}

.

}

其中,pPolygon是:

class pPolygon {

static public int computePerimeter(Polygon polygon) {...}

static public boolean isConvex(Polygon polygon) {...}

static public boolean

containsPoint(Polygon polygon, Point p) {...}

}

从类的名字pPolygon可以看出,该类所封装的过程主要与Polygon类型的对象有关。名字前面的p表示该类的唯一目的是组织公用静态过程。在Java中,类的名字以小写字母开头是一种非标准的做法,但象pPloygon这样的类事实上并不提供普通Java类的功能。也就是说,它并不代表着一类对象,它只是Java语言组织代码的一种机制。

在上面这个例子中,改动代码的最终效果是使得应用Polygon功能的客户代码不必再从Polygon继承。Polygon类的功能现在已经由pPolygon类以过程为单位提供。客户代码只使用自己需要的代码,无需关心 Polygon类中自己不需要的功能。但它并不意味着在这种新式过程化编程中类的作用有所削弱。恰恰相反,在组织和封装对象数据成员的过程中,类起到了不可或缺的作用,而且正如本文接下来所介绍的,类通过多重接口实现多态性的能力本身也带来了卓越的代码重用支持。然而,由于用实例方法封装代码功能并不是首选的代码重用手段,所以通过类继承达到代码重用和多态性支持也不是最理想的。



措施二:把参数类型改成接口

正如Allen Holub在《Build User Interfaces for Object-Oriented Systems》中所指出的,在面向对象编程中,代码重用真正的要点在于通过接口参数类型利用多态性,而不是通过类继承:

“……我们通过对接口而不是对类编程达到代码重用的目的。如果某个方法的所有参数都是对一些已知接口的引用,那么这个方法就能够操作这样一些对象:当我们编写方法的代码时,这些对象的类甚至还不存在。从技术上说,可重用的是方法,而不是传递给方法的对象。”

在“措施一”得到的结果上应用Holub的看法,当某块代码能够编写为独立的全局过程时,只要把它所有类形式的参数改为接口形式,我们就可以进一步提高它的可重用能力。经过这个改动之后,过程的参数可以是实现了该接口的所有类的对象,而不仅仅是原来的类所创建的对象。由此,过程将能够对可能存在的大量的对象类型进行操作。

例如,假设有这样一个全局静态方法:

static public boolean contains(Rectangle rect, int x, int y) {...}

这个方法用于检查指定的点是否包含在矩形里面。在这个例子中,rect参数的类型可以从Rectangle类改变为接口类型,如下所示:

static public boolean contains(Rectangular rect, int x, int y) {...}

而Rectangular接口的定义是:

public interface Rectangular {Rectangle getBounds();}

现在,所有可以描述为矩形的类(即,实现了Rectangular接口的类)所创建的对象都可以作为提供给pRectangular.contains()的rect参数。通过放宽参数类型的限制,我们使方法具有更好的可重用性。

不过,对于上面这个例子,Rectangular接口的getBounds方法返回 Rectangle,你可能会怀疑这么做是否真正值得。换言之,如果我们知道传入过程的对象会在被调用时返回一个Rectangle,为什么不直接传入 Rectangle取代接口类型呢?之所以不这么做,最重要的原因与集合有关。让我们假设有这样一个方法:

static public boolean areAnyOverlapping(Collection rects) {...}

该方法用于检查给定集合中的任意矩形对象是否重叠。在这个方法的内部,当我们用循环依次访问集合中的各个对象时,如果我们不能把对象cast成为Rectangular之类的接口类型,又如何能够访问对象的矩形区域呢?唯一的选择是把对象cast成为它特有的类形式(我们知道它有一个方法可以返回矩形),它意味着方法必须事先知道它所操作的对象类型,从而使得方法的重用只限于那几种对象类型。而这正是前面这个措施力图先行避免的问题!



措施三:选择最简单的参数接口类型

在实施第二个措施时,应该选用哪一种接口类型来取代给定的类形式?答案是哪一个接口完全满足过程对参数的需求,同时又具有最少的多余代码和数据。描述参数对象要求的接口越简单,其他类实现该接口的机会就越大——由此,其对象能够作为参数使用的类也越多。从下面这个例子可以很容易地看出这一点:

static public boolean areOverlapping(Window window1, Window window2) {...}

这个方法用于检查两个窗口(假定是矩形窗口)是否重叠。如果这个方法只要求从参数获得两个窗口的矩形坐标,此时相应地简化这两个参数是一种更好的选择:

static public boolean areOverlapping(Rectangular rect1, Rectangular rect2) {...}

上面的代码假定Window类型实现了Rectangular接口。经过改动之后,对于任何矩形对象我们都可以重用该方法的功能。

有些时候可能会出现描述参数需求的接口拥有太多方法的情况。此时,我们应该在全局名称空间中定义一个新的公共接口供其他面临同一问题的代码重用。

当我们需要象使用C语言中的函数指针一样使用参数时,创建唯一的接口描述参数需求是最好的选择。例如,假设有下面这个过程:

static public void sort(List list, SortComparison comp) {...}

该方法运用参数中提供的比较对象comp,通过比较给定列表list中的对象排序list列表。sort对comp对象的唯一要求是要调用一个方法进行比较。因此,SortComparison应该是只带有一个方法的接口:

public interface SortComparison {

boolean comesBefore(Object a, Object b);

}

SortComparison接口的唯一目的在于为sort提供一个它所需功能的钩子,因此SortComparison接口不能在其他地方重用。

总而言之,本文三个措施适合于改造现有的、按照面向对象惯例编写的代码。这三个措施与面向对象编程技术结合就得到了一种可在以后编写代码时使用的新式代码编写技术,它能够简化方法的复杂性和依赖关系,同时提高方法的可重用能力和内部凝聚力。

当然,这里的三个措施不能用于那些天生就不适合重用的代码。不适合重用的代码通常出现在应用的表现层。例如,创建程序用户界面的代码,以及联结到输入事件的控制代码,都属于那种在程序和程序之间千差万别的代码,这种代码几乎不可能重用。


Java开发,重在整合-----Struts 2整合Spring(15)

luohg666收录,使用标签:Java, struts2, spring,时间:2008-4-29 12:07:51 | 相关网摘我也收藏

13.2 Struts 2整合Spring
Struts 2框架为整合其他技术提供了良好的可扩展性,可以通过插件的方式来实现同Spring技术的整合。

13.2.1

整合步骤

Struts 2框架整合Spring很简单,下面是整合的步骤。

(1)复制文件。复制struts2-spring-plugin-x-x-x.jar和spring.jar到WEB-INF/lib目录下。其中的x对应了Spring的版本号。还需要复制commons-logging.jar文件到WEB-INF/lib目录下。

(2)配置struts.objectFactory属性值。在struts.properties中设置struts.objectFactory属性值:

struts.objectFactory = spring

或者在XML文件中进行常量配置:







(3)配置Spring监听器。在web.xml文件中增加如下内容:



org.springframework.web.context.ContextLoaderListener



(4)Spring配置文件。默认情况下,Spring配置文件为applicationContext.xml,该文件需要保存在Web应用的WEB-INF目录下。内容示例如下所示:




"-//SPRING//DTD BEAN//EN"

"http://www.springframework.org/dtd/spring-beans.dtd">







开发者实际上可以使用多个Spring配置文件,在web.xml中进行下列设置,从而使Spring的ApplicationContext通过匹配所给定模式的文件来初始化对象:





contextConfigLocation

/WEB-INF/applicationContext-*.xml,classpath*:applicationContext-*.xml



(5)修改Struts配置文件。Struts 2框架整合Spring框架,需要在Struts配置文件中有所改变,下面是一个示例:


"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"

"http://struts.apache.org/dtds/struts-2.0.dtd">









foo.ftl









bar.ftl







该配置文件中定义了两个Action配置:foo是一个标准的Struts 2框架Action配置,指定了Action实现类为com.acme.Foo;bar对应的class并不存在,那么框架将在Spring配置文件中查找id属性为“bar”的定义,该配置文件如下所示:




"-//SPRING//DTD BEAN//EN"

"http://www.springframework.org/dtd/spring-beans.dtd">





...



13.2.2

整合原理

Struts2与Spring的集成要用到Spring插件包struts2-spring-plugin-x-x-x.jar,这个包是同Struts2一起发布的。Spring插件是通过覆盖(override)Struts2的ObjectFactory来增强核心框架对象的创建。当创建一个对象的时候,它会用Struts2配置文件中的class属性去和Spring配置文件中的id属性进行关联,如果能找到,则由Spring创建,否则由Struts 2框架自身创建,然后由Spring来装配。Spring插件具体有如下几个作用:

— 允许Spring创建Action、Interceptror和Result。

— 由Struts创建的对象能够被Spring装配。

— 如果没有使用Spring ObjectFactory,提供了2个拦截器来自动装配action。

★ 注意 ★

开发者不必在Spring中去注册action,尽管可以这么去做,通常Struts框架会自动地从action mapping中创建action对象。



struts2-spring-plugin-x-x-x.jar插件中有一个struts-plugin.xml文件,该文件内容如下所示:


"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"

"http://struts.apache.org/dtds/struts-2.0.dtd">

























其中设置了Struts 2框架常量struts.objectFactory的值为spring,实际上,spring是org.apache.struts2.spring.StrutsSpringObjectFactory类的缩写,默认情况下所有由Struts 2框架创建的对象都是由ObjectFactory实例化的,ObjectFactory提供了与其他IoC容器如Spring、Pico等集成的方法。覆盖这个ObjectFactory的类必须继承ObjectFactory类或者它的任何子类,并且要带有一个不带参数的构造方法。在这里用org.apache.struts2.spring.StrutsSpring ObjectFactory代替了默认的ObjectFactory。

如果Action不是使用Spring ObjectFactory创建的话,插件提供了两个拦截器来自动装配Action,默认情况下框架使用的自动装配策略是name,也就是说框架会去Spring中寻找与Action属性名字相同的bean,可选的装配策略还有:type、auto、constructor,开发者可以通过常量struts.objectFactory.spring.autoWire来进行设置。

★ 说明 ★

Struts 2框架整合Spring后,处理用户请求的Action并不是Struts框架创建的,而是由Spring插件创建的。创建实例时,不是利用配置Action时指定的class属性值,根据bean的配置id属性,从Spring容器中获得相应的实例。

13.3 整合开发示例


视频:Netbeans 6.0.1 中文版 +JBoss 4 EJB 开发 (15:37)

luohg666收录,使用标签:Java, ejb, jboss,时间:2008-4-29 12:06:13 | 相关网摘我也收藏

1. 下载Netbeans6

2. 安装Netbeans6

3. 下载和安装JBoss

4. 启动Netbeans,配置JBoss

5. 新建EJB项目

6. 开发并发布无状态会话Bean

7. 创建Java程序访问EJB

视频浏览地址:http://beansoft.java-cn.org/netbeans/netbeans6_jboss1/

今天试了试一个台湾产的在线教程制作软件,看情况还不错,适合在线播放,虽然需要客户端下载个小插件(不是病毒),不过生成的文件太大了,内容也比较多,而且不区分左右键点击这样的鼠标动作。下面是视频截图:


Jbuilder2006 MySQL5.0 Tomcat5.5.9数据源配置

luohg666收录,使用标签:Java,时间:2008-4-29 12:05:09 | 相关网摘我也收藏

Jbuilder2006+MySQL5.0+Tomcat5.5.9数据源配置(原创)

MationChen 2008-4-27

这些天在网上找了不少有关这方面的资料,对于JB自带的猫(5.5.9)+MySQL5.0配置数据源总结了方法如下:

第一步,创建JB工程myApp,其web Module 名为 myWeb,并在其下创建DBConntest.jsp作为测试之用。

第二步,创建MySQL数据库,数据库名为mytest,在数据库中建立数据库表test,并创建测试用字段test1,test2,写入测试用数据

语句如下:

/*

MySQL Data Transfer

Source Host: localhost

Source Database: mytest

Target Host: localhost

Target Database: mytest

Date: 2008-4-27 11:52:21

*/



SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------

-- Table structure for test

-- ----------------------------

CREATE TABLE `test` (

`test1` varchar(255) default '',

`test2` varchar(255) default ''

) ENGINE=InnoDB DEFAULT CHARSET=gbk;



-- ----------------------------

-- Records

-- ----------------------------

第三步,运行JB中的DBConntest.jsp页面,目的是在工程文件夹下生成web.xml和

server8083.xml(如果猫的端口是8080则生成server8080.xml)



其中,server8083.xml文件位置在工程文件夹下面myApp\tomcat\conf目录下,笔者的工程在 D:\work\myApp,则文件位置就应该在D:\work\myApp\Tomcat\conf目录下。

而web.xml则在myApp\myWeb\WEB-INF\目录下,也可以在JB的工程中找到web.xml。



以上三步是基本的准备工作,接下来就要开始进行联接啦!

第四步,是mysql驱动,笔者用的是mysql-connector-java-5.0.8-bin.jar,读者可以在网上自己下载。

驱动应该放在JB安装目录下面的\thirdparty\jakarta-tomcat-5.5.9\common\lib下边,就是说要把驱动给拷到\thirdparty\jakarta-tomcat-5.5.9\common\lib文件夹下。



第五步,修改xml文件,这一步是最为关键的,要不然就够你受的啦!



首先,记住,是首先。也就是说是第一步,网上都说这一步在修改sever.xml之后,我觉得不然,应该是这一步在前!

应该在web.xml文件中的 中间添加:



db conn

jdbc/myWeb

javax.sql.DataSource

Container





解释一下,jdbc/myWeb

res-ref-name填写的内容是JNDI Name名称,应与server8083.xml以及程序中的lookup("jdbc/myWeb")保持一致。

其他的,description是描述,可以自己写,而res-type,res-auth则按默认即可。



然后,修改之前提到的server8083.xml,文件如下图所示



看到灰色部分了吧?红圈中的那些,记住,一定要删除!

即删除如下文字



这一段的意大概就是说这个文件是JB自己生成的,把这一段注释删除就能防止JB自己修改那个文件!所以一定要删除的



然后便在中间添加如下内容:





完成之后文件如下

Server8083.xml































最后,建立测试页面

DBConntest.jsp



<%@ page contentType="text/html; charset=GBK"

import="java.sql.*,javax.sql.*,javax.naming.*" %>

<%request.setCharacterEncoding("GBK"); %>







DBConntest









mySql联接测试



<%

out.print("测试开始!+");

DataSource ds=null;

try{

InitialContext initCtx=new InitialContext();

Context ctx = (Context) initCtx.lookup("java:comp/env");

ds=(DataSource)ctx.lookup("jdbc/myWeb");

//"jdbc/myWeb"对应web.xml以及server8083.xml文件中的JNDI Name

Connection conn=ds.getConnection();

Statement stmt=conn.createStatement();

String strSql="select * from test";

ResultSet rs=stmt.executeQuery(strSql);

while(rs.next()){

out.print(rs.getString(1));

out.print("");

out.print(rs.getString(2));

out.print("");

}

out.print("测试完成!");

}catch(Exception e){

e.printStackTrace();

}

%>







在浏览器中输入:http://localhost:8083/myWeb/DBConntest.jsp



运行页面,可以得到如下结果:





这样,配置就算是大功告成啦!还想再提一下,本文与网上许多文章不同的地方就是强调一定要先配置web.xml,再修改server8083.xml,这是笔者经过好几天实践所得出的结论^_^



调试过程中可能会遇到一些异常

异常一:Cannot create JDBC driver of class '' for connect URL 'null'

解决方法:

中间添加



异常二:javax.naming.NameNotFoundException: Name XXX is not bound in this Context

解决方法:

网上对于这个异常的解决方法是在Web.xml中添加



db conn

jdbc/myWeb

javax.sql.DataSource

Container



但是刚形如笔者和许多网友一样按此方法并不能解决此问题,经过几天的摸索,笔者觉得是在配置过程中的web.xml和server808.xml顺序问题,按照笔者的方法配置去配置并没有出现这个异常,大家不妨试试^_^

(PS:网上有一篇对于mysql联接过程中出现在异常进行总结的文章分析得详细,大家也可以参考参考:http://neonlight.bokee.com/5517972.html)


HTML编辑器FCKeditor使用详解

luohg666收录,使用标签:Java, html,时间:2008-4-22 16:41:49 | 相关网摘我也收藏



本文介绍FCKeditor在Java环境下的使用方法。

一、简介
功能:所见即所得,支持图片和Flash,工具栏可自由配置,使用简单
兼容性:IE 5.5+、Firefox 1.5+、Safari 3.0+、Opera 9.50+、Netscape 7.1+、 Camino 1.0+
成熟度:使用广泛,被Baidu、CSDN等选用

二、下载

官方下载首页:http://www.fckeditor.net/download/,当前版本为2.5.1
需要下载FCKeditor 2.5.1(FCKeditor_2.5.1.zip)和FCKeditor.Java(FCKeditor-2.3.zip)

三、部署

本例以WebRoot作为应用根路径,部署后的目录结构如下图所示:



1、FCKeditor_2.5.1.zip解压,将fckeditor文件夹复制到/WebRoot/下

2、FCKeditor-2.3.zip解压,将commons-fileupload.jar和FCKeditor-2.3.jar复制到/WebRoot/WEB-INF/lib/下

3、修改/WebRoot/WEB-INF/web.xml文件,增加以下内容:
Connector
com.fredck.FCKeditor.connector.ConnectorServlet

baseDir
/UserFiles/


debug
true

1



SimpleUploader
com.fredck.FCKeditor.uploader.SimpleUploaderServlet

baseDir
/UserFiles/


debug
true


enabled
true


AllowedExtensionsFile



DeniedExtensionsFile
php|php3|php5|phtml|asp|aspx|ascx|jsp|cfm|cfc|pl|bat|exe|dll|reg|cgi


AllowedExtensionsImage
jpg|gif|jpeg|png|bmp


DeniedExtensionsImage