Java中怎么颠倒"是非"
Java中如何颠倒"是非"
我们先来看一段代码:
你觉得这段代码的输出是什么?1. 什么也没有。 2. true == false. 3. 这可不好说。
选1的都是好孩子,直接选3的就请不要往下看了。选2的,只能说,你们太配合了。
其实这是源于stackoverflow很久前的一个帖子,前几天和同事正好聊起到。在我的机器上,这段代码是输出true== false的,这是因为我没把代码粘贴全,其实完整的代码是这样的:
Boolean类内部有这么两个静态成员变量:
当调用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之类的对它进行反序列化,
结果是:
因为fastjson会把解析后的值存到一个map里,这样的话就正好触发了自动装箱。在不知不觉中,你的结果已经被修改了。
当然能做的远不止这些,比如下面这段代码:
看吧,再也不用管什么三七二十一了。
这是因为Integer会把-128~127之间的整型缓存起来,自动装箱的时候会优先使用缓存的这些对象。而System.out.format后面是个变长参数,这会触发int类型的自动装箱。前面之所以加了128是因为cache数组的前128位存的是负数:
想得到这个输出,你除了可以修改IntegerCache外,还可以自己写一个PrintStream,把System.out给替换掉,这样你想输出什么就能输出什么了。不过这个也得用到反射,因为System.out是final类型的。
当然了,权当娱乐,可千万不要这么写代码。如果它让你感到害怕,你可以看下这个讨论,了解下如何把这个功能给屏蔽掉。
原创文章转载请注明出处:http://it.deepinmind.com
?没有啊,我都是运行过的
哦还真是
我以为说第二段呢。。眼真尖,哈哈
你是运行我粘贴的第二段代码吗?
我们先来看一段代码:
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
1 楼
jiiming
昨天
2 楼
LazyDonkey
23 小时前
true == false的那段代码会编译错误的
3 楼
deepinmind
22 小时前
LazyDonkey 写道
true == false的那段代码会编译错误的
?没有啊,我都是运行过的
4 楼
与天争锋
18 小时前
第一段代码 表示完全神马都木有输出。楼上是说。。。多了一个括号
5 楼
deepinmind
18 小时前
与天争锋 写道
第一段代码 表示完全神马都木有输出。楼上是说。。。多了一个括号
哦还真是
6 楼
Night舞夜
6 小时前
我想说我机器上毛都没输出.Boolean.valueOf("true")输出的是true, 难道这还与机器有关?
7 楼
deepinmind
5 小时前
Night舞夜 写道
我想说我机器上毛都没输出.Boolean.valueOf("true")输出的是true, 难道这还与机器有关?
你是运行我粘贴的第二段代码吗?