《Java 程序设计教程(第九版)》读书笔记

Table of Contents

本作品采用知识共享署名 – 非商业性使用 – 相同方式共享 4.0 国际许可协议进行许可。

Update:原文是大一上期末时期编写的,但其实没写完,跑去做原题去了。最终期末得分为 92。我的评价是考题和原文关系不大,不如做原题。

第1章 计算机系统概述

1.1 计算机处理过程

  • 计算机系统中的主要硬件组件:

    • CPU
    • I/O
    • 主存储器
    • 辅助存储设备
  • 主存用于保存正在被CPU执行的程序,辅存(e.g. 硬盘,USB闪存)则以永久保存的方式存储软件

1.1.1 软件分类

  • GUI 利用了图形化的屏幕元素,其中一些元素包括:
    • 窗口,用于将屏幕分隔成不同的工作区
    • 图标,代表各种资源(例如,文件)的小图标
    • 菜单、复选框、单选钮,为用户提供选项
    • 滚动条,允许用户在一个取值范围内选择
    • 按钮,通过按下鼠标指定用户的选择

1.2 硬件组件

1.2.1 计算机结构

  • 信息通过一组线路在组件间传输,这组线路称为总线(bus)
  • 除了CPU和主存以外,几乎所有其他的计算机设备和组件都被称为外围设备,简称外设。这些外设在系统的外围执行操作,尽管它们可能被安装在同一个机箱中
  • 控制器是协调各种外设工作的设备。由于每个设备都有自己数据格式化和数据通信的方式,所以控制器的作用之一就是处理这些特性,并且对其他硬件部分屏蔽这些特性差异。更进一步说,控制器更多的是处理实际的信息传输,从而使CPU能够专注于其他方面的处理

1.2.3 主存储器和辅助存储器

  • 对于很多计算机来说,每一个主存单元保存1字节(Byte)。保存的数据超过1B时,将由多个且连续的主存单元保存

  • 主存是不稳定的,仅当有电力供应时,数据和信息才得以保存

  • 辅助存储器通常是稳定的,在断电后依然可以保存下来

  • 高速缓存(cache)用于减少CPU访问指令和数据的平均时间,是一个访问速度很快的小存储区,其中保存了主存访问最频繁的信息。现代CPU同时具有一个加速指令获取速度的指令缓存,和一个加速数据访问的数据缓存

  • 磁盘是磁性介质,每个数据位在磁性介质中都以磁化粒子表示。读写磁头通过在旋转的磁盘片上移动,从而按需要读取或写入信息。一个磁盘通常包含有许多层磁盘片,全部固定在同一个轴上,每一层磁盘都有自己的读写磁头

  • 磁盘是直接存取设备,其读写磁头可以在磁盘上移动,直接移动到所需信息的位置

  • USB闪存也是直接存取设备,但不需要机械移动

  • “直接存取”和“随机存取”这两个术语有事可以互换使用

  • 在磁盘变得流行之前,磁带曾作为辅助存储设备使用

  • 只有当磁带中的信息经过磁头时,才可以读写数据,而不是由磁头直接到达指定的读写位置

  • 由于磁带必须经过快速倒带或者快进才能使得相应信息达到磁头位置,因此被认为是顺序存取设备

  • RAM和主存储器一般可以互换称谓,它们是存储动态程序和数据的设备

  • ROM是嵌入在计算机主板上的芯片或便携式存储器(e.g. 光盘)

  • BIOS就是用ROM存的

  • CD-ROM = Compact Disc,read-only memory

  • 新型CD技术,如CD-R,可以使用户进行一次性的数据烧录,一经烧录无法更改

  • CD是用模子刻出来的,而CD-R是用激光烧录的

  • DVD = Digital Video Disc / Digital Versatile Disc,比CD有着更高密度的压缩格式

  • Moore’s Law:每隔18个月,存储设备的容量增加一倍。しかし、当存储容量接近绝对物理极限时,这种增长速度将逐步降低

1.2.4 中央处理器

  • CPU由三个主要部分组成,控制单元协调CPU内部的处理过程;寄存器为CPU本身提供少量的存储空间;算术/逻辑单元负责运算和决策
  • 寄存器是系统中最小但最快的缓存
  • 控制单元控制并协调数据和指令在主存和CPU内部寄存器之间的传输,同时也要管理ALU的电路对Register的数据进行的运算
  • 对于大多数CPU,其内部的某些Register会专门用作特殊用途。例如,指令寄存器用于保存当前正在执行的指令,程序计数器则用于存放下一条将要执行的指令的地址。除了这些特殊寄存器以外,CPU还有一些通用寄存器用于存放临时数据
  • 将程序指令和数据共同放在主存中的思想,就是Von Neumann体系结构的基本原理
  • Von Neumann架构的计算机按照取指-译码-执行的循环不断工作
  • CPU是一个被称为微处理器的芯片,是计算机电路板的组成部分。主板上同时还具有ROM和设备控制器的通信接口,如显示器控制接口
  • 主板上另一个至关重要的组件是系统时钟,which 以一定的时间间隔产生电子脉冲信号,使CPU事件同步,脉冲产生的频率就是时钟频率,大小取决于不同的处理器,that 可大致描述CPU执行指令的速度

1.3 网络

