Java中的“引用传递”

做Java的小伙伴们肯定都知道,java的参数传递仅仅支持值传递,不像C#中可以用ref、out关键字传递引用!!

今天小编突然脑洞大开,Java也可以“引用传递”!!

话不多说,先上一段基本的Java代码

值类型参数传递

public static void main(String[] args) {
    int num = 9;
    change(num);
    System.out.println("main --> num:" + num);
}

private static void change(int num) {
    num += 2;
    System.out.println("change --> num:" + num);
}

输出结果

这个没有疑问!so easy

引用类型参数传递(String对象和基本数据类型对象)

用String做演示

 1 public static void main(String[] args) {
 2     String str = "--mainMethod--";
 3     change(str);
 4     System.out.println("main --> str:" + str);
 5 }
 6 
 7 private static void change(String str) {
 8     str = "--changeMethod--";
 9     System.out.println("change --> str:" + str);
10 }

 

输出结果

也没有疑问?不管有没有,这里都要稍作解释一下

我们加上输出哈希编码,看看结果如何!

 1 public static void main(String[] args) {
 2     String str = "--mainMethod--";
 3     System.out.println("main --> "+str.hashCode());
 4     change(str);
 5     System.out.println("main --> str:" + str);
 6 }
 7 
 8 private static void change(String str) {
 9     str = "--changeMethod--";
10     System.out.println("change --> "+str.hashCode());
11     System.out.println("change --> str:" + str);
12 }

 

输出结果

我们都知道,一般情况下 hashCode 的值不一样,表示对象也不一样。因此得出结果:这个字符串对象的引用在change方法中发生了改变(在Java中,只要是String对象中的值发生改变,就会重新创建一个String对象)。

但是为什么main方法中的str没有改变呢?

这里要说到Java的方法传参了。Java中方法传参是值类型传递的,这个大家都知道,但是,不仅如此,传送的还是参数的副本。

什么意思呢?就是在传递参数的同时,Java虚拟机做了个操作,将这个参数备份了一下,将副本传给这个方法。

在上面这个change方法中,副本的引用地址发生了改变,会影响到原参数吗?答案:不会的!

接着再看一段代码

