乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • 学习路径
  • 第1章 Java概述与环境搭建

    • Java概述与环境搭建
    • Java语言概述
    • 解释型语言与编译型语言对比
    • JDK安装与配置
    • JDK、JRE、JVM 详解
    • HelloWorld程序详解
    • IDE 介绍
  • 第2章 标识符与基本数据类型

    • 章节导读
    • 变量概述
    • 常量概述
    • 基本类型与包装类
    • 字节型 byte
    • 短整型 short
    • 整型 int
    • 长整型 long
    • 单精度浮点型 float
    • 双精度浮点型 double
    • 字符型 char
    • 布尔型 boolean
    • 类型转换
  • 第3章 运算符与表达式

    • 章节导读
    • 算术运算符
    • 赋值运算符
    • 关系运算符
    • 逻辑运算符
    • 位运算符
    • 条件运算符
    • 运算符优先级
    • 表达式
  • 第4章 流程控制

    • 章节导读
    • 常见的程序运行流程
    • if-else 选择结构
    • switch 多分支选择
    • while 循环
    • do-while 循环
    • for 循环
    • break 与 continue
  • 第5章 数组

    • 章节导读
    • 一维数组
    • 多维数组
    • Arrays 工具类
  • 第6章 类与对象

    • 章节导读
    • 类与对象
    • 方法定义与调用
    • 构造方法
    • 封装
    • 访问修饰符
    • package 与 import
    • static 关键字
    • this 关键字
    • 参数传递 详解
    • 枚举
    • 成员内部类
    • 局部内部类
    • 静态内部类
    • 匿名内部类
  • 第7章 接口与继承

    • 章节导读
    • 继承
    • super 关键字
    • final 关键字
    • 多态
    • 向上转型与向下转型
    • 抽象类
    • 接口
    • 抽象类与接口对比
  • 第8章 注解

    • 章节导读
    • 注解基础
    • 元注解详解
    • 自定义注解
  • 第9章 常用类

    • 章节导读:Java 常用类
    • Object 类:万类之祖
    • 包装类:基本类型的对象化
    • String:不可变的字符串
    • StringBuffer:线程安全的可变字符串
    • StringBuilder:可变的字符串构建器
    • Math:数学运算工具类
    • Random:伪随机数生成器
    • 大数值运算 详解
    • 日期时间API 详解
  • 第10章 异常机制

    • 章节导读
    • 异常体系与分类
    • try-catch-finally
    • try-with-resources
    • throws 与 throw
    • 自定义异常
  • 第11章 泛型

    • 章节导读
    • 泛型基础
    • 通配符与PECS原则
    • 类型擦除
  • 第12章 集合框架

    • 章节导读
    • 集合框架概述
    • ArrayList
    • LinkedList
    • HashMap 详解
    • LinkedHashMap 详解
    • TreeMap 详解
    • HashSet
    • TreeSet 详解
    • TreeSet 与 Comparable
    • Collections 工具类详解
  • 第13章 IO流

    • 章节导读
    • IO流概述
    • 字节流
    • 字符流
    • 缓冲流
    • 转换流 详解
    • 序列化 详解
    • NIO与Files 详解
    • NIO与Files工具类
  • 第14章 多线程与并发

    • 第十六章 多线程与并发 —— 章节导读
    • 线程基础详解
    • synchronized 详解
    • Lock 与显式锁详解
    • volatile 详解
    • wait 与 notify 详解
    • ThreadLocal详解
    • 原子类详解
    • 并发工具类详解
    • 线程池详解
  • 第15章 反射

    • 章节导读
    • 反射概述与 Class 对象
    • Constructor 与对象创建
    • Field与Method详解
    • 反射应用详解
  • 第16章 JDK8新特性

    • 章节导读
    • Lambda 表达式
    • Stream API 基础
    • Stream API 高级详解
    • Optional 详解
    • 新日期时间API详解
  • 第17章 JDK9-11新特性

    • 章节导读
    • 模块化系统 — Project Jigsaw(JDK 9)
    • var 局部变量类型推断(JDK 10)
    • 集合工厂方法与增强(JDK 9 / 10 / 11)
    • 接口增强:private 方法(JDK 9)
    • Stream API 增强(JDK 9)
    • Optional 增强(JDK 9 / 10 / 11)
    • String 新增方法(JDK 11)
    • HTTP Client 与 Files 增强(JDK 11)
    • 直接运行 Java 源文件 — JEP 330(JDK 11)
  • 第18章 JDK12-17新特性

    • 章节导读
    • Switch 表达式(JDK 12 预览 / JDK 14 正式)
    • 文本块 Text Blocks(JDK 13 预览 / JDK 15 正式)
    • Records 记录类(JDK 14 预览 / JDK 16 正式)
    • 密封类 Sealed Classes(JDK 15 预览 / JDK 17 正式)
    • instanceof 模式匹配(JDK 14 预览 / JDK 16 正式)
    • Switch 模式匹配 — Pattern Matching for switch(JDK 17 预览 / JDK 21 正式)
    • Helpful NPE 与 String 增强(JDK 12 / JDK 14 / JDK 15)
    • Stream 增强(JDK 12 / JDK 16)
    • 日期时间增强 — Day Period 支持(JDK 16)
  • 第19章 JDK18-21新特性

    • 章节导读
    • 虚拟线程(JDK 19 预览 / JDK 20 第二预览 / JDK 21 正式)
    • 序列集合(JDK 21 正式)
    • Switch 模式匹配(JDK 17 预览 / JDK 18 第二预览 / JDK 20 第四预览 / JDK 21 正式)
    • Record 模式匹配(JDK 19 预览 / JDK 20 第二预览 / JDK 21 正式)
    • 未命名模式与变量(JDK 21 预览 / JDK 22 正式)
  • 第20章 JDK 22-25 新特性

    • 章节导读
    • 字符串模板(JDK 22 预览 / JDK 23 第二预览 / JDK 24 第三预览)
    • Stream Gatherers(JDK 22 预览 / JDK 24 第二预览)
    • 隐式声明类与实例方法(JDK 23 预览 / JDK 24 第二预览)
    • 原始类型模式匹配(JDK 24 预览)
  • 附录

    • Java 核心知识点
    • Java SE 专业术语
    • Java特性索引(JDK 8 → 25)

