20145215《Java程序设计》第5周学习总结

20145215《Java程序设计》第五周学习总结

教材学习内容总结

异常处理

语法与继承架构

异常就是程序在运行时出现不正常情况,异常的由来是因为Java把出现的问题封装成了对象,换句话说Java中的错误也是以对象的方式呈现为java.lang.Throwable的各种子类实例,那么对于我们来说,当然是希望能解决程序的异常,因此在Java中也提供了特有的处理异常的语句。

  • 使用try、catch: Java中所有错误都会被包装成对象,可以尝试(try)执行程序并捕捉(catch)代表错误的对象后做一些处理。使用了try、catch语法,JVM会尝试执行try区块中的程序代码,如果发生错误,执行程序会跳离错误发生点,然后比对catch括号中声明的类型,是否符合被抛出的错误对象类型,如果是就执行catch中的程序代码。try、catch的基本用法如下:
try
{
	需要被检测的代码;
}
catch(异常类 变量)
{
	处理异常的代码(处理方式);
}
  • 异常继承架构:错误会被包装为对象,这些对象均可抛出,因此设计错误对象都继承自java.lang.Throwable类,Throwable定义了取得错误信息、堆栈追踪(Stack Trace)等方法,它有两个子类:java.lang.Error与java.lang.Exception。在此简述一下Error类与Exception类的区别,在Java中对于比较严重的问题,是通过Error类来进行描述的,而对于非严重的问题,则是通过Exception类来进行描述的。对于Error,一般不编写针对性的代码对其进行处理,因为此时已经超出了JVM的运行能力范围之外了,例如下面这串简单的代码:
public class ErrorTest {
    public static void main(String[] args) {
        byte[] arr=new byte[1024*1024*600];
    }
}

便会弹出Error:

这是因为这个程序已经超出了JVM的内存空间,自然就会报出Error了,因此对于Error对象抛出时,基本上不用处理,任其传播至JVM为止。而对于Exception,我们可以编写针对性的代码对其进行处理,所以我们通常也称错误处理为异常处理。

  • 单就语法与继承架构上来说,如果某个方法声明会抛出Throwable或子类实例,只要不是属于Error或java.lang.RuntimeException或其子类实例,就必须明确使用try、catch语法加以处理,或者在方法中用throws声明这个方法会抛出异常,否则会编译失败,throws的使用也提高了代码的安全性。
  • Exception中有一个特殊的子类异常叫RuntimeException异常,就是运行时异常,它的特点是如果在函数内容抛出该异常,函数上可以不用声明,编译一样通过,如果在函数上声明了该异常,调用者可以不用进行处理,编译一样通过。Exception或其子对象,但非属于RuntimeException或其子类对象,称为受检异常(Checked Exception),属于RuntimeException衍生出来的类实例亦称为非受检异常(Unchecked Exception),受检异常存在的目的,在于API设计者实现某方法时,某些条件成立时会引发错误,而且认为调用方法的客户端有能力处理错误,要求编译程序提醒客户端必须明确处理错误,不然不可通过编译,API客户端无权选择要不要处理。
  • 如果父类异常对象在子类异常对象前被捕捉,则catch子类异常对象将永远不会被执行,编译程序会检查出这个错误。从JDK7开始,可以使用多重捕捉语法(详情见教材234页),不过仍需注意异常的继承,catch括号中列出的异常不得有继承关系,否则会发生编译错误。
  • 在catch区块进行完部分错误处理之后,可以使用throw将异常再度抛出,这里将throw和throws要区分开,throws使用在函数上,后面跟的是异常类,可以跟多个,用逗号隔开,而throw是使用在函数内,后面跟的是异常对象。
  • 若想得知异常发生的根源,以及多重方法调用下异常的堆栈传播,可以利用异常对象自动收集的堆栈追踪来取得相关信息,例如调用异常对象的printStackTrace()getStackTrace()等方法。要善于堆栈追踪,前提是程序代码中不可有私吞异常的行为、对异常做了不适当的处理,或显示了不正确信息。在使用throw重抛异常时,异常的追踪堆栈起点,仍是异常的发生根源,而不是重抛异常的地方。如果想要让异常堆栈起点为重抛异常的地方,可以使用fillInStackTrace(),这个方法会重新装填异常堆栈,将起点设为重抛异常的地方,并返回Throwable对象。

异常与资源管理

  • finally区块:finally代码块定义一定会执行的代码,它通常用于关闭资源。对于异常的部分,如果没有做finally处理,那么这个程序是有缺陷的,每次调用完资源再把资源释放掉是必须的,否则对对方的运行压力会特别大,由此也可以看出finally的重要性。finally经常会与try、catch语法一起用,它的基本用法如下:
