开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
引言
基本类型、类、接口、枚举类都是在表示和操作数据,操作的过程中可能会出现报错,可能来自内部、外部或者代码写错,统称为异常
异常分类
- 异常大的分为两类:
-
- Error:JVM 无法解决的问题
- Exception:分为三大类,IO、运行时异常和 数据库异常
处理原则
异常处理主要是为了提示用户或者程序员操作的时候出错了,比方说一个网页中输入手机号码,要求是数字,但是用户输成字母了,就需要爆出消息提示用户,这是异常处理的常见场景
对于运行时异常,我们发现代码错误后必须立即解决,保证代码正确运行,所以不需要使用 try-catch-finally 捕获并处理异常,直接找到具体代码然后修改即可
而对于编译时异常,就类似上面说的场景,通过 try-catch-finally,捕获异常并进行一定的处理
处理方式
try-catch-finally
//格式
try{
//可能出现异常的代码
}catch(异常类型 1 变量名 1){
//异常处理方式1
}catch(异常类型 2 变量名 2){
//异常处理方式2
}finally{
//一定会执行的代码
}
//例子
@Test
public void test1(){
try{
String str = "123";
str = "abc";
int num = 0;
System.out.println("hello-----0");
num = Integer.parseInt(str);
System.out.println("hello-----1");
}catch(NumberFormatException e){
System.out.println(e.getMessage());
e.printStackTrace();
System.out.println("出现数值转换异常,不要着急");
}catch(NullPointerException e){
System.out.println("出现空指针异常,不要着急");
}
finally{
System.out.println("hello-----2");
}
System.out.println("hello-----3");
}
//结果
hello-----0
For input string: "abc"
java.lang.NumberFormatException: For input string: "abc"
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 Error.ExceptionTest.test212(ExceptionTest.java:20)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
出现数值转换异常,不要着急
hello-----2
hello-----3
针对上述结果,可以反推在 try-catch-finally 的执行顺序:
- 执行 try 中的语句
- 一旦出现异常,就对照着异常进入相应的 catch 中,然后执行里面的代码,一般显示异常信息或者输出一些提示信息。
- 执行完 catch 后不再执行 try 后面的语句,直接执行 finally 中的内容
- try catch finally 执行结束,执行后面的代码
注意:
- try 中定义的变量作用域为 try 语句中,如果后面想用,需要把变量放到外面
@Test
public void test2(){
String str = "123";
str = "abc";
int num = 0;
try{
System.out.println("hello-----0");
num = Integer.parseInt(str);
System.out.println("hello-----1");
}catch(NumberFormatException e){
System.out.println(e.getMessage());
e.printStackTrace();
System.out.println("出现数值转换异常,不要着急");
}catch(NullPointerException e){
System.out.println("出现空指针异常,不要着急");
}
finally{
System.out.println("hello-----2");
}
System.out.println("hello-----3");
System.out.println(num);
}
//结果
hello-----0
For input string: "abc"
java.lang.NumberFormatException: For input string: "abc"
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 Error.ExceptionTest.test212(ExceptionTest.java:20)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
出现数值转换异常,不要着急
hello-----2
hello-----3
0
finally
finally 主要用于关闭一些连接,比如数据库连接,输入流输出流,socket 连接等等。举个例子就是进行数据库操作的时候,如果出现异常了,即使我们使用 try catch finally,也可能无法关闭数据库连接,所以需要用 finally 防止代码出现异常时,无法执行关闭连接的代码。
@Test
public void testMethod1(){
try {
File file = new File("hello.md");
FileInputStream fis = new FileInputStream(file);
int data = fis.read();
while(data != -1){
System.out.println((char) data);
data = fis.read();
}
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
fis.close();
}
}
//但是这样子 finally 中的 fis 没有被定义,会报错,所以一般都这样子写,把声明防外面
@Test
public void testMethod1(){
FileInputStream fis = null;
try {
File file = new File("hello.txt");
fis = new FileInputStream(file);
int data = fis.read();
while(data != -1){
System.out.print((char) data);
data = fis.read();
}
} catch (FileNotFoundException e){
e.printStackTrace();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if(fis != null) fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
注意
finally 后面不建议写 return,因为会把 catch 中的 return 覆盖掉
throws
上面的 try-catch-finally 主要用于处理异常,如果无法处理异常,就需要用 throws,把程序往上抛,直到有程序进行处理
public static void main(String[] args) {
try {
method02();
} catch(IOException e){
System.out.println("输入的文件名字不正确");
}
}
public static void method02() throws IOException{
method01();
}
public static void method01() throws FileNotFoundException, IOException {
File file = new File("hello1.txt");
FileInputStream fis = new FileInputStream(file);
int data = fis.read();
while(data != -1){
System.out.print((char) data);
data = fis.read();
}
fis.close();
}
//结果
输入的文件名字不正确