zoukankan      html  css  js  c++  java
  • 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+" : "+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

        @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 一个新的对象。
  • 相关阅读:
    chapter16 计算机体系结构基础
    ASP.NET 2.0加密Web.config 配置文件
    用IIS6.0的Kernel Caching 压缩技术提高应用程序性能
    .net中怎样执行一个字符串
    ASP.NET 页面生存周期中的关键事件
    ASP.NET获取客户端IP及MAC地址
    DOS命令关闭计算机
    1个式子检测密码强度
    徐州话六级考试
    .net2.0中新增的Substitution控件
  • 原文地址:https://www.cnblogs.com/excellencesy/p/12484555.html
Copyright © 2011-2022 走看看