1.3.1 网络连接

  • 共享一条通信线路将导致延迟,但是可以降低成本,并且使得在网络中加入新计算机更加容易
  • 一项可以缓解延迟问题的技术,是将大数据量的信息分割成若干小块,称为数据包。然后,这些独立的数据包与其他用户发送的数据一起分布传输

1.3.2 局域网和广域网

  • 广域网包含了两个或多个局域网,并且通常覆盖很大的区域

1.3.3 因特网

  • 协议(protocol)是管理两个事物间如何通信的一组规则
  • 实现因特网上信息传输的软件,必须遵守一组传输协议——TCP/IP,namely Transmission Control Protocol & Internet Protocol
  • TCP软件用于处理当大量信息同时到达一个一个目的地时可能产生的问题,如到达的数据包的原始顺序错乱或者信息丢失等;IP软件决定如何组织信息及将信息从源位置传送到目的地

1.3.4 万维网

  • 万维网(World Wide Web,or WWW, or Web)使得信息的交换更加容易,which 以超文本和超媒体的思想为基础

1.3.5 统一资源定位器

  • URL,namely Uniform Resource Locator
  • URL中没有给出具体文件名的,将提供默认的网页供浏览(e.g. index.html)

1.4 Java编程语言

  • Java语言同时提供附带的软件库供程序开发使用,被称为Java API或简称Java标准类库。

1.4.1 Java程序

  • println等方法并不定义在程序中,而是定义在System.out对象中,which 属于Java标准类库
  • 技术上讲,println等方法并不是Java语言的组成部分,但是可以在任何Java程序中使用

