-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
370 lines (191 loc) · 102 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
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Lrq's blogs</title>
<link href="/atom.xml" rel="self"/>
<link href="http://yoursite.com/"/>
<updated>2020-02-17T06:29:27.854Z</updated>
<id>http://yoursite.com/</id>
<author>
<name>Lrq</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>Data transformation pipeline - Data Cleaning(2)</title>
<link href="http://yoursite.com/2020/02/17/DataCleaning(2)/"/>
<id>http://yoursite.com/2020/02/17/DataCleaning(2)/</id>
<published>2020-02-17T06:29:27.796Z</published>
<updated>2020-02-17T06:29:27.854Z</updated>
<content type="html"><![CDATA[<h1 id="Data-Cleaning-2"><a href="#Data-Cleaning-2" class="headerlink" title="Data Cleaning(2)"></a>Data Cleaning(2)</h1><h2 id="Creating-a-deployable-artifact"><a href="#Creating-a-deployable-artifact" class="headerlink" title="Creating a deployable artifact"></a>Creating a deployable artifact</h2><p> The next job we need to do is to package our application after cleaning data. If my code consists of many modules, I must package them in the <code>zip</code> format by navigating the root of my pipeline.<br> Here, the root of the pipeline is the directory of ‘pydiaper’, so we <code>cd</code> to the ‘spark_pipeline’ directory and invoke <code>zip --recurse-paths zip_file.zip pipeline_folder</code>, where <code>zip_file</code>is <strong>pydiaper.zip</strong> and <code>pipeline_folder</code> is <strong>pydiaper</strong>. Just like this, <code>zip --recurse-paths pydiaper.zip pydiaper</code>.</p><div align="center"> <img src="http://q5cad0flf.bkt.clouddn.com/2020-02-17-12-29-32.png"> <br> Pic.1 Directory</div><br><br> As we can see, the pydiaper.zip is generated.<div align="center"> <img src="http://q5cad0flf.bkt.clouddn.com/2020-02-17-12-37-44.png"> <br> Pic.2 Directory After Generate pydiaper.zip</div><h2 id="Submitting-Spark-job"><a href="#Submitting-Spark-job" class="headerlink" title="Submitting Spark job"></a>Submitting Spark job</h2><p> With the dependencies of a job ready to be distributed across a cluster’s nodes, we can submit a job to a cluster, using command <code>spark-submit --py-files PY_FILES MAIN_PYTHON_FILE</code>, where PY_FILES is the archive file we zipped, and MIAN_PYTHON_FILE is the entry point of your application. Now we are in <code>~/workspace/spark_pipelines/pydiaper</code> directory, So we invoke <code>spark-submit --py-files pydiapers.zip pydiaper/cleaning/clean_ratings.py</code></p><h2 id="Verifying-your-pipeline’s-output"><a href="#Verifying-your-pipeline’s-output" class="headerlink" title="Verifying your pipeline’s output"></a>Verifying your pipeline’s output</h2><p> After We submit application with spark-submit, we want to check whether it actually created file in the right location.<br> We <code>cd</code> to <code>~/workspace/mnt/data_lake/clean/product_ratings</code> and <code>ls</code> the files description. We can find two parquet format files. The Spark distributed the data among the number of cores this machine has (in this case 2) and based on the size of the data that needs to be processed, created two partitions for it. Each partition ends up in one of these parquet files.</p><p><div align="center"><img src="http://q5cad0flf.bkt.clouddn.com/2020-02-17-14-25-58.png">Pic.3 Pipeline's Output</div><br> </p><h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><p><a href="https://datacamp.com" target="_blank" rel="noopener">https://datacamp.com</a></p>]]></content>
<summary type="html">
<h1 id="Data-Cleaning-2"><a href="#Data-Cleaning-2" class="headerlink" title="Data Cleaning(2)"></a>Data Cleaning(2)</h1><h2 id="Creating-a-
</summary>
<category term="Data Science" scheme="http://yoursite.com/categories/Data-Science/"/>
<category term="PySpark" scheme="http://yoursite.com/tags/PySpark/"/>
</entry>
<entry>
<title>Data transformation pipeline - Data Cleaning(1)</title>
<link href="http://yoursite.com/2020/02/16/DataCleaning/"/>
<id>http://yoursite.com/2020/02/16/DataCleaning/</id>
<published>2020-02-16T14:20:38.112Z</published>
<updated>2020-02-17T06:11:41.595Z</updated>
<content type="html"><![CDATA[<h1 id="Data-Cleaning-1"><a href="#Data-Cleaning-1" class="headerlink" title="Data Cleaning(1)"></a>Data Cleaning(1)</h1><h2 id="Why-we-should-clean-data"><a href="#Why-we-should-clean-data" class="headerlink" title="Why we should clean data?"></a>Why we should clean data?</h2><p> Most data sources people work with are not ready for analytics. Hence, the second step in the data transformation pipeline is cleaning the data.</p><h2 id="How-to-clean-data"><a href="#How-to-clean-data" class="headerlink" title="How to clean data"></a>How to clean data</h2><p> We use PySpark package to achieve the need. We import PySpark package and use the function they provided for us.</p><h3 id="1-Removing-invalid-rows"><a href="#1-Removing-invalid-rows" class="headerlink" title="1. Removing invalid rows"></a>1. Removing invalid rows</h3><figure class="highlight python"><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">from</span> pyspark.sql <span class="keyword">import</span> SparkSession</span><br><span class="line"><span class="comment"># First, we create a session</span></span><br><span class="line">spark = SparkSession.builder.getOrCreate()</span><br><span class="line"><span class="comment"># We add parameters mode='DROPMALFORMED' in the 'options' function to drop invalid rows of dataset.</span></span><br><span class="line">ratings = spark.read.options(header=<span class="literal">True</span>, mode=<span class="string">'DROPMALFORMED'</span>).csv(<span class="string">'my_csv_path.csv'</span>)</span><br><span class="line">ratings.show()</span><br></pre></td></tr></table></figure><p>If we don’t use the mode=’DROPMALFORMED’ parameter, as you can see, the data ratings show to us include null value. But once we add that parameters, the row contains null value is deleted.</p><div align="center"> <img src="http://q5cad0flf.bkt.clouddn.com/before.JPG"> Pic.1 Before Using parameter</div><div align="center"> <img src="http://q5cad0flf.bkt.clouddn.com/after.JPG"> Pic.2 after Using parameter</div><h3 id="2-Filling-unknown-data"><a href="#2-Filling-unknown-data" class="headerlink" title="2.Filling unknown data"></a>2.Filling unknown data</h3><p> What if almost all fields in a row are valid, except one or two. Sometimes, these fields are not critical and can be left empty, sometimes they can be given a default value.<br>After we Removing the invalid rows, we execute a operation as follow.<br><figure class="highlight python"><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">print(<span class="string">'BEFORE'</span>)</span><br><span class="line">ratings.show()</span><br><span class="line"></span><br><span class="line">print(<span class="string">'AFTER'</span>)</span><br><span class="line"><span class="comment"># Replace nulls with arbitrary value on column subset("comfort")</span></span><br><span class="line">ratings = ratings.fillna(<span class="number">4</span>, subset=[<span class="string">"comfort"</span>])</span><br><span class="line">ratings.show()</span><br></pre></td></tr></table></figure></p><div align="center"> <img src="http://q5cad0flf.bkt.clouddn.com/compare.JPG"> Pic.3 Compare between filling unknown data or not</div><h3 id="3-Conditionally-replacing-values"><a href="#3-Conditionally-replacing-values" class="headerlink" title="3. Conditionally replacing values"></a>3. Conditionally replacing values</h3><p> Another common situation is that you have values that you want to replace or that don’t make any sense. You can select the column to be transformed by using the <code>.withColumn()</code> method, conditionally replace those values using the <code>pyspark.sql.functions.when</code> function when values meet a given condition or leave them unaltered when they don’t with the <code>.otherwise()</code> method.<br><figure class="highlight python"><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">from</span> pyspark.sql.functions <span class="keyword">import</span> col, when</span><br><span class="line">print(<span class="string">"Before"</span>)</span><br><span class="line">categorized_ratings.show()</span><br><span class="line"></span><br><span class="line">print(<span class="string">"After"</span>)</span><br><span class="line"><span class="comment"># Add/relabel the column</span></span><br><span class="line">categorized_ratings = ratings.withColumn(</span><br><span class="line"> <span class="string">"comfort"</span>,</span><br><span class="line"> <span class="comment"># Express the condition in terms of column operations</span></span><br><span class="line"> when(col(<span class="string">"comfort"</span>) > <span class="number">3</span>, <span class="string">"sufficient"</span>).otherwise(<span class="string">"insufficient"</span>))</span><br><span class="line"></span><br><span class="line">categorized_ratings.show()</span><br></pre></td></tr></table></figure></p><div align="center"> <img src="http://q5cad0flf.bkt.clouddn.com/ConditionallyReplacingValues.JPG"> Pic.4 Compare between replacing values or not</div><h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><p><a href="https://datacamp.com" target="_blank" rel="noopener">https://datacamp.com</a></p>]]></content>
<summary type="html">
<h1 id="Data-Cleaning-1"><a href="#Data-Cleaning-1" class="headerlink" title="Data Cleaning(1)"></a>Data Cleaning(1)</h1><h2 id="Why-we-shou
</summary>
<category term="Data Science" scheme="http://yoursite.com/categories/Data-Science/"/>
<category term="PySpark" scheme="http://yoursite.com/tags/PySpark/"/>
</entry>
<entry>
<title>一些关于递归算法的总结</title>
<link href="http://yoursite.com/2020/02/07/%E4%B8%80%E4%BA%9B%E9%80%92%E5%BD%92%E7%AE%97%E6%B3%95%E7%9A%84%E6%80%BB%E7%BB%93/"/>
<id>http://yoursite.com/2020/02/07/%E4%B8%80%E4%BA%9B%E9%80%92%E5%BD%92%E7%AE%97%E6%B3%95%E7%9A%84%E6%80%BB%E7%BB%93/</id>
<published>2020-02-07T14:42:55.941Z</published>
<updated>2020-02-07T16:23:31.083Z</updated>
<content type="html"><![CDATA[<h2 id="简述"><a href="#简述" class="headerlink" title="简述"></a>简述</h2><p>  递归是函数调用函数本身,并且每一次都做相同的动作。因此在看待递归时,只需要将子问题解决(解决子问题的时候需要使用到当前解),合并到当前的解中,然后去递归的求解下一个子问题,直到最大的问题的解被解决。<br>  下面两个问题虽然都和反转链表有关,虽然我都使用了递归求解,但是仔细品味,两者递归的使用,虽然都是正确的,但是它们表达出的思想是不同的。</p><h2 id="第一题:两两交换链表中的节点"><a href="#第一题:两两交换链表中的节点" class="headerlink" title="第一题:两两交换链表中的节点"></a>第一题:两两交换链表中的节点</h2><h3 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h3><p>给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。<br>你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。</p><h3 id="实现思路"><a href="#实现思路" class="headerlink" title="实现思路"></a>实现思路</h3><p>我们可以递归的交换两两相邻的链表节点。定义递归函数swapPairs,我们在其中一级递归中,</p><ol><li>将head和temp指向的两个节点互换。<br> head->next = swapPairs(temp->next);<br> temp->next = head;</li><li>返回交换后的,本级递归的头节点供调用方的head所指向。</li></ol><p>交换前:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="http://q5cad0flf.bkt.clouddn.com/beforeswap.JPG" alt title> </div> <div class="image-caption"></div> </figure> <p>交换后<br><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="http://q5cad0flf.bkt.clouddn.com/afterswap.jpg" alt title> </div> <div class="image-caption"></div> </figure></p><p>“未交换过的节点”虽然还指向head节点,但是这是不影响的,因为对“未交换过的节点”中的相邻节点进行交换时,temp->next = head会将这条指向边改变。</p><h3 id="实现代码-Java"><a href="#实现代码-Java" class="headerlink" title="实现代码(Java)"></a>实现代码(Java)</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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> ListNode <span class="title">swapPairs</span><span class="params">(ListNode head)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span>(head == <span class="keyword">null</span> || head.next == <span class="keyword">null</span>){</span><br><span class="line"> <span class="keyword">return</span> head;</span><br><span class="line"> }</span><br><span class="line"> ListNode tmp = head.next;</span><br><span class="line"> head.next = swapPairs(tmp.next);</span><br><span class="line"> tmp.next = head;</span><br><span class="line"> <span class="keyword">return</span> tmp;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="第二题:反转链表"><a href="#第二题:反转链表" class="headerlink" title="第二题:反转链表"></a>第二题:反转链表</h2><h3 id="题目描述-1"><a href="#题目描述-1" class="headerlink" title="题目描述"></a>题目描述</h3><p>反转一个单链表。<br>示例:<br><figure class="highlight plain"><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">输入: 1->2->3->4->5->NULL</span><br><span class="line">输出: 5->4->3->2->1->NULL</span><br></pre></td></tr></table></figure></p><h3 id="实现思路-1"><a href="#实现思路-1" class="headerlink" title="实现思路"></a>实现思路</h3>]]></content>
<summary type="html">
<h2 id="简述"><a href="#简述" class="headerlink" title="简述"></a>简述</h2><p>&emsp;&emsp;递归是函数调用函数本身,并且每一次都做相同的动作。因此在看待递归时,只需要将子问题解决(解决子问题的时候需要使用到当
</summary>
<category term="Algorithm" scheme="http://yoursite.com/categories/Algorithm/"/>
<category term="recursion" scheme="http://yoursite.com/tags/recursion/"/>
</entry>
<entry>
<title>最长回文子串</title>
<link href="http://yoursite.com/2020/02/05/leetcode_%E6%9C%80%E9%95%BF%E5%9B%9E%E6%96%87%E5%AD%90%E4%B8%B2/"/>
<id>http://yoursite.com/2020/02/05/leetcode_%E6%9C%80%E9%95%BF%E5%9B%9E%E6%96%87%E5%AD%90%E4%B8%B2/</id>
<published>2020-02-05T04:17:22.469Z</published>
<updated>2020-02-07T14:44:19.104Z</updated>
<content type="html"><![CDATA[<h2 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h2><p>给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。<br>例如<br><figure class="highlight plain"><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">输入: "babad"</span><br><span class="line">输出: "bab"</span><br><span class="line">注意: "aba" 也是一个有效答案。</span><br></pre></td></tr></table></figure><br><figure class="highlight plain"><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">输入: "cbbd"</span><br><span class="line">输出: "bb"</span><br></pre></td></tr></table></figure></p><h2 id="实现思路"><a href="#实现思路" class="headerlink" title="实现思路"></a>实现思路</h2><h3 id="方法一:暴力算法"><a href="#方法一:暴力算法" class="headerlink" title="方法一:暴力算法"></a>方法一:暴力算法</h3><p>枚举所有字串,花费为 $ O(n^2) $ ,判断是否为回文串,花费为 $ O(n) $ 。总的时间复杂度为 $ O(n^3) $ 。显然字符串最大长度为1000,该算法是无法通过的,但也不失为一种正确的算法。</p><h3 id="方法二:动态规划"><a href="#方法二:动态规划" class="headerlink" title="方法二:动态规划"></a>方法二:动态规划</h3><p>考虑一个回文字串 $ s’ $ ,其为字符串 $ s $ 的下标为 $ [i,j] $ 的字串。若 $ s[i-1]=s[j+1] $ ,那么可以认为s的下标为 $ [i-1,j+1] $ 的字串也为回文字串。<br>定义 $ dp[i][j] $ 为字符串 $ s $ 的下标为 $ [i,j] $ 的字串是否为回文串,是则 $ dp[i][j] = true $ ,不是则 $ dp[i][j] = false $ 。<br>于是我们可以得到状态转移方程:</p><script type="math/tex; mode=display">dp[i][j]=\left\{\begin{array}{rcl}true & {i = j} \\true & {s[i] == s[i+1]} \\dp[i + 1][j - 1] \&\& s[i] == s[j] & other \\\end{array} \right.</script><p>首先,dp数组的边界条件为</p><ol><li>所有元素本身构成回文,即 $ i = j $ 时, $ dp[i][j] = true $ 。</li><li>所有两个相邻元素相同构成回文,即 $ s[i] == s[i+1] $ 时, $ dp[i][j] = true $ 。</li></ol><p>  为了获得最长的回文串本身,需要记录回文串开始位置的索引,以及回文串的最大长度。在初始化时,设置最大长度为1,起始位置为0 (处理边界条件,如 $ s:”a” $ ;另外记得判空)。<br>  在进行边界处理时,若出现相邻元素为回文串的情况也需要记录起始位置以及最大长度为2(这是因为在动态规划填充dp数组时无法处理此种情况)。<br>  进行动态规划填充数组时,若出现比当前最大长度还长的回文字串,需要将起始位置和最大长度进行更新。另外,动态规划时,需要特别注意填充数组的顺序。状态转移方程中,计算 $ dp[i][j] $ 需要使用到 $ dp[i+1][j-1] $ 的值,因此需要从下向上遍历dp数组,才能利用到 $ dp[i+1][j-1] $ (即当前元素左下角的元素)的值。</p><h4 id="动态规划的实现代码-C"><a href="#动态规划的实现代码-C" class="headerlink" title="动态规划的实现代码(C++)"></a>动态规划的实现代码(C++)</h4><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><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="class"><span class="keyword">class</span> <span class="title">Solution</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="keyword">int</span> dp[<span class="number">1005</span>][<span class="number">1005</span>];</span><br><span class="line"></span><br><span class="line"> <span class="keyword">int</span> maxlen = <span class="number">1</span>; </span><br><span class="line"> <span class="keyword">int</span> start = <span class="number">0</span>;</span><br><span class="line"> <span class="function"><span class="built_in">string</span> <span class="title">longestPalindrome</span><span class="params">(<span class="built_in">string</span> s)</span> </span>{</span><br><span class="line"> <span class="comment">/* 判空 */</span></span><br><span class="line"> <span class="keyword">if</span>(s == <span class="string">""</span>) <span class="keyword">return</span> <span class="string">""</span>;</span><br><span class="line"> <span class="built_in">memset</span>(dp, <span class="literal">false</span>, <span class="keyword">sizeof</span>(<span class="literal">false</span>));</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i < s.<span class="built_in">size</span>(); i++){</span><br><span class="line"> <span class="comment">/* 每个字符都是回文子串 */</span></span><br><span class="line"> dp[i][i] = <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">if</span>(i + <span class="number">1</span> < s.<span class="built_in">size</span>()){</span><br><span class="line"> <span class="comment">/* 相邻的相同字符是回文子串 */</span></span><br><span class="line"> dp[i][i + <span class="number">1</span>] = (s[i] == s[i + <span class="number">1</span>]);</span><br><span class="line"> <span class="comment">/* 更新start和maxlen,i是当前的字串开始 */</span></span><br><span class="line"> <span class="comment">/* 由于maxlen只可能为2,因此就省去比较的代码了 */</span></span><br><span class="line"> <span class="keyword">if</span>(dp[i][i + <span class="number">1</span>] == <span class="literal">true</span>){</span><br><span class="line"> start = i;</span><br><span class="line"> maxlen = <span class="number">2</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/* 从右下向左上遍历/填充dp数组,以使用到dp[i+1][j-1]的值*/</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = s.<span class="built_in">size</span>() - <span class="number">2</span>; i >= <span class="number">0</span>; i--){</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> j = i + <span class="number">1</span>; j < s.<span class="built_in">size</span>(); j++){</span><br><span class="line"> <span class="comment">/* 之前初始化过的为true的字串范围就跳过,不要再遍历了 */</span></span><br><span class="line"> <span class="keyword">if</span>(dp[i][j] != <span class="literal">true</span>){</span><br><span class="line"> <span class="comment">/* 不为true的字串范围通过转移方程计算 */</span></span><br><span class="line"> dp[i][j] = (dp[i + <span class="number">1</span>][j - <span class="number">1</span>]) && (s[i] == s[j]);</span><br><span class="line"> <span class="comment">/* 更新start和maxlen,i是当前的字串开始 */</span></span><br><span class="line"> <span class="keyword">if</span>(dp[i][j] == <span class="literal">true</span>){</span><br><span class="line"> <span class="comment">/* 这里的比较代码不能省 */</span></span><br><span class="line"> <span class="keyword">if</span>(maxlen < j - i + <span class="number">1</span>){</span><br><span class="line"> maxlen = j - i + <span class="number">1</span>;</span><br><span class="line"> start = i;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</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">return</span> s.substr(start, maxlen); </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>中心扩展时,可以将暴力算法中的枚举子串和判定回文同时进行。对于每一个原始字符串 $ s $ 来说,有 $ 2n-1 $ 个中心。为什么是 $ 2n-1 $ 个中心呢?这是因为不但每个字符本身可以当作中心,像 $ “bb” $ 这种字符串,两个 $ b $ 之间也可以当作中心。因此我们把每个字符串进行填充,填充的字符不能在原始字符串中出现过。例如 $ “abdbe” $ 可以填充为 $ “a#b#d#d#e” $ ,这样包括 $ “#“ $ 的每一个字符就都能作为中心向两边扩展了。</p><h4 id="中心扩展实现代码"><a href="#中心扩展实现代码" class="headerlink" title="中心扩展实现代码"></a>中心扩展实现代码</h4><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><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="keyword">int</span> maxlen = <span class="number">1</span>;</span><br><span class="line"> <span class="function"><span class="built_in">string</span> <span class="title">centerExpand</span><span class="params">(<span class="built_in">string</span> s, <span class="keyword">int</span> pos)</span></span>{</span><br><span class="line"> <span class="keyword">int</span> i, j;</span><br><span class="line"> i = j = pos;</span><br><span class="line"> <span class="comment">/* 用i, j两个索引向中心pos两边扩展,注意判断边界条件*/</span></span><br><span class="line"> <span class="keyword">while</span>( i >= <span class="number">0</span> && j < s.<span class="built_in">size</span>() && (s[i] == s[j]) ) {</span><br><span class="line"> i--;</span><br><span class="line"> j++;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/* 循环退出时,i和j指向不相等的元素,需要撤销一步操作 */</span></span><br><span class="line"> i++, j--;</span><br><span class="line"> <span class="comment">/* 返回最长的回文子串并去掉'#'字符 */</span></span><br><span class="line"> s = s.substr(i, j - i + <span class="number">1</span>);</span><br><span class="line"> <span class="built_in">string</span> str;</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">char</span> ch : s){</span><br><span class="line"> <span class="keyword">if</span>(ch != <span class="string">'#'</span>) str.push_back(ch);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> str;</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="built_in">string</span> <span class="title">longestPalindrome</span><span class="params">(<span class="built_in">string</span> s)</span> </span>{</span><br><span class="line"> <span class="built_in">string</span> str;</span><br><span class="line"> <span class="comment">/* 对字符串填充 '#' */</span></span><br><span class="line"> <span class="keyword">char</span> pad_ch = <span class="string">'#'</span>;</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i < s.<span class="built_in">size</span>(); i++){</span><br><span class="line"> str.push_back(s[i]);</span><br><span class="line"> <span class="keyword">if</span>(i != s.<span class="built_in">size</span>() - <span class="number">1</span>) str.push_back(<span class="string">'#'</span>); </span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">string</span> ans;</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i < str.<span class="built_in">size</span>(); i++){</span><br><span class="line"> <span class="comment">/* 枚举每一个中心,计算回文串长度,并更新最大长度和字串本身 */</span></span><br><span class="line"> <span class="built_in">string</span> curans = centerExpand(str, i);</span><br><span class="line"> <span class="keyword">if</span>( curans.<span class="built_in">size</span>() >= maxlen ){</span><br><span class="line"> ans = curans;</span><br><span class="line"> maxlen = curans.<span class="built_in">size</span>();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> ans;</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<h2 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h2><p>给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。<br>例如<br><figure
</summary>
<category term="Algorithm" scheme="http://yoursite.com/categories/Algorithm/"/>
<category term="dp" scheme="http://yoursite.com/tags/dp/"/>
<category term="中心扩展/Manacher(暂未实现)" scheme="http://yoursite.com/tags/%E4%B8%AD%E5%BF%83%E6%89%A9%E5%B1%95-Manacher%EF%BC%88%E6%9A%82%E6%9C%AA%E5%AE%9E%E7%8E%B0%EF%BC%89/"/>
</entry>
<entry>
<title>无重复字符的最长子串</title>
<link href="http://yoursite.com/2020/02/04/leetcode_%E6%97%A0%E9%87%8D%E5%A4%8D%E5%AD%97%E7%AC%A6%E7%9A%84%E6%9C%80%E9%95%BF%E5%AD%90%E4%B8%B2/"/>
<id>http://yoursite.com/2020/02/04/leetcode_%E6%97%A0%E9%87%8D%E5%A4%8D%E5%AD%97%E7%AC%A6%E7%9A%84%E6%9C%80%E9%95%BF%E5%AD%90%E4%B8%B2/</id>
<published>2020-02-04T06:17:54.951Z</published>
<updated>2020-02-07T16:24:18.542Z</updated>
<content type="html"><![CDATA[<h2 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h2><p>给定一个字符串,请你找出其中不含有重复字符的 <strong>最长子串</strong> 的长度<br><code>[注]子串是连续的,子序列是可以不连续的</code></p><p>例如:<br><figure class="highlight plain"><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">输入: "abcabcbb"</span><br><span class="line">输出: 3 </span><br><span class="line">解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。</span><br></pre></td></tr></table></figure><br><figure class="highlight plain"><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">输入: "pwwkew"</span><br><span class="line">输出: 3</span><br><span class="line">解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。</span><br><span class="line"> 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。</span><br></pre></td></tr></table></figure></p><h2 id="求解方法"><a href="#求解方法" class="headerlink" title="求解方法"></a>求解方法</h2><p>维护一个滑动窗口(Sliding Window),窗口中保存不相同的字符串,窗口的大小即当前最长的<strong>不重复子串</strong><br><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="http://q5cad0flf.bkt.clouddn.com/example.JPG" alt title> </div> <div class="image-caption"></div> </figure></p><p>  如图,当前窗口大小为3。当j指向第四个字符元素时,若窗口继续增大,则index=1处的元素与index=4处的元素重复,不符合。因此窗口的下界应该增大(i增大)。<br>  i应该增长多少,才能保证算法的效率尽可能高呢?<br>  我们发现,index=1和index=4处的字符元素相同,它们之间所构成的<strong>最大窗口</strong>的大小,一定小于等于当前的窗口大小(下图为等于当前窗口大小的情况)。因此,我们可以将i直接增长到index=1字符的下一处,即index=2。</p><p>  当然,有时也会遇到以下情况。<br><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="http://q5cad0flf.bkt.clouddn.com/example2.jpg" alt title> </div> <div class="image-caption"></div> </figure></p><p>  j = 3时,由于有两个c相同,故i从i = 0跳至i = 2。j = 4时,此时又发现index = 0处的字符元素与index = 4处的字符元素相同,可此时i > 前一个字符的index(index = 0)。这种情况下i不能跳到index = 1处。<br>  故,i在进行跳转时,i = max(前一个相同字符的索引 + 1, i)</p><h2 id="实现思路"><a href="#实现思路" class="headerlink" title="实现思路"></a>实现思路</h2><p>  为了保存字符的索引,我们采用STL库中的map。Java可采用HashMap,原理是相同的。<br>j在遍历整个字符串时,需要不停的更新/插入新的Entry对(不存在当前元素则插入,存在当前元素则更新)。进行更新是为了保留最新的字符索引位置,以便字符跳转时可以跳转到正确的位置。</p><h2 id="实现代码-C"><a href="#实现代码-C" class="headerlink" title="实现代码(C++)"></a>实现代码(C++)</h2><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><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="function"><span class="keyword">int</span> <span class="title">lengthOfLongestSubstring</span><span class="params">(<span class="built_in">string</span> s)</span> </span>{</span><br><span class="line"> <span class="comment">/* 保存字符的索引 */</span></span><br><span class="line"> <span class="built_in">map</span><<span class="keyword">char</span>, <span class="keyword">int</span>> idx;</span><br><span class="line"> <span class="keyword">int</span> ans = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>, j = <span class="number">0</span>; j < s.<span class="built_in">size</span>(); j++){</span><br><span class="line"> <span class="comment">/* 查找索引表中是否在j之前存在相同元素 */</span></span><br><span class="line"> <span class="keyword">if</span>(idx.<span class="built_in">find</span>(s[j]) != idx.<span class="built_in">end</span>()){</span><br><span class="line"> <span class="comment">/* 若存在,并且i比找到的那个索引小,那么就可以让i跳转到那个索引的下一个位置 */</span></span><br><span class="line"> i = <span class="built_in">max</span>(idx[s[j]] + <span class="number">1</span>, i);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/* 更新当前窗口的大小,j - 1 + 1即当前窗口大小 */</span></span><br><span class="line"> ans = <span class="built_in">max</span>(j - i + <span class="number">1</span>, ans);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 不断更新/插入字符索引位置 */</span></span><br><span class="line"> idx[s[j]] = j;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> ans;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<h2 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h2><p>给定一个字符串,请你找出其中不含有重复字符的 <strong>最长子串</strong> 的长度<br><code>[注]子
</summary>
<category term="Algorithm" scheme="http://yoursite.com/categories/Algorithm/"/>
<category term="Sliding Window" scheme="http://yoursite.com/tags/Sliding-Window/"/>
</entry>
<entry>
<title>JVM垃圾收集器小结(一)</title>
<link href="http://yoursite.com/2020/01/23/JVM%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E5%99%A8%E5%B0%8F%E7%BB%93%EF%BC%88%E4%B8%80%EF%BC%89/"/>
<id>http://yoursite.com/2020/01/23/JVM%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E5%99%A8%E5%B0%8F%E7%BB%93%EF%BC%88%E4%B8%80%EF%BC%89/</id>
<published>2020-01-23T08:08:51.936Z</published>
<updated>2020-01-23T09:15:29.776Z</updated>
<content type="html"><![CDATA[<h1 id="JVM垃圾收集器小结(一)"><a href="#JVM垃圾收集器小结(一)" class="headerlink" title="JVM垃圾收集器小结(一)"></a>JVM垃圾收集器小结(一)</h1><h2 id="一、可达性分析算法"><a href="#一、可达性分析算法" class="headerlink" title="一、可达性分析算法"></a>一、可达性分析算法</h2><p>  在可达性分析算法中一个核心概念是 <strong><code>GC Root</code></strong>。<br>  垃圾收集器通过从GC Root对象开始,根据引用关系,向下搜索,搜索过程所走过的路径称为<code>“引用链”(Reference Chain)</code>。如果某个对象到GC Root是有引用链相连的,那么这个对象就是可达的。<br>  在垃圾收集执行过程中,第一步所要做的事情就是<code>初始标记</code>,即进行可达性分析。所有与GC Root之间有引用链相连的对象,都会被标记,最终不会被清理。则其余的<font color='red'>可能</font>被清理。(在并发下,可能还需要重新标记,以保证标记的正确性)</p><p>  什么对象可以被当作GC Root呢?</p><ul><li>虚拟机栈中的引用</li><li>方法区中,类静态属性引用的对象</li><li>方法区中,常量引用的对象(字符串常量池中的引用)</li><li>本地方法栈中JNI(Native 方法)引用的对象</li><li>运行时常量池中引用的对象</li><li>JVM内部引用,如基本数据类型(int, char…)对应的Class对象、系统类加载器、常驻的异常对象(NullPointException, OutOfMemoryException…)</li><li>JMXBean, JVMTI中注册的回调</li></ul><p>在Minor GC发生时,由于存在老年代对新生代的跨代引用,因此有些从GC Root搜索后不可达的对象可能被老年代引用,因此该新生代不需被清理。所以应该在GC Root中来扫描所有老年代对象,但是这样的话开销太大,因此引入了记忆集。在记忆集中,记录了老年代对新生代的跨代引用,因此无须将所有老年代对象加入GC Root,只需将记忆集中老年代对象加入GC Root</p><p>在Old GC发生时,需要将所有年轻代对象加入GC Root进行可达性分析以解决跨代引用的问题。这是因为新生代不稳定,引用变化十分频繁,因此更新记忆集的开销会比较大。</p><h2 id="二、G1与CMS的比较"><a href="#二、G1与CMS的比较" class="headerlink" title="二、G1与CMS的比较"></a>二、G1与CMS的比较</h2><ul><li>G1划分Region,在逻辑上的分代,不要求物理上分代,而CMS是逻辑和物理都分代。</li><li>G1在回收阶段采用标记-整理算法,而CMS采用标记-清除算法。这也说明了CMS在回收阶段可以与用户线程并发而G1需要STW。</li><li>G1可面对堆内存任何部分组成回收集合(Collection Set),即Mixed GC,并且通过设定最大停顿时间来选择收益较高的Region回收。而CMS分old GC和young/minor GC,每次收集都收集所有的垃圾。</li><li>G1在重新标记阶段采用STAB(原始快照)方法,而CMS采用增量更新方法。</li><li>G1的优点:没有内部碎片,可指定最大停顿时间,按收益确定回收集<br>G1的缺点:内存占用高,因为每一个Region都需要一份卡表。而CMS只有一份卡表(记录老年代到新生代的引用);执行负载高,因为G1不但需要写后屏障来更新卡表,还需要写前屏障实现STAB跟踪并发时的指针变化,以减少并发标记和重新标记阶段的消耗。</li></ul>]]></content>
<summary type="html">
<h1 id="JVM垃圾收集器小结(一)"><a href="#JVM垃圾收集器小结(一)" class="headerlink" title="JVM垃圾收集器小结(一)"></a>JVM垃圾收集器小结(一)</h1><h2 id="一、可达性分析算法"><a href="#
</summary>
<category term="Java学习" scheme="http://yoursite.com/categories/Java%E5%AD%A6%E4%B9%A0/"/>
<category term="JVM" scheme="http://yoursite.com/tags/JVM/"/>
<category term="垃圾收集器" scheme="http://yoursite.com/tags/%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E5%99%A8/"/>
</entry>
<entry>
<title>构造器调用顺序</title>
<link href="http://yoursite.com/2020/01/22/%E6%9E%84%E9%80%A0%E5%99%A8%E8%B0%83%E7%94%A8%E9%A1%BA%E5%BA%8F/"/>
<id>http://yoursite.com/2020/01/22/%E6%9E%84%E9%80%A0%E5%99%A8%E8%B0%83%E7%94%A8%E9%A1%BA%E5%BA%8F/</id>
<published>2020-01-22T06:30:11.799Z</published>
<updated>2020-01-22T06:40:16.242Z</updated>
<content type="html"><![CDATA[<h1 id="Java构造器调用顺序"><a href="#Java构造器调用顺序" class="headerlink" title="Java构造器调用顺序"></a>Java构造器调用顺序</h1><p>继承关系图:<br>Meal->Lunch->PortableLunch->SandWich</p><ol><li>递归的调用基类构造器。首先构造继承结构中的根类,然后是下一层导出类,直到当前类</li><li>按声明顺序调用成员的初始化方法</li><li>调用导出类构造器的主体<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.sql.SQLOutput;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Meal</span></span>{</span><br><span class="line"> Apple apple = <span class="keyword">new</span> Apple();</span><br><span class="line"> Meal(){</span><br><span class="line"> System.out.println(<span class="string">"Meal()"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Bread</span></span>{</span><br><span class="line"> Bread(){</span><br><span class="line"> System.out.println(<span class="string">"Bread()"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Apple</span></span>{</span><br><span class="line"> Apple(){</span><br><span class="line"> System.out.println(<span class="string">"Apple()"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Banana</span></span>{</span><br><span class="line"> Banana() {</span><br><span class="line"> System.out.println(<span class="string">"Banana()"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Cheese</span></span>{</span><br><span class="line"> Cheese(){</span><br><span class="line"> System.out.println(<span class="string">"Cheese()"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Lettuce</span></span>{</span><br><span class="line"> Lettuce(){</span><br><span class="line"> System.out.println(<span class="string">"Lettuce()"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Lunch</span> <span class="keyword">extends</span> <span class="title">Meal</span></span>{</span><br><span class="line"> Cheese cheese = <span class="keyword">new</span> Cheese();</span><br><span class="line"> Lunch(){</span><br><span class="line"> System.out.println(<span class="string">"Lunch()"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PortableLunch</span> <span class="keyword">extends</span> <span class="title">Lunch</span></span>{</span><br><span class="line"> Lettuce lettuce = <span class="keyword">new</span> Lettuce();</span><br><span class="line"> PortableLunch(){</span><br><span class="line"> System.out.println(<span class="string">"PortableLunch()"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SandWich</span> <span class="keyword">extends</span> <span class="title">PortableLunch</span></span>{</span><br><span class="line"> <span class="keyword">private</span> Banana banana = <span class="keyword">new</span> Banana();</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">SandWich</span><span class="params">()</span></span>{</span><br><span class="line"> System.out.println(<span class="string">"SandWich()"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> <span class="keyword">new</span> SandWich();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>输出顺序为<figure class="highlight plain"><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">Apple() //Meal类成员apple初始化</span><br><span class="line">Meal() //Meal类构造函数</span><br><span class="line">Cheese() //Lunch类成员cheese初始化</span><br><span class="line">Lunch() //Lunch类构造函数</span><br><span class="line">Lettuce() //PortableLunch类成员lettuce初始化</span><br><span class="line">PortableLunch() //PortableLunch类构造函数</span><br><span class="line">Banana() //SandWich类成员banana初始化</span><br><span class="line">SandWich() //SandWich类构造函数</span><br></pre></td></tr></table></figure></li></ol>]]></content>
<summary type="html">
<h1 id="Java构造器调用顺序"><a href="#Java构造器调用顺序" class="headerlink" title="Java构造器调用顺序"></a>Java构造器调用顺序</h1><p>继承关系图:<br>Meal-&gt;Lunch-&gt;Porta
</summary>
<category term="Java学习" scheme="http://yoursite.com/categories/Java%E5%AD%A6%E4%B9%A0/"/>
<category term="JavaSE" scheme="http://yoursite.com/tags/JavaSE/"/>
</entry>
<entry>
<title>MySQL索引及底层实现</title>
<link href="http://yoursite.com/2020/01/18/mysql%E7%B4%A2%E5%BC%95%E5%8E%9F%E7%90%86/"/>
<id>http://yoursite.com/2020/01/18/mysql%E7%B4%A2%E5%BC%95%E5%8E%9F%E7%90%86/</id>
<published>2020-01-18T14:33:23.906Z</published>
<updated>2020-01-18T16:25:43.415Z</updated>
<content type="html"><![CDATA[<h1 id="MySQL索引及底层实现"><a href="#MySQL索引及底层实现" class="headerlink" title="MySQL索引及底层实现"></a>MySQL索引及底层实现</h1><h2 id="一、什么是索引"><a href="#一、什么是索引" class="headerlink" title="一、什么是索引"></a>一、什么是索引</h2><blockquote><p>“A database index is a data structure that improves the speed of operations in a table”<br>–MySQL Tutorial</p></blockquote><p>将以上索引定义翻译成中文,即<br><font color='red' size=4> 索引是用来提高操作数据表速度的数据结构<br></font></p><h2 id="二、为什么要使用索引"><a href="#二、为什么要使用索引" class="headerlink" title="二、为什么要使用索引"></a>二、为什么要使用索引</h2><p>  在对数据表进行查询时,若所查询的列没有定义索引,MySQL则会进行顺序查找,那么若数据规模为N,查找的时间复杂度为$O(N)$。在数据规模较大时,$O(N)$的时间是达不到所预期的查找效率的,若有千万级的数据需要查询时,可能查询几十秒都是有可能的。因此,使用索引的目的就是为了加快查询速度。</p><h2 id="三、MySQL索引的数据结构"><a href="#三、MySQL索引的数据结构" class="headerlink" title="三、MySQL索引的数据结构"></a>三、MySQL索引的数据结构</h2><p>  在MySQL中,索引采用B+树的数据结构进行组织。下面与几种常见的数据结构做对比,来分析为什么要用B+树,而不是其它数据结构。</p><ol><li><p>二叉搜索树<br>若采用二叉搜索树对一列数据建立索引整体上的确能提高搜索的效率,但在一些特殊数据上(递增,或递减),二叉搜索树的高度逐渐增加,导致其搜索效率逐渐低下,接近于顺序查找甚至有可能完全退化成链表,查询效率退化成顺序查询</p></li><li><p>二叉平衡树<br>  既然高度会对搜索效率影响,那使用平衡树(AVL,红黑树等)是不是就能更好的解决问题了呢?答案是肯定的,平衡树可以很好的控制整棵树的高度在$O(log_2(N))$的数量级上,进而将查询效率控制在接近二分查找的效率。<br>  但是,为什么MySQL不采用这种方式组织索引呢?我们想,数据库中文件可以基本分为“数据表”,“数据文件”,“索引文件”,这三种文件没有人会把他们都存放在内存里。实际上,它们是存放在磁盘中的(innoDB将索引文件和数据文件存放在一起,即.ibd文件,MyISAM数据文件存放在.MYD中,索引文件存放.MID中)。数据规模较大时,平衡树的高度能达到20余层,由于数据存放在磁盘中,因此在进行数据比较时需要先进行磁盘I/O从磁盘中读取节点数据到内存中,再进行比对。由于磁盘读写是以块为单位的,一个节点显然很难达到一个块那么大,更别要求整个块都是节点数据了。因此20余层平衡树需要做至多20余次磁盘I/O。从下图中可以看出,一次磁盘读取速度要比一次内存读取速度慢上至少50倍,这对于数据库查找的开销实在是太大了。因此要想办法减少磁盘I/O次数。</p><div align=center><img src="https://www.storagereview.com/images/ramdiskarticle1.jpg"/></div><center>图1 磁盘与RAM读写性能</center><br/></li><li><p>B树<br>  B树相比于二叉平衡树来说,其每一个节点都可以存放多个数据元素,如下图。相比于二叉平衡树来说,B树可以一次性读取一个节点,存满整个磁盘块。在Windows操作系统上,每个磁盘块的大小为4KB(4096Bytes),MySQL中,定义一个索引节点大小为16KB(16384Bytes),因此一次磁盘I/O可以读取整整4个磁盘块。再继续分析,由于每一个节点包含许多数据元素,因此可以有效减少树的高度,从而有效减少磁盘I/O次数。<br>  B树相比于二叉平衡树又有了性能上的改进,那为什么还要用B+树呢,B树相比于B+树,又有哪些性能上还可以提高的点呢?<br>  我们来继续看这张图,B树的每一个节点中,包含一个数字和一个data,其中数字就是索引项,data就是本条索引项所对应的所有数据(除所索引项)。换句话说,它们一起构成了数据表中的一个记录。我们可以知道,当一个数据库的某些数据表中,可能存在许多的数据列,因此一个记录就要占很大的空间,我们保守平均按1KB来算,忽略节点中的指针域,每个节点最多也就存放16个数据。对于百万,千万级的数据来讲,其树高也达到了<br>$O(log_{16}(N))$。可以发现,树的高度相对来说还是没有很好的控制(相比于B+树)。因此有必要引入B+树了</p><img src="https://img-blog.csdnimg.cn/20190614160951754.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3o2OTE4Mzc4Nw==,size_16,color_FFFFFF,t_70"/><center>图2 B树示意图</center></li></ol><h2 id="四、为什么要使用B-树作为索引的数据结构"><a href="#四、为什么要使用B-树作为索引的数据结构" class="headerlink" title="四、为什么要使用B+树作为索引的数据结构"></a>四、为什么要使用B+树作为索引的数据结构</h2><p>  如下图(以InnoDB引擎为例),我们可以看到,首先,B+树中只有叶子节点存放data,且不存放指针。这样做,可以将B+树每个节点中存放更多的索引项,进而控制的树的高度。我们可以计算一下,按照一个索引项4B,一个指针域6B,每一个非叶节点就可以存放$16384/(4+6)=1638$个索引项,每一个叶子节点按1KB来算,可以存放16个项。因此若B+树为3层,总共可以存放$1638<em>1638</em>16=42,928,704$个数据,已经可以满足千万级别的数据了。事实上,MySQL中B+树的层数可以控制到小于4层的,进而很好的控制了磁盘I/O次数。<br>  其次,我们可以发现,B+树的叶子节点之间是有指针连接点(图片上是单向指针,实际上是双向指针)。这样做的好处是可以解决范围查询,如select salary from T where salary > 10000,MySQL先查找到不大于10000的第一个元素,进而通过叶子节点的指针一直遍历到最后一个元素。<br><img src="https://img-blog.csdnimg.cn/2019061416104528.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3o2OTE4Mzc4Nw==,size_16,color_FFFFFF,t_70"/></p><center>图3 B+树(InnoDB聚集索引)示意图</center><h2 id="五、聚集索引和非聚集索引"><a href="#五、聚集索引和非聚集索引" class="headerlink" title="五、聚集索引和非聚集索引"></a>五、聚集索引和非聚集索引</h2><p>  InnoDB采用支持聚集索引的实现方式,MyISAM只采用非聚集索引的实现方式。它们两者的区别在于,InnoDB将data存放在B+树的叶子节点中,而MyISAM将data存放在单独的一个.MYD文件中,B+树的叶子节点中存放访问data的指针。相比较而言,聚集索引的速度比较快,因为它可以直接找到目标data元素,而非聚集索引需要进行额外的磁盘I/O寻找相应的数据位置。</p><img src="https://ask.qcloudimg.com/http-save/yehe-1752328/332nzio50h.png?imageView2/2/w/1620"><center>图4 MyISAM存储引擎示意图</center>]]></content>
<summary type="html">
<h1 id="MySQL索引及底层实现"><a href="#MySQL索引及底层实现" class="headerlink" title="MySQL索引及底层实现"></a>MySQL索引及底层实现</h1><h2 id="一、什么是索引"><a href="#一、什么是索
</summary>
<category term="MySQL" scheme="http://yoursite.com/categories/MySQL/"/>
<category term="MySQL索引" scheme="http://yoursite.com/tags/MySQL%E7%B4%A2%E5%BC%95/"/>
</entry>
<entry>
<title>JDK动态代理实现与原理</title>
<link href="http://yoursite.com/2020/01/16/JDK%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86/"/>
<id>http://yoursite.com/2020/01/16/JDK%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86/</id>
<published>2020-01-16T03:44:02.283Z</published>
<updated>2020-02-17T06:33:18.755Z</updated>
<content type="html"><![CDATA[<h1 id="JDK动态代理实现与原理"><a href="#JDK动态代理实现与原理" class="headerlink" title="JDK动态代理实现与原理"></a>JDK动态代理实现与原理</h1><h2 id="一、代理模式"><a href="#一、代理模式" class="headerlink" title="一、代理模式"></a>一、代理模式</h2><p>  <strong><code>代理模式(Proxy Pattern)</code></strong> 是程序设计中的一种设计模式,结构如下图。<strong>代理类</strong>为<strong>委托(实现)类</strong>提供代理,以控制对<strong>委托类对象</strong>的访问。<strong>代理类</strong>负责为<strong>委托类</strong>预处理消息,过滤并转发消息,以及进行消息被<strong>委托类</strong>执行后的后续处理。<br>  为了保持行为的一致,<strong>代理类</strong>和<strong>委托类</strong>通常实现同样的接口,通过<strong>代理类</strong>可以对<strong>委托类对象</strong>进行访问,也可以隐藏和保护<strong>委托类对象</strong>。</p><div align="center"><img src="http://q5cad0flf.bkt.clouddn.com/2020-02-17-14-32-05.png"><br>图1 代理模式的结构示意图</div><div align="center"><img src="http://q5cad0flf.bkt.clouddn.com/2020-02-17-14-32-49.png"><br>图2 代理模式的UML图</div><h2 id="二、JDK动态代理"><a href="#二、JDK动态代理" class="headerlink" title="二、JDK动态代理"></a>二、JDK动态代理</h2><p>使用Java中的动态代理,需要使用以下两个类/接口。</p><ol><li><font color="blue">interface</font> InvocationHandler<br> 该接口中只定义了一个方法:<br> <strong><font color="purple">public</font> object <font color="blue">invoke</font>(Object obj, Method method, Object[] args);</strong><br> obj:指代理类对象,<br> method:指被代理的方法<br> args:该代理方法method的参数</li><li><font color="blue">class</font> Proxy<br> 该类为动态代理类,包含以下成员函数;<br> <strong>protected Proxy(InvocationHandler h)</strong><br> 构造函数<br><br> <strong>static Class getProxyClass (ClassLoader loader, Class[] interfaces)</strong><br> 获得一个代理类加载器<br> loader:代理类的类加载器<br> interfaces:代理类实现的接口类型数组<br><br> <strong>static Object newProxyInstance(ClassLoader loader,<br> Class[] interfaces,<br> InvocationHandler h)</strong><br> 返回代理类的一个实例。使用委托类的引用指向此实例,即可通过invoke调用委托类在接口中声明过的方法。<br> loader:需要代理的类的类加载器<br> interfaces:需要代理的类所实现的接口<br> h:实现InvocaionHandler接口的类的实例,代理类对象将通过invoke来调用委托类方法以及其增强功能。</li></ol><p>因此,在使用动态代理时,必须实现InvocationHandler接口,也就是实现其中的invoke方法,然后通过Proxy类中的静态方法newProxyInstance创建一个代理类实例,</p><h2 id="三、使用方法(基于AOP)"><a href="#三、使用方法(基于AOP)" class="headerlink" title="三、使用方法(基于AOP)"></a>三、使用方法(基于AOP)</h2><p>首先,创建一个ICustomerDao接口<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lrq.AOP;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ICustomerDao</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">add</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">delete</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">update</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">find</span><span class="params">()</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><br>接着对此接口进行实现<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><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.lrq.AOP;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * CustomerDao类,实现了ICustomerDao接口中的add,delete,update,find方法</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CustomerDao</span> <span class="keyword">implements</span> <span class="title">ICustomerDao</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">add</span><span class="params">()</span></span>{</span><br><span class="line"> System.out.println(<span class="string">"add..."</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">delete</span><span class="params">()</span></span>{</span><br><span class="line"> System.out.println(<span class="string">"delete..."</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">update</span><span class="params">()</span></span>{</span><br><span class="line"> System.out.println(<span class="string">"update..."</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">find</span><span class="params">()</span></span>{</span><br><span class="line"> System.out.println(<span class="string">"find..."</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><br>创建一个切面类Aspect(用于增强CustomerDao功能)<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lrq.AOP;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 切面类,用于对CustomerDao类的功能增强</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">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Aspect</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">before</span><span class="params">()</span></span>{</span><br><span class="line"> System.out.println(<span class="string">"before execute"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">after</span><span class="params">()</span></span>{</span><br><span class="line"> System.out.println(<span class="string">"after execute"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><br>最后基于Spring IOC的思想,创建一个工厂类BeanFactory作为代理类,用切面类Aspect对CustomerDao功能进行增强(可以理解为,CustomerDao为委托类,代理类中增加了Aspect类中的功能)<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><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.lrq.AOP;</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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BeanFactory</span> </span>{</span><br><span class="line"> <span class="keyword">static</span> CustomerDao customerDao = <span class="keyword">new</span> CustomerDao();</span><br><span class="line"> <span class="keyword">static</span> Aspect aspect = <span class="keyword">new</span> Aspect();</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></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> ICustomerDao <span class="title">getBean</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">return</span> (ICustomerDao) Proxy.newProxyInstance(</span><br><span class="line"> <span class="comment">/* 获取委托类的类加载器和实现的接口 */</span></span><br><span class="line"> CustomerDao<span class="class">.<span class="keyword">class</span>.<span class="title">getClassLoader</span>(),</span></span><br><span class="line"><span class="class"> <span class="title">CustomerDao</span>.<span class="title">class</span>.<span class="title">getInterfaces</span>(),</span></span><br><span class="line"><span class="class"> /* 使用匿名内部类实现<span class="title">InvocationHandler</span>接口 */</span></span><br><span class="line"><span class="class"> <span class="title">new</span> <span class="title">InvocationHandler</span>() </span>{</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Object <span class="title">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable </span>{</span><br><span class="line"> <span class="comment">/* aspect.before()和aspect.after()是用来增强的功能 */</span></span><br><span class="line"> aspect.before();</span><br><span class="line"> <span class="comment">/* 调用method.invoke,第一个参数是委托类对象*/</span></span><br><span class="line"> Object returnValue = method.invoke(customerDao, args);</span><br><span class="line"> aspect.after();</span><br><span class="line"> <span class="keyword">return</span> returnValue;</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"> jdk8之后可以使用lambda表达式功能,使得代码更简介</span></span><br><span class="line"><span class="comment"> 其中实现InvocationHandler()接口的内部类可以用如下方式替换</span></span><br><span class="line"><span class="comment"> (proxy, method, args) -> {</span></span><br><span class="line"><span class="comment"> aspect.before();</span></span><br><span class="line"><span class="comment"> </span></span><br><span class="line"><span class="comment"> Object returnValue = method.invoke(customerDao, args);</span></span><br><span class="line"><span class="comment"> aspect.after();</span></span><br><span class="line"><span class="comment"> return returnValue;</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><br><span class="line">}</span><br></pre></td></tr></table></figure></p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.lrq.AOP;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.junit.Test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="keyword">static</span> org.junit.Assert.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AOPTest</span> </span>{</span><br><span class="line"> <span class="meta">@Test</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">()</span></span>{</span><br><span class="line"> ICustomerDao customerDao = BeanFactory.getBean();</span><br><span class="line"> customerDao.add();</span><br><span class="line"> customerDao.delete();</span><br><span class="line"> customerDao.find();</span><br><span class="line"> customerDao.update();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><br>输出结果如下</p><blockquote><p>before execute<br>add…<br>after execute<br>before execute<br>delete…<br>after execute<br>before execute<br>find…<br>after execute<br>before execute<br>update…<br>after execute</p></blockquote><p>可见在add, delete, find, update函数前后都添加了增强功能</p><h2 id="四、动态代理的实现"><a href="#四、动态代理的实现" class="headerlink" title="四、动态代理的实现"></a>四、动态代理的实现</h2><p>动态代理实现的关键在于:</p><blockquote><p>Proxy.newProxyInstance(loader, interface, <font color="red">handler</font>);</p></blockquote><p>调用这个方法后,委托类引用会指向代理对象,当其调用真实的方法时,会自动跳转至代理对象关联的<font color="red">handler</font>中的invoke方法中,进行调用。也就是说,当调用customerDao.add()方法时,会自动去调用实现InvocationHandler接口的类中的invoke方法(在上面是通过匿名内部类实现的)</p><blockquote><p>参考文章:<a href="https://blog.csdn.net/jiankunking/article/details/52143504" target="_blank" rel="noopener">https://blog.csdn.net/jiankunking/article/details/52143504</a></p></blockquote>]]></content>
<summary type="html">
<h1 id="JDK动态代理实现与原理"><a href="#JDK动态代理实现与原理" class="headerlink" title="JDK动态代理实现与原理"></a>JDK动态代理实现与原理</h1><h2 id="一、代理模式"><a href="#一、代理模式"
</summary>
<category term="Java学习" scheme="http://yoursite.com/categories/Java%E5%AD%A6%E4%B9%A0/"/>
<category term="设计模式" scheme="http://yoursite.com/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
<category term="jdk动态代理" scheme="http://yoursite.com/tags/jdk%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86/"/>
<category term="Spring AOP实现原理" scheme="http://yoursite.com/tags/Spring-AOP%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/"/>
</entry>
<entry>
<title>Java内存区域与内存溢出异常day02</title>
<link href="http://yoursite.com/2020/01/12/JVM%E8%87%AA%E5%8A%A8%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86day02/"/>
<id>http://yoursite.com/2020/01/12/JVM%E8%87%AA%E5%8A%A8%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86day02/</id>
<published>2020-01-11T16:12:03.169Z</published>
<updated>2020-01-23T09:12:00.155Z</updated>
<content type="html"><![CDATA[<h1 id="Java虚拟机——自动内存管理"><a href="#Java虚拟机——自动内存管理" class="headerlink" title="Java虚拟机——自动内存管理"></a>Java虚拟机——自动内存管理</h1><h2 id="第二章-Java内存区域与内存溢出异常"><a href="#第二章-Java内存区域与内存溢出异常" class="headerlink" title="第二章 Java内存区域与内存溢出异常"></a>第二章 Java内存区域与内存溢出异常</h2><hr><h3 id="2-3-HotSpot-虚拟机对象探秘"><a href="#2-3-HotSpot-虚拟机对象探秘" class="headerlink" title="2.3 HotSpot 虚拟机对象探秘"></a>2.3 HotSpot 虚拟机对象探秘</h3><h4 id="2-3-1-对象的创建"><a href="#2-3-1-对象的创建" class="headerlink" title="2.3.1 对象的创建"></a>2.3.1 对象的创建</h4><p><strong><font size=4 color='red'>在JVM中,一个对象(普通JAVA对象)被创建过程如下:</font></strong></p><ol><li>当Java虚拟机遇到一条字节码new指令时,首先检查此参数是否能在<code>常量池</code>中定位到一个类的符号引用</li><li>检查符号引用代表的类是否已经被加载、解析和初始化过,若没有则执行类加载过程。</li><li>给新生对象在Java堆中分配内存</li><li>并发时采用CAS与失败重试来保证分配内存时的线程安全。</li><li>对将生成的对象进行必要的设置(哪个类的实例,如何找到类的Meta Data,对象的HashCode等)</li></ol><p>  Java堆如果是<code>绝对规整</code>的,即通过一个指针将空闲内存和可用内存分开,那么则通过<code>指针碰撞(Bump The Pointer)</code>来分配内存。如果Java堆是不规整的,即空闲内存和可用内存交错在一起,那么则采用<code>空闲列表(Free List)</code>来分配内存。<br><strong>[注]选择哪种方式是由Java堆是否规整来决定的,而Java堆是否规整又由GC是否带有<code>空间压缩整理(Compact)</code>的能力决定。因此,当使用Serial, ParNew等带压缩整理的收集器,则采用指针碰撞;当使用CMS这种基于<code>清除(Sweep)</code>算法的收集器时,理论上就只能采用复杂的空闲列表来分配内存。</strong></p><p>  并发时,可能出现正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况,因此<br><strong><font size=4 color='red'>在并发情况下,有两种方案可以保证线程安全:</font></strong></p><ol><li>需要使用<code>CAS(Compare and Swap)</code>配上<code>失败重试</code>的方式来保证更新操作的原子性</li><li>把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存即<code>TLAB(Thread Local Allocation Buffer)</code>,哪个线程要分配内存,就在哪个线程的本地缓冲区分配,只用本地缓冲区用完了,分配新的缓冲区时才需要同步锁定。</li></ol><p>在虚拟机的视角中,对象已经建立完成,而在Java程序的视角来看,对象创建才刚刚开始——构造函数,Class文件中的<init>()方法还没有执行,所有的字段都为默认的零值,对象需要的其他资源和状态信息也还没有按照预定的意图构造好。一般来说new指令之后就会接着执行<init>()方法,按程序员的意愿对对象进行初始化。</p><hr><h4 id="2-3-2-对象的内存布局"><a href="#2-3-2-对象的内存布局" class="headerlink" title="2.3.2 对象的内存布局"></a>2.3.2 对象的内存布局</h4><p><strong><font size=4 color='red'>在HotSpot虚拟机中,对象在堆内存中的存储布局可以划分为三个部分:</font></strong></p><ol><li>对象头(Header)</li><li>实例数据(Instance Data)</li><li>对齐填充(Padding)</li></ol><p>首先,对于对象头包含了两部分信息。</p><ol><li>用于存储对象自身的运行时数据<br>如HashCode、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。这部分在32位和64位虚拟机中分别为32bit和64bit,称为<code>Mark Word</code></li><li>类型指针<br>类型指针即对象指向它类型元数据的指针,Java虚拟机通过这个指针来确定该对象是哪个类的实例(并不一定所有虚拟机实现都保留此指针)。</li><li>长度数据(Java数组)<br>若对象是一个Java数组,则在对象头中还必须有一块记录数组长度的数据。</li></ol><p>实例数据是对象真正存储的有效信息,即我们在程序代码里面所定义的各种类型字段内容,无论是从父类继承下来的,还是在子类中定义的。</p><p>对其填充并不是必然存在的,也没有特别的含义,仅仅起占位符的作用。HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,也就是任何对象的大小都必须是8字节的整数倍。(对象头已经被精心设计成正好是8字节的倍数)</p><hr><h4 id="2-3-3对象的访问定位"><a href="#2-3-3对象的访问定位" class="headerlink" title="2.3.3对象的访问定位"></a>2.3.3对象的访问定位</h4><p>创建对象自然是为了后续使用该对象。</p><blockquote><p>Java虚拟机规范称,Java程序会通过栈上的reference数据来操作堆上的具体对象。</p></blockquote><p>由于知识定义了reference是一个指向对象的引用,并没有定义这个引用还应该通过什么方式区定位、访问堆中的具体位置,因此实现由虚拟机而定。<br><strong><font size=4 color='red'>主流的实现方式有以下两种:</font></strong><br>1.句柄访问<br>Java堆中划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例与类型数据各自具体的地址信息<br><strong>优势:reference中存储的是稳定句柄地址,在对象被移动时指挥改变句柄中的实例数据指针,refenence本身不需要更改</strong><br>2.直接指针访问<br>Java堆中对象的内存布局就必须考虑如何放置访问类型数据的相关信息,reference中存储的直接就是对象地址,如果知识访问对象本身的话,就不需要多一次间接访问的开销。<br><strong>优势:速度更快,节省了一次指针定位的时间开销</strong><br><strong>[注]在HotSpot虚拟机中,主要使用<code>直接指针访问</code>的方式进行对象访问(有例外情况,如果使用了Shenandoah收集器可能会有一次额外的转发),但从整个软件开发的范围来看,在各种语言、框架中使用句柄来访问的情况十分常见</strong></p>]]></content>
<summary type="html">
<h1 id="Java虚拟机——自动内存管理"><a href="#Java虚拟机——自动内存管理" class="headerlink" title="Java虚拟机——自动内存管理"></a>Java虚拟机——自动内存管理</h1><h2 id="第二章-Java内存区域与
</summary>
<category term="Java学习" scheme="http://yoursite.com/categories/Java%E5%AD%A6%E4%B9%A0/"/>
<category term="JVM" scheme="http://yoursite.com/tags/JVM/"/>
</entry>
<entry>
<title>Java内存区域与内存溢出异常day01</title>
<link href="http://yoursite.com/2020/01/10/JVM%E8%87%AA%E5%8A%A8%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86day01/"/>
<id>http://yoursite.com/2020/01/10/JVM%E8%87%AA%E5%8A%A8%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86day01/</id>
<published>2020-01-10T15:44:14.194Z</published>
<updated>2020-01-23T09:11:51.715Z</updated>
<content type="html"><![CDATA[<h1 id="Java虚拟机——自动内存管理"><a href="#Java虚拟机——自动内存管理" class="headerlink" title="Java虚拟机——自动内存管理"></a>Java虚拟机——自动内存管理</h1><h2 id="第二章-Java内存区域与内存溢出异常"><a href="#第二章-Java内存区域与内存溢出异常" class="headerlink" title="第二章 Java内存区域与内存溢出异常"></a>第二章 Java内存区域与内存溢出异常</h2><hr><h3 id="2-2-运行时数据区域"><a href="#2-2-运行时数据区域" class="headerlink" title="2.2 运行时数据区域"></a>2.2 运行时数据区域</h3><h4 id="2-2-2-Java虚拟机栈(VM-Stack)"><a href="#2-2-2-Java虚拟机栈(VM-Stack)" class="headerlink" title="2.2.2 Java虚拟机栈(VM Stack)"></a>2.2.2 Java虚拟机栈(VM Stack)</h4><ul><li>Java虚拟机栈是<strong>线程私有的</strong>。</li><li>虚拟机栈的“栈”更多情况下指的是其<code>局部变量表</code>部分。</li><li>局部变量表中存放了编译期可知的Java虚拟机基本数据类型(int, long, double…),它们以局部变量槽(Slot)来表示,其中64位长度的long和double类型的数据会占用两个变量槽,其余的数据类型只占用一个</li><li>StackOverflowError异常:线程请求的<strong>栈深度大于虚拟机所允许的深度</strong>。<br>OutOfMemoryError异常:若Java虚拟机栈空间可以动态扩展,<strong>栈扩展时无法申请到足够的内存时发生</strong>。<blockquote><p><strong>[注]虚拟机真正使用多大内存空间(一个slot占用32bit或64bit)来实现一个变量槽,这是完全由具体的虚拟机自行决定的事情。</strong></p></blockquote></li></ul><hr><h4 id="2-2-3-本地方法栈(Native-Method-Stacks)"><a href="#2-2-3-本地方法栈(Native-Method-Stacks)" class="headerlink" title="2.2.3 本地方法栈(Native Method Stacks)"></a>2.2.3 本地方法栈(Native Method Stacks)</h4><ul><li>与VM Stack发挥的作用非常相似,VM Stack为虚拟机执行Java方法(字节码)服务,本地方法则是为本地方法(Native)服务</li></ul><hr><h4 id="2-2-4-Java堆(Java-Heap)"><a href="#2-2-4-Java堆(Java-Heap)" class="headerlink" title="2.2.4 Java堆(Java Heap)"></a>2.2.4 Java堆(Java Heap)</h4><ul><li>为所有Java线程共享的一片区域</li><li>几乎所有对象实例都在堆中分配内存</li><li>是垃圾收集器管理的内存区域,因此也被称为<code>GC堆(Garbage Collected Heap)</code></li><li>Java堆既可以被实现成固定大小的,也可以是可扩展的。在当前主流Java虚拟机中都是按照可扩展来实现的(通过-Xmx和-Xms设定)。<strong>如果Java堆中没有内存完成实例分配,并且堆再也无法扩展时,Java虚拟机将会抛出OutOfMemoryError异常</strong></li></ul><hr><h4 id="2-2-5-方法区(Method-Area)(非堆)"><a href="#2-2-5-方法区(Method-Area)(非堆)" class="headerlink" title="2.2.5 方法区(Method Area)(非堆)"></a>2.2.5 方法区(Method Area)(非堆)</h4><ul><li>为所有Java线程共享的内存区域</li><li>用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据,</li><li>一般方法区不进行内存回收,若回收,主要是针对常量池的回收和堆类型的卸载,但结果往往比较难令人满意,尤其是堆类型的卸载,条件十分苛刻(但是还是有必要的)。</li><li><blockquote><p>《Java虚拟机规范》规定,如果方法去无法满足新的内存分配需求,将抛出OutOfMemoryError异常</p></blockquote></li></ul><hr><h4 id="2-2-6-运行时常量池(Runtime-Constant-Pool)"><a href="#2-2-6-运行时常量池(Runtime-Constant-Pool)" class="headerlink" title="2.2.6 运行时常量池(Runtime Constant Pool)"></a>2.2.6 运行时常量池(Runtime Constant Pool)</h4><ul><li>是方法区的一部分</li><li>Class文件中除了有<strong>类的模板、字段、方法、接口等描述信息外</strong>,还有一项信息是<code>常量池表(Constant Pool Table)</code>,用于存放编译器生成的各种<strong>字面量</strong>和<strong>符号引用</strong>,这部分内容将在类加载之后存放到方法区的运行时常量池中。</li><li>与Class文件常量池的区别:运行时常量池具备动态性,Java语言的常量不一定只有编译期才能产生,并非预置入Class文件中的常量池的内容才能进入运行时常量池,<strong>运行期间也可以将新的常量放入池中</strong>。</li><li>既然RCP是方法区的一部分,因此常量池无法再申请到内存时,会抛出OutOfMemoryError异常</li></ul><hr><h4 id="2-2-7-直接内存(Direct-Memory)"><a href="#2-2-7-直接内存(Direct-Memory)" class="headerlink" title="2.2.7 直接内存(Direct Memory)"></a>2.2.7 直接内存(Direct Memory)</h4><ul><li>并不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域,但是这部分的内存频繁的使用,也可能导致OutOfMemoryError异常出现。</li><li>JDK 1.4中新加入的NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。</li></ul>]]></content>
<summary type="html">
<h1 id="Java虚拟机——自动内存管理"><a href="#Java虚拟机——自动内存管理" class="headerlink" title="Java虚拟机——自动内存管理"></a>Java虚拟机——自动内存管理</h1><h2 id="第二章-Java内存区域与
</summary>
<category term="Java学习" scheme="http://yoursite.com/categories/Java%E5%AD%A6%E4%B9%A0/"/>
<category term="JVM" scheme="http://yoursite.com/tags/JVM/"/>
</entry>
<entry>
<title>mybatis环境搭建</title>
<link href="http://yoursite.com/2020/01/10/day01_mybatis%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/"/>
<id>http://yoursite.com/2020/01/10/day01_mybatis%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/</id>
<published>2020-01-10T03:21:05.000Z</published>
<updated>2020-01-23T09:11:39.301Z</updated>
<content type="html"><![CDATA[<h1 id="mybatis环境搭建"><a href="#mybatis环境搭建" class="headerlink" title="mybatis环境搭建"></a>mybatis环境搭建</h1><hr><h3 id="mybatis环境搭建步骤"><a href="#mybatis环境搭建步骤" class="headerlink" title="mybatis环境搭建步骤"></a><font color="blue">mybatis环境搭建步骤</font></h3><ol><li>创建maven工程并导入坐标</li><li>创建实体类和dao接口</li><li>创建mybatis的主配置文件<code>SqlMapConfig.xml</code></li><li>创建映射配置文件<code>IUserDao.xml</code></li></ol><hr><h3 id="mybatis环境搭建注意事项"><a href="#mybatis环境搭建注意事项" class="headerlink" title="mybatis环境搭建注意事项"></a><font color="blue">mybatis环境搭建注意事项</font></h3><ol><li>创建<code>IUserDao.xml</code> 和 <code>IUserDao.java</code>时名称是为了和我们之前的知识保持一致。在mybatis中它把持久层的操作接口名称和映射文件叫做<code>Mapper</code>。因此 <code>IUserDao</code> 和 <code>IUserMapper</code>是一样的</li><li>在<code>IntelliJ Idea</code>中创建目录的时候,它和包是不一样的。包在创建时,<code>com.lrq.dao</code>是<strong>三级结构</strong>,但目录在创建时,是<strong>一级结构</strong></li><li><strong>mybatis的映射配置文件位置必须和<code>dao</code>接口的包结构相同</strong>(因此我们需要分三级创建目录,保证<code>IUserMapper(Dao).xml</code>文件所在结构和<code>Dao.java</code>所在结构相同)</li><li>映射文件的mapper标签的<code>namspace</code>属性的取值必须是<code>dao</code>接口的<strong>全限定类名</strong></li><li>映射配置文件的操作配置(select或其他),id的属性取值必须是dao接口的<strong>方法名</strong></li></ol><p><strong>当我们遵循了3, 4, 5点之后,在开发中就无须再去写dao的实现类</strong></p>]]></content>
<summary type="html">
<h1 id="mybatis环境搭建"><a href="#mybatis环境搭建" class="headerlink" title="mybatis环境搭建"></a>mybatis环境搭建</h1><hr>
<h3 id="mybatis环境搭建步骤"><a href="
</summary>
<category term="Java学习" scheme="http://yoursite.com/categories/Java%E5%AD%A6%E4%B9%A0/"/>
<category term="mybatis" scheme="http://yoursite.com/tags/mybatis/"/>
</entry>
<entry>
<title>小鸡你别生气了,大狗错了啵啵啵</title>
<link href="http://yoursite.com/2020/01/09/%E5%B0%8F%E9%B8%A1%E4%BD%A0%E5%88%AB%E7%94%9F%E6%B0%94%E4%BA%86%EF%BC%8C%E5%A4%A7%E7%8B%97%E9%94%99%E4%BA%86%E5%95%B5%E5%95%B5%E5%95%B5/"/>
<id>http://yoursite.com/2020/01/09/%E5%B0%8F%E9%B8%A1%E4%BD%A0%E5%88%AB%E7%94%9F%E6%B0%94%E4%BA%86%EF%BC%8C%E5%A4%A7%E7%8B%97%E9%94%99%E4%BA%86%E5%95%B5%E5%95%B5%E5%95%B5/</id>
<published>2020-01-08T17:00:27.000Z</published>
<updated>2020-01-08T17:00:27.359Z</updated>
<summary type="html">
</summary>
</entry>
<entry>
<title>Third</title>
<link href="http://yoursite.com/2020/01/09/Third/"/>
<id>http://yoursite.com/2020/01/09/Third/</id>
<published>2020-01-08T16:53:00.000Z</published>
<updated>2020-01-08T16:53:00.208Z</updated>
<summary type="html">
</summary>
</entry>
</feed>