之前在翻译学习EOPL过程中回顾以前的代码时发现一个让人后背发凉的隐患,一种极其罕见、但是一旦出现就难以发现并可能造成非常大影响的bug,本文就记录下这个问题。

问题场景

下面来看一段常见的示例程序:

public class DemoActivity extends Activity {public static final String TAG = DemoActivity.class.getSimpleName();@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);findViewById(R.id.btn_test).setOnClickListener(new BaseClickListener() {@Overrideprotected void onClicked(View v) {Log.i(TAG, "onClicked: clicked");}});}
}

这是一个防止快速点击的示例,这个程序中类DemoActivity包含了一个BaseClickListener的匿名内部类,然后这个类中引用了外部类的声明的变量TAG(好像是这样)。但事实上,BaseClickListener的实现是这样的:

public abstract class BaseClickListener implements View.OnClickListener {public static final String TAG = BaseClickListener.class.getSimpleName();private long lastClickTime = 0L;@Overridepublic final void onClick(View v) {long currentTime = System.currentTimeMillis();if (currentTime - lastClickTime >= 500) {onClicked(v);} else {Log.d(TAG, "onClick: click to fast");}lastClickTime = currentTime;}protected abstract void onClicked(View v);
}

因此在DemoActivity中匿名内部类引用的其实是BaseClickListener类中声明的TAG。这里发生了变量遮蔽的现象,而且还是一种不可见的、“隐式”的遮蔽。这种遮蔽的存在使得外部文件的修改可以直接影响当前文件的语义。

试想,这两个类属于不同的模块,甚至不同的项目,使用BaseClickListener的人甚至有可能不会去看源码、甚至拿不到源码,而编写BaseClickListener的人更不会考虑DemoActivity的实现。一开始BaseClickListener类中可能没有TAG变量,DemoActivity中对TAG的引用指向DemoActivity.TAG,后来BaseClickListener引入了这个变量,这时候,不管是DemoActivity的开发者还是BaseClickListener的开发者都不知道这里出了问题,而DemoActivity的程序语义已经发生了改变。

上述示例程序中问题范围仅仅是打印日志,所以影响范围有限。但如果换成其他的变量,比如常见的idrootViewnamecontentcurrentTimecount等等等等,那么运气好的情况下直接编译不过,运气不好的情况程序崩溃甚至数据污染,而且这个问题需要通过跟踪变量引用才能发现,光肉眼看还可能看不出来。

我还没有在实际项目中遇到过这个问题,但如果真的遇到了,那我可能要跟我的同事“友尽”了,因此将其列为“友尽”级bug。

问题分析

上面问题产生原因如下:

  1. 这是“隐式”发生的遮蔽,还是无意识的遮蔽,且遮蔽跨越多文件;
  2. 变量/常量/方法声明定义与使用跨多文件,且引用采用简化形式,并非完全形式;

首先简要谈谈遮蔽(shadow)。(之所以简要,是因为这个概念结合作用域、上下文来讲比较容易讲透,这个另外在写文章总结)

遮蔽在现在大部分流行语言里都或多或少出现一点,除了上面展示的,还有例如下面javascript程序中典型的遮蔽:

function outer(x){function inner(x){{let x = 3console.log("x1:" + x)// x = 3}console.log("x2:" + x)// x = 2}console.log("x3:" + x)// x = 1inner(x + 1)
}outer(1)

这种遮蔽还相对安全,因为是可见范围内发生的显式遮蔽。但是Java的类作用域与词法作用域结合使得可以发生隐式的遮蔽,只要出现内部类,不管是成员域还是方法甚至局部变量都有可能发生这类遮蔽。这是语言设计中的意外,对于语言使用者来说难以避免这类问题。

一点点建议

  • 最差的建议:避免使用内部类;
  • 次好的建议:对于类的成员域以及方法的访问采用全限定名,比如:OuterClass.this.name
  • 较好的建议:外部类可使用简写来引用成员域或方法,内部类则采用全名。

记录一个因变量遮蔽引起的“友尽”级bug相关推荐

  1. 记录一个android性能优化宝藏级总结

    发现一个android性能优化文章宝藏级总结,太赞了,感谢大佬的无私奉献总结,防止丢失,在此记录一下 传送门 复制一些目录,增加一些篇幅{嘻嘻} 优化心得和经验 抖音 Android 性能优化系列:启 ...

  2. 两个自变量和一个因变量spss_多个自变量(包括离散变量和连续变量)对一个因变量的影响(SPSS:协方差分析)...

    协方差分析解决的问题:多个自变量(包括离散变量和连续变量)对一个因变量(连续数据)的影响.自变量中的连续变量被作为协变量加以"控制"(控制变量). 协方差分析可以在一定程度上排除非 ...

  3. 记录一个海思TOE的BUG

    原始引用地址: 记录一个海思TOE的BUG time: 2020.5.3 17:57 发现的过程 ​ 最近在做onvif开发时,有x86的验证的功能没有问题,移动到海思Hi3536上简单运行貌视也很正 ...

  4. 两个自变量和一个因变量spss_多个自变量对一个因变量的影响(SPSS:协方差分析)...

    协方差分析解决的问题:多个自变量(包括离散变量和连续变量)对一个因变量(连续数据)的影响.自变量中的连续变量被作为协变量加以'控制'(控制变量). 协方差分析可以在一定程度上排除非处理因素的影响,从而 ...

  5. 复习Java第一个项目学生信息管理系统 04(权限管理和动态挂菜单功能) python简单爬数据实例Java面试题三次握手和四次挥手生活【记录一个咸鱼大学生三个月的奋进生活】016

    记录一个咸鱼大学生三个月的奋进生活016 复习Java(学生信息管理系统04权限管理和动态挂菜单功能) 改写MainFrame的构造方法 新增LoginFrame的验证登录是否成功的代码 新增Logi ...

  6. 记录一个vue监控屏幕宽度的问题

    记录一个vue监控屏幕宽度的问题 因为今天有查询过如何用vue监控屏幕宽度的问题,搜到的很多都是这样的写法: data() {return {screenWidth: null, //屏幕尺寸};}, ...

  7. 记录一个db2 中LISTAGG函数问题:The length resulting from “LISTAGG“ is greater than “4000“

    记录一个db2 中LISTAGG函数问题 什么是LISTAGG函数 LISTAGG("字段名","分隔符") as "自定义字段名" 就是将 ...

  8. 记录一个C++多线程的坑

    记录一个C++多线程的坑 VS2019报错 解决方案: 错误代码 修改方案: 写在最后,发牢骚 VS2019报错 1>C:\Program Files (x86)\Microsoft Visua ...

  9. 记录一个CentOS 6版本中,yum install 命令无法实现的问题 # 谭子

            记录一个CentOS 6版本中,yum install 命令无法实现的问题. 在初次使用yum install mysql命令时,出现了下文内容,这里的报错内容主要指向的内容是 Cen ...

最新文章

  1. oracle分区交换速度,oracle交换分区对数据的加载提速案例
  2. onrsd.exe应用程序错误
  3. android之下载416错误
  4. ASP 调用dll(VB)及封装dll实例
  5. Windows下多个JDK版本的切换方法
  6. mybatis学习笔记-03-CRUD操作
  7. 无人驾驶(基于计算机视觉的高精度地图)
  8. 随想录(thread类的编写)
  9. Nginx启动/重启脚本详解
  10. 程序员有了孩子,老大叫玲玲,老二叫玲依,老三叫...
  11. struts2第一个程序的详解(配图)
  12. VIIRS和DMSP夜间灯光数据(1992-2020)
  13. JavaWeb笔记(一)Java网络编程
  14. linux安装lrzsz工具
  15. 公有云与私有云的概念解读与优势分析
  16. 计算机硬件找不到网络适配器,图文学习网络适配器不见了 【操作教程】 的恢复方法_...
  17. 华为服务器批量系统软件,华为云ECS批量管理工具
  18. 赵小楼《天道》深度解析(75)客观是对现有事实的认可,嘴上认可可不行,得心里认,否则就是自欺
  19. 60天造个火箭给你玩玩,你想要不?
  20. 学习笔记:李航统计学习方法

热门文章

  1. Word2016自定义快速访问工具栏将最常用的命令或者工具添加进去
  2. python爬取豆瓣TOP250电影
  3. 等保评测要求和评测材料需要哪些 你想知道的全攻略都在这里
  4. 消费力回落,名创优品大涨:深耕兴趣消费实现逆势增长
  5. [转] [Flash/Flex] 使用Flare3D来实现Flash 3D的推箱子游戏原型---纹理
  6. 2011年RSA大会纪实:手机恶意软件依旧是摇钱树
  7. 关于container_of的理解
  8. 2018 0612 ----最小系统STM32 + oled(SPi连接)
  9. 《深入理解 C# (第2版)》 - 学习笔记
  10. HTML 学习 | Day01 HTML简介(网页、常用的浏览器、Web标准)