异常:
1、 异常处理概述
2、 异常的捕获和处理
3、 Java异常API
异常处理概述:
异常是方法的意外结果。
1、使用返回值状态标识异常
这个方法有很多坏处:首先,返回值可以是任意的,需要调用API的程序自己判断并解释返回值的含义;没有机制保证异常一定会得到处理,需要检测返回值并处理异常情况;这种方式还会造成程序代码冗长。
2、异常处理机制
3、Throwable、Error和Exception
Java异常结构中定义有Throwable类,Exception和Error是其派生的两个子类。其中Exception是可以修复的异常,Error表示Java运行时环境出现的错误,例如:JVM内存资源耗尽等等,是不可恢复的错误。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class Demo {
public static void main(String[] args){
//从控制台输入一个时间(yyyy-M-d)
//parse(String)方法就声明了异常
//如果输入的字符串格式不正确,就抛出异常
//处理这个异常:规则,如果输入时间不正确,就使用当前系统时间作为输入结果。
Scanner in=new Scanner(System.in);
System.out.println("输入日期:");
String str=in.nextLine();
SimpleDateFormat fmt=new SimpleDateFormat("yyyy-M-d");
Date date;
try {
//parse方法就是有异常声明的方法
//如果parse执行一旦出现异常,将跳到catch执行
date=fmt.parse(str);//会抛出异常
System.out.println("解析正确!");
}catch(ParseException e) {
System.out.println("解析错误!"+e.getMessage());
date=new Date();
}
//Java编译器进行异常的语法检查,如果调用了有异常抛出的方法,就必须处理异常,
//而异常的处理方式有两种:1、使用try-catch处理,2、将异常抛出。
//违反了规则,将有编译错误,现在就是编译错误。
System.out.println(fmt.format(date));
}
}
4、Try-catch
Try程序块若是有异常发生,程序的运行便中断,并抛出“异常类所产生的对象”,抛出的对象如果属于catch()括号内欲捕获的异常类,catch则会捕捉此异常,然后进入到catch块里继续执行。
每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常。Catch捕获的异常类型由上至下的捕获异常类型的顺序应该是子类到父类。子类型捕获异常在前,父类型捕获异常在后,这样的顺序依次捕获,否则编译不通过。
5、Finally的作用:
无论try所指定的程序块中是否抛出异常,finally所指定的代码都要被执行,通常在finally语句中可以进行资源的释放工作,如关闭打开的文件、删除临时文件等。
public class testFinally {
public static void main(String[] args) {
System.out.println(t("55"));
System.out.println(t(""));//空字符串,长度为0
System.out.println(t(null));//字符串为空字符串
}
public static int t(String str) {
try {
char c=str.charAt(0);//'5'
return c-'0';//'5'-'0'
}catch(NullPointerException e){
return -2;
}catch(RuntimeException e){//是StringIndexOutofBoundsException的父类
return -1;
}finally {//无论try是否发生异常都会执行的代码
//结果:100 100 100
//每次return后的结果都在缓冲区,然后finally一执行,缓冲区中的内容就被改写了,所以返回三个100
//通常不能这么写
//在最后执行的代码
return 100;
}
}
}
6、throw关键字
当程序发生错误而无法处理的时候,会抛出对应的异常对象,除此之外,在某些时刻,您可能会想要自行抛出异常,例如在异常处理结束后,再将异常抛出,让下一层异常处理块来捕捉,若想要自行抛出异常,您可以使用“throw”关键字,并生成指定的异常对象后抛出。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class testThrow {
public static void main(String[] args) {
String id="12345619990101001x";
try {
Date date=dayOfBirth(id);
System.out.println(date);
} catch (ParseException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//根据一个人的身份证号码,计算出生日
//throws:表示这个方法在执行期间可能有意外,如果没有意外,正常返回Date
//throws:用于在方法上声明异常
//throw:用于在方法中如果出现意外情况抛出异常
//throw会使方法结束,提前返回
public static Date dayOfBirth(String id) throws Exception,ParseException{
//处理不了的情况就抛掉
if(id==null) {
throw new Exception("有木有搞错!");
}
if(!id.matches("^\\d{17}[0-9Xx]$")) {
throw new Exception("还有错误!");
}
String s=id.substring(6, 6+8);
SimpleDateFormat fmt=new SimpleDateFormat("yyyyMMdd");
Date date=fmt.parse(s);//parse作用是将时间转换为java时间,处理异常ParseException
return date;
}
}
总结:
1、异常:一段代码的意外结果,不是正常结果
2、学习异常:对于意外结果的处理语法和处理规范
3、关键字:
try{}catch{}finally{}:用于捕获异常
throws:在方法(构造器)上声明这个方法在执行期间可能出现意外结果。
throw:在软件中如果出现了意外情况,就抛出异常对象,使方法异常结束。
4、编译规则:
1、如果调用了有异常(可检查异常)抛出的方法,就必须处理异常,否则有编译错误,有两种处理办法。
A、try捕获异常
B、使用throws声明抛出异常
2、处理习惯:上面两种处理办法选择哪种处理办法
A、如果是代码中能够处理的异常,就尽量处理,如果不能处理一定抛出。要依据业务规则处理。
B、异常不能随便丢弃。
C、打印异常中方法调用跟踪栈
e.printStackTrace()
出异常好找到位置
Java异常:
1、Java异常
Java异常分为可检测异常和非检测异常
1、 可检测异常
可检测异常经编译器验证,对于声明抛出异常的任何方法,编译器将强制执行处理或声明规则,不捕捉这个异常,编译器就通不过,不允许编译。
2、 非检测异常
非检测异常不遵循处理或声明规则。在产生此类异常时,不一定非要采取任何适当操作,编译器会检查是否已经解决了这样一个异常。
2、RuntimeException
这类异常属于非检测异常,因为普通JVM操作引起的运行时异常随时可能发生,此类异常一般是由特定操作引发。但这些操作在java应用程序中会频繁出现。因此它们不受编译器检查与处理或声明规则的限制。
代码见:Day10.TestCase中的testInteger()
3、异常的分类(继承关系)
Throwable 父类是Object
|---Error
| |---OutOfMemoryError
| |---…
|---Exception
|---IOException检查异常
| |---FileNotFoundException
| |---EOFException
|---CopyException 自定义异常
|---RuntimeException 非检查异常,编译器不检查
| |---NumberFormatException当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当的格式时,抛出该异常
| |---NullPointerException引用变量值为空
| |---ArrayIndexOutOfBoundsException 当使用的数组下标超过数组允许范围时,向负方向越界,抛出该异常
| |---IndexOutOfBoundsException向正方向越界
| |---StringIndexOutOfBoundsException
| |---IllegalArgumentException抛出的异常表明向方法传递了一个不合法或不正确的参数
| |---ClassCastException当试图将对象强制转换为不是实例的子类时,抛出该异常
public void testNullPointer() {
String[] names=new String[5];
//{null,null,null,null,null}自动初始化为null
System.out.println(names[0].length());//出现NullPointerException,因为names[0]是null
System.out.println(names.length);//数组的长度,是5
}
public void testIndexOutBounds() {
ArrayList<String> names=new ArrayList<String>();
System.out.println(names.size());//0
System.out.println(names.get(-1));//ArrayIndexOutOfBoundsException,负方向越界
System.out.println(names.get(1));//IndexOutOfBoundsException,正方向越界
}
public void testClassCast() {
Object o=4.5;//自动包装为Double类型的对象
//会出现ClassCastException,因为在运行期间不能将Double类型强制转换为Integer类型
int i=(Integer)o;//ClassCastException
}
Exception常用API:
1、 printStackTrace
原则上但凡遇到异常都要打印这个方法执行的堆栈。
Throwable中定义了一个方法可以输出错误信息,用来跟踪异常事件发生时执行堆栈的内容。}
2、 getMessage
Throwable中定义了一个方法可以得到有关异常事件的信息。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class TestprintStackException {
public static void main(String[] args) {
//从控制台输入日期:
Scanner sc=new Scanner(System.in);
System.out.println("请输入日期:");
String in=sc.nextLine();
SimpleDateFormat fmt=new SimpleDateFormat("yyyy-M-d");
try {
Date d=fmt.parse(in);//转换为java时间
} catch (ParseException e) {
System.out.println(e.getMessage());//返回异常的信息
e.printStackTrace();
//如果不加上面这句,就不能简单粗暴的捕获并丢弃异常!
}
}
}
3、 getCause
一个异常由另一个异常引起的时候,使用getCause来获取原因
自定义Exception:
1、 用户自定义异常:
1、要从Exception继承
2、要增加构造器,与父类型一样的构造器
3、一般不需要填写任何方法,父类型继承的方法够用了
/**
* 复制文件
* @param src
* @param dst
* @throws CopyException 复制失败时候抛出
*/
public static void copy(String src,String dst) throws CopyException{
//CopyException是自己定义的异常
File srcFile=new File(src);
File dstFile=new File(dst);
if(!srcFile.exists()) {
throw new CopyException("木有源文件"+src);
}
if(srcFile.isDirectory()) {
throw new CopyException("源文件是目录!");
}
if(dstFile.exists()&&dstFile.isDirectory()) {
dstFile=new File(dstFile,srcFile.getName());
}
//目标文件是文件的时候,就把内容复制
FileInputStream in=null;
FileOutputStream out=null;
try {
in=new FileInputStream(srcFile);//把文件打开
out=new FileOutputStream(dstFile);
byte[] buf=new byte[1024*8];
int n;
while((n=in.read())!=-1) {
out.write(buf, 0, n);
}
}catch(IOException e) {
e.printStackTrace();
//抛出带有根本原因e的CopyException异常
throw new CopyException("复制失败!",e);
}finally {
try {
if(in!=null) {
in.close();
}
if(out!=null) {
out.close();
}
}catch(IOException e) {
//在打开或关闭文件时候的异常,不需要抛出的异常,这个异常没有办法处理!
e.printStackTrace();
}
}
}
}
public void testCopy() {
String src="E:/1.rar";
String dst="Z:/";//Z盘是光驱,只能读不能写
//以上测试案例,会产生有根本原因的异常
try {
FileUtils.copy(src, dst);
} catch (CopyException e) {
e.printStackTrace();
System.out.println(e.getCause());
//根本原因:Caused by: java.io.FileNotFoundException: Z:\ (设备未就绪。)
}
}
方法的异常声明方式:有两种
1、利用返回值来说明异常
int read()方法
正常返回:0~255
异常情况:当返回-1时候,表示读取文件EOF
2、利用异常声明,说明异常情况
1、reatInt()方法:
正常返回:文件中连续4个byte拼接为一个整数
异常情况:当读取到文件末尾或者不够4个byte抛出异常EOFException
public void testReadInt() throws Exception {
String file="int.bin";
RandomAccessFile raf=new RandomAccessFile(file,"rw");//可以写完不关闭然后读取
for(int i=100;i<500;i+=100) {
raf.writeInt(i);//把4个整数写到文件中
}
raf.seek(0);//移动文件指针到文件头
try {
while(true) {//循环使用异常结束
int i=raf.readInt();//读取整数
System.out.println(i);
}
}catch(EOFException e) {
System.out.println("读取结束!");
}
raf.close();
}
}
2、readObject()方法:
正常返回:返回一个对象
异常情况:抛出异常EOFException 读取到文件末尾了
总结:
1、 异常:
异常:异常的处理规则选择依赖于具体的业务场景!
原则:能够处理尽量处理,不能处理一定抛出。
处理:try catch
抛出:throws 在方法声明,throw 抛出异常对象
2、 异常分类:
1、 Error:系统不可恢复的错误:是应用程序没有机会处理的错误,一旦出现当前系统已经崩溃掉了。
2、 Exception:系统可恢复的异常:应用程序可以编程处理,处理以后可以继续执行。
3、 RuntimeException:
非检查异常:Java编译器会忽略掉这类(子类)异常的调用和抛出语法检查!