渣渣小本求职复习之路每天一博客系列——Java基础(5)

———————————————————————时间紧,不闲聊————————————————————————

第七章:异常处理

  第三节:认识堆栈追踪

  在写程序的时候,经常会有多重方法调用,那么我们是如何知道异常发生点在某个特定的方法之中呢?如果我们想知道异常发生的根源以及多重方法调用下异常的堆栈传播,可以利用异常对象自动收集的堆栈追踪(Stack Trace)来取得相关信息。

  查看堆栈追踪最简单的方法,就是直接调用异常对象的printStackTrace()方法。相信这个方法大家都比较有印象,所以这里就不贴出代码示范了。printStackTrace()还有接收PrintStream、PrintWriter的版本,做什么用呢?就是可以将堆栈追踪信息以指定方式输出到指定地点(例如log文件)。如果想要取得个别的堆栈追踪元素进行处理,则可以使用getStackTrace(),这会返回一个StackTraceElement数组。数组中索引为0的是异常根源的相关信息,之后为各方法调用中的信息,就可以使用StackTraceElement的getClassName()、getFileName()、getLineNumber()、getMethodName()等方法取得对应的信息。

  下面给大家展示几种坑自己不止还坑队友的异常处理行为:

NO.1:捕捉了异常啥都不干,让别人出了什么错都不知道

1 try{
2     ...
3 }catch(SomeException ex){
4      //我就不处理,你能拿我怎么样?
5 }

NO.2:胡说八道,乱提示错误

