JAVA基础之-参数传递

打印 上一主题 下一主题

主题 898|帖子 898|积分 2694

准备整理一个系列,这是系列的第一篇。
这是一个经典的问题,也是JAVA程序员所必须掌握的。
一、小结论和例子

1.1结论

内容没有多少,可以先说结论:
变量的表现和参数传递

  • 变量是如何表现,尤其是参数是如何表现的

    •  存储则具体看变量是什么类型:类静态、实例变量、方法
    • 变量表现-基本类型直接存储,类类型则存储地址



  • 值是如何传递的

    • 假如是基本类型-则是值的副本
    • 假如是类类型-则是指向具体数据的地址的副本

变量通过方法加工后,对原来变量的影响

  • 方法对基础类型参数做任何处置惩罚,都不会影响到参数关联的变量的值
  • 假如方法中对对象类型参数不做重新赋值,那么方法会影响参数关联的变量的值
  • 假如方法中对对象类型参数重新赋值,那么方法不会影响参数关联的变量的值
1.2示例代码

本例子是基于JDK17编写:
  1. package study.base.param;
  2. import java.lang.reflect.InvocationTargetException;
  3. import java.util.Random;
  4. import com.alibaba.fastjson2.JSON;
  5. /**
  6. * 本类主要演示以下内容:
  7. * <pre>
  8. *    1.在方法中传递对象
  9. *    2.如何在方法中交换两个对象的值
  10. *    3.通过对象地址验证方法参数被重新赋值后,会指向另外一个对象的地址
  11. * </pre>
  12. */
  13. public class TestPassObjectParam {
  14.    
  15.     public void testPassint(int x) {
  16.         x=x+10;
  17.         System.out.println("x=x+10="+x);
  18.     }
  19.     /**
  20.      * 测试-传递字符串,但是对参数整体调整,不会影响外部的变量,
  21.      * 因为这会给参数重新赋值,即重新指向另外一个对象的地址,已经不指向原来的对象
  22.      * @param s1
  23.      * @param s2
  24.      */
  25.     public  void testPassString(String s1,String s2) {
  26.         System.out.println("参数s1,s2在方法中被重新赋值,但不会影响到相关的变量");
  27.         s1=s1+"**";
  28.         s2=s2.substring(2);
  29.     }
  30.    
  31.     /**
  32.      * 改变参数的局部值,会改变量本身
  33.      * @param dog
  34.      */
  35.     public void  testPassObject(Dog dog) {
  36.         dog.www();
  37.         dog.eat();
  38.         System.out.printf("参数dog的逻辑地址=%s \n",System.identityHashCode(dog));
  39.     }
  40.    
  41.     /**
  42.      * 为对象类型参数重新赋值,不会改变变量
  43.      * @param dog
  44.      */
  45.     public void  testPassObjectAndchange(Dog dog) {
  46.         dog=new Dog("等级很高", "白", 24);
  47.         System.out.printf("参数dog被重新赋值后的逻辑地址=%s\n",System.identityHashCode(dog));
  48.     }
  49.    
  50.     public Dog createDog(String name,String color,Integer weight){
  51.         Dog dog=new Dog(name,color,weight);
  52.         return dog;
  53.     }
  54.    
  55.     /**
  56.      * 经典的字符串交换例子--这是不可能的,这是因为字符类型是不可变的。
  57.      * @param a  
  58.      * @param b
  59.      */
  60.     public void swap(String a,String b) {
  61.         String tmp=a;
  62.         a=b;
  63.         b=tmp;
  64.     }
  65.    
  66.     public void swapDog(Dog a,Dog b) {
  67.         Dog c=a;
  68.         a=b;
  69.         b=c;
  70.     }
  71.    
  72.     public void swapDog2(Dog a,Dog b) {
  73.         //Dog c=a;
  74.         String tsa=JSON.toJSONString(a);
  75.         String tsb=JSON.toJSONString(b);
  76.         a=JSON.to(Dog.class, tsb);
  77.         b=JSON.to(Dog.class, tsa);
  78.     }
  79.    
  80.     public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
  81.         TestPassObjectParam test=new TestPassObjectParam();
  82.         
  83.         // 演示基本类型和对象类型传递
  84.         // a.1  传递基本类型。基本类型 的值不会被改变
  85.         int x=10;
  86.         test.testPassint(x);
  87.         System.out.println(x); // 基本类型不会被改变,因为传递的是x的副本
  88.         
  89.         //a.2  传递对象。可能改变变量的值,也可能不会。   这里需要格外小心,尤其是传递String类型的时候。
  90.         //在方法中对对象类型进行处理,是否会修改对象,需要格外小心,有时候会修改变量,有时候不会
  91.         //大体上可以有3个基本对的结论:
  92.         // 1.如果只是对参数的局部属性进行修改,那么变量也会被改变
  93.         // 2.如果对参数整体进行处理,或者重新赋值,那么变量不会被改变
  94.         // 3.以上2点用String不好理解,最好使用更加复杂的一些类进行测试
  95.         
  96.         String name="lzf";
  97.         String address="宇宙银河系太阳系地球中国";
  98.         System.out.println("变量name,address被传入方法前的值");
  99.         System.out.printf("name=%s,address=%s",name,address);
  100.         test.testPassString(name, address);
  101.         System.out.println("变量name,address被传入方法后,查看它们的值是否改变");
  102.         System.out.printf("name=%s,address=%s \n\r",name,address);
  103.         System.out.println("...现在交换字符串变量name,address");
  104.         test.swap(name, address);
  105.         System.out.println("...name,address交换后的值(事实证明挖法在简单传参的情况下交换两个对象,包括字符串)");
  106.         System.out.printf("name=%s,address=%s \n\r",name,address);        
  107.         
  108.         System.out.println("---------------------------------------------------------");
  109.         
  110.         // 通过对象的哈希编码验证在方法中对参数的赋值影响=》会被赋予另外一个对象的地址
  111.         //a.3  只是修改参数的属性,会影响变量。    演示一个Dog类型的属性变换会影响到变量,因为没有为参数重新赋值
  112.         Dog dog=test.createDog(name, "黄",15);
  113.         System.out.printf("变量dog传入方法前的逻辑地址=%s \n",System.identityHashCode(dog));
  114.         test.testPassObject(dog);
  115.         dog.www();
  116.         
  117.         //a.4  参数被重新赋值,不会改变变量
  118.         test.testPassObjectAndchange(dog);
  119.         dog.www();
  120.         
  121.         
  122.         //b:简单传参交换两个对象也是不行的
  123.         System.out.println("------------------------------------------------------------");
  124.         Dog a=test.createDog("ss", "red", 10);
  125.         Dog b=test.createDog("ww", "black", 20);
  126.         System.out.printf("Dog a,b在交换前的颜色:%s,%s \n",a.getColor(),b.getColor());
  127.         String cb=a.getColor();
  128.         test.swapDog(a, b);
  129.         String ca=a.getColor();
  130.         System.out.printf("Dog a,b在交换后的颜色:%s,%s \n",a.getColor(),b.getColor());
  131.         
  132.         if (cb.equals(ca)) {
  133.             System.out.println("Dog a,b交换失败(无法通过简单传递来交换两个对象)");
  134.         }
  135.         
  136.         System.out.println("Dog a,b通过尝试通过json序列和反序列进行交换");
  137.         test.swapDog2(a, b);
  138.         System.out.printf("Dog a,b在交换后的颜色:%s,%s \n",a.getColor(),b.getColor());
  139.         
  140.         cb=a.getColor();
  141.         if (cb.equals(ca)) {
  142.             System.out.println("Dog a,b交换失败(无法通过简单传递来交换两个对象)");
  143.         }
  144.         System.out.println("通过简单的论证,可以得出结论:两个对象通过一个函数来进行简单的交换属性,是不可行");
  145.         System.out.println("在没有特殊的情况下,java不可能再调整为参数复制/变量赋值的方法:先创建值,然后把值的地址赋予类变量/参数");
  146.         
  147.     }
  148.    
  149.     class Dog{
  150.         private String name;
  151.         private String color;
  152.         private Integer weight;
  153.         
  154.         
  155.         Dog(String name,String color,Integer weight){
  156.             this.name=name;
  157.             this.color=color;
  158.             this.weight=weight;
  159.         }
  160.         
  161.         public void eat() {
  162.             Random rand=new Random();
  163.             int randValue=rand.nextInt(1,10);
  164.             int rd=rand.nextInt(10,100);
  165.             if (rd>50) {
  166.                 this.weight+=randValue;
  167.             }
  168.             else {
  169.                 this.weight-=randValue;
  170.             }
  171.         }
  172.         
  173.         public void www() {
  174.             System.out.println("有一只"+color+"色,重"+weight.toString()+"斤,它正在吠叫:"+name);
  175.         }
  176.         public String getName() {
  177.             return name;
  178.         }
  179.         public void setName(String name) {
  180.             this.name = name;
  181.         }
  182.         public String getColor() {
  183.             return color;
  184.         }
  185.         public void setColor(String color) {
  186.             this.color = color;
  187.         }
  188.         public Integer getWeight() {
  189.             return weight;
  190.         }
  191.         public void setWeight(Integer weight) {
  192.             this.weight = weight;
  193.         }
  194.         
  195.         
  196.     }
  197. }
