Java编程思想读书笔记

1.对象导论

面向对象语言基本特性
1.万物皆为对象。
2.程序是对象的集合,它们通过发送消息来告知彼此所要做的。
3.每个对象都有自己的由其他对象所构成的存储。
4.每个对象都拥有其类型。
5.某一特定类型的所有对象都可以接收同样的消息。

当正在试图开发或理解一个程序设计时,最好的方法之一就是将对象想像为“服务提供者”。程序本身将向用户提供服务,它将通过调用其他对象提供的服务来实现这一目的。你的目的就是去创建(或者最好是在现有的代码库中寻找)能够提供理想的服务来解决问题的一系列对象。

访问控制的第一个存在原因就是让客户端程序员无法触及他们不应该触及的部分-这部分对数据类型的内部操作来说是必须的,但并不是用户解决特定问题所需的接口的一部分。
访问控制的第二个存在原因就是允许库设计者可以改变类内部的工作方式而不用担心会影响到客户端程序员。

新的类可以由任意数量、任意类型的其他对象以任意可以实现新的类中想要的功能的方式所组成。因为是在使用现有的类合成新的类,所以这种概念被称为合(composition),如果组合是动态发生的,那么它通常被称为聚合(aggregation)。组合经常被称为“has-a”(拥有)关系。

新类的成员对象通常都被声明为private,使得使用新类的客户端程序员不能访问它们。

把将导出类看作是它的基类的过程称为向上转型(upcasting)
向下转型为更具体的类型,这种转型的方式称为向下转型

正是因为多态才使得事情总是能够被正确处理。

单根继承结构:所有的类最终都继承自单一的基类(Object)。

参数化类型,Java中称为泛型。一对尖括号,中间包含类型信息,通过这些特征就可以识别对泛型的使用。如:可以用下面这样的语句来创建一个存储Shape的ArrayList:

ArrayList<Shape> shapes = new ArrayList<Shape>();

对象的创建和生命周期:
其一,为了追求最大的执行速度,对象的存储空间和声明周期可以在编写程序时确定,这可以通过将对象置于堆栈(它们有时被称为自动变量automatic variable或限定变量scoped variable)或静态存储区内来实现。
其二,再堆(heap)的内存池中动态的创建对象。
在Java中采用了动态内存分配方式。使用new关键字来构建此对象的动态实例。

在计算机编程中有一个基本的概念,就是在同一时刻处理多个任务的思想。也可以理解为把问题切分成多个可独立运行的部分(任务),从而提高程序的响应能力。在程序中,这些彼此独立运行的部分称之为线程,此概念被称为“并发”。

2.一切都是对象

2.1 用引用操作对象
2.2 必须由你创建所有对象,new操作符实现这一目的。
对象内存怎么分配?有五个不同的地方可以存储数据:
1.寄存器:最快的存储区,它位于不同于其他存储区的地方–处理器内部。
2.堆栈:位于通用RAM(随机访问存储器)中,但通过堆栈指针可以从处理器那里获得直接支持。速度仅次于寄存器。
3.堆:一种通用的内存池(也位于RAM区),用于存放所有的Java对象。堆不同于堆栈的好处是编译器不需要知道存储的数据在堆里存活多长时间。
4.常量存储:常量值通常直接存放在程序代码内部,这样做事安全的,因为它们永远不会被改变。
5.非RAM存储:如果数据完全存活于程序之外,那么它可以不受程序的任何控制,在程序没有运行时也可以存在。如流对象和持久化对象。
2.6 static关键字:当声明一个事物是static时,就意味着这个域或方法不会与包含它的那个类的任何对象实例关联在一起。所以,即使从未创建某个类的任何对象,也可以调用其static方法或访问其static域。通常,你必须创建一个对象,并用它来访问数据或者方法。因为非static域和方法必须知道它们一起运作的特定对象。
2.8 注释和嵌入式文档:javadoc

3.操作符

