精华内容
下载资源
问答
  • java中的参数传递(只有值传递没有引用传递)

    万次阅读 多人点赞 2019-07-31 19:25:14
    Java中只有传值调用(值传递),没有传址调用(址传递或者引用传递)。所以在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值传递和引用传递详细说明

    千次阅读 2020-07-14 15:53:17
    学过Java基础的人都知道:值传递和引用传递是初次接触Java时的一个难点,有时候记得了语法却记不得怎么实际运用,有时候会的了运用却解释不出原理,而且坊间讨论的话题又是充满争议:有的论坛帖子说Java只有值传递,...

    本文旨在用最通俗的语言讲述最枯燥的基本知识

    学过Java基础的人都知道:值传递和引用传递是初次接触Java时的一个难点,有时候记得了语法却记不得怎么实际运用,有时候会的了运用却解释不出原理,而且坊间讨论的话题又是充满争议:有的论坛帖子说Java只有值传递,有的博客说两者皆有;这让人有点摸不着头脑,下面我们就这个话题做一些探讨,对书籍、对论坛博客的说法,做一次考证,以得出信得过的答案。

    其实,对于值传递和引用传递的语法和运用,百度一下,就能出来可观的解释和例子数目,或许你看一下例子好像就懂,但是当你参加面试,做一道这个知识点的笔试题时感觉自己会,胸有成熟的写了答案,却发现是错的,或者是你根本不会做。

    是什么原因?

    那是因为你对知识点没有了解透彻,只知道其皮毛。要熟读一个语法很简单,要理解一行代码也不难,但是能把学过的知识融会贯通,串联起来理解,那就是非常难了,在此,关于值传递和引用传递,小编会从以前学过的基础知识开始,从内存模型开始,一步步的引出值传递和引用传递的本质原理,故篇幅较长,知识点较多,望读者多有包涵。

    1. 形参与实参

    我们先来重温一组语法:

    1. 形参:方法被调用时需要传递进来的参数,如:func(int a)中的a,它只有在func被调用期间a才有意义,也就是会被分配内存空间,在方法func执行完成后,a就会被销毁释放空间,也就是不存在了

    2. 实参:方法被调用时是传入的实际值,它在方法被调用前就已经被初始化并且在方法被调用时传入。

    举个栗子:

    1public static void func(int a){
    2 a=20;
    3 System.out.println(a);
    4}
    5public static void main(String[] args) {
    6 int a=10;//变量
    7 func(a);
    8}
    

    例子中
    int a=10;中的a在被调用之前就已经创建并初始化,在调用func方法时,他被当做参数传入,所以这个a是实参。
    而func(int a)中的a只有在func被调用时它的生命周期才开始,而在func调用结束之后,它也随之被JVM释放掉,,所以这个a是形参。

    2. Java的数据类型

    所谓数据类型,是编程语言中对内存的一种抽象表达方式,我们知道程序是由代码文件和静态资源组成,在程序被运行前,这些代码存在在硬盘里,程序开始运行,这些代码会被转成计算机能识别的内容放到内存中被执行。
    因此

    数据类型实质上是用来定义编程语言中相同类型的数据的存储形式,也就是决定了如何将代表这些值的位存储到计算机的内存中。

    所以,数据在内存中的存储,是根据数据类型来划定存储形式和存储位置的。
    那么
    Java的数据类型有哪些?

    1. 基本类型:编程语言中内置的最小粒度的数据类型。它包括四大类八种类型:

    4种整数类型:byte、short、int、long
    2种浮点数类型:float、double
    1种字符类型:char
    1种布尔类型:boolean

    1. 引用类型:引用也叫句柄,引用类型,是编程语言中定义的在句柄中存放着实际内容所在地址的地址值的一种数据形式。它主要包括:


    接口
    数组

    有了数据类型,JVM对程序数据的管理就规范化了,不同的数据类型,它的存储形式和位置是不一样的,要想知道JVM是怎么存储各种类型的数据,就得先了解JVM的内存划分以及每部分的职能。

    3.JVM内存的划分及职能

    Java语言本身是不能操作内存的,它的一切都是交给JVM来管理和控制的,因此Java内存区域的划分也就是JVM的区域划分,在说JVM的内存划分之前,我们先来看一下Java程序的执行过程,如下图:

    640?wx_fmt=png


    有图可以看出:Java代码被编译器编译成字节码之后,JVM开辟一片内存空间(也叫运行时数据区),通过类加载器加到到运行时数据区来存储程序执行期间需要用到的数据和相关信息,在这个数据区中,它由以下几部分组成:

     

    1. 虚拟机栈

    2. 堆

    3. 程序计数器

    4. 方法区

    5. 本地方法栈

    我们接着来了解一下每部分的原理以及具体用来存储程序执行过程中的哪些数据。


    1. 虚拟机栈

    虚拟机栈是Java方法执行的内存模型,栈中存放着栈帧,每个栈帧分别对应一个被调用的方法,方法的调用过程对应栈帧在虚拟机中入栈到出栈的过程。

    栈是线程私有的,也就是线程之间的栈是隔离的;当程序中某个线程开始执行一个方法时就会相应的创建一个栈帧并且入栈(位于栈顶),在方法结束后,栈帧出栈。

    下图表示了一个Java栈的模型以及栈帧的组成:

    640?wx_fmt=png


    栈帧:是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。

     

    每个栈帧中包括:

    1. 局部变量表:用来存储方法中的局部变量(非静态变量、函数形参)。当变量为基本数据类型时,直接存储值,当变量为引用类型时,存储的是指向具体对象的引用。

    2. 操作数栈:Java虚拟机的解释执行引擎被称为"基于栈的执行引擎",其中所指的栈就是指操作数栈。

    3. 指向运行时常量池的引用:存储程序执行时可能用到常量的引用。

    4. 方法返回地址:存储方法执行完成后的返回地址。


    2. 堆:

    堆是用来存储对象本身和数组的,在JVM中只有一个堆,因此,堆是被所有线程共享的。


    3. 方法区:

    方法区是一块所有线程共享的内存逻辑区域,在JVM中只有一个方法区,用来存储一些线程可共享的内容,它是线程安全的,多个线程同时访问方法区中同一个内容时,只能有一个线程装载该数据,其它线程只能等待。

    方法区可存储的内容有:类的全路径名、类的直接超类的权全限定名、类的访问修饰符、类的类型(类或接口)、类的直接接口全限定名的有序列表、常量池(字段,方法信息,静态变量,类型引用(class))等。


    4. 本地方法栈:

    本地方法栈的功能和虚拟机栈是基本一致的,并且也是线程私有的,它们的区别在于虚拟机栈是为执行Java方法服务的,而本地方法栈是为执行本地方法服务的。

    有人会疑惑:什么是本地方法?为什么Java还要调用本地方法?


    5. 程序计数器:

    线程私有的。
    记录着当前线程所执行的字节码的行号指示器,在程序运行过程中,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、异常处理、线程恢复等基础功能都需要依赖计数器完成。


    4. 数据如何在内存中存储?

    从上面程序运行图我们可以看到,JVM在程序运行时的内存分配有三个地方:

    • 静态方法区

    • 常量区

    相应地,每个存储区域都有自己的内存分配策略:

    • 堆式:

    • 栈式

    • 静态

    我们已经知道:Java中的数据类型有基本数据类型和引用数据类型,那么这些数据的存储都使用哪一种策略呢?
    这里要分以下的情况进行探究:

    1. 基本数据类型的存储:

    • A. 基本数据类型的局部变量

    • B. 基本数据类型的成员变量

    • C. 基本数据类型的静态变量

    2. 引用数据类型的存储


    1. 基本数据类型的存储


    我们分别来研究一下:

    A.基本数据类型的局部变量

    1. 定义基本数据类型的局部变量以及数据都是直接存储在内存中的栈上,也就是前面说到的“虚拟机栈”,数据本身的值就是存储在栈空间里面。

      640?wx_fmt=png


      如上图,在方法内定义的变量直接存储在栈中,如

    1int age=50;
    2int weight=50;
    3int grade=6;
    

    当我们写“int age=50;”,其实是分为两步的:

    1int age;//定义变量
    2age=50;//赋值
    

    首先JVM创建一个名为age的变量,存于局部变量表中,然后去栈中查找是否存在有字面量值为50的内容,如果有就直接把age指向这个地址,如果没有,JVM会在栈中开辟一块空间来存储“50”这个内容,并且把age指向这个地址。因此我们可以知道:
    我们声明并初始化基本数据类型的局部变量时,变量名以及字面量值都是存储在栈中,而且是真实的内容。

    我们再来看“int weight=50;”,按照刚才的思路:字面量为50的内容在栈中已经存在,因此weight是直接指向这个地址的。由此可见:栈中的数据在当前线程下是共享的

    那么如果再执行下面的代码呢?

    1weight=40;
    

    当代码中重新给weight变量进行赋值时,JVM会去栈中寻找字面量为40的内容,发现没有,就会开辟一块内存空间存储40这个内容,并且把weight指向这个地址。由此可知:

    基本数据类型的数据本身是不会改变的,当局部变量重新赋值时,并不是在内存中改变字面量内容,而是重新在栈中寻找已存在的相同的数据,若栈中不存在,则重新开辟内存存新数据,并且把要重新赋值的局部变量的引用指向新数据所在地址。


    B. 基本数据类型的成员变量

    成员变量:顾名思义,就是在类体中定义的变量。
    看下图:

    640?wx_fmt=png

     

    我们看per的地址指向的是堆内存中的一块区域,我们来还原一下代码:

     1public class Person{
     2  private int age;
     3  private String name;
     4  private int grade;
     5//篇幅较长,省略setter getter方法
     6  static void run(){
     7     System.out.println("run...."); 
     8   };
     9}
    10
    11//调用
    12Person per=new Person();
    

    同样是局部变量的age、name、grade却被存储到了堆中为per对象开辟的一块空间中。因此可知:基本数据类型的成员变量名和值都存储于堆中,其生命周期和对象的是一致的。


    C. 基本数据类型的静态变量

    前面提到方法区用来存储一些共享数据,因此基本数据类型的静态变量名以及值存储于方法区的运行时常量池中,静态变量随类加载而加载,随类消失而消失


    2. 引用数据类型的存储:

    上面提到:堆是用来存储对象本身和数组,而引用(句柄)存放的是实际内容的地址值,因此通过上面的程序运行图,也可以看出,当我们定义一个对象时

    1Person per=new Person();
    

    实际上,它也是有两个过程:

    1Person per;//定义变量
    2per=new Person();//赋值
    

    在执行Person per;时,JVM先在虚拟机栈中的变量表中开辟一块内存存放per变量,在执行per=new Person()时,JVM会创建一个Person类的实例对象并在堆中开辟一块内存存储这个实例,同时把实例的地址值赋值给per变量。因此可见:
    对于引用数据类型的对象/数组,变量名存在栈中,变量值存储的是对象的地址,并不是对象的实际内容。

    6. 值传递和引用传递

    前面已经介绍过形参和实参,也介绍了数据类型以及数据在内存中的存储形式,接下来,就是文章的主题:值传递和引用的传递。

    值传递:
    在方法被调用时,实参通过形参把它的内容副本传入方法内部,此时形参接收到的内容是实参值的一个拷贝,因此在方法内对形参的任何操作,都仅仅是对这个副本的操作,不影响原始值的内容。

    来看个例子:

     1public static void valueCrossTest(int age,float weight){
     2    System.out.println("传入的age:"+age);
     3    System.out.println("传入的weight:"+weight);
     4    age=33;
     5    weight=89.5f;
     6    System.out.println("方法内重新赋值后的age:"+age);
     7    System.out.println("方法内重新赋值后的weight:"+weight);
     8    }
     9
    10//测试
    11public static void main(String[] args) {
    12        int a=25;
    13        float w=77.5f;
    14        valueCrossTest(a,w);
    15        System.out.println("方法执行后的age:"+a);
    16        System.out.println("方法执行后的weight:"+w);
    17}
    

    输出结果:

    1传入的age:25
    2传入的weight:77.5
    3
    4方法内重新赋值后的age:33
    5方法内重新赋值后的weight:89.5
    6
    7方法执行后的age:25
    8方法执行后的weight:77.5
    

    从上面的打印结果可以看到:
    a和w作为实参传入valueCrossTest之后,无论在方法内做了什么操作,最终a和w都没变化。

    这是什么造型呢?!!

    下面我们根据上面学到的知识点,进行详细的分析:

    首先程序运行时,调用mian()方法,此时JVM为main()方法往虚拟机栈中压入一个栈帧,即为当前栈帧,用来存放main()中的局部变量表(包括参数)、操作栈、方法出口等信息,如a和w都是mian()方法中的局部变量,因此可以断定,a和w是躺着mian方法所在的栈帧中
    如图:

    640?wx_fmt=jpeg


    而当执行到valueCrossTest()方法时,JVM也为其往虚拟机栈中压入一个栈,即为当前栈帧,用来存放valueCrossTest()中的局部变量等信息,因此age和weight是躺着valueCrossTest方法所在的栈帧中,而他们的值是从a和w的值copy了一份副本而得,如图:

    640?wx_fmt=png

    因而可以a和age、w和weight对应的内容是不一致的,所以当在方法内重新赋值时,实际流程如图:

    640?wx_fmt=jpeg

    也就是说,age和weight的改动,只是改变了当前栈帧(valueCrossTest方法所在栈帧)里的内容,当方法执行结束之后,这些局部变量都会被销毁,mian方法所在栈帧重新回到栈顶,成为当前栈帧,再次输出a和w时,依然是初始化时的内容。
    因此:
    值传递传递的是真实内容的一个副本,对副本的操作不影响原内容,也就是形参怎么变化,不会影响实参对应的内容。

     

    引用传递:
    ”引用”也就是指向真实内容的地址值,在方法调用时,实参的地址通过方法调用被传递给相应的形参,在方法体内,形参和实参指向通愉快内存地址,对形参的操作会影响的真实内容。

    举个栗子:
    先定义一个对象:

     1public class Person {
     2        private String name;
     3        private int age;
     4
     5        public String getName() {
     6            return name;
     7        }
     8        public void setName(String name) {
     9            this.name = name;
    10        }
    11        public int getAge() {
    12            return age;
    13        }
    14        public void setAge(int age) {
    15            this.age = age;
    16        }
    17}
    

    我们写个函数测试一下:

     1public static void PersonCrossTest(Person person){
     2        System.out.println("传入的person的name:"+person.getName());
     3        person.setName("我是张小龙");
     4        System.out.println("方法内重新赋值后的name:"+person.getName());
     5    }
     6//测试
     7public static void main(String[] args) {
     8        Person p=new Person();
     9        p.setName("我是马化腾");
    10        p.setAge(45);
    11        PersonCrossTest(p);
    12        System.out.println("方法执行后的name:"+p.getName());
    13}
    

    输出结果:

    1传入的person的name:我是马化腾
    2方法内重新赋值后的name:我是张小龙
    3方法执行后的name:我是张小龙
    

    可以看出,person经过personCrossTest()方法的执行之后,内容发生了改变,这印证了上面所说的“引用传递”,对形参的操作,改变了实际对象的内容。

    那么,到这里就结题了吗?
    不是的,没那么简单,
    能看得到想要的效果
    是因为刚好选对了例子而已!!!

    下面我们对上面的例子稍作修改,加上一行代码,

    1public static void PersonCrossTest(Person person){
    2        System.out.println("传入的person的name:"+person.getName());
    3        person=new Person();//加多此行代码
    4        person.setName("我是张小龙");
    5        System.out.println("方法内重新赋值后的name:"+person.getName());
    6    }
    

    输出结果:

    1传入的person的name:我是马化腾
    2方法内重新赋值后的name:我是张小龙
    3方法执行后的name:我是马化腾
    

    `
    为什么这次的输出和上次的不一样了呢?
    看出什么问题了吗?

    按照上面讲到JVM内存模型可以知道,对象和数组是存储在Java堆区的,而且堆区是共享的,因此程序执行到main()方法中的下列代码时

    1Person p=new Person();
    2        p.setName("我是马化腾");
    3        p.setAge(45);
    4        PersonCrossTest(p);
    

    JVM会在堆内开辟一块内存,用来存储p对象的所有内容,同时在main()方法所在线程的栈区中创建一个引用p存储堆区中p对象的真实地址,如图:

    640?wx_fmt=png


    当执行到PersonCrossTest()方法时,因为方法内有这么一行代码:

    1person=new Person();
    

    JVM需要在堆内另外开辟一块内存来存储new Person(),假如地址为“xo3333”,那此时形参person指向了这个地址,假如真的是引用传递,那么由上面讲到:引用传递中形参实参指向同一个对象,形参的操作会改变实参对象的改变

    可以推出:实参也应该指向了新创建的person对象的地址,所以在执行PersonCrossTest()结束之后,最终输出的应该是后面创建的对象内容。

    然而实际上,最终的输出结果却跟我们推测的不一样,最终输出的仍然是一开始创建的对象的内容。

    由此可见:引用传递,在Java中并不存在。

    但是有人会疑问:为什么第一个例子中,在方法内修改了形参的内容,会导致原始对象的内容发生改变呢?

    这是因为:无论是基本类型和是引用类型,在实参传入形参时,都是值传递,也就是说传递的都是一个副本,而不是内容本身。

    640?wx_fmt=png

     

    有图可以看出,方法内的形参person和实参p并无实质关联,它只是由p处copy了一份指向对象的地址,此时:

    p和person都是指向同一个对象

    因此在第一个例子中,对形参p的操作,会影响到实参对应的对象内容。而在第二个例子中,当执行到new Person()之后,JVM在堆内开辟一块空间存储新对象,并且把person改成指向新对象的地址,此时:

    p依旧是指向旧的对象,person指向新对象的地址。

    所以此时对person的操作,实际上是对新对象的操作,于实参p中对应的对象毫无关系

    结语

    因此可见:在Java中所有的参数传递,不管基本类型还是引用类型,都是值传递,或者说是副本传递。
    只是在传递过程中:

    如果是对基本数据类型的数据进行操作,由于原始内容和副本都是存储实际值,并且是在不同的栈区,因此形参的操作,不影响原始内容。

    如果是对引用类型的数据进行操作,分两种情况,一种是形参和实参保持指向同一个对象地址,则形参的操作,会影响实参指向的对象的内容。一种是形参被改动指向新的对象地址(如重新赋值引用),则形参的操作,不会影响实参指向的对象的内容。

    原文转至:https://blog.csdn.net/bntx2jsqfehy7/article/details/83508006

    展开全文
  • Java值传递

    千次阅读 2016-01-31 17:18:26
    java中值传递,我的理解:一个参数A,传递A本身内容给变量B,在使用过程中若B直接改变A传递过来的内容,则A就会变化,否则不会变化。 而对于A本身内容,若是基本类型,则传递本身;若是对象,则传递他指向的地址。...

    本人拙见,大家共同学习,有问题还望指正


    java中值传递,我的理解:

    对于对象,则传递对象引用地址,所有在此地址上的修改,凡是指向改地址的引用都会发生改变,即改变了传递的参数。

    对于基本类型和不可变对象例如String,本身是不可变的,无法对其进行直接修改,所以不会改变传递的参数

    简单的代码说明

    <pre name="code" class="java">public class Test{
    	
    	public static void main(String[] args){
    		Test test = new Test();
    		// 声明并初始化两个对象,用于测试
    		MyObject myObject1 = new MyObject();
    		myObject1.set(1,"I'm myObject1!");
    		MyObject myObject2 = new MyObject();
    		myObject2.set(2,"I'm myObject2!");
    
    		System.out.println("初始对象值为:");
    		System.out.println("myObject1 : " + myObject1.toString());
    		System.out.println("myObject2 : " + myObject2.toString());
    		System.out.println();
    
    		// 交换对象值,查看结果
    		test.swapObject(myObject1,myObject2);
    		System.out.println("交换后对象值为:");
    		System.out.println("myObject1 : " + myObject1.toString());
    		System.out.println("myObject2 : " + myObject2.toString());
    		System.out.println();		
    
    		// 改变对象值,查看结果
    		test.changeObject(myObject1);
    		System.out.println("改变后对象值为:");
    		System.out.println("myObject1 : " + myObject1.toString());
    		System.out.println();
    
    		int a = 1;
    		int b = 2;
    		System.out.println("初始值为:");
    		System.out.println("a : " + a);
    		System.out.println("b : " + b);
    		System.out.println();
    
    		// 交换
    		test.swapInt(a, b);
    		System.out.println("交换后值为:");
    		System.out.println("a : " + a);
    		System.out.println("b : " + b);
    		System.out.println();
    
    		test.changeInt(a);
    		System.out.println("交换后值为:");
    		System.out.println("a : " + a);
    	}
    
    
    	public void swapObject(MyObject myObject1, MyObject myObject2){
    		MyObject temp = myObject1;
    		myObject1 = myObject2;
    		myObject2 = temp;
    	}
    
    	public void changeObject(MyObject myObject){
    		myObject.id = 1000;
    		myObject.decription = "I'm changed!";
    	}
    
    	public void swapInt(int a, int b){
    		int temp = a;
    		a = b;
    		b = temp;
    	}
    
    	public void changeInt(int a){
    		a = 1000;
    	}
    }
    
    // 简单对象
    class MyObject{
    
    	public int id;
    
    	public String decription;
    
    	public void set(int id, String decription){
    		this.id = id;
    		this.decription = decription;
    	}
    
    	// 重写toString方法,打印类的所有属性
    	@Override
    	public String toString(){
    		return "MyObject [id=" + id + ", decription=" + decription + "]";
    	}
    
    }

     
    

    执行结果为

    初始对象值为:
    myObject1 : MyObject [id=1, decription=I'm myObject1!]
    myObject2 : MyObject [id=2, decription=I'm myObject2!]
    
    交换后对象值为:
    myObject1 : MyObject [id=1, decription=I'm myObject1!]
    myObject2 : MyObject [id=2, decription=I'm myObject2!]
    
    改变后对象值为:
    myObject1 : MyObject [id=1000, decription=I'm changed!]
    
    初始值为:
    a : 1
    b : 2
    
    交换后值为:
    a : 1
    b : 2
    
    交换后值为:
    a : 1

    在对象交换中,没有对本身的值进行改变,并且在方法中只是在对B的指向地址做了改变,跟A没有多大关系

    在对象改变中,B直接对A传递的内容进行了更改,这里是直接把myObject1的属性做了修改

    在基本类型中,两种都没有直接对内容的改变,从始至终1还是1,没有改变,而且a始终指向的是1

    展开全文
  • Java值传递,引用传递,数组的传递

    千次阅读 2017-08-12 10:48:44
    本文在转载了别人文章的基础上又加上了自己的...数组若传递的是引用则会改变原来数组中的值,若传递的是单个值则是值传递不会改变原来数组的值 与其他语言不同,Java不允许程序员选择按值传递还是按引用传递各个参数

     转载于:http://blog.csdn.net/niuniu20008/article/details/2953785

    本文在转载了别人文章的基础上又加上了自己的理解。

    总结:只有引用类型可以改变原数据,值引用不会改变原来的数据。数组若传递的是引用则会改变原来数组中的值,若传递的是单个值则是值传递,不会改变原来数组的值

    与其他语言不同,Java不允许程序员选择按值传递还是按引用传递各个参数,基本类型(byte--short--int--long--float--double--boolean--char)的变量总是按值传递。就对象而言,不是将对象本身传递给方法,而是将对象的的引用或者说对象的首地址传递给方法,引用本身是按值传递的-----------也就是说,讲引用的副本传递给方法(副本就是说明对象此时有两个引用了),通过对象的引用,方法可以直接操作该对象(当操作该对象时才能改变该对象,而操作引用时源对象是没有改变的)。

     

    现在说说数组:如果将单个基本类型数组的元素传递给方法,并在方法中对其进行修改,则在被调用方法结束执行时,该元素中存储的并不是修改后的值,因为这种元素是按值传递,如果传递的是数组的引用,则对数组元素的后续修改可以在原始数组中反映出来(因为数组本身就是个对象,int[] a = new int[2];,这里面的int是数组元素的类型,而数组元素的修改是操作对象)。

     

    对于单个非基本类型数组的元素在方法中修改,则在被调用方法结束执行时,该元素中存储的是修改后的值,因为这种元素是按引用传递的,对象的改动将在源数组的数组元素中反映出来。


    看个小程序:

    public class Test{
        
        String str = new String("good");
        char[] ch = {'a','b','c'};
        int i = 10;
        public void change(String str,char[] ch,int i){
        
            str = "test ok";
            ch[0] = 'g';
            i++;    
        }
        
        public static void main(String[] args){
        
            Test tt = new Test();
            tt.change(tt.str,tt.ch,tt.i);
            System.out.println(tt.i);
            System.out.print(tt.str+" and ");
            System.out.println(tt.ch);     
        }
    }

    str是String类型的引用,i是基本类型变量,ch是数组名,也是数组对象的引用

    在chang()方法里,str="test ok",是一个新的对象把首地址放在引用变量str上;就是说在chang()方法中,str指向的是"test ok"的首地址,但原来的str还是指向的是原来的地址。

    而ch[0]='g';因为传的是数组的引用,而此时ch[0]='g';是对数组元素的操作,能修改源数组的内容;

    i是整型值,只是把值copy了一份给方法,在方法的变化是不改变的源i的。

    所以结果是:

    10

    good and gbc

    现在咱们把代码变化一下:

    public class Test{
        
        String str = new String("good");
        char[] ch = {'a','b','c'};
        int i = 10;
        public void change(String str,char ch,int i){
        
            str = "test ok";
            ch = 'g';
            this.i = i+1;    
        }
        
        public static void main(String[] args){
        
            Test tt = new Test();
            tt.change(tt.str,tt.ch[0],tt.i);
            System.out.println(tt.i);
            System.out.print(tt.str+" and ");
            System.out.println(tt.ch);     
        }
    }

    仔细观察下实参以及入参有何变化?

    change()方法里的入参char[] ch变成--------------char ch;

    这次传递的是个char值的单个数组元素,按照上面的解析,此时ch='9';是不影响源数组元素的。

    this.i = i+1;这里面等号左边的i是属性i,等号右边的i是局部变量(入参里的i);

    此时i+1后赋值给属性的i,自然会改变属性i的值,同时17行,tt.i又是调用属性的i,这次的结果是:

     

    11

    good and abc


     

    现在是不是有点明白了?

    那好再看下面一个小程序

    public class Test{
        
        public void change(StringBuffer x,StringBuffer y){
            
            x.append(y);
            y=x;    
        }
        public static void main(String[] args){
        
            StringBuffer buffA = new StringBuffer("a");
            StringBuffer buffB = new StringBuffer("b");
            new Test().change(buffA,buffB);
            System.out.println(buffA+","+buffB);   
        }
    }

    这次传递的是两个对象的引用的值,

    在方法change()里 的x.append(y),     其中引用x调用api方法append()修改了new StringBuffer("a");的内容。

     y=x;是一个修改内容的对象把首地址赋值给引用变量y了,此时操作的是引用,但只在方法中为x的引用,而先前y是new StringBuffer("b");的引用变量,所以在main方法中输出结果是:

    ab,b

    下面是个稍难的小程序,先自己用笔画画过程,写出自己的结果,而后再上机操作下,如果自己的结果和在电脑上的结果一样,那么再碰到这类题就不难了

    我还是把结果贴出来吧,先看程序:

    public class Test{
        
        private String nn = new String("1");
        private String[] mm = {"2","5"};
        
        void test(String nn,String[] mm){
            
            nn = new String("3");
            this.nn = "9";
            
            mm[0] = "4";
            System.out.println("in test(),mm[0]: "+mm[0]);
            mm = new String[]{"8","7"};
            System.out.println("in test(),nn: "+nn);
            System.out.println("this.nn: "+this.nn);
            System.out.println("mm[0]: "+mm[0]);
        }
        
        public static void main(String[] args){
            
            Test s = new Test();
            s.test(s.nn,s.mm);
            System.out.println(s.nn+"  "+s.mm[0]);
        }
    }

    分析:1.test()方法中的参数传递的都是引用类型,nn=new String("3"); 在内存中给“3”新分配了内存空间,并将nn指向它的首地址,所以在输出"in test(),nn="时结果为:3

    2.this.nn ="9" 等号左边的nn是属性,因为使用了this关键字,this指的就是类本身,所以这时候类的属性nn引用指向的地址中结果就从“1”变成了“9”。

    3.mm[0]="4",因为参数中的数组传递的也是引用,所以这时候改变数组中的值是会返回到源数组中的,所以无论是“in test(),mm[0]=”还是main方法中的s.mm[0]结果都为4

    4.mm = new String[]{"8","7"}跟1同理,在test()方法中mm指向了新空间的首地址,但出了方法,mm仍然指向的源数组的地址。

    结果如下:

    in test(),mm[0]: 4

    in test(),nn: 3

    this.nn: 9

    mm[0]: 8

    9和4


     


    展开全文
  • 关于java值传递的问题

    2015-04-09 12:59:42
    怎么来理解java中的值传递,为什么被传递的对象的之不会改变
  • Java值传递还是引用传递

    万次阅读 多人点赞 2019-03-20 02:40:16
    最近整理面试题,整理到值传递、引用传递,到网上搜了一圈,争议很大。带着一脸蒙圈,线上线下查了好多资料。最终有所收获,所以分享给大家,希望能对...当然如果针对 Java 语言本身来讲,Java 中只有 值传递,没有...
  • Java 值传递 & 引用传递 引发的思考

    千次阅读 多人点赞 2019-12-21 20:57:24
    这是一篇正经又带点幽默的文章,希望各位观众老爷们喜欢。 Java 到底是值传递还是引用传递,这是一个问题。 本文先由概念出发,穿插Java内存结构,最后举例探讨该问题。
  • Java值传递和引用传递

    千次阅读 2017-11-10 10:57:10
    Java中传递的方式有两种,第一种是值传递,第二种是引用传递
  • Java值传递与引用传递

    万次阅读 2011-10-26 13:30:28
    Java面试题:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 答案基本上是:值传递说明:得出这种结论的前提必须是“参数的值就是对...
  • java值传递

    千次阅读 2013-09-24 23:53:37
    Java使用按值传递的函数调用方式,这往往使我感到迷惑。因为在基础数据类型和对象的传递上,我就会纠结于到底是按值传递,还是按引用传递。其实经过学习,Java在任何地方,都一直发挥着按值传递的本色。   首先,...
  • Java方法中通过传递参数改变变量的
  • java 值传递及地址传递(引用传递)

    千次阅读 2014-09-21 19:17:05
    一直来觉得对值传递和地址传递了解的很清楚,刚才在开源中国上看到一篇帖子介绍了java中的值传递和地址传递,看完后感受颇深。下边总结下以便更容易理解。  按照以前的理解,java中基本数据类型是值传递,对象是...
  • java学习——java值传递和按址传递

    千次阅读 2017-02-09 14:17:27
    java中的按值传递和按址传递(按引用传递),要明白这两个概念,要理解按值和按址。
  • Java 值传递和对象传递详解

    千次阅读 2016-05-12 17:46:04
    堆和栈:Java中基本数据类型的和对象的引用保存在栈中,具体对象保存在堆中。传递原理: 一个方法传递的参数如果是基本数据类型,则是对具体的拷贝;如果是对象数据类型,则是对对象引用地址的拷贝,而非...
  • java String 值传递 还是引用传递

    万次阅读 2017-07-28 10:56:44
    值传递与引用传递 ...首先要说明的是java中是没有指针的,java中只存在值传递,只存在值传递!!! 然而我们经常看到对于对象(数组,类,接口)的传递似乎有点像引用传递,可以改变对象中某个属性的值。但是不
  • java值传递,引用传递,数组传递

    千次阅读 2014-10-14 13:03:07
    Java关于值传递的问题, 基本数据类型和子
  • 值传递 值传递,像int,float之类的简单类型进行的是值传递 public static void main(String[] args) { int i = 900; System.out.println(i); changeInt(i); System.out.println(i); } public ...
  • 五分钟学JavaJava到底是值传递还是引用传递?

    千次阅读 多人点赞 2019-12-11 08:43:27
    在逛 Stack Overflow 的时候,发现了一些访问量像阿尔卑斯山一样高的问题,比如说这个:Java 到底是值传递还是引用传递?访问量足足有 188万+,这不得了啊!说明有很多很多的程序员被这个问题困扰过。实话实说吧,我...
  • C++,Java值传递,引用传递

    千次阅读 2015-03-06 10:12:00
    1.除内置类型、STL迭代器和函数对象采用“pass-by-value”(值传递),其他...(1):“在Java里面参数传递都是按值传递”这句话的意思是:按值传递是传递的值的拷贝,按引用传递其实传递的是引用的地址值,所以统称按值
  • 深入理解--Java值传递和按引用传递

    万次阅读 多人点赞 2017-07-20 15:48:49
    引言最近刷牛客网上的题目时碰到不少有关Java值传递和按引用传递的问题,这种题目就是坑呀,在做错了n次之后,查找了多方资料进行总结既可以让自己在总结中得到提高,又可以让其他人少走弯路。何乐而不为?Java按...
  • JAVA 值传递(副本传递)的理解

    千次阅读 2015-03-13 11:18:26
    Java参数,不管是原始类型还是引用类型,传递的都是副本(有另外一种说法是传值,但是说传副本更好理解吧,传值通常是...如果参数类型是引用类型,那么传过来的就是这个引用参数的副本(地址值,本质上是按值传递),这
  • JAVA值传递和引用传递

    千次阅读 2020-11-09 14:29:27
    JAVA值传递和引用传递 我们先来看一下值传递和引用传递的定义; 值传递(pass by value):在调用函数时,将实际参数复制一份传递到函数中,这样在函数中对参数进行修改,就不会影响到原来的实际参数; 引用传递...
  • 为什么Java只有值传递

    2021-01-20 02:08:46
    值传递:是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。 引用传递:是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的...
  • JAVA参数传递方式 (按值传递与引用传递区别)

    千次阅读 多人点赞 2018-09-30 22:58:30
    首先要明确的是JAVA中没有引用传递, 全部是按调用 令大家所费解的 当对象引用作为参数时 函数为什么能修改真实的对象呢?这不是引用传递的特征吗? 尤其先学习C++再学习JAVA的同学(比如说我自己)会这样认为, 用...
  • java值传递or引用传递解惑

    千次阅读 2014-06-13 14:32:23
    java中的参数传递本质上
  •  值传递的特点:不管方法内部对传进去的参数作任何改变,也不会影响方法外部的变量的值  引用传递的特点:方法内部对传进去的参数作的改变也会影响方法外部的变量的值  那么哪些是值传递,哪些是引用传递呢...
  • JAVA是引用传递还是值传递

    千次阅读 2017-10-19 11:34:42
    那么java到底是值传递还是引用传递呢? 对于初学者或者说没有仔细思考过的同学来说这个概念即使知道了也没有很明确的答案!(ps:哈哈 至少我当初就是这样的 ,现在想想写下来和大家交流学习学习) 举个例子...
  • java数据传递方式

    万次阅读 2020-10-26 14:07:17
    值传递:作为参数传递时只传递了值,参数本身没有传递,如果被传递的方法改变传递后的变量参数值,原变量不会改变。 引用传递: 作为参数传递时传递的是参数本身,如果被传递的方法改变传递后的变量参数值,原变量...
  • java只有值传递

    千次阅读 2016-03-07 17:50:34
    有次去面试,面试官问我java值传递和引用传递是什么,问我有没 有用因为用错这2者而导致错误。 我回答值传递就是传递的数值,引用传递传递的指针,感觉自己回答的 不好后来查了下资料总结下 例子public static ...
  • 与其他语言不同,Java不允许程序员选择按值传递还是按引用传递各个参数,基本类型(byte--short--int--long--float--double--boolean--char)的变量总是按值传递。就对象而言,不是将对象本身传递给方法,而是将对象的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 225,145
精华内容 90,058
关键字:

java值传递不会改变

java 订阅