try
{
	需要检测的代码;
}
catch(异常类 变量)
{
	异常处理代码;
}
finally
{
	一定会执行的代码;
}

如果程序撰写的流程中先return了,也有finally区块,finally区块会先执行完后,再将值返回,例如下面的例子:

public class FinallyDemo {
    public static void main(String[] args) {
        System.out.println(test(true));
    }

    static int test(boolean flag) {
        try {
            if (flag) {
                return 1;
            }
        } finally {
            System.out.println("finally…");
        }
        return 0;
    }
}

它会先显示finally…再显示1。Finally代码块只有一种情况不会被执行,就是在之前执行了System.exit(0),System.exit(0)是将你的整个虚拟机里的内容都停掉了,也就是退出了JVM,括号里面的0表示的是正常退出程序,如果是1则表示非正常退出。

  • 想要尝试关闭资源(Try-With-Resources)的对象,是撰写在try之后的括号中,如果无须catch处理任何异常,可以不用撰写,也不要撰写finally。尝试关闭资源语法可套用的对象,必须操作java.lang.AutoCloseable接口,该语法也可以同时关闭两个以上的对象资源,只要中间以分号隔开。在try的括号中,越后面撰写的对象资源会越早被关闭。

Collection与Map

使用Collection收集对象

  • 收集对象的行为,像是新增对象的add()方法、移除对象的remove()方法等,都是定义在java.util.Collection中。既然可以收集对象,也要能逐一取得对象,这就是java.lang.Iterable定义的行为,它定义了iterable()方法返回java.util.Iterator操作对象,可以让你逐一取得收集的对象。Collection接口中有三个子接口,分别是List、Set和Queue。如果希望收集时记录记录每个对象的索引顺序,并可依索引取回对象,可以使用java.util.List接口,如果希望收集的对象不重复,具有集合的行为,可以使用java.util.Set接口,如果希望收集对象时以队列方式,收集的对象假如至尾端,取得对象时从前端,则可以使用java.util.Queue接口,如果希望对Queue的两端进行加入、移除等操作,则可以使用java.util.Deque

  • List接口中常用的类有:ArrayList和LinkedList,ArrayList的特点是线程不安全,但查询速度快,像排序时使用ArrayList便是利用了它速度快这一特点。LinkedList在操作List接口时,采用了链接结构,不会事先耗费内存,它的增删速度比较快。
  • 不论是List、Set或者是Queue,都会有个iterator()方法,增强式for循环可运用在数组上,还可运用在操作Iterable接口的对象上,例如在foreach()方法中,用增强式for循环更加简化:
public class ForEach {
    public static void main(String[] args) {
        List names = Arrays.asList("Justin", "Monica", "Irene");
        forEach(names);
        forEach(new HashSet(names));
        forEach(new ArrayDeque(names));
    }
    static void forEach(Iterable iterable) {
        for (Object o : iterable) {
            System.out.println(o);
        }
    }
}
  • 在Java中,跟顺序有关的行为,通常要不对象本身是Comparable,要不就是另行指定Comparator对象告知如何排序。JDK8在List上增加了sort()方法,可接受Comparator实例来指定排序方式。

键值对应的Map

  • 可以事先利用java.util.Map接口的操作对象来建立键值对应数据,之后若要取得值,只要用对应的键就可以迅速取得。判断键是否重复是根据hashcode()与equals(),所以作为键的对象必须操作hashcode()与equals()。常用Map操作类有HashMap、TreeMap和Properties。HashMap的特点是线程不安全,速度快,允许存放null 键,null值,TreeMap会对键进行排序,条件是作为键的对象必须操作Comparable接口,或者是在创建TreeMap时指定操作Comparable接口的对象,Properties的setProperty()可以指定字符串类型的键值,getProperty()可以指定字符串类型的键,取回字符串类型的值,通常称为属性名称与属性值。

  • 如果想取得Map所有键,可以调用Map的keySet()返回Set对象,如果想取得Map中所有的值,则可以使用values()返回Collection对象。如果想同时取得Map的键与值,可以使用entrySet()方法,这回返回一个Set对象,每个元素都是Map.Entry实例,可以调用getKey()取得键,调用getvalue()取得值。

教材学习中的问题和解决过程

本周的学习发现了教材中的两个错误:

  • 教材269页的Students.java原代码如下:
class Student {
    private String name;
    private String number;

    Student(String name, String number) {
        this.name = name;
        this.number = number;
    }