3.7 关系操作符
关系操作符生成的是一个Boolean(布尔)结果。这里提一点 关系操作符 == 和 equals()
如果想比较两个对象的实际内容是否相同,必须使用所有对象都适用的特殊方法equals()。但这个方法不适用于”基本类型“,基本类型直接使用 ==和!=即可。
ps:equals()的默认行为是比较引用。但是大多数Java类库都实现了equals()方法,以便用来比较对象的内容,而非比较对象的引用。
3.12 条件操作符 if-else (也称为三元操作符)
3.16 Java没有sizeof()。因为所有数据类型在所有机器中的大小都是相同的。

4.控制执行流程 (if-else等等)

5.初始化与清理

5.1 用构造器确保初始化
5.4.1 在构造器中调用构造器-可以使用this关键字。
this只能调用一个构造器,并且必须将构造器调用置于最起始处。
5.4.2 static方法没有this的方法。在static方法的内部不能调用非静态方法,反过来可以。
5.7.1 初始化顺序
在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布于方法定义之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。
初始化的顺序是先静态对象(如果它们尚未因前面的对象创建过程而被初始化),而后是”非静态对象“。
5.7.2 静态数据的初始化。
无论创建多少个对象,静态数据都是占用一份存储区域。static关键字不能应用于局部变量,因此它只能作用于域。
对象创建过程:(假设有个名为Dog的类)
1.即使没有显示地使用static关键字,构造器实际上也是静态方法。因此,当首次创建类型为Dog的对象时(构造器可以看成静态方法),或者Dos类的静态方法/静态域首次被访问时,Java解释器必须查找类路径,以定位Dog.class文件。
2.然后载入Dog.class(这将创建一个Class对象),有关静态初始化的所有动作都会执行。因此,静态初始化只在Class对象首次加载的时候进行一次。
3.当用new Dog()创建对象的时候,首先将在堆上为Dog对象分配足够的存储空间。
4.这块存储空间会被清零,这就自动地将Dog对象中的所有基本类型数据都设置成了默认值(对数字来说就是0,对布尔型和字符型也相同),而引用则被设置成了null。
5.执行所有出现于字段定义出的初始化动作。
6.执行构造器。(涉及继承的时候,可能会牵涉到很多动作)
5.7.3 显式的静态初始化
Java允许将多个静态初始化动作组织成一个特殊的”静态子句“(有时也叫做”静态块“)。

public class Spoon
{
	static int i;
	static {
		i = 47;
	}
}

与其他静态初始化动作一样,这段代码仅执行一次,当首次生成这个类的一个对象时,或者首次访问属于那个类的静态数据成员时(即便从未生成过那个类的对象)

6.访问权限控制

6.1 包:库单元
6.1.2 创建独一无二的包名
Java解释器的运行过程如下:首先,找出环境变量CLASSPATH(可以通过操作系统来设置,有时也可通过安装程序-用来在你的机器上安装Java或基于Java的工具来设置)。CLASSPATH包含一个或多个目录,用作查找.class文件的根目录。从根目录开始,解释器获取的包的名称并将每个句点替换成反斜杠,以从CLASSPATH根中产生一个路径名称(于是,package.foo.bar.baz就变成为foo\bar\baz或foo/bar/baz或foo/bar/baz或其他,这一切取决于操作系统)。得到的路径
会与CLASSPATH中的各个不同的项相连接,解释器就在这些目录中查找与你所要创建的类名称相关的.class文件。(解释器还会去查找某些涉及Java解释器所在位置的标准目录。)
6.2 Java访问权限修饰词
如果不提供任何访问权限修饰词,则意味着它是“包访问权限”。
取得对某成员的访问权的唯一途径是:
1.使该成员成员public。于是,无论是谁,无论在哪里,都可以访问该成员。
2.通过不加访问权限修饰词并将其他类放置于同一个包内的方式给成员赋予包访问权。于是包内的其他类也就可以访问该成员了。
3.使用继承,继承而来的类既可以访问public成员也可以访问protected成员(但是访问private成员却不行)。只有在两个类都处于同一个包内时,它才可以访问包访问权限的成员。
4.提供访问器(accessor)和变异器(mutator)方法(也称作get/set方法),以读取和改变数值。对于OOP而言,这是最优雅的方式,而且这也是JavaBeans的基本原理。
6.2.3 private:你无法访问
6.2.4 protected:继承访问权限
当基类的创建者会希望有某个特定成员,把对它的访问权限赋予派生类而不是所有类。这就需要protected来完成这一工作。
6.3 接口和实现
访问权限的控制常被称为是具体实现的隐藏。把数据和方法包装进类中,以及具体实现的隐藏,常共同被称作是封装。其结果是一个同时带有特征和行为的数据类型。
6.4 类的访问权限
类既不可以是private的,也不可以是protected的。所以对于类的访问权限,仅有两个选择:包访问权限或public。

