zoukankan      html  css  js  c++  java
  • 如何看破真假美猴王 ? --java中的Shadowing和Obscuring

    故事背景

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

     

    java之真假美猴王

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

    public class Pet {
        public final String name;
        public final String food;
        public final String sound;
        public Pet(String name, String food, String sound) {
            this.name = name;
            this.food = food;
            this.sound = sound;
        }
        public void eat() {
            System.out.println(name + ": Mmmmm, " + food);
        }
        public void play() {
            System.out.println(name + ": " + sound + " " + sound);
        }
        public void sleep() {
            System.out.println(name + ": Zzzzzzz...");
        }
        public void live() {
            new Thread() {
                public void run() {
                    while (true) {
                        eat();
                        play();
                        sleep();
                    }
                }
            }.start();
        }
        public static void main(String[] args) {
            new Pet("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.
     */
     public static native void sleep(long millis) 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.
     */
     public static void sleep(long millis, int nanos)
     throws InterruptedException {
     if (millis < 0) {
     throw new IllegalArgumentException("timeout value is negative");
     }
     if (nanos < 0 || nanos > 999999) {
     throw new 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 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.
    
    It 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.
    
    It 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.
    
    It 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.

    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 {
     public static void main(String[] args) {
     int i;
     for (int i = 0; i < 10; i++)
     System.out.println(i);
     }
    }

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

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

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

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

    原因找到了,那该怎么解决呢?

    问题解决

    方式一:线程内调用,改成Pet.this.sleep();限定具体的方法:

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

    方式二:

    将sleep名称改为其它不冲突的名称,如petSleep,然后线程内调用该方法

        public void petSleep() {
            System.out.println(name + ": Zzzzzzz...");
        }

    方式三:也是最好的方式,使用Thread(Runnable)构造器来替代对Thread 的继承。那个匿名类不会再继承Thread.sleep 方法,故也不会有冲突了。

    public void live(){
    new Thread(new Runnable(){
    public void run(){
    while(true){
    eat();
    play();
    sleep();
    }
    }
    }).start();
    }

    参考资料

    【1】https://docs.oracle.com/javase/specs/jls/se12/html/jls-6.html#jls-6.4

    【2】java解惑

  • 相关阅读:
    jzoj5377 开拓
    JZOJ5371 组合数问题
    JZOJ 10043 第k小数
    联赛emacs配置
    11.7 NOIP总复习总结
    cogs791 [HAOI2012] 音量调节
    bzoj1968 [Ahoi2005]COMMON 约数研究
    cogs 1330 [HNOI2008]玩具装箱toy
    cogs2479 偏序 cdq+树套树
    【CJOJ2433】陌上花开 CDQ分治
  • 原文地址:https://www.cnblogs.com/davidwang456/p/11613260.html
Copyright © 2011-2022 走看看