Math:数学运算工具类

本章定位:java.lang.Math 提供了常用数学函数(绝对值、幂运算、三角函数、取整等),所有方法都是静态的,是科学计算、游戏开发、数据模拟的基础工具类。


小崔的年终奖计算

小崔在计算飞翔科技的年终奖分配时遇到了几个问题:

double bonus = 3.7;  // 3.7 个月年终奖
// 向上取整还是向下取整?
// round() 和 ceil() 有什么区别?

白歌看着他困惑的样子,在白板上画了一张取整函数对比图。


Math 类概述

Math 类位于 java.lang 包,所有方法都是 static,构造函数私有化,不允许实例化。

常量

常量值说明
Math.PI3.141592653589793圆周率 π
Math.E2.718281828459045自然常数 e

常用方法速查

方法功能示例
abs(x)绝对值Math.abs(-5) → 5
max(a, b)最大值Math.max(3, 7) → 7
min(a, b)最小值Math.min(3, 7) → 3
pow(a, b)a 的 b 次幂Math.pow(2, 3) → 8.0
sqrt(x)平方根Math.sqrt(16) → 4.0
cbrt(x)立方根Math.cbrt(27) → 3.0
round(x)四舍五入Math.round(3.5) → 4
ceil(x)向上取整Math.ceil(3.1) → 4.0
floor(x)向下取整Math.floor(3.9) → 3.0
random()[0.0, 1.0) 随机数Math.random()
sin(x) / cos(x) / tan(x)三角函数(弧度制)Math.sin(Math.PI/2) → 1.0
log(x) / log10(x)自然对数 / 常用对数Math.log(Math.E) → 1.0
exp(x)e 的 x 次幂Math.exp(1) → 2.718...
toDegrees(rad)弧度转角度Math.toDegrees(Math.PI) → 180.0
toRadians(deg)角度转弧度Math.toRadians(180) → 3.1415...

