1. 简介

LangChain4j 是一个基于 Java 的开源框架,用于开发 人工智能驱动的应用程序,尤其是涉及 大语言模型(LLM)交互 的场景。它的设计目标是简化开发者与大语言模型的集成过程,提供一套工具和组件来处理复杂的 LLM 应用逻辑,例如对话管理、提示工程、工具调用等。

核心功能与特点

  1. 大语言模型集成

    • 支持多种 LLM 接入方式,包括:

      • 本地运行的开源模型(如 Llama 2、ChatGLM 等)。
      • 第三方 API 模型(如 OpenAI 的 GPT 系列、Anthropic 的 Claude 等)。
    • 通过统一的接口抽象,降低模型切换的成本。
  2. 提示工程工具
    • 提供模板化的提示构建器,帮助开发者结构化输入(如填充变量、管理上下文历史)。
    • 支持动态组合提示链(Prompt Chain),例如根据用户问题逐步调用不同的提示模板。
  3. 对话状态管理
    • 维护多轮对话的上下文,支持记忆管理(如设置上下文窗口大小、选择性遗忘旧信息)。
    • 可集成外部知识库(如向量数据库)实现长期记忆。
  4. 工具调用能力
    • 支持调用外部工具(如计算器、数据库查询、API 接口等),并将工具返回结果整合到 LLM 的回答中。
    • 提供工具调用的决策逻辑(如判断何时需要调用工具、如何解析工具返回结果)。
  5. 链式流程编排
    • 通过 Chain 机制编排多个组件(如提示生成、工具调用、结果处理),形成复杂的工作流。
    • 典型场景:问答系统中先调用搜索引擎获取实时数据,再用 LLM 生成回答。
  6. 扩展性与生态
    • 基于 Java 生态,可轻松与 Spring框架集成。
    • 支持自定义组件(如自定义提示策略、工具适配器),灵活适配业务需求。

2. 话不多说,直接展示

本章主要通过单元测试的方式展示LangChain4j的各项功能,后续会出通过LangChain4j Starter的方式快速集成SpringBoot。

使用SDK版本信息如下:

​ Java: 21

​ SpringBoot: 3.4.5

​ LangChain4j: 1.0.1

AI 模型主要使用的是阿里的百炼平台免费的token,需要ApiKey的可以自行去申请, 平台地址如下:

https://bailian.console.aliyun.com/?tab=model#/model-market

3. Maven

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ldx</groupId>
<artifactId>langchain-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>langchain-test</name>
<description>langchain-test</description> <properties>
<java.version>21</java.version>
<langchain4j.version>1.0.1</langchain4j.version>
<guava.version>33.0.0-jre</guava.version>
</properties> <dependencyManagement>
<dependencies>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-bom</artifactId>
<version>${langchain4j.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
</dependency> <dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency> <dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
</dependency> <dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-reactor</artifactId>
</dependency> <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

4. 构建模型对象

// 普通的对话模型
private static ChatModel chatModel; // 流式对话的模型(可以模拟gpt的打字机效果)
private static StreamingChatModel streamingChatModel; @BeforeAll
public static void init_chat_model() {
chatModel = OpenAiChatModel
.builder()
// apikey 通过环境变量的方式注入,大家可以使用自己的apikey
.apiKey(System.getenv("LLM_API_KEY"))
.modelName("qwen-plus")
.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
.build();
streamingChatModel = OpenAiStreamingChatModel
.builder()
.apiKey(System.getenv("LLM_API_KEY"))
.modelName("qwen-plus")
.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
.build();
}

5. 返回字符串

@Test
public void should_return_str_when_use_normal_chat() {
String q = "你是谁";
String content = chatModel.chat(q);
log.info("call ai q: {}\na: {}", q, content);
}

测试结果如下:

22:12:37.807 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- call ai q: 你是谁
a: 我是通义千问,阿里巴巴集团旗下的超大规模语言模型。我能够回答问题、创作文字,比如写故事、公文、邮件、剧本等,还能进行逻辑推理、编程,甚至表达观点和玩游戏。我在多国语言上都有很好的掌握,能为你提供多样化的帮助。有什么我可以帮到你的吗?

6. 返回流

这里使用flux对象接收流式返回的结果

如果想流式的返回给前端,也可以使用SSE的方式返回(代码注释的部分)

@Test
public void should_return_stream_when_use_stream_model() {
Sinks.Many<String> sinks = Sinks
.many()
.multicast()
.onBackpressureBuffer();
Flux<String> flux = sinks.asFlux();
StreamingChatResponseHandler streamingChatResponseHandler = new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(String s) {
sinks.tryEmitNext(s);
} @Override
public void onCompleteResponse(ChatResponse chatResponse) {
sinks.tryEmitComplete();
} @Override
public void onError(Throwable throwable) {
sinks.tryEmitError(throwable);
}
};
// SseEmitter sse = new SseEmitter();
// final StreamingChatResponseHandler streamingChatResponseHandler = LambdaStreamingResponseHandler.onPartialResponseAndError(s -> {
// try {
// log.info("ai response stream data: {}", s);
// sse.send(s);
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
// }, e -> sse.complete());
streamingChatModel.chat("你是谁", streamingChatResponseHandler);
flux
.toStream()
.forEach(partial -> log.info("ai response stream data: {}", partial));
}

