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

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

教材学习内容总结

异常

程序中总有些意想不到的状况所引发的错误,如果不对异常进行正确的处理,则可能导致程序的中断执行,造成不必要的损失,

所以在程序的设计中必须要考虑各种异常的发生,并正确的做好相应的处理,这样才能保证程序正常的执行。

使用try、catch

  • java中所有的错误都会被打包为对象,并提供了特有的语句进行处理。使用了try、catch语法,JVM会尝试执行try区块中的程序代码,如果发生错误,执行流程会跳离错误发生点,然后对比catch括号中声明的类型,是否符合被抛出的错误对象类型,如果是的话,就执行catch区块中的程序代码。
  • 代码如下
import java.util.*;
public class Average2{
	public static void main(String[] args){
		try{
			Scanner console=new Scanner(System.in);
		double sum=0;
		int count=0;
		while(true){
			int number=console.nextInt();
			if(number==0){
				break;
			}
			sum+=number;
			count++;
		}
		System.out.printf("average %.2f%n",sum/count);
		}catch (InputMismatchException ex){
			System.out.printf("you must input an integer");
		}
		
	}
}
  • 运行结果如下

异常类的继承结构

在整个java的异常结构中,实际上有以下两个最常用的类:Exception、Error,这两个类全都是Throwable的子类
Exception:一般表示的是程序中出现的问题,可以直接使用try...catch处理。
Error:一般指的是JVM错误,程序中无法处理。
在先前的Average范例中,没用到try、catch语句,照样可以编译执行。为什么在书上230页的范例会编译错误?要解决这个错误信息有两种方式,一是使用try、catch打包System.in.read(),二是在main()方法旁声明throws java.io.IOException。

  • 代码如下
import java.util.Scanner;
public class Average4{
	public static void main(String[] args){
		double sum=0;
		int count=0;
		while(true){
			int number=console.nextInt();
			if(number==0){
			break;
			}
			sum+=number;
			count++;
		}
		System.out.printf("average %.2f%n",sum/count);
		}
		static Scanner console=new Scanner(System.in);
		static int nextInt(){
			String input=console.next();
		while(!input.matches("\\d*")){
			System.out.println("please input a number");
			input=console.next();
		}
		return Integer.parseInt(input);
		}
}
  • 运行结果如下

要抓还是要抛

如果方法设计流程中发生异常,而你设计时并没有充足的信息知道该如何处理那么可以抛出异常,让调用方法的客户端来处理。操作对象的过程中如果会抛出受检异常,但目前环境信息不足以处理异常,无法使用try、catch处理时,可由方法的客户端依据当时调用的环境信息进行处理。在catch区块进行完部分错误处理后,可以使用throw将异常再抛出。可以在任何流程中抛出异常,不一定要在catch区块中。
如果使用继承时,父类某个方法声明throws某些异常,子类重新定义该方法时可以:1.不声明throws任何异常。 2.throws父类该方法中声明的某些异常。 3.throws父类该方法中声明异常的子类。4.throws父类方法中未声明的其他异常。 5.throws父类方法中声明异常的父类

  • 代码如下
import java.io.*;
import java.util.Scanner;
public class FileUtil{
    public static String readFile(String name)throws FileNotFoundException{
        StringBuilder text=new StringBuilder();
        try{
            Scanner console=new Scanner(new FileInputStream(name));
            while(console.hasNext()){
                text.append(console.nextLine())
                .append('\n');
            }
        }catch(FileNotFoundException ex){
            ex.printStackTrace();
            throw ex;
        }
        return text.toString();
    }
}

贴心还是造成麻烦

  • 异常处理的本意:在程序错误发生时能有有明确的方式通知API客户端。
  • java是唯一采用受检异常的语言。这有两个目的:一是文件化,受检异常声明会是API操作接口的一部分,客户端只要查阅文件,就可以知道方法可能会引发哪些异常。二是提供编译程序信息,让编译程序能够在编译时期就检查出API客户端没有处理异常。
  • 受检异常本意良好,有助于程序设计人员注意到异常的可能性并加以处理,但在应用程序规模扩大时,会逐渐对维护造成困难。

认识堆栈追踪

  • 在多重方法调用下,异常发生点可能是在某个方法之中,若想得知异常发生的根源,以及多重方法调用下异常的堆栈传播,可以使用堆栈追踪来取得相关信息。
  • 使用方法:直接调用异常对象的printStackTrace()。
  • 堆栈追踪信息中显示了异常类型,最顶层是异常的根源,以下是调用方法的顺序,程序代码行数是对应于当初的程序原始码,如果想要取得个别的堆栈追踪元素进行处理,则可以使用getStackTrace(),在捕捉异常后什么都不做的话或者做了不适当的处理,这种程序代码会对应用程序维护造成严重伤害。在使用throws重抛异常时,异常的追踪堆栈起点,仍是异常的发生根源,而不是重抛异常的地方。如果想要让异常堆栈起点为重抛异常的地方,可以使用fillInStackTrace()方法,这个方法会重新装填异常堆栈,将起点设为重抛异常的地方,并返回Throwable对象。
  • 代码如下