控制对成员的访问权限有两个原因
1.为了使用户不要触碰那些他们不应该碰触的部分,这些部分对于类内部的操作是必要的,但是它并不属于客户点程序员所需接口的一部分。因此,将方法和域指定为private,对客户端程序员而言是一种服务。因为这样他们可以很清楚地看到什么对他们重要,什么是他们可以忽略的。
2.为了让类库设计者可以更改类的内部工作方式,而不必担心这样会对客户端程序员产生重大的影响。

7.复用类

7.1 组合语法
类中域为基本类型时能够自动被初始化为零,但是对象引用会被初始化为null。
想初始化引用,可以在代码中的下列位置进行:
1.在定义对象的地方。这意味着它们总是能够在构造器被调用之前被初始化。
2.在类的构造器中。
3.就在正要使用这些对象之前,这种方式称为惰性初始化。在生成对象不值得及不必每次都生成对象的情况下,这种方式可以减少额外的负担。
4.使用实例初始化。
7.2 继承语法
继承是所有OOP语言和Java语言不可缺少的组成部分。当创建一个类时,总是在继承,因此,除非明确指出要从其他类中继承,否则就是在隐式地从Java的标准根类Object进行继承。
继承采用关键字extends实现。当继承时,会自动得到基类中所有的域和方法。
构建过程是从基类"向外"扩散的,所以基类在导出类构造器可以访问它之前,就已经完成了初始化。即使不为派生类创建构造器,编译器也会为派生类合成一个默认的构造器,该构造器将调用基类的构造器。
带参的构造器:使用关键字super显示地编写调用基类构造器的语句,并且配以适当的参数列表。
7.3 代理:继承和组合的中庸之道。
7.4 结合使用组合和继承
确保正确清理。
7.4 在组合与继承之间选择
组合和继承都允许在新的类中放置子对象,组合是显式地这样做,而继承则是隐式地做。
7.7 向上转型
“为新的类提供方法”并不是继承技术中最重要的方面,其最重要的方面是用来表现新类和基类之间的关系。这种关系可以用“新类是现有类的一种类型”这句话加以概括。
由于向上转型是从一个较专用类型向较通用类型转换,所以总是很安全的。也就是说,导出类是基类的一个超集。它可能比基类含有更多的方法,但它必须至少具备基类中所有的方法。因此,在向上转型的过程中,类接口中唯一可能发生的事情是丢失方法,而不是获取它们。

到底该用组合还是用继承,一个最清晰的判断办法就是问问自己是否需要从新类向基类进行向上转型。如果需要,则继承是必要的。如果不需要,则应该好好考虑是否需要使用继承。

7.8 final关键字
有时数据的恒定不变是很有用的。例如:
1.一个永不改变的编译时常量。
2.一个在运行时被初始化的值,而你不希望它被改变。
当对对象引用而不是基本类型运用final时,其含义会有一点令人迷惑。
对于基本类型,final使数值恒定不变,
而用于对象引用,final使引用恒定不变。
一旦引用被初始化指向一个对象,就无法再把它改为指向另一个对象。然而,对象其自身却是可以被修改的,Java并未提供使任何对象恒定不变的途径(但可以自己编写类以取得使对象恒定不变的效果)。

