序#
《On Java》是一本值得一读的好书。尽管有Java
的基础,但在阅读本书时,在基础部分还是能获得新的理解。作者作为《Thinking in Java》的作者,擅长在讲解用法之余,还能向你普及这个知识点的由来,为什么要用它,它的价值体现在哪里,它到底是什么。我不希望读了一遍后又忘了,所以每章都根据心得体会写下些什么,后续也可以直接阅读本文来回顾。你是不是忘记了什么中二计划?
第 1 章 什么是对象#
这一章主要是给读者总结Java
的核心特点。没有学会Java
的读者可以以小见大,心中有一个对Java
大概的了解;有过基础的读者也可以进一步加深印象,更能读到作者对Java
独到的见解。
图灵官方对本章的导读指南为 “了解即可”。就个人来说的话,本章最主要的知识重点为以下几点:
抽象的历程#
所有编程语言都是一种抽象。甚至可以说,我们能够解决的问题的复杂程度直接取决于抽象的类型和质量。
汇编语言是对机器码的一种抽象;面向过程编程的 C 语言,它是对汇编语言的一种抽象。在面向过程编程时期,程序员要把自身姿态更多的放到计算机层面,要以计算机的角度看问题如何解决。这样的抽象类型,还是需要频繁考虑计算机的结构而非问题本身的结构。
面向对象编程的出现,很好的解决了之前面对过程编程的痛点。按本书说法,早期的程序员需要在机器模型(解决方案空间,也就是计算机)和实际需要解决的问题模型(问题实际存在的空间,也就是业务)之间建立关联。但面向过程编程建立的关联抽象程度不高,程序员仍然要考虑计算机,需要花费精力维护这种关联。而面对对象编程,实际上将机器模型的元素与问题模型的元素通过对象这一载体一一映射,使得程序员可以通过面向对象思想的强大抽象能力在机器模型中直接描述问题模型,解决关联问题。你阅读的既是机器模型提供的解决方案 —— 代码,也是对问题模型中实际业务的描述。显然这种抽象类型更通用,也更高效。
面对对象编程的宗旨#
面对对象编程的宗旨,就是创建或使用对象提供的某种服务来解决问题。程序也为用户提供服务,它通过使用某些对象提供的服务来做到这一点。
实现隐藏#
设计一个好的类的技巧之一是隐藏细节。不要事无巨细的暴露细节给使用方,因为你不知道对方会如何使用你的类,这将为今后修改类制造隐患。所以我们对外只提供服务,隐藏其他所有不必要的信息。让使用方更信任我们的服务不会因更新而失效,我们也能放心大胆的修改重构,不必担心某段代码是否有被使用的可能。
复用、继承与多态#
这里介绍了一些Java
的重要特点。继承与多态的目的其实就是为了尽可能的复用代码。例如当我们需要创建一个与已有的类十分相似的时候,就不用再写一堆代码,直接继承就好了。但实际上应该谨慎考虑使用继承,继承的原则在于尽量满足替换原则,即符合 “is-a” 关系,意思是 “A 是 B”。一个较为清晰的判断是否需要使用继承的问题是:“我是否需要向上转型?”。
在实际编写过程中,常常需要让方法不依赖具体的类。方法只要保证传入的是基类就行了,无需关心传入的子类具体是什么,省去了编写冗长的判断代码,交给运行时去执行具体子类的方法就好了,这就是多态。
第 2 章 安装 Java 和本书示例#
值得一提的是,本章并没有关于环境变量与
classpath
等的讲解,安装步骤也使用Win
平台的Chocolatey与Mac OS
的HomeBrew这类包管理工具解决了,两者都会自动处理环境变量。当然,我们需要知道设置环境变量是为了方便在控制台编译和运行Java
程序,只是现在的IDE
与脚本越来越智能,已经能实现自动设置环境变量了。
第 3 章 对象无处不在#
正所谓 “万物皆对象”。在本章中,作者将论述Java
中最重要的对象,以及参与Java
程序运行的各方面 “势力”。图灵导读的建议是 “了解运行和创建一个简单的Java
程序所需知识”,因此本章重点总结以下几点:
对象与引用#
本节中作者讲到,虽然Java
中对象无处不在,但实际上我们不是真正的在和对象打交道。我们都知道,编程语言最终是在内存处理数据的,那么,如何安全的在内存中处理自然就是一个问题。是直接操作内存好呢?还是用某种方法间接操作内存呢?Java
给出了它认为的理想的答案:使用引用。引用就像遥控器操纵电视机一样控制对象,调用对象行为,改变对象状态。
数据保存在哪里?#
这一节介绍了Java
的数据存储方式:
- 1. 寄存器。从计组中已经认识到寄存器是存取速度最快的数据存储方式,毕竟它直接保存在中央处理器,也就是 CPU 里。不过寄存器寸土寸金,所以
Java
不允许手动控制寄存器的分配。 - 2. 栈。在 RAM 中,处理器可以通过栈指针操作数据。可是根据数据结构我们知道,栈这种数据结构,在增删方面不够灵活,因此只有某些数据(比如对象引用)保存在栈上,对象本身却并非如此。
- 3. 堆。在 RAM 中,堆是一个通用的内存池,用于存放所有
Java
对象。编译器并不关心堆上对象的生命周期,因为其有对应的分配和清理办法,而且随着时代的进步,Java
的堆内存分配机制已经变得非常高效了。 - 4. 常量存储。常量直接保存在程序代码中,或 ROM 中。例如所有的字符串和字符串常量,都保存在字符串资源池中。
- 5. 非 RAM 存储。通常意义就是那些不在内存的数据,比如序列化对象,一种可以发送到其他机器的对象;还有持久化对象,一种保存到磁盘上的对象。这些数据存储类型可以将对象以其他媒介保存,需要时再转换回对象。最典型的例子就是数据库存储。
第 4 章 & 第 5 章 操作符 & 控制流#
这两章详细阐述了Java
的操作符和控制流语法。图灵导读的建议是 “都需掌握,无难点”。鉴于基本与C
无异,这里只记录一下读书后比较感兴趣的点。
对象赋值需谨慎#
前面说过,我们其实并没有真正操作对象本身,只是操作对象的引用,所以当执行对象赋值时,其实是将引用复制给了另一个引用,这会导致两个引用指向同一个对象,而被赋值的引用所对应的对象就没有引用了。这个示例告诉我们对象赋值需要谨慎,可能会有意想不到的结果。
Java == C++ -- --#
这是Java
创始人高司令的观点,他认为Java
是C++
去除了一些没必要又很难的内容,是一种更精简的语言。当然也不是纯减法 2333
== 和!=#
不同方法创建的相同内容的封装类,内存中存放的位置会有不同。而操作符==
和!=
比较的是对象的引用,所以对对象比较时,最好使用equals()
。
boolean-exp ? value0 :value1
三元操作符#
它主要用于从两个值中选择一个给变量赋值的场景。比完成同样场景的if-else
更紧凑。
for-in
#
float[] f = new float[10];
for(int i=0; i<10; i++)
f[i] = rand.nextFloat();
for(float x : f)
System.out.println(x);
可惜for-in
只能运用于遍历相关。你并不能修改 f 中的元素。
for(;;)
#
编译器同等对待
while(true)
和for(;;)
,Java
源码中对这两种方法的使用也几乎平分秋色。
Math.random()
的取值范围#
其实本来是一个使用字符串的switch
例子。告诉我们Math.random()
的严格取值范围为[0,1)
。