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 |