Java允许生成“空白final”,即指被声明为final但又未给定初值的域。
Java允许在参数列表中以声明的方式将参数指明为final。这意味着将无法再方法中更改参数引用所指向的对象。

final方法:使用final方法的原因有两个:
1.把方法锁定,以防任何继承类修改它的含义。想要确保在继承中使方法行为保持不变,并且不会被覆盖。
2.效率。

类中所有的private方法都隐式的指定为是final的。

当将某个类的整体定义为final时(通过将关键字final置于它的定义之前),就表明了不打算继承该类,也表示该类不希望有子类
7.9 初始化及类的加载
类的代码在初次使用时才加载,这通常是指加载发生于创建类的第一个对象之时,但是当访问static域或static方法时,也会发生加载。
初次使用之处也是static初始化发生之处。所有的static对象和static代码段都会在加载时依程序中顺序(即定义类时的书写顺序)而依次初始化。定义为static的东西只会被初始化一次。

继承和组合都能从现有类型生成新类型。组合一般是将现有类型作为新类型底层实现的一部分来加以复用,而继承复用的是接口。使用继承导出的类可以向上转型至基类。(多态)

8.多态(也称动态绑定、后期绑定或运行时绑定)

在OOP中,多态是继数据抽象和继承之后的第三种基本特征。
多态通过分离做什么和怎么做,从另一角度将接口和实现分离开来。
多态不但能够改善代码的组织结构和可读性,还能够创建可扩展的程序-即无论在项目最初创建时还是在需要添加新功能都可以“生长”的程序。
8.1 再论向上转型
对象既可以作为它自己本身的类型使用,也可以作为它的基类型使用。把对某个对象的引用视为对其基类型的引用的做法被称为向上转型
8.2 转机
将一个方法调用同一个方法主体关联起来被称作绑定
若在程序执行前执行绑定(如果有的话,由编译器和连接程序实现),叫做前期绑定。这也是面向过程的语言中不需要选择就默认的绑定方式。如C语言
在运行时根据对象的类型进行绑定叫后期绑定,也叫动态绑定或运行时绑定。
8.3 构造器和多态
构造器不具有多态性,因为它们是隐式声明的static方法。
基类的构造器总是在导出类的构造过程中被调用,而且按照继承层次逐渐向上链接,以使得每个基类的构造器都能得到调用。
基类构造器的特性:检查对象是否被正确地构造。导出类只能访问它自己的成员,不能访问基类中的成员(基类成员通常式private类型)。只有基类的构造器才具有恰当的知识和权限来对自己的元素进行初始化。
复杂对象调用构造器要遵循下面的顺序:
1.调用基类构造器。这个步骤会不断地反复递归下去,首先是构造这种层次结构的根,然后是下一层导出类,等等,直到最低层的导出类。
2.按声明顺序调用成员的初始化方法。
3.调用导出类构造器的主体。
8.3.2 继承和清理:通常子对象都会留给垃圾回收器进行处理。如果当确实要清理的时候,子类覆盖基类的清理方法,并且必须调用基类的清理方法。否则基类的清理动作就不会发生。super.基类清理方法()。

编写构造器时有一条有效的准则:“用尽可能简单的方法使对象进入正常状态,如果可以的话,避免调用其他方法”。在构造器内唯一能够安全调用的那些方法是基类中final方法(也适用于private方法,它们自动属于final方法)。

8.4 协变返回类型
Javase5中添加了协变返回类型,表示导出类中的被覆盖方法可以返回基类方法的返回类型的某种导出类型。

