`
deepinmind
  • 浏览: 444640 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
1dc14e59-7bdf-33ab-841a-02d087aed982
Java函数式编程
浏览量:40805
社区版块
存档分类
最新评论

Java中如何颠倒"是非"

阅读更多
我们先来看一段代码:

public static void  main(String[] args)  {
         if(Boolean.valueOf("true") == false)
            System.out.println("true == false");
} 


你觉得这段代码的输出是什么?1. 什么也没有。 2. true == false.  3. 这可不好说。

选1的都是好孩子,直接选3的就请不要往下看了。选2的,只能说,你们太配合了。

其实这是源于stackoverflow很久前的一个帖子,前几天和同事正好聊起到。在我的机器上,这段代码是输出true== false的,这是因为我没把代码粘贴全,其实完整的代码是这样的:


import java.lang.reflect.Field;
import java.lang.reflect.Modifier; 

public class Test {

    static {
        try {
            Field f = Boolean.class.getDeclaredField("TRUE");

            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);

            f.setAccessible(true);
            f.set(Boolean.class, Boolean.FALSE);

        }
        catch(Exception ex) {}
    }

    public static void  main(String[] args)  {

        if(Boolean.valueOf("true") == false)
            System.out.println("true == false");

    }
} 





Boolean类内部有这么两个静态成员变量:


    /**
     * The <code>Boolean</code> object corresponding to the primitive
     * value <code>true</code>.
     */
    public static final Boolean TRUE = new Boolean(true);

    /**
     * The <code>Boolean</code> object corresponding to the primitive
     * value <code>false</code>.
     */
    public static final Boolean FALSE = new Boolean(false); 


当调用Boolean.valueOf()或者触发编译器的自动装箱的时候,都会用到这两个变量。上面的代码里我们就是通过反射将其中的那个TRUE修改成了new Boolean(false),因此能会输出"true==false”。注意这个字段是final类型的,所以简单的.setAccessible(true);它可不吃这一套,因此后面会将它的修饰符改成非final的: modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);这里稍微有点饶,这个修饰符是在TRUE对应的这个静态成员变量的Field实例(Boolean.class.getDeclaredField("TRUE"))的一个叫modifier的字段里(Field.class.getDeclaredField("modifiers");)。

当然,如果你看到你的员工写出Boolean.valueOf("true") == false这样的语句的话,你估计第一反应就是把他给炒了。但是有的情况就不那么容易发现了。我们经常会把数据序列化成XML或者JSON的格式,当然我们同样也需要将它们进行反序列化。

假设有这么一段JSON:


{  "deepinmind" : true }

你可以用fastjson之类的对它进行反序列化,


    public static void  main(String[] args)  {

           JSONObject json = (JSONObject)JSON.parse("{ \"deepinmind\" : true }") ;
           System.out.println("deepinmind: "+json.getBoolean("deepinmind")); 

    } 


结果是:

deepinmind: false



因为fastjson会把解析后的值存到一个map里,这样的话就正好触发了自动装箱。在不知不觉中,你的结果已经被修改了。


当然能做的远不止这些,比如下面这段代码:

public class Test {

    static {
        try {
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        
        Class<?> cls = Class.forName("java.lang.Integer$IntegerCache")  ;
        Field f =  cls.getDeclaredField("cache");
        f.setAccessible(true);

        modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);

        Integer[] array = (Integer[])f.get(cls);
        array[128+21] = new Integer(22);

        }
        catch(Exception ex) {}
    }

    public static void  main(String[] args)  {

                System.out.format("3 * 7 = %d", 21);

    }
}




3 * 7 = 22

看吧,再也不用管什么三七二十一了。

这是因为Integer会把-128~127之间的整型缓存起来,自动装箱的时候会优先使用缓存的这些对象。而System.out.format后面是个变长参数,这会触发int类型的自动装箱。前面之所以加了128是因为cache数组的前128位存的是负数:


private static class IntegerCache {
        static final int high;
        static final Integer cache[];

        static {
            final int low = -128;

            // high value may be configured by property
            int h = 127;
            if (integerCacheHighPropValue != null) {
                // Use Long.decode here to avoid invoking methods that
                // require Integer's autoboxing cache to be initialized
                int i = Long.decode(integerCacheHighPropValue).intValue();
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - -low);
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }

        private IntegerCache() {}
    } 



想得到这个输出,你除了可以修改IntegerCache外,还可以自己写一个PrintStream,把System.out给替换掉,这样你想输出什么就能输出什么了。不过这个也得用到反射,因为System.out是final类型的。


当然了,权当娱乐,可千万不要这么写代码。如果它让你感到害怕,你可以看下这个讨论,了解下如何把这个功能给屏蔽掉。

原创文章转载请注明出处:http://it.deepinmind.com

7
2
分享到:
评论
8 楼 Night舞夜 2014-06-16  
deepinmind 写道
Night舞夜 写道
我想说我机器上毛都没输出.Boolean.valueOf("true")输出的是true, 难道这还与机器有关?


你是运行我粘贴的第二段代码吗?

我只运行了这个Boolean.valueOf("true")
7 楼 deepinmind 2014-04-04  
Night舞夜 写道
我想说我机器上毛都没输出.Boolean.valueOf("true")输出的是true, 难道这还与机器有关?


你是运行我粘贴的第二段代码吗?
6 楼 Night舞夜 2014-04-04  
我想说我机器上毛都没输出.Boolean.valueOf("true")输出的是true, 难道这还与机器有关?
5 楼 deepinmind 2014-04-03  
与天争锋 写道
第一段代码 表示完全神马都木有输出。楼上是说。。。多了一个括号


哦还真是 我以为说第二段呢。。眼真尖,哈哈
4 楼 与天争锋 2014-04-03  
第一段代码 表示完全神马都木有输出。楼上是说。。。多了一个括号
3 楼 deepinmind 2014-04-03  
LazyDonkey 写道
true == false的那段代码会编译错误的


?没有啊,我都是运行过的
2 楼 LazyDonkey 2014-04-03  
true == false的那段代码会编译错误的
1 楼 jiiming 2014-04-02  

相关推荐

    JAVA_API1.6文档(中文)

    java.security.interfaces 提供的接口用于生成 RSA Laboratory Technical Note PKCS#1 中定义的 RSA(Rivest、Shamir 和 Adleman AsymmetricCipher 算法)密钥,以及 NIST 的 FIPS-186 中定义的 DSA(数字签名算法)...

    Java 中文入门学习手册合集[chm版]

    第一章 Java语言的产生及其特点 第二章 Java程序开发与运行环境 第三章 Java程序设计基础 第四章 Java应用程序的基本框架 第五章 Java的类 第六章 Java图形用户接口 第七章 多线程 第八章 Java的"异常" 第九...

    java中文排序,数字字母汉字排序

    java实现中文排序,按数字字母汉字的顺序进行排序

    ElasticSearch Java API 中文文档

    ElasticSearch Java API 中文文档 ElasticSearch Java API 中文文档

    java jdk 8 帮助文档 中文 文档 chm 谷歌翻译

    JDK1.8 API 中文谷歌翻译版 java帮助文档 JDK API java 帮助文档 谷歌翻译 JDK1.8 API 中文 谷歌翻译版 java帮助文档 Java最新帮助文档 本帮助文档是使用谷歌翻译,非人工翻译。准确性不能保证,请与英文版配合使用 ...

    Head First Java 中文高清版pdf

    Head First Java 中文高清版Head First Java 中文高清版Head First Java 中文高清版

    java 实现国际化 中英文语言切换

    java实现国际化中英文语言切换 java语言切换JSP国际化 java实现国际化中英文语言切换 java语言切换JSP国际化

    Java 面经手册·小傅哥.pdf

    当你仔细阅读书籍时,会发现Java中有大量的数学知识,包括:扰动函数、负载因子、拉链寻址、开放寻址、斐波那契(Fibonacci)散列法还有黄金分割点的使用等等。 适合人群 1. 具备一定编程基础,工作1-3年的研发...

    java源码包---java 源码 大量 实例

     Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码...

    java中main方法发送httpPost请求

    java语言后台请求网站操作 java中main方法发送httpPost请求

    JAVA API官方中文版手册chm文件文档

    JAVA API官方文档中文版软件包 java.applet java.awt java.awt.color java.awt.datatransfer java.awt.dnd java.awt.event java.awt.font java.awt.geom java.awt.im java.awt.im.spi java.awt.image ...

    JAVA中英文翻译+JAVA文献检索

    JAVA方面的一篇中英文翻译(不是很准确 用的话改改 ^_^) 以及JAVA文献检索摘要

    Java API文档 中文网页版

    Java API文档中文版,欢迎下载!!

    Java Language Specification(Java编程规范)中英文

    Java Language Specification(Java编程规范)中英文合集 这是我从网上分别下的中文版和英文版打包在一起的,希望能为你们节省很多搜索找寻时间,其中那个中文版真的很难找~~

    java源码包2

     Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java...

    java源码包4

     Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java...

    openCV java的API文档

    open CV2.9.4版本的java ApI

    JAVA上百实例源码以及开源项目

     Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码...

    《kettle中调用java类》示例代码

    《kettle中调用java类》示例代码;http://blog.csdn.net/neweastsun/article/details/40022823

Global site tag (gtag.js) - Google Analytics