Java transfer value and the argument passed by reference

Learned Java-based people know: value is passed and passed by reference is a difficult time of the initial contact with Java, but sometimes remember the syntax for practical application can not remember how, but sometimes the use of the principle of no explanation, and the printing of discussion the topic is controversial: some forum posts that Java is only passed by value, said both, and some blog; this was a bit confusing, let's do some research on this topic, in books, on the Forum's blog say, do a research to obtain reliable answers.

In fact, the value is passed and passed by reference for the syntax and use of Baidu, will be able to come out and explain the considerable number of examples, maybe you look at the example seems to understand, but when you pen questions for an interview, do a point when this knowledge I will feel chest mature written answer, but found to be wrong, or are you simply will not do.

what is the reason?

That's because you do not have a thorough understanding of the knowledge, know its fur. To a familiar syntax is very simple, it is not difficult to understand a line of code, but can the knowledge learned mastery, understood in the series, it is very difficult, at this, and pass on values ​​passed by reference, Xiao Bian will be from the previous learned the basics of beginning, start from the memory model, leads passed by value and passing references to the essence of the principle of step by step, so lengthy, more knowledge, more than a hope readers bear with me.

1. Types of actual parameter

Let's remind ourselves of a set of syntax:

  1. Parameters need to pass incoming method is invoked, such as:: parameter in func (int a) a, it only makes sense to be called in during a func, that is, will be allocated memory space, after the completion of the implementation of the method func , a free space will be destroyed, that does not exist
  2. Arguments: method is called when the actual value is passed, it has been initialized before the method is called and passed when the method is called.

For chestnut:

1public static void func(int a){
2 a=20;
3 System.out.println(a);
4}
5public static void main(String[] args) {
int a=10;//实参
7 func(a);
8}
复制代码

Example
int a = 10; in a already created and initialized before being invoked when calling func method, he was passed as a parameter, so this is a argument.
And when func (int a) in a func only be invoked in its life cycle began, and after the end of the func call, which also will be freed JVM ,, so this is a parameter.

2. Java data types

The so-called data type is an abstract expression of the memory of the programming language, we know that the program code file and is composed of static resources, before the program is run, these codes exist in the hard disk, the program starts running, the code will be converted into computer can recognize the contents into memory to be executed.
therefore

Data type definitions are stored in the form of essentially a programming language used in the same types of data, that is, how to determine the values ​​of the bit memory into the computer's memory.

Therefore, data stored in the memory, the storage form is demarcated and the storage location of the data type.
So
Java data types are there?

  1. Basic types: data types of the programming language built minimum particle size. It includes eight types of four categories:

4 kinds of integer types: byte, Short, int, Long
2 Zhong floating-point types: float, Double
1 word character types: char
1 Zhong boolean type: boolean

  1. Reference types: reference, also known as a handle, a reference type, the actual content is stored in the address where the handle in the form of an address to a data value of the programming language defined. It includes:

Class
Interface
Array

With data types, JVM management of data on standardized procedures, different types of data, its storage format and location are not the same, to know JVM is how to store various types of data, you have to first understand the JVM memory division and functions of each part.

3.JVM memory division and functions

Java language itself is not operating memory, it's everything to JVM to manage and control, so Java is divided memory areas is zoning the JVM, JVM memory before said division, we first look at Java execution of the program, as shown below:


There can be seen: Java code is compiled into bytecode compiler after, the JVM an open memory space (also called runtime data area), by the class loader was added to the runtime data area storing programs needed during execution data and information, the data in this area, which consists of the following components:

 

1. virtual machine stack
2. heap
3. Program Counter
4. The method area
The native method stacks

Then we look at each part of the principles and specific data which is used to store program execution process.


1. virtual machine stack

VM stack memory model Java method is executed, the stack frame is stored in the stack, each stack frame a corresponding method is called, the calling procedure of the method corresponds to a procedure push the stack frame of the stack in a virtual machine.

Stack is a thread private, that is, between the thread stack is isolated; when the program started a thread in a corresponding method will create a stack frame and the stack (top of the stack), at the end of the method, pop stack frame.

The figure below shows the model and the composition of a Java stack frame stack:


Stack frame : is used to support virtual machine data structures, method calls and method of execution, it is a virtual machine runtime data area of the virtual machine stack stack elements.

 

