首页 > 编程语言 >JAVA基础之-参数传递

JAVA基础之-参数传递

时间:2024-08-19 18:26:12浏览次数:10  
标签:JAVA String 基础 Dog System 参数传递 out 变量 name

准备整理一个系列,这是系列的第一篇。

这是一个经典的问题,也是JAVA程序员所必须掌握的。

一、小结论和例子

1.1结论

内容没有多少,可以先说结论:

变量的表示和参数传递

  • 变量是如何表示,尤其是参数是如何表示的
    •  存储则具体看变量是什么类型:类静态、实例变量、方法
    • 变量表示-基本类型直接存储,类类型则存储地址
  • 值是如何传递的
    • 如果是基本类型-则是值的副本
    • 如果是类类型-则是指向具体数据的地址的副本

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

  1. 方法对基础类型参数做任何处理,都不会影响到参数关联的变量的值
  2. 如果方法中对对象类型参数不做重新赋值,那么方法会影响参数关联的变量的值
  3. 如果方法中对对象类型参数重新赋值,那么方法不会影响参数关联的变量的值

1.2示例代码

本例子是基于JDK17编写:

package study.base.param;

import java.lang.reflect.InvocationTargetException;
import java.util.Random;

import com.alibaba.fastjson2.JSON;

/**
 * 本类主要演示以下内容:
 * <pre>
 *    1.在方法中传递对象
 *    2.如何在方法中交换两个对象的值
 *    3.通过对象地址验证方法参数被重新赋值后,会指向另外一个对象的地址
 * </pre>
 */
public class TestPassObjectParam {
    
    public void testPassint(int x) {
        x=x+10;
        System.out.println("x=x+10="+x);
    }
    /**
     * 测试-传递字符串,但是对参数整体调整,不会影响外部的变量,
     * 因为这会给参数重新赋值,即重新指向另外一个对象的地址,已经不指向原来的对象
     * @param s1
     * @param s2
     */
    public  void testPassString(String s1,String s2) {
        System.out.println("参数s1,s2在方法中被重新赋值,但不会影响到相关的变量");
        s1=s1+"**";
        s2=s2.substring(2);
    }
    
    /**
     * 改变参数的局部值,会改变量本身
     * @param dog
     */
    public void  testPassObject(Dog dog) {
        dog.www();
        dog.eat();
        System.out.printf("参数dog的逻辑地址=%s \n",System.identityHashCode(dog));
    }
    
    /**
     * 为对象类型参数重新赋值,不会改变变量
     * @param dog
     */
    public void  testPassObjectAndchange(Dog dog) {
        dog=new Dog("等级很高", "白", 24);
        System.out.printf("参数dog被重新赋值后的逻辑地址=%s\n",System.identityHashCode(dog));
    }
    
    public Dog createDog(String name,String color,Integer weight){
        Dog dog=new Dog(name,color,weight);
        return dog;
    }
    
    /**
     * 经典的字符串交换例子--这是不可能的,这是因为字符类型是不可变的。 
     * @param a  
     * @param b
     */
    public void swap(String a,String b) {
        String tmp=a;
        a=b;
        b=tmp;
    }
    
    public void swapDog(Dog a,Dog b) {
        Dog c=a;
        a=b;
        b=c;
    }
    
    public void swapDog2(Dog a,Dog b) {
        //Dog c=a;
        String tsa=JSON.toJSONString(a);
        String tsb=JSON.toJSONString(b);
        a=JSON.to(Dog.class, tsb);
        b=JSON.to(Dog.class, tsa);
    }
    
    public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        TestPassObjectParam test=new TestPassObjectParam();
        
        // 演示基本类型和对象类型传递
        // a.1  传递基本类型。基本类型 的值不会被改变
        int x=10;
        test.testPassint(x);
        System.out.println(x); // 基本类型不会被改变,因为传递的是x的副本
        
        //a.2  传递对象。可能改变变量的值,也可能不会。   这里需要格外小心,尤其是传递String类型的时候。
        //在方法中对对象类型进行处理,是否会修改对象,需要格外小心,有时候会修改变量,有时候不会
        //大体上可以有3个基本对的结论:
        // 1.如果只是对参数的局部属性进行修改,那么变量也会被改变 
        // 2.如果对参数整体进行处理,或者重新赋值,那么变量不会被改变
        // 3.以上2点用String不好理解,最好使用更加复杂的一些类进行测试
        
