目标:
- 掌握String与StringBuffer的区别,可以熟练使用StringBuffer中的各种方法进行相关操作
- 能够自己编写一个得到日期的操作类,并将这些日期进行格式化操作
- 掌握比较器及其基本原理,并可以通过比较起进行对象数组的比较操作
- 掌握对象克隆技术及其实现要求
- 能够灵活地应用正则表达式对字符串的组成进行判断
- 了解并使用JAVA中提供的观察者操作机制
- 认识JAVA中的Random、Locale、Math等常用类,并可以使用Local进行国际化程序的编写
- 描述出Object、System类对垃圾收集的支持
- 使用NumberFormat、DecimalFormat、BigInteger、BigDecimal进行数字的操作
- 了解JAVA定时器任务调度的实现
11.1 StringBuffer类
11.1.1 认识StringBuffer类
String类: String 的内容一旦声明则不可改变。 如果要改变,改变的肯定是String 的引用地址!!!
StringBuffer类: 如果一个字符串要经常被改变,为了节约资源,则必须使用StringBuffer类!!!
字符串的连接方式: String类通过 “+” 进行字符串的连接; 而StringBuffer类只能使用append()方法进行字符串的连接。例如:String类用“+”,"Hello" + " " + "World" + " " + "!!!" 而StringBuffer类用append()方法 , "Hello" append " " append "World" append " " append "!!!"
下面来一一验证这些方法
tips: StringBuffer支持的方法大部分与String类似,因为StringBuffer在开发中可以提升代码的性能,所以使用较多,这样为了保证用户操作的适应性。
一, 字符串连接操作: 在程序中使用append()方法可以进行字符串的连接,而且此方法返回了一个StringBuffer类的实例,这样就可以采用代码链的形式一直调用append()方法。
package org.forfan06.stringbufferdemo;
public class StringBufferDemo01{
public static void main(String args[]){
StringBuffer buf = new StringBuffer();
buf.append("Hello");
buf.append("World").append("!!!");
buf.append("\n");
buf.append("数字 = ").append(1).append("\n");
buf.append("字符 = ").append('C').append("\n");
buf.append("布尔 = ").append(true);
System.out.println(buf);
}
}
运行结果:
-------------------------------------------------
Hello World!!!
数字 = 1
字符 = C
布尔 = true
-------------------------------------------------
/*验证StringBuffer的内容是可以修改的!!!*/
package org.forfan06.stringbufferdemo;
public class StringBufferDemo02{
public static void main(String args[]){
StringBuffer buf = new StringBuffer();
buf.append("Hello "); //向StringBuffer对象中添加内容
fun(buf); //调用方法,传递StringBuffer引用
System.out.println(buf); //输出结果是:Hello 优快云 forfan06
}
//fun方法没有返回值,但是在方法中修改了StringBuffer对象的内容,主方法输出的是修改后的值。说明StringBuffer是引用数据类型。
public static void fun(StringBuffer s){ //接收StringBuffer引用
s.append("优快云 ").append("forfan06"); //修改StringBuffer内容
}
}
/*在任意位置处向StringBuffer添加内容!!!*/
package org.forfan06.stringbufferdemo;
public class StringBufferDemo03{
public static void main(String args[]){
StringBuffer buf = new StringBuffer();
buf.append("World!!!"); //向StringBuffer对象中添加内容
buf.insert(0, "Hello "); //在所有内容之前添加新的内容
System.out.println(buf); //输出结果是:Hello World!!!
buf.insert(buf.length(), " forfan06"); //在最后添加新的内容
System.out.println(buf); //输出结果是:Hello World!!! forfan06
}
}
/*StringBuffer字符串反转操作!!!*/
package org.forfan06.stringbufferdemo;
public class StringBufferDemo04{
public static void main(String args[]){
StringBuffer buf = new StringBuffer();
buf.append("World!!!");
buf.insert(0, "Hello ");
String str = buf.reverse().toString(); //将buf内容反转后,转换成String类型。效果与buf.reverse()然后直接输出buf一样。
System.out.println(str); //输出结果:!!!dlroW olleH
}
}
注意,在字符串的操作中,反转是一种较为常见的操作!!!最早的字符串反转操作实际上是通过入栈和出栈功能来完成的。
/*替换StringBuffer字符串中指定范围的内容!!!*/
package org.forfan06.stringbufferdemo;
public class StringBufferDemo05{
public static void main(String args[]){
StringBuffer buf = new StringBuffer();
buf.append("Hello ").append("World!!!");
System.out.println(buf); //输出结果:Hello World!!!
buf.replace(6, 11, "forfan06"); //用字符串forfan06替换buf从6到11的字符
System.out.println("内容替换之后的结果是:" + buf); //输出结果:Hello forfan06!!!
}
}
在String中如果要进行替换,则使用的是replaceAll()方法,而在StringBuffer中使用的是replace()方法!!!!
<span style="color:#ff0000;">public StringBuffer replace(int start, int end, String str)</span>
三个参数的含义: start - 开始索引,包含此点; end - 终止索引,不包含此点; str - 要插入的串
/*截取StringBuffer字符串!!!*/
package org.forfan06.stringbufferdemo;
public class StringBufferDemo06{
public static void main(String args[]){
StringBuffer buf = new StringBuffer();
buf.append("Hello ").append("World!!!");
System.out.println(buf); //输出结果:Hello World!!!
buf.replace(6, 11, "forfan06"); //用字符串forfan06替换buf从6到11的字符
System.out.println("内容替换之后的结果是:" + buf); //输出结果:Hello forfan06
String str = buf.substring(6, 15); //包含start点,不包括end点
System.out.println("截取之后的内容:" + str); //输出结果:forfan06!
}
}
/*删除StringBuffer字符串中指定范围的字符串!!!*/
package org.forfan06.stringbufferdemo;
public class StringBufferDemo07{
public static void main(String args[]){
StringBuffer buf = new StringBuffer();
buf.append("Hello ").append("World!!!");
System.out.println(buf); //输出结果:Hello World!!!
buf.replace(6, 11, "forfan06"); //用字符串forfan06替换buf从6到11的字符
System.out.println("内容替换之后的结果是:" + buf); //输出结果:Hello forfan06
String str = buf.delete(6, 15).toString(); //包含start点,不包括end点
System.out.println("删除之后的内容:" + str); //输出:Hello !!
}
}
查找指定的内容是否存在: 通过indexOf()方法可以查找指定的内容。如果找到了,则返回内容的位置;如果没有找到则返回-1
/*删除StringBuffer字符串中指定范围的字符串!!!*/
package org.forfan06.stringbufferdemo;
public class StringBufferDemo08{
public static void main(String args[]){
StringBuffer buf = new StringBuffer();
buf.append("Hello ").append("World!!!");
System.out.println(buf); //输出结果:Hello World!!!
if(buf.indexOf("Hello") == -1){
System.out.println("没有找到指定的内容");
}else{
System.out.println("找到了指定内容:" + buf.indexOf("Hello")); //输出结果:找到了指定内容:0
}
}
}
11.1.2 StringBuffer类的应用
对于频繁修改字符串内容的地方,最好使用StringBuffer类完成。 其优点可以从下面程序看出:
/*使用String类实现修改字符串的功能!!!*/
package org.forfan06.stringbufferdemo;
public class StringBufferDemo09{
public static void main(String args[]){
String str = "forfan06";
for (int i = 0; i < 100; i++){
str += i;
}
System.out.println(str);
}
}
上面程序中,虽然修改了字符串的内容,但实际上是通过不断修改对象的引用来实现的。其性能很差。 要解决此类的问题可以依靠StringBuffer类。因为StringBuffer类的内容是可以修改的。
/*使用StringBuffer类实现修改字符串的功能!!!*/
package org.forfan06.stringbufferdemo;
public class StringBufferDemo10{
public static void main(String args[]){
StringBuffer buf = new StringBuffer();
buf.append("forfan06");
for (int i = 0; i < 100; i++){
buf.append(i);
}
System.out.println(buf);
}
}
11.2 Runtime类
11.2.1 认识Runtime类
在Java中Runtime类表示运行时操作类,是一个封装了JVM进程的类,每一个JVM都对应一个Runtime类的实例,此实例由JVM运行时为其实例化。所以在JDK文档中不会发现任何有关Runtime类中构造方法的定义,这是因为Runtime类本身的构造方法是私有化的(单例设计), 如果想要去的一个Runtime类的实例,则只能通过以下方法:
Runtime run = Runtime.getRuntime();
也就是说Runtime类中提供了一个静态方法getRuntime()方法,此方法可以取得Runtime类的实例。
因为Runtime表示的是每一个JVM实例,则可以通过Runtime取得一些系统信息。 方法如下所示:
public static Runtime getRuntime() //取得Runtime类的实例
public long freeMemory() //返回Java虚拟机中的空闲内存量
public long maxMemory() //返回JVM的最大内存量
public void gc() //运行垃圾回收器,释放空间
public Process exec(String command) throws IOException //执行本机命令,返回的是一个Process的实例化对象
11.2.2 得到JVM的内存空间信息
使用Runtime类可以取得JVM中的内存空间,包括最大内存空间、空闲内存空间等等。通过这些信息可以清楚地知道JVM的内存使用情况。程序代码如下:
/*观察JVM的内存空间!!!*/
package org.forfan06.runtimedemo;
public class RuntimeDemo01{
public static void main(String args[]){
Runtime run = Runtime.getRuntime(); //通过Runtime类的静态方法为其进行实例化操作
System.out.println("JVM最大内存量:" + run.maxMemory()); //观察最大内存量,根据机器环境会存在差异
System.out.println("JVM空闲内存量:" + run.freeMemory()); //取得程序运行之前的内存空闲量
String str = "Hello " + "World" + "!!!" + "\t" +"Welcome " + "To" + "优快云" + "!";
System.out.println(str);
for (int i = 0; i < 100; i++){
str += i;
}
System.out.println("操作String之后的,JVM空闲内存量:" + run.freeMemory()); //观察有多个垃圾空间产生之后的内存空闲量
run.gc(); //运行垃圾收集,释放空间
System.out.println("垃圾回收之后,JVM空闲内存量:" + run.freeMemory());
StringBuffer buf = new StringBuffer();
buf.append("Hello ").append("World").append("!!!").append("\t").append("Welcome ").append("To ").append("优快云").append("!");
for (int i = 0; i < 100; i++){
buf.append(i);
}
System.out.println("操作StringBuffer之后的,JVM空闲内存量:" + run.freeMemory());
}
}
运行结果:
c:\JAVA Demo\runtimedemo>javac -d . RuntimeDemo01.java
c:\JAVA Demo\runtimedemo>java org.forfan06.runtimedemo.RuntimeDemo01
Max memory is: 259522560
Free meomory is: 15601616
After change String 100 time, free memory is: 15510968
After running gc, free memory is: 15746544
After change StringBuffer 100 times, free memory is: 15562848
11.2.3 Runtime类与Process类
可以直接使用Runtime类运行本机的可执行程序。例如下面程序可以调用本机的记事本程序,记事本程序的执行命令是“notepad.exe”。
/*Runtime类与Process类:调用本机可执行程序,如记事本!!!*/
package org.forfan06.runtimedemo;
import java.io.*;
package org.forfan06.runtimedemo;
public class RuntimeDemo02{
public static void main(String args{}){
Runtime run = Runtime.getRuntime();
try {
run.exec("notepad.exe"); //调用本机可执行程序。必须进行异常处理。因为exec定义出throws IOException
}catch(IOException e){
e.printStackTrace();
}
}
}
运行下面命令
javac -d . RuntimeDemo02.java
<pre class="java" name="code">java org.forfan06.runtimedemo.RuntimeDemo02
Runtime类对exec方法的定义如下:
public Process exec(String command) throws IOException
可以发现,exec方法的返回值类是一个Process的实例。并且调用exec方法处必须进行异常的捕获处理。
我们可以利用Process类来对系统进程进行控制。例如,销毁一个进程可以方法:
public void destroy()
/*Runtime类与Process类:调用本机可执行程序,如记事本!!!*/
package org.forfan06.runtimedemo;
import java.io.*;
package org.forfan06.runtimedemo;
public class RuntimeDemo02{
public static void main(String args{}){
Runtime run = Runtime.getRuntime();
Process pro = null;
try {
pro = run.exec("notepad.exe"); //调用本机可执行程序。必须进行异常处理。因为exec定义出throws IOException
}catch(IOException e){
e.printStackTrace();
}
try {
Thread.sleep(5000);
}catch(InterruptedException e){
e.printStackTrace();
}
pro.destroy();
}
}
11.3 国际化程序
国际化程序的操作就是指一个程序可以同时适应多门语言。让一个程序适应各个国家的语言要求。 其程序代码是不变的,只是其显示文字上有所差异而已!!!
实现国际化程序需要Locale类、属性文件、ResourceBundle类的支持!!!所谓的属性文件是指后缀为.properties的文件,文件中的内容保存结构为“key = value”形式(关于属性文件的具体操作可以参考jva类集部分)。 因为国际化的程序只是显示语言的不同,那么就可以根据不同的国家定义不同的属性文件,属性文件中保存真正要用的文字信息,要访问这些属性文件,可以使用ResourcdBundle类来完成。
11.3.1 国际化程序的实现思路
根据各个不同的国家配置不同的资源文件(资源文件也称为属性文件,因为其后缀是.properties),所有的资源文件以“key --> value”的形式出现(类似 message = 你好!),在程序执行中只是根据key找到value并将value的内容进行显示。也就是说,只要key不变,value的内容可以任意更换。
如果要实现java程序的国际化操作必须通过以下3个类完成:
- java.util.Locale: 用于表示一个国家语言类
- java.util.ResourceBundle: 用于访问资源文件,也就是属性文件
- java.text.MessageFormat: 格式化资源文件的占位字符串
这3个类的具体操作流程为: 通过Locale类所指定的区域码,然后ResourceBundle根据Locale类所指定的区域码找到对应的资源文件,如果资源文件存在动态文本,则使用MessageFormat进行格式化。
11.3.2 Locale类
要实现程序的国际化首先要认识Locale类,因为此类是实现国际化的一个重要类。 下面列出了Locale类的构造方法:
public Locale(String language) //根据语言代码构造一个语言环境
public Locale(String language, String country) //根据语言和国家构造一个语言环境
对于各个国家都有对应的ISO编码。 例如中国的编码是zh-CN; 英语-美国的编码是en-US; 法语的编码为fr-FR。
11.3.3 ResourceBundle类
ResourceBundle类主要作用是读取属性文件,读取属性文件时可以直接指定属性文件的名称(指定名称时不需要文件的后缀名),也可以根据Locale所指定的区域码来选取指定的资源文件,ResourceBundle类的常用方法如下:
public static final ResourceBundle getBundel(String baseName) //取得ResourceBundle的实例,并指定要操作的资源文件名称
public static final ResourceBundle getBundle(String baseName, Locale locale) //取得ResourceBundle的实例,并指定要操作的资源文件名称和区域码
public final String getString(String key) //根据key从资源文件中取出对应的value
如果现在要使用ResourceBundle对象,则肯定直接通过ResourceBundle类中的静态方法getBundle()取得。
实例:通过ResourceBundle取得资源文件中的内容
(1)定义资源文件Message.properties
info = HELLO
(2)从资源文件中取得内容
import java.util.ResourceBundle;
public class InterDemo01{
public static void main(String args[]){
ResourceBundle rb = ResourceBundle.getBundle("Message"); //找到资源文件
System.out.println("内容:" + rb.getString("info")); //从资源文件中取得内容
}
}
11.3.4 Java国际化程序实现
在程序中结合Locale类和ResourceBundle类一起完成一个国际化的程序开发。
下面完成一个简单的国际化操作,可以根据Locale所选择的国家不同,输出不同国家的“你好!” 中文: 你好! 英语: Hello! 法语: Bonjour!
首先根据不同的国家代码建立不同的属性文件,建立的属性文件与生成的*.class保存在同一个文件夹中。 因为现在程序要使用的有3种语言,所以要定义3种属性文件,在属性文件定义时必须按照“名称_国家代码”的形式命名,即所有相关的属性文件的名称全部一样,只有国家代码不一样,代码如下所示。
(1)中文的属性文件: Message_zh_CN.properties
info = \u4f60\u597d\uff01
实际上以上的信息就是中文的“你好!”, 在属性文件的操作中是不能直接写入中文字符的;就算是写入了中文字符,读取出来的也必然是乱码, 因此必须将相应的中文变为Unicode编码才可以,要成功地将一个中文编码变为Unicode编码,可以直接在运行处执行“native2ascii.exe”命令,输入相应的中文之后会自动将其进行编码。
*******native2ascii.exe是java的一个文件转码工具,是将特殊各异的内容 转为 用指定的编码标准文本形式统一的表现出来,通常位于JDK_home\bin目录下*******
(2)英语的属性文件: Message_en_US.properties
info = Hello!
(3)法语的属性文件: Message_fr_FR.properties
info = Bonjour!
通过Locale类和ResourceBundle类读取属性文件的内容,测试代码如下:
<pre class="java" name="code">import java.util.ResourceBundle;
import java.util.Locale;
public class InterDemo02{
public static void main(String args[]){
Locale zhLoc = new Locale("zh", "CN");
Locale enLoc = new Locale("en", "US");
Locale frLoc = new Locale("fr", "FR");
ResourceBundle zhrb = ResourceBundle.getBundle("Message", zhLoc); //找到资源文件
ResourceBundle enrb = ResourceBundle.getBundle("Message", enLoc);
ResourceBundle frrb = ResourceBundle.getBundle("Message", frLoc);
System.out.println("中文:" + zhrb.getString("info")); //从资源文件中取得内容
System.out.println("英语:" + enrb.getString("info"));
System.out.println("法语:" + frrb.getString("info"));
}
}
通过上面程序可以发现,根据Locale所制定的国家不同,读取的资源文件也不同,这样也就实现了国际化程序。
摘录: JAVA国际化
对Locale的设置:
Locale(String language, String country)
例如中文:zh_CN,new Locale("zh","CN");//表示中国的Locale,由语言,国家组成
美国英语:en_US,new Locale("en","US");
对ResourceBundle的操作:
ResourceBundle resource=ResourceBundle.
getBundle(String baseName, Locale locale);
baseName,跟properties要对应,是第一个"_"前面的内容(资源文件名=baseName_local.language_locale.country.properties);
locale,就是上面的Locale;
e.g.: Locale zhLoc = new Locale("zh","CN");
ResourceBundle resource=ResourceBundle.
getBundle("Message",zhLoc);
这里调用的资源文件就是Message_zh_CN.properties;
如果要更改语言类型。
ResourceBundle resource=ResourceBundle.
getBundle("Message",new Locale("en","US"));
这时候调用的资源文件就是Message_en_US.properties了。
如果要取出对应的Text,可以使用resource.getString("")方法。
11.3.5 处理动态文本
以上程序中,所有资源内容都是固定的,但是输出的消息中如果包含了一些动态文本,则必须使用占位符清楚地表示出动态文本的位置,占位符使用“ {编号} ” 的格式出现。使用占位符之后,程序可以直接通过MessgeFormat对信息进行格式化,为占位符动态设置文本内容。
MessageFormat是Format类的子类,Format类主要实现格式化操作,除了MessageFormat子类之外,Format还有NumberFormat、DateFormat两个子类,这两个子类也是非常重要的章节。
- 在进行国际化操作时,不光只有文字需要处理,实际上数字的显示、日期的显示都要符合各个区域的要求。 可以通过PC端Control Panel的“区域和语言选项”对话框观察到,需要同时改变的有数字、货币、时间等等。 所以Format类提供了MessageFormat、 NumberFormat、 DateFormat这3个子类。
现在以输出信息是“你好,xxx!",其中xxx的内容是程序动态设置的,所以此时要修改之前的3个属性文件,让其动态地接收程序的3个文本。
(1) 中文的属性文件: Message_zh_CN.properties
info = \u4f60\u597d\uff0c{0}\uff01
(2) 英文的属性文件: Message_en_US.properties
info = Hello, {0}!
(3) 法语的属性文件: Message_fr_FR.properties
info = Bonjour, {0}!
以上3个属性文件中,都加入了“ {0} ” ,表示一个占位符,如果有更多的占位符,则直接在后面继续加上“ {1} ” 、“ {2} ”即可。
然后继续使用之前的Locale类和ResourceBundle类读取资源文件的内容,但是读取之后的文件因为要处理占位符的内容,所以要使用MessageFormat类进行处理。主要使用以下方法:
public static String format(String pattern, Object...arguments)
上面format()方法中,第1个参数表示要匹配的字符串, 第2个参数“Object...arguments”表示输入参数可以任意多个,没有个数的限制!!!
测试代码如下:
import java.util.ResourceBundle;
import java.util.Locale;
import java.text.MessageFormat;
public class InterDemo03{
public static void main(String args[]){
Locale zhLoc = new Locale("zh", "CN");
Locale enLoc = new Locale("en", "US");
Locale frLoc = new Locale("fr", "FR");
ResourceBundle zhrb = ResourceBundle.getBundle("Message", zhLoc); //找到资源文件
ResourceBundle enrb = ResourceBundle.getBundle("Message", enLoc);
ResourceBundle frrb = ResourceBundle.getBundle("Message", frLoc);
String str1 = zhrb.getString("info");
String str2 = enrb.getString("info");
String str3 = frrb.getString("info");
System.out.println("中文:" + MessageFormat.format(str1, "某某某"));
System.out.println("英语:" + MessageFormat.format(str2, "forfan06"));
System.out.println("法语:" + MessageFormat.format(str3, "forfan08"));
}
}
补充:java新特性 -- 可变参数传递中可以接收多个对象。
在方法传递参数时可以使用如下的格式:
返回值类型 方法名称(Object...arguments)
表示方法可以接收任意多个参数,然后按照数组的方式输出即可!!!!!!!!!!!!!!!!!!
/*测试可变参数的传递*/
public class MultiArgs01{
public static void main(String args[]){
System.out.print("The first running: ");
fun("forfan06", "dylan", "优快云");
System.out.print("\nThe second running: ");
fun("Heaven");
}
public static void fun(Object...args){
for (int i = 0; i < args.length; i++){
System.out.print(args[i] + "、");
}
}
}
也可以使用数组的方式向可变参数传递内容: 和上面的方式效果是一样的。
/*使用数组传递参数*/
public class MultiArgs02{
public static void main(String args[]){
System.out.print("The first time running: ");
Object args1[] = {"forfan06", "dylan", "优快云"};
fun(args1);
System.out.print("\nThe second time running: ");
Object[] args2 = {"Heaven"};
fun(args2);
System.out.print("\nThe third time running: ");
Object args3[] = {};
fun(args3);
}
public static void fun(Object...args){
for (int i = 0; i < args.length; i++){
System.out.print(args[i] + "、");
}
}
}
11.3.6 使用类代替资源文件
可以使用属性文件保存所有的资源信息,当然,在java中也可以使用类来保存所有的资源信息,但是在开发中此种做法并不多见,主要还是以属性文件的应用为主。
大胆猜想:使用属性文件做到了数据和代码的分离~~~~~~~~~~~~~~~
与之前的资源文件一样,如果使用类保存信息,则也必须按照 key-value的形式出现,而且类的命名必须与属性文件一致;且此类必须继承自ListResourceBundle类!!!继承之后要覆写此类中的getContents()方法。
/*建立中文的资源类*/
package org.forfan06.internaltionaldemo;
import java.until.ListResourceBundle;
public class Message_zh_CN extends ListResourceBundle{
private final Object data[][] = {"info", "你好,{0}"};
public Object[][] getContents(){
return data;
}
}
/*在国际化中使用以上定义的类*/
import java.util.ResourceBundle;
import java.util.Locale;
import java.text.MessageFormat;
public class InterDemo04{
public static void main(String args[]){
Locale zhLoc = new Locale("zh", "CN");
ResourceBundle zhrb = ResourceBundle.getBunlde("org.forfan06.internationaldemo.Message", zhLoc);
String str1 = zhrb.getString("info");
System.out.println("中文:" + MessageFormat.format(str1, "某某某"));
}
}
!!!!!!!!!!!!!!!!在资源类中的属性一定是一个二维数组!!!!!!!!!!!!!!!!!
- 如果在程序中同时出现了Message.properties、Message_zh_CN.properties、 Message_zh_CN.class 该如何访问??? 只会使用一个,按照优先级使用。顺序为:Message_zh_CN.class、Message_zh_CN.properties、 Message.properties