1、课程名称:Java常用类库
2、知识点
2.1、本次预计讲解的知识点
1、 StringBuffer类及其操作方法
2、 国际化程序的实现
3、 日期操作
4、 比较器
3、具体内容
3.1、StringBuffer类(重点)
3.1.1、什么是StringBuffer
从开发角度来看,在实际的操作中经常会出现字符串内容循环修改的情况,但是如果此时直接使用String做,则代码性能会非常的低,因为String的内容不可改变。
所以在java中提供了一个可以修改的字符串类,此类称为StringBuffer。
在String中使用“+”号完成字符串的连接操作,那么在StringBuffer类中使用append()方法完成字符串的连接操作,StringBuffer类定义在java.lang包中。
3.1.2、StringBuffer的常用方法
StringBuffer本身是一个类,则类中会有很多的操作方法,操作方法如下:
No. 方法名称 类型 描述
1 public StringBuffer() 构造 构造一个空的StringBuffer对象
2 public StringBuffer(String str) 构造 将指定的String变为StringBuffer的内容
3 public StringBuffer(CharSequence seq) 构造 接收CharSequence接口的实例
4 public StringBuffer append(数据类型 b) 普通 提供了很多的append()方法,用于进行字符串连接
5 public StringBuffer delete(int start,int end) 普通 删除指定位置的内容
6 public int indexOf(String str) 普通 字符串的查询功能
7 public StringBuffer insert(int offset,数据类型 b) 普通 在指定位置上增加一个内容
8 public StringBuffer replace(int start,int end,String str) 普通 将指定范围的内容替换成其他内容
9 public StringBuffer reverse() 普通 字符串反转
10 public String substring(int start) 普通 字符串截取
11 public String substring(int start,int end) 普通 截取指定范围的字符串
注意:
1、 String本身已经实现了CharSequence接口,那么此接口的作用是进行字符的操作。
2、 既然所有的append()方法都返回StringBuffer的对象,那么此时就可以一直使用StringBuffer的append()方法进行字符串的连接,例如:StringBuffer对象.append().append().append() ….
3、 字符串的反转最早的时候可以采用栈的功能完成,此功能以后会讲解到。
4、 StringBuffer不能像String那样采用直接赋值的方式操作,必须使用构造方法的形式操作。
3.1.3、StringBuffer的操作实例
3.1.3.1、字符串连接
在字符串的连接中使用的方法是append()方法。
public class StringBufferDemo01 {
public static void main(String[] args) {
StringBuffer buf = new StringBuffer();
buf.append("hello"); // 添加字符串
buf.append("world").append("!!!\n").append("JAVA"); // 字符串连接
System.out.println(buf) ;
}
}
在以上的操作中所有的操作最终的返回结果是StringBuffer,那么此时就不能够使用String类进行接收,如果要将一个StringBuffer类的对象变为String的内容,可以使用StringBuffer类中从Object覆写的toString()方法完成。
public class StringBufferDemo02 {
public static void main(String[] args) {
StringBuffer buf = new StringBuffer();
buf.append("hello"); // 添加字符串
buf.append("world").append("!!!\n").append("JAVA"); // 字符串连接
String str = buf.toString();// 将StringBuffer变为String
System.out.println(str);
}
}
3.1.3.2、字符串替换操作
在StringBuffer中提供了一个replace()方法。可以直接将指定范围的内容进行替换。
public class StringBufferDemo03 {
public static void main(String[] args) {
StringBuffer buf = new StringBuffer();
buf.append("hello"); // 添加字符串
buf.append("world").append("!!!").append("JAVA"); // 字符串连接
buf.replace(0, 5, "lixinghua") ;
System.out.println(buf) ;
}
}
3.1.3.3、字符串反转操作
使用reverse()方法完成字符串的反转操作。
public class StringBufferDemo04 {
public static void main(String[] args) {
StringBuffer buf = new StringBuffer();
buf.append("hello"); // 添加字符串
buf.append("world").append("!!!").append("JAVA"); // 字符串连接
buf.reverse();// 字符串反转
System.out.println(buf);
}
}
3.1.3.4、字符串增加操作
StringBuffer中最大的好处是可以在指定的位置上加入指定的字符串。使用一系列的insert()方法完成。
public class StringBufferDemo05 {
public static void main(String[] args) {
StringBuffer buf = new StringBuffer();
buf.append("world").append("!!!").append("JAVA"); // 字符串连接
buf.insert(0, "hello ").insert(5, "~~~") ;
System.out.println(buf);
}
}
3.1.3.5、字符串截取操作
截取的方法是substring(),此方法String类中也存在。
public class StringBufferDemo06 {
public static void main(String[] args) {
StringBuffer buf = new StringBuffer();
buf.append("world").append("!!!").append("JAVA"); // 字符串连接
buf.insert(0, "hello ").insert(5, "~~~") ;
System.out.println(buf.substring(10));
System.out.println(buf.substring(0,14));
}
}
3.1.4、StringBuffer的应用
StringBuffer的应用只在于字符串的循环修改上,例如:如下的代码是一种错误的操作:
public class StringBufferDemo07 {
public static void main(String[] args) {
String str = "";
for (int x = 0; x < 1000; x++) {
str += x; // 进行字符串修改
}
System.out.println(str);
}
}
以上的操作性能绝对很低,那么对于这种情况,肯定要使用StringBuffer完成。如果使用StringBuffer则采用如下的形式:
public class StringBufferDemo08 {
public static void main(String[] args) {
StringBuffer str = new StringBuffer(); // StringBuffer必须通过构造方法实例
for (int x = 0; x < 1000; x++) {
str.append(x); // 进行字符串修改
}
System.out.println(str);
}
}
以上的操作结果的性能绝对要比之前的代码高很多,所以开发中只要是牵扯到循环修改数据的时候则肯定就一个原则,使用StringBuffer。
例如:在搜索引擎上,如果现在要想搜索多个关键字,多个关键字之间要加上“ ”,例如:“java vince 培训中心”。
既然StringBuffer本身是可以修改的,那么就可以将StringBuffer的对象传递的普通的方法之中完成引用传递。
public class StringBufferDemo09 {
public static void main(String[] args) {
StringBuffer str = new StringBuffer(); // StringBuffer必须通过构造方法实例
str.append("hello ");
fun(str); // 接收引用
System.out.println(str);//hello world
}
public static void fun(StringBuffer s) {
s.append("world");
}
}
采用String
package vince.stringbuffer;
public class StringBufferDemo01 {
public static void main(String[] args) {
String str="hello";
fun(str); // 接收引用
System.out.println(str);//hello
}
public static void fun(String s) {
s=s+("world");
}}
3.2、国际化程序(理解)
国际化程序可以这样理解:
• 同一套程序代码可以在各个语言环境下进行使用。
• 各个语言环境下,只是语言显示的不同,那么具体的程序操作本身都是一样的,那么国际化程序完成的就是这样的一个功能。
在实际的开发中如果要想进行一个国际化程序的开发,则肯定会非常的麻烦,而且此种项目一般较少,那么理解国际化的基本操作和实现思想即可。
所谓的国际化的实现核心在于显示的语言上,那么对于这些显示的语言,最好的做法是将其定义成若干个属性文件(文件后缀是*.properties),属性文件中的格式采用“key=value”的形式进行操作。
如果要想实现国际化程序则肯定需要依靠以下几个类:
• java.util.Locale类:用于表示一个国家的语言类
• java.util.ResourceBundle类:用于读取资源文件(属性文件,*.properties)
• java.text.MessageFormat类:用于进行文本的格式化操作
3.2.1、Locale类
Locale类本身表示一个国际化程序的语言操作类,是整个操作的核心,那么此类的操作方法如下:
No. 方法名称 类型 描述
1 public Locale(String language,String country) 构造 构造的时候跟上语言和国家
2 public static Locale getDefault() 普通 根据本地的语言环境得到Locale类对象
如果现在要想设置一个表示中文的Locale对象,则必须使用如下的方式:new Locale(“zh”,”CN”) ;
如果是英文的美国的语言环境:new Locale(“en”,”US”) ;
但是,以上的这些代码你是怎么知道的呢?
浏览器本身是不是属于多国语言的操作,那么实际上所有的国家的编码都可以通过浏览器找到。
【工具】 【Internet选项】 【语言】
没有任何的必要去强记这些操作,因为所有的编码在浏览器之上全都有显示。
3.2.2、ResourceBundle类
ResourceBundle类表示的是一个资源文件的读取操作,所有的资源文件需要使用ResourceBundle进行读取,读取的时候不需要加上文件的后缀,文件的后缀默认就是“*.properties”。
那么此类的方法如下:
No. 方法名称 类型 描述
1 public static final ResourceBundle getBundle(String baseName) 普通 根据本地的语言环境,并设置要操作的属性文件的名称,得到实例
2 public static final ResourceBundle getBundle(String baseName,Locale locale) 普通 根据指定的语言环境,设置要操作属性文件的名称,得到实例
3 public final String getString(String key) 普通 根据资源文件中的key取得对应的value
下面就使用ResouceBundler类得到资源文件中的内容。
1、 建立一个资源文件:Message.properties
info = Hello
2、 建立操作类,访问此属性文件,并且得到相关的信息
import java.util.ResourceBundle;
public class InternationDemo01 {
public static void main(String[] args) {
// 得到ResourceBundle类的实例,并且设置要操作的资源文件的名称
ResourceBundle res = ResourceBundle.getBundle("Message");
String str = res.getString("info"); // 根据所指定的资源文件中key取得与之对应value
System.out.println("内容:" + str) ;
}
}
那么以上的程序中可以发现通过ResouceBundle类找到了资源文件中的内容并进行显示。
3.2.3、实现国际化
下面编写一个程序,此程序可以显示中文的“你好”或者显示英文的“hello”,为了完成此种操作,那么此时需要建立多个资源文件,而且每一个资源文件的后缀是不同的,后缀的形式“资源文件_后缀.properties”/
1、 中文的资源文件:Message_zh_CN.properties
• 但是在Eclipse中在保存中文信息的时候将出现提示,说只能保存的是“ISO 8859-1的英文”内容,而不能保存中文内容,所以,此时如果是想操作中文的话,则必须对中文进行转码操作,使用JDK 目录中的一个“native2ascii.exe”。
info = \u4f60\u597d\uff01\uff01\uff01\uff01\uff01\uff01\uff01
“#”开头的信息表示的是注释,只要是注释中也不能存在中文。
2、 英文的资源文件:Message_en_US.properties
info = Hello !!!!!!!!!
之后,编写程序,同时建立两个Locale对象,一个表示读取中文,另外一个表示读取英文。
import java.util.Locale;
import java.util.ResourceBundle;
public class InternationDemo02 {
public static void main(String[] args) {
Locale zhLoc = new Locale("zh", "CN"); // 得到中文的Locale对象
Locale enLoc = new Locale("en", "US"); // 得到英文的Locale对象
// 得到ResourceBundle类的实例,并且取得资源文件的名称
// 以下得到的是中文的资源文件:Message_zh_CN.properties
ResourceBundle zhRes = ResourceBundle.getBundle("Message", zhLoc);
// 以下得到的是英文的资源文件:Message_en_US.properites
ResourceBundle enRes = ResourceBundle.getBundle("Message", enLoc);
System.out.println("中文内容:" + zhRes.getString("info"));
System.out.println("英文内容:" + enRes.getString("info"));
}
}
此时,只要设置了正确的区域,则会读取不同的文件,根据资源文件的所指定的区域。
因为此时存在了更家具体的语言的资源文件,所以“Message.properties”根本就不会被读取到。
如果此时要读的具体的资源文件不存在。则会使用默认的文件进行内容的读取。
3.2.4、处理动态文本
之前的所有操作中,读取的内容都是固定的,但是如果现在假设要想打印这样的信息“欢迎,Xxx光临!”,具体的登陆名称不是固定的,所以之前的操作就会存在问题了,那么此时就可以使用动态文本进行程序的处理。
如果要想进行动态的文本处理,则必须使用java.text.MessageFormat类完成。此类是java.text.Format的子类。
java.text.Format类中有三个常用的子类:MessageFormat、DateFormat、NumberFormat。
修改之前的资源文件:
1、 修改Message_zh_CN.properties文件:
info = \u4f60\u597d\uff0c{0}\uff0c\u6b22\u8fce{1}\u5149\u4e34\uff01
2、 修改Message_en_US.properties文件:
info = Hello ,{0}.Welcome {1} !
以上有两个参数,所以在使用的时候就需要准确的设置,使用的方法如下:
No. 方法名称 类型 描述
1 public static String format(String pattern,Object… arguments) 普通 设置动态文本,采用可变参数的形式进行设置
范例:读取动态文本
import java.text.MessageFormat;
import java.util.Locale;
import java.util.ResourceBundle;
public class InternationDemo03 {
public static void main(String[] args) {
Locale zhLoc = new Locale("zh", "CN"); // 得到中文的Locale对象
Locale enLoc = new Locale("en", "US"); // 得到英文的Locale对象
// 得到ResourceBundle类的实例,并且取得资源文件的名称
// 以下得到的是中文的资源文件:Message_zh_CN.properties
ResourceBundle zhRes = ResourceBundle.getBundle("Message", zhLoc);
// 以下得到的是英文的资源文件:Message_en_US.properites
ResourceBundle enRes = ResourceBundle.getBundle("Message", enLoc);
String zhInfo = zhRes.getString("info"); // 读取中文的内容,但是里面存在占位符
String enInfo = enRes.getString("info"); // 读取英文的内容,但是里面存在占位符
System.out
.println("中文内容:" + MessageFormat.format(zhInfo, "爪哇", "java"));
System.out.println("英文内容:"
+ MessageFormat.format(enInfo, "pig", "psir"));
}
}
以上在开发中使用较多,属于动态文本的操作。
3.3、其他操作类
以上的操作都属于比较大的一些操作类,代码都比较多,但是在Java的类库中还有一些小的操作类,下面来观察:Math、Random、Arrays类
3.3.1、Math类
Math类表示的是数学的计算操作,在Math类中提供了很多的操作方法。在此类中有一个方法很重要:
• 四舍五入:public static long round(double a)
public class MathDemo {
public static void main(String[] args) {
System.out.println("PI:" + Math.PI);
System.out.println("ROUND:" + Math.round(234.567));
System.out.println("ROUND:" + Math.round(234.567*100)/100.00);
}
}
3.3.2、Random类
java.util.Random类的作用是生成随机数,操作方法如下:
No. 方法名称 类型 描述
1 public Random() 构造 实例化对象
2 public int nextInt(int n) 普通 取出不大于n的整数
3 public float nextFloat() 普通 取出小数
范例:使用Random类生成10个不大于100的整数
package org.randomdemo;
import java.util.Random;
public class RandomDemo {
public static void main(String[] args) {
Random r = new Random();
for (int x = 0; x < 10; x++) {
int temp = r.nextInt(100);
System.out.print(temp + "、");
}
}
}
3.3.3、Arrays类
java.util.Arrays类在之前的数组排序的操作中已经讲解过了,实际上此类是一个专门操作数组的类,那么在此类中规定了以下的方法:
No. 方法名称 类型 描述
1 public static int binarySearch(数据类型[] a,数据类型 key) 普通 二分查找法,必须保证数组是排序的操作
2 public static boolean equals(数据类型[] a,数据类型[] a2) 普通 比较两个数组的内容是否相等
3 public static void fill(数据类型[] a,数据类型 val) 普通 以指定的内容对数组进行填充操作
4 public static void sort(数据类型[] a) 普通 数组的排序操作
5 public static String toString(数据类型[] a) 普通 输出数组
范例:观察Arrays类的使用
package org.randomdemo;
import java.util.Arrays;
public class ArraysDemo {
public static void main(String[] args) {
int x[] = { 1, 3, 4, 1, 43, 4, 5, 76, 7, 8, 9, 0, 1, 23, 6 }; // 数组无序
System.out.println("声明的数组:" + Arrays.toString(x)); // 数组输出
Arrays.sort(x); // 排序
System.out.println("排序的数组:" + Arrays.toString(x)); // 数组输出
System.out.println("二分查找法:" + Arrays.binarySearch(x, 5));
Arrays.fill(x, 3); // 数组中的内容使用数字3填充
System.out.println("填充的数组:" + Arrays.toString(x)); // 数组输出
}
}
3.4、日期操作类(重点)
在所有的编程语言中都会对日期有所支持,在Java中也提供了很多的类可以对日期进行支持的。
3.4.1、Date类
java.util.Date类的使用非常简单。此类使用的时候直接输出即可。
package org.datedemo;
import java.util.Date;
public class DateDemo {
public static void main(String[] args) {
Date date = new Date(); // 实例化Date对象
System.out.println(date); // 输出Date对象
}
}
程序运行效果:
Tue Mar 31 14:02:39 CST 2009
以上确实可以显示出时间,但是这样的时间格式并不完全符合于国人的喜好,所以,如果要想得到一个好的时间,则必须使用Calendar类,通过此类可以得到一个比较完整的时间。
3.4.2、Calendar
使用此类可以将时间精确到毫秒显示,此类的定义如下:
public abstract class Calendar
extends Object
implements Serializable, Cloneable, Comparable
此类是一个抽象类,既然是抽象类则使用的时候肯定要依靠其子类进行实例化操作。使用“GregorianCalendar”类就可以完成功能。
package org.calendardemo;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class CalendarDemo {
public static void main(String[] args) {
Calendar calendar = new GregorianCalendar();
System.out.println("YEAR: " + calendar.get(Calendar.YEAR));
System.out.println("MONTH: " + (calendar.get(Calendar.MONTH) + 1));
System.out.println("DAY_OF_MONTH: "
+ calendar.get(Calendar.DAY_OF_MONTH));
System.out
.println("HOUR_OF_DAY: " + calendar.get(Calendar.HOUR_OF_DAY));
System.out.println("MINUTE: " + calendar.get(Calendar.MINUTE));
System.out.println("SECOND: " + calendar.get(Calendar.SECOND));
System.out
.println("MILLISECOND: " + calendar.get(Calendar.MILLISECOND));
}
}
此时就可以通过这样的操作得到一个系统的时间了,但是这样操作得到时间太麻烦了,现在能不能单独设计一个类,那么以后通过此类就可以得到以下的时间格式:
• 简单日期:2009-03-31
• 简单日期时间:2009-03-31 14:09:23.123
• 得到完整日期:2009年-3月31日
• 得到完整日期时间;2009年03月31日14时09分23秒123毫秒
• 时间戳:20090331140923123
我的答案:
package org.datetimedemo;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class DateTime {
private int year;
private int month;
private int day;
private int hour;
private int minute;
private int second;
private int millisecond;
private DateTime(){
Calendar calendar = new Calendar();
this.year=calendar.get(Calendar.YEAR);
this.month=(calendar.get(Calendar.MONTH) + 1);
this.day=calendar.get(Calendar.DAY_OF_MONTH);
this.hour=calendar.get(Calendar.HOUR_OF_DAY);
this.minute=calendar.get(Calendar.MINUTE);
this.second=calendar.get(Calendar.SECOND);
this.millisecond=calendar.get(Calendar.MILLISECOND);
}
private String insertZero(int temp,int len){
StringBuffer stb=new StringBuffer();
stb.append(temp);
if(stb.length()<2){
stb.insert(0,0);
}
return stb.toString();
}
public String getDate(){
StringBuffer stb=new StringBuffer();
stb.append(this.year).append('-');
stb.append(this.insertZero(this.month, 2)).append('-');
stb.append(this.insertZero(this.day, 2));
return stb.toString();
}
public String getDateTime(){
StringBuffer stb=new StringBuffer();
stb.append(this.getDate()).append(" ");
stb.append(this.insertZero(this.hour, 2)).append(":");
stb.append(this.insertZero(this.minute, 2)).append(":");
stb.append(this.insertZero(this.second, 2)).append(".");
stb.append(this.insertZero(this.millisecond, 3));
return stb.toString();
}
public String getFullDate(){
StringBuffer stb=new StringBuffer();
stb.append(this.year).append('年');
stb.append(this.insertZero(this.month, 2)).append('月');
stb.append(this.insertZero(this.day, 2)).append("日");
return stb.toString();
}
public String getFullDateTime(){
StringBuffer stb=new StringBuffer();
stb.append(this.getFullDate());
stb.append(this.insertZero(this.hour, 2)).append("时");
stb.append(this.insertZero(this.minute, 2)).append("分");
stb.append(this.insertZero(this.second, 2)).append("秒");
stb.append(this.insertZero(this.millisecond, 3)).append("豪秒");
return stb.toString();
}
public String getDateTimeStamp(){
StringBuffer stb=new StringBuffer();
stb.append(this.year);
stb.append(this.insertZero(this.month, 2));
stb.append(this.insertZero(this.day, 2));
stb.append(this.insertZero(this.hour, 2));
stb.append(this.insertZero(this.minute, 2));
stb.append(this.insertZero(this.millisecond, 3));
return stb.toString();
//用String用于总涉及修改、连接的字符串,性能低下,改用StringBuffer
//return ""+this.year+this.month+this.day+this.hour+this.minute +this.millisecond ;//前面加""使得真个表达式按照字符串的格式进行连接,不是整数相加
}
//为了测试程序把main方法写在此处
public static void main(String args[]){
DateTime dt=new DateTime();
System.out.println(dt.getDate());
System.out.println(dt.getDateTime());
System.out.println(dt.getFullDate());
System.out.println(dt.getFullDateTime());
System.out.println(dt.getDateTimeStamp());
}
}
要完成此功能则肯定应该将其封装成一个类,但是在操作的时候需要处理前导0,也就是说如果一些日期的时间数前面没有0的话,需要加上0。
而且此操作最终肯定是以字符串的形式返回,而且此字符串的内容会多次修改,肯定使用StringBuffer类完成。
package org.calendardemo;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class DateTime {
private Calendar calendar = null;
public DateTime() {
this.calendar = new GregorianCalendar(); // 构造方法中进行对象的实例化操作
}
public String getSimpleDate() { // 取得一个简单的日期
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(); // 将StringBuffer变为String
}
public String getDate() { // 取得一个简单的日期
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(); // 将StringBuffer变为String
}
public String getSimpleDateTime() { // 取得一个简单的日期时间
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(" ");
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(); // 将StringBuffer变为String
}
public String getDateTime() { // 取得一个简单的日期时间
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("日");
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(); // 将StringBuffer变为String
}
public String getTimeStamp() { // 取得一个时间戳
StringBuffer buf = new StringBuffer();
buf.append(calendar.get(Calendar.YEAR));
buf.append(this.addZero((calendar.get(Calendar.MONTH) + 1), 2));
buf.append(this.addZero(calendar.get(Calendar.DAY_OF_MONTH), 2));
buf.append(this.addZero(calendar.get(Calendar.HOUR_OF_DAY), 2));
buf.append(this.addZero(calendar.get(Calendar.MINUTE), 2));
buf.append(this.addZero(calendar.get(Calendar.SECOND), 2));
buf.append(this.addZero(calendar.get(Calendar.MILLISECOND), 3));
return buf.toString(); // 将StringBuffer变为String
}
private String addZero(int temp, int len) {
StringBuffer buf = new StringBuffer();
buf.append(temp);
while (buf.length() < len) {
buf.insert(0, 0);// 在前面补上数字0
}
return buf.toString();
}
public static void main(String args[]) {
System.out.println(new DateTime().getTimeStamp());
}
}
以上的操作基本功能实现了,但是如果所有的操作都按照此种格式编写的话,则代码太麻烦了,之前学习过了Date类,直接输出此类就可以完成时间的取得,但是这个类的显示形式并不好,所以,此时,如果可以将Date类的显示形式进行修改,那么得出的时间就应该可以很好的符合用户的需要了的。
3.4.3、DateFormat
java.text.DateFormat类表示的是日期的格式化类,可以将一个日期按照指定的风格进行格式化的操作。
public abstract class DateFormat extends Format
DateFormat类本身也属于抽象类,但是此抽象类使用的时候可以不经过子类实例化,在此类中定义了如下的操作:
No. 方法名称 类型 描述
1 public static final DateFormat getDateInstance() 普通 得到默认的对象
2 public static final DateFormat getDateInstance(int style,Locale aLocale) 普通 根据Locale的指定区域得到对象
3 public static final DateFormat getDateTimeInstance() 普通 得到默认的日期时间对象
4 public static final DateFormat getDateTimeInstance(int dateStyle,int timeStyle,Locale aLocale) 普通 得到指定Locale的日期、时间对象
5 public final String format(Date date) 普通 对日期进行格式化的操作
但是在讲解之前需要注意的是DateFormat类本身也属于Format类的子类。之前讲解MessageFormat类的时候曾经说过MessageFormat类也是Format的子类。
一个国际化的操作不光只有文字,还有货币显示和日期的显示风格。
package org.dateformatdemo;
import java.text.DateFormat;
import java.util.Date;
public class DateFormatDemo01 {
public static void main(String[] args) {
DateFormat df1 = DateFormat.getDateInstance();// 得到默认的对象
DateFormat df2 = DateFormat.getDateTimeInstance();// 得到默认的对象
System.out.println(df1.format(new Date())); // 格式化日期时间
System.out.println(df2.format(new Date())); // 格式化日期时间
}
}
此时的日期格式,采用的是本机默认的语言环境。当然,也可以为其加入一些其他的地区标记。
package org.dateformatdemo;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
public class DateFormatDemo02 {
public static void main(String[] args) {
DateFormat df1 = DateFormat.getDateInstance(DateFormat.YEAR_FIELD,
new Locale("en", "US"));// 得到默认的对象
DateFormat df2 = DateFormat.getDateTimeInstance(DateFormat.YEAR_FIELD,
DateFormat.ERA_FIELD, new Locale("en", "US"));// 得到默认的对象
System.out.println(df1.format(new Date())); // 格式化日期时间
System.out.println(df2.format(new Date())); // 格式化日期时间
}
}
所以,一般来讲在开发中比较常用的操作就是DateFormat类的子类:SimpleDateFormat类。
3.4.4、SimpleDateFormat
SimpleDateFormat类的主要功能是完成日期显示格式的转换,例如,现在有如下的一个日期时间:
• 原始日期:2009-03-31 14:55:42.718
• 转换日期:2009年03月31日14时55分42秒718毫秒
那么,这个时候就必须使用SimpleDateFormat类完成功能,但是在转换的时候需要采用如下的步骤:
1、 指定一个模板,并根据此模板,从第一个日期中取出所有的时间数字
2、 所有的时间数字将使用Date保存
3、 将所有的时间数字重新进行格式的转换
如果要想将全部的日期-时间数组取出,则必须定义相应的操作模板,此模板要求如下:
No. 日期 模板 描述
1 年 y 表示年,年一般是四位数字,所以需要使用“yyyy”表示
2 月 M 表示月,月一般是二位数字,所以需要使用“MM”表示
3 日 d 表示日,日一般是二位数字,所以需要使用“dd”表示
4 时 HH 表示时,时一般是二位数字,所以需要使用“HH”表示
5 分 mm 表示分,分一般是二位数字,所以需要使用“mm”表示
6 秒 ss 表示秒,秒一般是二位数字,所以需要使用“ss”表示
7 豪秒 S 表示毫秒,豪秒一般是三位数字,所以需要使用“SSS”表示
范例:下面进行模板的转换
package org.dateformatdemo;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SimpleDateFormatDemo01 {
public static void main(String[] args) throws Exception {
String str = "2009-03-31 14:55:42.718";
String pat1 = "yyyy-MM-dd HH:mm:ss.SSS";
String pat2 = "yyyy年MM月dd日 HH时mm分ss秒SSS毫秒";
SimpleDateFormat sdf1 = new SimpleDateFormat(pat1); // 定义第一个模板
SimpleDateFormat sdf2 = new SimpleDateFormat(pat2); // 定义第一个模板
Date date = sdf1.parse(str); // 将字符串的日期时间取出
String newDate = sdf2.format(date); // 重新进行新的格式套用
System.out.println(newDate);
}
}
3.5、比较器(重点)
3.5.1、问题的引出
java.util.Arrays类本身可以完成数组的排序功能,在此类中定义了如下的一个方法:
• 为对象数组排序public static void sort(Object[] a)
例如:现在有如下的操作类
package org.compareabledemo01;
public class Person {
private String name ;
private int age ;
public Person(){}
public Person(String name,int age){
this.setName(name) ;
this.setAge(age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "姓名:" + this.name + "\t" + "年龄:" + this.age;
}
}
之后,要求产生6个此类的对象,并定义成对象数组,并进行排序的操作。
package org.compareabledemo01;
import static java.util.Arrays.*; // 静态导入
public class ComparableDemo01 {
public static void main(String[] args) {
Person[] per = { new Person("张三", 18), new Person("李四", 22),
new Person("王五", 10), new Person("赵六", 30),
new Person("孙七", 33), new Person("王八", 90) };
sort(per); // 排序操作
fun(per); // 打印输出
}
public static void fun(Person p[]) {
for (Person x : p) {
System.out.println(x);
}
}
}
以上程序的操作语法并没有任何的问题,下面执行程序,出现了以下的错误:
Exception in thread “main” java.lang.ClassCastException: org.compareabledemo01.Person cannot be cast to java.lang.Comparable
at java.util.Arrays.mergeSort(Arrays.java:1144)
at java.util.Arrays.sort(Arrays.java:1079)
at org.vince.compareabledemo01.ComparableDemo01.main(ComparableDemo01.java:10)
出现了类型转换异常,但是所有的代码中并没有类型转换。
从Arrays类的sort()方法的定义上可以发现,如果没有实现Comparable接口会造成此问题。那么为什么要实现此接口呢?
思考:之前进行数字排序,应该按照大小进行排序的操作,但是现在给的是对象。对象现在有排序规则吗?
3.5.2、排序规则 —— Comparable接口
所有的排序规则必须依靠Comparable接口完成,此接口定义格式如下:
public interface Comparable{
public int compareTo(T o) ;
}
以上的方法是compareTo()方法,此方法是作为比较操作存在的,但是此方法的返回值是int型数据。此方法有三种返回结果:
• 返回0:表示相等
• 返回1:表示大于
• 返回-1:表示小于
所以,此时,如果要想正确的进行排序操作的话,则Person类必须实现Comparable接口。
package org.compareabledemo02;
public class Person implements Comparable<Person> {
private String name;
private int age;
public int compareTo(Person o) {
if (this.age > o.age) {
return 1;
} else if (this.age < o.age) {
return -1;
} else {
return 0;
}
}
public Person() {
}
//setter及getter方法
public String toString() {
return "姓名:" + this.name + "\t" + "年龄:" + this.age;
}
}
下面再次运行比较程序,观察结果。
只要以后牵扯到对象的比较,则永远都需要Comparable接口完成。
3.5.3、深入研究Comparable接口(理解)
从Comparable操作的compareTo()方法上可以发现,此方法返回的有三种值:-1、0、1,那么实际上这种排序规则就非常类似于数据结构中学习过的BT(Binary Tree)算法。
现在假设有如下的一组数据:“5、6、1、2、3、90、35、7”,要求现在使用BT算法排序。
二叉树算法的排序规则:
1、 选择第一个元素作为根节点
2、 之后如果元素大于根节点放在右子树,如果元素小于根节点,则放在左子树
3、 最后按照中序遍历的方式进行输出,则可以得到排序的结果(左根右)
中序遍历之后结果是:1 2 3 5 6 7 35 90
此时,排序的功能完成。
在执行之前先来观察以下的一种操作代码:
package org.compareabledemo03;
public class ComDemo {
public static void main(String[] args) {
Comparable com1 = "hello"; // 向上转型
Comparable com2 = 1; // 向上转型
System.out.println(com1);
System.out.println(com2);
}
}
只要实现了Comparable接口的对象,都可以使用Comparable接口进行内容的接收。
既然明白之后,那么下面动手自己手工实现一个二叉树。
package org.compareabledemo03;
class BinaryTree { // 定义二叉树的操作类
class Node {
private Comparable data; // 保存数据
private Node left;// 表示左子树
private Node right;// 表示右子树
public Node(Comparable data) {
this.data = data;
}
public void addNode(Node newNode) {
if (newNode.data.compareTo(this.data) < 0) {
if (this.left == null) { // 当前的左子树是否等于空
this.left = newNode;
} else {
this.left.addNode(newNode);// 继续向下继续判断
}
}
if (newNode.data.compareTo(this.data) >= 0) {
if (this.right == null) { // 当前的右子树是否等于空
this.right = newNode;
} else {
this.right.addNode(newNode);
}
}
}
public void printNode() {
if (this.left != null) {
this.left.printNode();
}
System.out.println(this.data);
if (this.right != null) {
this.right.printNode();
}
}
}
private Node root; // 定义根节点
public void add(Comparable data) { // 表示增加节点
Node newNode = new Node(data);
if (this.root == null) { // 此时没有根节点,第一个元素作为根节点
this.root = newNode;
} else { // 判断节点是放在左子树还是右子树
this.root.addNode(newNode);
}
}
public void print() { // 打印节点
this.root.printNode();
}
}
public class BinaryTreeDemo {
public static void main(String[] args) {
BinaryTree bt = new BinaryTree();
bt.add(5);
bt.add(3);
bt.add(1);
bt.add(90);
bt.add(90);
bt.add(100);
bt.add(60);
bt.print();
}
}
在面试之前最好将此程序巩固一下,因为一个是链表,一个是二叉树是最有可能在笔试中出现的题目。
3.5.4、Comparator(理解)
以上的操作代码中,基本上都是在类定义的时候已经加入了比较器的操作,那么如果现在一个类已经定义完成了,再加入比较器,则肯定会很麻烦。
在java.util.Arrays类中存在以下的一个方法:public static void sort(T[] a, Comparator super T> c)
此方法用于接收java.util.Comparator的比较操作,Comparator实际上就属于一个挽救的比较器接口。
package org.compareabledemo04;
import java.util.Comparator;
public class PersonComparator implements Comparator {
public int compare(Person o1, Person o2) {
if (o1.getAge() > o2.getAge()) {
return 1;
} else if (o1.getAge() < o2.getAge()) {
return -1;
} else {
return 0;
}
}
}
此时Person类已经开发完成了,以上的操作类只是挽救了一个比较的操作而已。
package org.vince.compareabledemo04;
import static java.util.Arrays.*; // 静态导入
public class ComparatorDemo {
public static void main(String[] args) {
Person per[] = { new Person("张三", 18), new Person("李四", 22),
new Person("王五", 10), new Person("赵六", 30),
new Person("孙七", 33), new Person("王八", 90) };
sort(per,new PersonComparator()); // 排序操作
fun(per); // 打印输出
}
public static void fun(Person[] p) {
for (Person x : p) {
System.out.println(x);
}
}
}
两种操作比较起来,Comparable接口使用的是最多的,而对于Comparator接口一般使用较少,因为要开发两个类比较麻烦。
3.6、对象的克隆技术(理解)
将一个对象复制一份,称为对象的克隆技术。
在Object类中存在一个clone()方法:protected Object clone() throws CloneNotSupportedException
但是,并不是所有的对象都具备克隆的功能,所以,如果某个类的对象要想被克隆,则对象所在的类必须实现Cloneable接口。但是此接口并没有方法,那么此接口实际上将作为一个标识接口出现。
package org.vince.clonedemo;
public class Person implements Cloneable { // 表示可以被克隆
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
下面对程序代码进行测试:
package org.clonedemo;
public class ClonePerson {
public static void main(String[] args) throws CloneNotSupportedException {
Person per = new Person();
per.setName("张三");
per.setAge(20);
Person p = (Person) per.clone();
p.setName("李四");
System.out.println(per);
System.out.println(p);
}
}
此时,证明对象已经被成功的克隆了。
4、总结
1、 String与StringBuffer的区别
2、 Date可以取得一个时间,SimpleDateFormat可以对时间进行格式的转换
4、 Comparable比较器的使用,了解其基本的操作原理
5、 了解国际化程序的实现
5、作业
1、 使用SimpleDateFormat类修改之前的DateTime类,使用SimpleDateFormat完成取得时间、日期、时间戳的操作。
2、 定义一个StringBuffer类对象,然后通过append()方法向对象里添加26个小写字母,要求每次只添加一个,共添加26次。之后按照逆序的方式输出,并且可以删除掉前五个字符。
3、 编写程序,用0~1之间的随机数来模拟扔硬币试验,统计扔1000次后出现正、反面的次数并输出。
4、 按照“姓名:年龄:成绩 | 姓名:年龄:成绩”的格式定义字符串:“张三:21:98|李四:22:89|王五20:70”,要求将每组值分别保存在Student对象之中,并对这些对象进行排序,排序的原则如下“按照成绩由高到低排序,如果成绩相等,则按照年龄由低到高排序”。