第 4 讲 Java 对象容器与异常处理

82
上上上上上上上上上上上上上上上 4 4 Java Java 第第第第第第第第第 第第第第第第第第第

description

第 4 讲 Java 对象容器与异常处理. 一、 Java 容器. 内容. 对象保持 再论数组 容器概述 List , Set , Map 小结. 1. 对象保持. 在程序中,经常需要动态产生一些对象 你无法知道要产生多少对象 你无法事先知道这些对象的类型 对象持有方式 内置的数组方式 容器类. 2. 再论数组 (Arrays). 提供了存储及随机访问一系列对象的 最有效率 的方法 由于线性存储,所以访问效率高 容量固定 提供边界检查 定义数组时,需要定义特定类型 概念 array object 是本身对象 - PowerPoint PPT Presentation

Transcript of 第 4 讲 Java 对象容器与异常处理

上海交通大学计算机科学与工程系

第第 44 讲 讲 JavaJava 对象容器与异常处对象容器与异常处理理

上海交通大学计算机科学与工程系

一、一、 JavaJava 容器容器

上海交通大学计算机科学与工程系23/4/19 3

内容内容对象保持再论数组容器概述 List , Set , Map小结

上海交通大学计算机科学与工程系23/4/19 4

1. 1. 对象保持对象保持在程序中,经常需要动态产生一些对象

你无法知道要产生多少对象 你无法事先知道这些对象的类型

对象持有方式 内置的数组方式 容器类

上海交通大学计算机科学与工程系23/4/19 5

2. 2. 再论数组再论数组 (Arrays)(Arrays)

提供了存储及随机访问一系列对象的最有效率的方法 由于线性存储,所以访问效率高 容量固定 提供边界检查 定义数组时,需要定义特定类型

概念 array object是本身对象 objects array”由对象形成的数组” primitive array”基本型别元素构成的数组”

上海交通大学计算机科学与工程系23/4/19 6

//: arrays/ArrayOptions.java// Initialization & re-assignment of arrays.import java.util.*;import static net.mindview.util.Print.*;public class ArrayOptions { public static void main(String[] args) { // Arrays of objects: BerylliumSphere[] a; // Local uninitialized variable BerylliumSphere[] b = new BerylliumSphere[5]; // The references inside the array are // automatically initialized to null: print("b: " + Arrays.toString(b)); BerylliumSphere[] c = new BerylliumSphere[4]; for(int i = 0; i < c.length; i++) if(c[i] == null) // Can test for null reference c[i] = new BerylliumSphere(); // Aggregate initialization: BerylliumSphere[] d = { new BerylliumSphere(), new BerylliumSphere(), new BerylliumSphere() }; // Dynamic aggregate initialization: a = new BerylliumSphere[]{ new BerylliumSphere(), new BerylliumSphere(), };}

// (Trailing comma is optional in both cases) print("a.length = " + a.length); print("b.length = " + b.length); print("c.length = " + c.length); print("d.length = " + d.length); a = d; print("a.length = " + a.length);

// Arrays of primitives: int[] e; // Null reference int[] f = new int[5]; // The primitives inside the array are // automatically initialized to zero: print("f: " + Arrays.toString(f)); int[] g = new int[4]; for(int i = 0; i < g.length; i++) g[i] = i*i; int[] h = { 11, 47, 93 }; // Compile error: variable e not initialized: //!print("e.length = " + e.length); print("f.length = " + f.length); print("g.length = " + g.length); print("h.length = " + h.length); e = h; print("e.length = " + e.length); e = new int[]{ 1, 2 }; print("e.length = " + e.length); }

上海交通大学计算机科学与工程系23/4/19 7

返回数组返回数组

//: arrays/IceCream.java// Returning arrays from methods.import java.util.*;public class IceCream { private static Random rand = new Random(47); static final String[] FLAVORS = { "Chocolate", "Strawberry", "Vanilla Fudge Swirl", "Mint Chip", "Mocha Almond Fudge", "Rum Raisin", "Praline Cream", "Mud Pie" };

public static String[] flavorSet(int n) { if(n > FLAVORS.length) throw new IllegalArgumentException("Set too big"); String[] results = new String[n]; boolean[] picked = new boolean[FLAVORS.length]; for(int i = 0; i < n; i++) { int t; do t = rand.nextInt(FLAVORS.length); while(picked[t]); results[i] = FLAVORS[t]; picked[t] = true; } return results; } public static void main(String[] args) { for(int i = 0; i < 7; i++) System.out.println(Arrays.toString(flavorSet(3))); }}

上海交通大学计算机科学与工程系23/4/19 8