public class StackTraceDemo3{
    public static void main(String[] args){
        try{
            c();
        }catch(NullPointerException ex){
            ex.printStackTrace();
        }
    }
    static void c(){
        try{
            b();
        }catch(NullPointerException ex){
            ex.printStackTrace();
            Throwable t=ex.fillInStackTrace();
            throw (NullPointerException) t;
        }
    }
    static void b(){
        a();
    }
    static String a(){
        String text=null;
        return text.toUpperCase();
    }
}
  • 运行结果如下

关于assert

  • assert的两种使用语法:
    1.assert boolean_expression
    2.assert boolean_expression : detail_expression
  • boolean_expression 若为true则什么事都不会发生,若为false则会发生java.lang.Assertionerror。
  • 关于何时该使用断言?1.断言客户端调用方法前,已经准备好某些前置条件。2.断言客户端调用方法后名具有方法承诺的结果。3.断言对象某个时间点下的状态。4.使用断言取代批注。5.断言程序流程中绝对不会执行到的程序代码部分。

使用finally

  • finally代码块:定义一定执行的代码。
  • 如果创建FileInputStream实例就会开启文档,不使用时,应当调用close()关闭文档。若想要的是无论如何,最后一定要执行关闭动作。
  • try、catch语法还可以搭配finally使用,无论try区块中有无发生异常,若撰写有finally区块,则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;
    }
}
  • 运行结果如下

自动尝试关闭资源

  • 想要尝试自动关闭资源的对象,是撰写在try之后的括号中,如果无须catch处理任何异常,可以不用撰写,也不用撰写finally自行尝试关闭资源。
  • 使用自动尝试关闭资源语法时,也可以搭配catch,并不影响对特定异常的处理,实际上,自动尝试关闭资源语法也仅仅是协助你关闭资源,而不是用于处理异常。

java.lang.AutoCloseable接口

  • JDK7的尝试关闭资源语法可套用的对象,必须操作java.lang.AutoCloseable接口。
  • AutoCloseable是JDK7新增的接口,仅定义了close()方法。只要操作AutoCloseable接口,就可以套用至尝试关闭资源语法。尝试关闭资源语法也可以同时关闭两个以上的对象资源,只要中间以分号间隔。
  • 在try的括号中,越后面撰写的对象资源会越早被关闭。
  • 代码如下
import static java.lang.System.out;

public class AutoClosableDemo2{
    public static void main(String[] args){
        try(ResourceSome some=new ResourceSome();
        ResourceOther other=new ResourceOther()){
            some.doSome();
            other.doOther();
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }
}

class ResourceSome implements AutoClosable{
    void doSome(){
        out.println("做一些事");
    }
    @Override
    public void close() throws Exception{
        out.println("资源Some被关闭");
    }
}

class ResourceOther implements AutoClosable{
    void doOther(){
        out.println("做其他事");
    }
    @Override
    public void close() throws Exception{
        out.println("资源Other被关闭");
    }
}

认识Collection架构

  • 程序中常有收集对象的需求,其中学过的只有使用object数组,在javaSE中其实就提供了数个收集对象的类。
  • 收集对象的行为,像是新增对象的add()方法,移除对象的remove()方法等,都是定义在java.util.Collection中。
  • 既能收集对象,也能逐一取得对象,这就是java.lang.Iterable定义的行为,它定义了iterator()方法返回java.util.Iterator操作对象,可以让你逐一取得对象。然而收集对象会有不同的需求,如果希望收集时记录每个对象的索引顺序,并可依索引取回对象,这样的行为定义在java.util.List接口中。如果希望收集的对象不重复,具有集合的行为,则由java.util.Set定义。如果希望收集对象时,以队列排列。收集的对象加入至尾端,取得对象时从前端,则可以使用java.util.Queue。如果希望对Queue的两端进行加入、移除等动作,则可以使用java.util.Deque。

具有索引的List

  • List是一种Collection,作用是收集对象,并以索引方式保留收集的对象顺序,其操作类之一是java.util.ArrayList。
  • 查看API文件的时候发现,List接口定义了add()、remove()、set()等许多依索引操作的方法。
  • ArrayList适合排序的时候用,可得到较好的速度表现。而LinkedList采用了链接结构,当需要调整索引顺序时,比较适用。
  • ArrayList特性:数组在内存中会是连续的线性空间,根据索引随机存取时速度快。有可指定容量的构造函数。
  • LinkedList特性:若收集的对象经常会有变动索引的情况。

内容不重复的Set

  • 使用Set接口的操作对象:同样是收集对象,在收集过程中若有相同对象,则不再重复收集。
  • String的Split()方法,可以指定切割字符串的方式。一般用hashcode()与equals()来判断对象是否相同。
  • 代码如下
import java.util.*;

class Student2 {
    private String name;
    private String number;

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