        String name="lzf";
        String address="宇宙银河系太阳系地球中国";
        System.out.println("变量name,address被传入方法前的值");
        System.out.printf("name=%s,address=%s",name,address);
        test.testPassString(name, address);
        System.out.println("变量name,address被传入方法后,查看它们的值是否改变");
        System.out.printf("name=%s,address=%s \n\r",name,address);
        System.out.println("...现在交换字符串变量name,address");
        test.swap(name, address);
        System.out.println("...name,address交换后的值(事实证明挖法在简单传参的情况下交换两个对象,包括字符串)");
        System.out.printf("name=%s,address=%s \n\r",name,address);        
        
        System.out.println("---------------------------------------------------------");
        
        // 通过对象的哈希编码验证在方法中对参数的赋值影响=》会被赋予另外一个对象的地址
        //a.3  只是修改参数的属性,会影响变量。    演示一个Dog类型的属性变换会影响到变量,因为没有为参数重新赋值
        Dog dog=test.createDog(name, "黄",15);
        System.out.printf("变量dog传入方法前的逻辑地址=%s \n",System.identityHashCode(dog));
        test.testPassObject(dog);
        dog.www();
        
        //a.4  参数被重新赋值,不会改变变量
        test.testPassObjectAndchange(dog);
        dog.www();
        
        
        //b:简单传参交换两个对象也是不行的
        System.out.println("------------------------------------------------------------");
        Dog a=test.createDog("ss", "red", 10);
        Dog b=test.createDog("ww", "black", 20);
        System.out.printf("Dog a,b在交换前的颜色:%s,%s \n",a.getColor(),b.getColor());
        String cb=a.getColor();
        test.swapDog(a, b);
        String ca=a.getColor();
        System.out.printf("Dog a,b在交换后的颜色:%s,%s \n",a.getColor(),b.getColor());
        
        if (cb.equals(ca)) {
            System.out.println("Dog a,b交换失败(无法通过简单传递来交换两个对象)");
        }
        
        System.out.println("Dog a,b通过尝试通过json序列和反序列进行交换");
        test.swapDog2(a, b);
        System.out.printf("Dog a,b在交换后的颜色:%s,%s \n",a.getColor(),b.getColor());
        
        cb=a.getColor();
        if (cb.equals(ca)) {
            System.out.println("Dog a,b交换失败(无法通过简单传递来交换两个对象)");
        }
        System.out.println("通过简单的论证,可以得出结论:两个对象通过一个函数来进行简单的交换属性,是不可行");
        System.out.println("在没有特殊的情况下,java不可能再调整为参数复制/变量赋值的方法:先创建值,然后把值的地址赋予类变量/参数");
        
    }
    
    class Dog{
        private String name;
        private String color;
        private Integer weight;
        
        
        Dog(String name,String color,Integer weight){
            this.name=name;
            this.color=color;
            this.weight=weight;
        }
        
        public void eat() {
            Random rand=new Random();
            int randValue=rand.nextInt(1,10);
            int rd=rand.nextInt(10,100);
            if (rd>50) {
                this.weight+=randValue;
            }
            else {
                this.weight-=randValue;
            }
        }
        
        public void www() {
            System.out.println("有一只"+color+"色,重"+weight.toString()+"斤,它正在吠叫:"+name);
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getColor() {
            return color;
        }

        public void setColor(String color) {
            this.color = color;
        }

        public Integer getWeight() {
            return weight;
        }

        public void setWeight(Integer weight) {
            this.weight = weight;
        }
        
        
    }
}

注:所谓的简单交换,即经典的交换方法,通过一个临时变量过度。

二、注意事项和其它一些问题

 大部分情况下,参数的传递并不是一个问题,这里的注意事项,其实主要就是和字符(String)类型有关。