数组的几个功能数组的几个功能充填//: arrays/FillingArrays.java

// Using Arrays.fill()import java.util.*;import static net.mindview.util.Print.*;

public class FillingArrays { public static void main(String[] args) { int size = 6; boolean[] a1 = new boolean[size]; byte[] a2 = new byte[size]; char[] a3 = new char[size]; short[] a4 = new short[size]; int[] a5 = new int[size]; long[] a6 = new long[size]; float[] a7 = new float[size]; double[] a8 = new double[size]; String[] a9 = new String[size];

Arrays.fill(a1, true); print("a1 = " + Arrays.toString(a1)); Arrays.fill(a2, (byte)11); print("a2 = " + Arrays.toString(a2)); Arrays.fill(a3, 'x'); print("a3 = " + Arrays.toString(a3)); Arrays.fill(a4, (short)17); print("a4 = " + Arrays.toString(a4)); Arrays.fill(a5, 19); print("a5 = " + Arrays.toString(a5)); Arrays.fill(a6, 23); print("a6 = " + Arrays.toString(a6)); Arrays.fill(a7, 29); print("a7 = " + Arrays.toString(a7)); Arrays.fill(a8, 47); print("a8 = " + Arrays.toString(a8)); Arrays.fill(a9, "Hello"); print("a9 = " + Arrays.toString(a9)); // Manipulating ranges: Arrays.fill(a9, 3, 5, "World"); print("a9 = " + Arrays.toString(a9)); }}

上海交通大学计算机科学与工程系23/4/19 9

复制int[] i = new int[7]; int[] j = new int[10]; Arrays.fill(i, 47); Arrays.fill(j, 99); System.arraycopy(i, 0, j, 0, i.length);

上海交通大学计算机科学与工程系23/4/19 10

比较

//: arrays/ComparingArrays.java// Using Arrays.equals()import java.util.*;import static net.mindview.util.Print.*;

public class ComparingArrays { public static void main(String[] args) { int[] a1 = new int[10]; int[] a2 = new int[10]; Arrays.fill(a1, 47); Arrays.fill(a2, 47); print(Arrays.equals(a1, a2)); a2[3] = 11; print(Arrays.equals(a1, a2)); String[] s1 = new String[4]; Arrays.fill(s1, "Hi"); String[] s2 = { new String("Hi"), new String("Hi"), new String("Hi"), new String("Hi") }; print(Arrays.equals(s1, s2)); }}

上海交通大学计算机科学与工程系23/4/19 11

元素的比较 方法 1 :实现 java.lang.Comparable

interface,中的函数 compareTo() 方法 2 :撰写一个 class,令它实现 Comparator

interface, 它包含两个函数: compare() 和equals()

Strategy 模式:将变化的部分与不变的部分分离,变化的部分放在单独的类( Strategy 对象)中

上海交通大学计算机科学与工程系23/4/19 12

排序 使用内置的 sorting函数,你可以针对任何

primitives array进行排序,也可以针对任何objects array进行排序(只要那些对象实现了Comparable或者拥有相关之 Comparator)

上海交通大学计算机科学与工程系23/4/19 13

import com.bruceeckel.util.*;import java.util.*;public class CompType implements Comparable { int i; int j; public CompType(int n1, int n2) { i = n1; j = n2; } public String toString() { return "[i = " + i + ", j = " + j + "]"; } public int compareTo(Object rv) { int rvi = ((CompType)rv).i; return (i < rvi ? -1 : (i == rvi ? 0 : 1)); } private static Random r = new Random(); public static Generator generator() { return new Generator() { public Object next() {return new CompType(r.nextInt(100),r.nextInt(100)); } }; } public static void main(String[] args) { CompType[] a = new CompType[10]; Arrays2.fill(a, generator()); System.out.println( "before sorting, a = " + Arrays.asList(a)); Arrays.sort(a); System.out.println( "after sorting, a = " + Arrays.asList(a)); }} ///:~

package com.bruceeckel.util;public interface Generator { Object next(); } ///:~

匿名内隐类

必须实现的接口

上海交通大学计算机科学与工程系23/4/19 14

3. 3. 容器概述容器概述当你撰写程序时不知道究竟需要多少对象时,使用容器

两种类型的容器 Collection:一组各自独立的元素,包括 List,

Set Map: 成对的 key-value对象

上海交通大学计算机科学与工程系23/4/19 15

3.1 3.1 容器放入对象容器放入对象

ArrayList不进行类型检查

import java.util.*;class Apple { private static long counter; private final long id = counter++; public long id() { return id; }}class Orange {}public class ApplesAndOrangesWithoutGenerics { @SuppressWarnings("unchecked") public static void main(String[] args) { ArrayList apples = new ArrayList(); for(int i = 0; i < 3; i++) apples.add(new Apple()); // Not prevented from adding an Orange to apples: apples.add(new Orange()); for(int i = 0; i < apples.size(); i++) ((Apple)apples.get(i)).id(); // Orange is detected only at run time }} /* (Execute to see output) *///:~

上海交通大学计算机科学与工程系23/4/19 16

//: holding/ApplesAndOrangesWithGenerics.javaimport java.util.*;

public class ApplesAndOrangesWithGenerics { public static void main(String[] args) { ArrayList<Apple> apples = new ArrayList<Apple>(); for(int i = 0; i < 3; i++) apples.add(new Apple()); // Compile-time error: // apples.add(new Orange()); for(int i = 0; i < apples.size(); i++) System.out.println(apples.get(i).id()); // Using foreach: for(Apple c : apples) System.out.println(c.id()); }}

上海交通大学计算机科学与工程系23/4/19 17

3.23.2 容器打印容器打印

上海交通大学计算机科学与工程系23/4/19 18

3.33.3 容器的缺点容器的缺点当对象放入容器时,型别信息就被损失容器中保存的都是引用,需要自己维护其类型

上海交通大学计算机科学与工程系23/4/19 19

3.43.4 迭代器(迭代器( Iterators)Iterators)

容器需要提供一些公共的功能 取元素 删除元素 查询元素

提供一个公共对象,执行基本功能,从而可以不管类型: Iterator

上海交通大学计算机科学与工程系23/4/19 20

Java中的 Iterator功能比较简单,并且只能单向移动:  使用方法 iterator()要求容器返回一个 Iterator。第一次调用 Iterator 的 next()方法时,它返回序列的第一个元素。 

使用 next()获得序列中的下一个元素。 使用 hasNext()检查序列中是否还有元素。 使用 remove()将迭代器新返回的元素删除。 Iterator 是 Java迭代器最简单的实现,为 List设计的 ListIterator具有更多的功能,它可以从两个方向遍历 List,也可以从 List中插入和删除元素。 

上海交通大学计算机科学与工程系23/4/19 21

上海交通大学计算机科学与工程系23/4/19 22

4 4 容器分类容器分类 短虚线方块代表 interfaces,实线方块代表一般的类,虚线箭头表示实现某个接口的类,实线箭头表示某个类可以产生所指的那个类的对象

上海交通大学计算机科学与工程系23/4/19 23

4.1 Iterator4.1 Iterator 与与ListIteratorListIterator

ListIterator 有 add()方法,可以向 List中添加对象,而 Iterator不能 

ListIterator 和 Iterator都有 hasNext() 和 next()方法,可以实现顺序向后遍历,但是 ListIterator 有hasPrevious() 和 previous()方法,可以实现逆向(顺序向前)遍历。 Iterator就不可以。 

ListIterator可以定位当前的索引位置, nextIndex()和 previousIndex()可以实现。 Iterator没有此功能。 

都可实现删除对象,但是 ListIterator可以实现对象的修改, set()方法可以实现。 Iierator仅能遍历,不能修改。

上海交通大学计算机科学与工程系23/4/19 24

4.2 Collection4.2 Collection 的功能的功能 Set 和 List具备 Collection的功能,Map并不继承

Collection boolean add(Object) boolean addAll(Collection) (“Optional.”) void clear( ) (“Optional.”) boolean contains(Object) boolean containsAll(Collection) boolean isEmpty( ) Iterator iterator( ) boolean remove(Object)(“Optional.”) boolean removeAll(Collection) (“Optional.”) boolean retainAll(Collection)(“Optional.”) int size( ) Object[] toArray( ) Object[] toArray(Object[] a)

可选:防止在设计中出现接口爆炸的情况,允许特例下,那些方法没有实现,但是绝大多数情形下是

实现的 只保留相交

的元素

上海交通大学计算机科学与工程系23/4/19 25

4.3 List4.3 List

List(interface): 次序是 List最重要的特点;它确保维护元素特定的顺序。 List 为 Collection添加了许多方法,使得能够向 List中间插入与移除元素 ( 只推荐 LinkedList使用 ) 。一个 List可以生成ListIterator,使用它可以从两个方向遍历 List,也可以从 List中间插入和删除元素。

ArrayList: 由数组实现的 List。它允许对元素进行快速随机访问,但是向List中间插入与移除元素的速度很慢。 ListIterator只应该用来由后向前遍历 ArrayList,而不是用来插入和删除元素,因为这比 LinkedList开销要大很多。 

LinkedList: 对顺序访问进行了优化,向 List中间插入与删除的开销不大,随机访问则相对较慢 ( 可用 ArrayList代替 ) 。它具有方法addFirst() 、 addLast() 、 getFirst() 、 getLast() 、 removeFirst() 、 removeLast(),这些方法 ( 没有在任何接口或基类中定义过 ) 使得LinkedList可以当作堆栈、队列和双向队列使用。 

上海交通大学计算机科学与工程系23/4/19 26

Set(interface): 存入 Set的每个元素必须是唯一的,因为 Set不保存重复元素。加入 Set 的 Object必须定义 equals()方法以确保对象的唯一性。Set 与 Collection有完全一样的接口。 Set接口不保证维护元素的次序。 

HashSet: 为快速查找而设计的 Set。存入HashSet的对象必须定义hashCode()。

TreeSet: 保持次序的 Set,底层为树结构。使用它可以从 Set中提取有序的序列。 

LinkedHashSet: 具有HashSet的查询速度,且内部使用链表维护元素的顺序( 插入的次序 ) 。于是在使用迭代器遍历 Set时,结果会按元素插入的次序显示。

HashSet采用散列函数对元素进行排序,这是专门为快速查询而设计的;TreeSet采用红黑树的数据结构进行排序元素; LinkedHashSet内部使用散列以加快查询速度,同时使用链表维护元素的次序,使得看起来元素是以插入的顺序保存的。需要注意的是,生成自己的类时, Set需要维护元素的存储顺序,因此要实现 Comparable接口并定义 compareTo()方法。 

上海交通大学计算机科学与工程系23/4/19 27

4.4 Map4.4 Map

如果需要依据某些条件来在一串对象中进行选择 Map 提供了一个更通用的元素存储方法。 

Map 集合类用于存储元素对(称作“键”和“值”),其中每个键映射到一个值。 

上海交通大学计算机科学与工程系23/4/19 28

Map 接口和方法: 覆盖的方法, 我们Object 的这两个方法覆盖,以正确比较 

Map 对象的等价性   equals(Object o):比较指定对象与此 Map 的等价性  hashCode() :返回此 Map 的哈希码 

Map 构建 , Map 定义了几个用于插入和删除元素的变换方法

clear():从 Map 中删除所有映射  remove(Object key):从 Map 中删除键和关联的值 put(Object key, Object value):将指定值与指定键相关联 putAll(Map t):将指定 Map 中的所有映射复制到此 map

上海交通大学计算机科学与工程系23/4/19 29

查看 Map 迭代 Map 中的元素不存在直接了当的方法。 如果要查询某个 Map 以了解其哪些元素满足特定查询,或如果要迭代其所有元素(无论原因如何),则您首先需要获取该 Map 的“视图”。 有三种可能的视图

entrySet():返回 Map 中所包含映射的  Set 视图。  Set 中的每个元素都是一个 Map.Entry 对象,可以使用 getKey() 和  getValue() 方法(还有一个  setValue() 方法)访问后者的键元素和值元素

keySet():返回 Map 中所包含键的  Set 视图。 删除  Set 中的元素还将删除 Map 中相应的映射(键和值) 

values(): 返回 map 中所包含值的  Collection 视图。 删除  Collection 中的元素还将删除 Map 中相应的映射(键和值) 

上海交通大学计算机科学与工程系23/4/19 30

访问元素 : Map 通常适合按键(而非按值)进行访问,这些方法检索有关 Map 内容的信息但不更改 Map 内容。  get(Object key) 返回与指定键关联的值 containsKey(Object key) 如果 Map 包含指定键的映射,则返回  true 

containsValue(Object value)如果此 Map 将一个或多个键映射到指定值,则返回  true 

isEmpty()如果 Map 不包含键 -值映射,则返回 true 

size()返回 Map 中的键 -值映射的数目 

上海交通大学计算机科学与工程系23/4/19 31

Map的类别

上海交通大学计算机科学与工程系23/4/19 32

HashMapHashMap 的使用的使用

上海交通大学计算机科学与工程系23/4/19 33

上海交通大学计算机科学与工程系23/4/19 34

关于关于 hashCodehashCode

如果不覆写所使用的 key 的 hashcode()和 Equals(),那么HashSet 或HashMap都无法正确处理

上海交通大学计算机科学与工程系23/4/19 35

引用类引用类 Java 2 平台引入了  java.lang.ref 包,其中包括的类可以让您引用对象,而不将它们留在内存中。这些类还提供了与垃圾收集器( garbage collector)之间有限的交互。

引用类的主要功能就是能够引用仍可以被垃圾收集器回收的对象。

上海交通大学计算机科学与工程系23/4/19 36

在引入引用类之前,我们只能使用强引用( strong reference)。举例来说,下面一行代码显示的就是强引用  obj : 

Object obj = new Object(); obj 这个引用将引用堆中存储的一个对象。只要  obj 引用还存在,垃圾收集器就永远不会释放用来容纳该对象的存储空间。 

当  obj 超出范围或被显式地指定为  null 时,垃圾收集器就认为没有对这个对象的其它引用,也就可以收集它了。 

强引用是使用最普遍的引用。如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空 间不足, Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题 

上海交通大学计算机科学与工程系23/4/19 37

软引用( Soft Reference) 如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。

只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

软引用可以和一个引用队列( ReferenceQueue )联合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

上海交通大学计算机科学与工程系23/4/19 38

弱引用(WeakReference) 弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。 弱引用可以和一个引用队列( ReferenceQueue )联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

上海交通大学计算机科学与工程系23/4/19 39

虚引用( PhantomReference)  “虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。

虚引用主要用来跟踪对象被垃圾回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列( ReferenceQueue)联合使用。当垃 圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是 否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

上海交通大学计算机科学与工程系23/4/19 40

上海交通大学计算机科学与工程系23/4/19 41

上海交通大学计算机科学与工程系23/4/19 42

WeakHashMapWeakHashMap

在这种Map中存放了键对象的弱引用,当一个键对象被垃圾回收器回收时,那么相应的值对象的引用会从Map中删除。WeakHashMap能够节约存储空间,可用来缓存那些非必须存在的数据 

上海交通大学计算机科学与工程系23/4/19 43

小结小结 1 、容器类和 Array的区别、择取    *  容器类仅能持有对象引用(指向对象的指针),而不是将对象信息 copy一份至数列某位置。    *  一旦将对象置入容器内,便损失了该对象的型别信息。

2 、   *  在各种 Lists中,最好的做法是以 ArrayList作为缺省选择。当插入、删除频繁时,使用 LinkedList();     Vector总是比 ArrayList慢,所以要尽量避免使用。   *  在各种 Sets中,HashSet通常优于HashTree(插入、查找)。只有当需要产生一个经过排序的序列,才用 TreeSet。     HashTree存在的唯一理由:能够维护其内元素的排序状态。

   *  在各种Maps中  HashMap用于快速查找。   *  当元素个数固定,用 Array,因为 Array效率是最高的。

结论:最常用的是ArrayList , HashSet , HashMap , Array。

上海交通大学计算机科学与工程系23/4/19 44

1 、 Collection没有 get()方法来取得某个元素。只能通过 iterator()遍历元素。2 、 Set 和 Collection拥有一模一样的接口。3 、 List,可以通过 get()方法来一次取出一个元素。使用数字来选择一堆对象中的一个, get(0)... 。 (add/get)4 、一般使用ArrayList。用 LinkedList构造堆栈 stack、队列 queue。

5 、 Map用  put(k,v) / get(k),还可以使用 containsKey()/containsValue()来检查其中是否含有某个 key/value。   HashMap会利用对象的 hashCode来快速找到 key。     *  hashing       哈希码就是将对象的信息经过一些转变形成一个独一无二的 int值,这个值存储在一个 array中。       我们都知道所有存储结构中, array查找速度是最快的。所以,可以加速查找。             发生碰撞时,让 array指向多个 values。即,数组每个位置上又生成一个梿表。

6 、 Map中元素,可以将 key序列、 value序列单独抽取出来。使用 keySet()抽取 key序列,将map中的所有 keys生成一个 Set。使用 values()抽取 value序列,将map中的所有 values生成一个 Collection。

为什么一个生成 Set,一个生成 Collection?那是因为, key总是独一无二的, value允许重复。

上海交通大学计算机科学与工程系

二、异常处理二、异常处理

上海交通大学计算机科学与工程系23/4/19 46

内容内容

异常的概念异常的分类捕获异常声明异常抛出异常创造自己的异常

上海交通大学计算机科学与工程系23/4/19 47

1 1 异常的概念异常的概念

什么是异常 ? 异常实际上是程序中错误导致中断了正常的指令流的一种事件 .

没有处理错误的程序 : read-file { openTheFile; determine its size; allocate that much memory; closeTheFile; }

上海交通大学计算机科学与工程系23/4/19 48

以常规方法处理错误 openFiles; if (theFilesOpen) { determine the lenth of the file; if (gotTheFileLength){ allocate that much memory; if (gotEnoughMemory) { read the file into memory; if (readFailed) errorCode=-1; else errorCode=-2; }else errorCode=-3; }else errorCode=-4 ; }else errorCode=-5;

上海交通大学计算机科学与工程系23/4/19 49

观察前面的程序你会发现大部分精力花在出错处理上了 .

只把能够想到的错误考虑到 ,对以外的情况无法处理

程序可读性差出错返回信息量太少

上海交通大学计算机科学与工程系23/4/19 50

用异常的形式处理错误read-File;{ try { openTheFile; determine its size; allocate that much memory; closeTheFile; }catch(fileopenFailed) { dosomething; } catch(sizeDetermineFailed) {dosomething;} catch(memoryAllocateFailed){ dosomething;} catch(readFailed){ dosomething;} catch(fileCloseFailed) { dosomething; }}

上海交通大学计算机科学与工程系23/4/19 51

和传统的方法比较异常的优点 :1.把错误代码从常规代码中分离出来2. 把错误传播给调     用堆栈3. 按错误类型和    错误差别分组4. 系统提供了对于一些无法预测的错误的捕获和处理

5. 克服了传统方法的错误信息有限的问题

method1

method2

method3

method4 产生异常传 递

处理异常

上海交通大学计算机科学与工程系23/4/19 52

class ExcepTest{ public void main(String args[]) { int b=0; int a; try { a=4/b;} catch(ArithmeticException e) { System.out.println(“divided by 0”);} }}

try{ URL url=new URL(http://www.hit.edu.cn/”,”hit.gif”);}catch(MalformedURLEception e){ badURL=true; repaint();}

上海交通大学计算机科学与工程系23/4/19 53

异常的输入参数异常的输入参数异常对象由 new构造 ,有两个构造函数

缺省构造函数 可以接受字符串的构造函数

上海交通大学计算机科学与工程系23/4/19 54

2. 2. 异常的分类异常的分类

异常是一个对象 ,它继承自 Throwable 类 ,所有的Throwable类的子孙类所产生的对象都是例外 .

Error: 由 Java虚拟机生成并抛出 ,Java程序不做处理 .

Runtime Exception( 被 0 除等系统错误 ,数组下标超范围 ):由系统检测 , 用户的 Java 程序可不做处理 ,系统将它们交给缺省的异常处理程序 .

Exception( 程序中的问题 ,可预知的 ): Java编译器要求 Java程序必须捕获或声明所有的非运行时异常

throw:用户自己产生异常

上海交通大学计算机科学与工程系23/4/19 55

.

Throwable

ErrorException

RuntimeException

缺省的异常处理程序

由用户捕获或声明并处理

不做处理

用户自己产生的异常

要处理

上海交通大学计算机科学与工程系23/4/19 56

3. 3. 捕获异常捕获异常

捕获并处理异常try { //接受监视的程序块 ,在此区域内发生 //的异常 , 由 catch中指定的程序处理 ;}catch( 要处理的异常种类和标识符 ) { //处理异常 ;}catch( 要处理的异常种类和标识符 ) { //处理异常 ;}

注意 :Catch需要从特殊到一般

上海交通大学计算机科学与工程系23/4/19 57

常见的异常 ArithmeticException ArrayIndexOutOfBandsException ArrayStoreException IOException FileNotFoundException NullPointerException MalformedURLException NumberFormatException OutOfMemoryException

如果在使用能够产生异常的方法而没有捕获和处理,将不能通过编译

上海交通大学计算机科学与工程系23/4/19 58

例 : 编写 Java程序 ,包含三种异常 算术异常 , 字符串越界 ,数组越界

观察输出信息 : 每个异常对象可以直接给出信息

上海交通大学计算机科学与工程系23/4/19 59

4.3 4.3 捕获异常捕获异常class first_exception

{ public static void main(String args[])

{ char c; int a,b=0;int[] array=new int[7];

String s="Hello";

try {a=1/b;}catch(ArithmeticException ae) { System.out.println(“Catch “+ae));}try {array[8]=0;} catch(ArrayIndexOutOfBoundsException ai){ System.out.println((“Catch “+ai);}

try{ c=s.charAt(8));}catch(StringIndexOutOfBoundsException se){ System.out.println((“Catch “+se);}}}

上海交通大学计算机科学与工程系23/4/19 60

一定会执行的程序块 ---finally异常处理的统一出口try { //常规的代码 ;}catch(){ //处理异常  }finally { //不论发生什么异常 ( 或者不发生任何异常 ),都要执行的部分 ;

}

上海交通大学计算机科学与工程系23/4/19 61

//: exceptions/FinallyWorks.java// The finally clause is always executed.class ThreeException extends Exception {}public class FinallyWorks { static int count = 0; public static void main(String[] args) { while(true) { try { // Post-increment is zero first time: 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; // out of "while" } } }} /* Output:ThreeExceptionIn finally clauseNo exceptionIn finally clause*///:~

上海交通大学计算机科学与工程系23/4/19 62

finally在文件处理时非常有用try { 对文件进行处理的程序 ;}catch(IOException e) { //对文件异常进行处理 ;}finally { 不论是否发生异常 ,都关闭文件 ;}

上海交通大学计算机科学与工程系23/4/19 63

4. 4. 声明异常声明异常 一个方法不处理它产生的异常 ,而是沿着调用层次向上传递 ,由调用它的方法来处理这些异常 ,叫声明异常 .

声明异常的方法在产生异常的方法名后面加上要抛出 (throws)的异常的列表

void compute(int x)throws ArithmeticException {…}

returnType methodName([parameterlist]) throws exceptionList

注意是 throws, 而不是 throw

上海交通大学计算机科学与工程系23/4/19 64

例 : 若因某种原因不想在创建 URL的方法中处理异常

public method1(){ int x; try { x=System.in.read(); compute(x);} catch(IOException ioe) { System.out.println(“read error”); } catch(ArithmeticException e) { System.out.println(“devided by 0”); }}

public int compute(int x) throws ArithmeticException e){ return z=100/x;}

上海交通大学计算机科学与工程系23/4/19 65

method1

compute 异常抛出

处理

上海交通大学计算机科学与工程系23/4/19 66

例 :说出程序执行结果public class exception1{ static void Proc(int sel) throws ArithmeticException, ArrayIndexOutOfBoundsException { System.out.println(“In Situation" + sel ); if (sel==0) { System.out.println("no Exception caught"); return; }else if(sel==1) {int iArray[]=new int[4]; iArray[10]=3; } }

上海交通大学计算机科学与工程系23/4/19 67

public static void main(String args[]){ try { Proc(0); Proc(1); }catch(ArrayIndexOutOfBoundsException e){ System.out.println("Catch"+e); }}

c:>java exception1In Situation 0no Exception caughtIn Situation 1Catch java.lang.ArrayIndexOutOfBoundsException:10

上海交通大学计算机科学与工程系23/4/19 68

//: exceptions/ExceptionMethods.java// Demonstrating the Exception Methods.import static net.mindview.util.Print.*;public class ExceptionMethods { public static void main(String[] args) { try { throw new Exception("My Exception"); } catch(Exception e) { print("Caught Exception"); print("getMessage():" + e.getMessage()); print("getLocalizedMessage():" + e.getLocalizedMessage()); print("toString():" + e); print("printStackTrace():"); e.printStackTrace(System.out); } }} /* Output:Caught ExceptiongetMessage():My ExceptiongetLocalizedMessage():My ExceptiontoString():java.lang.Exception: My ExceptionprintStackTrace():java.lang.Exception: My Exception at ExceptionMethods.main(ExceptionMethods.java:8)*///:~

上海交通大学计算机科学与工程系23/4/19 69

5. 5. 抛出异常抛出异常

抛出异常 : 不是出错产生 ,而是人为地抛出 throw ThrowableObject; throw new ArithmeticException(); 例 :编写程序人为抛出 (JavaThrow.prj)

ArithmeticException,

ArrayIndexOutOfBoundsException

StringIndexOutOfBoundsExceptionA method Exception Another method

throw caught

上海交通大学计算机科学与工程系23/4/19 70

4.5 4.5 抛出异常抛出异常class JavaThrow{ public static void main(String args[])

{try{ throw new ArithmeticException();}catch(ArithmeticException ae){ System.out.println(ae); }

try{ throw new ArrayIndexOutOfBoundsException();}catch(ArrayIndexOutOfBoundsException ai){ System.out.println(ai); }

try { throw new StringIndexOutOfBoundsException();}catch(StringIndexOutOfBoundsException si){{ System.out.println(si); }

上海交通大学计算机科学与工程系23/4/19 71

重新抛出异常重新抛出异常将异常在 Catch中重新抛出,会将异常移动到更高层的 Context内处理函数去。

//: c09:Rethrowing.java// Demonstrating fillInStackTrace()// From 'Thinking in Java, 3rd ed.' (c) Bruce Eckel 2002import com.bruceeckel.simpletest.*;public class Rethrowing { private static Test monitor = new Test(); public static void f() throws Exception { System.out.println("originating the exception in f()"); throw new Exception("thrown from f()"); } public static void g() throws Throwable { try { f(); } catch(Exception e) { System.err.println("Inside g(),e.printStackTrace()"); e.printStackTrace(); throw e; // 17 // throw e.fillInStackTrace(); // 18 } } public static void main(String[] args) throws Throwable { try { g(); } catch(Exception e) { System.err.println( "Caught in main, e.printStackTrace()"); e.printStackTrace(); } }} ///:~

上海交通大学计算机科学与工程系23/4/19 72

6 6 创造自己的异常创造自己的异常

不是由 Java系统监测到的异常 ( 下标越界 , 被0-除等 ),而是由用户自己定义的异常 .

用户定义的异常同样要用 try--catch捕获 ,但必须由用户自己抛出  throw new MyException.

异常是一个类 ,用户定义的异常必须继承自Throwable 或 Exception 类 ,建议用Exception 类 .

上海交通大学计算机科学与工程系23/4/19 73

形如 : class MyException extends Exception {….}; 例 1 :计算两个数之和 ,当任意一个数超出范围时 ,抛出自己的异常

public class NumberRangeException extends Exception{ public NumberRangeException(String msg) { super(msg); }}

上海交通大学计算机科学与工程系23/4/19 74

6. 6. 创造自己的异常创造自己的异常

public boolean action(Event evt, Object arg) { try { int answer = CalcAnswer(); answerStr = String.valueOf(answer); }catch (NumberRangeException e) { answerStr = e.getMessage(); } repaint(); return true;}

上海交通大学计算机科学与工程系23/4/19 75

4.6 4.6 创造自己的异常创造自己的异常 .

public int CalcAnswer() throws NumberRangeException{ int int1, int2; int answer = -1; String str1 = textField1.getText(); String str2 = textField2.getText(); try { int1 = Integer.parseInt(str1); int2 = Integer.parseInt(str2); if ((int1 < 10) || (int1 > 20) || (int2 < 10) || (int2 > 20)) { NumberRangeException e = new NumberRangeException (”Numbers not within the specified range."); throw e; } answer = int1 + int2; }catch (NumberFormatException e) { answerStr = e.toString(); } return answer;}

上海交通大学计算机科学与工程系23/4/19 76

例 2 :在定义银行类时 ,若取钱数大于余额则作为异常处理(InsufficientFundsException). 思路 : 产生异常的条件是余额少于取额 , 因此是否抛出异常要判断条件

取钱是withdrawal方法中定义的动作 ,因此在该方法中产生异常 .

处理异常安排在调用withdrawal的时候 ,因此withdrawal方法要声明异常 ,由上级方法调用

要定义好自己的异常类

上海交通大学计算机科学与工程系23/4/19 77

4.6 4.6 创造自己的异常创造自己的异常 .

class Bank{ double balance; public void deposite(double dAmount) { if(dAmount>0.0) {balance+=dAmount;}}

public void withdrawal(double dAmount) throws InsufficientFundsException { if (balance<dAmout) { throw new InsufficientFundsException(this,dAmount); } balance=balance-dAmount; } public void show_balance() { System.out.println("The balance is "+(int)balance);}}

上海交通大学计算机科学与工程系23/4/19 78

public class ExceptionDemo

{ public static void main(String args[])

{ try

{ Bank ba=new Bank(50);

ba.withdrawal(100);

System.out.println(“Withdrawal successful!”);

}catch(Exception e)

{ System.out.println(e.toString()); }

}

上海交通大学计算机科学与工程系23/4/19 79

public class InsufficientFundsException extends Exception{ private Bank excepbank; private double excepAmount; InsufficientFundsException(Bank ba, double dAmount) { excepbank=ba; excepAmount=dAmount; } public String excepMesagge() { String str=“The balance”+ excepbank.showBalance()+ “The withdrawal was”+excepAmount; return str; }

上海交通大学计算机科学与工程系23/4/19 80

7. 7. 小结小结

1.一般格式 : 正常程序和出错处理分离开来try { Java statement;}catche(ExceptionType1 ExceptionObject) { Exception1 handling;} catche(ExceptionType2 ExceptionObject) { Exception2 handling;}….}finally { final handling; // ( 统一的出口 ,最终必定要执行 )}}

上海交通大学计算机科学与工程系23/4/19 81

把异常传播给堆栈 ,沿着被调用的顺序往前寻找 ,只要找到符合该异常种类彻底异常处理程序 ,就交给这部分程序去处理

Method1 Method2 Method3 Read-filecall call call

Try catch 产生异常

throwsthrowsthrows

上海交通大学计算机科学与工程系23/4/19 82

3.异常可以人为地抛出 , 用 throw new 语句4.异常可以是系统已经定义好的 , 也可以是用户自己定义的

5.用户自己定义的异常一定继承自 Throwable或 Exception类