一.单元概述
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相同,则为同一个公民。

  1. public class Citizen{
  2. private int id;
  3. private String name;
  4. public Citizen(int id, String name) {
  5. this.id = id;
  6. this.name = name;
  7. }
  8. //覆盖父类的equals()方法
  9. public boolean equals(Object o) {
  10. Citizen c = (Citizen) o;
  11. if (c.id == this.id) {
  12. return true;
  13. } else {
  14. return false;
  15. }
  16. }
  17. //主方法,测试equals()方法
  18. public static void main(String args[]) {
  19. Citizen xiaoming = new Citizen(21000, "xiaoming");
  20. Citizen mingming = new Citizen(21000, "mingming");
  21. System.out.println(xiaoming.equals(mingming));
  22. }
  23. }
  24. //程序运行结果: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. 【例1-2 == equals()方法在String类中的区别
  2. String str1 = "hello"; 
  3. String str2 = new String("hello"); 
  4. System.out.println(str1==str2); 
  5. System.out.println(str1.equals(str2));
  6. 程序运行结果:
  7. false
  8. 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. 【例1-3 查看Object类和String类的toString()方法返回值。
  2. public class ObjectSample {
  3. public static void main(String args[]){
  4. Object o = new Object();
  5. System.out.println(o);
  6. System.out.println(o.toString());
  7. String s = new String("hello");
  8. System.out.println(s);
  9. }
  10. }
  11. 程序运行结果:
  12. java.lang.Object@1db9742
  13. java.lang.Object@1db9742
  14. hello

程序分析:前两个输出结果表示对象o在内存中的地址,事实上返回这样的字符串没有什么实际的意义。一般子类都会覆盖该方法,让它返回有意义的文本。例如上例中的对象s的输出为”hello”,这是因为字符串String类对toString()进行了覆盖,让它返回此String的本身内容。
下面通过实例来看一下如何在自己编写的类中覆盖equals()方法和toString()方法。

  1. 【例1-4】 equals()方法和toString()的覆盖
  2. //圆类
  3. public class MyCircle {
  4. private int radius;//半径
  5. public MyCircle(int r) {
  6. radius = r;
  7. }
  8. public int getRadius() {
  9. return radius;
  10. }
  11. public void setRadius(int r) {
  12. radius = r;
  13. }
  14. //覆盖Object中的equals()方法
  15. public boolean equals(Object obj) {
  16. MyCircle o = (MyCircle) obj;
  17. if (o.getRadius() == this.radius)
  18. return true;
  19. else
  20. return false;
  21. }
  22. //覆盖Object中toString方法
  23. public String toString() {
  24. return "radius = " + radius;
  25. }
  26. }
  27. //测试类
  28. public class Test {
  29. public static void main(String[] args) {
  30. MyCircle obj1 = new MyCircle(3);
  31. System.out.println("obj1:" + obj1.toString());
  32. MyCircle obj2 = new MyCircle(3);
  33. System.out.println("obj2:" + obj2.toString());
  34. if (obj1.equals(obj2))
  35. System.out.println("the two objects are equal");
  36. else
  37. System.out.println("the two objects are not equal");
  38. }
  39. }
  40. 程序运行结果:
  41. obj1radius = 3
  42. obj2radius = 3
  43. 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是它们的继承层次结构。
image-639.png
基本数据类型的变量没有默认值,而包装类型的变量默认值是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 字符串与基本数据类型、包装类型转换

09.常见工具类-高级篇 - 图2

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. 【例1-5】包装类、基本数据类型和字符串之间转换的综合案例。
  2. public class TestWrapper {
  3. public static void main(String ars[]){
  4. Double obj_d = new Double(3.14); //基本数据类型转换为包装类
  5. double d = obj_d.doubleValue(); //包装类转换为基本数据类型
  6. Integer obj_i = new Integer("5"); //字符串转换为包装类
  7. String s = obj_i.toString(); //包装类转换为字符串转换
  8. double n = Double.parseDouble("5"); //字符串类转换为基本数据类型
  9. String s = obj_j.toString(); //基本数据类型转换为字符串类型
  10. Integer aa = 9; //自动装箱
  11. int bb = aa; //自动拆箱
  12. }
  13. }

程序分析:通过以上案例可以看出,基本数据类型和字符串类型转换为包装类,使用包装类的构造器来完成;包装类转换为基本数据类型,调用包装类的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所示。
image.png
图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所示。
image-631.png
图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) 测试此字符串是否以指定的后缀结束

