当前位置: 首页 > 后端技术 > Node.js

Java参数传递是按值传递还是按引用传递?

时间:2023-04-03 11:36:21 Node.js

前言先明确Java中方法参数的传递方式是传值。对于基本类型(inta,longb),在传参的时候传值,比如inta=5,传5。如果是引用类型,则传递指向具体对象内存地址的地址值。比如System.out.println(newObject())打印出来的java.lang.Object@7716f4中@符号后的7716f4就是16进制的内存地址,System.out.println实际上默认调用了对象的toString方法,publicStringtoString(){returngetClass().getName()+"@"+Integer.toHexString(hashCode());}可以看出hashCode()输出了最多7716f4。如果一个对象重写了hashCode方法,输出的可能不是该对象的初始内存地址。因此,如果想准确获取对象的初始地址,建议调用System.identityHashCode()。值得一提的是,在Java中获取一个对象的内存地址一般是没有意义的,因为它可能在程序运行过程中随着垃圾回收等动作被JVM改变。但是下面我们可以根据引用的对象地址是否相同来看参数传递的各种情况。举例说明基本类型作为参数传递publicclassValuePass{publicstaticvoidmain(String[]args){//值传递示例intnum=10;System.out.println("修改前的值:"+num);修改(数量);System.out.println("修改后的值:"+num);}privatestaticvoidmodify(intnum2){num2=11;}}输出的结果是修改前的值:10修改后的值:10通过这个例子说明,当基本数据类型作为参数传递时,传递的是值的副本。不管副本怎么变,原来的值是不会变的。对象作为参数传递对象又可以分为普通对象、集合类型和数组类型。我们依次来看一下效果。CommonobjectpublicclassReferenceBasicPass{输出结果的实参node指向的内存地址为:366712642,当到达相同地址时,通过node.setVal(11),通过地址找到真正的对象进行操作。这里TreeNode没有重写hashCode方法,所以集合对象因为ArrayList重写了hashcode()方法,所以这里使用System.identityHashCode获取地址值。公共类ReferenceBasicPass{静态类TreeNode{intval;树节点离开;树节点对;publicTreeNode(intx){val=x;}publicvoidsetVal(intval){this.val=val;}publicintgetVal(){返回值;}}publicstaticvoidmain(String[]args){//普通对象TreeNodenode=newTreeNode(10);System.out.println("实参node指向的内存地址为:"+node.hashCode());System.out.println("修改前的值:"+node.getVal());修改(节点);System.out.println("修改后的值:"+node.getVal());}privatestaticvoidmodify(TreeNodenode){System.out.println("形参node指向的内存地址为:"+node.hashCode());//引用同一个地址,操作同一个堆内存node.setVal(11);}}输出结果为实参节点指向的内存地址为:366712642变化前的值为:10形参节点指向的内存地址为:366712642变化后的值为:11对于set,传递的值也是引用地址,通过函数中的形参获取引用地址的副本后,才对真实对象进行操作,导致访问真实对象时实际参数被修改了。如果形参指向一个新的内存地址,修改不会影响原对象的值。注意:JsonUtils是用Jackson实现的。数组普通数组和集合一样,都是引用类型nodes.add(新树节点(1));nodes.add(新树节点(2));System.out.println("修改前实参node指向的内存地址为:"+System.identityHashCode(nodes));System.out.println("修改之前实参node指向的地址存储的对象内容为:"+JsonUtils.toJson(nodes));修改(节点);System.out.println("修改后,实参node指向的内存地址为:"+System.identityHashCode(nodes));System.out.println("修改后,实参node指向的地址存储的对象内容为:"+JsonUtils.toJson(nodes));System.out.println("\n--------------------------------------------------\n");修改2(节点);System.out.println("修改后实参node指向的内存地址为:"+System.identityHashCode(nodes));System.out.println("修改后实参nodes指向的地址存储的对象内容为:"+JsonUtils.toJson(nodes));}私人统计icvoidmodify(Listnodes){//引用同一个块地址,堆内存同一个块nodes.add(newTreeNode(3));}privatestaticvoidmodify2(Listnodes){System.out.println("形参nodes指向的内存地址:"+nodes.hashCode());//形参nodes指向一个新的内存地址,对其进行操作但不影响实参指向的实际内存地址Objectnodes=newArrayList<>();nodes.add(新树节点(5));System.out.println("形参nodes指向的新内存地址:"+nodes.hashCode());系统输出。println("形参nodes指向的新地址存储的对象内容为:"+JsonUtils.toJson(nodes));}}输出修改前实参节点指向的内存地址:366712642修改前实参节点指向的地址存储的对象内容为:[{"val":1},{"val":2}]修改后,实参节点指向的内存地址为:366712642修改后,实参节点指向的地址存储的对象内容为:[{"val":1},{"val":2},{"val":3}]---------------------------------------------形参节点指向的内存地址:1110478811形参节点指向的新内存地址:1458540949新地址中存储的对象内容形参nodes指向的是:[{"val":5}]再次修改后,实参nodes指向的内存地址为:366712642再次修改后的实参nodes该地址存储的对象内容为:[{"val":1},{"val":2},{"val":3}]数组和集合也是如此值得注意的是,对于封装类型的基本类型,参数传递也是地址值传递;publicclassReferenceArrayPass{publicstaticvoidmain(String[]args){//普通数组,同集合引用类型,数组本质上就是int[]ints=newint[3];整数[0]=1;整数[1]=2;System.out.println("实参ints指向的内存地址为:"+System.identityHashCode(ints));System.out.println("修改前Ints索引为2"+ints[2]);修改(整数);System.out.println("修改后Ints索引为2值"+ints[2]);//普通数组的类是[I,I表示int类型System.out.println(ints.getClass());}privatestaticvoidmodify(int[]ints){//引用同一个地址,操作同一个堆内存System.out.println("形参ints指向的内存地址为:"+System.identityHashCode(整数));整数[2]=3;}}输出结果为实参ints指向的内存地址为:366712642修改前ints索引的值为2:0形参ints指向的内存地址为:366712642ints索引的值为修改后为2:3但是由于jdk1.5上面的自动装箱的特性,Integeri=20相当于执行Integeri=Integer.valueOf(20),valueOf()方法参考源码,如果传入的值在-128-127之间,会从常量池中得到一个InTeger对象返回,如果不在范围内,newInteger(20)就会返回,也就是说Integer的地址会随着值的变化而变化,其实就是引用类型的赋值,指向一个新的内存地址,比如上面的integer=21的例子等价于integer=Integer.valueOf(21),不管之前是否创建过21,这个integer指向一个新的内存地址,但是不影响实参,并且外部值仍然是20