Spring 更简单的读取和存储对象

 前言:

📕作者简介:热爱编程的小七,致力于C、Java、Python等多编程语言,热爱编程和长板的运动少年!

📘相关专栏Java基础语法,JavaEE初阶,数据库,数据结构和算法系列等,大家有兴趣的可以看一看。

😇😇😇有兴趣的话关注博主一起学习,一起进步吧!

一、存储 Bean 对象

1.1添加注解存储 Bean 对象✍️

想要将对象存储在 Spring 中,有两种注解类型可以实现:✍️
1. 类注解:

@Controller:【控制器】校验参数的合法性(安检系统)

@Service:【服务】业务组装(客服中心)

@Repository:【数据持久层】实际业务处理(实际办理的业务)

@Component:【组件】工具类层

@Configuration:【配置层】配置

(可同时使用注解和XML存储Bean)
2. 方法注解:@Bean。

1.1.1 @Controller(控制器存储)

//将对象存储到Spring容器中
@Controller
public class UserController {public void sayHi(){System.out.println("Hi,UserController!");}
}

此时我们先使用之前读取对象的方式来读取上面的 UserController 对象,如下代码所示:

public class App {public static void main(String[] args) {//1.得到Spring上下文ApplicationContext contest=new ClassPathXmlApplicationContext("spring-config.xml");//2.得到bean对象UserController userController=contest.getBean("userController",UserController.class);//3.使用bean对象userController.sayHi();}
}

1.1.2 @Service(服务存储)

//将对象存储到Spring容器中
@Service
public class UserService {public void sayHi(){System.out.println("Hi,UserService!");}
}

读取 bean 的代码:

public class App {public static void main(String[] args) {//1.得到Spring上下文ApplicationContext contest=new ClassPathXmlApplicationContext("spring-config.xml");//2.得到bean对象UserService userService=contest.getBean("userService",UserService.class);//3.使用bean对象userService.sayHi();}
}

1.1.3 @Repository(仓库存储)

@Repository
public class UserRepository {public void sayHi(){System.out.println("Hi,UserRepository!");}
}

读取 bean 的代码:

public class App {public static void main(String[] args) {//1.得到Spring上下文ApplicationContext contest=new ClassPathXmlApplicationContext("spring-config.xml");//2.得到bean对象UserRepository userRepository=contest.getBean("userRepository",UserRepository.class);//3.使用bean对象userRepository.sayHi();}
}

1.1.4 @Component(组件存储)

@Component
public class User {public void sayHi(){System.out.println("Hi User!");}
}

读取 bean 的代码: 

public class App {public static void main(String[] args) {//1.得到Spring上下文ApplicationContext contest=new ClassPathXmlApplicationContext("spring-config.xml");//2.得到bean对象User user=contest.getBean("user",User.class);//3.使用bean对象user.sayHi();}
}

1.1.5 @Configuration(配置存储)

@Configuration
public class User {public void sayHi(){System.out.println("Hi User!");}
}

读取 bean 的代码:

public class App {public static void main(String[] args) {//1.得到Spring上下文ApplicationContext contest=new ClassPathXmlApplicationContext("spring-config.xml");//2.得到bean对象User user=contest.getBean("user",User.class);//3.使用bean对象user.sayHi();}
}

1.2为什么需要这么多类注解?

既然功能是一样的,为什么需要这么多的类注解呢?

原因就是让程序员看到类注解之后,就能直接了解当前类的用途,比如:

  • @Controller:表示的是业务逻辑层;
  • @Servie:服务层;
  • @Repository:持久层;
  • @Configuration:配置层。

程序的工程分层,调用流程如下:

1.2.1类注解之间的关系

查看 @Controller / @Service / @Repository / @Configuration 等注解的源码发现: 

其实这些注解 都有 个注解 @Component,说明它们本身就是属于 @Component 的“ 类”。

1.2.2Bean 命名规则

通过上述示例,我们可以看出,通常我们 bean 使用的都是标准的大驼峰命名, 读取的时候小写就可以获取到 bean 了,如下图所示: 

然而 ,当我们首字母和第二个字母都是大写时,就不能正常读取到 bean 了,如下图所示: 

这个时候,我们就要查询 Spring 关于 bean 存储时生成的命名规则了。

在IDEA中按两次 shift 键可以查询关键字

最后找到了 bean 对象的命名规则的方法: 它使用的是 JDK Introspector 中的 decapitalize 方法,源码如下: 

public static String decapitalize(String name) {if (name == null || name.length() == 0) {return name;}
// 如果第一个字母和第二个字母都为大写的情况,是把 bean 的首字母也大写存储了if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&Character.isUpperCase(name.charAt(0))) {return name;}
// 否则就将首字母小写char chars[] = name.toCharArray();chars[0] = Character.toLowerCase(chars[0]);return new String(chars);
}

所以对于上面报错的代码,我们只要改为以下代码就可以正常运行了:

总结:

如果首字母是大写,第二个字母是小写,那么Bean的名称就是类名小写。

如果不满足首字母大写和第二个字母小写的情况,那么Bean的名称就是原类名。

1.3方法注解 @Bean

类注解是添加到某个类上的,而方法注解是放到某个方法上的,如以下代码的实现: 

public class User {private int id;private String name;public void setId(int id){this.id=id;}public void setName(String name){this.name=name;}public String toString(){return "{id="+id+",name="+name+"]";}public void sayHi(){System.out.println("Hi User!");}
}public class Users {@Beanpublic User users(){User user=new User();user.setId(1);user.setName("张三");return user;}
}

 然而,当我们写完以上代码,尝试获取 bean 对象中的 user1 时却发现,根本获取不到:

public class App {public static void main(String[] args) {//1.得到Spring上下文ApplicationContext contest=new ClassPathXmlApplicationContext("spring-config.xml");//2.得到bean对象User user=contest.getBean("user",User.class);//3.使用bean对象System.out.println(user);}
}

这是为什么呢?

1.3.1方法注解要配合类注解使用

在 Spring 框架的设计中,方法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中,如下代码所示: 

@Component
public class Users {@Beanpublic User user1(){User user=new User();user.setId(1);user.setName("张三");return user;}
}
public class App {public static void main(String[] args) {//1.得到Spring上下文ApplicationContext contest=new ClassPathXmlApplicationContext("spring-config.xml");//2.得到bean对象User user=(User)contest.getBean("user1");//3.使用bean对象System.out.println(user.toString());}
}

1.3.2重命名 Bean

@Bean获取时的注意事项:@Bean的默认命名 = 方法名

可以通过设置 name 属性给 Bean 对象进行重命名操作,如下代码所示: 

@Component
public class Users {@Bean(name = "u1")public User user1(){User user=new User();user.setId(1);user.setName("张三");return user;}
}

此时使用原来的类名首字母小写是否能正确获取到对象呢?

默认命名注意事项:当@Bean重命名之后,那么默认的使用方法名获取Bean对象的方式就不能使用了。

同样我们可以重命名多个值,如下代码所示:

@Component
public class Users {//通过花括号包起来@Bean(name = {"u1", "u2"})public User user1(){User user=new User();user.setId(1);user.setName("张三");return user;}
}

 并且 name={} 可以省略,如下代码所示:

@Component
public class Users {//通过花括号包起来@Bean({"u1", "u2"})public User user1(){User user=new User();user.setId(1);user.setName("张三");return user;}
}

注意事项:如果多个Bean使用相同的名称,那么程序执行时不会报错,但是第一个Bean之后的对象不会被存放到容器中,也就是只有在第一次创建Bean的时候会将对应的Bean名称关联起来,后续再有相同的名称的Bean存储的时候,容器会自动忽略。 


二、获取 Bean 对象(对象装配)

获取 bean 对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注入

对象装配(对象注入)的实现方法以下 3 种:
1. 属性注入
2. 构造方法注入
3. Setter 注入

2.1属性注入

属性注入是使用 @Autowired 实现的,将 Service 类注入到 Controller 类中。

//将对象存储到Spring容器中
@Service
public class UserService {public User getUser(){//伪代码的实现User user=new User();user.setId(1);user.setName("张三");return user;}
}
//将对象存储到Spring容器中
@Controller
public class UserController {@Autowiredprivate UserService userService;public User getUser(){return userService.getUser();}
}
public class App {public static void main(String[] args) {//1.得到Spring上下文ApplicationContext contest=new ClassPathXmlApplicationContext("spring-config.xml");//2.得到bean对象UserController userController=contest.getBean("userController",UserController.class);//3.使用bean对象System.out.println(userController.getUser().toString());}
}

最终结果如下:

属性注入的核心实现如下:

2.1.1属性注入的优点

属性注入最大的优点就是实现简单、使用简单,只需要给变量上添加一个注解(@Autowired),就可以在不 new 对象的情况下,直接获得注入的对象了(这就是 DI 的功能和魅力所在),所以它的优点就是使用简单。

2.1.2属性注入的缺点

然而,属性注入虽然使用简单,但也存在着很多问题,甚至编译器 Idea 都会提醒你“不建议使用此注入方式”,Idea 的提示信息如下:

image.png

属性注入的缺点主要包含以下 3 个:

