2023年6月20日发(作者:)

《Java编程思想》总结⼀、从机器语⾔到⾼级语⾔程序员对计算机的任何操作都是通过机器语⾔完成的。机器语⾔是⼆进制代码,由操作码和操作数组成。在物理层⾯上,每⼀个操作码都由相应的电路来实现。由于机器语⾔的可读性和可移植性很差,先后出现了低级语⾔和⾼级语⾔。(相较于“低级”和“⾼级”,机器语⾔是“底层”语⾔)最常见的低级语⾔是汇编语⾔,汇编语⾔程序经过汇编过程可以转换为机器码。汇编语⾔⽤⼀些容易理解和记忆的字母或单词来代替指令,因此程序的可读性得到了提升。但是,汇编语⾔的可移植性仍然特别差:在不同的设备中,汇编语⾔对应着不同的机器语⾔指令集,不同平台之间不可直接移植。⾼级语⾔基本脱离了机器的硬件系统,⽤⼈类更易理解的⽅式编写程序。⾼级语⾔的执⾏⽅式有两种,⼀种是解释,⼀种是编译。解释是源程序翻译⼀句就执⾏⼀句的过程,⽽编译是把源程序翻译成可执⾏的⽬标代码,再由⽤户决定何时执⾏。C语⾔是⽐较传统的⾼级语⾔。C语⾔程序经过编译转换为汇编码,再经过汇编转换为机器码。与汇编语⾔相⽐,C语⾔简洁、紧凑,书写形式⾃由,具有良好的可读性。C语⾔具有⼀定的可移植性:在不同的设备上,C语⾔程序可以“⼊乡随俗地”根据设备的规则编译成可执⾏的汇编码,再经过汇编执⾏。但是这样的可移植性是不够的。Java也是⼀门⾼级语⾔,它的设计⽬标之⼀便是“⼀次编译,到处运⾏。”

Java解决可移植性问题的⽅式是统⼀使⽤JVM(Java Virtual Machine,Java虚拟机)来运⾏Java程序(即平台适应代码)。在不同的设备上,遵循《Java虚拟机规范》实现JVM——虽然在不同设备上JVM的实现是有区别的,但它们遵循相同的规范。在运⾏Java程序时,Java程序先经过编译转换为字节码,再由JVM解释执⾏。Java凭借着JVM获得了优秀的可移植性。语⾔实际上是帮助程序员更容易地操作计算机的⼯具,选择何种语⾔来编程,是Java还是C++,本质上相当于“选择腾讯视频还是优酷视频来观看电视节⽬(那么选择汇编语⾔就是选择了电视机)”。正如腾讯视频是腾讯公司的产品,Java是美国公司Sun的产品。希望读者能明⽩:语⾔只是⼯具。⼆、JDKJDK(Java Development Kit,Java开发⼯具包)为程序员开发Java程序提供了⽀持。举个例⼦,假如程序员想要创建字符串"Hello World",Java代码写作: String str = “Hello World”;Java语⾔规范认为上⾯的语句是合法的,⽽程序员之所以可以这样写代码,是因为在JDK中提供了String⼯具——如果没有JDK,编译器就不认识String。我们研究Java,实际上也是在研究JDK源码。三、Hello World如何判断⼀个⼈是不是程序员?看到“Hello World”,DNA动了的是程序员,否则就不是。下⾯是经典的Hello World程序: public class HelloWorld { public static void main(String[] args) { n("Hello World"); } }运⾏此程序,打印台输出字符串“Hello World”。main⽅法是程序的⼊⼝,执⾏main⽅法的实际过程如下:1)运⾏%JAVA_HOME%/bin/,对⽂件进⾏编译,⽣成⽂件。2)运⾏%JAVA_HOME%/bin/,解释执⾏。.java⽂件是Java的编译单元,⾥⾯是我们写的Java代码,.class⽂件则对应着字节码。四、Java是⼀门程序设计语⾔在讨论Java的任何特性之前,⾸先Java是⼀门程序设计语⾔。既然是程序设计语⾔,就离不开语句和程序控制结构。语句包括变量和操作符。程序控制结构包括顺序结构、分⽀结构和循环结构。这部分内容⽐较好理解(⽽且很琐碎),就不细说了。在Java中,字符串操作⼗分常见。字符串String最重要的特点是:String对象不可变。这意味着String类中每⼀个看起来会修改String值的⽅法,实际上都是创建了⼀个全新的String对象,以包含修改后的字符串内容。补充:《Java编程思想》中提到:如果程序中有多个String对象,都包含相同的字符串序列,那么这些String对象都映射到同⼀块内存区域。五、Java是⾯向对象的什么是对象?《Java编程思想》中提到:我们将问题空间中的元素及其在解空间中的表⽰称为“对象”。我对这句话的理解是:⼀个对象就是⼀个问题和这个问题的解法的组合。还是很难理解。那么我们就还是通俗地理解为:万物皆为对象。⾯向对象程序中的⼀切⾏为都由对象来完成。如果想要解决某个问题,就创建⼀个可以解决这个问题的对象,因此每个对象都是带着它的职责诞⽣的。《Java编程思想》中提到:每个对象都有⼀个接⼝。Java初学者很容易狭隘地把接⼝仅仅理解为抽象类型interface,事实上,接⼝是更加⼴泛的概念。我对接⼝的理解是:接⼝实际上像是⼀种“刺激-反应”机制,它描述对象接收怎样的信息和对象收到信息后会返回怎样的信息,除此之外,对象的其它信息将会被隐藏——接⼝是建⽴在Java的封装机制之上才有意义的。

