定时执行任务的三种方法

1)java.util.Timer 这个方法应该是最常用的,不过这个方法需要手工启动你的任务: Timer timer=new Timer(); timer.schedule(new ListByDayTimerTask(),10000,86400000); 这里的ListByDayTimerTask类必须extends TimerTask里面的run()方法。 2)ServletContextListener 这个方法在web容器环境比较方便,这样,在web server启动后就可以 自动运行该任务,不需要手工操作。 将ListByDayListener implements ServletContextListener接口,在 contextInitialized方法中加入启动Timer的代码,在contextDestroyed 方法中加入cancel该Timer的代码;然后在web.xml中,加入listener: <listener> <listener-class>com.qq.customer.ListByDayListener</listener-class> </listener> 3)org.springframework.scheduling.timer.ScheduledTimerTask 如果你用spring,那么你不需要写Timer类了,在schedulingContext-timer .xml中加入下面的内容就可以了: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="timer" class="org.springframework.scheduling.timer.TimerFactoryBean"> <property name="scheduledTimerTasks"> <list> <ref local="MyTimeTask1"/> </list> </property> </bean> <bean id="MyTimeTask" class="com.qq.timer.ListByDayTimerTask"/> <bean id="MyTimeTask1" class="org.springframework.scheduling.timer.ScheduledTimerTask"> <property name="timerTask"> <ref bean="MyTimeTask"/> </property> <property name="delay"> <value>10000</value> </property> <property name="period"> <value>86400000</value> </property> </bean> </beans> <淘宝热门商品:
 

98.00 元 

莎莎调配速效去皱眼霜15g 淘宝眼霜销量冠军

 

145.00 元 

八心八箭瑞士钻石六爪旁镶钻经典钻戒指

来源:程序员网

小小豆叮

Ajax 陷阱(Ajax Gotchas)

