异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。
比如说,你的代码少了一个分号,那么运行出来结果是提示是错误 java.lang.Error;如果你用System.out.println(11/0),那么你是因为你用0做了除数,会抛出 java.lang.ArithmeticException 的异常。
java中常见异常:
1.数组越界异常
public static void main(String[] args) {
int[] arr = {1,2,3,4};
System.out.println(arr[5]);
}
public static void main(String[] args) {
System.out.println(1/0);
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();//这里输入1.0
}
public static void main(String[] args) {
int[] arr = null;
System.out.println(arr.length);
}
所有的异常类是从 java.lang.Exception 类继承的子类。
1. Throwable:是异常体系的顶层类,其派生出两个重要的子类, Error 和 Exception
2. Error:指的是Java虚拟机无法解决的严重问题,比如:JVM的内部错误、资源耗尽等,典型代表:StackOverflowError和OutOfMemoryError,一旦发生回力乏术。
3. Exception:异常产生后程序员可以通过代码进行处理,使程序继续执行。比如:感冒、发烧。我们平时所说的异常就是Exception
1.检查性异常也称为编译时异常(Checked Exception):最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
2.运行时异常也称为非受检查异常(Unchecked Exception): 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。比如:NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException。
在Java中,异常处理主要的5个关键字:throw、try、catch、final、throws。
使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。Catch 语句包含要捕获异常类型的声明,使用 try/catch 的语法如下:
try
{
// 程序代码
}catch(ExceptionName e1)
{
//Catch 块
}
具体实例:
public static void main(String[] args) {
try{
int[] arr = {1,2,3};
arr[3] = 1;
}catch(ArrayIndexOutOfBoundsException e) {
e.printStackTrace();//打印异常信息
System.out.println("数组越界异常!");
}
System.out.println("out");
}
- try块内抛出异常位置之后的代码将不会被执行
- 如果抛出异常类型与catch时异常类型不匹配,即异常不会被成功捕获,也就不会被处理,继续往外抛,直到JVM收到后中断程序----异常是按照类型来捕获的
多重捕获块:
public static void main(String[] args) {
try{
int arr[] = {0,1,2};
arr[3] = 0;
System.out.println(2/arr[0]);
}catch(ArrayIndexOutOfBoundsException e) {
e.printStackTrace();//打印异常信息
System.out.println("数组越界异常!");
}catch(ArithmeticException e) {
e.printStackTrace();//打印异常信息
System.out.println("算术异常!");
}
System.out.println("out");
}
try中可能会抛出多个不同的异常对象,则必须用多个catch来捕获----即多种异常,多次捕获
捕获多个异常:
try{
}catch(ArrayIndexOutOfBoundsException | NullPointerException e) {
}
如果异常之间具有父子关系,一定是子类异常在前catch,父类异常在后catch,否则编译报错.
public static void main(String[] args) {
try{
}catch(Exception e) {
}catch(ArrayIndexOutOfBoundsException e) {
}
}
public static void main(String[] args) {
try{
}catch(Exception e) {
}
}
在编写程序时,如果程序中出现错误,此时就需要将错误的信息告知给调用者,比如:参数检测。在Java中,可以借助throw关键字,抛出一个指定的异常对象,将错误信息告知给调用者。具体语法如下:
throw关键字:
public static int fun(int[] arr,int index) {
if(index < 0 || index >= arr.length) {
throw new ArrayIndexOutOfBoundsException("数组越界!");
}
if(null == arr) {
throw new NullPointerException("数组为空!");
}
return arr[index];
}
public static void main(String[] args) {
int[] arr = {0,1,2};
System.out.println(fun(arr, 3));
}
- throw必须写在方法体内部
- 抛出的对象必须是Exception 或者 Exception 的子类对象
- 如果抛出的是 RunTimeException 或者 RunTimeException 的子类,则可以不用处理,直接交给JVM来处理
- 如果抛出的是编译时异常,用户必须处理,否则无法通过编译
- 异常一旦抛出,其后的代码就不会执行
throws关键字:
下面方法的声明抛出一个 RemoteException 异常:
public void deposit(double amount) throws RemoteException
{
throw new RemoteException();
}
一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。例如,下面的方法声明抛出 RemoteException 和 InsufficientFundsException:
public void withdraw(double amount) throws RemoteException,
InsufficientFundsException
{
}
- throws必须跟在方法的参数列表之后
- 声明的异常必须是 Exception 或者 Exception 的子类
- 方法内部如果抛出了多个异常,throws之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型
具有父子关系,直接声明父类即可。- 调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用throws抛出
在写程序时,有些特定的代码,不论程序是否发生异常,都需要执行,比如程序中打开的资源:网络连接、数据库连接、IO流等,在程序正常或者异常退出时,必须要对资源进进行回收。另外,因为异常会引发程序的跳转,可能导致有些语句执行不到,finally就是用来解决这个问题的。
public static void main(String[] args) {
try{
int[] arr = {1,2,3};
arr[3] = 1;
}catch(ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}finally {
System.out.println("finally已执行!");
}
}
相信大家有一个疑问,try-catch后面的代码会执行,为什么还要使用finally?
public static int get() {
Scanner scanner = null;
int n = 0;
try{
scanner = new Scanner(System.in);
n = scanner.nextInt();
return n;
}catch(InputMismatchException e) {
e.printStackTrace();
}finally {
System.out.println("finally!");
}
System.out.println("try-catch执行完毕!");
if(null != scanner) {
scanner.close();
}
return n;
}
public static void main(String[] args) {
int n = get();
}
当我们输入的值为整型类型时,就会出现类型泄露.
这里执行到第二步程序就终止了,没有对scanner进行释放,只有在finally中加上才能实现程序的安全性.
finally的危险性:
public static int func1() {
try{
return 1;
}finally {
return 2;
}
}
public static void main(String[] args) {
System.out.println(func1());
}
这里输出的是1还是2?
finally 执行的时机是在方法返回之前(try 或者 catch 中如果有 return 会在这个 return 之前执行 finally). 但是如果finally 中也存在 return 语句, 那么就会执行 finally 中的 return, 从而不会执行到 try 中原有的 return.
如果我们一直将异常向上抛,抛给主方法还没有处理,系统将会抛给JVM处理,异常终止程序.try中的异常如果没有catch捕捉处理也是一样的.
public static void func2() {
int[] arr = {1,2,3};
arr[3] = 1;
}
public static void main(String[] args) {
func2();
System.out.println("main未处理异常!");
}
1.程序先执行 try 中的代码
2.如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
3.如果找到匹配的异常类型, 就会执行 catch 中的代码
4.如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
5.无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
6.如果上层调用者也没有处理的了异常, 就继续向上传递.
7.一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止.
在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点:
1.所有异常都必须是 Throwable 的子类。
2.如果希望写一个检查性异常类,则需要继承 Exception 类。
3.如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
可以像下面这样定义自己的异常类:
class MyException extends Exception{
}
实现一个登录功能,自定义异常类
public class NameException extends Exception{
public NameException(String message) {
super(message);
}
}
public class PassException extends Exception{
public PassException(String message) {
super(message);
}
}
1.自定义异常通常会继承自 Exception 或者 RuntimeException
2.继承自 Exception 的异常默认是受查异常
3.继承自 RuntimeException 的异常默认是非受查异常.
private String Name = "zd";
private String Word = "000000";
public static void login(String Name,String Word)throws NameException,WordException {
if(!Name.equals(Name)) {
throw new UserNameException("用户名错误!");
}
if(!Word.equals(Word)) {
throw new PassWordException("密码错误!");
}
System.out.println("登录成功!");
}
public static void main(String[] args) {
try {
login("zd","000000");
}catch (NameException e) {
e.printStackTrace();
}catch (WordException e) {
e.printStackTrace();
}
}