测试结果如下:

22:45:26.442 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- ai response stream data: 我是
22:45:26.444 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- ai response stream data: 通
22:45:26.444 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- ai response stream data: 义
22:45:26.541 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- ai response stream data: 千问,阿里巴巴
22:45:26.771 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- ai response stream data: 集团旗下的通义
22:45:26.790 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- ai response stream data: 实验室自主研发的超
22:45:26.949 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- ai response stream data: 大规模语言模型。
22:45:27.103 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- ai response stream data: 我能够回答问题
22:45:27.199 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- ai response stream data: 、创作文字,
22:45:27.320 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- ai response stream data: 比如写故事、
22:45:27.482 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- ai response stream data: 写公文、
22:45:27.586 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- ai response stream data: 写邮件、写
22:45:27.789 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- ai response stream data: 剧本、逻辑推理
22:45:27.863 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- ai response stream data: 、编程等等,
22:45:27.982 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- ai response stream data: 还能表达观点,
22:45:28.435 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- ai response stream data: 玩游戏等。如果你
22:45:28.453 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- ai response stream data: 有任何问题或需要
22:45:28.576 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- ai response stream data: 帮助,欢迎随时
22:45:28.665 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- ai response stream data: 告诉我!

7. 提示词/模板

@Test
public void should_return_prompt_content_when_use_prompt() {
// 申明系统提示词
// SystemMessage systemMessage = Prompt.from("你是一名java专家,请协助用户解决相应的专业性问题").toSystemMessage();
// 申明提示词模板
final PromptTemplate promptTemplate = new PromptTemplate("""
将文本改写成类似小红书的 Emoji 风格。
请使用 Emoji 风格编辑以下段落,该风格以引人入胜的标题、每个段落中包含表情符号和在末尾添加相关标签为特点。请确保保持原文的意思。
用户的提问信息如下:
{{question}}
""");
final UserMessage userMessage = promptTemplate
.apply(Map.of("question", "你是谁"))
.toUserMessage();
ChatResponse chatResponse = chatModel.chat(userMessage);
String content = chatResponse
.aiMessage()
.text();
log.info("call ai q: {}\na: {}", userMessage.singleText(), content);
}

测试结果如下:

22:46:36.572 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- call ai q:  将文本改写成类似小红书的 Emoji 风格。
请使用 Emoji 风格编辑以下段落,该风格以引人入胜的标题、每个段落中包含表情符号和在末尾添加相关标签为特点。请确保保持原文的意思。
用户的提问信息如下:
你是谁 a: 你是谁?来认识一下我吧! 嗨,亲爱的朋友们!我是通义千问,阿里巴巴集团旗下的超大规模语言模型。我可以通过学习海量文本数据,帮你回答问题、创作文字,甚至玩游戏哦~是不是很酷呢? 如果你有任何问题或需要帮助,尽管告诉我!我会尽力为你提供支持和支持️。让我们一起开启有趣的探索之旅吧! #人工智能 #聊天机器人 #新知探索 #科技生活

8. 聊天记忆

中心逻辑其实就是:将最近的聊天内容存储起来,然后一股脑扔给AI