    @Override
    public String toString() {
        return String.format("(%s,%s)", name, number);
    }
}
public class Students {
    public static void main(String[] args) {
        Set students = new HashSet();
        students.add(new Student("Justin", "B835031"));
        students.add(new Student("Monica", "B835032"));
        students.add(new Student("Justin", "B835031"));
        System.out.println(set);
    }
}

编译时出现错误:

仔细一想,这里根本就没有set这个关键字,怎么可能不报错呢,也不可能是Set,因为Set是一个类,不可能用println,所以println括号里面应该跟的是students,这样运行结果就正确了:

  • 教材上266页SimpleLinkedList.java编译时弹出如下错误:

一开始我也没注意到为什么用elem会报错,后来看到了蔡野的博客(http://www.cnblogs.com/20145208cy/p/5343281.html),才发现原来代码中前面用的是Object o,后面却用了elem,程序自然也就报错了。所以,这也侧面反映出了多看看别人博客的好处,大家可以一起讨论,共同进步。

  • 关于finally块中的代码是否一定会被执行的问题,在进过多次验证之后,我发现finally块中的代码一定会被执行,后来查阅网上的资料,发现只有一种情况下finally块不会被执行,那就是使用System.exit(0),这相当于直接关掉了JVM,并没有太大意义,出此之外就没有其他方法了。
  • 对于迭代,教材中的描述不是很清楚,在查阅资料后,我对迭代有了更深的印象。迭代是取出集合中元素的一种方式,因为Collection中有iterator方法,所以每一个子类集合对象都具备迭代器,用法如下:
for(Iterator iter = iterator();iter.hasNext(); )
{
	System.out.println(iter.next());
}

迭代器的next方法是自动向下取元素,要避免出现NoSuchElementException,迭代器的next方法返回值类型是Object,所以要记得类型转换。

代码调试中的问题和解决过程

以下是我自己编的一段测试代码:

class FinallyTest{
    public static void main(String args[]) {
        try{
            int x=0;
            int y=20;
            int z=y/x;
            System.out.println("y/x 的值是 :"+z);
        }catch(ArithmeticException e){
            System.out.println(" 捕获到算术异常 : "+e);
        }
        finally{
            System.out.println(" 执行到 finally 块内!");
            try{
                String name;
                if(name.equals(" LXM ")){
                    System.out.println(" 你的名字叫LXM!");
                }
            }
            catch(Exception e){
                System.out.println(" 又捕获到另一个异常 : "+e);
            }
            finally{
                System.out.println(" 执行到内层的 finally 块内!");
            }
        }
    }
}

编译时弹出了下面的错误:

一开始我以为定义name时后面什么都不加,系统会自动对它进行初始化,就和C语言一样,后来想起来在Java中是对对象进行操作,所以必须要绑定对象,于是我在String name后面加了一个null,代码成功按照我预想的进行:

心得体会

经过这五周的学习,可以说,我们已经基本学完了Java的基础知识,但是学完并不代表掌握了,如果现在让我们独立去编写一个程序,我觉得我们可能依旧会错误百出。但是在我看来,真正的知识就是在实践的基础上获得的,我们之前的学习,包括敲书上的代码,只是为了让我们的脑子里对这些概念有个了解,真正想要掌握这些知识,只能是通过自己独立编写程序来获得。可能第一次编的时候会出现许多的错误,就像我一样,但凡事都有第一次,这次出现的错误,当你思考分析出问题所在之后,下次编的时候就能够避免,经验也是这样通过一点一滴累积起来的。相反,如果只是一味的照着书上的代码敲上去,我觉得我们可能很难有什么突破,当然,我不是说书上的代码毫无意义,我们在看书上代码的同时应该更多地去独立编写一些代码,这样对一些知识点也会理解的更加透彻。总而言之,多动手只会有益无害,学习Java是这样,学习其他的课程也是一样。以上便是我本周学习的一些感悟,也希望对大家能够起到一定的帮助!

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 5000行 30篇 400小时 编写了Hello Java代码
第一周 100/100 2/2 12/12 编写了Hello Java代码
第二周 200/300 2/4 15/27 理解了printf和println的区别
第三周 450/750 1/5 22/49 对对象有了更深层次的理解
第四周 869/1619 1/6 28/77 对对象的三大特征有了更全面的认识
第五周 1123/2742 1/7 25/102 学会了异常处理

【附1】本周学习的代码已经成功托管,截图如下:

【附2】利用wc统计代码行数,截图如下:

参考资料

posted @ 2016-04-02 23:49  L.X.M  阅读(347)  评论(6编辑  收藏  举报