必知的技术知识:Integer引发的思考

简介: 必知的技术知识:Integer引发的思考

1,思考以下程序的输出结果


@Test


public void test1() throws Exception {


Integer num1 = 1;


Integer num2 = 2;


System.out.println(num1+" : "+num2);


sweap(num1,num2);


System.out.println(num1+" : "+num2);


}


private void sweap(Integer num1, Integer num2) {


Integer temp = num1;


num1 = num2;


num2 = temp;


}


注:引用传递传递的是地址,准确的说是地址的文本,程序在调用sweap函数时,将num1的地址传递给sweap函数内的num1,但是这两个num1,并不是同一个变量,只是指向了同一个地址,所以说,当num1的引用指向num2的地址的时候,对主函数中的num1是没有影响的。


@Test


public void test2() throws Exception {


User user1 = new User("user1");


User user2 = new User("user2");


System.out.println("*处理前*");


System.out.println(user1);


System.out.println(user2);


System.out.println("*sweap*");


sweap(user1, user2);


System.out.println(user1);


System.out.println(user2);


System.out.println("*handle**");


handle(user1, user2);


System.out.println(user1);


System.out.println(user2);


System.out.println("*toNull**");


toNull(user1, user2);


System.out.println(user1);


System.out.println(user2);


}


private void sweap(Object num1, Object num2) {


Object temp = num1;


num1 = num2;


num2 = temp;


}


private void handle(User user1,User user2) {


String name1 = user1.getName();


user1.setName(user2.getName());


user2.setName(name1);


}


private void toNull(User user1,User user2) {


user1 = null;


user2 = null;


}


注:


(1)这里的sweap函数的原理跟上面的一样,只修改引用的指向,对主函数中的参数并无影响


(2)handle函数中同样,主函数的user1将地址传递给函数中的user1,他们两个虽然不是同一个变量但是确指向了同一个对象,在handle函数中,可以通过getName()获取到对象的name,也可以通过setName()给name重新赋值。


(3)toNull函数中,将参数置空其实是将toNull中的user1变量的引用与主函数中用户对象断开联系,也不会影响到对象的值。


2,交换两个Integer对象的值的正确操作:


private void sweap1(Integer num1, Integer num2) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {


Field field = Integer.class.getDeclaredField("value");


field.setAccessible(true);


Integer temp = new Integer(num1.intValue());


field.set(num1,num2.intValue());


field.set(num2, temp);


}


3,涉及到的知识点


(1)通过反射去修改final类型的值


在Integer的源码中可以看到,Integer的value是private,final的,所以直接访问就会报错,如下:


private void sweap1(Integer num1, Integer num2) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {


Field field = Integer.class.getDeclaredField("value");


Integer temp = new Integer(num1.intValue());


field.set(num1,num2.intValue());


field.set(num2, temp);


}


@Test


