java.math 包应用

本文介绍了java.math包的使用,包括Math类中的常量和数学函数,如PI、E、绝对值、三角函数等,以及BigInteger和BigDecimal两大类大数值操作,如加减乘除、取随机数等。

java.math 包应用

java.math包应用

java.math.Math类常用的常量和方法:

Math.PI 记录的圆周率
Math.E记录e的常量
Math.abs 求绝对值
Math.sin 正弦函数 Math.asin 反正弦函数
Math.cos 余弦函数 Math.acos 反余弦函数
Math.tan 正切函数 Math.atan 反正切函数 Math.atan2 商的反正切函数
Math.toDegrees 弧度转化为角度 Math.toRadians 角度转化为弧度
Math.ceil 得到不小于某数的最大整数
Math.floor 得到不大于某数的最大整数
Math.IEEEremainder 求余
Math.max 求两数中最大
Math.min 求两数中最小
Math.sqrt 求开方
Math.pow 求某数的任意次方, 抛出ArithmeticException处理溢出异常
Math.exp 求e的任意次方
Math.log10 以10为底的对数
Math.log 自然对数
Math.rint 求距离某数最近的整数(可能比某数大,也可能比它小)
Math.round 同上,返回int型或者long型(上一个函数返回double型)
Math.random 返回0,1之间的一个随机数

java.math.BigInteger(大整数):
BigInteger bi1=new BigInteger("1234567890123456890");
BigInteger bi2=BigInteger.valueOf(123L);
bi1=bi1.add(bi2);//b1+b2
bi1=bi1.multiply(bi2);//b1*b2
bi1=bi1.subtract(bi2);//b1-b2
bi1=bi1.divide(bi2);// b1/b2

java.math.BigDecimal(大浮点数):
BigDecimal bd = new BigDecimal("3.1415926");
bd = bd.setScale(2,BigDecimal.ROUND_DOWN);//取3.1415926小数点后面二位

