文档章节

两个Integer的引用对象传递给一个swap方法的内部进行交换,返回后,两个引用的值是否会发生变化

须臾之余
 须臾之余
发布于 08/17 14:23
字数 1016
阅读 18
收藏 0

示例一:

/**
 * 大厂面试题(微博、百度、腾讯):
 *      两个Integer的引用对象传递给一个swap方法的内部进行交换,返回后,两个引用的值是否会发生变化
 */
public class Test001 {
    public static void main(String[] args) {
        Integer a=1,b=2;
        //a=Integer@533=1,b=Integer@534=2
        System.out.println("before:a="+a+",b="+b);
        swap(a,b);
        //a=Integer@533=1,b=Integer@534=2
        System.out.println("after:a="+a+",b="+b);
    }
    private static void swap(Integer i1, Integer i2) {
        //tmp=i1=Integer@533---1
        Integer tmp=i1;
        //i1=i2=Integer534@--2
        i1=i2;
        //i2=tmp=i1=Integer@533----1
        i2=tmp;
    }

输出结果:

数组元素作为函数的实参时,用法跟普通变量作参数相同,将数组元素的值传递给形参时进行函数体调用,函数调用完返回后,数组元素的值不变。这种传递方式是”值传递“方式,即只能从实参传递给形参,而不能从形参传递给实参

我们通过Java反编译工具查看,底层通过Integer.valueOf()来转换

我们通过源码来看看valueOf()方法实现原理

public static Integer valueOf(int i) {
    //如果是在Integer缓存中-128到127之间则去缓存中取值
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    //否则直接开辟一个新的内存空间
    return new Integer(i);
}
private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];//缓存数组

.....
}

我们Integer a=1,b=2在Integer缓存范围之内,所以走 return IntegerCache.cache[i + (-IntegerCache.low)];去缓存数组中拿值

线程对变量的所有操作(读取、赋值)都必须在工作内存中进行,而不能直接读写主内存中的变量。在swap方法内部交换引用,只会交换线程的工作内存中持有的方法参数,

而工作内存中的方法参数是主内存中变量的副本,因此执行这样的swap方法不会改变主内存中变量的指向  

案例二:

public class Test002 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Integer a=1,b=2;
        //a=Integer@534=1,b=Integer@535=2
        System.out.println("before:a="+a+",b="+b);
        swap(a,b);
        //a=Integer@534=2,b=Integer@535=2
        System.out.println("after:a="+a+",b="+b);
    }
    private static void swap(Integer i1, Integer i2) throws NoSuchFieldException, IllegalAccessException {
        //反射获取成员变量
        Field value = Integer.class.getDeclaredField("value");
        value.setAccessible(true);
        //tmp=1
        int tmp=i1.intValue();
        //i1=Integer@534=2
        value.set(i1,i2.intValue());
        //i2=Integer@535=tmp=i1.intValue()=2
        value.set(i2,tmp);
    }

输出结果

使用反射机制,传递的是数组元素对应的地址,这样形参数组和实参数组共占用一段内存单元,当形参值发生变化时,实参值也发生变化。

查看反编译结果

private final int value;

交换的是引用地址,修改成员变量final value的值,可用通过反射机制修改。

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

还是会去Integer缓存数组中找到这个值2,并设置给 i1,因为tmp=i1.intValue(),栈中的tmp的地址会指向Integer在堆中数组对应值为i1的地址,所以

经过 value.set(i1, Integer.valueOf(i2.intValue()));之后,tmp就=2,最后 value.set(i2, Integer.valueOf(tmp));将2赋值给 i2.

案例三:

public class Test003 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Integer a=1,b=2;
        //a=Integer@534=1,b=Integer@535=2
        System.out.println("before:a="+a+",b="+b);
        swap(a,b);
        //a=Integer@534=2,b=Integer@535=1
        System.out.println("after:a="+a+",b="+b);
    }
    private static void swap(Integer i1, Integer i2) throws NoSuchFieldException, IllegalAccessException {
        //反射获取成员变量
        Field value = Integer.class.getDeclaredField("value");
        value.setAccessible(true);
        //重新开辟一个内存空间
        //tmp=Integer@545=1
        Integer tmp=new Integer(i1.intValue());
        //i1=Integer@534=2
        value.set(i1,i2.intValue());
        //i2=Integer@535=tmp=new Integer(i1.intValue())=1
        value.set(i2,tmp);

    }
}

输出结果为:

这里总总结前面的经验,new Integer开辟新的内存空间,不会走缓存了

 

© 著作权归作者所有

须臾之余
粉丝 125
博文 68
码字总数 178724
作品 0
吉安
程序员
私信 提问
你真的了解JAVA的形参和实参吗?

前几天在头条上看到一道经典面试题,引发了一些思考。也是写这篇文章的导火索。 背景 请看题: 看到这个题后 瞬间觉得有坑。也觉得为什么要书写一个 方法呢?如下实现不是更简单: 输出: 完美实...

技术小能手
2018/09/19
0
0
Java交换两个Integer-一道无聊的题的思考

1.最近网上看到的一道题,有人说一道很无聊的题,但我觉得有必要记录一下。 2.题目

汉堡OSC
04/14
21
0
java 值传递、引用传递笔记

Java总是采用按值调用。方法得到的是所有参数值的一个拷贝,特别的,方法不能修改传递给它的任何参数变量的内容。 方法参数共有两种类型: 基本数据类型 对象引用 1. 基本数据类型为参数 查看...

ts88
2018/07/11
9
0
8-Java基础语法-Java方法

什么是方法? 首先会想到的应该是我们最常使用到的主方法 main方法是程序的入口 类,Scanner是一个类,sc是它实例化出的对象。nextInt()是从键盘获取一个整型值的方法,next()是从键盘获取一...

天涯明月笙
2018/08/02
0
0
Python学习13.03:Python函数参数传递机制(超级详细)

Python中,函数参数由实参传递给形参的过程,是由参数传递机制来控制的。通过学习《Python函数值传递和引用传递》一节我们知道,根据实际参数的类型不同,函数参数的传递方式分为值传递和引用...

太空堡垒185
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

75、GridFS

GridFS是MongoDB提供的用于持久化存储文件的模块,CMS使用Mongo DB存储数据,使用FGridFS可以快速集成开发。 工作原理: 在GridFS存储文件是将文件分块存储,文件会按照256KB的大小分割成多个...

lianbang_W
32分钟前
4
0
js bind 绑定this指向

本文转载于:专业的前端网站➱js bind 绑定this指向 1、示例代码 <!DOCTYPE html><html lang="zh"> <head> <meta charset="UTF-8" /> <title>bind函数绑定this指向......

前端老手
35分钟前
4
0
CentOS Linux 7上将ISO映像文件写成可启动U盘

如今,电脑基本上都支持U盘启动,所以,可以将ISO文件写到U盘上,用来启动并安装操作系统。 我想将一个CentOS Linux 7的ISO映像文件写到U盘上,在CentOS Linux 7操作系统上,执行如下命令: ...

大别阿郎
42分钟前
4
0
深入vue-公司分享ppt

组件注册 全局注册 注册组件,传入一个扩展过的构造器 Vue.component('my-component', Vue.extend({/*...*/})) 注册组件,传入一个选项对象(自动调用Vue.extend) Vue.component('my-comp...

莫西摩西
43分钟前
3
0
gitlab重置管理员密码

登录gitlab服务器 [root@localhost bin]# sudo gitlab-rails console productionLoading production environment (Rails 5.2.3)irb(main):001:0> u = User.where(email: 'admin@example.co......

King华仔o0
53分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部