<java编程思想>(thing in java) 阅读笔记(第十一章至第十二章)

第11章 持有对象
容器还有一些其他特性。例如,set对于每个值都只保存一个对象,Map是允许你将某些对象与其他一些对象关联起来的关联数组,java的容器类是可以自动调整自己的尺寸。
即使java中没有直接关键字的支持,容器类依旧是可以显著增前你的编程能力的基本工具。

11.1泛型和类型安全的容器

11.2基本概念
java容器类类库的用途是“保存对象”,并将其划分为两个不同的概念:
1)Collection。一个独立元素的序列,这些元素都服从一条或多条规则。List必须按照插入的顺序保存元素,而set不能有重复元素,Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)
2)Map。一组成对的“键值对”对象,允许你使用键来查找值。Arraylist允许你使用数字来查找值。因此在某种意义上将,它将数字和对象关联在了一起。映射表允许我们使用另一个对象来查找某个对象,它也被称为“关连数组”,因为它将某些对象与另外一些对象关联在一起;或者被成为“字典”,因为你可以使用键对象来查找值对象,就像在字典中使用单词来定义一样。

List<Apple> list =new ArrayList<Apple>();
你应该创建一个具体类的对象,将其转型为对应的接口,然后在其余的代码中都使用这个接口。

这种方式并非总能奏效,因为某些类具有额外的功能,例如,LinkedList具有在List接口中未包含的额外方法,而TreeMap也具有Map接口中未包含的方法。如果你需要使用这些方法,就不能将它们向上转型为更通用的接口。

Collection接口概念了序列的概念——一种存放一组对象的方式。

11.3添加一组元素
在java.util包中的Arrays和Collections类中有很多使用方法,可以在一个Collections添加一组元素。
Arrays.aslist()方法接受一个数组或是一个用逗号分隔的元素列表(使用可变参数),并将其转换为List对象。

11.4容器的打印
List,它以特定的顺序保存一组元素
Set ,元素不能重复 
Queue ,只允许在容器的一“端”插入对象,并从另外一“端”移除对象。Map在每个槽内保存了两个对象,即键和与之相关联的值。

Collection打印出来的内容,用方括号括住,每个元素由逗号分隔。Map用大括号括注,键与值由等号联系。

键和值在Map中的保存顺序并不是它们的插入顺序,因为HashMap实现使用的是一种非常快的算法来控制顺序。

本例试用了三种基本风格的Map:HashMap,TressMap和LinkedHashMap。

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

List允许在它被创建之后添加元素、移除元素,或者自我调整尺寸。这正是它的重要价值所在:一种可修改的序列。
你可以用contains()方法来确定某个对象是否在列表中。如果你想移除一个对象,则可以将这个对象的引用传递给remove()方法。同样,如果你有一个对象的引用,则可以使用indexof()来发现该对象的List中所处位置的索引编号。

11.6迭代器
如果原本是对着list编码,但是后来发现如果能够把相同的代码应用于set,将会显得非常方便,此时应该怎么做?或者打算从头开始编写通用的代码,它们只是使用容器,不知道或者说不关心容器的类型,那么如何才能不重写代码就可以应用于不同类型的容器?
迭代器是一个对象,它的工作是遍历并选择序列中的对象,而客户端程序员不必知道或关心该序列底层的结果。此外,迭代器通常被成为轻量级对象:创建它的代价小。
例如,java的iterator只能单向移动,这个Iterator只能用来:
1)使用方法Iterator()要求容器返回一个iterator,iterator将准备好返回序列的第一个元素。
2)使用next()获得序列中的下一个元素。
3)使用hasNext()检查序列中是否还有元素
4)使用remove()将迭代器新近返回的元素删除
Iterator的真正威力:能够将遍历序列的操作与序列底层的结构分离。正由于此,我们有时会说,迭代器统一了对容器的访问方法。

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

11.7LinkedList
LinkedList也像ArrayList一样实现了基本的List接口,但是它执行某些操作时比ArrayList更高效,但随机访问操作方面更逊色一些。

