exception
- return语句之外,方法的另外一个出口
- IOException通常代表"预期之内的异常"
- 万能解决方案,alt+enter
异常会击穿所有的栈帧
package com.github.hcsp.io;w
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
public class Crawler {
public static void main(String[] args) throws IOException {
readFile(null);
}
public static String readFile(File file) throws IOException {
int i = 0;
i++;
int j = 1;
j++;
if (true) {
throw new RuntimeException();
}
return FileUtils.readFileToString(file, Charset.defaultCharset());
}
}
-
加try catch去避免击穿栈帧
-
不管正确错误最终一定要处理,最常见的就是关闭文件,关闭数据库连接,做一些清理工作,
-
finally里面是不赞成使用return语句,finally 里return会打破之前的return
private static void a() {
try {
b();
return 0;
} catch (Exception e) {
e.printStackTrace();
return 1;
} finally {
closeFile();
}
}
private static void closeFile() {
}
private static void b() {
}
- catch可以干掉,catch里面一般都要
// 处理异常
// 在日志里打印异常
// 返回对应对异常的处理
try with resources 有限制只能在同一个方法
- 一些远古代码用file leak detector
- try with resources,try后面加括号,java会偷偷摸摸的加一个
- 两者等价,生命在括号里面的东西,finally会自动被关闭,必须实现了autoCloseable接口的东西
- 如果可以的话, IDE会有提示
- 例子2
public static void main2() throws IOException {
FileInputStream is = null;
try {
is = new FileInputStream("name");
} finally {
if (is != null) {
is.close();
}
}
}
- 多行代码变一行代码,可以写成
public static void main2() throws IOException {
try (FileInputStream is = new FileInputStream("name")) {
}
}
throw和throws
- throw抛一个异常
- throws只是一个声明
- throw的使用
public static void main(String[] args) throws IOException {
a();
}
private static void a() {
b();
}
private static void b() {
foo();
}
private static void c() throws Exception {
if (true) {
throw new Exception();
}
}
Java的异常体系
- Throwable - 可以被抛出的东西(有毒)
- Exception - checked exception (受检异常,有毒)
- RuntimeException(运行时异常,无毒)
- Error(错误,无毒)
- Exception - checked exception (受检异常,有毒)
- catch的级联与合并
- throwable是所有错误和异常的父类表示可以被丢出的东西
- 有毒的意思表示有传递性,声明了Throwable的方法,被调用的地方也一定要声明throws Throwable,学名叫受检异常checked exception
- unchecked exception 无毒 不受检异常
public static void main(String[] args) throws Throwable {
throwCheckedException();
}
private static void throwCheckedException() throws Throwable {
}
最早设计的时候只有有毒
- 想法是保证安全性,但是后来发现实际使用太麻烦了
- java异常设计中,exception表示预期之内被抛出的异常,比如IOException
- RuntimeException是运行时异常,意料之外的,因此不需要声明
- Error是不能恢复的,比如outMemeryError,代表不正常的情况
- catch的级联和合并,根据抛出不同的异常进行处理,从小到大的排
private static void throwCheckedException() throws Exception {
if (fileNotFound()) {
throw new FileNotFoundException();
} else if (reachFileEnd()) {
throw new EOFException();
} else {
throw new IOException();
}
}
public static void main(String[] args) {
try {
throwCheckedException();
} catch (FileNotFoundException e) {
System.out.println("文件没找到");
} catch (EOFException e) {
System.out.println("文件已到达末尾");
} catch (Exception e) {
e.printStackTrace();
}
}
- java7之后引入了异常合并,按alt+enter用|来去掉重复代码
Throwable
- 栈轨迹Stacktrace(排查问题最重要的信息,没有之一)
- 异常链(Caused by)
- 栈轨迹
把有毒的受检异常包成没有毒的异常
- 包装一层,把环境异常也抛出
private static class UserAlreadyExistException extends RuntimeException {
public UserAlreadyExistException(String message, Throwable cause) {
super(message, cause);
}
}
private static void foo() {
Integer userId = 1;
try {
insertIntoDatabase();
} catch (SQLException e) {
throw new UserAlreadyExistException("插入id为" + userId + "的数据时候发生了异常", e);
}
}
private static void insertIntoDatabase() throws SQLException {
throw new SQLException("重复的健值");
}
- 异常链,包一层
异常抛出的原则
- 能用if/else处理的,不要使用异常,原因有两个1.不能保证catch的异常是你想要抓住,2.异常的创建非常昂贵,能不用异常就不用
- 今早抛出异常,尽早抛出,问题简单化
- 异常要准确,比如文件没找到EOF,带有详细信息,尽可能把环境放进日志
- 抛出异常也比悄悄地执行错误的逻辑强的多
- e.printStackTrace,打印到当前进程的标准错误流
- 本方法是否有责任处理这个异常?不要处理不归自己管的异常
- 本方法是否有能力处理这个异常?如果自己无法处理,就抛出,比如数据库异常
- 如非万分必要,不要忽略异常, 以下这种是特例
try {
URLDecoder.decode("", "UTF-8");
} catch (UnsupportedEncodingException ignored) {
}
了解和使用JDK内置的异常
NullPointerException,空指针异常ClassNotFoundException/NoClassDefFoundError,类不存在的异常IllegalStateException,不正常的状态IllegalArgumentException,非法的参数IllegalAccessException,非法的访问
爬虫持久化
- 第三方库,搜索github api sdk
- String是不可变的
课后练习题
- 1-grep-file
package com.github.hcsp.io;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class FileSearch {
// 找到第一个包含text的行的行号,行号从1开始计算。若没找到,则返回-1。
// 如果指定的文件不存在或者无法被读取,抛出一个IllegalArgumentException。
// 请不要让这个方法抛出checked exception
private static class NotThrowCheckedException extends RuntimeException {
private NotThrowCheckedException(String message, Throwable cause) {
super(message, cause);
}
}
public static int grep(File target, String text) {
try {
FileInputStream inputStream = new FileInputStream(target);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
int count = 0;
String str;
while ((str = bufferedReader.readLine())!= null) {
count += 1;
if (str.contains(text)) {
return count;
}
}
return -1;
} catch (IOException e) {
throw new IllegalArgumentException();
}
}
public static void main(String[] args) {
File projectDir = new File(System.getProperty("basedir", System.getProperty("user.dir")));
System.out.println("结果行号:" + grep(new File(projectDir, "log.txt"), "BBB"));
}
}
- 2-fix-exception-handling
package com.github.hcsp.exception;
import java.io.File;
import java.sql.*;
public class DatabaseReader {
public static void main(String[] args) {
File projectDir = new File(System.getProperty("basedir", System.getProperty("user.dir")));
String jdbcUrl = "jdbc:h2:file:" + new File(projectDir, "test").getAbsolutePath();
System.out.println(jdbcUrl);
Connection connection = null;
try {
connection = DriverManager.getConnection(jdbcUrl, "sa", "");
PreparedStatement statement =
connection.prepareStatement("select * from PULL_REQUESTS where number > ?");
statement.setInt(1, 0);
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
System.out.println(
resultSet.getInt(1)
+ " "
+ resultSet.getString(2)
+ " "
+ resultSet.getString(2));
}
} catch (SQLException e) {
System.out.println(e);
} finally {
try {
connection.close();
} catch (SQLException e) {
System.out.println(e);
}
}
}
}