引言
在 Java 编程世界中,核心类库是构建一切应用的基础。第 8 章将带大家深入学习 Java 中最常用的核心类,这些类贯穿于日常开发的方方面面。掌握这些核心类的使用技巧,能让我们的代码更简洁、高效、易维护。本章将从 Object 类讲起,逐步介绍 Math 类、基本类型包装类以及日期时间 API,每个知识点都配有完整可运行的代码示例,帮助大家快速上手。
8.1 Object:终极父类
在 Java 中,所有类都直接或间接继承自 Object 类,它是 Java 类层次结构的根。Object 类定义了所有对象都具备的基本方法,掌握这些方法是理解 Java 面向对象特性的基础。
类图(Object 类核心方法)
@startuml
class Object {
+ toString(): String
+ equals(Object): boolean
+ hashCode(): int
+ clone(): Object
+ finalize(): void
+ getClass(): Class<?>
+ notify(): void
+ notifyAll(): void
+ wait(): void
+ wait(long): void
+ wait(long, int): void
}
@enduml
8.1.1 toString () 方法
作用:返回对象的字符串表示形式,默认实现为 类名@哈希码的十六进制
。
使用场景:打印对象、日志输出等需要对象文字描述的场景。
最佳实践:自定义类建议重写 toString (),返回对象的关键属性信息。
代码示例:toString () 方法使用与重写
/**
* 演示toString()方法的默认实现与重写
*/
public class ToStringDemo {
public static void main(String[] args) {
// 创建默认对象,使用默认toString()
Object obj = new Object();
System.out.println("Object默认toString(): " + obj.toString());
// 创建自定义对象,使用重写后的toString()
Student student = new Student("张三", 20, "计算机科学");
System.out.println("Student重写toString(): " + student.toString());
// 打印对象时会自动调用toString()
System.out.println("直接打印对象: " + student);
}
// 自定义学生类
static class Student {
private String name;
private int age;
private String major;
public Student(String name, int age, String major) {
this.name = name;
this.age = age;
this.major = major;
}
// 重写toString()方法,返回对象的关键信息
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + ", major='" + major + "'}";
}
}
}
运行结果:
8.1.2 equals () 方法
作用:判断两个对象是否 "相等",默认实现为 this == obj
(比较内存地址)。
与 == 的区别:
==
对于基本类型比较值,对于引用类型比较内存地址- equals () 默认比较内存地址,重写后可自定义 "相等" 逻辑(如比较属性值)
重写原则:
- 自反性:
x.equals(x)
应返回 true - 对称性:
x.equals(y)
与y.equals(x)
结果一致 - 传递性:若
x.equals(y)
和y.equals(z)
为 true,则x.equals(z)
也为 true - 一致性:多次调用结果应一致
- 非空性:
x.equals(null)
应返回 false
代码示例:equals () 方法重写与使用
/**
* 演示equals()方法的使用与重写
*/
public class EqualsDemo {
public static void main(String[] args) {
// 字符串的equals()已重写,比较内容
String s1 = new String("Java");
String s2 = new String("Java");
System.out.println("s1 == s2: " + (s1 == s2)); // false(不同对象)
System.out.println("s1.equals(s2): " + s1.equals(s2)); // true(内容相同)
// 自定义对象的equals()重写演示
Student stu1 = new Student("张三", 20);
Student stu2 = new Student("张三", 20);
Student stu3 = new Student("李四", 21);
System.out.println("stu1.equals(stu2): " + stu1.equals(stu2)); // true(属性相同)
System.out.println("stu1.equals(stu3): " + stu1.equals(stu3)); // false(属性不同)
System.out.println("stu1.equals(null): " + stu1.equals(null)); // false(非空性)
}
static class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// 重写equals(),当姓名和年龄都相同时认为相等
@Override
public boolean equals(Object o) {
// 1. 自反性检查:地址相同直接返回true
if (this == o) return true;
// 2. 非空性和类型检查
if (o == null || getClass() != o.getClass()) return false;
// 3. 转换类型并比较属性
Student student = (Student) o;
return age == student.age && name.equals(student.name);
}
}
}
运行结果:
8.1.3 hashCode () 方法
作用:返回对象的哈希码值,主要用于哈希表(如 HashMap、HashSet)中快速定位对象。
与 equals () 的关系:
- 若
x.equals(y)
为 true,则x.hashCode()
必须等于y.hashCode()
- 若
x.hashCode()
不等,则x.equals(y)
一定为 false - 若
x.hashCode()
相等,x.equals(y)
不一定为 true(哈希冲突)
重写建议:当重写 equals () 时,必须同时重写 hashCode (),确保相等的对象有相同的哈希码。
代码示例:hashCode () 方法重写
import java.util.Objects;
/**
* 演示hashCode()方法的重写与使用
*/
public class HashCodeDemo {
public static void main(String[] args) {
Student stu1 = new Student("张三", 20);
Student stu2 = new Student("张三", 20);
Student stu3 = new Student("李四", 21);
System.out.println("stu1.hashCode(): " + stu1.hashCode());
System.out.println("stu2.hashCode(): " + stu2.hashCode()); // 与stu1相同
System.out.println("stu3.hashCode(): " + stu3.hashCode()); // 与stu1不同
}
static class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// 重写equals()
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
// 重写hashCode(),基于name和age计算哈希码
@Override
public int hashCode() {
// Objects.hash()会自动处理null值
return Objects.hash(name, age);
}
}
}
运行结果:
8.1.4 clone () 方法
作用:创建并返回对象的副本,实现对象的复制。
使用条件:
- 类必须实现
Cloneable
接口(标记接口,无方法) - 重写 clone () 方法(通常调用
super.clone()
) - 若有引用类型属性,需考虑深拷贝
浅拷贝 vs 深拷贝:
- 浅拷贝:基本类型属性复制值,引用类型属性复制地址(共享对象)
- 深拷贝:所有属性都复制新对象,完全独立
代码示例:对象克隆(浅拷贝与深拷贝)
/**
* 演示clone()方法的使用(浅拷贝与深拷贝)
*/
public class CloneDemo {
public static void main(String[] args) throws CloneNotSupportedException {
// 创建原始对象
Address addr = new Address("北京市", "海淀区");
Person p1 = new Person("张三", 20, addr);
// 克隆对象(浅拷贝)
Person p2 = (Person) p1.clone();
// 打印原始对象和克隆对象
System.out.println("原始对象p1: " + p1);
System.out.println("克隆对象p2: " + p2);
// 修改原始对象的引用属性
addr.setCity("上海市");
System.out.println("修改地址后p1: " + p1);
System.out.println("修改地址后p2: " + p2); // 浅拷贝会受影响
}
// 地址类(引用类型)
static class Address {
private String city;
private String district;
public Address(String city, String district) {
this.city = city;
this.district = district;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return city + "-" + district;
}
}
// 人员类(实现Cloneable接口)
static class Person implements Cloneable {
private String name;
private int age;
private Address address; // 引用类型属性
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
// 重写clone()方法(浅拷贝)
@Override
protected Object clone() throws CloneNotSupportedException {
// 调用父类clone()实现浅拷贝
return super.clone();
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", address=" + address + "}";
}
}
}
运行结果:
深拷贝实现:修改 Person 类的 clone () 方法,对引用类型属性也进行克隆:
// 深拷贝实现
@Override
protected Object clone() throws CloneNotSupportedException {
// 1. 先浅拷贝基本类型
Person clone = (Person) super.clone();
// 2. 对引用类型属性单独克隆
clone.address = new Address(address.city, address.district);
return clone;
}
8.1.5 finalize () 方法
作用:垃圾回收器回收对象前调用,用于释放资源(如文件句柄、网络连接)。
特点:
- 执行时间不确定(垃圾回收时机不确定)
- 不推荐依赖此方法释放资源(建议用 try-with-resources)
- Java 9 后已标记为过时(@Deprecated)
代码示例:finalize () 方法演示
/**
* 演示finalize()方法的使用(仅作了解,实际开发不推荐)
*/
public class FinalizeDemo {
private String name;
public FinalizeDemo(String name) {
this.name = name;
System.out.println(name + "对象创建了");
}
// 重写finalize()方法
@Override
protected void finalize() throws Throwable {
try {
System.out.println(name + "对象的finalize()被调用,释放资源");
} finally {
// 调用父类的finalize()
super.finalize();
}
}
public static void main(String[] args) {
// 创建对象
new FinalizeDemo("A");
new FinalizeDemo("B");
// 提示垃圾回收(不保证立即执行)
System.gc();
// 暂停当前线程,给垃圾回收器时间
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("程序结束");
}
}
运行结果:
8.2 Math 类
Math 类是 Java 提供的数学工具类,包含大量静态方法,用于基本数学运算。
常用方法分类
类别 | 常用方法 | 功能描述 |
---|---|---|
绝对值 | abs(int/double) |
计算绝对值 |
最值 | max(a,b) , min(a,b) |
计算最大值 / 最小值 |
三角函数 | sin() , cos() , tan() , toRadians() |
三角函数计算(弧度制) |
指数对数 | pow(a,b) , sqrt() , log() , exp() |
幂运算、平方根、对数 |
取整 | ceil() , floor() , round() |
向上取整、向下取整、四舍五入 |
随机数 | random() |
生成 [0.0,1.0) 的随机数 |
代码示例:Math 类常用方法综合案例
import java.util.Scanner;
/**
* Math类常用方法综合案例
*/
public class MathDemo {
public static void main(String[] args) {
// 1. 基本运算
System.out.println("5的绝对值: " + Math.abs(-5));
System.out.println("3和7的最大值: " + Math.max(3, 7));
System.out.println("4.2和3.8的最小值: " + Math.min(4.2, 3.8));
// 2. 指数与对数
System.out.println("2的3次方: " + Math.pow(2, 3));
System.out.println("16的平方根: " + Math.sqrt(16));
System.out.println("e的2次方: " + Math.exp(2));
// 3. 取整操作
System.out.println("3.2向上取整: " + Math.ceil(3.2));
System.out.println("3.8向下取整: " + Math.floor(3.8));
System.out.println("3.5四舍五入: " + Math.round(3.5));
// 4. 随机数应用:生成1-100的随机整数
int randomNum = (int) (Math.random() * 100) + 1;
System.out.println("1-100的随机数: " + randomNum);
// 5. 综合案例:计算圆的面积和周长
Scanner scanner = new Scanner(System.in);
System.out.print("请输入圆的半径: ");
double radius = scanner.nextDouble();
double area = Math.PI * Math.pow(radius, 2); // 面积=πr²
double circumference = 2 * Math.PI * radius; // 周长=2πr
System.out.printf("半径为%.2f的圆,面积: %.2f,周长: %.2f%n",
radius, area, circumference);
scanner.close();
}
}
运行结果:
8.3 基本类型包装类
Java 为 8 种基本类型提供了对应的包装类,用于将基本类型转为对象,便于在泛型、集合等场景中使用。
包装类对应关系
基本类型 | 包装类 | 父类 |
---|---|---|
byte | Byte | Number |
short | Short | Number |
int | Integer | Number |
long | Long | Number |
float | Float | Number |
double | Double | Number |
char | Character | Object |
boolean | Boolean | Object |
8.3.1 Character 类
Character 类用于操作单个字符,提供字符判断和转换的静态方法。
常用方法
isLetter(char)
:是否为字母isDigit(char)
:是否为数字isWhitespace(char)
:是否为空白字符isUpperCase(char)
/isLowerCase(char)
:是否为大小写toUpperCase(char)
/toLowerCase(char)
:转换大小写
代码示例:Character 类使用
/**
* Character类常用方法演示
*/
public class CharacterDemo {
public static void main(String[] args) {
char c1 = 'A';
char c2 = '5';
char c3 = ' ';
char c4 = '中';
System.out.println(c1 + "是字母? " + Character.isLetter(c1)); // true
System.out.println(c2 + "是数字? " + Character.isDigit(c2)); // true
System.out.println(c3 + "是空白字符? " + Character.isWhitespace(c3)); // true
System.out.println(c1 + "是大写? " + Character.isUpperCase(c1)); // true
// 字符转换
System.out.println(c1 + "转小写: " + Character.toLowerCase(c1)); // a
// 综合案例:统计字符串中各类字符数量
String str = "Hello World! 123 中文测试";
countCharacters(str);
}
// 统计字符串中字母、数字、空格和其他字符的数量
public static void countCharacters(String str) {
int letterCount = 0; // 字母数量
int digitCount = 0; // 数字数量
int spaceCount = 0; // 空格数量
int otherCount = 0; // 其他字符数量
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (Character.isLetter(c)) {
letterCount++;
} else if (Character.isDigit(c)) {
digitCount++;
} else if (Character.isWhitespace(c)) {
spaceCount++;
} else {
otherCount++;
}
}
System.out.println("\n字符串统计结果:");
System.out.println("字母: " + letterCount);
System.out.println("数字: " + digitCount);
System.out.println("空格: " + spaceCount);
System.out.println("其他字符: " + otherCount);
}
}
运行结果:
8.3.2 Boolean 类
Boolean 类是 boolean 的包装类,提供了 boolean 与 String 的转换方法。
常用方法
parseBoolean(String)
:将字符串转为 boolean("true" 返回 true,其他返回 false)valueOf(boolean)
/valueOf(String)
:返回 Boolean 对象booleanValue()
:将 Boolean 对象转为 boolean 基本类型
代码示例:Boolean 类使用
/**
* Boolean类常用方法演示
*/
public class BooleanDemo {
public static void main(String[] args) {
// 创建Boolean对象
Boolean b1 = Boolean.TRUE;
Boolean b2 = Boolean.FALSE;
Boolean b3 = Boolean.valueOf("true");
Boolean b4 = Boolean.valueOf(false);
System.out.println("b1: " + b1); // true
System.out.println("b2: " + b2); // false
System.out.println("b3: " + b3); // true
System.out.println("b4: " + b4); // false
// 字符串转boolean
String str1 = "true";
String str2 = "TRUE"; // 不区分大小写?
String str3 = "yes";
boolean bool1 = Boolean.parseBoolean(str1);
boolean bool2 = Boolean.parseBoolean(str2); // 注意:仅"true"(忽略大小写? 不,严格小写"true"才返回true)
boolean bool3 = Boolean.parseBoolean(str3);
System.out.println(str1 + "转boolean: " + bool1); // true
System.out.println(str2 + "转boolean: " + bool2); // false(必须严格小写"true")
System.out.println(str3 + "转boolean: " + bool3); // false
// 对象转基本类型
boolean primitive = b1.booleanValue();
System.out.println("b1转基本类型: " + primitive); // true
}
}
运行结果:
8.3.3 创建数值类对象
数值类(Integer、Double 等)继承自 Number 类,提供多种创建对象的方式。
代码示例:数值类对象创建
/**
* 数值包装类对象创建方式演示
*/
public class NumberObjectDemo {
public static void main(String[] args) {
// 1. 使用构造方法(已过时,推荐用valueOf())
Integer i1 = new Integer(100);
Double d1 = new Double(3.14);
// 2. 使用valueOf()方法(推荐,可能使用缓存)
Integer i2 = Integer.valueOf(100);
Double d2 = Double.valueOf(3.14);
Integer i3 = Integer.valueOf("100"); // 字符串转包装类
Double d3 = Double.valueOf("3.14");
// 3. 输出对象值
System.out.println("i1: " + i1); // 100
System.out.println("d1: " + d1); // 3.14
System.out.println("i3: " + i3); // 100
System.out.println("d3: " + d3); // 3.14
// 4. 基本类型转包装类(自动装箱的手动形式)
int num = 200;
Integer i4 = Integer.valueOf(num);
System.out.println("i4: " + i4); // 200
}
}
8.3.4 数值类的常量
数值类定义了表示取值范围和特殊值的常量。
代码示例:数值类常量使用
/**
* 数值包装类常量演示
*/
public class NumberConstantDemo {
public static void main(String[] args) {
// Integer类常量
System.out.println("int最小值: " + Integer.MIN_VALUE); // -2147483648
System.out.println("int最大值: " + Integer.MAX_VALUE); // 2147483647
System.out.println("int位数: " + Integer.SIZE); // 32(bit)
System.out.println("int字节数: " + Integer.BYTES); // 4
// Double类常量
System.out.println("\ndouble最小值: " + Double.MIN_VALUE); // 最小正非零值
System.out.println("double最大值: " + Double.MAX_VALUE);
System.out.println("正无穷大: " + Double.POSITIVE_INFINITY);
System.out.println("负无穷大: " + Double.NEGATIVE_INFINITY);
System.out.println("非数字: " + Double.NaN);
// 演示无穷大和NaN
double inf = 1.0 / 0.0;
double nan = 0.0 / 0.0;
System.out.println("1.0/0.0 = " + inf); // Infinity
System.out.println("0.0/0.0 = " + nan); // NaN
System.out.println("判断NaN: " + Double.isNaN(nan)); // true
}
}
运行结果:
8.3.5 自动装箱与自动拆箱
- 自动装箱:基本类型自动转为包装类对象(如
int → Integer
) - 自动拆箱:包装类对象自动转为基本类型(如
Integer → int
)
代码示例:自动装箱与拆箱
/**
* 自动装箱与自动拆箱演示
*/
public class AutoBoxingDemo {
public static void main(String[] args) {
// 1. 自动装箱:基本类型 → 包装类
Integer i = 100; // 等价于 Integer i = Integer.valueOf(100);
Double d = 3.14; // 等价于 Double d = Double.valueOf(3.14);
// 2. 自动拆箱:包装类 → 基本类型
int num = i; // 等价于 int num = i.intValue();
double pi = d; // 等价于 double pi = d.doubleValue();
System.out.println("i = " + i); // 100
System.out.println("num = " + num); // 100
System.out.println("d = " + d); // 3.14
System.out.println("pi = " + pi); // 3.14
// 3. 集合中的自动装箱
java.util.List<Integer> list = new java.util.ArrayList<>();
list.add(1); // 自动装箱:int → Integer
list.add(2);
list.add(3);
// 4. 运算中的自动拆箱
Integer a = 200;
Integer b = 300;
int sum = a + b; // 自动拆箱为int后计算
System.out.println("a + b = " + sum); // 500
// 5. 注意:包装类可能为null,拆箱时需避免NullPointerException
Integer c = null;
// int error = c; // 运行时抛出NullPointerException
System.out.println("c是否为null: " + (c == null)); // true
// 6. Integer缓存机制(-128~127之间的值会缓存)
Integer x = 127;
Integer y = 127;
Integer m = 128;
Integer n = 128;
System.out.println("x == y: " + (x == y)); // true(缓存命中)
System.out.println("m == n: " + (m == n)); // false(超出缓存范围)
System.out.println("x.equals(y): " + x.equals(y)); // true(值比较)
}
}
运行结果:
8.3.6 字符串转换为基本类型
包装类提供了parseXxx(String)
方法,将字符串转为对应的基本类型。
代码示例:字符串转基本类型
import java.util.Scanner;
/**
* 字符串转换为基本类型演示
*/
public class StringToPrimitiveDemo {
public static void main(String[] args) {
// 1. 字符串转int
String strInt = "123";
int num = Integer.parseInt(strInt);
System.out.println("字符串\"123\"转int: " + num); // 123
// 2. 字符串转double
String strDouble = "3.14159";
double pi = Double.parseDouble(strDouble);
System.out.println("字符串\"3.14159\"转double: " + pi); // 3.14159
// 3. 字符串转boolean(仅"true"返回true)
String strBool = "true";
boolean flag = Boolean.parseBoolean(strBool);
System.out.println("字符串\"true\"转boolean: " + flag); // true
// 4. 综合案例:计算用户输入的数字之和
Scanner scanner = new Scanner(System.in);
System.out.print("请输入第一个数字: ");
String input1 = scanner.nextLine();
System.out.print("请输入第二个数字: ");
String input2 = scanner.nextLine();
try {
// 字符串转数字
double num1 = Double.parseDouble(input1);
double num2 = Double.parseDouble(input2);
double sum = num1 + num2;
System.out.println("两个数字之和: " + sum);
} catch (NumberFormatException e) {
System.out.println("输入格式错误,无法转换为数字!");
}
scanner.close();
}
}
运行结果:
8.3.7 BigInteger 和 BigDecimal 类
- BigInteger:用于任意精度的整数运算
- BigDecimal:用于任意精度的小数运算(解决 float/double 的精度问题)
代码示例:高精度计算
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
/**
* BigInteger和BigDecimal类演示
*/
public class BigIntegerDemo {
public static void main(String[] args) {
// 1. BigInteger:大整数运算
// 普通int无法表示的大整数
BigInteger bigInt1 = new BigInteger("12345678901234567890");
BigInteger bigInt2 = new BigInteger("98765432109876543210");
// 大整数加法
BigInteger sum = bigInt1.add(bigInt2);
// 大整数乘法
BigInteger product = bigInt1.multiply(bigInt2);
System.out.println("大整数1: " + bigInt1);
System.out.println("大整数2: " + bigInt2);
System.out.println("大整数之和: " + sum);
System.out.println("大整数之积: " + product);
// 2. BigDecimal:高精度小数运算(解决double精度问题)
// 问题:double运算有精度误差
double d1 = 0.1;
double d2 = 0.2;
System.out.println("\ndouble 0.1 + 0.2 = " + (d1 + d2)); // 0.30000000000000004(误差)
// 使用BigDecimal解决精度问题
BigDecimal bd1 = new BigDecimal("0.1"); // 注意:用字符串构造,避免double本身的误差
BigDecimal bd2 = new BigDecimal("0.2");
BigDecimal bdSum = bd1.add(bd2);
System.out.println("BigDecimal 0.1 + 0.2 = " + bdSum); // 0.3
// 3. BigDecimal除法(需指定保留位数和舍入模式)
BigDecimal dividend = new BigDecimal("1");
BigDecimal divisor = new BigDecimal("3");
// 保留2位小数,四舍五入
BigDecimal result = dividend.divide(divisor, 2, RoundingMode.HALF_UP);
System.out.println("1 ÷ 3 = " + result); // 0.33
// 4. 综合案例:计算圆的面积(高精度)
BigDecimal radius = new BigDecimal("2.5");
BigDecimal pi = new BigDecimal("3.14159265358979323846");
BigDecimal area = pi.multiply(radius.pow(2)); // 面积=πr²
// 保留4位小数
area = area.setScale(4, RoundingMode.HALF_UP);
System.out.println("半径为2.5的圆面积(高精度): " + area); // 19.6349
}
}
运行结果:
8.4 日期 - 时间 API
Java 8 引入了新的日期 - 时间 API(java.time 包),解决了旧 API(Date、Calendar)的线程不安全、设计混乱等问题。
新日期时间 API 核心类关系图
8.4.1 本地日期类 LocalDate
LocalDate 表示不含时间的日期(年 - 月 - 日),不可变且线程安全。
常用方法
now()
:获取当前日期of(int year, int month, int dayOfMonth)
:创建指定日期plusDays(long)
/minusDays(long)
:增减天数getYear()
/getMonthValue()
/getDayOfMonth()
:获取年 / 月 / 日isLeapYear()
:判断是否闰年
代码示例:LocalDate 使用
import java.time.LocalDate;
/**
* LocalDate类常用方法演示
*/
public class LocalDateDemo {
public static void main(String[] args) {
// 1. 获取当前日期
LocalDate today = LocalDate.now();
System.out.println("当前日期: " + today); // 格式:yyyy-MM-dd
// 2. 创建指定日期
LocalDate birthday = LocalDate.of(2000, 5, 20);
System.out.println("指定日期(生日): " + birthday); // 2000-05-20
// 3. 获取日期组件
int year = today.getYear();
int month = today.getMonthValue(); // 1-12
int day = today.getDayOfMonth();
int dayOfYear = today.getDayOfYear(); // 当年的第几天
String weekday = today.getDayOfWeek().name(); // 星期几(英文)
System.out.println("年: " + year);
System.out.println("月: " + month);
System.out.println("日: " + day);
System.out.println("当年第几天: " + dayOfYear);
System.out.println("星期: " + weekday);
// 4. 日期加减
LocalDate tomorrow = today.plusDays(1);
LocalDate lastMonth = today.minusMonths(1);
System.out.println("明天: " + tomorrow);
System.out.println("上个月今天: " + lastMonth);
// 5. 判断闰年
boolean isLeap = today.isLeapYear();
System.out.println("今年是闰年? " + isLeap);
// 6. 日期比较
boolean isAfter = today.isAfter(birthday);
boolean isBefore = today.isBefore(birthday);
System.out.println("今天在生日之后? " + isAfter); // true
System.out.println("今天在生日之前? " + isBefore); // false
}
}
运行结果(因当前日期不同而变化):
8.4.2 本地时间类 LocalTime
LocalTime 表示不含日期的时间(时:分: 秒。纳秒),不可变且线程安全。
常用方法
now()
:获取当前时间of(int hour, int minute, int second)
:创建指定时间plusHours()
/minusMinutes()
:增减时 / 分 / 秒getHour()
/getMinute()
/getSecond()
:获取时分秒
代码示例:LocalTime 使用
import java.time.LocalTime;
/**
* LocalTime类常用方法演示
*/
public class LocalTimeDemo {
public static void main(String[] args) {
// 1. 获取当前时间
LocalTime now = LocalTime.now();
System.out.println("当前时间: " + now); // 格式:HH:MM:SS.sssssssss
// 2. 创建指定时间
LocalTime morning = LocalTime.of(8, 30, 0); // 8:30:00
LocalTime noon = LocalTime.of(12, 0, 0); // 12:00:00
System.out.println("指定时间(早上): " + morning);
System.out.println("指定时间(中午): " + noon);
// 3. 获取时间组件
int hour = now.getHour(); // 24小时制
int minute = now.getMinute();
int second = now.getSecond();
int nano = now.getNano(); // 纳秒
System.out.println("时: " + hour);
System.out.println("分: " + minute);
System.out.println("秒: " + second);
System.out.println("纳秒: " + nano);
// 4. 时间加减
LocalTime later = now.plusHours(2).plusMinutes(30);
LocalTime earlier = now.minusMinutes(45);
System.out.println("2小时30分后: " + later);
System.out.println("45分钟前: " + earlier);
// 5. 时间比较
boolean isMorningLater = morning.isAfter(noon);
System.out.println("早上8:30在中午12:00之后? " + isMorningLater); // false
}
}
运行结果:
8.4.3 本地日期时间类 LocalDateTime
LocalDateTime 是 LocalDate 和 LocalTime 的组合,表示完整的日期和时间,是最常用的日期时间类。
常用方法
now()
:获取当前日期时间of(...)
:创建指定日期时间toLocalDate()
/toLocalTime()
:拆分日期和时间- 继承 LocalDate 和 LocalTime 的所有方法
代码示例:LocalDateTime 使用
import java.time.LocalDateTime;
import java.time.LocalDate; // 添加LocalDate导入
import java.time.LocalTime; // 添加LocalTime导入
/**
* LocalDateTime类常用方法演示
*/
public class LocalDateTimeDemo {
public static void main(String[] args) {
// 1. 获取当前日期时间
LocalDateTime now = LocalDateTime.now();
System.out.println("当前日期时间: " + now); // 格式:yyyy-MM-ddTHH:MM:SS.sssssssss
// 2. 创建指定日期时间
LocalDateTime specificTime = LocalDateTime.of(2025, 12, 31, 23, 59, 59);
System.out.println("指定日期时间: " + specificTime); // 2025-12-31T23:59:59
// 3. 拆分日期和时间
LocalDateTime dateTime = LocalDateTime.now();
LocalDate date = dateTime.toLocalDate();
LocalTime time = dateTime.toLocalTime();
System.out.println("提取的日期: " + date);
System.out.println("提取的时间: " + time);
// 4. 修改日期时间
LocalDateTime modified = now
.withYear(2026) // 修改年份
.withMonth(1) // 修改月份
.withDayOfMonth(1) // 修改日期
.withHour(0) // 修改小时
.withMinute(0); // 修改分钟
System.out.println("修改后的日期时间(2026年元旦): " + modified);
// 5. 日期时间加减
LocalDateTime nextWeek = now.plusWeeks(1);
LocalDateTime lastYear = now.minusYears(1);
System.out.println("一周后: " + nextWeek);
System.out.println("一年前: " + lastYear);
// 6. 综合案例:计算距离新年还有多久
LocalDateTime newYear = LocalDateTime.of(2026, 1, 1, 0, 0, 0);
System.out.println("距离2026年元旦还有: ");
}
}
运行结果:
8.4.4 Instant 类、Duration 类和 Period 类
- Instant:表示时间戳(UTC 时区的瞬间),类似旧 API 的 Date
- Duration:计算两个时间(LocalTime、LocalDateTime、Instant)之间的间隔(时分秒)
- Period:计算两个日期(LocalDate)之间的间隔(年月日)
代码示例:时间间隔计算
import java.time.*;
import java.time.temporal.ChronoUnit;
/**
* Instant、Duration和Period类演示
*/
public class DurationPeriodDemo {
public static void main(String[] args) {
// 1. Instant类(时间戳)
Instant nowInstant = Instant.now();
System.out.println("当前时间戳(UTC): " + nowInstant); // 格式:yyyy-MM-ddTHH:MM:SS.sssssssssZ
// 转换为本地日期时间(需指定时区)
LocalDateTime localDateTime = LocalDateTime.ofInstant(nowInstant, ZoneId.systemDefault());
System.out.println("转换为本地日期时间: " + localDateTime);
// 2. Duration类(计算时间间隔)
LocalTime time1 = LocalTime.of(9, 0, 0); // 9:00
LocalTime time2 = LocalTime.of(18, 30, 0); // 18:30
Duration duration = Duration.between(time1, time2);
System.out.println("\n从9:00到18:30的间隔:");
System.out.println("总秒数: " + duration.getSeconds());
System.out.println("小时数: " + duration.toHours());
System.out.println("分钟数: " + duration.toMinutes());
System.out.println("小时+分钟: " + duration.toHours() + "小时" + duration.toMinutes()%60 + "分钟");
// 3. Period类(计算日期间隔)
LocalDate date1 = LocalDate.of(2000, 5, 20); // 生日
LocalDate date2 = LocalDate.now(); // 今天
Period period = Period.between(date1, date2);
System.out.println("\n从2000-05-20到今天的间隔:");
System.out.println("年数: " + period.getYears());
System.out.println("月数: " + period.getMonths());
System.out.println("天数: " + period.getDays());
System.out.println("总天数(近似): " + ChronoUnit.DAYS.between(date1, date2));
// 4. 综合案例:计算两个日期时间的完整间隔
LocalDateTime start = LocalDateTime.of(2025, 1, 1, 10, 0, 0);
LocalDateTime end = LocalDateTime.of(2025, 12, 31, 23, 59, 59);
Duration timeDiff = Duration.between(start, end);
Period dateDiff = Period.between(start.toLocalDate(), end.toLocalDate());
System.out.println("\n2025年全年间隔:");
System.out.println("年: " + dateDiff.getYears() + ", 月: " + dateDiff.getMonths() + ", 日: " + dateDiff.getDays());
System.out.println("总小时数: " + timeDiff.toHours());
System.out.println("总分钟数: " + timeDiff.toMinutes());
}
}
运行结果:
8.4.5 其他常用类
- ZoneId/ZoneOffset:表示时区
- ZonedDateTime:带时区的日期时间
- Clock:时钟,用于获取当前时间(可指定时区)
代码示例:时区相关类使用
import java.time.*;
import java.util.Set;
/**
* 时区相关类演示
*/
public class ZoneDateTimeDemo {
public static void main(String[] args) {
// 1. 获取所有可用时区
System.out.println("部分可用时区:");
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
// 打印部分常用时区
zoneIds.stream()
.filter(zone -> zone.contains("Asia") || zone.contains("UTC"))
.limit(5)
.forEach(System.out::println);
// 2. 获取系统默认时区
ZoneId defaultZone = ZoneId.systemDefault();
System.out.println("\n系统默认时区: " + defaultZone);
// 3. ZonedDateTime:带时区的日期时间
ZonedDateTime beijingTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime newYorkTime = ZonedDateTime.now(ZoneId.of("America/New_York"));
System.out.println("\n北京当前时间: " + beijingTime);
System.out.println("纽约当前时间: " + newYorkTime);
// 4. 时区转换
ZonedDateTime converted = beijingTime.withZoneSameInstant(ZoneId.of("Europe/London"));
System.out.println("北京时区时间转换为伦敦时间: " + converted);
// 5. Clock类使用
Clock clock = Clock.system(ZoneId.of("Asia/Tokyo"));
LocalDateTime tokyoTime = LocalDateTime.now(clock);
System.out.println("\n东京当前时间: " + tokyoTime);
}
}
运行结果:
8.4.6 日期时间解析和格式化
使用DateTimeFormatter
类对日期时间进行格式化(对象→字符串)和解析(字符串→对象)。
常用预定义格式
ISO_LOCAL_DATE
:yyyy-MM-ddISO_LOCAL_TIME
:HH:mm:ssISO_LOCAL_DATE_TIME
:yyyy-MM-ddTHH:mm:ssofPattern(String)
:自定义格式(如 "yyyy 年 MM 月 dd 日 HH:mm:ss")
代码示例:日期时间格式化与解析
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
/**
* 日期时间格式化与解析演示(修正版)
*/
public class DateTimeFormatDemo {
public static void main(String[] args) {
// 1. 获取当前日期时间
LocalDateTime now = LocalDateTime.now();
System.out.println("原始日期时间: " + now);
// 2. 预定义格式格式化
DateTimeFormatter isoDate = DateTimeFormatter.ISO_LOCAL_DATE;
DateTimeFormatter isoTime = DateTimeFormatter.ISO_LOCAL_TIME;
DateTimeFormatter isoDateTime = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
System.out.println("\nISO日期格式: " + now.format(isoDate)); // yyyy-MM-dd
System.out.println("ISO时间格式: " + now.format(isoTime)); // HH:mm:ss.sssssssss
System.out.println("ISO日期时间格式: " + now.format(isoDateTime)); // yyyy-MM-ddTHH:mm:ss.sssssssss
// 3. 本地化格式(修正:FULL格式需要时区,使用ZonedDateTime或指定时区)
DateTimeFormatter mediumFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);
// 解决FULL格式问题:为格式化器指定时区
DateTimeFormatter fullFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL)
.withZone(ZoneId.systemDefault()); // 添加系统默认时区
System.out.println("\n本地化中等格式: " + now.format(mediumFormatter)); // 2025-7-28 21:05:39
// 使用带时区的格式化器处理LocalDateTime
System.out.println("本地化完整格式: " + fullFormatter.format(now)); // 包含时区信息的完整格式
// 4. 自定义格式(常用模式字母:y-年, M-月, d-日, H-24时, m-分, s-秒, a-上午/下午)
DateTimeFormatter customFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss EEEE");
String formatted = now.format(customFormatter);
System.out.println("\n自定义格式: " + formatted); // 2025年07月28日 21:05:30 星期一
// 5. 字符串解析为日期时间(必须与格式完全匹配)
String dateTimeStr = "2023年10月01日 08:00:00 星期日";
LocalDateTime parsedDateTime = LocalDateTime.parse(dateTimeStr, customFormatter);
System.out.println("解析后的日期时间: " + parsedDateTime);
// 6. 综合案例:格式化生日并计算年龄
String birthdayStr = "2000-05-20";
// 解析生日字符串
LocalDateTime birthday = LocalDateTime.parse(birthdayStr + "T00:00:00", isoDateTime);
// 格式化生日
String birthdayFormatted = birthday.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日"));
// 计算年龄
int age = LocalDateTime.now().getYear() - birthday.getYear();
System.out.println("\n生日: " + birthdayFormatted + ", 年龄: " + age + "岁");
}
}
运行结果:
8.5 小结
本章介绍了 Java 中最常用的核心类,这些类是 Java 编程的基础,在日常开发中频繁使用:
Object 类:所有类的父类,重点掌握
toString()
、equals()
、hashCode()
的重写原则,理解浅拷贝与深拷贝的区别。Math 类:提供丰富的数学运算方法,注意
random()
生成随机数的使用场景。基本类型包装类:
- 掌握自动装箱与拆箱的机制及注意事项(如 Integer 缓存)
- 字符串与基本类型的转换方法(
parseXxx()
) - 高精度计算使用
BigInteger
和BigDecimal
日期 - 时间 API(Java 8+):
- 核心类:
LocalDate
、LocalTime
、LocalDateTime
- 时间间隔计算:
Duration
(时间)和Period
(日期) - 格式化与解析:
DateTimeFormatter
自定义格式
- 核心类:
这些核心类极大简化了 Java 编程,提高了代码的可读性和安全性,建议多动手练习加深理解。
编程练习
练习 1:学生信息管理系统(Object 类应用)
需求:设计 Student 类,重写 toString ()、equals ()、hashCode () 方法,实现学生信息的添加、查询和比较。
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Scanner;
/**
* 练习1:学生信息管理系统
*/
public class StudentManagement {
static class Student {
private String id; // 学号(唯一标识)
private String name; // 姓名
private int age; // 年龄
private String major; // 专业
public Student(String id, String name, int age, String major) {
this.id = id;
this.name = name;
this.age = age;
this.major = major;
}
// 重写toString()显示学生信息
@Override
public String toString() {
return "学号: " + id + ", 姓名: " + name + ", 年龄: " + age + ", 专业: " + major;
}
// 重写equals():学号相同则认为是同一个学生
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(id, student.id);
}
// 重写hashCode():基于学号计算
@Override
public int hashCode() {
return Objects.hash(id);
}
// getter方法
public String getId() { return id; }
public String getName() { return name; }
}
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
Scanner scanner = new Scanner(System.in);
// 添加示例学生
students.add(new Student("2025001", "张三", 20, "计算机科学"));
students.add(new Student("2025002", "李四", 21, "软件工程"));
students.add(new Student("2025003", "王五", 19, "人工智能"));
while (true) {
System.out.println("\n===== 学生信息管理系统 =====");
System.out.println("1. 添加学生");
System.out.println("2. 查看所有学生");
System.out.println("3. 查询学生");
System.out.println("4. 比较学生是否相同");
System.out.println("5. 退出");
System.out.print("请选择功能: ");
int choice = scanner.nextInt();
scanner.nextLine(); // 消费换行符
switch (choice) {
case 1:
// 添加学生
System.out.print("请输入学号: ");
String id = scanner.nextLine();
System.out.print("请输入姓名: ");
String name = scanner.nextLine();
System.out.print("请输入年龄: ");
int age = scanner.nextInt();
scanner.nextLine(); // 消费换行符
System.out.print("请输入专业: ");
String major = scanner.nextLine();
Student newStudent = new Student(id, name, age, major);
// 检查学号是否已存在
if (students.contains(newStudent)) {
System.out.println("学号已存在,添加失败!");
} else {
students.add(newStudent);
System.out.println("添加成功!");
}
break;
case 2:
// 查看所有学生
System.out.println("\n所有学生信息:");
if (students.isEmpty()) {
System.out.println("暂无学生信息");
} else {
for (Student s : students) {
System.out.println(s);
}
}
break;
case 3:
// 查询学生
System.out.print("请输入要查询的学号: ");
String queryId = scanner.nextLine();
Student queryStudent = new Student(queryId, "", 0, "");
boolean found = false;
for (Student s : students) {
if (s.equals(queryStudent)) {
System.out.println("找到学生: " + s);
found = true;
break;
}
}
if (!found) {
System.out.println("未找到学号为" + queryId + "的学生");
}
break;
case 4:
// 比较学生是否相同
System.out.print("请输入第一个学生的学号: ");
String id1 = scanner.nextLine();
System.out.print("请输入第二个学生的学号: ");
String id2 = scanner.nextLine();
Student s1 = new Student(id1, "", 0, "");
Student s2 = new Student(id2, "", 0, "");
boolean isSame = students.contains(s1) && students.contains(s2) && s1.equals(s2);
System.out.println("两个学生是否为同一个人? " + isSame);
break;
case 5:
// 退出
System.out.println("程序退出");
scanner.close();
return;
default:
System.out.println("无效选择,请重新输入");
}
}
}
}