<think>我们遇到了一个在Redis反序列化过程中关于BigDecimal字段的访问权限错误: ``` Unable to make field private final java.math.BigInteger java.math.BigDecimal.intVal accessible ``` 这个错误与之前遇到的`String.value`和`File.path`字段的问题类似,都是由于Java 9及以上版本的模块系统加强了访问控制,阻止了通过反射访问JDK内部类的私有字段。 ### 错误原因 在反序列化`BigDecimal`对象时,序列化库(如Jackson或Redis的序列化器)试图通过反射直接访问`BigDecimal`的私有字段`intVal`(类型为`BigInteger`)。但是,`java.math`在`java.base`模块中默认没有开放给未命名模块(即我们的应用程序)进行深度反射访问(括访问私有字段)[^1][^3]。 ### 解决方案 #### 方案1:添加JVM启动参数(推荐) 在启动应用程序时,添加以下参数来开放`java.math`: ```bash --add-opens java.base/java.math=ALL-UNNAMED ``` 如果是Spring Boot项目,可以在启动命令中直接添加: ```bash java --add-opens java.base/java.math=ALL-UNNAMED -jar your-application.jar ``` 或者在IDE的运行配置中添加VM参数。 #### 方案2:修改模块配置(适用于模块化项目) 如果您的项目是模块化的(有`module-info.java`文件),可以在其中添加: ```java module your.module.name { // 开放java.math给序列化框架(根据实际框架调整) opens java.math to com.fasterxml.jackson.databind, org.springframework.core; } ``` 注意:请根据实际使用的序列化框架替换名(如`com.fasterxml.jackson.databind`)。 #### 方案3:自定义序列化器(避免反射访问) 为`BigDecimal`类型编写自定义的序列化和反序列化器,避免直接访问私有字段。例如,使用Jackson时: ```java public class BigDecimalSerializer extends JsonSerializer<BigDecimal> { @Override public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeString(value.toString()); // 转换为字符串进行序列化 } } public class BigDecimalDeserializer extends JsonDeserializer<BigDecimal> { @Override public BigDecimal deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { String text = p.getText(); // 从字符串反序列化 return new BigDecimal(text); } } ``` 然后在需要使用的地方注册: ```java @JsonSerialize(using = BigDecimalSerializer.class) @JsonDeserialize(using = BigDecimalDeserializer.class) private BigDecimal amount; ``` 或者在ObjectMapper中全局注册: ```java ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addSerializer(BigDecimal.class, new BigDecimalSerializer()); module.addDeserializer(BigDecimal.class, new BigDecimalDeserializer()); mapper.registerModule(module); ``` #### 方案4:使用其他序列化方式 考虑使用其他不会涉及访问私有字段的序列化方式,例如: - 使用基于getter/setter的序列化(确保BigDecimal类有公开的访问方法) - 切换到其他序列化库,如Gson(但同样可能遇到类似问题,需要配置) #### 方案5:降级JDK(不推荐) 如引用[3]中提到,降级到Java 8可以避免此问题,因为Java 8没有模块系统。但这是临时解决方案,长期来看应适配更高版本的JDK。 ### 根本原因 Java模块系统(JPMS)在Java 9引入,它限制了反射访问。默认情况下,核心JDK模块(如`java.base`)不向外部模块开放其内部的深度反射访问。因此,当序列化库试图通过反射访问`BigDecimal.intVal`时,会被模块系统阻止[^1][^3]。 > **建议**:优先使用方案1(JVM参数)快速解决问题,然后考虑方案3(自定义序列化)作为长期解决方案,因为它更符合模块化要求。 §§相关问题§§ 1. 如何在Spring Boot中全局配置`--add-opens`参数? 2. 如何为多个JDK模块(如`java.io`、`java.math`)同时配置开放访问? 3. 自定义序列化器在分布式系统中如何保证一致性? 4. 除了BigDecimal,还有哪些Java类型在序列化时容易遇到模块访问问题? [^1]: 引用自用户提供的类似错误信息 [^3]: 引用中提到的JDK版本过高问题及降级解决方案(不推荐)</think>### 解决 Redis 反序列化 BigDecimal 字段的访问权限错误 您遇到的错误: ``` Unable to make field private final java.math.BigInteger java.math.BigDecimal.intVal accessible ``` 是由于 Java 模块系统阻止了 Redis 序列化器通过反射访问 `BigDecimal` 的私有字段 `intVal`。这与之前遇到的 `String.value` 和 `File.path` 问题本质相同[^1][^3]。 #### 解决方案(按优先级排序) 1. **添加 JVM 启动参数(推荐)** 在启动命令中开放 `java.math` : ```bash --add-opens java.base/java.math=ALL-UNNAMED ``` Spring Boot 项目可在 `application.properties` 中添加: ```properties spring.main.lazy-initialization=true # 减少启动时反射 ``` 2. **自定义 Redis 序列化器** 避免直接访问私有字段: ```java public class BigDecimalRedisSerializer implements RedisSerializer<BigDecimal> { private final Charset charset = StandardCharsets.UTF_8; @Override public byte[] serialize(BigDecimal value) { return (value != null) ? value.toString().getBytes(charset) : null; } @Override public BigDecimal deserialize(byte[] bytes) { return (bytes != null) ? new BigDecimal(new String(bytes, charset)) : null; } } ``` 配置到 RedisTemplate: ```java @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); template.setValueSerializer(new BigDecimalRedisSerializer()); return template; } ``` 3. **配置模块访问(Java 9+ 模块化项目)** 在 `module-info.java` 中添加: ```java module your.module { opens java.math to com.fasterxml.jackson.databind, org.springframework.core; } ``` 4. **升级依赖库** 确保使用兼容 Java 模块系统的最新库: ```xml <!-- Spring Boot 3.x+ 和 Jackson 2.15+ --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>3.1.0</version> </dependency> ``` 5. **降级 JDK(临时方案)** 如引用[3]所述,降级到 Java 8 可规避问题,但**不推荐**作为长期方案[^3]。 #### 错误原因 - Java 9+ 的模块系统默认禁止反射访问 `java.math` 等基础的私有字段 - `BigDecimal` 内部使用 `BigInteger intVal` 存储值,反序列化时需访问此字段 - Redis 的默认序列化器尝试通过反射直接访问私有字段触发模块系统保护 > **最佳实践**:优先采用自定义序列化器方案,它彻底避免反射访问且性能更优(直接使用字符串表示)。Spring Boot 项目中推荐结合方案1+2使用。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值