取整函数对比(重点)

这是小崔最容易混淆的地方:

函数方向正数 3.7负数 -3.7返回值类型
floor(x)向负无穷3.0-4.0double
ceil(x)向正无穷4.0-3.0double
round(x)四舍五入4-4long(float 参数返回 int)

记忆口诀:floor 是"地板"(往下走),ceil 是"天花板"(往上走),round 是"四舍五入"。


Math.random() 的底层

Math.random() 底层调用的是 java.util.Random 类:

// Math 类源码
public static double random() {
    return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}

private static final class RandomNumberGeneratorHolder {
    static final Random randomNumberGenerator = new Random();
}

所以 Math.random() 本质上等价于 new Random().nextDouble(),返回 [0.0, 1.0) 之间的 double。


完整示例一:年终奖计算与取整

场景

小崔需要根据员工绩效系数计算实际发放的年终奖月数。

代码

public class BonusCalculator {
    public static void main(String[] args) {
        // 各部门绩效系数
        double devCoeff = 3.7;    // 技术部:3.7 个月
        double salesCoeff = 4.2;  // 销售部:4.2 个月
        double hrCoeff = 2.3;     // 人事部:2.3 个月

        System.out.println("===== 飞翔科技年终奖计算 =====\n");

        // ----- 技术部的年终奖 -----
        System.out.println("技术部基础系数: " + devCoeff);
        System.out.println("  floor (向下取整): " + Math.floor(devCoeff) + " 个月");
        System.out.println("  ceil  (向上取整): " + Math.ceil(devCoeff) + " 个月");
        System.out.println("  round (四舍五入): " + Math.round(devCoeff) + " 个月");
        // 实际发放取 floor,不满整月不发
        System.out.println("  实际发放: " + (int) Math.floor(devCoeff) + " 个月");

        System.out.println();

        // ----- 销售部的年终奖(特殊规则:四舍五入) -----
        System.out.println("销售部基础系数: " + salesCoeff);
        long salesBonus = Math.round(salesCoeff);
        System.out.println("  四舍五入后: " + salesBonus + " 个月");

        System.out.println();

        // ----- 计算年终奖金额 -----
        int baseSalary = 15000;  // 月薪
        double bonusAmount = baseSalary * devCoeff;
        System.out.println("技术部年终奖总额: " + String.format("%,.2f", bonusAmount) + " 元");

        // ----- 幂运算:公司利润增长预测 -----
        double annualGrowth = 1.15;  // 年增长率 15%
        double fiveYearMultiplier = Math.pow(annualGrowth, 5);
        System.out.println("5年后利润倍数: " + String.format("%.4f", fiveYearMultiplier));

        // ----- 平方根:办公室面积规划 -----
        double officeArea = 10000;  // 办公室面积 10000 m²
        double side = Math.sqrt(officeArea);
        System.out.println("正方形办公室边长: " + String.format("%.2f", side) + " m");

        // ----- 三角函数:公司Logo设计中的圆形计算 -----
        double radius = 50;
        double circumference = 2 * Math.PI * radius;
        double area = Math.PI * radius * radius;
        System.out.println("Logo 圆周长: " + String.format("%.2f", circumference) + " mm");
        System.out.println("Logo 圆面积: " + String.format("%.2f", area) + " mm²");
    }
}

运行输出

===== 飞翔科技年终奖计算 =====

技术部基础系数: 3.7
  floor (向下取整): 3.0 个月
  ceil  (向上取整): 4.0 个月
  round (四舍五入): 4 个月
  实际发放: 3 个月

销售部基础系数: 4.2
  四舍五入后: 4 个月

技术部年终奖总额: 55,500.00 元
5年后利润倍数: 2.0114
正方形办公室边长: 100.00 m
Logo 圆周长: 314.16 mm
Logo 圆面积: 7,853.98 mm²

完整示例二:三角函数与对数运算

场景

白歌在飞翔科技内部技术分享中,用 Math 函数演示了信号波形生成和数据压缩中的对数变换。

