第10章 内部类
1 可以将一个类的定义放在另一个类的定义内部,就是内部类
public class ArrayApp {
class Contents{
private int i = 11;
public int value(){ return i;}
}
class Destination{
private String label;
Destination(String whereTo){
label = whereTo;
}
String readLabel(){ return label;}
}
public void ship(String dest){
Contents c = new Contents();
Destination d = new Destination(dest);
System.out.println(d.readLabel());
}
public static void main(String[] args){
ArrayApp p = new ArrayApp();
p.ship("Tasmania");
}
}
2 内部类能访问其外部对象的所有成员,而不需要任何特殊条件。此外内部类还拥有其外部类的所有元素的访问权。
interface Selector{
boolean end();
Object current();
void next();
}
public class ArrayApp {
private Object[] items;
private int next = 0;
public ArrayApp(int size){ items = new Object[size];}
public void add(Object x){
if(next < items.length)
items[next++] = x;
}
private class ArraySelector implements Selector{
private int i = 0;
public boolean end(){ return i == items.length;}
public Object current(){ return items[i];}
public void next(){
if(i < items.length)
i++;
}
}
public Selector selector(){
return new ArraySelector();
}
public static void main(String[] args){
ArrayApp p = new ArrayApp(10);
for(int i=0;i<10;i++)
p.add(Integer.toString(i));
Selector selector = p.selector();
while(!selector.end()){
System.out.print(selector.current()+" ");
selector.next();
}
}
}
3 如果需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟圆点和this,如下面的ArrayApp.this
public class ArrayApp {
void f(){ System.out.println("ArrayApp.f()"); }
public class Inner{
public ArrayApp outer(){
return ArrayApp.this; // 返回外部类对象的引用
}
}
public Inner inner(){ return new Inner(); }
public static void main(String[] args){
ArrayApp p = new ArrayApp();
ArrayApp.Inner dt1 = p.inner();
dt1.outer().f();
}
}
4 创建某个内部类的对象,需要使用.new语法
public class ArrayApp {
public class Inner{}
public static void main(String[] args){
ArrayApp dn = new ArrayApp();
ArrayApp.Inner dt = dn.new Inner(); // 用.new创建内部类
}
}
在拥有外部类对象之前是不可能创建内部类对象。但如果是嵌套类(静态内部类),就不需要对外部类对象的引用。
5 内部类向上转型
interface Destination{
String readLabel();
}
interface Contents{
int value();
}
public class ArrayApp {
private class PContents implements Contents{
private int i = 11;
public int value(){return i;}
}
protected class PDestination implements Destination{
private String label;
private PDestination(String whereTo){
label = whereTo;
}
public String readLabel(){ return label; }
}
public Destination destination(String s){
return new PDestination(s);
}
public Contents contents(){
return new PContents();
}
public static void main(String[] args){
ArrayApp dn = new ArrayApp();
Contents c = dn.contents(); // 向上转型
Destination d = dn.destination("Tasmania"); // 向上转型
}
}
6 在函数和作用域内的内部类
interface Destination{
String readLabel();
}
public class ArrayApp {
public Destination destination(String s){
class PDestination implements Destination{ // 函数内的内部类
private String label;
private PDestination(String whereTo){
label = whereTo;
}
public String readLabel(){return label;}
}
return new PDestination(s);
}
}
// 作用域内的内部类,只在作用域内可用,作用域外不可用
if(true){
class TrackingSlip{
...
}
TrackingSlip s = new TrackingSlip();
}
7 匿名内部类
public class ArrayApp {
public Contents contents(){
return new Contents(){ // 匿名内部类
private int i = 11;
public int value(){ return i; }
};
}
public static void main(String[] args){
ArrayApp p = new ArrayApp();
Contents c = p.contents();
}
}
8 如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译器会要求其参数引用是final。
9 内部类声明为static,通常称为嵌套类。普通的内部类对象隐式地保存了一个引用,指向创建它的外部类对象。但嵌套类不是这样,这意味着
- 要创建嵌套类对象,并不需要其外部类对象
- 不能从嵌套类对象中访问非静态的外部类对象
interface Destination{
String readLabel();
}
interface Contents{
int value();
}
public class ArrayApp {
private static class ParcelContents implements Contents{
private int i = 11;
public int value(){ return i; }
}
protected static class ParcelDestination implements Destination{
private String label;
private ParcelDestination(String whereTo){
label = whereTo;
}
public String readLabel(){ return label; }
public static void f(){}
static int x = 10;
static class AnotherLevel{
public static void f(){}
static int x = 10;
}
}
public static Contents contents(){
return new ParcelContents();
}
public static Destination destination(String s){
return new ParcelDestination(s);
}
public static void main(String[] args){
Contents c = contents();
Destination d = destination("ok");
}
}
10 接口内的任何类都自动是public和static,因为类是static,只是将嵌套类置于接口的命名空间内,并不违反接口的规则。甚至可以在内部类中实现外部接口。
public interface ArrayApp {
void howdy();
class Test implements ArrayApp{
public void howdy(){
System.out.println("Howdy!");
}
public static void main(String[] args){
new Test().howdy();
}
}
}
要执行内部类的main()函数,可以用java ArrayApp$Test
11 从多层嵌套类中访问外部类的成员
class MNA{
private void f(){}
class A{
private void g() {}
public class B {
void h(){
g();
f();
}
}
}
}
public class ArrayApp {
public static void main(String[] args){
MNA mna = new MNA();
MNA.A mnaa = mna.new A();
MNA.A.B mnaab = mnaa.new B();
mnaab.h();
}
}
12 每个内部类都能独立地继承自一个接口实现,所以无论外部类是否已经继承了某个接口的实现,对于内部类都没有影响。
13 内部类的继承
class WithInner{
class Inner{}
}
public class ArrayApp extends WithInner.Inner {
ArrayApp(WithInner wi){
wi.super(); //必须要调用
}
public static void main(String[] args){
WithInner wi = new WithInner();
ArrayApp aa = new ArrayApp(wi);
}
}
14 内部类会被覆盖吗?
class Egg{
private Yolk y;
protected class Yolk{
public Yolk(){
System.out.println("Egg.Yolk()");
}
}
public Egg(){
System.out.println("New Egg()");
y = new Yolk();
}
}
public class ArrayApp extends Egg {
public class Yolk{
public Yolk(){
System.out.println("BigEgg.Yolk()");
}
}
public static void main(String[] args){
new ArrayApp();
}
}
输出
New Egg()
Egg.Yolk()
15 内部类标识符 $ ,如果内部类是嵌套在别的内部类之中,只需直接将它们的名字加在其外部类标识符与$后面,如Outer$Inner
第11章 持有对象
1 Java实用类库有一套完整的容器类,其中基本类型是List,Set,Queue和Map。这些对象类型也称为集合类。
2 Set对于每个值都只保存一个对象,Map是允许将某些对象与其他一些对象关联起来的关联数组。Java容器类都可以自动调整自己的尺寸。
3 使用ArrayList很简单:创建一个实例,用add( )插入对象,然后用get( )访问这些对象。可以像数组一样用索引,但不需要方括号。ArrayList还有一个size( )。
class Apple{
private static long counter;
private final long id = counter++;
public long id(){ return id; }
}
class Orange{}
public class ArrayApp {
@SuppressWarnings("unchecked")
public static void main(String[] args){
ArrayList apples = new ArrayList();
for(int i=0;i<3;i++)
apples.add(new Apple());
apples.add(new Orange());
for(int i=0;i<apples.size();i++)
((Apple)apples.get(i)).id();
}
}
ArrayList apples = new ArrayList( ) ;
apples.get( ) // 因为ArrayList是泛型,没有指明类型,使用get( )返回的Object类型
ArrayList<Orange> oranges = new ArrayList<Orange>( ) ;
oranges.get( ) // 因为ArrayList指明了类型,使用get( )返回的Orange类型
4 Java容器类类库的用途是保存对象,并将其划分为两个不同的概念
- Collection:一个独立元素的序列。List必须按照插入的顺序保存元素,而Set不能有重复元素。Queue按照排队规则来确定对象产生的顺序
- Map:一组成对的“键值对”对象,允许使用键来查找值。ArrayList使用数字来查找。
Collection<Integer> c = new ArrayList<Integer>();
for(int i=0;i<10;i++)
c.add(i);
for(Integer i:c)
System.out.print(i+" ");
所有Collection都可以用foreach语法遍历。
5 Arrays.asList( )函数接受一个数组或一个用逗号分隔的元素列表(使用可变参数),并将其转换为一个List对象。Collections.addAll( )函数接受一个Collection对象,以及一个数组或是一个用逗号分隔的列表,将元素添加到Collection中。
public class ArrayApp {
@SuppressWarnings("unchecked")
public static void main(String[] args){
Collection<Integer> collection = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5));
Integer[] moreInts = {6,7,8,9,10};
collection.addAll(Arrays.asList(moreInts));
Collections.addAll(collection, 11,12,13,14,15);
Collections.addAll(collection, moreInts);
List<Integer> list = Arrays.asList(16,17,18,19,20);
list.set(1, 99); // 将索引为1的元素设置为99
for(Integer i:collection)
System.out.print(i+" ");
System.out.println();
for(Integer i:list)
System.out.print(i+" ");
}
}
输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 6 7 8 9 10
16 99 18 19 20
6 Arrays.asList( )函数的限制是它对所产生的List的类型做出了最理想的假设,而并没有注意你对它会赋予说明样的类型。
class Snow{}
class Power extends Snow{}
class Light extends Power{}
class Heavy extends Power{}
class Crusty extends Snow{}
class Slush extends Snow{}
class Orange{}
public class ArrayApp {
@SuppressWarnings("unchecked")
public static void main(String[] args){
List<Snow> snow1 = Arrays.asList(
new Crusty(),new Slush(),new Power());
List<Snow> snow2 = Arrays.asList( // error
new Light(),new Heavy());
List<Snow> snow3 = new ArrayList<Snow>();
Collections.addAll(snow3, new Light(),new Heavy());
List<Snow> snow4 = Arrays.<Snow>asList( // 显式类型参数说明
new Light(),new Heavy());
}
}
7 容器的打印,Collection打印出来的内容用方括号括住,每个元素用逗号分隔。Map用大括号括住,键与值由等号联系。
public class ArrayApp {
static Collection fill(Collection<String> collection){
collection.add("rat");
collection.add("cat");
collection.add("dog");
collection.add("dog");
return collection;
}
static Map fill(Map<String,String> map){
map.put("rat","Fuzzy");
map.put("cat","Rags");
map.put("dog","Bosco");
map.put("dog","Spot");
return map;
}
@SuppressWarnings("unchecked")
public static void main(String[] args){
System.out.println(fill(new ArrayList<String>()));
System.out.println(fill(new LinkedList<String>()));
System.out.println(fill(new HashSet<String>()));
System.out.println(fill(new TreeSet<String>()));
System.out.println(fill(new LinkedHashSet<String>()));
System.out.println(fill(new HashMap<String,String>()));
System.out.println(fill(new TreeMap<String,String>()));
System.out.println(fill(new LinkedHashMap<String,String>()));
}
}
输出
[rat, cat, dog, dog]
[rat, cat, dog, dog]
[cat, dog, rat]
[cat, dog, rat]
[rat, cat, dog]
{cat=Rags, dog=Spot, rat=Fuzzy}
{cat=Rags, dog=Spot, rat=Fuzzy}
{rat=Fuzzy, cat=Rags, dog=Spot}
8 HashSet是HashSet,LinkedHashSet,TreeSet三个中获取元素最快的方式。如果存储顺序很重要,则TreeSet是三者中较好的,会按照比较结果的升序保存对象。
9 HashMap,TreeMap和LinkedHashMap的比较,HashMap提供了最快的查找技术,但没按任何顺序保存元素,TreeMap按比较结果的升序保存键,LinkedHashMap按插入顺序来保存键,同时也保留了HashMap的查询速度
10 ArrayList长于随机访问元素,但插入和移除操作慢;LinkedList长于插入和移除操作,但随机访问慢
11 迭代器是一个对象,它的工作是遍历并选择序列中的对象。
- iterator( )函数要求容器返回一个Iterator,Iterator将准备好返回序列的第一个元素
- next( )函数获得序列中的下一个元素
- hasNext( )函数检查序列中是否还有元素
- remove( )函数将迭代器新近返回的元素删除
public static void main(String[] args){
ArrayList<Integer> pets = new ArrayList<Integer>();
Collections.addAll(pets, 1,2,3,4,5,6,7,8,9,10);
Iterator<Integer> it = pets.iterator();
while(it.hasNext()){
System.out.println(it.next());
it.remove();
}
}
12 ListIterator是一个更强大的Iterator子类型,它只能用于各种List类的访问,ListIterator可以双向移动
ArrayList<Integer> pets = new ArrayList<Integer>();
Collections.addAll(pets, 1,2,3,4,5,6,7,8,9,10);
ListIterator<Integer> it = pets.listIterator();
while(it.hasNext()){
System.out.print(it.next()+", "+it.nextIndex()+", "+it.previousIndex()+":");
}
System.out.println();
while(it.hasPrevious()) // 因为it已经在next()跑到最后一位,所以倒数输出
System.out.print(it.previous()+" ");
System.out.println();
System.out.println(pets);
while(it.hasNext()){
it.next();
it.set(-1);
}
System.out.print(pets);
输出
1, 1, 0:2, 2, 1:3, 3, 2:4, 4, 3:5, 5, 4:6, 6, 5:7, 7, 6:8, 8, 7:9, 9, 8:10, 10, 9:
10 9 8 7 6 5 4 3 2 1
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
13 LinkedList
LinkedList<Integer> pets = new LinkedList<Integer>();
Collections.addAll(pets, 1,2,3,4,5,6,7,8,9,10);
System.out.println(pets);
System.out.println("pets.getFirst(): "+pets.getFirst());
System.out.println("pets.element(): "+pets.element());
System.out.println("pets.peek(): "+pets.peek());
System.out.println("pets.remove(): "+pets.remove());
System.out.println("pets.removeFirst(): "+pets.removeFirst());
System.out.println("pets.poll(): "+pets.poll());
System.out.println(pets);
pets.addFirst(11);
System.out.println("After pets.addFirst(): "+pets);
pets.offer(12);
System.out.println("After pets.offer(): "+pets);
pets.add(13);
System.out.println("After add(): "+pets);
pets.addLast(14);
System.out.println("After pets.addLast(): "+pets);
System.out.println("pets.removeLast(): "+pets.removeLast());
输出
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
pets.getFirst(): 1
pets.element(): 1
pets.peek(): 1
pets.remove(): 1
pets.removeFirst(): 2
pets.poll(): 3
[4, 5, 6, 7, 8, 9, 10]
After pets.addFirst(): [11, 4, 5, 6, 7, 8, 9, 10]
After pets.offer(): [11, 4, 5, 6, 7, 8, 9, 10, 12]
After add(): [11, 4, 5, 6, 7, 8, 9, 10, 12, 13]
After pets.addLast(): [11, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14]
pets.removeLast(): 14
14 map的get( )函数,如果key不在容器中则返回null。
Random rand = new Random(47);
Map<Integer,Integer> m = new HashMap<Integer,Integer>();
for(int i=0;i<10000;i++){
int r = rand.nextInt(20);
Integer freq = m.get(r);
m.put(r, freq==null?1:freq+1);
}
System.out.println(m);
15 LinkedList也支持队列,它实现了Queue接口,所以LinkedList可以向上转型为Queue。
public static void printQ(Queue queue){
while(queue.peek() != null){ // 判断是否有元素
System.out.print(queue.remove()+" ");
}
System.out.println();
}
public static void main(String[] args){
Random rand = new Random(47);
Queue<Integer> m = new LinkedList<Integer>();
for(int i=0;i<10;i++){
int r = rand.nextInt(20);
m.offer(rand.nextInt(i+10)); // 添加元素
}
printQ(m);
Queue<Character> qc = new LinkedList<Character>();
for(char c:"Brontosaurus".toCharArray())
qc.offer(c);
printQ(qc);
}
offer( )将元素插入队尾,peek( )和element( )都在不删除元素的情况返回队头,但peek( )会在队列为空时返回null,而element( )会抛出异常。poll( )和remove( )将移除并返回队头,但poll( )在队列为空时会返回null,而remove( )会抛出移除。
16 PriorityQueue默认排序是自然递增顺序,但可以通过Comparator来修改顺序。
public static void printQ(Queue queue){
while(queue.peek() != null){
System.out.print(queue.remove()+" ");
}
System.out.println();
}
public static void main(String[] args){
PriorityQueue<Integer> priorityQueue = new PriorityQueue<Integer>(); // 默认元素会按大小递增
Random rand = new Random(47);
for(int i=0;i<10;i++)
priorityQueue.offer(rand.nextInt(i+10));
printQ(priorityQueue);
List<Integer> ints = Arrays.asList(25,22,20,18,14,9,3,1,1,2,3,9,14,18,21,23,25);
priorityQueue = new PriorityQueue<Integer>(ints); // 默认元素会按大小递增
printQ(priorityQueue);
priorityQueue = new PriorityQueue<Integer>(ints.size(),Collections.reverseOrder()); // 设置元素按大小递减
priorityQueue.addAll(ints);
printQ(priorityQueue);
String fact = "EDUCATION SHOULD ESCHEW OBFUSCATION";
List<String> strings = Arrays.asList(fact.split(" "));
PriorityQueue<String> pq = new PriorityQueue<String>(strings);
printQ(pq);
pq = new PriorityQueue<String>(strings.size(),Collections.reverseOrder()); // 设置元素按大小递减
pq.addAll(strings);
printQ(pq);
Set<Character> s = new HashSet<Character>();
for(char c:fact.toCharArray())
s.add(c);
PriorityQueue<Character> p = new PriorityQueue<Character>(s);
printQ(p);
}
17 Collection是描述所有序列容器的共性的根接口
18 foreach语法不仅可用于数组,也可用于任何Collection对象。因为它有Iterable接口,该接口包含能够产生Iterator的iterator( )函数,并且Iterable接口被foreach用来在序列中移动。
public class ArrayApp implements Iterable<String> {
protected String[] words = ("And that is how we know the Earth to be banana-shaped.".split(" "));
@Override
public Iterator<String> iterator() {
// TODO Auto-generated method stub
return new Iterator<String>(){
private int index = 0;
@Override
public boolean hasNext() {
// TODO Auto-generated method stub
return index < words.length;
}
@Override
public String next() {
// TODO Auto-generated method stub
return words[index++];
}
@Override
public void remove() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
};
}
public static void main(String[] args){
for(String s:new ArrayApp())
System.out.print(s+",");
}
}
即 foreach语句可用于任何Iterable,数组不是一个Iterable,如下
public class ArrayApp {
static <T> void test(Iterable<T> ib){
for(T t:ib)
System.out.print(t+" ");
}
public static void main(String[] args){
test(Arrays.asList(1,2,3));
String[] strings = {"A","B","C"};
test(strings); // 数组不是一个Iterable
test(Arrays.asList(strings));
}
}
第12章 通过异常处理错误
1 所有标准异常类都应有两个构造器,一个是默认构造器,另一个是接受字符串作为参数,以便能把相关信息放入异常对象的构造器。
2 Throwable类是所有异常类型的根类
3 异常处理try块
try {
// Code that might generate exceptions
} catch( Type1 id1 ) {
// Handle exceptions of Type1
} catch( Type2 id2 ) {
// Handle exceptions of Type2
} catch( Type3 id3 ) {
// Handle exceptions of Type3
}
4 创建自定义异常
class MyException extends Exception{}
public class ArrayApp {
public void f() throws MyException {
System.out.println("Throw MyException from f()");
throw new MyException();
}
public static void main(String[] args){
ArrayApp app = new ArrayApp();
try{
app.f();
}catch(MyException e){
System.out.println("Caught it~");
}
}
}
5 异常与记录日志
class LoggingException extends Exception{
private static Logger logger = Logger.getLogger("LoggingException");
public LoggingException(){
StringWriter trace = new StringWriter();
printStackTrace(new PrintWriter(trace));
logger.severe(trace.toString());
}
}
public class ArrayApp {
public static void main(String[] args){
try{
throw new LoggingException();
}catch(LoggingException e){
System.err.println("Caught "+e);
}
try{
throw new LoggingException();
}catch(LoggingException e){
System.err.println("Caught "+e);
}
}
}
输出
Sep 27, 2018 3:21:23 PM c06.LoggingException <init>
严重: c06.LoggingException
at c06.ArrayApp.main(ArrayApp.java:39)
Caught c06.LoggingException
Sep 27, 2018 3:21:23 PM c06.LoggingException <init>
严重: c06.LoggingException
at c06.ArrayApp.main(ArrayApp.java:44)
Caught c06.LoggingException
6 如果函数名后带throws表示该函数会抛出异常,void f( ) throws XXXException { }
7 因为Exception是所有异常类型的基类,catch( Exception e )可以捕获所有类型的异常,最好放在处理程序所有列表的最后。
8 打印部分信息
try{
throw new Exception();
}catch(Exception e){
System.out.println("Caught Exception");
System.out.println("getMessage(): "+e.getMessage());
System.out.println("getLocalizedMessage(): "+e.getLocalizedMessage());
System.out.println("toString(): "+e);
System.out.print("PrintStackTrace(): ");
e.printStackTrace(System.out);
}
输出
Caught Exception
getMessage(): null
getLocalizedMessage(): null
toString(): java.lang.Exception
PrintStackTrace(): java.lang.Exception
at c06.ArrayApp.main(ArrayApp.java:39)
9 栈轨迹,通过getStackTrace( )返回一个由栈轨迹的元素所构成的数组,如下
public class ArrayApp {
static void f(){
try{
throw new Exception();
}catch(Exception e){
for(StackTraceElement ste:e.getStackTrace()) // 获取栈轨迹
System.out.println(ste.getMethodName());
}
}
static void g(){ f(); }
static void h(){ g(); }
public static void main(String[] args){
f();
System.out.println("-----------------------------");
g();
System.out.println("-----------------------------");
h();
}
}
输出
f
main
-----------------------------
f
g
main
-----------------------------
f
g
h
main
10 重新抛出异常,printStackTrace( )方法显示的是原来异常抛出点的调用栈信息,而fillInStackTrace( )方法则更新了调用栈信息,采用当前的点。
public class ArrayApp {
public static void f() throws Exception{
System.out.println("originating the exception in f()");
throw new Exception("throw from f()");
}
public static void g() throws Exception{
try{
f();
}catch(Exception e){
System.out.println("Inside g(),e.printStackTrace()");
e.printStackTrace(System.out);
throw e;
}
}
public static void h() throws Exception{
try{
f();
}catch(Exception e){
System.out.println("Inside h(),e.printStackTrace()");
e.printStackTrace(System.out);
throw (Exception)e.fillInStackTrace(); //更新了异常抛出点
}
}
public static void main(String[] args){
try{
g();
}catch(Exception e){
System.out.println("main: printStackTrace()");
e.printStackTrace(System.out);
}
try{
h();
}catch(Exception e){
System.out.println("main: printStackTrace()");
e.printStackTrace(System.out);
}
}
}
输出
originating the exception in f()
Inside g(),e.printStackTrace()
java.lang.Exception: throw from f()
at c06.ArrayApp.f(ArrayApp.java:38)
at c06.ArrayApp.g(ArrayApp.java:42)
at c06.ArrayApp.main(ArrayApp.java:60)
main: printStackTrace()
java.lang.Exception: throw from f()
at c06.ArrayApp.f(ArrayApp.java:38)
at c06.ArrayApp.g(ArrayApp.java:42)
at c06.ArrayApp.main(ArrayApp.java:60)
originating the exception in f()
Inside h(),e.printStackTrace()
java.lang.Exception: throw from f()
at c06.ArrayApp.f(ArrayApp.java:38)
at c06.ArrayApp.h(ArrayApp.java:51)
at c06.ArrayApp.main(ArrayApp.java:66)
main: printStackTrace()
java.lang.Exception: throw from f()
at c06.ArrayApp.h(ArrayApp.java:55)
at c06.ArrayApp.main(ArrayApp.java:66)
11 try块里只要有个catch捕获到异常,剩下的catch就不会进行捕获
12 在Throwable的子类中,只有三种基本异常类提供了带cause参数的构造器,他们是Error,Exception和RuntimeException。
13 Throwable被用来表示任何可以作为异常被抛出的类。Throwable对象可分为两种类型:Error表示编译时和系统错误;Exception是可以抛出的基本类型。
14 特例:RuntimeException类型的异常被称为不受检查异常,即编译器会自动捕获。
public class ArrayApp {
static void f(){
throw new RuntimeException("From f()");
}
static void g(){
f();
}
public static void main(String[] args){
g();
}
}
15 finally在try块总会执行
public class ArrayApp {
static int count = 0;
public static void main(String[] args){
while(true){
try{
if(count++ == 0){
throw new ThreeException();
}
System.out.println("No exception");
}catch(ThreeException e){
System.out.println("ThreeException");
}finally{
System.out.println("In finally clause");
if(count == 2)
break;
}
}
}
}
输出
ThreeException
In finally clause
No exception
In finally clause
16 在return中使用finally,在return前会先执行finally。
public class ArrayApp {
static int count = 0;
public static void f(int i){
System.out.println("Initialization that requires cleanup");
try{
System.out.println("Point 1");
if(1 == i) return;
System.out.println("Point 2");
if(2 == i) return;
System.out.println("Point 3");
if(3 == i) return;
System.out.println("End");
return;
}finally{
System.out.println("Performing cleanup");
}
}
public static void main(String[] args){
for(int i=1;i<4;i++)
f(i);
}
}
输出
Initialization that requires cleanup
Point 1
Performing cleanup
Initialization that requires cleanup
Point 1
Point 2
Performing cleanup
Initialization that requires cleanup
Point 1
Point 2
Point 3
Performing cleanup
17 finally会有造成异常丢失的风险,如下所示,OneException异常丢失了
class OneException extends Exception{
public String toString(){
return "One exception";
}
}
class TwoException extends Exception{
public String toString(){
return "Two exception";
}
}
public class ArrayApp {
void f() throws OneException{
throw new OneException();
}
void dispose() throws TwoException{
throw new TwoException();
}
public static void main(String[] args){
try{
ArrayApp a = new ArrayApp();
try{
a.f();
}finally{
a.dispose();
}
}catch(Exception e){
System.out.println(e);
}
}
}
18 当覆盖方法时,只能抛出在基类方法的异常说明里列出的那些异常,这是为了保证基类使用的代码应用到派生类对象时仍有效。
class BaseballException extends Exception {}
class Foul extends BaseballException {}
class Strike extends BaseballException {}
abstract class Inning{
public Inning() throws BaseballException {}
public void event() throws BaseballException {}
public abstract void atBat() throws Strike,Foul;
public void walk() {}
}
class StormException extends Exception {}
class RainedOut extends StormException {}
class PopFoul extends Foul {}
interface Storm{
public void event() throws RainedOut;
public void rainHard() throws RainedOut;
}
public class ArrayApp extends Inning implements Storm{
public ArrayApp() throws RainedOut,BaseballException{}
public ArrayApp(String s) throws Foul,BaseballException{}
// void walk() throws PopFoul{} // error,因为基类该函数没有抛出异常
// public void event() throws RainedOut{} // event()有两种异常抛出,无法判断
public void rainHard() throws RainedOut{}
public void event(){}
public void atBat() throws PopFoul{}
public static void main(String[] args){
try{
ArrayApp a = new ArrayApp();
a.atBat();
}catch(PopFoul e){
System.out.println("Pop Foul");
}catch(RainedOut e){
System.out.println("Rained Out");
}catch(BaseballException e){
System.out.println("Generic baseball exception");
}
try{
Inning i = new ArrayApp();
i.atBat();
}catch(Strike e){
System.out.println("Strike");
}catch(Foul e){
System.out.println("Foul");
}catch(RainedOut e){
System.out.println("Rained Out");
}catch(BaseballException e){
System.out.println("Generic baseball exception");
}
}
}
19 异常的派生类也可以被基类捕获匹配上
class Annoyance extends Exception {}
class Sneeze extends Annoyance {}
public class ArrayApp {
public static void main(String[] args){
try{
throw new Sneeze();
}catch(Annoyance e){ // 被父类匹配到
System.out.println("Annoyance");
}
}
}