揭秘Integer实现原理及缓存机制

571 篇文章 6 订阅
571 篇文章 3 订阅

Java涉及知识点

  1. 装箱和拆箱
  2. 反射及设置对象访问权限原理
  3. 函数参数的值传递和引用传递
  4. Integer原理及缓存机制
  5. 实现一个函数来交换参数的两个值

1. 装包和拆包

java提供的基本类型以及其对于的包装类型

以Integer为例子介绍装箱和拆箱:”

装箱存在的情况: int 赋值给Integer 即基本类型赋值给包装类型时

Integer i = 1; 等价于 Integer i = Integer.valueOf(1); java自动完成装箱操作

拆箱存在的情况: int与Integer比较 及基本类型和包装类型比较时

Integer i = new Integer(1);

int j = 1;

i == j //等价于 i.intValue() == j i对象会自动拆箱int类型和j进行比较

2. 反射及设置对象访问权限原理

JAVA反射机制是运行过程中,任意实体都可可以知道这个实体类的对象的所有方法和属性信息 对于任何对象都可以调用它的任意方法和属性,这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

这里我们反射的目标是修改某个类的private属性值。以Integer为例:

//获取Integer类对象的方式
//Class clazz = Class.forName("java.lang.Integer");

Class clazz = Integer.class;

//获取Integer类中私有属性value的域 getDeclaredField获取类本身对应的所有访问
//权限的属性 getField获取类以及父类所有的public属性
Field field = clazz.getDeclaredField("value"); 
field.setAccessible(true); //设置private域访问权限
Integer a = 1;
field.set(a, 2);//将a 对象中的value属性设置为2 

对象访问权限原理

反射访问对象的原理主要看Field类的setAccessible和set即可。一个设置域的可见性,一个设置属性值。

Field类声明:public final class Field extends AccessibleObject implements Member
//setAccessible源码 父类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父类中的override属性设置为flag,也就是我们设置的true
 }
//set Feild中的set方法
public void set(Object obj, Object value)
 throws IllegalArgumentException, IllegalAccessException
 {
 if (!override) {//同样是判断field父类中的override,为true则不检查对象访问权限 直接设值
 if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
 Class<?> caller = Reflection.getCallerClass();
 checkAccess(caller, clazz, obj, modifiers);
 }
 }
 getFieldAccessor(obj).set(obj, value);
 }

3. 函数参数的值传递和引用传递

java中的函数参数传递分为值传递引用传递实际上都可以认为是值传递(引用传递实质传递的是引用的值(可以看作C++中指针,存放地址的变量),也同样需要拷贝副本,不过不是对象的副本,而且存放对象地址的变量的副本)。

值传递:针对于基本数据类型

引用传递:针对于对象

int i = 1
void add1 (int i){//值传递 在i参数传入的时候 实际上会拷贝i的一副本,然后在函数中进行操作
 i++;//实际操作的是i的副本,所以函数外部的i变量不受影响
}
System.out.println(i);//输出为1

Integer j = 1;// j = Integer.valueOf(1)
void add2 (Integer j){//引用传递 在j参数传入的时候 实际上会拷贝j对象的引用 然后传入函数中进行操作
 j++; //装箱和拆箱的等价? j=j+1 j = Integer.valueOf(j.intValue()+1) 实际上是副本j引用指向了新的Integer对象,外面的j引用还是指向原来的对象 
}
System.out.println(j);//输出同样为1

4. Integer原理及缓存机制

Integer是int基本数据类型的包装类,无非是在int基本类型的基础上增加了一些操作和其他属性。

Integer的实际对应int值是通过intValue()方法获取的,源码如下:

 private final int value;//对应int基本类型的数值 是一个常量整型
 public int intValue() {
 return value;
 }

前面说过的装箱用到的一个方法是valueOf(),让我们看看源码:

//可以看到传入的i 先和IntegerCache比较 在IntegerCache中则返回IntegerCache中的Integer不存在则new一个Integer对象
 public static Integer valueOf(int i) {
 if (i >= IntegerCache.low && i <= IntegerCache.high)
 return IntegerCache.cache[i + (-IntegerCache.low)];
 return new Integer(i);
 }

IntegerCache实现如下:

//IntegerCache是个Integer的内部类,在类加载的时候创建了256个缓存Integer对象,范围-128至127
 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() {}
 }

知道Integer缓存的存在,下面我们看看下面的几个例子:

注:== 对象比较比较的是对象的引用是否相等 equals则根据对象内部的实现情况进行比较

Integer i = 1; //i = Integer.valueOf(1) 取缓存对象 IntegerCache.cache[129]
Integer j = 1; //j = Integer.valueOf(1) 取缓存对象 IntegerCache.cache[129]
System.out.println(i == j);//输出true i 和j指向同一个对象 
Integer i = 1; //i = Integer.valueOf(1) 取缓存对象 IntegerCache.cache[129]
Integer j = new Integer(1); // 新创建一个对象
System.out.println(i == j);//输出false i 和j指向的不是同一个对象 
Integer i = 128; //i = Integer.valueOf(128) 不在缓存访问内 new Integer(128)
Integer j = 128; //j = Integer.valueOf(128) 不在缓存访问内 new Integer(128)
System.out.println(i == j); //输出false i 和j指向的不是同一个对象 

所以Integer对象在比较是否相等的时候 不要用 == 用equals Integer内部实现了自己用equals,源码如下:

public boolean equals(Object obj) {
 if (obj instanceof Integer) {
 return value == ((Integer)obj).intValue();
 }
 return false;
 }

5. 实现一个函数来交换参数的两个值

前面已经补充了一些Java的相关知识点了,现在我们就来实现一个函数来交换参数的两个值

public class Main {

 static void swap1(Integer a, Integer b){ //第3节已经说明了 这里函数中只是操作的引用副本,是不影响函数外a,b的变化的
 Integer temp = a;
 a = b;
 b = temp;
 }//输出a=1 b=2

 static void swap2(Integer a, Integer b) throws NoSuchFieldException, IllegalAccessException {//通过反射机制来修改引用指向对象的value值即改改变函数外a,b中的value数属性
 Field field = Integer.class.getDeclaredField("value");//value是private final
 field.setAccessible(true);//绕过安全检查
 //temp = 1 temp ->Integer.valueOf(a.intValue()) -> Integer.valueOf(1) ->IntegerCache.cache[129]
 Integer temp = a.intValue();
 //public void set(Object obj, Object value)
 //a.value->Integer.valueOf(b.intValue()).intValue(); 修改a指向对象中value值为2 
 //a指向的是缓存中的IntegerCache.cache[129] 所以IntegerCache.cache[129]
 //中的value被修改成2
 field.set(a, b.intValue()); 

 //b -> IntegerCache.cache[130]
 //b.value->IntegerCache.cache[129].value 所以 IntegerCache.cache[130] IntegerCache.cache[129] value都是2 所以a b中value 都是2
 field.set(b, temp);
 }//输出a=2 b=2
 
 static void swap3(Integer a, Integer b) throws NoSuchFieldException, IllegalAccessException {
 Field field = Integer.class.getDeclaredField("value");
 field.setAccessible(true);//绕过安全检查
 Integer temp = new Integer(a.intValue());//和swap2唯一区区别就在这一行 
 field.set(a, b.intValue()); //a->Integer.valueOf(b.intValue()).intValue();
 field.set(b,temp);//b.value->new Integer(a.intValue()).intValue() 而不是修改成IntegerCache.cache[129].intValue();
 }//输出a=2 b=1 但是存在一个问题就是同样修改了IntegerCache中缓存值 后续存在隐患
 
 public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
 Integer a = 1, b = 2; //装箱操作 等价于 Integer a = Integer.valueOf(1); Integer b = Integer.valueOf(2);
 swap1(a, b);
 System.out.println("a="+a+" b="+b);//输出a=1 b=2
 }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值