1.4.2 注释

  • 如果在/*后面紧跟着第二个星号,则可以使用Javadoc自动将该段注释生成为程序的外部注释文档

1.4.3 标识符和保留字

  • 自定义标识符可以是任何字母、数字、下划线、美元符号的组合,但不能以数字开头;长度任意
  • Case sensitive
  • Usually,类名首字母大写

1.4.4 空白符

  • 空白符由空格、制表符和换行符组成

1.5 程序开发

1.5.1 编程语言的级别

  • 按照本书分类,编程语言可分为以下四类:
    • 机器语言
    • 汇编语言
    • 高级语言
    • 第四代语言
  • 每种类型的CPU都有自己的机器语言
  • 汇编语言和机器语言都是低级语言
  • 一些编程语言被认为是比高级语言更加高级的语言,自带一些有特殊用途的实用工具,用于报表自动生成或数据库交互等,被称为第四代语言,或简称为4GL(因为在前三种语言之后产生)

1.5.2 编辑器、编译器和解释器

  • Compiler就是将一种编程语言代码翻译成另一种语言的等效代码的程序
  • 编译后的代码称为目标代码
  • 很多传统的编译器都是直接将源代码编译成特定的机器语言码,这样对于特定版本的程序,编译过程只需一次,所产生的可执行程序随时都可以运行
  • Interpreter无需单独编译,但是因为编译过程和执行过程交替进行,会使程序运行速度变慢
  • 编译和执行Java程序的过程通常是结合使用compiler和interpreter,将source code编译成类似机器语言代码的低级形式的Java字节码,然后由JVM的Java解释器执行这些字节码
  • Java字节码和机器语言代码的区别在于,字节码独立于处理器类型。可移植性强,条件是平台上有JVM
  • 字节码的运行效率介于直接运行机器码和解释器运行之间

1.5.5 错误

  • 本书声称的程序的三种错误:
    • 编译时错误
    • 运行时错误
    • 逻辑错误

第2章 数据与表达式

2.1 字符串

  • 字符串""并不是null,也不是"\0"

2.1.1 printprintln方法

  • System.out对象表示一个输出设备或文件。Particularly speaking,这里的对象名为out,保存在System类中

2.1.2 字符串的拼接

  • ”+“运算符的两个操作数中至少有一个是字符串,那么执行的就是字符串拼接操作

2.3 基本数据类型

  • 8种基本数据类型:4种整型(byte, short, int, long),2种浮点(float, double),字符及布尔

2.3.1 整型与浮点型

类型 存储空间(bit) 最小值 最大值
byte 8 -128 127
short 16 -32768 32767
int 32 -2147483648 2147483647
long 64 -9223372036854775808 9223372036854775807
float 32 约为-3.4E+38,7位有效数字 约为3.4E+38,7位有效数字
double 64 约为-1.7E+308,15位有效数字 约为1.7E+308,15位有效数字
  • Java默认所有的整型数值常量都是int,只有在数字后加上”L“或者”l“,才表示该常量是long,如45L
  • Java默认所有的浮点数值常量都是double,只有在数字后加上”F“或者”f“,才表示该常量是float,如45.0f
  • double类型的数字也可以在后面加”D“或者”d“
  • java并不支持无符号数

2.3.3 布尔型

  • 布尔型值不能够转换为其他任何类型的值,反之亦然

2.4 表达式

2.4.1 算术运算符

  • 模的返回值的符号和被除数符号一致,如-20 % 3 == -2

  • 如果有一个或两个操作数是浮点型,那么结果就是浮点型

  • 如果两个数都是整数,那么”/“就完成整除,如果有浮点数,那么就完成浮点除法

  • 优先级 运算符 结合性
    1 [](数组索引),.(对象成员引用),后缀++,后缀– 从左到右
    2 前缀++,前缀–,前缀+,前缀-,~(按位not),!(逻辑not) 从右到左
    3 new(实例化对象),(TYPENAME)(强制类型转换) 从右到左
    4 *,/,% 从左到右
    5 +,- 从左到右
    6 <<,>>,>>>(带0右移) 从左到右
    7 <,>,<=,>=,instanceof(类型比较) 从左到右
    8 ==,!= 从左到右
    9 &(按位and 以及 布尔and) 从左到右
    10 ^(按位xor 以及 布尔xor) 从左到右
    11 |(按位or 以及 布尔or) 从左到右
    12 &&(逻辑and) 从左到右
    13 ||(逻辑or) 从左到右
    14 ?:(三目条件运算符) 从右到左
    15 =(赋值),+=,-=,*=,/=,%=,<<=,>>=,>>>=,&=,^=,|= 从右到左
    16 ->(lambda表达式) 从右到左
  • a++的结果不能作为左值,而++a可以

  • 表达式a++的值是一个临时变量,所以不能给临时变量赋值(因为它们马上就消失了)

  • 表达式++a的值是 a,不是临时变量;因为没有创建临时变量,++a的效率比a++更高(在没有编译器优化的条件下)

第3章 类与对象

3.1 创建对象

  • println为例:这一方法是标准输入输出流System.out对象提供的服务。更确切地说,标识符out是一个对象变量,保存在System类中
  • System类是java标准类库中的预定义类,直接使用即可
  • 基本类型的变量保存数据的value,对象变量则不能保存对象本身而是其地址
  • 将一个实例设为null表示该变量不指向任何对象
  • 对于String对象,无需显式使用new以及构造方法就可以创建对象

3.1.1 别名

  • 让多个变量指向同一个对象,这些变量互为别名
  • 当一个对象所有的引用都丢失后,它无法再被使用,将成为“垃圾”而被回收

3.2 String类

  • 方法 介绍
    String(String str) 构造
    char charAt(int index) 下标读取
    int compareTo(String str) 正值,0,负值分别表示本对象的字符串按字典序先于、等于、后于str对象的字符串
    String concat(String str) 返回一个本对象字符串和str对象字符串拼接后的新字符串
    boolean equals(String str) 如果本对象字符串和str的相同,返回true,否则为false
    boolean equalsIgnoreCase(String str) 忽略大小写的比较
    int length() 返回字符个数
    String replace(char oldChar, char newChar) 将本对象字符串中的oldChar用newChar替换后,构成新字符串对象返回,必须同时为字符串或者字符
    String substring(int offset, int endIndex) 返回从offset开始到endIndex-1为止的子串
    String toLowerCase() 串中所有大写转小写
    String toUpperCase() 串中所有小写转大写
  • String的索引从0开始

3.3 包

  • 类库由一套支持程序开发的类组成
  • 类库有几组相关的类簇构成,通常称为Java API
  • Java标准类库的类被划分成包,每一个类属于一个具体的包。例如,String类和System类是Java.lang包中的类,而Scanner类是java.util包中的类
  • 这种包结构比API名称更基础、更基于语言。尽管在包名称和API名称之间存在基本的对应关系,但构成某一指定API的一组类可能分属于多个包

3.3.1 import声明

  • java.lang包中的类自动成为可用类,但其他包中的类则需要完整地声明相应类的引用,或者使用import声明
  • java.util.Scanner,即写出所属的包名和类名,或者先import再写Scanner,import后只需要写类名
  • 类名冲突时,写完整的类引用名称即可

3.4 Random类

  • 位于java.util包中
  • 方法 介绍
    Random() 构造
    float nextFloat() 返回一个分布在[0.0,1.0)的浮点随机数
    int nextInt() 返回随机整数,取值分布在整个int的区间
    int nextInt(int num) 返回一个随机整数,取值分布在0 ~(num – 1)

3.5 Math类

  • 位于java.lang中
  • 所有方法都是静态方法,无需实例化,直接用过类名调用

3.6 格式化输出

3.6.1 NumberFormat类

  • 位于java.text
  • NumberFormat对象不用new实例化,而是用类名调用静态方法
  • 提供通用的数据格式化能力,返回的是字符串

3.6.2 DecimalFormat类

  • 采用传统方式实例化,要new
  • 构造方法是接受一个String参数以确定格式化模式
  • 调用applyPattern可以应用不同的格式化模式
  • 调用format进行格式化,并返回字符串

3.6.3 printf方法

  • 仍然有使用价值的老软件,称为遗留系统(legacy system)
  • java中的printf是为了支持遗留系统的移植
  • printf方法并不是一种纯粹的OOP的解决方法

3.7 枚举类型

  • 枚举值没有个数限制
  • 枚举值服从标识符的命名规则
  • 在enum内部,每个枚举值的序数值按顺序分别为0,1,2,etc.
  • 枚举型是一种特殊的类
  • ordinal方法可以返回枚举值在enum中的序数值
  • name方法返回枚举值的名称,which和定义该枚举值的标识符相同

3.8 包装容器

  • 包装容器允许将primitive variable作为对象管理
  • 存在对void的包装类Void,但不能实例化,它仅代表一个null引用概念
  • 在int和Integer比较时,java会自动拆包,比较结果和实际结果一致
  • 从jdk 9以后便不建议使用new Integer()构造Integer了(在IDEA中甚至会直接报错);官方推荐使用Integer.valueOf()构造,或者也可以直接赋值,或者parseInt
  • parseInt返回的是int,而valueOf返回的是Integer,parseInt效率会更高一些
  • 各个包装类有MIN_VALUEMAX_VALUE等常用的静态常量

3.8.1 自动装箱

  • primitive type和object之间的赋值通常是不兼容的,autoboxing只能在两者对应的时候发生

3.9 JavaFX简介

  • java的GUI支持路径(按照书上声称的)大体是:AWT(Abstract Windowing Toolkit) -> Swing API -> JavaFX
  • Swing更适合桌面应用,对于Web browser上的Java程序,有其他技术
  • Oracle已经不再支持AWT和Swing
  • (按照书上声称的)JavaFX现在已经是用于开发图形和GUI的Java程序的首选工具
  • P82-P92部分JavaFX内容,因为暂时不学,故先不谈

第4章 编写类

4.2 类的分析

4.2.1 实例数据

  • java会自动初始化在类级声明中的变量,例如所有的数值类型会被自动初始化为 0

4.2.2 UML类图

  • UML = Unified Modelling Language,统一建模语言
  • (按照书上声称的)UML已经成为最流行的一种描述OOP的符号体系
  • UML有多种类型的图,每种都用来描述OOP系统的不同方面
  • UML类图中,每个类用一个矩形表示,其中由三部分构成:类名,属性(数据)和操作(方法)
  • 箭头用来表示两个类之间的关系,虚线箭头表示一个类使用另一个类的方法
  • 类之间不同类型的OOP机制的关系,用不同的连接线和箭头表示
  • 在UML中,变量的类型写在变量后,用冒号隔开;返回值类型也采用相同的方法描述

4.3 封装

  • As mentioned above,对象必须是自我管理的,namely 实例数据只能由对象自己修改
  • 应该使class之外的代码难于甚至无法访问和修改class内的变量,这种特性称为封装
  • java通过修饰符实现对象封装

4.3.1 可见性修饰符

  • public:可以从class外直接访问;private:只能从class内访问
  • protected:只与继承性有关
  • public将破坏class的封装性,因此实例数据应当是private的
  • 为客户提供服务的方法必须是public,以便客户可以引用,这种方法称为服务方法;private方法不能在class外引用,唯一用途是协助class的其他方法,因而有时被称为支持方法
  • 通常认为final variable可以是public,因为他们不可能被修改
  • 封装性对final variable基本上无意义
  • 在UML中,“+”表示public,“-”表示private

4.4 方法的分析

4.4.1 return语句

  • constructor方法没有返回值类型,也不是void

4.4.2 参数

  • java中,primitive type传参数时传值,object传地址
  • 加入final修饰符的object,仍然可以被修改,但primitive type不行
  • 包装类按照primitive type处理

4.5 构造方法回顾

  • 不必为每个class定义constructor,因为每一个class都有一个不带参数的default constructor

p107-119的GUI开发部分,同上暂且略去

第5章 条件判断与循环

5.1 布尔表达式

5.1.2 逻辑运算符

  • &&||的一个重要特点就是“短路”。当左操作数以足以确定整个运算的布尔结果,那么右操作数就不会再参与运算

5.2 if语句

5.2.3 嵌套if语句

  • else子句和它前面最近的没有匹配项的if语句相匹配

5.3 数据比较

5.3.1 浮点数比较

  • ==比较浮点数时,只有所有的二进制位都相等时才相等
  • 常取用eps = 1e-8

5.3.3 比较对象

  • ==比较对象时,实际上是判断两个引用变量是否互为别名

  • 字符串常量提供了一种方便性,实际上是一种创建字符串对象的快捷技术

  • java在需要时只会为多次使用的一个字符串常量创建一个对象。比如,如果字符串常量“Hi”在某个方法中多次被使用,java只会创建一个String对象来代表它。因此下面两个if语句的布尔表达式都为true:

    String str = "software";
    if(str == "software") { do sth. }
    if(str.equals("software")) { do sth. }
  • 在上述代码中,后面每次使用字符串常量“software”,都会引用其原始对象

  • compareTo方法来知道两个字符串的相对字典序

  • compareTo先逐字比较字符大小,最后才考虑长度

5.4 while语句

5.4.1 无限循环

  • 以下是一个无限循环的例子:

    double num = 1.0;
    while(num != 0.0){
        num = num - 0.1;
    }

    5.5 迭代器

  • iterator是一个对象

  • Technically,java中的迭代器对象是用Iterator接口来定义的

  • 每一个迭代器对象都有一个可以返回布尔值的hasNext方法,以及一个next方法,用来取得集合中下一个要处理的元素

  • java标准类库中有几个定义迭代器对象的class,其中一个是Scanner

5.5.1 读取文本文件

  • 从文件读取:fileScan = new Scanner(new File("filename"));
  • 从字符串读取:urlScan = new Scanner(str);
  • Scanner默认情况下用空白字符(空格,制表符,空行)作为分隔符,但也可以用sc.useDelimiter()方法设定分隔符,比如读取url时可以:urlScan.useDelimiter("/")
  • 如果要使用多个可选的分隔符,或者以更复杂的方式分析输入的数据,可利用Scanner类支持的模式(正则表达式)进行处理

5.6 ArrayList类

  • 属于java.util包
  • ArrayList使用数组来管理列表
  • 索引值从0开始
  • ArrayList保存的是对象类型的元素的引用,不能创建用来保存基本数据类型,但是可以用包装类
  • 有自带的toString方法输出所有元素

P148-157 讲GUI,暂略去

第6章 其他条件判断与循环

6.1 switch语句

  • switch语句中开始的表达式的结果必须是charbyteshortint,枚举类型,jdk7以后也可以是String。但是,不能为boolean或者float
  • case子句中的表达式必须是常量,不能是变量或者其他表达式
  • switch-case的布尔条件是基于相等性的,不能进行其他的关系判断(e.g. 小于)

6.2 条件运算符

  • 三目运算符?:在效率上不会比if-else更快。效率是否相等取决于编译器是否会进行优化
  • 在java中,表达式和完整意义的语句有别,不同于c++

6.4 for语句

6.4.1 for-each循环

  • for-each的写法示例:for(int i:arr) ...do something...,其中arr是一个可以遍历的对象,代码中假定每个元素均为int
  • 迭代器对象有hasNextnext方法,可以处理一个集合中的各元素项
  • 使用for-each的前提是对象实现了Iterable接口

P173-180,GUI,略

第7章 面向对象设计

7.1 软件开发活动

  • (按照书上声称的,)任何一个正确的软件开发项目,都应该包含下述4个基本的开发过程
    • 确定软件需求
    • 软件设计
    • 实现软件设计
    • 软件测试
  • 软件需求指定了程序必须完成的功能,指明了程序应当执行的任务,而不是描述如何执行所规定的任务
  • 通常,软件需求用一个称为功能说明的文档来描述

7.2 明确类和对象

  • OO的软件设计的基本内容就是确定类,which 决定着软件的结构

7.3 静态类成员

  • static方法是通过类名而不是对象来调用的
  • 不仅method可以具有静态属性,变量也可以
  • class designing 的一个关键环节,就是决定是否将一个method或变量声明为static

7.3.1 静态变量

  • 静态变量有时也被称为类变量

  • static 变量由所有的class实例共享

  • 对于一个class的所有object,只存在一个static变量试题。因此,在一个object中改变static变量的值将会直接影响其他所有对象

  • final声明的常量,也常使用static修饰

7.3.2 静态方法

  • 静态方法有时也被称为类方法
  • static method可以通过class名称来调用,因此调用时不必实例化
  • As we all know,Math类的所有method都是static
  • Math类的method根据传递的value完成基本的计算,这种情况下没有object可维护,因此没有必要通过建立object来请求method
  • main方法必须static,这样做是为了解释器执行main方法时,不必实例化含有main方法的类
  • 由于static method不是在一个具体object的上下文环境中操作,因此不能引用实例变量;实例变量仅存在于class的instance中
  • static method可以引用static变量
  • main方法,therefore,只能访问static变量和局部变量

7.4 类间关系

  • 软件系统中的class有各种各样的关系,其中最基本的三种关系是依赖、聚合和继承

7.4.3 聚合

  • UML中,聚合关系表示为两个class间的连接关系,连接符号中有空心菱形的一端是聚合类
  • String是非常基础的类,以至于在UML中经常将它作为primitive类型来描述

7.4.4 this引用

  • this是java保留字,允许对象引用自己
  • As described above,non-static method需要通过具体的对象或类来引用。this引用可以引用当前正在运行的object

7.5 接口

  • The term “Interface” 表示一组public的method,通过这组method可以和object交互
  • interface是一组常量和abstract method方法的集合
  • 所谓abstract method是没有实现的方法,即没有代码体,包含parameters列表的方法声明头后面仅有一个分号
  • 接口不能被实例化
  • abstract method声明前边可以使用保留字abstract,但在接口中的方法通常不必如此
  • 接口方法的默认可见性是public
  • class通过实现定义在interface中的每个abstract method来实现这个interface
  • 实现接口的class(namely 实现类)在类声明头部使用保留字implements,接着再给出接口名
  • 如果一个类声明它要实现某个接口,则必须提供这个接口中所有method的实现代码,任何一个method在class中没有实现代码,compiler就会报错
  • UML类图中,接口的表示仍类似于一个类节点,不同的是上方插入了“\<\<interface>>”字样以表明它是接口。实现类与接口之间用虚线箭头连接,空心箭头一端是端口
  • 可以由多个class实现同一个interface,由此可对相同的method进行不同的定义
  • 一个class可以实现多个interface,只要implements子句中用逗号隔开即可
  • interface中除了定义抽象方法之外,还可以用final修饰符定义常量
  • 当一个class实现了一个interface时,这个class就获得了对该interface定义的所有常量的访问权限
  • 接口形式化地定义了clients与实现类进行交互地方式,奠定了具有强大优势的多态性程序设计技术的基础

7.5.1 Comparable接口

  • java标准类库中包含了许多interface和class
  • Comparable接口定义在java.lang包中
  • Comparable接口中只有一个compareTo方法,参数是一个对象,返回一个整数
  • 目的是提供两个object进行比较的通用机制
  • 可以知道,String类有这个方法是因为它实现了Comparable接口,通过基于Unicode字符集的字典序的比较实现了compareTo方法

7.5.2 Iterator接口

  • 也定义在java标准类库中,由代表一个对象集合的类使用
  • 提供在对象集合中每操作一次移动到下一个对象的方法
  • 大多数迭代器(包括Scanner类的object)是利用Iterator接口定义的
  • 两个主要方法是hasNextnext,前者返回布尔值,后者返回对象,均没有参数
  • next方法以什么样的顺序返回下一个处理对象,取决于Iterator接口实现类的设计者
  • next并不会删除基本集合中的下一个对象,而是仅仅返回下一个对象的引用
  • Iterator接口有一个remove方法,无参,void,可以删除最近一次由next方法返回的对象

7.6 枚举类型

  • 前面曾讨论过,枚举类型是一种特殊的class,枚举类型的值是object
  • 事实上,枚举值是枚举类型的实例。例如,winter是Season类(Season是一个enum)的一个对象
  • 由于枚举类型是一种特殊的class,因此枚举类型的变量是一个对象引用变量,它的值只能是定义枚举的时候列出的枚举值
  • 所有的枚举值实际上是枚举类型对象的引用,并且作为具有公用静态属性的变量保存在枚举类中
  • 枚举值可以直接带构造,详见p204代码

7.7 方法设计

7.7.2 方法分解

  • java中,所有参数以传值的方式传递
  • 不过,传递对象的时候,实际上传递的是这个object的reference,实参形参互为别名
  • 另一方面,如果改变形参引用本身,比如使形参指向一个新对象,不会影响实参

7.8 方法重载

  • java中,只要参数个数、参数类型、参数顺序不同,就可以将一个方法名用于多个方法进行重载
  • 方法名,参数个数、类型、顺序称为方法的签名,compiler会根据签名将一个方法调用和适当的方法绑定在一起
  • 方法的返回类型不是签名的一部分,也就是说,两个重载方法的差别不能仅仅是返回值的类型不同。这是因为,方法的返回值可以被调用语句忽略,这时compiler就无法区分该引用哪一个
  • 构造也可以重载

p217-223,GUI,略

第8章 数组

  • 在ArrayList类中,正如类名所暗示的,它内部是通过数组实现的

8.1 数组元素

  • java中,具有N个值的数组,可访问的下标为[0, N – 1]

8.2 声明和使用数组

  • java中,数组是对象
  • 数组一经声明为确定的大小后,该数组的大小就不可再更改
  • 索引运算符“[]”有着最高的优先级

8.2.1 边界检查

  • java自动执行边界检查,保证只能引用数组有效范围内的索引值,试图越界访问会抛出异常ArrayIndexOutOfBoundsException
  • 数组对象有一个公有常量length,保存数组可保存的元素个数。建立数组时将设置这个值,之后不能再改变
  • Character类有isUpperCaseisLowerCase可以用

8.2.2 数组声明方式

  • 语法上说,两种定义数组的方式都可以。不过c风格的写法int a[]不被推荐

8.2.3 数组初始值表

  • 可以用初始值表实例化一个数组对象,此时不需要new

8.2.4 数组作为参数

  • 数组作为参数的方法可以实际改变数组元素,因为数组是对象

8.3 对象数组

  • 数组中的每一个对象都必须分别实例化
  • 可以直接在初始值表中new,因为new完会返回对象引用

8.4 命令行实参

  • String[] args参数代表命令行实参,在调用解释器时将提供给程序
  • 传给main方法的参数总是String对象数组,如果需要别的类型,必须由程序实现类型转换
  • 应当注意,在一些程序开发环境中,命令行并不用于给解释器提交程序,这种情况将以其他某种形式指定命令行信息,详细资料参考有关文档‘

8.5 可变长度参数表

  • java提供了一种方法,用于定义接受可变长度参数表的方法
  • 可以通过使用某种特殊语法的形参表,使所定义的方法能接受任意个数的参数,并自动存入数组以便在方法中进行处理
  • 例如:public double average(int ... list){},省略号表示该方法接收的参数个数是可变的,在方法内可以按正常的数组处理参数
  • 很显然,一个方法不能接收两组可变参数,并且可变参数必须在参数表的最后

8.6 二维数组

8.6.1 多维数组

  • java并不原生支持多维数组,多维数组表示为保存数组对象reference的数组

p249-256,GUI,略

第9章 继承

  • 继承是组织和创建类的基本技术。功能强大,决定着OO软件的设计方式,增强已设计类在软件开发中的复用性

9.1 创建子类

  • 通过继承,新类自动包含了原始类的变量和方法,然后programmer可以将新变量和method添加到派生的新类,或者修改所继承的变量和方法。
  • 继承的目的之一就是复用现有软件
  • 用于派生新类的原始类称为父类、超类或基类,被派生的称为子类或者亚类
  • java中,用保留字extends指明新类由现有类派生
  • 子类的实例化并不依赖父类的实例化
  • 继承具有单向性,换言之,父类不能引用子类内部声明的变量和方法
  • UML中,用空心箭头线表示继承关系,从子类指向父类

9.1.1 protected修饰符

  • 可见性修饰符用于控制对类成员的访问,这种可控制也延伸到继承的过程中

  • 父类的private方法或变量不能在子类中访问,或者通过子类对象访问

  • 当一个变量或方法用protected修饰时,子类就可以引用它,并且使父类保持了一定的封装性

  • protected的可见性介于private和public之间

  • Specifically,声明为protected的变量和方法,除了可被子类引用,还可以由同一个包内的任何类引用

  • 关于可见性修饰符

    修饰符 类和接口 方法和变量
    无(默认) 所有包中可见 同一包中任何类和子类可见
    public 任何地方可见 任何地方可见
    protected 仅应用于内部类;包内和子类可见 同一包内的任何类和子类可见
    private 仅应用于内部类;仅在外围类中可见 对其它类都不可见
  • UML中protected成员前加“#”

  • 子类继承所有的的方法和变量,即使是private的

  • 子类不能按名引用父类的private成员

  • 但是,构造方法作为一种特殊的方法不会被继承

9.1.2 super引用

  • 在一个class中可以用保留字super引用父类
  • super的用处之一就是调用父类的constructor
  • 也许直接构造设置比用super引用访问构造更简单,但让每一个class自己管理自己是更好的class design原则,而且不用手动更改继承类的构造(if 父类的构造改了的话)
  • 一般情况下,constructor的第一行应该用super引用调用父类的constructor。若不存在这样的调用,则会自动在constructor的开始处产生一行super()调用
  • 上述做法,可以确保在子类的constructor执行之前,父类的constructor会先初始化自己的变量
  • 用super引用调用父类constructor的操作只能在子类中执行,且必须是第一行
  • super引用也可以用于引用父类的其他变量和方法

9.1.3 多继承

  • java的继承方法为单继承,namely 子类只能有唯一的父类;一些OO的语言(说的就是你,c++!)允许子类有多个父类,称为多继承

9.2 重写方法

  • 当子类和父类有相同的方法名和签名时,子类方法将重写父类方法,子类方法优先
  • 调用方法的对象决定了哪一个方法将被实际执行
  • 可以用final修饰符定义一个方法,子类将不能重写final方法。这种技术用于保证子类必须使用一个特定的方法
  • 方法重写在体现多态性时显得尤为重要

9.2.1 影子变量

  • 子类可以定义与父类同名的变量,though not suggested
  • 概念上,与重写方法的处理类似

9.3 类层次结构

  • 从一个父类派生出的子类,还可以是它自己子类的父类,且多个子类可以从一个父类派生
  • 子类个数和类层次数都没有限制
  • 同一个父类的两个子类称为同胞,二者间显然没有继承关系
  • 继承机制具有传递性,namely 一个被继承的特性,可能来自直接的父类,也可能来自祖先类

9.3.1 Object类

  • java中,所有的类归根结底都由Object类派生
  • 如果一个class的定义没有用到extends子句显式地从另一个类中派生自己,则将作为默认情况自动从Object类派生
  • Since 所有的class都由Object类派生,因此Object类的所有public method都被每一个java类继承,这些方法可以由java程序中的任何对象调用
  • Object类的一些方法:equals,toString,clone(用于创建并返回对象的一个副本)
  • 调用println方法时传递了一个对象参数,这时println方法将自动调用toString方法

9.3.2 抽象类

  • 抽象类通常含有一个或多个尚未实现的抽象方法,不能被实例化
  • 抽象类和接口有点相似,但抽象类还可以有非抽象方法,并且除了常量之外还可以声明数据
  • 在抽象类中(不像接口那样),每一个抽象方法都必须使用abstract修饰符,但抽象类不必一定包含抽象方法
  • 即使一个抽象类中完全没有用abstract修饰的变量或方法,也不能实例化
  • UML图中,抽象类的类名用斜体表示
  • Abstract class在类层次上的定义位置不受限制,但一般应将abstract class设计在较高的类层次上
  • 从非抽象父类派生出抽象类也是可以的
  • 由抽象类派生出的子类必须重写所有父类的abstract method,否则该子类仍然是abstract class
  • 对一个abstract method使用final或static修饰符将产生矛盾,因为在子类中不能重写final method,因此将无法为一个abstract final method提供定义;一个 static method可以由类名调用,而不必通过实例化一个object来调用,因为abstract method没有实现代码,因此abstract static method没有任何意义

9.3.3 接口层次

  • 继承的概念可以应用到interface和class,即一个interface可以由另一个interface派生,从而形成接口层次结构
  • UML图中,同样用空心箭头线表示接口之间的继承关系
  • 当一个父接口用于派生子接口时,子接口就继承了父接口所有抽象方法和常量,任何实现子接口的类都必须实现所有的abstract method
  • 接口间的继承不存在可见性问题,因为一个接口的所有成员都是公有的
  • 类的继承和接口的继承不能重叠,namely,接口不能用于派生新类,类不能用于派生接口。仅当一个类设计为实现一个接口时,这个实现类和接口之间才有交互

9.3 可见性

  • 在子类中,即使不能直接引用父类成员,父类成员也总是存在,而且可以被间接的引用

9.5 继承关系的设计

9.5.1 继承的限制

  • final修饰符可用于限制继承的能力
  • final方法在任何派生类中都不能被重写,final类则不能再用于派生新类

p276-284,GUI,略

第10章 多态性

10.1 后绑定

  • 引用变量的类型和该引用变量指向的对象必须是兼容的,但不必完全相同
  • 术语“多态性”可以理解为“有许多形式”,一个多态性引用是可以在不同时间指向不同类型对象的引用变量
  • 在程序执行的某个时刻,可能会产生一个请求事件,要求执行某段代码来完成一个方法调用,这种请求事件称为一个方法调用与一个方法定义的绑定
  • 许多情况下,绑定发生在compiling阶段,但对于多态性引用,绑定要延迟到程序运行时才执行,并且要绑定的方法定义取决于当时引用变量所引用的对象,这一被延迟的请求事件称为后绑定或者动态绑定或者晚绑定
  • 显然,后绑定的效率低于compiling阶段的绑定效率
  • 在java中,可以用继承方式和接口方式两种建立多态性引用

10.2 利用继承实现多态性

  • 一个Object引用可以指向任何对象,因为所有类归根到底都是Object类的后代类
  • 比如,ArrayList具有多态性,就在于它保存的是Object引用,这就是为什么未规定元素类型的ArrayList能够用于保存任何一种类型的对象
  • 一个特定的ArrayList之所以能够同时保存几种不同类型的对象,正是由于继承关系使得这些不同类型的对象都是某种Object对象
  • 实际将调用的方法版本,取决于对象的类型而不是引用变量的类型
  • 类可以是abstract,不影响用abstract class定义多态性引用变量的能力

10.3 利用接口实现多态性

  • 一个接口引用变量可以指向实现该接口的任何类的任何对象
  • 当使用接口引用变量时,只能调用定义在接口中的方法,即使接口引用变量所指向的对象还有其他一些可用方法,也不能调用
  • BTW,注意接口是没有constructor的

10.4 排序

10.4.1 选择法排序

  • java中所有的包装类都实现了Comparable接口

p310-317,GUI,略

第11章 异常处理

11.1 异常

  • 一个Exception是一个定义非正常情况或错误的对象,由程序或runtime environment抛出,可根据需要catch和处理
  • 错误类似于异常,不同之处是错误代表不可恢复的问题且必须捕捉处理
  • java预定义了一组程序执行中可能发生的异常和错误
  • 错误也是一种对象
  • 一些常见的引起Exception抛出的问题:
    • 除0
    • 数组索引越界
    • 找不到指定文件
    • 不能正常完成被请求的I/O操作
    • 使用null引用
    • 操作违反了某种安全规则
  • 一个Exception代表一个错误,但也仅代表一种意外的情况
  • 异常处理提供一种处理上述情况的有效方式,尤其是用于处理不会经常发生的意外情况
  • 面对异常,你可以:
    • 根本不处理
    • 异常发生时处理
    • 在程序的某个位置集中处理

11.2 未捕获的异常

  • 如果程序不处理异常,则将非正常地终止执行,并产生描述在何处发生什么异常的信息
  • 第一行Exception输出信息表明抛出的是什么异常,并提供抛出该Exception的原因;其它行的信息是方法调用栈踪迹信息,给出发生异常的方法名、文件名、行号
  • getMessage方法可以返回一个字符串解释抛出异常的原因,printStackTrace可以输出调用栈踪迹

11.3 try-catch语句

  • catch子句位于某个try语句块的后面。一个try语句块可以有多个对应的catch子句
  • 每个catch子句,都被称为一个异常处理器
  • 如果使用finally子句,则无论try时是否发生异常,都将执行finally子句

11.3.1 finally子句

  • 一条try-catch语句可以有一个可选的finally子句,用于定义一段无论是否有异常发生都将执行的代码
  • 如果没有Exception,try完再finally;如果有Exception,先catch再finally
  • 如果有finally子句,则必须跟在所有的catch子句后面
  • try语句块可以没有catch子句

11.5 异常类层次结构

  • 定义各种异常的类由继承关系关联在一起
  • Throwable类是Error类和Exception类的父类
  • 虽然这些高层的类定义在java.lang包中,但定义各种异常的子类却分散定义在其他几个包中。继承关系可以跨越包边界
  • throw语句用于开始一个Exception的传递
  • 建立新异常类的方式常常是继承一个现有的异常类并保存着一个具体的信息串,which描述了Exception所代表的问题
  • 由于新异常类是Exception类和Throwable类的后代类,新异常类可以用throw语句抛出

11.5.1 检查型与非检查型异常

  • checked exception必须由方法捕捉,或者必须在可能抛出或传递异常方法的throws子句中列出来
  • 在方法定义的声明头中追加throws子句,就明确承诺了该方法在异常发生时将抛出或传递异常
  • unchecked exception不需要使用throws子句
  • Java中唯一的unchecked exception是RuntimeException类的对象或该类的后代类对象;所有其他的异常,都是checked exception
  • 一个方法需要throws子句是因为它所抛出的异常类由Exception类派生,从而使之成为一个checked exception

11.6 I/O异常

  • 一个程序可以同时处理多个输入流和输出流。一个数据存储(如文件)可以作为一个程序的输入流或输出流,但一般不能同时既是输入流又是输出流
  • 标准I/O流有三种:System.in(标准输入流)、System.out(标准输出流)、System.err(标准错误流(输出错误信息)),它们是System类中的三种对象引用变量,均为public且static,以便可以通过System类直接访问
  • Scanner对象对各种I/O异常进行内部处理,一旦需要时将创建InputMismatchException异常对象
  • (按照书上声称的)默认情况下,标准I/O流代表一些实际的设备。System.in流对象代表键盘,而System.out和System.err流对象代表显示器屏幕上的一个具体窗口。虽然两者可以代表不同的窗口,但默认时两个流对象会将信息输出到同一个窗口(通常是执行程序的窗口)。System.err流对象常用于发出错误信息
  • 除了标准输入流之外,Java标准类库的java.io包还提供了许多类,可用于定义具有不同特点的各种流,分别处理文件、内存或字符串
  • I/O类执行的许多操作都可能抛出IOException异常
  • IOException类是几个异常类的父类,代表试图执行I/O操作时发生的问题
  • IOException异常是checked exception
Share