复制代码
注:所谓的简朴互换,即经典的互换方法,通过一个暂时变量过度。
二、留意事项和其它一些问题

 大部分情况下,参数的传递并不是一个问题,这里的留意事项,其实主要就是和字符(String)类型有关。
我们都知道,由于某些原因String本身是final存储的:
  1. public final class String
  2.     implements java.io.Serializable, Comparable<String>, CharSequence,
  3.                Constable, ConstantDesc {
  4.     /**
  5.      * The value is used for character storage.
  6.      *
  7.      * @implNote This field is trusted by the VM, and is a subject to
  8.      * constant folding if String instance is constant. Overwriting this
  9.      * field after construction will cause problems.
  10.      *
  11.      * Additionally, it is marked with {@link Stable} to trust the contents
  12.      * of the array. No other facility in JDK provides this functionality (yet).
  13.      * {@link Stable} is safe here, because value is never null.
  14.      */
  15.     @Stable
  16.     private final byte[] value;
复制代码
 
 这就意味着字符对一个字符串变量重新赋值,则必须重新创建一个字符串对象。而一个新的对象必然指向一个新的地址。
当变量/参数被指向新的地址的时候,对原来的对象天然无法产生影响。
对字符串做变更的操作都会导致为创建一个新的对象,并为变量重新赋予新对象的地址。
例如常见的substring,concat,replace都是这样的,假如仅仅是访问字符变量的属性,是不会改变字符的。
 
所以,假如希望通过一个函数修改一个字符串,那么必须只有两种途径可以影响原来的字符变量:
1.函数返回新的字符串,并把这个新的字符串赋值给原来的变量
2.把字符串包装在某个对象内部,然后在方法中为对象的字符属性重新赋值
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

三尺非寒

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表