-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
229 lines (121 loc) · 308 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>怒放吧德德</title>
<subtitle>一个有梦有戏的人</subtitle>
<link href="https://lydandtry.github.io/atom.xml" rel="self"/>
<link href="https://lydandtry.github.io/"/>
<updated>2024-09-19T03:07:44.552Z</updated>
<id>https://lydandtry.github.io/</id>
<author>
<name>Liyongde</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>启航·JVM概论</title>
<link href="https://lydandtry.github.io/posts/34839/"/>
<id>https://lydandtry.github.io/posts/34839/</id>
<published>2023-10-26T18:50:34.000Z</published>
<updated>2024-09-19T03:07:44.552Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>为什么要学习JVM?相信好多开发者开头也处于迷惑之中,对于Java虚拟机也是很头疼,大多开发者都会认为,学习JVM无非就是面试。然而,学习JVM并不是为了面试,更是为了深入去理解Java这门语言,笔者大学毕业入坑Java以来也有一年多了,说长也不长,但是在开发的过程中却也是遇到一些性能问题,最后也是由领导解决。个人认为,学习Java虚拟机更是一种基础知识,在开发中才能够深入理解一些细节。<br><a name="GRAh9"></a></p><h2 id="虚拟机和Java虚拟机"><a href="#虚拟机和Java虚拟机" class="headerlink" title="虚拟机和Java虚拟机"></a>虚拟机和Java虚拟机</h2><p><a name="F0KAA"></a></p><h3 id="虚拟机"><a href="#虚拟机" class="headerlink" title="虚拟机"></a>虚拟机</h3><p>虚拟机(Virtual Machine)是一种软件或硬件实体,它模拟了物理计算机的功能,允许在其上运行操作系统和应用程序。虚拟机通常用于隔离不同的计算环境,提供了一种独立于物理硬件的计算平台。<br><a name="iUk45"></a></p><h3 id="Java虚拟机"><a href="#Java虚拟机" class="headerlink" title="Java虚拟机"></a>Java虚拟机</h3><p>Java虚拟机(Java Virtual Machine,JVM)是一种软件虚拟机,特别为执行Java程序而设计。JVM是Java平台的核心组件之一,它负责将Java字节码翻译成可以在特定计算机上运行的本地机器代码。Java程序员编写的Java代码首先被编译成字节码,然后由JVM执行。这种方式使Java程序具有跨平台的特性,因为只需要在不同平台上实现JVM,就可以在各种操作系统上运行相同的Java程序。</p><blockquote><p>JVM的主要任务包括字节码加载、解释执行或即时编译、垃圾回收、内存管理等。它还提供了各种标准库和API,用于支持Java程序的运行和互动。不同的Java虚拟机实现可以适应不同的硬件和操作系统,但它们都遵循Java虚拟机规范(Java Virtual Machine Specification),以确保Java程序的跨平台性。</p></blockquote><p><a name="GmxlG"></a></p><h3 id="JVM的位置"><a href="#JVM的位置" class="headerlink" title="JVM的位置"></a>JVM的位置</h3><p>Java虚拟机试运行在操作系统之上的,和硬件是没有直接交互的。<br />在Java 生态系统中的三个关键组件:JDK(Java Development Kit)、JRE(Java Runtime Environment)和 JVM(Java Virtual Machine)<br />① JDK(Java Development Kit)</p><blockquote><p>JDK是用于Java应用程序的开发工具包。它包括Java编译器(javac)、Java虚拟机(JVM),以及各种开发工具和库,如调试器、性能分析工具、API文档生成工具等。JDK是开发人员用于创建、编译和调试Java应用程序的关键工具,它使开发者能够编写Java源代码并将其编译成Java字节码。JDK的结构包括bin目录,其中包含编译器和其他命令行工具,以及lib目录,其中包含Java类库和运行时支持。</p></blockquote><p>② JRE(Java Runtime Environment)</p><blockquote><p>JRE是用于运行Java应用程序的运行时环境。它包括Java虚拟机(JVM)和Java类库(Java API),允许用户执行已编译的Java应用程序,而不需要进行开发。JRE没有开发工具,因此不包含编译器或其他开发相关的工具。JRE的结构包括bin目录,其中包含用于运行Java应用程序的JVM可执行文件,以及lib目录,其中包含Java类库</p></blockquote><p>三者中,JDK 包含 JRE,而 JRE 包含 JVM。JDK 提供了完整的 Java 开发和运行时环境,适用于开发人员,而 JRE 提供了用于运行 Java 应用程序的环境,适用于普通用户或生产环境。 JVM 是在 JDK 和 JRE 中共享的核心组件,负责执行 Java 应用程序。<br><a name="rBZO8"></a></p><h3 id="JVM的整体结构"><a href="#JVM的整体结构" class="headerlink" title="*JVM的整体结构"></a>*JVM的整体结构</h3><p><a name="qEaQI"></a></p><h4 id="简单介绍HotSpot-VM"><a href="#简单介绍HotSpot-VM" class="headerlink" title="简单介绍HotSpot VM"></a>简单介绍HotSpot VM</h4><blockquote><p>HotSpot VM(HotSpot Virtual Machine)是Oracle(以前是Sun Microsystems)开发的 Java 虚拟机的一种实现,它是Java平台上最广泛使用的虚拟机之一。HotSpot VM在性能和可移植性方面表现出色,它是Java应用程序的运行时引擎,负责将Java字节码翻译成本地机器代码并执行程序。</p></blockquote><p>HotSpot VM是Oracle JDK(Java Development Kit)的一部分,也是许多其他Java发行版的基础,包括OpenJDK。由于其卓越的性能和稳定性,HotSpot VM一直是广泛使用的Java虚拟机之一,特别适用于生产环境和要求高性能的Java应用程序。<br><a name="OU6ij"></a></p><h4 id="整体结构介绍"><a href="#整体结构介绍" class="headerlink" title="整体结构介绍"></a>整体结构介绍</h4><p><img src="https://cdn.nlark.com/yuque/0/2023/png/32574087/1697037352181-0af00ce8-0848-4fda-a682-6c6e270b406e.png#averageHue=%23a8a34b&clientId=ua1a09f3e-d408-4&from=paste&height=656&id=u3840c66d&originHeight=820&originWidth=988&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=93707&status=done&style=none&taskId=u4789864f-b3c5-4e3b-9bd6-64f00ffd4a2&title=&width=790.4" alt="未命名文件 (1).png"><br />对于上图,这里简单介绍一下<br />首先是Java类通过编译成class文件,在用过类加载器加载到内存中。</p><blockquote><p>类加载是Java虚拟机(JVM)的一个关键组件,负责将Java字节码文件加载到内存中并转换为可执行的类。类加载器的主要任务是查找和加载类文件,确保在程序运行时可以访问所需的类。类加载器的工作使Java的动态性成为可能,允许在运行时加载和卸载类。</p></blockquote><p>接着看这个第二层,运行时的数据区,这里包含了方法区、Java栈、本地方法栈、堆、程序计数器</p><blockquote><p><strong>方法区</strong>是存储类信息、常量、静态变量以及编译后的字节码的地方,包括类的结构信息、方法和字段描述,以及运行时常量池。<br><strong>堆</strong>是Java程序中用于存储对象实例的区域。<br><strong>栈</strong>是每个线程独有的,用于存储方法调用、局部变量以及方法返回值。<br><strong>本地方法栈</strong>与栈类似,但它存储的是Java程序调用本地方法(使用JNI,Java Native Interface)时的信息。<br><strong>程序计数器</strong>是当前线程执行的字节码指令的位置计数器。</p></blockquote><p>总结</p><blockquote><p>方法区和堆:这两个是多线程共享的<br>Java栈、本地方法栈和程序计数器:这是每个线程都独有一份</p></blockquote><p>最后看第三层,这一层是和操作系统和硬件交互的重要组件。执行引擎,这是Java虚拟机核心组件之一,他是负责执行Java字节码,将Java字节码解释成底层平台的机器码,或者通过即时编译器(Just-In-Time Compiler,JIT)将字节码编译成本地机器代码以提高性能。本地方法接口是Java虚拟机提供的机制,允许Java代码与本地代码(通常是用C或C++编写的)进行交互,在Java中就是呗native修饰的接口方法。本地方法库是包含本地方法的动态链接库(通常是.so文件或.dll文件),用C或C++编写。<br><a name="H06VI"></a></p><h3 id="Java代码执行流程"><a href="#Java代码执行流程" class="headerlink" title="Java代码执行流程"></a>Java代码执行流程</h3><p>Java代码的执行流程可以分为一下几个步骤:</p><ul><li>编写Java代码:编写一个xxx.java的文件。</li><li>编译源代码:使用Java编译器,将Java源文件编译成xx.class的字节码文件(通常是使用javac)。</li><li>Java虚拟机:在Java虚拟机(JVM)中,其负责加载字节码文件并将其转换为可执行代码。在类加载过程中还有一些操作,类加载、链接、初始化等等。</li><li>执行程序:一旦类加载完成并且程序初始化完成,JVM开始执行程序。</li><li>运行时垃圾收集:JVM会定期检查不再被引用的对象,并进行垃圾回收以释放内存资源</li><li>程序结束:程序执行完main方法或遇到异常时,程序将结束执行。</li></ul><p><img src="https://cdn.nlark.com/yuque/0/2023/png/32574087/1697115366666-d576e5ba-1a0d-47ef-9aaf-da3bee24cf33.png#averageHue=%23ace069&clientId=u27bdacaf-3f72-4&from=paste&height=478&id=u58ed14c0&originHeight=597&originWidth=876&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=81702&status=done&style=none&taskId=u9fb3889f-3261-4a43-8a99-860ccbb735d&title=&width=700.8" alt="未命名文件 (2).png"><br><a name="cdebK"></a></p><h3 id="JVM的生命周期"><a href="#JVM的生命周期" class="headerlink" title="JVM的生命周期"></a>JVM的生命周期</h3><p>Java虚拟机的生命周期简单来说就是三个阶段:启动、执行、退出。JVM的生命周期确保了Java程序的安全加载、初始化和执行,以及资源管理和垃圾回收。这一过程是Java跨平台性的关键,因为它将Java程序从特定硬件平台解耦,使其能够在不同操作系统上运行。<br><a name="w8OAC"></a></p><h4 id="虚拟机启动"><a href="#虚拟机启动" class="headerlink" title="虚拟机启动"></a>虚拟机启动</h4><p>Java虚拟机在启动的时候是通过类加载器创建一个初始类来完成。在这一阶段,类加载器加载Java字节码文件,并对其进行验证、准备、解析和初始化等操作,接着链接,包括验证类的正确性、为静态变量分配内存和解析符号引用。然后初始化,在这个阶段,JVM执行类的静态初始化代码,初始化静态变量。<br><a name="D8pai"></a></p><h4 id="虚拟机执行"><a href="#虚拟机执行" class="headerlink" title="虚拟机执行"></a>虚拟机执行</h4><p>JVM进入运行阶段,开始执行程序,通常从main方法开始。程序在运行过程中可以加载、链接、初始化其他类,并执行程序逻辑。同时,JVM的垃圾回收器定期检查不再被引用的对象,进行垃圾回收。这个Java程序在Java虚拟机中就是一个进程。<br><a name="zlLYs"></a></p><h4 id="虚拟机退出"><a href="#虚拟机退出" class="headerlink" title="虚拟机退出"></a>虚拟机退出</h4><p>在程序执行完成或出现严重错误时,JVM会进入终止阶段,可以执行清理操作。最后,如果某个类不再被引用,并且没有实例存在,JVM可能会进行卸载,释放内存。<br><a name="NRH9w"></a></p><h3 id="常见虚拟机"><a href="#常见虚拟机" class="headerlink" title="常见虚拟机"></a>常见虚拟机</h3><p>常见的虚拟机,每种的实现通常都是针对不同的需求,比如sun classic vm、exact vm、jrockit、j9等等。以下笔者通过网络查阅到一些的简单介绍。</p><blockquote><p>在Java生态系统中,有几个常见的Java虚拟机(JVM)实现,每个实现通常针对不同的用例和需求。以下是一些常见的JVM实现:</p><ul><li>Oracle HotSpot JVM:这是Oracle公司的官方JVM实现,广泛用于生产环境。它以高性能和优化而闻名,是许多企业应用程序的首选JVM。</li><li>OpenJDK:OpenJDK是Oracle的开放源JVM实现,也是许多其他JVM的基础。它提供了Java开发工具和库,并被许多Linux发行版采用作为默认JVM。</li><li>IBM J9 JVM:IBM的J9虚拟机是WebSphere应用服务器和其他IBM产品的一部分。它专注于嵌入式和企业级应用。</li><li>Apache Harmony:虽然已经终止,但Apache Harmony曾是一个Apache项目,旨在创建一个免费的、开放源的JVM实现。虽然它不再维护,但它仍然被某些项目和研究用途使用。</li><li>GraalVM:GraalVM是Oracle开发的一款全栈虚拟机,支持多种语言,包括Java、JavaScript、Python等。它具有高性能和多语言互操作性,适用于各种应用场景。</li><li>Azul Zing:Azul Zing是专门针对大规模、高性能Java应用程序而设计的JVM。它包括C4垃圾收集器,可显著降低垃圾回收停顿时间。</li><li>SAP JVM:SAP JVM是针对SAP应用的JVM实现,优化了SAP系统的性能和稳定性。</li></ul><p>这些JVM实现在性能、垃圾回收策略、支持的平台和工具等方面存在差异,开发人员可以根据其应用程序的需求选择最适合的JVM。总的来说,Java虚拟机的多样性为Java生态系统的健康发展提供了重要的选择。</p></blockquote><p><a name="C5GPT"></a></p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>本章就是学习一下Java虚拟机的概念,为了对Java虚拟机有个简单的认识。总之我们知道的JVM有许多,但是我们经常遇到的就是<strong>Oracle HotSpot JVM ,</strong>Java虚拟机的主要任务包括字节码加载、解释执行或即时编译、垃圾回收、内存管理等。它还提供了各种标准库和API,用于支持Java程序的运行和互动。不同的Java虚拟机实现可以适应不同的硬件和操作系统。</p>]]></content>
<summary type="html">学习JVM并不是为了面试,更是为了深入去理解Java这门语言。</summary>
<category term="JVM系列" scheme="https://lydandtry.github.io/categories/JVM%E7%B3%BB%E5%88%97/"/>
<category term="JVM" scheme="https://lydandtry.github.io/tags/JVM/"/>
<category term="Java" scheme="https://lydandtry.github.io/tags/Java/"/>
</entry>
<entry>
<title>摸索·类加载子系统与加载过程</title>
<link href="https://lydandtry.github.io/posts/11372/"/>
<id>https://lydandtry.github.io/posts/11372/</id>
<published>2023-10-26T18:50:34.000Z</published>
<updated>2024-09-19T03:07:44.552Z</updated>
<content type="html"><![CDATA[<h2 id="内存结构简介"><a href="#内存结构简介" class="headerlink" title="内存结构简介"></a>内存结构简介</h2><p>Java虚拟机会通过类加载器子系统去加载字节码,在类加载的过程还包括了将字节码文件加载到内存,JVM会验证字节码文件的格式、解析符号引用,初始化类、接口等,为静态字段分配内存并初始化。会在内存中创建相应的对象,以及对一些静态变量进行初始化等操作,这部分主要是在方法区中执行。然而真正执行字节码指令的是执行引擎,他会根据字节码的顺序去执行,这也就涉及了计数器、栈等的操作。<br /><img src="https://cdn.nlark.com/yuque/0/2023/png/32574087/1697037352181-0af00ce8-0848-4fda-a682-6c6e270b406e.png#averageHue=%23a8a34b&from=url&id=xlZiG&originHeight=820&originWidth=988&originalType=binary&ratio=1.25&rotation=0&showTitle=false&status=done&style=none&title=" alt=""></p><h2 id="类的加载过程"><a href="#类的加载过程" class="headerlink" title="类的加载过程"></a>类的加载过程</h2><p>类加载器包括了加载、链接、初始化三个阶段,每个阶段都有各自的处理行为。</p><blockquote><p>类加载器(Class Loader)是Java虚拟机(JVM)的一个关键组件,它负责将类文件加载到内存中,以便在程序中使用。类加载器的主要任务是在运行时查找并加载类文件,然后生成类的字节码以供JVM执行。类加载器的主要功能包括加载、链接和初始化类。</p></blockquote><p><img src="https://cdn.nlark.com/yuque/0/2023/png/32574087/1697375598201-8ecb3c8c-a886-4ca7-9c2d-3b27a7d0d6be.png#averageHue=%2350db8e&clientId=u5a754bc8-3171-4&from=paste&height=214&id=u0076547e&originHeight=268&originWidth=735&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=33210&status=done&style=none&taskId=u885f21a2-dd65-4c50-b04e-25cf7076430&title=&width=588" alt="未命名文件 (3).png"><br />类的加载流程如下:<br /><strong>1) 加载(Loading):</strong>类加载器查找类文件,通常是.class文件,然后将它们加载到内存中。加载过程包括查找类文件、读取类文件的字节码数据,并创建一个表示类的java.lang.Class对象。<br /><strong>2) 链接(Linking):</strong>链接分为三个阶段:验证、准备和解析。<br /><strong>① 验证(Verification):</strong>在这个阶段,类加载器确保类文件的字节码是合法、符合规范的,并且不包含危险的构造。这是为了确保安全性。<br /><strong>② 准备(Preparation):</strong>在这个阶段,类的静态变量分配内存并初始化为默认值。<br /><strong>③ 解析(Resolution):</strong>这个阶段负责将符号引用解析为直接引用,以便JVM能够识别和访问其他类和方法。<br /><strong>3) 初始化(Initialization):</strong>在这个阶段,类的静态初始化块(static initializer)被执行,静态变量被赋予初始值。这是类加载的最后一个阶段。<br />*类加载器子系统只是负责加载字节码文件,至于它能否运行,还是得由执行引擎来决定。加载类信息包括类的结构信息、运行时常量池、父类信息并且建立继承和接口实现关系。</p><blockquote><p>需要注意的是,在Java 8之前,这些信息存储在方法区中,而在Java 8及以后,方法区被替代为元数据区(Metaspace),并且元数据区采用了一种不同的内存管理方式,它会根据需要动态分配和回收内存,而不是采用固定的区域大小。</p></blockquote><h3 id="1-加载-Loading"><a href="#1-加载-Loading" class="headerlink" title="1) 加载(Loading)"></a>1) 加载(Loading)</h3><p>加载时类加载过程的第一个阶段,在加载阶段,Java虚拟机需要完成以下三件事情:</p><ul><li>通过一个类的全限定名来获取其定义的二进制字节流。</li><li>将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。</li><li>在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口</li></ul><p><img src="https://cdn.nlark.com/yuque/0/2023/png/32574087/1697548671267-24c87672-7c3a-4f96-91c2-5a04d85a290a.png#averageHue=%23f9b57e&clientId=uab87d42f-1192-4&from=paste&height=279&id=uaab16947&originHeight=349&originWidth=660&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=29710&status=done&style=none&taskId=ud8676c8d-3ebc-4131-b15d-b979c364242&title=&width=528" alt="未命名文件 (4).png"><br />这个类加载就是加载二进制字节流的动作,开发者不仅可以使用系统提供的加载器加载,也可以是自己自定义一个类加载器,在自己自定义的类加载器中去做一些其他的操作。<br />类加载会将class文件的结构、方法、字段、注解、访问权限等信息加载到方法区/元数据区中,堆中会用于存储对象实例的内存区域。</p><blockquote><p>类的加载方式</p><ul><li>从本地系统中直接加载</li><li>通过网络下载.class文件</li><li>从zip,jar等归档文件中加载.class文件</li><li>从专有数据库中提取.class文件</li><li>将Java源文件动态编译为.class文件</li></ul></blockquote><h3 id="2-链接-Linking"><a href="#2-链接-Linking" class="headerlink" title="2) 链接(Linking)"></a>2) 链接(Linking)</h3><p>链接阶段又分为三个阶段,分别是验证、准备、解析。目的是为了确保类的结构和引用在运行时是有效的和安全的。通过验证、准备和解析,JVM可以在类加载时会进行必要的检查和调整,以防止潜在的安全问题和运行时错误。这有助于维护Java程序的稳定性和安全性。在链接阶段中,类变量(静态变量)会被分配默认值。对于基本数据类型,这个默认值通常是0或0.0。对于对象引用类型,这个默认值是null。 </p><h4 id="①-验证-Verification"><a href="#①-验证-Verification" class="headerlink" title="① 验证(Verification)"></a>① 验证(Verification)</h4><p>在验证阶段,类加载器需要确保类文件的字节码是否合法、符合规范的,并且不会造成危险。简单来说就是确保被加载类的正确性。</p><blockquote><p>验证的方式:</p><ul><li>格式验证:检查字节码文件的格式是否正确。</li><li>语义验证:检查字节码文件中的操作是否符合Java语言规范。</li><li>字节码验证:检查字节码文件中的操作是否与类的结构和继承关系一致。</li><li>符号引用验证:检查符号引用是否有效,不引用不存在的类或字段。</li></ul></blockquote><p>类加载的字节码文件,字节流数据是以0xCAFEBABE开头。这是一种规范,能够来验证字节码文件是否符合Java规范。<br />这里我们可以看一下编译后的Java字节码的二进制数据长什么样。我们可以在idea中安装一个插件【BinEd-binary-hexadecimal-editor】,然后我们找到编译后的Java字节码文件,然后右键->open in->Binary Editor;就可以在界面上看到十六进制/二进制与代码的对应。<br /><img src="https://cdn.nlark.com/yuque/0/2023/png/32574087/1697382248738-9ffdff35-30ac-407a-be47-fb5cc68bf3f3.png#averageHue=%233e4244&clientId=u5a754bc8-3171-4&from=paste&height=577&id=uc4dfb85a&originHeight=721&originWidth=1866&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=172423&status=done&style=none&taskId=u03916a15-a54a-497e-a83b-351e7addc6b&title=&width=1492.8" alt="image.png"><br />从图中我们就能看到0xCAFEBABE这段十六进制数值。</p><h4 id="②-准备-Preparation"><a href="#②-准备-Preparation" class="headerlink" title="② 准备(Preparation)"></a>② 准备(Preparation)</h4><ul><li><p>准备阶段主要是用于为类的静态字段分配内存并初始化这些字段。这些字段将在类初始化阶段(Initialization)中赋予实际的初始值。</p><blockquote><p>当我们在类中定义了一个变量<code>public static int a = 1;</code>此时在准备阶段的时候,这个值是0,并不是1,直到初始化阶段的时候才会被赋值为1。</p></blockquote></li><li><p>但是这里不包含使用final修饰的static,因为final修饰的变量在编译的时候就会分配好内存,准备阶段会显式初始化。如果final修饰的变量进行赋值,此时会直接报错。</p><blockquote><p>在准备阶段,常量字段(public static final修饰的字段被视为常量)会被分配内存并赋予初始值。对于<code>public static final int c = 3;</code> 这个常量字段,它的初始值将被设置为3。这是因为<code>public static final</code>字段被认为是编译时常量,其值在编译时已经确定。</p></blockquote></li><li><p>这里不会为实例变量分配初始化,类变量会分配到方法区中,而实例变量是会随着对象一起分配到Java堆中。</p><h4 id="③-解析-Resolution"><a href="#③-解析-Resolution" class="headerlink" title="③ 解析(Resolution)"></a>③ 解析(Resolution)</h4></li><li><p>解析阶段负责将符号引用解析为直接引用,以便JVM能够识别和访问其他类和方法。</p></li><li>符号引用是一种符号化的引用,它以符号的方式描述了类、字段、方法或接口的引用,而不包括直接的内存地址或偏移量。直接引用是实际的内存地址或偏移量,它用于直接定位并访问类的实例、字段、方法或接口。</li><li><p>解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。</p><h3 id="3-初始化-Initialization"><a href="#3-初始化-Initialization" class="headerlink" title="3) *初始化(Initialization)"></a>3) *初始化(Initialization)</h3><p>初始化阶段(Initialization)是初始化过程的关键步骤。在这个阶段,类的静态初始化块(static initializer)会被执行,用于为静态字段分配实际的初始值。静态初始化块可以包含任意Java代码,通常用于执行一些静态设置操作。需要注意的是,初始化是懒加载的,即只有在首次触发初始化时才会执行。一旦类被成功初始化,它不会再次初始化,除非应用程序中的某些特殊情况强制重新初始化类。</p><h4 id="何时促发初始化:"><a href="#何时促发初始化:" class="headerlink" title="何时促发初始化:"></a>何时促发初始化:</h4></li><li><p>创建类的实例:当创建类的实例时,首先要确保类已经被初始化。</p></li><li>访问类的静态字段或静态方法:访问静态成员时,会触发类的初始化。</li><li><p>使用反射操作:通过反射方式访问类时,也会触发初始化。</p><h4 id="与"><a href="#与" class="headerlink" title="()与()"></a><init>()与<clinit>()</h4><p>首先我们需要了解一下<init>()与<clinit>()这两个Java编译自动生成的方法。<br /><strong><init> 方法:</strong>这是类的构造方法,用于对象的初始化。构造方法的名称是<init>,它包括在类的字节码中以执行对象初始化操作。不同的构造方法可以接受不同的参数,用于初始化对象的各个属性。<br />首先我们通过一个例子来了解一下Java 的<init>方法,以下编写了一段两个变量的Java代码,a是成员变量,b、c是局部变量,通过构造方法来对a进行赋值。我们在idea安装<strong>jclasslib-bytecode-viewer </strong>插件,可以通过这个插件来查看Java字节码。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JVM01</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">JVM01</span><span class="params">()</span> {</span><br><span class="line"> a = <span class="number">10</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">int</span> <span class="variable">b</span> <span class="operator">=</span> <span class="number">3</span>;</span><br><span class="line"> <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> <span class="number">4</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>点击view->show byteCode with jclasslib就能够看到字节码<br /><img src="https://cdn.nlark.com/yuque/0/2023/png/32574087/1697466976067-d5d303c4-774a-42d1-9635-7a1d23662ac7.png#averageHue=%235a8351&clientId=uc3448cbe-265c-4&from=paste&height=569&id=u750760d0&originHeight=711&originWidth=2117&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=108883&status=done&style=none&taskId=ud52789a2-979b-48fc-a844-91c72bc2e96&title=&width=1693.6" alt="image.png"><br />init方法是类的构造方法,我们点开init下的code,可以清楚的看到字节码执行过程,一开始加载变量a,初始化1,然后进行赋值为10。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">0</span> aload_0</span><br><span class="line"> <span class="number">1</span> invokespecial #<span class="number">1</span> <java/lang/Object.<init> : ()V></span><br><span class="line"> <span class="number">4</span> aload_0</span><br><span class="line"> <span class="number">5</span> iconst_1</span><br><span class="line"> <span class="number">6</span> putfield #<span class="number">2</span> <com/lyd/testboot/jvm/JVM01.a : I></span><br><span class="line"> <span class="number">9</span> aload_0</span><br><span class="line"><span class="number">10</span> bipush <span class="number">10</span></span><br><span class="line"><span class="number">12</span> putfield #<span class="number">2</span> <com/lyd/testboot/jvm/JVM01.a : I></span><br><span class="line"><span class="number">15</span> <span class="keyword">return</span></span><br></pre></td></tr></table></figure><p>同样,我们可以在main#code中看到main中成员变量的赋值情况</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">0</span> iconst_3</span><br><span class="line"><span class="number">1</span> istore_1</span><br><span class="line"><span class="number">2</span> iconst_4</span><br><span class="line"><span class="number">3</span> istore_2</span><br><span class="line"><span class="number">4</span> <span class="keyword">return</span></span><br></pre></td></tr></table></figure><p>我们可以看到开始给b赋值了3,后面给c赋值了4。可见字节码的执行也是按照代码的顺序去执行的。<br /><strong><clinit> 方法:</strong>这是类的静态初始化块(static initializer),用于静态成员的初始化。静态初始化块的名称是<clinit>,它包括在类的字节码中,用于执行静态字段的初始化和执行静态初始化块中的代码。静态初始化块在类加载的初始化阶段执行,确保静态成员的初始值被正确设置。<br />clinit方法是类的静态初始化,我们简单的做个案例,定义一个sta静态变量,并令其值为1,在静态代码块中,我又给他赋值了111,在main方法再次赋值为11,最终静态变量sta的值会是最后的值”11”;接着我们在创建一个变量stb,这回,我们把变量的赋值放在之前,在静态代码块之后去声明变量。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JVM01</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">sta</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">JVM01</span><span class="params">()</span> {</span><br><span class="line"> a = <span class="number">10</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">static</span> {</span><br><span class="line"> sta = <span class="number">111</span>;</span><br><span class="line"> stb = <span class="number">222</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">stb</span> <span class="operator">=</span> <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">int</span> <span class="variable">b</span> <span class="operator">=</span> <span class="number">3</span>;</span><br><span class="line"> <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> <span class="number">4</span>;</span><br><span class="line"> JVM01.sta = <span class="number">11</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>我们通过<strong>jclasslib-bytecode-viewer </strong>插件来看一下字节码<br /><img src="https://cdn.nlark.com/yuque/0/2023/png/32574087/1697468300752-7cbea752-f8a4-4ca3-b50a-daac94b31aa0.png#averageHue=%235c8650&clientId=uc3448cbe-265c-4&from=paste&height=708&id=u4cd0892f&originHeight=885&originWidth=2116&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=127607&status=done&style=none&taskId=u1e0a02c1-a180-4a12-b9f6-43ba342cd06&title=&width=1692.8" alt="image.png"><br />sta,stb在准备阶段的时候默认值是0(因为是int类型),直到分配内存确认值之后分别赋值为1和2,接着会执行静态代码块,执行的时机是在类被加载到内存中,但在类的实例被创建之前。静态代码块在连接阶段执行,而不是在类的实例化或初始化阶段执行。此时分别会被赋值为111和222,那么sta在main中赋值为11?等静态代码块执行完毕之后就会执行main方法,在执行main方法就会将sta的值赋值为11,所以最终的值也就是11。<br />我们点开main#code来看一下执行流程,可见被赋值为11。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">0</span> iconst_3</span><br><span class="line"><span class="number">1</span> istore_1</span><br><span class="line"><span class="number">2</span> iconst_4</span><br><span class="line"><span class="number">3</span> istore_2</span><br><span class="line"><span class="number">4</span> bipush <span class="number">11</span></span><br><span class="line"><span class="number">6</span> putstatic #<span class="number">3</span> <com/lyd/testboot/jvm/JVM01.sta : I></span><br><span class="line"><span class="number">9</span> <span class="keyword">return</span></span><br></pre></td></tr></table></figure><h2 id="类的加载器"><a href="#类的加载器" class="headerlink" title="类的加载器"></a>类的加载器</h2><p>类的加载器负责将类文件加载到内存中,以便在Java程序中使用。类加载器是Java实现动态加载和模块化编程的基础,它有助于实现模块化开发、热部署和插件化架构等功能。主要包括引导类加载器(Bootstrap Class Loader)、扩展类加载器(Extension Class Loader)、应用程序类加载器(Application Class Loader)还有自定义类加载器(User Class Loader)。这些类加载器在jvm启动是自动创建并运行,他们的操作都遵循双亲委派机制,就是在加载类的时候都会向上委托,询问父类加载类,知道所有的父类加载器无法加载的情况下,当前类加载器才会尝试加载。<br /><img src="https://cdn.nlark.com/yuque/0/2023/png/32574087/1697470264647-52eb946a-ecb7-4d98-b3fd-afc9ae9b110d.png#averageHue=%23170d0c&clientId=uc3448cbe-265c-4&from=paste&height=478&id=u54487fef&originHeight=597&originWidth=900&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=60656&status=done&style=none&taskId=u2c21a7e0-e54d-4e94-9ce2-30fec49535b&title=&width=720" alt="未命名文件 (6).png"></p><h3 id="1-三个核心类加载器"><a href="#1-三个核心类加载器" class="headerlink" title="1) 三个核心类加载器"></a>1) 三个核心类加载器</h3><p>简单说一下引导类、扩展类、系统类加载器是什么,每个都有特定的职责和加载类的范围。<br />① 引导类加载器(Bootstrap Class Loader):<br />引导类加载器是负责加载核心的Java类库,如java.lang包中的类,以及其他Java平台的关键组件。这些类库通常存储在JVM安装目录下的lib目录中。开发人员无法直接访问引导类加载器,它主要用于加载Java运行时环境的核心类。引导类加载器只会加载包为java、javax、sun等开头的类。<br />② 扩展类加载器(Extension Class Loader):<br />扩展类加载器可以通过系统属性java.ext.dirs来指定扩展类库的位置。<br />③ 系统类加载器(Application Class Loader):<br />系统类加载器负责加载位于类路径(Classpath)上的类文件,包括用户编写的类和第三方库。大多数Java应用程序都是由系统类加载器加载的,因为它加载位于类路径上的类。<br />我们可以在代码层面来查看加载器,通过java.lang.ClassLoader#getSystemClassLoader()来获取系统类加载器。得到的ClassLoader类,可以继续ClassLoader#getParent()获取上层的类加载器,也就是扩展加载器,直到最上一层的引导类加载器,此时我们获取的值是为null,由此可见,我们是拿不到引导类加载器。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ClassLoadShow</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="comment">// 获取系统类加载器</span></span><br><span class="line"> <span class="type">ClassLoader</span> <span class="variable">classLoader</span> <span class="operator">=</span> ClassLoader.getSystemClassLoader();</span><br><span class="line"> System.out.println(classLoader);</span><br><span class="line"> <span class="comment">// 获取上层:扩展加载器</span></span><br><span class="line"> <span class="type">ClassLoader</span> <span class="variable">extClassLoader</span> <span class="operator">=</span> classLoader.getParent();</span><br><span class="line"> System.out.println(extClassLoader);</span><br><span class="line"> <span class="comment">// 继续获取上层加载器: null 获取不到引导类加载器</span></span><br><span class="line"> <span class="type">ClassLoader</span> <span class="variable">bootstrapClassLoader</span> <span class="operator">=</span> extClassLoader.getParent();</span><br><span class="line"> System.out.println(bootstrapClassLoader);</span><br><span class="line"> <span class="comment">// 获取当前类</span></span><br><span class="line"> <span class="type">ClassLoader</span> <span class="variable">nowClassLoader</span> <span class="operator">=</span> ClassLoadShow.class.getClassLoader();</span><br><span class="line"> System.out.println(nowClassLoader);</span><br><span class="line"> <span class="comment">// String是根据引导类加载器加载的。Java的核心类库都是</span></span><br><span class="line"> System.out.println(String.class.getClassLoader());</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>接下来我们通过sun.misc.Launcher#getBootstrapClassPath().getURLs()获取引导类路径(Bootstrap Class Path)中的URLs。这些路径内的核心Java类库都是引导类加载器,里面的任何一个类.class#getClassLoader()都是为null。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> URL[] urLs = Launcher.getBootstrapClassPath().getURLs();</span><br><span class="line"> <span class="keyword">for</span> (URL urL : urLs) {</span><br><span class="line"> System.out.println(urL.toExternalForm());</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="2-自定义类加载器"><a href="#2-自定义类加载器" class="headerlink" title="2) 自定义类加载器"></a>2) 自定义类加载器</h3><p>我们还可以通过自定类加载器来定制加载类的方式。</p><h4 id="为什么要自定义加载类"><a href="#为什么要自定义加载类" class="headerlink" title="为什么要自定义加载类"></a>为什么要自定义加载类</h4></li><li><p>隔离加载类</p></li><li>修改类的加载方式</li><li>扩展加载方式</li><li><p>防止源码泄露</p><h4 id="如何自定义类加载器"><a href="#如何自定义类加载器" class="headerlink" title="如何自定义类加载器"></a>如何自定义类加载器</h4><p>① 需要继承java.lang.ClassLoader类,可以选择扩展ClassLoader的子类,以便更容易加载外部类文件。<br />②需要重写findClass(String name)方法,这个方法负责查找和加载类的字节码。你需要提供自定义的类加载逻辑。<br />③使用的时候创建一个测试类,实例化你的自定义类加载器,然后使用它来加载类。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.testboot.jvm;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.File;</span><br><span class="line"><span class="keyword">import</span> java.io.FileInputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2023/10/17 00:08</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>:</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClassLoader</span> <span class="keyword">extends</span> <span class="title class_">ClassLoader</span> {</span><br><span class="line"> <span class="comment">// 指定加载类的路径</span></span><br><span class="line"> <span class="keyword">private</span> String classPath;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">MyClassLoader</span><span class="params">(String classPath, ClassLoader parent)</span> {</span><br><span class="line"> <span class="built_in">super</span>(parent);</span><br><span class="line"> <span class="built_in">this</span>.classPath = classPath;</span><br><span class="line"> }</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">protected</span> Class<?> findClass(String name) <span class="keyword">throws</span> ClassNotFoundException {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="type">byte</span>[] classData = loadClassData(); <span class="comment">// 从文件或其他地方加载类字节码</span></span><br><span class="line"> <span class="keyword">return</span> defineClass(name, classData, <span class="number">0</span>, classData.length);</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">super</span>.findClass(name);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 使用字节流获取字节数据</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> IOException</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">private</span> <span class="type">byte</span>[] loadClassData() <span class="keyword">throws</span> IOException {</span><br><span class="line"> <span class="comment">// 这里要读入.class的字节,因此要使用字节流,需要转成绝对路径 D:/Code/Fly.class</span></span><br><span class="line"> <span class="type">File</span> <span class="variable">file</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">File</span>(classPath);</span><br><span class="line"> <span class="type">FileInputStream</span> <span class="variable">fis</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(file);</span><br><span class="line"> <span class="type">int</span> <span class="variable">length</span> <span class="operator">=</span> fis.available();</span><br><span class="line"> <span class="type">byte</span>[] data = <span class="keyword">new</span> <span class="title class_">byte</span>[length];</span><br><span class="line"> fis.read(data);</span><br><span class="line"> fis.close();</span><br><span class="line"> <span class="keyword">return</span> data;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>测试的时候,Class.forName有一个三个参数的重载方法,可以指定类加载器,平时我们使用的Class.forName(“XX.XX.XXX”)都是使用的系统类加载器Application ClassLoader。也可以是使用自定义的加载器MyClassLoader#loadClass()。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClassLoaderTest</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">MyClassLoader</span> <span class="variable">myClassLoader</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyClassLoader</span>(<span class="string">"D:\\Code\\Fly.class"</span>, ClassLoader.getSystemClassLoader().getParent());</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">// 输入外部类的绝对路径</span></span><br><span class="line"><span class="comment">// Class<?> myClass = myClassLoader.loadClass("com.lyd.testboot.jvm.Fly");</span></span><br><span class="line"> Class<?> myClass = Class.forName(<span class="string">"com.lyd.testboot.jvm.Fly"</span>, <span class="literal">true</span>, myClassLoader);</span><br><span class="line"> <span class="type">Object</span> <span class="variable">o</span> <span class="operator">=</span> myClass.newInstance();</span><br><span class="line"> System.out.println(o);</span><br><span class="line"> System.out.println(o.getClass().getClassLoader());</span><br><span class="line"> } <span class="keyword">catch</span> (ClassNotFoundException e) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line"> } <span class="keyword">catch</span> (InstantiationException e) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line"> } <span class="keyword">catch</span> (IllegalAccessException e) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>我们可以通过打印的信息看到,是通过我们自定义的类加载器去实现的<br /><img src="https://cdn.nlark.com/yuque/0/2023/png/32574087/1697548189968-ca14b38e-da58-43cb-b888-a8df22f23b11.png#averageHue=%23997f5b&clientId=uab87d42f-1192-4&from=paste&height=156&id=u6b438901&originHeight=195&originWidth=586&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=17447&status=done&style=none&taskId=u4d339a87-d4e8-4e58-b9f7-d8a06975e1b&title=&width=468.8" alt="image.png"><br />在这里,个人出现最头疼的事情是第二行输出,一直打印出来的是”sun.misc.Launcher$AppClassLoader”。这是因为这个Fly类会自动编译到项目的target目录底下,这下面自然就是由Application ClassLoader加载的。<br />然而解决方式就是在构造方法加上ClassLoader.getSystemClassLoader().getParent(),把自定义ClassLoader的父加载器设置为Extension ClassLoader,这样父加载器加载不到Person.class,就交由子加载器MyClassLoader来加载了。</p><h3 id="3-双亲委派机制"><a href="#3-双亲委派机制" class="headerlink" title="3) 双亲委派机制"></a>3) 双亲委派机制</h3><p>双亲委派机制(Parent Delegation Model)是Java类加载机制的一个关键概念,它用于描述类加载器在加载类时的工作方式。这个机制的核心思想是,类加载器在加载类时首先尝试委派(delegate)加载请求给其父类加载器,只有在父类加载器无法加载该类时,才会自己尝试加载。<br />这个机制能够确保类的一致性,避免类被重复加载。<br /><img src="https://cdn.nlark.com/yuque/0/2023/png/32574087/1697473086627-a8c85dac-b205-4790-b43e-33fc2242c797.png#averageHue=%2318100f&clientId=uc3448cbe-265c-4&from=paste&height=478&id=ucd11c1a8&originHeight=597&originWidth=606&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=47042&status=done&style=none&taskId=u9c31ce8a-6f4e-4bec-a141-8d65f621bf2&title=&width=484.8" alt="未命名文件 (7).png"></p><h4 id="①-双亲委派的原理"><a href="#①-双亲委派的原理" class="headerlink" title="① 双亲委派的原理"></a>① 双亲委派的原理</h4><p>简单理解就是逐层委托,知道父类加载器加载失败,逐层下来,直到都失败了,最后由当前类加载器加载。那么其原理是什么呢?我们可以由几个概念来学习。<br /><strong>i 委派链</strong><br />Java类加载器按照层次结构组织,每个类加载器都有一个父类加载器,直到根加载器。这个层次结构构成了一个委派链。<br /><strong>ii 加载类的请求</strong><br />当一个类加载器得到请求的时候,并不会自己就尝试去加载类,而是不断的委派给父类加载器。<br /><strong>iii 双亲优先</strong><br />父类加载器优先加载,如果父类加载器能够成功加载请求的类,子类加载器就不再尝试加载。<br /><strong>iv 检查类已加载</strong><br />在委派链上传递加载请求时,每个类加载器都会首先检查是否已经加载了请求的类。如果类已经加载,就直接返回该类的Class对象,否则继续委派加载请求。<br /><strong>v 根加载器</strong><br />根加载器(通常是引导类加载器)是类加载器层次结构的顶级,它不具有父加载器。如果根加载器无法加载请求的类,加载请求会向下传递到应用程序类加载器。<br /><strong>vi 应用程序类加载器</strong><br />应用程序类加载器是Java应用程序的类加载器,它通常是加载应用程序的起点。如果应用程序类加载器无法加载请求的类,加载请求会继续向下传递到扩展类加载器,然后到引导类加载器。</p><h4 id="②-双亲委派机制优势"><a href="#②-双亲委派机制优势" class="headerlink" title="② 双亲委派机制优势"></a>② 双亲委派机制优势</h4></li><li><p>防止类的重复加载</p></li><li>保护程序,防止核心API被篡改<h4 id="③-沙箱安全机制"><a href="#③-沙箱安全机制" class="headerlink" title="③ 沙箱安全机制"></a>③ 沙箱安全机制</h4>Java虚拟机 (JVM) 中的沙箱安全机制是一种用于保护系统免受不受信任的Java代码的潜在威胁的措施。这种机制是通过多层安全性措施和类加载机制来实现的,以确保Java应用程序在运行时不会执行危险操作或访问敏感资源。就比如说我们自定义了一个String类,这个类是lang包下的,是由引导类加载器加载,引导类会先加载rt.jar中的java\lang\String.class,并且会报错说没有新增的方法。<h4 id="④-破坏双亲委派机制"><a href="#④-破坏双亲委派机制" class="headerlink" title="④ 破坏双亲委派机制"></a>④ 破坏双亲委派机制</h4>虽然双亲委派机制是默认行为,但在某些情况下,开发人员可能需要破坏这一机制,自定义类加载器以实现特定的类加载行为。通常是在自定义加载器中去重写<code>loadClass</code>或者<code>findClass</code>方法,覆盖其加载逻辑就行。<br />*<blockquote><p>在JVM中,类的完整类名(全限定名)需要完全一致,并且加载器也需要是一样的,这样才能说明是同一个类,如果完整类名一样,但是由不同的类加载器加载,那么这两个类对象就不是同一个。</p></blockquote></li></ul><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>类加载器子系统是JVM中的重要组件,它是负责加载类,在加载这一过程中还细化了加载、链接、初始化的三个重要阶段。在通过字节码我们可以看出Java的init与clinit方法为静态变量进行初始化与赋值。类的加载是遵循双亲委派机制,向上层层委托,但是三层都各有各自加载对应的“范围”,然而在不需要层层委托的情况,可以通过自定义加载类去重写<code>loadClass</code>或者<code>findClass</code>方法达到破坏双亲委派机制。</p><h2 id="博客文献"><a href="#博客文献" class="headerlink" title="博客文献"></a>博客文献</h2><p><a href="https://www.cnblogs.com/xrq730/p/4847337.html">自定义一个类加载器 - 五月的仓颉 - 博客园</a><br /><a href="https://blog.csdn.net/weixin_36586120/article/details/117457014">【精选】Java双亲委派模型:为什么要双亲委派?如何打破它?破在哪里?<em>为什么要打破双亲委派</em>徐同学呀的博客-CSDN博客</a></p>]]></content>
<summary type="html">JVM是如何加载类呢?本期介绍类加载子系统与加载过程。</summary>
<category term="JVM系列" scheme="https://lydandtry.github.io/categories/JVM%E7%B3%BB%E5%88%97/"/>
<category term="JVM" scheme="https://lydandtry.github.io/tags/JVM/"/>
<category term="Java" scheme="https://lydandtry.github.io/tags/Java/"/>
</entry>
<entry>
<title>【JDBC系列】- jdbc的概念以及与数据库的交互流程</title>
<link href="https://lydandtry.github.io/posts/47524/"/>
<id>https://lydandtry.github.io/posts/47524/</id>
<published>2023-07-23T15:36:33.000Z</published>
<updated>2024-09-19T03:07:44.552Z</updated>
<content type="html"><![CDATA[<h1 id="核心API之preparedstatement用法"><a href="#核心API之preparedstatement用法" class="headerlink" title="核心API之preparedstatement用法"></a>核心API之preparedstatement用法</h1><h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>上一篇jdbc系列文章中介绍了概念与statement的使用,但是statement会有一些缺处,所以这篇来学习一下preparedstatement这个API的用法。</p><h2 id="Statement的缺点"><a href="#Statement的缺点" class="headerlink" title="Statement的缺点"></a>Statement的缺点</h2><p>Statement的最大缺点是会有SQL注入的风险。使用Statement时,SQL查询通常是通过字符串拼接构建的。如果没有正确地验证和处理用户输入,可能会导致SQL注入攻击,即恶意用户可以在输入中插入恶意SQL代码,从而执行非授权的数据库操作。</p><p>例如以下SQL语句</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> <span class="string">"lyd"</span>;</span><br><span class="line"><span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">"SELECT * FROM tb_user WHERE username = '"</span> + username + <span class="string">"'"</span>;</span><br></pre></td></tr></table></figure><p>假设username是通过输入传过来的,如果再没做校验的情况下,当输入的值为<code>lyd' or '1' = '1</code>最后形成的SQL语句就变成了</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">"SELECT * FROM tb_user WHERE username = 'lyd' or '1' = '1'"</span>;</span><br></pre></td></tr></table></figure><p>这样我们就能查到指定数据之外的数据了,这就会造成安全隐患。</p><p>更多缺点如下</p><blockquote><ol><li><strong>SQL注入风险:</strong> 使用<code>Statement</code>时,SQL查询通常是通过字符串拼接构建的。如果没有正确地验证和处理用户输入,可能会导致SQL注入攻击,即恶意用户可以在输入中插入恶意SQL代码,从而执行非授权的数据库操作。</li><li><strong>性能问题:</strong> <code>Statement</code>执行SQL查询时,每次都要将SQL语句发送给数据库服务器进行编译。对于重复执行的查询,这会导致性能问题,因为数据库服务器需要重复编译相同的查询语句。</li><li><strong>可读性和维护性:</strong> 使用<code>Statement</code>时,SQL查询通常以字符串形式嵌入Java代码中,这可能导致SQL语句在代码中分散分布,降低代码的可读性和维护性。特别是对于复杂的SQL查询,代码的可读性将变得更差。</li><li><strong>不支持参数化查询:</strong> 在<code>Statement</code>中,SQL查询通常是硬编码在Java代码中的,没有很好地支持参数化查询。参数化查询是一种更安全和高效的方式,它允许将参数值与SQL语句分开,避免了SQL注入风险,并且可以利用数据库的查询缓存。</li></ol></blockquote><h2 id="预编译statement-PreparedStatement"><a href="#预编译statement-PreparedStatement" class="headerlink" title="预编译statement - PreparedStatement"></a>预编译statement - PreparedStatement</h2><p>使用preparedstatement与使用statement的流程是一致的,只是预编译的需要多加一部去传入参数。</p><h3 id="1、简单例子演示预编译statement"><a href="#1、简单例子演示预编译statement" class="headerlink" title="1、简单例子演示预编译statement"></a>1、简单例子演示预编译statement</h3><p>用个简单的例子来演示一下。与statement没什么太大的区别,主要是创建传输sql的对象不一样,不然其他基本都是相同的,这里需要注意的是,在使用预编译statement的SQL语句需要用“?”占位符来替代动态数据。在创建PreparedStatement对象之后,需要根据下标进行给占位符赋值,顺序是从左往右,从1开始。最后执行与statement大致相同,单纯查询就是用executeQuery。最后得到的结果集可以直接解析。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 使用预编译Statement</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2023/7/23</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JDBCPreparedStatement</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ClassNotFoundException, SQLException {</span><br><span class="line"> <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> <span class="string">"lyd"</span>;</span><br><span class="line"> <span class="comment">// 1、注册驱动</span></span><br><span class="line"> Class.forName(<span class="string">"com.mysql.cj.jdbc.Driver"</span>);</span><br><span class="line"> <span class="comment">// 2、创建连接</span></span><br><span class="line"> <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> DriverManager.getConnection(<span class="string">"jdbc:mysql://127.0.0.1:3306/cloud_user"</span>, <span class="string">"root"</span>, <span class="string">"12356"</span>);</span><br><span class="line"> <span class="comment">// 3、创建preparedstatement</span></span><br><span class="line"> <span class="comment">// 3.1、编写sql语句 不包含动态部分,动态值用占位符?来替代</span></span><br><span class="line"> <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">"SELECT * FROM tb_user WHERE username = ?"</span>;</span><br><span class="line"> <span class="comment">// 3.2、创建preparedstatement,传入动态值</span></span><br><span class="line"> <span class="type">PreparedStatement</span> <span class="variable">preparedStatement</span> <span class="operator">=</span> connection.prepareStatement(sql);</span><br><span class="line"> <span class="comment">// 3.3、占位符赋值: 从左向右,从1开始。set是有类型的,可以使用Object</span></span><br><span class="line"> preparedStatement.setObject(<span class="number">1</span>, name);</span><br><span class="line"> <span class="comment">// 4、发送sql 查询就用executeQuery</span></span><br><span class="line"> <span class="type">ResultSet</span> <span class="variable">resultSet</span> <span class="operator">=</span> preparedStatement.executeQuery();</span><br><span class="line"> <span class="comment">// 5、解析结果</span></span><br><span class="line"> <span class="keyword">while</span> (resultSet.next()) {</span><br><span class="line"> System.out.println(resultSet.getString(<span class="number">1</span>));</span><br><span class="line"> System.out.println(resultSet.getString(<span class="number">2</span>));</span><br><span class="line"> System.out.println(resultSet.getString(<span class="number">3</span>));</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 6、释放资源</span></span><br><span class="line"> resultSet.close();</span><br><span class="line"> preparedStatement.close();</span><br><span class="line"> connection.close();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="2、进一步了解PreparedStatement"><a href="#2、进一步了解PreparedStatement" class="headerlink" title="2、进一步了解PreparedStatement"></a>2、进一步了解PreparedStatement</h3><p>通过上面的简单实例,我们可以大致了解预编译statement无论是在安全上还是性能上都会比statement更胜一筹。我们进入PreparedStatement接口中可以看到他实际上是继承了Statement。</p><p><img src="https://img1.imgtp.com/2023/07/23/CbPfJj7x.png" alt="image-20230723140703940.png"></p><p>官方也提供了一个例子,通过下标给占位符去赋值。这样就能够很好的解决SQL注入的风险。</p><h4 id="1-、执行DML语句"><a href="#1-、执行DML语句" class="headerlink" title="1)、执行DML语句"></a>1)、执行DML语句</h4><p>使用预编译statement执行DML语句,操作步骤与查询是一致的,只是在发送SQL的时候使用的是executeUpdate,他返回的并不是结果集,而是行数,正如我们用Navicat执行时显示的几行受影响一样,成功执行了多少行数据就会返回多少行,当执行失败就会返回0。</p><p>代码如下</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JDBCDMLPreparedStatement</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ClassNotFoundException, SQLException {</span><br><span class="line"> <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> <span class="string">"lyd"</span>;</span><br><span class="line"> <span class="comment">// 1、注册驱动</span></span><br><span class="line"> Class.forName(<span class="string">"com.mysql.cj.jdbc.Driver"</span>);</span><br><span class="line"> <span class="comment">// 2、创建连接</span></span><br><span class="line"> <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> DriverManager.getConnection(<span class="string">"jdbc:mysql://127.0.0.1:3306/cloud_user"</span>, <span class="string">"root"</span>, <span class="string">"12356"</span>);</span><br><span class="line"> <span class="comment">// 3、创建preparedstatement</span></span><br><span class="line"> <span class="comment">// 3.1、编写sql语句 不包含动态部分,动态值用占位符?来替代</span></span><br><span class="line"> <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">"INSERT INTO tb_user(id,username,address) VALUES (?,?,?)"</span>;</span><br><span class="line"> <span class="comment">// 3.2、创建preparedstatement,传入动态值</span></span><br><span class="line"> <span class="type">PreparedStatement</span> <span class="variable">preparedStatement</span> <span class="operator">=</span> connection.prepareStatement(sql);</span><br><span class="line"> <span class="comment">// 3.3、占位符赋值: 从左向右,从1开始。set是有类型的,可以使用Object</span></span><br><span class="line"> preparedStatement.setObject(<span class="number">1</span>, <span class="number">3</span>);</span><br><span class="line"> preparedStatement.setObject(<span class="number">2</span>, <span class="string">"opo"</span>);</span><br><span class="line"> preparedStatement.setObject(<span class="number">3</span>, <span class="string">"quanzhou"</span>);</span><br><span class="line"> <span class="comment">// 4、发送sql DML语句使用</span></span><br><span class="line"> <span class="type">int</span> <span class="variable">row</span> <span class="operator">=</span> preparedStatement.executeUpdate();</span><br><span class="line"> <span class="comment">// 5、解析结果</span></span><br><span class="line"> System.out.println(row + <span class="string">"行受影响"</span>);</span><br><span class="line"> <span class="comment">// 6、释放资源</span></span><br><span class="line"> preparedStatement.close();</span><br><span class="line"> connection.close();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>执行后,可以在数据库中看到新增了这条数据。</p><p><img src="https://img1.imgtp.com/2023/07/23/1wvf3ZTd.png" alt="image-20230723141932197.png"></p><h4 id="2-、执行DQL语句"><a href="#2-、执行DQL语句" class="headerlink" title="2)、执行DQL语句"></a>2)、执行DQL语句</h4><p>这次我们获取所有数据,并且遍历结果集,将每行的数据保存到一个链表当中,用map进行key-value进行映射。</p><h5 id="注册驱动"><a href="#注册驱动" class="headerlink" title="注册驱动"></a>注册驱动</h5><p>还是一样的方法,我们采用反射的方式来注册驱动。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Class.forName(<span class="string">"com.mysql.cj.jdbc.Driver"</span>);</span><br></pre></td></tr></table></figure><h5 id="创建连接"><a href="#创建连接" class="headerlink" title="创建连接"></a>创建连接</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> DriverManager.getConnection(<span class="string">"jdbc:mysql://127.0.0.1:3306/cloud_user"</span>, <span class="string">"root"</span>, <span class="string">"12356"</span>);</span><br></pre></td></tr></table></figure><h5 id="创建preparedstatement"><a href="#创建preparedstatement" class="headerlink" title="创建preparedstatement"></a>创建preparedstatement</h5><p>因为不需要传入参数,也就没有使用占位符,然而这里也就不需要去对占位符进行赋值,只需要创建preparedstatement对象,将SQL语句放入就行。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 编写sql语句 不包含动态部分,动态值用占位符?来替代</span></span><br><span class="line"><span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">"SELECT * FROM tb_user"</span>;</span><br><span class="line"><span class="type">PreparedStatement</span> <span class="variable">preparedStatement</span> <span class="operator">=</span> connection.prepareStatement(sql);</span><br></pre></td></tr></table></figure><h5 id="发送SQL"><a href="#发送SQL" class="headerlink" title="发送SQL"></a>发送SQL</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 发送sql 查询就用executeQuery</span></span><br><span class="line"><span class="type">ResultSet</span> <span class="variable">resultSet</span> <span class="operator">=</span> preparedStatement.executeQuery();</span><br></pre></td></tr></table></figure><h5 id="解析结果集"><a href="#解析结果集" class="headerlink" title="解析结果集"></a>解析结果集</h5><p>我们需要将每行数据进行提取,并且提取列名作为key而列值作为value来进行类似映射封装,最后存在List中。在之前就知道了ResultSet包含了一个行的游标,通过next可以将游标移动到下一行。然后可以通过get方法,来根据列标签或者是下表来获取值。如果要达成我们的目的,可以直接自定义key名称,再通过get方法获取值。虽然这样可以做到,但是灵活度不高。而本次要学习的是灵活度相对高一点的方案。</p><p>我们看一下数据集,这有3行数据,每行有3列,我们可以通过next方法来做到移动游标获取行数据,那么列呢?我们要如何才能获得数据库的列名或者查询数据的列标签呢?</p><p><img src="https://img1.imgtp.com/2023/07/23/zUpWAFfJ.png" alt="image-20230723145713518.png"></p><h5 id="ResultSetMetaData"><a href="#ResultSetMetaData" class="headerlink" title="ResultSetMetaData"></a>ResultSetMetaData</h5><p>ResultSet中有一个getMetaData()方法,它能够返回一个ResultSetMetaData对象,这个对象就是当前结果集列信息对象。我们通过源码可以看到这个列信息对象中包含了会话和字段等信息。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ResultSetMetaData</span> <span class="keyword">implements</span> <span class="title class_">java</span>.sql.ResultSetMetaData {</span><br><span class="line"> <span class="keyword">private</span> Session session;</span><br><span class="line"> <span class="keyword">private</span> Field[] fields;</span><br><span class="line"> <span class="type">boolean</span> <span class="variable">useOldAliasBehavior</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line"> <span class="type">boolean</span> <span class="variable">treatYearAsDate</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">private</span> ExceptionInterceptor exceptionInterceptor;</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>我们可以通过getColumnCount()方法获取这个结果集有多少个字段(列)。在package com.mysql.cj.jdbc.result.ResultSetMetaData#getColumnCount,实际上就是统计了 Field[] fields 这个字段数组的长度。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getColumnCount</span><span class="params">()</span> <span class="keyword">throws</span> SQLException {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.fields.length;</span><br><span class="line"> } <span class="keyword">catch</span> (CJException var2) {</span><br><span class="line"> <span class="keyword">throw</span> SQLExceptionsMapping.translateException(var2, <span class="built_in">this</span>.exceptionInterceptor);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这样我们就可以在每行数据集中去遍历每列数据,这里需要注意的是,遍历的下标是从1开始到列长度数量。</p><p>获取列的信息就通过ResultSetMetaData对象获取,获取行相关信息就通过ResultSet对象获取,ResultSetMetaData能够获取列名或者列标签,建议使用列标签,因为有可能在查询的时候取了别名。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">ResultSet</span> <span class="variable">resultSet</span> <span class="operator">=</span> preparedStatement.executeQuery();</span><br><span class="line">List<Map> list = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line"><span class="type">ResultSetMetaData</span> <span class="variable">metaData</span> <span class="operator">=</span> resultSet.getMetaData();</span><br><span class="line"><span class="type">int</span> <span class="variable">columnCount</span> <span class="operator">=</span> metaData.getColumnCount();</span><br><span class="line"><span class="comment">// 解析结果</span></span><br><span class="line"><span class="keyword">while</span> (resultSet.next()) {</span><br><span class="line"> <span class="type">Map</span> <span class="variable">map</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span><>();</span><br><span class="line"> <span class="comment">// 遍历数据</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i <= columnCount; i++) {</span><br><span class="line"> <span class="comment">// 通过下标获取列名 - 通过 ResultSetMetaData对象获取</span></span><br><span class="line"> <span class="type">String</span> <span class="variable">columnLabel</span> <span class="operator">=</span> metaData.getColumnLabel(i);</span><br><span class="line"> <span class="comment">// 通过下标获取列值 - 通过 ResultSet对象获取</span></span><br><span class="line"> <span class="type">String</span> <span class="variable">value</span> <span class="operator">=</span> resultSet.getString(i);</span><br><span class="line"> map.put(columnLabel, value);</span><br><span class="line"> }</span><br><span class="line"> list.add(map);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>运行之后的结果</p><p><img src="https://img1.imgtp.com/2023/07/23/i4VVzCyo.png" alt="image-20230723151558555.png"></p><p>ResultSetMetaData对象中还有许多的方法,如果是学习mybatis底层源码的时候,这些都是必不可少的资料。通过断点可以查看这个对象中的属性有哪些。这些后续会慢慢深入研究,并把学习成果分享出来。</p><p><img src="https://img1.imgtp.com/2023/07/23/7aqcEccR.png" alt="image-20230723151906760.png"></p>]]></content>
<summary type="html">这篇来学习一下preparedstatement这个API的用法。</summary>
<category term="JDBC" scheme="https://lydandtry.github.io/categories/JDBC/"/>
<category term="JDBC" scheme="https://lydandtry.github.io/tags/JDBC/"/>
<category term="数据库" scheme="https://lydandtry.github.io/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
<category term="java" scheme="https://lydandtry.github.io/tags/java/"/>
</entry>
<entry>
<title>【JDBC系列】- jdbc的概念以及与数据库的交互流程</title>
<link href="https://lydandtry.github.io/posts/47523/"/>
<id>https://lydandtry.github.io/posts/47523/</id>
<published>2023-07-16T17:29:33.000Z</published>
<updated>2024-09-19T03:07:44.552Z</updated>
<content type="html"><![CDATA[<h1 id="jdbc的概念以及与数据库的交互流程"><a href="#jdbc的概念以及与数据库的交互流程" class="headerlink" title="jdbc的概念以及与数据库的交互流程"></a>jdbc的概念以及与数据库的交互流程</h1><p><img src="https://img1.imgtp.com/2023/07/16/Sufu1Glx.jpg" alt="jdbc1.jpg"></p><h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>人总是向前走的,目标是更上一层楼。原本笔者奔着一颗炙热的心去学习mybatis底层原理,无奈看了一章感觉我还是太菜了,没有对jdbc有个基础的了解,看起来就比较吃力,因此这次就来学习一下jdbc。本次所学习的大致内容是jdbc的概念与理解,以及如何去使用jdbc来实现Java程序和数据库进行打交道。</p><h2 id="JDBC的概念"><a href="#JDBC的概念" class="headerlink" title="JDBC的概念"></a>JDBC的概念</h2><p>JDBC(Java Database Connectivity),它是Java编程语言中用于连接和操作数据库的API。JDBC提供了一组类和接口,允许Java应用程序与各种关系型数据库进行通信。它提供了一种标准化的方式来与数据库交互。</p><h3 id="1、jdbc与数据的交互"><a href="#1、jdbc与数据的交互" class="headerlink" title="1、jdbc与数据的交互"></a>1、jdbc与数据的交互</h3><p>Java程序通过jdbc这个规范(接口)来与第三方数据库厂商进行交互,根据jdbc规范根据具体的实现驱动代码,不同厂商数据库的驱动代码不同,但是方法是相同的。jdbc是一组接口,最终实现是由各个数据库厂商(jar)来实现的,是一种面向接口编程。无论是MySQL、Oracle、SQL Server还是其他关系型数据库系统,只需要切换对应的数据库驱动jar包。</p><p><img src="https://img1.imgtp.com/2023/07/16/6K2ELCJk.png" alt="image-20230715220756994.png"></p><h3 id="2、jdbc的一些概念"><a href="#2、jdbc的一些概念" class="headerlink" title="2、jdbc的一些概念"></a>2、jdbc的一些概念</h3><p>接下来就了解一些主要的概念,包括驱动、连接、语句、结果集等。</p><blockquote><ol><li>数据库驱动程序(Database Driver):数据库驱动程序是实现JDBC接口的软件组件,它允许Java应用程序与特定类型的数据库进行通信。不同的数据库供应商提供不同的驱动程序实现。</li><li>连接(Connection):连接是通过JDBC与数据库建立的会话。它表示Java应用程序与数据库之间的通信通道。使用连接,应用程序可以执行SQL语句并获取结果。</li><li>语句(Statement):语句是在数据库上执行的SQL命令。JDBC提供了多种语句类型,包括普通语句(Statement)、预编译语句(PreparedStatement)和可调用语句(CallableStatement)等。应用程序可以使用这些语句来执行查询、更新、插入和删除等操作。</li><li>结果集(ResultSet):结果集是从数据库检索到的数据集合。它是通过执行查询语句获得的,并且提供了一种迭代的方式来遍历和访问结果。</li><li>批处理(Batch Processing):JDBC支持批处理操作,它允许一次执行多个SQL语句。这对于批量插入、更新或删除数据非常有用,可以提高性能和效率。</li><li>事务(Transaction):事务是一组数据库操作,它们要么全部成功执行,要么全部回滚(撤销)。JDBC支持事务管理,可以通过开始事务、提交事务或回滚事务来确保数据的一致性和完整性。</li></ol></blockquote><h2 id="使用核心API-statement"><a href="#使用核心API-statement" class="headerlink" title="使用核心API - statement"></a>使用核心API - statement</h2><p>在JDBC中,statement(语句)是用于执行SQL命令的接口之一。它用于执行静态SQL语句[1],即在编译时已经确定的SQL语句,不允许参数化。</p><blockquote><p>[1] 静态SQL语句是在编译时已经确定的SQL语句,其结构和内容在程序运行时不会改变。它是指那些SQL查询或数据操作语句的文本内容在编译时就已经确定,并且不需要在运行时进行参数化或动态修改的语句。</p></blockquote><h3 id="1、jdbc是如何进行与数据库通信的呢?"><a href="#1、jdbc是如何进行与数据库通信的呢?" class="headerlink" title="1、jdbc是如何进行与数据库通信的呢?"></a>1、jdbc是如何进行与数据库通信的呢?</h3><p>jdbc是Java程序与数据库交互的一个桥梁,完成通信需要通过六个步骤:</p><p>①、加载驱动程序,需要加载适用于特定数据库的驱动程序,本次是使用mysql8.0,所以需要加载mysql-connector-java-8.0.27.jar包。</p><p>②、建立连接,通过驱动去建立连接,可以理解为这样就会有一条与数据库服务器交互的管道。实际上就是使用数据库连接URL指定数据库的地址、端口和其他连接参数,通过TCP/IP协议建立与数据库服务器的连接。</p><p>③、创建Statement或PreparedStatement对象,这个对象是存储sql语句,可以理解为是一个搭载sql语句的盒子,需要被送到数据库服务器中执行。</p><p>④、执行SQL语句,需要将statement对象通过连接通道送到数据库服务器执行sql语句并且返回结果集。</p><p>⑤、处理结果,将返回的结果集resultSet进行解析处理。</p><p>⑥、关闭资源,使用完Statement、PreparedStatement、ResultSet和连接等资源后,需要调用它们的<code>close()</code>方法来关闭资源。关闭资源可以释放数据库连接和相关的系统资源。</p><p><img src="https://img1.imgtp.com/2023/07/16/1SnXwaJI.png" alt="image-20230715225147420.png"></p><p>以上这张图简单的画出了jdbc与数据库通信的流程步骤。</p><h3 id="2、用代码实现jdbc与数据库通信步骤"><a href="#2、用代码实现jdbc与数据库通信步骤" class="headerlink" title="2、用代码实现jdbc与数据库通信步骤"></a>2、用代码实现jdbc与数据库通信步骤</h3><p>接下来就简单使用一个demo来实现以上jdbc与数据的通信步骤,主要是通过驱动管理器DriverManager来注册驱动和建立连接,在建立连接的时候需要传入数据库服务器url和用户名、密码、数据库信息,与navicat的连接是差不多的,只是使用java代码来实现,然后创建statement对象去执行sql语句,并且能获得一个ResultSet对象的结果集,里面有next()方法用来移动游标输出数据,当开始是指向第一行数据之前,next()将游标往下移动一行,接着通过getString(columnLabel)等方法获取每行的列数据。</p><h4 id="准备数据"><a href="#准备数据" class="headerlink" title="准备数据"></a>准备数据</h4><p>需要准备数据库,这里用一张tb_user的表来做测试,里面就只有id、name、address三个字段。</p><p><img src="https://img1.imgtp.com/2023/07/16/sCPVr0hy.png" alt="image-20230716131453492.png"></p><h4 id="代码如下"><a href="#代码如下" class="headerlink" title="代码如下"></a>代码如下</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.lyd.jdbc;</span><br><span class="line"><span class="keyword">import</span> com.mysql.cj.jdbc.Driver;</span><br><span class="line"><span class="keyword">import</span> java.sql.*;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: Jdbc - statement使用</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2023/7/15</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JdbcDemo</span> {</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * DriverManager</span></span><br><span class="line"><span class="comment"> * Connection</span></span><br><span class="line"><span class="comment"> * Statement</span></span><br><span class="line"><span class="comment"> * ResultSet</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> args</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> SQLException {</span><br><span class="line"> <span class="comment">// 1、注册驱动</span></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 注册驱动,如果是8.0+就使用com.mysql.cj.jdbc.Driver</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> DriverManager.registerDriver(<span class="keyword">new</span> <span class="title class_">Driver</span>());</span><br><span class="line"> <span class="comment">// 2、获取连接</span></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 连接数据库需要填入数据库ip地址,端口号,用户名,密码,连接的数据库名称</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> DriverManager.getConnection(<span class="string">"jdbc:mysql://127.0.0.1:3306/cloud_user"</span>, <span class="string">"root"</span>, <span class="string">"12356"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 3、创建statement</span></span><br><span class="line"> <span class="type">Statement</span> <span class="variable">statement</span> <span class="operator">=</span> connection.createStatement();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 4、发送sql语句并执行</span></span><br><span class="line"> <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">"select * from tb_user;"</span>;</span><br><span class="line"> <span class="type">ResultSet</span> <span class="variable">resultSet</span> <span class="operator">=</span> statement.executeQuery(sql);</span><br><span class="line"> <span class="comment">// 5、解析结果集</span></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 有没有下行数据</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">while</span> (resultSet.next()) { <span class="comment">// 最开始指定的是数据的上一行即起始位置,并无数据,.next方法会将游标往下移动一行。</span></span><br><span class="line"> <span class="type">long</span> <span class="variable">id</span> <span class="operator">=</span> resultSet.getLong(<span class="string">"id"</span>);</span><br><span class="line"> <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> resultSet.getString(<span class="string">"username"</span>);</span><br><span class="line"> <span class="type">String</span> <span class="variable">address</span> <span class="operator">=</span> resultSet.getString(<span class="string">"address"</span>);</span><br><span class="line"> System.out.println(id + <span class="string">" - "</span> + username + <span class="string">" - "</span> + address);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 6、关闭资源</span></span><br><span class="line"> resultSet.close();</span><br><span class="line"> statement.close();</span><br><span class="line"> connection.close();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="执行结果"><a href="#执行结果" class="headerlink" title="执行结果"></a>执行结果</h4><p>通过控制台输出获取得到的数据</p><p><img src="https://img1.imgtp.com/2023/07/16/qfhHIesv.png" alt="image-20230716131544439.png"></p><h2 id="详细介绍使用核心API-statement"><a href="#详细介绍使用核心API-statement" class="headerlink" title="详细介绍使用核心API - statement"></a>详细介绍使用核心API - statement</h2><p>接下来详细介绍以下流程步骤中的一些小问题与小细节。</p><h3 id="1、注册驱动"><a href="#1、注册驱动" class="headerlink" title="1、注册驱动"></a>1、注册驱动</h3><p>注册驱动,可以通过驱动管理(<strong>DriverManager</strong>)的<code>DriverManager.registerDriver(new Driver());</code>方法来实现。这样的注册驱动就会出现驱动被注册了两次,我们看一下DriverManager.registerDriver()方法内容</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">registerDriver</span><span class="params">(java.sql.Driver driver)</span></span><br><span class="line"> <span class="keyword">throws</span> SQLException {</span><br><span class="line"></span><br><span class="line"> registerDriver(driver, <span class="literal">null</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>再看一下new Driver()的时候,在Driver.class里面的静态代码块也是会注册一次驱动。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Driver</span> <span class="keyword">extends</span> <span class="title class_">NonRegisteringDriver</span> <span class="keyword">implements</span> <span class="title class_">java</span>.sql.Driver {</span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Driver</span><span class="params">()</span> <span class="keyword">throws</span> SQLException {</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> DriverManager.registerDriver(<span class="keyword">new</span> <span class="title class_">Driver</span>());</span><br><span class="line"> } <span class="keyword">catch</span> (SQLException var1) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">"Can't register driver!"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>对于这个问题,我们只要让Driver的静态代码块执行就行了。这里我们就要先了解以下静态代码块是何时触发的呢?</p><blockquote><p>静态代码块的执行时期:</p><p>类的加载机制:在类的加载的时候会执行静态代码块。简单的介绍一下步骤:</p><p> 1、加载(Loading):将类的字节码文件加载到内存中。</p><p> 2、链接(Linking):链接又分了三个阶段,验证(Verification):对加载的字节码进行验证,确保其符合Java虚拟机规范,不会造成安全或运行时错误;准备(Preparation):为类的静态变量分配内存空间,并设置默认初始值;解析(Resolution):将符号引用(Symbolic References)解析为直接引用(Direct References),即将类、方法、字段等符号引用解析为内存中的实际数据。</p><p> 3、初始化(Initialization):对类进行初始化,包括静态变量的赋值和静态代码块的执行。</p><p>触发类加载:</p><p> 1、通过new关键字;2、调用了静态方法;3、调用了静态属性;4、jdk1.8的接口加上了default关键字;5、反射;6、子类触发父类;7、程序的入口main。</p></blockquote><h4 id="驱动重复注册的解决"><a href="#驱动重复注册的解决" class="headerlink" title="驱动重复注册的解决"></a>驱动重复注册的解决</h4><p>显然我们就不能直接使用<code>DriverManager.registerDriver(new Driver());</code> 来直接注册驱动,我们如果想不使它重复注册,可以让他只是执行一次静态代码块直接<code>new Driver()</code> ,当然这样做的也有弊端,如果我们使用的不是mysql我们就要导入对应的包,就显得不太灵活。</p><p><img src="https://img1.imgtp.com/2023/07/16/mOdSzDCB.png" alt="image-20230716144510821.png"></p><p>换种方式,我们可以使用反射的机制来实现。当使用不同的数据库,就只要更换一下驱动的全类名,这个字符串是可以提出来作为一种可配置的参数,这样就比较灵活了。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Class.forName(<span class="string">"com.mysql.cj.jdbc.Driver"</span>);</span><br></pre></td></tr></table></figure><h3 id="2、获取连接"><a href="#2、获取连接" class="headerlink" title="2、获取连接"></a>2、获取连接</h3><p>我们可以看到DriverManager.getconnection()重载了三个方法。三个方法的效果是一样的,只是携带的参数写法并不相同。</p><p><img src="https://img1.imgtp.com/2023/07/16/jcHR3jlx.png" alt="image-20230716145004432.png"></p><h4 id="1-、getConnection-String-url-String-user-String-password"><a href="#1-、getConnection-String-url-String-user-String-password" class="headerlink" title="1)、getConnection(String url, String user, String password)"></a>1)、getConnection(String url, String user, String password)</h4><p>我们需要将数据库服务器的地址,用户名,密码传入就能建立连接。用户名和密码没什么可说的,就是登录凭证。就是这个url地址,这个地址的组成是:</p><figure class="highlight tex"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">jdbc:数据库管理软件名称://ip地址|主机名:port端口号/数据库?key=value<span class="built_in">&</span>key=value,后面是可选参数。</span><br><span class="line">如果是localhost:3306可以替换成 jdbc:数据库管理软件名称:///数据库</span><br></pre></td></tr></table></figure><p>这个方法实现,传进来的用户名密码,最后也是放到了java.util.Properties这个对象之中。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@CallerSensitive</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> Connection <span class="title function_">getConnection</span><span class="params">(String url,</span></span><br><span class="line"><span class="params"> String user, String password)</span> <span class="keyword">throws</span> SQLException {</span><br><span class="line"> java.util.<span class="type">Properties</span> <span class="variable">info</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">java</span>.util.Properties();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (user != <span class="literal">null</span>) {</span><br><span class="line"> info.put(<span class="string">"user"</span>, user);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (password != <span class="literal">null</span>) {</span><br><span class="line"> info.put(<span class="string">"password"</span>, password);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> (getConnection(url, info, Reflection.getCallerClass()));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="2-、getConnection-String-url-java-util-Properties-info"><a href="#2-、getConnection-String-url-java-util-Properties-info" class="headerlink" title="2)、getConnection(String url, java.util.Properties info)"></a>2)、getConnection(String url, java.util.Properties info)</h4><p>这个方法与上一个不一样的是,将用户和密码在调用的时候直接放入java.util.Properties这个对象之中,</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Properties</span> <span class="variable">properties</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line">properties.put(<span class="string">"user"</span>, <span class="string">"root"</span>);</span><br><span class="line">properties.put(<span class="string">"password"</span>, <span class="string">"root"</span>);</span><br><span class="line">DriverManager.getConnection(<span class="string">"jdbc:mysql://127.0.0.1:3306/cloud_user"</span>, properties);</span><br></pre></td></tr></table></figure><h4 id="3-、getConnection-String-url"><a href="#3-、getConnection-String-url" class="headerlink" title="3)、getConnection(String url)"></a>3)、getConnection(String url)</h4><p>这个方法就是把所有的需要的参数都放在这个url里头了,其组成如下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jdbc:数据库管理软件名称://ip地址|主机名:port端口号/数据库?user=value&password=value</span><br></pre></td></tr></table></figure><h3 id="3、创建Statement对象"><a href="#3、创建Statement对象" class="headerlink" title="3、创建Statement对象"></a>3、创建Statement对象</h3><p>通过连接connection去创建,就能够获得一个statement对象。并且需要一条sql语句的字符串。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Statement</span> <span class="variable">statement</span> <span class="operator">=</span> connection.createStatement();</span><br></pre></td></tr></table></figure><h3 id="4、发送SQL语句"><a href="#4、发送SQL语句" class="headerlink" title="4、发送SQL语句"></a>4、发送SQL语句</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">"SELECT * FROM tb_user WHERE username = 'lyd';"</span>;</span><br><span class="line"><span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> statement.executeUpdate(sql);</span><br><span class="line"><span class="type">ResultSet</span> <span class="variable">resultSet</span> <span class="operator">=</span> statement.executeQuery(sql);</span><br></pre></td></tr></table></figure><p>当使用的是executeUpdate就会返回一个int数值,使用executeQuery将会返回一个结果集。</p><p>我们需要根据不用的SQL类型去使用对应的方法,在statement接口中已经标明了注释。</p><p>使用executeQuery:如果发送的sql语句是静态sql SELECT语句,返回的结果就是包含给定查询产生的数据的ResultSet对象,如果是没有数据,那就返回为null。</p><p>使用executeUpdate:如果语句是SQL数据操作语言(DML)语句,(INSERT、UPDATE或DELETE);或者是不返回任何结果的SQL语句,比如DDL语句。返回的结果就是数据操作语言(DML)语句的行数,没有就是返回0。</p><p><img src="https://img1.imgtp.com/2023/07/16/XIHAMBrB.png" alt="image-20230716160924816.png"></p><p>学习一下SQL主要的分类:</p><blockquote><p>数据定义语言(Data Definition Language,DDL):DDL用于创建和管理数据库对象,如表、视图、索引等。常见的DDL语句包括CREATE(创建)、ALTER(修改)、DROP(删除)等。</p><p>数据操作语言(Data Manipulation Language,DML):DML用于查询和操作数据库中的数据记录。它包括SELECT(查询)、INSERT(插入)、UPDATE(更新)、DELETE(删除)等语句,用于从数据库中检索、插入、更新和删除数据。</p><p>数据控制语言(Data Control Language,DCL):DCL用于管理数据库的访问权限和安全性。它包括GRANT(授权)和REVOKE(撤销授权)等语句,用于控制用户对数据库对象的访问权限。</p><p>事务控制语言(Transaction Control Language,TCL):TCL用于控制数据库中的事务处理。它包括BEGIN TRANSACTION(开始事务)、COMMIT(提交事务)、ROLLBACK(回滚事务)等语句,用于确保数据库操作的原子性、一致性、隔离性和持久性。</p><p>除了上述常见的分类,SQL还可以根据具体的数据库系统和实现方式进行进一步分类,如存储过程语言(Stored Procedure Language)、游标语言(Cursor Language)等。每种类型的SQL语句都有其特定的语法和功能,开发人员可以根据需要选择合适的语句来进行数据库操作。</p></blockquote><h3 id="5、解析结果集"><a href="#5、解析结果集" class="headerlink" title="5、解析结果集"></a>5、解析结果集</h3><p>当查询找到了所需要的数据,就会将找到的数据封装到ResultSet对象,结果集ResultSet对象维护一个指向其当前数据行的游标。最初,游标位于第一行之前。next方法将光标移动到下一行,因为当ResultSet对象中没有更多的行时,它返回false,所以可以在while循环中使用它来遍历结果集。</p><p>我们通过断点可以看到,刚开始结果集指向的当前行是-1,代表是具体数据的上一行。</p><p><img src="https://img1.imgtp.com/2023/07/16/A8qkLE3d.png" alt="image-20230716162104620.png"></p><p>我们通过while循环去遍历数据,并且使用next将游标移动到下行,如果有数据,此时结果集就指向了数据行。</p><p>通过resultSet.getLong(columnIndex|columnLabel)来获取对应类型的数据,这个get属性可以使用两种方式来获取:</p><p>①、通过列数据的下标,这个下标是从1开始。</p><p>②、通过列标签,这里的列标签可以是列名也可以是别名,当sql语句中使用了<code>AS</code> 给字段取别名时,这就需要使用别名了。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> (resultSet.next()) {</span><br><span class="line"> <span class="type">long</span> <span class="variable">aLong</span> <span class="operator">=</span> resultSet.getLong(<span class="number">1</span>);</span><br><span class="line"> <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> resultSet.getString(<span class="string">"username"</span>);</span><br><span class="line"></span><br><span class="line"> System.out.println(<span class="string">"id: "</span> + aLong + <span class="string">", username: "</span> + username);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="6、关闭资源"><a href="#6、关闭资源" class="headerlink" title="6、关闭资源"></a>6、关闭资源</h3><p>当使用完之后就需要将资源进行销毁</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 6、关闭资源</span></span><br><span class="line">resultSet.close();</span><br><span class="line">statement.close();</span><br><span class="line">connection.close();</span><br></pre></td></tr></table></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>使用jdbc与数据库进行通信,简单来说就只有6个步骤,首先需要注册驱动,获取连接,接着创建statement对象,用来存放sql语句并且执行,然后将获得到的数据进行解析,最后就是要把资源进行销毁。这里只是基本的使用jdbc,像mybatis、jpa、ibatis等持久层框架,都是对jdbc进行封装,万变不离其宗,想要学习底层知识,就需要把这学基础知识给学好!</p>]]></content>
<summary type="html">本次所学习的大致内容是jdbc的概念与理解,以及如何去使用jdbc来实现Java程序和数据库进行打交道。</summary>
<category term="JDBC" scheme="https://lydandtry.github.io/categories/JDBC/"/>
<category term="JDBC" scheme="https://lydandtry.github.io/tags/JDBC/"/>
<category term="数据库" scheme="https://lydandtry.github.io/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
<category term="java" scheme="https://lydandtry.github.io/tags/java/"/>
</entry>
<entry>
<title>Java设计模式-工厂模式</title>
<link href="https://lydandtry.github.io/posts/60745/"/>
<id>https://lydandtry.github.io/posts/60745/</id>
<published>2023-06-24T23:04:41.000Z</published>
<updated>2024-09-19T03:07:44.552Z</updated>
<content type="html"><![CDATA[<h1 id="Java设计模式-工厂模式"><a href="#Java设计模式-工厂模式" class="headerlink" title="Java设计模式 -工厂模式"></a>Java设计模式 -工厂模式</h1><blockquote><p>😄 不断学习才是王道<br>🔥 继续踏上学习之路,学之分享笔记<br>👊 总有一天我也能像各位大佬一样<br>🏆 一个有梦有戏的人 @怒放吧德德<br>🌝分享学习心得,欢迎指正,大家一起学习成长!</p></blockquote><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>在大多情况下,我们都是用new去实例化对象。但是,有时候有的对象的类别有很多种,又存在着共性,就好比如汽车,有奔驰,红旗,宝马等品牌,如果是一个一个去创建类,那就需要创建很多,因此就需要用到工厂模式。</p><h2 id="1、普通工厂-Simple-Factory-模式"><a href="#1、普通工厂-Simple-Factory-模式" class="headerlink" title="1、普通工厂(Simple Factory)模式"></a>1、普通工厂(Simple Factory)模式</h2><p>在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。<br>UML如图:<br><img src="https://img1.imgtp.com/2023/06/24/9lYQUxph.jpg" alt="简单工厂模式.jpg"></p><h3 id="①、定义类"><a href="#①、定义类" class="headerlink" title="①、定义类"></a>①、定义类</h3><p>首先定义一个汽车抽象类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.entity;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 产品实体</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-21</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">ICar</span> {</span><br><span class="line"> <span class="keyword">protected</span> String name;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">ICar</span><span class="params">(String name)</span> {</span><br><span class="line"> <span class="built_in">this</span>.name = name;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> name;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setName</span><span class="params">(String name)</span> {</span><br><span class="line"> <span class="built_in">this</span>.name = name;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">contract</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">pickUp</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"提车->"</span> + name);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>定义奔驰汽车类和红旗汽车类。并且继承基类<br>奔驰:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.entity;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 奔驰汽车类</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-21</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BenzCar</span> <span class="keyword">extends</span> <span class="title class_">ICar</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">BenzCar</span><span class="params">(String name)</span> {</span><br><span class="line"> <span class="built_in">super</span>(name);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">contract</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"奔驰旗舰店和客户签订合同"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>红旗类与奔驰类似,这里就不贴冗余代码了</p><h3 id="②、定义简单的工厂类"><a href="#②、定义简单的工厂类" class="headerlink" title="②、定义简单的工厂类"></a>②、定义简单的工厂类</h3><p>工厂类:</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.simpleFactory;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.entity.BenzCar;</span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.entity.HongQiCar;</span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.entity.ICar;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.naming.NotContextException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 普通工厂模式</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-20</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CarFactory</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> ICar <span class="title function_">createCar</span><span class="params">(String carId)</span> <span class="keyword">throws</span> NotContextException {</span><br><span class="line"> <span class="keyword">switch</span> (carId) {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"1"</span>: <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">BenzCar</span>(<span class="string">"奔驰GLA"</span>);</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"2"</span>: <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">HongQiCar</span>(<span class="string">"红旗H7"</span>);</span><br><span class="line"> <span class="keyword">default</span>: <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NotContextException</span>(<span class="string">"没有该编号的汽车"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="③、实例"><a href="#③、实例" class="headerlink" title="③、实例"></a>③、实例</h3><p>测试实验</p><blockquote><p>对调用者来说,就只需要知道createCar方法,指定汽车的编号就可以实例化出对应的对象</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.entity.ICar;</span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.simpleFactory.CarFactory;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.naming.NotContextException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 测试类</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-21</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SimpleFactoryTest</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> NotContextException {</span><br><span class="line"> <span class="type">ICar</span> <span class="variable">car</span> <span class="operator">=</span> CarFactory.createCar(<span class="string">"1"</span>);</span><br><span class="line"> car.contract();</span><br><span class="line"> car.pickUp();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>运行结果:<br><img src="https://img1.imgtp.com/2023/06/24/SJmKfwrv.png" alt="截图.png"></p><h2 id="2、抽象工厂-Abstract-Factory-模式"><a href="#2、抽象工厂-Abstract-Factory-模式" class="headerlink" title="2、抽象工厂(Abstract Factory)模式"></a>2、抽象工厂(Abstract Factory)模式</h2><p>抽象工厂模式提供一个接口,不必去指定具体的产品,创建多个产品族的产品对象。对于普通的工厂来说,可以解决一类对象的创建,但是对象有时候很复杂,在有几十种对象中又分了好几种类型,只有一个工厂的话,实现起来就十分复杂,因此需要吧工厂分为很多种,就比如汽车中有奔驰、红旗等,在奔驰中又分为了A级C级等,这就可以吧奔驰,红旗创建各自的工厂,由工厂来划分是具体哪个对象。<br>UML如图:<br><img src="https://img1.imgtp.com/2023/06/24/utlVF1g1.jpg" alt="抽象工厂模式.jpg"></p><h3 id="①、定义类-1"><a href="#①、定义类-1" class="headerlink" title="①、定义类"></a>①、定义类</h3><p>采用以上的Icar抽象类,本次定义了四个类:BenzGLA、BenzGLB、HongQiH5、HongQiH7</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.car.entity.benz;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.car.entity.ICar;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 奔驰GLA</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-21</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BenzGLA</span> <span class="keyword">extends</span> <span class="title class_">ICar</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">BenzGLA</span><span class="params">(String name)</span> {</span><br><span class="line"> <span class="built_in">super</span>(name);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">contract</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"奔驰旗舰店和客户签订合同-购买->"</span> + name);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>其他类都一样,就不一个一个粘贴</p><h3 id="②、定义工厂接口"><a href="#②、定义工厂接口" class="headerlink" title="②、定义工厂接口"></a>②、定义工厂接口</h3><p>ICarFactory:<br>只提供一个创建产品的接口</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.car.factory;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.car.entity.ICar;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 汽车工厂</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-21</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">ICarFactory</span> {</span><br><span class="line"> <span class="keyword">public</span> ICar <span class="title function_">createProduct</span><span class="params">(String CarNo)</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>定义奔驰和红旗的工厂类</p><blockquote><p>两个工厂类也都是差不多,需要实现ICarFactory,并且实现具体方法,在此方法来根据需要生成对应的对象。</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.car.factory.impl;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.car.entity.ICar;</span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.car.entity.benz.BenzGLA;</span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.car.entity.benz.BenzGLB;</span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.car.factory.ICarFactory;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 奔驰汽车的工厂类</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-21</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BenzFactory</span> <span class="keyword">implements</span> <span class="title class_">ICarFactory</span> {</span><br><span class="line"> <span class="keyword">public</span> ICar <span class="title function_">createProduct</span><span class="params">(String CarNo)</span> {</span><br><span class="line"> System.out.println(<span class="string">"奔驰工厂"</span>);</span><br><span class="line"> <span class="type">ICar</span> <span class="variable">car</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="comment">// 根据id创建产品</span></span><br><span class="line"> <span class="keyword">if</span> (CarNo.equals(<span class="string">"GLA"</span>)) {</span><br><span class="line"> <span class="comment">// 生成GLA型号的奔驰汽车</span></span><br><span class="line"> car = <span class="keyword">new</span> <span class="title class_">BenzGLA</span>(CarNo);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (CarNo.equals(<span class="string">"GLB"</span>)) {</span><br><span class="line"> <span class="comment">// 生成GLB型号的奔驰汽车</span></span><br><span class="line"> car = <span class="keyword">new</span> <span class="title class_">BenzGLB</span>(CarNo);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (car != <span class="literal">null</span>) <span class="keyword">return</span> car;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>红旗的工厂类同</p></blockquote><p>汽车工厂类:</p><blockquote><p>根据一定的逻辑(本次实验根据编号)来生成对应的工厂,在由工厂去判断实例化哪个对象。</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.car.factory.impl;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.car.entity.ICar;</span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.car.factory.ICarFactory;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 汽车工厂</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-21</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CarFactory</span> <span class="keyword">implements</span> <span class="title class_">ICarFactory</span> {</span><br><span class="line"> <span class="keyword">public</span> ICar <span class="title function_">createProduct</span><span class="params">(String CarNo)</span> {</span><br><span class="line"> <span class="type">ICarFactory</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">if</span> (CarNo.equals(<span class="string">"GLA"</span>) || CarNo.equals(<span class="string">"GLB"</span>)) { <span class="comment">// 如果是奔驰的品牌</span></span><br><span class="line"> factory = <span class="keyword">new</span> <span class="title class_">BenzFactory</span>();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> factory = <span class="keyword">new</span> <span class="title class_">HongQiFactory</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (factory != <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">return</span> factory.createProduct(CarNo);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="③、实例-1"><a href="#③、实例-1" class="headerlink" title="③、实例"></a>③、实例</h3><p>测试抽象工厂模式</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.car.entity.ICar;</span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.car.factory.impl.CarFactory;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 测试类</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-21</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ProductTest</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">CarFactory</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CarFactory</span>();</span><br><span class="line"> <span class="type">ICar</span> <span class="variable">car</span> <span class="operator">=</span> factory.createProduct(<span class="string">"GLA"</span>);</span><br><span class="line"> car.contract();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>运行结果:<br><img src="https://img1.imgtp.com/2023/06/24/4ibWlCyn.png" alt="截图"></p>]]></content>
<summary type="html">在大多情况下,我们都是用new去实例化对象。但是,有时候有的对象的类别有很多种,又存在着共性,就好比如汽车,有奔驰,红旗,宝马等品牌,如果是一个一个去创建类,那就需要创建很多,因此就需要用到工厂模式。</summary>
<category term="Java设计模式" scheme="https://lydandtry.github.io/categories/Java%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
<category term="Java" scheme="https://lydandtry.github.io/tags/Java/"/>
<category term="设计模式" scheme="https://lydandtry.github.io/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
</entry>
<entry>
<title>Java设计模式-责任链模式</title>
<link href="https://lydandtry.github.io/posts/33900/"/>
<id>https://lydandtry.github.io/posts/33900/</id>
<published>2023-06-24T22:39:25.000Z</published>
<updated>2024-09-19T03:07:44.552Z</updated>
<content type="html"><![CDATA[<h1 id="Java设计模式-责任链模式"><a href="#Java设计模式-责任链模式" class="headerlink" title="Java设计模式 - 责任链模式"></a>Java设计模式 - 责任链模式</h1><blockquote><p>😄 不断学习才是王道<br>🔥 继续踏上学习之路,学之分享笔记<br>👊 总有一天我也能像各位大佬一样<br>🏆 一个有梦有戏的人 @怒放吧德德</p></blockquote><h2 id="拦截器"><a href="#拦截器" class="headerlink" title="拦截器"></a>拦截器</h2><p>拦截器可以拦截目标方法,进行一系列的操作。也可以取代代理对象的方法等功能。以下代码用JDK动态代理来实现一个拦截器的逻辑。</p><h3 id="1、接口提供"><a href="#1、接口提供" class="headerlink" title="1、接口提供"></a>1、接口提供</h3><p>首先提供方法接口和实现类作为动态代理拦截的方法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.service;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 普通的接口</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-19</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">HelloWorldService</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sayHelloWorld</span><span class="params">(String name)</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>实现类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.service.impl;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.service.HelloWorldService;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 接口实现</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-19</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloWorldServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">HelloWorldService</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sayHelloWorld</span><span class="params">(String name)</span> {</span><br><span class="line"> System.out.println(<span class="string">"Hello World! "</span> + name);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="2、定义拦截器接口Interceptor"><a href="#2、定义拦截器接口Interceptor" class="headerlink" title="2、定义拦截器接口Interceptor"></a>2、定义拦截器接口Interceptor</h3><p>这里定义了三个方法:<strong>before、around、after</strong>,都有:代理对象 <strong>proxy</strong>, 真实对象 <strong>target</strong>, 方法 <strong>method</strong>, 参数 <strong>args</strong></p><ul><li>before方法返回的是Boolean,在真实对象前调用。</li><li>返回true时,反射真实对象的方法,false时,调用around方法<br>最后执行after方法</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.interceptor;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 拦截器接口</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-17</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Interceptor</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">before</span><span class="params">(Object proxy, Object target, Method method, Object[] args)</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">around</span><span class="params">(Object proxy, Object target, Method method, Object[] args)</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">after</span><span class="params">(Object proxy, Object target, Method method, Object[] args)</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="3、定义拦截器实现类"><a href="#3、定义拦截器实现类" class="headerlink" title="3、定义拦截器实现类"></a>3、定义拦截器实现类</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.impl;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.interceptor.Interceptor;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 实现拦截接口</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-17</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyInterceptor</span> <span class="keyword">implements</span> <span class="title class_">Interceptor</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">before</span><span class="params">(Object proxy, Object target, Method method, Object[] args)</span> {</span><br><span class="line"> System.out.println(<span class="string">"反射方法前逻辑"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>; <span class="comment">// 不反射被代理对象原有方法</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">around</span><span class="params">(Object proxy, Object target, Method method, Object[] args)</span> {</span><br><span class="line"> System.out.println(<span class="string">"取代了代理对象的方法"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">after</span><span class="params">(Object proxy, Object target, Method method, Object[] args)</span> {</span><br><span class="line"> System.out.println(<span class="string">"反射方法后逻辑"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="4、采用JDK动态代理的方式使用拦截器"><a href="#4、采用JDK动态代理的方式使用拦截器" class="headerlink" title="4、采用JDK动态代理的方式使用拦截器"></a>4、采用JDK动态代理的方式使用拦截器</h3><p>构建jdk代理:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.jdk;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.interceptor.Interceptor;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.InvocationHandler;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Proxy;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: JDK动态代理使用拦截器</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-17</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">InterceptorJdkProxy</span> <span class="keyword">implements</span> <span class="title class_">InvocationHandler</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> Object target; <span class="comment">// 真实对象</span></span><br><span class="line"> <span class="keyword">private</span> <span class="type">String</span> <span class="variable">interceptorClass</span> <span class="operator">=</span> <span class="literal">null</span>; <span class="comment">// 拦截器全限定名</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">InterceptorJdkProxy</span><span class="params">(Object target, String interceptorClass)</span> {</span><br><span class="line"> <span class="built_in">this</span>.target = target;</span><br><span class="line"> <span class="built_in">this</span>.interceptorClass = interceptorClass;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 绑定委托对象并返回一个【代理占位】</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> target 真实对象</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> 代理对象</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title function_">bind</span><span class="params">(Object target, String interceptorClass)</span> {</span><br><span class="line"> <span class="keyword">return</span> Proxy.newProxyInstance(target.getClass().getClassLoader(),</span><br><span class="line"> target.getClass().getInterfaces(),</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">InterceptorJdkProxy</span>(target, interceptorClass));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> Object <span class="title function_">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable {</span><br><span class="line"> <span class="keyword">if</span> (interceptorClass == <span class="literal">null</span>) {</span><br><span class="line"> <span class="comment">// 没有设置拦截器则直接反射原有方法</span></span><br><span class="line"> <span class="keyword">return</span> method.invoke(proxy, args);</span><br><span class="line"> }</span><br><span class="line"> <span class="type">Object</span> <span class="variable">result</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="comment">// 通过反射生成拦截器</span></span><br><span class="line"> <span class="type">Interceptor</span> <span class="variable">interceptor</span> <span class="operator">=</span> (Interceptor) Class.forName(interceptorClass).newInstance();</span><br><span class="line"> <span class="comment">// 调用前置方法</span></span><br><span class="line"> <span class="keyword">if</span> (interceptor.before(proxy, target, method, args)) {</span><br><span class="line"> <span class="comment">//反射原有方法</span></span><br><span class="line"> <span class="keyword">return</span> method.invoke(target, args);</span><br><span class="line"> } <span class="keyword">else</span> { <span class="comment">// 返回false方法执行around方法</span></span><br><span class="line"> interceptor.around(proxy, target, method, args);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 调用后置方法</span></span><br><span class="line"> interceptor.after(proxy, target, method, args);</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="5、实例"><a href="#5、实例" class="headerlink" title="5、实例"></a>5、实例</h3><p>测试:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.jdk.InterceptorJdkProxy;</span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.service.HelloWorldService;</span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.service.impl.HelloWorldServiceImpl;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 测试类</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-19</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">InterceptorTest</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">HelloWorldService</span> <span class="variable">proxy</span> <span class="operator">=</span> (HelloWorldService) InterceptorJdkProxy.bind(<span class="keyword">new</span> <span class="title class_">HelloWorldServiceImpl</span>(), <span class="string">"com.lyd.demo.impl.MyInterceptor"</span>);</span><br><span class="line"> proxy.sayHelloWorld(<span class="string">"lyd"</span>);</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>结果:<br><img src="https://img1.imgtp.com/2023/06/24/68dCQLuz.png" alt="结果1"></p><h2 id="责任链模式"><a href="#责任链模式" class="headerlink" title="责任链模式"></a>责任链模式</h2><p>当一个对象在一条链上被多个拦截器拦截处理,当然也可以不拦截处理,这种的设计模式就是责任链模式。可以在这被拦截的期间进行加工处理。就好比如,我们申请请假,需要经过项目经理,再到部门经理,最后到人事。在这期间,经理审批时候可以修改请假天数。本次实验用以上部分代码。</p><h3 id="1、定义拦截器"><a href="#1、定义拦截器" class="headerlink" title="1、定义拦截器"></a>1、定义拦截器</h3><p>这里定义三个拦截器分别是三个类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.impl;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.interceptor.Interceptor;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 责任链 - 拦截器1</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-20</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Interceptor1</span> <span class="keyword">implements</span> <span class="title class_">Interceptor</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">before</span><span class="params">(Object proxy, Object target, Method method, Object[] args)</span> {</span><br><span class="line"> System.out.println(<span class="string">"拦截器1的before"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">around</span><span class="params">(Object proxy, Object target, Method method, Object[] args)</span> {</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">after</span><span class="params">(Object proxy, Object target, Method method, Object[] args)</span> {</span><br><span class="line"> System.out.println(<span class="string">"拦截器1的after"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>拦截器2,3也是如此就不粘贴代码了。</p><h3 id="2、责任链上使用拦截器实例"><a href="#2、责任链上使用拦截器实例" class="headerlink" title="2、责任链上使用拦截器实例"></a>2、责任链上使用拦截器实例</h3><p>测试:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.jdk.InterceptorJdkProxy;</span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.service.HelloWorldService;</span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.service.impl.HelloWorldServiceImpl;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 责任链模式测试</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-20</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ResponsibilityChainTest</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">HelloWorldService</span> <span class="variable">proxy1</span> <span class="operator">=</span> (HelloWorldService) InterceptorJdkProxy.bind(<span class="keyword">new</span> <span class="title class_">HelloWorldServiceImpl</span>(), <span class="string">"com.lyd.demo.impl.Interceptor1"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="type">HelloWorldService</span> <span class="variable">proxy2</span> <span class="operator">=</span> (HelloWorldService) InterceptorJdkProxy.bind(proxy1, <span class="string">"com.lyd.demo.impl.Interceptor2"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="type">HelloWorldService</span> <span class="variable">proxy3</span> <span class="operator">=</span> (HelloWorldService) InterceptorJdkProxy.bind(proxy2, <span class="string">"com.lyd.demo.impl.Interceptor3"</span>);</span><br><span class="line"></span><br><span class="line"> proxy3.sayHelloWorld(<span class="string">"怒放吧德德"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>运行结果:<br><img src="https://img1.imgtp.com/2023/06/24/KOGXMFqs.png" alt="结果2"></p>]]></content>
<summary type="html">拦截器可以拦截目标方法,进行一系列的操作。也可以取代代理对象的方法等功能。以下代码用JDK动态代理来实现一个拦截器的逻辑。</summary>
<category term="Java设计模式" scheme="https://lydandtry.github.io/categories/Java%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
<category term="Java" scheme="https://lydandtry.github.io/tags/Java/"/>
<category term="设计模式" scheme="https://lydandtry.github.io/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
</entry>
<entry>
<title>Java设计模式-动态代理</title>
<link href="https://lydandtry.github.io/posts/27007/"/>
<id>https://lydandtry.github.io/posts/27007/</id>
<published>2023-06-24T21:58:34.000Z</published>
<updated>2024-09-19T03:07:44.552Z</updated>
<content type="html"><![CDATA[<h1 id="【设计模式】Java设计模式-动态代理"><a href="#【设计模式】Java设计模式-动态代理" class="headerlink" title="【设计模式】Java设计模式 - 动态代理"></a>【设计模式】Java设计模式 - 动态代理</h1><blockquote><p>😄 不断学习才是王道<br>🔥 继续踏上学习之路,学之分享笔记<br>👊 总有一天我也能像各位大佬一样<br>🏆 一个有梦有戏的人 @怒放吧德德<br>🌝最近工作比较忙,没啥时间学习</p></blockquote><h2 id="1、简介"><a href="#1、简介" class="headerlink" title="1、简介"></a>1、简介</h2><p>动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。动态代理就需要建立真实对象和代理对象关系,再去实现代理逻辑方法。<br><strong>Java</strong>中实现动态代理有许多方法,<strong>JDK、CGLIB、Javassist、ASM</strong>,常用的是<strong>JDK</strong>和<strong>CGLIB</strong>,在<strong>spring</strong>中就是使用了这两种,然而<strong>mybatis</strong>还是用了<strong>Javassist</strong>。</p><h2 id="2、JDK动态代理"><a href="#2、JDK动态代理" class="headerlink" title="2、JDK动态代理"></a>2、JDK动态代理</h2><p>提供简单的接口类以及其实现类,在通过建立真是对象与代理对象的关系,并且实现代理逻辑。</p><h3 id="1-、准备接口类"><a href="#1-、准备接口类" class="headerlink" title="(1)、准备接口类"></a>(1)、准备接口类</h3><p>先提供接口类:<br>HelloWorldService:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.service;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 普通的接口</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-17</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">HelloWorldService</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sayHelloWorld</span><span class="params">()</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>HelloWorldServiceImpl:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.service.impl;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.service.HelloWorldService;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 接口实现类</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-17</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloWorldServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">HelloWorldService</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sayHelloWorld</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"Hello World!"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="2-、jdk动态代理"><a href="#2-、jdk动态代理" class="headerlink" title="(2)、jdk动态代理"></a>(2)、jdk动态代理</h3><p>在JDK动态代理中,通过bind将真实对象和代理对象绑定起来,实现代理逻辑就要去实现<strong>java.lang.reflect.InvocationHandler</strong>接口,并且去实现invoke方法</p><blockquote><p>①、首先需要声明 bind 方法去建立真实对象与代理对象的关系,把本类中的target保存真实对象。在通过Proxy的newProxyInstance方法来建立并生成对象,target.getClass().getClassLoader():target本身的类加载器,target.getClass().getInterfaces():把生成的动态代理对象下挂在接口中,this:当前对象,是定义实现方法逻辑的代理类。<br>②、实现InvocationHandler类中的invoke方法,可以实现代理逻辑,当我们使用了代理对象调度方法后就会进入到invoke方法中。</p></blockquote><p>代码如下:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.jdk;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.InvocationHandler;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Proxy;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 动态代理绑定和代理逻辑实现</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-17</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JdkProxyExample</span> <span class="keyword">implements</span> <span class="title class_">InvocationHandler</span> {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 真实对象</span></span><br><span class="line"> <span class="keyword">private</span> <span class="type">Object</span> <span class="variable">target</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 建立代理对象和真实对象的代理关系,并且返回代理逻辑实现</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> target 真实对象</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> 代理对象</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> Object <span class="title function_">bind</span><span class="params">(Object target)</span> {</span><br><span class="line"> <span class="built_in">this</span>.target = target; <span class="comment">// 绑定对象</span></span><br><span class="line"> <span class="keyword">return</span> Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), <span class="built_in">this</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 代理方法逻辑</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> proxy 代理对象</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> method 当前调度方法</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> args 当前方法的参数</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> 代理结果返回</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> Throwable 异常</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> Object <span class="title function_">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable {</span><br><span class="line"> System.out.println(<span class="string">"进入代理逻辑方法"</span>);</span><br><span class="line"> System.out.println(<span class="string">"在调度真实对象之前的服务"</span>);</span><br><span class="line"> <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> method.invoke(target, args); <span class="comment">// 相当于调用了sayHelloWorld的方法</span></span><br><span class="line"> System.out.println(<span class="string">"在调度真实对象之后的服务"</span>);</span><br><span class="line"> <span class="keyword">return</span> obj;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="3-、实例"><a href="#3-、实例" class="headerlink" title="(3)、实例"></a>(3)、实例</h3><p>测试jdk动态代理<br>通过 <strong>HelloWorldService proxy = (HelloWorldService) jdkProxyExample.bind(new HelloWorldServiceImpl());</strong> 去代理对象,然后就是使用proxy去点接口里面的方法了。<br>代码如下:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.jdk.JdkProxyExample;</span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.service.HelloWorldService;</span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.service.impl.HelloWorldServiceImpl;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 测试jdk动态代理</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-17</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">jdkProxyText</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">JdkProxyExample</span> <span class="variable">jdkProxyExample</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JdkProxyExample</span>();</span><br><span class="line"> <span class="comment">// 绑定关系,因为挂载带接口下,因此声明一个代理对象</span></span><br><span class="line"> <span class="type">HelloWorldService</span> <span class="variable">proxy</span> <span class="operator">=</span> (HelloWorldService) jdkProxyExample.bind(<span class="keyword">new</span> <span class="title class_">HelloWorldServiceImpl</span>()); <span class="comment">// 对象是new 实现类</span></span><br><span class="line"> <span class="comment">// 调用方法</span></span><br><span class="line"> proxy.sayHelloWorld();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>实验结果<br><img src="https://img-blog.csdnimg.cn/13501d3d09a649d7bf147badf00c75b2.png" alt="在这里插入图片描述"><br>可以带入参数:以下是带入参数的例子</p><blockquote><p>在接口中的方法添加参数</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sayHelloWorld</span><span class="params">(String name)</span>;</span><br></pre></td></tr></table></figure><blockquote><p>在实现类的实现方法中打印出来</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">System.out.println(<span class="string">"Hello World! "</span> + name);</span><br></pre></td></tr></table></figure><p>调用方法的时候添加参数</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 调用方法</span></span><br><span class="line">proxy.sayHelloWorld(<span class="string">"lyd"</span>);</span><br></pre></td></tr></table></figure><p>代理模式十分重要,要理解里面的逻辑,可以通过debug打断点去一步一步查看。</p><h2 id="3、CGLIB-动态代理"><a href="#3、CGLIB-动态代理" class="headerlink" title="3、CGLIB 动态代理"></a>3、CGLIB 动态代理</h2><p>JDK动态代理需要接口才能完成,而如果不提供接口,只有实现的方法类,可以使用三方插件CGLIB来动态代理,采用这个动态代理技术,需要引入三方jar包,可以搭建maven项目,引入CGLIB <strong>jar包</strong> ,通过maven官网搜索添加,亦可以直接下载jar文件。</p><h3 id="1-、加入CGLIB依赖"><a href="#1-、加入CGLIB依赖" class="headerlink" title="(1)、加入CGLIB依赖"></a>(1)、加入CGLIB依赖</h3><p>maven引入依赖:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"><!-- https://mvnrepository.com/artifact/cglib/cglib --></span></span><br><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>cglib<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>cglib<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>3.3.0<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br></pre></td></tr></table></figure><h3 id="2-、准备实现类"><a href="#2-、准备实现类" class="headerlink" title="(2)、准备实现类"></a>(2)、准备实现类</h3><p>准备一个实现类,cglib不需要接口,只要实现就可以:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.impl;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 实现类</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-17</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloWorldServiceImpl</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sayHelloWorld</span><span class="params">(String name)</span> {</span><br><span class="line"> System.out.println(<span class="string">"Hello World! "</span> + name);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="3-、代理类"><a href="#3-、代理类" class="headerlink" title="(3)、代理类"></a>(3)、代理类</h3><p>代理类需要<strong>MethodInterceptor</strong>去实现方法</p><blockquote><p>这里使用了增强者enhancer,通过设置超类和使用setCallback方法设置代理类,CGLIB是通过invokeSuper方法代理逻辑的。</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.cglib;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> net.sf.cglib.proxy.Enhancer;</span><br><span class="line"><span class="keyword">import</span> net.sf.cglib.proxy.MethodInterceptor;</span><br><span class="line"><span class="keyword">import</span> net.sf.cglib.proxy.MethodProxy;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>:</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-17</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CglibProxyExample</span> <span class="keyword">implements</span> <span class="title class_">MethodInterceptor</span> {</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 生成CGLIB对象</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> cls -----对象类</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> Class的CGLIB代理对象</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> Object <span class="title function_">getProxy</span><span class="params">(Class cls)</span> {</span><br><span class="line"> <span class="comment">//CGLIB的增强类对象</span></span><br><span class="line"> <span class="type">Enhancer</span> <span class="variable">enhancer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Enhancer</span>();</span><br><span class="line"> <span class="comment">//设置增强对象</span></span><br><span class="line"> enhancer.setSuperclass(cls);</span><br><span class="line"> <span class="comment">//定义代理逻辑对象为当前对象,要求对象实现MethodInterceptor方法</span></span><br><span class="line"> enhancer.setCallback(<span class="built_in">this</span>);</span><br><span class="line"> <span class="comment">//生成返回代理对象</span></span><br><span class="line"> <span class="keyword">return</span> enhancer.create();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 代理逻辑方法</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> proxy 代理对象</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> method 方法</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> args 参数</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> methodProxy 方法代理</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> 代理逻辑返回</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> Throwable 异常处理</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> Object <span class="title function_">intercept</span><span class="params">(Object proxy, Method method, Object[] args, MethodProxy methodProxy)</span> <span class="keyword">throws</span> Throwable {</span><br><span class="line"> System.out.println(<span class="string">"调用对象之前"</span>);</span><br><span class="line"> <span class="comment">//使用CGLIB反射真实对象的方法</span></span><br><span class="line"> <span class="type">Object</span> <span class="variable">result</span> <span class="operator">=</span> methodProxy.invokeSuper(proxy, args);</span><br><span class="line"> System.out.println(<span class="string">"调用对象之后"</span>);</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="4-、实例"><a href="#4-、实例" class="headerlink" title="(4)、实例"></a>(4)、实例</h3><p>测试:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.cglib.CglibProxyExample;</span><br><span class="line"><span class="keyword">import</span> com.lyd.demo.impl.HelloWorldServiceImpl;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 测试CGLIB</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-17</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CGLIBProxyTest</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">CglibProxyExample</span> <span class="variable">cglibProxyExample</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CglibProxyExample</span>();</span><br><span class="line"> <span class="type">HelloWorldServiceImpl</span> <span class="variable">proxy</span> <span class="operator">=</span> (HelloWorldServiceImpl) cglibProxyExample.getProxy(HelloWorldServiceImpl.class); <span class="comment">// 获取对象,可以不需要接口类</span></span><br><span class="line"> proxy.sayHelloWorld(<span class="string">"lyd"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><br>结果<br><img src="https://img-blog.csdnimg.cn/750d26e40ae0495d8890d2facf5f41c7.png" alt="在这里插入图片描述"></p>]]></content>
<summary type="html">动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。动态代理就需要建立真实对象和代理对象关系,再去实现代理逻辑方法。</summary>
<category term="Java设计模式" scheme="https://lydandtry.github.io/categories/Java%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
<category term="Java" scheme="https://lydandtry.github.io/tags/Java/"/>
<category term="设计模式" scheme="https://lydandtry.github.io/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
</entry>
<entry>
<title>Java设计模式-反射机制</title>
<link href="https://lydandtry.github.io/posts/25987/"/>
<id>https://lydandtry.github.io/posts/25987/</id>
<published>2023-06-24T15:02:06.000Z</published>
<updated>2024-09-19T03:07:44.552Z</updated>
<content type="html"><![CDATA[<h1 id="Java设计模式-反射机制"><a href="#Java设计模式-反射机制" class="headerlink" title="Java设计模式 - 反射机制"></a>Java设计模式 - 反射机制</h1><blockquote><p>😄 不断学习才是王道<br>🔥 继续踏上学习之路,学之分享笔记<br>👊 总有一天我也能像各位大佬一样<br>🏆 关注我的CSDN:<a href="https://blog.csdn.net/qq_43843951?spm=1011.2415.3001.5343">一个有梦有戏的人</a><br>👊 打算连载Java设计模式,记录自己的学习心得,分享学习经验。</p></blockquote><h2 id="Java反射技术"><a href="#Java反射技术" class="headerlink" title="Java反射技术"></a>Java反射技术</h2><blockquote><p>反射的应用机制广泛,能够配置类的全限定名(包名 + 类型名)、方法和参数,完成对象的初始化,并且可以大大增强Java的可配置性,SpringIOC的基本原理就是如此。</p><h3 id="1、通过反射构建对象"><a href="#1、通过反射构建对象" class="headerlink" title="1、通过反射构建对象"></a>1、通过反射构建对象</h3><p>反射构建对象可以是有参和无参。<br><strong>无参:</strong><br>先定义<strong>ReflectDemo</strong>类,并且有个无参数的方法。</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReflectDemo</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sayHello</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"反射机制!!!"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>通过反射去构建对象</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 通过反射去构建</span></span><br><span class="line"><span class="keyword">public</span> ReflectDemo <span class="title function_">getInstance</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">ReflectDemo</span> <span class="variable">object</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> object = (ReflectDemo) Class.forName(<span class="string">"com.lyd.demo.reflect.ReflectDemo"</span>).newInstance();</span><br><span class="line"> } <span class="keyword">catch</span> (IllegalAccessException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (InstantiationException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (ClassNotFoundException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> object;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>其实就是通过<strong>Class</strong>的<strong>forName()</strong>去找到这个类,在通过<strong>newInstance()</strong>方法创建实例。<br>给类加载器去注册了一个类<strong>ReflectDemo</strong>的全限定名</p><blockquote><p>object = (ReflectDemo) Class.forName(“com.lyd.demo.reflect.ReflectDemo”).newInstance();</p></blockquote><p>含参:<br>先定义<strong>ReflectParamDemo</strong>类,并且有个含参数的方法以及其构造方法。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReflectParamDemo</span> {</span><br><span class="line"> <span class="keyword">private</span> String name;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> age;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">ReflectParamDemo</span><span class="params">(String name, <span class="type">int</span> age)</span> {</span><br><span class="line"> <span class="built_in">this</span>.name = name;</span><br><span class="line"> <span class="built_in">this</span>.age = age;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sayHello</span><span class="params">(String name, <span class="type">int</span> age)</span> {</span><br><span class="line"> System.out.println(<span class="string">"名字: "</span> + name + <span class="string">" 年龄: "</span> + age);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>同样,根据Class.forName去创建实例,但是需要注意的是,需要加上参数。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 通过反射去构建</span></span><br><span class="line"><span class="keyword">public</span> ReflectParamDemo <span class="title function_">getInstance</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">ReflectParamDemo</span> <span class="variable">object</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> object = (ReflectParamDemo) Class.forName(<span class="string">"com.lyd.demo.reflect.ReflectParamDemo"</span>)</span><br><span class="line"> .getConstructor(String.class, <span class="type">int</span>.class)</span><br><span class="line"> .newInstance(<span class="string">"李四"</span>, <span class="number">18</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InstantiationException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (IllegalAccessException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (InvocationTargetException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (NoSuchMethodException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (ClassNotFoundException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> object;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>getConstructor(),带入参数类型</p><blockquote><p>object = (ReflectParamDemo) Class.forName(“com.lyd.demo.reflect.ReflectParamDemo”)<br>.getConstructor(String.class, int.class)<br>.newInstance(“李四”, 18);</p></blockquote><h3 id="2、反射方法"><a href="#2、反射方法" class="headerlink" title="2、反射方法"></a>2、反射方法</h3><p>无参:<br>暂时先演示反射方法,所以用<strong>new</strong>的方法来创建实例<strong>target</strong>,具体的代码请看实例, 通过<strong>ReflectDemo.class.getMethod(“sayHello”);</strong>可以获取到类中的方法,并且通过invoke<strong>加粗样式</strong>方法完成。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//反射方法</span></span><br><span class="line"><span class="keyword">public</span> Object <span class="title function_">reflectMethod</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">Object</span> <span class="variable">retuenObj</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="type">ReflectDemo</span> <span class="variable">target</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReflectDemo</span>();</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="type">Method</span> <span class="variable">method</span> <span class="operator">=</span> ReflectDemo.class.getMethod(<span class="string">"sayHello"</span>);</span><br><span class="line"> retuenObj = method.invoke(target);</span><br><span class="line"> } <span class="keyword">catch</span> (NoSuchMethodException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (IllegalAccessException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (InvocationTargetException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> retuenObj;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>target: 指定对象<br>invoke(target):调用哪个对象中的方法,如果是含参的,可以在后面添加参数,可以是多个参数</p></blockquote><h3 id="3、实例:"><a href="#3、实例:" class="headerlink" title="3、实例:"></a>3、实例:</h3><p>综合反射对象与方法<br>无参:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//实例</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title function_">reflect</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">ReflectDemo</span> <span class="variable">object</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> object = (ReflectDemo) Class.forName(<span class="string">"com.lyd.demo.reflect.ReflectDemo"</span>).newInstance();</span><br><span class="line"> <span class="type">Method</span> <span class="variable">method</span> <span class="operator">=</span> ReflectDemo.class.getMethod(<span class="string">"sayHello"</span>);</span><br><span class="line"> method.invoke(object);</span><br><span class="line"> } <span class="keyword">catch</span> (InstantiationException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (IllegalAccessException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (ClassNotFoundException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (NoSuchMethodException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (InvocationTargetException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> object;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>含参:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//实例</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title function_">reflect</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">ReflectParamDemo</span> <span class="variable">object</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> object = (ReflectParamDemo) Class.forName(<span class="string">"com.lyd.demo.reflect.ReflectParamDemo"</span>)</span><br><span class="line"> .getConstructor(String.class, <span class="type">int</span>.class)</span><br><span class="line"> .newInstance(<span class="string">"李四"</span>, <span class="number">18</span>);</span><br><span class="line"> <span class="type">Method</span> <span class="variable">method</span> <span class="operator">=</span> ReflectParamDemo.class.getMethod(<span class="string">"sayHello"</span>, String.class, <span class="type">int</span>.class);</span><br><span class="line"> method.invoke(object, <span class="string">"李四"</span>, <span class="number">18</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InstantiationException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (IllegalAccessException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (ClassNotFoundException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (NoSuchMethodException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (InvocationTargetException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> object;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="总体代码"><a href="#总体代码" class="headerlink" title="总体代码"></a>总体代码</h2><p>ReflectDemo:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.reflect;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.InvocationTargetException;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 反射机制-无参数</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-14</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReflectDemo</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sayHello</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"反射机制!!!"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 通过反射去构建</span></span><br><span class="line"> <span class="keyword">public</span> ReflectDemo <span class="title function_">getInstance</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">ReflectDemo</span> <span class="variable">object</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> object = (ReflectDemo) Class.forName(<span class="string">"com.lyd.demo.reflect.ReflectDemo"</span>).newInstance();</span><br><span class="line"> } <span class="keyword">catch</span> (IllegalAccessException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (InstantiationException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (ClassNotFoundException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> object;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//反射方法</span></span><br><span class="line"> <span class="keyword">public</span> Object <span class="title function_">reflectMethod</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">Object</span> <span class="variable">retuenObj</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="type">ReflectDemo</span> <span class="variable">target</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReflectDemo</span>();</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="type">Method</span> <span class="variable">method</span> <span class="operator">=</span> ReflectDemo.class.getMethod(<span class="string">"sayHello"</span>);</span><br><span class="line"> retuenObj = method.invoke(target);</span><br><span class="line"> } <span class="keyword">catch</span> (NoSuchMethodException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (IllegalAccessException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (InvocationTargetException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> retuenObj;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//实例</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title function_">reflect</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">ReflectDemo</span> <span class="variable">object</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> object = (ReflectDemo) Class.forName(<span class="string">"com.lyd.demo.reflect.ReflectDemo"</span>).newInstance();</span><br><span class="line"> <span class="type">Method</span> <span class="variable">method</span> <span class="operator">=</span> ReflectDemo.class.getMethod(<span class="string">"sayHello"</span>);</span><br><span class="line"> method.invoke(object);</span><br><span class="line"> } <span class="keyword">catch</span> (InstantiationException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (IllegalAccessException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (ClassNotFoundException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (NoSuchMethodException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (InvocationTargetException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> object;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>ReflectParamDemo:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.reflect;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.InvocationTargetException;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 反射机制-带参数</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-14</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReflectParamDemo</span> {</span><br><span class="line"> <span class="keyword">private</span> String name;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> age;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">ReflectParamDemo</span><span class="params">(String name, <span class="type">int</span> age)</span> {</span><br><span class="line"> <span class="built_in">this</span>.name = name;</span><br><span class="line"> <span class="built_in">this</span>.age = age;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sayHello</span><span class="params">(String name, <span class="type">int</span> age)</span> {</span><br><span class="line"> System.out.println(<span class="string">"名字: "</span> + name + <span class="string">" 年龄: "</span> + age);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 通过反射去构建</span></span><br><span class="line"> <span class="keyword">public</span> ReflectParamDemo <span class="title function_">getInstance</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">ReflectParamDemo</span> <span class="variable">object</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> object = (ReflectParamDemo) Class.forName(<span class="string">"com.lyd.demo.reflect.ReflectParamDemo"</span>)</span><br><span class="line"> .getConstructor(String.class, <span class="type">int</span>.class)</span><br><span class="line"> .newInstance(<span class="string">"李四"</span>, <span class="number">18</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InstantiationException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (IllegalAccessException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (InvocationTargetException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (NoSuchMethodException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (ClassNotFoundException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> object;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//实例</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title function_">reflect</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">ReflectParamDemo</span> <span class="variable">object</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> object = (ReflectParamDemo) Class.forName(<span class="string">"com.lyd.demo.reflect.ReflectParamDemo"</span>)</span><br><span class="line"> .getConstructor(String.class, <span class="type">int</span>.class)</span><br><span class="line"> .newInstance(<span class="string">"李四"</span>, <span class="number">18</span>);</span><br><span class="line"> <span class="type">Method</span> <span class="variable">method</span> <span class="operator">=</span> ReflectParamDemo.class.getMethod(<span class="string">"sayHello"</span>, String.class, <span class="type">int</span>.class);</span><br><span class="line"> method.invoke(object, <span class="string">"李四"</span>, <span class="number">18</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InstantiationException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (IllegalAccessException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (ClassNotFoundException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (NoSuchMethodException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">catch</span> (InvocationTargetException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> object;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>测试:ReflectTest</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lyd.demo.reflect;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 反射机制测试</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022-08-14</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReflectTest</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> ReflectDemo.reflect();</span><br><span class="line"> ReflectParamDemo.reflect();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>结果:<br><img src="https://s2.loli.net/2023/06/24/F39akm5z4SctlDY.png" alt="截图.png"></p>]]></content>
<summary type="html">反射的应用机制广泛,能够配置类的全限定名(包名 + 类型名)、方法和参数,完成对象的初始化,并且可以大大增强Java的可配置性,SpringIOC的基本原理就是如此。</summary>
<category term="Java设计模式" scheme="https://lydandtry.github.io/categories/Java%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
<category term="Java" scheme="https://lydandtry.github.io/tags/Java/"/>
<category term="设计模式" scheme="https://lydandtry.github.io/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
</entry>
<entry>
<title>锁的机制与底层优化原理</title>
<link href="https://lydandtry.github.io/posts/53870/"/>
<id>https://lydandtry.github.io/posts/53870/</id>
<published>2023-06-19T20:13:33.000Z</published>
<updated>2024-09-19T03:07:44.552Z</updated>
<content type="html"><![CDATA[<h1 id="【多线程与高并发】-锁的机制与底层优化原理"><a href="#【多线程与高并发】-锁的机制与底层优化原理" class="headerlink" title="【多线程与高并发】- 锁的机制与底层优化原理"></a>【多线程与高并发】- 锁的机制与底层优化原理</h1><p><img src="https://s2.loli.net/2023/06/21/8OdFqoz1WukpYaS.jpg" alt="锁的机制与底层优化原理.jpg"></p><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>最近经常研究一些关于线程并发的问题,再开发中也实实在在遇到过许多的并发问题,之前所学的是如何解决这些问题,然而接下来就得理解一下底层原理。</p><h2 id="简单例子"><a href="#简单例子" class="headerlink" title="简单例子"></a>简单例子</h2><p>首先用一个简单的例子来进行对锁的开篇认知。</p><p>如下代码,我们定义一个类,在这个类中提供了一个自增的方法。我们通过多线程的方式去执行自增,并且主线程也加入进行自增,最后输出这个值。这段代码都知道在自增的时候会出现并发问题,我们在通过加锁,控制对互斥资源的访问,最后就能得到期望的值。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Number</span> {</span><br><span class="line"> <span class="type">int</span> <span class="variable">num</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getNum</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> num;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">autoAccretion</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">synchronized</span>(<span class="built_in">this</span>) {</span><br><span class="line"> num++;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestNum</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException {</span><br><span class="line"> <span class="type">Number</span> <span class="variable">number</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Number</span>();</span><br><span class="line"> <span class="type">long</span> <span class="variable">startTime</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line"> <span class="type">Thread</span> <span class="variable">thread</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i < <span class="number">10000000</span>; i++) {</span><br><span class="line"> number.autoAccretion(); <span class="comment">// 自增</span></span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> thread.start();</span><br><span class="line"> <span class="comment">// 主线程也执行</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i < <span class="number">10000000</span>; i++) {</span><br><span class="line"> number.autoAccretion();</span><br><span class="line"> }</span><br><span class="line"> thread.join();</span><br><span class="line"> <span class="type">long</span> <span class="variable">endTime</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line"> System.out.println(String.format(<span class="string">"%sms"</span>, endTime - startTime));</span><br><span class="line"> System.out.println(number.num);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>我们通过执行会发现,这个值并不是我们期望所得到的,这是因为这里面出现了并发的问题。</p><p><img src="https://s2.loli.net/2023/06/21/Xwq4smxauUvz1ZV.png" alt="image-20230603143159955.png"></p><p>当然,我们都知道要想解决这个并发问题,只需要在调用对象的方法上加上<code>synchronized</code> 就行。</p><h2 id="锁的机制"><a href="#锁的机制" class="headerlink" title="锁的机制"></a>锁的机制</h2><p>通过以上代码,我们使用synchronized锁来控制对num++的执行,当多个线程进来的时候,只有一个锁能拿到synchronized这把锁。</p><h3 id="1、jdk1-6之前"><a href="#1、jdk1-6之前" class="headerlink" title="1、jdk1.6之前"></a>1、jdk1.6之前</h3><p>在jdk1.6以前,真正加锁的对象是synchronized内部的monitor对象[1];那么,如果拿不到synchronized锁的线程最后会是怎样的呢?他会放到一个队列中(即重量级锁),直到锁被释放后才能让下个线程拿到锁,这是jdk1.6以前的做法,如果一直不释放锁,那么就会导致这些等待线程一直处于等待,很明显这样会导致性能的问题。</p><p>在来说一下重量级锁,在底层可能出现线程阻塞,上下文切换,等待得到锁的线程将锁释放掉,通过操作系统对线程的调度,将阻塞态的线程唤醒。操作系统实现线程的切换还需要从用户态切换到核心态,成本非常高。</p><blockquote><p>[1]在操作系统和并发编程领域中,Monitor(监视器)是一种同步机制,用于控制对共享资源的访问。它可以用于确保在任何时刻只有一个线程能够进入临界区(Critical Section)并执行相关操作,从而实现线程安全。</p></blockquote><p><img src="https://s2.loli.net/2023/06/21/nXSZyt19IWE8khG.png" alt="线程锁1.png"></p><h3 id="2、CAS机制"><a href="#2、CAS机制" class="headerlink" title="2、CAS机制"></a>2、CAS机制</h3><p>CAS(Compare and Swap)简单说就是比较并交换,它是一种并发编程中常用的原子操作,用于实现无锁的线程安全操作。它通常用于解决多个线程同时对同一个共享变量进行修改的竞争问题。我们通常将cas称为无锁、自旋锁、乐观锁以及轻量级锁。</p><blockquote><p>CAS操作包含三个操作数:内存位置(或称为期望值),当前值和新值。CAS操作会比较内存位置的当前值与期望值是否相等,如果相等,则将内存位置的值更新为新值;如果不相等,则不进行任何操作。CAS操作是原子的,即在执行过程中不会被其他线程中断。它通过比较当前值和期望值来确定内存位置是否被修改,从而避免了传统的锁机制带来的竞争和阻塞。</p></blockquote><p>CAS的大致流程如下:</p><p> ①获取内存中的原始值,即备份数据。</p><p> ②进行比较,将当前值与期望值进行比对看是否相等。</p><p> ③如果相等,就将当前值覆盖旧的值,反之通过循环,重复操作,直到获得到对的值。</p><p>我们最常见的AtomicInteger类,他就是属于原子性操作的,它可以在并发环境下进行原子操作,确保对整数的操作是线程安全的。如下代码,我们可以通过这个类的自增方法来替换用synchronized锁包围的自增运算。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">autoAccretion</span><span class="params">()</span> {</span><br><span class="line"> <span class="comment">// synchronized (this) {</span></span><br><span class="line"> <span class="comment">// num++;</span></span><br><span class="line"> <span class="comment">// }</span></span><br><span class="line"> atomicInteger.incrementAndGet();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>我们可以看它的底层代码,通过unsafe类调用自增方法,实际底层原理也是进行比较交换的规则来保证原子性。会先获取内部原始的值,在将这个值自增1,在进行比较,如果当前值等于期望值,则自动将值设置为给定的更新值。但是,如果比较不相等,可能是在获取原始值之后做自增的时候,原始值已经被其他线程给操作成功覆盖了,则这个新的值是错误,需要刷新备份数据,再去循环尝试,直到得到对的数据才会去刷新旧值。大量的线程过来执行这个compareAndSet方法,如果在执行的时候没有被其他线程执行,那就能够将新的值将旧的值替换掉,就算说是失败了,能够通过循环继续执行,在多线程的执行能够确保数据的正确性,至于线程的先后执行也只是看运气。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="type">int</span> <span class="title function_">incrementAndGet</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> unsafe.getAndAddInt(<span class="built_in">this</span>, valueOffset, <span class="number">1</span>) + <span class="number">1</span>; </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//unsafe.class</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="type">int</span> <span class="title function_">getAndAddInt</span><span class="params">(Object var1, <span class="type">long</span> var2, <span class="type">int</span> var4)</span> {</span><br><span class="line"> <span class="type">int</span> var5;</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> var5 = <span class="built_in">this</span>.getIntVolatile(var1, var2);</span><br><span class="line"> } <span class="keyword">while</span>(!<span class="built_in">this</span>.compareAndSwapInt(var1, var2, var5, var5 + var4)); <span class="comment">// 最后的底层原理通过循环自备份,自增与比较</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> var5;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>我们可以看到内部代码是看不到的,它底层是通过c++编写的。Java中的CAS操作都是通过sun包下Unsafe类实现,而Unsafe类中的方法都是native方法,表示该方法的实现是由外部的本地代码,这里我下载了lookaside_java-1.8.0-openjdk的源码(可以从github上拉取),可以从host底层来看源码。</p><p><img src="https://s2.loli.net/2023/06/21/tV47NXn3DfHAEK8.png" alt="image-20230612205419845.png"></p><p>就AtomicInteger内的compareAndSwapInt方法,我们通过jdk1.8的 <code>hotspot/src/share/vm/prims/unsafe.cpp</code> 下的源码可以看到底层由c++编写。它使用原子比较和交换操作来比较和替换指定内存地址上的整数值,并返回比较结果。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">UNSAFE_ENTRY</span>(jboolean, <span class="built_in">Unsafe_CompareAndSwapInt</span>(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))</span><br><span class="line"> <span class="built_in">UnsafeWrapper</span>(<span class="string">"Unsafe_CompareAndSwapInt"</span>);</span><br><span class="line"> <span class="comment">// We are about to write to this entry so check to see if we need to copy it.</span></span><br><span class="line"> <span class="comment">// 执行了一个写屏障操作(write barrier),用于保证在修改对象之前进行必要的处理。</span></span><br><span class="line"> <span class="comment">// JNIHandles::resolve(obj)将obj从JNI句柄解析为Java对象,并使用oopDesc::bs()执行写屏障操作。</span></span><br><span class="line"> oop p = oopDesc::<span class="built_in">bs</span>()-><span class="built_in">write_barrier</span>(JNIHandles::<span class="built_in">resolve</span>(obj));</span><br><span class="line"> jint* addr = (jint *) <span class="built_in">index_oop_from_field_offset_long</span>(p, offset);<span class="comment">// 计算出要进行原子比较和交换操作的内存地址</span></span><br><span class="line"> <span class="keyword">return</span> (jint)(Atomic::<span class="built_in">cmpxchg</span>(x, addr, e)) == e; <span class="comment">// 核心代码 执行原子的比较和交换操作</span></span><br><span class="line">UNSAFE_END</span><br></pre></td></tr></table></figure><blockquote><p><strong>(jint)(Atomic::cmpxchg(x, addr, e)) == e;</strong>: 这一行代码是主要的核心代码,使用<strong>Atomic::cmpxchg</strong>函数执行原子的比较和交换操作。它尝试将<strong>addr</strong>指向的内存地址上的值与e进行比较,如果相等,则将其替换为x。最后,它将比较结果与e进行比较,如果相等,则返回true,否则返回false。</p></blockquote><p>接下来在jdk1.8的源码:atomic_linux_x86.inline.hpp中看一下这个核心代码的底层逻辑,这段代码会先通过操作系统的内核方法判断是否为多处理器系统,在通过LOCK_IF_MP获取lock指令,实际上拿到的汇编指令lock与cmpxchgl来实现原子性。当拿到lock指令的时候就能给进行比较并交换,没有得到锁的情况需要等待锁被释放,这就达到了原子性问题。当缓存不是很大的情况是使用缓存行锁,但如果超过了缓存行大小,就会使用总线锁。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">inline</span> jint <span class="title">Atomic::cmpxchg</span> <span class="params">(jint exchange_value, <span class="keyword">volatile</span> jint* dest, jint compare_value)</span> </span>{</span><br><span class="line"> <span class="type">int</span> mp = os::<span class="built_in">is_MP</span>(); <span class="comment">// 调用操作系统内核方法用于判断当前的系统是否为多处理器系统。</span></span><br><span class="line"> <span class="comment">// LOCK_IF_MP获取lock指令,他是判断是否位多处理器</span></span><br><span class="line"> <span class="function">__asm__ <span class="title">volatile</span> <span class="params">(LOCK_IF_MP(%<span class="number">4</span>) <span class="string">"cmpxchgl %1,(%3)"</span> <span class="comment">// 汇编指令,前面已经定义#define LOCK_IF_MP(mp) "cmp $0, " #mp "; je 1f; lock; 1: "</span></span></span></span><br><span class="line"><span class="params"><span class="function"> : <span class="string">"=a"</span> (exchange_value)</span></span></span><br><span class="line"><span class="params"><span class="function"> : <span class="string">"r"</span> (exchange_value), <span class="string">"a"</span> (compare_value), <span class="string">"r"</span> (dest), <span class="string">"r"</span> (mp)</span></span></span><br><span class="line"><span class="params"><span class="function"> : <span class="string">"cc"</span>, <span class="string">"memory"</span>)</span></span>;</span><br><span class="line"> <span class="keyword">return</span> exchange_value;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Synchronized底层的锁优化机制"><a href="#Synchronized底层的锁优化机制" class="headerlink" title="Synchronized底层的锁优化机制"></a>Synchronized底层的锁优化机制</h2><h3 id="1、锁的状态升级变迁"><a href="#1、锁的状态升级变迁" class="headerlink" title="1、锁的状态升级变迁"></a>1、锁的状态升级变迁</h3><p>在jdk1.6以后Synchronized锁引用了许多状态切换:无状态、偏向锁、轻量级锁、重量级锁,根据不同的条件进行状态的切换升级,能够在一定的程度中使性能提升。</p><h4 id="1-、锁状态mark-word结构"><a href="#1-、锁状态mark-word结构" class="headerlink" title="(1)、锁状态mark word结构"></a>(1)、锁状态mark word结构</h4><p>synchronized锁在线程第一次访问的时候,实际上是没有加锁的,只是在mark word中记录了线程ID,这种就是偏向锁,默认是认为不会有多个线程抢着用,mark word是通过64bit来表示的,通过最低2位也就是锁标志位,偏向锁与无锁的值是01,轻量级锁用00表示,重量级锁用10表示,标记了GC的用11表示,无锁与偏向锁低2位是一致的,在倒数第3位有1位来表示偏向锁位:值为1表示偏向锁。</p><p><img src="https://s2.loli.net/2023/06/21/Poki3GF5W2RDEHb.png" alt="image-20230613131954515.png"></p><h4 id="2-、锁升级流程"><a href="#2-、锁升级流程" class="headerlink" title="(2)、锁升级流程"></a>(2)、锁升级流程</h4><p>在Java中,synchronized锁的状态可以根据竞争情况进行升级和降级,结合上图,我们就可以清晰的了解synchronized底层锁的状态变化过程。</p><p>初始状态下,对象没有被任何线程锁定,此时是无状态锁;当有一个线程第一次进入synchronized代码块时,JVM会偏向该线程,将锁的对象头标记为偏向锁,此时还会记录这个线程ID,能够直接进入同步块,标记偏向线程id是为了等下次线程过来访问的时候,会进行线程id比较,如果相同,就能够获取这把锁;然而,当多个线程来争抢这把锁,这时候就会进行锁升级,会将偏向锁升级为轻量级锁,它会使用CAS操作来尝试将锁的对象头设置为指向锁记录(Lock Record)的指针,如果CAS成功,就能够获得这把锁,如果获得不到,会通过自旋;当轻量级锁竞争失败时,锁会升级为重量级锁。此时,JVM会使用操作系统的互斥量(Mutex)来实现锁的互斥操作。重量级锁涉及到用户态和内核态之间的切换,开销较大。</p><p><img src="https://s2.loli.net/2023/06/21/MG793bamDBesr1d.png" alt="image-20230611235859502.png"></p><h4 id="3-、轻量级锁一定比重量级锁性能高吗?"><a href="#3-、轻量级锁一定比重量级锁性能高吗?" class="headerlink" title="(3)、轻量级锁一定比重量级锁性能高吗?"></a>(3)、轻量级锁一定比重量级锁性能高吗?</h4><p>当线程足够多的时候,如果使用轻量级锁,很多个线程会自旋,没有成功将会一直自旋,这样还会消耗cpu,此时还不如直接放在队列中使用重量级锁。总的来说如果竞争不激烈,轻量级锁可以提供更好的性能。而在高度竞争的情况下,重量级锁可能更适合,避免了自旋和不断重试的开销。在实际使用中,需要根据具体情况进行评估和测试,选择适当的锁机制。</p><h3 id="2、synchronized锁升级状态变化原理"><a href="#2、synchronized锁升级状态变化原理" class="headerlink" title="2、synchronized锁升级状态变化原理"></a>2、synchronized锁升级状态变化原理</h3><p>接下来我们从代码的形式来了解synchronized锁的升级状态变化。</p><p>因为使用了 java对象的内存布局以及使用ClassLayout查看布局,首先需要导入依赖, 0.13是显示二进制,0.17最新版本是显示十六进制。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"><!-- java对象的内存布局以及使用ClassLayout查看布局 --></span></span><br><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.openjdk.jol<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>jol-core<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>0.17<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br></pre></td></tr></table></figure><p>我们在main方法中,通过ClassLayout查看布局,这里User只是定义的一个对象实体,里面包含id和name属性。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">User</span> <span class="variable">userTemp</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>();</span><br><span class="line"><span class="comment">/*java对象的内存布局以及使用ClassLayout查看布局*/</span></span><br><span class="line">System.out.println(<span class="string">"无状态(001):"</span> + ClassLayout.parseInstance(userTemp).toPrintable());</span><br></pre></td></tr></table></figure><p>我们看一下这段代码的输出,可以对照锁的状态图来看,这些信息包含对象头mark,class,还有对象属性,最后4字节是对齐位,因为位数是8的整数倍。mark由8个字节,64bit组成,以下是十六进制,我们转换成二进制:0…..001对比锁状态图来看是无锁状态。</p><figure class="highlight ceylon"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">无状态(<span class="number">001</span>):cn.lyd.test.service.user.dto.User <span class="keyword">object</span> internals:</span><br><span class="line">OFF SZ TYPE DESCRIPTION VALUE</span><br><span class="line"> <span class="number">0</span> <span class="number">8</span> (<span class="keyword">object</span> header: mark) <span class="number">0</span>x<span class="number">0000000000000001</span> (non-biasable; age: <span class="number">0</span>)</span><br><span class="line"> <span class="number">8</span> <span class="number">4</span> (<span class="keyword">object</span> header: <span class="keyword">class</span>) <span class="number">0</span>xf<span class="number">800</span>c<span class="number">143</span></span><br><span class="line"> <span class="number">12</span> <span class="number">4</span> int User.id <span class="number">0</span></span><br><span class="line"> <span class="number">16</span> <span class="number">4</span> java.lang.String User.name null</span><br><span class="line"> <span class="number">20</span> <span class="number">4</span> (<span class="keyword">object</span> alignment gap) </span><br><span class="line">Instance size: <span class="number">24</span> bytes</span><br><span class="line">Space losses: <span class="number">0</span> bytes internal + <span class="number">4</span> bytes external = <span class="number">4</span> bytes total</span><br></pre></td></tr></table></figure><p>先给出全部代码,后面针对代码进行解析,通过这段代码,就能够清晰看见锁是如何升级的。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: lyd</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: synchronized锁升级状态变化</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2023/6/11</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LockUpgrade</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException { <span class="comment">// 主线程</span></span><br><span class="line"> <span class="type">User</span> <span class="variable">userTemp</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>();</span><br><span class="line"> <span class="comment">/*java对象的内存布局以及使用ClassLayout查看布局*/</span></span><br><span class="line"> System.out.println(<span class="string">"无状态(001):"</span> + ClassLayout.parseInstance(userTemp).toPrintable());</span><br><span class="line"> <span class="comment">// jvm默认延迟4s自动开机偏向锁,可以通过-XX:BiasedLockingStartupDelay = 0 取消延迟</span></span><br><span class="line"> <span class="comment">// 如果不需要偏向锁,使用-XX:- UseBiasedLocking = false 关闭</span></span><br><span class="line"> <span class="comment">// -> 一开始是不会自动使用偏向锁的,如果一开始就使用synchronized锁,就会直接使用重量级锁,jvm中需要延迟4s才能够开启偏向锁。所以这里延迟了5s</span></span><br><span class="line"> Thread.sleep(<span class="number">5000</span>);</span><br><span class="line"> <span class="comment">// 偏向锁认为一开始只有一个线程来访问</span></span><br><span class="line"> <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>(); <span class="comment">// 重新new一个对象</span></span><br><span class="line"> System.out.println(<span class="string">"启用偏向锁(101):"</span> + ClassLayout.parseInstance(user).toPrintable());</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i < <span class="number">2</span>; i++) {</span><br><span class="line"> <span class="keyword">synchronized</span> (user) { <span class="comment">// 加上偏向锁</span></span><br><span class="line"> System.out.println(<span class="string">"偏向锁(101)(带线程ID):"</span> + ClassLayout.parseInstance(user).toPrintable());</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 虽然这里会释放偏向锁,但实际上不会主动释放,头部(高54位)是不会做修改的,这些数值代表属于哪个线程</span></span><br><span class="line"> <span class="comment">// 在偏向锁中,高54位来标识线程id,主要是如果同个线程过来的,就直接走这个偏向锁</span></span><br><span class="line"> System.out.println(<span class="string">"释放偏向锁(101)(带线程ID):"</span> + ClassLayout.parseInstance(user).toPrintable());</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -> { <span class="comment">// 多个线程竞争,可能升级为轻量级锁</span></span><br><span class="line"> <span class="keyword">synchronized</span> (user) {</span><br><span class="line"> System.out.println(<span class="string">"轻量级锁(00)(带线程ID):"</span> + ClassLayout.parseInstance(user).toPrintable());</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> System.out.println(<span class="string">"睡眠3秒钟=========================================="</span>);</span><br><span class="line"> Thread.sleep(<span class="number">3000</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line"> }</span><br><span class="line"> System.out.println(<span class="string">"轻量级锁升级重量级锁(10):"</span> + ClassLayout.parseInstance(user).toPrintable());</span><br><span class="line"> }</span><br><span class="line"> }).start();</span><br><span class="line"> Thread.sleep(<span class="number">1000</span>);</span><br><span class="line"> <span class="comment">// 开启新的线程, 第一个线程还在睡眠中,意味着线程还没有结束,此时第二个线程就执行了,这样就会导致多个线程的访问,这是就会升级位重量级锁</span></span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -> {</span><br><span class="line"> <span class="keyword">synchronized</span> (user) {</span><br><span class="line"> System.out.println(<span class="string">"重量级锁(10)(带线程ID):"</span> + ClassLayout.parseInstance(user).toPrintable());</span><br><span class="line"> }</span><br><span class="line"> }).start();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>代码解析:</p><p>首先是无锁的状态,这个在前面已经介绍了,现在就不再继续赘述。我们要知道,jvm默认是延迟4s才自动开机偏向锁,我们可以通过<code>-XX:BiasedLockingStartupDelay = 0</code> 取消延迟。</p><p>一开始是不会自动使用偏向锁的,如果一开始就使用synchronized锁,就会直接使用重量级锁,jvm中需要延迟4s才能够开启偏向锁。所以这里延迟了5s。</p><p>重新new一个对象,此时我们可以看到下启用了偏向锁,注意,这里虽然还没有上锁,但是锁的使用是需要先开启锁的。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>(); <span class="comment">// 重新new一个对象</span></span><br><span class="line">System.out.println(<span class="string">"启用偏向锁(101):"</span> + ClassLayout.parseInstance(user).toPrintable());</span><br></pre></td></tr></table></figure><p>看一下控制台的输出,可以看到头部mark信息,转换二进制就是000…….0101,这可以看到偏向锁就已经启用了,但是我们观察到偏向线程id的54bit都是0,显然这时候还没有上锁,只是开启了偏向锁而已。</p><figure class="highlight ceylon"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">启用偏向锁(<span class="number">101</span>):cn.lyd.test.service.user.dto.User <span class="keyword">object</span> internals:</span><br><span class="line">OFF SZ TYPE DESCRIPTION VALUE</span><br><span class="line"> <span class="number">0</span> <span class="number">8</span> (<span class="keyword">object</span> header: mark) <span class="number">0</span>x<span class="number">0000000000000005</span> (biasable; age: <span class="number">0</span>)</span><br><span class="line"> <span class="number">8</span> <span class="number">4</span> (<span class="keyword">object</span> header: <span class="keyword">class</span>) <span class="number">0</span>xf<span class="number">800</span>c<span class="number">143</span></span><br><span class="line"> <span class="number">12</span> <span class="number">4</span> int User.id <span class="number">0</span></span><br><span class="line"> <span class="number">16</span> <span class="number">4</span> java.lang.String User.name null</span><br><span class="line"> <span class="number">20</span> <span class="number">4</span> (<span class="keyword">object</span> alignment gap) </span><br><span class="line">Instance size: <span class="number">24</span> bytes</span><br><span class="line">Space losses: <span class="number">0</span> bytes internal + <span class="number">4</span> bytes external = <span class="number">4</span> bytes total</span><br></pre></td></tr></table></figure><p>通过for循环,主线程两次访问user这把锁,此时启动的是偏向锁,这里是携带了线程id,在偏向锁中,高54位来标识线程id,主要是如果同个线程过来的,就直接走这个偏向锁。输出完后释放偏向锁,实际上不会主动释放,头部高54位并没有做修改。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i < <span class="number">2</span>; i++) {</span><br><span class="line"> <span class="keyword">synchronized</span> (user) { <span class="comment">// 加上偏向锁</span></span><br><span class="line"> System.out.println(<span class="string">"偏向锁(101)(带线程ID):"</span> + ClassLayout.parseInstance(user).toPrintable());</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 虽然这里会释放偏向锁,但实际上不会主动释放,头部(高54位)是不会做修改的,这些数值代表属于哪个线程</span></span><br><span class="line"> <span class="comment">// 在偏向锁中,高54位来标识线程id,主要是如果同个线程过来的,就直接走这个偏向锁</span></span><br><span class="line"> System.out.println(<span class="string">"释放偏向锁(101)(带线程ID):"</span> + ClassLayout.parseInstance(user).toPrintable());</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>因为是主线程来两次访问,线程id都是相同的,根据头mark的二进制可以解出线程id:000….001100111111010100这是将0x00000000033f5005转换成二进制,取高54位的来。</p><figure class="highlight ceylon"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">偏向锁(<span class="number">101</span>)(带线程ID):cn.lyd.test.service.user.dto.User <span class="keyword">object</span> internals:</span><br><span class="line">OFF SZ TYPE DESCRIPTION VALUE</span><br><span class="line"> <span class="number">0</span> <span class="number">8</span> (<span class="keyword">object</span> header: mark) <span class="number">0</span>x<span class="number">00000000033f</span><span class="number">5005</span> (biased: <span class="number">0</span>x<span class="number">000000000000</span>cfd<span class="number">4</span>; epoch: <span class="number">0</span>; age: <span class="number">0</span>)</span><br><span class="line"> <span class="number">8</span> <span class="number">4</span> (<span class="keyword">object</span> header: <span class="keyword">class</span>) <span class="number">0</span>xf<span class="number">800</span>c<span class="number">143</span></span><br><span class="line"> <span class="number">12</span> <span class="number">4</span> int User.id <span class="number">0</span></span><br><span class="line"> <span class="number">16</span> <span class="number">4</span> java.lang.String User.name null</span><br><span class="line"> <span class="number">20</span> <span class="number">4</span> (<span class="keyword">object</span> alignment gap) </span><br><span class="line">Instance size: <span class="number">24</span> bytes</span><br><span class="line">Space losses: <span class="number">0</span> bytes internal + <span class="number">4</span> bytes external = <span class="number">4</span> bytes total</span><br><span class="line"></span><br><span class="line">释放偏向锁(<span class="number">101</span>)(带线程ID):cn.lyd.test.service.user.dto.User <span class="keyword">object</span> internals:</span><br><span class="line">OFF SZ TYPE DESCRIPTION VALUE</span><br><span class="line"> <span class="number">0</span> <span class="number">8</span> (<span class="keyword">object</span> header: mark) <span class="number">0</span>x<span class="number">00000000033f</span><span class="number">5005</span> (biased: <span class="number">0</span>x<span class="number">000000000000</span>cfd<span class="number">4</span>; epoch: <span class="number">0</span>; age: <span class="number">0</span>)</span><br><span class="line"> <span class="number">8</span> <span class="number">4</span> (<span class="keyword">object</span> header: <span class="keyword">class</span>) <span class="number">0</span>xf<span class="number">800</span>c<span class="number">143</span></span><br><span class="line"> <span class="number">12</span> <span class="number">4</span> int User.id <span class="number">0</span></span><br><span class="line"> <span class="number">16</span> <span class="number">4</span> java.lang.String User.name null</span><br><span class="line"> <span class="number">20</span> <span class="number">4</span> (<span class="keyword">object</span> alignment gap) </span><br><span class="line">Instance size: <span class="number">24</span> bytes</span><br><span class="line">Space losses: <span class="number">0</span> bytes internal + <span class="number">4</span> bytes external = <span class="number">4</span> bytes total</span><br><span class="line"></span><br><span class="line">偏向锁(<span class="number">101</span>)(带线程ID):cn.lyd.test.service.user.dto.User <span class="keyword">object</span> internals:</span><br><span class="line">OFF SZ TYPE DESCRIPTION VALUE</span><br><span class="line"> <span class="number">0</span> <span class="number">8</span> (<span class="keyword">object</span> header: mark) <span class="number">0</span>x<span class="number">00000000033f</span><span class="number">5005</span> (biased: <span class="number">0</span>x<span class="number">000000000000</span>cfd<span class="number">4</span>; epoch: <span class="number">0</span>; age: <span class="number">0</span>)</span><br><span class="line"> <span class="number">8</span> <span class="number">4</span> (<span class="keyword">object</span> header: <span class="keyword">class</span>) <span class="number">0</span>xf<span class="number">800</span>c<span class="number">143</span></span><br><span class="line"> <span class="number">12</span> <span class="number">4</span> int User.id <span class="number">0</span></span><br><span class="line"> <span class="number">16</span> <span class="number">4</span> java.lang.String User.name null</span><br><span class="line"> <span class="number">20</span> <span class="number">4</span> (<span class="keyword">object</span> alignment gap) </span><br><span class="line">Instance size: <span class="number">24</span> bytes</span><br><span class="line">Space losses: <span class="number">0</span> bytes internal + <span class="number">4</span> bytes external = <span class="number">4</span> bytes total</span><br><span class="line"></span><br><span class="line">释放偏向锁(<span class="number">101</span>)(带线程ID):cn.lyd.test.service.user.dto.User <span class="keyword">object</span> internals:</span><br><span class="line">OFF SZ TYPE DESCRIPTION VALUE</span><br><span class="line"> <span class="number">0</span> <span class="number">8</span> (<span class="keyword">object</span> header: mark) <span class="number">0</span>x<span class="number">00000000033f</span><span class="number">5005</span> (biased: <span class="number">0</span>x<span class="number">000000000000</span>cfd<span class="number">4</span>; epoch: <span class="number">0</span>; age: <span class="number">0</span>)</span><br><span class="line"> <span class="number">8</span> <span class="number">4</span> (<span class="keyword">object</span> header: <span class="keyword">class</span>) <span class="number">0</span>xf<span class="number">800</span>c<span class="number">143</span></span><br><span class="line"> <span class="number">12</span> <span class="number">4</span> int User.id <span class="number">0</span></span><br><span class="line"> <span class="number">16</span> <span class="number">4</span> java.lang.String User.name null</span><br><span class="line"> <span class="number">20</span> <span class="number">4</span> (<span class="keyword">object</span> alignment gap) </span><br><span class="line">Instance size: <span class="number">24</span> bytes</span><br><span class="line">Space losses: <span class="number">0</span> bytes internal + <span class="number">4</span> bytes external = <span class="number">4</span> bytes total</span><br></pre></td></tr></table></figure><p>刚刚跑的是主线程,我们可以创建一个新的线程,使得多个线程竞争,这就可能将偏向锁升级为轻量级锁。当线程获取到轻量级锁,其他线程进来会拿不到,此时会自旋。这里我们睡眠3s,来模拟线程紧握锁不放,这时候我们在开一个新的线程,还是来争抢user这把锁,这就导致CAS自旋失败,锁膨胀就会将轻量级锁升级为重量级锁。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(() -> { <span class="comment">// 多个线程竞争,可能升级为轻量级锁</span></span><br><span class="line"> <span class="keyword">synchronized</span> (user) {</span><br><span class="line"> System.out.println(<span class="string">"轻量级锁(00)(带线程ID):"</span> + ClassLayout.parseInstance(user).toPrintable());</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> System.out.println(<span class="string">"睡眠3秒钟=========================================="</span>);</span><br><span class="line"> Thread.sleep(<span class="number">3000</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line"> }</span><br><span class="line"> System.out.println(<span class="string">"轻量级锁升级重量级锁(10):"</span> + ClassLayout.parseInstance(user).toPrintable());</span><br><span class="line"> }</span><br><span class="line">}).start();</span><br><span class="line">Thread.sleep(<span class="number">1000</span>);</span><br><span class="line"><span class="comment">// 开启新的线程, 第一个线程还在睡眠中,意味着线程还没有结束,此时第二个线程就执行了,这样就会导致多个线程的访问,这是就会升级位重量级锁</span></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(() -> {</span><br><span class="line"> <span class="keyword">synchronized</span> (user) {</span><br><span class="line"> System.out.println(<span class="string">"重量级锁(10)(带线程ID):"</span> + ClassLayout.parseInstance(user).toPrintable());</span><br><span class="line"> }</span><br><span class="line">}).start();</span><br></pre></td></tr></table></figure><p>通过以下日志,我们可以将头mark转换成二进制,取最后两位锁标记位,能够清晰看到轻量级锁升级到重量级锁。</p><figure class="highlight ceylon"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">轻量级锁(<span class="number">00</span>)(带线程ID):cn.lyd.test.service.user.dto.User <span class="keyword">object</span> internals:</span><br><span class="line">OFF SZ TYPE DESCRIPTION VALUE</span><br><span class="line"> <span class="number">0</span> <span class="number">8</span> (<span class="keyword">object</span> header: mark) <span class="number">0</span>x<span class="number">000000002</span>a<span class="number">11</span>ef<span class="number">08</span> (thin lock: <span class="number">0</span>x<span class="number">000000002</span>a<span class="number">11</span>ef<span class="number">08</span>)</span><br><span class="line"> <span class="number">8</span> <span class="number">4</span> (<span class="keyword">object</span> header: <span class="keyword">class</span>) <span class="number">0</span>xf<span class="number">800</span>c<span class="number">143</span></span><br><span class="line"> <span class="number">12</span> <span class="number">4</span> int User.id <span class="number">0</span></span><br><span class="line"> <span class="number">16</span> <span class="number">4</span> java.lang.String User.name null</span><br><span class="line"> <span class="number">20</span> <span class="number">4</span> (<span class="keyword">object</span> alignment gap) </span><br><span class="line">Instance size: <span class="number">24</span> bytes</span><br><span class="line">Space losses: <span class="number">0</span> bytes internal + <span class="number">4</span> bytes external = <span class="number">4</span> bytes total</span><br><span class="line"></span><br><span class="line">睡眠<span class="number">3</span>秒钟==========================================</span><br><span class="line">轻量级锁升级重量级锁(<span class="number">10</span>):cn.lyd.test.service.user.dto.User <span class="keyword">object</span> internals:</span><br><span class="line">OFF SZ TYPE DESCRIPTION VALUE</span><br><span class="line"> <span class="number">0</span> <span class="number">8</span> (<span class="keyword">object</span> header: mark) <span class="number">0</span>x<span class="number">0000000024232f</span><span class="number">6</span>a (fat lock: <span class="number">0</span>x<span class="number">0000000024232f</span><span class="number">6</span>a)</span><br><span class="line"> <span class="number">8</span> <span class="number">4</span> (<span class="keyword">object</span> header: <span class="keyword">class</span>) <span class="number">0</span>xf<span class="number">800</span>c<span class="number">143</span></span><br><span class="line"> <span class="number">12</span> <span class="number">4</span> int User.id <span class="number">0</span></span><br><span class="line"> <span class="number">16</span> <span class="number">4</span> java.lang.String User.name null</span><br><span class="line"> <span class="number">20</span> <span class="number">4</span> (<span class="keyword">object</span> alignment gap) </span><br><span class="line">Instance size: <span class="number">24</span> bytes</span><br><span class="line">Space losses: <span class="number">0</span> bytes internal + <span class="number">4</span> bytes external = <span class="number">4</span> bytes total</span><br><span class="line"> </span><br><span class="line">重量级锁(<span class="number">10</span>)(带线程ID):cn.lyd.test.service.user.dto.User <span class="keyword">object</span> internals:</span><br><span class="line">OFF SZ TYPE DESCRIPTION VALUE</span><br><span class="line"> <span class="number">0</span> <span class="number">8</span> (<span class="keyword">object</span> header: mark) <span class="number">0</span>x<span class="number">0000000024232f</span><span class="number">6</span>a (fat lock: <span class="number">0</span>x<span class="number">0000000024232f</span><span class="number">6</span>a)</span><br><span class="line"> <span class="number">8</span> <span class="number">4</span> (<span class="keyword">object</span> header: <span class="keyword">class</span>) <span class="number">0</span>xf<span class="number">800</span>c<span class="number">143</span></span><br><span class="line"> <span class="number">12</span> <span class="number">4</span> int User.id <span class="number">0</span></span><br><span class="line"> <span class="number">16</span> <span class="number">4</span> java.lang.String User.name null</span><br><span class="line"> <span class="number">20</span> <span class="number">4</span> (<span class="keyword">object</span> alignment gap) </span><br><span class="line">Instance size: <span class="number">24</span> bytes</span><br><span class="line">Space losses: <span class="number">0</span> bytes internal + <span class="number">4</span> bytes external = <span class="number">4</span> bytes total</span><br></pre></td></tr></table></figure><h2 id="文章资料"><a href="#文章资料" class="headerlink" title="文章资料"></a>文章资料</h2><p><a href="https://zhuanlan.zhihu.com/p/160185717">https://zhuanlan.zhihu.com/p/160185717</a></p><p><a href="https://juejin.cn/post/6978882583492821023">https://juejin.cn/post/6978882583492821023</a></p>]]></content>
<summary type="html"><h1 id="【多线程与高并发】-锁的机制与底层优化原理"><a href="#【多线程与高并发】-锁的机制与底层优化原理" class="headerlink" title="【多线程与高并发】- 锁的机制与底层优化原理"></a>【多线程与高并发】- 锁的机制与底层优化原理</summary>
<category term="多线程与高并发" scheme="https://lydandtry.github.io/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B%E4%B8%8E%E9%AB%98%E5%B9%B6%E5%8F%91/"/>
</entry>
</feed>