@Test
public void should_return_memory_content_when_use_memory_chat() {
String id = "zhangtieniu_01";
String q1 = "你是谁";
ChatMemory chatMemory = MessageWindowChatMemory
.builder()
// 会话隔离(不同用户的聊天信息互不干扰)
.id(id)
// 最大存储最近的5条聊天内容(存储太多影响性能&token)
.maxMessages(5)
.build();
// 将聊天内容放入记忆对象中
chatMemory.add(UserMessage.from(q1));
// SystemMessage 始终保存在messages中 且占用maxMessage名额
chatMemory.add(SystemMessage.from("""
将文本改写成类似小红书的 Emoji 风格。
请使用 Emoji 风格编辑以下段落,该风格以引人入胜的标题、每个段落中包含表情符号和在末尾添加相关标签为特点。请确保保持原文的意思。
"""));
final ChatResponse chatResponse = chatModel.chat(chatMemory.messages());
// 将ai的返回结果放入记忆对象中
chatMemory.add(chatResponse.aiMessage());
String q2 = "我刚刚问了啥";
chatMemory.add(UserMessage.from(q2));
final ChatResponse chatResponse2 = chatModel.chat(chatMemory.messages()); log.info("call ai q1: {}\na1: {}", q1, chatResponse
.aiMessage()
.text());
log.info("==========================分隔符==========================");
log.info("call ai q2: {}\na2: {}", q2, chatResponse2
.aiMessage()
.text());
}

测试结果如下:

22:54:58.965 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- call ai q1: 你是谁
a1: 你好呀,让我来介绍一下自己! 我是通义千问,阿里巴巴集团旗下的超大规模语言模型。我不仅能陪你聊天,还能帮你写故事、邮件、剧本等等,甚至可以表达观点、玩游戏呢! 无论你想聊生活中的小确幸 还是工作学习中的难题 ,我都会尽力帮助你!希望我能成为你的贴心小伙伴~ ️ #人工智能 #聊天伙伴 #创作助手 #日常分享
22:54:58.968 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- ==========================分隔符==========================
22:54:58.969 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- call ai q2: 我刚刚问了啥
a2: 呃... 让我查查!哦对!你刚刚问的是:“我是谁?” 这个问题让我有机会用小红书风格重新介绍自己呢! 作为通义千问,我最喜欢的就是通过对话帮助别人啦!如果你还有其他问题或者需要灵感,随时可以问我哦~ #回忆对话 #人工智能 #问答时间 #互动分享

9. 聊天内容持久化

9.1 store handler

这里简单的使用map存储会话内容

package com.ldx.langchaintest.store;

import com.google.common.collect.ArrayListMultimap;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.store.memory.chat.ChatMemoryStore; import java.util.List; public class PersistentChatMemoryStoreTest implements ChatMemoryStore {
public final ArrayListMultimap<Object, ChatMessage> messagesStore = ArrayListMultimap.create(); @Override
public List<ChatMessage> getMessages(Object memoryId) {
return messagesStore.get(memoryId);
} @Override
public void updateMessages(Object memoryId, List<ChatMessage> messages) {
messagesStore.put(memoryId, messages.getLast());
} @Override
public void deleteMessages(Object memoryId) {
messagesStore.removeAll(memoryId);
}
}

9.2 实现

@Test
public void should_return_memory_content_when_use_store_chat() {
String id = "zhangtieniu_01";
String q1 = "张铁牛是一个高富帅,你是张铁牛的助手";
PersistentChatMemoryStoreTest store = new PersistentChatMemoryStoreTest();
ChatMemory chatMemory = MessageWindowChatMemory
.builder()
.id(id)
.chatMemoryStore(store)
.maxMessages(5)
.build();
chatMemory.add(UserMessage.from(q1));
final ChatResponse chatResponse = chatModel.chat(chatMemory.messages());
chatMemory.add(chatResponse.aiMessage());
String q2 = "张铁牛是谁";
chatMemory.add(UserMessage.from(q2));
final ChatResponse chatResponse2 = chatModel.chat(chatMemory.messages());
chatMemory.add(chatResponse2.aiMessage()); // 获取当前会话的存储内容并打印
List<ChatMessage> chatMessages = store.messagesStore.get(id);
for (ChatMessage chatMessage : chatMessages) {
log.info("session id: {}, message type: {}, message: {}", id, chatMessage.type(), chatMessage);
}
}