引用类型参数传递(非(String对象和基本数据类型对象)

用List做演示

 1 public static void main(String[] args) {
 2     List<String> list = new ArrayList<String>();
 3     list.add("chb");
 4     change(list);
 5     System.out.println("main --> size:"+list.size());
 6     for (String item : list) {
 7         System.out.println("foreach --> "+item);
 8     }
 9 }
10 
11 private static void change(List<String> list) {
12     list.add("yiming");
13     System.out.println("change --> size:"+list.size());
14 }

输出结果

唉!怎么回事,这个值竟然添加进去了!

先嫑(不要)惊讶,看完下面代码,再惊讶也不晚!!

输出一下哈希编码

 1 public static void main(String[] args) {
 2     List<String> list = new ArrayList<String>();
 3     list.add("chb");
 4     System.out.println("main --> hashCode:"+list.hashCode());
 5     change(list);
 6     System.out.println("main --> size:"+list.size());
 7     for (String item : list) {
 8         System.out.println("foreach --> "+item);
 9     }
10 }
11 
12 private static void change(List<String> list) {
13     list.add("yiming");
14     System.out.println("change --> hashCode:"+list.hashCode());
15     System.out.println("change --> size:"+list.size());
16 }

输出结果

what?和上面的结论不一样了?hashCode改变了,为什么main函数中的list发生了改变呢?

输出一下,调用change方法之后的list的哈希编码

 1 public static void main(String[] args) {
 2     List<String> list = new ArrayList<String>();
 3     list.add("chb");
 4     System.out.println("main --> hashCode:"+list.hashCode());
 5     change(list);
 6     System.out.println("main --> hashCode:"+list.hashCode());
 7     System.out.println("main --> size:"+list.size());
 8     for (String item : list) {
 9         System.out.println("foreach --> "+item);
10     }
11 }
12 
13 private static void change(List<String> list) {
14     list.add("yiming");
15     System.out.println("change --> hashCode:"+list.hashCode());
16     System.out.println("change --> size:"+list.size());
17 }

输出结果

是不是有些明白了?

再看下面代码,在change方法中重新new一次

 1 public static void main(String[] args) {
 2     List<String> list = new ArrayList<String>();
 3     list.add("chb");
 4     System.out.println("main --> hashCode:"+list.hashCode());
 5     change(list);
 6     System.out.println("main --> hashCode:"+list.hashCode());
 7     System.out.println("main --> size:"+list.size());
 8     for (String item : list) {
 9         System.out.println("foreach --> "+item);
10     }
11 }
12 
13 private static void change(List<String> list) {
14     list = new ArrayList<String>();
15     list.add("yiming");
16     System.out.println("change --> hashCode:"+list.hashCode());
17     System.out.println("change --> size:"+list.size());
18 }

输出结果

越看越乱,看看源码(add方法源码以及add方法关联方法源码)

 1 //add
 2 public boolean add(E e) {
 3     ensureCapacityInternal(size + 1);  // Increments modCount!!
 4     elementData[size++] = e;
 5     return true;
 6 }
 7 
 8 //ensureCapacityInternal
 9 private void ensureCapacityInternal(int minCapacity) {
10     modCount++;
11     // overflow-conscious code
12     if (minCapacity - elementData.length > 0)
13          grow(minCapacity);
14 }
15 
16 //grow
17 private void grow(int minCapacity) {
18     // overflow-conscious code
19     int oldCapacity = elementData.length;
20     int newCapacity = oldCapacity + (oldCapacity >> 1);
21     if (newCapacity - minCapacity < 0)
22         newCapacity = minCapacity;
23     if (newCapacity - MAX_ARRAY_SIZE > 0)
24         newCapacity = hugeCapacity(minCapacity);
25     // minCapacity is usually close to size, so this is a win:
26     elementData = Arrays.copyOf(elementData, newCapacity);
27 }

看到这里,原来如此,调用list函数的方法时,list集合长度改变,相当于重新创建了list集合,所以才会hashCode改变

所以呢,小编想到的方法是什么呢?

找到一个规律:只要不是让传入的对象等于(=)另一个对象,这个对象所做的改变,是会影响调用者传入的对象的!

一段代码,搞懂!

 1 public static void main(String[] args) {
 2     // 问题: 我想传入一个 String 实现 C# 中的 ref、out 关键字的作用
 3     // 思路:不能用等号
 4     // 用别的对象封装起来呗!!
 5     // 什么对象?非(基本数据类型对象和String对象)
 6     // 那么用list试试吧!
 7     String str = "chb";
 8     List<String> list = new ArrayList<String>();
 9     list.add(str);// 封装
10     change(list);// 此方法中进行一系列操作,将 str 的值进行改变
11     str = list.get(0);//将操作后的值,返还给 str
12     System.out.println(str);
13 }
14 
15 private static void change(List<String> list) {
16     // 一系列操作省略
17     list.set(0, "wsm");// 将集合中下标是0的值,替换成:wsm
18 }

输出结果

这个时候就会有人说了,我用返回值不行吗?

小编给你的回答:可以,完全可以!但是,你想一下,编写的代码不可能总像小编写的这么简单吧!返回值要是返回了另一个数据呢?你这个值怎么办?

1 private static Element teleseme_template(String[] obj, List<List<String[]>> s) throws Exception {
2 //方法体保密
3 }

就像上面这个方法,怎么办?我在这个方法中更改了s参数中的List<String[]>集合的值,但是,在调用者那边还需要得到s参数里面List<String[]>的新值,而我的返回值类型却是一个实体!!

若有疑问或者发现小编写的有问题,还请留言!谢谢!!

posted @ 2018-01-16 18:21  chbyiming  阅读(400)  评论(0)    收藏  举报