Java基础 成员变量和局部变量

时间:2023-02-15 13:14:27

在Java中,根据定义变量位置的不同,可以将变量分为两大类:成员变量和局部变量。
一、成员变量和局部
成员变量指的是在类里定义的变量,也就是前面介绍的field;
局部变量指的是在方法里定义的变量

成员变量被分为类变量和实例变量两种
定义成员变量时没有static修饰的就是实例变量,有static修饰的就是类变量
其中类变量从该类的准备阶段开始存在,直到系统完全销毁这个类,类变量的作用域与这个类的生存范围相同
实例变量则该类的实例变量被创建起开始存在,直到系统完全销毁这个实例变量的作用域与之对应的实例的生存范围相同

正是基于这个原因,可以把类变量和实例变量统称为成员变量。
其中类变量可以理解为类成员变量,它作为类本身的一个成员,与类本身共存亡;
实例变量则可理解为实例变量成员,作为实例的一个成员,与实例共存亡。

只要类存在,程序就可以访问该类的类变量。在程序中访问类变量通过如下语法:
类.类变量

只要实例存在,程序就可以访问该实例的实例变量。在程序中访问实例变量通过如下语法:
实例.实例变量

当然,类变量也可以让该类的实例来访问,通过实例来访问类变量的语法如下:
实例.类变量

但由于这个实例并不是拥有这个类变量,因此它访问的并不是这个实例的变量,依然访问它对于类的类变量。

类变量的作用域比实例变量的作用域更大:实例变量随实例的存在而存在,而类变量随类的存在而存在,实例也可访问类变量,同一个类的所有实例访问类变量时,实际*问的是该类本身的同一个变量。 也就是说,访问了同一片内存区

局部变量根据定义形式不同:分为如下三种。
形参:在定义方法签名时定义的变量,形参的作用域在整个方法内有效。

方法局部变量:在方法内定义的局部变量,它的作用域是从定义该变量的地方生效。到该方法结束时失效。

代码块局部变量:在代码中定义的局部变量,这个局部变量的作用域从定义该变量的地方生效,到该代码块结束时失效。

下面的代码是定义代码块局部变量的实例程序:

public class BlockTest
{
public static void main(String[] args)
{
{
//定义一个代码块局部变量a
int a;
//下面代码将出现错误,因为a变量还未初始化
//System.out.println("代码局部变量a的值:" + a);
//为a变量赋初始值,也就是进行出事啊胡
a = 5;
System.out.println("代码局部变量a的值:" + a);
}
//下面试图访问a变量并不存在
//System.out.println(a);
}
}

从上面可以看出,只要离开了代码块局部变量所在的代码块,这个局部变量立即被销毁,变为不可见。

二、成员变量的初始化和内存中的运行机制
当系统加载类或创建该类的实例时,系统自动为成员变量分配内存空间,并在分配内存空间后,自动为成员变量指定初始值。

//创建第一个Person对象
Person p1 = new Person();
//创建第二个Person对象
Person p2 = new Person();
//分别为两个Person对象的name实例变量赋值
p1.name = "张三";
p1.name = "李四";
//分别为Person的eyeNum类变量赋值
p1.eyeNum = 2;
p2.eyeNum = 2;

当指向第一行代码Person = new Person();如果这行代码是第一次使用Person类,则系统通常会在第一次使用Person类时候加载这个类,并初始化这个类。在类的准备的阶段,系统为为该类的类变量分配内存空间,并指定默认初始值。

当Person类初始化完成后,系统在堆内存区中为Person类分配一块内存(当为Person类初始化完成后,系统会为Person类创建一个类对象),这块内存里保存eyeNum类变量的内存,并设置eyeNum的默认初始值:0。

系统接着创界了一个Person对象,并把这个Person对象赋给p1变量,Person对象里包含了名为name的实例变量,实例变量是在创建实例变量时分噢诶内存空间并指定初始值的。

eyeNum类变量并不属于Person对象,它是属于Person类的,所以创建一个Person对象时并不需要为eyeNum类变量分配内存,系统只是为name实例变量分配了内存空间,并指定默认值:null。

当程序执行p1.name = “张三”;代码时,将为p1的name实例变量赋值,也就是让堆内存中的name指向”张三”。

name实例变量时属于单个Person实例的,因此修改第一个Person对象的name实例变量时仅仅与该对象有关,与Person类和其他Person对象没有任何关系。

通过p1来访问类变量时,实际*问的是Person类的eyeNum类变量。事实上,所有的Person实例访问eyeNum类变量时都将访问到Person类的eyeNum类变量。换句话说,不管通过哪个Person实例来访问eyeNum类变量,本质都是通过Person类来访问eyeNum类变量,它们访问的都是同一块内存。

基于这个理由,当程序需要访问类变量时,尽量使用类作为主调,而不要使用对象作为主调,这样可以避免产生歧义,提高程序可读性。

三、局部变量的初始化和内存中的运行机制

局部变量定义后,必须经过显式初始化后才能使用,系统不会为局部变量执行初始化。这意味着定义局部变量后,系统并未为这个变量分配内存空间,直到等到程序为这个变量赋初始化值时,系统为局部变量分配内存,并将初始化值保存到这块内存中。

与成员变量不同,局部变量不属于任何类或实例,因此它总是保存在其所在方法的栈内存中。
局部变量是基本类型的变量,则直接把这个变量的值保存该变量对应的内存中;如果局部变量时引用类型的变量,则这个变量里存放的是地址,通过该地址引用到该变量实际引用的对象或数组。

栈内存的变量无须系统垃圾回收,往往随着方法或代码块的运行结束而结束。因此,局部变量的作用域从初始化该变量开始,直到该方法或该代码块运行完成而结束。因为局部变量只保存基本类型的值或者对象的引用,因此局部变量所占的内存区通常比较小。

四、变量的使用规则
如下几种情形,则应该考虑使用成员变量。
如果要定义的变量时用于描述某个类或某个对象固有信息的,例如人的身高、体重等信息,它们是人对象的固有信息,每个人对象都具有这些信息。这种变量应该定义为成员变量。如果这种信息对这个类的所有实例完全相同,或者说它是类相关的,例如人的眼睛数量,所有人的眼睛数量都是2,这种类相关的信息应该定义成类变量如果这种信息是实例相关的,例如人的身高、体重可能互不相同,这种信息是实例相关的,应该定义成实例变量

如果在某个类中需要以一个变量来保存该类或者实例运行时的状态信息,例如五子棋程序中的棋盘数组,它用以保存五子棋实例运行时的状态信息。这种勇于保存某个类或某个类实例的状态信息的变量通常应该使用成员变量。

如果某个信息需要在某个类的多个方法之间进行共享,则这个信息应该使用成员变量来保存

即使在程序中使用局部变量,也应该尽可能地缩小局部变量的作用范围,局部变量的作用范围越小,它在内存中停留时间越短,程序运行性能就也好。