java匿名内部类访问局部变量为啥要用final

要明白这个问题,首先要知道java参数传递方式只有值传递,即函数调用过程中形参的改变不会影响实参。允许一下代码:

 1 public class Demo {
 2 
 3     private User user;
 4     public Demo(User user){
 5         user.setName("李四");
 6         user.setAge(234);
 7         this.user=user;
 8     }
 9 
10     public static void main(String[] args) {
11         User user=new User();
12         user.setName("张三");
13         user.setAge(20);
14         Demo demo=new Demo(user);
15         System.out.print(user);
16     }
17 
18 

 

 

 

 

 

 

 哎?怎么和书上说的对不上?说好的值传递呢?这个问题也困扰了我一段时间。再修改一下运行:

 1 public class Demo {
 2 
 3     private User user;
 4     public Demo(User user){
 5         user=new User();
 6         user.setName("李四");
 7         user.setAge(234);
 8         this.user=user;
 9     }
10 
11     public static void main(String[] args) {
12         User user=new User();
13         user.setName("张三");
14         user.setAge(20);
15         Demo demo=new Demo(user);
16         System.out.print(user);
17     }
18 
19 }

 

 

 这一次我在第7行加了一行代码 user=new User(); 重新创建了对象,执行结果就完全不一样了。为啥会出现这种情况?这是因为java引用对象的传递是将实际参数的引用地址复制一份传给形参,即两个引用地址对应一个对象,当形参修改时实参也会跟着变化,即第一种情形。若形参重新创建一个对象,会重新分配一个新对象的引用地址,这样形参的修改不会影响实参,即第二情形

ps:若参数是基本类型,那么传递的是复制基本类型的值,形参的改变不会影响实参。

了解的参数传递,我们来看下包含匿名内部类使用代码:

public class Demo {

    private User user;
    public Demo(User user){
        user=new User();
        user.setName("李四");
        user.setAge(234);
        this.user=user;
    }

    public static void main(String[] args) {
        final User user=new User();
        user.setName("张三");
        user.setAge(20);
        //Demo demo=new Demo(user);
        //System.out.print(user);

        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(user);
            }
        });
        thread.start();
    }

}

在看下匿名内部类Demo$1.class 反编译后结果

class Demo$1 implements Runnable {
    Demo$1(User var1) {
        this.val$user = var1;
    }

    public void run() {
        System.out.println(this.val$user);
    }
}

编译器将匿名内部类所使用的局部变量传入了构造函数中,根据我们对前面参数传递的理解,这是一个复制的引用地址。但是从程序员的角度来看,这两个应该是一个对象。如果java不在语义上限制,有可能会造成我们人认为是一个对象,而机器理解是两个对象,造成程序得出结果和预期不符的情况,所以java在匿名类中引用局部变量必须用final来限制修改,避免造成歧义。

posted @ 2022-11-15 22:02  简单才是好  阅读(371)  评论(0)    收藏  举报