每个对象都⽀持这样的“刺激-反应”机制,在代码层⾯上,对象的接⼝就是这个对象可以调⽤的所有的⽅法,接⼝接收的信息就是⽅法名及⽅法的⼊参,接⼝返回的信息就是⽅法的返回值。再去理解“每个对象都有⼀个接⼝”这句话,⼤概就是,每个对象都有它可以调⽤的⽅法集。注意在上⾯的代码层⾯的描述中,⾃始⾄终都没有提到过⽅法体,Java抽象类型interface中的抽象⽅法也确实是没有⽅法体的。抽象过程的意图是站在⼀定的⾼度上去分析问题,因此对于程序设计具有重要的意义,在抽象与实现的关系上,原先是有了已实现的⽅法,再将这个⽅法抽象化(上升)。抽象类型interface将这个过程逆转了:先设计出接⼝,再将这个接⼝实现(落地)。这意味着由原先的对象选择接⼝,变成了接⼝选择对象,⽽⾯向对象的核⼼思想便是创建解决问题的对象,因此抽象类型interface体现了⾯向对象的思想。每个对象可以调⽤的⽅法都是由这个对象的类(class)描述的。类和对象的关系,就像是⼈类和张三的关系。⼈类是⼀套规范,张三是符合这套规范的个体,在⾯向对象的术语中,我们称张三是⼈类的⼀个实例。《Java编程思想》中提到:因为类描述了具有相同特性(数据元素)和⾏为(功能)的对象集合,所以⼀个类实际上就是⼀个数据类型。因此可以说,Java中的基本数据类型有8种,⽽数据类型有⽆数种。程序员通过定义类来适应问题,⽽不再被迫只能使⽤基本的数据类型(适应计算机中的存储单元)来解决问题。在整个编程过程中,程序员的⼯作就是定义类、创建对象、并引导对象解决实际问题。(实际上还要销毁对象,不过JVM帮我们做了这件事)六、⽤引⽤操纵对象《Java编程思想》中提到:尽管⼀切都看作对象,但操纵的标识符实际上是对象的⼀个引⽤。以下⾯的代码为例: Person zhangSan = new Person();其中的zhangSan就是引⽤,相当于对象的名称。在Java中,对象必须通过引⽤来操纵,就像⼀个⼈必须通过姓名或称号来指挥。⽽对于基本数据类型: int i = 2;int型变量2不是对象,i也不应该被称作对象的引⽤。这说明Java并不是纯粹的⾯向对象语⾔。对于⼀般类型的对象,其引⽤标识的内存区域内存储的是对象所在的地址。⽽对于8种基本数据类型的变量,其标识符标识的内存区域内存储的是真实的数值。之所以会这样,是因为每种基本数据类型的变量占⽤存储空间的⼤⼩是确定的,⽽对象的⼤⼩是难以确定的。七、封装《Java编程思想》中提到:把数据和⽅法包装进类中,以及具体实现的隐藏,共同被称作是封装。在Java中,最能体现封装思想的关键字是package。Java中的四种访问权限从⼤到⼩依次是:public、protected、包访问权限(没有关键字)和private。《Java编程思想》中提到:为了继承,⼀般的规则是将所有的数据成员都指定为private,将所有的⽅法指定为public。控制对成员的访问权限有两个原因:第⼀个原因是为了使⽤户不要碰触那些他们不该碰触的部分,⽤户实际上只应该使⽤为他们提供的接⼝。第⼆个原因是为了让类库设计者可以更改类的内部⼯作⽅式,⽽不必担⼼这样会对客户端程序员产⽣重⼤的影响——访问权限控制确保不会有任何客户端程序员依赖于某个类的底层实现的任何部分。⼋、代码复⽤Java中主要有两种代码复⽤⽅式:组合和继承。组合只需将对象引⽤置于新类中即可。假如我们要创建计算机类Computer,⽽此时类库中已经有了主机类Host、显⽰器类Monitor、⿏标类Mouse、键盘类Keyboard,我们就可以让Computer类包含⼀个Host类型的成员属性、⼀个Monitor类型的成员属性、⼀个Mouse类型的成员属性和⼀个Keyboard类型的成员属性(⼀个类就是⼀种数据类型),⽽不⽤在Computer类中再写有关主机、显⽰器、⿏标、键盘的代码,达到代码复⽤的⽬的。继承是使⽤关键字extends实现的。在⼀段继承关系中,导出类默认拥有它可以访问到的(public、protected修饰的、或者同包下包访问权限的)基类中的所有属性和⽅法。在⼀段继承关系的基类和导出类之间,应当存在is-a或者is-like-a的语义关系,意思就是不要继承没有⽤的代码(何况Java只⽀持单继承)。在写代码时,程序员通常是⾃由的,你完全可以⽤可乐类Cola继承书籍类Book,⽽Cola类的实例永远也⽤不到Book类的接⼝,我们对此情形的理解是:可乐不是(is-not-a)书,并且可乐不像(is-not-like-a)书。不过,如果我们认为Cola类和Book类中都应该有makeHappy()⽅法,我们可以这么理解:可乐像书⼀样(is-like-a)使我们快乐。这样的代码复⽤才是有价值的。is-a和is-like-a语义的区别在于,当导出类只覆盖了基类中的⽅法,⽽没有添加任何新⽅法的情况下,导出类和基类具有完全相同的接⼝,那么在任何场合下,导出类对象都可以完全替代⼀个基类对象,此时导出类和基类之间存在is-a语义。这是⼀种处理继承的理想⽅式,称为替代原则。那么如何在组合与继承之间进⾏选择?《Java编程思想》中提到:is-a(是⼀个)的关系是⽤继承来表达的,⽽has-a(有⼀个)的关系是⽤组合来表达的。除了组合和继承,还有第三种代码复⽤⽅式是代理。Java并没有提供对它的直接⽀持。代理是继承与组合之间的中庸之道,它将⼀个成员对象置于所要构造的类中(就像组合),但与此同时在新类中暴露该成员对象的所有⽅法(就像继承)。代理是常⽤的设计模式。九、多态我们举个例⼦说明什么是向上转型和向下转型:我们认为“男孩⼉是孩⼦,⼥孩⼉也是孩⼦,孩⼦都会做游戏”,写成代码就是: Child child = new Child(); me(); Child boy = new Boy(); me(); Child girl = new Girl(); me();在第3⾏代码中,new Boy()创建的是⼀个Boy类型的对象,⽽“=”操作符左边是⼀个操纵Child类型对象的引⽤,将⼀个Boy对象交给⼀个操纵Child对象的引⽤来操纵时,发⽣向上转型,Boy对象转型成Child对象。在第4⾏代码中,boy调⽤⽅法playGame()。boy操纵的是⼀个由Boy对象向上转型得到的Child对象,此时发⽣向下转型,这个对象重新转型成Boy对象,然后绑定Boy类中的playGame()⽅法⽽不是Child类中的playGame()⽅法。将⼀个⽅法调⽤同⼀个⽅法主体关联起来被称作绑定。若在程序执⾏前进⾏绑定(由编译器和连接程序实现),叫作前期绑定。多态技术的实现依赖于Java的后期绑定机制(也叫做动态绑定或运⾏时绑定),它的含义是在运⾏时根据对象的类型进⾏绑定。在Java中,动态绑定是默认⾏为,通常情况下,动态绑定会⾃动发⽣。除⾮你加了static或final关键字。《Java编程思想》中提到:多态是⼀项让程序员“将改变的事物与未变的事物分离开来”的重要技术。多态技术极⼤提升了程序的可扩展性。⼗、内部类为什么要在Java中增加内部类这项语⾔特性呢?

