也许当你对某段代码进行修改的时候会发现出现了一个警告,系统提醒某处代码的使用有问题,尽管你并没有做针对那段代码的修改。
例如:这样一段代码,这段代码的作用无非是当被调用时打印“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
变量的错误。