代码

public class MathAdvancedDemo {
    public static void main(String[] args) {
        System.out.println("===== 飞翔科技 数学函数高级应用 =====\n");

        // ========== 1. 三角函数:模拟正弦波采样 ==========
        System.out.println("--- 正弦波 10 点采样(0° ~ 360°) ---");
        for (int i = 0; i <= 360; i += 36) {
            double radian = Math.toRadians(i);          // 角度 → 弧度
            double sinValue = Math.sin(radian);
            // 用星号可视化波形
            int stars = (int) ((sinValue + 1) * 20);     // 映射到 [0, 40]
            StringBuilder bar = new StringBuilder();
            for (int j = 0; j < stars; j++) {
                bar.append("*");
            }
            System.out.printf("%3d° | sin=%.4f | %s%n", i, sinValue, bar);
        }

        // ========== 2. 对数运算:数据压缩比例 ==========
        System.out.println("\n--- 对数变换(Web 访问量压缩) ---");
        long[] pageViews = {10, 100, 1000, 10000, 100000, 1000000};
        System.out.println("原始值 → log10 → 归一化");
        for (long pv : pageViews) {
            double logVal = Math.log10(pv);
            // 归一化到 [0, 1](假设最大 10^6)
            double normalized = logVal / 6.0;
            System.out.printf("  %,8d → %.4f → %.4f%n", pv, logVal, normalized);
        }

        // ========== 3. 指数与自然对数 ==========
        System.out.println("\n--- 自然指数验证 ---");
        double x = 2.0;
        double expX = Math.exp(x);           // e^2
        double logExpX = Math.log(expX);     // ln(e^2) = 2
        System.out.println("Math.exp(2.0)     = " + String.format("%.6f", expX));
        System.out.println("Math.log(exp(2))  = " + String.format("%.6f", logExpX));
        System.out.println("e 的精确值验证:    " + (Math.abs(logExpX - x) < 1e-10));

        // ========== 4. 极值运算:多维度对比 ==========
        System.out.println("\n--- 极值运算 ---");
        double a = 3.14, b = -2.71, c = 1.41;
        double maxOfThree = Math.max(Math.max(a, b), c);
        double minOfThree = Math.min(Math.min(a, b), c);
        System.out.println("max(3.14, -2.71, 1.41) = " + maxOfThree);
        System.out.println("min(3.14, -2.71, 1.41) = " + minOfThree);

        // ========== 5. 立方根:体积反算边长 ==========
        System.out.println("\n--- 立方根:仓库体积反算 ---");
        double warehouseVolume = 3375;  // 立方米
        double sideLength = Math.cbrt(warehouseVolume);
        System.out.println("体积 " + warehouseVolume + " m³ 的正方体仓库,边长 = "
                + String.format("%.2f", sideLength) + " m");
    }
}

运行输出

===== 飞翔科技 数学函数高级应用 =====

--- 正弦波 10 点采样(0° ~ 360°) ---
  0° | sin=0.0000 | 
 36° | sin=0.5878 | *******************************
 72° | sin=0.9511 | ***************************************
108° | sin=0.9511 | ***************************************
144° | sin=0.5878 | *******************************
180° | sin=0.0000 | 
216° | sin=-0.5878 | ********
252° | sin=-0.9511 | *
288° | sin=-0.9511 | *
324° | sin=-0.5878 | ********
360° | sin=-0.0000 | 

--- 对数变换(Web 访问量压缩) ---
原始值 → log10 → 归一化
        10 → 1.0000 → 0.1667
       100 → 2.0000 → 0.3333
     1,000 → 3.0000 → 0.5000
    10,000 → 4.0000 → 0.6667
   100,000 → 5.0000 → 0.8333
 1,000,000 → 6.0000 → 1.0000

--- 自然指数验证 ---
Math.exp(2.0)     = 7.389056
Math.log(exp(2))  = 2.000000
e 的精确值验证:    true

--- 极值运算 ---
max(3.14, -2.71, 1.41) = 3.14
min(3.14, -2.71, 1.41) = -2.71