image-632.png

  1. 例如,下面的语句先显示true,然后显示false.
  2. String s1 = new String("welcome to Java");
  3. String s2 = "welcome to Java";
  4. String s3 = "welcome to C++";
  5. System.out.println(s1.equals(s2)); //true
  6. System.out.println(s1.equals(s3));//false
  7. 【例1-5】字符串equals方法练习
  8. String s1 = "abc";
  9. String s2 = new String("abc");
  10. String s3 = new String("abc");
  11. String s4 = "abc";
  12. System.out.println(s1.equals(s2)); //true
  13. System.out.println(s1.equals(s4)); // true
  14. System.out.println(s2.equals(s3)); //true
  15. 程序分析:equals方法用来比较两个字符串是否相等。
  16. 【例1-6】重写equals()方法,判断不同的点,是否是坐标系上的同一点。
  17. //点类
  18. public class Point {
  19. private int x; //x坐标
  20. private int y;//y坐标
  21. public Point(int x,int y){
  22. this.x = x;
  23. this.y = y;
  24. }
  25. public int getX() {
  26. return x;
  27. }
  28. public void setX(int x) {
  29. this.x = x;
  30. }
  31. public int getY() {
  32. return y;
  33. }
  34. public void setY(int y) {
  35. this.y = y;
  36. }
  37. @Override
  38. public String toString() {
  39. return "("+x+","+y+")";
  40. }
  41. //如果x,y的值相同,就认为是坐标上的同一点
  42.     public boolean equals(Object obj) {
  43.   if(obj == null){
  44.   return false;
  45.    }
  46.   if(this == obj){
  47.   return true;
  48.    }
  49.   if(!(obj instanceof Point)){
  50.   return false;
  51.    } else {
  52.   Point p = (Point)obj;
  53.   if(p.x==x&&p.y==y){
  54.   return true;
  55.    } else {
  56.   return false;
  57.   }
  58.    }
  59. }
  60. }
  61. //测试类
  62. public class TestPoint {
  63. public static void main(String[] args) {
  64. Point p1 = new Point(55,66);
  65. Point p2 = new Point(55,66);
  66. Point p3 = new Point(22,33);
  67. System.out.println(p1.equals(p2));
  68. System.out.println(p1==p2);
  69. System.out.println(p1.equals(p3));
  70. System.out.println(p1==p3);
  71. }
  72. }
  73. 程序运行结果:
  74. true
  75. false
  76. false
  77. 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. 【例1-7 使用平台的默认字符集,统计一个字符串所占用的字节数。
  2. public class TestStringByte{ 
  3. public static void main(String[] args) {
  4. String str = "Java语言程序设计";
  5. byte bytes[] = str.getBytes();
  6. System.out.println(bytes.length);
  7. } 
  8. }
  9. 程序运行结果:
  10. 16

程序分析:通过getBytes()方法把字符串转换成字节数组,字节数组的长度就是字符串所占的字节数。

  1. 【例1-8】通过字节数组创建字符串对象
  2. public class TestStringCharset { 
  3. public static void main(String[] args) {
  4. byte[] bName = new byte[10]; 
  5. String name1 = "张三"; 
  6. try{ 
  7. bName = name1.getBytes("utf-8"); //这种编码方式一个中文占三个字节
  8.   String name2 = new String(bName,"utf-8"); 
  9.   System.out.println("name2="+name2); 
  10. for(int i = 0;i< bName.length;i++){ 
  11.   System.out.print(bname[i]); 
  12. } 
  13. }catch (UnsupportedEncodingException e){ 
  14. e.printStackTrace(); 
  15. } 
  16. } 
  17. }
  18. 程序运行结果:
  19. name2=张三
  20. -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所示
image-632.png

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. 【例1-9 统计一个字符串中字符e出现的次数。
  2. public class TestString {
  3. public static void main(String[] args) {
  4. String s = "abecedkjkacedjkdseddklj";
  5. int num = 0;
  6. for(int i=0; i<s.length(); i++){
  7. char c = s.charAt(i);
  8. if(c == 'e'){
  9. num++;
  10. }
  11. }
  12. System.out.println("该字符串中字符e出现"+num+"次");
  13.   }
  14. }

程序分析:程序中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

  1. import java.io.DataInputStream;
  2. public class StringTest {
  3. public static void main(String args[]){
  4. System.out.println("计算第一个字符串在第二个字符串中出现的次数");
  5. // 通过如下语句,实例化一个从控制台读取输入内容的对象din
  6. DataInputStream din = new DataInputStream(System.in);
  7. try{
  8. System.out.println("请输入第一个字符串");
  9. String str1 = din.readLine();//读入一行数据
  10. System.out.println("请输入第二个字符串");
  11. String str2 = din.readLine();
  12. String str3 = str2.replace(str1,"");//把str2中的str1用空串替换,并赋给str3
  13. int count = str2.length() - str3.length();//str2的长度减去str3的长度
  14. System.out.println(str1+"在"+str2+"中出现的次数为:"+count);
  15. }catch(Exception e){
  16. System.out.println(e.toString());
  17. }
  18. }
  19. }

