故事背景
《西游记》第五十七回:唐僧因悟空又打死拦路强盗,再次把他撵走。六耳猕猴精趁机变作悟空模样,抢走行李关文,又把小妖变作唐僧、八戒、沙僧模样,欲上西天骗取真经。真假二悟空从天上杀到地下,菩萨、玉帝、地藏王等均不能辨认真假,直到雷音寺如来佛处,才被佛祖说出本相,猕猴精被悟空打死。

java之真假美猴王
java中有时候也会出现真假美猴王的事件,请看下面的程序后打印什么?

publicclass Pet {
publicfinal String name;
publicfinal String food;
publicfinal String sound;
public Pet(String name, String food, String sound) {
this.name = name;
this.food = food;
this.sound = sound;
}
publicvoid eat() {
System.out.println(name + ": Mmmmm, " + food);
}
publicvoid play() {
System.out.println(name + ": " + sound + " " + sound);
}
publicvoid sleep() {
System.out.println(name + ": Zzzzzzz...");
}
publicvoid live() {
new Thread() {
publicvoid run() {
while(true) {
eat();
play();
sleep();
}
}
}.start();
}
publicstaticvoid main(String[] args) {
newPet("Fido", "beef", "Woof").live();
}
}

我们期望程序打印:
Fido: Mmmmm, beef
Fido: Woof Woof
Fido: Zzzzzzz…
实际上报编译错误。
The method sleep(long) in the type Thread is not applicable for the arguments ()
查看Thread的sleep方法:

/** * Causes the currently executing thread to sleep (temporarily cease
* execution) for the specified number of milliseconds, subject to
* the precision and accuracy of system timers and schedulers. The thread
* does not lose ownership of any monitors.
*
* @param millis
* the length of time to sleep in milliseconds
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/publicstaticnativevoidsleep(longmillis)throws InterruptedException;
/** * Causes the currently executing thread to sleep (temporarily cease
* execution) for the specified number of milliseconds plus the specified
* number of nanoseconds, subject to the precision and accuracy of system
* timers and schedulers. The thread does not lose ownership of any
* monitors.
*
* @param millis
* the length of time to sleep in milliseconds
*
* @param nanos
* {@code 0-999999} additional nanoseconds to sleep
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative, or the value of
* {@code nanos} is not in the range {@code 0-999999}
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/publicstaticvoidsleep(longmillis,int nanos)
throws InterruptedException {
if(millis < 0) {
thrownewIllegalArgumentException("timeout value is negative");
}
if(nanos < 0 || nanos > 999999) {
thrownew IllegalArgumentException(
"nanosecond timeout value out of range");
}
if(nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
sleep(millis);
}

等等!

我不是要调用Thread的sleep方法,而是要调用Pet的sleep方法。为什么出现这种情况呢?
JSL-6.4定义了这种情况:

It is a compile-time errorifthe name of a formal parameter is used to declare anewvariable within the body of the method, constructor, or lambda expression, unless thenewvariable is declared within aclass declaration contained by the method, constructor, or lambda expression.
It is a compile-time errorifthe name of a local variable v is used to declare anewvariable within the scope of v, unless thenewvariable is declared within aclass whose declaration is within the scope of v.
It is a compile-time errorifthe name of an exception parameter is used to declare anewvariable within the Block of thecatchclause, unless thenewvariable is declared within aclassdeclaration contained by the Block of thecatch clause.
It is a compile-time errorifthe name of a localclassC is used to declare anewlocalclasswithin the scope of C, unless thenewlocalclassis declared within anotherclasswhose declaration is within the scope of C.

java中有Shadowing(遮蔽)的描述,其中:
Shadowing:Some declarations may be shadowed in part of their scope by another declaration of the same name, in which case a simple name cannot be used to refer to the declared entity.
简单的意思是:在作用域内,一个地方的声明可能被另一个同名的声明所遮蔽。在这种情况下不能简单的使用名字来引用他们所声明的实体。
变量Shadowing举例:

class Test1 {
publicstaticvoid main(String[] args) {
int i;
for(inti = 0; i < 10; i++)
System.out.println(i);
}
}

编译报错,但编译检测也不是万能的,也有一些trick来逃避:

class Test2 {
publicstaticvoid main(String[] args) {
int i;
class Local {
{
for(inti = 0; i < 10; i++)
System.out.println(i);
}
}
new Local();
}
}

如果在不同block,则不会出现Shadowing的问题:

class Test3 {
publicstaticvoid main(String[] args) {
for(inti = 0; i < 10; i++)
System.out.print(i + " ");
for(inti = 10; i > 0; i--)
System.out.print(i + " ");
System.out.println();
}
}

原因找到了,那该怎么解决呢?
问题解决
方式一:线程内调用,改成Pet.this.sleep();限定具体的方法:

publicvoid live() {
new Thread() {
publicvoid run() {
while(true) {
eat();
play();
Pet.this.sleep();
}
}
}.start();
}

方式二:
将sleep名称改为其它不冲突的名称,如petSleep,然后线程内调用该方法
publicvoid petSleep() {
System.out.println(name + ": Zzzzzzz...");
}
方式三:也是最好的方式,使用Thread(Runnable)构造器来替代对Thread 的继承。那个匿名类不会再继承Thread.sleep 方法,故也不会有冲突了。

publicvoid live(){newThread(new Runnable(){publicvoid run(){while(true){
eat();
play();
sleep();
}
}
}).start();
}

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69901620/viewspace-3125546/ ,如需转载,请注明出处,否则将追究法律责任。