Ajax陷阱 Ajax is an awesome technology that is driving a new generation of web apps, from maps.google.com to colr.org to backpackit.com. But Ajax is also a dangerous technology for web developers, its power introduces a huge amount of UI problems as well as server side state problems and server load problems. Ajax是一项重要的技术,它将引领下一代web应用程序开发的风潮,从maps.google.com 到 colr.org 再到 backpackit.com ,处处充满了ajax的倩影。但是对于web开发者来说,ajax也是一项危险的技术,它的力量也导致了一大堆UI问题,如服务器端状态问题和服务器负载问题。 I've compiled a list of the many mistakes developers using Ajax often make. Javascript itself is a dangerous UI technology, but I've tried to keep the list to problems particular to Ajax development: 我编辑了一个开发者在使用ajax时经常犯的错误列表。Javascript本身就是一个危险的UI技术,但是我仍尽量使得这份列表只包含ajax开发中所遇到的问题,而剔除掉Javascript本身的问题。 Mistakes: 错误: Not giving immediate visual cues for clicking widgets. If something I'm clicking on is triggering Ajax actions, you have to give me a visual cue that something is going on. An example of this is GMail loading button that is in the top right. Whenever I do something in GMail, a little red box in the top right indicates that the page is loading, to make up for the fact that Ajax doesn't trigger the normal web UI for new page loading. 没有及时给出点击控件后的可视化提示信息。如果我点击的这个东西触发了ajax一个动作,那么你必须给我一个可视化的提示,告诉我有些动作正在执行。一个例子就是Gmail页面右上角的装载按钮(loading button)。无论何时,当我在Gmail中执行任何操作时,页面右上角的小红色按钮就会提示我,页面正在载入中。这样明显的向用户提示出你的动作,从而克服ajax不再重载页面而带来的用户不知操作是否进行的不便。 Coofucoo say: 习惯是可以培养的。就像用户用惯windows时,就会习惯那个“开始”按钮一样,尽管刚开始他们会问:“为什么我想关机,还要从‘开始’处开始呢?”。当大多数web用户习惯于页面刷新即意味着数据提交这一传统观念时,一旦你打破这种观念,你就一定要小心,尽量多的提示用户你当前的操作,用户就不至于感到莫名其妙,“到底这个该死的程序在干些什么??!!”。反面例子你可以在球球的blog中找到。 Breaking the back button The back button is a great feature of standard web site user interfaces. Unfortunately, the back button doesn't mesh very well with Javascript. Keeping back button functionality is a major reason not to go with a pure Javascript web app. Coofucoo say: 这是硬伤,似乎无解。我目前想到的解决办法就是在程序里自己提供一个后退按钮,来代替浏览器提供的按钮。不过这样恐怕又会加大工作量。 破坏后退按钮的功能。后退按钮时标准web用户界面的一个很重要的功能。但是不幸的是,后退按钮的功能与Javascript结合的并不好。保持后退按钮的功能也正是我们不采用纯粹Javascript web应用程序的原因。 Changing state with links (GET requests) As I've referenced in a previous posting, Ajax applications introduce lots of problems for users who assume GET operations don't change state. Not only do state changing links cause problems for robots, users who are accustomed to having links drive navigation can become confused when links are used to drive application state changes. 竟然用链接来转换状态?(GET方式发送请求)就像我前面提到的一样,对于一些认为GET操作不会变更状态的用户来说,ajax应用程序导致了许多问题。那些可以改变状态的链接不仅仅导致web应用程序使用习惯的变化,更容易使那些习惯的认为链接就是起导航最用的用户感到很困惑。 Coofucoo say: 感觉这个地方不好理解,解释一下。超级链接最原始的功能就是在页面之间进行切换,你由一个页面点击一个超链接,就会被带到另一个地方。之后超链接有所发展,可以采用GET方法向服务器发送一些数据,不过有些用户还是会认为超链接的功能就是起导航的作用。但是现在情况变化了,在ajax里,由于大量引入了Javascript事件,所以一个普通的超链接同样可以起到原先button才可以起到的作用,比如asp.net中的linkbutton。这样也许会功能更强大,但是却给部分用户带来了习惯和观念上的变更,就会引入问题。 Blinking and changing parts of the page unexpectedly The first A in Ajax stands for asynchronous. The problem with asynchronous messages is that they can be quite confusing when they are pop in unexpectedly. Asynchronous page changes should only ever occur in narrowly defined places and should be used judiciously, flashing and blinking in messages in areas I don't want to concentrate on harkens back to days of the html blink tag. 页面的某部分可能出乎意料的,一眨眼的就变化了。Ajax这组缩写字母中的第一个字母“A”就代表了“异步(asynchronous)”。异步的信息所带来的问题就是他们会悄无声息的执行,毫无征兆的突然弹出,让人感到非常迷惑。异步页面的变化部分应该被谨慎设计,明确说明,明智使用,提示的信息应该被放置在那些我不会全神贯注的地方,并且用例如blink标记这种技术让他闪烁起来。 Not using links I can pass to friends or bookmark Another great feature of websites is that I can pass URLs to other people and they can see the same thing that I'm seeing. I can also bookmark an index into my site navigation and come back to it later. Javascript, and thus Ajax applications, can cause huge problems for this model of use. Since the Javascript is dynamically generating the page instead of the server, the URL is cut out of the loop and can no longer be used as an index into navigation. This is a very unfortunate feature to lose, many Ajax webapps thoughtfully include specially constructed permalinks for this exact reason. 超链接变得毫无意义,不能在传递给朋友或者收藏了。另一个web站点的重要功能就是你可以将一个URL传递给另一个人,而他就可以通过这个URL看到你看到的东西。当然,我也可以将这个URL放到我的收藏夹里,以便我以后访问他。Javascript,包括基于Javascript的ajax技术将会对这种应用模式带来巨大的挑战。由于Javascript取代了服务器,用来动态的产生数据,所以URL就不能具体代表整个交互循环的某个状态点,也就不再能够收藏了。这方面的功能缺失非常令人遗憾,许多ajax web应用程序在这方面考虑的非常仔细,包括专门构造一个特殊的链接(permalinks)来应对此种情况。 Too much code makes the browser slow Ajax introduces a way to make much more interesting javascript applications, unfortunately interesting often means more code running. More code running means more work for the browser, which means that for some javascript intensive websites, especially poorly coded ones, you need to have a powerful CPU to keep the functionality zippy. The CPU problem has actually been a limit on javascript functionality in the past, and just because computers have gotten faster doesn't mean the problem has disappeared. 太多的代码让浏览器变得缓慢。Ajax带来了一种非常有趣的javascript应用程序,但是不幸的是,有趣也就意味着更多的代码在运行。更多的代码就意味着浏览器要做更多的工作,特别是有些不良的代码虽然加强了功能,却需要你有一个强劲的CPU去支持这些功能的实现。CPU不够强劲实际上是过去限制javascript功能的一个重要因素,而且即使当下的计算机已经变得越来越快,这个问题也并没有完全消失。 Inventing new UI conventions A major mistake that is easy to make with Ajax is: 'click on this non obvious thing to drive this other non obvious result'. Sure, users who use an application for a while may learn that if you click and hold down the mouse on this div that you can then drag it and permanently move it to this other place, but since that's not in the common user experience, you increase the time and difficulty of learning your application, which is a major negative for any application. 创造新的UI使用习惯。使用ajax很容易犯这样的错误:“想当然的要求用户去点击一个不明显的东西,然后再一个不明显的地方将结果返回给用户”。诚然,当一个用户使用你的程序一段时间,他就可以了解到,如果他将鼠标在一块区域上点击,并且保持按下的状态进行拖拽,就可以将这块区域永久的移动到另一个位置。但是这些经验并不是每个用户天生就知道的,如果你这么做,那么你就为你的用户学习使用你的应用程序增加了难度和时间,而这样的情况对于大多数应用程序来说都有极大的负面影响。 Not cascading local changes to other parts of the page Since Ajax/Javascript gives you such specific control over page content, it's easy to get too focused on a single area of content and miss the overall integrated picture. An example of this is the Backpackit title. If you change a Backpackit page title, they immediately replace the title, they even remember to replace the title on the right, but they don't replace the head title tag with the new page title. With Ajax you have to think about the whole picture even with localized changes. 当局部发生变化时,可能需要成串同步页面其他部分。从Ajax/Javascript赋予你控制(更改)页面具体内容的时候,就特别容易犯下只注重局部数据变化,而忘记在整个页面同步该变化的错误。一个例子就是Backpackit(www.backpackit.com)的标题。如果你执行了一个改变Backpackit页面的动作,标题会被立刻改变过来,甚至右边的标题也会被一起变化,但是他们忘记了为新页面同步位于<head>标签中的<title>标记。在ajax里,当你更改局部时,你需要考虑整个页面的同步问题。 Asynchronously performing batch operations Sure with Ajax you can make edits to a lot of form fields happen immediately, but that can cause a lot of problems. For example if I check off a lot of check boxes that are each sent asynchronously to the server, I lose my ability to keep track of the overall state of checkbox changes and the flood of checkbox change indications will be annoying and disconcerting. 分批异步执行操作带来的问题。确实,在ajax里你可以编辑许多不同的表单,并且表单会及时提交,但是这样也带来了许多问题。例如,我编辑了一些一些复选框,每个都可以异步的单独将数据提交给服务器,但是同时我却丧失了在整体上控制这些复选框状态的能力,并且每个复选框提交之后返回结果的提示信息也会变得非常令人讨厌。 Scrolling the page and making me lose my place Another problem with popping text into a running page is that it can effect the page scroll. I may be happily reading an article or paging through a long list, and an asynchronous javascript request will decide to cut out a paragraph way above where I'm reading, cutting my reading flow off. This is obviously annoying and it wastes my time trying to figure out my place. 另一个问题是,即时向一个正在运行的页面写入内容可能会导致页面滚动。我可能会比较喜欢阅读一篇全部显示出来的文章,尽管文章可能会很长。可是一个异步的javascript程序也许会将我所读过的部分丢掉,从而影响我的阅读。当我试图再次找回我之前阅读的位置时,这种机制会显得特别不方便。 In addition, some programming issues: 另外,还有些编程的问题: Be careful about what you expose on the server, especially if you're using remote-stub frameworks like SAJAX. You can't necessarily let Javascript use generic services that you might let a server-side script call. Don't rely on a global XMLHttpRequest object. See Call Tracking. Coofucoo say: 这个地方我没有翻译出来,对他描述的情况知道得不是很确切。哪位知道得拜托通知一下。 Be careful about changing the DOM in one call, such that when a later call returns, it ends up writing a value into the wrong DOM object - or perhaps the DOM object doesn't return at all (To be added to [[Call Tracking). 当你响应一个调用而更改DOM的内容时,一定要小心,如果此时稍后的一个调用返回,很可能会导致将数据写入错误的DOM对象中,或者可能导致对DOM对象的操作根本就不返回。 Consider the effects of a call never returning. You can establish a timeout mechanism by setting a timer as soon as the call is made (with Javascript's ontimeout function). The timer can explicitly call the request's abort() method if no return has occurred, and inform the user accordingly. 考虑调用没有返回的情况。你可以通过在函数调用后设置一个timer(用Javascript的ontimeout函数)来建立一个超时机制。如果没有返回的话,这个timer就可以明确地执行abort()(终止操作),并且通知用户。 Consider the effects of a call returning with an error status - perhaps alert the user somehow. A pattern like Synchronisation Status helps here, to alert the user that some data is stale. 考虑调用返回一个错误状态的情况,这种情况下可能会提示用户一些莫名其妙的信息。像Synchronisation Status这样的模式就可以在这里为我们提供帮助,警告用户有些数据已经出问题了。 Retrieved from "http://www.ajaxpatterns.org/Ajax_Gotchas" 引用地址:"http://www.ajaxpatterns.org/Ajax_Gotchas" <淘宝热门商品:
 

 

皇冠舒友阁 顶级特效美容护肤 效果才是硬道理

 

17.80 元  

居家家--18个月上四皇冠,家居类信用最高卖家

简家 四皇冠信用 鲸鱼USB暖手鼠标垫D4063 冬天上网必备

来源:程序员网

小小豆叮

SQL谎言

我们开发的是一套大型门户系统,因为是Internet访问的,所以,Statement会出现安全问题,这个在 http://blog.csdn.net/hongbo781202/archive/2005/09/19/485092.aspx里面已经讨论过了,然后我抄一段ORACLE电子杂志的话给大家看看:“除了缓冲的问题之外,至少还有一个更好的原因使我们在企业应用程序中更喜欢使用 PreparedStatement对象,那就是安全性。传递给PreparedStatement对象的参数可以被强制进行类型转换,使开发人员可以确保在插入或查询数据时与底层的数据库格式匹配。 当处理公共Web站点上的用户传来的数据的时候,安全性的问题就变得极为重要。传递给 PreparedStatement的字符串参数会自动被驱动器忽略。最简单的情况下,这就意味着当你的程序试着将字符串“DAngelo”插入到 VARCHAR2中时,该语句将不会识别第一个“,”,从而导致悲惨的失败。几乎很少有必要创建你自己的字符串忽略代码。 在Web环境中,有恶意的用户会利用那些设计不完善的、不能正确处理字符串的应用程序。特别是在公共Web站点上,在没有首先通过PreparedStatement对象处理的情况下,所有的用户输入都不应该传递给SQL语句。此外,在用户有机会修改SQL语句的地方,如HTML的隐藏区域或一个查询字符串上,SQL语句都不应该被显示出来。”。这段话和我们群里面的讨论一模一样。 关于性能问题,再给大家看段话:“通常认为PreparedStatement 对象比Statement对象更有效,特别是如果带有不同参数的同一SQL语句被多次执行的时候。PreparedStatement对象允许数据库预编译SQL语句,这样在随后的运行中可以节省时间并增加代码的可读性。 然而,在Oracle环境中,开发人员实际上有更大的灵活性。当使用 Statement或PreparedStatement对象时,Oracle数据库会缓存SQL语句以便以后使用。在一些情况下,由于驱动器自身需要额外的处理和在Java应用程序和Oracle服务器间增加的网络活动,执行PreparedStatement对象实际上会花更长的时间。” 实际上,我的测试结果是:在同一SQL执行5次的情况下,PrepareStatement比Statement要慢3%. 我们再看看http://www.oreilly.com/catalog/jorajdbc/chapter/ch19.html 里面有详细的图表说明为什么Statement比PreparedStatement快。结论是:一个prepared statement要执行65次以上才能赶上一个普通statement的执行效率。 另外一个问题就是Index的使用,基本的原则就是: 如果是多列Index,一般应该保证这几个列都在查询条件中。而且对于单列Index,只有满足查询出来的结果命中率在20%以下,使用索引会才会加快速度! 否则可能会越Index越慢哦! 根据经验看,上面的结论对Oracle,Sybase,Mysql,Informix都成立,好可怕啊,不知道我前两年的程序怎么做的,原来我前两年都是在谎言的边缘度过! <淘宝热门商品:
 

24.00 元  

輝縂、QQ业務连鎖店

官方秒开QQ会员QQ紫钻QQ黄钻QQ红钻QQ蓝钻QQ粉钻8元/月

 

18.50 元 

出口韩国 棉质弹性不透内搭显瘦踩脚裤/打底裤

来源:程序员网

小小豆叮

Java设计模式之计数代理模式

计数代理模式在客户对象调用服务提供者对象上方法的前后执行诸如日志(logging)和计数(counting)一系列附加功能时很有用。计数代理模式建议把这些附加功能封装在一个单独的对象,这个对象就是指计数代理对象,而不是把这些附加的功能实现放到服务提供者的内部。良好的对象设计的一个特征就是对象要专注于提供特定的功能。换句话说,理想的对象不应该做各种不相干的事情。把诸如日志(logging)和计数(counting)等类似的功能封装为一个单独的对象,而让服务提供者对象仅提供它自己的特定功能。也就是说,只允许服务提供者对象执行定义良好、特定的任务。   计数代理被设计成可以被客户访问的与服务提供者具有相同接口的对象。客户对象不是直接访问服务提供者,而是调用计数代理对象上的方法,计数代理执行必要的纪录日志(logging)和计数(counting)功能后,再把方法调用传递给服务提供着对象。如图1 Figure1: Generic Class Association When the Counting Proxy Pattern Is Applied   下面的例子说明了如何在应用程序中利用计数代理。   例子:   让我们设计一个Order类,类层次如图2,OrderIF接口声明了getAllOrders读取数据库中所有订单的简单方法。 Figure2: Order Class Hierarchy public interface OrderIF { public Vector getAllOrders(); }   作为getAllOrders方法实现的一部分,Order类实用了FileUtil工具类从order.txt文件中读取订单项。 public class Order implements OrderIF {  public Vector getAllOrders() {   FileUtil fileUtil = new FileUtil();   Vector v = fileUtil.fileToVector("orders.txt");   return v;  } }   让我们假定在调用getAllOrders()时,需要把取数据文件所花费的时间和记录条数要记录的log日志文件中。   这个附加的功能可以设计一个单独的OrderProxy类来实现,它与真实对象Order一样实现OrderIF接口。这样保证了OrderProxy对象提供给客户与真实对象Order一样的接口。如图3 Figure3: Order Class Hierarchy with the Counting Proxy public class OrderProxy implements OrderIF {  private int counter = 0;  public Vector getAllOrders() {   Order order = new Order();   counter++;   long t1 = System.currentTimeMillis ();   Vector v = order.getAllOrders();   long t2 = System.currentTimeMillis();   long timeDiff = t2 ? t1;   String msg = "Iteration=" + counter + "::Time=" + timeDiff + "ms";   //log the message   FileUtil fileUtil = new FileUtil();   fileUtil.writeToFile("log.txt”,msg, true, true);   return v;  } }   客户对象MainApp就想调用真实对象Order一样调用OrderProxy对象上的getAllOrders()方法,OrderProxy对象传递这个调用给真实对象Order,计算读取所有订单所花费的时间并使用FileUtil帮助类将其纪录的log日志文件中。在这个过程中,OrderProxy扮演者计数代理的角色。 public class MainApp {  public static void main(String[] args) {   OrderIF order = new OrderProxy();   Vector v = order.getAllOrders();   v = order.getAllOrders();   v = order.getAllOrders();   v = order.getAllOrders();  } } <淘宝热门商品:
 

0.70 元  

滋补品专门店/雪蛤油 燕窝 低价运作拒绝暴利,令美丽不再昂贵!

 

¥:78.00 

皇冠舒友阁 顶级特效美容护肤 效果才是硬道理

皇冠热销 印疤修复液 消除深浅痘印等色印、有效改善凹凸疤痕

来源:程序员网

小小豆叮

手机报纸略探

从2001年《扬子晚报》推出的短信版“扬子随身看”到2004年《中国妇女报》推出的中国第一份图文并茂的彩信版手机报纸,再到最近由南京聚点科技有限公司推出的全文版的掌信手机报,中国的手机报纸发展已经走过了四年的风雨历程。可手机报纸到底是什么?笔者在这里尝试给其下个定义: 手机报纸就是具有独特风格的,以手机等移动终端为载体,将电子信息、服务资讯传递给用户的一种媒体,其形式上应适合手机屏幕,手机的操作、阅读习惯,内容上注重个性化,即时互动性,并提供一些特色资讯服务。 定义解释: 1、具有独特风格 手机报纸首先是一份报纸,一个媒体,不是大杂烩,更不是SP业务,应有自己明确的定位,针对何种人群,提供哪些内容,特点是什么,只有回答好这些问题,才能形成独特的风格。 2、适应性 手机品牌,型号多种多样,千差万别。其差别主要表现在屏幕、操作习惯的不同。 (1)、手机屏幕 尺寸的大小、分辨率高低直接影响了图片文字的视觉效果,其中尺寸的影响较大。解决办法:一是给不同尺寸的手机发送不同大小的图片,技术要求低,实际操作复杂,少数大型的彩信业务商采用此法;二是在手机上安装相应程序,通过程序调整显示效果,达到最佳效果,技术要求高,实际操作简单,多数游戏和聚点掌信手机报纸采用此法。当然了,还有一种办法,就是牺牲用户利益,用较小尺寸的图片适应所有的手机。这是现今多数彩信商的做法。 (2)、手机操作 手机的操作取决于手机本身所采用的操作系统。和PC市场不同,现在主流的手机操作系统有很多,其中主要有四种:Symbian,Windows Mobile,PALM,Linux。四大操作系统相对独立,标准不相统一。针对操作系统的适应工作是个难点,目前只有针对不同系统开发相应的程序,除非你是运营商,强制各手机厂商统一。当然,在某一系统上,还是可以做很多工作的,例如调用系统程序,增加快捷键,让用户使用更方便。 3、阅读习惯 关于用户阅读习惯分为二种:一种和手机技术相关。一种和个人用户相关。作为用户,是否接受在手机上阅读报纸这一形式?手机报纸改变的是信息传递的载体,传递的实质还是信息。首先,不能要求让所有的人都接受。有的人习惯是非常难以改变的。其次,要让更多的人体验,通过一些宣传、活动使其认知,享受阅读手机报纸的乐趣,从而养成新的习惯。第三还需要从内容、形式上作些改变,不能简单的将纸报的内容搬到手机上,每篇信息的字数不能超过500字;第四内容可以查询,是一个随身的资料库。形式上可以采用文字、图片、FLSH、音频,增加活跃的元素,以形式的不断创新来吸引更多的人使用。 4、个性化 手机是个人用品,具有完全的个性特征,是个性的展示,是隐私的空间。在手机上搭载的信息,应针对不同人的爱好,可自我选择,包括内容、版式、色彩、背景。必须充分尊重个人稳私,一方面保护用户的注册资料安全,一方面不应向用户传递其反感的信息,即使是广告,也应有针对性,用户可自我选择。 5、即时互动 对于手机报纸的实时性,从技术而言,由于手机本身特性,实现起来,没有任何问题。关键在于手机报纸从业者的经营管理水平,能否即时提供最新信息。而互动的形式多种多样,有用户之间的互动,用户和报纸栏目的互动,中心在于通过互动让用户参与,能发表自己的意见,从而紧紧抓住用户的心。 6、特色服务 手机报纸除了提供大众新闻之外,还可以提供更多的生活资讯,为用户的吃、穿、住、行提供帮助,提供的服务不宜太多,在于精。将你的服务建立数据库,并提供查询系统,让用户能方便、快捷的查到想要的资料,得到相应的服务。 <淘宝热门商品:
 

50.00 元 

黑头粉刺净 扫光黑头粉刺顽痘 卖疯了

 

¥:10.00 

【北京商盟】风の轩杂货铺●小美贵族瘦身●

来源:程序员网

小小豆叮

教你用JAVA ID生成器去生成逻辑主键

在一个数据库设计里,假如使用了逻辑主键,那么你一般都需要一个ID生成器去生成逻辑主键。 在许多数据库里面,都提供了ID生成的机制,如Oracle中的sequence,MSSQL中的identity,可惜这些方法各种数据库都不同的,所以很多人愿意找寻一种通用的方式。 编写代码,1、2、3……这样一直累加是最直接的想法,JAVA用以下方式去实现 private static AtomicInteger uniqueId = new AtomicInteger(0); public static String nextId() { return Integer.toString(uniqueId.incrementAndGet()); } 当然,这样太简单了,并且一重新启动,计数器就归 0 了,一般的做法可以用 时间 + 计数器 的方式, private static final long ONE_STEP = 10; private static final long BASE = 1129703383453l; private static final Lock LOCK = new ReentrantLock(); private static long lastTime = System.currentTimeMillis(); private static short lastCount = 0; /** * a time (as returned by {@link System#currentTimeMillis()}) at which * the VM that this UID was generated in was alive * @serial */ private final long time; /** * 16-bit number to distinguish UID instances created * in the same VM with the same time value * @serial */ private final short count; /** * Generates a UID that is unique over time with * respect to the host that it was generated on. */ public UID() { LOCK.lock(); try { if (lastCount == ONE_STEP) { boolean done = false; while (!done) { long now = System.currentTimeMillis(); if (now == lastTime) { // pause for a second to wait for time to change try { Thread.currentThread().sleep(1); } catch (java.lang.InterruptedException e) { } // ignore exception continue; } else { lastTime = now; lastCount = 0; done = true; } } } time = lastTime; count = lastCount++; } finally { LOCK.unlock(); } } 在一个群集的环境里面,通常还需要加上IP的前缀,即 IP + 时间 + 计数器,这个就是JAVA原版本的实现了。 <淘宝热门商品:
 

32.00 元  

卖疯了 牙亮白 7天显著洁白牙齿

 

67.00 

【漂亮宝贝】童装打底裤名牌男童裤子背心T恤公主裙

X2-82103青蛙皇子童装130-155CM女中童T恤背心短裤两件套238

来源:程序员网

小小豆叮

JDO技术分析及如何进入企业应用的研究

JDO(Java Data Object)是JCP中较早开发出来并形成规范的JSR12,该规范对数据的持久化存储进行了一系列规范,并已有众多的商业产品和开源项目是基于该规范。作为一种需要引起重视的技术,研究并探讨其企业应用可行性是十分重要的。 前言 在企业级的应用开发中,常需要有良好的持久化技术来支持数据存储。通过良好的规范或API,将企业的领域业务对象进行持久化存储,大多采用O/R映射技术来进行模式化的数据转换及自动映射工作。 JDO(Java Data Object)是JCP中较早开发出来并形成规范的JSR12,该规范对数据的持久化存储进行了一系列规范,并已有众多的商业产品和开源项目是基于该规范。作为一种需要引起重视的技术,研究并探讨其企业应用可行性是十分重要的。 以下主要对JDO(JDO 1.0规范)的应用开发技术作扼要介绍,通过该文,可以由浅入深、并较为全面地了解JDO,掌握主要的技术细节及过程,理解其运行机制,并对企业级应用有个总体的把握,这将有助于企业应用软件的技术选型、体系架构及分析设计活动。 该文适合企业应用架构师、及关心数据持久层设计开发人员。 JDO基本思想及特点 企业信息系统的一个重要问题是解决数据的存储,即持久化。在软件开发过程中,分析员分析领域业务,提取出领域业务模型,并对应设计出数据库中需要进行存储业务数据的数据库表及相应字段。 并根据业务流程,设计业务处理逻辑单元,进行数据的加工、处理及存储、查询等业务。其中一个较为繁烦、枯燥的工作,就是处理大量的数据持久化代码。 为了解决数据从业务对象层向数据存储层之间的转换工作,JDO提供了相应的开发规范及API,解决了由Java对象直接存储为数据库相应表的底层处理过程,有助于设计人员更加专注于面向业务流程、面向业务对象等较高层次的应用。 由于采用JDO的映射机制,能降低了业务系统与数据存储系统的耦合,使得业务系统相对于关系数据库或对象型数据库,具有可移植性,同时,由于采用面向对象(而非传统的面向记录)的持久化技术,系统更为轻便、简洁,增强了可维护性。 JDO应用示例及分析 以下将通过一些示例,由浅及深地讲解JDO技术。 临时对象与持久对象 这是一个普通的业务对象的代码。 package business.model; public class Book { private String isbn; private String name; private Date publishDate; public void setISBN(String isbn) { this.isbn = isbn; } public String getISBN() { return this.isbn; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } public void setPublishDate(Date pubDate) { this.publishDate = pubDate; } public Date getPublishDate() { return this.publishDate; } } 现在将它作为一个JDO中对象保存到数据库中。代码如下: Book book = new Book(); book.setISBN(“isbn-1234567”); book.setName(“Java设计模式”); PersistenceManager manager = persistenceManagerFactory. getPersistenceManager(); manager.currentTransaction().begin(); manager.makePersistence(book); manager.currentTransaction().commit(); Book类的实例book对JDO的API而言,就是一个持久对象。类Book是可持久类。那任何一个普通java类都是JDO的可持久类吗?不是的。只有具备以下的条件,一个对象才可以被JDO持久到数据库中。 它所属类应标记为可持久的类,有以下两种方法: 显式:实现接口,javax.jdo.PersistenceCapable即可; 隐式:以Sun的JDO参考实现为例,Book.java类的相同路径下还须有Book.jdo文件。 并通过字节码增强工具(本例采用Sun的字节码增强工具)处理: javac Book.java java com.sun.jdori.enhancer.Main Book.class Book.jdo 通过上述两种方法,获得的Book.class才是一个可持久的类。 字节码增强的有如下功能:当应用程序通过set方法修改某个字段1时,由于通过增强过程,在其内部插入了某些代码,JDO会获得数据状态变化的信息,从而在持久过程中,进行有选择性的处理。 按照JDO规范,增强后的类可以在不同的JDO实现上使用,而无需重新编译或增强。 并不是所有Book对象都是持久对象,只有当makePersistence后,该对象才是持久对象,并会通过JDO实现存储到数据库中。通过JDO的供应商扩展标记符(vendor-extension),可详细描述Book类的存储特性,如为该可持久类指定数据库表和对应字段。 持久对象查询 JDO查询主要有以下两种方式。 使用Extend查询 Extend可以查询指定类及子类的持久对象。 PersistenceManager manager = persistenceManagerFactory. getPersistenceManager(); manager.currentTransaction().begin(); Extend extend = manager.getExtend(Book.class,true); //true表明同时查询子类 Iterator it = extend.iterator(); while(it.hasNext()) { Book book = (Book)it.next(); System.out.println(book.getISBN()); } extend.closeAll(); manager.currentTransaction().commit(); Extend查询方法,提供了一种基于类的查询途径,它可以与下面的Query构成更为强大的查询。 使用Query查询 Query可以指定过滤条件,是一种常用的查询方式。 下例是查找条件为“书名以‘Java设计模式’开头且出版日期小于今天”的书籍。 String filter = “((String)name).startsWith(\”Java设计模式\”) && publishDate < today”; Query query = pm.getQuery(Book.class,filter); query.declareImports(“import java.util.Date”); query.declareParameters(“Date today); Date today = new Date(); results = (Collection) query.execute(today); //传入参数值today if (results.isEmpty()) { System.out.println(“No data!”); }else{ Iterator it = results.iterator(); while(it.hasNext()) { Book book = (Book)it.next(); System.out.println (“Book Name:” + book.getName() + “, ISBN:” + book.getISBN()); } } 注:该条件使用了一个变元‘today’,通过“declareParameters”来声明该变量,并在“execute”方法中传入该变量的实例。这种带参数的查询,很类似于我们以前采用JDBC的带?的查询方式。 其中startsWith(String s)是JDO提供的标准字符方法,类似的方法还有endsWith(String s)。 JDOQL:上述使用的就是一个JDOQL样例,JDOQL是JDO规范一个组成部分。使用JDOQL可以使用应用在不同的JDO实现上运行。为了解决JDOQL的某些不足,JDO规范提供了支持特定JDO供应商查询语句接口。 查询排序 下例是将查询结果按“出版日期降序、书名升序”进行排序。 Query query = pm.newQuery(Book.class, filter); String orderStr = “publishDate decending, name ascending”; query.setOrdering(orderStr); results = query.execute(today); 对象更新 当客户端对业务数据进行了更新后,需要通过业务过程将其更新到持久层中。这有两个过程,首先根据主键找到该实例,接着更新字段及提交。如下例,将指定书目编号的书本的出版日期进行更改。 public void updateBookPublishDate (String isbn, Date newDate) { PersistenceManager pm = null; try{ pm = pmf.getPersistenceManager(); Object obj = pm.newObjectIdInstance(Book.class,isbn); Book book = (Book)pm.getObjectById(obj,true); book.setPublishDate(newDate); }catch(Exception e) { xxxContext.setRollbackOnly(); throw new Exception(e); }finally{ try{ if (pm != null && !pm.isClosed()) { pm.close(); } }catch(Exception ex) { System.out.println(ex); } } 注,在PersistenceManager使用newObjectIdInstance()方法时,JDO是如何知道通过书目编号ISBN来找到该对象呢?其实在本可持久类Book的jdo描述文件中,还需提供如下信息: 其中“identity-type=“application””声明可持久类Book采用程序标识方式,即应用程序传入ID(字段isbn为“primary-key”)信息,JDO实现构造出指定的“objectid-class”的实例(即newObjectIdInstance过程),并由JDO来检索出指定的持久化对象(即getObjectById)。 BookKey类源码如下: package businesss.model; public class BookKey implements java.io.Serializable { public String isbn; public BookKey() { } public BookKey(String oid) { isbn = oid; } public String toString() { return isbn; } public Boolean equals(Object obj) { return isbn.equals ((BookKey)obj).isbn); } public int hashCode() { return isbn.hashCode(); } } 符合 JDO 的“objectid-class”类,如“BookKey”,须具备以下条件: 类声明为 public,并实现 java.io.Serializable; 带有一个公有且不带参数的构造方法; 当字段作为主键时,须有公有的,且名称和类型与持久类的字段一致,如:public String isbn; equals 和 hashCode 须使用全部(特指多字段的联合主键)的主键字段值; 类必须有一个构造方法,与 toString 方法的处理过程是逆向过程;即将 toString 的输出值,作为该构造方法的输入值,又可以重新生成该实例(如构造方法“public BookKey(String oid)”)。 综上所述,如果Book由两个字段作为主键,如isbn和name,则可能的代码是pm.newObjectIdInstance(Book.class,isbn+“#”+name),且BookKey的构造方法作相应更改,并有两个公有字段“isbn”和“name”。 对象删除 对象删除采用方法deletePersistence。示例如下: pm.currentTransaction().begin(); Object obj = pm.newObjectIdInstance (Book.class,isbn); Book book = (Book)pm.getObjectById(obj,true); pm.deletePersistence(book); pm.currentTransaction().commit(); 获得PersistenceManager实例 上述的所有操作与需要PersistenceManager实例,它可以在两种环境方法下获得:非受管环境和受管环境。 非受管环境 非受管环境是多指两层开发模式,应用程序直接获得资源对象,进行业务操作。一般事务管理、安全管理或资源管理都需要应用程序自行维护。 Properties properties = new Properties(); properties.put(“javax.jdo. PersistenceManagerFactoryClass”, “com.xxx.jdo.xxxPMFClass”); properties.put(“javax.jdo. option.ConnectionURL”, “xxx”); properties.put(“javax.jdo. option.ConnectionUserName”, “xxx”); properties.put(“javax.jdo. option.ConnectionPassword”, “xxx”); PersistenceManagerFactory pmf = JDOHelper.getPersistence ManagerFactory(properties); PersistenceManager pm = pmf.getPersistenceManager(); 与JDBC获取类似。 受管环境 受管环境一般是多层开发模式,尤其是处于J2EE应用环境中,程序通过容器获得资源对象,进行业务操作,由于在容器环境下,事务、安全及资源管理都由容器进行统一集中管理,从而简化了代码结构。 以下是EJB(EntityBean、SessionBean、MessageDrivenBean)中的setXXXContext中的代码示例。 private PersistenceManagerFactory pmf; public void setXXXContext (XXXContext context) { try{ InitialContext ctx = new InitialContext(); Object obj = ctx.lookup (“java:compenvjdofactory”); pmf = (PersistenceManagerFactory) PortableRemoteObject.narrow (o,PersistenceManagerFactory.class); }catch(NamingException e) { throw new Exception(e); } } PMF是如何绑定到J2EE环境下的JNDI上,有兴趣可参考JCA相关的技术文档。 事务管理 事务管理及使用,主要有以下三种情形。 使用JDO事务的Bean管理情形 一般在非J2EE环境下,使用该事务管理模式。 PersistenceManager pm = pmf.getPersistenceManager(); pm.currentTransaction().begin(); //do some business with jdo pm.currentTransaction().commit(); pm.close(); 使用JTA事务的Bean管理情形 一般在J2EE环境下,采用Bean管理的事务情形下,使用以下方式。 该方式可用在EJB的事务环境下。 xxxContext.getUser Transaction().begin(); PersistenceManager pm = pmf.getPersistenceManager(); //do some business with jdo xxxContext.getUserTransaction().commit(); pm.close(); 使用JTA事务的容器管理情形 一般在J2EE环境下,采用容器管理的事务情形下,使用如下方式。 如下是某个会话Bean的业务方法。 public void doBusiness(){ PersistenceManager pm ; try{ pm = pmf.getPersistenceManager(); //do some business with jdo }catch(Exception e){ xxxContext.setRollbackOnly(); System.out.println(e); }finally{ try{ if (pm != null && !pm.isClosed()) pm.close(); }catch(Exception ex){ System.out.println(ex); } } } 综上,可以得出结论,JDO的操作完全与JDBC的操作相差无几。 JDO高级应用 复杂对象的持久化 在实际的应用中,一个可持久化类要远比Book类复杂很多。它可能会引用其它的Java类型、类、集合或数组,及可能复杂的继承关系。当这些对象的状态发生变化时,JDO是如何感知及跟踪状态变化? JDO提供了相应的API及规范来实现该功能。 基本类型及引用 可持久化类的字段能被JDO实现进行持久化的原则。原始类型、java.util.Date等被支持(其它较为复杂或可选特性,详见JDO规范);如果引用是一个可持久类,则JDO进行持久化处理;通过元数据(如jdo文件)声明的字段,一般是非可持久化类的引用,JDO进行持久化; 前面两种情形,当状态发生变化时,JDO会自动感知,但如果引用是非可持久化类,则需要代码进行显式通知,否则JDO不会将变化进行存储。如下例: public class Book { …… private Object picture; public void setPicture(Object pic) { picture = pic; } public Object getPicture() { Return picture; } } 该类字段picture需要持久化,但java.lang.Object不是一个可持久类,故声明如下: 如果其它模块通过getPicture获得对象,并在JDO不可感知的外部,修改对象,则变化不会存储,较好的办法是修改setPicture方法,如下: public void setPicture(Object pic) { JDOHelper.makeDirty(this, “picture”); picture = pic; } 并通过setPicture方法来更新数据。JDO的“makeDirty”方法,主要负责通知JDO实现,可持久化类Book某个实例(this)的“picture”字段发生了变化。 集合 可持久类的字段引用为集合时,按照JDO规范,强制支持java.util.HashSet,对HashMap、HashTable、TreeMap、TreeSet、LinkedList、ArrayList及Vector的支持对JDO实现而言是可选的,通过PersistenceManagerFactory的supportedOptions方法获得实现特性。 数组 如果可持久类的引用是数组类型,当数组单元发生变化时,需要调用“makeDirty”来通知JDO实现,该实例的数组引用内容发生了变化。与引用是非可持久类实例不同,不需要进行JDO的元数据声明。 继承 如果使用可持久性,一般继承的子类与父类都为可持久类,以减少系统复杂性,这时需要在子类的元数据中指出其可持久超类,如下: < class name=“TechBook” persistence-capable-superclass=“Book”/> 可为非持久类扩展持久类,或可为持久类扩展非可持久类;这两种情形JDO实现都将忽略非 可持久类的字段部分,而不保存到数据库。 JDO的一些不足之处 JDO对数据的持久化技术相比于成熟的SQL,还有不足之处,这些不足在某些情况下将可能会影响数据处理部分的设计实现。以下列举了常用数据访问的必要功能 查询增强 如字符串不支持通配符、大小写比较; 不支持聚合操作,无法实现MIN、MAX、SUM和AVG; 不支持投影操作,JDO查询返回为对象,而不像SQL那样返回字段; 不支持联合、交叉(UNION/INTERSECT); 不支持JOIN、IN和EXISTS; 企业应用探究 由于JDO采用面向对象的持久化处理技术,从而为解决企业业务系统的持久化问题提供了一个新技术解决方案。但是先进的未必就最适用。在某些应用场景下,需要结合各种因素,采取灵活的策略。 面向对象与面向记录 现在大多业务系统都采用面向对象分析设计,这就需要每个应用系统都自行实现将对象映射成记录,并存储到数据库中,而有JDO这样的面向对象的持久化技术,在某种程度上解放了这种转化设计的不规范性,进而获得相对更优的系统结构。 另一方面,一个业务系统的数据持久化,一般都有这样的过程:对象层->记录层->物理层,JDO无疑使分析设计人员从记录层的苦海中解脱出来,从而更加专注于对象层,这对开发人员无疑是一个令人欢欣鼓舞的技术。 JDO并不能完全代替JDBC。 根据经典的“8-2原理”,如果用JDO来解决80%的问题,余下的20%由JDBC来实现,这种相互补充,各取所长的策略,是一个很有效的办法。 这样一方面可以获得较好的结构及提升开发质量,另一方面解决了JDO的某些技术不足,并可根据以后的技术变化,再做适当转化。 性能问题 JDO与JDBC究竟谁的性能更优,目前还没有一个权威、且令人满意的答案。但对于一些JDO实现而言,通过采用缓存机制,使得性能有了较大提高。 跨数据库 使用JDO的系统能更好地进行数据库移植,甚至可以在对象数据库上运行;当然JDBC处理层如果完全遵循SQL-92标准,也同样具有很好的跨数据库能力。 <淘宝热门商品:
 

105.00 元 

Levi's 复古铜系列情侣款女装牛仔裤

 

¥:88.00 

玩儿宝贝 小朋友的礼物

精装礼品手提箱

来源:程序员网

小小豆叮

Java中ThreadLocal的设计与使用

早在Java 1.2推出之时,Java平台中就引入了一个新的支持:java.lang.ThreadLocal,给我们在编写多线程程序时提供了一种新的选择。使用这个工具类可以很简洁地编写出优美的多线程程序,虽然ThreadLocal非常有用,但是似乎现在了解它、使用它的朋友还不多。 ThreadLocal是什么 ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。线程局部变量并不是Java的新发明,在其它的一些语言编译器实现(如IBM XL FORTRAN)中,它在语言的层次提供了直接的支持。因为Java中没有提供在语言层次的直接支持,而是提供了一个ThreadLocal的类来提供支持,所以,在Java中编写线程局部变量的代码相对比较笨拙,这也许是线程局部变量没有在Java中得到很好的普及的一个原因吧。 ThreadLocal的设计 首先看看ThreadLocal的接口: Object get() ; // 返回当前线程的线程局部变量副本 protected Object initialValue(); // 返回该线程局部变量的当前线程的初始值 void set(Object value); // 设置当前线程的线程局部变量副本的值 ThreadLocal有3个方法,其中值得注意的是initialValue(),该方法是一个protected的方法,显然是为了子类重写而特意实现的。该方法返回当前线程在该线程局部变量的初始值,这个方法是一个延迟调用方法,在一个线程第1次调用get()或者set(Object)时才执行,并且仅执行1次。ThreadLocal中的确实实现直接返回一个null: protected Object initialValue() { return null; } ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。比如下面的示例实现: public class ThreadLocal { private Map values = Collections.synchronizedMap(new HashMap()); public Object get() { Thread curThread = Thread.currentThread(); Object o = values.get(curThread); if (o == null && !values.containsKey(curThread)) { o = initialValue(); values.put(curThread, o); } return o; } public void set(Object newValue) { values.put(Thread.currentThread(), newValue); } public Object initialValue() { return null; } } 当然,这并不是一个工业强度的实现,但JDK中的ThreadLocal的实现总体思路也类似于此。 ThreadLocal的使用 如果希望线程局部变量初始化其它值,那么需要自己实现ThreadLocal的子类并重写该方法,通常使用一个内部匿名类对ThreadLocal进行子类化,比如下面的例子,SerialNum类为每一个类分配一个序号 public class SerialNum { // The next serial number to be assigned private static int nextSerialNum = 0; private static ThreadLocal serialNum = new ThreadLocal() { protected synchronized Object initialValue() { return new Integer(nextSerialNum++); } }; public static int get() { return ((Integer) (serialNum.get())).intValue(); } } SerialNum类的使用将非常地简单,因为get()方法是static的,所以在需要获取当前线程的序号时,简单地调用: int serial = SerialNum.get(); 即可。 在线程是活动的并且ThreadLocal对象是可访问的时,该线程就持有一个到该线程局部变量副本的隐含引用,当该线程运行结束后,该线程拥有的所以线程局部变量的副本都将失效,并等待垃圾收集器收集。 ThreadLocal与其它同步机制的比较 ThreadLocal和其它同步机制相比有什么优势呢?ThreadLocal和其它所有的同步机制都是为了解决多线程中的对同一变量的访问冲突,在普通的同步机制中,是通过对象加锁来实现多个线程对同一变量的安全访问的。这时该变量是多个线程共享的,使用这种同步机制需要很细致地分析在什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放该对象的锁等等很多。所有这些都是因为多个线程共享了资源造成的。ThreadLocal就从另一个角度来解决多线程的并发访问,ThreadLocal会为每一个线程维护一个和该线程绑定的变量的副本,从而隔离了多个线程的数据,每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的整个变量封装进ThreadLocal,或者把该对象的特定于线程的状态封装进ThreadLocal。 由于ThreadLocal中可以持有任何类型的对象,所以使用ThreadLocal get当前线程的值是需要进行强制类型转换。但随着新的Java版本(1.5)将模版的引入,新的支持模版参数的ThreadLocal类将从中受益。也可以减少强制类型转换,并将一些错误检查提前到了编译期,将一定程度地简化ThreadLocal的使用。 总结 当然ThreadLocal并不能替代同步机制,两者面向的问题领域不同。同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式;而ThreadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源(变量),这样当然不需要对多个线程进行同步了。所以,如果你需要进行多个线程之间进行通信,则使用同步机制;如果需要隔离多个线程之间的共享冲突,可以使用ThreadLocal,这将极大地简化你的程序,使程序更加易读、简洁。 <淘宝热门商品:
 

108.00 元  

电视购物热销.夏娃之秀魔力挺,让女人都有杀人"胸"器

 

19.00 元  

【广州商盟】靓一族专营匡威运动鞋/秋冬针织帽子

特价19元 09年新款出口韩国时尚个性净版球球针织帽爆款卷边

来源:程序员网

小小豆叮

Java 理论与实践: 再谈 Urban 性能传言

流行问题:哪种语言的原始分配性能更快,Java 语言还是 C/C++?答案可能令人惊讶 —— 现代 JVM 中的分配比执行得最好的 malloc 实现还要快得多。HotSpot 1.4.2 之后虚拟机中的 new Object() 常见代码路径最多 10 条机器指令(Sun 提供的数据;请参阅 参考资料),而用 C 语言实现的执行得最好的 malloc 实现,每个调用平均要求的指令在 60 到 100 条之间(Detlefs 等;请参阅 参考资料)。而且分配性能在整体性能中不是一个微不足道的部分,测评显示:对于许多实际的 C 和 C++ 程序(例如 Perl 和 Ghostscript),整体执行时间中的 20% 到 30% 都花在 malloc 和 free 上,远远多于健康的 Java 应用程序在分配和垃圾收集上的开销(Zorn;请参阅 参考资料)。 继续,弄得一团糟 没有必要搜索众多的 blog 或 Slashdot 贴子,去寻找像“垃圾收集永远不会像直接内存管理一样有效”这样能够说服人的陈述。而且,从某个方面来说,这些话说的是对的 —— 动态内存管理并不一样快 —— 而是快得多。malloc/free 技术一次处理一个内存块,而垃圾收集机制则采用大批量方式处理内存管理,从而形成更多的优化机会(以一些可以预见到的损失为代价)。 这条“听起来有理的意见” (以大批量清理垃圾要比一天到晚一点点儿清理垃圾更容易)得到了数据的证实。一项研究(Zorn; 请参阅 参考资料)测量了在许多常见 C++ 应用程序中,用保守的 Boehm-Demers-Weiser(BDW)替换 malloc 的效果,结果是:许多程序在采用垃圾收集而不是传统的分配器运行时,表现出了速度提升。(BDW 是个保守的、不移动的垃圾收集器,严重地限制了对分配和回收进行优化的能力,也限制了改善内存位置的能力;像 JVM 中使用的那些精确的浮动收集器可以做得更好。) 在 JVM 中的分配并不总是这么快,早期 JVM 的分配和垃圾收集性能实际上很差,这当然就是 JVM 分配慢这一说法的起源。在非常早的时候,我们看到过许多“分配慢”的意见 —— 因为就像早期 JVM 中的一切一样,它确实慢 —— 而性能顾问提供了许多避免分配的技巧,例如对象池。(公共服务声明:除了对最重量的对象之外,对象池现在对于所有对象都是严重的性能损失,而且要在不造成并发瓶颈的情况下使用对象池也很需要技巧。)但是,从 JDK 1.0 开始已经发生了许多变化;JDK 1.2 中引入的分代收集器(generational collector)支持简单得多的分配方式,可以极大地提高性能。 分代垃圾收集 分代垃圾收集器把堆分成多代;多数 JVM 使用两代,“年轻代”和“年老代”。对象在年轻代中分配;如果它们在一定数量的垃圾收集之后仍然存在,就被当作是”长寿的“,并晋升到年老代。 HotSpot 提供了使用三个年轻代收集器的选择(串行拷贝、并行拷贝和并行清理),它们都采用“拷贝”收集器的形式,有几个重要的公共特征。拷贝收集器把内存空间从中间分成两半,每次只使用一半。开始时,使用中的一半构成了可用内存的一个大块;分配器满足分配请求时,返回它没有使用的空间的前 N 个字节,并把指针(分隔“使用”部分)从“自由”部分移动过来,如清单 1 的伪代码所示。当使用的那一半用满时,垃圾收集器把所有活动对象(不是垃圾的那些对象)拷贝到另一半的底部(把堆压缩成连续的),然后从另一半开始分配。 清单 1. 在存在拷贝收集器的情况下,分配器的行为 void *malloc(int n) { if (heapTop - heapStart < n) doGarbageCollection(); void *wasStart = heapStart; heapStart += n; return wasStart; } 从这个伪代码可以看出为什么拷贝收集器可以实现这么快的分配 —— 分配新对象只是检查在堆中是否还有足够的剩余空间,如果还有,就移动指针。不需要搜索自由列表、最佳匹配、第一匹配、lookaside 列表 ,只要从堆中取出前 N 个字节,就成功了。 如何回收? 但是分配仅仅是内存管理的一半,回收是另一半。对于多数对象来说,直接垃圾收集的成本为零。这是因为,拷贝收集器不需要访问或拷贝死对象,只处理活动对象。所以在分配之后很快就变成垃圾的对象,不会造成收集周期的工作量。 在典型的面向对象程序中,绝大多数对象(根据不同的研究,在 92% 到 98% 之间)“死于年轻”,这意味着它们在分配之后,通常在下一次垃圾收集之前,很快就变成垃圾。(这个属性叫作 分代假设,对于许多面向对象语言已经得到实际测试,证明为真。)所以,不仅分配要快,对于多数对象来说,回收也要自由。 线程本地分配 如果分配器完全像 清单 1 所示的那样实现,那么共享的 heapStart 字段会迅速变成显著的并发瓶颈,因为每个分配都要取得保护这个字段的锁。为了避免这个问题,多数 JVM 采用了 线程本地分配块,这时每个线程都从堆中分配一个更大的内存块,然后顺序地用这个线程本地块为小的分配请求提供服务。所以,线程花在获得共享堆锁的大量时间被大大减少,从而提高了并发性。(在传统的 malloc 实现的情况下要解决这个问题更困难,成本更高;把线程支持和垃圾收集都构建进平台促进了这类协作。) 回页首 堆栈分配 C++ 向程序员提供了在堆或堆栈中分配对象的选择。基于堆栈的分配更有效:分配更便宜,回收成本真正为零,而且语言提供了隔离对象生命周期的帮助,减少了忘记释放对象的风险。另一方面,在 C++ 中,在发布或共享基于堆栈的对象的引用时,必须非常小心,因为在堆栈帧整理时,基于堆栈的对象会被自动释放,从而造成孤悬的指针。 基于堆栈的分配的另一个优势是它对高速缓存更加友好。在现代的处理器上,缓存遗漏的成本非常显著,所以如果语言和运行时能够帮助程序实现更好的数据位置,就会提高性能。堆栈的顶部通常在高速缓存中是“热”的,而堆的顶部通常是“冷”的(因为从这部分内存使用之后可能过了很长时间)。所以,在堆上分配对象,比起在堆栈上分配对象,会带来更多缓存遗漏。 更糟的是,在堆上分配对象时,缓存遗漏还有一个特别讨厌的内存交互。在从堆中分配内存时,不管上次使用内存之后留下了什么内容,内存中的内容都被当作垃圾。如果在堆的顶部分配的内存块不在缓存中,执行会在内存内容装入缓存的过程中出现延迟。然后,还要用 0 或其他初始值覆盖掉刚刚费时费力装入缓存的那些值,从而造成大量内存活动的浪费。(有些处理器,例如 Azul 的 Vega,包含加速堆分配的硬件支持。) escape 分析 Java 语句没有提供任何明确地在堆栈上分配对象的方式,但是这个事实并不影响 JVM 仍然可以在适当的地方使用堆栈分配。JVM 可以使用叫作 escape 分析 的技术,通过这项技术,JVM 可以发现某些对象在它们的整个生命周期中都限制在单一线程内,还会发现这个生命周期绑定到指定堆栈帧的生命周期上。这样的对象可以安全地在堆栈上而不是在堆上分配。更好的是,对于小型对象,JVM 可以把分配工作完全优化掉,只把对象的字段放入寄存器。 清单 2 显示了一个可以用 escape 分析把堆分配优化掉的示例。Component.getLocation() 方法对组件的位置做了一个保护性的拷贝,这样调用者就无法在不经意间改变组件的实际位置。先调用 getDistanceFrom() 得到另一个组件的位置,其中包括对象的分配,然后用 getLocation() 返回的 Point 的 x 和 y 字段计算两个组件之间的距离。 清单 2. 返回复合值的典型的保护性拷贝方式 public class Point { private int x, y; public Point(int x, int y) { this.x = x; this.y = y; } public Point(Point p) { this(p.x, p.y); } public int getX() { return x; } public int getY() { return y; } } public class Component { private Point location; public Point getLocation() { return new Point(location); } public double getDistanceFrom(Component other) { Point otherLocation = other.getLocation(); int deltaX = otherLocation.getX() - location.getX(); int deltaY = otherLocation.getY() - location.getY(); return Math.sqrt(deltaX*deltaX + deltaY*deltaY); } } getLocation() 方法不知道它的调用者要如何处理它返回的 Point;有可能得到一个指向 Point 的引用,比如把它放在集合中,所以 getLocation() 采用了保护性的编码方式。但是,在这个示例中,getDistanceFrom() 并不会这么做,它只会使用 Point 很短的时间,然后释放它,这看起来像是对完美对象的浪费。 聪明的 JVM 会看出将要进行的工作,并把保护性拷贝的分配优化掉。首先,对 getLocation() 的调用会变成内联的,对 getX() 和 getY() 的调用也同样处理,从而导致 getDistanceFrom() 的表现会像清单 3 一样有效。 清单 3. 伪代码描述了把内联优化应用到 getDistanceFrom() 的结果 public double getDistanceFrom(Component other) { Point otherLocation = new Point(other.x, other.y); int deltaX = otherLocation.x - location.x; int deltaY = otherLocation.y - location.y; return Math.sqrt(deltaX*deltaX + deltaY*deltaY); } 在这一点上,escape 分析可以显示在第一行分配的对象永远不会脱离它的基本块,而 getDistanceFrom() 也永远不会修改 other 组件的状态。(escape 指的是对象引用没有保存到堆中,或者传递给可能保留一份拷贝的未知代码。)如果 Point 真的是线程本地的,而且也清楚它的生命周期限制在分配它的基本块内,那么它既可以进行堆栈分配,也可以完全优化掉,如清单 4 所示。 清单 4. 伪代码描述了从 getDistanceFrom() 优化掉分配后的结果 public double getDistanceFrom(Component other) { int tempX = other.x, tempY = other.y; int deltaX = tempX - location.x; int deltaY = tempY - location.y; return Math.sqrt(deltaX*deltaX + deltaY*deltaY); } 结果就是得到了与所有字段都是 public 时能够得到的相同的性能,同时保持了封装和保护性拷贝(在其他安全编码技术之中)提供的安全性。 Mustang 中的 escape 分析 escape 分析是一项被议论了很久的优化,它最后终于出现了:Mustang(Java SE 6)的当前构建中可以做 escape 分析,并在适当的地方把堆分配转换成堆栈分析(或者不分配)。用 escape 分析清除一些分配,会带来更快的平均分配时间,简化的内存工作,更少的缓存遗漏。而且,优化掉一些分配,可以降低垃圾收集器的压力,从而让收集运行得更少。 即使在源代码中进行堆栈分配不太现实的地方,即使语言提供了分配的选项,escape 分析也能找到堆栈分配的机会,因为特定的分配是否会被优化掉,是根据特定代码路径中实际上如何使用对象返回方法的结果而决定的。getLocation() 返回的 Point 可能不是在所有情况下都适合进行堆栈分配,但是一旦 JVM 内联了 getLocation(),它就可以自由而且独立地优化每个调用,从而在两方面都提供了最好的结果:最优的性能,最少的时间花在进行低级的性能调整决策上。 结束语 JVM 擅长发现我们一直以为只有开发人员才能知道的事情,这令人震惊。让 JVM 根据具体情况在堆栈分配和堆分配之间进行选择,我们就能得到堆栈分配的性能好处,却不必让程序员在进行堆栈分配还是进行堆分配上费脑筋。 <淘宝热门商品:
 

105.00 元 

Levi's 复古铜系列情侣款女装牛仔裤

 

:鲜花速递/蛋糕配送/园艺花艺 

缤纷园艺淘宝店--苗圃直销各类花卉苗木 购买送惊喜

来源:程序员网

小小豆叮

IC卡应用系统开发-(二)加密机访问

在有卡片的应用系统中,很多数据都是进行加密处理的.如有的系统是采用密文传输数据,有的采用明文+MAC方式,也有的是密文+MAC.但不管采用那种方式,都是与加密/解密息息相关的. 在我开发的系统中,为保证系统的安全,卡片有效和数据完整,做了一个软加密机.就是一个java程序实现对数据的加密,解密和MAC运算.采用3DES算法,它提供密钥经分散后的加/解功能.关于java实现DES的程序,在网上可以找到,这里不在赘述.本文介绍对软加密机访问的例程. 我还是定义了3个类: 接口类:IHSM.JAVA,说明对外提供的行为. 实现类:HSM.JAVA,对加密机的访问具体实现. 异常类:HSMException.java,继承系统Exception类. package com.microapp.business.hsm; /** *

Title: System

* *

Description:

* *

Copyright: Copyright (c) 2005

* *

Company: my

* * @author microapp * @version 1.0 */ public interface IHSM { /** * 加密数据 * @param interspersed * @param data String * @param index int * @return String * @throws HSMException */ public String getEncryption(String interspersed, String data, int index) throws HSMException; /** * getKey * * @param inInterspersed String * @param outInterspersed String * @param index int */ public String getKey(String inInterspersed, String outInterspersed, int index) throws HSMException; /** * 解密数据 * @param interspersed * @param data String * @param index int * @return String * @throws HSMException */ public String getDecryption(String interspersed, String data, int index) throws HSMException; /** * 取得MAC * @param interspersed * @param challenge String * @param data String * @return String * @throws HSMException */ public String getMAC(String interspersed, String data, int index) throws HSMException; } 异常类: ackage com.microapp.business.hsm; /** *

Title:

* *

Description:

* *

Copyright: Copyright (c) 2005

* *

Company:

* * @author microapp * @version 1.0 */ public class HSMException extends Exception { String message; public HSMException() { } public HSMException(String errmsg) { super(errmsg); } public HSMException(int errCode) { switch(errCode) { case 80: message - "命令不可识别!"; break; case 81: message - "报文长度错误!"; break; default: message - "其他错误!"; } } } 实现文件 package com.microapp.business.hsm; /** *

Title:

* *

Description:

* *

Copyright: Copyright (c) 2005

* *

Company:

* * @author me * @version 1.0 */ import java.io.*; import java.net.*; public class HSM implements IHSM { private String address = "192.168.1.88"; private int port = 8888; public HSM() { } public HSM(String addr, int port) { this.address = addr; this.port = port; } public void setAddress(String addr) { this.address = addr; } public String getAddress() { return address; } public void setPort(int port) { this.port = port; } public int getPort() { return port; } /** * 加密 * @param interspersed String * @param data String * @param index int * @return String * @throws HSMException */ public String getEncryption(String interspersed, String data, int index) throws HSMException { System.out.println("Hava the data request at HSMe " + data); int l = data.length(); if(l != 16) { throw new HSMException("Data length error!"); } Socket Client; DataInputStream InputS; DataInputStream KeyS; PrintStream OutputS; byte[] sendbuf = new byte[45]; byte[] recvbuf = new byte[64]; int i = 0; byte b1 = 0; byte b2 = 0; try { Client = new Socket(address, port); InputS = new DataInputStream(Client.getInputStream()); OutputS = new PrintStream(Client.getOutputStream()); KeyS = new DataInputStream(System.in); sendbuf[0] = 0; //长度(高) sendbuf[1] = 45; //长度(低) sendbuf[2] = (byte)0xB0; //命令代码 sendbuf[3] = 1; //密钥索引 sendbuf[4] = 24; //密钥长度 sendbuf[5] = 0; //密钥 for(i = 0; i < 24; i++) { sendbuf[5 + i] =(byte)(Integer.parseInt(interspersed.substring((i * 2), (i * 2) +2), 16)); } sendbuf[6] = 0x80; //计算方式:80-加密 sendbuf[7] = 0x00; //计算数据长度(高) sendbuf[8] = 8; //计算数据长度(低) sendbuf[9] = 0; //计算数据 for(i = 0; i < 8; i++) { sendbuf[9 + i] =(byte)(Integer.parseInt(data.substring((i * 2), (i * 2) +2), 16)); } OutputS.write(sendbuf); l = InputS.read(recvbuf); Client.close(); if (l < 6) { throw new HSMException("Return data length error." + Integer.toHexString(l)); } if((recvbuf[3] != 0)) { throw new HSMException("Return data error." + Integer.toHexString(recvbuf[3]).toUpperCase()); } l = (recvbuf[4] * 256 + recvbuf[5]); StringBuffer sb = new StringBuffer(); String s = new String(); for(i = 0; i < l; i++) { s = Integer.toHexString(recvbuf[i + 6]).toUpperCase(); if(s.length() == 1) { sb.append("0" + s); } else if(s.length() > 2) { sb.append(s.substring(s.length() - 2, s.length())); } else { sb.append(s); } } return sb.toString(); } catch(IOException e) { throw new HSMException("IOException Happened" + e.getMessage()); } } /** * 解密 * @param interspersed String * @param data String * @return String * @throws HSMException */ public String getDecryption(String interspersed, String data, int index) throws HSMException { System.out.println("Hava the data request at HSMd " + data); int l = data.length(); if((l % 2) != 0) { throw new HSMException("Data length error!"); } Socket Client; DataInputStream InputS; DataInputStream KeyS; PrintStream OutputS; byte[] sendbuf = new byte[45]; byte[] recvbuf = new byte[64]; int i = 0; byte b1 = 0; byte b2 = 0; try { Client = new Socket(address, port); InputS = new DataInputStream(Client.getInputStream()); OutputS = new PrintStream(Client.getOutputStream()); KeyS = new DataInputStream(System.in); sendbuf[0] = 0; //长度(高) sendbuf[1] = 45; //长度(低) sendbuf[2] = (byte)0xB0; //命令代码 sendbuf[3] = 1; //密钥索引 sendbuf[4] = 24; //密钥长度 sendbuf[5] = 0; //密钥 for(i = 0; i < 24; i++) { sendbuf[5 + i] =(byte)(Integer.parseInt(interspersed.substring((i * 2), (i * 2) +2), 16)); } sendbuf[6] = 0x81; //计算方式:81-解密 sendbuf[7] = 0x00; //计算数据长度(高) sendbuf[8] = (byte)(l / 2); //计算数据长度(低) sendbuf[9] = 0; //计算数据 for(i = 0; i < (l / 2); i++) { sendbuf[9 + i] =(byte)(Integer.parseInt(data.substring((i * 2), (i * 2) +2), 16)); } OutputS.write(sendbuf); i = InputS.read(recvbuf); Client.close(); if(i < 6) { throw new HSMException("Return data length error." + Integer.toHexString(i)); } if((recvbuf[3] != 0)) { throw new HSMException("Return data error." + Integer.toHexString(recvbuf[3]).toUpperCase()); } l = (recvbuf[4] * 256 + recvbuf[5]); StringBuffer sb = new StringBuffer(); String s = new String(); for(i = 0; i < l; i++) { s = Integer.toHexString(recvbuf[i + 6]).toUpperCase(); if(s.length() == 1) { sb.append("0" + s); } else if(s.length() > 2) { sb.append(s.substring(s.length() - 2, s.length())); } else { sb.append(s); } } return sb.toString(); } catch(IOException e) { throw new HSMException("IOException Happened" + e.getMessage()); } } /** * 获取MAC * @param interspersed String * @param challenge String :随机数 * @param data String :加密数据 * @return String * @throws HSMException */ public String getMAC(String interspersed, String data, int index) throws HSMException { Socket Client; DataInputStream InputS; DataInputStream KeyS; PrintStream OutputS; byte[] sendbuf = new byte[64]; byte[] recvbuf = new byte[64]; int i = 0; byte b1 = 0; byte b2 = 0; try { int l = data.length(); if ((l % 2) != 0) { throw new HSMException("Data length must be 2 times."); } Client = new Socket(address, port); InputS = new DataInputStream(Client.getInputStream()); OutputS = new PrintStream(Client.getOutputStream()); KeyS = new DataInputStream(System.in); sendbuf[0] = 0x00; //长度 sendbuf[1] = (byte) (l + 12); //长度 sendbuf[2] = (byte) 0xB0; //命令代码 sendbuf[3] = 1; //密钥索引 sendbuf[4] = 24; //密钥长度 sendbuf[5] = 0; //密钥 for(i = 0; i < 24; i++) { sendbuf[5 + i] =(byte)(Integer.parseInt(interspersed.substring((i * 2), (i * 2) +2), 16)); } sendbuf[6] = 0x82; //计算方式:82-计算MAC sendbuf[7] = 0; //计算数据长度 sendbuf[8] = (byte) i; //计算数据长度 sendbuf[9] = 0; //计算数据 for (i = 0; i < (l / 2); i++) { sendbuf[37 + i] =(byte)(Integer.parseInt(data.substring((i * 2), (i * 2) +2), 16)); } OutputS.write(sendbuf); i = InputS.read(recvbuf); Client.close(); if (i < 6) { throw new HSMException("Return data length error." + Integer.toHexString(i)); } if ( (recvbuf[3] != 0)) { throw new HSMException("Return data error." + Integer.toHexString(recvbuf[3]).toUpperCase()); } l = (recvbuf[4] * 256 + recvbuf[5]); StringBuffer sb = new StringBuffer(); String s = new String(); for (i = 0; i < l; i++) { s = Integer.toHexString(recvbuf[i + 6]).toUpperCase(); if(s.length() == 1) { sb.append("0" + s); } else if(s.length() > 2) { sb.append(s.substring(s.length() - 2, s.length())); } else { sb.append(s); } } return sb.toString(); } catch (IOException e) { throw new HSMException("IOException Happened" + e.getMessage()); } } } 为在应用服务器中能够实现逻辑数据组合,我建立了一个无状态session bean.在bean主要是实现数据的打包和转换. package com.microapp.business; import javax.ejb.SessionBean; import javax.ejb.SessionContext; import javax.ejb.CreateException; import com.vsc.ets.business.hsm.*; import java.util.Random; public class HSMModuleBean implements SessionBean { public static final int EXTERNAL_AUTHEN_KEY = 0x46; public static final int INTERNAL_AUTHEN_KEY = 0x45; public static final int MAC_KEY = 0x44; public static final int GENERAL_KEY = 0x41; SessionContext sessionContext; IHSM hsm = new HSM("192.168.1.88", 8888); Random rd = new Random(); String challenge; public void ejbCreate() throws CreateException { } public void ejbRemove() { } public void ejbActivate() { } public void ejbPassivate() { } public void setSessionContext(SessionContext sessionContext) { this.sessionContext = sessionContext; } public String getMAC(String s1, String s2, String s3, String challenge, String data) { try { String interspersed = s1 + s2 + s3; return hsm.getMAC(interspersed, challenge, MAC_KEY); } catch (Exception ex) { return null; } } public boolean internalAuthentication(String s1, String s2, String s3, String data) { String interspersed = s1 + s2 + s3; try { String s = hsm.getDecryption(interspersed, data, INTERNAL_AUTHEN_KEY); System.out.println("HSM internal authentication: " + s + " challenge: " + challenge); if(s.equals(challenge)) { return true; } else { return false; } } catch(HSMException ex) { return false; } } public String[] externalAuthentication(String s1, String s2, String s3, String data) { String interspersed = s1 + s2 + s3; String[] s = new String[2]; this.challenge = "1234567887654321"; try { s[0] = hsm.getEncryption(interspersed, data, EXTERNAL_AUTHEN_KEY); s[1] = this.challenge; System.out.println("Ext: Encryption : " + s[0] + " Challenge: " + s[1]); return s; } catch(HSMException ex) { return null; } } public String provideEncryption(String interspersed, String data) { try { return hsm.getEncryption(interspersed, data, GENERAL_KEY); } catch (Exception ex) { return null; } } public String provideDecryption(String interspersed, String data) { try { return hsm.getDecryption(interspersed, data, GENERAL_KEY); } catch (Exception ex) { return null; } } } 该过程在实践中应用,完全与硬加密机HSM相同. <淘宝热门商品:
 

 

【皇冠康康美美】同仁堂保健品 协和护肤品 营养品 减肥瘦身品

 

¥:180.00 

安婴房婴儿用品童装旗舰店/上海总经销可批发、团购

【双皇冠】热卖再次到货!原单DISNEY维尼小熊 专业婴儿定型枕

来源:程序员网

小小豆叮

第五媒体——井喷中的商机

  2000年5月17日,我国移动通信运营商开始向用户提供一种通过手机在移动网络上传送简短信息的无线应用服务,这项被称为短信服务(SMS)的业务迅速融入了无数人的日常生活,成为最便捷的交流和沟通方式,海量的短信业务迅即成为无线增值业务的杀手级应用,创造了“拇指经济”的奇迹。到2005年,我国短信业务的市场规模将达到400亿元。   短信业务的勃兴,吸引了网易、搜狐、新浪等三大网络门户相继加入“拇指经济”的洪流,并为之推波逐澜。通过互联网平台与移动通信平台的对接,人们可以通过互联网向手机发送短信,特别是取自这些网站的极为精彩的短信内容,触发了新的应用浪潮。这些网站则通过与移动运营商分帐和向用户收费的商业模式,获得了丰厚的收益,并在纳斯达克股市上有了出色的表现。   然而,在惊人而短暂的高速增长之后,我国手机短信市场进入了一个平台期,市场的增长呈现出下滑的趋势,寻找和发展新的杀手级应用,成为运营商和服务提供商(SP)们的心头之痒。一度被看好是继短信之后的下一个杀手级应用的“彩信”表现平平,而实现手机移动上网流览与下载的WAP技术和包含了丰富的音乐元素的“彩铃/炫铃”却出人意料地受到了用户的追捧。手机的技术越来越新,以手机为终端的无线增值应用也日益丰富,包括WAP、彩信、彩铃/炫铃、无线音乐、无线游戏、无线博客等,正在形成一个新的、潜力无限的“无线互联产业”的宏大版图。   这个宏大的产业版图的核心基础,是一种被称为“第五媒体”的互动传播媒体。所谓“第五媒体”,是指继报纸、广播、电视、互联网之后出现的新媒体。这种新媒体究竟是一种什么形态,甚至在概念上怎么描述,目前还处于比较模糊的争议阶段。但是,像手机这样整合了无线通信和互联网、具备声音、文本、图像乃至视频的即时互动传播能力,同时具有便携性、移动性、即时性等特性,这些特性不同于任何一种传统媒体。   基于手机作为媒体的特性,新的无线增值应用在短信之后不断涌现,包括WAP、彩信、IVR等技术应用,出现了手机报纸、手机电视、无线新闻网站、手机新闻直播等,将手机作为媒体的功能和特性日益发掘出来。通过不断地与传统媒体对接,又不断地将之超越,手机正在从一种移动通讯终端逐渐演变成为一种信息终端,在“第五媒体”中占据了极为有利的位置。   与此前的四类传统媒体相比,手机作为媒体具有更大优势:它比报纸更互动,比广播更自由,比电视更便携,比联网电脑更普及。事实上,没有一种媒体比手机更迅捷。尤为重要的是,手机作为媒体所拥有的庞大的用户数量,超过了任何一种传统媒体。据信息产业部预测,2005年我国手机用户有望达到4.02亿,平均每3个人拥有一部手机。因此,最初级的手机媒体——手机报纸将会第一步覆盖广大手机用户。   面对手机这种被称为“第五媒体”的新势力,以话音业务启动和培育了移动通信市场、推动手机普及的运营商,也随之进入无线互联产业的新天地,变身担当新角色。这个新角色的根本任务,就是要整合产业链的每一个环节,开发和设计基于手机终端的新内容、新产品、新应用,发掘新的用户群,开辟新的渠道,拓展新的市场。 <淘宝热门商品:
 

50.00 元  

韩服CASH之家

韩服 HF 跑跑卡丁车 新劳迪 劳迪Introduction 永久 支持韩文名

 

0.90 元  

:①般般dē我 开【不】①般般的店

『四钻信誉』官方QB正规秒冲Q币,按元直充0.9元/QQ币

来源:程序员网

小小豆叮

java高级应用符合oo惯例的表现层控制

Hibernate的reference的副标题叫做:符合java惯例的O/R 持久化,这揭示了目前三层结构的重大问题,就是三层的不统一。到目前为止,仍然难于在web界面上实现C/S模式中"master-detail","lookup"的快捷的用户交互。      目前常见的web application的结构,包含web browser/application server/database。database占据主流的仍然是经典的E/R模型,这个模型是基于行集的,因此在vb/delphi/power builder的实践中,data source/table set都是基于行集的,odbc/jdbc driver也都是基于行集的。view层的DbGrid也是基于行集的,和Entity模型对应得非常好,开发简易直观,相信这是C/S模式得到迅速推广的重点原因之一。“master-detail”,"lookup"都是C/S模式下极为常见和直观的关联模式。      但本质上,Object pascal/java都是面向对象的。在此,就出现了一次重大的不统一:OO vs E-R。出现的解决方式就是EJB和O/R mapping 工具。EJB的entity bean是早期的entity封装形式。但是和现在以hibernate为代表的先进工具(对POJO执行持久化)比较起来,在OO与ER的对应上显得笨重而难于使用。在这些工具中,代表OO与E-R融合的最本质的功能则是继承树与表结构的对应关系。hibernate2支持整棵继承树与一个表对应、继承树中每个类与一个表对应两种基本的对应关系,而hibernate 3引入的join标记则更可以将二者融合,实现每个类可选与基类在同一个表中持久或者在新表中保存部分持久数据,可以说hibernate 3把这个对应的任务完成得非常出色。"master-detail","lookup"则对应hbm.xml这样的映射文件中的"one-many","many-one"关联。      database与java的融合完成之后,下一步,不可避免的就是现有的web client与服务器端代码之间的融合。从表面上看,web client大多采用html/javascript完成,而服务器端采用java输出,二者是简单的命令/反馈的模型,这个模型从model 1发展到MVC的模型后,编写代码变得清晰,但是开发人员仍然发现,编写web app仍然不是一件简单的事情。struts/webwork仍然只是非常底层的基础,对编写客户端业务对象没有什么帮助。比如说,在服务器端java程序建模时,大家已经习惯用pojo分析订单/客户/产品,但是在编写web client时,struts/webwork都只能帮助你完成页面提交/反馈的流程,却不能帮助你分析客户端业务:新建订单时,选择了客户之后,判断此客户是否有足够的预收款,这样一个简单用例在程序员心目中的反映仍然是每个字段的input tag,每个页面post上来的model,以及如何用action的处理再次渲染下一个页面。      最大的问题,就是作为表现层的web client端代码与服务器端代码蕴含的语义脱节。具体表现在:      在采用struts/webwork这样的MVC结构的时候,通常不会考虑在客户端进行业务控制,比如由javascipt判断预收款是否足够。因此需要不断的多次页面刷新才能完成整个逻辑。      要解决此问题,通常可以采用把业务逻辑部分转移到客户端,以javascript + xmlhttp或javascript + web service,java applet/application,甚至采用office平台(嵌入代码到excel)完成整个业务逻辑。也有很多问题:      1,若要在客户端实现业务逻辑,可能客户端代码没有对应Pojo这样的基础object设施。javascript缺乏如interface这样的基础结构。excel方案在这点更加难于进行,因为整个开发涉及到的语言太多,造成开发难度加大,项目控制困难。      直接后果就是,难于在客户端代码中定义"master-detail","lookup"等关联。就算在项目规划中在javascript中定义pojo(plain old javascript object)及其关联,也难于利用hbm.xml这样的现成关联描述。      2,客户端基础设施难于进行界面元素绑定。在处理大量数据时,excel方案在此体现出杰出的优势,客户对内置程序的excel的接受程度非常高,但缺点是这种excel程序难于做到xmlhttp可以轻松做到的动态查询等特性。      3,客户端基础设施难于与服务器端进行交互。xmlhttp以及web service可选,但是在企业应用中其低下效率可能会带来服务器的压力隐患,降低性能和吞吐量。若excel方案,则同样面临着与服务器数据交互的难题。不管是xmlhttp方案还是application方案,都面临着抛弃struts/webwork重新实现request/response dispatch的要求。      4,客户端基础设施难于进行单元测试。有junit4js,port了junit 3.8.1,但没有成熟的stub/mock工具。excel方案在此几乎不可测试。      5, 客户端基础设施难于调试。javascript缺乏类似log4j这样的log工具(log4js http://www.petrusrex.com/Programmes/jslib.htm ;这样的工具还远没有成熟),也难于进行断点跟踪。excel方案倒是有完整的vba环境。      6, 客户端基础设施运行效率低。javascipt/vba都是解释语言,难于实现复杂逻辑,其性能决定只能用它们进行细粒度的界面控制。      7,由于浏览器的分裂,造成语言的不标准,应用程序难以跨平台使用。在IE平台上可以使用behavior和expression这种类AOP的操作,却无法在mozilla中实现。      jsf方案有望成为备选方案,但是按照myfaces目前的情况,要实现更多的表现层控件,才能完成更复杂灵活的控制。      下面一次软件开发方式的突破,向前看,可能出现设计方式的突破,MDA是方向;另一个方向就是向后对具体实现的突破,在类似webapp这样的具体技术(除了webapp,application同样面临类似问题)上,对于是否能够把model的定义直接带入到表现层,JSF和.NET可能会有新一轮竞争 <淘宝热门商品:
 

3.80 元  

幸福生活 联盟津沽 种子蔬菜种子、花卉种子、园艺用品

【天津商盟】【皇冠信誉】食用草莓种子-观赏草莓种子-花种

 

4.00 元  

阳光网游挂机程序专业店

[自动发货]梦幻西游辅助脚本单开压镖+半自动跑商+单开打图日卡

来源:程序员网

小小豆叮

深入浅出Java设计模式之迭代器模式

一、 引言   迭代这个名词对于熟悉Java的人来说绝对不陌生。我们常常使用JDK提供的迭代接口进行java collection的遍历: Iterator it = list.iterator(); while(it.hasNext()){  //using “it.next();”do some businesss logic }   而这就是关于迭代器模式应用很好的例子。   二、 定义与结构   迭代器(Iterator)模式,又叫做游标(Cursor)模式。GOF给出的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。   从定义可见,迭代器模式是为容器而生。很明显,对容器对象的访问必然涉及到遍历算法。你可以一股脑的将遍历方法塞到容器对象中去;或者根本不去提供什么遍历算法,让使用容器的人自己去实现去吧。这两种情况好像都能够解决问题。   然而在前一种情况,容器承受了过多的功能,它不仅要负责自己“容器”内的元素维护(添加、删除等等),而且还要提供遍历自身的接口;而且由于遍历状态保存的问题,不能对同一个容器对象同时进行多个遍历。第二种方式倒是省事,却又将容器的内部细节暴露无遗。   而迭代器模式的出现,很好的解决了上面两种情况的弊端。先来看下迭代器模式的真面目吧。   迭代器模式由以下角色组成:   1) 迭代器角色(Iterator):迭代器角色负责定义访问和遍历元素的接口。   2) 具体迭代器角色(Concrete Iterator):具体迭代器角色要实现迭代器接口,并要记录遍历中的当前位置。   3) 容器角色(Container):容器角色负责提供创建具体迭代器角色的接口。   4) 具体容器角色(Concrete Container):具体容器角色实现创建具体迭代器角色的接口——这个具体迭代器角色于该容器的结构相关。   迭代器模式的类图如下: 从结构上可以看出,迭代器模式在客户与容器之间加入了迭代器角色。迭代器角色的加入,就可以很好的避免容器内部细节的暴露,而且也使得设计符号“单一职责原则”。   注意,在迭代器模式中,具体迭代器角色和具体容器角色是耦合在一起的——遍历算法是与容器的内部细节紧密相关的。为了使客户程序从与具体迭代器角色耦合的困境中脱离出来,避免具体迭代器角色的更换给客户程序带来的修改,迭代器模式抽象了具体迭代器角色,使得客户程序更具一般性和重用性。这被称为多态迭代。   三、 举例   由于迭代器模式本身的规定比较松散,所以具体实现也就五花八门。我们在此仅举一例,根本不能将实现方式一一呈现。因此在举例前,我们先来列举下迭代器模式的实现方式。   1.迭代器角色定义了遍历的接口,但是没有规定由谁来控制迭代。在Java collection的应用中,是由客户程序来控制遍历的进程,被称为外部迭代器;还有一种实现方式便是由迭代器自身来控制迭代,被称为内部迭代器。外部迭代器要比内部迭代器灵活、强大,而且内部迭代器在java语言环境中,可用性很弱。   2.在迭代器模式中没有规定谁来实现遍历算法。好像理所当然的要在迭代器角色中实现。因为既便于一个容器上使用不同的遍历算法,也便于将一种遍历算法应用于不同的容器。但是这样就破坏掉了容器的封装——容器角色就要公开自己的私有属性,在java中便意味着向其他类公开了自己的私有属性。   那我们把它放到容器角色里来实现好了。这样迭代器角色就被架空为仅仅存放一个遍历当前位置的功能。但是遍历算法便和特定的容器紧紧绑在一起了。   而在Java Collection的应用中,提供的具体迭代器角色是定义在容器角色中的内部类。这样便保护了容器的封装。但是同时容器也提供了遍历算法接口,你可以扩展自己的迭代器。   好了,我们来看下Java Collection中的迭代器是怎么实现的吧。 //迭代器角色,仅仅定义了遍历接口 public interface Iterator {  boolean hasNext();  Object next();  void remove(); } //容器角色,这里以List为例。它也仅仅是一个接口,就不罗列出来了 //具体容器角色,便是实现了List接口的ArrayList等类。为了突出重点这里指罗列和迭代器相关的内容 //具体迭代器角色,它是以内部类的形式出来的。AbstractList是为了将各个具体容器角色的公共部分提取出来而存在的。 public abstract class AbstractList extends AbstractCollection implements List { …… //这个便是负责创建具体迭代器角色的工厂方法 public Iterator iterator() {  return new Itr(); } //作为内部类的具体迭代器角色 private class Itr implements Iterator {  int cursor = 0;  int lastRet = -1;  int expectedModCount = modCount;  public boolean hasNext() {   return cursor != size();  }  public Object next() {   checkForComodification();   try {    Object next = get(cursor);    lastRet = cursor++;    return next;   } catch(IndexOutOfBoundsException e) {    checkForComodification();    throw new NoSuchElementException();   }  }  public void remove() {   if (lastRet == -1)    throw new IllegalStateException();    checkForComodification();   try {    AbstractList.this.remove(lastRet);    if (lastRet < cursor)     cursor--;    lastRet = -1;    expectedModCount = modCount;   } catch(IndexOutOfBoundsException e) {    throw new ConcurrentModificationException();   }  }  final void checkForComodification() {   if (modCount != expectedModCount)    throw new ConcurrentModificationException();  } }   至于迭代器模式的使用。正如引言中所列那样,客户程序要先得到具体容器角色,然后再通过具体容器角色得到具体迭代器角色。这样便可以使用具体迭代器角色来遍历容器了……   四、 实现自己的迭代器   在实现自己的迭代器的时候,一般要操作的容器有支持的接口才可以。而且我们还要注意以下问题:   在迭代器遍历的过程中,通过该迭代器进行容器元素的增减操作是否安全呢?   在容器中存在复合对象的情况,迭代器怎样才能支持深层遍历和多种遍历呢?   以上两个问题对于不同结构的容器角色,各不相同,值得考虑。   五、 适用情况   由上面的讲述,我们可以看出迭代器模式给容器的应用带来以下好处:   1) 支持以不同的方式遍历一个容器角色。根据实现方式的不同,效果上会有差别。   2) 简化了容器的接口。但是在java Collection中为了提高可扩展性,容器还是提供了遍历的接口。   3) 对同一个容器对象,可以同时进行多个遍历。因为遍历状态是保存在每一个迭代器对象中的。   由此也能得出迭代器模式的适用范围:   1) 访问一个容器对象的内容而无需暴露它的内部表示。   2) 支持对容器对象的多种遍历。   3) 为遍历不同的容器结构提供一个统一的接口(多态迭代)。   六、 总结   迭代器模式在我们的应用中很广泛,希望本文能帮助你理解它。如有不对之处,还请不吝指正。 <淘宝热门商品:
 

¥:89.00 

爱相依外贸童装.精品低价.15天无理由退货

特价 贺年款 精美棉大衣外套/棉衣/棉袄 帽子可拆卸 3971

 

 

:◤╭ Shanghai 秀卡蒂╮╰女性健康購物中心 ╯◥◣瘦身╮減肥╮

来源:程序员网

小小豆叮

使用Ajax的挑战

与任何技术一样,使用Ajax在相当多的方面都可能范错误。我在这儿讨论的问题目前都缺少解决方案,并将会随着Ajax的成熟而解决或提高。随着开发Ajax应用经验的不断获取,开发者社区中将会出现最好的实践经验与指导方针。   1、XMLHttpRequest的有效性   Ajax开发者面对的一个最大问题是当XMLHttpRequest不可用时如何反应。虽然大部分现代浏览器支持XMLHttpRequest,但还是有少量的用户,他们的浏览器不能支持,或由于浏览器安全设置而阻止对XMLHttpRequest的使用。若你的Web应用发布于公司内部的Intranet上,你很可能可以指定支持哪种浏览器,并可以确保XMLHttpRequest是可用的。若你在公共WEB上发布,则你必须意识到由于假定XMLHttpRequest是可用的,所有就阻止了老浏览器、手持设备浏览器等等用户来使用你的系统。   然而,你应该尽力保证应用系统“正常降级”使用,在系统中保留适用于不支持XMLHttpRequest的浏览器的功能。在购物车例子中,最好的方法是有一个Add to Cart按钮,可以进行常规的提交处理,并刷新页面来反映购物车状态的变化。Ajax行卫可以在页面被载入时通过JavaScript添加到页面中,只在XMLHttpRequest可用的情况下,为每个Add to Cart按钮加上JavaScript处理函数。另一个方法是在用户登录时检测XMLHttpRequest,再决定是提供Ajax版本还是常规基于form提交的版本。   2、可用性考虑   围绕着Ajax应用的大部分问题都是很普通的问题。例如,让用户知道他们的输入已经被注册并处理,是很重要的,因为在XMLHttpRequest处理过程中并不能提供通常的漏斗旋转光标。一种方法是将“确认”按扭上的文本替换为“正在更新中…”,以避免用户在等待响应时多次点击按钮。   另一个问题是,用户可能没有注意到他们正在观看的页面已经被更新。可以通过使用各种视觉技巧来将用户的眼光吸引到页面的更新区域。还有一个问题是通过Ajax更新页面打断了浏览器“退回前页”按钮的正常工作,地址栏中的URL不能反映页面的全部状态,并且不能使用书签功能。参见Resource章节中列出的网站地址上的文章来了解更多Ajax应用关于可用性方面的问题。   3、服务器负载   使用Ajax界面代替传统的基于form的界面可能戏剧性地增加传递到服务器的请求数量。例如,一个普通的Google搜索给服务器造成一次命中,并在用户确认搜索表单时发生。然而,Google Suggest,将会试图自动完成你的搜索词,在用户打字时将会往服务器发送多个请求。在开发一个Ajax应用时,要注意到你将会发送多少请求到用户器端,以及服务器的负载指标。你可以通过在客户端适当地缓存请求、与服务器响应来缓减负载压力。你也应该在设计Ajax应用时尽量在客户端处理更多的逻辑,而不用与服务器端通讯。   4、处理异步   一定要记住,没有任何东西可以保证XMLHttpRequest将会按照它们被发送的顺序来依次结束。实际上,你在设计系统时,脑子里应该始终假定它们不会按原来顺序结束。在购物车例子中,使用了一个最后更新的时间戳来保证最新的数据不会被改写。这个非常基本的方法可以在购物车场景中工作,但可能不能在其它情况下工作。在设计时刻就要考虑你该如何处理异步服务器响应。   结论   你现在应该对于Ajax的基本原则有了一个良好的了解,另外,你应该理解一些更高级的随Ajax方法而来的设计问题。创建一个成功的Ajax应用需要一系列的方法—从JavaScript UI设计到服务器端架构—但是你现在应该已经具备了需要使用到的Ajax核心知识。 <淘宝热门商品:
 

 

天使名妆 08日韩版秋季OL洋装平价针织2件包邮 时尚only韩国代购

 

 

自然美人 红酒面膜及口碑护理产品

来源:程序员网

小小豆叮

浅析Java语言中两种异常的差别

Java提供了两类主要的异常:runtime exception和checked exception。所有的checked exception是从java.lang.Exception类衍生出来的,而runtime exception则是从java.lang.RuntimeException或java.lang.Error类衍生出来的。   它们的不同之处表现在两方面:机制上和逻辑上。 一、机制上   它们在机制上的不同表现在两点:1.如何定义方法;2. 如何处理抛出的异常。请看下面CheckedException的定义: public class CheckedException extends Exception {  public CheckedException() {}  public CheckedException( String message )  {   super( message );  } }   以及一个使用exception的例子: public class ExceptionalClass {  public void method1()   throws CheckedException   {    // ... throw new CheckedException( “...出错了“ );   }  public void method2( String arg )   {    if( arg == null )    {     throw new NullPointerException( “method2的参数arg是null!” );    }   }  public void method3() throws CheckedException   {    method1();   } }   你可能已经注意到了,两个方法method1()和method2()都会抛出exception,可是只有method1()做了声明。另外,method3()本身并不会抛出exception,可是它却声明会抛出CheckedException。在向你解释之前,让我们先来看看这个类的main()方法: public static void main( String[] args ) {  ExceptionalClass example = new ExceptionalClass();  try  {   example.method1();   example.method3();  }  catch( CheckedException ex ) { } example.method2( null ); }   在main()方法中,如果要调用method1(),你必须把这个调用放在try/catch程序块当中,因为它会抛出Checked exception。   相比之下,当你调用method2()时,则不需要把它放在try/catch程序块当中,因为它会抛出的exception不是checked exception,而是runtime exception。会抛出runtime exception的方法在定义时不必声明它会抛出exception。   现在,让我们再来看看method3()。它调用了method1()却没有把这个调用放在try/catch程序块当中。它是通过声明它会抛出method1()会抛出的exception来避免这样做的。它没有捕获这个exception,而是把它传递下去。实际上main()方法也可以这样做,通过声明它会抛出Checked exception来避免使用try/catch程序块(当然我们反对这种做法)。   小结一下:   * Runtime exceptions:   在定义方法时不需要声明会抛出runtime exception;   在调用这个方法时不需要捕获这个runtime exception;   runtime exception是从java.lang.RuntimeException或java.lang.Error类衍生出来的。   * Checked exceptions:   定义方法时必须声明所有可能会抛出的checked exception;   在调用这个方法时,必须捕获它的checked exception,不然就得把它的exception传递下去;   checked exception是从java.lang.Exception类衍生出来的。   二、逻辑上   从逻辑的角度来说,checked exceptions和runtime exception是有不同的使用目的的。checked exception用来指示一种调用方能够直接处理的异常情况。而runtime exception则用来指示一种调用方本身无法处理或恢复的程序错误。   checked exception迫使你捕获它并处理这种异常情况。以java.net.URL类的构建器(constructor)为例,它的每一个构建器都会抛出MalformedURLException。MalformedURLException就是一种checked exception。设想一下,你有一个简单的程序,用来提示用户输入一个URL,然后通过这个URL去下载一个网页。如果用户输入的URL有错误,构建器就会抛出一个exception。既然这个exception是checked exception,你的程序就可以捕获它并正确处理:比如说提示用户重新输入。   再看下面这个例子: public void method() {  int [] numbers = { 1, 2, 3 };  int sum = numbers[0] numbers[3]; }   在运行方法method()时会遇到ArrayIndexOutOfBoundsException(因为数组numbers的成员是从0到2)。对于这个异常,调用方无法处理/纠正。这个方法method()和上面的method2()一样,都是runtime exception的情形。上面我已经提到,runtime exception用来指示一种调用方本身无法处理/恢复的程序错误。而程序错误通常是无法在运行过程中处理的,必须改正程序代码。   总而言之,在程序的运行过程中一个checked exception被抛出的时候,只有能够适当处理这个异常的调用方才应该用try/catch来捕获它。而对于runtime exception,则不应当在程序中捕获它。如果你要捕获它的话,你就会冒这样一个风险:程序代码的错误(bug)被掩盖在运行当中无法被察觉。因为在程序测试过程中,系统打印出来的调用堆栈路径(StackTrace)往往使你更快找到并修改代码中的错误。有些程序员建议捕获runtime exception并纪录在log中,我反对这样做。这样做的坏处是你必须通过浏览log来找出问题,而用来测试程序的测试系统(比如Unit Test)却无法直接捕获问题并报告出来。   在程序中捕获runtime exception还会带来更多的问题:要捕获哪些runtime exception?什么时候捕获?runtime exception是不需要声明的,你怎样知道有没有runtime exception要捕获?你想看到在程序中每一次调用方法时,都使用try/catch程序块吗? <淘宝热门商品:
 

145.00 元 

八心八箭瑞士钻石六爪旁镶钻经典钻戒指

 

268.00 元 

日本08超人氣減肥品 日本瘦身美白净脂素

来源:程序员网

小小豆叮

IC卡应用系统开发-(一)卡片读写

最近在开发一个基于CPU卡应用的J2EE项目,应用服务器用的是Weblogic,数据库是oracle, 开发工具jbuilder2005.系统架构是:客户端(pc/sc读写器)<-->应用服务器<-->加密机,应用服务器<-->数据库服务器. 客户端首先要安装IC读写器驱动(必须支持PC/SC规范),然后到pcscworkgroup网站下载文件installOCF.class和Reference_Impl.class. 运行>java installOCF 按照提示一步一步进行安装操作. 运行>java Reference_Impl 默认安装操作. 在安装完后将thirdparty\OCF1.2\lib\OCFPCSC1.DLL和thirdparty\OCF1.2\lib\OCFPCSCM.DLL拷贝到jdk的bin目录. 在jbuilder工程文件目录下建立opencard.properties文件,内容如下, # Configure the CardService Registry: # Use the service factory for the IBM MultiFunction Cards OpenCard.services = opencard.opt.util.PassThruCardServiceFactory #OpenCard.services = com.ibm.opencard.factory.MFCCardServiceFactory # OpenCard.services = com.slb.opencard.CyberflexAccess.CyberflexAccessCardServiceFactory # Configure the CardTerminal Registry: com.ibm.opencard.terminal.pcscmig.PcscMigCardTerminalFactory OpenCard.terminals = com.ibm.opencard.terminal.pcsc10.Pcsc10CardTerminalFactory # Configure Tracing. Detailed for all samples only: OpenCard.trace = opencard:8 在jbuilder中建立pcsc库. tools-->configure-->libraries 在user home下建立pcsc10,在required libraries选项卡中选择安装的目录下lib下的所有文件. 我用的读写期是某公司的,命令集与PBOC稍有改动,但基本格式是按照PCSC的. 我建立了3个文件: 接口文件:IReader.java,用于程序将来兼容其他读写器; 实现文件:ReaderMA,java,实现接口, 异常文件:ReaderException,用户捕获异常及错误处理. 实现文件如下: package com.microapp.application.reader; /** *

Title: 基于PC/SC标准的读写器实现

* *

Description:

* *

Copyright: Copyright (c) 2005

* *

Company: 我的软件公司

* * @author etd group * @version 1.0 */ import java.util.*; import opencard.core.*; import opencard.core.service.*; import opencard.core.terminal.*; import opencard.core.util.*; import opencard.opt.util.*; public class ReaderMA implements IReader { private static final int IFD_TIMEOUT = 5; // unit: seconds private static final int MAX_APDU_SIZE = 200; // unit: byte private static final int SW1_OK = (byte)0x90; private static final int SW2_OK = 0x00; private static final int RESPONSE_OK = 0x61; private static SmartCard sc = null; private static PassThruCardService ptcs = null; private static CardRequest cr = null; private static CommandAPDU capdu = null; private static ResponseAPDU rapdu = null; byte[] rbuf = null; public ReaderMA() { } /** * 复位 * @throws ReaderPCSCException */ public byte[] Reset() throws ReaderException { try { if (cr == null) { SmartCard.start(); cr = new CardRequest(CardRequest.ANYCARD, null, PassThruCardService.class); } cr.setTimeout(IFD_TIMEOUT); sc = SmartCard.waitForCard(cr); if(sc == null) { throw new ReaderPCSCException("did not get a SmartCard object!"); } CardID cardID = sc.getCardID(); byte[] atr = cardID.getATR(); capdu = new CommandAPDU(MAX_APDU_SIZE); ptcs = (PassThruCardService) sc.getCardService(PassThruCardService.class, true); return atr; } catch(CardTerminalException ex) { throw new ReaderException("Reset failed!" + ex.getMessage()); } catch(CardServiceException ex) { throw new ReaderException("Reset failed!" + ex.getMessage()); } catch(ClassNotFoundException ex) { throw new ReaderException("Reset failed!" + ex.getMessage()); } catch(OpenCardPropertyLoadingException ex) { throw new ReaderException("Reset failed!" + ex.getMessage()); } } /** * 下电 * @throws ReaderPCSCException */ public void ShutDown() throws ReaderException { try { if(sc.isStarted()) { sc.shutdown(); sc.close(); SmartCard.shutdown(); } sc = null; cr = null; } catch(CardTerminalException ex) { throw new ReaderException("Shutdown failed!" + ex.getMessage()); } } /** * 读随机数 * @return byte[] */ public byte[] getChallenge() throws ReaderException { try { byte[] command = new byte[5]; command[0] = 0x00; // CLA command[1] = (byte)0x84; // INS command[2] = 0x00; // P1 command[3] = 0x00; // P2 command[4] = 0x08; // Lc capdu.setLength(0); capdu.append(command); ResponseAPDU response = ptcs.sendCommandAPDU(capdu); if(((response.sw1() == SW1_OK) && (response.sw2() == SW2_OK)) || (response.sw1() == RESPONSE_OK)) { return response.data(); } else { throw new ReaderPCSCException(response.sw1(), response.sw2()); } } catch(OpenCardException ex) { throw new ReaderPCSCException("Get challenge failed!" + ex.getMessage()); } } /** * SelectTicketApplication * @return boolean */ public boolean SelectApplication() throws ReaderPCSCException { try { byte[] command = new byte[7]; command[0] = 0x00; // CLA command[1] = (byte)0xA4; // INS command[2] = 0x00; // P1 command[3] = 0x00; // P2 command[4] = 0x02; // Lc command[5] = 0x2F; command[6] = 0x01; capdu.setLength(0); capdu.append(command); ResponseAPDU response = ptcs.sendCommandAPDU(capdu); if(((response.sw1() == SW1_OK) && (response.sw2() == SW2_OK)) || (response.sw1() == RESPONSE_OK)) { return true; } return false; } catch(OpenCardException ex) { throw new ReaderPCSCException("Select Ticket Application failed!" + ex.getMessage()); } } /** * 外部认证 * @param data String * @return boolean : true: pass, false: error. */ public boolean ExternalAuthentication(byte[] data) throws IndexOutOfBoundsException, NumberFormatException, ReaderPCSCException { try { byte[] command = new byte[13]; command[0] = 0x00; // CLA command[1] = (byte)0x82; // INS command[2] = 0x00; // P1 command[3] = 0x02; // P2 command[4] = 0x08; // Lc command[5] = data[0]; command[6] = data[1]; command[7] = data[2]; command[8] = data[3]; command[9] = data[4]; command[10] = data[5]; command[11] = data[6]; command[12] = data[7]; capdu.setLength(0); capdu.append(command); ResponseAPDU response = ptcs.sendCommandAPDU(capdu); if((response.sw1() != SW1_OK) || (response.sw2() != SW2_OK)) { throw new ReaderPCSCException(response.getByte(0), response.getByte(1)); } return true; } catch(OpenCardException ex) { throw new ReaderPCSCException("Get challenge failed!" + ex.getMessage()); } catch(ReaderPCSCException ex) { throw new ReaderPCSCException(ex.getMessage()); } } /** * 内部认证 * @param data String * @return byte[] */ public byte[] IntenalAuthentication(byte[] data) throws IndexOutOfBoundsException, NumberFormatException, ReaderPCSCException { try { byte[] command = new byte[13]; command[0] = 0x00; // CLA command[1] = (byte)0x88; // INS command[2] = 0x00; // P1 command[3] = 0x01; // P2 command[4] = 0x08; // Lc command[5] = data[0]; command[6] = data[1]; command[7] = data[2]; command[8] = data[3]; command[9] = data[4]; command[10] = data[5]; command[11] = data[6]; command[12] = data[7]; capdu.setLength(0); capdu.append(command); ResponseAPDU response = ptcs.sendCommandAPDU(capdu); if(response.sw1() == 0x61) { command = new byte[5]; command[0] = 0x00; // CLA command[1] = (byte)0xC0; // INS command[2] = 0x00; // P1 command[3] = 0x00; // P2 command[4] = 0x08; // Lc capdu.setLength(0); capdu.append(command); response = ptcs.sendCommandAPDU(capdu); if((response.sw1() != SW1_OK) || (response.sw2() != SW2_OK)) { throw new ReaderPCSCException(response.sw1(), response.sw2()); } return response.data(); } else { throw new ReaderPCSCException(response.getByte(0), response.getByte(1)); } } catch(OpenCardException ex) { throw new ReaderPCSCException("Get challenge failed!" + ex.getMessage()); } catch(ReaderPCSCException ex) { throw new ReaderPCSCException(ex.getMessage()); } } /** * 读基本信息文件 * @return byte[] * @throws ReaderPCSCException */ public byte[] ReadBasicFile() throws ReaderPCSCException { try { byte[] command = new byte[5]; command[0] = 0x00; // CLA command[1] = (byte)0xb0; // INS command[2] = (byte)0x95; // P1 command[3] = 0x00; // P2 command[4] = 0x27; // Lc capdu.setLength(0); capdu.append(command); ResponseAPDU response = ptcs.sendCommandAPDU(capdu); if((response.sw1() != SW1_OK) || (response.sw2() != SW2_OK)) { throw new ReaderPCSCException(response.getByte(39), response.getByte(40)); } return response.data(); } catch(OpenCardException ex) { throw new ReaderPCSCException("Read base file failed!" + ex.getMessage()); } } /** * 读持卡人信息文件 * @return String * @throws ReaderPCSCException */ public byte[] ReadHolderFile() throws ReaderPCSCException { return null; } /** * 读基本信息文件 * @param record int * @return String * @throws ReaderPCSCException */ public byte[] ReadBasicInfo(int record) throws ReaderPCSCException { try { byte[] command = new byte[5]; command[0] = 0x00; command[1] = (byte)0xB2; command[2] = (byte)record; command[3] = (byte)0x84; command[4] = (byte)0x9C; capdu.setLength(0); capdu.append(command); ResponseAPDU response = ptcs.sendCommandAPDU(capdu); if((response.sw1() != SW1_OK) || (response.sw2() != SW2_OK)) { throw new ReaderPCSCException(response.sw1(), response.sw2()); } return response.data(); } catch(OpenCardException ex) { throw new ReaderPCSCException("Read ticket file error!" + ex.getMessage()); } } /** * 写基本信息文件 * @param record * @param data String * @throws ReaderPCSCException */ public boolean WriteBasicInfo(int record, byte[] data) throws ReaderPCSCException { try { byte[] command = new byte[161]; command[0] = 0x00; command[1] = (byte)0xDC; command[2] = (byte)record; command[3] = (byte)0x84; command[4] = (byte)0x9C; System.arraycopy(data, 0, command, 5, 0x9C); capdu.setLength(0); capdu.append(command); ResponseAPDU response = ptcs.sendCommandAPDU(capdu); if((response.sw1() != SW1_OK) || (response.sw2() != SW2_OK)) { throw new ReaderPCSCException(response.sw1(), response.sw2()); } return true; } catch(OpenCardException ex) { throw new ReaderPCSCException("Get challenge failed!" + ex.getMessage()); } catch(ReaderPCSCException ex) { throw new ReaderPCSCException(ex.getMessage()); } } 用opencard规范开发非常的简单,特别是处理返回数据,在responseAPDU内用sw1和sw22个方法用于返回sw1,sw2.数据是直接用response.data()即可.省缺了一般读写拼字节的过程. 在读写器之上我定义了一个基于应用的卡片类,AppCard,在这个类内实现卡片的具体操作. 如内部认证,外部认证,获取随机数等. public void reset() throws TicketException { try { byte[] batr = reader.Reset(); StringBuffer sb = new StringBuffer(); String s = new String(); for(int i = 0; i < batr.length; i++) { s = Integer.toHexString(batr[i]).toUpperCase(); if(s.length() == 1) { sb.append("0" + s); } else if(s.length() > 2) { sb.append(s.substring(s.length() - 2, s.length())); } else { sb.append(s); } sb.append(" "); } atr = sb.toString(); } catch(ReaderPCSCException rex) { throw new TicketException("Reset failed! " + rex.getMessage()); } } /** * ShutDown */ public void ShutDown() throws TicketException { try { reader.ShutDown(); } catch(ReaderException ex) { throw new TicketException("Shutdown failed!"); } } /** * 获取随机数 * @return String */ public String getChallenge() throws TicketException { try { StringBuffer sb = new StringBuffer(); String s = new String(); byte[] buf = reader.getChallenge(); for(int i = 0; i < buf.length; i++) { s = Integer.toHexString(buf[i]).toUpperCase(); if(s.length() == 1) { sb.append("0" + s); } else if(s.length() > 2) { sb.append(s.substring(s.length() - 2, s.length())); } else { sb.append(s); } } return sb.toString(); } catch(ReaderPCSCException rex) { throw new TicketException(rex.getMessage()); } } /** * 内部认证 * @param data String * @return String */ public String IntenalAuthentication(String data) throws TicketException { try { byte[] sendbuf = new byte[8]; sendbuf[0] = (byte)(Integer.parseInt(data.substring(0,2), 16)); sendbuf[1] = (byte)(Integer.parseInt(data.substring(2,4), 16)); sendbuf[2] = (byte)(Integer.parseInt(data.substring(4,6), 16)); sendbuf[3] = (byte)(Integer.parseInt(data.substring(6,8), 16)); sendbuf[4] = (byte)(Integer.parseInt(data.substring(8,10), 16)); sendbuf[5] = (byte)(Integer.parseInt(data.substring(10,12), 16)); sendbuf[6] = (byte)(Integer.parseInt(data.substring(12,14), 16)); sendbuf[7] = (byte)(Integer.parseInt(data.substring(14,16), 16)); StringBuffer sb = new StringBuffer(); String s = new String(); byte[] recvbuf = reader.IntenalAuthentication(sendbuf); for(int i = 0; i < recvbuf.length; i++) { s = Integer.toHexString(recvbuf[i]).toUpperCase(); if(s.length() == 1) { sb.append("0" + s); } else if(s.length() > 2) { sb.append(s.substring(s.length() - 2, s.length())); } else { sb.append(s); } } return sb.toString(); } catch(ReaderPCSCException rex) { throw new TicketException(rex.getMessage()); } catch(NumberFormatException ex) { throw new TicketException(ex.getMessage()); } catch(IndexOutOfBoundsException ex) { throw new TicketException(ex.getMessage()); } } /** * 外部认证 * * @param data String */ public boolean ExternalAuthentication(String data) throws TicketException { try { byte[] buf = new byte[8]; buf[0] = (byte)(Integer.parseInt(data.substring(0,2), 16)); buf[1] = (byte)(Integer.parseInt(data.substring(2,4), 16)); buf[2] = (byte)(Integer.parseInt(data.substring(4,6), 16)); buf[3] = (byte)(Integer.parseInt(data.substring(6,8), 16)); buf[4] = (byte)(Integer.parseInt(data.substring(8,10), 16)); buf[5] = (byte)(Integer.parseInt(data.substring(10,12), 16)); buf[6] = (byte)(Integer.parseInt(data.substring(12,14), 16)); buf[7] = (byte)(Integer.parseInt(data.substring(14,16), 16)); return reader.ExternalAuthentication(buf); } catch(ReaderPCSCException rex) { throw new TicketException(rex.getMessage()); } catch(NumberFormatException ex) { throw new TicketException(ex.getMessage()); } catch(IndexOutOfBoundsException ex) { throw new TicketException(ex.getMessage()); } } 利用该方法,在客户端实现了用户卡的读写,大大简化了开发,并运行稳定,性能完全预期要求. <淘宝热门商品:
 

39.00 元  

零售批发*洁丽雅毛巾专卖店*更齐更全更多*更专业

洁丽雅浴巾8464 无捻线,柔软吸水,140*70 390克

 

¥:15.50 

暖手宝,热水袋,手套,帽子批发零售.自封袋自粘袋不干胶袋

来源:程序员网

小小豆叮