- 我们接下来的主要内容是 io 流。但是在讲流之前先要讲一下 文件 和 异常。
- 为什么?
因为 流 主要就是用来操作文件的(文件的上传和下载)所以我们要先学习 文件。
在操作文件的过程中,有可能会出现问题。出现问题之后,我们就需要做出对应的处理。
所以我们还要学习 异常。
1,异常
1.1 概述
-就是java程序的不正常情况
1.2 为什么有异常类?异常类的由来?
- 不正常的情况(问题)也是现实生活中一个真实存在的具体事物,所以java也用类的形式进行了描述,封装了对象。
- 这个对象就是java对不正常的情况描述后的具体体现。
- 我们在写代码的时候,会经常出现的异常
空指针异常
索引越界异常
类型转换异常
…
1.3 异常的分类
异常:Throwable
严重问题:Error
我们不处理,这种问题一般都很严重,我们解决不了。不是我们自己的问题。
内存不够。。。
问题:Exception
编译期问题:非RuntimeException
这种问题我们必须改代码。不改代码编译都不能通过。
运行期问题:RuntimeException
出现这样的问题,大部分都是因为代码写的不够严谨,需要改成代码
-
Throwable
Error
子类
Exception
非RuntimeException
子类
RuntimeException
子类 -
举例:
过年了,我打算带着家人开车出去旅游。
问题一:打算出门的时候,突然下起了大雪,不能出去了 — 严重的问题
问题二:刚准备上车的时候,发现车没气了,给车打个气 — 出发前就因该检查的问题
问题三:开车出门了,正在正常行驶,突然路边出现了一个美女,吸引了我的眼睛。没有注意开车,
看美女了,然后车撞了— 过程中出现的问题。 -
如果程序出现了问题,我们没有做任何处理。最终jvm会做出默认的处理。
1.4 jvm默认处理是如何处理的?
把异常的名字,原因和出现的问题,位置等信息输出到控制台上。
同时程序会结束,后面的代码不会执行。
当代码执行到System.out.println(1/0);这一步的时候,jvm发现这个元素已经违反了数学运算规则,
java把类似这种常见的问题都进行了描述,并封装成了类---ArithmeticException
当除0运算发生后,jvm就会把该问题打包成一个异常对象,并且会把对象抛给调用者main
方法,new ArithmeticException("/ by zero");
main方法接收到这个问题对象时,有2种处理方式:
第一种:我们自己处理,程序可以继续运行。
第二种:我们不处理(或者我们无法处理),最终就只能交给main方法的调用者 jvm 来处理
jvm会走它的默认处理机制。
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.momo.demo.Demo24.main(Demo24.java:12)
2 异常的处理方案
2.1 异常处理方式由两大类
- try…catch…finally
- throws
2.2 try…catch…finally
try{
可能出现问题的代码
}catch(异常类名 变量名){
针对问题处理方式
}finally{
无论代码有没有问题,都必须执行的代码
释放资源
}
- 注意:try中的代码越少越好。尽可能只把有可能会出现问题的代码放进去,
确定不会出问题的代码尽量不要往里面放。
catch里面必须有内容。哪怕只是一个提示语句都行。 - 又有很多变形格式
try…catch
try…catch…catch…
try…catch…catch…finally
try…finally
2.3 try…catch处理方式
- 又分为:
一个异常
多个异常
平级关系
子父级关系
package com.momo.demo;
/*多个异常:
* 每一个异常写一个try...catch
* 另一种方式用try...catch...catch.....
* 注意:2中方式的区别:
* 一旦try里面的代码出现问题,就会在这里把问题抛出去,
* 然后和catch里面的问题类型进行匹配,一旦匹配成功,就会执行对应catch里面的处理方式。
* try..catch就结束了。然后代码会继续向下执行。 不会回头。
* */
public class Demo5 {
public static void main(String[] args) {
System.out.println("开始执行");
//一起处理
int a = 5;
int b = 1;
int[] arr = {1,2,3,4};
try {
System.out.println(a / b);
System.out.println(arr[5]);
}catch (ArrayIndexOutOfBoundsException ai){
System.out.println("数组索引越界了");
}catch (ArithmeticException ae){
System.out.println("除数不能是0");
}
//分开处理
/* int a = 5;
//int b = 1;
int b = 0;
try {
System.out.println(a / b);
}catch (ArithmeticException ae){
System.out.println("除数不能是0");
}
int[] arr = {1,2,3,4};
try {
System.out.println(arr[5]);
}catch (ArrayIndexOutOfBoundsException ai){
System.out.println("数组索引越界了");
}*/
System.out.println("执行结束");
}
}
- jdk7之后出现了一种新的异常处理方式:
package com.momo.demo;
/*
*jdk7之后出现了一种新的异常处理方式:
* try{
* ...
* }catch(异常类名一|异常类名二|.... 变量名){
* ....
* }
*
* 注意:这种方式虽然看起来简单一点。但是不太好。
* 多种异常的处理方式是一致的。(实际应用比较方便。开发中很多时候就是针对多种问题,给一个处理方式。)
* 多个异常之间必须是平级关系,不能出现子父级关系。
* */
public class Demo7 {
public static void main(String[] args) {
System.out.println("开始");
int a = 5;
int b = 0;
int[] arr = {1,2,3,4};
try{
System.out.println(a/b);
System.out.println(arr[5]);
System.out.println("假设我这里还有其他代码,也有可能会出现问题。" +
"但是我不知道它到底会出现什么问题,怎么呢?" +
"");
}catch (ArithmeticException|ArrayIndexOutOfBoundsException e){
System.out.println("出问题了。。。");
}
System.out.println("结束");
}
}
3 编译期异常和运行期异常的区别
java中异常分为2大类:
- 编译期异常:
非RuntimeException的异常都是编译期异常
必须修改代码,否则程序报错,编译不通过。 - 运行期异常:
所有的RuntimeException以及它的子类都是运行期异常
可以和编译期异常一样去处理,也可以不处理。
4 Throwable中的方法
- String getMessage() 以字符串的形式返回异常信息
- void printStackTrace() 获取异常类名和信息,以及异常所出现的位置。
- void printStackTrace(PrintStream s) 这个方法一般用来把异常信息保存到日志文件中,方便查阅。
- String toString() 以字符串的形式返回异常类名以及信息
package com.momo.demo;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
* try里面发现问题后,jvm会帮我们创建一个异常对象,然后把对象抛出去
* 和catch里面的类进行匹配。
* 如果有类型匹配上了,那么就会执行对应catch里面的处理方式。
* */
public class Demo9 {
public static void main(String[] args) {
System.out.println("开始");
String s = "2020-05-05";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = sdf.parse(s);//一但出错,已经产生了一个具体的错误。
// 它就会创建具体的 new ParseException();然后把这个对象抛出去了
//和catch中进行匹配。
} catch (ParseException e) {
//ParseException e = new ParseException();
// e.printStackTrace();
/*
* java.text.ParseException: Unparseable date: "2020-05-05"
at java.text.DateFormat.parse(DateFormat.java:366)
at com.momo.demo.Demo9.main(Demo9.java:15)
* */
//String message = e.getMessage();
//System.out.println(message);
//Unparseable date: "2020-05-05"
//String s1 = e.toString();
//System.out.println(s1);
//java.text.ParseException: Unparseable date: "2020-05-05"
/* try {
e.printStackTrace(new PrintStream("log.txt"));
} catch (FileNotFoundException ex) {
ex.printStackTrace();
}*/
}
System.out.println(date);
System.out.println("结束");
}
}
5 throws
5.1 概述
定义方法时,有可能在使用的时候会出现问题,但是这个问题我无法处理,或者说我没有权限处理。我们就需要把问题暴漏出来交给调用者去处理。
我们就需要使用throws在方便上标记。
package com.momo.demo;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
* 有些时候,我们可以对异常进行处理。
* 又有些时候,我们没办法处理某个异常
* 为了解决出错问题,java就提供了另一种处理方式:抛出去
*
* 格式:throws 异常类名
* 注意:这个格式必须跟在方法的() 后面。
*
* 注意:尽量不要在main方法上抛,最后就走jvm的默认处理机制了
* 后面上课的时候为了方便我就直接抛了
*
* 编译期异常的抛出:将来调用者必须处理
* 运行期异常的抛出:将来调用者可以不处理
* */
public class Demo10 {
public static void main(String[] args) {
System.out.println("程序开始");
/* try {
fun1();
} catch (ParseException e) {
e.printStackTrace();
}*/
fun2();
System.out.println("程序结束");
}
//运行期异常的抛出
public static void fun2() throws ArithmeticException{
System.out.println(5/0);
}
//编译期异常的抛出
public static void fun1() throws ParseException {
String s = "2020-05-05";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse(s);
System.out.println(date);
}
}
package com.momo.demo;
/*
* thorw:如何抛异常
* 问:throw和throws的区别?
* throws:
* 在方法声明后,跟的时异常的类名,如果又多个异常,用逗号隔开。
* 表示的是抛出异常,将来由调用者来处理
* 表示的是一种可能性,不一定会发生这些问题。
* throw:
* 在方法内部,跟的是异常的对象
* 只能抛除一个异常对象
* 表示抛出异常,throw一旦执行表示一定抛出了某个异常对象。
* */
public class Demo11 {
public static void main(String[] args) {
System.out.println("aaaa");
/* try {
fun2();
}catch (Exception e){
//Exception e = new ArithmeticException();
e.printStackTrace();
}*/
fun1();
System.out.println("bbbbbb");
}
public static void fun1(){
int a = 5;
int b = 0;
if(b==0){
throw new ArithmeticException();
}else{
System.out.println(a/b);
}
}
public static void fun3() throws ArithmeticException{
int a = 5;
int b = 0;
if(b==0){
throw new ArithmeticException();
}else{
System.out.println(a/b);
}
}
public static void fun2() throws ArithmeticException,ArrayIndexOutOfBoundsException{
int a = 5;
int b = 0;
System.out.println(a/b);
int[] arr = {1,2,3,4,5};
System.out.println(arr[6]);
}
}
5.2 我们遇到异常的时候到底该如何处理呢?
- 原则:如果在写方法的时候,能通过改代码提高程序安全性的时候就改代码
如果改代码解决不了,能用try就用try处理。
如果try处理不了,就交给调用者用throws - 区别:如果出现问题,后面的代码还想继续执行就用try
如果出现问题,后面的代码不想继续执行就用throws
6 finally
6.1 finally的特点
- 写在finally中的代码一定会执行
- 注意:有一种特殊情况,在执行到finally之前,jvm退出了。
6.2 2finally的作用
- 主要作用是用来释放资源的,后面的学习慢慢会见到。
package com.momo.demo;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo12 {
public static void main(String[] args) {
System.out.println("start");
String s = "2020-05-05";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = sdf.parse(s);
System.out.println(5/1);
} catch (ParseException e) {
e.printStackTrace();
//System.exit(0);
return;
} catch (ArithmeticException e) {
e.printStackTrace();
} finally {
sdf = null;
System.out.println("我是finally");
}
System.out.println(date);
System.out.println("end");
}
}
6.3 finally的相关问题
- 问:final,finally,finalize的区别?
- 问:如果catch语句中有return语句,请问,finally中的代码还会执行吗?
retrun的作用:结束方法,把结果返回给调用者。
会执行。 到底是怎么执行的?是在return之前执行的吗?还是在return之后?
是在return之前执行。
其实更像是在中间执行的
package com.momo.demo;
public class Demo13 {
public static void main(String[] args) {
System.out.println(get());
}
public static int get(){
int a = 5;
//System.out.println(a);
try {
System.out.println(a / 0);
a = 55;
// System.out.println(a);
}catch (ArithmeticException ae){
a = 555;
// System.out.println(a);
return a;
/*
* 当代码走到这里的时候已经该返回了,这个时候a是555;
* 所以在这里已经不是return a了,是return 555;
* 但是发现后面有fianlly,所以就去执行了finally中的代码
* 虽然在finally中给a赋值5555,但是对我的返回结果已经没有影响了
* 执行完成finally之后,会再次回到return 555返回,结束。
* */
} finally {
a = 5555;
// System.out.println(a);
return a;
}
// return a;
}
}
6.4 要记住try…catch…finally的常见的变型格式
- try…catch…finally
- try…catch…
- try…catch…catch…
- try…catch…catch…finally
- try…finally 就是为了释放资源
package com.momo.demo;
import java.util.Scanner;
public class Demo14 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
try{
System.out.println(5/0);
}finally {
sc.close();
System.out.println("释放资源");
}
}
}
7 自定义异常
7.1 概述
- java不可能考虑到所有的异常情况。所以在实际开发的时候,我们需要自定义异常。
- 比如:我自定义的逻辑。判断成绩是否在1-100之间。像这种情况我们就需要自定义异常类来处理自定义逻辑的异常。
7.2 我们随意写的一个类不能直接作为异常类来处理异常的。
要想使我们定义的类是一个异常类,可以处理异常。那么这个类就必须继承 Exception 或者 RuntimeException。
- 继承Exception: 编译期异常
- 继承RuntimeException:运行期异常
package com.momo.myexception;
public class My1 extends Exception {
}
package com.momo.myexception;
public class My2 extends RuntimeException {
}
package com.momo.demo;
import com.momo.myexception.My;
import com.momo.myexception.My1;
import com.momo.myexception.My2;
public class Demo15 {
public static void main(String[] args){
// fun();
fun2();
}
public static void fun() throws My1{}
public static void fun2() throws My2 {}
}
package com.momo.myexception;
/*public class MyException extends Exception {
public MyException(){}
public MyException(String message){
super(message);
}
}*/
public class MyException extends RuntimeException {
public MyException(){}
public MyException(String message){
super(message);
}
}
package com.momo.domain;
import com.momo.myexception.MyException;
public class Tea {
public void check(int score) throws MyException {
if(score>100||score<1){
throw new MyException("分数不合法,1-100之间。");
}else{
System.out.println("成绩合法");
}
}
}
package com.momo.demo;
import com.momo.domain.Tea;
import com.momo.myexception.MyException;
import java.util.Scanner;
public class Demo16 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入成绩:");
int i = sc.nextInt();
Tea t = new Tea();
/* try {
t.check(i);
} catch (MyException e) {
e.printStackTrace();
}*/
t.check(i);
}
}
8 异常的注意事项
- 子类重写父类方法时,子类的方法只能抛出和父类一样的异常或者是父类异常的子类。
- 子类重写父类方法时,如果父类方法抛出了多个异常,子类只能抛出相同的异常或者是父类异常的子集,不能抛出父类没有的异常。
- 类重写父类方法时,如果父类方法没有抛异常,子类重写的时候也不能抛异常。
如果子类重写的时候出现了异常,就只能try,不能throws
- 儿子不能不父亲更坏
package com.momo.domain;
import org.omg.CORBA.portable.ApplicationException;
import java.rmi.AlreadyBoundException;
import java.rmi.activation.ActivationException;
import java.security.acl.AclNotFoundException;
import java.text.ParseException;
public class Father {
public void show() throws Exception
{
}
public void fun(){}
}
package com.momo.domain;
import org.omg.CORBA.portable.ApplicationException;
import java.rmi.AlreadyBoundException;
import java.rmi.activation.ActivationException;
import java.security.acl.AclNotFoundException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class Son extends Father {
public void show() throws NullPointerException,ArrayIndexOutOfBoundsException,ArithmeticException {
System.out.println("zishow");
}
public void fun (){
SimpleDateFormat sdf = new SimpleDateFormat("");
try {
sdf.parse("");
} catch (ParseException e) {
e.printStackTrace();
}
}
}