文档章节

关于2+2=5的问题

于忠达
 于忠达
发布于 2014/06/06 10:32
字数 1381
阅读 117
收藏 0
点赞 0
评论 0

在“如何编程实现2+2=5”一文中讨论了一个可行的方案,窃以为众人的焦点都被转移了。原文见http://www.oschina.net/news/52412/write-a-program-that-makes-2-2-5
1. 文中的纰漏问题

Integer a = new Integer(2);
Integer b = new Integer(2);
System.out.print(a == b);

这个在java中输出必然是false,因为两个地址是不一样的,两边类型对等都是对象时,==比较的是地址,而不是值。如果有一侧是原生类型,则尝试把另一边转换成为原生类型来进行比较。把基本类型封装拆成原始类型值的隐式转换,这就从JDK1.5开始引入的自动拆包(拆箱)。至于有评论说是true,应该是没有亲自去试试,或者不熟悉源码。new是产生一个新的对象,不是工厂模式,去取缓存中一个已经存在的。

2. 巧妙的障眼法

不觉得文中使用的打印方法有点不正常么?一般人打印的时候我想都是这样的:

System.out.println("2+2="+(2+2));

而文中给出的打印方式为:

System.out.printf("%d",2 + 2);

这是一个典型的障眼法,也是配合他的思想的。

printf这个方法其实在C中是常用的,在Java中的声明是这样的:

    public PrintStream printf(String format, Object ... args)

这里给我们看到,参数是一个变长的数组类型,元素的类型是Object

2+2得到的是一个原生的数字类型4,并且这个结果不会变的。但原生类型4怎样转换成为Object呢?由于4是原始类型,对应的包装类型是Integer,因此首先自动装箱成为Integer,而这个过程是调用Integer.valueOf()方法来完成。谁来调用的呢?编译器。因此Java语法中许多的操作都是编译器来做繁琐的处理,而不是语法真的能实现这个功能。比如字符串和数字的相加,并非+有这样的能力,而是编译器来完成语法的解析和执行。+就只做数字的运算。

所以调用printf的过程中,原生的数字4被调用Integer.valueOf(4)来转换成为Object,这正中下怀,从Integer.valueOf的实现来看,通过修改缓存的数字序列,的确达到了移花接木的效果。如果不是使用printf,而使用print或者println,你都得不到这个结果。

3. 反射操作的技巧

在这个方法中,真正的技巧和难度其实不在于修改cache序列中的引用,当然这是一个绝佳的点子,并且很好的应用了自动装箱这个方式完成了从里面的取值,但真正应该学习的技巧却是关于反射的使用,尤其是这几句:

        Class cache = Integer.class.getDeclaredClasses()[0];
        Field c = cache.getDeclaredField("cache");
        c.setAccessible(true);
        Integer[] array = (Integer[]) c.get(cache);

我估计学过Java的人可能大多只是知道这几句是什么意思,但也只是看着代码去解释而已,如果让你自己写,八成是写不出来的。

第一句是获取Java类中所定义的内部类,并且取了其中的第一个。这个只有读过源码才知道,Integer里面也只定义了一个静态内部类,就是用来做存储的,存储的成员变量名字叫cache,也是静态的。因此这里写死了取第一个([0])。

第二句是从这个类中取定义的成员变量(field)。由于这个成员变量是外部不可访问的(包级访问权限),因此第三句c.setAccessible(true)用来通知JVM当这个方法或成员变量被调用的时候,不要进行可访问性的检查(参见AccessableObject.java的setAccessible的JavaDoc)。然后就可以顺利的从cache这个类中取出其成员成员变量cache,其类型是Integer[],然后后面的处理就没有悬念了。


总结:

文章给出的2+2=5其实并不是真的计算结果是5,而是把计算结果打印成为5,所采用的方案是利用自动装箱时把4装成5。由5在缓存的区间里面,因此修改装箱会引用的那个值,加上自动装箱功能,就可以打印成为5了。由于表演要像一点,因此打印的时候需要选择装箱后打印,这里没有自己写一个Object类型的参数打印方法(Integer类型的,Number类型的都行,只要先装箱就好办了),而是选择了Java中不常用的printf方法。但是这个方法带来的副作用是,4的缓存被指向5了,因此只要是结果为4的自动装箱都将被包装成为5,因此不只是2+2=5了,1+3,6-2都是5了。

不管怎么说,这个还是很有创意的一个想法。而且,由于修改的是Integer类,因此不一定把修改缓存的这部分都写在main方法中,写在静态代码中可能更具有隐蔽性,并且如果静态代码块在引用的别的类中,那么这个隐蔽性就更强了。


© 著作权归作者所有

共有 人打赏支持
于忠达
粉丝 112
博文 13
码字总数 16484
作品 0
青岛
程序员
SQL*Plus中替换变量与定义变量

替换变量 SQL*Plus中的替换变量又叫替代变量,它一般用来临时存储相关数据;在SQL语句之间传递值。一般使用&或&&前缀来指定替换变量. 关于使用替换变量,一般是利用其创建通用的脚本或达到和...

breakawaylove
2014/10/24
26
0
蓝桥杯题目——瓷砖铺放

问题描述   有一长度为N(1<=N<=10)的地板,给定两种不同瓷砖:一种长度为1,另一种长度为2,数目不限。要将这个长度为N的地板铺满,一共有多少种不同的铺法?  例如,长度为4的地面一共...

孤单的狗
2017/03/10
0
0
计算时间复杂度

