Java异常
一、异常概述
1、异常的概念
public class Test{
double f(int x){
int y = 3;
int z = y/x;
return z;
}
public static void main(String args[]){
Test t = new Test();
System.out.println(t.f(0));
}
}
异常是运行时发生的错误,不是编译错误或语法错误。
异常在java中代表一个错误的实体对象。
有些可能发生的异常在我们的程序当中必须处理,有些则不需要处理。
异常有5个关键字:
try、catch、finally、throw、throws
2、异常的体系结构
Throwable:所有异常类的父类。
Error:称为错误,由Java虚拟机生成并抛出,包括动态链接失败、虚拟机错误、系统崩溃等,程序对其不作处理。
Exception:因程序编码错误或外在因素导致的问题,这些问题能够被系统捕获并进行处理,从而避免应用程序非正常中断。又可分为以下两种:
非检查型异常:编译器不要求强制处置的异常,由于编码或设计不当导致的,可以避免,RuntimeException及其所有子类都属于非检查型异常。
检查型异常:编译器要求必须处理的异常,是程序运行时因外界因素而导致的,RuntimeException及子类之外的异常均为检查型异常。
3、异常处理机制
捕获异常:try…catch…finally…
将可能产生异常的代码放在try语句中进行隔离,如果遇到异常,程序会停止执行try块的代码,调到catch块中进行处理。
抛出异常:throw、throws
当前方法不知道如何处理所出现的异常,该异常应由上一级调用者进行处理,可在定义该方法时使用throws声明抛出异常。
二、捕获异常
1、单catch处理语句
public class SingleCatchDemo{
public static void main(String[] args){
try{
int i = 10/0;
System.out.println(“i的值为:”+i);
}catch(Exception e){
//输出异常信息
e.printStackTrace();
}
System.out.println(“end”);
}
}
运行结果:
单catch处理语句执行流程:
当执行try块中的语句产生异常时,try块中剩下的代码不会再执行,而是执行catch语句;catch语句处理结束后,程序继续往下执行。因此最后的输出语句会执行,”end”字符串被输出显示,程序正常退出。
异常对象的几个常用方法:
getMessage()——返回该异常的详细描述字符串。
printStackTrace()——将该异常的跟踪栈信息输出到标准错误输出。
getStackTrace()——返回该异常的跟踪栈信息。
注意:在使用getMessage()和getStackTrace()都需要print输出才能显示,而printStackTrace()会直接输出异常。
2、多catch处理语句
执行多catch处理语句时,当异常对象被其中的一个catch语句捕获,则剩下的catch语句将不再进行匹配。所以安排catch语句的顺序时,首先应该捕获一些子类异常,然后再捕获父类异常。如Exception异常一般放在最后。
MultiCatchDemo.java:
import java.util.Scanner;
public class MultiCatchDemo{
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
int array[] = new int[3];
try{
System.out.println("请输入第1个数:");
//从键盘获取一个字符串
String str = scanner.next();
//将不是整数的字符串转换成整数,会引发NumberFormatException
int n1 = Integer.parseInt(str);
System.out.println("请输入第2个数:");
//从键盘获取一个整数
int n2 = scanner.nextInt();
//两个数相除,除数为0,会引发ArithmeticException
array[0] = n1 / n2;
//给array[3]赋值,下标越界,会引发ArrayIndexOutOfBoundsException
array[3] = n1 * n2;
System.out.println("两个数的和是" + (n1 + n2));
}catch(NumberFormatException ex){
System.out.println("数字格式化异常!");
}catch(ArithmeticException ex){
System.out.println("算术异常!");
}catch(ArrayIndexOutOfBoundsException ex){
System.out.println("下标越界异常!");
}catch(Exception ex){
System.out.println("其它未知异常!");
}
System.out.println("程序结束!");
}
}
3、try…catch…finally语句
try块是必须的,catch块和finally块是可选的,但二者至少出现其一,也可以同时出现。
try…catch…finally语句顺序不能颠倒。
除非在try或catch块中调用System.exit()方法退出应用程序,否则finally块中的代码总会被执行。
FinallyDemo:
import java.io.*;
public class FinallyDemo{
public static void main(String[] args){
FileInputStream fis = null;
try{
fis = new FileInputStream("1.txt");
}catch(IOException ioe){
System.out.println(ioe.getMessage());
}
finally{
if(fis != null){
try{
fis.close();
}catch(IOException ioe){
System.out.println(ioe.getMessage());
}
}
System.out.println("执行finally块中的资源回收!");
}
}
}
4、自动关闭资源的try语句
在上一小节中,关闭资源的代码放在finally中,Java 7开始,允许在try关键字后紧跟一堆小括号,小括号内可以声明、初始化一个或多个资源,当try语句执行结束时会自动关闭这些资源。例子:略(课本P236)
5、嵌套的try…catch语句
例子:略(课本P237)
try{
….
try{
….
}catch(异常类 A){
….
}
}catch(异常类 B ){
….
}
6、多异常捕获
例子:略(课本P239)
try{
//业务实现代码(可能发生异常)
…
}catch(异常类 A|异常类 B|异常类 C|…){
//多异常捕获处理代码
….
}
三、抛出异常
1、throw抛出异常对象
throw是用来手工抛出异常。throw语句抛出的不是异常类,而是一个异常实例对象,并且每次只能抛出一个异常实例对象。
ThrowDemo.java:
import java.util.Scanner;
public class ThrowDemo{
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
try{
System.out.println("请输入年龄:");
int age = scanner.nextInt();
if(age<0||age>80){
//抛出一个异常
throw new Exception("请输入0-80之间的年龄");
}
}catch(Exception ex){
ex.printStackTrace();
}
System.out.println("程序结束!");
}
}
throw new Exception(“###”)抛出异常对象
2、throws声明抛出异常序列
如果在方法中会有异常发生,不想在方法中直接处理,而想要由方法的调用者来处理,则可使用 throws 关键词来声明这个方法可能会抛出某种异常。在调用该方法时需要对异常进行捕获处理。
示例:
void f() throws ArithmeticException{
int z = x/y;
throw new ArithmeticException(“错误报告”);
}
ThrowsDemo:
import java.util.Scanner;
public class ThrowsDemo{
public static void myThrowsFunction() throws NumberFormatException,ArithmeticException,Exception{
Scanner scanner = new Scanner(System.in);
System.out.println("请输入第1个数:");
//从键盘获取一个字符串
String str = scanner.next();
//将不是整数的字符串转换成整数,会引发NumberFormatException
int n1 = Integer.parseInt(str);
System.out.println("请输入第2个数:");
//从键盘获取一个整数
int n2 = scanner.nextInt();
//若除数为零,会引发ArithmeticException
System.out.println("两个数相除的结果是" + n1 / n2);
}
public static void main(String[] args){
try{
myThrowsFunction();
}catch(NumberFormatException ex){
ex.printStackTrace();
}catch(ArithmeticException ex){
ex.printStackTrace();
}catch(Exception ex){
ex.printStackTrace();
}
}
}
四、自定义异常
根据业务逻辑,程序员在某些时候需要定义自己的异常类来处理一些业务。自定义异常类都继承Exception或RuntimeException类。
AgeException.java:
public class AgeException extends Exception{
public AgeException(){
}
public AgeException(String msg){
super(msg);
}
}
MyExceptionDemo.java:
import java.util.*;
public class MyExceptionDemo{
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
try{
System.out.println("请输入年龄:");
int age= scanner.nextInt();
if(age<0||age>80){
throw new AgeException("年龄不合法,必须在0-80之间");
}
}catch(AgeException ex){
ex.printStackTrace();
}
System.out.println("程序结束!");
}
}
五、垃圾收集机制
垃圾收集是将分配给对象不再使用的内存回收或释放的过程。
垃圾收集机制的特点:
(1)内存优化;
(2)动态回收:自动识别不再被程序引用的对象并回收,如果一个对象没有指向它的引用或将其赋值为null,则此对象将进行垃圾收集。
(3)回收的不确定性:优先级较低,一般在CPU空闲或者内存不足时自动进行。
(3)占用系统开销:JVM采用了不同的垃圾回收机制和回收算法以减少对系统的影响。
习题——内存解析
class Birthday {
private int day;
private int month;
private int year;
public Birthday(int d,int m,int y){
day=d;month=m;year=y;
}
public int getDay(){ return day;}
public int getMonth(){return month;}
public int getYear(){return year;}
public void setDay(int d){day=d;}
public void setMonth(int m)
{month=m;}
public void setYear(int y){year=y;}
public void display(){
System.out.println
(day+"-"+month+"-"+year);}
}
public class Test{
public static void main(String[] args){
Test test=new Test();
int date=9;
Birthday d1=new Birthday(24,1,1986);
Birthday d2=new Birthday(13,9,1984);
test.change1(date);
test.change2(d1);
test.change3(d2);
System.out.println("date="+date);
d1.display();
d2.display();
}
public void change1(int i){i=1234;}
public void change2(Birthday b){
b=new Birthday(22,2,2022);}
public void change3(Birthday b){b.setDay(22);}
}