程序运行结果:
计算第一个字符串在第二个字符串中出现的次数
请输入第一个字符串
ab
请输入第二个字符串
abcedabsdabajab
ab在abcedabsdabajab中出现的次数为:4
需要注意的是String本身是一个常量,一旦一个字符串创建了,它的内容是不能改变的,那么如何解释下面的代码:
s1+=s2;
这里并不是把字符串s2的内容添加到字符串s1的后面,而是新创建了一个字符串,内容是s1和s2的连接,然后把s1指向了新创建的这个字符串。如果一个字符串的内容经常需要变动,不应该使用String,因为在变化的过程中实际上是不断创建对象的过程,这时候应该使用StringBuffer。

  1. 【例1-11】使用字符串中的常用方法实现Email格式的判断。
  2. public class EmailCheckException extends Exception {
  3. public EmailCheckException(String msg) {
  4. super(msg);
  5. }
  6. public class StringExercise {
  7. public static void checkEmail(String email) throws EmailCheckException {
  8. int len = email.length(); //email的长度
  9. int begin = email.indexOf('@'); //从开始检索字符@在email中的位置
  10. int end = email.lastIndexOf('@'); //从结尾检索字符@在email中的位置
  11. int dot = email.indexOf('.', begin);//检索.是否在@后存在
  12. //判断长度是否不超过20
  13. if (email.length() > 20)
  14. throw new EmailCheckException("Email长度不能大于20");
  15. //判断是否唯一包含@
  16. else if (begin != end)
  17. throw new EmailCheckException("Email中含有多个@");
  18. //判断@是否存在或在开头或在结尾
  19. else if (begin == -1 || begin == 0 || begin == (len - 1))
  20. throw new EmailCheckException("Email中没有@或@位置错误");
  21. //判断@后是否有.
  22. else if (dot == -1)
  23. throw new EmailCheckException("@后缺少域分隔符");
  24. //判断@是否在末尾
  25. else if (dot == (len - 1))
  26. throw new EmailCheckException("分隔符错误");
  27. }
  28. public static void main(String args[]) {
  29. String email = args[0];
  30. try {
  31. checkEmail(email);
  32. } catch (EmailCheckException e) {
  33. e.printStackTrace();
  34. }
  35. }
  36. }
  37. }

程序分析:自定义了一个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. 【例1-17Date类常用方法练习
  2. public class TestDate {
  3. public static void main(String[] args) {
  4. Date now = new Date();//获取当前系统时间
  5. System.out.println(now); //获取1970年1月1日1000毫秒之后的时间
  6. Date d1 = new Date(1000);
  7. System.out.println(now.after(d1));
  8. System.out.println(now.before(d1));
  9. System.out.println(now.compareTo(d1));
  10. }
  11. }
  12. 程序运行结果:
  13. true
  14. false
  15. 1
  16. 程序分析:本程序主要练习日期类的几个常用方法,这几个方法的详细说明请参见表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类中的信息

  1. 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. 【例1-18Calendar类使用练习
  2. public class TestCalendar {
  3. public static void main(String[] args) {
  4. //获取Calendar类的实例
  5. Calendar c = Calendar.getInstance();
  6.   System.out.println(c.get(Calendar.YEAR)+"年"+(c.get(Calendar.MONTH)+1)+"月"+
  7. c.get(Calendar.DAY_OF_MONTH)+"日");
  8. //设置指定时间
  9. c.set(2011,10,11);
  10. System.out.println(c.get(Calendar.YEAR)+"年"+ (c.get(Calendar.MONTH)+1)+"月"+           c.get(Calendar.DAY_OF_MONTH)+"日");
  11.   Calendar c1 = Calendar.getInstance();
  12.   Calendar c3 = Calendar.getInstance();
  13.     c3.add(Calendar.DATE, 100);
  14.     int year1 = c3.get(Calendar.YEAR);
  15.     int month1 = c3.get(Calendar.MONTH) + 1; //月份
  16.     int date1 = c3.get(Calendar.DATE); //日期
  17.     System.out.println(year1 + "年" + month1 + "月" + date1 + "日");
  18. }
  19. }

