第十章 内部类
- https://www.cnblogs.com/devinkin/p/9880195.html
- 可以将一个类的定义放在另一个类的定义内部,这就是内部类
使用.this和.new
- 如果你需要生成对外部对象的引用,可以使用外部类的名字后紧跟原点和 this
- 有时候想要告知某些其他对象去创建某个内部类的对象,必须在new表达式中提供对其他外部类对象的引用, .new 语法
方法或者作用域内的内部类
- 在方法中的内部类,称为局部内部类
匿名内部类
- 匿名内部类末尾的分好,并不是用来标记此内部类的结束的,实际上,它标记的是表达式的结束.
- 如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译器会要求其参数引用是final的.(jdk8后没有该问题出现)
- 匿名内部类想要实现构造器的行为,可以使用代码块进行示例初始化,因为匿名内部类中不可能有命名构造器.
- 匿名内部类与正规的继承相比有些受限,因为匿名内部类即可以扩展类,也可以实现接口,但是不能两者兼备.而且如果实现接口,也只能是实现一个接口
嵌套类
- 如果不需要内部类对象与其外围类对象之间有联系,那么可以将内部类声明为static.
- 普通的内部类对象隐式地保存了一个引用,指向创建它的外围类对象.
- 当内部类为static(即嵌套类)
- 要创建嵌套类的对象,并不需要其外围类的对象
- 不能从嵌套类的对象中访问非静态的外围类
- 普通内部类的字段与方法,只能放在类的外部层次上,所以普通的内部类不能由static数据和static字段,也不能包含嵌套类,但是嵌套类可以包含所有这些东西。
接口内部的类
- 嵌套类可以作为接口的一部分,因为接口中的任何类都是自动是public和static的,所以其作用就是将嵌套类置于接口的命名空间内.
- 一般用在:想要创建某些公共代码,使得它们可以被某个接口的所有不同实现所共用.
从多层嵌套类中访问外部类的成员
- 一个内部类被嵌套多少层并不重要,它能透明的访问所有它所嵌入的外围类的所有成员.
为什么需要内部类
- 每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响.
- 如果拥有的是抽象的类或具体的类,而不是接口,那就只能使用内部类才能实现多重继承.
闭包与回调
- 闭包(closure)是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域.
- 通过闭包的定义可以知道内部类是面向对象的闭包.
内部类的继承
- 因为内部类的构造器必须连接到指向其外围类对象的引用,所以在继承内部类的时候,情况会变得复杂.
第十一章 持有对象
- https://www.cnblogs.com/mibloom/p/9040399.html
保存对象引用
- 数组,数组具有固定大小
- 容器类,可自动调节大小
容器类
- List Set Queue Map 及常用的实现类
Collection (接口)
List
- 按插入顺序保存,可重复。
ArrayList :先当于大小可变的数组,随机访问快,插入移除慢。
LinkedList :插入移除快,访问慢。
Set
- 无序不能有重复元素
-
- HashSet :最快获取元素,内部元素无规则排序。
LinkHashSet : 按插入顺序保存元素,也有HashSet的查询速度 .
- HashSet :最快获取元素,内部元素无规则排序。
-
- TreeSet : 升序保存对象
Queue
- 容器的一端插入一端删除
- PriorityQueue 优先队列
Map(接口)
- 保存键值对对象,键不能重复。键也是对象,值也是对象,按键查找。(也称映射表,关联数组,字典–>通过对象找对象)
- HashMap : 查找获取速度快,内部无规则排序
- LinkHashMap : 按插入顺序保存键,同时保存HashMap的查询速度
- TreeMap : 键升序保存对象。
预定义泛型
- 未预定义泛型时向容器内存入的是Object对象,所以get()取出来的也是Object对象,需要具体类型对象还需强制转型
- 预定义泛型保存特点对象,可以防止将错误的类型放入容器中。 例如ArrayList只保存Apple类型或者其子类型对象。
- 类型参数可以有多个但不可以是这样 ArrayList<Apple,Orange>。
- 向上转型对于泛型一样适用,可以把Apple的子类放入ArrayList中,用get(index)取出来还是原来的子类型。
添加一组元素
- Arrays类和Collections类中的一些方法可以向Collection中添加一组元素。
- Arrays.asList()接受一个数组或者用逗号分割的元素列表(列表元素类型可不同),并将其转换成一个List对象返回。使用显式类型说明Arrays.<类型>asList()可以确定List保存的类型。注意:此List对象底层是数组,因此不可改变其大小,不能添加删除。
- Collections.addAll(a,b) 接受一个Collection对象a,以及一个数组或者用逗号分割的列表b,将b中的元素添加到a中。
- Collection.addAll(a) 只接受另一个Collection对象a,此方法运行速度快。
- Collection的构造器可以接收一个Collection将自身初始化。
- 向Map中增加值 Map.put(key,value),取值Map.get(key)
容器打印
- 直接可打印,toString()方法已被覆写
打印数组
- 打印数组必须使用Arrays.toString(arrays) 打印数组(数组也是对象,直接打印对象引用输出只是内存地址)
List中的一些方法
- a.contains(reference)确定该引用指向的对象是否在列表中。
- a.romove(reference/index)移除该引用指向/该索引的对象
- a.indexOf(reference) 找出该引用指向对象的索引编号
- a.subList(2,5) 从大列表a创建一个小列表从索引2开始,包括2,到5结束,不包括5。返回一个List
- a.containsAll(b)确定b这个小列表是否在大列表中,与顺序无关。
- a.retainAll(b ) a,b取交集,并给a
- a.removeAll(b) 把a中所有在b中的元素移除。
- set(index , A) 用A对象替换索引处的对象。
- a.isEmpty() a是否为空
- a.clean() 清空a
- a.toArray() 将Collection转换为一个数组,无参数为Object数组,传入一个具体数组对象参数,则转换为该数组(类型不能错),如果传入的参数数组太小,将会自动创建合适大小的数组。
Iterator(接口) 迭代器
- 迭代器是个对象,它是用来遍历并选择序列中的对象
- Iterator只能单向移动
- 用来产生迭代器对象的方法: iterator(),该方法属于Iterator接口。一个容器调用该方法就可返回一个迭代器对象,并且该对象准备好返回容器的第一个元素。可以把该迭代器对象看成一个游标,创建完成后指向容器序列的第一个元素之前。
Iterator sl = al.iterator(); //sl就是迭代器,就相当于一个指针 - sl.next()获取下一个元素,执行完next()后当前sl就指向这个位置不变
- sl.remove()删除sl指向的元素,只有在next()执行之后指向一个存在元素才能使用,
- sl.hasNext() 检查序列中是否还有元素,有返回true,无返回false。
ListIterator
- 一个功能更强大的Iterator子类型,只能用于List访问,而且可以双向移动。
- previous() 获取前一个元素,hasPrevious()判断前一个元素存在不。
- List调用listIterator()产生一个指向第一个元素之前的迭代器,如果时listIerator(n),则开始就指向第n个元素。
- 相比于iterator()产生的迭代器多了add()和set()方法,add()将在游标之前插入,set()将修改游标所指内容。
foreach与迭代器
- foreach主要用于数组,也可用于所有Collection对象。这是因为Collection接口继承了Iterable接口。
- Iterable接口包含一个产生Iterator对象的iterator()方法,只要实现Iterable接口,覆写iterator()就可用于foreach。大量类已经实现了Iterable接口并覆写了iterator()方法,如全部的Collection,但Map全不是。
- 执行foreach时会自动调用iterator()方法,该方法产生Iterator对象,该对象调用next() hasNext() remove()方法。
- 实现Collection必须要实现iterator(),因为Collection继承Iterable.
适配器
- 解决一个接口需要另一个接口的问题。比如想既能正向又能反向foreach一个List,如果重写iterator()那就失去了正向功能,解决办法就是添加一个方法,该方法能够产生一个Iterable对象,覆写该对象的iterator()方法使其具有反向功能。直接foreach该List对象则正向输出,foreach该对象添加的方法则反向输出
LinkedList
- LinkedList 中的方法可以实现 栈 队列 或者双端队列的效果
Stack (类)栈
- 后进先出(LIFO)容器。
- jdk 提供的Stack类继承自Vector,Vector底层用数组实现的,push 、pop 性能大大降低,Stack最好使用链表实现
- 可以使用LinkedList实现。
Set
- Set与Collection有着完全一样的接口,它没有其他额外的功能,不像List。在分类上可以把它俩分为平级,实际上Set就是ollection,只是行为不同。Set要保持元素独立,是基于对象值来确定归属
- set1.contains/contiansAll(set2),用来确定set2是否归属与set1。
Map
- map.containsKey(key),测试map是否包含该键,map.containsValue(value),测试map是否包含值。
- Map可以返回它的键的Set,值的Collection,键值对的Set.
Queue (接口)
- 队列 先进先出(FIFO),并发编程中很重要。
- offer() 将一个元素插入队尾或者返回false.
- peek()和element()都在不移除的情况下返回队头。peek()在队列为空时返回null,element()则抛异常
- poll()和remove()移除情况下返回队头。poll()在队列为空时返回null,remove()则抛异常
PriorityQueue
- 优先级队列。先进先出描述了最典型的队列规则 ,而优先级队列则声明下一个弹出最需要(优先级最高)的元素。
- 调用offer()方法时该对象会在队列中被排序,可以使用自己提供的Comparator来修改这一顺序。
- 在调用peek(),poll(),remove()方法时获取的元素总是优先级最高的。
队列规则
- 先进先出是典型的队列规则,队列规则是指给定一组队列中的元素情况下,确定下一个弹出的元素的规则。优先级队列就是一种非典型的队列
补充
- 最基本最可靠的容器: ArrayList, add()插入一个元素,get(index)按索引访问元素,size()返回容器内元素的数量。
- 所有 Collection 都可以使用 foreach
- 向Collection中添加多个new A()对象,这些对象不是同一个对象,Set中可存,索引也不同
第十二章 通过异常处理错误
基本异常
- 异常情形是指组织当前方法或者作用域继续执行的问题.
- 普通问题是指在当前环境下能得到足够的信息,总能处理这个错误.
捕获异常
- 异常处理理论有两种基本模型:终止模型 恢复模型
- java支持终止模型:假设错误非常关键,以至于程序无法返回异常发生的地方的时候继续执行。一旦异常抛出就表示错误无法挽回,也不能回来继续执行。
- 恢复模型:异常处理程序的工作是修正错误,然后重新调用出问题的方法,并且认为第二次可以成功。
- java可以将try块放到while循环中,直到得到满意的结果
异常说明
- 异常说明使用了附加的关键字throws,后面接一个所有潜在异常类型的列表,方便客户端程序员查看
捕获所有异常
- 通过捕获异常类型的基类Exception就可以处理所有类型的异常.
- Exception从基类Throwable继承的方法有
栈轨迹
- printStackTrace()方法提供的信息可以通过getStackTrace()方法直接访问
- getStackTrace()方法返回一个由根轨迹中的元素所构成的数组,每一个元素都表示栈中的一帧.
重新抛出异常
- 重新抛出异常会把异常抛给上一级环境中的异常处理程序,同一个try块的后续catch字句将忽略.
- 异常对象的所有信息都得以保持,所以高一级环境中捕获此异常的处理程序可以从这个异常对象中得到所有消息.
- 如果只是把当前异常对象重新抛出,那么printStackTrace()方法显示的将是原来异常抛出点的调用栈信息,而非重新抛出点的信息.
- 想要更新这个信息,可以调用fillInStackTrace()方法,这将返回一个Throwable对象,它是通过把当前调用栈信息填入原来那个异常对象而建立的.
异常链
- 捕获一个异常后抛出另一个异常,并且希望把原始异常的信息保存下来,被称为异常链.
- Throwable子类在构造器中可以接受一个cause(因由)对象作为参数.这个cause就是用来表示原始异常,这样通过原是异常传递给新的异常,使得即使在当前位置创建并抛出了新的异常,也能通过这个异常链追中岛异常最初发生的位置.
- 在Throwable子类中,只有三种基本异常类提供了带cause参数的构造器
- Error(用于Java虚拟机报告系统错误)
- Exception
- RuntimeException
- 如果要其他类型的异常链接起来,应该使用initCause()方法而不是构造器.
Java标准异常
- Throwable这个Java类被用来表示任何可以作为异常被抛出的类.
- Throwable可分为两种类型(从Throwable继承而得到的类型):
- Error用来表示编译时和系统错误
- Exception是可以被抛出的基本类型
RuntimeException
- 属于运行时异常的类型有很多,它们会自动被Java虚拟机抛出,这些异常都是从RuntimeException中继承而来的.
- RuntimeException是 “不受检查异常” ,这种异常属于错误,将被自动捕获.
- 只能在代码中忽略RuntimeException(及其子类)类型的异常,其他类型的异常处理都是由编译器强制实施的.
- RuntimeException代表的是编程错误:
- 无法预料的错误.比如你从控制范围之外传递进来的null引用.
- 作为程序员,应该在代码中进行检查的错误.
使用finallly进行清理
- Java中使用finally一般把除内存之外的资源恢复到它们的初始状态时.
- 异常处理机制会在跳到更高一层的异常处理程序之前,执行finally子句
在return中使用finally
- 因为finally子句总是会执行,所以在一个方法中,可以从多个点返回,并且可以保证重要清理工作仍旧会执行.
- return语句返回之前会执行finally子句的代码块.
异常丢失的情况
- 前一个异常还没处理就抛出下一个异常,没有catch捕获异常,使用finally抛出下一个异常.
- 在finally中加入return语句,没有用catch语句捕获异常
异常的限制
- 不能基于异常说明来重载方法.
- 子类抛出的异常要 小于父类
构造器
- 构造器抛出异常要格外注意清理方法是否有必要调用
- 设计异常时,直接向上层抛出的却能简化编程.
- 对于在构造阶段可能会抛出异常,并且要求清理的类,最安全的使用方式是使用嵌套的try语句.
- 这种通用的清理惯用法在构造器不抛出任何异常时也应该使用,基本规则是:在创建需要清理的对象之后,立即进入一个try-finally语句块
异常匹配
- 抛出异常的时候,异常处理系统会安装代码书写顺序找出"最近的"处理程序
其他可选的方式
- "被检查异常"强制你在还没准备好处理错误的时候被迫加上catch子句,这就导致了吞食则有害的问题.异常被吞食了.
- 最简答而又不用写多少代码就能保护异常信息的方法,就是把它们从main()传递到控制台.
- 当我们不知道该怎么处理这个异常,但是也不想把它"吞"了,或者打印一些无用的信息,可以使用异常链的思路解决.
- 把"被检查的异常"包装进RuntimeException里面,避免异常被"吞"
异常使用指南
- 在恰当的级别处理问题.(在知道该如何处理的情况下才捕获异常)
- 解决问题并且重新调用产生异常的方法.
- 进行少许修补,然后绕过异常发生的地方继续执行.
- 用别的数据进行计算,以代替方法预计会返回的值.
- 把当前环境下能做的事情尽量做完,然后把相同的异常重抛到更高层.
- 把当前环境下能做的事情尽量做完,然后把不同的异常抛到更高层.
- 终止程序.
- 进行简化.
- 让类库和程序更加安全