class Grain{
	public String toString(){return "Grain";}
}
class Wheat extends Grain{
	public String toString(){return "Wheat";}
}
class Mill{
	Grain process(){return new Grain();}
}
class WheatMill extends Mill{
	Wheat process() {return new Wheat();} //!!!!
}
public class CovariantReturn{
	public static void main(String[] args)
	{
		Mill m = new Mill();
		Grain g = n.process();
		System.out.println(g);
		m = new WheatMill();
		g = m.process();
		System.out.println(g);
	}
}
/*output*/
Grain
Wheat

Java SE5与Java较早版本之间的主要差异就是**较早的版本将强制process()的覆盖版本必须返回Grain,而不能返回Wheat,尽管Wheat是从Grain导出的,**因而也应该是一种合法的返回类型。协变返回类型允许返回更具体的Wheat类型
8.5 用继承进行设计
用继承表达行为间的差异,并用字段表达状态上的变化。

多态意味着“不同的形式”。在面向对象的程序设计中,我们持有从基类继承而来的相同接口,以及使用该接口的不同形式,不同版本的动态绑定方法。

9.接口

接口和内部类为我们提供一种将接口与实现分离的更加结构化的方法。
9.1 抽象类和抽象方法
仅有声明而没有方法体并用abstract修饰的方法称为抽象方法
包含抽象方法的类叫做抽象类。如果一个类包含一个或多个抽象方法,该类必须被限定为抽象的。(C++必须包含一个纯虚函数才能叫做抽象类)
9.2 接口
interface关键字产生一个完全抽象的类,它根本就没有提供任何具体实现。它允许创建者确定方法名、参数列表和返回类型,但是没有任何方法体。接口只提供了形式,而未提供任何具体实现。
interface不仅仅是一个极度抽象的类,因为它允许通过创建一个能够被向上转型为多种基类的类型,来实现某种类似多重继变种的特性。
使用implements关键字可以让一个类遵循某个特定接口或者是一组接口。
“interface只是它的外貌,但是现在implements要声明它是如何工作的”

使用接口的原因:
1.为了能够向上转型为多个基类型以及由此带来的灵活性。
2.防止客户点程序员创建该类的对象,并确保这仅仅是建立一个接口。

如果要创建不带任何方法定义和成员变量的基类,那么就应该选择接口而不是抽象类。
事实上,如果知道某事物应该成为一个基类,那么第一选择应该是使它成为一个接口。

9.5 通过继承来扩展接口
通过继承,可以很容易地在接口中添加新的方法声明,还可以通过继承在新接口中组合数个接口。 尽量避免组合接口时的名字冲突
9.6 适配接口
接口最吸引人的原因之一就是允许同一个接口有多个不同的具体实现。
接口的一种常见用法是策略设计模式
9.9 接口与工厂
接口是实现多重继承的途径,而生成遵循某个接口的对象的典型方式就是工厂方法设计模式
这与直接调用构造器不同,我们在工厂对象上调用的是创建方法,而该工厂对象将生成接口的某个实现的对象。

确定接口是理想选择,因而应该总是选择接口而不是具体的类—这其实是一种引诱。
恰当的原则应该是优先选择类而不是接口。从类开始,如果接口的必须行变得非常明确,那么就进行重构。接口是一种重要的工具,但是它们容易被滥用。

10.内部类 //回头再看

可以将一个类的定义放在另一个类的定义内部,这就是内部类
10.5 在方法和作用域内的内部类
可以在一个方法里面或者在任意的作用域内定义内部类。
这么做有如下两点理由:
1.实现了某类型的接口,于是可以创建并返回对其的引用。
2.要解决一个复杂的问题,想创建一个类来辅助你的解决方案,但是又不希望这个类是公共可用的。
10.1 如果你有一个好的案爆发是

11.持有对象