    @Override
    public int hashCode()
    {
        int hash = 7;
        hash = 47 * hash + Objects.hashCode(this.name);
        hash = 47 * hash + Objects.hashCode(this.number);
        return hash;
    }

    @Override
    public boolean equals(Objects obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Student2 other = (Student2) obj;
        if (!Objects.equals(this.name, other.name)) {
            return false;
        }
        if (!Objects.equals(this.number, other.number)) {
            return false;
        }
        return true;
    }

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

支持队列操作的Queue

  • Queue继承自Collection,所以也具有Collection的add()、remove()、element()等方法,然而Queue定义了自己的offer()、poll()与peek()等方法。
  • 最主要的差别在于,add()、remove()、element()等方法操作失败时会抛出异常,而offer()、poll()、peek()等方法操作失败时会返回特定值。
  • 代码如下
import java.util.*;
import static java.lang.System.out;

public class Stack
{
    private Deque elems = new ArrayDeque();
    private int capacity ;

    public Stack(int capacity)
    {
        this.capacity = capacity;
    }

    public boolean push(Object elem)
    {
        if(isFull())
        {
            return false;
        }
        return elems.offerLast(elem);
    }

    private boolean ifFull()
    {
        return elems.size() + 1 > capacity;
    }

    public Object pop()
    {
        return elems.pollLast();
    }

    public Object peek()
    {
        return elems.size();
    }

    public static  void main (String[] agrs)
    {
        Stack stack = new Stack(5);
        stack.push("Justin");
        stack.push("Monica");
        stack.push("Irene");
        out.println(stack.pop());
        out.println(stack.pop());
        out.println(stack.pop());
    }
}

使用泛型

  • JDK5之后增加了泛型语法。若接口支持泛型,在操作时也会比较方便,只要声明参考时有指定类型,那么创建对象时就不用再写类型了,泛型也可以仅定义在方法上,最常见的是在静态方法上定义泛型。
  • 代码如下
import java.util.Arrays;
import java.util.Objects;

public class ArrayList<E>
{
    private Object[] elems;
    private int next;

    public ArrayList(int capacity)
    {
        elems = new Object [capacity];
    }

    public ArrayList()
    {
        this(16);
    }

    public void add(E e)
    {
        if(next == elems.length)
        {
            elems = Arrays.copyOf(elems,elems.length * 2);
        }
        elems[next++] = e;
    }

    public E get (int index)
    {
        return (E) elems[index];
    }

    public int size()
    {
        return next;
    }
}

简介Lambda表达式

  • Lambda表达式的语法省略了接口类型与方法名称,->左边是参数列,右边是方法本体。
    简化以下代码。
IntegerFunction doubleFunction = new IntegerFunction()
{
    public Integer apply(Integer i)
    {
        return i*2;    
    }
    
}

简化后

IntegerFunction doubleFunction = (Integer i) -> i * 2;

常用Map操作类

  • 常用的Map操作类为java.util.HashMap与java.util.TreeMap,其继承自抽象类java.util.AbstractMap。
  • 建立Map操作对象时,可以使用泛型语法指定键与值的类型。要建立键值对应,可以使用put()方法,第一个自变量是键,第二个自变量是值,对于Map而言,键不会重复,判断键是否重复是根据hashCode()与equals(),所以作为键的对象必须操作hashCode()与equals()。
  • 若要指定键取回对应的值,则使用get()方法,在hashMap中建立键值对应后,键是无序的,这可以在执行结果中看到。如果想让键是有序的,则可以使用TreeMap。

访问Map键值

  • Map虽然与Collection没有继承上的关系,但它们却是彼此的API。
  • 如果想取得Map中所有的键,可以调用Map的keySet() 返回Set对象,由于键是不重复的,所以用Set操作返回是理所当然的做法。如果想取得Map中所有的值,则可以使用values()返回Collection对象。

本周代码托管截图

其他(感悟、思考等,可选)

之前感觉java每周的学习太过紧张,因为是将任务全都堆到周末去做,所以在周末的两天里要看大量的视频,写大量的文字。但是其实只要每天都拿出时间来学java,就可以减轻一点周末写博客时的压力写博客也不只是抄书,而要进行自己的思考与感悟,虽然现在让我们自己写代码仍是有一定难度,毕竟练习的还是少,实话说我现在根本写不出,只能依葫芦画瓢去打书上的代码,稍微改动一点就会蒙掉,但是我相信经过之后的学习与努力,我最终可以写出自己的代码。

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 5000行 30篇 400小时
第一周 200/200 1/2 20/20
第二周 300/500 1/3 18/38
第三周 500/1000 1/4 22/60
第四周 1292/1300 1/5 40/90
第五周 993/1300 1/6 35/160

参考资料

posted @ 2016-04-03 20:18  20145218  阅读(186)  评论(2编辑  收藏  举报