第十一章、JAVA的常用类库
11-1、JAVA常用类库——StringBuffer类
StringBuffer是使用缓冲区的,本身也是操作字符串的,但是与String类不同,String类的内容一旦声明之后则不可改变,改变的只是其内存地址的指向,而StringBuffer中的内容是可以改变的。
对于StringBuffer而言,本身是一个具体的操作类,所以不能像String那样采用直接赋值的方式进行对象的实例化,必须通过构造方法完成。
StringBuffer连接字符操作:
当一个字符串的内容需要被经常改变时就要使用StringBuffer,在StringBuffer中使用append()方法完成字符串的连接操作:
11-1-1、实例操作一:字符串连接操作(append)
在StringBuffer中提供了一系列的append()方法,下面观察具体使用(这些方法最终都会返回StringBuffer类的实例化对象)。
继续验证StringBuffer的内容是可以修改的,通过引用传递的方式完成。
StringBuffer类的常用方法
11-1-2、实例操作二:在任意位置处为StringBuffer添加内容
可以直接使用insert()方法添加内容:public StringBuffer insert(int offset,Boolean b)
在StringBuffer中与String类非常类似的是都可以使用length()获得字符串长度。
11-1-3、实例操作三:字符串的反转操作
直接使用reverse()方法就可以完成反转的功能:public StringBuffer reverse()
11-1-4、实例操作四:替换指定范围的内容
在StringBuffer类中也存在replace()方法,使用此方法就可以对指定范围的内容进行替换:
public StringBuffer replace(int end,String str)
11-1-5、实例操作五:字符串截取
通过substring()方法直接从StringBuffer中指定范围截取内容:public String substring(int start,int end)
11-1-6、实例操作六:删除指定范围内的内容
通过delete()方法直接从StringBuffer中删除指定范围内的内容:public String delete(int start,int end)
11-1-7、实例操作七:查找指定的内容是否存在
在String类中可以使用indexOf()方法进行查找,而在StringBuffer中也可以继续使用此方法,此方法如果找到内容则返回位置,否则返回-1。
11-1-8、StringBuffer的应用
先看以下代码操作:
以上的操作代码因为要频繁修改字符串中的内容,所以使用String根本是不合理的,最好的做法是使用StringBuffer。
从以上示例中可以看出,凡是需要频繁修改字符串内容的时候,都要使用StringBuffer类完成,而且在StringBuffer类中也提供了一些方法是String类中所没有的,包括:delete()、insert()等等。这些方法需要的时候直接通过DOC文档进行查找。
11-2、JAVA常用类库——Runtime类
Runtime运行时,是一个封装了JVM进程的类。每一个JAVA程序实际上都是启动了一个JVM进程,那么每一个JVM进程都是对应这一个Runtime实例,此实例是由JVM为其实例化的。
本类的定义中根本就没有构造方法,本类的构造方法被私有化了。则在此类中肯定有一个方法可以返回本类的实例化对象。
直接使用此静态方法就可以取得Runtime类的实例。
实例化Runtime对象:
·Runtime run = Runtime.getRuntime();
Runtime类的常用方法
一旦取得实例之后,以上的方法就可以进行操作了。
11-2-1、得到JVM信息
每一个Runtime对象都是由JVM进行实例化,所以,可以直接通过此类取得一些信息。
11-2-2、Runtime与Process类
除了观察内存使用量之外,也可以直接使用Runtime类运行本机的可执行程序。
·public Process exec(String command) throws IOException
以上的程序只是打开了一个记事本,现在要求一个记事本程序在运行5秒之后自动进行关闭,如果要想实现此功能,就必须操作进程,因为每一个记事本打开都是会存在一个进程的。exec()方法的返回值是Procese,表示一个进程的操作类,可以通过destroy()方法销毁一个进程。
11-3、JAVA常用类库——国际化程序
所谓国际化程序是指一个程序可以同时适应多门语言,即:如果现在程序者是中国人,则会以中文显示文字,如果现在程序的使用者是英国人,则会以英语显示的文字,也就是说可以通过国际化操作,让一个程序适应各个国家的语言要求。
程序根据不同的语言环境找到不同的资源文件,之后从资源文件中取出内容,资源文件中的内容都是以keyvalue的形式保存的,所以在读取的时候通过其key找到对应的value即可。
11-3-1、国际化实现的支持类
如果要想实现Java程序的国际化操作必须通过以下的三个类完成:
·java.util.Locale : 用于表示一个国家语言类
·java.util.ResourceBundle : 用于访问资源文件
·java.text.MessageFormat :格式化资源文件的占位字符串
Locale类
Locale表示的是本地,实际上使用的是一个ISO编码的封装类。对于各个国家来说都存在一个唯一的编码,那么这种编码就称为ISO编码,使用Locale可以指定好一个具体的国家编码。
例如:中国的编码:zh-CN
英语-美国的编码:en-US
法语的编码:fr-FR
ResourceBundle类
此类是专门完成属性文件读取操作的,读取的时候直接指定文件名称即可(此文件名称一般不需要指定后缀,后缀统一为*.properties),可以根据Locale所指定的区域码来自动选择所需要的资源文件。
public static final ResourceBundle getBundle(String baseName),此方法就是指定要操作的资源文件,此方法找到的是默认的操作系统的语言Locale对象。
public static final ResourceBundle getBundle(String baseName,Locale locale),此方法也是指定操作的资源文件,并传入Locale对象。
public final String getString(String key)根据key取得对应的value
下面通过一道程序来观察资源文件的使用,以及如何使用ResourceBundle读取资源文件。
资源文件命名的时候最好采用单词首字母大写的方式完成。
Message.properties:
其中info是程序中需要的内容,而Hello是此info所指向的具体内容。
从资源文件中读取数据:
MessageFormat类
11-3-2、JAVA国际化程序实现
大概了解了ResourceBundle类和资源文件之后,下面就结合Locale类一起完成国际化程序的开发。
开发要求:
可以根据不同的国家输出不同国家的你好:
·中文:你好!
·英语:Hello!
·法语:Bonjour!
分别定义不同的资源文件,此时需要定义出三个资源文件,同时在定义资源文件的时候需要指定好此资源文件对应的语言编码:
·中文:Message_zh_CN.properties
·英文:Message_en_US.properties
·法文:Message_fr_FR.properties
以上信息确实读取出来了,但是在程序开发中有一点特别需要注意,对于中文的资源文件,虽然现在可以直接通过中文读取,但是这样做是不合理的,应该将其进行Unicode编码,转换为JAVA认识的16进制,这样可以避免一些系统所带来的乱码问题,此工具为JDK自行提供。
只要是中文,就必须进行转码操作。
11-3-3、处理动态文本
之前的资源文件中的所有内容实际上都是固定的,而如果现在有些内容是动态的,那么此时就必须在资源文件中进行一些动态文本的配置,设置占位符。这些符号中的内容暂时不固定,而是在程序执行的时候由程序进行设置的,而要想实现这样的功能,则必须使用MessageFormat类。此类是在java.text包中定义的。
在Format类中还存在数字格式的Format(NumberFormat)、日期格式化的Format(DateFormat)。
占位符使用(数字)的形式表示,如果现在表示第一个内容“{0}”、第二个内容“{1}”……,依次类推。
在MessageFormat类中主要使用format()方法,此方法定义如下:
使用:
处理动态文本。
设置多个占位符:
11-3-4、JAVA新特性——可变参数
在JDK1.5之后JAVA增加了新特性的操作,可以向方法中传递可变的参数,以前的定义的方法,实际上里面的参数都是固定个数的,在JDK1.5之后为了解决操作的麻烦,增加了此特性。
测试参数传递:
直接传入一个数组:
11-3-5、使用一个类代替资源文件
所有的要显示的内容实际上都应该放在资源文件之中,但是在JAVA中为了照顾部分习惯使用类的用户,所以也可以直接使用一个类来存放所有的资源文件内容,但是,此类在操作的时候就必须有一个明显的注意点:必须继承ListResourceBundle
编写类文件Message_zh_CN.class,继承java.util包中的ListResourceBundle方法,并覆写getContents()方法:
编写主类文件,导入java.util包即可完成操作:
不管是资源类还是资源文件,找的时候都是Message,那么如果现在多种资源文件一起出来,那么最终找的是哪一个呢?
此时就需要区分优先级: 执行级别
·Message_zh_CN.class 高
·Message_zh_CN.properties
·Message.properties 低
国际化程序实现的思路:程序与显示相分离,根据不同的Locale指定的区域找到不同的资源文件并根据其key取得对应的value。
在实际的开发中,包括struts,都经常使用资源文件的方式保存所有的信息内容,其基本原理就是依靠了ResourceBundle类取得资源文件中的内容。
11-4、JAVA常用类库——System类
System类是一些与系统相关的属性和方法的集合,而且在System类中所有的属性都是静态的,要想引用这些属性和方法,直接使用System类调用即可。
11-4-1、System类的基本应用
System类中除了可以进行内容的输出外,还可以通过方法取得一个操作的计算时间。
部属性:
如果只需要取得几个固定的属性,则可以通过以下取得:
11-4-2、垃圾回收
一个对象如果不使用,则肯定要等待进行垃圾收集,垃圾收集可以自动调用也可以手工调用,手工调用的时候就是调用其所长System.gc( )或者Runtime.getRuntime( ).gc( )。但是,如果一个对象在回收之前需要做一些收尾的工作,则就必须覆写Object类中的:protected void finalize( ) throws Throwable。在对象被回收之前进行调用,以处理对象回收前的若干操作,例如释放资源等等。
输出结果:
只有强制性调用gc()的时候才可以发现对象被回收,如果不调用,则系统也会在一定时间内自行动进行回收。
11-5、JAVA常用类库——日期操作类(Date、Calendar)
Date类是一个较为常用的类,但是其操作的日期格式会有一些不符合于个人的要求,而如果要想进一步取得一些自己需要的时间,则可以使用Calendar类。
11-5-1、Date类
在java.util包中定义了Date类,Date类本身使用非常简单,直接输出其实例化对象即可。
输出结果:
以上显示的日期格式如果不合适,可以使用Calendar类。
11-5-2、Calendar类
使用此类可以直接将日期精确到毫秒。
Calendar类是一个抽象类,既然是一个抽象类则肯定无法直接使用,此时就要利用对象多态性的概念,通过向上的转型关系实例化本类对象。Calendar类的子类是GregorianCalendar类。
通过Calendar类取得一个完整的日期。使用其子类:
通过此类可以方便的取得一个完整的日期,但是在取得月份的时候需要增加1。
如果执照此种方式取得,则也会很麻烦,最好的做法是将Date进行一些相关的格式化操作。
11-5-3、DateFormat类
此类是一个日期的格式化类,专门格式化日期的操作,因为java.util.Date类本身就已经包含了完整的日期,所以只需要将些日期按照一些好的格式格式化一下显示就可以。
此类是定义在java.text包中的。
但是从定义上可以发现,此类是一个抽象类,按照以往的思路,直接使用其子类实例化即可,但是DateFormat类本身的内部提供了可以直接为其实例化的操作。
·得到日期的DateFormat对象:public static final DateFormat getDateInstance()
·得到日期时间的DateFormat对象:public static final DateFormat getDateTimeInstance()
直接使用DateFormat类完成Date类的转换功能:
·public final String format(Date date)
通过此类可以直接将Date类的显示进行合理的格式化操作,此时采用的是默认的格式化操作,也可以通过Locale对象指定要显示的区域。例如指定的区域是中国:
11-5-4、SimpleDateFormat类
此类的功能是完成日期的显示格式化,例如,在开发中,可能会将一种日期格式变为另外一种日期格式,如下所示:
·原始日期:2008-10-19 10:11:30:345
·转换后日期:2008年10月19日10时11分30秒345毫秒
但是以上的两个日期中日期的数字是完全一样的,唯一不同的是日期的显示格式不同,所以要想实现这样的转换功能就必须依靠SimpleDateFormat类。
如果要想实现转换,则必须首先准备好一个模板,通过此模板进行日期数字的提取工作。
在SimpleDateFormat类使用的时候,必须注意的是在构造对象时要传入匹配的模板。
·构造方法:public SimpleDateFormat(String pattern)
·转换:public Date parse(String source) throws ParseException 此时取得的是全部的时间数
·格式化:public final String format(Date date) 将时间重新格式化成字符串显示
11-5-5、取得日期
开发中经常需要取得日期,而且每次取得日期的时候代码都会重复,所以既然是重复的代码就可以将其定义成一个类,以方便重复调用,但是在操作的时候有点特别需要注意:
·如果月份是小于10,则应该在当前月份前加“0”,但系统肯定会将数字“0”忽略。
基于Calendar类操作
除了取出日期之外,取得时间戳也是一种比较常见的操作,例如:以下的日期:
· 2009-01-16 11:25:34.943
· 时间戳:20090116112534943
以上的程序已经取得了日期时间、中文的日期时间、时间戳,但是所有的操作都比较麻烦,因为每一个地方还需要进行补零操作,所以,在直接使用Calendar类的时候虽然可以方便的将时间取得精确到毫秒,但是在对于取得完整日期的时候却不这么好使了。
基于SimpleDateFormat类操作
Java.util.Date已经就是一个完整的日期了,SimpleDateFormat类中存在一个方法,可以针对于Date重新格式化,那么如果现在将一个表示当前日期的date对象通过SimpleDateFormat类指定好的模板进行相关的格式化操作的话,那么取得的时间就非常的方便了。
通过代码可以发现,直接使用SimpleDateFormat类取得时间会比使用Calendar类更加方便,而且不用去增加补零的操作,所以在开发中如果需要取得一个日期的话,则基本上都使用SimpleDateFormat类进行操作。
11-5-6、Math与Random类
Math类
表示数学操作,例如:平方根、四舍五入等。
上面代码的求四舍五入的时候实际上是将小数点之后的内容全部忽略掉了,如果此时需要进行准确位数的四舍五入,则需要使用其他的类完成——BigDecimal。
Random类
Random类的主要功能是产生随机数,可以产生一个指定范围的随机数。Random类是定义在java.util包中的类。
例:生成10个随机数字,且数字不大于100:
11-5-7、NumberFormat类
NumberFormat表示数字的格式化类,即:可以按照本地的风格习惯进行数字的显示。类的定义如下:
·public abstract class NumberFormat extends Format
MessageFormat、DateFormat、NumberFormat是Format三个常用的子类,如果要想进一步完成一个好的国际化程序,则肯定需要同时使用这样三个类完成,根据不同的国家显示日期,或者显示货币的形式。
此类在java.text包中,所以直接导入此包即可。
11-5-8、DecimalFormat类
DecimalFormat也是Format的一个子类,主要的作用是用来格式化数字使用,当然,在格式化数字的时候要比直接使用NumberFormat更加方便,因为可以直接指定按用户自定义的方式进行格式化操作,与之前讲解的SimpleDateFormat类似,如果要想进行自定义格式化操作,则必须指定格式化操作的模板。
例:
11-5-9、大数操作(BigIntger、BigDecimal)
正常情况下一个整数最多只能放在long类型之中,但是如果现在有如下的一个数字:11212121212121212111122121212121212121
根本就是无法保存的,所以为了解决这样的问题,在java中引入了两个大数的操作类:
·操作整型:BigInteger
·操作小数:BigDecimal
BigInteger
如果在操作的时候一个整型数据已经超过了整数的最大类型长度long的话,则此数据就无法装入,所以,此时要使用BigInteger类进行操作。
BigInteger是在java.math包中。
发现divide()方法本身只是把最终的商保存下来了,但是这样的两个数字相除的时候肯定是无法整除,肯定存在余数。
BigDecimal
对于不需要任何准确计算精度的程序可以直接使用float或double完成,但是如果需要精确计算的结果,则必须使用BigDecimal类。
例:
11-5-10、对象的克隆技术
对象克隆:对象的复制,完整的复制一个对象。
如果要想完成对象克隆的话,则肯定依靠Object类。
以上的方法就是对象克隆的方法,如果现在一个类的对象被克隆,则就必须在此类中明确的覆写此方法,但是此方法不能直接调用。
Cloneable是一个接口,但是在此接口中并没有规定任何的操作方法,所以此接口实际上属于标识接口,表示一种能力。
对象所在的类中必须实现Cloneable接口才可以完成对象的克隆操作。
11-5-11、Arrays
Arrays表示数组的操作类,是直接定义在java.util包中的,主要的功能可以实现数组元素的查找,数组内容的填充、排序等。
例子:
11-5-12、比较器(Comparable、Comparator)
Comparable接口的作用
可以直接使用java.util.Arrays类进行数组的排序操作,但对象所在的类必须实现Comparable接口,用于指定排序接口。Comparable接口定义如下:
public interface Comparable<T>{
public int compareTo(To);
}
此方法返回一个int类型的数据,但是此int的值只能是以下三种:
· 1:表示大于
·-1:表示小于
· 0:表示相等
编写一个实例,要求:
·定义一个学生类,里面有姓名、年龄、成绩三个属性,要求按成绩由高到低排序,如果成绩相等则按照年龄由低到高排序。
注意:
如果在此时Student类中没有实现Comparable接口,则在执行时会出现以下的异常:
Exception in thread “main” java.lang.ClassCastException;
分析比较器的排序原理
实际上之前的排序过程,就是数据结构中的二叉树的排序方法,通过二叉树进行排序,之后利用中序遍历的方式把内容依次读取出来。二叉树排序的基本原理是:
将第一个内容作为根节点保存,之后如果后面的值比根节点的值大,则放在根节点的左子树,如果后面的值比根节点的值小,则放在根节点的右子树。
下面手工实现一个二叉树的比较算法。
为了操作方便,此处使用Integer类完成。
了解了此特性之后,下面就可以动手完成一个二叉树算法:
另一种比较器:Comparator
如果一个类已经开发完成,但是在此类建立的初期并没有实现Comparable接口,此时肯定是无法进行对象排序操作的,所以为了解决这样的问题,java又定义了另一个比较器的操作接口——Comparator。此接口定义在java.util包中,接口定义如下:
下面定义一个学生类,该类没有实现Comparable接口:
以上的类不存在排序功能,为了让此类可以进行排序操作,需要单独定义一个比较器:
定义好了一个第三方的比较器操作类,此类是按照年龄进行排序的。
在使用中尽可能还是使用Comparable在需要排序的类上实现好此接口,而Comparator需要单独建立一个排序的类,这样如果有很多的庆,则排序的规则类也就会非常的多,操作起来比较麻烦。
11-5-13、观察者设计模式
如果要想实现观察者模式,则必须依靠java.util包中提供的Obeservable类和Observer接口。
在Observer接口中的update方法里面的两个参数:
·o :表示Observerable类的对象
·arg :需要被观察的内容
11-5-14、正则表达式
正则表达式可以方便的对数据进行匹配,可以执行更加复杂的字符串验证、拆分、替换功能。例如:现在要求判断一个字符串是否由数字组成,则可以有以下的两种做法:
·不使用正则完成
·使用正则完成
以上操作是不使用正则表达式操作。基本思路:就是将字符串拆分,之后一个个的进行比较的验证,但是这样比较麻烦,而且现在只是验证是否由数字组成,如果更加复杂的呢?
此时,将代码换成正则表达式:
以上的代码也完成了字符串的验证功能,可以发现,使用此种验证方法比之前的操作代码更加简单。
Pattern、Matcher类
如果要想在程序中应用正则表达式则必须依靠Pattern类与Matcher类,这两个类定义在java.util.regex包中,为正则的核心操作类。Pattern类的主要作用是进行正则规范(如之前的“[0-9]”就属于正则规范)的编写,而Matcher类主要是执行规范,验证一个字符串是否符合其规范。
常用正则规则—I
常用正则规则—II
数量表示(X表示一组规范)
逻辑运算符(X、Y表示一组规范)
以上的正则,如果要想驱动起来,则必须依靠Pattern类和Matcher类。
Pattern主要是表示一个规则的意思,即:正则表达式的规则需要在Pattern类中使用。
Matcher类主要表示使用Pattern指定好的验证规则。
Pattern类的常用方法
在Pattern类中如果要想取得Pattern类实例,则必须调用compile()方法。
本类中没有明确的构造方法可以使用,那么则肯定此类的构造方法被私有化了,则可以直接从Pattern类中取得本类的实例。
指定好操作的正则:public static Pattern compile(String regex)
可以为matcher类实例化:public Matcher matcher(CharSequence input)
拆分:public String[] split(CharSequence)
Matcher类的常用方法
例:使用正则规范日期格式
在Pattern类中使用正则进行字符串拆分操作:
还可以使用Matcher类中的字符串替换功能。
例:将字符串中的全部数字替换成“_”:
只要使用正则的验证规则,就可以匹配各种复杂的字符串。
String类对正则的支持
从之前的操作中,可以发现,很多的代码除了要求的字符串不同,使用的正则规则不同,基本上就没有什么特别的。所以在JDK1.4之后,JAVA对正则进行了一些扩充,在String中开始直接支持正则的操作。
在String类中有以下三个方法是支持正则操作的:
例:
但是,在使用正则的时候有一点是需要特别注意的。
现在假设有如下的一个字符串的拆分程序:
如果有时候发现一个字符串无法按照指定的字符拆分的话,则需要使用“\”转义,转义的时候两个“\”表示一个“\”。
11-5-15、JAVA常用类库–定时调度
定时调度就是每当一段时间,程序会自动执行。
如果要使用定时调度,则必须保证程序始终运行,也就是相当于定时调度是在程序外又启动了一个新的线程。
Timer类
Timer类是一种线程设施,可以用来实现在某一个时间或某一段时间后安排某一个任务执行一次,或定期重复执行。该功能要与TimerTask配合使用。TimerTask类用来实现由Timer安排的一次或重复执行的某一个任务。
每一个Timer对象对应的是一个线程,因此计时器所执行的任务应该迅速完成,否则可能会延迟后续任务的执行,而这些后续的任务就有可能堆在一起,等到该任务完成后才能快速连续执行。
Timer类中的常用方法
schedule()与scheduleAtFixedRate()方法的区别:
两者的区别在于重复执行任务时,对于时间间隔出现延迟的情况处理:
schedule()方法的执行时间间隔永远是固定的,如果之前出现了延迟的情况,之后也会继续按照设定好的间隔时间来执行。
scheduleAtFixedRate()方法可以根据出现的延迟时间自动调整下一次间隔的执行时间。
TimerTask类
要想执行具体的任务,则必须使用TimerTask类。TimerTask是一个抽象类,如果要使用该类,需要自己建立一个类来继承此类,并实现其中的抽象方法。
TimerTask类方法
如果现在一个Timer类要想调度程序的话,则需要使用TimerTask的子类。
就依照此概念,完成一个定时调度的程序,每隔2秒打印一次时间:
完成的是一个具体的任务操作类,以后定时调度就调度的是此类的操作,方法的主体就是run()方法。建立测试类,并执行任务调度:
一般在web的开发中此内容比较有用,因为要维护一个容器不关闭才可以一直定时操作下去。