public static void main(String[] args) {
List first = new ArrayList();
first.add(1);
first.add(“2”);
first.add(‘3’);
List third = first;
for (String each : third) { // 类型转换异常
System.out.println(each);
}
}
类型转换异常?
我们使用IDEA的jclasslib反编译插件,得到main函数的代码如下:
0 new #2 <java/util/ArrayList>
3 dup
4 invokespecial #3 <java/util/ArrayList.>
7 astore_1
8 aload_1
9 iconst_1
10 invokestatic #4 <java/lang/Integer.valueOf>
13 invokeinterface #5 <java/util/List.add> count 2
18 pop
19 aload_1
20 ldc #6 <2>
22 invokeinterface #5 <java/util/List.add> count 2
27 pop
28 aload_1
29 bipush 51
31 invokestatic #7 <java/lang/Character.valueOf>
34 invokeinterface #5 <java/util/List.add> count 2
39 pop
40 aload_1
41 astore_2
42 aload_2
43 invokeinterface #8 <java/util/List.iterator> count 1
48 astore_3
49 aload_3
50 invokeinterface #9 <java/util/Iterator.hasNext> count 1
55 ifeq 79 (+24)
58 aload_3
59 invokeinterface #10 <java/util/Iterator.next> count 1
64 checkcast #11 <java/lang/String>
67 astore_4
69 getstatic #12 <java/lang/System.out>
72 aload_4
73 invokevirtual #13 <java/io/PrintStream.println>
76 goto 49 (-27)
79 return
从42到76行对应foreach循环的逻辑,可以修剪列表的迭代器进行遍历,取出每个元素后强转为String类型,存储到局部变量表索引为4的位置,然后进行打印。
如果对反编译不熟悉可以去目标目录,双击编译后的类文件,使用IDEA自带的插件进行反编译:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.chujianyun.common.json;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class JsonGenericDemo {
public JsonGenericDemo() {
}
public static void main(String[] args) {
List first = new ArrayList();
first.add(1);
first.add(“2”);
first.add(‘3’);
List third = first;
Iterator var3 = first.iterator();
while(var3.hasNext()) {
String each = (String)var3.next();
System.out.println(each);
}
}
}
印证了上述说法,可见在String each =(String)var3.next(); 这里出现了类型转换异常。
三、解决之道
3.1猜想验证
我们猜测是不是可以通过某种途径将泛型作为参数传递给fastjson,让fastjson某个返回值是带泛型的,从而解决这个矛盾呢?
显然我们要去原始码中寻找,在JSONObject类中找到了以下的方法:
/**
*
* String jsonStr = “[{“id”:1001,“name”:“Jobs”}]”;
* List models = JSON.parseObject(jsonStr, new TypeReference<List>() {});
*
* @param text json string
* @param type type refernce
* @param features
* @return
*/
@SuppressWarnings(“unchecked”)
public static T parseObject(String text, TypeReference type, Feature… features) {
return (T) parseObject(text, type.type, ParserConfig.global, DEFAULT_PARSER_FEATURE, features);
}
该函数的注释上还贴心地标注了相关用法,因此我们改造下:
public static void main(String[] args) {
String jsonString = “[“a”,“b”]”;
List list = JSONObject.parseObject(jsonString, new TypeReference<List>() {
});
System.out.println(list);
}
警告解除了。
所以大功告成?
难道上述做法可能为了消除一个警告,满足强迫症们的心愿而已吗???
且慢,我们看下面的例子:
import lombok.Data;
@Data
public class User {
private Long id;
private String name;
}
mport com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import java.util.ArrayList;
import java.util.List;
public class JsonGenericDemo {
public static void main(String[] args) {
// 构造数据
User user = new User();
user.setId(0L);
user.setName(“tom”);
List users = new ArrayList<>();
users.add(user);
// 转为JSON字符串
String jsonString = JSON.toJSONString(users);
// 反序列化
List usersGet = JSONObject.parseObject(jsonString, List.class);
for (User each : usersGet) {
System.out.println(each);
}
}
}
大家执行上述示例会出现类型转换异常!
线程“主”中的异常java.lang.ClassCastException:com.alibaba.fastjson.JSONObject无法转换为com.chujianyun.common.json.com上的用户。com.chujianyun.common.json.JsonGenericDemo.main(JsonGenericDemo.java:26 )
有了第二部分的分析,大家可能就可以比较容易地想到
JSONObject.parseObject(jsonString, List.class)
构造出来的List存放的是JSONObject元素,foreach循环嵌套使用转换器遍历每个元素并强转为User类型是报类型转换异常。
那么为啥fastjson不能帮我们转换为List<User>
类型呢?
有人说“由于泛型取向,没有泛型信息,所以无法逆向构造回初始类型”。
看下其实JSONObject.parseObject(jsonString, List.class);
第一个参数的英文字符串,第二个参数是List.class。压根就没有提供泛型信息给FASTJSON。
作为这个工具函数本身,怎么猜得到要列出里面真正该存放啥类型呢?
因此如果能够通过某种途径,告诉它泛型的类型,就可以帮助您反序列化成真正的类型。
使用JSONObject.parseObject(jsonString, new TypeReference<List<User>>() { });
即可。
因此我们使用TypeReference并同时为了消除警告,或者为了了解fastjson泛型的具体类型,正确反序列化泛型的类型。
那么突破原理是啥呢?我们看下com.alibaba.fastjson.TypeReference#TypeReference()
/**
* Constructs a new type literal. Derives represented class from type
* parameter.
*
Clients create an empty anonymous subclass. Doing so embeds the type
* parameter in the anonymous class’s type hierarchy so we can reconstitute it
* at runtime despite erasure.
*/
protected TypeReference(){
// 获取父类的 Type
Type superClass = getClass().getGenericSuperclass();
// 如果父类是参数化类型,会返回 java.lang.reflect.ParameterizedType
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
![img](https://img-blog.csdnimg.cn/img_convert/fc7566bc2f267b47a7e4d73a6ca22164.jpeg)
总结
对于面试还是要好好准备的,尤其是有些问题还是很容易挖坑的,例如你为什么离开现在的公司(你当然不应该抱怨现在的公司有哪些不好的地方,更多的应该表明自己想要寻找更好的发展机会,自己的一些现实因素,比如对于我而言是现在应聘的公司离自己的家更近,又或者是自己工作到达了迷茫期,想跳出迷茫期等等)
Java面试精选题、架构实战文档
整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~
你的支持,我的动力;祝各位前程似锦,offer不断!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
e5c14a7895254671a72faed303032d36.jpg" alt=“img” style=“zoom: 33%;” />
总结
对于面试还是要好好准备的,尤其是有些问题还是很容易挖坑的,例如你为什么离开现在的公司(你当然不应该抱怨现在的公司有哪些不好的地方,更多的应该表明自己想要寻找更好的发展机会,自己的一些现实因素,比如对于我而言是现在应聘的公司离自己的家更近,又或者是自己工作到达了迷茫期,想跳出迷茫期等等)
[外链图片转存中…(img-xvNA5yO6-1713015698351)]
Java面试精选题、架构实战文档
整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~
你的支持,我的动力;祝各位前程似锦,offer不断!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!