[{"content":"参加了一次笔试，收获颇多。\n不同的输入方法 和力扣不一样，一些大厂的笔试包括面试时的算法题都是 ACM 模式，也就是需要自己处理输入，然后对输入处理并输出，而力扣是核心代码模式，不用你管输入，只需要实现指定方法，平台自会调用你的方法。在参加之前，我去卡码网上了解了一下，发现还是 Scanner 那一套，也就没在意，但当我真正开始做的时候感到有点不对，Scanner 恐怕不太够用。这篇博客主要讲一下 Java 里的输入流。\nJava 的输入体系建立在 流 的概念之上，主要有两个抽象基类：InputStream 和 Reader ，前者为字节流，处理原始字节数据，如图片、音频等；后者为字符流，处理字符数据，如文本文件、控制台输入等。两者之间通过 InputStreamReader 作为桥梁，将字节流转换为字符流。\nInputStream 字节流 InputStream 类定义了所有字节输入流必须实现的核心办法。包括 int read() , int read(byte[] b) , void close()等方法。\nJava 的 IO 体系采用了装饰器模式，意味着可以通过组合不同的流来实现复杂的功能。这里将 InputStream 的子类大致分为两类。\n第一种，这种类直接与特定的数据源进行交互，比如 FileInputStream ByteArrayInputStream 。\n第二种，这种类不直接连接数据源，而是通过继承装饰器基类 FilterInputStream 来包装一个已存在的 InputStream ，给它提供一些额外的功能，比如缓冲、数据类型转换等。这种类的典型代表有 BufferedInputStream DataInputStream 等。\nReader 字符流 Reader 类是字符输入流的抽象基类，定义的方法有 int read() , int read(char[] cbuf) , void close() 等。\n它的子类和 InputStream 一样，也分两种，这里不再多说。但它有一个特别的子类： InputStreamReader 从它的类名就能看出来，它是字节流到字符流的单向桥梁，它可以读取字节，然后根据指定的字符编码将其解码为字符。\nSystem.in 它是 System 类里的一个静态变量，类型是 InputStream ，所以它是一个字节流。如果需要按字符或行读取，通常需要对其包装，比如刚才提到的 InputStreamReader 和常见的 Scanner。通常情况下，System.in 默认连接到控制台（键盘），同时它读取数据也是阻塞式的，程序会一直等待，直到有数据可读或者流被关闭。\nScanner Scanner 不属于 InputStream 或 Reader ，它是一个独立的类。它的内部有这样一个变量 private Readable source; ，表示内部有一个 Readable 接口的实现，这就是关键所在，因为 Reader 就实现了 Readable 接口，所以， Scanner 可以接受 Reader , InputStream（通过桥转换） , String 作为输入源。这就是 new Scanner(System.in) 的用法由来。\n这一点设计的十分巧妙， Scanner 并不依赖具体的类，不依赖 InputStream ，不依赖 Reader ，它只依赖能力，它只需要一个实现了 Readable 接口的对象。\n下面是它的部分构造器\npublic Scanner(Readable source) { this(Objects.requireNonNull(source, \u0026#34;source\u0026#34;), WHITESPACE_PATTERN); } public Scanner(InputStream source) { this(new InputStreamReader(source), WHITESPACE_PATTERN); } public Scanner(String source) { this(new StringReader(source), WHITESPACE_PATTERN); } Scanner 内部使用正则表达式解析输入，提供了 nextInt() , nextLine() 等方法，极其方便。成也萧何，败也萧何，它会利用正则表达式来对输入类型进行检查，保证了安全性，但当数据量较大时，读取速度会显著下降，同时它的缓冲机制效率也相对较低，比不上 BufferedReader 等专为高效 IO 设计的类。\n我进行了一个小实验，准备了一个文件，里面的内容是100万行数字1，分别用 Scanner 和 BufferedReader 对文件读取，耗时相差十倍左右。\n还有一点，Scanner 是在 Java.util 包里，而前面提到的输入流都是在 java.io 包里。\n总结 分析了不同输入方法，我总结了一下。一般就两种情况，第一种：输入量极小，一般都是第一题，这时用 Scanner 完全没问题；第二种：输入量在 10 的六次方左右，或者说有很多组数据。一般都是下面这种写法，当一行里面有多个数据时，可以使用 StringTokenizer 来分词。\nBufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); int k = Integer.parseInt(st.nextToken()); int q = Integer.parseInt(st.nextToken()); 除了上面两种，还有更快的，就是自定义一个 FastScanner ，类里面使用 BufferedInputStream 直接处理字节流输入，同时需要自己写方法解析输入。\n上面讲的全是输入流，其实输出流的设计和输入流简直一模一样，InputStream 对应 OutputStream ， Reader 对应 Writer ，输出流里也有一个桥：OuputStreamWriter，用来将字符流转换为字节流。System.in 也对应了 System.out ，同样，当需要输出很多行时，System.out.println 的性能也稍差，可以使用 StringBuilder 对输出拼接，最后转为字符串。\nAI面试记录 记录一下问到的问题：\nRedis 两种持久化机制的优缺点。 Redis 提供两种持久化机制\nRDB（快照）：在指定时间间隔内将内存数据生成快照保存到磁盘。 优点：文件小；恢复速度快；对性能影响小（可fork子进程）。适合冷备份，灾难恢复。 缺点：可能丢失最后一次快照之后，崩溃之前这一部分的数据。 AOF（追加文件）：记录每个写操作命令到日志文件，重启时重写命令。 优点：最多丢失一秒的数据（默认），安全性高；可读性强，内容为纯文本格式；体积过大会自动重写。 缺点：文件体积大；恢复速度慢；对写性能有一定影响。 Redis4.0 后支持混合持久化，AOF 重写期间，先以 RDB 格式将内存中的数据快照保存到 AOF 文件的开头，再将重写期间的命令以 AOF 格式追加到文件末尾。\n栈和队列的区别，使用场景 栈先进后出，入栈出栈都在一端进行，栈天然支持递归和回溯。常用使用场景有：函数调用栈、表达式求值。\n队列先进先出，入队出队在两端进行。常见使用场景有消息队列、任务调度（线程池的任务队列）、BFS。\n泛型类型的擦除原理 在编译期，编译器会将泛型代码中的泛型参数 E、K 等替换为边界类型，如果没有边界统一替换为 Object。运行期，JVM 眼中不存在泛型类或泛型方法。关于泛型可以 看Java泛型 | withdong02\n这篇博客就到这里，错误不可避免，欢迎指正。我会持续更新，欢迎收藏我的网站。\n","permalink":"http://localhost:1313/posts/%E7%AC%94%E8%AF%95%E6%94%B6%E8%8E%B7/","summary":"\u003cp\u003e参加了一次笔试，收获颇多。\u003c/p\u003e\n\u003ch2 id=\"不同的输入方法\"\u003e不同的输入方法\u003c/h2\u003e\n\u003cp\u003e和力扣不一样，一些大厂的笔试包括面试时的算法题都是 ACM 模式，也就是需要自己处理输入，然后对输入处理并输出，而力扣是核心代码模式，不用你管输入，只需要实现指定方法，平台自会调用你的方法。在参加之前，我去卡码网上了解了一下，发现还是 \u003ccode\u003eScanner\u003c/code\u003e 那一套，也就没在意，但当我真正开始做的时候感到有点不对，\u003ccode\u003eScanner\u003c/code\u003e 恐怕不太够用。这篇博客主要讲一下 \u003cstrong\u003eJava 里的输入流。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eJava 的输入体系建立在 \u003cstrong\u003e流\u003c/strong\u003e 的概念之上，主要有两个抽象基类：\u003ccode\u003eInputStream\u003c/code\u003e 和 \u003ccode\u003eReader\u003c/code\u003e ，前者为字节流，处理原始字节数据，如图片、音频等；后者为字符流，处理字符数据，如文本文件、控制台输入等。两者之间通过 \u003ccode\u003eInputStreamReader\u003c/code\u003e 作为桥梁，将字节流转换为字符流。\u003c/p\u003e\n\u003ch3 id=\"inputstream-字节流\"\u003eInputStream 字节流\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003eInputStream\u003c/code\u003e 类定义了所有字节输入流必须实现的核心办法。包括 \u003ccode\u003eint read()\u003c/code\u003e , \u003ccode\u003eint read(byte[] b)\u003c/code\u003e , \u003ccode\u003evoid close()\u003c/code\u003e等方法。\u003c/p\u003e\n\u003cp\u003eJava 的 IO 体系采用了装饰器模式，意味着可以通过组合不同的流来实现复杂的功能。这里将 \u003ccode\u003eInputStream\u003c/code\u003e 的子类大致分为两类。\u003c/p\u003e\n\u003cp\u003e第一种，这种类直接与特定的数据源进行交互，比如 \u003ccode\u003eFileInputStream\u003c/code\u003e \u003ccode\u003eByteArrayInputStream\u003c/code\u003e 。\u003c/p\u003e\n\u003cp\u003e第二种，这种类不直接连接数据源，而是通过继承装饰器基类 \u003ccode\u003eFilterInputStream\u003c/code\u003e 来包装一个已存在的 \u003ccode\u003eInputStream\u003c/code\u003e ，给它提供一些额外的功能，比如缓冲、数据类型转换等。这种类的典型代表有 \u003ccode\u003eBufferedInputStream\u003c/code\u003e \u003ccode\u003eDataInputStream\u003c/code\u003e 等。\u003c/p\u003e\n\u003ch3 id=\"reader-字符流\"\u003eReader 字符流\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003eReader\u003c/code\u003e 类是字符输入流的抽象基类，定义的方法有 \u003ccode\u003eint read()\u003c/code\u003e , \u003ccode\u003eint read(char[] cbuf)\u003c/code\u003e , \u003ccode\u003evoid close()\u003c/code\u003e 等。\u003c/p\u003e\n\u003cp\u003e它的子类和 \u003ccode\u003eInputStream\u003c/code\u003e 一样，也分两种，这里不再多说。但它有一个特别的子类： \u003ccode\u003eInputStreamReader\u003c/code\u003e 从它的类名就能看出来，它是字节流到字符流的单向桥梁，它可以读取字节，然后根据指定的字符编码将其解码为字符。\u003c/p\u003e\n\u003ch3 id=\"systemin\"\u003eSystem.in\u003c/h3\u003e\n\u003cp\u003e它是 \u003ccode\u003eSystem\u003c/code\u003e 类里的一个静态变量，类型是 \u003ccode\u003eInputStream\u003c/code\u003e ，所以它是一个字节流。如果需要按字符或行读取，通常需要对其包装，比如刚才提到的 \u003ccode\u003eInputStreamReader\u003c/code\u003e 和常见的 \u003ccode\u003eScanner\u003c/code\u003e。通常情况下，\u003ccode\u003eSystem.in\u003c/code\u003e 默认连接到控制台（键盘），同时它读取数据也是阻塞式的，程序会一直等待，直到有数据可读或者流被关闭。\u003c/p\u003e","title":"笔试收获"},{"content":"常见的权限模型有两种：RBAC 和 ABAC。\n两种模型介绍 RBAC 模型 Role-Based Access Control，翻译为基于角色的访问控制。角色拥有某些权限，通过赋予用户某种角色来达到给用户授予相关权限的目的，这就是基于角色。实现较简单。在一些大型组织中，角色可能爆炸式增长，导致管理复杂、权限冗余。\nABAC 模型 Attribute-Based Access Control，翻译为基于属性的访问控制，它比 RBAC 更加灵活，因为在 ABAC 模型中，一个操作是否被允许是基于对象、资源、操作和环境信息共同动态计算决定的。它的控制更细粒度，比如它能实现“仅允许大学生在水课上睡觉”的控制。\n派聪明的权限控制 派聪明的权限模型是改进版的 RBAC，或者说简化版的 ABAC。设计了 admin 和 user 两种角色，管理员拥有管理知识库，查看用户列表等权限，普通用户拥有查看私有知识库等权限。但单纯的 RBAC 有两种局限性：权限控制力度不够细和没办法基于数据属性做动态授权，所以添加了组织标签机制。\n在上传文件时，给每份文档标记上传者所属的标签，这样用户访问知识库，系统能够动态的基于当前用户的组织标签和文档的组织标签进行匹配过滤。从而实现更细粒度的数据隔离和动态授权。\n此外，派聪明还考虑到了多级组织，设计了组织标签树。比如有这样三个层级标签：总部、研发部、后端部。一个拥有后端部组织标签的员工可以访问研发部、总部这两个父组织的资料。我个人在这一部分绕了很久，总感觉设计反了，现在是这样理解的，抛弃父标签级别高，所以权力大的想法，这样想：\n父标签（如“总公司”）：它定义的是一个最基础、最通用的权限范围。就像一张“员工卡”，让你能访问公司的基础公共资源。 子标签（如“技术部”、“财务部”）：它在父标签的基础上，增加了更具体、更细分的权限。就像一张“技术部员工卡”，它包含了“员工卡”的所有权限，还额外增加了进入技术部专属区域的权限。 这就是派聪明中有关权限模块的设计。\n转转统一权限系统的设计与实现 经过分析，转转技术部门选择了基于 RBAC 模型来实现，但它也有所改动，在原有基础上，新增了给用户直接增加权限的能力，也就是说既可以给用户添加角色，也可以给用户直接添加权限。所以用户最终的权限为 所拥有角色带来的权限和用户独立配置的权限的并集。\n转转的技术文章里还从权限系统自身和具体业务系统两层设计了六种身份，这里不再多说。\n参考文章：\n✅派聪明 RAG用户管理模块设计方案 - 技术派 - Java技术社区 | RAG+Agent实战项目教程+AI助手 转转统一权限系统的设计与实现（设计篇） ","permalink":"http://localhost:1313/posts/%E6%9D%83%E9%99%90%E6%A8%A1%E5%9E%8B/","summary":"\u003cp\u003e常见的权限模型有两种：RBAC 和 ABAC。\u003c/p\u003e\n\u003ch2 id=\"两种模型介绍\"\u003e两种模型介绍\u003c/h2\u003e\n\u003ch3 id=\"rbac-模型\"\u003eRBAC 模型\u003c/h3\u003e\n\u003cp\u003eRole-Based Access Control，翻译为基于角色的访问控制。角色拥有某些权限，通过赋予用户某种角色来达到给用户授予相关权限的目的，这就是基于角色。实现较简单。在一些大型组织中，角色可能爆炸式增长，导致管理复杂、权限冗余。\u003c/p\u003e\n\u003ch3 id=\"abac-模型\"\u003eABAC 模型\u003c/h3\u003e\n\u003cp\u003eAttribute-Based Access Control，翻译为基于属性的访问控制，它比 RBAC 更加灵活，因为在 ABAC 模型中，一个操作是否被允许是基于\u003cstrong\u003e对象、资源、操作和环境信息\u003c/strong\u003e共同动态计算决定的。它的控制更细粒度，比如它能实现“仅允许大学生在水课上睡觉”的控制。\u003c/p\u003e\n\u003ch2 id=\"派聪明的权限控制\"\u003e派聪明的权限控制\u003c/h2\u003e\n\u003cp\u003e派聪明的权限模型是改进版的 RBAC，或者说简化版的 ABAC。设计了 admin 和 user 两种角色，管理员拥有管理知识库，查看用户列表等权限，普通用户拥有查看私有知识库等权限。但单纯的 RBAC 有两种局限性：权限控制力度不够细和没办法基于数据属性做动态授权，所以添加了组织标签机制。\u003c/p\u003e\n\u003cp\u003e在上传文件时，给每份文档标记上传者所属的标签，这样用户访问知识库，系统能够动态的基于当前用户的组织标签和文档的组织标签进行匹配过滤。从而实现更细粒度的数据隔离和动态授权。\u003c/p\u003e\n\u003cp\u003e此外，派聪明还考虑到了多级组织，设计了组织标签树。比如有这样三个层级标签：总部、研发部、后端部。一个拥有后端部组织标签的员工可以访问研发部、总部这两个父组织的资料。我个人在这一部分绕了很久，总感觉设计反了，现在是这样理解的，抛弃父标签级别高，所以权力大的想法，这样想：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e父标签（如“总公司”）：它定义的是一个\u003cstrong\u003e最基础、最通用\u003c/strong\u003e的权限范围。就像一张“员工卡”，让你能访问公司的基础公共资源。\u003c/li\u003e\n\u003cli\u003e子标签（如“技术部”、“财务部”）：它在父标签的基础上，\u003cstrong\u003e增加了更具体、更细分的权限\u003c/strong\u003e。就像一张“技术部员工卡”，它包含了“员工卡”的所有权限，还额外增加了进入技术部专属区域的权限。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e这就是派聪明中有关权限模块的设计。\u003c/p\u003e\n\u003ch2 id=\"转转统一权限系统的设计与实现\"\u003e转转统一权限系统的设计与实现\u003c/h2\u003e\n\u003cp\u003e经过分析，转转技术部门选择了基于 RBAC 模型来实现，但它也有所改动，在原有基础上，新增了给用户直接增加权限的能力，也就是说既可以给用户添加角色，也可以给用户直接添加权限。所以用户最终的权限为 所拥有角色带来的权限和用户独立配置的权限的并集。\u003c/p\u003e\n\u003cp\u003e转转的技术文章里还从权限系统自身和具体业务系统两层设计了六种身份，这里不再多说。\u003c/p\u003e\n\u003cp\u003e参考文章：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://paicoding.com/column/10/13\"\u003e✅派聪明 RAG用户管理模块设计方案 - 技术派 - Java技术社区 | RAG+Agent实战项目教程+AI助手\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://mp.weixin.qq.com/s/ONMuELjdHYa0yQceTj01Iw\"\u003e转转统一权限系统的设计与实现（设计篇）\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e","title":"权限模型"},{"content":"学习 Spring，跟着二哥简单写个 IoC 容器。\n@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface MyAutowired { } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface MyComponent { } public class SimpleIoC { Map\u0026lt;Class\u0026lt;?\u0026gt;, Object\u0026gt; beans = new HashMap\u0026lt;\u0026gt;(); //扫描 public void scan(String packageName) { List\u0026lt;Class\u0026lt;?\u0026gt;\u0026gt; classes = getClassesInPackage(packageName); for (Class\u0026lt;?\u0026gt; clazz : classes) { if (clazz.isAnnotationPresent(MyComponent.class)) { registerBean(clazz); } } di(); } //获取包下的类，简化实现 private List\u0026lt;Class\u0026lt;?\u0026gt;\u0026gt; getClassesInPackage(String packageName) { // 实际实现需要扫描classpath，这里简化处理. return Arrays.asList(UserDao.class, UserService.class); } //注册bean private void registerBean (Class\u0026lt;?\u0026gt; clazz) { try { Object instance = clazz.getDeclaredConstructor().newInstance(); beans.put(clazz, instance); } catch (Exception e) { throw new RuntimeException(\u0026#34;创建bean失败\u0026#34;); } } //获取bean @SuppressWarnings(\u0026#34;unchecked\u0026#34;) public \u0026lt;T\u0026gt; T getBean(Class\u0026lt;T\u0026gt; clazz) { return (T)beans.get(clazz); } //依赖注入 private void di() { for (Object bean : beans.values()) { Field[] fields = bean.getClass().getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(MyAutowired.class)) { field.setAccessible(true); Object dependency = getBean(field.getType()); try { field.set(bean, dependency); } catch (Exception e) { throw new RuntimeException(\u0026#34;依赖注入失败\u0026#34;); } } } } } } //DAO层 @MyComponent class UserDao { public void save(String user) { System.out.println(\u0026#34;保存用户: \u0026#34; + user); } } // Service层 @MyComponent class UserService { @MyAutowired private UserDao userDao; public void createUser(String name) { userDao.save(name); System.out.println(\u0026#34;用户创建完成\u0026#34;); } } class Test { public static void main(String[] args) { SimpleIoC ioc = new SimpleIoC(); ioc.scan(\u0026#34;package\u0026#34;); UserService bean = ioc.getBean(UserService.class); bean.createUser(\u0026#34;dong\u0026#34;); } } 参考链接：\n二哥的Java进阶之路\nJava反射 | withdong02\nJava泛型 | withdong02\n","permalink":"http://localhost:1313/posts/%E6%89%8B%E5%86%99%E7%AE%80%E5%8D%95ioc%E5%AE%B9%E5%99%A8/","summary":"\u003cp\u003e学习 Spring，跟着二哥简单写个 IoC 容器。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-Java\" data-lang=\"Java\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nd\"\u003e@Target\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eElementType\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eFIELD\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nd\"\u003e@Retention\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eRetentionPolicy\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eRUNTIME\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nd\"\u003e@interface\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eMyAutowired\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nd\"\u003e@Target\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eElementType\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eTYPE\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nd\"\u003e@Retention\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eRetentionPolicy\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eRUNTIME\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nd\"\u003e@interface\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eMyComponent\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eSimpleIoC\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"n\"\u003eMap\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eClass\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;?\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eObject\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ebeans\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eHashMap\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"c1\"\u003e//扫描  \u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nf\"\u003escan\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eString\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003epackageName\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003eList\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eClass\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;?\u0026gt;\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eclasses\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003egetClassesInPackage\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epackageName\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eClass\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;?\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eclazz\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eclasses\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eclazz\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eisAnnotationPresent\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eMyComponent\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eclass\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                \u003c/span\u003e\u003cspan class=\"n\"\u003eregisterBean\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eclazz\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003edi\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"c1\"\u003e//获取包下的类，简化实现  \u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eList\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eClass\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;?\u0026gt;\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003egetClassesInPackage\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eString\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003epackageName\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"c1\"\u003e// 实际实现需要扫描classpath，这里简化处理.  \u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eArrays\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003easList\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eUserDao\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eclass\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eUserService\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eclass\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"c1\"\u003e//注册bean  \u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nf\"\u003eregisterBean\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eClass\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;?\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eclazz\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003etry\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"n\"\u003eObject\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003einstance\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eclazz\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetDeclaredConstructor\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"na\"\u003enewInstance\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"n\"\u003ebeans\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eput\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eclazz\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003einstance\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003ecatch\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eException\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"k\"\u003ethrow\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eRuntimeException\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;创建bean失败\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"c1\"\u003e//获取bean  \u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nd\"\u003e@SuppressWarnings\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;unchecked\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eT\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eT\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nf\"\u003egetBean\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eClass\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eT\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eclazz\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eT\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"n\"\u003ebeans\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eclazz\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"c1\"\u003e//依赖注入  \u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nf\"\u003edi\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eObject\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ebean\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ebeans\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003evalues\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"n\"\u003eField\u003c/span\u003e\u003cspan class=\"o\"\u003e[]\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003efields\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ebean\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetClass\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"na\"\u003egetDeclaredFields\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eField\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003efield\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003efields\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                \u003c/span\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003efield\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eisAnnotationPresent\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eMyAutowired\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eclass\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                    \u003c/span\u003e\u003cspan class=\"n\"\u003efield\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003esetAccessible\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                    \u003c/span\u003e\u003cspan class=\"n\"\u003eObject\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003edependency\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003egetBean\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003efield\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetType\u003c/span\u003e\u003cspan class=\"p\"\u003e());\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                    \u003c/span\u003e\u003cspan class=\"k\"\u003etry\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                        \u003c/span\u003e\u003cspan class=\"n\"\u003efield\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebean\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003edependency\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003ecatch\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eException\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                        \u003c/span\u003e\u003cspan class=\"k\"\u003ethrow\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eRuntimeException\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;依赖注入失败\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e//DAO层  \u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nd\"\u003e@MyComponent\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eUserDao\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nf\"\u003esave\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eString\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003euser\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003eSystem\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eout\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eprintln\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;保存用户: \u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003euser\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Service层  \u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nd\"\u003e@MyComponent\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eUserService\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nd\"\u003e@MyAutowired\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eUserDao\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003euserDao\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nf\"\u003ecreateUser\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eString\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003euserDao\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003esave\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003eSystem\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eout\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eprintln\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;用户创建完成\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eTest\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003estatic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nf\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eString\u003c/span\u003e\u003cspan class=\"o\"\u003e[]\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eargs\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003eSimpleIoC\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eioc\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eSimpleIoC\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003eioc\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003escan\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;package\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003eUserService\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ebean\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eioc\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetBean\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eUserService\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eclass\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003ebean\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003ecreateUser\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;dong\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e参考链接：\u003c/p\u003e","title":"手写简单IoC容器"},{"content":"第二次碰到这道题了，又学了两种解题方法，记录一下。\n题目介绍 设计一个支持 push ，pop ，top 操作，并能在常数时间内检索到最小元素的栈。\n实现 MinStack 类:\nMinStack() 初始化堆栈对象。 void push(int val) 将元素val推入堆栈。 void pop() 删除堆栈顶部的元素。 int top() 获取堆栈顶部的元素。 int getMin() 获取堆栈中的最小元素。 解法一：使用辅助栈 这个也是最简单的，题目没有空间限制，考虑使用两个栈，主栈正常记录元素进出，辅助栈记录每个元素对应最小值的进出，辅助栈的栈顶保持为主栈所有元素的最小值。\n当元素入栈时，比较该元素和辅助栈的栈顶，如果该元素更小，那么将其推入主栈的同时也将其推入辅助栈。\n当元素出栈时，如果主栈栈顶元素值和辅助栈的栈顶元素值相等。那么将该元素从两个栈里同时弹出，否则正常弹出主栈栈顶元素。\nclass MinStack { Deque\u0026lt;Integer\u0026gt; stack1; Deque\u0026lt;Integer\u0026gt; stack2; public MinStack() { stack1 = new ArrayDeque\u0026lt;\u0026gt;(); stack2 = new ArrayDeque\u0026lt;\u0026gt;(); } public void push(int val) { stack1.push(val); if (stack2.isEmpty() || val \u0026lt;= stack2.peek()) { stack2.push(val); } } public void pop() { int num = stack1.pop(); if (num == stack2.peek()) stack2.pop(); } public int top() { return stack1.peek(); } public int getMin() { return stack2.peek(); } } 解法二：使用自定义链表 评论区有评论说面试官要求使用栈以外的数据结构来设计，所以找到这种方法。\n每个节点存储三个值：该节点的值，整个链表的最小值，指向下一个节点的指针。我设计的是头插法，head 指向表头，也可以看作栈顶。\nclass Node { int val; int minVal; Node next; public Node(int val, int minVal) { this.val = val; this.minVal = minVal; } public Node(int val, int minVal, Node next) { this.val = val; this.minVal = minVal; this.next = next; } } class MinStack { Node head; public MinStack() { } public void push(int val) { if (head == null) { head = new Node(val, val); } else { head = new Node(val, Math.min(val, head.minVal), head); } } public void pop() { head = head.next; } public int top() { return head.val; } public int getMin() { return head.minVal; } } 解法三：使用O(1)的辅助空间 如果只是存储节点值使用 int 即可，而中间要计算差值，考虑到溢出的可能性，所以使用 long 类型来存储。注意 long 和 int 之间的转换。\nclass MinStack { Deque\u0026lt;Long\u0026gt; stack = new ArrayDeque\u0026lt;\u0026gt;(); long min; public MinStack() { } public void push(int val) { if (stack.isEmpty()) { stack.push(0L); min = val; } else { long diff = val - min;//栈里存储的是差值 stack.push(diff); if (diff \u0026lt; 0) min = val;//差值小于0，说明要更新min } } public void pop() { Long pop = stack.pop(); if (pop \u0026lt; 0) min -= pop; //如果pop小于0.说明当时推入栈时val\u0026lt;min，min被更新了。 //而现在这个值要弹出，min也要恢复之前较大的状态。 } public int top() { Long peek = stack.peek(); //如果peek小于0，说明栈顶就是最小值 if (peek \u0026lt; 0) return (int)min; //否则计算正常值，因为栈中存储的是偏移量 return (int)min + peek.intValue(); } public int getMin() { return (int)min; } } ","permalink":"http://localhost:1313/posts/leetcode155-%E6%9C%80%E5%B0%8F%E6%A0%88/","summary":"\u003cp\u003e第二次碰到这道题了，又学了两种解题方法，记录一下。\u003c/p\u003e\n\u003ch3 id=\"题目介绍\"\u003e题目介绍\u003c/h3\u003e\n\u003cp\u003e设计一个支持 \u003ccode\u003epush\u003c/code\u003e ，\u003ccode\u003epop\u003c/code\u003e ，\u003ccode\u003etop\u003c/code\u003e 操作，并能在常数时间内检索到最小元素的栈。\u003c/p\u003e\n\u003cp\u003e实现 \u003ccode\u003eMinStack\u003c/code\u003e 类:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eMinStack()\u003c/code\u003e 初始化堆栈对象。\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003evoid push(int val)\u003c/code\u003e 将元素val推入堆栈。\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003evoid pop()\u003c/code\u003e 删除堆栈顶部的元素。\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eint top()\u003c/code\u003e 获取堆栈顶部的元素。\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eint getMin()\u003c/code\u003e 获取堆栈中的最小元素。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"解法一使用辅助栈\"\u003e解法一：使用辅助栈\u003c/h3\u003e\n\u003cp\u003e这个也是最简单的，题目没有空间限制，考虑使用两个栈，主栈正常记录元素进出，辅助栈记录每个元素对应最小值的进出，辅助栈的栈顶保持为主栈所有元素的最小值。\u003c/p\u003e\n\u003cp\u003e当元素入栈时，比较该元素和辅助栈的栈顶，如果该元素更小，那么将其推入主栈的同时也将其推入辅助栈。\u003c/p\u003e\n\u003cp\u003e当元素出栈时，如果主栈栈顶元素值和辅助栈的栈顶元素值相等。那么将该元素从两个栈里同时弹出，否则正常弹出主栈栈顶元素。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-Java\" data-lang=\"Java\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eMinStack\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"n\"\u003eDeque\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eInteger\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003estack1\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"n\"\u003eDeque\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eInteger\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003estack2\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nf\"\u003eMinStack\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003estack1\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eArrayDeque\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003estack2\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eArrayDeque\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nf\"\u003epush\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eval\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003estack1\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003epush\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eval\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estack2\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eisEmpty\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e||\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eval\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003estack2\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003epeek\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"n\"\u003estack2\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003epush\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eval\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nf\"\u003epop\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003enum\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003estack1\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003epop\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enum\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e==\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003estack2\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003epeek\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003estack2\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003epop\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nf\"\u003etop\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003estack1\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003epeek\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nf\"\u003egetMin\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003estack2\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003epeek\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"解法二使用自定义链表\"\u003e解法二：使用自定义链表\u003c/h3\u003e\n\u003cp\u003e评论区有评论说面试官要求使用栈以外的数据结构来设计，所以找到这种方法。\u003c/p\u003e","title":"LeetCode155.最小栈"},{"content":"一切源于一道题目：8. 字符串转换整数 (atoi) - 力扣（LeetCode）\n考虑这样一个问题：给你一个数字字符串，如何在32位环境下安全的处理可能超过[-2^31, 2^31 - 1]范围的数字，不能使用64位变量临时存储。\n因为我熟悉Java， 所以下面提到的数据类型都为 Java 中的数据类型。\nInteger.MAX_VALUE = 2^31 - 1 = 2147483647; Integer.MIN_VALUE = -2^31 = -2147483648; 首先题目说不能使用64位变量，所以 Long 类型不能使用，那就一步一步累加，具体思路就是设置两个变量 digit, res，从头到尾遍历字符串每一位，同时计算 res = res * 10 + digit。但这样的话，累加过程中 res 可能会无征兆直接溢出，程序直接抛异常。那怎么办，介绍一下从 K神题解 里学来的方法。\n整体思路也是一步一步累加，但提前预判溢出。\n这里有个核心变量 bndry = 2^31 / 10 = 214748364，，它是32位有符号整数最大值去掉最后一位的结果。\n在执行 res = res * 10 + digit 之前，先检查 res 与 bndry 的关系，有下面两种溢出的可能。\n情况一：res \u0026gt; bndry ，意味着 res 乘10后，即使加上最小的数字0，结果也会超过 2147483647，溢出。 情况二：res == bndry，此时是否溢出取决于要累加的数字 digit。 如果 digit \u0026lt;= 7，此时正数负数都不会溢出，但等于七对正数来说已经达到最大值。 如果 digit = 8，拼接后值为 2147483648，比 Integer.MAX_VALUE 大 1，而负数即 -2147483648 还未溢出。 如果 digit \u0026gt; 8，正数负数都溢出。 思路还是很清晰的，在下一位拼接前进行判断可以很好的避开溢出问题。下面看一下题解代码\nclass Solution { public int myAtoi(String s) { char[] chars = s.trim().toCharArray(); if (chars.length == 0) return 0; int res = 0; int bndry = Integer.MAX_VALUE / 10; int i = 1, sign = 1; if (chars[0] == \u0026#39;-\u0026#39;) { sign = -1; } else if (chars[0] != \u0026#39;+\u0026#39;) { i = 0; } for (int j = i; j \u0026lt; chars.length; j++) { if (chars[j] \u0026lt; \u0026#39;0\u0026#39; || chars[j] \u0026gt; \u0026#39;9\u0026#39;) break; if (res \u0026gt; bndry || (res == bndry \u0026amp;\u0026amp; chars[j] \u0026gt; \u0026#39;7\u0026#39;)) { return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE; } res = res * 10 + chars[j] - \u0026#39;0\u0026#39;; } return sign * res; } } 判断条件 chars[j] \u0026gt; '7' 对正数和负数同样有效。当 digit = 8 时，正数已溢出，返回 Integer.MAX_VALUE；负数虽然未溢出，但此时返回 Integer.MIN_VALUE 恰好就是正确结果。\n其实除了下面的溢出判断，K神对首字符的处理也很妙，大佬不愧是大佬。\n","permalink":"http://localhost:1313/posts/leetcode8.%E5%AD%97%E7%AC%A6%E4%B8%B2%E8%BD%AC%E6%8D%A2%E6%95%B4%E6%95%B0/","summary":"\u003cp\u003e一切源于一道题目：\u003ca href=\"https://leetcode.cn/problems/string-to-integer-atoi/description/\"\u003e8. 字符串转换整数 (atoi) - 力扣（LeetCode）\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e考虑这样一个问题：给你一个数字字符串，如何在32位环境下安全的处理可能超过\u003ccode\u003e[-2^31, 2^31 - 1]\u003c/code\u003e范围的数字，不能使用64位变量临时存储。\u003c/p\u003e\n\u003cp\u003e因为我熟悉Java， 所以下面提到的数据类型都为 Java 中的数据类型。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-Java\" data-lang=\"Java\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eInteger\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eMAX_VALUE\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003e2\u003c/span\u003e\u003cspan class=\"o\"\u003e^\u003c/span\u003e\u003cspan class=\"n\"\u003e31\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003e1\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003e2147483647\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eInteger\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eMIN_VALUE\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"n\"\u003e2\u003c/span\u003e\u003cspan class=\"o\"\u003e^\u003c/span\u003e\u003cspan class=\"n\"\u003e31\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"n\"\u003e2147483648\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e首先题目说不能使用64位变量，所以 Long 类型不能使用，那就一步一步累加，具体思路就是设置两个变量 \u003ccode\u003edigit, res\u003c/code\u003e，从头到尾遍历字符串每一位，同时计算 \u003ccode\u003eres = res * 10 + digit\u003c/code\u003e。但这样的话，累加过程中 \u003ccode\u003eres\u003c/code\u003e 可能会无征兆直接溢出，程序直接抛异常。那怎么办，介绍一下从 \u003ca href=\"https://leetcode.cn/problems/string-to-integer-atoi/solutions/2361399/8-zi-fu-chuan-zhuan-huan-zheng-shu-atoiq-a2e8/\"\u003eK神题解\u003c/a\u003e 里学来的方法。\u003c/p\u003e\n\u003cp\u003e整体思路也是一步一步累加，但提前预判溢出。\u003c/p\u003e\n\u003cp\u003e这里有个核心变量 \u003ccode\u003ebndry = 2^31 / 10 = 214748364\u003c/code\u003e，，它是32位有符号整数最大值去掉最后一位的结果。\u003c/p\u003e\n\u003cp\u003e在执行 \u003ccode\u003eres = res * 10 + digit\u003c/code\u003e 之前，先检查 \u003ccode\u003eres\u003c/code\u003e 与 \u003ccode\u003ebndry\u003c/code\u003e 的关系，有下面两种溢出的可能。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e情况一：\u003ccode\u003eres \u0026gt; bndry\u003c/code\u003e ，意味着 \u003ccode\u003eres\u003c/code\u003e 乘10后，即使加上最小的数字0，结果也会超过 2147483647，溢出。\u003c/li\u003e\n\u003cli\u003e情况二：\u003ccode\u003eres == bndry\u003c/code\u003e，此时是否溢出取决于要累加的数字  \u003ccode\u003edigit\u003c/code\u003e。\n\u003cul\u003e\n\u003cli\u003e如果 \u003ccode\u003edigit \u0026lt;= 7\u003c/code\u003e，此时正数负数都不会溢出，但等于七对正数来说已经达到最大值。\u003c/li\u003e\n\u003cli\u003e如果 \u003ccode\u003edigit = 8\u003c/code\u003e，拼接后值为 2147483648，比 \u003ccode\u003eInteger.MAX_VALUE\u003c/code\u003e 大 1，而负数即 -2147483648 还未溢出。\u003c/li\u003e\n\u003cli\u003e如果 \u003ccode\u003edigit \u0026gt; 8\u003c/code\u003e，正数负数都溢出。\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e思路还是很清晰的，在下一位拼接前进行判断可以很好的避开溢出问题。下面看一下题解代码\u003c/p\u003e","title":"LeetCode8.字符串转换整数"},{"content":"先附上二哥网站上关于集合框架的结构图\n版本为JDK21\nArrayList 扩容机制 先介绍一下 ArrayList 中的关键变量：\ntransient Object[] elementData 底层用来存储元素的数组 private int size; 表示集合中元素的实际数量 private static final int DEFAULT_CAPACITY = 10; 默认初始容量 private static final Object[] EMPTY_ELEMENTDATA = {}; 当用户调用 new ArrayList(0) 时，elementData 会引用该数组。 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 当用户调用默认构造参数时会引用该数组。 private Object[] grow(int minCapacity) { int oldCapacity = elementData.length; if (oldCapacity \u0026gt; 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { int newCapacity = ArraysSupport.newLength(oldCapacity, minCapacity - oldCapacity, /* minimum growth */ oldCapacity \u0026gt;\u0026gt; 1 /* preferred growth */); return elementData = Arrays.copyOf(elementData, newCapacity); } else { return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)]; } } private Object[] grow() { return grow(size + 1); } 上面是有关扩容的源代码。\n添加元素（比如调用 add(E e) 方法）时会进行判断 if (s == elementData.length) elementData = grow() 如果当前元素数量（size）等于数组容量（elementData.length），则会触发扩容机制。\n在无参 grow 方法计算 size + 1，也就是有参 grow 函数的参数 minCapacity（所需最小容量）。\n当数组通过默认构造实现，且从未添加元素（即 oldCapacity == 0 \u0026amp;\u0026amp; elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA）时，会进入 else 分支，创建一个新数组，数组长度为10。\n如果进入 if 分支，会计算新容量，newLength 方法如下\npublic static int newLength(int oldLength, int minGrowth, int prefGrowth) { // preconditions not checked because of inlining // assert oldLength \u0026gt;= 0 // assert minGrowth \u0026gt; 0 int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflow if (0 \u0026lt; prefLength \u0026amp;\u0026amp; prefLength \u0026lt;= SOFT_MAX_ARRAY_LENGTH) { return prefLength; } else { // put code cold in a separate method return hugeLength(oldLength, minGrowth); } } 这段代码会比较 minGrowth 和 prefGrowth，一个表示最小增长量，一个表示首选增长量，在大多数情况下后者更大，所以一般情况下会说1.5倍扩容。计算得到 prefLength ，如果该值超过了最大容量限制，则调用 hugeLength 方法，这里不再描述。\n得到 newCapacity 后，创建新数组并拷贝原数组数据，返回新数组，扩容结束。\n有时创建集合时，可以在构造函数中指定数组大小，避免频繁的扩容。\nHashMap 的 put 方法 public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } 第一步，在 put 方法里先将 key 传入 hash 函数（在该函数里，获得 key 的哈希值后进一步对其位运算和异或运算，扰乱哈希值，减少哈希冲突），然后将得到的值与其他值传入 putVal 函数。\nif ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((p = tab[i = (n - 1) \u0026amp; hash]) == null) tab[i] = newNode(hash, key, value, null); 第二步，如果数组为空，进行第一次数组扩容。同时计算索引位置，如果当前位置为空，则直接将键值对插入该位置。这里有个小技巧，当 n 是2的幂次方时 hash \u0026amp; (n - 1) 在效果上和 hash % n 是一样的。\nelse { Node\u0026lt;K,V\u0026gt; e; K k; if (p.hash == hash \u0026amp;\u0026amp; ((k = p.key) == key || (key != null \u0026amp;\u0026amp; key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode\u0026lt;K,V\u0026gt;)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount \u0026gt;= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } if (e.hash == hash \u0026amp;\u0026amp; ((k = e.key) == key || (key != null \u0026amp;\u0026amp; key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size \u0026gt; threshold) resize(); afterNodeInsertion(evict); return null; 第三步，如果当前位置不为空，进入 else 分支。此时 p 指向当前桶位 tab[i] 的第一个节点。\n全方位比较传入对象和变量 p 的属性：先比较哈希值，如果 hash 值不同，key 肯定不同。然后比较引用或值，一个判断内存地址是否相同，一个判断内容是否相同。如果条件都满足，证明找到了完全相同的 key ，将变量 e 指向 p。\n如果进入 elseif 分支，会判断是否为红黑树节点，如果第一个节点不匹配，且该节点为 TreeNode 类型，说明该桶已树化，调用其他方法将新节点插入到红黑树中，在该方法中查找 key，找到则返回对应节点，找不到返回 null。\n如果进入 else 分支，就是循环遍历链表，如果遍历到尾部也没找到，在尾部插入新节点，并判断是否需要树化。如果中途找到，则停止遍历。变量 e 指向找到的节点。\n到这里 e 会有两种情况，如果 e != null，说明链表或红黑树中找到了已存在的 key，用新 value 覆盖旧 value，然后返回旧值。如果 e == null，说明没有找到，并成功在链表或红黑树中插入了新节点。\n最后，根据 size 大小判断是否需要再次扩容。\n整体思路是先查后改，查完之后变量 e 指向了需要更新的旧节点。\nHashMap 传参初始化 先说结论，如果传的是 17，HashMap 会将容量调整到大于等于 17 的最小的 2 的幂次方，如果不传参默认值是 16。\npublic HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity \u0026lt; 0) throw new IllegalArgumentException(\u0026#34;Illegal initial capacity: \u0026#34; + initialCapacity); if (initialCapacity \u0026gt; MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor \u0026lt;= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException(\u0026#34;Illegal load factor: \u0026#34; + loadFactor); this.loadFactor = loadFactor; this.threshold = tableSizeFor(initialCapacity); } static final int tableSizeFor(int cap) { int n = -1 \u0026gt;\u0026gt;\u0026gt; Integer.numberOfLeadingZeros(cap - 1); return (n \u0026lt; 0) ? 1 : (n \u0026gt;= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; } 相关代码如图，函数调用从上到下，最关键的是 tableSizeFor 方法。\n第一步先将 cap 减一，这是为了处理 cap 本身就是 2 的幂次方的情况。\n第二步进入 numberOfLeadingZeros 方法，该方法返回从最高位开始连续的 0 的个数，比如传入 15，返回 28（二进制表示共 32 位）。\n第三步将 -1 无符号右移前导零的个数。-1 的二进制表示位 32 个 1（补码），无符号右移高位补 0。\n第四步进行三元运算符判断。如果右移 32 位的话，先对 32 取模，比如 -1 右移 32 位还是 -1，因为 32 取模后为 0，相当于没有右移。\n举个例子，当 cap 为 18 时，二进制表示为0000 0000 0000 0000 0000 0000 0001 0010，减一后为0000 0000 0000 0000 0000 0000 0001 0001，前导 0 有27个，所以将 -1 的二进制表示右移 27 个，得到0000 0000 0000 0000 0000 0000 0001 1111，十进制表示为 31，最后加 1 为 32。\npublic static int numberOfLeadingZeros(int i) { // HD, Count leading 0\u0026#39;s if (i \u0026lt;= 0) return i == 0 ? 32 : 0; int n = 31; if (i \u0026gt;= 1 \u0026lt;\u0026lt; 16) { n -= 16; i \u0026gt;\u0026gt;\u0026gt;= 16; } if (i \u0026gt;= 1 \u0026lt;\u0026lt; 8) { n -= 8; i \u0026gt;\u0026gt;\u0026gt;= 8; } if (i \u0026gt;= 1 \u0026lt;\u0026lt; 4) { n -= 4; i \u0026gt;\u0026gt;\u0026gt;= 4; } if (i \u0026gt;= 1 \u0026lt;\u0026lt; 2) { n -= 2; i \u0026gt;\u0026gt;\u0026gt;= 2; } return n - (i \u0026gt;\u0026gt;\u0026gt; 1); } 接下来解释这段神奇的源码：把负数和 0 的情况排除后，先假设最高位在 31 位，如果 i \u0026gt;= 1 \u0026lt;\u0026lt; 16，意思就是 i 大于等于 2 的 16 次方，说明最高位 1 一定在前 16 位中，这时 n 减 16，i 右移 16 位，这个操作相当于把 i 的低十六位丢弃，因为都是 0，然后将高 16 位移到低 16 位处理，如果 i 不大于等于 2 的 16 次方，就说明最高位 1 在后十六位，不做处理。接下来处理类似，在剩下的 16 位中，如果高 8 位满足条件，说明最高位 1 在这高 8 位中。n 减去 8，i 右移 8 位。\n处理到最后， i 的值最多剩下 2 位，分别是 00 01 10 11，n - (i \u0026gt;\u0026gt;\u0026gt; 1) 实际上是在判断剩下 2 位中最高位 1 的位置。整体思想类似二分查找。\n这个方法上有 @IntrinsicCandidate 注解，表明会使用专门的 CPU 指令来代替这段代码，从而获得极高的执行速度。\nHashMap resize 扩容方法 常见的执行 resize 方法的有两种情况：第一次 put 时的初始化扩容；元素数量超过阈值时的扩容。\n先贴一下完整的方法\nfinal Node\u0026lt;K,V\u0026gt;[] resize() { Node\u0026lt;K,V\u0026gt;[] oldTab = table; int oldCap = (oldTab == null) ? 0 : oldTab.length; int oldThr = threshold; int newCap, newThr = 0; if (oldCap \u0026gt; 0) { if (oldCap \u0026gt;= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; } else if ((newCap = oldCap \u0026lt;\u0026lt; 1) \u0026lt; MAXIMUM_CAPACITY \u0026amp;\u0026amp; oldCap \u0026gt;= DEFAULT_INITIAL_CAPACITY) newThr = oldThr \u0026lt;\u0026lt; 1; // double threshold } else if (oldThr \u0026gt; 0) // initial capacity was placed in threshold newCap = oldThr; else { // zero initial threshold signifies using defaults newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap \u0026lt; MAXIMUM_CAPACITY \u0026amp;\u0026amp; ft \u0026lt; (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } threshold = newThr; @SuppressWarnings({\u0026#34;rawtypes\u0026#34;,\u0026#34;unchecked\u0026#34;}) Node\u0026lt;K,V\u0026gt;[] newTab = (Node\u0026lt;K,V\u0026gt;[])new Node[newCap]; table = newTab; if (oldTab != null) { for (int j = 0; j \u0026lt; oldCap; ++j) { Node\u0026lt;K,V\u0026gt; e; if ((e = oldTab[j]) != null) { oldTab[j] = null; if (e.next == null) newTab[e.hash \u0026amp; (newCap - 1)] = e; else if (e instanceof TreeNode) ((TreeNode\u0026lt;K,V\u0026gt;)e).split(this, newTab, j, oldCap); else { // preserve order Node\u0026lt;K,V\u0026gt; loHead = null, loTail = null; Node\u0026lt;K,V\u0026gt; hiHead = null, hiTail = null; Node\u0026lt;K,V\u0026gt; next; do { next = e.next; if ((e.hash \u0026amp; oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } else { if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; } 先说前面的条件判断：\n第一种情况：正常扩容。此时 oldCap \u0026gt; 0，说明是因为元素过多触发了扩容。如果容量已达上限，将阈值设为 Integer.MAX_VALUE，这样就不会触发扩容了，直接返回旧数组；否则进入 elseif 分支（其实这个分支的条件我不理解），容量和阈值都翻倍。\n第二种情况：带参构造初始化，此时 oldCap == 0 \u0026amp;\u0026amp; oldThr \u0026gt; 0，初始容量设置为旧的 threshold。从上面的构造器源码可以看出，如果带参构造，loadFactor 和 threshold 将被赋值，所以 oldThr \u0026gt; 0。\n第三种情况：无参构造，这种情况下指定容量为 16，阈值为 12（16 * 0.75）。\n然后补充计算一下新阈值，因为如果是第二种情况，newThr 仍然为 0。\n接下来是 resize 的后半部分。\n首先更新属性 threshold，创建新数组并赋值给 table，接下来迭代遍历旧 table。注意 oldTab[j] = null，为了方便回收器回收。\n第一种情况，该桶只有一个节点，根据公式计算新索引位置，公式在 put 方法里提到过\n第二种情况，该位置是红黑树节点，调用 split 方法重新分配。\n第三种情况，排除前两种后，该位置只可能是链表，使用特殊处理方法。\n对链表的处理比较复杂，也是 jdk8 优化的地方，jdk7 会重新计算每个元素的哈希值，而现在可以通过位运算直接判断位置。核心就是根据条件构建两条链表，根据 e.hash \u0026amp; oldCap 的值，如果等于 0，将节点接入低位链（low），否则接入高位链（high）。链表遍历结束后将低位链放在原索引位置：newTab[j] = loHead; ，高位链移动到原索引 + oldCap 的位置上：newTab[j + oldCap] = hiHead。\n推导一下涉及到的公式 (e.hash \u0026amp; oldCap) == 0：\n明确元素在表中的位置由hash \u0026amp; (n-1)决定。那么 e.hash \u0026amp; oldCap 实际上是在检查哈希值中对应 oldCap 那个 1 的位是否为1：如果为 1，说明扩容后位置会移动 oldCap 的长度，也就对应了该公式：newTab[j + oldCap] = hiHead；如果为 0，说明扩容后位置不变。\n举一个具体的例子，全部用二进制表示，所描述的第几位是从低位开始数。\n假设 oldCap = 10000, newCap = 100000。\n如果元素 e 的 hash 值为 000101，第五位为 0 ，说明扩容后索引位置不变，经过验证 e.hash \u0026amp; oldCap == e.hash \u0026amp; newCap。\n如果元素 e 的 hash 值为 010101，第五位为 1 ，说明扩容后索引位置增加 oldCap，经过验证 (e.hash \u0026amp; oldCap) + oldCap == e.hash \u0026amp; newCap。\n妙！\n","permalink":"http://localhost:1313/posts/java%E9%9B%86%E5%90%88%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB/","summary":"\u003cp\u003e先附上二哥网站上关于集合框架的结构图\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image-20260126165155214\" loading=\"lazy\" src=\"https://dongimagehost-1356670526.cos.ap-nanjing.myqcloud.com/2025/07/image-20260126165155214.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e版本为JDK21\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"arraylist-扩容机制\"\u003eArrayList 扩容机制\u003c/h2\u003e\n\u003cp\u003e先介绍一下 ArrayList 中的关键变量：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003etransient Object[] elementData\u003c/code\u003e 底层用来存储元素的数组\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eprivate int size;\u003c/code\u003e 表示集合中元素的实际数量\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eprivate static final int DEFAULT_CAPACITY = 10;\u003c/code\u003e 默认初始容量\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eprivate static final Object[] EMPTY_ELEMENTDATA = {};\u003c/code\u003e 当用户调用 \u003ccode\u003enew ArrayList(0)\u003c/code\u003e 时，elementData 会引用该数组。\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eprivate static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};\u003c/code\u003e 当用户调用默认构造参数时会引用该数组。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-Java\" data-lang=\"Java\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eObject\u003c/span\u003e\u003cspan class=\"o\"\u003e[]\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nf\"\u003egrow\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eminCapacity\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eoldCapacity\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eelementData\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003elength\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eoldCapacity\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003e0\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e||\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eelementData\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e!=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eDEFAULTCAPACITY_EMPTY_ELEMENTDATA\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003enewCapacity\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eArraysSupport\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003enewLength\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eoldCapacity\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                                                  \u003c/span\u003e\u003cspan class=\"n\"\u003eminCapacity\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eoldCapacity\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cm\"\u003e/* minimum growth */\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                                                  \u003c/span\u003e\u003cspan class=\"n\"\u003eoldCapacity\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003e1\u003c/span\u003e\u003cspan class=\"w\"\u003e           \u003c/span\u003e\u003cspan class=\"cm\"\u003e/* preferred growth */\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eelementData\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eArrays\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003ecopyOf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eelementData\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003enewCapacity\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eelse\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eelementData\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eObject\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eMath\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003emax\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eDEFAULT_CAPACITY\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eminCapacity\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eObject\u003c/span\u003e\u003cspan class=\"o\"\u003e[]\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nf\"\u003egrow\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003egrow\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esize\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e上面是有关扩容的源代码。\u003c/p\u003e","title":"Java集合源码阅读"},{"content":"整理一下MySQL锁的八股，全文整理自二哥博客。\n在MySQL数据库中，锁是用来协调多个进程或线程并发访问同一资源的机制。锁不仅保证了数据的一致性和有效性，而且是影响数据库并发访问性能的一个重要因素。\n共享锁与排他锁 共享锁也叫 S(shared) 锁，允许多个事务进行读操作，阻塞写操作。\n排他锁也叫 X(exclusive) 锁，只允许一个事务进行读写操作，阻塞其他事务的读写操作。\n兼容性如下：\n表锁与行锁 表锁：锁定整个表，资源开销小，加锁快，但并发度低，不会出现死锁，适合查询为主、少量更新的场景（如 MyISAM 引擎）。可以细分为表级S锁、表级X锁。\n行锁：锁定单行或多行，开销大、加锁慢，可能出现死锁，但并发度高（InnoDB 默认支持）。可以细分为记录锁、间隙锁、临键锁，也可以分为共享锁和排他锁（与表级S锁、表级X锁一个意思，前提是行锁）。\n表锁详细版： 表锁常见于 MyISAM 引擎， InnoDB 也可手动加锁，适合读多写少、全表扫描或者表结构变更的场景。\nLOCK TABLES table_name READ; -- 显式加读锁 select... -- 其他会话可读，不可写 UNLOCK TABLES; -- 释放锁 LOCK TABLES table_name WRITE; -- 显式加写锁 INSERT/UPDATE/DELETE table_name; -- 其他会话读写均阻塞 UNLOCK TABLES; MyISAM 在执行 SELECT 时会自动加读锁，执行 INSERT/UPDATE/DELETE 时会加写锁。\n对于 InnoDB 引擎，无索引的 UPDATE/DELETE 可能会导致锁升级为表锁。执行 ALTER TABLE 时会自动加表锁，阻塞所有读写操作。\n行锁详细版： 行锁是 InnoDB 存储引擎中最细粒度的锁，它锁定表中的一行记录，允许其他事务访问表中的其他行。\n底层是通过给索引加锁实现的，这就意味着只有通过索引条件检索数据时，InnoDB 才能使用行级锁，否则会退化为表锁。\n默认情况下，InnoDB 在 REPEATABLE READ 事务隔离级别运行，默认的行锁类型为临键锁。\n如果使用排他锁，注意两个点：第一就是必须在事务中使用，否则锁会立即释放。第二就是使用时必须注意是否命中索引，否则可能退化为表锁。\n行锁又可以细分为记录锁、间隙锁和临键锁三种形式。\n记录锁是行锁最基本的表现形式，当我们使用唯一索引或者主键索引进行等值查询时，MySQL 会为该记录自动添加排他锁，禁止其他事务读取或者修改锁定记录。（很奇怪）\n间隙锁用于在范围查询时锁定记录之间的“间隙”，防止其他事务在该范围内插入新记录。仅在可重复读及以上的隔离级别下生效，主要用于防止幻读。\n临键锁是记录锁和间隙锁的结合体，锁住的是索引记录和索引记录之间的间隙。临键锁的间隙是一个左开右闭区间。间隙锁为左开右开。\nMySQL 默认的行锁类型就是临键锁。当使用唯一索引的等值查询匹配到一条记录时，临键锁会退化成记录锁；如果没有匹配到任何记录，会退化成间隙锁。\n意向锁 意向锁是一种表级锁，表示事务打算对表中的某些行数据加锁，但不会直接锁定数据行本身。\n由 InnoDB 自动管理，当事务需要添加行锁时，会先在表上添加意向锁。这样当要添加表锁的时候，可以通过查看表上的意向锁，快速判断是否有冲突，而无需逐行检查，从而提高加锁效率。\n存在的意义：在没有意向锁的情况下，当事务 A 持有某表的行锁时，如果事务 B 想添加表锁，InnoDB 必须检查表中每一行数据是否被加锁，这种全表扫描的方式效率极低。有了意向锁之后，事务在加行锁前，先在表上加对应的意向锁；其他事务加表锁时，只需检查表上的意向锁，无需逐行检查。\n意向锁之间相互兼容\n乐观锁与悲观锁 悲观锁是一种\u0026quot;先上锁再操作\u0026quot;的保守策略，它假设数据被外界访问时必然会产生冲突，因此在数据处理过程中全程加锁，保证同一时间只有一个线程可以访问数据。MySQL 中的行锁和表锁都是悲观锁。\n乐观锁会假设并发操作不会总发生冲突，属于小概率事件，因此不会在读取数据时加锁，而是在提交更新时才检查数据是否被其他事务修改过。它并不是 MySQL 内置的锁机制，而是通过程序逻辑实现的，常见的实现方式有版本号机制和时间戳机制，对应的向表中添加 version 字段或者 timestamp 字段来实现。\n如何通过悲观锁和乐观锁解决库存问题\n死锁问题 MySQL 的死锁是由于多个事务持有资源并相互等待引起的。我通过 SHOW ENGINE INNODB STATUS 查看死锁信息，定位到是加锁顺序不一致导致的，最后通过调整加锁顺序解决了这个问题。\n补充：\n全局锁就是对整个数据库实例进行加锁，当执行全局锁定操作时，整个数据库将会处于只读状态，所有写操作都会被阻塞，直到全局锁被释放。\n在进行全库备份，或者数据迁移时，可以使用全局锁来保证数据的一致性。\n","permalink":"http://localhost:1313/posts/mysql%E9%94%81%E6%9C%BA%E5%88%B6%E5%85%AB%E8%82%A1%E6%95%B4%E7%90%86/","summary":"\u003cp\u003e整理一下\u003ccode\u003eMySQL\u003c/code\u003e锁的八股，全文整理自\u003ca href=\"https://javabetter.cn/sidebar/sanfene/mysql.html#_53-%F0%9F%8C%9Fmysql-%E4%B8%AD%E6%9C%89%E5%93%AA%E5%87%A0%E7%A7%8D%E9%94%81\"\u003e二哥博客\u003c/a\u003e。\u003c/p\u003e\n\u003cp\u003e在MySQL数据库中，锁是用来协调多个进程或线程并发访问同一资源的机制。锁不仅保证了数据的一致性和有效性，而且是影响数据库并发访问性能的一个重要因素。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image-20251206162234600\" loading=\"lazy\" src=\"https://dongimagehost-1356670526.cos.ap-nanjing.myqcloud.com/2025/07/image-20251206162234600.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"共享锁与排他锁\"\u003e共享锁与排他锁\u003c/h2\u003e\n\u003cp\u003e共享锁也叫 S(shared) 锁，允许多个事务进行读操作，阻塞写操作。\u003c/p\u003e\n\u003cp\u003e排他锁也叫 X(exclusive) 锁，只允许一个事务进行读写操作，阻塞其他事务的读写操作。\u003c/p\u003e\n\u003cp\u003e兼容性如下：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image-20251206181825893\" loading=\"lazy\" src=\"https://dongimagehost-1356670526.cos.ap-nanjing.myqcloud.com/2025/07/image-20251206181825893.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"表锁与行锁\"\u003e表锁与行锁\u003c/h2\u003e\n\u003cp\u003e表锁：锁定整个表，资源开销小，加锁快，但并发度低，不会出现死锁，适合查询为主、少量更新的场景（如 MyISAM 引擎）。可以细分为表级S锁、表级X锁。\u003c/p\u003e\n\u003cp\u003e行锁：锁定单行或多行，开销大、加锁慢，可能出现死锁，但并发度高（InnoDB 默认支持）。可以细分为记录锁、间隙锁、临键锁，也可以分为共享锁和排他锁（与表级S锁、表级X锁一个意思，前提是行锁）。\u003c/p\u003e\n\u003ch3 id=\"表锁详细版\"\u003e表锁详细版：\u003c/h3\u003e\n\u003cp\u003e表锁常见于 MyISAM 引擎， InnoDB 也可手动加锁，适合读多写少、全表扫描或者表结构变更的场景。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eLOCK\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eTABLES\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003etable_name\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eREAD\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c1\"\u003e-- 显式加读锁\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eselect\u003c/span\u003e\u003cspan class=\"p\"\u003e...\u003c/span\u003e\u003cspan class=\"w\"\u003e                    \u003c/span\u003e\u003cspan class=\"c1\"\u003e-- 其他会话可读，不可写\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eUNLOCK\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eTABLES\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e               \u003c/span\u003e\u003cspan class=\"c1\"\u003e-- 释放锁\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eLOCK\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eTABLES\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003etable_name\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eWRITE\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"c1\"\u003e-- 显式加写锁 \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eINSERT\u003c/span\u003e\u003cspan class=\"o\"\u003e/\u003c/span\u003e\u003cspan class=\"k\"\u003eUPDATE\u003c/span\u003e\u003cspan class=\"o\"\u003e/\u003c/span\u003e\u003cspan class=\"k\"\u003eDELETE\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003etable_name\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c1\"\u003e-- 其他会话读写均阻塞 \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eUNLOCK\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eTABLES\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eMyISAM 在执行 \u003ccode\u003eSELECT\u003c/code\u003e 时会自动加读锁，执行 \u003ccode\u003eINSERT/UPDATE/DELETE\u003c/code\u003e 时会加写锁。\u003c/p\u003e\n\u003cp\u003e对于 InnoDB 引擎，无索引的 \u003ccode\u003eUPDATE/DELETE\u003c/code\u003e 可能会导致锁升级为表锁。执行 \u003ccode\u003eALTER TABLE\u003c/code\u003e 时会自动加表锁，阻塞所有读写操作。\u003c/p\u003e\n\u003ch3 id=\"行锁详细版\"\u003e行锁详细版：\u003c/h3\u003e\n\u003cp\u003e行锁是 InnoDB 存储引擎中最细粒度的锁，它锁定表中的一行记录，允许其他事务访问表中的其他行。\u003c/p\u003e\n\u003cp\u003e底层是通过给索引加锁实现的，这就意味着只有通过索引条件检索数据时，InnoDB 才能使用行级锁，否则会退化为表锁。\u003c/p\u003e","title":"MySQL锁机制八股整理"},{"content":"整理一下MySQL索引的八股。\n索引介绍 索引是数据库中用于加速查询的数据结构，通过对表中某列或多列的值进行排序，可以快速定位所要查找的数据。就像小时候在汉语字典里查字时，会先根据偏旁笔画数找到偏旁，然后根据剩余笔画找到目标字，这比在整个字典中一个一个查找是快很多的。\n索引分类 索引有以下分类\n索引详细介绍 主键索引： 主键索引用于唯一标识表中的每条记录，其值必须唯一且非空。每个表只允许有一个主键索引，一般为表中的自增id。\n创建主键时，数据库会自动生成主键索引。若没有指定主键，MySQL的InnoDB存储引擎会优先选择一个非空的唯一索引作为主键，如果没有符合条件的索引，MySQL会自动生成一个隐藏的聚簇索引。\n唯一索引： 主键索引 = 唯一索引 + 非空，每个表可以有多个唯一索引，同时唯一索引允许值为NULL，这是和主键索引不同的地方。唯一索引强制字段值的唯一性，插入或更新时会触发唯一检查，适用于业务唯一性约束的字段，比如邮箱。在建表定义唯一键时，会自动生成唯一索引。\n键和索引的区别在于键是逻辑概念，而索引是物理实现，在磁盘中有着实际的存储。\n普通索引： 普通索引仅用于加速查询，不会限制字段值的唯一性。\n全文索引 全文索引是MySQL一种优化文本数据检索的特殊类型索引，适用于CHAR, VARCHAR, TEXT等字段。MySQL 5.7 及以上版本内置了 ngram 解析器，可处理中文、日文和韩文等分词。全文索引底层不再是 B+ 树索引。\n建表时通过 FULLTEXT (title, body) 来定义。通过 MATCH(col1, col2) AGAINST('keyword') 进行检索，默认按照降序返回结果，支持布尔模式查询。\n+ 表示必须包含； - 表示排除； * 表示通配符； -- 使用布尔模式查询 SELECT * FROM articles WHERE MATCH(title, content) AGAINST(\u0026#39;+MySQL -Oracle\u0026#39; IN BOOLEAN MODE); 这样查询性能比LIKE '%keyword%'高很多。对于复杂的中文场景可以用Elasticsearch等专业搜索引擎替代。\nB+ 树索引： B+ 树是一种高度平衡的多路查找树，能有效降低磁盘的 IO 次数，并且支持有序遍历和范围查询。\n相比普通二叉树，B+ 树可以将亿级数据量控制在3-4层树高，极大减少磁盘的 I/O 次数。因为树越高意味着查找数据时就需要更多的磁盘 IO，因为每一层都可能需要从磁盘加载新的节点。\n相比二叉平衡树，B+ 树每个节点拥有多个子节点，可存储数据更多。\n相比 B 树：B+ 树的非叶子节点只存储键值，叶子节点存储数据并通过链表连接，支持范围查询。这样的好处是非叶子节点不存储数据，就可以存储更多的键值对；叶子节点构成有序链表（双向链表），范围查询时可以直接通过叶子节点间的指针 顺序访问 整个查询范围内的记录，而无需对树进行多次遍历，查询效率高。\n相比哈希表：B+ 树支持范围查询和排序\n哈希索引： Hash 索引基于哈希函数将键值映射到固定长度的哈希值，通过哈希值定位数据存储的位置，完全无序，只支持等值查找，速度快功能少，常见于 Memory 引擎。\nInnoDB 内部使用了一种名为 “自适应哈希索引” 的技术，当某些索引值频繁访问时，InnoDB 会在 B+ 树基础上自动创建哈希索引，兼具两者的优点。\n聚簇索引与非聚簇索引： 聚簇索引的叶子节点存储了完整的数据行，数据和索引是在一起的，不仅存储了主键值，还存储了其他列的值。\nInnoDB 的主键索引就是聚簇索引，因此按照主键进行查询的速度会非常快。\n每个表只能有一个聚簇索引，通常由主键定义。\n非聚簇索引的叶子节点只包含了索引列和主键值，需要通过回表按照主键去聚簇索引查找其他列的值，唯一索引、普通索引等非主键索引都是非聚簇索引。回表通常需要访问额外的数据页，如果数据不在内存中还需要从磁盘读取，增加 I/O 开销。可通过覆盖索引或者联合索引来避免回表。\n补充 一棵 B+ 树能存多少数据： 一棵 B+ 树能存多少数据，取决于它的分支因子和高度。在 InnoDB 中，页的默认大小为 16KB，当主键为 bigint 时，3 层 B+ 树通常可以存储约 2000 万条数据。\nB+ 树相比 B 树的三个优势 B 树的每个节点既存储键值，又存储数据和指针，导致单节点存储的键值数量较少。\nB 树的范围查询需要通过中序遍历逐层回溯；而 B+ 树的叶子节点通过双向链表顺序连接，范围查询只需定位起始点后顺序遍历链表即可，没有回溯开销。\nB 树的数据可能存储在任意节点，假如目标数据恰好位于根节点或上层节点，查询仅需 1-2 次 I/O；但如果数据位于底层节点，则需多次 I/O，导致查询时间波动较大。\n而 B+ 树的所有数据都存储在叶子节点，查询路径的长度是固定的，时间稳定为 O(logN)(N是数据总量)，对 MySQL 在高并发场景下的稳定性至关重要。\n联合索引介绍： 前面提到可通过覆盖索引或者联合索引来避免回表。如果一个查询只需要访问索引中已经包含的列，那么这个查询就可以被索引“覆盖”，不需要回表，这就是覆盖索引。至于联合索引就是把多个字段放在一个索引里，必须遵循 \u0026ldquo;最左前缀\u0026rdquo; 原则。\n联合索引属于非聚簇索引。与单列索引不同的是，联合索引的每个节点会存储所有索引列的值，而不仅仅是第一列的值。\n关于最左前缀涉及到的一些问题：\n范围查询只能应用于最左前缀的最后一列。范围查询之后的列无法使用索引。\nSQL -- 索引(a,b,c) SELECT * FROM table WHERE a = 1 AND b \u0026gt; 2 AND c = 3; -- 只能使用a和b，c无法使用索引 联合索引在 B+ 树中是按照最左字段优先排序构建的，如果跳过最左字段，MySQL 无法判断查找范围从哪里开始，自然也就无法使用索引。\n如果查询模式是后缀通配符 LIKE 'prefix%'，且该字段有索引，优化器通常会使用索引。否则即便是遵循最左前缀匹配，LIKE 字段也无法命中索引。\n如果排序(order by)或分组(group by)的列是最左前缀的一部分，索引还可以加速操作。\n索引下推： 索引下推是指：MySQL 把 WHERE 条件尽可能“下推”到索引扫描阶段，在存储引擎层提前过滤掉不符合条件的记录。\n在传统的查询处理方式中，存储引擎首先根据索引读取数据并将其加载到内存中，然后在内存中应用 WHERE 子句中的过滤条件，筛选出符合条件的数据行。这种方式可能导致大量的数据传输，尤其是当数据量较大时。\n而使用索引下推优化后，存储引擎在存储层就应用 WHERE 子句中的过滤条件，只有符合条件的数据才会被加载到内存中进一步处理。这可以减少数据传输量，从而提高查询效率。\n其他问题 哪些情况下索引会失效： 对索引列使用函数或表达式会导致索引失效。 LIKE 模糊查询以通配符开头会导致索引失效。 联合索引违反了最左前缀原则，索引会失效。 使用 OR 连接非索引列条件，会导致索引失效。 使用 != 或 \u0026lt;\u0026gt; 不等值查询会导致索引失效。 （隐式类型转换也会导致索引失效） 创建索引有哪些注意点： 选择合适的字段。比如说频繁出现在 WHERE、JOIN、ORDER BY、GROUP BY 中的字段。优先选择区分度高的字段，比如用户 ID、手机号等唯一值多的，而不是性别、状态等区分度极低的字段，如果真的需要，可以考虑联合索引。 要控制索引的数量，避免过度索引，每个索引都要占用存储空间，单表的索引数量不建议超过 5 个。 联合索引的时候要遵循最左前缀原则。区分度高的字段放在左侧，等值查询的字段优先于范围查询的字段。例如 WHERE A=1 AND B\u0026gt;10 AND C=2，优先 (A, C, B)。 全篇整理自二哥博客。\n","permalink":"http://localhost:1313/posts/mysql%E7%B4%A2%E5%BC%95%E5%85%AB%E8%82%A1%E6%95%B4%E7%90%86/","summary":"\u003cp\u003e整理一下\u003ccode\u003eMySQL\u003c/code\u003e索引的八股。\u003c/p\u003e\n\u003ch2 id=\"索引介绍\"\u003e索引介绍\u003c/h2\u003e\n\u003cp\u003e索引是数据库中用于加速查询的数据结构，通过对表中某列或多列的值进行排序，可以快速定位所要查找的数据。就像小时候在汉语字典里查字时，会先根据偏旁笔画数找到偏旁，然后根据剩余笔画找到目标字，这比在整个字典中一个一个查找是快很多的。\u003c/p\u003e\n\u003ch2 id=\"索引分类\"\u003e索引分类\u003c/h2\u003e\n\u003cp\u003e索引有以下分类\u003c/p\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003ch2 id=\"索引详细介绍\"\u003e索引详细介绍\u003c/h2\u003e\n\u003ch3 id=\"主键索引\"\u003e主键索引：\u003c/h3\u003e\n\u003cp\u003e主键索引用于唯一标识表中的每条记录，其值必须唯一且非空。每个表只允许有一个主键索引，一般为表中的自增id。\u003c/p\u003e\n\u003cp\u003e创建主键时，数据库会自动生成主键索引。若没有指定主键，\u003ccode\u003eMySQL\u003c/code\u003e的\u003ccode\u003eInnoDB\u003c/code\u003e存储引擎会优先选择一个非空的唯一索引作为主键，如果没有符合条件的索引，\u003ccode\u003eMySQL\u003c/code\u003e会自动生成一个隐藏的聚簇索引。\u003c/p\u003e\n\u003ch3 id=\"唯一索引\"\u003e唯一索引：\u003c/h3\u003e\n\u003cp\u003e主键索引 = 唯一索引 + 非空，每个表可以有多个唯一索引，同时唯一索引允许值为NULL，这是和主键索引不同的地方。唯一索引强制字段值的唯一性，插入或更新时会触发唯一检查，适用于业务唯一性约束的字段，比如邮箱。在建表定义唯一键时，会自动生成唯一索引。\u003c/p\u003e\n\u003cp\u003e键和索引的区别在于键是逻辑概念，而索引是物理实现，在磁盘中有着实际的存储。\u003c/p\u003e\n\u003ch3 id=\"普通索引\"\u003e普通索引：\u003c/h3\u003e\n\u003cp\u003e普通索引仅用于加速查询，不会限制字段值的唯一性。\u003c/p\u003e\n\u003ch3 id=\"全文索引\"\u003e全文索引\u003c/h3\u003e\n\u003cp\u003e全文索引是\u003ccode\u003eMySQL\u003c/code\u003e一种优化文本数据检索的特殊类型索引，适用于\u003ccode\u003eCHAR, VARCHAR, TEXT\u003c/code\u003e等字段。MySQL 5.7 及以上版本内置了 ngram 解析器，可处理中文、日文和韩文等分词。全文索引底层不再是 B+ 树索引。\u003c/p\u003e\n\u003cp\u003e建表时通过 \u003ccode\u003eFULLTEXT (title, body)\u003c/code\u003e 来定义。通过 \u003ccode\u003eMATCH(col1, col2) AGAINST('keyword')\u003c/code\u003e 进行检索，默认按照降序返回结果，支持布尔模式查询。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003e+\u003c/code\u003e 表示必须包含；\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e-\u003c/code\u003e 表示排除；\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e*\u003c/code\u003e 表示通配符；\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e-- 使用布尔模式查询\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eSELECT\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eFROM\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003earticles\u003c/span\u003e\u003cspan class=\"w\"\u003e \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eWHERE\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eMATCH\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003econtent\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eAGAINST\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;+MySQL -Oracle\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eIN\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nb\"\u003eBOOLEAN\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eMODE\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e这样查询性能比\u003ccode\u003eLIKE '%keyword%'\u003c/code\u003e高很多。对于复杂的中文场景可以用\u003ccode\u003eElasticsearch\u003c/code\u003e等专业搜索引擎替代。\u003c/p\u003e\n\u003ch3 id=\"b-树索引\"\u003eB+ 树索引：\u003c/h3\u003e\n\u003cp\u003eB+ 树是一种高度平衡的多路查找树，能有效降低磁盘的 IO 次数，并且支持有序遍历和范围查询。\u003c/p\u003e","title":"MySQL索引八股整理"},{"content":"泛型定义 先看这样一个例子：\npublic static void main(String[] args) { List list = new ArrayList(); list.add(\u0026#34;aaa\u0026#34;); list.add(\u0026#34;bbb\u0026#34;); list.add(\u0026#34;ccc\u0026#34;); for (int i = 0; i \u0026lt; list.size(); i++) { System.out.println((String)list.get(i)); } } ArrayList集合中可以加入任何类型的对象，我本意是用这个集合来存储字符串（因为ArrayList默认存储的是Object类型的对象，所以输出时需要强转 ），但我粗心的写错了list.add(123)，在我运行后发现报错java.lang.ClassCastException，显然报错原因就是输出时Integer类型并不能强转为String类型。那么如何避免这种情况呢，答案就是泛型。\n借用一下维基百科的定义：\n泛型程序设计是程序设计语言的一种风格或范型，泛型允许程序员在强类型程序设计语言中 编写代码时 使用一些 以后才指定的类型，在实例化时作为参数指明这些类型。\n说一下我的理解，泛字让我联想到了一个词语：泛泛而谈，指那些浮浅平淡，不深入的谈话。这里也可以这样理解，定义时我模糊的说明一下参数，不指明参数具体是哪种类型，等我使用时再说明。\n有这样一种说法：泛型的本质就是“参数化类型”。想象一下，定义一个方法需要形参，待你调用时，又需要传递实参。泛型亦是如此，定义时形式上意思意思，真正使用时再说明。其实都一个意思。\n下面我将从泛型类、泛型方法、泛型接口、通配符、类型擦除五个方面来对Java泛型进行详细说明。\n泛型类 来看一下ArrayList这个类的定义\npublic class ArrayList\u0026lt;E\u0026gt; extends AbstractList\u0026lt;E\u0026gt; implements List\u0026lt;E\u0026gt;, RandomAccess, Cloneable, java.io.Serializable {...} 相比其他普通类，这个类的类名称后面多了个\u0026lt;E\u0026gt;，这就是泛型类的核心标识，其中 E 为类型参数，理论上可以为任何字母，但有一些约定俗成的习惯\nT：代表一般的任何类 E：element 元素的意思 K：代表 key 的意思 V：代表 value 的意思，经常和 K 搭配作为键值对 关于类型参数怎么用，来看个例子。\npublic class Vehicle\u0026lt;T\u0026gt; { private T brand;//属性 public T getBrand() {//方法返回值 return brand; } public void setBrand(T newBrand) {//方法形参 this.brand = newBrand; } } 一共有三种使用方法，代码中已做标注。注意：静态属性和静态方法中不能使用泛型类声明的类型参数。否则编译报错：'org.example.myblog.generics.Vehicle.this' cannot be referenced from a static context\n原因如下：静态成员是属于类本身的，不属于任何实例。当类加载时，泛型参数 T 还没有被具体类型替换（因为此时还没有任何实例被创建），编译器无法确定 T 的具体类型，自然也就无法为静态成员使用 T 分配内存或验证类型。有一个小例外，静态方法自身可以是泛型方法，详情见下文（坑1挖）。\n如果把 T 视为一个真实存在的类型，其实它的使用方法与其他类型并无区别。类型参数也可以是多个，用,分隔。\n泛型接口 泛型接口和泛型类差不多，下面是List接口的定义。\npublic interface List\u0026lt;E\u0026gt; extends SequencedCollection\u0026lt;E\u0026gt; {...} 说几点与泛型类不同的地方\n因为接口中的属性的修饰符默认是public static final，所以属性不能使用类型参数声明。除静态方法外其他方法可以使用类型参数。\n在接口A继承接口B时，如果接口B是泛型接口，那么接口A可以选择确定父接口的类型参数（成为非泛型接口），也可以选择保留泛型参数并传递给父接口（自身仍是泛型接口，如List接口），示例如下\n// 父接口：泛型接口，T 是它的类型参数 interface IUsb\u0026lt;T\u0026gt; { void connect(T device); // 连接设备（设备类型为 T） } // 子接口：保留泛型参数 E，并传递给父接口 IUsb 的 T interface IA\u0026lt;E\u0026gt; extends IUsb\u0026lt;E\u0026gt; { // 此时父接口 IUsb 的 T 被“替换”为子接口的 E // 所以 IA 继承的方法实际是：void connect(E device) } 当一个类实现接口时，可以选择将自身泛型参数传递给接口，成为泛型实现类，也可以指定接口的泛型参数，成为一个普通的实现类。当接口有多个泛型参数时，实现类不允许部分指定（接口继承接口也不允许部分指定）。\n泛型类与泛型接口的类型推断：\n类型参数的确定时机：类型参数在创建实例（泛型类）或声明子类型（泛型接口）时确定。\n不指定类型参数时的类型推断：会退化为“原始类型”，将所有泛型参数视为Object（坑2挖），不会进行类型检查，不推荐这样使用。\n指定类型参数时的类型匹配：所有使用该类型参数的成员需遵循“参数化类型”的约束：\n传入的实参必须是 “指定类型” 或其子类型。\n返回值会被自动视为 “指定类型”（无需强制转换）。\nList\u0026lt;Number\u0026gt; list = new ArrayList\u0026lt;\u0026gt;(); list.add(123); //Integer类型 list.add(1.2);//Double类型 如果有多个类型参数，不允许部分指定（上文提到过）。\n泛型方法 在方法的返回值前加个类型参数就是泛型方法。\n下面这个方法是泛型方法吗？\npublic T getBrand() { return brand; } 当然不是，它仅使用了泛型类定义的类型参数，并没有在方法签名中声明\u0026lt;T\u0026gt;，不能简单的认为泛型类里的方法就是泛型方法。下面的代码则是泛型方法，泛型方法里也可以同时声明多个类型参数。\npublic \u0026lt;T\u0026gt; T test(T t){ return t; } class Test\u0026lt;T\u0026gt; { public T method(T t) { return t; } public \u0026lt;E\u0026gt; T genericsMethod(T t, E e) { return t; } } 看上面的例子，Test是一个泛型类，泛型参数是 T ；method是泛型类里的一个方法，用到了泛型类的泛型参数 T ；genericsMethod是泛型方法，泛型参数是 E，该方法同时用到了两个泛型参数。需要注意的是，这两个泛型参数相互独立，作用域不同，泛型类的泛型参数 T 的作用域是整个类，而泛型方法的泛型参数 E 仅限于这个方法。如果泛型方法里需要同时用到该方法的泛型参数和泛型类的泛型参数，推荐分开命名，提高可读性。（这个例子不太好，方法体里并未使用到泛型参数 E）\n前面提到过，在泛型类里，不允许静态方法使用泛型类声明的类型参数。但可以将静态方法声明为泛型方法。代码如下：（坑1填）\nclass Test\u0026lt;T\u0026gt; { // 泛型类定义的类型参数 T 不能在静态方法中使用 // 但可以将静态方法声明为泛型方法，方法中便可以使用其声明的类型参数了 public static \u0026lt;E\u0026gt; E show(E one) { return null; } } 泛型方法的类型推断：\n确定时机：调用该方法时确定类型参数。\n不指定类型参数时的类型推断：优先通过实参推断；结合返回值类型调整；多类型实参时取共同父类（最小上界）\n// 泛型方法：返回两个参数中较大的一个（假设T是可比较的） public static \u0026lt;T extends Comparable\u0026lt;T\u0026gt;\u0026gt; T max(T a, T b) { return a.compareTo(b) \u0026gt;= 0 ? a : b; } // 泛型方法：创建包含一个元素的列表 public static \u0026lt;T\u0026gt; List\u0026lt;T\u0026gt; createList(T element) { List\u0026lt;T\u0026gt; list = new ArrayList\u0026lt;\u0026gt;(); list.add(element); return list; } // 泛型方法：打印两个参数的类型 public static \u0026lt;T\u0026gt; void printTypes(T a, T b) { System.out.println(a.getClass().getSimpleName()); System.out.println(b.getClass().getSimpleName()); } //********************************************************************** Integer result1 = max(3, 5); //根据实参推断 T = Integer String result2 = max(\u0026#34;apple\u0026#34;, \u0026#34;banana\u0026#34;); //根据实参推断 T = String // 接收变量是 List\u0026lt;Number\u0026gt;，实参是 Integer（Number 的子类） // 推断 T = Number（而非 Integer），因为返回值需要匹配 List\u0026lt;Number\u0026gt; //如果只有createList(123); 那么编译器仅根据实参推断 T = Integer List\u0026lt;Number\u0026gt; numList = createList(123); //实参为Integer和String,最小上界为Object，推断 T = Object printTypes(123, \u0026#34;123\u0026#34;); 指定类型参数时的类型推断：\n// 情况1：推断可能歧义时显式指定 List\u0026lt;Number\u0026gt; list = createList\u0026lt;\u0026gt;(123); // 等价于 createList\u0026lt;Number\u0026gt;(123) // 情况2：实参类型与预期不符时强制指定 List\u0026lt;Object\u0026gt; objList = createList\u0026lt;Object\u0026gt;(\u0026#34;hello\u0026#34;); // 显式指定 T = Object，即使实参是 String（String 是 Object 的子类，合法） 如果泛型方法没有参数或参数不涉及泛型参数，编译器无法通过实参推断，此时需要显式指定类型参数，或通过返回值接收类型推断。\n// 泛型方法：创建一个空列表 public static \u0026lt;T\u0026gt; List\u0026lt;T\u0026gt; createEmptyList() { return new ArrayList\u0026lt;\u0026gt;(); } // 方式1：通过接收变量类型推断 T = String List\u0026lt;String\u0026gt; strList = createEmptyList(); // 方式2：显式指定 T = Integer List\u0026lt;Integer\u0026gt; intList = createEmptyList\u0026lt;Integer\u0026gt;(); 类型擦除 在编译期，编译器会将泛型代码中的泛型参数\u0026lt;T\u0026gt;,\u0026lt;E\u0026gt;等等替换为边界类型（涉及到通配符的使用，下文有），如果没有边界统一替换为Object类型（坑2填）。在运行期，JVM眼中不存在泛型类或泛型方法。\n泛型类/接口的擦除\n//无边界泛型 class Box\u0026lt;T\u0026gt; { private T value; } // 擦除后 → class Box { private Object value; } //有边界泛型 class NumberBox\u0026lt;T extends Number\u0026gt; { private T value; } // 擦除后 → class NumberBox { private Number value; } 泛型方法的擦除\n//有边界和无边界的擦除规则同上 public static \u0026lt;T\u0026gt; T getFirst(List\u0026lt;T\u0026gt; list) { return list.get(0); } // 擦除后 → public static Object getFirst(List list) { return list.get(0); } // 调用时编译器自动补全转换： List\u0026lt;String\u0026gt; list = new ArrayList\u0026lt;\u0026gt;(); String s = (String) getFirst(list); // 手动写代码时无需加，编译器隐式添加 泛型类被继承时，类型擦除可能导致子类方法与父类方法签名不匹配，编译器会自动生成 “桥接方法” 保证多态性，看下面的例子\nclass Parent\u0026lt;T\u0026gt; { public void set(T t) {} } class Child extends Parent\u0026lt;String\u0026gt; { @Override public void set(String s) {} // 子类方法 } // 父类擦除后方法为：public void set(Object t) {},此时父类和子类的该方法形参不一致，不构成重写，但实际上有一个桥接方法 // 编译器为 Child 生成桥接方法： public void set(Object t) { set((String) t); } // 调用子类的 set(String) 泛型是 JDK5 引入的特性。类型擦除的设计是为了兼容 JDK5 之前的非泛型代码。但是类型擦除也带来了一定的影响，比如运行期无法获取泛型参数类型，泛型参数不能是基本类型等\n通配符使用 泛型是类型严格的，虽然String是Object的子类，但List\u0026lt;String\u0026gt;和List\u0026lt;Object\u0026gt;之间没有继承关系。通配符可以在保证类型安全的前提下，允许泛型类型之间的“有限兼容”。\n无界通配符\u0026lt;?\u0026gt;（表示任意类型） 特性：可读。适用于 “只需要读取泛型容器中的元素，不需要关心具体类型” 的场景\npublic static void printList(List\u0026lt;?\u0026gt; list) { for (Object obj : list) { // 只能以 Object 类型读取 System.out.println(obj); } // list.add(\u0026#34;abc\u0026#34;); // 编译报错：无法确定添加的类型是否匹配未知类型 list.add(null); // 唯一例外：null 可以添加，但无意义 } // 调用：可接收任何 List 类型 printList(new ArrayList\u0026lt;String\u0026gt;()); printList(new ArrayList\u0026lt;Integer\u0026gt;()); 上界通配符\u0026lt;? extends T\u0026gt;（表示 T 及其子类） 特性：可读（读取的元素可视为 T 类型，即向上转型）。适用于 “需要读取泛型容器中的元素，且元素类型是 T 的子类型” 的场景\n// 求和 public static double sum(List\u0026lt;? extends Number\u0026gt; numbers) { double total = 0; for (Number num : numbers) { // 读取为 Number 类型（安全） total += num.doubleValue(); } // numbers.add(10); // 编译报错：无法确定添加的 Integer 是否匹配未知子类型（如 Double），即只知道是子类，不确定添加哪个子类 return total; } // 调用：可接收 List\u0026lt;Integer\u0026gt;、List\u0026lt;Double\u0026gt; 等 sum(new ArrayList\u0026lt;Integer\u0026gt;(Arrays.asList(1, 2, 3))); // 6.0 sum(new ArrayList\u0026lt;Double\u0026gt;(Arrays.asList(1.5, 2.5))); // 4.0 下界通配符\u0026lt;? super T\u0026gt;（表示 T 及其父类） 特性：可写（写入 T 及其子类的元素是安全的），但读取受限（读取的元素只能视为 Object 类型）。适用于 “需要向泛型容器中写入元素，且元素类型是 T 的子类型” 的场景（如添加元素到容器）。\n// 向列表中添加整数（列表类型可以是 Integer 的父类：Number、Object 等） public static void addIntegers(List\u0026lt;? super Integer\u0026gt; list) { list.add(10); // 写入 Integer 及其子类（安全，因为父类容器可接收子类对象） list.add(20); // Integer num = list.get(0); // 编译报错：读取的元素只能视为 Object，因为不清楚是哪个父类，而Object是顶级父类，所以可以用Object接受 } // 调用：可接收 List\u0026lt;Integer\u0026gt;、List\u0026lt;Number\u0026gt;、List\u0026lt;Object\u0026gt; addIntegers(new ArrayList\u0026lt;Integer\u0026gt;()); addIntegers(new ArrayList\u0026lt;Number\u0026gt;()); addIntegers(new ArrayList\u0026lt;Object\u0026gt;()); PECS原则 Producer Extends：如果泛型容器是 “生产者”（主要用于读取元素），使用 \u0026lt;? extends T\u0026gt;（如 List\u0026lt;? extends Number\u0026gt; 生产 Number 类型元素）。 Consumer Super：如果泛型容器是 “消费者”（主要用于写入元素），使用 \u0026lt;? super T\u0026gt;（如 List\u0026lt;? super Integer\u0026gt; 消费 Integer 类型元素）。 总结 泛型在集合框架（List，Map，Set）和主流框架（Spring，Mybatis）中的频繁出现证明其重要性，所以学会使用泛型还是很重要的。\n这篇博客先写到这里，感谢豆包。欢迎评论区指正，我也会继续更新，欢迎收藏我的网站。我的眼皮要闭上了😑\n2026年3月7日修改：\n泛型练习请看本站博客：手写IoC容器。\n参考文章\nJava 中的泛型（两万字超全详解）_java 泛型-CSDN博客\n泛型编程 - Wikiwand\nJava 泛型 | 菜鸟教程\n","permalink":"http://localhost:1313/posts/java%E6%B3%9B%E5%9E%8B/","summary":"\u003ch2 id=\"泛型定义\"\u003e泛型定义\u003c/h2\u003e\n\u003cp\u003e先看这样一个例子：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-Java\" data-lang=\"Java\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003estatic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nf\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eString\u003c/span\u003e\u003cspan class=\"o\"\u003e[]\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eargs\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"n\"\u003eList\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003elist\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eArrayList\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"n\"\u003elist\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eadd\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;aaa\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"n\"\u003elist\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eadd\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;bbb\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"n\"\u003elist\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eadd\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;ccc\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003elist\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003esize\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"o\"\u003e++\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\t\u003c/span\u003e\u003cspan class=\"n\"\u003eSystem\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eout\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eprintln\u003c/span\u003e\u003cspan class=\"p\"\u003e((\u003c/span\u003e\u003cspan class=\"n\"\u003eString\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"n\"\u003elist\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003eArrayList\u003c/code\u003e集合中可以加入任何类型的对象，我本意是用这个集合来存储字符串（因为\u003ccode\u003eArrayList\u003c/code\u003e默认存储的是\u003ccode\u003eObject\u003c/code\u003e类型的对象，所以输出时需要强转 ），但我粗心的写错了\u003ccode\u003elist.add(123)\u003c/code\u003e，在我运行后发现报错\u003ccode\u003ejava.lang.ClassCastException\u003c/code\u003e，显然报错原因就是输出时\u003ccode\u003eInteger\u003c/code\u003e类型并不能强转为\u003ccode\u003eString\u003c/code\u003e类型。那么如何避免这种情况呢，答案就是\u003cstrong\u003e泛型\u003c/strong\u003e。\u003c/p\u003e\n\u003cp\u003e借用一下维基百科的定义：\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e泛型程序设计是程序设计语言的一种风格或范型，泛型允许程序员在强类型程序设计语言中 编写代码时 使用一些 以后才指定的类型，在实例化时作为参数指明这些类型。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e说一下我的理解，泛字让我联想到了一个词语：泛泛而谈，指那些浮浅平淡，不深入的谈话。这里也可以这样理解，定义时我模糊的说明一下参数，不指明参数具体是哪种类型，等我使用时再说明。\u003c/p\u003e\n\u003cp\u003e有这样一种说法：泛型的本质就是“参数化类型”。想象一下，定义一个方法需要形参，待你调用时，又需要传递实参。泛型亦是如此，定义时形式上意思意思，真正使用时再说明。其实都一个意思。\u003c/p\u003e\n\u003cp\u003e下面我将从\u003cstrong\u003e泛型类\u003c/strong\u003e、\u003cstrong\u003e泛型方法\u003c/strong\u003e、\u003cstrong\u003e泛型接口\u003c/strong\u003e、\u003cstrong\u003e通配符\u003c/strong\u003e、\u003cstrong\u003e类型擦除\u003c/strong\u003e五个方面来对Java泛型进行详细说明。\u003c/p\u003e\n\u003ch2 id=\"泛型类\"\u003e泛型类\u003c/h2\u003e\n\u003cp\u003e来看一下\u003ccode\u003eArrayList\u003c/code\u003e这个类的定义\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-Java\" data-lang=\"Java\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eArrayList\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eE\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003eextends\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eAbstractList\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eE\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"kd\"\u003eimplements\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eList\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eE\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eRandomAccess\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eCloneable\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ejava\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eio\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eSerializable\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{...}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e相比其他普通类，这个类的类名称后面多了个\u003ccode\u003e\u0026lt;E\u0026gt;\u003c/code\u003e，这就是泛型类的核心标识，其中 E 为类型参数，理论上可以为任何字母，但有一些约定俗成的习惯\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eT：代表一般的任何类\u003c/li\u003e\n\u003cli\u003eE：element 元素的意思\u003c/li\u003e\n\u003cli\u003eK：代表 key 的意思\u003c/li\u003e\n\u003cli\u003eV：代表 value 的意思，经常和 K 搭配作为键值对\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e关于类型参数怎么用，来看个例子。\u003c/p\u003e","title":"Java泛型"},{"content":"介绍 正则表达式（regular expression），常简写为regex，用简单字符串来描述、匹配文中全部匹配指定格式的字符串。人话讲就是根据一些规则制定一个字符串，然后你可以用这个字符串来筛选满足规则的字符串。许多程序设计语言都支持用正则表达式操作字符串，这里主要介绍正则表达式在Java中的运用。\n不同编程语言的正则表达式引擎有所不同，这里提供一个链接，里面详细介绍了不同语言对各种特性的支持程度。\n快速使用 先说明一下\\的使用。\n在Java普通字符串中，反斜杠\\本身就是转义字符，比如\\n被转义为\u0026quot;换行符\u0026quot;，又比如\\\\被转义为\\。而正则表达式也有自己的语法，它也使用反斜杠作为转义字符，比如\\d表示“匹配一个数字”。\n那么二者结合起来呢🧐。以\u0026quot;\\\\d\u0026quot;为例。编译器看到字符串\u0026quot;\\\\d\u0026quot;会根据字符串规则将其转换为两个字符，一个\\，一个d。接下来正则表达式引擎会对其进行解析，最终生效的正则模式就是\\d。可以这样理解：正则表达式需要 \\d 来匹配数字。但在Java字符串里，一个 \\ 需要写成 \\\\。所以，要把正则的 \\d 放到Java字符串里，就变成了 \\\\d。\n到底需不需要两个\\\\，idea会给你答案。\njava.util.regex包是Java标准库中用于支持正则表达式操作的包，主要涉及到Pattern和Matcher这两个类的操作。这里有个简单的例子：\nString pattern = \u0026#34;java\\\\d\u0026#34;; String text1 = \u0026#34;java1\u0026#34;; String text2 = \u0026#34;javaBad\u0026#34;; Pattern p = Pattern.compile(pattern); Matcher m = p.matcher(text1); System.out.println(m.matches());//true m = p.matcher(text2); System.out.println(m.matches());//false 先调用Pattern类的静态方法compile（参数为正则表达式）生成一个实例对象，通过调用该对象的matcher方法（参数为待匹配文本）生成一个Matcher实例。接下来就有很多方法供你选择，这里我调用的是matches方法来输出布尔值，在例子中体现为字符串Java后面能否匹配上数字。Matcher类里还有个find方法也很常见，下文会提到。\n匹配规则详解 简单匹配 为方便演示，接下来的示例代码使用String类的matches方法，该方法底层原理仍然是Pattern和Matcher这两个类的使用，后面有详细说明。下面示例参考廖雪峰和菜鸟教程。\n匹配任意字符：.可以匹配除\\r\\n之外的任何单个字符。如a.c可以匹配abc但不能匹配abbc和ac\n匹配数字：\\d匹配 0~9 的数字，同样只匹配一个字符。匹配非数字：\\D匹配非数字。\n匹配常用字符：\\w可以匹配一个字母、数字或下划线\n匹配空格字符：\\s可以匹配任何空白字符，包括空格、制表符、换页符等。与[\\f\\n\\r\\t\\v]等效。\\W和\\S和\\D同样是反着来的。\n重复匹配：\n*可以匹配任意个字符，包括0个字符。\n+可以匹配至少一个字符。比如A\\d+可以匹配A11111和A0。但不能匹配A,因为至少一个字符。\n?可以匹配0个或一个字符。\n如果想精确指定n个字符，使用{n}，比如A\\d{3}可以匹配到A123。指定匹配n~m个字符，用{n,m}， 例如A\\d{3,5}可以精确匹配A123 A1234 A12345。{n,}表示可以匹配至少n个字符。m和n为非负整数，其中n \u0026lt;= m。再举一个例子:o{2}和Bob中的一个o不匹配，而匹配food中的两个o。不同表达式可能是等效的，比如o{0,1}和o?\n来个综合点的例子：假如电话号码规则如下:34位数字表示区位，78位数字表示电话，中间用-连接。答案：\\\\d{3,4}-\\\\d{7,8}。对于连字符-，一般情况下只是一个普通字符，不需要进行转义，当然写上两个反斜杠也是对的，idea会给出提示移除多余的反斜杠。\nString pattern = \u0026#34;\\\\d{3,4}-\\\\d{7,8}\u0026#34;;//不知道需不需要写\\？idea会给你的答案 String text1 = \u0026#34;0123-123456\u0026#34;; String text2 = \u0026#34;010-1234567\u0026#34;; System.out.println(text1.matches(pattern));//false System.out.println(text2.matches(pattern));//true 复杂匹配： 匹配开头和结尾：^匹配输入字符串开始的位置，$匹配输入字符串结束的位置。他们俩的作用是将匹配过程限制在整个字符串上，避免了在子串中成功匹配的情况。其实matches()方法的行为已经隐含了^...$锚点的效果，而find()方法则没有。matches方法尝试将整个输入序列与模式匹配，而find方法会在输入序列中查找下一个与模式匹配的子序列。仔细品味这两个方法的名字，你也许会理解。\n匹配指定范围：[xyz]匹配包含的任一字符，比如[abc]匹配plain中的 a。[a-z]\n匹配 a 到 z 范围内的任何小写字母。[^a-z]匹配任何不在 a 到 z 范围内的字符，取补集的意思，和前面的\\b，\\B类似。[0-9]和[A-Z]同样理解。\n如果要匹配6位十六进制数，可以这样写：[0-9a-fA-F]{6}\n或规则匹配：AB|CD表示可以匹配 AB 或 CD。(z|f)ood匹配 zood 或 food，当然这个正则表达式也可以写成[zf]ood。\n分组匹配： 字面意思，通过()将表达式分组处理，可以配合Matcher类的group方法使用。\nPattern p = Pattern.compile(\u0026#34;(\\\\d{3,4})\\\\-(\\\\d{7,8})\u0026#34;); Matcher m = p.matcher(\u0026#34;010-12345678\u0026#34;); if (m.matches()) { String g1 = m.group(1); String g2 = m.group(2); System.out.println(g1); // 010 System.out.println(g2); // 12345678 } 非贪婪匹配：\nPattern pattern = Pattern.compile(\u0026#34;(\\\\d+)(0*)\u0026#34;); Matcher matcher = pattern.matcher(\u0026#34;1230000\u0026#34;); if (matcher.matches()) { System.out.println(\u0026#34;group1=\u0026#34; + matcher.group(1)); // \u0026#34;1230000\u0026#34; System.out.println(\u0026#34;group2=\u0026#34; + matcher.group(2)); // \u0026#34;\u0026#34; } 观察这个例子，第二个输出空字符串，输出没问题，因为\\d+可以匹配到后面的数字。正则表达式默认使用贪婪匹配，以\\d+为例，后面有多少数字就匹配多少（只要连续），这样0*就匹配到空字符串。如果想让\\d+少匹配，可以写成(\\\\d+?)(0*)，?表示非贪婪匹配，这样输出就变成了\u0026quot;123\u0026quot; \u0026quot;0000\u0026quot;。\n不能简单的把非贪婪匹配认为最少匹配，觉得输出应该是1和230000。非贪婪匹配是在保证后面表达式都能匹配上的前提下尽量少匹配。引擎保证的是整体成功优先，我认为可以是一种平衡吧，这里不做过多解释，因为更深层的原理我也不懂。\n这里的?和前面提到的?不一样。(\\d??)(9*)，\\d?表示匹配0个或1个数字，后面的?表示非贪婪匹配。如果给定字符串\u0026quot;9999\u0026quot;，匹配到的两个子串分别为\u0026quot;\u0026quot; \u0026quot;9999\u0026quot;\n实战演练 **匹配邮箱：**这里假设字符@前可以出现数字、英文字母、下划线和中划线，字符@后是域名格式，长度不限。\n先分析邮箱名称部分，只能出现数字、英文字母和下划线、中划线，那么可以这样写[0-9a-zA-Z_-]，也可以选择\\w，这里我选择第二种。又因为不止一个字符，所以加个+。变成\\w+\n然后分析域名部分，域名一般是weixin.qq.com这种类型，也就是**.**.**。可以以第一个英文句点为分界线将其拆解为两部分。一部分是**一部分是.**的复制粘贴。第一部分依然可以这样写\\w+，一个.**这样写\\.\\w+，多个.**这样写(.\\w+)+。\n经过分析，答案就是\\w+@\\w+(.\\w+)+，放到java中，需要写成\\\\w+@\\\\w+(.\\\\w+)+，还是那句话：到底需不需要写反斜杠，idea会给你答案。\n如果你在浏览器搜索“正则表达式邮箱匹配”，你可能会得到很多答案，当你不确定时，多去尝试。\n拓展 Matcher类除了提到的matches方法和find方法，还有start``replaceAll等，可以点击链接了解更多。这里我想介绍一下Pattern和Matcher这两个类出现的其他地方，比如上文提到的String.matches()方法。\n为什么说String.matches()方法底层原理仍然是Pattern和Matcher这两个类的使用。ctrl+鼠标左键点开方法源码即可发现：\npublic boolean matches(String regex) { return Pattern.matches(regex, this); } 发现调用的是Pattern类的静态方法matches，同时传进去两个参数，一个正则表达式，一个待匹配字符串。接着点进去matches方法\npublic static boolean matches(String regex, CharSequence input) { Pattern p = Pattern.compile(regex); Matcher m = p.matcher(input); return m.matches(); } 然后就会发现这跟之前的示例代码没啥两样，只是compile和matcher这两个方法的参数不再是字符串常量，而是传进来的参数regex和input。\n再说一个例子，就是String.split()方法。简单说一下：这个方法最终调用的是String类的private String[] split(String regex, int limit, boolean withDelimiters) {...}这个方法，可以看到传进去一个正则表达式作为参数，这个方法的最后几行代码是这样的\nPattern pattern = Pattern.compile(regex); return withDelimiters ? pattern.splitWithDelimiters(this, limit) : pattern.split(this, limit); 根据参数withDelimiters的布尔值来确定调用对象pattern的哪个方法，其实这两个方法最终调用的还是一个方法，兜兜转转还是回到了Pattern类和Matcher类。\n这篇博客就到这里，错误不可避免，欢迎指正。我会持续更新，欢迎收藏我的网站。\n参考文章：\n复杂匹配规则 - Java教程 - 廖雪峰的官方网站 Java 正则表达式 | 菜鸟教程 Java 正则表达式的用法和实例-CSDN博客 ","permalink":"http://localhost:1313/posts/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/","summary":"\u003ch2 id=\"介绍\"\u003e介绍\u003c/h2\u003e\n\u003cp\u003e正则表达式（regular expression），常简写为regex，用简单字符串来描述、匹配文中全部匹配指定格式的字符串。人话讲就是根据一些规则制定一个字符串，然后你可以用这个字符串来筛选满足规则的字符串。许多程序设计语言都支持用正则表达式操作字符串，这里主要介绍正则表达式在Java中的运用。\u003c/p\u003e\n\u003cp\u003e不同编程语言的正则表达式引擎有所不同，这里提供一个\u003ca href=\"https://deerchao.cn/tutorials/regex/diffs.html\"\u003e链接\u003c/a\u003e，里面详细介绍了不同语言对各种特性的支持程度。\u003c/p\u003e\n\u003ch2 id=\"快速使用\"\u003e快速使用\u003c/h2\u003e\n\u003cp\u003e先说明一下\u003ccode\u003e\\\u003c/code\u003e的使用。\u003c/p\u003e\n\u003cp\u003e在Java普通字符串中，反斜杠\u003ccode\u003e\\\u003c/code\u003e本身就是转义字符，比如\u003ccode\u003e\\n\u003c/code\u003e被转义为\u0026quot;换行符\u0026quot;，又比如\u003ccode\u003e\\\\\u003c/code\u003e被转义为\u003ccode\u003e\\\u003c/code\u003e。而正则表达式也有自己的语法，它也使用反斜杠作为转义字符，比如\u003ccode\u003e\\d\u003c/code\u003e表示“匹配一个数字”。\u003c/p\u003e\n\u003cp\u003e那么二者结合起来呢🧐。以\u003ccode\u003e\u0026quot;\\\\d\u0026quot;\u003c/code\u003e为例。编译器看到字符串\u003ccode\u003e\u0026quot;\\\\d\u0026quot;\u003c/code\u003e会根据字符串规则将其转换为两个字符，一个\u003ccode\u003e\\\u003c/code\u003e，一个\u003ccode\u003ed\u003c/code\u003e。接下来正则表达式引擎会对其进行解析，最终生效的正则模式就是\u003ccode\u003e\\d\u003c/code\u003e。可以这样理解：\u003cstrong\u003e正则表达式需要 \u003ccode\u003e\\d\u003c/code\u003e 来匹配数字。但在Java字符串里，一个 \u003ccode\u003e\\\u003c/code\u003e 需要写成 \u003ccode\u003e\\\\\u003c/code\u003e。所以，要把正则的 \u003ccode\u003e\\d\u003c/code\u003e 放到Java字符串里，就变成了 \u003ccode\u003e\\\\d\u003c/code\u003e。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e到底需不需要两个\u003ccode\u003e\\\\\u003c/code\u003e，idea会给你答案。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003ejava.util.regex\u003c/code\u003e包是Java标准库中用于支持正则表达式操作的包，主要涉及到\u003ccode\u003ePattern\u003c/code\u003e和\u003ccode\u003eMatcher\u003c/code\u003e这两个类的操作。这里有个简单的例子：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-Java\" data-lang=\"Java\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eString\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003epattern\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;java\\\\d\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eString\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003etext1\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;java1\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eString\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003etext2\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;javaBad\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ePattern\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ePattern\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003ecompile\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epattern\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eMatcher\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003em\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003ematcher\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etext1\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eSystem\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eout\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eprintln\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003ematches\u003c/span\u003e\u003cspan class=\"p\"\u003e());\u003c/span\u003e\u003cspan class=\"c1\"\u003e//true\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003em\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003ematcher\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etext2\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eSystem\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eout\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eprintln\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003ematches\u003c/span\u003e\u003cspan class=\"p\"\u003e());\u003c/span\u003e\u003cspan class=\"c1\"\u003e//false\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e先调用\u003ccode\u003ePattern\u003c/code\u003e类的静态方法\u003ccode\u003ecompile\u003c/code\u003e（参数为正则表达式）生成一个实例对象，通过调用该对象的\u003ccode\u003ematcher\u003c/code\u003e方法（参数为待匹配文本）生成一个\u003ccode\u003eMatcher\u003c/code\u003e实例。接下来就有很多方法供你选择，这里我调用的是\u003ccode\u003ematches\u003c/code\u003e方法来输出布尔值，在例子中体现为字符串Java后面能否匹配上数字。\u003ccode\u003eMatcher\u003c/code\u003e类里还有个\u003ccode\u003efind\u003c/code\u003e方法也很常见，下文会提到。\u003c/p\u003e\n\u003ch2 id=\"匹配规则详解\"\u003e匹配规则详解\u003c/h2\u003e\n\u003ch3 id=\"简单匹配\"\u003e简单匹配\u003c/h3\u003e\n\u003cp\u003e为方便演示，接下来的示例代码使用\u003ccode\u003eString\u003c/code\u003e类的\u003ccode\u003ematches\u003c/code\u003e方法，该方法底层原理仍然是\u003ccode\u003ePattern\u003c/code\u003e和\u003ccode\u003eMatcher\u003c/code\u003e这两个类的使用，后面有详细说明。下面示例参考\u003ca href=\"https://liaoxuefeng.com/books/java/reg-exp/match-rules/index.html\"\u003e廖雪峰\u003c/a\u003e和\u003ca href=\"https://www.runoob.com/java/java-regular-expressions.html\"\u003e菜鸟教程\u003c/a\u003e。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e匹配任意字符：\u003c/strong\u003e\u003ccode\u003e.\u003c/code\u003e可以匹配除\u003ccode\u003e\\r\\n\u003c/code\u003e之外的任何单个字符。如\u003ccode\u003ea.c\u003c/code\u003e可以匹配\u003ccode\u003eabc\u003c/code\u003e但不能匹配\u003ccode\u003eabbc\u003c/code\u003e和\u003ccode\u003eac\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e匹配数字：\u003c/strong\u003e\u003ccode\u003e\\d\u003c/code\u003e匹配 0~9 的数字，同样只匹配一个字符。\u003cstrong\u003e匹配非数字：\u003c/strong\u003e\u003ccode\u003e\\D\u003c/code\u003e匹配非数字。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e匹配常用字符：\u003c/strong\u003e\u003ccode\u003e\\w\u003c/code\u003e可以匹配一个字母、数字或下划线\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e匹配空格字符：\u003c/strong\u003e\u003ccode\u003e\\s\u003c/code\u003e可以匹配任何空白字符，包括空格、制表符、换页符等。与\u003ccode\u003e[\\f\\n\\r\\t\\v]\u003c/code\u003e等效。\u003ccode\u003e\\W\u003c/code\u003e和\u003ccode\u003e\\S\u003c/code\u003e和\u003ccode\u003e\\D\u003c/code\u003e同样是反着来的。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e重复匹配：\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e*\u003c/code\u003e可以匹配任意个字符，包括0个字符。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e+\u003c/code\u003e可以匹配至少一个字符。比如\u003ccode\u003eA\\d+\u003c/code\u003e可以匹配\u003ccode\u003eA11111\u003c/code\u003e和\u003ccode\u003eA0\u003c/code\u003e。但不能匹配\u003ccode\u003eA\u003c/code\u003e,因为至少一个字符。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e?\u003c/code\u003e可以匹配0个或一个字符。\u003c/p\u003e\n\u003cp\u003e如果想精确指定n个字符，使用\u003ccode\u003e{n}\u003c/code\u003e，比如\u003ccode\u003eA\\d{3}\u003c/code\u003e可以匹配到\u003ccode\u003eA123\u003c/code\u003e。指定匹配n~m个字符，用\u003ccode\u003e{n,m}\u003c/code\u003e， 例如\u003ccode\u003eA\\d{3,5}\u003c/code\u003e可以精确匹配\u003ccode\u003eA123\u003c/code\u003e \u003ccode\u003eA1234\u003c/code\u003e \u003ccode\u003eA12345\u003c/code\u003e。\u003ccode\u003e{n,}\u003c/code\u003e表示可以匹配至少n个字符。m和n为非负整数，其中n \u0026lt;= m。再举一个例子:\u003ccode\u003eo{2}\u003c/code\u003e和Bob中的一个o不匹配，而匹配food中的两个o。\u003cstrong\u003e不同表达式可能是等效的，比如\u003ccode\u003eo{0,1}\u003c/code\u003e和\u003ccode\u003eo?\u003c/code\u003e\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e来个综合点的例子：假如电话号码规则如下:3\u003cdel\u003e4位数字表示区位，7\u003c/del\u003e8位数字表示电话，中间用\u003ccode\u003e-\u003c/code\u003e连接。答案：\u003ccode\u003e\\\\d{3,4}-\\\\d{7,8}\u003c/code\u003e。对于连字符\u003ccode\u003e-\u003c/code\u003e，一般情况下只是一个普通字符，不需要进行转义，当然写上两个反斜杠也是对的，idea会给出提示移除多余的反斜杠。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-Java\" data-lang=\"Java\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eString\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003epattern\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\\\\d{3,4}-\\\\d{7,8}\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"c1\"\u003e//不知道需不需要写\\？idea会给你的答案\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eString\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003etext1\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;0123-123456\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eString\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003etext2\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;010-1234567\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eSystem\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eout\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eprintln\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etext1\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003ematches\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epattern\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\u003cspan class=\"c1\"\u003e//false\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eSystem\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eout\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eprintln\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etext2\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003ematches\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epattern\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\u003cspan class=\"c1\"\u003e//true\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"复杂匹配\"\u003e复杂匹配：\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003e匹配开头和结尾：\u003c/strong\u003e\u003ccode\u003e^\u003c/code\u003e匹配输入字符串开始的位置，\u003ccode\u003e$\u003c/code\u003e匹配输入字符串结束的位置。他们俩的作用是将匹配过程限制在整个字符串上，避免了在子串中成功匹配的情况。其实\u003ccode\u003ematches()\u003c/code\u003e方法的行为已经隐含了\u003ccode\u003e^...$\u003c/code\u003e锚点的效果，而\u003ccode\u003efind()\u003c/code\u003e方法则没有。matches方法尝试将整个输入序列与模式匹配，而find方法会在输入序列中查找下一个与模式匹配的子序列。仔细品味这两个方法的名字，你也许会理解。\u003c/p\u003e","title":"正则表达式"},{"content":"什么是反射 在spring项目中，只需要写个@Service或者@Component，然后在别的地方用@Autowired声明一个接口变量，Spring就能返回给我们一个实现了该接口的具体对象。这是如何实现的呢？它不可能在编译时就知道加了注解的类与类之间的关系，所以只能是在程序启动运行时，Spring动态地发现了这些类，读取了他们的结构，然后创建对象。这背后的技术支撑又是什么？答案就是反射。\n反射是Java提供的一种在程序运行时\n检查/获取类、接口、字段、方法、构造器等结构信息的能力。 操作/调用对象、字段、方法的能力。 它就像一面镜子，让程序在运行时“照见”自己的结构。\n反射的基石：Class对象 编译器在编译 Java 源代码时会生成 .class 文件（字节码文件）。当 JVM 需要用到某个类时，它的类加载器会读取并解析对应的 .class 文件，在方法区（或元空间）构建该类的运行时数据结构，同时在堆内存中创建一个代表该类的 java.lang.Class 对象。每个被加载的类在 JVM 中都有且只有一个对应的 Class 对象（在同一个类加载器命名空间内）。\n这里的Class是一个类的名字，不要和class关键字搞混。\n有三种方法获取Class对象\nPerson person = new Person(\u0026#34;me\u0026#34;, 20); //法一：通过对象实例 Class\u0026lt; ? extends Person\u0026gt; clazz1 = person.getClass(); //法二：自带属性（基本数据类型也有） Class\u0026lt;Person\u0026gt; clazz2 = Person.class; //法三：Class类的静态方法 forName try { Class\u0026lt;?\u0026gt; clazz3 = Class.forName(\u0026#34;org.myblog.reflection.Person\u0026#34;); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } 解释一下这三种方法泛型的使用：法一使用Class\u0026lt;? extends Person\u0026gt; 因为Class对象是在运行时从Person实例获取的，而Person实例的具体类型只能在运行时创建和确定，编译阶段无法判断，所以使用通配符 ，又因为person可能是Person实例，也可能是Person的子类实例，所以最终写成Class\u0026lt; ? extends Person\u0026gt;；法二使用Class\u0026lt;Person\u0026gt;因为编译时已知具体类型；法三使用Class\u0026lt;?\u0026gt;因为通过字符串动态加载类，编译时无法确定具体类型，所以使用通配符。这三个 Class 对象都是同一个（上面也提到了，一个类唯一对应一个 Class 对象）。\n最常用、最灵活的是法三\n反射的核心操作 Person类具体代码如下：\npublic class Person { public String name; private int age; public static String species = \u0026#34;Human\u0026#34;; public Person() { this(\u0026#34;Unknown\u0026#34;, 0); } public Person(String name, int age) { this.name = name; this.age = age; } private Person(String name) { this.name = name; this.age = 18; } public void greet(String name) { System.out.println(\u0026#34;Hello \u0026#34; + name + \u0026#34;,I\u0026#39;m \u0026#34; + this.name); } private void celebrateBirthday() { age++; System.out.println(name + \u0026#34; is now \u0026#34; + age + \u0026#34; years old!\u0026#34;); } public static void describeSpecies() { System.out.println(\u0026#34;We are all \u0026#34; + species); } } 获取类的信息 Class\u0026lt;?\u0026gt; clazz = Class.forName(\u0026#34;org.example.myblog.reflection.Person\u0026#34;); System.out.println(\u0026#34;类名：\u0026#34; + clazz.getName()); System.out.println(\u0026#34;包名：\u0026#34; + clazz.getPackage().getName()); System.out.println(\u0026#34;父类：\u0026#34; + clazz.getSuperclass()); System.out.println(\u0026#34;接口：\u0026#34; + Arrays.toString(clazz.getInterfaces())); System.out.println(\u0026#34;修饰符：\u0026#34; + Modifier.toString(clazz.getModifiers())); 类名：org.example.myblog.reflection.Person 包名：org.example.myblog.reflection 父类：class java.lang.Object 接口：[] 修饰符：public 操作字段 System.out.println(\u0026#34;操作字段+++++++++++++++++++++++++++++++++\u0026#34;); Field[] allFields = clazz.getDeclaredFields(); for (Field f : allFields) { System.out.println(\u0026#34;- \u0026#34; + Modifier.toString(f.getModifiers()) + \u0026#34; \u0026#34; + f.getType().getSimpleName() + \u0026#34; \u0026#34; + f.getName()); } Person me = new Person(\u0026#34;me\u0026#34;, 20); //访问public字段 Field nameField = clazz.getField(\u0026#34;name\u0026#34;); System.out.println(nameField.get(me)); //访问private字段 Field ageField = clazz.getDeclaredField(\u0026#34;age\u0026#34;); ageField.setAccessible(true); System.out.println(ageField.get(me)); ageField.set(me, 18); System.out.println(\u0026#34;修改后年龄: \u0026#34; + ageField.get(me)); //访问static字段 Field speciesField = clazz.getField(\u0026#34;species\u0026#34;); System.out.println(speciesField.get(null)); - public String name - private int age - public static String species me 20 修改后年龄: 18 Human 说明几点：\ngetDeclaredFields()是获取所有字段，并返回一个数组，getField则是根据参数返回指定字段，返回的是Field实例。\n最后一行speciesField.get(null)传入参数null，因为静态字段是属于这个类的，当然也可以传入对象me。\nageField.setAccessible(true);这个方法传入参数true表示屏蔽Java语言的访问检查。看下面这个例子\nField[] allFields = clazz.getDeclaredFields(); for (Field f : allFields) { Object target = Modifier.isStatic(f.getModifiers()) ? null : me; System.out.println(\u0026#34; 可访问性: \u0026#34; + f.canAccess(target)); } public String name 可访问性: true private int age 可访问性: false public static String species 可访问性: true ​\t输出如上，age是不可访问的，在setAccessible(true)后可以访问并修改。否则会报错。\n如果想要访问private或其他非public字段，必须使用getDeclaredField()，注意里面有Declared，这个规律同样适用后面的Method和Constructor。 操作方法 System.out.println(\u0026#34;\\n==== 方法操作 ====\u0026#34;); Person Dong = new Person(\u0026#34;dong\u0026#34;, 18); Method[] allMethods = clazz.getDeclaredMethods(); System.out.println(\u0026#34;所有方法:\u0026#34;); for (Method m : allMethods) { System.out.println(\u0026#34;- \u0026#34; + m.getName() + \u0026#34;()\u0026#34;); } // 调用public方法 Method greetMethod = clazz.getMethod(\u0026#34;greet\u0026#34;, String.class); greetMethod.invoke(Dong, \u0026#34;李明\u0026#34;); // 调用private方法 Method birthdayMethod = clazz.getDeclaredMethod(\u0026#34;celebrateBirthday\u0026#34;); birthdayMethod.setAccessible(true); birthdayMethod.invoke(Dong); System.out.println(\u0026#34;新年龄: \u0026#34; + ageField.get(Dong)); // 验证年龄增加 // 调用static方法 Method speciesMethod = clazz.getMethod(\u0026#34;describeSpecies\u0026#34;); speciesMethod.invoke(null); 所有方法: - celebrateBirthday() - describeSpecies() - greet() Hello 李明,I\u0026#39;m dong dong is now 19 years old! 新年龄: 19 We are all Human 其实和操作字段有很多相似之处，这里说一下invoke方法\nMethod greetMethod = clazz.getMethod(\u0026#34;greet\u0026#34;, String.class); greetMethod.invoke(Dong, \u0026#34;李明\u0026#34;); getMethod方法有两个参数，一个是方法名称，一个是参数的class对象\ngreetMethod方法同样有两个参数，一个是对象实例（如果是静态方法则传入null），一个是传递的参数。这两个方法的参数个数不是固定的(其实是个数组)，这取决于目标方法的参数个数。\n操作构造器 // 获取所有构造器 Constructor\u0026lt;?\u0026gt;[] constructors = clazz.getDeclaredConstructors(); System.out.println(\u0026#34;所有构造器:\u0026#34;); for (Constructor\u0026lt;?\u0026gt; c : constructors) { System.out.println(\u0026#34;参数数量: \u0026#34; + c.getParameterCount()); } // 使用public无参构造器创建对象 Constructor\u0026lt;?\u0026gt; emptyConstructor = clazz.getConstructor(); Person unknown = (Person) emptyConstructor.newInstance(); System.out.println(\u0026#34;无参构造创建: \u0026#34; + unknown.name); // 使用public带参构造器创建对象 Constructor\u0026lt;?\u0026gt; paramConstructor = clazz.getConstructor(String.class, int.class); Person sarah = (Person) paramConstructor.newInstance(\u0026#34;Sarah\u0026#34;, 28); System.out.println(\u0026#34;带参构造创建: \u0026#34; + sarah.name + \u0026#34;, \u0026#34; + ageField.get(sarah)); // 使用private构造器创建对象 Constructor\u0026lt;?\u0026gt; privateConstructor = clazz.getDeclaredConstructor(String.class); privateConstructor.setAccessible(true); Person secret = (Person) privateConstructor.newInstance(\u0026#34;Secret\u0026#34;); System.out.println(\u0026#34;私有构造创建: \u0026#34; + secret.name + \u0026#34;, \u0026#34; + ageField.get(secret)); 所有构造器: 参数数量: 1 参数数量: 2 参数数量: 0 无参构造创建: Unknown 带参构造创建: Sarah, 28 私有构造创建: Secret, 18 经过前面叙述，这里的相关方法也是很好理解，getConstructor方法的参数取决于你拿到的构造器的参数（其实也是个数组）。\nnewInstance()方法可以根据你拿到的构造器来创建该构造器所在类的实例，参数同上理解。例子中是直接强转类型了，如果clazz是用法二得到的，也可以使用cast方法进行类型转换。一般来说这个方法创建的实例用Object来接收，因为创建的对象是在运行时动态生成的，编译阶段无法知道。建议使用反射直接调用和操作对象的方法和字段，而不是先进行类型转换，毕竟如果在编写阶段已经明确了要转换的类型，那么直接显示地调用更合适，而不必依赖于反射。反射的真正价值在于处理编译时未知的类型，从而编写更具有通用性的代码。\n示例里用Constructor\u0026lt;?\u0026gt;接收clazz.getConstructor()创建的实例是因为clazz的创建就使用的是通配符。如果在创建时指定类，那便可以指定泛型参数，相当于构造器就知道自己要构造的对象是什么类型了。\n结尾 上述内容只是反射部分的冰山一角，我已经尽我所能把这“一角”讲述清楚。\n反射很强，它能提供运行时动态操作类和对象的能力，是很多框架的基石，但同时它也带来了很多问题，性能开销大、代码可读性差等等。在日常开发中，程序员还是会优先选择直接调用、接口、设计模式等更清晰、高效的方式。\n有机会我也会更新一个与反射有关的实战案例，欢迎收藏我的网站。\n2026年3月7日修改：\n反射练习请看本站博客：手写IoC容器。\n参考文章：\n大白话说Java反射：入门、使用、原理 - 陈树义 - 博客园\nJava基础之—反射（非常重要）-CSDN博客\nJava中的反射 Reflection in Java_哔哩哔哩_bilibili\n","permalink":"http://localhost:1313/posts/java%E5%8F%8D%E5%B0%84/","summary":"\u003ch2 id=\"什么是反射\"\u003e什么是反射\u003c/h2\u003e\n\u003cp\u003e在\u003ccode\u003espring\u003c/code\u003e项目中，只需要写个\u003ccode\u003e@Service\u003c/code\u003e或者\u003ccode\u003e@Component\u003c/code\u003e，然后在别的地方用\u003ccode\u003e@Autowired\u003c/code\u003e声明一个接口变量，\u003ccode\u003eSpring\u003c/code\u003e就能返回给我们一个实现了该接口的具体对象。这是如何实现的呢？它不可能在编译时就知道加了注解的类与类之间的关系，所以只能是在程序启动\u003cstrong\u003e运行时\u003c/strong\u003e，Spring动态地发现了这些类，读取了他们的结构，然后创建对象。这背后的技术支撑又是什么？答案就是反射。\u003c/p\u003e\n\u003cp\u003e反射是Java提供的一种在\u003cstrong\u003e程序运行时\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e检查/获取类、接口、字段、方法、构造器等结构信息的能力。\u003c/li\u003e\n\u003cli\u003e操作/调用对象、字段、方法的能力。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e它就像一面镜子，让程序在运行时“照见”自己的结构。\u003c/p\u003e\n\u003ch2 id=\"反射的基石class对象\"\u003e反射的基石：\u003ccode\u003eClass\u003c/code\u003e对象\u003c/h2\u003e\n\u003cp\u003e编译器在编译 Java 源代码时会生成 \u003ccode\u003e.class\u003c/code\u003e 文件（字节码文件）。当 JVM 需要用到某个类时，它的类加载器会读取并解析对应的 \u003ccode\u003e.class\u003c/code\u003e 文件，在方法区（或元空间）构建该类的运行时数据结构，同时在堆内存中创建一个代表该类的 \u003cstrong\u003e\u003ccode\u003ejava.lang.Class\u003c/code\u003e 对象\u003c/strong\u003e。每个被加载的类在 JVM 中都有且只有一个对应的 \u003ccode\u003eClass\u003c/code\u003e 对象（在同一个类加载器命名空间内）。\u003c/p\u003e\n\u003cp\u003e这里的\u003ccode\u003eClass\u003c/code\u003e是一个类的名字，不要和\u003ccode\u003eclass\u003c/code\u003e关键字搞混。\u003c/p\u003e\n\u003cp\u003e有三种方法获取\u003ccode\u003eClass\u003c/code\u003e对象\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-Java\" data-lang=\"Java\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ePerson\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ePerson\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;me\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003e20\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e//法一：通过对象实例\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eClass\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e?\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003eextends\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ePerson\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eclazz1\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetClass\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e//法二：自带属性（基本数据类型也有）\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eClass\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003ePerson\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eclazz2\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ePerson\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eclass\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e//法三：Class类的静态方法 forName\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003etry\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"n\"\u003eClass\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;?\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eclazz3\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eClass\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eforName\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;org.myblog.reflection.Person\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003ecatch\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eClassNotFoundException\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\t\u003c/span\u003e\u003cspan class=\"k\"\u003ethrow\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eRuntimeException\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e解释一下这三种方法泛型的使用：法一使用\u003ccode\u003eClass\u0026lt;? extends Person\u0026gt; \u003c/code\u003e因为\u003ccode\u003eClass\u003c/code\u003e对象是在运行时从\u003ccode\u003ePerson\u003c/code\u003e实例获取的，而\u003ccode\u003ePerson\u003c/code\u003e实例的具体类型只能在运行时创建和确定，编译阶段无法判断，所以使用通配符 ，又因为\u003ccode\u003eperson\u003c/code\u003e可能是\u003ccode\u003ePerson\u003c/code\u003e实例，也可能是\u003ccode\u003ePerson\u003c/code\u003e的子类实例，所以最终写成\u003ccode\u003eClass\u0026lt; ? extends Person\u0026gt;\u003c/code\u003e；法二使用\u003ccode\u003eClass\u0026lt;Person\u0026gt;\u003c/code\u003e因为编译时已知具体类型；法三使用\u003ccode\u003eClass\u0026lt;?\u0026gt;\u003c/code\u003e因为通过字符串动态加载类，编译时无法确定具体类型，所以使用通配符。这三个 Class 对象都是同一个（上面也提到了，一个类唯一对应一个 Class 对象）。\u003c/p\u003e","title":"Java反射"},{"content":"此篇博客记录我学习过程中遇到的奇葩BUG\nSpringBoot项目启动失败 如上图，SpringBoot项目启动失败。\n虽然说是端口占用，但我执行相关命令后没有任何输出，修改启动端口不行，尝试了很多办法都未成功，那只好使出我的终极大招：重启idea。还是不行。幸好我还有终极终极大招：重启电脑。嘿，你猜怎么着，成了！🤣\n接口测试报错getaddrinfo ENOTFOUND https 一次简单的接口测试，出现报错getaddrinfo ENOTFOUND https，搜索后得到答案：这通常意味着应用程序无法解析主机名或域名。这可能是由于DNS配置问题、网络连接问题或拼写错误等原因导致的。前两个果断排除，开始以为参数错，但一想那不应该试着错误，后来想可能域名错，但也没找到错误。最后发现是https:后面多了个空格。😑\nmysql容器连接报错 情况描述：本机为windows系统，装有wsl。在配置一个项目时使用docker-compose命令构建项目所需容器。构建好容器后，我测试mysql是否连接成功，结果出现报错2013 - Lost connection to server at 'handshake: reading initial communication packet', system error: 0.\n于是到网上搜索解决方案，有注释掉bind-address = 127.0.0.1的，有添加配置skip-name-resolve的，还有换系统的，但都无法解决我的问题。直到看见这篇博客CSDN博客。于是我直接修改docker-compose.yaml文件，添加network_mode: host，同时从豆包处得知：使用 host 网络模式后，容器会直接使用宿主机的网络栈，因此 ports 配置会被忽略。如果需要 MySQL 监听 3307 端口，需在 MySQL 配置文件（/etc/my.cnf 目录下的配置）中添加 port=3307，照做后问题解决。\n还要记得修改application.yml中的端口设置。\n","permalink":"http://localhost:1313/posts/%E9%81%87%E5%88%B0%E7%9A%84%E5%A5%87%E8%91%A9bug/","summary":"\u003cp\u003e此篇博客记录我学习过程中遇到的奇葩BUG\u003c/p\u003e\n\u003ch2 id=\"springboot项目启动失败\"\u003e\u003ccode\u003eSpringBoot\u003c/code\u003e项目启动失败\u003c/h2\u003e\n\u003cp\u003e\u003cimg alt=\"image-20250801212248493\" loading=\"lazy\" src=\"https://dongimagehost-1356670526.cos.ap-nanjing.myqcloud.com/2025/07/image-20250801212248493.png\"\u003e\u003c/p\u003e\n\u003cp\u003e如上图，\u003ccode\u003eSpringBoot\u003c/code\u003e项目启动失败。\u003c/p\u003e\n\u003cp\u003e虽然说是端口占用，但我执行相关命令后没有任何输出，修改启动端口不行，尝试了很多办法都未成功，那只好使出我的终极大招：重启idea。还是不行。幸好我还有终极终极大招：重启电脑。嘿，你猜怎么着，成了！🤣\u003c/p\u003e\n\u003ch2 id=\"接口测试报错getaddrinfo-enotfound-https\"\u003e接口测试报错\u003ccode\u003egetaddrinfo ENOTFOUND https\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cimg alt=\"image-20250827154544934\" loading=\"lazy\" src=\"https://dongimagehost-1356670526.cos.ap-nanjing.myqcloud.com/2025/07/image-20250827154544934.png\"\u003e\u003c/p\u003e\n\u003cp\u003e一次简单的接口测试，出现报错\u003ccode\u003egetaddrinfo ENOTFOUND https\u003c/code\u003e，搜索后得到答案：这通常意味着应用程序无法解析主机名或域名。这可能是由于DNS配置问题、网络连接问题或拼写错误等原因导致的。前两个果断排除，开始以为参数错，但一想那不应该试着错误，后来想可能域名错，但也没找到错误。最后发现是\u003ccode\u003ehttps:\u003c/code\u003e后面多了个空格。😑\u003c/p\u003e\n\u003ch2 id=\"mysql容器连接报错\"\u003emysql容器连接报错\u003c/h2\u003e\n\u003cp\u003e情况描述：本机为windows系统，装有wsl。在配置一个项目时使用\u003ccode\u003edocker-compose\u003c/code\u003e命令构建项目所需容器。构建好容器后，我测试mysql是否连接成功，结果出现报错\u003ccode\u003e2013 - Lost connection to server at 'handshake: reading initial communication packet', system error: 0\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003e于是到网上搜索解决方案，有注释掉\u003ccode\u003ebind-address = 127.0.0.1\u003c/code\u003e的，有添加配置\u003ccode\u003eskip-name-resolve\u003c/code\u003e的，还有换系统的，但都无法解决我的问题。直到看见这篇博客\u003ca href=\"https://blog.csdn.net/liudongyang123/article/details/108381944\"\u003eCSDN博客\u003c/a\u003e。于是我直接修改\u003ccode\u003edocker-compose.yaml\u003c/code\u003e文件，添加\u003ccode\u003enetwork_mode: host\u003c/code\u003e，同时从豆包处得知：使用 \u003ccode\u003ehost\u003c/code\u003e 网络模式后，容器会直接使用宿主机的网络栈，因此 \u003ccode\u003eports\u003c/code\u003e 配置会被忽略。如果需要 MySQL 监听 3307 端口，需在 MySQL 配置文件（\u003ccode\u003e/etc/my.cnf\u003c/code\u003e 目录下的配置）中添加 \u003ccode\u003eport=3307\u003c/code\u003e，照做后问题解决。\u003c/p\u003e\n\u003cp\u003e还要记得修改\u003ccode\u003eapplication.yml\u003c/code\u003e中的端口设置。\u003c/p\u003e","title":"遇到的奇葩BUG"},{"content":"什么是序列化和反序列化 序列化，人话讲就是将对象转换为字节序列（也可以是JSON、XML等文本格式），反序列化就是把这个过程倒置。\n下面是维基百科关于序列化的介绍\n序列化（serialization）在计算机科学的资料处理中，是指将数据结构或对象状态转换成可取用格式（例如存成文件，存于缓冲，或经由网络中发送），以留待后续在相同或另一台计算机环境中，能恢复原先状态的过程。依照序列化格式重新获取字节的结果时，可以利用它来产生与原始对象相同语义的副本。对于许多对象，像是使用大量引用的复杂对象，这种序列化重建的过程并不容易。面向对象中的对象序列化，并不概括之前原始对象所关系的函数。这种过程也称为对象编组（marshalling）。从一系列字节提取数据结构的反向操作，是反序列化（也称为解编组、deserialization、unmarshalling）。\n对于Java这种面向对象的编程语言来说，是对实例化后的对象进行序列化，而对于C++这种半面向对象的编程语言来说，序列化的目标不仅有对象（class）还有数据结构（struct）\n序列化的使用场景 数据存储：比如序列化可以将存储在 JVM 堆区中的对象转换成字节序列，从而实现持久化。\n网络通信：将对象转换为字节序列方便其在网络中进行传递和接收。\n使用Java实现序列化 以 JDK 自带序列化方法为例，实现java.io.Serializable接口\n@Data public class Cat implements Serializable { private int age; private String name; private Date birth; } 序列化演示：\npublic class serializeTest { public static void main(String[] args) { Cat cat = new Cat(); cat.setName(\u0026#34;tom\u0026#34;); cat.setAge(2); cat.setBirth(new Date()); //使用ObjectOutputStream将cat对象序列化并存入test1.txt文件中 try (FileOutputStream fileOutputStream = new FileOutputStream(\u0026#34;test1.txt\u0026#34;); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)) { objectOutputStream.writeObject(cat); } catch (IOException e) { e.printStackTrace(); } } } 反序列化演示：\npublic class deserializeTest { public static void main(String[] args) { //使用ObjectInputStream对test.txt文件读取并反序列化 try (FileInputStream fileInputStream = new FileInputStream(\u0026#34;test1.txt\u0026#34;); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) { Cat cat = (Cat) objectInputStream.readObject(); System.out.println(cat); } catch (Exception e) { e.printStackTrace(); } } } 反序列化结果如下:\nCat(age=2, name=tom, birth=Fri Jul 04 21:37:13 CST 2035) 接下来让我们走进Serializable的源代码\npublic interface Serializable { } 你没看错，Serializable接口中没有任何方法和字段，它的作用仅仅是告诉JVM：实现该接口的类可以被序列化，\n其实除了实现Serializable接口外，还有一个选择就是实现Externalizable接口（该接口是Serializable的子接口），不过需要重写两个方法，示例如下：\n@Data public class Cat implements Externalizable { private int age; private String name; private Date birth; @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(name); out.writeObject(birth); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { name = (String) in.readObject(); birth = (Date) in.readObject(); } } 序列化和反序列化代码一样，最终结果如下：\nCat(age=0, name=tom, birth=Fri Jul 04 21:43:23 CST 2025) writeExternal和readExternal可以自定义哪些字段需要序列化，我这个例子中就没有对age进行操作，在反序列化后它被赋初值0。需要注意的是\nThe readExternal method must read the values in the same sequence and with the same types as were written by writeExternal.\n翻译过来意思就是这两个方法应该用相同的顺序和相同的类型对字段进行读写，相同的顺序容易理解，相同的类型意思就是在readExternal中强转的目标类型必须和writeExternal原类型保持一致。在实现Externalizable接口时，还有一点需要注意的是序列化对象中必须提供无参构造，以Cat类为例,当我加入一个有参构造覆盖默认无参构造后，序列化正常运行，反序列化出现报错\n关于序列化有几点需要注意：\n只有实现该接口的类才可以被序列化。\n可序列化类的所有子类是可以被序列化的。不可序列化类的子类型可以被序列化。\n如果不想对某个变量序列化，可以用transient关键字修饰，它只能修饰变量，不能修饰类和方法。transient 修饰的变量，在反序列化后变量值将会被置成类型的默认值。例如，如果是修饰 int 类型，那么反序列后结果就是 0。\n序列化运行时会为每个可序列化类分配一个版本号serialVersionUID该版本号在序列化机制中用于验证类的版本是否一致，如果反序列化时的UID和原来序列化的UID不同，则会抛出InvalidClassException。\n这个字段可以手动显示声明，也可以自动生成。如果显示声明，这个字段必须是static final且类型为long的。如果该字段未被显示声明，JVM会根据该类的结构自动生成一个serialVersionUID，枚举类的serialVersionUID会被定义为0L。\n官方强烈建议除了枚举类型以外的所有可序列化类显示声明serialVersionUID值，因为生成算法对类的细节非常敏感（如方法、字段等的变化都会导致serialVersionUID改变），从而导致兼容性问题。显式设置 serialVersionUID 可以确保在类发生非兼容更改时由开发者主动决定是否更新 UID，而不是因为类结构微小变化而导致反序列化失败。同时官方建议使用private来声明serialVersionUID，因为该字段不会被子类继承使用，因此没必要设置为public或protected。下面是一个完整的示例\n@Serial//Java14后建议添加 private static final long serialVersionUID = 1L; 这时小明举手说：老师老师，如果serialVersionUID被static修饰，那他就属于这个类，而不属于实例化后的对象了，那怎么将这个字段序列化呢？老师说：好问题，我也不知道。 序列化保存的是对象的状态，也就是实例变量的值，然而serialVersionUID是一个特例，它本身确实不作为对象状态被序列化，但它的值被序列化机制特殊处理了。屏幕前的你明白了吗，反正我还是有点糊涂，我觉得把它视为一个特例就好了，要真正弄懂怕是要搞明白JVM，那要很久以后了。\nJava反序列化漏洞 这里我推荐观看5i1encee的相关博客，关于反序列化漏洞我简要介绍一下。\n当攻击者通过构造恶意输入，让反序列化产生非预期的的对象，那么在反序列化这个过程中就可能执行恶意代码。已经有多个版本的库或框架被发现存在反序列化漏洞，如Apache Commons Collections 。借用一位大哥的例子来简单说明\npublic class VulnerabilityTest{ public static void main(String args[]) throws Exception{ MyObject myObj = new MyObject(); myObj.name = \u0026#34;hi\u0026#34;; FileOutputStream fos = new FileOutputStream(\u0026#34;object\u0026#34;); ObjectOutputStream os = new ObjectOutputStream(fos); os.writeObject(myObj); os.close(); FileInputStream fis = new FileInputStream(\u0026#34;object\u0026#34;); ObjectInputStream ois = new ObjectInputStream(fis); MyObject objectFromDisk = (MyObject)ois.readObject(); System.out.println(objectFromDisk.name); ois.close(); } } class MyObject implements Serializable { public String name; private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{ in.defaultReadObject(); Runtime.getRuntime().exec(\u0026#34;calc\u0026#34;); } } 在MyObject这个类中，自定义了readObject这个方法，相当于可以自己决定对哪些字段反序列化。在readObject这个方法里通过调用defaultReadObject来实现默认的反序列化机制。之后执行了名为calc的系统命令，这是Windows下启动计算器的命令。当我进行反序列化操作时，电脑上的计算器也随之启动。实际上，Java 反序列化漏洞产生的原因大多数是因为反序列化时没有进行校验，或者有些校验使用黑名单方式又被绕过，最终使得包含恶意代码的序列化对象在服务器端被反序列化执行。\n其他序列化协议 上述有关代码的举例均是 JDK 自带的序列化协议，但它在开发中很少被使用，主要原因如下：\n严重的安全风险：正如例子中提到的readObject方法，攻击者可构造恶意序列化数据触发任意代码执行 跨语言兼容性差：该协议序列化后的二进制数据只能被 Java 程序识别，无法与其他语言交互，多语言协作困难。 性能低下：序列化后的二进制数据体积远大于 JSON 或其他二进制协议。同时因为需要反射和递归处理对象图，导致速度较慢。 常用的序列化协议有Hessian、Kryo、 Protobuf、 ProtoStuff 等，这些都是基于二进制的序列化协议。SpringBoot 项目中也可以集成 Jackson、Fastjson 等将对象序列化为JSON格式的库。\n第一篇博客先写到这里，个人理解难免有偏差，欢迎评论区指正。我以后也会继续更新，欢迎收藏我的网站。\n参考文章：\nJava 序列化详解 | JavaGuide (6 封私信 / 70 条消息) Java反序列化安全漏洞怎么回事? - 知乎 ","permalink":"http://localhost:1313/posts/java%E5%BA%8F%E5%88%97%E5%8C%96%E4%B8%8E%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/","summary":"\u003ch2 id=\"什么是序列化和反序列化\"\u003e什么是序列化和反序列化\u003c/h2\u003e\n\u003cp\u003e序列化，人话讲就是将对象转换为字节序列（也可以是JSON、XML等文本格式），反序列化就是把这个过程倒置。\u003c/p\u003e\n\u003cp\u003e下面是维基百科关于序列化的介绍\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e序列化\u003c/strong\u003e（serialization）在\u003ca href=\"https://zh.wikipedia.org/wiki/%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8\"\u003e计算机科学\u003c/a\u003e的资料处理中，是指将\u003ca href=\"https://zh.wikipedia.org/wiki/%E8%B3%87%E6%96%99%E7%B5%90%E6%A7%8B\"\u003e数据结构\u003c/a\u003e或\u003ca href=\"https://zh.wikipedia.org/wiki/%E7%89%A9%E4%BB%B6_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%B8)\"\u003e对象\u003c/a\u003e状态转换成可取用格式（例如存成文件，存于缓冲，或经由网络中发送），以留待后续在相同或另一台计算机环境中，能恢复原先状态的过程。依照序列化格式重新获取\u003ca href=\"https://zh.wikipedia.org/wiki/%E4%BD%8D%E5%85%83%E7%BB%84\"\u003e字节\u003c/a\u003e的结果时，可以利用它来产生与原始对象相同语义的副本。对于许多对象，像是使用大量\u003ca href=\"https://zh.wikipedia.org/wiki/%E5%8F%83%E7%85%A7\"\u003e引用\u003c/a\u003e的复杂对象，这种序列化重建的过程并不容易。面向对象中的对象序列化，并不概括之前原始对象所关系的函数。这种过程也称为对象编组（marshalling）。从一系列字节提取数据结构的反向操作，是\u003cstrong\u003e反序列化\u003c/strong\u003e（也称为解编组、deserialization、unmarshalling）。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e对于Java这种面向对象的编程语言来说，是对实例化后的对象进行序列化，而对于C++这种半面向对象的编程语言来说，序列化的目标不仅有对象（class）还有数据结构（struct）\u003c/p\u003e\n\u003ch2 id=\"序列化的使用场景\"\u003e序列化的使用场景\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e数据存储：比如序列化可以将存储在 JVM 堆区中的对象转换成字节序列，从而实现持久化。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e网络通信：将对象转换为字节序列方便其在网络中进行传递和接收。\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"使用java实现序列化\"\u003e使用Java实现序列化\u003c/h2\u003e\n\u003cp\u003e以 JDK 自带序列化方法为例，实现\u003ccode\u003ejava.io.Serializable\u003c/code\u003e接口\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nd\"\u003e@Data\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eCat\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003eimplements\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eSerializable\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eage\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eString\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eDate\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ebirth\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e序列化演示：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-Java\" data-lang=\"Java\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eserializeTest\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003estatic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nf\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eString\u003c/span\u003e\u003cspan class=\"o\"\u003e[]\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eargs\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003eCat\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ecat\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eCat\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003ecat\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003esetName\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;tom\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003ecat\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003esetAge\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003ecat\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003esetBirth\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eDate\u003c/span\u003e\u003cspan class=\"p\"\u003e());\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"c1\"\u003e//使用ObjectOutputStream将cat对象序列化并存入test1.txt文件中\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003etry\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eFileOutputStream\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003efileOutputStream\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eFileOutputStream\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;test1.txt\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e             \u003c/span\u003e\u003cspan class=\"n\"\u003eObjectOutputStream\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eobjectOutputStream\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eObjectOutputStream\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003efileOutputStream\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"n\"\u003eobjectOutputStream\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003ewriteObject\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecat\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003ecatch\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eIOException\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eprintStackTrace\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e反序列化演示：\u003c/p\u003e","title":"Java序列化与反序列化"},{"content":"简介 这是 粗体 文本，这是 斜体 文本。\n访问 Hugo 网站！\n","permalink":"http://localhost:1313/posts/my-first-post/","summary":"\u003ch2 id=\"简介\"\u003e简介\u003c/h2\u003e\n\u003cp\u003e这是 \u003cstrong\u003e粗体\u003c/strong\u003e 文本，这是 \u003cem\u003e斜体\u003c/em\u003e 文本。\u003c/p\u003e\n\u003cp\u003e访问 \u003ca href=\"https://gohugo.io\"\u003eHugo\u003c/a\u003e 网站！\u003c/p\u003e","title":"我的第一篇文章"}]