异常综述
在进行项目开发的时候,程序员极致把代码写得完美,但在项目运行过程中还会出现一些问题,不是靠写代码就能避免的,是外部原因引起的。比如客户输入数据的格式,读取文件是否存在
异常
定义
将程序执行过程中发生的出乎意料情况称为异常,语法错误和逻辑错误不属于异常
对于这些异常,有两种解决方法,一种碰到错误终止程序,一种程序员在编写程序的过程中,就考虑到了程序可能出现的错误,以及错误消息的提示,并且也给出异常的处理
分类
**Error 错误, 一般指系统错误或Java虚拟机无法解决错误,栈溢出,内存不足等,**一般不进行相关代码的处理,程序员无法解决。StackOverflowError, OutOfMemoryError。如main方法里调用main(args); new Integer[1024 * 1024 * 1024];
**Exception 异常,**捕获异常最理想的是在编译期间,但有些异常是在运行时才会发生
所以分为运行期异常(RuntimeException)和非运行期异常
运行期异常,编译期不报错,运行时出错;(非受检异常)unchecked
非运行期异常,编译期就报错,不处理的话,无法运行程序。(受检异常) checked
异常情况有,空指针,读取不存在文件,除数为0,数组下标越界
Error和Exception即所有异常错误的父类是java.lang.Throwable
举例说明运行期异常和非运行期异常
运行期异常:
NullPointerException,ArrayIndexOutOfBoundsException,StringIndexOutOfBoundsException,ClassCastException,NumberFormatException,InputMismatchException(输入不匹配),ArithmeticException(除数为0)
StringIndexOutOfBoundsException
String str = "abc";
sout(str.charAt(3));
ClassCastException
Object obj = new Date();
String str = (String)obj;
NumberFormatException
String str = "123";
str = "abc";
int num = Integer.parseInt(str);
非运行期异常:
ClassNotFoundException,
IOExcepion–> FileNotFoundException
public static void main(String[] args){
FileInputStream files = new FileInutStream("C:\\Users\\Desktop\\a.docx")
//文件没有找到异常
}
异常处理方式
编写程序,如果在可能出现错误的代码地方处加上检测代码,比如判断分母是否为0,数据是否为空。使用到的if-else语句会导致程序代码变长,可读性差,因为采用异常处理机制
异常处理机制,是将异常处理的代码集中在一起,和正常的 代码分开,异常代码中不用过多的if-else解决,使得程序简洁,易于维护。
方式一
try…catch…finally 直接处理
-
把有可能出现异常的代码放到try块中,try的作用试图找到和发现异常,一旦try代码中有异常出现剩下的代码不会执行,会立即找到对应的catch代码块执行。出现异常的话,就会生成一个对应异常类的对象,根据此对象的类型,到catch中进行匹配。
-
可能会有多个catch块,一个try中的异常匹配到某个catch时,就进入catch中进行异常处理,处理完毕,跳出当前try-catch结构,继续执行其后的代码,另外的catch就不会进入了
-
catch中的异常类型如果没有父子关系,则谁声明在上面下面无所谓;若是catch中的异常类型有父子类关系,则要求将子类的声明放在父类上面,否则,编译报错,catch块达不到
-
catch块中异常对象处理方式,调用getMessage()方法,返回值为String,即可以是System.out.println(e.getMessage()),说报错原因。另一种是printStackTrace()方法,直接输入e.printStackTrace();包含报错原因,还有具体的代码报错位置。
-
finally可选,在try结构中声明的变量,出了try结构的大括号后,不能再调用
try-catch-finally处理编译时异常,使得程序在编译时不再报错,但是运行时仍可能报错,相当于使用了该结构后,将一个编译时可能出现的异常,延迟到运行时出现;
开发中,运行时异常比较常见,通常不针对此异常进行try-catch-finally处理,针对编译时异常,一定要考虑异常的处理
try{
//可能出现异常的代码
}catch (异常类型1 变量名1){
//处理异常的方式1
}catch (异常类型2 变量名2){
//处理异常的方式2
}
...
finally{
//一定会执行的代码
}
}
public static void main(String[] args) {
String str = "123";
str = "abc";
try{
int i = Integer.parseInt(str);
System.out.println(i);
}catch (NumberFormatException e){
System.out.println("数据转换异常");
}
}
- finally中声明的代码一定会被执行,即使catch块中又出现异常,try中有return语句,catch块中有return语句等情况。
try块中异常捕获处理,但catch中又出现异常
public static void main(String[] args) {
try {
int a =10;
int b = 0;
System.out.println(a / b);
} catch (Exception e) {
//e.printStackTrace();
int[] arr = new int[10];
System.out.println(arr[10]);
} finally {
System.out.println("一定会被执行到");
}
}
若不使用finally块,直接在此结构外输入最后的一定会执行语句,catch中出现异常,会导致程序终止,不会执行
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10
at com.csdn.day5.FinalllyTest.main(FinalllyTest.java:16)
而用finally处理的话,肯定会被执行到
一定会被执行到
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10
at com.csdn.day5.FinalllyTest.main(FinalllyTest.java:16)
try中有return语句,catch块中有return语句
try中没异常的话,执行完毕,返回1,有异常,进入catch块,返回2,但在返回之前必做的操作是进入finally结构中,执行代码,执行完成才会回到return语句
public class FinalllyTest {
public static void main(String[] args) {
int m = method();
System.out.println(m);
}
public static int method(){
try {
int[] arr = new int[10];
System.out.println(arr[10]);
return 1;
} catch (Exception e) {
//e.printStackTrace();
return 2;
} finally {
System.out.println("一定会被执行");
return 3;
}
}
}
一定会被执行
3
- 如数据库连接,输入输出流,网络连接Socket等资源,JVM是不能自动回收。我们需要自己手动的进行资源的释放,此时资源释放,就需要声明在finally中
- 多个catch可以合并,只需把catch的异常范围写大
方式二
throws 抛出异常,给这个方法的调用者处理
public class ExceptionTest2 {
public static void main(String[] args) {
try {
method2();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static void method2() throws FileNotFoundException {
method();
}
public static void method() throws FileNotFoundException {
File file = new File("hello.txt");
FileInputStream fis = new FileInputStream(file);
}
}
throws + 异常类型,写在方法的声明处,说的是此方法在执行时,可能会抛出的异常类型,出现异常,仍会在代码处生成一个异常类对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码,不会被执行
try结构真正的将异常解决掉,在后续调用过程中,不会再出错;而throws方式只是将异常抛给了方法的调用者,而不是真正的解决掉了异常
方法重写时,子类重写的方法抛出的异常类型不大于父类被重写的方法的异常类型
public class OverrideTest {
public static void main(String[] args) {
OverrideTest test = new OverrideTest();
test.display(new SubClass());
//实则执行的是子类的方法,它是抛出一个子类异常,若是抛出父类异常的话,catch块无法捕获到,代码出现缺陷
}
public void display(SuperClass s) {
try {
s.method();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class SuperClass{
public void method() throws IOException{
}
}
class SubClass extends SuperClass{
/*public void method(){
}*/
public void method()throws FileNotFoundException {
}
/* public void method()throws IOException{
}*/
}
开发选择使用try-catch-finally结构还是throws?
- 若父类中被重写的方法没有抛出异常即throws方式处理,那么子类重写的方法也不能使用throws方式,意味着如果子类重写的方法中有异常,必须使用try结构方式处理.
- 比如说执行的方法中,先后又调用了多个方法,这几个方法是递进关系,即上个方法中的得出的值作为下个方法的参数处理,在处理时,将这几个方法分别抛出异常,throws方式处理,在执行的方法中使用try-catch块整个包裹处理异常.这样某个方法调用出现异常,直接到catch块中,剩下代码不执行.若是方法内直接try_catch,异常已经出现,又处理掉了,可能要的数据没拿到
目前所讲的异常处理方式只是说代码在执行之前可能会出现问题,提前做的预案,万一出现问题,怎么处理,真的出现问题,有提示了,还要去做修改代码
手动抛异常
异常对象的产生,系统自动生成的异常对象;手动的生成一个异常对象,并抛出throw
public class StudentTest {
public static void main(String[] args) {
Student s = new Student();
s.regist(10010);
System.out.println(s);
}
}
class Student {
private int id;
public void regist(int id){
if(id > 0){
this.id = id;
}else{
System.out.println("输入数据非法");
}
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
'}';
}
}
Student{id=10010}
加入id是负值,如下,正常情况下应该抛出异常,而不是还输出一个默认的错误数据
public class StudentTest {
public static void main(String[] args) {
Student s = new Student();
s.regist(-10010);
System.out.println(s);
}
}
输入数据非法
Student{id=0}
这时需要自己手动的抛出异常对象,new对象时,一般是选择Exception或RuntimeException.throw new Exception时,需要显式地去处理.
public class StudentTest {
public static void main(String[] args) {
try {
Student s = new Student();
s.regist(-10010);
System.out.println(s);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
class Student {
private int id;
public void regist(int id) throws Exception {
if(id > 0){
this.id = id;
}else{
//手动生成异常对象
throw new Exception("输入数据非法");
}
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
'}';
}
}
JDK源码中可能有使用throw抛出异常的,在我们写代码时需要使用throws处理
自定义异常类
- 继承于现有的异常类,RuntimeException,Exception
- 提供全局常量
- 提供重载构造器
public class MyException extends RuntimeException{
static final long serialVersionUID = 1L;
//提供无参构造方法
public MyException() {
}
//提供有参构造方法,可自动生成
public MyException(String msg){
super(msg); //把参数传递给Throwable的带String参数的构造方法
}
}
public class StudentTest {
public static void main(String[] args) {
try {
Student s = new Student();
s.regist(-10010);
System.out.println(s);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
class Student {
private int id;
public void regist(int id){
if(id > 0){
this.id = id;
}else{
//手动生成异常对象
throw new MyException("输入数据非法");
}
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
'}';
}
}
总结:
-
有异常,try-catch-finally;无异常,try-finally,跳过catch;catch,finally至少出现一个
-
throws后面的异常可以是多个,之间用,隔开,或者合并为父异常
-
throw和throws的区别?
throw用来抛出异常的,后面跟一个具体的异常对象,执行throw则一定抛出了某种异常对象;throws是用来声明方法内可能会有异常,不一定会发生异常
throw写在方法内,throws写在方法的参数列表后面