接上篇
三 JsonReader 和 JsonWriter
在进行 json 的序列化和反序列化源码解析之前先了解一下其主体工具类。
1 JsonReader
JsonReader 是 Gson 中用于 json 反序列化的主体。
在 Gson 包中可以不使用 Gson 门面而单独使用 JsonReader 进行 json 的反序列化:
public static void main(String[] args){
//json 字符串
String json = "{\"name\":\"zhangsan\",\"age\":18}";
//使用 StringReader 包装字符串
StringReader strReader = new StringReader(json);
//使用 JsonReader 包装 StringReader
JsonReader jsonReader = new JsonReader(strReader);
//创建一个参数均为空的目标对象
Person p = new Person();
try {
//beginObject(...) 方法用于告诉 jsonReader 开始读取一个 object
jsonReader.beginObject();
//while 循环读取
while(jsonReader.hasNext()){
//读取一个 json 的 key 值
String name = jsonReader.nextName();
switch (name){
case "name":
p.setName(jsonReader.nextString()); //读取一个 String 作为 value,存入对象中
break;
case "age":
p.setAge(jsonReader.nextInt()); //读取一个 int 作为 value,存入对象中
break;
}
}
jsonReader.endObject();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("名字:" + p.getName());
System.out.println("年龄:" +p.getAge())
}
JsonReader 的构造方法:
//JsonReader.class
public JsonReader(Reader in) {
if (in == null) {
throw new NullPointerException("in == null");
}
//传入的 Reader 中保存着要读取的 json 主体
//此处比较常用的是 StringReader,但是也可以是 FileReader 等其它 Reader 接口的实现类
this.in = in;
}
JsonReader 仅此一个构造方法,创建对象均调用此方法。
先来看一下 JsonReader 中非常重要的一个非公开方法 doPeek():
//JsonReader.class
int doPeek() throws IOException {
//stack 是一个定义在 JsonReader 中的 int 数组,作为 JsonReader 的指令集存在,用于控制变量 peeked 的状态
//在 JsonReader 初始化的时候会将 stack 的第一个元素变成6,其余均为0
//6的意思根据官方注释为 "No object or array has been started"(还没开始读取对象或列表)
//6作为常量保存在 JsonScope 中,JsonScope 中还保存了很多代表指令的常量,下列会用到
//stackSize 是 stack 的有效元素计数器,初始化时 stackSize = 1,即只有第一个元素是有效的
int peekStack = stack[stackSize - 1];
//JsonScope.EMPTY_ARRAY = 1
if (peekStack == JsonScope.EMPTY_ARRAY) {
//JsonScope.NONEMPTY_ARRAY = 2
stack[stackSize - 1] = JsonScope.NONEMPTY_ARRAY;
} else if (peekStack == JsonScope.NONEMPTY_ARRAY) {
//在第一次调用 nextNonWhitespace(true) 方法的时候,json 字符串会被转存为一个 char 数组
//该方法以 int 值的形式返回下一个要解析的 char 对象
int c = nextNonWhitespace(true);
switch (c) {
case ']':
//peeked 是 JsonReader 中最重要的用来状态控制的 int 变量
//peeker 和 stack 会协同控制 JsonReader 的逻辑行为
return peeked = PEEKED_END_ARRAY; //PEEKED_END_ARRAY = 4
case ';':
//检查标准协议选项,json 标准中的符号没有分号
//所以在 lenient = false 的时候就会报错
checkLenient();
case ',':
break;
default:
throw syntaxError("Unterminated array");
}
//JsonScope.EMPTY_OBJECT = 3,JsonScope.NONEMPTY_OBJECT = 5
} else if (peekStack == JsonScope.EMPTY_OBJECT || peekStack == JsonScope.NONEMPTY_OBJECT) {
//JsonScope.DANGLING_NAME = 4
stack[stackSize - 1] = JsonScope.DANGLING_NAME;
if (peekStack == JsonScope.NONEMPTY_OBJECT) {
int c = nextNonWhitespace(true);
switch (c) {
case '}':
return peeked = PEEKED_END_OBJECT; //PEEKED_END_OBJECT = 2
case ';':
checkLenient();
case ',':
break;
default:
throw syntaxError("Unterminated object");
}
}
int c = nextNonWhitespace(true);
switch (c) {
case '"':
return peeked = PEEKED_DOUBLE_QUOTED_NAME; //PEEKED_DOUBLE_QUOTED_NAME = 13
case '\'':
checkLenient();
return peeked = PEEKED_SINGLE_QUOTED_NAME; //PEEKED_SINGLE_QUOTED_NAME = 12
case '}':
if (peekStack != JsonScope.NONEMPTY_OBJECT) {
return peeked = PEEKED_END_OBJECT;
} else {
throw syntaxError("Expected name");
}
default:
checkLenient();
pos--;
if (isLiteral((char) c)) {
return peeked = PEEKED_UNQUOTED_NAME; //PEEKED_UNQUOTED_NAME = 14
} else {
throw syntaxError("Expected name");
}
}
} else if (peekStack == JsonScope.DANGLING_NAME) {
stack[stackSize - 1] = JsonScope.NONEMPTY_OBJECT;
int c = nextNonWhitespace(true);
switch (c) {
case ':':
break;
case '=':
checkLenient();
//buffer 是储存 json 字符串的 char 数组
//pos 是已经读取到字符的数量指针
//limit 是 buffer 的可用部分的总长
if ((pos < limit || fillBuffer(1)) && buffer[pos] == '>') {
pos++;
}
break;
default:
throw syntaxError("Expected ':'");
}
//JsonScope.EMPTY_DOCUMENT = 6
//第一次进入方法的时候,会进入这个 if 语句中
} else if (peekStack == JsonScope.EMPTY_DOCUMENT) {
//检查标准化协议相关
if (lenient) {
consumeNonExecutePrefix();
}
//JsonScope.NONEMPTY_DOCUMENT = 7
stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT;
} else if (peekStack == JsonScope.NONEMPTY_DOCUMENT) {
int c = nextNonWhitespace(false);
if (c == -1) {
return peeked = PEEKED_EOF;
} else {
checkLenient();
pos--;
}
//JsonScope.CLOSED = 8
} else if (peekStack == JsonScope.CLOSED) {
throw new IllegalStateException("JsonReader is closed");
}
//在这里获取到了下一个要解析的 char 的 int 值
int c = nextNonWhitespace(true);
//进入 switch 去进行定位,定位到了之后修改 peeked 的状态
switch (c) {
case ']':
if (peekStack == JsonScope.EMPTY_ARRAY) {
return peeked = PEEKED_END_ARRAY;
}
case ';':
case ',':
if (peekStack == JsonScope.EMPTY_ARRAY || peekStack == JsonScope.NONEMPTY_ARRAY) {
checkLenient();
pos--;
return peeked = PEEKED_NULL;
} else {
throw syntaxError("Unexpected value");
}
case '\'':
checkLenient();
return peeked = PEEKED_SINGLE_QUOTED;
case '"':
return peeked = PEEKED_DOUBLE_QUOTED;
case '[':
return peeked = PEEKED_BEGIN_ARRAY;
case '{':
return peeked = PEEKED_BEGIN_OBJECT;
default:
pos--;
}
//peekKeyword() 方法会从 buffer 数组里获取下一个 char
//然后根据这个字符判断下一个要处理的字符串是不是 true、false、null 等特殊字符
//如果不是,会返回 result = PEEKED_NONE
int result = peekKeyword();
if (result != PEEKED_NONE) {
//不等于 PEEKED_NONE,证明下一个确实是特殊字符
return result;
}
//peekNumber() 方法和上述 peekKeyword() 方法很类似
//用于判断下一个要处理的字符串是否是数字
result = peekNumber();
if (result != PEEKED_NONE) {
return result;
}
//isLiteral(buffer[pos]) 用于判断下一个字符是否是特殊符
//比如 换行符、井号、括号 等
//如果是 换行符 的话这里就会抛出错误
if (!isLiteral(buffer[pos])) {
throw syntaxError("Expected value");
}
checkLenient();
return peeked = PEEKED_UNQUOTED; //PEEKED_UNQUOTED = 10
}
方法虽然比较长,但是代码其实比较简单,本质上是根据 stack 指令去修改 peeked 的值,从而达到状态控制的效果。
再来看 beginObject() 方法:
//JsonReader.class
public void beginObject() throws IOException {
int p = peeked;
//初始化时 peeked = PEEKED_NONE
//在 doPeek() 方法中会修改成 PEEKED_BEGIN_OBJECT,即开始一个 Object 的序列化
if (p == PEEKED_NONE) {
p = doPeek();
}
if (p == PEEKED_BEGIN_OBJECT) {
//push(...) 方法会检查 stack 数组的容积,适时进行扩容,并把传入的指令存放到数组中
//此处将 EMPTY_OBJECT 指令存入到 stack 中
push(JsonScope.EMPTY_OBJECT);
//将 peeked 状态初始化
peeked = PEEKED_NONE;
} else {
throw new IllegalStateException("Expected BEGIN_OBJECT but was " + peek() + locationString());
}
}
再来看 nextName() 方法:
//JsonReader.class
public String nextName() throws IOException {
//老样子进行 peeked 的状态获取
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
}
String result;
//在这里通过 if 语句和 peeked 定位 json 的 key 是用单引号还是双引号包裹的
//result 就是 key 的字符串
if (p == PEEKED_UNQUOTED_NAME) {
result = nextUnquotedValue();
} else if (p == PEEKED_SINGLE_QUOTED_NAME) {
result = nextQuotedValue('\'');
} else if (p == PEEKED_DOUBLE_QUOTED_NAME) {
result = nextQuotedValue('"');
} else {
throw new IllegalStateException("Expected a name but was " + peek() + locationString());
}
//将 peeked 状态初始化
peeked = PEEKED_NONE;
//pathNames 是一个用来储存所有 key 的字符串的数组
pathNames[stackSize - 1] = result;
return result;
}
nextQuotedValue(...) 方法里实际上是比较繁琐的字符串处理,在这里暂时不展开了。
hasNext() 和 nextString() 方法其实都和 nextName() 方法差不多,本质上都是根据 peeked 的值去进入不同的 if 语句来处理字符串。
2 JsonWriter
JsonWriter 是 Gson 中用于 json 序列化的主体。
和 JsonReader 一样,也可以不使用 Gson 门面而单独使用 JsonWriter 进行 json 的序列化:
public static void main(String[] args){
//组装 bean
Person person = new Person();
person.setName("zhangsan");
person.setAge(18);
//创建一个 StringWriter,本质是 StringBuffer 的封装
StringWriter writer = new StringWriter();
//用 JsonWriter 去封装 StringWriter
JsonWriter jsonWriter = new JsonWriter(writer);
try {
//启动一个 object 的写入
jsonWriter.beginObject();
//写入 key-value
jsonWriter.name("name").value(person.getName());
jsonWriter.name("age").value(person.getAge());
//结束命令
jsonWriter.endObject();
//将 JsonWriter 里的数据存入到 StringWriter 中
jsonWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(writer.toString());
}
JsonWriter 的构造方法:
//JsonWriter.class
public JsonWriter(Writer out) {
if (out == null) {
throw new NullPointerException("out == null");
}
this.out = out;
}
追踪一下 beginObject() 方法:
//JsonWriter.class
public JsonWriter beginObject() throws IOException {
//这个方法主要的作用是在不同的 object 之间加逗号
//如果是起始第一个 object 就不需要了,会直接跳过
writeDeferredName();
return open(EMPTY_OBJECT, "{");
}
继续追踪 open(...) 方法:
//JsonWriter.class
private JsonWriter open(int empty, String openBracket) throws IOException {
//该方法会使用 stack 中最新的指令进行操作
beforeValue();
//push(...) 方法会检查 stack 数组的容积,适时进行扩容,并把传入的指令存放到数组中
push(empty);
//写入字符串
out.write(openBracket);
return this;
}
JsonWriter 中没有 JsonReader 中那么复杂的指令操作,所以没有引入 peeked 变量,仅仅使用 stack 数组来控制状态。
stack 的控制封装在 beforeValue() 方法中:
//JsonWriter.class
private void beforeValue() throws IOException {
//peek() 方法会根据 stack 数组的指令获取到下一个要操作的字符类型
switch (peek()) {
case NONEMPTY_DOCUMENT:
if (!lenient) {
throw new IllegalStateException(
"JSON must have only one top-level value.");
}
case EMPTY_DOCUMENT:
//replaceTop(...) 方法会将 stack 最新的指令更新成传入的参数
replaceTop(NONEMPTY_DOCUMENT);
break;
case EMPTY_ARRAY:
replaceTop(NONEMPTY_ARRAY);
//换行
newline();
break;
case NONEMPTY_ARRAY:
out.append(',');
newline();
break;
case DANGLING_NAME:
//separator 即为 ":" (冒号)
out.append(separator);
replaceTop(NONEMPTY_OBJECT);
break;
default:
throw new IllegalStateException("Nesting problem.");
}
}
来看一下 name(...) 方法:
//JsonWriter.class
public JsonWriter name(String name) throws IOException {
if (name == null) {
throw new NullPointerException("name == null");
}
if (deferredName != null) {
throw new IllegalStateException();
}
if (stackSize == 0) {
throw new IllegalStateException("JsonWriter is closed.");
}
//这一行代码中会暂时将传入的 name 参数保存在一个全局变量中,
//所以 JsonWriter 在调用 value(...) 方法之前不能再调用 name(...) 方法了,不然在上方的判断中会报错
deferredName = name;
return this;
}
来看一下 value(...) 方法:
//JsonWriter.class
public JsonWriter value(String value) throws IOException {
if (value == null) {
//nullValue() 方法会在 value 值的地方存入一个 null
return nullValue();
}
//这一方法会将 之前保存在 deferredName 中的字符串写入到 writer 中
//方法中会处理加逗号、将 deferredName 变量清空等问题
//核心是调用 string(...) 方法写入
writeDeferredName();
//更新 stack 指令
beforeValue();
//和上方写入 deferredName 一样,此处调用写入 value
string(value);
return this;
}
总的来说 JsonWriter 比 JsonReader 简单,简要描述一下不过多展开。
四 JSON 字符串转 Bean
该 part 的起点:
Person person = gson.fromJson(json,Person.class);
追踪 fromJson(...) 方法:
//Gson.class
public T fromJson(String json, Class classOfT) throws JsonSyntaxException {
//将字符串转成 object 的主体方法
//3.1
Object object = fromJson(json, (Type) classOfT);
return Primitives.wrap(classOfT).cast(object);
}
先来看一下上述的第二行代码:
return Primitives.wrap(classOfT).cast(object);
Primitives.wrap(...) 方法的实现:
//Primitives.class
public static Class wrap(Class type) {
//PRIMITIVE_TO_WRAPPER_TYPE 是一个 map 对象
//$Gson$Preconditions.checkNotNull(...) 用来效验 type 不为空
@SuppressWarnings("unchecked")
Class wrapped = (Class) PRIMITIVE_TO_WRAPPER_TYPE.get(
$Gson$Preconditions.checkNotNull(type));
//如果 map 中不存在 type 为 key 的值,则返回 type,否则返回取到的 value
return (wrapped == null) ? type : wrapped;
}
PRIMITIVE_TO_WRAPPER_TYPE 是一个定义在 Primitives 中的 map 对象:
//Primitives.class
private static final Map, Class>> PRIMITIVE_TO_WRAPPER_TYPE;
PRIMITIVE_TO_WRAPPER_TYPE 中主要存放了 float、int、double 等原始类型的 class:
//Primitives.class
static {
Map, Class>> primToWrap = new HashMap, Class>>(16);
Map, Class>> wrapToPrim = new HashMap, Class>>(16);
//以下代码是将原始类型和包装类型存入两个 map 的过程
add(primToWrap, wrapToPrim, boolean.class, Boolean.class);
add(primToWrap, wrapToPrim, byte.class, Byte.class);
add(primToWrap, wrapToPrim, char.class, Character.class);
add(primToWrap, wrapToPrim, double.class, Double.class);
add(primToWrap, wrapToPrim, float.class, Float.class);
add(primToWrap, wrapToPrim, int.class, Integer.class);
add(primToWrap, wrapToPrim, long.class, Long.class);
add(primToWrap, wrapToPrim, short.class, Short.class);
add(primToWrap, wrapToPrim, void.class, Void.class);
//Collections.unmodifiableMap(...) 返回一个只能阅读不能修改的 map
//原始类型作为 key,包装类型作为 value
PRIMITIVE_TO_WRAPPER_TYPE = Collections.unmodifiableMap(primToWrap);
//包装类型作为 value,原始类型作为 key
WRAPPER_TO_PRIMITIVE_TYPE = Collections.unmodifiableMap(wrapToPrim);
}
由此可见 Primitives.wrap(...) 本质上是判断传入的 type 是否是原始类型,如果是,则会转换成包装类型并返回。
至于 Primitives.wrap(classOfT).cast(object) 中的 cast(...),则是定义在 Class 中的方法:
//Class.class
@HotSpotIntrinsicCandidate
public T cast(Object obj) {
//isInstance(...) 方法等价于关键词 instanceof
//如果 obj 不为 null 且 不为该 Class 对象的子类,则会抛出错误
if (obj != null && !isInstance(obj))
throw new ClassCastException(cannotCastMsg(obj));
return (T) obj;
}
代码比较简单,本质上就是强转类型。
再来看这行代码:
Object object = fromJson(json, (Type) classOfT);
追踪具体实现:
//Gson.class
public T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
//非 null 判断
if (json == null) {
return null;
}
//StringReader 是一个 jdk 中存在的 String 和 Reader 的关联封装类
StringReader reader = new StringReader(json);
//主体功能实现方法
T target = (T) fromJson(reader, typeOfT);
//返回一个指定泛型的对象
return target;
}
继续追踪重载方法:
//Gson.class
public T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
//初始化一个 JsonReader
JsonReader jsonReader = newJsonReader(json);
//主体功能实现方法
T object = (T) fromJson(jsonReader, typeOfT);
//在整个反序列化过程结束之前效验 jsonReader 的 peeked 的状态
//如果 peeker 未处于结束状态,则会报错
assertFullConsumption(object, jsonReader);
return object;
}
继续追踪重载方法:
//Gson.class
public T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
boolean isEmpty = true;
boolean oldLenient = reader.isLenient();
//打开 reader 的标准化检验
reader.setLenient(true);
try {
//此处相当于调用了一次 JsonReader 中的 doPeek() 方法
reader.peek();
isEmpty = false;
//TypeToken 本质上是 Class 的增强封装类
TypeToken typeToken = (TypeToken) TypeToken.get(typeOfT);
//根据 TypeToken 获取对应的能够处理其类型的 TypeAdapter
TypeAdapter typeAdapter = getAdapter(typeToken);
//反射创建 object
T object = typeAdapter.read(reader);
return object;
} catch (EOFException e) {
if (isEmpty) {
return null;
}
throw new JsonSyntaxException(e);
} catch (IllegalStateException e) {
throw new JsonSyntaxException(e);
} catch (IOException e) {
throw new JsonSyntaxException(e);
} catch (AssertionError e) {
throw new AssertionError("AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage(), e);
} finally {
reader.setLenient(oldLenient);
}
}
先来看一下 getAdapter(...) 方法:
//Gson.class
public TypeAdapter getAdapter(TypeToken type) {
//typeTokenCache 是 Gson 中的一个 map 对象,用于储存 TypeAdapter
//typeTokenCache 是一个 Gson 中各个线程公用的一个缓存池
TypeAdapter> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type);
if (cached != null) {
//如果本身就有储存了,就直接返回
return (TypeAdapter) cached;
}
//calls 是一个 ThreadLocal 对象
//ThreadLocal 是 Gson 中单个线程使用的缓存池,在里面存入的对象会在 finally 代码块中清空掉
Map, FutureTypeAdapter>> threadCalls = calls.get();
//判断是否需要清空 ThreadLocal
boolean requiresThreadLocalCleanup = false;
if (threadCalls == null) {
threadCalls = new HashMap, FutureTypeAdapter>>();
calls.set(threadCalls);
//这里存入了对象,所以需要清空 ThreadLocal
requiresThreadLocalCleanup = true;
}
//如果存在对象,就会在这里取用出来并返回
//FutureTypeAdapter 是一个门面模式的应用,其实本质是使用内部的 TypeAdapter 去处理业务
//如果内部没有存入实际处理业务的 TypeAdapter,就会报错
FutureTypeAdapter ongoingCall = (FutureTypeAdapter) threadCalls.get(type);
if (ongoingCall != null) {
return ongoingCall;
}
try {
FutureTypeAdapter call = new FutureTypeAdapter();
threadCalls.put(type, call);
//这个方法的主体是这个 for 循环,用于从 Gson 初始化的时候储存的列表中获取到对应的 TypeAdapter
for (TypeAdapterFactory factory : factories) {
//TypeAdapter 的 create(...) 方法对于不是对应类型的参数会返回 null
TypeAdapter candidate = factory.create(this, type);
if (candidate != null) {
//在此处会存入业务处理的 TypeAdapter
call.setDelegate(candidate);
//在此处存入公用缓存
typeTokenCache.put(type, candidate);
return candidate;
}
}
throw new IllegalArgumentException("GSON (" + GsonBuildConfig.VERSION + ") cannot handle " + type);
} finally {
//清除 ThreadLocal 缓存
threadCalls.remove(type);
if (requiresThreadLocalCleanup) {
calls.remove();
}
}
}
到此为止 json 的反序列化就基本完毕了.
五 Bean 转 JSON 字符串
该 part 的起点:
String json2 = gson.toJson(person);
追踪 toJson(...) 方法:
//Gson.class
public String toJson(Object src) {
if (src == null) {
return toJson(JsonNull.INSTANCE);
}
return toJson(src, src.getClass());
}
toJson(JsonNull.INSTANCE) 方法最后会输出一个 null 字符串,不多展开了。
继续来关注下方主要实现逻辑:
//Gson.class
public String toJson(Object src, Type typeOfSrc) {
//新建一个 StringWriter
StringWriter writer = new StringWriter();
//主要逻辑
toJson(src, typeOfSrc, writer);
//返回
return writer.toString();
}
继续追踪主要逻辑方法:
//Gson.class
public void toJson(Object src, Type typeOfSrc, Appendable writer) throws JsonIOException {
try {
//这里将 StringWriter 包装成了 JsonWriter
JsonWriter jsonWriter = newJsonWriter(Streams.writerForAppendable(writer));
//主要逻辑
toJson(src, typeOfSrc, jsonWriter);
} catch (IOException e) {
throw new JsonIOException(e);
}
}
继续追踪主要逻辑方法:
//Gson.class
public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException {
//获取适配的 TypeAdapter
TypeAdapter> adapter = getAdapter(TypeToken.get(typeOfSrc));
//下方代码均为储存并存入一些标准化的设置
boolean oldLenient = writer.isLenient();
writer.setLenient(true);
boolean oldHtmlSafe = writer.isHtmlSafe();
writer.setHtmlSafe(htmlSafe);
boolean oldSerializeNulls = writer.getSerializeNulls();
writer.setSerializeNulls(serializeNulls);
//写入
try {
((TypeAdapter
基本的逻辑都在上面讲过了,不赘述。
到此为止 json 的序列化就基本完毕了。
四 总结
Gson 的代码封装很薄,本身不难,但是为了照顾兼容性,代码中存在很多条件判断,导致代码看上去很繁琐。同时为了兼顾性能做了很多有意思的设计,比如获取适配器的时候的双缓存设计,应该是为了提高解析器的复用效率,具体有待研究。
总结一下 Gson 的基本思路:
· 解析器(Gson)将使用者传入的字符串或对象存入读取器(Reader)或者写入器(Writer)中
· 解析器遍历并获取能够处理对应类型的适配器工厂(TypeAdapterFactory)
· 适配器工厂会创建出对应类型的适配器(TypeAdapter)
· 解析器将阅读器或写入器交给适配器
· 适配器自行通过业务逻辑操作读取器或写入器,输出需要的结果
· 解析器接收此输出,并交给使用者
五 一点唠叨
· Gson 太过强调功能的全面,解析器的初始化非常复杂
· JsonReader 的状态控制太过复杂和精密,笔者到现在也没全部弄清楚
· 在本篇源码解析中,Gson 内部还有一些拓展功能,比如 JsonElement、JsonParser 等的工具类没有提及
· 仅为个人的学习笔记,可能存在错误或者表述不清的地方,有缘补充