我们都知道,由于某些原因String本身是final存储的:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {

    /**
     * The value is used for character storage.
     *
     * @implNote This field is trusted by the VM, and is a subject to
     * constant folding if String instance is constant. Overwriting this
     * field after construction will cause problems.
     *
     * Additionally, it is marked with {@link Stable} to trust the contents
     * of the array. No other facility in JDK provides this functionality (yet).
     * {@link Stable} is safe here, because value is never null.
     */
    @Stable
    private final byte[] value;

 

 这就意味着字符对一个字符串变量重新赋值,则必须重新创建一个字符串对象。而一个新的对象必然指向一个新的地址。

当变量/参数被指向新的地址的时候,对原来的对象自然无法产生影响。

对字符串做变更的操作都会导致为创建一个新的对象,并为变量重新赋予新对象的地址。

例如常见的substring,concat,replace都是这样的,如果仅仅是访问字符变量的属性,是不会改变字符的。

 

所以,如果希望通过一个函数修改一个字符串,那么必须只有两种途径可以影响原来的字符变量:

1.函数返回新的字符串,并把这个新的字符串赋值给原来的变量

2.把字符串包装在某个对象内部,然后在方法中为对象的字符属性重新赋值

 

标签:JAVA,String,基础,Dog,System,参数传递,out,变量,name
From: https://www.cnblogs.com/lzfhope/p/18367732

相关文章

  • Docker部署Java项目
    本文使用Dockerfile的形式进行Java项目的部署第一步:创建Dockerfile文件Dockerfile是用于创建Docker对象的脚本,先创建Dockerfile文件,以下为我的文件模版: FROM:java对应的jdk版本RUN: 在构建过程中执行命令,用于安装软件、配置环境等ENV:设置环境变量WORKDIR:指向构建镜像时使......
  • java基础private/封装篇
    private的使用private设置后想要更改变量只能在此类中更改若想在其他类中更改和使用需要用get/set方法get获取变量值set更改变量值需自定义方法可加判断构造方法的概述构造方法是一种特殊的方法作用:创建对象格式:publicclass类名{......
  • JavaScript-正则表达式入门指南-全-
    JavaScript正则表达式入门指南(全)原文:IntroducingRegularExpressions协议:CCBY-NC-SA4.0一、正则表达式简介为了开始介绍正则表达式,我将从一个例子开始。这是一个你已经经历了几百次的问题。当您在线输入客户数据时,许多web表单会要求您提供电子邮件地址。为了避免输入......
  • Java熔断框架:resilience4j
    1.文档中文文档:https://github.com/lmhmhl/Resilience4j-Guides-Chinese/blob/main/index.md 2.maven依赖<dependency><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-spring-boot2</artifa......
  • Java-人工智能初学者实用手册-全-
    Java人工智能初学者实用手册(全)零、前言在一切都由技术和数据驱动的现代世界中,人工智能变得越来越重要,它是使任何系统或流程自动化的过程,以自动执行复杂的任务和功能,从而实现最佳生产率。面向初学者的Java人工智能实践解释了使用流行的基于Java的库和框架来构建智能应用程......
  • JavaScript-快速语法参考-全-
    JavaScript快速语法参考(全)原文:JavaScriptQuickSyntaxReference协议:CCBY-NC-SA4.0一、使用JavaScript要开始试验JavaScript,您应该安装一个支持这种语言的集成开发环境(IDE)。有很多不错的选择,比如NetBeans、Eclipse、VisualStudio、括号。在本书中,我们将使用NetBe......
  • JavaScript-入门指南-全-
    JavaScript入门指南(全)原文:BeginningJavaScript协议:CCBY-NC-SA4.0一、JavaScript简介这些年来,avaScript发生了很大变化。我们目前正处于一个JavaScript库的时代,你可以构建任何你想构建的东西。JavaScript存在于客户机和服务器上,存在于桌面和移动设备上。这本书的目......
  • 工作一年多,准备重新缕一下Java全流程(JDK8和JDK17,搭建环境)
    在重新学习的过程中哥们会吧一些理解不深的有疑问的记录在此系列中有好兄弟想一起学习,可以一起打卡记录一下一搭建环境今天下载了一下jdk17,因为工作中用8所以配置了一些兼容性的东西给大伙分析一些首先我们可以去官网下载jdk17,下载的话走默认路径就可以JavaDownloads|......
  • 【Java 并发编程】(四) ThreadLocal 源码解读
     介绍每个Thread对象,内部有一个ThreadLocalMapthreadLocals,这是一个哈希表,底层是一个Node[]table;当在某个线程中调用ThreadLocal的set方法时,会使用Thread.currentThread获取当前先线程的thread对象,然后将ThreadLocal对象作为key,将set方法的参数作为value......
  • java学习第八周
    临近开学,本周的任务完成情况不够好,平常乱七八糟的事情比较多,所以放在学习上的心思比较少。平均每天放在JAVA学习的时间约1个小时,放在编程的时间约半小时,解决问题的时间约1小时。下一个星期就要开学了,回看自己暑期的JAVA学习情况感觉比之前的暑期有很大的进步,在家中能拿出大量的时......