我奶奶都能懂java异常

Posted 浦江之猿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我奶奶都能懂java异常相关的知识,希望对你有一定的参考价值。

前段时间考完OCA决定对java异常做一个总结,但一直被其他的知识点给占用了时间,于是一直托到今天。本篇博客继续从java的基础知识出发,希望能帮助大家。
示例的源码可以直接通过csdn下载,也可以通过git导出:https://github.com/igdnss/java_exception.git

异常的概念

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

异常的分类

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

  • Error:JVM,硬件,执行逻辑错误,无法通过手动处理。
    例如:StackOverflowError, OutOfMemoryError
  • Exception: 程序在运行和配置中产生的问题,可以通过手动处理
    例如:
    RuntimeException:运行时异常,可处理可不处理。
    CheckedException:检查(编译)时异常,必须处理,不处理程序编译不通过。
    在这里插入图片描述

常见的异常

异常说明类别
NullPointerException空指针异常运行时异常
ArrayIndexOutOfBoundException数组越界异常运行时异常
ClassCastException类型转换异常运行时异常
ArithmeticException算术异常运行时异常
NumberFormatException数字格式化异常运行时异常
FileNotFoundException文件不存在异常检查(编译)时异常

示例:

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秒解!

以上是关于我奶奶都能懂java异常的主要内容,如果未能解决你的问题,请参考以下文章

我奶奶都能懂java枚举类型

我奶奶都能懂java枚举类型

我奶奶都能懂java8特性-日期时间

我奶奶都能懂java子类构造方法

我奶奶都能懂java8特性-lambda表达试

我奶奶都能懂docker基本操作