11.8 Stack
LinkedList具有能够直接实现栈的所有功能的方法,因此可以直接将LinkedList作为栈使用。不过,有时一个真正的栈,更能把事情讲清楚。

11.9 Set 
Set不保存重复的元素()。你可以很容易询问某个对象是否在某个set中。正因如此,查找就成为了set中最重要操作。因此你通常都会选择一个hashset的实现,它专门对快速查找进行了优化。
实际上set就是Collection,只是行为不同。set是基于对象的值来确定归属性的。

11.10Map
在main()中,自动包装机制将随机生成的int转换为hashMap可以使用的Interger引用(不能用基本类型的容器)。如果键不在容器中,get()方法将返回null。
Map和数组和其他的Collection一样,可以很容易地扩展到多维,而我们只需将其值设置为Map。因此,我们能够很容易地将容器组合起来从而快速地生成强大的数据结构。
Map可以返回它的键Set,它的值的Collection,或者它的键值对的Set。

11.11Queue
队列是一个典型的先进先出的容器。即从容器的一端放入事物,从另一端取出,并且事物放入容器的顺序与取出的顺序是相同的。队列常被当作一种可靠的将对象从程序某个区域传输到另一个区域的途径。队列在并发编程中特别重要。

LinkedList提供了方法以支持队列的行为,并且它实现了Queue接口,因此LinkedList可以用作Queue的一种实现,通过将LinkedList向上转型为Queue。

自动包装机制会自动地将nextInt()方法的int结果转换为queue所需的Integer对象,将char c转为queue所需的Character对象。Queue接口窄化了对LinkedList的方法的访问权限,以使得只有恰当的方法才可以使用。

11.11.1priorityQueue
先进先出描述了最典型的队列规则。队列规则是指在给定一组队列中的元素的情况下,确定下一个弹出队列的元素的规则。先进闲出声明的是下一个元素应该是等待时间最长的元素。
优先级队列声明下一个弹出元素是最需要的元素(具有最高的优先级)。如果构建了一个消息系统,某些消息比其他消息更重要,因而应该更快地得到处理。

11.12Collection 和Iterator 
Collection是描述所有序列容器的共性的接口,它可能会被认为是一个附属接口,即因为要表示其他若干个接口的共性而出现的接口。

使用接口描述的一个理由就是它可以使我们能够创建更通用的代码。通过针对接口而非具体实现来编写代码。我们的代码可以应用于更多的对象类型。如果编写的方法接受一个Collection,那么该方法就可以应用于任何实现了Collectionde的类。

生成Iterator是将队列与消费队列的方法接在一起耦合度最小的方法。并且与实现Collection相比,它在序列类上所是假的约束也少的多。

11.13      Foreach与迭代器

该接口包含一个能够产生Iterator的iterator()方法,并且iterable接口被foreach用来在序列中移动。因此,如果你创建了任何实现iterable的类,都可以将它用于foreach语句中。
iterator()方法返回的是实现了Iterator《String》的匿名内部类的实例,该匿名内部类可以遍历数组中的所有单词。

System.getenv()返回一个Map. entrySet()产生一个Map.Entry的元素构成的Set。并且这个Set是一个Iterable。

11.13.1适配器方法惯用法
如果现有一个Iterable类,你想要添加一种或多种在foreach语句中使用这个类的方法,应该怎么做呢?例如,假设你希望可以选择以向前的方向或是向后的方向迭代一个单词列表。如果直接继承这个类,并覆盖iterator()方法,你只能替换现有的方法,而不能实现选择。
一种解决方法是所谓适配器方法的惯用法。“适配器”部分来自于设计模式,因为你必须提供特定接口以满足foreach语句。

