发布于2026-07-16 阅读(0)
扫一扫,手机访问
本文先回顾C/C++的swap方式,再探讨Ja va中为何无法直接实现,最后通过反射给出一个取巧方案。
说起交换两个变量的值,C语言的老手马上会想到指针——传个地址进去,把两个内存位置的值一调换,干净利落。代码长这样:
void swap(int *a, int *b){
int temp;
temp = *a;
*a = *b;
*b = temp;
}
C++就更灵活了,指针和引用任选:
void swap1(int *a, int *b){
int temp;
temp = *a;
*a = *b;
*b = temp;
}
void swap2(int &a, int &b){
int temp;
temp = a;
a = b;
b = temp;
}
跑个测试看看,结果自然是如愿以偿:

原理很简单:函数拿到的是变量在内存中的真实地址(或者引用,本质也是地址的别名),对地址所指向的内容做修改,外面跟着变。可一旦切换到Ja va,情况就彻底不一样了。
Ja va不像C/C++那样暴露指针给你玩。基本类型(int、double、float等等)传参的时候统统是值传递——函数里拿到的是原始值的一个副本。你在函数里把副本换来换去,外面的变量纹丝不动。效果就像这样:

那对象类型呢?Ja va里对象传的是“引用”,但这个引用和C++的引用不是一回事。Ja va的值拷贝传递的是引用的副本——说白了,是两个指向同一对象的遥控器。你可以通过其中一个遥控器修改对象的内部状态,但如果试图把遥控器本身(引用)重新指向别的对象,那改的只是函数内部那个副本,外面的遥控器依然指向原来的对象。所以下面的代码同样达不到交换目的:

内存里的实际情况是这样的:两个引用副本交换了指向,但原始引用纹丝不动。

那么,Ja va真的没法实现一个像样的swap吗?也不尽然。下面就用反射,对Integer类搞个“偷天换日”:

import ja va.lang.reflect.Field;
public class SwapTest {
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
System.out.println(a + " " + b);
swap(a, b);
System.out.println(a + " " + b);
}
public static void swap(Integer a, Integer b) {
if (a == null || b == null) {
return;
}
// 获得a的class对象
Class integerClass = (Class) a.getClass();
try {
// 获得value属性
Field value = integerClass.getDeclaredField("value");
// 设置权限为可访问
value.setAccessible(true);
// 交换
int temp = a;
value.setInt(a, b);
value.setInt(b, temp);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
注意,这个方案只适用于Integer这类封装了基本类型的对象(而且依赖内部字段“value”)。换成其他自定义对象,只要内部有可修改的字段,道理是一样的。但话说回来,这种写法更多是演示反射的威力,实际工程中压根没必要这样折腾——为了swap绕这么大弯子,不如直接换个思路设计代码。时间宝贵,把精力花在学习真正有意义的东西上吧。
C/C++的swap走的是“传地址、改内存”的路子——指针或引用直接操作变量所在的内存位置。Ja va则对程序员屏蔽了地址概念,为的是减少指针误用带来的风险。
Ja va里所有方法参数传递都是值拷贝:基本类型拷贝值的本体;对象类型拷贝引用的副本。通过这个引用副本可以修改对象内部的值,但无法改变外部实参的引用指向。反射机制可以绕过访问控制,强行修改对象的私有字段,从而在某种程度上实现swap,但代价高、可维护性差,并不推荐在日常代码中使用。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8