文章目录
六、Java基础类库
6.1 用户输入
Java 程序入口 main() 方法的方法签名:
// Java 程序入口:main()函数
public static void main(String[] args)
public
修饰符:Java 类由 JVM 调用;static
修饰符:静态方法,JVM 直接通过该类调用主方法;void
返回值:无返回值;Args[ ]
:数组的作用是接受命令行的参数,在程序运行的时候进行参数的传递;
运行 Java 程序时传入参数只能在程序开始之前就设定几个固定的参数,而使用 Scanner 类可以获取用户的键盘输入,Scanner 是一个基于正则表达式的文本扫描器,它可以从文件、输入流、字符串中解析出基本类型值和字符串值。Scanner 类提供了多个构造器,不同的构造器可以接受文件、输入流、字符串作为数据源,用于从文件、输入流、字符串中解析数据。
- boolean hasNextXxx():是否还有下一个输入项,其中 Xxx可以是 Int、Long 等代表基本数据类型的字符串,如果只是判断是否包含下一个字符串,则直接使用 hasNext();其中数值类型可以指定进制 int raidx 参数。
- nextXxx():获取下一个输入项,格式同上。
默认情况下,Scanner 使用空白符(包括空格、Tab空白、回车)作为多个输入项之间的分隔符。
public class ScannerKeyBoardTest {
public static void main(String[] args) {
//System.in代表标准输入,就是键盘输入
Scanner sc = new Scanner(System.in);
//把回车当分隔符
sc.useDelimiter("\n");
//判断是否还有下一个输入项
while(sc.hasNext())
{
//输出输入项
System.out.println("键盘输入的内容是:" + sc.next());
}
}
}
Scanner 的读取操作可能被阻塞(当前执行顺序暂停)来等待信息的输入,如果输入源>没有结束,Scanner 又读取不到更多输入项时(尤其键盘输入),Scanner 的 hasNext() 和 next() 方法都有可能阻塞。设置分隔符 useDelimiter(String pattern) 方法,该方法参数应该是一个正则表达式,该程序就会把键盘的每行输入当成一个输入项,不会以空格、Tab空白等作为分隔符。
Scanner 逐行读取方法为:
- boolean hasNextLine():返回输入源是否还有下一行;
- String nextLine():返回输入源中下一行的字符串;
public class ScannerLineTest {
public static void main(String[] args) throws FileNotFoundException {
//将一个File对象作为Scanner的构造器参数,Scanner读取文件内容
Scanner sc = new Scanner(new File("E://test.txt"));
//判断是否还有下一行
while (sc.hasNextLine()) {
//输出文件的下一行
System.out.println(sc.nextLine());
}
}
}
6.2 系统相关
Java 程序在不同操作系统上运行时,可能需要取得平台相关的属性,或者调用平台命令来完成特定功能。Java 提供了 System 类和 Runtime 类来与程序的运行平台进行交互。
6.2.1 System类
System 类代表当前 Java 程序的运行平台,程序不能创建 System 类的对象(private 构造器),System 类提供了一些类变量和类方法,允许直接通过 System 类来调用这些类变量和类方法。System 类提供了代表标准输入、标准输出和错误输出的类变量,并提供了一些静态方法用于访问环境变量、系统属性的方法,还提供了加载文件和动态链接库的方法。下面通过 System 类来访问操作的环境变量和系统属性。
加载文件和动态链接库主要对 native 方法有用,对于一些特殊的功能(如访问操作系统底层硬件设备等)Java 程序无法实现,必须借助 C 语言来实现,步骤如下:
- Java程序中生命 native 修饰的方法,类似于 abstract 方法,只有方法签名,没有实现,编译程序生成 class 文件;
- 用 javah 编译 class 文件产生一个 .h 文件;
- 写一个 .cpp 文件实现 native 方法,这一步需要包含第2步产生的 .h 文件;
- 将第3步的 .cpp 文件编译成动态链接库文件;
- 在 Java 中用 System 类的 loadLibrary…() 方法或 Runtime 类的 LoadLibrary() 文件加载第4步产生的动态链接库文件,Java 就可以调用这个 native 方法了。
public class SystemTest {
public static void main(String[] args) throws Exception {
//获取系统所有的环境变量
Map<String, String> env = System.getenv();
for (String name : env.keySet()) {
System.out.println(name + "--->" + env.get(name));
}
//获取指定环境变量的值
System.out.println(System.getenv("JAVA_HOME"));
//获取所有的系统属性
Properties Props = System.getProperties();
Props.store(new FileOutputStream("props.txt"), "System Properties");
//输出特定的系统属性
System.out.println(System.getProperty("os.name"));
}
}
System 类提供了通知系统进行垃圾回收的 gc() 方法,以及通知系统进行资源清理的 runFinalization() 方法。System 类还有两个获取系统当前时间的方法:
public class SystemTime {
public static void main(String[] args) {
//返回当前时间与UTC 1970年1月1日午夜的时间差,分别以毫秒和纳秒作为单位
System.out.println(System.currentTimeMillis());
System.out.println(System.nanoTime());
}
}
System 类的 in、out 和 err 分别代表系统的标准输入(通常是键盘)、标准输出(通常是显示器)和错误输出流,并提供了 setIn()、setOut 和 setErr() 方法来改变系统的标准输入、标准输出和标准错误输出流。
System 类还提供了一个 identityHashCode(Object x) 方法,该方法返回指定对象的精确 hashCode 值,也就是根据该对象的地址计算得到的 hashCode 值。当某个类的 hashCode() 方法被重写后,该类实例的 hashCode() 方法就不能唯一地标识该对象;但通过 identityHashCode(Object x) 方法返回的 hashCode 值,依然是根据该对象的地址计算得到的 hashCode 值,所以如果两个对象的 identityHashCode 值相同,则两个对象绝对是同一个对象。
public class IdentityHashCodeTest {
public static void main(String[] args) {
//s1和s2是两个不同的对象
String s1 = new String("Java");
String s2 = new String("Java");
System.out.println(s1 == s2);//输出false
//String重写了hashCode()方法,改为根据字符序列计算hashcode值
//所以s1和s2的hashcode返回值相同
System.out.println(s1.hashCode() == s2.hashCode());//输出true
//s1和s2是不同的字符串对象,所以identityHashCode()值不同
System.out.println(System.identityHashCode(s1) == System.identityHashCode(s2));//输出false
}
}
6.2.2 Runtime类
Runtime 类代表 Java 程序的运行时环境,每个 Java 程序都有一个对应的 Runtime 实例,应用程序通过该对象与其运行时环境相连。应用程序不能创建自己的 Runtime 实例,但可以通过 getRuntime() 方法获取与之关联的 Runtime 对象。与 System 类似的是,Runtime 类也提供了 gc() 方法和 runFinalization() 方法来通知系统进行垃圾回收和清理系统资源,并提供了 load(String filename) 和 loadLibrary(String libname) 方法来加载文件和动态链接库。
Runtime 类代表 Java 程序的运行时环境,可以访问 JVM 的相关信息,如处理器数量、内存信息等,exec() 方法运行操作系统的命令。
public class RuntimeTest {
public static void main(String[] args) throws Exception{
//获取Java程序关联的运行时对象,访问JVM相关信息
Runtime rt = Runtime.getRuntime();
System.out.println("处理器数量:" + rt.availableProcessors());
System.out.println("空闲内存数:" + rt.freeMemory());
System.out.println("总内存数:" + rt.totalMemory());
System.out.println("可用最大内存数:" + rt.maxMemory());
//单独启动一个进程来运行操作系统的命令
rt.exec("notepad.exe");
}
}
6.3 常用类
6.3.1 Object类
Object 类是所有类、数组、枚举类的父类,Java 允许将任何类型的对线赋给 Object 类型的变量。当定义一个类时没有使用 extends 关键字为它显式指定父类,则该类默认继承 Object 父类,任何 Java 对象都可以调用 Object 类的方法
- boolean equals(Object obj):判断指定对象与该对象是否相等,此处相等的标准是两个对象是同一个对象,因此太大的实用价值;
- protected void finalize():当系统中没有引用变量引用到该对象时,垃圾回收器调用此方法来清理该对象的资源;
- Class<?> getClass():返回该对象的运行时类;
- int hashCode():返回该对象的 hashcode 值,默认情况下 Object 类的 hashcode() 方法根据该对象的地址来计算)(即与 System.identityHashCode(Object x) 方法的计算结果相同),但很多类重写 Object 类的 hashCode() 方法,不再根据地址来计算其 hashCode() 返回值;
- String toString():返回该对象的字符串表示,当程序使用 System.out.println() 方法输出一个对象或者把某个对象和字符串进行连接运算时,系统会自动调用该对象的 toString() 方法返回该对象的字符串表示。Object 类的 toString() 方法返回运行时类名@十六进制 hashCode 值格式的字符串,但很多类都重写了 Object 类的 toString() 方法,用于返回可以表述该对象信息的字符串。
除此之外,Object 类还提供了 wait() 、notify()、notifyAll() 等方法,控制线程的暂停和运行。Java 还提供了一个 protected 修饰的 clone() 方法,该方法用于帮助其他对象来实现“自我克隆”,所谓“自我克隆”就是得到一个当前对象的副本,而且两者之间完全隔离。由于 Object 类提供的 clone() 方法使用 protected 修饰,因此该方法只能被子类重写或调用。自定义类实现“克隆”的步骤如下:
- 自定义类实现 Cloneable 接口,这是一个标记性的接口,实现该接口的对象可以实现“自我克隆”,接口里没有定义任何方法;
- 自定义类实现自己的 clone() 方法,访问控制符设为 public;
- 实现 clone() 方法时通过 super.clone();调用 Object 实现的 clone() 方法来得到该对象的副本,并返回该副本;
class Address {
String detail;
public Address(String detail) {
this.detail = detail;
}
}
//实现cloneable接口
class User implements Cloneable {
int age;
Address address;
public User(int age) {
this.age = age;
address = new Address("北京");
}
//通过调用super.clone()来实现clone()方法
@Override
public User clone() throws CloneNotSupportedException {
return (User) super.clone();
}
}
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
User u1 = new User(29);
//clone得到u1对象的副本
User u2 = u1.clone();
//判断u1、u2是否相同
System.out.println(u1 == u2);//输出false
//判断u1、u2的address是否相等
System.out.println(u1.address == u2.address);//输出true
}
}
User 对象就实现了“自我克隆”,克隆出来的对象时原有对象的副本,所以二者并不相等;Object 类提供的 Clone 机制只对对象里的各实例变量进行简单复制,如果实例变量的类型是引用类型,Object 的 Clone 机制也只是简单地复制这个引用变量,这样原有对象的引用类型的实例变量与克隆对象的引用类型的实例变量依然指向内存中的同一个实例。
Object 类提供的 clone() 方法是一个 native 本地方法,由底层操作系统实现,十分高效,但它只是一种“浅克隆”,只克隆该对象的所有成员变量值,不会对引用类型的成员变量值所引用的对象进行克隆。
class Address implements Cloneable{
String detail;
public Address(String detail) {
this.detail = detail;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
//实现cloneable接口
class User implements Cloneable {
int age;
Address address;
public User(int age) {
this.age = age;
address = new Address("北京");
}
//通过调用super.clone()来实现clone()方法
@Override
public User clone() throws CloneNotSupportedException {
User user = null;
user = (User)super.clone();//浅复制
user.address = (Address)address.clone();
return user;
}
}
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
User u1 = new User(29);
//clone得到u1对象的副本
User u2 = u1.clone();
//判断u1、u2是否相同
System.out.println(u1 == u2);//输出false
//判断u1、u2的address是否相等
System.out.println(u1.address == u2.address);//输出false
}
}
在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。
6.3.2 Objects类
Java 7 新增了一个 Objects 工具类,它提供了一些工具方法来操作对象,这些工具方法大多是“空指针”安全的。
Java 为工具类的命名习惯是添加一个字母 s,比如操作数组的工具类是 Arrays,操作集合的工具类是 Collections。
public class ObjectsTest {
//定义一个Obj变量,默认值为null
static Objects obj;
public static void main(String[] args) {
//输出一个null对象的hashcode值,输出0
System.out.println(Objects.hashCode(obj));
//输出一个null对象的toString值,输出null
System.out.println(Objects.toString(obj));
//要求obj不能为null,如果obj为null则引发异常
System.out.println(Objects.requireNonNull(obj, "obj的参数不能是null"));
}
}
6.3.3 String、StringBuffer和StringBuilder类
String 类是不可变类,即一旦一个 String 对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。StringBuffer 对象则代表一个字符序列可变的字符串,当一个StringBuffer被创建以后,通过 append()、insert()、reverse()、setCharAt()、setlength() 等方法可以改变这个字符串对象的字符序列,一旦通过 StringBuffer 生成了最终想要的字符串,就可以调用 toString 方法转换成 String 对象。JDK 1.5 以后又新增了一个 StringBulider类,StringBuilder和StringBuffer基本相似,两个类的构造器和方法也基本相同,但StringBuilder没有实现线程安全,所以性能略高,因此创建可变字符串对象,应优先考虑使用StringBuilder类。
String、StringBuilder、StringBuffer都实现了CharSequence接口,因此CharSequence可认为是一个字符串的协议接口。
public class StringTest {
public static void main(String[] args) {
/*
String构造器
*/
//创建一个包含0字符串序列的String对象(并不是null)
String str1 = new String();
//使用指定的字符集将指定byte[]数组解码成一个新的String对象
//String(byte[] bytes, Charset charsets),常见US-ASCII、GBK、UTF-8、UTF-16
//String(byte[] bytes, String charsetName)
byte[] byteArr = new byte[]{48, 49, 50};
String str2 = new String(byteArr, Charset.forName("US-ASCII"));//字符串为012
//使用平台默认字符集将指定byte数组从offset开始,长度为length的子数组解码成新的String对象
//String(byte[] bytes, int offset, int length)
//String(byte[] bytes, int offset, int length, String charsetName)
String str3 = new String(byteArr, 0, 2);//字符串为01
//将指定字符数组从offset开始,长度为count的字符元素连缀成字符串
//String(char[] value, int offset, int count)
char[] value = new char[]{'J', 'a', 'v', 'a'};
String str4 = new String(value, 0, 4);//字符串为Java
//根据字符串直接量来创建一个String对象
String str5 = new String("Hello World!");//字符串为Hello World
//根据StringBuffer对象和StringBuilder对象创建String对象
//String(StringBuffer buffer)
//String(StringBuilder builder)
StringBuffer buffer = new StringBuffer("Hello World!");
String str6 = new String(buffer);
/*
操作字符串方法
*/
//获取字符串中指定位置的字符,0 ~ length - 1
//char charAt(int index)
String s1 = new String("Java");
System.out.println(s1.charAt(2));//输出v
//比较字符串大小,如果字符序列相等返回0,不相等时返回第一个不相等的字符差或长度差(子串)
//int compareTo(String anotherString)
String s2 = new String("abcdefg");
String s3 = new String("abce");
String s4 = new String("abcd");
System.out.println(s2.compareTo(s3));//输出字符差 -1
System.out.println(s2.compareTo(s4));//输出长度差 3
//将该String对象与str连接在一起,与字符串连接运算符'+'功能相同
//String concat(String str)
System.out.println(s3.concat(s4));//输出abceabcd
//将该String对象与String对象sb进行比较,包含的字符序列相同返回true
//boolean contentEquals(StringBuffer sb)
StringBuffer sb = new StringBuffer("Java");
System.out.println(s1.contentEquals(sb));//输出true
//将字符数组连缀成字符串,与String(char[] content)构造器功能相同
//static String copyValueOf(char[] data)
//将char数组的子数组中的元素连缀成字符串,与String(char[] value, int offset, int count)构造器相同
//static String copyValueOf(char[], int offset, int count)
//返回该String对象是否以suffix结尾
//boolean endsWith(String suffix)
String s5 = new String("javaSE");
System.out.println(s5.endsWith("SE"));//输出true
//将该String对象与指定对象的字符序列比较,相同则返回true
//boolean equals(Object anObject)
//boolean equalsIgnoreCase(Object anObject) 忽略大小写
//将该String对象转换成byte数组
//byte[] getBytes(String str)
System.out.println(Arrays.toString(str2.getBytes()));//输出[48, 49 ,50]
//将字符串中从srcBegin开始到srcEnd-1结束的字符赋值到dst字符数组中
//void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
char[] chArr = new char[]{'I', ' ', 'L', 'O', 'V', 'E', ' ', 'C', 'H', 'I', 'N', 'A'};
String s6 = new String("JAVA ");
s6.getChars(0, 5, chArr, 7);
System.out.println(chArr);//字符数组可以直接打印,输出I LOVE Java
//找出ch字符(字符串)在该字符串中第一次(最后一次)出现的位置
//int indexOf(int ch)
//int indexOf(int ch, int fromIndex)
//int indexOf(String str)
//int indexOf(String str, int fromIndex)
//int lastIndexOf(int ch)
//int lastIndexOf(int ch, int fromIndex)
//int lastIndexOf(String str)
//int lastIndexOf(String str, int fromIndex)
//int length():返回当前字符串长度
//将字符串第一个oldChar替换成newChar
//String replace(char oldChar, char newChar)
//判断该字符串是否以prefix开始
//boolean startsWith(String prefix)
//boolean startsWith(String prefix, int offset)
String s7 = new String("Java");
System.out.println(s7.startsWith("Ja"));//输出true
//获取从beginIndex-1位置到结束的子字符串
//String substring(int beginIndex)
//获取从beginIndex位置到endIndex-1位置的子字符串
//String substring(int beginIndex, int endIndex)
System.out.println(s7.substring(0, 2));
//将String对象转换成char数组
//char[] toCharArray()
//字符串转换大小写
//String toLowerCase()
//String toUpperCase()
//将基本类型转换成String对象的方法
//static String valueOf(X x)
/*
StringBuilder、StringBuffer有两个属性:length(字符串长度)、capacity(容量)
*/
StringBuffer strBuffer = new StringBuffer();
//追加字符串append(X x),参数为boolean、char、int、long、float、double、char[]、String、StringBuffer
strBuffer.append("Java");//Java
//插入insert(int offset, X x),参数同上
strBuffer.insert(0, "Hello ");//Hello Java
//替换replace(int start, int end, String str) 位置 end - 1
strBuffer.replace(5, 6, ",");//Hello,Java
//删除delete(int start, int end)
strBuffer.delete(5, 6);//HelloJava
//反转reserve()
strBuffer.reverse();//avaJolleH
System.out.println(strBuffer.length());//9
System.out.println(strBuffer.capacity());//16
strBuffer.setLength(5);//保留前五位 avaJo
//System.out.println(strBuffer);
}
}
6.3.4 Math类
Java 提供了 Math 工具类来完成复杂的运算,Math 类的构造器被定义成 private,因此无法创建 Math 类的对象;Math 类中所有方法都是类方法,可以直接通过类名来调用;还提供了两个类变量 PI 和 E,也即是 π 和 e。
6.3.5 Random类
Random 类专门用于生成一个伪随机函数,它由两个构造器:一个构造器使用默认的种子(以当前时间作为种子),另一个构造器需要程序员显示传入一个 long 型整数的种子。
ThreadLocalRandom 类是 Java 7 新增的一个 Random 增强版类,在并发环境下,使用 ThreadLocalRandom 来代替 Random 可以减少多线程资源竞争,最终保证系统具有更好的线程安全性。
public class RandomTest {
public static void main(String[] args) {
Random rand = new Random();
System.out.println("rand.nextBoolean: " + rand.nextBoolean());
//生成随机byte数组
byte[] bArr = new byte[16];
rand.nextBytes(bArr);
System.out.println(Arrays.toString(bArr));
//生成0.0~1.0之间的浮点类型伪随机数
System.out.println(rand.nextDouble());
System.out.println(rand.nextFloat());
//生成平均值是0.0,标准差是1.0的伪高斯数
System.out.println(rand.nextGaussian());
//生成int范围的伪随机整数
System.out.println(rand.nextInt());
//生成0~2020之间的伪随机整数(左闭右开)
System.out.println(rand.nextInt(2020));
//生成处于long范围的伪随机整数
System.out.println(rand.nextLong());
}
}
Random 使用48位的种子,如果这个类的两个实例是同一个种子创建的,对它们以同样的顺序调用方法,则会产生相同的数字序列。也就是说,Random 产生的数字并不是真正随机的,而是一种伪随机,为避免两个 Random 对象产生相同的数字序列,通常推荐使用当前时间作为 Random 对象的种子。
Random rand = new Random(System.currentTimeMillis());
ThreadLocalRandom 与 Random 都比 Math 的 random() 方法提供了更多方式来生成各种随机数,可以指定对随机数的范围。ThreadLocalRandom 类用法与 Random 类的用法相似,提供一个静态的 current() 方法来获取 ThreadLocalRandom 对象,获取该对象之后即可调用各种 nextXxx() 方法来获取伪随机数。
ThreadLocalRandom tlr = ThreadLocalRandom.current();
//生成2000到2020之间的伪随机int数
System.out.println(tlr.nextInt(2000, 2020));
//生成0.0 ~ π之间的伪随机double数
System.out.println(tlr.nextDouble(0.0, Math.PI));
6.3.6 BigDecimal
Java 以及很多编程语言都存在浮点数精度丢失的问题,为了精确表示和计算浮点数,Java 提供了 BIgDecimal 类,该类提供了大量的构造器用于创建 BIgDecimal 对象,包括把所有的基本数值型变量转换成一个 BigDecimal 对象,也包括利用数字字符串、数字字符数组来创建 BigDecimal 对象。
不推荐使用 Double 作为参数的构造器,因为 double 是一个近似数,如果一定要使用,可以通过 BIgDecimal.valueOf(double value) 静态方法来创建 BIgDecimal 对象。通常建议优先使用基于 String 的构造器。
BIgDecimal 类提供了 add()、substract()、mutiply()、divide()、pow() 等方法对精确浮点数进行常规算术运算。
public class BigDecimalTest {
public static void main(String[] args) {
BigDecimal f1 = new BigDecimal("0.05");
BigDecimal f2 = BigDecimal.valueOf(0.01);
//使用String构造器的BigDecimal
System.out.println(f1.add(f2));
System.out.println(f1.subtract(f2));
System.out.println(f1.multiply(f2));
System.out.println(f1.divide(f2));
}
}
6.4 日期时间类
6.4.1 Date类
Java 提供了 Data 类来处理日期、时间(java.util 包下的 Date 类,而不是 java.sql 包下的Date 类),但大部分构造器、方法已经过时,不再推荐使用。
6.4.2 Calendar类
Calendar 类是一个抽象类,是所有日历类的模板,可以创建自己的 Calendar 子类作为 Calendar 对象使用(多态)。该类本身不能使用构造器来创建 Calendar 对象。但它提供了几个静态 getInstance 方法来获取 Calendar 对象,这些方法根据 TimeZone,Locale 类来获取特定的 Calendar,如果不指定 TimeZone、Locale,则使用默认的 TimeZone、Locale 来创建对象。
Calendar 类提供了大量访问、修改日期时间的方法,field 是 Calendar 类的类变量,注意月份的起始值是 0,常用方法如下:
public class CalendarTest {
public static void main(String[] args) {
Calendar c = Calendar.getInstance();
//int get(int field):返回指定日历字段的值
System.out.println(c.get(Calendar.YEAR));//年
System.out.println(c.get(Calendar.MONTH));//月
System.out.println(c.get(Calendar.DATE));//日
//int getActualMaximum(int field):返回指定日历字段的最大值
//int getActualMinimum(int field):返回指定日历字段的最小值
System.out.println(c.getActualMaximum(Calendar.YEAR));//292278994
System.out.println(c.getActualMaximum(Calendar.MONTH));//11
System.out.println(c.getActualMaximum(Calendar.DATE));//31
System.out.println(c.getActualMinimum(Calendar.YEAR));//1
System.out.println(c.getActualMinimum(Calendar.MONTH));//0
System.out.println(c.getActualMinimum(Calendar.DATE));//1
//void set(int year, int month, int date):将给定的日历字段设置为给定值
//void set(int year, int month, int date, int hourOfDay, int minute):将给定的日历字段设置为给定值
//void set(int year, int month, int date, int hourOfDay, int minute, int second):将给定的日历字段设置为给定值
c.set(1949, 9, 1, 12, 0, 0);
System.out.println(c.getTime());
//void add(int field, int amount):根据日历规则给指定的日历字段添加或减去指定的时间值
//void roll(int field, int amount):与add类似,区别在于加上amount超过字段所能表示的最大值不会向前进位
c.add(Calendar.MONTH, 3);
System.out.println(c.getTime());//1950-1-1
c.roll(Calendar.DATE,31);
System.out.println(c.getTime());//1950-1-1,月份没有进位
}
}
- add 和 roll 方法的区别:
- add(int field, int amount) 主要用于改变 Calendar 的特定字段的值,如果需要增加某字段的值,则 amount 为正数,反之为负数。当被修改的字段超出它允许的范围时,会发生进位,即上一个字段也会增大。如果下一级字段也需要改变,那么字段会修正到变化最小的值。
Calendar cAdd = Calendar.getInstance();
cAdd.set(2020, 4,31);//2020-5-31
//上一个字段自动增加
//cAdd.add(Calendar.MONTH, 9);//2021-2-28
cAdd.add(Calendar.MONTH, 10);//2021-3-31
//由于2月没有31日,自动修正到28日
cAdd.add(Calendar.MONTH, -1);//2021-2-28
- roll(int field, int amount) 方法遇到被修改的字段超出允许范围时上一级字段不会增加,下一级字段处理规则与 add() 相似。
Calendar cRoll = Calendar.getInstance();
cRoll.set(2020, 4, 31);//2020-5-31
//上一个字段不会自动增加
//cRoll.roll(Calendar.MONTH, 9);//2020-2-29
cRoll.roll(Calendar.MONTH, 10);//2020-3-31
cRoll.roll(Calendar.MONTH, -1);//2020-2-29
System.out.println(cRoll.getTime());
- 设置 Calendar 的容错性:
Calendar 默认支持较好的容错性,通过 setLenient() 方法可以设置容错性,让它进行严格的参数检查,当 Calendar 处于 lenient 模式时,每个时间字段可接受超出它允许范围的值;当 Calendar 处于 non-lenient 模式时,超出范围程序会抛出异常。
Calendar cal = Calendar.getInstance();
//结果是YEAR字段加1,MONTH字段为1(2月)
cal.set(Calendar.MONTH, 13);
System.out.println(cal.getTime());
//关闭容错性
cal.setLenient(false);
cal.set(Calendar.MONTH, 13);
System.out.println(cal.getTime());//输出日期会报错 IllegalArgumentException: MONTH
- set() 方法延迟修改
set(f, value) 方法将日历字段 f 更改为 value,此外它还设置了一个内部成员变量指示日历字段 f 已经被更改。尽管日历字段 f 是立即更改的,但该 Calendar 所代表的时间却不会立即修改,直到下次调用 get()、getTime()、getTimeMillis()、add() 或 roll() 才会重新计算日历的时间,这被称为 set() 的延迟修改,这样可以多次调用 set() 不会触发多次不必要的计算(需要计算出一个代表实际时间的 long 整数)。
cal.setLenient(true);
cal.set(2020, 6, 31);
//将月份改为11,但11月并不存在31日
//如果立即修改,系统会自动调整到12月1日
cal.set(Calendar.MONTH, 10);
//System.out.println(cal.getTime());
cal.set(Calendar.DATE, 1);
System.out.println(cal.getTime());//输出2020-11-1