前言
在Java项目中Long是很常用的类型,通常用来做ID字段,近期的分布式雪花ID也是可以使用Long类型的,今天来看一下Long类型在项目中的坑。
最近在项目中偶然看到所有Long类型的字段返回前端之后都变成了字符串,导致前端需要进行一次转换才能使用。当时很不解,后来找到了项目中之前架构师写的转换代码,所有的Long类型和BigInteger都转换成String返回给前端,当时就蒙了,满脑子都是问号??????为啥?
为什么要转换?
这个要聊到Long类型的最大承受数值,众所周知long 数据类型是 64 位、有符号的以二进制补码表示的整数 ,取值范围是−263~263−1
我们可以使用以下代码查看Long类型的最大和最小值
1 2
| System.out.println(Long.MIN_VALUE); System.out.println(Long.MAX_VALUE);
|
现在我们需要了解以下前端数字类型能承受的最大数值了。
在我们小学二年级学到过,JavaScript中的数字都是Number类型,所有数字都是采用IEEE 754 标准定义的双精度64位格式存储,即使整数也是如此这就是说,JavaScript 语言的底层根本没有整数,所有数字都是小数(64位浮点数)。其结构如图:

各位的含义如下:
- 1位(s) 用来表示符号位,0表示正数,1表示负数
- 11位(e) 用来表示指数部分
- 52位(f) 表示小数部分(即有效数字)
双精度浮点数(double
)并不是能够精确表示范围内的所有数, 虽然双精度浮点型的范围看上去很大: 2.23×10−308 ~ 1.79×10308。 可以表示的最大整数可以很大,但能够精确表示、使用算数运算的并没有这么大。因为小数部分最大是 52位,因此 JavaScript 中能精准表示的最大整数是 253−1,十进制为 9007199254740991
。所以JavaScript中存在一个概念安全整数
取值范围为 −253 ~ 253 。而超过这个范围,会有两个或更多整数的双精度表示是相同的;即超过这个范围,有的整数是无法精确表示的,只能大约(round)到与它相近的浮点数(说到底就是科学计数法)表示,这种情况下叫做不安全整数
,例如:
1 2 3 4 5
| console.log(Number.MAX_SAFE_INTEGER + 1); console.log(Number.MAX_SAFE_INTEGER + 2); console.log(Number.MAX_SAFE_INTEGER + 3); console.log(Number.MAX_SAFE_INTEGER + 4); console.log(Number.MAX_SAFE_INTEGER + 5);
|
总结:
- Java中Long类型的数值范围为−263~263−1
- JavaScript中的Number类型安全整数范围为 −253 ~ 253 ,超过这个范围数字将可能会丢失。
- 如果不做处理,当Long类型数字超过Number的安全整数范围后,数字可能会变化,导致跟原数据不一样。
有什么解决方法呢?
注解
1 2
| @JsonSerialize(using=ToStringSerializer.class) private Long bankcardHash;
|
使用HttpMessageConverter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
|
@Configuration public class WebConfig extends WebMvcConfigurationSupport { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); builder.serializationInclusion(JsonInclude.Include.NON_NULL); ObjectMapper objectMapper = builder.build(); SimpleModule simpleModule = new SimpleModule(); simpleModule.addSerializer(Long.class, ToStringSerializer.instance); objectMapper.registerModule(simpleModule); objectMapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8")); objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); converters.add(new MappingJackson2HttpMessageConverter(objectMapper)); converters.add(new StringHttpMessageConverter(Charsets.UTF_8)); converters.add(new ByteArrayHttpMessageConverter()); converters.add(new ResourceHttpMessageConverter()); converters.add(new ResourceRegionHttpMessageConverter()); super.configureMessageConverters(converters); }
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("swagger-ui.html") .addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/"); }
@Override public void addInterceptors(InterceptorRegistry registry) { }
@Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowCredentials(true) .allowedHeaders("*") .allowedOrigins("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .maxAge(3600); } }
|
总结
因为Java中Long能承受的整数范围比JavaScript Number类型的安全整数范围大,所以可能会导致数字不准确。