11.1 泛型和类型安全的容器
11.2 基本概念
Java容器类类库的用途是“保存对象”,并将其划分为两个不同的概念:
1.Collection。一个独立元素的序列,这些元素都服从一条或多条规则。List必须按照插入的顺序保存元素,而Set不能有重复的元素。Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)
2.Map。一组成对的“键值对”对象,允许你使用键来查找值。ArrayList允许你使用数字来查找值。映射表允许我们使用另一个对象来查找某个对象,它也被称为“关联数组”。

Collection接口概况了序列的概念–一种存放一组对象的方式。
11.5 List
List承诺可以将元素维护在特定的序列中。List接口在Collection的基础上添加了大量的方法,使得可以在List的中间插入和移除元素。
有两种类型的List:
1.基本的ArrayList,它长于随机访问元素,但是在List的中间插入和移除元素时较慢
2.LinkedList,它通过代价较低的在List中间进行的插入和删除操作,提供了优化的顺序访问。LinkedList在随机访问方面相对比较慢,但是它的特性集较ArrayList更大。

11.6 迭代器
迭代器(也是一种设计模式)的概念可以用于达成不重写代码就可以应用于不同的类型的容器目的目的。
迭代器是一个对象,它的工作是遍历并选择序列中的对象,而客户点程序员不必知道或关心该序列底层的结构。
迭代器通常被称为轻量级对象:创建它的代价小。
Java的Iterator只能单向移动,这个Iterator只能用来:
1.使用方法iterator()要求容器返回一个Iterator。Iterator将准备好返回序列的第一个元素。
2.使用next()获得序列中的下一个元素。
3.使用hasNext()检查序列中是否还有元素。
4.使用remove()将迭代器新近返回的元素删除。

11.6.1 Listlterator
Listlterator是一个更加强大的Iterator的子类型,它只能用于各种List类的访问。
虽然Iterator只能向前移动,但是ListIterator可以双向移动。
它还可以产生相对于迭代器在列表中指向当前位置的前一个和后一个元素的索引,并且可以使用set()方法替换它访问过的最后一个元素。

11.7 LinkerList
LinkedList也像ArrayList一样实现了基本的List接口,但是它执行某些操作(在List的中间插入和移除)时比ArrayList更高效,但在随机访问操作方面却要逊色一些。
LinkedList还添加了可以使其用作栈、队列或双端队列的方法。

11.8 Stack
"栈"通常指是“后进先出LIFO)”的容器。有时候也被称为叠加栈
LinkedList具有能够直接实现栈的所有功能的方法,因此可以直接将LinkedList作为栈使用。

11.9 Set
Set不保存重复的元素
Set中最常被使用的是测试归属性,很容易的询问某个对象是否在某个Set中。
查找成了Set中最重要的操作,因此通常都会选择一个HashSet的实现,它专门对快速查找进行了优化
Set具有与Collection完全一样的接口,因此没有任何额外的功能,不像前面有两个不同的List。
实际上Set就是Collection,只是行为不同。(这是继承与多态思想典型应用)
出于速度原因的考虑,HashSet使用了散列。HashSet所维护的顺序与TreeSet或LinkedHashSet都不同,因为它们的实现具有不同的元素存储方式。

11.10 Map
将对象映射到其他对象的能力是一种解决编程问题的杀手锏。
11.11 Queue
队列是一个典型的先进先出(FIFO)的容器
队列常被当做一种可靠的将对象从程序的某个区域传输到另一个区域的途径。
LinkedList提供了方法以支持队列的行为,并且它实现了Queue接口,因此LinkedList可以用作Queue的一种实现。通过将LinkedList向上转型为Queue。
11.11.1 PriorityQueue
先进先出描述了最典型的队列规则
队列规则是指在给定一组队列中的元素的情况下,确定下一个弹出队列的元素的规则。
先进先出声明的是下一个元素应该是等待时间最长的元素。
优先级队列声明下一个弹出元素是最需要的元素(具有最高的优先级)。
11.12 Collection和Iterator
Collection是描述所有序列容器的共性的根接口,表示其他若干个接口的共性而出现的接口。
使用接口描述的一个理由是它可以使我们能够创建更通用的代码。
11.13 Foreach与迭代器
foreach语法主要用于数组,但是它也可以应用于任何Collection对象。
Java SE5引入了新的被称为Iterable的接口,该接口包含一个能够产生Iterator的iterator()方法,并且Iterable接口被foreach用来在序列中移动。

