我们先来看一段代码:
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
分享到:
相关推荐
在Java中,API主要由Sun Microsystems(现为Oracle公司)维护,是Java平台的核心组成部分。它包括了Java标准库中的各种类库,如集合框架、输入/输出流、网络编程、多线程、图形用户界面(GUI)等。 Java API文档...
除此之外,书中的案例涵盖了集合框架,包括ArrayList、LinkedList、HashMap等,这些都是Java开发中不可或缺的数据结构。此外,还会介绍泛型、枚举和注解,这些都是现代Java编程中的重要元素。 最后,书籍还讲解了...
3. **依赖(Dependency)**: 介绍了如何在Java项目中添加ElasticSearch Java API依赖,特别是推荐使用与ElasticSearch版本号一致的transport版本号。这是使用ElasticSearch Java API前的必要配置。 4. **Java客户端...
在这个主题中,我们将深入探讨如何使用Java和JSP进行语言切换,以满足用户对中英文显示的需求。 1. **Java 国际化基础** Java 提供了 `java.util.Locale` 类来表示不同的语言环境,如英文(`en`)和中文(`zh`)。...
首先,Java中发送HTTP POST请求通常会用到`HttpURLConnection`类或者第三方库如Apache HttpClient或OkHttp。下面我们将主要使用`HttpURLConnection`来演示,因为它内置在JDK中,无需额外引入依赖。 1. **创建HTTP...
当你仔细阅读书籍时,会发现Java中有大量的数学知识,包括:扰动函数、负载因子、拉链寻址、开放寻址、斐波那契(Fibonacci)散列法还有黄金分割点的使用等等。 适合人群 1. 具备一定编程基础,工作1-3年的研发...
Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码...
Java API(应用程序接口)是Java编程语言的核心组成部分,它提供了大量的类库,使得开发者能够构建出功能丰富的应用程序。这份“JAVA API官方文档 中文版”是对于Java开发者的宝贵资源,帮助他们理解和使用Java平台...
2. **Nashorn JavaScript引擎**: 提供了在Java中执行JavaScript代码的能力,便于混合编程。 3. **并行数组操作**: `java.util.parallel`包提供了并行版本的数组操作,如`Arrays.parallelSort()`。 4. **集合工厂...
首先,我们需要了解Java中用于处理图像的主要类库:Java Advanced Imaging (JAI) 和 Java 2D API。虽然JAI提供了更强大的图像处理功能,但Java 2D API通常更易于理解和使用,尤其对于简单的图像操作如旋转、翻转和...
由于Java和Pascal的语法差异,转换过程中可能会遇到一些挑战,比如Java中的匿名内部类在Pascal中可能需要不同的表示方式,或者Java的泛型在Pascal中可能没有直接对应的概念。 Java2Pas.exe很可能是这个工具的可执行...
在Java中,我们通常会利用TTS(Text To Speech)库来实现这一功能。PCM(Pulse Code Modulation)是数字音频的基本表示形式,它直接对模拟信号进行采样、量化和编码。MP3则是广泛使用的音频压缩格式,具有较高的压缩...
* C++ 中对类的定义与 Java 有些不同,其中的 public 和 private 部分是分开的,而 Java 中,每一个元素都必须标明 public 或 private。 * C++ 中类的定义只包含函数的声明,真正的实现另外单独列出。 * 访问函数...
根据给定文件的信息,本文将深入探讨如何在Java中实现32位无符号整数的二进制位颠倒,并进一步讨论如何通过查表法优化该算法。 ### 一、基本实现方法 #### 问题背景 给定一个32位无符号整数,任务是颠倒它的二进制...
在Java编程环境中,读取PDF文件中的内容是一个常见的任务,特别是在处理文档自动化或者数据分析时。PDF(Portable Document Format)是一种跨平台的文件格式,用于精确地保留文档的格式和内容。下面将详细介绍如何...
答:JDBC(Java Database Connectivity)是Java中的数据库连接技术,用于连接数据库。 10. 在Java中,什么是Statement? 答:Statement是JDBC中的对象,用于执行SQL语句。 11. 在Java中,什么是PreparedStatement...
Java到JavaScript转换工具有助于开发者将已有的Java代码库移植到JavaScript环境中,这在Web开发中尤其有用,因为JavaScript是浏览器端的主要脚本语言。这样的工具能够帮助开发者利用Java的强大功能来构建前端应用,...
Java中的Kerberos支持主要体现在Java的`javax.security.auth.kerberos`包中,提供了与Kerberos协议交互的一系列类和接口。这些类允许Java应用进行认证过程,包括获取票据授予票据(Ticket-Granting Ticket, TGT)...
在Java中使用OpenCV,我们需要先安装OpenCV库,并将其与Java项目集成。这通常包括下载OpenCV库,配置Java的系统路径,以及在项目中引入相应的依赖。OpenCV提供了丰富的API,能够帮助我们进行图像的读取、显示、变换...