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 @   简单才是好  阅读(363)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
点击右上角即可分享
微信分享提示