java中的Return和Finally的执行

在try catch块以及finally中return的位置直接影响程序执行的结果,我以返回值的类型和return出现的位置分类对各个情况的执行结果进行说明。
先上结论:

  1. 当返回类型为基本数据类型时:
    (1) finally中没有return,返回值就是return处的值。eg:

              public static void main(String[] args) {
                            System.out.println(testPrimitive());           
            } 
            
             public static int testPrimitive() {
    		        int x = 0;
    		        try {
    		            x = 1;
    		            return x;
    		        } catch (Exception e) {
    		            x = 2;
    		            return x;
    		        } finally {
    		            x = 3;          //基本类型的返回值,不可被修改
    		         //   return x;    //可以被“具体值”覆盖
    		        }     }
    输出 1
    

    可能有人会想说finally下面有return呢?其实finally下面有return的作用等效于在try块里面和catch块里面加上return,作用是一样的,把try块里面的return和catch里面的return注释掉,然后在finally外面加上return,是一回事。所以,finally外面有return,结果还是1.
    原因:之所以出现这种原因经过查看class文件(可以通过dos命令查看class文件: javap -verbose className或者javap - c className) 可以了解在try的return之后会将x变量的值赋给新的变量x’,然后最终将x’返回,所以fianlly怎么操作x都不影响x‘的值。
    在这里插入图片描述

    (2) finally中有return,首先要说明,正常是不建议在finally块里面写return语句的,原因就是因为
    a: 可能会覆盖正常的流程返回的值造成代码难以理解,
    b:如果finally块中包含了return语句,如果try块中抛出异常即使在catch块重新抛出了异常,则调用该方法的语句也不会获得catch块重新抛出的异常,由于最终会执行finally块,如果在finally中有返回return,n那会得到finally块的返回值,并且不会捕获异常。异常就被“吃”掉了。

    但是如果写了,正常也是可以编译过不报错的,所以这里进行一下具体的分析:
    eg:

     public static void main(String[] args) {
     
    System.out.println(test());;
    }
    
        static int test()
    {
    	int x = 2;
    	try
    	{
    		x++;
    	  // int	i =  1/0;
    		return x;
    	}catch(Exception e){
    		x++;
    		// int	i =  1/0;
    		return x;
    	}finally
    {
    
    	//try {
    	//	int i = 1/0;
    	
    	++x;
    	return x;
    }
    

    }
    输出 3.
    原因
    在这里插入图片描述
    这里给出一个示例,通过查看calss字节码文件可知,在执行try块的时候在返回之前直接goto到了finally,try块中的return x并没有执行到。返回的仍旧是变量x.
    以上是基本类型的,那接下来再看看引用类型的。
    (1)finally中没有return

     public static void main(String[] args) {
            System.out.println(test());
            //System.out.println(testPrimitive());
        }
     
    public static Student test() {  //对象的地址值没有变
        Student result = new Student("Tom", 0);
        try {
            result.setAge(1);
            return result;
        } catch (Exception e) {
            result.setAge(2);
            return result;
        } finally {
            result.setAge(3);       //引用类型的返回值,可被修改
            //return new Student("Kobe", 33);   //可以被“具体值”覆盖
        }
      //  return new Student("Kobe", 33); 
    }
    输出:Tom 3
    

原因:这个很容易理解,因为java中应用类型时址传递,因此finally中其实是对new Student(“Tom”, 0);这个对象的地址操作,看字节码:
在这里插入图片描述
(2)finally中有return

public static Student test() {  //对象的地址值没有变
		        Student result = new Student("Tom", 0);
		        try {
		            result.setAge(1);
		            return result;
		        } catch (Exception e) {
		            result.setAge(2);
		            return result;
		        } finally {
		           // result.setAge(3);       //引用类型的返回值,可被修改
		         //  return result;
		            return new Student("Kobe", 33);   //可以被“具体值”覆盖
		        }
		      //  return new Student("Kobe", 33); 
		    }

同样字节码中同样goto了,看图:
在这里插入图片描述
所以这里最后输出Kobe 33
再贴一个例子大家看看:

  public static void main(String[] args) {
	    System.out.println(getMap().get("KEY").toString());
	}
 
public static Map<String, String> getMap() {
    Map<String, String> map = new HashMap<String, String>();
    map.put("KEY", "INIT");
     
    try {
        map.put("KEY", "TRY");
       // System.out.println(map + "111111");
        return map;
    }
    catch (Exception e) {
        map.put("KEY", "CATCH");
        return map;
    }
    finally {
        map.put("KEY", "FINALLY");
    	// System.out.println(map + "222222");
        map = null;
       // System.out.println(map + "333333");
      //  return map;
    }
   // System.out.println(map + "444444");
   // return map;
}

刚开始我一直以为会是空指针,因为按照字节码在try块的return处拷贝了一个变量出来,但是在finally处已经将map这个对象置空了,所以应该拷贝的对象也是空,但是最后输出结果是FINALLY后来发现自己一直有一个知识点错误:
看下例:

public static void main(String[] args) {
       // System.out.println(test());
        //System.out.println(testPrimitive());
		Student result = new Student("Tom", 0);
		System.out.println(result);
		Student s = result;
		//s.setAge(20);
		//result.setName("Jerry");
		System.out.println(s);
		result = null;
		//s.setAge(10);
		System.out.println("--------------");
		System.out.println(result);
		System.out.println(s);
    }
    看输出:
    com.csdn.ReturnAndFinallyTest$Student@179935d
    com.csdn.ReturnAndFinallyTest$Student@179935d
    --------------
    null
    com.csdn.ReturnAndFinallyTest$Student@179935d

我最初以为result置空之后s应该也是空,但是实际情况是s还是指向的是原对象,所以Student s = result;可以理解为将new Student(“Tom”, 0);这个对象再增加一个引用,s也指向这个对象,当result为nul时,s仍然不变。
类似的错误还有:

Clazz c1 = new Clazz();
c1.setP1("123);
System.out.println(c1.getP1()); //输出123
method(c1);
System.out.println(c1.getP1());//还是输出123,因为method方法中的c1是局部变量,方法执行完就弹栈了。如果没有c1 = new Clazz(); 那这里输出的就是234
  void  method(Clazz c1){
       c1 = new Clazz();
       c1.setP1("234");
 
};

以上。
参考:http://www.cnblogs.com/aigongsi/archive/2012/04/19/2457735.html
指令集参考:
https://www.cnblogs.com/longjee/p/8675771.html

猜你喜欢

转载自blog.csdn.net/wzp1124358508/article/details/84934344