-
java中的参数传递(只有值传递没有引用传递)
2019-07-31 19:25:14Java中只有传值调用(值传递),没有传址调用(址传递或者引用传递)。所以在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中只有值传递,基本类型传递的是值的副本,引用类型传递的是引用的副本。
-
引用传递
2015-03-24 14:30:33今天书又看到了java的引用传递,想了下,在C/C++有值传递、指针传递、引用传递,但在java里面就只有引用传递了,其实我感觉在java里面引用传递就大致相当于指针,原理差不多,那下面通过几个例子来看下java的引用...今天书又看到了java的引用传递,想了下,在C/C++有值传递、指针传递、引用传递,但在java里面就只有引用传递了,其实我感觉在java里面引用传递就大致相当于指针,原理差不多,那下面通过几个例子来看下java的引用传递。
引用传递是整个java的精髓所在,所以了解这个东西是很有必要的。
示例One:
class Demo { private int data = 10; public Demo(int data) { this.data = data; } public void setData(int data) { this.data = data; } public int getData() { return this.data; } } public class TestDemo { public static void main(String args[]) { Demo demo = new Demo(100); fun(demo); // 等价于:Demo temp = demo ; System.out.println(demo.getData()); } public static void fun(Demo temp) { // 接收引用 temp.setData(30); // 修改属性内容 } }
Result:
30
代码中,main()方法中先实例化了一个对象Demo,同时为data赋值100,之后将对象传递给了fun()方法,由于类本身属于引用数据类型,所以在fun()方法中的修改直接会修改原始对象中的内容。
示例Two:
public class TestDemo { public static void main(String args[]) { String str = "Hello"; // 定义字符串 fun(str); // 引用传递:String temp = str ; System.out.println(str); } public static void fun(String temp) { temp = "World"; // 修改字符串内容 } }
Result:
Hello
咦?咋没变呢。。。
这个地方要注意了,代码中使用了String类做为引用的数据类型,这里要了解的是String的内容一旦声明则不可改变,改变的是内存地址的指向。所以在修改字符串数据(temp = “World”)时就发生了引用地址的变更,此时会在堆空间中生成一个“World”,而是把“World”的地址赋值给了temp,但之前的str指向的地址却没有发生变更。因此就出现了上述结果。
对于像Strng、int等基本数据类型,要注意这些情况。示例Three:
class Demo { private String data; public Demo(String data) { this.data = data; } public void setData(String data) { this.data = data; } public String getData() { return this.data; } } public class TestDemo { public static void main(String args[]) { Demo demo = new Demo("Hello"); // 实例化对象 fun(demo); // 引用传递:Demo temp = demo ; System.out.println(demo.getData()); } public static void fun(Demo temp) { temp.setData("World"); } }
Result:
World
本例中有用到了String数据类型,这里我们要理解的是其对应的内存关系:Demo是String的引用,存放在栈中,而data又是字符串的引用也要存放于栈中,字符串在堆中,在Demo堆内存中保存了data对字符串的引用关系,这个理清楚了,也就明白怎么回事了。
关于Java引用传递先说这么多。
A little more for everyday.
-
Java 是值传递还是引用传递
2019-03-20 02:40:16最近整理面试题,整理到值传递、引用传递,到网上搜了一圈,争议很大。带着一脸蒙圈,线上线下查了好多资料。最终有所收获,所以分享给大家,希望能对你有所帮助。 首先说下我的感受,这个题目出的很好,但是在 ...最近整理面试题,整理到值传递、引用传递,到网上搜了一圈,争议很大。带着一脸蒙圈,线上线下查了好多资料。最终有所收获,所以分享给大家,希望能对你有所帮助。
首先说下我的感受,这个题目出的很好,但是在 Java 中这个题目是有问题的(在下面我会解释)。并且,有很多结论是 Java 中只有 值传递。我认为这样说不够严谨。当然如果针对 Java 语言本身来讲,Java 中只有 值传递,没有引用传递,是正确的。但是如果针对 值传递,引用传递的定义来说,Java 中还是有引用传递的。下面来分析:一、值传递、引用传递定义
在深入分析问题之前,先让初问者简单明白一下什么是值传递,引用传递。我先用 Java 代码解释:public class StringBase { public static void main(String[] args) { int c = 66; //c 叫做实参 String d = "hello"; //d 叫做实参 StringBase stringBase = new StringBase(); stringBase.test5(c, d); // 此处 c 与 d 叫做实参 System.out.println("c的值是:" + c + " --- d的值是:" + d); } public void test5(int a, String b) { // a 与 b 叫做形参 a = 55; b = "no"; } }
【运行结果】
c的值是:66 --- d的值是:hello可以看出通过方法传递后,int 类型与 String 类型的原值并没有受到前面 test5 方法执行后的影响,还是输出了原值。这种形为通常被说成值传递。如果原值经过 test5 方法后被改变了,这种形为通常被描述为引用传递。
定义
值传递:指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递:是指在调用函数时将实际参数的地址直接传递到函数中(的形参),那么在函数中对参数所进行的修改,将影响到实际参数。
引用传递:形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。(下面文章中 C++ 的定义,我觉得这样说更精简形象一些,所以放了两个定义,其实意思是一样的)以上,就是相关的定义,大家对这个定义几乎没有分歧,但是我建议大家,有必要去看看 C++ 中 值传递、引用传递的定义。因为在 C++ 中有三个定义:值传递、引用传递、指针传递,推荐一个地址: C++ 值传递、指针传递、引用传递详解
//引用传递 void change2(int &n) { cout << "引用传递--函数操作地址" << &n << endl; n++; }
我们看上边 C++ 引用传递的代码,使用的 & 操作符。& 操作符在 C++ 中被定义为"引用",引用在 C++ 中的定义是“引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样”,再看引用其中的一个描述:“声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元”。因此这引用的概念在 Java 中根本不存在。Java 中哪有给变量起个别名的!!!
因此说,这个题出的就有问题,在 Java 官方中我一直没有找到明确的证据说“Java 中 值传递、引用传递 的定义”我所看到的全是说 C++ 中关于值传递、引用传递的定义。但是,在 Java 中没有 C++ 里"引用"的概念。Java 里只有对象,new 关键字。这就很尴尬了,拿 C++ 中的定义,来解释 Java,我觉得这就是有问题的。问题就出在了引用传递!!!
在 C++ 中关于引用传递的定义明确,代码解释清晰。在 C++ 中引用传递,传递的是一个别名,操作别名就跟操作原值一个样。
然而在 Java 中,没有引用的概念,Java 中只要定义变量就会开辟一个存储单元。因此,对 Java 语言来说只有值传递,没有引用传递是正确的。
虽然 Java 中没有引用(C++ 中 引用"&")。但是,引用传递的定义,在 Java 中还是有符合条件的。抛开语言中的特性。只针对:值传递、引用传递的定义我们来分析一下,Java 是属于值传递还是引用传递。
要想知道 Java 是属于值传递还是引用传递,这就要从 Java 内存模型聊起了,我们来看基本数据类型与引用类型在内存中的存储方式。二、基本数据类型、引用类型
1.基本数据类型、引用类型定义
基本数据类:Java 中有八种基本数据类型“byte、short、int、long、float、double、char、boolean”
引用类型:new 创建的实体类、对象、及数组
2.基本数据类型、引用类型在内存中的存储方式
基本数据类型:存放在栈内存中。用完就消失。
引用类型:在栈内存中存放引用堆内存的地址,在堆内存中存储类、对象、数组等。当没用引用指向堆内存中的类、对象、数组时,由 GC回收机制不定期自动清理。
3.基本类型、引用类型内存简单说明图
好,看了基本的内存图,应该能明白 Java 是属于值传递还是引用传递。不明白,也没关系,下面会详细说明,先说引起争议的代码。三、在 Java 中 值传递 与 引用传递,产生模糊不清的代码
public class TransmitTest { public static void main(String[] args) { String a = "hello"; //String 引用数据类型,调用 pass 方法后 b 的值没有改变,不是 hello int b = 1; //int 基本数据类型,调用 pass 方法后 a 的值没有改变,还是 1 User user = new User(); //new Class 引用类型,调用 pass 方法后 name 与 age 的值改变了 user.setName("main"); // 调用 pass 后,name 为 pass 了 user.setAge(2); //调用 pass 后,age 为 4 了 pass(user, a, b); //pass 方法调用 System.out.println("main 方法 user 是:" + user.toString()); System.out.println("main 方法 a 的值是:" + a + " --- b 的值是:" + b); } public static void pass(User user, String a, int b) { a = "你好"; b = 3; user.setName("pass"); user.setAge(4); System.out.println("pass 方法 user 是:" + user.toString()); System.out.println("pass 方法 a 的值是:" + a + " --- b 的值是:" + b); } } class User { String name; int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "name = " + name + " --- age = " + age; } }
【运行结果】
pass 方法 user 是:name = pass --- age = 4
pass 方法 a 的值是:你好 --- b 的值是:3
main 方法 user 是:name = pass --- age = 4
main 方法 a 的值是:hello --- b 的值是:1 结果分析,int b,实参是 1,pass 方法调用后,值还是 1 没变,说明基本数据类型是值传递,大家对这个也几乎没争议。
争议的在下边了:
1.String a 是引用类型,pass 方法调用后,值还是 hello 没变。(结论:好像引用类型也是值传递啊!!!)
2.new User() 也是引用类型,在方法调用后,值居然变了。原值不应该是 name = main,age = 2 的吗?为什么 name = pass,age = 4 了呢?(结论:引用类型好像是引用传递啊???)
这就奇葩了,String 与 new 创建的类,同为引用类型,为什么产生的结果不一样呢?String 不也是一个类吗?User 不也是一个类吗?
有人解释说这个代码比喻的不对,应该用如下代码比喻,在 pass 方法中添加一行代码,user = new User(),pass 方法修改如下:public static void pass(User user, String a, int b) { a = "你好"; b = 3; user = new User(); user.setName("pass"); user.setAge(4); System.out.println("pass 方法 user 是:" + user.toString()); System.out.println("pass 方法 a 的值是:" + a + " --- b 的值是:" + b); }
【运行结果】
pass 方法 user 是:name = pass --- age = 4
pass 方法 a 的值是:你好 --- b 的值是:3
main 方法 user 是:name = main --- age = 2
main 方法 a 的值是:hello --- b 的值是:1这样一来,改变了形参的值,但是实参没有改变。因此有人得出结论,Java 中只有值传递,没有引用传递。(我并不这么认为,原因如下)
使用 user = new User() 这个代码来做验证,我觉得是符合 String 类型做形参时的验证地,但是,此示例不符合引用传递的验证。
在验证之前,我们先看下使用 user=new User(); 语句之前与之后的内存模型图,能有助于我们更好的验证结果,同时也有助于更好的理解 Java 内存模型。我们看 TransmitTest 类在 Java 内存模型中的存储图:图1 pass() 方法中没有使用 user=new User() 语句的内存模型图
在 图1 中,main() 方法中的 user 类,与 pass() 方法中的 user 类,指向的是同一个堆内存中的 User 类,红色虚线是在 main() 方法中初次给 name 属性赋的值"main"。实线部分,是在 pass() 方法中给 name 属性赋的值"pass"。因为在堆内存中只有一个 User 类实体,因此 main() 方法与 pass() 方法中的 user 指向的都是同一个 User 类 0x000031。因此,无论在 main() 方法还是 pass() 方法中,改变其 user 的属性值后,打印 User 类的属性值肯定是一样的,他们用的是一个实体类。图2 pass() 方法中使用了 user=new User() 语句的内存模型图
在 图2 中,main() 方法中的 user 类首次加载,堆内存开辟了一个地址为 0x000031 的 User 类实体。当把 main() 方法中的实参 user 传递给 pass() 方法中形参 user 的时候,栈内存在 pass() 方法区中开辟了一个空间,并引用了地址为 0x000031 的 User 类。此时两个方法中的 User 类其实是一个。
然而当 pass() 方法中的 user=new User()语句执行后,堆内存中新开辟了一个地址为 0x000032 的 User 类,pass() 方法中的 user 从此指向了地址为 0x000032 的 User 类。
因为 pass() 方法 与 main() 方法中的 user 属性分别指向了不同的 User 类,所以两个方法中的 User 类的属性无论怎么修改,相互都不影响。
但是,这种操作是不能验证引用传递定义的。因为实参传值给形参后,形参自己改变了地址,这就和引用传递无关了。我们再来用代码验证。
我们可以使用 C++ 引用传递代码来验证,使用 user = new User() 语句验证引用传递的错误性。
C++ 中引用传递代码:class User { public: int age; // 长度 string name; // 宽度 }; //引用传递 void pass(User &user) { cout << "引用传递 -- user的地址是:" << &user << endl; user.age = 2; user.name = "你好"; } int main() { User user; user.age = 1; user.name = "hello"; cout << "实参 -- user的地址是:" << &user << endl; pass(user); cout << "实参 -- user的值 age=" << user.age << ",name=" << user.name << endl; system("pause"); return false; }
【运行结果】
实参 -- user的地址是:00DCF768
引用传递 -- user的地址是:00DCF768
实参 -- user的值 age=2,name=你好在 C++ 中,引用传递的实参与形参地址一致,在引用的方法中,使用的就是实参的地址。当修改形参值后,实参值也跟着变。现在我们按照 user=new User(); 的方法改变一下引用方法 pass 如下:
//引用传递 void pass(User &user) { cout << "引用传递 -- user的地址是:" << &user << endl; User user2 = user; //相当于 Java 中的 user=new User(); cout << "引用传递 -- user2的地址是:" << &user2 << endl; user2.age = 2; user2.name = "你好"; }
【运行结果】
实参 -- user的地址是:00CFFACC
引用传递 -- user的地址是:00CFFACC
引用传递 -- user2的地址是:00CFF9AC
实参 -- user的值 age=1,name=hello我们看,改变引用传递中形参 user 的地址后(后期改变的地址,这跟引用传递,值传递还有什么关系?),再修改形参 user 的值,实参没有任何变化。这就破坏了引用传递的场景,因此不能使用 user=new User(); 语句来验证引用传递的定义。
排除了其他异议,我们再来分析 Java 中有没有引用传递。
先把引用传递的定义放上:
引用传递:是指在调用函数时将实际参数的地址直接传递到函数中(的形参),那么在函数中对参数所进行的修改,将影响到实际参数。
引用传递:形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。经过上面的长篇大论,我想这时候你应该能明白了。在对引用类型做方法传递的时候,是不是先把实参的地址给形参的?之后对形参的操作是,是不是相当于操作实参?最后有没有影响到实际参数?
答案肯定都是有的。定义关键1:是指在调用函数时将实际参数的地址直接传递到函数中(给形参了)
证明:Java 在进行方法调用传递引用类型参数的时候,就是先给形参一个与实参相同的地址的(此处与 C++ 的不同之处是,C++ 是别名,没有在内存中给形参开辟空间,而 Java 给形参开辟了一个栈内存空间,存放与实参相同的引用地址。但是这与引用传递的定义不违背啊!!!定义可没说形参是否有开辟空间的概念)。定义关键2:在函数中对参数所进行的修改,将影响到实际参数。
证明:Java 在进行方法调用传递引用类型参数后,修改形参的内容后,就是影响了实参的值。四、String 与包装类的特殊分析
好了,解决了实例对象,我们再来说 String 与包装类,为什么 String 与包装类作为引用类型,却有值传递的功能,居然没有影响到实参!
原因如下:
我们都知道。String 类型及其他七个包装类,是一群特殊群体。当使用 String a = "hello"; 语句时,相当于执行了 String a = new String("hello")。然而在 Java 中每一次 new 都是一次对象的创建。如果你创建的对象在堆中不存在,便会创建一个,如果是新创建的对象,那么地址都会变的,后期改变的地址,这跟引用传递,值传递还有什么关系?其实 String 型方法参数传值的过程,可以用以下代码来解释,我们先看 String 类型的还原:
String a = "hello"; //a 相当于实参 String a1 = a; //a1 就相当于形参 a1 = "你好"; System.out.println("a是:" + a + " --- a1是:" + a1);
【运行结果】
a是:hello --- a1是:你好逐步还原解释:
String a = "hello"; 在 String 池中检查并创建一个常量:"hello",给 a 分配一个栈内存,在此存储常量 hello 的地址。
String a1 = a; 给 a1 分配一个栈内存,在此存储常量 hello 的地址。相当于 a 把自己持有的地址,复制给了 a1。内存图如下
a1 = "你好"; 等同于 a1 = new String("你好")。在 String 池中检查是否有 "你好" 的常量。如果有,将 a1 的地址指向 "你好" 的地址。如果 String 池中没有 "你好" 常量,在堆内存中创建 "你好" 常量,并将 a1 地址指向 "你好"。内存图如下
总结如下:String 类型,在进行方法传参的时候,是先将实参地址,赋值给形参(形参在栈内存中确实新开辟了一个新的内存空间,用于存储地址)。但是当再次给 String 类型的形参赋值(与实参内容不一样的值时),形参地址变了,这就和引用传递无关了。我们可以用 C++ 代码中的引用传递,来验证 String 型的这一特殊情况,代码如下://引用传递 void pass(string &a, int &b) { cout << "引用传递 -- a的地址是:" << &a << " --- b的地址是:" << &b << endl; cout << "引用传递 -- a的值是:" << a << " --- b的值是:" << b << endl; string c = a; // 相当于 java 中的 new String int e = b; cout << "引用传递 -- c的地址是:" << &c << " --- e的地址是:" << &e << endl; cout << "引用传递 -- c的值是:" << c << " --- e的值是:" << e << endl; c = "你好"; //在引用传递中改变形参地址后做修改操作,不影响实参 e = 2; //在引用传递中改变形参地址后做修改操作,不影响实参 } int main() { string a = "hello"; int b = 1; cout << "实参 -- a的地址是:" << &a << " --- b的地址是:" << &b << endl; pass(a, b); cout << "实参 -- a的值是:" << a << " --- b的值是:" << b << endl; system("pause"); return false; }
【运行结果】
实参 -- a的地址是:00CFF9CC --- b的地址是:00CFF9C0
引用传递 -- a的地址是:00CFF9CC --- b的地址是:00CFF9C0
引用传递 -- a的值是:hello --- b的值是:1
引用传递 -- c的地址是:00CFF8A0 --- e的地址是:00CFF894
引用传递 -- c的值是:hello --- e的值是:1
实参 -- a的值是:hello --- b的值是:1我们看,在 C++ 中的引用传递方法中,改变形参的地址后做修改操作,照样不影响实参的值,这就破坏了引用传递的本质,不能这样比喻。
因此,String 与其他包装类,在做形参的时候,由于他们在赋不同于实参的值时,改变了形参的地址,因此使引用传递,看起来像值传递,其实本质还是引用传递。
五、总结
1.这个题目出的不严谨,但是很好(因为涉及了 Java 内存模型)
2.就 Java 语言本身来说,只有值传递,没有引用传递。
3.根据 值传递,引用传递的定义来说:
Java 中的基本类型,属于值传递。
Java 中的引用类型,属于引用传递。
Java 中的 String 及包装类,属于特殊群体,作为形参时,由于每次赋值都相当于重新创建了对象,因此看起来像值传递,但是其特性已经破坏了,值传递、引用传递的定义。因此他们属于引用传递的定义,却表现为值传递。此题争议很大,我仅分享自己的理解,如有不同结论,欢迎指正,一起共勉!
-
五分钟学Java:Java到底是值传递还是引用传递?
2019-12-11 08:43:27在逛 Stack Overflow 的时候,发现了一些访问量像阿尔卑斯山一样高的问题,比如说这个:Java 到底是值传递还是引用传递?访问量足足有 188万+,这不得了啊!说明有很多很多的程序员被这个问题困扰过。实话实说吧,我...在逛 Stack Overflow 的时候,发现了一些访问量像阿尔卑斯山一样高的问题,比如说这个:Java 到底是值传递还是引用传递?访问量足足有 188万+,这不得了啊!说明有很多很多的程序员被这个问题困扰过。实话实说吧,我就是其中之一。
来回顾一下提问者的问题:
我一直认为 Java 是按引用传递的,但是我看一些博客上说不是的。我就纳闷了,Java 到底是值传递还是引用传递?值传递和引用传递有什么区别呢?
如果你也曾被这个问题困扰过,或者正在被困扰,就请随我一起来梳理一下问题的答案。打怪进阶喽!
01、值传递和引用传递
什么是值传递,什么是引用传递?我们需要先把这两个定义搞清楚,才能搞清楚 Java 是按值传递还是按引用传递。
值传递(pass by value)是指在调用方法时将实参复制一份传递到方法中,这样当方法对形参进行修改时不会影响到实参。
引用传递(pass by reference)是指在调用方法时将实参的地址直接传递到方法中,那么在方法中对形参所进行的修改,将影响到实参。上面是比较官方的定义,读起来不免生硬。在我看来,值传递和引用传递的关键区别有两点:
1)调用方法时有没有对实参进行复制。
2)方法内对形参的修改会不会影响到实参。
what?值传递和引用传递还没有搞清楚,又来两个新名词:实参和形参。别急,别急。
02、实参和形参
实参和形参理解起来比值传递和引用传递容易的多,前者就好像是一元一次方程,后者就像是一元二次方程。
形参:定义方法名和方法体的时候使用的参数,目的是用来接收调用该方法时传入的参数。
实参:在调用有参方法时传入的参数,方法名后面的括号中的参数通常被称为“实参”。大家应该都写过“hello world”程序了,就像下面这样。
public class Cmower { public static void main(String[] args) { System.out.println("hello world"); } }
其中
args
就相当于是形参,而字符串“hello world”就相当于是实参。如果觉得这个例子不容易理解,那再来看一个。public class Cmower { public static void main(String[] args) { Cmower cmower = new Cmower(); cmower.sop("沉默王二"); } public void sop(String name) { System.out.println("hello " + name); } }
其中“沉默王二”为实参;有参方法
sop(String name)
中的 name 为形参。形参就好像实参与被调用方法之间的一个桥梁,否则调用者没法传递参数,被调用的方法无法接收参数。03、基本类型是值传递的
Java 中的数据类型可以分为两种,一种是基本类型,一种是引用类型。我相信大家在看本篇文章之前,就能够达成这样一个共识:基本类型是值传递的。这一点毫无疑问。
public class Cmower { public static void main(String[] args) { Cmower cmower = new Cmower(); int age = 18; cmower.sop(age); System.out.println("main 中的 age " + age); } public void sop(int age) { age = 28; System.out.println("sop 中的 age " + age); } }
上面这段代码中,
sop()
方法的实参 age 为 18,尽管sop()
方法的形参被修改为 28,但并不会影响实参的值。这一点可以从输出结果中加以证明。sop 中的 age 28 main 中的 age 18
具体的执行过程如下图所示。
04、引用类型是值传递吗?
大家之所以不确定 Java 是值传递的还是引用传递的,原因就出在这个引用类型上面。单从字面的意思上就容易搞混:引用类型不是引用传递难道还是值传递?
public class Cmower { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public static void main(String[] args) { Cmower cmower = new Cmower(); cmower.setName("沉默王二"); cmower.sop(cmower); System.out.println("main 中的 cmower " + cmower.getName()); } public void sop(Cmower cmower) { cmower.setName("沉默王三"); System.out.println("sop 中的 cmower " + cmower.getName()); } }
在
main()
方法中,我们通过 new 关键字创建了一个对象 cmower,并将其 name 属性设置为“沉默王二”;然后将实参 cmower 传递给sop()
方法,在sop()
方法中将形参 cmower 的 name 属性修改为“沉默王三”。输出结果是什么样子呢?sop 中的 cmower 沉默王三 main 中的 cmower 沉默王三
呀!实参 cmower 的属性 name 竟然不是“沉默王二”而是“沉默王三”了!看看,看看,Java 不是值传递吧?
别急别急。我们在 main 方法中追加几行代码。
Cmower cmower = new Cmower(); cmower.setName("沉默王二"); Cmower old = cmower; cmower.sop(cmower); System.out.println("main 中的 cmower " + cmower.getName()); System.out.println(old == cmower);
old == cmower
会是 true 还是 false 呢?闭上眼睛想一想。如果实在是想不出,抛一枚硬币吧,反正不是 true 就是 false。假如引用类型是引用传递的,根据引用传递的定义(形参的修改将会影响到实参),那么结果一定就是 false。我们来看一下输出结果:
sop 中的 cmower 沉默王三 main 中的 cmower 沉默王三 true
true?开什么玩笑?
不好意思,没有开玩笑。Java 的确是值传递的。只不过,引用类型在调用有参方法的时候,传递的是对象的引用,并不是对象本身。而对象的引用在传递的过程中并没有发生改变,虽然对象本身发生了变化。可以通过下面这幅图感受一下。
这下理解了吧?
05、总结
来看下面这段代码。
int age = 18; String name = "沉默王二";
age 是基本类型,所以值就直接保存在变量中;而 name 是引用类型,变量中保存的只是对象的内存地址,这种变量一般称之为对象的引用。
基本类型作为参数被传递时肯定是值传递;引用类型作为参数被传递时也是值传递,只不过“值”为对应的引用。
好了各位读者朋友们,以上就是本文的全部内容了。能看到这里的都是最优秀的程序员,升职加薪就是你了👍。如果觉得不过瘾,我把本系列文章做了汇总,这里推荐给大家。
五分钟学Java:为什么会发生ArrayIndexOutOfBoundsException?
五分钟学Java:什么是 NullPointerException?
谢谢大家的阅读,原创不易,喜欢就点个赞,这将是我最强的写作动力。
-
php引用传递
2019-03-26 11:07:34引用传递:将一个变量通过引用传递给函数,这样该函数就可以修改其参数的值。被调函数的形式参数虽然也作为局部变量在堆栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址 注:在函数调用时... -
【JAVA】 什么是值传递和引用传递?
2019-06-04 11:10:05引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。 一般认为,java内的基础类型数据传递都是值传递. java中实例对象的传递是引用传递 首先,不要纠结于 Pass By Value 和 Pass ... -
值传递和引用传递的区别
2019-01-08 14:41:42引用传递:针对于基本类型进行封装,对封装进行传递,是引用传递 代码:值传递(int类型) public class Test { public static void main(String[] args) { int int1 = 10; int int2 = int1; System.out... -
值传递和引用传递
2018-09-20 21:35:45当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并 可返回变化后的结果,那么这里到底是值传递还是引用传递? 解答:值传递。 值传递和引用传递的区别: 值传递对象发送改变 不会对引用的内存... -
递归中的引用传递和常引用传递
2019-08-22 12:46:54通过递归加深引用传递和常引用传递的区别,同时加深递归回溯的过程 void process(string& str) { if (str.size() >= 5) { cout << str << ",return" << endl; return; } cout <... -
整形 值传递 引用传递 指针传递
2015-07-22 10:30:21C++ 整形 值传递 引用传递 指针传递 -
JS参数传递(值传递和引用传递)
2018-06-07 15:11:01书P66 一个加粗框中写到:ECMAScript中所有参数传递都是值,不可能通过引用传递参数当时看过没有怎么仔细想过,一知半解吧,今天理解的更加深一些。当然也是昨天做了个题目就因为这个掉坑里一时没爬出来!访问变量有... -
变量的值传递和引用传递
2019-08-20 18:14:20变量的值传递与引用传递,值传递与引用传递的区别 -
Java中值传递 引用传递
2017-10-12 18:28:28Java中值传递 引用传递一直以来对按值传递,引用传递,以及堆栈上的对象不是很明白,今天整理一下自己的想法 形参:用于定义方法的时候使用的参数 说明:只有在被调用时才会分配内存单元,在调用结束,即刻释放所... -
Java中值传递和引用传递的理解
2018-05-09 11:21:58一、基本类型和引用类型的理解Java中的数据类型分为两种为基本类型和引用类型。1、基本类型的变量保存原始值,所以变量就是数据本身。常见的基本类型:byte,short,int,long,char,float,double,Boolean,returnAddress... -
java基本数据类型传递与引用传递区别详解
2016-04-19 17:12:57java的值传递和引用传递在面试中一般都会都被涉及到,今天我们就来聊聊这个问题,首先我们必须认识到这个问题一般是相对函数而言的,也就是java中的方法参数,那么我们先来回顾一下在程序设计语言中有关参数传递给... -
Java值传递和引用传递详细说明
2020-07-14 15:53:17学过Java基础的人都知道:值传递和引用传递是初次接触Java时的一个难点,有时候记得了语法却记不得怎么实际运用,有时候会的了运用却解释不出原理,而且坊间讨论的话题又是充满争议:有的论坛帖子说Java只有值传递,... -
String是值传递还是引用传递
2018-06-28 09:53:30String是值传递还是引用传递 今天上班时,同事发现了一个比较有意思的问题。他把一个String类型的参数传入方法,并在方法内改变了引用的值。 然后他在方法外使用这个值,发现这个String还是之前的值,并没有改变。 ... -
PHP-变量(二)(变量值传递与引用传递,及值传递转换成引用传递)
2017-07-19 10:32:171. PHP 中的值传递 2. PHP 中的引用传递 3. 将值传递转变成引用传递 -
Python值传递还是引用传递
2017-11-02 17:38:55Python值传递还是引用传递Python作为一门动态语言,变量本身的类型是不固定的,因此更加灵活。那Python到底是值传递还是引用传递呢?问题引出本人在本周写代码时,遇到这么一个让我吃惊的问题,问题可以抽象如下:def... -
浅谈参数传递(值传递和引用传递)
2020-03-25 18:17:40浅谈参数传递(值传递和引用传递) 博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢! 说明 突然学到这里的是否,发现这个概念自己以前... -
引用传递-指针传递-值传递概念
2019-04-11 18:34:01指针传递,值传递和引用传递是C++中最容易引起混淆的地方,现总结一下: 值传递 形式参数是对实参的一个拷贝,改变形参的值不会影响外部实参的值。值传递是从实参到形参单向传递的,参数的值只能传入,不能传出。... -
c++ vector 引用传递
2019-01-19 10:55:42c++中vector作为参数,在调用函数中需修改vector的值时,要用到引用传递。 vector作为参数的两种引用传递的方式: 1、 void func1(vector<int> &q){ ... } int main(){ vector<... -
探究Java方法的参数传递是值传递还是引用传递
2020-03-04 15:39:19探究Java方法的参数传递是值传递还是引用传递 -
什么是值传递和引用传递
2019-10-12 22:45:56引用传递等于是传递了一个句柄 Java是值传递 值传递是对于基本类型而言的,所传递的是一个基本数据类型的副本,改变副本不改变原变量 引用传递一般是对于对象变量类型而言的,传递的是该对象地址的一个副本,而不是... -
Python函数值传递与引用传递
2019-03-27 10:11:32值传递与引用传递 对于函数的参数而言,我们都知道什么是形式参数,什么是实际参数。 但在此之后,我们还应该清楚什么是值传递,什么是引用传递。 值传递就是当实际参数为不可变对象时进行的参数传递。而引用传递... -
指针传递和引用传递
2015-05-12 23:27:28指针传递和引用传递 为了更好的理解指针和引用,我们下面来介绍一下指针传递和引用传递。当指针和引用作为函数的函数是如何传值的呢?(下面这一段引用了C++中引用传递与指针传递区别(进一步整理)) 指针传递... -
Java值传递和引用传递基础分析
2017-08-29 21:51:36针对Java中值传递和引用传递进行简单分析总结
-
install_flash_player_ax_cn_34_0_0_92离线安装包
-
vue前端UI框架有哪些?
-
【Jeecg】【代码生成】【VUE】-edit-接口调用-405错误
-
(新)备战2021软考网络工程师培训学习套餐
-
windows下的socket tcp压力测试工具(附突破连接限制的方法和工具).zip
-
visual c++ vc修改文件属性中的创建时间,修改时间,访问时间.zip
-
UnityShader used without having been completely initialized解决办法
-
Git-2.30.0.2-64-bit&32-bit官网下载.rar
-
day84-商城业务-nginx-搭建域名访问环境一(反向代理配置)
-
【Andriod Studio学习】6.线程
-
Duet Display windows版 version 1.9.2.7
-
Kotlin协程极简入门与解密
-
巴图鲁前端实习面经(2021.1.14)
-
MySQL性能优化的最佳经验.zip
-
C/C++编程全家桶(Daozy极限编程)
-
flash actionscript3游戏编程之AS3 PureMVC设计模式在AS中的应用例子_详解MVC的作用.zip
-
pyechart数据可视化
-
uni-app实战专题
-
gostudy_zip dos下怎么样才能下go棋
-
跟我练内测小分队学习礼包