关我疑事
- 前言
- 一、原文简介
- 二、分析
- 1.改造代码, 增加信息打印
- 2.分析
- 3. 总结
- 三、体会
前言
闲暇之余, 抱着学习(好奇)的心态去看了这样一篇文章 一段很有意思的代码!! 这是由我最尊敬(指每次遇到比较系统的问题都会去这里学习, 一日为师下句我就不说了~)的公众号 [ 菜鸟教程 ] 转载的 业内分布式技术大佬 [ 冰河 ] 原创的一篇文章. 原文找了半天没看到, 不清楚这个疑问是否有人已经提出. 因此我就斗胆的提出我的拙见~ 如有问题欢迎大家指出. 鄙人在此抱拳~~~
一、原文简介
本文对以下代码在调用 print 方法后, 同时执行了if
和else
分支进行分析, 代码和输出结果如下图所示:
public class Test {
public static void main(String[] args) {
// 核心代码
new Test().print(args==null || new Test() {{Test.main(null);}}.equals(null));
}
public void print(boolean flag){
if(flag){
System.out.println("我是if语句的分支");
}else{
System.out.println("我是else语句的分支");
}
}
}
原作者文中说法可简述为,:
在 || 前 args == null 为 true,执行 print() 方法的 if 语句.
后面因为再次创建了一个 Test 类的对象实例(不为null), 因此 equals((Object)null) 为 false , 执行 print() 方法 else语句
通过留言来看, 大多说人都把作者的描述理解为 || 前面为 true 后, 又继续执行后面的
那么事实真是如此吗?, 看到这里, 我眉头一皱发现事情并不简单
其实, 仔细阅读后你就会发现, 作者的本意不是如此. 因为文中强调 main 方法执行了两次
因此, 综合来看, 作者的意思实际是: 第一次执行 main 时, args == null 为 true , 执行 print() 方法的 if 语句,
然后第二次执行main 时, equals((Object)null) 会返回 false, 执行了 else 语句的逻辑
二、分析
那么? 事情真的如作者说的那样? 出于一种奇怪的本能我发现事情并没有这么简单 于是, 带着疑问我们来对代码动点小手术, 加两行打印语句, 看下执行结果
1.改造代码, 增加信息打印
代码语言:javascript复制 public static void main(String[] args) {
// 判断 args == null 的结果情况
System.out.println(args == null);
new Test().print(args==null || new Test() {
{
System.out.println("第二层main()执行开始" );
Test.main(null);
System.out.println("第二层main()执行结束" );
}
}.equals(null));
}
public void print(boolean flag){
if(flag){
System.out.println("我是if语句的分支");
}else{
System.out.println("我是else语句的分支");
}
}
代码运行结果
2.分析
- 可以看到, 第一次运行时, main函数入参
args == null 是false
! - 为什么 为false ? 这就考察我们 == 的含义这个知识点了
在基本数据类型时 == 比较的是值, 而在对象或其他类型中比较的是引用地址
因为在函数声明时已经定义好 args 数组并在堆内存中开辟空间建立历引用(相当于初始化), 因此
args == null
结果为false - 我们在把核心代码
new Test().print(args==null || new Test() {{Test.main(null);}}.equals(null));
优化成new Test().print(A || B);
, 所以在第一次执行外层main方法时,A = args == null = false
, 由于 || 代表短路或, 只有 || 前后都为false 才返回 false, 而如果 || 前为 false 为 false 时会继续判断后面的语句 在本代码中相当于继续执行B代码块 - 执行B代码块后, 相当于在main方法中再次调用了一次 main 方法
在第二次 main 中通过类名.静态方法来调用main方法, 并且传参null. 这相当于强制令数组等于null,
因此在执行第二次main方法中,
new Test().print(A || B);
A代码块为true, 因为 || 代表短路或, 遇到true会直接返回, 所以会直接执行print方法的if语句 - 执行玩第二次main方法后, 第一次 main 方法也执行完毕, 由下图图可知 B 永远为 false
因此在第一次main方法的核心代码 new Test().print(A || B)中
false || false 结果为 false
, 所以才会执行else 语句!
3. 总结
第一次运行
main 方法
时, 核心代码new Test().print(A || B);
因为 A 代码块为false
因此执行 B 代码块 但在B代码块中又调用了一次main方法
并且为main方法
设置入参, 导致没有执行完第一次main 方法
的 B代码块前又执行第二次main 方法
, 它的核心代码也是new Test().print(A || B);
, 并且 A为true
, 因为短路或遇到true
会直接返回true
, 因此首先执行print 方法的 if 语句
然后将第一次main 方法
的 B 代码块执行完毕(false
), 同时因为 A =false
, A || B 为false
, 因此执行print 方法的 else 语句
三、体会
- 通过对一个问题的探究巩固了很多知识点 比如: 短路或的使用, == 的含义, 函数以及形参在内存中的模型等等
- 对于感到兴趣或者疑问的问题, 不妨多动手试试, 或许有意想不到的结果
- 夏天到了, 一曲 冰柜 送上