《Java编程思想》中提到:使⽤内部类最吸引⼈的原因是:每个内部类都能独⽴地继承⾃⼀个(接⼝的)实现,所以⽆论外围类是否已经继承了某个(接⼝的)实现,对于内部类都没有影响。内部类使得多重继承的解决⽅案变得完整。接⼝解决了部分问题,⽽内部类有效地实现了多重继承。也就是说,内部类允许继承多个⾮接⼝类型(类或抽象类)。⼗⼀、异常《Java编程思想》中提到:异常的基本的概念是⽤名称代表发⽣的问题,并且异常的名称应该可以望⽂知意。异常也是对象,所有的异常类都继承⾃Throwable类。JDK中定义了⼤量的异常类型,每当某种已定义的异常发⽣时,会创建⼀个异常对象并抛出(throw new xxxException(“xxx xxx xxx”))。Java异常分为被检查的异常(CheckedException)和不被检查的异常(UncheckedException)。在开发Java程序时,有时候编译器会强制你为某次调⽤操作做异常处理,这是因为被调⽤的⽅法中抛出了被检查的异常(如IOException)。《Java编程思想》中提到:被检查的异常强制你在可能还没准备好处理错误的时候被迫加上catch⼦句,会导致“吞⾷则有害”的问题:catch⽽不处理,异常将会丢失。异常确实发⽣了,但“吞⾷”后它却完全消失了。不被检查的异常是编译器不强制要求处理的异常,换⾔之,编译时不会检查。不被检查的异常⼀般包括各种运⾏时异常(RuntimeException),如空指针异常、数组越界异常等。⼗⼆、Java I/O系统《Java编程思想》中提到:流代表任何有能⼒产出数据的数据源对象或者是有能⼒接收数据的接收端对象。——流不是数据本⾝。所谓的输⼊流和输出流是相对于内存来说的,输⼊流从磁盘到内存,输出流从内存到磁盘。⼗三、泛型 《Java编程思想》中提到:泛型实现了参数化类型的概念,“泛型”这个术语的意思是:“适⽤于许多许多的类型”。《Java编程思想》中提到:Java泛型是使⽤擦除来实现的,这意味着当你在使⽤泛型时,任何具体的类型信息都被擦除了,你唯⼀知道的就是你在使⽤⼀个对象。《Java编程思想》中提到:擦除的核⼼动机是它使得泛化的客户端可以⽤⾮泛化的类库来使⽤,反之亦然,这经常被称为“迁移兼容性”。(直⽩地讲,兼容JDK 1.5之前没有泛型的版本的类库,也是因为这个原因,Java泛型的能⼒⼤打折扣)正如你想的那样,即使你从不使⽤泛型,仍然可以完成⼤部分的Java开发⼯作。那么为什么要往Java中引⼊泛型呢?《Java编程思想》中提到:我相信被称为泛型的通⽤语⾔特性(并⾮必须是其在Java中的特定实现)的⽬的在于可表达性,⼆不仅仅是为了创建类型安全的容器。类型安全的容器是能够创建更通⽤的代码这⼀能⼒所带来的副作⽤。《Java编程思想》中提到:泛型正如其名称所暗⽰的:它是⼀种⽅法,通过它可以编写出更“泛化”的代码,这些代码对于它们能够作⽤的类型具有更少的限制,因此单个的代码段可以应⽤到更多的类型上。⼗四、其他的⼀些内容有很多我没有写到的内容,包括注解、反射、枚举、并发等。我看过⼀些关于注解和反射的资料,总觉得对它们是理解的,但不能灵活运⽤到⾃⼰的代码中。在对它们有更加深⼊的了解之前,暂时写不出特别有价值的内容。枚举是⼀个⽐较简单的功能,没什么想说的。并发的内容⽐较多,⽽《Java编程思想》也没有讲特别深,(⽬前我看来)这部分内容需要知道的知识点⽐需要理解的知识点更多,我没有试着去整理这部分内容。《Java编程思想》是⼀本800多页的书,⼤概读到600多页的时候,切⾝感受到⾷之⽆味,弃之可惜。要啃这本书需谨慎。

