(彩云间娱乐娱乐)----C}f}p}到这一步要怎么进去啊没有什么选择可以选啊(新手)

这些天开发一个项目,服务器是tomcat,操作系统是xp,采用的是MVC架构,模式是采用Facade模式,总是出现乱码,自己也解决了好多天,同事也帮忙解决,也参考了网上众多网友的文章和意见,总算是搞定。但是好记性不如烂笔杆,所以特意记下,以防止自己遗忘,同时也给那些遇到同样问题的人提供一个好的参考途径:(一)&&&&JSP页面上是中文,但是看的是后是乱码:解决的办法就是在JSP页面的编码的地方&%@&page&language=&java&&contentType=&text/charset=GBK&&%&,因为Jsp转成Java文件时的编码问题,默认的话有的服务器是ISO-8859-1,如果一个JSP中直接输入了中文,Jsp把它当作ISO8859-1来处理是肯定有问题的,这一点,我们可以通过查看Jasper所生成的Java中间文件来确认(二)&&&&当用Request对象获取客户提交的汉字代码的时候,会出现乱码:解决的办法是:要配置一个filter,也就是一个Servelet的过滤器,代码如下:import&java.io.IOEimport&javax.servlet.Fimport&javax.servlet.FilterCimport&javax.servlet.FilterCimport&javax.servlet.ServletEimport&javax.servlet.ServletRimport&javax.servlet.ServletRimport&javax.servlet.UnavailableE/**&*&Example&filter&that&sets&the&character&encoding&to&be&used&in&parsing&the&*&incoming&request&*/public&class&SetCharacterEncodingFilter&implements&Filter&{&&&&/**&&&&&*&Take&this&filter&out&of&service.&&&&&*/&&&&public&void&destroy()&{&&&&}&&&&/**&&&&&*&Select&and&set&(if&specified)&the&character&encoding&to&be&used&to&&&&&*&interpret&request&parameters&for&this&request.&&&&&*/&&&&public&void&doFilter(ServletRequest&request,&ServletResponse&response,&&&&FilterChain&chain)throws&IOException,&ServletException&{&&&&request.setCharacterEncoding(&GBK&);&&&&//&传递控制到下一个过滤器&&&&chain.doFilter(request,&response);&&&&}&&&&public&void&init(FilterConfig&filterConfig)&throws&ServletException&{&&&&}}配置web.xml&filter&&filter-name&Set&Character&Encoding&/filter-name&&filter-class&SetCharacterEncodingFilter&/filter-class&&/filter&&filter-mapping&&filter-name&Set&Character&Encoding&/filter-name&&url-pattern&/*&/url-pattern&&/filter-mapping&如果你的还是出现这种情况的话你就往下看看是不是你出现了第四中情况,你的Form提交的数据是不是用get提交的,一般来说用post提交的话是没有问题的,如果是的话,你就看看第四中解决的办法。还有就是对含有汉字字符的信息进行处理,处理的代码是:package&dbJavaBpublic&class&CodingConvert{&&&&public&CodingConvert()&{&&//&}&public&String&toGb(String&uniStr){&&&&&String&gbStr&=&&&;&&&&&if(uniStr&==&null){&&&uniStr&=&&&;&&&&&}&&&&&try{&&&byte[]&tempByte&=&uniStr.getBytes(&ISO8859_1&);&&&gbStr&=&new&String(tempByte,&GB2312&);&&&&&}&&catch(Exception&ex){&&&&}&&&&&return&gbS&}&&&&public&String&toUni(String&gbStr){&&&&&String&uniStr&=&&&;&&&&&if(gbStr&==&null){&&&gbStr&=&&&;&&&&&}&&&&&try{&&&byte[]&tempByte&=&gbStr.getBytes(&GB2312&);&&&uniStr&=&new&String(tempByte,&ISO8859_1&);&&&&&}catch(Exception&ex){&&&&}&&&&return&uniS&}}你也可以在直接的转换,首先你将获取的字符串用ISO-8859-1进行编码,然后将这个编码存放到一个字节数组中,然后将这个数组转化成字符串对象就可以了,例如:String&str=request.getParameter(&girl&);Byte&B[]=str.getBytes(&ISO-8859-1&);Str=new&String(B);通过上述转换的话,提交的任何信息都能正确的显示。(三)&&&&在Formget请求在服务端用request.&getParameter(&name&)时返回的是乱码;按tomcat的做法设置Filter也没有用或者用request.setCharacterEncoding(&GBK&);也不管用问题是出在处理参数传递的方法上:如果在servlet中用doGet(HttpServletRequest&request,&HttpServletResponse&response)方法进行处理的话前面即使是写了:request.setCharacterEncoding(&GBK&);response.setContentType(&text/charset=GBK&);也是不起作用的,返回的中文还是乱码!!!如果把这个函数改成doPost(HttpServletRequest&request,&HttpServletResponse&response)一切就OK了。同样,在用两个JSP页面处理表单输入之所以能显示中文是因为用的是post方法传递的,改成get方法依旧不行。由此可见在servlet中用doGet()方法或是在JSP中用get方法进行处理要注意。这毕竟涉及到要通过浏览器传递参数信息,很有可能引起常用字符集的冲突或是不匹配。解决的办法是:1)&打开tomcat的server.xml文件,找到区块,加入如下一行:&URIEncoding=&GBK&&完整的应如下:&&Connector&port=&8080&&maxThreads=&150&&minSpareThreads=&25&&maxSpareThreads=&75&&enableLookups=&false&&redirectPort=&8443&&acceptCount=&100&&debug=&0&&connectionTimeout=&20000&&disableUploadTimeout=&true&&URIEncoding=&GBK&/&&2)重启tomcat,一切OK。需要加入的原因大家可以去研究&$TOMCAT_HOME/webapps/tomcat-docs/config/http.html下的这个文件就可以知道原因了。需要注意的是:这个地方如果你要是用UTF-8的时候在传递的过程中在Tomcat中也是要出现乱码的情况,如果不行的话就换别的字符集。(四)&&&&JSP页面上有中文,按钮上面也有中文,但是通过服务器查看页面的时候出现乱码:&&&&&解决的办法是:首先在JSP文件中不应该直接包含本地化的消息文本,而是应该通过&bean:message&标签从Resource&Bundle中获得文本。应该把你的中文文本放到Application.properties文件中,这个文件放在WEB-INF/classes/*下,例如我在页面里有姓名,年龄两个label,我首先就是要建一个Application.properties,里面的内容应该是name=&姓名&&age=&年龄&,然后我把这个文件放到WEB-INF/classes/properties/下,接下来根据Application.properties文件,对他进行编码转化,创建一个中文资源文件,假定名字是Application_cn.properties。在JDK中提供了native2ascii命令,他能够实现字符编码的转换。在DOS环境中找到你放置Application.properties的这个文件的目录,在DOS环境中执行一下命令,将生成按GBK编码的中文资源文件Application_cn.properties:native2ascii&?encoding&gbk&Application.properties&Application_cn.properties执行以上命令以后将生成如下内容的Application_cn.properties文件:name=\u59d3\u540d&age=\u5e74\u9f84,在Struts-config.xml中配置:&message-resources&parameter=&properties.Application_cn&/&。到这一步,基本上完成了一大半,接着你就要在JSP页面上写&%@&page&language=&java&&contentType=&text/charset=GBK&&%&,到名字的那个label是要写&bean:message&key=&name&&,这样的化在页面上出现的时候就会出现中文的姓名,年龄这个也是一样,按钮上汉字的处理也是同样的。(五)&&&&写入到数据库是乱码:解决的方法:要配置一个filter,也就是一个Servelet的过滤器,代码如同第二种时候一样。如果你是通过JDBC直接链接数据库的时候,配置的代码如下:jdbc:mysql://localhost:3306/workshopdb?useUnicode=true&characterEncoding=GBK,这样保证到数据库中的代码是不是乱码。如果你是通过数据源链接的化你不能按照这样的写法了,首先你就要写在配置文件中,在tomcat&5.0.19中配置数据源的地方是在C:\Tomcat&5.0\conf\Catalina\localhost这个下面,我建立的工程是workshop,放置的目录是webapp下面,workshop.xml的配置文件如下:&!–&insert&this&Context&element&into&server.xml&–&&Context&path=&/workshop&&docBase=&workshop&&debug=&0&reloadable=&true&&&&&&Resource&name=&jdbc/WorkshopDB&&&&&&&&&&&&&&&&auth=&Container&&&&&&&&&&&&&&&&type=&javax.sql.DataSource&&/&&&&ResourceParams&name=&jdbc/WorkshopDB&&&&&&&parameter&&&&&&&&name&factory&/name&&&&&&&&value&org.apache.commons.dbcp.BasicDataSourceFactory&/value&&&&&&/parameter&&&&&&parameter&&&&&&&&name&maxActive&/name&&&&&&&&value&100&/value&&&&&&/parameter&&&&&&parameter&&&&&&&&name&maxIdle&/name&&&&&&&&value&30&/value&&&&&&/parameter&&&&&&&&&&parameter&&&&&&&&name&maxWait&/name&&&&&&&&value&10000&/value&&&&&&/parameter&&&&&&&&parameter&&&&&&&name&username&/name&&&&&&&value&root&/value&&&&&&/parameter&&&&&&parameter&&&&&&&name&password&/name&&&&&&&value&&/value&&&&&&/parameter&&&&&&!–&Class&name&for&mm.mysql&JDBC&driver&–&&&&&&parameter&&&&&&&&&name&driverClassName&/name&&&&&&&&&value&com.mysql.jdbc.Driver&/value&&/parameter&&&&&parameter&&&&&&&&name&url&/name&&&value&&![CDATA[jdbc:mysql://localhost:3306/workshopdb?useUnicode=true&characterEncoding=GBK]]&&/value&&&&&&/parameter&&&&/ResourceParams&&/Context&粗体的地方要特别的注意,和JDBC直接链接的时候是有区别的,如果你是配置正确的化,当你输入中文的时候到数据库中就是中文了,有一点要注意的是你在显示数据的页面也是要用&%@&page&language=&java&&contentType=&text/charset=GBK&&%&这行代码的。需要注意的是有的前台的人员在写代码的是后用Dreamver写的,写了一个Form的时候把他改成了一个jsp,这样有一个地方要注意了,那就是在Dreamver中Action的提交方式是request的,你需要把他该过来,因为在jsp的提交的过程中紧紧就是POST和GET两种方式,但是这两种方式提交的代码在编码方面还是有很大不同的,这个在后面的地方进行说明。3以上就是我在开发系统中解决中文的问题,不知道能不能解决大家的问题,时间匆忙,没有及时完善,文笔也不是很好,有些地方估计是词不达意。大家可以给我意见,希望能共同进步。
moson 阅读(27) |
1.字节和unicode&&&&java内核是unicode的,就连class文件也是,但是很多媒体,包括文件/流的保存方式是使用字节流的。因此java要对这些字节流经行转化。char是unicode的,而byte是字节。java中byte/char互转的函数在sun.io的包中间有。其中ByteToCharConverter类是中调度,可以用来告诉你,你用的convertor。其中两个很常用的静态函数是&public&static&ByteToCharConverter&getDefault();&public&static&ByteToCharConverter&getConverter(String&encoding);&如果你不指定converter,则系统会自动使用当前的encoding,gb平台上用gbk,en平台上用8859_1。byte&&&〉char:&&你&的gb码是:0xc4e3&,unicode是0×4f60&&String&encoding&=&&gb2312&;&&byte&b[]&=&{(byte)’\u00c4′,(byte)’\u00e3′};&&ByteToCharConverter&converter&=&ByteToCharConverter.getConverter(encoding);&&char&c[]&=&converter.convertAll(b);&&for&(int&i&=&0;&i&&&c.&i++)&{&&&&&&System.out.println(Integer.toHexString(c[i]));&&}&结果是什么?0×4f60&如果encoding&=&8859_1&,结果又是什么?0×00c4,0×00e3&如果代码改为&byte&b[]&=&{(byte)’\u00c4′,(byte)’\u00e3′};&ByteToCharConverter&converter&=&ByteToCharConverter.&getDefault();&char&c[]&=&converter.convertAll(b);&for&(int&i&=&0;&i&&&c.&i++)&{&&&&System.out.println(Integer.toHexString(c[i]));&}&结果将又是什么?根据平台的编码而定。&char&&&〉byte:&&&&String&encoding&=&&gb2312&;&&&&char&c[]&=&{‘\u4f60′};&&&&CharToByteConverter&converter&=&CharToByteConverter.getConverter(encoding);&&&&byte&b[]&=&converter.convertAll(c);&&&&for&(int&i&=&0;&i&&&b.&i++)&{&&&&&&&System.out.println(Integer.toHexString(b[i]));&&&&}结果是什么?0×00c4,0×00e3如果encoding&=&8859_1&,结果又是什么?0×3f如果代码改为String&encoding&=&&gb2312&;&&&&char&c[]&=&{‘\u4f60′};&&&&CharToByteConverter&converter&=&CharToByteConverter.getDefault();&&&&byte&b[]&=&converter.convertAll(c);&&&&for&(int&i&=&0;&i&&&b.&i++)&{&&&&&&&System.out.println(Integer.toHexString(b[i]));&&&&}结果将又是什么?根据平台的编码而定。很多中文问题就是从这两个最简单的类派生出来的。而却有很多类不直接支持把encoding输入,这给我们带来诸多不便。很多程序难得用encoding了,直接用default的encoding,这就给我们移植带来了很多困难。2.utf-8utf-8是和unicode一一对应的,其实现很简单7位的unicode:&0&_&_&_&_&_&_&_&11位的unicode:&1&1&0&_&_&_&_&_&1&0&_&_&_&_&_&_&16位的unicode:&1&1&1&0&_&_&_&_&1&0&_&_&_&_&_&_&1&0&_&_&_&_&_&_&21位的unicode:&1&1&1&1&0&_&_&_&1&0&_&_&_&_&_&_&1&0&_&_&_&_&_&_&1&0&_&_&_&_&_&_&大多数情况是只使用到16位以下的unicode:&&你&的gb码是:0xc4e3&,unicode是0×4f60&&&&0xc4e3的二进制:&&&&&&&&&&1100&,0100&,1110&,0011由于只有两位我们按照两位的编码来排,但是我们发现这行不通,因为第7位不是0因此,返回&?&&&&&0×4f60的二进制:&&&&&&&&&&&&&&&0100&,1111&,0110&,0000&&&&&&&&&我们用utf-8补齐,变成:&&&&&&&&&&&&&&&1110&,0100&,1011&,1101&,1010&,0000&&&&&&&&&e4–bd–&a0&&&&&&&&&于是返回:0xe4,0xbd,0xa0。3.string和byte[]string其实核心是char[],然而要把byte转化成string,必须经过编码。string.length()其实就是char数组的长度,如果使用不同的编码,很可能会错分,造成散字和乱码。例如:String&encoding&=&&&;byte&[]&b={(byte)’\u00c4′,(byte)’\u00e3′};&String&str=new&String(b,encoding);  如果encoding=8859_1,会有两个字,但是encoding=gb2312只有一个字这个问题在处理分页是经常发生&。4.Reader,Writer&/&InputStream,OutputStreamReader和Writer核心是char,InputStream和OutputStream核心是byte。但是Reader和Writer的主要目的是要把char读/写InputStream/OutputStream。例如:文件test.txt只有一个&你&字,0xc4,0xe3String&encoding&=&&gb2312&;&&&&InputStreamReader&reader&=&new&InputStreamReader(new&FileInputStream(&&&&&&&&&text.txt&),&encoding);&&&&char&c[]&=&new&char[10];&&&&int&length&=&reader.read(c);&&&&for&(int&i&=&0;&i&&&&i++)&{&&&&&&&System.out.println(c[i]);&&&&}结果是什么?你如果encoding&=&8859_1&,结果是什么???两个字符,表示不认识。反过来的例子自己做。5.我们要对java的编译器有所了解:javac&?encoding我们常常没有用到encoding这个参数。其实encoding这个参数对于跨平台的操作是很重要的。如果没有指定encoding,则按照系统的默认encoding,gb平台上是gb2312,英文平台上是iso8859_1。java的编译器实际上是调用sun.tools.javac.main的类,对文件进行编译,这个类有compile函数中间有一个encoding的变量,-encoding的参数其实直接传给encoding变量。编译器就是根据这个变量来读取java文件的,然后把用utf-8形式编译成class文件。例子代码:String&str&=&&你&;&&&&FileWriter&writer&=&new&FileWriter(&text.txt&);&&&&write.write(str);&&&&writer.close();如果用gb2312编译,你会找到e4&bd&a0的字段&;如果用8859_1编译,&00c4&00e3的二进制:&&,&,&,因为每个字符都大于7位,因此用11位编码:&,,,&c1 c3– &a3&你会找到c1&84&c3&a3&。但是我们往往忽略掉这个参数,因此这样往往会有跨平台的问题:样例代码在中文平台上编译,生成zhclass样例代码在英文平台上编译,输出enclass&&(1). &zhclass在中文平台上执行ok,但是在英文平台上不行&&&(2).&&&&enclass在英文平台上执行ok,但是在中文平台上不行原因:&&(1).&&&&在中文平台上编译后,其实str在运行态的char[]是0×4f60, 在中文平台上运行,filewriter的缺省编码是gb2312,因此&chartobyteconverter会自动用调用gb2312的converter,把str转化成byte输入到fileoutputstream中,于是0xc4,0xe3放进了文件。&但是如果是在英文平台下,chartobyteconverter的缺省值是8859_1,&filewriter会自动调用8859_1去转化str,但是他无法解释,因此他会输出&?&&&(2).&&&&在英文平台上编译后,其实str在运行态的char[]是0×00c4&0×00e3,&在中文平台上运行,中文无法识别,因此会出现??;在英文平台上,0×00c4&#,0×00e3-&0xe3,因此0xc4,0xe3被放进了文件。6.&&&&其它原因:&%@&page&contentType=&text/&charset=GBK&&%&设置浏览器的显示编码,如果response的数据是utf8编码,显示将是乱码,但是乱码和上述原因还不一样。7.&&&&发生编码的地方:?&&&&从数据库到java程序&byte&&〉char?&&&&从java程序到数据库&char&&〉byte?&&&&从文件到java程序&byte&&〉char?&&&&从java程序到文件&char&&〉byte?&&&&从java程序到页面显示&char&&〉byte?&&&&从页面form提交数据到java程序byte&&〉char?&&&&从流到java程序byte&&〉char?&&&&从java程序到流char&&〉byte谢志钢的解决方法:我是使用配置过滤器的方法解决中文乱码的:&web-app&&&&filter&&&&&&filter-name&RequestFilter&/filter-name&&&&&&filter-class&net.golden.uirs.util.RequestFilter&/filter-class&&&&&&init-param&&&&&&&&param-name&charset&/param-name&&&&&&&&param-value&gb2312&/param-value&&&&&&/init-param&&&&/filter&&&&filter-mapping&&&&&&filter-name&RequestFilter&/filter-name&&&&&&url-pattern&*.jsp&/url-pattern&&&&/filter-mapping&&/web-app&&&public&void&doFilter(ServletRequest&req,&ServletResponse&res,&&&&&&&&&&&&&&&&&&&&&&&FilterChain&fChain)&throws&IOException,&ServletException&{&&&&HttpServletRequest&request&=&(HttpServletRequest)&&&&&HttpServletResponse&response&=&(HttpServletResponse)&&&&&HttpSession&session&=&request.getSession();&&&&String&userId&=&(String)&session.getAttribute(&userid&);req.setCharacterEncoding(this.filterConfig.getInitParameter(&charset&));&//&设置字符集?实际上是设置了byte&&&〉char的encoding&&&&try&{&&&&&&if&(userId&==&null&||&userId.equals(&&))&{&&&&&&&&if&(!request.getRequestURL().toString().matches(&&&&&&&&&&&&&.*/uirs/logon/logon(Controller){0,1}\\x2Ejsp$&))&{&&&&&&&&&&session.invalidate();&&&&&&&&&&response.sendRedirect(request.getContextPath()&+&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&/uirs/logon/logon.jsp&);&&&&&&&&}&&&&&&}&&&&&&else&{&//&看看是否具有信息上报系统的权限&&&&&&&&if&(!net.golden.uirs.util.UirsChecker.check(userId,&&信息上报系统&,&&&&&&&&&&&&net.golden.uirs.util.UirsChecker.ACTION_DO))&{&&&&&&&&&&if&(!request.getRequestURL().toString().matches(&&&&&&&&&&&&&&&.*/uirs/logon/logon(Controller){0,1}\\x2Ejsp$&))&{&&&&&&&&&&&&response.sendRedirect(request.getContextPath()&+&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&/uirs/logon/logonController.jsp&);&&&&&&&&&&}&&&&&&&&}&&&&&&}&&&&}&&&&catch&(Exception&ex)&{&&&&&&response.sendRedirect(request.getContextPath()&+&&&&&&&&&&&&&&&&&&&&&&&&&&&&&/uirs/logon/logon.jsp&);&&&&}&&&&fChain.doFilter(req,&res);&&}
moson 阅读(40) |
明海棠文集之日期时间1.0——–它不是原创,是一种思念Java&语言的Calendar,GregorianCalendar&(日历),Date(日期),&和DateFormat(日期格式)组成了Java标准的一个基本但是非常重要的部分.&日期是商业逻辑计算一个关键的部分.&所有的开发者都应该能够计算未来的日期,&定制日期的显示格式,&并将文本数据解析成日期对象。学习日期,&日期格式,&日期的解析和日期的计算。&我们将讨论下面的类:&1、&&具体类(和抽象类相对)java.util.Date&2、&&抽象类java.text.DateFormat&和它的一个具体子类,java.text.SimpleDateFormat&3、&&抽象类java.util.Calendar&和它的一个具体子类,java.util.GregorianCalendar&具体类可以被实例化,&但是抽象类却不能.&你首先必须实现抽象类的一个具体子类.1.&&&java.util.Date及其格式化Date&类从Java&开发包(JDK)&1.0&就开始进化,&当时它只包含了几个取得或者设置一个日期数据的各个部分的方法,&比如说月,&日,&和年.&这些方法现在遭到了批评并且已经被转移到了Calendar类里去了,&我们将在本文中进一步讨论它.&这种改进旨在更好的处理日期数据的国际化格式.&就象在JDK&1.1中一样,&Date&类实际上只是一个包裹类,&它包含的是一个长整型数据,&表示的是从GMT(格林尼治标准时间)1970年,&1&月&1日00:00:00这一刻之前或者是之后经历的毫秒数.&1.1.&创建java.util.DateJava统计从日起的毫秒的数量表示日期。也就是说,例如,日,是在1月1日后的86,400,000毫秒。同样的,日是在日前86,400,000毫秒。Java的Date类使用long类型纪录这些毫秒值.因为long是有符号整数,所以日期可以在日之前,也可以在这之后。Long类型表示的最大正值和最大负值可以轻松的表示290,000,000年的时间,这适合大多数人的时间要求。让我们看一个使用系统的当前日期和时间创建一个日期对象并返回一个长整数的简单例子.&这个时间通常被称为Java&虚拟机(JVM)主机环境的系统时间.&import&java.util.D&public&class&DateExample1&{&public&static&void&main(String[]&args)&{&//&Get&the&system&date/time&Date&date&=&new&Date();&//&打印出具体的年,月,日,小时,分钟,秒钟以及时区System.out.println(date.getTime());&}&&&}&在星期六,&日,&下午大约是6:50的样子,&上面的例子在系统输出设备上显示的结果是&0.&在这个例子中,值得注意的是我们使用了Date&构造函数创建一个日期对象,&这个构造函数没有接受任何参数.&而这个构造函数在内部使用了System.currentTimeMillis()&方法来从系统获取日期.&//1年前日期&&&java.util.Date&myDate=new&java.util.Date();&&&&&long&myTime=(myDate.getTime()/*24*365;&&&myDate.setTime(myTime*1000);&&&String&mDate=formatter.format(myDate);//明天日期&&&myDate=new&java.util.Date();&&&&myTime=(myDate.getTime()/*24;&&&myDate.setTime(myTime*1000);&&&mDate=formatter.format(myDate);//两个时间之间的天数&&&SimpleDateFormat&myFormatter&=&new&SimpleDateFormat(&yyyy-MM-dd&);&&&java.util.Date&date=&myFormatter.parse(&&);&&&&java.util.Date&mydate=&myFormatter.parse(&&);&&&long&&day=(date.getTime()-mydate.getTime())/(24*60*60*1000);//加半小时SimpleDateFormat&format&=&new&SimpleDateFormat(&yyyy-MM-dd&hh:mm:ss&);java.util.Date&date1&=&format.parse(&&23:16:00&);long&Time=(date1.getTime()/;date1.setTime(Time*1000);String&mydate1=formatter.format(date1);//年月周求日期SimpleDateFormat&formatter2&=&new&SimpleDateFormat(&yyyy-MM&F&E&);java.util.Date&date2=&formatter2.parse(&&星期五&);&SimpleDateFormat&formatter3&=&new&SimpleDateFormat(&yyyy-MM-dd&);String&mydate2=formatter3.format(date2);//求是星期几mydate=&myFormatter.parse(&&);SimpleDateFormat&formatter4&=&new&SimpleDateFormat(&E&);String&mydate3=formatter4.format(mydate);&&&1.2.&Date格式化能以一种用户明白的格式来显示这个日期呢?&在这里类java.text.SimpleDateFormat&和它的抽象基类&java.text.DateFormat。那么,&现在我们已经知道了如何获取从日开始经历的毫秒数了.&我们如何才format&就派得上用场了.&//&我们能不能用下面的代码构件出&&8:8&&&&import&java.io.*;&&&&import&java.util.*;&&&&&public&class&WhatIsDate&&&&{&&&&&&&&public&static&void&main(String[]&args)&{&&&&&&&&&&&&Date&date&=&new&Date(,&8,&8,&8);&&&&&&&&&&&&System.out.println(date);&&&&&&&&}&&&&}&Java&的编译器竟然报如下信息&(Sun&JDK1.3,&Windows&2000&中文下)注意:WhatIsDate.java&使用或覆盖一个不鼓励使用的API。注意:使用-deprecation重新编译,以得到详细信息。!&那么&Date&对象究竟是为了满足哪个需求呢?看来它不是用来实现基于年/月/日小时:分钟&的时间表述。我们查看&Java&的文档,我们看到有&getTime()&方法,它返回的竟然是一个&long&值。文档进一步又告诉我们这个值代表了当前系统的时间离&0:0&的毫秒差,而且是在&GMT&时区下(也被称为&EPOC)。如果我们指定的时间是在此之前的,那它将返回一个负数值。这个发现让我们对&Date&对象有了一个全新的认识-Date&存放的是与&EPOC&的偏差值。换而言之我们也可通过&long&类型来表示时间?对了,这个猜想是得到了&Java&的支持:&&&//&第二种获得当前时间的方法&&&&long&dateInMilliSeconds&=&System.currentTimeMillis();&&&&//&这时候打印出的只是一串数字而已&&&&System.out.println(dateInMilliSeconds);&对程序执行效率敏感的程序员可以发现这个方法只是生成一个&Java&的原始类型&(primitive&type)&long,&不需要实例化一个对象。因此如果我们对时间的处理只是在内部进行时,可以用&long&来代替&Date&对象。最典型的应用就是在一段代码开始和结束时,分别获得系统当前的时间,然后计算出代码执行所需的时间(微秒级)。&&&long&start&=&System.currentTimeMillis();&&&&//&代码段&&&&System.out.println(&需要&&+(System.currentTimeMillis()-start)+&&微秒&);&那么当我们要把这个&long&值已更为友好的表现形式显示处理的时候,我们可以用它来构造&Date&对象:Date&date&=&new&Date(dateInMilliSeconds);System.out.println(date);&我们看到了在&Java&中对时间最为基本的表示,有通过对EPOC&的偏差值进行处理。Date&对象是对它的一个对象的封装。我们同时也看到了,在现时世界中我们对时间的描述通常是通过&某年某月某日某时某分&来定义的。Date&的显示(实际上是&toString()&方法)描述了这些信息,但&Java&并不建议我们用这种方式直接来构件&Date&对象。因此我们需要找出哪个对象可以实现这个需求。这就是我们下面就要讲述的&Calendar&对象的功能。在我们进一步研究&Calendar&之前,请记住&Date&只是一个对&long&值(基于&GMT&时区)的对象封装。它所表现出来的年/月/日小时:分钟时区的时间表述,只是它的&toString()&方法所提供的。千万不要为这个假象所迷惑。假如我们希望定制日期数据的格式,&比方星期六-9月-29日-2001年.&下面的例子展示了如何完成这个工作:&import&java.text.SimpleDateF&import&java.util.D&public&class&DateExample2&{&public&static&void&main(String[]&args)&{&SimpleDateFormat&bartDateFormat&=&new&SimpleDateFormat(&EEEE-MMMM-dd-yyyy&);&Date&date&=&new&Date();&System.out.println(bartDateFormat.format(date));&}}&只要通过向SimpleDateFormat&的构造函数传递格式字符串&EEE-MMMM-dd-yyyy&,&我们就能够指明自己想要的格式.&你应该可以看见,&格式字符串中的ASCII&字符告诉格式化函数下面显示日期数据的哪一个部分.&EEEE是星期,&MMMM是月,&dd是日,&yyyy是年.&字符的个数决定了日期是如何格式化的.传递&EE-MM-dd-yy&会显示&Sat-09-29-01.&请察看Sun&公司的Web&站点获取日期格式化选项的完整的指示.&1.3.&文本数据解析成日期对象&假设我们有一个文本字符串包含了一个格式化了的日期对象,&而我们希望解析这个字符串并从文本日期数据创建一个日期对象.&我们将再次以格式化字符串&MM-dd-yyyy&&调用SimpleDateFormat类,&但是这一次,&我们使用格式化解析而不是生成一个文本日期数据.&我们的例子,&显示在下面,&将解析文本字符串&9-29-2001&并创建一个值为&的日期对象.&通过parse()方法,DateFormat能够以一个字符串创立一个Date对象。这个方法能抛出ParseException异常,所以你必须使用适当的异常处理技术。例子程序:&import&java.text.SimpleDateF&import&java.util.D&public&class&DateExample3&{&public&static&void&main(String[]&args)&{&//&Create&a&date&formatter&that&can&parse&dates&of&//&the&form&MM-dd-yyyy.&SimpleDateFormat&bartDateFormat&=&new&SimpleDateFormat(&MM-dd-yyyy&);&//&Create&a&string&containing&a&text&date&to&be&parsed.&String&dateStringToParse&=&&9-29-2001&;&try&{&//&Parse&the&text&version&of&the&date.&//&We&have&to&perform&the&parse&method&in&a&//&try-catch&construct&in&case&dateStringToParse&//&does&not&contain&a&date&in&the&format&we&are&expecting.&Date&date&=&bartDateFormat.parse(dateStringToParse);&//&Now&send&the&parsed&date&as&a&long&value&//&to&the&system&output.&System.out.println(date.getTime());&}catch&(Exception&ex)&{&System.out.println(ex.getMessage());&}}&}&&1.4.&使用标准的日期格式化过程&既然我们已经可以生成和解析定制的日期格式了,&让我们来看一看如何使用内建的格式化过程.&方法&DateFormat.getDateTimeInstance()&让我们得以用几种不同的方法获得标准的日期格式化过程.&在下面的例子中,&我们获取了四个内建的日期格式化过程.&它们包括一个短的,&中等的,&长的,&和完整的日期格式.&import&java.text.DateF&import&java.util.D&public&class&DateExample4&{&public&static&void&main(String[]&args)&{&Date&date&=&new&Date();&DateFormat&shortDateFormat&=&DateFormat.getDateTimeInstance(&DateFormat.SHORT,&DateFormat.SHORT);&DateFormat&mediumDateFormat&=&DateFormat.getDateTimeInstance(&DateFormat.MEDIUM,&DateFormat.MEDIUM);&DateFormat&longDateFormat&=&DateFormat.getDateTimeInstance(&DateFormat.LONG,&DateFormat.LONG);&DateFormat&fullDateFormat&=&DateFormat.getDateTimeInstance(&DateFormat.FULL,&DateFormat.FULL);&System.out.println(shortDateFormat.format(date));&System.out.println(mediumDateFormat.format(date));&System.out.println(longDateFormat.format(date));&System.out.println(fullDateFormat.format(date));&}&}&注意我们在对&getDateTimeInstance的每次调用中都传递了两个值.&第一个参数是日期风格,&而第二个参数是时间风格.&它们都是基本数据类型int(整型).&考虑到可读性,&我们使用了DateFormat&类提供的常量:&SHORT,&MEDIUM,&LONG,&和&FULL.&要知道获取时间和日期格式化过程的更多的方法和选项,&请看Sun&公司Web&站点上的解释.&运行我们的例子程序的时候,&它将向标准输出设备输出下面的内容:&9/29/01&8:44&PM&Sep&29,&:45&PM&September&29,&:45&PM&EDT&Saturday,&September&29,&:45&PM&EDT&2.&&&Calendar&日历类&首先请记住&Calendar&只是一个抽象类,&也就是说你无法直接获得它的一个实例,换而言之你可以提供一个自己开发的&Calendar&对象。那究竟什么是一个&Calendar&呢?中文的翻译就是日历,那我们立刻可以想到我们生活中有阳(公)历、阴(农)历之分。它们的区别在哪呢?比如有:月份的定义&-&阳`(公)历&一年12&个月,每个月的天数各不同;阴(农)历,每个月固定28天,每周的第一天&-&阳(公)历星期日是第一天;阴(农)历,星期一是第一天实际上,在历史上有着许多种纪元的方法。它们的差异实在太大了,比如说一个人的生日是&八月八日&&那么一种可能是阳(公)历的八月八日,但也可以是阴(农)历的日期。所以为了计时的统一,必需指定一个日历的选择。那现在最为普及和通用的日历就是&&Gregorian&Calendar&。也就是我们在讲述年份时常用&&公元几几年&。Calendar&抽象类定义了足够的方法,让我们能够表述日历的规则。Java&本身提供了对&&Gregorian&Calendar&&规则的实现。我们从&Calendar.getInstance()&中所获得的实例就是一个&&GreogrianCalendar&&对象(与您通过&new&GregorianCalendar()&获得的结果一致)。下面的代码可以证明这一点:&&&import&java.io.*;&&&&import&java.util.*;&&&&&public&class&WhatIsCalendar&&&&{&&&&&&&&public&static&void&main(String[]&args)&{&&&&&&&&&&&&Calendar&calendar&=&Calendar.getInstance();&&&&&&&&&&&&if&(calendar&instanceof&GregorianCalendar)&&&&&&&&&&&&&&&&System.out.println(&It&is&an&instance&of&GregorianCalendar&);&&&&&&&&}&&&&}&&Calendar&在&Java&中是一个抽象类(Abstract&Class),GregorianCalendar&是它的一个具体实现。Calendar&与&Date&的转换非常简单:&&&Calendar&calendar&=&Calendar.getInstance();&&&&//&从一个&Calendar&对象中获取&Date&对象&&&&Date&date&=&calendar.getTime();&&&&//&将&Date&对象反应到一个&Calendar&对象中,&&&&//&Calendar/GregorianCalendar&没有构造函数可以接受&Date&对象&&&&//&所以我们必需先获得一个实例,然后设置&Date&对象&&&&calendar.setTime(date);&&&Calendar&对象在使用时,有一些值得注意的事项:1.&Calendar&的&set()&方法set(int&field,&int&value)&-&是用来设置&年/月/日/小时/分钟/秒/微秒&等值field&的定义在&Calendar&中set(int&year,&int&month,&int&day,&int&hour,&int&minute,&int&second)&但没有set(int&year,&int&month,&int&day,&int&hour,&int&minute,&int&second,&int&millisecond)&前面&set(int,int,int,int,int,int)&方法不会自动将&MilliSecond&清为&0。另外,月份的起始值为0而不是1,所以要设置八月时,我们用7而不是8。calendar.set(Calendar.MONTH,&7);我们通常需要在程序逻辑中将它清为&0,否则可能会出现下面的情况:&&&import&java.io.*;&&&&import&java.util.*;&&&&&public&class&WhatIsCalendarWrite&&&&{&&&&&&&&public&static&void&main(String[]&args)&throws&Exception{&&&&&&&&&&&&ObjectOutputStream&out&=&&&&&&&&&&&&&&&&new&ObjectOutputStream(&&&&&&&&&&&&&&&&&&&&new&FileOutputStream(&calendar.out&));&&&&&&&&&&&&Calendar&cal1&=&Calendar.getInstance();&&&&&&&&&&&&cal1.set(,&0,&0,&0);&&&&&&&&&&&&out.writeObject(cal1);&&&&&&&&&&&&Calendar&cal2&=&Calendar.getInstance();&&&&&&&&&&&&cal2.set(,&0,&0,&0);&&&&&&&&&&&&cal2.set(Calendar.MILLISECOND,&0);&&&&&&&&&&&&out.writeObject(cal2);&&&&&&&&&&&&out.close();&&&&&&&&}&&&&}&我们将&Calendar&保存到文件中&&&import&java.io.*;&&&&import&java.util.*;&&&&&public&class&WhatIsCalendarRead&&&&{&&&&&&&&public&static&void&main(String[]&args)&throws&Exception{&&&&&&&&&&&&ObjectInputStream&in&=&&&&&&&&&&&&&&&&new&ObjectInputStream(&&&&&&&&&&&&&&&&&&&&new&FileInputStream(&calendar.out&));&&&&&&&&&&&&Calendar&cal2&=&(Calendar)in.readObject();&&&&&&&&&&&&Calendar&cal1&=&Calendar.getInstance();&&&&&&&&&&&&cal1.set(,&0,&0,&0);&&&&&&&&&&&&if&(cal1.equals(cal2))&&&&&&&&&&&&&&&&System.out.println(&Equals&);&&&&&&&&&&&&else&&&&&&&&&&&&&&&&System.out.println(&NotEqual&);&&&&&&&&&&&&System.out.println(&Old&calendar&&+cal2.getTime().getTime());&&&&&&&&&&&&System.out.println(&New&calendar&&+cal1.getTime().getTime());&&&&&&&&&&&&cal1.set(Calendar.MILLISECOND,&0);&&&&&&&&&&&&cal2&=&(Calendar)in.readObject();&&&&&&&&&&&&if&(cal1.equals(cal2))&&&&&&&&&&&&&&&&System.out.println(&Equals&);&&&&&&&&&&&&else&&&&&&&&&&&&&&&&System.out.println(&NotEqual&);&&&&&&&&&&&&System.out.println(&Processed&Old&calendar&&+cal2.getTime().getTime());&&&&&&&&&&&&System.out.println(&Processed&New&calendar&&+cal1.getTime().getTime());&&&&&&&&}&&&&}&然后再另外一个程序中取回来(模拟对数据库的存储),但是执行的结果是:NotEqualOld&calendar&&&————&最后三位的MilliSecond与当前时间有关New&calendar&&&———–/EqualsProcessed&Old&calendar&Processed&New&calendar&&&另外我们要注意的一点是,Calendar&为了性能原因对&set()&方法采取延缓计算的方法。在&JavaDoc&中有下面的例子来说明这个问题:Calendar&cal1&=&Calendar.getInstance();&&&&cal1.set(,&0,&0&,&0);&//&&&&cal1.set(Calendar.MONTH,&Calendar.SEPTEMBER);&//应该是&,也就是&&&&&cal1.set(Calendar.DAY_OF_MONTH,&30);&//如果&Calendar&转化到&,那么现在的结果就该是&&&&&System.out.println(cal1.getTime());&//输出的是,说明&Calendar&不是马上就刷新其内部的记录&在&Calendar&的方法中,get()&和&add()&会让&Calendar&立刻刷新。Set()&的这个特性会给我们的开发带来一些意想不到的结果。我们后面会看到这个问题。2.&Calendar&对象的容错性,Lenient&设置我们知道特定的月份有不同的日期,当一个用户给出错误的日期时,Calendar&如何处理的呢?&&&import&java.io.*;&&&&import&java.util.*;&&&&&public&class&WhatIsCalendar&&&&{&&&&&&&&public&static&void&main(String[]&args)&throws&Exception{&&&&&&&&&&&&Calendar&cal1&=&Calendar.getInstance();&&&&&&&&&&&&cal1.set(,&0,&0,&0);&&&&&&&&&&&&System.out.println(cal1.getTime());&&&&&&&&&&&&cal1.setLenient(false);&&&&&&&&&&&&cal1.set(,&0,&0,&0);&&&&&&&&&&&&System.out.println(cal1.getTime());&&&&&&&&}&&&&}&它的执行结果是:&&&Tue&Feb&01&00:00:00&PST&2000&&&&Exception&in&thread&&main&&java.lang.IllegalArgumentException&&&&&&&&at&java.util.GregorianCalendar.computeTime(GregorianCalendar.java:1368)&&&&&&&&at&java.util.Calendar.updateTime(Calendar.java:1508)&&&&&&&&at&java.util.Calendar.getTimeInMillis(Calendar.java:890)&&&&&&&&at&java.util.Calendar.getTime(Calendar.java:871)&&&&&&&&at&WhatIsCalendar.main(WhatIsCalendar.java:12)&当我们设置该&Calendar&为&Lenient&false&时,它会依据特定的月份检查出错误的赋值。3.&不稳定的&Calendar我们知道&Calendar&是可以被&serialize&的,但是我们要注意下面的问题&&&import&java.io.*;&&&&import&java.util.*;&&&&&public&class&UnstableCalendar&implements&Serializable&&&&{&&&&&&&&&public&static&void&main(String[]&args)&throws&Exception{&&&&&&&&&&&&Calendar&cal1&=&Calendar.getInstance();&&&&&&&&&&&&cal1.set(,&0,&0&,&0);&&&&&&&&&&&&cal1.set(Calendar.MILLISECOND,&0);&&&&&&&&&&&&ObjectOutputStream&out&=&&&&&&&&&&&&&&&&new&ObjectOutputStream(&&&&&&&&&&&&&&&&new&FileOutputStream(&newCalendar.out&));&&&&&&&&&&&&out.writeObject(cal1);&&&&&&&&&&&&out.close();&&&&&&&&&&&&ObjectInputStream&in&=&&&&&&&&&&&&&&&&new&ObjectInputStream(&&&&&&&&&&&&&&&&new&FileInputStream(&newCalendar.out&));&&&&&&&&&&&&Calendar&cal2&=&(Calendar)in.readObject();&&&&&&&&&&&&cal2.set(Calendar.MILLISECOND,&0);&&&&&&&&&&&&System.out.println(cal2.getTime());&&&&&&&&}&&&&}&&运行的结果竟然是:&Thu&Jan&01&00:00:00&PST&1970它被复原到&EPOC&的起始点,我们称该&Calendar&是处于不稳定状态。这个问题的根本原因是&Java&在&serialize&GregorianCalendar&时没有保存所有的信息,所以当它被恢复到内存中,又缺少足够的信息时,Calendar&会被恢复到&EPOCH&的起始值。Calendar&对象由两部分构成:字段和相对于&EPOC&的微秒时间差。字段信息是由微秒时间差计算出的,而&set()&方法不会强制&Calendar&重新计算字段。这样字段值就不对了。下面的代码可以解决这个问题:&&&import&java.io.*;&&&&import&java.util.*;&&&&&public&class&StableCalendar&implements&Serializable&&&&{&&&&&&&&public&static&void&main(String[]&args)&throws&Exception{&&&&&&&&&&&&Calendar&cal1&=&Calendar.getInstance();&&&&&&&&&&&&cal1.set(,&0,&0&,&0);&&&&&&&&&&&&cal1.set(Calendar.MILLISECOND,&0);&&&&&&&&&&&&ObjectOutputSt
— ldapman @ 2:08 pm
简介 在计算机程序中精确的处理日期是困难的。不仅有显而易见的(英语: January, 法语: Janvier, 德语: Januar, 等)国际化需求, 而且得考虑不同的日期系统(并非所有的文化都用基督耶稣的生日作为纪年的开始)。如有高精度或非常大规模的时间需要被处理, 就有额外的方面需要被注意,比如闰秒或时间系统的变化。(公历(阳历, 格里高利历法)在西方被普遍接受是在1582年,但并非所有的国家在同一天接受!) 尽管有关于闰秒, 时区, 夏令时, 阴历的问题, 度量时间却是一个非常简单的概念: 时间的进行是线性的很容易被忽略。一旦时间轴的区域被定义, 任何时间点被从起点时间的流逝就可以确定。这和地理位置或当地时区是独立的 & 对任意指定的时间点, 对任意地区, 从起点的过程是相同的(忽略相对论的矫正)。 ——————————————————————————– 可当我们试图根据某些日历解释这一时间点的时候困难来了, 比如, 根据月, 日, 或者年来表示它。在这一步上地理信息变得相关: 在时间上的同一个点对应不同的天的某一时间, 依赖于区域 (比如: 时区)。基于解释日期的修正经常是必要的(今天一个月以后是哪一天?) 并且增加了额外的困难: 上溢和下溢(12月15号的后一个月是下一年), 且不明确(1月30号后的一个月是哪一天?). 在最初的JDK 1.0, 一个时间点, 通过把它解释为java.util.Date类, 它被计算在一起来表示. 虽然相对容易处理, 但它并不支持国际化; 从JDK 1.1.4 或JDK 1.1.5, 多样的负责处理日期的职责被分配到以下类中: java.util.Date 代表一个时间点. abstract java.util.Calendar java.util.GregorianCalendar extends java.util.Calendar 解释和处理Date. abstract java.util.TimeZone java.util.SimpleTimeZone extends java.util.TimeZone 代表一个任意的从格林威治的偏移量, 也包含了适用于夏令时(daylight savings rules)的信息. abstract java.text.DateFormat extends java.text.Format java.text.SimpleDateFormat extends java.text.DateFormat 变形到格式良好的, 可打印的String, 反之亦然. java.text.DateFormatSymbols 月份, 星期等的翻译, 作为从Locale取得信息的一种替代选择. java.sql.Date extends java.util.Date java.sql.Time extends java.util.Date java.sql.Timestamp extends java.util.Date 代表时间点, 同时为了在sql语句中使用也包含适当的格式. 注意: DateFormat 和相关的类在java.text.*包. 所有的java.sql.*包中日期处理相关类继承了java.util.Date类. 所有的其它类在java.util.*包中. 这些&新&类来自三个分离的继承层次, 其顶层类(Calendar, TimeZone, and DateFormat)是抽象的. 针对每一个抽象类, Java标准类库提供了一个具体的实现. java.util.Date 类java.util.Date代表一个时间点. 在许多应用中, 此种抽象被称为&TimeStamp.& 在标准的Java类库实现中, 这个时间点代表Unix纪元January 1, :00 GMT开始的毫秒数. 因而概念上来说, 这个类是long的简单封装. 根据此种解释, 类中仅有的没有过期的(除了那些毫秒数的get和set方法)是那些排序方法. 这个类依靠System.currentTimeMillis() 来取得当前的时间点. 因此它的准确度和精度由System的实现和它所调用底层(本质是)决定. The java.util.Date API 在最初的 Date类使用中名字和约定引起了无尽的混淆. 然而用0-11计算月, 从1900计算年的决定模仿了C标准类库的习惯, 调用函数 getTime()返回起始于Unix纪元的毫秒数和 getDate()返回星期的决定显然是Java类设计者自己的. java.util.Calendar 语义 Calendar代表一个时间点(一个&Date&), 用以在特定的区域和时区适当的解释器. 每一个Calendar 实例有一个包含了自纪元开始的代表时间点的long变量. 这意味着Calendar 不是一个(无状态) 变换者或解释器, 也不是一个修改dates的工厂. 它不支持如下方式: Month Interpreter.getMonth(inputDate) or Date Factory.addMonth(inputDate) Instead, Calendar实例必须被初始化到特定的Date. 此Calendar实例可以被修改或查询interpreted属性. 奇怪的是, 此类的instances 总是被初始化为当前时间. 获得一个初始化为任意Date的Calendar 实例是不可能的&API强制程序员通过一系列的在实例上的方法调用, 比如setTime(date)来显式的设置日期. 访问Interpreted 字段和类常量 Calendar类遵从一不常用的方式来访问interpreted date实例的单个字段. 而不是提供一些dedicated属性 getters和setters方法(比如getMonth()), 它仅提供了一个, 使用一个标示作为参数来获取请求的属性的方法: int get(Calendar.MONTH) 等等. 注意这个函数总是返回一个int! 这些字段的标示符被定义为Calendar类的public static final变量. (这些identifiers是raw的整数, 没有被封装为一个枚举抽象.) 除了对应这些字段标示(键值), Calendar 类定义了许多附加的public static final 变量来保存这些字段的值. 因此, 为测试某一特定date (由Calendar 的实例calendar表示) 是否在一年的第一个月, 会有像如下的代码: if (calendar.get(Calendar.MONTH) == Calendar.JANUARY) {…} 注意月份被叫做 JANUARY, FEBRUARY, 等等, 不管location(相对更中性的名字比如: MONTH_1, MONTH_2, 等等). 也有一个字段UNDECIMBER, 被一些(非公历(阳历, 格里高利历法))日历使用, 代表一年的第十三个月. 不幸的是, 键值和值既没有通过名字也没分组成嵌套的inerface来区分. 处理 Calendar提供了三种办法来修改当前实例代表的日期: set(), add(), 和roll(). set()方法简单的设置特定的字段为期望的值. add() 和 roll() 的不同在于它们处理over- and underflows: add() 传递变更到&较小&或&较大&的字段, 而roll()不影响其它字段. 比如, 当给代表12月15号的Calendar实例增加一个月, 当add()使用年会增加, 但使用roll()不会发生任何变化. 为每一种case对应一个函数的决定的动机是, 它们可能在GUI中不同的使用情形. 由于Calendar的实现的方式, 它包含冗余的数据: 所有的字段都可以从给定的时区和纪元开始的毫秒数计算出来,反之亦然. 这个类为这些操作分别定义了抽象方法computeFields()和computeTime(), 又定义了complete()方法执行完全的来回旅程. 因为有两套冗余的数据, 这两套数据可能不同步. 根据类的JavaDoc文档, 当发生变更的时候依赖的数据以lazily 的方式重新计算. 当重新计算需要的时候, 子类必须维护一套脏数据标志作为符号. ——————————————————————————– 实现的Leakage 对于一个&新&的日期相关处理类, 不得不说实现的细节在某种程度上被泄漏到了API中. 在这点上, 这是它们有意用作基类的自定义开发的反映, 但它也偶然看出是不充分清晰设计一个公共接口的结果.Calendar 抽象是否维护两个冗余数据集合完全是一个实现的细节, 因而应当对客户类隐藏. 这也包括打算通过继承来重用此类. 附加的功能 Calendar基类提供的附加功能分成三类. 几个静态的工厂方法来获得用任意时区和locales初始化的实例. 如前面提到的, 所有以这种方式获得实例已经被初始化为当前时间. 没有工厂方法被提供来获得初始化为任意时间点的实例. 第二组包含before(Object)和after(Object)方法. 它们接受Object类型的参数, 因而允许这些方法被子类以任意类型的参数覆盖掉. 最后, 有许多附加的方法来获得设置附加的属性, 比如当前的时区. 当中有几个用以查询特定字段在当前Calendar实现下的可能和实际的最大、最小值. java.util.GregorianCalendar GregorianCalendar 是仅有可用的Calendar的子类. 它提供了基础Calendar抽象适合于根据在西方的习惯解释日期的实现. 它增加了许多public的构造函数, 也有针对于Gregorian Calendars的方法, 比如isLeapYear(). java.util.TimeZone 和 java.util.SimpleTimeZone TimeZone类和其子类是辅助类, 被Calendar用以根据选择的时区来解释日期. 按字面意思来说, 一个时区表示加到GMT上后到当前时区的一定的偏移. 显然, 这个偏移在夏令时有效的时候会发生变化. 因而为了计算对于给定日期和时间的本地时间, TimeZone抽象不仅需要明白当DST有效时的额外偏移, 而且还需明白什么时候DST有效的规则. 抽象基类TimeZone 提供了基本的处理&raw&(没有考虑夏令时)实际偏移(用毫秒数!)的方法, 但任何关于DST规则的功能实现被留给了子类, 比如SimpleTimeZone. 后者提供了许多方法来指定控制DST开始和结束的规则, 比如在一个月中明确的某一天或某一天随后的周几. 每一个TimeZone 有一个可读的, 本地无关的显示名. 显示名以两种风格: LONG和SHORT呈现. 星期的开始? Calendar的文档投入了相当的文字来正确的计算月或年中的weeks. weekday 被认为是一周的开始在因国家的不同而不同. 在美国, 一周通常被认为从周日开始. 在部分欧洲国家一周从周一开始结束于周日.这将影响到哪一周被认为是在一年(或月)第一个完整的周, 和计算一年的周数. 时区由一标示字符串明确的决定. 基类提供静态方法String[] getAvailableIDs()来获得所有已知安装(JDK内带有)的标准时区. (在我的安装内有557个, JDK1.4.1) 假如需要, JavaDoc 定义了严格的建立自定义时区标示符的语法. 也提供了静态工厂方法用以获取 & 指定ID或缺省的当前时区的TimeZone 实例. SimpleTimeZone提供了一些公有的构造函数, 奇怪的是对于一个抽象类, 如TimeZone. (JavaDoc 写到 &子类构造函数调用.& 显然, 应该声明为protected.) java.text.DateFormat 尽管Calendar和相关类处理locale-specific日期的解释,仍有DateFormat 类辅助日期和(人类)可阅读字符串之间的变换. 表示一个时间点时, 会出现附加的本地化问题: 不仅仅在语言, 而且日期格式是地区独立的(美国: Month/Day/Year,德国: Day.Month.Year, 等等). DateFormat 尽力地为程序员管理这些不同. 抽象基类DateFormat不需要(且不允许) 任意的, 程序员定义的日期格式. 作为替代, 它定义了四种格式化风格: SHORT, MEDIUM, LONG, 和FULL (以冗余增加的顺序).对一给定locale和style, 程序员可依靠此类获取适当的日期格式. 抽象基类DateFormat 没有定义静态方法来完成文本和日期之间的格式化和转换. 作为替代, 它定义了几个静态工厂方法来获取被初始化为给定locale和选定style的实例. 既然标准的格式化总是包含日期和时间, 附加工厂方法可用来获取仅处理时间或日期部分的实例. String format(Date)和Date parse(String) 方法然后执行变形. 注意具体的子类可以选择打破这种习惯. 在其内部使用, 解释日期的Calendar对象是可访问和修改的, TimeZone和NumberFormat对象也同样. 然而, 一旦DateFormat 被实例化locale和style就不能再修改. 亦有可用的(抽象的)用以拼接的字符串解析和格式化的方法, 分别接受额外的ParsePosition或FieldPosition参数. 这些方法的每一个都有两个版本. 一个接受或返回Date实例另一个接受或返回普通的Object, 来允许在子类中有选择性的处理Date. 它定义了一些以_FIELD 结尾的public static变量来标示多种可能和FieldPosition一起使用的变量(cf. java.util.Format的Javadoc). 仅有且最常用的DateFormat类的具体实现是SimpleDateFormat. 它提供了所有上述的功能, 且允许定义任意的时间格式. 有一套丰富语法来指定格式化模式; JavaDoc提供了所有细节. 模式可以被指定为构造函数的参数或显式的设置. Printing a Timestamp: A Cut-and-Paste Example 想象你要用用户定义的格式打印当前的时间; 比如, 到log文件. 以下就是做这些的: // 创建以下格式的模式: Hour(0-23):Minute:Second SimpleDateFormat formatter = new SimpleDateFormat( &HH:mm:ss& ); Date now = new Date(); String logEntry = formatter.format(now); // 从后端读入 try { Date sometime = formatter.parse(logEntry); } catch ( ParseException exc ) { exc.printStackTrace(); } 注意需要被catch的ParseException. 当输入的字符串不能被parse的时候被抛出. java.sql.*相关类 在java.sql.*包中的日期时间处理类都继承了java.util.Date. 事实上它们三个反映了三种标准SQL92模型的类型需要DATE, TIME, and TIMESTAMP. 像java.util.Date, SQL包中的这三个类是表示一个时间点的数字的简单封装. 分别地Date和Time类忽略关于一天中的时间或日历的日期. 可Timestamp类, 不仅包含到毫秒精度, 通常的时间和日期, 而且允许存储附加的精确到纳秒精度的时间点的数据. (纳秒是一秒的十亿分之一) 除了影射对应的SQL数据类型, 这些类处理与SQL一致的字符串表示的转换. 在这一点, 这三个类中的每一个覆盖了toString()方法. 此外, 每个类提供了静态的工厂方法, valueOf(String), 返回被初始化为传递参数字符串表示的时间的当前调用类的实例. 这三个方法的字符串表示的格式已被SQL标准选定, 且不能被程序员改变. 存储纳秒需要的额外数据, 没有很好的与在Timestamp中其它通常的时间和日期信息的表示一致. 比如, 在Timestamp实例上调用 getTime() 将返回自Unix纪元开始的毫秒数,忽略了纳秒数据. 简单地, 根据JavaDoc文档, hashCode() 方法在子类中没有被覆盖, 因而也忽略了纳秒数据. java.sql.Timestamp的JavaDoc指出&inheritance relationship (…) 实际表示实现的继承, 而不是类型继承(这违反了继承的初衷). 但即使这句话是错误的, 既然Java没有私有继承的概念(也即继承实现). 所有java.sql.*包中的类应该被设计为封装一个java.util.Date对象, 而不是继承它, 仅暴露需要的方法 & 最起码, 方法比如hashCode() 应该被适当的覆盖. 最后一个评论是关于数据库引擎的时区的处理. 在java.sql.*包中的类不允许显式的设置时区. 数据库服务器(或驱动) 可自由的依据服务器server的当地时区解释这些信息, 且其可能被影响而变化(比如, 因为夏令时). 总结 通过前面的讨论, 很清楚, Java的日期处理相关类并非很复杂, 但是没有被很好设计. 封装被疏漏, APIs结构复杂且没有被很好的组织, 且非常见的思路经常被无缘由的使用. 实现更有其它的莫名奇妙(提议看看Calendar.getInstance(Locale)对于所有可用locale实际返回对象的类型!) 另一方面, the classes manage to treat all of the difficulties inherent in internationalized date handling and, in any case, are here to stay. 希望这篇文章对帮助你搞清它们的用法有所帮助. Call Me By My True Names As a last example of the wonderful consistency and orthogonality of Java’s APIs, I would like to list three (maybe there are more!) different methods to obtain the number of milliseconds since the start of the Unix epoch: long java.util.Date.getTime() long java.util.Calendar.getTimeInMillis() (New with JDK 1.4.1. Note that java.util.Calendar.getTime() returns a Date object!) long java.lang.System.currentTimeMillis()
— ldapman @ 2:06 pm
1.当你正在忙时,却把手机开著, 等著她/他的短信.. 你已经爱上她/他了 2.如果你喜欢和她/他两个人单独漫步.. 你已经爱上她/他了 3.当你和她/他在一起时,你会假装不注意他,但是当她离/他开你的视线时,你会 急著寻找她/他… 你已经爱上她了 4.当她/他受伤或生病时,你会很关心她, 替她/他著急.. 你已经爱上他了 5.当她/他和别人要好时, 你会感到吃不知其味… 你已经爱上她了 6.当你看到她/他那甜美的笑时,你的嘴角会扬起一丝得意的笑.. 你已经爱上她/他了 7.当你看到这篇文章时, 心里想到某个人.. 那么你肯定已经爱上她/他
— ldapman @ 12:57 am
俺知道俺该死,你杀死俺都是应该的。那一天俺来到河南,曾经有份真诚的胡辣汤放在俺的面前,俺没有珍惜。问题很严重,河南人民不尔俺了,俺很郁闷。等到后来有一天俺喝了一碗胡辣汤才后悔,靠,中啊,喝着还怪不赖里。怎么能这样呢?不防这几年河南人三个代表落实的好,与时俱进了,还有喝着如此得劲的汤,我TM以前咋木牛喝里。俺喝了第1碗,木牛喝够,河南人又逞了第2碗,喝了第2碗,还是木牛喝够,热情好客的河南人又端上了第3碗,这时侯我才真正感到河南人实在呀。我就纳了闷了,这么好的人为啥总是受到不公正待遇呢?同样是在中国生活的各地人,和河南人相比差距咋就这大呢?河南雄起。喝了第3碗,越喝越想喝。一直喝了六碗后,俺的老搭档吴孟达怕俺撑坏了,就喊来了赵伟、李香两个人,想恶心恶心我,不让俺再喝了,俺说:&瞧她俩那小样,还想忽悠俺 &。结果俺没当回事。喝第8碗时,他又请来了福荣姐、木子梅、流氓燕、青桐等风头正盛的人物,俺只是受了点小影响,喝得慢了点。最后老吴使出杀手锏,搬来了两位超一流大侠,俺一听此二人名字就对老吴说:&I 服了 you 了。&天啊是李鸭朋、梦露胡(胡冰),俺再也没有胃口了,支持不住,我倒,狂吐ing。  央视哥们听说俺撑坏了,以迅雷不及掩耳盗铃之势派来了最有名的大夫给俺会诊。  NO.1 王小丫    王小丫大夫冲进手术室,关切地问俺:&你知道你伤在哪儿了吗?&   俺吃力地摇摇头,王小丫接着说:&给你三个备选答案吧!a.胃溃疡;b.胸腔大面积出血在;c.颅内出血。&   俺咬了咬牙,说:&a!&   王小丫迷人地一笑问道:&你确定吗?你还可以打一个求助电话。&   俺狂吐血道:&我要打给我父亲!&   &那好,你们的交流时间只有30秒!&   &老爸,我不行了,快换医院&&&   NO.2 李咏   李咏大夫冲进手术室,看了一下病情,说:&现在病人需要过&&&   于是全体护士一起喊:&幸运第一关!&接着一个直拳击向伤者面门。   NO.3 倪萍   倪萍大夫以最快的速度冲进手术室,以最快的速度从眼中流下两行液体,对全体护士动情地说:&其实&&他(指俺)已经离开家在外面漂泊了两个多小时,他是多么地想家呀,多么地想他的母亲呀,多想抱抱他那未满月的孩子呀,但是现在&&&   倪萍擦了擦眼泪,接着对病人说:&请您对家里的亲人说上一句话吧!表达一下此时此刻的心情!&   (全体护士含泪鼓掌中)俺艰难地张开嘴:&妈,我没救了!&   NO.4 韩乔生   韩乔生大夫飞一样冲进手术室,对全体护士说:&忽如一夜春风来,全国流行胡辣汤,这位病人因为喝过多的胡辣汤而生病,具体受伤位置好像是头部&&哦对不起,是脚部,还不是,哪就一定是腚部&&& 一位护士提醒道:&韩大夫,是肚子大呀!& 韩乔生瞪了她一眼,接着说:&病人的身高1.75厘米,体重大约108公斤。&(全体护士倒地。)   最后,韩乔生开始安排手术任务:&1号,你负责观察病人心电图;2号,你负责给病人电击;3号,你负责给病人缝合;病人,你负责给病人打麻药。&   俺哭道:&俺命真苦呀!打麻药都得自己动手。&   NO.5 鞠萍   鞠萍大夫飘然走进手术室,对护士们说:&全体护士小朋友请注意,这就是受伤的患者星星,大衣里面正在作痛的是肚子,你们可能看他和平时不一样,那是因为他太贪吃了。&   NO.6 赵忠祥   赵忠祥大夫稳重地迈进手术室,对护士们说:&像大多数动物一样,每当人类为了在危急关头能够延续受伤的生命,人类会有一种专门负责救治的人及时出现。这种动物群体内自我救治的例子还很多,像生活在非洲的眼镜蛇,都能够自己给自己医治伤口&&大家快看,这个人正在挣扎、抖动,一个字,紧  病人痛苦地大叫:&天哪!谁让你们给我请来个兽医!&   人世间对俺最好的就是你了,你用刀劈死俺吧,不用想了,如果上天能再给俺一次机会,俺说啥也得对河南人讲三个字:我爱喝!不中了俺就死在河南去球了,赵本山那话,小样,谁怕谁呀,条件允许的话,倒插门也是可以的,你能把俺怎么的&&哈哈哈哈(未完待续)1
— ldapman @ 1:44 am
第1章基础知识1.1. 单钥密码体制单钥密码体制是一种传统的加密算法,是指信息的发送方和接收方共同使用同一把密钥进行加解密。通常,使用的加密算法比较简便高效,密钥简短,加解密速度快,破译极其困难。但是加密的安全性依靠密钥保管的安全性,在公开的计算机网络上安全地传送和保管密钥是一个严峻的问题,并且如果在多用户的情况下密钥的保管安全性也是一个问题。单钥密码体制的代表是美国的DES1.2. 消息摘要一个消息摘要就是一个数据块的数字指纹。即对一个任意长度的一个数据块进行计算,产生一个唯一指印(对于SHA1是产生一个20字节的二进制数组)。消息摘要有两个基本属性:两个不同的报文难以生成相同的摘要 难以对指定的摘要生成一个报文,而由该报文反推算出该指定的摘要 代表:美国国家标准技术研究所的SHA1和麻省理工学院Ronald Rivest提出的MD51.3. Diffie-Hellman密钥一致协议密钥一致协议是由公开密钥密码体制的奠基人Diffie和Hellman所提出的一种思想。先决条件,允许两名用户在公开媒体上交换信息以生成&一致&的,可以共享的密钥代表:指数密钥一致协议(Exponential Key Agreement Protocol)1.4. 非对称算法与公钥体系1976年,Dittie和Hellman为解决密钥管理问题,在他们的奠基性的工作&密码学的新方向&一文中,提出一种密钥交换协议,允许在不安全的媒体上通过通讯双方交换信息,安全地传送秘密密钥。在此新思想的基础上,很快出现了非对称密钥密码体制,即公钥密码体制。在公钥体制中,加密密钥不同于解密密钥,加密密钥公之于众,谁都可以使用;解密密钥只有解密人自己知道。它们分别称为公开密钥(Public key)和秘密密钥(Private key)。迄今为止的所有公钥密码体系中,RSA系统是最著名、最多使用的一种。RSA公开密钥密码系统是由R.Rivest、A.Shamir和L.Adleman俊教授于1977年提出的。RSA的取名就是来自于这三位发明者的姓的第一个字母1.5. 数字签名所谓数字签名就是信息发送者用其私钥对从所传报文中提取出的特征数据(或称数字指纹)进行RSA算法操作,以保证发信人无法抵赖曾发过该信息(即不可抵赖性),同时也确保信息报文在经签名后末被篡改(即完整性)。当信息接收者收到报文后,就可以用发送者的公钥对数字签名进行验证。 在数字签名中有重要作用的数字指纹是通过一类特殊的散列函数(HASH函数)生成的,对这些HASH函数的特殊要求是:接受的输入报文数据没有长度限制; 对任何输入报文数据生成固定长度的摘要(数字指纹)输出 从报文能方便地算出摘要; 难以对指定的摘要生成一个报文,而由该报文反推算出该指定的摘要; 两个不同的报文难以生成相同的摘要 代表:DSA第2章在JAVA中的实现2.1. 相关Diffie-Hellman密钥一致协议和DES程序需要JCE工具库的支持,可以到 http://java.sun.com/security/index.html 下载JCE,并进行安装。简易安装把 jce1.2.1\lib 下的所有内容复制到 %java_home%\lib\ext下,如果没有ext目录自行建立,再把jce1_2_1.jar和sunjce_provider.jar添加到CLASSPATH内,更详细说明请看相应用户手册2.2. 消息摘要MD5和SHA的使用使用方法:首先用生成一个MessageDigest类,确定计算方法java.security.MessageDigest alga=java.security.MessageDigest.getInstance(&SHA-1&);添加要进行计算摘要的信息alga.update(myinfo.getBytes());计算出摘要byte[] digesta=alga.digest();发送给其他人你的信息和摘要其他人用相同的方法初始化,添加信息,最后进行比较摘要是否相同algb.isEqual(digesta,algb.digest())相关AIPjava.security.MessageDigest 类static getInstance(String algorithm)返回一个MessageDigest对象,它实现指定的算法参数:算法名,如 SHA-1 或MD5void update (byte input)void update (byte[] input)void update(byte[] input, int offset, int len)添加要进行计算摘要的信息byte[] digest()完成计算,返回计算得到的摘要(对于MD5是16位,SHA是20位)void reset()复位static boolean isEqual(byte[] digesta, byte[] digestb)比效两个摘要是否相同代码:import java.security.*;public class myDigest {public static void main(String[] args) {myDigest my=new myDigest();my.testDigest();}public void testDigest(){try {String myinfo=&我的测试信息&;//java.security.MessageDigest alg=java.security.MessageDigest.getInstance(&MD5&);java.security.MessageDigest alga=java.security.MessageDigest.getInstance(&SHA-1&);alga.update(myinfo.getBytes());byte[] digesta=alga.digest();System.out.println(&本信息摘要是:&+byte2hex(digesta));//通过某中方式传给其他人你的信息(myinfo)和摘要(digesta) 对方可以判断是否更改或传输正常java.security.MessageDigest algb=java.security.MessageDigest.getInstance(&SHA-1&);algb.update(myinfo.getBytes());if (algb.isEqual(digesta,algb.digest())) {System.out.println(&信息检查正常&);}else{System.out.println(&摘要不相同&);}}catch (java.security.NoSuchAlgorithmException ex) {System.out.println(&非法摘要算法&);}}public String byte2hex(byte[] b) //二行制转字符串{String hs=&&;String stmp=&&;for (int n=0;n&b.n++){stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));if (stmp.length()==1) hs=hs+&0&+else hs=hs+if (n&b.length-1) hs=hs+&:&;}return hs.toUpperCase();}}2.3. 数字签名DSA对于一个用户来讲首先要生成他的密钥对,并且分别保存 生成一个KeyPairGenerator实例java.security.KeyPairGenerator keygen=java.security.KeyPairGenerator.getInstance(&DSA&);如果设定随机产生器就用如相代码初始化SecureRandom secrand=new SecureRandom();secrand.setSeed(&tttt&.getBytes()); //初始化随机产生器keygen.initialize(512,secrand); //初始化密钥生成器否则keygen.initialize(512);生成密钥公钥pubkey和私钥prikeyKeyPair keys=keygen.generateKeyPair(); //生成密钥组PublicKey pubkey=keys.getPublic();PrivateKey prikey=keys.getPrivate();分别保存在myprikey.dat和mypubkey.dat中,以便下次不在生成(生成密钥对的时间比较长java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream(&myprikey.dat&));out.writeObject(prikey);out.close();out=new java.io.ObjectOutputStream(new java.io.FileOutputStream(&mypubkey.dat&));out.writeObject(pubkey);out.close();用他私人密钥(prikey)对他所确认的信息(info)进行数字签名产生一个签名数组 从文件中读入私人密钥(prikey)java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream(&myprikey.dat&));PrivateKey myprikey=(PrivateKey)in.readObject();in.close();初始一个Signature对象,并用私钥对信息签名java.security.Signature signet=java.security.Signature.getInstance(&DSA&);signet.initSign(myprikey);signet.update(myinfo.getBytes());byte[] signed=signet.sign();把信息和签名保存在一个文件中(myinfo.dat)java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream(&myinfo.dat&));out.writeObject(myinfo);out.writeObject(signed);out.close();把他的公钥的信息及签名发给其它用户其他用户用他的公共密钥(pubkey)和签名(signed)和信息(info)进行验证是否由他签名的信息 读入公钥java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream(&mypubkey.dat&));PublicKey pubkey=(PublicKey)in.readObject();in.close();读入签名和信息in=new java.io.ObjectInputStream(new java.io.FileInputStream(&myinfo.dat&));String info=(String)in.readObject();byte[] signed=(byte[])in.readObject();in.close();初始一个Signature对象,并用公钥和签名进行验证java.security.Signature signetcheck=java.security.Signature.getInstance(&DSA&);signetcheck.initVerify(pubkey);signetcheck.update(info.getBytes());if (signetcheck.verify(signed)) { System.out.println(&签名正常&);}对于密钥的保存本文是用对象流的方式保存和传送的,也可可以用编码的方式保存.注意要import java.security.spec.*import java.security.*具休说明如下public key是用X.509编码的,例码如下: byte[] bobEncodedPubKey=mypublic.getEncoded(); //生成编码//传送二进制编码//以下代码转换编码为相应key对象X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(bobEncodedPubKey);KeyFactory keyFactory = KeyFactory.getInstance(&DSA&);PublicKey bobPubKey = keyFactory.generatePublic(bobPubKeySpec);对于Private key是用PKCS#8编码,例码如下: byte[] bPKCS=myprikey.getEncoded();//传送二进制编码//以下代码转换编码为相应key对象PKCS8EncodedKeySpec priPKCS8=new PKCS8EncodedKeySpec(bPKCS);KeyFactory keyf=KeyFactory.getInstance(&DSA&);PrivateKey otherprikey=keyf.generatePrivate(priPKCS8);常用API java.security.KeyPairGenerator 密钥生成器类public static KeyPairGenerator getInstance(String algorithm) throws NoSuchAlgorithmException以指定的算法返回一个KeyPairGenerator 对象参数: algorithm 算法名.如:&DSA&,&RSA&public void initialize(int keysize)以指定的长度初始化KeyPairGenerator对象,如果没有初始化系统以1024长度默认设置参数:keysize 算法位长.其范围必须在 512 到 1024 之间,且必须为 64 的倍数public void initialize(int keysize, SecureRandom random)以指定的长度初始化和随机发生器初始化KeyPairGenerator对象参数:keysize 算法位长.其范围必须在 512 到 1024 之间,且必须为 64 的倍数random 一个随机位的来源(对于initialize(int keysize)使用了默认随机器public abstract KeyPair generateKeyPair()产生新密钥对java.security.KeyPair 密钥对类public PrivateKey getPrivate()返回私钥public PublicKey getPublic()返回公钥java.security.Signature 签名类public static Signature getInstance(String algorithm) throws NoSuchAlgorithmException返回一个指定算法的Signature对象参数 algorithm 如:&DSA&public final void initSign(PrivateKey privateKey)throws InvalidKeyException用指定的私钥初始化参数:privateKey 所进行签名时用的私钥public final void update(byte data)throws SignatureExceptionpublic final void update(byte[] data)throws SignatureExceptionpublic final void update(byte[] data, int off, int len)throws SignatureException添加要签名的信息public final byte[] sign()throws SignatureException返回签名的数组,前提是initSign和updatepublic final void initVerify(PublicKey publicKey)throws InvalidKeyException用指定的公钥初始化参数:publicKey 验证时用的公钥public final boolean verify(byte[] signature)throws SignatureException验证签名是否有效,前提是已经initVerify初始化参数: signature 签名数组*/import java.security.*;import java.security.spec.*;public class testdsa {public static void main(String[] args) throws java.security.NoSuchAlgorithmException,java.lang.Exception {testdsa my=new testdsa();my.run();}public void run(){//数字签名生成密钥//第一步生成密钥对,如果已经生成过,本过程就可以跳过,对用户来讲myprikey.dat要保存在本地//而mypubkey.dat给发布给其它用户if ((new java.io.File(&myprikey.dat&)).exists()==false) {if (generatekey()==false) {System.out.println(&生成密钥对败&);};}//第二步,此用户//从文件中读入私钥,对一个字符串进行签名后保存在一个文件(myinfo.dat)中//并且再把myinfo.dat发送出去//为了方便数字签名也放进了myifno.dat文件中,当然也可分别发送try {java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream(&myprikey.dat&));PrivateKey myprikey=(PrivateKey)in.readObject();in.close();// java.security.spec.X509EncodedKeySpec pubX509=new java.security.spec.X509EncodedKeySpec(bX509);//java.security.spec.X509EncodedKeySpec pubkeyEncode=java.security.spec.X509EncodedKeySpecString myinfo=&这是我的信息&; //要签名的信息//用私钥对信息生成数字签名java.security.Signature signet=java.security.Signature.getInstance(&DSA&);signet.initSign(myprikey);signet.update(myinfo.getBytes());byte[] signed=signet.sign(); //对信息的数字签名System.out.println(&signed(签名内容)=&+byte2hex(signed));//把信息和数字签名保存在一个文件中java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream(&myinfo.dat&));out.writeObject(myinfo);out.writeObject(signed);out.close();System.out.println(&签名并生成文件成功&);}catch (java.lang.Exception e) {e.printStackTrace();System.out.println(&签名并生成文件失败&);};//第三步//其他人通过公共方式得到此户的公钥和文件//其他人用此户的公钥,对文件进行检查,如果成功说明是此用户发布的信息.//try {java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream(&mypubkey.dat&));PublicKey pubkey=(PublicKey)in.readObject();in.close();System.out.println(pubkey.getFormat());in=new java.io.ObjectInputStream(new java.io.FileInputStream(&myinfo.dat&));String info=(String)in.readObject();byte[] signed=(byte[])in.readObject();in.close();java.security.Signature signetcheck=java.security.Signature.getInstance(&DSA&);signetcheck.initVerify(pubkey);signetcheck.update(info.getBytes());if (signetcheck.verify(signed)) {System.out.println(&info=&+info);System.out.println(&签名正常&);}else System.out.println(&非签名正常&);}catch (java.lang.Exception e) {e.printStackTrace();};}//生成一对文件myprikey.dat和mypubkey.dat—私钥和公钥,//公钥要用户发送(文件,网络等方法)给其它用户,私钥保存在本地public boolean generatekey(){try {java.security.KeyPairGenerator keygen=java.security.KeyPairGenerator.getInstance(&DSA&);// SecureRandom secrand=new SecureRandom();// secrand.setSeed(&tttt&.getBytes()); //初始化随机产生器// keygen.initialize(576,secrand); //初始化密钥生成器keygen.initialize(512);KeyPair keys=keygen.genKeyPair();// KeyPair keys=keygen.generateKeyPair(); //生成密钥组PublicKey pubkey=keys.getPublic();PrivateKey prikey=keys.getPrivate();java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream(&myprikey.dat&));out.writeObject(prikey);out.close();System.out.println(&写入对象 prikeys ok&);out=new java.io.ObjectOutputStream(new java.io.FileOutputStream(&mypubkey.dat&));out.writeObject(pubkey);out.close();System.out.println(&写入对象 pubkeys ok&);System.out.println(&生成密钥对成功&);}catch (java.lang.Exception e) {e.printStackTrace();System.out.println(&生成密钥对失败&);};}public String byte2hex(byte[] b){String hs=&&;String stmp=&&;for (int n=0;n&b.n++){stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));if (stmp.length()==1) hs=hs+&0&+else hs=hs+if (n&b.length-1) hs=hs+&:&;}return hs.toUpperCase();}}2.4. DESede/DES对称算法首先生成密钥,并保存(这里并没的保存的代码,可参考DSA中的方法)KeyGenerator keygen = KeyGenerator.getInstance(Algorithm);SecretKey deskey = keygen.generateKey();用密钥加密明文(myinfo),生成密文(cipherByte)Cipher c1 = Cipher.getInstance(Algorithm);c1.init(Cipher.ENCRYPT_MODE,deskey);byte[] cipherByte=c1.doFinal(myinfo.getBytes());传送密文和密钥,本文没有相应代码可参考DSA………….用密钥解密密文c1 = Cipher.getInstance(Algorithm);c1.init(Cipher.DECRYPT_MODE,deskey);byte[] clearByte=c1.doFinal(cipherByte);相对来说对称密钥的使用是很简单的,对于JCE来讲支技DES,DESede,Blowfish三种加密术对于密钥的保存各传送可使用对象流或者用二进制编码,相关参考代码如下SecretKey deskey = keygen.generateKey();byte[] desEncode=deskey.getEncoded();javax.crypto.spec.SecretKeySpec destmp=new javax.crypto.spec.SecretKeySpec(desEncode,Algorithm);SecretKey mydeskey=相关APIKeyGenerator 在DSA中已经说明,在添加JCE后在instance进可以如下参数DES,DESede,Blowfish,HmacMD5,HmacSHA1javax.crypto.Cipher 加/解密器public static final Cipher getInstance(java.lang.String transformation)throws java.security.NoSuchAlgorithmException,NoSuchPaddingException返回一个指定方法的Cipher对象参数:transformation 方法名(可用 DES,DESede,Blowfish)public final void init(int opmode, java.security.Key key)throws java.security.InvalidKeyException用指定的密钥和模式初始化Cipher对象参数:opmode 方式(ENCRYPT_MODE, DECRYPT_MODE, WRAP_MODE,UNWRAP_MODE)key 密钥public final byte[] doFinal(byte[] input)throws java.lang.IllegalStateException,IllegalBlockSizeException,BadPaddingException对input内的串,进行编码处理,返回处理后二进制串,是返回解密文还是加解文由init时的opmode决定注意:本方法的执行前如果有update,是对updat和本次input全部处理,否则是本inout的内容/*安全程序 DESede/DES测试*/import java.security.*;import javax.crypto.*;public class testdes {public static void main(String[] args){testdes my=new testdes();my.run();}public void run() {//添加新安全算法,如果用JCE就要把它添加进去Security.addProvider(new com.sun.crypto.provider.SunJCE());String Algorithm=&DES&; //定义 加密算法,可用 DES,DESede,BlowfishString myinfo=&要加密的信息&;try {//生成密钥KeyGenerator keygen = KeyGenerator.getInstance(Algorithm);SecretKey deskey = keygen.generateKey();//加密System.out.println(&加密前的二进串:&+byte2hex(myinfo.getBytes()));System.out.println(&加密前的信息:&+myinfo);Cipher c1 = Cipher.getInstance(Algorithm);c1.init(Cipher.ENCRYPT_MODE,deskey);byte[] cipherByte=c1.doFinal(myinfo.getBytes());System.out.println(&加密后的二进串:&+byte2hex(cipherByte));//解密c1 = Cipher.getInstance(Algorithm);c1.init(Cipher.DECRYPT_MODE,deskey);byte[] clearByte=c1.doFinal(cipherByte);System.out.println(&解密后的二进串:&+byte2hex(clearByte));System.out.println(&解密后的信息:&+(new String(clearByte)));}catch (java.security.NoSuchAlgorithmException e1) {e1.printStackTrace();}catch (javax.crypto.NoSuchPaddingException e2) {e2.printStackTrace();}catch (java.lang.Exception e3) {e3.printStackTrace();}}public String byte2hex(byte[] b) //二行制转字符串{String hs=&&;String stmp=&&;for (int n=0;n&b.n++){stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));if (stmp.length()==1) hs=hs+&0&+else hs=hs+if (n&b.length-1) hs=hs+&:&;}return hs.toUpperCase();}}2.5. Diffie-Hellman密钥一致协议公开密钥密码体制的奠基人Diffie和Hellman所提出的 &指数密钥一致协议&(Exponential Key Agreement Protocol),该协议不要求别的安全性先决条件,允许两名用户在公开媒体上交换信息以生成&一致&的,可以共享的密钥。在JCE的中实现用户alice生成DH类型的密钥对,如果长度用1024生成的时间请,推荐第一次生成后保存DHParameterSpec,以便下次使用直接初始化.使其速度加快System.out.println(&ALICE: 产生 DH 对 …&);KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance(&DH&);aliceKpairGen.initialize(512);KeyPair aliceKpair = aliceKpairGen.generateKeyPair();alice生成公钥发送组bobbyte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();bob从alice发送来的公钥中读出DH密钥对的初始参数生成bob的DH密钥对注意这一步一定要做,要保证每个用户用相同的初始参数生成的DHParameterSpec dhParamSpec = ((DHPublicKey)alicePubKey).getParams();KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance(&DH&);bobKpairGen.initialize(dhParamSpec);KeyPair bobKpair = bobKpairGen.generateKeyPair();bob根据alice的公钥生成本地的DES密钥KeyAgreement bobKeyAgree = KeyAgreement.getInstance(&DH&);bobKeyAgree.init(bobKpair.getPrivate());bobKeyAgree.d}

我要回帖

更多关于 彩云间娱乐 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信