public void test1() throws Exception {


try {


Integer num1 = 1;


Integer num2 = 2;


System.out.println(num1+" //代码效果参考:http://hnjlyzjd.com/hw/wz_25338.html

: "+num2);

sweap1(num1,num2);


System.out.println(num1+" : "+num2);


} catch (Exception e) {


e.printStackTrace();


}


}


解决方法:使用field.setAccessible(true);可以绕过安全检查。


在Integer的源码中可以看到,程序先判断


可以看出通过field.setAccessible(true);设置了override的值;


在来看field.set()方法的源码


由上可以看,程序会在进行安全检查之前先判断override的值,在override的值设置为true后,就不再进行安全检查。


正确的交换写法:


private void sweap1(Integer num1, Integer num2) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {


Field field = Integer.class.getDeclaredField("value");


field.setAccessible(true);


Integer temp = new Integer(num1.intValue());


field.set(num1,num2.intValue());


field.set(num2, temp);


}


(2)注意temp的类型不可以是int


//代码效果参考:http://hnjlyzjd.com/xl/wz_25336.html

@Test

public void test1() throws Exception {


try {


Integer num1 = 1;


Integer num2 = 2;


System.out.println(num1+" : "+num2);


sweap1(num1,num2);


System.out.println(num1+" : "+num2);


} catch (Exception e) {


e.printStackTrace();


}


}


private void sweap1(Integer num1, Integer num2) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {


Field field = Integer.class.getDeclaredField("value");


field.setAccessible(true);


int temp = num1.intValue();


field.set(num1,num2.intValue());


field.set(num2, temp);


}


这是涉及到Java中装箱,拆箱的原理:


在Field.set()方法中,第二个参数指定的数据类型是Object,所以当传递一个int对象的时候,jdk就会对其做自动装箱,将其封装成Integer对象


field.set(num1,num2.intValue());


的完成操作应该是:


num1.valueOf(num2.intValue());


所以改该行代码执行完之后,num1的value的值已经变成了2;


而temp是指向num1的value的,所以他的值也就是2;


因此field.set(num2, temp);并不能将num2的value设置成num1最开始的值。


那为什么该城Integer temp = new Integer(num1.intValue())就可以了呢?


因为这样写是给temp创建了一个新的对象,而不是指向num1值的一个引用,所以num1的变化,并不会引起temp的变化。


扩展:包装类


(1)在jdk1.5版本之前:


  Integer num = new Integer(3);


在jdk1.5版本,为简化操作:


  Integer num = 3;


这种写法只是为了简化编码,但是实际的操作依然是new Integer(3),只不过在这里做了基本数据类型的自动装箱


  num = num + 1;


这里的num是个Integer类型的对象,这计算的时候会进行自动拆箱,转换成基本数据类型;实际操作时


num = num.intValue()+1;


这里需要注意的是Integer类型的num是可以为null的


所以在num的值为null时对其进行运算,在自动拆箱调用其intValue()方法时就会抛出空指针异常


(2)Integer num1 = 127;


Integer num2 = 127;


syso(num1 == num2)


这里的输出结果会是true,因为Integer中缓存了-128~127,这些数值在获取的时候是拿的同一个对象


Integer num3 = 128;


Integer num4 = 128;


syso(num3 == num4)


这里输出就会是false,128并不在缓存的范围内,通过看Integer源码也能看出来,在byte范围之外会new 一个新的对象。

相关文章
|
20天前
|
存储 Java
深入Java List:探寻有序集合背后的故事
【6月更文挑战第17天】Java List接口,作为有序集合,用于数据存储与处理。ArrayList和LinkedList是常见实现类:ArrayList基于数组,适合随机访问但插入删除慢;LinkedList基于链表,插入删除快但随机访问效率低。在需要频繁在开头插入元素并高效访问时,应选用LinkedList。了解这些原理能帮助优化代码性能。
|
2月前
|
存储 C++ 容器
c++的学习之路:14、list(1)
c++的学习之路:14、list(1)
22 0
|
2月前
|
C++
c++的学习之路:15、list(2)
c++的学习之路:15、list(2)
18 0
|
2月前
|
C++
c++的学习之路:16、list(3)
c++的学习之路:16、list(3)
20 0
|
2月前
|
存储 算法 C语言
c++的学习之路:9、STL简介与string(1)
c++的学习之路:9、STL简介与string(1)
36 0
|
2月前
|
存储 Java 程序员
List:程序员的得力助手
List:程序员的得力助手
38 0
|
编译器 C语言 C++
【C++】一文带你吃透string的模拟实现 (万字详解)(下)
【C++】一文带你吃透string的模拟实现 (万字详解)(下)
【C++】一文带你吃透string的模拟实现 (万字详解)(下)
|
存储 编译器 C++
【C++】一文带你吃透string的模拟实现 (万字详解)(上)
【C++】一文带你吃透string的模拟实现 (万字详解)(上)
【C++】一文带你吃透string的模拟实现 (万字详解)(上)
|
存储 缓存 Java
10年老鸟竟不知道Integer如何比较大小
10年老鸟竟不知道Integer如何比较大小
532 0
10年老鸟竟不知道Integer如何比较大小