Java小白踩坑录 - Shadowing & Obscuring 揭秘

简介: Java小白踩坑录 - Shadowing & Obscuring 揭秘

image.png

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

publicclassPet {
publicfinalStringname;
publicfinalStringfood;
publicfinalStringsound;
publicPet(Stringname, Stringfood, Stringsound) {
this.name=name;
this.food=food;
this.sound=sound;
    }
publicvoideat() {
System.out.println(name+": Mmmmm, "+food);
    }
publicvoidplay() {
System.out.println(name+": "+sound+" "+sound);
    }
publicvoidsleep() {
System.out.println(name+": Zzzzzzz...");
    }
publicvoidlive() {
newThread() {
publicvoidrun() {
while (true) {
eat();
play();
sleep();
                }
            }
        }.start();
    }
publicstaticvoidmain(String[] args) {
newPet("Fido", "beef", "Woof").live();
    }
}

我们期望程序打印:

Fido: Mmmmm, beefFido: WoofWoofFido: 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) throwsInterruptedException;
/*** 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, intnanos)
throwsInterruptedException {
if (millis<0) {
thrownewIllegalArgumentException("timeout value is negative");
 }
if (nanos<0||nanos>999999) {
thrownewIllegalArgumentException(
"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.


注:如果形参的名字用在方法体、构造器体或者 lambda 表示式体内声明的新变量,将会抛出编译时错误,除非该新变量是在方法体、构造器体或者 lambda 表示式所包含的类声明内声明的。


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.


注:如果局部变量v的名字用来在v的作用域内声明新的变量,将会抛出编译时错误,除非该新变量是其类声明在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.


注:如果表达式参数的名字用在catch子句的语句块内声明的新变量,将会抛出编译时错误,除非该新变量是在catch子句的语句块所包含的类声明内声明的。


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.


注:如果局部类c的名字用来在c的作用域内声明新的局部类,将会抛出编译时错误,除非该新局部类是在其类声明在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 举例:

classTest1 {
publicstaticvoidmain(String[] args) {
inti;
for (inti=0; i<10; i++)
System.out.println(i);
 }
}

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

classTest2 {
publicstaticvoidmain(String[] args) {
inti;
classLocal {
        {
for (inti=0; i<10; i++)
System.out.println(i);
        }
    }
newLocal();
 }
}

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

classTest3 {
publicstaticvoidmain(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(); 限定具体的方法:

publicvoidlive() {
newThread() {
publicvoidrun() {
while (true) {
eat();
play();
Pet.this.sleep();
            }
        }
    }.start();
}

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

publicvoidpetSleep() {
System.out.println(name+": Zzzzzzz...");
}

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

publicvoidlive(){
newThread(newRunnable(){
publicvoidrun(){
while(true){
eat();
play();
sleep();
        }
        }
    }).start();
}
目录
相关文章
|
存储 算法 安全
【加密算法】AES对称加密算法简介
【加密算法】AES对称加密算法简介
|
算法 Unix Linux
Linux与Qt线程优先级的对应关系:一次全面解析
Linux与Qt线程优先级的对应关系:一次全面解析
333 0
|
测试技术
CRC-16 MODBUS原理,附实测可用源码
之前做串口解析,CRC校验一直用和校验,就是吧各个位加在一起,新来一个串口协议,是CRC-16 MODBUS的形式校验,不会呀,从网上找了找资源,没有找到源码,都要下载,分享出来。
CRC-16 MODBUS原理,附实测可用源码
|
NoSQL Redis Docker
Docker中Redis数据迁移到本地
Docker中Redis数据迁移到本地
484 1
|
前端开发 JavaScript
React 表单处理技巧
【10月更文挑战第24天】本文从初学者角度出发,详细介绍了 React 中表单处理的基本概念、常见问题及解决方案。涵盖受控组件与非受控组件的区别、状态更新、表单验证、多字段管理及高级技巧,通过代码示例帮助读者更好地理解和应用。
309 7
|
SQL 监控 Oracle
Oracle SQL性能优化全面指南
在数据库管理领域,Oracle SQL性能优化是确保数据库高效运行和数据查询速度的关键
1526 6
|
API
【threejs教程】场景视角切换的神器:轨道控制器
【8月更文挑战第5天】threejs教程:场景视角切换的神器,轨道控制器
932 1
【threejs教程】场景视角切换的神器:轨道控制器
|
缓存 前端开发 数据库
项目部署(三)
项目部署(三)
|
机器学习/深度学习 TensorFlow 算法框架/工具
使用Python实现深度学习模型:迁移学习与领域自适应教程
【7月更文挑战第3天】 使用Python实现深度学习模型:迁移学习与领域自适应教程
374 0
|
SQL 关系型数据库 MySQL
MySQL密码修改的解决方法大全
MySQL密码修改的解决方法大全