jdk之java.lang.Integer源码理解
基本数据类型的包装类java.lang.Integer是我们频繁使用的一个系统类,那么通过一个示例反应出的几个问题来深入理解一下此类的源码。
需求:实现Integer类型的两个数值交换。
package cn.integer;
public class Demo {
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
System.out.println("bofore swap a:"+a+",b:"+b);
//交换a和b的值
swap(a,b);
System.out.println("after swap a:"+a+",b:"+b);
}
/**
* 数据交换
* @param a
* @param b
*/
private static void swap(Integer a, Integer b) {
/**
* 如何实现呢?尝试几种方法
*/
/*01.方式一(无法实现)
a = a^b;
b = a^b;
a = a^b;*/
/*02.方式二(无法实现)
int tmp = a;
a = b;
b = tmp;*/
/**
* 以上两种方式是因为java的值传递特性故无法实现数据交换。
*/
}
}
Java值传递的示意图如下:

当调用swap(..)方法时,在堆中会创建这两个值得副本,形参num1和num2指向副本的数据。原ab指向的数据不会改变。
那么如何通过修改swap()方法实现呢?
观察java.lang.Integer源码:
public final class Integer extends Number implements Comparable<Integer> {
...
}
可以发现 Integer和String一样,是final修饰的,这是因为jdk将这些系统类封装起来不希望被破坏。
继续看Integer类:
/**
* The value of the {@code Integer}.
*
* @serial
*/
private final int value; /**
* Constructs a newly allocated {@code Integer} object that
* represents the specified {@code int} value.
*
* @param value the value to be represented by the
* {@code Integer} object.
*/
public Integer(int value) {
this.value = value;
}
这里的value也是使用final修饰的,那么如果想修改它的值,可以使用反射的方式获取value字段并进行修改。
修改swap()方法中代码:
public class Demo {
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
System.out.println("bofore swap a:"+a+",b:"+b);
//交换a和b的值
swap(a,b);
System.out.println("after swap a:"+a+",b:"+b);
}
/**
* 数据交换
* @param a
* @param b
*/
private static void swap(Integer a, Integer b) {
try {
//获取Integer类中私有字段value
Field field = Integer.class.getDeclaredField("value");
/**
* 开启访问权限
* public final class Field extends AccessibleObject implements Member {}
* 跟进AccessibleObject
* public void setAccessible(boolean flag) throws SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
setAccessible0(this, flag);
}
private static void setAccessible0(AccessibleObject obj, boolean flag)
throws SecurityException
{
if (obj instanceof Constructor && flag == true) {
Constructor<?> c = (Constructor<?>)obj;
if (c.getDeclaringClass() == Class.class) {
throw new SecurityException("Cannot make a java.lang.Class" +
" constructor accessible");
}
}
obj.override = flag;
}
*/
field.setAccessible(true);
//临时变量
int tmp = a.intValue();
//数据交换
field.set(a, b);
field.set(b, tmp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果:

诶,我去。怎么只改变了一个值?咋成功了一半呢?
01.首先,Integer a = 1; 这里会自动装箱,将 int类型的1 转换成 Integer类型。
通过valueOf方法进行装箱
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
这个方法非常重要,如果传入的参数在 -128-127之间,会返回一个缓存中的数据。否则就 new出一个Integer对象!
Integer.IntegerCache是Integer中的内部类:
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
02.当我们获取数值1时,
return IntegerCache.cache[i + (-IntegerCache.low)]
计算出IntegerCache.cache[129]的值(也就是1)返回。 cache这个数组存放着 -128-127这些数据。故index=129就是返回1。 03. 执行这行时 field.set(a, b);
将传入的2复制给a,此时a为2.
注意:这里通过set方法改变的的是缓存cache数组中的数据! 04. 当执行到 field.set(b, tmp);
tmp为1,而set方法的参数类型是Object,可以传入int类型的tmp
public void set(Object obj, Object value)
throws IllegalArgumentException, IllegalAccessException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
getFieldAccessor(obj).set(obj, value);
}
所以这个tmp=1是从Integer的缓存数组中取得,因上一行操作已经将cache中index为129位置得原数值1改变为了2,故在cache中获取的是2! 那么把2赋值给了b自然是2!(b对应的下标[130],a对应的下标[129])
我们发现,问题的关键在于这个缓存cache!
如果说可以避开走缓存这一步,我们就能实现数据交换。除了传入 -128-127之外的数据,我们还可以:
001.将tmp转换成Integer对象后在传入Field的set方法:
private static void swap(Integer a, Integer b) {
try {
//获取Integer类中私有字段value
Field field = Integer.class.getDeclaredField("value");
field.setAccessible(true);
//临时变量
int tmp = a.intValue();
//数据交换
field.set(a, b);
field.set(b, new Integer(tmp));
} catch (Exception e) {
e.printStackTrace();
}
}
运行结果:

002. 使用 Field类的 setInt方法
public void setInt(Object obj, int i)
throws IllegalArgumentException, IllegalAccessException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
getFieldAccessor(obj).setInt(obj, i);
}
修改代码 field.setInt(b,tmp); 也可以实现。
003. 非要实现打印效果的话,使用非常规手短,直接在swap中打印结果...
private static void swap(Integer a, Integer b) {
System.out.println("after swap a:2,b:1");
System.exit(0);
}
这个示例中涉及到技能点:

jdk之java.lang.Integer源码理解的更多相关文章
- 【JDK】:java.lang.Integer源码解析
本文对JDK8中的java.lang.Integer包装类的部分数值缓存技术.valueOf().stringSize().toString().getChars().parseInt()等进行简要分 ...
- java.lang.Integer源码浅析
Integer定义,final不可修改的类 public final class Integer extends Number implements Comparable<Integer> ...
- 如何查看JDK以及JAVA框架的源码
如何查看JDK以及JAVA框架的源码 设置步骤如下: 1.点 “window”-> "Preferences" -> "Java" -> &q ...
- 结合java.util.TreeMap源码理解红黑树
前言 本篇将结合JDK1.6的TreeMap源码,来一起探索红-黑树的奥秘.红黑树是解决二叉搜索树的非平衡问题. 当插入(或者删除)一个新节点时,为了使树保持平衡,必须遵循一定的规则,这个规则就是红- ...
- java.lang.StringBuffer源码分析
StringBuffer是一个线程安全的可变序列的字符数组对象,它与StringBuilder一样,继承父类AbstractStringBuilder.在多线程环境中,当方法操作是必须被同步,Stri ...
- java中Object源码理解
java阅读笔记 1.object getClass() 返回是的此object运行时的类,返回的对象是被object锁定的对象,调用这个方法不需要进行强转 public static void ma ...
- 如何在Eclipse中查看JDK以及JAVA框架的源码(转载)
原文链接:http://www.cnblogs.com/outlooking/p/5243415.html 设置步骤如下: 1.点 “window”-> "Preferences&qu ...
- 如何在Eclipse中查看JDK以及Java框架的源码
方法一:快速简单 第一步: 打开你的Eclipse,然后随便找一个Java文件,随便找一个Java类库,比如String什么的,然后按住Ctrl,再点击它,你会发现跳到如下界面: 你会发现报错了:So ...
- Java之Integer源码
1.为什么Java中1000==1000为false而100==100为true? 这是一个挺有意思的讨论话题. 如果你运行下面的代码 Integer a = 1000, b = 1000; Syst ...
随机推荐
- .NET/C# 万能 HTTP 模拟请求框架
我是一名 ASP.NET 程序员,专注于 B/S 项目开发.累计文章阅读量超过一千万,我的博客主页地址:https://www.itsvse.com/blog_xzz.html HttpHelper ...
- Spring-Cloud微服务踩坑
@feignclient和@requestmapping混用的时候出错 重写springmvc扫描controller时不带有@feignclient才实例化 @Configuration @Cond ...
- P1462 通往奥格瑞玛的道路【二分+Dij】
P1462 通往奥格瑞玛的道路 提交 29.89k 通过 6.88k 时间限制 1.00s 内存限制 125.00MB 题目提供者gconeice 难度提高+/省选- 历史分数100 提交记录 查看题 ...
- pcl库卸载再重装
系统版本:ubuntu 16.04 sudo rm -r /usr/include/pcl-1.7 /usr/share/pcl /usr/bin/pcl* /usr/lib/libpcl* sudo ...
- virtual box虚拟机跨物理机迁移
VirtualBox保存虚机数据的是vdi文件,但是直接拷贝在新的VirtualBox中不能打开,因为每个vdi文件都包含了一个uuid的数据,如果直接拷贝,uuid在新的VirtualBox中是不能 ...
- 二叉堆(3)SkewHeap
斜堆. 测试文件 main.cpp: #include <iostream> #include "SkewHeap.h" using std::cout; using ...
- UTF自动化测试工具
自UFT推出后,QTP慢慢退出历史舞台 UFT测试的基本流程:录制测试脚本—-编辑测试脚本—-调试测试脚本—-运行测试脚本—-分析测试结果 UFT(QTP)介绍 http://blog.csdn. ...
- Pytest学习7-参数化
在测试过程中,参数化是必不可少的功能,本文就讨论下pytest的几种参数化方法 @pytest.mark.parametrize:参数化测试函数 1.内置的pytest.mark.parametriz ...
- Winform form窗体已弹出框的形式出现并回传值
From2(弹出框)回传数据到From1 Form1(数据接收form): public string Sstr; private void button1_Click(object sender, ...
- Linux安装Tomcat,解决不能访问Manager App
在Windows环境中安装Tomcat时,只需要在Tomcat目录下/conf/tomcat-user.xml文件增加用户就可以了.在Linux系统中添加了username还是不能访问. 一.Li ...