Java提供了大量持有对象的方式:
1.数组将数字与对象联系起来。它保存类型明确的对象,查询对象时,不需要对结果做类型转换。它可以是多维的,可以保存基本类型的数据。但是,数组一旦生成,其容量就不能改变。
2.Collection保存单一的元素,而Map保存相关的键值对。有了Java的泛型,就可以指定容器中存放的对象类型,就不会将错误类型的对象放置到容器中,并且在从容器中获取元素时,不必进行类型转换。各种Collection和各种Map都可以在你向其中添加更多元素时,自动调整其尺寸。容器不能持有基本类型,但是自动包装机制会仔细地执行基本类型到容器中所有持有的包装器类型之间的双向转换。
3.像数组一样,List也建立数字索引与对象的关联,因此,数组和List都是排好序的容器。List能够自动扩充容量
4.如果要进行大量的随机访问,就使用ArrayList。如果要经常从表中间插入或删除元素,则应使用LinkedList
5.各种Queue以及栈的行为,由LinkedList提供支持。
6.Map是一种将对象(而非数字)与对象相关联的设计。HashMap设计用来快速访问;而TreeMap保持“键”始终处于排序状态,所以没有HashMap快。LinkedHashMap保持元素插入的顺序,但是也通过散列提供了快速访问能力。
7.Set不接受重复元素。HashSet提供最快的查询速度,而TreeSet保持元素处于排序状态。LinkedHashSet以插入顺序保存元素。
8.新程序中不应该使用过时的Vector、Hashtable和Stack。
在这里插入图片描述

12.通过异常处理错误

Java的基本理念是“结构不佳的代码不能运行”
12.2 基本异常
异常情形(exceptional condition)是指阻止当前方法或作用域继续执行的问题。
异常处理程序将程序从错误状态中恢复,以使程序要么换一种方式运行,要么继续运行下去。
12.2.1 异常参数
标准异常类都有两个构造器:一个是默认构造器,另一个是接收字符串作为参数,以便能把相关信息放入异常对象的构造器。

throw new NullPointerException("t = null");

12.3 捕获异常
理解监控区域(guarded region)的概念,才能明白异常是如何被捕获的。它是一段可能产生异常的代码,并且后面跟着处理这些异常的代码。
try块

try{
	//可能产生异常的地方
}

异常处理程序:抛出的异常必须在某处得到处理。
异常处理程序紧跟在try块之后,以关键字catch表示:

try{
	//code that might generate exceptions
}catch(Type1 id1)
{
	//Handle exceptions of Type1
}

异常处理程序必须紧跟在try块之后。当异常被抛出时,异常处理机制将负责搜寻参数与异常类型相匹配的第一个处理程序。只有匹配的catch子句才能得到执行
ps:这与switch语句不同,switch语句需要在每一个case后面跟一个break,以避免执行后续的case子句。
终止与恢复
异常处理理论上有两种基本模型。Java支持终止模型(Java和C++所支持的模型)。
终止模型:一旦异常被抛出,就表明错误已无法挽回,也不能回来继续执行。
恢复模型:异常处理程序的工作是修正错误,然后重新尝试调用出问题的方法,并认为第二次能够成功。

12.4 创建自定义异常
要自定义异常类,必须从已有的异常类继承,最好是选择意思相近的异常类继承(这样的异常并不容易找)。
建立新的异常类型最简单的方法就是让编译器为你产生默认构造器。

12.5 异常说明
Java鼓励人们把方法可能会抛出的异常告知使用此方法的客户端程序员。
异常说明使用了附加的关键字throws,后面接一个所有潜在异常类型的列表。

