春天里,百花香...
2007年6月12日
#
我习惯连代码和文章一起贴,现在空间用完了,重新注册了一个( http://www.blogjava.net/heyang ),欢迎大家访问。
摘要: 我们可以使用DOM来解析来自服务器端的XML反馈,但返回结果比较复杂时我们必须在XML文档中一个节点一个节点的向下钻探,而使用XPath(专门用于定位匹配模式的一个或多个节点的小语言)只要一行代码就能遍历多个节点。与使用DOM相比,使用XSLT和XPath编码所投入的精力要小得多,随着应用规模的增长,后者的优势会越来越显著。
阅读全文
摘要: 侧边栏静态树状菜单在WebApp中很常见,本文涉及了静态树状菜单的制作和显示控制。比较简单。
阅读全文
摘要: 如果WebApp的侧面菜单项较多时我们可以采用SlideBar的方式将部分菜单显示,大部隐藏,类似Visio中做得那样。本文讨论了Slidebar的做法和显示控制,比较简单。
阅读全文
摘要: 工字型布局中都有一个侧边菜单栏目用以导航,它们存在的一个普遍问题是:用户无法迅速的找到自己所处页面在整个网站中的位置。
当菜单项较多时这会演变成一个大问题,当用户需要刻意寻找网页标志来确定自己所处位置时,这已经说明网站给了客户一种迷宫的感觉,有流失客户的潜在可能性。很多网站采用了球拍式菜单来凸显当前所在页面,本文探讨了这种球拍式菜单的实现方式。
阅读全文
摘要: SQL注入攻击的基本原理,是从客户端合法接口提交特殊的非法代码,让其注入到服务器端执行业务的SQL中去,进而改变SQL语句的原有逻辑和影响服务器端正常业务的处理。SQL注入攻击是Web应用中一个重要的安全问题,虽然Java具备较高的安全性,但如果开发人员不注意,也有可能留下安全隐患,本文将对此展开一些粗浅的探讨,欢迎批评指正。
阅读全文
摘要: 将网页链接做成按钮形状是侧边菜单栏和顶端菜单栏常采用的形态,本文总结了四种常用链接按钮形态的CSS制法。
阅读全文
//*****************************************************
// 《飞翔》 话剧《切格瓦拉》插曲 词 黄继苏 曲 张广天
//*****************************************************
陆地淹没了,
你就在海上飞翔。
海洋干涸了,
你就在天上飞翔。
天雷滚动了,
你就在火里飞翔。
火焰熄灭了,
你就在苦难中飞翔。
过去倒下了,
你就在未来飞翔。
未来退却了,
你就在现在飞翔。
现在迟疑了,
你就在心中飞翔。
心灵败坏了,
你就在创造中飞翔。
飞翔,飞翔,
永远的飞翔。
飞翔,飞翔,
不朽的飞翔!
//*****************************************************
// 随遇而安方为福 曾国藩
//*****************************************************
人生世间,自有知识以来,即有忧患如意事。小儿叫号,皆其意有不平。自幼至少至壮至老,如意之事常少,不如意之事常多。虽大富贵之人,天下所仰慕以为神仙,而其不如意处各自有之,与贫贱人无异,特所忧虑之事异尔。故谓之缺陷世界,以人生世间无足心满意者。能达此理而顺受之,则可稍安。
//*****************************************************
// 治学之道 《中华处世绝学》 一三九页
//*****************************************************
治学之道,最紧要的是立下坚卓不俗的大志,立志是事业的大门,一个人追求的目标越高,他的学问长进就越快。
当然,仅有高大的志向是远远不够的。治学,还要有“只问耕耘,不问收获”的务实精神,避免奢谈,踏实认真。要明白学问的取得,不是一朝一夕的事情,必须勤学好问,持之以很。学问好比金字塔,基础越深越搏越好,这样才能在广播的基础上求得高精尖。做学问,必须重视读书的方法,不要贪多,而要专注于一书,力求吃透。同时,治学须避免门户之见,博采众长,兼收并蓄,为我所用,才能学贯中西,博古通今。而依赖于不俗的才学,一个人才可能为国立功,为己立德,为人立言,受到后人的敬仰。
//*****************************************************
// 子曰
//*****************************************************
君子博学而日参省乎己 则知明而行无过矣.
子曰:赐也,汝以予为多学而识之者与?对曰:然,非与?曰:解也!予一以贯之。
//*****************************************************
// 尼采:在世纪的转折点上 周国平著
//*****************************************************
大自然的星空,群星灿烂。哪最早闪现的,未必是最亮的星宿。有的星宿孤独的燃烧着,熄灭了,很久很久以后,它的光才到达我们的眼睛。
历史和文化的星空何尝不是如此呢?
谁终将声震人间,必长久深自缄默;谁终将点燃闪电,必长久如云漂泊。
一个精神贫乏,缺乏独特个性的人,当然不会遭受精神上危机的折磨。
许多人的所谓成熟,不过是被习俗磨去了棱角,变得世故而实际了。那不是成熟,而是精神的早衰和个性的夭亡。真正的成熟,应当是独特个性的形成,真实自我的发现,精神上的结果和丰收。
当一个人要靠作品来批准自己的一生,他在根基上就变得极为苛求了。
书籍,知识,他人的思想都只能拿来为我所用,而不应当成为目的本身。
伟大的思想,与美丽的女子有相同的趣味,绝不肯让萎靡的弱者来占有自己。
人只以勇敢和毅力所许可的限度接近真理。强者必须认识并肯定现实,正如弱者必须害怕和逃避现实一样。
一个人倘若有健全旺盛的内在生命力,他是不会屈服于悲观主义的,悲观主义是生命力衰退的表现,屈服于悲观主义有如屈服于霍乱,表明肌体已经患病。
一个人健康,他就向往人生的快乐;一个人羸弱,他就念念不忘死亡,就悲观厌世。一个要在世间有所建树的人最忌悲观主义“看破红尘--这是巨大的疲劳和一切创造者的末日。”
没有痛苦,人只能有卑微的幸福。伟大的幸福正是战胜巨大痛苦所产生的生命的崇高感。痛苦磨练了意志,激发了生机,解放了心灵。
热爱人生的人纵然比别人感受到更多更多强烈的痛苦,同时也感受到更多更强烈的生命之欢乐。与痛苦相对抗,是人生最有趣味的事。
假如你在伟大的事业中失败了,你自己因此便是失败了么?假如你们自己是失败了,人类因此便是失败了么?假如人类也是失败了,好吧,别在意!
坚强而沉重,或者坚强而阴郁,仍然不符合酒神精神。人生的伟大肯定者应该兼有坚硬的骨头和轻捷的足,和歌者,武士与自由精神为一体。他应当学会“神圣的舞蹈”,学会欢笑。
//*****************************************************
// 自悟
//*****************************************************
唯有才华具有穿透心灵和穿越时空的力量。
//*****************************************************
// 其它摘录
//*****************************************************
可以缺钱、可以缺吃、缺化,却不可以缺德、确信用、缺操守!公司可以缺资金、缺设备,确不可以确伦理、缺道德、缺人才!
世事复杂,干什么事都不是那么简单。要想在一生中有所作为,干一番事业,思想上必须有这样的准备:别怕麻烦,肯于吃苦,受些窝囊气也能挺得住。否则,遇到麻烦事,意外事,不顺心事就急躁,想逃避,不肯吃苦,不能耐心处理麻烦事;或者遇见不公就会生怒气,发牢骚而不再努力,这样的人事业难成。
平时外表懒散而身怀绝技的高手只存在于古龙的武侠小说里,平时不认真,关键时刻肯定掉链子。
你可以靠谎言暂时领先,可不能靠它领先一辈子。
生活是一面镜子,他照出了你的现实,别人对你不好,一定是自己的原因,决不是别人。
只有挣到钱、这才是男人成功的绝佳体现。有时候想,钱的确比文凭、文章等一切虚无的东西更实在,更能证明一个人。
问题是带人走出困境的最好的向导 危机是教人进行创造的最好的老师 看到问题就是看到出路 碰到危机就是碰到机会。对于勇视现实 不满现状 只求进取 冷静观察 深入分析 甚至敢于自绘败状 自觉接受挑战的人来说 问题就是希望 危机就是专辑。 或者简单地说,只有在危机中不能惊觉新转机的人,有真正的危机.
摘要: 模拟Blogjava制作的一个用CSS控制样式的表格,比较简单。
阅读全文
摘要: 工字型布局是Web中应用比较广泛的布局,它将整个页面分成页头,侧面导航栏,内容栏和页脚栏四部分,页头一般包括logo,网站标题等;侧面导航栏是导航菜单,根据客户的喜好可以放在左边也可以放在右边;内容是正文部分,左右也可以根据用户的喜好放置;页脚包括版权信息,联系我们等。根据content栏的宽度是否会随着浏览器的宽度改变可以将工字型布局分为固定两栏方式和可变两栏方式,本文讨论了这两种方式的制法。
阅读全文
摘要: 除了Div,Table,Form外,我们最常用的Html元素之一就是无序列表ul,使用它通常可以实现以下形式:
1.实现文本数据列表,这是无序列表的原始意图。
2.嵌套使用无序列表,以实现树状结构。
3.修改无序列表的样式,将它作为菜单使用。相对于用表格制作的菜单项,它修改起来比较方便,样式也很容易设置。
第三点就是本文的主要议题。
阅读全文
摘要: 一次将数据库设计三范式应用于表设计的实践过程,比较浅显。
阅读全文
摘要: 表单是Web应用中一个重要的组成部分,用户向服务器端提交数据主要依靠表单进行. 好的表单能帮助用户顺利的完成数据的填写, 不好的表单会让用户对填写过程充满困惑和挫折感.这些都会影响客户的心理,进而会影响客户对整个网站的感觉.
我觉得,前台的表单设计和后台的业务组件都很重要,和程序设计一样,表单的设计也要遵照一定的原则和规范.
设计一个良好的表单,程序员需要综合运用HTML,CSS,JavaScript等方面的知识,下面就是本人的一些关于表单设计的粗浅想法,斗胆拿出来和大家一起探讨探讨.
阅读全文
摘要: 本文就分页的理由,分页的方式和MySql,Oracle中两种不同的分页技术进行了一些阐述,比较浅显。
阅读全文
摘要: 此文是“Web页面表单域验证方式的改进”的续篇。
示例页面:登录页面
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ taglib uri="/WEB-INF/tld/struts-html.tld" prefix="html"...
阅读全文
摘要: 在基于Model2的应用中,控制层的类总会包含对业务层诸类的调用,业务层诸类不可避免的要产生各种异常,如果统一到控制层进行处理的话会导致代码变得庞大臃肿还有不少重复,这种的例子在Web应用中的Servlet和Action诸类中并不少见。
如果我们使用模板方法模式(Template Method Pattern)将业务处理和异常处理分开,能有效简化控制层诸类的代码,借用这种模式,我们可以把固定的异常处理代码放在基类中,而让子类来实现具体的业务,如果执行业务过程中出现异常如数据库无法连接,用户找不到等异常后,直接将异常抛出让基类来处理,这样做成功的把业务处理和异常处理分开到了子类和基类两种类中,涉及具体业务处理的子类代码得到了很大的简化,更方便阅读,修改和管理。
有点疑惑的是,现在还不确定这样做会有什么消极印象,如安全性或结构方面的,大家要是觉得有问题请不吝赐教。
阅读全文
摘要: 一般来说涉及数据库的应用中,表的主键有两种生成方案,一种是专门定义一个主键表,在其中放置一个自增长的字段为其它表提供主键;另一种是使用Oracle的sequence。这两种方案都有一定麻烦,Spring为此专门提供了一个ID增长器以简化具体步骤,下文就是它的相关使用方法的,使用的数据库是MySql5.
归纳
使用Spring的自增长ID生成器完成以下三步即可:
1)配置自增长id生成器,它需要一个数据源的支持。
2)根据配置将自增长id生成器注入DAO各类中。
3)使用nextStringValue,nextIntValue,nextLongValue方法得到ID。
阅读全文
注意:本文说到的log4j版本为1.2.15,使用的配置文件是属性文件(properties),如果这些与您的环境不符则请快速离开,以免耽误你的宝贵时间。
一.log4j在桌面程序中的配置
这个相对简单了,它的步骤就这样两步:
1)将log4j-1.2.15.jar引入到工程的lib目录中.
2)确保配置文件log4j.properties在程序的代码目录(如src目录,cfg目录)中,它编译后应该位于类路径classes中.
log4j.properties示例(可以拷贝使用):
- log4j.rootLogger=debug, stdout, R
-
- log4j.appender.stdout=org.apache.log4j.ConsoleAppender
- log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-
- # Pattern to output the caller's file name and line number.
- log4j.appender.stdout.layout.ConversionPattern=%d %5p [%t] (%F:%L) - %m%n
-
- log4j.appender.R=org.apache.log4j.RollingFileAppender
- log4j.appender.R.File=输出log文件.log
-
- log4j.appender.R.MaxFileSize=1000KB
- # Keep one backup file
- log4j.appender.R.MaxBackupIndex=1
-
- log4j.appender.R.layout=org.apache.log4j.PatternLayout
- log4j.appender.R.layout.ConversionPattern=%d %5p [%t] (%F:%L) - %m%n
要对这个文件进行修改的话,基本上改两个地方就行了。
一个是输出文件名称,一个是输出等级设置。
1) 输出文件名称:
log4j.appender.R.File=输出log文件.log
2) 输出等级:
log4j.rootLogger=debug, stdout, R
Debug说明只要是logger.debug以上的都记录
配置到这里,就结束了。下面请看如何在程序中使用log4j。
二.log4j的使用
1) 首先,那个类要用到log4j记录日志,就应该为类添加一个静态的成员变量loogger,示例如下:
- public class Main{
- private static Logger logger = Logger.getLogger(Main.class);
-
- public static void main(String[] args){
- logger.info("成员管理程序启动");
- new MemberMngCtrl();
- }
- }
2) 其次,你就可以使用logger.debug ,logger.info, logger.warn, logger.error, logger.fatal等函数(记录等级依次提高)来记录日志内容了,确实是很简单方便的。
三.log4j在Web工程中的配置
与桌面程序一样的是,properties文件也需要能被编译到classes(WEB-INF/classes/)中,建议将属性文件放在特定的目录下并设置为源码目录,另外放在WEB-INF\src下也不错。
这一步比前面稍多的是需要配置一个初始化log4j的initServlet,就是在一开始就启动的Servlet,代码如下:
- public class Log4jInit extends HttpServlet {
- private static final long serialVersionUID = -4499302208753939187L;
- static Logger logger = Logger.getLogger(Log4jInit.class);
-
- public void init(ServletConfig config) throws ServletException {
- String prefix = config.getServletContext().getRealPath("/");
- String file = config.getInitParameter("log4j");
- String filePath = prefix + file;
- Properties props = new Properties();
-
- try {
- FileInputStream istream = new FileInputStream(filePath);
- props.load(istream);
- istream.close();
-
- String logFile = prefix + props.getProperty("log4j.appender.R.File");
- props.setProperty("log4j.appender.R.File",logFile);
-
-
- PropertyConfigurator.configure(props);
- } catch (IOException e) {
- System.out.println("Could not read configuration file [" + filePath + "].");
- System.out.println("Ignoring configuration file [" + filePath + "].");
- return;
- }
- }
- }
然后,在Web.xml中配置一下,让它在一开始启动就可以了。
-
- <servlet>
- <servlet-name>log4j-init</servlet-name>
- <servlet-class>
- com.sitinspring.action.Log4jInit
- </servlet-class>
- <init-param>
- <param-name>log4j</param-name>
- <param-value>WEB-INF/classes/log4j.properties</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
全文完。
一般来说, 在创建一个应用程序之前,首先要决定这个应用程序的体系结构。应用程序体系结构(Application Architecture)由应用程序开发者设计,它指定了在各种各样的终端系统上,应用程序是如何组织在一起的。为了降低设计难度,大部分程序都以层(称为layer或level)的方式组织在一起,每一层都建立在它的下层基础上,使用下层提供的服务,下层对上层隐藏了许多服务实现的细节。这种方法几乎应用于整个计算机科学领域,也可以称为信息隐藏,数据类型抽象,数据封装,面向对象编程等。
分层即是对类进行一些规划,以流程中的类的用途和所处环节划分,把程序中将要用到的各个类分别归纳到各个包(目录)中。分层是对系统进行细分的第一步,它旨在将系统按具体功能和用途分解为相对独立的各个部分.如果说细分是将把难以解决的大问题分解成了各个容易解决的小问题的话,分层则是把解决同类小问题的类归纳到一起,这样程序的结构更加清晰,程序的可读性和可维护性越好,也更容易得到重用。
从大的尺度来讲,一个程序可粗略的分成三个层次:
界面层(UI layer),这是用户能直接感受到的,包含显示和控制两部分;
业务层(Business layer),其中包含了业务逻辑和业务处理;
持久层(Persistence layer),它用来将数据存储和将数据从持久层提取出来。
界面层(UI layer)中,包含两个层次:视图层View和控制层Controller.
视图层View是用户查看数据,输入和向用户输出结果的一层,这一层是用户唯一能够感受软件功能的窗口,它或者由Swing组件搭建(桌面系统或C/S系统中),或者由JSP搭建(B/S系统),它负责让用户输入数据和将控制层返回的数据显示给客户。其中返回的数据一般是领域对象的变体或者直接就是领域对象或其集合。在Web程序中jsp基本就属于这一层的。
控制层Controller是用来将界面和业务层联系在一起的,在系统的各层次中,应该和View层打交道一般只有Controller层, Controller层是View层和系统其它层次进行交互的中介者, View层越过中介者直接调用其它层次的行为应该尽量避免。
一般来说,为了减少耦合,提高程序的可维护性,我们一般采用MVC架构模式将业务层,视图层和控制层分开。
业务层(Business layer)中包含领域层 Domain,服务层 Service和实用工具层Util。
业务层是整个系统的关键部分,它主要由领域模型和业务逻辑组成,领域模型定义系统内相互作用的各个实体,业务逻辑则定义了领域模型所能执行的不同操作, 领域层的各个类代表了领域模型,而服务层的各个类代表了业务逻辑. 领域层和服务层是起点,其它各层都从这里起步.
领域层 Domain:领域对象是对现实世界业务处理对象的抽象和归纳,领域层中的类基本上都是实体(Entity)类,如员工管理系统中的Employee,学籍管理系统中的Student,借贷管理系统中的Contract等,系统的业务处理中用到那些实体对象,领域层中一般就应该有和这个实体对象相对应的实体类。这些类在刚开始设计时可能只有一些属性和对应的getter/setter方法,以后会不断的加入新的内容(主要是方法),如果有必要的话,可以为这些领域对象设计一些上层的抽象类或者接口,借助于泛型,反射,控制反转等高级技能能在一定程度上简化程序的编写过程。此外,领域层是程序的核心内容,因为其他层次都在很大程度上依赖Domain层的设计,如果这一层设计不够完善会使以后的工作步履蹒跚.
服务层Service:这一层就是为领域对象提供服务用的,领域对象一般不直接和表现层,持久层直接打交道而是通过服务层进行代理.服务层是UI层到持久层的中间通道,它处于上通界面下通持久层的中间环节,这个特性是使的这一层决定了软件功能的多少。
一般来说,UI层向服务层传入的是用户输入的一些参数,服务层进行验证,重组后向下层DAO传输;而服务层从Dao层收到的是领域对象或其集合,而它向UI层返回的是领域对象或者其集合的变体或者直接是领域对象或者其集合本身。Service诸类的实例在桌面程序和CS程序中一般作为Model的一个私有成员,而在Web程序中常常要用到时再创建出来。除领域层外,其余各层是在围绕它而设计.
实用工具层Util:这一层相对简单,它包含了各种工具类,类中包含的主要是静态函数和静态成员变量,这些类对共通的函数,变量进行了归纳,它旨在消除重复代码,降低主体代码的复杂程度.一般此层中类的复用程度很高.值得通过项目积累.
持久层(Persistence layer)是直接与持久介质打交道的层次,持久介质可以是常见的关系型数据库,文件甚至Web Service,它一般包含两个部分。
数据存储对象层(DAO层),sql语句一般写在这层中, 然后由它调用;DAO层是最低的一层,与持久介质直接打交道,它包含具体文件的位置,数据库连接等;
另一个部分就是持久介质,通常是关系型数据库。
Dao层中各个类一般作为Service的私有成员,供Service调用。
下图是各层间的位置关系图:
如何从需求中分析出诸个层次中的类呢,我们在大尺度上可以按照下面的步骤进行:
Domain the first:首先从业务流和业务规则中归纳总结出领域对象.
Service the second:为领域对象设计服务类。
Persistence the third:持久层的负责领域对象持久化到持久介质以及逆过程,它的设计在领域层和服务层之后,比较典型的持久层设计有数据库表的设计和ER图(实体关系图)的绘制.
View the last:最后设计表现层,表现层受领域层和服务层制约, 容易变化且易于修改,通常放在最后实现.
具体步骤如下
1.理解,分析,钻研需求,彻底了解你的客户想要什么,需要你做些什么.
2.将大系统分解成一个个子系统,细分出各个层次,搞清楚各层的任务。
3.分析业务逻辑,归纳出业务流.
4.从业务流和业务规则中总结出领域对象.
5.为领域层实现服务层.
6.以Domain层和Service层为核心设计表现层和持久层,直到形成完整的程序.
7.加入实用层消除重复代码,梳理结构和简化流程,.
8.限制跨层的调用.
软件开发过程中,唯一不变的就是变化。这是一句老生常谈,也就是说软件开发中永恒的主题就是变化。当你把代码都写好了,测试也完成了,准备交付的时候客户忽然要求你在指定时间做出变化,这种情况在外包行业中很常见;而对一些银行金融项目,边调研边开发边测试屡见不鲜;对于一些Web项目,从来就只有永远的Beta版,改来改去的事更是家常便饭。对此,程序员一定要求清晰的认识,抱怨只能是嘴上痛快,不解决实际问题。真要解决实际问题,非要动一番脑筋不可,如果合理使用了设计模式,反射或是Spring的IoC,便能变修改噩梦为一次有趣的智慧之旅。
首先我们看原始要求:客户要求将一批雇员名单存入到CSV和XML两种文件中去,以后还有可能增加别的文件格式,比如PDF,XLS等,虽然这是下一期的内容,但这一期应该考虑到变化,客户要求扩展性一定要好。
没问题,有了设计模式响应变化不难。这时我们可以用到模板方法模式:
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
先请看骨架抽象类:
- public abstract class FileMaker {
-
-
-
- private List<Employee> employees;
-
-
-
-
-
-
-
- public final void makeFile(List<Employee> employees,String fileName){
- setEmployees(employees);
- makeFile(fileName);
- }
-
-
-
-
-
- protected abstract void makeFile(String fileName);
-
- public final void setEmployees(List<Employee> employees) {
- this.employees = employees;
- }
-
- public List<Employee> getEmployees() {
- return employees;
- }
- }
很好,固定的函数和步骤都在抽象基类中写定了,再看两个具体实现类,它们要实现的就是makeFile函数而已。
- public class CSVFileMaker extends FileMaker{
- protected void makeFile(String fileName){
- try {
- BufferedWriter out = new BufferedWriter(new FileWriter(fileName));
-
- for(Employee emp:getEmployees()){
- String line="";
- line+=emp.getName()+",";
- line+=(emp.isMale()?"男":"女")+",";
- line+=emp.getAge()+",";
-
- out.write(line+"\r\n");
- }
-
- out.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- public class XMLFileMaker extends FileMaker{
- protected void makeFile(String fileName){
- try {
- Document document = DocumentHelper.createDocument();
- Element root = document.addElement("employees");
-
- for(Employee emp:getEmployees()){
- Element empElm=root.addElement("employee");
-
- Element nameElm=empElm.addElement("name");
- nameElm.setText(emp.getName());
-
- Element sexElm=empElm.addElement("sex");
- sexElm.setText(emp.isMale()?"男":"女");
-
- Element ageElm=empElm.addElement("age");
- ageElm.setText(String.valueOf(emp.getAge()));
- }
-
- OutputFormat format = OutputFormat.createPrettyPrint();
- format.setEncoding("GBK");
- XMLWriter writer = new XMLWriter(new FileWriter(fileName),format);
-
- writer.write(document);
- writer.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
这样昨完以后感觉很好,因为我们成功的把变化和不变分离开来,不变的部分放在了抽象基类中,而容易变化的部分放在了两个具体的子类中,这样如果再增加一种新文件格式,从抽象基类再扩展出一个子类即可。很好,这样就不怕变化了。客户对此也没有异议。
调用示例如下:
- List<Employee> emps=new ArrayList<Employee>();
- emps.add(new Employee("Andy",true,21));
- emps.add(new Employee("Bill",false,23));
- emps.add(new Employee("Cindy",true,25));
- emps.add(new Employee("Douglas",false,28));
-
- FileMaker fileMaker=new CSVFileMaker();
- fileMaker.makeFile(emps, "1.csv");
-
- fileMaker=new XMLFileMaker();
- fileMaker.makeFile(emps, "2.xml");
客户看到了我们的调用的例子,觉得应该更灵活一些,他说存成各种不同的文件是通过点击按钮来实现的,如果每个按钮的事件处理函数都要生成具体子类岂不是太死板了吗?这样做每个文件下载按钮的事件处理代码不是都不一样?
有点道理,如今理解到这一层的客户实在是不多见了。不过很容易满足他的需求,我们可以引入反射的方法:
- public static void main(String[] args) {
- List<Employee> emps=new ArrayList<Employee>();
- emps.add(new Employee("Andy",true,21));
- emps.add(new Employee("Bill",false,23));
- emps.add(new Employee("Cindy",true,25));
- emps.add(new Employee("Douglas",false,28));
-
- callByReflect("csv",emps,"1.csv");
- callByReflect("xml",emps,"2.xml");
- }
-
- public static void callByReflect(String type,List<Employee> emps,String fileName){
- try{
- Class cls=Class.forName("com.heyang."+type.toUpperCase()+"FileMaker");
- FileMaker fileMaker=(FileMaker)cls.newInstance();
- fileMaker.makeFile(emps, fileName);
- }
- catch(Exception ex){
- ex.printStackTrace();
- }
- }
因为按钮上的文字和类名是有关的,如下载CSV的按钮上就有CSV的文字,这可以通过正则表达式取道,再组合一下不就是类名了吗?csv到com.heyang.CSVFileMaker,xml到com.heyang.XMLFileMaker,其实变化就是三个字母而已。如果增加按钮,取出按钮中的三个字母再调用callByReflect函数即可,这个过程简直可以固化。
客户看到反射方法以后很是满意,没有意见了。待客户走后,项目经理把你拉到一边,说:
“你刚才的方法不错,确实很强,但看得懂反射并能灵活掌握的人水平要够一年经验才行,维护的活让一年经验的人去干太可惜了,最好改改,最好达到让新手也能掌握并修改的程度。”。
没办法,领导总有领导的考虑,他这么说也很合理,成本问题我可以不考虑,但如果把程序搞得复杂貌似NB,能让一些学艺不精的人产生云山雾罩的感觉,有时还能被人尊称一声“大侠”,但谁也不比谁傻多少,这声大侠不是白叫的,但是出了问题或是有了变化别人还是要找你,到头来还是给自己添乱,这些都是义务劳动,何苦来呢?还是应该改得容易些,让大家都能修改,我可不愿意半夜三更被人叫起来问问题。
用Spring的IoC就可以解决问题,写一个新类并配置到XML文件中对新手来说问题不大,这下可以让领导放心了,自己就更放心了。
IoC方案代码如下:
- public class Main {
- public static void main(String[] args) {
- List<Employee> emps=new ArrayList<Employee>();
- emps.add(new Employee("Andy",true,21));
- emps.add(new Employee("Bill",false,23));
- emps.add(new Employee("Cindy",true,25));
- emps.add(new Employee("Douglas",false,28));
-
- callByIoc("csv",emps,"1.csv");
- callByIoc("xml",emps,"2.xml");
- }
-
- public static void callByIoc(String type,List<Employee> emps,String fileName){
- try{
- ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
- FileMaker fileMaker=(FileMaker)ctx.getBean(type);
- fileMaker.makeFile(emps, fileName);
- }
- catch(Exception ex){
- ex.printStackTrace();
- }
- }
- }
Bean。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="csv" class="com.heyang.CSVFileMaker"/>
- <bean id="xml" class="com.heyang.XMLFileMaker"/>
- </beans>
好了。到这里问题就彻底结束了,终于满足了客户和上级的要求,可以回家睡个好觉了,不用担心别人打搅了。
态度改变一切,变化来了人总是要多做一些,心理当然是不愿意的,但抱怨或是消极抵制都不是解决问题之道;如果把它看做一个挑战的契机,凡事多思考一些,不但能解决问题,自己也会有所提高,这就是积极的态度带来的好处。
摘要: 在工程中经常有发送邮件的任务,如果使用JavaMail来发送邮件,用到的代码较多,过程和细节也相对复杂,而使用Spring的MailSender能相对简单方便些,这样使程序员能更快捷的完成邮件发送任务。下面请看示例代码:
注意在执行代码前,请确认已经将activation.jar,commons-logging-1.0.4.jar,mail.jar和spring.jar载入工程。...
阅读全文
在Web开发中,文本邮件发送的任务比较常见,我们可以利用它进行一些客户通知和异常通知,文本邮件发送一般用到JavaMail API,下面是一个我有时用到的邮件发送实用工具类,把其中一些参数修改一下就能为你所用。
注意:在执行代码前,请把mail.jar和activation.jar载入工程。
代码如下:
- package com.heyang;
-
- import java.util.Date;
- import java.util.Properties;
-
- import javax.mail.Address;
- import javax.mail.Message;
- import javax.mail.Session;
- import javax.mail.Transport;
- import javax.mail.internet.InternetAddress;
- import javax.mail.internet.MimeMessage;
-
-
-
-
-
-
- public final class MailUtil {
-
- private static final String SenderEmailAddr = "XXXXXXX@163.com";
-
-
- private static final String SMTPUserName = "XXXX";
-
-
- private static final String SMTPPassword = "XXXXXXX";
-
-
- private static final String SMTPServerName = "smtp.163.com";
-
-
- private static final String TransportType = "smtp";
-
-
- private static Properties props;
-
-
-
-
-
- private MailUtil() {
-
- }
-
-
-
-
- static {
- MailUtil.props = new Properties();
-
-
- MailUtil.props.put("mail.smtp.host", MailUtil.SMTPServerName);
-
- MailUtil.props.put("mail.smtp.auth", "true");
- }
-
-
-
-
-
-
-
- public static void sendMail(String emailAddr, String mailTitle,
- String mailConcept) {
-
- Session s = Session.getInstance(MailUtil.props, null);
-
-
- s.setDebug(false);
-
-
- Message message = new MimeMessage(s);
- try {
-
- Address from = new InternetAddress(MailUtil.SenderEmailAddr);
- message.setFrom(from);
-
-
- Address to = new InternetAddress(emailAddr);
- message.setRecipient(Message.RecipientType.TO, to);
-
-
- message.setSubject(mailTitle);
-
- message.setText(mailConcept);
-
- message.setSentDate(new Date());
-
- message.saveChanges();
-
- Transport transport = s.getTransport(MailUtil.TransportType);
-
- transport.connect(MailUtil.SMTPServerName, MailUtil.SMTPUserName,
- MailUtil.SMTPPassword);
-
-
- transport.sendMessage(message, message.getAllRecipients());
- transport.close();
-
- System.out.println("发送邮件,邮件地址:" + emailAddr + " 标题:" + mailTitle
- + " 内容:" + mailConcept + "成功!");
- } catch (Exception e) {
- System.out.println(e.getMessage());
- System.out.println("发送邮件,邮件地址:" + emailAddr + " 标题:" + mailTitle
- + " 内容:" + mailConcept + "失败! 原因是" + e.getMessage());
- }
- }
-
-
-
-
-
- public static void main(String[] args){
- MailUtil.sendMail("XXXXXX@gmail.com", "title", "concept");
- }
- }
在面向对象编程中,我们一般采用从顶向下的编程方式,即先设计类的层次,如View,Controller,Service,Dao,Domain,Util等,再完善各层中的类。在这个过程中,我发现按功能和形态来分,系统中类可分为以下几个类别:
1.实体类(Entity Classes):这种类一般是现实世界事物在代码世界中的抽象表示,和现实事物有着一一对应关系.存储到持久介质中时一般对应着一条记录.如MIS系统中常见的雇员类Employee,论坛系统中常见的Topic等.由于这些类可以直接从现实事物中归纳抽象得来,写出它们的框架代码一般相对方便容易,但要真正理顺实体类之间的关系需要投入不少精力,这些类一般处于Domain层中.
2.通道类(Plumbing Classes):这种类一般用于充当传输实体类的通道,在编程中,经常需要从持久层取出一个或多个实体类的对象或是将实体类的对象存储到持久层中,这种任务一般由通道类来完成.它们一般由Service或是Dao层中的类来承担.这些类一般不保存状态,对外界来说,它们的对外接口(Public Interface)一般比具体的实现重要,在数量较多时,也经常抽象出一些上层的抽象类或是接口出来以方便调用.
3.辅助类(Assistant Classes):这些类一般起辅助任务,一般可以把共通的处理和变量放在其中供其他层次类调用,这样做一能避免散弹式修改,二能减少重复代码,三能提高代码复用度.辅助类一般放在Util包中.
4.框架类(Framework Classes):这些类一般由固定的框架提供,程序员不能改变.在类的层次上它一般处于界面和业务层之间,即控制层的位置,jsp/servlet中的Servlet,Struts1,2中的Action都是这样的类,它承担了接受用户输入,并展示业务处理的结果的任务.
一个技术人员要生存,要发展,要成一番事业,必须遵循一定固定的法则,若逆天而行而不自觉,轻则徒劳无功,白费精力;重则无法立足,庸碌一生。因此把握住自己的发展之道是技术人首要的大事,只有走在正确的道路上,前进才有意义。
一个技术人员,首先要固本培元,什么是技术人的根本呢?无论语言,框架和技术如何发展,数据结构和算法都是其核心内容,所谓万变不离其宗,有了良好的数据结构和算法的根基,接受并掌握一个新兴事物不过旬月时间,若没有而盲目跟随,事倍而功半矣。另外面向对象的精髓也要把握,从根本上来讲,任何现代框架其核心思想还是没有超越面向对象的范畴,都是面向对象的继承和发展,理解掌握了面向对象的思想,就把握住了框架根本性的东西,学习掌握起来就更容易把握其本质.
其次,技术人员必须把握主流技术方向才不至于迷失自己。若在支流中迷失自己恐有空执屠龙之技无用武之地之忧,古代也许还能自娱自乐,现代社会这样做温饱都无法解决,房子,车子,孩子更是白扯;只有置身主流,才能继续奋斗下去。当前的主流技术方向,无非SSH(Struts1/2,Spring,Hibernate)而已,彻底弄清楚它们,才有安身立命之本.君不见诸多招聘广告,均写SSH乎.这三项其实也不好掌握,尤其Hibernate,掌握不精深也不行,有些大侠也曾阴沟里翻过船。
其三,技术人员要乐于善于总结提高,对于已经掌握的内容,要及时归纳总结到纸面上,这样做一能梳理脉络,让自己掌握得更全面细致;二能查漏补缺,发现以前忽视或是未接触过的领域;三能求其友声,放在博客上供大家分析阅读讨论,弥补自己的不足.有此三益,于己于人都是一件大好事,何乐而不为呢?
其四,技术人员要展示自己的能力和价值,应该具备自己的产品,它可以用来巩固和展现自己的实力,在产品的研发过程中,技术人员能把自己的知识智慧实用化,可避免走入象牙塔之患;外界也能通过产品来了解发掘自己.这也是一件于己于人都有利的事情.
其五,技术人员应该具备完整的思想体系,有自己独到的见解并能有所突破创新. 人云亦云无异于鹦鹉学舌,有何能哉? 要想上一个层次,必须鲤鱼跃龙门. Gosing和Kaven两人可作为最好的榜样。
最后,广博的知识不可少.拘泥于一处难免死钻牛角尖,很多情况下换一种思维顿时有拨云见日之感,如有闲暇,技术人员应该跳出圈子,广采能用之材为我所用.
摘要: 称球问题经常是面试中的常客,这里我用做了一个称球的程序,主要的方法就是递归和扫描,贴出来请大家指正。
阅读全文
摘要: /** *//**
* 二叉树节点类
* @author HEYANG
* @since 2008-7-26 下午02:59:06
*/
class Node<T extends Comparable> {
 ...
阅读全文
摘要: 这是一个美国IT企业的面试题,原题大意是从一个文件中读取出可连通的城市对,给出两个城市,判断是否可连通,如果可连通就输出yes,不可连通就输出no,否则给出命令行帮助。
其实判断连接状态不用遍历图,用蔓延法即可,具体做法就是从起始城市开始,依次改变其周边连通城市的连通状态,再从周边开始向周边连通城市蔓延,如果能蔓延到结束城市的周边可连通城市,则说明两个城市是完全可连通的。这种做法和多米诺骨牌效应很像。我姑且称之为蔓延法。
阅读全文
找出以下字符串=符号后面对应的属性值
"职务=GM 薪水=50000 , 姓名=职业经理人 ; 性别=男 年龄=45 ";
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** *//**
* 用正则表达式找出每个属性对应的值
* @author HEYANG
* @since 2008-7-23 下午08:12:45
*/
public class RegexFindProperty {
public static void main(String[] args) {
String input = "职务=GM 薪水=50000 , 姓名=职业经理人 ; 性别=男 年龄=45 ";
// =号和空白符之间是非空格字符,这种写法比去分开组合字母,数字和汉字的方式要快捷
Pattern pattern = Pattern.compile("=(\\S+)\\s*");
// 用Pattern类的matcher()方法生成一个Matcher对象
Matcher m = pattern.matcher(input);
// 使用find()方法查找第一个匹配的对象
boolean result = m.find();
// 使用循环找出模式匹配的内容打印
while (result) {
// 取得匹配的结果
String replaceStr = m.group(1);
System.out.println("匹配的属性等于=" + replaceStr);
result = m.find();
}
}
}
摘要: package com.sitinspring.datetime;
import java.util.ArrayList;
import java.util.List;
public class MonthlyCalendar{
private static f...
阅读全文
摘要: 输出示例:
当前日期时间为:2008.07.18 10:48:57
当前日期为:2008.07.18
当前日期为:2008.7.18
当前时间为:10:48:57
2008.07.05与2008.07.18之间相隔:13天
当前年月为:2008.07
本月第一天为周2
本月有31天
阅读全文
Comparator的具体实现类
public class AgeComparator implements Comparator {
public int compare(Object op1, Object op2) {
Employee eOp1 = (Employee) op1;
Employee eOp2 = (Employee) op2;
// 按年龄排序
return eOp1.getAge()-(eOp2.getAge());
}
}
public class NameComparator implements Comparator {
public int compare(Object op1, Object op2) {
Employee eOp1 = (Employee) op1;
Employee eOp2 = (Employee) op2;
// 按姓名排序
return eOp1.getName().compareTo(eOp2.getName());
}
}
public class SalaryComparator implements Comparator {
public int compare(Object op1, Object op2) {
Employee eOp1 = (Employee) op1;
Employee eOp2 = (Employee) op2;
// 按薪水排序
return eOp1.getSalary()-(eOp2.getSalary());
}
}
Employee类:
public class Employee{
protected String name;
protected int age;
protected int salary;
public Employee(String name,int age,int salary){
this.name=name;
this.age=age;
this.salary=salary;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
}
测试:
public class Main{
public static void main(String[] args){
List<Employee> employees=new ArrayList<Employee>();
employees.add(new Employee("Andy",21,2000));
employees.add(new Employee("Felix",21,3000));
employees.add(new Employee("Bill",35,20000));
employees.add(new Employee("Helen",21,10000));
employees.add(new Employee("Cindy",28,8000));
employees.add(new Employee("Douglas",25,5000));
// 按名称排序
Collections.sort(employees,new NameComparator());
display(employees);
// 按年龄排序
Collections.sort(employees,new AgeComparator());
display(employees);
// 按薪水排序
Collections.sort(employees,new SalaryComparator());
display(employees);
}
public static void display(List<Employee> employees){
for(Employee e:employees){
System.out.println("雇员名="+e.getName()+" 年龄="+e.age+" 薪水="+e.getSalary());
}
System.out.println();
}
}
输出:
雇员名=Andy 年龄=21 薪水=2000
雇员名=Bill 年龄=35 薪水=20000
雇员名=Cindy 年龄=28 薪水=8000
雇员名=Douglas 年龄=25 薪水=5000
雇员名=Felix 年龄=21 薪水=3000
雇员名=Helen 年龄=21 薪水=10000
雇员名=Andy 年龄=21 薪水=2000
雇员名=Felix 年龄=21 薪水=3000
雇员名=Helen 年龄=21 薪水=10000
雇员名=Douglas 年龄=25 薪水=5000
雇员名=Cindy 年龄=28 薪水=8000
雇员名=Bill 年龄=35 薪水=20000
雇员名=Andy 年龄=21 薪水=2000
雇员名=Felix 年龄=21 薪水=3000
雇员名=Douglas 年龄=25 薪水=5000
雇员名=Cindy 年龄=28 薪水=8000
雇员名=Helen 年龄=21 薪水=10000
雇员名=Bill 年龄=35 薪水=20000
摘要: 回溯法有“通用的解题法“之称。用它可以系统的搜索一个问题的所有解或任一解。会所法是一个既带有系统性又带有跳跃性的搜索算法,他在包含问题的所有解的解空间树中,按照深度有限的策略,从根节点出发搜索解空间树,算法搜索至解空间树的任一节点时,总是先判断该节点是否肯定不包含问题的解。如果肯定不包含,则跳过对该节点为根的子树的系统搜索,逐层向其祖先节点回溯,否则进入该子树,继续按照深度优先的策略进行搜索。回溯法在用来求问题的任一接时,只要搜索到问题的一个解就可以结束。
这种深度优先的解的算法称为回溯法,它适合于解一些组合数较大的问题。
用回溯法解n皇后问题时,可以用一棵完全n叉树来表示其解空间。剪去不满足行列和斜线攻击的子树后,剩下的就是问题的解答。
阅读全文
摘要: package com.sitinspring.roundtable;
/** *//** *//** *//**
* 循环链表节点类
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-7-1-...
阅读全文
摘要: package com.sitinspring;
/** *//**
* 单链表节点类
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-7-1-下午10:42:49
* @param&nb...
阅读全文
摘要: 代码:
package com.sitinspring;
import java.util.Arrays;
/** *//**
* 泛型动态数组类,以数组为数据容器实现动态数组的功能
* @author: sitinspring(junglesong@gmail.com)
*&nbs...
阅读全文
摘要: package com.sitinspring;
/** *//**
* 整形数组实用类,能求两数组的并交差集,不借助于集合类
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-6-24-下午10:13:33
&...
阅读全文
摘要: 判断集合中存在重复是常见编程任务之一,当集合中数据量比较大时我们通常希望少进行几次扫描,这时双重循环法就不可取了。
位图法比较适合于这种情况,它的做法是按照集合中最大元素max创建一个长度为max+1的新数组,然后再次扫描原数组,遇到几就给新数组的第几位置上1,如遇到5就给新数组的第六个元素置1,这样下次再遇到5想置位时发现新数组的第六个元素已经是1了,这说明这次的数据肯定和以前的数据存在着重复。这种给新数组初始化时置零其后置一的做法类似于位图的处理方法故称位图法。它的运算次数最坏的情况为2N。如果已知数组的最大值即能事先给新数组定长的话效率还能提高一倍。
阅读全文
摘要: package com.sitinspring;
/** *//**
* 从8*8的方阵中找出最大价值轰炸目标,轰炸范围为9格
*
* @author sitinspring(junglesong@gmail.com)
* @since 2008-6-17...
阅读全文
摘要: 求两字符串的公共子串,如abc123与123456的公共字串为123,基本想法是在长的字符串前面加上长度等于短字符串的空格前缀,然后拿短字符串与新字符串挨个匹配,匹配上的置上匹配字符,否则置上空格,这样的新串就包含了匹配字串和空格,再劈分放入set即可,重复的元素会被set略过去。
阅读全文
下面的代码涉及判断数组元素是否存在重复,要求时间复杂度为O(1)。
这样的题肯定不能用双循环比较,这样太慢,用hashcode判断是正道,使用现成的hashset更能简化代码。
代码如下:
package com.sitinspring;
import java.util.HashSet;
import java.util.Set;
/** *//**
* 数组重复测试,要求时间复杂度为O(n)
* @author sitinspring(junglesong@gmail.com)
* @since 2008-6-11 上午11:12:53
* @vsersion 1.00 创建 sitinspring 2008-6-11 上午11:12:53
*/
public class ArrayDuplicateTest{
/** *//**
* 构造函数
*
*/
public ArrayDuplicateTest(int[] arr){
System.out.print("数组:");
for(int temp:arr){
System.out.print(temp+",");
}
if(hasDuplicateItem(arr)==false){
System.out.println("无重复结果");
}
else{
System.out.println("有重复结果");
}
}
/** *//**
* 取得检测结果
* @param arr
* @return
*/
private boolean hasDuplicateItem(int[] arr){
Set<Integer> set=new HashSet<Integer>();
for(int i:arr){
if(!set.add(i)){
return true;
}
}
return false;
}
public static void main(String[] args){
int[] arr1={1,2,3,4,5};
new ArrayDuplicateTest(arr1);
int[] arr2={1,2,3,4,5,5,53,43,42,2,454,6,5456,4534,4};
new ArrayDuplicateTest(arr2);
int[] arr3={1,2,3,4,5,767,4332,534,76,6583,356};
new ArrayDuplicateTest(arr3);
}
}
输出:
数组:1,2,3,4,5,无重复结果
数组:1,2,3,4,5,5,53,43,42,2,454,6,5456,4534,4,有重复结果
数组:1,2,3,4,5,767,4332,534,76,6583,356,无重复结果
摘要: package com.sitinspring;
import java.util.LinkedList;
import java.util.List;
/** *//**
* 将5,6,7,8,9添入到下面的算式中,使得他们的积有最大值
* _ _ _ * ...
阅读全文
摘要: Spring的AOP支持可以被用于从系统核心逻辑中分离交叉业务(cross-business)如日志,事务管理和安全等,使用AOP,你可以用各种功能层来覆盖核心业务层,这些功能层可以灵活的应用到你的系统中,甚至核心业务层都不知道它们的存在,这是一个强大的概念。
AOP(aspect-oriented programming)的核心就是动态代理,掌握它对于理解AOP尤为重要,犹如反射对理解IoC一样。
阅读全文
摘要: 本文将试图讨论一些决定一个程序员一生的核心的东西,这是个人对程序员生涯的一孔之见,只代表作者的个人想法,其中疏漏甚至错误之处在所难免,希望大家多提宝贵意见。
前言
丰厚的薪水,高端的职位和有成就感的事业是人人都想要的,而这些都取决于你每天的认真工作,努力学习和灵活做人上。日子就像一块块砖,你就像是一个泥瓦匠每天在堆砌着你的人生,最终砌出一个宏伟的大厦或是一幢低矮的小屋甚至是堆成一堆瓦砾全取决于你自己。
阅读全文
摘要: 在上文的表单验证解决方案中,有这样几个问题:
1。页面中存在自定义标签,这通不过W3C验证。
2。自定义标签不能写在Struts标签中,造成此解决方案在Struts工程中不适用。
3。验证的方式和页面内容混合在一起,没有分开。
4。原反馈信息span的id必须符合一定规范,这是额外的负担。
为了解决这些问题,我采取了如下步骤:
1。建立一种数据结构,让它容纳文本框id,反馈span的id,验证正则表达式,是否必须输入等四个信息。这样页面就不会混杂自定义标签,w3c验证和struts标签不支持的问题就解决了。
2。建立一个包含多个这种数据结构的数组,其中元素与要验证的文本域一一对应。此数组在需要验证时建立。这里虽然要多些一些JS代码,但验证信息更集中更容易修改了。
3。需要验证时取得数组元素,挨个验证即可,需要的信息都可以从数组元素中取得。整个过程可以库化通用化,页面不需要写验证。
如此做完后,我认为原先的问题基本得到解决了。下面请看具体代码
阅读全文
摘要: 对于实现文件上传功能来说,Commons-fileupload组件是一个不错的选择,本文使用它实现了单个文件及多个文件上传,这里将实现过程写出来与大家共享。
阅读全文
摘要: 权限设计是很多系统重要的组成部分,主要用于控制功能和流程,本文将几种常见的权限设计方案(权限系统的名都是自己起的)的基本设计写出来,其中不恰当处还请大家指出,我们来讨论一下.
阅读全文
下面的方法能解出九宫格,但对于更高阶只有理论可能性,因为耗时太长,不能作为通用解决方案。
输出:
2 7 6
9 5 1
4 3 8
package com.sitinspring;
public class SquarePuzzle{
/**
* 阶数
*/
private int n;
/**
* 方阵数组
*/
private Integer[] arr;
/**
* 平均值
*/
private int average;
public SquarePuzzle(int n){
this.n=n;
// 建立数组并得到平均值
arr=new Integer[n*n];
average=0;
for(int i=1;i<=n*n;i++){
arr[i-1]=i;
average+=i;
}
average=average/n;
// 递归查看
permutation(arr,0,arr.length);
}
private void permutation(Integer[] arr,int start,int end){
if(start<end+1){
permutation(arr,start+1,end);
for(int i=start+1;i<end;i++){
Integer temp;
temp=arr[start];
arr[start]=arr[i];
arr[i]=temp;
permutation(arr,start+1,end);
temp=arr[i];
arr[i]=arr[start];
arr[start]=temp;
}
}
else{
/*for(int i=0;i<end;i++){
System.out.print(arr[i]);
}
System.out.print("\n");*/
int i,sum=0;
for(i=0;i<n;i++){
sum+=arr[i];
}
if(sum!=average){
return;
}
// 查看是否纵横对角线值都相等
checkAndPrint(arr);
}
}
private void checkAndPrint(Integer[] arr){
Integer[][] arr2=new Integer[n][n];
int i,j,sum;
for(i=0;i<n;i++){
for(j=0;j<n;j++){
arr2[i][j]=arr[i*n+j];
}
}
// 横
for(i=0;i<n;i++){
sum=0;
for(j=0;j<n;j++){
sum+=arr2[i][j];
}
if(sum!=average){
return;
}
}
// 纵
for(i=0;i<n;i++){
sum=0;
for(j=0;j<n;j++){
sum+=arr2[j][i];
}
if(sum!=average){
return;
}
}
// 对角线
sum=0;
for(i=0;i<n;i++){
sum+=arr2[i][i];
}
if(sum!=average){
return;
}
// 对角线
sum=0;
for(i=0;i<n;i++){
sum+=arr2[i][n-i-1];
}
if(sum!=average){
return;
}
// 最终打印
for(i=0;i<n;i++){
for(j=0;j<n;j++){
System.out.print(arr2[i][j]+"\t");;
}
System.out.print("\n");;
}
System.out.print("\n");;
System.exit(0);
}
public static void main(String[] args){
new SquarePuzzle(3);
}
}
摘要: 本文书写了使用JFreeChart生成平面饼图,3D饼图,折线图和柱状图的示例代码,并附有代码下载.
阅读全文
摘要: 在上一篇“Web页面表单域验证方式的改进“中,我们通过把验证法则(正则表达式和是否必填字段)写在表单域中,将验证过程和验证法则成功的分离,从而减少了重复代码,使验证变得简单易行,在实际使用中,我们可以把验证过程放在一个JS文件中,对于全输入验证界面,在页面的表单验证部分只需要调用其中的checkForm函数就能进行有效验证,页面上不再需要书写重复性高的JS验证代码;对于复杂的表单,比如其中含有复选框或是需要两个文本框比较的地方,这种方法也可让你不写通用验证代码而把主要精力放在特殊的验证上。这些能减轻不少工作量,让繁琐的工作变得轻松愉快起来。
阅读全文
摘要: 我们对网页表单域验证常采取JS验证的方式,即取得某个表单域的值,然后对它进行正则表达式验证,如果通过则进行下一项验证,否则显示出错文字并置上焦点,这种做法很常见而且很凑效,但这样的页面写多了或者表单字段多了也容易让人烦躁,比如说这些验证除了具体的正则表达式不同,其他代码均高度相似,其中明显有大量的重复内容,而且表现和行为也未完全分离。能否将它改进一下呢?本文将探讨一下新的方法,这种做法的想法是把验证的正则表达式作为表单域属性的一部分,这样取值验证就融合到了一起,另外让提示span的id也和表单域ID关联起来,这样出错时能更快找到它。如此处理后验证的代码可以归纳到一个表单验证实用类中,大大减少了页面的JS代码量同时减轻我们的重复劳动。
阅读全文
摘要: 创建表格
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--><%@ page contentType="text/html; charset=UTF-8"%>
<!DOCTYP...
阅读全文
摘要: 表单鼠标掠过特效
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--> <table&...
阅读全文
摘要: Struts中,ActionServlet作为总控Servlet接受请求并转发到各Action,它的原理并不复杂,本文即展示了ActionServlet模拟实现过程。
阅读全文
摘要: 转化效果:
阿拉伯数字等于:0 大写汉字等于:零整
阿拉伯数字等于:3.14159 大写汉字等于:叁点壹肆壹伍玖
阿拉伯数字等于:101.3 大写汉字等于:壹佰零壹点叁
阿拉伯数字等于:10203040506070809 大写汉字等于:壹萬兆零贰佰零叁兆零肆佰零伍億零陆佰零柒萬零捌佰零玖整
阿拉伯数字等于:7897645 大写汉字等于:柒佰捌拾玖萬柒仟陆佰肆拾伍整
阿拉伯数字等于:500000001000000 大写汉字等于:伍佰兆零壹佰萬整
阿拉伯数字等于:2435685 大写汉字等于:贰佰肆拾叁萬伍仟陆佰捌拾伍整
阿拉伯数字等于:5345438976 大写汉字等于:伍拾叁億肆仟伍佰肆拾叁萬捌仟玖佰柒拾陆整
阅读全文
package com.sitinspring;
/**
* 全排列算法示例
如果用P表示n个元素的排列,而Pi表示不包含元素i的排列,(i)Pi表示在排列Pi前加上前缀i的排列,那么,n个元素的排列可递归定义为:
如果n=1,则排列P只有一个元素i
如果n>1,则排列P由排列(i)Pi构成(i=1、2、.、n-1)。
根据定义,容易看出如果已经生成了k-1个元素的排列,那么,k个元素的排列可以在每个k-1个元素的排列Pi前添加元素i而生成。
例如2个元素的排列是1 2和2 1,对3个元素而言,p1是2 3和3 2,在每个排列前加上1即生成1 2 3和1 3 2两个新排列,
p2和p3则是1 3、3 1和1 2、2 1,
按同样方法可生成新排列2 1 3、2 3 1和3 1 2、3 2 1。
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-3-25
*/
public class Permutation{
public static void main(String[] args){
String[] arr={"1","2","3"};
Integer[] arr02={4,5,6,7};
permutation(arr02,0,arr02.length);
}
public static void permutation(Object[] arr,int start,int end){
if(start<end+1){
permutation(arr,start+1,end);
for(int i=start+1;i<end;i++){
Object temp;
temp=arr[start];
arr[start]=arr[i];
arr[i]=temp;
permutation(arr,start+1,end);
temp=arr[i];
arr[i]=arr[start];
arr[start]=temp;
}
}
else{
for(int i=0;i<end;i++){
System.out.print(arr[i]);
}
System.out.print("\n");
}
}
}
摘要: 在Webapp编程中程序员经常要和前台页面打交道,CSS,HTML和JS等都是经常需要操作的内容,对于直接用户来说,这些就是他们印象中的整个程序,如果前台页面不美观或是使用不便将影响用户对程序的印象,因此程序员应该在前台上投入一些精力学习是值得的,在Web2.0时代来临之后更是如此。
下面就是一个通用网页表单的制作和验证过程。
阅读全文
摘要: 使用无序列表实现纵向和横向菜单,无序列表UL在网页中除显示树状结构外,最常用的用途就是作为导航栏的菜单使用,而它的样式通过CSS来设定,这种把样式与数据分离的做法既能有丰富多彩的效果,又易于变换,值得好好研究一下。
阅读全文
摘要: 利用正则表达式写了一个解析单句SQL的类,效果还可以,欢迎试用并提出宝贵意见。
阅读全文
摘要: Tomcat工程中的log4j配置,很简单。
阅读全文
摘要: Orcacle数据库的分页SQL语句分析,主要是使用rownum+子查询实现.同样的道理,对MySql可以使用limit子句,对DB2数据库可以使用rownumber()函数.附带为本人开发的免费工具SqlToolBox做个小广告。
阅读全文
摘要: SqlToolBox的前身是本人制作的SqlAnywhere,我制作此软件旨在为Java程序员在操作数据库时提供一个趁手的工具,此软件完全免费,且功能还在不断增加中.它绝对能帮你在撰写关于数据库的Java程序时帮上大忙。
阅读全文
摘要: 软件的核心任务不外乎是收集和整理数据,然后以用户需要的形式表现给他们而已,此外还有数据的存储,数据的传输等外围任务。
数据的收集,整理,表现,存储和传输就是软件的主要任务,它们也是程序员的主要工作内容,也是程序员编写代码的最终目的。
那么该如何编写代码让软件完成它的主要任务呢?编写代码的过程是否有规律可循?编写代码需要注意那些方面的问题?本人想就这些问题罗列自己一些粗浅的看法,并大家进行一些探讨。
阅读全文
摘要: 弄着玩的,没多少意义。
在此类的帮助下,你不必关注细节就能将对象持久化到XML文件以及读取,删除,只有更新麻烦一点,你需要先删除再添加。
阅读全文
摘要: 在把对象持久化到XML文件和从XML文件取出时,我们总是要书写冗长乏味的一个萝卜一个坑式的代码,类成员越多越觉得繁琐,本文利用反射简化了这个过程,欢迎大家指点。
阅读全文
摘要: 对对象字段设值取值是一个繁琐的过程,尤其当字段多时更加如此,本文讲述了使用反射机制加以简化的过程.
阅读全文
摘要: 领域层类可以理解为程序运行时数据流的功能单位,而服务层类是为领域层类提供服务的,常见的服务有增删改查等操作,在没有泛型之前,我们只能采用一个服务类为一个领域类提供服务的方案,如果说服务的性质类似,如都是增删改查等,则代码的功能重复性很高,维护起来也很麻烦.如果采用了泛型类,我们就可以把性质相似的服务类归纳成一个,很大程度上能简化编码.
阅读全文
摘要: 树状结构是生活中常见的数据结构,如公司等级,军队等级,类别归属,标签结构都是树状结构的具体例子,如何将树状结构持久化和从持久化中取出对于使用关系型数据库的应用一直比较麻烦,不如DB4O这样的数据库直接存取这样简单.本人用XML文件模拟关系型数据库,实现了树状结构存入文件及从文件中取出的完整功能,对为树状结构存取头疼的程序员有一定参考价值.
例中使用的数据结构为标签结构,如Java包括J2EE和J2SE,J2EE包括JSp,EJB等,j2se包括swing,awt,applet等.
阅读全文
摘要: JTable是Swing编程中很常用的控件,这里总结了一些常用方法以备查阅.
阅读全文
sitinspring(如坐春风)原创,转载请注明作者及出处.
要使用dom4j读写XML文档,需要先下载dom4j包,dom4j官方网站在 http://www.dom4j.org/
目前最新dom4j包下载地址:http://nchc.dl.sourceforge.net/sourceforge/dom4j/dom4j-1.6.1.zip
解开后有两个包,仅操作XML文档的话把dom4j-1.6.1.jar加入工程就可以了,如果需要使用XPath的话还需要加入包jaxen-1.1-beta-7.jar.
以下是相关操作:
一.Document对象相关
1.读取XML文件,获得document对象.
SAXReader reader = new SAXReader();
Document document = reader.read(new File("input.xml"));
2.解析XML形式的文本,得到document对象.
String text = "<members></members>";
Document document = DocumentHelper.parseText(text);
3.主动创建document对象.
Document document = DocumentHelper.createDocument();
Element root = document.addElement("members");// 创建根节点
二.节点相关
1.获取文档的根节点.
Element rootElm = document.getRootElement();
2.取得某节点的单个子节点.
Element memberElm=root.element("member");// "member"是节点名
3.取得节点的文字
String text=memberElm.getText();
也可以用:
String text=root.elementText("name");
这个是取得根节点下的name字节点的文字.
4.取得某节点下名为"member"的所有字节点并进行遍历.
List nodes = rootElm.elements("member");
for (Iterator it = nodes.iterator(); it.hasNext();) {
Element elm = (Element) it.next();
// do something
}
5.对某节点下的所有子节点进行遍历.
for(Iterator it=root.elementIterator();it.hasNext();){
Element element = (Element) it.next();
// do something
}
6.在某节点下添加子节点.
Element ageElm = newMemberElm.addElement("age");
7.设置节点文字.
ageElm.setText("29");
8.删除某节点.
parentElm.remove(childElm);// childElm是待删除的节点,parentElm是其父节点
9.添加一个CDATA节点.
Element contentElm = infoElm.addElement("content");
contentElm.addCDATA(diary.getContent());
三.属性相关.
1.取得某节点下的某属性
Element root=document.getRootElement();
Attribute attribute=root.attribute("size");// 属性名name
2.取得属性的文字
String text=attribute.getText();
也可以用:
String text2=root.element("name").attributeValue("firstname");
这个是取得根节点下name字节点的属性firstname的值.
3.遍历某节点的所有属性
Element root=document.getRootElement();
for(Iterator it=root.attributeIterator();it.hasNext();){
Attribute attribute = (Attribute) it.next();
String text=attribute.getText();
System.out.println(text);
}
4.设置某节点的属性和文字.
newMemberElm.addAttribute("name", "sitinspring");
5.设置属性的文字
Attribute attribute=root.attribute("name");
attribute.setText("sitinspring");
6.删除某属性
Attribute attribute=root.attribute("size");// 属性名name
root.remove(attribute);
四.将文档写入XML文件.
1.文档中全为英文,不设置编码,直接写入的形式.
XMLWriter writer = new XMLWriter(new FileWriter("output.xml"));
writer.write(document);
writer.close();
2.文档中含有中文,设置编码格式写入的形式.
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("GBK"); // 指定XML编码
XMLWriter writer = new XMLWriter(new FileWriter("output.xml"),format);
writer.write(document);
writer.close();
五.字符串与XML的转换
1.将字符串转化为XML
String text = "<members> <member>sitinspring</member> </members>";
Document document = DocumentHelper.parseText(text);
2.将文档或节点的XML转化为字符串.
SAXReader reader = new SAXReader();
Document document = reader.read(new File("input.xml"));
Element root=document.getRootElement();
String docXmlText=document.asXML();
String rootXmlText=root.asXML();
Element memberElm=root.element("member");
String memberXmlText=memberElm.asXML();
六.使用XPath快速找到节点.
读取的XML文档示例
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>MemberManagement</name>
<comment></comment>
<projects>
<project>PRJ1</project>
<project>PRJ2</project>
<project>PRJ3</project>
<project>PRJ4</project>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
使用XPath快速找到节点project.
public static void main(String[] args){
SAXReader reader = new SAXReader();
try{
Document doc = reader.read(new File("sample.xml"));
List projects=doc.selectNodes("/projectDescription/projects/project");
Iterator it=projects.iterator();
while(it.hasNext()){
Element elm=(Element)it.next();
System.out.println(elm.getText());
}
}
catch(Exception ex){
ex.printStackTrace();
}
}
摘要: 一个解析文本中的信息并向对象赋值过程的思考,比较浅显.
阅读全文
摘要: private的成员变量能被子类继承吗?回答是父类的所有成员变量包括私有成员变量都会被子类继承,private只是把可见性限制在改类内部的方法中而已,子类仍然可以通过父类的成员函数来访问不可见的从父类继承下来的私有成员.
这个机制对于复杂的类体系中保护父类不被滥用很有好处.
阅读全文
摘要: 本文分析了java.util.ConcurrentModificationException出现的原因及处置办法.
阅读全文
摘要: 本文是"使XML作为持久存储介质的解决方案 "的续文.
上文中对成员的CRUD都采用同步进行资源保护,这种方案实际上是保护过度,带来的消极影响是降低了程序的效率,在下面的例子中,我们应该使用读写锁对资源进行保护.关于读写锁的分析请见"读写锁的OO分析(http://www.blogjava.net/sitinspring/archive/2007/10/21/154652.html)".
...
阅读全文
线程回调方式我们已经在"
使用回调和线程处理一个耗时响应过程"文中进行了讲述,但是有些情况下用户希望在指定时间内返回一个结果,免得无休止的等待下去.这时我们需要使用"限时线程回调方式",它在原有线程回调的基础上加上了一个Timer以计算消耗的时间,如果时间期限到了任务还没有执行完的话即中断线程,示例代码如下:
package com.sitinspring;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
/** *//**
* 定时回调线程类
*
* @author sitinspring(junglesong@gmail.com)
*
* @date 2007-11-6
*/
public class TimedCallBackThread implements Runnable {
// 一秒的毫秒数常量
private final static int ONE_SECOND = 1000;
// 限制时间,以秒为单位
private final int waitTime;
// 已经流逝的时间
private int passedTime;
private Timer timer;
private Thread thread;
private MvcTcModel model;
private MvcTcView view;
public TimedCallBackThread(MvcTcModel model, MvcTcView view, int waitTime) {
this.model = model;
this.view = view;
this.waitTime = waitTime;
this.passedTime = 0;
// 创建并启动定时器
timer = new Timer(ONE_SECOND, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
timeListener();
}
});
timer.start();
// 创建并启动线程来完成任务
thread = new Thread(this);
thread.start();
}
private void timeListener() {
passedTime++;
// 动态显示状态
int modSeed = passedTime % 3;
if (modSeed == 0) {
view.getLabel2().setText("响应中");
} else if (modSeed == 1) {
view.getLabel2().setText("响应中..");
} else if (modSeed == 2) {
view.getLabel2().setText("响应中.");
}
// 如果流逝时间大于规定时间则中断线程
if (passedTime > waitTime) {
passedTime = waitTime;
thread.interrupt();
}
}
public void run() {
while (passedTime < waitTime) {
try {
Thread.sleep(10000);// 模拟一个耗时相应过程
timer.stop();// 任务完成,停止Timer
view.getLabel2().setText(model.getText2());
} catch (InterruptedException ex) {
timer.stop();// 线程中断,停止Timer
view.getLabel2().setText("在指定时间内未响应");
} catch (Exception ex) {
ex.printStackTrace();
}
return;
}
}
}
执行效果如下:
本文代码下载(点击第二个按钮):
http://www.blogjava.net/Files/sitinspring/TimedThreadCallBack20071106194506.rar
摘要: 一般来说,可以把系统粗略的分为三个层次,视图层,简称为View,它负责数据的输出和输入;业务层,简称为Model,它代表程序的实际业务;控制层,简称为Controller,处理界面的相应并调用业务层进行处理,有时把View和Controller两层合称为UI层。
在程序发展的历史上,MVC模式进过了多次演化,MVC1和MVC2是两种比较典型的模式,它们的区别主要在于View和Model的联系方式...
阅读全文
摘要: 我们有时会遇到对同一个内存区域如数组或者链表进行多线程读写的情况,一般来说有以下几种处理方式:
1.不加任何限制,多见于读取写入都很快的情况,但有时也会出现问题.
2.对读写函数都加以同步锁,比如使用singleton模式,这下问题是没了,但效率也下去了,比如说两个读取线程不是非要排队进入不可.
3.读写锁,安全和效率都得到了解决,特别合适读线程多于写线程的情况.也就是下面将要展现的模式.
读写锁的本意是分别对读写状态进行互斥区分,有互斥时才加锁,否则放行.互斥的情况有:
1.读写互斥.
2.写写互斥.
不互斥的情况是:读读,这种情况不该加以限制.
我们只要让锁对象知道当前读写状态就可以了,再根据情况进行锁定和解锁,然后再分情况进行锁定.请看代码
阅读全文
摘要: 这篇文章是"调度员,工人及任务的OO分析过程"的续篇.
上次的情况是由调度员主动分配任务,但有些情况下需要工人自动取得任务而不是由调度员分配,这时需要对线程进行通知,使用的主要方法就是对象的wait(),notify(),notifyAll()三个函数,它们都必须从同步方法(synchronized method)中调用.
阅读全文
摘要: 日常编码中,我们常需要为各种业务进行建模,为工厂中的任务调度建模就很有意思的,它的主要流程是基本是这样:
1.调度员将工件图纸交付给工人,这是任务的下达.
2.工人工作,完成工件,这是任务的进行过程.
3.工件完成后,工人将图纸和工件返还给调度员,或者到了确定的时间后由调度员去取.这是任务的完成.
4.重复上述三个步骤.
在这个流程中,还存在一些特征:
1.工人可以有多项任务,但他在一个时间只能做一个活,具体做那个由任务的优先级确定.
2.任务完成后要让调度员知道,现实中是工人来找调度员或者调度员找工人来实现的.
从上述情况分析,我们需要任务,工人,调度员三个类来完成建模,另外为了方便任务的存储和管理,还需要一个任务串类来辅助.
阅读全文
摘要: Ajax程序中,ResponseXml输出一般用字符串拼接方式,它有必须成对书写和节点关系不明两个缺点,本人对此提出了改进方案(利用dom4j).
阅读全文
摘要: 乱码是经常困扰非英语Web程序开发的程序员的问题,可喜的是网上的解决方案也不少,有从客户端想办法的,有从服务端想办法的.本人收集了一些材料,总结出自认为比较容易轻松的方案如下,希望与大家一起探讨.
阅读全文
摘要: 级联对象与相应XML之间的互相转化是在制作Web Service或者JMS程序时常遇到的问题,此文讨论了如何简化级联对象和相应XML的相互转化程序的方法.
阅读全文
摘要: 如何让Swing控件如JLabel,JButton等显示动态Gif图片
阅读全文
摘要: 如何使用JDK1.5编译Maven工程
阅读全文
功能:在一张大图片上截取一部分,比画笔精确,做着好玩的东西.
下载地址:
http://www.blogjava.net/Files/sitinspring/CutPictureExe.zip
见下图,红框处就是需要截取下来的部分.Result.jpg就是截取结果.
屏幕截图:
摘要: 本软件用于取得鼠标所在处的屏幕颜色,以RGB的形式显示出来.本作品与其它取色软件不同之处在于它使用热键Ctrl+PrintScreen可以将你要获取的颜色保存起来,去除了你记忆和换算的烦恼,也便于你多次采集颜色.在程序的下半部分就是你已经获取的颜色,从左到右分别是获取时间,颜色显示,颜色的细节,点击拷贝按钮将把颜色细节拷贝到剪贴板,删除按钮用于删除这一次的截取内容. 截取内容过多时将会出现滚动条以方便浏览. 这些功能对平面设计人员和网页制作人员应该是很有帮助的.
阅读全文
摘要: 用于控制系统音量大小,即可以可视化的方式(前台有效)控制,也可以系统热键的方式(在前台后台都一样有效.)控制.
阅读全文
摘要: 本软件用于批量更改图片文件的大小,可处理的图片包括常见的bmp,jpeg,jpg,gif,tif等.
阅读全文
摘要: "小闹钟定时器"是一款用于执行定时提醒任务的小工具,它可以设定在某个时刻按系统设定执行某个文件,比如Exe文件会被执行,MP3文件则会被播放等;它也可设定为弹出一段提示文字,比如说2:30需要给客户打电话等.具体执行周期有每日执行,每周执行,每月执行,每年执行等,只要设定好了时间,程序将会忠实的按照设定运行.有了它的帮助,相信会省却您的很多烦恼
阅读全文
摘要: 控件数组是VB提供的一个优秀的设计解决方案,它能很方便快捷的处理大批同类控件的响应和时间处理,但不知为什么在C#中这个优秀特性没有传承下来,甚为可惜,本文将要探讨就是如何在C# WinForm程序实现它.
总结起来,在C#中创建控件数组很简单,首先在类中创建一个控件类型的数组,然后初始化它,具体初始化是动态创建还是链接到已有控件可以根据情况自行选择,然后为数组元素添加事件,最后实现事件即可,在事件实现中即可以通过转化sender来得到相应控件.
阅读全文
摘要: "个人事务备忘录"使用C#编成,用于进行个人每天的事务管理,它分三个页面,"某日事务"页面用于添加,删除和转移事务;"日历"页面用于以日历形式列举每天需要做的和已经做完的事务;"事务详细"页面用于查询具体事务.
阅读全文
摘要: C#做的小工具"文件批量命名器"下载及介绍
阅读全文
摘要: 小工具"目录文件比较器"下载及介绍
阅读全文
MVC有MVC1和MVC2的区别,它们的区别在于MVC1中用Model来通知View进行改变,而MVC2中使用Controller来通知View.在桌面程序中一般采用MVC1,而Web程序多采用MVC2,这是因为web程序中,Model无法知道View的原因.
在Swing程序中,我们通常采用让View实现Observer接口,让Model继承Observable类来实现MVC1,而让Controller把它们创建及连接起来,具体代码如下:
public class XXXControl {
private XXXModel model = null;
private XXXView view = null;
public XXXControl() {
model = new XXXModel();
view = new XXXView();
model.addObserver(view);
}
.
.
.
}
而Model进过处理后得到了结果,它采用Observable的notifyObservers()方法来通知View进行改变,而View的public void update(Observable o, Object arg)方法将相应这一改变,它通过解析Observable类型的对象o得到处理结果,再进行具体的表现层改变.
粗看起来MVC各司其职,很完美,但还有不和谐的隐患:
1.View必须知道解析Model,造成了二者的耦合.
2.View非得实现Observer接口,Model非得继承Observable类,这个处理不是必要的.
3.这种模式只适合即时处理,即相应很快的处理,对于耗时过程并不适合.
4.由于Model中数据众多,很多时候我们还需要建立一个常量类来区分各种情况和决定View更新的地方,进一步加重了类之间的耦合程度.
综上,我觉得对于稍大的Swing程序,MVC2+线程回调方式更适合,它的主要处理是:
1.依然由Controller创建View和Model,它们担负的职责也和原来一样,但是View不实现Observer接口,Model不继承Observable类,它们该怎么样还是怎么样,而让Controller来充当它们之间的中介者.
2.如果是即时处理,可以在Controller中添加事件处理时就直接写来.如果是耗时处理,可以将View和Model的引用(或Model中元素的引用)传递给一个线程处理类,具体的运算和界面反应在线程处理类中完成.
下面是一个调用例子:
new FetchTablesThread(model.getDataSource(), view,schema).start();
下面是线程类的例子:
public class FetchTablesThread extends BaseThread {
private static Logger logger = Logger.getLogger(FetchTablesThread.class);
private String schema;
public FetchTablesThread(DataSource dataSource, SqlWindowView view,
String schema) {
super(dataSource, view);
this.schema = schema;
}
public void run() {
OutputPanel outputPanel = view.getTabbedPanel().getInputOutputPanel().getOutputPanel();
try {
if (dataSource.getDbtype().equals("mysql")) {
// Specail Process for MySql
new FetchTables4MySqlThread(dataSource, view, schema).start();
} else {
// Ordinary Process for other DB
List tables = dataSource.getTablesInSchema(schema);
if (tables.size() > 0) {
// Find tables under schema
view.getCatalogTablesPanel().getMultiTable().refreshTable(
tables);
outputPanel.showText(true);
String text = "Find " + tables.size()
+ " tables under schema:" + schema
+ " successfully!";
outputPanel.appendOutputText(text);
logger.info(text);
} else {
// Can't find tables under schema
outputPanel.showText(true);
String text = "Can't find any table under schema:" + schema;
outputPanel.appendOutputText(text);
logger.info(text);
}
}
} catch (Exception ex) {
outputPanel.showText(true);
String text = "Can't find any table under schema:" + schema+" and errorMsg="+ex.getMessage();
outputPanel.appendOutputText(text);
logger.info(text);
}
}
}
这样做有两个好处一是使程序结构松散化,适于修改,二是相对传统的MVC2,Controller中事件处理的代码也容易变得简单而清晰,可维护性更佳.
综上,我认为MVC2+线程回调方式是一种值得推荐的Swing桌面程序写法.
关于线程回调方式,您可以参考:
http://www.blogjava.net/sitinspring/archive/2007/06/28/126809.html关于MVC,您可以参考:
http://junglesong.yculblog.com/post.2665424.html
摘要: 本文参考了http://www.java2s.com/Code/Java/Swing-JFC/TableSortTest.htm的做法。主要处理是取得用户点击的列,得到按此列排序的新数组,删除原有元素,再把新数组加入进表格;如果已经排序,则进行逆序处理。处理完毕后,用户点击表头即可实现排序和逆序。
阅读全文
1.类代码如下
package com.junglesong.mvc.common.menu;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
/**
* 程序风格菜单
* @author junglesong@gmail.com
*
*/
public class StyleMenu extends JMenu {
// 程序的主框架
final JFrame mainFrame;
/**
* 构造函数
* @param text:菜单条文字
* @param frame:程序的主框架
*/
public StyleMenu(String text,JFrame frame) {
super(text);
mainFrame=frame;
addSubMenuItems();
}
/**
* 添加下级菜单项
*
*/
private void addSubMenuItems() {
// 取得系统当前可用感观数组
UIManager.LookAndFeelInfo[] arr = UIManager
.getInstalledLookAndFeels();
ButtonGroup buttongroup = new ButtonGroup();
for (int i = 0; i < arr.length; i++) {
JRadioButtonMenuItem styleMitem = new JRadioButtonMenuItem(
arr[i].getName(), i == 0);
final String className = arr[i].getClassName();
// 添加下级菜单项的事件相应
styleMitem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
UIManager.setLookAndFeel(className);
SwingUtilities.updateComponentTreeUI(mainFrame);
} catch (Exception ex) {
System.out.println("Can't Change Lookandfeel Style to "
+ className);
}
}
});
buttongroup.add(styleMitem);
this.add(styleMitem);
}
}
}
2.用法如下
JMenuBar menubar = new JMenuBar();
mainFrame.setJMenuBar(menubar);
......
menubar.add(Box.createHorizontalGlue());
JMenu styleMenu = new StyleMenu("Syle", mainFrame);
menubar.add(styleMenu);
......
例图:
我在工作过程中一般习惯把一些如代码段,文,下载文件档和图片等临时文件放在桌面上,这样能更方便一些,但是时间一长就容易积聚很多文件,密密麻麻的,删了吧又怕以后某时能用到,再找或者重做一个都很花时间,何况有些是不可恢复的.
为了解决这个问题,本人用微软的JS(非JavaScript,虽然语法很像)制作了一个脚本放在桌面上,感觉桌面文件过多时就可以选上拖曳到这个脚本上,它会按日期把选上的文件自动存放到一个备份目录里,这样找起来就方便了,也不会丢失重要信息,如果实在没用再删除备份中的目录或文件就可以了.
下面就是这个文件的代码,如果需要使用的话拷贝这段进入写字板,在另存为**.js的文件,放在桌面上即可使用,其中backupRoot清修改成你需要备份桌面文件的目录.
或者从这里下载:
http://www.blogjava.net/Files/sitinspring/deskSweep.rar
var backupRoot="E:\\Backup\\";// The folder you backup files
var target = backupRoot+getCurrTime()+"\\";// subfolder under backupRoot
var fso = WScript.CreateObject("Scripting.FileSystemObject");
if(!fso.FolderExists(target)){
fso.CreateFolder(target);
}
var args = WScript.Arguments; // Command arguments
var movedNum=0;
for(var i=0;i<args.length;i++){
storeFile(args(i),target);
}
WScript.Echo(movedNum.toString()+" Files have been backup to folder:"+target);
function storeFile(file,storeDir){
try{
if(fso.FileExists(file)) {
fso.MoveFile(file,storeDir);
}
else if(fso.FolderExists(file)) {
fso.CopyFolder(file+"*",storeDir);
fso.DeleteFolder(file);
}
movedNum++;
}
catch(e){
WScript.Echo(file+" can't be backup to folder:"+target);
}
}
function getCurrTime(){
var d, s = ""; // 声明变量。
d = new Date(); // 创建 Date 对象。
s += d.getYear()+ "-"; // 获取年份。
s += (d.getMonth() + 1) + "-"; // 获取月份。
s += d.getDate() ; // 获取日。
return(s); // 返回日期。
}
摘要: 在Maven工程中将资源文件打包
阅读全文
摘要: pom.xml基本元素介绍+mvn site初体验.
阅读全文
摘要: 使用Maven进行JUnit单元测试.
阅读全文
书分专业书与闲书.专业书是安身立命之本,建功立业之基,只要对自己有益的,在经济能力能承受的基础上应该多买多看.闲书基本只起业余调剂作用,可看可不看,切勿拿小菜当正餐,过分阅读只有迷惑心智,分散精力的后果.切不可沉迷于其中,所谓玩物丧志是也.
无论钱多寡,买书都应该精挑细选,确实是好书才买,买了坏书或一般书除了浪费钱财,耽误时间,还丧失了读好书的时间和机会,要知道在现在烦杂的社会生活中偷得浮生半日闲也是不容易的,何况以后看见还添堵,与其如此还不如锻炼出鉴别良莠的眼光.
专业书分精品与平装.精品是指那种老外写的,大部头的,全面详细的书,这种书虽贵,但读来收益很大,有微言大义,能常读常新,不像那些国内翻译,改写的,只抄个皮毛,买来如同鸡肋,还不如不买.
看书一定要动笔记点写点什么,所谓"不动笔墨不读书",道理说不上来,但确实有效,写在笔记本或博客都行.闲书可写读后感,锻炼以下文笔也不错.当然看书能把所读熟记于心最好,不要依赖笔记.依赖笔记会养成怠惰的习惯,真要用时一不能立即提出对策,二不一定能找到笔记在那干着急.笔记是用来总结回味的,不是随用随查的电子书籍.
书只能教你知识,不要期待书本或者人能指明你现在该自己干什么,他们只能大致指出一个方向,道还要靠自己去追寻.
摘要: 1.论坛和电视非常相象.
2.论坛的最大作用是开阔眼界,但开阔的程度取决于你自己.它犹如一个垃圾堆,找到件宝贝需要花费大量时间.
3.论坛的积分,精华,良好数都是虚幻的,应当视如草芥.
4.最好只在无聊时需要消遣时逛逛论坛,这样你花的时间和逛论坛所得在价值上才对等.
5.拿你自己的时间生命投资现实比投资虚幻要来得实在.
阅读全文
摘要: 1.急于出名赚钱的想法.
2.从众的想法.
3.破罐子破摔的想法.
4.早有蜻蜓立上头的想法.
阅读全文
本人在做一个在JTable上点击右键弹出菜单的程序时,遇到了这样的问题--菜单首项需要根据点击的表格行的"表名列"改变,这需要我们做一点小小的工作,其实就是
根据点击的位置推算所在行,好了,废话少说,看代码吧.
1.表格的建立过程
String[] headers = { "No", "Table Name" };
Object[][] cellData = null;
DefaultTableModel model = new DefaultTableModel(cellData, headers) {
public boolean isCellEditable(int row, int column) {
// 第N列可以编辑
/*
* if (column == N) { return false; }
*/
// 整个表都不能编辑
return false;
}
};
table = new JTable(model);
2.菜单的建立
popupMenu=new JPopupMenu();
tableNameItem=new JMenuItem("");
selectItem=new JMenuItem("Select SQL");
insertItem=new JMenuItem("Insert SQL");
deleteItem=new JMenuItem("Delete SQL");
updateItem=new JMenuItem("Update SQL");
hbmItem=new JMenuItem("hbm xml");
hbmPojoItem=new JMenuItem("Pojo Class");
popupMenu.add(tableNameItem);
popupMenu.addSeparator();
popupMenu.add(selectItem);
popupMenu.add(insertItem);
popupMenu.add(deleteItem);
popupMenu.add(updateItem);
popupMenu.addSeparator();
popupMenu.add(hbmItem);
popupMenu.add(hbmPojoItem);
3.菜单的弹出处理
table.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent evt) {
if (evt.isPopupTrigger()) {
// 取得右键点击所在行
int row=evt.getY()/table.getRowHeight();
/**
* 取得是表名的那一列
*/
int tableNameColumn=-1;
for(int i=0;i<table.getColumnModel().getColumnCount();i++){
TableColumn selColumn = table.getColumnModel().getColumn(i);
String columnHeader=(String)selColumn.getHeaderValue();
if(columnHeader.equals("Table Name")){
tableNameColumn=i;
break;
}
}
/**
* 取得表名并弹出菜单
*/
if(tableNameColumn!=-1){
/**
* 修改菜单首条的名称
*/
String tableName=(String)table.getValueAt(row,tableNameColumn);
tableNameItem.setText(tableName);
// 弹出菜单
popupMenu.show(evt.getComponent(), evt.getX(), evt.getY());
}
}
}
}
4.实现的效果如下:
sitinspring(http://www.blogjava.net)原创,转载请注明出处.