程序分析:本例使用时间和日期处理方法进行计算。该程序实现的原理为:首先代表两个特定的时间点,这里使用Calendar的对象进行代表,然后将两个时间点转换为对应的相对时间,求两个时间点相对时间的差值,然后除以1天的毫秒数(24小时X60分钟X60秒X1000毫秒)即可获得对应的天数。

  1. 【例1-20】输出当前月的月历,该示例的功能是输出当前系统时间所在月的日历,例如当前系统时间是2009310日,则输出20093月的日历。
  2. import java.util.Calendar;
  3. public class DateExample2{
  4.   public static void main(String[] args){
  5.     //获得当前时间
  6.     Calendar c = Calendar.getInstance();
  7.     //设置代表的日期为1号
  8.     c.set(Calendar.DATE,1);
  9.     //获得1号是星期几
  10.     int start = c.get(Calendar.DAY_OF_WEEK);
  11.     //获得当前月的最大日期数
  12.     int maxDay = c.getActualMaximum(Calendar.DATE);   
  13.     //输出标题
  14.     System.out.println("星期日星期一星期二星期三星期四星期五星期六");
  15.     //输出开始的空格
  16.     for(int i = 1;i < start;i++){
  17.       System.out.print("   "); 
  18.     }
  19.     //输出该月中的所有日期
  20.     for(int i = 1; i <= maxDay; i++){
  21.      //输出日期数字
  22.     System.out.print(" " + i);
  23.      //输出分隔空格
  24.      System.out.print("    ");
  25.     if(i < 10){
  26.        System.out.print(' ');
  27.      }
  28.     //判断是否换行
  29.      if((start + i - 1) % 7 == 0){
  30.        System.out.println();       
  31.      }
  32.     }
  33.     //换行
  34.     System.out.println();       
  35.   }       
  36. }
  37. 程序运行结果:
  38. 星期日   星期一   星期二  星期三  星期四  星期五   星期六
  39. 1      2
  40. 3    4      5    6    7    8     9
  41. 10    11     12    13     14  15    16
  42. 17    18     19    20     21  22    23
  43. 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的使用。

  1. public class TestDateFormat {
  2.    public static void main(String[] args) {
  3.    //获取指定日期格式的SimpleFormat实例 1999年09月09日 12:12:12
  4.    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    //获取指定日期的格式化字符串
  5. String nowStr = sdf.format(new Date());
  6.    System.out.println(nowStr);
  7.    //将格式化的字符串转换成日期
  8.    try {
  9. Date d = sdf.parse("2011-11-11 11:11:11");
  10. System.out.println(d);
  11. } catch (ParseException e) {
  12. System.out.println("输入的字符串不符合日期的格式");
  13. e.printStackTrace();
  14. }
  15.   }
  16. }

程序分析: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. 【例1-22DecimalFormat类的使用
  2. import java.text.DecimalFormat;
  3. import java.util.Random;
  4. import java.util.Locale;
  5. public class TestNumberFormat {
  6.  public static void main(String[] args) {
  7.   double pi = 3.1415927;//圆周率
  8.   //取一位整数
  9.   System.out.println(new DecimalFormat("0").format(pi)); //3
  10.   //取一位整数和两位小数
  11.   System.out.println(new DecimalFormat("0.00").format(pi)); //3.14        
  12.   //取两位整数和三位小数,整数不足部分以0填补。
  13.   System.out.println(new DecimalFormat("00.000").format(pi)); //03.142
  14.   //取所有整数部分
  15.   System.out.println(new DecimalFormat("#").format(pi)); //3
  16.   //以百分比方式计数,并取两位小数
  17.   System.out.println(new DecimalFormat("#.##%").format(pi)); //314.16%
  18.   long c = 299792458;//光速
  19.   //显示为科学计数法,并取五位小数
  20.   System.out.println(new DecimalFormat("#.#####E0").format(c)); //2.99792E8
  21.   //显示为两位整数的科学计数法,并取四位小数
  22.   System.out.println(new DecimalFormat("00.####E0").format(c)); //29.9792E7
  23.   //每三位以逗号进行分隔。
  24.   System.out.println(new DecimalFormat(",###").format(c)); //299,792,458
  25.   //将格式嵌入文本
  26.   System.out.println(new DecimalFormat("光速大小为每秒,###米。").format(c));//光速大小为每秒299,792,458米。
  27.   //直接截取
  28.   System.out.println(Math.floor(24.335*100)/100);  //24.33        
  29.   System.out.println((int)Math.floor(23.45));   //截取整数 23
  30.   System.out.println((int)Math.ceil(23.2));    //凑整 24
  31.   System.out.println((int)Math.rint(23.567));   //四舍五入取整 24
  32.   System.out.println(new DecimalFormat("0").format(25.5)); //四舍五入取整 26
  33.   System.out.println(Math.random());   //随机double 0.9786833725139645
  34.   System.out.println(new Random().nextFloat());  //随机浮点数 0.5196178
  35.   System.out.println(new Random().nextBoolean()); //随机boolean
  36.   }
  37. }
  38. 程序分析: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;编程实现。
[

](https://null_688_6639.gitee.io/javase/01JavaSE/%E7%AC%AC8%E7%AB%A0%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86.html)