概述
Java涉及知识点
- 装箱和拆箱
- 反射及设置对象访问权限原理
- 函数参数的值传递和引用传递
- Integer原理及缓存机制
- 实现一个函数来交换参数的两个值
1. 装包和拆包
java提供的基本类型以及其对于的包装类型
基本类型 | 对于包装类型 |
---|---|
byte | Byte |
char | Character |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
void | Void |
以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即可。一个设置域的可见性,一个设置属性值。
File类声明: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
}
}
最后
以上就是有魅力老鼠为你收集整理的Integer实现原理及缓存机制的全部内容,希望文章能够帮你解决Integer实现原理及缓存机制所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复