如果一个方式可能会出现异常,但没有能力处理这些异常,可以在方式声明处用throws谓词来申明抛出异常。例如车辆在运行时它可能会出现故障,汽车本身没办法处理这个故障,那就让驾车的人来处理。
而throws用于进行异常类的申明,若该方式可能有多种异常情况形成,那么在throws前面可以写多个异常类,用冒号隔开。
public class ThrowsDemo2 {
public static void main(String[] args) throws IOException {
readFile("a.txt");
}
//若该方法可能有多种异常情况产生,那么在throws后面可以写多个异常类,用逗号隔开
//若有异常a是异常b的子类,也可以直接省略,写b异常
private static void readFile(String path) throws FileNotFoundException, IOException {
if (!path.equals("a.txt")) {//如果不是 a.txt这个文件
// 我假设 如果不是 a.txt 认为 该文件不存在 是一个错误 也就是异常 throw
throw new FileNotFoundException("文件不存在");
}
if (!path.equals("b.txt")) {
throw new IOException();
}
}
}
throws抛出异常的规则:
如果是非受检异常(unchecked exception),即Error、RuntimeException或它们的泛型,那么可以不使用throws关键字来申明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。如果一个方式可能出现受检异常(checked exception),要么用try-catch句子捕获,要么用throws谓词申明将它抛出,否则会导致编译错误。只有当抛出了异常时,该方式的调用者才必须处理或则重新抛出该异常。若当方式的调用者无力处理该异常的时侯,应该继续抛出。调用方式必须遵守任何可查异常的处理和申明规则。若覆盖一个方式,则不申明与覆盖方式不同的异常。声明的任何异常必须是被覆盖方式所申明异常的同类或泛型。3.5 捕获异常try 、finally 、catch
这三个关键字主要有下边几种组合形式try-catch 、try-finally、try-catch-finally。
注意:catch句子可以有一个或则多个或则没有,finally至多有一个,try必要有。
那这儿你会问有没有单独try模块出现,那我想问下你,try是拿来窃听是否有异常,那倘若发生了异常,谁来捕获呢?所以没有try单独出现。而且编译不能通过。
所以跟try模块一样,例如catch,finally也不能单独使用出现
捕获异常句型如下:
3.5.1 try-catch 形式:
try{
编写可能会出现异常的代码
}catch(异常类型 e){
处理异常的代码
//记录日志/打印异常信息/继续抛出异常
}
例如:
public class TryCatchDemo {
public static void main(String[] args) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
//当产生异常时,必须有处理方式。要么捕获,要么声明。
Date date = simpleDateFormat.parse("2020-10-06");
} catch (ParseException e) {// 括号中需要定义什么呢?
//try中抛出的是什么异常,在括号中就定义什么异常类型
e.printStackTrace();//记录日志/打印异常信息/继续抛出异常
}
/*
public Date parse(String source) throws ParseException{}
//parse抛出了ParseException异常
public class ParseException extends Exception {}
*/
}
}
如何获取异常信息:
Throwable类中定义了一些查看方式:
public class TryCathDemo2 {
public static void main(String[] args) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
//当产生异常时,必须有处理方式。要么捕获,要么声明。
//演示下获取异常信息,修改了格式。
Date date = simpleDateFormat.parse("2020年10月06");
} catch (ParseException e) {
//public String getMessage():获取异常的描述信息,原因(提示给用户的时候,就提示错误原因
System.out.println(e.getMessage());//Unparseable date: "2020年10月06"
System.out.println(e.toString());//java.text.ParseException: Unparseable date: "2020年10月06"
e.printStackTrace();//输出信息而且飘红!!!
/*
java.text.ParseException: Unparseable date: "2020年10月06"
at java.text.DateFormat.parse(DateFormat.java:366)
at com.it.test3.TryCathDemo2.main(TryCathDemo2.java:13)
*/
}
}
}
而倘若有多个异常使用捕获我们又该怎么处理呢?
多个异常分别处理。多个异常一次捕获,多次处理。
一般我们是使用一次捕获多次处理方法,格式如下:
try{
编写可能会出现异常的代码
}catch(异常类型A e){ 当try中出现A类型异常,就用该catch来捕获.
处理异常的代码
//记录日志/打印异常信息/继续抛出异常
}catch(异常类型B e){ 当try中出现B类型异常,就用该catch来捕获.
处理异常的代码
//记录日志/打印异常信息/继续抛出异常
}
例如:
public class TryCatchDemo3 {
public static void main(String[] args) {
//test();
test2();
}
//多个异常一次捕获,多次处理。
public static void test2(){
int[] arr = {11, 22, 66, 0};
try {
//System.out.println(arr[5]);//一旦这个报错,下面的代码就不会执行
System.out.println(arr[2]);
System.out.println(arr[0] / arr[arr.length-1]);
} catch (ArithmeticException e) {
System.out.println("除数不为0");
}catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组下标越界");
}catch (Exception e) {
e.printStackTrace();
}
}
//分别处理的方式
public static void test() {
int a = 10;
int b = 0;
try {
System.out.println(a / b);
} catch (ArithmeticException e) {
System.out.println("除数不为0");//除数不为0
}
int[] arr = {1, 2, 3};
try {
System.out.println(arr[4]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组下标越界");//数组下标越界
}
}
}
注意:一次捕获,多次处理的异常处理方法,要求多个catch中的异常不能相同,并且若catch中的多个异常之间有子父类异常的关系,那么泛型异常要求在前面的catch处理,父类异常在下边的catch处理。
例如:
3.5.2 try-finally 形式:
try{
//(尝试运行的)程序代码
}finally{
//异常发生,总是要执行的代码
}
try-finally表示对一段代码不管执行情况怎样,都会走 finally 中的代码,
例如:
public class TryFinallyDemo {
public static void main(String[] args) {
int a = 10;
int b = 0;
try{
System.out.println(a / b);
System.out.println("会走try吗");
}finally{
System.out.println("会finally吗");//会finally吗
}
System.out.println("会走外面吗");
/*
没有捕获的话,他只会走finally语句然后报出异常。
会finally吗
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.it.test3.TryFinallyDemo.main(TryFinallyDemo.java:8)
*/
}
}
可以看见程序异常了,还是会去走finally语句块的代码。
3.5.3 try-catch-finally 形式:
try {
// 可能会发生异常的程序代码
} catch (异常类型A e){
// 捕获并处置try抛出的异常类型A
} finally {
// 无论是否发生异常,都将执行的语句块
}
跟try-finally一样表示对一段代码不管执行情况怎样,都会走 finally 中的代码。
当方式中发生异常,异常处以后的代码不会再执行,如果之前获取了一些本地资源须要释放,则须要在技巧正常结束时和 catch 语句中都调用释放本地资源的代码,显得代码比较冗长,finally 语句可以解决这个问题。
例如:
public class TryCatchFinallyDemo {
public static void main(String[] args) {
test();
}
public static void test() {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
//date = simpleDateFormat.parse("2020-10-06");//第一次运行成功
date = simpleDateFormat.parse("2020年10月06日");
} catch (ParseException e) {
e.printStackTrace();
}finally{
System.out.println("finally这里一定会执行");
}
System.out.println("会走外面这里吗" + date);
}
}
运行成功的代码后结果:
finally这里一定会执行
会走外面这里吗Tue Oct 06 00:00:00 CST 2020
运行失败的代码后结果:
java.text.ParseException: Unparseable date: "2020/10/06"
at java.text.DateFormat.parse(DateFormat.java:366)
at com.it.test3.TryCatchFinallyDemo.test(TryCatchFinallyDemo.java:19)
at com.it.test3.TryCatchFinallyDemo.main(TryCatchFinallyDemo.java:12)
finally这里一定会执行
会走外面这里吗null
可以看见,无论失败,都会执行finally语句块的代码。
运行结果:
java.lang.ArithmeticException: / by zero
at com.it.test3.TryCatchFinallyDemo2.test(TryCatchFinallyDemo2.java:11)
at com.it.test3.TryCatchFinallyDemo2.main(TryCatchFinallyDemo2.java:5)
finally
可以看见,就算catch中 return了,finally也会执行。
那finally是在return前呢,还是return后呢?
让我们看下边的代码?
public class TryCatchFinallyDemo2 {
public static void main(String[] args) {
// test();
System.out.println(test2()); // 我有执行到吗 try
System.out.println(test3()); // 我有执行到吗 catch
}
public static String test3() {
String str = "";
try {
str = "try";
System.out.println(10 / 0);
return str;
}catch(Exception e) {
str = "catch";
return str;
}finally {
str = "finally";
System.out.println("我有执行到吗");
}
}
public static String test2() {
String str = "";
try {
str = "try";
return str;
}catch(Exception e) {
str = "catch";
return str;
}finally {
str = "finally";
System.out.println("我有执行到吗");
}
}
}
运行结果:
我有执行到吗
try
我有执行到吗
catch
看到这儿发觉无论是否异常,finally就会执行,但是都在在return之前就执行了代码。可是而且,为什么返回下来的字符串不是finally呢?让我们一起来思索思索:
是不是对这个现象有了一定的了解。嘿嘿,这里我们再转换下:
public class TryCatchFinallyDemo2 {
public static void main(String[] args) {
// test();
// System.out.println(test2()); // try
// System.out.println(test3()); // catch
System.out.println(test4());
}
public static String test4() {
String str = "";
try {
str = "try";
return str;
}catch(Exception e) {
str = "catch";
return str;
}finally {
str = "finally";
return str;
}
}
}
这里我们猜想下,结果是什么呢?
运行结果:finally
再看一下: 我们是不是晓得finally句子是一定执行的,但是能有办法使他不执行吗?
既然我说了,那么就是一定有的啦。
public class TryCatchFinallyDemo3 {
public static void main(String[] args) {
try{
System.out.println(10 / 0);
}catch(Exception e) {
e.printStackTrace();
System.exit(0);
}finally {
System.out.println("finally我有执行到吗");
}
}
}
执行结果:
java.lang.ArithmeticException: / by zero
at com.it.test3.TryCatchFinallyDemo3.main(TryCatchFinallyDemo3.java:6)
可以发觉:
当只有在try或则catch中调用退出JVM的相关技巧,此时finally才不会执行,否则finally永远会执行。
3.5.4 小结try块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。catch块:用于处理try捕获到的异常。finally块:无论是否捕获或处理异常,finally块里的句子就会被执行。当在try块或catch块中遇见return句子时,finally语句块将在方式返回之前被执行。在以下4种特殊情况下,finally块不会被执行:在finally语句块中发生了异常。在上面的代码中用了System.exit()退出程序。程序所在的线程死亡。关闭CPU。3.6 如何选择异常类型
可以按照右图来选择是捕获异常,声明异常还是抛出异常
我们在日常处理异常的代码中,应该遵守的原则:
4 JDK1.7有关异常新特点4.1 try-with-resources
Java 类库中有许多资源须要通过 close 方法进行关掉。比如 InputStream、OutputStream等。作为开发人员常常会忽视掉资源的关掉方式,导致内存泄漏。当然不是我啦!
在JDK1.7之前呢,try-catch-finally句子是确保资源会被关掉的最佳方式,就算异常或则返回也一样可以关掉资源。
是不是我们必须finally语句块中自动关掉资源,否则会导致资源的泄漏
在JDK1.7及之后的版本:
JDK1.7 中引入了try-with-resources 语句。
那我们来瞧瞧如何使用吧:
格式:
try (创建流对象语句,如果多个,使用';'隔开) {
// 读写数据
} catch (IOException e) {
e.printStackTrace();
}
演示下:
/**
* JDK1.7之后就可以使用try-with-resources,不需要
* 我们在finally块中手动关闭资源
*/
public class TryWithResourcesDemo {
public static String readLineFormFile(String path) {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
两者的对比:
4.2 catch多种异常并抛出新的异常5 自定义异常5.1 为什么须要自定义异常类:
我们说了Java中不同的异常类,分别表示着某一种具体的异常情况,那么在开发中,当Java外置的异常都不能明晰的说明异常情况的时侯,需要创建自己的异常。例如年纪正数问题,考试成绩正数问题。
什么是自定义异常类呢:
在开发中按照自己业务的异常情况来自己定义异常类。例如:登录系统中,年龄能为正数吗,不能就须要自己定义一个登陆异常类。
5.2 怎样定义自定义异常类自定义一个编译期异常: 自定义类并承继于java.lang.Exception 。自定义一个运行时期的异常类:自定义类并承继于java.lang.RuntimeException 。
public class MyException extends Exception {
public MyException(){ }
public MyException(String message){
super(message);
}
}
5.3 自定义异常的反例
需求:我们模拟登录操作,如果用户名已存在,则抛出异常并提示:亲,该用户名早已被注册。这个相信你们也常常看到吧。
首先定义一个登录异常类LoginException :/**
* 登陆异常类
*/
public class LoginException extends Exception {
public LoginException() {
}
public LoginException(String message) {
super(message);
}
}
模拟登录操作,使用链表模拟数据库中储存的数据,并提供当前注册帐号是否存在方式用于判别。
public class LoginTest {
// 模拟数据库中已存在账号
private static String[] names = {"hello", "world", "fish"};
public static void main(String[] args) {
//调用方法
try{
// 可能出现异常的代码
checkUsername("fish");
System.out.println("注册成功");//如果没有异常就是注册成功
} catch(LoginException e) {
//处理异常
e.printStackTrace();
}
}
//判断当前注册账号是否存在
//因为是编译期异常,又想调用者去处理 所以声明该异常
public static boolean checkUsername(String uname) throws LoginException {
for (String name : names) {
if(name.equals(uname)){//如果名字在这里面 就抛出登陆异常
throw new LoginException("亲"+name+"已经被注册了!");
}
}
return true;
}
}
执行结果:注册成功。
6. 完结散花
相信诸位看官都对异常这一个体系有了一定了解,在我们实际应用当中,一般还会对可能出现异常的地方进行一些有效处理,比如在倘若出现异常的情况,可能会返回到友好界面或则友好信息给用户,总不可能把一些异常信息给用户吧,这对用户的体验是相当差劲滴!所以异常处理相当必要!
最后
小伙伴们,帮忙一键三连呀
题外话,我在一线互联网企业工作十余年里,指导过不少同行小辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给你们,也可以通过我们的能力和经验解答你们在Java学习中的好多困扰,所以在工作忙碌的情况下还是坚持各类整理和分享。但苦于知识传播途径有限,很多程序员同学未能获得正确的资料得到学习提高
故此将并将重要的Java进阶资料包括并发编程、JVM调优、SSM、设计模式、spring等知识技术、阿里面试题精编汇总、常见源码剖析等录播视频免费分享下来,需要申领的后台私信【555】免费发放
Java进阶视频资料
Java面试题精编汇总
JAVA核心知识点整理
免费获取方法: 只需你点赞后私信【555】领取