时间复杂度是总运算次数表达式中受n的变化影响最大的那一项(不含系数) 比如:一般总运算次数表达式类似于这样:a2^n+bn^3+cn^2+dnlg(n)+en+fa ! =0时,时间复杂度就是O(2^n);a=0,b<>0 =>O(n...

JasonWung
2016/07/10
69
0
关于cloudstack的Dashboard的显示存储空间容量的问题

说明:下面是我们在cloudstack的Dashboard显示的磁盘容量信息 如图上我们可以发现,主存储的空间为12T,已使用空间为1.70T,但实际存储空间是否是12T,实际使用空间是否也为1.58T了? 这个时...

冰冻vs西瓜
2017/12/22
0
0
python 是什么~

  Python的非正式介绍   在后面的例子中,区分输入和输出的方法是看是否有提示符(“>>> ”和“.. ”):想要重复这些例子的话,你 就要在提示符显示后输入所有的一切;没有以提示符开始的行...

python爱好者
2013/03/11
479
1
Bellman-Ford和松弛技术

适用条件 有向图 但是没有负环 比之迪杰斯特拉 在于他可以计算负的权值边 负环是什么 环 负环就是至少有一条边是负数 松弛技术 这里是发现了2+2=4<5,所以B点更新为4 所以不能是负环,因为这...

qq_36523667
01/18
0
0
51Nod 1305 Pairwise Sum and Divide

有这样一段程序,fun会对整数数组A进行求值,其中Floor表示向下取整: fun(A) sum = 0 for i = 1 to A.length for j = i+1 to A.length sum = sum + Floor((A[i]+A[j])/(A[i]*A[j])) return......

Akatsuki__Itachi
2017/12/19
0
0
基于Markovitz统计套利策略

摘要 Markovitz是基于风险组合的套利策略,其核心是对资产组合进行最优化组合配置。 1Markovitz的原理 马柯维茨(Markovitz)的均值方差投资组合理论第一次用均值、方差等数学理论和工具探讨了...

lwglucky
2017/10/14
0
0
reactjs开发自制编程语言编译器:实现变量绑定和函数调用

在编程时,我们会初始化一个变量,给变量赋初值,例如下面语句: 上面代码被编译器解读后,变量x就会和数值25绑定在一起。下次使用到变量x时,编译器会读取它绑定的值,然后用于相关代码的执...

望月从良
05/18
0
0
openjweb快速开发平台中使用Groovy动态语言作为规则引擎解决方案

由于在工作流设计中需要为条件分支设定条件表达式,所以需要可以动态执行的语言脚本作为工作流的规则引擎.工作流条件分支纯粹使用sql脚本肯定是不行的,经过试验,groovy的脚本可以返回为java接...

迷途d书童
2012/03/09
521
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

实现异步有哪些方法

有哪些方法可以实现异步呢? 方式一:java 线程池 示例: @Test public final void test_ThreadPool() throws InterruptedException { ScheduledThreadPoolExecutor scheduledThre......

黄威
今天
0
0
linux服务器修改mtu值优化cpu

一、jumbo frames 相关 1、什么是jumbo frames Jumbo frames 是指比标准Ethernet Frames长的frame,即比1518/1522 bit大的frames,Jumbo frame的大小是每个设备厂商规定的,不属于IEEE标准;...

六库科技
今天
0
0
牛客网刷题

1. 二维数组中的查找(难度:易) 题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入...

大不了敲一辈子代码
今天
0
0
linux系统的任务计划、服务管理

linux任务计划cron 在linux下,有时候要在我们不在的时候执行一项命令,或启动一个脚本,可以使用任务计划cron功能。 任务计划要用crontab命令完成 选项: -u 指定某个用户,不加-u表示当前用...

黄昏残影
昨天
0
0
设计模式:单例模式

单例模式的定义是确保某个类在任何情况下都只有一个实例,并且需要提供一个全局的访问点供调用者访问该实例的一种模式。 实现以上模式基于以下必须遵守的两点: 1.构造方法私有化 2.提供一个...

人觉非常君
昨天
0
0
《Linux Perf Master》Edition 0.4 发布

在线阅读:https://riboseyim.gitbook.io/perf 在线阅读:https://www.gitbook.com/book/riboseyim/linux-perf-master/details 百度网盘【pdf、mobi、ePub】:https://pan.baidu.com/s/1C20T......

RiboseYim
昨天
1
0
conda 换源

https://mirrors.tuna.tsinghua.edu.cn/help/anaconda/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/conda config --add channels https://mir......

阿豪boy
昨天
1
0
Confluence 6 安装补丁类文件

Atlassian 支持或者 Atlassian 缺陷修复小组可能针对有一些关键问题会提供补丁来解决这些问题,但是这些问题还没有放到下一个更新版本中。这些问题将会使用 Class 类文件同时在官方 Jira bug...

honeymose
昨天
0
0
非常实用的IDEA插件之总结

1、Alibaba Java Coding Guidelines 经过247天的持续研发,阿里巴巴于10月14日在杭州云栖大会上,正式发布众所期待的《阿里巴巴Java开发规约》扫描插件!该插件由阿里巴巴P3C项目组研发。P3C...

Gibbons
昨天
1
0
Tomcat介绍,安装jdk,安装tomcat,配置Tomcat监听80端口

Tomcat介绍 Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta项目中的一个核心项目,由Apache、Sun和其他一些公司及个人共同开发而成。 java程序写的网站用tomcat+jdk来运行...

TaoXu
昨天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部