今天想起快排的实现,便用Java实现它,我发现一个swap(int a ,int b)交换方法不能改变两个数的值,好吧我Java基础好烂,用C传地址交换做过无数遍,但用Java还真没试过,上网找方法解决,看到一篇文章写得挺不错的。
先来看下面这三段代码:
//Example1: public class Example1 { static void check(int a) { a++; } public static void main(String[]args) { int x=10; check(x); System.out.println(“Example1.x=”+x);} } //Example2: public class Example2 { static void check(StringBuffer obj) { obj.append(“JAVA”); } public static void main(String[]args) { StringBuffer x=new StringBuffer(“Hello ”); check(x); System.out.println(“Example2.x=”+x); } } //Example3: Public class Example3 { static void check(String obj) { obj=“JAVA”; } public static void main(String[]args) { String x=”Hello ”; check(x); System.out.println(“Example3.x=”+x); } }
编译运行结果是:
Example1.x=10
Example2.x=Hello JAVA
Example3.x=Hello
类Example1的结果是最容易理解的,因为此时向方法check(int a)传递的是一个整型变量,而整型变量是基本数据类型的一种。当向一个方法传递基本数据类型时(基本数据类型包括 byte,int,short,char,float,double以及boolean),传递的只是该数据内容的一个副本,因此无论方法针对该副本值做怎样的改变,都不会影响到被传入的数据本身。
类Example2的结果也是比较容易理解的,因为此时向方法check(StringBuffer obj)传递的是一个StringBuffer变量,这个变量是对象型数据类型的一种。当向一个方法传递对象型数据类型(包括String, StringBuffer,类对象引用,接口引用和数组等)时,传递的是该数据对象的某个引用变量而不是对象内容本身,因此,在将引用变量x传入方法时,obj和x便同时对原来x所引用的对象(这个对象的内容是”Hello ”)具有了引用关系,也就是说,obj和x都是对象(即”Hello ”)的引用,由于JAVA对于对象的访问是通过访问对象的引用来完成的,因此,当方法对obj的引用对象内容进行改变时,实际上也是在针对x所引用的对象的内容进行改变,这自然导致了读者所看到的结果。
但类Example3所显示的结果就令人费解了。这个例子颇像类Example2,传入方法的String也是一个对象型数据,按理说传入方法的就应该是引用而不是类似Example1的副本值,但结果恰好相反,方法并未对x所引用的对象造成任何影响。因此,有些教材就此定论,String对象是特例,只有String这个特例向方法传递对象内容的副本值,而其它对象型数据则传递对象引用,事实果真如此吗?
这种理解绝对是错误的!用户对传入方法的数据倒底是传值还是传引用的问题感到迷惑,其原因主要是用户对数据类型的使用机制不够了解。因而,在解释上述问题之前,先来清楚一下JAVA中数据类型的使用机制。
JAVA中基本数据类型是在系统的栈中创建的,而对象型数据则是在堆中创建的。位于栈中的基本数据类型简化了调用机制,可以被直接调用而没有引用的概念,同时也抛弃了作为对象可以带来复杂属性设置的好处,属于轻装上阵,因而运行效率高;对象型数据正好相反,尽管运行效率低于前者,但可以向用户提供作为对象带来属性设置的好处,不过,用户必须也只能通过对象的引用变量来实现对对象内容的访问,不能象基本数据类型那样直接访问对象内容。例如有这样一句代码:
String a;
在这里,a并不是对象,而是可以访问对象的一个引用变量,一个对象可以有多个引用变量指向它,但每个引用变量却只能指向一个对象。那么对象在哪里呢?这时的对象还未创建,系统只以默认的null来充当对象,直到出现下面的代码:
a=new String(“Hello”);
这时的对象已通过new关键字被创建(JAVA中几乎所有的对象都要使用new关键字来创建)也许有读者会问对象的名字,恕难奉告,因为对象根本就没有名字,只有内容,大小等诸多属性,还有就是一个或多个可被用来访问对象的引用变量,例如这里的a。
需要说明,上述针对对象型数据的描述,并不局限于String,同样适用于StringBuffer,类对象引用,接口引用和数组等对象型数据。
这样,我们终于知道,类Example3中传入方法的x是对象的引用变量而不是对象内容的副本,因为针对String这个对象型数据的内容的访问必须通过调用其引用变量来实现,系统不可能将对象内容直接传入方法。但其结果又为何与类Example2中的显示大相径庭呢?其实根源就在String,String不仅仅是一种对象型数据类型,而且还是一种不可变类型。这里的不可变,指的String引用的对象内容不可变,而不是引用变量本身的引用方向不能变。例如在Example3中,当x将其引用传入方法时,的确使obj引用变量与x引用变量同时引用了对象“Hello ”,但随着代码obj=”JAVA”;的执行,obj将其引用的目标从原来的对象“Hello ”转移至新对象“JAVA”,注意:此时obj并不能去改变自己原来的引用对象“Hello ”本身,只能改变自己的引用方向并引用新对象“JAVA”。(“Hello ”这个对象还存在,只不过现在只剩x变量一个引用了,如果再失去x的引用,例如执行语句:x=null;那么对象“Hello ”就象断了线的风筝,只能等待垃圾回收机制的处理了)这正是“不可变类”真正含义。由此可见,check(String obj)方法的调用,只是改变了obj引用变量的引用关系,而对x引用的对象内容“Hello ”未构成任何影响。
回过头来再看类Example2,StringBuffer类则是一个可变类,不仅其对象内容可变(使用类的append方法),其引用关系也可变(使用“=”),请读者尝试将代码:
obj.append(“JAVA”); 改写为:
obj=new StringBuffer(“JAVA”);想想结果又会是怎样?
最后的问题,我们需要对上述的知识进行总结和扩展。JAVA中,向一个方法传递简单数据类型时,传入的是副本值,方法的改变不会影响到被传入的变量值;传入对象型数据类型时,传入的是对象引用而不是对象内容,方法的改变绝对不会影响到被传入引用变量的引用关系,但却有可能改变被传入引用变量所引用的对象的内容。请在这里注意String这个不可变类的真正含义。除上述外,向方法传入数组,类对象引用或接口引用得到的结果都是与StringBuffer类的处理相似的。
看到这里,要用Java解决两个数的交换,看来要把数组封装成类再去操作了.