作为一名Java开发者,日常工作中最常打交道的就是各种API类库。今天我想系统梳理一下Java中那些高频使用的工具类,特别是那些看似简单却暗藏玄机的API。本文不仅会介绍基本用法,还会分享我在实际项目中积累的经验技巧和避坑指南。
Math类是Java中最基础的数学工具类,所有方法都是静态的,这意味着我们不需要创建实例就能直接使用。下面这些方法我几乎每天都会用到:
java复制// 绝对值计算
Math.abs(-12); // 返回12
// 向上取整(天花板函数)
Math.ceil(1.000001); // 返回2.0
// 向下取整(地板函数)
Math.floor(3.9999999); // 返回3.0
// 最大值/最小值比较
Math.max(2, 4.1); // 返回4.1
Math.min(-3, 3.10); // 返回-3.0
// 幂运算
Math.pow(2,3); // 8.0(2的3次方)
Math.pow(4,0.5); // 2.0(4的平方根)
// 随机数生成
Math.random(); // 返回[0,1)之间的随机小数
特别注意:Math.random()生成的随机数实际上是伪随机数,如果需要加密安全的随机数,应该使用SecureRandom类。
虽然Math类很方便,但在处理浮点数运算时可能会遇到精度问题:
java复制System.out.println(0.1 + 0.2); // 输出0.30000000000000004
这种精度问题在金融计算等场景是致命的。我的经验是:
System类提供了与系统交互的接口,这几个方法特别实用:
java复制// 获取当前时间戳(毫秒)
long start = System.currentTimeMillis();
// 数组复制(比循环效率高)
int[] src = {1,2,3};
int[] dest = new int[3];
System.arraycopy(src, 0, dest, 0, src.length);
// 强制垃圾回收(只是建议,不保证立即执行)
System.gc();
// 获取系统属性
String javaVersion = System.getProperty("java.version");
String osName = System.getProperty("os.name");
性能测试小技巧:用currentTimeMillis()做简单性能测试时,要注意JVM的预热问题。更精确的基准测试应该使用JMH框架。
Runtime类是单例模式的一个经典实现,它代表了Java应用的运行时环境:
java复制Runtime runtime = Runtime.getRuntime();
// 获取处理器核心数(线程池配置时很有用)
int processors = runtime.availableProcessors();
// 内存信息(单位字节)
long totalMem = runtime.totalMemory(); // JVM总内存
long freeMem = runtime.freeMemory(); // JVM空闲内存
// 执行外部程序(谨慎使用!)
Process process = runtime.exec("notepad.exe");
Thread.sleep(5000);
process.destroy(); // 关闭程序
安全警告:exec()方法有命令注入风险,如果参数包含用户输入,必须做好过滤和校验。
先看一个经典问题:
java复制System.out.println(0.1 + 0.2); // 输出0.30000000000000004
这是因为浮点数在计算机中是以二进制形式存储的,有些十进制小数无法精确表示为二进制小数。BigDecimal通过使用十进制进行运算,解决了这个问题。
错误示范:
java复制BigDecimal d = new BigDecimal(0.1); // 仍然会有精度问题!
正确做法:
java复制// 方式1:使用字符串构造
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
// 方式2:使用valueOf方法(内部会调用toString)
BigDecimal a = BigDecimal.valueOf(0.1);
BigDecimal b = BigDecimal.valueOf(0.2);
java复制BigDecimal a = BigDecimal.valueOf(0.1);
BigDecimal b = BigDecimal.valueOf(0.3);
// 加法
BigDecimal sum = a.add(b); // 0.4
// 减法
BigDecimal difference = a.subtract(b); // -0.2
// 乘法
BigDecimal product = a.multiply(b); // 0.03
// 除法(需要指定舍入模式)
BigDecimal quotient = a.divide(b, 2, RoundingMode.HALF_UP); // 0.33
重要提示:除法必须指定舍入模式,否则如果不能精确表示时会抛出ArithmeticException
在金融系统中使用BigDecimal时,我总结了几条经验:
Date类是最基础的日期时间表示:
java复制Date now = new Date(); // 当前时间
long timestamp = now.getTime(); // 获取时间戳
// 时间戳转Date
Date date = new Date(timestamp);
// 修改时间
date.setTime(timestamp + 3600_000); // 加1小时
注意:Date的很多方法(如getYear())已经废弃,因为设计有缺陷。
SimpleDateFormat用于日期和字符串之间的转换:
java复制SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// Date转字符串
String dateStr = sdf.format(new Date());
// 字符串转Date
Date date = sdf.parse("2023-01-01 12:00:00");
线程安全问题:SimpleDateFormat不是线程安全的!在多线程环境下应该:
Calendar提供了更灵活的日期操作:
java复制Calendar cal = Calendar.getInstance();
// 获取各字段
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH) + 1; // 月份从0开始
// 修改日期
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.add(Calendar.DAY_OF_MONTH, 5); // 加5天
// Calendar和Date转换
Date date = cal.getTime();
cal.setTime(date);
月份陷阱:Calendar的月份是从0开始的(0=一月),这导致了很多bug,使用时务必小心。
Java 8引入了全新的时间API,解决了旧API的诸多问题:
java复制LocalDate date = LocalDate.now(); // 只有日期
LocalTime time = LocalTime.now(); // 只有时间
LocalDateTime dateTime = LocalDateTime.now(); // 日期+时间
// 创建指定日期
LocalDate birthday = LocalDate.of(1990, Month.JANUARY, 1);
// 日期运算(不可变对象,每次返回新实例)
LocalDate nextWeek = date.plusWeeks(1);
LocalDateTime twoHoursLater = dateTime.plusHours(2);
// 获取字段
int year = dateTime.getYear();
Month month = dateTime.getMonth(); // 枚举更安全
int hour = dateTime.getHour();
不可变优势:新API的所有类都是不可变的,天生线程安全,且每次修改都会返回新对象。
Instant表示时间线上的一个瞬时点:
java复制Instant now = Instant.now(); // 当前UTC时间
// 与LocalDateTime转换
LocalDateTime localDateTime = LocalDateTime.ofInstant(now, ZoneId.systemDefault());
// 时间戳操作
Instant later = now.plusSeconds(3600); // 加1小时
新的格式化API是线程安全的:
java复制DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 格式化
String formatted = dateTime.format(formatter);
// 解析
LocalDateTime parsed = LocalDateTime.parse("2023-01-01 12:00:00", formatter);
java复制// 获取所有可用时区
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
// 时区转换
ZonedDateTime shanghaiTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime newYorkTime = shanghaiTime.withZoneSameInstant(ZoneId.of("America/New_York"));
java复制// 计算两个日期之间的周期
Period period = Period.between(LocalDate.of(2023, 1, 1),
LocalDate.of(2023, 12, 31));
System.out.println(period.getMonths()); // 11个月
// 计算时间间隔
Duration duration = Duration.between(
LocalTime.of(8, 0),
LocalTime.of(17, 30)
);
System.out.println(duration.toHours()); // 9小时
java复制long timeout = TimeUnit.SECONDS.toMillis(30); // 比30*1000更清晰
通过本文的梳理,我们深入探讨了Java中常用的工具类API:
最后给初学者的建议:不要死记硬背API,而是理解设计原理,在实际项目中多练习。遇到问题时,学会查阅官方文档(Javadoc)和源码,这是成长为高级开发者的必经之路。