2023年6月20日发(作者:)

《Java编程思想》总结⼀、从机器语⾔到⾼级语⾔程序员对计算机的任何操作都是通过机器语⾔完成的。机器语⾔是⼆进制代码,由操作码和操作数组成。在物理层⾯上,每⼀个操作码都由相应的电路来实现。由于机器语⾔的可读性和可移植性很差,先后出现了低级语⾔和⾼级语⾔。(相较于“低级”和“⾼级”,机器语⾔是“底层”语⾔)最常见的低级语⾔是汇编语⾔,汇编语⾔程序经过汇编过程可以转换为机器码。汇编语⾔⽤⼀些容易理解和记忆的字母或单词来代替指令,因此程序的可读性得到了提升。但是,汇编语⾔的可移植性仍然特别差:在不同的设备中,汇编语⾔对应着不同的机器语⾔指令集,不同平台之间不可直接移植。⾼级语⾔基本脱离了机器的硬件系统,⽤⼈类更易理解的⽅式编写程序。⾼级语⾔的执⾏⽅式有两种,⼀种是解释,⼀种是编译。解释是源程序翻译⼀句就执⾏⼀句的过程,⽽编译是把源程序翻译成可执⾏的⽬标代码,再由⽤户决定何时执⾏。C语⾔是⽐较传统的⾼级语⾔。C语⾔程序经过编译转换为汇编码,再经过汇编转换为机器码。与汇编语⾔相⽐,C语⾔简洁、紧凑,书写形式⾃由,具有良好的可读性。C语⾔具有⼀定的可移植性:在不同的设备上,C语⾔程序可以“⼊乡随俗地”根据设备的规则编译成可执⾏的汇编码,再经过汇编执⾏。但是这样的可移植性是不够的。Java也是⼀门⾼级语⾔,它的设计⽬标之⼀便是“⼀次编译,到处运⾏。”