Each stack frame comprising:

  1. Local variable table : Local variables used (non-static variable, function parameter) storage method. When the variable is the basic data type, the value is directly stored, when a reference type variable, is directed to a particular storage object.
  2. Operand stack : Java virtual machine interpretation execution engine called "stack-based execution engine", which refers to the stack within the meaning of the operand stack.
  3. Pointing runtime constant pool of references : references to constants may be used to store the program execution.
  4. Methods return address : the return address after the storage method execution is completed.

2. heap:

The heap is used to store the object itself and arrays, only a heap in the JVM, therefore, the heap is shared by all threads.


3. The method area:

The method is an area shared by all threads of memory logic area, the JVM is only one method area, used to store some threads may share the content, it is thread-safe, multiple threads access the method area when the same content, only can have a thread loads the data, another thread can wait.

The method may store the contents of the area are: full path name of the class, weight class direct superclass access modifiers fully qualified name of the class, the class type (class or interface), the direct interface to the fully qualified class name orderly list, constant pool (field, method information, static variables, the type of reference (class)) and so on.


4. A native method stacks:

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

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


5. 程序计数器:

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


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

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

  • 静态方法区
  • 常量区

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

  • 堆式:
  • 栈式
  • 静态

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

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

  • A. 基本数据类型的局部变量
  • B. 基本数据类型的成员变量
  • C. 基本数据类型的静态变量

2. 引用数据类型的存储


1. 基本数据类型的存储


我们分别来研究一下:

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

    如上图,在方法内定义的变量直接存储在栈中,如
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. 基本数据类型的成员变量

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

我们看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方法所在的栈帧中
如图:


When performing the valueCrossTest () method, but also for the JVM stack of the virtual machine to a pressed stack is the current stack frame, for storing valueCrossTest () information in local variables, age and weight are thus lying the method valueCrossTest stack frame is located, and their values are obtained copy of a copy, as shown from the values of a and w:
.
And a content may thus age, w, and the corresponding weight is inconsistent, when the re-assignment in the method, as shown in the actual process:

That is, age and weight changes, but changes the current stack frame (valueCrossTest method where when the stack frame) in the content, after the end of the method execution, these local variables will be destroyed, the stack frame mian method where the back top of the stack becomes the current stack frame, and outputs a w again, when the content is still initializing.
Therefore:
value passed is a copy of the transfer is true, does not affect the operation of a copy of the original content, that is, how parameter changes will not affect the actual parameter corresponding to the content.

 

Passing by reference:
"reference" is pointing to the real content of the address value, when the method is called, arguments of the address by the process is passed to the corresponding parameter called in vivo, shape parameters and arguments directed through pleasant memory address, the real content will affect the parameter operation.

For chestnut:
define an object:

 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}
复制代码

We write a function test:

 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}
复制代码

Output:

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

As can be seen, after performing person through personCrossTest () method, the content has changed, which confirms the above-mentioned "reference transfer" , for the operation parameter, changing the contents of the actual object.

Well, here on the knot yet?
No, not that simple,
could see the desired effect
is just because the election of example only! ! !

Let's make some modifications to the examples above, plus a line of code,

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    }
复制代码

Output:

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

`
Why the output and not the same as the last of it?
To see what is the problem?

According to the above mentioned JVM memory model can know, and arrays are objects in Java heap memory, and is shared heap region, the program execution to the main () method in the following code when

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

JVM will open up in a heap of memory used to store all content p object while () method where the thread's stack area to create a real address referenced memory heap area p p objects in the main, as shown:


When performing the PersonCrossTest () method, because the method of such a line of code:

 

1person=new Person();
复制代码

JVM requires additional memory to store an opened new Person () in the stack, if the address is "xo3333", at this time it was pointed at the person parameter address, and if they really are passed by reference, then the above-mentioned: reference Transferring parameter argument pointing to the same object, the parameter changing operation changes the argument object .

It can be introduced: the argument should point to the address of the person newly created object, so after executing PersonCrossTest () end, the final output should be the subject of content creation behind.

In practice, however, the final output but speculation is not like us, the final output is still content objects created in the beginning.

Thus: passed by reference, it does not exist in Java.

But some people may wonder: Why in the first example, modify the contents of the parameter in the method will cause the contents of the original object changes it?

This is because: both are basic types and reference types, when an incoming parameter arguments are passed by value, is a copy that is delivered, rather than the content itself.

There can be seen, formed in the method of the reference person and no real correlation p arguments, it simply copy the address of a pointing object of p at this time:

p and person are pointing to the same object .

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

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

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

结语

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

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

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



转自:https://juejin.im/post/5bce68226fb9a05ce46a0476

Guess you like

Origin www.cnblogs.com/luizw/p/11800440.html