--- 立方根:仓库体积反算 ---
体积 3375.0 m³ 的正方体仓库,边长 = 15.00 m

易错场景

反例一:Math.round() 的负数陷阱

// ❌ 可能误解:以为 round 总是向正无穷方向取整
System.out.println(Math.round(-3.5));  // 输出 -3,不是 -4!

Math.round() 的规则是:加上 0.5 后向下取整。-3.5 + 0.5 = -3.0,floor 后是 -3。

// ✅ 纠正:理解 round 的实现
// round(x) = floor(x + 0.5)
// -3.5 + 0.5 = -3.0 → floor(-3.0) = -3
//  3.5 + 0.5 =  4.0 → floor(4.0)  =  4

反例二:Math.abs(Integer.MIN_VALUE)

// ❌ 惊悚事实:Math.abs(Integer.MIN_VALUE) 返回负数!
System.out.println(Math.abs(Integer.MIN_VALUE));  // -2147483648,仍然是负数!

原因:Integer.MIN_VALUE = -2147483648,它的绝对值 2147483648 超出了 int 最大值 2147483647,取反后溢出又回到自身。

// ✅ 纠正:对于 int 最小值,用 long 处理
int min = Integer.MIN_VALUE;
long absMin = Math.abs((long) min);  // 2147483648,正确!

反例三:Math.random() 在并发场景下的问题

// ❌ Math.random() 底层是全局单例 Random,高并发下有锁竞争
for (int i = 0; i < 1000000; i++) {
    double r = Math.random();  // 底层 synchronized
}

纠正:

// ✅ 高并发场景使用 ThreadLocalRandom(JDK 7+)
import java.util.concurrent.ThreadLocalRandom;
for (int i = 0; i < 1000000; i++) {
    int r = ThreadLocalRandom.current().nextInt(100);  // 无锁
}

面试考点

Q1:Math.round(-1.5) 的结果是多少?为什么?

结果是 -1,不是 -2。round() 的实现是 floor(x + 0.5)。-1.5 + 0.5 = -1.0,floor(-1.0) = -1.0,转为 long 就是 -1。同理 Math.round(-1.6) = floor(-1.1) = -2。面试官会特别关注这个负数边缘情况。

Q2:Math.random() 和 new Random().nextDouble() 有什么区别?

没有本质区别——Math.random() 底层就是 Random.nextDouble()。区别在于 Math.random() 使用的是全局单例 Random 对象,无法指定种子;而 new Random(long seed) 可以指定种子以获得可复现的随机序列。

Q3:Math.abs(Integer.MIN_VALUE) 返回什么?为什么?

返回 Integer.MIN_VALUE 本身(-2147483648),仍然是负数。因为它的绝对值 2147483648 超出了 int 的表达范围(最大值 2147483647),取反溢出绕回自身。这是 Java 中一个著名的边缘案例,解决方法是先强转为 long 再取绝对值。

Q4:floor、ceil、round 三者对负数的处理有何不同?

floor 向负无穷方向取整(-3.7 → -4.0);ceil 向正无穷方向取整(-3.7 → -3.0);round 四舍五入(-3.7 → -4)。关键记忆点:对于负数,floor 和 round 可能结果相同(都是更小的整数),但 ceil 得到更大的整数。例如 -3.2:floor=-4.0,ceil=-3.0,round=-3。

Q5:为什么高并发场景不推荐 Math.random()?

Math.random() 底层调用 java.util.Random,其 next() 方法使用 CAS 操作保证线程安全,高并发下多个线程竞争同一个 AtomicLong seed,性能下降明显。JDK 7 引入的 ThreadLocalRandom 为每个线程维护独立的种子,无竞争,性能更高。


大翔在部门例会上说:"数学是计算机科学的基石。不过别担心,你不需要记住所有的公式——只要知道 Math 类提供了什么,用的时候查 Javadoc 就够了。但取整函数和随机数的边界值,必须刻在脑子里。"

上一页
StringBuilder:可变的字符串构建器
下一页
Random:伪随机数生成器