11.14总结
java提供了大量持有对象的方法
1)数组将数字与对象联系起来。它保存类型明确的对象,查询对象时,不需要对结果做类型转换。它可以是多维的,可以保存基本类型的数据。但是,数组一旦生成,其容量就不能改变
2)Collection保存单一的元素,而Map保存相关联的键值对。有了java的泛型,你就可以指定容器中存放的对象类型。各种Collection和Map都可以在你向其中添加更多的元素时,自动调整其尺寸。容器不能持有基本类型,但是自动包装机制会仔细执行基本类型到容器中所持有的包装器类型之间的双向转换。
3)像数组一样,List也简历数字索引与对象的关联,因此,数组与List都是排好序的容器,List能够自动扩充容量
4)如果要进行大量的随机访问,就使用ArrayList,如果要经常从表中间插入或删除元素,则应该使用LinkedList。

5)各种栈以及栈的行为,由LinkedList提供支持。


第12章 通过异常处理错误
java的基本理念是:结构不佳的代码不能运行
“异常”这个词有“我对此感到意外”的意思。问题出现了,你也许不清楚该如何处理,但你的确知道不应该置之不理;你要停下来,看看是不是有别人或在别的地方,能够处理这个问题,只是当前的环境中还没有足够的信息来解决这个问题,所以就把这个问题提交到一个更高级别的环境中,在这里将作出正确的决定

12.2基本异常
异常情形是指阻止当前方法或作用域继续执行的问题。把异常清醒与普通问题相区分很重要。所谓的普通普问题是指,在当前环境下能得到足够的信息,总能处理这个错误。而对于异常情形,就不能继续下去了,因为在当前环境下无法获得必要的信息来解决问题。你所能做的就是从当前环境跳出,并且把问题提交给上一级环境。这就是抛出异常时所发生的事情。

当抛出异常后,有几件事会随之发生。首先,同java中其他对象的创建一样,将使用new在堆上创建异常对象。然后当前的执行路径(它不能继续下去了)被终止,并且从当前环境中弹出对异常对象的引用。此时,异常处理机制管理程序,并开始寻找一个恰当的地方来继续执行程序,这个恰当的地方就是异常处理程序,它的任务是将程序从错误状态中恢复,以使程序能换一种方式运行,要么继续运行下去。

异常使得我们可以将每件事看都当作一个事务来考虑,而异常可以看护着这些事务的底线“。。。事务的基本保障是我们所需的在分布式计算中的异常处理。事务是计算机中的合同法,如果出了什么问题,我们只要放弃整个计算。”我们还可以将异常看作是一种内建的恢复系统,因为我们在程序中可以拥有不同的恢复点。如果程序的某部分失败了,异常将“恢复”到程序中某个已知的稳定地点上。

12.2.1 异常参数 
所有标准异常类都有两个构造器:一是默认构造器;另一个是接受字符串作为参数,以便能把相关信息放入异常对象的构造器。

抛出异常与方法正常返回值的相似之处:可以简单地把异常处理看成一种不同的返回机制。另外还能用抛出异常的方式从当前的作用域退出。在这两种情况下,将会返回一个异常对象,然后退出方法或作用域。
异常返回的“地点”与普通方法调用返回的“地点”完全不同。
能够抛出任意类型的Throwable对象,它是异常类型的根类。上一层环境通过这些信息来决定如何处理异常。

12.3 捕获异常

12.3.1  try块

12.3.2 异常处理程序 
异常处理程序必须紧跟在try块之后。
注意在try块内部,许多不同的方法调用可能会产生类型相同的异常。

终止与恢复
异常处理理论上有两种基本模型。java支持终止模型。在这种模型中,将假设错误非常关键,以至于程序无法返回到异常发生的地方继续执行。
另一种称为“恢复模型”。异常处理程序的工作就是修正错误,然后重新尝试调用出问题的方法,并认为第二次能成功。在遇见错误时,就不能抛出异常,而是调用方法来修正该错误,或者把try块放在while循环里,这样就不断地进入try块。

12.4创建自定义异常

12.4.1 异常与记录日志
静态的Logger.getLogger()方法创建了一个String参数相关联的Logger对象(通常与错误相关的包名与类名)。

12.5异常说明 
java提供了相应的语法,使你能以礼貌的方式告知客户端程学院某个方法可能会抛出的异常类型,然后客户端程序员就可以进行相应的处理。这就是异常说明。它属于方法生命的一部分,紧跟在形式参数列表之后。

