一.单元概述
Java API(Java Application Programming Interface)是Java应用程序编程接口的缩写,Java中的API,就是JDK提供的具有各种功能的Java类,灵活使用Java API能够大大提高使用Java语言编写程序的效率,本章对Java中提供的最常用的工具类进行介绍,包括Object、包装类、字符串处理类String、StringBuffer以及StringBuilder、Math类以及日期类。
二,教学重点与难点
重点:
(1) Object类及其常用方法
(2) 包装类的概念及其和基本数据类型、字符串之间的相互转换
(3) 三种不同的字符串类及其用法
(4) 日期类Date、Calendar及SimpleDateFormat的使用
难点:
(1) Object类的常用方法
(2) 包装类和基本数据类型、字符串之间的相互转换
(3) 字符串类的使用
(4) 日期类的使用
1.1 Java API
Java API(Java Application Programming Interface)是Java应用程序编程接口的缩写,Java中的API,就是JDK提供的具有各种功能的Java类,由于Java类库非常庞大,而且在不断壮大,本文不可能一一介绍所有类的使用方法,读者应该养成查阅Java API文档的习惯,就像查询字典一样,在需要用到某个类的时候,可以从Java API文档中获得这个类的详细信息。
Java API的帮助文档可到 http://docs.oracle.com/javase/8/docs/api/ 下载,灵活使用Java API能够提高使用Java语言编写程序的效率,下面对Java中提供的最常用的包进行介绍。
- java.lang:Java语言包, 该包中提供了利用Java语言进行程序设计的基础类,比如String、Math、System类,在任何类中,该包中的类不需要使用import语句进行导入,都会被自动导入。
- java.util:该包中主要包含一些实用的工具类,比如集合框架类、日期处理类、字符串解析类、随机数生成类等。
- java.awt:该包中主要包含处理用户界面和绘制图形图像的各种类。
- java.io:该包中主要包含处理输入、输出编程的各种类。
- java.net:该包中主要包含处理网络编程的各种类。
- java.sql:该包中主要包含处理数据库编程的各种类。
- java.lang包中常用的类如表1-1所示。
表1-1 java.lang包中的常用类
Boolean | Object | Error |
---|---|---|
Byte | String | Throwable |
Character | StringBuffer | Exception |
Double | StringBuilder | ClassNotFoundException |
Float | System | NullPointerException |
Integer | Math | NumberFormatException |
Long | Runnable(接口) | RuntimeException |
Short | Thread | ArithmeticException |
java.util包中常用的类如表1-2所示。
表1-2 java.util包中的常用类
Collection(接口) | Arrays | Calendar |
---|---|---|
Iterator(接口) | Set(接口) | Date |
ListIterator(接口) | HashSet | Random |
List(接口) | TreeSet | Scanner |
ArrayList | Map(接口) | Collections |
LinkedList | HashMap | |
Vector | Hashtable | |
Stack | TreeMap |
表1-3 java.io包中的常用类
BufferedInputStream | FileReader | PrintWriter |
---|---|---|
BufferedOutputStream | FileWriter | Reader |
BufferedReader | InputStream | Writer |
BufferedWriter | InputStreamReader | Serializable(接口) |
DataInputStream | OutputStream | Externalizable接口) |
DataOutputStream | OutputStreamWriter | IOException |
File | ObjectInputStream | FileNotFoundException |
FileInputStream | ObjectOutputStream | InvalidClassException |
FileOutputStream | PrintStream |
java.net包中常用的类如表1-4所示。
表1-4 java.net包中的常用类
ServerSocket |
---|
Socket |
UnknownHostException |
1.2 Object类
在Java中,有这样一个类,它是所有类的祖先类,也就是说,任何类都是其子孙类,它就是java.lang.Object。如果一个类没有显式地指明其父类,那么它的父类就是Object,也就是说,下面两条语句的作用是一样的:
public class ClassName{…} 等价于 public class ClassName extends Object{…}
如同我们称自己为炎黄子孙一样,所有的类都可以称为Object子孙类,所以,在Object类中定义的方法,在所有类中都可以使用。同时,Object类也是Java语言中唯一一个没有父类的类。
做为一个超级祖先类,Object类为子类提供了一些public修饰的方法,便于子类覆盖来实现子类自己特定的功能,下面我们要详细介绍其中的equals()方法、hashCode()方法和toString()方法。
(1)public boolean equals(Object obj)
该方法在Object类中的作用是:比较两个引用所指向的对象是否是同一个对象,即两个引用所指向对象的地址是否相等。
注意,通常情况下,Object的任何子类均可以按照自己的需要对equals()方法进行覆盖,以改变此方法的含义,所以有的类中equals()方法是比较地址,有的类中该方法不是比较地址,具体的,就看子类中该方法是如何实现的。
【例1-1】 定义公民类Citizen,覆盖该类的equals()方法,该方法的定义为:如果两个公民的身份id相同,则为同一个公民。
public class Citizen{
private int id;
private String name;
public Citizen(int id, String name) {
this.id = id;
this.name = name;
}
//覆盖父类的equals()方法
public boolean equals(Object o) {
Citizen c = (Citizen) o;
if (c.id == this.id) {
return true;
} else {
return false;
}
}
//主方法,测试equals()方法
public static void main(String args[]) {
Citizen xiaoming = new Citizen(21000, "xiaoming");
Citizen mingming = new Citizen(21000, "mingming");
System.out.println(xiaoming.equals(mingming));
}
}
//程序运行结果:true
程序分析:上例定义了一个Citizen类,此类包含了公民的两个属性id和name,同时覆盖了equals()方法,该方法实现为:当传入对象的id和当前对象的id相等时,返回true,否则返回false。所以在main()方法最后一行调用equals()方法时,调用的实际上是Citizen类的equals()方法,xiaoming和mingming对象的id都为21000,所以程序运行结果为true。而假设如果Citizen类没有覆盖equals()方法,则在最后一行调用equals()方法时,实际调用的Object类的equals()方法,该方法比较两个对象是否指向同一块地址,所以肯定返回false,读者可自行验证。
接下来说一下”==”运算符和equals()方法的区别。”==”是比较运算符,既能用于比较两个基本数据类型,也能用于比较两个引用数据类型,当比较基本数据类型时,判断两个基本数据类型的字面量是否相等,当比较两个引用数据类型时,判断两个”引用”的值是否相等,也就是两个”引用”是否指向同一个对象。而equals()方法只能用于比较引用数据类型,Object类的equals()方法比较两个对象引用的值是否相等,但equals()方法通常都会被子类覆盖,所以子类中equals()方法的含义,就要看子类中的具体实现了。
【例1-2】 == 和 equals()方法在String类中的区别
String str1 = "hello";
String str2 = new String("hello");
System.out.println(str1==str2);
System.out.println(str1.equals(str2));
程序运行结果:
false
true
程序分析:因为String是引用数据类型,使用”==”运算符比较两个引用地址是否相同,第二行使用关键字new重新生成了一个String对象,所以第三行输出为false。在String类中,覆盖了Object的equals()方法,该方法判断两个String对象的内容是否相等,所以第4行输出为true。
(2) public int hashCode()
该方法返回对象的哈希码,哈希码是一个代表对象的十六进制整数,好比对象的身份证号。在程序运行期间,每次调用同一个对象的hashCode()方法,返回的哈希码必定相同,但是多次执行同一个程序,程序的一次执行和下一次执行期间,同一个对象的哈希码不一定相同。实际上默认的哈希码是将对象的内存地址通过某种转换得到的,所以不同对象会有不同的哈希码。
在Java中有个规定:如果equals()方法返回两个对象是相等的,那这两个对象上调用hashCode()返回的整数必须相等,否则在使用Hash类型集合时就会产生错误,所以在我们覆盖equals()方法同时,还要记得覆盖hashCode()方法。需要说明,如果equals()返回两个对象不等,它们的hashCode()也可以返回相同的整数,但是最好让它们的hashCode()返回不同的整数,这有利于提高Hash类型集合的性能。
那么重写equals()方法时,一定要重写hashcode()方法吗?
先说说hashcode()方法调用的条件,如果你想往map里面放一个类作为map的键值,这个类又是你自己设计的,或者这个类不是你写的但是你修改了这个类的equals()方法,这个时候,你就要重写hashcode()方法,这样当你往map里放值的时候,系统会调用这个对象的hashcode()方法来生成相应的hash值,来映射相应的对象。
如果同一个类的两个对象的属性值相等,那么他们的hashcode()一定相等吗?这个要看你具体如何实现你的hashcode()方法,如果你希望他们的值一样hashcode()也一样,你就可以这样实现。但是hashcode的实现,一般要满足几个特征,比如自反性,传递性什么的。自反性,即对任意一个非空的指引值x,x.equals(x)永远返回true,传递性,当x.equals(y)返回true并且y.equals(z)返回true时,x.equals(z)也返回true。
(3)public String toString()
该方法返回对象的字符串表示,形如:类名@hashcode,toString()方法是一个从字面上就容易理解的方法,它的功能是得到一个能够代表该对象的一个字符串:类名+”@”+代表该对象的一个唯一的16进制数,例如下面这个例子:
【例1-3】 查看Object类和String类的toString()方法返回值。
public class ObjectSample {
public static void main(String args[]){
Object o = new Object();
System.out.println(o);
System.out.println(o.toString());
String s = new String("hello");
System.out.println(s);
}
}
程序运行结果:
java.lang.Object@1db9742
java.lang.Object@1db9742
hello
程序分析:前两个输出结果表示对象o在内存中的地址,事实上返回这样的字符串没有什么实际的意义。一般子类都会覆盖该方法,让它返回有意义的文本。例如上例中的对象s的输出为”hello”,这是因为字符串String类对toString()进行了覆盖,让它返回此String的本身内容。
下面通过实例来看一下如何在自己编写的类中覆盖equals()方法和toString()方法。
【例1-4】 equals()方法和toString()的覆盖
//圆类
public class MyCircle {
private int radius;//半径
public MyCircle(int r) {
radius = r;
}
public int getRadius() {
return radius;
}
public void setRadius(int r) {
radius = r;
}
//覆盖Object中的equals()方法
public boolean equals(Object obj) {
MyCircle o = (MyCircle) obj;
if (o.getRadius() == this.radius)
return true;
else
return false;
}
//覆盖Object中toString方法
public String toString() {
return "radius = " + radius;
}
}
//测试类
public class Test {
public static void main(String[] args) {
MyCircle obj1 = new MyCircle(3);
System.out.println("obj1:" + obj1.toString());
MyCircle obj2 = new MyCircle(3);
System.out.println("obj2:" + obj2.toString());
if (obj1.equals(obj2))
System.out.println("the two objects are equal");
else
System.out.println("the two objects are not equal");
}
}
程序运行结果:
obj1:radius = 3
obj2:radius = 3
the two objects are equal
程序分析:从程序的运行结果来看,我们创建的两个MyCircle对象是相等的,因为MyCircle类中覆盖的equals()方法规定,当两个MyCircle对象的半径相等时,认为这两个对象相等。而我们在调用MyCircle类的构造方法创建对象时设定这两个MyCircle对象的半径均为3,所以obj1.equals(obj2)返回true。
1.3包装类
出于对性能的考虑,Java编程语言不把基本数据类型看作对象。因为处理对象需要额外的系统开销,所以,如果将基本数据类型当作对象,就会给语言性能带来负面影响。然而,许多Java中的方法需要将对象作为参数,为了让基本数据类型的这些数据也具有面向对象的特性,Java 编程语言提供了包装类来将基本数据类型看作对象,基本类型和包装类型之间能够互相转换。
1.3.1包装类简介
包装类型和基本数据类型的名字基本相同,首字母变成了大写,但是int和char的包装类型为Integer和Character。基本数据类型不是对象层次结构的组成部分。有时需要像处理对象一样处理这些基本数据类型,可通过相应的“包装类”来将其“包装”。表1-5列出了基本数据类型及其包装类型。
表1-5基本数据类型及其包装类型
基本类型 | 包装类 |
---|---|
boolean | Boolean |
byte | Byte |
char | Character |
double | Double |
float | Float |
int | Integer |
long | Long |
short | Short |
八种基本类型都有对应的包装类,图1-1是它们的继承层次结构。
基本数据类型的变量没有默认值,而包装类型的变量默认值是null。在这八个包装类中,除了Integer和Character类以外,其它六个类的类名和基本数据类型一致,只是类名的第一个字母大写即可。对于包装类说,这些类的用途主要包含两种:
a、作为和基本数据类型对应的类类型存在,方便涉及到对象的操作。
b、包含每种基本数据类型的相关属性如最大值、最小值等,以及相关的操作方法。
1.3.2 基本类型与包装类之间的转换
包装类有什么作用呢?包装类能够完成数据类型之间(除boolean)的相互转换,尤其是基本数据类型和String类型的转换。包装类中包含了对应基本数据类型的值,封装了String和基本数据类型之间相互转换的方法,还有一些处理这些基本数据类型时非常有用的属性和方法。
由于八个包装类的使用比较类似,下面以最常用的Integer类为例子介绍包装类的实际使用。
1、实现int和Integer类之间的转换
在实际转换时,使用Integer类的构造方法和Integer类内部的intValue()方法实现int和Interger类型之间的相互转换,实现的代码如下:
int n = 10;
//将int类型转换为Integer类型
Integer in = new Integer(n);
//将Integer类型的对象转换为int类型
int m = in.intValue();
2、Integer类内部的常用方法
在Integer类内部包含了一些和int操作有关的方法,下面介绍一些比较常用的方法:
a、parseInt方法
public static int parseInt(String s)
该方法的作用是将数字字符串转换为int数值。在界面编程中,将字符串转换为对应的int数字是一种比较常见的操作。使用示例如下:
String s = “123”;
int n = Integer.parseInt(s);
则int变量n的值是123,该方法实际上实现了字符串和int之间的转换,如果字符串包含非数字字符,则程序执行将出现异常。
另外一个parseInt方法:
public static int parseInt(String s, int radix)
则实现将字符串按照参数radix指定的进制转换为int,使用示例如下:
int n = Integer.parseInt(“120”,10);
//将字符串”120”按照十进制转换为int,则结果为120
int n = Integer.parseInt(“12”,16);
//将字符串”12”按照十六进制转换为int,则结果为18
int n = Integer.parseInt(“ff”,16);
//将字符串”ff”按照十六进制转换为int,则结果为255
这样可以实现更灵活的转换。
b、toString方法
public static String toString(int i)
该方法的作用是将int类型转换为对应的String类型。
使用示例代码如下:
int m = 1000;
String s = Integer.toString(m);
则字符串s的值是”1000”。
1.3.3 字符串与基本数据类型、包装类型转换
1、包装类转换成字符串
几乎java.lang.Object类派生的所有类提供了toString()方法,即将该类转换为字符串。例如:Characrer、Integer、Float、Double、Boolean、Short等类的toString()方法分别用于将字符型、整型、浮点型、双精度浮点型、短整型等类转换为字符串。举例如下所示:
int i1=10;
float f1=3.14f;
double d1=3.1415926;
Integer i1 = new Integer(i1);//生成Integer类
Float f1 = new Float(f1); //生成Float类
Double d1 = new Double(d1); //生成Double类
//分别调用包装类的toString() 方法转换为字符串
String si1 = i1.toString();
String sf1 = f1.toString();
String sd1 = d1.toString();
Sysytem.out.println(“si1”+si1);
Sysytem.out.println(“sf1”+sf1);
Sysytem.out.println(“sd1”+sd1);
整数转换成字符串也可以采用如下方法:
int myInt = 1234;
String myString = “” + myInt;
其它数据类型可以利用同样的方法转换成字符串
2、字符串转换为基本数据类型
字符串转换成整数:
String myNumber =”1234”;
int myInt = Integer.parseInt(myNumber);
字符串转换成byte、shor、int、float、double、long 等数据类型,可以分别参考Byte、Short、Integer、Float、Double、Long 类的parseXXX() 方法。
其实,JDK自从5.0版本以后,就引入了自动拆装箱的语法,也就是在进行基本数据类型和对应的包装类转换时,系统将自动进行,这将大大方便程序员的代码书写。使用示例代码如下:
//int类型会自动转换为Integer类型
int m = 12;
Integer in = m;
//Integer类型会自动转换为int类型
int n = in;
所以在实际使用时的类型转换将变得很简单,系统将自动实现对应的转换。Java允许基本类型和包装类型之间进行自动转换。例如可以自动装箱将一个基本数据类型转换为对象。
Integer intObject=new Integer(2);等价于 Integer intObject=2; //自动装箱
将基本类型转换为包装类对象的过程称为装箱,相反的过程称为拆箱。如果一个基本类型出现在需要对象的环境中,编译器会将基本类型值进行自动装箱;如果一个对象出现在需要基本类型的环境中,编辑器将对象进行自动拆箱。考虑下面的例子:
1:Integer [] intArray ={1,2,3};
2:System.out.printLn(intArray[0] + intArray [1] + intArray[2]);
在第一行中,基本类型1、2、3被自动装箱成对象new Integer(1)、new Integer(2)、new Integer(3) ,在第二行中,对象intArray[0]、intArray[0]、intArray[0]被自动转换为int值,然后进行相加。
【例1-5】包装类、基本数据类型和字符串之间转换的综合案例。
public class TestWrapper {
public static void main(String ars[]){
Double obj_d = new Double(3.14); //基本数据类型转换为包装类
double d = obj_d.doubleValue(); //包装类转换为基本数据类型
Integer obj_i = new Integer("5"); //字符串转换为包装类
String s = obj_i.toString(); //包装类转换为字符串转换
double n = Double.parseDouble("5"); //字符串类转换为基本数据类型
String s = obj_j.toString(); //基本数据类型转换为字符串类型
Integer aa = 9; //自动装箱
int bb = aa; //自动拆箱
}
}
程序分析:通过以上案例可以看出,基本数据类型和字符串类型转换为包装类,使用包装类的构造器来完成;包装类转换为基本数据类型,调用包装类的xxxValue()方法来完成;包装类转换为字符串类型,调用包装类的toString()方法;字符串转换为基本数据类型,调用包装类的parseXXX()方法;基本数据类型转换为字符串类型,先把基本数据类型转换为包装类型,然后再调用包装类的toString()方法;装箱就是把基本类型用它们相应的包装类型包装起来,使其具有对象的性质。int包装成Integer、float包装成Float,拆箱和装箱相反,将包装类型的对象转化成基本类型类型的数据。
1.4 字符串类
字符串是我们在编程中最常使用的一种数据类型,Java中用类来描述字符串,其中最常用的字符串处理类是String,此外还有StringBuffer和StringBuilder。在本节,我们会了解每种字符串的特点,以便能在应用时选择合适的字符串类型。字符串不属于8种基本数据类型,而是一种引用类型。String对象代表一组不可改变的Unicode字符序列,对它的任何修改实际上又产生一个新的字符串,String类是final类型的类,所以String类对象的内容一旦被初始化就不能再改变。StringBuffer对象代表一组可改变的Unicode字符序列,StringBuilder是JDK5.0版本后引入的字符串处理类,其中的方法与StringBuffer类的相同。
1.4.1 String对象的创建
String是比较特殊的数据类型,它不属于基本数据类型,但是可以和使用基本数据类型一样直接赋值,也可以像引用数据类型一样,使用关键字new进行实例化。
String类型对象的实例化有两种方式:
–静态方式(常用):直接给变量赋值,如:String s1 = “abc”; String s2 = “abc”;
–动态方式:使用new运算符动态的给变量赋值,如:String s3 = new String(“abc”); String s4 = new String(“abc”);
那么这两种方式创建的字符串是同一个字符串对象吗?答案是否定的,这两种方式创建的字符串对象是有区别的。
区别在于:使用静态方式创建的字符串,如果前后两次创建的字符串内容相同,则在堆区的常量池中只会产生一个字符串对象,即两个引用指向同一块地址。而使用动态方式创建的字符串,不管前后两次创建的字符串内容是否相同,每创建一次,都会在堆内存中会产生出不同的对象,即两个引用指向不同的地址。
对于上面采用静态方式创建的字符串s1、s2与采用动态方式创建字符串s3和s4其内存空间分配方式示意如图1-3所示。
图1-3 字符串动态创建与静态创建示意图
动态创建String对象时需要用到构造方法,String类的常用的构造方法如下,更多构造方法请直接查阅Java API。
- 初始化一个新创建的String 对象,它表示一个空字符序列。
String 变量名 = new String() ;
初始化一个新创建的String对象,表示一个空字符串(” “);注意空字符串与null的区别,空字符串表示String对象的内容为空,而null表示String类的变量不指向任何的String对象。
- 初始化一个新创建的 String对象,表示一个与该参数相同的字符序列。
String 变量名 = new String (String value) ;
- String(char chars[]) 使用一个字符数组创建一个String对象。
另外String在使用的时候不需要用import语句导入。
注意,当使用”+”运算符进行运算时,如果参与运算的有字符串,则”+”的含义为进行字符串连接,当参与运算的没有字符串时,则”+”的含义为算术运算符加号。例如:
String str1 = “hello “;
String str2 = “world”;
System.out.println (str1 + str2); //输出结果为”hello world”
System.out.println(5 + 6 + ‘A’); //输出结果为76
System.out.println(5 + 6 + “A”); //输出结果为11A
System.out.println(5 + “A” +6); //输出结果为5A6
1.4.2 String 对象的不可变性
任何一个String对象在创建之后都不能对它的内容作出任何改变。对于连接、获得子串和改变大小写等操作,如果返回值同原字符串不同,实际上是产生了一个新的String对象,在程序的任何地方,相同的字符串字面常量都是同一个对象,下面的代码会改变字符串s的内容吗?
String s = “Java”;
s = “HTML”;
答案是不会。第一条语句创建了一个内容为”Java”的String对象,并将其引用赋值给s。第二条语句创建了一个内容为”HTML”的新String对象,并将其引用赋值给s。赋值后第一个String对象仍然存在,但是不能再访问它,因为变量s现在指向了新的对象HTML,如图1-4所示。
图1-4字符串是不可改变的,一旦创建,它们的内容不能修改
因为字符串在程序设计中是不可改变的,但同时又会被频繁地使用,所以Java虚拟机为了提高效率并节省内存,对具有相同字符串序列的字符串直接使用同一个实例。例如下面的语句:
String str1 = “hello”;
String str2 = new String(“hello”);
String str3 = “hello”;
System.out.println(str1==str2);
System.out.println(str1==str3);
程序运行结果:
false
true
1.4.3 字符串的比较
String类提供了多种对字符串比较的方法,具体如表1-6所示。
表1-6 字符串比较方法
方法 | 含义 |
---|---|
boolean equals(String) | 判断两个字符串对象的内容是否相等 |
boolean equalsIgnoreCase(String) | 比较两个字符串的内容是否相等,忽略大小 |
int compareTo(s1:String) | 返回一个大于0、等于0或者小于0的整数以表明这个字符串是大于、等于还是小于s1 |
int compareToIgnoreCase(String) | 除了不区分大小写外,其他都和compareTo是一样的 |
boolean regionMatchs(int, String, int, int) | 如果这个字符串指定的子域精确匹配字符串s1中指定的子域则返回true |
boolean startsWith(String prefix) | 测试此字符串是否以指定的前缀开始 |
boolean endsWith(String suffix) | 测试此字符串是否以指定的后缀结束 |
例如,下面的语句先显示true,然后显示false.
String s1 = new String("welcome to Java");
String s2 = "welcome to Java";
String s3 = "welcome to C++";
System.out.println(s1.equals(s2)); //true
System.out.println(s1.equals(s3));//false
【例1-5】字符串equals方法练习
String s1 = "abc";
String s2 = new String("abc");
String s3 = new String("abc");
String s4 = "abc";
System.out.println(s1.equals(s2)); //true
System.out.println(s1.equals(s4)); // true
System.out.println(s2.equals(s3)); //true
程序分析:equals方法用来比较两个字符串是否相等。
【例1-6】重写equals()方法,判断不同的点,是否是坐标系上的同一点。
//点类
public class Point {
private int x; //x坐标
private int y;//y坐标
public Point(int x,int y){
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
@Override
public String toString() {
return "("+x+","+y+")";
}
//如果x,y的值相同,就认为是坐标上的同一点
public boolean equals(Object obj) {
if(obj == null){
return false;
}
if(this == obj){
return true;
}
if(!(obj instanceof Point)){
return false;
} else {
Point p = (Point)obj;
if(p.x==x&&p.y==y){
return true;
} else {
return false;
}
}
}
}
//测试类
public class TestPoint {
public static void main(String[] args) {
Point p1 = new Point(55,66);
Point p2 = new Point(55,66);
Point p3 = new Point(22,33);
System.out.println(p1.equals(p2));
System.out.println(p1==p2);
System.out.println(p1.equals(p3));
System.out.println(p1==p3);
}
}
程序运行结果:
true
false
false
false
程序分析:上例测试中main()方法中实例化了3个点,分别使用equals方法与==进行比较,因为Point类覆盖了父类的equals()方法,该方法判断如果两个Point对象的x坐标和y坐标相等,则返回true,所以第1行输出为true,”==”运算符比较引用数据类型时,判断两个引用是否指向同一个对象,p1,p2,p3都分别是不同的对象,所以第2行和第4行输出为false。
compareTo方法也可以用来对字符串进行比较。方法如下:
s1.compareTo(s2)
如果s1与s2相等,那么该方法返回值为0;如果按字典序(即以统一码的顺序)s1小于s2,那么方法返回值小于0;如果按字典序s1大于s2,方法返回值大于0。
方法compareTo返回的实际值是依据s1和s2从左到右数第一个不同字符之间的距离得出的,例如,假设s1为“abc”,s2为“abg”,那么s1.compareTo(s2)返回-4。首先比较的是s1与s2中第一个位置的字符(a与a)。因为它们相等,所以比较第二个位置的两个字符(b与b)。因为它们也相等,所以比较第三个位置的两个字符(c与g)。由于字符c比字符g小4,所以比较之后返回-4。如果使用像>、>=、<或<=这样的比较运算符比较两个字符串,就会发生错误,替代的方法就是使用s1.compareTo(s2)来进行比较。
如果两个字符串相等,equals方法返回true;如果不相等,方法返回false。compareTo方法会根据一个字符串是否等于、大于或小于另一个字符串,分别返回0、正整数或负整数。
1.4.4字符串与数组之间的转换
(1)与字符数组之间的转换
为了将字符串转换成一个字符数组,可以使用toCharArray方法。例如,下述语句将字符串”Java “转换成一个字符数组:
Char[] chars= “Java”.toCharArray();
因此chars[0]是’J’,chars[1]是’a’,chars[2]是’v’,chars[3]是’a’。
还可以使用方法getChars(int srcBegin,int srcEnd,char[]dst,int dstBegin)将下标从srcBegin到srcEnd-1的子串复制到字符数组dst中下标从dstBegin开始的位置。例如,下面的代码功能是把字符串”CS3720”中下标从2到6-1的子串”3720”复制到字符数组dst中,赋值时下标从4开始的位置:
Char[] dst={‘j’,’a’,’v’,’a’,’1’,’3’,’0’,’1’};
“CS3720”.getChars(2,6,dst,4);
这样,dst就变成了{‘j’,’a’,’v’,’a’,’3’,’7’,’2’,’0’}。
为了将一个字符数组转换成字符串,应该使用构造方法String(char[])或者方法valueOf(char[])。例如,下面的语句使用String构造方法把一个字符数组构造成一个字符串:
String str = new String(new char[]{‘j’,’a’,’v’,’a’});
下面的语句使用valueOf方法把一个字符数组构造成一个字符串:
String str = String.valueOf(new char[]{‘j’,’a’,’v’,’a’});
(2)与字节数组之间的转换
Java中能够把字符串转换为字节数组,有3种形式。
形式1:public byte[] getBytes()
方法定义:以默认编码把字符串转换为字节数组
形式2:public byte[] getBytes(Charset charset)
方法定义:按照指定的字符集把字符串转换为字节数组
形式3:public byte[] getBytes(String charsetName)
方法定义:按照指定的字符集把字符串转换为字节数组
【例1-7】 使用平台的默认字符集,统计一个字符串所占用的字节数。
public class TestStringByte{
public static void main(String[] args) {
String str = "Java语言程序设计";
byte bytes[] = str.getBytes();
System.out.println(bytes.length);
}
}
程序运行结果:
16
程序分析:通过getBytes()方法把字符串转换成字节数组,字节数组的长度就是字符串所占的字节数。
【例1-8】通过字节数组创建字符串对象
public class TestStringCharset {
public static void main(String[] args) {
byte[] bName = new byte[10];
String name1 = "张三";
try{
bName = name1.getBytes("utf-8"); //这种编码方式一个中文占三个字节
String name2 = new String(bName,"utf-8");
System.out.println("name2="+name2);
for(int i = 0;i< bName.length;i++){
System.out.print(bname[i]);
}
}catch (UnsupportedEncodingException e){
e.printStackTrace();
}
}
}
程序运行结果:
name2=张三
-27-68-96-28-72-119
程序分析:getBytes()是将一个字符串转化为一个字节数组。String的getBytes()方法是得到一个系统默认的编码格式的字节数组。将一个String类型的字符串中包含的字符转换成byte类型并且存入一个byte数组中。在Java中的所有数据底层都是字节,字节数据可以存入到byte数组。存储字符数据时(字符串就是字符数据),会先进行查表,然后将查询的结果写入设备,读取时也是先查表,把查到的内容打印到显示设备上,getBytes()是使用默认的字符集进行转换,getBytes(“utf-8”)是使用UTF-8编码表进行转换。例如: byte [] b_Uf8 = “中国”.getBytes(“UTF-8”);//获得字节数组 String s_Utf8 = new String(b_utf8,”UTF-8”);//使用指定的UTF-8编码来将byte[]解析成字符串。
(3)String类与数组的转换示意图
字符串可以和字节数组与字符数组之间互相转换,
其转换过程如图1-5所示
1.4.5 String中的常用的方法
String的一些常用方法如表1-7所示。
表1-7 String类的常用方法
方法 | 含义 |
---|---|
byte[] getBytes(Charset charset) | 使用给定的 charset将此String 编码到byte 序列,并将结果存储到新的byte 数组 |
String[] split(String regex) | 根据给定正则表达式的匹配拆分此字符串。 |
String replace(char oldChar, char newChar) | 返回一个新的字符串,它是通过用newChar 替换此字符串中出现的所有oldChar 得到的 |
String toUpperCase() | 将String对象中的所有字符都转换为大写 |
String toLowerCase() | 将String对象中的所有字符都转换为小写 |
char charAt(int) | 返回指定索引处的 char 值 |
String substring(int begin) | 返回一个新字符串,该字符串是从begin开始的字符串的内容 |
String substring(int begin,int end) | 返回一个新字符串,该字符串是从begin开始到end-1结束的字符串的内容 |
int indexOf/lastIndexOf(char) | 返回指定字符在此字符串中第一次/最后一次出现处的索引。 |
int indexOf/lastIndexOf(char,int) | 从指定的索引开始搜索,返回在此字符串中第一次/最后一次出现指定字符处的索引 |
int indexOf/lastIndexOf(String) | 返回第一次出现的指定子字符串在此字符串中的索引 |
int indexOf/lastIndexOf(String,int) | 从指定的索引开始搜索,返回在此字符串中第一次/最后一次出现指定字符串处的索引 |
String trim() | 返回新的字符串,忽略前导空白和尾部空白 |
int length() | 返回此字符串的长度 |
String concat(String str) | 将指定字符串连接到此字符串的结尾 |
byte[] getBytes() | 使用平台的默认字符集将此String 编码为byte 序列,并将结果存储到一个新的byte 数组中 |
下面对常用的一些方法进行详细说明,为了便于说明,方法中使用的示例字符串为:str=”this is a test!”;
(1)求长度
方法定义:public int length() 。
方法描述:获取字符串中的字符的个数。
例如:
str.length()
结果:
15
(2)获取字符串中的字符
方法定义:public char charAt(int index)。
方法描述:获取字符串中的第index个字符,从0开始。
例如:
str.charAt(3)
结果:
s
注意:是第4个字符。
【例1-9】 统计一个字符串中字符e出现的次数。
public class TestString {
public static void main(String[] args) {
String s = "abecedkjkacedjkdseddklj";
int num = 0;
for(int i=0; i<s.length(); i++){
char c = s.charAt(i);
if(c == 'e'){
num++;
}
}
System.out.println("该字符串中字符e出现"+num+"次");
}
}
程序分析:程序中num的作用是用来记录字符’e’出现的次数。
(3)取子串
有两种形式:
形式1如下:
方法定义:public String substring(int beginIndex,int endIndex)。
方法描述:获取从beginIndex 开始到endIndex 结束的子串,包括beginIndex,不包括endIndex。
例如:
str.substring(1,4)
结果:
his
形式2如下:
方法定义:public String substring(int beginIndex)
方法描述:获取从beginIndex开始到结束的子串
例如:
str.substring(5)
结果:
is a test!
(4)定位字符或者字符串
有4种形式:
形式1如下:
方法定义:public int indexOf(int ch)
方法描述:定位参数所指定的字符。
例如:
str.indexOf(‘i’)
结果:
2
形式2如下:
方法定义:public int indexOf(int ch,int index)
方法描述:从index开始定位参数所指定的字符。
例如:
str.indexOf(‘i’,4)
结果:
5
形式3如下:
方法定义:public int indexOf(String str)
方法描述:定位参数所指定的字符串。
例如:
str.indexOf(“is”)
结果:
2
形式4如下:
方法定义:public int indexOf(String str,int index)
方法描述:从index开始定位str所指定的字符串。
例如:
str.indexOf(“is”,6)
结果:
-1表示没有找到
(5)替换字符和字符串
有3种形式:
形式1如下:
方法定义:public String replace(char c1,char c2)
方法描述:把字符串中的字符c1替换成字符c2
例如:
str.replace(‘i’,’I’)
结果:
thIs Is a test!
形式2如下:
方法定义:public String replaceAll(String s1,String s2)
方法描述:把字符串中出现的所有的s1替换成s2
例如:
replaceAll(“is”,”IS”)
结果:
thIS IS a test!
形式3如下:
方法定义:public String replaceFirst(String s1,String s2)
方法描述:把字符串中的第一个s1替换成s2
例如:
replaceFirst(“is”,”IS”)
结果:
thIS is a test!
(6)比较字符串内容
两种形式:
形式1如下:
方法定义:public boolean equals(Object o)
方法描述:比较字符串内容是否与参数相同,区分大小写。
例如:
str.equals(“this”)
结果:
false
形式2如下:
方法定义:public boolean equalsIgnoreCase(Object o)
方法描述:比较是否与参数相同,不区分大写小。
例如:
str.equalsIgnoreCase(“this”)
结果:
false
(7)大小写转换
转换成大写或者转换成小写。
转换成大写:
方法定义:public String toUpperCase()
方法描述:把字符串中的所有字符都转换成大写。
例如:
str.toUpperCase()
结果:
THIS IS A TEST!
转换成小写:
方法定义:public String toLowerCase()
方法描述:把字符串中的所有字符都转换成小写。
例如:
str.toLowerCase()
结果:
this is a test!
(8)前缀和后缀
判断字符串是否以指定的参数开始或者结尾。
判断前缀:
方法定义:public boolean startsWith(String prefix)
方法描述:字符串是否以参数指定的子串为前缀。
例如:
str.startsWith(“this”)
结果:
true
判断后缀:
方法定义:public boolean endsWith(String suffix)
方法描述:字符串是否以参数指定的子串为后缀。
例如:
str.endsWith(“this”)
结果:
false
import java.io.DataInputStream;
public class StringTest {
public static void main(String args[]){
System.out.println("计算第一个字符串在第二个字符串中出现的次数");
// 通过如下语句,实例化一个从控制台读取输入内容的对象din
DataInputStream din = new DataInputStream(System.in);
try{
System.out.println("请输入第一个字符串");
String str1 = din.readLine();//读入一行数据
System.out.println("请输入第二个字符串");
String str2 = din.readLine();
String str3 = str2.replace(str1,"");//把str2中的str1用空串替换,并赋给str3
int count = str2.length() - str3.length();//str2的长度减去str3的长度
System.out.println(str1+"在"+str2+"中出现的次数为:"+count);
}catch(Exception e){
System.out.println(e.toString());
}
}
}
程序运行结果:
计算第一个字符串在第二个字符串中出现的次数
请输入第一个字符串
ab
请输入第二个字符串
abcedabsdabajab
ab在abcedabsdabajab中出现的次数为:4
需要注意的是String本身是一个常量,一旦一个字符串创建了,它的内容是不能改变的,那么如何解释下面的代码:
s1+=s2;
这里并不是把字符串s2的内容添加到字符串s1的后面,而是新创建了一个字符串,内容是s1和s2的连接,然后把s1指向了新创建的这个字符串。如果一个字符串的内容经常需要变动,不应该使用String,因为在变化的过程中实际上是不断创建对象的过程,这时候应该使用StringBuffer。
【例1-11】使用字符串中的常用方法实现Email格式的判断。
public class EmailCheckException extends Exception {
public EmailCheckException(String msg) {
super(msg);
}
public class StringExercise {
public static void checkEmail(String email) throws EmailCheckException {
int len = email.length(); //email的长度
int begin = email.indexOf('@'); //从开始检索字符@在email中的位置
int end = email.lastIndexOf('@'); //从结尾检索字符@在email中的位置
int dot = email.indexOf('.', begin);//检索.是否在@后存在
//判断长度是否不超过20
if (email.length() > 20)
throw new EmailCheckException("Email长度不能大于20");
//判断是否唯一包含@
else if (begin != end)
throw new EmailCheckException("Email中含有多个@");
//判断@是否存在或在开头或在结尾
else if (begin == -1 || begin == 0 || begin == (len - 1))
throw new EmailCheckException("Email中没有@或@位置错误");
//判断@后是否有.
else if (dot == -1)
throw new EmailCheckException("@后缺少域分隔符");
//判断@是否在末尾
else if (dot == (len - 1))
throw new EmailCheckException("分隔符错误");
}
public static void main(String args[]) {
String email = args[0];
try {
checkEmail(email);
} catch (EmailCheckException e) {
e.printStackTrace();
}
}
}
}
程序分析:自定义了一个EmailCheckException异常类,当Email格式不符合要求时将抛出一个异常对象。程序中使用字符串的length()方法判断Email长度是否正确。通过indexOf()和lastIndexOf()分别从头和从尾查找@的位置,若从头查找和从尾查找到的@位置相等,说明字符串中只有一个@字符,否则存在多个@字符,则不是合法的Email格式。最后查找字符“.”的位置。若其位置在@字符后,且字符串中含有这个字符则是合法的Email格式。
1.5Date类中常用方法
表1-10列出了Date类的常用方法。
表1-10 Date类的常用方法
方法 | 含义 |
---|---|
boolean after(Date when) | 测试此日期是否在指定日期之后 |
boolean before(Date when) | 测试此日期是否在指定日期之前 |
int compareTo(Date anotherDate) | 比较两个日期的顺序。如果参数 Date 等于此 Date,则返回值 0;如果此 Date 在 Date 参数之前,则返回小于 0 的值;如果此 Date 在 Date 参数之后,则返回大于 0 的值。 |
boolean equals(Object obj) | 比较两个日期的相等性。 |
Date类不支持国际化。现在我们更应该多使用Calendar类来实现日期和时间字段之间转换,使用DateFormat类来格式化和分析日期字符串,Date中的相应方法已废弃。
【例1-17】Date类常用方法练习
public class TestDate {
public static void main(String[] args) {
Date now = new Date();//获取当前系统时间
System.out.println(now); //获取1970年1月1日1000毫秒之后的时间
Date d1 = new Date(1000);
System.out.println(now.after(d1));
System.out.println(now.before(d1));
System.out.println(now.compareTo(d1));
}
}
程序运行结果:
true
false
1
程序分析:本程序主要练习日期类的几个常用方法,这几个方法的详细说明请参见表1-10。
1.5.3 Calender类
从JDK1.1版本开始,在处理日期和时间时,系统推荐使用Calendar类进行实现。在设计上,Calendar类的功能要比Date类强大很多,而且在实现方式上也比Date类要复杂一些,下面就介绍一下Calendar类的使用。
java.util.Calendar是一个抽象的基类,可以提取详细的日历信息,例如年、月、日、小时、分和秒,Calendar的子类可以实现特定的日历系统。在实际使用时实现特定的子类的对象,创建对象的过程对程序员来说是透明的,只需要使用getInstance()方法创建即可。
1、使用Calendar类代表当前时间
Calendar c = Calendar.getInstance();
由于Calendar类是抽象类,且Calendar类的构造方法是protected的,所以无法使用Calendar类的构造方法来创建对象,API中提供了getInstance()方法用来创建对象。
使用该方法获得的Calendar对象就代表当前的系统时间,由于Calendar类toString实现的没有Date类那么直观,所以直接输出Calendar类的对象意义不大。
2、使用Calendar类代表指定的时间
Calendar c1 = Calendar.getInstance();
c1.set(2009, 3 - 1, 9);
使用Calendar类代表指定的时间,需要首先创建一个Calendar的对象,然后再设定该对象中的年月日参数来完成。
set()方法的声明为:
public final void set(int year,int month,int date)
以上示例代码设置的时间为2009年3月9日,其参数的结构和Date类不一样。Calendar类中年份的数值直接书写,月份的值为实际的月份值减1,日期的值就是实际的日期值。
如果只设定某个字段,例如日期的值,则可以使用如下set()方法:
public void set(int field,int value)
在该方法中,参数field代表要设置的字段的类型,常见类型如下:
Calendar.YEAR——年份
Calendar.MONTH——月份
Calendar.DATE——日期
Calendar.DAY_OF_MONTH——日期,和上面的字段完全相同
Calendar.HOUR——12小时制的小时数
Calendar.HOUR_OF_DAY——24小时制的小时数
Calendar.MINUTE——分钟
Calendar.SECOND——秒
Calendar.DAY_OF_WEEK——星期几
后续的参数value代表,设置成的值。例如:
c1.set(Calendar.DATE,10);
该代码的作用是将c1对象代表的时间中日期设置为10号,其它所有的数值会被重新计算,例如星期几以及对应的相对时间数值等。
3、获得Calendar类中的信息
Calendar c2 = Calendar.getInstance();<br /> int year = c2.get(Calendar.YEAR); //年份<br /> int month = c2.get(Calendar.MONTH) + 1; //月份<br /> int date = c2.get(Calendar.DATE); //日期<br /> int hour = c2.get(Calendar.HOUR_OF_DAY); //小时<br /> int minute = c2.get(Calendar.MINUTE); //分钟<br /> int second = c2.get(Calendar.SECOND); //秒<br /> int day = c2.get(Calendar.DAY_OF_WEEK); //星期几<br /> System.out.println("年份:" + year);<br /> System.out.println("月份:" + month);<br /> System.out.println("日期:" + date);<br /> System.out.println("小时:" + hour);<br /> System.out.println("分钟:" + minute);<br /> System.out.println("秒:" + second);<br /> System.out.println("星期:" + day);<br />使用Calendar类中的get()方法可以获得Calendar对象中对应的信息,get()方法的声明如下:<br />public int get(int field)<br />其中参数field代表需要获得的字段的值,字段说明和上面的set()方法保持一致。需要说明的是,获得的月份为实际的月份值减1,获得的星期的值和Date类不一样。在Calendar类中,周日是1,周一是2,周二是3,依次类推。
4、其它方法说明
其实Calendar类中还提供了很多其它有用的方法,下面简单的介绍几个常见方法的使用。
a、add()方法
public abstract void add(int field,int amount)
该方法的作用是在Calendar对象中的某个字段上增加或减少一定的数值,增加时amount的值为正,减少时amount的值为负。
例如,计算一下当前时间100天以后的日期,代码如下:
Calendar c3 = Calendar.getInstance();
c3.add(Calendar.DATE, 100);
int year1 = c3.get(Calendar.YEAR);
int month1 = c3.get(Calendar.MONTH) + 1; //月份
int date1 = c3.get(Calendar.DATE); //日期
System.out.println(year1 + “年” + month1 + “月” + date1 + “日”);
这里add()方法是指在c3对象的Calendar.DATE,也就是日期字段上增加100,类内部会重新计算该日期对象中其它各字段的值,从而获得100天以后的日期,例如若当前时间为2015年8月7日,则程序的输出结果为:2015年11月15日
b、after()方法
public boolean after(Object when)
该方法的作用是判断当前日期对象是否在when对象的后面,如果在when对象的后面则返回true,否则返回false。例如:
Calendar c4 = Calendar.getInstance();
c4.set(2009, 10 - 1, 10);
Calendar c5 = Calendar.getInstance();
c5.set(2010, 10 - 1, 10);
boolean b = c5.after(c4);
System.out.println(b);
在该示例代码中对象c4代表的时间是2009年10月10号,对象c5代表的时间是2010年10月10号,则对象c5代表的日期在c4代表的日期之后,所以after()方法的返回值是true。
另外一个类似的方法是before(),该方法是判断当前日期对象是否位于另外一个日期对象之前。
c、getTime()方法
public final Date getTime()
该方法的作用是将Calendar类型的对象转换为对应的Date类对象,两者代表相同的时间点。
类似的方法是setTime(),该方法的作用是将Date对象转换为对应的Calendar对象,该方法的声明如下:
public final void setTime(Date date)
转换的示例代码如下:
Date d = new Date();
Calendar c6 = Calendar.getInstance();
//Calendar类型的对象转换为Date对象
Date d1 = c6.getTime();
//Date类型的对象转换为Calendar对象
Calendar c7 = Calendar.getInstance();
c7.setTime(d);
5、Calendar对象和相对时间之间的互转
Calendar c8 = Calendar.getInstance();
long t = 1252785271098L;
//将Calendar对象转换为相对时间
long t1 = c8.getTimeInMillis();
//将相对时间转换为Calendar对象
Calendar c9 = Calendar.getInstance();
c9.setTimeInMillis(t1);
在转换时,使用Calendar类中的getTimeInMillis()方法可以将Calendar对象转换为相对时间。在将相对时间转换为Calendar对象时,首先创建一个Calendar对象,然后再使用Calendar类的setTimeInMillis()方法设置时间即可。
1.5.4 Calendar 类常用属性
表1-11列出了Calendar类的属性。
表1-11 Calendar类的常用属性
属性 | 含义 |
---|---|
static int HOUR | 小时时间 |
static int MINUTE | 分时间 |
static int SECOND | 秒时间 |
static int DATE | 日期的Date部分 |
static int MONTH | 日期的Month部分 |
static int YEAR | 日期的年部分 |
【例1-18】Calendar类使用练习
public class TestCalendar {
public static void main(String[] args) {
//获取Calendar类的实例
Calendar c = Calendar.getInstance();
System.out.println(c.get(Calendar.YEAR)+"年"+(c.get(Calendar.MONTH)+1)+"月"+
c.get(Calendar.DAY_OF_MONTH)+"日");
//设置指定时间
c.set(2011,10,11);
System.out.println(c.get(Calendar.YEAR)+"年"+ (c.get(Calendar.MONTH)+1)+"月"+ c.get(Calendar.DAY_OF_MONTH)+"日");
Calendar c1 = Calendar.getInstance();
Calendar c3 = Calendar.getInstance();
c3.add(Calendar.DATE, 100);
int year1 = c3.get(Calendar.YEAR);
int month1 = c3.get(Calendar.MONTH) + 1; //月份
int date1 = c3.get(Calendar.DATE); //日期
System.out.println(year1 + "年" + month1 + "月" + date1 + "日");
}
}
程序分析:本例使用时间和日期处理方法进行计算。该程序实现的原理为:首先代表两个特定的时间点,这里使用Calendar的对象进行代表,然后将两个时间点转换为对应的相对时间,求两个时间点相对时间的差值,然后除以1天的毫秒数(24小时X60分钟X60秒X1000毫秒)即可获得对应的天数。
【例1-20】输出当前月的月历,该示例的功能是输出当前系统时间所在月的日历,例如当前系统时间是2009年3月10日,则输出2009年3月的日历。
import java.util.Calendar;
public class DateExample2{
public static void main(String[] args){
//获得当前时间
Calendar c = Calendar.getInstance();
//设置代表的日期为1号
c.set(Calendar.DATE,1);
//获得1号是星期几
int start = c.get(Calendar.DAY_OF_WEEK);
//获得当前月的最大日期数
int maxDay = c.getActualMaximum(Calendar.DATE);
//输出标题
System.out.println("星期日星期一星期二星期三星期四星期五星期六");
//输出开始的空格
for(int i = 1;i < start;i++){
System.out.print(" ");
}
//输出该月中的所有日期
for(int i = 1; i <= maxDay; i++){
//输出日期数字
System.out.print(" " + i);
//输出分隔空格
System.out.print(" ");
if(i < 10){
System.out.print(' ');
}
//判断是否换行
if((start + i - 1) % 7 == 0){
System.out.println();
}
}
//换行
System.out.println();
}
}
程序运行结果:
星期日 星期一 星期二 星期三 星期四 星期五 星期六
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
程序分析:该程序实现的原理为:首先获得该月1号是星期几,然后获得该月的天数,最后使用流程控制实现按照日历的格式进行输出即可。即如果1号是星期一,则打印一个单位的空格,如果1号是星期二,则打印两个单位的空格,依次类推。打印完星期六的日期以后,进行换行。
1.5.5 SimpleDateFormat类的使用
java.text.SimpleDateFormat是一个以与语言环境相关的方式来格式化和分析日期的具体类,是抽象类java.text.DateFormat类的子类,SimpleDateFormat使得可以选择任何用户定义的日期-时间格式的模式,获取SimpleDateFormat的实例如下:
SimpleDateFormat sdf=new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
上面代码确立了转换的格式,yyyy是完整的西元年,MM是月份,dd是日期,HH:mm:ss分别是小时、分和秒。至于为什么有的格式大写,有的格式小写,那是怕避免混淆,例如MM是月份,mm是分;HH是24小时制,而hh是12小时制,表1-12列出了SimpleDateFormat类中的模式字母。
表1-12 SimpleDateFormat类中的模式字母
字母 | 日期或时间元素 |
---|---|
y | 年 |
M | 年中的月份 |
d | 月份中的天数 |
E | 星期中的天数 |
a | Am/pm 标记 |
H | 一天中的小时数(0-23) |
h | am/pm 中的小时数(1-12) |
m | 小时中的分钟数 |
s | 分钟中的秒数 |
S | 毫秒数 |
1.字符串转日期:
2002-10-8 15:30:22要把它转成日期,可以用 Date date=sdf.parse(“2002-10-8 15:30:22”);
2.日期转字符串:
假如把今天的日期转成字符串可用 String datestr=sdf.format(new Date());
这个字符串的内容便类似2002-10-08 14:55:38
透过这个API我们便可以随心所欲的将日期转成我们想要的字符串格式,例如希望将日期输出成2002年10月08日,可以这么写:
SimpleDateFormat sdf=new SimpleDateFormat(“yyyy年MM月dd日”);
String dateStr=sdf.format(new Date());
dateStr便会依照我们设定的格式输出。
【例1-21】SimpleDateFormat的使用。
public class TestDateFormat {
public static void main(String[] args) {
//获取指定日期格式的SimpleFormat实例 1999年09月09日 12:12:12
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //获取指定日期的格式化字符串
String nowStr = sdf.format(new Date());
System.out.println(nowStr);
//将格式化的字符串转换成日期
try {
Date d = sdf.parse("2011-11-11 11:11:11");
System.out.println(d);
} catch (ParseException e) {
System.out.println("输入的字符串不符合日期的格式");
e.printStackTrace();
}
}
}
程序分析:SimpleDateFormat 是Java中一个非常常用的类,该类用来对日期字符串进行解析和格式化输出。
1.6DecimalFormat和NumberFormat
在很多时候需要对输出的内容进行格式化,尤其是当输入的内容为数字的时候,需要按照特定的格式进行输出。另外,对运行的结果可能需要特殊的处理,例如结果只保留小数点后2位。对数字进行格式化需要使用下面的两个类:
java.text.DecimalFormat
java.text.NumberFormat
DecimalFormat是NumberFormat的一个具体子类,通常使用DecimalFormat的构造方法来生成格式,例如:
NumberFormat nf=new DecimalFormat(“0.00”);
“0.00”表示数字的格式为小数点后保留两位,如果整数部分为0,0不能省略,小数点后如果是0也不能省略。下面是3个转换的例子:
10.374——-10.37
10.301——-10.30
0.301———0.30
在格式中另外还有一个“#”,表示一个数字,如果是0不显示,下面的例子使用了“#”号,并且整数部分每3位中间用“,”号隔开。
NumberFormat nf2=new DecimalFormat(“###.###.###.##”);
【例1-22】DecimalFormat类的使用
import java.text.DecimalFormat;
import java.util.Random;
import java.util.Locale;
public class TestNumberFormat {
public static void main(String[] args) {
double pi = 3.1415927;//圆周率
//取一位整数
System.out.println(new DecimalFormat("0").format(pi)); //3
//取一位整数和两位小数
System.out.println(new DecimalFormat("0.00").format(pi)); //3.14
//取两位整数和三位小数,整数不足部分以0填补。
System.out.println(new DecimalFormat("00.000").format(pi)); //03.142
//取所有整数部分
System.out.println(new DecimalFormat("#").format(pi)); //3
//以百分比方式计数,并取两位小数
System.out.println(new DecimalFormat("#.##%").format(pi)); //314.16%
long c = 299792458;//光速
//显示为科学计数法,并取五位小数
System.out.println(new DecimalFormat("#.#####E0").format(c)); //2.99792E8
//显示为两位整数的科学计数法,并取四位小数
System.out.println(new DecimalFormat("00.####E0").format(c)); //29.9792E7
//每三位以逗号进行分隔。
System.out.println(new DecimalFormat(",###").format(c)); //299,792,458
//将格式嵌入文本
System.out.println(new DecimalFormat("光速大小为每秒,###米。").format(c));//光速大小为每秒299,792,458米。
//直接截取
System.out.println(Math.floor(24.335*100)/100); //24.33
System.out.println((int)Math.floor(23.45)); //截取整数 23
System.out.println((int)Math.ceil(23.2)); //凑整 24
System.out.println((int)Math.rint(23.567)); //四舍五入取整 24
System.out.println(new DecimalFormat("0").format(25.5)); //四舍五入取整 26
System.out.println(Math.random()); //随机double 0.9786833725139645
System.out.println(new Random().nextFloat()); //随机浮点数 0.5196178
System.out.println(new Random().nextBoolean()); //随机boolean
}
}
程序分析:DecimalFormat 是 NumberFormat 的一个具体子类,用于格式化十进制数字。
1.7 课后作业
1.编写一个程序,实现从命令行参数输入两个字符串类型的数值,并计算输出两个数值的和。
2.编写一个程序,实现从命令行参数输入一字符串,统计该字符串中字符“e”出现的次数。
3.生成十个0~100之间的随机数,放到数组中,然后排序输出。
4.巴黎时间比北京时间晚7个小时,纽约时间比北京时间晚12个小时,试编写一程序,根据输入的北京时间输出相应的巴黎和纽约时间。
5.解析一个邮箱地址是否合法,如果合法则打印出用户名部分和该邮箱所属的网站域名,如果邮箱地址不合法则显示不合法的原因
5.1 提示:邮箱地址不合法的因素:
5.1.1 邮箱地址中不包含@或.
5.1.2 邮箱地址中含有多了@或.
5.1.3 邮箱地址中.出现在@的前面
5.1.4 用户名里有其他字符
5.2 实现步骤:
5.2.1 创建一个类,类名:mailtest
6.分别在控制台输入字符串和子字符串,并计算字符串中子字符串出现的次数。
7.有一个字符串,其中包含中文字符、英文字符和数字字符,请统计和打印出各个字符的个数。
8.有一种数叫回文数,正读和反读都一样,如12321便是一个回文数。编写一个程序,从命令行得到一个整数,判断该数是不是回文数。
9.编写一个方法为物品生成一个指定长度的编号,要求编号的每一位或者为0到9的数字,或者为A到Z的大写字母,每次产生的编号是随机的。
10.编写一个方法验证用户输入的日期格式是否正确,要求格式为:2006/12/12。方法的参数是要验证的日期字符串,如果格式正确返回true,否则返回false。
11.请编程实现:由键盘输入的任意一组字符,统计其中大写字母的个数m和小写字母的个数n,并输出m、n中的较大者。
12.输入一行字符,将其中的字母变成其后续的第3个字母,输出。例:a→d,x → a;y → b;编程实现。
[