精华内容
下载资源
问答
  • Java参数传递问题

    2015-07-22 18:46:06
    Java参数传递问题

    下面通过内存模型的方式来讨论一下Java中的参数传递。

    内存模型涉及到两种类型的内存:栈内存(stack)和堆内存(heap)。
    基本类型作为参数传递时,传递的是这个值的拷贝:

    public class ParameterTransfer {
        public static void main(String[] args) {
            int num = 30;
            System.out.println("调用add方法前num=" + num);
            add(num);
            System.out.println("调用add方法后num=" + num);
        }
    
        public static void add(int param) {
            param = 100;
        }
    }

    这段代码运行的结果如下:

    调用add方法前num=30
    调用add方法后num=30

    程序运行的结果也说明这一点,改变参数param的值,原值num不会改变。

    通过内存模型来分析:
    1、当执行了int num = 30;这句代码后,程序在栈内存中开辟了一块地址为AD8500的内存,里边放的值是30。
    2、执行到add()方法时,程序在栈内存中又开辟了一块地址为AD8600的内存,将num的值30传递进来,此时这块内存里边放的值是30,执行param = 100;后,AD8600中的值变成了100。
    3、 地址AD8600中用于存放param的值,和存放num的内存没有任何关系,无论\怎么改变param的值,实际改变的是地址为AD8600的内存中的值,而AD8500中的值并未改变,所以num的值也就没有改变。

    以上是基本类型参数的传递方式,下来我们讨论一下对象作为参数传递的方式:

    public class ParameterTransfer {
    
        public static void main(String[] args) {
            String[] array = new String[] {"huixin"};
            System.out.println("调用reset方法前array中的第0个元素的值是:" + array[0]);
            reset(array);
            System.out.println("调用reset方法后array中的第0个元素的值是:" + array[0]);
        }
    
        public static void reset(String[] param) {
            param[0] = "hello, world!";
        }
    }

    运行的结果如下:

    调用reset方法前array中的第0个元素的值是:huixin
    调用reset方法后array中的第0个元素的值是:hello, world!

    当对象作为参数传递时,传递的是对象的引用,也就是对象的地址。

    1、当程序执行了String[] array = new String[] {“huixin”}后,程序在栈内存中开辟了一块地址编号为AD9500内存空间,用于存放array[0]的引用地址,里边放的值是堆内存中的一个地址,假设为BE2500,可以理解为有一个指针指向了堆内存中的编号为BE2500的地址。堆内存中编号为BE2500的这个地址中存放的才是array[0]的值:huixin。
    2、当程序进入reset方法后,将array的值,也就是对象的引用BE2500传了进来。这时,程序在栈内存中又开辟了一块编号为AD9600的内存空间,里边放的值是传递过来的值,即AD9600。可以理解为栈内存中的编号为AD9600的内存中有一个指针,也指向了堆内存中编号为BE2500的内存地址。这样一来,栈内存AD9500和AD9600(即array[0]和param的值)都指向了编号为BE2500的堆内存。
    3、在reset方法中将param的值修改为hello, world!后,改变对象param的值实际上是改变param这个栈内存所指向的堆内存中的值。param这个对象在栈内存中的地址是AD9600,里边存放的值是BE2500,所以堆内存BE2500中的值就变成了hello,world!。程序放回main方法之后,堆内存BE2500中的值仍然为hello,world!,main方法中array[0]的值时,从栈内存中找到array[0]的值是BE2500,然后去堆内存中找编号为BE2500的内存,里边的值是hello,world!。所以main方法中打印出来的值就变成了hello,world!

    无论是基本类型作为参数传递,还是对象作为参数传递,实际上传递的都是值,只是值的的形式不用而已。第一个示例中用基本类型作为参数传递时,将栈内存中的值30传递到了add方法中。第二个示例中用对象作为参数传递时,将栈内存中的值BE2500传递到了reset方法中。当用对象作为参数传递时,真正的值是放在堆内存中的,传递的是栈内存中的值,而栈内存中存放的是堆内存的地址,所以传递的就是堆内存的地址。这就是它们的区别。
    (意思就是说:参数传递始终是传递栈内存中的值,而对于基本类型,其值就在栈内存中,而对于引用类型,其值存在于堆内存中,栈内存中只是存放其地址,因而,对于引用类型,改变方法参数,会改变原参数本身!!!)

    补充一下,在Java中,String是一个引用类型,但是在作为参数传递的时候表现出来的却是基本类型的特性,即在方法中改变了String类型的变量的值后,不会影响方法外的String变量的值。关于这个问题,可以参考如下两个地址:

    http://freej.blog.51cto.com/235241/168676
    http://dryr.blog.163.com/blog/static/58211013200802393317600/

    我觉得是这两篇文章中提到的两个原因导致的,一个是String实际上操作的是char[],可以理解为String是char[]的包装类。二是给String变量重新赋值后,实际上没有改变这个变量的值,而是重新new了一个String对象,改变了新对象的值,所以原来的String变量的值并没有改变。

    展开全文
  • java参数传递问题

    2016-11-05 13:17:01
    关于Java参数传递问题,我们在学校学习编程语言的时候会经常听老师说方法的参数传递包括值传递和引用传递,那么什么是值传递和引用传递呢?我的理解是引用传递只是值传递的一种特殊情况,看下面的代码: public ...

    关于Java参数传递的问题,我们在学校学习编程语言的时候会经常听老师说方法的参数传递包括值传递和引用传递,那么什么是值传递和引用传递呢?我的理解是引用传递只是值传递的一种特殊情况,看下面的代码:

    public static void main(String[] args){		
    		int c = 5;
    		int d = 6;
    		change(c,d);
    		System.out.println(c);
    		System.out.println(d);
    	}
    private static void change(int a,int b) {
    		a=1;
    		b=3;
    	}

    以上代码运行结果:5,6  为什么不是1,3 呢?这个就是因为值传递问题,因为实参传给形参的实际上是实参的一份copy,这份copy的改变不会影响实参的值,不明白?接着往下看

    public static void main(String[] args){
    		int a[] = new int[]{3,2,6,5};
    		change(a);
    		for(int i=0;i<a.length;i++){
    			System.out.println(a[i]);
    		}
    	}
    private static void change(int[] b) {
    		b[0]=1;
    	}
    上面的例子中,我们将数组a传递给change函数,这个函数只是简单地将第一个元素改为1,运行结果:1,2,6,5。也就是说原数组a的第一个值被修改了,为什么这里又被改变了呢?这里面a数组是将数组的引用传递给了形参b,换句话说,a和b都指向了同一块内存地址,就像你和你女票去吃饭一样(没有女票那就找个基友去吃吧),你将一双筷子传递给了你女票,但是你们吃饭的时候夹的都是同一个盘子里的菜,你吃了一块肥肉,那肥肉就到你肚子里了,那盘子(数组)里的菜(元素)是不是就改变了。所以说,引用传递实际上就是实参将地址传递给了形参(也就是我所理解的特殊的值传递)。那最上面说的值传递又怎么理解?很简单,你和你女票(基友)去吃饭,你打了一碗饭给你女票,你女票吃了你给她打的饭,但是你那碗饭并没有减少(如果你没有吃过的话)。接着往下看

    public static void main(String[] args){	
    		int a[] = new int[]{3,2,6,5};
    		change(a);
    		for(int i=0;i<a.length;i++){
    			System.out.println(a[i]);
    		}
    	}
    private static void change(int[] b) {
    		int[] c = new int[]{5,6,7,8,9};
    		b = c;
    	}
    上面的运行结果:3,2,6,5。为什么?还是你和女票(基友)去吃饭,女票想喝汤,你递了一个勺子给她,接到勺子之后她突然间不想喝了,反而拿起筷子吃饭了,那么汤还会有改变吗?当然不会,换句话说,b数组重新指向了c数组的地址。







    展开全文
  • Java 参数传递问题

    2009-06-29 23:46:10
    最基础的Java参数传递方式: 1。基本数据类型为值传递(注:String属于基本类型,不是Java的对象) 2。引用类型为地址传递,即:传递的是一个引用(就像其名字一样,引用类型,即:对象) 但如果在传递过程中...
    最基础的Java参数传递方式:

    1。基本数据类型为值传递(注:String属于基本类型,不是Java的对象)

    2。引用类型为地址传递,即:传递的是一个引用(就像其名字一样,引用类型,即:对象)

    但如果在传递过程中,如果引用又指向了其它的对象,则只保留在没改变指向时对原对象的成员

    变量的修改。如果在传递后直接就改变指向,则原对象的成员变量不会发生任何的更改。
    展开全文
  • JAVA参数传递问题

    2012-04-07 14:46:38
    问题: 1) 在main方法中,添加下面几行语句,分析结果 swap1(c1,c2); System.out.println("执行swap1(c1,c2)后"); System.out.println("c1引用的牌是:"+c1+"\tc2引用的牌是:"+c2); System.out.println("第...
  • 探讨Java参数传递问题

    2019-10-30 14:32:01
    探讨Java参数传递问题 前言: 可能很多人都知道参数有形参和实参之分,却不知道区别到底是什么;知道Java中内存分为栈、堆、方法区等5片内存,不知道每片内存中保存的都是什么;关于参数的传递到底是值传递还是...

    探讨Java参数传递问题

     

    前言:

    可能很多人都知道参数有形参和实参之分,却不知道区别到底是什么;知道Java中内存分为栈、堆、方法区等5片内存,不知道每片内存中保存的都是什么;关于参数的传递到底是值传递还是引用传递傻傻分不清楚。本文将为你逐一揭秘!

    一、形参和实参:

    • 形参:就是定义方法时,该方法携带的参数。比如定义如下方法:
    public static void test(String name){
       System.out.println(name);
    }
    

    test方法中的参数name就是形参,只有在test方法在被调用这个name的生命周期才开始,才会分配内存空间,当test方法调用完后,这个name也就不复存在。

    • 实参:方法在被调用时实际传入的参数值,实参在方法调用前就已经被初始化。看例子:
    public static void main(String[] args){
            String name = "刘亦菲";
            test(name);
    }
    

    这个String name = "刘亦菲"中这个name,在test方法被调用之前就就已被创建并且初始化,在调用test方法时,它就被当作实际参数传入,这就是实参。

    二、Java中的内存:

    Java中内存分为5片,分别是栈、堆、方法区、程序计数器、本地方法栈
    1、栈:
    又称虚拟机栈。特点是先进后出。栈的线程是私有的,也就是线程之间的栈是隔离的。栈中有若干栈帧,每个栈帧对应一个方法。也就是说,当程序开始执行一个方法时,就会在栈中创建一个栈帧入栈,方法结束后,该栈帧出栈。看下面的图解:

    栈与栈帧


    每个栈帧主要包括:

     

    • 局部变量表:存储方法中的局部变量。当局部变量是基本类型时,存储的是变量的值;当变量是引用类型时,存储的是地址值。
    • 运行时常量池的引用:存储程序执行时可能会用到的常量的引用。
    • 方法返回地址:存储方法执行完成后的返回地址。

    2、堆:
    堆内存用来存储对象和数组。数组以及所有new出来的对象都存储在堆内存中。在JVM中只有一个堆,所以堆是被所有线程共享的。

    3、方法区:
    方法区也是所有线程共享的区域,主要存储静态变量、常量池等。

    三、数据在内存中的存储:

    1、基本类型的存储:

    • 基本类型的局部变量:变量以及数值都是存储在栈内存中。比如在某个方法中定义有如下局部变量:
    int age = 6;
    int grade = 6;
    int weight = 50;
    

    先创建一个age变量,存储在栈帧中的局部变量表,然后查找栈中是否有字面量值为6的内容,如果有,直接把age指向这个地址,没有开辟内存空间来存储"6"这个内容,同时让age指向它。当创建grade变量时,因为已经有字面量为"6"的内容了,所以直接拿过来用。所以栈中的数据在当前线程下是共享的。上面的代码在内存中的图解如下:

     

     

     

    如果给age重新赋值:

    age = 10;
    

    难么就会在栈中查找是否有字面量为"10"的内容,有就直接拿来用,没有就开辟内存空间存储"10",然后age指向这个10。所以基本类型的变量,变量值本身是不会改变的,重新赋值后,只是指向了新的引用而已。

     

    重新赋值

    • 基本类型的成员变量:基本类型的成员变量的变量名和值都是存储在堆内存中的,其生命周期和对象是一致的。看下面的代码:
    public class User{
       private int age;
       private String name;
       private int grade;
       ......
    }
    

    调用:

    User user = new User();
    

    在内存中的存储图解:

     

    User的存储

    • 基本类型的静态变量:基本类型的静态变量存储于方法区的常量池中,随着类的加载而加载。

    2、引用类型的存储:
    通过上图可以发现,执行

    User user = new User();
    

    时分两个过程:

    User user;// 定义变量
    user = new User();// 赋值
    

    定义变量时,会在栈中开辟内存空间存放user变量;赋值时会在堆内存中开辟内存空间存储User实例,这个实例会有一个地址值,同时把这地址值赋给栈中的user变量。所以引用类型的变量名存储在栈中,变量值存储的是堆中相对应的地址值,并不是存储的实际内容。

    四、参数传递问题:

    关于参数的传递,可能有点难理解,到底是值传递还是引用传递?下面一起来学习一下:

    • 值传递:方法调用时,实际参数把它的值的副本传递给对应的形式参数,此时形参接收到的其实只是实参值的一个拷贝,所以在方法内对形参做任何操作都不会影响实参。看下面一段代码:
    public class Test {
        public static void test(int age,String name){
            System.out.println("传入的name:"+name);
            System.out.println("传入的age:"+age);
            age = 66;
            name = "张馨予";
            System.out.println("方法内重新赋值的name:"+name);
            System.out.println("方法内重新赋值的age:"+age);
        }
    
        public static void main(String[] args){
            String name = "刘亦菲";
            int age = 44;
            test(age,name);//调用方法
            System.out.println("方法执行后的name:"+name);
            System.out.println("方法执行后的age:"+age);
        }
    }
    

    执行结果如下:

     

    运行结果

    从结果可以发现,name和age在方法调用后并没有改变,所以传入方法的只是实参的拷贝。

    • 引用传递:当参数是对象的时候,其实传递的对象的地址值,所以实参的地址值传给形参后,在方法内对形参进行操作会直接影响真实内容。看下面的代码:
      定义对象:
    @Data
    public class User {
        private String name;
        private int age;
    }
    

    测试:

    public class Test {
        public static void userTest(User user){
            System.out.println("传入的user:"+user);
            user.setName("张馨予");
            user.setAge(20);
            System.out.println("方法内重新赋值的user:"+user);
        }
    
        public static void main(String[] args){
            User user = new User();
            user.setName("刘亦菲");
            user.setAge(18);
            userTest(user);//调用方法
            System.out.println("方法执行后的user:"+user);
        }
    }
    

    结果如下:

     

    第一次运行结果

     

    可以看到在方法内对user重新赋值,直接影响这个对象,所以方法执行完毕后输出的是修改后的user。

    对上面的测试方法稍作修改:

    public class Test {
        public static void userTest(User user){
            System.out.println("传入的user:"+user);
            user = new User();//新增这行代码
            user.setName("张馨予");
            user.setAge(20);
            System.out.println("方法内重新赋值的user:"+user);
        }
    
        public static void main(String[] args){
            User user = new User();
            user.setName("刘亦菲");
            user.setAge(18);
            userTest(user);//调用方法
            System.out.println("方法执行后的user:"+user);
        }
    }
    

    执行结果如下:

     

    第二次运行结果

     

    结果却是,方法执行后的user竟然没改变。

    分析一下这两次的执行过程:
    第一次:

     

    第一次执行过程

    第一次执行过程如上图,main方法进栈后,在堆中new了一个user对象x0001,然后调用userTest方法,userTest方法进栈,并且把user对象的地址值x0001传入userTest方法,所以在userTest方法中对user进行操作直接影响地址值为x0001的对象。所以就出现了第一次运行结果。

    第二次:

     

    第二次执行过程

     

    第二次执行过程如上图,main方法进栈后,在堆中new了一个user对象x0001,然后调用userTest方法,userTest方法进栈,并且把user对象的地址值x0001传入userTest方法,在此之前都是和第一次一样的。接下来在该方法中有

    user = new User();
    

    到了这里,又在堆中new了一个user对象x0002,然后让栈中user变量指向新的user对象的地址值x0002。所以接下来在方法中对user的操作都是对地址值为x0002的对象的操作,自然不会影响到地址值为x0001的对象。所以就出现了第二次的运行结果。

    • 小结:由上面的案例可以得出结论,基本类型传的是值的副本,引用类型传的是地址值,所以不论传的是引用类型还是基本类型,其实都是值传递。

    总结:

    本文介绍了形参与实参、Java中的内存以及各片内存主要存储哪些东西,最后讨论了一下参数传递问题。以上内容为个人理解,如果错误,欢迎批准指正!



    作者:贪挽懒月
    链接:https://www.jianshu.com/p/9bfdecc2ccb5
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    展开全文
  • 给出了基本类型和引用类型参数传递时行为的示例,并给出了String类型作为参数传递时表现出的和一般引用类型传递行为不同的原因使用基本类型进行参数传递时的代码示例public class Test1 {public static void main...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,497
精华内容 1,798
关键字:

java参数传递问题

java 订阅