2006年2月18日
#
--sunfruit
在Linux Enterprise 5安装Oracle的时候有时候会报错例如“makefile '/oracle/product/10.2.0/db_1/rdbms/lib/ins_rdbms.mk' 的目标 'all_no_orcl' 时出错”
经过多次测试是缺少相关的系统包文件,至少我的环境中确实是这样,只要是安装了下面的包文件,这个错误可以避免(版本号在不同环境中略有不同)
compat-libstdc++-33-3.2.3-61
compat-libstdc++-296-2.96-138
gcc-4.1.1-52.el5
gcc-c++-4.1.1-52.el5
glibc-2.5-12
glibc-common-2.5-12
glibc-devel-2.5-12
glibc-headers-2.5-12
libgcc-4.1.1-52.el5
make-3.81-1.1
binutils-2.17.50.0.6-2.el5
openmotif-2.3.0-0.3.el5
以上的安装包由于在安装Linux的选择的包以及具体的环境不同可能,可能有些不需要,不过如果出现了ins_rdbms.mk的错误,可以按照这个方法尝试一下
注:以上的安装在Linux Enterprise 5的安装盘中都包含,不需要额外在网上下载
-sunfruit
如果在安装Oracle10g的时候没有选择字符集,则按照下面的3部进行字符集的修改就可以完全正常的使用Oracle10g(注:关于下面的1、2、3均是转贴,后面添加了个人在使用的时候一些心得)
1、检查服务器上Oracle数据库的字符集
sqlplus /nolog
SQL> connect / as sysdba
连接成功.
SQL> desc props$
列名 可空值否 类型
------------------------------- -------- ----
NAME NOT NULL VARCHAR2(30)
VALUE$ VARCHAR2(2000)
COMMENT$ VARCHAR2(2000)
SQL> col value$ format a40
SQL> select name,value$ from props$;
NAME VALUE$
------------------------------ -------------------------
DICT.BASE 2
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_CURRENCY $
NLS_ISO_CURRENCY AMERICA
NLS_NUMERIC_CHARACTERS .,
NLS_DATE_FORMAT DD-MON-YY
NLS_DATE_LANGUAGE AMERICAN
NLS_CHARACTERSET ZHS16GBK
NLS_SORT BINARY
NLS_CALENDAR GREGORIAN
NLS_RDBMS_VERSION 7.3.4.0.0
GLOBAL_DB_NAME ORACLE.WORLD
EXPORT_VIEWS_VERSION 3
NLS_CHARACTERSET和NLS_CHAR_CTERSET这个参数应该是ZHS16GBK,如不是,改为它。
SQL*Plus中修改方法:
SQL> update props$ set value$='ZHS16GBK' where name='NLS_CHARACTERSET';
2、确认字符集是否修改的不彻底。
SELECT DISTINCT (NLS_CHARSET_NAME(CHARSETID)) CHARACTERSET,
DECODE(TYPE#, 1,
DECODE(CHARSETFORM, 1, 'VARCHAR2', 2, 'NVARCHAR2', 'UNKOWN'),
9,
DECODE(CHARSETFORM, 1, 'VARCHAR', 2, 'NCHAR VARYING', 'UNKOWN'),
96,
DECODE(CHARSETFORM, 1, 'CHAR', 2, 'NCHAR', 'UNKOWN'),
112,
DECODE(CHARSETFORM, 1, 'CLOB', 2, 'NCLOB', 'UNKOWN')) TYPES_USED_IN
FROM SYS.COL$
WHERE CHARSETFORM IN (1, 2)
AND TYPE# IN (1, 9, 96, 112);
3、如果上面的查询的确显示有多个字符集的设定,则进行如下处理:
SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
ALTER SYSTEM ENABLE RESTRICTED SESSION;
ALTER SYSTEM SET JOB_QUEUE_PROCESSES=0;
ALTER SYSTEM SET AQ_TM_PROCESSES=0;
ALTER DATABASE OPEN;
COL VALUE NEW_VALUE CHARSET
SELECT VALUE FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER='NLS_CHARACTERSET';
COL VALUE NEW_VALUE NCHARSET
SELECT VALUE FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER='NLS_NCHAR_CHARACTERSET';
--INTERNAL_USE是没有写在文档中的参数,用以强制完成字符集一致化
ALTER DATABASE CHARACTER SET INTERNAL_USE &CHARSET;
ALTER DATABASE NATIONAL CHARACTER SET INTERNAL_USE &NCHARSET;
SHUTDOWN IMMEDIATE;
STARTUP;
-- 再次启动数据库一遍
SHUTDOWN IMMEDIATE;
STARTUP;
**************注意****************
本人在使用上面的方法设置以后Oracle10g可以正常使用,在导入dmp,sql文件的时候则需要在Linux中做如下设置
sql文件,dmp文件在导入oracle的时候需要设置字符集
export LANG=zh_CN.GBK //这个是linux的字符集设置
export NLS_LANG=AMERICAN_AMERICA.ZHS16GBK //这个是oracle的字符集设置
--sunfruit
在applet上叠加层有2种方式
第一:使用iframe的方式
第二:使用window.createPopup();方式
<div></div>的方式我是没有试成功,无论如何设置,div都在applet的下面,要是哪位实验成功了,经验也共享一下
由于window.createPopup()主要用于创建右键菜单,由于其的一些特性,例如在其他地方点击,该window.createPopup()对象消失,所以叠加层方式使用window.createPopup()并不合适,使用iframe制作叠加层的效果更好更好
效果图如下
演示:
http://www.fruitres.cn/applet.jsp,可以从该演示页直接下载js代码,或者到代码下载页下载
代码下载:
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=1084563770&number=0
更多下载:
http://www.fruitres.cn/
--sunfruit
代码演示了基于ArcIMS开发包进行地图开发的基础方法
代码不是单独的可执行类,所以只看private void initMap()方法和public BufferedImage render()方法即可。MapBoundModel不用深究可以理解为等同Envelope类即可
在确定以下三个值
private String host = "172.17.8.99";
private int port = 5300;
private String servicename = "sde";
设置正确并且调用refresh();方法也没有异常抛出,但是getStream();返回null值得时候,则需要调整AXL文件添加<OUTPUT method="stream"/>
重新发布,具体AXL内容请注册后下载加密附件查看
查看代码直接下载demo文件即可
地址:
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=1257242391&number=0
更多下载:
http://www.fruitres.cn
--sunfruit
Geotools是一个开源的Java GIS工具包,可利用它来开发符合标准的地理信息系统。Geotools提供了OGC(Open Geospatial Consortium)规范的一个实现来作为他们的开发
代码演示了基于GeoTools开源包进行地图开发的基础方法
代码不是单独的可执行类,所以只看private void init()方法和public BufferedImage render()方法即可。MapBoundModel不用深究可以理解为等同Envelope类即可
直接下载demo文件即可
下载地址:
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=627152979&number=0
更多下载:
http://www.fruitres.cn
--sunfruit
3sNews最新消息,微软现在也已经成为OGC主要成员之一,OGC组织也对微软的加入感到高兴,他们最需要做的事情当然是把Virtual Earth 和 SQL Server 2008纳入OpenGIS兼容标准.微软是随Google之后加入OGC协会的。而2大巨头的加入也标志着空间信息产业已进入发展的快车道,逐渐成为IT主流。
Google Earth的KML文件格式是开放地理协会的最佳应用之一,这一协会包含345个成员,涵盖公司、政府机构和大学等角色,他们的目标是建立一个OpenGIS标准.2007年春季,Google递交了Keyhole Markup Language (KML 2.1)--Google Earth的最新格式,通过了OGC的验证,很快,它在世界范围内引起了广泛的兴趣,OGC也将他们加入了 -- 地理标示语言(GML)中.
原贴:http://www.3snews.net/index.php/action_viewnews_itemid_16707.html
--sunfruit
这是一个自定义图层的演示程序,演示向自定义图层添加POI、修改自定义图层中的POI、隐藏/显示自定义图层,关于图层和POI的定义参考原来发布的例子
通过这个demo可以了解数据部署方式,需要的lib文件,至于其它的就是Swing+Graphics的基本应用,所以不提供源代码
Swing+Graphics的用法请参考原来发布的作品
《基本绘图演示源代码,绘制矩形》
《JAVA开发的俄罗斯方块源代码》
JDK 1.5.0
功能:放大、缩小、平移
地图底图图层控制
自定义图层控制
参考MapXtremeJava48_DG_CHS.pdf,在MapInfo网站下载,或是安装开发包以后在文档目录中提供
下载地址:
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=2087703113&number=0
更多下载:
http://www.fruitres.cn/
--sunfruit
使用MapInfo地图引擎的自定义图层的代码
主要思路是自己建立系统图层,该图层和MapInfo引擎图层无关(关于MapInfo引擎图层控制请参考原来发布的例子),例如建立自己的ATM机图层,要求可以控制显示、隐藏ATM机图层。那么需要建立一个图层类和一个ATM机类,当然最好是接口的设计,这样在2D绘制的时候可以很容易的进行扩展添加其他的图层
注:不包含地图数据和Lib包
参考MapXtremeJava48_DG_CHS.pdf,在MapInfo网站下载,或是安装开发包以后在文档目录中提供
下载地址:
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=1367471068&number=0
更多下载:
http://www.fruitres.cn/
--sunfruit
上次发布了一个使用MapInfo地图引擎进行图层控制源代码,这次照例写了一个java程序来演示图层控制
通过这个demo可以了解数据部署方式,需要的lib文件,至于其它的就是Swing+Graphics的基本应用,所以不提供源代码
Swing+Graphics的用法请参考原来发布的作品
《基本绘图演示源代码,绘制矩形》
《JAVA开发的俄罗斯方块源代码》
JDK 1.5.0
功能:放大、缩小、平移
地图底图图层控制
参考MapXtremeJava48_DG_CHS.pdf,在MapInfo网站下载,或是安装开发包以后在文档目录中提供
下载地址:
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=842314737&number=0
更多下载:
http://www.fruitres.cn
--sunfruit
使用MapInfo地图引擎控制地图图层的代码
该代码只是对地图引擎底图数据图层的控制,对于自定义图层和添加新的图层,以后会出相关的代码
注:不包含地图数据和Lib包
参考MapXtremeJava48_DG_CHS.pdf,在MapInfo网站下载,或是安装开发包以后在文档目录中提供
直接下载demo文件即可
下载地址:
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=196483354&number=0
更多下载:
http://www.fruitres.cn/
--sunfruit
Oracle® Spatial
User’s Guide and Reference
10g Release 2 (10.2)
非常好的Oracle10g Spatial(10.2)的指南和参考文档
英文的
目录有3部分20多个章节,这里只节选了第二章节的目录
2 Spatial Data Types and Metadata
2.1 Simple Example: Inserting, Indexing, and Querying Spatial Data..... 2-1
2.2 SDO_GEOMETRY Object Type ...... 2-5
2.2.1 SDO_GTYPE.......... 2-5
2.2.2 SDO_SRID.......... 2-7
2.2.3 SDO_POINT .......... 2-7
2.2.4 SDO_ELEM_INFO......... 2-7
2.2.5 SDO_ORDINATES ........ 2-10
2.2.6 Usage Considerations ......... 2-11
2.3 SDO_GEOMETRY Methods......... 2-11
2.4 SDO_GEOMETRY Constructors........ 2-13
2.5 Geometry Examples........ 2-14
2.5.1 Rectangle......... 2-14
2.5.2 Polygon with a Hole......... 2-15
2.5.3 Compound Line String ....... 2-17
2.5.4 Compound Polygon ......... 2-19
2.5.5 Point........... 2-20
2.5.6 Oriented Point .......... 2-21
2.5.7 Type 0 (Zero) Element......... 2-23
2.5.8 Several Geometry Types........ 2-25
2.6 Geometry Metadata Views ........ 2-29
2.6.1 TABLE_NAME......... 2-30
2.6.2 COLUMN_NAME......... 2-30
2.6.3 DIMINFO........ 2-30
2.6.4 SRID........... 2-31
2.7 Spatial Index-Related Structures ....... 2-31
2.7.1 Spatial Index Views ....... 2-31
2.7.1.1 xxx_SDO_INDEX_INFO Views....... 2-31
2.7.1.2 xxx_SDO_INDEX_METADATA Views..... 2-32
2.7.2 Spatial Index Table Definition ....... 2-34
2.7.3 R-Tree Index Sequence Object ....... 2-35
2.8 Unit of Measurement Support ........ 2-35
直接下载demo文件即可
下载地址:
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=1500381985&number=0
更多下载:
http://www.fruitres.cn
--sunfruit
前一段时间写了一个基于MapInfo地图引擎生成地图图片的例子,这次写了一个java程序来使用MapInfo地图引擎浏览地图,可以看到加载地图数据以后,进行放大、缩小、平移的操作,抛去网络因素,速度方面还是可以接受的
通过这个demo可以了解数据部署方式,需要的lib文件,至于其它的就是Swing+Graphics的基本应用,所以不提供源代码
Swing+Graphics的用法请参考原来发布的作品
《基本绘图演示源代码,绘制矩形》
《JAVA开发的俄罗斯方块源代码》
JDK 1.5.0
功能:放大、缩小、平移
参考MapXtremeJava48_DG_CHS.pdf,在MapInfo网站下载,或是安装开发包以后在文档目录中提供
下载地址:http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=254133506&number=0
更多下载:http://www.fruitres.cn/
-sunfruit
使用MapInfo地图引擎生成地图图片的代码,不包含地图数据和Lib包
直接下载demo文件即可
参考MapXtremeJava48_DG_CHS.pdf,在MapInfo网站下载,或是安装开发包以后在文档目录中提供
JDK1.5
下载地址:
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=447120696&number=0
更多下载:
http://www.fruitres.cn/
--sunfruit
WINXP中选择多个磁盘进行碎片整理虽然bux一个批命令文件,可以顺序执行多个磁盘的碎片整理工作,这样虽然不能在WIN XP中选择多个磁盘进行碎片整理,利用这个方法也可以一次执行多个磁盘的碎片整理,比较实用
下载地址:
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=1942443612&number=0
更多资料:
http://www.fruitres.cn/
--sunfruit
该文档非常好的Oracle维护学习文档
文档目录如下
监控SQL
常用户用SQL
查询表结构
表空间使用状况
查询某个模式下面数据不为空的表
客户端主机信息
安装Oracle以后,经常使用的修改表空间的SQL代码
查看回滚段名称及大小
PL/SQL入门教程
在from后面使用变量
Oracle常用数据字典
在Oracle中实现数据库的复制
SQL*PLUS环境输入'&字符'的方法
简说创建用户
简说Oracle启动及关闭数据库实例
简说Oracle数据库导出(exp)/导入(imp)
实例:Oracle导出Excel文件
实例:Oracle导出HTM文件
查看数据库保留字
数据字典及某些字段意义
下载地址
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=244294537&number=0
更多资料
http://www.fruitres.cn/
--sunfruit
源代码四个文件,在这个DEMO中可以看到
如何使用2D组件,如何把JPanel做为画板使用,如何使用Canvas替代JPanel,如何设置2D组件的线宽
这个只是针对像入门的新手,高手就不需要看了,免得扔鸡蛋
下载地址
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=1016162856&number=0
更多工具和文档请到
http://www.fruitres.cn/
--sunfruit
将Oracle表结构导出成Html页面的工具
1.0.4
增加了导出触发器、存储过程、函数的功能,并且修改了不能导出KEYS全部类型的BUG
1.0.3
增加了导出Sequences、View的功能,并且导出界面默认导出到安装文件夹中的export目录中
1.0.2
增加了导出KEYS、INDEX的功能
1.0.1
增加了导出完毕以后直接点击打开文件夹、打开文件的查看方式,并且支持Linux/Win 平台
1.0.0
导出为html文件,导出完毕后直接查看index.html文件即可。导出的内容包括各个表的名称索引和注释以及表中各个字段的明细和注释
下载地址
http://www.fruitres.cn/servlet/buyproductservlet?tag=single&tag1=info&PRODUCT_ID=942025602&number=0 下载版本1.0.4
更多工具和文档请到
http://www.fruitres.cn/
--sunfruit
设置图片的每个象素Alpha,使得图片透明,核心代码如下
private void setAlpha(ByteArrayOutputStream os) {
/**
* 增加测试项
* 读取图片,绘制成半透明
*/
try {
ImageIcon imageIcon = new ImageIcon(os.toByteArray());
BufferedImage bufferedImage = new BufferedImage(imageIcon.getIconWidth(),imageIcon.getIconHeight()
, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g2D = (Graphics2D) bufferedImage.getGraphics();
g2D.drawImage(imageIcon.getImage(), 0, 0,
imageIcon.getImageObserver());
//循环每一个像素点,改变像素点的Alpha值
int alpha = 100;
for (int j1 = bufferedImage.getMinY(); j1 < bufferedImage.getHeight(); j1++) {
for (int j2 = bufferedImage.getMinX(); j2 < bufferedImage.getWidth(); j2++) {
int rgb = bufferedImage.getRGB(j2, j1);
rgb = ( (alpha + 1) << 24) | (rgb & 0x00ffffff);
bufferedImage.setRGB(j2, j1, rgb);
}
}
g2D.drawImage(bufferedImage, 0, 0, imageIcon.getImageObserver());
//生成图片为PNG
ImageIO.write(bufferedImage, "png", new File(图片路径));
}
catch (Exception e) {
e.printStackTrace();
}
}
--sunfruit
在openConnection之前加上
Properties prop = System.getProperties();
System.getProperties().put("proxySet","true");
// 设置http访问要使用的代理服务器的地址
prop.setProperty("http.proxyHost","xxxxxxx");
// 设置http访问要使用的代理服务器的端口
prop.setProperty("http.proxyPort","xxxxx");
然后并不像有的贴子说的那样添加
prop.setProperty("http.proxyUser","xxxxxxxx");
prop.setProperty("http.proxyPassword","xxxxx");
添加这样的内容是不行的,而是将"username:password"进行base64编码,具体代码如下
String authentication = "username:password";
String encodedLogin = new BASE64Encoder().encode(authentication.getBytes());
httpurlconnection.setRequestProperty("Proxy-Authorization", " Basic " + encodedLogin);
注意BASE64Encoder().encode(authentication.getBytes());这里不能使用BASE64Encoder().encodeBuffer(authentication.getBytes())否则报错
具体原因参考sun公布的JDK BUG , Bug ID: 4615330
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4615330
--sunfruit
前一段时间发了一个“
[原创]JAVAMAIL发送邮件正文是html格式并且显示图片还带有附件的邮件”的文章,后来经过深入的使用该功能发现几个要点说明如下:
第一:正文必须第一个添加到Multipart对象中
第二:IMGX X是序号,该序号必须从1开始,并且只能为数字
以上两点缺少一点都不会成功
另:ByteArrayDataSource 这个类在旧的javamail开发包中没有,需要新的javamail开发包
--sunfruit
有三个压缩包需要下载,下载以后放在同一个目录中解压即可
下载包1 下载包2 下载包3
--sunfruit
没有什么说的,源代码如下
public void sendMail(int id) {
//数据初始化
Transport transport; //发邮件时使用
String part = GetResource.getXMLValue("mail_port"); //端口
String mailhost = GetResource.getXMLValue("mail_server"); //服务器
String user = GetResource.getXMLValue("mail_user"); //用户名
String password = GetResource.getXMLValue("mail_password"); //密码
String from = GetResource.getXMLValue("mail_from"); //发件mail
String strname = GetResource.getXMLValue("mail_strname"); //在地址栏上显示的名字
String to = GetResource.getXMLValue("mail_to"); //收件人
String cc = ""; //抄送人
String bcc = ""; //密送人
String title = GetResource.getXMLValue("mail_title"); //标题
byte[] bytes = null;
try {
bytes = FileIO.readFile(GetResource.getXMLValue("mail_contentpath")); //正文
} catch (IOException ex) {
log.error("",ex);
}
String fromat = GetResource.getXMLValue("mail_fromat");
//获得下载需要的key
byte[] bkeydes=null;
try {
bkeydes=FileIO.readFile(GetResource.getXMLValue("附件地址"));
} catch (IOException ex) {
log.error("",ex);
}
try {
Properties props = System.getProperties(); //获得系统属性
props.put("mail.smtp.host", mailhost); //设置SMTP主机
props.put("mail.smtp.auth", "true"); //设置身份验证为真,若须身份验证则必须设为真
//获得邮件会话对象
Session session = null;
//需要验证
session = Session.getDefaultInstance(props,
new SmtpAuthenticator(user,
password));
//创建MIME邮件对象
MimeMessage mimeMsg = new MimeMessage(session);
//设置发信人
if (strname != null && !strname.equals("")) {
mimeMsg.setFrom(new InternetAddress(from, strname));
} else {
mimeMsg.setFrom(new InternetAddress(from));
}
//设置收信人
if (!to.equals("")) {
mimeMsg.setRecipients(Message.RecipientType.TO,
InternetAddress.parse(to));
}
//设置抄送人
if (!cc.equals("")) {
mimeMsg.setRecipients(Message.RecipientType.CC,
InternetAddress.parse(cc));
}
//设置暗送人
if (!bcc.equals("")) {
mimeMsg.setRecipients(Message.RecipientType.BCC,
InternetAddress.parse(bcc));
}
//设置邮件主题
sun.misc.BASE64Encoder enc = new sun.misc.BASE64Encoder();
// mimeMsg.setSubject("=?GB2312?B?" + enc.encode(title.getBytes()) +
// "?=");
mimeMsg.setSubject(title, "GBK");
//设置邮件内容
//mimeMsg.setText(content, "gb2312");
//发送日期
mimeMsg.setSentDate(new Date());
/*******************附件 begin××××××××××××××××××××××××*/
BodyPart mbp = null; //正文
//MimeBodyPart mbp1 = null; //附件
mbp = new MimeBodyPart();
//设置邮件正文格式(html/text)
if (fromat.equals("0") || fromat.equals("")) {
//普通格式
mbp.setText(new String(bytes,"GBK"));
} else {
//网页格式
mbp.setDataHandler(new DataHandler(new String(bytes,"GBK"),
"text/html;charset=gb2312"));
}
//!!!!注意必须为new MimeMultipart("related"); 这样的实例化才能发送html正文显示图片的邮件 "related"
//new MimeMultipart(); 这样实例化则不能发送html正文显示图片的邮件 只可以发送其他的邮件
Multipart mp = new MimeMultipart("related");
if (mbp != null)
mp.addBodyPart(mbp); //正文
//附件key.des
BodyPart messageBodyPart = new MimeBodyPart();
ByteArrayDataSource fileds = new ByteArrayDataSource(bkeydes,"application/octet-stream");
messageBodyPart.setDataHandler(new DataHandler(fileds));
//解决附件中文问题
//mbp1.setFileName(fileds.getName(),"gb2312");
messageBodyPart.setFileName(MimeUtility.encodeWord("key.des", "GB2312", null));
mp.addBodyPart(messageBodyPart);
//附件 图标
messageBodyPart = new MimeBodyPart();
bytes=FileIO.readFile(GetResource.getXMLValue("img_logo"));
fileds = new ByteArrayDataSource(bytes,"application/octet-stream");
messageBodyPart.setDataHandler(new DataHandler(fileds));
//解决附件中文问题
// messageBodyPart.setFileName("fruitrsource_touming.jpg");
// messageBodyPart.setHeader("Content-ID", "fruitrsource_touming");
messageBodyPart.setFileName("1.jpg");
//!!!!注意这里是"<IMG1>" 带有尖括号 而在正文的html里面则是src="cid:IMG1"
messageBodyPart.setHeader("Content-ID", "<IMG1>");
mp.addBodyPart(messageBodyPart);
// 添加 Multipart到Message中
mimeMsg.setContent(mp);
mimeMsg.saveChanges();
transport = session.getTransport("smtp");
transport.connect(mailhost, Integer.parseInt(part), user, password);
//发送邮件
//transport.send(mimeMsg, mimeMsg.getAllRecipients());
//transport.send(mimeMsg);
transport.sendMessage(mimeMsg, mimeMsg.getAllRecipients());
//System.out.println("mail send!");
transport.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
class SmtpAuthenticator extends Authenticator {
//SMTP身份验证
public SmtpAuthenticator(String username, String password) {
this.username = username;
this.password = password;
}
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(this.username, this.password);
}
String username = null;
String password = null;
}
--sunfruit
JAVA编写的使用像素RGB值还原图像的方法,代码如下
public class ImageData {
private int[][] idata=null;
public ImageData() {
readImageData();
writeImageData();
}
private int[][] readImageData()
{
try {
//imagedata的键值就是一个存储了图像像素RGB值得文本文件
RandomAccessFile randomAccessFile = new RandomAccessFile(new File(GetResource.getXMLValue("imagedata")),"r");
long filesize=randomAccessFile.length();
long filepointer=0;
DynArrayInt dynArrayInt=null;
ArrayList arrayList=new ArrayList();
int n1=0;
while(filesize>filepointer)
{
filepointer=randomAccessFile.getFilePointer();
String str1=randomAccessFile.readLine();
if(str1!=null)
{
String[] str2=str1.split(" ");
String[] str3=str2[1].split(",");
dynArrayInt=new DynArrayInt();
for(int i=0;i<str3.length;i++)
{
if(!str3[i].equals(""))
{
int pix = Integer.parseInt(str3[i], 16);
dynArrayInt.addInt(pix);
}
}
if(n1==0)
n1=dynArrayInt.getSize();
arrayList.add(dynArrayInt);
}
}
idata=new int[arrayList.size()][dynArrayInt.getSize()];
for(int i=0;i<idata.length;i++)
{
DynArrayInt dynArrayInt1=(DynArrayInt)arrayList.get(i);
for(int j=0;j<idata[i].length;j++)
{
idata[i][j]=dynArrayInt1.getInt(j);
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
return idata;
}
private void writeImageData()
{
BufferedImage bufferedImage=new BufferedImage(idata[0].length,idata.length,BufferedImage.TYPE_INT_RGB);
for(int i=0;i<idata.length;i++)
{
for(int j=0;j<idata[i].length;j++)
{
bufferedImage.setRGB(j,i,idata[i][j]);
}
}
Random random=new Random(System.currentTimeMillis());
String ext="jpg";
String filepath = System.getProperty("java.io.tmpdir") + random.nextInt(99999) + "." + ext;
try {
ImageIO.write(bufferedImage, ext, new File(filepath));
System.out.println("文件已经生成,路经为" + filepath);
}
catch (IOException ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
ImageData imagedata = new ImageData();
}
}
其中的“GetResource”,“DynArrayInt”为自定义类,GetResource得功能就是解析获取properties文件的内容,DynArrayInt是一个整形动态数组,可以在以前的文章里获得“DynArrayInt”的源代码,所谓的图像的像素值就是如下数据
px1 44525b,44555d,475a61,47585d,44555a,46575c,44575d,43565c,42575e,43585f,445960,435962,435964,425861,425861,41585e,3f555e,3e545f,3d5261,3c5162,3d4f59,3d4f59,3f515b,40525c,40525c,40525c,40525c,3f515b,3f515b,3f4f5a,3f4f5c,40505d,3f515f,3f515f,3d5060,3c4f5f,3b505f,3a4e58,3e525e,3e525e,3d5060,415466,3f5264,405363,405460,41555f,42575d,42575e,43585f,445862,445862,445864,445864,445866,445866,415760,435962,445a63,455b66,455b66,445a67,455b68,485d6c,4c626f,4d636e,4e646f,4d636c,4b616a,51686e,536a70,566d73,5c7378,6f7c7a,7c8987,86908f,8d9796,8e9696,949a9a,969c9c,9ca2a2,a1a3a1,a8aaa8,aeb1ac,b2b5b0,b9bcb7,c5c8c2,cccfc9,ced1c9,d6d9d1,d5d8d3,d7dad5,dadbd5,dcddd7,dcdbd4,ddddd3,e0ddd4,e0ddd4,deddd6,deddd6,deddd6,dfded7,dfded7,e0dfd8,e0dfd8,e0dfd8,e0dfd8,e3dfd9,e3dfd9,e3dfd9,e3dfd9,e4e0da,e4e0da,e4e0da,e5e1db,e5e1db,e2e1da,e2e1da,e3e2db,e3e2db,e4e3dc,e4e3dc,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e6e5de,e6e5de,e6e5de,e6e5de,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e4e1d8,e5e2d9,e5e2d9,e4e1d8,e2dfd6,e3e0d7,e2dfd6,e1ded5,e1ded5,e2dfd6,e2dfd6,e2dfd6,e2dfd6,e1ded5,e1ded5,e1ded5,e1ded5,e1ded5,dedad4,dedad4,dcdbd4,dcdbd4,dbdcd4,dbdcd4,d9dcd4,d9dcd4,dedad4,dedad4,ddd9d3,ddd9d3,dbd7d1,dbd7d1,dcd8d2,dcd8d2,dbd7d1,d5d8d0,d5d8d0,d7d8d0,d7d8d0,d7d6cf,d9d5cf,d9d5cf,d9d5cf,d6d5ce,d6d5ce,d6d5ce,d6d5ce,d6d5ce,d6d5ce,d6d5ce,d5d4cd,d5d4cd,d5d7cc,d5d7cc,d5d7cc,d5d7cc,d4d6cb,d4d6cb,d5d7cc,d5d7cc,d5d7cc,d2d7d0,d2d7d0,d3d6d0,d3d6d0,d4d5cf,d4d5cf,d5d4cf,d5d4cf,d5d4cf,d5d4cf,d5d4cf,d5d4cf,d4d3ce,d4d3ce,d4d3ce,d4d3ce,d4d3ce,d0d3cb,d2d3cb,d3d2cb,d6d0cb,d6d0cb,d4d3cc,d3d4cc,d2d5cd,d3d2ce,d3d2ce,d3d2ce,d3d2ce,d3d2ce,d3d2ce,d3d2ce,d3d2ce,d3d2ce,d0d3cb,d0d3cb,d2d3cb,d2d3cb,d3d2cb,d3d2cb,d5d1cb,d5d1cb,d2d1cc,d2d1cc,d2d1cc,d2d1cc,d2d1cc,d2d1cc,d2d1cc,d2d1cc,d2d1cc,d1d0c9,d1d0c9,d2d1ca,d2d1ca,d3d2cb,d3d2cb,d3d2cb,d3d2cb
px0 44525b,44575e,465b62,485a5a,455757,46585a,44575b,42555b,41565d,435761,445862,445a63,435966,435964,42595f,42595e,40575c,3f5560,3d5263,3c5066,3a4c58,3b4d59,3d4f5b,3e505a,40525c,40525c,40535a,3f5259,3f5259,3f5057,3f5057,405158,3f5259,3f5259,3d515b,3c505a,3b515c,3a4e5a,3e5260,3e5260,3d5060,415468,3f5266,3f5262,405462,405460,42575d,42575e,43585f,435761,41555f,425662,445864,465a68,465a68,415760,425861,445a63,445a65,445a65,465c69,475d6a,475c6b,44596a,485d6e,4c6373,4d6474,4a616f,4b6472,4b6472,496270,4c6573,5f6c6e,677476,6f7c7e,798587,818a8d,899295,8f969a,91989c,949895,9a9e9b,9ea39d,a1a6a0,a9aea8,aeb3ac,babfb8,c0c5bc,ccd1c8,cfd2cd,d2d5d0,d5d6d0,d7d8d2,dcdbd4,ddddd3,e0ddd4,e0ddd4,deddd6,deddd6,deddd6,dfded7,dfded7,e0dfd8,e0dfd8,e0dfd8,e0dfd8,e3dfd9,e3dfd9,e3dfd9,e3dfd9,e4e0da,e4e0da,e4e0da,e5e1db,e5e1db,e2e1da,e2e1da,e3e2db,e3e2db,e4e3dc,e4e3dc,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e6e5de,e6e5de,e6e5de,e6e5de,e5e4dd,e5e4dd,e5e4dd,e5e4dd,e4e1d8,e4e1d8,e4e1d8,e3e0d7,e2dfd6,e2dfd6,e2dfd6,e1ded5,e1ded5,e2dfd6,e2dfd6,e2dfd6,e2dfd6,e1ded5,e1ded5,e1ded5,e1ded5,e1ded5,ddd9d3,ddd9d3,dbdad3,dbdad3,dadbd3,dadbd3,d8dbd3,d8dbd3,dedad4,dedad4,ddd9d3,ddd9d3,dad6d0,dbd7d1,dcd8d2,dcd8d2,dbd7d1,d5d8d0,d5d8d0,d5d8d0,d7d8d0,d6d7cf,d7d6cf,d7d6cf,d9d5cf,d6d5ce,d6d5ce,d6d5ce,d6d5ce,d6d5ce,d6d5ce,d6d5ce,d5d4cd,d5d4cd,d6d8cd,d6d8cd,d6d8cd,d6d8cd,d4d6cb,d4d6cb,d5d7cc,d5d7cc,d5d7cc,d3d8d1,d3d8d1,d4d7d1,d4d7d1,d4d5cf,d4d5cf,d5d4cf,d5d4cf,d5d4cf,d5d4cf,d5d4cf,d5d4cf,d4d3ce,d4d3ce,d4d3ce,d4d3ce,d4d3ce,d0d3cb,d2d3cb,d3d2cb,d6d0cb,d7d1cc,d4d3cc,d4d5cd,d2d5cd,d4d2d1,d4d2d1,d4d2d1,d4d2d1,d4d2d1,d3d1d0,d3d1d0,d3d1d0,d3d1d0,d0d3cb,d0d3cb,d2d3cb,d2d3cb,d3d2cb,d3d2cb,d5d1cb,d5d1cb,d3d2cd,d3d2cd,d3d2cd,d3d2cd,d3d2cd,d2d1cc,d2d1cc,d2d1cc,d2d1cc,d1d0c9,d1d0c9,d2d1ca,d2d1ca,d4d3cc,d4d3cc,d4d3cc,d4d3cc
以上数据表示图像有两行像素,每一行有260个像素组成,每个像素的RGB值就是44525b
--sunfruit
求上图1点到其他各点的最短路径,依据图论知识建立矩阵模型,进一步得到代码如下
public class ShortPathA {
private static int[][]
a = {
{0, 50, 10, 100000, 45, 100000}, {100000, 0, 15, 100000, 10, 100000}, {20, 100000, 0, 15, 100000, 100000}, {
100000, 20, 100000, 0, 35, 100000}, {100000, 100000, 1000000, 30, 0, 100000}, {100000, 100000, 100000, 3, 100000, 0}
};
private static boolean[] mark = new boolean[a.length];
public ShortPathA() {
int Vo = 0; //源点
//源点到其他各点的距离
int[] b = new int[a.length];
DynArrayInt S = new DynArrayInt();
for (int i = 0; i < a.length; i++) {
mark[i] = false;
//b[i] = a[Vo][i];
}
int best = -1;
mark[0] = true;
b[0] = 0; //{0为源点}
while (best != 0) {
best = 0;
int best_j = 0;
for (int i = 0; i < b.length; i++)
{
if (mark[i]) //{对每一个已计算出最短路径的点}
{
for (int j = 0; j < b.length; j++) {
if ( (!mark[j]) && (a[i][j] > 0)) {
if ( (best == 0) || (b[i] + a[i][j] < best)) {
best = b[i] + a[i][j];
best_j = j;
}
}
}
}
}
if (best > 0) {
b[best_j] = best;
mark[best_j] = true;
}
}
System.out.println(java.util.Arrays.toString(b));
}
public static void main(String[] args) {
ShortPathA shortpath = new ShortPathA();
}
}
--sunfruit
当通过URLConnection第一次和服务器发起POST或是GET请求以后,通过getHeaderField方法获得SessionID,具体方法为:
String session_value=getHeaderField("Set-Cookie");
第二次发起POST或是GET请求的时候需要把刚才获得的SessionID放置在请求的头部然后再提交,这样就能服务器就会认为是同一个Session请求了,具体方法为:
setRequestProperty("Cookie", session_value);
如果取得Set-Cookie的值比较长,取值到分号前即可 如 JSESSIONID=575F7196EDB52825D288F4061C66BC29;
这样就实现了保存session向服务器提交请求
--sunfruit
上图求一笔画的路径,利用图论的相关知识可以得到程序如下:
public class OnePath {
private static int[][]
links = { {0,1,1,0,0,0,1,0}, {1,0,0,1,0,0,0,1}, {1,0,0,1,1,1,0,0},
{0,1,1,0,1,1,0,0}, {0,0,1,1,0,1,1,0}, {0,0,1,1,1,0,0,1}, {1,0,0,0,1,0,0,0}, {0,1,0,0,0,1,0,0}
};
public OnePath() {
int sum = 0;
//存放每个点的度
int[] point = new int[links[0].length];
for (int i = 0; i < links[0].length; i++) {
int[] templink = links[i];
for (int j = 0; j < links[0].length; j++) {
point[i] += templink[j];
}
sum += point[i];
}
//计算度数是奇数点的个数,如果大于2则不能一笔画
int odt = 0;
int start = -1;
for (int i = 0; i < point.length; i++) {
int mod = point[i] % 2;
if (mod > 0) {
//if(start==-1)
start = i;
odt++;
}
}
if(odt>2)
{
System.out.println("该图不能一笔画");
return;
}
int r = 0;
//从一个奇数点开始计算
int nowd=start;
System.out.print(nowd+1);
while (sum > 0) {
r=0;
//对于起点nowd 检查当前的点r 是否合适
//links[nowd][r]==0 判断是否有可以走的没有用过的线路
//(point[r]<=1 && sum!=2) 判断是否是最后一次,如果不是最后一次,那么避开度数是1的点
while (links[nowd][r]==0 || (point[r]<=1 && sum!=2)) {
r++;
}
links[nowd][r]=0; //已经用过的线路
links[r][nowd]=0; //已经用过的线路 links[nowd][r] links[r][nowd]互为往返路线,用过1->2那么2->1也作废了
sum=sum-2; //总度数减2 因为从1->2 消耗了1的度和2的度
point[nowd]--; //起点和终点的度都减1 1->2 那么1的度和2的度都减1
point[r]--; //起点和终点的度都减1 1->2 那么1的度和2的度都减1
nowd =r; //设置新的起点
System.out.print("->"+(r+1));
}
}
public static void main(String[] args) {
new OnePath();
}
}
如果你使用JavaScript编程,你或许会怀疑它是否包含了面向对象(OO)的结构。实际上,JavaScript的确支持面向对象的架构――在某种程度上。本文将通过一个可扩展向量图形(SVG)的实例来说明JavaScript的OO结构。
我如何在类中定义方法和属性?
OO开发的一个基本方面是类及其相应的方法和/或属性的使用。JavaScript通过function关键字支持类(及其属性)的使用。下面的代码定义了一个叫做Figure的JavaScript类:
function Figure() {
this.centerX=0;
this.centerY=0;
this.area=0;
this.transform = transform; // methods are defined like this
function transform(moveX,moveY,angle) {
this.centerX += moveX;
this.centerY += moveY;
} }
这个Figure类有三个属性:centerX,centerY,和area。另外,它还有一个方法叫做transform()。前三行是这个类的构造器。
但是它看起来不像一个类
你会想Figure()看起来不像一个类,而更像一个JavaScript的函数。那么为什么Figure()定义的是个类?
严格的说,Figure()函数没有定义一个类,但是它仿造了一个。它实际上创建了一个对象,在括号里的代码使这个对象的构造器。JavaScript的对象支持是很基础的,它并不区分类和对象。
这就引到了问题为什么Figure()函数创建的是一个对象。对象是可以有属性和方法的。基本上,因为Figure()函数同时包含了属性和方法,它就是个对象。在JavaScript里,所有的函数即是对象又是可调用的代码块。这不像它听起来的那样容易被误解。要创建一个Figure()类/对象,你只用使用以下句法:
MyFigure = new Figure();
你也可以把Figure()函数当作代码块调用,就像这样:
figValue = Figure();
变量figValue没有被定义是因为代码块Figure()没有返回任何值。如果你把return(this.area)加到函数的最后一行,figValue就会有个值0。所以figValue是个类型数字,MyFigure是对象 Rectangle的实例。
为什么所有的变量前面都一个“this”?
这个关键字this表示这是对象的实例变量,可以使用MyFigure.centerX从对象外部访问。要让变量成为私有变量,去掉前缀this就行了。this.transform = transform这一行让方法成为公用方法。这个方法通过MyFigure.transform(100,100,0)调用。
这些类有层次之分吗?
另一个好问题的是JavaScript的类是否有层次之分。回答是肯定有。我们来仔细看看是怎么做到分层的。我们可以定义一个Rectangle子类,并把Figure作为父类:
function Rectangle(startX, startY, endX, endY) {
this.width = endX - startX;
this.height = endY - startY;
this.centerX = (endX + startX)/2;
this.centerY = (endY + startY)/2;
this.computeArea = computeArea;
function computeArea() {
this.area = this.width*this.height;
} }
Rectangle.prototype = new Figure();
Rectangle对象是用4个自变量创建的,前四行是构造器。 Rectangle类包含了一个方法: computeArea()。最后一行Rectangle.prototype = new Figure();,把Rectangle类定义为从Figure类继承来的子类。
然我来解释一下prototype(原型)。每个对象构造器都有prototype属性;这是用来给所有的对象增加新属性和方法的。这就是为什么原型被用来实现继承:child.prototype = new parent();。通过原型,父对象的所有属性和方法都被添加到子对象上。
要注意this.centerX,this.centerY,和area是Rectangle类中所使用的属性,但是它们是 Figure父类的属性。和Rectangle类相似,Circle类可以被定义成Figure类的原型。这种父子关系可以按你需要来定义深度;你可以创建另一个Rectangle的子类。
我如何创建一个类的实例?
在JavaScript里创建一个类的实例很容易:
rect = new Rectangle(100,100,900,800);
这就创建了Rectangle类型的一个对象。Rectangle的构造器在属性width, height, centerX, 和centerY中填入了值。rect.area属性的值是零(0)。使用这个命令就能调用area方法:
rect.computeArea();
rect.area的值现在是560,000。要调用transform方法使用:
rect.transform(100,200,0);
父和子对象的属性可以像这样访问到:
var ar = rect.area;
var wi = rect.width;
我能超越属性和方法吗?
就像你在Java中的一样,你可以超越属性和方法。在子类中定义的属性或者方法可以超越同名的父类的属性和方法。
和全局变量互动
JavaScript也支持全局变量的使用。在以下代码段中测试一下g_area变量的范围:
<HTML>
<SCRIPT>
var g_area = 20;
function Figure() {
…
this.area=g_area;
…
}
function Rectangle(){ … }
Rectangle.prototype = new Figure();
function test(){
g_area = 40;
rect = new Rectangle();
alert(rect.area);
}
</SCRIPT>
<BODY onLoad = 'test()'/>
</BODY>
</HTML>
rect.area的值是20(不是你预计的40),这是因为Rectangle对象是Figure对象的原型,这种关系在test()被调用以前就被定义了。要使用g_area的新值,你需要用以下的方法:
function test() {
g_area = 40;
Rectangle.prototype = new Figure();
rect = new Rectangle();
alert(rect.area);
}
对于所有的Rectangle的新实例,这将改变area属性的值。或者,你可以使用这种方法:function test() {
g_area = 40;
rect = new Rectangle();
Rectangle.prototype.area = g_area;
alert(rect.area);
}
这将改变Rectangle所有现存的以及新实例的area属性的值。
结论
为了效仿OO开发,JavaScript提供了必需的继承、封装和超越属性,尽管它不支持接口和方法的过载。如果你是刚接触到OO开发,用它试试。OO概念允许开发者将一组数据和相关操作集中入一个对象。这在管理浏览器事件和管理浏览器内SVG图时很有用。
摘要: --sunfruit如何让你的网站排名靠前 网站做好了,怎样才能让你的网站在各大搜索引擎中排名靠前呢?网上的帖子很多,通过搜索和总结,整理出了一套自己行之有效的方法,写出来供大家参考 成功案例推荐:http://sooboo.com.cn/ 还在继续整理中,而且我们的网站也在不断优化中.........1、 网站建好后首先到各大搜索引擎免费登录你的网站http...
阅读全文
--sunfruit
很多时候需要上传附件到服务器,一般采用在页面放置<input type="file" name="upload" value=""> 的方式让用户选择要上传的文件进行上传,使用的是HTTP协议,这样的方式很方便开发也简单,不过如果上传的附件比较大的时候,会出现IE响应很慢的情况,如果用户急性子,多点几下上传的按钮,那么就会导致IE不响应的情况,这个时候如果在文件上传得过程中,给用户一个动态的提示甚至是一个上传的进度条,效果就会好多了,这样就会用到Ajax技术了,让Ajax以一个固定的间隔时间检查上传情况然后在页面以文字或是图片的方式体现出来就行了。
在使用Ajax进行附件上传进度查询的时候也想过,直接使用Ajax进行附件上传,在实现过程中发现问题比较多,所以就使用了变通的方式:使用标准的附件上传方式,结合Ajax进行上传的进度检查
主要的代码如下:
Ajax的封装
/**
* 创建 XMLHttpRequest 对象
*/
function getXMLHttpRequest()
{
var http_request;
if (window.XMLHttpRequest) {
//非IE浏览器框架创建 XMLHttpRequest 对象
http_request = new XMLHttpRequest();
if(http_request.overrideMimeType)
{
http_request.overrideMimeType('text/xml');
}
}else if (window.ActiveXObject){
// 创建 XMLHttpRequest 对象
try {
http_request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e1) {
try {
http_request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e2) {
// 不能创建 XMLHttpRequest 对象
}
}
}
return http_request;
}
/**
* Get请求
*/
function sendGetDictate(http_request,url)
{
req.open("GET", url, true);
http_request.send(null);
}
以上是Ajax的的基础部分,下面说文件上传部分的检查部分,文件上传本身的流程不变,只是在提交上传以后,需要执行setTimeout(checkupload,500); 这样的方法 checkupload 方法要自己编写,例如
function checkupload()
{
req=getXMLHttpRequest();
req.onreadystatechange = setActiveContent;
sendGetDictate(req,"/manager/servlet/imageservlet?tag=ajaxuploadfilecheck&WARE_ID=609187669&nocache="+Math.random(),"name=111");
}
然后需要编写setActiveContent方法,例如
var count=0; //防止无限循环,并且在页面提交上传得时候设置为0
function setActiveContent()
{
if (req.readyState == 4) {
if (req.status == 200) {
var rettext=req.responseText; //这里是servlet返回的内容,检查上传得状态,可以在javabean或是servlet里面设置全局的静态变量来表明上传状态
if(rettext=="-1")
{
//停止循环
alert("服务器更新错误");
}
else if(rettext=="0")
{
//继续循环检查
if(count<6)
{
setTimeout("checkupload()",500);
count++;
}
else
{
alert("上传失败");
}
}
else if(rettext=="1")
{
alert("文件上传成功");
}
}
}
}
基本流程就是这样了,至于文字表现上传过程还是进度条表现,就看自己了
Painting in AWT and Swing
Good Painting Code Is the Key to App Performance
By Amy Fowler
在图形系统中, 窗口工具包(windowing toolkit)通常提供一个框架以便相对容易地创建一个图形用户接口(GUI),在正确的时间、正确的屏幕位置显示一个正确的图像位。
AWT (abstract windowing toolkit,抽象窗口工具包) 和Swing都提供这种框架。但是实现这种框架的APIs对一些开发人员来讲不是很好理解 -- 这就导致一些程序的运行达不到预期的效果。
本文详细地解释AWT和Swing的绘画机制,目的是帮助开发人员写出正确的和高率的GUI绘画代码。然而,这篇文章只包括一般的画图机制(即,在什么地方和什么时间去呈现),而不介绍Swing的图形API怎样去呈现图形。想学习怎样去显示漂亮的图形,请访问Java 2D 网站。
绘画系统的演变
当最初的、为JDK1.0使用的AWT API发布时,只有重量级(heavyweight)部件("重量级" 的意思是说该部件有它自己的、遮光(opaque)的、与生俱来的窗体)。这样就使得AWT在很大程度上依赖于本地平台的绘画系统。这样的安排需要开发人员写代码的时候要考虑到很多细节问题,象重画检测(damage detection)、剪切(clip)计算、以及Z轴次序等。随着JDK 1.1中轻量级(lightweight)部件的引入("轻量级" 部件重用了与它最接近的重量级祖先的本地窗体),需要AWT能在共享的代码里为轻量级部件实现绘画处理。因此,重量级和轻量级部件在它们各自的绘画处理方法有着微妙的差别。
在JDK 1.1之后,当发布了Swing工具的时候,引入了它自己的绘画风格。Swing的绘画机制在很大程度上类似并且依赖于AWT,但是,也有它自己的观点,还带来了新的API,使得应用程序可以容易地定制绘画工作。
在AWT中绘画
去理解AWT绘画API怎样工作,有助于我们搞明白是什么触发了窗口环境中的绘画操作。AWT中有两种绘画操作:系统触发的绘画,和程序触发的绘画
系统触发的绘画操作
在系统触发的绘画操作中,系统需要一个部件显示它的内容,通常是由于下列中的原因:
部件第一次在屏幕上显示
部件的大小改变了
部件显示的内容受损需要维护。(比如,先前挡住部件的其它物体移走了,于是部件被挡住的部分曝露出来。
程序触发的绘画操作
在程序触发的绘画操作,是部件自己决定要更新自身的内容,因为部件内部的状态改变了。(比如,监测到鼠标按钮已经按下,那么它就需要去画出按钮"被按下"时的样子>
画图的方法
不管是谁触发了画图请求,AWT都是利用"回调"机制来实现绘画,这个机制对于“重量级”和“轻量级”的部件都是相同的。这就意味着程序应该在一个特定的可覆盖的方法中放置那些表现部件自身的的代码,并且在需要绘画的时候,工具包就会调用这个方法。这个可覆盖的方法在java.awt.Component中声明:
public void paint(Graphics g)
当AWT调用这个方法时,作为参数的、负责在这个特定的部件上绘画的Graphics对象是在之前已经配置了的,拥有恰当的状态值。
Graphics的颜色 值被设置为部件的前景。
Graphics的字体 设置为部件的字体。
Graphics的平移(translation) 也给设定,使用坐标(0,0)定位部件的左上角。
Graphics的裁剪框(clip rectangle)设置为部件需要画图的区域。
程序必须使用这个Graphics(或者其派生类)对象来呈现绘画,并且可以根据自己的需要任意改变Graphics对象的属性值。
这里是一个回调绘画的简单例子,在部件的范围内呈现一个实体园:
public void paint(Graphics g) {
// 根据部件的范围,动态计算圆的尺寸信息。
Dimension size = getSize();
// 直径
int d = Math.min(size.width, size.height);
int x = (size.width - d)/2;
int y = (size.height - d)/2;
// 画圆(颜色已经预先设置为部件的前景颜色)
g.fillOval(x, y, d, d);
g.setColor(Color.black);
g.drawOval(x, y, d, d);
}
初次接触AWT的开发人员可以看看PaintDemo example,那里介绍了一个在AWT程序中怎样使用画图回调方法的例子。
一般情况下,程序应该避免把绘画代码放置在回调方法paint()的范围之外。为什么呢?因为paint方法之外的绘画代码可能会在不适合画图的时候被调用 -- 例如,在部件变为可见之前或者已经在使用一个有效的Graphics。同时,不推荐在程序中直接调用paint()。
为了使能够由程序触发绘画操作,AWT提供了下面的java.awt.Component的方法,这样程序就可以提出一个异步的绘画请求:
public void repaint()
public void repaint(long tm)
public void repaint(int x, int y, int width, int height)
public void repaint(long tm, int x, int y,
int width, int height)
下面的代码显示了一个简单的鼠标监听器的例子,当鼠标按下和抬起的时候,使用repaint()来触发“假想按钮”的更新操作。
MouseListener l = new MouseAdapter() {
public void mousePressed(MouseEvent e) {
MyButton b = (MyButton)e.getSource();
b.setSelected(true);
b.repaint();
}
public void mouseReleased(MouseEvent e) {
MyButton b = (MyButton)e.getSource();
b.setSelected(false);
b.repaint();
}
};
如果部件要呈现复杂的图形,就应该使用带参数的repaint()方法,通过参数来指定需要更新的区域。一个比较常见的错误是总是调用无参数的repaint()来提出重画请求,这个方法会重画整个部件,经常导致一些不必要的画图处理。
paint() vs. update()
为什么我们要区分绘画操作是"系统触发" 还是"程序触发"呢?因为在“重量级”部件上,AWT对这两种请求的在处理上稍有不同(“轻量级”的情况将在后面介绍),并且不幸的是与此相关的代码非常复杂,难以更改。
对于“重量级”部件,这两种方式的绘画产生于两条不同的途径,取决于是“系统触发”还是“程序触发”。
系统触发的绘画
下面介绍“系统触发”的绘画操作是怎么产生的:
AWT确定是一部分还是整个部件需要绘画。
AWT促使事件分派线程调用部件的paint()方法。
程序触发的绘画
由程序触发的绘画的产生如下所示:
程序确定是一部分还是全部部件需要重画以对应内部状态的改变。
程序调用部件的repaint(),该方法向AWT登记了一个异步的请求 -- 当前部件需要重画。
AWT促使事件分派线程去调用部件的update() 方法。
注意: 在最初的重画请求处理完成之前,如果在该部件上有多次对repaint()的调用,那么这些调用可以被合并成对update()的一次调用。决定什么时候应该合并多次请求的运算法则取决于具体的实现。如果多次请求被合并,最终被更新的区域将是所有这些请求所要求更新的区域的联合(union)。
如果部件没有覆盖(override)update()方法,update()的默认实现会清除部件背景(如果部件不是“轻量级”),然后只是简单地调用paint()方法。
因为作为默认的最终结果都是一样的(paint()方法被调用),很多开发人员完全不知道一个分离的update() 方法的意义。确实,默认的update()的实现最终会转回到对paint()方法的调用,然而,如果需要,这个更新操作的 "钩子(hook)"可以使程根据不同的情况来处理程序触发的绘画。程序必须这么设想,对paint()的调用意味着Graphics的裁剪区"损坏"了并且必须全部重画;然而对update()的调用没有这种含义,它使程序做增量的绘画。
如果程序希望只把要增加的内容敷盖于已存在于该部件的像素位之上,那么就使用增量画图操作。UpdateDemo example 示范了一个利用update()的优点做增量绘画的程序。
事实上,大多数GUI部件不需要增量绘画,所有大部分程序可以忽略update()方法,并且简单地覆盖(override)paint()来呈现部件的当前状态。这就意味着不管“系统触发”还是“程序触发”,在大多数部件上的表现从其本质上讲是是等价的。
绘画与轻量级部件
从应用开发人员的观点看,“轻量级”的绘画API基本上和“重量级”一样(即,你只需要覆盖paint()方法,同样,调用repaint()方法去触发绘图更新)。然而,因为AWT的“轻量级”部件的框架全部使用普通Java代码实现,在轻量级部件上绘画机制的实现方式有一些微妙的不同。
“轻量级”部件是怎样被绘制的
“轻量级”部件需要一个处在容器体系上的“重量级”部件提供进行绘画的场所。当这个“重量级”的“祖宗”被告知要绘制自身的窗体时,它必须把这个绘画的请求转化为对其所有子孙的绘画请求。这是由java.awt.Container的paint()方法处理的,该方法调用包容于其内的所有可见的、并且与绘画区相交的轻量级部件的paint()方法。因此对于所有覆盖了paint()方法的Container子类(“轻量级”或“重量级”)需要立刻做下面的事情:
public class MyContainer extends Container {
public void paint(Graphics g) {
// paint my contents first...
// then, make sure lightweight children paint
super.paint(g);
}
}
如果没有super.paint(),那么容器(container)的轻量级子孙类就不会显示出来(这是一个非常普遍的问题,自从JDK1.1初次引进“轻量级”部件之后)。
这种情况相当于注释掉了默认的Container.update()方法的执行,从而不能 使用递归去调用其轻量级子孙类的update()或者paint()方法。这就意味着任何使用update()方法实现增量绘画的重量级Container子类必须确保其轻量级子孙在需要时,能够被它的递归操作所调用从而实现重画。幸运的是,只有少数几个重量级的容器(Container)需要增量绘图,所以这个问题没有影响到大多数的程序。
轻量级与系统触发型的画图
为轻量级部件实现窗体行为(显示、隐藏、移动、改变大小等)的轻量级框架的代码全部用Java代码写成。经常的,在这些功能的Java实现中,AWT必须明确地吩咐各个轻量级部件执行绘画(实质上讲这也是系统触发的绘画,尽管它不是源于本地的 操作系统)。而轻量级框架使用repaint()方法来吩咐部件执行绘画,这是我们前面解释过的,将导致一个update()的调用而不是直接地对paint()的调用。因此,对于轻量级,系统触发型的画图操作可以遵循下面的两种途径:
系统触发的绘画要求产生于本地系统(例如,轻量级的重量级祖先第一次现身的时候),这导致对paint()的直接调用。
系统触发型的绘图要求产生于轻量框架(例如,轻量级部件的尺寸改变了),这导致对update()的调用,该方法进而默认地调用paint()。
简单地讲,这意味着轻量级部件在update()和paint()之间没有实质的差别,进一步讲这又意味着“增量的绘图技术”不能用到轻量级部件上。
轻量级部件与透明
因为轻量级部件"借用"了本属于其“重量级”祖先的屏幕,所以它们支持“透明”的特征。这样做是因为轻量级部件是从底往上绘画,因此如果轻量级部件遗留一些或者全部它们祖先的像素位而没有画,底层的部件就会"直接显示。"出来。这也是对于轻量级部件,update()方法的在默认实现将不再清除背景的原因。
LightweightDemo 例程示范了轻量级部件的透明特征。
"灵活巧妙地"绘画方法
当AWT尝试着使呈现部件的处理尽可能高效率时,部件自身paint()的实现可能对整体性能有重大的影响。影响这个处理过程的两个关键领域是:
使用裁剪区来缩小需要呈现的范围。
应用内部的版面布局信息来缩小对子部件的笼罩范围(仅适用于轻量级).。
如果你的部件很简单 -- 比如,如果是一个按钮 -- 那么就不值得花费气力去改善它的呈现属性,使它仅仅去绘画与修剪区相交的部分;不理会Graphics的裁剪区直接绘制整个部件反而更划算。然而,如果你创建的部件界面很复杂,比如文本部件,那么迫切需要你的代码使用裁剪信息来缩小需要绘图的范围。
更进一步讲,如果你写了一个容纳了很多部件的复杂的轻量级容器,其中的部件和容器的布局管理器,或者只是容器的布局管理器拥有布局的信息,那么就值得使用所知道的布局信息来更灵活地确定哪个子部件需要绘画。Container.paint()的默认实现只是简单地按顺序遍历子部件,检查它是否可见、是否与重换区域相交 -- 对于某几个布局管理这种操作就显得不必要的罗嗦。比如,如果容器在100*100的格子里布置部件,那么格子的信息就可以用来更快得确定这10,000个部件中哪个与裁剪框相交,哪个就确实需要绘制。
AWT绘画准则
AWT为绘制部件提供了一个简单的回调API。当你使用它是,要遵循下面的原则:
对于大多数程序,所有的客户区绘画代码应该被放置在部件的paint()方法中。
通过调用repaint()方法,程序可以触发一个将来执行的paint()调用,不能直接调用paint()方法。
对于界面复杂的部件,应该触发带参数的repaint()方法,使用参数定义实际需要更新的区域;而不带参数调用会导致整个部件被重画。
因为对repaint()的调用会首先导致update()的调用,默认地会促成paint()的调用,所以重量级部件应该覆盖update()方法以实现增量绘制,如果需要的话(轻量级部件不支持增量绘制) 。
覆盖了paint()方法的java.awt.Container子类应当在paint()方法中调用super.paint()以保证子部件能被绘制。
界面复杂的部件应该灵活地使用裁剪区来把绘画范围缩小到只包括与裁剪区相交的范围。
在Swing中的绘画
Swing起步于AWT基本绘画模式,并且作了进一步的扩展以获得最大化的性能以及改善可扩展性能。象AWT一样,Swing支持回调绘画以及使用repaint()促使部件更新。另外,Swing提供了内置的双缓冲(double-buffering)并且作了改变以支持Swing的其它结构(象边框(border)和UI代理)。最后,Swing为那些想更进一步定制绘画机制的程序提供了RepaintManager API。
对双缓冲的支持
Swing的最引人注目的特性之一就是把对双缓冲的支持整个儿的内置到工具包。通过设置javax.swing.JComponent的"doubleBuffered"属性就可以使用双缓冲:
public boolean isDoubleBuffered()
public void setDoubleBuffered(boolean o)
当缓冲激活的时候,Swing的双缓冲机制为每个包容层次(通常是每个最高层的窗体)准备一个单独的屏外缓冲。并且,尽管这个属性可以基于部件而设置,对一个特定的容器上设置这个属性,将会影响到这个容器下面的所有轻量级部件把自己的绘画提交给屏外缓冲,而不管它们各自的"双缓冲"属性值
默认地,所有Swing部件的该属性值为true。不过对于JRootPane这种设置确实有些问题,因为这样就使所有位于这个上层Swing部件下面的所有部件都使用了双缓冲。对于大多数的Swing程序,不需要作任何特别的事情就可以使用双缓冲,除非你要决定这个属性是开还是关(并且为了使GUI能够平滑呈现,你需要打开这个属性)。Swing保证会有适宜的Graphics对象(或者是为双缓冲使用的屏外映像的Graphics,或者是正规的Graphics)传递给部件的绘画回调函数,所以,部件需要做的所有事情仅仅就是使用这个Graphics画图。本文的后面,在绘制的处理过程这一章会详细解释这个机制。
其他的绘画属性
为了改善内部的绘画算法性能,Swing另外引进了几个JComponent的相互有关联的属性。引入这些属性为的是处理下面两个问题,这两个问题有可能导致轻量级部件的绘画成本过高:
透明(Transparency): 当一个轻量级部件的绘画结束时,如果该部件的一部分或者全部透明,那么它就可能不会把所有与其相关的像素位都涂上颜色;这就意味着不管它什么时候重画,它底层的部件必须首先重画。这个技术需要系统沿着部件的包容层次去找到最底层的重量级祖先,然后从它开始、从后向前地执行绘画。
重叠的部件(Overlapping components): 当一个轻量级部件的绘画结束是,如果有一些其他的轻量级部件部分地叠加在它的上方;就是说,不管最初的轻量级部件什么时候画完,只要有叠加在它上面的其它部件(裁剪区与叠加区相交),这些叠加的部件必须也要部分地重画。这需要系统在每次绘画时要遍历大量的包容层次,以检查与之重叠的部件。
遮光性
在一般情况下部件是不透明的,为了提高改善性能,Swing增加了读写javax.swing.JComponent的遮光(opaque)属性的操作:
public boolean isOpaque()
public void setOpaque(boolean o)
这些设置是:
true:部件同意在它的矩形范围包含的里所有像素位上绘画。
false:部件不保证其矩形范围内所有像素位上绘画。
遮光(opaque)属性允许Swing的绘图系统去检测是否一个对指定部件的重画请求会导致额外的对其底层祖先的重画。每个标准Swing部件的默认(遮光)opaque属性值由当前的视-感UI对象设定。而对于大多数部件,该值为true。
部件实现中的一个最常见的错误是它们允许遮光(opaque)属性保持其默认值true,却又不完全地呈现它们所辖的区域,其结果就是没有呈现的部分有时会造成屏幕垃圾。当一个部件设计完毕,应该仔细的考虑所控制的遮光(opaque)属性,既要确保透的使用是明智的,因为它会花费更多的绘画时间,又要确保与绘画系统之间的协约履行。
遮光(opaque)属性的意义经常被误解。有时候被用来表示“使部件的背景透明”。然而这不是Swing对遮光的精确解释。一些部件,比如按钮,为了给部件一个非矩形的外形可能会把“遮光”设置为false,或者为了短时间的视觉效果使用一个矩形框围住部件,例如焦点指示框。在这些情况下,部件不遮光,但是其背景的主要部分仍然需要填充。
如先前的定义,遮光属性的本质是一个与负责重画的系统之间订立的契约。如果一个部件使用遮光属性去定义怎样使部件的外观透明,那么该属性的这种使用就应该备有证明文件。(一些部件可能更合适于定义额外的属性控制外观怎样怎样增加透明度。例如,javax.swing.AbstractButton提供ContentAreaFilled属性就是为了达到这个目的。)
另一个毫无价值的问题是遮光属性与Swing部件的边框(border)属性有多少联系。在一个部件上,由Border对象呈现的区域从几何意义上讲仍是部件的一部分。就是说如果部件遮光,它就有责任去填充边框所占用的空间。(然后只需要把边框放到该不透明的部件之上就可以了)。
如果你想使一个部件允许其底层部件能透过它的边框范围而显示出来 -- 即,通过isBorderOpaque()判断border是否支持透明而返回值为false -- 那么部件必须定义自身的遮光属性为false并且确保它不在边框的范围内绘图。
"最佳的"绘画方案
部件重叠的问题有些棘手。即使没有直接的兄弟部件叠加在该部件之上,也总是可能有非直系继承关系(比如"堂兄妹"或者"姑婶")的部件会与它交叠。这样的情况下,处于一个复杂层次中的每个部件的重画工作都需要一大堆的树遍历来确保'正确地'绘画。为了减少不必要的遍历,Swing为javax.swing.JComponent增加一个只读的isOptimizedDrawingEnabled属性:
public boolean isOptimizedDrawingEnabled()
这些设置是:
true:部件指示没有直接的子孙与其重叠。
false: 部件不保证有没有直接的子孙与之交叠。
通过检查isOptimizedDrawingEnabled属性,Swing在重画时可以快速减少对交叠部件的搜索。
因为isOptimizedDrawingEnabled属性是只读的,于是部件改变默认值的唯一方法是在其子类覆盖(override)这个方法来返回所期望的值。除了JLayeredPane,JDesktopPane,和JViewPort外,所有标准Swing部件对这个属性返回true。
绘画方法
适应于AWT的轻量级部件的规则同样也适用于Swing部件 -- 举一个例子,在部件需要呈现的时候就会调用paint() -- 只是Swing更进一步地把paint()的调用分解为3个分立的方法,以下列顺序依次执行:
protected void paintComponent(Graphics g)
protected void paintBorder(Graphics g)
protected void paintChildren(Graphics g)
Swing程序应该覆盖paintComponent()而不是覆盖paint()。虽然API允许这样做,但通常没有理由去覆盖paintBorder()或者paintComponents()(如果你这么做了,请确认你知道你到底在做什么!)。这个分解使得编程变得更容易,程序可以只覆盖它们需要扩展的一部分绘画。例如,这样就解决先前在AWT中提到的问题,因为调用super.paint()失败而使得所有轻量级子孙都不能显示。
SwingPaintDemo例子程序举例说明了Swing的paintComponent()回调方法的简单应用。
绘画与UI代理
大多数标准Swing部件拥有它们自己的、由分离的观-感(look-and-feel)对象(叫做"UI代理")实现的观-感。这意味着标准部件把大多数或者所有的绘画委派给UI代理,并且出现在下面的途径:
paint()触发paintComponent()方法。
如果ui属性为non-null,paintComponent()触发ui.update()。
如果部件的遮光属性为true,ui.udpate()方法使用背景颜色填充部件的背景并且触发ui.paint()。
ui.paint()呈现部件的内容。
这意味着Swing部件的拥有UI代理的子类(相对于JComponent的直系子类),应该在它们所覆盖的paintComponent方法中触发super.paintComponent()。
public class MyPanel extends JPanel {
protected void paintComponent(Graphics g) {
// Let UI delegate paint first
// (including background filling, if I'm opaque)
super.paintComponent(g);
// paint my contents next....
}
}
如果因为某些原因部件的扩展类不允许UI代理去执行绘画(是如果,例如,完全更换了部件的外观),它可以忽略对super.paintComponent()的调用,但是它必须负责填充自己的背景,如果遮光(opaque)属性为true的话,如前面在遮光(opaque)属性一章讲述的。
绘画的处理过程
Swing处理"repaint"请求的方式与AWT有稍微地不同,虽然对于应用开发人员来讲其本质是相同的 -- 同样是触发paint()。Swing这么做是为了支持它的RepaintManager API (后面介绍),就象改善绘画性能一样。在Swing里的绘画可以走两条路,如下所述:
(A) 绘画需求产生于第一个重量级祖先(通常是JFrame、JDialog、JWindow或者JApplet):
事件分派线程调用其祖先的paint()
Container.paint()的默认实现会递归地调用任何轻量级子孙的paint()方法。
当到达第一个Swing部件时,JComponent.paint()的默认执行做下面的步骤:
如果部件的双缓冲属性为true并且部件的RepaintManager上的双缓冲已经激活,将把Graphics对象转换为一个合适的屏外Graphics。
调用paintComponent()(如果使用双缓冲就把屏外Graphics传递进去)。
调用paintChildren()(如果使用双缓冲就把屏外Graphics传递进去),该方法使用裁剪并且遮光和optimizedDrawingEnabled等属性来严密地判定要递归地调用哪些子孙的paint()。
如果部件的双缓冲属性为true并且在部件的RepaintManager上的双缓冲已经激活,使用最初的屏幕Graphics对象把屏外映像拷贝到部件上。
注意:JComponent.paint()步骤#1和#5在对paint()的递归调用中被忽略了(由于paintChildren(),在步骤#4中介绍了),因为所有在swing窗体层次中的轻量级部件将共享同一个用于双缓冲的屏外映像。
(B) 绘画需求从一个javax.swing.JComponent扩展类的repaint()调用上产生:
JComponent.repaint()注册一个针对部件的RepaintManager的异步的重画需求,该操作使用invokeLater()把一个Runnable加入事件队列以便稍后执行在事件分派线程上的需求。
该Runnable在事件分派线程上执行并且导致部件的RepaintManager调用该部件上paintImmediately(),该方法执行下列步骤:
使用裁剪框以及遮光和optimizedDrawingEnabled属性确定“根”部件,绘画一定从这个部件开始(处理透明以及潜在的重叠部件)。
如果根部件的双缓冲属性为true,并且根部件的RepaintManager上的双缓冲已激活,将转换Graphics对象到适当的屏外Graphics。
调用根部件(该部件执行上述(A)中的JComponent.paint()步骤#2-4)上的paint(),导致根部件之下的、与裁剪框相交的所有部件被绘制。
如果根部件的doubleBuffered属性为true并且根部件的RepaintManager上的双缓冲已经激活,使用原始的Graphics把屏外映像拷贝到部件。
注意:如果在重画没有完成之前,又有发生多起对部件或者任何一个其祖先的repaint()调用,所有这些调用会被折叠到一个单一的调用 回到paintImmediately() on topmostSwing部件 on which 其repaint()被调用。例如,如果一个JTabbedPane包含了一个JTable并且在其包容层次中的现有的重画需求完成之前两次发布对repaint()的调用,其结果将变成对该JTabbedPane部件的paintImmediately()方法的单一调用,会触发两个部件的paint()的执行。
这意味着对于Swing部件来说,update()不再被调用。
虽然repaint()方法导致了对paintImmediately()的调用,它不考虑"回调"绘图,并且客户端的绘画代码也不会放置到paintImmediately()方法里面。实际上,除非有特殊的原因,根本不需要超载paintImmediately()方法。
同步绘图
象我们在前面章节所讲述的,paintImmediately()表现为一个入口,用来通知Swing部件绘制自身,确认所有需要的绘画都能适当地产生。这个方法也可能用来安排同步的绘图需求,就象它的名字所暗示的,即一些部件有时候需要保证它们的外观实时地与其内部状态保持一致(例如,在JScrollPane执行滚定操作的时候确实需要这样并且也做到了)。
程序不应该直接调用这个方法,除非有合理实时绘画需要。这是因为异步的repaint()可以使多个重复的需求得到有效的精简,反之直接调用paintImmediately()则做不到这点。另外,调用这个方法的规则是它必须由事件分派线程中的进程调用;它也不是为能以多线程运行你的绘画代码而设计的。关于Swing单线程模式的更多信息,参考一起归档的文章"Threads and Swing."
RepaintManager
Swing的RepaintManager类的目的是最大化地提高Swing包容层次上的重画执行效率,同时也实现了Swing的'重新生效'机制(作为一个题目,将在其它文章里介绍)。它通过截取所有Swing部件的重画需求(于是它们不再需要经由AWT处理)实现了重画机制,并且在需要更新的情况下维护其自身的状态(我们已经知道的"dirty regions")。最后,它使用invokeLater()去处理事件分派线程中的未决需求,如同在"Repaint Processing"一节中描述的那样(B选项).
对于大多数程序来讲,RepaintManager可以看做是Swing的内部系统的一部分,并且甚至可以被忽略。然而,它的API为程序能更出色地控制绘画中的几个要素提供了选择。
"当前的"RepaintManager
RepaintManager设计 is designed to be dynamically plugged, 虽然 有一个单独的接口。下面的静态方法允许程序得到并且设置"当前的"RepaintManager:
public static RepaintManager currentManager(Component c)
public static RepaintManager currentManager(JComponent c)
public static void
setCurrentManager(RepaintManager aRepaintManager)
更换"当前的"RepaintManager
总的说来,程序通过下面的步骤可能会扩展并且更换RepaintManager:
RepaintManager.setCurrentManager(new MyRepaintManager());
你也可以参考RepaintManagerDemo ,这是个简单的举例说明RepaintManager加载的例子,该例子将把有关正在执行重画的部件的信息打印出来。
扩展和替换RepaintManager的一个更有趣的动机是可以改变对重画的处理方式。当前,默认的重画实现所使用的来跟踪dirty regions的内部状态值是包内私有的并且因此不能被继承类访问。然而,程序可以实现它们自己的跟踪dirty regions的机制并且通过超载下面的方法对重画需求的缩减:
public synchronized void
addDirtyRegion(JComponent c, int x, int y, int w, int h)
public Rectangle getDirtyRegion(JComponent aComponent)
public void markCompletelyDirty(JComponent aComponent)
public void markCompletelyClean(JComponent aComponent) {
addDirtyRegion()方法是在调用Swing部件的repaint()的之后被调用的,因此可以用作钩子来捕获所有的重画需求。如果程序超载了这个方法(并且不调用super.addDirtyRegion()),那么它改变了它的职责,而使用invokeLater()把Runnable放置到EventQueue ,该队列将在合适的部件上调用paintImmediately()(translation: not for the faint of heart).
从全局控制双缓冲
RepaintManager提供了从全局中激活或者禁止双缓冲的API:
public void setDoubleBufferingEnabled(boolean aFlag)
public boolean isDoubleBufferingEnabled()
这个属性在绘画处理的时候,在JComponent的内部检查过以确定是否使用屏外缓冲显示部件。这个属性默认为true,但是如果程序希望在全局范围为所有Swing部件关闭双缓冲的使用,可以按照下面的步骤做:
RepaintManager.currentManager(mycomponent).
setDoubleBufferingEnabled(false);
注意:因为Swing的默认实现要初始化一个单独的RepaintManager实例,mycomponent参数与此不相关。
Swing绘画准则
Swing开发人员在写绘画代码时应该理解下面的准则:
对于Swing部件,不管是系统-触发还是程序-触发的请求,总会调用paint()方法;而update()不再被Swing部件调用。
程序可以通过repaint()触发一个异步的paint()调用,但是不能直接调用paint()。
对于复杂的界面,应该调用带参数的repaint(),这样可以仅仅更新由该参数定义的区域;而不要调用无参数的repaint(),导致整个部件重画。
Swing中实现paint()的3个要素是调用3个分离的回调方法:
paintComponent()
paintBorder()
paintChildren()
Swing部件的子类,如果想执行自己的绘画代码,应该把自己的绘画代码放在paintComponent()方法的范围之内。(不要放在paint()里面)。
Swing引进了两个属性来最大化的改善绘画的性能:
opaque: 部件是否要重画它所占据范围中的所有像素位?
optimizedDrawingEnabled: 是否有这个部件的子孙与之交叠?
如故Swing部件的(遮光)opaque属性设置为true,那就表示它要负责绘制它所占据的范围内的所有像素位(包括在paintComponent()中清除它自己的背景),否则会造成屏幕垃圾。
把一个部件设置为遮光(opaque)同时又把它的optimizedDrawingEnabled属性设置为false,将导致在每个绘画操作中要执行更多的处理,因此我们推荐的明智的方法是同时使用透明并且交叠部件。
使用UI代理(包括JPanel)的Swing部件的扩展类的典型作法是在它们自己的paintComponent()的实现中调用super.paintComponent()。因为UI代理可以负责清除一个遮光部件的背景,这将照顾到#5.
Swing通过JComponent的doubleBuffered属性支持内置的双缓冲,所有的Swing部件该属性默认值是true,然而把Swing容器的遮光设置为true有一个整体的构思,把该容器上的所有轻量级子孙的属性打开,不管它们各自的设定。
强烈建议为所有的Swing部件使用双缓冲。
界面复杂的部件应该灵活地运用剪切框来,只对那些与剪切框相交的区域进行绘画操作,从而减少工作量。
总结
不管AWT还是Swing都提供了方便的编程手段使得部件内容能够正确地显示到屏幕上。虽然对于大多数的GUI需要我们推荐使用Swing,但是理解AWT的绘画机制也会给我们带来帮助,因为Swing建立在它的基础上。
关于AWT和Sing的特点就介绍到这里,应用开发人员应该尽力按照本文中介绍的准则来撰写代码,充分发挥这些API功能,使自己的程序获得最佳性能。
浙大高分子物理郑强教授的激情演讲摘录
作为一个学者,我不是来卖弄嘴皮子的。借助这个讲坛,我认为各行各业对知识的接受是潜移默化,循序渐近的过程。下面从自然科学的角度来谈谈我的一些观点,我提出几个重大问题:
第一,我国搞了几十年的科学研究与攻关,在几十个工业门类中,到底有哪几个是属于中国的民族工业或者可以称为自己的工业的?到底有哪几项科研在国际上是数一数二的?中国现在到底需要什么?我经常出国,每出一次国,灵魂就受到一次洗礼,就巴不得在回来的第二天就去中小学讲。为什么?就是感到紧迫的压力和羞愧,特别是去了日本和韩国,这两个同属于东方国家去了之后,感触更多。
下面这四个方面,是近5年吹得最厉害的。先说超导,这也是从美国人开始的。我是一个教授,在浙大当老师,浙大的“求是”是个无价之宝。现在中国的知识分子有个弱点,就是不喜欢人家说自己的缺陷,更不愿意自己说自己的缺陷。我今天就愿意来揭揭自己的短,面对产业家,我更应该说实话。
中国今天的科技很多都是“跟踪”,这也难为中国的教授,因为日子过得较苦,没有钱,加上很多领导同志本身也没有知识,为了蒙领导,让他们拨一点钱,总得把一些文章、报告、口号写得越高越好。比如,现在教育界号称“建世界第一流大学”,教育部跨世纪发展计划中定的10所大学,现在已选定了9所,第10所还未选出。在中国这个发展中国家,你能建10所世界一流大学,那美国有多少所?日本有多少所?现在的实际状况是:世界上前200所大学,中国一所都排不进!在亚洲能排出几所?我到国外去看了以后,感到要将浙大建成世界一流大学就像共产主义理想,我们永远要努力!但是,我们不应该去追求这样虚幻的目标,去呼这样的口号,我们的差距还很大。
现在的几个行业也是前几年套着中国科技产业目标走得几个方向。像纳米,它只不过是一个尺度概念。各种尺度的材料都有各自的用处,比如,为什么要把泥土拿来做成砖,砖的尺度比沙泥要大得多,因为小的沙粒没有强度。我们怎么能把丰富多彩的物质世界只说成是一个纳米呢?所以,谁都没有注意是谁提出的这个口号的,其实我们又中了美国人的圈套!这与军备竞赛是一样的。超导中国科学院在搞;基因上海在搞;纳米全国在搞,连工厂技术员也在搞。
刚才何祚庥先生讲的悬浮列车,不要以为上海的高楼大厦与东京、大阪一样,中国就现代化了。修一个房子、修一座桥非常容易,但你要看看国民素质到了一个什么样的阶段。日本大楼里走出来的人都是受过高等教育的;而我们上海的大厦里走出来的人却都是些腰缠万贯的文盲!怎么能说国家已经现代化了?浙江杭州搞的世贸会挺漂亮,但你可以去西溪河看看,如果西溪河能出现小鸟、天鹅,杭州就现代化了,这不是一个穿一件衣服的问题。我们最需要的是什么?我们不要用这些东西来摆样子!我们应该关注我国的哪几个方面?
这是我的一些建议,提出的一些口号,以前说“无知无畏”,现在却是“无知才无畏“,许多企业把浙江省技术监督局、科委的人请来吃一顿饭,喝一点酒,他就给你签个字,再把我们这些教授胁迫到那里去,给你盖个章,然后就是“填补国内外空白“、“国际先进水平”,写论文则是“国际领先水平的研究成果”、“首次科学发现“等等,这都是目前非常严重的问题!作为一个大学教授,我深深地为此担忧!这不是我们的责任,是我们的领导无知,是他们倡导了这个主流。
我知道在座的处长或老总日子很难过,因为你们不写这样的报表,就拿不到钱,项目就得不到批准。教授也同样如此,天天写报告,而不是在实验室静下心来好好搞研究,这是很严重的!科学家说,纳米无所不能,这一说,大家就都去搞纳米了;老板说:纳米商机无限,再不上就没有机会了,因此,宁波想干,诸暨想干,天台也想干,大家都来搞纳米。科学家知道纳米不稳定,它做成材料就聚集在一起,但产业家并不知道这一点。有人说,纳米这么好,我们都变成纳米人多好,但高的篮球谁打? 篮框得降低了。纳米人的作用可否用在脑血栓治疗上,不用吃药,脑袋上挖个小孔,让纳米人钻进去清理一下就行了。
这些东西可能只有我在说不好。纳米在最近的一二个月里在浙江炒得很厉害,什么“纳米冰箱能防霉”,我就不明白:一个是生物学与卫生学的概念、一个是材料尺寸的纳米怎么会防霉?所以,要千万注意,无知的人骗起人来不得了,因为他胆子大,什么都敢说,所以说:“无知才无畏”,我们懂一点的就不敢随便讲,这是误导!现在,让科学界的人感到很困惑:许多与纳米研究无关,不具备纳米科研的单位都在上纳米项目,开设了许多新的战场。作为商界的人士,这个投入是风险投入,所得到的绝对回报是非常危险的。越是高科技的东西,越不要指望它的市场回报,这个概念是我今天提出的。
新闻界的人在干什么?新闻界的同志喜欢写一些新东西。我知道,没有新东西写起来不精彩。误导与新闻界有关,新闻界听到一点就是一个片,如此描述宣传以致于浙江的报纸整版整版地写纳米。我特别地告诫大家,这是把整个一个学科的概念搞得非常庸俗!
下面我谈谈科技问题。我们整个国家民族工业的基础极其脆弱,这几年我在日本商店购物,日本的袜子、手巾、低档毛衣、低档服装全都是中国产的,这是否感到光荣?
其实,我们所谓的外资合营企业,所引进的东西,真正有科技的含量极少,这就是我们的天真,就是我们领导的天真!
比如,东京这么大的城市,全部的电视频道就只有6个,杭州有多少个?这是很清楚的。拿什么钱吃什么饭,我们现在是在吃国家的饭,所以,开了这么多频道不怕赔也不怕赚,而日本就不能这样。
我在日本10天,没有看到一条有关中国的消息,这也是中国人的天真。广岛亚运会在日本召开时,有哪一个中国人得金牌的镜头能在日本的电视上看得到?
日本人喜欢下围棋,但他不知道聂卫平是谁。我国现在是需要国际化,但国际化了以后连自己的祖宗是谁都不知道了,连自己的民族文化是什么都不知道了,所以我要说,改革开放20年,我们到底在经济上,科技上得到了什么?
好的是有的,但作为一个科学家,我要深思:我们在高科技上得到了什么?日本、韩国、美国基本都是把国内不能生产的、低价的东西转移到第叁世界国家来生产。你并没有得到高科技,你以为他会给你高科技吗?不会的!
比如汽车,我们联营了这么多家,但日本的汽车技术,比如丰田,都是第二代、第叁代之前的,他不会给你先进的!为什么现在柯尼卡、柯达、富士在中国“大跳水”? 就是要挤跨乐凯,这个中国现在剩下的唯一一个国产胶卷!等哪一天乐凯垮掉了,国外的胶卷就会全部涨价!
现在在中国卖的进口胶卷比日本、美国的都便宜,这就是倾销!这一点中国人并不知道。
为什么我们的大学排不进前200名?因为规定要有800篇FCA的论文才能进入前200名。为什么浙大好不容易搞了10年的科研却没有钱?教授的论文写得少了,平均一个教授没有一篇,像我这样一年能写5、6篇的很少。为什么?中国的教授一个月的工资平均只有1500元,相当于170美元;美国的教授拿多少工资?他的一篇文章值多少钱?可见中国教授便宜了!美国教授成本高!
为什么会是这样?所以,我非常担心这样下去,再过5年、10年,你到底还有些什么东西?这样恶性循环后,我们基本上没有自己的工业了!我不赞成“造船不如买船,买船不如租船“,过若干年再看看,将会是什么样子?
下面谈谈我个人的观点:第一,我们国家的现实和发展就是这样:凡是依赖不成的,我们自己都能搞得像模像样,比如二弹一星;凡是能够引进的,就都搞不成。为什么?
企业的科技人员辛辛苦苦地搞一个技术革新,只要区里的计委领导、省里的计经委领导哪一天带着人到美国去考察一下,买来几个电器产品,跟他们签个协议,然后再到美国去培训10天,引进一条线,马上就可以把你这个国营企业打跨,这就是现实!现在很多合资企业就这样,卖点东西,而没有去考虑这些深层次的东西。殊不知,这就是社会的恶性循环!
关于电信的事,我只跟电信部门这样说:这点电话费我付得起!但是,你们想过没有,你们从自己的这个角度赚了消费者的钱,但消费者里面有大夫,你电信的人就不生病吗?你若进医院,他给你卖高价药行吗?
我开玩笑:“你别看我是个穷教授,赚不了钱。我今天回去就去查名单,看我这个学院、我这个系的学生有多少是你们电信部门的子女,到了下个学期全部不及格!若要及格,每分交5000元钱。“但我决不会这么做!若这样做,这个世界就乱了。作为社会成员,一定要有这样的思想:大家是互相依存的,我们这个国家也同样。
但是很多人只考虑自己,只要自己赚钱而不管别人怎样,若那样的话,明天就可能得到报应。这是我的一个观点。下面这几个问题是最重要的问题:中国需要什么样的教育?中国需要什么样的人才?中国需要什么样的科技?中国需要什么样的产业?教育、学习是为了什么?
最近,幼儿园、小学、中学、大学以及饭店、各个厂矿企业等单位都邀请我去演讲,这是我应该为社会做的,是我的本份,加上我身兼数职:院长、系主任,所长等,他们开口就要找博士。其实,这需要共同语言受教育的层次、人格的素质,这才是最重要的!
现在很多人对学生的教育没有注重这个方面。中小学在搞素质教育,什么叫素质?英语、计算机、钢琴都会一点儿能算是素质吗?我住的那层楼的孩子都在弹钢琴,我就听到二楼的一个男孩子在弹《致爱丽丝》时,边弹边哭、边骂他妈妈。爸爸妈妈都是音盲,却一定要把孩子培养成音乐家。这么好的太阳,让孩子坐在屋里,他能受得了吗?这是何必呢?幼儿园3岁开始学英语,我这个观点不知你们是否赞同,我在学校讲课时,学生们听了觉得我所说的正符合了他们的心意。
我认为:语言、计算机就是工具。中国的外语教授讲英语还不如美国卖菜的农民!怎么看待这个问题?日本博士、德国教授说不出英语的多得是!我们怎么能说一个人不会说英语就是文盲呢?语言就是一个工具!你没有那个环境,他怎么能讲这个语言呢?比如,我35岁开始学日语,我现在的日语是顶呱呱的。但是,我在国内进修一年,派日本教授来教我,可我就是学不好,非常紧张。因为年龄较大了,学得我白天的语法都错了,晚上做梦全讲日语,且这时讲的语法都是对的!到了日本后,我是在实验室工作,而打工的人2个月学的日语就比我学得溜,这主要就是因为没有环境。
所以说,如果我是教育部长,我要改革二件事:第一,取消六级考试,你一个研究生连中文一级都不及格,你英文考六级干什么呢?看看研究生写得论文,自己的民族文化都没有学好,天天考英语──打勾:托福打勾,GRE打勾,英文考出很高的分,可哪个写的英文论文在我面前过得了关呢?过不了关!这样培养出来的人能干什么? 自己搞的专业一点都没学好!
我跟在座的老板们提醒一句,等下我要讲一下人才使用问题,你们现在的招聘动不动
就要英语好,干什么呢?在厕所里讲英语?需要吗?要计算机好?说不会计算机就是
文盲,这又是一个误区!我现在是教授,我顾不上搞计算机,可是,浙大搞计算机的
就特别敬佩我、巴结我:“郑强,你材料搞得这么好,我给你点计算机,你去用用玩
玩,今后搞材料与我们合作合作,我们也可沾点光。“
物质世界不是算出来的,算能把肚子算饱吗?现在我国搞了点软件,很多精英──年轻人都跑到计算机行业去打工,自己成不了材,可惜啊!不像我一直在做材料的教授,光荣得很!我们浙大就一个计算机院士潘云鹤,浙大的计算机在全国排在第几呢?浙大最好的学生都去学计算机了,我经常呼吁:这是在害人!
我的同学现在在美国都在卖菜、卖中药,成不了材,他们现在倒是非常羡慕我在国内搞得这么好。他们的钱是稍微比我多一点,但是我现在在中国过的日子比他们在美国过得好!由此可见,这不是钱就能解决的问题。
我到日本留学时受了许多苦,这次我到英国大学见到了十几个中国留学生,他们都向我诉苦:每天只睡4个小时!浙大有很多老教授,夫妻两个连走路都走不动了,孩子却在美国。
我认为中国人的民族心理这几年扭曲到什么程度了!外国人不理解,认为中国人有病:为什么夫妻不在一起,而是一方在国外、一方和孩子在国内,中国人这是何苦呢?!父母老了,是否需要人服侍?在国外的,是否想念父母呢?回答是肯定的,但不知是为了撑什么面子?!
中国穷得这副样子,我可以公开告诉大家,我这次是跟浙大的最高人物一起去日本的──因为我在日本留了这么长时间的学,在日本,我只能请他吃一碗面:一天晚上,请了8个教授一个人一虢油面,就花了我1000元人民币!所以,我在日本就呆不住,像我这样的人,我现在就不愿出国,去10天可以,去1个月我就受不了!
言归正传,现在的孩子学英语,但父母都不会讲英语,我的一个朋友的女儿在美国学
了英语,回来就忘记了,这就是语言的特点。语言没有环境,就学不好。所以,第
一,要取消六级,让孩子们放松;第二,大学一年级开始的叁个月像军训那样突击一下英语,马上过关,然后就任其自然。
你看我,从高中开始学英语,大学学,硕士学,博士学,花了我多少精力!你说中国人怎么做得出高科技的研究成果?我这几天就教训我手下的几位女学生,问她们在干什么,看不到人影,一天到晚考这样、考那样的,到美国去干什么?在国内要干的事多着呢!你整天考英语,美国人连报个名都要收你们的钱,日本人也是如此,中国学生到日本去要交手续费,到日本留学是为日本人打工,好不容易挣点钱交了学费,读完博士在日本的公司就职,当劳动力,挣了一笔钱后要回国了就买了家电,把钱全给了日本人。你们都没有注意这件事,这里面都是经济问题。
这就是素质教育到底是什么。现在有这样一个现象:大学叁年级时有42名学生,毕业时只剩15人了,其他全部不能毕业。这是为什么?就是因为他们在大学里玩。这就是中小学教育的失败!中小学的教育就是听话,老师管干部,干部管同学,孩子们都学会了成年化的处世方式,这是害人啊!这样强迫性地做了一些好事后,没有把做好事与做人准则结合起来去培养,而只学会了拍马屁、讨老师喜欢、说成人话。上次电视上就曾经播出,一个小孩得了个奖,主诗人问他最愿意说什么,他说:“我最愿意跟江爷爷说:我向你报告!““江爷爷”是谁?还不是老师教的!
孩子们在中小学活得很累,到了大学就没人管了,所以就要玩、就要谈恋爱。我们系有个男生,跟四川一个女孩谈恋爱,前几天班主任向我汇报,上个月跑回去20天看他女朋友,这还得了!这是为什么?这就是从小教育的结果!我的儿子在班上就有6个女同学有“记录”的特权,记录哪个男同学动了。有了6次记录后,男同学就得写检查,家长也要跟着写检查。
我第一次写时很难受,怎么能做这种事呢?后来就习惯了。到了星期五就问儿子:
“这个星期要不要写?”反正脸皮也厚了,写就写吧。问题是,实际上他只违反了一次纪律就同时被6个女孩子看见了,这也算6次!但是,我并不为我儿子担心,我认为:他受这点挫折也好、压抑也好,对他一生的成长有好处;而我恰恰担心的是那6个女同学,以后怎么能够经历得起打击和挫折!这样的教育是很令人深思的。
所以,我作为一个博士生导师,我从来是看人不看学历的,学历不等于能力。你们现在的招聘动不动就要高学历,我就是要批判这样做。我提出一个命名,叫“消费学历“──就是滥用学历。现在提拔干部也同样,不看他的身体,到了60岁,有些身体特别棒且有能力的人也要让他退休;而有的人叁、四十岁得了肝炎,还得让他干,这就是“一刀切”。
招聘时也总是看学历。学历是指一个人读书还可以,并不能代表他能够当你的经营人员、开发人员。我们有很多同学成绩好,却什么都做不了。在我们大学像我这种程度的人,招博士生是从来不看成绩的,成绩算什么!现在我从事的这个领域在中国有叁个杰出的人才,当初在读研究生时都补考过,而成绩考得好的几个人却都跑到美国去卖中药了,这说明了什么问题?作老板的可不能这样啊!
现在浙大有规定,有博士点的,留校就必须全部留博士生而不准留硕士生,这样,仪器就没有人去操作。人才的梯队一定要合理,而不要认为教授就是万能的、博士就是万能的。中国的教育体系就是让每一个老百姓都充满希望和理想,教育孩子们要树立远大的理想。
实际上,人的能力是不一样的,扫地能扫好,也应该受到尊重;打扫厕所能打扫干净,也应该受到尊重,不能动不动就要高学历。我要提醒的是:在国外可不是这样,反之,美国、日本的博士就很难找到工作,为什么?因为老板心疼钱,招博士要给他高工资,而他能做什么用呢?这是个具体问题。我不知道现在的组织部长、人事处长在干什么,真的是在“选女婿”吗?找这么高学历的人干什么呢?
现在,中国的大学提出要培养高层次的人才,我说这话错了,中国现在教育特点应该是让全民得到教育,而不是去培养少数的专门人才。上次全国的化学奥林匹克竞赛在杭州举行,是浙大主办的,学校说来了这么多高校的人挖人才,浙大也要派人去挖,于是派我去了。我去讲了话,我毫不客气地说:进这扇门,我的心情是又高兴又沉重。你们把孩子们搞坏了!为什么?我国搞奥林匹克竞赛──中小学叫奥赛班是举国体制,就是为了得到世界上的一个荣誉!而在美国、日本、西欧国家就只有一个学校,叫“Play- again”就是搞着玩的。难道有一个学生得了奥林匹克的冠军,就说明中国的教育好吗?
不是,它不能代表我国的真实情况。在那些非重点学校里,有多少孩子在外面赌博、打游戏机!这就是我国教育的一种误区。我指出:如果作为一个教授来做这样的事,在座的人都感到心痛!作为父母,我们绝不能这样宠爱自己的孩子,把这些孩子当宝贝一样。化学的奥林匹克竞赛,清华、北大的教授来了一大堆,我们今天是在做一件害孩子的事!
我的话讲完后,主诗人要下面哪个大学的教授接着讲,他们都不敢上台讲了。请扪心自问:我们这样做对吗?奥林匹克竞赛的结果在浙大的一个现象是:在中学学得好的、保送的,到了大学叁年级成绩都降下去了,孩子这么小,怎么能分等级呢?其实,他们根本就是在同一个起跑线上的,成长的路程还长得很,后天的努力才是最重要的。所以,我始终对孩子、对学生都是鼓励的。我们系里有一个女同学,我看到她一天到晚没有笑脸,就是为了得到那个高分数、为了得到高额的奖学金,我感到很痛心!我说:你这是在用青春买荣誉、买光环,你今后的心灵是要受到创伤的!我希望她不要看重明年从第1名掉到第2名,后年从第2名掉到第3名。我这个老师当得怪吧,我不是要求她往上走,而是往下走,我这是在救她!
昨天,我的孩子参加环湖跑,我对他说:“你不要去争第1名,慢慢跑。”不是说我不要孩子上进,而是这些老师在干什么?让5-6岁、7-8岁的孩子跑4000米,为了争得那个第一,把孩子的身体都跑坏了,我之所以长的矮,就是因为我在5岁时早晨起床跑步,骨质过早钙化了。现在懂科学了,就知道了,小孩子不能随便大运动量地运动。
可是求是小学每天早上第一节课就是把全校的孩子弄到街上去跑步,刚刚吃了早饭,能量还没有发挥,能量是要用到脑子上的,要上一天的课。可早上的跑步就让能量消耗了,这不科学!还有,沿着马路跑步,汽车尾汽对孩子的健康是很有损害的。这样的教育到底是为了什么?
我敢讲一点:我们浙大有很多教授就被小学老师教育得像儿子一样,小学老师每次开会的第一句话就是:“亲爱的家长同志们:孩子是你们的,也是我们的,但是归根结底还是你们的!“每次都讲这些话,然后就说:“你们不懂科学、不懂教育。”我心里就在想:孩子都是你们教育了送到我们手上来的,就非常担心,孩子的心灵从小让你们这么一整就麻烦了。
我觉得我现在最对不起的就是我小学、中学、大学的同学们。我从小干部当惯了,一直在管人,到现在我心里觉得最对不起的就是那时被我管过的这些同学,不管他们现在是在开出租车还是在干啥,都非常有成就。所以,怎样把孩子培养好,从小让他有一个健康的心理,这比成绩还重要。
何况各位家长,你们由于自己的学历低些,总是希望孩子出头,我也知道有些家长在双休日的上午、下午、晚上都要安排孩子参加各种各样的学习,把孩子完全拖垮了,其结果孩子是要厌恶的。过早弹钢琴的人,除非他真正是个音乐家,十有八、九到了中途都会厌恶,这就是逆反心理学。所以,你们不能这么干,这么干是摧残人才,这是我今天讲的,为人才的培养谈一些自己的看法。
科技到底该干什么?高科技到底该干什么?如果我是科技部长,该玩的就玩,就像陈景润,他就是玩。陈景润如果是处在今天的中国,他绝对是要去讨饭的,因为他不会去搞产业化,他的英语也不好,他说话都不流利,中文都讲不好,按现在“标准”,他是个文盲,还谈什么教授!
日本人就是喜欢美国人,我跟日本人说:你们这个民族爱谁,谁就要向你们扔原子弹。日本人就是喜欢黑人也不喜欢中国人。现在在日本奖学金最高的是美国人、欧美人;第二是韩国人、台湾人;第叁是巴基斯坦人、马来西亚人;第四是印度人、非洲国家的人;第五才轮到中国人。
我就特别对我们的女教授、女同学说:在日本人面前一句日文都不要讲,会也不要讲;日本人一听说你讲英文,特别是看到中国女孩讲英文,腿都要发软,这是真的! 日本人不知道龙井茶,而只知道乌龙茶,就因为旭日升乌龙茶的广告宣传。日本人在开始做乌龙茶广告时找了6个最漂亮的中国女孩,日本人就从这个广告得出一个结论:
中国女性的漂亮是因为喝了乌龙茶。所以,日本人特别崇尚乌龙茶,而不知道龙井茶。
日本就是崇尚欧美,谁能讲英语,谁就是老大。日本首相森嘉郎不会英语,但为了表现自己会英语,就叫秘书安排了一套程序,准备在冲绳开政府间合作会议时,见到克林顿用英语问候一下。但是日本人讲英语很糟糕,森喜郎见到克林顿,将“How are you!”说成了“Who are you?”,克林顿以为这是森喜郎在跟他幽默:我明明是美国总统,他还不知道吗?他为什么还这么问我?我今天也跟他来个幽默:“I am husband of Hilary.”(“我是希拉里的丈夫”),森喜郎不管克林顿怎么说,就忙按照程序回答:“Too me.”、“Me too.”,克林顿想你这个玩笑开得太大了,我老婆怎么变成你老婆了?这在日本就成了个大笑话!
最近,我们在办高新科技园区,就国家的投资而言,我们的领导的意见往往是去追世界潮流:想去建世界一流的国家,达到一流的水平。我非常惭愧地向大家谴责一下自己:我们中国的教授很多拿了国家的钱就像小孩搞家家一样,把钱用完了,就写点文章,塞在抽屉里,一点用都没有,就向领导报告:我做完了。这很对不起国家的钱! 说实话,应该弄清楚哪些是搞着玩的,哪些是对企业有帮助的,这一点我们没有做好。日本、韩国的科技进步就是在于针对性非常强。现在有一个现象:所有的企业家到了浙大就问我:“郑教授,现在有什么能赚钱的项目?给我们吧!”其实,你们太不了解了,教授是完全是远离市场的,根本不知道市场是什么!他只是在搞游戏,玩家家,想一些新玩意,不要以为教授什么都有!
当然,这不能怪你们,因为今天中国的生产力的发展,还没有到让我们的老板们具备很高的意识。刚才何先生就讲得很好,在目前的阶段,中国还有假货的话,就不能指望做假的人有什么超前的科技需求,他赚一把是一把。但是,当中国的经济秩序真正完善以后,造假绝对活不下去!现在在日本就没有人敢造假,只要稍微有一点点造假,马上就完蛋。中国总有一天要进入到有序的阶段,一有序,造假的人肯定活不下去。企业能不能生存,说到底就是看你有没有绝招。这个绝招,第一是产品的特色;第二是科技的含量。上次我毫不客气地对张德江书记说:浙江要建经济大省,文化强省,但是“大”不等于“强”,“民富”不等于“国强”。科威特有的是钱,伊拉克要打垮它就打垮了,这是很简单的事。我说:经济强省应该有下列标志。我问张书记:第一,浙江省现在的经济总量中有多少具有科技的含量?第二,浙江省目前的经济有几个是关系到中国国民经济命脉的、民生的、大的工业?第叁,浙江的经济真的有一天在亚洲金融风暴来临时能抗得住吗?这些浙江省都有问题。浙江的市场发育得很完全,但如果我是外省人,如果哪一天我自己那里方便了,我为什么还要跑到你这儿买?现在之所以有市场,是因为浙江市场里卖假货的很多,要么走私、要么卖假,这几年就是这样发起来的。如果不是这样,再过几年,辽宁的人要货在北京买就行,何必再跑到你浙江来买,如果不到你这儿买你怎么办?
现在浙江称为塑料大省,全国叁分之一的塑料制品产在浙江,上次在余姚,就召开过塑料科技大会。浙江的西服很多,像杉杉西服等,还有娃哈哈。但是,请各位注意:
西服、果奶不是民生大计!千岛湖的农夫山泉是好,但水什么地方没有?这就是问题。另外,温州经验好不好?好!但如果我是国家主席,我可不会在全国搞温州经验。温州经验说到底是民族经济全部被国外吃掉。你的资本算什么,温州老板的那点钱与日本老板相比就不算钱,马上会被吃掉,而且技术含量极低。我去了温州好几次,看到的都是家庭工厂,与现代企业能比吗?此外,把西部的博士挖过来,用高薪养起来,这些都不能算是集约经营和规模经营,这些都是危机!但是我们却尽唱高调。那天,我最后是带着感情与张德江谈的:我是爱浙江的,作为一个教师,我教育学生习惯了,我认为对待学生最好的方式就是从教育的角度讲他,而不是去夸他“长得好“、“今后要当科学家”,这都是害人的话。所以我愿意对张书记提点意见。
再进一步,老板会提问:“郑教授,我现在做的这些,你看看结合你的知识能否给我改进一下?“我认为这样的提问是最好的。上次我们青年教授到天台去,天台是个穷地方,前面11个教授都讲IT产业,讲得那些企业家第二天都要来搞通讯行业了。我最后发言:前面的人讲的话又把你们害苦了。你们这么穷的地方怎么搞得过宁波、杭州?你们应该把本地的东西通过我们的先进思想和技术改造好,做得更好一点。所以我希望我们的企业家在下次见到教授时提出这类问题,你们就进步了。真正的科技产业进步的源泉在你们,而不在教授。当你们有钱了、有产业规模了该怎么办,就应该学学日本。我国现在到底需要什么?软件搞得这么红火,但我们并不需要软件。其实,我们现在的软件搞不过印度。美国硅谷一大半的人才是从印度去的。但印度的国防部长常常惊骇这叁、四年来与中国的差距──他是从奥运会得了一块金牌谈起的,民总产值才是中国的叁分之一,人口与中国差不多,而粮食产量却连中国的一半都不到。中国现在最需要的是材料,是制造与控制。不要以为我们什么都有,我这次到韩国看了真是痛心,在韩国的街上见不到一辆外国的进口车,绝对没有!我又听说现代企业的生产量比我国的桑塔纳、长春、武汉、富康的总和还多,且他们所有制造汽车的精密仪器全部都是国产的。而我们的这些部分却全部是进口的,没有一样是自己的。人家想做什么,什么都能做好,我们却是想做什么,什么都做不成。归根结底,不是靠我们的软件,搞计算机的人什么都会玩,上次到天台演示房子设计的动态,把房地产公司的人看傻眼了。其实,这都是哄人的,房子要造起来还是要靠材料,靠造房子的人。
现在我国在这两方面是最薄弱的。高等学校投了几十个亿买研究设备,但这些设备90%都是进口的,没有一样是国产的。买来的时候觉得很了不得,可以哄领导,但是若有哪个学生把仪器弄坏了,就死了,我们国内的企业没有一家敢修仪器的,即使一个很简单的实验仪器没有一家工厂能够生产,这就是我国现在整个民族工业的一个缩影!我们不愿意踏实地来做这些工作,而只是做了很多表面文章,既生产不出材料,也没有先进的制造工艺。我的这个观点得到了大多数教授的拥护。但是,要求省里的科委主任把钱投到材料和制造工艺这两方面,他就不干,他要做世界第一流的“纳米“。说到底,这都是些表面现象,我们现在最需要的是人们的一种坚韧不拔的精神,我们民族已经没有这个精神了。我们现在想的只是“卖得越多越好“。
这次校长、书记在日本,见到东京名古屋大学的校长,他提了个问题:我们都属于东方文化,现在就要召集大家来讨论怎样保护东方文化,保护东方文化的特色。大家知道,生物是多样化的,世界上若只有美国人就没有味道。日本人说:“我们培养的很多人也到美国去了,当是他们很惊讶,你们的7位中国校长一致认为这种现象没有关系,这是国际化。“日本人这番话引起了我的深思:我们中国没有一点本钱来谈我们的人才储备敢与日本比,但是,我们所表现出了一种莫明奇妙的胸怀。比如,日本的许多大学为什么能得到许多捐款,就是因为捐款的人对母校有感情,对社会有报答。哪一个日本大学为日本的财团或企业输送的高级人才越多,所得到的奖学金就越多。
现在,教育界有一种反思,清华大学自建校以来从来没有像今天这样完全实现了她当初的办学宗旨:“成为美利坚合众国的预备学校”。如果我是中国的纳税人,我怎么能想得通,我国国民花那么多钱投入到这个学校,可它培养的人却都跑到美国去了,为他们服务了。可是你有没有看到美国政府为我国哪个大学捐过点钱?你们体现出这么一种胸怀是什么意思呢?
你们到韩国、日本、新加坡去看看,你们就知道中国人在精神上垮到了什么地步!我恨日本人,我到日本是作为日本大使馆面试的中国最杰出的博士生去的,当时在四川只选了我一个人,我到日本拿的奖学金都被告知不要告诉日本人,怕日本人嫉妒,他们就是要培养汉奸。但是我到日本是越培养越恨,神□大地震时我在日本,我是中国留学生的领导,那时死了许多中国人,其中杭州人最多,就因为日本人先救日本人,根本顾不上救中国人!在日本空港,日本国民的进港通道有8个,而仅给外国人留一个,你得排队,等日本人全部走完了,他再转换牌子,让外国人再进来。
中国人为什么这些年都往外跑,最重要的是要让国民自己爱自己国家。在广西,美国人的骨头埋了几十年,还叫中国农民去找,把美国人的骨头找到了,放在棺材里,送回到白宫,举行隆重的仪式、行军礼,这怎么能让美国人不自豪?反之,当找美国人骨头的中国农民在寻找时摔了一跤,骨头摔坏了,给200元钱就打发回家了,连“劳动模范“都没给人家,你怎么能让你的国民爱这个国家,有自尊的呢?!
如果我是杭州的市长,我绝对不是狭隘的民族自尊心──如果杭州有什么灾难,我就首先把杭州的老百姓安排在香格里拉,让外国人在外面排队!(掌声!)这样,你才会让你的国民爱自己的国家!
一个日本的农民跑到峨嵋山去玩,骨头摔断了,你就用中国空军的直升飞机去救他,而在日本大学一名中国留学生在宿舍里死了7天才被发现;名古屋大学的一对中国博士夫妇和孩子误食有毒磨菇,孩子和母亲死了,父亲则是重肝炎,在名古屋大学医院的门诊室等了12个小时,也没有一个日本教授来看望!而你们为什么还要这么友好,以为自己很大度,实际上是被人家耻笑,笑你的无知!你们这个民族贱!我们不能这样!
我们的领导人跑到国外去访问,看到有几个人在欢迎他们,就感到挺有面子;而外国来了个什么人物,都是警车开道,这究竟是怎么回事?这让我们中国人感到是自豪还是悲哀?
所有这些,对教育工作来讲,都是深层次的问题。所以我经常讲,我作为一位自然科学工作者,我教育我的学生,首先是学会做人,没有这些,你学了高分子,外语都是花架子,你不是一个完整的人。怎样建立一个科技体系?在国外,这个部分不是大学办的,我坚信,目前“教授+商人”的这种状态,过20年后会改变。在国外,没有哪个教授是既当公司经理又当教授的,这种现象极少,只有中国有!这就算是国情吧。一个人的精力是有限的,既要办公司,又要当教授,能当好吗?我不否认有这类特殊人才,很少见。关键是把大学教授神化了,让他们全部面向产业界,缺少了中间地带,这是中国目前最大的一个缺陷。认为今后企业改革的方向应该是把他们直接作为企业法人结合在一起。
日本叁菱公司的研究院,富士研究所在日本是具备最好条件的研究院,他们不发表文章,就是专门做能与产业结合的项目。这是一句口号:“吃着、端着、看着”,“吃着的“――企业正在干的;“端着的”――产业开发做的;“看着的”――前瞻性的东西、超前的学术研究让大学去做,这就是一个良性循环。但是,要做好这件事,我认为财团和经营界的介入也是非常重要的。同时也要建立一种体系,让这些信息能够互通与共享。
我今天讲的,一个是高分子的介绍,另外是把我对科技、教育、社会等方面的一些思
考奉献给大家。主要是跟大学一起交流,有不对的地方希望在座的各位老同志、各位
企业家、各位领导对我批评指正。
--sunfruit
空间数据库Oracle Spatial的建立过程如下:
-- 创建最基本的个人兴趣点表结构
drop table poi;
create table poi (gid INTEGER,
opid INTEGER,
gname VARCHAR2(256),
gshape MDSYS.SDO_GEOMETRY);
-- 更新用户空间数据对象视图(建立索引依赖她)
delete from USER_SDO_GEOM_METADATA where TABLE_NAME='POI' and COLUMN_NAME='GSHAPE';
insert into USER_SDO_GEOM_METADATA values ('poi',
'gshape',
MDSYS.SDO_DIM_ARRAY(MDSYS.SDO_DIM_ELEMENT('lon', -- lontitude
-64800000, -- min(china 26430867 73.41907434101486)
64800000, -- max(china 49679991 137.99997381765377)
1), -- scale (china abs 23249124)
MDSYS.SDO_DIM_ELEMENT('lat', -- latitude
-32400000, -- min(china -1677502 -4.6597267116858045)
32400000, -- max(china 21571819 59.92171939467364)
1)), -- scale (china abs 23249321)
NULL);
-- 插入一个个人兴趣点的SQL语句,使用标准点地物空间数据类型
delete from POI;
insert into POI values (20010001,
1,
'我的家',
MDSYS.SDO_GEOMETRY(2001, -- SDO_GTYPE
NULL, -- SDO_SRID
SDO_POINT_TYPE(41884696, 14377039, NULL), NULL, NULL));
-- 插入一个个人兴趣点的SQL语句,使用另一种点地物空间数据组织结构
delete from POI;
insert into POI values (20010001,
1,
'我的家',
MDSYS.SDO_GEOMETRY(2001, -- SDO_GTYPE
NULL, -- SDO_SRID
NULL, -- SDO_POINT
MDSYS.SDO_ELEM_INFO_ARRAY (1, -- SDO_STARTING_OFFSET
1, -- SDO_ETYPE
1), -- SDO_INTERPRETATION
MDSYS.SDO_ORDINATE_ARRAY (41884696,14377039)));
-- 创建缺省的R-tree空间索引
drop index POI_IDX;
CREATE INDEX POI_IDX on poi(gshape)
INDEXTYPE is MDSYS.SPATIAL_INDEX;
-- PARAMETERS('SDO_LEVEL=10000'); -- 180*60*60*1000*2/100/100*90*60*60*1000*2/100/100 = 8398080000
-- 索引粗滤矩形窗口选择SQL语句(对于点地物对象,索引粗滤的结果是精确的)
SELECT * FROM POI P
WHERE sdo_filter(P.gshape,
mdsys.sdo_geometry(2003,NULL,NULL,
mdsys.sdo_elem_info_array(1,1003,3),
mdsys.sdo_ordinate_array(41883696,14376039, 41885696,14378039)),
'querytype=window') = 'TRUE';
-- 精确匹配矩形窗口选择SQL语句(计算非常耗时)
SELECT * FROM POI P
WHERE sdo_relate(P.gshape,
mdsys.sdo_geometry(2003,NULL,NULL,
mdsys.sdo_elem_info_array(1,1003,3),
mdsys.sdo_ordinate_array(41883696,14376039, 41885696,14378039)),
'mask=INSIDE querytype=window') = 'TRUE';
Java虚拟机的深入研究
作者:刘学超
1 Java技术与Java虚拟机
说起Java,人们首先想到的是Java编程语言,然而事实上,Java是一种技术,它由四方面组成: Java编程语言、Java类文件格式、Java虚拟机和Java应用程序接口(Java API)。它们的关系如下图所示:
图1 Java四个方面的关系
运行期环境代表着Java平台,开发人员编写Java代码(.java文件),然后将之编译成字节码(.class文件)。最后字节码被装入内存,一旦字节码进入虚拟机,它就会被解释器解释执行,或者是被即时代码发生器有选择的转换成机器码执行。从上图也可以看出Java平台由Java虚拟机和Java应用程序接口搭建,Java语言则是进入这个平台的通道,用Java语言编写并编译的程序可以运行在这个平台上。这个平台的结构如下图所示:
在Java平台的结构中, 可以看出,Java虚拟机(JVM) 处在核心的位置,是程序与底层操作系统和硬件无关的关键。它的下方是移植接口,移植接口由两部分组成:适配器和Java操作系统, 其中依赖于平台的部分称为适配器;JVM 通过移植接口在具体的平台和操作系统上实现;在JVM 的上方是Java的基本类库和扩展类库以及它们的API, 利用Java API编写的应用程序(application) 和小程序(Java applet) 可以在任何Java平台上运行而无需考虑底层平台, 就是因为有Java虚拟机(JVM)实现了程序与操作系统的分离,从而实现了Java 的平台无关性。
那么到底什么是Java虚拟机(JVM)呢?通常我们谈论JVM时,我们的意思可能是:
- 对JVM规范的的比较抽象的说明;
- 对JVM的具体实现;
- 在程序运行期间所生成的一个JVM实例。
对JVM规范的的抽象说明是一些概念的集合,它们已经在书《The Java Virtual Machine Specification》(《Java虚拟机规范》)中被详细地描述了;对JVM的具体实现要么是软件,要么是软件和硬件的组合,它已经被许多生产厂商所实现,并存在于多种平台之上;运行Java程序的任务由JVM的运行期实例单个承担。在本文中我们所讨论的Java虚拟机(JVM)主要针对第三种情况而言。它可以被看成一个想象中的机器,在实际的计算机上通过软件模拟来实现,有自己想象中的硬件,如处理器、堆栈、寄存器等,还有自己相应的指令系统。
JVM在它的生存周期中有一个明确的任务,那就是运行Java程序,因此当Java程序启动的时候,就产生JVM的一个实例;当程序运行结束的时候,该实例也跟着消失了。下面我们从JVM的体系结构和它的运行过程这两个方面来对它进行比较深入的研究。
2 Java虚拟机的体系结构
刚才已经提到,JVM可以由不同的厂商来实现。由于厂商的不同必然导致JVM在实现上的一些不同,然而JVM还是可以实现跨平台的特性,这就要归功于设计JVM时的体系结构了。
我们知道,一个JVM实例的行为不光是它自己的事,还涉及到它的子系统、存储区域、数据类型和指令这些部分,它们描述了JVM的一个抽象的内部体系结构,其目的不光规定实现JVM时它内部的体系结构,更重要的是提供了一种方式,用于严格定义实现时的外部行为。每个JVM都有两种机制,一个是装载具有合适名称的类(类或是接口),叫做类装载子系统;另外的一个负责执行包含在已装载的类或接口中的指令,叫做运行引擎。每个JVM又包括方法区、堆、Java栈、程序计数器和本地方法栈这五个部分,这几个部分和类装载机制与运行引擎机制一起组成的体系结构图为:
图3 JVM的体系结构
JVM的每个实例都有一个它自己的方法域和一个堆,运行于JVM内的所有的线程都共享这些区域;当虚拟机装载类文件的时候,它解析其中的二进制数据所包含的类信息,并把它们放到方法域中;当程序运行的时候,JVM把程序初始化的所有对象置于堆上;而每个线程创建的时候,都会拥有自己的程序计数器和Java栈,其中程序计数器中的值指向下一条即将被执行的指令,线程的Java栈则存储为该线程调用Java方法的状态;本地方法调用的状态被存储在本地方法栈,该方法栈依赖于具体的实现。
下面分别对这几个部分进行说明。
执行引擎处于JVM的核心位置,在Java虚拟机规范中,它的行为是由指令集所决定的。尽管对于每条指令,规范很详细地说明了当JVM执行字节码遇到指令时,它的实现应该做什么,但对于怎么做却言之甚少。Java虚拟机支持大约248个字节码。每个字节码执行一种基本的CPU运算,例如,把一个整数加到寄存器,子程序转移等。Java指令集相当于Java程序的汇编语言。
Java指令集中的指令包含一个单字节的操作符,用于指定要执行的操作,还有0个或多个操作数,提供操作所需的参数或数据。许多指令没有操作数,仅由一个单字节的操作符构成。
虚拟机的内层循环的执行过程如下:
do{
取一个操作符字节;
根据操作符的值执行一个动作;
}while(程序未结束)
由于指令系统的简单性,使得虚拟机执行的过程十分简单,从而有利于提高执行的效率。指令中操作数的数量和大小是由操作符决定的。如果操作数比一个字节大,那么它存储的顺序是高位字节优先。例如,一个16位的参数存放时占用两个字节,其值为:
第一个字节*256+第二个字节字节码。
指令流一般只是字节对齐的。指令tableswitch和lookup是例外,在这两条指令内部要求强制的4字节边界对齐。
对于本地方法接口,实现JVM并不要求一定要有它的支持,甚至可以完全没有。Sun公司实现Java本地接口(JNI)是出于可移植性的考虑,当然我们也可以设计出其它的本地接口来代替Sun公司的JNI。但是这些设计与实现是比较复杂的事情,需要确保垃圾回收器不会将那些正在被本地方法调用的对象释放掉。
Java的堆是一个运行时数据区,类的实例(对象)从中分配空间,它的管理是由垃圾回收来负责的:不给程序员显式释放对象的能力。Java不规定具体使用的垃圾回收算法,可以根据系统的需求使用各种各样的算法。
Java方法区与传统语言中的编译后代码或是Unix进程中的正文段类似。它保存方法代码(编译后的java代码)和符号表。在当前的Java实现中,方法代码不包括在垃圾回收堆中,但计划在将来的版本中实现。每个类文件包含了一个Java类或一个Java界面的编译后的代码。可以说类文件是Java语言的执行代码文件。为了保证类文件的平台无关性,Java虚拟机规范中对类文件的格式也作了详细的说明。其具体细节请参考Sun公司的Java虚拟机规范。
Java虚拟机的寄存器用于保存机器的运行状态,与微处理器中的某些专用寄存器类似。Java虚拟机的寄存器有四种:
- pc: Java程序计数器;
- optop: 指向操作数栈顶端的指针;
- frame: 指向当前执行方法的执行环境的指针;。
- vars: 指向当前执行方法的局部变量区第一个变量的指针。
在上述体系结构图中,我们所说的是第一种,即程序计数器,每个线程一旦被创建就拥有了自己的程序计数器。当线程执行Java方法的时候,它包含该线程正在被执行的指令的地址。但是若线程执行的是一个本地的方法,那么程序计数器的值就不会被定义。
Java虚拟机的栈有三个区域:局部变量区、运行环境区、操作数区。
局部变量区
每个Java方法使用一个固定大小的局部变量集。它们按照与vars寄存器的字偏移量来寻址。局部变量都是32位的。长整数和双精度浮点数占据了两个局部变量的空间,却按照第一个局部变量的索引来寻址。(例如,一个具有索引n的局部变量,如果是一个双精度浮点数,那么它实际占据了索引n和n+1所代表的存储空间)虚拟机规范并不要求在局部变量中的64位的值是64位对齐的。虚拟机提供了把局部变量中的值装载到操作数栈的指令,也提供了把操作数栈中的值写入局部变量的指令。
运行环境区
在运行环境中包含的信息用于动态链接,正常的方法返回以及异常捕捉。
动态链接
运行环境包括对指向当前类和当前方法的解释器符号表的指针,用于支持方法代码的动态链接。方法的class文件代码在引用要调用的方法和要访问的变量时使用符号。动态链接把符号形式的方法调用翻译成实际方法调用,装载必要的类以解释还没有定义的符号,并把变量访问翻译成与这些变量运行时的存储结构相应的偏移地址。动态链接方法和变量使得方法中使用的其它类的变化不会影响到本程序的代码。
正常的方法返回
如果当前方法正常地结束了,在执行了一条具有正确类型的返回指令时,调用的方法会得到一个返回值。执行环境在正常返回的情况下用于恢复调用者的寄存器,并把调用者的程序计数器增加一个恰当的数值,以跳过已执行过的方法调用指令,然后在调用者的执行环境中继续执行下去。
异常捕捉
异常情况在Java中被称作Error(错误)或Exception(异常),是Throwable类的子类,在程序中的原因是:①动态链接错,如无法找到所需的class文件。②运行时错,如对一个空指针的引用。程序使用了throw语句。
当异常发生时,Java虚拟机采取如下措施:
- 检查与当前方法相联系的catch子句表。每个catch子句包含其有效指令范围,能够处理的异常类型,以及处理异常的代码块地址。
- 与异常相匹配的catch子句应该符合下面的条件:造成异常的指令在其指令范围之内,发生的异常类型是其能处理的异常类型的子类型。如果找到了匹配的catch子句,那么系统转移到指定的异常处理块处执行;如果没有找到异常处理块,重复寻找匹配的catch子句的过程,直到当前方法的所有嵌套的catch子句都被检查过。
- 由于虚拟机从第一个匹配的catch子句处继续执行,所以catch子句表中的顺序是很重要的。因为Java代码是结构化的,因此总可以把某个方法的所有的异常处理器都按序排列到一个表中,对任意可能的程序计数器的值,都可以用线性的顺序找到合适的异常处理块,以处理在该程序计数器值下发生的异常情况。
- 如果找不到匹配的catch子句,那么当前方法得到一个"未截获异常"的结果并返回到当前方法的调用者,好像异常刚刚在其调用者中发生一样。如果在调用者中仍然没有找到相应的异常处理块,那么这种错误将被传播下去。如果错误被传播到最顶层,那么系统将调用一个缺省的异常处理块。
操作数栈区
机器指令只从操作数栈中取操作数,对它们进行操作,并把结果返回到栈中。选择栈结构的原因是:在只有少量寄存器或非通用寄存器的机器(如Intel486)上,也能够高效地模拟虚拟机的行为。操作数栈是32位的。它用于给方法传递参数,并从方法接收结果,也用于支持操作的参数,并保存操作的结果。例如,iadd指令将两个整数相加。相加的两个整数应该是操作数栈顶的两个字。这两个字是由先前的指令压进堆栈的。这两个整数将从堆栈弹出、相加,并把结果压回到操作数栈中。
每个原始数据类型都有专门的指令对它们进行必须的操作。每个操作数在栈中需要一个存储位置,除了long和double型,它们需要两个位置。操作数只能被适用于其类型的操作符所操作。例如,压入两个int类型的数,如果把它们当作是一个long类型的数则是非法的。在Sun的虚拟机实现中,这个限制由字节码验证器强制实行。但是,有少数操作(操作符dupe和swap),用于对运行时数据区进行操作时是不考虑类型的。
本地方法栈,当一个线程调用本地方法时,它就不再受到虚拟机关于结构和安全限制方面的约束,它既可以访问虚拟机的运行期数据区,也可以使用本地处理器以及任何类型的栈。例如,本地栈是一个C语言的栈,那么当C程序调用C函数时,函数的参数以某种顺序被压入栈,结果则返回给调用函数。在实现Java虚拟机时,本地方法接口使用的是C语言的模型栈,那么它的本地方法栈的调度与使用则完全与C语言的栈相同。
3 Java虚拟机的运行过程
上面对虚拟机的各个部分进行了比较详细的说明,下面通过一个具体的例子来分析它的运行过程。
虚拟机通过调用某个指定类的方法main启动,传递给main一个字符串数组参数,使指定的类被装载,同时链接该类所使用的其它的类型,并且初始化它们。例如对于程序:
class HelloApp
{
public static void main(String[] args)
{
System.out.println("Hello World!");
for (int i = 0; i < args.length; i++ )
{
System.out.println(args[i]);
}
}
}
编译后在命令行模式下键入: java HelloApp run virtual machine
将通过调用HelloApp的方法main来启动java虚拟机,传递给main一个包含三个字符串"run"、"virtual"、"machine"的数组。现在我们略述虚拟机在执行HelloApp时可能采取的步骤。
开始试图执行类HelloApp的main方法,发现该类并没有被装载,也就是说虚拟机当前不包含该类的二进制代表,于是虚拟机使用ClassLoader试图寻找这样的二进制代表。如果这个进程失败,则抛出一个异常。类被装载后同时在main方法被调用之前,必须对类HelloApp与其它类型进行链接然后初始化。链接包含三个阶段:检验,准备和解析。检验检查被装载的主类的符号和语义,准备则创建类或接口的静态域以及把这些域初始化为标准的默认值,解析负责检查主类对其它类或接口的符号引用,在这一步它是可选的。类的初始化是对类中声明的静态初始化函数和静态域的初始化构造方法的执行。一个类在初始化之前它的父类必须被初始化。整个过程如下:
图4:虚拟机的运行过程
4 结束语
本文通过对JVM的体系结构的深入研究以及一个Java程序执行时虚拟机的运行过程的详细分析,意在剖析清楚Java虚拟机的机理。
--sunfruit
Oracle的空间数据库的操作驱动更新了,新的驱动适用于Oracle8.0或以上,新驱动在对数据库的操作上面和原有的驱动差别比较大,不过有一点:使用变得简单了
建立空间数据库和建立空间索引的步骤就略过了,那些网上有很多例子,而且实现方式上面也没有变化,下面列出查询空间数据库记录的代码:
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import oracle.spatial.geometry.JGeometry;
import java.sql.PreparedStatement;
import oracle.sql.STRUCT;
import java.sql.Connection;
/**
* <p>Title: </p>
*
* <p>Description: </p>
*
* <p>Copyright: Copyright (c) 2006</p>
*
* <p>Company: </p>
*
* @author sunfruit
* @version 1.0
*/
public class SdoSelect {
public SdoSelect() {
}
public static void main(String[] args) {
String driver = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@172.16.75.200:1521:starmap";
String uid = "hmbst";
String psw = "hmbst";
Connection conn = null;
PreparedStatement ps=null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, uid, psw);
JGeometry jGeometry=new JGeometry(41884696,14377039,42884696,14477039,0);
STRUCT obj =jGeometry.store(jGeometry,conn);
String sql = "SELECT * FROM POISDO p WHERE sdo_filter(p.gshape,?,'querytype=window')='TRUE'";
ps = conn.prepareStatement(sql,ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
ps.clearParameters();
ps.setObject(1, obj);
// 插入点地物记录
ResultSet rs=ps.executeQuery();
while(rs.next())
{
STRUCT st = (oracle.sql.STRUCT)rs.getObject("gshape");
JGeometry j_geom = JGeometry.load(st);
double[] dou=j_geom.getPoint();
String buff="";
for(int i=0;i<dou.length;i++)
{
buff=buff+String.valueOf((int)dou[i])+" ";
}
System.out.println(buff);
}
}
catch (Exception ex) {
ex.printStackTrace();
}
finally
{
if(conn!=null)
{
try {
conn.close();
}
catch (SQLException ex) {
ex.printStackTrace();
}
}
if(ps!=null)
{
try {
ps.close();
}
catch (SQLException ex) {
ex.printStackTrace();
}
}
}
}
}
表POISDO的结构如下
create table poi (
id INTEGER,
gname VARCHAR2(256),
gshape MDSYS.SDO_GEOMETRY);
--sunfruit
Oracle的空间数据库的操作驱动更新了,新的驱动适用于Oracle8.0或以上,新驱动在对数据库的操作上面和原有的驱动差别比较大,不过有一点:使用变得简单了
建立空间数据库和建立空间索引的步骤就略过了,那些网上有很多例子,而且实现方式上面也没有变化,下面列出添加一条空间数据库记录的代码:
import java.sql.*;
import oracle.sql.*;
import oracle.spatial.geometry.JGeometry;
/**
* <p>Title: </p>
*
* <p>Description: </p>
*
* <p>Copyright: Copyright (c) 2006</p>
*
* <p>Company: </p>
*
* @author not attributable
* @version 1.0
*/
public class SdoAdd {
public static void main(String[] args) {
String driver = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@172.16.75.200:1521:starmap";
String uid = "hmbst";
String psw = "hmbst";
Connection conn = null;
PreparedStatement ps=null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, uid, psw);
JGeometry jGeometry=new JGeometry(41884696,14377039,0);
STRUCT obj =jGeometry.store(jGeometry,conn);
String sql =
"insert into POISDO values(seq_poi_id.nextval,?,?)";
ps = conn.prepareStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
ps.clearParameters();
ps.setString(1, "我的家");
ps.setObject(2, obj);
// 插入点地物记录
ps.executeUpdate();
conn.commit();
}
catch (Exception ex) {
ex.printStackTrace();
}
finally
{
if(conn!=null)
{
try {
conn.close();
}
catch (SQLException ex) {
ex.printStackTrace();
}
}
if(ps!=null)
{
try {
ps.close();
}
catch (SQLException ex) {
ex.printStackTrace();
}
}
}
}
}
表POISDO的结构如下
create table poi (
id INTEGER,
gname VARCHAR2(256),
gshape MDSYS.SDO_GEOMETRY);
Step1. 创建一张表,其中shape用来存放空间数据
CREATE TABLE mylake (
feature_id NUMBER PRIMARY KEY,
name VARCHAR2(32),
shape MDSYS.SDO_GEOMETRY);
Step2. 在user_sdo_geom_metadata
表中插入新记录,用于描述空间字段
INSERT INTO user_sdo_geom_metadata VALUES (
'mylake', //---表名
'shape', //---字段名
MDSYS.SDO_DIM_ARRAY(
MDSYS.SDO_DIM_ELEMENT('X', 0, 100, 0.05), //---X维最小,最大值和容忍度。
MDSYS.SDO_DIM_ELEMENT('Y', 0, 100, 0.05) //---Y维最小,最大值和容忍度
),
NULL //---坐标系,缺省为笛卡尔坐标系
);
Step3. 创建空间索引
CREATE INDEX mylake_idx ON mylake(shape)
INDEXTYPE IS MDSYS.SPATIAL_INDEX
Step4. 插入空间数据
Oracle Spatial用MDSYS.SDO_GEOMETRY
来存储空间数据,定义为:CREATE TYPE sdo_geometry AS OBJECT (
SDO_GTYPE NUMBER,
SDO_SRID NUMBER,
SDO_POINT SDO_POINT_TYPE,
SDO_ELEM_INFO MDSYS.SDO_ELEM_INFO_ARRAY,
SDO_ORDINATES MDSYS.SDO_ORDINATE_ARRAY);
SDO_GTYPE:用四个数字定义了所有的形状
第一位:维数
第二位:线性表示。用于3,4维数据,二维为0
最后两位:Value | Geometry | Description |
---|
00 | UNKNOWN_GEOMETRY | Spatial ignores this value |
01 | POINT | A single point element |
02 | LINE or CURVE | Contains one line string element that may be linear, curved or both |
03 | POLYGON | Contains one polygon element with or without other polygon elements in it |
04 | COLLECTION | A heterogeneous collection of elements |
05 | MULTIPOINT | Contains one or more points |
06 | MULTILINE or MULTICURVE | Contains one or more line string elements |
07 | MULTIPOLYGON | Contains multiple polygon elements that maybe disjoint |
SDO_SRID:坐标系,NULL为笛卡尔坐标系。
SDO_POINT:Oracle Spatial也可定义单个的点,SDO_POINT的定义:
CREATE TYPE sdo_point_type AS OBJECT (X NUMBER,Y NUMBER,Z NUMBER);
如何是二维,Z为NULL。
SDO_ELEM_INFO:每三个值描述一个元素。
第一个值:第一个顶点在SDO_ORDINATES_ARR开始位置
第二个值:元素类型
第三个值:顶点连接方式:1-通过直线连接,2-通过圆弧连接
定义为
CREATE TYPE sdo_elem_info_array AS VARRAY (1048576) of NUMBER;
SDO_ORDINATES:几何图形所有顶点列表。定义为
CREATE TYPE sdo_ordinate_array AS VARRAY (1048576) of NUMBER;
FONT color=#003366>// 插入包含一个岛屿的湖泊
INSERT INTO mylake VALUES(
10,
'Lake Calhoun',
MDSYS.SDO_GEOMETRY(
2003,
NULL,
NULL,
MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1, 19,2003,1),
MDSYS.SDO_ORDINATE_ARRAY(0,0, 10,0, 10,10, 0,10, 0,0, 4,4, 6,4, 6,6, 4,6, 4,4)
));
// 插入两艘小船
INSERT INTO mylake VALUES(
11,
'The Windswept',
MDSYS.SDO_GEOMETRY(
2003,
NULL,
NULL,
MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1),
MDSYS.SDO_ORDINATE_ARRAY(2,2, 3,2, 3,2, 2,3, 2,2)
)
);
INSERT INTO mylake VALUES(
12,
'Blue Crest',
MDSYS.SDO_GEOMETRY(
2003,
NULL,
NULL,
MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1),
MDSYS.SDO_ORDINATE_ARRAY(7,7, 8,7, 8,7, 7,8, 7,7)
)
);
Step4. 查询
Oracle Spatial查询数据包括二个处理过程:
1.只通过索引查询候选项。通过函数SDO_FILTER实现:
SDO_FILTER(geometry1 MDSYS.SDO_GEOMETRY, geometry2 MDSYS.SDO_GEOMETRY, params VARCHAR2)
geometry1:
必须是被索引的几何数据
geometry2:不一定是表中的空间字段,也不要求被索引
params:Filter类型
querytype=WINDOW:geometry2不要求来自表
querytype=JOIN:geometry2必须来自表
SELECT name boat_name
FROM mylake t
WHERE feature_id = 12
AND SDO_FILTER(t.shape, mdsys.sdo_geometry(2003,NULL,NULL,
mdsys.sdo_elem_info_array(1,1003,1),
mdsys.sdo_ordinate_array(2,2, 5,2, 5,5, 2,5, 2,2)),
'querytype=WINDOW') = 'TRUE';
2.再检查每个候选项是否和条件精确匹配。通过函数SDO_RELATE实现:
SDO_RELATE(geometry1 MDSYS.SDO_GEOMETRY, geometry2 MDSYS.SDO_GEOMETRY, params VARCHAR2)
params:masktype类型
DISJOINT
— the boundaries and interiors do not intersect
TOUCH
— the boundaries intersect but the interiors do not intersect
OVERLAPBDYDISJOINT
— the interior of one object intersects the boundary and interior of the other object, but the two boundaries do not intersect. This relationship occurs, for example, when a line originates outside a polygon and ends inside that polygon.
OVERLAPBDYINTERSECT
— the boundaries and interiors of the two objects intersect
EQUAL
— the two objects have the same boundary and interior
CONTAINS
— the interior and boundary of one object is completely contained in the interior of the other object
COVERS
— the interior of one object is completely contained in the interior of the other object and their boundaries intersect
INSIDE
— the opposite of CONTAINS
. A INSIDE B
implies B CONTAINS A
.
COVEREDBY
— the opposite of COVERS
. A COVEREDBY B
implies B COVERS A
.
ON
— the interior and boundary of one object is on the boundary of the other object (and the second object covers the first object). This relationship occurs, for example, when a line is on the boundary of a polygon.
ANYINTERACT
— the objects are non-disjoint.
// 选择在定义矩形内的所有小船
SELECT name boat_name
FROM mylake t
WHERE feature_id = 12
AND SDO_FILTER(t.shape, mdsys.sdo_geometry(2003,NULL,NULL,
mdsys.sdo_elem_info_array(1,1003,1),
mdsys.sdo_ordinate_array(2,2, 5,2, 5,5, 2,5, 2,2)),
'querytype=WINDOW') = 'TRUE'
AND SDO_RELATE(t.shape, mdsys.sdo_geometry(2003,NULL,NULL,
mdsys.sdo_elem_info_array(1,1003,1),
mdsys.sdo_ordinate_array(2,2, 5,2, 5,5, 2,5, 2,2)),
'masktype=INSIDE querytype=WINDOW') = 'TRUE'
// masktype可联合使用
SELECT feature_id id
FROM mylake t
WHERE feature_id = 12
AND SDO_FILTER(t.shape, mdsys.sdo_geometry(2003,NULL,NULL,
mdsys.sdo_elem_info_array(1,1003,1),
mdsys.sdo_ordinate_array(2,2, 5,2, 5,5, 2,5, 2,2)),
'querytype=WINDOW') = 'TRUE'
AND SDO_RELATE(t.shape, mdsys.sdo_geometry(2003,NULL,NULL,
mdsys.sdo_elem_info_array(1,1003,1),
mdsys.sdo_ordinate_array(2,2, 5,2, 5,5, 2,5, 2,2)),
'masktype=INSIDE+TOUCH querytype=WINDOW') = 'TRUE'
Oracle Spatial 提供的其他查询函数:
Query | Description |
SDO_NN | Nearest neighbor |
SDO_SDO_WITHIN_DISTANCE | All geometries with a certain distance |
|
Functions | Description |
SDO_GEOM.SDO_MBR | The minimum bounding rectangle for a geometry |
SDO_GEOM.SDO_DISTANCE | The distance between two geometries |
SDO_GEOM.SDO_INTERSECTION | Provides the intersection point of two geometries |
--sunfruit
AGPS——Assisted GPS,用中文来说应该是网络辅助GPS定位系统。通俗的说AGPS是在以往通过卫星接受定位信号的同时结合移动运营的GSM或者CDMA网络机站的定位信息,就是一方面由具有AGPS的手机获取来自卫星的定位信息,而同时也要靠该手机透过中国移动的GPRS网络下载辅助的定位信息,两者相结合来完成定位。与传统GPS(Global Positioning System全球定位系统)首次定位要2、3分钟相比AGPS的首次定位时间最快仅需几秒钟,同时AGPS也彻底解决了普通GPS设备在室内无法获取定位信息的缺陷。
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
/*******************************************************************
* 该JavaBean可以直接在其他Java应用程序中调用,实现屏幕的"拍照"
* This JavaBean is used to snapshot the GUI in a
* Java application! You can embeded
* it in to your java application source code, and us
* it to snapshot the right GUI of the application
* @see javax.ImageIO
* @author liluqun ([email]liluqun@263.net[/email])
* @version 1.0
*
*****************************************************/
public class GuiCamera
{
private String fileName; //文件的前缀
private String defaultName = "GuiCamera";
static int serialNum=0;
private String imageFormat; //图像文件的格式
private String defaultImageFormat="png";
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
/****************************************************************
* 默认的文件前缀为GuiCamera,文件格式为PNG格式
* The default construct will use the default
* Image file surname "GuiCamera",
* and default image format "png"
****************************************************************/
public GuiCamera() {
fileName = defaultName;
imageFormat=defaultImageFormat;
}
/****************************************************************
* @param s the surname of the snapshot file
* @param format the format of the image file,
* it can be "jpg" or "png"
* 本构造支持JPG和PNG文件的存储
****************************************************************/
public GuiCamera(String s,String format) {
fileName = s;
imageFormat=format;
}
/****************************************************************
* 对屏幕进行拍照
* snapShot the Gui once
****************************************************************/
public void snapShot() {
try {
//拷贝屏幕到一个BufferedImage对象screenshot
BufferedImage screenshot = (new Robot()).createScreenCapture(new
Rectangle(0, 0, (int) d.getWidth(), (int) d.getHeight()));
serialNum++;
//根据文件前缀变量和文件格式变量,自动生成文件名
String name=fileName+String.valueOf(serialNum)+"."+imageFormat;
File f = new File(name);
System.out.print("Save File "+name);
//将screenshot对象写入图像文件
ImageIO.write(screenshot, imageFormat, f);
System.out.print("..Finished!\n");
}
catch (Exception ex) {
System.out.println(ex);
}
}
public static void main(String[] args)
{
GuiCamera cam= new GuiCamera("d:\\Hello", "png");//
cam.snapShot();
}
}
--sunfruit
建立存储过程如下,注意这个存储过程建立的时候,不需要像Oracle那样建立返回的游标
开始在Sql 2000按照Oracle那样建立了返回的游标[Sql 2000 的帮助里面也是建立返回的游标的],但是调用总是不成功,后来使用了没有返回游标的存储过程,并且修改了调用存储过程的方式就可以了:
CREATE PROCEDURE PROC_WARE_INFO_STAT
@XX_ID bigint
AS
SELECT * FROM TableName where colname=XX_ID
GO
在调用的时候和Oracle有明显的不同,如下:
Connection connection=xxxxxx;
CallableStatement callableStatement = connection.prepareCall("{call PROC_WARE_INFO_STAT(?) }");
callableStatement.setInt(1,100);
callableStatement.executeQuery();
ResultSet rs = callableStatement.getResultSet();
--sunfruit
建立存储过程,存储过程为:
CREATE OR REPLACE PROCEDURE TESTC(p_CURSOR out TESTPACKAGE.Test_CURSOR) IS
BEGIN
OPEN p_CURSOR FOR SELECT * FROM HYQ.TESTTB;
END TESTC;
可以看到,它是把游标(可以理解为一个指针),作为一个out 参数来返回值的。
在java里调用时就用下面的代码:
package com.hyq.src;
import java.sql.*;
import java.io.OutputStream;
import java.io.Writer;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import oracle.jdbc.driver.*;
public class TestProcedureTHREE {
public TestProcedureTHREE() {
}
public static void main(String[] args ){
String driver = "oracle.jdbc.driver.OracleDriver";
String strUrl = "jdbc:oracle:thin:@127.0.0.1:1521:hyq";
Statement stmt = null;
ResultSet rs = null;
Connection conn = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(strUrl, "hyq", "hyq");
CallableStatement proc = null;
proc = conn.prepareCall("{ call hyq.testc(?) }");
proc.registerOutParameter(1,oracle.jdbc.OracleTypes.CURSOR);
proc.execute();
rs = (ResultSet)proc.getObject(1);
while(rs.next())
{
System.out.println("<tr><td>" + rs.getString(1) + "</td><td>"+rs.getString(2)+"</td></tr>");
}
}
catch (SQLException ex2) {
ex2.printStackTrace();
}
catch (Exception ex2) {
ex2.printStackTrace();
}
finally{
try {
if(rs != null){
rs.close();
if(stmt!=null){
stmt.close();
}
if(conn!=null){
conn.close();
}
}
}
catch (SQLException ex1) {
}
}
}
}
在这里要注意,在执行前一定要先把oracle的驱动包放到class路径里,否则会报错的。
使用
JAVA
技术实现新一代
OSS/BSS
SUN中国工程研究院 软件技术中心王刚
摘要:本文着眼于OSS/BSS发展的现状,对于OSS/J的技术架构、API规范以及如何进行基于OSS/J的系统开发做了简要介绍。
OSS/BSS
概述
OSS(Operations Support Systems)是指“运营支持系统”,BSS(Business Support Systems)为“业务支持系统”,OSS/BSS是这两类系统的结合在一起形成的综合的电信业务运营和管理平台,在国内OSS/BSS有时也被称为BOSS。
标准化组织电信管理论坛(TMF)对OSS/BSS提出了被业界广泛接受的功能模型。在这个模型中,OSS/BSS包括三大功能:业务开通、业务保障和计费(或称业务计量)。业务开通是指电信运营商接受客户订购电信服务的订单,通过对电信资源的分配、配置、安装和部署为客户提供所需的服务,并能够对服务进行计费。
作为一种高效的信息管理系统,OSS/BSS已在国外电信运营商中得到广泛的运用,并在实践中积累了大量的成功案例。OSS/BSS解决方案也在这一过程中趋于完善,同时也暴露出越来越多的难以克服的问题:
图1、OSS/BSS的“集成的噩梦”
l
OSS/BSS的软件系统相对复杂,从而使得网管系统、计费系统、营账系统、客服系统等都是各成体系,要想把它们有机地整合在一起,几乎是不可能的,对于这种“杂乱无章”的系统结构(参见图1),简直可以称之为系统集成的噩梦(Integration Nightmare)。
l 很多OSS/BSS开发商都有同感——缺少训练有素的工程师,这也是由前一条所决定的,需要工程师同时精通电信的专业知识,又能熟悉各类软件,的确要求比较苛刻。
l 行业标准问题。尽管在近几年来国际国内都陆续推出了一些标准规范,但大多是停留在纸面上,同时也缺少更直观的技术指导和成功案例。
l 一个OSS/BSS,往往会涉及若干个分离的系统,除了集成,对系统进行测试、维护都是十分耗时的。
以上各方面的问题,OSS/J就可以解决,原因在于:
l 采用符合OSS/J规范而开发的软件接口相对简单,OSS/BSS内部的各个子系统是可以互换的( Interchangable )。
l
OSS/J是基于J2EE技术的,开发人员只要熟悉J2EE的开发(甚至仅仅熟悉JAVA的开发)就足够了,他们就能够与设计人员合作,完成系统开发。
l
OSS/J不仅包括了技术规范,而且有真实的代码实现以及测试工具。这能够帮助开发人员很快的上手。
l 因为各个子系统都符合标准的接口,所以系统的后期测试和维护工作会比较简单。
什么是
OSS/J
OSS/J(OSS Through Java)是以JAVA技术为动力的新一代的OSS/BSS解决方案。
说到OSS/J,我们需要提及一个称为OSS Through Java Initiative的工作组,这个工作组由众多的业界新技术的倡导者(例如Motorola,Nokia,Sun, BEA, IBM)派出的专家组成。自2000年成立以来,他们一直在为加速OSS/BSS解决方案的开发、简化其中的系统组件的部署和集成而努力。工作组利用JAVA技术,为OSS/BSS定义实现了一系列的开放的标准API,提供给OSS/BSS的开发者使用。在不久的将来,电信行业的设备制造商、软件开发商、系统集成商都遵循这些标准API的定义,那么最后建立起来的OSS/BSS将是一个组件化的、有机结合在一起的综合管理平台(参见图2),“杂乱无章”的系统结构将成为过去。
图2、采用OSS/J构建的系统结构
需要指出的是,OSS/J并不是要定义另一个通用的OSS/BSS集成框架。工作组的成员在定义标准的API之前,已经汲取了众多标准规范和协议中的精华,例如,OSS/J很好的继承了来自3rd Generation Partnership Project (3GPP), 3GPP2, Mobile Wireless Internet Forum(MWIF)以及TeleManagement Forum(TMF)等组织或论坛推出的规范和框架体系。因此,工作组将所有的经历投入到了JAVA API的定义和编码实现上,而且使用OSS/J规范的的用户可以免费地获得这些资料。
TMF在NGOSS 3.0(Next Generation Operations Support Systems下一代运营支持系统)的文档中,推出了详细的OSS/BSS的定义。(参见http://
www.tmforum.org
)。OSS/J的API定义遵守了NGOSS eTOM (enhanced Telecom Operations Map)的规定,详细内容请见“OSS/J API简介”部分。概括地说,NGOSS为我们提供了独立于技术实现的普遍适用的框架,而OSS/J则是以该框架为基础,提出了采用JAVA技术的实现方案。
OSS/J的规范的推出是在JCP( Java Community Process, http://jcp.org)支持下完成的。通过访问JCP的网站,或者光临http://java.sun.com/products/oss,你都可以下载到OSS/J的规范、参考实现和兼容性测试工具,下面逐一简介:
OSS/J的规范:包括OSS/J API规范和OSS/J J2EE系统设计指导。这些内容将在“OSS/J API简介”中详细叙述。
OSS/J 参考实现( Reference Implementation或 RI):主要内容是根据OSS/J API规范而完成的系统实现的代码。推出RI一方面是为了验证规范的可执行性,所以RI的代码未曾经过很好的优化。RI的另一个重要的作用是它能够使得开发者很快的着手进行设计和开发工作,而且,RI中的所有代码可以被开发人员直接使用到商业系统的开发中去。所以,仔细阅读分析RI的代码能大大缩短你用于熟悉OSS/J的时间。
兼容性测试工具( Test Compatibility Kits或 TCK ):当一个OSS/BSS(或其中的一个子系统)的开发完成了以后,我们如何才能知道它是否符合OSS/J 规范的规定呢?TCK可以完成这样的测试,并产生一个测试报告。如果开发的产品符合OSS/J规范的要求,那么它将很容易和其它同样兼容OSS/J规范的产品集成在一起。
OSS/J的规范推出以后,得到了业界的广泛认可,许多电信运营商、服务提供商、系统集成商争相追随。来自IDC的2002年的报告说,“……随着SA、TT、Qos API的发布,许多服务提供商和供应商认为,采用JAVA技术实现OSS已经到了实际可行的阶段。”
OSS/J
与
J2EE
上文提到,OSS/J可以帮助我们终结“系统集成的噩梦”,因为它为我们定义了一系列的标准API,只要各个厂商都能遵守API中的规定,那么OSS/BSS的集成难的问题将迎刃而解。那么具体的底层实现机制是怎样的呢?——OSS/J采用了J2EE作为技术平台。
J2EE(Java 2 Enterprise Edition)即Java 2企业版,是提供给开发者的采用组件技术构建分布式系统的编程框架,需要更深入了解J2EE,请浏览http://java.sun.com/j2ee/。总体来说,J2EE使得开发人员无须去考虑分布式系统中的底层技术实现细节,例如线程管理,网络通信等,而是集中精力开发符合业务逻辑的代码,这无疑大大加快了应用程序的开发进程,而且简化了系统的部署和后期维护工作。目前全球的J2EE开发人员总数已经达到了几百万,这个群体还在迅速膨胀。
图3、采用J2EE实现OSS/BSS
作为服务器端的开发技术,企业JavaBean(EJB)、扩展标记语言(XML)以及JAVA Management Extensions(JMX)都在OSS/J中被采纳。因为J2EE、XML、JMX已经在很多的大型企业应用(特别是服务器端的应用程序)中获得了成功,所以OSS/J采用它们定义在组装、开发和部署OSS/BSS解决方案时所需要的API。
图3是采用J2EE实现OSS/BSS的示意。以OSS/J API为基础,我们开发了支持SA、TT等功能的EJB,这些EJB可以根据需要通过JDBC存取数据库,或通过JNDI访问目录服务器。对于已有的遗留系统以及EMS(Element Management Systems),可以采用J2EE连接器的架构(Java Connector Architecture即JCA)通过SNMP、CMIP或其他专有协议实现集成。OSS的客户端可以是浏览器或定制的应用程序,通过HTTP/XML/Java/IIOP和系统相联。与此同时,JAVA的消息机制为我们提供了更加灵活的“松耦合(loosely-coupled)”的集成方式,利用它可以简单地实现和Intranet/Internet中的其他系统的连接。
OSS/J API
简介
图4将OSS/J中的核心API和TMF的eTOM的各个过程做了映射。从图中可以看出,OSS/J核心API囊括了客户管理、订单管理、服务开通等20个,关于每个API的详细描述,可参见http://java.sun.com/products/oss/apis.html上的OSS/J API Roadmap。目前,已经完成的API有:OSS服务开通API,OSS故障单API,OSS通用API,OSS IP计费API和OSS服务质量控制API,而OSS 库存 API不久将发行。除了API,OSS/J工作组还为开发者提供了《OSS/J J2EE 系统设计指导》。
图4、OSS/J API到eTOM的映射
OSS通用 API( OSS Common API):和其他OSS/J API不同的是,它本身没有对OSS/BSS在业务逻辑提供支持,而是为开发者使用OSS/J API提供了一个基础框架。可以认为这部分API是《OSS/J J2EE 系统设计指导》一个具体实施。需要强调的是,既然是基础框架,以下提及的所有OSS/J API都是依赖于通用API的。
OSS/J J2EE系统设计指导( OSS/J J2EE Design Guideline或 OSS/J J2EE DG):定义了一系列的设计模式(Design Patterns),这些模式非常适合于采用J2EE/EJB搭建网络服务管理系统。总体来看,DG中提及的设计模式都是来自于J2EE设计模式,关于J2EE设计模式的详细信息,请参见http://java.sun.com/blueprints/corej2eepatterns。DG中主要涉及到以下要点:
l
OSS中的功能都是采用EJB组件的形式实现的
l 这些EJB提供了面向业务逻辑的粗略的接口
l 应用服务器为OSS/BSS系统提供了集群、扩展和故障处理等功能
l 采用消息(Messaging)交换机制来减小组件之间的耦合程度
l 结合消息机制和JCA架构实现系统的集成和工作流的管理
贯穿DG的一个重要概念就是“软件构件(Software Building Block)”的概念。一块软件积木是若干软件组件(Component)的集合体,这些软件组件相互协作,从而满足系统业务逻辑的需求。需要强调的是,OSS/J API的所有定义和实现方式都是遵从OSS/J J2EE DG的。
OSS服务开通 API( OSS Service Activation API或 SA API):主要提供了对订单的管理功能(例如生成、修改、删除、查询订单等)和服务的管理功能。API中并没有给出指定的“服务信息模型(Service Information Model)”,而是将这部分工作留给开发者去实现,这样开发者可以根据自己的业务逻辑的需要定义服务信息模型。SA API中关于订单管理的定义是根据TMF 603中的“世界订单信息协定”(World Ordering Information Agreement)以及OMG WMF/WfMC的“订单状态模型(Order State Model)”的定义完成的。
OSS故障单 API( OSS Trouble Ticket API或 TT API):定义了生成、更新、查询、关闭故障单的一系列操作。网管系统可以通过调用TT API自动生成故障单,服务提供商也可以利用它产生和处理故障单,客户关怀系统能够调用这些API将故障单发送给服务提供商(见图5);如果故障单的管理是在一个工作流程中完成的话,那么开发人员可以使用这些API与工作流引擎进行信息传递。
OSS IP计费 API( OSS IP Billing API):定义了IP计费的数据源和计费系统之间的接口。这部分API适用于针对2.5G和3G网络的OSS/BSS开发。而且API的定义重点放在(但不不局限于)无线通信的领域。该规范的定义是为了实现计费系统、计费数据采集系统、计费数据源这些不同的子系统之间够实现无缝连接,流畅地完成各种记录类型(例如CDR、SDR、IPDR等)的交换和传输。
OSS服务质量 API( OSS Quality of Service API或 OSS QOS API): QOS API使得QOS系统能够从其他系统得到影响服务质量的数据,例如网络性能、极限值以及故障数据等等。QOS API主要涉及到性能和使用情况的数据监测、系统极限值的监测及故障数据的监测三个方面的内容。
OSS 库存 API ( OSS Inventory API ): OSS/BSS在进行操作的时候,大多需要关于网络可以提供的产品、服务和资源的规划、使用的情况,这些功能要由库存API来提供。这部分API包括了对产品和服务的目录管理,并且有跟踪用户预定和使用产品或服务的功能;同时API中对网络资源管理(例如网络上的设备和网络拓扑结构的管理)的功能对于OSS/BSS来说也是不可或缺的。
下图展示了各个OSS/J API之间的关系,其中椭圆形的边界可以看做是API的定义,矩形的内容是由开发商来实现的,而箭头代表方法或功能的调用,这些功能调用,尤其是各个子系统之间(例如从Qos调用故障单中的功能)应该使用OSS/J的API来完成。
图5、OSS/J API的关系
OSS/J
开发指导
了解了OSS/J API的概况以后,可能你已经决定着手进行基于OSS/J的系统开发了。无论你是系统设计人员还是程序员,应该具备J2EE的开发经验,而且最好了解电信行业知识(特别是关于OSS/BSS方面的知识)。而且,建议你按照以下的步骤来开展工作。
研读 OSS/J相关的资料
浏览OSS/J的网站http://java.sun.com/products/oss,你可以下载到OSS/J的规范和相关技术文章(如图6所示)。前面提到,试着部署一下RI并研究其代码能帮助你迅速了解OSS/J。如果你在学习和使用的时候有什么疑问和意见,都可以发表在相应的新闻组里(新闻组的地址参见OSS/J的网站)。
图6、下载OSS/J规范、RI进行开发
开始 OSS/BSS 的设计与实现
这个过程和标准的软件开发没有大的区别,主要经历从制定计划、需求分析、系统设计、详细设计、编码实现和测试等几个环节。由于进行OSS/J的开发是基于J2EE技术的,所以选择稳定高效的应用服务器(Application Server)和集成开发环境(IDE)十分重要,好在由于J2EE的开放性,我们有很多选择,例如Sun ONE应用服务器、BEA Weblogic、Borland Jbuilder等。在系统设计和详细设计时,设计人员需要首先熟悉OSS/J J2EE设计指导中内容,按照其中讲述的设计模式进行设计。至于编码实现,和一般的J2EE开发没什么区别,主要涉及到EJB的编程,同时需要程序员对XML比较熟悉。进入到测试阶段,除了完成普通软件开发需要完成的一系列测试以外,OSS/J规范的兼容性的测试也是必须的,如上文所述,TCK可以帮你完成这一个步骤(参见图7)。
图7、使用TCK进行测试
发布自己的产品信息
当你开发的产品成功地通过了TCK的测试以后,就可以获得OSS/J的认证。OSS/J针对OSS/BSS产品规定了一个灵活的“自我认证流程”,参见图8。上文中提到,使用TCK测试你的产品,将产生一个测试报告。如果测试成功,可以将报告发布到你的公共网站上,同时通知OSS/J工作组的程序管理小组。经过核实以后,OSS/J程序管理小组将会更新OSS/J的网站,将你发布的产品测试报告的链接(URL)加到通过认证的产品列表中去。到现在,整个“自我认证过程”就完成了。获得了OSS/J的认证,意味着你开发的产品可以很容易地和其它厂商的也获得认证的产品集成起来,可以作为有机的一部分加入到基于OSS/J API的OSS/BSS中去。参见http://java.sun.com/products/oss/adoption/ossj_certified_products_table.html,在这里,你可以看到IBM、Ilog、Nokia等许多厂商的产品都已经获得了OSS/J的认证。
图8、OSS/J的“自我认证”
加入 OSS/J工作组
如果你希望能最及时地了解OSS/J的发展动向,获得最新的信息,甚至希望参与规范的制订和修改,那么最有效的方式就是加入到OSS/J的工作组中来。OSS/J工作组中的成员有着不同的级别,分别有不同的权利。请查看http://java.sun.com/products/oss/members.html了解加入OSS/J工作组的细节内容。
总结
OSS/J为我们提供了一个采用Java技术实现OSS/BSS的技术框架,它借鉴了众多的既定行业规范,以J2EE/XML技术为基础,为OSS/BSS的开发者提供了一个标准的环境,以解决目前OSS/BSS中存在的集成问题。相信随着相关技术的进步,OSS/J会拥有越来越多的支持者,从而发展成为用JAVA实施OSS/BSS的技术标准。
参考资料
l
Operations Support Systems, http://www.iec.org
l
OSS
through Java Initiative, By Philippe Lalande
l
OSS
through Java Initiative, Simplifying Integration with Common APIs, http://java.sun.com/products/oss
l
OSS/J API Roadmap
l
Case Study: From NGOSS Map to OSS/J Roadwork, http://www.tmforum.org/browse.asp?catID=1483&sNode=1483&Exp=Y&linkID=28103, By Peter Lambert
l
OSS
through Java Design Guidelines, http://java.sun.com/products/oss/apis.html
l
OSS
Trouble Ticket API
l
OSS
Service Activation API
l
OSS
Common API
l
OSS
IP Billing API
l
OSS
Quality of Service API
l
OSS
Inventory API
王卫乡
随着信息时代的到来,我国各行各业都面临着诸多机遇与挑战,党和政府适时地提出"以信息化带动工业化,发挥后发优势,实现社会生产力的跨越式发展"的伟大战略决策。在近几年的信息化建设中,一些企业在企业信息化建设方面的投入较大,但收效甚微。原因之一在于:缺乏统一的信息化框架、标准和规范。在我国电子商务、电子政务全面发展之际,此框架、标准和规范就显得尤为重要,只有按照统一的框架、标准和规范,才可能避免重复建设、实现信息共享。
目前人们都把电信企业当作是信息化建设的排头兵,这隐含有四层意思:
(1)电信企业是国家信息基础设施的主要建设者和运营者,担负着基础建设的重要使命;
(2)电信业自身的信息化建设能够强化企业自身的管理规范,提高运营效率和综合竞争能力;
(3)电信企业可以利用自身的信息化典范,为其他的企业提供信息化的解决方案,作为一种新的业务为客户提供更好的服务;
(4)电信企业自身的信息化建设更具有推进社会信息化、政府信息化、企业信息化的示范作用。
信息化建设需要规范的业务流程和管理流程做基础,电信运营业务流程最新的国际规范eTOM3.0版对电信相关企业的信息化建设和企业运营至关重要。
一、eTOM中"e"的含义
"eTOM"中的"e"常规指"增强"之意,但它却包含了与业务流程框架有关的很多含义,如:企业流程(Enterprise processes)、电子商务激活(eBusiness enabled)、扩展的(Expanded)、每事(Everything)、每处(Everywhere)、每时(Every time)等。
二、eTOM业务流程框架的目的
eTOM业务流程框架作为电信运营业务流程的向导蓝图和业务及运营支撑系统(分别为BSS和OSS)发展和集成的始发点,有助于推动和发展NGOSS(Next Generation Operations Systems and Software,下一代运营系统和软件)解决方案。对于服务提供商来说,当他们考虑内部流程重组需求、合作关系、联盟以及与其它服务提供商总的工作协议时,eTOM提供了一个中性的参考点。对供应商来说,eTOM框架给出了软件各组件的潜在边界以及支撑产品所需的功能、输入和输出。
eTOM3.0版包含以下内容:
(1)eTOM业务流程框架的角色描述;
(2)服务提供商的电子商务环境和所需的更为复杂的业务关系环境关联模型;
(3)服务提供商的企业流程和子流程在高级层面的业务流程框架和解释的侧重点是自上而下、以顾客为中心、端到端,TOM正逐步向eTOM演变,目前的eTOM业务流程框架对于服务提供商来说是整个企业的框架;
(4)所有流程的分解,从eTOM框架的最高层面的概念视图到工作层面,有选择地给出框架中很多较低层面的流程分解;
(5)流程流向和分解流程的选择描述,包括流程目的或描述、业务规则、高级层面的信息等;
(6)如何运用流程框架。
eTOM的目的是通过对业务流程的实施来管理企业,帮助企业明确目标,确保有关服务交付和支持所有关键的企业支撑系统进行集成。eTOM关注的焦点是:服务提供商使用的业务流程、流程间的联系、接口的识别,即如何利用客户、服务、资源、供应商/合作伙伴以及其它多重流程使用的信息。
eTOM业务流程框架和相关的业务流程模型描述了流程及组成端对端的连接点,描述了运营、战略、基础设施与产品流程区域中开通、保障、计费等客户运营流程流向。eTOM业务流程框架对信息和通信服务、技术管理有特殊意义,这种模型同样适用其它类型的业务。
服务提供商应用这种通用的流程框架,以确保与其它实体交易的高效和有效,确保第三方软件的开发和应用不需太多的客户化定制。在电子商务环境下,对流程的共同理解,此框架在管理信息和通信业务市场中十分关键。企业电子商务集成化已成为强大的流程集成最成功的范例。eTOM不仅是电子贸易或电子商务流程框架,它还支持传统业务流程与电子商务的集成。
三、eTOM是什么
eTOM,是enhanced Telecom Operations Map的英文首字母缩写,英文全称为enhanced Telecom Operations MapTM (eTOM)。The Business Process Framework For The Information and Communication Services Industry,中文意思为增强的电信运营图(eTOM)信息和通信服务行业的业务流程框架。eTOM现在已经发展到3.0版,并已于2002年6月通过电信管理论坛(TM论坛)的批准,正式公开发布。eTOM大大增强了TOM的功能,是服务提供商运营流程实际依照的行业标准。
eTOM是一种业务流程模型或框架,它为服务提供商提供所要求的企业流程。但是,它不是服务提供商的业务模型。它不陈述以下策略:服务提供商的目标客户、服务提供商所服务的市场、服务提供商前景、任务等,业务流程框架是业务模型策略的一部分,是为服务提供商提出计划。
eTOM是基于TOM(Telecom Operation Map)发展而来的,eTOM把TOM扩展到整个企业架构,并陈述对电子商务的影响。虽然eTOM比TOM复杂得多,在某种意义上,eTOM更为直接明了,它消除了TOM中企业管理(公司型)流程、市场营销流程、保留客户流程、供应商和合作伙伴管理流程之间的隔膜等。
很多服务提供商(包括了系统集成商、ASP和软件供应商)指出,在eTOM未被确认前,他们已经在运用eTOM,因为eTOM较好地代表了他们的真实需要,他们在采购软件、设备以及面对业务关系网络与其它服务提供商的接口,都需要行业的标准框架。虽然,很多服务提供商已接受TOM,并把它作为流程框架核心或是作为保持相容性的标准,但大多数服务提供商承认需要进一步扩展TOM,以便反映电子商务集成化和全企业的框架。图1为eTOM业务流程框架0级流程图。
图1: eTOM业务流程框架——0级流程图
图1显示的是eTOM业务流程框架最高层面的概念视图。该视图把战略和生命周期流程从运营流程中分离出来,形成两大流程群组,同时把关键功能区域分成四个横向层面。此外,图1也显示了企业内、外部的相互影响、相互作用的五大实体(客户、供应商/合作伙伴、股东、雇员、其他利益相关者)。图2为eTOM业务流程框架一级流程图。
图2:eTOM业务流程框架—— 一级流程
图2给出了eTOM框架中从0级视图看到的一级流程。该视图称为企业流程框架CEO(即:首席执行官)层面的视图。然而,人们倾向于使用二级流程的一级视图,因为在分析业务时需要这些细节。图2给出了7个纵向流程群组。这些端对端的流程用以支持客户和管理业务。eTOM关注的焦点是以客户运营流程的开通、保障和计费(FAB)为核心。运营支持与就绪流程从FAB实施流程中分出来单列,以增强对FAB中实现支持和自动化的关注,即:对在线和对客户实现即时支持。战略与承诺及两个生命周期管理纵向流程群组也分离出来,因为与运营流程群组不同,它们没有直接支持客户,与运营流程群组有着本质差别,并且工作在不同的业务周期。图2中的横向流程群组区分了功能运营流程和其它类型的业务功能流程,如:市场营销对销售、服务开发对服务配置等。左边的功能流程(在战略与承诺、基础设施生命周期管理、产品生命周期管理纵向流程群组)保证支持和指导运营流程区域的纵向流程工作。
四、TOM与eTOM的比较
服务管理业务流程模型被称之为TOM,是根据服务提供商的运营管理要求,围绕流程、输入、输出和活动开发。它关注的焦点和范围是运营和运营管理,它竭力为电信业服务,支持对服务提供商流程的理解,在业务、运营系统和软件方面为服务提供商的问题提供解决方案。TOM仍然处于eTOM业务流程框架的核心。
TOM2.1版本是TM论坛成员现在认可的业务流程框架或模型。世界各地的服务提供商广泛接受它作为运营业务流程框架,而很多供应商把它作为产品开发和销售的基础。基于以下两点,把TOM的修改称为eTOM。第一,成员们很久以来就想把TOM扩展为全企业业务流程框架;第二,利用电子商务和互联网取得成功很关键。TOM并没有充分地分析电子商务对商业环境、业务驱动力、电子商务流程集成化要求的影响,也没有分析日渐复杂化的服务提供商的业务关系。图3为电信运营图业务流程模型。
图3为电信运营图(TOM)业务流程模型
如图2和图3所示,与TOM相比,eTOM做了以下改善:
(1)把范围扩展到所有的企业流程;
(2)鉴于市场营销在电子商务环境中所处的重要地位,明确标识市场营销流程;
(3)明确标识企业管理流程,以便企业中的每个人都能够确定其关键流程,从而使整个企业都接受流程框架;
(4)把开通、保障和计费(FAB)引入高级层面的框架视图中,从而强调客户优先流程是企业关注的焦点;
(5)定义运营支持与就绪纵向流程群组,此定义可适用于除企业管理以外的所有功能层面。为使电子商务集成和客户自助管理成为现实,企业必须了解自己需要的流程,以保证客户运营支持和客户自助管理;
(6)明确SIP(Strategy, Infrastructure and Product)流程:战略与承诺,基础设施生命周期管理和产品生命周期管理,识别这三种企业流程群组,它们明显区别于客户运营流程;
(7)识别战略和生命周期管理流程的不同周期时间,需要把这些流程从最需要自动化的客户优先运营流程中分离出来,可以通过把战略与承诺和两个生命周期管理流程从日常的客户运营流程中分离来实现。
(8)从关注客户或是面向服务转为面向客户关系管理,强调客户自助管理和控制,增加客户对企业产生的价值,利用信息来为单个客户个性化和客户化,这可以为客户运营功能层面增加更多的元素,更好地代表销售流程,完成市场营销在客户关系管理范围内的集成;
(注意:eTOM对客户关系管理进行了更广泛的定义,比CRM某些定义的范围要大。)
(9)明确了跨技术管理资源的要求(即:应用、计算和网络),由"网络和系统管理"功能流程向"资源管理与运营"集成。它把IT管理引入到该功能层面,与外向流程群组形成对照。
TOM是服务提供商运营管理事实上的行业标准,eTOM与TOM相比保留了TOM中以下优点:
(1)关注于业务流程;
(2)以客户为中心的驱动手段;
(3)自上而下的定位;
(4)通用直白的描述和手段;
(5)本身吸引,服务提供商能立即了解运营的工作方式;
(6)一直关注运营管理;
(7)在服务提供商、供应商和媒体之间广泛使用;
(8)灵活地支持大多数SP的流程模型。
这些优点促使TOM成为运营系统具有推动作用的架构,成为服务提供商的软件解决方案,它仍然作为业务流程框架,并涉及更多的流程和学科。在电子商务环境下,实体间的联系是流程中最重要的环节。eTOM会加强以客户为中心的驱动途径,因为目前和将来的环境能够掌握客户。电子商务使市场从面向供货转为面向需求,或者说由"推向客户"变为"客户拉动"。eTOM中保留自上而下的定位,不仅因为它是TOM的核心概念,而且它是良好的业务流程建模。
五、eTOM的使用对象
eTOM提供了通用的服务提供商企业流程视图,较容易转换成单个提供商的内部手段。
eTOM的优点之一是能为服务提供商根据需要在不同层面广为采用。eTOM也可作为翻译,允许服务提供商参照行业框架绘制其明确流程。当流程样本开发出来,服务提供商就能利用和调整样本以适应自己的业务环境。
eTOM针对的是信息和通信行业广大的专业人员,对于有经验的通信专业人员来说,TOM和eTOM是直观的、强大而通用的服务提供商企业流程。
eTOM针对服务提供商和网络运营商的决策者,需要了解和输入通用的业务流程框架,以较好的性价比实现企业自动化。对于从事业务和运营自动化的专家,它也是很重要的架构。
eTOM将继续为服务提供商和供应商提供一个通用的框架进行讨论,在复杂的行业中讨论复杂的技术和复杂的业务需求。这些复杂性来自:从开发自己的业务和运营系统软件,转向更多的采购和系统集成的方式;服务提供商和网络运营商之间新型的业务关系。
建立新型的业务关系和不进行内部开发是对市场驱动力的回应。市场驱动力要求服务提供商和网络运营商增加业务的范围、缩短新业务投向市场的时间、提高服务速度以及降低系统和运营成本。
eTOM业务流程框架针对服务提供商和网络运营商涉及业务流程重组、运营、采购和其他活动的人员,使他们能了解并推动集成化和自动化进程的通用业务流程框架,参与提供流程、输入、优先级和要求的活动。
eTOM业务流程框架也针对业务和运营管理系统软件的设计者、集成者以及设备供应商,通过了解管理流程和应用需求,为服务提供商和网络运营商带来利润。
eTOM业务流程框架提供一个通用架构,支持大量的集成、合并活动,了解好通用流程和通用流程框架将为合并大大改善集成性能。eTOM适用于所有的电信服务提供商。不是所有供应商都要用到eTOM定义的所有流程。eTOM框架灵活,使服务提供商可以根据模块基础和恰当的具体层面需求来选择所需的流程。
中国中信集团公司管理信息中心 王卫乡博士
近年来,我国电信业发展迅猛,电话普及率已达33.7%,电话用户总数跃居世界第一。与世界发达国家的电信业相比,我国电信运营商在技术设备和网络基础设施等"硬件"方面的差距并不大,但是在业务流程、企业管理和劳动生产率等"软件"方面仍存在较大差距。eTOM将为我国电信业与国际接轨、缩小"软件"差距带来机遇。
eTOM的发展
eTOM,英文全称为enhanced Telecom Operations Map。(eTOM)———The Business Process Framework For The Informationand Communication Services Industry。
中文意思为增强的电信运营图(eTOM)———信息和通信服务行业的业务流程框架.eTOM源自TOM(TelecomOperationsMap)。TOM侧重的是电信运营行业的服务管理业务流程模型,关注的焦点和范围是运营和运营管理。世界各地的服务提供商广泛接受它作为运营业务流程框架,而且很多供应商已把TOM作为产品开发和销售的基础。随着企业在业务中使用因特网、集成电子商务机遇的需要,仅关注运营管理的TOM已显出极大的局限性。TOM没有充分地分析电子商务对商业环境、业务驱动力、电子商务流程集成化要求的影响,也没有分析日渐复杂化的服务提供商的业务关系。因此,TM论坛的成员们很久以来就想把TOM扩展为全企业业务流程框架。eTOM中的e常规指"增强"之意,但它却包含了与业务流程框架有关的很多观念,如:企业流程(Enterpriseprocesses)、电子商务激活(eBusinessenabled)、扩展的(Expanded)、每事(Everything)、每处(Everywhere)、每时(Everytime)等。TOM仍然处于eTOM业务流程框架的核心。
eTOM作为电信运营业务流程向导的蓝图,是NGOSS(NextGenerationOperationsSystemsandSoftware,即下一代运营系统和软件)的重要概念和关键组成元素。NGOSS中的"OSS"虽与通常的"OSS"(OperationSupportSystem)在字面上相同,但是内涵已经发生了很大变化。NGOSS强调包含有文档、模型和代码等知识库的创建,侧重于业务流程和信息模型的定义、系统框架的定义、合作催化试点项目的实施等关键元素。
eTOM是什么
eTOM是一种业务流程模型或框架,它为服务提供商提供所要求的企业流程,但它不是业务模型。
它不陈述以下策略问题:谁是服务提供商的目标客户,服务提供商所服务的市场是怎样的,以及服务提供商的愿景如何、任务是什么等等。
eTOM较好地代表了电信运营业的真实世界,很多服务提供商(包括了系统集成商、ASP和软件供应商)已经在运用eTOM,因为他们在采购软件、设备,以及面对愈加复杂的业务关系网络中与其它服务提供商的接口,都需要行业的标准框架。
对于服务提供商来说,当他们考虑内部流程重组需求、合作关系、联盟以及与其它提供商的总的工作协议时,eTOM提供了一个中立性的参考点。对供应商来说,eTOM框架给出了软件各组件的潜在边界,以及支撑产品所需的功能、输入和输出。
为方便起见,笔者对TM论坛公布的eTOM的0级和1级流程视图进行了组合。eTOM阐述了电信运营商及其所处的经营环境,给出了企业内、外部的相互影响、相互作用的五大实体:客户、供应商/合作伙伴、股东、雇员、其他利益相关者。
eTOM给出了三大流程群组:
1)战略、基础设施和产品;
2)运营;
3)企业管理。
这三大流程群组进一步分解为23个一级流程群组和87个二级流程以及若干三、四级流程;其中7个一级的纵向流程群组,是端对端的流程,用以支持客户和管理业务;16个横向流程群组区分了功能运营流程和其它类型的业务功能流程。eTOM的关注焦点是以客户运营流程的开通、保障和计费(FAB)为核心,运营支持与就绪流程从FAB实施流程中分出来单列,以增强对FAB中实现支持和自动化的关注。
eTOM对我国电信业的意义
我国电信业经过前几年的快速发展,网络基础设施和用户数量都已达到相当大的规模。如何有效地管理和充分利用这些资源(网络基础设施、客户资源、信息资源等)是各电信运营商都要面对的关键问题。eTOM的目的是通过业务流程的实施来管理企业,它涵括了战略、经营和保障等企业的三大高层流程及其相互间的集成。服务提供商需要这种通用的流程框架,以确保有效和高效地与其它实体进行交易和交互,确保第三方软件的开发和应用而不需太多的客户化定制。在电子商务环境下,这种对流程的共同理解,在管理电信业务市场中愈来愈复杂的业务关系中极其重要。
在经济全球化、信息化时代,在电子商务环境下,业务流程已经逐渐取代资金和技术,成为支撑企业赚钱的最主要因素。在价值网中,企业是通过紧密相联的业务流程,把技术、产品和服务,转变为现金。可以说,业务流程已经成为企业核心竞争力的重要组成部分。在同等的人、财、物的投入条件下,不同的业务流程所产生的结果将是完全不同的。eTOM从企业整体和所处的社会经济环境的角度和高度来认识和看待电信企业运营的业务流程框架,对我国电信业的稳步、快速发展将具有深远的现实意义。
在战略流程方面,eTOM体现了对企业资源的全生命周期管理和一体化管理的理念。eTOM明确识别了SIP(Strategy,InfrastructureandProduct)流程群组:战略与承诺,基础设施生命周期管理和产品生命周期管理。战略和生命周期管理流程具有不同的时间周期,需要把这些流程从最需要自动化的客户优先运营流程中分离出来。
在运营流程方面,eTOM体现了面向客户关系管理、对客户提供区别服务和营销的理念。除了FAB外,eTOM还定义了运营支持与就绪纵向流程群组。为使电子商务集成和客户自助管理成为现实,企业必须了解自己需要的流程,以保证直接的、愈来愈多的在线以及客户运营支持和客户自助管理。从关注客户或是面向服务转为面向客户关系管理,强调客户自助管理和控制,增加客户对企业产生的价值,利用信息来为单个客户个性化和客户化。明确了跨技术管理资源的要求(即:应用、计算和网络),由TOM的"网络和系统管理"功能流程向eTOM的"资源管理与运营"集成。
在保障流程方面,eTOM明确标识了企业管理流程,把企业管理流程和运营、战略作为一个整体,以便企业中的每个人都能够确定其关键流程,从而使整个企业都接受流程框架。
eTOM提供了通用的服务提供商企业流程视图,它很容易转换成单个提供商的内部手段。eTOM能为服务提供商根据需要在不同层面广为采用。eTOM的框架很灵活,专门的服务提供商可以根据模块基础和恰当的具体层面需求来选择自己所需的流程。
eTOM为服务提供商和供应商提供了一个通用的框架,便于在复杂的行业中讨论复杂的技术和复杂的业务需求。eTOM从企业整体的角度和高度,全方位地提供了技术人员与管理人员之间沟通的桥梁、语言与规范。而现实情况中,技术人员与管理人员因为看问题的侧重点不同,常常难以进行全面、深入、良好的沟通,难以从不同侧面、不同层次对企业运营的流程达成共识。eTOM特别关注服务提供商使用的业务流程、流程间的联系、接口的识别,如何利用客户、服务、资源、供应商/合作伙伴以及其它多重流程使用的信息。在电子商务的环境下,从业务的各个方面来充分利用信息,实现自动化以提高生产率和收入以及改善与客户的关系尤为重要。
除了电信运营商,eTOM也适用于业务和运营管理系统软件的设计者和集成者,以及设备制造商和供应商。他们通过了解管理流程和应用需求如何共同工作,为服务提供商和网络运营商带来利润,而他们也将从中获利。
几点建议
经过近几年的信息化建设,不少企业在企业信息化方面的投入很大,却见效甚微。原因之一是:缺乏统一的业务流程框架、标准和规范。信息化需要规范的业务流程和管理流程作为基础。在电子商务、电子政务全面出击之际,框架、标准和规范就显得尤其重要;只有按照统一的框架、标准和规范,才可能避免重复建设,实现信息共享。人们都把电信企业当作是信息化建设的排头兵,这实际上隐含有四层意思:1)电信企业是国家信息基础设施的主要建设者和运营者,担负着基础建设的重要使命;2)电信业自身的信息化建设能够强化企业自身的管理规范,提高运营效率和综合竞争能力;3)电信企业可以利用自身的信息化典范,为其他的企业和单位提供信息化的解决方案,为企业开拓新的信息化业务,作为一种新的业务来为客户提供更好的服务;4)电信企业自身的信息化建设更具有推进社会信息化、政府信息化、企业信息化的示范和带头作用。因此,电信企业在信息化建设进程中应该率先垂范,成为"以信息化带动工业化,发挥后发优势,实现社会生产力的跨越式发展"的伟大战略实践的先行者。
在电子商务环境下,业务流程的互动牵动着各企业之间的互动,各经济实体之间的联系是流程中最重要的环节。为此,笔者建议:电信运营商应从业务流程的梳理与再造着手,先梳理然后再造业务流程,理顺业务流程各环节的关系,真正关注客户能够感受到的服务质量。
内部流程的梳理与再造。这是指在企业内部围绕业务流程来安排各项工作,其重点是大规模削减企业组织内部的成本,提高质量和生产率。以eTOM为指导,整体规划,建立比较完善的XSS(XSupportSystems,如OSS,BSS,MSS等)。在软件开发和业务关系方面,无论是服务提供商,还是网络运营商,都要考虑:1)从开发自己的业务和运营系统软件,转向更多的采购和系统集成的方式;2)服务提供商和网络运营商之间新型的业务关系。建立新型的业务关系和不进行内部开发是市场驱动力所致。市场驱动力要求服务提供商和网络运营商增加业务的范围、缩短新业务投向市场的时间、提高服务速度以及降低系统和运营成本。
外部流程的梳理与再造。从改善企业内部的绩效开始,在跨越企业组织界线的操作与处理过程中考虑更多的改良,为企业的运营方式带来突破性的革新。也就是说:通过广泛应用信息技术,重新规划跨越组织界线的业务流程,以实现经营绩效的突破性提升。这须要企业重新审视、梳理整个企业的经营模式,对业务流程和其中各个环节之间的相互关系进行审查,审查的对象不仅仅是企业与客户的关系,还应包括企业与供应商、合作伙伴、员工和竞争对手之间的关系。各电信运营商之间既是竞争对手,同时又是合作伙伴,应该采取有效措施加快和确保电信网络基础设施之间的互联互通。
服务质量和服务等级协议。组织研究和实施"客户QoS(QualityofService)/SLA(ServiceLevelAgreement)管理"。以前,电信运营商只关注网络的QoS,而轻视客户的QoS;今后,电信运营商更需要看重客户能够真正感受到的服务质量(QoS),而不仅仅是网络的QoS。因为,客户能够真正感受到的服务质量,其内涵远远多于网络QoS。这就需要监视、管理和报告在企业的服务描述、客户合同或产品组合中有具体定义的和实际提供给客户的服务质量的对比;同时,关注与企业的业绩、某些专门服务的服务等级协议(SLA)相关的产品与服务,以及其他与服务相关的文档,包括:网络、资源性能和可用性等运营参数,还包含跨服务合同或规则参数的性能;如:订单请求的准时完成率,修复承诺的时间,客户联系的实施等。如果不能满足合同规定的SLA要求,就要采取向客户报告、并对客户进行计费调整等措施,以取得客户的理解和谅解,让客户感到满意。(人民邮电报)
--sunfruit
最近
做了一个资料管理软件用来管理日常资料,编写好了基本功能以后总是感觉这个软件缺少了什么,是的,是全文检索,没有全文检索功能,这个管理软件用到最后也一定对于查找资料相当困难,于是加紧赶工加上了作为基本功能的全文检索。
有人了解检索技术,而且也不是什么高深技术,我也不班门弄斧,就是把原理大概介绍一下,感兴趣的朋友也可以自己尝试编写一下。
全文检索技术其实就是用空间换时间--用硬盘空间换取检索时间。
首先需要字索引,要想建立字索引就需要字库,这个字库其实就是每一个汉字,大概6000个汉字左右吧,再加上a-z,A-Z,0-9,基本上就可以作为字索引的字库了,然后就是利用字库生成字索引。
生成字索引的原来就是以字库为基础,为字库里面的每一个字做索引,在每一个文章里面做匹配,把一个文章里面所有的匹配的位置记录下来,举个例子:比如字库里面的"啊",如果现在要建立"啊"的索引,那么要遍历所有要做索引的文章,把所有的"啊"的位置要记录下来,当然要可以区分位置是哪个文章的,要不然混成一堆,就没有意义了[因为最后检索索引的最终目的是要定位出文章的ID],这样就形成了字索引。依此类推,所有汉字的字索引,索引建立就完成了
检索索引,这个是关键的关键,速度是否快,全看这里,当然索引建立的合理与否也能影响检索速度,举个例子:比如要查询"业务"这个关键词,
首先在字索引中查到"业": 在文件号11111 中的位置有11,40,99 在文件号11112 中的位置有22,33,45
然后在字索引中查到"务": 在文件号11111 中的位置有12,66,100 在文件号11112 中的位置有27,39,60
经过计算会得到命中的文件号为 11111 ,因为业务必须位置号码要连续的出现才算命中
大概的思路就是这样
当然检索技术还会有词索引技术,根据字索引可以进一步生成词索引,并且有切词技术。。。就不多说了
那个资料管理软件的DEMO下载地址 http://www.blogjava.net/sunfruit/archive/2006/04/01/38625.html
DEMO的界面样式如下
检索界面
主界面
注意:使用全文检索的时候,首先要生成索引[在工具菜单栏里面],并且不能重复生成同一个文章的索引,而只能重新生成,这个在生成的时候有选项,因为如果重复生成那么在字索引里面就会重复记录,到时候检索的时候就会检索出来多个同样的文章了
--sunfruit
整天和电脑打交道,信息资料随着时间的推移也积累的越来越多,直到从资料里面找到所需的资料越来越难,于是就编写了一个资料管理软件,用于管理日常资料
说明:
数据库:在数据的存储方面为了方便,使用了Access
JDK :使用的JDK版本是1.5
相关说明:
正本的保存:由于正文包含图片和样式,所以采取了将正文的文档对象序列化到数据库中的方式进行保存和读取
附件的保存:和正文的保存方式一样
由于涉及的功能比较多,所以只是实现了最基本的功能,其他的功能逐步添加和完善,软件的界面样式如下
注:数据检索功能已经完成[使用全文检索技术实现]
自从JDK1.5的推出,JAVA的界面风格比原来漂亮多了
demo下载地址:http://www.fruitres.cn/useruploadfile/4/1473814960_demo.rar
演示地址:http://www.fruitres.cn/useruploadfile/4/1473814960_jnlp.jnlp
使用过程中发现问题欢迎指正和交流
--sunfruit
我们有的时候需要在javascript里面实现延时功能,比如:当某一个按钮按下提交内容以后就马上把状态改为失效,过2秒钟以后再把状态改为有效,才可以进行下一次提交
这个过程就需要延时功能了,这里用到了javascript的setTimeout函数,这个函数的入口参数为2个,第一个参数是需要执行的内容,这个可以是一个URL,也可以是另一个javascript函数,第二个参数是延时的时间数单位是毫秒,下面举个例子
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>无标题文档</title>
<script language="javascript">
function r1()
{
setTimeout("r2()",2000);
}
function r2()
{
alert("OK");
}
</script>
</head>
<body>
<input name="uu" type="button" id="uu" value="按钮" onClick="r1();">
<form name="form1" method="post" action="">
</form>
</body>
</html>
执行这个例子的效果是:按钮按下去以后2秒钟“OK”对话框弹出,呵呵用这个特性实现button的失效和有效就简单多了
这里有一个技巧,就是在页面body的onload属性里面加上setTimeout的执行方法,而且setTimeout方法的第一个参数为执行自身页面,这样的效果就是间隔一定时间刷新本页,也许有人问了,html的自身属性就提供定时刷新页面功能,jsp页面的head属性里面也有类似的功能,干嘛这么麻烦使用setTimeout方法
不知道大家注意了没有,不管是使用html的自身提供定时刷新页面功能还是jsp页面head属性里面类似的功能,都会有浏览器发出的“啪啪”的声音,而使用setTimeout则没有这样的声音,所以使用setTimeout方法还是很有用的
--sunfruit
其实解决办法很简单
首先保证进行了正确的数字签名
然后使用java web start 发布application一定会有jnlp文件,关键就在这里
在jnlp文件里面添加
<security>
<all-permissions/>
</security>
这几行就可以了,当用户执行了app的时候,会弹出提示框提示用户是否信任xxx证书,当用户选择信任以后就可以访问本地系统了
--sunfruit
在使用java web start发布Application还有在网页上面发布Applet的时候如果不进行数字签名,那么会有安全级别的限制,并且在程序界面的最下面会有Application window 的字样,很是不爽,只要是进行了数字签名这些苦恼就没有了,而且也可以访问用户的本地IO系统,下面就说一下如何进行数据签名
其实签名过程很简单,JDK自身就有工具可以进行签名,下面的过程中 xxxx 表示该内容是自定义的
第一部生成 keystore 文件
keytool -genkey -keystore xxxx.keystore -alias xxxx 例如[keytool -genkey -keystore sunfruit.keystore -alias sunfruit]
这个过程比较繁琐,要填写好几项内容,比如生成的keystore文件为 sunfruit.keystore ,下面需要使用
第二步进行数据签名,呵呵,快吧
jarsigner -keystore sunfruit.keystore xxxx.jar sunfruit
命令行中的xxxx.jar是要进行签名的jar文件
第二步进行完毕以后,jar文件就已经签名完毕了,可以使用,当然还有一些其他的功能,比如导出cer文件
keytool -export -keystore sunfruit.keystore -alias sunfruit -file sunfruit.cer
简单吧,其实签名--就是这么简单
--sunfruit
用HttpURLConnection进行Post方式提交,下面给出一个例子
URL url = null;
HttpURLConnection httpurlconnection = null;
try
{
url = new URL("http://xxxx");
httpurlconnection = (HttpURLConnection) url.openConnection();
httpurlconnection.setDoOutput(true);
httpurlconnection.setRequestMethod("POST");
String username="username=02000001";
httpurlconnection.getOutputStream().write(username.getBytes());
httpurlconnection.getOutputStream().flush();
httpurlconnection.getOutputStream().close();
int code = httpurlconnection.getResponseCode();
System.out.println("code " + code);
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
if(httpurlconnection!=null)
httpurlconnection.disconnect();
}
其中HttpURLConnection中的addRequestProperty方法,并不是用来添加Parameter 的,而是用来设置请求的头信息,比如:
setRequestProperty("Content-type","text/html");
setRequestProperty("Connection", "close");
setRequestProperty("Content-Length",xxx);
当然如果不设置的话,可以走默认值,上面的例子中就没有进行相关设置,但是也可以正确执行
-----
-----
-----
-----
-----
-----
-----
-----
-----
-----
-----
--sunfruit
前一段时间写了一个
[原创]JAVA中图片上叠加文字的方法,本来这方面的例子在网络上面很多,而且当时写的时候也没有遇到什么问题,所以也没有什么感觉要注意的地方
昨天由于一些原因需要在一个已有的图片上面画点,然后再显示出来,感觉上和
[原创]JAVA中图片上叠加文字的方法很类似,也就没当回事按照自己的思路就写了,很容易想到读取已存在的图片内容然后生成
ImageIcon imageIcon=new ImageIcon(bytes); 对象然后生成
Image image=imageIcon.getImage(); 对象,这个时候只要是从image实例里面获得Graphics对象就可以对图片进行编辑了但是下面这步却报错了
Graphics g =image.getGraphics(); //这步抱错:UnsupportedOperationException: getGraphics() not valid for images created with createImage(producer)
怎么调试都不行,于是google一把,发现遇到这个问题的人不少,解决办法是不能从Image对象获得Graphics,而是要从BufferedImage对象获得Graphics,于是调整思路将代码修改为
BufferedImage bufferedImage=new BufferedImage(imageIcon.getIconHeight(),imageIcon.getIconWidth(),BufferedImage.TYPE_INT_RGB);
Graphics2D g=(Graphics2D)bufferedImage.getGraphics();这次测试通过可以获得Graphics对象了
这里说明一点:Graphics g =image.getGraphics(); 这一步如果是从Java的图形组件里面获得的Image对象,然后获得Graphics对象就不会有问题
在已有的图片上面画图的完整例子见:
[原创]JAVA在已有图片上面画图的实例
--sunfruit
简介:JAVA在已有图片上面画图的实例,下面的程序在已有的图片上面画了一个蓝色的方块
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import java.util.Random;
import java.io.IOException;
import java.io.File;
public class ImageTest {
public ImageTest() throws Exception {
String ext="png";
FileInputStream in = new FileInputStream("已有图片的路径");
byte[] bytes = new byte[in.available()];
in.read(bytes);
in.close();
Random random=new Random(System.currentTimeMillis());
ImageIcon imageIcon = new ImageIcon(bytes);
BufferedImage bufferedImage=new BufferedImage(imageIcon.getIconHeight(),imageIcon.getIconWidth(),BufferedImage.TYPE_INT_RGB);
Graphics2D g=(Graphics2D)bufferedImage.getGraphics();
g.setColor(Color.blue);
g.drawRect(5,5,5,5);
g.fillRect(5,5,5,5);
g.drawImage(imageIcon.getImage(),0,0,imageIcon.getIconHeight(),imageIcon.getIconWidth(),imageIcon.getImageObserver());
String filepath = System.getProperty("java.io.tmpdir") + random.nextInt(99999) + "." + ext;
try {
ImageIO.write(bufferedImage, ext, new File(filepath));
System.out.println("文件已经生成,路经为" + filepath);
}
catch (IOException ex) {
ex.printStackTrace();
}
}
/**
* @param args
*/
public static void main(String[] args) throws Exception {
new ImageTest();
}
}
--sunfruit
《谈谈心,恋恋爱》,续《
爱,直至成伤》没啥说的,下载地址
觉得好的要顶欧 : )
http://www.blogjava.net/Files/sunfruit/ttxlla.rar
--sunfruit
JDK:1.3.x以上
功能:下面是一个简单在图片上面叠加文字的方法,有朋友如果有这方面的问题,就起个了解的作用
代码如下:
import java.io.IOException;
import javax.imageio.ImageIO;
import java.io.File;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.util.Random;
/**
* <p>Title: 图片叠加文字类</p>
* <p>Description: </p>
* <p>Copyright: Copyright (c) 2005</p>
* <p>Company: </p>
* @author sunfruit
* @version 1.0
*/
public class ImageAddWord {
Random random=new Random(System.currentTimeMillis());
BufferedImage buffImage=null;
Graphics2D g=null;
public ImageAddWord(int width, int height) {
buffImage=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
}
public ImageAddWord()
{
this(32,32);
}
/**
* 图片生成方法,如果需要在已有的图片叠加文字,需要先调用g.drawImage方法将图片绘制,再将文字绘制
* @param str String 文字内容
* @param ext String 文件后缀名 png或是jpg
*/
public void drawWord(String str,String ext)
{
g=buffImage.createGraphics();
/**
如果要在已有的图片叠加文字,这里调用g.drawImage()该方法,绘制图片,酌情去掉下面的方法
*/
g.setColor(Color.WHITE);//在已有的图片叠加文字时 该方法酌情添加
g.fillRect(0, 0, buffImage.getWidth(), buffImage.getHeight());//在已有的图片叠加文字时 该方法酌情添加
g.setColor(Color.BLACK);//设定文字颜色
g.drawString(str,0,12);
String filepath=System.getProperty("java.io.tmpdir") +random.nextInt(99999)+"." + ext;
try {
ImageIO.write(buffImage, ext,
new File(filepath));
System.out.println("文件已经生成,路经为"+filepath);
} catch (IOException ex) {
ex.printStackTrace();
}
}
public void setFont(Font font)
{
g.setFont(font);
}
public static void main(String[] args) {
ImageAddWord imageAddWord=new ImageAddWord(132,16);
String str="A B C";
imageAddWord.drawWord(str,"png");
}
}
--sunfruit
用JAVA编写的桌面程序启动的预显窗口实例
简介:
程序启动会预先显示一个预显窗口,主程序启动完毕后预显窗口关闭
欢迎大家提意见,交流
下载地址
说明:apprun.jar可以直接运行 src中是源代码
http://www.blogjava.net/Files/sunfruit/apprunmodel.rar
--sunfruit
开发web相关程序的时候总是要遇到限制用户不能使用同一个帐号同时多次登录的问题,我从三个方向对这样的问题做了监控
1,用户登录以后点击注销推出
2,用户点击IE的 X 关闭里IE窗口
3,用户的session过期
只要是监控了以上三点,就能满足绝大部分的要求,以上是一个思路,有一个缺陷,就是如果客户机突然断电,那么只有session过期了以后才能登录,这个地方是一个缺陷。
下面给出具体的实现代码,如果大家有更好的办法,欢迎交流,共同进步
http://sunfruit.bokee.com/inc/session.rar
附件说明 :
1.工程使用JB做的,使用了log4j,log4j的包在工程的WEB-INF的LIB文件夹内,取出后重新引用。
2.可以直接发布war包,直接可以测试
--sunfruit
在处理日期的格式转换的时候总结些经验,和大家分享,如下:
String text ="1996-2-1";
Date d = null;
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
df.setLenient(false);//这个的功能是不把1996-13-3 转换为1997-1-3
try
{
d = df.parse(text);
}
catch(Exception e)
{
d=new Date();
System.out.println("你输入的日期不合法,请重新输入");
}
String sdata=df.format(d);
System.out.println(sdata);
这样输入1996-2-31这样的数据也会验证出来错误的,但是前提是要设置Lenient为false
--sunfruit
用JAVA实现了数据库基类
JDK版本
JDK1.4.x
功能
实现了灵活的连接数据库或数据库连接池
提供了update的多种方式
现在提供的有DriverClass和DriverClassTomcat,分别是直接连接和tomcat连接池连接
可以继续写出DriverClassWeblogic等其他的连接方式方到自己指定的地方,然后修改那个xml的class参数就可以了
修改连接方式的时候无需修改代码
欢迎大家提意见,交流
代码下载
http://sunfruit.blogchina.com/inc/database.rar
--sunfruit
用Java事件处理机制实现录制回放功能
目前在一些java应用程序的GUI测试工具,可以提供捕获用户操作的能力并在代码被修改之后能够自动回放用户的操作。文章将分析Java的事件 处理模型及其原理,介绍了基于事件源识别的捕获/回放所需要了解的关键技术并给出了两种实现方式。
1、 Java事件介绍
1.1什么是事件
首先我们来回答"什么是事件"这一基本问题。其实事件本身就是一个抽象的概念,他是表现另一对象状态变化的对象。在面向 对象的程序设计中,事件消息是对象间通信的基本方式。在图形用户界面程序中,GUI组件对象根据用户的交互产生各种类型的事件消息,这些 事件消息由应用程序的事件处理代码捕获,在进行相应的处理后驱动消息响应对象做出反应。我们在GUI上进行叫化操作的时候,在点击某个可 响应的对象时如,按钮,菜单,我们都会期待某个事件的发生。其实围绕GUI的所有活动都会发生事件,但Java事件处理机制却可以让您挑选出 您需要处理的事件。事件在Java中和其他对象基本是一样的,但有一点不同的是,事件是由系统自动生成自动传递到适当的事件处理程序。
1.2Java事件处理的演变
当java的开发者开始解决用java创建应用程序这一问题时,他们就认识到java事件模型的必要性。下面对java事件处理的发展做简要的概 括。
在JDK1.0的版本采用用的事件模型,提供了基本的事件处理功能。这是一种包容模型,所有事件都封装在单一的类Event中,所有事件 对象都由单一的方法handleEvent来处理,这些定义都在Component类中。为此,只有Component类的子类才能充当事件处理程序,事件处理传递 到组件层次结构,如果目标组件不能完全处理事件,事件被传递到目标组件的容器。
JDK1.1是编程界的一次革命,修正了前面版本的一些缺陷,同时增加了一些重要的新功能如,RMI、JNI、JDBC、JavaBean。在事件模型 上基本框架完全重写,并从Java1.0模型迁移到委托事件模型,在委托模型中事件源生成事件,然后事件处理委托给另一段代码。
从JDK1.2开始,引入了Swing包事件处理模型功能更强大,更加可定制GUI组件与他们相关联的支持类。在后面的版本基本保持了整个事 件模型,但加入了一些附加事件类和接口。在1.3版本开始引入Rebot类,它能模拟鼠标和键盘事件,并用于自动化测试、自动运行演示、以及 其他要求鼠标和键盘控制的应用程序。
我们把JDK1.0事件处理模型成为java 1.0事件模型,而从jdk1.1后的版本事件处理模型称为Java 2事件处理模型。
2、 Java 2事件处理模型
在Java1.0事件处理模型中事件处理是以如下方法执行的。deliverEvent()用于决定事件的目标,目标是处理事件的组件或容器,此过程 开始于GUI层的最外部而向内运作。当按一个button时,如果检测到是该按钮激发的事件,该按钮会访问它的deliverEvent()方法,这一操作由 系统完成。一旦识别目标组件,正确事件类型发往组件的postEvent()方法,该方法依次把事件送到handleEvent()方法并且等待方法的返回值 。"true"表明事件完全处理,"false"将使postEvent()方法联系目标容器,希望完成事件处理。
下面给一个实例:
import java.applet.*;
import java.awt.*;
public class Button1Applet extends Applet{
public void init(){
add(new Button("Red"));
add(new Button("Blue"));
}
public boolean action(Enent evt,Object whatAction){
if( !( evt.target instanceof Button))return false;
String buttonlabel=(String)whatAction;
if(buttonlabel=="Red")setBackground(Color.red);
if(buttonlabel==" Blue")setBackground(Color.blue);
repaint();
return true;
}
}
|
在Java2处理事件时,没有采用dispatchEvent()-postEvent()-handleEvent()方式,采用了监听器类,每个事件类都有相关联的监听器 接口。事件从事件源到监听者的传递是通过对目标监听者对象的Java方法调用进行的。
对每个明确的事件的发生,都相应地定义一个明确的Java方法。这些方法都集中定义在事件监听者(EventListener)接口中,这个接 口要继承java.util.EventListener。 实现了事件监听者接口中一些或全部方法的类就是事件监听者。 伴随着事件的发生,相应的状态通常都封装在事件状态对象中,该对象必须继承自java.util.EventObject。事件状态对象作为单参传递给应响 应该事件的监听者方法中。 发出某种特定事件的事件源的标识是:遵从规定的设计格式为事件监听者定义注册方法,并接受对指定事件监听者接口实例的引用。 有时,事件监听者不能直接实现事件监听者接口,或者还有其它的额外动作时,就要在一个源与其它一个或多个监听者之间插入一个事件适配 器类的实例,来建立它们之间的联系。
我们来看下面一个简单的实例:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class SimpleExample extends JFrame {
JButton jButton1 = new JButton();
public SimpleExample() {
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SimpleExample simpleExample = new SimpleExample();
}
private void jbInit() throws Exception {
jButton1.setText("jButton1");
jButton1.addActionListener(new SimpleExample_jButton1_actionAdapter(this));
jButton1.addActionListener(new SimpleExample_jButton1_actionAdapter(this));
this.getContentPane().add(jButton1, BorderLayout.CENTER);
this.setVisible(true);
}
void jButton1_actionPerformed(ActionEvent e) {
System.exit(0);
}
}
class SimpleExample_jButton1_actionAdapter implements java.awt.event.ActionListener {
SimpleExample adaptee;
SimpleExample_jButton1_actionAdapter(SimpleExample adaptee) {
this.adaptee = adaptee;
}
public void actionPerformed(ActionEvent e) {
adaptee.jButton1_actionPerformed(e);
}
}
|
3、 事件捕获与回放
3.1 Java事件生命周期
Java事件和万事一样有其生命周期,会出生也会消亡。下图3.1给出了Java事件生命周期的示意图,
事件最初由事件源产生,事件源可以是GUI组件Java Bean或由生成事件能力的对象,在GUI组件情况下,事件源或者是组件的同位体( 对于Abstract Window Toolkit[awt]GUI组件来说)或组件本身(对于Swing组件来说)。事件生成后放在系统事件队列内部。现在事件处于事件分发线程的控 制下。事件在队列中等待处理,然后事件从事件队列中选出,送到dispatchEvent()方法,dispatchEvent()方法调用processEvent()方法并将 事件的一个引用传递给processEvent()方法。此刻,系统会查看是否有送出事件的位置,如果没有这种事件类型相应的已经注册的监听器,或 者如果没有任何组件受到激活来接收事件类型,事件就被抛弃。当然上图显示的是AWTEvent类的子类的生命周期。dispatchEvent()方法和proc essEvent()方法把AWTEvent作为一个参数。但对,javax.swing.event并不是AWTEvent子类,而是从EventObject直接继承过来,生成这些事件 的对象也会定义fireEvent()方法,此方法将事件送到包含在对象监听器列表内的那种类型的任何监听器。
3.2 Java事件捕获
从上面的分析我们知道,任何事件产生到dispatchEvent()方法分发方法前,所有的事件都是存放在系统事件的队列中,而且所有的事件都 由dispatchEvent()方法来分派。所以只要能重载dispatchEvent()方法就可以获取系统的所有事件,包括用户输入事件。一般来说,系统事件 队列的操作对用户来说是可以控制。它在后台自动完成所要完成的事情,使用EventQueue类可以查看甚至操纵系统事件队列。
Java提供了EventQueue类来访问甚至操纵系统事件队列。EventQueue类中封装了对系统事件队列的各种操作,除dispatchEvent()方法 外,其中最关键的是提供了push()方法,允许用特定的EventQueue来代替当前的EventQueue。只要从EventQueue类中派生一个新类,然后通过p ush()方法用派生类来代替当前的EventQueue类即可。这样,所有的系统事件都会转发到派生EventQueue类。然后,再在派生类中重载dispatch Event()方法就可以截获所有的系统事件,包括用户输入事件。下面一段代码给出一个操纵EventQueue的实例:
import java.awt.*;
import java.awt.event.*;
public class GenerateEventQueue extends Frame implements ActionListener{
Button button1 = new Button();
TextField textField1 = new TextField();
public GenerateEventQueue() {
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
GenerateEventQueue generateEventQueue = new GenerateEventQueue();
}
private void jbInit() throws Exception {
button1.setLabel("button1");
button1.addActionListener(this) ;
textField1.setText("textField1");
this.add(button1, BorderLayout.SOUTH);
this.add(textField1, BorderLayout.CENTER);
EventQueue eq=getToolkit().getSystemEventQueue() ;
eq.postEvent(new ActionEvent(button1,ActionEvent.ACTION_PERFORMED,"test" )) ;
addWindowListener(new WinListener());
setBounds(100,100,300,200);
setVisible(true);
}
public void actionPerformed(ActionEvent e) {
textField1.setText("event is :"+e.getActionCommand()) ;
}
}
class WinListener extends WindowAdapter{
public void windowClosing(WindowEvent we){
System.exit(0) ;
}
}
|
运行结果如下图所示:
在文本域中首先出现的是"event is :test",这是因为首先得到处理的是EventQueue对象发送到系统事件队列上的ActionEvent。
下面的代码简单说明了如何捕获事件:
import java.awt.EventQueue;
import java.awt.*;
import java.util.*;
public class MyQueueEvent extends EventQueue {//定义EventQueue的子类
public MyQueueEvent() {
}
public static void main(String[] args) {
SimpleExample.main(new String[]{null}) ;
MyQueueEvent myQueueEvent1 = new MyQueueEvent();
Toolkit.getDefaultToolkit().getSystemEventQueue().push(myQueueEvent1) ;
}
//在这里重载事件分发的方法
public void dispatchEvent(AWTEvent ae){
if(ae.getSource() instanceof javax.swing.JButton)
System.out.println("My apture:"+((javax.swing.JButton)ae.getSource()).getText()) ;
super.dispatchEvent(ae);
}
|
这个程序可以打印出当前应用的所有的事件,可以将这些事件中选出你需要的事件保存当然你还需要解析该控件的特征。在上面加黑部 分的代码,打印事件源控件的名称。
除此之外,还可以通过实现java.awt.event. AWTEventListener接口实现对事件的捕获。这个侦听器接口可以接收Component or MenuComponent 以及它们的派生类在整个系统范围内所分发的事件,AWTEventListeners只是被动的监控这些事件。如果要监控系统事件,除了要实现接口,还 要用Toolkit的addAWTEventListener方法注册这个侦听器。
下面我们来看一个实例:
import java.awt.AWTEvent;
import java.awt.Frame;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.AWTEventListener;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.lang.ref.WeakReference;
public class MyAWTEventListener implements AWTEventListener{
private static MyAWTEventListener s_singleton = null;//保证该类只被初始化一次
public static MyAWTEventListener getInstance(){
if(s_singleton==null){
s_singleton=new MyAWTEventListener();
}
return s_singleton;
}
private MyAWTEventListener(){
//注意下面这行代码,如果没有这行代码,将无法接收到系统分发的事件
// 下面代码在注册时,只请求了接收WINDOW_EVENT_MASK事件
//但实际上,你可以接收其他AWTEvent中定义的事件类型
Toolkit.getDefaultToolkit().addAWTEventListener(this,
AWTEvent.COMPONENT_EVENT_MASK
);
}
/*
这就是接口方法的实现
*/
public void eventDispatched(final AWTEvent theEvent) {
processEvent(theEvent);
}
private static void processEvent(final AWTEvent theEvent) {
System.out.println(theEvent.getSource() ) ;//打印事件源
switch (theEvent.getID()) {
case WindowEvent.WINDOW_OPENED:
//System.out.println(((Frame)theEvent.getSource()).getTitle() ) ;
case WindowEvent.WINDOW_ACTIVATED:
case WindowEvent.WINDOW_DEACTIVATED:
case WindowEvent.WINDOW_CLOSING:
default: break;
}
}
}
|
3.3 Java事件回放
事件的回放其实比较简单了,比如我们现在记录的是frame1下的jButton1点击事件回放。看下面一段简单的程序,只要点一下jButton1, 就在控制台打印一次"click me"的字符串。
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Frame1 extends JFrame {
private JButton jButton1 = new JButton();
public Frame1() {
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Frame1 frame1 = new Frame1();
frame1.setVisible(true) ;
}
private void jbInit() throws Exception {
jButton1.setText("jButton1");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
jButton1_actionPerformed(e);
}
});
this.setTitle("Test");
this.getContentPane().add(jButton1, BorderLayout.CENTER);
}
void jButton1_actionPerformed(ActionEvent e) {
System.out.println("click me") ;
}
}
|
下面是回放的程序,在下面的程序中用到了java.awt.Robot类,这个类通常用来在自动化测试或程序演示中模拟系统事件,在某些需要 控制鼠标或键盘的应用程序中这个类也是很有用,这个类主要的目的就是为方便的实现java的GUI自动化测试平台。在事件回放时,我们同样需 要该类来模拟生成系统的事件,完成记录的操作的回放,在下面的代码中,给出了一个简单的例子。
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class TestReplay extends Thread{
public static void main(String[] args) {
try{
//启动要回放的应用程序
Frame1.main(new String[]{null}) ;
//等应用程序启动后延迟3秒再进行回放
Thread.currentThread().sleep(3000) ;
Robot robottest=new Robot();
robottest.waitForIdle();
//根据标题名获取当前应用的主窗体,在本例中为"test"
Frame jframe=getFrame("test");;
//根据给定的窗体和窗体中要find的控件的名称来获取控件的引用
JButton jbtn=getButton(jframe,"jButton1");
//将鼠标移到控件所在的位置
robottest.mouseMove(jbtn.getLocationOnScreen().x+jbtn.getWidth()/2
,jbtn.getLocationOnScreen().y+jbtn.getHeight()/2) ;
//在控件所在位置,生成鼠标点击事件
robottest.mousePress(InputEvent.BUTTON1_MASK ) ;
robottest.mouseRelease(InputEvent.BUTTON1_MASK ) ;
}catch(Exception ee){
ee.printStackTrace() ;
}
}
//获得标题为title的frame
private static Frame getFrame(String title){
Frame[] jframes=(Frame[])JFrame.getFrames();
for(int i=0;i<jframes.length ;i++){
if(jframes[i].getTitle().equalsIgnoreCase(title))return jframes[i];
}
return null;
}
//获取某一个frame下的某个名为jButton1的控件
private static JButton getButton(Frame jf,String text){
/*注意下面这行代码,因为实例比较简单只有ContentPane一个Container类型的控件,
如果在JFrame中有多个Container控件//的话,必须进行递归处理,搜索出所有的控件
*/
Component[] coms=((JFrame)jf).getContentPane().getComponents();
for(int i=0;i<coms.length ;i++){
if(!(coms[i] instanceof JButton))continue;
if(((JButton)coms[i]).getText().equalsIgnoreCase(text))return (JButton)coms[i];
}
return null;
}
public void run(){
}
}
|
该程序运行完,你会发现在控制台同样打印出了:
"click me"的字符串说明事件被正确回放了。
当然还可以通过直接操纵系统事件队列实现输入事件的回放。先通过记录下的窗口/组件名获得对应窗口引用,然后重构鼠标/键盘事件 ,最后将重构的事件直接放入系统事件队列,由分派线程执行后续的事件分派工作。还需要解决关键问题如何能根据窗口名称获得其引用。这 里还是可以通过系统事件队列来实现的,因为Java程序在新建/删除一个容器时都会向系统事件队列发出一个Containerevent事件,其中包含了 对该容器的引用。所以,事件回放器在载入被测测试程序后便监视系统队列,截获所有的Containerevent事件。如果新建容器,便获得新建Con tainer的引用。因为所有的Container都实现了getComponets(),可以返回所有该容器所包含的组件或容器,只需要保存到一个HashMap结构中 ,需要时检索出来就可以了。该过程所用到的知识,其实在上面都有提到而且在实际引用中,既然Robot已经帮我们完成许多事情,也没有必要 自己再去重构一个鼠标或键盘事件了,不过有兴趣的朋友也可以去试试。
4、 结束语
随着我国软件业的发展,软件测试技术作为软件质量保证的重要环节越来越受到重视,而在基于GUI的应用中采用自动化测试工具可以提高 软件测试的有效性和效率,特别在回归测试中可以大大减少人力投入,还可以提高测试脚本的复用。因此,软件自动测试平台开发已经成为软 件测试的一个重要领域。本文介绍了基于Java的GUI应用的自动测试平台开发需要的基本但关键的捕获、回放功能,所有相关系统开发其实都离 不开本文说的方法。
--sunfruit
关于加密解密的理论知识已经不少了,这里只给出一个加密解密以及生成key的源代码,给大家参考
源代码下载地址:
http://sunfruit.blogchina.com/inc/des.rar
--sunfruit
java读取文件或是文件流的代码,涵盖了读取jar文件中的文件流,网络文件流等,有些读取方式为了防止编码转换带来的问题,采取了动态byte[]的方式读取,源码如下
import java.io.BufferedInputStream;
import java.io.File;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class Util {
public Util() {
}
/**
* 读取源文件内容
* @param filename String 文件路径
* @throws IOException
* @return byte[] 文件内容
*/
public static byte[] readFile(String filename) throws IOException {
File file =new File(filename);
if(filename==null || filename.equals(""))
{
throw new NullPointerException("无效的文件路径");
}
long len = file.length();
byte[] bytes = new byte[(int)len];
BufferedInputStream bufferedInputStream=new BufferedInputStream(new FileInputStream(file));
int r = bufferedInputStream.read( bytes );
if (r != len)
throw new IOException("读取文件不正确");
bufferedInputStream.close();
return bytes;
}
/**
* 将数据写入文件
* @param data byte[]
* @throws IOException
*/
public static void writeFile(byte[] data,String filename) throws IOException {
File file =new File(filename);
file.getParentFile().mkdirs();
BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(new FileOutputStream(file));
bufferedOutputStream.write(data);
bufferedOutputStream.close();
}
/**
* 从jar文件里读取class
* @param filename String
* @throws IOException
* @return byte[]
*/
public byte[] readFileJar(String filename) throws IOException {
BufferedInputStream bufferedInputStream=new BufferedInputStream(getClass().getResource(filename).openStream());
int len=bufferedInputStream.available();
byte[] bytes=new byte[len];
int r=bufferedInputStream.read(bytes);
if(len!=r)
{
bytes=null;
throw new IOException("读取文件不正确");
}
bufferedInputStream.close();
return bytes;
}
/**
* 读取网络流,为了防止中文的问题,在读取过程中没有进行编码转换,而且采取了动态的byte[]的方式获得所有的byte返回
* @param bufferedInputStream BufferedInputStream
* @throws IOException
* @return byte[]
*/
public byte[] readUrlStream(BufferedInputStream bufferedInputStream) throws IOException {
byte[] bytes = new byte[100];
byte[] bytecount=null;
int n=0;
int ilength=0;
while((n=bufferedInputStream.read(bytes))>=0)
{
if(bytecount!=null)
ilength=bytecount.length;
byte[] tempbyte=new byte[ilength+n];
if(bytecount!=null)
{
System.arraycopy(bytecount,0,tempbyte,0,ilength);
}
System.arraycopy(bytes,0,tempbyte,ilength,n);
bytecount=tempbyte;
if(n<bytes.length)
break;
}
return bytecount;
}
}
--sunfruit
写了一个收发邮件的应用程序[在列表里面可以看到]但是毕竟有些复杂,关键部分其实也就是几行代码,为了大家使用方便,我把发送邮件的代码单独拿了出来,并且分为发送附件/不发送附件两个方法,便于大家查看,只是什么设计啦,编程思想啦,等等就谈不到了,呵呵,大家将就吧
JDK版本
1.4.x
其 他
JAVAMAIL相关包
功能简介:
简单的邮件发送功能,可以发送附件
源代码如下
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
import java.util.*;
import java.util.*;
import java.text.*;
import java.io.*;
public class SendMail
{
//传入的参数有密码、姓名、谁发、发给谁、主题、正文内容、smtp地址、附件文件路径、附件的新文件名、发送类型(text/html)
//发送邮件主函数
public String sendmail(int myport,String password,String username,String myfrom,String myto,String mysubject,String mytext,String mysmtp,String[] filepath,String[] newfilename,String htmlandtext)
{
try{
int indexstr=0;
if (filepath[0]!=null && !filepath[0].equals(""))
indexstr=1;
//替换字符串
// jbemail myjbemail=new jbemail();
// filepath=myjbemail.myreplace(filepath,"\\","\\\\");
// System.out.println("附件地址"+filepath+"服务器地址"+mysmtp+mysmtp.length());
//Properties props = new Properties();
Properties props = System.getProperties();
Session sendMailSession;
Store store; //收邮件时使用
Transport transport;//发邮件时使用
props.put("mail.smtp.host",mysmtp);
props.put("mail.smtp.auth","true");
SmtpAuthenticator sa=new SmtpAuthenticator(username,password);
sendMailSession = Session.getInstance(props,sa);
//sendMailSession = Session.getInstance(props,null);
sendMailSession.setDebug(true);
MimeMessage newMessage = new MimeMessage(sendMailSession);
newMessage.setFrom(new InternetAddress(myfrom));
newMessage.setRecipient(Message.RecipientType.TO, new InternetAddress(myto));
//设定邮件格式
newMessage.setSentDate(new Date());
System.out.println(htmlandtext+"邮件正文格式");
Multipart multipart = new MimeMultipart();
if (htmlandtext.equals("text"))
{
//获得文本格式的邮件
newMessage.setSubject(mysubject);
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setText(mytext);
multipart.addBodyPart(messageBodyPart);
}
else if(htmlandtext.equals("html"))
{
//设置邮件内容,将邮件body部分转化为HTML格式
newMessage.setSubject(mysubject,"gb2312");
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setDataHandler(new DataHandler(mytext,"text/html;charset=gb2312"));
multipart.addBodyPart(messageBodyPart);
}
if (indexstr>0)
{
for(int i=0;i {
if (newfilename[i]!=null)
{
//创建BodyPart对象以便获得附件
BodyPart messageBodyPart = new MimeBodyPart();
System.out.println("附件地址"+filepath[i]);
DataSource source = new FileDataSource(filepath[i]);
messageBodyPart.setDataHandler(new DataHandler(source));
messageBodyPart.setFileName(newfilename[i]);
multipart.addBodyPart(messageBodyPart);
}
}
}
//将正文和附件添加到邮件中
newMessage.setContent(multipart);
newMessage.saveChanges();
//transport = sendMailSession.getStore("pop3");
transport = sendMailSession.getTransport("smtp");
transport.connect(mysmtp,myport,username,password);
//transport.connect();
transport.send(newMessage,newMessage.getAllRecipients());
System.out.println("成功发送到"+myto);
return "ok";
}
catch(MessagingException m)
{
System.out.println(m.toString()+"失败");
return myto;
}
}
//不含发送附件的函数
//传入的参数有port地址、密码、姓名、谁发、发给谁、主题、正文内容、smtp地址、发送类型(text/html)
public String sendmail(String mailPathlog,int myport,String password,String username,String myfrom,String myto,String mysubject,String mytext,String mysmtp,String htmlandtext)
{
try{
//解码
mysubject=java.net.URLDecoder.decode(mysubject);
//Properties props = new Properties();
Properties props = System.getProperties();
Session sendMailSession;
Store store; //收邮件时使用
Transport transport;//发邮件时使用
props.put("mail.smtp.host",mysmtp);
props.put("mail.smtp.auth","true");
SmtpAuthenticator sa=new SmtpAuthenticator(username,password);
//身份验证
sendMailSession = Session.getInstance(props,sa);
//sendMailSession = Session.getInstance(props,null);
sendMailSession.setDebug(true);
MimeMessage newMessage = new MimeMessage(sendMailSession);
try
{
newMessage.setFrom(new InternetAddress(myfrom,"法律之星"));
newMessage.setRecipient(Message.RecipientType.TO, new InternetAddress(myto));
}
catch(java.io.UnsupportedEncodingException ex)
{
System.out.println(ex.toString());
}
//设定邮件格式
newMessage.setSentDate(new Date());
System.out.println(htmlandtext+"邮件正文格式");
Multipart multipart = new MimeMultipart();
if (htmlandtext.equals("text"))
{
//获得文本格式的邮件
newMessage.setSubject(mysubject);
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setText(mytext);
multipart.addBodyPart(messageBodyPart);
}
else if(htmlandtext.equals("html"))
{
//设置邮件内容,将邮件body部分转化为HTML格式
newMessage.setSubject(mysubject,"gb2312");
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setDataHandler(new DataHandler(mytext,"text/html;charset=gb2312"));
multipart.addBodyPart(messageBodyPart);
}
//将正文添加到邮件中
newMessage.setContent(multipart);
newMessage.saveChanges();
//transport = sendMailSession.getStore("pop3");
transport = sendMailSession.getTransport("smtp");
transport.connect(mysmtp,myport,username,password);
//transport.connect();
transport.send(newMessage,newMessage.getAllRecipients());
System.out.println("成功发送到"+myto+mytext);
return "ok";
}
catch(MessagingException m)
{
System.out.println(m.toString()+"失败");
//生成当前日期
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date dateTime= new Date();
String sDateTime=dateFormat.format(dateTime);
//生成日志文件
try
{
File filelog=new File(mailPathlog+"\\"+"mainlog.txt");
BufferedWriter out2=new BufferedWriter(new FileWriter(filelog.getPath(),true));
String newline = System.getProperty("line.separator");
out2.write(sDateTime+"/"+mysmtp+"/"+myfrom+"/"+myto+"/"+m.toString()+"/"+newline);
out2.close();
}
catch (IOException ex)
{
System.out.println(ex.toString());
}
return myto;
}
}
class SmtpAuthenticator extends Authenticator
{
//SMTP身份验证
public SmtpAuthenticator(String username,String password)
{
this.username=username;
this.password=password;
}
public PasswordAuthentication getPasswordAuthentication()
{
return new PasswordAuthentication(this.username,this.password);
}
String username=null;
String password=null;
}
}
--sunfruit
很多介绍用户自定义事件都没有例子,或是例子不全,下面写了一个完整的例子,并写入了注释以便参考,完整的实例源代码如下:
package demo;
import java.util.EventObject;
/**
* Title: 事件处理类,继承了事件基类
* Description:
* Copyright: Copyright (c) 2005
* Company: cuijiang
* @author not attributable
* @version 1.0
*/
public class DemoEvent extends EventObject
{
private Object obj;
private String sName;
public DemoEvent(Object source,String sName) {
super(source);
obj = source;
this.sName=sName;
}
public Object getSource()
{
return obj;
}
public void say()
{
System.out.println("这个是 say 方法...");
}
public String getName()
{
return sName;
}
}
package demo;
import java.util.EventListener;
/**
* Title: 监听器接口
* Description:
* Copyright: Copyright (c) 2005
* Company: cuijiang
* @author not attributable
* @version 1.0
*/
public interface DemoListener extends EventListener{
public void demoEvent(DemoEvent dm);
}
package demo;
import java.util.*;
/**
*Title: 使用事件的类
* Description: 该类实现了监听器的添加和监听器方法的执行,并且实现了由于属性的改变而执行事件
* Description: 在添加、删除、执行监听器的时候都要注意同步问题
* Copyright: Copyright (c) 2005
* Company: cuijiang
* @author not attributable
* @version 1.0
*/
public class DemoSource{
private Vector repository = new Vector();
private DemoListener dl;
private String sName="";
public DemoSource()
{
}
//注册监听器,如果这里没有使用Vector而是使用ArrayList那么要注意同步问题
public void addDemoListener(DemoListener dl)
{
repository.addElement(dl);//这步要注意同步问题
}
//如果这里没有使用Vector而是使用ArrayList那么要注意同步问题
public void notifyDemoEvent(DemoEvent event) {
Enumeration enum = repository.elements();//这步要注意同步问题
while(enum.hasMoreElements())
{
dl = (DemoListener)enum.nextElement();
dl.demoEvent(event);
}
}
//删除监听器,如果这里没有使用Vector而是使用ArrayList那么要注意同步问题
public void removeDemoListener(DemoListener dl)
{
repository.remove(dl);//这步要注意同步问题
}
/**
* 设置属性
* @param str1 String
*/
public void setName(String str1)
{
boolean bool=false;
if(str1==null && sName!=null) bool=true;
else if(str1!=null && sName==null) bool=true;
else if(!sName.equals(str1)) bool=true;
this.sName=str1;
//如果改变则执行事件
if(bool) notifyDemoEvent(new DemoEvent(this,sName));
}
public String getName()
{
return sName;
}
}
package demo;
import java.lang.Thread;
/**
* Title: 测试类
* Description: 测试了由于改变属性而引起的事件发生
* Copyright: Copyright (c) 2005
* Company: cuijiang
* @author not attributable
* @version 1.0
*/
public class TestDemo
implements DemoListener {
private DemoSource ds;
public TestDemo()
{
ds=new DemoSource();
ds.addDemoListener(this);
System.out.println("添加监听器完毕");
try {
Thread.sleep(3000);
//改变属性,触发事件
ds.setName("改变属性,触发事件");
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
ds.addDemoListener(this);
System.out.println("添加监听器完毕2");
try {
Thread.sleep(3000);
//改变属性,触发事件
ds.setName("改变属性,触发事件2");
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
ds.removeDemoListener(this);
System.out.println("添加监听器完毕3");
try {
Thread.sleep(3000);
//改变属性,触发事件
ds.setName("改变属性,触发事件3");
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
}
public static void main(String args[])
{
new TestDemo();
}
/**
* demoEvent
* @param dm DemoEvent
* @todo Implement this test.DemoListener method
*/
public void demoEvent(DemoEvent dm) {
System.out.println("事件处理方法");
System.out.println(dm.getName());
dm.say();
}
}
--sunfruit
用JAVA实现了带有复选框的树目录
JDK版本
JDK1.4.x
功能
实现了带有复选框的资源管理器树目录,还有需要改进的地方,我在以后更新,如果那位朋友有好的建议欢迎提出
欢迎大家提意见,交流
代码如下
import javax.swing.tree.*;
import javax.swing.filechooser.*;
import javax.swing.event.*;
import java.awt.Cursor;
import java.awt.Component;
import java.awt.Font;
import java.io.*;
import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
/**
* Title: 系统级树目录
* Description:
* Copyright: Copyright (c) 2004
* Company:
* @author cuijiang contact cj0063@sina.com or cuij7718@yahoo.com.cn
* @version 1.0
*/
public class AgileSuperJTreeBasic
extends JTree
implements TreeExpansionListener, TreeSelectionListener, MouseListener {
protected DefaultTreeModel treeModel;
protected FileSystemView fileSystemView; //建立文件系统视类对象
protected FileNode root;
public AgileSuperJTreeBasic() {
Font myFont = new Font("宋体", 11, 12);
fileSystemView = FileSystemView.getFileSystemView();
root = new FileNode(fileSystemView.getRoots()[0]);
root.explore();
treeModel = new DefaultTreeModel(root);
this.setModel(treeModel); //设定树形菜单
this.addTreeExpansionListener(this); //打开/关闭节点事件
this.addTreeSelectionListener(this); //选择的事件
this.setCellRenderer(new MyTreeCellRenderer()); //生成图标
this.setFont(myFont);
this.setRootVisible(true);
this.setRowHeight(18);
this.addMouseListener(this);
}
//图标生成类
protected class MyTreeCellRenderer
extends JPanel
implements TreeCellRenderer {
JCheckBox check = new JCheckBox();
BorderLayout borderLayout1 = new BorderLayout();
JLabel label = new JLabel();
public MyTreeCellRenderer() {
this.setLayout(null);
this.add(check);
this.add(label);
check.setBackground(UIManager.getColor("Tree.textBackground"));
label.setBackground(UIManager.getColor("Tree.textBackground"));
this.setBackground(UIManager.getColor("Tree.textBackground"));
}
public Dimension getPreferredSize() {
Dimension checkDimension = check.getPreferredSize();
Dimension labelDimension = label.getPreferredSize();
return new Dimension(checkDimension.width + labelDimension.width,
(checkDimension.height < labelDimension.height ?
labelDimension.height : checkDimension.height));
}
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean sel, boolean expanded,
boolean leaf, int row,
boolean hasFocus) {
String stringValue = tree.convertValueToText(value, sel, expanded, leaf,
row, hasFocus);
setEnabled(tree.isEnabled());
label.setFont(tree.getFont());
check.setSelected( ( (FileNode) value).isSelected());
//设置图标为系统的文件类型图标
FileSystemView fileSystemView = FileSystemView.getFileSystemView();
label.setIcon(fileSystemView.getSystemIcon( ( (FileNode) value).getFile()));
label.setText(stringValue);
return this;
}
public void doLayout() {
Dimension checkDimension = check.getPreferredSize();
Dimension labelDimension = label.getPreferredSize();
int checkY = 0;
int labelY = 0;
if (checkDimension.height > labelDimension.height) {
labelY = (checkDimension.height - labelDimension.height) / 2;
}
else {
checkY = (labelDimension.height - checkDimension.height) / 2;
}
check.setLocation(0, checkY);
check.setBounds(0, checkY, checkDimension.width, checkDimension.height);
label.setLocation(checkDimension.width, labelY);
label.setBounds(checkDimension.width, labelY, labelDimension.width,
labelDimension.height);
}
}
//节点张开事件
public void treeExpanded(TreeExpansionEvent event) {
//判断是否是叶节点
//if (this.getLastSelectedPathComponent() == null) {
//System.out.println("ok");
//return;
//}
setCursor(new Cursor(Cursor.WAIT_CURSOR));
TreePath path = event.getPath();
//System.out.println(path.toString());
FileNode node = (FileNode) path.getLastPathComponent();
node.explore();
treeModel.nodeStructureChanged(node);
this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
//节点闭合事件
public void treeCollapsed(TreeExpansionEvent event) {
}
//文件节点类
protected class FileNode
extends DefaultMutableTreeNode {
private boolean isSelected = false;
private boolean explored = false;
public FileNode(File file) {
this(file, false);
}
public FileNode(File file, boolean bool) {
super(file);
this.isSelected = bool;
}
//
public boolean isSelected() {
return isSelected;
}
public void setSelected(boolean isSelected) {
this.isSelected = isSelected;
if (children != null) {
Enumeration enum = children.elements();
while (enum.hasMoreElements()) {
FileNode node = (FileNode) enum.nextElement();
node.setSelected(isSelected);
}
}
}
//
public boolean getAllowsChildren() {
return isDirectory();
}
public boolean isLeaf() {
return!isDirectory();
}
public File getFile() {
return (File) getUserObject();
}
public boolean isExplored() {
return explored;
}
public void setExplored(boolean b) {
explored = b;
}
public boolean isDirectory() {
return getFile().isDirectory();
}
public String toString() {
File file = (File) getUserObject();
String filename = file.toString();
int index = filename.lastIndexOf(File.separator);
return (index != -1 && index != filename.length() - 1)
? filename.substring(index + 1) : filename;
}
public void explore() {
if (!isExplored()) {
File file = getFile();
File[] children = file.listFiles();
if (children == null || children.length == 0)
return;
for (int i = 0; i < children.length; ++i) {
File f = children[i];
if (f.isDirectory())
add(new FileNode(children[i], isSelected));
}
explored = true;
}
}
}
/**
* 选择节点触发的事件
* 继承或是直接引用需要重新写此方法
* @param e
*/
public void valueChanged(TreeSelectionEvent e) {
//文件路径
String sFilePath = "";
Object myobj = this.getLastSelectedPathComponent();
if (myobj != null) {
sFilePath = ( (File) ( ( (DefaultMutableTreeNode) (myobj)).getUserObject())).
getPath();
}
//System.out.println(sFilePath);
}
/**
* Invoked when the mouse button has been clicked (pressed and released) on a
* component.
* @param e MouseEvent
* @todo Implement this java.awt.event.MouseListener method
*/
public void mouseClicked(MouseEvent e) {
int count = e.getClickCount();
if (count != 1) {
//System.out.println(count);
}
else {
int x = e.getX();
int y = e.getY();
int row = this.getRowForLocation(x, y);
TreePath path = this.getPathForRow(row);
if (path != null) {
FileNode node = (FileNode) path.getLastPathComponent();
boolean isSelected = ! (node.isSelected());
node.setSelected(isSelected);
( (DefaultTreeModel)this.getModel()).nodeChanged(node);
}
}
}
/**
* Invoked when a mouse button has been pressed on a component.
* @param e MouseEvent
* @todo Implement this java.awt.event.MouseListener method
*/
public void mousePressed(MouseEvent e) {
}
/**
* Invoked when a mouse button has been released on a component.
* @param e MouseEvent
* @todo Implement this java.awt.event.MouseListener method
*/
public void mouseReleased(MouseEvent e) {
}
/**
* Invoked when the mouse enters a component.
* @param e MouseEvent
* @todo Implement this java.awt.event.MouseListener method
*/
public void mouseEntered(MouseEvent e) {
}
/**
* Invoked when the mouse exits a component.
* @param e MouseEvent
* @todo Implement this java.awt.event.MouseListener method
*/
public void mouseExited(MouseEvent e) {
}
}
--sunfruit
用户自定义事件
User defined Event listeners javaAll of us who have used java.awt.event package in either applets
or in Frames or in swing JFrame, shall agree to the fact that event
propagation and listener concept is very very useful for capturing
any action initiated either by user or by system, should be caught
at the right place and without unneccessarily wasting time traversing
through all the way from begining to end of source code just to know
any takers for this event.
This concept is implemented in form of MouseEvent, KeyEvent, WindowEvent so on so forth.
JDK has given us a handle for creating our own event object and as well
as event handling framework using java.util.EventObject and java.util.EventListener.
You can imagine a scenario whereby you have to write an application, in
which a email notification feature in a forum.
Whenever a special event takes place , then your have to notify all the users
who have registered themselves for this program.
One solution is to use a Database and query all the users from the table
and send notification to each and every user.
But this is more of Database centric approach like old days thick client
and server model.
Think about the overhead on database and instead another solution is
to create an event notification framework, whereby you have to create
your own version of Event Object and Event Listeners.
Event object represents the special event and all the users who register
themselves for the event implements this Event listener.
Let us take a more deeper plunge into this:
1. Create a Demo event by creating a DemoEvent.java file that
extends to java.util.EventObject.
This class has a constructor that takes the event originator object (DemoSource).
and a method getSource(), that gives the reference to the object that originated
this event.
DemoEvent.java
package demo.listener;
import java.util.EventObject;
public class DemoEvent extends EventObject
{
Object obj;
public DemoEvent(Object source)
{
super(source);
obj = source;
}
public Object getSource()
{
return obj;
}
public void say()
{
System.out.println("This is say method...");
}
}
|
2. Create the source of event notification file DemoSource.java
It has a java.util.Vector that acts like a repository for storing all the
listeners to the events this source provides.
It has two other common method such as addListener(..) is to add any
new user to this event.
And notifyDemoEvent(..) is to alert all the users about this perticular event
occurance.
DemoEvent.java
package demo.listener;
import java.util.*;
public class DemoSource
{
private Vector repository = new Vector();
DemoListener dl;
public DemoSource()
{
}
public void addDemoListener(DemoListener dl)
{
repository.addElement(dl);
}
public void notifyDemoEvent()
{
Enumeration enum = repository.elements();
while(enum.hasMoreElements())
{
dl = (DemoListener)enum.nextElement();
dl.demoEvent(new DemoEvent(this));
}
}
}
|
3. Define a listener interface that extends to java.util.EventListener
and contains the method for invoking the Event, here it is demoEvent(DemoEvent de).
DemoListener.java
package demo.listener;
import java.util.EventListener;
public interface DemoListener extends EventListener
{
public void demoEvent(DemoEvent dm);
}
|
4. Define all the users, who want to listen to this events.
These objects should implement DemoListener, to be able to
capture the event propagated from DemoSource.java.
Listener1.java
package demo.listener;
public class Listener1 implements DemoListener
{
public void demoEvent(DemoEvent de)
{
System.out.println("Inside listener1...");
}
}
|
Listener2.java
package demo.listener;
public class Listener2 implements DemoListener
{
public void demoEvent(DemoEvent de)
{
System.out.println("Inside listener2...");
}
}
|
Listener3.java
package demo.listener;
public class Listener3 implements DemoListener
{
public void demoEvent(DemoEvent de)
{
System.out.println("Inside listener3...");
}
}
|
5. Now it time to write the client for testing this Framework.
Create a client such as TestDemo.java and create a DemoSource
object and all the users such as Listener1, Listener2, Listener3.
And add all these listeners to the demo source by using the addDemoListener(..).
When the notifyDemoEvent(..) is called on the demo source object,
the event gets notified to all the users (listener1, listener2, listener3)
and the output comes out to be
Inside listener1...
Inside listener2...
Inside listener3...
TestDemo.java
package demo.listener;
public class TestDemo
{
DemoSource ds;
public TestDemo()
{
try{
ds = new DemoSource();
Listener1 l1 = new Listener1();
Listener2 l2 = new Listener2();
Listener3 l3 = new Listener3();
ds.addDemoListener(l1);
ds.addDemoListener(l2);
ds.addDemoListener(l3);
ds.notifyDemoEvent();
}catch(Exception ex){ex.printStackTrace();}
}
public static void main(String args[])
{
new TestDemo();
}
}
|
--sunfruit
介绍了JAVA的反射机制,比较全面
Java Reflection (JAVA反射)
Reflection 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性。例如,使用它能获得 Java 类中各成员的名称并显示出来。
Java 的这一能力在实际应用中也许用得不是很多,但是在其它的程序设计语言中根本就不存在这一特性。例如,Pascal、C 或者 C++ 中就没有办法在程序中获得函数定义相关的信息。
JavaBean 是 reflection 的实际应用之一,它能让一些工具可视化的操作软件组件。这些工具通过 reflection 动态的载入并取得 Java 组件(类) 的属性。
1. 一个简单的例子
考虑下面这个简单的例子,让我们看看 reflection 是如何工作的。
import java.lang.reflect.*;
public class DumpMethods {
public static void main(String args[]) {
try {
Class c = Class.forName(args[0]);
Method m[] = c.getDeclaredMethods();
for (int i = 0; i < m.length; i++)
System.out.println(m[i].toString());
} catch (Throwable e) {
System.err.println(e);
}
}
}
按如下语句执行:
java DumpMethods java.util.Stack
它的结果输出为:
public java.lang.Object java.util.Stack.push(java.lang.Object)
public synchronized java.lang.Object java.util.Stack.pop()
public synchronized java.lang.Object java.util.Stack.peek()
public boolean java.util.Stack.empty()
public synchronized int java.util.Stack.search(java.lang.Object)
这样就列出了java.util.Stack 类的各方法名以及它们的限制符和返回类型。
这个程序使用 Class.forName 载入指定的类,然后调用 getDeclaredMethods 来获取这个类中定义了的方法列表。java.lang.reflect.Methods 是用来描述某个类中单个方法的一个类。
2.开始使用 Reflection
用于 reflection 的类,如 Method,可以在 java.lang.relfect 包中找到。使用这些类的时候必须要遵循三个步骤:第一步是获得你想操作的类的 java.lang.Class 对象。在运行中的 Java 程序中,用 java.lang.Class 类来描述类和接口等。
下面就是获得一个 Class 对象的方法之一:
Class c = Class.forName("java.lang.String");
这条语句得到一个 String 类的类对象。还有另一种方法,如下面的语句:
Class c = int.class;
或者
Class c = Integer.TYPE;
它们可获得基本类型的类信息。其中后一种方法中访问的是基本类型的封装类 (如 Integer) 中预先定义好的 TYPE 字段。
第二步是调用诸如 getDeclaredMethods 的方法,以取得该类中定义的所有方法的列表。
一旦取得这个信息,就可以进行第三步了——使用 reflection API 来操作这些信息,如下面这段代码:
Class c = Class.forName("java.lang.String");
Method m[] = c.getDeclaredMethods();
System.out.println(m[0].toString());
它将以文本方式打印出 String 中定义的第一个方法的原型。
在下面的例子中,这三个步骤将为使用 reflection 处理特殊应用程序提供例证。
模拟 instanceof 操作符
得到类信息之后,通常下一个步骤就是解决关于 Class 对象的一些基本的问题。例如,Class.isInstance 方法可以用于模拟 instanceof 操作符:
class A {
}
public class instance1 {
public static void main(String args[]) {
try {
Class cls = Class.forName("A");
boolean b1 = cls.isInstance(new Integer(37));
System.out.println(b1);
boolean b2 = cls.isInstance(new A());
System.out.println(b2);
} catch (Throwable e) {
System.err.println(e);
}
}
}
在这个例子中创建了一个 A 类的 Class 对象,然后检查一些对象是否是 A 的实例。Integer(37) 不是,但 new A() 是。
3.找出类的方法
找出一个类中定义了些什么方法,这是一个非常有价值也非常基础的 reflection 用法。下面的代码就实现了这一用法:
import java.lang.reflect.*;
public class method1 {
private int f1(Object p, int x) throws NullPointerException {
if (p == null)
throw new NullPointerException();
return x;
}
public static void main(String args[]) {
try {
Class cls = Class.forName("method1");
Method methlist[] = cls.getDeclaredMethods();
for (int i = 0; i < methlist.length; i++) {
Method m = methlist[i];
System.out.println("name = " + m.getName());
System.out.println("decl class = " + m.getDeclaringClass());
Class pvec[] = m.getParameterTypes();
for (int j = 0; j < pvec.length; j++)
System.out.println("param #" + j + " " + pvec[j]);
Class evec[] = m.getExceptionTypes();
for (int j = 0; j < evec.length; j++)
System.out.println("exc #" + j + " " + evec[j]);
System.out.println("return type = " + m.getReturnType());
System.out.println("-----");
}
} catch (Throwable e) {
System.err.println(e);
}
}
}
这个程序首先取得 method1 类的描述,然后调用 getDeclaredMethods 来获取一系列的 Method 对象,它们分别描述了定义在类中的每一个方法,包括 public 方法、protected 方法、package 方法和 private 方法等。如果你在程序中使用 getMethods 来代替 getDeclaredMethods,你还能获得继承来的各个方法的信息。
取得了 Method 对象列表之后,要显示这些方法的参数类型、异常类型和返回值类型等就不难了。这些类型是基本类型还是类类型,都可以由描述类的对象按顺序给出。
输出的结果如下:
name = f1
decl class = class method1
param #0 class java.lang.Object
param #1 int
exc #0 class java.lang.NullPointerException
return type = int
-----
name = main
decl class = class method1
param #0 class [Ljava.lang.String;
return type = void
-----
4.获取构造器信息
获取类构造器的用法与上述获取方法的用法类似,如:
import java.lang.reflect.*;
public class constructor1 {
public constructor1() {
}
protected constructor1(int i, double d) {
}
public static void main(String args[]) {
try {
Class cls = Class.forName("constructor1");
Constructor ctorlist[] = cls.getDeclaredConstructors();
for (int i = 0; i < ctorlist.length; i++) {
Constructor ct = ctorlist[i];
System.out.println("name = " + ct.getName());
System.out.println("decl class = " + ct.getDeclaringClass());
Class pvec[] = ct.getParameterTypes();
for (int j = 0; j < pvec.length; j++)
System.out.println("param #" + j + " " + pvec[j]);
Class evec[] = ct.getExceptionTypes();
for (int j = 0; j < evec.length; j++)
System.out.println("exc #" + j + " " + evec[j]);
System.out.println("-----");
}
} catch (Throwable e) {
System.err.println(e);
}
}
}
这个例子中没能获得返回类型的相关信息,那是因为构造器没有返回类型。
这个程序运行的结果是:
name = constructor1
decl class = class constructor1
-----
name = constructor1
decl class = class constructor1
param #0 int
param #1 double
-----
5.获取类的字段(域)
找出一个类中定义了哪些数据字段也是可能的,下面的代码就在干这个事情:
import java.lang.reflect.*;
public class field1 {
private double d;
public static final int i = 37;
String s = "testing";
public static void main(String args[]) {
try {
Class cls = Class.forName("field1");
Field fieldlist[] = cls.getDeclaredFields();
for (int i = 0; i < fieldlist.length; i++) {
Field fld = fieldlist[i];
System.out.println("name = " + fld.getName());
System.out.println("decl class = " + fld.getDeclaringClass());
System.out.println("type = " + fld.getType());
int mod = fld.getModifiers();
System.out.println("modifiers = " + Modifier.toString(mod));
System.out.println("-----");
}
} catch (Throwable e) {
System.err.println(e);
}
}
}
这个例子和前面那个例子非常相似。例中使用了一个新东西 Modifier,它也是一个 reflection 类,用来描述字段成员的修饰语,如“private int”。这些修饰语自身由整数描述,而且使用 Modifier.toString 来返回以“官方”顺序排列的字符串描述 (如“static”在“final”之前)。这个程序的输出是:
name = d
decl class = class field1
type = double
modifiers = private
-----
name = i
decl class = class field1
type = int
modifiers = public static final
-----
name = s
decl class = class field1
type = class java.lang.String
modifiers =
-----
和获取方法的情况一下,获取字段的时候也可以只取得在当前类中申明了的字段信息 (getDeclaredFields),或者也可以取得父类中定义的字段 (getFields) 。
6.根据方法的名称来执行方法
文本到这里,所举的例子无一例外都与如何获取类的信息有关。我们也可以用 reflection 来做一些其它的事情,比如执行一个指定了名称的方法。下面的示例演示了这一操作:
import java.lang.reflect.*;
public class method2 {
public int add(int a, int b) {
return a + b;
}
public static void main(String args[]) {
try {
Class cls = Class.forName("method2");
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
Method meth = cls.getMethod("add", partypes);
method2 methobj = new method2();
Object arglist[] = new Object[2];
arglist[0] = new Integer(37);
arglist[1] = new Integer(47);
Object retobj = meth.invoke(methobj, arglist);
Integer retval = (Integer) retobj;
System.out.println(retval.intvalue());
} catch (Throwable e) {
System.err.println(e);
}
}
}
假如一个程序在执行的某处的时候才知道需要执行某个方法,这个方法的名称是在程序的运行过程中指定的 (例如,JavaBean 开发环境中就会做这样的事),那么上面的程序演示了如何做到。
上例中,getMethod 用于查找一个具有两个整型参数且名为 add 的方法。找到该方法并创建了相应的 Method 对象之后,在正确的对象实例中执行它。执行该方法的时候,需要提供一个参数列表,这在上例中是分别包装了整数 37 和 47 的两个 Integer 对象。执行方法的返回的同样是一个 Integer 对象,它封装了返回值 84。
7.创建新的对象
对于构造器,则不能像执行方法那样进行,因为执行一个构造器就意味着创建了一个新的对象 (准确的说,创建一个对象的过程包括分配内存和构造对象)。所以,与上例最相似的例子如下:
import java.lang.reflect.*;
public class constructor2 {
public constructor2() {
}
public constructor2(int a, int b) {
System.out.println("a = " + a + " b = " + b);
}
public static void main(String args[]) {
try {
Class cls = Class.forName("constructor2");
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
Constructor ct = cls.getConstructor(partypes);
Object arglist[] = new Object[2];
arglist[0] = new Integer(37);
arglist[1] = new Integer(47);
Object retobj = ct.newInstance(arglist);
} catch (Throwable e) {
System.err.println(e);
}
}
}
根据指定的参数类型找到相应的构造函数并执行它,以创建一个新的对象实例。使用这种方法可以在程序运行时动态地创建对象,而不是在编译的时候创建对象,这一点非常有价值。
8.改变字段(域)的值
reflection 的还有一个用处就是改变对象数据字段的值。reflection 可以从正在运行的程序中根据名称找到对象的字段并改变它,下面的例子可以说明这一点:
import java.lang.reflect.*;
public class field2 {
public double d;
public static void main(String args[]) {
try {
Class cls = Class.forName("field2");
Field fld = cls.getField("d");
field2 f2obj = new field2();
System.out.println("d = " + f2obj.d);
fld.setDouble(f2obj, 12.34);
System.out.println("d = " + f2obj.d);
} catch (Throwable e) {
System.err.println(e);
}
}
}
这个例子中,字段 d 的值被变为了 12.34。
9.使用数组
本文介绍的 reflection 的最后一种用法是创建的操作数组。数组在 Java 语言中是一种特殊的类类型,一个数组的引用可以赋给 Object 引用。观察下面的例子看看数组是怎么工作的:
import java.lang.reflect.*;
public class array1 {
public static void main(String args[]) {
try {
Class cls = Class.forName("java.lang.String");
Object arr = Array.newInstance(cls, 10);
Array.set(arr, 5, "this is a test");
String s = (String) Array.get(arr, 5);
System.out.println(s);
} catch (Throwable e) {
System.err.println(e);
}
}
}
例中创建了 10 个单位长度的 String 数组,为第 5 个位置的字符串赋了值,最后将这个字符串从数组中取得并打印了出来。
下面这段代码提供了一个更复杂的例子:
import java.lang.reflect.*;
public class array2 {
public static void main(String args[]) {
int dims[] = new int[]{5, 10, 15};
Object arr = Array.newInstance(Integer.TYPE, dims);
Object arrobj = Array.get(arr, 3);
Class cls = arrobj.getClass().getComponentType();
System.out.println(cls);
arrobj = Array.get(arrobj, 5);
Array.setInt(arrobj, 10, 37);
int arrcast[][][] = (int[][][]) arr;
System.out.println(arrcast[3][5][10]);
}
}
例中创建了一个 5 x 10 x 15 的整型数组,并为处于 [3][5][10] 的元素赋了值为 37。注意,多维数组实际上就是数组的数组,例如,第一个 Array.get 之后,arrobj 是一个 10 x 15 的数组。进而取得其中的一个元素,即长度为 15 的数组,并使用 Array.setInt 为它的第 10 个元素赋值。
注意创建数组时的类型是动态的,在编译时并不知道其类型。
--sunfruit
提供了获得汉字的拼音首字母的方法
JDK版本 无版本限制
功能 实现了获得一个汉字的拼音首字母功能,为汉字排序提供了方便
欢迎大家提意见,交流
代码如下:
/**
* Title:获得汉字的拼音首字母
* Description: GB 2312-80 把收录的汉字分成两级。第一级汉字是常用汉字,计 3755 个,
* 置于 16~55 区,按汉语拼音字母/笔形顺序排列;第二级汉字是次常用汉字,
* 计 3008 个,置于 56~87 区,按部首/笔画顺序排列,所以本程序只能查到
* 对一级汉字的声母。同时对符合声母(zh,ch,sh)只能取首字母(z,c,s)
* Copyright: Copyright (c) 2004
* Company:
* @author not attributable
* @version 1.0
*/
public class GetFirstLetter {
// 国标码和区位码转换常量
private static final int GB_SP_DIFF = 160;
//存放国标一级汉字不同读音的起始区位码
private static final int[] secPosvalueList = {
1601, 1637, 1833, 2078, 2274, 2302, 2433, 2594, 2787,
3106, 3212, 3472, 3635, 3722, 3730, 3858, 4027, 4086,
4390, 4558, 4684, 4925, 5249, 5600};
//存放国标一级汉字不同读音的起始区位码对应读音
private static final char[] firstLetter = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
't', 'w', 'x', 'y', 'z'};
//获取一个字符串的拼音码
public static String getFirstLetter(String oriStr) {
String str = oriStr.toLowerCase();
StringBuffer buffer = new StringBuffer();
char ch;
char[] temp;
for (int i = 0; i < str.length(); i++) { //依次处理str中每个字符
ch = str.charAt(i);
temp = new char[] {
ch};
byte[] uniCode = new String(temp).getBytes();
if (uniCode[0] < 128 && uniCode[0] > 0) { // 非汉字
buffer.append(temp);
}
else {
buffer.append(convert(uniCode));
}
}
return buffer.toString();
}
/** 获取一个汉字的拼音首字母。
* GB码两个字节分别减去160,转换成10进制码组合就可以得到区位码
* 例如汉字"你"的GB码是0xC4/0xE3,分别减去0xA0(160)就是0x24/0x43
* 0x24转成10进制就是36,0x43是67,那么它的区位码就是3667,在对照表中读音为‘n'
*/
private static char convert(byte[] bytes) {
char result = '-';
int secPosvalue = 0;
int i;
for (i = 0; i < bytes.length; i++) {
bytes[i] -= GB_SP_DIFF;
}
secPosvalue = bytes[0] * 100 + bytes[1];
for (i = 0; i < 23; i++) {
if (secPosvalue >= secPosvalueList[i] &&
secPosvalue < secPosvalueList[i + 1]) {
result = firstLetter[i];
break;
}
}
return result;
}
}
--sunfruit
在面试的时候会问及如何不用第三个变量来交换a,b的值,下面给出答案
a=a^b;
b=b^a;
a=a^b;
使用了位操作,至于为什么可以,列举下面的示例你就会明白
1^1=0;
0^0=0;
1^0=1;
0^1=1;
是不是明白了,呵呵,要是面试的时候再有人问你你就可以从容的回答了,而且不怕他问为什么
--sunfruit
一段令人深有感触地话
一个老工程师的话
诸位,咱当电子工程师也是十余年了,不算有出息,环顾四周,也没有看见几个有出息的!回顾工程师生涯,感慨万千,愿意讲几句掏心窝子的话,也算给咱们师弟师妹们提个醒,希望他们比咱们强!
[1]好好规划自己的路,不要跟着感觉走!根据个人的理想决策安排,绝大部分人并不指望成为什么院士或教授,而是希望活得滋润一些,爽一些。那么,就需要慎重安排自己的轨迹。从哪个行业入手,逐渐对该行业深入了解,不要频繁跳槽,特别是不要为了一点工资而转移阵地,从长远看,这点钱根本不算什么,当你对一个行业有那么几年的体会,以后钱根本不是问题。频繁地动荡不是上策,最后你对哪个行业都没有摸透,永远是新手!
[2]可以做技术,切不可沉湎于技术。千万不可一门心思钻研技术!给自己很大压力,如果你的心思全部放在这上面,那么注定你将成为孔乙己一类的人物!适可而止为之,因为技术只不过是你今后前途的支柱之一,而且还不是最大的支柱
[3]不要去做技术高手,只去做综合素质高手!在企业里混,我们时常瞧不起某人,说他“什么都不懂,凭啥拿那么多钱,凭啥升官!”这是普遍的典型的工程师的迂腐之言。8051很牛吗?人家能上去必然有他的本事,而且是你没有的本事。你想想,老板搞经营那么多年,难道见识不如你这个新兵?人家或许善于管理,善于领会老板意图,善于部门协调等等。因此务必培养自己多方面的能力,包括管理,亲和力,察言观色能力,攻关能力等,要成为综合素质的高手,则前途无量,否则只能躲在角落看示波器!技术以外的技能才是更重要的本事!!从古到今,美国日本,一律如此!
[4]多交社会三教九流的朋友!不要只和工程师交往,认为有共同语言,其实更重要的是和其他类人物交往,如果你希望有朝一日当老板或高层管理,那么你整日面对的就是这些人。了解他们的经历,思维习惯,爱好,学习他们处理问题的模式,了解社会各个角落的现象和问题,这是以后发展的巨大的本钱
[6]抓住时机向技术管理或市场销售方面的转变!要想有前途就不能一直搞开发,适当时候要转变为管理或销售,前途会更大,以前搞技术也没有白搞,以后还用得着。搞管理可以培养自己的领导能力,搞销售可以培养自己的市场概念和思维,同时为自己以后发展积累庞大的人脉!应该说这才是前途的真正支柱!!!
[7]逐渐克服自己的心里弱点和性格缺陷!多疑,敏感,天真(贬义,并不可爱),犹豫不决,胆怯,多虑,脸皮太薄,心不够黑,教条式思维。。。这些工程师普遍存在的性格弱点必须改变!很难吗?只在床上想一想当然不可能,去帮朋友守一个月地摊,包准有效果,去实践,而不要只想!不克服这些缺点,一切不可能,甚至连项目经理都当不好--尽管你可能技术不错!
[8]工作的同时要为以后做准备!建立自己的工作环境!及早为自己配置一个工作环境,装备电脑,示波器(可以买个二手的),仿真器,编程器等,业余可以接点活,一方面接触市场,培养市场感觉,同时也积累资金,更重要的是准备自己的产品,咱搞技术的没有钱,只有技术,技术的代表不是学历和证书,而是产品,拿出象样的产品,就可技术转让或与人合作搞企业!先把东西准备好,等待机会,否则,有了机会也抓不住!
[9]要学会善于推销自己!不仅要能干,还要能说,能写,善于利用一切机会推销自己,树立自己的品牌形象,很必要!要创造条件让别人了解自己,不然老板怎么知道你能干?外面的投资人怎么相信你?提早把自己推销出去,机会自然会来找你!搞个个人主页是个好注意!!特别是培养自己在行业的名气,有了名气,高薪机会自不在话下,更重要的是有合作的机会...
[10]该出手时便出手!永远不可能有100%把握!!!条件差不多就要大胆去干,去闯出自己的事业,不要犹豫,不要彷徨,干了不一定成功,但至少为下一次冲击积累了经验,不干永远没出息,而且要干成必然要经历失败。不经历风雨,怎么见彩虹,没有人能随随便便成功!
--sunfruit
获得系统提供的所有字体
GraphicsEnvironment eq = GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] fontNames = eq.getAvailableFontFamilyNames();
说明:如果在linux下需要安装界面相关的包,具体的那个包记不清了,如果不安装可能会无法获得字体列表.
当时的版本是red hat 7.0,至于现在的版本默认安装是否包括需要的包就不知道了
--sunfruit
成长中的应该知道的。。。追求忘我
不要把自己当做鼠,否则肯定被猫吃。
1858年,瑞典的一个富豪人家生下了一个女儿。然而不久,孩子染患了一种无法解释的瘫痪症,丧失了走路的能力。
一次,女孩和家人一起乘船旅行。船长的太太给孩子讲船长有一只天堂鸟,她被这只鸟的描述迷住了,极想亲自看一看。于是保姆把孩子留在甲板上,自己去找船长。孩子耐不住性子等待,她要求船上的服务生立即带她去看天堂鸟。那服务生并不知道她的腿不能走路,而只顾带着她一道去看那只美丽的小鸟。奇迹发生了,孩子因为过度地渴望,竟忘我地拉住服务生的手,慢慢地走了起来。从此,孩子的病便痊愈了。女孩子长大后,又忘我地投入到文学创作中,最后成为第一位荣获诺贝尔文学奖的女性,也就是茜尔玛·拉格萝芙。
温馨提示:忘我是走向成功的一条捷径,只有在这种环境中,人才会超越自身的束缚,释放出最大的能量。
--sunfruit
成长中的应该知道的。。。心中的顽石
阻碍我们去发现、去创造的,仅仅是我们心理上的障碍和思想中的顽石。
从前有一户人家的菜园摆着一颗大石头,宽度大约有四十公分,高度有十公分。到菜园的人,不小心就会踢到那一颗大石头,不是跌倒就是擦伤。
儿子问:“爸爸,那颗讨厌的石头,为什么不把它挖走?”
爸爸这么回答:“你说那颗石头喔?从你爷爷时代,就一直放到现在了,它的体积那么大,不知道要挖到到什么时候,没事无聊挖石头,不如走路小心一点,还可以训练你的反应能力。”
过了几年,这颗大石头留到下一代,当时的儿子娶了媳妇,当了爸爸。
有一天媳妇气愤地说:“爸爸,菜园那颗大石头,我越看越不顺眼,改天请人搬走好了。”
爸爸回答说:“算了吧!那颗大石头很重的,可以搬走的话在我小时候就搬走了,哪会让它留到现在啊?”
媳妇心底非常不是滋味,那颗大石头不知道让她跌倒多少次了。
有一天早上,媳妇带着锄头和一桶水,将整桶水倒在大石头的四周。
十几分钟以后,媳妇用锄头把大石头四周的泥土搅松。
媳妇早有心理准备,可能要挖一天吧,谁都没想到几分钟就把石头挖起来,看看大小,这颗石头没有想像的那么大,都是被那个巨大的外表蒙骗了。
温馨提示:你抱着下坡的想法爬山,便无从爬上山去。如果你的世界沉闷而无望,那是因为你自己沉闷无望。改变你的世界,必先改变你自己的心态。
--sunfruit
为JAVA的图形界面的字体设置统一的格式
执行下列代码
//设置系统的默认字体
private static void setUIFont() {
Font myFont = new Font("宋体", 9, 12);
javax.swing.plaf.FontUIResource fontRes = new javax.swing.plaf.
FontUIResource(myFont);
java.util.Enumeration keys = UIManager.getDefaults().keys();
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
Object value = UIManager.get(key);
if (value instanceof javax.swing.plaf.FontUIResource) {
UIManager.put(key, fontRes);
}
}
}
--sunfruit
成长中的应该知道的。。。成功并不像你想像的那么难
并不是因为事情难我们不敢做,而是因为我们不敢做事情才难的。
1965年,一位韩国学生到剑桥大学主修心理学。在喝下午茶的时候,他常到学校的咖啡厅或茶座听一些成功人士聊天。这些成功人士包括诺贝尔奖获得者,某一些领域的学术权威和一些创造了经济神话的人,这些人幽默风趣,举重若轻,把自己的成功都看得非常自然和顺理成章。时间长了,他发现,在国内时,他被一些成功人士欺骗了。那些人为了让正在创业的人知难而退,普遍把自己的创业艰辛夸大了,也就是说,他们在用自己的成功经历吓唬那些还没有取得成功的人。
作为心理系的学生,他认为很有必要对韩国成功人士的心态加以研究。1970年,他把《成功并不像你想像的那么难》作为毕业论文,提交给现代经济心理学的创始人威尔;布雷登教授。布雷登教授读后,大为惊喜,他认为这是个新发现,这种现象虽然在东方甚至在世界各地普遍存在,但此前还没有一个人大胆地提出来并加以研究。惊喜之余,他写信给他的剑桥校友--当时正坐在韩国政坛第一把交椅上的人--朴正熙。他在信中说,“我不敢说这部著作对你有多大的帮助,但我敢肯定它比你的任何一个政令都能产生震动。”
后来这本书果然伴随着韩国的经济起飞了。这本书鼓舞了许多人,因为他们从一个新的角度告诉人们,成功与“劳其筋骨,饿其体肤”、“三更灯火五更鸡”、“头悬梁,锥刺股”没有必然的联系。只要你对某一事业感兴趣,长久地坚持下去就会成功,因为上帝赋予你的时间和智慧够你圆满做完一件事情。后来,这位青年也获得了成功,他成了韩国泛业汽车公司的总裁。
温馨提示:人世中的许多事,只要想做,都能做到,该克服的困难,也都能克服,用不着什么钢铁般的意志,更用不着什么技巧或谋略。只要一个人还在朴实而饶有兴趣地生活着,他终究会发现,造物主对世事的安排,都是水到渠成的。
--sunfruit
成长中的应该知道的。。。永远的坐票
生活真是有趣:如果你只接受最好的,你经常会得到最好的。
有一个人经常出差,经常买不到对号入坐的车票。可是无论长途短途,无论车上多挤,他总能找到座位。
他的办法其实很简单,就是耐心地一节车厢一节车厢找过去。这个办法听上去似乎并不高明,但却很管用。每次,他都做好了从第一节车厢走到最后一节车厢的准备,可是每次他都用不着走到最后就会发现空位。他说,这是因为像他这样锲而不舍找座位的乘客实在不多。经常是在他落座的车厢里尚余若干座位,而在其他车厢的过道和车厢接头处,居然人满为患。
他说,大多数乘客轻易就被一两节车厢拥挤的表面现象迷惑了,不大细想在数十次停靠之中,从火车十几个车门上上下下的流动中蕴藏着不少提供座位的机遇;即使想到了,他们也没有那一份寻找的耐心。眼前一方小小立足之地很容易让大多数人满足,为了一两个座位背负着行囊挤来挤去有些人也觉得不值。他们还担心万一找不到座位,回头连个好好站着的地方也没有了。与生活中一些安于现状不思进取害怕失败的人,永远只能滞留在没有成功的起点上一样,这些不愿主动找座位的乘客大多只能在上车时最初的落脚之处一直站到下车。
温馨提示:自信、执着、富有远见、勤于实践,会让你握有一张人生之旅永远的坐票。
--sunfruit
成长中的应该知道的。。。飞翔的蜘蛛
信念是一种无坚不催的力量,当你坚信自己能成功时,你必能成功。
一天,我发现,一只黑蜘蛛在后院的两檐之间结了一张很大的网。难道蜘蛛会飞?要不,从这个檐头到那个檐头,中间有一丈余宽,第一根线是怎么拉过去的?后来,我发现蜘蛛走了许多弯路--从一个檐头起,打结,顺墙而下,一步一步向前爬,小心翼翼,翘起尾部,不让丝沾到地面的沙石或别的物体上,走过空地,再爬上对面的檐头,高度差不多了,再把丝收紧,以后也是如此。
温馨提示:蜘蛛不会飞翔,但它能够把网凌结在半空中。它是勤奋、敏感、沉默而坚韧的昆虫,它的网制得精巧而规矩,八卦形地张开,仿佛得到神助。这样的成绩,使人不由想起那些沉默寡言的人和一些深藏不露的智者。于是,我记住了蜘蛛不会飞翔,但它照样把网结在空中。奇迹是执着者造成的。
--sunfruit
成长中的应该知道的。。。阴影是条纸龙
人生中,经常有无数来自外部的打击,但这些打击究竟会对你产生怎样的影响,最终决定权在你手中。
祖父用纸给我做过一条长龙。长龙腹腔的空隙仅仅只能容纳几只蝗虫,投放进去,它们都在里面死了,无一幸免!祖父说:“蝗虫性子太躁,除了挣扎,它们没想过用嘴巴去咬破长龙,也不知道一直向前可以从另一端爬出来。因而,尽管它有铁钳般的嘴壳和锯齿一般的大腿,也无济于事。
”当祖父把几只同样大小的青虫从龙头放进去,然后关上龙头,奇迹出现了:仅仅几分钟,小青虫们就一一地从龙尾爬了出来。
温馨提示:命运一直藏匿在我们的思想里。许多人走不出人生各个不同阶段或大或小的阴影,并非因为他们天生的个人条件比别人要差多远,而是因为他们没有思想要将阴影纸龙咬破,也没有耐心慢慢地找准一个方向,一步步地向前,直到眼前出现新的洞天。
--sunfruit
分析了使用正则表达式和使用isNaN函数验证输入的内容是否全部为数字的区别
1、使用正则表达式
var patrn=/^[0-9]{1,20}$/;
if(xxx!=null && !patrn.exec(xxx))
{
alert("请保证输入的全是数字");
}
这个表达式要求输入的字符每一个都必须是数字,123是正确的而1.23不正确
2、使用isNaN
if (isNaN(xxx))
{
alert('请输入数字!');
}
这个表达式认为123,1.23,-1.23甚至是-.23都是正确的
可以看出,使用正则表达式更适合验证输入内容必须为纯数字的情况,而使用isNaN对输入的内容要求更为宽松一些
--sunfruit
成长中的应该知道的。。。昂起头来真美
别看它是一条黑母牛,牛奶一样是白的。
珍妮是个总爱低着头的小女孩,她一直觉得自己长得不够漂亮。有一天,她到饰物店去买了只绿色蝴蝶结,店主不断赞美她戴上蝴蝶结挺漂亮,珍妮虽不信,但是挺高兴,不由昂起了头,急于让大家看看,出门与人撞了一下都没在意。
珍妮走进教室,迎面碰上了她的老师,“珍妮,你昂起头来真美!”老师爱抚地拍拍她的肩说。
那一天,她得到了许多人的赞美。她想一定是蝴蝶结的功劳,可往镜前一照,头上根本就没有蝴蝶结,一定是出饰物店时与人一碰弄丢了。
自信原本就是一种美丽,而很多人却因为太在意外表而失去很多快乐。
温馨提示:无论是贫穷还是富有,无论是貌若天仙,还是相貌平平,只要你昂起头来,快乐会使你变得可爱——人人都喜欢的那种可爱。
--sunfruit
用JAVA编写的邮件客户端程序,使用JAVAMAIL技术
JDK版本
1.4.x
功能简介:
支持邮件的本地阅览和存储,数据库格式为access
支持多个邮件账户,并具备单个账户邮件阅览和总帐户的邮件阅览功能
账户树目录可以按照账户的添加/删除自动调整
支持多个附件
支持设定是否删除(保留)服务器的改邮件的副本
邮件列表面板的表头单击支持排序(附件、发件人、主题三项可以排序)
可以回复、转发邮件
可以将邮件转移邮箱
扩展功能:
添加中。。。。
提供更强的扩展功能,如数据库支持更多格式(Oracle,MySql...)
提供"通讯簿"功能
需要改进的地方:
有些网站有乱吗问题,需要改进
欢迎大家提意见,交流
http://blog.blogchina.com/upload/2005-03-04/20050304112327596459.rar
--sunfruit
成长中的应该知道的。。。为生命画一片树叶
只要心存相信,总有奇迹发生,希望虽然渺茫,但它永存人世。
美国作家欧;亨利在他的小说《最后一片叶子》里讲了个故事:病房里,一个生命垂危的病人从房间里看见窗外的一棵树,在秋风中一片片地掉落下来。病人望着眼前的萧萧落叶,身体也随之每况愈下,一天不如一天。她说:“当树叶全部掉光时,我也就要死了。”一位老画家得知后,用彩笔画了一片叶脉青翠的树叶挂在树枝上。
最后一片叶子始终没掉下来。只因为生命中的这片绿,病人竟奇迹般地活了下来。
温馨提示:人生可以没有很多东西,却唯独不能没有希望。希望是人类生活的一项重要的价值。有希望之处,生命就生生不息!
--sunfruit
用VB做的文本编辑器,下载程序里有源代码
很早前做的程序,拿出来大家批评一下,呵呵
VB版本
VB6.0
功能
VB6.0做的文本编辑器,文本的样式颜色设置,查找功能等
实现了文件的拖拽,其中按住"Ctrl"拖拽文件是添加文件内容
附件中提供源代码
欢迎大家提意见,交流
下载地址
http://blog.blogchina.com/upload/2005-03-04/20050304004430248671.rar
--sunfruit
用JAVA编写的绘图程序,使用JAVA 2D API
提供源代码下载
JDK版本
1.4.x
功能简介:
支持存储,格式为XML,并支持颜色信息的存取,有导出为图片功能
支持多个组件选择:CTRL选择(或取消)和拉框选择或是复合选择(或取消)
支持多个组件拖动:选择多个组件可以同时拖动
支持全选功能和复制功能和删除功能,并提供快捷方式
单个组件支持右键菜单,其中包括设置组件名称功能
在担任dramflow.xml中设置相关属性
连线规则:
直线的起点和终点在两个不同的组件上时才可以连接
选中的直线也是可以删除的@
扩展功能:
添加中。。。。
提供更强的扩展功能,可以在一个组件单元上配置更详细的信息
提供"Undo","Redo"功能
在配置环节中的日期选择组件使用了sunking的开源组件,在此表示感谢
欢迎大家提意见,交流
演示
http://www.fruitres.cn/useruploadfile/4/641051137_jnlp.jnlp
DEMO
http://www.fruitres.cn/useruploadfile/4/641051137_demo.rar
--sunfruit
成长中的应该知道的。。。断箭
不相信自己的意志,永远也做不成将军。
春秋战国时代,一位父亲和他的儿子出征打战。父亲已做了将军,儿子还只是马前卒。又一阵号角吹响,战鼓雷鸣了,父亲庄严地托起一个箭囊,其中插着一只箭。父亲郑重对儿子说:“这是家袭宝箭,配带身边,力量无穷,但千万不可抽出来。”
那是一个极其精美的箭囊,厚牛皮打制,镶着幽幽泛光的铜边儿,再看露出的箭尾。一眼便能认定用上等的孔雀羽毛制作。儿子喜上眉梢,贪婪地推想箭杆、箭头的模样,耳旁仿佛嗖嗖地箭声掠过,敌方的主帅应声折马而毙.
果然,配带宝箭的儿子英勇非凡,所向披靡。当鸣金收兵的号角吹响时,儿子再也禁不住得胜的豪气,完全背弃了父亲的叮嘱,强烈的欲望驱赶着他呼一声就拔出宝箭,试图看个究竟。骤然间他惊呆了。
一只断箭,箭囊里装着一只折断的箭。
我一直刳着只断箭打仗呢!儿子吓出了一身冷汗,仿佛顷刻间失去支柱的房子,轰然意志坍塌了。
结果不言自明,儿子惨死于乱军之中。
拂开蒙蒙的硝烟,父亲拣起那柄断箭,沉重地啐一口道:“不相信自己的意志,永远也做不成将军。”
把胜败寄托在一只宝箭上,多么愚蠢,而当一个人把生命的核心与把柄交给别人,又多么危险!比如把希望寄托在儿女身上;把幸福寄托在丈夫身上;把生活保障寄托在单位身上……
温馨提示:自己才是一只箭,若要它坚韧,若要它锋利,若要它百步穿杨,百发百中,磨砺它,拯救它的都只能是自己。
--sunfruit
近来webservice很流行,所以自己也做了一个webservice的server和client调试成功,其中也遇到了一些问题为了让大家在调试的时候少走冤路,把写好的server和client的源代码发布出来给大家做一个参考
由于用JB发布一个webservice的server很容易,所以就不具体说明发布过程了,而且网上也有很多这样的例子,或者自己摸索着也能发布成功
在源代码中写了必要的注释便于大家解读
server的源代码
package com.service;
public class HelloWorld{
public String sayHello(){
return "Hello world!";
}
public String echo(String u){
return "Hello " + u;
}
public void setName(String name){
System.out.println(name);
}
}
很简单吧,本来就不复杂的,主要是对于接口的设计和接口的实现设计牵扯的业务啦、扩展啦等等比较多,那些才算复杂,就webservice的技术本身来说不复杂的
下面来看click的源代码
package clicktest;
import java.util.*;
import java.net.*;
import org.apache.soap.*;
import org.apache.soap.rpc.*;
public class ClickTest {
public static void main(String[] args) throws Exception {
//改成你的地址
URL url = new URL ("http://localhost:8080/WebModule2/services/HelloWorld");
// 创建调用
Call call = new Call ();
//设置编码方式,不用修改
call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);
Response resp = null;
//调用方法名,因为没有返回值所以只需要这一行
call.setMethodName("setName");
//设置该方法的参数,如果没有参数,则不需要设置
Vector params = new Vector();
params.addElement(new Parameter("test", String.class, "my name setName", null));
call.setParams(params);
call.invoke(url, "");
//调用方法名
call.setMethodName("echo");
params.clear();
params.addElement(new Parameter("test", String.class, "my name echo", null));
call.setParams(params);
//返回值对象
try {
resp = call.invoke(url, "");
}
catch (SOAPException e) {
e.printStackTrace();
}
// 检查返回值
if (resp != null && !resp.generatedFault()) {
Parameter ret = resp.getReturnValue();
Object value = ret.getValue();
System.out.println("Answer--> " + value);
}
else {
Fault fault = resp.getFault();
System.err.println("Generated fault echo: ");
System.out.println(" Fault Code = " + fault.getFaultCode());
System.out.println(" Fault String = " + fault.getFaultString());
}
//调用方法名
call.setMethodName("sayHello");
//由于没有参数所以将Params置为null
call.setParams(null);
//返回值对象
resp = null;
try {
resp = call.invoke(url, "");
}
catch (SOAPException e) {
e.printStackTrace();
}
// 检查返回值
if (resp != null && !resp.generatedFault()) {
Parameter ret = resp.getReturnValue();
Object value = ret.getValue();
System.out.println("Answer--> " + value);
}
else {
Fault fault = resp.getFault();
System.err.println("Generated fault sayHello: ");
System.out.println(" Fault Code = " + fault.getFaultCode());
System.out.println(" Fault String = " + fault.getFaultString());
}
}
}
感觉有些多是吧,其实也不多的,主要是模拟了接口的三个方法的调用,如果是一个没有返回值得方法调用也就是几行就可以了,是不是感觉清楚了
这个server和client在JBX中测试通过
--sunfruit
成长中的应该知道的。。。生命的价值
不要让昨日的沮丧令明天的梦想黯然失色!
在一次讨论会上,一位著名的演说家没讲一句开场白,手里却高举着一张20美元的钞票。
面对会议室里的200个人,他问:“谁要这20美元?”一只只手举了起来。他接着说:“我打算把这20美元送给你们中的一位,但在这之前,请准许我做一件事。”他说着将钞票揉成一团,然后问:“谁还要?”仍有人举起手来。
他又说:“那么,假如我这样做又会怎么样呢?”他把钞票扔到地上,又踏上一只脚,并且用脚碾它。尔后他拾起钞票,钞票已变得又脏又皱。
“现在谁还要?”还是有人举起手来。
“朋友们,你们已经上了一堂很有意义的课。无论我如何对待那张钞票,你们还是想要它,因为它并没贬值,它依旧值20美元。人生路上,我们会无数次被自己的决定或碰到的逆境击倒、欺凌甚至碾得粉身碎骨。我们觉得自己似乎一文不值。但无论发生什么,或将要发生什么,在上帝
的眼中,你们永远不会丧失价值。在他看来,肮脏或洁净,衣着齐整或不齐整,你们依然是无价之宝。”
温馨提示:生命的价值不依赖我们的所作所为,也不仰仗我们结交的人物,而是取决于我们本身!我们是独特的——永远不要忘记这一点!
--sunfruit
java中的基本的数据类型如int,float,long,String[这个是一个特殊的类,有基本的数据类型的特性]等,在方法的参数传递的时候不存在引用传递,只有值传递方式,下面有一个实例
public class Test
{
public Test()
{
int j=9;
setInt(j);
System.out.println(j);
j=setInt(j);
System.out.println(j);
String str2="abc";
setString(str2);
System.out.println(str2);
str2=setString(str2);
System.out.println(str2);
StringBuffer buff=new StringBuffer();
buff.append("abcbuff");
setStringBuffer(buff);
System.out.println(buff.toString());
}
public static void main(String[] args)
{
new Test();
}
private int setInt(int i)
{
i+=2;
return i;
}
private String setString(String str1)
{
str1+=" test";
return str1;
}
private StringBuffer setStringBuffer(StringBuffer buff1)
{
buff1.append(" test");
return buff1;
}
}
运行结果
9
11
abc
abc test
abcbuff test
可以看到基本数据类型的传递方式是值传递
--sunfruit
用java实现了整形数字的动态数组
JDK版本
1.3.1
功能
实现了添加整数到动态数组中,JDK(1.5以下)不提供整形类型的集合,比如ArrayList这样的集合不允许添加整数,
但是在编程过程中会遇到需要整形的动态数组的情况,所以这个类实现了这样的功能
欢迎大家提意见,交流
代码如下:
/**
* Title: 整形动态数组
* Description: 实现了整形数字的动态添加
* Copyright: Copyright (c) 2003
* Company: LingTu
* @author cuijiang
* @version 2.0
*/
public class DynArrayInt {
/**
* 原始数组
*/
private int[] data_All;
/**
* 计数器(数组长度)
*/
private int size_count;
/**
* 构造器,初始长度默认为10
*/
public DynArrayInt() {
this(10);
}
/**
* 构造器,设置数组的初始长度
*
* @param iniSize int 数组的初始长度
*/
public DynArrayInt(int iniSize) {
data_All = new int[iniSize];
}
/**
* 添加数据,调用checkAdd(int i)
* @param i int 一个整形数字
*/
public void addInt(int i) {
//判断是否增长
this.checkAdd(size_count + 1);
//赋值
data_All[size_count++] = i;
//添加时数组长度加一
}
/**
* 添加数字,判断是否增长
* @param i int 一个整形数字
*/
private void checkAdd(int i) {
//获得原来的大小
int star = data_All.length;
//判断是否增长
if (i > star) {
int starData[] = data_All;
//设定增长大小
int endall = star * 2;
data_All = new int[endall];
System.arraycopy(starData, 0, data_All, 0, size_count);
}
}
/**
* 获取数据
* @param i int 索引号
* @return int
*/
public int getInt(int i) {
if (i < 0 || i >= size_count) {
throw new IndexOutOfBoundsException("超出最大或最小索引值,无法取得数据");
} else {
return data_All[i];
}
}
/**
* 获取数据转换成字符串模式
* @param i int 索引号
* @return String
*/
public String getIntToString(int i) {
if (i < 0 || i >= size_count) {
throw new IndexOutOfBoundsException("超出最大或最小索引值,无法取得数据");
} else {
return String.valueOf(data_All[i]);
}
}
/**
* 删除数据
* @param j int 一个要删除的整数
*/
public void remove(int j) {
for (int i = 0; i < size_count; i++) {
if (data_All[i] == j) {
System.arraycopy(data_All, i+1, data_All, i, size_count-i-1); // 复制数据
--size_count;
return;
}
}
}
/**
* 删除数据
* @param j int 一个要删除的索引
*/
public void removeIndex(int j) {
if (j < 0 || j >= size_count) {
throw new IndexOutOfBoundsException("超出最大或最小索引值,无法删除数据");
} else {
System.arraycopy(data_All, j + 1, data_All, j, size_count -j- 1); // 复制数据
--size_count;
return;
}
}
/**
* 获取大小
* @return int 获得数组长度
*/
public int getSize() {
return size_count;
}
/**
* 获取数组对象
* @return int[] 获得数组对象
*/
public int[] getAllInt() {
int[] starData = new int[size_count];
System.arraycopy(data_All, 0, starData, 0, size_count);
return starData;
}
/**
* 获得数组对象,String格式
* @return String[] 获得数组的对象
*/
public String[] getAllIntToString() {
int[] tempint = getAllInt();
String[] starData = new String[tempint.length];
for (int i = 0; i < starData.length; i++) {
starData[i] = String.valueOf(tempint[i]);
}
return starData;
}
/**
* 删除全部内容
*/
public void removeAll() {
data_All = new int[10];
size_count = 0;
}
}
--sunfruit
说了开源软件的一些想法
注:转贴
开源软件不要求代码有多么的漂亮,但是设计的架构必须要很精简,很清晰。这样别人才能在了解架构的前提下做出扩充。不要指望有人通过修改源代码来扩展功能,没有人会愿意去完全读懂一堆source,即使是大师级的source code。Apache的Avalon虽然功能很多,但是由于落后的架构,也不得不惨淡收场。
所以开源软件必然是个优秀的产物,就像是个受到各方严格监督的孩子,虽然现在还弱小,但将来不可限量啊!
不过光靠基金会的支持也还不足以让这个孩子茁壮成长,商业化才是最终的出路
--sunfruit
说明了原码、补码和反码的关系以及算法
原码、补码和反码 |
|
(1)原码表示法
原码表示法是机器数的一种简单的表示法。其符号位用0表示正号,用:表示负号,数值一般用二进制形式表示。设有一数为x,则原码表示可记作[x]原。
例如,X1= +1010110
X2= 一1001010
其原码记作:
[X1]原=[+1010110]原=01010110
[X2]原=[-1001010]原=11001010
原码表示数的范围与二进制位数有关。当用8位二进制来表示小数原码时,其表示范围:
最大值为0.1111111,其真值约为(0.99)10
最小值为1.1111111,其真值约为(一0.99)10
当用8位二进制来表示整数原码时,其表示范围:
最大值为01111111,其真值为(127)10
最小值为11111111,其真值为(-127)10
在原码表示法中,对0有两种表示形式:
[+0]原=00000000
[-0] 原=10000000
|
(2)补码表示法
机器数的补码可由原码得到。如果机器数是正数,则该机器数的补码与原码一样;如果机器数是负数,则该机器数的补码是对它的原码(除符号位外)各位取反,并在未位加1而得到的。设有一数X,则X的补码表示记作[X]补。
例如,[X1]=+1010110
[X2]= 一1001010
[X1]原=01010110
[X1]补=01010110
即 [X1]原=[X1]补=01010110
[X2] 原= 11001010
[X2] 补=10110101+1=10110110
补码表示数的范围与二进制位数有关。当采用8位二进制表示时,小数补码的表示范围:
最大为0.1111111,其真值为(0.99)10
最小为1.0000000,其真值为(一1)10
采用8位二进制表示时,整数补码的表示范围:
最大为01111111,其真值为(127)10
最小为10000000,其真值为(一128)10
在补码表示法中,0只有一种表示形式:
[+0]补=00000000
[+0]补=11111111+1=00000000(由于受设备字长的限制,最后的进位丢失)
所以有[+0]补=[+0]补=00000000
|
(3)反码表示法
机器数的反码可由原码得到。如果机器数是正数,则该机器数的反码与原码一样;如果机器数是负数,则该机器数的反码是对它的原码(符号位除外)各位取反而得到的。设有一数X,则X的反码表示记作[X]反。
例如:X1= +1010110
X2= 一1001010
[X1]原=01010110
[X1]反=[X1]原=01010110
[X2]原=11001010
[X2]反=10110101
反码通常作为求补过程的中间形式,即在一个负数的反码的未位上加1,就得到了该负数的补码。
例1. 已知[X]原=10011010,求[X]补。
分析如下:
由[X]原求[X]补的原则是:若机器数为正数,则[X]原=[X]补;若机器数为负数,则该机器数的补码可对它的原码(符号位除外)所有位求反,再在未位加1而得到。现给定的机器数为负数,故有[X]补=[X]原十1,即
[X]原=10011010
[X]反=11100101
十) 1
[X]补=11100110
例2. 已知[X]补=11100110,求[X]原。
分析如下:
对于机器数为正数,则[X]原=[X]补
对于机器数为负数,则有[X]原=[[X]补]补
现给定的为负数,故有:
[X]补=11100110
[[X]补]反=10011001
十) 1
[[X]补]补=10011010=[X]原 | | |
--sunfruit
简述了IOC的意义和JAVA的反射机制
IOC模式:
看到很多Java的文档都谈到了Ioc,原来IoC就是Inverse of Control啊,就是所谓的控制反转。
即由外部传入所需的对象,而非在内部定义。好处自然就是灵活性了。当然对传入的参数的要求就是面向接口了。
Java的反射机制:
通过Class类,实现动态的生成Instance(可以使用class.newInstance调用无参数的constructor或者取得特定的Constructor,再通过Constructor.newInstance。),以及动态调用Methods和设置Fields。不过对于调用特定的Method似乎也没什么很大意义,除非是已知实现了某些interface的情况下,调用指定的Method。
相比之下,C++就不能在给定class名的情况下生成Instance了,更不要谈之后的调用Method了
--sunfruit
说明了Oracle数据库中的索引隐式失效的问题
1、隐式转换导致索引失效.这一点应当引起重视.也是开发中经常会犯的错误.
由于表的字段tu_mdn定义为varchar2(20),但在查询时把该字段作为number类型以where条件传给Oracle,这样会导致索引失效.
错误的例子:select * from test where tu_mdn=13333333333;
正确的例子:select * from test where tu_mdn='13333333333';
2、对索引列进行运算导致索引失效,我所指的对索引列进行运算包括(+,-,*,/,! 等)
错误的例子:select * from test where id-1=9;
正确的例子:select * from test where id=10;
3、使用Oracle内部函数导致索引失效.对于这样情况应当创建基于函数的索引.
错误的例子:select * from test where round(id)=10; 说明,此时id的索引已经不起作用了
正确的例子:首先建立函数索引,create index test_id_fbi_idx on test(round(id));
然后 select * from test where round(id)=10; 这时函数索引起作用了
--sunfruit
总结了Oracle中关于日期函数的一些用法和技巧
上月末天:
SQL> select to_char(add_months(last_day(sysdate),-1),'yyyy-MM-dd') LastDay from
2 dual
3 ;
LASTDAY
----------
2004-09-30
上月今天
SQL> select to_char(add_months(sysdate,-1),'yyyy-MM-dd') PreToday from dual
2 ;
PRETODAY
----------
2004-09-29
上月首天
SQL> select to_char(add_months(last_day(sysdate)+1,-2),'yyyy-MM-dd') FirstDay
2 from dual;
FIRSTDAY
----------
2004-09-01
以下转贴:
Oracle
在日期使用上允许极大的灵活性。由于可以在日期字段存储时间和日期,从而有函数可以既引用日期又引用时间。
Oracle 所提供的一些日期函数如下所示。
1. SYSDATE
返回当前的日期和时间。
示例
SELECT sysdate FROM dual;
SYSDATE
----------
05-3月 -03
2. ADD_MONTHS(d, no_of_month)
当前日期"m"后推"no_of_month" 个月。参数"no_of_month"可为任何整数。
示例
SELECT add_months(sysdate,2) FROM dual;
ADD_MONTHS
----------
05-5月 -03
SELECT add_months(sysdate,-2) FROM dual;
ADD_MONTHS
----------
05-1月 -03
3. LAST_DAY(month_day)
返回变量"month_day"中所指定月份的最后一天的日期。
示例
SELECT last_day(sysdate) FROM dual;
LAST_DAY(S
----------
31-3月 -03
4. MONTHS_BETWEEN(d1, d2)
返回日期 d1 和 d2 之间的月份数。如果 d1 晚于 d2,结果为正,否则返回负数。
示例
SELECT months_between(sysdate,to_date('20030101','YYYYMMDD')) FROM dual;
MONTHS_BETWEEN(SYSDATE,TO_DATE('20030101','YYYYMMDD'))
------------------------------------------------------
2.15773932
5. NEXT_DAY(d, day_of_week)
返回由"day_of_week"命名的,在变量"d"指定的日期之后的第一个工作日的日期。参数"day_of_week"必须为该星期中的某一天。
示例
-- 2003.3.2 是星期日
SELECT next_day(to_date('20030226','YYYYMMDD'),1) FROM dual;
NEXT_DAY(T
----------
02-3月 -03
日期格式
格式元素 说明
AD 或 A.D. 带有或不带有句号的 AD 标记
BC 或 B.C. 带有或不带有句号的 BC 标记
D 一周中的天 (1-7)
DAY 天的名称 (Sunday - Saturday)
DD 一月中的天 (1 - 31)
DDD 一年中的天 (1 - 366)
DY 天的缩写 (Sun - Sat)
HH 一天中的小时 (1 - 12)
HH24 一天中的小时 (0 - 23)
MI 分钟 (0-59)
MM 月 (01-12)
MON 月名称的缩写
MONTH 月的名称
SS 秒 (0-59)
YYYY 4 个数字表示的年
--sunfruit
java做的ftp上传软件,可以定时上传文件
JDK版本
1.4.x 推荐1.4.2
服务端软件
目前只能支持SERVER_U
参数配置
在xmlfile/config.xml里面配置连接参数
功能
可以使用FTP协议上传文件,建立文件夹,建立定时上传任务
欢迎大家提意见,交流
下载链接
http://blog.blogchina.com/upload/2005-03-03/20050303145205983322.rar
--sunfruit
非常好的JSF教程,推荐
由于本站不允许上传zip、rar文件,所以把压缩好的文件做成doc的内置对象了,打开doc解压即可
下载地址:http://sunfruit.blogchina.com/inc/JSF.doc
--sunfruit
Ioc模式是一种框架技术性质的模式,它同时也为AOP的Java实现提供了一种途径
注:原贴来自J道
Ioc模式
分离关注( Separation of Concerns : SOC)是Ioc模式和AOP产生最原始动力,通过功能分解可得到关注点,这些关注可以是 组件Components, 方面Aspects或服务Services。
从GoF设计模式中,我们已经习惯一种思维编程方式:Interface Driven Design 接口驱动,接口驱动有很多好处,可以提供不同灵活的子类实现,增加代码稳定和健壮性等等,但是接口一定是需要实现的,也就是如下语句迟早要执行:
AInterface a = new AInterfaceImp();
AInterfaceImp是接口AInterface的一个子类,Ioc模式可以延缓接口的实现,根据需要实现,有个比喻:接口如同空的模型套,在必要时,需要向模型套注射石膏,这样才能成为一个模型实体,因此,我们将人为控制接口的实现成为"注射"。
Ioc英文为 Inversion of Control,即反转模式,这里有著名的好莱坞理论:你呆着别动,到时我会找你。
其实Ioc模式也是解决调用者和被调用者之间的一种关系,上述AInterface实现语句表明当前是在调用被调用者AInterfaceImp,由于被调用者名称写入了调用者的代码中,这产生了一个接口实现的原罪:彼此联系,调用者和被调用者有紧密联系,在UML中是用依赖 Dependency 表示。
但是这种依赖在分离关注的思维下是不可忍耐的,必须切割,实现调用者和被调用者解耦,新的Ioc模式 Dependency Injection 模式由此产生了, Dependency Injection模式是依赖注射的意思,也就是将依赖先剥离,然后在适当时候再注射进入。
Ioc模式(Dependency Injection模式)有三种:
第一种类型 从JNDI或ServiceManager等获得被调用者,这里类似ServiceLocator模式。 1. EJB/J2EE
2. Avalon(Apache的一个复杂使用不多的项目)
第二种类型 使用JavaBeans的setter方法 1. Spring Framework,
2. WebWork/XWork
第三种类型 在构造方法中实现依赖 1. PicoContainer,
2. HiveMind
有过EJB开发经验的人都知道,每个EJB的调用都需要通过JNDI寻找到工厂性质的Home接口,在我的教程EJB是什么章节中,我也是从依赖和工厂模式角度来阐述EJB的使用。
在通常传统情况下,为了实现调用者和被调用者解耦,分离,一般是通过工厂模式实现的,下面将通过比较工厂模式和Ioc模式不同,加深理解Ioc模式。
工厂模式和Ioc
假设有两个类B 和 C:B作为调用者,C是被调用者,在B代码中存在对C的调用:
public class B{
private C comp;
......
}
实现comp实例有两种途径:单态工厂模式和Ioc。
工厂模式实现如下:
public class B{
private C comp;
private final static MyFactory myFactory = MyFactory.getInstance();
public B(){
this.comp = myFactory.createInstanceOfC();
}
public void someMethod(){
this.comp.sayHello();
}
......
}
特点:
每次运行时,MyFactory可根据配置文件XML中定义的C子类实现,通过createInstanceOfC()生成C的具体实例。
使用Ioc依赖性注射( Dependency Injection )实现Picocontainer如下,B类如同通常POJO类,如下:
public class B{
private C comp;
public B(C comp){
this.comp = comp;
}
public void someMethod(){
this.comp.sayHello();
}
......
}
假设C接口/类有有一个具体实现CImp类。当客户端调用B时,使用下列代码:
public class client{
public static void main( String[] args ) {
DefaultPicoContainer container = new DefaultPicoContainer();
container.registerComponentImplementation(CImp.class);
container.registerComponentImplementation(B.class);
B b = (B) container.getComponentInstance(B.class);
b.someMethod();
}
}
因此,当客户端调用B时,分别使用工厂模式和Ioc有不同的特点和区别:
主要区别体现在B类的代码,如果使用Ioc,在B类代码中将不需要嵌入任何工厂模式等的代码,因为这些工厂模式其实还是与C有些间接的联系,这样,使用Ioc彻底解耦了B和C之间的联系。
使用Ioc带来的代价是:需要在客户端或其它某处进行B和C之间联系的组装。
所以,Ioc并没有消除B和C之间这样的联系,只是转移了这种联系。
这种联系转移实际也是一种分离关注,它的影响巨大,它提供了AOP实现的可能。
Ioc和AOP
AOP我们已经知道是一种面向切面的编程方式,由于Ioc解放自由了B类,而且可以向B类实现注射C类具体实现,如果把B类想像成运行时的横向动作,无疑注入C类子类就是AOP中的一种Advice,如下图:
通过下列代码说明如何使用Picocontainer实现AOP,该例程主要实现是记录logger功能,通过Picocontainer可以使用简单一行,使所有的应用类的记录功能激活。
首先编制一个记录接口:
public interface Logging {
public void enableLogging(Log log);
}
有一个LogSwitcher类,主要用来激活具体应用中的记录功能:
import org.apache.commons.logging.Log;
public class LogSwitcher
{
protected Log m_log;
public void enableLogging(Log log) {
m_log = log;
m_log.info("Logging Enabled");
}
}
一般的普通应用JavaBeans都可以继承这个类,假设PicoUserManager是一个用户管理类,代码如下:
public class PicoUserManager extends LogSwitcher
{
..... //用户管理功能
}
public class PicoXXXX1Manager extends LogSwitcher
{
..... //业务功能
}
public class PicoXXXX2Manager extends LogSwitcher
{
..... //业务功能
}
注意LogSwitcher中Log实例是由外界赋予的,也就是说即将被外界注射进入,下面看看使用Picocontainer是如何注射Log的具体实例的。
DefaultPicoContainer container = new DefaultPicoContainer();
container.registerComponentImplementation(PicoUserManager.class);
container.registerComponentImplementation(PicoXXXX1Manager.class);
container.registerComponentImplementation(PicoXXXX2Manager.class);
.....
Logging logging = (Logging) container.getComponentMulticaster();
logging.enableLogging(new SimpleLog("pico"));//激活log
由上代码可见,通过使用简单一行logging.enableLogging()方法使所有的应用类的记录功能激活。这是不是类似AOP的advice实现?
总之,使用Ioc模式,可以不管将来具体实现,完全在一个抽象层次进行描述和技术架构,因此,Ioc模式可以为容器、框架之类的软件实现提供了具体的实现手段,属于架构技术中一种重要的模式应用。
--sunfruit
讲述了SOA的基本概念,理论上让大家混个脸熟
Web service已经不再是新婚的娘子。众多企业都已经创建各种实验性Web Services 项目,事实证明,这项新兴的分布式计算技术确实能够降低集成和开发的成本。另外,一些关键的Web Services标准纷纷制定,强安全(robust security)和管理方面的产品也陆续问世。对于志向远大的企业来说,他们已经在考虑下一步了。
对大多数公司来说,下一步要考虑的不再是点对点的应用,而是Web services在企业间以及业务伙伴间更为宽广的应用。这种技术的变迁需要更松散耦合、面向基于标准的服务的架构。这样一个架构要求对IT在组织中的角色有新的观点和认识,而不仅仅是一种实现方法。通过对业务的敏捷反应,企业可以得到实实在在的回报,而要达到这一点,面向服务架构设计师的角色非常关键。除此之外,潜在的回报更是不可胜数-分布计算技术能够保证对业务需求足够灵活的反应,而这种业务上的敏捷正是各公司梦寐以求而目前还遥不可及的。
分布式计算将网络上分布的软件资源看作是各种服务。面向服务架构是一种不错的解决方案。但这种架构不是什么新思想;CORBA和DCOM就很类似,但是,这些过去的面向服务架构都受到一些难题的困扰:首先,它们是紧密耦合的,这就意味着如分布计算连接的两端都必须遵循同样API的约束。打比方说,如果一个COM对象的代码有了更改,那么访问该对象的代码也必须作出相应更改。其二,这些面向服务架构受到厂商的约束。Microsoft控制DCOM自不必说,CORBA也只是一个伪装的标准化努力,事实上,实现一个CORBA架构,经常都是在某个厂商对规范的实现上进行工作。
Web services是在改进DCOM和CORBA缺点上的努力。今天应用Web services的面向服务架构与过去不同的特点就在于它们是基于标准以及松散耦合的。广泛接受的标准(如XML和SOAP)提供了在各不同厂商解决方案之间的交互性。而松散耦合将分布计算中的参与者隔离开来,交互两边某一方的改动并不会影响到另一方。这两者的结合意味着公司可以实现某些Web services而不用对使用这些Web services的客户端的知识有任何了解。我们将这种基于标准的、松散耦合的面向服务的架构简称为SOA。
SOA的强大和灵活性将给企业带来巨大的好处。如果某组织将其IT架构抽象出来,将其功能以粗粒度的服务形式表示出来,每种服务都清晰地表示其业务价值,那么,这些服务的顾客(可能在公司内部,也可能是公司的某个业务伙伴)就可以得到这些服务,而不必考虑其后台实现的具体技术。更进一步,如果顾客能够发现并绑定可用的服务,那么在这些服务背后的IT系统能够提供更大的灵活性。
但是,要得到种强大和灵活性,需要有一种实现架构的新方法,这是一项艰巨的任务。企业架构设计师必须要变成“面向服务的架构设计师”,不仅要理解SOA,还要理解SOA的实践。在架构实践和最后得到的架构结果之间的区别非常微妙,也非常关键。本文将讨论SOA的实践,即:面向架构的设计师在构建SOA时必须要做的事情。
SOA的原则
SOA是一种企业架构,因此,它是从企业的需求开始的。但是,SOA和其它企业架构方法的不同之处在于SOA提供的业务敏捷性。业务敏捷性是指企业对变更快速和有效地进行响应、并且利用变更来得到竞争优势的能力。对架构设计师来说,创建一个业务敏捷的架构意味着创建这样一个IT架构,它可以满足当前还未知的业务需求。
要满足这种业务敏捷性,SOA的实践必须遵循以下原则:
* 业务驱动服务,服务驱动技术
从本质上说,在抽象层次上,服务位于业务和技术中间。面向服务的架构设计师一方面必须理解在业务需求和可以提供的服务之间的动态关系,另一方面,同样要理解服务与提供这些服务的底层技术之间的关系。
* 业务敏捷是基本的业务需求
SOA考虑的是下一个抽象层次:提供响应变化需求的能力是新的“元需求”,而不是处理一些业务上的固定不变的需求。从硬件系统而上的整个架构都必须满足业务敏捷的需求,因为,在SOA中任何的瓶颈都会影响到整个IT环境的灵活性。
* 一个成功的SOA总在变化之中
SOA工作的场景,更象是一个活的生物体,而不是象传统所说的“盖一栋房子”。IT环境唯一不变的就是变化,因此面向服务架构设计师的工作永远不会结束。对于习惯于盖房子的设计师来说,要转向设计一个活的生物体要求崭新的思维方式。如下文所写的,SOA的基础还是一些类似的架构准则。
SOA基础
在IT行业有两个越来越普遍的发展方向,一个是架构方面的,一个是方法学方面的,面向服务的架构设计师可以从中有所收获。第一个就是MDA(模型驱动架构),由提出CORBA的OMG模型提出。MDA认为架构设计师首先要对待创建的系统有一个形式化的UML(也是由OMG提出)的模型。MDA首先给出一个平台无关的模型来表示系统的功能需求和use cases,根据系统搭建的平台,架构设计师可以由这个平台无关的模型得到平台相关的模型,这些平台相关模型足够详细,以至于可以用来直接生成需要的代码。
MDA的核心就在于在设计阶段系统就已经完全描述,这样,在创建系统的时候,几乎就没有错误解释的可能,模型也就可以直接生成代码。但MDA有一些局限性:首先,MDA假设在创建模型之前,业务需求已经全部描述,而这一点,在当前典型的动态业务环境中几乎是不可能的。第二,MDA没有一个反馈机制。如果开发人员对模型有需要改动的地方,并没有提供给他们这么一个途径。
SOA的另一个基础是敏捷方法(AM),其中非常有名的方法是极限编程(XP)。象XP这样的AM提供了在需求未知或者多变的环境中创建软件系统的过程。XP要求在开发团队中要有一个用户代表,他帮助书写测试来指导开发人员的日常工作。开发团队中的所有成员都参与到设计之中,并且设计要尽量小并且非形式化。AM的目标是仅仅创建用户想要的,而不是在一些形式化模型上耗费工作量。AM的核心思想就在于其敏捷性-处理需求变更的敏捷性。AM的主要弱点是其规模上的限制,例如,XP在一个小团队和中型项目中效果不错,但是当项目规模增大时,如果没有一个一致的清晰的计划,项目成员很难把握项目中的方方面面。
从表面看来,MDA和AM似乎是相对立的-MDA假定需求是固定的,而AM恰恰相反。MDA的中心是形式化的模型,而AM恰恰要避开它们。但是,我们还是决定冒险把这些不同方法中的一些元素提取出来,放入到一个一致的架构实践中。
在SOA中有三个抽象层次,按照SOA的第一条准则:业务驱动服务、服务驱动技术。AM将业务模型直接和实践连接起来,表现在平台相关的模型之中。MDA并没有把业务模型和平台无关模型分开来,而是把平台无关模型做为起点。SOA必须连接这些模型,或者说抽象层次,得到单一的架构方法。我们将从五个视图的架构实现方法来实现这个连接。
SOA的五视图实现方法
企业架构设计师发现他们的职业非常有竞争力并且值得骄傲,因为他们要从很多方面来通盘考虑IT系统。Kruchten(RUP的开发负责人)将这些方面提取出来,在应用到SOA时,我们称为五视图实现方法(five-view approach)。
四个方框表示对一个架构的不同审视方法,分别代表不同的涉众(stakeholder)。弟五个视图,use-case视图涵盖了其它视图,在架构中扮演的是一个特殊的角色。部署视图将软件映射到底层平台和相关硬件上,是系统部署人员对架构的视图;实现视图描述了软件代码的组织,是从开发人员角度出发的视图;业务分析人员则利用过程视图进行工作,它描述的是软件系统的运行时特性。最后,逻辑视图表示的是用户的功能需求。在SOA中,面向服务的架构必须能够以use-case视图中的用例将用户连接到服务,将服务连接到底层的技术。
为了表示面向对象的架构是如何工作在这些视图之上,让我们将他们置于SOA元模型的上下文之中。SOA中两个领域存在重叠:由业务模型和服务模型表示的业务领域和由服务模型及平台相关模型表示的技术领域(两个领域共享服务模型)。业务用户通过逻辑视图和过程视图处理粗粒度的业务服务,根据变化的业务需求,按照需要将它们安排在过程之中。另一方面,技术专家的工作是创建并维护服务和地层技术之间的抽象层。表示这些服务的中间模型,起到的是轴心的作用,业务以它为中心进行。
SOA元模型从MDA中继承平台无关模型和平台相关模型,但是添加了AM和用户交互以及敏捷的反馈这两部分,后者通过椭圆之间的双向箭头来表现。类似地,元模型通过引入由中心的服务模型提供的中间层抽象解决了AM在伸缩性方面的问题。这样,服务模型中的任何需求的变化,都会反映到用户每天的业务处理中。同样,由于底层技术是模型驱动的,技术专家也可以根据这些变化的需求迅速而有效地作出应变。
SOA实践和过去解决企业架构传统方式的不同之处就在于其对敏捷性的支持。如前所说,SOA的第三条原则就在于它总在变化之中。这种恒在的变化性环境是SOA实践的基石。如图所示,涉众(stakeholders,译者注:RUP中也有这个词,表示软件开发中涉及到的各种角色如:用户、设计人员、开发人员乃至测试人员等等。)在一个必需的基础上影响到整个架构的变化。在当技术专家在每天的日常工作中不断对变化的业务需求作出响应的这种情况下,设计阶段和运行阶段之间的界限变得模糊起来,很难清晰地分离这两个阶段。
剩下的部分
我们已经为面向服务的架构提供了一个高层次的框架,其中MDA和AM的元素帮助工具的使用者来创建和维护SOA。但是,SOA中还缺少一些内容-那就是软件开发商和专业的服务组织必需提供的。理想情况下,开发商必需提供面向服务的业务流程、工作流以及服务的协调工具和服务;另外,能够以一种敏捷的、平台无关的方式充分反映业务服务的建模工具也是必须的;技术专家必须配备可以从模型中自动生成代码,并在代码变化时更新模型的工具,最后,开发商必须提供支持SOA的软件,帮助面向服务的架构设计师以一种可信并且可伸缩的方式创建位于服务和底层技术之间的抽象层次。幸运的是,这方面的产品即将上市。
另外,最重要的就是贯穿本文的自顶而下的SOA实现方法了。今天关于Web services的大部分思考都是自底而上的:“这是如何创建Web services的方法,现在,我们来使用它们集成吧”,对Web services技术的这种方法是伟大的第一步,因为它可以惊人地降低集成的开销,这是现在的技术管理人员最乐意见到的了。但当经济进一步发展,IT走出低谷,企业会寻求IT的帮助来提高组织战略意义上的核心价值。使用面向服务的架构,IT可以提供给企业实现业务敏捷性的这样一个框架。
--sunfruit
今天安家在blogjava了,呵呵,大家多关照欧