Repository navigation
Expand file tree
/
Copy pathJavamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html
More file actions
246 lines (245 loc) · 132 KB
/
Copy pathJavamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html
File metadata and controls
246 lines (245 loc) · 132 KB
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
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="generator" content="VuePress 2.0.0-rc.14">
<script>
(function() {
const userMode = localStorage.getItem('vuepress-reco-color-scheme') || 'auto';
const systemDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
if (userMode === 'dark' || (userMode === 'auto' && systemDarkMode)) {
document.documentElement.classList.toggle('dark', true);
}
})();
</script>
<title>接口、lambda表达式与内部类 | Yunge Hu 's Blog</title><meta name="description" content="Just playing around">
<link rel="preload" href="/assets/style-DFiSfBVn.css" as="style"><link rel="stylesheet" href="/assets/style-DFiSfBVn.css">
<link rel="modulepreload" href="/assets/app-B51tXhJU.js"><link rel="modulepreload" href="/assets/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html-B4BIhUeh.js">
<link rel="prefetch" href="/assets/timeline.html-CDRCyYXN.js" as="script"><link rel="prefetch" href="/assets/posts.html-Cm3Bj9S1.js" as="script"><link rel="prefetch" href="/assets/friendship-link.html-CPYWbDf2.js" as="script"><link rel="prefetch" href="/assets/1.html-BvGA5exa.js" as="script"><link rel="prefetch" href="/assets/1.html-CQiPtbSY.js" as="script"><link rel="prefetch" href="/assets/1.html-B91OJGi4.js" as="script"><link rel="prefetch" href="/assets/1.html-Tfqc0eqa.js" as="script"><link rel="prefetch" href="/assets/1.html-DkR1aWCw.js" as="script"><link rel="prefetch" href="/assets/1.html-9JnLqyKk.js" as="script"><link rel="prefetch" href="/assets/2.html-Bgp2cdo4.js" as="script"><link rel="prefetch" href="/assets/3.html-BrEt4hPg.js" as="script"><link rel="prefetch" href="/assets/index.html-BKyJy_kV.js" as="script"><link rel="prefetch" href="/assets/Install-Neo4j-in-Ubuntu-and-Use-Gremlin.html-D3zJqoKu.js" as="script"><link rel="prefetch" href="/assets/Neo4j.html-RU17f5ie.js" as="script"><link rel="prefetch" href="/assets/index.html-DTD_Tf27.js" as="script"><link rel="prefetch" href="/assets/README.zh.html-C95KaJWV.js" as="script"><link rel="prefetch" href="/assets/Javajibenchengxushejijiegou.html-Cop84p5k.js" as="script"><link rel="prefetch" href="/assets/Javamianxiangduixiangchengxusheji——duixiangyulei.html-RHrWrJ8D.js" as="script"><link rel="prefetch" href="/assets/Javamianxiangduixiangchengxusheji——jicheng.html-CDNHmPwq.js" as="script"><link rel="prefetch" href="/assets/bingfa.html-BObwH6f-.js" as="script"><link rel="prefetch" href="/assets/yichangduanyanherizhi.html-Bw2Q646T.js" as="script"><link rel="prefetch" href="/assets/fanxingyujihe.html-ChyCepcN.js" as="script"><link rel="prefetch" href="/assets/index.html-B_tI2L4g.js" as="script"><link rel="prefetch" href="/assets/suoyincaozuo.html-M9SgPFiv.js" as="script"><link rel="prefetch" href="/assets/lab0.html-DQt1B-xd.js" as="script"><link rel="prefetch" href="/assets/lab1.html-Dvm30kLN.js" as="script"><link rel="prefetch" href="/assets/lab2.html-gAWXyUFt.js" as="script"><link rel="prefetch" href="/assets/lab3.html-LQVGolsr.js" as="script"><link rel="prefetch" href="/assets/lab4.html-CF_2B461.js" as="script"><link rel="prefetch" href="/assets/lab5.html-2WxLf5I9.js" as="script"><link rel="prefetch" href="/assets/lab6.html-ueb45YH9.js" as="script"><link rel="prefetch" href="/assets/index.html-CjBzVZj0.js" as="script"><link rel="prefetch" href="/assets/404.html-Dk0UZDtj.js" as="script"><link rel="prefetch" href="/assets/Valine.min-Dm4Ijz6H.js" as="script"><link rel="prefetch" href="/assets/giscus-2a044aea-CdPDkb7_.js" as="script">
</head>
<body>
<div id="app"><!--[--><div class="theme-container series--no show-catalog"><header class="navbar-container not-open"><div class="navbar-inner"><div class="site-brand nav-item"><img class="logo" src="/logo.png" alt="Yunge Hu 's Blog"><a href="/" class="site-name can-hide">Yunge Hu 's Blog</a></div><div class="nav-item navbar-links-wrapper" style=""><div><form class="search-box" role="search"><input type="search" autocomplete="off" spellcheck="false" value><!----></form></div><nav class="navbar-links"><!--[--><div class="navbar-links__item"><a href="/" class="link router-link-active" aria-label="Home"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->Home<!--]--></span></span><!--[--><!--]--></a></div><div class="navbar-links__item"><a class="link" href="https://github.com/gitDebuger/gitdebuger.github.io" target="_blank" rel="noopener noreferrer" aria-label="GitHub"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->GitHub<!--]--></span></span><span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span><!--[--><!--]--></a></div><!--]--></nav><span class="xicon-container btn-toggle-dark-mode btn--dark-mode"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32" style="width:20px;height:20px;font-size:20px;color:inherit;"><path d="M15 2h2v3h-2z" fill="currentColor"></path><path d="M27 15h3v2h-3z" fill="currentColor"></path><path d="M15 27h2v3h-2z" fill="currentColor"></path><path d="M2 15h3v2H2z" fill="currentColor"></path><path d="M5.45 6.884l1.414-1.415l2.121 2.122l-1.414 1.414z" fill="currentColor"></path><path d="M23 7.58l2.121-2.12l1.414 1.414l-2.121 2.121z" fill="currentColor"></path><path d="M23.002 24.416l1.415-1.414l2.12 2.122l-1.413 1.414z" fill="currentColor"></path><path d="M5.47 25.13L7.59 23L9 24.42l-2.12 2.12l-1.41-1.41z" fill="currentColor"></path><path d="M16 8a8 8 0 1 0 8 8a8 8 0 0 0-8-8zm0 14a6 6 0 0 1 0-12z" fill="currentColor"></path></svg></span><span class="xicon-container btn-toggle-menus"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32" style="width:20px;height:20px;font-size:20px;color:inherit;"><circle cx="16" cy="8" r="2" fill="currentColor"></circle><circle cx="16" cy="16" r="2" fill="currentColor"></circle><circle cx="16" cy="24" r="2" fill="currentColor"></circle></svg></span></div></div></header><!----><!----><!----><!--[--><main class="page-container"><aside class="series-container"><!--[--><!--]--></aside><div class="page-content"><h1 class="page-title">接口、lambda表达式与内部类</h1><div class="page-info"><span class="xicon-container left"><!--[--><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32" class="xicon-icon" style="width:18px;height:18px;font-size:18px;color:inherit;"><path d="M16 4a5 5 0 1 1-5 5a5 5 0 0 1 5-5m0-2a7 7 0 1 0 7 7a7 7 0 0 0-7-7z" fill="currentColor"></path><path d="M26 30h-2v-5a5 5 0 0 0-5-5h-6a5 5 0 0 0-5 5v5H6v-5a7 7 0 0 1 7-7h6a7 7 0 0 1 7 7z" fill="currentColor"></path></svg><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->Yunge Hu<!--]--></span></span><!----><span class="xicon-container left"><!--[--><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32" class="xicon-icon" style="width:18px;height:18px;font-size:18px;color:inherit;"><path d="M11.17 6l3.42 3.41l.58.59H28v16H4V6h7.17m0-2H4a2 2 0 0 0-2 2v20a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H16l-3.41-3.41A2 2 0 0 0 11.17 4z" fill="currentColor"></path></svg><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[--><!--[--><a href="/categories/Java/1.html" class="">Java</a><!--]--><!--]--></span></span><!----><!----></div><div class="theme-reco-md-content"><div><h1 id="_3-接口、lambda表达式与内部类" tabindex="-1"><a class="header-anchor" href="#_3-接口、lambda表达式与内部类"><span>3 接口、lambda表达式与内部类</span></a></h1><p>接口用于描述类应该做什么,而不指定它应该怎么做,一个类可以实现一个或多个接口。只要符合所要求的接口,就可以使用实现了这个接口的类的对象。</p><p>lambda表达式用于创建可以在将来的某个时间点执行的代码块,通过其可以用一种简洁的方式表示使用回调或可变行为的代码。</p><p>内部类定义在另一个类的内部,他们的方法可以访问其外部类的字段,内部类技术在设计合作类集合时很有用。</p><p>代理是实现任意接口的对象,是一种非常专业的构造,可以用来构造系统级的工具。</p><h2 id="_3-1-接口" tabindex="-1"><a class="header-anchor" href="#_3-1-接口"><span>3.1 接口</span></a></h2><h3 id="_3-1-1-接口的概念" tabindex="-1"><a class="header-anchor" href="#_3-1-1-接口的概念"><span>3.1.1 接口的概念</span></a></h3><p>接口不是类而是对希望符合这个接口的类的一组需求。</p><p>例如 <code>Arrays</code> 类的 <code>sort</code> 方法要求的 <code>Comparable</code> 接口</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">Comparable</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">int</span> <span class="token function">compareTo</span><span class="token punctuation">(</span><span class="token class-name">Object</span> other<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>在这个接口中,<code>compareTo</code> 方法是抽象的,因为它没有具体实现。任何实现 <code>Comparable</code> 接口的类都必须提供一个 <code>compareTo</code> 方法,这个方法的签名必须和接口中声明的相同;否则,这个类应当是抽象的,也就是说不能构造这个类的对象。</p><p>在Java 5中,该接口已经提升为一个泛型类型</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">Comparable</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">T</span><span class="token punctuation">></span></span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">int</span> <span class="token function">compareTo</span><span class="token punctuation">(</span><span class="token class-name">T</span> other<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>当然,原始的接口依然可以使用。</p><p>接口中的所有方法都是 <code>public</code> 方法,在接口中声明方法时不需要手动声明访问权限。</p><p>接口可能包含多个方法,还可以定义常量,但接口不能提供实例字段。Java 8之前接口中的方法都是抽象方法,但现在接口中还可以有其他方法。</p><p>为了让类实现一个接口,需要完成以下两个步骤:</p><ol><li>将类声明为实现给定的接口</li><li>对接口中所有的方法提供定义</li></ol><p>要声明一个类实现某个接口,需要使用关键字 <code>implements</code> 。</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name">Employee</span> <span class="token keyword">implements</span> <span class="token class-name">Comparable</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><p>在实现方法时,必须把方法声明为 <code>public</code> 否则编译器会报错,指出你试图提供更严格的访问权限。</p><blockquote><p><code>Comparable</code> 接口的文档建议 <code>compareTo</code> 方法应当与 <code>equals</code> 方法相容。但是 <code>BigDecimal</code> 类例外,如果两个数值大小相等但精度不同,调用 <code>compareTo</code> 方法会返回 <code>0</code> 但是 <code>equals</code> 方法会返回 <code>false</code> 。</p></blockquote><p>Java语言规范规定 <code>compareTo</code> 方法必须满足对称性,即 <code>sgn(x.compareTo(y))==-sgn(y.compareTo(x))</code> 。</p><p>与 <code>equals</code> 方法一样,使用继承时会出现问题。解决方案和 <code>equals</code> 一样:</p><ol><li>如果不同子类有不同的比较有不同的含义,应当将不同不同子类之间的比较视为非法。</li><li>如果存在一个比较子类对象的通用算法,只需要在超类中提供一个声明为 <code>final</code> 的 <code>compareTo</code> 方法。</li><li>或者说希望不同子类之间有等级划分,可以提供一个 <code>rank</code> 方法,让每个子类覆盖 <code>rank</code> 并提供基于 <code>rank</code> 的 <code>compareTo</code> 方法。</li></ol><h3 id="_3-1-2-接口的属性" tabindex="-1"><a class="header-anchor" href="#_3-1-2-接口的属性"><span>3.1.2 接口的属性</span></a></h3><p>接口不是类,不能构造接口类型的对象。但是可以声明接口类型的变量并引用实现了这个接口的类对象。</p><p>可以使用 <code>instanceof</code> 操作符检测某个对象是否实现了某个特定的接口。</p><p>接口也可以进行扩展,允许有多条接口链,从通用性较高的接口扩展到专用性较高的接口,类似于类的继承层次结构。</p><p>虽然接口中不能包含实例字段,但是可以包含常量。</p><p>接口中的方法都自动为 <code>public</code> ,接口中的字段总是自动设置为 <code>public static final</code> 。</p><p>尽管每个类只能有一个超类,也就是不支持多重继承,但是一个类可以实现多个接口,使用逗号将想要实现的各个接口分隔开。</p><blockquote><p>记录和枚举类不能扩展其他类,但是它们可以实现接口。</p></blockquote><p>接口也可以是密封的,声明方式和权限限制与密封类相同。</p><h3 id="_3-1-3-接口与抽象类" tabindex="-1"><a class="header-anchor" href="#_3-1-3-接口与抽象类"><span>3.1.3 接口与抽象类</span></a></h3><p>至于为什么要使用接口而不是抽象类,原因在于使用抽象基类表示通用属性存在一个严重问题,每个类只能扩展一个类,不支持多重继承,但是每个类可以实现多个接口。接口可以提供多重继承的大多数好处,同时还能避免多重继承的复杂性和低效。</p><p><code>String</code> 和 <code>StringBuider</code> 以及其他一些类字符串都实现了 <code>CharSequence</code> 接口。这个接口包含所有的管理字符序列的类的公共方法,通过这个接口可以得到字符序列的长度、迭代处理码点或代码单元、提取子序列以及按字典序比较两个序列。另外,在Java 17中增加了判断字符序列是否为空的方法。</p><h3 id="_3-1-4-静态和私有方法" tabindex="-1"><a class="header-anchor" href="#_3-1-4-静态和私有方法"><span>3.1.4 静态和私有方法</span></a></h3><p>在Java 8中允许在接口中增加静态方法。不过在标准库中,采用了接口加实用工具类的方式,实用工具类的命名方式为在接口名的后面加 <code>s</code> 字符,比如 <code>Collection/Collections</code> 和 <code>Path/Paths</code> 。有些比较新的版本在接口中提供了等价的方法,这样实用工具类就不再是必须的了。</p><p>在Java 9中,接口中的方法可以是 <code>private</code> 方法,可以是静态方法也可以是实例方法,这些方法只能作为接口中其他方法的辅助方法。</p><h3 id="_3-1-5-默认方法" tabindex="-1"><a class="header-anchor" href="#_3-1-5-默认方法"><span>3.1.5 默认方法</span></a></h3><p>可以为任何接口方法提供一个默认实现,使用 <code>default</code> 修饰符标记。默认方法也可以调用其他方法。</p><p>默认方法的一个重要用法是接口演化。比如在Java 8中为 <code>Collection</code> 接口增加了一个 <code>stream</code> 方法,如果该方法不是默认方法,那么曾经实现了这个接口的类将不能通过编译,为接口增加一个非默认方法不能保证源代码兼容。如果不重新编译这个类,只是使用原先包含这个类的JAR文件,这个类仍能正常加载,但是调用这个新方法就会抛出 <code>AbstractMethodError</code> 异常。将方法实现为默认方法就可以解决这两个问题,没有提供具体实现方法的情况下将自动调用默认方法。</p><h3 id="_3-1-6-解决默认方法冲突" tabindex="-1"><a class="header-anchor" href="#_3-1-6-解决默认方法冲突"><span>3.1.6 解决默认方法冲突</span></a></h3><p>如果先在一个接口中将一个方法定义为默认方法,然后又在超类或另一个接口中定义了同样的方法,程序将按照如下规则选择某一方法执行:</p><ol><li>超类优先。如果超类提供了一个具体方法,有相同签名的默认方法将会被忽略。</li><li>接口冲突。如果至少有一个接口提供了一个具体实现,则必须提供一个定义进行覆盖。</li></ol><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token keyword">interface</span> <span class="token class-name">Person</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">default</span> <span class="token class-name">String</span> <span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">return</span> <span class="token string">""</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"><span class="token keyword">interface</span> <span class="token class-name">Named</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">default</span> <span class="token class-name">String</span> <span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">return</span> <span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"_"</span> <span class="token operator">+</span> <span class="token function">hashCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name">Student</span> <span class="token keyword">implements</span> <span class="token class-name">Person</span><span class="token punctuation">,</span> <span class="token class-name">Named</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">public</span> <span class="token class-name">String</span> <span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">return</span> <span class="token class-name">Person</span><span class="token punctuation">.</span><span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>类优先规则可以确保与Java 7的兼容性,如果为一个接口增加默认方法,则对于有默认方法之前能正常工作的代码不会有任何影响。</p><p>绝对不能创建一个默认方法重新定义 <code>Object</code> 类中的某个方法,由于 <code>Object</code> 类是所有类的公共超类,这样的方法绝对无法超越 <code>Object</code> 类中的方法。</p><h3 id="_3-1-7-接口与回调" tabindex="-1"><a class="header-anchor" href="#_3-1-7-接口与回调"><span>3.1.7 接口与回调</span></a></h3><p>回调是一种常见的程序设计模式,在这种模式中,可以指定某个特定事件发生时应当采取的动作。</p><p>在C语言中可以使用函数指针,在C++中除了函数指针外,还可以使用函数对象或lambda表达式将函数传递给有函数指针参数的函数。</p><p>在Java中,可以传递某个类的一个对象,然后这个方法调用这个对象的某个方法,这个类需要实现某个特定的接口。</p><p>比如 <code>java.swing</code> 包中的 <code>Timer</code> 类,这是一个定时器,构造定时器时,需要设定一个时间间隔,并告诉定时器经过这个时间间隔要做些什么。这个定时器要求指定类实现 <code>java.awt.event</code> 包中的 <code>ActionListener</code> 接口。</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">ActionListener</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">void</span> <span class="token function">actionPerformed</span><span class="token punctuation">(</span><span class="token class-name">ActionEvent</span> event<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>这个方法中的 <code>ActionEvent</code> 参数提供了事件的相关信息,例如发生这个事件的时间。 <code>event.getWhen()</code> 会返回这个事件的时间,表示纪元以来的毫秒数,将它传入 <code>Instant.ofEpochMilli</code> 可以得到更可读的描述。</p><p><code>Timer</code> 构造器的第一个参数是时间间隔,单位毫秒。第二个参数是监听器对象。</p><blockquote><p>注意,一定要导入 <code>javax.swing.Timer</code> 类,另外还有一个稍有区别的 <code>java.util.Timer</code> 类。</p></blockquote><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token keyword">import</span> <span class="token import"><span class="token namespace">javax<span class="token punctuation">.</span>swing<span class="token punctuation">.</span></span><span class="token operator">*</span></span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>time<span class="token punctuation">.</span></span><span class="token operator">*</span></span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>awt<span class="token punctuation">.</span>event<span class="token punctuation">.</span></span><span class="token operator">*</span></span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>awt<span class="token punctuation">.</span></span><span class="token operator">*</span></span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Main</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">var</span> listener <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">TimePrint</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token keyword">var</span> timer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Timer</span><span class="token punctuation">(</span><span class="token number">5000</span><span class="token punctuation">,</span> listener<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> timer<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"> <span class="token class-name">JOptionPane</span><span class="token punctuation">.</span><span class="token function">showMessageDialog</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token string">"Quit program?"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">exit</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name">TimePrint</span> <span class="token keyword">implements</span> <span class="token class-name">ActionListener</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">actionPerformed</span><span class="token punctuation">(</span><span class="token class-name">ActionEvent</span> event<span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"At the tone, the time is"</span> <span class="token operator">+</span> <span class="token class-name">Instant</span><span class="token punctuation">.</span><span class="token function">ofEpochMilli</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span><span class="token function">getWhen</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token class-name">Toolkit</span><span class="token punctuation">.</span><span class="token function">getDefaultToolkit</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">beep</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="_3-1-8-comparator接口" tabindex="-1"><a class="header-anchor" href="#_3-1-8-comparator接口"><span>3.1.8 Comparator接口</span></a></h3><p>前面提到 <code>Arrays.sort</code> 方法可以对实现了 <code>Comparable</code> 接口的类的对象数组进行排序。但是如果需要按照其他方式进行排序,该排序方法还提供了第二个版本,接受一个数组和一个比较器参数,比较器是实现了 <code>Comparator</code> 接口的类的实例。</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">Comparator</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">T</span><span class="token punctuation">></span></span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">int</span> <span class="token function">compare</span><span class="token punctuation">(</span><span class="token class-name">T</span> first<span class="token punctuation">,</span> <span class="token class-name">T</span> second<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>例如对字符串数组按长度进行排序,可以定义如下的比较器类</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name">LengthComparator</span> <span class="token keyword">implements</span> <span class="token class-name">Comparator</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">String</span><span class="token punctuation">></span></span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">compare</span><span class="token punctuation">(</span><span class="token class-name">String</span> first<span class="token punctuation">,</span> <span class="token class-name">String</span> second<span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>first<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> second<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> first<span class="token punctuation">.</span><span class="token function">compareTo</span><span class="token punctuation">(</span>second<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token keyword">return</span> first<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> second<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>尽管这个类没有字段,依然需要创建一个对象实例,通过这个实例调用比较方法,这个方法不是静态方法。</p><h3 id="_3-1-9-对象克隆" tabindex="-1"><a class="header-anchor" href="#_3-1-9-对象克隆"><span>3.1.9 对象克隆</span></a></h3><p>对象克隆需要 <code>Cloneable</code> 接口,这个接口表示一个类提供了一个安全的 <code>clone</code> 方法。</p><p><code>clone</code> 方法是 <code>Object</code> 类的一个 <code>protected</code> 方法,这说明你的代码不能直接调用这个方法。默认的 <code>Object.clone</code> 方法是对对象的字段逐个拷贝,也就是浅拷贝,如果对象中的所有实例字段都是基本类型,那么这样操作没有任何问题。如果对象包含子对象的引用,拷贝字段就会得到相同子对象的引用,原对象和克隆对象会共享一部分信息。如果共享的子对象是不可变的或者在对象的声明周期中子对象一直保持不变,没有更改器方法改变它,也没有方法会生成它的引用,那么浅拷贝在这种情况下就是安全的。</p><p>如果子对象是可变的,就需要重新定义 <code>clone</code> 方法建立深拷贝,完成对所有子对象的克隆。</p><p>对于每一个类,需要确定以下选项是否成立:</p><ol><li>默认的 <code>clone</code> 方法就能满足。</li><li>可以在可变的子对象上调用 <code>clone</code> 来弥补默认的 <code>clone</code> 方法。</li><li>不该使用 <code>clone</code> 方法。</li></ol><p>如果选择使用 <code>clone</code> 方法,类必须满足以下条件:</p><ol><li>实现 <code>Cloneable</code> 接口。</li><li>重新定义 <code>clone</code> 方法,并指定 <code>public</code> 修饰符。</li></ol><p>这个 <code>Cloneable</code> 接口并没有指定任何 <code>clone</code> 方法,该方法是从 <code>Object</code> 类继承来的。这个接口只是作为一个标记,指示类设计者了解克隆过程。这个接口是Java提供的少数标记接口之一,标记接口不包含任何方法,它唯一的作用就是允许在类型查询中使用 <code>instanceof</code> 操作符。如果一个对象请求克隆,但是没有实现这个接口,就会生成一个检查型异常。</p><p>即使默认的 <code>clone</code> 方法实现能够满足要求,还是需要实现 <code>Cloneable</code> 接口,将该方法重新定义为 <code>public</code> 然后再调用 <code>super.clone()</code> 方法。</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name">TestClass</span> <span class="token keyword">implements</span> <span class="token class-name">Cloneable</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">public</span> <span class="token class-name">TestClass</span> <span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">CloneNotSupportException</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token class-name">TestClass</span><span class="token punctuation">)</span><span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>在Java 1.4之前,该方法的返回类型总是 <code>Object</code> 类型。从Java 1.5开始,子类在覆盖返回类型不是基本类型的方法时,允许将返回类型改为原类型的子类型,称为协变返回类型。</p><p>必须当心子类的克隆,如果子类没有正确修正超类提供的 <code>clone</code> 方法,就有可能出现问题。</p><p>但其实,克隆并没有那么的常用,所以其实并没有太大必要提供克隆方法。</p><p>所有数组类型都有一个公共的 <code>clone</code> 方法,而不是受保护的方法。</p><h2 id="_3-2-lambda表达式" tabindex="-1"><a class="header-anchor" href="#_3-2-lambda表达式"><span>3.2 lambda表达式</span></a></h2><p>使用lambda表达式可以通过一种简洁的语法定义代码块。</p><h3 id="_3-2-1-为什么引入lambda表达式" tabindex="-1"><a class="header-anchor" href="#_3-2-1-为什么引入lambda表达式"><span>3.2.1 为什么引入lambda表达式</span></a></h3><p>lambda表达式是一个可传递的代码块,可以在以后执行一次或多次。在Java中传递一段代码块并不容易,因为Java是一款纯粹的面向对象编程语言,所以必须构造一个对象,这个对象的类要有一个方法包含所需的代码,前面使用了反射和接口达到了传递代码块的目的。不过这两种方法,总归是不太方便。</p><h3 id="_3-2-2-lambda表达式的语法" tabindex="-1"><a class="header-anchor" href="#_3-2-2-lambda表达式的语法"><span>3.2.2 lambda表达式的语法</span></a></h3><p>lambda表达式就是一个代码块,以及必须传入代码的所有变量的规范。它的格式包括:小括号及内部的参数,箭头以及一个表达式。</p><p>如果一个表达式内放不下,可以将这些代码放在大括号中,并包含显式的 <code>return</code> 语句;即使没有参数,也需要提供空括号;如果可以推断出参数类型,则可以忽略其类型;如果表达式只有一个参数并且该参数的类型可以推导得出,那么甚至可以省略小括号;无需指定表达式的返回类型,它的返回类型总是推导得出;可以使用 <code>var</code> 指示一个推导的类型,这个语法是为了关联注解。</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token comment">// an easy lambda</span></span>
<span class="line"><span class="token class-name">Comparator</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">String</span><span class="token punctuation">></span></span> comp <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">String</span> first<span class="token punctuation">,</span> <span class="token class-name">String</span> second<span class="token punctuation">)</span> <span class="token operator">-></span> first<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> second<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token comment">// a complex lambda</span></span>
<span class="line"><span class="token punctuation">(</span><span class="token class-name">String</span> first<span class="token punctuation">,</span> <span class="token class-name">String</span> second<span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>first<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator"><</span> second<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>first<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">></span> second<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token number">1</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token keyword">else</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="_3-2-3-函数式接口" tabindex="-1"><a class="header-anchor" href="#_3-2-3-函数式接口"><span>3.2.3 函数式接口</span></a></h3><p>对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个lambda表达式,这种接口称为函数式接口。</p><blockquote><p>接口完全有可能重新声明 <code>Object</code> 类的方法,如 <code>toString</code> 或 <code>clone</code> 方法,这些声明有可能让方法不再是抽象的。更重要的是,接口可以声明非抽象方法。</p></blockquote><p>lambda表达式可以看作某个包含一个匿名方法的匿名类的一个对象,这样就可以用相应的接口类型变量引用这个对象,并且通过调用接口中的方法调用这个lambda表达式表示的代码块。</p><p>Java API在 <code>java.util.function</code> 包中定义了很多非常通用的函数式接口,比如 <code>BigFunction<T,U,R></code> 描述了参数类型为 <code>T</code> 和 <code>U</code> 并且返回类型为 <code>R</code> 的函数,上面的lambda表达式就可以保存在这个类型的变量中,但是这种做法对于实际应用并没有什么帮助。每种接口往往都有特定的用途,而不只是提供一个有指定参数和返回类型的方法,想要用lambda表达式时,最好还是根据它的用途,使用特定类型的函数式接口引用它。</p><p>这个包中有一个接口 <code>Predicate</code> 很有用:</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">Predicate</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">T</span><span class="token punctuation">></span></span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">boolean</span> <span class="token function">test</span><span class="token punctuation">(</span><span class="token class-name">T</span> t<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p><code>ArrayList</code> 类有一个 <code>removeIf</code> 方法,它的一个参数就是 <code>Predicate</code> 接口,这个接口专门用来传递lambda表达式。</p><p>另一个有用的函数式接口是 <code>Supplier<T></code> 接口:</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">Supplier</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">T</span><span class="token punctuation">></span></span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token class-name">T</span> <span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>该接口的方法没有参数,调用时会产生一个 <code>T</code> 类型的值,该接口用于实现懒计算,比如以下调用:</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token class-name">LocalDate</span> hireDay <span class="token operator">=</span> <span class="token class-name">Objects</span><span class="token punctuation">.</span><span class="token function">requireNonNullElse</span><span class="token punctuation">(</span>day<span class="token punctuation">,</span> <span class="token class-name">LocalDate</span><span class="token punctuation">.</span><span class="token function">of</span><span class="token punctuation">(</span><span class="token number">1970</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><p>预计 <code>day</code> 很少为 <code>null</code> 所以希望只在必要时才构造默认的 <code>LocalDate</code> 对象,通过这个接口就可以延迟这个构造:</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token class-name">LocalDate</span> hireDay <span class="token operator">=</span> <span class="token class-name">Objects</span><span class="token punctuation">.</span><span class="token function">requireNonNullElseGet</span><span class="token punctuation">(</span>day<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">LocalDate</span><span class="token punctuation">.</span><span class="token function">of</span><span class="token punctuation">(</span><span class="token number">1970</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><h3 id="_3-2-4-方法引用" tabindex="-1"><a class="header-anchor" href="#_3-2-4-方法引用"><span>3.2.4 方法引用</span></a></h3><p>考虑下面的两行代码</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token keyword">var</span> timer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Timer</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">,</span> event <span class="token operator">-></span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>event<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">var</span> timer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Timer</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">,</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token operator">::</span><span class="token function">println</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div></div></div><p>这两种写法是等价的,下面的表达式中的 <code>System.out::println</code> 是一个方法引用,它指示编译器生成一个函数式接口的实例,覆盖这个接口的抽象方法来调用给定的方法。</p><p>再比如字符串排序传递的比较器参数也可以使用方法引用传递</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token class-name">Arrays</span><span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span>strings<span class="token punctuation">,</span> <span class="token class-name">String</span><span class="token operator">::</span><span class="token function">compareToIgnoreCase</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><p>方法引用主要有三种情况,它等价于一个lambda表达式。</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line">object<span class="token operator">::</span><span class="token function">instanceMethod</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token class-name">Class</span><span class="token operator">::</span><span class="token function">instanceMethod</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token class-name">Class</span><span class="token operator">::</span><span class="token function">staticMethod</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>第一种情况,参数将直接传递到方法;</p><p>第二种情况,第一个参数会成为方法的隐式参数 <code>this</code> ,其他的参数依次传递;</p><p>第三种情况,所有的参数都传递到静态方法。</p><p>虽然方法引用和lambda表达式可以等价转换,但是他们还是有一些细微的差别的。例如方法引用 <code>separator::equals</code> 和lambda表达式 <code>x->seperator.equals(x)</code> ,如果 <code>seperator</code> 为 <code>null</code> ,构造方法引用会立即抛出异常,lambda表达式只有在调用时才会抛出异常。</p><p>另外,在方法引用中也可以使用 <code>this</code> 和 <code>super</code> 参数,例如 <code>this::equals</code> 等价于 <code>this.equals(x)</code> 。</p><h3 id="_3-2-5-构造器引用" tabindex="-1"><a class="header-anchor" href="#_3-2-5-构造器引用"><span>3.2.5 构造器引用</span></a></h3><p>构造器引用就相当于方法引用的第二种情况,只不过方法名为 <code>new</code> 。</p><p>可以用数组类型建立构造器引用,例如 <code>int[]::new</code> 是一个构造器引用,它的参数是数组的长度。利用数组构造器引用可以克服Java不能构造泛型数组的限制,这样就可以构造正确类型的数组,而不是 <code>Object</code> 类型的数组。</p><h3 id="_3-2-6-变量作用域" tabindex="-1"><a class="header-anchor" href="#_3-2-6-变量作用域"><span>3.2.6 变量作用域</span></a></h3><p>类似于C++的lambda表达式,Java的表达式也可以捕获外部变量,不过不需要 <code>[]</code> 设置捕获的范围。</p><p>代码块连同自由变量值有一个术语:闭包。</p><p>为了确保所捕获的值是明确定义的,表达式中只能引用值不会改变的变量,不能在外部或表达式内部更改这个变量的值,也就是说捕获的变量必须是事实最终变量,这个变量初始化后就不会再为它赋新值。如果在lambda表达式内可以更改变量,并发执行多个动作时就会不安全。</p><p>lambda表达式的体和嵌套块有相同的作用域,这里同样适用于命名冲突和遮蔽的有关规则。</p><p>在lambda表达式中使用 <code>this</code> 关键字时,指的是创建这个表达式的方法的 <code>this</code> 参数。</p><h3 id="_3-2-7-处理lambda表达式" tabindex="-1"><a class="header-anchor" href="#_3-2-7-处理lambda表达式"><span>3.2.7 处理lambda表达式</span></a></h3><p>如果想要创建一个接受lambda表达式参数的方法,需要选择一个函数式接口,Java API提供了很多实用的接口类型,可以直接使用不需要再自行定义。并且Java API还提供了一些关于基本类型的特殊化接口,这些接口比使用通用接口更加高效。</p><p>大多数标准函数式接口都提供了非抽象方法来生成或合并函数,例如 <code>Predicate.isEqual</code> <code>and</code> <code>or</code> <code>negate</code> 等。</p><p>设计接口时,如果只有一个抽象方法,可以使用 <code>@FunctionalInterface</code> 注解来标记这个接口,不过这不是必须的。</p><h3 id="_3-2-8-再谈comparator" tabindex="-1"><a class="header-anchor" href="#_3-2-8-再谈comparator"><span>3.2.8 再谈Comparator</span></a></h3><p><code>Comparator</code> 接口提供了很多方便的静态方法来创建比较器,这些方法可用于lambda表达式或方法引用。</p><p>静态 <code>comparing</code> 方法接受一个键提取器函数,它将类型 <code>T</code> 映射成一个可以比较的类型,对要比较的对象应用这个函数,然后对返回的键完成比较。</p><p>可以把比较器和 <code>thenComparing</code> 方法串起来,来处理比较结果相同的情况。</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token class-name">Arrays</span><span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span>people<span class="token punctuation">,</span> <span class="token class-name">Comparator</span><span class="token punctuation">.</span><span class="token function">comparing</span><span class="token punctuation">(</span><span class="token class-name">Person</span><span class="token operator">::</span><span class="token function">getLastName</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">thenComparing</span><span class="token punctuation">(</span><span class="token class-name">Person</span><span class="token operator">::</span><span class="token function">getFirstName</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><p>还可以为比较器方法提取的键指定一个比较器</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token class-name">Arrays</span><span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span>people<span class="token punctuation">,</span> <span class="token class-name">Comparator</span><span class="token punctuation">.</span><span class="token function">comparing</span><span class="token punctuation">(</span><span class="token class-name">Person</span><span class="token operator">::</span><span class="token function">getName</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>s<span class="token punctuation">,</span> t<span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">Integer</span><span class="token punctuation">.</span><span class="token function">compare</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> t<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><p>另外,比较器还有针对基本类型的变体,避免装箱带来的效率下降。</p><p>如果键函数可能返回 <code>null</code> ,可能需要用到 <code>nullsFirst</code> 和 <code>nullsLast</code> 适配器。这些静态方法会修改现有的比较器,从而在遇到 <code>null</code> 时不会抛出异常,而是将这个值标记为小于或大于正常值。这两个方法同样需要传递一个比较器。</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token class-name">Comparator</span><span class="token punctuation">.</span><span class="token function">comparing</span><span class="token punctuation">(</span><span class="token class-name">Person</span><span class="token operator">::</span><span class="token function">getMiddleName</span><span class="token punctuation">,</span> <span class="token class-name">Comparator</span><span class="token punctuation">.</span><span class="token function">nullsFirst</span><span class="token punctuation">(</span><span class="token class-name">Comparator</span><span class="token punctuation">.</span><span class="token function">naturalOrder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><p>其中 <code>naturalOrder</code> 方法可以为任何实现了 <code>Comparable</code> 的类建立一个比较器。</p><p>静态 <code>reverseOrder</code> 方法会提供自然顺序的逆序,要让比较器逆序比较,可以使用 <code>reversed</code> 实例方法,例如 <code>naturalOrder().reversed()</code> 等价于 <code>reverseOrder()</code> 。</p><h2 id="_3-3-内部类" tabindex="-1"><a class="header-anchor" href="#_3-3-内部类"><span>3.3 内部类</span></a></h2><p>内部类是定义在另一个类中的类,使用内部类的原因主要有两个:</p><ol><li>内部类可以对同一个包中的其他类隐藏。</li><li>内部类方法可以访问定义这些方法的作用域中的数据,包括原本私有的数据。</li></ol><p>Java的内部类类似于C++的嵌套类,但是Java的内部类对象会有一个隐式引用,指向实例化这个对象的外部类对象,通过这个引用可以访问外部对象的全部状态,不需要显式包含外部对象的引用。</p><p>但是静态内部类没有这个附加的外部对象引用,所以它就相当于C++的嵌套类。</p><h3 id="_3-3-1-使用内部类访问对象状态" tabindex="-1"><a class="header-anchor" href="#_3-3-1-使用内部类访问对象状态"><span>3.3.1 使用内部类访问对象状态</span></a></h3><p>将以下面的程序解释内部类的相关概念</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>awt<span class="token punctuation">.</span></span><span class="token operator">*</span></span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>awt<span class="token punctuation">.</span>event<span class="token punctuation">.</span></span><span class="token operator">*</span></span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>time<span class="token punctuation">.</span></span><span class="token operator">*</span></span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">import</span> <span class="token import"><span class="token namespace">javax<span class="token punctuation">.</span>swing<span class="token punctuation">.</span></span><span class="token operator">*</span></span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Main</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">var</span> clock <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">TalkingClock</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> clock<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token class-name">JOptionPane</span><span class="token punctuation">.</span><span class="token function">showMessageDialog</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token string">"Quit program?"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">exit</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name">TalkingClock</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token keyword">int</span> interval<span class="token punctuation">;</span></span>
<span class="line"> <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token keyword">boolean</span> beep<span class="token punctuation">;</span></span>
<span class="line"> <span class="token keyword">public</span> <span class="token class-name">TalkingClock</span><span class="token punctuation">(</span><span class="token keyword">int</span> interval<span class="token punctuation">,</span> <span class="token keyword">boolean</span> beep<span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">this</span><span class="token punctuation">.</span>interval <span class="token operator">=</span> interval<span class="token punctuation">;</span></span>
<span class="line"> <span class="token keyword">this</span><span class="token punctuation">.</span>beep <span class="token operator">=</span> beep<span class="token punctuation">;</span></span>
<span class="line"> <span class="token punctuation">}</span></span>
<span class="line"> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">var</span> listener <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">TimePrinter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token keyword">var</span> timer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Timer</span><span class="token punctuation">(</span>interval<span class="token punctuation">,</span> listener<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> timer<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token punctuation">}</span></span>
<span class="line"> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">TimePrinter</span> <span class="token keyword">implements</span> <span class="token class-name">ActionListener</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">actionPerformed</span><span class="token punctuation">(</span><span class="token class-name">ActionEvent</span> event<span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"At the zone, the time is "</span> <span class="token operator">+</span> <span class="token class-name">Instant</span><span class="token punctuation">.</span><span class="token function">ofEpochMilli</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span><span class="token function">getWhen</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>beep<span class="token punctuation">)</span> <span class="token class-name">Toolkit</span><span class="token punctuation">.</span><span class="token function">getDefaultToolkit</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">beep</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token punctuation">}</span></span>
<span class="line"> <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>这里的 <code>TimePrinter</code> 类定义在 <code>TalkingClock</code> 类的内部,但是不代表每个 <code>TalkingClock</code> 对象都有 <code>TimePrinter</code> 对象,这个对象是由 <code>TimePrinter</code> 中的方法构造的。</p><p>内部类中的 <code>beep</code> 指示的是外部类对象的实例字段,一个内部类方法可以访问自身的实例字段,也可以访问外部类对象的实例字段,这里有一个隐式引用指向创建它的外部类对象。但要注意的是,这个引用在内部类的定义中是不可见的,对外部类对象的访问会由编译器自动完成。</p><p>外部类的引用在构造器中设置,编译器会修改所有的内部类构造器,添加一个对应外部类引用的参数,在这个例程中,内部类 <code>TimePrinter</code> 类只有一个无参数构造器,所以编译生成了这样一个构造器</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token keyword">public</span> <span class="token class-name">TimePrinter</span><span class="token punctuation">(</span><span class="token class-name">TalkingClock</span> clock<span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line"> outer <span class="token operator">=</span> clock<span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>这里的 <code>outer</code> 不是关键字,它只是用于说明内部类的相关机制。</p><p>调用内部类构造器时,编译器会自动将当前外部类对象的 <code>this</code> 引用传递给这个构造器。</p><p>由于内部类可以直接访问外部类的字段,所以可以避免提供一个只有某一个类需要的访问器。</p><p>这里也可以把 <code>TimePrinter</code> 类声明为私有,这样就只有 <code>TalkingClock</code> 类的方法可以构造它的对象。也就是说,私有内部类只能由定义其的外部类使用。需要注意的是,只有内部类可以是私有的,常规的类只有包可见性和公共可见性。</p><h3 id="_3-3-2-内部类的特殊语法规则" tabindex="-1"><a class="header-anchor" href="#_3-3-2-内部类的特殊语法规则"><span>3.3.2 内部类的特殊语法规则</span></a></h3><p>前面使用 <code>outer</code> 表示外部类引用,这个引用的正规语法为 <code>OuterClass.this</code> ,通过这个语法可以更明确地表示外部类的字段。</p><p>同样,也可以使用以下语法更明确地编写内部类的构造器</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line">outerObject<span class="token punctuation">.</span><span class="token keyword">new</span> <span class="token class-name">InnerClass</span><span class="token punctuation">(</span>construction parameters<span class="token punctuation">)</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><p>在外部类的作用域之外,可以这样引用内部类</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token class-name">OuterClass<span class="token punctuation">.</span>InnerClass</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><p>内部类中声明的所有静态字段都必须是 <code>final</code> 并初始化为一个编译时常量,如果这个字段不是常量就可能不唯一。</p><p>内部类不能有 <code>static</code> 方法,按理说也可以有只能访问外部类静态字段和方法的静态方法,但是Java并没有添加这个语法。</p><h3 id="_3-3-3-内部类是否有用、必要和安全" tabindex="-1"><a class="header-anchor" href="#_3-3-3-内部类是否有用、必要和安全"><span>3.3.3 内部类是否有用、必要和安全</span></a></h3><p>内部类编译后将得到常规的类文件,文件命名为 <code>OuterClass$InnerClass.class</code> 。</p><p>前面提到,编译器会自动将外部类对象的引用传递给内部类,但是如果手动实现这一功能,将某一个对象的 <code>this</code> 引用传递给另一个常规类的方法或构造器,会被报告错误,拒绝这种操作。所以相比于外部类,内部类拥有更高的访问权限。</p><p>在Java 11之前,内部类纯粹是一种编译器现象,虚拟机对它们并没有什么特别的了解。编译时,编译器会在外部类中添加一个静态方法,它将返回作为参数传递的那个对象的某一字段。在Java 11及以后,虚拟机了解类之间的嵌套关系,不再生成访问方法。</p><h3 id="_3-3-4-局部内部类" tabindex="-1"><a class="header-anchor" href="#_3-3-4-局部内部类"><span>3.3.4 局部内部类</span></a></h3><p>内部类也可以定义在某个方法的内部,声明局部内部类时不能有访问说明符,局部内部类的作用域总是限定在声明这个局部类的方法的内部。局部类对外部世界完全隐藏,除了声明这个类的方法,没有其他任何方法能够访问这个类。</p><h3 id="_3-3-5-由外部方法访问变量" tabindex="-1"><a class="header-anchor" href="#_3-3-5-由外部方法访问变量"><span>3.3.5 由外部方法访问变量</span></a></h3><p>除了外部类的字段,局部内部类还可以访问局部变量,但是局部变量必须是事实最终变量,他们一旦赋值就不会就绝对不会改变。</p><p>不过为了让内部类的方法能够正常工作,内部类必须在局部变量消失之前复制到另一个地方进行保存。编译器会在局部内部类添加一个字段用于保存局部变量,在Java 11中可以使用嵌套伴侣访问来实现,Java 11之前内部类构造器会有一个额外的参数来设置这个字段。但无论如何实现,即使局部变量出了作用域,内部类字段都将持久保存。</p><h3 id="_3-3-6-匿名内部类" tabindex="-1"><a class="header-anchor" href="#_3-3-6-匿名内部类"><span>3.3.6 匿名内部类</span></a></h3><p>使用局部内部类时,如果只需要创建这个类的一个对象,那么可以使用匿名内部类。</p><p>它的语法规则如下:</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token keyword">new</span> <span class="token class-name">SuperType</span><span class="token punctuation">(</span>construction parameters<span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line"> inner <span class="token keyword">class</span> methods and data<span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>在这里 <code>SuperType</code> 可以是接口,内部类要实现这个接口;也可以是一个类,内部类要扩展这个类。</p><p>匿名内部类没有构造器,构造参数要传递给超类的构造器,如果匿名内部类实现的是接口,就不能有任何构造参数。</p><p>尽管匿名内部类不能有构造器,但是可以提供一个对象初始化块。</p><p>尽管匿名内部类可以用于实现接口,但是最好还是使用lambda表达式。</p><p>如果将一个匿名内部类存储在用 <code>var</code> 定义的一个变量中,这个变量会了解增加的方法和字段,例如</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token keyword">var</span> bob <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token class-name">String</span> name <span class="token operator">=</span> <span class="token string">"Bob"</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"><span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>bob<span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>如果声明为 <code>Object</code> 这段代码将无法通过编译,事实上创建的这个变量类型是一个不可指示的类型,不过编译器可以设置这个类型。</p><p>有一种双括号初始化的技巧,它利用了内部类的初始化块。比如下面的代码创建了一个数组列表并把它传递给一个方法。</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token keyword">var</span> friends <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">String</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">friends<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"Harry"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">friends<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"Tony"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token function">invite</span><span class="token punctuation">(</span>friends<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token comment">// the same meaning</span></span>
<span class="line"><span class="token function">invite</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">String</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"Harry"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"Tony"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>不过这种技巧很少使用,完全可以这么写</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token function">invite</span><span class="token punctuation">(</span><span class="token class-name">List</span><span class="token punctuation">.</span><span class="token function">of</span><span class="token punctuation">(</span><span class="token string">"Harry"</span><span class="token punctuation">,</span> <span class="token string">"Tony"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><p>建立一个与超类大体相似的匿名子类通常会很方法,但是需要注意对 <code>equals</code> 方法的使用</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!=</span> other<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><p>上面的这段测试会失败。</p><p>对于静态方法,如果想要得到所在的类名,可以如下操作</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getEnclosingClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><p>这段代码首先创建一个匿名类的匿名对象,然后获取这个对象所在的外部类,也就是包含这个静态方法的类。</p><h3 id="_3-3-7-静态内部类" tabindex="-1"><a class="header-anchor" href="#_3-3-7-静态内部类"><span>3.3.7 静态内部类</span></a></h3><p>如果将内部类声明为静态,就不会生成外部类的对象的引用,只有内部类可以声明为静态。如果在静态方法内构造内部类,那么这个内部类必须是静态内部类。但其实,如果不需要外部对象的引用,建议将内部类声明为静态。</p><p>与常规内部类不同,静态内部类可以有静态字段和方法。</p><p>在接口中声明的内部类自动设置为静态和公共可见。</p><p>类中声明的接口、记录和枚举自动设置为静态。</p><h2 id="_3-4-服务加载器" tabindex="-1"><a class="header-anchor" href="#_3-4-服务加载器"><span>3.4 服务加载器</span></a></h2><p>JDK提供了加载服务的简单机制,这种机制由Java平台提供支持。</p><p>提供一个服务时,程序通常希望服务设计者对于如何实现这个服务的特性能有一些自由,另外还希望有多个实现可以选择。利用 <code>ServiceLoader</code> 类可以很容易地加载符合一个公共接口的服务。</p><p>首先定义一个接口或者一个超类,其中包含这个服务的各个实例应当提供的方法;服务提供者可以提供一个或多个实现这个服务的类。实现类可以放在任意的包中,每个实现类必须有一个无参数构造器。</p><p>然后把这些类的类名增加到 <code>META-INF/services</code> 目录下的一个UTF-8编码的文本文件中,文件名必须和接口的完全限定名一致,文件中还要求包含特定的一些文字。</p><p>完成之后程序可以初始化一个服务加载器,这个初始化只在程序中完成一次。</p><p>服务加载器的 <code>iterator</code> 方法会返回一个迭代器来处理所提供的所有服务实现,然后可以循环遍历,选择适当的对象完成服务。或者也可以使用流来查找所需要的服务。</p><h2 id="_3-5-代理" tabindex="-1"><a class="header-anchor" href="#_3-5-代理"><span>3.5 代理</span></a></h2><p>利用代理可以在运行时创建实现了一组给定接口的新类,只有在编译时无法确定需要实现哪个接口时才有必要使用代理。这种技术主要用于系统编程,对于应用程序的开发并不常见。</p><h3 id="_3-5-1-何时使用代理" tabindex="-1"><a class="header-anchor" href="#_3-5-1-何时使用代理"><span>3.5.1 何时使用代理</span></a></h3><p>代理类可以在运行时创建全新的类,这样一个代理类能够实现指定的接口。代理类包含以下方法:</p><ol><li>指定接口所需要的全部方法。</li><li><code>Object</code> 类中定义的全部方法。</li></ol><p>不过不能在运行时为这些方法定义新代码,实际上必须提供一个调用处理器,调用处理器是实现了 <code>InvocationHandler</code> 接口的类的对象,这个接口只有一个方法</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token class-name">Object</span> <span class="token function">invoke</span><span class="token punctuation">(</span><span class="token class-name">Object</span> proxy<span class="token punctuation">,</span> <span class="token class-name">Method</span> method<span class="token punctuation">,</span> <span class="token class-name">Object</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><p>无论何时调用代理对象的方法,都会调用这个调用处理器的 <code>invoke</code> 方法,并提供 <code>Method</code> 对象和原调用的参数。之后,调用处理器会决定如何处理这个调用。</p><h3 id="_3-5-2-创建代理对象" tabindex="-1"><a class="header-anchor" href="#_3-5-2-创建代理对象"><span>3.5.2 创建代理对象</span></a></h3><p>要想创建一个代理对象,需要使用 <code>Proxy</code> 类的 <code>newProxyInstance</code> 方法,这个方法有三个参数:</p><ol><li>类加载器。</li><li><code>Class</code> 对象数组,每个元素对应需要实现的各个接口。</li><li>调用处理器。</li></ol><p>下面是书中的例程</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre class="language-java"><code><span class="line"><span class="token keyword">package</span> <span class="token namespace">proxy</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>reflect<span class="token punctuation">.</span></span><span class="token operator">*</span></span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token operator">*</span></span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token doc-comment comment">/**</span>
<span class="line"> * This program demonstrates the use of proxies.</span>
<span class="line"> * <span class="token keyword">@version</span> 1.02 2021-06-16</span>
<span class="line"> * <span class="token keyword">@author</span> Cay Horstmann</span>
<span class="line"> */</span></span>
<span class="line"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ProxyTest</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span></span>
<span class="line"> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">var</span> elements <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">[</span><span class="token number">1000</span><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"> <span class="token comment">// fill elements with proxies for the integers 1 . . . 1000</span></span>
<span class="line"> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> elements<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span></span>
<span class="line"> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token class-name">Integer</span> value <span class="token operator">=</span> i <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token keyword">var</span> handler <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">TraceHandler</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token class-name">Object</span> proxy <span class="token operator">=</span> <span class="token class-name">Proxy</span><span class="token punctuation">.</span><span class="token function">newProxyInstance</span><span class="token punctuation">(</span></span>
<span class="line"> <span class="token class-name">ClassLoader</span><span class="token punctuation">.</span><span class="token function">getSystemClassLoader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span></span>
<span class="line"> <span class="token keyword">new</span> <span class="token class-name">Class</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">{</span> <span class="token class-name">Comparable</span><span class="token punctuation">.</span><span class="token keyword">class</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> handler<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> elements<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">=</span> proxy<span class="token punctuation">;</span></span>
<span class="line"> <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"> <span class="token comment">// construct a random integer</span></span>
<span class="line"> <span class="token class-name">Integer</span> key <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token class-name">Math</span><span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span> elements<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"> <span class="token comment">// search for the key</span></span>
<span class="line"> <span class="token keyword">int</span> result <span class="token operator">=</span> <span class="token class-name">Arrays</span><span class="token punctuation">.</span><span class="token function">binarySearch</span><span class="token punctuation">(</span>elements<span class="token punctuation">,</span> key<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"> <span class="token comment">// print match if found</span></span>
<span class="line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>result <span class="token operator">>=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>elements<span class="token punctuation">[</span>result<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token doc-comment comment">/**</span>
<span class="line"> * An invocation handler that prints out the method name and parameters, then</span>
<span class="line"> * invokes the original method</span>
<span class="line"> */</span></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name">TraceHandler</span> <span class="token keyword">implements</span> <span class="token class-name">InvocationHandler</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">private</span> <span class="token class-name">Object</span> target<span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"> <span class="token doc-comment comment">/**</span>
<span class="line"> * Constructs a TraceHandler</span>
<span class="line"> * <span class="token keyword">@param</span> <span class="token parameter">t</span> the implicit parameter of the method call</span>
<span class="line"> */</span></span>
<span class="line"> <span class="token keyword">public</span> <span class="token class-name">TraceHandler</span><span class="token punctuation">(</span><span class="token class-name">Object</span> t<span class="token punctuation">)</span></span>
<span class="line"> <span class="token punctuation">{</span></span>
<span class="line"> target <span class="token operator">=</span> t<span class="token punctuation">;</span></span>
<span class="line"> <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"> <span class="token keyword">public</span> <span class="token class-name">Object</span> <span class="token function">invoke</span><span class="token punctuation">(</span><span class="token class-name">Object</span> proxy<span class="token punctuation">,</span> <span class="token class-name">Method</span> m<span class="token punctuation">,</span> <span class="token class-name">Object</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Throwable</span></span>
<span class="line"> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token comment">// print implicit argument</span></span>
<span class="line"> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span>target<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token comment">// print method name</span></span>
<span class="line"> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"."</span> <span class="token operator">+</span> m<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"("</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token comment">// print explicit arguments</span></span>
<span class="line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>args <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span></span>
<span class="line"> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> args<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span></span>
<span class="line"> <span class="token punctuation">{</span></span>
<span class="line"> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span>args<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token keyword">if</span> <span class="token punctuation">(</span>i <span class="token operator"><</span> args<span class="token punctuation">.</span>length <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token string">", "</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token punctuation">}</span></span>
<span class="line"> <span class="token punctuation">}</span></span>
<span class="line"> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">")"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"> <span class="token comment">// invoke actual method</span></span>
<span class="line"> <span class="token keyword">return</span> m<span class="token punctuation">.</span><span class="token function">invoke</span><span class="token punctuation">(</span>target<span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"> <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="_3-5-3-代理类的特性" tabindex="-1"><a class="header-anchor" href="#_3-5-3-代理类的特性"><span>3.5.3 代理类的特性</span></a></h3><p>代理类是在程序运行过程中动态创建的,不过一旦创建,他们和其他类没有本质区别。</p><p>所有的代理类都扩展自 <code>Proxy</code> 类,每个代理类只有调用处理器一个实例字段,它在 <code>Proxy</code> 超类中定义,完成代理对象任务所需要的任何额外数据都要存储在调用处理器中。</p><p>所有的代理类都要覆盖 <code>Object</code> 类的 <code>toString</code> <code>equals</code> <code>hashCode</code> 方法,这些方法和其他代理方法一样都是在调用处理器上调用 <code>invoke</code> 方法, <code>Object</code> 类中的其他方法没有重新定义。</p><p>对于一个特定的类加载器和一组接口,只能有一个代理类,如果使用同一个类加载器和接口数组调用两次创建代理对象,将得到同一个类的两个对象,可以利用 <code>getProxyClass</code> 方法获得这个类。</p><p>代理类总是 <code>public</code> 和 <code>final</code> 的,如果代理类的所有接口都是 <code>public</code> 那么这个代理类就不属于任何特定的包;否则所有的非公共接口都必须属于同一个包,并且代理类也属于这个包。</p><p>可以通过调用 <code>Proxy</code> 类的 <code>isProxyClass</code> 方法检测一个特定的 <code>Class</code> 对象是否表示一个代理类。</p><p>使用一个目标代理的默认方法会触发调用处理器,要具体调用这个方法,可以使用 <code>InvocationHandler</code> 接口的静态 <code>invokeDefault</code> 方法,这样可以绕过调用处理器,直接调用代理对象的默认方法。</p><blockquote><p>代理类的关键在于类加载器,将参数传递给 <code>newProxyInstance</code> 方法创建代理对象之后,其余的代码和不使用代理并没有什么区别,只是只要在代理对象上调用一个方法就会触发调用处理器。</p></blockquote></div></div><footer class="page-meta"><div class="meta-item edit-link"><span class="xicon-container left meta-item-label"><!--[--><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32" class="xicon-icon" style="width:20px;height:20px;font-size:20px;color:inherit;"><path d="M2 26h28v2H2z" fill="currentColor"></path><path d="M25.4 9c.8-.8.8-2 0-2.8l-3.6-3.6c-.8-.8-2-.8-2.8 0l-15 15V24h6.4l15-15zm-5-5L24 7.6l-3 3L17.4 7l3-3zM6 22v-3.6l10-10l3.6 3.6l-10 10H6z" fill="currentColor"></path></svg><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->Edit this page<!--]--></span></span></div><div class="meta-item last-updated"><span class="xicon-container left meta-item-label"><!--[--><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32" class="xicon-icon" style="width:20px;height:20px;font-size:20px;color:inherit;"><path d="M26 4h-4V2h-2v2h-8V2h-2v2H6c-1.1 0-2 .9-2 2v20c0 1.1.9 2 2 2h20c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 22H6V12h20v14zm0-16H6V6h4v2h2V6h8v2h2V6h4v4z" fill="currentColor"></path></svg><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->Last Updated 2024/9/22 18:17:07<!--]--></span></span></div></footer><!----><!----></div><div class="page-catalog-container"><h5 class="tip">ON THIS PAGE</h5><ul><!--[--><!--[--><li class="page-catalog-menu-depth_2"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-1-接口" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.1 接口"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.1 接口<!--]--></span></span><!--[--><!--]--></a></li><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-1-1-接口的概念" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.1.1 接口的概念"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.1.1 接口的概念<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-1-2-接口的属性" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.1.2 接口的属性"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.1.2 接口的属性<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-1-3-接口与抽象类" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.1.3 接口与抽象类"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.1.3 接口与抽象类<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-1-4-静态和私有方法" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.1.4 静态和私有方法"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.1.4 静态和私有方法<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-1-5-默认方法" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.1.5 默认方法"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.1.5 默认方法<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-1-6-解决默认方法冲突" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.1.6 解决默认方法冲突"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.1.6 解决默认方法冲突<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-1-7-接口与回调" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.1.7 接口与回调"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.1.7 接口与回调<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-1-8-comparator接口" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.1.8 Comparator接口"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.1.8 Comparator接口<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-1-9-对象克隆" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.1.9 对象克隆"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.1.9 对象克隆<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--]--><!--[--><li class="page-catalog-menu-depth_2"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-2-lambda表达式" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.2 lambda表达式"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.2 lambda表达式<!--]--></span></span><!--[--><!--]--></a></li><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-2-1-为什么引入lambda表达式" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.2.1 为什么引入lambda表达式"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.2.1 为什么引入lambda表达式<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-2-2-lambda表达式的语法" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.2.2 lambda表达式的语法"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.2.2 lambda表达式的语法<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-2-3-函数式接口" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.2.3 函数式接口"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.2.3 函数式接口<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-2-4-方法引用" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.2.4 方法引用"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.2.4 方法引用<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-2-5-构造器引用" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.2.5 构造器引用"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.2.5 构造器引用<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-2-6-变量作用域" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.2.6 变量作用域"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.2.6 变量作用域<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-2-7-处理lambda表达式" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.2.7 处理lambda表达式"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.2.7 处理lambda表达式<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-2-8-再谈comparator" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.2.8 再谈Comparator"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.2.8 再谈Comparator<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--]--><!--[--><li class="page-catalog-menu-depth_2"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-3-内部类" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.3 内部类"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.3 内部类<!--]--></span></span><!--[--><!--]--></a></li><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-3-1-使用内部类访问对象状态" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.3.1 使用内部类访问对象状态"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.3.1 使用内部类访问对象状态<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-3-2-内部类的特殊语法规则" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.3.2 内部类的特殊语法规则"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.3.2 内部类的特殊语法规则<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-3-3-内部类是否有用、必要和安全" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.3.3 内部类是否有用、必要和安全"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.3.3 内部类是否有用、必要和安全<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-3-4-局部内部类" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.3.4 局部内部类"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.3.4 局部内部类<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-3-5-由外部方法访问变量" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.3.5 由外部方法访问变量"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.3.5 由外部方法访问变量<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-3-6-匿名内部类" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.3.6 匿名内部类"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.3.6 匿名内部类<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-3-7-静态内部类" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.3.7 静态内部类"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.3.7 静态内部类<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--]--><!--[--><li class="page-catalog-menu-depth_2"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-4-服务加载器" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.4 服务加载器"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.4 服务加载器<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_2"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-5-代理" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.5 代理"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.5 代理<!--]--></span></span><!--[--><!--]--></a></li><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-5-1-何时使用代理" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.5.1 何时使用代理"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.5.1 何时使用代理<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-5-2-创建代理对象" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.5.2 创建代理对象"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.5.2 创建代理对象<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--[--><li class="page-catalog-menu-depth_3"><a aria-current="page" href="/blogs/Java/Javamianxiangduixiangchengxusheji——jiekou、lambdabiaodashiyunabulei.html#_3-5-3-代理类的特性" class="router-link-active router-link-exact-active link page-catalog-item page-catalog-item" aria-label="3.5.3 代理类的特性"><!--[--><!--]--><span class="xicon-container left"><!--[--><!----><!--]--><span class="xicon-content" style="color:inherit;font-size:14px;"><!--[-->3.5.3 代理类的特性<!--]--></span></span><!--[--><!--]--></a></li><!--]--><!--]--><!--]--></ul></div></main><!--]--></div><!--[--><!----><!----><!--]--><!--]--></div>
<script type="module" src="/assets/app-B51tXhJU.js" defer></script>
</body>
</html>