异常 是什么, 异常机制有什么作用
程序在执行过程中,发生了不正常的情况,这种情况就叫做异常
Java提供了异常处理的方法,程序执行过程中出现了不正常的情况,Java把该异常信息打印到控制太
程序员阔以根据异常信息对程序修改
异常增加了程序的健壮性
例如:
public class ExceptionTest01 {
public static void main(String[] args) {
int a=10;
int b=0;
//实际上JVM在执行到此处的时候new一个ArithmeticException类的对象: new ArithmeticException("/ by zero");
int c=a/b;
System.out.println(c);
}
}
Exception in thread "main" java.lang.ArithmeticException: / by zero
//这个信息成为异常信息,是JVM打印的
异常是以什么形式存在的?
是以类的形式存在的
异常类都可以创建
import java.lang.String;
public class ExceptionTest02 {
public static void main(String[] args) {
//通过异常类实例化异常类对象
NumberFormatException nfe = new NumberFormatException("数组格式化异常");
System.out.println(nfe);
//通过异常类实例化异常类对象
NullPointerException npe= new NullPointerException("空指针异常发生了");
System.out.println(npe);
}
}
//输出 java.lang.NumberFormatException: 数组格式化异常
//输出java.lang.NullPointerException: 空指针异常发生了
与错误的区别:发生错误,java程序只能终止程序执行,退出JVM,错误是不能处理的
Object下有Throwable接口
Throwable下有两个分支:Error(不可处理,强制退出)和Exception(可处理)
编译时异常和运行时异常,都发生在运行时,因为运行时才会才会new对象
编译时异常是因为这种异常必须在编译阶段预先处理,如不处理编译器就会报错
编译时异常和运行时异常的区别:
编译时异常发生的概率比较高
对于发生概率较高的异常,需要在运行之前进行预处理
例如:下雨天,不打伞可能就会生病。而且这个异常发生的概率很高,所以出门前需要带一把伞
拿一把伞就是对生病异常的预处理
运行时异常发生的概率比较低
例:小明走在街上会被飞机轮子砸到
被飞机轮子砸到也算一种异常,但发生的概率很低
没必要对发生这种发生概率很低的异常进行预处理
异常处理两种方式
第一种:在方法声明的位置使用throw关键字,抛给上一级(异常上抛)
第二种:使用try…catch语句进行异常的捕捉
如果选择了上抛,抛给了调用者,那么调用者也需要对这个异常进行处理,也是同样的两种方式
如果一直上抛直到JVM,那么就会终止任务
运行时异常RuntimeException
所有RuntimeException及其子类,都属于运行时异常。在编译阶段可以处理也可以不处理。
RuntimeException子类:NullPointerException, ClassCastException, NumberFormatException …
例如:
public class ExceptionTest03 {
public static void main(String[] args) {
/*程序执行到此处ArithmeticException异常(运行时异常)
底层new了一个ArithmeticException异常对象
然后抛出,由于是main方法调用100/0,
所以这个异常抛给了main方法,main方法没有处理,将这个异常抛给了JVM
JVM最终终止了程序
*/
System.out.println(100/0);
System.out.println("hello");
}
}
编译时异常ExceptionSubClass
所有ExceptionSubClass及其子类,都属于编译时异常。编译时异常是表示必须在编写程序的时候预先对这种异常进行处理
如果不处理,编译器报错。
/*
报错的原因是:doSome方法声明时用了throws ClassNotFoundException
而ClassNotFoundException是编译时异常,必须编写代码时进行处理,没有处理编译器报错
*/
public class ExceptionTest04 {
public static void main(String[] args) {
//调用doSome方法,因为doSome方法有throws ClassNotFoundException
//我们在调用doSome方法的时候必须对这种异常进行预先的处理,如果不处理,编译就会报错
//报错信息:Unhandled exception: java.lang.ClassNotFoundException
//doSome();
}
/*
在方法声明的地方使用throws ClassNotFoundException
表示在doSome种可能会出现ClassNotFoundException异常(类没有找到)编译时异常
*/
public static void doSome()throws ClassNotFoundException{
System.out.println("doSome");
}
}
异常的处理
public class ExcepotionTest05 {
/*第一种方式:使用异常上抛
public static void main(String[] args)throws ClassNotFoundException {
doSome();
}
*/
/*第二种方式:try..catch..进行捕捉
捕捉是真正的把异常拦下来,把异常处理了
public static void main(String[] args)throws ClassNotFoundException {
try {
doSome();
}catch(ClassNotFoundException e){
e.printStackTrace();
}
}
*/
public static void doSome()throws ClassNotFoundException{
System.out.println("doSome");
}
}
如果不处理编译时异常,就无法运行
import java.io.FileInputStream;
public class ExceptionTest06 {
public static void main(String[] args) {
private static void m1 () {
System.out.println("main begin");
m1();
System.out.println("main end");
}
}
private static void m1(){
System.out.println("m1 begin");
m2();
System.out.println("m1 end");
}
private static void m2() {
System.out.println("m2 begin");
m3();
System.out.println("m2 end");
}
private static void m3() {
System.out.println("m3 begin");
//创建一个输入流对象
new FileInputStream("C:\\Users\\代涵\\Desktop\\java\\TestTXT.txt");
//报错是因为public FileInputStream(String name) throws FileNotFoundException {
// this(name != null ? new File(name) : null);
// }
//本身声明就有了throws FileNotFoundException(编译时异常)
//编译时异常在编译时程序员必须对他进行处理
System.out.println("m3 end");
}
}
异常处理:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class ExceptionTest06 {
public static void main(String[] args) {
System.out.println("main begin");
m1();
System.out.println("main end");
}
private static void m1(){
System.out.println("m1 begin");
m2();//是否出现异常,都不影响这里的执行,因为异常已经在m2处理完了
System.out.println("m1 end");
}
private static void m2(){
System.out.println("m2 begin");
try {
m3();
//m3()出现异常之后,后面的语句不会执行
System.out.println("hello World!");
} catch (FileNotFoundException e) {
//出现异常之后直接进入catch语句块
System.out.println("文件不存在");
}
//catch语句块处理完异常之后接着执行下面的语句
System.out.println("m2 end");
}
private static void m3() throws FileNotFoundException {
System.out.println("m3 begin");
//创建一个输入流对象
new FileInputStream("C:\\Users\\代涵\\Desktop\\java\\TesTXT.txt");
//出异常之后,会立即上抛,并且后面的语句不会执行
System.out.println("m3 end");
}
}
catch可以是具体异常也可以是父异常
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class ExceptionTest07 {
public static void main(String[] args) {
/*try {
FileInputStream fis = new FileInputStream("C:\\Users\\代涵\\Desktop\\java\\TestTXT.txt");
}catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("文件不存在");
}
System.out.println("hello world");
*/
try {
FileInputStream fis = new FileInputStream("C:\\Users\\代涵\\Desktop\\java\\TestTXT.txt");
}catch (IOException e) { //多态,IOException是FileNotFoundException父类
System.out.println("文件不存在");
}
System.out.println("hello world");
}
}
一个try可以多个catch,建议catch精确的一个一个处理
各个catch顺序:必须要先写子类,后写父类,否者先写父类 子类就用不到了
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class ExceptionTest07 {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("C:\\Users\\代涵\\Desktop\\java\\TestTXT.txt");
fis.read();//还有异常需要处理
}catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("文件不存在");
}catch(IOException 3){
System.out.println("读文件错误");
}
System.out.println("hello world");
}
}
JDK8之后允许
catch(FileNotFoundException | ArithmeticException e){
…
}
异常对象有两个重要方法
获取简单的描述信息
打印异常追踪的堆栈信息
/*异常对象有两个重要方法
获取简单的描述信息
String msg = exception.getMessage();
打印异常追踪的堆栈信息
exception.printStackTrace();
*/
public class ExceptionTest08 {
public static void main(String[] args) {
//new了异常对象,但是没有抛出,JVM会认为这是一个简单的java对象
NullPointerException e = new NullPointerException("空指针异常");
//throw e;手动抛异常
String msg=e.getMessage();
System.out.println(msg);
//打印异常信息是在另一个线程
e.printStackTrace();
System.out.println("hello World");
}
}
例如:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class ExceptionTest09 {
public static void main(String[] args) {
try {
m1();
} catch (FileNotFoundException e) {
//IDEA自动生成会是打印堆栈信息
//实际开发中建议这个
e.printStackTrace();
}
}
private static void m1() throws FileNotFoundException {
m2();
}
private static void m2() throws FileNotFoundException {
m3();
}
private static void m3() throws FileNotFoundException {
new FileInputStream("C:\\Users\\Desktop\\java\\TestTXT.txt");
}
}
try catch finally 重点
try 、catch 和 finally
try…catch中的finally子句:
finally子句代码是最后执行的,并且一定会执行,即使try语句块中的代码出现了异常
finally子句必须和try一起出现
通常使用在:
资源的释放/关闭。因为即使try中的语句出现异常,finally中的语句也能执行
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
finally子句中的代码是最后执行的,并且一定会执行,即使try语句块中的代码出现了异常
通常使用在:
*/
public class ExceptionTest10 {
public static void main(String[] args) {
FileInputStream fis=null;
try {
//创建输入流对象
fis=new FileInputStream("C:\\Users\\代涵\\Desktop\\java\\TestTXT.txt");
//。。。
//出现空指针异常
String s=null;
s.toString();
//。。。
//流使用完毕,需要关闭
//即使以上代码出现问题,流也需要关闭。但是放在这里流就有可能关不了
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}catch(NullPointerException e) {
e.printStackTrace();
}finally {
//流的关闭放在这里比较保险
//即使try中出现了异常,也会执行
//由于fis是在try语句块中声明的,所以访问不了,应该在外面声明
if(fis!=null) {
try {
//close方法有异常,采用捕捉的方式
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
可以只有try finally没有catch
public class ExceptionTest11 {
public static void main(String[] args) {
//只有try和finally没有catch
//try不能单独使用
try{
System.out.println("try...");
return;
}finally {
System.out.println("finally...");
}
//以上代码,先执行try再执行finally最后return
System.out.println("hello"); //这里不能写,执行不到
}
}
退出JVM之后finally子句不执行
public class ExceptionTest11 {
public static void main(String[] args) {
try{
System.out.println("try...");
System.exit(0);//退出JVM
}finally {
System.out.println("finally...");//不会再执行了
}
}
}
自定义异常
实际开发中的异常,JDK中很多都是没有的,和业务相关,这是需要我们自定义
步骤
1、编写一个类继承Exception(编译时,高频)或者RuntimeException(运行时,低频)
2、提供无参构造和带String 的构造方法
public class MyExcepotion extends Exception{
public MyExcepotion(String message) {
super(message);
}
public MyExcepotion() {
}
}
异常与方法覆盖
重写之后的方法不能比重写之前的方法抛出更多(更广泛)的异常
//不能这么写
class Animals{
public void doSome(){
}
}
public class Cat extends Animals{
public void doSome() throws Exception{
}
}
可以这么写
class Animals{
public void doSome() throw Exception{
}
}
public class Cat extends Animals{
public void doSome(){ //抛不抛出都可以,也可以抛出比父类所抛出异常类范围更小的异常
}
}
throws和throw
throws在方法声明位置使用
throw手动抛出异常