标题
1.异常入门
Shortcuts:选中代码块->快捷键->Ctrl+Alt+t (Surround with)
改正之后:
public class Exception01 {
public static void main(String[] args) {
int num1 = 1;
int num2 = 0;
try {
int num3 = num1 / num2;
} catch (Exception e) {
System.out.println(e.getMessage());
}
System.out.println("程序执行完成……");
}
}
2. Throwable类的异常体系图
- 体现了继承和实现关系:实线是继承,虚线是实现。
- 运行时异常一般指编程时的逻辑错误,编译器检测不出;
- 编译时异常,是编译器要求必须处理的异常;
2.1 NullPointerException
Exception in thread “main” java.lang.NullPointerException
at com.zzw.exception_.NullPointerException_.main(NullPointerException_.java:10)
public class NullPointerException_ {
public static void main(String[] args) {
String name = null;
System.out.println(name.length());
}
}
2.2 ArithmeticException
Exception in thread “main” java.lang.ArithmeticException: / by zero
at com.zzw.exception_.NullPointerException_.main(NullPointerException_.java:11)
public class NullPointerException_ {
public static void main(String[] args) {
int num1 = 1;
int num2 = 0;
int num3 = num1 / num2;
}
}
2.3 ArrayIndexOutOfBoundsException
Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: 3
at com.zzw.exception_.NullPointerException_.main(NullPointerException_.java:11)
public class NullPointerException_ {
public static void main(String[] args) {
int[] arr = new int[3];
for (int i = 0; i <= arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
}
2.4 ClassCastException
Exception in thread “main” java.lang.ClassCastException: com.zzw.exception_.B cannot be cast to com.zzw.exception_.C
at com.zzw.exception_.NullPointerException_.main(NullPointerException_.java:11)
public class NullPointerException_ {
public static void main(String[] args) {
A a = new B();//向上转型
B b = (B)a;//向下转型
C c = (C)a;//不允许,抛异常
}
}
class A {}
class B extends A {}
class C extends A {}
2.5 NumberFormatException数字格式不正确异常
当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换成适当格式时,抛出该异常。
Exception in thread “main” java.lang.NumberFormatException: For input string: “赵志伟”
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.parseInt(Integer.java:615)
at com.zzw.exception_.NumberFormatException_.main(NumberFormatException_.java:15)
public class NumberFormatException_ {
public static void main(String[] args) {
String nums = "1234";//正确
String name = "赵志伟";
//将String转成int
int i1 = Integer.parseInt(nums);
int i2 = Integer.parseInt(name);
System.out.println(i1);//1234
}
}
3. try-catch-finally
如果没有使用finally,语法是可以通过的;
3.1 try-catch细节
1.如果发生了异常,则异常之后的代码不会执行,直接进入catch块
public class TryCatchDetail {
public static void main(String[] args) {
try {
String str = "zzw";
int a = Integer.parseInt(str);
System.out.println("数字:" + a);
} catch (NumberFormatException e) {
System.out.println("报错信息:" + e.getMessage());
}
System.out.println("程序继续执行");
}
}
输出:报错信息:For input string: "zzw"
程序继续执行
2.如果异常没有发生,则顺序执行try代码块,不会进入到catch代码块
public class TryCatchDetail {
public static void main(String[] args) {
try {
String str = "123";
int a = Integer.parseInt(str);
System.out.println("数字:" + a);
} catch (NumberFormatException e) {
System.out.println("报错信息:" + e.getMessage());
}
System.out.println("程序继续执行");
}
}
输出:数字:123
程序继续执行
3.如果希望不管是否发生异常,都执行某段代码(关闭连接、释放资源),放在finally代码块内;
public class TryCatchDetail {
public static void main(String[] args) {
try {
String str = "zzw";
int a = Integer.parseInt(str);
System.out.println("数字:" + a);
} catch (NumberFormatException e) {
System.out.println("报错信息:" + e.getMessage());
} finally {
System.out.println("finally代码块被执行");
}
System.out.println("程序继续执行");
}
}
输出:
报错信息:For input string: "zzw"
finally代码块被执行
程序继续执行
public class TryCatchDetail {
public static void main(String[] args) {
try {
String str = "123";
int a = Integer.parseInt(str);
System.out.println("数字:" + a);
} catch (NumberFormatException e) {
System.out.println("报错信息:" + e.getMessage());
} finally {
System.out.println("finally代码块被执行");
}
System.out.println("程序继续执行");
}
}
输出:
数字:123
finally代码块被执行
程序继续执行
4.如果try代码块内有多个异常,可以使用多个catch 分别捕获不同的异常,相应处理;
4.1 要求子类异常写在前面,父类异常写在后面;
public class TryCatchDetail02 {
public static void main(String[] args) {
try {
Person person = new Person();
person = null;
//NullPointerException
System.out.println("person的名字:" + person.getName());
int num1 = 1;
int num2 = 0;
//ArithmeticException
int res = num1 / num2;
} catch (NullPointerException e) {
System.out.println("空指针异常->" + e.getMessage());
} catch (ArithmeticException e) {
System.out.println("算术异常->" + e.getMessage());
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println();
}
}
}
输出:
空指针异常->null
3.2 try-finally
try-finally可以配合使用,这种用法相当于没有捕获异常,因此程序会直接崩掉、退出;
应用场景:就是执行一段代码,不管是否发生异常,都必须执行某个业务逻辑;
public class TryCatchDetail03 {
public static void main(String[] args) {
try {
int n1 = 10;
int n2 = 0;
int res = n1 / n2;
} finally {
//这里会执行.....
System.out.println("执行了finally");
}
//这里不会执行.....
System.out.println("程序继续执行……");
}
}
输出:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.zzw.try_.TryCatchDetail03.main(TryCatchDetail03.java:12)
执行了finally
3.3 练习
finally必须执行, 即使前面存在return
public class TryCatchExercise01 {
public static void main(String[] args) {
System.out.println(method());//4
}
public static int method() {
try {
String[] names = new String[3];
//names[1] = null
if(names[1].equals("tom")) {//空指针异常
System.out.println(names[1]);
} else {
names[3] = "zzw";//数组越界
}
return 1;
} catch (ArrayIndexOutOfBoundsException e) {
return 2;
} catch (NullPointerException e) {
return 3;//很遗憾, 这里不会执行, 即不会返回3
} finally {//因为finally必须执行
return 4;//所以会在这里返回4
}
}
}
TryCatchExercise02 .java
public class TryCatchExercise02 {
public static void main(String[] args) {
System.out.println(method());
}
public static int method() {
int i = 1;
try {
i++;//2
String[] names = new String[3];
if(names[1].equals("tom")) {//空指针异常
System.out.println(names[1]);
} else {
names[3] = "zzw";
}
return 1;
} catch (ArrayIndexOutOfBoundsException e) {
return 2; //这里不会执行
} catch (NullPointerException e) {
return ++i;//这里不会返回, 但++i还是要执行 i=3
} finally {//必须执行
return ++i;//4
}
}
}
输出:
4
TryCatchExercise03.java
public class TryCatchExercise03 {
public static void main(String[] args) {
System.out.println(method());
}
public static int method() {
int i = 1;//i=1
try {
i++;//i=2
String[] names = new String[3];
if (names[1].equals("tom")) {//空指针异常
System.out.println(names[1]);
} else {
names[3] = "zzw";
}
return 1; //这里不会执行
} catch (ArrayIndexOutOfBoundsException e) {
return 2; //这里不会执行
} catch (NullPointerException e) {
//++i执行 i=3, 因为有finally, 即return不会马上执行
//又因为finally没有return语句
//所以底层通常是把这里要返回的 i => 保存到临时变量 temp, 即 temp = 3;
//然后执行finally语句, 最后返回临时变量
return ++i;
} finally {//必须执行
++i;//i=4
System.out.println("i=" + i);//i=4
}
}
}
输出:
i=4
3
小结
1)如果没有出现异常, 则执行try块中所有语句, 不执行catch块中语句. 如果有finally, 最后还需要执行finally里面的语句.
2)如果出现异常, 则try块中异常发生后, try块剩下的语句不再执行, 将执行catch块中的语句, 如果有finally, 最后还需要执行finally里面的语句!
TryCatchExercise04 .java
如果用户输入的不是一个整数, 就提示他反复输入, 直到输入一个整数
public class TryCatchExercise04 {
public static void main(String[] args) {
method();
}
public static void method() {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入一个整数: ");
String num = scanner.nextLine();
try {
Integer.parseInt(num);
} catch (NumberFormatException e) {
System.out.println("==========输入错误, 请重新输入==========");
method();
}
}
}
4. throws
如果没有显示的使用try-catch,默认使用throws方法;
注意事项和细节
1.对于编译异常,程序必须处理;(比如:FileNotFoundException);
public class Throw01 {
public static void main(String[] args) {
}
public void f1() throws FileNotFoundException,NullPointerException,ArrayIndexOutOfBoundsException {
//这里的异常FileInputStream 是编译异常
//1.方法一,可以使用try-catch
//2.方法二,可以使用throws抛出异常,让调用f1()方法的调用者(方法)处理
//3.throws关键字后,也可以跟异常列表,即可以跟多个列表
FileInputStream fileInputStream = new FileInputStream("D://a.txt");
}
public void f2() throws Exception {
//这里的异常FileInputStream 是编译异常
//1.方法一,可以使用try-catch
//2.方法二,可以使用throws抛出异常,让调用f1()方法的调用者(方法)处理
FileInputStream fileInputStream = new FileInputStream("D://a.txt");
}
}
2.对于运行异常,程序中如果没有处理,默认就是throws方式处理;
public class ThrowDetail {
public static void main(String[] args) throws ArithmeticException{
f2();
}
public static void f2() throws ArithmeticException{
int n1 = 1;
int n2 = 0;
int res = n1 / n2;
}
}
3.在继承中对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类此方法抛出的异常类型一致,要么为父类抛出异常类型的子类型;
子类重写方法的异常抛出类型必须和父类的异常类型相同或为其子类;
class Father {
public void method() throws RuntimeException { }
}
class Son extends Father {
@Override
public void method() throws NullPointerException {
super.method();
}
}
课后练习
调用f2()报错,因为f2()抛出的是编译异常
解决方案一:throws抛出异常
public class ThrowDetail {
public static void main(String[] args) {
}
public static void f1() throws FileNotFoundException {
//1.因为f2()方法抛出的是一个编译异常
//2.解决方案:throws抛出异常
//3.try-catch
f2();
}
public static void f2() throws FileNotFoundException {
FileInputStream fileInputStream = new FileInputStream("D://a.txt");
}
}
解决方案二:try-catch
public class ThrowDetail {
public static void main(String[] args) {
}
public static void f1() throws FileNotFoundException {
//1.因为f2()方法抛出的是一个编译异常
//2.解决方案:throws抛出异常
//3.try-catch
try {
f2();
} catch(FileNotFoundException e) {
e.printStackTrace();
}
}
public static void f2() throws FileNotFoundException {
FileInputStream fileInputStream = new FileInputStream("D://a.txt");
}
}
1.这里调用f4()是ok的,原因是f4()抛出的是运行异常,运行时异常有默认处理机制;
2.java中,并不要求程序员显示处理;
public static void f3() {
//这里调用f4()是ok的
f4();
}
public static void f4() throws ArithmeticException { }
1.这里调用f4()会报错,原因是f4()抛出的是编译异常,编译异常程序员必须处理;
public static void f3() {
//这里调用f4()会报错
f4();
}
public static void f4() throws FileNotFoundException { }
🌼自定义异常
当程序中出现了某些“错误”,但该错误信息并没有在Throwable子类中描述处理,这个时候可以自己设计异常类,用于描述该错误信息;
- 自定义异常类名(程序员自己设计),继承Exception或者RuntimeException;
- 如果继承Exception,属于编译异常;
- 如果继承RuntimeException,属于运行异常(一般来说,继承RuntimeException)
public class CustomException_ {
public static void main(String[] args) {
int age = 12;
if (!(age >= 18 && age <= 120)) {
//这里可以通过构造器设置信息
throw new AgeException("年龄需要在18-120之间");
}
System.out.println("你的年龄范围正确");
}
}
//自定义的一个异常
class AgeException extends RuntimeException {
//创建构造器
public AgeException(String message) {
super(message);
}
}
6.throw 和 throws区别
throw在方法体内,throw后跟的是一个具体的异常对象
throws后面跟的是异常类型
- | 意义 | 位置 | 后面跟的东西 |
---|---|---|---|
throws | 异常处理的一种方式 | 方法声明处 | 异常类型 |
throw | 手动生成异常对象的关键字 | 方法体内 | 异常对象 |
练习1
public class ThrowException {
public static void main(String[] args) {
try {
methodA();
} catch (Exception e) {
System.out.println(e.getMessage());//(3)
}
methodB();
}
static void methodA() {
try {
System.out.println("进入方法A");//(1)
throw new RuntimeException("制造异常");
} finally {
System.out.println("用A方法的finally");//(2)
}
}
static void methodB() {
try {
System.out.println("进入方法B");//(4)
return;
} finally {
System.out.println("调用B方法的finally");//(5)
}
}
}
练习2
编写方法,接受命令行的两个参数。两数相除,捕获数据格式不正确、缺少命令行参数、除数为0;
public class Homework01 {
public static void main(String[] args) {
//1.数组格式不正确,NumberFormatException
//2.缺少命令行参数,ArrayIndexOutOfBoundsException
//3.除数为0异常处理,ArithmeticException
String[] name = args;
try {
if(name.length != 2) {
throw new ArrayIndexOutOfBoundsException("命令行参数个数不对");
}
int num1 = Integer.parseInt(name[0]);
int num2 = Integer.parseInt(name[1]);
cal(num1,num2);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println(e.getMessage());
} catch (NumberFormatException e) {
System.out.println(e.getMessage());
} catch (ArithmeticException e) {
System.out.println(e.getMessage());
}
}
public static double cal(int num1, int num2) {
return num1 / num2;
}
}
练习3
Object o = args[2] √;
Integer i = (Integer) o ×;//看他的运行类型,怎么能把String转成Integer呢?
public class Homework02 {
public static void main(String[] args) {
if(args[4].equals("john")) {//ArrayIndexOutOfBoundsException
System.out.println("AA");
} else {
System.out.println("BB");
}
Object o = args[2];//string->Object 向上转型
Integer i = (Integer) o;//向下转型 ClassCastException
System.out.println("i=" + i);
}
}
练习4
public class Homework03 {
public static void main(String[] args) {
try {
func();
System.out.println("A");//try代码块中的代码出现错误,那么该代码后面的一系列代码不再执行
} catch (Exception e) {//在这里被捕获到异常,try里的代码不再执行
System.out.println(e.getMessage());//(2)
}
System.out.println("D");//(3)这里输出,catch捕获到异常程序继续运行
}
public static void func() {
try {
throw new RuntimeException("出错了");//try代码块里抛出错误
} finally {
System.out.println("B");//(1)
}
}
}