Statistics
8
Views
0
Downloads
0
Donations
Support
Share
Uploader

高宏飞

Shared on 2026-02-19

Author[美] 布鲁斯·埃克尔

由mobi格式的电子书转换得到,和实体书扫描版比起来代码块更容易复制

Tags
No tags
ISBN: 7115585024
Publisher: 人民邮电出版社
Publish Year: 2022
Language: 中文
Pages: 656
File Format: PDF
File Size: 7.1 MB
Support Statistics
¥.00 · 0times
Text Preview (First 20 pages)
Registered users can read the full content for free

Register as a Gaohf Library member to read the complete e-book online for free and enjoy a better reading experience.

(This page has no text content)
版权信息 书名:On Java 中⽂版 进阶卷 作者:[美] 布鲁斯·埃克尔 译者:孙卓 陈德伟 臧秀涛 ISBN:978-7-115-58502-8 本书由北京图灵⽂化发展有限公司发⾏数字版。版权所有,侵权必究。 您购买的图灵电⼦书仅供您个⼈使⽤,未经授权,不得以任何⽅式复制和 传播本书内容。 我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。 如果购买者有侵权⾏为,我们可能对该⽤户实施包括但不限于关闭该帐号 等维权措施,并可能追究法律责任。
第 1 章 枚举类型 1.1 枚举类型的基本特性 静态导⼊枚举类型 1.2 在枚举类型中增加⾃定义⽅法 重载枚举类型中的⽅法 1.3 在switch语句中使⽤枚举 1.4 values()⽅法的神秘之处 1.5 实现,⽽不是继承 1.6 随机选择 1.7 使⽤接⼝来组织枚举 1.8 ⽤EnumSet来代替标记 1.9 使⽤EnumMap 1.10 常量特定⽅法 1.10.1 ⽤枚举实现职责链模式 1.10.2 ⽤枚举实现状态机 1.11 多路分发 1.11.1 使⽤枚举类型分发 1.11.2 使⽤常量特定⽅法 1.11.3 使⽤EnumMap分发 1.11.4 使⽤⼆维数组 1.12 ⽀持模式匹配的新特性 1.13 新特性:switch中的箭头语法 1.14 新特性:switch中的case null 1.15 新特性:将switch作为表达式 1.16 新特性:智能转型 1.17 新特性:模式匹配 1.17.1 违反⾥⽒替换原则 1.17.2 守卫 1.17.3 ⽀配性 1.17.4 覆盖范围
1.18 总结 第 2 章 对象传递和返回 2.1 传递引⽤ 引⽤别名 2.2 创建本地副本 2.2.1 值传递 2.2.2 克隆对象 2.2.3 为类增加可克隆能⼒ 2.2.4 成功的克隆 2.2.5 Object.clone()的效果 2.2.6 克隆组合对象 2.2.7 深拷⻉ArrayList 2.2.8 通过序列化进⾏深拷⻉ 2.2.9 在继承层次结构中增加可克隆性并向下覆盖 2.2.10 为什么⽤这种奇怪的设计 2.3 控制可克隆性 复制构造器 2.4 不可变类 2.4.1 创建不可变类 2.4.2 不可变性的缺点 2.4.3 String很特殊 2.5 总结 第 3 章 集合主题 3.1 样例数据 3.2 List的⾏为 3.3 Set的⾏为 3.4 在Map上使⽤函数式操作 3.5 选择Map的部分元素 3.6 填充集合 3.6.1 使⽤Suppliers来填充Collection
3.6.2 使⽤Suppliers来填充Map 3.7 使⽤享元⾃定义Collection和Map 3.8 Collection的功能 3.9 可选的操作 不⽀持的操作 3.10 Set与存储顺序 SortedSet 3.11 Queue 3.11.1 优先级队列 3.11.2 Deque 3.12 理解Map 3.12.1 性能 3.12.2 SortedMap 3.12.3 LinkedHashMap 3.13 ⼯具函数 3.13.1 List上的排序和查找 3.13.2 创建不可修改的Collection或Map 3.13.3 同步Collection或Map 3.14 持有引⽤ WeakHashMap 3.15 Java 1.0/1.1的集合类 3.15.1 Vector和Enumeration 3.15.2 Hashtable 3.15.3 Stack 3.15.4 BitSet 3.16 总结 第 4 章 注解 4.1 基本语法 4.1.1 定义注解 4.1.2 元注解
4.2 编写注解处理器 4.2.1 注解元素 4.2.2 默认值的限制 4.2.3 ⽣成外部⽂件 4.2.4 注解不⽀持继承 4.2.5 实现处理器 4.3 ⽤javac处理注解 4.3.1 最简单的处理器 4.3.2 更复杂的处理器 4.4 基于注解的单元测试 4.4.1 在@Unit中使⽤泛型 4.4.2 实现@Unit 4.5 总结 第 5 章 并发编程 5.1 令⼈迷惑的术语 并发的新定义 5.2 并发的超能⼒ 5.3 并发为速度⽽⽣ 5.4 Java并发四定律 5.4.1 不要使⽤并发 5.4.2 ⼀切都不可信,⼀切都很重要 5.4.3 能运⾏并不代表没有问题 5.4.4 你终究要理解并发 5.5 残酷的事实 5.6 本章剩余部分 5.7 并⾏流 5.7.1 parallel()并⾮灵丹妙药 5.7.2 parallel()和limit()的作⽤ 5.7.3 并⾏流只是看起来很简单 5.8 创建和运⾏任务
5.8.1 Task和Executor 5.8.2 使⽤更多的线程 5.8.3 ⽣成结果 5.8.4 作为任务的lambda与⽅法引⽤ 5.9 终⽌⻓时间运⾏的任务 5.10 CompletableFuture 5.10.1 基本⽤法 5.10.2 其他操作 5.10.3 合并多个CompletableFuture 5.10.4 模拟场景应⽤ 5.10.5 异常 5.11 死锁 5.12 构造器并不是线程安全的 5.13 ⼯作量、复杂性、成本 5.14 总结 5.14.1 缺点 5.14.2 Java核⼼设计的失败之处 5.14.3 其他的库 5.14.4 设想⼀种为并发⽽设计的语⾔ 5.14.5 延伸阅读 第 6 章 底层并发 6.1 什么是线程? 6.1.1 最佳线程数 6.1.2 我可以创建多少线程 6.2 捕获异常 6.3 共享资源 6.3.1 资源竞争 6.3.2 解决资源竞争 6.3.3 将EvenProducer同步化 6.4 volatile关键字
6.4.1 字分裂 6.4.2 可⻅性 6.4.3 (指令)重排序和先⾏发⽣ 6.4.4 何时使⽤volatile 6.5 原⼦性 6.5.1 Josh的序列号 6.5.2 原⼦类 6.6 临界区 6.6.1 在其他对象上进⾏同步 6.6.2 使⽤显式Lock对象 6.7 库组件 6.7.1 延迟队列DelayQueue 6.7.2 优先级阻塞队列PriorityBlockingQueue 6.7.3 ⽆锁集合 6.8 总结 第 7 章 Java I/O系统 7.1 I/O流 7.1.1 各种InputStream类型 7.1.2 各种OutputStream类型 7.1.3 添加属性和有⽤的接⼝ 7.1.4 各种Reader和Writer 7.1.5 ⾃成⼀家的RandomAccessFile 7.1.6 I/O流的典型⽤法 7.1.7 ⼩结 7.2 标准I/O 7.2.1 从标准输⼊中读取 7.2.2 将System.out转换为PrintWriter 7.2.3 标准I/O重定向 7.2.4 进程控制 7.3 新I/O系统
7.3.1 字节缓冲区ByteBuffer 7.3.2 转换数据 7.3.3 获取基本类型 7.3.4 视图缓冲区 7.3.5 ⽤缓冲区操纵数据 7.3.6 内存映射⽂件 7.3.7 ⽂件加锁 第 8 章 设计模式 8.1 设计模式的概念 8.2 单例模式 8.3 设计模式的分类 8.4 模板⽅法 8.5 封装实现 8.5.1 代理模式 8.5.2 状态模式 8.5.3 状态机模式 8.6 ⼯⼚模式:封装对象的创建 8.6.1 动态⼯⼚模式 8.6.2 多态⼯⼚模式 8.6.3 抽象⼯⼚模式 8.7 函数对象模式 8.7.1 命令模式 8.7.2 策略模式 8.7.3 职责链模式 8.8 改变接⼝ 8.8.1 适配器模式 8.8.2 外观模式 8.9 解释器模式:运⾏时的灵活性 8.10 回调 8.10.1 观察者模式
8.10.2 ⽰例:观察花朵 8.10.3 ⼀个可视化的观察者⽰例 8.11 多路分发 访问者模式:多路分发中的⼀种类型 8.12 模式重构 8.12.1 Trash和它的⼦类 8.12.2 信使对象 8.12.3 使⼯⼚通⽤化 8.12.4 从⽂件解析Trash 8.12.5 ⽤DynaFactory实现回收 8.12.6 将⽤法抽象化 8.12.7 ⽤多路分发重新设计 8.12.8 访问者模式 8.12.9 反射是有害的? 8.13 总结 附录 A 编程指南 A.1 设计建议 A.2 实现建议 附录 B Javadoc B.1 语法 B.2 嵌⼊式HTML B.3 部分⽰例标签 B.3.1 @see B.3.2 {@linkpackage.class\#member label} B.3.3 {@docRoot} B.3.4 {@inheritDoc} B.3.5 @version B.3.6 @author B.3.7 @since B.3.8 @param
B.3.9 @return B.3.10 @throws B.3.11 @deprecated B.4 ⽂档⽰例 附录 C 理解equals()和hashCode() C.1 经典的equals() ⼦类型之间的相等性 C.2 哈希和哈希码 C.2.1 理解hashCode() C.2.2 ⽤哈希来提⾼速度 C.2.3 重写hashCode() C.3 HashMap调优 附录 D 数据压缩 D.1 使⽤GZIP进⾏简单的压缩 D.2 使⽤Zip进⾏多⽂件存储 D.3 Java档案(Jars) 附录 E 对象序列化 E.1 概述 E.2 查找对应的类 E.3 控制序列化 E.3.1 transient关键字 E.3.2 Externalizable的替代⽅案 E.3.3 版本控制 E.4 使⽤持久性 XML 附录 F 静态类型检查的利与弊 F.1 2021版前⾔ F.2 原版前⾔ F.3 静态类型检查与测试 类型检查也只是⼀种测试
F.4 如何讨论类型检查 F.4.1 弱类型与弱类型检查 F.4.2 模板、潜在类型和Java泛型 F.4.3 灵活性与(所认为的)安全性 F.4.4 是否需要潜在类型/结构化类型 F.4.5 我们其实是在讨论测试 F.4.6 决定论 F.5 ⽣产⼒成本 F.6 静态与动态 附录 G 成为⼀名程序员 G.1 我是如何⾛上编程道路的 G.2 计算机相关职业 延伸阅读 G.3 神奇的5% G.4 编写软件就像写作 G.5 编程即打字 G.6 做热爱的事情
第 1 章 枚举类型 enum关键字⽤于创建⼀个新类型,其中包含⼀组数量有限的命名变 量,并视这些变量为常规程序组件。实践表明这是⼀种⾮常有⽤的类 型。 Joshua Bloch为本章的写作提供了极⼤的帮助。 《On Java中⽂版 基础卷》 第6章6.9节曾对枚举类型做过简短的介绍。 不过,鉴于你现在已经了解了Java的⼀些进阶知识,我们可以对Java枚举 类型做⼀些更深⼊的讲解。你会看到枚举类型⾮常有⽤,⽽且还会领略更 多的语⾔特性,⽐如泛型和反射等。同时你还会了解更多设计模式⽅⾯的 知识。 后简称“基础卷”。——编者注 1 1 2 2
1.1 枚举类型的基本特性 正如在第6章中所看到的,你可以调⽤枚举类型中的values()⽅法来遍历 枚举常量列表。values()⽅法⽣成⼀个由枚举常量组成的数组,其中常 量的顺序和常量声明的顺序保持⼀致,这样你就可以⽅便地(⽐如通过 for-in循环)使⽤结果数组了。 当创建枚举类型时,编译器会为你⽣成⼀个辅助类,这个类⾃动继承⾃ java.lang.Enum。java.lang.Enum提供了下例所⽰的⼀些功能: // enums/EnumClass.java // Enum类的能⼒ enum Shrubbery { GROUND, CRAWLING, HANGING } public class EnumClass { public static void main(String[] args) { for(Shrubbery s : Shrubbery.values()) { System.out.println( s + " ordinal: " + s.ordinal()); System.out.print( s.compareTo(Shrubbery.CRAWLING) + " "); System.out.print( s.equals(Shrubbery.CRAWLING) + " "); System.out.println(s == Shrubbery.CRAWLING); System.out.println(s.getDeclaringClass()); System.out.println(s.name()); System.out.println("********************"); } // 根据字符串名⽣成⼀个枚举值: for(String s : "HANGING CRAWLING GROUND".split(" ")) { Shrubbery shrub = Enum.valueOf(Shrubbery.class, s); System.out.println(shrub); } } } /* 输出: GROUND ordinal: 0 -1 false false class Shrubbery GROUND ******************** CRAWLING ordinal: 1 0 true true class Shrubbery CRAWLING ******************** HANGING ordinal: 2
1 false false class Shrubbery HANGING ******************** HANGING CRAWLING GROUND */ ordinal()⽅法返回⼀个从0开始的int值,代表每个枚举实例的声明顺 序。你可以放⼼地使⽤==来⽐较枚举实例(equals()和hashCode()⽅ 法会由编译器⾃动为你⽣成)。Enum类实现了Comparable接⼝(因此可 ⽐较),所以⾃动包含了compareTo()⽅法,另外它还实现了 Serializable接⼝(因此可序列化)。 如果调⽤枚举实例的getDeclaringClass() ⽅法,则会得到该枚举实 例所属的外部包装类。 name()⽅法返回枚举实例被声明的名称,使⽤toString()同样也可以 返回该名称。valueOf()⽅法是Enum类中的静态⽅法,它根据传⼊的 String,返回名称与该String匹配的枚举实例。如果匹配的实例不存 在,则抛出异常。 静态导⼊枚举类型 下⾯是基础卷第6章中Burrito.java类的⼀个变体: // enums/SpicinessEnum.java package enums; public enum SpicinessEnum { NOT, MILD, MEDIUM, HOT, FLAMING } // enums/Burrito2.java // {java enums.Burrito2} package enums; import static enums.SpicinessEnum.*; public class Burrito2 { SpicinessEnum degree; public Burrito2(SpicinessEnum degree) { this.degree = degree; } @Override public String toString() { return "Burrito is "+ degree; } public static void main(String[] args) {
System.out.println(new Burrito2(NOT)); System.out.println(new Burrito2(MEDIUM)); System.out.println(new Burrito2(HOT)); } } /* 输出: Burrito is NOT Burrito is MEDIUM Burrito is HOT */ static import将所有的枚举实例标识符都引⼊了本地命名空间,因此 它们不需要显式地使⽤枚举类型来限定。相较于显式地⽤枚举类型来限定 枚举实例,哪种⽅式更好呢?这很⼤程度上要视代码的复杂程度⽽定。编 译器肯定会保障类型的正确性,所以你唯⼀要关⼼的就是代码的可读性如 何。⼀般来说不会有⼤问题,但还是要根据具体情况评估。 注意,如果枚举定义在同⼀个⽂件中,或者定义在默认包中,则⽆法使⽤ 该⽅式(显然在Sun公司的内部,对于是否允许这种情况有过⼀些争 论)。
1.2 在枚举类型中增加⾃定义⽅法 对于枚举类型来说,除了⽆法继承它以外,基本可以将它看作⼀个普通的 类。这意味着你可以在⾥⾯增加⾃定义的⽅法,甚⾄可以增加⼀个 main()⽅法。 正如你所⻅,默认的toString()⽅法只会返回枚举实例的名称,⽽你很 可能想为枚举实例⽣成不同于该默认⽅式的描述。为此,你可以实现⼀个 构造⽅法,以获取额外的信息,然后再⽤额外的⽅法来提供扩展描述,如 下例所⽰: // enums/OzWitch.java // 《绿野仙踪》中的⼥巫 public enum OzWitch { // 实例必须在⽅法之前定义: WEST("Miss Gulch, aka the Wicked Witch of the West"), NORTH("Glinda, the Good Witch of the North"), EAST("Wicked Witch of the East, wearer of the Ruby " + "Slippers, crushed by Dorothy's house"), SOUTH("Good by inference, but missing"); private String description; // 构造器的访问权限必须是包级或private: private OzWitch(String description) { this.description = description; } public String getDescription() { return description; } public static void main(String[] args) { for(OzWitch witch : OzWitch.values()) System.out.println( witch + ": " + witch.getDescription()); } } /* 输出: WEST: Miss Gulch, aka the Wicked Witch of the West NORTH: Glinda, the Good Witch of the North EAST: Wicked Witch of the East, wearer of the Ruby Slippers, crushed by Dorothy's house SOUTH: Good by inference, but missing */ 如果你想增加⾃定义⽅法,则必须先⽤分号结束枚举实例的序列。同时, Java会强制你在枚举中先定义实例。如果在定义实例之前定义了任何⽅法 或字段,则会抛出编译时错误。 枚举类型的构造器和⽅法的写法与普通类⼀样,因为除了少量特殊限制 外,它就是⼀个普通的类。你⼏乎可以对它做任何你想做的事(虽然你通
常只会使⽤最简单的枚举类型)。 虽然本例中的构造器是私有的,但使⽤哪种访问权限实际上区别并不⼤: 构造⽅法只能⽤来创建你在枚举定义中声明的枚举实例;在枚举定义完成 后,编译器不会允许你⽤它来创建任何新的类型。 重载枚举类型中的⽅法 还有另⼀种为枚举⽣成不同的String值的⽅式:重载toString()⽅ 法。在下⾯的⽰例中,实例名没什么问题,但我们希望换⼀种格式来显 ⽰。重载enum的toString()⽅法和重载任何普通类的⽅法相同: // enums/SpaceShip.java import java.util.stream.*; public enum SpaceShip { SCOUT, CARGO, TRANSPORT, CRUISER, BATTLESHIP, MOTHERSHIP; @Override public String toString() { String id = name(); String lower = id.substring(1).toLowerCase(); return id.charAt(0) + lower; } public static void main(String[] args) { Stream.of(values()) .forEach(System.out::println); } } /* 输出: Scout Cargo Transport Cruiser Battleship Mothership */ toString()⽅法通过调⽤name()⽅法获取SpaceShip的名称,并且修 改了结果,使得结果中的英⽂单词仅⾸字⺟为⼤写。
1.3 在switch语句中使⽤枚举 枚举类型的⼀个⾮常⽅便之处就是可以⽤在switch语句中。通常, switch语句只能使⽤整型或字符串类型的值,但是由于enum内部已经构 建了⼀个整型序列,并且可以通过ordinal()⽅法来得到枚举实例的顺序 (显然编译器做了相应的⼯作),所以枚举类型可以⽤在switch语句 中。 虽然通常要使⽤枚举实例,就必须⽤枚举的类型名来限定它,但在case语 句中你⽆须这么做。以下⽰例中使⽤了枚举来创建⼀个简单的状态机: // enums/TrafficLight.java // 在switch语句中使⽤枚举 // 定义⼀个枚举类型: enum Signal { GREEN, YELLOW, RED, } public class TrafficLight { Signal color = Signal.RED; public void change() { switch(color) { // 注意在case语句中,⽆须使⽤Signal.RED case RED: color = Signal.GREEN; break; case GREEN: color = Signal.YELLOW; break; case YELLOW: color = Signal.RED; break; } } @Override public String toString() { return "The traffic light is " + color; } public static void main(String[] args) { TrafficLight t = new TrafficLight(); for(int i = 0; i < 7; i++) { System.out.println(t); t.change(); } } } /* 输出: The traffic light is RED The traffic light is GREEN The traffic light is YELLOW The traffic light is RED The traffic light is GREEN The traffic light is YELLOW The traffic light is RED */
编译器没有因为该switch中没有default语句⽽报错,但这并不是因为 它注意到了你为每个Signal实例都编写了case分⽀。即使你注释掉某个 case分⽀,也仍然不会报错。这意味着你必须要⼩⼼,确保你⼿动覆盖了 所有的分⽀。但是,如果此时你在case语句中调⽤了return(⽽且没有 编写dafault),则编译器会报错,即使你已经覆盖到了枚举中的所有 值。