Java中匿名内部类与局部变量访问

也许当你对某段代码进行修改的时候会发现出现了一个警告,系统提醒某处代码的使用有问题,尽管你并没有做针对那段代码的修改。

例如:这样一段代码,这段代码的作用无非是当被调用时打印“name 执行了线程 ID”;

public class InnerClassTest {
    public void doSomethings(){
        String name = "center";

        class ThreadPrint{
            public void printThreadId(long tid){
                System.out.println(name+" implemented thread "+tid);

            }
        }

        ThreadPrint threadPrint = new ThreadPrint();
        Thread thread = new Thread();
        threadPrint.printThreadId(thread.getId());
        
    }

    public static void main(String[] args) {
        InnerClassTest innerClassTest = new InnerClassTest();
        innerClassTest.doSomethings();
    }
}

尽管它写的相当凌乱,这只是为了引出本文的一个示例,事实上 ThreadPrint  所做的事情相当简单,并且其功能与外部类的耦合度不高,完全可以将其提取为一个独立的普通类。

此时代码的运行是没有问题的,我们可以看到控制台打印了出了需要的结果。

center implemented thread 15

但是,当我们要在 doSomethings() 这一方法中再添加针对 name = "system" 的打印时,会发生什么,也许你会这么改。

public class InnerClassTest {
    public void doSomethings(){
        String name = "center";
        int tid = 0;
        class ThreadPrint{
            public void printThreadId(long tid){
                System.out.println(name+" implemented thread "+tid);

            }
        }
        ThreadPrint threadPrint = new ThreadPrint();
        Thread thread1 = new Thread();
        threadPrint.printThreadId(thread1.getId());

        name = "system";
        Thread thread2 = new Thread();
        threadPrint.printThreadId(thread2.getId());
    }

    public static void main(String[] args) {
        InnerClassTest innerClassTest = new InnerClassTest();
        innerClassTest.doSomethings();
    }
}

但是此时你会发现原本正常的name此时已经产生了报错。

 这个报错的原因是:在内部类中访问了外部类方法中的变量 name 。Java 要求如果内部类要访问外部类方法中的局部变量,这个变量必须是 final 或者 effectively final 的。

那么问题来了,在原来的代码中 name = "center" 也并没有被 final 修饰,为什么能够成功通过编译。实时上出现这个情况的原因是什么?在JAVA8以及更高的版本中,即使局部变量未被显示声明为final,但如果其在初始化之后并没有被修改过,那么JAVA会隐式的认为它是effectively final 的,当在内部类中访问该对象的时候,也是可以调用此对象的。

那么在下列代码中又是否会产生报错呢?

public void doSomethings(){
        Student student = new Student("name",12,"数学");
        int tid = 0;
        class ThreadPrint{
            public void printThreadId(long tid){
                System.out.println(student+" implemented thread "+tid);

            }
        }
        ThreadPrint threadPrint = new ThreadPrint();
        Thread thread1 = new Thread();
        threadPrint.printThreadId(thread1.getId());

        student.setName("lisi");
        Thread thread2 = new Thread();
        threadPrint.printThreadId(thread2.getId());
    }

答案是否定的,尽管Student对象在被创建后,值发生过变化,但是由于对象 student 本身的引用没有改变,只是其内部的属性值(如 name)发生了改变。

name implemented thread 15
lisi implemented thread 16

对于内部类访问外部方法的局部对象,只要对象的引用不变,只是对象内部的属性值修改,是被允许的。但如果重新给 student 变量赋了一个新的对象引用,就会导致类似之前 name 变量的错误。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值