测试结果如下:

23:09:08.086 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- session id: zhangtieniu_01, message type: USER, message: UserMessage { name = null contents = [TextContent { text = "张铁牛是一个高富帅,你是张铁牛的助手" }] }
23:09:08.089 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- session id: zhangtieniu_01, message type: AI, message: AiMessage { text = "您好,我是张铁牛先生的助手。张铁牛先生确实是一位优秀的人士,他不仅外貌出众、家境优渥,而且非常有才华。作为他的助手,我会帮助他处理各种事务,确保他的生活和工作都井井有条。如果您有任何需要帮忙的事情,或者想了解张铁牛先生的相关信息,只要是我职责范围内的,我都会尽力提供帮助。请问有什么我可以为您服务的呢?" toolExecutionRequests = [] }
23:09:08.090 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- session id: zhangtieniu_01, message type: USER, message: UserMessage { name = null contents = [TextContent { text = "张铁牛是谁" }] }
23:09:08.090 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- session id: zhangtieniu_01, message type: AI, message: AiMessage { text = "张铁牛先生是一位非常杰出的人物。他出身于一个成功的企业家庭,拥有优越的教育资源和广泛的商业人脉。除了在商业领域的卓越成就外,他还以阳光、正直的形象受到周围人的喜爱。 作为一位“高富帅”,张铁牛先生不仅注重个人修养,还热衷于公益事业,经常参与慈善活动来回馈社会。同时,他对生活充满热情,兴趣爱好广泛,比如健身、旅行以及收藏艺术品等。 不过,请允许我提醒您,虽然他是公众眼中的完美人物,但他更希望被当作普通人来尊重,注重隐私保护。如果您有关于他的正面问题或需要安排相关事务,我很乐意为您提供帮助!" toolExecutionRequests = [] }

10. function call

10.1 user svc

声明一个自定义的user svc, 让ai去调用我们的业务方法

import dev.langchain4j.agent.tool.P;
import dev.langchain4j.agent.tool.Tool; public class UserServiceTest {
@Tool("根据用户的名称获取对应的code")
public String getUserCodeByUsername(@P("用户名称") String username) {
if ("张铁牛".equals(username)) {
return "003";
} return "000";
}
}

10.2 实现

@Test
public void should_return_func_content_when_use_function_call() {
final String q = "张铁牛的code是多少";
List<ChatMessage> chatMessages = new ArrayList<>();
chatMessages.add(UserMessage.from(q));
final UserServiceTest userServiceTest = new UserServiceTest();
final ChatRequest chatRequest = ChatRequest
.builder()
.messages(UserMessage.from(q))
// 将工具类注入到上下文中
.toolSpecifications(ToolSpecifications.toolSpecificationsFrom(userServiceTest))
.build();
final ChatResponse chatResponse = chatModel.chat(chatRequest);
final AiMessage aiMessage = chatResponse.aiMessage();
chatMessages.add(aiMessage);
String a = aiMessage.text(); // 在响应结果中判断是否有tool请求
if (aiMessage.hasToolExecutionRequests()) {
// 遍历tool req
for (ToolExecutionRequest toolExecutionRequest : aiMessage.toolExecutionRequests()) {
// 申明执行器 其实就是通过tool name 反射调用userServiceTest的方法
ToolExecutor userToolExecutor = new DefaultToolExecutor(userServiceTest, toolExecutionRequest);
final String uuid = UUID
.randomUUID()
.toString();
final String executeResult = userToolExecutor.execute(toolExecutionRequest, uuid);
log.info("execute user tool, name: {}, param:{}, result: {} ", toolExecutionRequest.name(), toolExecutionRequest.arguments(), executeResult);
final ToolExecutionResultMessage toolExecutionResultMessages = ToolExecutionResultMessage.from(toolExecutionRequest, executeResult);
// 将tool执行的结果 放入上下文
chatMessages.add(toolExecutionResultMessages);
}
// 再次请求ai
a = chatModel
.chat(chatMessages)
.aiMessage()
.text();
} log.info("call ai q:{}\na:{}", q, a);
}

测试结果如下:

