异常就是一个在程序执行期间发生的事件,它中断了正在执行程序的正常指令流。为了能够显示这个异常并且处理异常,这个时候我们就需要通过异常类来进行处理。
(通俗来讲就是运行的程序突然被中断了,这就是异常)
(1)自身写代码的问题。比如数组越界异常、空指针异常、算数异常等等。这些异常都是因为我们自身写代码时出的错误,所以这些错误往往是在程序代码中进行修改;
(2)java自身内部出现的错误。比如java虚拟机出现了错误;
(3)自身手动产生的异常。因为有时候有些代码段可能会出现错误,所以我们就会通过throw手动来抛出异常信息,当这个代码段真的出现错误时,我们就可以通过这个异常定位到这个代码段;
(1)Throwable:它是异常类的顶层类,它下面有两个重要的子类:Error、Exception
Error用来表示错误、Exception用来表示异常;
(2)Exception:用于程序可能出现异常的类。它可以用来自定义异常(即自己定制异常信息)
(3)Error:用于显示运行时与系统本身有关的错误
运行时异常都是 RuntimeException 类及其子类异常,它继承于Exception。这些异常一般由程序逻辑错误引起,程序应该从逻辑角度尽可能避免这类异常的发生。
比如10/0会出现异常:
编译时异常:除了运行时异常的之外的异常,类型上都属于Exception及其子类,这类异常是必须处理的异常,在程序还没有运行的时候就要进行处理的异常,如果不处理的话,编译就不能通过。
在编写程序的时候,如果程序出现错误,这个时候就需要将程序的错误信息发送给调用者,这个时候可以通过throw抛出异常,在程序运行时出现错误,调用者可以根据这个抛出的错误快速定位到错误处,进行检查解决错误;
通过抛出的错误信息,可以快速的定位到出错的为14行,这个时候可以定位到14行进行检查解决。
throws在方法参数之后声明。当程序中出现编译时异常时,自己不想处理这个异常,但是不处理这个异常又不能运行程序,这个时候就可以通过throws将异常交给方法的调用者来处理。
细节:当声明异常时,如果有多个异常用逗号进行分隔;
如果多个异常有父子关系,这个时候直接写父类异常就行了(因为父类异常里面包含了子类异常,子类异常不过是对父类异常中的异常进行了细化);
调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用throws抛出
如果程序抛出了异常,那么可以通过try来捕获它里面代码出现的异常,catch再打印异常信息
在方法实现中出现的异常,如果在实现中没有处理,那么会往调用处传递
换句话说,可以在方法实现处或方法调用处进行异常处理(捕获)
运行时异常
(1)调用处传递、实现处传递
案例
public static void main(String[] args) {
//异常传递:在方法实现中出现异常;可传递到调用处
//异常传递可在实现处或调用处处理
//案例:运行时异常传递
//1.方法实现处捕获---方法实现处后面也能执行
//2.方法调用处捕获---main方法后面可以执行
try {
a();
} catch (Exception e) {
e.printStackTrace();
}System.out.println("最后执行...");
}private static void a() {
//try {
int i=1/0;
//}catch (Exception e) {
// e.printStackTrace();
//}
System.out.println("方法实现的最后执行..");
}
(2)编译时异常传递
案例:
public static void main(String[] args) {
//异常传递:在方法实现中出现异常;可传递到调用处
//异常传递可在实现处或调用处处理
//案例:运行时异常传递
//1.方法实现处捕获---方法实现处后面也能执行
//2.方法调用处捕获---main方法后面可以执行
try {
a();
} catch (Exception e) {
e.printStackTrace();
}System.out.println("最后执行...");
}
private static void a() {
//try {
int i=1/0;
//}catch (Exception e) {
// e.printStackTrace();
//}
System.out.println("方法实现的最后执行..");
}
finally主要用在捕获中,表示无论是否捕获住,都会执行改区域的代码
无论程序是否崩溃,都会执行finally里的代码
用处:常用于关闭资源io、数据库等
try{
}catch(Exception e){
}finally{
}
try{
}finally{
}
3.finnally与return优先级的比较
结论:finally优先级更高,return之前,必须先执行finally
案例:
try {
int i = 1/0;
}catch (Exception e) {
System.out.println("捕获异常..");
return;
}finally {
System.out.println("最后..");
}System.out.println("main最后");
在项目中,如果我们需要自己定制一个异常的提示,那么我们需要自己来设计;例如ID为空的异常,通过自定义异常我们能够更明确出现的是什么问题
案例:
自定义异常类继承运行时异常类
当不符合条件的时候,抛出异常
在方法调用处try捕获异常,catch打印异常信息
注意:
自定义异常默认会继承 Exception 或者 RuntimeException
继承于 Exception 的异常默认是受查异常
继承于 RuntimeException 的异常默认是非受查异常
class StuAttrException extends RuntimeException{
public StuAttrException(String msg) {
super(msg);
}
}
//自定义异常案例:创建学生对象,属性需要进行封装,
//并判断姓名长度不能大于6;年龄必须在1~100之间
//自定义异常好处:可读性更好
class Student{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
if(name.length()>6) {
//System.out.println("姓名出现异常...重新录入");
//抛出单个对象--手动产生异常
throw new StuAttrException("姓名出现异常...重新录入");
}else {
this.name = name;
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age>100||age<=0) {
//System.out.println("年龄出现异常...重新录入");
throw new StuAttrException("年龄出现异常...重新录入");
}else {
this.age = age;
}
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
public class Test1 {
public static void main(String[] args) {
Student st = new Student();
try {
st.setName("张三丰凤飞飞纷纷");
} catch (Exception e) {
e.printStackTrace();
}
try {
st.setAge(120);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("执行最后...");
}
}
重写中的异常:不能抛出比父类更宽泛的异常
class Animal{
public void eat() throws RuntimeException {
}
}
class Dog extends Animal{
@Override
public void eat() throws RuntimeException { //此处子类不能抛Exception异常
}
}