Java的变量存储
注:本文的描述针对beginner做了简化。
1. 非static
的成员变量
我们通过下面的例子来简要说明Java是如何维护变量的。我们需要借助类与对象一节中使用的Cat类,
package animal;
public class Cat {
// Cat的属性(成员变量)
public int age = 0;
public String name = "";
public String owner = "";
}
创建一个包含main
方法的类来使用Cat
类,并定义其他类型的变量。(为了简洁起见,我们将变量定义在main
函数里,这样并不影响我们理解Java维护变量的机制)
这里我们创建两个基本数据类型变量a
,b
,一个String
类型的变量str
,和三个Cat
类的对象cat1
, cat2
, cat4
;
对应的内存图见Fig.1.
import animal.Cat;
public class JavaMemoryManagement {
public static void main(String[] args) {
int a = 3;
double b = 2.1;
String str = "Hello world";
Cat cat1 = new Cat();
Cat cat2 = new Cat();
Cat cat4;
// 给cat1的属性赋值
cat1.age = 3;
cat1.name = "Ame";
cat1.owner = "XU, Xiaoyan";
}
}
- 堆stack和栈heap可简单理解为内存中两种不同的存储空间;
- Java运行时,会在栈上创建一个变量表,用来记录所有运行过程中需要用到的变量;
- 对于 基本数据类型 int,double等,可以简单理解为Java把数据直接写在变量对应的栈中,如;(实际是分开存储,但是基本类型的数值还是存储在栈上,对应过程比较复杂,beginner没必要深究)
- String类型变量:
Java
会在变量表中记录str
变量;- 为其赋值时,
Java
会在堆上开辟一个存储空间,写入值"Hello world"
,并将str
指向这个存储空间(str
记录该存储空间的内存地址); - 使用
str
变量时,Java会按照其记录的内存地址(沿着箭头)找到堆上的数据,并使用;
Cat
类生成的对象:Cat cat2 = new Cat();
- 等号左边
Cat cat2
声明了一个Cat
类型的变量,叫cat2
; - 等号右边
new Cat()
在堆中开辟一个存储空间来存储这个新对象,用来存储cat2
对象的实体; - 等号将变量表中的
cat2
指向上述开辟出来的内存空间; cat2
的所有属性均为初始值。
- 等号左边
cat1
的创建和使用过程:Cat cat1 = new Cat()
会在变量表中记录一个Cat
类型的变量cat1
,同时在堆上开辟一块内存空间来存储这个新对象,并将cat1
指向这个内存空间;- 通过后续代码
cat1.age = 3
等给cat1
的属性赋值,会修改cat1对应的对象的值。
- Cat cat4;
- 这条语句只声明一个Cat类的变量,叫cat4(即在变量表中录入cat4);
- 因为没有new Cat(),
Java
不会给cat4
开辟内存空间!因此cat4只有其名,未有其实,我们通常说cat4 is null; - 这种情况下,如果尝试使用
cat4
的属性或方法,例如cat4.age
,系统会出错!
Fig. 1. Java内存管理的大致示意图
2. static
成员变量和方法
如下代码中,我们给Cat类添加两个static成员变量:lifespan
和species
,和一个static
的成员方法staticPrint()
.
public class Cat {
// 属于Cat类的静态变量
public static String species = "Animal";
public static int lifespan = 20;
// Cat类型的<对象>拥有的变量
public int age = 0;
public String name = "";
public String owner = "";
// 属于Cat类的静态方法
public static void staticPrint(String something){
System.out.println("staticPrint method of Cat: " + something);
}
// Cat类型的<对象>拥有的方法
public void print(String something){
System.out.println("Non-static print method of Cat: " + something);
}
}
Static成员变量lifespan
和species
会被记录到一个特殊的metaspace类型的存储空间,他们有如下特性:
- (1)
static
成员变量不属于任何对象!它只属于类; - (2)
static
成员变量在其作用范围内有全局变量的意味; - (3)
static
成员变量在程序运行期间会一直存在; - (4) 直接用类名.变量名使用
static
成员变量。
static的成员方法同样只属于类,不属于任何对象;可以直接用**类名.方法名(参数)**直接调用。
例. 在JavaMemoryManagement.java
类里,使用Cat
的lifespan
和species
变量,并调用Cat
的staticPrint
方法(传入参数"Hello Cat"
)。
public class JavaMemoryManagement {
public static void main(String[] args) {
//用Cat.lifespan调用 Cat的lifespan静态属性
System.out.println("Cat's lifespan = " + Cat.lifespan);
System.out.println("Cat belongs to " + Cat.species);
// 用Cat.staticPrint()调用Cat的staticPrint静态方法,并传入参数
Cat.staticPrint("Hello Cat");
}
}
注:
- 静态方法只能调用静态变量和静态方法!
- 静态方法不能使用非静态的变量和方法!
例. 在下面的例子中,a
为非static
的类成员变量,print()
是非静态的成员方法,尝试在static
的main
方法里引用a
将会导致错误。
public class TestStatic {
int a = 1;
public static void main(String[] args) {
int b = a + 1; // 报错:静态的main无法引用非静态的成员变量a
print(); // 报错:静态的main无法调用非静态的print()方法
}
public void print(){
System.out.println("This is a non-static method.");
}
}
提问:那么上述例子中,如果想要引用a变量和print()方法,该怎么办呢?