异常类
/*
- 什么是异常(Exception)
- 异常是程序在编译或运行过程中出现的例外,这些例外有的可以避免,有的却无法避免。
- Exception类继承自Throwable类,Throwable类还有一个子类Error
- Error代表的是错误,不再是程序员编程处理的范围。
- 检查异常也称为编译期异常
- 不可避免,必须进行异常处理,要不编译器报错
- Exception以及它的子类(除去RuntimeException)
- 未检查异常也称为运行时异常
- 可以避免,不需要必须处理
- RuntimeException以及它的子类
*/
常见的异常
package SE02.n1Exception;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo01Exception {
public static void main(String[] args){
// 常见的运行时异常
// NullPointerException 空指针异常
String name=null;
System.out.println(name.toString());
// ArithmeticException 算数异常
System.out.println(1/0);
// ArrayIndexOutOfBoundsException 数组下标越界异常
String s[]=new String[2];
System.out.println(s[3]);
// ClassCastException 类型转换异常
Object o=new Integer(20);
String s= (String)o;
// 常见编译期异常
// ParseException 解析异常
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
String string="1000年2月2日";
Date d=sdf.parse(string);//需异常处理
}
}
异常处理方法
// Java编译期异常必须处理,否则编译器会提示错误,且源文件无法成功编译
// 处理方法两种
// 1.使用try_catch_finally关键字捕获并处理
// 2.使用throws关键字声明抛出异常,让别人去处理
try_catch_finally关键字
package SE02.n1Exception;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo02try_catch {
public static void main(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
String string="1000年2月2日";
try {
Date d=sdf.parse(string);//需异常处理
} catch (ParseException e) {
// TODO 自动生成的 catch 块
System.out.println("try语句中发生异常,会跳转到此语句块执行");
e.printStackTrace();//打印栈路径
}finally {
// 不论是否捕捉到异常都要执行的代码块
System.out.println("执行了finally代码块!!!");
}
}
}
在try_catch语句块中有一个特殊情况:return的用法以及细节
在try_catch_finally这三个语句块中分别执行return会有不同的细节,上代码!
package SE02.n1Exception;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo03Retrun {
public static void main(String[] args) {
System.out.println(test1());
// System.out.println(test2());
// System.out.println(test3());
// System.out.println(test4());
}
private static String test1() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
String string="1000年2月2日";//代码正常,可以被正常年月日格式化
String rStr="begin";
try {
Date d=sdf.parse(string);//需异常处理
return rStr;//上一条报错,此条不可达,不运行
}catch(ParseException e){
rStr="catch";
return rStr;//先封存,再finally,再return封存
}finally {
rStr="finally";
return rStr;
}
当return写在finally代码块中时,无论是无异常还是有异常,最后返回的都是“finally”
接下来进行分类讨论:
(1)当try语句中的代码无异常:
try中的return rStr不会立马执行,会将rStr的内存地址封存在一个空间中,等finally代码块执行完才会return封存的内存地址,再根据内存地址寻找要输出的字符串。但是,当finally代码块中有return关键字时,程序会优先执行finally代码块中的return关键字,try语句块中的return封存的内存地址将被永久封存,return的是finally代码块中的rStr。
(2)当try语句中的代码需要异常处理:
同样的是封存try中的return,进入catch,再对catch中的return进行封存,最后执行finally中的代码块,若finally中有return语句,则执行finally中的return语句,而try和catch中return封存的值将永久封存,最终输出finally中的return。
所以上面代码输出的结果为:finally
以上是finally中有return的情况
下面介绍finally中没有return的情况
话不多说先上代码!
private static String test1() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
String string="2000年2月2日";//该字符串在格式化年月日时不会抛出异常,格式正确
String rStr="begin";
try {
Date d=sdf.parse(string);//不需要异常处理
return rStr;//此条可达,执行
}catch(ParseException e){
rStr="catch";
return rStr;//先封存,再finally,再return封存
}finally {
System.out.println("这里是finally代码块");
}
}
先上输出结果: 这里是finally代码块
begin
语句依次执行try—>catch—>finally。在执行try时:Date d=sdf.parse(string)执行完毕后,执行return语句,封存rStr的内存地址(begin),跳过catch语句块,执行finally语句块中的输出语句,最后return封存中的begin
private static String test1() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
String string="2000年2月2日";//该字符串在格式化年月日时会抛出异常,非正确格式
String rStr="begin";
try {
Date d=sdf.parse(string);//需要异常处理
return rStr;//此条不可达,不执行
}catch(ParseException e){
rStr="catch";
return rStr;//先封存,再finally,再return封存
}finally {
System.out.println("这里是finally代码块");
}
}
先上输出结果: 这里是finally代码块
finally
同样,语句依次执行try—>catch—>finally。在执行try时:Date d=sdf.parse(string)会抛出异常,从而导致直接跳进catch语句块,使return不可达。进入catch语句后:此时rStr重新赋值。(注意!java中String类型的对象是固定的不变的,在本人的其他博客中有详细介绍String类型)此时的rStr和最开始定义的String rStr="begin"的内存地址是不同的,虽然变量名相同,但内存地址不同,这里的return封存的就是内存地址,所以现在封存的是catch。最后执行finally代码块的输出语句,再return rStr。
以上是return String类型的语句看不出太明显的细节
下面介绍return int类型的try_catch
private static int test2() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
String string="100年2月2日";
int i =0;
try {
Date d=sdf.parse(string);//可能需异常处理的语句
i=1;
return i;
}catch(ParseException e){
System.out.println("catch");
i=2;
System.out.println("ii");
return i;//先封存(基本数据类型的值)(八种数据类型的值可以直接放在栈中),再finally,再return封存
}finally {
i=3;
System.out.println("finally");
return i;
}
}
先上结果: finally
3
同样如果finally中有return,不管try和catch中的封存,直接执行return中的封存。
private static int test2() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
String string="100年2月2日";
int i =0;
try {
Date d=sdf.parse(string);//需异常处理
i=1;
return i;
}catch(ParseException e){
System.out.println("catch");
i=2;
System.out.println("ii");
return i;//先封存(基本数据类型的值)(八种数据类型的值可以直接放在栈中),再finally,再return封存
}finally {
i=3;
System.out.println("finally");
}
}
先上结果: finally
1
需要注意的是:finally代码块中虽然i被重新赋值,但是try中的return已经率先封存了i=1的内存地址,所以…
与String类型相似的是:先封存(基本数据类型的值)(八种数据类型的值可以直接放在栈中),再finally,再return封存
以上是int型与String类型类似,下面介绍一种比较特殊的类型
自己新建一个Class:Person类 令try_catch语句的返回值为Person
Person类
package SE02.n1Exception;
public class Person {
int age=0;
@Override
public String toString() {
return "Person [age=" + age + "]";
}
}
private static Person test3() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
String string="20**年2月2日";
Person p =new Person();//此时p.age=0
try {
Date d=sdf.parse(string);//需异常处理的代码
System.out.println("try");//若进行异常处理,则代码不可达
}catch(ParseException e){
p.age=12;
System.out.println("catch");
return p;//先封存(引用的地址值),再finally,再return封存
}finally {
// p.age=11;
System.out.println("finally");
}
return p;
}
先上结果: catch
finally
Person [age=12]
由于封存的都是p的内存地址,所以每次p的属性被重新赋值都会改变封存中的内容。所以如果finally中有有关p对象的赋值语句,return中的封存内容就会被改写(内存地址值不变)。
private static Person test3() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
String string="20**年2月2日";
Person p =new Person();
try {
Date d=sdf.parse(string);//需异常处理
System.out.println("try");
}catch(ParseException e){
p.age=12;
System.out.println("catch");
return p;//先封存(引用的地址值),再finally,再return封存
}finally {
p.age=11;
System.out.println("finally");
}
return p;
}
先上输出结果: catch
finally
Person [age=11]
当finally中有赋值语句,就会改写封存内容
以上是返回Person类,下面看StringBuilder类的相关细节
private static StringBuilder test4() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
String string="100#0年2月2日";
StringBuilder rStr=new StringBuilder("begin");
try {
Date d=sdf.parse(string);//需异常处理
return rStr;//上一条报错,此条不可达,不运行
}catch(ParseException e){
rStr.append("catch");
return rStr;//先封存,再finally,再return封存
}finally {
rStr.append("finally");
return new StringBuilder("finally");
}
}
}
先上结果: finally
原因:finally中出现return,其他语句块的封存全部失效
private static StringBuilder test4() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
String string="100#0年2月2日";
StringBuilder rStr=new StringBuilder("begin");
try {
Date d=sdf.parse(string);//需异常处理
return rStr;//上一条报错,此条不可达,不运行
}catch(ParseException e){
rStr.append("catch");
return rStr;//先封存,再finally,再return封存
}finally {
rStr.append("finally");
}
}
}
注:有关StringBuilder的相关介绍和方法介绍在本人其他博客中有详解,请客官移步。
先上输出结果:begincatchfinally
从输出结果中可以看出,rStr游历了所有语句块,同样的是return封存了内存地址,而rStr对象中的属性改变,return出的rStr依旧会是append后的结果