转译自https://java-programming.mooc.fi/part-5/3-primitive-and-reference-variables
Primitive and reference variables(基本变量和引用变量)
Learning Objectives
学习目标:
•您了解基本变量和引用变量的概念。
•您知道Java中的基本变量类型,也知道引用变量可以有无限多种不同的类型。
•您了解在给它们赋值或将它们用作方法参数时,基本变量和引用变量之间的行为差异。
Variables in Java are classified into primitive and reference variables. From the programmer's perspective, a primitive variable's information is stored as the value of that variable, whereas a reference variable holds a reference to information related to that variable. reference variables are practically always objects in Java. Let's take a look at both of these types with the help of two examples.
您了解在给它们赋值或将它们用作方法参数时,基本变量和引用变量之间的行为差异。
在Java中,变量被分为基本变量和引用变量两种类型。从程序员的角度来看,基本变量的信息存储为该变量的值,而引用变量则保存与该变量相关的信息的引用。在Java中,引用变量几乎总是对象。让我们通过两个示例来看看这两种类型的变量。
int value = 10;
System.out.println(value);
Sample output
10
public class Name {
private String name;
public Name(String name) {
this.name = name;
}
}
Name luke = new Name("Luke");
System.out.println(luke);
Sample output(样例输出)
Name@4aa298b7
In the first example, we create a primitive int variable, and the number 10 is stored as its value. When we pass the variable to the System.out.println method, the number 10 is printed. In the second example, we create a reference variable called luke. A reference to an object is returned by the constructor of the Name class when we call it, and this reference is stored as the value of the variable. When we print the variable, we get Name@4aa298b7 as output. What is causing this?
The method call System.out.println prints the value of the variable. The value of a primitive variable is concrete, whereas the value of a reference variable is a reference. When we attempt to print the value of a reference variable, the output contains the type of the variable and an identifier created for it by Java: the string Name@4aa298b7 tells us that the given variable is of type Name and its identifier is 4aa298b7.
在第一个示例中,我们创建了一个基本int变量,并将数字10存储为其值。当我们将变量传递给System.out.println方法时,将打印数字10。在第二个示例中,我们创建了一个名为luke的引用变量。当调用Name类的构造函数时,将返回对对象的引用,并将该引用存储为变量的值。当我们打印变量时,我们得到Name@4aa298b7作为输出。是什么导致了这种情况?
方法call System.out.println打印变量的值。基本变量的值是具体的,而引用变量的值是引用。当我们尝试打印引用变量的值时,输出包含变量的类型和Java为其创建的标识符:字符串Name@4aa298b7告诉我们给定变量的类型为Name,其标识符为4aa298b7。
The previous example applies whenever the programmer has not altered an object's default print format. You can modify the default print by defining the toString method within the class of the given object, where you specify what the objects print should look like. In the example below, we've defined the public String toString() method within the Name class, which returns the instance variable name. Now, when we print any object that is an instance of the Name class with the System.out.println command, the string returned by the toString method is what gets printed.
只要程序员没有改变对象的默认打印格式,前面的例子就适用。可以通过在给定对象的类中定义toString方法来修改默认打印,在该类中指定对象的打印应该是什么样子。在下面的示例中,我们在Name类中定义了公共String toString()方法,该方法返回实例变量名。现在,当我们使用System.out.println命令打印任何作为Name类实例的对象时,toString方法返回的字符串将被打印出来。
public class Name {
private String name;
public Name(String name) {
this.name = name;
}
public String toString() {
return this.name;
}
}
Name luke = new Name("Luke");
System.out.println(luke); // equal to System.out.println(luke.toString());
Sample output(样例输出)
Luke
Primitive Variables
基本变量
Java has eight different primitive variables. These are: boolean (a truth value: either true or false), byte (a byte containing 8 bits, between the values -128 and 127), char (a 16-bit value representing a single character), short (a 16-bit value that represents a small integer, between the values -32768 and 32767), int (a 32-bit value that represents a medium-sized integer, between the values -2^31 and 2^31-1), long (a 64-bit value that represents a large integer, between values -2^63 and 2^63-1), float (a floating-point number that uses 32 bits), and double (a floating-point number that uses 64 bits).
Out of all of these, we've mainly been using the truth value (boolean), integer (int) and floating-point variables (double).
Java有八种不同的基本变量。它们是:boolean(一个真值:true或false),byte(一个包含8个位的字节,取值范围在-128到127之间),char(一个16位值,表示一个单个字符),short(一个16位值,表示一个小整数,取值范围在-32768到32767之间),int(一个32位值,表示一个中等大小的整数,取值范围在-2^31到2^31-1之间),long(一个64位值,表示一个大整数,取值范围在-2^63到2^63-1之间),float(一个使用32位的浮点数),和double(一个使用64位的浮点数)。
在所有这些中,我们主要使用了真值(boolean)、整数(int)和浮点数变量(double)。
boolean truthValue = false;
int integer = 42;
double floatingPointNumber = 4.2;
System.out.println(truthValue);
System.out.println(integer);
System.out.println(floatingPointNumber);
Sample output(样例输出)
false
42
4.2
Declaring a primitive variable causes the computer to reserve some memory where the value assigned to the variable can be stored. The size of the storage container reserved depends on type of the primitive. In the example below, we create three variables. Each one has its own memory location to which the value that is assigned is copied.
声明一个基本变量会导致计算机保留一些内存空间,用于存储分配给该变量的值。所保留的存储容器的大小取决于基本类型。在下面的示例中,我们创建了三个变量。每个变量都有自己的内存位置,将分配给它的值复制到该位置。
int first = 10;
int second = first;
int third = second;
System.out.println(first + " " + second + " " + third);
second = 5;
System.out.println(first + " " + second + " " + third);
Sample output(样例输出)
10 10 10
10 5 10
The name of the variable tells the memory location where its value is stored. When you assign a value to a primitive variable with an equality sign, the value on the right side is copied to the memory location indicated by the name of the variable. For example, the statement int first = 10 reserves a location called first for the variable, and then copies the value 10 into it.
Similarly, the statement int second = first; reserves in memory a location called second for the variable being created and then copies into it the value stored in the location of variable first.
The values of variables are also copied whenever they're used in method calls. What this means in practice is that the value of a variable that's passed as a parameter during a method call is not mutated in the calling method by the method called. In the example below, we declare a 'number' variable in the main method whose value is copied as the method call's parameter. In the method being called, the value that comes through the parameter is printed, its value is then incremented by one. The value of the variable is then printed once more, and the program execution finally returns to the main method. The value of the 'number' variable in the main method remains unaltered because it has nothing to do with the 'number' variable defined as the parameter of the method that's called.
变量的名称指示了存储其值的内存位置。当你使用等号给基本变量赋值时,等号右侧的值会被复制到变量名称所指示的内存位置中。例如,语句int first = 10会为变量保留一个名为first的位置,并将值10复制到该位置中。
类似地,语句int second = first会在内存中为正在创建的变量保留一个名为second的位置,并将存储在变量first的位置中的值复制到该位置中。
变量的值在方法调用中使用时也会被复制。这在实践中意味着,在方法调用期间作为参数传递的变量的值不会被调用的方法在调用方法中改变。在下面的示例中,我们在main方法中声明了一个名为“number”的变量,它的值被复制为方法调用的参数。在被调用的方法中,通过参数传递的值被打印出来,然后它的值增加了一。变量的值再次被打印出来,程序执行最后返回到main方法。main方法中的“number”变量的值保持不变,因为它与被调用方法的参数’number’变量无关。
public class Example {
public static void main(String[] args) {
int number = 1;
call(number);
System.out.println("Number still: " + number);
}
public static void call(int number) {
System.out.println("Number in the beginning: " + number);
number = number + 1;
System.out.println("Number in the end: " + number);
}
}
Reference Variables
引用变量
All of the variables provided by Java (other than the eight primitive variables mentioned above) are reference type. A programmer is also free to create their own variable types by defining new classes. In practice, any object instanced from a class is a reference variable.
Let's look at the example from the beginning of the chapter where we created a variable called 'leevi' of type Name.
除了上面提到的八种基本变量之外,Java提供的所有变量都是引用类型。程序员也可以通过定义新的类来创建自己的变量类型。在实践中,从类实例化的任何对象都是引用变量。
让我们看一下本章开头的示例,我们创建了一个名为’leevi’的Name类型变量。
Name leevi = new Name("Leevi");
The call has the following parts:
Name leevi = new Name("Leevi");
Name leevi...
... new Name("Leevi");
Name leevi = new Name("Leevi");
这个调用有以下几个部分:
• 每当声明一个新变量时,必须首先声明其类型。我们在下面声明了一个Name类型的变量。为了使程序成功执行,程序必须有一个可用的Name类供程序使用。
Name …
• 我们将变量的名称声明为其声明的名称。您可以稍后通过名称引用变量的值。在下面的示例中,变量名被定义为leevi。
Name leevi…
• 可以为变量赋值。通过调用类的构造函数可以从类创建对象。该构造函数定义了分配给正在创建的对象的实例变量的值。在下面的示例中,我们假设Name类有一个以字符串为参数的构造函数。
… new Name(“Levi”);
• 构造函数调用返回一个值,该值是对新创建的对象的引用。等号告诉程序将右侧表达式的值复制为左侧变量的值。构造函数调用返回的对新创建对象的引用被复制为变量leevi的值。
Name leevi = new Name(“Levi”);
The most significant difference between primitive and reference variables is that primitives (usually numbers) are immutable. The internal state of reference variables, on the other hand, can typically be mutated. This has to do with the fact that the value of a primitive variable is stored directly in the variable, whereas the value of a reference variable is a reference to the variable's data, i.e., its internal state.
Arithmetic operations, such as addition, subtraction, and multiplication can be used with primitive variables — these operations do not change the original values of the variables. Arithmetic operations create new values that can be stored in variables as needed. Conversely, the values of reference variables cannot be changed by these arithmetic expressions.
The value of a reference variable — i.e., the reference — points to a location that contains information relating to the given variable. Let's assume that we have a Person class available to us, containing an instance variable 'age'. If we've instantiated a person object from the class, we can get our hands on the age variable by following the object's reference. The value of this age variable can then be changed as needed.
基本变量和引用变量之间最重要的区别是基本变量(通常是数字)是不可变的。另一方面,引用变量的内部状态通常是可变的。这是因为基本变量的值直接存储在变量中,而引用变量的值是对变量数据(即其内部状态)的引用。
算术运算,如加法、减法和乘法,可以与基本变量一起使用,这些运算不会改变变量的基本值。算术运算会创建新的值,可以根据需要存储在变量中。相反,引用变量的值不能被这些算术表达式改变。
引用变量的值——即引用——指向包含与给定变量相关信息的位置。假设我们有一个可用的Person类,其中包含一个实例变量’age’。如果我们从该类实例化了一个person对象,我们可以通过跟随对象的引用来获取age变量。然后可以根据需要更改此age变量的值。【译注:说明本段话的意思的一个简单的示例代码:
// 定义一个Person类
class Person {
// Person类有一个实例变量age
int age;
// Person类的构造器,用于创建Person对象时初始化age
Person(int initialAge) {
age = initialAge;
}
// 一个方法,用于改变Person对象的age
void setAge(int newAge) {
age = newAge;
}
// 一个方法,用于获取Person对象的age
int getAge() {
return age;
}
}
// 主类
public class Main {
public static void main(String[] args) {
// 创建一个Person对象,并将引用存储在变量john中
Person john = new Person(20);
// 通过john这个引用访问Person对象的age变量
System.out.println("John's age: " + john.getAge());
// 改变这个Person对象的age变量
john.setAge(25);
// 再次通过john这个引用访问改变后的age变量
System.out.println("John's new age: " + john.getAge());
}
}
在这个例子中,我们首先定义了一个Person类,它有一个实例变量age。我们通过Person类的构造器创建了一个Person对象,并将其引用存储在变量john中。
通过这个引用john,我们可以访问和修改这个Person对象的age属性。首先,我们打印出john的初始年龄,然后使用setAge方法来改变john的年龄,并再次打印出来以确认更改。
这个过程展示了如何通过对象的引用来访问和修改对象的实例变量。在这个过程中,我们并没有复制或创建新的Person对象,而是通过引用操作了同一个对象。】
Primitive and Reference Variable as Method Parameters
We mentioned earlier that the value of a primitive variable is directly stored in the variable, whereas the value of a reference variable holds a reference to an object. We also mentioned that assigning a value with the equality sign copies the value (possibly of some variable) on the right-hand side and stores it as the value of the left-hand side variable.
A similar kind of copying occurs during a method call. Regardless of whether the variable is primitive or reference type, the value passed to the method as an argument is copied for the called method to use. With primitive variables, the value of the variable is conveyed to the method. With reference variables, it's a reference.
Let's look at this in practice and assume that we have the following Person class available to us.
基本变量和引用变量作为方法参数
前面提到,基本变量的值直接存储在变量中,而引用变量的值保存对对象的引用。我们还提到,赋一个带有等号的值会复制右边的值(可能是某个变量的值),并将其存储为左边变量的值。
类似的复制发生在方法调用期间。无论变量是基本类型还是引用类型,作为参数传递给方法的值都会被复制以供被调用的方法使用。对于基本变量,变量的值被传递给方法。对于引用变量,它是一个引用。
让我们在实践中看看这一点,并假设我们有以下Person类可用。
public class Person {
private String name;
private int birthYear;
public Person(String name) {
this.name = name;
this.birthYear = 1970;
}
public int getBirthYear() {
return this.birthYear;
}
public void setBirthYear(int birthYear) {
this.birthYear = birthYear;
}
public String toString() {
return this.name + " (" + this.birthYear + ")";
}
}
We'll inspect the execution of the program step by step.
我们将一步一步地检查程序的执行情况。
public class Example {
public static void main(String[] args) {
Person first = new Person("First");
System.out.println(first);
youthen(first);
System.out.println(first);
Person second = first;
youthen(second);
System.out.println(first);
}
public static void youthen(Person person) {
person.setBirthYear(person.getBirthYear() + 1);
}
}
Sample output(样例输出)
First (1970)
First (1971)
First (1972)
The program's execution starts off from the first line of the main method. A variable of type Person is declared on its first line, and the value returned by the Person class constructor is copied as its value. The constructor creates an object whose birth year is set to 1970 and whose name is set to the value received as a parameter. The constructor returns a reference. Once the row has been executed, the program's state is the following — a Person object has been created in memory and the first variable defined in the main method contains a reference to it.
*In the illustrations below, the call stack is on the left-hand side, and the memory of the program on the right.
程序的执行从main方法的第一行开始。在第一行声明Person类型的变量,并复制Person类构造函数返回的值作为其值。构造函数创建一个对象,其出生年份设置为1970,其名称设置为作为参数接收的值。构造函数返回一个引用。一旦行被执行,程序的状态如下—在内存中创建了Person对象,并且在main方法中定义的第一个变量包含对它的引用。
在下面的插图中,调用堆栈在左边,程序的内存在右边。
On the third row of the main method, we print the value of the variable first. The method call System.out.println searches for the toString method on the reference variable that has been given to it as the parameter. The Person class has the toString method, so this method is called on the object referenced by the first variable. The value of the name variable in that object is "First", and the value of the birthYear variable is 1970. The output becomes "First (1970)".
On the fourth row, the program calls the youthen method, to which we pass the variable first as an argument. When the method youthen is called, the value of the parameter variable is copied to be used by the youthen method. The execution of the main method remains waiting in the call stack. As the variable first is a reference type, the reference that was created earlier is copied for the method's use. At the end of the method execution, the situation is as follows — the method increments the birth year of the object it receives as a parameter by one.
在main方法的第三行,我们首先打印变量的值。println方法在作为参数提供给它的引用变量上搜索toString方法。Person类有toString方法,因此在第一个变量引用的对象上调用该方法。该对象中name变量的值是“First”,birthYear变量的值是1970。输出变为“First(1970)”。
在第四行,程序调用youthen方法,我们首先将变量作为参数传递给该方法。当youthen方法被调用时,参数变量的值被复制以供youthen方法使用。主方法的执行仍然在调用堆栈中等待。由于变量首先是引用类型,因此将复制先前创建的引用以供方法使用。在方法执行结束时,情况如下-该方法将其作为参数接收的对象的出生年份加1。
When the execution of the method makeYounger ends, we return back to the main method. The information related to the execution of the makeYounger disappears from the call stack.
当makeYounger方法的执行结束时,我们返回到主方法。与makeYounger执行相关的信息从调用堆栈中消失。
Once we've returned from the method call, we once again print the value of the variable first. The object referenced by the variable first has been mutated during the youthen method call: the birthYear variable of the object has been incremented by one. The final value printed is "First (1971)".
A new Person-type variable called second is then declared in the program. The value of the variable first is copied to the variable second, i.e., the value of the variable second becomes a reference to the already-existing Person object.
从方法调用返回后,我们再次首先打印变量的值。变量首先引用的对象在youthen方法调用期间发生了变化:对象的birthYear变量增加了1。最后打印的值是“First(1971)”。
然后在程序中声明一个名为second的新的person类型变量。变量first的值被复制到变量second,也就是说,变量second的值成为对已经存在的Person对象的引用。
The program calls the youthen method after this, which is given the second variable as a parameter. The value of the variable passed during the method call is copied as a value for the method, i.e., the method receives the reference contained in the second variable for its use. After the method's execution, the birth year of the object referenced by the method has increased by one.
程序在此之后调用youthen方法,该方法将第二个变量作为参数给定。在方法调用期间传递的变量的值被复制为方法的值,也就是说,方法接收包含在第二个变量中的引用以供其使用。方法执行后,该方法引用的对象的出生年份增加1。
Finally, the method execution ends, and the program returns to the main method where the value of the variable first is printed one more time. The final result of the print is "First(1972)".
最后,方法执行结束,程序返回到主方法,在主方法中再次打印变量的值。打印(输出)的最终结果是“First(1972)”。
Variables and Computer Memory
变量与计算机内存
In the course's material, concrete details related to variables and computer memory are simplified. Topics related to memory are dealt with on a level of abstraction that's suitable for learning programming. As an example, the description that the statement int number = 5 reserves a location for the variable number in the memory, and copies the value 5 into it, is sufficient with regard to the learning objectives of this course.
From the perspective of the operating system, a lot more happens when the statement int number = 5 is executed. A locker of size 32-bits is reserved in memory for the value 5, and another one for the number variable. The size of the location is determined by the type of variable in question. Once this is done, the contents of the memory location storing the value 5 are copied into the memory location of the number variable.
To add to the above, the number variable is technically not a memory location or a container. The value of the variable number is an address in memory — information attached to the variable about its type specifies how much data should be retrieved from its address. As an example, this is 32 bits for an integer.
We'll return to this briefly in the advanced programming course. The topic is dealt with in depth in the Computer Organization course.
You have reached the end of this section! Continue to the next section:
→ 4. Objects and references
Remember to check your points from the ball on the bottom-right corner of the material!
在课程材料中,与变量和计算机内存相关的具体细节被简化了。与内存相关的主题在适合学习编程的抽象级别上进行处理。例如,描述语句int number = 5在内存中为变量number保留一个位置,并将值5复制到该位置,对于本课程的学习目标来说已经足够了。
从操作系统的角度来看,执行语句int number = 5时会发生更多的事情。在内存中为值5保留了一个32位大小的存储空间,为变量number保留了另一个存储空间。位置的大小取决于所涉及的变量类型。完成这一步骤后,将存储值5的内存位置的内容复制到number变量的内存位置。
此外,number变量在技术上不是一个内存位置或容器。变量number的值是内存中的一个地址,与变量相关的类型信息指定应从其地址检索多少数据。例如,对于整数来说,这是32位。
我们将在高级编程课程中简要回顾这个问题。这个主题在计算机组织课程中会深入讨论。
您已经到达本节的末尾!继续下一节:
→4. 对象和引用
记得检查材料右下角的球上的分数!