  1. 功能性问题:无法注入一个不可变的对象(final 修饰的对象);
  2. 通用性问题:只能适应于 IoC 容器;
  3. 设计原则问题:更容易违背单一设计原则。

2.1.2.1缺点1:功能性问题

使用属性注入无法注入一个不可变的对象(final 修饰的对象),如下图所示:

image.png

原因也很简单:在 Java 中 final 对象(不可变)要么直接赋值,要么在构造方法中赋值,所以当使用属性注入 final 对象时,它不符合 Java 中 final 的使用规范,所以就不能注入成功了。

PS:如果要注入一个不可变的对象,要怎么实现呢?使用下面的构造方法注入即可。

2.1.2.2缺点2:通用性问题

使用属性注入的方式只适用于 IoC 框架(容器),如果将属性注入的代码移植到其他非 IoC 的框架中,那么代码就无效了,所以属性注入的通用性不是很好。

2.1.2.3缺点3:设计原则问题

使用属性注入的方式,因为使用起来很简单,所以开发者很容易在一个类中同时注入多个对象,而这些对象的注入是否有必要?是否符合程序设计中的单一职责原则?就变成了一个问题。 但可以肯定的是,注入实现越简单,那么滥用它的概率也越大,所以出现违背单一职责原则的概率也越大。 注意:这里强调的是违背设计原则(单一职责)的可能性,而不是一定会违背设计原则,二者有着本质的区别。

2.2构造方法注入 

构造方法注入是在类的构造方法中实现注入,如下代码所示:

//将对象存储到Spring容器中
@Controller
public class UserController {private UserService userService;public User getUser(){return userService.getUser();}@Autowiredpublic UserController(UserService userService){this.userService=userService;}
}

注意:如果只有一个构造方法,那么 @Autowired 注解可以省略,如下图所示:

 

但是如果类中有多个构造方法,那么需要添加上 @Autowired 来明确指定到底使用哪个构造方法,否则程序会报错,如下图所示: 

2.2.1构造方法注入的优点

  1. 可注入不可变对象;
  2. 注入对象不会被修改;
  3. 注入对象会被完全初始化;
  4. 通用性更好。

2.2.1.1优点1:注入不可变对象

使用构造方法注入可以注入不可变对象,如下代码所示:

image.png

2.2.1.2 优点2:注入对象不会被修改

构造方法注入不会像 Setter 注入那样,构造方法在对象创建时只会执行一次,因此它不存在注入对象被随时(调用)修改的情况。

2.2.1.3优点3:完全初始化

因为依赖对象是在构造方法中执行的,而构造方法是在对象创建之初执行的,因此被注入的对象在使用之前,会被完全初始化,这也是构造方法注入的优点之一。

2.2.1.4优点4:通用性更好

构造方法和属性注入不同,构造方法注入可适用于任何环境,无论是 IoC 框架还是非 IoC 框架,构造方法注入的代码都是通用的,所以它的通用性更好。

2.3 Setter 注入 

Setter 注入和属性的 Setter 方法实现类似,只不过在设置 set 方法的时候需要加上 @Autowired 注解,如下代码所示:

@Controller
public class UserController {private UserService userService;public User getUser(){return userService.getUser();}/* public UserController(UserService userService){this.userService=userService;}*/@Autowiredpublic void setUserService(UserService userService){this.userService=userService;}
}

注意:若不加 @Autowired 注解会报错

2.3.1Setter注入的优点

  • 完全符合单一职责的设计原则,因为每一个 Setter 只针对一个对象

2.3.2Setter注入的缺点

