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 ...
随机推荐
- java遍历统计一个文件夹中所有文件单词出现次数
思路就是通过递归进行文件遍历,把所有文件夹中的非目录文件都遍历一遍,并判断其中读取到的字符串是不是单词,并对单词进行记录和计数,并在最后进行排序输出,下面是代码: package word; impo ...
- vim和emacs
vim和emacs 在编程界一直有两大神器的传说.这两大神器一个是emacs,一个是vim.一个是神的编辑器,一个是编辑器之神. 程序员的圈子里面也一直流传着一个段子,说是世界上的程序员分为三种.使用 ...
- Android 开发调试最优选项
1 开发环境 VS2019 16.4.5 2 开发调试选项 Android 选项
- 一道有趣的for循环题
一道有趣的for循环题 今天在复习js基础知识时发现了一个for循环的题,第一眼看到直接懵逼了,没想到for循环竟然还可以这样玩?涨姿势了. 题目是这样的 for(i=0, j=0; i<10, ...
- 最新NetSarang Xmanager安装激活-XShell、XFtp
NetSarang Xmanager Enterprise 是一个简单易用的高性能的运行在 Windows 平台上的 X Server 软件.它能把远端 Unix/Linux 的桌面无缝地带到你的Wi ...
- Linux运维--14.Kolla部署OpenStack使用external MariaDB Galera Cluster
使用haproxy+keepalived实现Mariadb负载均衡 controller2: 10.100.2.52 haproxy+keepalived controller3: 10.100.2. ...
- 机器学习笔记(十一)OCR技术的应用
1.介绍OCR: OCR(Photo optical character recognition 照片光学字符识别) 应用于读取电子照片中的文字. 2.算法思路: ① 识别文字区域: ② 字符切分: ...
- [CF1304D] Shortest and Longest LIS - 贪心
看样例,>><>><,要构造 LIS 最短的,我们需要找最小链划分的方案,即包含最少的下降列 很容易想到把连续 < 的看成一段,比如样例就是 .|.|. .| ...
- Spring框架详解介绍-基本使用方法
1.Spring框架-控制反转(IOC) 2.Spring框架-面向切面编程(AOP) 3.Spring 内置的JdbcTemplate(Spring-JDBC) Spring框架-控制反转(IOC) ...
- react 渲染
目录 React渲染 createElement的三个参数 element如何生成真实节点 ReactDOMComponent 作用 ReactCompositeComponentWrapper 作用 ...