Java SE 11th day:常用类库(上)
1、本次课程知识点
1、StringBuffer及String的区别
2、Runtime和Process
3、System及垃圾收集机制
4、国际化程序实现
5、日期操作
6、比较器
7、正则表达式
8、观察者设计模式
2、具体内容
2.1 StringBuffer(重点)
String是一个字符串,但是字符串的内容不可改变,改变的只是内存地址的指向,那么如果现在要想让字符串的内容可以修改,则必须使用StringBuffer类完成。
2.1.1 StringBuffer的基本使用
在String中使用“+”号进行字符串的链接,在StringBuffer中使用append()方法进行字符串的链接。
在使用StringBuffer时不能像String那样可以直接使用字符串进行赋值的操作,必须通过关键字new开辟对象才可以使用,之后使用append()方法。
package org.lxh.stringbufferdemo; public class StringBufferDemo01 { public static void main(String[] args) { StringBuffer buf = new StringBuffer() ; buf.append("hello") ; buf.append(" ").append("world").append("!!!") ; System.out.println(buf) ; } } |
hello world!!! |
在程序中就只是简单的将“+”替换成了append()方法而已,但是要说的是,String与StringBuffer没有直接的关系,那该怎么办呢?
String类和StringBuffer类的联系,观察他们的继承关系:
String类 | Buffer类 |
public final class String extends Object implements Serializable, Comparable<String>, CharSequence | public final class StringBuffer extends Object implements Serializable, CharSequence |
可以发现这两个类实际上都是CharSequence接口的子类,而像之前使用的charAt()方法和length()方法就是此接口所定义的方法,所以以后只要看见了CharSequence接口就记住使用字符串为其进行对象实例化操作。
虽然String和StringBuffer两个都是CharSequence接口的子类,但是这两个对象之间也不能直接进行对象的转换,需要使用一些操作。
范例:StringBuffer → String,利用StringBuffer类中toString()方法完成。
package org.lxh.stringbufferdemo;
public class StringBufferDemo02 { public static void main(String[] args) { StringBuffer buf = new StringBuffer() ; buf.append("hello") ; buf.append(" ").append("world").append("!!!") ; String str = buf.toString() ; // StringBuffer --> String System.out.println(str.indexOf("hello")) ; } } |
0 |
范例:Sting → StringBuffer,利用StringBuffer类的构造,或者是append()等方法。
package firstCourse; public class APIDemo { public static void main(String arg[]) { String str = "Transformation"; StringBuffer sbuf = new StringBuffer(str); System.out.println(sbuf); } } |
Transformation |
那么一般情况下StringBuffer往往都用在频繁修改字符串的地方。
范例:频繁修改字符串内容。
package org.lxh.stringbufferdemo; public class StringBufferDemo03 { public static void main(String[] args) { StringBuffer buf = new StringBuffer(); fun(buf); // 传递引用 for (int i = 0; i < 1000; i++) { buf.append(i) ; // 修改1000次内容 } System.out.println(buf); }
public static void fun(StringBuffer b) { b.append("helloworld!!!").append("\n"); } } |
hello world!!! 01234567891011121314151617181920212223242526………… |
2.1.2 StringBuffer常用方法
在开发之中,String和StringBuffer两个类之间的关系是属于互补的操作关系,String类中有许多的方法,但是StringBuffer类中有许多String类所没有的方法。
在StringBuffer中提供了大量的操作方法,有些方法与String类似,关键是查看那些新增加的方法。
1、在指定位置处添加内容
● 默认情况下所有的内容都是在StringBuffer最后添加的。
● 使用insert ()方法完成:public StringBuilder insert(intoffset, 数据类型 变量)
package org.lxh.stringbufferdemo; public class StringBufferAPIDemo01 { public static void main(String[] args) { StringBuffer buf = new StringBuffer(); buf.append("********"); System.out.println(buf); buf.insert(1, "ABC"); buf.insert(0, "--------"); System.out.println(buf); } } |
******** --------*ABC******* |
2、删除指定范围的内容
可以直接使用delete()方法删除指定范围的内容:public StringBuffer delete(intstart,int end)
package org.lxh.stringbufferdemo; public class StringBufferAPIDemo02 { public static void main(String[] args) { StringBuffer buf = new StringBuffer() ; buf.append("hello") ; buf.append(" ").append("world").append("!!!") ; buf.insert(1, "LXH") ; ; buf.insert(0, "MLDN") ; System.out.println(buf.delete(0, 9)) ; // 将0~9的内容删除掉 } } |
llo world!!! |
3、可以反转字符串的内容
● 使用reverse()方法:public StringBuffer reverse()
package org.lxh.stringbufferdemo; public class StringBufferAPIDemo03 { public static void main(String[] args) { StringBuffer buf = new StringBuffer() ; buf.append("hello") ; buf.append(" ").append("world").append("!!!") ; System.out.println(buf.reverse()) ; // 将内容反转 } } |
!!!dlrow olleh |
扩展:
如果假设要求实现字符串的反转,最早可以通过栈的方式完成(栈操作详见后面章节Framework Collection)。
4、替换指定范围的内容
● 使用replace()方法:public StringBuffer replace(int start, int end, String str)
package firstCourse;
public class APIDemo { public static void main(String[] args) { StringBuffer buf = new StringBuffer(); buf.append("Hello").insert(0, "China,").replace(6, 11, "Come on"); System.out.println(buf); } } |
China,Come on |
面试题:请解释String和StringBuffer的区别?
Ÿ String的内容不能改变,而StringBuffer的内容可以改变;
Ÿ String类和StringBuffer类中定义的方法可以很好的实现互补;
从开发的角度而言,大部分的情况下使用的都是String类完成,很少使用到StringBuffer,使用StringBuffer只有一个前提,就是当字符串的内容需要不断修改的时候。
2.2 Runtime类
每一个JVM启动的时候实际上都对应了一个Runtime实例,表示运行时。
但是此类的构造方法被私有化了,所以在API文档中并没有写出其构造方法的定义。因为此类的构造对外部不可见,整个一个JVM进程只有一个Runtime类的实例化对象,这是一个典型的单例设计模式应用,而单例设计模式之中,肯定会有一个静态方法可以取得本类的实例化对象:
● 静态方法:public static Runtime getRuntime()
取得Runtime类的实例化对象之后,可以取得一些JVM的内存使用信息,有如下几个方法:
u 最大可用内存:public long maxMemory();
u 总共可用内存:public long totalMemory(),此数值小于maxMemory();
u 空闲内存:public long freeMemory();
在Runtime类中也可以进行内存的释放:public void gc()
范例:观察内存信息。
package firstCourse;
public class APIDemo { public static void main(String[] args) throws Exception { Runtime run = Runtime.getRuntime(); System.out.println("Current state, " + gainMemoryInformation(run)); String temp = ""; for (int i = 0; i < 10000000; i++) { temp += ""; //产生大量垃圾 } System.out.println("Massive garbage generated, " + gainMemoryInformation(run)); run.gc(); System.out.println("After Garbage Collection, " + gainMemoryInformation(run)); }
public static String gainMemoryInformation(Runtime runArg) { double max = runArg.maxMemory() / 1024 / 1024; double total = runArg.totalMemory() / 1024 / 1024; double free = runArg.freeMemory() / 1024 / 1024; return "max:" + max + "MB, total:" + total + "MB, free:" + free + "MB"; } } |
Current state, max:1320.0MB, total:89.0MB, free:88.0MB Massive garbage generated, max:1320.0MB, total:539.0MB, free:424.0MB After Garbage Collection, max:1320.0MB, total:539.0MB, free:529.0MB |
面试题:请解释一下Java中的垃圾收集机制
垃圾收集机制指的是可以对无用的内存空间进行释放,而对于垃圾的收集可以采用两种方式完成:
u 不定期的进行自动的垃圾回收,不可控;
u 利用Runtime类中的gc()方法进行垃圾的回收,手工;
使用Runtime类除了取得以上的信息之外,也可以取得进程的信息,即直接运行本机的程序。
● 取得进程:public Process exec(String command) throws IOException
范例:运行本机上的calc.exe程序(计算器)
package org.lxh.runtimedemo; public class RuntimeDemo01 { public static void main(String[] args) throws Exception { Runtime run = Runtime.getRuntime(); // 单例设计 run.exec("calc.exe");// 执行程序 } } |
可以让指定的程序,进行自动关闭。exec()方法返回一个Process类的实例,表示一个进程的对象,如果要想关闭,则使用此类中:publicabstract void destroy()
package org.lxh.runtimedemo; public class RuntimeDemo02 { public static void main(String[] args) throws Exception { Runtime run = Runtime.getRuntime(); // 单例设计 Process pro = run.exec("calc.exe");// 执行程序 Thread.sleep(2000); // 看2秒 pro.destroy(); // 销毁 } } |
另一种实现方式:
package firstCourse;
public class APIDemo { public static void main(String[] args) throws Exception { Process pro = Runtime.getRuntime().exec("mspaint.exe"); //启动进程 Thread.sleep(2000); pro.destroy(); //销毁进程 } } |
这种代码根本就没有任何用处,Java是跨平台的,不要和特定的平台程序绑定。
2.3 System类
System类是一个系统类,是一个陪伴我们时间最长的几个类,例如:System.out.println()就是类中提供的操作。
2.3.1 取得计算时间
使用此类可以取得计算的时间。例如,计算一个程序的运行时间。
● 得到当前的时间:public static long currentTimeMillis()
package org.lxh.systemdemo; public class SystemDemo01 { public static void main(String[] args) { long beginning = System.currentTimeMillis(); // 取得当前时间 String str = ""; for (int i = 0; i < 10000; i++) { str += i; // 会产生大量的垃圾 } long end = System.currentTimeMillis(); // 取得当前时间 System.out.println(end - beginning);// 取得计算的时间 } } |
408 |
对于此类功能以后也会在一些框架见到。
2.3.2 System与垃圾收集
在System类中存在一个gc()方法:
● public static void gc()
调用此方法实际上就是调用了Runtime类中的gc()方法。
如果一个对象不用的话则就有可能进行垃圾的收集,但是一个对象在被收集前需要做一些收尾工作。
在Object类中存在一个方法,此方法将在对象被回收前调用:
● protectedvoid finalize()throws Throwable
一般情况下在Java中程序抛出的都应该是Exception,但是现在抛出的是Throwable,那么就意味着此处即有可能产生Exception也有可能产生Error。
package firstCourse;
class Elder { private String name; private int age; public Elder(String name, int age) { this.name = name; this.age = age; }
public String toString() { return "姓名:" + this.name + ",年龄:" + this.age; }
public void makeException() throws Exception{ throw new Exception("可以抛出的异常."); } public void finalize() throws Throwable { System.out.println("我被回收了,我完蛋了。。。(" + this + ")"); throw new Exception("抛不出的异常!"); } }
public class APIDemo { public static void main(String[] args) { Elder per = new Elder("张三", 99); try { per.makeException(); } catch (Exception e) { e.printStackTrace(); } per = null; // 取消引用,表示已变成垃圾! System.gc();// 强制进行垃圾收集 } } |
java.lang.Exception: 可以抛出的异常. at firstCourse.Elder.makeException(APIDemo.java:16) at firstCourse.APIDemo.main(APIDemo.java:28) 我被回收了,我完蛋了。。。(姓名:张三,年龄:99) |
但是从一般的开发角度看,很少有专门的开发在对象回收前进行操作的,所以此操作理解即可。
面试题:请说出final、finally、finalize的区别
● final是Java的一个关键字,用户定义不能被继承的类,不能被覆写的方法,常量;
● finally是Java的一个关键字,是异常处理操作的统一出口;
● finalize是Object类中所提供的一个方法,用于对象回收前进行收尾操作。
2.4 国际化程序实现(理解)
国际化程序:一套程序可以同时适应多个不同国家的语言要求,那么这样的程序称为国际化程序。
如果想实现国际化程序,则必须依靠Locale、ResourceBundle、MessageFormat几个完成,并结合属性文件(*properties)。
2.4.1 Locale
Locale非常好理解,表示的是一个地区,也就是说在国际化程序中通过Local指定当前所在的地区。世界上各个国家都存在一个国家的编号,例如:
● 中国:zh_CN
● 美国:en_US
实际上以上的这些编码根本就没有必要记住,因为在浏览器中都有指示。
如果要想创建Locale类的对象有两种方式:
● 第一种是直接取得本机的语言:public static LocalegetDefault()
● 第二种是指定一种显示的语言:public Locale(Stringlanguage,String country)
但是如果想要实现国际化只靠Locale类是不够的,需要一个配合的属性文件存在,一个地区的Local对象都应该一个不同的属性文件。
找到属性文件之后,下一步就需要将内容读取出来,所有的内容需要依靠ResourceBundle类来读取。
2.4.2 ResourceBundle
此类首先是在java.util中定义的类,使用时会通过里面的静态方法取得实例,有一下两种方式:
● 根据本机的Local取得属性文件:
|- public static final ResourceBundlegetBundle(String baseName)
● 根据指定的Local取得属性文件:
|- public static final ResourceBundlegetBundle(String baseName, Locale locale)
下面通过一段代码来演示如何去访问属性文件。
属性文件:Message.properties
info = 你好! |
其中info为属性的key,而“你好!”为属性的values,操作程序时通过key找到value。
在使用ResourceBundle读取的时候要根据key进行读取,方法是:
● public final String getString(String key)
package org.lxh.localedemo; import java.util.Locale; import java.util.ResourceBundle; public class LocaleDemo01 { public static void main(String[] args) { Locale loc = Locale.getDefault(); // 得到默认的Locale // 要找到Message的属性文件,此时操作时省略了后缀,并指定了操作的区域 ResourceBundle rb = ResourceBundle.getBundle("Message", loc); String value = rb.getString("info") ; // 根据key找到内容 System.out.println("内容为:" + value); } } |
内容为:?????? |
此时,内容确实读取进来了,但是在显示的时候出现了乱码,因为对于属性文件来讲里面是不能够有中文的,如果出现中文则必须进行转码操作,转换成UNICODE编码。
在JDK中提供了一个转换工具,可以将文件变为16进制编码,此工具在bin\native2ascii.exe
将上面的内容进行修改:(注释里也不要写中文)
#你好! info = \u4f60\u597d\uff01 |
那么此时再次读取。可以发现内容可以正确的读取进来了。
2.4.3 完成国际化操作
现在假设如果是中国的语言则显示:你好,如果是英文则显示:hello,要想完成此程序,则首先应该建立两个属性文件,这两个属性文件有命名要求:属性名称_区域名称.properties
● 中文:Message_zh_CN.properties
● 英文:Message_en_US.properties
package org.lxh.localedemo; import java.util.Locale; import java.util.ResourceBundle; public class LocaleDemo02 { public static void main(String[] args) { Locale chLoc = new Locale("zh", "CN"); // 指定中文环境 Locale enLoc = new Locale("en", "US"); // 指定英文环境 // 要找到Message的属性文件,此时操作时省略了后缀,并指定了操作的区域 ResourceBundle chRb = ResourceBundle.getBundle("Message", chLoc); ResourceBundle enRb = ResourceBundle.getBundle("Message", enLoc); String zhValue = chRb.getString("info"); // 根据key找到内容 String enValue = enRb.getString("info"); // 根据key找到内容 System.out.println("中文内容为:" + zhValue); System.out.println("英文内容为:" + enValue); } } |
中文内容为:你好! 英文内容为:hello! |
在整个国际化程序中,属性文件是最关键的,因为可以根据不同的语言进行自动的文字转载。
范例:同时存在如下两个属性文件:
Message.properties:
info = \u4f60\u597d\uff01 |
Message_zh_CN.properties:
info = \u4f60\u597d\uff01********** |
运行LocaleDemo01:
内容为:你好!********** |
如果不存在Message_zh_CN.properties此属性文件,则运行结果为:
内容为:你好! |
2.4.4 动态文本
之前的所有内容实际上在属性文件中保存的值都是固定的,如果现在希望某些内容进行动态的显示,例如:你好,XXX!
那么,此时就可以使用占位符的方式完成,在属性文件中加入“{编号}”即可。
Message_zh_CN.properties:
info = \u4f60\u597d\uff01{0},{1} |
Message_en_US.properties:
info = hello!{0}{1} |
之后就可以使用MessageFormat进行文字的格式化。
此类在java.text包中定义。使用如下方法完成内容的增加:
● public static String format(String pattern,Object... arguments)
package org.lxh.localedemo; import java.text.MessageFormat; import java.util.Locale; import java.util.ResourceBundle; public class LocaleDemo03 { public static void main(String[] args) { Locale chLoc = new Locale("zh", "CN"); // 指定中文环境 Locale enLoc = new Locale("en", "US"); // 指定英文环境 // 要找到Message的属性文件,此时操作时省略了后缀,并指定了操作的区域 ResourceBundle chRb = ResourceBundle.getBundle("Message", chLoc); ResourceBundle enRb = ResourceBundle.getBundle("Message", enLoc); String zhValue = chRb.getString("info"); // 根据key找到内容 String enValue = enRb.getString("info"); // 根据key找到内容 System.out.println("中文内容为:" + MessageFormat.format(zhValue, "李兴华", 10)); System.out.println("英文内容为:" + MessageFormat.format(enValue, "lxh", 20)); } } |
中文内容为:你好!李兴华,10 英文内容为:hello!lxh,20 |
2.4.5 使用资源表示内容(了解)
在程序中可以通过类存储全部的资源内容,那么此类必须继承ListResourceBundle。
package org.lxh.localedemo; import java.util.ListResourceBundle; public class Message_zh_CN extends ListResourceBundle { Object data[][] = { {"info","你好,{0},年龄:{1}!"} }; protected Object[][] getContents() { return this.data; } } |
编译Message.java后运行LocaleDemo03,结果:
中文内容为:你好,李兴华,年龄:10! 英文内容为:hello!lxh,20 |
通过观察,如果在一个classpath中同时存在:
●Message.properties
●Message_zh_CN.properties
●Message.java
●Message_zh_CN.java
运行顺序是:
1、Message_zh_CN.java
2、Message.java
3、Message_zh_CN.properties
4、Message.properties
但是从实际的角度来看,一般不会使用类表示一个资源文件的内容,都使用properties文件保存。
2.5 日期操作类(重点)
在java中对日期的显示进行了很好的类的支持,使用Date、DateFormat、SimpleDateFormat、Calendar类都可以取得日期。
2.5.1 Data类
java.util.Date是一个系统提供的日期操作类,此类的使用非常简单,此类是一个非常方便的实现日期的取得。
package org.lxh.datedemo;
import java.util.Date;
public class DateDemo { public static void main(String[] args) { System.out.println(new Date()) ; } } |
Mon Apr 30 12:43:08 CST 2012 |
此时,日期已经可以正常的显示,但是日期的显示格式并不符合国人的喜好。
2.5.2 SimpleDateFormat类(核心)
使用java.util.Date的确是可以进行日期的取得,但是取得的日期格式实在是太差了,所以在Java中专门提供了一个java.text.SimpleDateFormat类进行日期格式化操作。
此类的功能与Oracle中的to_char()、to_date()函数很相似,如果要进行日期的转换,则首先必须取得日期的操作标记:
● 年(yyyy)、月(MM)、日(dd)、时(HH)、分(mm)、秒(ss)、毫秒(SSS)。
范例:将Date型数据格式化显示为String型
import java.util.Date; import java.text.SimpleDateFormat;
public class Te { public static void main(String args[]) { Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); String str = sdf.format(date); // 格式化日期 System.out.print(str); } } |
2012-10-23 19:04:28:245 |
范例:将String变成Date类型
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date;
public class Te { public static void main(String args[]) { String str = "2012-10-23 19:04:28:245"; Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); try { // 由于不确定String书写格式是否正确,所以需要进行异常处理 date = sdf.parse(str); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(date); } } |
Tue Oct 23 19:04:28 CST 2012 |
那么此时也就完成了基本类型的互转换:
● 基本数据类型 → String:利用包装类和String类的valueOf()方法;
● String →Date: SimpleDateFormat类,这个类就具备了Oracle中的to_char()和to_date()两个方法的作用。
2.5.3 Calendar
通过此类可以将时间精确到毫秒。Calendar类的定义:
public abstract class Calendar extends Object implements Serializable, Cloneable, Comparable<Calendar> |
此类是一个抽象类,则使用时必须依靠其子类。
package org.lxh.datedemo; import java.util.Calendar; public class CalendarDemo { public static void main(String[] args) { Calendar calendar = new GregorianCalendar(); System.out.println("年: " + calendar.get(Calendar.YEAR)); System.out.println("月: " + (calendar.get(Calendar.MONTH) + 1)); System.out.println("日: " + calendar.get(Calendar.DAY_OF_MONTH)); System.out.println("时: " + calendar.get(Calendar.HOUR_OF_DAY)); System.out.println("分: " + calendar.get(Calendar.MINUTE)); System.out.println("秒: " + calendar.get(Calendar.SECOND)); System.out.println("毫秒: " + calendar.get(Calendar.MILLISECOND)); } } |
年: 2012 月: 4 日: 30 时: 12 分: 51 秒: 27 毫秒: 693 |
2.5.4 题目:设计一个取得日期的类
在程序的开发中基本上都会大量的使用取出日期的操作,而如果每次都像之前那样取出日期的话,则会很麻烦,所以最好可以设计一个类,那么此类可以直接返回日期,返回的日期包含几种形式:
● 形式一:2012-04-30
● 形式二:2012-04-30 12:51:27.693
● 形式三:2012年04月30日
● 形式四:2012年04月30日12时51分27秒693毫秒
package org.lxh.datedemo;
import java.util.Calendar; import java.util.GregorianCalendar;
public class DateTime { private Calendar calendar = new GregorianCalendar(); // 实例化Calendar对象
public String getDate() {// 2009-03-02 StringBuffer buf = new StringBuffer(); buf.append(calendar.get(Calendar.YEAR)).append("-"); buf.append(this.addZero((calendar.get(Calendar.MONTH) + 1), 2)).append( "-"); buf.append(this.addZero(calendar.get(Calendar.DAY_OF_MONTH), 2)); return buf.toString(); }
public String getDateTime() {// 2009-03-02 16:19:34.123 StringBuffer buf = new StringBuffer(); buf.append(this.getDate()).append(" "); buf.append(this.addZero(calendar.get(Calendar.HOUR_OF_DAY), 2)).append( ":"); buf.append(this.addZero(calendar.get(Calendar.MINUTE), 2)).append(":"); buf.append(this.addZero(calendar.get(Calendar.SECOND), 2)).append("."); buf.append(this.addZero(calendar.get(Calendar.MILLISECOND), 3)); return buf.toString(); }
public String getDateComplete() {// 2009年03月02日 StringBuffer buf = new StringBuffer(); buf.append(calendar.get(Calendar.YEAR)).append("年"); buf.append(this.addZero((calendar.get(Calendar.MONTH) + 1), 2)).append( "月"); buf.append(this.addZero(calendar.get(Calendar.DAY_OF_MONTH), 2)) .append("日"); return buf.toString(); }
public String getDateTimeComplete() {// 2009年03月02日16时19分34秒123毫秒 StringBuffer buf = new StringBuffer(); buf.append(this.getDateComplete()); buf.append(this.addZero(calendar.get(Calendar.HOUR_OF_DAY), 2)).append( "时"); buf.append(this.addZero(calendar.get(Calendar.MINUTE), 2)).append("分"); buf.append(this.addZero(calendar.get(Calendar.SECOND), 2)).append("秒"); buf.append(this.addZero(calendar.get(Calendar.MILLISECOND), 3)).append( "毫秒"); return buf.toString(); }
private String addZero(int temp, int len) { StringBuffer str = new StringBuffer(); str.append(temp);// 加入数字 while (str.length() < len) { str.insert(0, 0); // 在第一个位置加上字母0 } return str.toString(); }
public static void main(String args[]) { System.out.println(new DateTime().getDateTimeComplete()); } } |
2012年04月30日15时16分47秒269毫秒 |
3、总结
1、StringBuffer比String的性能更高,而且最大的特点是StringBuffer的内容可以修改,而String的内容无法修改。
2、Runtime使用单例设计,需要通过getRuntime()方法取得Runtime类的实例。
3、Runtime中的gc()方法用于垃圾的回收,System.gc()方法实际上也是调用了Runtime类中的gc()方法。
4、Object类中的finalize()方法用于对象回收时释放空间。
5、国际化程序的实现思路,使用属性文件保存全部的显示信息,使用Locale指定操作的区域,使用ResourceBundle读取属性文件,根据地区读取。
6、日期操作类中可以使用Calendar类将日期精确到毫秒。