随笔-88  评论-77  文章-48  trackbacks-0
  2006年4月28日

发布21个已知被微软黑屏的序列号提醒相关用户不要冒险去验证升级

发布21个已知被微软黑屏的序列号,提醒使用该序列号的用户就不要冒险去验证升级了。

已知被黑屏的序列号(这21个序列号是我今天一个个通过验证测试出来的结果):

XP8BF-F8HPF-PY6BX-K24PJ-TWT6M 

F4297-RCWJP-P482C-YY23Y-XH8W3

HH7VV-6P3G9-82TWK-QKJJ3-MXR96

HCQ9D-TVCWX-X9QRG-J4B2Y-GR2TT

MRX3F-47B9T-2487J-KWKMF-RPWBY

T72KM-6GWBP-GX7TD-CXFT2-7WT2B

QHYXK-JCJRX-XXY8Y-2KX2X-CCXGD

DG8FV-B9TKY-FRT9J-6CRCC-XPQ4G

MFBF7-2CK8B-93MDB-8MR7T-4QRCQ

MRX3F-47B9T-2487J-KWKMF-RPWBY

DFXFM-DKWTG-MYDWJ-68DQF-YBCYG

TB7JC-3VJKY-J3VMR-PP8TP-HMTWY

KR88V-RCBCB-D78QP-V4FF9-JW77M

HWCTG-CQPWK-D22DQ-JD7TQ-GXVW8

Q7TG8-MQ3BK-RHXQC-YDXJD-6Q6M3

BP6XM-YXMKY-V4Q74-KJP8X-VW3CQ

BFBPF-TF64Y-BT8T9-DQJFY-3TH7W

GQ9CX-QDFPR-XFRMF-T3M94-2JG9Y

XPF44-PDM32-XT4B8-R8W8C-CPP6T

RC7QJ-KX66J-V4R9W-32HC4-CJH2B

DDQXW-THQ8M-79V6K-2YFGH-R793Q

注:但有网友反映MRX3F-47B9T-2487J-KWKMF-RPWBY这个序列号有些地方能通过正版验证,有些地方不能通过正版验证,看来微软这次验证还分蛮细的,并可以按地区来判断序列号是否为盗版和正版呀。
posted @ 2008-10-26 00:40 崛起的程序员 阅读(368) | 评论 (0)编辑 收藏

AJAX 流行之后,总想好好学习一下。但是众多的框架实在难以选择。说明一下 ASP.NET AJAX 并不包括在 AJAX 框架之中。
刚开始学了 JQuqery, 众多的 $get(),...等等符号早已把我搞晕了。暂时就放弃了。
后来学习 ASP.NET AJAX ,在微软的领导下,逐渐由服务器端转向客户端编程。 激起我客户端编程的兴趣,
才想起学习一下了 Jquery.
      随着WEB2.0及ajax思想在互联网上的快速发展传播,陆续出现了一些优秀的Js框架,其中比较著名的有Prototype、YUI、jQuery、mootools、Bindows以及国内的JSVM框架等,通过将这些JS框架应用到我们的项目中能够使程序员从设计和书写繁杂的JS应用中解脱出来,将关注点转向功能需求而非实现细节上,从而提高项目的开发速度。
      jQuery是继prototype之后的又一个优秀的Javascript框架。它是由 John Resig 于 2006 年初创建的,它有助于简化 JavaScript™ 以及Ajax 编程。有人使用这样的一比喻来比较prototype和jQuery:prototype就像Java,而jQuery就像ruby. 它是一个简洁快速灵活的JavaScript框架,它能让你在你的网页上简单的操作文档、处理事件、实现特效并为Web页面添加Ajax交互。

它具有如下一些特点:
1、代码简练、语义易懂、学习快速、文档丰富。
2、jQuery是一个轻量级的脚本,其代码非常小巧,最新版的JavaScript包只有20K左右。
3、jQuery支持CSS1-CSS3,以及基本的xPath。
4、jQuery是跨浏览器的,它支持的浏览器包括IE 6.0+, FF 1.5+, Safari 2.0+, Opera 9.0+。
5、可以很容易的为jQuery扩展其他功能。
6、能将JS代码和HTML代码完全分离,便于代码和维护和修改。
7、插件丰富,除了jQuery本身带有的一些特效外,可以通过插件实现更多功能,如表单验证、tab导航、拖放效果、表格排序、DataGrid,树形菜单、图像特效以及ajax上传等。

jQuery的设计会改变你写JavaScript代码的方式,降低你学习使用JS操作网页的复杂度,提高网页JS开发效率,无论对于js初学者还是资深专家,jQuery都将是您的首选。
jQuery适合于设计师、开发者以及那些还好者,同样适合用于商业开发,可以说jQuery适合任何JavaScript应用的地方,可用于不同的Web应用程序中。
官方站点:http://jquery.com/  中文站点:http://jquery.org.cn/

1.2、目的
通过学习本文档,能够对jQuery有一个简单的认识了解,清楚JQuery与其他JS框架的不同,掌握jQuery的常用语法、使用技巧及注意事项。

二、使用方法
在需要使用JQuery的页面中引入JQuery的js文件即可。
例如:<script type="text/javascript" src="js/jquery.js"></script>
引入之后便可在页面的任意地方使用jQuery提供的语法。

三、学习教程及参考资料
请参照《jQuery中文API手册》和http://jquery.org.cn/visual/cn/index.xml
推荐两篇不错的jquery教程:《jQuery的起点教程》和《使用 jQuery 简化 Ajax 开发》


四、语法总结和注意事项

1、关于页面元素的引用
通过jquery的$()引用元素包括通过id、class、元素名以及元素的层级关系及dom或者xpath条件等方法,且返回的对象为jquery对象(集合对象),不能直接调用dom定义的方法。

2、jQuery对象与dom对象的转换
只有jquery对象才能使用jquery定义的方法。注意dom对象和jquery对象是有区别的,调用方法时要注意操作的是dom对象还是jquery对象。
普通的dom对象一般可以通过$()转换成jquery对象。
如:$(document.getElementById("msg"))则为jquery对象,可以使用jquery的方法。
由于jquery对象本身是一个集合。所以如果jquery对象要转换为dom对象则必须取出其中的某一项,一般可通过索引取出。
如:$("#msg")[0],$("div").eq(1)[0],$("div").get()[1],$("td")[5]这些都是dom对象,可以使用dom中的方法,但不能再使用Jquery的方法。
以下几种写法都是正确的:
$("#msg").html();
$("#msg")[0].innerHTML;
$("#msg").eq(0)[0].innerHTML;
$("#msg").get(0).innerHTML;

3、如何获取jQuery集合的某一项
对于获取的元素集合,获取其中的某一项(通过索引指定)可以使用eqget(n)方法或者索引号获取,要注意,eq返回的是jquery对象,而get(n)和索引返回的是dom元素对象。对于jquery对象只能使用jquery的方法,而dom对象只能使用dom的方法,如要获取第三个<div>元素的内容。有如下两种方法:
$("div").eq(2).html();    //调用jquery对象的方法
$("div").get(2).innerHTML;  //调用dom的方法属性

4、同一函数实现set和get
Jquery中的很多方法都是如此,主要包括如下几个:
$("#msg").html();    //返回id为msg的元素节点的html内容。
$("#msg").html("<b>new content</b>");  
//将“<b>new content</b>” 作为html串写入id为msg的元素节点内容中,页面显示粗体的new content

$("#msg").text();    //返回id为msg的元素节点的文本内容。
$("#msg").text("<b>new content</b>");  
//将“<b>new content</b>” 作为普通文本串写入id为msg的元素节点内容中,页面显示<b>new content</b>

$("#msg").height();    //返回id为msg的元素的高度
$("#msg").height("300");  //将id为msg的元素的高度设为300
$("#msg").width();    //返回id为msg的元素的宽度
$("#msg").width("300");  //将id为msg的元素的宽度设为300

$("input").val(");  //返回表单输入框的value值
$("input").val("test");  //将表单输入框的value值设为test

$("#msg").click();  //触发id为msg的元素的单击事件
$("#msg").click(fn);  //为id为msg的元素单击事件添加函数
同样blur,focus,select,submit事件都可以有着两种调用方法

5、集合处理功能
对于jquery返回的集合内容无需我们自己循环遍历并对每个对象分别做处理,jquery已经为我们提供的很方便的方法进行集合的处理。
包括两种形式:
$("p").each(function(i){this.style.color=['#f00','#0f0','#00f'][i]})  
//为索引分别为0,1,2的p元素分别设定不同的字体颜色。

$("tr").each(function(i){this.style.backgroundColor=['#ccc','#fff'][i%2]})  
//实现表格的隔行换色效果

$("p").click(function(){alert($(this).html())})    
//为每个p元素增加了click事件,单击某个p元素则弹出其内容

6、扩展我们需要的功能
$.extend({
  min: function(a, b){return a < b?a:b; },
  max: function(a, b){return a > b?a:b; }
});  //为jquery扩展了min,max两个方法
使用扩展的方法(通过“$.方法名”调用):
alert("a=10,b=20,max="+$.max(10,20)+",min="+$.min(10,20));

7、支持方法的连写
所谓连写,即可以对一个jquery对象连续调用各种不同的方法。
例如:
$("p").click(function(){alert($(this).html())})
.mouseover(function(){alert('mouse over event')})
.each(function(i){this.style.color=['#f00','#0f0','#00f'][i]});

8、操作元素的样式
主要包括以下几种方式:
$("#msg").css("background");    //返回元素的背景颜色
$("#msg").css("background","#ccc")  //设定元素背景为灰色
$("#msg").height(300); $("#msg").width("200");  //设定宽高
$("#msg").css({ color: "red", background: "blue" });//以名值对的形式设定样式
$("#msg").addClass("select");  //为元素增加名称为select的class
$("#msg").removeClass("select");  //删除元素名称为select的class
$("#msg").toggleClass("select");  //如果存在(不存在)就删除(添加)名称为select的class

9、完善的事件处理功能
Jquery已经为我们提供了各种事件处理方法,我们无需在html元素上直接写事件,而可以直接为通过jquery获取的对象添加事件。
如:
$("#msg").click(function(){alert("good")})  //为元素添加了单击事件
$("p").click(function(i){this.style.color=['#f00','#0f0','#00f'][i]})
//为三个不同的p元素单击事件分别设定不同的处理
jQuery中几个自定义的事件:
(1)hover(fn1,fn2):一个模仿悬停事件(鼠标移动到一个对象上面及移出这个对象)的方法。当鼠标移动到一个匹配的元素上面时,会触发指定的第一个函数。当鼠标移出这个元素时,会触发指定的第二个函数。
//当鼠标放在表格的某行上时将class置为over,离开时置为out。
$("tr").hover(function(){
$(this).addClass("over");
},
  function(){
   $(this).addClass("out");
});
(2)ready(fn):当DOM载入就绪可以查询及操纵时绑定一个要执行的函数。
$(document).ready(function(){alert("Load Success")})
//页面加载完毕提示“Load Success”,相当于onload事件。与$(fn)等价
(3)toggle(evenFn,oddFn): 每次点击时切换要调用的函数。如果点击了一个匹配的元素,则触发指定的第一个函数,当再次点击同一元素时,则触发指定的第二个函数。随后的每次点击都重复对这两个函数的轮番调用。
  //每次点击时轮换添加和删除名为selected的class。
  $("p").toggle(function(){
    $(this).addClass("selected");  
  },function(){
     $(this).removeClass("selected");
  });
(4)trigger(eventtype): 在每一个匹配的元素上触发某类事件。
例如:
  $("p").trigger("click");    //触发所有p元素的click事件
(5)bind(eventtype,fn),unbind(eventtype): 事件的绑定与反绑定
从每一个匹配的元素中(添加)删除绑定的事件。
例如:
$("p").bind("click", function(){alert($(this).text());});  //为每个p元素添加单击事件
$("p").unbind();  //删除所有p元素上的所有事件
$("p").unbind("click")  //删除所有p元素上的单击事件

10、几个实用特效功能
其中toggle()和slidetoggle()方法提供了状态切换功能。
如toggle()方法包括了hide()和show()方法。
slideToggle()方法包括了slideDown()和slideUp方法。

11、几个有用的jQuery方法
$.browser.浏览器类型:检测浏览器类型。有效参数:safari, opera, msie, mozilla。如检测是否ie:$.browser.isie,是ie浏览器则返回true。
$.each(obj, fn):通用的迭代函数。可用于近似地迭代对象和数组(代替循环)。

$.each( [0,1,2], function(i, n){ alert( "Item #" + i + ": " + n ); });
等价于:
var tempArr=[0,1,2];
for(var i=0;i<tempArr.length;i++){
  alert("Item #"+i+": "+tempArr[i]);
}
也可以处理json数据,如
$.each( { name: "John", lang: "JS" }, function(i, n){ alert( "Name: " + i + ", Value: " + n ); });
结果为:
Name:name, Value:John
Name:lang, Value:JS
$.extend(target,prop1,propN):用一个或多个其他对象来扩展一个对象,返回这个被扩展的对象。这是jquery实现的继承方式。
如:
$.extend(settings, options);  
//合并settings和options,并将合并结果返回settings中,相当于options继承setting并将继承结果保存在setting中。
var settings = $.extend({}, defaults, options);
//合并defaults和options,并将合并结果返回到setting中而不覆盖default内容。
可以有多个参数(合并多项并返回)
$.map(array, fn):数组映射。把一个数组中的项目(处理转换后)保存到到另一个新数组中,并返回生成的新数组。
如:
var tempArr=$.map( [0,1,2], function(i){ return i + 4; });
tempArr内容为:[4,5,6]
var tempArr=$.map( [0,1,2], function(i){ return i > 0 ? i + 1 : null; });
tempArr内容为:[2,3]
$.merge(arr1,arr2):合并两个数组并删除其中重复的项目。
如:$.merge( [0,1,2], [2,3,4] )  //返回[0,1,2,3,4]
$.trim(str):删除字符串两端的空白字符。
如:$.trim("  hello, how are you?   ");   //返回"hello,how are you? "

12、解决自定义方法或其他类库与jQuery的冲突
很多时候我们自己定义了$(id)方法来获取一个元素,或者其他的一些js类库如prototype也都定义了$方法,如果同时把这些内容放在一起就会引起变量方法定义冲突,Jquery对此专门提供了方法用于解决此问题。
使用jquery中的jQuery.noConflict();方法即可把变量$的控制权让渡给第一个实现它的那个库或之前自定义的$方法。之后应用Jquery的时候只要将所有的$换成jQuery即可,如原来引用对象方法$("#msg")改为jQuery("#msg")。
如:
jQuery.noConflict();
// 开始使用jQuery
jQuery("div   p").hide();
// 使用其他库的 $()
$("content").style.display = 'none';

posted @ 2008-09-21 21:24 崛起的程序员 阅读(308) | 评论 (0)编辑 收藏
1377-4167-5844-4698-0048-5821
posted @ 2008-03-02 19:35 崛起的程序员 阅读(332) | 评论 (0)编辑 收藏

Flex License:
1307-1581-4356-2616-4951-7949 (Commercial Version)
1307-1581-4356-2939-1231-4484 (Education Version)

Charting License:

1301-4581-4356-7349-9369-3351 (Commercial Version)

posted @ 2008-02-24 21:43 崛起的程序员 阅读(400) | 评论 (0)编辑 收藏

Today we shipped Visual Studio 2008 and .NET 3.5.  You can download the final release using one of the links below:

  • If you are a MSDN subscriber, you can download your copy from the MSDN subscription site (note: some of the builds are just finishing being uploaded now - so check back later during the day if you don't see it yet).

  • If you are a non-MSDN subscriber, you can download a 90-day free trial edition of Visual Studio 2008 Team Suite here.  A 90-day trial edition of Visual Studio 2008 Professional (which will be a slightly smaller download) will be available next week.  A 90-day free trial edition of Team Foundation Server can also be downloaded here.

  • If you want to use the free Visual Studio 2008 Express editions (which are much smaller and totally free), you can download them here

  • If you want to just install the .NET Framework 3.5 runtime, you can download it here.

Quick Tour of Some of the New Features

Visual Studio 2008 and .NET 3.5 contain a ton of new functionality and improvements.  Below are links to blog posts I've done myself as well as links to videos you can watch to learn more about it:

VS 2008 Multi-Targeting Support

VS 2008 enables you to build applications that target multiple versions of the .NET Framework.  This means you can use VS 2008 to open, edit and build existing .NET 2.0 and ASP.NET 2.0 applications (including ASP.NET 2.0 applications using ASP.NET AJAX 1.0), and continue to deploy these application on .NET 2.0 machines.  You can learn more about how this works from my blog post here:

ASP.NET AJAX and JavaScript Support

.NET 3.5 has ASP.NET AJAX built-in (no separate download required).  In addition to including all of the features in ASP.NET AJAX 1.0, ASP.NET 3.5 also now includes richer support for UpdatePanels integrating with WebParts, ASP.NET AJAX integration with controls like <asp:menu> and <asp:treeview>, WCF support for JSON, and many other AJAX improvements.

VS 2008 and Visual Web Developer 2008 also now have great support for integrating JavaScript and AJAX into your applications.  You can learn more about this from my blog posts here:

You can watch some videos that discuss ASP.NET AJAX and Visual Studio 2008 support for it here

I also highly recommend the excellent ASP.NET AJAX in Action book to learn more about ASP.NET AJAX (both client-side and server-side).

VS 2008 Web Designer and CSS Support

VS 2008 and Visual Web Developer 2008 Express includes a significantly improved HTML web designer (the same one that ships with Expression Web).  This delivers support for split-view editing, nested master pages, and great CSS integration.  Below are some articles I've written that discuss this more:

ASP.NET 3.5 also has a new <asp:ListView> control that provides the ability to perform rich data scenarios with total control over the markup.  It works nicely with the new CSS support in VS 2008.  You can learn more about it from my article here:

You can watch some videos that discuss the new Visual Studio 2008 web designer features and the new ListView/DataPager controls here

Language Improvements and LINQ

The new VB and C# compilers in VS 2008 deliver significant improvements to the languages.  Both add functional programming concepts that enable you to write cleaner, terser, and more expressive code.  These features also enable a new programming model we call LINQ (language integrated query) that makes querying and working with data a first-class programming concept with .NET. 

Below are some of the articles I've written that explore these new language features using C#:

Here are a few additional blog posts I've written that show off some of the new VS 2008 code editing support and some cool ways to use these new language features:

The Visual Basic team has also created some great free videos that cover LINQ.  You can watch them here.

Data Access Improvements with LINQ to SQL

LINQ to SQL is a built-in OR/M (object relational mapper) in .NET 3.5.  It enables you to model relational databases using a .NET object model.  You can then query the database using LINQ, as well as update/insert/delete data from it.  LINQ to SQL fully supports transactions, views, and stored procedures.  It also provides an easy way to integrate business logic and validation rules into your data model.  Below are some of the articles I've written that explore how to use it:

I think you'll find that LINQ and LINQ to SQL makes it much easier to build much cleaner data models, and write much cleaner data code.  I'll be adding more posts to my LINQ to SQL series in the weeks and months ahead (sorry for the delay in finishing them earlier - so much to-do and so little time to-do it all!).

Scott Stanfield is also working on creating some great LINQ to SQL videos for the www.asp.net site based on my article series above (all videos are in both VB and C#).  You can watch the first 4 videos in this series here.

Browsing the .NET Framework Library Source using Visual Studio

As I blogged a few weeks ago, we will be releasing a reference version of the .NET Framework library source code as part of this release.  Visual Studio 2008 has built-in debugger support to automatically step-into and debug this code on demand (VS 2008 can pull down the source for the appropriate .NET Framework library file automatically for you).

We are deploying the source servers to enable this right now, and will be publishing the steps to turn this feature on in the next few weeks.

Lots of other improvements

The list above is only a small set of the improvements coming.  For client development VS 2008 includes WPF designer and project support.  ClickOnce and WPF XBAPs now work with FireFox.  WinForms and WPF projects can also now use the ASP.NET Application Services (Membership, Roles, Profile) for roaming user data. 

Office development is much richer - including support for integrating with the Office 2007 ribbon, and with Outlook.  Visual Studio Tools for Office support is also now built-into Visual Studio (you no longer need to buy a separate product).

New WCF and Workflow projects and designers are now included in VS 2008.  Unit testing support is now much faster and included in VS Professional (and no longer just VSTS).  Continuous Integration support is now built-in with TFS.  AJAX web testing (unit and load) is now supported in the VS Test SKU.  And there is much, much more...

Installation Suggestions

People often ask me for suggestions on how best to upgrade from previous betas of Visual Studio 2008.  In general I'd recommend uninstalling the Beta2 bits explicitly.  As part of this you should uninstall Visual Studio 2008 Beta2, .NET Framework Beta2, as well as the Visual Studio Web Authoring Component (these are all separate installs and need to be uninstalled separately).  I then usually recommend rebooting the machine after uninstalling just to make sure everything is clean before you kick off the new install.  You can then install the final release of VS 2008 and .NET 3.5 on the machine.

Once installed, I usually recommend explicitly running the Tools->Import and Export Settings menu option, choosing the "Reset Settings" option, and then re-pick your preferred profile.  This helps ensure that older settings from the Beta2 release are no longer around (and sometimes seems to help with performance).

Note that VS 2008 runs side-by-side with VS 2005 - so it is totally fine to have both on the same machine (you will not have any problems with them on the same box).

Silverlight Tools and VS Web Deployment Project Add-Ins

Two popular add-ins to Visual Studio are not yet available to download for the final VS 2008 release.  These are the Silverlight 1.1 Tools Alpha for Visual Studio and the Web Deployment Project add-in for Visual Studio.  Our hope is to post updates to both of them to work with the final VS 2008 release in the next two weeks.  If you are doing Silverlight 1.1 development using VS 2008 Beta2 you'll want to stick with with VS 2008 Beta2 until this updated Silverlight Tools Add-In is available. 

posted @ 2007-12-02 02:24 崛起的程序员 阅读(845) | 评论 (0)编辑 收藏

PYHYP-WXB3B-B2CCM-V9DX9-VDY8T

在开始>设置>控制面版>添加或删除程序>卸载vs.net2008>出现卸载界面>点击Next>输入上面CD-key ->出现Success画面。。激动ING

本人使用的是VS2008 RTM版..


Microsoft.Visual.Studio.Team.System.2008.Team.Suite-ZWTiSO
ed2k: Microsoft.Visual.Studio.Team.System.2008.Team.Suite-ZWTiSO.iso  [3.83 Gb]
ed2k: Microsoft.Visual.Studio.Team.System.2008.Team.Suite-ZWTiSO.nfo  [5.8 Kb]
[ Add all 2 links to your ed2k client ]

Microsoft.Visual.Studio.2008.Professional.Edition-ZWTiSO
ed2k: Microsoft.Visual.Studio.2008.Professional.Edition-ZWTiSO.iso  [3.31 Gb]
ed2k: Microsoft.Visual.Studio.2008.Professional.Edition-ZWTiSO.nfo  [5.5 Kb]
[ Add all 2 links to your ed2k client ]

Microsoft.Visual.Studio.Team.System.2008.Development.Edition
ed2k: Microsoft.Visual.Studio.Team.System.2008.Development.Edition-ZWTiSO.iso  [3.81 Gb]
ed2k: Microsoft.Visual.Studio.Team.System.2008.Development.Edition-ZWTiSO.nfo  [5.5 Kb]
[ Add all 2 links to your ed2k client ]

Microsoft.Visual.Studio.Team.System.2008.Team.Foundation.Server.Workgroup.Edition-ZWTiSO
ed2k: Microsoft.Visual.Studio.Team.System.2008.Team.Foundation.Server.Workgroup.Edition-ZWTiSO.iso  [1.29 Gb]
ed2k: Microsoft.Visual.Studio.Team.System.2008.Team.Foundation.Server.Workgroup.Edition-ZWTiSO.nfo  [5.7 Kb]
[ Add all 2 links to your ed2k client ]

Microsoft.Visual.Studio.Team.System.2008.Test.Load.Agent-ZWT
ed2k: Microsoft.Visual.Studio.Team.System.2008.Test.Load.Agent-ZWTiSO.iso  [551.01 Mb]
ed2k: Microsoft.Visual.Studio.Team.System.2008.Test.Load.Agent-ZWTiSO.nfo  [5.7 Kb]
[ Add all 2 links to your ed2k client ]

posted @ 2007-12-02 02:21 崛起的程序员 阅读(20607) | 评论 (30)编辑 收藏

注册机下载地址

http://www.blogjava.net/Files/ioriqw/Altova%202008.rar
posted @ 2007-10-30 16:23 崛起的程序员 阅读(993) | 评论 (1)编辑 收藏
     摘要: 在网上找了一圈Ajax的Java框架,感觉不错的只有两个,一个是被提到比较多的DWR(Direct Web Remoting),另一个是刚刚1.0的Ajax4JSF。本文先用代码说话,各用两个框架开发同样功能的Ajax输入验证的小程序。在最后表达一下鄙人对这两个框架的浅见。·程序功能    页面上有两个输入框,一个填用户名,一个填密码。两个框空着时提示...  阅读全文
posted @ 2007-08-27 20:17 崛起的程序员 阅读(561) | 评论 (0)编辑 收藏
Go to window-preferences-java-installed jre's- edit
and add the values
-Xms512m -Xmx512m -Xss256k
(or the amount of memory you want)
eclipse starts tomcat with those values, so your memory problems should be solved
posted @ 2007-06-10 22:26 崛起的程序员 阅读(374) | 评论 (0)编辑 收藏
 
有个系统隐藏的文件夹System Volume Information会达到1G甚至20G,这是系统还原的文件夹,这个目录是WINDOWS对于大硬盘搜索方便的索引记录文件!会在WINDOWS空闲时自动记录,所以这个文件夹会越来越大,然后PF使用率不断上升,导致机器卡住!&W&D.C g V b j [ a
我们可以禁用这个自动索引功能!打开搜索功能->改变首选项->不使用制作索引服务->不,不要启用制作索引服务->确定。还有一件事,就是回到刚才的地方,下面还有一个“改变制作索引服务设置(高级)”,按下去,右键弹出的窗口中的那个索引服务->删除,就好了!海腾数据中心服务器论坛 Y \ M z(R1{
O

f ?&f t j9Y&] B `
SystemVolumeInformation\catalog.wci的文件用来存放索引文件,而且在系统空闲时,Windows会自动读硬盘更新索引,安装的文件越多,索引文件会越来越大。
       D Y }&X
删除索引服务:海腾数据、服务器论坛联盟、win服务器、代理服务器,邮件服务器、代码、程序、游戏下载、软件、电脑技术、设计、图片、信息发布 N i$y l z:M }
运行msconfig,然后选择服务选项,找到IndexingService,将前面的小勾去掉,再删掉文件夹即可。
posted @ 2007-04-11 10:36 崛起的程序员 阅读(1429) | 评论 (0)编辑 收藏

Introduction

Changing proxy settings of IE is a frequent requirement of mine. Then I got the idea of writing a tool by myself, at last. I have not found clear instructions on this. Many articles recommend to modify registry directly, but unfortunately their instruction is not enough. Most of them direct me to modify the following values in registry :-

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings]
"ProxyEnable"=dword:00000001
"ProxyServer"=":"
"ProxyOverride"=""
"DisablePasswordCaching"=dword:00000001

Details

I tested it and find that it does not work at least on my computer.( I access internet by ADSL connection.) So I backed up registry and modified proxy settings via Internet Explorer, finding that [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections] is also changed. So I wrote the following code snippet to change proxy settings:

Collapse
				void ShowError(long lerr)
{
    LPVOID lpMsgBuf;
    if (!FormatMessage( 
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM | 
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        lerr,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
        (LPTSTR) &lpMsgBuf,
        0,
        NULL ))
    {
        return;
    }
    MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
    LocalFree( lpMsgBuf );
}
void CieproxyDlg::OnBnClickedOk()
{//set proxy server
    UpdateData();
    GetDlgItemText(IDC_EDIT1,m_sIEProxy);
    HKEY hk;
    LONG lret=RegOpenKeyEx(HKEY_CURRENT_USER,
        "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
        NULL,KEY_WRITE|KEY_SET_VALUE,&hk);
    if(lret==ERROR_SUCCESS&&NULL!=hk)
    {
        TCHAR* pbuf=m_sIEProxy.GetBuffer(1);
        lret=RegSetValueEx( hk,"ProxyServer",NULL,REG_SZ,pbuf,m_sIEProxy.GetLength());
        DWORD dwenable=1;
        lret=RegSetValueEx(hk,"ProxyEnable",NULL,REG_DWORD,
           (LPBYTE)&dwenable,sizeof(dwenable));
        RegCloseKey(hk);
    }
    const TCHAR* keyname3=_T(
      "software\\Microsoft\\windows\\currentversion\\Internet Settings\\Connections");
    lret=RegOpenKeyEx(HKEY_CURRENT_USER,keyname3,NULL,
        KEY_READ|KEY_WRITE|KEY_SET_VALUE,&hk);
    if(lret==ERROR_SUCCESS&&NULL!=hk)
    {
        DWORD dwtype;
        char pbuf[256];
        DWORD dwlen=sizeof(pbuf);
        constchar* valname="Connection to adsl3";
        lret=RegQueryValueEx(hk,valname,NULL,&dwtype,pbuf,&dwlen);
        if(lret!=ERROR_SUCCESS)
        {
            ShowError(lret);
        }
        pbuf[8] = 3;//enable proxy
        pbuf[4]=pbuf[4]+1;
        constchar* p=m_sIEProxy.GetBuffer(1);
        memcpy(pbuf+16,p,m_sIEProxy.GetLength());
        char c=0;
        for(int i=m_sIEProxy.GetLength();i<20;i++)
            pbuf[16+i]=c;
        m_sIEProxy.ReleaseBuffer();
        lret=RegSetValueEx(hk,valname,NULL,REG_BINARY,pbuf,dwlen);
        RegCloseKey(hk);
    }
    DWORD dwret;
    SendMessageTimeout(HWND_BROADCAST,WM_SETTINGCHANGE,NULL,NULL,
        SMTO_NORMAL,1000,&dwret);
}

void CieproxyDlg::OnBnClickedDisableProxy()
{
    UpdateData();
    GetDlgItemText(IDC_EDIT1,m_sIEProxy);
    HKEY hk;
    LONG lret=RegOpenKeyEx(HKEY_CURRENT_USER,
        "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
        NULL,KEY_WRITE|KEY_SET_VALUE,&hk);
    if(lret==ERROR_SUCCESS&&NULL!=hk)
    {
        DWORD dwenable=0;
        lret=RegSetValueEx(hk,"ProxyEnable",NULL,REG_DWORD,
            (LPBYTE)&dwenable,sizeof(dwenable));
        RegCloseKey(hk);
    }
    const TCHAR* keyname3=_T(
      "software\\Microsoft\\windows\\currentversion\\Internet Settings\\Connections");
    lret=RegOpenKeyEx(HKEY_CURRENT_USER,keyname3,
        NULL,KEY_READ|KEY_WRITE|KEY_SET_VALUE,&hk);
    if(lret==ERROR_SUCCESS&&NULL!=hk)
    {
        DWORD dwtype;
        char pbuf[256];
        DWORD dwlen=sizeof(pbuf);
        constchar* valname="Connection to adsl3";
        lret=RegQueryValueEx(hk,valname,NULL,&dwtype,pbuf,&dwlen);
        if(lret!=ERROR_SUCCESS)
        {
            ShowError(lret);
        }
        pbuf[8] = 1;//enable proxy
        pbuf[4]=pbuf[4]+1;
        lret=RegSetValueEx(hk,valname,NULL,REG_BINARY,pbuf,dwlen);
        RegCloseKey(hk);
    }
    DWORD dwret;
    SendMessageTimeout(HWND_BROADCAST,WM_SETTINGCHANGE,NULL,NULL,SMTO_NORMAL,
        1000,&dwret);
}

Problem with above code is that existing Internet Explorer instances don't know the change of settings. What is more, changing registry directly is not an elegant method. Then the following must be more attractive :

Collapse
BOOL SetConnectionOptions(LPCTSTR conn_name,LPCTSTR proxy_full_addr)
{
    //conn_name: active connection name. //proxy_full_addr : eg "210.78.22.87:8000"
    INTERNET_PER_CONN_OPTION_LIST list;
    BOOL    bReturn;
    DWORD   dwBufSize = sizeof(list);
    // Fill out list struct.
    list.dwSize = sizeof(list);
    // NULL == LAN, otherwise connectoid name.
    list.pszConnection = conn_name;
    // Set three options.
    list.dwOptionCount = 3;
    list.pOptions = new INTERNET_PER_CONN_OPTION[3];
    // Make sure the memory was allocated.if(NULL == list.pOptions)
    {
        // Return FALSE if the memory wasn't allocated.
        OutputDebugString("failed to allocat memory in SetConnectionOptions()");
        return FALSE;
    }
    // Set flags.
    list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS;
    list.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT |
        PROXY_TYPE_PROXY;

    // Set proxy name.
    list.pOptions[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER;
    list.pOptions[1].Value.pszValue = proxy_full_addr;//"http://proxy:80";// Set proxy override.
    list.pOptions[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS;
    list.pOptions[2].Value.pszValue = "local";

    // Set the options on the connection.
    bReturn = InternetSetOption(NULL,
        INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize);

    // Free the allocated memory.delete [] list.pOptions;
    InternetSetOption(NULL, INTERNET_OPTION_SETTINGS_CHANGED, NULL, 0);
    InternetSetOption(NULL, INTERNET_OPTION_REFRESH , NULL, 0);
    return bReturn;
}
BOOL DisableConnectionProxy(LPCTSTR conn_name)
{
    //conn_name: active connection name. 
    INTERNET_PER_CONN_OPTION_LIST list;
    BOOL    bReturn;
    DWORD   dwBufSize = sizeof(list);
    // Fill out list struct.
    list.dwSize = sizeof(list);
    // NULL == LAN, otherwise connectoid name.
    list.pszConnection = conn_name;
    // Set three options.
    list.dwOptionCount = 1;
    list.pOptions = new INTERNET_PER_CONN_OPTION[list.dwOptionCount];
    // Make sure the memory was allocated.if(NULL == list.pOptions)
    {
        // Return FALSE if the memory wasn't allocated.
        OutputDebugString("failed to allocat memory in DisableConnectionProxy()");
        return FALSE;
    }
    // Set flags.
    list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS;
    list.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT  ;
    // Set the options on the connection.
    bReturn = InternetSetOption(NULL,
        INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize);
    // Free the allocated memory.delete [] list.pOptions;
    InternetSetOption(NULL, INTERNET_OPTION_SETTINGS_CHANGED, NULL, 0);
    InternetSetOption(NULL, INTERNET_OPTION_REFRESH , NULL, 0);
    return bReturn;
}

The usage is very straightforward:

				//set proxy
				const
				char* connection_name="Connection to adsl3";
    SetConnectionOptions(connection_name,"62.81.236.23:80");
//disable proxy 
    DisableConnectionProxy(connection_name);
    

Existing Internet Explorer instances are notified by INTERNET_OPTION_SETTINGS_CHANGED and INTERNET_OPTION_REFRESH

posted @ 2007-01-25 20:13 崛起的程序员 阅读(883) | 评论 (0)编辑 收藏
Hot Spot JVM5中的GC调优

Written by Halatu Hubisi


引言
有JAVA开发经验的朋友们一定碰到过下面的这种情况,那就是自己所开发的应用运行了一段时间后其性能或者响应速度会有明显的降低.这是由多方面的原因造成的即有程序本身的优化问题,也有运行环境问题.此运行环境即包括硬件环境也包括软件环境.大多数人第一个能想到的解决方法是提升硬件的配置而忽略了程序本身的运行环境JVM也提供了比较多的调优选项.本文将重点描述利用JVM的一些选项对GC进行调优.

约定:
1.读者应具备一定JAVA的知识.

2.本文中的JVM选项均以SUN公司发布的HotSpot JVM 5为准(不过大多数的选项在JVM1.3,JVM1.4中也是可用的).

3.以JAVA_HOME下demo/jfc/SwingSet2/SwingSet2.jar为例进行说明.

4.阅读本文需要一些关于GC的知识,可以到附录A中了解这些知识。

关键字:
JVM(java虚拟机),调优,GC(垃圾回收)

JVM GC调优
为了能够将JVM GC的调优能够使用在具体的实践当中,下面将利用若干个例子来说明GC的调优.
例1:Heap size 设置
JVM堆的设置是指java程序运行过程中JVM可以调配使用的内存空间的设置.JVM在启动的时候会自动设置Heap size的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1/4。可以利用JVM提供的-Xmn -Xms -Xmx等选项可进行设置。Heap size 的大小是Young Generation 和Tenured Generaion 之和。
当在JAVA_HOME下demo/jfc/SwingSet2/目录下执行下面的命令。
java -jar -Xmn4m -Xms16m -Xmx16m SwingSet2.jar
系统输出为:
Exception in thread "Image Fetcher 0" java.lang.OutOfMemoryError: Java heap space
Exception in thread "Image Fetcher 3" java.lang.OutOfMemoryError: Java heap space
Exception in thread "Image Fetcher 1" java.lang.OutOfMemoryError: Java heap space
Exception in thread "Image Fetcher 2" java.lang.OutOfMemoryError: Java heap space
除了这些异常信息外,还会发现程序的响应速度变慢了。这说明Heap size 设置偏小,GC占用了更多的时间,而应用分配到的执行时间较少。
提示:在JVM中如果98%的时间是用于GC且可用的Heap size 不足2%的时候将抛出此异常信息。
将上面的命令换成以下命令执行则应用能够正常使用,且未抛出任何异常。
java -jar -Xmn4m -Xms16m -Xmx32m SwingSet2.jar
提示:Heap Size 最大不要超过可用物理内存的80%,一般的要将-Xms和-Xmx选项设置为相同,而-Xmn为1/4的-Xmx值。

例2:Young Generation(-Xmn)的设置
在本例中看一下Young Generation的设置不同将有什么现象发生。
假设将Young generation 的大小设置为4M ,即执行java -jar -verbose:gc -Xmn4m -Xms32m -Xmx32m -XX:+PrintGCDetails SwingSet2.jar,屏幕输出如下(节选)
[GC [DefNew: 3968K->64K(4032K), 0.0923407 secs] 3968K->2025K(32704K), 0.0931870 secs]
[GC [DefNew: 4021K->64K(4032K), 0.0356847 secs] 5983K->2347K(32704K), 0.0365441 secs]
[GC [DefNew: 3995K->39K(4032K), 0.0090603 secs] 6279K->2372K(32704K), 0.0093377 secs]
[GC [DefNew: 3992K->23K(4032K), 0.0057540 secs] 6325K->2356K(32704K), 0.0060290 secs]
[GC [DefNew: 3984K->27K(4032K), 0.0013058 secs] 6317K->2360K(32704K), 0.0015888 secs]
[GC [DefNew: 3981K->59K(4032K), 0.0023307 secs] 6315K->2422K(32704K), 0.0026091 secs]
将程序体制并将Young Generation的大小设置为8M,即执行java -jar -verbose:gc -Xmn8m -Xms32m -Xmx32m -XX:+PrintGCDetails SwingSet2.jar,屏幕输出如下(节选)
[GC [DefNew: 7808K->192K(8000K), 0.1016784 secs] 7808K->2357K(32576K), 0.1022834 secs]
[GC [DefNew: 8000K->70K(8000K), 0.0149659 secs] 10165K->2413K(32576K), 0.0152557 secs]
[GC [DefNew: 7853K->59K(8000K), 0.0069122 secs] 10196K->2403K(32576K), 0.0071843 secs]
[GC [DefNew: 7867K->171K(8000K), 0.0075745 secs] 10211K->2681K(32576K), 0.0078376 secs]
[GC [DefNew: 7970K->192K(8000K), 0.0201353 secs] 10480K->2923K(32576K), 0.0206867 secs]
[GC [DefNew: 7979K->30K(8000K), 0.1787079 secs] 10735K->4824K(32576K), 0.1790065 secs]
那么根据GC输出的信息(这里取第一行)做一下Minor收集的比较。可以看出两次的Minor收集分别在Young generation中找回3904K(3968K->64K)和7616K(7808K->192K)而对于整个jvm则找回1943K(3968K->2025)和5451K(7808K->2357K)。第一种情况下Minor收集了大约50%(1943/3904)的对象,而另外的50%的对象则被移到了tenured generation。在第二中情况下Minor收集了大约72%的对象,只有不到30%的对象被移到了Tenured Generation.这个例子说明此应用在的Young generation 设置为4m时显的偏小。
提示:一般的Young Generation的大小是整个Heap size的1/4。Young generation的minor收集率应一般在70%以上。当然在实际的应用中需要根据具体情况进行调整。

例3:Young Generation对应用响应的影响
还是使用-Xmn4m 和-Xmn8m进行比较,先执行下面的命令

java -jar -verbose:gc -Xmn4m -Xms32m -Xmx32m -XX:+PrintGCDetails -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime SwingSet2.jar
屏幕输出如下(节选)
Application time: 0.5114944 seconds
[GC [DefNew: 3968K->64K(4032K), 0.0823952 secs] 3968K->2023K(32704K), 0.0827626 secs]
Total time for which application threads were stopped: 0.0839428 seconds
Application time: 0.9871271 seconds
[GC [DefNew: 4020K->64K(4032K), 0.0412448 secs] 5979K->2374K(32704K), 0.0415248 secs]
Total time for which application threads were stopped: 0.0464380 seconds
Young Generation 的Minor收集占用的时间可以计算如下:应用线程被中断的总时常/(应用执行总时?L+应用线程被中断的总时常),那么在本例中垃圾收集占用的时?L约为系统的5%~14%。那么当垃圾收集占用的时间的比例越大的时候,系统的响应将越慢。
提示:对于互联网应用系统的响应稍微慢一些,用户是可以接受的,但是对于GUI类型的应用响应速度慢将会给用户带来非常不好的体验。

例4:如何决定Tenured Generation 的大小
分别以-Xmn8m -Xmx32m和-Xmn8m -Xmx64m进行对比,先执行
java -verbose:gc -Xmn8m -Xmx32m-XX:+PririntGCDetails -XX:+PrintGCTimeStamps java类,命令行将提示(只提取了Major收集)

111.042: [GC 111.042: [DefNew: 8128K->8128K(8128K), 0.0000505 secs]111.042: [Tenured: 18154K->2311K(24576K), 0.1290354 secs] 26282K->2311K(32704K), 0.1293306 secs]
122.463: [GC 122.463: [DefNew: 8128K->8128K(8128K), 0.0000560 secs]122.463: [Tenured: 18630K->2366K(24576K), 0.1322560 secs] 26758K->2366K(32704K), 0.1325284 secs]
133.896: [GC 133.897: [DefNew: 8128K->8128K(8128K), 0.0000443 secs]133.897: [Tenured: 18240K->2573K(24576K), 0.1340199 secs] 26368K->2573K(32704K), 0.1343218 secs]
144.112: [GC 144.112: [DefNew: 8128K->8128K(8128K), 0.0000544 secs]144.112: [Tenured: 16564K->2304K(24576K), 0.1246831 secs] 24692K->2304K(32704K), 0.1249602 secs]
再执行java -verbose:gc -Xmn8m -Xmx64m-XX:+PririntGCDetails -XX:+PrintGCTimeStamps java类,命令行将提示(只提取了Major收集)
90.597: [GC 90.597: [DefNew: 8128K->8128K(8128K), 0.0000542 secs]90.597: [Tenured: 49841K->5141K(57344K), 0.2129882 secs] 57969K->5141K(65472K), 0.2133274 secs]
120.899: [GC 120.899: [DefNew: 8128K->8128K(8128K), 0.0000550 secs]120.899: [Tenured: 50384K->2430K(57344K), 0.2216590 secs] 58512K->2430K(65472K), 0.2219384 secs]
153.968: [GC 153.968: [DefNew: 8128K->8128K(8128K), 0.0000511 secs]153.968: [Tenured: 51164K->2309K(57344K), 0.2193906 secs] 59292K->2309K(65472K), 0.2196372 secs]
可以看出在Heap size 为32m的时候系统等候时间约为0.13秒左右,而设置为64m的时候等候时间则增大到0.22秒左右了。但是在32m的时候系统的Major收集间隔为10秒左右,而Heap size 增加到64m的时候为30秒。那么应用在运行的时候是选择32m还是64m呢?如果应用是web类型(即要求有大的吞吐量)的应用则使用64m(即heapsize大一些)的比较好。对于要求实时响应要求较高的场合(例如GUI型的应用)则使用32m比较好一些。
注意:
1。因为在JVM5运行时已经对Heap-size进行了优化,所以在能确定java应用运行时不会超过默认的Heap size的情况下建议不要对这些值进行修改。
2。Heap size的 -Xms -Xmn 设置不要超出物理内存的大小。否则会提示“Error occurred during initialization of VM Could not reserve enough space for object heap”。

例5:如何缩短minor收集的时间
下面比较一下采用-XX:+UseParNewGC选项和不采用它的时候的minor收集将有什么不同。先执行
java -jar -server -verbose:gc -Xmn8m -Xms32m -Xmx32m SwingSet2.jar
系统将输出如下信息(片段〕
[GC 7807K->2641K(32576K), 0.0676654 secs]
[GC 10436K->3108K(32576K), 0.0245328 secs]
[GC 10913K->3176K(32576K), 0.0072865 secs]
[GC 10905K->4097K(32576K), 0.0223928 secs]
之后再执行 java -jar -server -verbose:gc -XX:+UseParNewGC -Xmn8m -Xms32m -Xmx32m SwingSet2.jar
系统将输出如下信息(片段〕
[ParNew 7808K->2656K(32576K), 0.0447687 secs]
[ParNew 10441K->3143K(32576K), 0.0179422 secs]
[ParNew 10951K->3177K(32576K), 0.0031914 secs]
[ParNew 10985K->3867K(32576K), 0.0154991 secs]
很显然使用了-XX:+UseParNewGC选项的minor收集的时间要比不使用的时候优。

例6:如何缩短major收集的时间
下面比较一下采用-XX:+UseConcMarkSweepGC选项和不采用它的时候的major收集将有什么不同。先执行
java -jar -verbose:gc -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -Xmn64m -Xms256m -Xmx256m SwingSet2.jar
系统将输出如下信息(片段〕
[Full GC 22972K->18690K(262080K), 0.2326676 secs]
[Full GC 18690K->18690K(262080K), 0.1701866 secs
之后再执行 java -jar -verbose:gc -XX:+UseParNewGC -Xmn64m -Xms256m -Xmx256m SwingSet2.jar
系统将输出如下信息(片段〕
[Full GC 56048K->18869K(260224K), 0.3104852 secs]
提示:此选项在Heap Size 比较大而且Major收集时间较长的情况下使用更合适。

例7:关于-server选项 在JVM中将运行中的类认定为server-class的时候使用此选项。SUN 的Hot Spot JVM5 如果判断到系统的配置满足如下条件则自动将运行的类认定为server-class,并且会自动设置jvm的选项(当没有手工设置这选项的时候〕而且HOTSPOT JVM5提供了自动调优的功能,他会根据JVM的运行情况进行调整。如果没有特别的需要是不需要太多的人工干预的。SUN形象的称这个机制为“人体工学”(Ergonomics〕。具体可以参考http://java.sun.com/docs/hotspot/gc5.0/ergo5.html
*.具有2个或更多个物理的处理器
*.具有2G或者更多的物理内存
提示:此选项要放在所有选项的前面。例如:java -server 其他选项 java类

附录A:预备知识
JVM中对象的划分及管理

JVM根据运行于其中的对象的生存时间大致的分为3种。并且将这3种不同的对象分别存放在JVM从系统分配到的不同的内存空间。这种对象存放空间的管理方式叫做Generation管理方式。
1。Young Generation:用于存放“早逝”对象(即瞬时对象)。例如:在创建对象时或者调用方法时使用的临时对象或局部变量。
2。Tenured Generation:用于存放“驻留”对象(即较长时间被引用的对象)。往往体现为一个大型程序中的全局对象或长时间被使用的对象。
3。Perm Generation:用于存放“永久”对象。这些对象管理着运行于JVM中的类和方法。

JVM选项的分类

JVM有这么几种选项供使用.
1.供-X选项使用的项目,又称为非标准选项,不同厂商的此类型选项是有所不同的。例如:IBM的JVM用的一些选项在Sun的JVM中就不一定能生效。这种选项的使用方式如下:
java -Xmn16m -Xms64m -Xmx64m java类名
2.供-XX选项使用的项目,这种类型的选项可能要求有对系统信息访问的权限。所以要慎用。这种选项的使用方式如下:
java -XX:MaxHeapFreeRatio=70 -XX:+PrintGCDetails java类名
3.java选项(即在命令行执行java后提示的选项).
java -server -verbose:gc -d64 java类名

垃圾收集分类

在JVM中有两种垃圾方式,一种叫做Minor(次收集),另一种叫做Major(主收集)。其中Minor在Young Generation的空间被对象全部占用后执行,主要是对Young Generation中的对象进行垃圾收集。而Major是针对于整个Heap size的垃圾收集。其中Minor方式的收集经常发生,并且Minor收集所占用的系统时间小。Major方式的垃圾收集则是一种“昂贵”的垃圾收集方式,因为在Major要对整个Heap size进行垃圾收集,这会使得应用停顿的时间变得较长。

GC信息的格式

[GC [<collector>: <starting occupancy1> -> <ending occupancy1>, <pause time1> secs] <starting occupancy3> -> <ending occupancy3>, <pause time3> secs]
<collector> GC为minor收集过程中使用的垃圾收集器起的内部名称.
<starting occupancy1> young generation 在进行垃圾收集前被对象使用的存储空间.
<ending occupancy1> young generation 在进行垃圾收集后被对象使用的存储空间
<pause time1> minor收集使应用暂停的时间长短(秒)
<starting occupancy3> 整个堆(Heap Size)在进行垃圾收集前被对象使用的存储空间
<ending occupancy3> 整个堆(Heap Size)在进行垃圾收集后被对象使用的存储空间
<pause time3> 整个垃圾收集使应用暂停的时间长短(秒),包括major收集使应用暂停的时间(如果发生了major收集).
GC信息的选项
-XX:+PrintGCDetails 显示GC的详细信息
-XX:+PrintGCApplicationConcurrentTime 打印应用执行的时间
-XX:+PrintGCApplicationStoppedTime 打印应用被暂停的时间
提示:1.":"后的"+"号表示开启此选项,如果是"-"号那么表示关闭此选项。
     2.在不同的选项和不同的收集方式和类型下输出的格式会有所不同。

附录B:HotSpot JVM 选项
请参考JavaTM HotSpot VM Options

附录C:其他资源
http://java.sun.com/docs/hotspot/gc5.0/gc_tuning_5.html
http://java.sun.com/docs/hotspot/gc5.0/ergo5.html

posted @ 2006-11-28 19:23 崛起的程序员 阅读(436) | 评论 (0)编辑 收藏
清晨的时候,
在近春园曲折的小路上走,
看见一个老人坐在湖边悠闲地垂钓
依稀听见湿漉漉的笛声
婉约进树林深处,
老人专注的神情似乎能
漾起水中的波影,
然后目光被长长的眉须剪断,
扑闪在晨雾中捕捉着空气.
这是怎样一种美妙啊!
我在他身边坐下,静静等待
鱼儿的到来.半个小时过去了,
我的心情开始烦躁,
可老人的微笑一如既往.
"你们二十几岁的年轻人啊,
心就像一个玻璃杯,
透明,盛满了阳光."
老人说话跟垂钓一样,安详.
和仙风道骨的老人话别后,
我决定从粉绿的湖边继续往前走,
去探寻校园极致的景色,
享受年轻的心情,
呵呵,像玻璃杯……                                   
posted @ 2006-11-19 19:57 崛起的程序员 阅读(290) | 评论 (0)编辑 收藏
String strs = request.getParameter("yourstrs");
byte b[] = strs.getBytes("ISO-8859-1");
strs = new String(b);








简洁写法:


String convert = new String(request.getParmater("inputname").trim().getBytes("ISO8859_1"), "GBK");
posted @ 2006-11-10 09:53 崛起的程序员 阅读(239) | 评论 (0)编辑 收藏
关于tomcat4.0配置digest认证注意事项:
MemoryRealm作为事例
在server.xml中需要配置 红色字体为注意事项
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                 debug="0" resourceName="UserDatabase" digest="MD5"/>

密码转换成密文的加密格式为:
{username}:{realm}:{cleartext-password}.

到bin/目录下找到digest.bat 文件执行如下命令得到密文
digest -a md5 admin:realm:admin

其中admin分别用户和密码,realm为所配置域名
admin:realm:admin:ae7cdd2406201487bcce77064b5fe10c

红色部分为加密后的密码。

完:
作者: Liming

posted @ 2006-11-09 04:30 崛起的程序员 阅读(2005) | 评论 (0)编辑 收藏
这是一篇很有趣的文档,所以摘要一下,其实类似阅读笔记,好像是3/25发布的:
不知怎么翻译Sweet Spots,难道翻译为甜处、甜头、蜜点、蜜穴?

本文基于对以下人的采访(最后两位的看法独到还是自己看吧!):
JSF             Jacob Hookom
RIFE            Geert Bevin
Seam            Gavin King
Spring MVC      Rob Harrop
Spring Web Flow Rob Harrop and Keith Donald
Stripes         Tim Fennell
Struts Action 1 Don Brown
Tapestry        Howard Lewis Ship
Trails          Chris Nelson
WebWork         Patrick Lightbody
Wicket          Eelco Hillenius


JSF(Jacob Hookom)

1、你认为你的framework的"甜点"在哪里?他最适合哪种类型的项目?
当你希望浏览器程序像桌面程序一样工作的时候,你可以遵循标准并获得大量第三方支持。它致力于降低复杂度。它允许你不与view和特定的action、参数传递、状态传递、渲染打交道就可以进行高质量的开发,不管是否使用工具。

2、它不适合于什么样的场景?在这些场景你推荐什么fremework?它是哪个?
它不适合大规模的、只读(其实指读为主)的网站。在这种情况推荐Struts,因为知识库丰富(应该指文档和用户群)。

3、在下面提到的framework中,你试验过他们么?如果试验过,你比较喜欢哪个?你不喜欢哪个?
Seam:
优点:非常简单直接
缺点:对于大项目过于简单;没有模块化开发的好例子
Struts:
优点:巨大的文档和用户群;跟着它没错
缺点:状态/行为的分离过于教条化
WebWork:
优点:比Struts易于使用
缺点:复杂的UI难于维护,UI代码过于复杂(JSF作者对action
Framework都攻击这一点)
Tapestry:
优点:概念新颖;可以应付复杂的UI
缺点:对于一个组件化(JSF主要竞争对手),它依然依附于page/action的概念

4、你的framework的未来会怎样?对于用户开发会有什么方便使用的变化?你会原生支持Ajax么?你们计划支持它了么?
他认为JSF这个标准下这些应该有第三方提供。JSF(2.0)会提供"Partial Faces Request",它是Ajax实现。JSF也会增强annotation组建编程。

5、有对你们的framework的传言需要澄清么?如果有,是哪个?很多JSF书都拿Struts作为对比。他认为这不能体现JSF的特点。他认为Struts和WebWork能做到的JSF也能做到。

6、你对Ruby on Rails的看法如何?
它与WebWork一样好用,它的CoC(Convention over Configration)和脚手架非常好用。他认为CoC可以被应用在任何framework,他认为这是RoR最大的优点。他还认为RoR会走上其它framework的路(复杂性),因为人们需要自己的扩展。

RIFE(Geert Bevin)

1、你认为你的framework的"甜点"在哪里?他最适合哪种类型的项目?
你可以付出10%的工作量,得到其它framework的90%的......,它是一个full-stack framework(如RoR)。它吸收了成熟的分层框架的架构,并将共同的优点汇集在一起。提供了web continuation,POJO驱动的CRUD生成,可扩展的基于组建的架构,无session的状态控制,关注REST作为API,双向无逻辑模版引擎,集成了内容控制框架(CMS?)。每个层次的组建提供了可复用性(AOP,site,sub-site,page,widget,portlet等)。适合于团队快速开发公共Web项目,适合喜欢开发可复用组件的人。

2、它不适合于什么样的场景?在这些场景你推荐什么fremework?它是哪个?
团队中的每个人都有其它framework的知识,难于培训他们。开发状态相关的服务器端Web组件,而不是用RIA或Ajax去实现。第三方支持很重要的情况下(可怜RIFE用户群还不大)。他推荐这种情况下使用JSF。或者在XML为主要发布形式的情况下,推荐Cocoon。

3、在下面提到的framework中,你试验过他们么?如果试验过,你比较喜欢哪个?你不喜欢哪个?
他试验过WebWork,JSF,Wicket。他喜欢WebWork的简单,但是不喜欢它的模版方式(tag的template,应该),它也不提供组件封装。他认为JSF的工具支持非常吸引人。Wicket的纯Java实现很不错,可惜XML配置很不爽。

4、你的framework的未来会怎样?对于用户开发会有什么方便使用的变化?你会原生支持Ajax么?你们计划支持它了么?
关于Ajax,RIFE刚刚集成了DWR,而且选定以后也使用这个。集成Dojo,Scriptaculous,Prototype都很容易集成进来。

5、有对你们的framework的传言需要澄清么?如果有,是哪个?这些错误理念:
  1)、RIFE的XML配置繁琐
  2)、RIFE是continuations server
  3)、RIFE重新造轮子没有提供新鲜东西
  4)、RIFE的模版语法很蹩脚过于简单和业余
  5)、RIFE是基于request的framework
  6)、RIFE的功能太多,学习曲线陡峭

6、你对Ruby on Rails的看法如何?
RoR对Java社区的冲击非常棒,元编成也得到了信任。RoR没什么特殊之处,也没有从Ruby语言获益很多。
我讨厌:它的模版。Partials(RoR中的组件)。URL的分散处理。Active Record提供了从数据库schema而来的DSL,但是却不是从domain model而来。没有l10n和i18n支持。手动状态转换。不能在JVM运行(......)。实际上脚手架生成了实际代码。Ruby缺少工具和IDE。

Seam(Gavin King)

1、你认为你的framework的"甜点"在哪里?他最适合哪种类型的项目?
拥有丰富用户交互体验的应用。方便实现多窗口的操作,回退的支持,单窗口多工作区,无状态浏览。对商务流程(BPM)的集成是独一无二的。Seam方便使用数据驱动的ORM。遵循JSF和EJB3,多任务支持(多窗口/多工作区),BPM的领先解决方案。

2、它不适合于什么样的场景?在这些场景你推荐什么fremework?它是哪个?
不适合只是将数据从数据库显示到网页的应用,这时应该使用PHP或RoR。不适合需要设计特别的HTML组件的情况,此时应该选用Tapestry或Wicket。还在使用JDK1.4的人们。还有那些喜欢Struts的人(嘿嘿,够狠)。

3、在下面提到的framework中,你试验过他们么?如果试验过,你比较喜欢哪个?你不喜欢哪个?
JSF:喜欢他的事件/交互模型。喜欢他的EL和模型绑定。不喜欢那么多XML(为什么没有annotation)。创建自己的controls太难了。
Tapestry:非常好。form验证是它的杀手锏!模版方式很有创意。不过非基于POJO的组件模型则让我对它失去兴趣。
RIFE:这个东西很怪,但是有创业也有趣。我想进一步学习。如果学习先要自费武功:D
Struts:这个东西的模型view绑定太复杂了。东西已经过时了。
WebWork:比Struts好一点,不过也过时了。XWork曾经是个很好的实现,不过现在也过时了。

4、你的framework的未来会怎样?对于用户开发会有什么方便使用的变化?你会原生支持Ajax么?你们计划支持它了么?
Portal支持。远程框架Seam Remoting Framework(Ajax)。模版消息的工具支持。以后还要集成ESB,计划引擎和异步支持。

5、有对你们的framework的传言需要澄清么?如果有,是哪个?
这些都不是真的:JSF不能处理GET requests。JSF post后无法redirect。JSF不能与REST共存。

6、你对Ruby on Rails的看法如何?
它是PHP的很好替代品。如果它有一个正经一点的持久化层它就可以和Java竞争了。

Spring MVC(Rob Harrop)和Spring Web Flow(Rob Harrop and Keith Donald)

1、你认为你的framework的"甜点"在哪里?他最适合哪种类型的项目?
Spring MVC:
稳定可扩展,支持了i18n、文件上传、异常处理,这些稳定的支持给开发者坚实的工作基础。是最佳实践,告诉你怎么做是最好的。与Spring集成,领先的IoC远生支持。支持,Spring社区活跃和庞大。Struts开发者可以平滑过渡。适合多种项目,可选的多种result类型。
Spring Web Flow:内置任务处理引擎,支持线性处理过程中的持续状态。抽象,减少开发的关注点。适合多种项目类型,插件支持Spring MVC、Struts、JSF等。

2、它不适合于什么样的场景?在这些场景你推荐什么fremework?它是哪个?
Spring MVC:不适合需要组件化开发的场景。它是一个request驱动的MVC。那些场景推荐JSF或Tapestry。
Spring Web Flow:处理线性页面流,不适合一般的"自由浏览"。当然Spring Web Flow可以与request驱动或者组件驱动共存。

3、在下面提到的framework中,你试验过他们么?如果试验过,你比较喜欢哪个?你不喜欢哪个?
Spring框架支持Struts和JSF集成。

4、你的framework的未来会怎样?对于用户开发会有什么方便使用的变化?你会原生支持Ajax么?你们计划支持它了么?
Spring MVC:简化JSP标签。更多的MVC配置schema。CoC风格的默认控制器、URL影射、view,学习Rails和Stripes的优点。增强数据绑定和验证(支持范型绑定)。Portlet支持。Spring也要接受Ajax,使用DWR库。
Spring Web Flow:一大堆,关心的可以自己看......

5、有对你们的framework的传言需要澄清么?如果有,是哪个?
Spring MVC难于配置。在Spring 2.0,将会改善,可以使用自己定义的基于schema的配置。

6、你对Ruby on Rails的看法如何?
Spring MVC:RoR非常有趣。不过现在就拿出来用还有点幼稚。这里举了个例子,关于变量的复数形式的处理,RoR会使用这样的CoC风格来处理变量list,而Spring MVC也实验了种种风格,但是受到的结果却很差。人们认为英语的复数很古怪,没有一定的规则,所以会带来混乱,如(person -> people)。所以Spring ...

Stripes(Tim Fennell)

1、你认为你的framework的“甜点”在哪里?他最适合哪种类型的项目?
与Spring MVC、WebWork等相同。它提供高质量action驱动的框架的同时,尽量简化配置,增进开发效率。Stripes适合复杂的数据交互的场合。这种情况下绑定验证的强项就完全体现出来了,能够很好的处理form和map转换等。

2、它不适合于什么样的场景?在这些场景你推荐什么fremework?它是哪个?
所有的action驱动的framework都适合用户在非Ajax驱动的情况下在一个页面进行松关联(loosely
related)和无状态交互的情况。适合每次都刷新的页面。管理多窗口间持续状态的应用会比较麻烦,此时应该选择JSF。不过我认为90%以上的Web程序都是前者,JSF只适合剩下的那9%,AJAX对于管理无状态UI更加适合。客户端不需要AJAX,则可以看看Wicket,它更加简单。

3、在下面提到的framework中,你试验过他们么?如果试验过,你比较喜欢哪个?你不喜欢哪个?
用过Struts、WebWork、Spring MVC。其中Struts做过商业项目,不过这个东西带来的麻烦远比带来的效率提升要多。它认为这些MVC都有三个缺点:暴露了过多的复杂性给可发者。没有提供足够的开发便利性,没有提供足够多的错误和提示信息,也没有date格式化等小的便利(其实有)。稳当太差。

4、你的framework的未来会怎样?对于用户开发会有什么方便使用的变化?你会原生支持Ajax么?你们计划支持它了么?
1.3要加入Inteceptor,实现AOP功能。验证系统要加强。支持Ajax。我还在寻找一个好的Ajax/javascript库。

5、有对你们的framework的传言需要澄清么?如果有,是哪个?
这些观点:1、Stripes使用了annotation代替XML,只是换汤不换药:由于元数据更接近代码,所以修改默认的配置非常方便,不像XML那样复杂,这是实质的变化。2、Annotation意味着你只能有一套配置:我认为90%的action都有自己的一套配置!Stripes会根据继承关系寻找Annotations,子类的annotation会覆盖父类的,因为像validation都是可以继承的,如果特别需要还可以覆盖。这样很合理。在1.3中允许validations基于UI事件进行。它向后兼容,不需要可以不用。

6、你对Ruby on Rails的看法如何?
我认为Java社区有很多可以从RoR学习的地方。Stripes学习了RoR的前端部分,开发者可以减少配置量。但是RoR的RHTML让我想到了以前的JSP中混乱的scriptlet。而后面的ActiveRecord是一个很好的理念,实现的也很好。ActiveRecord比Hibernate等复杂的ORM工具要容易理解,因为这样的特点RoR才引起了这么大的波澜。

Struts Action 1(Don Brown)

1、你认为你的framework的“甜点”在哪里?他最适合哪种类型的项目?
文档和用户基础,书籍和背后的支持。容易雇到人(也容易找工作)。虽然其他项目的理念比这个要先进,但是这些不算什么。实际上,Web层是很容易也很直接的。

2、它不适合于什么样的场景?在这些场景你推荐什么fremework?它是哪个?
如果你需要portlets或者复杂的页面(显示很多东西),那么Struts要么无法工作要么太枯燥。这种情况你需要一个基于组件的framework,如JSF、Tapestry/Wicket。

3、在下面提到的framework中,你试验过他们么?如果试验过,你比较喜欢哪个?你不喜欢哪个?
这些我基本都试验过,他们每个都工作的很不错。

4、你的framework的未来会怎样?对于用户开发会有什么方便使用的变化?你会原生支持Ajax么?你们计划支持它了么?
Struts Action2基于WebWork2,很快会开始。现在已经支持Ajax了,我们在寻找更加容易的开发方式,JSF支持(Struts Shale),continuation支持,还有支持更多的脚本语言(BSF扩展脚本撰写Action)。

5、有对你们的framework的传言需要澄清么?如果有,是哪个?
Struts太过时了,而且也不酷,难于使用。但是你可以自己修改或者扩展它。我认为团队对于你的限制远大于framework对你的限制。

6、你对Ruby on Rails的看法如何?
不需要D&D工具,旨在帮助开发人员提高开发效率的好例子。我们在Action2中将学习它的先进理念。

Tapestry(Howard Lewis Ship)

1、你认为你的framework的“甜点”在哪里?他最适合哪种类型的项目?
我想Tapestry对于中等规模或者大规模的应用会带来很多好处(甚至你可以在单页面的应用程序中获得好处)。这里有允许你创建新的组件的良好工具。Tapestry不关心数据从哪里来,很多“项目类型”都基于切面(aspect)(如CRUD vs. RSS feed vs. etc.)。我认为Tapestry非常容易与IoC集成(HiveMind或与Spring),方便进行测试。

2、它不适合于什么样的场景?在这些场景你推荐什么fremework?它是哪个?
我在其它Java framework中没有找到到强于Tapestry的优点。但是对于RoR,我自己没有使用过使用,很难说RoR中的项目应该是什么样子。我没有仔细对比过RIFE,它看起来受了RoR影响,尤其是类似ActiveRecord的数据访问层。但是如果你的应用需要特定的URL格式,那么在Tapestry中奋战胜算不大。

3、在下面提到的framework中,你试验过他们么?如果试验过,你比较喜欢哪个?你不喜欢哪个?
在这两年来我没怎么尝试过Tapestry以外的东西。我没怎么学习RoR,因为时间太有限了。

4、你的framework的未来会怎样?对于用户开发会有什么方便使用的变化?你会原生支持Ajax么?你们计划支持它了么?
Tapestry 4.0有很好的Ajax支持,通过Tacos库。而Tapestry4.1还要进一步强化这方面的支持。
Tapestry 5.0提供了明显的改进:没有abstract类(Tapestry的怪癖:)。没有强迫的继承关系。对属性进行annotation而不是方法。没有XML,只有模版和annotaions。只能类装载,自动寻找类的变化。最小化API,超越annotaion。面向方面(Aspect-oriented)模块构造,使用mix-ins。

5、有对你们的framework的传言需要澄清么?如果有,是哪个?
Tapestry3.0还不容易测试,4.0改善了一些。Tapestry只是个人秀;实际上我们有很多活跃的贡献者。Tapestry的学习曲线非场陡峭。它只有漂亮的模版实现;实际上Tapestry的特点在于状态管理(允许对象存储状态,而不是多线程的单例来管理requests之间的游离和持久状态)

6、你对Ruby on Rails的看法如何?
很有影响力。但是模版的实现非常丑陋。我听到了很多意见,关于RoR的优缺点。基于我的基本理解,这些观念对Tapestry4产生了影响(它对Tapestry 5影响更深)。
RoR意味着限制了你的选择:如果你选择RoR那么就要尊旬它的实践(CoC..),看起来你的钱会花的恨值。这些类似Microsoft的哲学。而Java更崇尚给你更宽松的选择,不限定你使用的工具,但是暧昧的说这需要你对你的工具理解更深。不仅对Tapestry,还对于JEE、Spring这写entire stack的框架,需要从RoR学习,不仅提供工具,还需要提供整套的解决方案。

Trails(Chris Nelson)

1、你认为你的framework的“甜点”在哪里?他最适合哪种类型的项目?
Trails的应用程序只需要Web界面和持久化的domain model就可以了。Trails给你的domain
model快速的提供一个界面,除了POJO自己不需要附加的代码。Trails允许你修改界面的外观和行为,包括验证、i18n、安全。这些都不需要java代码生成,不喜欢代码生成的人应该感觉很舒适。

2、它不适合于什么样的场景?在这些场景你推荐什么fremework?它是哪个?
Trails讲究够用就好。它允许你快速交付,问问你的客户:“这样够好么?”。这会改变你的工作流程,当然这不是可以覆盖所有需求的解决方案。当UI需求很高,Trails没有优势。我认为Trails适合于混合的应用,对于管理员他们只需要够用就好,那么就可以使用Trails。其它的部分我们可以订制开发,我们在使用Tapestry、Hibernate、Spring来实现这些部分,Trails正是基于它们。对于非交互的应用,Trails也不适合,如报表应用,可以考虑Eclipse BIRT。

3、在下面提到的framework中,你试验过他们么?如果试验过,你比较喜欢哪个?你不喜欢哪个?
我用Struts很多。它曾经是伟大的framework。主要的缺陷是它不能自动帮定数据到domain model。我也研究过JSF,它比Struts强,但是自定义组建非常难。Tapestry非常便于自定义组建,尤其对于建立高阶组件(有其它组件组成的)非常方便,Trails正在使用它。

4、你的framework的未来会怎样?对于用户开发会有什么方便使用的变化?你会原生支持Ajax么?你们计划支持它了么?
对于Trails来说我们站在巨人的肩膀上。Tapestry在ajax功能作了很多努力,所以Trails也不难与其共舞。但是我们需要创建更多的例子来演示这些。我们也致力于让Trails容易介入到已经进行的项目中。以后Trails还要加入基于实例的安全(instance-based security)(目前正在使用基于角色的role-based),还有method invocation。

5、有对你们的framework的传言需要澄清么?如果有,是哪个?
Trails是对RoR的移植。Trails的名字来自Rails。它是基于Rails的理念,但不是对它的移植。

6、你对Ruby on Rails的看法如何?
我认为我们有很多需要从RoR学习的地方,那将帮助我们享受开发Web程序的惬意。
posted @ 2006-08-18 00:02 崛起的程序员 阅读(187) | 评论 (0)编辑 收藏
程序员创业三关


当人们还在浩叹第一次网络泡沫破灭的时候,互联网已经悄悄迎来了它的第二个春天;从某种意义上看,甚至已经进入了夏天——热烈但不乏浮躁、兴奋但失之肤浅。某位投资人声称今年总共有三十亿美金资本进入中国,言下之意,大家都可以甩开膀子大干快上,登陆纳市不好说,搞笔钱进来花差花差多半是没问题的。

另一方面,程序员创业,在中国乃至国外,都不是什么新鲜事儿。我们是那么一群聪明、优秀的家伙,大脑发达,点子就像啤酒的泡沫,扑腾扑腾直往外冒,天生我才,有什么做不到的呢?遥想比尔盖茨当年,西装革履,谈笑间,多少豪杰灰飞烟灭……

于是我看到,无数公司成立了,眼看他雄心勃勃,眼看他一败涂地。成败固然不足以论英雄,然而英雄却不得不面对可能的成败。可惜的是,程序员朋友们在创业的时候,往往没有做好充分准备。据一些资料显示,百分之七十五的新创公司,会在两、三年内倒闭。笔者也曾经见过许多程序员创业失败的个案;成功或不成功,有很多因素制约;对于立志创业的程序员,至少应该突破三关。

第一是模式关。你的创业计划,也许起源于灵机一动。可惜的是,好点子并不能保证你创业成功。我相信多数程序员的点子,尚不至于低级到靠软色情、盗版、恶性SEO等等下作手段去骗取广告费的地步,但怎么从点子变成盈利模式,却是让很多创业者迷惑的事情。在一些业界聚会上,总听到类似“只要有流量,总有办法赚钱”之类的说法,在2000年持同样言论的创业者,多数已经成为失败的先烈,因为他们始终没明白,赚现钱的生意才是好生意。三大门户成功的要点,在于他们想出办法,把流量转化为盈利模式。缺乏有效的模式,流量只是成本。确定有效经营模式、组建有力创业团队,是首要任务。

第二是管理关。从程序员变成管理者,是艰难的过程。你得明白两件事:一、管理和写程序一样,是门科学;二、人和计算机不一样,人有感情、会出错。协调沟通能力,是程序员创业必备的素质。曾经眼见一些项目经理,和手下技术人员通过邮件争辩不休,甚至发展到在Blog上互相嘲讽,这样的管理,可谓彻底失败。另一个极端是,和手下称兄道弟、一团和气,工作被感情所左右,酒肉害了朋友。管理有那么难吗?我看未必。只做对公司有利的事,就是根本原则。在和你的手下打交道时,请三思:我这么做,对公司有好处吗?对事业有好处吗?如果答案是否定的,那你需要另一个解决方案。

第三是坚持关。一位多次创业不成功的朋友告诉我,他总结了一条“三年定律”,即任何事如果不能坚持三年,则一定失败。诚哉斯言!另一位朋友说,中国人相信机会,西方人相信方向和时间,虽有些偏激,却也不乏道理。许多程序员都属于思维活跃、点子特多的一类人,当有新鲜的想法出来时,他们倾向于放弃或冷落手上正在执行的计划。点子复点子,点子何其多,每天新点子,万事成蹉跎。西谚有云,双鸟在林不如一鸟在手;吃到嘴里的鸭子才是好鸭子,湖里那只鸭子看起来比较肥?也许吧,不过,吃了这只鸭子再去涉水抓那只,是不是更有把握呢?

文短意深,未尽之处不及一一道来。奉上忠言数句,与程序员朋友们共勉:你永远不是最聪明的人;手下比你强是好事;创业不怕起步晚,只怕起个不停。
posted @ 2006-08-15 14:49 崛起的程序员 阅读(276) | 评论 (0)编辑 收藏
在前些日子举办的2005年中国国际广播电视信息网络展览会上,瑞福特集团推出了“视讯梦网”的互动视讯运营平台,在网络电视正在火热的时候,“视讯梦网”的出现引起了很多人的关注。
推出“视讯梦网”品牌的互联天下将提供代收费服务,成为连接各SP与用户之间的纽带;并立志成为优秀的客户聚集者,通过统一的服务品牌和平台品牌建立并维持商业价值链上的多种客户关系,集中业务流量,形成庞大的用户规模。从这个商业模式上来看,很明显有模仿移动梦网的意味,从而使得移动梦网的模仿者又多了一个——自从移动梦网为中国移动带来巨大的成功以来,“联通在线”、“互联星空”、“天天在线”等都可以看到它的影子。有趣的是,移动梦网实际上也是模仿日本NTT DoCoMo的i-mode模式,模仿一定能够带来成功吗?

即便是中国成功的企业家在谈到创新和模仿时也有不同的看法,在不久前的一次论坛上,远大空调的张跃和泰康人寿的陈东升对此的观点截然不同,陈东升有一个著名的观点:"创新就是率先模仿"。他两次创业,"模仿"都起了很重要的作用。而张跃对“模仿”却不以为然,远大走的就完全是一条创新的路子。张跃总是时时不忘创新,认为这才是企业的生命力所在。

两个人的观点虽然不同,但他们达到的结果却很相似——他们都成功了。有人把创新分成不同的层次,原始性创新可算是“视讯梦网”的尖端、中间是创意型的技术改造、而处于金字塔底座的是大量的模仿性创新。张跃可能更重视前两个层次的创新,而陈东升更重视低层的创新。事实上模仿性创新可以发挥很重要的作用,日本企业其实就是这方面的典范,而在“学习日本经验”的运动中施乐公司率先实行的标杆管理,里面也有模仿性创新的因素。

所以,单纯地谈模仿是否能成功恐怕并没有多大意义,模仿移动梦网的瑞福特能否成功关键是看模仿中有没有创新,移动梦网的成功也是在于模仿中有创新。陈东升走遍各国探访先进的保险公司,其实是有选择的学习,集众家之长为己所用,也是一种创新。

模仿中最重要的就是不要失去自我,如果只是一味的模仿,没有创新,那最终的结果可能就会像邯郸学步的燕人一样,没学会别人,自己也不会走路了,只有爬着回去了。

Re:企业战略:创新还是模仿?

日本人的创新能力强吗?日本的科技我想在世界上应该有一定地位的.小日本的企业坚持一点:模仿+改良=创新.你有能力创新吗??没有或者不值得的话,那就请模仿吧!等把人家的技术的消化了再去创新!我想管理思想发展到今天我们都提倡学习型组织,迟早要到思考型组织,接下来就是创新型组织.创新是永远的主题,是大创新还是小创新?不同阶段的企业其策略重点也是不一样的,总的来说中国的企业大部分还是应该走模仿的道路,因为制造工厂说白了就是成本打天下!我们慈溪有个非常了不起的企业叫做"卓力"做电器的,大部分是出口生意.他的技术永远不会落后于他人,因为他会把全世界最新的产品买过来模仿.他的核心竞争力就是不断提高的比别人快一步的模仿力,这是一种能力!所以模仿和创新不是矛盾,也许会有一个境界的问题,但是我认为侧重点应该在模仿上,因为我们的发展阶段跟人家不一样.小企业多.连国家都说我们要抓大放小,小的一放,任何技术资源优势都没有,国家又不管,怎么办,唯一的优势就是流动----人才的流动,更重要的是信息的流动!
posted @ 2006-08-07 14:41 崛起的程序员 阅读(279) | 评论 (0)编辑 收藏
     康盛世纪首席执行官戴志康(25岁), PC POP首席执行官李想(25岁),MySee首席执行官邓迪(25岁)、总裁高燃(25岁)、技术主管张鹤翔(24岁),163888翻唱网首席执行官郑立(24岁),MaJoy总裁茅侃侃(23岁),非常在线首席执行官赵宁(23岁),“创智赢家”畅网科技首席技术官陈曦(24岁)……当这样一批二十多岁的精英集中出现在人们的视线中时,不由得让人惊呼,“80后”财富新生代正在崛起。

    似乎是数位少年作家的崭露头角,让人们开始注目“80后”。而当在人们眼中的“80后”还是代表着浮躁和反叛时,这个词却在悄悄地与财富画上连接符号,一批极为年轻的财富新贵的出现似乎是一夜之间,同时颠覆了人们心目中 企业家 的传统印象。
   
   也正由于他们的年纪之轻和拥有的财富之重,这段时间来,他们不断受到媒体和公众的追捧。

    看看上述这些代表人物从事的行业就知道,这批“80后”财富新贵的共同特点是夹了一本叫“互联网”的大课本匆匆上路了。互联网这座虚拟时空里的“大学”,在很多层面上反叛着以一间讲堂、一套书本、一种方言、一个系统为依托的传统 教育

    在李想看来,这正是“80后”的一代人所具有的互联网精神:完全不顾忌那些别人认为是必须去做的传统,破除墨守成规,只做最正确的事。

    和李想说话,你会发现他始终思路清晰、方向明确,回答每个问题不超过3句话,也挑不出一点废话。“我的方向和目标很明确,一开始我就知道我的事业方向就是互联网,然后我会确定每一个阶段的目标,一个一个去实现,所以我不会受到诱惑去烧钱,也不会因为碰到困难就放弃。”

    “像李想、戴志康这批‘80后’创业家的优势就在于他们创新能力很强,敢于尝试,想了就做。不像70年代的人干什么都思前想后,以致错过很多机会。”中华英才网总裁、著名人力资源专家张建国说:“但他们的问题是自我意识太强,而管理企业是要靠组织体系和团队协作的,因此,靠一个好的创意可以成就一个企业,但企业做大以后怎么管理好,这可能是‘80后’企业家应该注意的问题。”

    和张建国基本同龄的戴光对于儿子戴志康也有类似的担心,“你可以凭一项技术创业,但公司做大以后,如何从一个技术天才的角色中脱离出来,完成一个管理者的转型,是一个大问题”。

这些担心已经是李想们正在思考并一直着力解决的问题。

    “2003年时我的性格还是典型的‘80后’,太自我,不太考虑他人的感受。这给了我很多教训,让我慢慢改变,慢慢学会跳出来观察自己,学会了解每一个员工的性格和想法。”李想说,“而且我开始尝试用心跟别人沟通,而不是用脑子。用脑子太累,大家互相猜来猜去,我喜欢直截了当,怎么想的就怎么说。后来我发现其实大部分人都喜欢这种沟通方式,包括那些很有名的CEO们。所以规则不是不可以改写的,关键是你去不去做。”

    这正是“80后”财富新贵的共同特点,他们拥有与年龄不相符的成熟和老练。

    本期,我们走近“80后”财富新贵中的两位:李想和戴志康,也走访了其中一位的父亲,希望能从父辈的教育方式上对这批财富新贵的成功缘由有所挖掘。

    一个没有上过大学,一个做了大学的“半路逃兵”,李想和戴志康都没有受过完整的高等教育,但他们却创造了奇迹。

    这批没有接受正规高等教育却创业成功的年轻企业家的崛起,再次引发对现行高等教育的争议,中国高校究竟该培养什么样人才的问题又一次被提起。

    在关注他们成长的同时,我们也期待他们在今后的路上走得更加稳健和长久。

    见到25岁的PCPOP网首席执行官李想时,他的公司一片忙乱,所有的会议室都被腾出来接待面试者,李想准备继续扩大已有的100多人的团队,而他们2004年还显得空荡的公司变得拥挤不堪。李想说他已经在中关村一个新建的写字楼里租下了比现在大一倍的“新家”,马上就要搬家了。

    作为中国第三大中文 IT 专业网站——PCPOP网的创始人,李想持有公司50%以上的股份,身家在1亿元以上,网站去年的营业额收入近两千万元,利润一千万元,取20倍的市盈率,市场价值两亿元。但李想的野心决不仅限于此,“今年的营业额要达到去年的2.5倍,明年要超过1亿元,然后2008年达到一个上市的标准,这是我们未来几年发展的一个方向。”

    这个野心勃勃的年轻人8年前还是一个上不起网的高中生,当时一小时8元的上网费外加一小时4元的电话费对于一个孩子来说实在昂贵,后来上网费用降到只需一小时1元,他就是那时候开始接触互联网的,“我一碰互联网马上就迷上了,当时很单纯,就是因为可以在网上跟志同道合的人交流、学习知识”。
李想很快就开始做自己的IT资讯网站,网站的访问量很快就达到五六千人。当达到一万人时,就有
广告 商开始找他,要做广告,访问量一千次10元,‘我当时想,闲着也是闲着,就给他挂上了”。

    没有在意的李想两个月后收到了广告商6000元的付款,“我当时也很吃惊,这比我父母一个月加起来挣的还多。”不经意间挖到自己的第一桶金,本来心思就不在读书上的李想终于在高三毕业时作出了一个大胆决定:放弃考大学,自己创业。

    与李想同岁、同样靠互联网起家的康盛世纪科技有限公司CEO戴志康却是个从小和电脑泡到大的人。戴志康出生在大庆一个高级知识分子家庭,父亲戴光是大庆石油学院的博士后。还在上小学的戴志康没事就去鼓捣家里的电脑,从286到586,家里的电脑一次次为他升级,戴志康编程的本事也越来越高。

    2000年,戴志康竟然经过高考前一个月的突击准备考进一所知名大学的 通信 工程专业,这让老师同学大跌眼镜。然而进入大学一年级后,戴志康便很少在学校,而是自己在外创业。“我当时在外面租了个月租300块钱的房子,一天差不多15个小时都泡在电脑前面,闷头开发Discuz!”。

    Discuz!是一套能自动生成社区的软件,能让不懂技术的人也能管理自己设计的网络社区。刚开始研发时,戴志康并没打算以此创业,“我想毕业后找个好工作,所以开始研发Discuz!时只是兴趣,就想证明自己的能力。但在研发过程中,当我产生以此挣钱的念头时,就不再想找工作了。”

    现在,Discuz!已经是社区软件领域的老大,戴志康也开始进行公司的转型,把产品免费供应给客户,转而从为客户提供服务中获取收入。这个仍习惯于吃学校食堂、穿地摊衣服却开着银灰色 宝马 的年轻人,谈起自己公司的发展方向头头是道,老练得让人有些超出想像。

    宽松的家庭教育造就了创业素质

    戴志康的办公室立着一个空空的书架,“我不喜欢看书,我认为书是用来查的,不是用来翻的,我讨厌被动地接收信息,我喜欢从生活中掘取信息”。戴志康的学习与常人理解的不同,他既不靠学校也不靠书本,而是从生活的细节中学习。

    经常和朋友聊天到凌晨的戴志康,从中“偷学”到不少东西。

“另外,我喜欢独立思考。只有独立的人才会坚强。当遇到看似解决不了的困难时,不独立的人马上想到的是寻求帮助。但在一个商业社会里如果没有利益的关联,没有人会帮你,你获得了别人的帮助也就意味着自己商业利益的损失。”戴志康说,“再有,勇气和执著是创业必须的基本素质,而这两点独立的人很容易获得。”

    戴志康把这种独立思考能力的养成归功于家庭教育,“我父母的教育方式比较西化,给我很大的空间。当然,父母同时也要我自己负责任,什么都靠自己,不到万不得已他们不会伸手帮忙。而且我父母都是老师,每天和年轻人接触,心态也比较年轻。”

    戴志康的父亲戴光在向笔者总结他的教育方式时说,对孩子的培养不能限制得太死,要给孩子建议而不是命令,孩子觉得对的要采纳,要让他自己思考,不能给现成的答案,这样孩子才能有自信、有想法,不是一块只会跟着别人的木头。

    “我觉得戴志康之所以能取得一些成就,是他发挥了自己的特长,大学里就有了自己的产品,他的创造能力和创新思维特别强,又有敢想敢干的冒险精神,还有就是这孩子是个踏踏实实干事的人,能容得下事、容得下人。”戴光这样评价自己的儿子。

    无独有偶,李想同样来自一个环境宽松的家庭,他也把自己最重要的创业素质归结为“独立思考和总结能力强”以及不断学习的能力。“我小学时父母给的是命令,初中时给的是建议,高中时是从朋友的角度提出参考,到了高中以后就是信任了。父母给了我很多独立思考的机会,我基本没有受过什么束缚。”李想说。

    财富新贵引发教育新思考

    “因为出了我们这样一批人而在社会上引起什么‘高等教育无用论’,纯属瞎扯。现实是大学里没有目标、随波逐流的人太多了,所以我们这样目标明确并坚持下来的人才显现出来。

    ”没有上过大学的李想说。

    李想坦言,没有上大学的确是自己的一个遗憾,因为人生中缺少了一份体验。“如果你有明确的目标,大学绝对是一个非常好的学习场所。但相对于上学来说,更重要的是一个人要有目标和方向。”

    李想把现在的大学生分为两类:毕业后可以挑单位的和毕业后“海投”简历也找不到工作的,“凡是可以挑单位的学生,绝对是从大一起就有明确的目标并一直为之努力。现在很多大学生或者是迷茫,或者虽有目标,但一遇到困难,就‘喜新厌旧’,常立志而不立长志。”

    上了重点大学却半路“逃跑”的戴志康这样看待自己4年的大学生活:“我考通信专业时本来以为会学到数字通信,接触到一些大型的 交换机 ,结果到大三才学到收音机原理,大四才学了黑白 电视 机原理。大学的课程设置太落后于时代,与用人单位的实际需求太脱节。”

    本身就是大学教授的戴光坦言,现在大学的课程设置的确存在滞后问题。“虽然现在高等教育已进入 大众 化时代,但很多老师和高校管理者的思维还停留在精英教育上。精英教育是细线条的,什么都要讲到,是培养理论家的,而大众教育是粗线条的,应该给学生一定的空间,应该多一些应用性的东西。”

    清华大学科技园副主任、被誉为清华“创业之父”的罗建北认为,像戴志康、李想这样的人,在现有的大学体系中很难培养出来,因为在应试教育模式下,很难把创业所需的一些素质加入到教学中,“创业素质的培养和应试教育就是一对矛盾,因为你要学生多了解社会,从而发现市场的需求和商机,这都需要时间来实践,那考试怎么办?”

posted @ 2006-08-03 15:55 崛起的程序员 阅读(215) | 评论 (0)编辑 收藏
合肥留学生创业园 刘涛  
  易趣网上开店的人不少,而曲剑秋是其中比较特别的一个。小曲的特别之处是他经营自己的易趣网店的独特思路,和他的MBA(工商管理硕士)归国留学生的背景。小曲是英国名校爱丁堡商学院的MBA学生。
  小曲在网上卖的是玩具。他的网上商店名叫“Toys4U”。在易趣网按店铺名:Toys4U开心玩具检索就可以找到小曲的网上商店。打开网店的首页,你可以看到一个红色方块中的“:)”字符号正对你张开着笑脸。这是小曲的商标和网上商店的店标。“All U BUY IS NOT JUST TOYS”(你在这里得到的不仅仅是玩具!)这句意味深长的话向我们表达着这个小小网店的不同寻常的经营理念。
  小曲的小店才开了4个月每个月销售额上万元利润约在4000元左右,算是小有收获。
  抱着几分好奇和惊讶,我辗转联系上了这位放弃了很好的工作机会而选择网上开店创业的MBA归国留学生。电话里的小曲很是坦诚,对我的问题的回答应对之中还是可以感受到一位经过英国名校系统培训过的MBA的敏锐思路。
  小曲在英国呆了3年,其中一年半是在上学,另一年半是在实习。小曲上学的地点在爱丁堡。
  小曲家在大连办着企业,望子成龙的父母铁了心要掏30万元把小曲送到英国读MBA。小曲虽觉得MBA最好是有一定的阅历再读比较好。但还是服从了父母的安排。小曲从小喜欢玩具,一到英国就好比如鱼得水。小曲说在英国二三十岁的人隔一段时间给自己买一个心仪的玩具已成家常便饭,成人玩玩具已是再普通不过的事情。小曲说我不抽烟不喝酒,有点零钱就买玩具。在英国3年,小曲在网上就是eBay(www.ebay.com)和雅虎的玩具拍卖店的常客,对英国的玩具文化也是深有体会。小曲回国时玩具装满了箱,价值有几万元人民币的。小曲多年收藏了从美国、英国以及欧洲其它国家购买来的玩偶精品(从英国运回玩具的运费就高达1000英镑),其中不乏“花木兰”、“狮子王”、“小鸡快跑”和“星战前传”等全套造型玩偶。
  小曲的家境不错加上每到周末有时还有华人家庭请小曲去为孩子教钢琴一小时20英磅小曲小时候学过钢琴到英国后因参加了使馆组织的春节联欢会表演了弹钢琴算是打响了自己的品牌),让自己的个人爱好有了赚钱的机会,同时也让自己有了买玩具的零花钱。
  在结束了MBA的一年半课程后小曲争取到了一个机会,到玩具反斗城(Toys″R″us)做销售助理。玩具反斗城是一个非常有名的大型玩具超市它的分店遍及欧洲、北美乃至亚洲一些国家和地区。小曲实习的这家超市位于伦敦最繁华的牛津街上的Hamleys哈姆雷 。在玩具反斗城小曲对欧美的玩具文化有了更深的理解。小曲心想以后回国就以在中国建这样一个管理有序的大型玩具专营店为事业。
  在英国呆了3年后,小曲决定回国,上海是小曲的首选城市。小曲在上海与人合租了房子。小曲心目中的工作还是玩具行业,但小曲很快发现国内的大多玩具公司都是家族管理和家庭作坊,理念上并不能够彼此认同。后来终于打听到玩具反斗城在广州的公司可能在上海要招一个人,但小曲很快发现对方要的不过是大专毕业生。就算自己愿意从底层做起,恐怕别人会怀疑自己另有企图,而高级的职位人家更倾向于非公开招聘的方式。
  一次偶然的机会,小曲从一本讲述归国MBA的书上知道了易趣网,也知道了易趣网的掌门人BO(邵亦波先生,易趣网CEO)和谭海音小姐的创业故事。同样的归国留学背景让小曲对易趣网有了好感。易趣管理和操作的规范化、国际化,另外低成本的网上操作既是新商品发布的绝佳平台,又是一个最有效的市场信息搜集反馈的工具。小曲心想我为啥不选择创业作为自己的起点呢?MBA最终目标不都是能干出点事儿吗?无非是使创业的过程更加理性化。小曲开始在易趣建立自己的网上商店。一开始就坚信易趣提供的这个平台会被越来越多的人认可。在英国的3年多时间里一直在雅虎和eBay的英国网站上竞拍喜爱的动作玩偶(Action Figures)也是Toys″R″us网站(和亚马逊合并后证明了电子商务一样可以做得很好)的忠实顾客,3年下来竟也积攒了近十大箱子玩具运回国内。 绝大部分玩具都是中国制造的,但因为在国内没有销售而且终端顾客根本无法看到,这更加坚定小曲回国投身玩具销售,带给国人更多更好玩具的信念。就这样,回国没有多久的小曲制定了先在易趣开玩具店铺,通过其间和厂家以及顾客建立的良性纽带,进而巩固网上店铺并适时向外寻求加盟以拓展实体店铺的计划。
  小曲用自己租的住处作为自己网上商店的库房,客厅作为接待网上顾客看样的场所。自己在国外收藏的玩具当然是作为非卖品展示给客户,培养客户对玩具的爱好和对玩具文化的理解。同时也提高自己专业经营者的形象。同时小曲托在外贸工作的同学帮忙落实了国内的生产厂家。
  网店一开张小曲就感受到国内消费者对玩具的渴望。连续4个月每月都赢利4000元左右,月销售额在一万多。
  从香格里拉纪念版红毛熊熊的热销到由正版迪斯尼DISNEY系列玩偶所引起的不俗反响,小曲都是苦心经营,由于现在交易量很大,自己的女朋友也来帮忙,负责成交后网下的联系和交货发货。
  面对如此好的业绩,小曲说,这只是个开始,在以后的日子,仍会秉承一贯诚实经营、价格公道原则,诚心对待每一位新老顾客。还说,随着规模的扩大,买家将直接得到由于相关成本的降低而带来的实惠。
  问及在易趣开店的感受,小曲坦言说:体力和脑力的透支,但是精神上绝对放松,因为在做一件真正喜欢做的事情。赚钱绝不是第一位的(但绝对必要),否则一张英国MBA文凭加上在海外近三年学习工作的积累,也一定让小曲过得很好,但是那不是他所要追求的。他还说,每上传一件新商品,每看到买家收到玩具后满意的回复,打心眼儿里开心!
  小曲说起网上经营心得是:尽量及时回复买家留言和E-mail,没有人喜欢等待;商品选择要有自己的风格,物以稀为贵;买家永远是挑剔的,所以作为卖家的自己要不断改进商品和服务的质量。做玩具这一行,如同做时装店一样靠的是你进货的眼光,要了解你的客户。
  谈到4个月来通过网上商店对中国玩具市场的初步探索,小曲觉得相比之下中国的玩具消费市场还不是很发达,也不成熟。正因为如此才有了自己生存与发展的空间与机会。小曲说,既然我选择了这一行作为我的事业,我就要与市场一起成长。目前小曲的客户大多为收入2000元左右的年轻人。
  我问小曲想没想过将中国的玩具卖到国外去,小曲说不想,至少暂时不想,他更愿意为中国的消费者服务,“他们从我这里得到快乐,与国内的消费者之间我们更能够彼此认同。而如果卖给国外的消费者,他可能因我是个中国人而存有偏见,这是我无法接受的,万一我哪些方面做得不够,他又会说中国人怎样怎样,这也是我不愿看到的。我做这个不纯粹是为钱,如果单纯为钱我就不干这个了。”
  说到开网店的不足之处,小曲说,虽然网上开网店创业门槛低、投资小,但每次上传图片网上打理也怪费心费力的,一忙就精神高度紧张两个多小时。与网下店铺比,脑力的投入大多了。
通过充足的准备和网上的练兵,小曲准备利用自己经营网上商店建立起来的进货渠道,投资10万元在网下开一个玩具专营店。小曲的第一家以顾客为导向的先进管理的网下玩具专营店将在今年夏天于大连繁华地段开张营业。

案例分析


  看完了故事我们再做点儿分析,小曲特别之处在于他是一位MBA,而且是一位留学归来的MBA。但良好教育的最终目标是能干出点事儿,小曲也不例外,对玩具的强烈渴望梦想着拥有属于自己的玩具店,而后来所受的教育使得这种想法更加理性化。创业学也是MBA必修课之一。
  小曲现在初步展现在人们眼前的是一个充满生气、有自己独到经营理念、操作规范的网上店铺,三个月就成为在易趣有很高知名度和信用度的商铺。这是小曲将所学的知识在实践中给以应用的体现。 在同样做到初步开店成功的易趣个人网店店主中,小曲有着更广阔的视野、更好的策划、更详尽的目标、更高的目标。从一开始,小曲就认为易趣的模式必定可行,而且制定了“先在易趣开玩具店铺,通过其间和厂家以及顾客建立的良性纽带,进而巩固网上店铺并适时向外寻求加盟以拓展实体店铺”的计划。千里之行,始于足下。我们相信小曲会走的更远。
另外再来看一下目前小曲提供的一些数据:4个月来小曲共成交的客户有500多人。其中60%的客户会要求上门看货,上门看货后,全对小曲的专业性更加认可,并增大购买量。有本来在网上只不过是定了十几元的玩具,网下来一看定了几百元走。
  因此,我们可以看到即便是网上商店,网络也不是一切。网下的货要经得起推敲,网下看货等环节也要下功夫。网上商店只不过让客户通过网络知道你网下的地址。另外,网上不过是宣传联系客户,网下你也要做专业。开个玩具店也要开出水平来。
  我们再来看看对小曲的客户的一次性购买的情况:一次性购买最多的是1000元。买了500元以上商品的有20%,买了300元以上的有60%。
  因此,我们可以看到对于小曲这种产品单价在几十元上百元上下的玩具,这样的图表数据说明,大多购买者一次会挑好几样,这说明网上有这样的消费人群在你的网店可以提供比网下价格更好,品种更多的产品时,他们是愿意“疯狂”购物的。
  在小曲的500多个客户中有10%的人在四个月内重复购买了三四次。其中有十几个外地个体户上门来批发。批发的金额在几千元,对于批发的客户,小曲加上很少一点利润就出。小曲说这些生意我赚得很少,但我在帮厂家出货,我通过这些生意加强了与厂家的关系。
  因此,可见小曲的经营理念实施得比较到位,4个月内重复购买的客户已达30%。因此如果全年来统计会更高。因此,有没有回头客很重要,网店一定要有特色。另外,批发的客户数达到10%,交易额也不错,但创利在总利润额中所占比例很低,这与小曲的价格策略有关。
  可见,网络可以虚拟,生意还应以诚相待。小生意也要有远大目光,要做别人一辈子的生意,而不是一次两次。

posted @ 2006-07-28 09:29 崛起的程序员 阅读(303) | 评论 (0)编辑 收藏
posted @ 2006-07-27 19:03 崛起的程序员 阅读(239) | 评论 (0)编辑 收藏
诸位,咱当电子工程师也是十余年了,不算有出息,环顾四周,也没有看见几个有出息的!回顾工程师生涯,感慨万千,愿意讲几句掏心窝子的话,也算给咱们师弟师妹们提个醒,希望他们比咱们强!
  
    [1]好好规划自己的路,不要跟着感觉走!根据个人的理想决策安排,绝大部分人并不指望成为什么院士或教授,而是希望活得滋润一些,爽一些。那么,就需要慎重安排自己的轨迹。从哪个行业入手,逐渐对该行业深入了解,不要频繁跳槽,特别是不要为了一点工资而转移阵地,从长远看,这点钱根本不算什么,当你对一个行业有那么几年的体会,以后钱根本不是问题。频繁地动荡不是上策,最后你对哪个行业都没有摸透,永远是新手! 
  
    [2]可以做技术,切不可沉湎于技术。千万不可一门心思钻研技术!给自己很大压力,如果你的心思全部放在这上面,那么注定你将成为孔乙己一类的人物!适可而止为之,因为技术只不过是你今后前途的支柱之一,而且还不是最大的支柱,除非你只愿意到老还是个工程师!      
  
    [3]不要去做技术高手,只去做综合素质高手!在企业里混,我们时常瞧不起某人,说他“什么都不懂,凭啥拿那么多钱,凭啥升官!”这是普遍的典型的工程师的迂腐之言。8051很牛吗?人家能上去必然有他的本事,而且是你没有的本事。你想想,老板搞经营那么多年,难道见识不如你这个新兵?人家或许善于管理,善于领会老板意图,善于部门协调等等。因此务必培养自己多方面的能力,包括管理,亲和力,察言观色能力,攻关能力等,要成为综合素质的高手,则前途无量,否则只能躲在角落看示波器!技术以外的技能才是更重要的本事!!从古到今,美国日本,一律如此!
  
    [4]多交社会三教九流的朋友!不要只和工程师交往,认为有共同语言,其实更重要的是和其他类人物交往,如果你希望有朝一日当老板或高层管理,那么你整日面对的就是这些人。了解他们的经历,思维习惯,爱好,学习他们处理问题的模式,了解社会各个角落的现象和问题,这是以后发展的巨大的本钱,没有这些以后就会笨手笨脚,跌跌撞撞,遇到重重困难,交不少学费,成功的概率大大降低!   
  
    [5]知识涉猎不一定专,但一定要广!多看看其他方面的书,金融,财会,进出口,税务,法律等等,为以后做一些积累,以后的用处会更大!会少交许多学费!!    
  
    [6]抓住时机向技术管理或市场销售方面的转变!要想有前途就不能一直搞开发,适当时候要转变为管理或销售,前途会更大,以前搞技术也没有白搞,以后还用得着。搞管理可以培养自己的领导能力,搞销售可以培养自己的市场概念和思维,同时为自己以后发展积累庞大的人脉!应该说这才是前途的真正支柱!!!  
    [7]逐渐克服自己的心里弱点和性格缺陷!多疑,敏感,天真(贬义,并不可爱),犹豫不决,胆怯,多虑,脸皮太薄,心不够黑,教条式思维。。。这些工程师普遍存在的性格弱点必须改变!很难吗?只在床上想一想当然不可能,去帮朋友守一个月地摊,包准有效果,去实践,而不要只想!不克服这些缺点,一切不可能,甚至连项目经理都当不好--尽管你可能技术不错!       
  
    [8]工作的同时要为以后做准备!建立自己的工作环境!及早为自己配置一个工作环境,装备电脑,示波器(可以买个二手的),仿真器,编程器等,业余可以接点活,一方面接触市场,培养市场感觉,同时也积累资金,更重要的是准备自己的产品,咱搞技术的没有钱,只有技术,技术的代表不是学历和证书,而是产品,拿出象样的产品,就可技术转让或与人合作搞企业!先把东西准备好,等待机会,  否则,有了机会也抓不住!   
      
    [9]要学会善于推销自己!不仅要能干,还要能说,能写,善于利用一切机会推销自己,树立自己的品牌形象,很必要!要创造条件让别人了解自己,不然老板怎么知道你能干?外面的投资人怎么相信你?提早把自己推销出去,机会自然会来找你!搞个个人主页是个好注意!!特别是培养自己在行业的名气,有了名气,高薪机会自不在话下,更重要的是有合作的机会...      
  
    [10]该出手时便出手!永远不可能有100%把握!!!条件差不多就要大胆去干,去闯出自己的事业,不要犹豫,不要彷徨,干了不一定成功,但至少为下一次冲击积累了经验,不干永远没出息,而且要干成必然要经历失败。不经历风雨,怎么见彩虹,没有人能随随便便成功!
posted @ 2006-07-25 10:22 崛起的程序员 阅读(213) | 评论 (1)编辑 收藏
美国东部时间2004年9月29日早上10:58分(北京时间2004年9月29日晚10时58分),中国互联网人才招聘服务网站51job(前程无忧)成功登陆美国纳斯达克,开盘价为每存托股18.98美元,当天收市价为21.15美元,比发行价每股14美元上涨51%。前程无忧此次共发行了604万股美国存托股(ads),共筹得资金8453万美元……作为51job的创始人之一,首席执行长甄荣辉拥有公司30.6%的股份,是公司的第一大股东。”在国庆长假前的9月30日,国内各大媒体、网站纷纷报道了上述消息。

随着前程无忧的成功上市,又一个十亿(人民币)级的富翁走进了人们视线。也许人们津津乐道的是富豪们的身价排名,或是艳羡于他们“一夜暴富”的传奇,但是,在故事中的主人公看来,“事实上,成功一点都不难!最难的是:想成功,但没有计划!如果你有一个5年或者10年的成功目标,而且能够周密地计划,坚定地执行,那么,因为计划,成功率还是很高的。”

如果成功是能够被计划的,那么,出身于香港平民家庭的寒门“虎子”甄荣辉(1962年出生,属虎)依靠清晰的人生定位、严谨的职业发展路径计算、锲而不舍的努力与坚持,一步一步实现人生目标的创富故事将是可以被复制的……懵懂少年初识愁滋味无知贪玩的少年第一次经受了人生磨难,置之死地而后生的遭遇警醒了他内心深处的精英梦;洗澡排队、上厕所排队、做饭排队,香港平民的生存环境给他上了人生第一课:做任何事必须把握好时间与效率,看准了时机一定要毫不迟疑地冲上去。

少年时期的甄荣辉并没有显露出大器将成的先兆,反而以“懒”著称。

1962年甄荣辉出生在香港一个最普通的平民家庭。其父母是从广东“移居”到香港的,他和妈妈以及另外七八家20多口人“蜂居”在一套总面积不到80平米的公寓内,在这种高度拥挤状况下,做饭要排队,洗澡要排队,上厕所也要排队。做任何事必须要把握好时间与效率,这是家庭环境带给甄荣辉的重要一课。

炎热的夏天是最难过的。为了排队洗澡,他一放学就把洗漱用品准备好,然后每隔5分钟去洗澡间探查一次,一俟对方洗完走出房门,他一个箭步马上就冲了进去。生存的艰难,教育了一个十几岁的孩子,一定要抓住机会,看准了时机一定要毫不迟疑地冲上去。就从“抢”洗澡室开始,甄荣辉小小年纪便有了竞争意识。

因为父母都忙于打工挣钱养家,没有人顾及他的功课,于是那个时候的他每天回家把书包一扔就坐在沙发上看电视,然后看到很晚就去睡觉,尽可能地睡,一天睡16个小时。“我不是几天,几个月,而是几年的时间我都在跟自己说,哎呀,你怎么可以这么懒呢?每天到睡觉的时候我也会自责:今天一天又什么活也没干!明天一定不能这样!但是到了明天,依然还是看电视、睡觉,睡觉、看电视……”这样懒的直接后果是,在1979年他中学会考时,全香港30多家预科学校,没有一家愿意要他,因为他的中学实在太烂了。如果不能念预科,不能念大学,对于他这样一个毫无背景的平民家庭的孩子而言,前途是渺茫的。他第一次开始考虑前途问题,告诉自己:再苦再难一定要考上大学。“有一天我在烈日下排了很长很长的队去报名,好不容易挤上去,人家一看我的中学,皱皱眉说不要。拿着报名表出来,后面还有一大群人在排队。在太阳下烤得太久,我都几乎有些中暑了,但我还要打起精神去找下一所学校。我一边打着出租车找学校,一边告诉自己,如果过了这一关,一定要努力读书。”

人是需要一些难关才会成长的。经过这一次的打击,甄荣辉的自我意识开始觉醒,他仿佛变了一个人似的,开始知道发奋努力了。最后,他进入了一所位于偏远而荒凉的山上的预科学校。经过1979年到1981年两年预科的努力,他以优异成绩考入了香港中文大学工程系。
小小工程师胸怀鸿鹄志

香港这个成熟的商业社会教育他,走工程师的路很稳定,但是不会有大的成功,而走商业的路,就像在满布鲨鱼的大海,游得快就会赢,否则会被吃掉。为了获得更大的成功,他进入hp香港公司,成为一名顶级sales。

成长时期的磨难都是锻炼。不管是从小学、中学还是高中,甄荣辉和他的同代人一样,一直处于一种熙熙攘攘的竞争环境中,千军万马要挤一条踏向成功的独木桥。“所以,对于竞争来说,我从小也就习惯了。”

在英国统治下的香港,甄荣辉接受的是英国精英教育的熏陶,一定要成为社会精英人士,爬上去了就是爬上去了。香港这个成熟的商业社会教给他的第一课,是让他对商业的重要性有了认识,他的创业意识亦有所萌芽了。

1984年甄荣辉大学毕业时,大学组织了一次就业辅导,有来自ibm sales的销售总监及香港电讯的总工程师来给学生讲课。那时他想:如果我要成功的话,20年后我会是他们中的哪一位?了解他们的工作及看过当场表现后,甄荣辉直觉上很想做sales的工作。“走工程师的路肯定是很稳定,走商业的路,就好像在满布鲨鱼的大海,游得快就会赢,否则会被吃掉。”自信自己会游得快,向往有更大的成功,甄荣辉选择了挑战度高的商业工作,进入hp的sales & marketing部门,成为一名销售工程师。

hp的工作薪水不错而且稳定。他第一个月工资就是4900港元。甄荣辉似乎天生具有销售才干,三年后,他就成为公司的销售冠军。而到第四年,他每天只需工作不到两个小时,就可以完成业绩。“那个时候是蛮舒服的。因为你有一批大客户在你的手里面,你把他们伺候好了,这个客户其实是很难丢掉的。”那时他每天十点钟出去拜访客户,之后就没事了,但他不敢回家,怕回家老妈操心说,你干嘛这么早回来,是不是给炒鱿鱼了?于是他就去电影院看电影,一个礼拜看两三场电影,看到后来电影都没得看了,就跑到图书馆睡觉。

轻轻松松就能拿到30万(港元)的年薪收入(包括销售提成),而且作为销售状元,在hp的发展前景光明,这样的环境是很多人所羡慕的,但是甄荣辉却开始不满足,觉得挑战不够大。“我分析自己的现状,觉得这么发展下去,只有两个选择。一种选择是改变自己,适应环境。哎呀,你不要要求这么高了。还有一个就是改变环境。其实你会发现mba是一个很好的换环境的踏脚石。你可以在高科技公司里做事,你也可以去做咨询管理顾问,或者是去投资银行,还可以去消费品行业工作……学一个mba可以给你好多路的。”甄荣辉决定放弃hp的工作去读mba。


“小聪明”赢得大机会

mba毕业后的年薪将会在15万美元以上,而且有机会成为企业高级管理人员。他放弃了hp30万港元的年薪和稳定而轻松的工作,将全部的积累用作mba学费。他最终选择去欧洲念书,“因为欧洲学校只需要1年时间,不仅花钱少,而且还能早挣钱。”

1984年加入hp时,月工资是4900港元,当甄荣辉1988年离开hp的时候,他的年薪达到30万港元,他把在hp攒下的积蓄作为念mba的学费。当时他申请的是法国insead商学院。之所以选择这所学校,是因为它学时只有一年,而且学校知名度并不逊于美国院校。“读美国学校mba需要2年时间,而欧洲学校只需要1年时间。因此,去欧洲读书不仅花钱少,而且还能早挣钱。”他是这样计算的。

当他决定离开hp时,他接受了平生第一次专访,正是在那次访问上,他第一次清晰表达了自己的人生愿望。“我记得是一份计算机周刊方面的杂志,他们到惠普来采访销售架构。后来发现我是销售冠军,结果我又去念书,他们就来采访我。他们问我的人生目标,我说我希望毕业20年以后成为一个企业高级管理人员。”那时,他是25岁。

要去法国念书,必须会法语,可是甄荣辉当时没有任何法语基础,他又一次要挑战自己的极限了。他花了三个月来突击强化法语,但是在考试那天,他依然没有太多把握。不过,他发现法语考试主要是面试,由主考老师问几个问题,时间大约为15至20分钟。他担心自己的语言不过关,如果让老师放开问,自己听不懂再答不上来不就麻烦了!他事先准备好20多分钟的讲演内容。当面试老师提出第一个问题后,他就一口气地回答下去,一直回答了20分钟,根本没有给主考老师提问下一个问题的工夫。结果,他就这样通过了入学语言考试。

勤奋成就百万年薪经理人

“我要先在国外工作一段时间,看看国外公司是如何运作的。而有国外工作的经历,即使以后回香港也能对我的简历更有帮助。”在四个不同的工作意向中,他最后选择进入贝恩国际策略顾问公司英国办事处。

mba毕业以后,甄荣辉面临着几种选择:一是回香港工作,二是留在国外工作,或者加入高科技企业,或者进入咨询管理公司。四个方向的工作他都有选择的机会,“我决定要先在国外工作一段时间,因为刚出来一年就回香港,没有机会看看国外是怎么运作的,对我的简历可能也不是很有帮助。而选择做咨询,是因为它比较新鲜,可以接触不同的事情,见识面广。”最后,他选择了贝恩国际策略顾问公司(bain & company),先在其英国办事处工作。

尽管选择进入贝恩公司,对他是反复权衡之后的理性选择,他很喜欢这份工作,但贝恩却给了他一个下马威。上班三天之后有一个项目汇报会,他参加了这个两小时的会议,却发现有70%的内容都听不懂。连会议都听不懂,还怎么开展工作?他给远在香港的妈妈打电话说可能很快要回香港了。在这次会议上的受“辱”经历给他以巨大的打击,他明白自己还得要拼命努力,就像当初考大学,当初学习法语一样,爱拼才能赢。

经过半年的磨合,他发现和其他英国同事相比,语言是自己的短处,而分析推理能力是自己的长处。于是,甄荣辉把工作重点放到了项目调查方面,收集了大量数据,进行调查分析,并从中找出许多从前别人没有看到的东西。最初,他的工作是咨询顾问,当他把事先经过大量调查分析后的资料提供给项目经理时,项目经理似乎惊呆了,因为很少有一个咨询顾问工作像他那样深入。慢慢地,所有贝恩英国公司的经理们都点名要求甄荣辉加入他们的队伍。正是由于这样苦干加巧干,当该年贝恩英国要裁员三分之一时,他一个华人却被保留下来。

在加入贝恩两年多后,甄荣辉顺利地从咨询顾问升到经理。1991年他被派驻香港建立办事处,业务发展迅速。很快,在他一手策划下,贝恩又在北京开设了办事处,他出任贝恩中国总裁。1994年,32岁的甄在加入贝恩4年半后,成为贝恩公司历史上最年轻的副总裁以及最年轻的合伙人之一。这在“不up(提升)就out(离职)”、聪明人成堆的咨询公司实属难得。



“亿万富翁梦”的诱惑

做一个百万年薪的职业经理人,“薪水很高,职务也很稳定”,但是永远不会成为有影响力的企业家。“中国未来20年高速发展的经济环境将造就一批百亿级的富豪”,这一诱惑使得他不辞辛苦,屡次创业。

“作咨询顾问,每天接触到不同的客户,为不同的案子服务。今天你可能是被客户用直升机直接接到他们公司总部的楼顶上,你感受了大公司的豪华气派;明天你没准又要坐长途汽车在农村奔波,与泥土牛粪为伴。二者的巨大差异,让你深深感受到这个世界好像不公平。钱好像很重要,但是有些东西还不只是金钱带来的差异。然后你就会想,人生到底追求什么呢?”在成为年薪百万的财务自由人后,甄荣辉不再是那个为抢厕所而煞费苦心的少年小伙,不再是因为有钱能多吃一碗饭而高兴的大学生,不再是那个第一次拥有自己爱车而在雨中飙车的浪漫青年,他开始思考“人活着为什么”等哲学层面的问题,能够做一些更有社会影响力的事情,成为他的追求目标。

在甄荣辉的记忆中,1995年的一段经历对他触动很大:“那时我正负责百威啤酒的项目,贝恩总部也过来了一个高级合伙人专门负责这个项目。我们陪着百威啤酒董事长august busch在中国各地考察,贝恩的高级合伙人从来没来过中国,我全程陪同。有一天晚上,我们和老外在一起喝酒聊天,他们说在中国未来发展的二十年里面一定会出现十几位十亿美元家产的富翁。听到这句话,我心中咯噔一下,因为我以前没想过这个问题,但仔细一想觉得很有道理。香港在60年代到80年代的高速发展中,产生了十多个十亿美元的大富翁,大陆未来20年的高速发展难道不能产生新的富翁吗?你既然相信了在中国未来?0年会有几十个富豪出现,那你再想一想,自己还有机会排上吗?”

在大陆经济快速发展的大潮中,香港人甄荣辉做起了“亿万富翁梦”。对他而言,“这个出发点不是钱的问题,而是一种成就感。钱不一定就代表成就,但是在某种程度上还是反映了事业的成功,或者在市场上的影响力。”

天上不会掉下馅饼,更不会掉下财富。要实现“亿万富翁”的理想,坐在办公室等待,或者安于做一个朝九晚五的打工族肯定是不可能的,必须要投资,要创立自己的企业。去管理顾问公司的人一般做了两三年就都想出来自己做事,甄荣辉最初其实也是有过创业的想法与尝试。早在1991年,他就投资做了一种用声音传递邮件的系统,后来他还投资做过磁性材料等等,两次创业均以失败告终,但也使他学到了不少东西,“创业需要很大的精力,你必须全职去做;同时,团队也很重要。”

遭受过两次创业失败的打击,如果不敢再尝试,就此放弃,也就没有今天的前程无忧以及今天的甄荣辉了。从失败中总结出了一些经验与教训,1998年甄荣辉又开始尝试新的投资项目——人力资源服务。

互联网创造了新机会

互联网经济的兴起,给甄荣辉带来新的机会,风险投资成了最热的话题。甄荣辉想借此良机把网站作大,决定引入风险投资。他为前程无忧网设计了一个美好的前程:引入投资、上市、做成国际品牌。

1998年甄荣辉和他的创业伙伴成立了一家人力资源服务公司。

之所以选择这个领域,与他的一段经历分不开。1994年,当时任贝恩公司中国区总裁的甄荣辉需要招募新人,他先在一份英文媒体上刊登了招聘信息,但效果很差。后来,经北京同事指点,他选择了北京人爱看的一份当地媒体,结果反馈很好。但甄荣辉自己却感到当时报纸的印刷质量太差。当时香港的《南华早报》每周有多达200多页的招聘专版,人力资源市场十分活跃。但是,比香港人口还多的北京却没有这样一份专业的招聘纸媒体。他隐约找到了市场的需求空白。

到了1998年,大陆的人才交流市场日趋活跃,无论是用人单位还是求职者个人,他们迫切需要一个更专业的、定位于白领青年的招聘渠道。市场已经成熟!甄荣辉经人介绍,和《中国贸易报》合作,首先在北京推出了《中国贸易报.前程招聘专版》。北京《前程招聘专版》的推出,获得了很大成功,受到了企业以及求职者的普遍欢迎。受到北京市场的启发与鼓舞,甄荣辉和他的创业团队,开始在全国复制北京模式。五年多,在全国19个城市与当地媒体合作,推出了针对当地市场的《前程招聘专版》。

1999年,互联网经济正在全球兴起,网络给甄荣辉带来了新的机遇。

1999年1月,甄荣辉先在上海推出了career-post.com网站,当然内容只能算是《前程招聘专版》的电子版,是一些企业招聘信息的集纳。1999年底,互联网经济开始膨胀,风险投资成了最热的话题。甄荣辉想借此良机把网站作大,决定引入风险投资。他为自己设计了一个美好的梦想:引入投资、上市、做成国际品牌。网站也因此易名为无忧工作网(51job.com)。无忧工作网的赢利模式十分符合中国国情,也受到国外风险投资者的青睐,很快,在2000年2月,一笔金额为1400万美元的投资进入了甄荣辉的创业公司。

2000年以前,甄荣辉在前程无忧的角色还只是一个“天使投资人”,不参与具体的经营管理。等到2000年1400万美元的风险投资进入后,公司发展需要他全力以赴地投入,这时,他再次面临着人生的选择:是继续做一个百万美金年薪的打工皇帝,还是放弃眼前的舒适去做一个前途未卜的小公司的ceo,领取6万美元的年薪?此时,甄荣辉已经37岁,在贝恩干了11年,如果继续待下去,“薪水很高,职务也很稳定”,但“那时就有些怕,觉得可能再干20年还是这样”。同时,互联网的起飞让他看到挑战背后的巨大机会:“在互联网业,即使你去年做得成功,过了一个年头必须重新洗牌。这种充满刺激的感觉很过瘾。可以赢得很大,也可以输得很大,完全看自己的机遇与功力。”

2000年4月,甄荣辉经过慎重考虑,决定离开贝恩,出任前程无忧ceo。

做中国人的ge公司

也许你看不清路,但你能看得见那里有一座山,那就是你的目标。我一直相信人才服务这块业务有前景,我相信我们有一天是能做到10亿美元的;我希望有一天,前程能像ge公司那样成为行业典范,影响更多的中国企业乃至跨国企业。

甄荣辉认为他的成功“一半是运气,一半是努力”。一直有好运气的甄这次也不例外。

2000年2月,前程无忧的1400万美元风险投资到位,而到了2000年4月,网络经济的泡沫开始破灭,很多创业网站不能再在投资市场上拿到钱了。而有了这笔投资,前程无忧却获得一个空间去慢慢成长。“有了这几年的时间给我们成长,我们可以把我们的规模做大,把我们的管理做好,把我们的市场地位巩固。”中国的人才市场正处在发育阶段,机会无限,前程无忧得以随着中国人才市场的成熟而成长,在甄荣辉看来,“这是非常幸运、非常快意的事”。

前程无忧的发展,其实并非一帆风顺,也有过十分艰难的时候。2000年、2001年网络泡沫破灭,全球经济放缓,9.11事件后众多跨国企业招聘计划冻结,2003年又遇到sars事件,形势似乎从来不是一片光明。在最艰难的时候,甄荣辉有没有过后悔,有没有想过放弃?

“我们知道人才服务这一块肯定是值得做的,即使最早在北京的东花市小学起步的时候我们也看得见。香港《南华早报》的招聘周刊有200多页,我们的《前程招聘专版》到现在还没有发展到200多页。所以,我知道成功的机会肯定是有的,而且这个机会只有在中国才能找到。日本的人才网站每年几十个亿美元的营业收入,我相信我们有一天是能做十亿美元的。但是你说是哪一天呢,我也说不出来。你说到底有多长时间呢,你到底要做多少东西才能得到呢,这个可能是摸着石头过河。但是信心我是一直有的。”甄荣辉从来没有怀疑过自己的选择。

据新浪财经报告显示:从1999年到2002年的三年间,前程无忧营业收入就增长了25倍。到2002年,整个财年度实现盈利,销售收入约2000万美元,2003年销售收入为3544万美元,增长77%。2004年前三季度销售收入为4386万美元,较去年同期增长80%。“我们不断加快速度开设办事处,现在已在全国20个城市开设有分公司或办事处;我们又不断开发新产品,为客户提供招聘猎头、培训测评和人事外包等十多项专业的人力资源服务……我们的产品线很长,服务很深入,和客户的联系紧密。这就是我们的竞争优势。”

四年前甄荣辉放弃贝恩的百万年薪而出任前程无忧6万年薪的ceo时,他的计划是将51job运作上市,但这只是一个开始,他心中还有一个更宏伟的梦想——要做一间中国人的ge公司。在甄荣辉理想中,“当企业成为一个行业或企业群里的典范,它的影响力可以超过它规模的数倍甚至数十倍。譬如ge公司,它的影响力是全球的,经营ge不再是经营一个企业那么简单,它在经营企业人的思想,它在与世人分享领先的管理经验。现在,前程无忧还很小,但我希望有一天,前程能像ge公司那样成为行业典范,影响更多的中国企业乃至跨国企业。”

这不是一个5年10年的工程,20年够不够呢?“在我退休之前,我希望能够实现这个愿望。”甄荣辉又开始了一个新的人生计划!
posted @ 2006-07-25 10:22 崛起的程序员 阅读(381) | 评论 (0)编辑 收藏

对synchronized(this)的一些理解
 
一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

 

二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。

 

三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

 

四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。

 

五、以上规则对其它对象锁同样适用.

 

举例说明:

 

一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

 

package ths;

public class Thread1 implements Runnable {
public void run() {
synchronized(this) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " synchronized loop " + i);
}
}
}
public static void main(String[] args) {
Thread1 t1 = new Thread1();
Thread ta = new Thread(t1, "A");
Thread tb = new Thread(t1, "B");
ta.start();
tb.start();
}
}

 

结果:

 

A synchronized loop 0
A synchronized loop 1
A synchronized loop 2
A synchronized loop 3
A synchronized loop 4
B synchronized loop 0
B synchronized loop 1
B synchronized loop 2
B synchronized loop 3
B synchronized loop 4

 

二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。

 

package ths;

public class Thread2 {
public void m4t1() {
synchronized(this) {
int i = 5;
while( i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
}
public void m4t2() {
int i = 5;
while( i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
public static void main(String[] args) {
final Thread2 myt2 = new Thread2();
Thread t1 = new Thread(
new Runnable() {
public void run() {
myt2.m4t1();
}
}, "t1"
);
Thread t2 = new Thread(
new Runnable() {
public void run() {
myt2.m4t2();
}
}, "t2"
);
t1.start();
t2.start();
}
}

 

结果:

 

t1 : 4
t2 : 4
t1 : 3
t2 : 3
t1 : 2
t2 : 2
t1 : 1
t2 : 1
t1 : 0
t2 : 0

 

三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

 

//修改Thread2.m4t2()方法:

public void m4t2() {
synchronized(this) {
int i = 5;
while( i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}

}

 

结果:

 

t1 : 4
t1 : 3
t1 : 2
t1 : 1
t1 : 0
t2 : 4
t2 : 3
t2 : 2
t2 : 1
t2 : 0

 

四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。

 

//修改Thread2.m4t2()方法如下:

public synchronized void m4t2() {
int i = 5;
while( i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}

 

结果:

 

t1 : 4
t1 : 3
t1 : 2
t1 : 1
t1 : 0
t2 : 4
t2 : 3
t2 : 2
t2 : 1
t2 : 0

 

五、以上规则对其它对象锁同样适用:

 

package ths;

public class Thread3 {
class Inner {
private void m4t1() {
int i = 5;
while(i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : Inner.m4t1()=" + i);
try {
Thread.sleep(500);
} catch(InterruptedException ie) {
}
}
}
private void m4t2() {
int i = 5;
while(i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i);
try {
Thread.sleep(500);
} catch(InterruptedException ie) {
}
}
}
}
private void m4t1(Inner inner) {
synchronized(inner) { //使用对象锁
inner.m4t1();
}
}
private void m4t2(Inner inner) {
inner.m4t2();
}
public static void main(String[] args) {
final Thread3 myt3 = new Thread3();
final Inner inner = myt3.new Inner();
Thread t1 = new Thread(
new Runnable() {
public void run() {
myt3.m4t1(inner);
}
}, "t1"
);
Thread t2 = new Thread(
new Runnable() {
public void run() {
myt3.m4t2(inner);
}
}, "t2"
);
t1.start();
t2.start();
}
}

 

结果:

尽管线程t1获得了对Inner的对象锁,但由于线程t2访问的是同一个Inner中的非同步部分。所以两个线程互不干扰。

 

t1 : Inner.m4t1()=4
t2 : Inner.m4t2()=4
t1 : Inner.m4t1()=3
t2 : Inner.m4t2()=3
t1 : Inner.m4t1()=2
t2 : Inner.m4t2()=2
t1 : Inner.m4t1()=1
t2 : Inner.m4t2()=1
t1 : Inner.m4t1()=0
t2 : Inner.m4t2()=0

 

现在在Inner.m4t2()前面加上synchronized:

 

private synchronized void m4t2() {
int i = 5;
while(i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i);
try {
Thread.sleep(500);
} catch(InterruptedException ie) {
}
}
}

 

结果:

尽管线程t1与t2访问了同一个Inner对象中两个毫不相关的部分,但因为t1先获得了对Inner的对象锁,所以t2对Inner.m4t2()的访问也被阻塞,因为m4t2()是Inner中的一个同步方法。

 

t1 : Inner.m4t1()=4
t1 : Inner.m4t1()=3
t1 : Inner.m4t1()=2
t1 : Inner.m4t1()=1
t1 : Inner.m4t1()=0
t2 : Inner.m4t2()=4
t2 : Inner.m4t2()=3
t2 : Inner.m4t2()=2
t2 : Inner.m4t2()=1
t2 : Inner.m4t2()=0

posted @ 2006-07-19 10:55 崛起的程序员 阅读(304) | 评论 (0)编辑 收藏

Configuration cfg = new Configuration().configure("/hibernate.cfg.xml");

  for (Iterator iter = cfg.getTableMappings(); iter.hasNext();) {
   Table table = (Table) iter.next();
   for (Iterator iterator = table.getColumnIterator(); iterator.hasNext();) {

    Column column = (Column) iterator.next();
    System.out.println(column.getName());
   }

  }

posted @ 2006-07-17 11:39 崛起的程序员 阅读(432) | 评论 (0)编辑 收藏
     摘要: Spring Aop Step-By-Step 学习笔记(上)  www.uusam.com   最近由于工作需要,要求掌握关于 Spring 方面的东西。所以花了两个星期的时间来学习 Spring 的基本知识,主要包括 Ioc ...  阅读全文
posted @ 2006-07-05 17:26 崛起的程序员 阅读(349) | 评论 (0)编辑 收藏
MRX3F-47B9T-2487J-KWKMF-RPWBY(VOL 中国工商银行版)
QC986-27D34-6M3TY-JJXP9-TBGMD
DG8FV-B9TKY-FRT9J-6CRCC-XPQ4G(VOL 上海政府专用)
使用XP修改器修改成为以上任意一个序列号。修改完成后到如下地址验证是否通过,优先选择中国工商银行版本,有的xp使用上海政府专用的序列号不行。
http://www.microsoft.com/genuine/downloads/WhyValidate.aspx?displaylang=zh-cn


验证结束!

感谢您验证 Microsoft Windows 软件。


感谢您使用 Windows 正版增值计划 计划。现在您可以访问正版 Windows 用户资源。


验证成功后,可以下载更新安装Media Player 11 和 IE7。
posted @ 2006-07-05 15:31 崛起的程序员 阅读(2974) | 评论 (1)编辑 收藏
Sun公司一名员工自己创作的歌,关于Java EE 5,很有意思的一首歌,程序员业余生活也可以这么丰富!

地址:http://tap.javalobby.org/javaee5.mp3

歌词:
Ladies and gentlemen, this is Java EE 5!

One, two, three, four, five
There's a technology I use day and night
For my application with a web frontend
They told me to use .Net
But I really don´t wanna

So many bugs I fixed last week.
My code is neat and talk is a cheap
I like Glassfish, JSF, persistence API
And as I continue you know they´re gettin´ sweeter

So what can I do I really beg you my Lord
To me codin' it´s just like a sport
All the bad code from the past, let me dump it
Please set in the trumpet

A little bit of injection in my life
A little bit of persistence by my side
A little bit of NetBeans is all I need
A little bit of EJB's what I see
A little bit of standards in the sun
A little bit of XML all night long
A little bit web services here I am
A little bit of code makes me real man

This is Java EE 5!

Jump up and down and move your code around
Shake your head to the sound bury bad code under ground
Move one step left and one step right
One to the front and one to the side
Refactor it once and refactor it twice
If it looks like this you're doin´ it right

A little bit of injection in my life
A little bit of persistence by my side
A little bit of NetBeans is all I need
A little bit of EJB's is what I see
A little bit of standards in the sun
A little bit of XML all night long
A little bit web services here I am
A little bit of code makes me real man

This is Java EE 5!

本文来源:http://www.blogjava.net/Andyluo/archive/2006/06/30/javasong.html

posted @ 2006-07-04 08:47 崛起的程序员 阅读(330) | 评论 (0)编辑 收藏

 

1.下载http://www.eu.apache.org/dist/jakarta/tomcat-5/
这里注意,在jakarta-tomcat-5.0.28.exe以前是有默认的admin模块,在jakarta-tomcat-5.5.9.exe则没有安装默认的admin模块,这时http://127.0.0.1:8080/admin打开时则会出现
Tomcat's administration web application is no longer installed by default. Download and install the "admin" package to use it.
因此我们现在需要下载"admin"package 包
把jakarta-tomcat-5.5.x.zip  与   jakarta-tomcat-5.5.x-compat.zip  与  jakarta-tomcat-5.5.x-admin.zip
三个文件解压在同一个目录中
(如果使用jdk1.4,才需要compat.zip用jdk1.5就可以免了这个。)

2.修改jakarta-tomcat-5.5.x\conf\tomcat-users.xml.
添加管理员账号lizongbo,密码为lizongbopass.
新xml如下:
<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
  <role rolename="tomcat"/>
  <role rolename="role1"/>
  <role rolename="manager"/>
  <role rolename="admin"/>
  <user username="tomcat" password="tomcat" roles="tomcat"/>
  <user username="role1" password="tomcat" roles="role1"/>
  <user username="both" password="tomcat" roles="tomcat,role1"/>
  <user username="lizongbo" password="lizongbopass" roles="admin,manager"/>
</tomcat-users>


有时在%CATALINA_HOME%\server\webapps\admin\WEB-INF\web.xml里面也要做些修改


<!-- Security is active on entire directory -->
  <security-constraint>
    <display-name>Tomcat Server Configuration Security Constraint</display-name>
    <web-resource-collection>
      <web-resource-name>Protected Area</web-resource-name>
      <!-- Define the context-relative URL(s) to be protected -->
      <url-pattern>*.jsp</url-pattern>
      <url-pattern>*.do</url-pattern>
      <url-pattern>*.html</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <!-- Anyone with one of the listed roles may access this area -->
      <role-name>admin</role-name>
    </auth-constraint>
  </security-constraint>

  <!-- Login configuration uses form-based authentication -->
  <login-config>
    <auth-method>FORM</auth-method>
    <realm-name>Tomcat Server Configuration Form-Based Authentication Area</realm-name>
    <form-login-config>
      <form-login-page>/login.jsp</form-login-page>
      <form-error-page>/error.jsp</form-error-page>
    </form-login-config>
  </login-config>

  <!-- Security roles referenced by this web application -->
  <security-role>
    <description>
      The role that is required to log in to the Administration Application
    </description>
    <role-name>admin</role-name>
  </security-role>
无论是 Authetication ( 身份验证  还是 Authorization ( 权限管控  都只有设置相关的 admin ROLE, 当你想要新增或修改相关的 AA, 就必须修改这一个文件, 来符合你的环境.
3.修改jakarta-tomcat-5.5.x\conf\server.xml来解决编码问题。
(给Connector 添加URIEncoding参数,参考http://blog.csdn.net/darkxie/archive/2004/10/25/TOMCATAPP.aspx)
(可以设置成GB18030)
    <Connector port="8080"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" redirectPort="8443" acceptCount="200"
               connectionTimeout="20000" disableUploadTimeout="true" URIEncoding="GBK"
               compression="on" compressionMinSize="2048"
      noCompressionUserAgents="gozilla, traviata"
      compressableMimeType="text/html,text/xml"/>
 
    <Connector port="8009"
               enableLookups="false" redirectPort="8443" protocol="AJP/1.3" URIEncoding="GBK"/>
 

4.启用支持gzip压缩.
(http://www.linuxaid.com.cn/forum/showdoc.jsp?l=1&i=81169)
添加下列属性
 compression="on"
      compressionMinSize="2048"
      noCompressionUserAgents="gozilla, traviata"
      compressableMimeType="text/html,text/xml"
 
5.设置虚拟主机。
在jakarta-tomcat-5.5.x\下建立文件夹vhost\www.mydomain.com。
然后修改jakarta-tomcat-5.5.x\conf\server.xml
 
<Engine defaultHost="localhost" name="Catalina">
      <Host appBase="vhost/www.mydomain.com" name="http://www.mydomain.com/">
      </Host>
      <Host appBase="webapps" name="localhost">
      </Host>
      <Realm className="org.apache.catalina.realm.UserDatabaseRealm"/>
    </Engine>
 
6.添加数据库驱动,更新mail.jar和actiovation.jar
复制mysql-connector-java-3.0.16-ga-bin.jar,pg74.215.jdbc3.jar到 jakarta-tomcat-5.5.x\common\lib\
还有javamail 1.3.2的mail.jar,jaf-1_0_2的 activation.jar
msSQl 2000 JDBC sp3,msbase.jar,msutil,jar,mssqlserver.jar
 
 
7.配置SSL
参考 http://jakarta.apache.org/tomcat/tomcat-5.5-doc/ssl-howto.html
D:\j2sdk1.4.2_06\bin>%JAVA_HOME%\bin\keytool -genkey -alias tomcat -keyalg RSA
输入keystore密码:  lizongbossl
您的名字与姓氏是什么?
  [tomcat5.5.x]:  tomcat5.5.x
您的组织单位名称是什么?
  [jakarta]:  jakarta
您的组织名称是什么?
  [apache]:  apache
您所在的城市或区域名称是什么?
  [hzcity]:  hzcity
您所在的州或省份名称是什么?
  [gdp]:  gdp
该单位的两字母国家代码是什么
  [CN]:  CN
CN=tomcat5.5.x, OU=jakarta, O=apache, L=hzcity, ST=gdp, C=CN 正确吗?
  [否]:  y
 
输入<tomcat>的主密码
        (如果和 keystore 密码相同,按回车):

(必须密码一致,因此直接回车)
然后再把userhome(例如:C:\Documents and Settings\lizongbo\)下的.keystore复制到
tomcat的conf\目录下。
 (例如:D:\jakarta-tomcat-5.5.x\conf\.keystore
配置jakarta-tomcat-5.5.x\conf\server.xml
加上
    <Connector port="8443"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" disableUploadTimeout="true"
               acceptCount="100" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS"
           keystoreFile="conf/.keystore"
           keystorePass="lizongbossl"> <!--与先前设置的密码一致-->
    </Connector>
8.禁止文件目录列表,
修改jakarta-tomcat-5.5.x\conf\web.xml,把listing设置为false
 
    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>true</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
 
 9.指定了自己的javaEncoding
(参考 http://gceclub.sun.com.cn/staticcontent/html/sunone/app7/app7-dg-webapp/ch6/ch6-4.html 

    <servlet>
        <servlet-name>jsp</servlet-name>
        <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
        <init-param>
            <param-name>fork</param-name>
            <param-value>false</param-value>
        </init-param>
                <init-param>
            <param-name>javaEncoding</param-name>
            <param-value>GB18030</param-value>
        </init-param>
        <init-param>
            <param-name>xpoweredBy</param-name>
            <param-value>true</param-value>
        </init-param>
        <load-on-startup>3</load-on-startup>
    </servlet>
 10.添加rar,iso等的mime-type映射
避免在浏览器里直接打开。
<mime-mapping>
<extension>mht</extension>
<mime-type>text/x-mht</mime-type>
</mime-mapping>
<mime-mapping>
        <extension>rar</extension>
        <mime-type>application/octet-stream</mime-type>
    </mime-mapping>
    <mime-mapping>
        <extension>iso</extension>
        <mime-type>application/octet-stream</mime-type>
    </mime-mapping>
    <mime-mapping>
        <extension>ape</extension>
        <mime-type>application/octet-stream</mime-type>
    </mime-mapping>
    <mime-mapping>
        <extension>rmvb</extension>
        <mime-type>application/octet-stream</mime-type>
    </mime-mapping>
    <mime-mapping>
        <extension>ico</extension>
        <mime-type>image/x-icon</mime-type>
    </mime-mapping>
10.1对html静态页面设置编码
<!--  修改下面两行以支持静态超文本的自动编码
  -->
 <mime-mapping>
  <extension>htm</extension>
  <mime-type>text/html;charset=gb2312</mime-type>
  </mime-mapping>
 <mime-mapping>
  <extension>html</extension>
  <mime-type>text/html;charset=gb2312</mime-type>
  </mime-mapping>
  </web-app>
 
11.添加welcome-file-list,并调整顺序。
 <welcome-file-list>   
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>default.html</welcome-file>
        <welcome-file>default.htm</welcome-file>
        <welcome-file>default.jsp</welcome-file>
    </welcome-file-list>

posted @ 2006-06-20 19:50 崛起的程序员 阅读(311) | 评论 (0)编辑 收藏
--h
--help
打印常用启动参数的描述。
--cp:p additional_classpath 将指定的类路径附加到 IDE 的类路径之前。
--cp:a additional_classpath 将指定的类路径附加到 IDE 的类路径之后。
--fontsize size 设置字体大小,在 IDE 用户界面中用磅表示。如果未使用此选项,则字体大小为 11 磅。
--jdkhome jdk_home_dir 注意:测试 IDE 时是采用所支持的 Java 2 SDK 版本进行的,并且该版本与 IDE 捆绑在一起;因此,在大多数情况下,不需要使用此开关。 对于所有支持的平台(除 Mac OS 之外),缺省 JDK 就是与本产品捆绑在一起的 JDK,它位于 <安装目录>/java 中。对于 Mac OS,缺省 JDK 为计算机上安装的最新 JDK。

请使用指定的 Java 2 SDK 版本而不要使用缺省的 SDK。在 Microsoft Windows 系统中,缺省情况下,加载器会在注册表中查找并使用最新的可用 SDK。

请在升级 IDE 使用的 SDK 之前,备份用户目录。如果以后需要恢复到先前的 JDK,可以切换到已备份的用户目录,从而确保不丢失任何设置。

要切换 IDE 的用户目录,请使用 -userdir 开关。

--Jjvm_option 将指定的标志直接传递给 JVM。
--locale language[:country[:variant]]

使用指定的语言环境。支持的语言包括:
英语 (en)、西班牙语 (es)、韩语 (ko)、日语 (ja)、法语 (fr) 以及简体中文 (zh_CN)。
例如,在 <系统驱动器>:\Program Files\Sun\Creator2\bin 目录下,键入:
creator.exe --locale es

--userdir userdir 显式指定用户目录,该目录是存储用户设置的位置。
如果在 UNIX 系统中未使用此选项,则缺省情况下存储位置为$HOME/.Creator/2_1

在 Microsoft Windows 系统中,缺省位置是您第一次启动 IDE 时指定的位置。在大多数情况下,该位置为<系统驱动器>:\Documents and Settings\<用户名>\.Creator\2_1

您可以在“关于”对话框中确定当前的用户目录。选择“帮助”>“关于”,然后选择“详细信息”标签。此标签列出了用户目录位置以及其他产品详细信息。
posted @ 2006-06-09 16:27 崛起的程序员 阅读(152) | 评论 (0)编辑 收藏

1.在JSP文件中加上:

<%@ page contentType="text/html; charset=utf-8" %>

<html:html lang="true"></html:html>

2.使用以下形式输出字符:

<bean:message bundle="index" key="Anonymous"/>

3.建立类似以下形式的文件(放在classses目录下):

application_en_US.properties

application_zh_TW.properties

application_zh_CN.properties

其中可能用到这样的命令:

native2ascii application_cn.properties application_zh_CN.properties

posted @ 2006-06-07 14:44 崛起的程序员 阅读(245) | 评论 (0)编辑 收藏

以下所说的struts-config.xml和ApplicationResources.properties等文件名是缺省时使用的,如果你使用了多模块,或指定了不同的资源文件名称,这些名字要做相应的修改。

1、“No bean found under attribute key XXX”
在struts-config.xml里定义了一个ActionForm,但type属性指定的类不存在,type属性的值应该是Form类的全名。或者是,在Action的定义中,name或attribute属性指定的ActionForm不存在。


2、“Cannot find bean XXX in any scope”
在Action里一般会request.setAttribute()一些对象,然后在转向的jsp文件里(用tag或request.getAttribute()方法)得到这些对象并显示出来。这个异常是说jsp要得到一个对象,但前面的Action里并没有将对象设置到request(也可以是session、servletContext)里。
可能是名字错了,请检查jsp里的tag的一般是name属性,或getAttribute()方法的参数值;或者是Action逻辑有问题没有执行setAttribute()方法就先转向了。
还有另外一个可能,纯粹是jsp文件的问题,例如<logic:iterate>会指定一个id值,然后在循环里<bean:write>使用这个值作为name的值,如果这两个值不同,也会出现此异常。(都是一个道理,request里没有对应的对象。)


3、“Missing message for key "XXX"”
缺少所需的资源,检查ApplicationResources.properties文件里是否有jsp文件里需要的资源,例如:


<bean:message key="msg.name.prompt"/>

这行代码会找msg.name.prompt资源,如果AppliationResources.properties里没有这个资源就会出现本异常。在使用多模块时,要注意在模块的struts-config-xxx.xml里指定要使用的资源文件名称,否则当然什么资源也找不到,这也是一个很容易犯的错误。

4、“No getter method for property XXX of bean teacher”
这条异常信息说得很明白,jsp里要取一个bean的属性出来,但这个bean并没有这个属性。你应该检查jsp中某个标签的property属性的值。例如下面代码中的cade应该改为code才对:

<bean:write name="teacher" property="cade" filter="true"/>


5、“Cannot find ActionMappings or ActionFormBeans collection”
待解决。

6、“Cannot retrieve mapping for action XXX”
在.jsp的<form>标签里指定action='/XXX',但这个Action并未在struts-config.xml里设置过。

7、HTTP Status 404 - /xxx/xxx.jsp
Forward的path属性指向的jsp页面不存在,请检查路径和模块,对于同一模块中的Action转向,path中不应包含模块名;模块间转向,记住使用contextRelative="true"。

8、没有任何异常信息,显示空白页面
可能是Action里使用的forward与struts-config.xml里定义的forward名称不匹配。


9、“The element type "XXX" must be terminated by the matching end-tag "XXX".”
这个是struts-config.xml文件的格式错误,仔细检查它是否是良构的xml文件,关于xml文件的格式这里就不赘述了。

10、“Servlet.init() for servlet action threw exception”
一般出现这种异常在后面会显示一个关于ActionServlet的异常堆栈信息,其中指出了异常具体出现在代码的哪一行。我曾经遇到的一次提示如下:

java.lang.NullPointerException
    at org.apache.struts.action.ActionServlet.parseModuleConfigFile(ActionServlet.java:1003)
    at org.apache.struts.action.ActionServlet.initModuleConfig(ActionServlet.java:955)


为解决问题,先下载struts的源码包,然后在ActionServlet.java的第1003行插入断点,并对各变量进行监视。很丢人,我竟然把struts-config.xml文件弄丢了,因此出现了上面的异常,应该是和CVS同步时不小心删除的。

11、“Resources not defined for Validator”
这个是利用Validator插件做验证时可能出现的异常,这时你要检查validation.xml文件,看里面使用的资源是否确实有定义,form的名称是否正确,等等。

posted @ 2006-06-07 14:43 崛起的程序员 阅读(245) | 评论 (0)编辑 收藏
这只小猫玩了它差不多也有一年多了,还没仔细瞅瞅长个什么样。当它耍性子的时候,常常弄的我手足无措,因为不懂它说出的一大堆乱七八糟的洋话。为了能让它说start就start,说down就down,下面要开始一段我训兽师的经历 ^_^。

宠物简介
Jakarta Tomcat服务器是一种Servlet/JSP容器,经历了3.x到4.0.x到4.1.x的变迁,现在最新的版本为5.0.x,支持Servlet2.4和JSP2.0规范,从apache网站上下载Tomcat 5,在环境变量中配置一下JAVA_HOME,小猫就能生灵活现的跑起来了。若小猫启动失败,DOS窗口会自动关闭,若运行catalina run命令DOS窗口是不会自动关闭的。

tomcat中有三个放置java库的地方,分别是/server/lib、/shared/lib和/common/lib。
区别:/server/lib - 其中的jar文件只能被tomcat服务器访问。
/shared/lib - 其中的jar文件可以被所有的Web应用访问,但不能被tomcat服务器访问到。
/common/lib - Web服务和tomcat服务器都可以访问的到。

server.xml文件解析 - 文件位置是<%CATALINA_HOME%>/conf/server.xml

配置虚拟主机
Host元素代表虚拟主机,在同一个Engine元素下可以配置多个虚拟主机。打开server.xml文件可以发现Engine元素下已经有一个名为localhost的Host元素了,可以在它后面加入下列代码:

< Host  name ="www.myname.com"  debug ="0"  appBase ="c:\myname"  unpackWar ="true"  autoDeploy ="true" >
    < alias > www.myname1.com </ alias >
    < alias > www.myname2.com </ alias >
    < Context  pathh ="/helloapp"  docBase ="helloapp"  debug ="0"  reloadable ="true" />
</ Host >  

Session的使用
Session是一种用来跟踪用户状态的机制,那它是怎么实现的呢?Servlet容器通过在客户端浏览器中保存一个Session ID来跟踪Session,调用session.getID()可以看到你的Session ID是多少。如果客户端支持Cookie,就把Session ID作为Cookie保持在浏览器中,现在绝大多数浏览器都会把Cookie功能打开,但如果用户禁止了Cookie呢?Java Servlet API中提出了另外一种机制,Servlet容器可以重写客户requst的URL,把Session ID添加到URL信息中,HttpServletResponse接口提供了这样的方法:public String encodeURL(String url)-先判断如果没有启用Session,例如jsp中<%@ page session="false"%>或执行了session.invalideate(),那么直接返回url,在判断客户端师父支持Cookie,如果不支持,就在url中加入Session ID的信息,然后返回修改后的url。

Session的管理
当一个sesson开始时,Servlet容器会创建一个HttpSession对象,在某些情况下把这些Httpsession对象从内存中转移到文件系统中或数据库中,需要访问的时候在把它们载入到内存中来。这样做的好处有两点:节约了内存的消耗,当web服务器产生故障时,还可以从文件系统或数据库中恢复Session的数据。
对于Session的管理,小猫提供了两个实现类:org.apache.catalina.session.StandardManager和org.apache.catalina.session.PersistentManager。
StandardManager -是默认的方法,当Tomcat服务器重启或重载的时候,会把Session对象保存到
<%CATALINA_HOME%>/work/Catalina/honstname/applicatonname/SESSIONS.ser(默认值)文件中,每个对象对应一个文件,以Session ID为文件名,例如:

< Context  path ="/helloapp"  docBase ="helloapp"  debug ="0"  reloadable ="true" >
    < Manager  className ="org.apache.catalina.session.StandardManager"  debug ="0"  
      maxActiveSessions="-1" checkInterval="60"
 / >
</ Context >

参数说明:checkInterval-检查session是否过期的时间间隔,以秒为单位,缺省值是60秒;
maxActiveSessions-可处于活动状态的session数。

PersistentManager -提供了更加灵活的管理方式,具有容错能力,可以及时把Session备份到Session Store中,可以控制内存中Session的数量。
小猫还提供了实现持久化Session Store的接口,org.apache.catalina.Store,目前提供了两个具体实现类:org.apache.catalina.FileStore和org.apache.catalina.JDBCStore。
server.xml中的配置File Store -

< Context  path ="/helloapp"  docBase ="helloapp"  debug ="0"  reloadable ="true" >
    < Manager  className ="org.apache.catalina.session.PersistentManager"  debug ="0"  saveOnRestart ="true"  
          maxActiveSessions
="-1"  minIdleSwap ="-1"  maxIdleSwap ="-1"  maxIdleBackup ="-1"   >
    < Store  className ="org.apache.catalina.session.FileStore"  directory ="mydir" />
    </ Manager >
</ Context >

参数说明:saveOnRestart-服务器关闭时,是否将所有的session保存到文件中;
maxActiveSessions-可处于活动状态的session数;
minIdleSwap/maxIdleSwap-session处于不活动状态最短/长时间(s),sesson对象转移到File Store中;
maxIdleBackup-超过这一时间,将session备份。(-1表示没有限制)

JDBCStore配置的区别:

< Store  calssName ="org.apache.catalina.JDBCStore"  driverName ="com.mysql.jdbc.Driver"  
    connectionURL
="jdbc:mysql://localhost/tomsessionDB?user=root&password="  
    sessionTable
="tomcat_session"  sessionIdCol ="session_id"  sessionDataCol ="session_data"  
    sessionValidCol
="session_valid"  sessionMaxInactiveCol ="max_inactive"  
    sessionLastAccessedCol
="last_access"  sessionAppCol ="app_name"  checkInterval ="60"  debug ="99"   />

Session失效时间的设定
在web.xml文件中,位于<servlet-mapping>和<welcome-file-list>元素之间加入如下代码,单位为分钟:

< session-config >    
    < session-timeout > 60 </ session-timeout >
</ session-config >

Tomcat的admin平台和manager平台
这是Tomcat中自带的两个Web应用,位于<%CATALINA_HOME%>/server/webapps/admin(manager),访问地址是http://localhsot:8080/admin(manager)。要访问这两个Web应用,需要在
<%CATALINA_HOME%>/conf/tomcat-users.xml中添加如下内容:
<user username="admin" password="1234" role="admin"/>           //对应admin Web应用
<user username="manager" password="1234" role="manager"/>   //对应manager Web应用

admin平台把所有可配置的信息分为三类:Tomcat Server、Resources、User Definition。
Tomcat Server-相当于server.xml中的<Server>元素及其子元素,<Service>、<Host>、<Context>、<Resources>、<Date Source>。
Resources-相当于server.xml中的<GlobalNamingResources>,共有四种资源:Date Source(JNDI数据源)、Mail Sessioin(JNDI Mail Session资源)、Environment Entry(环境变量)、User Database(安全域中的用户数据库)。
User Definition-与tomcat-users.xml相对应。

manager平台-列出来所有Web应用和状态,并提供了Start、Stop、Reload、 Undeploy命令,还可以发布
<%CATALINA_HOME%>/webapps目录下的Web应用或系统文件任意位置的WAR文件。

posted @ 2006-06-07 14:41 崛起的程序员 阅读(241) | 评论 (0)编辑 收藏
解决思路:
配置web工程web.xml加上
  <mime-mapping>
    <extension>doc</extension>
    <mime-type>application/msword</mime-type>
  </mime-mapping>
  <mime-mapping>
    <extension>xls</extension>
    <mime-type>application/vnd.ms-excel</mime-type>
  </mime-mapping>
修改后非常注意的要清空缓存这样才会弹出保存还是打开的窗口否则打开还是乱码.
posted @ 2006-06-02 12:32 崛起的程序员 阅读(1080) | 评论 (0)编辑 收藏
在weblogic下面增加虚拟目录
修改weblogic.xml
<?xml version="1.0" encoding="UTF-8"?>
<weblogic-web-app xmlns="
http://www.bea.com/ns/weblogic/80 " xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance " xsi:schemaLocation=" http://www.bea.com/ns/weblogic/80 http://www.bea.com/ns/weblogic/80/weblogic-web-app.xsd http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd ">
  <context-root>webdoc</context-root>
  <virtual-directory-mapping>
 <local-path>c:/word/doc</local-path>
 <url-pattern>/*</url-pattern>
  </virtual-directory-mapping>
</weblogic-web-app>

posted @ 2006-06-01 15:49 崛起的程序员 阅读(2495) | 评论 (3)编辑 收藏

                                      

使用Spring来创建一个简单的工作流引擎                     

spring是支持控制反转编程机制的一个相对新的框架。本文把spring作为简单工作流引擎,将它用在了更加通用的地方。在对工作流简单介绍之后,将要介绍在基本工作流场景中基于Spring的工作流API的使用。

  许多J2EE应用程序要求在一个和主机分离的上下文中执行处理过程。在许多情况下,这些后台的进程执行多个任务,一些任务依赖于以前任务的状态。由于这些处理任务之间存在相互依赖的关系,使用一套基于过程的方法调用常常不能满足要求。开发人员能够利用Spring来容易地将后台进程分离成活动的集合。Spring容器连接这些活动,并将它们组织成简单的工作流。

  在本文中,简单工作流被定义成不需要用户干预,以一定顺序执行的任意活动的集合。然而,我们并不建议将这种方式代替存在的工作流框架。在一些场景中,需要更多的用户交互,例如基于用户输入而进行的转向,连接或传输,这时,比较好的方法是配用一个单独的开源或者商业的工作流引擎。一个开源项目已经成功地将更复杂的工作流设计集成到spring中。

  如果你手上的工作流任务是简单的,那么,与功能完备的独立工作流框架相比,简单工作流的策略就会变得有意义,特别地,如果已经使用了spring,这种快速实现可以保证时间不会变得更加漫长。此外,考虑到spring轻量级的控制反转容器的特点,spring在资源负载上减少了资源负载。

  这篇文章简短地从编程主题的角度介绍工作流。通过使用工作流的概念,spring被用来作为驱动工作流引擎的框架。然后,讨论了生产部署选项。现在,让我们从工作流的设计模式和相关背景信息来介绍简单工作流的思想吧。

  简单工作流

  工作流模型是一个早在70年代就有人开始研究的主题,许多开发者都试图创建工作流模型规范。W.H.M. van der Aalst等人写了《工作流模型》白皮书(2003年7月),它成功地提炼出一组设计模式,这些设计模式准确地将大多数通用的工作流场景建模。当中,最普通的工作流模式是顺序模式 (Sequence pattern)。顺序工作流模式满足了简单工作流的设计原则,并且由一组顺序执行的活动组成。

  UML(统一建模语言)活动图通常被用来作为一个机制对工作流建模。图1显示了一个基本的使用标准UML活动图对顺序工作流过程的建模过程。

图 1顺序工作流模式

  顺序工作流是一个在J2EE中流行的标准工作流模式。J2EE应用程序在后台线程中,通常需要一些顺序发生的事件或者异步事件。图2中的活动图描述了一个简单的工作流,用来通知感兴趣的旅行者,他们感兴趣的目的地的机票价格已经下降的事件。

图 2.机票价格下降的简单工作流

- 作者: macrochen 2005年12月11日, 星期日 22:28

图1中的航线工作流负责创建和发送动态的email通知。过程中的每一步表示了一个活动(activity)。在工作流处于活动之前,一些额外事件必须发生。在这个例子中,事件是飞行路线费率的减少。

  让我们来简要的看一下航线工作流的业务逻辑。如果第一个活动找不到对费率减少通知感兴趣的用户,那么整个工作流就被取消。如果发现了感兴趣的用户,那么接下来的活动继续执行。随后,一个XSL(扩展样式表)转换生成消息内容,之后,记录审计信息 (audit information)。最后,工作流试图通过SMTP服务器发送这个消息。如果这个任务没有错误地完成,便在日志中记录成功的信息,进程结束。但是,如果在和SMTP服务器通讯时发生了错误,一个特别的错误处理例程将要管理这些错误。错误处理代码将会试着去重新发送消息。

  考虑这个航线的例子,一个明显的问题是:你怎么样有效地将顺序处理过程分解为单独的活动?这个问题被spring巧妙的处理了。下面,让我们快速地讨论spring的反转控制框架。

  控制反转

  Spring通过使用spring容器来负责控制对象之间的依赖关系,使得我们不再对对象之间的依赖负责。 这种依赖关系的实现就是大家所知道的控制反转(IoC)或依赖注射。参见Martin Fowler's "Inversion of Control Containers and the Dependency Injection Pattern"(martinfowler.com, 2004年2月)得到关于控制反转和依赖注射的更加深入的讨论。通过管理对象之间的依赖关系,spring就不需要那些只是为了使类能够相互协作,而将对象粘合的代码。

  作为spring beans的工作流组件

  在进一步讨论之前,现在是简要介绍spring中主要概念的恰当时候。接口ApplicationContext是从接口BeanFactory继承的,它被用来作为在spring容器内实际的控制实体和容器。

  ApplicationContext负责对一组作为spring beans的一组bean的初始化,配置和生命期管理。我们通过装配在一个基于XML的配置文件中的spring beans来配置ApplicationContext。这个配置文件说明了spring beans互相协作的本质特点。这样,用spring的术语来说,与其他spring beans交互的spring beans就被叫着协作者(collaborators)。缺省情况下,spring beans是作为单例存在于ApplicationContext中的,但是,单例的属性能够被设置为false,从而有效地改变他们在spring中调用原型模式时的行为。

  回到我们的例子,在飞机票价下降的时候,一个SMTP发送例程的抽象就被装配在工作流过程例子中的最后的活动(例子代码可以在 Resources中得到)。由于是第5个活动,我们命名它为activity5。要发送消息,activity5就要求一个代理协作者和一个错位处理句柄。



      class="org.iocworkflow.test.sequence.ratedrop.SendMessage">
     
         BR>     

     
        
     

  
/FONT>


  将工作流组件实施成spring beans产生了两个令人喜悦的结果,就是容易进行单元测试和很大程度上可重用能力。IoC容器的特点明显地提供了有效的单元测试。使用像spring这样的Ioc容器,在测试期间,协作者之间的依赖能够容易的用假的替代者替代。在这个航线的例子中,能够容易地从唯一的测试ApplicationContext中检索出像activity5活动这样的spring bean。用一个假的SMTP代理SMTP服务器,就有可能单独地测试activity5。

  第二个意外的结果,可重用能力是通过像XSL转换这样的工作流活动实现的。一个被抽象成工作流活动的XSL转换现在能够被任何处理XSL转换的工作流所重用。

  装配工作流

  在提供的API中(从Resources下载),spring控制了一些操作者以一种工作流的方式交互。关键接口如下:

Activity: 封装了工作流中一个单步业务逻辑
ProcessContext:在工作流活动之间传递具有ProcessContext类型的对象。实现了这个接口的对象负责维护对象在工作流转换中从一个活动转换到另一个活动的状态。
ErrorHandler: 提供错误处理的回调方法。
Processor: 描述一个作为主工作流线程的执行者的bean。

  下面从例子源码中摘录的代码是将航线例子装配为简单工作流过程的spring bean的配置。



  
     
        
           
           
           
           
           
         BR>     

     
         BR>      /property>
     
         org.iocworkflow.test.sequence.ratedrop.RateDropContextBR>     

   /property>

  SequenceProcessor类是一个对顺序模式建模的具体子类。有5个活动被连接到工作流处理器,工作流处理器将顺序执行这5个活动。

  与大多数过程式后台进程相比,工作流的解决方案真正的突出了高度强壮的错误处理。错误处理句柄可以单独地处理每个活动。这种类型的句柄在单一活动级别提供了细致的错误处理。如果没有单独处理单个活动的错误处理句柄,那么全局工作流处理器的错误处理句柄将会处理出现的问题。例如,如果在工作流处理过程中的任意时刻,一个没有被处理的错误出现了,那么它将会向外传播,被使用defaultErrorHandler属性装配的ErrorHandler Bean处理。

  更复杂的工作流框架将工作流转换之间的状态持久化存储到数据库中。在这篇文章中,我们仅仅对状态转换是自动完成的工作流感兴趣。状态信息仅仅在实际工作流运行时在ProcessContext中得到。在ProcessContext中,你仅仅能看到ProcessContext的接口的两个方法:


public interface ProcessContext extends Serializable {
      public boolean stopProcess();   
      public void setSeedData(Object seedObject);
   }


  用于航线例子工作流的具体的ProcessContext类是RateDropContext类。RateDropContext类封装了用于执行航线费率降低工作流必须的数据。

  到现在为止,经由缺省的ApplicationContext的作用,所有bean实例都已经成了单例。但是,对于每一个航线工作流的调用,我们必须创建一个新的RateDropContext类的实例。为了处理这种需求,需要配置SequenceProcessor,采用全限定类名作为processContextClass属性的值。对于每个工作流的执行,SequenceProcessor使用指定的类名从spring检索ProcessorContext类的一个新的实例。为了使这种机制能够起作用,非单件的spring bean或者类型org.iocworkflow.test.sequence.simple.SimpleContext的原型必须存在于ApplicationContext中(完整的列表,参见rateDrop.xml)。

  播种工作流

  既然我们知道怎样使用spring来组装一个简单的工作流,就让我们集中精力使用种子数据(seed data)示例工作流的过程。要明白怎样开始工作流,看一看在实际接口Processor上表现的方法:


public interface Processor {
      public boolean supports(Activity activity);
      public void doActivities();
      public void doActivities(Object seedData);
      public void setActivities(List activities);
      public void setDefaultErrorHandler(ErrorHandler defaultErrorHandler);
   }


  大多数情况下,工作流需要一些初始化激活才能开始。开始一个处理过程有两个选项:doActivities(ObjectseedData)方法或者无参数的doActivities()。下面的代码列表是包含在样例代码中为SequenceProcessor而实现的doActivities():


public void doActivities(Object seedData) {
   //Retrieve injected by Spring
   List activities = getActivities();
   //Retrieve a new instance of the Workflow ProcessContext
   ProcessContext context = createContext();
   if (seedData != null)
      context.setSeedData(seedData);
   //Execute each activity in sequential order
   for (Iterator it = activities.iterator(); it.hasNext();) {
      Activity activity = (Activity) it.next();
      try {
            context = activity.execute(context);
      } catch (Throwable th) {
         //Determine if an error handler is available at the activity level
         ErrorHandler errorHandler = activity.getErrorHandler();
         if (errorHandler == null) {
            getDefaultErrorHandler().handleError(context, th);
            break;
         } else {
            //Handle error using default handler
            errorHandler.handleError(context, th);
         }
      }
            //Ensure it's ok to continue the process
      if (processShouldStop(context, activity))
         break;
      }
   }

  在这个航空费用减少的例子中,工作流过程的种子数据包括航线信息和费率减少的信息。使用容易测试的航线工作流例子,通过doActivities(Object seedData)方法发出种子数据并激活一个单一的工作流过程是简单的:

BaseProcessor processor = (BaseProcessor)context.getBean("rateDropProcessor");
   processor.doActivities(createSeedData());


  这些代码是从包含在这篇文章中的测试例子中摘录的。rateDropProcessor Bean是从ApplicationContext中检索来的。rateDropProcessor实际上是装配成SequenceProcessor的实例来处理顺序执行。createSeedData()方法实例化一个对象,这个对象封装了初始化航线工作流所需要的所有种子数据。

  Processor选项

  虽然包含在源代码中的Processor具体的子类仅仅是SequenceProcessor,但是,许多Processor接口的实现也是可以想象得到的。可以开发其他工作流处理过程子类来控制不同的工作流类型,例如,另一种像并行切割模式那样有着变化的执行路径的工作流。对于简单工作流来说,因为活动的顺序是预先决定了的,所以SequenceProcessor是好的选择。尽管没有被包括进来,对于使用基于spring的简单工作流的实现来说,排他选择模式是另一个好的选择。当使用排他选择模式时,在每个活动执行之后,Processor具体类就会讯问ProcessorContext,接下来将要执行哪一个活动。

  注:有关并行切割,排他选择和其他工作流模式的更多信息,请参看W.M.P. van der Aalst等人写的《工作流模式》一书。

  启动工作流

  考虑到工作流过程常常需要异步执行的特点,使用分离的执行线程来启动工作流就变得有意义了。对于工作流的异步启动而言,有好几个选项;我们主要集中在其中的两个:积极地检测(actively polling)一个队列来启动工作流,或者使用通过ESB(enterprise service bus, 企业服务总线)的事件驱动方式来启动工作流,而Mule就是ESB的一个开源项目(关于Mule的更多信息,请参加"Event-Driven Services in SOA"(JavaWorld, 2005年1月)。

  图3和图4描绘了两种启动策略。图3中,积极检测在工作流中第一个活动经常检查资源的情形下发生,比如数据源或POP3邮件帐户。如果图3中的积极检测发现有任务等待处理,那么启动就会开始。

图 3. 通过积极检测来启动工作流

  另一方面,图4表示了使用JMS(JAVA消息服务)的J2EE应用程序把事件放到队列上的情形。一个通过ESB配置的事件监听器收到图4中的事件,并且开始工作流,这样,启动工作流过程。

图 4. 通过ESB事件来启动工作流

http://searchwebservices.techtarget.com.cn/tips/110/2127110.shtml

  使用所提供样例的代码,让我们更详细的看看主动选择启动方式与事件驱动的启动方式。


  积极检测

  积极检测是一种花费较少的启动工作流过程的方案。SequenceProcessor足够灵活,以使得能够通过平滑的选择工作来进行启动过程。尽管并不令人满意,在没有时间进行事件驱动子系统的配置和部署的许多情景中,积极检测是明智的选择。

  使用Spring的ScheduledTimerTask,检测模式就能够容易地装配。缺点就是必须创建额外的活动来进行检测。这个检测活动必须被设计来讯问某些实体,如数据库表,pop邮件帐户,或者Web服务,然后决定新的工作是否等待参与到工作流中。

  在所提供的例子中,PollingTestCase类实例化一个基于检测的工作流过程。使用一个有着积极检测处理过程与事件驱动的启动过程的不同之处在于,spring支持doActivities()方法的无参数版本。相反地,在事件驱动的启动中,启动处理过程的实体通过doActivities(Object seedData)方法提供了种子数据来启动工作流。检测方法的另一个缺点是:资源不一定能够被重复地使用。依赖于应用程序环境,这种资源的消耗是不可接受的。

  下面代码例子演示了使用积极检测来控制工作流启动的一个活动:

public class PollForWork implements Activity
{
   public ProcessContext execute(ProcessContext context) throws Exception {
      //First check if work needs to be done
      boolean workIsReady = lookIntoDatabaseForWork();
      if (workIsReady) {
         //The Polling Action must also load any seed data
         ((MyContext) context).setSeedData(createSeedData());
      } else {
         //Nothing to do, terminate the workflow process for this iteration
         ((MyContext) context).setStopEntireProcess(true);
      }
      return context;
   }
}


  此外,包含在例子代码的单元测试中的PollRates类提供了一个主动选举启动的可以运行的例子。PollRates模拟了对于航线费率下降的重复检查。

  通过ESB的事件驱动启动工作流

  理想地,一个包含了适当的种子数据的线程能够异步地启动工作流。这种情况的一个例子是收到从JAVA消息服务队列的消息。一个监听JMS队列或者主题的客户会收到通知,这个通知告知处理应该在onMessage()方法中开始工作流。然后,通过使用Spring和doActivities(Object seedData)方法就能够获得工作流处理器Bean。


  使用ESB,实际用于发送启动事件的机制能够恰当地从工作流处理器中分离出来。开源项目Mule ESB有紧凑地和Spring相集成的好处。任意传送机制,比如JMS,JVM,或者POP3邮箱都能够发起事件的传播。

  工作流的连续运行

  工作流引擎后台进程应该能够没有干扰地连续运行。对于正在运行的基于spring的工作流单一进程来说好,有几个选项。一个有着main()方法的简单Java类就足够演示与这篇文章伴随着的单元测试中的例子了。一个更加可靠的用于部署的机制是嵌入工作流到某种形式的J2EE组件中。Spring很好地支持和J2EE兼容的web应用程序归档或者war文件的集成。基于Java管理附件(JMX)服务归档和JBoss应用服务器(更多信息,参见JBoss homepage)支持的sar文件是更加合适的可部署组件,这种更合适的可部署组件也能够被用来将部署归档。在JBoss 4.0中,sar文件已经被大家所知道的deployer的格式所取代了。

  例子代码

  打包成zip格式的例程代码最好是用Apache Maven来使用它们。你能够在主源代码目录src/java找到API。src/java目录中有三个单元测试,包括:SimpleSequenceTestCase,RateDropTestCase和PoolingTestCase。要运行所有这些测试,在命令行shell中键入maven test,然后在编译和运行之前,Maven将会下载所有必需的jar文件。实际的XSL转换将会发生在两个测试中,它们的结果被管道输出到控制台。键入maven test:ui来拉出图形化的测试运行器,然后选择你想要运行的测试,并且观察控制台的结果。

  结论

  在这篇文章中你已经通过设计模式看到了工作流过程种类,在这些模式中,我们主要集中介绍了顺序模式。通过使用接口,我们来对基本工作流组件建模。通过装配多个接口实现到Spring,实现一个顺序工作流。最后还讨论了启动和部署工作流的不同选项。

  这里所提出的简单工作流技术肯定不是最终的和革命性的。但是,使用Spring来实现像工作流这样的通用任务是一个通过使用IoC容器而获得的效率的好的示例。由于减少了粘合性代码的需要,Spring在保持面向对象的约束同时,减少面向对象操作麻烦的程度。

http://macrochen.blogdriver.com/macrochen/1088243.html

什么是工作流引擎(Workflow Engine )- -

所谓工作流引擎是指workflow作为应用系统的一部分,并为之提供对各应用系统有决定作用的根据角色、分工和条件的不同决定信息传递路由、内容等级等核心解决方案。例如开发一个系统最关键的部分不是系统的界面,也不是和数据库之间的信息交换,而是如何根据业务逻辑开发出符合实际需要的程序逻辑并确保其稳定性、易维护性(模块化和结构化)和弹性(容易根据实际业务逻辑的变化作出程序上的变动,例如决策权的改变、组织结构的变动和由于业务方向的变化产生的全新业务逻辑等等)。 Workflow 引擎解决的就是这个问题:如果应用程序缺乏强大的逻辑层,势必变得容易出错(信息的路由错误、死循环等等)。

就好比一辆汽车,外表做得再漂亮,如果发动机有问题就只是一个摆设。应用系统的弹性就好比引擎转速方面的性能,加速到100 公里需要1 个小时(业务流程发生变动需要进行半年的程序修改)还能叫好车吗?引擎动不动就熄火(程序因为逻辑的问题陷入死循环)的车还敢开吗?

工作流解决方案与传统管理软件的关系传统的管理软件注重解决企业应用层现存的问题(例如提高企业的资源配置率或提高单一员工的生产效率)。例如:EXCEL 可以提高员工画表格的效率、财务软件可以规范财务人员的工作并提高帐目查询的效率、CRM 可以规范客户管理从而使客户资源掌握在公司手中而不是被一部分业务人员把持并提高客户响应时间、ERP 解决的是如何配置企业资源:使企业的人力资源、财力资源和物资资源能够根据业务的需求实现最大化配置。 workflow 关注的是如何缩短流程闲置时间,从而提高企业的业务处理能力并使企业能够关注于真正对企业有意义的增值业务上。从建立企业神经系统的角度也许更能理解两者的区别。传统软件不能解决工作流的问题,例如ERP 关注的是企业的资源配置,但不可能解决资源传输过程中的损耗和降低传输(流程)的成本;同样workflow也不能完全解决传统管理软件所能解决的问题,例如对生产管理的MRP 系统所能解决的生产过程控制通过workflow很难实现。但一个好的传统软件如果希望能自动化地在整个企业中应用起来,必须有一个强大的逻辑层,用以解决信息传递的逻辑判断和自动流转,这个时候就需要workflow的平台。所以说: 1.workflow 和传统管理软件不是同一种软件,不具可比性; 2.workflow 对于已经有传统管理软件的企业的作用非常明显,可以籍此平台整合企业的各种应用系统,使之成为一个完整的企业级应用,也就是通常所说的EAI. 3. 具备workflow功能的管理软件(workflow与传统管理软件的结合)对于传统管理软件有绝对的优势;4.workflow可以根据企业的需要开发解决信息传递问题的流程以及帮助企业开发与现有应用系统的接口

工作流自动化并不复杂因为下述几个原因,工作流自动化业界有" 适合处理复杂业务流程" 的名声。

1.常规工作流自动化软件包及其部署相当昂贵。通常,伴随产品的是长时期的咨询关系。所以为了非常简单的业务流程购买和部署软件是被不被采纳的。这些软件通常只被用于复杂、关键和控制成本相对较高而工作流自动化带来的效益明显的量产型工作流应用。因此经销商和用户都会不自觉地关注于将复杂的业务问题自动化。 2. 处于类似原因,工作流研究人士首先会关注解决了哪些复杂的业务流程问题。

而对于大多数案例而言,为解决简单工作流程问题部署自动化软件的成本显然是不经济的。这里遵循一条简单的道理:走之前必须先会爬,跑之前必须先会走。 3. 最后一条原因,也是"IT 业的尴尬".总经理对IT部门经理工作衡量的标准就是:能够解决复杂问题的能力。自然,IT经理就会不遗余力地解决那些复杂的问题,他们的方案通常也就复杂而且昂贵。

所有这些目前都在改变。针对桌面电脑的应用方案快速发展以及工作流解决方案的发展使解决日常工作流程问题成为可能。费用不再昂贵,部署更为简便。事实上,企业越来越意识到工作流的重要性,同时在部署复杂关键的流程自动化之前,愿意从一些简单的流程入手积累经验。

工作流会成为操作系统的一部分吗?

有人认为工作流会成为操作系统平台(例如WINDOWS 平台)的一部分。我们的观点是,基于下述几个原因,在可预见的未来,工作流不会成为操作系统的一部分: 1. 扩展表格、文字处理程序和数据库存在了20多年,成了家喻户晓的名词。这些技术被广泛理解和应用,也相应形成了各自的标准和相关术语。然而因为很多原因,直到今天这些技术也没有成为操作系统的一部分。最重要的原因之一是用户需要差异和选择的自由。相比较而言,工作流自动化软件是较新的技术,也更有差异性、不易被广泛理解并且比这些技术更为先进。因为工作流程的差异性和复杂性,工作流自动化的用户需要更多的选择空间。

2.财务软件包从电脑发明后就迅速普及了。这是实施、术语和规则被普遍接受的另一个领域。然而至今也没有哪种操作系统吹嘘集成了多少财务软件的功能。而工作流自动化软件比财务软件更为复杂和有差异性。 3. 操作系统提供商,例如微软和Sun ,不会为了使其系统具备工作流自动化的功能而大量改变他们现有的系统。他们有什么必要为工作流自动化软件投入开发和支持的成本呢? 4. 操作系统是为常规条件设计并使之最优化。正因如此,目前操作系统的开发成本几乎都要上亿美元。业务流程十分复杂并充满了例外情况,如在操作系统中内嵌工作流自动化程序会极大地增加开发成本和难度。因此,即便操作系统提供商决定做工作流软件,也会是巨额投入开发一套新的操作系统,而不是将工作流嵌入。

事实上,今天的很多优秀的工作流解决方案集成了短信息、页面服务、目标管理、文件管理和其他一些操作系统才提供的服务。

工作流自动化的主要成分工作流自动化如今成了管理的一句时髦话。市面上也有很多号称能激活工作流的自动化产品。只要他们的应用程序支持基本的E-mail功能,卖主就会随意地把" 激活工作流" 作为标签贴在产品上。然而,这类产品和真正工作流自动化软件之间的差别就如同写字版和Word之间的差别。我们相信,应用程序只有具备了下列主要特征,才能称其为工作流自动化解决方案:

能够画出工作流程图,当然以图形化界面设计的为佳;能为每个步骤设计电子表格;能将外部应用程序结合为工作流自动化的一部分;能与电子表格及企业数据库相连接;能设计基于复杂业务规则的条件型路由的工作流程图,最好无须编程;能根据功能、用户名称或上下级关系按规则传递信息;能够监控工作流执行状况;能够对工作流进行调节;能够模拟并测试工作流的行为;工作流的应用必须支持多用户并具高度可靠性;工作流的应用必须支持内部网或英特网及跨多种平台。

网友讨论工作流应该是一个中间件而不应该是一个完整的系统。工作流应该整合到其他系统中而不是单独使用。

工作流要完成的核心功能有流程设计,流程执行,流程和线程的调度,任务的分派与通知,集成已有信息系统(很多人忘了)。

工作流应该提供对组织机构,用户,权限管理,流程,任务的管理能力,但是对这些管理能力最基本实现方式是提供API ,而不是一个管理系统,即使把这些管理作为一个管理系统来实现(A ),也主要是用于演示,因为当工作流用于其它系统(B ),因为B 需要一个统一的管理界面,所以通常不会直接使用A.而表单设计,报表之类根本就是外围功能,是二次开发商的任务。

我基本赞同wangtaoyy 的说法,再补充一点。我觉得工作流与其说是中间件,还不如说是一个应用整合和集成的框架。类似在j2ee规范下各产商开发的应用服务器,工作流也应当是在wfmc标准下开发出来的" 容器" ,只要是满足了标准的应用程序或组件都能够在这个" 容器" 中按照预定的规则被调度和执行。我认为理想情况下工作流系统不应该提供API 作二次开发,工作流的内部对基于工作流的应用程序应当是完全不透明的,工作流应当提供给开发者的是一个类似于J2EE那样的标准,一套编程模型和接口模型。开发者在这个模型下去实现那些接口,开发出应用组件,再利用工作流提供的管理器进行" 注册".总而言之,对开发者而言,工作流是黑箱,他需要做的事情是开发标准组件,在工作流提供的UI管理工具中配置业务流程,包括业务过程、资源、权限、时间、规则等等。

1. j2ee 应用服务器也是中间件的一种。
2. 工作流要做成j2ee哪样的标准还是比较困难的, j2ee 重点在于提供开发全新系统的能力,所以可以制定比较好的容器- 组件标准,而工作流的重点是整合已经存在的系统,要在这些各式各样的老系统上强加标准是不现实的。
3.工作流应该提供api ,因为其他系统中的一些事件可能会启动一个流程,或者触发其他与流程相关的东西

工作流分为两种类型,一种是嵌入式的,另一种是非嵌入式的。这在WFMC的文档中已经有所介绍,大家可以找找看一下。按照工作流管理联盟的文档,大家说的都没有什么错误,只是侧重点不同。wangtaoyy 的观点倾向于前者,而coffeewoo 的观点倾向于后者。

我的看法并不是趋向于嵌入式工作流。我理解的工作流提供的api 并不是一般软件包的API ,而是一种服务方式的API ,类似于操作系统中的系统调用。

我们在软件中大量使用了操作系统提供的系统调用API ,但是操作系统并不是嵌入到我们软件系统中的。我认为工作流系统与操作系统有很强的可比性,只是工作流层次更高。比如流程设计相当于编程,模型相当于程序,流程实例相当于进程,流程分支相当于线程,操作系统要对进程和线程进行调度,工作流引擎要对流程实例和分支进行调度,操作系统和工作流系统都应该对内存进行管理避免耗尽系统内存,操作系统提供系统调用API 而工作流引擎提供工作流API.何其相似。

从功能的角度看:工作流系统的本职工作就是管理和控制业务流程,例如:流程实例的启动、停止;环节实例的启动、结束;任务的分配等等。从工作流系统的组成看:工作流系统应该包括流程引擎、流程定义工具、运行管理工具、api 系统。工作流系统应该该包括表单定义、组织机构定义及其管理、权限管理、数据流管理等等。

工作流系统虽然不包括上述功能,但是工作流系统一定会和上述功能发生交互关系,所以好的工作流产品并不是一个包办上述功能的产品,而是一个设计良好的能够和上述功能交互的系统。从和其他系统的关系看待工作流:如果站在基础业务平台的角度,那么,工作流系统、组织机构管理系统、表单自定义系统、权限管理系统、数据流管理系统、报表系统都是这个基础业务平台的服务。业务功能系统在运行的过程中会调用这些服务,这些服务之间本身也可能互相调用。例如:工作流服务和组织机构管理服务之间的关系就非常密切,尽管如此,如果认为工作流系统一定包含组织机构管理系统应该是不正确的。在oa系统中,表单自定义好像比较重要,而且流程常常需要引用表单上的数据,但是表单自定义绝对不是工作流系统的组成部分。流程在运行的过程中可能跨多个数据库系统,任务在流转的过程中需要“携带”大量的业务数据,但是这些也不是工作流要做的事情,完成这些工作的系统我称之为“数据流管理系统”。总之:从功能的角度,所有的功能都是必要的,但是从技术的角度,这些功能不可以做到一个“铁板一块”的所谓的“工作流”里面去。从技术发展的趋势看:工作流系统很可能发展成为一个类似关系型数据库管理系统的专职的系统。我那个工作流东东还在改进中,希望作出一个设计合理的(决对不是强行coding出来的),工程实用的东西出来

posted @ 2006-06-01 14:12 崛起的程序员 阅读(1977) | 评论 (0)编辑 收藏

Note for Eclipse 3.1 Update Manager users: you can use special Eclipse update site for most plugins presented here. Go to "Help -> Software Updates -> Find and Install... -> Search for new features to install -> Next -> New Remote Site..." and use the http://andrei.gmxhome.de/eclipse/ as url.

Extended VS Presentation plugin for Eclipse: examples

Enable/disable Extended VS Presentation here

Enable/Disable Extended VS Presentation

Set custom properties here (this settings will override current theme settings)

Set custom tab properties here

 

Change selected theme properties here (this settings will override current theme settings)

Change selected Theme properties here
posted @ 2006-05-31 15:50 崛起的程序员 阅读(298) | 评论 (0)编辑 收藏

凡是接触过 Java 的人都知道 JRE 的概念,即 Java 运行时环境( Java Runtime Environment ),因为它是运行 Java 程序必不可少的(除非程序用 GCJ 等编译,但我怀疑这样处理后还能不能称之为“ Java 程序”了)。

Java 喊出的带有标志性的口号“ Write Once Run Anywhere (一次编写,到处运行)”(记得某老师给俺们上课讲到这里时还不忘幽一默:到处运行?没有计算机就运行不了……),正是建立在 JRE 的基础之上。何以实现?就是在 Java 应用程序和操作系统之间增加了一虚拟层—— JRE 。程序源代码不是直接编译、链接成机器代码,而是先转化到字节码( bytecode )这种特殊的中间形式,字节码再转换成机器码或系统调用。前者是传统的编译方法,生成的机器代码就不可避免地跟特殊的操作系统和特殊的机器结构相关,很多装双系统的用户无法在 Linux 运行 Windows 下的大型游戏,心里那个郁闷(于是很多虚拟软件和模拟程序应运而生)。而 Java 程序的字节码文件可以放到任意装有 JRE 的计算机运行,再由不同 JRE 的将它们转化成相应的机器代码,这就实现了 Java 程序的可移植性。这样程序员也不用去关心程序运行的具体环境,而可以专心编写软件。这种分层抽象、隐藏细节的思想在计算机科学中处处可见,比如机器组织结构的设计、网络协议的实现等。 Pascal 语言的发明者 Niklaus Wirth ,就富有预见性地指出应该有这样一种可移植的语言,其生成的中间代码可以在一台假想的机器( a hypothetical machine )上运行。而 Java 虚拟机( Java virtual machine JVM )就是这样的一台机器,它模拟实际处理器的结构,解释字节码。怎么一会说是 JRE ,一会儿又成了 JVM ,两者是否同物不同名?

回答是否定的。

JVM Java 平台的基础,和实际的机器一样,它也有自己的指令集,并且在运行时操作不同的内存区域。 JVM 通过抽象操作系统和 CPU 结构,提供了一种与平台无关的代码执行方法,即与特殊的实现方法、主机硬件、主机操作系统无关。但是在一些小的方面, JVM 的实现也是互不相同的,比如垃圾回收算法,线程调度算法(可能不同 OS 有不同的实现)。 JVM 的主要工作是解释自己的指令集(即字节码)到 CPU 的指令集或 OS 的系统调用,保护用户免被恶意程序骚扰。 JVM 对上层的 Java 源文件是不关心的,它关注的只是由源文件生成的类文件( class file )。类文件的组成包括 JVM 指令集,符号表以及一些补助信息。

JRE Sun 公司发布的一个更大的系统,它里面就有一个 JVM JRE 就与具体的 CPU 结构和操作系统有关,我们从 Sun 下载 JRE 的时候就看到了不同的各种版本。同 JVM 一起组成 JRE 的还有一些 API (如 awt swing 等)。 JRE 是运行 Java 程序必不可少的。

Over


posted @ 2006-05-22 22:30 崛起的程序员 阅读(710) | 评论 (1)编辑 收藏
一、读大学,究竟读什么?
  大学生和非大学生最主要的区别绝对不在于是否掌握了一门专业技能,一个经过独立思考而坚持错误观点的人比一个不假思索而接受正确观点的人更值得肯定,草木可以在校园年复一年地生长,而我们却注定要很快被另外一群人替代,尽管每次网到鱼的不过是一个网眼,但要想捕到鱼,就必须要编织一张网。
  
  二、人生规划:三岔路口的抉择
  不走弯路就是捷径,仕途,商界,学术。在这人生的三岔路口,你将何去何从,与其跟一百个人去竞争五个职位,不如跟一个人去竞争一个职位,学术精神天然的应当与尘嚣和喧哗保持足够的距离,商场不忌讳任何神话。你也完全可能成为下一个传奇。
  
  三、专业无冷热,学校无高低
  没有哪个用人单位会认为你代表了你的学校或者你的专业,既然是概率,就存在不止一种可能性,如果是选择学术,冷门专业比热门专业更容易获得成就,跨专业几乎早已成为一种流行一种时尚,大学之间的实力之争到了考研考场和人才市场原来是那样的微不足道。
  
  四、不可一业不专,不可只专一业
  千招会,不如一招熟,十个百分之十并不是百分之百,而是零,在这个现实的社会,真正实现个人价值才是最体面最有面子最有尊严的事情,要想知道需要学什么,最好的方式就是留意招聘信息,很多专业因为不具备专长的有效性,所以成为了屠龙之术,为什么不将“买一送一”的促销思维运用到求职应聘的过程中来呢。
  
  五、不逃课的学生不是好学生
  什么课都不逃,跟什么课都逃掉没什么两样,读大学,关键是学会思考问题的方法,逃课没有错,但是不要逃错课,英语角绝对不是学英语的地方,为了英语丢了专业,那就舍本逐末了,招聘单位是用人才的地方,而不是培养人才的地方,既要逃课,又要让老师给高分。
  
  六、勤工俭学的辩证法
  对于贫困生来说,首先要做的不是挣钱,而是省钱,大部分女生将电脑当成了影碟机,大部分男生将电脑当成了游戏机,在这个处女膜都可以随意伪造的年代,还有什么值得轻易相信,态度决定一切,当学习下降到次要的地位,大学生就只能说是兼职的学生了。
  
  七、做事不如做人,人脉决定成败
  学问好不如做事好,做事好不如做人好,会说话,就能减少奋斗三十年,一个人有多少钱并不是指他拥有多少钱的所有权,而是指他拥有多少钱的使用权,一个人赚的钱,12.5%是靠自身的知识,87.5%则来自人脉关系,三十岁以前靠专业赚钱,三十岁以后拿人脉赚钱,你和世界上的任何一个人之间只隔着四个人。
  
  八、互联网:倚天剑与达摩克利斯之剑
  花两个小时就写出一篇天衣无缝的优秀毕业论文,在互联网领域创业的技术门槛并不高,关键的是市场眼光和营销能力,轻舞飞扬已经红颜薄命了,而痞子蔡却继续跟别的女孩发生着一次又一次的亲密接触,很多大学生的网友遍布祖国大江南北,可他们却从未主动向周围的人说一声:你好,我们可以聊聊吗。
  
  九、恋爱:花开堪折方须折
  爱情是不期而至的,可以期待,但不可以制造,越是寂寞,越要警惕爱情,既然单身是可耻的,那西门庆是不是应该被评为宋朝十大杰出青年,花开堪折方须折,莫让鲜花败残枝,一个有一万块钱的人为你花掉一百元,你只占了他的百分之一;而一个只有十块钱的人为你花掉十块,你就成了他的全部。
  
  十、性:上帝死了,众神在堕落
  爱要说,爱要做,我只有在肉体一下一下的撞击中才感到快乐。经过之后,将是更大的寂寞更大的空虚,为何要让别人的虚荣成为对自己的伤害,当她机械地躺在床上张开双腿,她的父母正在憧憬着女儿的未来,一朝春尽红颜老,花落人亡两不知。
  
  十一、考研:痛苦的安乐死
  没有比浪费青春更失败的事情了,研究生扩招的速度是30%,也就意味着硕士学历贬值的速度是30%,同样是付出三年的努力,你可以让E1的值增加1,也可以让E2的值增加2甚至增加3,读完硕士或博士并不等于工作能力更强,面对13.54万的成本,你还会毫不犹豫地投资读研究生吗,努力就会有结果,但不一定是好结果。
  
  十二、留学:“海龟”变“海带”
  月薪2500元的工作,居然引得三个“海归”硕士争相竞聘,对于某些专业而言,去美国留学和去埃塞俄比亚留学没什么两样,既然全世界的公司都想到中国的市场上来瓜分蛋糕,为什么中国人还要一门心思到国外去留学然后给外国人打工。
  
  十三、非统招:养卑照样处优
  她在中国信息产业界创下了几项纪录。她被称为中国的“打工皇后”。而她不过是一名自考大专生,要想把曾经输掉的东西赢回来,就必须把自己比别人少付出的努力补上来,非统招生不但要有一定的实力,而且必须掌握一定的技巧,做到扬长避短出奇制胜,路在脚下。好走,走好。
  
  十四、毕业:十面埋伏的陷阱
  母校不把自己当母亲,你又何必把自己当儿女,听辅导班不过是花钱买踏实,人才市场就是一个地雷阵,通过多种方式求职固然没有错,但是千万不要饥不择食,只要用人单位一说要你交钱,你掉头就走便是了,这年头立字尚且不足以为据,更何况一个口头约定。
  
  十五、求职:做人不要太厚道
  求职简历必须突出自己的核心竞争力,求职的时候大可不必像严守一那样“有一说一”,一个人说假话并不难,难的是把假话说到底,并且不露一丝破绽,在填写自己的特长时,一定要尽可能详细,一份求职简历只要用一张A4纸做个表格就足够了,面试其实是有规律的,每次面试的时候只要背标准答案就行了。
  
  十六、骑一头能找千里马的驴
  美国铁路两条铁轨之间的标准距离是4英尺8.5英寸,为什么呢?因为两匹马臀部之间的宽度是4英尺8.5英寸,垃圾是放错位置的人才,世界上最大的悲剧莫过于有太多的年轻人从来没有发现自己真正想做什么,中小型企业或许能够让你得到更充分的锻炼,从基层做起并不意味着可以从基层的每一个职位做起,要“钱途”,更要前途。
  
  十七、写字楼政治:白领必修课
  大公司是做人,小公司是做事,职员能否得到提升,很大程度不在于是否努力,而在于老板对你的赏识程度,公司的事情和秘密永远比你想象的还要复杂和深奥,在适当的时候装糊涂不但是必要的,而且是睿智的,就把你的同事当成一群你可以叫得出名字的陌生人好了。
  
  十八、创业:29岁以前做富翁
  瘦死的骆驼比马大,撑死胆大的,饿死胆小的,不再是“大鱼吃小鱼”,而是“快鱼吃慢鱼”,对于趋势的把握是一个创业者最重要的能力,高科技行业留给毕业生的空间已经很小,欲速则不达。在创业以前通过给别人打工而积累经验是非常必要的,市场永远比产品更重要,钱不够花,怎么办?第一,看菜吃饭;第二,借鸡生蛋。

posted @ 2006-05-19 15:30 崛起的程序员 阅读(199) | 评论 (0)编辑 收藏

关于李煜
    李煜(937-978),初名从嘉,字重光,号钟隐,又号钟峰白莲居士,即位后改名煜。南唐中主第六子。徐州人。宋建隆二年(961年)在金陵即位,在位十五年,世称李后主。他嗣位的时候,南唐已奉宋正朔,苟安于江南一隅。宋开宝七年(974年),宋太祖屡次遣人诏其北上,均辞不去。同年十月,宋兵南下攻金陵。明年十一月城破,后主肉袒出降,被俘到汴京,封违命侯。太宗即位,进封陇西郡公。太平兴国三年(978)七夕是他四十二岁生日,宋太宗恨他 有“故国不堪回首月明中”之词,命人在宴会上下牵机药将他毒死。追封吴王,葬洛阳邙山。 后主前期词作风格绮丽柔靡,还不脱“花间”习气。国亡后在“日夕只以眼泪洗面”的软禁生涯中,以一首首泣尽以血的绝唱,使亡国之君成为千古词坛的“南面王”(清沈雄《古今词话》语),正是“国家不幸诗家幸, 话到沧桑语始工”。这些后期词作,凄凉悲壮,意境深远,已为苏辛所谓的 “豪放”派打下了伏笔,为词史上承前启后的大宗师,如王国维《人间词话 》所言:“词至李后主而眼界始大,感慨遂深。”至于其语句的清丽,音韵 的和谐,更是空前绝后的了。 后主本有集,已失传。现存词四十四首,其中几首前期作品或为他人所作,可以确定者仅三十八首。
    李煜李煜是多才多艺身,又是多福多苦命。
    说他是多才多艺身,是因为他工书善画,能诗擅词,又精通音乐。他玩什么,都能玩出名堂。他的书法,自成一家,创造出了“聚针钉”、“金错刀”、“撮襟”等体式。他作画,“远过常流,高出意外”(郭若虚《图画见闻志》)。他创作的乐曲,也很“奇绝”,亡国前创作的《念家山》、《念家山破》等乐曲,流行很广,“宫中民间日夜奏之,未及两月,传满江南”(邵思《雁门野说》”。他还会点武艺,像李璟,弧矢箭术都不赖。多才多艺、精通音律的李煜,写起小词来,自然是驾轻就熟。他能创造出《虞美人》之类的绝妙好词,一点也不奇怪。
    说他多福,首先是因为他原本无缘也无心做世子、当国主,却一不小心,当上了世子,做上了国主。他是李璟的第六子,当世子、做国主原本没有他的份儿。可憨人有憨福。他的长兄弘冀早就被立为世子,可弘冀莫名其妙地暴死,其他四位兄长又都早夭,这样李煜就成了“老大”,于是在建隆二年(961),顺利地被立为世子。同年,李璟病逝,李煜在金陵登上国主的宝,拥有了至高无上的地位。
    后主登基的时候,宋太祖赵匡胤已建言了宋王朝,统一了北方。只是大宋要一个个地收拾其他小国,一时还顾不上平定南唐。于是李后主有李会苟延残喘,做了十五年的小国之君。宋太祖曾说:“卧榻之下,岂容他人酣睡。”李煜居然在宋太祖的卧榻之下,小心翼翼地睡了十五年。这也算是有福气吧。
    他的福气,还不止这些。事业、爱情都美满,才是真正圆满的人生、幸福的人生。李煜的爱情,也挺幸福的。先后两个王后,都是天姿国色,而且多才多艺,跟他又情投意合。大周后,小字娥皇,精通书史,善解间律,特别擅长演奏琵琶。既会谱曲,又能填词,可惜没有词作流传下来。她还是一位时装和发型设计师,曾设计“高髻纤裳及首翘鬃朵之妆”,人人仿效。有这样一位可人儿作伴侣,叫后主片不心满意足?小周后,是娥皇的妹妹,也是“警敏有才思,神彩端静”,风情万种。后主与小周后婚前的爱情,更具有刺激性,更令他心荡神怡。如果说大周后要是引发了后主对音乐的兴趣爱好,间接促进了后主词的创作,那么小周后与后主婚前的频频幽会,则是直接为后主的词作提供了素材。后主词中的“刬袜步香阶,手提金缕鞋”,就是写与小周后的恋爱经历。

菩萨蛮

花明月黯笼轻雾,今霄好向郎边去!

刬袜步香阶,手提金缕鞋。

画堂南畔见,一向偎人颤。

奴为出来难,教君恣意怜。

    李后主算不上好国主,却是一个优秀的词人,有着艺术家的天真和坦诚。身为国主,连自己的恋爱故事也不隐瞒,公开写到词里让人传唱,这是多么坦率多情!也许是他觉得这场恋爱太惬意太美满,以致于情不自禁,跟情人约会之后就把约会的情景用词记录下来,作为刻骨铬心的永恒纪念。
    据马令《南唐书》记载,在大周后病重期间,后主就与她的小妹相恋。娥皇死后,后主娶她小妹为继室,世称小周后。这首词就是写他与小周后婚前的一次约会。
    词以第一人称的口吻,让小周后自述约会的过程,表现她的多情与主动。后主真诚坦率之中,也有狭义狯的一面。他不说自己相约对方,而是说她如何主动大胆。在这场不太合乎当时伦理规范的恋爱中,他似乎是被动的。当然,他们爱得非常意愿。名人的恋爱故事本来就吸引人,风流帝王的恋爱故事更有魅力。这首词的魅力,倒不仅仅是因为名人效应,艺术上也相当成功。几个细节和动作,就把人物写得活灵活现。手提绣鞋,刬袜潜来,见出女子的谨慎心细,生怕被别人发现。“偎人颤”,传神地写出她与情郎见面时既紧张又惊喜的神态。词是专门抒情的诗体,但有些词抒情时隐含着故事,或者说是因事言情,在讲故事时表达情思。词中这种因事言情的物法,韦庄是最先使用的。他的《荷叶杯》上片回忆当年与情人约会的故事:“记得那年花下。深夜。初识谢娘时。水堂西面画帘垂。携手暗相期。”把约会的时间、地点、过程交代得一清二楚。词中这类故事到底是讲词人的经历,还是别人的事情,虽然很难判断,但总有一种逼真感、现场感。这种像是亲身经历的感情故事更能引起读者听众的兴趣。到了宋代,欧阳修和柳永的词也常常使用这种手法,在浓浓的情思中隐含着模糊的故事。

浪淘沙

往事只堪哀,对景难排。

秋风庭院藓侵阶。

一任珠帘闲不卷,终日谁来?

金剑已沉埋,壮气蒿莱。

晚凉天净月华开。

想得玉楼瑶殿影,空照秦淮!

    此词为入宋后抒写幽闭时心情。
   “往事只堪哀”,是说想起往事就悲哀,而不是说想起悲哀的往事。后主被俘入宋后,总是难忘故国的“往事”。《虞美人》词说“往事知多少”;《菩萨蛮》词说“往事已成空”,可见他的“往事”是指过去欢乐“往事”。如今触目皆悲,所以想起欢乐的往事,更倍增伤感。开篇流露的是幸福的失落感,接下来表现的是沉重的孤独感。庭院长满了苔藓,可见环境的极度荒凉冷清。室内也是死气沉沉。珠帘不卷,既是无人卷,也是无心卷帘。户外荒凉,触目肠断,不如呆在室内消磨时光。可长期龟缩幽闭一室,内心的孤独还是不能排解。他在期盼人来,期盼着与人交流、倾诉,可等待“终日”,不见人来,也无人敢来。据宋人王铚《默记》记载,后主在汴京开封的住处,每天都有“一老卒守门”,并“有旨不得与外人接”。李煜在汴京,实质是被软禁的囚徒。他明明知道没有人愿意来看望,也没有人敢来看望,却偏偏说“终日”有“谁来 ”。他是在失望中期盼,在期盼中绝望。这就是李后主的心态。
    在极度孤独中度日的李煜,打发时光、排遣苦闷的最好方式是回忆往事。金剑沉埋于废墟,壮气消沉于荒草,复国的机会与可能是一点儿也没有了,只好任命吧!就这样过一天算一天吧!
    上片写的是白天,下片写晚上,晚凉天静,月华普照,全词的境界闪出一丝亮色,主人公的心情也为之开朗。可这月亮已非故乡之月,就像建安时期王粲《登楼赋》所说的“虽信美而吾土”。于是他由月亮想到当年月光照耀下的秦淮河畔的故国宫殿。但玉楼瑶殿已非我有,明月照得再亮,也只能徒增伤感。后主总是这么执着地留恋过去,故国成了他解不开的情结。故国情结是他后期词作的一大主题,也是他打发孤独寂寞时光的一副强心剂。但故国情结并不能解脱心中的屈辱与痛苦。他靠回忆过去打发时光。可是一旦从过去的往事中回到现实,又痛苦不堪。这样周而复始,后主深深地陷入了无法解开的心理怪圈。
    由王子、君主到臣虏,李后主的人生经历了巨大的反差。正如唐圭璋在《李后主》评传》中所说:“他身为国王,富贵繁华到了极点,而身经亡国,繁华消竭,不堪回首,悲哀也到了极点。正因为他经历过这种极端的悲乐,遂使他在文学上的收成也格外光荣而伟大。在欢乐的词里,我们看见一朵朵美丽之花;在悲哀的词里,我们看见一缕缕的血痕泪痕。”
    然而富贵繁华也好,悲哀血泪也好,这些都违背了李煜的本愿。他的最初理想是做一个远离尘寰逍遥山林的隐逸之士。在《即位上宋太祖表》中,李后主说的很明白:臣本于诸子,实愧非才。自出胶庠,心疏利禄。……思追巢、许之余尘,远慕夷、齐之高义。意思是说:当君主并不符合自己本来的志愿。自己的理想只是追步巢父、许由的后尘,做一个像伯夷、叔齐式的隐士。他为自己取了许多别号,自称“钟山隐士”、“钟锋隐者”、“钟锋隐居”、“莲峰居士”,省称曰“钟隐”。可知他心神向往的,完完全全是超越尘俗的山林生活啊!
    正是:林花谢了春红, 太匆匆。无奈朝来寒雨,晚来风!

posted @ 2006-05-19 15:23 崛起的程序员 阅读(195) | 评论 (0)编辑 收藏

演讲人:浙大高分子物理郑强教授
地点: 浙江图书馆报告厅
  精彩语录节选:
  “在中国这个发展中国家,你能建10所世界一流大学,那美国有多少所?日本有多少所?现在的实际状况是:世界上前200所大学,中国一所都排不进!在亚洲能排出几所?我到国外去看了以后,感到要将浙大建成世界一流大学就像共产主义理想.“
  “以前说“无知无畏“,现在却是“无知才无畏“,许多企业把浙江省技术监督局、科委的人请来吃一顿饭,喝一点酒,他就给你签个字,再把我们这些教授胁迫到那里去,给你盖个章,然后就是“填补国内外空白“、“国际先进水平“,写论文则是“国际领先水平的研究成果“、“首次科学发现“等等,这都是目前非常严重的问题!作为一个大学教授,我深深地为此担忧!这不是我们的责任,是我们的领导无知,是他们倡导了这个主流.我知道在座的处长或老总日子很难过,因为你们不写这样的报表,就拿不到钱,项目就得不到批准.教授也同样如此,天天写报告,而不是在实验室静下心来好好搞研究,这是很严重的!“
  “我们国家的现实和发展就是这样:凡是依赖不成的,我们自己都能搞得像模像样,比如二弹一星;凡是能够引进的,就都搞不成.......现在很多合资企业就这样,卖点东西,而没有去考虑这些深层次的东西.殊不知,这就是社会的恶性循环!“
  “我认为:语言、计算机就是工具.中国的外语教授讲英语还不如美国卖菜的农民!怎么看待这个问题?日本博士、德国教授说不出英语的多得是!我们怎么能说一个人不会说英语就是文盲呢?语言就是一个工具!你没有那个环境,他怎么能讲这个语言呢?......如果我是教育部长,我要改革二件事:第一,取消六级考试,你一个研究生连中文一级都不及格,你英文考六级干什么呢?看看研究生写得论文,自己的民族文化都没有学好,天天考英语──打勾:托福打勾,GRE打勾,英文考出很高的分,可哪个写的英文论文在我面前过得了关呢?过不了关!这样培养出来的人能干什么?自己搞的专业一点都没学好!......说不会计算机就是文盲,这又是一个误区!我现在是教授,我顾不上搞计算机!“
  “你看我,从高中开始学英语,大学学,硕士学,博士学,花了我多少精力!你说中国人怎么做得出高科技的研究成果?我这几天就教训我手下的几位女学生,问她们在干什么,看不到人影,一天到晚考这样、考那样的,到美国去干什么?在国内要干的事多着呢!你整天考英语,美国人连报个名都要收你们的钱,日本人也是如此,中国学生到日本去要交手续费,到日本留学是为日本人打工,好不容易挣点钱交了学费,读完博士在日本的公司就职,当劳动力,挣了一笔钱后要回国了就买了家电,把钱全给了日本人.你们都没有注意这件事,这里面都是经济问题.这就是素质教育到底是什么.“
  “中小学的教育就是听话,老师管干部,干部管同学,孩子们都学会了成年化的处世方式,这是害人啊!这样强迫性地做了一些好事后,没有把做好事与做人准则结合起来去培养,而只学会了拍马屁、讨老师喜欢、说成人话.上次电视上就曾经播出,一个小孩得了个奖,主诗人问他最愿意说什么,他说:“我最愿意跟江爷爷说:我向你报告!““江爷爷“是谁?还不是老师教的!孩子们在中小学活得很累,到了大学就没人管了,所以就要玩、就要谈恋爱.
  “我们有很多同学成绩好,却什么都做不了.在我们大学像我这种程度的人,招博士生是从来不看成绩的,成绩算什么!现在我从事的这个领域在中国有叁个杰出的人才,当初在读研究生时都补考过,而成绩考得好的几个人却都跑到美国去卖中药了,这说明了什么问题?作老板的可不能这样啊!......人才的梯队一定要合理,而不要认为教授就是万能的、博士就是万能的.中国的教育体系就是让每一个老百姓都充满希望和理想,教育孩子们要树立远大的理想.实际上,人的能力是不一样的,扫地能扫好,也应该受到尊重;打扫厕所能打扫干净,也应该受到尊重,不能动不动就要高学历.我要提醒的是:在国外可不是这样,美国、日本的博士就很难找到工作,为什么?因为老板心疼钱,招了博士要给他高工资,而他能做什么用呢?这是个具体问题.“
  “科技到底该干什么?高科技到底该干什么?如果我是科技部长,该玩的就玩,就像陈景润,他就是玩!陈景润如果是处在今天的中国,他绝对是要去讨饭的,因为他不会去搞产业化,他的英语也不好,他说话都不流利,中文都讲不好,按现在“标准“,他是个文盲,还谈什么教授!日本人就是喜欢美国人,我跟日本人说:你们这个民族爱谁,谁就要向你们扔原子弹.日本人就是喜欢黑人也不喜欢中国人.......我特别对我们的女教授、女同学说:在日本人面前一句日文都不要讲,会也不要讲;日本人一听说你讲英文,特别是看到中国女孩讲英文,腿都要发软,这是真的!“
  “中国人为什么这些年都往外跑,最重要的是要让国民自己爱自己国家.......如果我是杭州的市长,我绝对不是狭隘的民族自尊心──如果杭州有什么灾难,我就首先把杭州的老百姓安排在香格里拉,让外国人在外面排队!(掌声!)这样,你才会让你的国民爱自己的国家!一个日本的农民跑到峨嵋山去玩,骨头摔断了,你就用中国空军的直升飞机去救他,而在日本大学一名中国留学生在宿舍里死了7天才被发现;名古屋大学的一对中国博士夫妇和孩子误食有毒磨菇,孩子和母亲死了,父亲则是重症肝炎,在名古屋大学医学院的门诊室等了12个小时,也没有一个日本教授来看望!而你们为什么还要这么友好,以为自己很大度,实际上是被人家耻笑,笑你的无知!你们这个民族*!我们不能这样!我们的领导人跑到国外去访问,看到有几个人在欢迎他们,就感到挺有面子;而外国来了个什么人物,都是警车开道,这究竟是怎么回事?这让我们中国人感到是自豪还是悲哀?所有这些,对教育工作来讲,都是深层次的问题.所以我经常讲,我作为一位自然科学工作者,我教育我的学生,首先是学会做人,没有这些,你学了高分子,外语都是花架子,你不是一个完整的人!“
  “一个观念或是一个问题:是不是技术越新越好?今天谈的就都是提醒大家的......技术并不是越新越好,技术要有储备.日本的企业现在卖的东西大都是10年或15年前的技术,好东西他不拿出来,他要等到现有的技术把成本收回并获得尽可能高额的利润以后才会拿出来.其次,我的对手什么时候推出新东西时,我才会出手.不要以为你今天好不容易搞了生产线,明天又有新的了,你的钱还没赚到就换新的,有什么用?我这次到日本刚好谈到悬浮列车──即使何先生在我也要说,这是中国人又在玩高新科技.悬浮列车目前在理论上都还不成熟.日本现在最完善,最经济的就是新干线!
从经济和市场的概念来讲,越先进的东西,风险越大,有可能得到的回报就越少!"

 

posted @ 2006-05-19 14:23 崛起的程序员 阅读(148) | 评论 (0)编辑 收藏
java.lang.NoSuchFieldError: tokenTypeToASTClassMap
        at org.hibernate.hql.antlr.HqlBaseParser.buildTokenTypeASTClassMap(HqlBaseParser.java:4224)
        at org.hibernate.hql.antlr.HqlBaseParser.<init>(HqlBaseParser.java:107)
        at org.hibernate.hql.antlr.HqlBaseParser.<init>(HqlBaseParser.java:112)
        at org.hibernate.hql.ast.HqlParser.<init>(HqlParser.java:47)
        at org.hibernate.hql.ast.HqlParser.getInstance(HqlParser.java:42)
        at org.hibernate.hql.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:203)
        at org.hibernate.hql.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:127)
        at org.hibernate.hql.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:83)
        at org.hibernate.impl.SessionFactoryImpl.getQuery(SessionFactoryImpl.java:422)
        at org.hibernate.impl.SessionFactoryImpl.checkNamedQueries(SessionFactoryImpl.java:383)
        at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:286)
很简单:查看antlr.jar这个包,把它替换成与当前版本hibernate统一的antlr包,其实也就包的版本不同一所造成的。
posted @ 2006-05-18 20:07 崛起的程序员 阅读(825) | 评论 (0)编辑 收藏

1、新建一个启动画面Window类
  1. java.awt.Window windowSplash;

2、调用prepareSplash()函数,初始化启动界面
  1.     private void prepareSplash()
  2.     {
  3.         Toolkit toolkit = Toolkit.getDefaultToolkit();
  4.         windowSplash = new Windowthis );
  5.         Image image = toolkit.getImage( "images" + File.separator + "splash.gif" );
  6.         ImageCanvas canvas = new ImageCanvas( image );
  7.         windowSplash.add( canvas, "Center" );
  8.         Dimension scmSize = toolkit.getScreenSize();
  9.         int imgWidth = image.getWidth( this );
  10.         int imgHeight = image.getHeight( this );
  11.         windowSplash.setLocation( scmSize.width2 - (imgWidth2), scmSize.height2 - (imgHeight2) );
  12.         windowSplash.setSize( imgWidth, imgHeight );
  13.     }

3、在Application的JFrame类(主界面)中调用startSplash(),显示启动界面,然后初试
化JFrame的各个可视化组件,初始化后台数据库等(如数据库的连接)
  1.     private void startSplash()
  2.     {
  3.         windowSplash.setVisible( true );
  4.         windowSplash.toFront();
  5.     }

4、在所有的初始化工作完成之后,调用stopSplash()函数,停止显示启动画面
  1.     private void stopSplash() {
  2.         windowSplash.dispose();
  3.     }
posted @ 2006-05-18 13:11 崛起的程序员 阅读(271) | 评论 (0)编辑 收藏
作者:金蝶中间件公司CTO袁红岗
不知不觉做软件已经做了十年,有成功的喜悦,也有失败的痛苦,但总不敢称自己是高手,因为和我心目中真正的高手们比起来,还差的太远。世界上并没有成为高手的捷径,但一些基本原则是可以遵循的。
  1. 扎实的基础。数据结构、离散数学、编译原理,这些是所有计算机科学的基础,如果不掌握他们,很难写出高水平的程序。据我的观察,学计算机专业的人比学其他专业的人更能写出高质量的软件。程序人人都会写,但当你发现写到一定程度很难再提高的时候,就应该想想是不是要回过头来学学这些最基本的理论。不要一开始就去学OOP,即使你再精通OOP,遇到一些基本算法的时候可能也会束手无策。
  2. 丰富的想象力。不要拘泥于固定的思维方式,遇到问题的时候要多想几种解决问题的方案,试试别人从没想过的方法。丰富的想象力是建立在丰富的知识的基础上,除计算机以外,多涉猎其他的学科,比如天文、物理、数学等等。另外,多看科幻电影也是一个很好的途径。
  3. 最简单的是最好的。这也许是所有科学都遵循的一条准则,如此复杂的质能互换原理在爱因斯坦眼里不过是一个简单得不能再简单的公式:E=mc2。简单的方法更容易被人理解,更容易实现,也更容易维护。遇到问题时要优先考虑最简单的方案,只有简单方案不能满足要求时再考虑复杂的方案。
  4. 不钻牛角尖。当你遇到障碍的时候,不妨暂时远离电脑,看看窗外的风景,听听轻音乐,和朋友聊聊天。当我遇到难题的时候会去玩游戏,而且是那种极暴力的打斗类游戏,当负责游戏的那部分大脑细胞极度亢奋的时候,负责编程的那部分大脑细胞就得到了充分的休息。当重新开始工作的时候,我会发现那些难题现在竟然可以迎刃而解。
  5. 对答案的渴求。人类自然科学的发展史就是一个渴求得到答案的过程,即使只能知道答案的一小部分也值得我们去付出。只要你坚定信念,一定要找到问题的答案,你才会付出精力去探索,即使最后没有得到答案,在过程中你也会学到很多东西。
  6. 多与别人交流。三人行必有我师,也许在一次和别人不经意的谈话中,就可以迸出灵感的火花。多上上网,看看别人对同一问题的看法,会给你很大的启发。
  7. 良好的编程风格。注意养成良好的习惯,代码的缩进编排,变量的命名规则要始终保持一致。大家都知道如何排除代码中错误,却往往忽视了对注释的排错。注释是程序的一个重要组成部分,它可以使你的代码更容易理解,而如果代码已经清楚地表达了你的思想,就不必再加注释了,如果注释和代码不一致,那就更加糟糕。
  8. 韧性和毅力。这也许是"高手"和一般程序员最大的区别。A good programming is 99 weat and 1ffee。高手们并不是天才,他们是在无数个日日夜夜中磨练出来的。成功能给我们带来无比的喜悦,但过程却是无比的枯燥乏味。你不妨做个测试,找个 10000以内的素数表,把它们全都抄下来,然后再检查三遍,如果能够不间断地完成这一工作,你就可以满足这一条。
  这些是我这几年程序员生涯的一点体会,希望能够给大家有所帮助
posted @ 2006-05-16 14:35 崛起的程序员 阅读(298) | 评论 (0)编辑 收藏
创业者要遵循的5个基本做事方法

对于想创业的人,我对他们的提醒是:不要被成功企业所编写的创业故事所迷惑,因为那里讲的都是一个企业成长过程中过五关斩六将的最精彩的部分。作为创业者,有5个很基本的做事方法你必须遵循。

我鼓励大家创业,但鼓励大家创业不等于盲目创业。对于想创业的人,我对他们的提醒是:不要被成功企业所编写的创业故事所迷惑,因为那里讲的都是一个企业成长过程中过五关斩六将的最精彩的部分。你要用自己的头脑去思考,去学习。作为创业者,有5个很基本的做事方法必须遵循:


第一,一定要有激情和理念,你才能感染自己和其他人。在最困难的时候,在所有人绝望的时候,你要感染你的客户,感染你的员工,感染你的合作伙伴……你要感染所有人!

第二,要专注。别说小公司,大公司多元化也有失败的例子,小公司更应该抓准一个点把它做深、做透。这样才能积累所有的资源。小公司到处试验,会让你的企业耗尽很多资源。专注就是有所不为才能有所为,这点非常重要。

3721坚持了6年,一直坚持做中文上网、中文搜索,我就做这一件事情;Google也是很专注,有了一定的积累以后才能横向扩展。很多创业者都栽在不够专注上,是因为他自己脑子里面没有想清楚,今天在这儿打一口井,明天在那儿打一口井,最后哪儿也没有挖出水,地面上只是留下了许多坑而已。你5个指头都叉开和一个拳头是不一样的,专注就是你把所有的资源都凝聚在一个点上。

第三是执行力,很多创业者很容易在这里摔跟头。我见过很多人夸夸其谈,估计他们创业,至少第一次肯定都不会成功,因为我觉得真正的创业者要少说多做,要把自己美妙的想法努力转化成结果。

创业者做事情着眼点要低,要现实。很多创业者去见风险投资人的时候,说起自己的事业都说得云山雾罩的,其实这样会让风险投资人根本不明白你在说什么。你只要踏踏实实地说你想做什么,这个东西能解决什么问题就可以了。

想法只是一个开头的方式,是不值钱的。我们坐在这儿,一个小时可以天马行空,弄出几十个想法来,脑子稍微一转,你的思想已经在宇宙走了好几个来回了—- 行动的成本才是最高的,对创业者来讲要看自己是不是有这种经验和执行力。同样的想法两个人同样做,谁的执行力更强,谁的经验更丰富,谁就更容易成功。

第四,创业者还要有一种胸怀,就是所谓与时俱进的学习能力。我看到很多人创业不成功就是因为他们太自负,不能从成功人士那里学到一些优点,听不进好的建议。很多创业者没有经验,没有经验不可怕,问题是你有没有谦虚、开放学习的心态,使你不能与时俱进。很多创业者会陷入一个死循环,他们通常认为自己看得准,才是出手的前提;积累经验越多,才能越看越准。但你没经验,又怎么可能看准?

解决这个问题有办法,时机不成熟,就不创业,先给别人打工。把公司让我做的事情做好,提高自己的能力,逐步就知道创业的方向了。我不赞成年轻人刚毕业就创业,我认为他们还是应该在公司里踏踏实实干五六年,虽然是打工,实际上是公司在给你“缴”学费,你在不同的平台通过积累经验,这是任何老板剥夺不走的,只有积累这种经验,你的创业能力才更高,才更有把握。我在方正工作时,从来没有觉得自己是打工,我一直认为自己是在创业,因为我觉得是在积累自己的能力,积累自己的资源。客观上,我对方正还是作了很多贡献。我到雅虎还是一种创业的心态,我把雅虎的文化改成创业的文化,因此雅虎中国在2004年取得了很好的业绩。保持良好的心态,这是你创业成功的前提。

第五,不要盲目去模仿和抄袭大公司的做法。比如,很多人在新浪、搜狐做过,他出来就会不自觉按照大公司的做法建立一些规范制度等,但大公司为了稳妥,一般都比较慢。大公司为这个“慢”付得起代价,小公司不能用大公司的这种做事方法。我讲过大象和兔子的故事:大象和骆驼3天不吃也没事,但是新创业的公司像小兔子一样,每一步都要跑得快,要到处找食。本来就是个兔子,却以为自己是个大象,用大象的心态做事,在狼面前慢慢踱步,最后就会被狼吃掉。创业意味着你要有创业的做事方式。

如果你跟大公司做一样的事,他的实力很强,跟他比是没有优势的。因此,如果把整个产业画成一张地图,你可以看哪些领域被谁占了,谁有什么优势。你应该找一个不在这张地图上的事情去做。比如说,前几年大家都不重视的搜索,现在就做起来了。创业公司应该踏踏实实把自己的事做好,不要在自己很小的时候就想要通吃,要颠覆,要灭掉谁,这是没有意义的。小公司要学会跟大公司合作,要学会广交朋友,在这个产业链跟别人合作,会使自己成功得更快一些。

周鸿祎:1970年10月生于湖北,1995年毕业于西安交大管理学院系统工程系,获硕士学位。曾就职方正集团,先后任研发中心副主任、事业部总经理等职。1998年10月,创建3721公司。2003年3721被雅虎收购,2004年3月出任雅虎中国总裁。2005年7月6日,宣布将于8月31日正式辞去雅虎中国总裁一职。9月1日,他将以投资合伙人的身份正式加盟IDGVC(国际数据集团风险投资基金)。

posted @ 2006-05-16 10:53 崛起的程序员 阅读(223) | 评论 (0)编辑 收藏
Eclipse Communication Framework (ECF)  是一个新的ECLIPSE项目,目的是为了提供一个开源的通讯框架,让开发者可以更容易开发出通讯相关的项目。

IBM的文章Getting started with the Eclipse Communication Framework 介绍了ECF,同时告诉我们如何开始使用ECF,同时还提供了一个基于Yahoo IM 的例子。

Getting started with the Eclipse Communication Framework

http://www.ibm.com/developerworks/opensource/library/os-ecl-commfwk/?ca=dgr-jw22ECF

下载地址
http://www.eclipse.org/ecf/downloads.html
引用页 http://www.eclipse.org/downloads/download.php?file=/technology/ecf/org.eclipse.ecf.sdk-0.8.0.S20060507.zip

安装完毕插件后,起服务目录在
$:\eclipse\features\org.eclipse.ecf.serverfeature_0.8.0\bin
startserver.cmd
然后打开eclipse 菜单ECF链接选择协议ecf generic 后设置服务器url例如http://localhost:3282/server,输入别名
进入强大的聊天室.让我们一起XP 编程吧

posted @ 2006-05-12 17:41 崛起的程序员 阅读(1055) | 评论 (0)编辑 收藏

一、6sigma管理的来源 

六西格玛(6σ或SixSigma)最早作为一种突破性的质量管理战略在八十年代末在摩托罗拉公司成型并付诸实践,三年后该公司的六西格玛质量战略取得了空前的成功:产品的不合格率从百万分之6210(大约四西格玛)减少到百万分之32(5.5西格玛),在此过程中节约成本超过20亿美金。随后即有德仪公司和联信公司(后与霍尼维尔合并)在各自的制造流程全面推广六西格玛质量战略。但真正把这一高度有效的质量战略变成管理哲学和实践,从而形成一种企业文化的是在杰克·韦尔奇领导下的通用电气公司。 

该公司在1996年初开始把六西格玛作为一种管理战略列在其三大公司战略举措之首(另外两个是全球化和服务业),在公司全面推行六西格玛的流程变革方法。而六西格玛也逐渐从一种质量管理方法变成了一个高度有效的企业流程设计、改造和优化技术,继而成为世界上追求管理卓越性的企业最为重要的战略举措,这些公司迅速运用六西格玛的管理思想于企业管理的各个方面,为组织在全球化、信息化的竞争环境中处于不败之地建立了坚实的管理和领导基础。 


二、6sigma管理的发展 

继摩托罗拉、德仪、联信/霍尼维尔、通用电气等先驱之后,几乎所有的财富500强的制造型企业都陆续开始实施六西格玛管理战略。值得注意的是,一直在质量领域领先全球的日本企业也在九十年代后期纷纷加入实施六西格玛的行列,这其中包括索尼、东芝、本田等。韩国的三星、LG也开始了向六西格玛进军的旅程。

另一值得注意的现象是自通用电气之后,所有公司都将六西格玛战略应用于组织的全部业务流程的优化,而不仅仅局限于制造流程。更有越来越多的服务性企业,如美国最大的花旗银行、全球最大的B2C网站公司Amazon.com等也成功的采用六西格玛战略来提高服务质量、维护高的客户忠诚度,所以六西格玛已不再是一种单纯的、面向制造性业务流程的质量管理方法,同时也是一种有效的提高服务性业务流程的管理方法和战略。更有一些政府机构也开始采用六西格玛的方法来改善政府服务。

目前,美国公司的平均水平已从十年前的三西格玛上下提高到了接近五西格玛的程度,而日本则已超过了5.5西格玛的水平。可以毫不夸张的说西格玛水平已成为衡量一个国家综合实力与竞争力的最有效的指标。 



【关于6sigma管理的含义】 

一、何谓6sigma 

6Sigma的概念最早由摩托罗拉提出,推行六西格玛之后的摩托罗拉,产生巨大的蜕变,在品质方面超越当时的日本。但是,让六西格玛在短短几年内,成为许多世界级企业争相投入,作为降低成本、提高竞争力最大妙方的最大功臣,则是美国通用公司的杰克·韦尔奇。 

Sigma(中文译名‘西格玛’)是希腊字母σ的中文译音,统计学上用来表示“标准偏差”,即数据的分散程度。6sigma即意为“6倍标准偏差”。
 
在质量上,6Sigma表示每百万个产品的不良品率(PPM)不大于3.4,意味着每一百万个产品中最多只有3.4个不合格品,即合格率是99.99966%。在整个企业流程中,6Sigma是指每百万个机会当中缺陷率或失误率不大于3.4,这些缺陷或失误包括产品本身以及采购、研发、产品生产的流程、包装、库存、运输、交货期、维修、系统故障、服务、市场、财务、人事、不可抗力……等等。流程的长期西格玛值与不良品率有如下关系: 
不良品率合格率(%)西格玛值 
3.499.999666 
23099.9775 
620099.384 
6680093.323 


【事例】

举一个航空公司的例子,如果某一航班的预计到达时间是下午五点,由于各种原因,真正在五点准时到达的情况是极少的。假如我们允许在五点半之前到达都算准点到达,一年里该航班共运营了200次,显然到达时间是个变量。如果其中的55次超过五点半到达,从质量管理的角度来说,这就是不良品,所以航空公司这一航班的合格品率为72.5%,大约为2.1个西格玛。如果该航班的准点率达到六西格玛,这意味着每一百万次飞行中仅有3.4次超过五点半到达,如果该航班每天运行一次,这相当于每805年才出现一次晚点到达的现象。所以六西格玛的业务流程几乎是完美的。对于制造性业务流程来说,在有均值漂移1.5σ的情况下六西格玛意味着每一百万次加工只有3.4个不良品。这个水平也叫做流程的长期的西格玛值。 


二、何谓6sigma管理 

6sigma管理即要求企业在整个流程中(而不仅限于产品质量),每百万个机会中的缺陷率少于3.4,这对企业来说是一个很高的目标。 

随着将近二十年来的应用发展,六西格玛已由原先摩托罗拉创建成型的质量管理战略上升到了一整套系统的使公司达到世界级的质量和竞争力的管理策略和技术手段。其实理解6Sigma不需要很深的统计学技术或背景,事实上,“6Sigma是什么”能以各种不同的方式回答。如果概括地回答的话,可以说6Sigma管理是:“寻求同时增加顾客满意和企业经济增长的经营战略途径。”即: 
◆在提高顾客满意程度的同时降低经营成本和周期的过程革新方法; 
◆通过提高组织核心过程的运行质量,进而提升企业赢利能力的管理方式; 
◆在新经济环境下企业获得竞争力和持续发展能力的经营策略。 

在这里我们将简洁、条理地定义6Sigma为: 
1、衡量企业产品质量、整体运作流程质量及整体竞争力水平的方法; 
2、改进企业产品质量、整体运作流程质量及提升核心竞争力的方法; 
3、真正实现卓越业绩和持续领先的管理哲学和方法论。 

6sigma管理的核心理念实际上不仅是一个质量上的标准,它更代表着一种全新的管理理念,即要企业改变过去那种“我一直都这样做,而且做得很好”的思想,因为尽管过去确实已经做得很好,但是离6sigma管理的目标还差得很远。 


三、受益于6sigma管理的企业和组织 

摩托罗拉公司在1986年率先提出6sigma管理模式并在企业中推行。自从采取6sigma管理后,该公司平均每年提高生产率12.3%,因质量缺陷造成的损失减少了84%,摩托罗拉公司因此取得了巨大的成功,成为世界著名跨国公司,并于1998年获得美国鲍德里奇国家质量管理奖。美国通用电气公司(GE)自1995年推行6sigma管理模式以来,由此所产生的效益每年呈加速度递增:每年节省的成本为1997年3亿美元、1998年7.5亿美元、1999年15亿美元;利润率从1995年的13.6%提升到1998年的16.7%。 

6sigma的最大受益组织之一通用公司的前CEO杰克·韦尔奇曾评价说:“6sigma是GE公司历史上最重要、最有价值、最赢利的事业。我们的目标是成为一个6sigma公司,这将意味着公司的产品、服务、交易零缺陷。”……“6Sigma是管理工具中最强有力的、最有突破性的,它适用于名种公司用来增加市场份额、降低成本及提高利益率底线。”……“六个西格玛所包含的中心思想是,在一个过程中您能否测量出有多少‘缺陷’,以及您能否系统地找出消除它们的方式,并尽可能地接近‘零缺陷’。六个西格玛已经改变了通用电气公司的遗传基因——现在,它贯穿我们所做的每一件事情,融入我们设计的每一件产品,成为了我们的运作方式。” 

6sigma管理已成功地应用于许多世界著名的大公司和组织,如摩托罗拉-1987、德州仪器-1988、ABB-1993、霍尼韦尔-1994、通用电气-1995、科达-1995、西屋-1996、西门子-1997、诺基亚-1997、索尼-1997、花旗银行-1999、Amazon.com-1999、东芝-2001、三星、LG。国内企业如:联想电脑、盐田国际、海南航空、上海中远、上海宝钢股份、美的空调、江苏小天鹅、中国移动、咸阳彩虹…… 


【关于实施6sigma管理的必然性】 

一、实施6sigma的目的 

事实上,企业组织和管理者实施任何业务改进的目的只能是,也必须是:“获利和持续获利”以及“成功和持续成功”。 

随着我国加入WTO和世界经济的进步放缓,中国企业和企业管理者所面临的最具挑战性的问题已不是“如何成功”,而是“如何持续成功”。六西格玛管理不但告诉我们怎样获得成功,而且可以帮助我们获得保持持续发展的核心能力。 

人才是企业适应变革和竞争的核心力量。六西格玛不仅为企业提供必须的管理工具和操作技巧,更为企业培养具备组织能力、激励能力、项目管理技术和数理统计诊断能力的领导者。这些将帮助企业降低质量缺陷和服务偏差并保持持久性的效益,促进快速突破性绩效。 


二、实施的必然性 

质量水平意味着什么 
一个组织在接触而甚至不是考虑是否实施6Sigma之前,首先让我们来看一下真正的质量水平意味着什么。 

在过去的半个世纪中,普遍存在的3σ质量水平不再为人们所接受了。 
事实上,我国的大多数企业运作在3—3.5个西格玛的水平,这意味着每百万个机会中已经产生10000至66800个缺陷,对应合格率为93.3%—96%。从企业内部的效率与成本来看,所有的不良品要么成为废品,要么需要返工或在客户现场维修、调换,这些都是企业的成本。美国的统计资料表明,一个3西格玛的公司直接与质量问题有关的成本占其销售收入的15%—30%!而从另一方面看,一个六西格玛水平的公司仅需耗费年销售额的1.5%来矫正失误。所以提高公司的综合质量水平对于公司的盈利性有直接的好处。当然,最为重要的是高质量、稳定的业务流程是提高客户满意度的根本要素。 

对一个3西格玛水平的企业来说,提高一个西格玛水平可获得下述收益: 
利润率增长20% 
产出能力提高12%—18% 
减少劳动力12% 
资本投入减少10%—30% 

那么为什么要追求六西格玛?停留在4或5西格玛行不行?毕竟这已是超过99%的“好”了。用中国邮政的统计资料,如果信件的处理达到99%(3.8西格玛)的准确投递率,这表明约每小时要投错或丢失邮件数超过9500件,六西格玛意味着这一数字将降到3.4。 

可见六西格玛并不是不现实的标准。从市场环境来看,世界经济一体化加剧了公司间的竞争,一个停留在3、4或5西格玛的公司是无法与一个六西格玛的公司竞争的。国家与国家之间的竞争最终体现在综合实力方面的竞争。很显然的,一个只有不到3西格玛的国家,其内部有大量的资源浪费、愤怒的消费者、在国际市场上缺乏竞争力的产品,等等,在新的世界经济中将很难与一个具有4、5、甚至6西格玛的国度一较高下。因此,六西格玛管理是一个追求世界级水平的质量评价过程,近年来已经引起了全球质量界的高度重视。 

◆什么都不做意味着什么 
我们不得不说明这个问题:“六西格玛真的值得做吗?”对应的另一个方面的问题是:“如果我们不做六西格玛会付出什么成本?与什么都不做的成本有多大区别?” 

“什么都不做”这一选择对于某个组织而言,可能是正确的选择,然而组织需要在比较什么都不做的成本与做一些的成本之后再做这个决策。 

如果组织现有的任何一个竞争对手或新的竞争对手,在行业内或组织的细分市场上达到六西格玛质量水平,组织获利的日子可能为数不多了。六西格玛可以带来大幅度改进过去质量水平的行动命令,并能从根本上改变组织的市场地位。一旦行业内某个组织成功地实施并坚持了六西格玛经营战略,成为并保持业界领导地位的方法则是比竞争对手更明智地实施六西格玛。 

杰克·韦尔奇很早就意识到人类对于品质的要求越来越高,他认为企业应该趁着还在赚钱的时候,将成本投注在品质的改善上,而不是等到面临困境,才来寻求改善。于是在1996年开始推动六个西格玛,强调“从流程改造入手”的六个西格玛,使得通用一年获利增加7亿5千万美元,并大幅降低了成本,因此引起全球注意。 

重要的问题不是组织是否实施,而是何时、以何种方式实施六西格玛经营战略。 


【关于实施6sigma管理的收益】 

一、组织能得到的收益 

事实上,我们所有人能达成共识的、毫无疑义的一点是:“任何一项管理模式,不管它在理论上有多大的创新和理论价值,如果它不能给企业带来实际的收益,那它也将是毫无意义的。”而6sigma管理正是保持企业在经营上的成功并将其经营业绩最大化的管理模式,它能给企业带来快速的增长及可观的收益。一般来说,经营业绩的改善包括以下部分: 
◆投资利润率的提高; 
◆市场占有率的提高; 
◆顾客满意率的提升; 
◆营运成本的降低; 
◆产品和资金周转时间的缩短; 
◆缺陷率的降低; 
◆产品开发加快; 
◆企业文化的改变等等。 

推动6Sigma活动,你可以做到: 
◆节约成本增加利润; 
◆提高生产力; 
◆扩大市场占有率; 
◆留住顾客; 
◆缩短周期; 
◆减少误差; 
◆改变文化; 
◆开发产品和服务其他好处等。 


二、支持收益的原因 

为什么6Sigma管理能给组织带来如此明显的利益收获?为什么6Sigma管理表现出强劲的发展劲头,成为企业组织在新经济环境下获得竞争力的重要手段呢?归纳起来可以有三个方面的原因: 

第一,关注底线结果。底线是指企业在一段时间内的净收益或利润。在六西格玛管理中,它是由改进顾客满意程度和过程业绩而实现了。通过对核心业务流程实施六西格玛项目并达到预期的目标,是六西格玛管理方法的核心部分。六西格玛项目的目标是增加底线的结果,所有六西格玛项目必须要达到增加底线结果,而取得底线结果正是企业最高管理者最为关注的,并因此会更加投入其中。 

第二,六西格玛管理综合了技术方法与人文因素等有关企业过程改进的所有要素。而对以往的质量改进方法来说,虽然强调了其中的一些要素,但没有像六西格玛管理那样将这些因素系统地整合起来。而这些要素对于效益的产生十分重要的。 

第三,将改进工具方法与专业化的改进过程相联结。将人力资源的培育、授权与专业化的过程改进方法相联结,将管理职责及团队工作与专业化的过程改进方法相连结,使专业化的改进过程成为企业经营活动不可缺少的部分。
 

三、组织收益的增长趋势 

根据麦肯锡公司的调查和研究表明,一个3sigma企业只要组织其现有资源进行核心业务流程改进,如果每年可以提高一个sigma水平,那么每年可以获得以下收益:利润率增加20%;产能提高12-18%;雇员减少12%;资本投入减少10-30%,而且直至提升到4.8sigma企业均无须大的资本投入,当达到4.8sigma时,再提高到6sigma则需要增加投入,但此时产品的竞争力已大幅提高,市场占有率极高,给企业带来的利润将远远大于此时的投入。GE、摩托罗拉、杜邦、福特、美国快递、联信、联想等公司实施6sigma管理取得的巨大成功就是最好的证明。
 

【关于中国企业与6sigma管理】 

一、国内企业目前面临的现状 

经过近二十年的改革开放,尤其是市场经济的逐步完善,中国的企业开始了对各种管理思想和方法的实践,其中不乏成功有效的例子。但是,中国企业由于宏观政策、制度和人的关系等原因,并没有在管理,特别是在企业运营方面取得大的成效和突破。内部管理在很大程度上还停留在作为一门艺术的阶段,尽管一些高级的质量方法和过程也有局部的应用,但是质量检查仍然是制造业的质量管理的主要内容。而整个服务性行业的企业则完全处在凭经验、人员态度或由信息系统来保障服务质量的阶段,没有一个科学、系统的保证服务、产品质量的方法。 

中国企业亟需形成一个追求管理卓越、实现完美流程的企业文化。 
更多的我们国内企业的现状描述不需赘言,下面列出来的总结大多数‘差不多’企业和先进企业管理特点对比的表格也许能给我们提供些什么。 


二、六西格玛管理与中国企业 

六西格玛随着外资的引进已在中国这块文明之地播种,在通用电气、摩托罗拉、联信和柯达等世界级大公司的中国合资企业中,六西格玛已成为其企业文化的一部分。例如,从事软件生产的希捷技术公司,三年多来一直在中国使用六西格玛模式并且对其大加赞赏。人们普遍认为六西格玛模式将有助于中国参与国际市场竞争,使他们争取更多的市场份额和削减制造成本。然而由于六西格玛作为企业成功的竞争优势之一,企业间不愿过多公司宣传或交流这方面的经验和具体实施细节,使得六西格玛在中国一直披着一层神秘的面纱。 

在这种情况下,国内企业唯一的选择就是迎头赶上,尽快开展六西格玛的学习和实施,以争取在新一轮的竞争中立于不败之地,并成长壮大。 

目前,六西格玛在中国企业中的认知度是非常低的。只有极少部分管理者和学术界对此有些认识或较有深度的研究。业界仅有屈指可数的几家公司能提供有限的和有意义的培训,至于能提供六西格玛的全程培训和实施咨询的则更是凤毛麟角。这主要是因为六西格玛咨询服务与一般的咨询服务的最大的区别在于咨询师必须具备实际的实施经验,并至少在黑带大师以上。而那种靠新鲜的MBA来提供服务的咨询公司是无法满足要求的。除了跨国公司以外,国内的企业真正全面实施六西格玛管理战略的也极少。可以预见,在相当长的时间内,知识与技能的匮乏将是中国企业实施六西格玛管理战略的最大障碍。 

除了企业传统、管理制度和市场方面的原因外,六西格玛战略在中国的实施还面临一个非常大的挑战,即人才与知识的准备不足。因此,专家称,中国企业能否成功实施六西格玛管理战略,关键在于组织的最高决策层是否坚持不懈。 

专家同时警告,必须防止把六西格玛管理实施当成又一个质量认证。六西格玛管理实施应该着眼于流程能力、产品质量或客户忠诚度的突破性提高。任何试图把六西格玛管理实施当成一个品牌、宣传或认证的手段是浪费资源,并不会取得任何实质性的管理变革。许多失败的例子业已证明了这点。 


【关于如何实施6sigma管理】 

目前,业界对6sigma管理的实施方法还没有一个统一的标准。大致上可以摩托罗拉公司提出并取得成功的“七步骤法”(Seven-StepMethod)作为参考。“七步骤法”的内容如下: 

1、找问题(Selectaproblemanddescribeitclearly)把要改善的问题找出来,当目标锁定后便召集有关员工,成为改善的主力,并选出首领,作为改善责任人,跟着便制定时间表跟进。 
2、研究现时生产方法(StudythePresentSystem)收集现时生产方法的数据,并作整理。 
3、找出各种原因(IdentifyPossiblecauses)集合有经验的员工,利用脑力风暴法(Brainstorming)、控制图(Controlchart)和鱼骨图(Causeandeffectdiagram),找出每一个可能发生问题的原因。 
4、计划及制定解决方法(Planandimplementasolution)再利用有经验的员工和技术人才,通过各种检验方法,找出各解决方法,当方法设计完成后,便立即实行。 
5、检查效果(Evaluateeffects)通过数据收集、分析、检查其解决方法是否有效和达到什么效果。 
6、把有效方法制度法(Standardizeanyeffectivesolutions)当方法证明有效后,便制定为工作守则,各员工必须遵守。 
7、检讨成效并发展新目标(Reflectonprocessanddevelopfutureplans)当以上问题解决后,总结其成效,并制定解决其它问题的方案。 


【关于实施6sigma的经验教训】 

现在学习和推动六西格玛的热潮正在我国三资企业或部分国有大中型企业中掀起。越来越多的企业开始关注6西格玛,而有些企业则处在观望中。无论如何这对提高企业的质量和竞争力是有好处的。但是应该看到,企业的高层领导在决定推进六西格玛之前必须对六西格玛有一定的了解和必要的思想准备。因为任何改进活动的进行都不会是一帆风顺的,尤其是六西格玛具有自上而下的特点,高层领导是否有彻底执行六西格玛的决心,对成功与否至关重要。在已经推行六西格玛的企业中,有哪些成功或失败的教训呢? 

1、缺少激励机制 
企业培养出来的合格黑带需要很高的成本,如果充分利用他们的知识和才能,可以为企业带来非常可观的效益。尤其在我国目前的状况下,六西格玛专业人才奇缺,所以企业的人力资源部门应为他们合理地规划发展前景并制定合理的激励机制。如果企业培养了很多黑带,然而他们没有得到充分的重视或重用,使他们看不到希望,人才流失就会成为严重问题。 

2、评审与授权不足 
实施六西格玛不是喊口号或者搞运动,需要实际的效果。仅仅培训了人员和选择了项目是远远不够的。在项目执行过程中,BB、GB们会遇到许多问题。比如缺少时间、缺少资金投入、缺少其他部门的配合、缺乏工具和方法的指导、没有得到主管领导的充分支持、改善措施受到流程使用者的抵制而无法实施等。因此需要足够的授权、监督跟踪和指导。否则虎头蛇尾甚至一无所获的现象是在所难免的。这样的经验教训已经很多。评审工作应该是由Champion来完成的。没有不成功的BB、GB,只有不成功的Champion。 

3、推动与拉动 
根据现代科学的管理方法,要求将公司的整体经营目标分解到各个部门,进而具体到每个人,即目标管理,以此作为绩效考核及晋升的依据。反过来说,每个人和每个部门的绩效指标都达到要求,公司的整体经营目标就能完成。为了完成这些指标,往往就需要有科学的、开拓创新的方法,将流程合理化,提高效率,降低成本。以这种需求拉动六西格玛实施才能将被动执行变成积极主动的行为。否则靠以往推动某项活动或运动的传统方法只能作表面文章 

4、财务支持 
六西格玛活动的开展需要财务部门的大力支持。无论在项目的选择还是结束过程都需要财务方面对项目经济效果客观的评估。效益评估容易产生两种误区:过于苛刻或过于宽松。 
评估过于苛刻的危害是改善效果得不到体现和承认,打击了BB、GB的改善活动积极性,使他们没有成就感,失去持续改善的动力。过于宽松的结果是片面夸大六西格玛的效果,失去真实性。此外,财务评估的及时性也很关键。在项目选择初期如果没有得到经济效果的确认,改善后期可能会发现BB、GB们历尽辛苦改善的项目几乎没有任何经济效果,白白浪费了人力、物力和财力资源。 

5、形式主义或者仅仅使用六西格玛的一些工具 
目前很多企业的CEO们都认识到改革的重要性,也听说过六西格玛方法在GE公司得到很好地发挥和运用,并获得成功,它能帮助企业达到突破性的效果,于是当作又一次质量运动,跟风似地上了六西格玛。但是由于对六西格玛理念和推进方法缺乏必要的认识,六西格玛活动成为质量部门的工作,针对局部的问题用六西格玛工具进行改善。结果发现效果远远不及当初想象中的那么好,于是认为六西格玛不适合自己公司,又在追寻另一条途径。 

6、来自部门间的壁垒或人为的抵触 
众所周知,六西格玛的实施不仅局限于质量部门,而是自上而下,在整个企业范围推动的活动。任何变革都不会是一帆风顺的,因为常常会触及到个别人的利益,比如工作的舒适程度降低、工作量和难易度增加等等。并且变革会逐渐打破部门间的壁垒,更多的横向联合使流程更趋于合理。然而一些人为的障碍和抵触会时有发生,提高人员的认识,强调改革的目的性变得更重要了。 

7、开源与节流并存 
六西格玛活动的另一个误区是只关注‘节流’,而忽视了‘开源’。众所周知,六西格玛要提高产品或服务的质量。受传统的质量管理思想影响,人们通常认为提高质量的同时必然会使成本增加,为了找质量与成本的平衡点,无须追求6SIGMA,3SIGMA或许正合适。这种观点在现在看来是非常落后和不具有竞争力的。事实上随着产品或服务变差的减少,质量成本COPQ会大幅度地下降,因为废品、返工、检验等非增值部分都降低了。退一步说,即使有一些资金的投入,如果能够带来更多的效益,远远超过初期投入,又何必固守‘节流’呢? 

8、必要的投资 
六西格玛是科学和严谨的方法,在流程改善方法论的同时,运用了大量统计工具。不大可能“自学成才”,在活动初期借助专业咨询公司的帮助是必要的。这是一项高回报率的投资项目。关键问题是如何选择专业的咨询机构。 


【关于实施6sigma管理的关键驱动因素】 

六西格玛取得极大的成功,还是惨重的失败,取决于它如何通过企业的体系架构来实施。创建一个成功的六西格玛体系架构是一个不断前进的过程,其目标是将一种质量意识灌输到每个员工的日常工作方式之中。因组织的文化和经营战略目标不同,各组织间的体系架构有着极大的区别。每个组织所创建的体系架构是独一无二的,然而,每个成功的体系架构的创建过程有其共同的因素。 

这些共同的因素包括成功实施六西格玛的关键驱动因素和关键的结果事项。如果各个关键驱动因素解决了,关键事项也就实现了,那么六西格玛管理就成功实施了。这些关键驱动因素按重要性从高到低的顺序排列如下: 
◆主管人员领导; 
◆以顾客为关注焦点; 
◆战略目标; 
◆项目选择; 
◆培训与执行; 
◆资源; 
◆黑带人员的选择; 
◆测量指标及反馈; 
◆文化; 
◆沟通; 
◆计划; 
◆结果。 


【关于6sigma管理与其他管理方法、体系、思想的关系】 

随着竞争日益残酷,组织在降低成本和增加产出的同时,在改进质量和顾客满意度方面,面临更大的压力。在拥有的可用资源越来越少的情况下,这成为一种更加艰巨的挑战。面对这些挑战,如果组织能够明智地实施六西格玛,它可以帮助组织学习并出类拔萃。 

从高层主管人员那里,我们经常听到的一个问题是:“六西格玛如何与其他的组织创新相匹配?”我们认为,不应该认为六西格玛不过是另一种创新,而应该在更高层将六西格玛与其他计划或创新整合起来,作为整个经营战略的一部分。六西格玛不应该取代其他创新,而是提供一种战术性方法论,以确定在特定的情景、过程中最好的方法。 

大家常关心的有: 
◆六西格玛与全面质量管理(TQM)的关系; 
◆六西格玛与精益制造/精益思想的关系; 
◆六西格玛与BPR、ERP、MRP(Ⅱ)的关系 
◆六西格玛和ISO9000的关系 
◆六西格玛与企业现行质量控制系统的关系 

 
posted @ 2006-04-30 09:36 崛起的程序员 阅读(426) | 评论 (0)编辑 收藏

web2.0定义是什么?
  
  web2.0定义是什么?这是一个简单直接但又是最核心的问题,讨论web2.0的文章层出不穷,内容包括如何实现web2.0, web2.0如何盈利, web2.0如何击败web1.0等。但没有多少人愿意讨论web2.0的定义,也没有多少人讨论web2.0已有的定义存在什么问题。没有清晰理性的定义,我们的讨论就像唐吉珂德骑着毛驴冲向他心目中的怪兽-大风车一样。可笑而又毫无意义。
  
  翻阅搜索引擎带给我们关于web2.0的海量资料,找到了Blogger Don的“WEB2.0概念诠释”,他写道“Web2.0是以 Flickr、Craigslist、Linkedin、Tribes、Ryze、Friendster、Del.icio.us、43Things.com等网站为代表,以Blog、TAG、SNS、RSS、wiki等社会软件的应用为核心,依据六度分隔、xml、ajax等新理论和技术实现的互联网新一代模式。” 这个定义把一些让人眼花缭乱的前卫名词和计算机专用术语堆砌在一起作为WEB2.0概念诠释,。把应用,协议,语言格式混杂在一起来描述WEB2.0概念。定义者要么对计算机知识一知半解,要么是企图让人们的感到神秘从而对其顶礼膜拜。按照其定义的方式对“国家”这个概念进行诠释,那国家就是“以中国,法国,美国,俄罗斯,土耳其等集合体为代表,以军队,警察,文官系统,议会等社会团体的应用为核心,依照量子力学,马斯络需求层次理论,混沌学等新理论和技术实现人类社会的聚集模式。“,多么荒唐的的定义
  
  一个新概念或新理论不可能从石头里蹦出来,也不会超越我们的时代凭空想象。他必然有自己的历史传承。理解WEB2.0,我们要了解WEB甚至是bbs的历史。 World Wide Web,简称WWW,由英国人TimBerners-Lee 1989年发明。通过WEB把互联网上的资源在一个网页里直观的表示出来并通过网页相互连接。在web之前,互联网相当数量的文档资源是存放在bbs上。通过对bbs发展历史的研究,我们发现web时代的内容服务网站主要是从bbs的功能中演化而成。
  
  BBS诞生于70年代末,那时还没有浏览器,搜索引擎,甚至没有个人网站。我们在BBS上可以做四件事1。发布新闻 2。发布交易信息 3。发布个人感想,心情描述4。互动式问答 前三个功能逐渐从bbs中分离出去,发展成三个方向的网站----新闻类,电子商务类和博客类,这三类网站经过整合逐渐成为各自的门户。如新浪,易趣,博客中国。第四个功能互动式问答正在分离,但还没有出现该领域的门户网站。如www.ideacool.net ,新浪的爱问,小i等网站。我们可以把这一类网站模式称之为威客模式,英文名witkey。
  
  我们应该已注意到1。新闻类网站。2.电子商务类网站 与3。博客类 4。威客类网站的功能和形式上有一个明显的鸿沟。这个鸿沟就应该是web1.0 与web2.0的划分界线。 新闻类网站和电子商务类网站为代表的web1.0时代是以信息接收者为中心,免费分享信息的时代。博客类和威客类网站为代表的web2.0时代是以信息提供者为中心。互联网用户能够管理个人的资料和信息。通过各种途径让自己的智慧,知识,经验,技能体现价值。简而言之,web2.0的定义就是提供一种平台让个人的智慧,知识,技能体现价值的一种互联网新模式。这种模式的表现形式主要为博客类,和威客类两种。实现的支撑包括互联网技术,信用制度,支付制度等。
  

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


Web2.0的盈利点

 

主要集中在威客模式上: 
  1. 威客模式面向的对象主要是有专业专长的用户如科学家,工程师,医生等,博客面向的是善于表达个人观点和思想的用户如记者,娱乐明星,专栏作家等等。
  
  2。个人的知识,技能,智慧可以体现价值。其中“体现价值”不一定指获得经济利益,获得受帮助者的赞扬,获得公众的认可也是体现价值的方式之一。但让参与者获得经济利益是威客模式的重要特点。
  
  3。个人的知识,技能,智慧可以体现价值,但不代表参与者一定能够获得收益,这要取决于其知识,技能的含金量,可以说,能力越大,收益越大。
  
  4。威客模式的建立需要实践该模式的平台建立信用制度,支付制度,例外处理制度等等。这些制度是否完善是该平台能否持续发展的决定性因素。
  
  5。威客模式也是人类大脑通过互联网协同工作的一种模式。
  
  6.为威客提供服务的平台通过分享威客交易知识的收益获取利润。

posted @ 2006-04-30 09:30 崛起的程序员 阅读(214) | 评论 (0)编辑 收藏
    进入2004年,伴随着各种机遇和挑战,中国的软件开发仍然在摸索中前进,程序员也在不断的学习和工作中探寻自己的未来。在这个过程中,当然少不了各种技术和公司对他们施加的影响。跟随谁,选择什么路线永远是一种充满未知数的挑战。

  谈到影响,可能以微软为首的软件巨头更容易被提及,毕竟开发人员所直接应用的操作系统、语言、开发工具等各种技术都掌握在这些公司手中。然而,这并不能抹杀和妨碍提起国内一些企业和人士对中国软件开发各方面产生的影响。

  站在这个角度,本刊在国内的软件企业和个人中评选出了如下20位人士,他们对近期软件开发产生了一定程度的影响。选出这些人,也是为了抛砖引玉,让国内的开发者更了解自己正在经历的变化和面临的挑战。

  在这些人员中,有些属于早一代的程序员,他们创造的业绩激励了很多程序员进入软件开发。鲍岳桥就是一个,尽管现在他已经成为了联众的总裁,但之前从UCDOS步入辉煌,Windows时代一度面临困境,最终凭借网 络游戏 成功 转型 ,这种以技术创业成功的例子对中国的程序员来说就是巨大的诱惑。

  与此同时,很多人从软件教育方向在逐渐改变整个软件行业的人才结构,这一说法毫不夸张。青鸟APTECH就是将印度软件教育模式带到中国来的典型,尤其是在中国软件人才结构并不合理的情况下,他们所做的工作也是有益的尝试。

  而且,面向未来的眼光也必不可少,很多人所做的工作带来的变化现在还不显著,但对未来可能会产生相当的影响。比如,中国软件人才的培养结构在逐渐向国际化靠拢,软件学院功不可没。更为超前的是,为了探索未来中国软件在底层技术上的引导性,有很多人在做努力,从Linux在中国的推广,到科泰世纪打造自己的操作系统和开发平台,尽管这些尝试可能会失败,但带给我们的是经验和深入的思考。

  现在,有人说程序员面对着太多的头绪,已经迷失了方向。的确,是有一些人迷失了方向,但这里的20位人士能够让我们以更清醒的眼光看待中国的软件开发,了解我们取得的成绩和面临的挑战。

  开国元勋

  严援朝

  所属公司:新浪网

  入选理由:开发第一个中文操作系统CCDOS,参与创办四通利方,掌控最大的中文网站新浪网技术总架构。

  “做软件就是在不断地明确目标,就是搞清楚你的GO 是什么,所有的软件都逃不出那三句话——IF、THEN、 ELSE。棒的程序员很快能够知道自己的GO是什么,没长进的程序员老也弄不清楚自己到底要干嘛,所以永远处在 学习过程中,手里永远拿着一本书,永远在学,永远也学不会。”这是严援朝很经典的一句话,甚至有程序员把这作为自己的座右铭。作为中国第一代程序员的象征,他如今依然负责在新浪网的整体技术架构上。不知是不是严援朝怕别人忘记自己,不久前,从新浪网炮制出了两篇文章,大谈自己的一些深奥看法。其实如果单纯是这种原因,应该大可不必,因为严援朝在中国软件开发史上奠定的地位无人可撼。

  求伯君

  所属公司:金山软件

  入选理由:软件领域的常青树,以榜样的力量激励众多程序员。

  每一个听到这个名字的程序员,都会有一种莫名的激动和敬仰。从金山成立的那一天起,公司的招聘广告上就把求伯君作为吸引程序员加入的重要宣传词,由此可见求伯君在程序员中的影响力。

  不久前,我们准备做“程序员的一天”的栏目,找到求伯君。他说:“做是可以,不过现在这些天不太适合,整天要陪着很多官员应酬。”看来,作为董事长的求伯君也仅仅能在精神上鼓舞程序员了。据说,金山正在筹拍一部 《剑侠情缘》 的电视剧,求伯君还可能在其中饰演一位方丈。到时候,我们或许可以欣赏求伯君的影视风采了。

  王江民

  所属公司: 江民科技

  入选理由:38岁开始编程,挖掘了杀毒软件的巨大市场潜力

  “38岁开始学习计算机,两三年之内成为中国最出色的反病毒专家之一;45岁只身一人独闯中关村办公司,产品很快占据反病毒市场的80%以上。”这些事实已经让我们无法忽视王江民对中国软件开发带来的影响力,在业内被尊称为老师的王江民,凭借坚持而开拓出了杀毒软件市场,这也成为中国软件产业所仅有的几个亮点之一。尽管现在KV系列产品中早已没有了一行他的代码,而且在市场中的表现也差强人意。但毋庸置疑,KV系列让众多的程序员知道了王江民,而他身残志坚的毅力和品质也让很多程序员面对困难和挫折时,从中得到鼓舞。

  鲍岳桥

  所属公司:联众公司

  入选理由:在DOS和Windows平台、网络时代下都获得成功的典型

  从UCDOS流行的那一天起,鲍岳桥也成为了中国无数程序员的偶像。然而,和很多程序员一样,他也并没有从UCDOS的成功中得到多少具体的物质利益。接下来,Windows平台开发的巨大冲击到来了,网络的冲击到来了,有谁能同时抓住两个机会?鲍岳桥无疑做了最好的注释。鲍岳桥认为,是否掌握某项技术不是关键,技术这个东西完全是触类旁通的,当今计算机的发展如此迅速,一个人不可能掌握所有的新技术,一个有经验的程序员应该能够利用自己原有的知识和经验,很快地掌握新技术和新知识。他与简晶、王建华的合作也是联众成长中的精彩经历,尽管现在他容登总裁宝座,而简晶和王建华离去创业,但他们当初的合作还让现在的众多程序员看到了初期团队合作的成功例子。

  宫敏

  所属公司:凝思科技

  入选理由:最早将自由软件引入中国

  宫敏,对很多人来说,这是一个陌生的名字。但正是宫敏博士,在芬兰参与了LINUX操作系统的开发与研究。后来,他用磁带背回了20GB容量的自由软件,形成了中国第一个自由软件库,被称为是中国自由软件界元老。

  然而,就在国内自由软件运动掀起狂潮时他却全身而退,埋头于开发和做实事去了。2000年,宫敏回国创办北京凝思科技有限公司,目标是在较短时间内为国家相关部门提供具有完全自主知识产权的高性能、高安全的国产信息安全 高技术 产品。宫敏认为,自由软件首先要重视教育,培养出一批有良好心态和真才实学的人。


        教育先导

  谭浩强

  入选理由:计算机基础教育

  雷军曾经说过:“是谭浩强让程序员感到了写程序就是自己的生命。”这可能是早一代程序员的感觉吧。在计算机基础教育方面,谭浩强无疑拥有极高的声望和经验。不过,现在社会已经进入新的阶段,尽管谭浩强已经成立了工作室,并不断出版了一系列的书籍,但真正如《Basic 语言》这样风行的书籍并没有再次出现。

  谭浩强的书集中体现了中国计算机教育的特点,浅显,作为入门级的书籍非常不错,但一旦真正要深入下去,可能就要选择国外那些大师级人物所写的书籍了。这也是国内很多程序员对谭浩强某些书籍颇有微词的原因所在。

  然而,不管如何,时代和机遇都造就了谭浩强在中国计算机史上的地位,这一点无法抹杀。

  李开复

  所属公司:微软

  入选理由:创办微软亚洲研究院,在学生中影响巨大

  有人说微软亚洲研究院现在很会作秀,大事小事总要通报一番,而且不时出来组织一些成果演示,或许这继承了李开复的风格。做过研究又在企业中多年锻炼的经历使得李开复把亚洲研究院当作企业一样的来运作,《追随智慧》等书籍的出版更将这种宣传推上了顶点。但从另外一方面来看,李开复的确给国内的计算机科学研究带来了新的气象,亚洲研究院的一些技术成果也对IT产业有着深远的影响。同时,微软亚洲研究院在大学中深入的各种活动使得李开复在学生中的影响力颇高。而最近《给中国学生的一封信》和《给中国学生的第二封信》都言辞恳切,引起了巨大反响。微软给中国印象最好的地方是亚洲研究院,这也算是李开复的高明之处吧。

  陈钟

  所属院校:北京大学软件学院院长

  入选理由:软件学院的积极倡导和实践者

  其实选择陈钟,不如说选择了软件学院更为恰当。毕竟从国家重视软件学院开始,有几十家高等院校开始了这方面的探索。而32岁就成为博导的陈钟作为北京大学软件学院的院长,在大兴开拓了新的天地,并取得了不错的成绩。

  最近,有朋友说:“以前,计算机系的学生对软件学院很不屑一顾,认为就是一群杂牌军。但现在,他们笑不起来了,软件学院的课程配置和教学完全是按照市场的要求去进行的,他们已经得到了市场的认可。” 当然在这些探索中,有的学院做的并不好,但作为国家希望改变现在计算机人才培养的方式而设的试点,我们有理由期待软件学院带来的改变。而陈钟将继续在这个过程中施展自己的才华。

  杨明

  所属公司:北大青鸟APTECH

  入选理由:将印度软件人才培养模式引进中国

  印度发展软件产业的成功被大肆宣扬后,吸收印度经验的探讨逐渐风行起来。但真正实施借鉴印度经验就是北大青鸟APTECH了。作为总经理的杨明,力图将这种模式和中国的实际情况相结合,打造出一种独特的软件从业人员的培训渠道。杨明并不喜欢别人称他培养出来的是“软件蓝领”,因为,在他看来,通过APTECH模式培养的人才是帮助很多人进入IT领域的“金光大道”。而且,采取特许经营这套模式也被巨额资金打造的产业链证明是可行的,因为目前已在全国建立和发展了近100家授权培训中心。据说,北大青鸟还将继续投资几个亿用来扩张,无论如何,教育模式方面,杨明带领的北大青鸟APTECH将会给国内软件业人才市场带来相当的影响。

  侯捷

  入选理由:技术写作创造品牌

  侯捷深入浅出的写作手法,给国内枯燥的技术书籍写作注入了一针强心剂。而且,国内的出版公司也从侯捷身上看到了一个优秀技术作者的市场潜力,因此将挖掘国内优秀作者作为重要的工作内容。

  同时,在侯捷直接和间接的影响下,国内一批软件开发者看到了技术写作的价值,开始出版技术书籍。现在,程序员发现了另外的一条可供选择的方向:技术写作。随着更多的开发者参与到其中,对提升国内开发者交流、共享技术起到了很好的促进作用。

  侯捷并不掩饰自己对财富的追求,他说:“好的作者就应该得到好的报酬,这样才能保证他能够静心研究技术,再写出更好的书来。”当网上攻击自己的帖子越来越多的时候,侯捷选择了自己的方式,他以老师的口吻试图来解释自己所倡导的这一切。然而,他的回应和解释并不能排解一些人的继续攻击,因为网络就是网络。

   创新者

  王志东

  现属公司:点击科技

  入选理由:在网络上取得成功后仍然钟情软件,投巨资做面向未来的协同软件

  不了解王志东的人觉得他放弃了软件,而后又被网络所抛弃;但了解王志东的都知道他最钟爱的还是软件,而且现在他同时拥有了网络。

  自从离开新浪之后,王志东的消息就逐渐从网络上减少了。应该说,王志东带给我们的消息更多是互联网所带来的影响力。

  对于王志东正在做的协同软件,很多人到现在也没有完全明白。尽管国外的一些厂商正在投巨资开发协同软件,但在国内,推广起来还存在很多的问题。但无论如何,王志东能够在功成名就后仍然投入资金来开发一个仍属未知数的软件领域,这种对软件的痴情不正彪显了开发者执著的特质吗。

  周奕

  入选理由:倡导走向国际市场,以共享软件挣美元的先行者。

  2001年的10月份,周奕从美国打来一个长途电话,对共享软件在国外寻求注册谈了很多他的想法,当时的他充满着发现新大陆的喜悦,也迫切希望与国内的程序员共同分享。随着媒体的宣传,“宝马车”给程序员带来了极大的吸引,一大批程序员、没有毕业的学生、甚至包括刚刚学习VB两三天的小孩子也都做起了美元梦。不过,周奕在国际上面临的压力也不小,一大批类似MP3 to CD的软件不断出现,其中不乏其他中国程序员的作品。尽管周奕的软件销售量下滑,但他从来没有后悔过这样做。

  现在国际市场上有数量众多的共享软件,其中中国、俄罗斯和美国占据了最大的份额,一大批共享软件作者通过这种途径换来了丰足的生活,当然也有另外一批人并没有带来所企盼的收获。但无论如何,周奕作为“共享软件走向国际市场”创始人被众多的程序员称为“老大”,这种影响力在中国的软件开发史值得一书。

  陈天桥

  所属公司:上海盛大

  入选理由:用市场的成功加速了网络游戏开发的进程

  陈天桥的入选可能争议很大,之所以坚持把他列入,完全是基于他给网络游戏市场带来的巨大冲击。掰开手指数数,中国有几个软件行业被如此追捧过,除财务管理软件和杀毒软件之外,还没有那个软件行业被疯狂的关注。正是基于上海盛大为代表的公司在网络游戏市场上取得的成绩,才使人把目光放在了网络游戏上。而且,正是盛大和韩国游戏开发商产生的冲突才使得自己和其他的运营商意识到了国产网络游戏研发的重要性。网络游戏程序员一时物以稀为贵,很多培训结构和高校都开设了相关专业,国家在这方面的政策也有了调整。以一己之力影响了网络游戏开发的产业进程,陈天桥带领盛大产生的影响力不可忽略。

  陈榕

  所属公司:科泰世纪

  入选理由:打造新一代操作系统

  陈榕的身上散发着一股锐气,有人说他是个天生的演讲家。不过当陈榕刚到国内推广其思想的时候,很多人不理解,他的情绪也会很激动。有人说他对技术研究的太深刻了,虽然提出的思想很对,但这种事情不应该在国内做,因为中国没有这样的环境。不知道陈榕如何看待这个问题。

  现在,美国的风险投资已经不易获得,而政府对这方面的支持使陈榕找到了一个机会。能否取得市场成功尽管是陈榕所关注的,但他可能更在意将多年以来在微软无法实现的一个梦在这里实现吧。

  然而,在微软的.NET已经推出,多种嵌入式操作系统相互争夺的市场上,科泰世纪的操作系统要取得成功还面临严峻的挑战。不过陈榕很有信心,他用一个例子来证明:微软很多的产品在1.0和2.0版本的时候都不被人看好,都是到3.0才成熟并得到市场认可的。我们也期望那一天早些到来。

  吴涛

  入选理由:易语言创始人

  如果说在中文语言编程上,中国有为数不多的开发者还寄托希望的话,那身处湖北省枝江市鑫源村的吴涛无疑是其中之一。这位数年前就开始做易语言的程序员远离了喧嚣的北京,毅然回家做起了中文可视化编程环境。

  在最近的版本中,吴涛建立了完全的易程序编译器系统,支持全编译,可以直接将程序编译为CPU指令码运行,从而突破了长期以来的速度瓶颈。同时,他的商业推广也做的不错,从易语言支持论坛的火爆中就可以看出这点。而吴涛一直执著在语言研究和底层编程工具的态度也是很多程序员需要学习的。尽管吴涛所做的工作不会撼动整个软件开发的大局,但有人执著于这样的工作也应该得到我们的尊敬。想一想,同样是基于中文的出发点,为什么WPS会得到推崇,而不能容忍易语言的存在呢。


   个人典范

  雷军

  现属公司:金山软件

  入选理由:从程序员向管理者成功转型的代表

  2003年初,当雷军在亚运村的一个并不为人所知的酒吧中聊天,探讨程序员成长和转型的时候,雷军曾说:“我现在认识到,CEO也是一种职业,这个职业对我更有吸引力。对公司未来的几年发展,我心中已经有了一个方向,关键就是如何去实施和时间快慢的问题而已。”

  应该说,媒体已经将雷军塑造成为了一个从程序员到管理者转型的典型例子。然而,在这种转变中,有谁能够了解到雷军当时所面临的压力和风险,如果失败了呢?没有人会问这个问题,也没有人会回答它。雷军带领金山发动的红色风暴也将通用软件的价格拉了下来,这也给程序员一次重新审视自己的机会。记得当时有报道谈到铭泰的程序员看到自己开发的软件不得不以几十元的低价销售时,甚至哭了起来。这算不算的雷军带给程序员的另外一种影响呢?

  不过,现在雷军作为管理者,同样面临着多种问题,当网络游戏火爆之后,雷军将赌注放在了剑侠情缘网络版上,有人还说雷军精于战术,但战略还有欠缺,这次的宝押对了吗?不管怎样,如果雷军成功了,这是管理者的成功,如果他失败了,也不过是管理者的失败案例之一。但雷军曾经有过的程序员身份将永远让我们关注他的人生。

  丁磊

  所属公司:网易

  入选理由:技术眼光抓住网络时代机遇的典范

  如果告诉你中国的首富也在看《程序员》杂志,你是否相信?这可能很难想像,但事实的确如此。

  如果说网络给了很多人一个机遇,包括软件开发人员,但并不是所有人都能抓住了这个机遇,即便抓住,又很难有人把这个机遇发挥到像丁磊这样极至。

  有人说丁磊不是一个技术人员,因为并没有开发过哪个令其扬名的产品,但丁磊所具有独到的技术眼光在转向网络服务和网络游戏时都起到了重要作用,说丁磊属于那种“玩技术”的更为合适。开发者如何利用各种技术挖掘到更为广阔的商业价值,这可能就是丁磊带给我们的启示吧。

  张小龙

  所属公司:博大

  入选理由:开发Foxmail和微软的OutLook抗衡,以免费软件一夜成名

  一个普通的程序员开发的软件能和微软的产品相抗衡,这很少见,而产品被巨资收购就更加是传奇,创造这个传奇的人就是张小龙。如果说互联网时代的软件只有几个算上成功的话,Foxmail肯定会名列其中。

  依靠这两点,张小龙对很多程序员产生的激励就不可小视。当然,这也是在当时的背景下发生的传奇,现在这样的故事可能不会再次发生了。

  张小龙加盟博大任副总裁和CTO后,他现在的工作首先是管理和组织各个产品和项目开发组,其次是规划公司的产品结构,公司现在有很多的产品和项目都要由他来统筹。这一次,团队合作的力量将成为他再次成功的基础。

  廖恒毅

  现属公司:佳软公司

  入选理由:十多年从事一线软件开发的代表,正打造基于.NET的企业级开发平台

  廖恒毅从不掩饰自己对微软的喜爱。从中文之星2.0到开发基于.NET的大型企业管理软件,他与微软的关系密不可分。而之前曾经在杜家滨领导下的微软中国公司担任开发合作部经理的经历,更给他身上打下了深深的微软烙印。尽管廖恒毅很坦率的谈到,他当时所做的工作不如现在的负责人,但作为一个纯粹的程序员,他利用微软技术创造的价值却无人可及。从中文之星2.0的热卖,到创造的拼音加加输入法带动了输入法技术的创新都是证明,而现在,他用.NET构架了佳软公司未来超速发展的动力,他说:“今天我更关注我的技术能否给社会而不仅仅是个人带来的价值。”

  如果有人说,微软的技术能做什么样的系统呢?廖恒毅一定会毫不迟疑的拿自己的系统举例,利用创建的软件平台,一个人可以在10多天的时间中打造出一个完整的企业管理系统,这是类似SAP和Sibel的平台,只是现在中国的软件环境并不适合其拿出来做公开的标准,公司更愿意使用这套系统来面向最终客户。未来,廖恒毅所创造的这一软件架构给中国企业软件开发带来冲击也并非不可能。当然,技术的领先并不代表市场的领先,这一点他也很清楚。

  廖恒毅并不习惯去公司分配给他的办公室去办公,他更喜欢扎在公司程序员的房子里面和大家一起工作。有次,聊程序员能做到什么时间,廖恒毅笑着说:“做了程序员,只要活着,就要一直写程序了。”

  袁红岗

  所属公司:金蝶中间件公司

  入选理由:最早开发Windows上的企业应用软件,打造独立知识产权的EJB服务器

  很多JAVA程序员对袁红岗极其佩服,源于他做了很多人不敢想更不敢做的事情,这就是他打造了国产的EJB服务器,很快,金蝶将在国内推出自主产权EJB服务器的3.0版本。

  同时,袁红岗还是在Windows平台上开发企业应用软件的最早的一批程序员,当时写出的适合小型企业使用的财务购销存一体化软件KINGDEE 2.7,令IT行业对金蝶刮目相看,使金蝶一举成名,正是这些人才奠定了中国的管理软件的基础。

  袁红岗将自己多年的经验以一种朴实的风格写出来,这些帖子在网上也影响深远。

  后记:

  我们选择了20位对中国软件开发最具影响力的人,从侧面也展示了中国软件开发的脉络。其中我们可以看到中国的软件开发大部分以国内市场为主,很多过去的那些软件英雄都与中文、汉化有着千丝万缕的联系。正是在这种情况下,这些人取得了成功,但同时也让国内的开发人员将目光局限在了国内市场上。而国内市场的不成熟以及其他一系列原因又导致了无法支撑起语言和底层开发工具技术厂商的存活。

  正因为如此,我们也有一些无奈,这些人大部分都是在心理上给程序员施以影响的人,而在技术层面上能够对软件开发方向产生影响的很少,即便是有影响力,其影响范围也很小。

  软件设计方面,国内没有一个企业或者个人引领某种规范使得大家可以靠过来。在全球技术统一化的今天,能否开发出面向全球市场的软件技术,这是需要重视的问题。我们的软件公司和程序员能否向这个方向努力呢?

posted @ 2006-04-30 09:11 崛起的程序员 阅读(217) | 评论 (0)编辑 收藏

摘要

Hibernate和struts是当前市面上几个最流行的开源的库之一。它们很有效率,是程序员在开发Java企业应用,挑选几个竞争的库的首选。虽然它们经常被一起应用,但是Hibernate的设计目标并不是和Struts一起使用,而Struts在Hibernate诞生好多年之前就发布了。为了让它们在一起工作,仍然有很多挑战。这篇文章点明了Struts和Hibernate之间的一些鸿沟,尤其关系到面向对象建模方面。文章也描述了如何在两者间搭起桥梁,给出了一个基于扩展Struts的解决方案。所有的基于Struts和Hibernate构建的Web应用都能从这个通用的扩展中获益。

在Hibernate in Action(Manning,2004十月)这本书里,作者Christian Bauer和Gavin King揭示了面向对象世界的模型和关系数据模型,两个世界的范例是不一致的。Hibernate非常成功地在存储层(persistence Layer)将两者粘合在一起。但是领域模型(domain model)(也就是Model-View-Controller的model layer)和HTML页面(MVC的View Layer)仍然存在不一致。在这篇文章中,我们将检查这种不一致,并且探索解决的方案。

范例不一致的再发现

让我们先看一个经典的parent-child关系例子(看下面的代码):product和category。Category类定义了一个类型为long的标示符id和一个类型为String的属性name。Product类也有一个类型为long的标示符id和一个类型为Category的属性category,表示了多对一的关系(也就是说很多product可以属于一个Category)

				
				
				
						/**
* @hibernate.class table="CATEGORY"
*/
public class Category {
   private Long id;

   private String name;

   /**
    * @hibernate.id generator-class="native" column="CATEGORY_ID"
    */
   public Long getId() {
      return id;
   }

   public void setId(Long id) {
      this.id = id;
   }

   /**
    * @hibernate.property column="NAME"
    */
   public String getName() {
      return name;
   }

   public void setName(Long name) {
      this.name = name;
   }
}

/**
* @hibernate.class table="PRODUCT"
*/
public class Product {
   private Long id;
   private Category category;

   /**
    * @hibernate.id generator-class="native" column="PRODUCT_ID"
    */
   public Long getId() {
      return id;
   }

   public void setId(Long id) {
      this.id = id;
   }

   /**
    * @hibernate.many-to-one
    * column="CATEGORY_ID"
    * class="Category"
    * cascade="none"
    * not-null="false"
    */
   public Category getCategory() {
      return category;
   }

   public void setCategory(Category category) {
      this.category = category;
   }
}




我们希望一个product可以被更改category,所以我们的HTML提供了一个下拉框列出所有Category。

				
				
				
						<select name="categoryId">
   <option value="">No Category</option>
   <option value="1">Category 1</option>
   <option value="2">Category 2</option>
   <option value="3">Category 3</option>
</select>




这里我们看出了两者的不一致:在Product领域对象里,category属性是Category类型,但是ProductForm只有一个类型为long的categoryId。这种不匹配不但增加了不一致,而且导致了不必要的代码进行primitive type的标示符和对应的对象之间的转换。

这种不一致部分是由于HTML Form自己引起的:它只代表了一种关系模型,不能代表面向对象的模型。面向对象和关系模型的不一致在存储层由对象关系映射(O/RM)解决。但是类似的问题在表示层(view layer)仍然存在。解决的关键是让他们一起无缝地工作。

Struts的功能和局限

幸运的是,Struts能够生成和解释内嵌的对象属性。Category下拉框可以用Struts page-construction(html) tag library:

				
				
				
						<html:select property="category.id">
   <option value="">No Category</option>
   <html:options collection="categories" property="id" labelProperty="name"/>
</html:select>



我们假设categories是Category对象的一个list。所以现在我们要修改ProductForm,让它变得更加“面向对象”,我们要修改ProductForm的categoryId,改成类型为Category的category。这种改变会导致在Product和ProductForm之间复制属性的工作更加繁琐,因为两者有相同的属性。

				
				
				
						public class ProductForm extends ActionForm {
     private Long id;
     private Category category;
     ...
}




当我们完成剩余的Struts Action, configuration, validator, jsp, hibernate层后,开始测试,我们马上在访问ProductForm.category.id时遇到了NullPointerException。这是预料中的!因为ProductForm.category还没有被设置,同时,Hibernate也会将多对一所联系的对象引用设为空(如果database field为空指)(译者:这里指Hiberate将product.category为Null,如果该Product没有联系到任何category)。Struts要求所有的对象在显示(生成HTML Form)和传播(提交HTML FORM)之前被建立。

让我们看看如何用ActionForm.reset()来架起桥梁。

(并非如此)臭名昭著的Struts ActionForm

在我第一个星期接触Struts的时候,我最大的一个疑问就是:为什么我必须为Properties, getter方法, setter方法保持几乎完全相同的两份copy, 一份在ActionForm Bean, 一份在DomainObject。这个繁琐的步骤成了Struts社区最主要的抱怨之一。

以我的观点,ActionForm存在有原因的。首先,它们可以区别于Domain Object因为他们但当了不同的角色。在MVC模式下,Domain Object是Model层的一个部分,ActionForm是View层的。因为Webpage的Field和Database的Field可能不一样,某些特制的转换是常见的。第二,ActionForm.validate()方法可以定义非常好用的验证规则。第三,可能有其他的,特定的View行为,但是又不想在domain layer实现,特别当persistence framework来管理domain object的时候。

提交Form

让我们来用ActionForm内有的方法-reset()-来解决view和model之间的不一致。这个reset()方法是在ActionForm在被Struts Controller Servlet处理request时候复制ActionForm属性之前调用的。这个方法最常见的使用是:checkbox必须被显式地设为false,让没有被选中的checkbox被正确识别。Reset()也是一个初始化用于view rending对象的合适地方。代码看起来是这样的:

				
				
				
						public class ProductForm extends ActionForm {
     private Long id;
     private Category category;
     ...
     public void reset(ActionMapping mapping, HttpServletRequest request)
     {
        super.reset( mapping, request );
        if ( category == null ) { category = new Category(); }
     }
}




Struts在使用用户提交的值填写ProductForm之前,Struts会调用reset(),这样category属性将会被初始化。请注意,你必须检查category看它是不是null,后面我们会讨论这个。

编辑Form

到目前为止,我们已经解决了form提交时候的问题。但是当我们在生成form页面的时候呢?Html:select tag也希望有一个非空的引用,所以我们将在form生成页面之前调用reset()。我们在action类里加入了一行:

				
				
				
						public class EditProductAction extends Action {
     public final ActionForward execute( ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response ) throws Exception
     {
        ...
        Product product = createOrLoadProduct();
        ProductForm productForm = (ProductForm)form;
        PropertyUtils.copyProperties( productForm, product );
        productForm.reset( mapping, request );
        ...
     }
}




我假设读者已经对action类和Jakarta commons Beanutils包非常熟悉了。CreateOrLoadProduct()建立了一个新的Product实例或者从数据库里载入一个已有的实例,具体取决于这个action是建立或者修改Product的。ProductForm被赋值后(译者:也就是调用PropertyUtils.copyProperties后),productForm.category已经从Product.category复制过来了(译者:实际上只是复制了category对象引用,并没有开销),然后,ProductForm就能用来生成页面了。我们同时也必须保证:不覆盖已经被Hibernate载入的对象,所以我们必须检查(category)是不是为null。

因为reset()方法是在ActionForm中定义的,我们可以把上述代码放入一个superclass,比如CommonEditAction,来处理这些事情:
    

				
				
				
						      Product product = createOrLoadProduct();
        PropertyUtils.copyProperties( form, product );
        form.reset( mapping, request );



如果你需要一个只读的Form, 你有两个选择: 第一检查所联系的jsp对象是不是null, 第二复制domain对象到ActionForm之后调用Reset()

保存domain对象

我们解决了提交Form和生成Form页面的问题, 所以Struts可以满足了。但是Hibernate呢?当用户选择了一个null ID option – 在我们的例子中“no category”option- 并且提交form, productForm.category指向一个新建立的hibernate对象,id为null。当category属性从ProductForm复制到Hibernate控制的Product对象并且存储时,Hibernate会抱怨product.category是一个临时对象,需要在Product存储前先被存储。当然,我们知道它是Null,并且不需要被存储。所以我们需要将product.category置为Null,然后Hibernate就能存储Product了(译者:在这种情况下,数据库product.category被设成空值)。我们也不希望改变Hibernate的工作方式,所以我们选择在复制到Domain对象之前清理这些临时对象,我们在ProductForm中加了一个方法:

				
				
				
						public class ProductForm extends ActionForm {
     private Long id;
     private Category category;
     ...
     public void reset(ActionMapping mapping, HttpServletRequest request) {
        super.reset( mapping, request );
        if ( category == null ) { category = new Category(); }
     }

     public void cleanupEmptyObjects() {
        if ( category.getId() == null ) { category = null; }
     }
}




我们在copyProperties之前清理掉这些临时对象,所以如果ProductForm.category只是用来放Null的,则将ProductForm.category置为Null。然后Domain对象的category也会被设成null:

				
				
				
						public class SaveProductAction extends Action {
     public final ActionForward execute( ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response ) throws Exception
     {
        ...
        Product product = new Product();
        ((ProductForm)form).cleanupEmptyObjects();
        PropertyUtils.copyProperties( product, form );
        SaveProduct( product );
        ...
     }
}




一对多关系

我还没有解决Category到Product的一对多关系。我们把它加入到Category的Metadata中:

				
				
				
						public class Category {
     ...
     private Set products;
     ...

     /**
      * @hibernate.set
      * table="PRODUCT"
      * lazy="true"
      * outer-join="auto"
      * inverse="true"
      * cascade="all-delete-orphan"
      *
      * @hibernate.collection-key
      * column="CATEGORY_ID"
      *
      * @hibernate.collection-one-to-many
      * class="Product"
      */
     public Set getProducts() {
        return products;
     }

     public void setProducts(Set products) {
        this.products = products;
     }
}



注意:Hibernate的cascade属性为all-delete-orphan表明:Hibernate需要在存储包含的Category对象时候,自动存储Product对象。和parent对象一起存储child对象的情况并不常见,常见的是:分别控制child的存储和parent的存储。在我们的例子中,我们可以容易地做到这一点,如果我们允许用户在同一个html page编辑Category和ProductS。用set表示Products是非常直观的:

				
				
				
						public class CategoryForm extends ActionForm {
     private Set productForms;
     ...
     public void reset(ActionMapping mapping, HttpServletRequest request) {
        super.reset( mapping, request );

        for ( int i = 0; i < MAX_PRODUCT_NUM_ON_PAGE; i++ ) {
           ProductForm productForm = new ProductForm();
           productForm.reset( mapping, request );
           productForms.add( productForm );
        }
     }

     public void cleanupEmptyObjects() {
        for ( Iterator i = productForms.iterator(); i.hasNext(); ) {
           ProductForm productForm = (ProductForm) i.next();
           productForm.cleanupEmptyObjects();
        }
     }
}




更进一步
我们已经可以察看,编辑,提交forms,并且存储相关的objects,但是为所有的ActionForm类定义CleanupEmptyObjects()和reset()方法是个累赘。我们将用一个抽象的ActionForm来完成协助完成这些工作。

作为通用的实现,我们必须遍历所有的Hibernate管理的domain对象,发现他们的identifier,并且测试id值。幸运的是:org.hibernate.metadata包已经有两个Utility类能取出domain对象的元数据。我们用ClassMetadata类检查这个object是不是Hibernate管理的。如果是:我们把它们的id Value取出来。我们用了Jakarta Commons Beanutils包来协助JavaBean元数据的操作。

				
				
				
						import java.beans.PropertyDescriptor;
import org.apache.commons.beanutils.PropertyUtils;
import org.hibernate.metadata.ClassMetadata;

public abstract class AbstractForm extends ActionForm {
   public void reset(ActionMapping mapping, HttpServletRequest request) {
      super.reset( mapping, request );

      // Get PropertyDescriptor of all bean properties
      PropertyDescriptor descriptors[] =
         PropertyUtils.getPropertyDescriptors( this );

      for ( int i = 0; i < descriptors.length; i++ ) {
         Class propClass = descriptors[i].getPropertyType();

         ClassMetadata classMetadata = HibernateUtil.getSessionFactory()
            .getClassMetadata( propClass );

         if ( classMetadata != null ) {   // This is a Hibernate object
            String propName = descriptors[i].getName();
            Object propValue = PropertyUtils.getProperty( this, propName );

            // Evaluate property, create new instance if it is null
            if ( propValue == null ) {
               PropertyUtils.setProperty( this, propName, propClass.newInstance() );
            }
         }
      }
   }

   public void cleanupEmptyObjects() {
      // Get PropertyDescriptor of all bean properties
      PropertyDescriptor descriptors[] =
      PropertyUtils.getPropertyDescriptors( this );

      for ( int i = 0; i < descriptors.length; i++ ) {
         Class propClass = descriptors[i].getPropertyType();
         ClassMetadata classMetadata = HibernateUtil.getSessionFactory()
            .getClassMetadata( propClass );

         if ( classMetadata != null ) {   // This is a Hibernate object
            Serializable id = classMetadata.getIdentifier( this, EntityMode.POJO );

            // If the object id has not been set, release the object.
            // Define application specific rules of not-set id here,
            // e.g. id == null, id == 0, etc.
            if ( id == null ) {
               String propName = descriptors[i].getName();
               PropertyUtils.setProperty( this, propName, null );
            }


         }
      }
   }
}



为了让代码可读,我们省略了Exception的处理代码。

我们的新AbstractForm类从Struts的ActionForm类继承,并且提供了通用行为:reset和cleanup多对一关联对象。当这个关系是相反的话(也就是一对多关系),那么每个例子将会有所不同,类似在Abstract类里实现是比较好的办法。

总结

Struts和Hibernate是非常流行和强大的框架,他们可以有效地相互合作,并且弥补domain模型和MVC视图(view)之间的差别。这篇文章讨论一个解决Struts/Hibernate Project的通用的方案,并且不需要大量修改已经有的代码。

posted @ 2006-04-30 09:01 崛起的程序员 阅读(279) | 评论 (0)编辑 收藏
大家应该对这两个词很熟悉了,但是对词里包含的意义可能并不是特别清楚。首先必须说明的是,程序员和系统分析员不存在谁高级谁低级的分别,他们是两种职业,对职业技能的要求完全不同。所以厉害的程序员就是系统分析员的说法是不对的。当然,系统分析员的技能要求他必须要懂得如何写程序,但是他的重心在于如何把一个很大的项目切割成适合个人的小块,然后将这些小块组织起来。程序员的职责就是如何更好更快的实现这些小块。

在这章之前,我们讨论的都是一个合格的程序员应当具备的技能,当然不止那一些内容。之所以在这里插进来讨论系统分析员的事情,是因为我们的栏目叫做软件工程而不是程序员从入门到精通之类的。

在正式开始之前,我们还是来看在Thinking In Java中作者对分析和设计的一段精辟见解:

分析和设计

面向对象的范式是思考程序设计时一种新的、而且全然不同的方式,许多人最开始都会在如何构造一个项目上皱起了眉头。事实上,我们可以作出一个“好”的设计,它能充分利用OOP提供的所有优点。

请原谅在这里突然出现了OOP这个词,他的意思是面相对象,虽然在之前没有提到,但是在现在OO概念满天飞的软件世界里,大家应该对他不会太陌生。这里我简要的说明一下。在之前我介绍的实际上都是在很早以前程序写作流传下来的经验(什么,教我们老古董,打他!),但是以前的非OO(就是基于过程)的软件设计方法目前在国际上已经很少采用,所以我这里讲软件设计的时候所有的概念都是基于OO的。即使OO的概念很简单的啦,大家思考一下,我们再学习C++的时候一开始使用的类不都是一些动物啦、正方形啦之类的,都是生活中的例子,对吧。其实OO就是我们看世界的一种方式。可是最早由于计算机技术的不发达,我们不得不用一些很奇怪的描述来表达我们的意思,只有这样计算机才能理解,很笨不是吗。比如我们必须使用参数、过程、函数。所以当时的软件设计方法都是基于过程的。举一个简单的例子来显示OO设计方法和基于过程的设计方法之间的差别:一句简单的日常短语--“我吃饭”,用OO的方法来表述还是“我吃饭”,可是如果用基于过程的方法来描述的话就变成“我吃饭(饭)”,是不是很别扭呢。如果大家觉得对于OO的方法还有什么问题的话,可以去看一下软件工程专栏下的另一篇专题:《Thinking In Java赏析》

有关OOP分析与设计的书籍大多数都不尽如人意。其中的大多数书都充斥着莫名其妙的话语、笨拙的笔调以及许多听起来似乎很重要的声明。我认为这种书最好压缩到一章左右的空间,至多写成一本非常薄的书。具有讽剌意味的是,那些特别专注于复杂事物管理的人往往在写一些浅显、明白的书上面大费周章!如果不能说得简单和直接,一定没多少人喜欢看这方面的内容。毕竟,OOP的全部宗旨就是让软件开发的过程变得更加容易。尽管这可能影响了那些喜欢解决复杂问题的人的生计,但为什么不从一开始就把事情弄得简单些呢?因此,希望我能从开始就为大家打下一个良好的基础,尽可能用几个段落来说清楚分析与设计的问题。

不要迷失

在整个开发过程中,最重要的事情就是:不要将自己迷失!但事实上这种事情很容易发生。大多数方法都设计用来解决最大范围内的问题。当然,也存在一些特别困难的项目,需要作者付出更为艰辛的努力,或者付出更大的代价。但是,大多数项目都是比较“常规”的,所以一般都能作出成功的分析与设计,而且只需用到推荐的一小部分方法。但无论多么有限,某些形式的处理总是有益的,这可使整个项目的开发更加容易,总比直接了当开始编码好!

也就是说,假如你正在考察一种特殊的方法,其中包含了大量细节,并推荐了许多步骤和文档,那么仍然很难正确判断自己该在何时停止。时刻提醒自己注意以下几个问题:

(1) 对象是什么?(怎样将自己的项目分割成一系列单独的组件?)
(2) 它们的接口是什么?(需要将什么消息发给每一个对象?)

在确定了对象和它们的接口后,便可着手编写一个程序。出于对多方面原因的考虑,可能还需要比这更多的说明及文档,但要求掌握的资料绝对不能比这还少。

整个过程可划分为四个阶段,阶段0刚刚开始采用某些形式的结构。

阶段0:拟出一个计划

第一步是决定在后面的过程中采取哪些步骤。这听起来似乎很简单(事实上,我们这儿说的一切都似乎很简单),但很常见的一种情况是:有些人甚至没有进入阶段1,便忙忙慌慌地开始编写代码。如果你的计划本来就是“直接开始开始编码”,那样做当然也无可非议(若对自己要解决的问题已有很透彻的理解,便可考虑那样做)。但最低程度也应同意自己该有个计划。

在这个阶段,可能要决定一些必要的附加处理结构。但非常不幸,有些程序员写程序时喜欢随心所欲,他们认为“该完成的时候自然会完成”。这样做刚开始可能不会有什么问题,但我觉得假如能在整个过程中设置几个标志,或者“路标”,将更有益于你集中注意力。这恐怕比单纯地为了“完成工作”而工作好得多。至少,在达到了一个又一个的目标,经过了一个接一个的路标以后,可对自己的进度有清晰的把握,干劲也会相应地提高,不会产生“路遥漫漫无期”的感觉。

从我刚开始学习故事结构起(我想有一天能写本小说出来),就一直坚持这种做法,感觉就象简单地让文字“流”到纸上。在我写与计算机有关的东西时,发现结构要比小说简单得多,所以不需要考虑太多这方面的问题。但我仍然制订了整个写作的结构,使自己对要写什么做到心中有数。因此,即使你的计划就是直接开始写程序,仍然需要经历以下的阶段,同时向自己提出一些特定的问题。

阶段1:要制作什么?

在上一代程序设计中(即“过程化或程序化设计”),这个阶段称为“建立需求分析和系统规格”。当然,那些操作今天已经不再需要了,或者至少改换了形式。大量令人头痛的文档资料已成为历史。但当时的初衷是好的。需求分析的意思是“建立一系列规则,根据它判断任务什么时候完成,以及客户怎样才能满意”。系统规格则表示“这里是一些具体的说明,让你知道程序需要做什么(而不是怎样做)才能满足要求”。需求分析实际就是你和客户之间的一份合约(即使客户就在本公司内部工作,或者是其他对象及系统)。系统规格是对所面临问题的最高级别的一种揭示,我们依据它判断任务是否完成,以及需要花多长的时间。由于这些都需要取得参与者的一致同意,所以我建议尽可能地简化它们——最好采用列表和基本图表的形式——以节省时间。可能还会面临另一些限制,需要把它们扩充成为更大的文档。

我们特别要注意将重点放在这一阶段的核心问题上,不要纠缠于细枝末节。这个核心问题就是:决定采用什么系统。对这个问题,最有价值的工具就是一个名为“使用条件”的集合。对那些采用“假如……,系统该怎样做?”形式的问题,这便是最有说服力的回答。例如,“假如客户需要提取一张现金支票,但当时又没有这么多的现金储备,那么自动取款机该怎样反应?”对这个问题,“使用条件”可以指示自动取款机在那种“条件”下的正确操作。

应尽可能总结出自己系统的一套完整的“使用条件”或者“应用场合”。一旦完成这个工作,就相当于摸清了想让系统完成的核心任务。由于将重点放在“使用条件”上,一个很好的效果就是它们总能让你放精力放在最关键的东西上,并防止自己分心于对完成任务关系不大的其他事情上面。也就是说,只要掌握了一套完整的“使用条件”,就可以对自己的系统作出清晰的描述,并转移到下一个阶段。在这一阶段,也有可能无法完全掌握系统日后的各种应用场合,但这也没有关系。只要肯花时间,所有问题都会自然而然暴露出来。不要过份在意系统规格的“完美”,否则也容易产生挫败感和焦燥情绪。

在这一阶段,最好用几个简单的段落对自己的系统作出描述,然后围绕它们再进行扩充,添加一些“名词”和“动词”。“名词”自然成为对象,而“动词”自然成为要整合到对象接口中的“方法”。只要亲自试着做一做,就会发现这是多么有用的一个工具;有些时候,它能帮助你完成绝大多数的工作。

尽管仍处在初级阶段,但这时的一些日程安排也可能会非常管用。我们现在对自己要构建的东西应该有了一个较全面的认识,所以可能已经感觉到了它大概会花多长的时间来完成。此时要考虑多方面的因素:如果估计出一个较长的日程,那么公司也许决定不再继续下去;或者一名主管已经估算出了这个项目要花多长的时间,并会试着影响你的估计。但无论如何,最好从一开始就草拟出一份“诚实”的时间表,以后再进行一些暂时难以作出的决策。目前有许多技术可帮助我们计算出准确的日程安排(就象那些预测股票市场起落的技术),但通常最好的方法还是依赖自己的经验和直觉(不要忘记,直觉也要建立在经验上)。感觉一下大概需要花多长的时间,然后将这个时间加倍,再加上10%。你的感觉可能是正确的;“也许”能在那个时间里完成。但“加倍”使那个时间更加充裕,“10%”的时间则用于进行最后的推敲和深化。但同时也要对此向上级主管作出适当的解释,无论对方有什么抱怨和修改,只要明确地告诉他们:这样的一个日程安排,只是我的一个估计!

阶段2:如何构建?

在这一阶段,必须拿出一套设计方案,并解释其中包含的各类对象在外观上是什么样子,以及相互间是如何沟通的。此时可考虑采用一种特殊的图表工具:“统一建模语言”(UML)。请到http://www.rational.com去下载一份UML规格书。作为第1阶段中的描述工具,UML也是很有帮助的。此外,还可用它在第2阶段中处理一些图表(如流程图)。当然并非一定要使用UML,但它对你会很有帮助,特别是在希望描绘一张详尽的图表,让许多人在一起研究的时候。除UML外,还可选择对对象以及它们的接口进行文字化描述(就象我在《Thinking in C++》里说的那样,但这种方法非常原始,发挥的作用亦较有限。
我曾有一次非常成功的咨询经历,那时涉及到一小组人的初始设计。他们以前还没有构建过OOP(面向对象程序设计)项目,将对象画在白板上面。我们谈到各对象相互间该如何沟通(通信),并删除了其中的一部分,以及替换了另一部分对象。这个小组(他们知道这个项目的目的是什么)实际上已经制订出了设计方案;他们自己“拥有”了设计,而不是让设计自然而然地显露出来。我在那里做的事情就是对设计进行指导,提出一些适当的问题,尝试作出一些假设,并从小组中得到反馈,以便修改那些假设。这个过程中最美妙的事情就是整个小组并不是通过学习一些抽象的例子来进行面向对象的设计,而是通过实践一个真正的设计来掌握OOP的窍门,而那个设计正是他们当时手上的工作!

作出了对对象以及它们的接口的说明后,就完成了第2阶段的工作。当然,这些工作可能并不完全。有些工作可能要等到进入阶段3才能得知。但这已经足够了。我们真正需要关心的是最终找出所有的对象。能早些发现当然好,但OOP提供了足够完美的结构,以后再找出它们也不迟。

阶段3:开始创建

读这本书的可能是程序员,现在进入的正是你可能最感兴趣的阶段。由于手头上有一个计划——无论它有多么简要,而且在正式编码前掌握了正确的设计结构,所以会发现接下去的工作比一开始就埋头写程序要简单得多。而这正是我们想达到的目的。让代码做到我们想做的事情,这是所有程序项目最终的目标。但切不要急功冒进,否则只有得不偿失。根据我的经验,最后先拿出一套较为全面的方案,使其尽可能设想周全,能满足尽可能多的要求。给我的感觉,编程更象一门艺术,不能只是作为技术活来看待。所有付出最终都会得到回报。作为真正的程序员,这并非可有可无的一种素质。全面的思考、周密的准备、良好的构造不仅使程序更易构建与调试,也使其更易理解和维护,而那正是一套软件赢利的必要条件。

构建好系统,并令其运行起来后,必须进行实际检验,以前做的那些需求分析和系统规格便可派上用场了。全面地考察自己的程序,确定提出的所有要求均已满足。现在一切似乎都该结束了?是吗?

阶段4:校订

事实上,整个开发周期还没有结束,现在进入的是传统意义上称为“维护”的一个阶段。“维护”是一个比较暧昧的称呼,可用它表示从“保持它按设想的轨道运行”、“加入客户从前忘了声明的功能”或者更传统的“除掉暴露出来的一切臭虫”等等意思。所以大家对“维护”这个词产生了许多误解,有的人认为:凡是需要“维护”的东西,必定不是好的,或者是有缺陷的!因为这个词说明你实际构建的是一个非常“原始”的程序,以后需要频繁地作出改动、添加新的代码或者防止它的落后、退化等。因此,我们需要用一个更合理的词语来称呼以后需要继续的工作。

这个词便是“校订”。换言之,“你第一次做的东西并不完善,所以需为自己留下一个深入学习、认知的空间,再回过头去作一些改变”。对于要解决的问题,随着对它的学习和了解愈加深入,可能需要作出大量改动。进行这些工作的一个动力是随着不断的改革优化,终于能够从自己的努力中得到回报,无论这需要经历一个较短还是较长的时期。

什么时候才叫“达到理想的状态”呢?这并不仅仅意味着程序必须按要求的那样工作,并能适应各种指定的“使用条件”,它也意味着代码的内部结构应当尽善尽美。至少,我们应能感觉出整个结构都能良好地协调运作。没有笨拙的语法,没有臃肿的对象,也没有一些华而不实的东西。除此以外,必须保证程序结构有很强的生命力。由于多方面的原因,以后对程序的改动是必不可少。但必须确定改动能够方便和清楚地进行。这里没有花巧可言。不仅需要理解自己构建的是什么,也要理解程序如何不断地进化。幸运的是,面向对象的程序设计语言特别适合进行这类连续作出的修改——由对象建立起来的边界可有效保证结构的整体性,并能防范对无关对象进行的无谓干扰、破坏。也可以对自己的程序作一些看似激烈的大变动,同时不会破坏程序的整体性,不会波及到其他代码。事实上,对“校订”的支持是OOP非常重要的一个特点。

通过校订,可创建出至少接近自己设想的东西。然后从整体上观察自己的作品,把它与自己的要求比较,看看还短缺什么。然后就可以从容地回过头去,对程序中不恰当的部分进行重新设计和重新实现(注释⑩)。在最终得到一套恰当的方案之前,可能需要解决一些不能回避的问题,或者至少解决问题的一个方面。而且一般要多“校订”几次才行。

构建一套系统时,“校订”几乎是不可避免的。我们需要不断地对比自己的需求,了解系统是否自己实际所需要的。有时只有实际看到系统,才能意识到自己需要解决一个不同的问题。若认为这种形式的校订必然会发生,那么最好尽快拿出自己的第一个版本,检查它是否自己希望的,使自己的思想不断趋向成熟。

反复的“校订”同“递增开发”有关密不可分的关系。递增开发意味着先从系统的核心入手,将其作为一个框架实现,以后要在这个框架的基础上逐渐建立起系统剩余的部分。随后,将准备提供的各种功能(特性)一个接一个地加入其中。这里最考验技巧的是架设起一个能方便扩充所有目标特性的一个框架(对这个问题,大家可参考第16章的论述)。这样做的好处在于一旦令核心框架运作起来,要加入的每一项特性就象它自身内的一个小项目,而非大项目的一部分。此外,开发或维护阶段合成的新特性可以更方便地加入。OOP之所以提供了对递增开发的支持,是由于假如程序设计得好,每一次递增都可以成为完善的对象或者对象组。

⑩:这有点类似“快速造型”。此时应着眼于建立一个简单、明了的版本,使自己能对系统有个清楚的把握。再把这个原型扔掉,并正式地构建一个。快速造型最麻烦的一种情况就是人们不将原型扔掉,而是直接在它的基础上建造。如果再加上程序化设计中“结构”的缺乏,就会导致一个混乱的系统,致使维护成本增加。

计划的回报

如果没有仔细拟定的设计图,当然不可能建起一所房子。如建立的是一所狗舍,尽管设计图可以不必那么详尽,但仍然需要一些草图,以做到心中有数。软件开发则完全不同,它的“设计图”(计划)必须详尽而完备。在很长的一段时间里,人们在他们的开发过程中并没有太多的结构,但那些大型项目很容易就会遭致失败。通过不断的摸索,人们掌握了数量众多的结构和详细资料。但它们的使用却使人提心吊胆在意——似乎需要把自己的大多数时间花在编写文档上,而没有多少时间来编程(经常如此)。我希望这里为大家讲述的一切能提供一条折衷的道路。需要采取一种最适合自己需要(以及习惯)的方法。不管制订出的计划有多么小,但与完全没有计划相比,一些形式的计划会极大改善你的项目。请记住:根据估计,没有计划的50%以上的项目都会失败!

非常佩服作者对软件构建过程的精辟见解,软件工程是一门内容非常繁杂的学科,但是作者能够用浅显易懂的句子把它描述出来,真的是非常不简单。软件工程最早的提出者并不是计算机的专业人士,而是一位建筑设计师,所以软件工程的很多思想来自于建筑学。经过了几十年的发展,软件工程经历了很多次的蜕变。形成了今天的世界上以一些大公司提出的架构为主的形式:比如微软提出的COM及COM+以及基于其上的DNA体系,SUN提出的EJB,CORBA,还有BEA、WebLogic、IBM等公司的架构。虽然架构有不同,但是他们的思想都是相通的,架构的作用都是起到辅助开发者实现规范的、科学的软件开发过程。至于谈软件项目的管理和开发,那么Rational公司就是这方面的鼻祖。综合来说,目前世界范围内的软件工程提倡的就是以渐进的、螺旋式的开发方法构建基于组件的软件产品。现在说这些东西可能有些画饼的嫌疑,随着我们专题讨论的继续深入,这些概念就会很清晰的展现在面前。

虽然很希望能够继续的讨论软件工程方面的东东,但是我们的这个专题毕竟是讨论如何编写优美的程序的,离题还是不要太过分的好,至于软件工程的详细讨论,我会在接下去的专题中继续。在接下去的篇幅中,我们会继续讨论程序员和系统分析员之间的差别。

posted @ 2006-04-29 19:46 崛起的程序员 阅读(223) | 评论 (0)编辑 收藏

【 原 文 由 骄 傲 的 中 国 人 所 发 表 】
就 象 ken_qian 所 说 的 , “ 出 了国门,大家都在变。”短短半年间,自己确实在观念上改变了许多。回想起来,以前在国内自己只是一部不停运作的编程机器,整天写code,写code,写code,写code!一旦灵感一来,就想方设法地把所想的写出来,根本就没有考虑到编程以外的事情,结果,光是写出来的soucecode足用十几M,真是写得昏天黑地,日月无光!!:)可是,到了硅谷,情形突变!自以为能在鬼老面前,炫耀一下编程的能耐,好让他们对中国人刮目相看。可惜我错了!他们根本就不屑一顾!上的课多了,与不少在世界闻名的大电脑公司任职的教授也交谈过,发现自己是多么的无知,多么的肤浅。:(在上projectmanagement课时,已经明显体现出观念的差别。在上client/serveroverview时,更加突出了,整本教材(在这里这本书是专业人员必读的)没有一行code,几乎涵盖了现在所有流行的先进client/server(2/3-tiers)技术。从软件的角度分析它们的起源,发展和前途,还有各种功能相似技术的对比。整本书贯穿着一种思想,它与国内被绝大多数人奉行为至理名言的一句话-"不管黑猫白猫,只要抓到老鼠就是好猫!"--截然相反:“即使能抓到老鼠的黑猫白猫,也不一定是好猫!”。而这堂课的project就是做一个3-tierclient/server的项目,不用写code,只是要详细写出用到的结构和技术,为什么要用这而不用那?其实就是让你对各种相近技术进行详细比较,清楚地认识各种技术的优劣!!

短短半年研究生学习,与本科时确实是天壤之别。人家认为本科只是写code的时期,给出一个项目,只要能完成就算成功了。而到了研究生阶段,就要学会分析比较,对所用技术一定要能说出个道理:“在众多技术中,为什么你要选择这个?”还要经得起别人的“穷追猛打”。举个例子:抓到老鼠的黑猫白猫,白的一天能抓10只老鼠,而黑的只能抓5只,但是白的饭量很大,是黑的两倍。黑的比白的要便宜一倍。。。那么到底谁是好猫呢??:)

象vcc所说的“程序员最重要的是思维能力,只有想不出,没有编不出”。在如今,internet流行,和控件泛滥的年代,对于大多数程序已经不是能不能写出来的问题了。我不是什么绝顶高手,也不是一个博学多才的人,有许多编程的问题还是不懂,但狂妄的说一句,现在只要能给我钻研上几天,长的几周,就没有什么不能编不出来的。可惜这有什么用呢,充其量只是一部编程机器。

只会给人牵着走,整天做牛做马。这里培养的是具有大局观的人才,编程水平可能不高,但是活跃的思维,管理的能力和高瞻远瞩的眼界是我自愧不如的。就象BILLGATES当年若没有超凡的管理头脑,他现在可能也只是一部顶级的编程机器。

对于中国,以前没有internet,资料奇缺。我以前学C++的时候,周围的人还不知道是什么玩意!靠的就是ONLINEHELP和以后的MSDN,想问别人,也没人懂!就象我在签名档所写的“孤身走我路...”。如今,internet的流行,我认为技术已经不是一个主要问题了,不懂的,上网查询,问人,什么最新的资料,sourcecode,控件应有尽有,还怕写不出来?!作为过来人,我只是想努力地把硅谷的一丁点文化,一丁点精神带给国内的同行。君不见,我所发表的每一长篇“大论”,全都是从大局出发,从观念出发,很少涉及到具体的编程代码。我也是中国人,我深知在“有中国特色”的制度下,硅谷的文化和精神是很难实现的。但是我总认为,虽然不能在现实社会中实现,但是可以在网上,在这虚拟的世界中营造一种气氛,使大伙能体会一下这种感觉。学C的毕竟是编程的正宗,有不少高手,而且是中国计算机业的中流砥柱。所以我选择了C版作为开始。可惜我错了,大家也许受现实工作生活的压力,在网上也不能够摆脱。就如我当年一样,“只是一部不停运作的编程机器”。你们总是抱怨现实的中国怎样怎样,个人如何如何渺小。可是到了网上,到了这个自由的天地,根本没有什么污腐制度的束缚,什么文凭证书的限制,完全可以靠大伙每个人的力量来实现,却不见有什么实际的行动。哈哈,怨天尤人有什么用,根本就不从自身找找原因,那么即使是给你一个很好的环境,一个很好的制度,结果又是怎样呢??我不希望这就是中国人的劣性,若是这样,我也无话可说了。

写完这遍文章,我也累了。也许,不!应该是肯定会招来不少人的反感,你们也许不屑一顾,也许破口大骂。不过我真的累了!!个人力量的确很渺小,要改变中国人的固有观念,的确不是我力所能及的。况且我本身也有许多不足。也许就象国歌所唱的:“中华民族到了最危险的时候”,每个人才被迫“发出最后的吼声,起来,起来,起来!!。。。”最后,还是象我在签名档所写的:“孤身走我路...”

Goodnight,everybody!

/Crazyjava

孤身走我路...

其实,路,两个人一起走比一个人要好。

posted @ 2006-04-29 19:37 崛起的程序员 阅读(169) | 评论 (0)编辑 收藏
我们都知道JAVA是一种解析型语言,这就决定JAVA文件编译后不是机器码,而是一个字节码文件,也就是CLASS文件。而这样的文件是存在规律的,经过反编译工具是可以还原回来的。例如Decafe、FrontEnd,YingJAD和Jode等等软件。下面是《Nokia中Short数组转换算法》

thread.jspa?threadID=872&tstart=0

类中Main函数的ByteCode:

0 ldc #16 
2 invokestatic #18 
5 astore_1
6 return

其源代码是:short [] pixels = parseImage(\"/ef1s.png\");

我们通过反编译工具是可以还原出以上源代码的。而通过简单的分析,我们也能自己写出源代码的。

第一行:ldc #16 
ldc为虚拟机的指令,作用是:压入常量池的项,形式如下

ldc index

这个index就是上面的16,也就是在常量池中的有效索引,当我们去看常量池的时候,我们就会找到index为16的值为String_info,里面存了/ef1s.png.

所以这行的意思就是把/ef1s.pn作为一个String存在常量池中,其有效索引为16。

第二行:2 invokestatic #18 

invokestatic为虚拟机指令,作用是:调用类(static)方法,形式如下

invokestatic indexbyte1 indexbyte2

其中indexbyte1和indexbyte2必须是在常量池中的有效索引,而是指向的类型必须有Methodref标记,对类名,方法名和方法的描述符的引用。

所以当我们看常量池中索引为18的地方,我们就会得到以下信息:

Class Name : cp_info#1 

Name Type : cp_info#19 

1 和19都是常量池中的有效索引,值就是右边<>中的值,再往下跟踪我就不多说了,有兴趣的朋友可以去JAVA虚拟机规范。

这里我简单介绍一下parseImage(Ljava/lang/String;)[S 的意思。

这就是parseImage这个函数的运行,我们反过来看看parseImage的原型就明白了

short [] parseImage(String)

那么Ljava/lang/String;就是说需要传入一个String对象,而为什么前面要有一个L呢,这是JAVA虚拟机用来表示这是一个Object。如果是基本类型,这里就不需要有L了。然后返回为short的一维数组,也就是对应的[S。是不是很有意思,S对应着Short类型,而“[”对应一维数组,那有些朋友要问了,两维呢,那就“[[”,呵呵,是不是很有意思。

好了,调用了函数,返回的值要保存下来吧。那么就是第三行要做的事情了。

第三行:5 astore_1

呵呵,很简单的。但是却有文章,也是比较容易混乱的地方。

astore_为虚拟机指令,作用为:将当前reference存储到局部变量中去。而必须是对当前框架的局部变量的有效索引。打个比方,可能我们这个函数中可能还要用到这个局部变量,我们可以通过来找到它。例如调用虚拟机指令:

aload_1,就能得到该值。

第四行:6 return

同样的,return也是虚拟机指令了,它的作用为:从方法返回void。

这里也就是退出main函数。

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

ok,终于啰嗦完毕了。有些朋友可能要问,这么复杂,才四行就说这么多,呵呵,可能是我这人废话过多,当然如果你熟悉了,一点就能看懂了。通过肉眼就可以反编译程序了。目前所有的反编译工具都无法做到完美反编译,在有问题的地方还需要人去修正。

好了,说了半天如何反编译,我们就来看看如果在你的程序如果防止别人来反编译。好不容易写好的程序被人反编译了,多郁闷。哈哈。工欲善其事,必先利其器,这句话用对了吗?

什么混淆等等的方法,我就不说了,我这里主要是要说一种通过添加代码来在某种程度来避免当前流行的反编译工具对你的代码进行反编译。

方案一。

1,首先要添加一个参数为Exception类型的函数,例如这样。

public static void Fake(Exception e)
{
e.toString();
}

一定要有e.toString();,因为要防止你的混淆器把无用的代码过滤。

2,然后在每个类中调用这个函数,放在try...catch(Exception e)..中的catch里面,例如:

try
{
...
}
catch (Exception e)
{
Fake(e);
}

请注意 ,一定要放在catch才有用,其他地方无用。

方案二。

如果以上方法还不够专业,我们再来一个。呵呵~

1,同样的,我们定义一个类,这个类叫做AntiCrack.。名字好像有点大。。。代码如下:

public class AntiCrack
{

private AntiCrack()
{
}

public static Throwable Fake(Throwable throwable, Throwable throwable1)
{
try
{
throwable.getClass().getMethod(\"initCause\", new Class[] {
java.lang.Throwable.class
}).invoke(throwable, new Object[] {
throwable1
});
}
catch(Exception exception) { }
return throwable;
}
}

2,同样的,我们在catch里面调用该函数。例如如下。

try
{

//your code here 

}
catch(IOException ioexception)
{
IllegalArgumentException illegalargumentexception = new IllegalArgumentException(ioexception.toString());
AntiCrack.fake(illegalargumentexception, ioexception);
throw illegalargumentexception;
}

或者也可以这样

public class AntiException extends Exception
{

public AntiException()
{
}

public AntiException(String s)
{
super(s);
}

public AntiException(String s, Throwable throwable)
{
super(s);
AntiCrack.fake(this, throwable);
}
}

然后在你的程序里面 

try
{

}

catch(IoException e)

{

throw new AntiException(ioexception.toString(), ioexception);

}

当采用以上方式后,任何类只要调用了该函数,生成的class反编译后出错,得不到结果。

Decafe、FrontEnd和YingJAD,反编译时都有exception,然后无法进行下去。大家可以多测试变得反编译工具。建议推荐用第二个方法。
posted @ 2006-04-29 09:56 崛起的程序员 阅读(958) | 评论 (0)编辑 收藏
WML Script标准函数库

这节会讨论标准的WML Script函数库。

6.1 WML Script规则

这些标准函数库提供一个扩展WML Script语言的机制,这些特定的函数库必须遵循WML Script的规则。

支持的数据格式

下面的WML Script格式使用于程序的定义之中,这样能记录程序参数与回转值的格式。

Boolean
Integer
Float
String
Invalid

除此之外,如果整数与浮点数参数值格式都能接受的话,则能使用number来记录参数格式,如果使用的格式是所支持的格式,则能用any来记录。

数据格式转换

函数库程序发生错误的处理方式和WML Script语言一样。

invalid程序参数会产生invalid的回传值。

程序的参数无法转成所需要使用参数格式,则会产生invalid的回传值。

与程序相关的错误得出回传一个适当的错误码,至于这个值就要看每个程序如何定义。

6.2 Lang函数库

名称:Lang

说明:这个函数库所含的程序同WML Script语言的核心有很密切的关系。

abs

程序:abs(value)
说明:回传给予数的绝对值。
如果给予的数是整数,则回传整数。
如果给予的数是浮点数,则回传浮点数。
参数:value=数字。
回传值:数字或invalid。
例外状况:var a =-3;
var b =Lang.abs(a);//b=3

min 


范例:var a = -3
var b = Lang.abs(a); 
var c = Lang.min(a.b);
var d = Lang.min(45、76.3);//d=45(ingteger)
var e = Lang.min(45、76.3);//e=45(ingteger)

max

程序:max(value1,value2)
说明:回传值给予的两个数之间的较大值,回传的值于格式同所选数值的值与格式相同,其选取的方式如下:
WML Script运算符数据格式的整数与浮点数转换法则可用来确认数据格式,以便执行比较的动作。
参数:value1 =数字
value2 =数字
回传值:数字或invalid
例外状况:无
范例:var a =-3;
var b =Lang.abs(a);//b=3
var c = Lang.min(a.b);
var d = Lang.min(45、76.3);//d=45(ingteger)
var e = Lang.min(45、76.3);//e=45(float)

parseInt

程序:parseInt(value)
说明:回传由字符串value所定义的整数值,合法的整数语法由WML Script数值字符串文法或是近值整数是字所界定,下列为额外的解析法则:
第一个字符不是由+、-或十进制数字当开头的话,解译结束。
结果:解析过的字符串回转换整数值。
范例:var i =Lang.parseInt(\"1234\"); // i=1234
var j =Lang.parseInt(\"100 m/s\"); // j=100

parseFloat

程序:parseFloat(value)
说明:回传由字符串value所定义的浮点数值,合法的浮点数语法由WML Script数值字符串文法或是近值整数实字所界定,下列为额外的解析法则:
第一个字符无法解析成浮点数表达式,解析结束。
结束:解析过的字符串回转换成浮点数。
参数:value=字符串
回传值:浮点数或invalid
例外状况:解析错误则传回invalid
范例:var a =Lang.parseFloat(\"123.7 Hz\"); // a=123.7
var b =Lang.parseFloat(\"7.34e2 Hz\"); // b=7.34e2
var c =Lang.parseFloat(\"70.0e-2 F\"); // c=70.0e-2
var d =Lang.parseFloat(\"-1.c\"); // d=0.1
var e =Lang.parseFloat(\"100\"); // e=100.0
var f =Lang.parseFloat(\"Number:5.5\"); // f=invalid
var g =Lang.parseFloat(\"7.3e meters\"); // g=invalid
var h =Lang.parseFloat(\"7.3e- m/s\"); // h=invalid

isInt

程序:isInt(value)
说明:如果各预的值value能使用parseInt(value)转成整数则回传布尔值ture,否则传回false。
参数:value=任意值
回传值:布尔值或invalid
例外状况:无
范例:var a=Lang.inInt(\"-123\"); //ture
var a =Lang.minInt(\"123.33\"); //ture
var a =Lang.minInt(\"string\"); //false
var a =Lang.minInt(\"#123\"); //false
var a =Lang.minInt(\"invalid\"); //invalid

isFloat

程序:isFloat(value)
说明:如果各预的值value能使用parseInt(value)转成整数则回传布尔值ture,否则传回false。
参数:value=任意值
回传值:布尔值或invalid
例外状况:无
范例:var a=Lang.inInt(\"-123\"); //ture
var a =Lang.minInt(\"123.33\"); //ture
var a =Lang.minInt(\"string\"); //false
var a =Lang.minInt(\"#123\"); //false
var a =Lang.minInt(\"invalid\"); //invalid

maxInt

程序:maxInt()
说明:传回最大的整数值。
参数:无
回传值:整数2147483647
例外状况:无
范例:var a =Lang.minInt();

minInt

程序:minInt()
说明:传回最小的整数值
参数:无
回传值:整数-2147483647
例外状况:无
范例:var a =Lang.minInt();

float

程序:float()
说明:如果有支持浮点数的话传回ture,没有的话传回false。
参数:无
回传值:布尔值
例外状况:无
范例:var floatsSupported = Lang.float();

exit

程序:exit(value)
说明:结束WML Script位码的解译然后回到调用WML Script解译器者的控制,并回传指定值value,你可以使用这个程序来执行由一般程序的结束,而且WML Script位码的执行必须停止。
参数:valre=任意值
回传值:无,这个程序结束解译
例外状况:无
范例:Lang.exit(\"Value:\" + myVal);//Returns a string
Lang,exit(invalid);// Returns invalid

abort

程序:abort(errorDescription)
说明:中止WML Script位码的解译然后回到调用WML Script解译器者的控制,并回传 errorDescription,你能使用这个程序执行不正常的中止,调用程序者检测到有严重错误,WML Script的执行并须中断。
如果errorDescription的格式为invalid,字符串invalid用代替errorDescription的使用。
参数:errorDescription =字符串
回传值:无,这个程序结束解译
例外状况:无
范例:Lang.abort(\"Error:\" + errVal); // Error value string

radndom

程序:random(value)
说明:回传一个正数的整数值,也就是说要大于或等于零,但必须要小于给定值value,回传值是由近是正常分布所随机选取的值。
参数:value=整数
回传值:整数或invalid
例外状况:如果value等于0,则程序回传0
如果value小于0,则程序回传invalid
范例:var a =10;
var b =Lang.random(5.1)*a;//b=0..50
var c = Lang.random(\"string\"); // c=invalid

reed

程序:seed(alue)
说明:初始化需随机数字顺序并回传一个空字符串
如果value为0或正整数,给予的value则用来初始化,反之则使用随机初始化的值。
如果value为浮点数,则会先使用Float.int()来计算确切的整数值。
参数:value=整数
回传值:字符串或invalid]
例外状况:无
范例:var a =Lang.reed(123);// a=\"\"
var b =Lang.random(20); // b=0..20
var c = Lang.seed(\"seed\"); // c=invalid (random seed //left unchanged)

characterSet

程序:characterSet()
说明:回传WML Script解译器所支持的字集,回传只是个整数用来记录由IANA所设定的MIB Enum值,这个只能表示所有的字集。
参数:无
回传值:整数
例外状况:无
范例:Var charset = Lang.characterSet(); //charset = 4 for latinl

6.3 Float函数库

名称:Float
说明:这个函数库包含了典型与常用的浮点数算术程序。

int

程序:int(value)
说明:回传给予值的整数部分。
参数:value=数字
回传值:整数或invalid
例外状况:无
范例:var a =3.14;
var b =Float.in(a); //b=3
var c =Float.in(-2.8); //c=-2

floor

程序:floor(value)
说明:回传整数值,这个只要最接近给予值但不能大于它。
如果value已经是个整数,其结果就是这个值本身。
参数:value=数字
回传值:整数或invalid
例外状况:无
范例:var a =3.14;
var b =Float.in(a); //b=3
var c =Float.in(-2.8); //c=-3

ceil

程序:ceil(value)
说明:回传一个只要最接近给予值但不能小于它的整数值。
如果value已经是个整数,其结果就是这个值本身。
参数:value=数字
回传值:整数或invalid
例外状况:无
范例:var a =3.14;
var b =Float.in(a); //b=4
var c =Float.in(-2.8); //c=-2

pow

程序:pow(x,y)
说明:回传x的y次方值。
如果x是负数,则y必须为正数。
参数:x=数字
    y=数字
回传值:浮点数或invlid
例外状况:如果x= =0而且 y<0,则回传invalid
如果x<0而且y不是个整数,则回传invalid
范例:var a =3
var b =Float.pow(a,2); //b=9

round

程序:round(value)
说明:传回最接近给予值的整数
若两个整数值跟value接近的程序相等,则选择比较大的数。
若value已经是个正数,其结果就是value本身。
参数:value=数字
回传值:整数或invalid
例外状况:无
范例:var a=Float.round(3.5); // a=4
var b=Float.round(-3.5); //b=-3
var c=Float.round(0.5); // c=1
var d=Float.round(-0.5); //d=0

squt

程序:sqrt(value)
说明:传回给予值value的平方根近似值。
参数:value=浮点数
回传值:浮点数或invalid
例外状况:如果value负数,则回传invlid
范例:var a=4;
var b=Float.squt(a); //b=2.0
var c=Float.squt(5); //c=2.2360679775

maxFloat

程序:maxFloat()
说明:传回IEEE 754所支持的但准浮点数格式中最大的浮点数值。
参数:无
回传值:浮点数3.40282347E+38
例外状况:无
范例:var a=Float.maxFloat();

minFloat

程序:minFloat()
说明:传回IEEE 754所支持的但准浮点数格式中最小的浮点数值。
参数:无
回传值:浮点数1.17549435E-38
例外状况:无
范例:var a=Float.minFloat();

6.4 string函数库

名称:字符串
说明:这个函数库包含了字符串程序的集合,一个字符串可以是字符数组,每个字符都有个索引,字符串的第一个字浮的索引为0,字符串的长度是字符在数组中的数目。

你能使用一些特殊的分隔符号来界定不同的字符串,这样你就能存取这些有分隔符号予元素索引所界定出的元素,字符串中第一个元素的索引值为0,每个字符串分隔符号回分隔出两个元素,但字符是不能用来做分隔符号。
一个空格的字符可能是下列字符其中之一:

TAB:水平跳格定位(horizontal tabulation)
VT:垂直跳格定位(ertival tabulation)
FF:跳页(from feed)
SP:空格(space)
LF:跳行(line feed)
CR:归位(carriage return)

length

程序:length(string)
说明:传回给予字符串的长度(字符的数目)。
参数:string=字符串
回传值:整数或invalid
例外状况:无
范例:var a=\"ABC\";
var b=string.length(a); //b=3
var c=string.length(\"\"); //c=0
var d=string.length(342); //d=3

is Empty

程序:is Empty(string)
说明:如果字符串长度为零则传回布尔值true,反之传回false。
参数:string=字符串
回传值:布尔值或invalid
例外状况:无
范例:var a=\"Hello;
var b=\"\";
var c=sting.isEmpty(a); //c=false
var c=sting.isEmpty(b); //d=ture 
var c=sting.isEmpty(ture); //e=false 

charAt

程序:charAt(sting.index)
说明:回传string中index值所指定的字符。
参数:string=字符数
index=数字(回传回index所指定的字符)
回传值:字符串或invalid
例外状况:如果index的值超过字符串的范围,则回传空字符串(\"\")
范例:var a=\"My name is Joe\"
var b=sting.charAt(a,0); //b= \"M\"
var c=sting.charAt(a,100); //c= \"\"
var d=sting.charAt(34.0); //d=\"3\"
var e=sting.charAt(a,\"first\"); //e=invalid

subString

程序:subString(string,startIndex,length)
说明:传回一个新的字符串来代替所给予的字符串, 这个新字符串给定的索引值开始,它的长度有所给予的length决定。
如果startIndex小于0,则会0来当作索引值。
如果length大于剩余字符的数目,则lenght会由剩余的字符数来代替。
如果startIndex予lenght是浮点数,则会先使用Float.int()来计算正确的整数值。
参数:string=字符数
startIndex=数字
lenght=数字
回传值:字符串或invalid
例外状况:如果startIndex大于最后的索引值,则回传会空字符串(\"\")
如果lenght<=0,传会空字符串(\"\")
范例:var a=\"ABCD\";
var b=String.subString(a,1,2); //b=\"BC\"
var c=String.subString(a,2,5); //c=\"CD\"
var d=String.subString(1234,0,2); //d=\"12\"

find

程序:find(string,substring)
说明:传会所要寻找的字符串substring和原始字符串string相符的第一个字符的索引值。
如果没有相符,则传会整数值-1。
两个字符传如果是相等的话,是定义为不符合。
参数:string=字符串 
substring=字符串
回传值:整数或invalid
例外状况:无
范例:var a=\"abcde\";
var b=String.find(a,\"cd\"); //b=2
var c=String.find(34.2,\"de\"); //c=-1
var d=String.find(a,\"gz\"); //d=-1
var e=String.find(34,\"3\"); //e=0

replace

程序:eplace(tring,oldSubString,newSubString)
说明:传会新字符串,这个新字符串是由和所给予字符串string相符的旧字符传oldSubString使用新字符串newSubString字符串加以代替。如果两字符串相等的话,定义为相符。
参数:string=字符串
oldSubString=字符串
newSubString=字符串
回传值:字符串或invalid
例外状况:无
范例:var a=\"Hello Joe.What is up Joe?\";
var newName=\"Don\";
var oldName=\"Joe\";
var c=String.replace(a,oldName,newName); //c=\"Hello Don.What is up Don?\"
var d=String.replace(a,oldName,newName); //c=\"Hello Don.What is up Don?\"

element

程序:element(string,separator)
说明:回传分隔符号separator所分隔的字符串string的元素数目,空字符串(\"\")是有效的元素,这表示了这程序永远不会回传一个小于或等于0得值。
参数:string=字符串
separator=字符串
回传值:整数或invalid
例外状况:如果separator是个空字符串,则回传invalid
范例:var a=\"My name is Joe;Age 50\";
var b=String.elements(a,\"\");//b=6
var c=String.elements(a,\";\");//c=3
var d=String.elements(\"\",\";\");//d=1
var e=String.elements(\"a\",\"\");//e=1
var f=String.elements(\";\",\";\");//f=2
var g=String.elements(\";;,;\",\";,\");//g=4
separator=;

elementAt

程序:elementAt(string,index,separator)
说明:寻找字符串string的第index个元素,这些元素是由分隔符号separator所加以分开,并回传相对应的元素。
如果index值小于0,则回传第一个元素。
如果index值大于元素的数目,则回传最后一个元素。
如果字符串为空字符串,则回传空字符串。
如果index值为浮点数,则须先使用Float.int()来计算出正确的索引值。
参数:string=字符串
index=数字
separator=字符串
回传值:字符串或invalid
例外状况:如果separator是个空字符串,则回传invalid
范例:var a=\"Hello Joe.What is up Joe?\";
var b=String.elementAt(a,0,\"\"); //b=\"My\"
var b=String.elementAt(a,14,\";\"); //c=\" \"
var b=String.elementAt(a,1,\";\"); //d=\"Age 50\"

removeAt

程序:removeAt(string,index,separator)
说明:将符合索引值index的分隔号separator与元素有字符串string中移出,并回传这个新字符串。
如果index值小于0,则回传第一个元素。
如果index值大于元素的数目,则回传最后一个元素。
如果字符串为空字符串,则回传空字符串。
如果index值为浮点数,则须先使用Float.int()来计算出正确的索引值。
参数:string=字符串
element=字符串
index=数字
separator=字符串
回传值:字符串或invalid
例外状况:如果separator是个空字符串,则回传invalid
范例:var a=\" A A;B C D\";
var s= \"\";
var c=String.removeAt(a,1,s); //b=\"A B C D\"
var d=String.removeAt(a,0,\";\"); //c=\" B C D\"
var e=String.removeAt(a,14,\";\"); //d=\"A A\"

replaceAt

程序:replaceAT(string,index,separator)
说明:在特定的index中的元素,使用所给予的元素element来代替,并回传这个新字符串。
如果index值小于0,则回传第一个元素。
如果index值大于元素的数目,则回传最后一个元素。
如果字符串为空字符串,则回传空字符串。
如果index值为浮点数,则须先使用Float.int()来计算出正确的索引值。
参数:string=字符串
element=字符串
index=数字
separator=字符串
回传值:字符串或invalid
例外状况:如果separator是个空字符串,则回传invalid
范例:var a= \"B C; E\";
var s=\"\";
var d=String.replaceAT(a,\"A\",0,s); //b=\"A C;E\"
var e=String.replaceAT(a,\"F\",5,\";\"); //d=\"B C;F\"

InsertAt

程序:insertAt(string,index,separator)
说明:将元素element与相对应的分隔符号separator插入与原始字符串string,在特定的element中的元素
如果index值小于0,则0会用来当索引值。
如果index值大于元素的数目,则元素element会附加上字符串string的为端。
如果字符串string为空字符串,则回传包含所给予元素element的新字符串。
如果index值为浮点数,则需先使用Float.int()来计算出正确的索引值。
参数:string=字符串
element=字符串
index=数字
separator=字符串
回传值:字符串或invalid 
例外状况:如果separator是个空字符串,则回传invalid
范例:var a= \"B C; E\";
var s=\"\";
var b=String.insertAt(a,\"A\",0,s); //b=\"A B C;E\"
var c=String.insertAt(a,\"X\",3,s); //c=\"B C;E X\"
var d=String.insertAt(a,\"D\",1,\";\"); //d=\"B C;D;E\"
var e=String.insertAt(a,\"F\",5,\";\"); //e=\"B C;E;F\"

squeeze

程序:squeeze(string)
说明:将字符串string中所有连续的空格减少为一个空格。
参数:stromg=字符串
回传值:字符串或invalid
例外状况:无
范例:var a=\"Hello\";
var b=\"Bye Jon.See you!\";
var c=String.squeeze(a); //c=\"Hello\"
var d=String.squeeze(b); //d=\"Bye Jon.See you!\"

trim

程序:trim(string)
说明:将字符串string中所有开头与连续的空格删除。
参数:string=字符串
回传值:字符串或invalid
例外状况:无
范例:var a=\"Hello\";
var b=\"Bye Jon.See you!\";
var c=String.squeeze(a); //c=\"Hello\"
var d=String.squeeze(b); //d=\"Bye Jon.See you!\"

compare

程序:compare(string,string2)
说明:这个程序的回传值会指出string1与string2在语汇上关系,这个关系是基于自然字集的字符码之间,其回传值如下:
如果string1小于string2,传会-1。
如果string1等于string2,传会-1。
如果string1大于string2,传会-1。
参数:string1=字符串
string2=字符串
回传值:整数或invalid
例外状况:无
范例:var a=\"Hello\";
var b=\"Hello\";
var c=String.compare(a,b); //c=0
var d=String.compare(\"Bye\",\"Jon\"); //d=-1
var e=String.compare(\"Jon\",\"Bye\"); //e=1

toString

程序:toString(value)
说明:回传一个能表示所给予的值value的字符串,这个程序跟WML Script的转换是一样的,除了invlaid值会回传一个\"invalid\"字符串。
参数:value=任意值
回传值:字符串
例外状况:无
范例:var a=string.toString(12); // a=\"12\"
var a=string.toString(true); // b=\"true\"

format

程序:format(format,value)
说明:将给予的值value转换成字符串,并依照所给予的格式format提供的格式化的字符串,这个格式字符串只能由一种特定格式,并能放置于字符串的任何地方,如果超过一种以上的格式需要使用,则能会使用最左边的格式,至于其他格式则有空字符串代替,这些格式如下:

[width][.precision]type
width参数为非负的是近制整数,这个参数控制与显现字符的最小数目,如果输出的字数小于指定的宽度width,则会在字符串的左边加上空白,直到符合最小宽度的要求,width参数永远不会是值value被删减,如果输出的字数大于特定的宽度或并没指定宽度的话,value中所有的字符都会被显现。

precision参数是个非负的十进制整数,这个引号之前必须限价上(.)的符号,其目的是用来设定输入值的精确度,这个值的解议会跟给予的格式有关:

d 界定数字最小的显现数目,如果value中数字的数目超过precision的值,输入值会在其左边加上0,如果数字的数目超过precision值,value的值并不会被删减,预设的precision值为1,如果precision值设定为0,而且value页被转换成0,则结果将是一个空字符串。

f 界定十进制小数后的数字数目,如果十进制的小数点出现了,在小数点之后至少要有一位数,这个值会被四舍五入到近似的数字数值,预设的precision为6,如果precision为0或小数点(.)后没有数字,则不会显现小数点,当value值的小数点后数字数目小于precision的值,字母0为被加入直到填满栏位(如:String.format(\"%2.3f\",1.2)会是\"1.200\")

s 界定字符所要显现的最大数目,预设值是显现所有的字符,当width值大于precision值,width值是可以忽略的,跟width值不同的是,precision只可能会造成浮点数值的四舍五入或输入值的删减。

type参数是唯一格式的参数,他出现在任何的格式栏为选项之后,type字符决定了所给予的value将会解译成整数,浮点数或字符串,支持的type参数如下:
d 整数:输入值的格式[-]dddd,这里的dddd是一个或以上的十进制数字。
f 浮点数:输入值的格式[-]dddd.dddd,这里的dddd是一个或以上的十进制数字,在小数点之前的数字数目和数字的大小有关,小数点之后的数字数目和精确度有关。
s 字符串:字符的显现跟精确度有关。
百分比字符(%)在格式字符串中能使用额外的百分比字符加以表示(%%)。
参数:format=字符串
value=任意值
回传值:字符串或invalid
例外状况;无效的格式会回传invalid值。
范例:var a=45;
var b=-45;
var c=\"now\";
var d=1.2345678;
var e=String.format(\"e:%6d\",a); //e=\"e:45\"
var e=String.format(\"%6d\",b); //f=\"-45\"
var e=String.format(\"e:%6d\",a); //g=\"0045\"
var e=String.format(\"%6.4d\",b); //h=\"-0045\"
var e=String.format(\"Do it %s\",c); //i=\"Do it now\"
var e=String.format(\"%3f\",d); //j=\"1.2345678\"
var e=String.format(\"%10.2f%%\",a); //k=\"1.23%.\"
var e=String.format(\"%3f %2f\",a); //l=\"1.234567.\"
var e=String.format(\"%.0d\",0); //m=\"\"
var e=String.format(\"%.7d\",\"Int\"); //n=\"invalid\"
var e=String.format(\"%s\",ture); //o=\"ture\"
posted @ 2006-04-29 09:48 崛起的程序员 阅读(180) | 评论 (0)编辑 收藏
WMLScript脚本程序设计
作为一种编辑语言,WMLScript提供了强大的函数、语句和库功能,以及外部编辑、访问控制等支持,同时对程序运行中可能产生的错误给出了检测手段和具体的解决办法。这些内容属于WMLScript的脚本程序设计知识和进一步的编程规定,我们本章就对此进行详细介绍。

5.1 语句

前面我们学习了变量、操作符和表达式,但仅由这些内容并不能完成某个完整的功能,因为他们不能形成完整的操作或处理程序。变量就如同与严重的单词,表达式如语言中的词组,他们都不能表达一个完整的意思;只有语句,是语言中完整的句子,能够表达完整的意思并实现某个完整的功能。WML Script提供了丰富的语句功能,使用这些语句我们可以在WML的卡片中建立交互功能和其他需要的复杂功能。

在WML Script中,每条语句的后面都需要以一个分号(;)结尾。为了养成严谨的编程风格,建议大家编写脚本时,语句后一定要加上分号(;),这也有助于我们形成一种良好的编程习惯。

WML Script语句的书写和排列格式比较自由。我们可以在同一程序行中连续写上多个语句,也可以把同一语句分成多行排列。WML Script将根据分号(;)来确定语句的具体内容。

WML Script的语句主要包括两类。第一类是基本语句,如空语句、表达式语句、块语句、变量语句和return语句等;第二类是条件语句,如if语句;第三类是循环语句,如while语句、for语句、break语句和continue语句等。下面我们分别讲解这些语句的语法、功能和使用方法。

5.1.1 基本语句

WML Script基本语句主要用于程序格式控制和变量声明,其中有些语句我们已经不太陌生了。

空语句

空语句用于定义一个空的程序行,它没有任何标识符和操作符,也不执行任何操作。它只是以分号(;)结束。其语法格式为:



显然,空语句是一种十分特殊的语句。由是我们为了让程序具有更好的可读性,通常在程序中的适当地方加上几个空语句,以起到分隔或突出的作用。例如,下面的几行程序中就含有一个空语句:

str=\"Hello\";

val=25;

;

MyVal=val*val+5;

alert(\"Hi,Hi!!!\");

再如,while语句用于判断一个条件并在条件满足的时候执行相应的任务,但如果希望条件满足的时候什么也不作,那么就可以给它配上一个空语句,使之条件满足的执行空操作:

while(!poll(device));

这实际上是while语句和空语句组成的两条语句。其中的分号(;)在这里就代表了空语句。这两条语句的作用是在poll()函数为真()之前一直等待。

表达式语句

表达式语句用于向变量赋值,或进行数学计算,或进行函数调用。表达式语句也是我们最常用的一种语句,语法格式为:

表达式;

下面几行程序都是合法的表达式语句:

str=\"Hey\"+yourName;
val3=prevVal+4
counter++;
myValue1=counter,myValue2=val3
alert(\"Watch out!\");
retVal=16*Lang.max(val3,counter); 

块语句

块语句使用两个花括号({ })包含一个语句集,形成一个语句体。WML Script的许多语句都需要使用块语句来实现语句体,快语句的语法格式为:

{

语句列表;

}
下面的简单程序就是使用块语句的例子:


}
vari=0;
var x=Lang.abs(b);
popUp(\"Remember!\");

}

变量语句

变量语句用于生命变量并可进行变量的初始化赋值。如果用户不赋值,那么WML Script会自动将变量语句生命的变量赋予一个空字符串(\"\")。基本的语法格式为:

var 变量名;

如果像一次生命多个变量,则相邻变量名之间使用逗号(,)间隔,其语法格式为:

VAR 变量名1,变量名2......,变量名n;

如果想在生命变量时同时初始化变量,则可按如下语法格式书写:

var 变量名=初始化

为便于大家更好的掌握变量语句,我们给出一个多出使用该语句的程序: 


function count(stu){

var result=0; //声明变量的同时初始化变量

while(str!=\"\"){

var ind=0; //每次循环都初始化一次

// 为退出循环,本块语句内应当提供修改变量str值的语句

};

return result

};

function example(param){

var a=0;

if(param>a){

var b=a+1; //声明b变量的同时使用a变量初始化b变量
}else{

var b=a+2; //声明c变量的同时使用a变量初始化c变量
};

return a; //返回a变量的值

}; 

注释语句

严格来讲,注释语句在WML Script中还不算是真正的语句,它只是一种前指向的规定。不过它也有严格的语法和标注方法,所以我们这里还是像其他编程语言处理的一样,把WML Script注释方法以语句的形式介绍一下。

注释在程序执行的时候没有任何作用,但是可以用于对程序进行解释,则增强程序的可读性。为了形成良好的编程风格,我们应该养成书写注释的良好习惯,注释有两种表达方式:

(1)通过双斜线注释一行,这样在双斜先后的字符将成为注释而不被执行。该注释行可以单独一行书写,也可以放在其他语句的后面。

例如,可以进行如下所示的注释:

//变量j用于小数每月的天数

j=0; //我们这里将j赋值为0

(2)通过符号“/*”和“*/”来规定注释语句,这种注释方式可以进行多行注释,符号“/*”和“*/”之间的内容就是注释语句。例如,可以进行如下所示的多行注释:

/*我们定义了两个变量:i和j。其中:

i用于描述每年中的月数,

而j用于描述每月的天数*/

j=0; /*我们这里将j赋值为0*/

return语句

return语句主要用在函数体中,在函数结束前,可以通过return语句,把函数处理的结果返回给调用函数的语句。它的语法格式如下:

return表达式;

下面的函数给出了应用return语句的例子: 

function square(x){

if(!(Lang.isFloat(x)))return invalid;

return x*x

}; 

5.1.2 条件语句

在条件语句中,当满足某种条件时 ,就会执行指定的一些代码,而在代码组另外某种条件时,则会执行另外一些代码。WML Script的条件语句就是if...else语句,它的一般表达相识如下:

if(条件){

代码块1

}

else{

代码块2



这样,当条件满足时,就执行代码块1;如果条件不满足则执行代码块2。代码块和代码块2中如果只有一个条语句,那么,花括号({ })就可以省略;而如果有多条语句,则必须实用花括号将代码块包括在其中。在if...else语句种,其中的else部分是可选的,也就是说,我们可以使用如下的表达形式:

if(条件){

代码块

}

这样,当条件满足时,就执行代码块,如果条件不满足则什么也不做。

例如,如果我们需要对一个学生的成绩进行判定,如果大于等于60分,那么我们就认为该学生成绩合格了,反之则认为不合格,同时一并将状态记录到变量status种,相应的WMLScript语句如下所示:

if(score>=60) status=\"pass\";

else status=\"fail\";

再如,我们可以通过对天气是否阳光普照(sunShines)的情况进行判断,来给变量myDay赋值,并累计好天气(goodDays)的天数。程序如下:

if(sunShines) {
myDay=\"Good\";

goodDays++;

}else

myDays=\"Oh well...\"; 


5.1.3 循环语句

使用循环语句可以反复执行某个代码块,直到循环结束条件满足后才停止执行。WML Script中有两种循环语句:for语句和while语句,同时还有两终于循环密切相关的操作语言:break语句和continue语句。

for语句

for语句可以创建一个带条件的循环,它还有3个可选的条件表达式,用于控制循环。这3个条件表达式放在一个括号里,并以分号(;)间隔。for语句的一般语法形式如下:

for(初始表达式;循环条件;递增表达式){

代码块

}


for语句的执行主要包括以下几个步骤:

(1)执行初始表达式。在一般情况下,初始表达式完成的功能是在循环中对循环计数器赋初值。所以在这种意义上,初始表达式也可以采用“var 变量声明列表;”的形式来定义。

(2)判断循环条件。如果循环条件为真(ture),则执行循环体中的语句,即至步骤(3);否则,循环条件为假(false)或者invalid,就结束循环;

(3)执行循环代码。然后,在执行递增表达式。一般情况下,我们在递增表达式中对循环计数器进行处理,最后在返回步骤2执行。

例如,下面的for语句建立了一个循环。初始表达式为定义变量index并付初值0,循环条件为index<100,递增表达式为每循环一次index增加1。当index增加到100时候,循环结束。程序如下: 

for(var index=0;index<100;index++){

count+=index;

myFunc(count);

}; 


while 语句

while语句也可常见一个循环,它的一般语法表达式如下:

while(循环条件){

代码块




while语句的执行过程包括以下几个步骤:

(1)判断循环条件是否为真。如果循环条件为真,则执行循环;如果为假或为invalid,则跳出循环。

(2)执行循环中的代码块,然后返回步骤(1)

下面的程序就是使用while语句的简单例子:

var counter=0

var total=0

while(counter<3){

counter++;

total+=c;

}; 


其中建立的循环仅当变量counter的值小于3时执行,否则就结束循环。

显然,如果循环条件不能为假或为invalid,那么while循环就会无休止的反复执行下去。因此,我们在代码块中一定要有能够改变循环条件的变量,否则,就很有可能会陷入死循环而不能终止程序,下面就是一个死循环的例子:

var x=1;

var y=0;

while(x<=1){

y=x+1;




这个程序中,因为变量x的值在循环中不能发生变化,所以循环条件在判断的时候永远为真,所以成为了死循环。因此,对于while语句我们往往使用如下所示的语法形式:

初始表达式

while(循环条件){

代码块

递增表达式

}




这种情况下,while语句的功能和for语句的功能就一样了,不过用while语句编写的程序可读性更强一些。所以我们也可以采用while语句来完成index增加到100的循环。

程序如下:

var index=0

while (index<100){

counter+=index;

myFunc(count);

index++;

}; 


break语句

为了更好的解决死循环问题,WML Script项大多数编成语言一样提供了break语句。break语句可以使程序执行跳出循环。不论是for语句还是while语句,只要在循环中使用了break语句,那么程序执行到break语句后就立即跳出当前循环,然后继续执行下去。

break语句的予发行时如下:

break;

例如,在下面的函数中我们使用了break语句,它是当index=3时跳出循环。如果不是用该语句,函数中的while循环直到index=6时才可以结束。程序如下:

funcition testBreak(x){

var index=0;

while(index<6){

if(index==3)break;

index++

};

retrun index*x;

; 


continue语句

continue语句的功能和break语句的功能看起来有些类似,但实际上却不一样。循环执行时遇到break语句通常是跳出当前循环,但循环执行到continue语句并不跳出当前循环,而是不执行循环中在continue语句后面的代码块,直接结束循环的本轮运行,然后马上开始下一轮循环的运行。

在while语句的循环中,遇到continue语句后,程序会直接判断循环条件从而开始下一轮循环。在for语句的循环中,遇到continue语句后程序会直接执行递增表达式,然后判断循环条件从而开始下一轮循环。

例如,我们想利用for循环求1到10之间偶数的和,其WML Script语句如下:

var sum=0;

for (var j=1;j<=10;j++){

if(j%2!=0)

continue;

sun+j;

}; 


在这个例子中,在j%2!=0的情况下,也就是j为奇数的情况下,程序执行continue语句,这时,并没有如同break语句一样跳出循环的运行,而是不执行循环中后面的语句而直接执行递增表达式开始下一轮循环的执行,这样,就可以不将其数j的之类即如总和中。

再如,我们想利用while循环求0到4之间出3以外几个数的和,则可以使用continue语句进行控制。程序如下;

var index=0;

var count=0;

while (index<5){

index++;

if(index==3)

continue;

cont+=index;

}; 


这以程序中,当index等于3时,“index==3”为真,所以执行continue语句,不再把此时index的值加大count中,而是开始下一轮的循环。 
5.2 函数的声明与调用

在WML Script中,函数是一种能够完成某种功能的代码块,并可以在脚本中被事件处理或被其他语句调用,也可以被WML程序所处理和调用。一般地,当我们编写WML Script脚本时,如果脚本中的代码长度还是很长,则一般还可以根据功能将函数再进行划分,分成为几个功能更加单一的函数。虽然说这样对长代码的处理方法并不是编写脚本程序的强制性要求,但通过函数的划分和运用,我们可以使得WML Script脚本具有更好的可读性,也便于我们对脚本程序的编写与调试。而且,如果在某些脚本中有多处完全相同的代码块,那么我们也可以将这些代码快些为一个函数,然后在脚本中调用这个函数,从而提高代码的重要性,简化代码的编写工作。

WML Script的函数共用和Jave语言、C/C++语言的函数有所不同。我们知道,Jave语言、C/C++语言中有函数和过程之分,函数能够完成一定的功能并有返回值,而过程进可完成一定的功能但没有返回值。可是,WML Script中并不区分函数和过程,因为它只有函数,没有过程。WML Script的函数完成一定功能后始终有返回值,不过返回值分两种情况,即非空的返回值和空字符串(\"\")形式的返回值。前者是真正的返回值,后者其实相当于没有返回值。也就是说,WML Script中的函数同时具有其他语言中的函数和过程的功能。

5.2.1 函数的声明

使用函数时,要根据函数的调用使用,而调用函数前必须声明函数,也就是需要先定义函数。WML Script中定义函数的一般方式如下:

function函数名(参数列表)

{

代码块

};

另外,WML Script规定使用extern关键字来声明一个外部函数:

extern function函数名(参数列表)

{

代码块

};

从中可以看出,函数的定义有以下3部分组成:

(1)函数名。即函数的名称,其命名规则应遵守WML Script的标识规则。调用函数时都是通过函数名进行调用的,所以函数必须要有函数名。

函数命名时,一般要使用能够描述函数功能的但此来作为函数名,也可以使用多个单词组合进行命名,这样做的好处是能够提高WML Script脚本的可读性。

函数名在同一个WML Script脚本文件里必须是唯一的。如若不然,则会导致函数定义混乱。

(2)参数列表。即调用函数时需要的参数。参数列表通常是可选的,有的函数需要,有的函数可能不需要。参数列表的作用是向函数传递一些参数,使得函数可以直接使用这些参数的值。

调用函数的时候,参数个数和类型必须和函数定义示所声明的参数个数即类型保持一致。而且函数的参数就如同似函数体内的局部变量,它们在函数调用的时候被初始化。

(3)代码块。它是函数的主体部分。代码块中的代码包含在以对花括号({ })中,代码块可以执行并完成函数的功能。编写代码块是应当遵循WML Script的编程规则。

有时,函数需要返回一个值给调用函数的语句,则应该在代码块的后面一行使用return语句,返回所需的数值。

与C/C++等语言类似,WML Script的函数是可以嵌套的,以就是说,在一个函数中还可以调用其他函数。但是,函数声明是不能嵌套,这是WML Script的强制性规定。

下面几行语句就是定义函数的简单例子:

function currencyConverter(currency,exchangeRate)

{

return currency*exchangeRate;

};


其中,该函数的名称为currencyConverter,参数有currency和exchangeRate两个,函数代码块包含一条语句,用于返回currency和exchangeRate的乘积。

下面是一个使用extern定义外部函数的例子。其中函数名为textIt,它没有参数,函数体中定义了两个赋值变量,一个赋整数,一个赋函数值:

extern function testIt(){

var USD=10;

var FIM =currencyConverter(USD,5.3)

};

5.2.2 函数的调用

编写好的函数必须经过合法的调用,才可以发挥它应用的作用。函数调用将返回一个值,比如一个计算结果。WML Script中的函数主要可以分为内部函数、外部函数和库函数,下面我们就介绍这3类函数的调用方法。

内部函数

所谓内部函数是指函数的定义与其调用函数在同一个脚本文件内的函数,对内部函数的调用称为内部调用。内部函数的调用非常简单,只需提供函数名和所需参数值即可,参数值必须和函数定义时指定的参数个数即类型一致。而且函数调用需要使用操作符来接收或处理被调用的返回值。

内部函数可以在其定义之前调用,也可以在其定义之后调用。例如,下面就是一个在函数定义之后调用的例子。 

function test1(val){

return val*val;

};

function test2(param){

return test1(param+1);

}; 


这个例子中定义了两个函数test1和test2。test1函数用于计算给定参数值的平方并将结果返回;test2函数将给定的参数值加1,然后这个和为参数值,来调用test1函数,得到结果后在将给结果返回到调用test2函数的语句。

注意,本例中test2函数调用了test1函数,这种在函数中调用其他函数的方法称为函数调用嵌套。WML Script的内部函数、外部函数和库函数都支持嵌套调用,后面我们专门介绍这方面的内容。

外部函数

外部函数使一个在WML Scrupt外部文件中定义的函数。调用外部函数的方法与调用内部函数的方法基本类似,不同之处在于调用处外部函数时一是要指定外部文件的地址即名称,二是要在调用的外部函数名的前面加上外部文件的名称。

WML Script规则使用use url来指定外部文件,语法格式为:

use url还有外部函数的外部文件名 外部文件所在的URL;

这样,WML Script的预编译头就可以将外部文件映射为一个可以在内部使用的标识。然后,使用这个标识并加上井号键(#)和标准的函数调用即可实现外部函数调用,语法格式为:

外部文件名#外部函数(参数列表);

例如,http://www.host.com/script下有我们需要的外部文件,名为OtherScript,所以我们可使用use url来指定该文件:

use url OtherScript\"http://www.host.com/script\"

这一外部文件中含有我们需要调用的外部函数testme,则可采用“外部文件名#外部函数(参数列表)”的形式来调用它:

OtherScript#testme(param+1);

这个例子完整的写出来,就是下面的程序:

use url OtherScript\"http://www.host.com/script\"

function test(param){ 

return OtherScript#testme(param+1);

};

库函数

特别指定,WML Script的库函数一律是指它的标准库函数。因为与标准库函数对应,WML Script还有一些非标准的库函数。我们这里先介绍标准库函数,非标准库函数后面再介绍。

所有库函数都有所数的库,函数的库中通常含有一类函数。因此,调用某个库函数时,一要指定它的库名,二要指定它的函数名。WML Script规定,调用标准库函数时可以通过在函数库的名字后面加上句点号(.)和库函数的标准调用来实现,语法格式为:

函数库名.函数名(参数列表);

例如,WML Script的浮点库即Float库中有一个开根方的函数sqrt,该函数只有一个参数,那么调用squrt库函数的方法为:

Float.sqrt(number);//这里要求number大于或等于0

下面给出了调用库函数的简单例子。首先一param参数值调用Lang.abs()函数,返回结果加1后再作为参数调用Float.sqrt()函数,它的返回结果作为内部函数test的返回值:

function test(param){

return Float.sqrt(Lang.abs(param)+1);

};

2.3 函数的嵌套调用

WML Script的函数定义都是互相平行、独立的,定义函数的时候我们不能在一个函数内定义另外一个函数,也就是说,函数定义是不能嵌套的。但是,函数调用确是可以嵌套的,也就是说,我们可以在调用一个函数的过程中调用另外一个函数。

它的执行过程是:

(1)执行a函数开头部分;

(2)遇到调用b函数的操作语句,流程则专区执行b函数;

(3)执行b函数开头部分;

(4)遇到调用c函数的操作语句,流程则专区执行c函数;

(5)执行b函数,如果没有其他嵌套的函数,则完成c函数的全部操作;

(6)返回调用c函数的语句,即返回到b函数;

(7)继续执行b函数中尚未执行的操作,直到b函数结束;

(8)返回a函数中调用b函数的语句;

(9)继续执行a函数的剩余操作,直到函数结束。

function myFunC(param1){

return param1*param1=Float.squt(Lang.abs(param)+1);

};

function myFunB(param0){

return myFunC(param0+1)*|param0+12;

};

function myFunA(param){

return myFunB(param*param+1);

};


5.3 预编译

WML Script的预编译主要用于在编译阶段控制编译器的行为。与编译头一般在文件开头和函数声明之前指定,WML Script规定所有的预编译头都是一关键词use加上指定的预编译属性进行指定。

在大多数的编程中,我们比较长用的预编译行为主要涉及外部文件声明、访问权和Meta信息设置。

5.3.1 外部文件

我们知道,使用URL地址可以定位一个WML Script文件。利用该URL地址;在WML Script编程中我们可以通过预编译来调用WML Script的外部文件,外部文件预编译头的声明方法是use url,其语法格式如下:

use url外部文件名 \"URL\"地址

这样,我们在当前文件的编程中就可以使用该预编译头声明的外部文件,从而可以调用该外部文件的函数。其语法格式为:

外部文件名#函数名(参数列表);

例如,我们希望在当前的WML Script程序中调用OtherScript外部文件中的check()函数,而且我们知道OtherScript文件的URL地址为http://www.host.com/app/script。因此,我们可以使用use url来声明这一外部文件:

use url OtherScript\"http://www.host.com/script\"

随后,我们就可以在程序中调用OtherScript中的check()函数了:

function test(par1,par2)

{

return OtherScript#check(par1,parr2);

};

其中调用执行的过程如下:

(1)找到WML Script外部文件的URL地址;

(2)当前函数从指定的URL地址值装载外部文件;

(3)检测外部文件的内容,并执行其中的check()函数。

ure url 预编译头指定的外部文件名在当前程序中必须唯一,用户不能指定不同URL地址的同名外部文件,否则在调用外部文件时就会发生混乱。

另外,use url预编译头中的URL地址也可以是相对URL地址。相对URL的起始位置是当前程序文件所在的位置,并在此基础上根据URL进行定位。

如果URL地址中的字符包含有转义字符,则WML Script将根据转义要求进行转义。不过,程序在编译的时候编译器并不会对他们进行转义,而是在程序执行时完成,检查URL格式和URL地址的有效性。

5.3.2 访问权限

我们可以使用访问权限预编译设保护文件的内容,实现访问控制。WML Script编程中,必须在调用外部函数之前使用访问权限预编译头声明外部文件的访问权限。不过,WML Script访问权限检查的缺省值是不进行检查,即disabled.但访问权限一经声明,以后当调用外部函数的时候,编译器就会检查外部文件的访问权限,以决定调用这是否有权使用该文件及其内含函数。

访问权限预编译头的声明方法是use access,其语法格式如下:

use access domain操作域名 path操作路径:

访问权限预编译头通过指定domain和path属性来决定编译器将要进行什么样的检查工作。如果文件有domain或者path属性,那么文件所在的URL就必须和属性中的值一致。比较时,域和路径都依据URL大写规则进行比较。具体的比较预则如下:

(1)操作域与URL中的域后缀相匹配。与后缀匹配是值所有的子域从后向前都必须一致。例如:www.wapforum.org和wapforum.org相匹配,而与forum.org并不匹配。

(2)操作路径和URL中的路径前缀相匹配。路径前缀匹配是值从前向后必须一致。例如:“/X/Y”与“/X”相匹配,而不是和“/XZ”相匹配。

(3)却省的domain数行为当前的文件域,就是“/”。

不过,为了简化编程,有时WMLScript并不需要直到外部文件的绝对路径,我们只需提供文件的相对URL即可,用户浏览器执行程序是卡相对路径自动转换为绝对路径,根据路径属性进行匹配。例如:如果访问权限预编译头及其指定属性为:

use access domain\"wapforum.org\"path\"/finance\";

则可以使用以下的路径来调用指定文件中的外部函数,它们都符合相对URL地址匹配规则:

http://wapforum.org/finance/money.cgi

http://www. wapforum.org/finance/money.cgi

http://www. wapforum.org/finance/demos/packages.cgi?x+123&y+456

而以下的路径调用则非法的,因为它们或者操作域不对,或者URL地址不能与指定的相对URL相匹配:

heep//www.test.net/finance

http//www.qapforum.org/internal/foo.wml

需要强调指出的是,WML Script规定,同一程序中只能定义一个访问权限与编译头,否则就会导致编译错误。

5.3.3 Meta 信息

我们还可以通过与编译头的形式声明WML Script文件的Meta信息。Meta信息主要用于指定文件所需Mete属性的属性名(Property name)、属性值(Content)以及文件的配置(信息),属性都属于字符串类型的数据。Mate信息域编译头使用use meta声明,其语法格式为:

usr meta 属性 该属性Meta信息:

Meta的属性主要包括Name、HTTP Equiv和User Agent三种,下面我们分别讲解它们的声明方法:

(1)Name。该属性用于指定服务器使用的Meta信息。这些信息仅供服务使用,用户浏览器并不理会这些信息。

例如,以下Name属性的Meta信息指定了服务器的创建时间:

use meta name \"Created\"\"26-June-2000\";

该信息只会作用于服务器,而不会影响用户浏览器的操作。

(2)HTTP Equiv。该属性用于指定需要解释为HTTP头的Meta信息。对于已经编译的文件来说,当它到达用户浏览器前,WML Script将根据HTTP Equiv属性指定的Meta信息将文件转换为WSP或HTTP的响应头,进行文件的解释和执行。

例如,以下声明的http equiv属性指定按照脚本语言的关键字来解释当前文件:

use meta http equiv\"Keywords\"\"Script,Language\";

(3)User Agent。该属性用于定义用户浏览器使用的数据类型。例如:

use meta user agent\"Type\"\"Test\";

它指定当前数据必须立即发送给用户浏览器,然后马上清除掉。

5.4 执行时的错误检测与处理

WML Script函数的功能提供用户服务,并希望用户界面能在任何的状况下运作顺利,因此错误的处理是最大的课题,这表示了语言可能不提供预期的机制,如他应该可以防止错误发生或提醒用户注意并采取适当的动作,种植储蓄执行是最后的手段。下面几个小节列出了当为码下载并执行时会发生的错误,一些程序上的错误并不在谈论的范围(如无穷循环),像这类的例子就需要手动来终止。

5.4.1 错误检测

错误检测工具能让你检测错误但会干扰系统的动作,因为WML Script是弱格式语言,所以由一些特殊功能的工具来检测有invalid数据格式所引起的错误:

检测给定的变量包含的是正确值:WML Scritp含有格式确认函数库程序如:Lang.isInt()Lang.isFloat()、Lang.parseInt()、Lang.parseFloat。

检测给定的变量包含的只是正确的格式:WMLScript含有运算符typeof与isvalid能让你使用。

5.4.2错误处理

错误处理是在发生错误之后,有些状况是错误检测无法防止的,如内存限制后外部信号等,或者是数据很难处理,如溢出(overflow)或亏失(underflow),而这些状况可以分为两类:

严重错误(fatalerror):这种错误会造成程序终止,因为WML Scritp程序会让一些用户界面调用,程序终止通常会跟调用它的用户界面发出信号,用户界面就会告知用户这个错误。

错误(non-fatalerrow):这种错误会把信号传回程序,如一些特殊的值,然后由程序决定所要采取的行动。

下列的错误是根据他们的严重性来区分。

5.4.3 严重错误(fatalerror)

下面的小节会讨论WML Script的严重错误。

位码错误(bytedode error)

这些错误跟位码与由WML Script位码解译器所执行的指令有关他们指出了错误的元素群、无效的指令、指令所使用的参数无效,或指令无法执行。

验证错误(verification failed)

说明:调用的程序中的特定位码无法通过验证。
如何发生:每次程序试着用外部程序。
范例:var a = 3*OtherScript#doThis(param)
严重性:严重。
判定状况:当检测位码验证式。
解决方法:终止程序与WML Script解译其调用者的错误信号。

说明:调用一个函数库程序时发生严重错误。
如何发生:每次调用函数库程序。
范例:var a = string.format(param)
严重性:严重。
判定状况:无
解决方法:终止程序与WML Script解译其调用者的错误信号。 
说明:调用函数参数的数目跟被调用函数的参数数目不符合。
如何发生:调用外部程序。
范例:编译器参生一个无效的参数给予指令使用,或者被调用的程序参数数目改变了。
严重性:严重。
判定状况:无
严重性:严重。
解决方案:终止程序与WML Script解译器调用着的错误信号。

说明: 在特定的程序中找不到所需要的外部程序。
如何发生:调用外部程序。
范例: var a =3*OtherScript#doThis(param)
严重性:严重。
判定状况:无
解决方案:终止程序与WML Script解译器调用着的错误信号。


说明: 由于在网络服务器的程序存取又无法修复的错误或特定程序并不在网络服务器中所引起的程序无法载入。
如何发生:调用外部程序。
范例: var a =3*OtherScript#doThis(param) 
严重性:严重。
判定状况:无
解决方案:终止程序与WML Script解译器调用着的错误信号。

说明: 存取错误,所调用的外部程序加了保护。
如何发生:调用外部程序
范例:var a =3*OtherScript#doThis(param) 
严重性:严重。
判定状况:无
解决方案:终止程序与WML Script解译器调用着的错误信号。

说明: 因为程序错误造成stack underflow。 
如何发生:程序要取出(pop)一个空堆
范例: 当组译器产生错误码。
严重性:严重。
判定状况:无
解决方案:终止程序与WML Script解译器调用着的错误信号。

说明:执行调用Lang.abort() 是发生的错误。
如何发生:每当程序调用Lang.abort()函数。
范例: Lang.abort(\"unrecoverable error\")
严重性:严重。
判定状况:无
解决方案:终止程序与WML Script解译器调用着的错误信号。

说明:发生堆栈溢出。 
如何发生:程序资源太多或要推入太多的变量到运算之中。
范例: function f|(x)(f(x+1););
严重性:严重。
判定状况:无
解决方案:终止程序与WML Script解译器调用着的错误信号。


说明:没有多余的内存可供解译器使用。
如何发生:作业系统无法配置多余的空间给解译器适使用。
范例: function f(x){
x=x+\"abcdefghijklmnopqrstuvxyz\";
f(x) ;
};
严重性:严重。
判定状况:无
解决方案:终止程序与WML Script解译器调用着的错误信号。

说明:用户终止程序的执行(如按下reset钮)
如何发生:随时。
范例: 当应用程序正在执行是用户按下reset钮。
严重性:严重。
判定状况:无
解决方案:终止程序与WML Script解译器调用着的错误信号。
说明:当程序执行中,发生了外部严重的错误。
如何发生:随时。
范例: 电力微弱,系统自动。
严重性:严重。
判定状况:无
解决方案:终止程序与WML Script解译器调用着的错误信号。

5.4.4 一般错误(Nonfatal error)

下面说明了WML Script的一般错误:

计算错误(computational error)

这些错误是由于WML Script数学上的运算所造成。

除以零(divide by zero)
说明:发生了除以零的状况
如何发生:当程序中有除以零的状况。
范例:var a= 10;
var b= 0;
var x= a/b;
var y= a div b;
var z= a%b;
a/=b;
严重性: 一般。
判定状况:高
解决方案:产生结果为invalid。

说明:发生了除以零的状况
如何发生:程序要执行浮点数运算。
范例:var a = Float.precision();
var b = Float.precision();
var c = a* b
严重性:一般。
判定状况:高,在某些状况很困难。
解决方法:产生的结果为浮点数值0.0

常数参考错误(constant reference error)

说明:所参考的浮点数实字为not a number。
如何发生:程序试着存取一个浮点数实字但组译器产生了not a number的浮点数常数。
范例:参考浮点数常数。
严重性:一般。
判定状况:高
解决方法:这会产生invalid值。 

说明:参考的浮点数实字不是正无穷大就是负无穷大的浮点数常数。
如何发生:程序试着存取一个浮点数实字但组译器产生了正无穷大或负无穷大的浮点数常数。
范例:参考浮点数常数。
严重性:一般。
判定状况:高
解决方法:这会产生invalid值。

说明:需要参照浮点数值所发生的错误。
如何发生:程序需要使用浮点数值但环境值支持整数值。
范例:var a = 3.14;
严重性:一般
判定状况:高 
解决方法:这会产生invalid值。 

转换错误

这个错误的发生同WML Script所支持的自动转换有关。

说明:欲转换成整数值,但这个值超过整数所能接受的范围(正或负)。
如何发生:程序试着自动转换成整数时。
范例: var a = -\"99999999999999999999999999999999999999999\";
严重性:一般 
判定状况:高
解决方法:这会产生invalid值。 
说明:欲转换成浮点数,但这个值小于浮点数所能接受的范围(正或负)。
如何发生:程序时值自动转换成浮点数时。 
范例:var a = -\"99999999999999999999999999999999999999999\"; 
严重性:一般 
判定状况:高 
解决方法:这会产生invalid值。 

说明:欲转换成浮点数,但这个值小于浮点数所能接受的范围(正或负)。
如何发生:程序时值自动转换成浮点数时。
范例:var a = -\"99999999999999999999999999999999999999999\"; 
严重性:一般 
判定状况:高
解决方法:这会产生浮点数0.0。
6.5 URL函数库

名称:URL
说明:这个函数库包含了处理绝对的URL与相对URL的程序,一般的URL语法如下:://:/;?#

isValid

程序:isValid(url)
说明:如果给予的url值是正确的URL语法,则回传ture,否则回传false
绝对与相关URL都能支持
相关URL不会被转成绝对URL
参数:url=字符串
回传值:布尔值或invalid
例外状况:无
范例:var a=URL.isValid
(\"http://www.acme.com/script#func()\"); //a=ture
var b=URL.isValid(\"../common#test()\"); //b=ture
var c=URL.isValid
(\"experimental?://www.acme.com/pub\"); //c=false 

getScheme

程序:getScheme(url)
说明:回传给予url的调用方式scheme
绝对与相关URL都能支持
相关URL不会被转成绝对URL
参数:url=字符串
回传值:字符串或invalid
例外状况:如果遇到不正确的URL语法,则回传invalid
范例:var a=URL.geScheme(\"http://w.a.com\"); //a=\"http\"
var b=URL.getSceme(\"w.a.com\"); //b=\"\"

getHost

程序:getHost(url)
说明:回传给予url的主机
绝对与相关URL都能支持
相关URL不会被转成绝对URL 
参数:url=字符串
回传值:字符串或invalid
例外状况:如果遇到不正确的URL语法,则回传invalid
范例:var a=URL.geHost(\"http://www.acom.com/pub\"); //a=\"www.acm.com\"
var b=URL.getHost(\"path#fray\"); //b=\"\"

getPort

程序:getPort(url)
说明:回传给予url的端口(port)
如果port没有制定,则回传空字符串
绝对与相关URL都能支持
相关URL不会被转成绝对URL 
参数:url=字符串
回传值:字符串或invalid
例外状况:如果遇到不正确的URL语法,则回传invalid
范例:var a=URL.getPort(\"http://www.acom.80/path\"); //a=\"80\"
var b=URL.getPort(\"http://www.acom./path\"); //b=\"\"

getPath

程序:getPath(url)
说明:回传给予url的路径(path)

参数:url=字符串
回传值:字符串或invalid
例外状况:如果遇到不正确的URL语法,则回传invalid
范例:var a=URL.getPath(\"http://w.a.com/home/sub/comp#frag\"); //a=\"/home/sub/comp\"
var b=URL.getPath(\"../home/sub/comp#frag\"); //b=\"../home/sub/comp\"

getParameters

程序:getParameters(url)
说明:回传给予url的路径(parameter)
如果没有指定参数,则回传空字符串
绝对与相关URL都能支持
相关URL不会被转成绝对URL 
参数:url=字符串
回传值:字符串或invalid
例外状况:如果遇到不正确的URL语法,则回传invalid
范例:var a=URL.getParameters(\"http://w.a.c/scr;3;2?x=1&y=3\"); //a=\"3;2\"
var b=URL.getParameters(\"../scr3;2?x=1&y=3\"); //b=\"../home/sub/comp\"

getQuery

程序:getQuery(url)
说明:回传给予url的询问部分(query) 
如果没有指定的询问部分,则回传空字符串
绝对与相关URL都能支持
相关URL不会被转成绝对URL 
参数:url=字符串
回传值:字符串或invalid
例外状况:如果遇到不正确的URL语法,则回传invalid
范例:var a=URL.getParameters(\"http://w.a.c/scr;3;2?x=1&y=3\"); //a=\"3;2\"

getFragment

程序:getFragment(url)
说明:回传给予url的片断(fragment)
如果没有指定片断,则回传空字符串
绝对与相关URL都能支持
相关URL不会被转成绝对URL 
参数:url=字符串
回传值:字符串或invalid
例外状况:如果遇到不正确的URL语法,则回传invalid
范例:var a=URL.getFragment(\"http://www.acom.com/cont#fray\"); //a=\"fray\"

getBase

程序:getBase()
说明:回传次WML Script程序的绝对URL(没有fragment的部分)。
参数:无
回传值:字符串
例外状况:无
范例:var a=URL.getBase(); //Result;
\"http://www.acme.com/test.scr\"

getReferer

程序:getReferer()
说明:回传调用目前程序资源的最小相关URL(与目前程序的基本URL的相关)
内部程序调用并不会改变参照者
如果目前的程序并没有参照者,则回传空字符串
参数:无
回传值:字符串
例外状况:无
范例:var base=URL.getBase(); //base
=\"http://www.acme.com/current.scr\"
var prferer=URL.getReferer(); //referer=\"app.wml\"

resolve

程序:resolve(baseUrl,embeddedUrl)
说明:根据RFC2396的文件,由给予的基本base与插入embeddedUrl回传绝对URL
参数:baseUrl=字符串
embeddedUrl=字符串
回传值:字符串或invalid
例外状况:如果遇到不正确的URL语法,则回传invalid
范例:var a=URL.resolve(http://www.foo.com/,\"foo.vcf\"); //a=\"http://www.foo.com/foo.vcf\"

escapeString

程序:escapeString(string)
说明:这个程序会将所给与string字符串之中的特殊字符使用十六进制逃脱序列来替代(你必须使用量为逃脱序列格式%xx),这些逃脱字符如下:
控制字符(control characters):US-ASCII编码的字符00-1F与7F
空格(Space):US-ASCII码编字符20十六进制
保留字(Reserved):\";\"|\"/\"|\"?\"|\":\"|\"@\"|\"=\"|\"+\"|\"$\"|\",\"
Unwise:\"{\"|\"}\"|\"|\"|\"\\\"|\"\"|\"[\"|\"]\"|\"`\"
Delimes:\"<\"|\">\"|\"#\"|\"%\"|\"<>\"
给予的字符串如果已经是使用逃脱自负,则不会执行URL解析
参数:string=字符串
回传值:字符串或invalid
例外状况:如果字符串string含有非US-ASCII的字符,则回传invalid
范例:var a=URL.escapeString
(\"http://w.a.c/dck?x=u007ef#crd\");
// a=\"http%3a2f%2fw.a.c%2fdck%3fx%3def%23crd\"

unescapeString

程序:unescapeString(string)
说明:这个程序会将所给与string字符串之中每个可能是由URL.escapeString() 程序所产生的逃脱序列使用它所代表的字符加以替代。
参数:string=字符串
回传值:字符串或invalid
例外状况:如果字符串string含有非US-ASCII的字符,则回传invalid
范例:var a=\"http%3a2f%2fw.a.c%2fdck%3fx%3def%23crd\";
var b=URL.unescapeString(a); //b
=\"http://w.a.c/dck?x=12#crd\"

loadString

程序:loadString(url,contentType)
说明:回传有所给予的绝对URL与contenttype所指出的内容。
如果内容格是不是下列法则所规范的话,则是错误的:
你只能界定一种内容格式,整个字符串必须和一种内容格式相符,而 且你不能有额外的前或后空格。
格式必须是正文,但次格式没有限制,格式的开头一定是\"text/\"。
这个程序的动作如下:
文件的内容会载入使用给予的contentType与url,其他所需的数形式有使用用户界面的预设面。
如果载入成功而且回传的内为格式与所给予的contentType相符,则内文会被转换成字符串再回传。
如果载入成功或回传的内文格式不正确的话,则会回传特定的错误码。
参数:url=字符串 
contentYype=字符串
回传值:字符串、整数或invalid
例外状况:如果载入失败其回传的错误码和所使用的URL Scheme有关
如果使用HTTP或WSP架构,会回传HTTP错误码。
如果给予的contentType错误的话,则会回传invalid
范例:var a=\"http%3a2f%2fw.a.c%2fdck%3fx%3def%23crd\";
var b=URL.unescapeString(a); //b
=\"http://w.a.c/dck?x=12#crd\"

loadString

程序:loadString(url,contentType)
说明:回传有所给予的绝对URL与contenttype所指出的内容。
如果内容格是不是下列法则所规范的话,则是错误的:
你只能界定一种内容格式,整个字符串必须和一种内容格式相符,而 且你不能有额外的前或后空格。
格式必须是正文,但次格式没有限制,格式的开头一定是\"text/\"。
这个程序的动作如下:
文件的内容会载入使用给予的contentType与url,其他内文所需的数形式有使用用户界面的预设面。
如果载入成功而且回传的内容为格式与所给予的contentType相符,则内文会被转换成字符串再回传。
如果载入成功或回传的内文格式不正确的话,则会回传特定的错误码。
参数:url=字符串 
contentYype=字符串
回传值:字符串、整数或invalid
例外状况:如果载入失败其回传的错误码和所使用的URL Scheme有关
如果使用HTTP或WSP架构,会回传HTTP错误码。
如果给予的contentType错误的话,则会回传invalid
范例:var myUrl=\"http://www.acme.com/vcards/myaddr.vcf\";
myCard=URL.loadString(myUrl,\"text/x-vcard\");

6.6 WML浏览器函数库

名称:WML Brower
说明:这个函数库所包含的程序是让WML Script用来存取与WML相关的内文,这些程序不能有任何的副作用,并在下列的状况下回传invalid值。
系统不支持WML浏览器。
WML浏览器无法使用WML Script解译器。

getVar

程序:getVar(name)
说明:回传目前浏览器内文的所给予名称name的变量值。
如果所指定的变量不存在,回传一个空字符串。
变量名称必须依照WML语法来使用。
参数:name=字符串
回传值:字符串或invalid
例外状况:如果变量名称不合语法,则回传invalid
范例:var a=WMLBrowser.getVar(\"name\");
// a\"Jon\"或者变量的值

setVar

程序:setVar(name,value)
说明:在目前的浏览器之中,如果所给予名称name的变量,它的值同给予的值value设定的一样的话,回传ture,否则回传false。
变量名称必须依照WML语法来使用。
变量值必须是合法的XML CD ATA
参数:name=字符串
value=字符串
回传值:布尔值或invalid
例外状况:如果变量名称或它的值不合语法,则回传invalid
范例:var a=WMLBrowser.setVar(\"name\",Mary); // a=true

go

程序:go(url)
说明:将给予的url所标记的内文载入,这个程序予WML的GO动作意思相同。
如果所给予的url字空字符串(\"\"),则不会载入任何内文。
go()与prev()函数库程序会互相推翻,在回传控制与WML浏览之前都可以加以调用多次。
只有最后的调用设定的会保持作用,如果最后的调用为go()或prev(),其所设定的url为空字符串(\"\"),所有的要求都会被取消。
这个程序回传空字符串。
参数:url=字符串
回传值:字符串或invalid
例外状况:无
范例:varcard=\"http://www.acme.com/loc/app.dck#start\";WMLBrowser.go(card
)

prev

程序:prev()
说明:告诉WML浏览器回到先前的WML Card,这个程序的功能与WML中的prev动作一样。
go()与prev()函数库程序会互相推翻,在回传控制与WML 浏览之前都可以加以调用多次。
只有最后的调用设定是会保持作用,如果最后的调用为go()或prew(),其所设定的url为空字符串(\"\"),所有的要求都会被取消。
这个程序回传空字符串。
参数:无
回传值:字符串或invalid
例外状况:无
范例:WMLBrowser.prev();

newContext

程序:newContext()
说明:将目前WML浏览器的内文清除并回传一个空字符串,这个程序的公用与WML的NEWCONTEXT属性一样。
参数:无
回传值:字符串或invalid
例外状况:无
范例:WMLBrowser.newContext();

getCurrentCard

程序:getCurrentCard()
说明:回传目前WML浏览器所处理card的最小相关URL,如果WML deck所包含目前程序的基本地址不同的话,则此程序会回传绝对URL。
参数:无
回传值:字符串或invalid
例外状况:如果没有目前的card,则回传invalid。
范例:var a=WMLBrowser.getCurrentCard();// a=\"deck#input\"

refresh

程序:refresh()
说明:强制WML浏览器更新它的内文并回传一个空字符串,而用户界面会加以更新以反应更新后的内容,这个程序与WML中的refresh功能一样。
参数:无
回传值:字符串或invalid
例外状况:无
范例:WMLBrowser.setVar(\"name\",\"Zorro\");
WMLBrowser.refresh();

6.7 Dialog函数库

名称:对话
说明:这个函数库包含典型的用户界面程序。

prompt

程序:prompt(message,defaultInput)
说明:显示给予的信息message与用户输入的提示符号,defaultInput参数包含了用户输入所需的初始内文,回传用户输入。
参数:message=字符串
defaultInput=字符串
回传值:字符串或invalid
例外状况:无
范例:var a=\"09-555 3456\"; var b=Dialogs.prompt(\"Phome number\";a);

confirm

程序:confirm(message,ok,cancel)
说明:显示所给予的信息message与两个选项:ok与cancel,等待用户选取其中一个,如果是ok则回传false。
参数:message=字符串
ok=字符串
cancel=字符串
回传值:布尔值invalid
例外状况:无
范例:function onAbort(){return Dialogs.confirm(\"Are you sure?\"),\"Yes\",\"No\";};

alert

程序:alert(message)
说明:显示所给予的信息message给用户,等待用户确定并回传一个空字符串。
参数:message=字符串
回传值:字符串或invalid
例外状况:无
范例:function testValue(textElement){
if (String.length(textElement)>8) {
Dialogs.alert(\"Enter name <8 chars!\");
};
};

6.8 函数库总结 
函数库 

函数库名称:

Lang

Float

String

URL

WML Browser

Dialogs

函数库与他们的程序:
Lang函数库

Abs

Min

Max

ParseInt

ParseFloat

IsInt

IsFloat

MaxInt

MinInt

Float

Exit

Abort

Random

Seed

CharacterSet



Float 函数库

Int

Ploor

Ceil

Pow

Round

Sqrt

MaxFloat

MinFloat



String 函数库

Length

IsEmpty

CharAt

SubString

Pind

Replace

Elements

ElementAt

RemoveAt

ReplaceAt

InsertAt

Squeeze

Frim

Compqre

ToString

format



URL 函数库

IsValid

GetScheme

GetHost

GetPort

GetPath

GetParameters

GetQuer

GetFragment

GetBase

GetReferer

Resolve

EscapeString

UnescapeString

loadString



WML Browse函数库

Get Var

SetVar

Go

Prev

NewContext

GetCurrentCard

Refresh



Dialogs函数库

Prompt

Confirm

Alert

posted @ 2006-04-29 09:47 崛起的程序员 阅读(378) | 评论 (0)编辑 收藏
WML Script语法基础

WML Script是属于无限应用协议WAP应用层的一部分,使用它可以向WML卡片组和卡片中添加客户端的处理逻辑,目前最新的版本是1.1版。WML Script1.1是在欧洲计算机制造商协议会制定的ECMAScript脚本语言的基础上,经过修改和优化而指定的。它能够更好的支持诸如移动电话类的乍带宽通信设备,在WML编程中使用WML Script可以有效的增强客户端应用的灵活性,而且,我们也可以把WML Script作为一个工具使用,开发出功能强大的WAP网络应用和无限网页。本章我们将详细讲解WML Script1.1编程的基础预法制时,如基本规则、变量预数据类型、操作赋予表达式等。为了叙述上的简便,以后我们将“WML Script1.1”简称“WMLScript 薄?
4.1 WML 程序中调用WML Script函数

经过前两章的学习,熟悉C语言的读者可能会认识到,WML 的函数功能、逻辑运算功能等都是十分有限的。而WMLScript提供了丰富的函数功能,我们在WAP应用开发可以使用WMLScript来增强WML编程。因此,WMLScript成为扩展WML编程能力的主要开发工具。

4.2 WMLScript的主要优点及其字节码解释器

WMLScript具有一套定义好的字节码和一个解释器参考结构。无线网络传输中WMLScript的数据均以二进制格式进行传输,所以,用户可以使用乍带宽通信信道,从而能够保持客户端手机只需要最小限度的内存。ECMAScript修改后得到的WMLScript能够更快、更小、更容易的编译程序为字节码形式。所有这些特点,是WMLScript具备了WML所不能具备的很多优点和功能。

4.2.1 使用WMLScript的主要优点

WMLScript的设计宗旨是为WMLScript系统提供一般的脚本处理能力,使用WMLScript我们可以进一步补充基于XML的WML语言的编程功能,开发针对乍带宽的网络应用及内容,如文本、图像、选择列表等,我们可以使用简单的格式编写出更灵活和更具可读性的用户界面。WMLScript具备的WML所不能具备的优点和功能,主要包括如下几个方面: (1)检查用户输入的合法性:

(2)扩展用户浏览器的功能,比如允许程序员开发手机的电话呼叫、发送短信息、存储电话号码、管理电话簿或SIM卡等;

(3)生成用户端的确认、提示、警告信息或操作对话框,并使之快速显示在浏览器上;

(4)在用户浏览器的更改后,能够对浏览器端的软件和参数进行扩展与配置;

(5)最大程度克服客户端的乍带宽通信连接限制,并提供丰富的程序功能;

(6)补充WML并使之实现针对微型移动终端设备的多种服务,如支持高级用户界面、增加客户端智能型、提供用户浏览器外围功能的访问能力,以及在服务器与客户端浏览传输数据是减少带宽占用等。

4.2.2 WMLScript的字节码解释器

在WMLScript的字节码解释器解释之前,WMLScript语言编写的文本格式的程序将被首先编译为二进制格式的代码。编译时,编译器通常先将WMLScript程序分成若干个编辑单位,每个单位的程序都包含一定数量的语句行和WMLScript函数,然后,WMLScript的编译器将按照这些编译单位,逐一将WMLScript程序作为输入内容,而把对应的字节码作为输出内容。当用户通过WAP手机调用WMLScript程序时,编译器的编码功能即被激活、执行。

4.3 WMLScript基本规则

WMLScript在许多基本规则方面沿用了WML的做法。不过,由于WMLScript是以C语言为蓝本而指定的,所以它的语法特征和C语言非常相像。如果大家对C语言比较熟悉,那么学习和掌握这部分内容应当是比较容易的。

4.3.1 WMLScript与URL

与WML一样,WMLScript也沿用了WWW和HTML访问资源的URL、HTTP等规范,并扩大了URL使用的范围。在WMLScript中,不仅超链接、文件路径即文件名可以作为URL处理,外部函数、访问控制信息等也可以作为URL处理。

为此,WMLScript采用了WML的变通方法,即改进HTML命名资源为值的方式,采用程序段锚点(Fragment Anchor)的形式来处理资源定位。程序段锚点根据文档URL规则进行定义,并按照程序段标识符前加井字号(#)的方式书写。使用程序段锚点,WMLScript程序可以在WMLScript编译单位内可任意指定的函数,并可在调用该函数的同时传递所需的参数。

4.3.2 词法结构

WMLScript编程中的词法结构并不复杂,我们下面就从大小写敏感、空格、换行、注释即保留字等方面讲解相关的具体规则。

(1)内容类型。WMLScript的内容类型主要针对文件形式和二进制形式两种情况,类型结构可以在服务器端进行指定,具体形式为:

文本形式:text/vnd.wap.wmlscript;

二进制形式:application/vnd.wap.wmlscriptc。

具体指定方法我们在第4章已经介绍过,这里不再重述。

(2)大小写敏感。WMLScript1.1是一种大小写敏感的脚本语言。它所设计的各种关键字、变量和函数名都必须合理的使用大小写。

(3)空格和换行。一般情况下,WMLScript程序值形式将忽略所有的空格、制表符合换行符等。但如果把这些特殊字符通过代码进行表述,或者作为字符串进行处理时,WMLScript将不再忽略它们。了如,字符串\"Oct 28,2001\"中含有空格,该空格在执行时就不会被忽略,它与不含空格的字符串\"Oct28,2001\"是不同的。

(4)注释。与WML编程一样,在WMLScript脚本程序中也可以加入注释内容。注释内容不被程序执行,且注释不能嵌套。WMLScript的注释方法有两种:

其一,行注释。即使用双斜行号(//)引导以一行内容,这一行内容全部作为注释内容。如:

//这是以行注释,由双斜杠号开始到结束都是注释。

其二,块选择。即以符号“/*”开始,而以符号“/*”结束的期间所有内容都是注释内容。如:

/*这就是块注释,加在中间的内容就是注释内容*/

(5)数据类型与直接编码。WMLScript允许将4种类型的数据直接编码并可嵌套并可嵌如在程序之中。直接编码的4中数据类型为:整数、浮点数、字符串和布尔值。另外,“无效性”值也可直接编码” 1. 整数。当以十进制、十六进制或八进制方式使用整数时,可对这类整数进行直接编码。

编程序时,十进制的数字均不以0开头,只包含0~9的数值串;十六进制的数据以OX或者Ox开头,只包含0~9、a~f或者A~F的字符串;八进制的数均以0开头,只包含0~7的数字串。

2.浮点数。浮点数通常定义为含有小数点的数字,可以包含小数和指数部分。浮点数的形式较多,可以使十进制的整数或浮点数,可以是分数,也可以是指数;但一个浮点数必须至少有一个数。

指数是以e或E开头,后面跟着一个整数。指数是以10为底幂。例如:e0时10的零次幂,例如:e0时10的另次幂,e-2时10的负2次幂集等于0.01。指数可以带符号,正好(+)或者减号(-),它们分别代表是正指数和负指数。

3.字符串。字符串是指定义在成对的双引号(\"\")或单引号(‘ ’)之间的内容。

由于WMLScript只允许使用成对的双引号或但引号来定义字符串,所以程序中使用一个单引号或一个双引号时就会出现编译错误。

考虑到有些特殊字符不能在字符串中直接显现出来,所以WMLScript提供了转译序列来表示这些特殊字符。

4.布尔型。它只是ture和false两个数值,用于表示WMLScript中的“真值”或“假值”。布尔型数据可参与异、或等运算,具体规则我们后面介绍。

5.无效型。也称为“空类型”,它是WMLScript支持的一个表示无效值的量,以invalid表示。该两与C语言中的NULL类似。

4.保留字。WMLScript中定义有一个保留字集合,含有一些表示特殊意义的单词这些次不能另外定义,也不能最为其他标识符。WMLScript中的保留字如下:

acces http agent if break isvalid continue meta header

div name div= path domain return else typeof while

equiv url extern use for user function var

另外,WMLScript还为将来的版本预留了一些保留字,主要有:

case finally catch import class private const public debugger

sizeof default struct do super enum switch export throw

extends try

WMLScript还有一些没有使用的保留字:

delete null in this lib void new with

7.标识符。WMLScript的标识符可以指定或命名3种元素:变量、函数和标注。标识符不能以数字开头,但能一段下划线(-)开头,而且,标识符不能是WMLScript的保留字。例如,timeOfDay、speed 、quality、HOME_ADDRESS、_myName、__、varO等都是合法的标识符;而以数字或非短下划线的特殊符号开头的字串,以及保留字等都属于不合法的标志赋,如while、for、if、my~name、$sys、123、3piecs、take.this等。

由于WMLScript是严格区分大小写的,所以字母相同但大小写不同的标识符不是同一个标识,例如,Work和work就是不同的标识符。

8.名称空间。WMLScript提供了比较自由的名称空间,同一标识符可以同时用作不同的目的。例如,作为某一函数名称的标识符,还可以同时用作变量名、函数参数、程序标注等,使用时他们的属性或值等并不相互影响。在下面的简单的例程中,myTest这一标识符即用作了函数名,又用作了变量名、函数参数名、函数参数名、常量名。显然,WMLScript的这一特定为我们编写程序提供了很大的方便。

4.3.3 WMLScript程序的基本书写规则

WMLScript程序的基本书本写规则:

1.程序由若干语句或函数组成,函数有由若干语句组成;

2.每个完整的语句后面必须加上分号(;),语句关键词语操作数之间必须有空间;分号(;)是WMLScript程序的组成部分;

3.函数体之间必须使用成对的花括号({ })括起来,而且函数结束时在右花括号(})的后面还要加上分号(;);函数说明部分,如函数名、函数类型、函数参数等要放在花括号({})的前面;

  4.有些语句可能也需要实用花括号({ })办含内容,这类语句通常也可以放在函数中,所以花括号({ })是可以嵌套的。

当然,不同的语句、参数、变量等元素在声明和书写时可能还有一些更细的要求,具体我们后面介绍这些元素是再专门给出,

4.4 变量与数据类型

变量即数据类型是所有编程语言的概念和组成部分,WMLScript对此也不例外。它对其变量使用方法和数据类型定义方法给出了详细的规定。变量通常与某数据之相对应,我们可以给变量赋值,并可在程序执行中改变变量的值。下面我们讲解WMLScript有关变量与数据类型的详细规定。

4.4.1变量及其声明

变量是在WMLScript脚本程序中具有值的符号名,或说标识符。使用变量可以存储和改变程序中需要的数据。与C语言不同的是,WMLScript仅支持函数内定义的变量或用于传送数的参变量。

变量使用前必须进行声明,也就是定义变量,即指定变量的名字。声明变量的关键字是var,它的后面根上作为变量名的合法的标识符,并于最后加上分号(;),即完成一个量的声明。声明变量是可以使用var一次声明多个变量名,相邻变量之间使用都号(,)间隔。

一般情况下,我们在给变量命名的时候,都希望能够使用有意义的变量名。例如,当需要使用一个变量表示一本书的价格时,虽然将变量命名为j或book都没有什么错误,但若能命名为bookPrice则会是的WMLScript脚本程序具有更好的可读性,可以方便编程人员进行脚本的编写和调试工作。而且,由于WMLScript在给变量命名是不能使用保留字,所以考虑到避免由于一时的疏忽时变量名欲保留字发生冲突,我们建议使用多个单词组合在以其作为一个变量的名称,这是一个比较好的解决办法。例如,如果要定义一个变量来存储的、一本书的价格,那我们可以不妨使用bookPrice或者book_price作为变量的名称,这样,一方面可以时变量显得更加清晰,另一方面也可以避免变量与保留字的冲突问题。

以上只是我们对于变量命名的建议,并不是强制性的要求,用户完全可以不按照我们的要求来做,只要遵守WMLScript对于标识符命名的要求就可以了,但养成良好的编程风格不论是对编程人员还是对脚本编写人员来说,都是十分有意义的。

4.4.2 变量的作用域与生命期

一个变量的作用域是指在程序中能够引用这个变量的一段代码。由于WMLScript仅支持函数内定义的变量,所以WMLScript变量的作用与通常就是定义它们的那个函数。在该函数之外,变量不再发挥直接作用。

变量的生命起始值从变量声明开始到失效为止。变量的生命期也被称为变量的持久期、存活期。一个变量在定义它的整个函数内都是有效的,函数内的任何语句块都不会削减变量的生命期或限制变量的作用域。

如果一个变量未经生命就直接使用,或声明过后再次声明,都会破坏变量的生命期。前一种情况会导致变量没有开始声明期,即没有“生命”;而后一种情况则导致变量声明期没有结束以前就重新赋予声明期,即让它多次“降生”。这都会导致变量无效使用。下面函数中的变量使用就说明了这一问题:

function foo(){

x=1;//错误:变量使用前没有声明,改变量还没有“声明”。

var x,y,z;

y=x+3;

var zd =invalid

if(x){

var(y);//错误:这一变量已经声明,这里是重复声明。

};

};

4.4.3 变量的使用

WMLScript的变量只能在定义它的函数内使用。使用时需要声明变量,声明变量是可以同时对变量赋值,甚至对变量进行运算。例如,下面的简单函数就说明了变量的这种灵活的使用方法:

function ourAge(){

var myAge=38;

var yourAge=26;

var ourAge=myAge+yourAge;

return ourAge;

};

使用变量时可通过调用变量名字的形式来实现。上面例子中的“var ourAge=myAge+yourAge;”一句,通过调用变量名,变量ourAge对变量myAge和变量yourAge实行了求和操作。

4.4.4 变量类型与数据类型

WMLScript是一种“弱类型”的语言,及其变量没有确定的类型。WMLScript变量的类型有改变量所赋数据的类型决定,并根据数据类型的改变而改变。WMLScript只支持内部定义的数据,因此我们编写程序是无需指定WMLScript变量的类型,WMLScript减根据变量而赋数据的类型自动进行匹配。由于WMLScript的数据类型共有整数、浮点数、字符串、布尔型和“无效型”五种类型,所以WMLScript变量的类型所能匹配的也就是这五种类型。

4.4.5 变量值域

由于变量类型尤其所赋数据的类型决定,所以变量值与域其所赋数据的可取范围等价的。下面的我们就给出整数、浮点数、字符串和布尔型的区值范围,以参照确定应类型变量的值域。 1.整数的范围。WMLScript支持的整数是32位的,也就是说整数的区值范围是从2147483648到+2147483647。我们可以在程序运行期是用Lang函数来取得这些值,如:

Lang.maxInt(); //获取最大的整数

Lang.maxInt(); //获取最小的整数

2.浮点数的范围。它是指WMLScript浮点数的精度所能表示的最小和最大数值WMLScript支持32为的单精度浮点数,其最大至时3.40282347E+38,最小的非零的数是1.17549435E-38或更小(按照正常的精度)。

我们可以使用浮点Float函数库在程序运行其取得这些数值:

Float.maxFloa(); //获得WMLScript所支持的最大浮点值

Float.maxFloa(); //获得WMLScript所支持的最小浮点值
对于运行期出现的一些特殊的浮点数,WMLscript将按照下述规则处理:

其一,如果操作结果是一个不能被单精度浮点数所能表示的数值,那么该结果将被认为是invalid,即无效值;

其二,如果操作结果发生下溢出,那么结果将作为0.0处理:

其三,负的零和正的零是完全相等的。

3.字符串的范围。任何由于字母、数字或特殊字符组成的符号串都是WMLScript中定义的有关字符串的操作或String库中的函数控制字符串。

4.布尔型数据的范围。布尔型数据只有ture和flase两个取值,这也是布尔型变量的两种数值。我们可以使用布尔型数据取初始化或指定某一变量的数值,或将布尔值变量写入一个需要布尔值作为参数的语句。布尔值可以是数值运算的结果,也可以是逻辑运算的结果。

下面就是定义布尔型变量并赋初值的例句:

var truth=truth;

var lie=!truth;


4.5 操作符与表达式

在WMLScript中,表达式可以把变量、常量与操作符结合起来,经过运算能够产生一定的运算结果。表达式运算后产生的结果可以是整数型、浮点数型、字符串型或布尔型的数据。其实,对于表达式我们并不陌生,例如,1+2就是一个简单的表达式。

WMLScript的表达式主要有两种类型。一种是赋值表达式,即把数据赋给变量的一种表达式,例如,myBook=3,在这个表达式中,将3 赋给变量myBook,同时,这个表达式本身也有一个运算结果,那就是3。另外一种是运算表达式,它是指产生一个运算结果而不进行赋值操作的表达式,例如1+2就是一个运算表达式,在这个表达式运算产生的结果是3, 但这个表达式并没有把运算结果赋给变量。

在表达式运算的过程中,表达式中操作一个或这两个数据产生运算的符号做操作符,被操作符操作的数据称作操作数,在WMLScript中我们会使用到各种操作符,下面就对操作符及有关的表达式进行详细讲解。

4.5.1 赋值操作符

赋值操作符用于赋值操作,即给变量指定所需的数值,它能把有操作数的运算结果给做操作数,最简单的赋值操作符就是“=”,例如x=2,就是将2赋值给变量x。在如以下几行语句都是赋值操作:

var=\"abc\";

var b=a;

b=\"def\";

赋值操作符不需要指定使用对象,也不会改变赋值操作符右边变量的数值。WMLScript的赋值操作符主要包括以下几种:

1.=。用于赋值操作,将有操作数赋给左操作。

2 +=。将有操作数与左操作数进行相见运算,然后把运算结果赋值给左操作数。例如,假设x=3,那么x+=2运算后的结果为x=5。

+=是比较特别的操作符,因为它可以将两个字符串相连,所以+=操作符也可以对字符串进行操作,然后将连接后的字符串赋给左操作数。例如,假设x=\"Happy\",那么x+=\"new Year\"运算后结果是x=\"Happy New Year\"。

3.-=。将左操作数简取有操作数,然后把运算结果赋值给左操作数。例如,假设x=3那么x-=运算后的结果为x=1。

4*=。将左操作属于有操作符进行相乘运算,然后把运算结果赋值给左操作数。例如,假设x=6,那么x*=2运算后的结果为x=6。



5/=。将右操作属于右操作符进行相乘运算,然后把运算结果赋值给左操作数。例如,假设x=6,那么x*=2运算后的结果为x=6。

6div=。拥有操作数处理左操作数,然后把运算结果中的整数部分赋值给左操作数,例如x=7,那么xdiv=2运算后的结果为x=3。

7%=。功能是求余数并赋值,用右操作数除以做操作数,最后把运算得到的余数值给操作数。例如,假设x=7,那么x%=3运算后的结果为x=1。

8<<=。功能是带符号左位移并赋值,即将左操作和右操作数进行左位移操作,在将结果赋给左操作数。

9>>=。可将左操作和右操作进行右操作进行补零右位移操作,在将结果赋给左操作数。

4.5.2 数学运算操作符

数学运算操作符可以对数值类型的操作数进行运算,然后返回一个数值类型的运算结果。

1+。这是加运算操作等,它对应着数学运算中的加法运算,例如表达式1+2的运算结果为3。

加操作符还可以对字符串类型的操作数进行运算,然后将两个字符串相连起来作为运算

2- 。即检操作符,对应着速学运算中的减法运算,例如表达式2-1的运算结果为1。

同时,“-”还是一个取负操作符,当它作为取负操作符的是一个操作数,取负操作符的功能使返回操作数的相反数。

3*。这是乘操作符,它对应着数学运算中的乘法运算,例如表达式2*3的运算结果为6。

4/。即除操作符,对应着数学运算中的除法运算,但WMLScript中的除法运算有些特别,在WMLScript中,除法运算后的结果是一个浮点数,而不想C语言或者Java语言那样在整数进行除法运算式井运算结果强行转化整数。在WMLScript中,1/2=0.5,而在Jave中,1/2=0。

5div。这是整除操作符,对应着数学运算中的整数运算,运算后的结果一个整数,这一点与C语言或者Java语言中的情况是一样的,可以在整数进行除法运算式将运算结果强行转换整数。

6%。即取模操作符,它对应着数学运算中的取莫运算,也就是将两个操作相除,返回相除后的余数。

取模操作符主要用于判断一个数字是否能被另一个数字整除。

((the Year%3==0))&&(the Year%100!=0))||(the Year%400==0)

其中&&代表的是逻辑运算中的与运算,||代表的是逻辑运算中的或运算,关于逻辑运算,我们后面会详细介绍。

7++。这是递增操作符,它只有一个操作数,操作可以在操作符的左边,也可以在操作符的右边,它所完成的运算操作是将操作数加1。假设操作数名称为j,值为2,那么++j加1,然后返回j的值3;而j++则实现返回j的值2,然后将j加1。

在循环中,我们常常会用到递增操作符的作用正好相反。递减操作完成的运算操作是经操作数减1。例如,假设操作数名称为j,值为2,那么--j先将j减1,然后返回的值1;而j--是先返回j的值2,然后将j减1。

4.5.3 位操作符

为操作符在运算实现将操作转化32位的二进制数,然后对每个操作数分别按位进行运算,运算后在将二进制的结果按照标准WMLScript数值类型返回运算结束。

1&。这是为与操作符,它可以对两个操作数按位进行于操作,其运算规则是:

0&0=0,0&1=0,1&0=0,1&1=1

2|。这是位或操作符,它可以对两个操作数按位进行或操作,运算规则是:

0|0=0 ,0|1=1,1|0=1,1|1=1

3^。这是唯一或操作符,它可以对两个操作按位进行异或操作,其运算规则是:0^0=0,0^1=1,1^0=1,1^1=0

4~。这是位非操作符,它只有一个操作数,可对操作数按位进行非操作,运算规则是:~0=1,~=0

5<<。这是左移操作,它可以对左操作数进行向左一位的操作,由操作数给定了要移动的位数,在移动过程中,左操作数的最低为补充0。

6>>。这是右移操作符,它可以对左操作数进行向右移位的操作,由操作数给定要移动的位数,在移位的过程中,丢弃向右移的位。

7>>>。这是填0右移操作符,它与右移操作符相似。当对证正数进行操作时,它们的效果完全相同;不同之处在于,当进行负整数右移操作时,因为负责转化为二进制后,最高为1,所以在进行右移操作后,最高位仍然补充1,而在进行填0右移操作时,最高为补充的是0,因此,这是负数将转化为正数。

4.5.4 逻辑操作符

逻辑操作符可以将布尔类型的表达式组合起来,完成逻辑运算操作,然后返回逻辑运算的结果--真或假,这样就可以完成比较复杂的逻辑判断工作。逻辑操作共有3种;

1&&。即逻辑与操作符它只有在两个操作数都为ture的时候,返回结果为ture,在其他情况下,返回结果为false或者invalid。

2||。这是或操作符,它在两个操作数至少有一个为ture的时候,返回结果为ture,在其他情况下,返回结果为false或者invalid。

3!。即非操作符,它只有一个操作数。当操作数为ture时,返回结果为flase;返回结果为ture.

4.5.5 比较操作符

比较操作符可以把操作数进行比较,然后返回一个逻辑值,表明这个比较操作的结果是否为真。比较操作的操作数可以是数值类型或者字符串类型的数据。比较操作符也常被称为关系运算符。

WMLScript支持的比较操作符共有6种,下面被介绍一下。

1. ==。即等于操作符,它可以比较两个操作数是否相等。如果两个操作数相等,则返回ture,否则返回false

2. !=。即不等操作符。它可以比较两个操作数是否相等。如果两个操作数相等,则返回false,否则返回ture。

3. >。即大于操作等。其运算规则时,如果左操作数大于右操作数,则返回ture。
posted @ 2006-04-29 09:44 崛起的程序员 阅读(184) | 评论 (0)编辑 收藏
元素和标签是WML的主要语法,它们决定了WML编程的基本原则。本章我们将从WML的元素、标签、属性等方面详细讲解WML的编程方法。学习本章知识之前,读者应当了解WML元素与标签的区别。WML的元素通常有一个首标签、内容、其它元素及一个尾标签组成。也就说,单独的标签是一个元素,成对出现的标签与其包含的内容也构成一个元素。由于元素牵涉及标签,标签又涉及属性。 
3.1 卡片、卡片组及其属性

我们前面介绍了WML的卡片与卡片组,主要从概念和相互关系的角度进行了分析。我们这里则从卡片、卡片组的组成、相关元素、标签技术性等编程角度进行分析和讲解。 3.1.1 共有属性

WML元素的共有属性主要有3个:id、class和xml:lang属性。

WML的所有元素都有两个核心属性,即标示(id)和类(class)属性。它们主要用于服务器方的信息传输。其中,id属性用于定义元素在卡片组中的唯一标示,即它的名称;class属性用于给当前元素定义一个或更多的类(class)。与卡片组一样,类(class)也是有名字的,而且多个元素可以使用一个类(class)名。具有相同类名的单一卡片组中的所有元素均可被看作相同类的一个部分。类名是区分大小写的。如果在class属性列表中,一个元素多个唯一的类名,那么该元素可以看作这些类中的一部分。具有同一属性的多个类名必须用两个以上的空格间隔,WML程序执行时将忽视其中多余的类名及其属性。

另外,在WML程序,所有包含文本的元素均具有“xml:lang”属性。该属性用于指定当前元素及其属性所用的描述语言,如英国英语、美国英语、法语、德语等,并可以为用户浏览器选择显示文本的语言提供依据。

3.1.2 WML程序的文件头

合法的WML卡片组均属合法的WML文件,因此它必须包含WML的声明及文件类型的声明。典型的WML程序的文件头包括我们前面多次提到的以下两行程序:

<?xml version=\"1.0\"?>
<!DOCTYPE wml PUBLIC \"-//wapforun//DTD WML 1.1//EN\" \"http://www.wapforum.org/DTD/wml_1.1.xml\">

编写WML程序时,我们必须写入这两行程序,并放在程序的开始处。其中\"-//wapforun//DTD WML 1.1//EN\"是标准通用标记语言SGML的公共标示;\"http://www.wapforum.org/DTD/wml_1.1.xml\"是WML程序文档类型的标示。文档类型标示也可以是\"text/vnd.wap.wml\"或“application/vnd.wap.wmlc”,其中前者制定WML的原文类型,后者贫╓ML程序编译后代码类型。

3.1.3 WML元素

WML的WML元素用于定义一个卡片组,并通过<wml>与</wml>标签包含和封装该卡片组中的所有卡片及信息。它的语法格式如下:

<wml xml:lang=\"lang\">
内容(content)
</wml>

其中xml:lang=\"lang\"用于指定文档所用语言(前面已有介绍),语言\"lang\"的值属于NMTOKEN型数据。

wml元素中包含的内容(content)中除了文本、图像等信息之外,还可以包含head、template及card元素。其中head、template元素如果包含的话则只可包含一次,而card元素必须至少包含一次。有关这些元素的用法我们后面介绍。

3.1.4 template元素

template元素用于为当前卡片组中的所有卡片定义一个模板,同一规定卡片的某些参数。模版中的事件处理功能则可将这些参数自动应用于同一卡片组中的所有卡片。不过,我们也可以是其中某个或某几个卡片不采用模板规定的形式,方法是在该卡片中定义一个同名的事件来替代模板块中相应的事件。template元素通过<template>和</template>标签含所需内容(content)而实现模板功能的,其语法格式如下:

<template oneterforward=\"href\" onenterbackwared=\"href\" ontimer=\"href\">
内容(content)
</template>

template元素包含的内容中,除了卡片的一般参数外,还可以包含任意多次的do元素和onevent元素。template元素属性的功能及用法说明如下:

1)oneterforward。当用户在浏览器中进入当前卡片时,该属性将指定超链(href)的URL地址,浏览器将据此打开URL指定的卡片或事件。

2)oneterbackward。与上一属性类似,该属性也可以指定其相应卡片或事件的URL地址。如果用户浏览时执行prev任务,那么浏览器就会定位到该属性所指定超链(href)的URL地址,并打开URL指定的卡片或事件。

3)ontimer。当指定时间timer过期的时候,用户浏览器就根据ontimer属性指定的URL打开相应的卡片。

3.1.5 card元素

WML的卡片组是由一个或多个卡片(card)构成的,每个卡片都包含有一套用户和浏览器交互操作的配置及模式。用户对交互操作的需求是多样性的,所以卡片定义时也必须是多样性的。为此,WML提供了card元素,通过<card>和</card>标签定义一个卡片的各种属性、包含内容。它的语法格式如下:

<card id=\"name\" title=\"label\" newcontext=\"boolean\" ordered=\"true\" onenterforward=\"href\" onenterbackward=\"href\" ontimer=\"href\">
内容(content)
</card>

card元素中包含的内容(content)中除了文本、图像信息之外,还可以包含onevent、timer、do和p元素。其中,timer元素只可使用一次,其余3种可使用多次。而且,如果card元素包含onevent元素或timer元素的话,那么onevent元素必须放在最前面,timer元素放在onevent元素的后面,随后才可以使用do或p元素。这个优先顺序是不能乱的。

card元素属性的功能及用法介绍如下:

1)id。用于指定card的名字。改名字是程序导航定位的依据,可以用作程序段锚点,比如<go href=\"#cardname\"/>。其中的cardname便是由id指定的卡片名。

2)title。用于为卡片制订一个简单的标题或说明信息。

3)newcontext。用于指定WAP手机浏览当用户重新进入的时候是否需要初始化卡片中所有的内容。它有true和false两种选择,当指定newcontext=\"ture\"时,卡片的所有内容在用户重新进入时将进行初始化,也不清除历史纪录;否则,指定newcontext=\"false\"时,将不进行初始化设置,也不清除历史纪录。默认状态下的设置值为false。另外,newcontext仅当作为go任务的一部分时才可被执行。

4)ordered。用于向用户手机的浏览器指明卡片内容的组织形式,以便让浏览器根据自身特点及卡片内容组织及时安排显示布局。它有两种布尔值得设置,即true和false。

当ordered=\"true\"时,浏览器将按照线性顺序显示卡片各区域的内容。这个线性顺序通常是大多数用户所习惯采用的信息浏览顺序,比如发送E-mail信息时,我们依次需要E-mail首件人地址、主题及E-mail内容,这个逻辑顺序就数线性顺序。

当ordered=\"flase\"时,浏览器将根据用户选择或指定的顺序来显示内容。这种情况主要是用于显示用户选项、无序组建或用户输入的简单数据纪录等。

5)onenterforward。onenterforward事件仅当用户使用go任务或类似于go的任务位和浏览卡片时才可发生,即如果用户执行go任务,则浏览器就会定位<go>标签中指定超链(href)的URL指定的卡片。card元素中的onenterforward属性是onevent元素的一个简单格式,用于直接指定onenterforward事件的URL地址。

6)onenterbackward。该属性可以指定其响应时间的URL地址。如果用户浏览时执行prev任务,那么浏览器就会定位到该属性所制定超链 (href)的URL地址,并打开URL指定的卡片。onenterbackward属性也属于onevent元素的一个简单格式。

7)ontimer。当指定时间timer过期的时候,用户浏览器就根据ontimer属性指定的URL打开相应的卡片。它也属于onevent元素的一个简单格式。

3.1.7 access元素

access元素是由一个单独的的标签<access>标签实现的元素。用于定义WML整个卡片组的操作权限,即访问控制参数。access元素必须在head元素内和其它的meta信息一起声明,而且每个卡片组只能有一个access元素。其语法格式如下:

<head>
<access domain=\"domain\" path=\"path\">
...
</head>

access元素属性的功能及用法如下:

1)domain。用于指定对卡片组进行操作的URL域,默认域是当前卡片组所在的域。domain的目的是限制访问,用户浏览时浏览器将根据domain值所规定的值来得出与值匹配的地址,并访问该地址对应的卡片或事件。 2)path。用于指定卡片组操作的其它卡片组所在的根目录。默认目录是“/”,即当前卡片组所在的根目录。默认目录的规定使得所有在domain域下的卡片组都可以操作当前卡片组。path的值是访问时需要匹配的路径,它的工作原理与domain十分相似,需要与路径的每个子路径相匹配,否则均属无效。

3.1.8 meta元素

meta元素用于定义WML卡片组相关的通用meta信息。该元素是由一个单独的标签即<meta/>标签实现的元素,其语法格式如下:

<meta name=\"name\"|http-equiv=\"name\" content=\"value\" forua=\"true|false\" scheme=\"format\"/>

其中,name属性和http-equiv属性只能选择使用一个;content属性是必选的,其值根据属性而定;scheme属性目前尚不支持;forua属性为可选属性。各属性功能及用法说明如下:

1)content。该属性用于指定meta信息的性质的值,是不必选的。

2)name。用于指定meta信息性质的名称。用户浏览器通常忽略已经命名meta数据,网络服务企业拒绝发送包含该属性所指定meta数据名称的内容。

3)http-equiv。该属性用于替代name属性,可将meta数据转为WSP或HTTP协议的响应头。

4)forua。该属性用于指定那些开发者希望传送值用户浏览器的性质。它有ture和fales两个取值,如果取false,则卡片组在发送往客户端以前必须用中间代理去除meta元素信息,这是因为传输的协议可能改变;若取值为true,则meta数据必须如实送往用户的浏览器。默认的状态下,该属性的值为false。

5)scheme。该属性用于指定解释meta信息性质值的形式或结构。具体的形式或结构因meta数据的类型不同而不同。

3.2 任务及其元素

WML允许我们在程序中指定一些任务,当某些特定的事件激活时,即可执行这些任务,从而完成需要的操作。例如,我们可以设定任务,当用户按下相应的功能键时,浏览器就可以打开指定的卡片组或卡片。目前,WML提供了4个任务元素,即go、prev、noop和refresh,它们主要与do元素和onevent元素中指定的事件相响应。本节我们就对任务的这些元素做一详细介绍。

3.2.1 go任务

go任务是通过go元素来声明的,而go元素是通过<go>和<go/>标签进行定义的。go元素主要用来定义浏览器需要导航的URL地址。如果该地址是一个WML卡片或卡片组的名字,则浏览器就会打开并显示相应的卡片、卡片组;否则,浏览器就会执行该URL指定的任务或事件等。在历史推栈中,go任务执行的是一个“推进(push)”操作,也就是说,它执行时浏览器浏览的URL地址将送入历史纪录列表中,以被它用。

go元素中可以包含任意次的setvar元素或postfield元素。postfield元素前面已有介绍,这里不再重述,setvar元素我们后面介绍。

go任务的语法格式如下:

<go href=\"href\" sendreferer=\"false|true\" method=\"get|post\" accept-charset=\"charset\">
内容(context)
<go/>

其中属性的功能及用法介绍如下:

1)href。该属性用于指定目标URL地址,比如让浏览器显示的卡片的地址即名称等。属性是必选的,其它属性为可选。

2)sendreferer。该属性用于指定是否传递调用href所指定的URL的卡片的URL,也是当前页的URL,即HTTP头中HTTP_REFERER。有两种选择:true或false。其中,默认值为false。

3)method。与HTML中的表单FORM的method属性一样,该属性用于指定表单是以GET的方式还是post的方式递交,以便通用网关接口CGI处理。默认值为get,但如果没有指定method属性,而<go>和<go/>之间存在postfield元素,则WAP手机浏览器会自动以post方式传递。

4)accept-charset。当web服务器处理来自浏览器的输入信息时,该属性可指定服务器进行数据编码时必须采用的字符集列表。也就是说,该属性指定的字符集替代HTTP头里指定的字符集,以便作为服务器选用字符集的标准。

3.2.2 prev任务

prev任务是由prev元素实现的。该元素通常是一个单独的标签<prve/>,不过有时也可由<prev>和</prev>一对标签进行定义。它用于指定将浏览器导航至历史推栈中的前一个URL地址。在浏览器操作的历史推栈中,prev任务执行的是“取出”操作,将前一个URL地址取出,并把当前URL地址推进历史推栈。如果历史推栈中没有前一个URL地址,即prev/元素不执行任何任务。

prev任务的语法格式为:

<prev/>
或<prev> 内容(content) </prev>

在后一语法格式中,prev元素包含的内容里面一般是setvar元素,该元素的含义前面已有介绍,这里不再重述,具体的用法随后介绍。

3.2.3 refresh任务

refresh任务由refresh元素声明,它用于刷新当前的卡片,对卡片内指定的变量进行更新。其语法格式为:

<refresh>
内容(context)
</refresh>

其中包含的内容(content)中一般有setvar元素,其语法格式为<setvar name=\"name\" value=\"value\"/>,它可指定更新的变量名name,即更新的变量值value。另外,refresh元素也可以不包含setvar元素。而通过时间限制(timer元素)对卡片进行刷新。

3.2.4 noop任务

noop任务由noop元素进行声明,表示什么也不做,是一个空操作,在替代卡片组级的do元素是十分有用。该元素是一个单独的标签,即 <noop/> 标签。其语法格式如下:

<nnop/>

noop元素没有属性,下面的简单程序中就包含了noop元素实现得空任务操作:

<card id=\"card1\">
<do type=\"options\" name=\"dome\">
<noop/>
</do>
...
</card>

3.3 时间及其元素

WML提供了几个元素,专门用于处理用户浏览器的导航和事件。利用这些元素用户可以给某任务制定关联事件。那么当事件触发时,浏览器就会执行相应的任务,比如URL导航就是通过事件实现的。而且,事件可以和一个需要完成的任务捆绑在一起。事件捆绑时一般是通过几种元素及其标签声明来实现的,如go、do和onevent等元素。下面我们就讲解WML的事件元素及事件。

3.3.1 do元素

do元素提供了一个通用的事件处理机制,使得用户可以参与当前卡片的事件处理。通过<do>和</do>标签将用户交互和某一个任务联系在一起。用户交互可以是用户按下的功能键、选择的菜单项,也可以是用户的声音提示。当用户激活这些交互功能时,用户浏览器就会执行与do元素相关的任务。其语法格式如下:

<do type=\"type\" label=\"label\" name=\"name\" optional=\"boolean\">
任务(task)
</do>

其中tast是与do元素关联的动作,也是条件激活时浏览器即将执行的内容。在do元素中,用户必须绑定且只能绑定go、prev、noop和refresh四种元素所实现任务中的一个任务(task)。go元素用于定位制定的URL地址,prev元素用于定位并打开前一操作或任务,doop为空操作,refresh用于刷新当前卡片组或任务,有关他们的详细用法我们后面会陆续介绍。

do元素可以用于卡片一级,也可用于卡片组一级。当用于卡片一级时,do元素必须包含在card元素中;而用于卡片组一级时,do元素必须包含在template元素中,由此定义的do元素将同时应用于当前卡片组的所有卡片。此时如果某个卡片不想应用模板中的do元素及其任务,则需采用我们前面介绍的方法,使用同名事件处理来替代模板中的do元素的事件处理。而且,不论事件关联的任务是否相同,当do元素定义的事件名称相同时,卡片的do元素将忽视卡片组一级do元素的影响,及卡片一组的do元素将被优先执行。

另外,含有空操作任务的do元素,不论它是否被激活,它都不会传送或显示到用户的浏览器中,这在一定程度上可以加快浏览器的工作效率,因为服务器端体它抛弃了一些空任务的判断。 do元素各个属性的功能及用法讲解如下:

1)type。用于指定do元素的类型(type),也即需要关联、绑定的用户交互事件,是必选属性。用户浏览器接到这些事件后,就会激活它们并执行相应的操作与处理。如果在一个卡片中定义了多个do元素并拥有同样type,那么用户必须为每个do指定不同的事件名才行,否则就会发生判断混乱的错误。

do元素典型的类型(type)及执行条件介绍如下:

1、accept。当用户选择或按下相应功能键时(accept)、选项、命名或按钮时,浏览器接收或激活当前所作选择。 2、prev。激活prev键时,浏览器将导航到历史记录中的前一个卡片。

3、help。激活HELP功能键或相应按钮、命名时,浏览器显示与当前内容相关的帮助信息。

4、reset。激活reset功能键或相应按钮、命名时,清除或重置当前卡片组或浏览器的状态。

5、options。激活options功能键或相应按钮、命名时,浏览器显示与当前内容有关的选项或附加操作。

6、delete。激活delete功能键或相应按钮、命名时,删除当前项目或选择。

7、unknown。如果给出的类型不能为do元素所识别,则一律按照unknown型处理,相当于类型为空,即type=\"\"。

8、vnd.*。vnd.*及其它不同大小写组合[Vv][Nn][Dd].*。这种类型定义的都是vnd.cotype,用于激活供应商或用户浏览器自定义的某个特定功能,其中co为公司(company)名的缩写。

9、X-*与X-*。扩展类型,目前WML中还没有使用。

2)label。该属性指定的文本字符串可以表示用户的交互事件。例如,当涯骋桓鋈挝癜蠖ㄔ赼ccept键上之后,并设置了label属性,比如label=\"gone\",那么浏览器就会将label的值“gome”显示在屏幕上;如果不指定,浏览器则会显示默认的“ok”字符串。为了保证能在较小的手机上显示出来,label的属性值最多不超过6个字符。不过这可能因WAP手机品牌、型号不同而稍有不同,有的手机最多不能超过5个字符。而且,如果手机浏览器不支持动态标签显示,那么它就会忽视label属性。

3)该属性用于指定do元索所绑定事件的名称。如果多个do元素制定了相同的name,那么他们绑定的事件统属一个。如果卡片一般与卡片组一级中do元素制定了相同的事件名,那么卡片一级的时间将被优先执行,卡片组一级的事件将被忽视。

WML规定,在同一卡片或在同一模板中,不得指定具有相同事件名(name)的两个或两个以上的do元素。

另外,如果name属性值为空,则相当于没有指定name属性,这时do元素执行的事件或操作由type的属性值决定。

4)optional。指定浏览器是否忽视do元素及其包含的任务。有两个可选值:true和false。如果值为true,则浏览器将忽视当前do元素,即不执行它所绑定的任务。反之,若值为false,则执行do元素。

3.3.2 ontimer事件

ontimer用于指定一个事件。当<timer/> 标签指定的时间到期后,浏览器就执行ontimer所指定的这个事件。ontimer的时间可以是一个URL地址,一个卡片组,一个WML网页,一幅图像或其他符合URL定位的规则的文件。<timer/>标签指定的时间为正整数,单位大小为1/10秒。

ontimer时间只能包含在card元素或template元素的标签中进行定义,其语法形式如下:

<card id=\"name\" title=\"label\" newcontext=\"boolean\" ordered=\"true\" onenterforward=\"href\" onenterbackward=\"href\" ontimer=\"href\">
内容(content)
</card>

或:

<template onenterforward=\"href\" onenterbackward=\"href\" ontimer=\"href\">
内容(content)
</template>

ontimer事件只有一个属性,即ontimer。它用于指定一个超链(href)的URL地址,指定时间timer过期的时候,用户浏览器就会按照超链(href)的URL打开相应的卡片。

3.3.3 onenterforward事件

onenterforward事件仅当用户使用go任务或类似于go任务的任务来定位和浏览卡片时才可发生。设置onenterforward事件后,当用户进入当前卡片组时,浏览器就会定位onenterforward属性或<go/>标签中指定超链(href)的URL地址,并打开URL指定的卡片。

onenterforward事件需要包含在card元素、template元素或onevent元素的标签中进行定义,其语法格式为:

<card id=\"name\" title=\"label\" newcontext=\"boolean\" ordered=\"true\" onenterforward=\"href\" onenterbackward=\"href\" ontimer=\"href\">
内容(content)
</card>

或:

<template onenterforward=\"href\" onterbackward=\"href\" ontimer=\"href\">
内容(content)
</template>

或:

<onevent type=\"onenterforward\">
<go href=\"href\"/>其他任务(task)
</onevent>

前两种格式中,onenterforward事件作为card元素或template元素标签中的一个属性进行定义的,该属性即为onenterforward,它制定了一个超链(href)的URL地址,当用户进入当前卡片时,浏览器就据此打开URL指定的卡片。这种格式制定的任务相当与go任务。

3.3.4 onenterbackward事件

当用户使用prev任务或类似的任务来导航至某一卡片时,onenterbackward事件才可发生。换句话说,当用户从历史堆栈中选取URL地址,并通过浏览器打开这一地址对应的卡片时,onenterbackward事件才可能发生。

与onenterforward事件类似,onenterbackward事件也需要包含在card元素、template元素或onevent元素的标签中进行定义。其具体语法格式如下:

<card id=\"name\" title=\"label\" newcontext=\"boolean\" ordered=\"true\" onenterforward=\"href\" onenterbackward=\"href\" ontimer=\"href\">
内容(content)
</card>

或:

<template onenterforward=\"href\" onterbackward=\"href\" ontimer=\"href\">
内容(content)
</template>

或:

<onevent type=\"onterbackward\">
<go href=\"href\"/>其他任务(task)
</onevent>

前两种格式中,onterbackward事件是作为card元素或template元素标签中的一个属性进行定义的,该属性即为onterbackward,它指定了一个超链(href)的URL地址,当用户使用prev等任务项回到地址时,浏览器就会打开URL指定的卡片。

后一种格式中,onterbackward事件作为onevent元素的一给类型值,并结合<go/>标签指定事件激活时浏览器需要打开的卡片的URL地址。

3.3.5 onpick事件

onpick事件在定义时一般通过onpick属性指定一些项目,当用户选择或取消这些项目时,即可触发onpick事件,执行onpick属性所指定的项目,如打开卡片、卡片组或其他事件等。onpick事件通常在option元素的标签中进行定义,其语法格式如下:

<option value=\"value\" onpick=\"href\">
内容(content)
</opiton>

可以看出onpick时间作为option元素的一个属性来定义具体的动作。这个属性即onpick,它指定了事件触发时浏览器需要定位的超链的URL地址。

3.3.6 onevent元素

onevent元素通过<onevent>和</onevent>标签可以把包含的任务与特定的时间捆绑在一起。当用户激活这一特定事件时,onevent元素所绑定的任务就会被立即执行。onevent元素的语法格式如下:

<onevent type=\"type\">
任务(task)
</onevent>

其中task是与onevent元素关联的动作,也是条件激活时浏览器即将执行的内容。与do元素一样,onevent元素中用户也必须绑定且只能绑定go、prev、noop和refresh四种元素所实现任务中的一个任务,go元素用于定位指定URL地址、prev元素用于定位并打开前一操作或任务,noop为空操作,refresh用于刷新当前卡片组或任务。

onevent元素只有一个属性,即type属性,它是必选属性,主要用于定义特定事件的名称。该属性值的数据类型为CDATA型。

3.3.7 postfield元素

postfield元素用于指定当浏览器接到URL请求时,向原服务器(origin server)传送的域名其域值。传输时,传输域及传输值的实际编码方式主要依赖于浏览器与原服务器的通信方式。postfield元素是通过单独?lt;postfield/>标签进行定义的,其语法格式如下:

<postfield name=\"name\" value=\"value\">

它共有两个属性:name与value,它们的取值均属于VDATA型数据。其中,name属性用于指定传输域的名称,value属性用于定义传输的值。这两个属性均为必选属性。

3.4 变量设置元素与变量设置的有关规定

几乎所有的WML内容都可通过设置参数来实现,这为我们灵活的开发WML程序提供了方便。本节我们先介绍一个变量设置元素,然后再介绍与变量设置有关的一些具体规定。

3.4.1 setvar元素

setvar元素用于指定在当前上下文内容中的变量的值,从侧面影响正在运行的任务。其语法格式如下: <setvar name=\"name\" value=\"value\"/>

它有两个属性:name和value。前者用于指定变量的名称,后者用于指定所需赋给变量的值。这两个属性都是必选的,它们的数据类型均属于VDATA型。如果name属性所规定的变量名不合法或不符合运行环境的要求,那么setvar元素在WML程序运行中将被忽视,不能发挥其应有的作用。

3.4.2 变量设置

WML编程中可以使用变量,变量使用前必须进行定义。变量的命名原则及定义方法我们上一章已经讲过了,这里不再重述。在这里,我们主要介绍WML程序中设置变量的规定。

如前所述,setvar元素可用来设置变量,设置时setvar元素一般需要在go、prev或refresh元素中进行定义。另外,利用input和select元素也可以设置变量。其中前者是将用户输入的文本赋给变量,作为变量的值;而后者则将用户从option元素中选择的value属性的值赋给变量。有关input元素和select元素的语法格式及具体用法我们后面再行介绍。

设置变量时,以下几种情况还应当引起大家注意:

1)可以使用WMLScript的变量值设置WML的变量,反之亦然。也就是说,使用WML及WMLScript编写程序时,它们可以使用同名编程。

2)在WAP开发工具中,通常提供有对变量进行管理和维护的选项卡或对话框,开发人员从中也可以对相应的变量进行设置及编辑。

3)在当前上下文内容中,可以使用card元素的newcontext属性来消除所有的变量值。

3.5 用户输入处理元素

通过WAP手机的按键,用户可以向浏览器显示的卡片中输入数据信息或操作信息。WML为此专门提供了处理用户输入的元素。

3.5.1 input元素

input元素用于定义文本实体对象,包含有对输入文本内容的格式、数据类型、长度、值、变量名等多种属性的具体规定。当用户输入满足input元素的规定时,则接收输入信息,并赋给指定的变量灵活进行相应的操作、处理;否则,就通过浏览器给出具体的处理意见,并进行是单个输入处理或变量初始化操作,比如刷新卡片以让用户重新输入,或给用户指出输入错误所在并等待进一步的处理指令等。input元素是WML编程中处理用户交互活动的重要元素,它通过单独的<input/>标签进行定义,其语法格式如下:

<input name=\"variable\" title=\"label\" type=\"type\" value=\"value\" default=\"default\" format=\"specifier\" emptyok=\"false|true\" size=\"n\" maxlength=\"n\" tabindex=\"n\"/>

其中除了name属性是必选的以外,其他属性都是可选的。这些属性的功能和用法介绍如下: 1)name。该属性用于指定用来保存用户输入文本的变量和名称。定义name属性后WML将根据该属性也即变量名,为即将输入的文本实体对象与之存储空间,以便接收用户输入。

2)title。该属性用于input元素的标签,通常是位于输入框前的提示信息。

3)type。用于指定文本输入区的类型,有text和password两种选择。默认值为text,指定的用户可以输入文本,而且输入的文本会同时逐渐响应并显示在浏览器中。如果选择password,则指定用户输入的文本作为密码文本处理,WML程序按文本实体接收输入的数据,而浏览器上响应用户输入显示时逐渐均为星号(*),由此起到保密的目的。

4)value。该属性用于指定name属性所定义变量的值,它将显示在输入框中。

5)default。该属性用于指定name属性所定义变量的默认值。

6)format。该属性用于格式化输入的数据。

7)maxlength。该属性用于指定用户可输入字符串的最大长度。该属性的上限为256,最多不能超过256个字符。

8)emptyok。用于指定用户是否可以不在输入框内输入内容。

9)size。该属性用于指定输入框的宽度,宽度值为字符个数。

10)tabindex。用于指定多个输入框存在时,类似于HTML中Tab键的具体位置。

3.5.2 select元素

选择列表属于输入元素,允许用户从选项列表中选择需要的项目。WML不仅支持单选列表,及单选项,而且支持多选列表,也就是复选项。select元素允许用户从选列表中选择所需的项目。列表中的选项采用后面我们就要讲到的option元素进行定义,一般是一行格式化的文本。编程时,我们可以使用optgroup元素将option元素的情况项目分成不同级别或层次的选项组,为用户选择提供方便。

select元素是通过<select>和</select>标签进行定义的,语法格式如下:

<select title=\"label\" multiple=\"false|true\" name=\"variable\" default=\"default\" iname=\"index_var\" ivalue=\"default\" tabindex=\"n\">
内容(content)
</select>

其中所有属性都是可选的。select元素各个属性的功能和用法介绍如下:

1)multiple。该属性用于指定选择列表是否可以使用复选框。

2)name。该属性用于指定接收选项值的变量的名称,变量值由value属性预设定。

3)value。用于制定name属性所定义变量的默认值。

4)iname。用于指定包含排序号的变量的名称。

5)ivalue。用于指定选择列表中被选中选项的值,是一个具有排序号性质的值。 6)title。用于指定选择列表的标题。

7)tabindex。用于指定当前选择光标在选择列表中的具体位置,该位置即为当前选择操作将要选择的选项所在的位置。

3.5.3 option元素

option元素用于定义select元素中的一组单选项。它通过<option>和</option>标签进行定义,并可包括事件和单选项的显示文本等信息,其语法格式如下:

<option title=\"label\" value=\"value\" onpick=\"href\">
内容(content)
</option>

option元素的属性均为可选,各属性功能及用法说明如下:

1)value。该属性用于设置键值。当用户选到该选项之后,option元素就会将该值赋给selet元素的name属性所指定的变量。

2)title。用于option元素制定的一个标题,以便提示用户操作。

3)onpick。该属性用于指定用户选到该项并按accept键后所打开卡片组的L。

3.5.4 optgroup元素

optgroup元素用于将多个相关的option元素进行分组,用户浏览器可以借助这种分组来安排选项列表的显示布局,以方便用户选择。optgroup元素是通过<optgroup>和</optgroup>标签进行定义的,其语法格式如下:

<optgroup title=\"label\">
内容(content)
</optgroup>

它所包含的内容中需要包含至少一次option元素或其他的optgroup元素。

optgroup元素只有一个属性,即title属性,用于定义optgroup元素的标题,以便提示用户操作。

3.5.5 fieldset元素

fieldset元素用于设定输入框和相应的说明文本,从而用户就可以利用input元素等借助该输入框输入所需的数据信息。fieldset元素的语法格式如下:

<fieldset title=\"label\">
内容(content)
</fieldset>

由于fieldset元素和输入有关,所以它们的内容中可以包含与输入有关的其他元素。 其语法格式可以看出,fieldset元素只有一个属性,即title属性,用于定义fieldset元素的标题,以便提示用户操作。

3.6 锚、图像、定时器及其元素

本节我们讲解与定位和定时控制有关的3类元素,包括anchor、a、img、timer几种元素。使用它们可以在WML卡片中创建超链接,或在文本流中显示一幅图像,或设置定时器来控制用户操作及卡片显示等。

3.6.1 anchor元素

anchor元素用于创建一个超链接的头部,超链接的其余部分为用户指定的URL地址。当程序运行中用户选中该超链接时,浏览器就会被引入到超连接指定的地址,如其他卡片组或同一卡片组中的其他卡片。

anchor元素由<anchor>和</anchor>标签进行定义,它所包含的超连接必须是真实存在的,而且是能够正确连接的超连接。anchor元素定位超链接时,必须通过相关的任务元素完成定位处理,如go元素、prev元素、refresh元素等。不过,在anchor元素中只能包含1个定位任务,多于一个时会导致WML运行错误。

anchor元素的语法格式如下:

<anchor title=\"label\">
任务
文本
</anchor>

其中的任务需要包含一个进行定位的任务元素。可以看到,anchor元素只有一个属性,即title属性,用于定义fieldset元素的标题,它用于定义即title属性,用于定义fieldset元素的标题,以便提示用户操作。元素的超连接的标题。用户浏览时可利用这一标题来及时了解操作的超连接的名称或者有关提示信息。

3.6.2 a元素

a元素是由anchor元素的简化形式,它内含了anchor元素需要包含的go元素功能爱完成超连接定位,并且不再包含其他任何变量设置。它使用<a>和</a>标签进行定义。

3.6.3 img元素

img元素用于格式化的文本中防止和显示一幅图像。当然,前提是用户所用的浏览器必须支持图像显示。img元素由单独的<img/>标签进行定义,它不包含其它元素。其语法格式如下:

<img alt=\"text\" src=\"url\" localsrc=\"icon\" aligh=\"alignment\" height=\"n\" width=\"n\" vspace=\"n\" hspace=\"n\"/>

属性中alt和src是必须要有的,其他可选。另外,需要注意的是img元素要放在p元素里
,而不能放在do或option元素里。

img元素各个属性的功能和用法介绍如下:

1)alt。该属性用来指定当手机不支持图像显示用来替代现实的文字文本。

2)src。该属性用于指定图像文件的URL地址。

3)localscr。该属性用来指定显示存在手机ROM的图标文件。

4)align。该属性用来指定图像显示是相对当前文本行的对齐方式。

5)height。用于设定图像显示时的高度。

6)width。与height属性类似,用于设定图像显示时的宽度或宽度百分比。

7)vspace。该属性用于指定图像显示时的上边距和下边距,默认值为0。

8)hspace。与vspace属性类似,该属性用于指定图像显示时的左边距和右边距。

3.6.4 timer元素

timer元素用于设定一个定时器,可以延时显示卡片组、卡片,或实现WML程序的等待操作,或在卡片组和卡片之间实现切换以取得动画效果。

一个卡片只能使用一次timer元素,也即是说只能设置一个定时器。当用户进入还有定时器的卡片时,定时器就会开始工作,其时间值就会逐渐减小。timer元素指定的时间值单位1/10秒。其语法格式如下:

<timer name=\"variable\" value=\"value\"/>

它的两个属性中,value属性是必选的,name属性为可选。name属性用于指定表示时间值的变量的名称,该变量的取值由定时器的时间值决定,时间值减小,该变量的值也相应地减小,并终始保持不变。

value属性用于指定name属性所定义变量的初始值。如果name属性定义的变量在定时器初始化时还没有值,那么该变量就将采用value属性指定的值;否则,改变量就会忽视value属性的值。如果没有定义name属性,也就是说,没有指定时间变量,那么timer元素指定的定时器仍将采用value属性的值进行延时处理。

3.7 文本格式化及其元素

WML程序中,为使显示的文本呈现出丰富的样式,WML提供了一些用于格式化的元素,我们通过这些元素及其相应的标签可以对文本进行标注和控制,从而实现不同的显示效果。

3.7.1 增强元素

增强元素都是一些成对的标签,用于指定文本的增强显示信息。比如b元素通过<b></b>标签可以控制其中的文本按照粗体字进行显示。 3.7.2 br元素

“br”即break,是用于换行的元素,它是使用单独的<br/>标签进行定义的。br元素的作用相当于插入一个回车符。

3.7.3 p元素

“p”即指paragraph,p元素用于划分段落,是当前文本换行并插入一个空白行。p元素可以使用单独的<p/>标签进行定义,也可以使用<p>和</p>标签成对的进行定义。其语法格式为:

<p aligh=\"alignment\" mode=\"wrapmode\"/>



<p aligh=\"alignment\" mode=\"wrapmode\"/>
文本
</p>

1)align。该属性用于设置段落在浏览器中的对齐方式,有left、center和righ三种取值。这三种参数值分别表示p元素当前定义的文本段落及浏览器窗口的左侧、中间和右侧进行对齐。默认值为left,及段落与浏览器窗口的左侧对齐排列。

2)mode。该属性用于指定下一段落的换行方式。

3.7.4 td元素

td元素用于规定表格单元格的内容。其语法格式如下:

<td> 单元格内容 </td>

3.7.5 tr元素

WML中的表格是按照行、列进行组织的。一个表格由若干行组成,每行由若干列组成。tr元素用于定义表格的行。其语法格式如下:

<tr> 单元格内容 </td>
</tr>

3.7.6 table元素

table元素与tr元素、td元素一起,可用来创建能容纳文本和图像的表格,并可设置表格各列中文本和图像的对齐方式。其语法格式如下:

<table align=\"alignment\" title=\"label\" columns=\"n\">



<table align=\"alignment\" title=\"label\" columns=\"n\">
内容
</table>

其中各个属性的功能和用法介绍如下:

1)align。该属性用于指定表各个列中文本和图像的对齐方式。

2)title。该属性用于指定table元素的标题。

3)columns.该属性用于指定表格的列数,该数不能为0。
posted @ 2006-04-29 09:42 崛起的程序员 阅读(229) | 评论 (0)编辑 收藏
我们首先以Microsoft 的Internet Infomation Server(IIS 4或IIS 5)以及Unix平台中最为普遍的Apache两种Web Server来介绍如何以它们来建制自己的WAP Sever,将原先已经建制的Web信息平台扩展到无限平台之上。

1.1 WAP Sever Configuration

其实WAP Sever建制非常容易,WAP在信息传输的部分是使用HTTP来进行的,与现有的WWW信息平台一样,因此,将现有的Web Sever都可以通过对配置的调整成为WAP Sever,提供对无线装置的服务。


                      图1.1
在WAP服务当中,所提供的新文件类型目前共有五种,以扩展名来分的话分别是wml,wmlc,wmls,wmlsc,wbmp,分别代表的是WML原始文件、WML文件的二进制码、WML Script的原始程序码、WML Script二进制码,以及单色的Wireless BMP文件。这些的扩展名必须新增到Web Server的MIME Type设定中,Web Server才能够提供WAP的服务。

不论使用何种Web Sever软件,例如Microsoft IIS、Netscape Enterprise Sever、Apache、或是任何一种,只要加入以上五个MIME Type设定,就可以提供WAP服务了。

接下来,我们就一步步带领大家以IIS及Apache两种不同的Server进行设定,将您的Web Server进化成WAP Server,以提供WAP服务。

1.2 把Microsoft IIS 变成WAP Server

在这一节里面,我们以IIS为例,进行MIME Type的设定。在Windows 系统的发展过程中,到了Windows 98、Windows NT 4.0的Option Pack公开之后,对于WWW信息平台的提供就变得更容易了。

本节中所使用的范例程序是IIS 5.0。但是Windows NT 4.0 Workstation版本以及 Windows 98所使用的Personal Web Server(PWS)则在设定步骤上面有些差异。

首先,进入IIS的管理画面。

启动了IIS管理画面之后,请选取您要管理的机器名称,在选取了机器名称,IIS管理画面会将该机器上面所有的WWW站点都显示出来。这是因为IIS中可以针对Windows 2000或Windows NT 中所设定的不同IP位置或同一个位置中不同的Port建立多个WWW的服务。

选取了要设定的Web站点之后,请在该站点的图示上面以鼠标右键点选一下,调用设定画面,如图1.1所示。在图中的设定画面选项里,可以设定包含虚拟目录、制作清单,以及目录安全设定等许多不同的功能,只是因为在这里我们所点选的是整个站点,因此所变更的设定将会被套用在整个站点中。

当然我们也可以只针对某几个特定的目录作设定,只要在特定的目录上面按鼠标右键,使用该目录的设定选项,如图1.2所示,更改设定并套用之后就可以了。

                        图1.2
设定选项之后,请直接选取“属性”显示变更设定内容的视窗,如图1.3

                        图1.3
在设定内容视窗中,一共包含了十大分项(这里的分项会因为IIS版本不同而有所差异)。进入设定内容视窗之后,一开始会位于整个WEB站点的主要设定部分,包含在IIS管理员中对于这个站点的名称、让这个Web站点对应的IP地址和Port号码,而我们要新增的MIME Type则要在HTTP标题的部分作设定的。

再进入了HTTP标题设定部分之后,会出现如图1.4所示的画面,在HTTP标题设定部分包含了四种不同的设定,分别是:启用内容限制服务,用来设定某些特殊的目录中的文件权限,以及自定义HTTP标题,用来设定自定义HTTP header meta资料,第三个是内容分级。可以将一个目录或站点中的文件内容设定为内含某种等级的文件,例如暴力、性、不当文字与言语等,第四个部分就是我们现在要设定的部分:MIME对应,将某个特定的扩展名与MIME type。

                        图1.4
接下来,请点击文件类型这个按键,调用MIME type设定画面,如图1.5所示。

                        图1.5
在图1.5中的新类型按键上面以鼠标左键点选之后,就会出现图1.6的输入画面,此时我们要将前述的五个MIME type一次一次的输入到扩展名与内容类型之中。当然了,一次只能输入一种新的扩展名与MIME Type的对应。而输入完成之后在IIS 5.0与4.0中就可以直接使用这些新的文件类型了。


                        图1.6
而Windows NT workstation或是Windows 98中的Personal Web Server(PWS)的设定方法有很大的差别。

因为在PWS之中并没有特别让使用者输入资料的设定界面,所有的PWS的MIME Type都是使用Windows操作系统内部的MIME Type对应资料的,因此我们必须在Windows资源管理器中的文件类型这部分来做新增的动作。

首先我们需要点击资源管理器上面的工具选项,选择其中的文件夹选项这个项目,调用设定系统配置的视窗界面,如图1.6所示。

一开始画面将会显示在第一页的设定部分,也就是图1.6中的“查看”那一页,我们新增的MIME Type的部分是位于文件类型的设定页中,因此我们必须以鼠标左键点击文件类型的Tag以切换到该设定页。此时以鼠标左键点击图1.6中的新建类型按钮,则会出现图1.7所示画面。

新增类型包含了:文件描述、关联扩展名、内容、类型,也就是MIME type,褂姓飧隼嘈偷脑ど璧睦┱姑T丛赪indows NT系统中还需要将对新增的文件类型加入所谓开启、编辑等对应动作的处理程序与参数设定,但是现在新增的MIME type只是为了在Personal Web Server(PWS)上面提供新的MIME type,因此便不用在此指定其他相对应的动作所要启动的程序。但读者如果想在Windows资源管理器当中以鼠标双击该文件进入编辑器来处理这个文件的话,请暗下动作选项(A)下面的新增按钮,则可以选择以系统中的那个程序来处理该种文件。

不过要注意到的是,目前的环境中还没有比较好的WML编辑器,所能用的应该也就是几个电信大厂所提供的工具了,所以读者也可以使用目前在网络上极为流行的几种文字编辑器来处理WML文件,像笔者就是以UltraEdit 6.0作为对WML文件的处理程序。

在Windows NT Workstation或Windows 98当中设定好了这些新的MIME type之后,必须重新启动计算机,让系统更新对于文件类型的对应,然后Personal Web Server(PWS)才能够提供WAP服务。

1.3 将Apache 升级为WAP Server

Apache可以说是目前所有跨平台的Web Server软件中支持最多平台的一个,它的前身是NCSA的httpd,一开始这个软件在改版的时候就已经考虑到大多数的操作系统平台了,包含了NT,Linux、以及各种不同的Unix操作系统,在这里我们将以Linux配合Apache作为设定的例子。

1.3.1 Apache 系统介绍

Apache系统比较像NCSA的httpd(因为是同一群工程师将原来的程序改良而成的),只是后来的Apache比前期的NCSA httpd增加了许多功能,像Apache提供了同步执行多个处理程序的功能,使得NCSA httpd原本效能不良的问题获得了长足的改善。现在,只要你使用的是Unix或者Linux系列的操作系统,同时也使用Apache作为你的Web Server的话,你可以再列出全部处理程序的时候看到其中有许多个httpd处理程序同时在执行。

Apache使用了同时维持一定的未忙闲状态的处理程序来改善效能问题。因为旧有的NCSA httpd言用所有的UNIX上面的成学习法方法,将所有的功能大都以一个处理程序(process)作为提供者。而在1995-2000这几年中,Web的使用者的数量极为快速的增加,因而就系统以单一处理程序提供服务的方式越来越不受欢迎,而在CGI与互动程序的效能与支持上面也增加了许多的功能。

例如在互动程序上新增了PHP程序的支持、JAVA Servlet、JSP,甚至于还支持Windows IIS系统中的ASP程序的功能,都是让许多人不断支持Apache的原因。除此之外,Apache对于CGI效能的提升也作了极大的改良,除了提供程序人员开发可植入Apache系统的界面,用以提升常用程序的效能之外,还针对一般CGI的效能问题与安全问题作了改善。

在把Apache Server装到Linux系统上面之后,Linux系统的/etc目录下就会新增一个httpd目录,用来存放所有的Apache的配置设定文件,而执行文件httpd则会被存放在/usb/sbin目录中,同时还会将一个名为mime.types的文件放到/etc目录下面。

且不论Apache里头的其他设定(这部分的设定文件都在/etc/httpd/conf底下,包含了目录的权限、虚拟目录的对应、以及Sever本身的其他相关的设定)MIME type的设定值需要修改/etc/mime.types这个文件即可。

/etc/MIME.types这个文件中储存的是以行作分隔的MIME types设定,每一行为一个独立的MIME type,如果要对应到特定的扩展区的话,就直接在MIME type之后留一个空白字符,在打上扩展名。以wmlc为例,必须在文件中加入以下这一行设定:

application/vnd.wap.wmlc wmlc

完成之后,重新启动Apache,Apache就可以提供wmlc这个文件类型让使用者端存取了,当然,在第一节中所介绍的五个MIME type也都得加到设定文件中才能提供完整的WAP服务。


1.4 建立WAP的测试环境

在WAP编程与开发中,为了对所编写的网页及应用进行测试,我们通常要建立WAP的测试环境。一般来说,WAP测试环境可以从浏览器环境、模拟环境、实际环境三个方面进行建立,本节就对此进行基本的介绍。

1.4.1 浏览器环境

浏览环境的建立十分简单。目前Internet尚有许多站点提供有WML浏览器的免费下载服务。比较著名的WML浏览器是Winwap(http://www.wapschool.com/chinese/download/winwap22.exe)以及各移动通信设备公司提供的浏览器。安装这些浏览器后,用户就可以在Windows系统环境下访问WAP站点,查看WAP页面。

在WAP的服务器端,开发人员则可以利用Windows NT 4.0或Windows 2000以及Internet信息服务器IIS(Internet Information Server 4.0/5.0)软件进行模拟。在原有的WWW服务子目录下再建立一个WAP子目录,将所有的WML网页放在其中,并对IIS进行必要的配置。然后,在WWW服务器正常运转的情况下,开发人员通过在Winwap等WAP浏览器中输入http://locallhost(本地计算机名)/wap/index.xml的形式,即可进入WAP网页进行浏览测试。

这种测试环境的优点是实施起来比较简单,建设比较快,操作起来也比较简单易学。其不足之处在于,这种测试用的浏览器毕竟是Windows环境下的浏览器,支持大部分的WML标记,查看窗口的界面可以扩大和缩小,比较自由,因而所看到的测试效果与实际手机上的效果可能会有比较大的差别,而且它也不能提供编辑、编译和调试的集成环境。

1.4.2 模拟环境

用于WAP测试的模拟环境是通过使用移动通信设备公司所提供的WAP手机模拟器来实现WML浏览的。目前可以从各公司站点上下载的模拟器有Nokia Toolkit、Ericsson R1.0 Emulator、Ericsson WapIDE、UpPhone UP>Simulator、Motorola Mobile ADK等。

相比较来说,Nokia和Motorola提供了比较完整地结成开发环境,其它两家主要提供了模拟WAP手机的WML浏览。由于模拟器一般都是供直接的HTML服务器直接连接,所以WAP服务器端只需要Windows NT/2000及IIS 4.0/5.0 软件即可进行模拟和调试。与上面介绍的方法一样,在WWW服务器工作正常的情况下,通过输入http://locallhost(本地计算机名)/wap/index.xml的形式,即可对WAP网页进行浏览测试。

虽然说这种模拟环境提供了集成环境及与WAP手机基本一致的模拟器,但仍难保证所用模拟器与其实际产品完全一致,尤其是没有WAP网关的参与,因此这是一种并不完备的检测。特别的,这种模拟环境下与无线电话应用WTA(Wireless Telephony Application)相关的服务根本没有办法进行检测。不过,对于单纯的开发测试来说,这样模拟环境基本能满足要求。

1.4.3. 实际环境

WAP测试的实际环境中需要WAP手机、网卡及服务器三个部分,因此,为了建立WAP测试的实际环境,开发者需要购买一些主流的WAP手机,同时使用前面介绍的方法在原来的HTML服务器上建立一个WAP专用的虚拟目录,已建立WAP服务器,然后使用现有网关或夹在移动通信公司提供的相应网关,那么只要三者都能顺利正确的工作,开发者就可以通过WAP手机对WAP网页及应用进行测试了。

1.5 完成WAP设定之后

在完成WAP设定之后,我们就可以开始提供给所有的使用者WAP服务了,当然,在这之前,还必须将Content(内容)准备好,也就是说,我们还得先将WML文件与相关的应用程序准备一下。
posted @ 2006-04-29 09:38 崛起的程序员 阅读(283) | 评论 (0)编辑 收藏
完成WAP服务器的建立和WAP浏览器的安装之后,我们接下来就可以使用WML语言来编写WAP网页或应用,并通过WAP服务器及浏览器进行调试。从本章开始我们将系统地学习WML语言,本章主要讲解WML语言的基础知识,下一章全面讲解WML的语法、标签和规则。 
2.1 WML的简单例子及编辑、测试方法

无限标记语言WML(Wireless Markup Language)是一种基于扩展标记语言XML(Extension Markup Language)的语言,是XML的子集。它可以显示各种文字、图像等数据,是由WAP论坛(http://www.wapforum.org)提出并专为无线设备用户提供交互界面而设计的,目前版本为1.1版。这些无线设备包括移动电话,呼机和个人数字助理PDA(Personal Digital Assistants)等。

2.1.1 WML与WAP设备

为了更好的了解和使用WML语言,开发人员应对WML使用的设备和支持WML的设备的特点、特征有个大概的了解。

一般而言,WML使用的无线设备通常具有以下特点:

与普通的个人计算机相比,体积较小;
设备的内存有限,且其CPU性能也有限;
通讯带宽较窄、时延较长。

以移动电话、PDA为例来讲,支持WML的设备主要具有以下特征:

有一个显示屏幕,可显示2.凶址啃?2各字符;2.凶址型ǔ0ūA舾δ馨磁サ囊恍校?br>支持数字和字符的输入;
支持操作者使用箭头或数字按钮进行选择;
支持ASCII的可打印码;
通常都有两个可编程功能键,即Accpet键和Options键,一般安排在接近键盘的屏幕下方;
通常有一个Prev导航键。

我们介绍WML所使用WAP设备的目的,是希望读者通过WAP设备的特点、特征来了解WML语言的特点,进而对WML编程所要解决的问题有个大概的认识。

2.1.2 使用文本编辑器面写WML程序

使用WML语言编写WAP网页或应用时,需要使用一个编辑器进行编辑。与HTML编程一样,WML编写的程序也是纯文件文本,可以使用任意文本编辑器进行编写,比如Windows系统中的“记事本(NotePad)”等。也可以使用比如Nokia WAP Toolkie等软件(有关此具体的用法会在以后的学习过程中提起)。我们先介绍第一种方法,随后介绍第二种。

如果要使用\"记事本(NotePad)\"来编写WML程序,则可以在Windows系统中,单击“开始”按钮,然后从出现的菜单中,依次将光标指向“程序”、“附件”、“记事本”,启动“记事本”程序。屏幕上随后就会出现它的编辑窗口,从中就可以输入并编写WML程序了。

作为举例,我们可以输入如下简单的程序。

<?xml version=\"1.0\"?>
<!DOCTYPE wml PUBLIC \"-//wapforun//DTD WML 1.1//EN\" \"http://www.wapforum.org/DTD/wml_1.1.xml\">
<xml>
<card id=\"card1\" title=\"Title\">
<P>
<!--Write your card implementation here.-->
Hello World!!
</P>
</card>
</xml> 


输完后将它保存为hello.xml文件。保存时注意文件的扩展名应为xml而不是txt。

2.2 WML程序结构

上一节我们降解了一个简单的WML程序,具有HTML编程的读者可以看出,WML程序在结构上形式上与html程序有很多相似之处。下面我们就根据一个实例来分析一下WML程序的结构及组成。

2.2.1 WML的元素和标签

分析实例之前,我们有必要对WML的元素和标签予以简单说明。与HTML类似,WML的主要语法也是元素和标签。元素是符合DTD(文档类似定义)的文档组成部分,如title(文档标题)、IMG(图像)、table(表格)等等,元素名不区分大小写。WML使用标签来规定元素的属性和它在文档中的位置。标签使用小于号(<)和大于号(>)括起来,即采用“<标签名>”的形式。标签分单独出现的标签和成对出现的标签两种。大多数标签是成对出现的,由首标签和尾标签组成。首标签和尾标签又分别称为起始标签和终止标签。首标签的格式为“<元素名>”,尾标签的格式为“</元素名>”。成对标签用于规定元素所含的范围,比?lt;b>和</b>标签用于界定黑体字的范围,也就是说<b>和</b>之间包住的部分采用黑体字显示。单独标签的格式为“<元素名/>”,他的作用是在相应的位置插入元素。如〈br/〉标签表示在该标签所在位置插入一个换行符。

2.2.2 WML程序结构形式及组成的实例分析

了解了上述知识后,下面我们在分析一个实例程序。程序如下:

<?xml version=\"1.0\"?>
<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\" \"http://www.wapfourm.org/DTD/wml_1.1.xml\">

<wml>

<card id=\"card1\" ontimer=\"#card2\" title=\"Tookit Demo\">
<timer value=\"50\"/>
<p aligh=\"center\">
</br></br></br>
<big>
<!--Write your card implementation here.-->
Welcome to....
</big>

</p>
</card>

<card id-\"card2\" ontimer=\"#card 3\"title=\"Toolkit Demo\">
<timer value=\"50\"/>
<p align=\"center\">
<br/><br/>
<b>
The Nokia<br/>
</b>
Wireless Application Protocol
</u>
...
</p>
</card>

<card id=\"card3\"title=\"Toolkit Demo\">
<p align=\"center\">
<br/><br/><br/>
<big>
<i>
Toolkit
</i>
</big>
</p>
</card>

</xml> 


该程序运行后将在WAP手机屏幕依次显示3屏信息。先显示\"Welcome to ...\",然后显示\"The Nokia Wireless Application Protocol...\",最后显示\"Tookit!\"。显示时每屏都有标题\"Tookit Demo\",相邻两屏之间延时为50,其单位大小为1/10秒,延时50即5秒。

通过以上实例我们可以了解到WML程序的结构形式及组成:

1)语法。WML与HTML极为相似。仍然是一种标记语言,并且延续了XML的语法规则,具体的语法我们会以后的学习过程中遇到。

2)文件声明。所有的WML程序必须在文件的开头处声明XML文件类型,包括XML的版本,WML的文档类型、所用规范等。声明形式如下:

<?xml version=\"1.0\">
<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\" \"http://www.wapforum.org/DTD/wml_1.1.xml\"> 


3)标签。在WML语言中需要使用标签(Tag),其使用形式与HTML和XML等标记语言中的形式是完全一致的。

4)元素。WML的元素(Element)用于描述卡片组(Deck)的标记信息即结构信息。一个元素通常有一个首标签、内容、其它元素及一个尾标签组成,具有下述两种结构之一:

<首标签>内容</尾标签>

<标签/>

元素包含的内容中还可以有元素,这些元素也是有首标签、相应内容、其它元素及尾标签组成。不包含内容的元素成为空元素。它为一个单独的标签。或者说,单独的标签也是一种元素。

5)属性。WML与XML一样,其标签可以包含很多属性。属性用于给标签提供必要的附加信息,且属性内容通常在起始标签内使用。不过,属性内容不会被浏览器显示,它至作为参数为标签提供必要的信息。

指明属性值的时候,需要把该值用引号扩起来,可以是单引号或者双引号,引号通常成对嵌套使用。属性名称必须小写。例如:<card id=\"card 1\" ontimer=\"#card2\" title=\"Toolkit Demo\">

而且,单引号的属性中还可以包含双引号的属性。实体字符也可以作为属性值。实体字符是指诸如&、<、>、'、\"的特殊字符,在WML程序中显示着类字符需要特殊处理,后面我们介绍具体方法。

6)注释。WML程序中也可以加入注释。注释内容用于给开发人员顺利阅读源代码提供方便,它不会被浏览器显示出来。注释内容在标签中用感叹号(!)引出,并用于<!--注释内容-->的形式。例如:<!-- Write your card implementation here.-->。需要说明的是,XML程序中不支持注释的嵌套。

7)文档结构。WML文档是由“卡片(Card)”和“卡片组(Deck)”构成的,一个Deck是一个或多个Card的集合。当客户端发出请求之后,WML即从网络上把Deck发送到客户浏览器,这是用户就可以浏览Deck内包含的所有Card,而不必从网上单独下载每一个Card,程序中的第一个Card是缺省得可见的Card。

注意:Deck是一副纸牌的意思,这里是指一叠卡片,所以我们在这里称之为它为“卡片组”。另外,Card指的是WAP手机屏幕大小的网页,尽管有时一个Card可能需要多屏才能显示完,但我们也可以把它翻译成“页面”,不过这样与HTML中的页面容易混合。因此我们在这里称之为卡片。

2.2.3 WML程序的基本结构

以上我们简单分析了WML的程序结构及组成,由此大家可以对WML程序有个整体上的初步认识。下面我们给出WML程序的基本结构。

<?xml version=\"1.0\"?>
<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\" \"http://www.wapforum.org/DTD/wml_1.1xml\">
<wml>
<head>
<access/>
<meta..../>
</head>
<card>
Some contents...
</card>
<wml> 


该基本结构可以分为以下几个关键部分:

1)声明。WML程序有许多Deck组成,对于每一个Deck,在其文档开头必须进行XML的声明和文档类型DOCTYPE的声明。

XML声明总是在文件的第一行,注意前面最好不要有空格或者还行:

<?xml version=\"1.0\"?>

2)紧跟着是DOCTYPE声明,注意声明是字母的大小写不要搞错:

<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\" \"http://www.wapforum.org/DTD/wml_1.1xml\">

3)<xml>标签。该标签用于包含和定义WML的一个Deck。它有一个可选的xml:lang属性来制定文档的语言,比如<wml xml:lang=\"zh\">表示文档语言为中文。

4)<head>标签。该标签用于包含和定义Deck的相关信息。<head>标签之间可以包含一个<access>标签和多个<meta>标签。

5)<access/>标签。它的一般形式是<access domain=\"域\" path=\"/路径\"/>,主要用于制定当前Deck的访问控制信息,有两个可选的属性。其中,domain用来制定域,默认值为当前域,path用来制定路径,默认值为“/”,即跟目录。由于<access>单独使用,所以要用“/”结尾,后面我们还会系统的讲解WML的各种标签,这里即使看不懂也没关系,主要有些感性的认识就可以了。

6)<meta...>标签。它的一般形式是<meta 属性 content=\"值\" scheme\"格式\" forua=\"true|false\"/>,用于提供当前Deck的meta信息,包括内存数据处理方式,以及数据传输方式和处理方式等。有关该标签的详细内容我们后面会专门给出。

7)<card>标签。一个Deck可以包含多个Card,每个Card的内容可能不止一屏显示。对于每一个Card,WML均使用<card>和</card>进行包含和定义。 <card>同时可以包含多个可选的属性,如<card id=\"name\" title=\"label\" newcontext=\"false\" ordered=\"true\" onenterforwand=\"url\" pmemterbackward=\"url\" ontimer=\"url\">。至于这些属性的具体含义及功能,我们将在后面介绍。

2.3 WML语言的基本知识

上一节我们介绍了WML程序的基本结构,接下来我们介绍WML语言的基本知识,主要包括WML的字符集、变量、数据类型及WML程序的基本组成部分等。
2.3.1 WML的字符集及编码

WML使用XML的字符集,即通用字符集ISO/IEC-1062.,也即统一字符编码标准Unicode 2.0。同时,WML还支持其他系列的字符集子集,例如UTF-8、ISO-8859-1或UCS-2等。其中:

UTF-8是指通用字符集UCS(Universal Character Set)的转换格式8(Transformation Format 8),主要传输国际字符集的转换编码。UTF-8采用了UCS字符的8位编码,提供了十分安全的编码格式,可以有效避免数据传输过程中的窃听、截取及非法解密。同时,UTF-8与7位ACSII码完全兼容,不会影响此类编码实现的程序;它的编码规则十分严格,能够有效避免同步传输错误,而且还会支持其它字符集提供了足够的空间。

ISO-8859-1字符集是国际标准化组织ISO(International Standardization Organization)制定的ACSII字符集的扩展集,能够表示所有西欧语言的字符。与ISO Latin-1一样,ISO-8859-1与Windows环境中普遍使用的美国国家标准协会ANSI(American National Standards Institute)的字符集极为类似,绝大多数情况下无需区分。在不特别指明的情况下,HTTP协议均使用ISOLatin-1字符集。因此,为了WML页面中表示非ACSII(non-ACSII)字符,开发人员需要使用相应的ISO Latin-1编码的字符。

UCS-2是ISO 1062.标准中自定义的通用多8位编码字符集(Universal Multiple-Octer Coded Character Set)的2字节(即16位)编码标准,其字符编码值与Unicode字符的标准编码值相等。

WML文档可以采用HTML 2.0规范所定义的任何字符编码标准经编码处理。一般说来,WML文档的字符编码是需要转换为另外的编码格式,以与WAP用户的手机浏览器所用字符标准相适应,否则,手机浏览器就无法显示WML页面中的字符。然而,编码转换时可能会丢失一些字符信息,所以,如果在用户端进行WML文档的编码转换,那么就可能导致某些结果信息丢失而不能被用户所浏览。因此,如有必要,我们应当尽量在WML页面传送到用户浏览器之前完成编码转换。

为了解决这一问题,一方面,我们需要为Web服务器补充定义WML的数据类型,以让服务器可以准确传输这些数据,另一方面,我们需要制订编码转换的原则。

2.3.2 WML字符使用基本规则

WML是一种比较严格的语言,字符使用必须遵守相应的规则,这些基本规则主要包括以下几个方面:

1)大小写敏感。在WML中,无论是标签元素还是属性内容都是大小写敏感的,这一点继承了XML的严格特性,任何大小写错误都可能导致访问错误。

一般来说,WML的所有标签,属性,规定和枚举及它们的可接受值必须小写,Card的名字和变量可大写和小写,但它是区分大小写的。包括参数的名字和参数的数值都是大小写敏感的,例如variable1、Variable1和vaRiable1都是不同的参数。 2)空格。对于连续的空字符,程序运行时只需要一个空格。属性名、符号(=)和值之间不能有空格。

3)标签。标签内属性的值必须使用双引号(\")或单引号(')括起来。对于不成对出现的标签,必须在大于号(>)前加上顺斜杠(/),比如换行标签必须写成<br/>才正确。

4)不显示的内容。在WML中,不显示的字符主要包括换行符、回车符、空格和水平制表符,它们的8位十六进制内码分别为10、13、32及9。

程序执行时,WML将忽视所有的多于一个以上的不显示字符,即WML会把一个或多个连续的换行、回车、水平制表符及空格转换成一个空个。

5)保留字符。这是WML的一些特殊字符,如小于号(<)、大于号(>)、单引号“'”、双引号“\"”、和号(&)。

6)显示汉字。如果希望WML程序执行时能够显示汉字,则只需要程序开头使用encoding指定汉字字符集即可。例如:<?xml version=\"1.0\" encoding=\"gb2312\">。

注意:指定汉字字符集的形式和方法可能因为开发工具或WAP手机的不同而不同。

2.3.3 变量

WML编程中可以使用变量,变量使用前必须进行定义。变量一旦在Deck中的某一个Card上定义过,其他Card则可以不必重新定义就能直接调用该变量。

定义变量的语法格式为:
$identifier
$(identifier)
$(identifier:conversion)

其中identifier指变量名,或说变量标示符;conversion指变量的替代。

变量名是由US-ACSII码、下划线和数字组成的,并且只能以US-ACSII码开头。变量名严格区分大小写,也即,变量名是大小写敏感的。

定义变量的语法在WML中享有最高的解释优先级。

有关变量的使用说明如下:

1)在WML中,变量可以在字符串中使用,并且在运行中可以更新变量的值。

2)当变量等同于空字符串时,变量将处于未设置状态,也就是空(Null)。

3)当变量不等同于空字符串时,变量将处于设置状态,也就是非空(Not Null)状态。

4)在“$identifier”形式下,WML通常以变量名后面的一个空格表示该变量名的结束。如果在某些情况下空格无法表示一个变量名的结束,或者变量名中包含有空格,则必须使用括号将变量名括起来,即采用“$(identifier)”的形式。

WML程序中的变量是可以替代的,我们可以把变量的数值赋给Card中的某一文本。有关变量替代说明如下:

1)在WML程序中,只有文本部分才可以实现替代。

2)替代一般在运行期发生,而且替代不会影响变量现在的值。

3)任何标签是按照字符串替代的方式实现的。

4)替代是按照字符串替代的方式实现的。

由于变量在语法中有最好的优先级,包含变量声明字符的字符串将被当作变量对待,所以如果要使程序显示“$”符号,则需要连续使用两个“$”进行说明。例如:<p> Your acconut has $$15.00 in it </p>一句显示的结果为:Your account has $15.00 in it。

2.3.2. WML核心数据类型

WML的核心数据类型均属于字符型数据,是根据XML的数据类型定义的,共有下述2.掷嘈停?1)CDATA型。这种数据类型是WML用得最多的一种,可以是数字、字符串或包含数字的字符串。不过定义时,不论是数字或字符串,都必须以文本的形式定义,及数据用引号引起来。CDATA型的数据仅用于属性值。例如\"$(value)\"或name=\"value\"等。注意,这里的value指CDATA型的数据值。

2)PCDATA型。这是从CDATA中分解出来的一类数据,除了可以是文本形式的数字、字符串或两者的混合串外,还可以是WML的标签。PCDATA型的数据只能用于WML的元素表示。

3)NMTOKEN型。这是一类特殊的数据,凡是包含或部分包含数字、字母及标点符号的数据均属于NMTOKEN型数据。这种数据可以用标点符号开头,但不用于定义变量名或元素名。

4)id型。专门用于定义WML元素名称的数据类型。

在这2.掷嘈椭校珻DATA型用起来比较灵活,它可以使变量或数据免于语法检查。这是因为,CDATA内的数据内容都会被当作文本来处理,从而可以避免WML的语法检查,直接作为文本显示出来。

2.3.5 WML数据值性质

除了NMTOKEN型数据外,WML其他3种数据都必须以文本形式即加上引号进行定义。我们关心的问题是,这些类型的数据可以表示哪些数据值呢?或者说,它们所表示的数据值的性质是什么呢?

事实上,WML数据只在性质上可以是长度(Length)、宏变量(Vdata)、流(Flow)、内行(Inline)、布局(Layout)、文本(Text)、超链(Href)、布尔值(Boolean)、数据(Number)或增强方式(Emphasis)。

2.3.6 卡片与卡片组

前面我们分析了WML程序的结构时,曾将讲到WML文档的信息是通过卡片集和卡片组集的形式进行组织的。一个Deck是一个或多个Card的集合。当客户终端发出请求之后,WML即从网络上把Deck发送到客户的浏览器,Deck是服务器发送信息的最小单位。用户浏览器收到Deck后,可以浏览其中包含的所有Card。Card用于表示或描述一个或多个用户交互单位。

2.3.7 卡片组模板

同一卡片组通常会含有许多卡片,这些卡片的定义、属性或格式通常都大同小异。如果我们逐一定义各个卡片,显然是十分麻烦的。为此,WML提供了卡片组模板的功能,模板内定义了一系列标准和参数,可以应用到同一卡片组的所有卡片中去,从而能够大大地提高我们的编程效率。有关卡片组模板的内容我们后面会专门介绍的。

2.3.8 WML与URL、程序段锚点

我们知道,环球网WWW是各种信息和设备的网络,为保证全球范围内的交互,人们制定了3种规范:其一,统一资源定位器URL提供所有网络资源的标准命名方式和定位方式;其二,标准协议,如HTTP协议等,提供WWW资源的传输方式;其三。标准内容类型,如HTML、WML,提供WWW资源的内容形式及标准。WML沿用了这些规范,并扩大了URL使用的范围。在WML中,不仅超连接、文件路径及文件名可以作为URL处理,卡片名、宏变量名及各种内部资源名等也可作为URL处理。

为此,WML改进了HTML命名资源位置的方式,采用程序锚点(Fragment Anchor)的形式来处理WML程序中某段程序的地位。程序段锚点根据文档WML规则进行定义,并按照程序段表示符前加井字好(#)的方式书写。使用程序段锚点,WML程序可以在同一卡片组中定位不同的卡片。如果在程序中不指定程序段,那么程序中引用的URL名称则指整个卡片组,而且卡片组的名称同时也是本卡片组内的第一个卡片的名称。 2.3.9 浏览器操作历史

为了在浏览器端管理WML程序的执行,WML使用“浏览器前后关系”的功能保存WML程序执行的状态及各种参数、变量等,这样可以用来记录用户的操作情况。同时,WML还提供了一个简单的导航历史模型,以URL地址的形式记录了用户浏览时的各种操作,并把这些URL地址放入历史推栈。通过推栈,用户即可实现历史浏览的回潮及其它操作。
posted @ 2006-04-29 09:31 崛起的程序员 阅读(164) | 评论 (0)编辑 收藏
目前的手机游戏基本分为两类:一是文字类,二是图形类。

  文字类游戏

  文字类的游戏主要分为WAP在线游戏和短信互动游戏。

  WAP游戏主要以手机上网的方式去进行游戏,也就是WAP网络支持,WAP是手机上网的一种通讯协定,其意义相当于TCP/IP。由于手机的画面有限,所呈现的网页也必须做精简化处理,所需要的网页编写语言就是WML,相当于HTML,而相对于目前互联网上的WWW,手机也会有自己的MMM网站,所以才成为WAP游戏。有些象我们目前所玩的网页游戏,一般来讲角色扮演类游戏比较多,需要手机上网的支持,所以水货手机就别想了……登陆移动梦网或者联通无限找到其对应的游戏频道即可进行了,一般收费是包月制,每月4到8左右。随着技术的进步,现在已经出现了图形化的WAP社区类游戏,玩家甚至可以上传自己的照片让其它玩家看到。

   短信游戏是以发送短信的形式进行游戏的。 发送游戏SP固定特服号开始参与游戏。首先注册游戏(一般为包月制)如:同城约会:移动用户发送123到456,联通用户发送123到789( 移动和联通使用不同的特服号)发送后会马上收到系统反馈信息,反馈信息中会有多重选择并应带有资费情况,(一般是确认是否订制)根据反馈信息选择,并回复相应指令到固定特服号。确定完成后将收到下一条系统回复信息,一般需要用户注册自己在游戏中的资料了。游戏不同,规则也不同,填好信息发送,注册成功开始游戏。用户向系统发送每一条短信后,自然会收到一条回复信息,并根据信息提示决定下一步的方向。如想结束游戏,可根据游戏开始前系统发送的规则说明中的指令退出操作。

  图形类游戏

  这里着重推荐的是图形类游戏。图形类分为下载类和内置类。

  下载类以Kjava和Brew以及联通新推出的Unijava为主。大概这几个名字可以说是耳熟能详了,不过他们仅仅是编写程序的语言,而对于我们只需要知道这个是图形类的游戏就够了。图形类的游戏目前已经达到了早起电视游戏或者电脑游戏的水平,根据玩家按键的不同,图像中的人物做着不同的动作,从而完成一系列的任务,目前大多以动作游戏占主流,兼备益智游戏、角色扮演、体育游戏、竞技游戏、射击游戏等多种多样的游戏,可以说是百花争放,陆续有大量的好游戏等着你呢。图形类的游戏可玩性高,种类丰富。所以被所有厂商视为发展的重点。
  
  内置类为手机本身自带的游戏,早期的游戏在手机中无法更换和删除,如NOKIA的贪吃蛇。现在也是可以更换和删除的。目前的内置式游戏越来越有趣,如摩托v303中的波斯王子,K700的3D网球。所以在选购手机的时候,这也是一个很大的参考价值哦。

  玩手机游戏具体需要哪些设备?

  想玩短信游戏?
 
  目前市面上的所有手机几乎都是支持中文短信的,只要您的手机支持,那么玩起短信游戏是没问题的了。

  想玩WAP游戏?
  
  和短信游戏需要短信支持一样,想网WAP游戏就要看你的手机支持不支持WAP了。早期的GSM手机可能无法直接登陆WAP的,因为早期的手机当中没有将登陆WAP的帐号和密码集成在手机当中。而后期的手机将帐号和密码集成在手机之中了,那么就可以轻松的登陆了,如果您的手机登陆WAP时需要提供帐号和密码的话,只能去移动通信大厅或者联通通信大厅去看看他们是否还提供这项业务了。若是连接不正确,先确认自己的手机业务是否开通了移动梦网或者联通无限,确认无误后可以参照下面的设置进行设置。

  新手上路:如何设置才能登陆移动梦网?
  新手上路:如何设置才能登陆联通无限?
  
  登陆后,进入游戏的项目就可以选择你喜欢的游戏进行游戏了。

  想玩下载的图形游戏?
  
  基本根据下载方式的不同可能需要的设备也不同了。
  
  1.登陆WAP去下载。这里就就需要手机支持WAP了,具体设置以及操作和上面的WAP游戏一样,不过是进入不同的选项,移动梦网进入“百宝箱-游戏百宝箱”去下载,联通无限进入“联通神奇宝典-软件超市-软件目录-游戏天地”中去下载。
  
  2.短信的方式下载。同样需要手机支持WAP网络并且能登陆的,只是减少了你选择游戏所花费的时间和昂贵的流量费用。直接从短信得到下载地址的连接。
  
  3.电脑下载传输到手机上。根据传输的方式不同,也需要不同的设备。可以分为机种传输的模式:蓝牙、红外线、数据线。依次也就需要蓝牙设备、红外设备和手机传输数据线了。由于操作较为麻烦并且需要一定的设备和电脑知识,这里暂不做讨论。 

  如何开通中国移动GPRS服务?

  1. 如何申请使用GPRS服务?

  答:现在中国移动GPRS服务正在试商用,GPRS手机用户可以申请使用。正式商用时,如果您买了GPRS手机,还需要申请开通GPRS业务功能。申请开通GPRS业务功能就和开通其他新业务一样,到营业厅填写业务变更表或直接打电话1860申请开通即可。

  2. GPRS如何收费?

  答:目前GPRS按流量进行计费,用户可以选择以下四种套餐:

月租费(元) 赠送的免费流量数(MB) 超过免费流量后的费用(元/KB) 
自由套餐 0 0 0.03 
经济套餐 20 1 0.01
时尚套餐 100 20 0.01 
商务套餐 200 不限量使用 
另外,用户漫游不加收漫游费。 
申请GPRS 功能不需要开户费,您只要拨打电话1860或到营业厅申请开通GPRS服务即可。此处资费供参考,如与各地移动公司有出入,各地以本地移动公司的资费为准。

  3. 按量收费和按时间收费有什么区别?

  答:按时间收费就是按照从接入网络至与网络断开的时间长度进行计费;按流量收费就是按照接入网络后产生的实际数据比特流来进行计费,接入了网络但没有数据传递是不收费的。

  例如用户在浏览网页的时候,新打开一个窗口然后进行阅读,产生了5kByte的流量,则只收用户5kB的钱,阅读的时间是不收费的;用户进行网上聊天,在一个小时内发送或者接收了6条信息,流量为1kB,则只收1kB的钱,而不是按一小时来收费;用户下载一首mp3,流量为4M,然后在线收听,则收费为 4M的流量费用,收听的时候是不收费的。

  GPRS为用户上网提供了一种更好、更快、更优质的服务,而且是完全按照实际流量来收费,收费更合理,因此也能为您省钱。

  4. GPRS需要换手机、换号、换卡吗?要不要开户费?

  答:使用GPRS业务就要换一个GPRS手机,不需要换号,不需要换卡,只需要打开GPRS业务功能,无需开户费。

  5. 使用GPRS手机如何设置?有何简化程序吗?

  答:使用GPRS 手机需要设一个连接(APN)设置(使用WAP业务设为CMWAP,使用其他www业务使用CMNET),用户名和密码设置为空,其他设置与原来的WAP设置一样。目前ericsson的手机在部分省市能够支持通过短信空中下载GPRS和WAP的设置。

  6. GPRS的覆盖和漫游情况如何?可不可以漫游?

  答:目前中国移动GPRS网络覆盖全国16省25个城市,今年10月份马上就可以覆盖全国所有省会城市和大部分大中城市。在国内GPRS覆盖的地方都可以实现GPRS 的自动漫游,且用户漫游不加收漫游费。

  在没有GPRS网络的时间GPRS 手机还可以用,可以进行正常通话和CSD方式的数据业务,但GPRS的业务就不能使用了。

  7. GPRS与原来的CSD(电路交换)拨号方式如何切换?

  答:在进行数据业务之前(如使用WAP)选择使用的连接方式,或者CSD 的拨号方式或者GPRS方式,要切换的话需要先关闭原来的业务再重新进行连接方式的选择。比如:用户启动WAP浏览器,手机会跳出菜单让你选择连接方式,(或默认为缺省的连接设置)如选择了GPRS,那么用户就会通过GPRS 浏览WAP的各类信息、游戏等,用户要换成拨号接入WAP,则需要推出WAP浏览器,选择连接方式为拨号,再启动WAP浏览器才能切换至拨号(CSD)方式的WAP。 

  8. 用户购买了支持GPRS的手机之后,可享受哪些新的增值业务?

  答:目前用户可以使用所有原有的WAP上面的信息与娱乐服务以外,还可以通过GPRS手机+笔记本或Pocket PC等设备进行www浏览,另外用户还可以通过GPRS手机直接收发POP3 email、进行网上聊天、网上会议、移动炒股、移动商务、移动娱乐、网上购物等等。

  9. 使用GPRS上网时,能否接电话和接收短信?

  答:可以。使用GPRS上网不影响正常的通话和接收短信。

  如何开通CDMA手机网络服务?
  1、如何开通CDMA手机网络服务 

  在您购买CDMA手机的时候,请询问经销商,您的手机是否支持上网服务,是否默认开通上网服务,一般提供上网服务的CDMA手机是默认开通的。上网服务是按照您访问网络所成生的数据流量来计算的。如果您的CDMA手机需要进行一些设置才可以进行手机上网,那么请参照您的手机说明书,或询问经销商以及相关网站。

  2、CDMA手机网络服务资费标准(以下资费如超过流量,均按照0.005元/KB计算流量)


套餐种类(普通CDMA用户)  资费标准 
互动世界(用于手机上网) 0.01元/KB 
互动世界低速上网
(用于手机上网) 0.1元/分钟 
掌中宽带
(用于外接电脑上网) 98元获得1000MB流量,198元获得5000MB流量,298元获得8000MB流量  
套餐种类(133月租号码) 资费标准 
联通无限随身定制(5元) 5元获得1700KB的流量(漫游则获得流量无效,按照0.005元/KB计算) 
联通无限随身定制(15元) 15元获得8.5MB流量(漫游则获得流量无效,按照0.005元/KB计算) 
联通无限随身定制(35元) 不限制流量(漫游则获得流量无效,按照0.005元/KB计算)  
posted @ 2006-04-29 09:21 崛起的程序员 阅读(236) | 评论 (0)编辑 收藏
1.Image格式文件转成byte[]?

答:第一步:
如何获得image对象的int数组呢?这个就简单了可以通过获得RGB数组就可以。Image对象中有直接的getRGB方法,不过这里的参数的位置和J2SE中不太一样
第二步,把得到的Int数组再生成byte数组。
原帖地址:http://www.j2medev.com/bbs/dispbbs.asp?boardID=2&ID=6559&page=1

2.eclipse中RMS保存的文件在哪里?

答:图像文件保存的时候先转化为byte[] 不是String 

存储的时候一般存储为***.db文件  目录在wtk_home/appdb/模拟器/***.db
原帖地址:http://www.j2medev.com/bbs/dispbbs.asp?boardID=2&ID=6470&page=1


3.关于播放midi

答:须明确的一点是 我们的MIDlet是运行在Java ME平台环境下的  不一定设备提供的功能在Java ME平台都得到了实现
你可以将虚拟机可以播放的音乐类型!
原帖地址:http://www.j2medev.com/bbs/dispbbs.asp?boardID=19&ID=6455&page=1


4.团队开发利器CVS培训基础讲义

答:详细见:http://www.j2medev.com/bbs/dispbbs.asp?boardID=4&ID=6324&page=1


5.netBean能用两个代码窗口显示同一个类吗?

答:不能

6.g.drawString()占用内存问题

答:drawString()方法本身不占内存,关键是drawString中的参数string是不是每次都new了一个,如果把分数变成图片这样就不会new新的对象,从而节省了内存和gc的频率。
原帖地址:http://www.j2medev.com/bbs/dispbbs.asp?boardID=11&ID=5319&page=2

7.手机上使用过三重的图形缓冲技术

答:三重图形缓冲又称翻页技术。这种方法不一定会提高绘制速度,在J2SE中系统会根据速度在二重缓冲和该方法中自动进行切换,当然仅仅是1.4版本以后添加的。一般的MIDP设备应该会吃不消。
原帖地址:http://www.j2medev.com/bbs/dispbbs.asp?boardID=11&ID=5376&page=2

8.索爱k700模拟器支持中文显示修改方法

答:SonyEricsson_Z520.properties和SonyEricsson_K700.properties的不同点,发现只是
font.default = .......这块字体属性不一样而已,因此就将k700所需的字体换成z520用的字体,就能显示中文了。
原帖地址:http://www.j2medev.com/bbs/dispbbs.asp?boardID=4&ID=3061&page=2

9.模拟核弹的爆炸效果

答:具体实现依赖于程序结构,原理就是显示一张图片后,循环一定次数才显示下一张图片。
原帖地址:http://www.j2medev.com/bbs/dispbbs.asp?boardID=11&ID=6182&page=2

10.UI界面和CANVAS类之间如何切换屏幕?

答:在Canvas的paint()方法中把屏幕清一下   比如用白色覆盖一下
原帖地址:http://www.j2medev.com/bbs/dispbbs.asp?boardID=19&ID=1413&page=1

11.关于 properties文件

答:用UE或者notepad应该都可以打开,这个文件是用来配置模拟器的特性的 比如是否支持触摸屏。
原帖地址:http://www.j2medev.com/bbs/dispbbs.asp?boardID=2&ID=6524&page=1

12.在E680I中如何实现全屏

答:在midp2.0下,摩托罗拉的机子也是可以用Canvas.setFullScreenMode()来全屏的,最上面的电池格以及信号强弱格那一窄条是固定的。而Nokia和索爱就不这样,全屏真的是全屏。(不过经过斑竹mydeman实际实验后指出,E680I的确可以实现真全屏,可能是各个版本不同的关系请广大站友自己实验后得出正确结论,然后反馈给我)
原帖地址:http://www.j2medev.com/bbs/dispbbs.asp?boardID=19&ID=6216&page=1

13.J2ME中显示时间的问题

答:Calendar ca = Calendar.getInstance();
        int year =ca.get(Calendar.YEAR);
        int month=ca.get(Calendar.MONTH)+1;
        int day=ca.get(Calendar.DATE);

14.这是个什么错误MontyThread -6

答:程序用了platformRequest来调用真机上不支持的功能
原帖地址:http://www.j2medev.com/bbs/dispbbs.asp?boardID=2&ID=6334&page=1

15.JDK1.5可否用来开发MIDP,

答:可以

16.如何由jar生成相应的jad文件

答:用ezJad等生成工具。
原帖地址:http://www.j2medev.com/bbs/dispbbs.asp?boardID=17&ID=6019&page=3
工具下载地址:http://www.j2medev.com/Soft/ShowSoft.asp?SoftID=299

17.怎樣判斷g.darwImage()方法是否執行完畢一次

答:加个flag 或者等paint()执行完  
原帖地址:http://www.j2medev.com/bbs/dispbbs.asp?boardID=2&ID=4683&page=4

18.eclipse开发的J2ME程序中显示图片出现异常

答:eclipse 中 file--->new--->java---->source folder 记得一定得是source folder
原帖地址:http://www.j2medev.com/bbs/dispbbs.asp?boardID=2&ID=568&page=4

19.Httpconnect 连接超时问题
答:It is not possible to set the connection timeout in a midlet. The timeout behavior is fixed and is:1. For open(), if the connection cannot be made after 60 seconds, the open() will throw IOException.2. For read(), if no data arrives after 40 seconds, the read() will throw IOException.3. For write(), if the data cannot be sent out after 40 seconds, the write() will throw IOException.
原帖地址:http://www.j2medev.com/bbs/dispbbs.asp?boardID=2&ID=5345&page=5

20.motov600的左右软键怎么用
答:v600的左右键为21,22和E398的正好相反.
原帖地址:http://www.j2medev.com/bbs/dispbbs.asp?boardID=2&ID=5284&page=5
posted @ 2006-04-29 09:11 崛起的程序员 阅读(226) | 评论 (0)编辑 收藏
PHP V5 新的面向对象编程特性显著提升了这个流行语言中的功能层次。学习如何用 PHP V5 动态特性创建可以满足需求的对象。

PHP V5 中新的面向对象编程(OOP)特性的引入显著提升了这个编程语言的功能层次。现在不仅有了私有的、受保护的和公共的成员变量和函数 —— 就像在 Java™、 C++ 或 C# 编程语言中一样 —— 但是还可以创建在运行时变化的对象,即动态地创建新方法和成员变量。而使用 Java、C++ 或 C# 语言是做不到这件事的。这种功能使得超级快速的应用程序开发系统(例如 Ruby on Rails)成为可能。

但是,在进入这些之前,有一点要注意:本文介绍 PHP V5 中非常高级的 OOP 特性的使用,但是这类特性不是在每个应用程序中都需要的。而且,如果不具备 OOP 的坚实基础以及 PHP 对象语法的初步知识,这类特性将会很难理解。

动态的重要性

对象是把双刃剑。一方面,对象是封装数据和逻辑并创建更容易维护的系统的重大方式。但另一方面,它们会变得很繁琐,需要许多冗余的代码,这时可能最希望做到的就是不要犯错。这类问题的一个示例来自数据库访问对象。一般来说,想用一个类代表每个数据库表,并执行以下功能:对象从数据库读出数据行;允许更新字段,然后用新数据更新数据库或删除行。还有一种方法可以创建新的空对象,设置对象的字段,并把数据插入数据库。

如果在数据库中有一个表,名为 Customers,那么就应当有一个对象,名为 Customer,它应当拥有来自表的字段,并代表一个客户。而且 Customer 对象应当允许插入、更新或删除数据库中对应的记录。现在,一切都很好,而且有也很多意义。但是,有许多代码要编写。如果在数据库中有 20 个表,就需要 20 个类。

有三个解决方案可以采用。第一个解决方案就是,坐在键盘前,老老实实地录入一段时间。对于小项目来说,这还可以,但是我很懒。第二个解决方案是用代码生成器,读取数据库模式,并自动编写代码。这是个好主意,而且是另一篇文章的主题。第三个解决方案,也是我在本文中介绍的,是编写一个类,在运行时动态地把自己塑造成指定表的字段。这个类执行起来比起特定于表的类可能有点慢 —— 但是把我从编写大量代码中解脱出来。这个解决方案在项目开始的时候特别有用,因为这时表和字段不断地变化,所以跟上迅速的变化是至关重要的。

所以,如何才能编写一个能够弯曲 的类呢?





回页首


写一个柔性的类

对象有两个方面:成员变量方法。在编译语言(例如 Java)中,如果想调用不存在的方法或引用不存在的成员变量,会得到编译时错误。但是,在非编译语言,例如 PHP 中,会发生什么?

在 PHP 中的方法调用是这样工作的。首先,PHP 解释器在类上查找方法。如果方法存在,PHP 就调用它。如果没有,那么就调用类上的魔法方法 __call(如果这个方法存在的话)。如果 __call 失败,就调用父类方法,依此类推。

魔法方法
魔法方法是有特定名称的方法,PHP 解释器在脚本执行的特定点上会查找魔法方法。最常见的魔法方法就是对象创始时调用的构造函数。

__call 方法有两个参数:被请求的方法的名称和方法参数。如果创建的 __call 方法接受这两个参数,执行某项功能,然后返回 TRUE,那么调用这个对象的代码就永远不会知道在有代码的方法和 __call 机制处理的方法之间的区别。通过这种方式,可以创建这样的对象,即动态地模拟拥有无数方法的情况。

除了 __call 方法,其他魔法方法 —— 包括 __get__set —— 调用它们的时候,都是因为引用了不存在的实例变量。脑子里有了这个概念之后,就可以开始编写能够适应任何表的动态数据库访问类了。





回页首


经典的数据库访问

先从一个简单的数据库模式开始。清单 1 所示的模式针对的是单一的数据表数据库,容纳图书列表。


清单 1. MySQL 数据库模式
														
																DROP TABLE IF EXISTS book;
CREATE TABLE book (
        book_id INT NOT NULL AUTO_INCREMENT,
        title TEXT,
        publisher TEXT,
        author TEXT,
        PRIMARY KEY( book_id )
);

														
												

请把这个模式装入到名为 bookdb 的数据库。

接下来,编写一个常规的数据库类,然后再把它修改成动态的。清单 2 显示了图书表的简单的数据库访问类。


清单 2. 基本的数据库访问客户机
														
																<?php
require_once("DB.php");

$dsn = 'mysql://root:password@localhost/bookdb';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()); }

class Book
{
  private $book_id;
  private $title;
  private $author;
  private $publisher;

  function __construct()
  {
  }

  function set_title( $title ) { $this->title = $title; }
  function get_title( ) { return $this->title; }

  function set_author( $author ) { $this->author = $author; }
  function get_author( ) { return $this->author; }

  function set_publisher( $publisher ) {
  $this->publisher = $publisher; }
  function get_publisher( ) { return $this->publisher; }

  function load( $id )
  {
    global $db;
$res = $db->query( "SELECT * FROM book WHERE book_id=?",
    array( $id ) );
    $res->fetchInto( $row, DB_FETCHMODE_ASSOC );
    $this->book_id = $id;
    $this->title = $row['title'];
    $this->author = $row['author'];
    $this->publisher = $row['publisher'];
  }

  function insert()
  {
    global $db;
    $sth = $db->prepare(
'INSERT INTO book ( book_id, title, author, publisher )
    VALUES ( 0, ?, ?, ? )'
    );
    $db->execute( $sth,
      array( $this->title,
        $this->author,
        $this->publisher ) );
    $res = $db->query( "SELECT last_insert_id()" );
    $res->fetchInto( $row );
    return $row[0];
  }

  function update()
  {
    global $db;
    $sth = $db->prepare(
'UPDATE book SET title=?, author=?, publisher=?
   WHERE book_id=?'
    );
    $db->execute( $sth,
      array( $this->title,
        $this->author,
        $this->publisher,
        $this->book_id ) );
  }

  function delete()
  {
    global $db;
    $sth = $db->prepare(
      'DELETE FROM book WHERE book_id=?'
    );
    $db->execute( $sth,
      array( $this->book_id ) );
  }

  function delete_all()
  {
    global $db;
    $sth = $db->prepare( 'DELETE FROM book' );
    $db->execute( $sth );
  }
}

$book = new Book();
$book->delete_all();
$book->set_title( "PHP Hacks" );
$book->set_author( "Jack Herrington" );
$book->set_publisher( "O'Reilly" );
$id = $book->insert();
echo ( "New book id = $id\n" );

$book2 = new Book();
$book2->load( $id );
echo( "Title = ".$book2->get_title()."\n" );
$book2->delete( );
?>

														
												

为了保持代码简单,我把类和测试代码放在一个文件中。文件首先得到数据库句柄,句柄保存在一个全局变量中。然后定义 Book 类,用私有成员变量代表每个字段。还包含了一套用来从数据库装入、插入、更新和删除行的方法。

底部的测试代码先删除数据库中的所有条目。然后,代码插入一本书,输出新记录的 ID。然后,代码把这本书装入另一个对象并输出书名。

清单 3 显示了在命令行上用 PHP 解释器运行代码的效果。


清单 3. 在命令行运行代码
														
																% php db1.php
New book id = 25
Title = PHP Hacks
%

														
												

不需要看太多,就已经得到重点了。Book 对象代表图书数据表中的行。通过使用上面的字段和方法,可以创建新行、更新行和删除行。





回页首


初识动态

下一步是让类变得稍微动态一些:动态地为每个字段创建 get_set_ 方法。清单 4 显示了更新后的代码。


清单 4. 动态 get_ 和 set_ 方法
														
																<?php
require_once("DB.php");

$dsn = 'mysql://root:password@localhost/bookdb';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()); }

class Book
{
  private $book_id;
  private $fields = array();

  function __construct()
  {
    $this->fields[ 'title' ] = null;
    $this->fields[ 'author' ] = null;
    $this->fields[ 'publisher' ] = null;
  }

  function __call( $method, $args )
  {
    if ( preg_match( "/set_(.*)/", $method, $found ) )
    {
      if ( array_key_exists( $found[1], $this->fields ) )
      {
        $this->fields[ $found[1] ] = $args[0];
        return true;
      }
    }
    else if ( preg_match( "/get_(.*)/", $method, $found ) )
    {
      if ( array_key_exists( $found[1], $this->fields ) )
      {
        return $this->fields[ $found[1] ];
      }
    }
    return false;
  }

  function load( $id )
  {
    global $db;
$res = $db->query( "SELECT * FROM book WHERE book_id=?",
   array( $id ) );
    $res->fetchInto( $row, DB_FETCHMODE_ASSOC );
    $this->book_id = $id;
    $this->set_title( $row['title'] );
    $this->set_author( $row['author'] );
    $this->set_publisher( $row['publisher'] );
  }

  function insert()
  {
    global $db;
    $sth = $db->prepare(
'INSERT INTO book ( book_id, title, author, publisher )
   VALUES ( 0, ?, ?, ? )'
    );
    $db->execute( $sth,
      array( $this->get_title(),
        $this->get_author(),
        $this->get_publisher() ) );
    $res = $db->query( "SELECT last_insert_id()" );
    $res->fetchInto( $row );
    return $row[0];
  }

  function update()
  {
    global $db;
    $sth = $db->prepare(
'UPDATE book SET title=?, author=?, publisher=?
  WHERE book_id=?'
    );
    $db->execute( $sth,
      array( $this->get_title(),
        $this->get_author(),
        $this->get_publisher(),
        $this->book_id ) );
  }

  function delete()
  {
    global $db;
    $sth = $db->prepare(
      'DELETE FROM book WHERE book_id=?'
    );
    $db->execute( $sth,
      array( $this->book_id ) );
  }

  function delete_all()
  {
    global $db;
    $sth = $db->prepare( 'DELETE FROM book' );
    $db->execute( $sth );
  }
}

..

														
												

要做这个变化,需要做两件事。首先,必须把字段从单个实例变量修改成字段和值组合构成的散列表。然后必须添加一个 __call 方法,它只查看方法名称,看方法是 set_ 还是 get_ 方法,然后在散列表中设置适当的字段。

注意,load 方法通过调用 set_titleset_authorset_publisher方法 —— 实际上都不存在 —— 来实际使用 __call 方法。





回页首


走向完全动态

删除 get_set_ 方法只是一个起点。要创建完全动态的数据库对象,必须向类提供表和字段的名称,还不能有硬编码的引用。清单 5 显示了这个变化。


清单 5. 完全动态的数据库对象类
														
																<?php
require_once("DB.php");

$dsn = 'mysql://root:password@localhost/bookdb';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()); }

class DBObject
{
  private $id = 0;
  private $table;
  private $fields = array();

  function __construct( $table, $fields )
  {
    $this->table = $table;
    foreach( $fields as $key )
      $this->fields[ $key ] = null;
  }

  function __call( $method, $args )
  {
    if ( preg_match( "/set_(.*)/", $method, $found ) )
    {
      if ( array_key_exists( $found[1], $this->fields ) )
      {
        $this->fields[ $found[1] ] = $args[0];
        return true;
      }
    }
    else if ( preg_match( "/get_(.*)/", $method, $found ) )
    {
      if ( array_key_exists( $found[1], $this->fields ) )
      {
        return $this->fields[ $found[1] ];
      }
    }
    return false;
  }

  function load( $id )
  {
    global $db;
    $res = $db->query(
  "SELECT * FROM ".$this->table." WHERE ".
  $this->table."_id=?",
      array( $id )
    );
    $res->fetchInto( $row, DB_FETCHMODE_ASSOC );
    $this->id = $id;
    foreach( array_keys( $row ) as $key )
      $this->fields[ $key ] = $row[ $key ];
  }

  function insert()
  {
    global $db;

    $fields = $this->table."_id, ";
    $fields .= join( ", ", array_keys( $this->fields ) );

    $inspoints = array( "0" );
    foreach( array_keys( $this->fields ) as $field )
      $inspoints []= "?";
    $inspt = join( ", ", $inspoints );

$sql = "INSERT INTO ".$this->table." ( $fields )
   VALUES ( $inspt )";

    $values = array();
    foreach( array_keys( $this->fields ) as $field )
      $values []= $this->fields[ $field ];

    $sth = $db->prepare( $sql );
    $db->execute( $sth, $values );

    $res = $db->query( "SELECT last_insert_id()" );
    $res->fetchInto( $row );
    $this->id = $row[0];
    return $row[0];
  }

  function update()
  {
    global $db;

    $sets = array();
    $values = array();
    foreach( array_keys( $this->fields ) as $field )
    {
      $sets []= $field.'=?';
      $values []= $this->fields[ $field ];
    }
    $set = join( ", ", $sets );
    $values []= $this->id;

$sql = 'UPDATE '.$this->table.' SET '.$set.
  ' WHERE '.$this->table.'_id=?';

    $sth = $db->prepare( $sql );
    $db->execute( $sth, $values );
  }

  function delete()
  {
    global $db;
    $sth = $db->prepare(
   'DELETE FROM '.$this->table.' WHERE '.
   $this->table.'_id=?'
    );
    $db->execute( $sth,
      array( $this->id ) );
  }

  function delete_all()
  {
    global $db;
    $sth = $db->prepare( 'DELETE FROM '.$this->table );
    $db->execute( $sth );
  }
}

$book = new DBObject( 'book', array( 'author',
   'title', 'publisher' ) );
$book->delete_all();
$book->set_title( "PHP Hacks" );
$book->set_author( "Jack Herrington" );
$book->set_publisher( "O'Reilly" );
$id = $book->insert();

echo ( "New book id = $id\n" );

$book->set_title( "Podcasting Hacks" );
$book->update();

$book2 = new DBObject( 'book', array( 'author',
  'title', 'publisher' ) );
$book2->load( $id );
echo( "Title = ".$book2->get_title()."\n" );
$book2->delete( );
? >

														
												

在这里,把类的名称从 Book 改成 DBObject。然后,把构造函数修改成接受表的名称和表中字段的名称。之后,大多数变化发生在类的方法中,过去使用一些硬编码结构化查询语言(SQL),现在则必须用表和字段的名称动态地创建 SQL 字符串。

代码的惟一假设就是只有一个主键字段,而且这个字段的名称是表名加上 _id。所以,在 book 表这个示例中,有一个主键字段叫做 book_id。主键的命名标准可能不同;如果这样,需要修改代码以符合标准。

这个类比最初的 Book 类复杂得多。但是,从类的客户的角度来看,这个类用起来仍很简单。也就是说,我认为这个类能更简单。具体来说,我不愿意每次创建图书的时候都要指定表和字段的名称。如果我四处拷贝和粘贴这个代码,然后修改了 book 表的字段结构,那么我可能就麻烦了。在清单 6 中,通过创建一个继承自 DBObject 的简单 Book 类,我解决了这个问题。


清单 6. 新的 Book 类
														
																..
class Book extends DBObject 
{
  function __construct()
  {
    parent::__construct( 'book', 
      array( 'author', 'title', 'publisher' ) );
  }
}

$book = new Book( );
$book->delete_all();
$book->{'title'} = "PHP Hacks";
$book->{'author'} = "Jack Herrington";
$book->{'publisher'} = "O'Reilly";
$id = $book->insert();

echo ( "New book id = $id\n" );

$book->{'title'} = "Podcasting Hacks";
$book->update();

$book2 = new Book( );
$book2->load( $id );
echo( "Title = ".$book2->{'title'}."\n" );
$book2->delete( );
?>

														
												

现在,Book 类真的是简单了。而且 Book 类的客户也不再需要知道表或字段的名称了。





回页首


改进的空间

对这个动态类我想做的最后一个改进,是用成员变量访问字段,而不是用笨重的 get_set_ 操作符。清单 7 显示了如何用 __get__set 魔法方法代替 __call


清单 7. 使用 __get 和 __set 方法
														
																<?php
require_once("DB.php");

$dsn = 'mysql://root:password@localhost/bookdb';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()); }

class DBObject
{
  private $id = 0;
  private $table;
  private $fields = array();

  function __construct( $table, $fields )
  {
    $this->table = $table;
    foreach( $fields as $key )
      $this->fields[ $key ] = null;
  }

  function __get( $key )
  {
    return $this->fields[ $key ];
  }

  function __set( $key, $value )
  {
    if ( array_key_exists( $key, $this->fields ) )
    {
      $this->fields[ $key ] = $value;
      return true;
    }
    return false;
  }

  function load( $id )
  {
    global $db;
    $res = $db->query(
  "SELECT * FROM ".$this->table." WHERE ".
   $this->table."_id=?",
      array( $id )
    );
    $res->fetchInto( $row, DB_FETCHMODE_ASSOC );
    $this->id = $id;
    foreach( array_keys( $row ) as $key )
      $this->fields[ $key ] = $row[ $key ];
  }

  function insert()
  {
    global $db;

    $fields = $this->table."_id, ";
    $fields .= join( ", ", array_keys( $this->fields ) );

    $inspoints = array( "0" );
    foreach( array_keys( $this->fields ) as $field )
      $inspoints []= "?";
    $inspt = join( ", ", $inspoints );

$sql = "INSERT INTO ".$this->table. 
   " ( $fields ) VALUES ( $inspt )";

    $values = array();
    foreach( array_keys( $this->fields ) as $field )
      $values []= $this->fields[ $field ];

    $sth = $db->prepare( $sql );
    $db->execute( $sth, $values );

    $res = $db->query( "SELECT last_insert_id()" );
    $res->fetchInto( $row );
    $this->id = $row[0];
    return $row[0];
  }

  function update()
  {
    global $db;

    $sets = array();
    $values = array();
    foreach( array_keys( $this->fields ) as $field )
    {
      $sets []= $field.'=?';
      $values []= $this->fields[ $field ];
    }
    $set = join( ", ", $sets );
    $values []= $this->id;

$sql = 'UPDATE '.$this->table.' SET '.$set.
  ' WHERE '.$this->table.'_id=?';

    $sth = $db->prepare( $sql );
    $db->execute( $sth, $values );
  }

  function delete()
  {
    global $db;
    $sth = $db->prepare(
'DELETE FROM '.$this->table.' WHERE '.
$this->table.'_id=?'
    );
    $db->execute( $sth,
      array( $this->id ) );
  }

  function delete_all()
  {
    global $db;
    $sth = $db->prepare( 'DELETE FROM '.$this->table );
    $db->execute( $sth );
  }
}

class Book extends DBObject 
{
  function __construct()
  {
  parent::__construct( 'book',
    array( 'author', 'title', 'publisher' ) );
  }
}

$book = new Book( );
$book->delete_all();
$book->{'title'} = "PHP Hacks";
$book->{'author'} = "Jack Herrington";
$book->{'publisher'} = "O'Reilly";
$id = $book->insert();

echo ( "New book id = $id\n" );

$book->{'title'} = "Podcasting Hacks";
$book->update();

$book2 = new Book( );
$book2->load( $id );
echo( "Title = ".$book2->{'title'}."\n" );
$book2->delete( );
?>

														
												

底部的测试代码只演示了这个语法干净了多少。要得到图书的书名,只需得到 title 成员变量。这个变量会调用对象的 __get 方法,在散列表中查找 title 条目并返回。

现在就得到了单个动态的数据库访问类,它能够让自己适应到数据库中的任何表。





回页首


动态类的更多用途

编写动态类不仅限于数据库访问。请看清单 8 中的 Customer 对象这个例子。


清单 8. 简单的 Customer 对象
														
																<?php
class Customer
{
  private $name;

  function set_name( $value )
  {
    $this->name = $value;
  }

  function get_name()
  {
    return $this->name;
  }
}

$c1 = new Customer();
$c1->set_name( "Jack" );
$name = $c1->get_name();
echo( "name = $name\n" );
?>

														
												

这个对象足够简单。但是如果我想在每次检索或设置客户名称时都记录日志,会发生什么呢?我可以把这个对象包装在一个动态日志对象内,这个对象看起来像 Customer 对象,但是会把 getset 操作的通知发送给日志。清单 9 显示了这类包装器对象。


清单 9. 动态包装器对象
														
																<?php
class Customer
{
  private $name;

  function set_name( $value )
  {
    $this->name = $value;
  }

  function get_name()
  {
    return $this->name;
  }
}

class Logged
{
  private $obj;

  function __call( $method, $args )
  {
    echo( "$method( ".join( ",", $args )." )\n" );
return call_user_func_array(array(&$this->obj,
   $method), $args );
  }

  function __construct( $obj )
  {
    $this->obj = $obj;
  }
}

$c1 = new Logged( new Customer() );
$c1->set_name( "Jack" );
$name = $c1->get_name();
echo( "name = $name\n" );
?>

														
												

调用日志版本的 Customer 的代码看起来与前面相同,但是这时,对 Customer 对象的任何访问都被记入日志。清单 10 显示了运行这个日志版代码时输出的日志。


清单 10. 运行日志版对象
														
																% php log2.php
set_name( Jack )
get_name(  )
name = Jack
%

														
												

在这里,日志输出表明用参数 Jack 调用了set_name 方法。然后,调用 get_name 方法。最后,测试代码输出 get_name 调用的结果。





回页首


结束语

如果这个动态对象素材对您来说理解起来有点难,我不会责备您。因为我自己也花了不少时间研究它并使用代码才理解它并看出它的好处。

动态对象有许多功能,但是也有相当的风险。首先,在刚开始编写魔法方法时,类的复杂性显著增加。这些类更难理解、调试和维护。另外,因为集成开发环境(IDE)变得越来越智能,所以在处理动态类时它们也会遇到这类问题,因为当它们在类上查找方法时会找不到方法。

现在,并不是说应当避免编写这类代码。相反。我非常喜欢 PHP 的设计者这么有想法,把这些魔法方法包含在语言中,这样我们才能编写这类代码。但是重要的是,既要理解优点,也要理解不足。

当然,对于应用程序(例如数据库访问)来说,在这里介绍的技术 —— 与广泛流行的 Ruby on Rails 系统上使用的技术类似 —— 能够极大地减少用 PHP 实现数据库应用程序所需要的时间。节约时间总不是坏事。





回页首


参考资料

学习
posted @ 2006-04-28 16:02 崛起的程序员 阅读(204) | 评论 (0)编辑 收藏

年轻人买套套的经历

  顾客“老板套子怎么卖?”

  老板“10元一个”

  顾客“我先试试行么?”

  老板“还试什么呀?便宜点9元好了。”

  顾客“晕,这也叫便宜啊?”

  老板“好啦8元可以了吧。”

  顾客“......”

  老板“不会还嫌贵吧?”

  顾客“不是贵,是让我精尽人亡啊”

  老板“没那么夸张吧,看你是小朋友,7元好了”

  顾客“恩,差不多啦,可是我没那么多钱啊。”

  老板“啊?你有多少啊?”

  顾客“5元。”

  老板“天啊,晕死了,怎么也得再添1元啊!”

  顾客“我很想添,可是我的资金有限啊”

  老板“好啦,我认倒霉,5元成交”

  顾客“我不是要给你5元,我得留下2元做车”

  老板“不会吧,你不会做车来这里买这东西吧?”

  顾客“是啊我做11路来的而且还是回头客啊”

  老板“虽然以前没看过你,不过希望你以后再来,3元成交行了吧。”

  顾客(脸上带红)“可是我还没有对象”

  老板“啊?那你买这干什么啊?”

  顾客“没关系,如果你再便宜1元的话我就能找到了!”

  老板“¥%……¥%……%¥算你狠,2元行了吧!”

  顾客“等等,这个怎么有个洞啊?”

  老板“没有洞怎么用啊!?”

  顾客“怎么看起来像用过的啊?”

  老板“侮辱我可以但是不要侮辱我的套子,这绝对是新的。”

  顾客“哇,上面还没干呢啊,你骗我啊!”

  老板“啊!!不好意思嘿嘿……做生意吗,你要知道我每天的门面房租金上千呢,不然我吃什么,1元可以了吧”

  顾客“你这种行为严重的危害了我的健康并且深深的影响了我的心灵.......”

  老板“啊呀!这么严重啊,你别生气,我5毛卖给你行了吧。”

  顾客“好,开张发票来!”

  老板晕死!

十二个避孕套

  爸爸和13岁的儿子走进屈臣氏,路经放避孕套的货架。儿子问爸爸「这些一盒盒的是什

么?爸爸告诉儿子:「这些是避孕套,是用来进行安全的性行为用的。儿子U「啊~原来这些

便是避孕套,上性教育课老师曾提及过!........[但为什么这些要一盒入里面有三个的?

  爸爸:「嗯......这些是给大学生用的,星期五一个,星期六一个,星期日一........」

  儿子:「.......那么这些一盒六个的是谁用的?」

  爸爸:「嗯........这是研究生用的,星期五两个,星期六两个,星期日两个........」

  儿子:「.......那这些呢?」

  儿子拿起了一盒十二个装的。

  爸爸透了一下凉气,凄凄道:「那是给已婚人仕用的,一月份一个,二月份一个,三月份一个............」


彩色的保险套

  有一个人想尝试新奇的事,便跑到情趣商品店买彩色的保险套他看到两个彩色的套子,

一个是黑色的,一个外型像是米老鼠他决定买那个黑色的回家,并跟太太大战了几回合不过

那个套子并没发生什作用,后来他太太怀孕了经过九月之后生下小baby,再经过6年之后孩子

长大了这个小孩有一天问他老爸:“为什么哥哥姊姊的肤色都是白的而我却是黑的?”爸爸

回答道:“孩子,你没长得像米老鼠就该谢天谢地了”


IT避孕套

有一天软件工业一蹶不振,软件业三大巨头SUN,UNIX和微软都决定改做避孕套生意,

他们生产的避孕套分别命名为JAVA避孕套,X避孕套和MS避孕套。 一个使用JAVA避孕

套的顾客来到SUN公司投诉,说戴着不合适,SUN公司回答说要等国际标准组织(ISO)制定相

应的标准才行,并吹牛说那时他们生产的避孕套将适合每个男人,顾客只好转而使用X避孕

套。可他发现等他读完随套附上的说明书后,他的妻子已经睡着了,他自己也忘了为什么

要用X避孕套。最后,他只好换用MS避孕套。出乎他意料的是,MS避孕套非常好用,他很愉

快的连续使用了好几个月,突然发现他妻子怀孕了。他非常生气气势汹汹的找到微软公

司,微软的回答是:补丁马上就到!

posted @ 2006-04-28 13:46 崛起的程序员 阅读(554) | 评论 (1)编辑 收藏
Stripes 1.3 版本发布了.Stripes是一个视图框架用于利用最新的Java技术来构建Web应用程序.
文章来源:http://www.matrix.org.cn/resource/news/670_Stripes.html
posted @ 2006-04-28 11:29 崛起的程序员 阅读(211) | 评论 (0)编辑 收藏
JIRA 3.6 版本发布了.JIRA是一个优秀的问题(or bugs,task,improvement,new feature )跟踪及管理软件。
文章来源:http://www.matrix.org.cn/resource/news/671_JIRA.html
posted @ 2006-04-28 11:29 崛起的程序员 阅读(205) | 评论 (0)编辑 收藏
Apache MyFaces 1.1.2 版本发布了.MyFaces是JavaServer Faces(JSF) Web框架 (JSR 127)的一个实现。JavaServer Faces Web框架是一个新的实现MVC模式的规范.它可以与Struts框架相媲美甚至的一些特性与观念已经超过了Struts.
文章来源:http://www.matrix.org.cn/resource/news/672_Apache+MyFaces.html
posted @ 2006-04-28 11:29 崛起的程序员 阅读(219) | 评论 (0)编辑 收藏
Apple 发布了 Mac OS X Tiger 的更新包.for for Mac OS X 10.4.5 Tiger 用户.更新包增加了对Java 2 Platform Standard Edition 5.0的可靠性和兼容性.
文章来源:http://www.matrix.org.cn/resource/news/673_Apple+Tiger+Java.html
posted @ 2006-04-28 11:29 崛起的程序员 阅读(247) | 评论 (0)编辑 收藏
Apache FOP 0.92 beta 版本发布了.FOP是Apache计划所发展的一个开源的XSL-FO处理器项目,可以把Formatting Object格式的文件转换成 可列印文件,如PDF、PostScript等格式。
文章来源:http://www.matrix.org.cn/resource/news/674_Apache+FOP.html
posted @ 2006-04-28 11:29 崛起的程序员 阅读(285) | 评论 (0)编辑 收藏
ObjectWeb ASM 3.0 beta2 版本发布了.ASM是一套JAVA字节码生成架构。它可以动态生成二进制格式的stub类或其他代理类,或者在类被JAVA虚拟机装入内存之前,动态修改类。
文章来源:http://www.matrix.org.cn/resource/news/675_ObjectWeb+ASM.html
posted @ 2006-04-28 11:29 崛起的程序员 阅读(260) | 评论 (0)编辑 收藏
Apache WS Policy 宣布改名为 Apache Neethi.
文章来源:http://www.matrix.org.cn/resource/news/676_Apache+Neethi.html
posted @ 2006-04-28 11:29 崛起的程序员 阅读(283) | 评论 (0)编辑 收藏
Jofti 1.2 beta4 版本发布了.Jofti可对缓存层中的对象,及支持Map接口的存储结构中的对象进行标记与查询。
文章来源:http://www.matrix.org.cn/resource/news/677_Jofti.html
posted @ 2006-04-28 11:29 崛起的程序员 阅读(491) | 评论 (1)编辑 收藏
4月24日消息,据外电报道,甲骨文CEO拉里-艾里森上周曾表示,红帽收购JBoss之后,甲骨文和IBM都应该重新考虑各自的Linux策略,以及和这两家公司暮献鞴叵怠4撕蟊阌写懦疲琁BM可能收购SAP
文章来源:http://www.matrix.org.cn/resource/news/678_IBM+SAP.html
posted @ 2006-04-28 11:29 崛起的程序员 阅读(240) | 评论 (0)编辑 收藏
JSwat 3.10 版本发布了.JSwat是一个基于Netbean 平台的,单机可扩展可绘图的Java 调试前端。
文章来源:http://www.matrix.org.cn/resource/news/679_JSwat.html
posted @ 2006-04-28 11:29 崛起的程序员 阅读(235) | 评论 (0)编辑 收藏
Tomcat Probe 1.4 发布 版本发布了.同时, 需要注意的是: Tomcat Probe开始更名为LambdaProbe..
文章来源:http://www.matrix.org.cn/resource/news/680_LambdaProbe.html
posted @ 2006-04-28 11:29 崛起的程序员 阅读(304) | 评论 (0)编辑 收藏
Sun Microsystems周一在公布季度净亏损的同时,同时宣布首席执行官麦克尼利将下台。不过该公司季度营收则上升,受惠于合并Storage Technology。Sun的共同创办人,现年51岁的麦克尼利(Scott McNealy)辞去CEO之职,但仍将担任董事长。
文章来源:http://www.matrix.org.cn/resource/news/681_SUN+CEO+McNealy.html
posted @ 2006-04-28 11:29 崛起的程序员 阅读(142) | 评论 (0)编辑 收藏
《ajax实战》是目前Ajax领域最为全面深入的一本著作,其中不仅有对于基础知识的介绍,还有对于Ajax开发中重大的体系架构问题的深入探讨,总结了大量 Ajax开发中的设计模式,并讨论了框架、安全性与性能等等。cleverpig在第一手拿到了《ajax实战》试读版本,在阅读部分章节后为翻译者的无私努力感到敬畏和佩服,特作此书评提交给Matrix的Ajax技术拥护者...
文章来源:http://www.matrix.org.cn/resource/news/682_Ajax.html
posted @ 2006-04-28 11:29 崛起的程序员 阅读(222) | 评论 (0)编辑 收藏
JAG 6.0 版本发布了.JAG是一个可从数据库或UML模型创建复杂的可用的J2EE应用的GUI工具,它的目的是减轻开发者的重复工作。
文章来源:http://www.matrix.org.cn/resource/news/683_JAG+J2EE+GUI.html
posted @ 2006-04-28 11:29 崛起的程序员 阅读(250) | 评论 (0)编辑 收藏
JasperServer 0.9.2 版本发布了.JasperServer 是JasperSoft Corporation的产品之一.JasperServer 旨在提供一个关于JasperReports的web应用
文章来源:http://www.matrix.org.cn/resource/news/684_JasperServer.html
posted @ 2006-04-28 11:29 崛起的程序员 阅读(324) | 评论 (0)编辑 收藏
经过10个月的努力,ehcache1.2终于发布了,(恭喜恭喜),此次版本增加了很多呼吁强烈的特性:支持插拔式(pluggable),分布式缓存,对象缓存以及可序列化缓存,全新的缓存清除策略,事件监听器,等等 。
文章来源:http://www.matrix.org.cn/resource/news/686_ehcache.html
posted @ 2006-04-28 11:29 崛起的程序员 阅读(279) | 评论 (0)编辑 收藏
IBM alphaWorks 宣布 BPEL Repository 初始版本发布了.BPEL Repository 能够将BPEL business processes 以及其他相关的XML文件保存在一个文件系统里.
文章来源:http://www.matrix.org.cn/resource/news/688_BPEL+Repository.html
posted @ 2006-04-28 11:29 崛起的程序员 阅读(236) | 评论 (0)编辑 收藏
Java Parallel Processing Framework 0.15.0 版本发布了.Java Parallel Processing Framework 是一个基于JAVA的并行框架.
文章来源:http://www.matrix.org.cn/resource/news/689_Parallel+Processing.html
posted @ 2006-04-28 11:29 崛起的程序员 阅读(241) | 评论 (0)编辑 收藏
在最新的mustang build 81 的changelog中, 我们可以看到:多层编译(Tiered Compilation ) 已经完成了, 已经在new features 列表里了..
文章来源:http://www.matrix.org.cn/resource/news/690_Tiered+Compilation.html
posted @ 2006-04-28 11:29 崛起的程序员 阅读(255) | 评论 (0)编辑 收藏
对于不想学习新标签的(比如jodd 等)
仍用struts 等的朋友,
把那些 “bean write”扔掉吧
换为 jsp2.0 的el表达式吧
例如
<img src=“<bean:write name="o" property="picUrl"/>”>
看这些双引号我就恼火

换为下面的清楚多了
<img src=${o.picUrl}>

当然
这些也支持
<logic:iterate id="o" name="listVO">
。。。
</logic:iterate>
这之间的 bean ,看起来爽多了吧

这中间还有一点
例如 我在request 里面setAttribute(“o”,“123”);
如果用bean write
<bean:write name="o" /> ,晕 报错! 说没有o 对象
用 ${o.} ,呵呵,为“” ,正式我想要的,


慢慢的你会发现可以放弃strtus 的标签了
具体的 看  
springside 项目了啊 :)
posted @ 2006-04-28 11:22 崛起的程序员 阅读(3403) | 评论 (0)编辑 收藏
对于不想学习新标签的(比如jodd 等)
仍用struts 等的朋友,
把那些 “bean write”扔掉吧
换为 jsp2.0 的el表达式吧
例如
<img src=“<bean:write name="o" property="picUrl"/>”>
看这些双引号我就恼火

换为下面的清楚多了
<img src=${o.picUrl}>

当然
这些也支持
<logic:iterate id="o" name="listVO">
。。。
</logic:iterate>
这之间的 bean ,看起来爽多了吧

这中间还有一点
例如 我在request 里面setAttribute(“o”,“123”);
如果用bean write
<bean:write name="o" /> ,晕 报错! 说没有o 对象
用 ${o.} ,呵呵,为“” ,正式我想要的,


慢慢的你会发现可以放弃strtus 的标签了
具体的 看  
springside 项目了啊 :)
posted @ 2006-04-28 11:15 崛起的程序员 阅读(174) | 评论 (0)编辑 收藏
    “Java产生于网络”,这是几年前java程序员一直津津乐道的话题,伴随着互联网的蓬勃发展,Java也曾有一股气吞山河,一统天下之势。然而今天,我们看到PHP占领全球网站应用的大半市场,Microsoft的ASP及.net占领了很大一部份市场(看看国内的各种网站、看看中国的党政网),还有其它Web开发语言也占据了一部份,最后剩下给Java可谓寥寥无几。PHP正在以他深得人心的简易开发优势快速扩张,如今又冒出个新贵Rails/Ruby在后面耀武扬威宣称要替代Java、消灭Java。我们不禁感叹:Java路在何方?

   成也萧何,败也萧何。我们认为,造成Java今天之局面的一个主要因素是Java技术的极度灵活,Java技术领域的“有组织、无纪律”。动不动JCP就给你搞一个Specification,管你怎么去实现;Java开发社区TTS及sourceforge.net每天都有新的框架或开源产品发布,最终这些技术及产品到其使用者(软件企业及程序员)手里时,他们根本不知道该如何选择。你不跟潮流别人就会笑话你古董,而且最让java程序员烦恼的莫过于昨天才辛辛苦苦掌握并引以为豪的新技术、新框架,今天就被别人嘲笑你所学会的东西已经落伍、老掉牙。

   另外,Java技术的“强大功能”及其“复杂性”也是造成Java应用步履蹒跚,举步为艰的一个不可忽视的因素。功能的强大是以技术的复杂作为代价,一个功能强大的系统总是由众多简单的小系统堆积而成。我们以为:“简易、实用才是硬道理”!再强大的功能也要以实用为基础,脱离了实际应用便是空谈乌托邦。如果实际应用中不可能用到,那我们又何必花那么多的精力煞费苦心地去搞那么多的设计及构架,(又何必点灯熬油挥汗如雨地去建一个很难登上的空中楼阁呢?)纵观今天java技术的应用,复杂得有点离普。一个简单的Web应用动辄就要戴上N层高帽,Web使用Struts(JSF)、持久层用hibernate(JDO/Entity Bean)、还引入了Spring或EJB,要IOC、AOP、支持任意数据库、支持任意存诸方式、支持组件对象关系任意配置、支持Web Service……如此琳琅满目的技术,如此强大的功能,想不复杂?难啊!时至今日,又有谁能象当年IBM总裁郭士纳一样让Java这个庞大,复杂的大象也能跳跳舞呢?

   Java在国内不能得到很好应用的一个主要原因是国产Java技术落伍。在Java世界,开源、优秀的东西很多很多,但基本全是泊来之品。也只有那些技术牛人、大型软件企业、外资企业,才有实力研究并很好运用。当前还有众多的中小型软件企业停留在JSP+Java Bean的时代,很多中小企业甚至老牌软件企业的CTO或者项目经理到现在还常理直气壮的说:"客户不就是要求J2EE平台吗?我们的JSP、Tomcat、JBoss不全是J2EE技术吗?”是的,软件嘛,能解决问题就行,好不容易花了两三年搭建起来的技术体系,说换就换,哪有那么容易啊!于是,软件越来越难以维护,越来越难以扩充,更加无法满足客户的新需求。

   再看国内很多比较知名的软件企业提出的一些新开发构架、开发体系、中间件及工具等,其基础核心仍然是国外引进的东东。比如很多软件公司开发的快速开发工具,构件开发等,都是以Struts、Spring、Hibernate等为基础,也就是要用他们的工具,开发东西还得要学Struts、Hibernate等。对于国内很多的初级开发者来说,学习这些框架的难度曲线太大。技术本土化不仅仅是把一门语言翻译成另外一门语言,更重要是思想的本土化、核心内容的本土化。

  说到技术及框架,一个国外的先近技术框架,都是别人先尝试用了N久证明了它的先进以后,我们国内才有一部份人拿进来开始慢慢学习,然后再宣传普及,等我们大部分人刚刚学会并以此为荣,奔走相告的时候,人家又搞了一个更加先进的出来。汗颜啊,难道我们只能疲于奔命地被人家牵着鼻子走吗?

  。。。。。。
posted @ 2006-04-28 11:11 崛起的程序员 阅读(176) | 评论 (0)编辑 收藏

         在2003年百富榜的前十位中,陈天桥即使不是崛起速度最快的富豪,也是最年轻的富豪。1973年出生的陈天桥凭借《传奇》开创了网络游戏最辉煌的案例。几年来,他的成功伴随着中国网络游戏业的成功,他的困顿也伴随着中国网络游戏业的困顿。

  “外面的世界非常热闹”

  南方都市报:1999年11月份盛大开始创业的时候,为什么会选择网络娱乐作为发展方向?

  陈天桥:当时我们一共是5个创业者,我从证券公司辞职,带上大学的一帮朋友,我们在思考什么样的产业是未来有前途的产业。那时充斥整个互联网的是旅游、是电子商务、是PORTAL(门户网站)、甚至是拍卖,是所有在美国最时髦的MODEL。

  而我们认为网络娱乐有这样几个特点,第一是没有物流问题,电话线不能把冰箱传到你家里,却可以把图像、游戏、互动的音乐传到你家里。第二,网络一定会给这个产业带来真正的附加价值。比如说,我把报纸的内容搬上网站,同样通过报纸可以浏览到新闻,我为什么要额外地付费呢?而内容通过网络会真正给你带来互动,一起体会两个人、三个人甚至上万个人的快乐。第三,要有清晰的侧重点,我一直认为盛大是娱乐企业、文化企业,而不是IT企业。第四,文化产业有一个很大的特点:同一个内容可以通过不同的形式获得不同的附加值。比如说迪斯尼的米老鼠可以通过动画展示,可以有音乐,有主题公园,它可以通过品牌提供内容,最大程度挖掘品牌效果。我当时提出“一鱼四吃”,而在网络文化产业,存在着“一鱼四吃”的可能性。

  但在最早的一年半时间里,盛大也经历了寒冬期,我当时告诫我的员工一定要耐得住寂寞,当时很多网站在做自己的广告,说自己的浏览量有多少,当时我说中国人每个人个子有一米七到一米八,你在那边跳啊跳,说自己有一米九,跳的时候就会伤自己的精力。何不坐下来等待时机的成熟。所以我特别感谢陪伴我们的团队,因为那段时间里,外面的世界非常热闹。

  南方都市报:软银亚洲今年为盛大投了4000万美元,并称看中的不是《传奇》,而是这个运营团队。一个企业在创业阶段,是如何达到相互信任的?

  陈天桥:大企业有完善的制度,而在企业很小的时候,因为制度不完善,需要相互信任,领导要相信员工、员工也要相信领导是信任他的。对企业来讲,信任是成本最低的途径。无数次的信任累积、最后会当作企业文化固定下来。这是一种博弈,就像两个罪犯分别关起来,都知道交代后一定会被枪毙,但是两个人都信任对方,谁也不肯说,结果最后就撑了过来。

  “敌进我退,敌退我进”

  南方都市报:2001年前,盛大经历了一年半的寒冬期,是不是因为当时对形势的错误估计?

  陈天桥:我们曾经犯过一个非常大的错误。现在想起来也非常好笑。当时整个公司只有50个人,但是我们就有动画、产品、平面媒体等几个事业部。伴随着“.COM”破灭,我们“一鱼四吃”的梦想也破灭了。

  2001年我们开始醒悟,那时候不瞒你说我特别喜欢看的是毛主席军事理论观点,感受最深的是,要想获得解放、获得成功,非常重要的一点就是集中优势兵力,各个歼灭,寻找敌人最薄弱的环节突破。当时正好韩国的游戏协会进入中国,首先找的是中国动画协会,动画协会说盛大喜欢做网络与动画结合的内容,于是就推荐了他们到盛大来访问。说实在的,尽管我们和韩国人之间发生了一些纠纷,但我们很钦佩韩国企业,他们在特定时候集中优势兵力,尤其金融危机后,他们把宽带领域和内容领域作为未来发展的突破口,这也是盛大从他们身上学到的一个非常重要的经验。

  南方都市报:你刚才提到毛主席军事理论观点,这是不是对你的判断、决策起到很大的作用?

  陈天桥:我上复旦的时候要军训一年。当时我们去井冈山上拉练,到过宁冈和三湾。看到毛主席在失去个人权力的时候,写下了“星星之火,可以燎原”这些文章。实际上,毛主席的胸襟、军事思想、处理问题的方式方法和企业是完全一样的。比如说“敌进我退,敌退我进”就是一个很好的例子:动画在国外已经发展了二三十年,我们不会去和对手正面交锋。但我们又拥有最大的网络游戏市场,这是我们进攻的机会。

  “盛大要做规则制定者”

  南方都市报:在经营游戏之后,你曾经说过盛大两年之内不准有人谈研发,因为需要专注,“谁跟我谈研发就把他开除”。但现在盛大又做研发又做销售,你担不担心用户不喜欢三心二意的运营商?

  陈天桥:截至去年,《传奇》同时在线的用户已经超过了60万。我们深信光靠运营这个环节,光靠我们对用户的理解和把握,就能够成为“中国在线”。但产业的发展,有时会超过个人的预计。网络文化这个产业,销售、研发、运营三个环节的紧密度实际上超过任何传统产业,它不像电影一样,看一场就结束了,网络游戏是持续发展的虚拟社会,在这样一个产业里面,如果你只是服务者而不是规则制定者,实际上会处于非常尴尬的位置。如果有人不愿意成为你的用户,这种痛苦可能还是短暂的。但如果他愿意成为你的用户,而你却无法提供他所需要的内容,那种痛苦才更为深刻。

  “花花世界原来别有洞天”

  南方都市报:现在盛大要转型做迪斯尼这样的娱乐公司,而多元化是很多企业的滑铁卢,也有很多人持怀疑态度,觉得盛大又回到自己的老路上,而且摊子铺得太大。

  陈天桥:我觉得一个产业发展到一定阶段,专注和多元化的矛盾一定会摆在企业的面前。我常说“比尔·盖茨最聪明的地方不是他做了什么,而是他没做什么”。以比尔·盖茨的实力,他可以买下纽约,可以去做房地产,但他专注在自己的操作系统、软件研发,而不被市场中别的诱惑所吸引。对于刚起步的企业来说,他没有什么可失去的,起步之后,到了一定的阶段,就需要适度多元化来分散风险。所以这是我说的一个企业发展所要经历的五个阶段,一是战略上寻找突破点,二是要专注,三是要进行整个产业链的整合,四是适度多元化,五是变成社会企业,承担适度的社会责任。

  南方都市报:你曾经说过做运营是一种病态的执着,但大家都认为,你正是靠着执着才把盛大做起来的,这种说法是不是反映了你的一种矛盾心情?

  陈天桥:之所以说病态是因为“一朝被蛇咬,十年怕井绳”。我们当时犯过多元化的错误,但现在大规模地进入研发,就像1949年解放战争的时候,是坚守阵地不敢出击,还是“宜将胜勇追穷寇?”我们也打了三大战役:《传奇》、《传奇世界》、与韩国人的官司和解。盛大已经形成了兵团的规模,我们不仅要运营的盛大,还要打到长江以南去,做研发的盛大。所以我们强调适度的多元化,我们没有回过头去做其它业务,例如经营房地产或保健品,但现在已经到了过江的时候,应该勇猛出击了。

  南方都市报:这和你以前“盛大要过独木桥”的说法相比,态度好像更积极了?

  陈天桥:任何做法从被动到主动都是有过程的。盛大开始运营游戏的时候不做研发,只是帮用户处理服务中的一些问题。但在《传奇世界》成功以后,我们发现长江以南才是“花花世界”,开始的时候只是粮草不够了,偷偷到长江以南运点回来,后来才发现长江以南别有洞天。不过我们一直也是摸着石头过河,如果可以不摸石头“噔噔噔”就过河的话,那就是圣人,我们还没有到那个程度。

  “财富榜不过是网络游戏”

  南方都市报:像盛大这种情形,很短时间里迅速积累起财富,会不会对企业员工心态有所影响?

  陈天桥:对,我们自己也认为这个时间很短。但我认为我们受财富的影响是很小的。盛大从开始到现在,一定是有变化的,但不是财富带来的变化,而是企业思考问题的方式、作风、眼界发生了改变。比如说两年前,当有一台服务器死机,遇到黑客进攻的时候,我们会十分紧张。但是经历了技术上、经营上、政策上的种种风险之后,我们改变的是胸襟、魄力、处理问题的态度和能力。财富带给人的变化很少,我们不会在这上面沾沾自喜。

  南方都市报:最近胡润、福布斯两个富豪排行榜出来以后,为什么盛大采取不作评价的态度?

  陈天桥:理论上我们对此不作评价,因为这和我们企业的风格不符。我们不是特别在意财富,而是在意我们的团队、我们的事业。财富榜对我们来讲不过是网络游戏,大家玩一场下来看看,我打了15级、他打了21级、丁磊的级别最高,仅此而已。或者在游戏中你是大侠、他是强盗、我是普通老百姓,但在现实生活中可能正好相反。我们只是按照胡润或福布斯的游戏规划扮演了财富榜上的角色而已。实际上这个东西出来第一天我们看了一下,说“哦,是这样。”从第二天起大家就再也不谈这件事了。

posted @ 2006-04-28 09:43 崛起的程序员 阅读(204) | 评论 (0)编辑 收藏