精华内容
下载资源
问答
  • 终极解释: java方法传递参数的方式

    千次阅读 2015-11-30 22:07:50
    如果你已经搜索过这个问题了,那么你会很容易看到大批的答案都是“java参数传递方式是值传递”,除此之外还会解释一通什么是引用传递。那么,请思考一个问题,什么是值传递?什么是引用传递?请看下面一段C语言:#...
    如果你还对此问题不清楚,或者似懂非懂有些疑惑,请看下文,看完此文,保证不用再看其他文章。
    

    首先,我们来看下现有网上大多数文章对此问题是如何解释的。

    如果你已经搜索过这个问题了,那么你会很容易看到大批的答案都是“java参数的传递方式是值传递”,除此之外还会解释一通什么是引用传递。

    那么,请思考一个问题,什么是值传递?什么是引用传递?

    请看下面一段C语言:

    #include <stdio.h> 
    
    fun(int arg) {
        arg = arg + 2;
    }
    
    funAnother(int & arg) {
        *arg = *arg + 2;
    }
    
    main() 
    {
        int a = 1;
        printf(a); 
        fun(a);
        printf(a);
        funAnother(*a);
    } 
    

    fun函数直接传递值,通常被人们称之为值传递;funAnother中传递的是地址,通常被人们称作地址传递,人们将此概念带到面向对象的语言中就叫做引用传递。

    为了完全弄明白此文要阐述的问题,请你务必在此处停下来想清楚什么叫传递,程序语言中传递指的是什么?也许你从开始学习第一门语言就学着写函数或者方法并熟练的使用他们,你一开始没有思考函数和方法的调用是什么样的一个过程,后来你更加习惯于写函数和方法并且代码写的相当漂亮,你已经忘记了此时可能需要问这个问题,但是请你现在好好思考一下它,或者你还真不明白。

    如果你没有思考上面的问题,或者说你没有思考清楚就看到了这里,说明两点:

    1、
    你觉得这个问题不值得考虑,你已经对它很清楚的知道。
    
    2、
    你没有耐心去思考这个问题,你急于想指导答案
    
    3、
    你觉得我在瞎掰,扯这么多干什么╮(╯▽╰)╭
    

    如果不是1。
    是2,我要告诉你,这个问题设计编译器的相关知识和汇编语言相关知识,篇幅所限,我不会在这里解释,还请自行Google - -。
    是3,我现在就可以告诉你答案:java中参数传递方式既不能叫值传递,也不能叫引用传递。值传递和引用传递用在描述C++还差不多,描述java不贴切。

    下面我们通过几个例子来解释下我为什么这么说。

    String

    我们先看下String这个特殊类作为参数的传递(这里用String 举例):

        private String testString(String test) {
            System.out.println(test.hashCode());
            test = new String("234");
            return test;
          }
    
        String testStr = new String("123");
        String testStr1 = "123";
        String testStr2 = testStr;
        String testStr3 = testStr1;
        System.out.println(testStr.hashCode());
        System.out.println(testStr1.hashCode());
        System.out.println(testStr2.hashCode());
        System.out.println(testStr3.hashCode());
    
        System.out.println(testString(testStr).hashCode());
        System.out.println(testStr.hashCode());

    此处解释下,hashCode()在api中的解释大意是对该对象在内存地址中的直接或间接运算,地址相同该值一定相同,反之亦然。

    在我电脑上打印结果是这样的:

    1:48690
    2:48690
    3:48690
    4:48690
    inner:48690
    5:49683
    6:48690

    你会看编号为5的结果不一样。请你思考片刻在继续看。

    对于String,jvm对其处理是这样的:
    new String(“123”) 和 “123”对于jvm来说是一样处理的,首先看到这个之后,jvm会在内存中初始化一个常量,这块内存中的值就是字符串’1’, ‘2’, ‘3’,它们三个紧邻,jvm通过其他方式确定它们三个一起构成了一个变量的值,类型为字符串。你也许已经想到了,那么初始化在内存的那块呢,对了,这就是地址的概念,其实初始化到那块是不确定的,并且初始化到内存的哪块对我们来说都是不用关心的,我们只需要关心它的开始地址是哪里就行了,这样即使你需要关心这个字符串所占的内存,也可以计算出来它所跨区域的所有内存地址。另外,java中你其实并不需要知道确切的地址是多少,jvm有意对此进行了屏蔽。

    前面说到jvm会初始化一个常量的“123”在内存中,对于String这种类型,对它的特殊处理就在于以后你声明多少变量=”123”,它都不会在初始化一个“123”出来,jvm只会将第一次初始化的那个首地址赋值给你声明的变量,这就是为什么第1和2的打印结果相同的原因,至于第3为什么和第2相同、第4和第1相同,很好理解,不再多说。

    testString方法中有一个打印编号为inner,结果和外面的编号1、2打印相同,但是这里有不同的含义:

    首先在调用testString(testStr)的时候,jvm并不是直接把testStr的地址传递过来(此处需注意“testStr的值”和“testStr的地址”的区别,这两者只是为了表达问题的一种叙述方式,testStr的值指的是它所指内存中的保存的值;testStr的地址指的“本质上其实是testStr这个变量真是的值”这句话很容易误导人请了解编译原理相关知识,但是这句话与后文中的相关介绍很有关系如果可以还是请搞清楚,容易理解的说就是指变量所在的内存首地址。),而是将testStr的首地址赋值给了另一个变量test,所以此时test和testStr同时指向了同一块内存地址但是两者又是不同的。所以此处打印hashCode必然相同。

    但是请注意接下来一句test = new String(“234”),然后return test,紧接着外面打印了它的hashCode,也就是第5编号的打印,你会发现此时的值不同了。这也很好理解,new之后jvm重新初始化了一个字符串“234”,在内存的另一块,那么地址就不一样了。

    如果读者将方法里test = new String(“234”)改成 test = new String(“123”),试试结果又是什么?

    对于Integer、Double、Float、Boolean、Short等这些类对象作为参数传递跟String是一样的,自己测试一下。

    int

    对于int、double、short、float、byte、long、char、boolean这类基本数据类型作为参数传递,其实只是传递值而已,对于它们而言,本身就不用关心地址的概念。

    Object

    对于对象,你可以理解它在内存中其实是多个像String对象一样的嵌套。

    public class Person {
        protected String name;
    
        public void setName(String name)
        {
          this.name=name;
        }
    
        public String getName()
        {
          return this.name;
        }
      }
    

    类就像一个盒子,盒子里还可以有盒子,这就是上面说的类的嵌套的含义。Person类中定义了String类型的name属性,类里面定义了类。

    先不看Name属性,我们先看下面的代码:

        private Person test(Person person) {
            System.out.println("inner:" + person.hashCode());
            person = new Person();
            return person;
          }
    
        Person person = new Person();
        System.out.println("1:" + person.hashCode());
        System.out.println("2:" + test(person).hashCode());
    

    我这里运行结果如下:

    1:662441761
    inner:662441761
    2:1618212626

    没错,你会发现它和前面讲的String方式是一样的。

    再来验证一下,我们通过改变Name的值来验证是否如我们预期的和String一样:

        private Person test(Person person) {
        System.out.println("inner:" + person.getName());
        System.out.println("inner':" + person.getName().hashCode());
        person.setName("Adam");
        re
    
        Person person = new Person();
        person.setName("John");
        System.out.println("1:" + person.getName());
        System.out.println("1':" + person.getName().hashCode());
        System.out.println("2:" + test(person).getName());
        System.out.println("2':" + test(person).getName().hashCode());
        System.out.println("3:" + person.getName());
        System.out.println("3':" + person.getName().hashCode());

    运行结果:

    1:John
    1':2314539
    inner:John
    inner':2314539
    2:Adam
    inner:Adam
    inner':2035631
    2':2035631
    3:Adam
    3':2035631

    请思考一下结果为什么是这样?

    没错,当Person对象person作为参数传递给test方法的时候,jvm将person的地址拷贝了一份副本传了进去,所以前面没有考虑Name的代码中inner编号的hashcode是一样的,当在test内部调用setName的时候,改变的依然是原person对象所在内存中Name位置的值,所以里面更改,就改变了外面的对象的属性值。

    如果你足够仔细,估计你已经想到了一件事情,根据前面的测试,我们还不能够说明一点“jvm将person的地址拷贝了一份副本传了进去”,目前还不能确定是拷贝的副本还是它自己。

    下面继续验证,我们将test稍微修改一下:

        private Person test(Person person) {
        System.out.println("inner:" + person.getName());
        System.out.println("inner':" + person.getName().hashCode());
        person = new Person();
        person.setName("Adam");
        return person;
      }

    再次运行这段代码:

        Person person = new Person();
        person.setName("John");
        System.out.println("1:" + person.getName());
        System.out.println("1':" + person.getName().hashCode());
        System.out.println("2:" + test(person).getName());
        System.out.println("2':" + test(person).getName().hashCode());
        System.out.println("3:" + person.getName());
        System.out.println("3':" + person.getName().hashCode());

    结果如下:

    1:John
    1':2314539
    inner:John
    inner':2314539
    2:Adam
    inner:John
    inner':2314539
    2':2035631
    3:John
    3':2314539

    请看,此时的编号3和编号1的结果是一样的,这就证明了传进来的是对外面person对象首地址的一个拷贝,而不是它自己。

    在编程中使用最广泛的java集合类、JsonObject、jsonArray等这些类作为参数传递使用,要深刻理解java方法传递参数方式,以防使用错误导致数据不一致。

    再回过头来请思考一下开头说的,java方法参数传递方式是值传递还是引用传递?我觉得理解了本质就不用去回答到底是值传递还是引用传递的问题了,如果面试中被问到这样的问题,请反问面试官“请解释值传递和引用传递的概念,好让我回答这个问题”。

    展开全文
  • java中的参数传递(只有值传递没有引用传递

    万次阅读 多人点赞 2019-07-31 19:25:14
    所以在java方法中改变参数的值是不会改变原变量的值的,但为什么改变引用变量的属性值却可以呢?请看下面的解答。 java中的数据类型 Java中数据类型分为两大类:基本类型和引用类型。相应的,变量也分这两种类型:...

    Java中只有传值调用(值传递),没有传址调用(址传递或者引用传递)。所以在java方法中改变参数的值是不会改变原变量的值的,但为什么改变引用变量的属性值却可以呢?请看下面的解答。

    java中的数据类型

    Java中数据类型分为两大类:基本类型和引用类型。相应的,变量也分这两种类型:基本类型和引用类型。

    基本类型的变量保存原始值,即它代表的值就是数值本身;

    而引用类型的变量保存的值是引用值,"引用值"指向内存空间的地址,代表了某个对象的引用,而不是对象本身,对象本身存放在这个引用值所表示的地址的位置。

    基本类型包括:byte,short,int,long,char,float,double,Boolean,returnAddress。

    引用类型包括:类、接口类型和数组。

    java中只有值传递

    在日常编码中,会经常看到如下现象:

    1、对于基本类型参数,在方法体内对参数进行重新赋值,不会改变原有变量的值。

    2、对于引用类型参数,在方法体内对参数进行重新赋予引用,不会改变原有变量所持有的引用。

    3、方法体内对参数进行运算,不会改变原有变量的值。

    4、对于引用类型参数,方法体内对参数所指向对象的属性进行操作,将改变原有变量所指向对象的属性值。

    举个例子:

    public class Main {
    
        private static void getMiddleOne(boolean b, Boolean boo, Boolean[] arr){
    
            b = true;
    
            boo = new Boolean(true);
    
            arr[0] = true;
    
        }
    
           //测试
    
        public static void main(String[] args) {
    
            boolean b = false;
    
            Boolean boo = new Boolean(false);
    
            Boolean[] arr = new Boolean[]{false};
    
            getMiddleOne(b, boo, arr);
    
            System.out.println(b);
    
            System.out.println(boo.toString());
    
            System.out.println(arr[0]);
    
            /**
    
            * output:
    
            * false
    
            * false
    
            * true
    
            */
    
        }
    
    }

    我们只要了解了下面两点就可以解答上面的现象了:

    1、基本数据类型的值就是数值本身,所以示例中的b的值就是false;包装类因为会自动装箱拆箱,所以可以和基本类型一样处理,所以示例中boo的值就是false;数组是引用类型,所以arr的值就是指向该Boolean[]的引用。

    2、java中只有值传递没有引用传递,所以传入getMiddleOne方法的三个参数分别是b的值拷贝, boo的值拷贝, arr的值拷贝。

    通过上面两点就可以清楚了,getMiddleOne方法中执行的 b=true 和 boo = new Boolean(true) 都是把新值赋给了他们的拷贝,所以不改变原变量的值;同样,arr[0] = true 是把true复制给了arr的拷贝所指向的数组的第一个元素,arr的值和arr的拷贝的值都是该数组的引用,所以arr的拷贝所指向的数组和arr所指向的数组是同一个,所以改变arr的拷贝的数组的元素会同样影响到原变量arr。

    总结

    java中只有值传递,基本类型传递的是值的副本,引用类型传递的是引用的副本。

     

    展开全文
  • 探究Java方法参数传递是值传递还是引用传递

    测试思路

    每个更改形参的方法,返回值都是void,不同方法的参数设置不同类型。

    注意在方法内测地址的时候在改之前测一下,才能看出传入参数是不是传了地址。(注意反正OS的内存地址是虚拟的,JVM中的也是,掰扯不清的,所以就姑且按照JVM中的虚拟地址来考虑吧)

    数组参数传递

    private static void changeArray(int[] array) {
        Arrays.fill(array, 2);
    }
    

    数组的值改变了,但地址没变,是引用传递。

    基本类型及其包装类的参数传递

    private static void changeInt(int data) {
        data = 3;
    }
    
    private static void changeInteger(Integer data) {
        data = 3;
    }
    

    无论是int还是Integer,没有返回值,所以外面的实参值没变。

    对于基本类型,打印地址也能看出地址改了,是值传递。
    对于包装类型,打印地址能看出地址没改,是引用传递。

    由于hashCode()被重写了,所以可以用System.out.println(System.identityHashCode(data));来看虚拟地址(当然不是真的地址啦)。

    普通类参数传递

    private static class Person {
        int id;
        String name;
        int age;
        Person(int id, String name, int age) {
            this.id = id;
            this.name = name;
            this.age = age;
        }
        @Override
        public String toString() {
            return "Person{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}';
        }
    }
    
    private static void changePerson(Person data) {
        data = new Person(101, "Bob", 15);
    }
    

    是引用类型,也是引用传递。打印地址测试过,确实在new之前的虚拟地址与传入实参的虚拟地址一致。(想测地址就把@Override的toString()注释掉,再打印对象啊)

    只改属性

    private static void changePersonAttributes(Person data) {
        data.id = 101;
        data.name = "Bob";
        data.age = 15;
    }
    

    改了属性,打印结果改了,但是实际地址没改,说明是引用传递。

    完整代码

    部分打印地址的测试内容就删掉不附上了,感兴趣可以自测。

    import java.util.Arrays;
    
    public class ReferenceTest {
        private static void changeArray(int[] array) {
            Arrays.fill(array, 2);
        }
    
        private static void changeInt(int data) {
            data = 144;
        }
    
        private static void changeInteger(Integer data) {
            data = 155;
        }
    
        private static class Person {
            int id;
            String name;
            int age;
            Person(int id, String name, int age) {
                this.id = id;
                this.name = name;
                this.age = age;
            }
            @Override
            public String toString() {
                return "Person{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}';
            }
        }
    
        private static void changePerson(Person data) {
            data = new Person(101, "Bob", 15);
        }
    
        private static void changePersonAttributes(Person data) {
            data.id = 101;
            data.name = "Bob";
            data.age = 15;
        }
    
        public static void main(String[] args) {
            int[] array = new int[5];
            System.out.println(Arrays.toString(array));
            changeArray(array);
            System.out.println(Arrays.toString(array));
            Integer a = 133;
            changeInteger(a);
            System.out.println(a);
            changeInt(a);
            System.out.println(a);
            Person p = new Person(100, "Sam", 10);
            System.out.println(p);
            changePerson(p);
            System.out.println(p);
            changePersonAttributes(p);
            System.out.println(p);
        }
    }
    

    测试结果

    [0, 0, 0, 0, 0]
    [2, 2, 2, 2, 2]
    133
    133
    Person{id=100, name='Sam', age=10}
    Person{id=100, name='Sam', age=10}
    Person{id=101, name='Bob', age=15}
    

    总结反思

    其实自己也一直没搞明白Java的参数传递到底是值传递还是引用传递,网上说法众说纷纭,于是下定决心自己认真测一下。

    按照JVM的虚拟地址来做测试,在整数测试的时候还考虑了整数缓存池,选了127以上的数。

    个人认为,其实不能单纯利用返回值为void的函数运行后查看原值来判断是值传递还是引用传递。我选择在传完参数后的函数内测地址,地址一样就是引用传递,不一样就是值传递。
    hashCode()一般包含了地址,但Integer的hashCode()则只是原值,得使用System.identityHashCode(data)来测。

    emmm,接下来,讲讲结论orz

    总结

    • 基本类型是值传递
    • 引用类型是引用传递,返回值为void的话未必能在外面看到改变。
      • 引用类型对象重新被new了以后,看不到改变;但改变属性能看到改变。
      • 数组也是引用类型,数组元素值的改变能在void方法外面测出来。

    其实研究这个问题Real迷惑,如有不足还请指正!


    Update on 2020.3.7
    强调String这个问题,如果你简单地测试得到地址没变,这是不对的,涉及到String常量池等等……
    因为我还不太会,所以回头再来补充orz

    看来这篇文章可能有些我思虑不周(尚未涉猎)的地方啊,果然水深莫涉啊orz

    展开全文
  • Java方法传递参数是值传递,但是在传递对象的引用的时候,引用的值就是对象实际在堆内存的地址

    代码如下

    //dataPage对象包含了List<Statistictime>,此处想改变dataPage里List里Statistictime的内容
    //将job_state字段从code如yjs转换为name如"已结束"
    GroupJobService groupJobService = new GroupJobService();
    //获取code与name的映射如key="yjs",value="已结束"
    Map<String, String> typeMapping = groupJobService.getTypeMapping();
    List<Statistictime> list = dataPage.getList();
    String jobState = null;
    for (Statistictime statistictime : list) {
        jobState = statistictime.getStr("job_state");
        jobState = jobState!=null&&!"".equals(jobState.trim()) ? jobState.trim() : null;
        if(jobState!=null && typeMapping.containsKey(jobState)) {//typeMapping包含该key
            statistictime.set("job_state", typeMapping.get(jobState));
            statistictime.set("JOB_STATE", typeMapping.get(jobState));
        }
        jobState = null;
    }

    从上面代码可以看出,我们只是直接从dataPage获取相应的List,并作出数据处理后并没有将List放回dataPage里,但dataPage的内容已经改变了,以下的简单demo可以验证

    public class MyTest {
        @Test
        public void testIt() {
            A a = new A();
            a.setA(10);
            System.out.println(a.getA());
            go(a);
            System.out.println(a.getA());
        }
        public void go(A a) {
            a.setA(5);
        }
    }
    public class A{
        private int a;
        public void setA(int a){
            this.a=a;
        }
        public int getA(){
            return this.a;
        }
    }

    上面的代码的运行结果为:10,5
    从而验证了传递引用相当于传递指针的值,该值是对象在堆内存里的地址.

    展开全文
  • Java方法中通过传递参数改变变量的值
  • Java语言的方法调用只支持参数的值传递。当一个对象实例作为一个参数传递方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的。 那么如何...
  • JAVA 方法参数是按值传递还是引用传递
  • java main方法参数传递

    万次阅读 2017-06-22 14:35:01
    一般情况下 我们的main方法是不需要参数传入的 但是如果在工作中需要...首先main方法传递参数是String数组 我们直接打印这个数组是没有值得空数组 所以可以编辑其下的代码 package com; public class TestMain {
  • Java方法参数传递机制,值传递还是引用传递
  • java方法参数传递方式--按值传递、引用传递

    万次阅读 多人点赞 2017-12-25 21:33:28
    java方法参数传递方式有两种,按值传递和引用传递 1.按值传递
  • java 传递参数的两种方式

    万次阅读 多人点赞 2015-10-19 15:39:28
     值传递方法调用时,实际参数把它的值传递给对应的形式参数方法执行中形式参数值的改变不影响实际参 数的值。  引用传递:也称为传地址。方法调用时,实际参数的引用(地址,而不是参数的值)被传递方法中相...
  • Java方法中的参数传递方式

    千次阅读 2015-08-04 19:11:25
    Java语言中,参数传递时,一般存在两种参数传递的规则 1.按值传递(by value) 使用该方式传递参数,参数原始的值不发生变化 适用范围:八种基本数据类型和String *String也是传递
  • new BigDecimal(Double.toString(a))等同于new BigDecimal(a+"")吗?
  • Java 方法参数的值传递和引用传递

    千次阅读 2016-02-10 10:58:10
    Java 方法参数的值传递和引用传递Java中,实际上都是值传递,并不存在引用传递。人们所说的引用传递,实则为指针间地址的传递。要理解此,我们必须明白一个概念,形如Object o;这样的定义,实际上我们在定义一个...
  • java之给方法传递对象参数

    千次阅读 2016-03-29 01:20:29
    java中给方法传递对象参数,和传递数组一样,传递对象实际上是传递对象的引用。 包括package的用法。 javac Jpro\Circes3.java javac Jpro\Demo032902.java java Jpro.Demo032902
  • 1 java返回多参数方法 在编写程序时经常遇到从一个方法中返回多个有用信息的情况。例如查找一个数组中最大值与最小值,一般的做法是通过一个for循环,两个if判断找出最值。但是在找到有用信息后需要通过函数返回的...
  • Java方法参数传递Java swap函数)

    千次阅读 2012-10-12 16:16:48
    Java方法参数传递方式只有一种:值传递。值传递,就是将实际参数值的副本(复制品)传入方法内,而参数本身不会受到任何影响。  public class PrimitiveTransferTest  {  public static void swap(int a,...
  • java方法参数传递

    千次阅读 2018-07-14 14:43:17
    java方法参数传递方式只有一种:值传递; 对于基本类型来说,传递的是实参的副本(值传递),故在方法内修改传递进来的值并不会影响实参本身; 对于引用类型来说,传递进来的是引用变量的副本(也是值传递),...
  • 关于JAVA方法参数赋值与参数传递

    千次阅读 2016-03-08 21:04:36
    摘自:http://blog.sina.com.cn/s/blog_7d991ba301018xkd.html1、按值传递(by value)适用范围:8种基本数据类型、String对象特点:在内存中复制一份数据,把复制后的数据传递方法内部作用:在方法内部改变参数的值...
  • Java方法调用时传递参数问题

    千次阅读 2015-10-17 15:23:04
    我们都知道将参数传递方法时有两种,第一种是按值调用(called by value)表示方法接收的是调用者提供的值,第二种是按引用传递(called by reference)
  • JAVA方法参数的引用传递

    千次阅读 2016-07-29 10:35:26
    JAVA方法参数的引用传递 java参数传递有两种,分别是按值传递和按引用传递。按值传递不必多说,下面就说一下按引用传递。 当一个对象被当作参数传递到一个方法”,这就是所谓的按引用传递。 public class ...
  • Java方法参数按值传递的理解

    千次阅读 2017-02-28 22:37:40
    今天看到(Java核心计数)Java方法参数按值传递的时候,居然怎么都想不通,故而自己写代码尝试了理解了一下: 定义一个Person类: class Person { private double m_dAge; public Person(double age) { m_dAge...
  • java方法参数传递方式有两种,按值传递和引用传递 1.按值传递 参数类型是int,long等基本数据类型(八大基本数据类型),参数传递的过程采用值拷贝的方式 代码片段1: public class Test { public static void ...
  • 深入理解java方法调用时的参数传递

    千次阅读 2017-11-09 18:08:20
    深入理解java方法调用与参数传递,解决以下问题:Java方法调用是如何传递参数的?被调用方法对调用方法内的变量有什么影响?java能使用返回值void的中间方法对变量进行加工吗?为什么静态成员变量的改变影响是全局的...
  • java方法参数传递解惑

    千次阅读 2008-09-27 15:49:00
    我不记得自己是怎么学到的,但是我相信绝大部分从事java学习Java的人都一直这么以为一个小的技术问题: 在Java方法参数传递,对象是传递引用,基本数据类型是传递值。而且一直一来都似乎没有人提出过疑问。 直到...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 660,799
精华内容 264,319
关键字:

java方法传递参数

java 订阅