void f() throws TooBig, TooSmall, DivZero{}

12.6 捕获所有异常
可以只写一个异常处理程序来捕获所有类型的异常。通过捕获异常类型的基类Exception

catch(Exception e)
{
	System.out.println("Caught an exception");
}

ps:最好将捕获所有异常放在处理程序列表的末尾,防止它抢在其他处理程序之前先把异常捕获了

12.6.3 异常链:想要在捕获一个异常后抛出另一个异常,并且希望把原始异常的信息保存下来。

12.7 Java标准异常
Throwable这个Java类被用来表示任何可以作为异常被抛出的类。Throwable对象可以分为两种类型(指从Throwable继承而得到的类型):Error用来表示编译时和系统错误。
Exception是可以被抛出的基本类型。

12.8 使用finally进行清理
对于没有垃圾回收和析构函数自动调用机制的语言来说,finally非常重要。
但是Java有垃圾回收机制,所以内存释放不再是问题。
Java在什么情况下才能用到finally?
当要把除内存之外的资源恢复到它们的初始状态时,就要用到finally子句。
当涉及break和continue语句的时候,finally子句也会得到执行。
因为在finally子句总会执行,所以在一个方法中,可以从多个点返回,并且可以保证重要的清理工作仍旧会执行。

12.11 异常匹配
抛出异常的时候,异常处理系统会按照代码的书写顺序找出“最近”的处理程序。找到匹配的处理程序后,它就认为异常将得到处理,然后就不再继续查找。
查找的时候并不要求抛出的异常同处理程序所声明的异常完全匹配。派生类的对象也可以匹配其基类的处理程序

12.13 异常使用指南
应该在下列情况下使用异常:
1.在恰当的级别处理问题。(在知道该如何处理的情况下才捕获异常)
2.解决问题并且重新调用产生异常的方法。
3.进行少许修补,然后绕过异常发生的地方继续执行。
4.用别的数据进行计算,以代替方法预计会返回的值。
5.把当前运行环境下能做的事情尽量做完,然后把相同异常重抛到更高层。
6.把当前运行环境下能做的事情尽量做完,然后把不同的异常抛到更高层。
7.终止程序。
8.进行简化。(如果你的异常模式使问题变得太复杂,那用起来会非常痛苦也很烦人。)
9.让类库和程序更安全。(这既是在为调试做短期投资,也是在为程序的健壮性做长期投资。)

异常处理的优点之一就是它使得你可以在某处集中精力处理你要解决的问题,而在另一处处理你编写的这段代码中产生的错误。
“报告”功能是异常的精髓所在

13.字符串

字符串操作是计算机程序设计中最常见的行为。
13.1 不可变String
String对象是不可变的
String类中每一个看起来会修改String值的方法,实际上都是创建了一个全新的String对象,以包含修改后的字符串内容。而最初的String对象则丝毫未动。
13.2 重载“+”与StringBuilder
String对象是不可变的,可以给一个String对象加任意多的别名。因为String对象具有只读特性,所以指向它的任何引用都不可能改变它的值。

String test_str = "ac" + "bd";

13.3 无意识的递归
Java中每个类从根本上都是继承自Object,标准容器类也不例外。因此容器类都有toString()方法,并且覆写了该方法,使得它生成的String结果能够表达容器自身,以及容器所包含的对象。
想要打印出对象的内存地址,应该调用Object.toString()方法,这才是负责此任务的方法。所以不该使用this,而是应该调用super.toString()方法。

13.4 String上的操作
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

13.5 格式化输出
printf()

13.6 正则表达式

14. 类型信息

运行时类型信息使得你可以在程序运行时发现和使用类型信息。
Java是如何让我们在运行时识别对象和类的信息的(Runtime Type Information - RTTI)。
方式一:“传统的”RTTI,它假定我们在编译时已经知道了所有的类型;
方式二:“反射”机制,它允许在运行时发现和使用类的信息。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wang 恒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值