try catch finally 层次结构
三个关键字一般同时出现在某个方法或者代码块中:如果try出现,catch finally 可以同时出现也可以只出现一个,但不能一个都不出现。若有finally代码块则必执行,无论try 或者catch代码块中有无异常抛出。
一般形如:
try {
// 代码
} catch (Throwable throwable) {
// 代码
} finally {
// 代码
}
执行顺序
代码执行是按照try 、 catch 、finally 顺序执行的,也就是先执行try中的逻辑,然后catch中的逻辑,最后是finally的逻辑,其中catch 中的逻辑不是必须执行,只有当try中抛出的异常,catch中有对应的捕获才会走catch逻辑。
使用注意点
在需要返回值的情况下,返回值总是最后一个return对应的值。
异常抛出到上层也是和return同样的情况,最后面执行抛出的异常才是抛出到上层方法执行的异常。
因此,这里要特别注意,如果在try catch finally 中写了多个return语句或者抛出多个异常。注意异常或者返回可能被覆盖问题。
java 中是值传递(复制一份值传递出去)不是引用传递。这个值可以是真实值,也可以是引用的地址值。
在return的时候总是记住当前返回的值并在执行完后续代码后执行返回。
多返回覆盖的举例:
public class TestTryCatchFinally {
public static void main(String[] args) {
try {
System.out.println("main 处-->> " + testPerson());
} catch (Exception e) {
e.printStackTrace();
}
}
public static Person testPerson() {
Person person = new Person();
try {
person.age = 1;
person.name = "try";
System.out.println("try 处 -->> " + person);
return person;
} finally {
person = new Person();
person.name = "finally";
System.out.println("finally 处 ->> " + person);
return person;
}
}
static class Person {
public Person() {
}
String name;
int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
}
打印结果:
try 处 -->> Person{name='try', age=1}
finally 处 ->> Person{name='finally', age=0}
main 处-->> Person{name='finally', age=0}
在try 、finally 是两个person对象。结果返回的是finally的对象,而不是try的对象。
多异常覆盖的例子:
import java.lang.reflect.Parameter;
public class TestTryCatchFinally {
public static void main(String[] args) {
try {
System.out.println("main 处-->> " + testPerson());
} catch (Exception e) {
e.printStackTrace();
}
}
public static Person testPerson() {
Person person = new Person();
try {
person.age = 1;
person.name = "try";
Integer.parseInt(null);
System.out.println("try 处 -->> " + person);
return person;
} finally {
String b = (String)(new Object());
person = new Person();
person.name = "finally";
System.out.println("finally 处 ->> " + person);
}
}
static class Person {
public Person() {
}
String name;
int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
}
打印结果:
java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String
at TestTryCatchFinally.testPerson(TestTryCatchFinally.java:21)
at TestTryCatchFinally.main(TestTryCatchFinally.java:6)
由打印可见,抛出的不是try中的NumberFormatException异常,而是finlally中的ClassCastException。
另外一个例子:
public class TestTryCatchFinally {
public static void main(String[] args) {
try {
System.out.println("main 处-->> " + testPerson());
} catch (Exception e) {
e.printStackTrace();
}
}
public static Person testPerson() {
Person person = new Person();
try {
person.age = 1;
person.name = "try";
System.out.println("try 处 -->> " + person);
return person;
} finally {
person = new Person();
person.name = "finally";
System.out.println("finally 处 ->> " + person);
}
}
static class Person {
public Person() {
}
String name;
int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
}
打印结果:
try 处 -->> Person{name='try', age=1}
finally 处 ->> Person{name='finally', age=0}
main 处-->> Person{name='try', age=1}
返回到main函数的结果是在try代码块中创建的name为try的person对象,而不是在finally代码块中更改后的name为finally的person对象,可见return标记了返回了的值,不可更改。
综上:
返回的时候是能执行到的最后一个return后面对应的值(例子中是引用地址值),finally关键字后面的代码块一定会执行。若前面已经执行return,则return标记变量(这里变量指person,实际上标记了变量对应的引用地址值),后续逻辑即使改变了变量指向的地址值,因此并不对return的结果产生任何影响。