异常机制
类型有两种:
- 非检测性异常,编译ok,运行时异常。如算术异常等,下面的代码不会执行
- 检测性异常。是编译错误,不处理就无法到达运行阶段
异常捕获
//打开c盘a.txt文件
FileInputStream fis = null;
try {
fis = new FileInputStream("d:/b.txt");
System.out.println("1");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//关闭文件
try {
fis.close();
System.out.println("2");
} catch (IOException e) {
e.printStackTrace();
}catch (NullPointerException e){
e.printStackTrace();
}
System.out.println("1111");
我们是没有b文件的
这里的第二个catch就是手动处理了异常,如果没有第二个catch的内容,异常没有被处理,则1111不会被输出。
只有一个catch的结果:
加入了catch (NullPointerException e)这行代码
catch了NullPointerException(即手动处理了异常)结果是
使用了catch语句以后成功执行了输出,也输出了异常
这里改进一下
用多态的离线,IOException和NullPointerException的父类的就是Exception,所以这里改成
public static void main(String[] args) {
//打开c盘a.txt文件
FileInputStream fis = null;
try {
fis = new FileInputStream("d:/b.txt");
System.out.println("1");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//关闭文件
try {
fis.close();
System.out.println("2");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("1111");
}
结果也是一样的
但是在实际开发中还是推荐上面那种写法,提高代码可读性
另外可以把Exception放后面,但不可以在前面,如:
public static void main(String[] args) {
//打开c盘a.txt文件
FileInputStream fis = null;
try {
fis = new FileInputStream("d:/b.txt");
System.out.println("1");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//关闭文件
try {
fis.close();
System.out.println("2");
} catch (IOException e) {
e.printStackTrace();
}catch (NullPointerException e){
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("1111");
}
是可行的
finally
try{
...
}catch{
}
...
finally{
编写无论是否发生异常都要执行的代码
}
特别注意一下这里
public static void main(String[] args) {
//打开c盘a.txt文件
FileInputStream fis = null;
try {
fis = new FileInputStream("d:/b.txt");
System.out.println("1");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//关闭文件
try {
fis.close();
System.out.println("2");
} catch (IOException e) {
e.printStackTrace();
}catch (NullPointerException e) {
e.printStackTrace();
String str = null;
str.length();//会发生空指针异常
} finally {
System.out.println("2222");
}
System.out.println("1111");
}
结果finally中语句2222被输出,而因为空指针异常没有被处理,则1111不会输出
所以finally执行一些关闭资源的操作
finally的笔试考点
public class ExceptationCatch {
public static int test(){
try{
int[] a = new int[5];
System.out.println(a[5]);
return 0;
} catch (ArrayIndexOutOfBoundsException e){
e.printStackTrace();
return 1;
} finally {
return 2;
}
}
public static void main(String[] args) {
int s1 = test();
System.out.println("返回值是:"+s1);
}
}
结果是
finally中的return会提前结束方法并返回数据
在e.printStackTrace();后就到了finally中了,不会由catch中的return结束
异常抛出
在某些情况下有些异常不能处理或不便于处理,就可以将该异常转移给方法的调用者。上面的捕获是把异常就地处理了,而这里throw只是转移,并没有处理异常。
当方法执行出现异常时,底层生成一个异常类抛出,异常代码的后续代码不再执行
且不建议在main方法中抛出异常,会直接把异常抛给java虚拟机,加重负担
public class ExceptionThrowTest {
public static void show() throws IOException {
FileInputStream fis = new FileInputStream("d:/b.txt");
System.out.println("查看是否继续向下执行");
fis.close();
}
public static void main(String[] args) {
try {
show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
结果是
补充
- 子类重写的方法可以抛出和父类一样的异常
- 子类重写的方法可以抛出和更小的异常
- 子类重写也可以不抛出异常
- 子类的重写不可以抛出和父类平级不一样的异常
- 子类的重写不可以抛出更大的异常
捕获与抛出的选择
- 若父类中被重写的方法没有抛出异常,则子类中重写的方法只能进行捕获处理
- 若一个方法内以递进方式调用了好几个其他方法(A调B,B调C,C调D),则建议这些方法内部可以使用抛出的方法,一直抛出到最外面那层再使用捕获处理
第2点看以下代码所示
public class ExceptionThrowTest {
public static void show() throws IOException {
FileInputStream fis = new FileInputStream("d:/b.txt");
System.out.println("查看是否继续向下执行");
fis.close();
}
public static void test1() throws IOException {
show();
}
public static void test2() throws IOException {
test1();
}
public static void test3() throws IOException {
test2();
}
public static void main(String[] args) {
try {
test3();
} catch (IOException e) {
e.printStackTrace();
}
}
}
自定义异常
Java可以自定义异常,而自定义异常都应该继承Exception基类
定义异常类时通常需要提供两个构造器:一个是无参数的构造器;另一个是带个字符串参数的构造器,这个字符串将作为该异常对象的描述信息(也就是异常对象的getMessage()方法的返回值
这里自定义一个年龄异常
public class AgeException extends Exception{
static final long serialVersionUID = -3387516993124229948L;//序列化版本号 与序列化操作有关系
// 1、提供一个无参构造器
public AgeException() {
}
// 2、带一个字符串参数的构造器
public AgeException(String msg) {
super(msg);
}
}
那么在父类Exception中也能使用AgeException中的内容了
上面代码中的做法是把msg内容传到父类中去,这样就新建了Exception的一个子类
自定义异常类的使用
对于上面自己定义的AgeException类
public class AgeException extends Exception {
static final long serialVersionUID = -3387516993124229948L;//序列化版本号 与序列化操作有关系
// 1、提供一个无参构造器
public AgeException() {
}
// 2、带一个字符串参数的构造器
public AgeException(String msg) {
super(msg);
}
}
我们构造一个person类
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) throws AgeException {
setName(name);
setAge(age);
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) throws AgeException {
if(age>0 && age <150) {
this.age = age;
}else{
//System.out.println("输入的年龄不合理!");
throw new AgeException("输入的年龄不合理!");
}
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Person类的测试
public class PersonTest {
public static void main(String[] args) {
Person s1 = null;
try {
s1 = new Person("XIAOMING",-30);
} catch (AgeException e) {
e.printStackTrace();
}
System.out.println("s1="+s1);
}
}
这时候就看到我们定义的AgeException的运用:
或者上面的方式也可以这样做
AgeException是不变的
Person类改为:
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) /*throws AgeException*/ {
setName(name);
setAge(age);
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) /*throws AgeException*/{
if(age>0 && age <150) {
this.age = age;
}else{
//System.out.println("输入的年龄不合理!");
try {
throw new AgeException("输入的年龄不合理!");
} catch (AgeException e) {
e.printStackTrace();
}
}
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
PersonTest类改为
public class PersonTest {
public static void main(String[] args) {
/*Person s1 = null;
try {
s1 = new Person("XIAOMING",-30);
} catch (AgeException e) {
e.printStackTrace();
}
System.out.println("s1="+s1);*/
Person s1 = new Person("XIAOMING",-30);
System.out.println("s1="+s1);
}
}
不同之处:这样的结果对象是可以new出来的(之前是null),但age的值是0
建议是用前一种方法,因为这样new出来的对象并没有什么意义