异常的体系结构
在上图中,Error和Exception都是Throwable的子类。Error类代表致命的错误,Exception代表非致命的错误。
异常的捕获和处理
try {
URL url = new URL("ssadad");
System.out.println("121111111");
//BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//String content = br.readLine();
} /*catch (xxxxxException | IOException e) {//这是java1.7之后提供新的处理异常
//将多个不同的异常有相同的处理,需要注意子类异常和父类不能同时存在,这种处理的方式更加适合多个异常采用相同的方法处理。
// TODO Auto-generated catch block
e.printStackTrace();
} */catch (MalformedURLException e) {//这种处理异常,只要是捕获的,不关父类、子类异常都可以存在
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("不是一个正常的url");
}
注意:在try中一旦发现异常,就立马去捕获,try后面的语句不会执行了。
运行时匹配多个catch
- 抛出的异常对象被catch捕获,catch块指定了法神异常类和其父类。
- 存在多个catch块的情况下,按照它们被指定的顺序依次进行匹配,如果找到catch块,其余的catch块将忽略。
- 如果先捕获了父类catch,那么子类的catch将永远捕获不到。
finally
try {
} catch (Exception e) {
// TODO: handle exception
}finally {
}
//这是一套流程,异常,捕获异常,最终关闭不需要的资源。
try {
} finally {
// TODO: handle finally clause
}
//这个主要用来就是关闭资源。
try {
try {
} finally {
// TODO: handle finally clause
}
} catch (Exception e) {
// TODO: handle exception
}
//这样的写法,主要解决如果在关闭资源的时候也出现异常,
//而finally里面是不能处理的,这样里面的try和finally出现异常都由catch去处理。
finally与return的解惑
public static void main(String[] args) {
//按照顺序走完
String result = Test60.show();
System.out.println(result);
}
public static String show() {
/*try {
URL url = new URL("laoqiang");
System.out.println("我是try");
return "我是try中return";
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
System.out.println("我是catch");
e.printStackTrace();
}finally {
System.out.println("我是finally");
return "我是finally中return";
}*/
/*try {
System.out.println("我是try");
return "我是try中return";
} finally {
System.out.println("我是finally");
return "我是finally中return";
}*/
//这个执行过程应该是这样的,先执行try中语句,
//当执行到return前面的时候,这时程序进入了finally,而此时finally有return
//所以程序提前结束了,如果finally中没有return语句,那么重点就来了,程序会执行try中
//return来结束。
try {
URL url = new URL("laoqiang");
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
System.out.println("我是catch");
e.printStackTrace();
return "我是catch的finally";
} finally {
System.out.println("我是finally");
return "我是finally中return";
}
//这个执行过程应该是这样的,捕获异常,先执行catch中语句,
//当执行到return前面的时候,这时程序进入了finally,而此时finally有return
//所以程序提前结束了,如果finally中没有return语句,那么重点就来了,程序会执行catch中
//return来结束。这个没办法验证,因为finally返回去掉,编译是不能通过,
//它会默认你的方法没有返回值,而你在catch写返回,前提要捕获的,所以暂时没有方法验证,先记住结论吧。
}
总结一下:任何执行try 或者catch中的return语句之前,都会先执行finally语句,如果finally存在的话。
如果finally中有return语句,那么程序就return了,程序就提前结束。如果finally没有return,那么会重新执行try或者catch中的return ,所以finally中的return是一定会被return的,
编译器把finally中的return实现为一个warning。
一个小例子:
public static int show() {
int i = 7;
try {
int b = i++;
//return b;//7
return i;//8
} finally {
// TODO: handle finally clause
++i;
}
}
//在try语句中,在执行return语句时,要返回的结果已经准备好了,就在此时,程序转到finally执行了。
在转去之前,try中先把要返回的结果存放到不同于x的局部变量中去,执行完finally之后,在从中取出返回结果,
因此,即使finally中对变量x进行了改变,但是不会影响返回结果。
它应该使用栈保存返回值。
上述关于finally和return的知识,借鉴了http://www.cnblogs.com/fery/p/4709841.html
温故而知新
private int i = 7;//错误的申明方式
我们可能在为啥我们平常定义就没有问题,这里要看大的前提,如果这是定义在一个类里面,那么这就是一个属性,如果是定义在一个方法中,那么这就是临时变量。而修饰符是用来修饰属性、类、方法的,临时变量只是在方法执行的时候用一下,就好了,没必要用修饰符修饰。明白!
使用try/catch/finally
- try块可以有零个或者多个catch块,但是只有一个finally。
- catch块和finally块必须总是结合try一起出现。
- catch块或finally必须跟随每一个try。
java 1.7资源关闭
上面讲述了利用finally可以实现文件的关闭,但是在java 1.7中提供了新的方法去自动关闭。
public static void main(String[] args) {
try( BufferedReader br = new BufferedReader(new InputStreamReader(System.in))){
System.out.println(br.readLine());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
我们将需要关闭的资源,放到try后面,不需要我们手动关闭,文件将在main方法的末尾自动关闭。如果要关闭多个只要用分号隔开,就好。但是这个方法有局限,就是每一个资源要实现了java.lange.AutoCloseable。
异常的分类
- 被检查异常
这种异常也被称之为编译时异常,如果不处理的话,编译是不会通过。
- 未检查异常
这种异常在编译的时候是不需要进行处理,未检查异常是程序在运行期间可能出现的异常,所以不会轻质程序员捕获异常或者在throws子句申明异常,但是它可能会让程序异常终止。
Throws
这个关键字,可以将方法中产生异常的部分,交给调用者进行集中处理,这样就不会让大量异常处理的程序代码,弄乱的的逻辑关系。
public static void main(String[] args) {
try {
Test63.showUrl();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void showUrl() throws MalformedURLException {
URL url = new URL("jjjj");
}
自定义异常
当你发现标准异常类给出的异常信息不是很直观,并且有必要提供更详细的消息给用户,这时就可以采用自定义异常。
public class TooHotException extends Exception{
public TooHotException(String message) {
//这个构造函数,指定信息
super(message);
// TODO Auto-generated constructor stub
}
}
public class TooColdException extends Exception {
public TooColdException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
}
public class Test64 {
public static void main(String[] args) {
for(int i = 1;i<=10;i++) {
System.out.println("today is"+i+"day");
try {
Test64.judgeTemperature();
} catch (TooHotException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
System.out.println(e.getMessage());
} catch (TooColdException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
public static void judgeTemperature() throws TooHotException, TooColdException {
double temperature = (Math.random())*100;
System.out.println(temperature);
if(temperature>37) {
throw new TooHotException("too hot");
}else if(temperature<-22) {
throw new TooColdException("too cold");
}else {
System.out.println("it is very nice");
}
}
}
Throw
当你构造了用户自定义的异常实例,Java运行时无法检测和创建用户自定义异常的实例,所以必须有某种机制传递异常给调用者,Java提供了throw关键字,允许异常被传递给调用者。throw语句不仅局限于用户自定义异常,还适用于系统生成的异常。
throw子句申明方法会将执行过程中产生的异常传递给方法的调用者,而throw语句可以明确的创建或获取异常对象传递给调用者。
在继承重写方法中申明异常规则
- 重写方法必须抛出与被重写方法异常的相同的异常或者是它异常的子类。
- 在重写方法抛出多个异常的情况下,重写方法必须抛出被重写方法的异常的子类。
- 如果父类方法没有抛出异常,那么子类也不能抛出。
看个小测试:
@Override
public void test1() throws RuntimeException {
// TODO Auto-generated method stub
super.test1();
try {
URL url = new URL("sdada");
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
RuntimeException m = new RuntimeException("url error");
m.initCause(e);
throw m;
}
}
test1这个方法是继承于父类的,但是父类这个方法没有抛出任何异常,为啥在这可以抛出。重要的就是在子类处理过程中出现的异常捕捉,然后在catch处理的时候,构造一个未检异常(或者是子类),添加异常信息,抛出这个异常对象给调用者,并且抛给方法的调用者。