故事背景
《西游记》第五十七回:唐僧因悟空又打死拦路强盗,再次把他撵走。六耳猕猴精趁机变作悟空模样,抢走行李关文,又把小妖变作唐僧、八戒、沙僧模样,欲上西天骗取真经。真假二悟空从天上杀到地下,菩萨、玉帝、地藏王等均不能辨认真假,直到雷音寺如来佛处,才被佛祖说出本相,猕猴精被悟空打死。
java之真假美猴王
java中有时候也会出现真假美猴王的事件,请看下面的程序后打印什么?
public class Pet {\n\tpublic final String name;\n\tpublic final String food;\n\tpublic final String sound;\n\tpublic Pet(String name, String food, String sound) {\n\t\tthis.name = name;\n\t\tthis.food = food;\n\t\tthis.sound = sound;\n\t}\n\tpublic void eat() {\n\t\tSystem.out.println(name + ": Mmmmm, " + food);\n\t}\n\tpublic void play() {\n\t\tSystem.out.println(name + ": " + sound + " " + sound);\n\t}\n\tpublic void sleep() {\n\t\tSystem.out.println(name + ": Zzzzzzz...");\n\t}\n\tpublic void live() {\n\t\tnew Thread() {\n\t\t\tpublic void run() {\n\t\t\t\twhile (true) {\n\t\t\t\t\teat();\n\t\t\t\t\tplay();\n\t\t\t\t\tsleep();\n\t\t\t\t}\n\t\t\t}\n\t\t}.start();\n\t}\n\tpublic static void main(String[] args) {\n\t\tnew Pet("Fido", "beef", "Woof").live();\n\t}\n}\n
我们期望程序打印:
Fido: Mmmmm, beef
Fido: Woof Woof
Fido: Zzzzzzz…
实际上报编译错误。
The method sleep(long) in the type Thread is not applicable for the arguments ()
查看Thread的sleep方法:
/**\n * Causes the currently executing thread to sleep (temporarily cease\n * execution) for the specified number of milliseconds, subject to\n * the precision and accuracy of system timers and schedulers. The thread\n * does not lose ownership of any monitors.\n *\n * @param millis\n * the length of time to sleep in milliseconds\n *\n * @throws IllegalArgumentException\n * if the value of {@code millis} is negative\n *\n * @throws InterruptedException\n * if any thread has interrupted the current thread. The\n * <i>interrupted status</i> of the current thread is\n * cleared when this exception is thrown.\n */\n public static native void sleep(long millis) throws InterruptedException;\n /**\n * Causes the currently executing thread to sleep (temporarily cease\n * execution) for the specified number of milliseconds plus the specified\n * number of nanoseconds, subject to the precision and accuracy of system\n * timers and schedulers. The thread does not lose ownership of any\n * monitors.\n *\n * @param millis\n * the length of time to sleep in milliseconds\n *\n * @param nanos\n * {@code 0-999999} additional nanoseconds to sleep\n *\n * @throws IllegalArgumentException\n * if the value of {@code millis} is negative, or the value of\n * {@code nanos} is not in the range {@code 0-999999}\n *\n * @throws InterruptedException\n * if any thread has interrupted the current thread. The\n * <i>interrupted status</i> of the current thread is\n * cleared when this exception is thrown.\n */\n public static void sleep(long millis, int nanos)\n throws InterruptedException {\n if (millis < 0) {\n throw new IllegalArgumentException("timeout value is negative");\n }\n if (nanos < 0 || nanos > 999999) {\n throw new IllegalArgumentException(\n "nanosecond timeout value out of range");\n }\n if (nanos >= 500000 || (nanos != 0 && millis == 0)) {\n millis++;\n }\n sleep(millis);\n }\n
等等!
我不是要调用Thread的sleep方法,而是要调用Pet的sleep方法。为什么出现这种情况呢?
JSL-6.4定义了这种情况:
It is a compile-time error if the name of a formal parameter is used to declare a new variable within the body of the method, constructor, or lambda expression, unless the new variable is declared within a class declaration contained by the method, constructor, or lambda expression.\nIt is a compile-time error if the name of a local variable v is used to declare a new variable within the scope of v, unless the new variable is declared within a class whose declaration is within the scope of v.\nIt is a compile-time error if the name of an exception parameter is used to declare a new variable within the Block of the catch clause, unless the new variable is declared within a class declaration contained by the Block of the catch clause.\nIt is a compile-time error if the name of a local class C is used to declare a new local class within the scope of C, unless the new local class is declared within another class whose declaration is within the scope of C.\n
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 {\n public static void main(String[] args) {\n int i;\n for (int i = 0; i < 10; i++)\n System.out.println(i);\n }\n}\n
编译报错,但编译检测也不是万能的,也有一些trick来逃避:
class Test2 {\n public static void main(String[] args) {\n int i;\n class Local {\n {\n for (int i = 0; i < 10; i++)\n System.out.println(i);\n }\n }\n new Local();\n }\n}\n
如果在不同block,则不会出现Shadowing的问题:
class Test3 {\n public static void main(String[] args) {\n for (int i = 0; i < 10; i++)\n System.out.print(i + " ");\n for (int i = 10; i > 0; i--)\n System.out.print(i + " ");\n System.out.println();\n }\n}\n
原因找到了,那该怎么解决呢?
问题解决
方式一:线程内调用,改成Pet.this.sleep();限定具体的方法:
\tpublic void live() {\n\t\tnew Thread() {\n\t\t\tpublic void run() {\n\t\t\t\twhile (true) {\n\t\t\t\t\teat();\n\t\t\t\t\tplay();\n\t\t\t\t\tPet.this.sleep();\n\t\t\t\t}\n\t\t\t}\n\t\t}.start();\n\t}\n
方式二:
将sleep名称改为其它不冲突的名称,如petSleep,然后线程内调用该方法
\tpublic void petSleep() {\n\t\tSystem.out.println(name + ": Zzzzzzz...");\n\t}\n
方式三:也是最好的方式,使用Thread(Runnable)构造器来替代对Thread 的继承。那个匿名类不会再继承Thread.sleep 方法,故也不会有冲突了。
public void live(){\nnew Thread(new Runnable(){\npublic void run(){\nwhile(true){\neat();\nplay();\nsleep();\n}\n}\n}).start();\n}\n
参考资料
【1】https://docs.oracle.com/javase/specs/jls/se12/html/jls-6.html#jls-6.4
【2】java解惑
版权声明:CosMeDna所有作品(图文、音视频)均由用户自行上传分享,仅供网友学习交流。若您的权利被侵害,请联系删除!
本文链接://www.cosmedna.com/article/731385546.html