12.6.1栈轨迹 257页 
printStackTrace()方法所提供的信息可以通过getStackTrace()方法来直接访问,这个方法将返回一个由栈轨迹中的元素所构成的数组,其中每一个元素都表示栈中的一帧。元素0是栈顶元素,并且是调用序列中的最后一个方法调用。数组中的最后一个元素和栈底是调用学历中的第一个方法调用。

12.6.2 重新抛出异常  258页 不理解 

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

12.7java标准异常

Throwable这个java类被用来表示任何可以作为异常被抛出的类。Throwable对象可分为两种类型:Error用来表示编译时和系统错误,Exception是可以被抛出的基本类型。
异常的基本概念是用名称代表发生的问题。
这些异常额可以通过它们的完整名称或者从它们的父类中看出端倪。

RuntimeException代表的是编程错误:
1、无法预料的错误。比如从你控制范围之外传递进来的null引用
2、作为程序,应该在代码中进行检查的错误。在一个地方发生异常,常常会在另一个地方导致错误

12.8使用finally进行清理
对于一些代码,可能会希望无论try块中的异常是否抛出,它们都能得到执行。这通常适用于内存回收之外的情况。因为回收由垃圾回收器完成。为了达到这个效果,可以在异常处理程序后面加上finally子句。

当java中的异常不允许我们回到异常抛出的地点时,那么该如何应对呢?如果不把try块放在循环里,就建立了一个“程序继续执行之前必须要达到”的条件。还可以加入static类型的计数器或者别的装置,使循环在放弃以前能尝试的一定的次数。

12.8.1finally用来做什么
当要把除内存之外的资源恢复到它们的初始状态时,就要用到finally子句。这种需要清理的资源包括:已经打开的文件或网络连接,在屏幕上画的图形,甚至可以是外部世界的某个开关。

甚至在异常没有被当前的异常处理程序捕获的情况下,异常处理机制也会在跳到更高一层的异常处理程序之前,执行finally子句。     

12.8.2在return中使用finally

12.8.3 缺憾:异常丢失 268页
缺憾的是,java的异常实现也有瑕疵。异常作为程序出错的标志,决不应该被忽略,但它还是有可能轻易地被忽略。

12.9异常的限制
当覆盖方法的时候,只能抛出基类方法的异常说明里列出的那些异常。这个限制很有用,因为这意味着,当基类使用的代码应用到其派生类对象的时候,一样能够工作,异常也不例外。

尽管在继承过程中,编译器会对异常说明做强制要求,但异常说明本身并不属于方法类型的一部分,方法类型是由方法的名字与参数的类型组成。因此,不能基于异常说明来重载方法。此外,一个出现在基类方法的异常说明中异常,不一定会出现在派生类方法的异常说明里。
在继承和覆盖的过程中,某个特定的方法的“异常说明的接口”不是变大了而是变小了。这恰好和类接口在继承时的情形相反。

12.10构造器 
除了内存的清理之外,所有的清理都不会自动发生。
对于在构造阶段可能会抛出异常,并且要求清理的类,最安全的使用方式是使用嵌套的try子句。
这种通用的清理惯用法在构造器不抛出任何异常时也应该运用,其基本规则是:在创建需要清理的对象之后,立即进入一个try-finally语句块。


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

12.12 其他可选方式
异常处理的一个重要原则是“只有在你知道如何处理的情况下才捕获异常”。实际上,异常处理的一个重要目标就是把错误处理的代码同错误发生的地点相分离。

12.13异常使用指南
1)在恰当级别处理问题
2)解决问题并且重新调用产生异常的方法
3)进行少许修补,然后绕过异常发生的地方继续执行
4)用别的数据进行计算,以代替方法预计会返回的值
5)把当前运行环境下能做的事情尽量昨晚,然后把相同的异常重跑到更高层。
6)把当前运行环境下能做的事情尽量昨晚,然后把不同的异常抛到更高层
7)终止程序
8)进行简化
9)让类库和程序更安全

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值