Java基础API探索:数值与字符串处理
立即解锁
发布时间: 2025-08-19 00:11:27 阅读量: 10 订阅数: 21 


Java编程基础与Android开发入门
# Java基础API探索:数值与字符串处理
## 1. 严格浮点计算与溢出问题
在进行中间计算时,如果用32/64位表示数值,可能会出现溢出或下溢的情况,但用更多位表示时可能就不会。这种差异会影响程序的可移植性。`strictfp` 关键字可以解决这个问题,它要求所有平台在中间计算时都使用32/64位。
`strictfp` 可以应用于方法,确保该方法中的所有浮点计算都严格符合要求;也可以用于类头声明,确保类中的所有浮点计算都严格执行。例如:
```java
public strictfp class FourierTransform {
// 类中的浮点计算将严格执行
}
```
需要注意的是,`Math` 和 `StrictMath` 类被声明为 `final`,不能被扩展,并且它们声明了私有的无参构造函数,不能被实例化。它们是工具类,用于存储实用常量和静态方法。
## 2. BigDecimal类的使用
### 2.1 避免使用浮点类型表示货币
在之前介绍的 `SavingsAccount` 类中,余额字段使用 `int` 类型,它可以记录账户中的美元数,也可以表示美分数。可能有人会想为什么不使用 `double` 或 `float` 类型,这样可以存储像 `18.26` 这样的值。但不使用它们的原因有两个:
- 并非所有能表示货币金额的浮点值都能在内存中精确存储。例如,`0.1` 没有精确的存储表示。以下代码:
```java
double total = 0.1;
for (int i = 0; i < 50; i++) {
total += 0.1;
}
System.out.println(total);
```
输出结果是 `5.099999999999998`,而不是正确的 `5.1`。
- 每次浮点计算的结果都需要四舍五入到最接近的美分,否则会引入微小误差,导致最终结果与正确结果不同。虽然 `Math` 类提供了 `round()` 方法,但它们是四舍五入到最接近的整数(美元)。
下面的 `InvoiceCalc` 应用程序展示了这两个问题:
```java
import java.text.NumberFormat;
public class InvoiceCalc {
final static double DISCOUNT_PERCENT = 0.1; // 10%
final static double TAX_PERCENT = 0.05; // 5%
public static void main(String[] args) {
double invoiceSubtotal = 285.36;
double discount = invoiceSubtotal * DISCOUNT_PERCENT;
double subtotalBeforeTax = invoiceSubtotal - discount;
double salesTax = subtotalBeforeTax * TAX_PERCENT;
double invoiceTotal = subtotalBeforeTax + salesTax;
NumberFormat currencyFormat = NumberFormat.getCurrencyInstance();
System.out.println("Subtotal: " + currencyFormat.format(invoiceSubtotal));
System.out.println("Discount: " + currencyFormat.format(discount));
System.out.println("SubTotal after discount: " + currencyFormat.format(subtotalBeforeTax));
System.out.println("Sales Tax: " + currencyFormat.format(salesTax));
System.out.println("Total: " + currencyFormat.format(invoiceTotal));
}
}
```
运行该程序,输出结果如下:
```
Subtotal: $285.36
Discount: $28.54
SubTotal after discount: $256.82
Sales Tax: $12.84
Total: $269.67
```
虽然正确显示了小计、折扣、折扣后的小计和销售税,但最终总价显示为 `269.67`,而正确值应该是 `269.66`。问题在于每次计算后没有将结果四舍五入到最接近的美分。
### 2.2 使用BigDecimal解决问题
Java 提供了 `java.math.BigDecimal` 类来解决上述问题。这是一个不可变类,用于表示任意精度的有符号十进制数,并带有一个关联的比例(指定小数点后的位数)。
`BigDecimal` 类声明了三个便利常量:`ONE`、`TEN` 和 `ZERO`,分别对应 `1`、`10` 和 `0`,比例为 `0`。同时,要避免使用 `ROUND_` 前缀的常量以及 `divide(BigDecimal divisor, int scale, int roundingMode)` 和 `setScale(int newScale, int roundingMode)` 方法,因为它们大多已过时。
以下是 `BigDecimal` 类的一些常用构造函数和方法:
| 方法 | 描述 |
| ---- | ---- |
| `BigDecimal(int val)` | 初始化 `BigDecimal` 实例为 `val` 的数字,比例设置为 `0`。 |
| `BigDecimal(String val)` | 初始化 `BigDecimal` 实例为 `val` 的十进制等效值,比例设置为小数点后的位数,若未指定小数点则为 `0`。当 `val` 为 `null` 时抛出 `NullPointerException`,当 `val` 的字符串表示无效时抛出 `NumberFormatException`。 |
| `BigDecimal abs()` | 返回一个新的 `BigDecimal` 实例,包含当前实例值的绝对值,结果的比例与当前实例相同。 |
| `BigDecimal add(BigDecimal augend)` | 返回一个新的 `BigDecimal` 实例,包含当前值与参数值的和,结果的比例为当前和参数比例的最大值。当 `augend` 为 `null` 时抛出 `NullPointerException`。 |
| `BigDecimal divide(BigDecimal divisor)` | 返回一个新的 `BigDecimal` 实例,包含当前值除以参数值的商,结果的比例为当前和参数比例的差值,可能会根据结果需要进行调整。当 `divisor` 为 `null` 或表示 `0` 或结果无法精确表示时抛出异常。 |
| `BigDecimal max(BigDecimal val)` | 返回 `this` 或 `val` 中值较大的 `BigDecimal` 实例。当 `val` 为 `null` 时抛出 `NullPointerException`。 |
| `BigDecimal min(BigDecimal val)` | 返回 `this` 或 `val` 中值较小的 `BigDecimal` 实例。当 `val` 为 `null` 时抛出 `NullPointerException`。 |
| `BigDecimal multiply(BigDecimal multiplicand)` | 返回一个新的 `BigDecimal` 实例,包含当前值与参数值的乘积,结果的比例为当前和参数比例的和。当 `multiplicand` 为 `null` 时抛出 `NullPointerException`。 |
| `BigDecimal negate()` | 返回一个新的 `BigDecimal` 实例,包含当前值的负值,结果的比例与当前比例相同。 |
| `int precision()` | 返回当前 `BigDecimal` 实例的精度。 |
| `BigDecimal remainder(BigDecimal divisor)` | 返回一个新的 `BigDecimal` 实例,包含当前值除以参数值的余数,结果的比例为当前和参数比例的差值,可能会根据结果需要进行调整。当 `divisor` 为 `null` 或表示 `0` 时抛出异常。 |
| `int scale()` | 返回当前 `BigDecimal` 实例的比例。 |
| `BigDecimal setScale(int newScale, RoundingMode roundingMode)` | 返回一个具有指定比例和舍入模式的新 `BigDecimal` 实例。当 `roundingMode` 为 `null` 或设置为 `RoundingMode.ROUND_UNNECESSARY` 但需要舍入时抛出异常。 |
| `BigDecimal subtract(BigDecimal subtrahend)` | 返回一个新的 `BigDecimal` 实例,包含当前值减去参数值的结果,结果的比例为当前和参数比例的最大值。当 `subtrahend` 为 `null` 时抛出 `NullPointerException`。 |
| `String toString()` | 返回此 `BigDecimal` 实例的字符串表示,必要时使用科学记数法。 |
`BigDecimal` 的舍入模式由 `java.math.RoundingMode` 枚举定义,常见的舍入模式如下:
| 常量 | 描述 |
| ---- | ---- |
| `CEILING` | 向正无穷大舍入。 |
| `DOWN` | 向零舍入。 |
| `FLOOR` | 向负无穷大舍入。 |
| `HALF_DOWN` | 向“最近邻”舍入,除非两个邻居距离相等,此时向下舍入。 |
| `HALF_EVEN` | 向“最近邻”舍入,除非两个邻居距离相等,此时向偶数邻居舍入。 |
| `HALF_UP` | 向“最近邻”舍入,除非两个邻居距离相等,此时向上舍入(这是学校常用的舍入模式)。 |
| `UNNECESSARY` | 不需要舍入,因为请求的操作产生精确结果。 |
| `UP` | 正值向正无穷大舍入,负值向负无穷大舍入。 |
以下是使用 `BigDecimal` 类正确执行发票计算的代码:
```java
import java.math.BigDecimal;
import java.math.RoundingMode;
public class InvoiceCalc {
public static void main(String[] args) {
BigDecimal invoiceSubtotal = new BigDecimal("285.36");
BigDecimal discountPercent = new BigDecimal("0.10");
BigDecimal discount = invoiceSubtotal.multiply(discountPercent);
discount = discount.setScale(2, RoundingMode.HALF_UP);
BigDecimal subtotalBeforeTax = invoiceSubtotal.subtract(discount);
subtotalBeforeTax = subtotalBeforeTax.setScale(2, RoundingMode.HALF_UP);
BigDecimal salesTaxPercent = new BigDecimal("0.05");
BigDecimal salesTax = subtotalBeforeTax.multiply(salesTaxPercent);
salesTax = salesTax.setScale(2, RoundingMode.HALF_UP);
BigDecimal invoiceTotal = subtotalBeforeTax.add(salesTax);
```
0
0
复制全文
相关推荐










