1.基本概念
当出现程序无法控制的外部环境问题(如:用户提供的文件不存在,文件内容损坏,网络不可用)时,java就会用异常对象描述。
2.处理
java中使用两种方式来处理异常:
1>在发生异常的地方直接处理;
2>将异常抛给调用者,让调用者处理。
3.异常分类
1>检查性异常 java.lang.Exception
检查性异常:
程序是正确的,但因为外界的环境条件不满足而引发。比如,我晚上要去吃饭,结果,去了饭馆没开门,这是我的错吗?我吃饭(就类似于程序)有错吗?没错,只是外界条件不满足,所以这就是异常。在java中,比如,程序试图打开一个远程并不存在的Socket端口,或者打开不存在的文件时,这不是程序本身的逻辑错误,而很可能是远程机器名字错误,对商业软件系统,程序开发者必须考虑并处理这个问题,java编译器强制要求,处理这类异常类,如果不捕获这些异常,程序不能被编译。
我举一个实际案例来说明问题:
package com.test; import java.io.FileReader; import java.net.Socket; public class test3 { public static void main(String[] args) { //检查性异常,1.打开文件 FileReader fr=new FileReader("d:\\aa.text"); //2.连接一个192.168.1.23 ip的端口号78 } }
FileReader fr=new FileReader("d:\\aa.text");
这条语句会报错,
未报告的异常错误FileNotFoundException; 必须对其进行捕获或声明以便抛出
----
Socket s=new Socket("192.168.1.23", 78);
会报错:
未报告的异常错误UnknownHostException; 必须对其进行捕获或声明以便抛出
----
就算你写的ip和端口都是对的,但是编译器认为你可能写错了。
所以,检查性异常就是编译器可以事先确认的异常。
2>运行期异常 java.lang.RuntimeException
运行时异常:
这意味着,程序存在bug,如:数组越界,0被除,入参不满足规范...........这类异常需要更改程序来避免。java编译器强制要求处理这类异常。
这类异常,编译通过,这一点上,编译器“并不是足够聪明”。所以,在运行的时候会出错。属于程序逻辑上出了错。一般比较难检查。
3>错误(最严重) java.lang.Error
错误:
一般很少见,也很难通过程序解决。它可能源于程序的bug,但一般更可能源于环境问题,比如,内存耗尽,错误在程序中处理,
而由运行环境处理。
java.lang.RuntimeException继承自 java.lang.Exception。
java.lang.Error和 java.lang.RuntimeException继承自java.lang.Throwable类。
但是顶层类都是java.lang.Throwable
4.异常处理:
1>try.....catch
程序运行产生异常时,将从异常的发生点中断程序,并向外抛出异常信息。
package com.test;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
public class test3 {
public static void main(String[] args) {
try {
//检查性异常,1.打开文件
FileReader fr=new FileReader("d:\\aa.text");
//2.连接一个192.168.12.12 ip的端口号4567
Socket s=new Socket("192.168.1.23", 78);
} catch (FileNotFoundException ex) {
Logger.getLogger(test3.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(test3.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
要做到最小捕获异常的原则。是在不知道,可以catch(Exception e),最大的。
捕获结果;
六月 11, 2015 9:47:34 下午 com.test.test3 main
严重: null
java.io.FileNotFoundException: d:\aa.text (设备未就绪。)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.
at java.io.FileInputStream.
at java.io.FileReader.
at com.test.test3.main(test3.java:15)
因为第一个有异常,所以第二个就没机会捕获了,程序已经中断了,想要看到第二个catch的作用,那么我现在把第一个语句注释掉,再次捕获。再次修改代码!
package com.test;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
public class test3 {
public static void main(String[] args) {
try {
//检查性异常,1.打开文件
// FileReader fr=new FileReader("d:\\aa.text");
//2.连接一个192.168.12.12 ip的端口号4567
Socket s=new Socket("192.168.1.23", 78);
System.out.println("connect success!");
} catch (FileNotFoundException ex) {
Logger.getLogger(test3.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(test3.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("ok");
}
}
运行结果:
java.net.ConnectException: Connection timed out: connect
at java.net.DualStackPlainSocketImpl.connect0(Native Method)
at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:345)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:589)
at java.net.Socket.connect(Socket.java:538)
at java.net.Socket.
at java.net.Socket.
at com.test.test3.main(test3.java:17)
ok
小结:再出现异常的地方,就终止执行代码,然后进入到catch,如果,你有多个catch语句,则进入匹配异常那个的catch。
2>finally
如果把finally块放在try......catch的语句后面,finally语句是不管catch语句块是否执行,一般都会执行的。
下面是非一般情况:
a. finally块中发生异常
b. 程序所在线程死亡
c. 在前面的代码中有system.exit();语句
d. 关闭cpu
package com.test;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
public class test3 {
public static void main(String[] args) {
FileReader fr=null;
try {
//检查性异常,1.打开文件
fr=new FileReader("H:\\aa.txt");
//2.连接一个192.168.12.12 ip的端口号4567
// Socket s=new Socket("192.168.1.23", 80);
} catch (FileNotFoundException ex) {
System.exit(-1);//finally就不会执行
Logger.getLogger(test3.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(test3.class.getName()).log(Level.SEVERE, null, ex);
}
finally{
System.out.println("进入finally语句块");
//这个语句块,不管有没有异常都会执行
//一般把需要关闭的资源,如文件,或者数据库的连接,开辟的一些内存等
if(fr!=null){
try {
fr.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
System.out.println("ok");
}
}
案例说明:
package com.test;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.logging.Level;
import java.util.logging.Logger;
public class test {
public static void main(String[] args) {
Father father=new Father();
father.test1();
}
}
class Father{
private Son son=null;
public Father(){
son=new Son();
}
public void test1(){
System.out.println("1");
//son.test2();//现在好了,儿子没有处理的异常抛给了父亲,父亲调用了儿子的方法,
//而throws语句,是抛给调用者的,所以,此时父亲有两种方法解决这个异常,
//一个是也不想管这个异常,往出抛,谁调用谁来处理这个异常,那么现在再
//往上抛就是JVM了,那好了, 既然是当父亲的,那么就管管吧,用try catch
//用throws的缺点就是,不知道异常出在了哪里
try {
son.test2();
} catch (FileNotFoundException ex) {
System.out.println("父亲在处理异常。");
ex.printStackTrace();
}
}
}
class Son{
public void test2() throws FileNotFoundException{
FileReader fr=null;
fr=new FileReader("d://aa.txt");//这个文件并不存在的
}
}
输出结果:
1
父亲在处理异常。
java.io.FileNotFoundException: d:\aa.txt (设备未就绪。)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.
at java.io.FileInputStream.
at java.io.FileReader.
at com.test.Son.test2(test.java:37)//真正的错误的根源在这一行
at com.test.Father.test1(test.java:27)//第27行在父亲类test1方法 里面调用了,
at com.test.test.main(test.java:11)//在主方法里面调用了
如果父亲也不管,都向上抛最后抛到主方法那里去,然后呢,Jvm处理,这样子做的效率并不高,不建议。建议,哪里出问题,就在哪里try catch。
就像下面这样的代码:不建议
package com.test;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.logging.Level;
import java.util.logging.Logger;
public class test {
public static void main(String[] args) throws FileNotFoundException {
Father father=new Father();
father.test1();
}
}
class Father{
private Son son=null;
public Father(){
son=new Son();
}
public void test1() throws FileNotFoundException{
System.out.println("1");
son.test2();//现在好了,儿子没有处理的异常抛给了父亲,父亲调用了儿子的方法,
//而throws语句,是抛给调用者的,所以,此时父亲有两种方法解决这个异常,
//一个是也不想管这个异常,往出抛,谁调用谁来处理这个异常,那么现在再
//往上抛就是JVM了,那好了, 既然是当父亲的,那么就管管吧,用try catch
//用throws的缺点就是,不知道异常出在了哪里
// try {
// son.test2();
// } catch (FileNotFoundException ex) {
// System.out.println("父亲在处理异常。");
// ex.printStackTrace();
// }
}
}
class Son{
public void test2() throws FileNotFoundException{
FileReader fr=null;
fr=new FileReader("d://aa.txt");
}
}
运行结果:
1
Exception in thread "main" java.io.FileNotFoundException: d:\aa.txt (设备未就绪。)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.
at java.io.FileInputStream.
at java.io.FileReader.
at com.test.Son.test2(test.java:37)
at com.test.Father.test1(test.java:21)
at com.test.test.main(test.java:11)
Java Result: 1