  1. 不能注入不可变对象(final 修饰的对象);
  2. 注入的对象可被修改。

2.3.2.1缺点1:不能注入不可变对象

使用 Setter 注入依然不能注入不可变对象,比如以下注入会报错:

image.png

 2.3.2.2缺点2:注入对象可被修改

Setter 注入提供了 setXXX 的方法,意味着你可以在任何时候、在任何地方,通过调用 setXXX 的方法来改变注入对象,所以 Setter 注入的问题是,被注入的对象可能随时被修改

2.4@Resource:另一种注入关键字

在进行类注入时,除了可以使用 @Autowired 关键字之外,我们还可以使用 @Resource 进行注入,如下代码所示:

//将对象存储到Spring容器中
@Controller
public class UserController {
@Resourceprivate UserService userService;public User getUser(){return userService.getUser();}
}

2.4.1@Autowired 和 @Resource 的区别

2.4.1.1来源不同

@Autowired 和 @Resource 来自不同的“父类”,其中 @Autowired 是 Spring 定义的注解,而 @Resource 是 Java 定义的注解,它来自于 JSR-250(Java 250 规范提案)。

小知识:JSR 是 Java Specification Requests 的缩写,意思是“Java 规范提案”。任何人都可以提交 JSR 给 Java 官方,但只有最终确定的 JSR,才会以 JSR-XXX 的格式发布,如 JSR-250,而被发布的 JSR 就可以看作是 Java 语言的规范或标准。

2.4.1.2依赖查找顺序不同

依赖注入的功能,是通过先在 Spring IoC 容器中查找对象,再将对象注入引入到当前类中。而查找有分为两种实现:按名称(byName)查找或按类型(byType)查找,其中 @Autowired 和 @Resource 都是既使用了名称查找又使用了类型查找,但二者进行查找的顺序却截然相反。

2.4.1.2.1 @Autowired 查找顺序

@Autowired 是先根据类型(byType)查找,如果存在多个 Bean 再根据名称(byName)进行查找,它的具体查找流程如下:

image.png

2.4.1.2.2@Resource 查找顺序

@Resource 是先根据名称查找,如果(根据名称)查找不到,再根据类型进行查找,它的具体流程如下图所示:

image.png

2.4.1.3支持的参数不同 

 @Autowired 和 @Resource 在使用时都可以设置参数,比如给 @Resource 注解设置 name 和 type 参数,实现代码如下:

@Resource(name = "userinfo", type = UserInfo.class)
private UserInfo user;

二者支持的参数以及参数的个数完全不同,其中 @Autowired 只支持设置一个 required 的参数,而 @Resource 支持 7 个参数,支持的参数如下图所示:

image.png

image.png

