Jackson 用起来!
👉 欢迎加入小哈的星球 ,你将获得: 专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡 / 赠书福利
全栈前后端分离博客项目 2.0 版本完结啦, 演示链接:http://116.62.199.48/ ,新项目正在酝酿中。全程手摸手,后端 + 前端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,直到项目上线。目前已更新了239小节,累计38w+字,讲解图:1645张,还在持续爆肝中.. 后续还会上新更多项目,目标是将Java领域典型的项目都整一波,如秒杀系统, 在线商城, IM即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,已有1100+小伙伴加入(早鸟价超低)
Jackson简介
Jackson是一个用于处理JSON数据的开源Java库。JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于阅读和编写,同时也易于计算机解析和生成。在Java领域,Jackson已经成为处理JSON数据的事实标准库。它提供了丰富的功能,包括将Java对象转换为JSON字符串(序列化)以及将JSON字符串转换为Java对象(反序列化)。
Jackson主要由三个核心包组成:
jackson-databind:提供了通用的数据绑定功能(将Java对象与JSON数据相互转换) jackson-core:提供了核心的低级JSON处理API(例如JsonParser和JsonGenerator) jackson-annotations:提供了用于配置数据绑定的注解
为什么选择Jackson
尽管Java生态系统中有其他处理JSON数据的库(如Gson和JSON-java),但Jackson仍然是许多开发者的首选,原因包括:
性能:Jackson性能优越,对内存和CPU的使用都相对较低。许多性能基准测试表明,Jackson在序列化和反序列化方面都比其他库更快。 功能丰富:Jackson提供了许多功能,包括注解、自定义序列化和反序列化、动态解析等,使其非常灵活和强大。 易于使用:Jackson的API简单易用,使得开发者可以轻松地在他们的应用程序中集成和使用。 社区支持:Jackson拥有庞大的开发者社区,这意味着有更多的文档、教程和问题解答可供参考。 模块化:Jackson支持通过模块扩展其功能,例如Java 8时间库、Joda-Time和Kotlin等。 兼容性:Jackson可以很好地与其他流行的Java框架(如Spring)集成。
综上所述,Jackson是一个强大且易于使用的库,值得Java开发者在处理JSON数据时使用。
Jackson的基本功能
Jackson库的核心功能是将Java对象转换为JSON字符串(序列化)以及将JSON字符串转换为Java对象(反序列化)。下面是这两个功能的详细介绍:
将Java对象转换为JSON字符串(序列化)
序列化是将Java对象转换为JSON字符串的过程。这在许多场景中非常有用,例如在将数据发送到Web客户端时,或者在将数据存储到文件或数据库时。Jackson通过ObjectMapper
类来实现序列化。以下是一个简单的示例:
import com.fasterxml.jackson.databind.ObjectMapper;
public class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public static void main(String[] args) {
ObjectMapper objectMapper = new ObjectMapper();
Person person = new Person("Alice", 30);
try {
String jsonString = objectMapper.writeValueAsString(person);
System.out.println(jsonString); // 输出:{"name":"Alice","age":30}
} catch (Exception e) {
e.printStackTrace();
}
}
}
将JSON字符串转换为Java对象(反序列化)
反序列化是将JSON字符串转换回Java对象的过程。这在从Web客户端接收数据或从文件或数据库读取数据时非常有用。同样,Jackson使用ObjectMapper
类来实现反序列化。以下是一个简单的示例:
import com.fasterxml.jackson.databind.ObjectMapper;
public class Person {
public String name;
public int age;
public Person() {
}
public static void main(String[] args) {
ObjectMapper objectMapper = new ObjectMapper();
String jsonString = "{\"name\":\"Alice\",\"age\":30}";
try {
Person person = objectMapper.readValue(jsonString, Person.class);
System.out.println("Name: " + person.name + ", Age: " + person.age); // 输出:Name: Alice, Age: 30
} catch (Exception e) {
e.printStackTrace();
}
}
}
这些示例展示了Jackson库的基本功能。接下来的部分将介绍如何使用Jackson库,包括添加依赖、创建Java对象模型以及使用ObjectMapper
进行序列化和反序列化。
由于Jackson库的API非常多,这里无法一一详细介绍。我将为你提供一些主要的API和组件概览,以便于你更好地了解Jackson库。具体实现和使用方法,你可以参考官方文档和相关教程。
以下是Jackson库的一些主要API和组件:
1.ObjectMapper:这是Jackson库的核心类,用于序列化和反序列化操作。主要方法有:
writeValueAsString(Object)
:将Java对象序列化为JSON字符串。readValue(String, Class)
:将JSON字符串反序列化为Java对象。
2.JsonParser:用于从JSON数据源(如文件、输入流或字符串)解析JSON数据。主要方法有:
nextToken()
:获取下一个JSON令牌(如START_OBJECT、FIELD_NAME等)。getValueAsString()
:将当前令牌作为字符串返回。getValueAsInt()
:将当前令牌作为整数返回。
3.JsonGenerator:用于将JSON数据写入数据源(如文件、输出流或字符串缓冲区)。主要方法有:
writeStartObject()
:写入开始对象标记({
)。writeFieldName(String)
:写入字段名称。writeString(String)
:写入字符串值。writeEndObject()
:写入结束对象标记(}
)。
4.JsonNode:用于表示JSON树模型中的节点,可以是对象、数组、字符串、数字等。主要方法有:
get(String)
:获取指定字段的子节点。path(String)
:获取指定字段的子节点,如果不存在则返回一个“missing”节点。isObject()
:检查当前节点是否是一个对象。isArray()
:检查当前节点是否是一个数组。
5.注解:Jackson提供了一系列注解来配置序列化和反序列化过程。一些常用注解包括:
@JsonProperty
:指定字段在JSON数据中的名称。@JsonIgnore
:指定字段在序列化和反序列化过程中被忽略。@JsonCreator
:指定用于反序列化的构造函数或工厂方法。@JsonSerialize
:指定用于序列化特定字段或类的自定义序列化器。@JsonDeserialize
:指定用于反序列化特定字段或类的自定义反序列化器。
这只是Jackson库API和组件的一个概览。如果你想深入了解具体的API和使用方法,请参考官方文档(github.com/FasterXML/j…[1] )和相关教程。同时,实际编程过程中,根据具体需求学习和了解相应的API也是非常有效的方法。
使用Jackson的基本步骤
要开始使用Jackson,你需要遵循以下基本步骤:
添加依赖(Maven或Gradle)
首先,你需要将Jackson库添加到你的项目中。这可以通过Maven或Gradle来完成。以下是添加Jackson库的方法:
Maven:
将以下依赖添加到你的pom.xml
文件中:
<dependencies>
<!-- Jackson core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.0</version>
</dependency>
<!-- Jackson databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>
<!-- Jackson annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.13.0</version>
</dependency>
</dependencies>
Gradle:
将以下依赖添加到你的build.gradle
文件中:
dependencies {
implementation 'com.fasterxml.jackson.core:jackson-core:2.13.0'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0'
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.0'
}
创建Java对象模型
在使用Jackson之前,你需要创建一个Java对象模型,该模型表示你要序列化和反序列化的JSON数据。例如,以下是一个表示Person的简单Java类:
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
在这个示例中,我们使用了一个简单的Java Bean(具有私有字段、公共构造函数和getter/setter方法的类)来表示Person对象。
使用ObjectMapper进行序列化和反序列化
使用ObjectMapper
类,你可以轻松地将Java对象序列化为JSON字符串以及将JSON字符串反序列化为Java对象。以下是一个简单的示例:
序列化:
import com.fasterxml.jackson.databind.ObjectMapper;
public class Main {
public static void main(String[] args) {
ObjectMapper objectMapper = new ObjectMapper();
Person person = new Person("Alice", 30);
try {
String jsonString = objectMapper.writeValueAsString(person);
System.out.println(jsonString); // 输出:{"name":"Alice","age":30}
} catch (Exception e) {
e.printStackTrace();
}
}
}
反序列化:
import com.fasterxml.jackson.databind.ObjectMapper;
public class Main {
public static void main(String[] args) {
ObjectMapper objectMapper = new ObjectMapper();
String jsonString = "{\"name\":\"Alice\",\"age\":30}";
try {
Person person = objectMapper.readValue(jsonString, Person.class);
System.out.println("Name: " + person.getName() + ", Age: " + person.getAge()); // 输出:Name: Alice, Age: 30
} catch (Exception e) {
e.printStackTrace();
}
}
}
这些示例展示了如何使用Jackson库进行序列化和反序列化操作。在实际项目中,你可能需要根据需求对这些操作进行更多的配置和自定义,例如使用注解、自定义序列化器和反序列化器等。
高级特性
注解(如@JsonProperty, @JsonIgnore)
Jackson库提供了一系列注解,可以帮助你在序列化和反序列化过程中对字段和类进行配置。以下是一些常用注解的示例:
@JsonProperty
注解:
该注解用于指定 Java 属性与 JSON 属性之间的映射关系,常用的参数有:
value
:用于指定 JSON 属性的名称,当 Java 属性和 JSON 属性名称不一致时使用。access
:用于指定该属性的访问方式,常用的取值有JsonAccess.READ_ONLY
(只读),JsonAccess.WRITE_ONLY
(只写)和JsonAccess.READ_WRITE
(可读可写)。
public class Person {
@JsonProperty(value = "name")
private String fullName;
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String password;
// getters and setters
}
Person person = new Person();
person.setFullName("John Smith");
person.setPassword("123456");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(person);
// {"name":"John Smith"}
Person person2 = mapper.readValue(json, Person.class);
System.out.println(person2.getFullName());
// John Smith
System.out.println(person2.getPassword());
// null
@JsonIgnore
注解:
该注解用于禁用 Java 属性的序列化和反序列化。
public class Person {
private String fullName;
@JsonIgnore
private String password;
// getters and setters
}
Person person = new Person();
person.setFullName("John Smith");
person.setPassword("123456");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(person);
// {"fullName":"John Smith"}
Person person2 = mapper.readValue("{\"fullName\":\"John Smith\",\"password\":\"123456\"}", Person.class);
System.out.println(person2.getFullName());
// John Smith
System.out.println(person2.getPassword());
// null
@JsonFormat
注解:
该注解用于指定 Java 属性的日期和时间格式,常用的参数有:
shape
:用于指定日期和时间的格式,可选的取值有JsonFormat.Shape.STRING
(以字符串形式表示)和JsonFormat.Shape.NUMBER
(以时间戳形式表示)。pattern
:用于指定日期和时间的格式模板,例如"yyyy-MM-dd HH:mm:ss"
。
public class Person {
private String fullName;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
private Date birthDate;
// getters and setters
}
Person person = new Person();
person.setFullName("John Smith");
person.setBirthDate(new Date());
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(person);
// {"fullName":"John Smith","birthDate":"2022-05-16 10:38:30"}
Person person2 = mapper.readValue(json, Person.class);
System.out.println(person2.getFullName());
// John Smith
System.out.println(person2.getBirthDate());
// Mon May 16 10:38:30 CST 2022
@JsonInclude
注解:
该注解用于指定序列化 Java 对象时包含哪些属性,常用的参数有:
value
:用于指定包含哪些属性,可选的取值有JsonInclude.Include.ALWAYS
(始终包含)、JsonInclude.Include.NON_NULL
(值不为 null 时包含)、JsonInclude.Include.NON_DEFAULT
(值不为默认值时包含)、JsonInclude.Include.NON_EMPTY
(值不为空时包含)和JsonInclude.Include.CUSTOM
(自定义条件)。content
:用于指定自定义条件的实现类。
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Person {
private String fullName;
private Integer age;
// getters and setters
}
Person person = new Person();
person.setFullName("John Smith");
// person.setAge(null);
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(person);
// {"fullName":"John Smith"}
Person person2 = mapper.readValue("{\"fullName\":\"John Smith\",\"age\":null}", Person.class);
System.out.println(person2.getFullName());
// John Smith
System.out.println(person2.getAge());
// null
@JsonCreator
注解:
该注解用于指定反序列化时使用的构造方法或工厂方法。
public class Person {
private String fullName;
private Integer age;
@JsonCreator
public Person(@JsonProperty("fullName") String fullName, @JsonProperty("age") Integer age) {
this.fullName = fullName;
this.age = age;
}
// getters and setters
}
ObjectMapper mapper = new ObjectMapper();
Person person = mapper.readValue("{\"fullName\":\"John Smith\",\"age\":30}", Person.class);
System.out.println(person.getFullName());
// John Smith
System.out.println(person.getAge());
// 30
@JsonSetter
注解:
该注解用于指定反序列化时使用的方法,常用的参数有:
value
:用于指定 JSON 属性的名称,当方法名和 JSON 属性名称不一致时使用。
public class Person {
private String fullName;
private Integer age;
@JsonSetter("name")
public void setFullName(String fullName) {
this.fullName = fullName;
}
// getters and setters
}
ObjectMapper mapper = new ObjectMapper();
Person person = mapper.readValue("{\"name\":\"John Smith\",\"age\":30}", Person.class);
System.out.println(person.getFullName());
// John Smith
System.out.println(person.getAge());
// 30
@JsonGetter
注解:
该注解用于指定序列化时使用的方法,常用的参数有:
value
:用于指定 JSON 属性的名称,当方法名和 JSON 属性名称不一致时使用。
public class Person {
private String fullName;
private Integer age;
@JsonGetter("name")
public String getFullName() {
return fullName;
}
// getters and setters
}
@JsonAnySetter
注解:
该注解用于指定反序列化时使用的方法,用于处理 JSON 中未知的属性。
public class Person {
private String fullName;
private Map<String, Object> otherProperties = new HashMap<>();
@JsonAnySetter
public void setOtherProperties(String key, Object value) {
otherProperties.put(key, value);
}
// getters and setters
}
ObjectMapper mapper = new ObjectMapper();
Person person = mapper.readValue("{\"fullName\":\"John Smith\",\"age\":30}", Person.class);
System.out.println(person.getFullName());
// John Smith
System.out.println(person.getOtherProperties());
// {age=30}
@JsonAnyGetter
注解:
该注解用于指定序列化时使用的方法,用于处理 Java 对象中未知的属性。
public class Person {
private String fullName;
private Map<String, Object> otherProperties = new HashMap<>();
public void addOtherProperty(String key, Object value) {
otherProperties.put(key, value);
}
@JsonAnyGetter
public Map<String, Object> getOtherProperties() {
return otherProperties;
}
// getters and setters
}
Person person = new Person();
person.setFullName("John Smith");
person.addOtherProperty("age", 30);
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(person);
// {"fullName":"John Smith","age":30}
@JsonTypeInfo
注解:
该注解用于指定 Java 对象在序列化和反序列化时的类型信息,常用的参数有:
use
:用于指定类型信息的使用方式,可选的取值有JsonTypeInfo.Id.CLASS
(使用 Java 类的全限定名)、JsonTypeInfo.Id.NAME
(使用名称)和JsonTypeInfo.Id.NONE
(不使用类型信息)。include
:用于指定类型信息的包含方式,可选的取值有JsonTypeInfo.As.PROPERTY
(作为 JSON 属性)和JsonTypeInfo.As.EXTERNAL_PROPERTY
(作为外部属性)。property
:用于指定包含类型信息的属性名,当include
的值为JsonTypeInfo.As.PROPERTY
时使用。visible
:用于指定类型信息是否可见。
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Rectangle.class, name = "rectangle"),
@JsonSubTypes.Type(value = Circle.class, name = "circle")
})
public abstract class Shape {
// ...
}
public class Rectangle extends Shape {
// ...
}
public class Circle extends Shape {
// ...
}
Shape shape = new Rectangle();
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(shape);
// {"type":"rectangle"}
Shape shape2 = mapper.readValue(json, Shape.class);
System.out.println(shape2.getClass().getSimpleName());
// Rectangle
自定义序列化和反序列化
你可以创建自定义序列化器和反序列化器以自定义特定字段或类的序列化和反序列化行为。为此,请创建一个实现JsonSerializer
或JsonDeserializer
接口的类,并在需要自定义的字段或类上使用@JsonSerialize
和@JsonDeserialize
注解。例如:
public class CustomDateSerializer extends JsonSerializer<Date> {
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
@Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(dateFormat.format(value));
}
}
public class Person {
private String name;
@JsonSerialize(using = CustomDateSerializer.class)
private Date birthdate;
// ...其他代码...
}
使用JsonNode进行动态解析
你可以使用JsonNode
类来动态地解析和操作JSON数据。例如:
String jsonString = "{\"name\":\"Alice\",\"age\":30,\"address\":{\"street\":\"Main St\",\"city\":\"New York\"}}";
ObjectMapper objectMapper = new ObjectMapper();
JsonNode rootNode = objectMapper.readTree(jsonString);
String name = rootNode.get("name").asText(); // Alice
int age = rootNode.get("age").asInt(); // 30
String street = rootNode.get("address").get("street").asText(); // Main St
处理日期和时间类型
Jackson可以处理Java日期和时间类型,例如java.util.Date
和Java 8时间库中的类型。你可以通过配置ObjectMapper
来指定日期和时间格式,例如:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
处理泛型
Jackson可以处理泛型类型,例如List<T>
和Map<String, T>
。在反序列化时,你需要使用TypeReference
来指定泛型类型。例如:
String jsonString = "[{\"name\":\"Alice\",\"age\":30},{\"name\":\"Bob\",\"age\":25}]";
ObjectMapper objectMapper = new ObjectMapper();
List<Person> persons = objectMapper.readValue(jsonString, new TypeReference<List<Person>>() {});
使用模块扩展Jackson(如Java 8时间支持)
Jackson可以通过模块来扩展其功能。例如,你可以使用jackson-datatype-jsr310
模块为Jackson添加对Java 8时间库的支持。首先,将依赖添加到项目中:
Maven:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.13.0</version>
</dependency>
Gradle:
dependencies {
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.0'
}
然后,你需要注册模块到ObjectMapper
:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
现在,Jackson可以正确地处理Java 8时间库中的类型,例如LocalDate
、LocalTime
和Instant
。
总结
Jackson的优势和局限性
优势:
性能优异:Jackson在序列化和反序列化过程中表现出优秀的性能,通常比其他Java JSON库更快。 灵活性:通过注解、自定义序列化器/反序列化器等功能,Jackson提供了丰富的配置选项,允许你根据需求灵活地处理JSON数据。 易于使用:Jackson的API设计简洁明了,易于学习和使用。同时,官方文档和社区支持也非常丰富。 可扩展性:通过模块系统,你可以轻松地为Jackson添加新功能或与其他库进行集成。
局限性:
库大小:由于Jackson提供了许多功能和选项,它的库文件相对较大。在某些对程序大小有严格要求的场景中,这可能是一个问题。 学习曲线:虽然Jackson的基本功能易于学习,但要充分利用其高级特性,可能需要一定的学习成本。
建议和最佳实践
了解你的需求:在使用Jackson之前,请确保你了解项目的需求。针对具体需求,学习和使用相应的API和特性会更加高效。 遵循Java命名规范:使用标准的Java命名规范(如驼峰命名法)来命名字段和类。这将有助于Jackson自动处理JSON字段映射。 尽量避免循环引用:在Java对象模型中避免循环引用,因为这可能导致序列化过程中出现问题。如果确实存在循环引用,请使用 @JsonManagedReference
和@JsonBackReference
注解来解决。使用注解进行配置:在可能的情况下,使用注解来配置序列化和反序列化过程。这将使配置更加集中和易于理解。 优先使用对象模型:尽量使用Java对象模型来表示JSON数据,而非动态解析。这将使代码更加清晰和易于维护。
其他的JSON库
除了Jackson之外,还有其他一些流行的Java JSON处理库。以下是一些常见的库:
Gson:Gson是Google开发的一个Java库,用于将Java对象转换为JSON表示以及将JSON字符串转换为等效的Java对象。Gson的API简洁易用,性能也相当不错。官方网站:github.com/google/gson[2] Fastjson:Fastjson是Alibaba开发的一个高性能的JSON库。Fastjson提供了灵活的API和丰富的功能,同时注重性能优化。然而,它在安全性方面存在一些问题,因此在使用时需要谨慎。官方网站:github.com/alibaba/fas…[3] JSON-java(org.json):JSON-java库,也称为org.json库,是一个非常轻量级的JSON处理库。它提供了基本的JSON编码和解码功能,但不支持对象映射等高级功能。官方网站:github.com/stleary/JSO…[4] Moshi:Moshi是Square公司开发的一个现代化的JSON库,具有简单易用的API和良好的性能。Moshi支持Kotlin协程,并与Kotlin编程语言非常兼容。官方网站:github.com/square/mosh…[5] Boon:Boon是另一个高性能的JSON处理库。它具有易用的API,支持流式处理和速度优化。然而,Boon的社区和文档相对较少。官方网站:github.com/boonproject…[6]
这些库各有优缺点,选择哪个库取决于项目的具体需求和团队的熟悉程度。在实际项目中,你可能需要比较这些库的性能、功能、易用性等方面的差异,以找到最适合你的解决方案。
参考资料
[1]https://github.com/FasterXML/jackson-docs: https://github.com/FasterXML/jackson-docs[2]https://github.com/google/gson: https://github.com/google/gson[3]https://github.com/alibaba/fastjson: https://github.com/alibaba/fastjson[4]https://github.com/stleary/JSON-java: https://github.com/stleary/JSON-java[5]https://github.com/square/moshi: https://github.com/square/moshi[6]https://github.com/boonproject/boon: https://github.com/boonproject/boon
👉 欢迎加入小哈的星球 ,你将获得: 专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡 / 赠书福利
全栈前后端分离博客项目 2.0 版本完结啦, 演示链接:http://116.62.199.48/ ,新项目正在酝酿中。全程手摸手,后端 + 前端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,直到项目上线。目前已更新了239小节,累计38w+字,讲解图:1645张,还在持续爆肝中.. 后续还会上新更多项目,目标是将Java领域典型的项目都整一波,如秒杀系统, 在线商城, IM即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,已有1100+小伙伴加入(早鸟价超低)
1. 我的私密学习小圈子~
最近面试BAT,整理一份面试资料《Java面试BATJ通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。
获取方式:点“在看”,关注公众号并回复 Java 领取,更多内容陆续奉上。
PS:因公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。
点“在看”支持小哈呀,谢谢啦