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

猜你喜欢

转载自blog.csdn.net/haima95/article/details/82826083