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

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教程(第4版)答案

    在Java中,注释有两种形式:单行注释(`//`)和多行注释(`/* */`)。`c`被认为是一个非法注释,因为它既不是以`//`开头的单行注释也不是以`/*`开头的多行注释。 **1.3 类路径问题** 当Java解释器无法找到指定的类时,...

    Java基础习题.pdf

    在Java中,函数常常与方法的概念相结合,即类中的函数称为方法。 - **数组与列表**:数组是一种数据结构,可以存储固定大小的顺序集合。在Java中,数组的长度在创建时确定,之后不可改变。 - **循环递推与递归**:...

    java认证题库

    题目中的正确答案是:“与全1的二进制数进行按位异或之后每一个数字都会颠倒。” ### 8. 控制语句 **知识点**:中断控制语句的工作原理。 **详细解释**:题目中提到了`break`、`continue`、`return`等控制语句。...

    java面试题技术类和人事面试问比较难回答的问题

    2. **String 不是最基本的数据类型**:Java 中的基本数据类型包括 byte、short、int、long、float、double、char 和 boolean,而 String 是一个对象,属于引用类型。 3. **String 对象创建**:`String s = new ...

    java 面试题 答案

    - **基本数据类型:** Java中的`int`、`char`、`byte`等基本数据类型及其范围。 - **类型转换:** 字符可以被自动转换为整数,整数也可以被显式或隐式地转换为字符。 ### 3. 方法覆盖与返回类型 此题考察了Java中...

    Java实现单向链表反转

    Java实现单向链表反转是指将单向链表的顺序颠倒,例如原链表为A-&gt;B-&gt;C-&gt;D-&gt;E-&gt;F,反转后变为F-&gt;E-&gt;D-&gt;C-&gt;B-&gt;A。这种操作在实际开发中非常有用,例如在数据处理、数据分析等领域。 单向链表反转的实现方式有两种,一...

    SCJP(310-065)考试参考题集1

    线程的执行顺序是非确定性的,因此正确答案是D("run."先打印,然后是"java.lang.RuntimeException: Problem")和E(两个输出可以按任何顺序出现,但都必须包含)。这表明考生需要理解线程并发执行的特点,包括异常...

    2021-2022计算机二级等级考试试题及答案No.3874.docx

    前后顺序可以任意颠倒, 不影响库中的数据关系**。在关系数据库中,记录的具体物理顺序对数据的关系没有影响。只要每个记录的字段值正确,记录的顺序是可以随意调整的。 #### 7. 需求分析阶段的主要文档 - **知识...

    《javaScrip开发技术大全》源代码

    • sample04.htm 在同一个网页中使用不同脚本语言 • sample05.htm 判断浏览器对JavaScript版本的支持情况 • sample06.htm 引用外部JavaScript • sample07.htm 在同一个HTML文档...

Global site tag (gtag.js) - Google Analytics