2006年4月19日
#
--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要求,就要采取向客户报告、并对客户进行计费调整等措施,以取得客户的理解和谅解,让客户感到满意。(人民邮电报)