怎么还学不会?我爷爷都能懂java异常了

65 阅读8分钟

异常的概念

其实程序中的异常跟我们生活中的异常一样,例如生活中遇到家里灯坏了,灯坏了怎么办,要么修,要么换一个,不可能一直放那不管,不用灯,而是想办法去解决,要让生活继续下去。程序中的异常指的是程序中的不正常现象。出现的异常需要对其处理,如果不处理,程序将终止运行,造成不必要的后果。

异常的分类

所有的一切异常和错误都继承于Throwable这个类,位于java.lang包中。

Error:JVM,硬件,执行逻辑错误,无法通过手动处理。

例如:StackOverflowError, OutOfMemoryError

Exception: 程序在运行和配置中产生的问题,可以通过手动处理

例如:

RuntimeException:运行时异常,可处理可不处理。

CheckedException:检查(编译)时异常,必须处理,不处理程序编译不通过。

常见的异常

示例:

public class ExceptionTest { public static void main(String[] args) { //空指针异常 String address= null; System.out.println(address); //数组越界异常 String[] arr = {"a"}; System.out.println(arr[2]); //算术异常 System.out.println(1/0); //类型转换异常 Object str = "aaa"; Long i = (Long)str; //数字格式化异常 int number = Integer.parseInt("1a"); //文件不存在异常 File file = new File("c:\abc.txt"); FileInputStream inputStream = new FileInputStream(file); } }

异常的产生

程序在运行时遇到不符合规范的代码或结果时,会产生异常,程序员通过throw关键字可以将异常抛出,使用程序能正常运行下去。如果出现异常时不处理,在哪出现,程序在哪中断,不会往下执行。

异常的传递

异常如果不处理会一直往上传递,如果最终没有处理异常,jvm会进行默认异常处理(打印异常信息)。

示例1:传递异常未处理

public class ExceptionTransfer1 { public static void main(String[] args) { calculate(1, 0); } public static void calculate(int a, int b) { divided(a, b); } public static void divided(int a, int b) { System.out.println(a/b); } }

运行结果:

示例2:传递异常已处理

public class ExceptionTransfer1 { public static void main(String[] args) {