Java解决可移植性问题的⽅式是统⼀使⽤JVM(Java Virtual Machine,Java虚拟机)来运⾏Java程序(即平台适应代码)。在不同的设备上,遵循《Java虚拟机规范》实现JVM——虽然在不同设备上JVM的实现是有区别的,但它们遵循相同的规范。在运⾏Java程序时,Java程序先经过编译转换为字节码,再由JVM解释执⾏。Java凭借着JVM获得了优秀的可移植性。语⾔实际上是帮助程序员更容易地操作计算机的⼯具,选择何种语⾔来编程,是Java还是C++,本质上相当于“选择腾讯视频还是优酷视频来观看电视节⽬(那么选择汇编语⾔就是选择了电视机)”。正如腾讯视频是腾讯公司的产品,Java是美国公司Sun的产品。希望读者能明⽩:语⾔只是⼯具。⼆、JDKJDK(Java Development Kit,Java开发⼯具包)为程序员开发Java程序提供了⽀持。举个例⼦,假如程序员想要创建字符串"Hello World",Java代码写作: String str = “Hello World”;Java语⾔规范认为上⾯的语句是合法的,⽽程序员之所以可以这样写代码,是因为在JDK中提供了String⼯具——如果没有JDK,编译器就不认识String。我们研究Java,实际上也是在研究JDK源码。三、Hello World如何判断⼀个⼈是不是程序员?看到“Hello World”,DNA动了的是程序员,否则就不是。下⾯是经典的Hello World程序: public class HelloWorld { public static void main(String[] args) { n("Hello World"); } }运⾏此程序,打印台输出字符串“Hello World”。main⽅法是程序的⼊⼝,执⾏main⽅法的实际过程如下:1)运⾏%JAVA_HOME%/bin/,对⽂件进⾏编译,⽣成⽂件。2)运⾏%JAVA_HOME%/bin/,解释执⾏。.java⽂件是Java的编译单元,⾥⾯是我们写的Java代码,.class⽂件则对应着字节码。四、Java是⼀门程序设计语⾔在讨论Java的任何特性之前,⾸先Java是⼀门程序设计语⾔。既然是程序设计语⾔,就离不开语句和程序控制结构。语句包括变量和操作符。程序控制结构包括顺序结构、分⽀结构和循环结构。这部分内容⽐较好理解(⽽且很琐碎),就不细说了。在Java中,字符串操作⼗分常见。字符串String最重要的特点是:String对象不可变。这意味着String类中每⼀个看起来会修改String值的⽅法,实际上都是创建了⼀个全新的String对象,以包含修改后的字符串内容。补充:《Java编程思想》中提到:如果程序中有多个String对象,都包含相同的字符串序列,那么这些String对象都映射到同⼀块内存区域。五、Java是⾯向对象的什么是对象?《Java编程思想》中提到:我们将问题空间中的元素及其在解空间中的表⽰称为“对象”。我对这句话的理解是:⼀个对象就是⼀个问题和这个问题的解法的组合。还是很难理解。那么我们就还是通俗地理解为:万物皆为对象。⾯向对象程序中的⼀切⾏为都由对象来完成。如果想要解决某个问题,就创建⼀个可以解决这个问题的对象,因此每个对象都是带着它的职责诞⽣的。《Java编程思想》中提到:每个对象都有⼀个接⼝。Java初学者很容易狭隘地把接⼝仅仅理解为抽象类型interface,事实上,接⼝是更加⼴泛的概念。我对接⼝的理解是:接⼝实际上像是⼀种“刺激-反应”机制,它描述对象接收怎样的信息和对象收到信息后会返回怎样的信息,除此之外,对象的其它信息将会被隐藏——接⼝是建⽴在Java的封装机制之上才有意义的。

