一、为什么有异常
在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美,在系统运行的时候仍然会出现很多靠代码不能规避的错误,比如:用户的输入格式,读取文件是否存在,网络是否保持畅通等。
二、异常
异常:在java中,将程序执行中发生不正常情况称为“异常”。(语法和逻辑的错误不是异常)
异常可以分为两类:
Error:java虚拟机无法解决的严重问题。JVM内存错误,资源耗尽等严重问题。比如:StackOverflowError。一般不编写针对性的代码进行处理。
Exception:其他因编程或者外在因素导致的一般性问题,可以用针对性的代码进行处理。比如:空指针访问,数组下标越界,数组不匹配等。
常见的异常:
*********下面是编译时异常*************
File file = new File("hello.text");
FileInputStream fis = new FileInputStream(file);
int data = fis.read();
while(data != -1){
System.out.println((char)data);
data = fis.read();
}
fis.close();
*********下面是运行时异常*************
//NullPointerException 空指针异常
String str = "abc";
str = null;
System.out.println(str.charAt(0));
//ArrayIndexOutOfBoundsException 数组越界
int[] a = new int[10];
System.out.println(a[10]);
//ClassCastException 类型转换异常
Object obj = new Date();
String str1 = (String)obj;
//NumberFormatException 数值转换异常
String str2 = "abc";
int b = Integer.parseInt(str2);
三、异常的处理(自动抛出)
在编写程序的时候,经常要在可能出现错误的地方加上检测代码,比如if-else,太多的这样的语句可能会导致代码的加长,臃肿,可读性差。因此常采用异常处理机制。Java采用异常处理机制,是将异常处理的程序代码集中在一起,与正常的代码分开,使得程序简洁、优雅、并且容易维护。
程序正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象,将次对象抛出,进行异常处理。
异常处理的两种方式:1.try-catch-finally 2.throws
1.try-catch-finally
try{
//可能会出现异常的代码
}catch(异常类型1 变量1){
//处理异常方式1
}catch(异常类型2 变量2){
//处理异常方式2
}
....
finally{
//必须执行的代码
}
说明:
1.finally是可选的,finally中声明的语句一定会被执行,即使catch中又出现异常,在try中又有return语句依然会执行。在一些时候finally必须写:向数据库连接、输入输出流、网络编程Socket等资源,JVM不能自动回收,我们需要在finally中手动回收。
2.使用try将可能出现异常的代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常的对象的类型,去catch中进行匹配。
3.一旦try中的异常对象匹配到某个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出结构,继续执行代码。
4.catc中的异常类型如果没有子父类关系,则谁上谁下没有关系;如果有,子类一定要在父类的上面。
5.常用的异常对象处理方式(1)String getMessage() (2)printStackTrace()。
6.try-catch-finally可以嵌套。
7使用try-catch-finally处理编译时异常,让程序在编译的时候不报错,但是运行的时候仍可能报错。相当于让编译时异常延时到运行时。
8开发种由于运行时异常比较常见,通常不针对运行时异常编写try-catch-finally语句。但是编译时异常一定要考虑。
2.throws
“throws + 异常类型”写在方法声明处,指执行此方法时,可能会抛出异常类型。一旦方法体执行出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后面的异常类型就被抛出。异常代码后的部分不执行。
public void method2(){
try {
method1();
}catch (IOException e){
e.printStackTrace();
}
}
public void method1() throws IOException {
File file = new File("hello.text");
FileInputStream fis = new FileInputStream(file);
int data = fis.read();
while(data != -1){
System.out.println((char)data);
data = fis.read();
}
fis.close();
}
注意:
try-catch-finally是真正将异常处理掉,
throws的方式只是将异常抛给方法的调用者,并没有真正的处理掉。
子类重写的方法抛出的异常不大于父类被重写方法抛出的异常。
public class Main {
public static void main(String[] args) {
Main test = new Main();
test.display(new SubClass());
}
public void display(SuperClass s){
try {
s.method();//子类重写的方法
}catch (IOException e){//子类的异常 要小于它
e.printStackTrace();
}
}
}
class SuperClass{
public void method() throws IOException{//父类没有抛异常,子类就不能抛异常
}
}
class SubClass extends SuperClass{
@Override
public void method() throws FileNotFoundException {//这里抛出的异常要比,不然 上面的catch无法接受
}
}
try-catch-finally 和 throws 的选择(处理和抛出的选择)
如果父类被重写的方法没有抛出异常,如果子类重写的方法要抛出异常的话,必须用try-catch-finally。
在执行方法a种,又调用了另外几个方法,并且这几个方法是递进的关系执行。建议这几个方法用throws的方式处理。而a方法考虑用try-catch-finally。处理。
如果是因为传参导致的异常(想要int型,缺填的其他类型),应该通过throws抛出
四、手动抛出异常
上面的异常都是系统自动生成异常对象。这里手动生成一个异常对象,并抛出throw, 区分上面的throws。throw只是一种异常的产生方式,不是异常的处理方式。
public class Main {
public static void main(String[] args) {
Student student = new Student();
try {
student.regist(-1);
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}
class Student{
private int id;
public void regist(int id) throws Exception {
if(id > 0) this.id = id;
else {//输入数据非法
运行时异常可以不用异常处理,直接这亚子就完了
//throw new RuntimeException("输入数据非法");
//这个异常既包括运行时异常,和编译时异常。所以要进行异常处理
throw new Exception("输入数据非法");//括号里面时异常信息,可以用getMessage
}
}
}
输入数据非法
throw和throws的区别:
throw :是产生一个异常类,是异常的生成方式,声明在方法体内
throws :是异常的处理方式,声明在方法的声明处
五、用户自定义异常类
1.继承现有的异常结构,RuntimeException,Exception
2.提供全局常量serialVersionUID
3.提供重载的构造器
class MyException extends RuntimeException{
static final long serialVersionUID = -1561651561561L;
public MyException() {
}
public MyException(String msg) {
super(msg);
}
}
练习:
public class Main {
public static void main(String[] args) {
try {
int i = Integer.parseInt(args[0]);
int j = Integer.parseInt(args[1]);
int result = ecm(i,j);
System.out.println(result);
}catch (NumberFormatException e){
System.out.println("数据类型不一致");
}catch (ArrayIndexOutOfBoundsException e){
System.out.println("缺少命令行参数");
}catch (ArithmeticException e){
System.out.println("除 0");
}catch (EcDef e){
System.out.println(e.getMessage());
}
}
public static int ecm(int i,int j) throws EcDef {//非运行时异常,要处理
if(i < 0 || j < 0){
throw new EcDef("分子或分母为负数");
}
return i/j;
}
}
class EcDef extends Exception{
static final long serialVersionUID = -15616511561561L;
public EcDef() {}
public EcDef(String msg) {
super(msg);
}
}