	calculate(1, 0);
}
public static void calculate(int a, int b) {
	try {
		divided(a, b);
	} catch (Exception e) {

System.out.println("除数不能为0");

	}
}
public static void divided(int a, int b) {
	System.out.println(a/b);
}

} 运行结果:除数不能为0

异常的处理

通过5个关键字处理异常:

try: 执行可能产生的异常代码

catch: 捕获异常,并处理

finally: 无论是否发生异常,代码总能执行(除了强制退出)

throw: 手动抛出异常

throws: 声明方法可能要抛出的各种异常,注意与throw的区别。throw是抛出,throws是声明。

try catch

使用try将可能出现异常的代码包起来,当真的有异常时,交给catch处理。

示例:指定正确的异常

public class TryCatchTest { public static void main(String[] args) { calculate(1, 0); } public static void calculate(int a, int b) { try {//函数中涉及到除法,因此可能会有算术异常,所以这里加上try divided(a, b); } catch (Exception e) {// 这里可以写成Exception(父类),也可以写指定的异常ArithmeticException,异常处理成功会后续代码会继续执行,因此会打印Hello System.out.println("除数不能为0"); } System.out.println("Hello"); } public static void divided(int a, int b) { System.out.println(a / b); } } 运行结果: 除数不能为0 Hello

注:catch中处理异常时如果指定特定异常,必须要指对,如果指定错误,异常是不会被处理的。示例:指定错误的异常

public class TryCatchTest2 { public static void main(String[] args) { calculate(1, 0); } public static void calculate(int a, int b) { try {//函数中涉及到除法,因此可能会有算术异常,所以这里加上try divided(a, b); } catch (FormatterClosedException e) {// 这里随便指定了一个异常,但不是算术异常,所以程序会中断,不会打印hello System.out.println("除数不能为0"); } System.out.println("Hello"); } public static void divided(int a, int b) { System.out.println(a / b); } } 运行结果: Exception in thread "main" java.lang.ArithmeticException: / by zero at com.research.exception.TryCatchTest2.divided(TryCatchTest2.java:22) at com.research.exception.TryCatchTest2.calculate(TryCatchTest2.java:14) at com.research.exception.TryCatchTest2.main(TryCatchTest2.java:9)

为了防止这种情况,建议catch时直接Exception

try catch finally

前文中已提到finally块中不管异常有没有发生,都会被执行,一般用于释放资源,例如关闭文件,断开连接。但程序强制退出时,finally块不会被执行。示例:

public class TryCatchFinallyTest { public static void main(String[] args) { calculate(1, 0); } public static void calculate(int a, int b) { try {//函数中涉及到除法,因此可能会有算术异常,所以这里加上try divided(a, b); // System.exit(0);当执行这句时,Haha不会打印 } catch (Exception e) { System.out.println("除数不能为0"); }finally { //必执行 System.out.println("Haha"); } System.out.println("Hello"); } public static void divided(int a, int b) { System.out.println(a / b); } }

多重catch

发生异常时按顺序逐个匹配直到结束,如果匹配成功,后续的catch不再执行,这里需要注意写码时子类异常在前,父类异常在后,否则编译不通过。示例:

public class MultiCatchTest { public static void main(String[] args) { calculate(1, 0); } public static void calculate(int a, int b) { try {//函数中涉及到除法,因此可能会有算术异常,所以这里加上try divided(a, b); Integer i = Integer.parseInt("124"); }catch(NumberFormatException e) { System.out.println("必须要为全数字");

	}catch (Exception e) {
		System.out.println("除数不能为0");
	}finally {
		//必执行
		System.out.println("Haha");
	}
	System.out.println("Hello");
}
public static void divided(int a, int b) {
	System.out.println(a / b);
}

}

try finally

没有catch,所以此组合不能捕获异常,但可以将异常向上抛出,只是在异常发生时做释放资源的操作,后续程序不会被执行。示例:

public class TryFinallyTest { public static void main(String[] args) { try { calculate(1, 0); }catch(Exception e) { System.out.println("在这里处理异常"); } } public static void calculate(int a, int b) { try {//函数中涉及到除法,因此可能会有算术异常,所以这里加上try divided(a, b); } finally { //释放资源 System.out.println("我想要怒放的生命"); } //有异常时,此句不会执行到 System.out.println("Hello"); } public static void divided(int a, int b) { System.out.println(a / b); } }

声明异常(throws)

使用throws关键字声明一个方法可能抛出的异常,调用该方法的地方需要对声明的异常进行处理,如果不处理,可能会出问题。声明的异常如果是编译期异常则必须要处理,如果不处理,编译不通过。如果声明的是运行时异常,如果不处理,一旦发生,则程序中断。简单的说就是告诉调用者,调用这个方法可能抛出异常,麻烦处理一下,如果不处理后果自负。这里处理异常时可以使用try catch,也可以在调用者上继续声明此异常,让其向上声明。注意与throw的区别,throw是抛出某一具体的异常,一次只能抛出一个,throws可以声明多个。

示例1:声明运行时异常

public class ThrowsTest1 { public static void main(String[] args) { //这里不处理不会出编译错误,因为是运行时异常 try { calculate(1, 0); }catch(ArithmeticException e) { System.out.println("处理异常"); } } public static void calculate(int a, int b) throws ArithmeticException { divided(a, b); //divided产生异常时,此名不执行 System.out.println("Hello"); } public static void divided(int a, int b) { System.out.println(a / b); } }

示例2:声明编译时异常

public class ThrowsTest2 { public static void main(String[] args) { //必须处理,不处理编译不通过 try { calculate(1, 0); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void calculate(int a, int b) throws FileNotFoundException { divided(a, b); // divided产生异常时,此名不执行 System.out.println("Hello"); } public static void divided(int a, int b) { System.out.println(a / b); } }

抛出异常(throw)

运行时异常都是系统自动抛出,但有些问题需要程序员自行抛出异常。自行抛出异常时使用throw + 异常对象。如果抛出的时运行时异常,方法调用处理,可不处理。如果抛出的是编译时异常,抛出的地方需要对其进行捕获或者通过方法向上声明,方法调用者必须处理,或者继续向上声明。

示例:

public class ThrowTest { public static void main(String[] args) { try { calculate(1, 0); }catch(RuntimeException e) { e.printStackTrace(); }

}
public static void calculate(int a, int b) {
	if (b != 0) {
		divided(a, b);
	} else {
		throw new RuntimeException("除数不能为0");
	}
}
public static void divided(int a, int b) {
	System.out.println(a / b);
}

}

自定义异常

jdk给我们提供了很多异常,但有时这些异常使用时不能见名知意,例如上文中的RuntimeException("除数不能为0"),只知道是个运行时异常,通过message方可知道他的用处,其实这里可以自己定义一个异常。自定义异常里需要继承Exception或其子类,一般自定的异常都是RuntimeException,所以常常继承RuntimeException.自定义时必须要提供构造方法,一个无参的一个有参的。定义好后没必要再写其它的代码,其意义就在于异常的名字。

**示例:重构ThrowTest **

public class ThrowTest { public static void main(String[] args) { try { calculate(1, 0); }catch(DenominatorCanNotBe0Exception e) { e.printStackTrace(); }

}
public static void calculate(int a, int b) {
	if (b != 0) {
		divided(a, b);
	} else {
		throw new DenominatorCanNotBe0Exception("除数不能为0");
	}
}
public static void divided(int a, int b) {
	System.out.println(a / b);
}

}

方法重写

这里的方法重写指的是带有异常声明的方法重写,需要注意以下三点。

方法名、参数列表、返回值类型必须和父类相同

子类的访问修饰符和父类相同或比父类更宽

子类中的方法,不能抛出比父类更多、更宽的检查时异常

结束语

至此,关于java异常的知识点就介绍完了。希望本文能帮助大家,祝大家在IT之路上少走弯路,一路绿灯不堵车,测试一性通过,bug秒解!喜欢请三连(更多知识关注私聊)