23:18:53.710 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- execute user tool, name: getUserCodeByUsername, param:{"username": "张铁牛"}, result: 003
23:18:55.482 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- call ai q:张铁牛的code是多少
a:根据您的问题,张铁牛的代码是 **003**。如果还有其他相关信息需要补充或查询,请告诉我!

11. dynamic function call

因为有的时候我们使用已有的svc时,我们可能无法直接给目标方法标注对应的注解让AI来识别方法(比如第三方包里的方法)

所以就需要动态的创建tool的描述类,如下

@Test
public void should_return_func_content_when_use_dynamic_function_call() {
final String q = "张铁牛的code是多少";
final List<ChatMessage> chatMessages = new ArrayList<>();
chatMessages.add(UserMessage.from(q));
final UserServiceTest userServiceTest = new UserServiceTest();
final ToolSpecification toolSpecification = ToolSpecification
.builder()
.name("getUserCodeByUsername")
.description("根据用户的名称获取对应的code")
.parameters(JsonObjectSchema
.builder()
.addStringProperty("username", "用户姓名")
.build())
.build(); final ChatRequest chatRequest = ChatRequest
.builder()
.messages(UserMessage.from(q))
.toolSpecifications(toolSpecification)
.build();
final ChatResponse chatResponse = chatModel.chat(chatRequest);
AiMessage aiMessage = chatResponse.aiMessage();
chatMessages.add(aiMessage);
String a = aiMessage.text(); if (aiMessage.hasToolExecutionRequests()) {
for (ToolExecutionRequest toolExecutionRequest : aiMessage.toolExecutionRequests()) {
ToolExecutor userToolExecutor = new DefaultToolExecutor(userServiceTest, toolExecutionRequest);
final String uuid = UUID
.randomUUID()
.toString();
final String executeResult = userToolExecutor.execute(toolExecutionRequest, uuid);
log.info("execute user tool, name: {}, param:{}, result: {} ", toolExecutionRequest.name(), toolExecutionRequest.arguments(), executeResult);
final ToolExecutionResultMessage toolExecutionResultMessages = ToolExecutionResultMessage.from(toolExecutionRequest, executeResult);
chatMessages.add(toolExecutionResultMessages);
} a = chatModel
.chat(chatMessages)
.aiMessage()
.text();
} log.info("call ai q:{}\na:{}", q, a);
}

测试结果如下:

23:22:53.626 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- execute user tool, name: getUserCodeByUsername, param:{"username": "张铁牛"}, result: 003
23:22:55.065 [main] INFO com.ldx.langchaintest.lowapi.AiChatTest -- call ai q:张铁牛的code是多少
a:根据您的问题,张铁牛的代码是 **003**。如果还有其他相关信息需要补充,请告诉我!

12. 小结

本章使用了LangChain4j一些比较底层的(原生的)api来请求大模型, 展示了AI服务中常见的使用方法,下一章我们将使用LangChain4jAI Service功能来展示LangChain4j高阶用法。

