zoukankan      html  css  js  c++  java
  • 谜题79: 这是狗的生活

    下面的这个类模拟了一个家庭宠物的生活。main 方法创建了一个 Pet 实例,用
    它来表示一只名叫 Fido 的狗,然后让它运行。虽然绝大部分的狗都在后院里奔
    跑(run),这只狗却是在后台运行(run)。那么,这个程序会打印出什么呢?

    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();
    }
    }
    

      

    main 方法创建了一个用来表示 Fido 的 Pet 实例,并且调用了它的 live 方法。
    然后,live 方法创建并且启动了一个线程,该线程反复的调用其外围
    (enclosing)的 Pet 实例的 eat、play 和 sleep 方法,就这么一直进行下去。
    这些方法都会打印单独的一行,所以你会想到这个程序会反复的打印以下的 3
    行:

    Fido: Mmmmm, beef
    Fido: Woof Woof
    Fido: Zzzzzzz…


    但是如果你尝试运行这个程序,你会发现它甚至不能通过编译。而产生的编译错
    误信息没有什么用处:

    Pet.java:28: cannot find symbol
    symbol: method sleep()
    sleep();


    为什么编译器找不到那个符号呢?这个符号确实是白纸黑字地写在那里。与谜题
    74 一样,这个问题的源自重载解析过程的细节。编译器会在包含有正确名称的
    方法的最内层范围内查找需要调用的方法[JLS 15.12.1]。在我们的程序中,对
    于对 sleep 方法的调用,这个最内层的范围就是包含有该调用的匿名类
    (anonymous class),这个类继承了 Thread.sleep(long)方法和
    Thread.sleep(long,int)方法,它们是该范围内唯一的名称为 sleep 的方法,但
    是由于它们都带有参数,所以都不适用于这里的调用。由于该方法调用的 2 个候
    选方法都不适用,所以编译器就打印出了错误信息。
    从 Thread 那里继承到匿名类中的 2 个 sleep 方法遮蔽(shadow)[JLS 6.3.1]
    了我们想要调用的 sleep 方法。正如你在谜题 71 和谜题 73 中所看到的那样,你
    应该避免遮蔽。在这个谜题中的遮蔽是间接地无意识地发生的,这使得它更加
    “阴险”。
    订正这个程序的一个比较显而易见的方法,就是把 Pet 中的 sleep 方法的名字改
    成 snooze, doze 或者 nap。订正该程序的另一个方法,是在方法调用的时候使

    用受限的(qualified) this 结构来显式地为该类命名。此时的调用就变成了
    Pet.this.sleep() 。
    订正该程序的第三个方法,也是可以被证明是最好的方法,就是采纳谜题 77 的
    建议,使用 Thread(Runnable)构造器来替代对 Thread 的继承。如果你这么做了,
    原有的问题将会消失,因为那个匿名类不会再继承 Thread.sleep 方法。
    程序经过少许的修改,就可以产生我们想要的输出了,当然这里的输出可能有点
    无聊:

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

      


    总之,要小心无意间产生的遮蔽,并且要学会识别表明存在这种情况的编译器错
    误信息。对于编译器的编写者来说,你应该尽力去产生那些对程序员来说有意义
    的错误消息。例如在我们的程序中,编译器应该可以警告程序员,存在着适用于
    方法调用但却被遮蔽掉的方法。

  • 相关阅读:
    【书签】数据可视化(三)- Seaborn简易入门
    【书签】连续型特征的归一化和离散特征的one-hot编码
    【书签】转:对SVM的个人理解---浅显易懂
    【书签】stacking、blending
    【书签】机器学习相关
    正则表达式:匹配单个数字重复n次
    pandas删除DataFrame中任意字段等于'null'字符串的行
    中文的csv文件的编码改成utf-8的方法
    Eslint 从入门到放弃
    图片压缩的网站工具https://tinypng.com/
  • 原文地址:https://www.cnblogs.com/yuyu666/p/9841025.html
Copyright © 2011-2022 走看看