为什么从线程调用类函数在Java中工作?



所以我得到了这个奇怪的场景,工作得很好,但没有任何意义,因为它为什么工作。根据我在c++中的经验,我非常确定这根本不起作用,并且会在编译期间抛出错误。

public class Practice {
private void testFunction() {
System.out.println("working fine");
System.out.println("testFunc: " + this);
}
public void start() {
System.out.println("start: " + this);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("run: " + this);
testFunction();
}
}).start();
}
}
// Inside main
Practice practice = new Practice();
practice.start()

输出
start: com.company.Practice@5e2de80c
run: com.company.Practice$1@6720b744
working fine
testFunc: com.company.Practice@5e2de80c

为什么! ?为什么会这样呢?我如何能够从一个Runnable调用testFunction()?我是不是应该创建一个新的实例,然后像Practice p = new Practice(); p.testFunction()那样调用这个函数?Java怎么知道testFunction()Practice类的一部分而不是Runnable?

还有,为什么testFunction()this的值与start()相同?它不应该和run()一样吗?

有一个"内部类";这里。

内部类的实例(Runnable的匿名子类)将包含对创建它们的外部类的引用(这里是Practice的实例),并且可以调用这些外部实例的方法。

你的testFunction();被编译成this.$myOuterInstance.testFunction();

Java如何知道testFunction()是Practice类的一部分而不是Runnable?

编译器通过首先查看内部类,然后转到外部作用域来解决这个问题,外部作用域不仅包括包含类,还包括在start方法中可能具有的任何有效的最终局部变量。

这与变量和字段的解析方式类似(当您有名称冲突时使用一些阴影规则)。

而且,testFunction()中的值如何与start()相同?它不应该和run()一样吗?

testFunctionPractice的实例上被调用(start也是)。

run在你的可运行Practice$1的实例上被调用。

您可以访问testFunction,因为您正在使用testFunction简单名称

this.testFunction();

那么你就不能访问它了

当你用简单的名字调用一个方法时,范围内的所有东西都被认为是解析这个方法的。(参见JLS 6.5.7.1)testFunction肯定在Runnable的范围内,因为它在Practice的范围内。

要调用testFunction,您仍然需要Practice的实例,对吗?好吧,编译器生成一些代码,将Practice的实例传递给start调用(即thisstart中的含义)到您正在创建的Runnable实例。具体来说,它创建了一个非静态内部类

class Practice {
public void start() {
...
new Thread(new SomeGeneratedRunnable()).start();
}
// not the actual name of the class. The actual name is Practice$1
private class SomeGeneratedRunnable implements Runnable {
public void run() {
System.out.println("run: " + this);
testFunction();
}
}
}

你可以这样想:

class Practice {
public void start() {
...
new Thread(new SomeGeneratedRunnable(this)).start();
}
}
class SomeGeneratedRunnable implements Runnable {
private final Practice outer;
public SomeGeneratedRunnable(Practice outer) {
this.outer = outer;
}
public void run() {
System.out.println("run: " + this);
outer.testFunction();
}
}

现在你应该明白为什么testFunction中的this不可能与Runnable中的this相同。Runnable中的thisSomeGeneratedRunnable(或Practice$1)的实例,而testFunction中的thisPractice的实例。