1 / 82

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

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

Download Presentation

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

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


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

  2. 一、Java容器

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

  4. 1. 对象保持 • 在程序中,经常需要动态产生一些对象 • 你无法知道要产生多少对象 • 你无法事先知道这些对象的类型 • 对象持有方式 • 内置的数组方式 • 容器类

  5. 2. 再论数组(Arrays) • 提供了存储及随机访问一系列对象的最有效率的方法 • 由于线性存储,所以访问效率高 • 容量固定 • 提供边界检查 • 定义数组时,需要定义特定类型 • 概念 • array object是本身对象 • objects array”由对象形成的数组” • primitive array”基本型别元素构成的数组”

  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); }

  7. 返回数组 publicstatic String[] flavorSet(int n) { if(n > FLAVORS.length) thrownew IllegalArgumentException("Set too big"); String[] results = new String[n]; boolean[] picked = newboolean[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; } publicstaticvoid main(String[] args) { for(int i = 0; i < 7; i++) System.out.println(Arrays.toString(flavorSet(3))); }} //: arrays/IceCream.java // Returning arrays from methods. import java.util.*; publicclass IceCream { privatestatic Random rand = new Random(47); staticfinal String[] FLAVORS = { "Chocolate", "Strawberry", "Vanilla Fudge Swirl", "Mint Chip", "Mocha Almond Fudge", "Rum Raisin", "Praline Cream", "Mud Pie" };

  8. 数组的几个功能 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)); } } • 充填 //: 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];

  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);

  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)); } } • 比较

  11. 元素的比较 • 方法1:实现java.lang.Comparable interface,中的函数compareTo() • 方法2:撰写一个class,令它实现Comparator interface, 它包含两个函数:compare()和equals() Strategy 模式:将变化的部分与不变的部分分离,变化的部分放在单独的类(Strategy对象)中

  12. 排序 • 使用内置的sorting函数,你可以针对任何primitives array进行排序,也可以针对任何objects array进行排序(只要那些对象实现了Comparable或者拥有相关之Comparator)

  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; publicinterface Generator { Object next(); } ///:~

  14. 3. 容器概述 • 当你撰写程序时不知道究竟需要多少对象时,使用容器 • 两种类型的容器 • Collection:一组各自独立的元素,包括List, Set • Map: 成对的key-value对象

  15. 3.1 容器放入对象 • ArrayList不进行类型检查 importjava.util.*; class Apple { privatestaticlongcounter; privatefinallong id = counter++; publiclong id() { return id; } } class Orange {} publicclassApplesAndOrangesWithoutGenerics { @SuppressWarnings("unchecked") publicstaticvoid main(String[] args) { ArrayList apples = newArrayList(); for(inti = 0; i < 3; i++) apples.add(new Apple()); // Not prevented from adding an Orange to apples: apples.add(new Orange()); for(inti = 0; i < apples.size(); i++) ((Apple)apples.get(i)).id(); // Orange is detected only at run time } } /* (Execute to see output) *///:~

  16. //: holding/ApplesAndOrangesWithGenerics.java import java.util.*; publicclass ApplesAndOrangesWithGenerics { publicstaticvoid 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()); } }

  17. 3.2容器打印

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

  19. 3.4迭代器(Iterators) • 容器需要提供一些公共的功能 • 取元素 • 删除元素 • 查询元素 • 提供一个公共对象,执行基本功能,从而可以不管类型:Iterator

  20. Java中的Iterator功能比较简单,并且只能单向移动:Java中的Iterator功能比较简单,并且只能单向移动: • 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。 • 使用next()获得序列中的下一个元素。 • 使用hasNext()检查序列中是否还有元素。 • 使用remove()将迭代器新返回的元素删除。 • Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。

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

  22. 4.1 Iterator与ListIterator • ListIterator有add()方法,可以向List中添加对象,而Iterator不能 • ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator就不可以。 • ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能。 • 都可实现删除对象,但是ListIterator可以实现对象的修改,set()方法可以实现。Iierator仅能遍历,不能修改。

  23. 4.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) 可选:防止在设计中出现接口爆炸的情况,允许特例下,那些方法没有实现,但是绝大多数情形下是实现的 只保留相交的元素

  24. 4.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可以当作堆栈、队列和双向队列使用。

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

  26. 4.4 Map • 如果需要依据某些条件来在一串对象中进行选择 • Map 提供了一个更通用的元素存储方法。 Map 集合类用于存储元素对(称作“键”和“值”),其中每个键映射到一个值。

  27. 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

  28. 查看 Map • 迭代 Map 中的元素不存在直接了当的方法。 如果要查询某个 Map 以了解其哪些元素满足特定查询,或如果要迭代其所有元素(无论原因如何),则您首先需要获取该 Map 的“视图”。 有三种可能的视图 • entrySet():返回 Map 中所包含映射的 Set 视图。 Set 中的每个元素都是一个 Map.Entry 对象,可以使用 getKey() 和 getValue() 方法(还有一个 setValue() 方法)访问后者的键元素和值元素 • keySet():返回 Map 中所包含键的 Set 视图。 删除 Set 中的元素还将删除 Map 中相应的映射(键和值) • values():返回 map 中所包含值的 Collection 视图。 删除 Collection 中的元素还将删除 Map 中相应的映射(键和值)

  29. 访问元素:Map 通常适合按键(而非按值)进行访问,这些方法检索有关 Map 内容的信息但不更改 Map 内容。 • get(Object key) 返回与指定键关联的值 • containsKey(Object key)如果 Map 包含指定键的映射,则返回 true • containsValue(Object value)如果此 Map 将一个或多个键映射到指定值,则返回 true • isEmpty()如果 Map 不包含键-值映射,则返回 true • size()返回 Map 中的键-值映射的数目

  30. Map的类别

  31. HashMap的使用

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

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

  34. 在引入引用类之前,我们只能使用强引用(strong reference)。举例来说,下面一行代码显示的就是强引用 obj : • Object obj = new Object(); obj 这个引用将引用堆中存储的一个对象。只要 obj 引用还存在,垃圾收集器就永远不会释放用来容纳该对象的存储空间。 • 当 obj 超出范围或被显式地指定为 null 时,垃圾收集器就认为没有对这个对象的其它引用,也就可以收集它了。 • 强引用是使用最普遍的引用。如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空 间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题

  35. 软引用(Soft Reference) • 如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。 • 只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。 • 软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

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

  37. 虚引用(PhantomReference) • “虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。 • 虚引用主要用来跟踪对象被垃圾回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃 圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是 否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

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

  39. 小结 • 1、容器类和Array的区别、择取 * 容器类仅能持有对象引用(指向对象的指针),而不是将对象信息copy一份至数列某位置。 * 一旦将对象置入容器内,便损失了该对象的型别信息。 • 2、 * 在各种Lists中,最好的做法是以ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList();Vector总是比ArrayList慢,所以要尽量避免使用。 * 在各种Sets中,HashSet通常优于HashTree(插入、查找)。只有当需要产生一个经过排序的序列,才用TreeSet。HashTree存在的唯一理由:能够维护其内元素的排序状态。 * 在各种Maps中HashMap用于快速查找。 * 当元素个数固定,用Array,因为Array效率是最高的。 • 结论:最常用的是ArrayList,HashSet,HashMap,Array。

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

  41. 二、异常处理

  42. 内容 • 异常的概念 • 异常的分类 • 捕获异常 • 声明异常 • 抛出异常 • 创造自己的异常

  43. 1 异常的概念 • 什么是异常? 异常实际上是程序中错误导致中断了正常的指令流的一种事件. • 没有处理错误的程序: read-file { openTheFile; determine its size; allocate that much memory; closeTheFile; }

  44. 以常规方法处理错误 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;

  45. 观察前面的程序你会发现大部分精力花在出错处理上了.观察前面的程序你会发现大部分精力花在出错处理上了. • 只把能够想到的错误考虑到,对以外的情况无法处理 • 程序可读性差 • 出错返回信息量太少

  46. 用异常的形式处理错误 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; } }

More Related