每个对象都⽀持这样的“刺激-反应”机制,在代码层⾯上,对象的接⼝就是这个对象可以调⽤的所有的⽅法,接⼝接收的信息就是⽅法名及⽅法的⼊参,接⼝返回的信息就是⽅法的返回值。再去理解“每个对象都有⼀个接⼝”这句话,⼤概就是,每个对象都有它可以调⽤的⽅法集。注意在上⾯的代码层⾯的描述中,⾃始⾄终都没有提到过⽅法体,Java抽象类型interface中的抽象⽅法也确实是没有⽅法体的。抽象过程的意图是站在⼀定的⾼度上去分析问题,因此对于程序设计具有重要的意义,在抽象与实现的关系上,原先是有了已实现的⽅法,再将这个⽅法抽象化(上升)。抽象类型interface将这个过程逆转了:先设计出接⼝,再将这个接⼝实现(落地)。这意味着由原先的对象选择接⼝,变成了接⼝选择对象,⽽⾯向对象的核⼼思想便是创建解决问题的对象,因此抽象类型interface体现了⾯向对象的思想。每个对象可以调⽤的⽅法都是由这个对象的类(class)描述的。类和对象的关系,就像是⼈类和张三的关系。⼈类是⼀套规范,张三是符合这套规范的个体,在⾯向对象的术语中,我们称张三是⼈类的⼀个实例。《Java编程思想》中提到:因为类描述了具有相同特性(数据元素)和⾏为(功能)的对象集合,所以⼀个类实际上就是⼀个数据类型。因此可以说,Java中的基本数据类型有8种,⽽数据类型有⽆数种。程序员通过定义类来适应问题,⽽不再被迫只能使⽤基本的数据类型(适应计算机中的存储单元)来解决问题。在整个编程过程中,程序员的⼯作就是定义类、创建对象、并引导对象解决实际问题。(实际上还要销毁对象,不过JVM帮我们做了这件事)六、⽤引⽤操纵对象《Java编程思想》中提到:尽管⼀切都看作对象,但操纵的标识符实际上是对象的⼀个引⽤。以下⾯的代码为例: Person zhangSan = new Person();其中的zhangSan就是引⽤,相当于对象的名称。在Java中,对象必须通过引⽤来操纵,就像⼀个⼈必须通过姓名或称号来指挥。⽽对于基本数据类型: int i = 2;int型变量2不是对象,i也不应该被称作对象的引⽤。这说明Java并不是纯粹的⾯向对象语⾔。对于⼀般类型的对象,其引⽤标识的内存区域内存储的是对象所在的地址。⽽对于8种基本数据类型的变量,其标识符标识的内存区域内存储的是真实的数值。之所以会这样,是因为每种基本数据类型的变量占⽤存储空间的⼤⼩是确定的,⽽对象的⼤⼩是难以确定的。七、封装《Java编程思想》中提到:把数据和⽅法包装进类中,以及具体实现的隐藏,共同被称作是封装。在Java中,最能体现封装思想的关键字是package。Java中的四种访问权限从⼤到⼩依次是:public、protected、包访问权限(没有关键字)和private。《Java编程思想》中提到:为了继承,⼀般的规则是将所有的数据成员都指定为private,将所有的⽅法指定为public。控制对成员的访问权限有两个原因:第⼀个原因是为了使⽤户不要碰触那些他们不该碰触的部分,⽤户实际上只应该使⽤为他们提供的接⼝。第⼆个原因是为了让类库设计者可以更改类的内部⼯作⽅式,⽽不必担⼼这样会对客户端程序员产⽣重⼤的影响——访问权限控制确保不会有任何客户端程序员依赖于某个类的底层实现的任何部分。⼋、代码复⽤Java中主要有两种代码复⽤⽅式:组合和继承。组合只需将对象引⽤置于新类中即可。假如我们要创建计算机类Computer,⽽此时类库中已经有了主机类Host、显⽰器类Monitor、⿏标类Mouse、键盘类Keyboard,我们就可以让Computer类包含⼀个Host类型的成员属性、⼀个Monitor类型的成员属性、⼀个Mouse类型的成员属性和⼀个Keyboard类型的成员属性(⼀个类就是⼀种数据类型),⽽不⽤在Computer类中再写有关主机、显⽰器、⿏标、键盘的代码,达到代码复⽤的⽬的。继承是使⽤关键字extends实现的。在⼀段继承关系中,导出类默认拥有它可以访问到的(public、protected修饰的、或者同包下包访问权限的)基类中的所有属性和⽅法。在⼀段继承关系的基类和导出类之间,应当存在is-a或者is-like-a的语义关系,意思就是不要继承没有⽤的代码(何况Java只⽀持单继承)。在写代码时,程序员通常是⾃由的,你完全可以⽤可乐类Cola继承书籍类Book,⽽Cola类的实例永远也⽤不到Book类的接⼝,我们对此情形的理解是:可乐不是(is-not-a)书,并且可乐不像(is-not-like-a)书。不过,如果我们认为Cola类和Book类中都应该有makeHappy()⽅法,我们可以这么理解:可乐像书⼀样(is-like-a)使我们快乐。这样的代码复⽤才是有价值的。is-a和is-like-a语义的区别在于,当导出类只覆盖了基类中的⽅法,⽽没有添加任何新⽅法的情况下,导出类和基类具有完全相同的接⼝,那么在任何场合下,导出类对象都可以完全替代⼀个基类对象,此时导出类和基类之间存在is-a语义。这是⼀种处理继承的理想⽅式,称为替代原则。那么如何在组合与继承之间进⾏选择?《Java编程思想》中提到:is-a(是⼀个)的关系是⽤继承来表达的,⽽has-a(有⼀个)的关系是⽤组合来表达的。除了组合和继承,还有第三种代码复⽤⽅式是代理。Java并没有提供对它的直接⽀持。代理是继承与组合之间的中庸之道,它将⼀个成员对象置于所要构造的类中(就像组合),但与此同时在新类中暴露该成员对象的所有⽅法(就像继承)。代理是常⽤的设计模式。九、多态我们举个例⼦说明什么是向上转型和向下转型:我们认为“男孩⼉是孩⼦,⼥孩⼉也是孩⼦,孩⼦都会做游戏”,写成代码就是: Child child = new Child(); me(); Child boy = new Boy(); me(); Child girl = new Girl(); me();在第3⾏代码中,new Boy()创建的是⼀个Boy类型的对象,⽽“=”操作符左边是⼀个操纵Child类型对象的引⽤,将⼀个Boy对象交给⼀个操纵Child对象的引⽤来操纵时,发⽣向上转型,Boy对象转型成Child对象。在第4⾏代码中,boy调⽤⽅法playGame()。boy操纵的是⼀个由Boy对象向上转型得到的Child对象,此时发⽣向下转型,这个对象重新转型成Boy对象,然后绑定Boy类中的playGame()⽅法⽽不是Child类中的playGame()⽅法。将⼀个⽅法调⽤同⼀个⽅法主体关联起来被称作绑定。若在程序执⾏前进⾏绑定(由编译器和连接程序实现),叫作前期绑定。多态技术的实现依赖于Java的后期绑定机制(也叫做动态绑定或运⾏时绑定),它的含义是在运⾏时根据对象的类型进⾏绑定。在Java中,动态绑定是默认⾏为,通常情况下,动态绑定会⾃动发⽣。除⾮你加了static或final关键字。《Java编程思想》中提到:多态是⼀项让程序员“将改变的事物与未变的事物分离开来”的重要技术。多态技术极⼤提升了程序的可扩展性。⼗、内部类为什么要在Java中增加内部类这项语⾔特性呢?

