• jdk之java.lang.Integer源码理解


    基本数据类型的包装类java.lang.Integer是我们频繁使用的一个系统类,那么通过一个示例反应出的几个问题来深入理解一下此类的源码。

    需求:实现Integer类型的两个数值交换。

     1 package cn.integer;
     2 
     3 public class Demo {
     4     public static void main(String[] args) {
     5         Integer a = 1;
     6         Integer b = 2;
     7         System.out.println("bofore swap a:"+a+",b:"+b);
     8         //交换a和b的值
     9         swap(a,b);
    10         System.out.println("after swap a:"+a+",b:"+b);
    11     }
    12     /**
    13      * 数据交换
    14      * @param a
    15      * @param b
    16      */
    17     private static void swap(Integer a, Integer b) {
    18         /**
    19          * 如何实现呢?尝试几种方法
    20          */
    21         /*01.方式一(无法实现)
    22         a = a^b;
    23         b = a^b;
    24         a = a^b;*/
    25         
    26         /*02.方式二(无法实现)
    27         int tmp = a;
    28         a = b;
    29         b = tmp;*/
    30         
    31         /**
    32          * 以上两种方式是因为java的值传递特性故无法实现数据交换。
    33          */
    34     }
    35 }

    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()方法中代码:

     1 public class Demo {
     2     public static void main(String[] args) {
     3         Integer a = 1;
     4         Integer b = 2;
     5         System.out.println("bofore swap a:"+a+",b:"+b);
     6         //交换a和b的值
     7         swap(a,b);
     8         System.out.println("after swap a:"+a+",b:"+b);
     9     }
    10     /**
    11      * 数据交换
    12      * @param a
    13      * @param b
    14      */
    15     private static void swap(Integer a, Integer b) {
    16         try {
    17             //获取Integer类中私有字段value
    18             Field field = Integer.class.getDeclaredField("value");
    19             /**
    20              * 开启访问权限
    21              * public final class Field extends AccessibleObject implements Member {}
    22              * 跟进AccessibleObject
    23              * public void setAccessible(boolean flag) throws SecurityException {
    24                     SecurityManager sm = System.getSecurityManager();
    25                     if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
    26                     setAccessible0(this, flag);
    27                 }
    28                 
    29                 private static void setAccessible0(AccessibleObject obj, boolean flag)
    30                     throws SecurityException
    31                     {
    32                         if (obj instanceof Constructor && flag == true) {
    33                         Constructor<?> c = (Constructor<?>)obj;
    34                         if (c.getDeclaringClass() == Class.class) {
    35                             throw new SecurityException("Cannot make a java.lang.Class" +
    36                                                 " constructor accessible");
    37                         }
    38                     }
    39                     obj.override = flag;
    40                 }
    41              */
    42             field.setAccessible(true);
    43             //临时变量
    44             int tmp = a.intValue();
    45             //数据交换
    46             field.set(a, b);
    47             field.set(b, tmp);
    48         } catch (Exception e) {
    49             e.printStackTrace();
    50         }
    51     }
    52 }

    运行结果:

    诶,我去。怎么只改变了一个值?咋成功了一半呢?

    01.首先,Integer a = 1; 这里会自动装箱,将 int类型的1 转换成 Integer类型。

    通过valueOf方法进行装箱

    1 public static Integer valueOf(int i) {
    2         if (i >= IntegerCache.low && i <= IntegerCache.high)
    3             return IntegerCache.cache[i + (-IntegerCache.low)];
    4         return new Integer(i);
    5     }

    这个方法非常重要,如果传入的参数在 -128-127之间,会返回一个缓存中的数据。否则就 new出一个Integer对象!

    Integer.IntegerCache是Integer中的内部类:

     1 private static class IntegerCache {
     2         static final int low = -128;
     3         static final int high;
     4         static final Integer cache[];
     5 
     6         static {
     7             // high value may be configured by property
     8             int h = 127;
     9             String integerCacheHighPropValue =
    10                 sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
    11             if (integerCacheHighPropValue != null) {
    12                 try {
    13                     int i = parseInt(integerCacheHighPropValue);
    14                     i = Math.max(i, 127);
    15                     // Maximum array size is Integer.MAX_VALUE
    16                     h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
    17                 } catch( NumberFormatException nfe) {
    18                     // If the property cannot be parsed into an int, ignore it.
    19                 }
    20             }
    21             high = h;
    22 
    23             cache = new Integer[(high - low) + 1];
    24             int j = low;
    25             for(int k = 0; k < cache.length; k++)
    26                 cache[k] = new Integer(j++);
    27 
    28             // range [-128, 127] must be interned (JLS7 5.1.7)
    29             assert IntegerCache.high >= 127;
    30         }
    31 
    32         private IntegerCache() {}
    33     }

    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);
        }

    这个示例中涉及到技能点:

     
  • 相关阅读:
    NHibernate源码分析
    jQuery插件:跨浏览器复制jQuery-zclip
    NHibernate入门实例
    NHibernate系列之一--刘东
    关于登陆窗口的关闭、传值
    java 内部类 多线程
    插入时排序
    按某些字段排序数据
    获取顺序排列
    Oracle数组类型
  • 原文地址:https://www.cnblogs.com/9513-/p/8486349.html
Copyright © 2020-2023  润新知