1. 程式人生 > >Java程式設計思想(3)

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