优化:将各分类菜单聚合到一个导航菜单下
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
---
|
||||
title: JavaScript 无法存储 Java Long 类型数据问题
|
||||
author: 查尔斯
|
||||
date: 2022/01/26 09:07
|
||||
categories:
|
||||
- Bug万象集
|
||||
tags:
|
||||
- JavaScript
|
||||
---
|
||||
|
||||
# JavaScript 无法存储 Java Long 类型数据问题
|
||||
|
||||
## 问题描述
|
||||
|
||||
今天在解决一个需求问题的时候,遇到了一个难得一见的 JS 问题。这个问题大概是和一些同学在开发环境使用 == 来比较包装类型的整数一样,由于比较的数值在缓存范围内,因缘际会的错过了 bug 的出现。
|
||||
|
||||
简单说一下问题经过,是这样的,笔者这个需求最终要求接口返回一组自定义结构的 k-v (不是单纯的键值对)数据,用于在前端表单中进行分类展示。
|
||||
|
||||
后端响应的数据结构类似如下:
|
||||
|
||||
```json {11,16,29,34}
|
||||
{
|
||||
"code": 200,
|
||||
"errorMsg": "",
|
||||
"result": [
|
||||
{
|
||||
"extend": null,
|
||||
"items": [
|
||||
{
|
||||
"extend": null,
|
||||
"key": "CentOS 8.1 64位",
|
||||
"val": 8014753905961037835
|
||||
},
|
||||
{
|
||||
"extend": null,
|
||||
"key": "CentOS 8.0 64位",
|
||||
"val": 8014753905961037838
|
||||
},
|
||||
],
|
||||
"key": "CentOS",
|
||||
"pubProperty": "",
|
||||
"val": 14
|
||||
},
|
||||
{
|
||||
"extend": null,
|
||||
"items": [
|
||||
{
|
||||
"extend": null,
|
||||
"key": "RedHat Enterprise Linux 8.0 64位",
|
||||
"val": 7979917486755315712
|
||||
},
|
||||
{
|
||||
"extend": null,
|
||||
"key": "RedHat Enterprise Linux 7.7 64位",
|
||||
"val": 8014753905961037829
|
||||
}
|
||||
],
|
||||
"key": "Redhat",
|
||||
"pubProperty": "",
|
||||
"val": 5
|
||||
}
|
||||
],
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
在前端表单中的展示效果大概如下:
|
||||
|
||||

|
||||
|
||||
## 原因分析
|
||||
|
||||
笔者相信各位同学都应该猜得到,当提交表单的时候,前端肯定会把选中的镜像的 val 值传递给后端,然后由后端继续进行处理。
|
||||
|
||||
但是就在这个环节,由前端传给后端的 val 值竟然错了,例如:当选中了 CentOS 8.1 64 位这个镜像时,本该传递的 val 值为 8014753905961037835,实际传递的却是: 8014753905961038000。
|
||||
|
||||
后端接口测试的响应数据没问题,那问题显然就是出在前端了。
|
||||
|
||||
最终,配合前端开发定位这个问题的原因是因为: JavaScript 中无法存储 Java 中的 Long 类型数据,当位数超过 JavaScript 整数存储的范围,就会以0来代替了。
|
||||
|
||||

|
||||
|
||||
## 解决方案
|
||||
|
||||
JavaScript 存储不了就存储不了吧,咱们这个需求还得解决啊,最终的解决方法就是将后端响应回来的 Long 类型数据转换为字符串。
|
||||
|
||||
```java {2}
|
||||
// 在序列化为 JSON 时将该字段转换为 String 类型
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||
private Long val;
|
||||
```
|
||||
|
||||
后端响应的数据结构类似如下:
|
||||
|
||||
```json {11,16,29,34}
|
||||
{
|
||||
"code": 200,
|
||||
"errorMsg": "",
|
||||
"result": [
|
||||
{
|
||||
"extend": null,
|
||||
"items": [
|
||||
{
|
||||
"extend": null,
|
||||
"key": "CentOS 8.1 64位",
|
||||
"val": "8014753905961037835"
|
||||
},
|
||||
{
|
||||
"extend": null,
|
||||
"key": "CentOS 8.0 64位",
|
||||
"val": "8014753905961037838"
|
||||
},
|
||||
],
|
||||
"key": "CentOS",
|
||||
"pubProperty": "",
|
||||
"val": 14
|
||||
},
|
||||
{
|
||||
"extend": null,
|
||||
"items": [
|
||||
{
|
||||
"extend": null,
|
||||
"key": "RedHat Enterprise Linux 8.0 64位",
|
||||
"val": "7979917486755315712"
|
||||
},
|
||||
{
|
||||
"extend": null,
|
||||
"key": "RedHat Enterprise Linux 7.7 64位",
|
||||
"val": "8014753905961037829"
|
||||
}
|
||||
],
|
||||
"key": "Redhat",
|
||||
"pubProperty": "",
|
||||
"val": 5
|
||||
}
|
||||
],
|
||||
"success": true
|
||||
}
|
||||
```
|
193
repos/categories/issues/2022/03/24.创建一个自身类的静态对象变量,究竟会如何执行?.md
Normal file
193
repos/categories/issues/2022/03/24.创建一个自身类的静态对象变量,究竟会如何执行?.md
Normal file
@@ -0,0 +1,193 @@
|
||||
---
|
||||
title: 创建一个自身类的静态对象变量,究竟会如何执行?
|
||||
author: 查尔斯
|
||||
date: 2022/03/24 21:30
|
||||
categories:
|
||||
- Bug万象集
|
||||
tags:
|
||||
- Java
|
||||
- JVM
|
||||
---
|
||||
|
||||
# 创建一个自身类的静态对象变量,究竟会如何执行?
|
||||
|
||||
## 前言
|
||||
|
||||
**C:** 近两周在疯狂给项目组面试招聘,昨天晚上10点多,产品总监在面试群里发了一道题,问运行结果是什么,题目如下:
|
||||
|
||||
```java {2-4}
|
||||
class Singleton {
|
||||
private static Singleton singleton = new Singleton();
|
||||
public static int count1;
|
||||
public static int count2 = 3;
|
||||
|
||||
private Singleton() {
|
||||
count1++;
|
||||
count2++;
|
||||
}
|
||||
|
||||
public static Singleton getInstance() {
|
||||
return singleton;
|
||||
}
|
||||
}
|
||||
|
||||
public class Test {
|
||||
public static void main(String[] args) {
|
||||
Singleton singleTon = Singleton.getInstance();
|
||||
System.out.println("count1=" + singleTon.count1);
|
||||
System.out.println("count2=" + singleTon.count2);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这激起了我们几个干技术的热情,那就分析一下吧。
|
||||
|
||||
## 简单分析
|
||||
|
||||
1、简单看了下题目,这不是一个采用了饿汉式单例模式的单例类嘛,接下来当然是去找程序入口了。
|
||||
|
||||
2、在 Test 类的 main 方法中,首先调用了 Singleton 类的 getInstance() 方法,很显然这是要获取 Singleton 这个单例类的唯一对象(实例)了。
|
||||
|
||||
3、然后在获取到唯一对象(实例)之后,输出了 Singleton 类的两个静态成员变量 count1、count2 的值。(虽然通过对象名调用静态信息这种方式不推荐,但是对结果没有影响)
|
||||
|
||||
4、看到这儿,两个类里也没别的地方有输出语句,所以最终运行结果就是要看看 count1、count2 的输出值了。
|
||||
|
||||
5、**重点来了:** 在调用 getInstance() 方法前,由于 Singleton 类没有加载,所以肯定要先加载类,由于 count1、count2、Singleton 的唯一对象(实例)都是静态的,所以它们会随着类的加载而加载。其中 int 类型的 count1 变量没有指定初始值,那默认值就是 0,count2 指定了初始值是 3, Singleton 类的唯一对象(实例)要创建会调用构造方法,构造方法里又对 count1 和 count2 进行了自增 1 的运算,那结果自然就是 count1 是 1,count2 是 4。
|
||||
|
||||
这么一顿火花带闪电的分析后,自信的将答案发到了群里。
|
||||
|
||||
```
|
||||
count1=1
|
||||
count2=4
|
||||
```
|
||||
|
||||
## 深度分析
|
||||
|
||||
很显然答错了,不然也不会单独记录了。之所以答错了,是因为忽略了静态信息的加载顺序,静态信息的加载顺序是由编码顺序决定的,上方分析中先入为主的把 count1 和 count2 加载完了,但实际上最先执行的是 Singleton 的唯一对象(实例)创建及变量赋值,随后才是执行 count1、count2。
|
||||
|
||||
我们可以通过 `javap -c Singleton.class` 反汇编一下字节码文件,反汇编后的 JVM 指令如下:
|
||||
|
||||
```java
|
||||
Compiled from "Test.java"
|
||||
class org.example.Singleton {
|
||||
public static int count1;
|
||||
|
||||
public static int count2;
|
||||
|
||||
public static org.example.Singleton getInstance();
|
||||
Code:
|
||||
// 获取 singleton 静态对象变量,并将其值压入栈顶
|
||||
0: getstatic #4 // Field singleton:Lorg/example/Singleton;
|
||||
// 从当前方法返回 singleton 对象引用
|
||||
3: areturn
|
||||
|
||||
static {};
|
||||
Code:
|
||||
// 1、创建 Singleton 类的对象,并赋值给静态对象变量 singleton
|
||||
// 1.1 创建对象
|
||||
0: new #5 // class org/example/Singleton
|
||||
// 1.2 复制栈顶数值并将复制值压入栈顶
|
||||
3: dup
|
||||
// 1.3 调用 Singleton 类构造方法,count1 和 count2 自增 1,此时 count1 为 1,count2 为 1
|
||||
4: invokespecial #6 // Method "<init>":()V
|
||||
// 1.4 对象创建成功将对象引用赋值给静态对象变量 singleton
|
||||
7: putstatic #4 // Field singleTon:Lorg/example/Singleton;
|
||||
|
||||
// 2、将 3 赋值给 count2
|
||||
// 2.1 将 int 型 3 推送至栈顶
|
||||
10: iconst_3
|
||||
// 2.2 为 count2 静态变量赋值
|
||||
11: putstatic #3 // Field count2:I
|
||||
|
||||
// 3、结束方法
|
||||
14: return
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
很显然了,count2 最后是被赋值为 3 了。
|
||||
|
||||
正确答案就是:
|
||||
|
||||
```
|
||||
count1=1
|
||||
count2=3
|
||||
```
|
||||
|
||||
## 额外扩展
|
||||
|
||||
那如果真的想得到之前的结果呢?
|
||||
|
||||
```
|
||||
count1=1
|
||||
count2=4
|
||||
```
|
||||
|
||||
只需要将 count1、count2 两个静态变量的顺序调整到 Singleton 类的唯一对象(实例)变量上方就可以了。
|
||||
|
||||
```java {2-4}
|
||||
class Singleton {
|
||||
public static int count1;
|
||||
public static int count2 = 3;
|
||||
private static Singleton singleton = new Singleton();
|
||||
|
||||
private Singleton() {
|
||||
count1++;
|
||||
count2++;
|
||||
}
|
||||
|
||||
public static Singleton getInstance() {
|
||||
return singleton;
|
||||
}
|
||||
}
|
||||
|
||||
public class Test {
|
||||
public static void main(String[] args) {
|
||||
Singleton singleTon = Singleton.getInstance();
|
||||
System.out.println("count1=" + singleTon.count1);
|
||||
System.out.println("count2=" + singleTon.count2);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
我们再次通过 `javap -c Singleton.class` 反汇编一下字节码文件,反汇编后的 JVM 指令如下:
|
||||
|
||||
```java
|
||||
Compiled from "Test.java"
|
||||
class org.example.Singleton {
|
||||
public static int count1;
|
||||
|
||||
public static int count2;
|
||||
|
||||
public static org.example.Singleton getInstance();
|
||||
Code:
|
||||
// 获取 singleton 静态对象变量,并将其值压入栈顶
|
||||
0: getstatic #4 // Field singleton:Lorg/example/Singleton;
|
||||
// 从当前方法返回 singleton 对象引用
|
||||
3: areturn
|
||||
|
||||
static {};
|
||||
Code:
|
||||
// 1、将 3 赋值给 count2,count2 此时为 3
|
||||
// 1.1 将 int 型 3 推送至栈顶
|
||||
0: iconst_3
|
||||
// 1.2 为 count2 静态变量赋值
|
||||
1: putstatic #3 // Field count2:I
|
||||
|
||||
// 2、创建 Singleton 类的对象,并赋值给静态对象变量 singleton
|
||||
// 2.1 创建对象
|
||||
4: new #5 // class org/example/Singleton
|
||||
// 2.2 复制栈顶数值并将复制值压入栈顶
|
||||
7: dup
|
||||
// 2.3 调用 Singleton 类构造方法,count1 和 count2 自增 1,count1 此时为 1,count2 此时为 4
|
||||
8: invokespecial #6 // Method "<init>":()V
|
||||
// 2.4 对象创建成功将对象引用赋值给静态对象变量 singleton
|
||||
11: putstatic #4 // Field singleTon:Lorg/example/Singleton;
|
||||
|
||||
// 3、结束方法
|
||||
14: return
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
很显然了,count2 最后是被自增为 4 了。
|
Reference in New Issue
Block a user