第九章 接口
1.抽象类和抽象方法
抽象方法表示形式abstract void method()
,仅仅只有声明没有方法体。
包含抽象方法的类叫做抽象类,class
前面添加关键字abstract
,如public abstract class Apple
一个类如果需要继承抽象类,必须要实现全部的抽象方法。
创建抽象类和抽象方法可以使类的抽象性明确起来,并告诉编译器打算怎样使用他们,如果一个类包含一个或多个抽象方法,该类必须被限定为抽象的。抽象类是很有用的重构工具,可以将公共方法沿着继承层次结构向上移动。抽象方法可以被继承。
2.接口
接口中可以定义变量,它们是隐式static和final的且是public的。接口中的方法是隐式public的,因为实现类需要重写其方法。接口可以多继承。接口不能被实例化。
抽象类和接口的区别:接口被用于常用的功能,便于日后维护和添加删除,而抽象类更倾向于充当公共类的角色,不适用于日后重新对立面的代码修改。功能需要累积时用抽象类,不需要累积时用接口。
~~抽象类:抽象类不能实例化,即不能通过new生成对象,但注意可以追加{}生成匿名实现类,仍然不是它自己的实例化。
抽象类可以有构造函数,但不能直接调用,通常由实现类构造函数调用。
抽象类的方法前添加abstract关键字,定义抽象方法,派生类必须重写该方法,然后才能实例化。Java类中如有抽象 方法,则类符号前必须也要添加abstract关键字,定义为抽象类(可以没有抽象方法)。
抽象类中可以没有抽象方法,即可以全部是含方法体的非抽象方法。
~~接口: 接口可以包含域,且隐式地是static 和 final的,显然,接口中的域不能是空final,这些域不是接口的一部分,它们存储 在该 接口的静态存储区域内。
接口关键字interface前可以添加public修饰符,不加默认是包访问权限,接口的方法默认都是public的。
因为Java接口没有任何具体实现,即没有任何与接口相关的存储,因此可以定义一个Java类来implements多个接口, 达到C++中多重继承的效果。
Java可以定义一个接口去extends另外的一个或多个接口来实现接口的扩展。
因为Java接口中的域自动是final和static的,所以接口就成了一种便捷的创建常量组的工具。在Java SE5之前,用这 种方式来产生enum的效果。Java SE5之后,Java有了enum关键字,因此使用接口来群组常量就没意义了。
3.完全解耦
如果有一个方法操作的是一个类,那这个方法只能用于这个类或者该类的子类。如果你想把这个方法应用在不在此继承结构的某个类,那肯定是无法使用的。接口可以很大程度放宽这种限制,因此可以编写复用性更高的代码。
此处使用策略设计模式,策略就是传递进去的参数对象,它包含要执行的代码。
4.Java中的多重继承
在Java中只能继承单个类,但是可以实现多个接口,将所有的接口名都置于implements关键字之后,用逗号隔开,并且可以向上转型为每个接口,因为每一个接口都是一个独立类型。通过此扩展了接口。
5.接口中的域
接口中的字段不是接口的一部分,它们的值被存储在该接口的静态存储区域内。
6.接口与工厂
把接口和实现类分开的思想。在工厂对象上调用的是创建方法,而该工厂对象将生成接口的某个实现的对象。
第十章 内部类
可以将一个类的定义放在另一个类的定义内部,这就是内部类。
1.当某个外围类的对象创建了一个内部类对象时,内部类对象必定会捕获一个指向外围类对象的引用。
2.生成对外部类对象的引用: 外部类名字.this
创建某个内部类对象: 外部类对象.new 内部类()
3.匿名内部类
末尾的分号,不是用来标记内部类结束的,实际上是标记表达式的结束。
创建匿名内部类,希望它使用一个在其外部定义的对象,要求其参数引用是final的。p198
4.匿名类中不可能有构造器,但如果想做一些构造器的行为,就可以通过实例初始化,就能达到匿名内部类创建一个构造器的效果。
5.如果不需要内部类对象与外围类对象之间有联系,可以将内部类声明为static,称为嵌套类。可以放到接口中,因为是静态和公共的。
内部类嵌套多层依旧能透明的访问外部类成员。
6.使用内部类的好处
每个内部类都能独立地继承自一个接口的实现,所以无论外围类是否已经继承了某个接口的实现,对于内部类都没有影响。
闭包和回调:
闭包是一个可调用的对象,记录了一些信息,这些信息来自于创建他的作用域。
通过回调,对象能够携带一些信息,这些信息允许它在稍后的某个时刻调用初始的对象。
7.内部类的继承
内部类指向外围类的秘密的引用必须被初始化,而在导出类中不再存在可连接的默认对象。 需要生成一个构造器时,传入一个指向外围类对象的引用,并使用这个引用.super.()
8.内部类覆盖没有作用,这两个内部类被认为是独立的个体,除非明确继承某个内部类。
9.使用局部内部类的原因就是需要不止一个还内部类的对象,也就是需要一个已经命名的构造器,或需要重载构造器,而匿名内部类只能用于实例初始化。
内部类是一种非常有用的特性,因为它允许你把一些逻辑相关的类组织在一起,并控制位于内部的类的可视性。然而必须要了解,内部类与组合是完全不同的概念,这一点很重要。在最初,内部类看起来就像是一种代码隐藏机制:将类置于其他类的内部。但是,你将会了解到,内部类远不止如此,它了解外围类,并能与之通信;而且你用内部类写出的代码更加优雅而清晰,尽管并不总是这样。
第十一章 持有对象
简单的容器分类:Map,List,Set和Queue。实现类用黑框表示。
空心箭头:一个特性的类实现了一个接口。
实线箭头:某个类可以生成箭头所指向类的对象。
1.泛型和类型安全的容器
要想定义用来保存Apple对象的ArrayList,你可以声明ArrayList<Apple>,而不仅仅只是ArrayList,其中尖括号括起来的是类型参数(可以有多个),它指定了这个容器实例可以保存的类型。通过使用泛型,就可以在编译期防止将错误类型的对象放置到容器中。向上转型也可以想做用于其他类型一样作用于泛型。
如果不需要使用每个元素的索引,你可以使用foreach语法来选择List中的每个元素。
2.基本概念
Java 容器类库的用途是“保存对象”,将其划分成为两个不同的概念:
1)Collection。一个独立元素的序列,这些元素都符合一定的规则。比如List,是要按照一定的顺序插入;又比如Set,不能有重复的元素;再比如Queue,按照排队顺序来确定对象产生的顺序。
2) Map。“键值对”对象,是一种映射表,也叫做“关联数组”。
3.添加一组元素
在java.util包中的Arrays和Collections类中都有很多实用的方法,可以在一个Collection中添加一组元素。Arrays.asList()方法接受一个数组或是一个用逗号分隔的元素列表(使用可变参数),并将其转换为一个List对象。Collections.addAll()方法接受一个Collection对象,以及一个数组或是一个用逗号分割的列表,将元素添加到Collection中。
Collections.addAll()这种方式很方便,因此它是首选方式。可以直接使用Arrays.asList()的输出,将其当做List,但是在这种情况下,其底层表示的是数组,因此不能调整尺寸。如果你试图用add()或delete()方法在这种列表中添加或删除元素,就有可能会引发去改变数组尺寸的尝试,因此你将在运行时获得“Unsupported Operation (不支持的操作)”错误。
4.容器的打印
默认的打印行为,使用容器提供的toString() 方法,(之前数组使用 Arrays.toString() 方法),直接输出容器对象即可。
5.List
List接口在Collection的基础上添加了大量的方法,使得可以在List的中间插入和移除元素。
有两种类型的List:
1)基本的ArrayList,它长于随机访问元素,但是在List的中间插入和移除元素时较慢。
2)LinkedList,它通过代价较低的在List中间进行插入和删除操作,提供了优化的顺序访问。LinkedList在随机访问方面相对比较慢,但是它的特性集较ArrayList更大。
常用方法:
contains() 方法来确定对象是否在列表中
remove() 方法 移除
indexOf() 方法 返回索引
subList() 方法 从整个List中拿出一个片段(子List)
toArray() 方法 将List转换成一个数组
6.迭代器
迭代器是一个对象,而集合是一个接口。
迭代器可以遍历并选择序列中的对象,而客户端程序员不必知道或关心该序列底层的结构。此外,迭代器通常被称为轻量级对象:创建它的代价小。但Java的Iterator只能单向移动,这个Iterator只能用来:
1)使用方法iteratior()要求容器返回一个Iterator。Iterator将准备好返回序列的第一个元素。
2)使用next()获得序列中的下一个元素。
3)使用hasNext()检查序列中是否还有元素。
4)使用remove()将迭代器新近返回的元素删除。
Iterator还可以移除由next()产生的最后一个元素,这意味着在调用remove()之前必须先调用next()。
public static void diaply(Iterator<Pet> it){
while(it.hasNext()){
Pet p = it.next();
System.out.println(p.id()+":"+p+" ");
}
}
ListIterator是一个更加强大的Iterator的子类型,它只能用于各种List类的访问。尽管Iterator只能向前移动,但是ListIterator可以双向移动。你可以通过调用listIterator()方法产生一个指向List开始处的ListIterator,并且还可以通过调用listIterator(n)方法创建一个一开始就指向列表索引为n的元素处的ListIterator。
7.LinkedList
LinkedList还添加了可以使其用作栈、队列或双端队列的方法。
常用方法:
getFirst(),element(),peek() 三者都是获得第一个元素,当链表为空时,peek()方法返回 null,其余二者报错
remove(),removeFirst(),poll() 三者都是移除 表头的元素并返回,链表为空,poll() 方法返回 null,其余二者报错
addFirst(),add(),offer(),addLast() 四者都是添加元素到末尾
removeLast() 移除并返回列表的最后一个元素
8.Stack
stack 就是我们说的“后进先出”的容器。而由于链表的特性,其可以实现栈的所有功能,所以通常可以把一个链表当做 stack 使用。LinkedList具有能够直接实现栈的所有功能的方法,因此可以直接将LinkedList作为栈使用。
常用方法:
push() pop() empty() peek()
9.Set
Set不保存重复的元素,因此很容易询问某个对象是否在某个Set中。
Set 的继承接口有 HashSet 和 TreeSet 。前者是无序的且专门对快速查找进行了优化,而后者是可以对结果进行排序的。
10.Map
Map 数组将 Collection数组扩展到多维的。Map可以返回它的键的Set,它的值的Collection,或者它的键值对的Set。
自动包装机制将随机生成的int转换为HashMap可以使用的Integer引用(不能使用基本类型的容器)。
11.Queue
Stack 是后进先出的代表,而这里的 Queue 则是 “先进先出” 的容器。LinkedList 提供了非常多的方法,所以除了我们前面说的其可以实现 Stack,这里其实也是可以实现 Queue的数据结构的。Queue接口窄化了对LinkedList的方法的访问权限,可以通过将LinkedList向上转型为Queue。
offer()方法是与Queue相关的方法之一,将一个元素插入到队尾,或者返回false。
peek()和element()都将不移除的情况下返回队头,但是peek()方法在队列为空时返回null,而element()会抛出NoSuchElementException()异常。
poll()和remove()方法将移除并返回队头,但是poll()在队列为空时返回null,而remove()会抛出NoSuchElementException()异常。
12.Collection和Iterator
Collection是描述所有序列容器的接口。java.util.AbstractCollection类提供了Collection的默认实现,使得你可以创建AbstractCollection的子类型,而其中没有不必要的代码重复。
如果你实现Collection,就必须实现iterator()。
13.Foreach与迭代器
foreach语句可以用来访问 Collection对象,这是由于所有Collection类(但是不包括各种Map)都是Iterable类型。再结合迭代器,Iterable接口,该接口包含了一个能够产生Iterator的iterator()方法,并且Iterable接口被foreach用来在序列中移动。因此如果你创建了任何实现Iterable的类,都可以将它用于foreach语句中。