JAVA中的成员变量和局部变量

前言

李刚老师《JAVA疯狂讲义》第5版,第5章学习笔记。

1.JAVA成员变量和局部变量

在JAVA中,根据变量定义的位置不同,可以把变量划分为成员变量和局部变量两大类,二者命名的语法规则是完全相同的,JAVA中的变量分类具体如下:
JAVA成员变量和局部变量
成员变量就是说这个变量是类的成员,局部变量就是说这个变量只是局部的方法或者代码块里面使用以下,毕竟JAVA中类才是老大,类的变量才有资格说自己是个成员!

1.1 成员变量

对于类变量和实例变量的区别:
类变量从类的准备阶段起开始存在,直到系统销毁这个类,类变量的作用域和类的生存范围相同;
实例变量从类的实例被创建起开始存在,直到系统销毁这个实例,实例变量的作用域和实例的生存范围相同。
类变量可以通过类或类的实例访问;实例变量需要通过类的实例访问。
类变量和成员变量都无需显示初始化,系统会在类的准备阶段或者创建该类的实例时进行默认初始化,初始化规则和数组动态初始化时的规则完全相同。

实例可以访问类变量,并不代表实例拥有类变量,类变量是属于类的。一个类可能有很多实例,都来访问了这个类变量,他们访问的都是同一个类变量,指向内存中的同一块地址。因此,如果使用一个类的实例访问并修改了类变量,那么所有的类的实例再次访问该类变量时,获得的都是修改过的值。

由于在一个类中,成员变量的作用域为整个类,因此,一个类中不能定义两个同名的成员变量,即使一个是类变量,一个是实例变量(想象以下,如果一个类变量和一个实例变量同名,那么借助实例调用这个变量的时候,到底是指的实例变量,还是类变量呢,程序就糊涂了,程序一糊涂,肯定说明我们搞错了)。

但是JAVA中允许成员变量和局部变量重名,这种情况下,局部变量会覆盖成员变量,如果确实需要在这种情况下访问成员变量(这种情况最好还是尽量避免),则需要使用this关键字,this关键字的使用请见该系列的另一篇文章:

JAVA中的类和对象

1.2 局部变量

局部变量可分为三种:

  1. 形参:定义方法签名时定义的变量,作用域:整个方法内
  2. 方法局部变量:方法体内部定义的变量,作用域:从定义变量开始,到方法结束时失效
  3. 代码块局部变量:代码块中定义的变量,作用域:从定义变量开始,到代码块结束时生效

除了形参以外,所有的局部变量都必须显示初始化,否则无法访问,因为不显示初始化,系统是不会在内存中为其分配空间的。

在一个方法中,不能定义两个同名的局部变量(即使一个是形参,一个是方法局部变量);同一个方法中,不同的代码块中,可以出现同名的局部变量。如果先定义代码块的局部变量,再定义代码块所属的方法的局部变量,则这两个变量可以同名。
简而言之,在同一个作用域中,不能出现两个同名的局部变量,因为运行代码时系统会搞不清楚,到底调用的是哪一个。

2.JAVA成员变量和局部变量的内存运行机制

JAVA中,成员变量和局部变量的初始化机制,和在内存中的运行机制均有所不同。

2.1 成员变量内存运行机制

当系统加载类,或者创建类的实例时,系统会自动为成员变量分配内存空间,并且自动为成员变量指定初始值。
以下一段代码为例,详细解释类的成员变量的内存运行机制:

	//定义一个小狗类
	public static class Dog{
		//定义类变量
		public static int leg;
		//定义成员变量
		public static String name;
	}
	public static void main(String args[]) {
		//创建两个Dog类的对象
		Dog d1 = new Dog();
		Dog d2 = new Dog();
		//对象的实例变量访问修改
		d1.name = "Tom";
		d2.name = "Jerry";
		//类变量访问修改
		d1.leg = 4;
		d2.leg = 3;
	}

当程序执行

Dog d1 = new Dog();

此时,如果是程序第一次使用Dog类,则系统会在第一次使用时加载该类,饼初始化,当Dog类初始化完成后,内存中形式如下:
JAVA成员变量内存运行机制1
JAVA系统会在Dog类初始化时,在内存中为其开辟一块空间,存放类变量,并对类变量初始化。当上述程序执行完毕后,创建了d1这个对象时,内存中如下:
JAVA成员变量内存运行机制2
如图所示,引用变量d1存放在栈内存中,实际对象存放在堆内存中。实例变量name也有系统自动初始化为null。当对创建完毕两个对象和其引用变量,并且对每个对象的name变量赋值后,内存中形式如下:
JAVA成员变量内存运行机制3
当程序执行:

		//类变量访问修改
		d1.leg = 4;
		d2.leg = 3;

此时,内存中存储形式为:
JAVA成员变量内存运行机制4
也就是说,无论是利用d1访问leg,还是用d2访问leg,指向的都是内存中的同一个区域,此时再用d1访问leg,得到的结果也是3而不是4。所以,建议在访问类变量时,尽量使用类来访问,尽量不要用类的对象来访问,容易产生混淆。

2.2 局部变量内存运行机制

与成员变量不同,系统不会自动为局部变量初始化,也就是说,定义局部变量后,系统不会自动在内存中为这个局部变量分配空间,知道程序给该变量赋值后,系统才会为其分配空间。

由于局部变量不属于类,或者类的对象,因此它是保存在所属方法的栈内存中,如果局部变量是基本类型,那么直接就保存在栈内存中;如果局部变量是引用类型,则栈内存中保存的为地址,这个地址指向堆内存中的实际数据。

3.JAVA变量使用的注意事项

从程序运行结果来看,如果把变量都定义成成员变量,而不使用局部变量,似乎也能解决问题。但是这种做法相当错误,具体有两个害处:

  1. 增大了变量的生存时间,导致更大的内存开销
  2. 扩大了变量的作用域,不利于提高程序的内聚性

对比以下三个程序:

public class demo1{
	// 定义一个类变量作为循环变量
	static int i;
	public static void main(String args[]){
		for( i=0; i <10; i++){
			System.out.println("Hello");
		}
	}
}
public class demo2{
	public static void main(String args[]){
		// 定义一个方法局部变量作为循环变量
		int i;
		for( i=0; i <10; i++){
			System.out.println("Hello");
		}
	}
}
public class demo3{
	public static void main(String args[]){
		// 定义一个代码块局部变量作为循环变量
		for( int i=0; i <10; i++){
			System.out.println("Hello");
		}
	}
}

结果虽然均相同,但是,程序运行效率相差较大。显然,第三个代码效率最高,因为i作为一个代码块的局部变量,当代码块结束后,这个局部变量就会被销毁,占用内存资源最少。(第二个代码的i会在方法结束后被销毁,而第一个代码的i一直系统完全销毁这个类后,这个变量才会被销毁,因此,第三个代码的效果是最好的)。因此,程序中定义变量时,在满足需求的情况下,应该使得变量的作用域越小越好!

  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值