Java基础-异常处理
- 异常概述与异常体系结构
- 常见异常
- 异常处理机制一:try-catch-finally
- 异常处理机制二:throws
- 方法重写时抛异常的规则
- 手动抛出异常:throw
- 用户自定义异常类
异常概述与异常体系结构
Error:
Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError和OOM。
一般不编写针对性的代码进行处理。
Exception:
其它因编程错误或偶然的外在因素导致的一般性问题
可以使用针对性的代码进行处理。例如:
空指针访问
试图读取不存在的文件
网络连接中断
数组角标越界
一、异常体系结构:
java.lang.Throwable
1、java.lang.Error:一般不编写针对性的代码进行处理。
2、java.lang.Exception:可以进行异常处理
2.1:编译时异常(checked)
> IOException(IO异常)
>> FileNotFoundException(无法找到文件异常)
> ClassNotFoundException(无法找到类异常)
2.2:运行时异常(unchecked/RuntimeException)
> NullPointerException(空指针异常)
> ArrayIndexOutOfBoundsException(数组角标越界异常)
> ClassCastException (类型转换异常)
> NumberFormatException(数字格式异常)
> InputMismatchException (输入不匹配异常)
> ArithmeticException (算术异常)
常见异常
面试题:常见的异常都有哪些?举例说明
Error:
public static void main(String[] args) {
1、栈溢出:Exception in thread “main” java.lang.StackOverflowError
main(args);
2、堆溢出:Exception in thread “main” java.lang.OutOfMemoryError
Integer[] arr = new Integer[102410241024];
}
//Exception:
@Test
//NullPointerException(空指针异常)
public void NullPointerExceptionTest(){
int[] arr = new int[3];
arr = null;
System.out.println(arr[0]);//NullPointerException
String str = "abc";
str = null;
System.out.println(str.charAt(0));//NullPointerException
}
@Test
//ArrayIndexOfBoundsException(数组角标越界异常)
public void ArrayIndexOfBoundsExceptionTest(){
int[] arr = new int[10];
System.out.println(arr[10]);//ArrayIndexOfBoundsException
String str = "abc";
System.out.println(str.charAt(3));//StringIndexOutOfBoundsException(字符串角标越界)
}
@Test
//ClassCastException(类型转换异常)
public void ClassCastExceptionTest(){
Object obj = new Date();
String str = (String)obj;//ClassCastException --->面向对象中2-向下转型有提到过。(Man和Woman两个子类互相转型,编译通过
//运行不通过 )
}
@Test
//NumberFormatException(数字格式异常)
public void NumberFormatExceptionTest(){
String str = "123";
str = "abc";
int i = Integer.parseInt(str);//NumberFormatException
}
@Test
//InputMismatchException(输入不匹配异常)
public void InputMismatchExceptionTest(){
Scanner scan = new Scanner(System.in);
int i = scan.nextInt();//没有输入数值类型,而是输入了abc导致InputMismatchException
scan.close();
}
@Test
//ArithmeticException (算术异常)
public void ArithmeticExceptionTest(){
int a = 10;
int b = 0;
System.out.println(a/b);//ArithmeticException
}
异常处理机制一:try-catch-finally
异常处理机制一:try-catch-finally
一、异常的处理:抓抛模型
过程一:"抛":程序在正常执行过程中,一旦出现异常,就会在异常处生成一个对应的异常类对象(如果没有手动生成,系统就会生成默认参数的异常对象)。
并将此对象抛出。
一旦抛出对象后,其后的代码就不再执行。
关于异常对象的生成:①系统自动生成的异常。
②手动生成的一个异常对象,并抛出(throw)。
过程二:"抓":可以理解为异常的处理方式:① try-catch-finally ②throws
二、try-catch-finally的使用
try{
//可能会出现异常的代码
}catch(异常类型1 变量名1){
//处理异常的方式1
}catch(异常类型2 变量名2){
//处理异常的方式2
}catch(异常类型3 变量名3){
//处理异常的方式3
}
......
finally{
//一定会执行的代码
}
说明:1、finally是可选的
2、使用try将可能出现异常的代码包装起来,在执行过程中,一旦出现异常,系统就会在其位置生成一个对应异常类的对象,
(如果没有手动生成,系统就会生成默认参数的异常对象)
根据此对象的类型,去catch中进行匹配。
3、一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常处理。一旦处理完成,
就跳出当前的try-catch结构(在没有写finally情况下)。继续执行其后的(解决异常后的)代码。
4、catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓。
catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错。
5、常用的异常对象处理方式:①String getMessage() ②printStackTrace()(打印堆栈跟踪,包含getMessage信息)
6、在try结构中声明的变量,在出了try结构以后,就不能再调用。
7、try-catch-finally结构可以嵌套。
体会1:使用try-catch-finally处理编译时异常,使得程序在编译时就不再报错,但是运行时仍可能报错。
相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。
体会2:开发中,由于运行时异常比较常见,所以我们通常不针对运行时异常编写try-catch-finally了。
针对于编译时异常,我们说一定要考虑异常处理。
try-catch-finally中finally的使用
1、finally是可选的
2、finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中有return语句,catch中有return语句等情况。
3、像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动回收的,我们需要自己手动的进行资源的释放。
此时的释放,就需要声明在finally中。# 异常处理机制二:throws
@Test
public void NumberFormatExceptionTest1(){
String str = "123";
str = "abc";
int i = 0; //声明在try结构外面,方便出了try后调用。
try {
i = Integer.parseInt(str); //此行系统自动生成了默认参数的NumberFormatException异常类型的对象
System.out.println("Test1-1");//后续代码,无法执行
}catch (NullPointerException e){
System.out.println("出现空指针异常!");
}catch (NumberFormatException e){ //匹配到了
System.out.println("出现数值转换异常!");//处理完成
//常用异常处理方法1:String getMessage()
//System.out.println(e.getMessage());
//常用异常处理方法2:printStackTrace()
//e.printStackTrace();
}catch (Exception e){
System.out.println("出现异常!");//父类不能在子类的上面,否则报错!
}
//跳出try-catch结构
System.out.println(i);//继续执行代码
System.out.println("Test-2");
}
@Test
//将编译时异常,延迟到运行时异常
public void CheckedTest(){
FileInputStream fis = null;
try {
File file = new File("hello.txt");
fis = new FileInputStream(file);
int data = fis.read();
while(data != -1){
System.out.println((char)data);
data = fis.read();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
try {
if(fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//try-catch-finally中finally的使用:
@Test
public void FinallyTest1(){
try {
int a = 10;
int b = 0;
System.out.println(a/b);
} catch (ArithmeticException e) {
e.printStackTrace();
int[] arr = new int[10];
System.out.println(arr[10]);//匹配到这这里的catch中,又出现异常。
}catch(Exception e){
e.printStackTrace();
}
//System.out.println("FinallyTest1");//不会被执行
finally {
System.out.println("FinallyTest1"); //一定会被执行。
}
}
@Test
public void Test(){
int num = FinallyTest2();
System.out.println(num);
}
public int FinallyTest2(){
try {
int[] arr = new int[10];
System.out.println(arr[10]); //系统自动生成默认参数的常类对象
return 1;
} catch (ArrayIndexOutOfBoundsException e) { //匹配到异常对象,执行语句。
e.printStackTrace();//执行打印堆栈跟踪
return 2;//在return 2,之前需要先执行finally中的语句
} finally {
System.out.println("FinallyTest2"); //执行输出语句
return 3; //执行return 3,结束try-catch-finally结构。使得不能再执行catch中的return 2。
}
}
异常处理机制二:throws
异常处理机制二:throws + 异常类型1,异常类型2, …
1、"throws 异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
一旦方法体执行时,出现异常,仍会在异常代码处生成一个异常对象,此对象满足throws的异常类型时,就会被抛出。
异常代码后续的代码,就不再执行了。
2、体会:try-catch-finally:真正的将异常处理掉了。
throws:只是将异常抛给了方法的调用者。并没有真正的将异常处理掉。
3、开发中如何选择使用try-catch-finally 还是使用throws?
3.1 如果父类中被重写的方法,没有throws方式处理异常,则子类重写的方法也不能使用throws。意味着如果子类重写的
方法中有异常,必须使用try-catch-finally方式处理。
3.2 执行的方法中,先后又调用了几个方法,这几个方法是递进关系执行的,建议这几个方法用throws处理。
而执行的方法a可以考虑用try-catch-finally方式处理。
//异常源
public static void ThrowsTest() throws FileNotFoundException,IOException{ //抛出异常
File file = new File("hello.txt"); //系统自动生成默认参数的FileNotFoundException异常类对象
//后续代码不再执行
FileInputStream fis = new FileInputStream(file);
int data = fis.read();
while(data != -1) {
System.out.println((char) data);
data = fis.read();
}
System.out.println("ThrowsTest");
fis.close();
}
//抛出异常,不处理
public static void method1() throws FileNotFoundException,IOException { //抛出异常
ThrowsTest();
}
//继续抛出异常,不处理
public static void method2() throws IOException { //如果异常处理方式是一样的,异常类又是子父类关系,可以直接合并为父类异常
method1();
}
//处理掉了异常
public static void method3(){
try {
method2();
} catch (IOException e) {
e.printStackTrace();
}
}
//到main方法就不能再抛异常了,虽然编译通过,但是JVM可能挂掉,所以必须try-catch掉
// public static void main(String[] args) {
//
// try { //必须try-catch处理掉
// method2();
// } catch (IOException e) {
// e.printStackTrace();
// }
//
// method3(); //method3方法中处理掉了异常,main方法调用method3方法就不用try-catch了
// }
方法重写时抛异常的规则
方法重写的规则:
子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型。
(如果父类方法没有抛出异常,子类重写父类的方法就不能抛出异常)
public void ExceptionOveride(ExceptionFather f){ //可以多态出传入父类、子类
try {
f.method(); //自动生成的try-catch结构,如果被try的方法有throws异常,则catch会以方法所抛出的异常类型为
// catch的形参。如果所有catch的形参类型,不是被try的方法所抛出的异常类型或其父类,则会报错。
} catch (IOException e) { //因为多态,可能会传入父类或子类的对象。如果子类抛出异常大于父类抛出的,则子类的异常
// 就可能无法多态的匹配catch的形参异常类型了,因为catch是根据父类方法抛出异常而生成形参的。
// 所以子类重写方法抛出的异常也只能是父类抛出的异常类型或异常类型的子类。
// (可以理解为多态时,子类可以传入父类类型的形参,但父类不能传入子类类型的形参,异常也是对象
// 重写方法时,他们跟着各自的父子类对象。所以多态对象时,如果方法有异常,也要多态生成的异常对象。)
e.printStackTrace();
}
}
@Test
public void ExceptionOverideTest(){
ExceptionOveride(new ExceptionFather());
ExceptionOveride(new ExceptionSon());
//Error: Exceptiongirl中的method()无法覆盖ExceptionFather中的method()被覆盖的方法未抛出java.lang.Exception
//ExceptionOveride(new ExceptionGirl());
}
//--------------------------- 类 -------------------------------------------------
//方法重写时抛异常的规则 -----ExceptionFather类
class ExceptionFather{
public void method() throws IOException{
}
}
//ExceptionSon类(ExceptionFather类的子类)
class ExceptionSon extends ExceptionFather{
@Override
public void method() throws IOException { //重写父类方法抛出的异常类型不能大于父类抛出的异常类型。也可以不抛出异常。
}
}
//ExceptionGirl类(ExceptionFather类的子类)
class ExceptionGirl extends ExceptionFather{
// @Override
// public void method() throws Exception { //错误:这里重写的异常类型大于父类抛出的异常类型。
//
// }
}
手动抛出异常:throw
1、手动通过throw 来调用异常类型的构造器。throw new 异常类型(String Message)。
2、如果抛出异常,必须是异常类型本身或它的父类。
3、形参列表就是,异常对象.getMessage的值,它是String类型的。
4、throw new的异常对象决定方法所抛出的异常类型范围(不小于throw),
而方法所抛出的异常类型决定catch形参的异常类型范围(不小于throws)。
private int id;
public void regist(int id) throws Exception{
if(id > 0){
this.id = id;
}else{
// System.out.println("您输入的数据非法!"); //此代码执行后,输出语句还是会调用toString还是会输出。
throw new RuntimeException("您输入的数据非法!"); //运行时异常,运行时弹出异常提示,方法结束,输出语句不会调用toString;
} //形参列表是String型的,传入的就是getMessage的值。
}
@Test
public void test(){
异常处理 std = new 异常处理();
try {
std.regist(-1001); //这里出现异常,会生成我们手动new的异常对象。如果没有手动new,那系统会自动匹配生成默认参数的异常对象。
System.out.println(std);
} catch (Exception e) { //catch自动生成的话,会匹配我们throws的异常类型。
System.out.println(e.getMessage());
}
}
//重写toString方法
@Override
public String toString() {
return "student{" +
"id=" + id +
'}';
}
用户自定义异常类
如何自定义异常类?
1、继承于现在有异常结构:RuntimeException、Exception
2、提供全局常量:serialVersionUID
3、重载的构造器
private int id2;
public void regist2(int id) {
if(id > 0){
id2 = id;
}else{
throw new MyException("您输入的是负数!");
}
}
@Test
public void test2(){
异常处理 std = new 异常处理();
try {
std.regist2(-1001);
System.out.println(std);
} catch (RuntimeException e) {
System.out.println(e.getMessage());
}
}
}
//-------------------- 类 ------------------------------
//自定义异常类
class MyException extends RuntimeException{
static final long serialVersionUID = -70348971166939L;
public MyException() {
}
public MyException(String message) {
super(message);
}
}