《Java编程思想》中提到:使⽤内部类最吸引⼈的原因是:每个内部类都能独⽴地继承⾃⼀个(接⼝的)实现,所以⽆论外围类是否已经继承了某个(接⼝的)实现,对于内部类都没有影响。内部类使得多重继承的解决⽅案变得完整。接⼝解决了部分问题,⽽内部类有效地实现了多重继承。也就是说,内部类允许继承多个⾮接⼝类型(类或抽象类)。⼗⼀、异常《Java编程思想》中提到:异常的基本的概念是⽤名称代表发⽣的问题,并且异常的名称应该可以望⽂知意。异常也是对象,所有的异常类都继承⾃Throwable类。JDK中定义了⼤量的异常类型,每当某种已定义的异常发⽣时,会创建⼀个异常对象并抛出(throw new xxxException(“xxx xxx xxx”))。Java异常分为被检查的异常(CheckedException)和不被检查的异常(UncheckedException)。在开发Java程序时,有时候编译器会强制你为某次调⽤操作做异常处理,这是因为被调⽤的⽅法中抛出了被检查的异常(如IOException)。《Java编程思想》中提到:被检查的异常强制你在可能还没准备好处理错误的时候被迫加上catch⼦句,会导致“吞⾷则有害”的问题:catch⽽不处理,异常将会丢失。异常确实发⽣了,但“吞⾷”后它却完全消失了。不被检查的异常是编译器不强制要求处理的异常,换⾔之,编译时不会检查。不被检查的异常⼀般包括各种运⾏时异常(RuntimeException),如空指针异常、数组越界异常等。⼗⼆、Java I/O系统《Java编程思想》中提到:流代表任何有能⼒产出数据的数据源对象或者是有能⼒接收数据的接收端对象。——流不是数据本⾝。所谓的输⼊流和输出流是相对于内存来说的,输⼊流从磁盘到内存,输出流从内存到磁盘。⼗三、泛型 《Java编程思想》中提到:泛型实现了参数化类型的概念,“泛型”这个术语的意思是:“适⽤于许多许多的类型”。《Java编程思想》中提到:Java泛型是使⽤擦除来实现的,这意味着当你在使⽤泛型时,任何具体的类型信息都被擦除了,你唯⼀知道的就是你在使⽤⼀个对象。《Java编程思想》中提到:擦除的核⼼动机是它使得泛化的客户端可以⽤⾮泛化的类库来使⽤,反之亦然,这经常被称为“迁移兼容性”。(直⽩地讲,兼容JDK 1.5之前没有泛型的版本的类库,也是因为这个原因,Java泛型的能⼒⼤打折扣)正如你想的那样,即使你从不使⽤泛型,仍然可以完成⼤部分的Java开发⼯作。那么为什么要往Java中引⼊泛型呢?《Java编程思想》中提到:我相信被称为泛型的通⽤语⾔特性(并⾮必须是其在Java中的特定实现)的⽬的在于可表达性,⼆不仅仅是为了创建类型安全的容器。类型安全的容器是能够创建更通⽤的代码这⼀能⼒所带来的副作⽤。《Java编程思想》中提到:泛型正如其名称所暗⽰的:它是⼀种⽅法,通过它可以编写出更“泛化”的代码,这些代码对于它们能够作⽤的类型具有更少的限制,因此单个的代码段可以应⽤到更多的类型上。⼗四、其他的⼀些内容有很多我没有写到的内容,包括注解、反射、枚举、并发等。我看过⼀些关于注解和反射的资料,总觉得对它们是理解的,但不能灵活运⽤到⾃⼰的代码中。在对它们有更加深⼊的了解之前,暂时写不出特别有价值的内容。枚举是⼀个⽐较简单的功能,没什么想说的。并发的内容⽐较多,⽽《Java编程思想》也没有讲特别深,(⽬前我看来)这部分内容需要知道的知识点⽐需要理解的知识点更多,我没有试着去整理这部分内容。《Java编程思想》是⼀本800多页的书,⼤概读到600多页的时候,切⾝感受到⾷之⽆味,弃之可惜。要啃这本书需谨慎。