 2.4.1.4依赖注入的支持不同

其中,@Autowired 支持属性注入、构造方法注入和 Setter 注入,而 @Resource 只支持属性注入和 Setter 注入,当使用 @Resource 实现构造方法注入时就会提示以下错误:

image.png

2.4.1.5编译器提示不同

当使用 IDEA 专业版在编写依赖注入的代码时,如果注入的是 Mapper 对象,那么使用 @Autowired 编译器会提示报错信息,报错内容如下图所示:

image.png

虽然 IDEA 会出现报错信息,但程序是可以正常执行的。 然后,我们再将依赖注入的注解更改为 @Resource 就不会出现报错信息了,具体实现如下:

image.png

2.5同一类型多个 @Bean 报错 

当出现以下多个 Bean,返回同一对象类型时程序会报错,如下代码所示:

@Component
public class Users {@Beanpublic User user1() {User user = new User();user.setId(1);user.setName("Java");return user;}@Beanpublic User user2() {User user = new User();user.setId(2);user.setName("MySQL");return user;}
}
//将对象存储到Spring容器中
@Controller
public class UserController {// 注入@Resourceprivate User user;public User getUser() {return user;}
}
public class App {public static void main(String[] args) {//1.得到Spring上下文ApplicationContext contest=new ClassPathXmlApplicationContext("spring-config.xml");//2.得到bean对象UserController userController=contest.getBean("userController",UserController.class);//3.使用bean对象System.out.println(userController.getUser().toString());}
}

报错的原因是,非唯一的 Bean 对象。

2.5.1同一类型多个 Bean 报错处理

解决同一个类型,多个 bean 的解决方案有以下两个:

1.将属性的名字和Bean的名字对应上
2.使用 @Qualifier 注解定义名称,配合@Autowired一起使用。 

//将对象存储到Spring容器中
@Controller
public class UserController {// 注入@Resource(name="user1")private User user;public User getUser() {return user;}
}

使用 @Qualifier:

//将对象存储到Spring容器中
@Controller
public class UserController {// 注入@Resource@Qualifier(value="user1")private User user;public User getUser() {return user;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/106116.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

诚迈科技子公司智达诚远与Unity中国达成合作,打造智能座舱新时代

2023 年 8 月 23 日,全球领先的实时 3D 引擎 Unity 在华合资公司 Unity 中国举办发布会,正式对外发布 Unity 引擎中国版——团结引擎,并带来专为次世代汽车智能座舱打造的团结引擎车机版。发布会上,诚迈科技副总裁、诚迈科技子公司…

行业追踪,2023-08-24

自动复盘 2023-08-24 凡所有相,皆是虚妄。若见诸相非相,即见如来。 k 线图是最好的老师,每天持续发布板块的rps排名,追踪板块,板块来开仓,板块去清仓,丢弃自以为是的想法,板块去留让…

研磨设计模式day12迭代器模式

目录 场景 解决方案 解决思路 代码示例 代码改造 Java实现迭代器 迭代器模式的优点 思考 何时选用 场景 大公司收购了一个小公司,大公司的工资系统采用List来记录工资列表,而小公司是采用数组,老板希望通过决策辅助系统来统一查看…

opencv案例03 -基于OpenCV实现二维码生成,发现,定位,识别

1.二维码的生成 废话不多说,直接上代码 # 生成二维码 import qrcode# 二维码包含的示例数据 data "B0018" # 生成的二维码图片名称 filename "qrcode.png" # 生成二维码 img qrcode.make(data) # 保存成图片输出 img.save(filename)img.sh…

C#矩阵XY排序

矩阵XY快速排序 using MyVision.Script.Method;public class MyScript : ScriptMethods {//struct MOTIONPOSXY_S{public double Pos_x;public double Pos_y;};//脚本执行该方法public bool Process(){//try{//脚本代码写在下方 List<double> PointX GetDoubleList(&qu…

搭建开发环境-Mac

概述 上一篇搭建开发环境-WSLUbuntu 记录了WSL 和Ubuntu 下开发环境的搭建。这一篇就说下Mac开发环境的搭建。 就像很多人误以为Mini 是专为女孩子设计的高颜值车&#xff0c;其实是一辆极其hardcore 的拉力车一样。 很多人都被Mac 那高颜值蒙蔽了&#xff0c;其实这是一台生产…

构造函数内的方法 直接写在构造函数内部 与 写在prototype上 的区别

文章目录 前言区别总结 前言 以前没注意过, 去创建一个构造函数的时候, 方法都是直接写在函数内的. 在构造函数需要多次实例化的情况下有缺点, 不过幸好以前项目里的构造函数也不需要多次实例化, 缺点没有生效. 区别 为了比较, 先在构造函数内部直接书写方法, 查看实例化结果…

Kubernetes 使用 Rancher 管理

K8S集群管理工具 只能管理单个K8S集群 kubectl命令行管理工具 dashboard&#xff08;K8S官方的UI界面图形化管理工具&#xff09; &#xff08;管理多集群很麻烦&#xff0c;切换不同集群每次需要更改kube-config文件[kubectl配置文件]&#xff0c;如果kubeadm部署每次都需…

item_search_img-按图搜索淘宝商品(拍立淘)

一、接口参数说明&#xff1a; item_search_img-按图搜索淘宝商品&#xff08;拍立淘&#xff09;&#xff0c;点击更多API调试&#xff0c;请移步注册API账号点击获取测试key和secret 公共参数 请求地址: https://api-gw.onebound.cn/taobao/item_search_img 名称类型必须描…

iconfont 图标在vue里的使用

刚好项目需要使用一个iconfont的图标&#xff0c;所以记录一下这个过程 1、iconfont-阿里巴巴矢量图标库 这个注册一个账号&#xff0c;以便后续使用下载代码时需要 2、寻找自己需要的图标 我主要是找两个图标 &#xff0c;一个加号&#xff0c;一个减号&#xff0c;分别加入到…

华为云渲染实践

// 编者按&#xff1a;云计算与网络基础设施发展为云端渲染提供了更好的发展机会&#xff0c;华为云随之长期在自研图形渲染引擎、工业领域渲染和AI加速渲染三大方向进行云渲染方面的探索与研究。本次LiveVideoStackCon 2023上海站邀请了来自华为云的陈普&#xff0c;为大家分…

[系统] 电脑突然变卡 / 电脑突然** / 各种突发情况解决思路

今天来公司办公&#xff0c;开机之后发现电脑出现各种问题&#xff0c;死机、卡顿、点什么都加载&#xff0c;甚至开一个文件夹要1分钟才能打开&#xff0c;花了2个小时才解决&#xff0c;走了很多弯路&#xff0c;其实早点想通&#xff0c;5分钟就能解决问题&#xff0c;所以打…

玩转科技|了解AI平台桌面客户端—ChatBox

目录 前言 特性 ​编辑 为什么需要 ChatBox&#xff1f; ChatGPT Plus 平替&#xff1f; 下载 支持系统 功能图 使用教程 ​感受 展示 前言 今天小编又来了&#xff0c;推荐给大家一款开源的OpenAI API桌面客户端ChatBox&#xff0c;它支持 Windows、Mac 和 Linux。…

Vue2向Vue3过度Vuex核心概念state状态

目录 1 核心概念 - state 状态1.目标2.提供数据3.访问Vuex中的数据4.通过$store访问的语法5.代码实现5.1模板中使用5.2组件逻辑中使用5.3 js文件中使用 2 通过辅助函数 - mapState获取 state中的数据1.第一步&#xff1a;导入mapState (mapState是vuex中的一个函数)2.第二步&am…

【Apollo学习笔记】——规划模块TASK之PATH_REUSE_DECIDER

文章目录 前言PATH_REUSE_DECIDER功能简介PATH_REUSE_DECIDER相关配置PATH_REUSE_DECIDER总体流程PATH_REUSE_DECIDER相关子函数IsCollisionFreeTrimHistoryPathIsIgnoredBlockingObstacle和GetBlockingObstacleS Else参考 前言 在Apollo星火计划学习笔记——Apollo路径规划算…

容器技术,1. Docker,2. Kubernetes(K8s):

目录 容器技术 1. Docker&#xff1a; 2. Kubernetes&#xff08;K8s&#xff09;&#xff1a; Docker和Kubernetes 容器的主要应用场景有哪些&#xff1f; 容器技术 有效的将单个操作系统的资源划分到孤立的组中&#xff0c;以便更好的在孤立的组之间平衡有冲突的资源使…

分布式 - 服务器Nginx:一小时入门系列之 HTTPS协议配置

文章目录 1. HTTPS 协议2. 生成 SSL 证书和私钥文件3. 配置 SSL 证书和私钥文件4. HTTPS 协议优化 1. HTTPS 协议 HTTPS 是一种通过计算机网络进行安全通信的协议。它是HTTP的安全版本&#xff0c;通过使用 SSL 或 TLS 协议来加密和保护数据传输。HTTPS的主要目的是确保在客户…

PHP小白搭建Kafka环境以及初步使用rdkafka

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、安装java&#xff08;Kafka必须安装java&#xff0c;因为kafka依赖java核心&#xff09;二、安装以及配置Kafka、zookeeper1.下载Kafka&#xff08;无需下载…

强化学习在游戏AI中的应用与挑战

文章目录 1. 强化学习简介2. 强化学习在游戏AI中的应用2.1 游戏智能体训练2.2 游戏AI决策2.3 游戏测试和优化 3. 强化学习在游戏AI中的挑战3.1 探索与利用的平衡3.2 多样性的应对 4. 解决方法与展望4.1 深度强化学习4.2 奖励设计和函数逼近 5. 总结 &#x1f389;欢迎来到AIGC人…

计算机网络——OSI与TCP/IP各层的结构与功能,都有哪些协议?

文章目录 一 OSI与TCP/IP各层的结构与功能,都有哪些协议?1.1 应用层1.2 运输层1.3 网络层1.4 数据链路层1.5 物理层1.6 总结一下 二 ⭐TCP 三次握手和四次挥手(面试常客)2.1 TCP 三次握手漫画图解2.2 为什么要三次握手⭐2.3 第2次握手传回了ACK&#xff0c;为什么还要传回SYN&…