本文主要是介绍Java浅解 值传递和引用传递,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
在上一篇文章对象中,略微提出了一些值传递和引用传递的概念,感觉需要写出篇简单的理解一下。
首先声明一点,java的传递时值传递,而非引用传递(即指针)。
- 值传递:值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
- 引用传递:引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。是C语言的一种传递方式。
如何体现是值传递,代码如下:
public class Test{public static void main(String[] args) {int a=10;//静态变量System.out.println(a);updata(a);System.out.println(a);}public static void updata(int a) {a=a+10;System.out.println("updata中a的值 "+a);}}
//输出结果
10
updata中a的值 20
10
可见在updata 中对a进行多了修改,但是没有实际的参数值a进行修改,而a的值只能在方法体内生效,也就是局部变量。
我们这里也就简单的拓展一下局内变量和全局变量。
-
局部变量:也称内部变量,是指在一个函数内部或复合语句内部定义的变量。
简单的看,就是在方法体内生效,在之外是不会生效的变量。
-
成员变量(全局变量):就是在方法体之外的变量。
java 变量取值,会就近原则,就是方法体内部有就不会去外部寻找如何理解:
注意: 此处的的a会全局变量
public class Test{static int a=10;//全局变量public static void main(String[] args) {System.out.println(a);updata(a);System.out.println(a);}public static void updata(int a) {a=a+10;System.out.println("updata中a的值"+a);}}
//输出结果
10
updata中a的值 20
10
这样看程序运行似乎,虽然变成了全局变量,但是具体结果和局部变量没有什么区别。
请看下面代码:
public class Test{static int a=10;public static void main(String[] args) {System.out.println(a);updata();System.out.println(a);}public static void updata() {a=a+10;System.out.println("updata中a的值"+a);}}
//输出结果
10
updata中a的值20
20
看见结果变了,难道就不知值传递了吗?
不,还是值传递,但是在updata 方法中没有形参 int a,所以也就没有所谓参数传递。而其运行规则我们说过,就近原则,所以局部没有,那么就需要找外面的的,而updata 中的a也就是全局变量a。所以此处不是引用传递,而是两个变量作用域不同而结果不同而已。
我们区别了全局变量和局内变量,现在我们回来继续说java中的值传递,想要理解这个,就必须对于java的堆和栈进一步理解。
java内存中分三个区:堆,栈,方法区。
-
堆:分配的是对象的存储空间,也就是new之后真正数据存在的内存空间。其中可以理解比如new Dog,而dog就会存储在堆当中,而其就是一个大房子,而其毛色 color,公母 sex 等属性就其里面的房间信息。
-
栈:基本数据类型以及对象的引用的地址
-
方法区:就是类运行的时候加载类的信息以及static修饰的变量。
- 加载类的信息:
- 这个类型的完整有效名。
- 这个类的直接父类的有效名,当然接口和object是除外,两者无父类
- 以及类的类型修饰符 public abstract等
- 方法信息:就是防护的修饰符,以及方法类型,方法的参数和类型等,方法有关信息
- static修饰的模块,以及修饰的变量
方法区包含以上,以及其他的域信息,方法表(提高运算是一种数据结构)等
- 加载类的信息:
现在有疑问了,不是说java是值传递吗?为什么栈的定义提到了引用。
java的参数传递的时候是值传递,而其java中也没有如同C语言中指针的概念(也就是引用),但是不代表其没有使用指针。
可以简单理解如上图所示的一个结构。
int num=10;
String str="apple";
因为int是基本数据类型,所以直接存储在栈中,而string是类,所以在栈中存放的是堆的地址。
看到上面结构,有的就了一个大胆的想法,那就是,如果用string 类型的话,是否在局部修改的时候会影响实际参数呢?不解释,先看代码:
public class Test{public static void main(String[] args) {String a="test";System.out.println(a);updata(a);System.out.println(a);}public static void updata(String a) {a=a+10;System.out.println("updata中a的值"+a);}}
//输出结果
test
updata中a的值test10
test
实参完全没有修改,不是说string是引用吗?为什么改变了而其实参没有改变。
其实这个就是在传递的时候形参复制了实参引用,但是string,本身尤其自己的特点,以为string 本身在建立后是不可改变的,但是所以虽然是通过+ 号进行了一个字符串添加,但是其意思就是新建了string对象,而其索引也就会变。
而如果需要string类型本身改变,因为大量使用string+会造成资源浪费,而java gc(垃圾回收)机制是jvm自己安排的。而是新建字符串,那就需要其衍生的两个类 StringBuffer和StringBuilder。这两个有append的方法可以在其对象上添加数据。
- StringBuilder 适用于单线程,其速度快但是不够安全。
- StringBuffer 适用于多线程,其速度慢但安全
现在我们看一下对象使用的引用,同样首先看代码
public class Dog {String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}public class Test{public static void main(String[] args) {Dog dog = new Dog();dog.setName("旺财") System.out.println(dog.getName());updata(dog);System.out.println(dog.getName());}public static void updata(Dog dog) {dog.setName("元宝")System.out.println("updata中的值"+dog.getName());}}
//输出的值
旺财
updata中的值元宝
元宝
同理,也是传递了所在堆的地址,但是其没有新建一个dog对象,等于还是在原有的对象上调用了setName对dog进行赋值,所以本质还是值传递,而是引用一直没有说,但是一直存在。
这篇关于Java浅解 值传递和引用传递的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!