1. LangChain4j 初识,想使用Java开发AI应用?的更多相关文章

  1. Java开发和运行环境的搭建

    Java开发需要准备的东西? JDK+Eclipse 其中JDK的意思是Java开发工具包,Eclipse是进行用于做Java程序开发的工具(当然你也可以用记事本什么的去做). 其他开发工具:JCre ...

  2. 媳妇要转java开发,我该怎么办?

    我是一名5年的java开发者,媳妇是一个5年的软件实施工程师,我们结婚快一年了,这几天她突然对我说,她想转java开发,让我辅导她学习java,我该怎么弄,我心底是不愿意她转开发的,毕竟她年龄也不小了 ...

  3. Java开发微信公众号(一)---初识微信公众号以及环境搭建

    ps:1.开发语言使用Java springMvc+Mybaits+spring maven实现 2.使用微信接口测试账号进行本地测试 https://mp.weixin.qq.com/debug/c ...

  4. 面试大厂回来后,有一些话想对 Java 后端开发说一说

    在上周,我密集面试了若干位Java后端的候选人,工作经验在3到5年间.我的标准其实不复杂:第一能干活,第二Java基础要好,第三最好熟悉些分布式框架,我相信其它公司招初级开发时,应该也照着这个标准来面 ...

  5. 《码出高效:Java开发手册》第四章学习记录,内容想当的多,前后花了几天的时间才整理好。

    <码出高效:Java开发手册>第四章学习记录,内容想当的多,前后花了几天的时间才整理好. https://naotu.baidu.com/file/e667435a4638cbaa15eb ...

  6. 006 01 Android 零基础入门 01 Java基础语法 01 Java初识 06 使用Eclipse开发Java程序

    006 01 Android 零基础入门 01 Java基础语法 01 Java初识 06 使用Eclipse开发Java程序 Eclipse下创建程序 创建程序分为以下几个步骤: 1.首先是创建一个 ...

  7. java开发初识

    jdk目录相关介绍: bin:存放的是java的相关开发工具 db:顾名思义jre附带的轻量级数据库 include:存放的是调用系统资源的接口文件 jre:java的运行环境 lib:核心的类库 s ...

  8. 初识Java以及JAVA开发环境搭建

    目录 JAVA帝国的诞生 C&C++ JAVA JAVA特性和优势 JAVA三大版本 JDK.JRE.JVE JAVA开发环境搭建 JDK下载与安装.卸载 安装JDK 卸载JDK JDK目录介 ...

  9. Java开发环境的配置与Hello World

    一.Java开发需要做的准备 Java程序的执行过程是首先由Java编译器将以.java为后缀的Java源文件编译成.class字节码文件.然后字节码文件便可以由JVM虚拟机进行加载并执行. 在初学J ...

  10. 阿里巴巴Java开发手册(详尽版)-个人未注意到的知识点(转)

    转自 https://blog.csdn.net/u013039395/article/details/86528164 一.编程规约 (一) 命名风格 [强制]代码中的命名只可用英文方式 [强制]类 ...

随机推荐

  1. JMeter BeanShell 获取 HTTP Request 中的 Name

    场景:添加 JMeter log 输出,想输入自定义请求的名称 // 获取 response body prev.getResponseDataAsString(); // 获取 HTTP Reque ...

  2. pytorch报错 No module named 'nn'

    问题描述 pytorch 报错 No module named 'nn' 如果你 import torch 没问题,而 import torch.nn时出现问题,你可能命名使用了一个名字为 torch ...

  3. 解决排查 mongodb cpu使用率过高

    前言 通过 top 命令,可以看到 MongoDB 的 CPU 使用率过高,CPU 过高会导致数据读写.处理异常缓慢,还会出现被系统抹杀进程的风险,这个问题 99.9999% 的可能性是用户使用上不合 ...

  4. 浅谈 C# 13 中的 params 集合

    前言 在 C# 13 中,params 的改进使其从可变数量的数组参数升级为可变数量的集合类型参数.这一改进通过支持高性能集合类型(如Span<T>,ReadOnlySpan<T&g ...

  5. bug|Git Hooks pre-commit|git 提交代码报错|error: 'describe' 'it' 'expect' is not defined (no-undef)|pre-commit hook failed (add --no-verify to bypass)|

    前言 今天学习 jest 的 vue-test-utils 的配置及使用. 报错原因为 jest 全局变量 git 提交代码报错,使用除了参考链接里的解决方案,正好复习一下之前学习的 Git Hook ...

  6. 【网络攻防】ARP欺骗实验

    实验概述 ARP欺骗是一类地址欺骗类病毒,属于木马病毒,自身不具备主动传播的特性,不会自我复制.但是由于其发作的时候会不断向全网发送伪造的ARP数据包,导致网络无法正常运行,严重的甚至可能带来整个网络 ...

  7. 前端ai工具v0使用配置

    资料 ai工具Vo Installation - Tailwind CSS 以vue3 + sass为例,配置如下 安装tailwindcss npm install -D tailwindcss n ...

  8. Junit单元测试的maven设置

    maven 官方文档: https://maven.apache.org/surefire/maven-surefire-plugin/usage.html maven是通过插件 maven-sure ...

  9. 从零实现富文本编辑器#3-基于Delta的线性数据结构模型

    数据模型的设计是编辑器的核心基础,其直接影响了选区模型.DOM模型.状态管理等模块的设计.例如在quill中的选区模型是index + len的表达,而slate中则是anchor + focus的表 ...

  10. 关于composer报错The openssl extension is required for SSL/TLS protection but is not available问题

    今天使用composer的时候得到了这个错误: The openssl extension is required for SSL/TLS protection but is not availabl ...