20145107 《Java程序设计》第5周学习总结
教材学习内容总结
在本周,我们进行了Java的第5周的学习,本周主要学习Java的第八,九章的内容。本周的主要学习目标是:
1.理解异常架构
2.牚握try...catch...finally处理异常的方法
3.会用throw,throws
4.理解Collection和Map架构
5.会用常见的数据结构和算法
6.了解Lambada和泛型
第八章的一开始即有关错误输入与处理的问题,书中先是有一个最基本的计算平均数的程序,程序如下:
package TryCatch;
import java.util.Scanner;
public class Average {
public static void main(String[] args) {
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("平均 %.2f%n", sum / count);
}
}
在此程序中,没有任何的关于输入不规范的检验,在正常的情况下输入数字是没有问题的,但是我们可以尝试一下不正常的输入,那么程序就会出现如下的错误提示:
也就是说所输入的对象不符合Scanner的预期,所以,要对程序进行改进:
package TryCatch;
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("平均 %.2f%n", sum / count);
} catch (InputMismatchException ex) {
System.out.println("必须输入整数");
}
}
}
程序运行如图:
改进后的程序相比原来的程序来说有了相应的检查功能,实现这一检查功能的就是try语句模块,当输入有错误的信息时,程序会告诉我们必须输入正确的信息,但对于一个更加先进的程序而言,我们希望在错误发生后程序不但可以检查出错误而且还可以继续运行直到输入正确的语句为止,所以,更加先进的程序三出现了:
package TryCatch;
import java.util.*;
public class Average3 {
public static void main(String[] args) {
Scanner console = new Scanner(System.in);
double sum = 0;
int count = 0;
while (true) {
try {
int number = console.nextInt();
if (number == 0) {
break;
}
sum += number;
count++;
} catch (InputMismatchException ex) {
System.out.printf("略过非正常的输入:%s%n", console.next());
}
}
System.out.printf("平均 %.2f%n", sum / count);
}
}
这个程序不但可以检查输入的错误,还可以对输入的错误滤掉并计算新的平均数。上面的程序使用了try,catch的语法,也算是java的一种新的语法类型。
接下来,书中在try,catch的基础上重点分析了异常继承架构。首先:在程序的运行中都难免会发生错误,而错误都会被包装为对象,这些对象都是可抛出的。错误的对象都继承自 java.lang.Throwable中,他有两个子类:java.lang.Error和java.lang.Expection。Error与其子类实例代表严重的系统错误,如硬件层面的错误,在发生问题时虽然也可以用try,catch来处理error对象,但在系统发生严重的错误时,java程序本身是无法进行恢复的。对于程序本身所发生的错误,建议使用Exception或其子类实例来表现所以通常称为错误处理或异常处理。单就语法与继承架构来说,如果某个方法生命会抛出Throwable或子类的实例只要不是属于Error,java.lang.RuntimeException或其子类实例,你就必须明确使用try,catch等语句,或者用throw声明这个方法会抛出异常,否则会编译失败。
所以,对于程序三我们还可以进行相应的改进,为了使程序三不出现InputMismatchException,应该是取得用户的字符串输入后,检查是否为数字的格式,若是,再转化为 int 型的整数,若格式不对则提醒用户重新输入相应改进的程序如下:
package TryCatch;
import java.util.Scanner;
public class Average4 {
public static void main(String[] args) {
double sum = 0;
int count = 0;
while(true) {
int number = nextInt();
if(number == 0) {
break;
}
sum += number;
count++;
}
System.out.printf("平均 %.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("请输入数字");
input = console.next();
}
return Integer.parseInt(input);
}
}
在上面的Scanner的next()方法来取得用户输入的下一个字符串时,如果字符串不是数字格式,就会提醒用户输入数字。
相应的程序截图如下所示:
8.13 throw与throws的用法:
操作对象的过程中如果会抛出受检异常,但目前的环境信息不足以处理异常,无法使用try,catch等进行处理时,可以→方法对的客户端依据当时调用的环境信息进行检查处理,为了告诉编译程序这个事实,必须使用throws声明此方法会抛出的异常类型或父类型,编译程序才会通过编译。实际上,在异常发生时,可以使用try,catch处理当时环境可进行的异常处理,当时环境下无法决定如何处理的部分,可以抛出由调用方法的客户端进行处理。如果想先处理部分事项再抛出,相应的代码如下:
package TryCatch;
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('
');
}
} catch (FileNotFoundException ex) {
ex.printStackTrace();
throw ex;
}
return text.toString();
}
}
异常与资源管理:
程序中因有错误而发生异常时,原本执行的程序就会发生中断,抛出异常之后的代码就不会执行,如果程序开启了相关的资源,使用完毕后是否考虑待相关的资源呢?下面就涉及到了资源的管理问题:
- 使用finally:
finally语句的作用是,无论try区块中有无异常的发生,若撰写finally区块,则词曲快就一定会被执行。程序如下:
package TryCatchFinally;
import java.io.*;
import java.util.Scanner;
public class FileUtil {
public static String readFile(String name) throws FileNotFoundException {
StringBuilder text = new StringBuilder();
Scanner console = null;
try {
console = new Scanner(new FileInputStream(name));
while (console.hasNext()) {
text.append(console.nextLine())
.append('
');
}
} finally {
if(console != null) {
console.close();
}
}
return text.toString();
}
}
如果撰写的流程中先return了,而且也有finally区块,那finally区块先会执行再将值返回,就像下面的程序:
package TryCatchFinally;
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,finally关闭资源时,会发现程序撰写的流程是类似的,就如先前FileUtil所示范的那样,你会先检查scanner是否为null,在调用close()方法关闭Scanner。在jdk
7之后,新增了尝试资源关闭的语法,下面就是它的使用方法:
package TryCatchFinally;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class FileUtil2 {
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('
');
}
}
return text.toString();
}
}
- java.lang.AutoCloseable接口:
jdk7的尝试关闭资源语法可套用的对象,必须操作java.lang.AutoCloseable接口,只要操作java.lang.AutoCloseable相应接口就可以套用至尝试关闭资源语法。术中给出的程序范例如下:
package TryCatchFinally;
public class AutoCloseableDemo {
public static void main(String[] args) {
try(Resource res = new Resource()) {
res.doSome();
} catch(Exception ex) {
ex.printStackTrace();
}
}
}
class Resource implements AutoCloseable {
void doSome() {
System.out.println("作一些事");
}
@Override
public void close() throws Exception {
System.out.println("资源被打开");
}
}
相应的程序运行如图;
如果此语法尝试关闭两个以上的资源,只要中间以分号分隔,书中给出了下面的例子:
package TryCatchFinally;
import static java.lang.System.out;
public class AutoCloseableDemo2 {
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 AutoCloseable {
void doSome() {
out.println("作一些事");
}
@Override
public void close() throws Exception {
out.println("资源Some被关闭");
}
}
class ResourceOther implements AutoCloseable {
void doOther() {
out.println("作其它事");
}
@Override
public void close() throws Exception {
out.println("资源Other被关闭");
}
}
在try的括号中,越后面撰写的资源对象会越早关闭。执行结果如下,ResourceOther实例先被关闭,然后再关闭ResourceSome实例。程序的运行如下所示:
第九章主要是Collection与Map架构问题
Collection架构
收集对象的行为,像是新增add()方法,移除对象remove()方法等,都是定义在java.util.Collection中。收集对象共同定义在Collection中然而对收集对象会有不同的需求。如果希望收集时记录每个对象的索引顺序,并可依索引取回对象,这样的行为定义在java.util.List中。如果希望收集的对象不重复而且具有集合行为,则由java.util.Set定义,其他功能的函数还有java.util.Queue,java.util.Deque等函数。
- 9.12 具有索引的List:
list是一种Collection,作用是收集对象,并以索引方式保留收集的对象顺序其操作类之一是:java.util.ArrayList,其原理与6.25中ArrayList的范例有点相同,相应的程序如下:
package Collection;
import java.util.*;
import static java.lang.System.out;
public class Guest {
public static void main(String[] args) {
List names = new java.util.ArrayList();
collectNameTo(names);
out.println("访客名单:");
printUpperCase(names);
}
static void collectNameTo(List names) {
Scanner console = new Scanner(System.in);
while(true) {
out.print("访客名称:");
String name = console.nextLine();
if(name.equals("quit")) {
break;
}
names.add(name);
}
}
static void printUpperCase(List names) {
for(int i = 0; i < names.size(); i++) {
String name = (String) names.get(i);
out.println(name.toUpperCase());
}
}
}
- 9.13 内容不重复的set:
在收集大量对象的过程中,如果有重复的对象,则不再重复收集,此时可以使用set接口的操作对象,书中给出了下面的范例:
package Collection;
import java.util.*;
public class WordCount {
public static void main(String[] args) {
Scanner console = new Scanner(System.in);
System.out.print("请输入英文:");
Set words = tokenSet(console.nextLine());
System.out.printf("不重复的单字有 %d 个:%s%n", words.size(), words);
}
static Set tokenSet(String line) {
String[] tokens = line.split(" ");
return new HashSet(Arrays.asList(tokens));
}
}
程序运行如图所示:
- 9.18 Comparable与Comparator
在收集对象后,对对象进行排序时很常用的动作,在Java中java.util.Collection提供有sort()的算法由于必须有索引才能进行排序,因此Collection的sort()方法接受List操作对象。例如:
package Collection;
import java.util.*;
public class Sort {
public static void main(String[] args) {
List numbers = Arrays.asList(10, 2, 3, 1, 9, 15, 4);
Collections.sort(numbers);
System.out.println(numbers);
}
}
程序运行如下所示:
若要进行更复杂一点的排列方法书中有一个关于银行储户的问题,程序如下:
package Collection;
import java.util.*;
class Account {
private String name;
private String number;
private int balance;
Account(String name, String number, int balance) {
this.name = name;
this.number = number;
this.balance = balance;
}
@Override
public String toString() {
return String.format("Account(%s, %s, %d)", name, number, balance);
}
}
public class Sort2 {
public static void main(String[] args) {
List accounts = Arrays.asList(
new Account("Justin", "X1234", 1000),
new Account("Monica", "X5678", 500),
new Account("Irene", "X2468", 200)
);
Collections.sort(accounts);
System.out.println(accounts);
}
}
但程序会出现以下的错误:
其具体的原因为我们没有告诉Collection的Sort()方法,程序不知道要根据Account,name,number,或balance进行排序,所以程序无法进行,我们对程序进行如下改进:
package Collection;
import java.util.*;
class Account2 implements Comparable<Account2> {
private String name;
private String number;
private int balance;
Account2(String name, String number, int balance) {
this.name = name;
this.number = number;
this.balance = balance;
}
@Override
public String toString() {
return String.format("Account2(%s, %s, %d)", name, number, balance);
}
@Override
public int compareTo(Account2 other) {
return this.balance - other.balance;
}
}
public class Sort3 {
public static void main(String[] args) {
List accounts = Arrays.asList(
new Account2("Justin", "X1234", 1000),
new Account2("Monica", "X5678", 500),
new Account2("Irene", "X2468", 200)
);
Collections.sort(accounts);
System.out.println(accounts);
}
}
就会出现如下的解法:
如果对象无法操作Comparable可以使用如下排序即操作Comparator:
package Collection;
import java.util.*;
public class Sort4 {
public static void main(String[] args) {
List words = Arrays.asList("B", "X", "A", "M", "F", "W", "O");
Collections.sort(words);
System.out.println(words);
}
}
程序执行如下所示:
- 9.2 键值对应的map问题:
java程序设计中有这样一种需求:根据某个key来取得相应的value,可以事先利用java.util.map接口的操作对象来建立键值对应的数据。
1.使用HashMap:
此程序可以根据指定的用户名去的对应的信息:
package Map;
import java.util.*;
import static java.lang.System.out;
public class Messages {
public static void main(String[] args) {
Map<String, String> messages = new HashMap<>();
messages.put("Justin", "Hello!Justin的讯息!");
messages.put("Monica", "给Monica的悄悄話!");
messages.put("Irene", "Irene的可爱的喵喵叫!");
Scanner console = new Scanner(System.in);
out.print("取得谁的信息:");
String message = messages.get(console.nextLine());
out.println(message);
out.println(messages);
}
}
程序的运行如下所示:
2.访问Map键值:
如果想取得Map中的所有键,可以调用Map中的keySet()返回Set对象,如果想取得Map中的所有值,则可以使用value()返回Collection对象。相应的程序如下:
package Map;
import java.util.*;
import static java.lang.System.out;
public class MapKeyValue {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("one", "一");
map.put("two", "二");
map.put("three", "三");
out.println("显示键");
// keySet()传回Set
map.keySet().forEach(key -> out.println(key));
out.println("显示值");
// values()传回Collection
map.values().forEach(key -> out.println(key));
}
}
程序在idea中的运行如下所示:
如果想同时取得Map的键与值,可以使用entrySet()方法,这会返回一个Set对象每个元素都是Map。Entry的实例,同时可以调用getKey()取得键,调用getvalue()取得值书中范例程序如下:
package Map;
import java.util.*;
public class MapKeyValue2 {
public static void main(String[] args) {
Map<String, String> map = new TreeMap<>();
map.put("one", "一");
map.put("two", "二");
map.put("three", "三");
foreach(map.entrySet());
}
static void foreach(Iterable<Map.Entry<String, String>> iterable) {
for(Map.Entry<String, String> entry: iterable) {
System.out.printf("(键 %s, 值 %s)%n",
entry.getKey(), entry.getValue());
}
}
}
相应的程序运行如下:
教材学习中的问题和解决过程
本周第8,9两章的学习让我们不断的认识各种类的使用,操作过程中对象的引用,接口的使用,与前面的章节有很大的关系,在编译程序时,有时会出现接口调用有问题,或程序运行不了的情况,在学习8,9两章的同时更多的复习了前面的继承,多态等内容觉得java的学习很有收获。
本周代码托管截图
本周代码统计及截图:
其他(感悟、思考等,可选)
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 200/200 | 2/2 | 20/20 | |
第二周 | 250/300 | 2/4 | 18/38 | |
第三周 | 300/500 | 3/7 | 22/60 | |
第四周 | 500/750 | 2/9 | 30/90 | |
第五周 | 750/1200 | 2/9 | 30/90 |