如何看破真假美猴王 ? --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解惑
如何看破真假美猴王 ? --java中的Shadowing和Obscuring的更多相关文章
- java中的锁
java中有哪些锁 这个问题在我看了一遍<java并发编程>后尽然无法回答,说明自己对于锁的概念了解的不够.于是再次翻看了一下书里的内容,突然有点打开脑门的感觉.看来确实是要学习的最好方式 ...
- java中的字符串相关知识整理
字符串为什么这么重要 写了多年java的开发应该对String不陌生,但是我却越发觉得它陌生.每学一门编程语言就会与字符串这个关键词打不少交道.看来它真的很重要. 字符串就是一系列的字符组合的串,如果 ...
- Java中的Socket的用法
Java中的Socket的用法 Java中的Socket分为普通的Socket和NioSocket. 普通Socket的用法 Java中的 ...
- java中Action层、Service层和Dao层的功能区分
Action/Service/DAO简介: Action是管理业务(Service)调度和管理跳转的. Service是管理具体的功能的. Action只负责管理,而Service负责实施. DAO只 ...
- Java中常用集合操作
一.Map 名值对存储的. 常用派生类HashMap类 添加: put(key,value)往集合里添加数据 删除: clear()删除所有 remove(key)清除单个,根据k来找 获取: siz ...
- java中的移位运算符:<<,>>,>>>总结
java中有三种移位运算符 << : 左移运算符,num << 1,相当于num乘以2 >> : 右移运算符,num >& ...
- 关于Java中进程和线程的详解
一.进程:是程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程,是一个动态的实体,它有自己的生命 周期.它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而 ...
- Java中的进程和线程
Java中的进程与线程 一:进程与线程 概述:几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进程运行时,内部可能包括多个顺序执行流,每个顺序执行流就是 ...
- Java中的进程与线程(总结篇)
详细文档: Java中的进程与线程.rar 474KB 1/7/2017 6:21:15 PM 概述: 几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进 ...
随机推荐
- python入门(三)列表、元组、range()、字典
列表(list) 列表简介:列表(list)是处理一组有序项目的数据结构.用方括号[]表示.可以进行添加,删除,替换,搜索操作.是可变的数据类型.列表可以嵌套和支持索引. name=[12," ...
- Leetcode之二分法专题-374. 猜数字大小(374. Guess Number Higher or Lower)
Leetcode之二分法专题-374. 猜数字大小(374. Guess Number Higher or Lower) 我们正在玩一个猜数字游戏. 游戏规则如下:我从 1 到 n 选择一个数字. 你 ...
- JS核心之DOM操作 上
JS一个重要功能就是操作DOM, 改变页面显示. 目录: 1.基本概念 2.节点类型 3.节点关系 4.节点操作 基本概念 DOM全称为Document Object Model ,即文档对象模型,是 ...
- ParquetDecodingException: Can not read value at 0 in block -1 in file hdfs:...
: jdbc:hive2://master01.hadoop.dtmobile.cn:1> select * from cell_random_grid_tmp2 limit 1; INFO : ...
- Bluetooth(蓝牙)连接过程分析
一 基本概念 蓝牙的连接过程是十分重要的,特别是做蓝牙的技术人员来说,这个是十分重要的.理它的流程,是一件必修课.虽然进入蓝牙行业很久了,以前没怎么系统化的做一些事情,趁此机会,就梳理一下这里面的内容 ...
- gym/102059/problem/I. Game on Plane SG函数做博弈
传送门: 题意: 给定一个正n边形的点.双方轮流连点成线,要求所画的线不能与之前的线相交.当某个人连成一个回路,这个人就输了.问先手必胜还是后手必胜. 思路: SG函数,因为一条线相当于把图劈成了两半 ...
- 牛客国庆集训派对Day3 B Tree(树形dp + 组合计数)
传送门:https://www.nowcoder.com/acm/contest/203/B 思路及参考:https://blog.csdn.net/u013534123/article/detail ...
- CodeForces 1082 G Petya and Graph 最大权闭合子图。
题目传送门 题意:现在有一个图,选择一条边,会把边的2个顶点也选起来,最后会的到一个边的集合 和一个点的集合 , 求边的集合 - 点的集合最大是多少. 题解:裸的最大权闭合子图. 代码: #inclu ...
- CodeForces 982 C Cut 'em all!
Cut 'em all! 题意:求删除了边之后,剩下的每一块联通块他的点数都为偶数,求删除的边最多能是多少. 题解:如果n为奇数,直接返回-1,因为不可能成立.如果n为偶数,随意找一个点DFS建树记录 ...
- CodeForces 980 C Posterized
Posterized 题意:将[0,255] 分成 若干段, 每一段的长度最多为k, 每一个数只能被放进一个段里, 然后每一段的数组都可以被这一段最小的数字表示, 求最小的字典序. 题解:每次一个访问 ...