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

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/2001/220/20
第二周300/5001/318/38
第三周500/10001/422/60
第四周1292/13001/540/90
第五周993/13001/635/160

参考资料

  • Java学习笔记(第8版)
  • 《Java学习笔记(第8版)》学习指导

以上是 20145218 《Java程序设计》第五周学习总结 的全部内容, 来源链接: utcz.com/z/392687.html

回到顶部