1  try{
2      ...
3  }catch(SomeException ex){
4        System.out.println("找不到文档");
5  }

  异常处理陋习还有很多,这里就不一一列举了。提供一个链接,大家有空看看哈。http://blog.jobbole.com/30230/#jtss-tsina

  第四节:异常与资源管理

  程序运行的时候,常常会因为错误而抛出异常,原来的执行流程就会中断,之后的代码就不会再被执行了。如果程序申请占用了某些资源,那么我们的设计是否还能正确地关闭资源呢?最后要执行资源的关闭,这是一定要保证的。所以我们可以使用try-catch语句搭配finally的方法,进行关闭资源。

 1 package cc.openhome;
 2 
 3 import java.io.FileInputStream;
 4 import java.io.FileNotFoundException;
 5 import java.util.Scanner;
 6 
 7 public class FileUtil {
 8     public static String readFile(String name) throws FileNotFoundException {
 9         StringBuilder builder = new StringBuilder();
10         Scanner scanner = null;
11         try {
12             scanner = new Scanner(new FileInputStream(name));
13             while (scanner.hasNext()) {
14                 builder.append(scanner.nextLine());
15                 builder.append('\n');
16             }
17         } finally {
18             if(scanner != null) {
19                 scanner.close();
20             }
21         }
22         return builder.toString();
23     }
24 }

  使用finally,无论try语句块中是否发生异常,finally语句块就一定会被执行。另外,就算程序撰写的流程中先有return语句,finally语句块也会先执行完,再将值返回。

 1 package cc.openhome;
 2 public class Main{
 3       public static int test(boolean flag){
 4            try{
 5                if(flag){
 6                     return 1;
 7                }
 8             }finally{
 9                  System.out.println("finally...");
10             }
11            return 0;
12 }
13 
14       public static void main(String[] args){
15             System.out.println(test(true));
16       }
17 } 

  第八章:Collection 

  程序设计中,我们常常会有收集对象的需求。到目前为止,我们复习到可以收集对象的方式就是使用Object数组。在Java SE中,其实就提供了几个收集对象的类,我们可以直接拿过来用。不过在用之前,我们必须对它们有一个足够深入的了解。

  第一节:认识Collection的架构

  在Java中,有各种各样的API。在使用这些API之前,我们可以先了解它们的继承与接口操作架构,才能明白到什么时候用哪个类,以及类与类之间是怎么配合使用的。这样,就不会陷入只是死记硬背API或者是Copy例子的尴尬境地。

  那么,我们先来看看Collection的继承架构。

  收集对象的行为,例如新增对象的add()方法,移除对象的remove()方法等,都是定义在java.util.Collection里面。这些共同行为定义在Collection中,然而收集对象会有不同的需求。如果希望收集的时候,记录下每个对象的索引顺序,并且可以做到根据索引读取对象,那么这样的行为定义在java.util.List接口中。如果希望收集到的对象没有重复的现象,具有集合的特征和行为,那么就由java.util.Set定义。如果希望收集对象时可以以队列的形式,收集的对象从尾端加入,获取的对象从首端开始,在可以使用java.util.Queue。如果希望可以对Queue的两段进行add、remove等操作,则可以考虑用java.util.Deque。

  根据继承架构图这种表示方式,可以更清楚地看出哪些类实现了哪些接口、继承了哪个类,或者哪些接口又继承自哪个接口。不过更详细的继承与实现架构,建议大家还是通过API说明文档进行了解,自行画出更符合自己理解需求的架构图。

  第二节:有索引的List

  List是一种Collection,具有收集对象的功能,并以索引的方式保存收集对象的顺序。查看API文档,我们可以发现List接口定义了add()、remove()、set()等根据索引进行操作的方法。

  而List下有两种比较出名的类,java.util.ArrayList以及java.util.LinkedList,那么我们来看看它们的特点和区别在哪里。

  对于ArrayList,内部就是使用Object数组来保存所收集的对象,因此我们在考虑是否使用ArrayList的时候,就等于是考虑是否要用到数组的特性。数组在内存中占用的是连续的线性空间,所以根据索引随机存取时速度非常快,如果需要排序等,就优先考虑ArrayList。有利必有弊,数组在需要插入或删除操作的时候,则会付出较大的代价,具体原因参照数据结构与算法系列文章的第二篇。

  对于LinkedList,学过C++或C的童鞋大概都能联想到链表,那么这种链接(Link)结构是如何在Java中实现的,我们先来看一个范例:

 1 package cc.openhome;
 2 
 3 public class SimpleLinkedList {
 4     private class Node {//定义节点
 5         Node(Object o) {
 6             this.o = o;
 7         }
 8         Object o;
 9         Node next;//存储下一个节点
10     }
11     
12     private Node first;//第一个节点
13 
14     public void add(Object o) {
15         if(first == null) {
16             first = new Node(o);
17         }
18         else {
19             Node last = first;
20             while(last.next != null) {
21                 last = last.next;
22             }
23             last.next = new Node(o);
24         }
25     }
26     
27     public int size() {
28         int count = 0;
29         Node last = first;
30         while(last.next != null) {
31             last = last.next;
32             count++;
33         }
34         return count;
35     }
36     
37     public Object get(int index) {
38         int size = size();
39         if(index >= size) {
40             throw new IndexOutOfBoundsException(
41                     String.format("Index: %d, Size: %d", index, size));
42         }
43         int count = 0;
44         Node last = first;
45         while(count < index) {
46             last = last.next;
47             count++;
48         }
49         return last.o;
50     }
51 }

  根据这个范例,结合之前对链表的认识,我们大概可以得出这么一个结论:LinkedList在插入删除会比较占优,但是像排序就很不适合使用Link结构。因此,若收集的对象经常会有变动索引的情况,也许考虑LinkedList会更好一些。

  在笔试面试题中,常常会问到ArrayList、LinkedList和Vector三者的区别是什么?结合刚才的内容,再加上一句话就可以了——Vector与ArrayList相似,但前者是线程安全的而后者不是。

  第三节:不重复收集对象的Set

  Set,也就是集合的意思。我们在初高中的时候,就已经学过集合的概念。同样是收集对象,在过程中如果有相同的对象出现,则不再重复收集,这就是实现Set接口的类的特点。我们可以通过下面一个范例来了解一下到底是怎么一回事(若有一个字符串,当中有许多的英文单词,写一个程序输出不重复的单词的个数,以及分别是哪些单词)

 

 1 package cc.openhome;
 2 
 3 import java.util.*;
 4 
 5 public class Words {
 6     public static void main(String[] args) {
 7         Set words = new HashSet();
 8         Scanner scanner = new Scanner(System.in);
 9         System.out.print("请输入英文:");
10         String line = scanner.nextLine();
11         String[] tokens = line.split(" ");//根据空格分割字符串
12         for(String token : tokens) {//使用HashSet收集字符串
13             words.add(token);
14         }
15         System.out.printf("不重复单子有 %d 个:%s%n", //输出结果
16                 words.size(), words);
17     }
18 }

 

  本来书上还有提到如何使用对象的hashCode()与equals()来判断对象是否相同的原理,但暂时没看懂,先记着,以后补上。

  第四节:支持队列操作的Queue

  如果希望收集对象的时候是以队列的方式,收集的对象加入至队尾,取得对象时从队首,则可以使用Queue接口的实现类进行操作。Queue接口继承自Collection,所以也具有Collection的add()、remove()、element()等方法,而且也定义了自己的offer()、poll()与peek()等方法,最主要的差别之一就是,前面三个方法运行失败时会抛出异常,而后三个方法则是返回特定的值。

  offer()方法用来在队尾加入对象,成功后会返回true,失败则会返回false。poll()方法用来取出队首的对象,若队列为空则返回null。peek()用来取得(但不取出)队首对象,若队列为空则返回null。

  前面提到过LinkedList,它不仅实现了List接口,也实现了Queue接口,所以我们可以将LinkedList当做队列来用。

 1 package cc.openhome;
 2 
 3 import java.util.*;
 4 
 5 interface Request {
 6     void execute();
 7 }
 8 
 9 public class ReuqestQueue {
10 
11     public static void main(String[] args) {
12         Queue requests = new LinkedList();
13         // 模拟将请求加入行列
14         for (int i = 1; i < 6; i++) {
15             requests.offer(new Request() {
16                 public void execute() {
17                     System.out.printf("处理数据 %f%n", Math.random());
18                 }
19             });
20         }
21         process(requests);
22     }
23     
24     private static void process(Queue requests) {
25         while(requests.peek() != null) {
26             Request request = (Request) requests.poll();
27             request.execute();
28         }
29     }
30 }

  今天的篇幅已经很长了,所以就先到这里。下一篇再接着讲Collection,还会有Map的内容哟,敬请期待。

 

————————————————————————第19天——————————————————————————

  最近状态越来越好了。

1.不过最近的事也多了起来,毕业设计的题目已经下来了,指导老师和师兄的任务都开始压了下来;上个学期参加的一个学校项目也已经不能再拖了;博客还是要一天一篇写下来。

2.不怕累,不怕忙,就怕迷茫不知所措。不知道大家会不会也有这样的感受。

3.我会加油的,马上21天了,30天的约定也很快会完成。

  

 

 

 

 

posted @ 2013-11-06 23:44  LevenYes  阅读(320)  评论(0编辑  收藏  举报