Java程式設計思想(3)
第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");
}
}
}